/* SWWReader Reader of SWW files from within OpenSceneGraph. copyright (C) 2004-2005 Geoscience Australia */ #include #include #include #include #include #include #include #include #include #include #include #include // compile time defaults #define DEFAULT_CULLANGLE 85.0 #define DEFAULT_ALPHAMIN 0.8 #define DEFAULT_ALPHAMAX 1.0 #define DEFAULT_HEIGHTMIN 0.0 #define DEFAULT_HEIGHTMAX 1.0 #define DEFAULT_BEDSLOPEOFFSET 0.0 #define DEFAULT_CULLONSTART false // only constructor, requires netcdf file SWWReader::SWWReader(const std::string& filename) { // assume failure until end of constructor _valid = false; // state initialization _state.bedslopetexturefilename = NULL; // netcdf filename _state.swwfilename = new std::string(filename); // netcdf open _status.push_back( nc_open(filename.c_str(), NC_NOWRITE, &_ncid) ); if (this->_statusHasError()) return; // dimension ids _status.push_back( nc_inq_dimid(_ncid, "number_of_volumes", &_nvolumesid) ); _status.push_back( nc_inq_dimid(_ncid, "number_of_vertices", &_nverticesid) ); _status.push_back( nc_inq_dimid(_ncid, "number_of_points", &_npointsid) ); _status.push_back( nc_inq_dimid(_ncid, "number_of_timesteps", &_ntimestepsid) ); if (this->_statusHasError()) return; // dimension values _status.push_back( nc_inq_dimlen(_ncid, _nvolumesid, &_nvolumes) ); _status.push_back( nc_inq_dimlen(_ncid, _nverticesid, &_nvertices) ); _status.push_back( nc_inq_dimlen(_ncid, _npointsid, &_npoints) ); _status.push_back( nc_inq_dimlen(_ncid, _ntimestepsid, &_ntimesteps) ); if (this->_statusHasError()) return; // variable ids _status.push_back( nc_inq_varid (_ncid, "x", &_xid) ); _status.push_back( nc_inq_varid (_ncid, "y", &_yid) ); _status.push_back( nc_inq_varid (_ncid, "z", &_zid) ); _status.push_back( nc_inq_varid (_ncid, "volumes", &_volumesid) ); _status.push_back( nc_inq_varid (_ncid, "time", &_timeid) ); _status.push_back( nc_inq_varid (_ncid, "stage", &_stageid) ); if (this->_statusHasError()) return; // allocation of variable arrays, destructor responsible for cleanup _px = new float[_npoints]; _py = new float[_npoints]; _pz = new float[_npoints]; _ptime = new float[_ntimesteps]; _pvolumes = new unsigned int[_nvertices * _nvolumes]; _pstage = new float[_npoints]; // loading variables from netcdf file _status.push_back( nc_get_var_float (_ncid, _xid, _px) ); // x vertices _status.push_back( nc_get_var_float (_ncid, _yid, _py) ); // y vertices _status.push_back( nc_get_var_float (_ncid, _zid, _pz) ); // bedslope heights _status.push_back( nc_get_var_float (_ncid, _timeid, _ptime) ); // time array _status.push_back( nc_get_var_int (_ncid, _volumesid, (int *) _pvolumes) ); // triangle indices if (this->_statusHasError()) return; osg::notify(osg::INFO) << "[SWWReader] number of volumes: " << _nvolumes << std::endl; osg::notify(osg::INFO) << "[SWWReader] number of vertices: " << _nvertices << std::endl; osg::notify(osg::INFO) << "[SWWReader] number of points: " << _npoints << std::endl; osg::notify(osg::INFO) << "[SWWReader] number of timesteps: " << _ntimesteps << std::endl; // sww file can optionally contain bedslope texture image filename size_t attlen; // length of text attribute (if it exists) if( nc_inq_attlen(_ncid, NC_GLOBAL, "texture", &attlen) != NC_ENOTATT ) { char* texfilename; if( (texfilename = (char*) malloc(attlen+1)) != NULL){ int status; status = nc_get_att_text(_ncid, NC_GLOBAL, "texture", texfilename); if( status == NC_NOERR ) { texfilename[attlen] = '\0'; // ensure string is terminated, not a requirement for netcdf attributes osg::notify(osg::INFO) << "[SWWReader] embedded image filename: " << texfilename << std::endl; // if sww isn't in current directory, need to prepend sww path to the bedslope texture if( osgDB::getFilePath(filename) == "" ) setBedslopeTexture( std::string(texfilename) ); else setBedslopeTexture( osgDB::getFilePath(filename) + std::string("/") + std::string(texfilename) ); } free( texfilename ); } } // sww file can optionally contain georeference offset int status1 = nc_get_att_float(_ncid, NC_GLOBAL, "xllcorner", &_xllcorner); int status2 = nc_get_att_float(_ncid, NC_GLOBAL, "yllcorner", &_yllcorner); if( status1 == NC_NOERR && status2 == NC_NOERR) { osg::notify(osg::INFO) << "[SWWReader] xllcorner: " << _xllcorner << std::endl; osg::notify(osg::INFO) << "[SWWReader] yllcorner: " << _yllcorner << std::endl; } else { _xllcorner = 0.0; // default value _yllcorner = 0.0; } // alpha-scaling defaults, can be overridden after construction by command line parameters _state.alphamin = DEFAULT_ALPHAMIN; _state.alphamax = DEFAULT_ALPHAMAX; _state.heightmin = DEFAULT_HEIGHTMIN; _state.heightmax = DEFAULT_HEIGHTMAX; // steepness culling default, can be overridden after construction by command line parameter _state.cullangle = DEFAULT_CULLANGLE; _state.culling = DEFAULT_CULLONSTART; // loop index size_t iv; // vertex indices unsigned int v1index, v2index, v3index; // bedslope range and resultant scale factors (note, these don't take into // account any xllcorner, yllcorner offsets - used during texture coord assignment) float xmin, xmax, xrange; float ymin, ymax, yrange; float zmin, zmax, zrange; float aspect_ratio; xmin = _px[0]; xmax = _px[0]; ymin = _py[0]; ymax = _py[0]; zmin = _pz[0]; zmax = _pz[0]; for( iv=1; iv < _npoints; iv++ ) { if( _px[iv] < xmin ) xmin = _px[iv]; if( _px[iv] > xmax ) xmax = _px[iv]; if( _py[iv] < ymin ) ymin = _py[iv]; if( _py[iv] > ymax ) ymax = _py[iv]; if( _pz[iv] < zmin ) zmin = _pz[iv]; if( _pz[iv] > zmax ) zmax = _pz[iv]; } xrange = xmax - xmin; yrange = ymax - ymin; zrange = zmax - zmin; aspect_ratio = xrange/yrange; _xscale = 1.0 / xrange; _yscale = 1.0 / yrange; // handle case of a flat bed that doesn't necessarily pass through z=0 _zscale = (zrange == 0.0) ? 1.0 : 1.0/zrange; _xoffset = xmin; _yoffset = ymin; _zoffset = zmin; _xcenter = 0.5; _ycenter = 0.5; _zcenter = 0.0; if( aspect_ratio > 1.0 ) { _scale = 1.0/xrange; _ycenter /= aspect_ratio; } else { _scale = 1.0/yrange; _xcenter *= aspect_ratio; } osg::notify(osg::INFO) << "[SWWReader] xmin: " << xmin << std::endl; osg::notify(osg::INFO) << "[SWWReader] xmax: " << xmax << std::endl; osg::notify(osg::INFO) << "[SWWReader] xrange: " << xrange << std::endl; osg::notify(osg::INFO) << "[SWWReader] xscale: " << _xscale << std::endl; osg::notify(osg::INFO) << "[SWWReader] xcenter: " << _xcenter << std::endl; osg::notify(osg::INFO) << std::endl; osg::notify(osg::INFO) << "[SWWReader] ymin: " << ymin << std::endl; osg::notify(osg::INFO) << "[SWWReader] ymax: " << ymax << std::endl; osg::notify(osg::INFO) << "[SWWReader] yrange: " << yrange << std::endl; osg::notify(osg::INFO) << "[SWWReader] yscale: " << _yscale << std::endl; osg::notify(osg::INFO) << "[SWWReader] ycenter: " << _ycenter << std::endl; osg::notify(osg::INFO) << std::endl; osg::notify(osg::INFO) << "[SWWReader] zmin: " << zmin << std::endl; osg::notify(osg::INFO) << "[SWWReader] zmax: " << zmax << std::endl; osg::notify(osg::INFO) << "[SWWReader] zrange: " << zrange << std::endl; osg::notify(osg::INFO) << "[SWWReader] zscale: " << _zscale << std::endl; osg::notify(osg::INFO) << "[SWWReader] zcenter: " << _zcenter << std::endl; osg::notify(osg::INFO) << std::endl; // compute triangle connectivity, a list (indexed by vertex number) // of lists (indices of triangles sharing this vertex) _connectivity = std::vector(_npoints); for (iv=0; iv < _nvolumes; iv++) { v1index = _pvolumes[3*iv+0]; v2index = _pvolumes[3*iv+1]; v3index = _pvolumes[3*iv+2]; _connectivity.at(v1index).push_back(iv); _connectivity.at(v2index).push_back(iv); _connectivity.at(v3index).push_back(iv); } // bedslope vertex array, shifting and scaling vertices to unit cube // centred about the origin _bedslopevertices = new osg::Vec3Array; for (iv=0; iv < _npoints; iv++) _bedslopevertices->push_back( osg::Vec3( (_px[iv]-_xoffset)*_scale - _xcenter, (_py[iv]-_yoffset)*_scale - _ycenter, (_pz[iv]-_zoffset)*_scale - _zcenter - DEFAULT_BEDSLOPEOFFSET) ); // bedslope index array, pvolumes array indexes into x, y and z _bedslopeindices = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLES, _nvolumes*_nvertices); for (iv=0; iv < _nvolumes*_nvertices; iv++) (*_bedslopeindices)[iv] = _pvolumes[iv]; // calculate bedslope primitive normal and centroid arrays _bedslopenormals = new osg::Vec3Array; _bedslopecentroids = new osg::Vec3Array; osg::Vec3 v1, v2, v3, side1, side2, nrm; for (iv=0; iv < _nvolumes; iv++) { v1index = _pvolumes[3*iv+0]; v2index = _pvolumes[3*iv+1]; v3index = _pvolumes[3*iv+2]; v1 = _bedslopevertices->at(v1index); v2 = _bedslopevertices->at(v2index); v3 = _bedslopevertices->at(v3index); side1 = v2 - v1; side2 = v3 - v2; nrm = side1^side2; nrm.normalize(); _bedslopenormals->push_back( nrm ); _bedslopecentroids->push_back( (v1+v2+v3)/3.0 ); } // success! _valid = true; } SWWReader::~SWWReader() { _status.push_back( nc_close(_ncid) ); delete[]( _px, _py, _pz, _ptime, _pvolumes, _pstage ); } void SWWReader::setBedslopeTexture( std::string filename ) { osg::notify(osg::INFO) << "[SWWReader::setBedslopetexture] filename: " << filename << std::endl; _state.bedslopetexturefilename = new std::string(filename); _bedslopegeodata.hasData = false; // GDAL Geospatial Image Library GDALDataset *pGDAL; GDALAllRegister(); pGDAL = (GDALDataset *) GDALOpen( filename.c_str(), GA_ReadOnly ); if( pGDAL == NULL ) osg::notify(osg::INFO) << "[SWWReader::setBedslopetexture] GDAL unable to open file: " << filename << std::endl; else { osg::notify(osg::INFO) << "[SWWReader::setBedslopetexture] GDAL Driver: " << pGDAL->GetDriver()->GetDescription() << "/" << pGDAL->GetDriver()->GetMetadataItem( GDAL_DMD_LONGNAME ) << std::endl; _bedslopegeodata.xresolution = pGDAL->GetRasterXSize(); _bedslopegeodata.yresolution = pGDAL->GetRasterYSize(); osg::notify(osg::INFO) << "[SWWReader::setBedslopetexture] xresolution: " << _bedslopegeodata.xresolution << std::endl; osg::notify(osg::INFO) << "[SWWReader::setBedslopetexture] yresolution: " << _bedslopegeodata.yresolution << std::endl; // check if image contains geodata double geodata[6]; if( pGDAL->GetGeoTransform( geodata ) == CE_None ) { _bedslopegeodata.xorigin = geodata[0]; _bedslopegeodata.yorigin = geodata[3]; _bedslopegeodata.rotation = geodata[2]; _bedslopegeodata.xpixel = geodata[1]; _bedslopegeodata.ypixel = geodata[5]; _bedslopegeodata.hasData = true; osg::notify(osg::INFO) << "[SWWReader::setBedslopetexture] xorigin: " << _bedslopegeodata.xorigin << std::endl; osg::notify(osg::INFO) << "[SWWReader::setBedslopetexture] yorigin: " << _bedslopegeodata.yorigin << std::endl; osg::notify(osg::INFO) << "[SWWReader::setBedslopetexture] xpixel: " << _bedslopegeodata.xpixel << std::endl; osg::notify(osg::INFO) << "[SWWReader::setBedslopetexture] ypixel: " << _bedslopegeodata.ypixel << std::endl; osg::notify(osg::INFO) << "[SWWReader::setBedslopetexture] rotation: " << _bedslopegeodata.rotation << std::endl; } } } osg::Image* SWWReader::getBedslopeTexture() { return osgDB::readImageFile( _state.bedslopetexturefilename->c_str() ); } osg::ref_ptr SWWReader::getBedslopeTextureCoords() { _bedslopetexcoords = new osg::Vec2Array; if( _bedslopegeodata.hasData ) // georeferenced bedslope texture { float xorigin = _bedslopegeodata.xorigin; float yorigin = _bedslopegeodata.yorigin; float xrange = _bedslopegeodata.xpixel * _bedslopegeodata.xresolution; float yrange = _bedslopegeodata.ypixel * _bedslopegeodata.yresolution; for( unsigned int iv=0; iv < _npoints; iv++ ) _bedslopetexcoords->push_back( osg::Vec2( (_px[iv] + _xllcorner - xorigin)/xrange, (_py[iv] + _yllcorner - yorigin)/yrange ) ); } else { // decaled using (x,y) locations scaled by extents into range [0,1] for( unsigned int iv=0; iv < _npoints; iv++ ) _bedslopetexcoords->push_back( osg::Vec2( (_px[iv]-_xoffset)*_xscale, (_py[iv]-_yoffset)*_yscale ) ); } return _bedslopetexcoords; } osg::ref_ptr SWWReader::getStageVertexArray(unsigned int index) { size_t start[2], count[2], iv; const ptrdiff_t stride[2] = {1,1}; start[0] = index; start[1] = 0; count[0] = 1; count[1] = _npoints; // empty array for storing list of steep triangles osg::ref_ptr steeptri = new osg::IntArray; // stage heights from netcdf file (x and y are same as bedslope) int status = nc_get_vars_float (_ncid, _stageid, start, count, stride, _pstage); // load stage vertex array, scaling and shifting vertices to lie in the unit cube _stagevertices = new osg::Vec3Array; for (iv=0; iv < _npoints; iv++) _stagevertices->push_back( osg::Vec3( (_px[iv]-_xoffset)*_scale - _xcenter, (_py[iv]-_yoffset)*_scale - _ycenter, (_pstage[iv]-_zoffset)*_scale - _zcenter) ); // stage index, per primitive normal and centroid arrays _stageprimitivenormals = new osg::Vec3Array; _stagecentroids = new osg::Vec3Array; osg::Vec3 v1b, v2b, v3b; osg::Vec3 v1s, v2s, v3s; osg::Vec3 side1, side2, nrm; unsigned int v1index, v2index, v3index; // cullangle given in degrees, test is against dot product float cullthreshold = cos(osg::DegreesToRadians(_state.cullangle)); // over all stage triangles for (iv=0; iv < _nvolumes; iv++) { v1index = _pvolumes[3*iv+0]; v2index = _pvolumes[3*iv+1]; v3index = _pvolumes[3*iv+2]; v1s = _stagevertices->at(v1index); v2s = _stagevertices->at(v2index); v3s = _stagevertices->at(v3index); v1b = _bedslopevertices->at(v1index); v2b = _bedslopevertices->at(v2index); v3b = _bedslopevertices->at(v3index); // current triangle primitive normal side1 = v2s - v1s; side2 = v3s - v2s; nrm = side1^side2; nrm.normalize(); // store centroid and primitive normal _stagecentroids->push_back( (v1s+v2s+v3s)/3.0 ); _stageprimitivenormals->push_back( nrm ); // identify steep triangles, store index if( fabs(nrm * osg::Vec3f(0,0,1)) < cullthreshold ) steeptri->push_back( iv ); } // stage height above bedslope mapped as alpha value // alpha = min( a(h-hmin) + alphamin, alphamax), h >= hmin // alpha = 0, h < hmin // where a = (alphamax-alphamin)/(hmax-hmin) float alpha, height, alphascale; alphascale = (_state.alphamax - _state.alphamin) / (_state.heightmax - _state.heightmin); _stagecolors = new osg::Vec4Array; for (iv=0; iv < _npoints; iv++) { // water height above corresponding bedslope height = _stagevertices->at(iv).z() - _bedslopevertices->at(iv).z(); if (height < _state.heightmin) alpha = 0.0; else { alpha = alphascale * (height - _state.heightmin) + _state.alphamin; if( alpha > _state.alphamax ) alpha = _state.alphamax; } _stagecolors->push_back( osg::Vec4( 1.0, 1.0, 1.0, alpha ) ); } // steep triangle vertices should have alpha=0, overwrite such vertex colours if( _state.culling ) { //std::cout << "culling on step: " << index << " steeptris: " << steeptri->size() << std::endl; for (iv=0; iv < steeptri->size() ; iv++) { int tri = steeptri->at(iv); v1index = _pvolumes[3*tri+0]; v2index = _pvolumes[3*tri+1]; v3index = _pvolumes[3*tri+2]; _stagecolors->at(v1index) = osg::Vec4( 1, 1, 1, 0 ); _stagecolors->at(v2index) = osg::Vec4( 1, 1, 1, 0 ); _stagecolors->at(v3index) = osg::Vec4( 1, 1, 1, 0 ); } } // per-vertex normals calculated as average of primitive normals // from contributing triangles _stagevertexnormals = new osg::Vec3Array; int num_shared_triangles, triangle_index; for (iv=0; iv < _npoints; iv++) { nrm.set(0,0,0); num_shared_triangles = _connectivity.at(iv).size(); for (int i=0; iat(triangle_index); } nrm = nrm / num_shared_triangles; // average nrm.normalize(); _stagevertexnormals->push_back(nrm); } return _stagevertices; } bool SWWReader::_statusHasError() { bool haserror = false; // assume success, trap failure std::vector::iterator iter; for (iter=_status.begin(); iter != _status.end(); iter++) { if (*iter != NC_NOERR) { osg::notify(osg::WARN) << nc_strerror(*iter) << std::endl; haserror = true; nc_close(_ncid); break; } } // on return start gathering result values afresh _status.clear(); return haserror; }