/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2003 Robert Osfield * * This library is open source and may be redistributed and/or modified under * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or * (at your option) any later version. The full license is in LICENSE file * included with this distribution, and on the openscenegraph.org website. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * OpenSceneGraph Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include // GDAL includes #include #include #include #include // standard library includes #include #include using namespace osgTerrain; enum CoordinateSystemType { GEOCENTRIC, GEOGRAPHIC, PROJECTED, LOCAL }; CoordinateSystemType getCoordinateSystemType(const osg::CoordinateSystemNode* lhs) { // set up LHS SpatialReference char* projection_string = strdup(lhs->getCoordinateSystem().c_str()); char* importString = projection_string; OGRSpatialReference lhsSR; lhsSR.importFromWkt(&importString); std::cout<<"getCoordinateSystemType("<GetValue(),"GEOCCS")==0) return GEOCENTRIC; if (lhsSR.IsGeographic()) return GEOGRAPHIC; if (lhsSR.IsProjected()) return PROJECTED; if (lhsSR.IsLocal()) return LOCAL; return PROJECTED; } double getAngularUnits(const osg::CoordinateSystemNode* lhs) { // set up LHS SpatialReference char* projection_string = strdup(lhs->getCoordinateSystem().c_str()); char* importString = projection_string; OGRSpatialReference lhsSR; lhsSR.importFromWkt(&importString); free(projection_string); char* str; double result = lhsSR.GetAngularUnits(&str); std::cout<<"lhsSR.GetAngularUnits("<getCoordinateSystem().c_str()); char* importString = projection_string; OGRSpatialReference lhsSR; lhsSR.importFromWkt(&importString); free(projection_string); char* str; double result = lhsSR.GetLinearUnits(&str); std::cout<<"lhsSR.GetLinearUnits("<_geoTransform.set( adfDstGeoTransform[1], adfDstGeoTransform[4], 0.0, 0.0, adfDstGeoTransform[2], adfDstGeoTransform[5], 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, adfDstGeoTransform[0], adfDstGeoTransform[3], 0.0, 1.0); data->computeExtents(); } else { std::cout << " No GeoTransform or GCP's - unable to compute position in space"<< std::endl; data->_geoTransform.set( 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0); data->computeExtents(); } return data; } } case(Source::MODEL): { osg::Node* model = osgDB::readNodeFile(source->getFileName().c_str()); if (model) { SourceData* data = new SourceData(source); data->_model = model; data->_extents.expandBy(model->getBound()); } } break; } return 0; } osg::BoundingBox DataSet::SourceData::getExtents(const osg::CoordinateSystemNode* cs) const { return computeSpatialProperties(cs)._extents; } const DataSet::SpatialProperties& DataSet::SourceData::computeSpatialProperties(const osg::CoordinateSystemNode* cs) const { // check to see it exists in the _spatialPropertiesMap first. SpatialPropertiesMap::const_iterator itr = _spatialPropertiesMap.find(cs); if (itr!=_spatialPropertiesMap.end()) { return itr->second; } if (areCoordinateSystemEquivilant(_cs.get(),cs)) { return *this; } if (_cs.valid() && cs) { if (_gdalDataSet) { //std::cout<<"Projecting bounding volume for "<<_source->getFileName()<getCoordinateSystem().c_str(), NULL, cs->getCoordinateSystem().c_str(), TRUE, 0.0, 1 ); if (!hTransformArg) { std::cout<<" failed to create transformer"<(cs); sp._geoTransform.set( adfDstGeoTransform[1], adfDstGeoTransform[4], 0.0, 0.0, adfDstGeoTransform[2], adfDstGeoTransform[5], 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, adfDstGeoTransform[0], adfDstGeoTransform[3], 0.0, 1.0); GDALDestroyGenImgProjTransformer( hTransformArg ); sp.computeExtents(); return sp; } } std::cout<<"DataSet::DataSource::assuming compatible coordinates."<getType()) { case(Source::IMAGE): std::cout<<"B.1"<s()*(intersect_bb.xMin()-d_bb.xMin())/(d_bb.xMax()-d_bb.xMin())),0); int destY = osg::maximum((int)floorf((float)destination._image->t()*(intersect_bb.yMin()-d_bb.yMin())/(d_bb.yMax()-d_bb.yMin())),0); int destWidth = osg::minimum((int)ceilf((float)destination._image->s()*(intersect_bb.xMax()-d_bb.xMin())/(d_bb.xMax()-d_bb.xMin())),(int)destination._image->s())-destX; int destHeight = osg::minimum((int)ceilf((float)destination._image->t()*(intersect_bb.yMax()-d_bb.yMin())/(d_bb.yMax()-d_bb.yMin())),(int)destination._image->t())-destY; std::cout<<" copying from "<GetRasterCount(); GDALRasterBand* bandGray = 0; GDALRasterBand* bandRed = 0; GDALRasterBand* bandGreen = 0; GDALRasterBand* bandBlue = 0; GDALRasterBand* bandAlpha = 0; for(int b=1;b<=numBands;++b) { GDALRasterBand* band = _gdalDataSet->GetRasterBand(b); if (band->GetColorInterpretation()==GCI_GrayIndex) bandGray = band; else if (band->GetColorInterpretation()==GCI_RedBand) bandRed = band; else if (band->GetColorInterpretation()==GCI_GreenBand) bandGreen = band; else if (band->GetColorInterpretation()==GCI_BlueBand) bandBlue = band; else if (band->GetColorInterpretation()==GCI_AlphaBand) bandAlpha = band; else bandGray = band; } GDALRasterBand* bandSelected = 0; if (!bandSelected && bandGray) bandSelected = bandGray; else if (!bandSelected && bandAlpha) bandSelected = bandAlpha; else if (!bandSelected && bandRed) bandSelected = bandRed; else if (!bandSelected && bandGreen) bandSelected = bandGreen; else if (!bandSelected && bandBlue) bandSelected = bandBlue; if (!(bandRed && bandGreen && bandBlue) && bandSelected) { // do a hack to get the handling of gery scale images working by // copying the single band three times... // will fix later, Robert Osfield, May 2nd 2004. bandRed = bandSelected; bandGreen = bandSelected; bandBlue = bandSelected; } if (bandRed && bandGreen && bandBlue) { // RGB unsigned int numBytesPerPixel = 1; GDALDataType targetGDALType = GDT_Byte; int pixelSpace=3*numBytesPerPixel; int lineSpace=-(int)(destination._image->getRowSizeInBytes()); unsigned char* imageData = destination._image->data(destX,destY+destHeight-1); std::cout << "reading RGB"<RasterIO(GF_Read,windowX,_numValuesY-(windowY+windowHeight),windowWidth,windowHeight,(void*)(imageData+0),destWidth,destHeight,targetGDALType,pixelSpace,lineSpace); bandGreen->RasterIO(GF_Read,windowX,_numValuesY-(windowY+windowHeight),windowWidth,windowHeight,(void*)(imageData+1),destWidth,destHeight,targetGDALType,pixelSpace,lineSpace); bandBlue->RasterIO(GF_Read,windowX,_numValuesY-(windowY+windowHeight),windowWidth,windowHeight,(void*)(imageData+2),destWidth,destHeight,targetGDALType,pixelSpace,lineSpace); } else { unsigned char* tempImage = new unsigned char[destWidth*destHeight*3]; bandRed->RasterIO(GF_Read,windowX,_numValuesY-(windowY+windowHeight),windowWidth,windowHeight,(void*)(tempImage+0),destWidth,destHeight,targetGDALType,pixelSpace,pixelSpace*destWidth); bandGreen->RasterIO(GF_Read,windowX,_numValuesY-(windowY+windowHeight),windowWidth,windowHeight,(void*)(tempImage+1),destWidth,destHeight,targetGDALType,pixelSpace,pixelSpace*destWidth); bandBlue->RasterIO(GF_Read,windowX,_numValuesY-(windowY+windowHeight),windowWidth,windowHeight,(void*)(tempImage+2),destWidth,destHeight,targetGDALType,pixelSpace,pixelSpace*destWidth); // now copy into destination image unsigned char* sourceRowPtr = tempImage; unsigned int sourceRowDelta = pixelSpace*destWidth; unsigned char* destinationRowPtr = imageData; unsigned int destinationRowDelta = lineSpace; for(int row=0; rowdestinationTotal) { // copy pixel across destinationColumnPtr[0] = sourceColumnPtr[0]; destinationColumnPtr[1] = sourceColumnPtr[1]; destinationColumnPtr[2] = sourceColumnPtr[2]; } #else if (sourceColumnPtr[0]!=0 || sourceColumnPtr[1]!=0 || sourceColumnPtr[2]!=0) { // copy pixel across destinationColumnPtr[0] = sourceColumnPtr[0]; destinationColumnPtr[1] = sourceColumnPtr[1]; destinationColumnPtr[2] = sourceColumnPtr[2]; } #endif } } delete [] tempImage; } } else { std::cout<<"Warning image not read as Red, Blue and Green bands not present"<getNumColumns()*(intersect_bb.xMin()-d_bb.xMin())/(d_bb.xMax()-d_bb.xMin())),0); int destY = osg::maximum((int)floorf((float)destination._heightField->getNumRows()*(intersect_bb.yMin()-d_bb.yMin())/(d_bb.yMax()-d_bb.yMin())),0); int destWidth = osg::minimum((int)ceilf((float)destination._heightField->getNumColumns()*(intersect_bb.xMax()-d_bb.xMin())/(d_bb.xMax()-d_bb.xMin())),(int)destination._heightField->getNumColumns())-destX; int destHeight = osg::minimum((int)ceilf((float)destination._heightField->getNumRows()*(intersect_bb.yMax()-d_bb.yMin())/(d_bb.yMax()-d_bb.yMin())),(int)destination._heightField->getNumRows())-destY; std::cout<<" copying from "<GetRasterCount(); GDALRasterBand* bandGray = 0; GDALRasterBand* bandRed = 0; GDALRasterBand* bandGreen = 0; GDALRasterBand* bandBlue = 0; GDALRasterBand* bandAlpha = 0; for(int b=1;b<=numBands;++b) { GDALRasterBand* band = _gdalDataSet->GetRasterBand(b); if (band->GetColorInterpretation()==GCI_GrayIndex) bandGray = band; else if (band->GetColorInterpretation()==GCI_RedBand) bandRed = band; else if (band->GetColorInterpretation()==GCI_GreenBand) bandGreen = band; else if (band->GetColorInterpretation()==GCI_BlueBand) bandBlue = band; else if (band->GetColorInterpretation()==GCI_AlphaBand) bandAlpha = band; else bandGray = band; } GDALRasterBand* bandSelected = 0; if (!bandSelected && bandGray) bandSelected = bandGray; else if (!bandSelected && bandAlpha) bandSelected = bandAlpha; else if (!bandSelected && bandRed) bandSelected = bandRed; else if (!bandSelected && bandGreen) bandSelected = bandGreen; else if (!bandSelected && bandBlue) bandSelected = bandBlue; if (bandSelected) { bool xyInDegrees = false; CoordinateSystemType cst = getCoordinateSystemType(_cs.get()); if (cst==GEOGRAPHIC) { xyInDegrees = true; } if (bandSelected->GetUnitType()) std::cout << "bandSelected->GetUnitType()=" << bandSelected->GetUnitType()<GetUnitType()= null" <GetNoDataValue(&success); if (success) { std::cout<<"We have NoDataValue = "<GetOffset(&success); if (success) { std::cout<<"We have Offset = "<GetScale(&success); if (success) { std::cout<<"We have Scale = "<getVerticalScale(); std::cout<<"We have no Scale from file so use DataSet vertical scale of "<getConvertFromGeographicToGeocentric()*/) ? 1.0f/111319.0f : 1.0f; } std::cout<<"********* getLinearUnits = "<RasterIO(GF_Read,windowX,_numValuesY-(windowY+windowHeight),windowWidth,windowHeight,floatdata,destWidth,destHeight,GDT_Float32,numBytesPerZvalue,lineSpace); bandSelected->RasterIO(GF_Read,windowX,_numValuesY-(windowY+windowHeight),windowWidth,windowHeight,heightData,destWidth,destHeight,GDT_Float32,0,0); std::cout<<" scaling height field"<=destY;--r) { for(int c=destX;csetHeight(c,r,offset + h*scale); else if (!ignoreNoDataValue) hf->setHeight(c,r,noDataValueFill); h = hf->getHeight(c,r); } } delete [] heightData; } } } void DataSet::SourceData::readModels(DestinationData& destination) { if (_model.valid()) { std::cout<<"Raading model"<_extents.xMax()-_sourceData->_extents.xMin())/(double)(_sourceData->_numValuesX-1); double dy = (_sourceData->_extents.yMax()-_sourceData->_extents.yMin())/(double)(_sourceData->_numValuesY-1); setSortValue( sqrt( dx*dx + dy*dy ) ); } } void DataSet::Source::loadSourceData() { std::cout<<"DataSet::Source::loadSourceData() "<<_filename<_cs = _cs; std::cout<<"assigning CS from Source to Data."<_cs; std::cout<<"assigning CS from Data to Source."<_geoTransform = _geoTransform; std::cout<<"assigning GeoTransform from Source to Data."<<_geoTransform<_numValuesX - 1); double div_y = 1.0/(double)(_sourceData->_numValuesY - 1); _geoTransform(0,0) *= div_x; _geoTransform(1,0) *= div_x; _geoTransform(2,0) *= div_x; _geoTransform(0,1) *= div_y; _geoTransform(1,1) *= div_y; _geoTransform(2,1) *= div_y; _sourceData->_geoTransform = _geoTransform; std::cout<<"assigning GeoTransform from Source to Data."<<_geoTransform<_geoTransform; std::cout<<"assigning GeoTransform from Data to Source."<<_geoTransform<computeExtents(); _extents = _sourceData->_extents; } bool DataSet::Source::needReproject(const osg::CoordinateSystemNode* cs) const { return needReproject(cs,0.0,0.0); } bool DataSet::Source::needReproject(const osg::CoordinateSystemNode* cs, double minResolution, double maxResolution) const { if (!_sourceData) return false; // handle modles by using a matrix transform only. if (_type==MODEL) return false; // always need to reproject imagery with GCP's. if (_sourceData->_hasGCPs) { std::cout<<"Need to to reproject due to presence of GCP's"<_geoTransform; double currentResolution = sqrt(osg::square(m(0,0))+osg::square(m(1,0))+ osg::square(m(0,1))+osg::square(m(1,1))); if (currentResolutionmaxResolution) return true; return false; } DataSet::Source* DataSet::Source::doReproject(const std::string& filename, osg::CoordinateSystemNode* cs, double targetResolution) const { // return nothing when repoject is inappropriate. if (!_sourceData) return 0; if (_type==MODEL) return 0; std::cout<<"reprojecting to file "<_gdalDataSet,_sourceData->_cs->getCoordinateSystem().c_str(), NULL, cs->getCoordinateSystem().c_str(), TRUE, 0.0, 1 ); if (!hTransformArg) { std::cout<<" failed to create transformer"<_gdalDataSet, GDALGenImgProjTransform, hTransformArg, adfDstGeoTransform, &nPixels, &nLines ) != CE_None ) { std::cout<<" failed to create warp"<0.0f) { std::cout<<"recomputing the target transform size"<getCoordinateSystem()<_gdalDataSet,1) ); if( hCT != NULL ) GDALSetRasterColorTable( GDALGetRasterBand(hDstDS,1), hCT ); /* -------------------------------------------------------------------- */ /* Setup warp options. */ /* -------------------------------------------------------------------- */ GDALWarpOptions *psWO = GDALCreateWarpOptions(); psWO->hSrcDS = _sourceData->_gdalDataSet; psWO->hDstDS = hDstDS; psWO->pfnTransformer = pfnTransformer; psWO->pTransformerArg = hTransformArg; psWO->pfnProgress = GDALTermProgress; /* -------------------------------------------------------------------- */ /* Setup band mapping. */ /* -------------------------------------------------------------------- */ psWO->nBandCount = GDALGetRasterCount(_sourceData->_gdalDataSet); psWO->panSrcBands = (int *) CPLMalloc(psWO->nBandCount*sizeof(int)); psWO->panDstBands = (int *) CPLMalloc(psWO->nBandCount*sizeof(int)); int i; for(i = 0; i < psWO->nBandCount; i++ ) { psWO->panSrcBands[i] = i+1; psWO->panDstBands[i] = i+1; } /* -------------------------------------------------------------------- */ /* Setup no datavalue */ /* -----------------------------------------------------`--------------- */ // check to see if no value values exist in source datasets. int numNoDataValues = 0; for(i = 0; i < _sourceData->_gdalDataSet->GetRasterCount(); i++ ) { int success = 0; GDALRasterBand* band = _sourceData->_gdalDataSet->GetRasterBand(i+1); band->GetNoDataValue(&success); if (success) ++numNoDataValues; } if (numNoDataValues) { // no data values exist, so populate the no data arrays. psWO->padfSrcNoDataReal = (double*) CPLMalloc(psWO->nBandCount*sizeof(double)); psWO->padfSrcNoDataImag = (double*) CPLMalloc(psWO->nBandCount*sizeof(double)); psWO->padfDstNoDataReal = (double*) CPLMalloc(psWO->nBandCount*sizeof(double)); psWO->padfDstNoDataImag = (double*) CPLMalloc(psWO->nBandCount*sizeof(double)); for(i = 0; i < _sourceData->_gdalDataSet->GetRasterCount(); i++ ) { int success = 0; GDALRasterBand* band = _sourceData->_gdalDataSet->GetRasterBand(i+1); double noDataValue = band->GetNoDataValue(&success); //double new_noDataValue = noDataValue; double new_noDataValue = 0; if (success) { std::cout<<"\tassinging no data value "<padfSrcNoDataReal[i] = noDataValue; psWO->padfSrcNoDataImag[i] = 0.0; psWO->padfDstNoDataReal[i] = new_noDataValue; psWO->padfDstNoDataImag[i] = 0.0; GDALRasterBandH band = GDALGetRasterBand(hDstDS,i+1); GDALSetRasterNoDataValue( band, new_noDataValue); } } psWO->papszWarpOptions = (char**)CPLMalloc(2*sizeof(char*)); psWO->papszWarpOptions[0] = strdup("INIT_DEST=NO_DATA"); psWO->papszWarpOptions[1] = 0; } else { psWO->padfSrcNoDataReal = (double*) CPLMalloc(psWO->nBandCount*sizeof(double)); psWO->padfSrcNoDataImag = (double*) CPLMalloc(psWO->nBandCount*sizeof(double)); psWO->padfDstNoDataReal = (double*) CPLMalloc(psWO->nBandCount*sizeof(double)); psWO->padfDstNoDataImag = (double*) CPLMalloc(psWO->nBandCount*sizeof(double)); for(i = 0; i < _sourceData->_gdalDataSet->GetRasterCount(); i++ ) { int success = 0; GDALRasterBand* band = _sourceData->_gdalDataSet->GetRasterBand(i+1); double noDataValue = band->GetNoDataValue(&success); double new_noDataValue = 0.0; if (success) { std::cout<<"\tassinging no data value "<padfSrcNoDataReal[i] = noDataValue; psWO->padfSrcNoDataImag[i] = 0.0; psWO->padfDstNoDataReal[i] = noDataValue; psWO->padfDstNoDataImag[i] = 0.0; GDALRasterBandH band = GDALGetRasterBand(hDstDS,i+1); GDALSetRasterNoDataValue( band, new_noDataValue); } else { psWO->padfSrcNoDataReal[i] = 0.0; psWO->padfSrcNoDataImag[i] = 0.0; psWO->padfDstNoDataReal[i] = new_noDataValue; psWO->padfDstNoDataImag[i] = 0.0; GDALRasterBandH band = GDALGetRasterBand(hDstDS,i+1); GDALSetRasterNoDataValue( band, new_noDataValue); } } psWO->papszWarpOptions = (char**)CPLMalloc(2*sizeof(char*)); psWO->papszWarpOptions[0] = strdup("INIT_DEST=NO_DATA"); psWO->papszWarpOptions[1] = 0; } /* -------------------------------------------------------------------- */ /* Initialize and execute the warp. */ /* -------------------------------------------------------------------- */ GDALWarpOperation oWO; if( oWO.Initialize( psWO ) == CE_None ) { oWO.ChunkAndWarpImage( 0, 0, GDALGetRasterXSize( hDstDS ), GDALGetRasterYSize( hDstDS ) ); } std::cout<<"new projection is "<_type = _type; newSource->_filename = filename; newSource->_temporaryFile = true; newSource->_cs = cs; newSource->_numValuesX = nPixels; newSource->_numValuesY = nLines; newSource->_geoTransform.set( adfDstGeoTransform[1], adfDstGeoTransform[4], 0.0, 0.0, adfDstGeoTransform[2], adfDstGeoTransform[5], 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, adfDstGeoTransform[0], adfDstGeoTransform[3], 0.0, 1.0); newSource->computeExtents(); // reload the newly created file. newSource->loadSourceData(); return newSource; } void DataSet::Source::consolodateRequiredResolutions() { if (_requiredResolutions.size()<=1) return; ResolutionList consolodated; ResolutionList::iterator itr = _requiredResolutions.begin(); double minResX = itr->_resX; double minResY = itr->_resY; double maxResX = itr->_resX; double maxResY = itr->_resY; ++itr; for(;itr!=_requiredResolutions.end();++itr) { minResX = osg::minimum(minResX,itr->_resX); minResY = osg::minimum(minResY,itr->_resY); maxResX = osg::maximum(maxResX,itr->_resX); maxResY = osg::maximum(maxResY,itr->_resY); } double currResX = minResX; double currResY = minResY; while (currResX<=maxResX && currResY<=maxResY) { consolodated.push_back(ResolutionPair(currResX,currResY)); currResX *= 2.0f; currResY *= 2.0f; } _requiredResolutions.swap(consolodated); } void DataSet::Source::buildOverviews() { return; if (_sourceData.valid() && _sourceData->_gdalDataSet ) { int anOverviewList[4] = { 2, 4, 8, 16 }; GDALBuildOverviews( _sourceData->_gdalDataSet, "AVERAGE", 4, anOverviewList, 0, NULL, GDALTermProgress/*GDALDummyProgress*/, NULL ); } } /////////////////////////////////////////////////////////////////////////////////////////////////////// DataSet::DestinationTile::DestinationTile(): _dataSet(0), _level(0), _tileX(0), _tileY(0), _imagery_maxNumColumns(4096), _imagery_maxNumRows(4096), _imagery_maxSourceResolutionX(0.0f), _imagery_maxSourceResolutionY(0.0f), _terrain_maxNumColumns(1024), _terrain_maxNumRows(1024), _terrain_maxSourceResolutionX(0.0f), _terrain_maxSourceResolutionY(0.0f), _complete(false) { for(int i=0;igetSourceData(); if (data && (*itr)->getType()!=Source::MODEL) { SpatialProperties sp = data->computeSpatialProperties(_cs.get()); if (!sp._extents.intersects(_extents)) { // std::cout<<"DataSet::DestinationTile::computeMaximumSourceResolution:: source does not overlap ignoring for this tile."<getType()) { case(Source::IMAGE): if (_imagery_maxSourceResolutionX==0.0f) _imagery_maxSourceResolutionX=sourceResolutionX; else _imagery_maxSourceResolutionX=osg::minimum(_imagery_maxSourceResolutionX,sourceResolutionX); if (_imagery_maxSourceResolutionY==0.0f) _imagery_maxSourceResolutionY=sourceResolutionY; else _imagery_maxSourceResolutionY=osg::minimum(_imagery_maxSourceResolutionY,sourceResolutionY); break; case(Source::HEIGHT_FIELD): if (_terrain_maxSourceResolutionX==0.0f) _terrain_maxSourceResolutionX=sourceResolutionX; else _terrain_maxSourceResolutionX=osg::minimum(_terrain_maxSourceResolutionX,sourceResolutionX); if (_terrain_maxSourceResolutionY==0.0f) _terrain_maxSourceResolutionY=sourceResolutionY; else _terrain_maxSourceResolutionY=osg::minimum(_terrain_maxSourceResolutionY,sourceResolutionY); break; default: break; } } } } } bool DataSet::DestinationTile::computeImageResolution(unsigned int& numColumns, unsigned int& numRows, double& resX, double& resY) { if (_imagery_maxSourceResolutionX!=0.0f && _imagery_maxSourceResolutionY!=0.0f && _imagery_maxNumColumns!=0 && _imagery_maxNumRows!=0) { unsigned int numColumnsAtFullRes = 1+(unsigned int)ceilf((_extents.xMax()-_extents.xMin())/_imagery_maxSourceResolutionX); unsigned int numRowsAtFullRes = 1+(unsigned int)ceilf((_extents.yMax()-_extents.yMin())/_imagery_maxSourceResolutionY); unsigned int numColumnsRequired = osg::minimum(_imagery_maxNumColumns,numColumnsAtFullRes); unsigned int numRowsRequired = osg::minimum(_imagery_maxNumRows,numRowsAtFullRes); numColumns = 1; numRows = 1; // round to nearest power of two above or equal to the required resolution while (numColumns_cs = _cs; _imagery->_extents = _extents; _imagery->_geoTransform.set(texture_dx, 0.0, 0.0,0.0, 0.0, -texture_dy, 0.0,0.0, 0.0, 0.0, 1.0,1.0, _extents.xMin(), _extents.yMax(), 0.0,1.0); _imagery->_image = new osg::Image; _imagery->_image->allocateImage(texture_numColumns,texture_numRows,1,GL_RGB,GL_UNSIGNED_BYTE); unsigned char* data = _imagery->_image->data(); unsigned int totalSize = _imagery->_image->getTotalSizeInBytesIncludingMipmaps(); for(unsigned int i=0;i_cs = _cs; _terrain->_extents = _extents; _terrain->_geoTransform.set(dem_dx, 0.0, 0.0,0.0, 0.0, -dem_dy, 0.0,0.0, 0.0, 0.0, 1.0,1.0, _extents.xMin(), _extents.yMax(), 0.0,1.0); _terrain->_heightField = new osg::HeightField; _terrain->_heightField->allocate(dem_numColumns,dem_numRows); _terrain->_heightField->setOrigin(osg::Vec3(_extents.xMin(),_extents.yMin(),0.0f)); _terrain->_heightField->setXInterval(dem_dx); _terrain->_heightField->setYInterval(dem_dy); //float xMax = _terrain->_heightField->getOrigin().x()+_terrain->_heightField->getXInterval()*(float)(dem_numColumns-1); //std::cout<<"ErrorX = "<_heightField->getOrigin().y()+_terrain->_heightField->getYInterval()*(float)(dem_numRows-1); //std::cout<<"ErrorY = "<getTile(_level,_tileX-1,_tileY),_dataSet->getTile(_level,_tileX-1,_tileY-1), _dataSet->getTile(_level,_tileX,_tileY-1),_dataSet->getTile(_level,_tileX+1,_tileY-1), _dataSet->getTile(_level,_tileX+1,_tileY),_dataSet->getTile(_level,_tileX+1,_tileY+1), _dataSet->getTile(_level,_tileX,_tileY+1),_dataSet->getTile(_level,_tileX-1,_tileY+1)); } } void DataSet::DestinationTile::setNeighbours(DestinationTile* left, DestinationTile* left_below, DestinationTile* below, DestinationTile* below_right, DestinationTile* right, DestinationTile* right_above, DestinationTile* above, DestinationTile* above_left) { _neighbour[LEFT] = left; _neighbour[LEFT_BELOW] = left_below; _neighbour[BELOW] = below; _neighbour[BELOW_RIGHT] = below_right; _neighbour[RIGHT] = right; _neighbour[RIGHT_ABOVE] = right_above; _neighbour[ABOVE] = above; _neighbour[ABOVE_LEFT] = above_left; // std::cout<<"LEFT="<<_neighbour[LEFT]< TileCornerPair; typedef std::vector TileCornerList; TileCornerList cornersToProcess; DestinationTile* tile=0; cornersToProcess.push_back(TileCornerPair(this,position)); tile = _neighbour[(position-1)%NUMBER_OF_POSITIONS]; if (tile) cornersToProcess.push_back(TileCornerPair(tile,(Position)((position+2)%NUMBER_OF_POSITIONS))); tile = _neighbour[(position)%NUMBER_OF_POSITIONS]; if (tile) cornersToProcess.push_back(TileCornerPair(tile,(Position)((position+4)%NUMBER_OF_POSITIONS))); tile = _neighbour[(position+1)%NUMBER_OF_POSITIONS]; if (tile) cornersToProcess.push_back(TileCornerPair(tile,(Position)((position+6)%NUMBER_OF_POSITIONS))); // make all these tiles as equalised upfront before we return. TileCornerList::iterator itr; for(itr=cornersToProcess.begin(); itr!=cornersToProcess.end(); ++itr) { TileCornerPair& tcp = *itr; tcp.first->_equalized[tcp.second] = true; } // if there is only one valid corner to process then there is nothing to equalize against so return. if (cornersToProcess.size()==1) return; typedef std::pair ImageCornerPair; typedef std::vector ImageCornerList; typedef std::pair HeightFieldCornerPair; typedef std::vector HeightFieldCornerList; ImageCornerList imagesToProcess; HeightFieldCornerList heightFieldsToProcess; for(itr=cornersToProcess.begin(); itr!=cornersToProcess.end(); ++itr) { TileCornerPair& tcp = *itr; if (tcp.first->_imagery.valid() && tcp.first->_imagery->_image.valid()) { imagesToProcess.push_back(ImageCornerPair(tcp.first->_imagery->_image.get(),tcp.second)); } if (tcp.first->_terrain.valid() && tcp.first->_terrain->_heightField.valid()) { heightFieldsToProcess.push_back(HeightFieldCornerPair(tcp.first->_terrain->_heightField.get(),tcp.second)); } } if (imagesToProcess.size()>1) { int red = 0; int green = 0; int blue = 0; // accumulate colours. ImageCornerList::iterator iitr; for(iitr=imagesToProcess.begin(); iitr!=imagesToProcess.end(); ++iitr) { ImageCornerPair& icp = *iitr; unsigned char* data=0; switch(icp.second) { case LEFT_BELOW: data = icp.first->data(0,0); break; case BELOW_RIGHT: data = icp.first->data(icp.first->s()-1,0); break; case RIGHT_ABOVE: data = icp.first->data(icp.first->s()-1,icp.first->t()-1); break; case ABOVE_LEFT: data = icp.first->data(0,icp.first->t()-1); break; default : break; } red += *(data++); green += *(data++); blue += *(data); } // divide them. red /= imagesToProcess.size(); green /= imagesToProcess.size(); blue /= imagesToProcess.size(); // apply colour to corners. for(iitr=imagesToProcess.begin(); iitr!=imagesToProcess.end(); ++iitr) { ImageCornerPair& icp = *iitr; unsigned char* data=0; switch(icp.second) { case LEFT_BELOW: data = icp.first->data(0,0); break; case BELOW_RIGHT: data = icp.first->data(icp.first->s()-1,0); break; case RIGHT_ABOVE: data = icp.first->data(icp.first->s()-1,icp.first->t()-1); break; case ABOVE_LEFT: data = icp.first->data(0,icp.first->t()-1); break; default : break; } *(data++) = red; *(data++) = green; *(data) = blue; } } if (heightFieldsToProcess.size()>1) { float height = 0; // accumulate heights. HeightFieldCornerList::iterator hitr; for(hitr=heightFieldsToProcess.begin(); hitr!=heightFieldsToProcess.end(); ++hitr) { HeightFieldCornerPair& hfcp = *hitr; switch(hfcp.second) { case LEFT_BELOW: height += hfcp.first->getHeight(0,0); break; case BELOW_RIGHT: height += hfcp.first->getHeight(hfcp.first->getNumColumns()-1,0); break; case RIGHT_ABOVE: height += hfcp.first->getHeight(hfcp.first->getNumColumns()-1,hfcp.first->getNumRows()-1); break; case ABOVE_LEFT: height += hfcp.first->getHeight(0,hfcp.first->getNumRows()-1); break; default : break; } } // divide them. height /= heightFieldsToProcess.size(); // apply height to corners. for(hitr=heightFieldsToProcess.begin(); hitr!=heightFieldsToProcess.end(); ++hitr) { HeightFieldCornerPair& hfcp = *hitr; switch(hfcp.second) { case LEFT_BELOW: hfcp.first->setHeight(0,0,height); break; case BELOW_RIGHT: hfcp.first->setHeight(hfcp.first->getNumColumns()-1,0,height); break; case RIGHT_ABOVE: hfcp.first->setHeight(hfcp.first->getNumColumns()-1,hfcp.first->getNumRows()-1,height); break; case ABOVE_LEFT: hfcp.first->setHeight(0,hfcp.first->getNumRows()-1,height); break; default : break; } } } } const char* edgeString(DataSet::DestinationTile::Position position) { switch(position) { case DataSet::DestinationTile::LEFT: return "left"; case DataSet::DestinationTile::BELOW: return "below"; case DataSet::DestinationTile::RIGHT: return "right"; case DataSet::DestinationTile::ABOVE: return "above"; default : return ""; } } void DataSet::DestinationTile::setTileComplete(bool complete) { _complete = complete; std::cout<<"setTileComplete("<_equalized[position2]=true; osg::Image* image1 = _imagery.valid()?_imagery->_image.get() : 0; osg::Image* image2 = tile2->_imagery.valid()?tile2->_imagery->_image.get() : 0; //std::cout<<"Equalizing edge "<_level<<"\t"<_tileX<<"\t"<_tileY<getPixelFormat()==image2->getPixelFormat() && image1->getDataType()==image2->getDataType() && image1->getPixelFormat()==GL_RGB && image1->getDataType()==GL_UNSIGNED_BYTE) { //std::cout<<" Equalizing image1= "<data()<<" with data2 = 0x"<<(int)image2->data()<data(0,1); // LEFT hand side delta1 = image1->getRowSizeInBytes(); data2 = image2->data(image2->s()-1,1); // RIGHT hand side delta2 = image2->getRowSizeInBytes(); num = (image1->t()==image2->t())?image2->t()-2:0; // note miss out corners. //std::cout<<" left "<data(1,0); // BELOW hand side delta1 = 3; data2 = image2->data(1,image2->t()-1); // ABOVE hand side delta2 = 3; num = (image1->s()==image2->s())?image2->s()-2:0; // note miss out corners. //std::cout<<" below "<data(image1->s()-1,1); // LEFT hand side delta1 = image1->getRowSizeInBytes(); data2 = image2->data(0,1); // RIGHT hand side delta2 = image2->getRowSizeInBytes(); num = (image1->t()==image2->t())?image2->t()-2:0; // note miss out corners. //std::cout<<" right "<data(1,image1->t()-1); // ABOVE hand side delta1 = 3; data2 = image2->data(1,0); // BELOW hand side delta2 = 3; num = (image1->s()==image2->s())?image2->s()-2:0; // note miss out corners. //std::cout<<" above "<_heightField.get():0; osg::HeightField* heightField2 = tile2->_terrain.valid()?tile2->_terrain->_heightField.get():0; if (heightField1 && heightField2) { //std::cout<<" Equalizing heightfield"<getHeight(0,1)); // LEFT hand side delta1 = heightField1->getNumColumns(); data2 = &(heightField2->getHeight(heightField2->getNumColumns()-1,1)); // RIGHT hand side delta2 = heightField2->getNumColumns(); num = (heightField1->getNumRows()==heightField2->getNumRows())?heightField1->getNumRows()-2:0; // note miss out corners. break; case BELOW: data1 = &(heightField1->getHeight(1,0)); // BELOW hand side delta1 = 1; data2 = &(heightField2->getHeight(1,heightField2->getNumRows()-1)); // ABOVE hand side delta2 = 1; num = (heightField1->getNumColumns()==heightField2->getNumColumns())?heightField1->getNumColumns()-2:0; // note miss out corners. break; case RIGHT: data1 = &(heightField1->getHeight(heightField1->getNumColumns()-1,1)); // LEFT hand side delta1 = heightField1->getNumColumns(); data2 = &(heightField2->getHeight(0,1)); // LEFT hand side delta2 = heightField2->getNumColumns(); num = (heightField1->getNumRows()==heightField2->getNumRows())?heightField1->getNumRows()-2:0; // note miss out corners. break; case ABOVE: data1 = &(heightField1->getHeight(1,heightField1->getNumRows()-1)); // ABOVE hand side delta1 = 1; data2 = &(heightField2->getHeight(1,0)); // BELOW hand side delta2 = 1; num = (heightField1->getNumColumns()==heightField2->getNumColumns())?heightField1->getNumColumns()-2:0; // note miss out corners. break; default : break; } for(int i=0;i_heightField.valid()) { osg::HeightField* hf = _terrain->_heightField.get(); // compute min max of height field float minHeight = hf->getHeight(0,0); float maxHeight = minHeight; for(unsigned int r=0;rgetNumRows();++r) { for(unsigned int c=0;cgetNumColumns();++c) { float h = hf->getHeight(c,r); if (hmaxHeight) maxHeight = h; } } if (minHeight==maxHeight) { std::cout<<"******* We have a flat tile ******* "< 1.2) numRows = (unsigned int)ceilf((float)numRows*ratio_y_over_x); else if (ratio_y_over_x < 0.8) numColumns = (unsigned int)ceilf((float)numColumns/ratio_y_over_x); hf->allocate(numColumns,numRows); hf->setOrigin(osg::Vec3(_extents.xMin(),_extents.yMin(),0.0f)); hf->setXInterval((_extents.xMax()-_extents.xMin())/(float)(numColumns-1)); hf->setYInterval((_extents.yMax()-_extents.yMin())/(float)(numRows-1)); for(unsigned int r=0;rsetHeight(c,r,minHeight); } } } } } osg::Node* DataSet::DestinationTile::createScene() { if (_dataSet->getGeometryType()==HEIGHT_FIELD) { return createHeightField(); } else { return createPolygonal(); } } osg::StateSet* DataSet::DestinationTile::createStateSet() { if (!_imagery.valid() || !_imagery->_image.valid()) return 0; std::string imageName(_name+".rgb"); _imagery->_image->setFileName(imageName.c_str()); osg::StateSet* stateset = new osg::StateSet; osg::Image* image = _imagery->_image.get(); osg::Texture2D* texture = new osg::Texture2D; texture->setImage(image); texture->setWrap(osg::Texture::WRAP_S,osg::Texture::CLAMP_TO_EDGE); texture->setWrap(osg::Texture::WRAP_T,osg::Texture::CLAMP_TO_EDGE); texture->setFilter(osg::Texture::MIN_FILTER,osg::Texture::LINEAR); texture->setFilter(osg::Texture::MAG_FILTER,osg::Texture::LINEAR); texture->setMaxAnisotropy(8); stateset->setTextureAttributeAndModes(0,texture,osg::StateAttribute::ON); bool inlineImageFile = _dataSet->getDestinationTileExtension()==".ive"; std::cout<<"__________________image->getPixelFormat()="<getPixelFormat()<getTextureType()==COMPRESSED_TEXTURE && (image->getPixelFormat()==GL_RGB || image->getPixelFormat()==GL_RGBA)) { texture->setInternalFormatMode(osg::Texture::USE_S3TC_DXT3_COMPRESSION); osg::ref_ptr state = new osg::State; texture->apply(*state); image->readImageFromCurrentTexture(); texture->setInternalFormatMode(osg::Texture::USE_IMAGE_DATA_FORMAT); std::cout<<">>>>>>>>>>>>>>>compress image.<<<<<<<<<<<<<<"<getTextureType()==RGB_16_BIT && image->getPixelFormat()==GL_RGB) { image->scaleImage(image->s(),image->t(),image->r(),GL_UNSIGNED_SHORT_5_6_5); } if (!inlineImageFile) { std::cout<<"Writing out imagery to "<_image,_imagery->_image->getFileName().c_str()); } return stateset; } osg::Node* DataSet::DestinationTile::createHeightField() { osg::ShapeDrawable* shapeDrawable = 0; if (_terrain.valid() && _terrain->_heightField.valid()) { std::cout<<"--- Have terrain build tile ----"<_heightField.get(); shapeDrawable = new osg::ShapeDrawable(hf); hf->setSkirtHeight(shapeDrawable->getBound().radius()*0.01f); } else { std::cout<<"**** No terrain to build tile from use flat terrain fallback ****"<allocate(2,2); hf->setOrigin(osg::Vec3(_extents.xMin(),_extents.yMin(),0.0f)); hf->setXInterval(_extents.xMax()-_extents.xMin()); hf->setYInterval(_extents.yMax()-_extents.yMin()); shapeDrawable = new osg::ShapeDrawable(hf); hf->setSkirtHeight(shapeDrawable->getBound().radius()*0.01f); } if (!shapeDrawable) return 0; osg::StateSet* stateset = createStateSet(); if (stateset) { shapeDrawable->setStateSet(stateset); } else { shapeDrawable->setColor(_dataSet->getDefaultColor()); } osg::Geode* geode = new osg::Geode; geode->addDrawable(shapeDrawable); return geode; } static osg::Vec3 computeLocalPosition(const osg::Matrixd& worldToLocal, double X, double Y, double Z) { return osg::Vec3(X*worldToLocal(0,0) + Y*worldToLocal(1,0) + Z*worldToLocal(2,0) + worldToLocal(3,0), X*worldToLocal(0,1) + Y*worldToLocal(1,1) + Z*worldToLocal(2,1) + worldToLocal(3,1), X*worldToLocal(0,2) + Y*worldToLocal(1,2) + Z*worldToLocal(2,2) + worldToLocal(3,2)); } osg::Node* DataSet::DestinationTile::createPolygonal() { std::cout<<"--------- DataSet::DestinationTile::createDrawableGeometry() ------------- "<getEllipsoidModel(); bool mapLatLongsToXYZ = _dataSet->getConvertFromGeographicToGeocentric() && et; bool useLocalToTileTransform = _dataSet->getUseLocalTileTransform(); osg::ref_ptr grid = 0; if (_terrain.valid() && _terrain->_heightField.valid()) { std::cout<<"--- Have terrain build tile ----"<_heightField.get(); } else { unsigned int minimumSize = 8; unsigned int numColumns = minimumSize; unsigned int numRows = minimumSize; if (mapLatLongsToXYZ) { float longitude_range = (_extents.xMax()-_extents.xMin()); float latitude_range = (_extents.yMax()-_extents.yMin()); if (longitude_range>45.0) numColumns = (unsigned int)ceilf((float)numColumns*sqrtf(longitude_range/45.0)); if (latitude_range>45.0) numRows = (unsigned int)ceilf((float)numRows*sqrtf(latitude_range/45.0)); std::cout<<"numColumns = "<getNumColumns(); unsigned int numRows = grid->getNumRows(); unsigned int numVerticesInBody = numColumns*numRows; unsigned int numVerticesInSkirt = createSkirt ? numColumns*2 + numRows*2 - 4 : 0; unsigned int numVertices = numVerticesInBody+numVerticesInSkirt; // create the geometry. osg::Geometry* geometry = new osg::Geometry; osg::Vec3Array& v = *(new osg::Vec3Array(numVertices)); osg::Vec2Array& t = *(new osg::Vec2Array(numVertices)); osg::UByte4Array& color = *(new osg::UByte4Array(1)); color[0].set(255,255,255,255); osg::ref_ptr n = new osg::Vec3Array(numVertices); // must use ref_ptr so the array isn't removed when smooothvisitor is used float skirtRatio = _dataSet->getSkirtRatio(); osg::Matrixd localToWorld; osg::Matrixd worldToLocal; osg::Vec3 skirtVector(0.0f,0.0f,0.0f); osg::Vec3 center_position(0.0f,0.0f,0.0f); osg::Vec3 center_normal(0.0f,0.0f,1.0f); osg::Vec3 transformed_center_normal(0.0f,0.0f,1.0f); double globe_radius = et ? et->getRadiusPolar() : 1.0; bool useClusterCullingCallback = mapLatLongsToXYZ; if (useLocalToTileTransform) { if (mapLatLongsToXYZ) { double midLong = grid->getOrigin().x()+grid->getXInterval()*((double)(numColumns-1))*0.5; double midLat = grid->getOrigin().y()+grid->getYInterval()*((double)(numRows-1))*0.5; double midZ = grid->getOrigin().z(); et->computeLocalToWorldTransformFromLatLongHeight(osg::DegreesToRadians(midLat),osg::DegreesToRadians(midLong),midZ,localToWorld); double minLong = grid->getOrigin().x(); double minLat = grid->getOrigin().y(); double minX,minY,minZ; et->convertLatLongHeightToXYZ(osg::DegreesToRadians(minLat),osg::DegreesToRadians(minLong),midZ, minX,minY,minZ); double midX,midY; et->convertLatLongHeightToXYZ(osg::DegreesToRadians(midLat),osg::DegreesToRadians(midLong),midZ, midX,midY,midZ); double length = sqrt((midX-minX)*(midX-minX) + (midY-minY)*(midY-minY)); skirtVector.set(0.0f,0.0f,-length*skirtRatio); center_normal.set(midX,midY,midZ); center_normal.normalize(); worldToLocal.invert(localToWorld); center_position = computeLocalPosition(worldToLocal,midX,midY,midZ); transformed_center_normal = osg::Matrixd::transform3x3(localToWorld,center_normal); } else { double midX = grid->getOrigin().x()+grid->getXInterval()*((double)(numColumns-1))*0.5; double midY = grid->getOrigin().y()+grid->getYInterval()*((double)(numRows-1))*0.5; double midZ = grid->getOrigin().z(); localToWorld.makeTranslate(midX,midY,midZ); worldToLocal.invert(localToWorld); skirtVector.set(0.0f,0.0f,-_extents.radius()*skirtRatio); } } else if (mapLatLongsToXYZ) { // no local to tile transform + mapping from lat+longs to XYZ so we need to use // a rotatated skirt vector - use the gravity vector. double midLong = grid->getOrigin().x()+grid->getXInterval()*((double)(numColumns-1))*0.5; double midLat = grid->getOrigin().y()+grid->getYInterval()*((double)(numRows-1))*0.5; double midZ = grid->getOrigin().z(); double X,Y,Z; et->convertLatLongHeightToXYZ(osg::DegreesToRadians(midLat),osg::DegreesToRadians(midLong),midZ,X,Y,Z); osg::Vec3 gravitationVector = et->computeLocalUpVector(X,Y,Z); gravitationVector.normalize(); skirtVector = gravitationVector * _extents.radius()* skirtRatio; } else { skirtVector.set(0.0f,0.0f,-_extents.radius()*skirtRatio); } unsigned int vi=0; unsigned int r,c; // populate the vertex/normal/texcoord arrays from the grid. double orig_X = grid->getOrigin().x(); double delta_X = grid->getXInterval(); double orig_Y = grid->getOrigin().y(); double delta_Y = grid->getYInterval(); double orig_Z = grid->getOrigin().z(); float min_dot_product = 1.0f; float max_cluster_culling_height = 0.0f; for(r=0;rgetHeight(c,r); double height = Z; if (mapLatLongsToXYZ) { et->convertLatLongHeightToXYZ(osg::DegreesToRadians(Y),osg::DegreesToRadians(X),Z, X,Y,Z); } if (useLocalToTileTransform) { v[vi] = computeLocalPosition(worldToLocal,X,Y,Z); } else { v[vi].set(X,Y,Z); } if (useClusterCullingCallback) { osg::Vec3 dv = v[vi] - center_position; double d = sqrt(dv.x()*dv.x() + dv.y()*dv.y() + dv.z()*dv.z()); double theta = acos( globe_radius/ (globe_radius + fabs(height)) ); double phi = 2.0 * asin (d*0.5/globe_radius); // d/globe_radius; double beta = theta+phi; double cutoff = osg::PI_2 - 0.1; //std::cout<<"theta="<getNormal(c,r); t[vi].x() = (c==numColumns-1)? 1.0f : (float)(c)/(float)(numColumns-1); t[vi].y() = (r==numRows-1)? 1.0f : (float)(r)/(float)(numRows-1); ++vi; } } //geometry->setUseDisplayList(false); geometry->setVertexArray(&v); if (n.valid()) { geometry->setNormalArray(n.get()); geometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX); } geometry->setColorArray(&color); geometry->setColorBinding(osg::Geometry::BIND_OVERALL); geometry->setTexCoordArray(0,&t); osg::DrawElementsUShort& drawElements = *(new osg::DrawElementsUShort(GL_TRIANGLES,2*3*(numColumns-1)*(numRows-1))); geometry->addPrimitiveSet(&drawElements); int ei=0; for(r=0;rgetNormalArray(); if (n.valid() && n->size()!=numVertices) n->resize(numVertices); #endif if (useClusterCullingCallback) { // set up cluster cullling, osg::ClusterCullingCallback* ccc = new osg::ClusterCullingCallback; ccc->set(center_position + transformed_center_normal*max_cluster_culling_height , transformed_center_normal, min_dot_product); geometry->setCullCallback(ccc); } osgUtil::Simplifier::IndexList pointsToProtectDuringSimplification; if (numVerticesInSkirt>0) { osg::DrawElementsUShort& skirtDrawElements = *(new osg::DrawElementsUShort(GL_QUAD_STRIP,2*numVerticesInSkirt+2)); geometry->addPrimitiveSet(&skirtDrawElements); int ei=0; int firstSkirtVertexIndex = vi; // create bottom skirt vertices r=0; for(c=0;c0;--c) { // assign indices to primitive set skirtDrawElements[ei++] = (r)*numColumns+c; skirtDrawElements[ei++] = vi; // mark these points as protected to prevent them from being removed during simplification pointsToProtectDuringSimplification.push_back((r)*numColumns+c); pointsToProtectDuringSimplification.push_back(vi); // add in the new point on the bottom of the skirt v[vi] = v[(r)*numColumns+c]+skirtVector; if (n.valid()) (*n)[vi] = (*n)[(r)*numColumns+c]; t[vi++] = t[(r)*numColumns+c]; } // create left skirt vertices c=0; for(r=numRows-1;r>0;--r) { // assign indices to primitive set skirtDrawElements[ei++] = (r)*numColumns+c; skirtDrawElements[ei++] = vi; // mark these points as protected to prevent them from being removed during simplification pointsToProtectDuringSimplification.push_back((r)*numColumns+c); pointsToProtectDuringSimplification.push_back(vi); // add in the new point on the bottom of the skirt v[vi] = v[(r)*numColumns+c]+skirtVector; if (n.valid()) (*n)[vi] = (*n)[(r)*numColumns+c]; t[vi++] = t[(r)*numColumns+c]; } skirtDrawElements[ei++] = 0; skirtDrawElements[ei++] = firstSkirtVertexIndex; } if (n.valid()) { geometry->setNormalArray(n.get()); geometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX); } osg::StateSet* stateset = createStateSet(); if (stateset) { geometry->setStateSet(stateset); } else { osg::Vec4Array* colours = new osg::Vec4Array(1); (*colours)[0] = _dataSet->getDefaultColor(); geometry->setColorArray(colours); geometry->setColorBinding(osg::Geometry::BIND_OVERALL); } osg::Geode* geode = new osg::Geode; geode->addDrawable(geometry); #if 1 osgDB::writeNodeFile(*geode,"NodeBeforeSimplification.osg"); #endif unsigned int targetMaxNumVertices = 2048; float sample_ratio = (numVertices <= targetMaxNumVertices) ? 1.0f : (float)targetMaxNumVertices/(float)numVertices; osgUtil::Simplifier simplifier(sample_ratio,geometry->getBound().radius()/2000.0f); simplifier.simplify(*geometry, pointsToProtectDuringSimplification); // this will replace the normal vector with a new one osgUtil::TriStripVisitor tsv; tsv.setMinStripSize(3); tsv.stripify(*geometry); if (useLocalToTileTransform) { osg::MatrixTransform* mt = new osg::MatrixTransform; mt->setMatrix(localToWorld); mt->addChild(geode); bool addLocalAxes = false; if (addLocalAxes) { float s = geode->getBound().radius()*0.5f; osg::MatrixTransform* scaleAxis = new osg::MatrixTransform; scaleAxis->setMatrix(osg::Matrix::scale(s,s,s)); scaleAxis->addChild(osgDB::readNodeFile("axes.osg")); mt->addChild(scaleAxis); } return mt; } else { return geode; } } void DataSet::DestinationTile::readFrom(CompositeSource* sourceGraph) { allocate(); std::cout<<"DestinationTile::readFrom() "<getSourceData(); if (data) { std::cout<<"DataSet::DestinationTile::readFrom -> SourceData::read() "<read(*_imagery); if (_terrain.valid()) data->read(*_terrain); } } optimizeResolution(); } void DataSet::DestinationTile::unrefData() { _imagery = 0; _terrain = 0; _models = 0; } void DataSet::DestinationTile::addRequiredResolutions(CompositeSource* sourceGraph) { for(CompositeSource::source_iterator itr(sourceGraph);itr.valid();++itr) { Source* source = itr->get(); if (source->intersects(*this)) { if (source->getType()==Source::IMAGE) { unsigned int numCols,numRows; double resX, resY; if (computeImageResolution(numCols,numRows,resX,resY)) { source->addRequiredResolution(resX,resY); } } if (source->getType()==Source::HEIGHT_FIELD) { unsigned int numCols,numRows; double resX, resY; if (computeTerrainResolution(numCols,numRows,resX,resY)) { source->addRequiredResolution(resX,resY); } } } } } void DataSet::CompositeDestination::computeNeighboursFromQuadMap() { // handle leaves for(TileList::iterator titr=_tiles.begin(); titr!=_tiles.end(); ++titr) { (*titr)->computeNeighboursFromQuadMap(); } // handle chilren for(ChildList::iterator citr=_children.begin(); citr!=_children.end(); ++citr) { (*citr)->computeNeighboursFromQuadMap(); } } void DataSet::CompositeDestination::addRequiredResolutions(CompositeSource* sourceGraph) { // handle leaves for(TileList::iterator titr=_tiles.begin(); titr!=_tiles.end(); ++titr) { (*titr)->addRequiredResolutions(sourceGraph); } // handle chilren for(ChildList::iterator citr=_children.begin(); citr!=_children.end(); ++citr) { (*citr)->addRequiredResolutions(sourceGraph); } } void DataSet::CompositeDestination::readFrom(CompositeSource* sourceGraph) { std::cout<<"CompositeDestination::readFrom() "<readFrom(sourceGraph); } // handle chilren for(ChildList::iterator citr=_children.begin(); citr!=_children.end(); ++citr) { (*citr)->readFrom(sourceGraph); } } void DataSet::CompositeDestination::equalizeBoundaries() { // handle leaves for(TileList::iterator titr=_tiles.begin(); titr!=_tiles.end(); ++titr) { (*titr)->equalizeBoundaries(); } // handle chilren for(ChildList::iterator citr=_children.begin(); citr!=_children.end(); ++citr) { (*citr)->equalizeBoundaries(); } } osg::Node* DataSet::CompositeDestination::createScene() { if (_children.empty() && _tiles.empty()) return 0; if (_children.empty() && _tiles.size()==1) return _tiles.front()->createScene(); if (_tiles.empty() && _children.size()==1) return _children.front()->createScene(); if (_type==GROUP) { osg::Group* group = new osg::Group; for(TileList::iterator titr=_tiles.begin(); titr!=_tiles.end(); ++titr) { osg::Node* node = (*titr)->createScene(); if (node) group->addChild(node); } // handle chilren for(ChildList::iterator citr=_children.begin(); citr!=_children.end(); ++citr) { osg::Node* node = (*citr)->createScene(); if (node) group->addChild(node); } return group; } // must be either a LOD or a PagedLOD typedef std::vector NodeList; typedef std::map RangeNodeListMap; RangeNodeListMap rangeNodeListMap; // insert local tiles into range map for(TileList::iterator titr=_tiles.begin(); titr!=_tiles.end(); ++titr) { osg::Node* node = (*titr)->createScene(); if (node) rangeNodeListMap[_maxVisibleDistance].push_back(node); } // handle chilren for(ChildList::iterator citr=_children.begin(); citr!=_children.end(); ++citr) { osg::Node* node = (*citr)->createScene(); if (node) rangeNodeListMap[(*citr)->_maxVisibleDistance].push_back(node); } osg::LOD* lod = new osg::LOD; unsigned int childNum = 0; for(RangeNodeListMap::reverse_iterator rnitr=rangeNodeListMap.rbegin(); rnitr!=rangeNodeListMap.rend(); ++rnitr) { float maxVisibleDistance = rnitr->first; NodeList& nodeList = rnitr->second; if (childNum==0) { // by deafult make the first child have a very visible distance so its always seen maxVisibleDistance = 1e10; } else { // set the previous child's minimum visible distance range lod->setRange(childNum-1,maxVisibleDistance,lod->getMaxRange(childNum-1)); } osg::Node* child = 0; if (nodeList.size()==1) { child = nodeList.front(); } else if (nodeList.size()>1) { osg::Group* group = new osg::Group; for(NodeList::iterator itr=nodeList.begin(); itr!=nodeList.end(); ++itr) { group->addChild(*itr); } child = group; } if (child) { lod->addChild(child,0,maxVisibleDistance); ++childNum; } } return lod; } bool DataSet::CompositeDestination::areSubTilesComplete() { for(ChildList::iterator citr=_children.begin(); citr!=_children.end(); ++citr) { for(TileList::iterator itr=(*citr)->_tiles.begin(); itr!=(*citr)->_tiles.end(); ++itr) { if (!(*itr)->getTileComplete()) { return false; } } } return true; } std::string DataSet::CompositeDestination::getSubTileName() { return _name+"_subtile"+_dataSet->getDestinationTileExtension(); } osg::Node* DataSet::CompositeDestination::createPagedLODScene() { if (_children.empty() && _tiles.empty()) return 0; if (_children.empty() && _tiles.size()==1) return _tiles.front()->createScene(); if (_tiles.empty() && _children.size()==1) return _children.front()->createPagedLODScene(); if (_type==GROUP) { osg::Group* group = new osg::Group; for(TileList::iterator titr=_tiles.begin(); titr!=_tiles.end(); ++titr) { osg::Node* node = (*titr)->createScene(); if (node) group->addChild(node); } // handle chilren for(ChildList::iterator citr=_children.begin(); citr!=_children.end(); ++citr) { osg::Node* node = (*citr)->createScene(); if (node) group->addChild(node); } return group; } // must be either a LOD or a PagedLOD typedef std::vector NodeList; // collect all the local tiles NodeList tileNodes; for(TileList::iterator titr=_tiles.begin(); titr!=_tiles.end(); ++titr) { osg::Node* node = (*titr)->createScene(); if (node) tileNodes.push_back(node); } float cutOffDistance = -FLT_MAX; for(ChildList::iterator citr=_children.begin(); citr!=_children.end(); ++citr) { cutOffDistance = osg::maximum(cutOffDistance,(*citr)->_maxVisibleDistance); } osg::PagedLOD* pagedLOD = new osg::PagedLOD; float farDistance = _dataSet->getMaximumVisibleDistanceOfTopLevel(); if (tileNodes.size()==1) { pagedLOD->addChild(tileNodes.front()); } else if (tileNodes.size()>1) { osg::Group* group = new osg::Group; for(NodeList::iterator itr=tileNodes.begin(); itr != tileNodes.end(); ++itr) { group->addChild(*itr); } pagedLOD->addChild(group); } cutOffDistance = pagedLOD->getBound().radius()*_dataSet->getRadiusToMaxVisibleDistanceRatio(); pagedLOD->setRange(0,cutOffDistance,farDistance); pagedLOD->setFileName(1,getSubTileName()); pagedLOD->setRange(1,0,cutOffDistance); if (pagedLOD->getNumChildren()>0) pagedLOD->setCenter(pagedLOD->getBound().center()); return pagedLOD; } osg::Node* DataSet::CompositeDestination::createSubTileScene() { if (_type==GROUP || _children.empty() || _tiles.empty()) return 0; // handle chilren typedef std::vector NodeList; NodeList nodeList; for(ChildList::iterator citr=_children.begin(); citr!=_children.end(); ++citr) { osg::Node* node = (*citr)->createPagedLODScene(); if (node) nodeList.push_back(node); } if (nodeList.size()==1) { return nodeList.front(); } else if (nodeList.size()>1) { osg::Group* group = new osg::Group; for(NodeList::iterator itr=nodeList.begin(); itr!=nodeList.end(); ++itr) { group->addChild(*itr); } return group; } else { return 0; } } void DataSet::CompositeDestination::unrefSubTileData() { for(CompositeDestination::ChildList::iterator citr=_children.begin(); citr!=_children.end(); ++citr) { (*citr)->unrefLocalData(); } } void DataSet::CompositeDestination::unrefLocalData() { for(CompositeDestination::TileList::iterator titr=_tiles.begin(); titr!=_tiles.end(); ++titr) { DestinationTile* tile = titr->get(); std::cout<<" unref tile level="<_level<<" X="<_tileX<<" Y="<_tileY<unrefData(); } } /////////////////////////////////////////////////////////////////////////////// DataSet::DataSet() { init(); _maximumVisiableDistanceOfTopLevel = 1e10; _radiusToMaxVisibleDistanceRatio = 7.0f; _verticalScale = 1.0f; _skirtRatio = 0.02f; _convertFromGeographicToGeocentric = false; _defaultColor.set(0.5f,0.5f,1.0f,1.0f); _databaseType = PagedLOD_DATABASE; _geometryType = POLYGONAL; _textureType = COMPRESSED_TEXTURE; _useLocalTileTransform = true; _decorateWithCoordinateSystemNode = true; setEllipsoidModel(new osg::EllipsoidModel()); } void DataSet::init() { static bool s_initialized = false; if (!s_initialized) { s_initialized = true; GDALAllRegister(); } } void DataSet::addSource(Source* source) { if (!source) return; if (!_sourceGraph.valid()) _sourceGraph = new CompositeSource; _sourceGraph->_sourceList.push_back(source); } void DataSet::addSource(CompositeSource* composite) { if (!composite) return; if (!_sourceGraph.valid()) _sourceGraph = new CompositeSource; _sourceGraph->_children.push_back(composite); } void DataSet::loadSources() { for(CompositeSource::source_iterator itr(_sourceGraph.get());itr.valid();++itr) { (*itr)->loadSourceData(); } } DataSet::CompositeDestination* DataSet::createDestinationGraph(CompositeDestination* parent, osg::CoordinateSystemNode* cs, const osg::BoundingBox& extents, unsigned int maxImageSize, unsigned int maxTerrainSize, unsigned int currentLevel, unsigned int currentX, unsigned int currentY, unsigned int maxNumLevels) { DataSet::CompositeDestination* destinationGraph = new DataSet::CompositeDestination(cs,extents); destinationGraph->_maxVisibleDistance = extents.radius()*getRadiusToMaxVisibleDistanceRatio(); // first create the topmost tile // create the name std::ostringstream os; os << _tileBasename << "_L"<_parent = parent; destinationGraph->_name = os.str(); destinationGraph->_level = currentLevel; destinationGraph->_tileX = currentX; destinationGraph->_tileY = currentY; destinationGraph->_dataSet = this; DestinationTile* tile = new DestinationTile; tile->_name = destinationGraph->_name; tile->_level = currentLevel; tile->_tileX = currentX; tile->_tileY = currentY; tile->_dataSet = this; tile->_cs = cs; tile->_extents = extents; tile->setMaximumImagerySize(maxImageSize,maxImageSize); tile->setMaximumTerrainSize(maxTerrainSize,maxTerrainSize); tile->computeMaximumSourceResolution(_sourceGraph.get()); insertTileToQuadMap(destinationGraph); if (currentLevel>=maxNumLevels-1) { // bottom level can't divide any further. destinationGraph->_tiles.push_back(tile); } else { destinationGraph->_type = LOD; destinationGraph->_tiles.push_back(tile); bool needToDivideX = false; bool needToDivideY = false; unsigned int texture_numColumns; unsigned int texture_numRows; double texture_dx; double texture_dy; if (tile->computeImageResolution(texture_numColumns,texture_numRows,texture_dx,texture_dy)) { if (texture_dx>tile->_imagery_maxSourceResolutionX) needToDivideX = true; if (texture_dy>tile->_imagery_maxSourceResolutionY) needToDivideY = true; } unsigned int dem_numColumns; unsigned int dem_numRows; double dem_dx; double dem_dy; if (tile->computeTerrainResolution(dem_numColumns,dem_numRows,dem_dx,dem_dy)) { if (dem_dx>tile->_terrain_maxSourceResolutionX) needToDivideX = true; if (dem_dy>tile->_terrain_maxSourceResolutionY) needToDivideY = true; } float xCenter = (extents.xMin()+extents.xMax())*0.5f; float yCenter = (extents.yMin()+extents.yMax())*0.5f; unsigned int newLevel = currentLevel+1; unsigned int newX = currentX*2; unsigned int newY = currentY*2; if (needToDivideX && needToDivideY) { float aspectRatio = (extents.yMax()- extents.yMin())/(extents.xMax()- extents.xMin()); if (aspectRatio>1.414) needToDivideX = false; else if (aspectRatio<.707) needToDivideY = false; } if (needToDivideX && needToDivideY) { std::cout<<"Need to Divide X + Y for level "<_children.push_back(createDestinationGraph(destinationGraph, cs, bottom_left, maxImageSize, maxTerrainSize, newLevel, newX, newY, maxNumLevels)); destinationGraph->_children.push_back(createDestinationGraph(destinationGraph, cs, bottom_right, maxImageSize, maxTerrainSize, newLevel, newX+1, newY, maxNumLevels)); destinationGraph->_children.push_back(createDestinationGraph(destinationGraph, cs, top_left, maxImageSize, maxTerrainSize, newLevel, newX, newY+1, maxNumLevels)); destinationGraph->_children.push_back(createDestinationGraph(destinationGraph, cs, top_right, maxImageSize, maxTerrainSize, newLevel, newX+1, newY+1, maxNumLevels)); // Set all there max distance to the same value to ensure the same LOD bining. float cutOffDistance = destinationGraph->_maxVisibleDistance*0.5f; for(CompositeDestination::ChildList::iterator citr=destinationGraph->_children.begin(); citr!=destinationGraph->_children.end(); ++citr) { (*citr)->_maxVisibleDistance = cutOffDistance; } } else if (needToDivideX) { std::cout<<"Need to Divide X only"<_children.push_back(createDestinationGraph(destinationGraph, cs, left, maxImageSize, maxTerrainSize, newLevel, newX, newY, maxNumLevels)); destinationGraph->_children.push_back(createDestinationGraph(destinationGraph, cs, right, maxImageSize, maxTerrainSize, newLevel, newX+1, newY, maxNumLevels)); // Set all there max distance to the same value to ensure the same LOD bining. float cutOffDistance = destinationGraph->_maxVisibleDistance*0.5f; for(CompositeDestination::ChildList::iterator citr=destinationGraph->_children.begin(); citr!=destinationGraph->_children.end(); ++citr) { (*citr)->_maxVisibleDistance = cutOffDistance; } } else if (needToDivideY) { std::cout<<"Need to Divide Y only"<_children.push_back(createDestinationGraph(destinationGraph, cs, bottom, maxImageSize, maxTerrainSize, newLevel, newX, newY, maxNumLevels)); destinationGraph->_children.push_back(createDestinationGraph(destinationGraph, cs, top, maxImageSize, maxTerrainSize, newLevel, newX, newY+1, maxNumLevels)); // Set all there max distance to the same value to ensure the same LOD bining. float cutOffDistance = destinationGraph->_maxVisibleDistance*0.5f; for(CompositeDestination::ChildList::iterator citr=destinationGraph->_children.begin(); citr!=destinationGraph->_children.end(); ++citr) { (*citr)->_maxVisibleDistance = cutOffDistance; } } else { std::cout<<"No Need to Divide"<getSourceData(); if (sd) { if (sd->_cs.valid()) { _destinationCoordinateSystem = sd->_cs; std::cout<<"Setting coordinate system to "<<_destinationCoordinateSystem->getCoordinateSystem()<getSourceData(); if (sd) { osg::BoundingBox local_extents(sd->getExtents(_intermediateCoordinateSystem.get())); std::cout<<"local_extents = xMin()"<computeNeighboursFromQuadMap(); } template struct DerefLessFunctor { bool operator () (const T& lhs, const T& rhs) { return lhs->getSortValue() > rhs->getSortValue(); } }; void DataSet::CompositeSource::setSortValueFromSourceDataResolution() { for(SourceList::iterator sitr=_sourceList.begin();sitr!=_sourceList.end();++sitr) { (*sitr)->setSortValueFromSourceDataResolution(); } for(ChildList::iterator citr=_children.begin();citr!=_children.end();++citr) { (*citr)->setSortValueFromSourceDataResolution(); } } void DataSet::CompositeSource::sort() { // sort the sources. std::sort(_sourceList.begin(),_sourceList.end(),DerefLessFunctor< osg::ref_ptr >()); // sort the composite sources internal data for(ChildList::iterator itr=_children.begin();itr!=_children.end();++itr) { (*itr)->sort(); } } void DataSet::updateSourcesForDestinationGraphNeeds() { std::string temporyFilePrefix("temporaryfile_"); // compute the resolutions of the source that are required. { _destinationGraph->addRequiredResolutions(_sourceGraph.get()); for(CompositeSource::source_iterator sitr(_sourceGraph.get());sitr.valid();++sitr) { Source* source = sitr->get(); std::cout<<"Source File "<getFileName()<getRequiredResolutions(); std::cout<<" resolutions.size() "<consolodateRequiredResolutions(); std::cout<<" consolodated resolutions.size() "<get(); if (source->needReproject(_intermediateCoordinateSystem.get())) { // do the reprojection to a tempory file. std::string newFileName = temporyFilePrefix + osgDB::getStrippedName(source->getFileName()) + ".tif"; Source* newSource = source->doReproject(newFileName,_intermediateCoordinateSystem.get()); // replace old source by new one. if (newSource) *itr = newSource; } } } // do sampling of data to required values. { for(CompositeSource::source_iterator itr(_sourceGraph.get());itr.valid();++itr) { Source* source = itr->get(); source->buildOverviews(); } } // sort the sources so that the lowest res tiles are drawn first. { for(CompositeSource::source_iterator itr(_sourceGraph.get());itr.valid();++itr) { Source* source = itr->get(); source->setSortValueFromSourceDataResolution(); std::cout<<"sort "<getFileName()<<" value "<getSortValue()<sort(); } std::cout<<"Using source_lod_iterator itr"<getFileName()<readFrom(_sourceGraph.get()); // for each DestinationTile equalize the boundaries so they all fit each other without gaps. _destinationGraph->equalizeBoundaries(); } else { // for each level // compute x and y range // from top row down to bottom row equalize boundairies a write out } } void DataSet::_readRow(Row& row) { std::cout<<"_readRow "<second; for(CompositeDestination::TileList::iterator titr=cd->_tiles.begin(); titr!=cd->_tiles.end(); ++titr) { DestinationTile* tile = titr->get(); std::cout<<" reading tile level="<_level<<" X="<_tileX<<" Y="<_tileY<readFrom(_sourceGraph.get()); } } } void DataSet::_equalizeRow(Row& row) { std::cout<<"_equalizeRow "<second; for(CompositeDestination::TileList::iterator titr=cd->_tiles.begin(); titr!=cd->_tiles.end(); ++titr) { DestinationTile* tile = titr->get(); std::cout<<" equalizing tile level="<_level<<" X="<_tileX<<" Y="<_tileY<equalizeBoundaries(); tile->setTileComplete(true); } } } void DataSet::_writeRow(Row& row) { std::cout<<"_writeRow "<second; CompositeDestination* parent = cd->_parent; if (parent) { if (!parent->getSubTilesGenerated() && parent->areSubTilesComplete()) { osg::ref_ptr node = parent->createSubTileScene(); std::string filename = parent->getSubTileName(); if (node.valid()) { std::cout<<" writeSubTile filename="<setSubTilesGenerated(true); parent->unrefSubTileData(); } else { std::cout<<" failed to writeSubTile node for tile, filename="< node = cd->createPagedLODScene(); if (_decorateWithCoordinateSystemNode) { node = decorateWithCoordinateSystemNode(node.get()); } //std::string filename = cd->_name + _tileExtension; std::string filename = _tileBasename+_tileExtension; if (node.valid()) { std::cout<<" writeNodeFile = "<_level<<" X="<_tileX<<" Y="<_tileY<<" filename="<createScene(); if (_decorateWithCoordinateSystemNode) { _rootNode = decorateWithCoordinateSystemNode(_rootNode.get()); } osgDB::writeNodeFile(*_rootNode,filename); } else // _databaseType==PagedLOD_DATABASE { // for each level build read and write the rows. for(QuadMap::iterator qitr=_quadMap.begin(); qitr!=_quadMap.end(); ++qitr) { Level& level = qitr->second; // skip is level is empty. if (level.empty()) continue; std::cout<<"New level"<second); Level::iterator curr_itr = prev_itr; ++curr_itr; for(; curr_itr!=level.end(); ++curr_itr) { _readRow(curr_itr->second); _equalizeRow(prev_itr->second); _writeRow(prev_itr->second); prev_itr = curr_itr; } _equalizeRow(prev_itr->second); _writeRow(prev_itr->second); } } } else { std::cout<<"Error: no scene graph to output, no file written."<