Outdated file. Correct one attached. Attachments will be the death of me.
-- Chris 'Xenon' Hanson, omo sanza lettere Xenon AlphaPixel.com PixelSense Landsat processing now available! http://www.alphapixel.com/demos/ "There is no Truth. There is only Perception. To Perceive is to Exist." - Xen
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2009 Robert Osfield * * This application is open source and may be redistributed and/or modified * freely and without restriction, both in commericial and non commericial * applications, as long as this copyright notice is maintained. * * This application 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. * */ /* file: src/osgPlugins/modifyterrain/ReaderWriterMODIFYTERRAIN.cpp * author: Chris Hanson http://xenon.arcticus.com/ [email protected] 2009-06-30 (based on ReaderWriterSCALE.cpp) * copyright: (C) 2009 Chris Hanson * license: OpenSceneGraph Public License (OSGPL) */ #include <osg/Notify> #include <osg/Matrix> #include <osg/MatrixTransform> #include <osg/ProxyNode> #include <osgDB/ReaderWriter> #include <osgDB/FileNameUtils> #include <osgDB/Registry> #include <osgDB/ReadFile> #include <osgDB/WriteFile> #include <osgTerrain/TerrainTile> #include <stdio.h> #include <sstream> #include <iomanip> #include <assert.h> #define EXTENSION_NAME "modifyterrain" /////////////////////////////////////////////////////////////////////////// /** * An OSG reader plugin for the ".modifyterrain" pseudo-loader/saver, which * modifies the elevation of heightfields in the scene graph according to * user-specified rules and options. * This pseudo-loader/saver make it possible to deform terrain during the VPB * build-and-save stage, or during the OSG load (or save) stage * * Usage: <modelfile.ext>.modifyterrain * where: * <modelfile.ext> = a model filename. * * Operations and options are passed in the osgDB::ReaderWriter::Options * Usage: * MODIFYTERRAIN_ELEV_ADD +5 Add 5 meters of elevation uniformly to all heightfield vertices * MODIFYTERRAIN_ELEV_ADD -5 Subtract 5 meters of elevation uniformly from all heightfield vertices * MODIFYTERRAIN_ELEV_SCALE 2 Multiply all heightfield vertices elevation values by 2.0 * MODIFYTERRAIN_ELEV_MAX 2000 Adjust any heightfield vertices elevation values that exceed 2000m to be 2000m * MODIFYTERRAIN_ELEV_MIN 0 Adjust any heightfield vertices elevation values that are lower than 0m to be 0m * MODIFYTERRAIN_ELEV_MIN 0:-9999 Adjust any heightfield vertices elevation values that are lower than 0m to be -9999m * MODIFYTERRAIN_ELEV_REPLACE 0:400 Adjust any heightfield vertices elevation values that are 0m to be 400m * * Operators are applied in a fixed order independent of the order they are listed in the options: * Replace, Add, Scale, Min, Max * * There is one common "Replace" variable, so the two-arg versions of Max and Min cannot be employed * simultaneously with each other, or with Find/Replace. * * example: osgviewer -O "MODIFYTERRAIN_ELEV_ADD 5" terrain.osg.modifyterrain */ /** * This functor is used to allow customization of the terrain modification process as driven by * the ModifyTerrainHeightField() and ModifyTerrainVisitor. * * This is an abstract base class and you _must_ override the pure virtual operator () to get anything done. * * <<<>>> Should go in osgTerrain namespace/library. */ class ModifyTerrainFunctor { public: /** * Connects ModifyTerrainVisitor to code that actually calculates elevation changes. * Called by ModifyTerrainVisitor::apply * This is NOT const so that you can track internal state changes and cache data in private member * variables of the ModifyTerrainFunctor-derived subclass. * This IS pure virtual -- you MUST override it in order to do anything. * * @param [in] terrainTileNode The tile currently being modified. * * @return True if any elevation samples were actually altered from their original value within this tile. * * @see ModifyTerrainVisitor */ virtual bool operator() (osgTerrain::TerrainTile &terrainTileNode) = 0; /** * Connects ModifyTerrainHeightField to code that actually calculates elevation changes. * Called by ModifyTerrainHeightField * This is NOT const so that you can track internal state changes and cache data in private member * variables of the ModifyTerrainFunctor-derived subclass. * This IS pure virtual -- you MUST override it in order to do anything. * * @param [in] inLocation Geospatial location where new elevation is requested. * @param [in] locator osgTerrain::Locator with info about how to interpret inLocation * @param [out] outElev Modified elevation from elevation calculations. Will always be written even if no change. * * @return True if successful (outElev was written) false if failed (outElev is undefined). * * @see ModifyTerrainHeightField */ virtual bool modifyTerrainElevation(const osg::Vec3d inLocation, const osg::Vec2d sampleSize, osgTerrain::Locator *locator, double &outElev) = 0; ModifyTerrainFunctor() {} virtual ~ModifyTerrainFunctor() {} }; /** * Terrain modification parameters * * Currently extracts from an osgDB::ReaderWriter::Options object. * This is specific to the modifyterrain pseudoloader. */ struct ModifyTerrainArgs { ModifyTerrainArgs(): _elevDeltaAdd(0.0), _elevScale(1.0), _elevMax(DBL_MAX), _elevMin(DBL_MIN), _elevFindReplaceEnabled(false), _elevFind(DBL_MAX), _elevReplace(DBL_MAX) { } // basic math operations double _elevDeltaAdd; double _elevScale; double _elevMax; double _elevMin; // single-value find/replace bool _elevFindReplaceEnabled; double _elevFind; double _elevReplace; /** * Extract args from an osgDB::ReaderWriter::Options * * @param [in] options osgDB::ReaderWriter::Options normally supplied by readNode or writeNode * * @return True if successful, false if failed. */ bool parseOptions(const osgDB::ReaderWriter::Options* options) { if(options) { std::istringstream iss(options->getOptionString()); std::string opt; while (iss >> opt) { if(opt=="MODIFYTERRAIN_ELEV_ADD") { iss >> _elevDeltaAdd; } // if if(opt=="MODIFYTERRAIN_ELEV_SCALE") { iss >> _elevScale; } // if if(opt=="MODIFYTERRAIN_ELEV_MAX") { std::string parseString; std::istringstream numericParseStream; iss >> parseString; // parse optional second value size_t colonPos; if((colonPos = parseString.find_first_of(':')) != parseString.npos) { // parse _elevMax value numericParseStream.str(parseString.substr(0, colonPos)); // grab up to colon numericParseStream >> _elevMax; // convert to _elevMax // parse _elevReplace value numericParseStream.str(parseString.substr(colonPos + 1, parseString.npos)); // grab all after colon numericParseStream >> _elevReplace; // convert to _elevReplace } // if else { // parse single _elevMax value numericParseStream.str(parseString); numericParseStream >> _elevMax; // convert to _elevMax } // else } // if if(opt=="MODIFYTERRAIN_ELEV_MIN") { std::string parseString; std::istringstream numericParseStream; iss >> parseString; // parse optional second value size_t colonPos; if((colonPos = parseString.find_first_of(':')) != parseString.npos) { // parse _elevMin value numericParseStream.str(parseString.substr(0, colonPos)); // grab up to colon numericParseStream >> _elevMin; // convert to _elevMin // parse _elevReplace value numericParseStream.str(parseString.substr(colonPos + 1, parseString.npos)); // grab all after colon numericParseStream >> _elevReplace; // convert to _elevReplace } // if else { // parse single _elevMin value numericParseStream.str(parseString); numericParseStream >> _elevMin; // convert to _elevMin } // else } // if if(opt=="MODIFYTERRAIN_ELEV_REPLACE") { std::string parseString; std::istringstream numericParseStream; iss >> parseString; // must have two values to proceed with find/replace size_t colonPos; if((colonPos = parseString.find_first_of(':')) != parseString.npos) { _elevFindReplaceEnabled = true; // parse _elevFind value numericParseStream.str(parseString.substr(0, colonPos)); // grab up to colon numericParseStream >> _elevFind; // convert to _elevFind // parse _elevReplace value numericParseStream.str(parseString.substr(colonPos + 1, parseString.npos)); // grab all after colon numericParseStream >> _elevReplace; // convert to _elevReplace } // if } // if } // while } // if options osg::notify(osg::DEBUG_INFO) << " elevDeltaAdd = " << _elevDeltaAdd << std::endl; osg::notify(osg::DEBUG_INFO) << " elevScale = " << _elevScale << std::endl; osg::notify(osg::DEBUG_INFO) << " elevMax = " << _elevMax << std::endl; osg::notify(osg::DEBUG_INFO) << " elevMin = " << _elevMin << std::endl; if(_elevFindReplaceEnabled) { osg::notify(osg::INFO) << " elevFindReplace enabled." << std::endl; osg::notify(osg::INFO) << " elevFind = " << _elevFind << std::endl; osg::notify(osg::INFO) << " elevReplace = " << _elevReplace << std::endl; } // if return true; } // parseOptions }; // ModifyTerrainArgs /** * Basic standalone function to calculate a new elevation given the original elevation and a * ModifyTerrainArgs object with the parameters/rules in effect. * Called by ModifyTerrainHeightField() via ModifyTerrainFunctor::modifyTerrainElevation() * This is specific to the modifyterrain pseudoloader. * * @param [in] inLocation Geospatial location where new elevation is requested. (not yet used by this implementation) * @param [in] locator osgTerrain::Locator with info about how to interpret inLocation (not yet used by this implementation) * @param [out] outElev Modified elevation from elevation calculations. Will always be written even if no change. * @param [in] modifyArgs Parameters to define the elevation modifications. * * @return True if successful (outElev was written) false if failed (outElev is undefined). * * @see ModifyTerrainHeightField() */ bool modifyTerrainElevationUsingArgs(const osg::Vec3d inLocation, const osg::Vec2d sampleSize, osgTerrain::Locator *locator, double &outElev, const ModifyTerrainArgs &modifyArgs) { outElev = inLocation.z(); // start with the original elevation // perform find/replace before all other operations if(modifyArgs._elevFindReplaceEnabled) { if(outElev == modifyArgs._elevFind) outElev = modifyArgs._elevReplace; } // if // perform add outElev += modifyArgs._elevDeltaAdd; // perform scale outElev *= modifyArgs._elevScale; // perform min clip if(outElev < modifyArgs._elevMin) { if(modifyArgs._elevReplace != DBL_MAX) { // replace with alternate value outElev = modifyArgs._elevReplace; } // if replace else { outElev = modifyArgs._elevMin; // simple min processing } // else normal min } // if min clip enabled // perform max clip if(outElev > modifyArgs._elevMax) { if(modifyArgs._elevReplace != DBL_MAX) { // replace with alternate value outElev = modifyArgs._elevReplace; } // if replace else { outElev = modifyArgs._elevMax; // simple max processing } // else normal max } // if max clip enabled return true; } // modifyTerrainElevationUsingArgs /** * Modify a Terrain's heightfield utilizing ModifyTerrainFunctor::modifyTerrainElevation() * Calls modifyTerrainElevation() to do the dirty work. * Called by ModifyTerrainVisitor::apply by way of ModifyTerrainFunctor::operator () * * <<<>>> This should be moved to osgTerrain library/namespace as it it useful for generalized * scenegraph terrain modification outside of just the .modifyterrain pseudoloader * * @param [in] terrainTileNode The osgTerrain::TerrainTile heightfield tile to modify. * @param [in] modifyFunctor The ModifyTerrainFunctor-derived functor object used to * invoke the actual elevation change calculation. * * @return Number of elevation samples actually altered from their original value within this tile. * * @see ModifyTerrainVisitor * @see ModifyTerrainFunctor * @see modifyTerrainElevation */ unsigned int ModifyTerrainHeightField(osgTerrain::TerrainTile &terrainTileNode, ModifyTerrainFunctor *modifyFunctor) { unsigned int numSamplesAltered = 0; osg::notify(osg::DEBUG_INFO) <<"ModifyTerrainHeightField"<<std::endl; if(osgTerrain::HeightFieldLayer *hfLayer = dynamic_cast<osgTerrain::HeightFieldLayer*>(terrainTileNode.getElevationLayer())) { if(hfLayer->getNumColumns() > 0 && hfLayer->getNumRows() > 0) { if(osg::HeightField *heightField = hfLayer->getHeightField()) { // Calculate X & Y coordinates of the vertices for spatially-variant elevation changes osg::notify(osg::DEBUG_INFO) << std::fixed << std::setprecision(8) <<" XInterval:"<<heightField->getXInterval()<<" YInterval:"<<heightField->getYInterval()<<std::endl; osg::notify(osg::DEBUG_INFO) << std::fixed << std::setprecision(8) <<" hfOriginX:"<<heightField->getOrigin().x()<<" hfOriginY:"<<heightField->getOrigin().y()<<std::endl; for(unsigned int yLoop = 0; yLoop < heightField->getNumRows(); yLoop++) { for(unsigned int xLoop = 0; xLoop < heightField->getNumColumns(); xLoop++) { double inElev, outElev = 0.0; // destLocation must be Vec3d (not Vec3f) to preserve geospatial precision osg::Vec3d destLocation; osg::Vec2d sampleSize(heightField->getXInterval(), heightField->getYInterval()); destLocation = heightField->getVertex(xLoop, yLoop); // calculate full local coords including origin inElev = destLocation.z(); // invoke the modifyFunctor's modifyTerrainElevation virtual method to calculate elevation // change for this individual vertex modifyFunctor->modifyTerrainElevation(destLocation, sampleSize, terrainTileNode.getLocator(), outElev); if(inElev != outElev) { numSamplesAltered++; osg::notify(osg::DEBUG_INFO) <<" IN:"<<inElev<<" OUT:"<<outElev<<std::endl; // DEBUG_INFO } // if Elev changed heightField->setHeight(xLoop, yLoop, (float)outElev); } // for xLoop } // for yLoop if(numSamplesAltered != 0) hfLayer->dirty(); } // if heightField } // if Rows and Columns } // if hfLayer osg::notify(osg::DEBUG_INFO) <<" Number of Samples Modified "<<numSamplesAltered<<std::endl; return(numSamplesAltered); } // ModifyTerrainHeightField /** * Implementation of ModifyTerrainFunctor to utilize ModifyTerrainArgs object and modifyTerrainElevationUsingArgs(). * * This should stay in ReaderWriterMODIFYTERRAIN.cpp * * @see ModifyTerrainVisitor * @see modifyTerrainElevationUsingArgs * @see ModifyTerrainArgs */ struct ModifyTerrainUsingArgsFunctor : public ModifyTerrainFunctor { ModifyTerrainUsingArgsFunctor(ModifyTerrainArgs *modifyArgs) : _modifyArgs(modifyArgs) {} /** * Connects ModifyTerrainVisitor to ModifyTerrainHeightField. * Calls ModifyTerrainHeightField() to do the dirty work. * Called by ModifyTerrainVisitor::apply * Passes 'this' (a class derived from ModifyTerrainFunctor) to callee to supply necessary parameters * * @param [in] terrainTileNode The tile currently being modified. * * @return True if any elevation samples were actually altered from their original value within this tile. * * @see ModifyTerrainVisitor * @see modifyTerrainElevationUsingArgs */ bool operator() (osgTerrain::TerrainTile &terrainTileNode) {return(ModifyTerrainHeightField(terrainTileNode, this) > 0);} /** * Connects ModifyTerrainHeightField to modifyTerrainElevationUsingArgs() that actually calculates elevation changes. * Called by ModifyTerrainHeightField * * @param [in] inLocation Geospatial location where new elevation is requested. * @param [in] locator osgTerrain::Locator with info about how to interpret inLocation * @param [out] outElev Modified elevation from elevation calculations. Will always be written even if no change. * * @return True if successful (outElev was written) false if failed (outElev is undefined). * * @see ModifyTerrainHeightField * @see modifyTerrainElevationUsingArgs */ bool modifyTerrainElevation(const osg::Vec3d inLocation, const osg::Vec2d sampleSize, osgTerrain::Locator *locator, double &outElev) {return(modifyTerrainElevationUsingArgs(inLocation, sampleSize, locator, outElev, *_modifyArgs));} ~ModifyTerrainUsingArgsFunctor() {} // nothing needed -- no dynamic members to clean up ModifyTerrainArgs *_modifyArgs; }; /** * This visitor takes a ModifyTerrainFunctor object and traverses all terrains in * a subgraph, performing the desired modifications to each. * Counts how many Terrain Tiles report they had any elevation samples altered. * Uses ModifyTerrainFunctor to do the dirty work. * Called from ReaderWriterMODIFYTERRAIN::readNode and ReaderWriterMODIFYTERRAIN::writeNode * * <<<>>> This should be moved to osgTerrain library/namespace as it it useful for generalized * terrain modification outside of .modifyterrain. * * @see ModifyTerrainFunctor * @see ReaderWriterMODIFYTERRAIN::readNode() * @see ReaderWriterMODIFYTERRAIN::writeNode() */ class ModifyTerrainVisitor : public osg::NodeVisitor { public: ModifyTerrainVisitor(ModifyTerrainFunctor &action): osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), _numTerrainsModified(0), _action(action) { osg::notify(osg::DEBUG_INFO) <<"Running ModifyTerrainVisitor..."<<std::endl; } ~ModifyTerrainVisitor() { osg::notify(osg::DEBUG_INFO) <<" Number of Terrains Modified "<<_numTerrainsModified<<std::endl; } /** * Traverses all terrains in a subgraph, performing the desired modifications to each. * Counts how many Terrain Tiles report they had any elevation samples altered. * Uses ModifyTerrainFunctor to do the dirty work. * Called from ReaderWriterMODIFYTERRAIN::readNode and ReaderWriterMODIFYTERRAIN::writeNode * * @param [in] node The subgraph to begin traversing. * * @see ModifyTerrainFunctor * @see ReaderWriterMODIFYTERRAIN::readNode() * @see ReaderWriterMODIFYTERRAIN::writeNode() */ void apply(osg::Node& node) { osgTerrain::TerrainTile *terrainTileNode = dynamic_cast<osgTerrain::TerrainTile*>(&node); if(terrainTileNode) { if(_action.operator ()(*terrainTileNode)) { terrainTileNode->dirtyBound(); // dirty bounds in case Z value changed bound sphere terrainTileNode->setDirty(true); // dirty geometry _numTerrainsModified++; } // if } // if terrainTileNode traverse(node); } // apply unsigned int _numTerrainsModified; ModifyTerrainFunctor &_action; }; // ModifyTerrainVisitor /** * This visitor traverses all Nodes in a subgraph, altering any PagedLOD/ProxyNode * nodes to incorporate the suffix in their filenames. * Counts how many PagedLOD or ProxyNodes had their suffix modified. * * <<<>>> This should be moved into a more generalied location within OSG itself * as it is not specific to either .modifyterrain or terrain databases in general. */ class ModifyDatabaseSuffixVisitor : public osg::NodeVisitor { public: ModifyDatabaseSuffixVisitor(std::string databaseSuffix): osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), _numNodesModified(0), _databaseSuffix(databaseSuffix) { osg::notify(osg::DEBUG_INFO) <<"Running ModifyDatabaseSuffixVisitor..."<<std::endl; } ~ModifyDatabaseSuffixVisitor() { osg::notify(osg::DEBUG_INFO) <<" Number of Nodes Modified "<<_numNodesModified<<std::endl; } /** * Alters any PagedLOD nodes to incorporate the database * suffix in their filenames. * Counts how many PagedLOD nodes had their suffix modified. * * @param [in] node The PagedLOD node to alter. */ void apply(osg::PagedLOD& node) { osg::notify(osg::DEBUG_INFO) <<"ModifyDatabaseSuffixVisitor::apply PagedLOD" << std::endl; // set the PagedLOD's suffix node.setDatabaseSuffix(_databaseSuffix); _numNodesModified++; traverse(node); } // apply /** * Alters any ProxyNode nodes to incorporate the database * suffix in their filenames. * Counts how many ProxyNode nodes had their suffix modified. * * @param [in] node The ProxyNode node to alter. */ void apply(osg::ProxyNode& node) { osg::notify(osg::DEBUG_INFO) <<"ModifyDatabaseSuffixVisitor::apply ProxyNode" << std::endl; // set the ProxyNode's suffix node.setDatabaseSuffix(_databaseSuffix); _numNodesModified++; traverse(node); } // apply unsigned int _numNodesModified; std::string _databaseSuffix; }; // ModifyDatabaseSuffixVisitor /** * Actual (pseudo)loader/saver for ModifyTerrain * Uses ModifyTerrainVisitor to traverse scene graph to do the work. * Uses ModifyTerrainArgs to parse and store the parameters for the operation. * @sideeffect writeNode modifies the data being written, contrary to what the const-ness of it would suggest. * * @see ModifyTerrainVisitor * @see ModifyTerrainArgs */ class ReaderWriterMODIFYTERRAIN : public osgDB::ReaderWriter { public: ReaderWriterMODIFYTERRAIN() { supportsExtension(EXTENSION_NAME,"ModifyTerrain Pseudo loader/saver"); } virtual const char* className() const { return "terrain-modifying pseudo-loader/saver"; } virtual bool acceptsExtension(const std::string& extension) const { return osgDB::equalCaseInsensitive( extension, EXTENSION_NAME ); } virtual ReadResult readNode(const std::string& fileName, const osgDB::ReaderWriter::Options* options) const { std::string ext = osgDB::getLowerCaseFileExtension(fileName); if( !acceptsExtension(ext) ) return ReadResult::FILE_NOT_HANDLED; osg::notify(osg::DEBUG_INFO) << "ReaderWriterMODIFYTERRAIN readNode( \"" << fileName << "\" )" << std::endl; // strip the pseudo-loader extension std::string subFileName = osgDB::getNameLessExtension( fileName ); if( subFileName.empty()) { osg::notify(osg::WARN) << "Missing subfilename for " EXTENSION_NAME " pseudo-loader" << std::endl; return ReadResult::FILE_NOT_HANDLED; } ModifyTerrainArgs modifyArgs; if(options) { modifyArgs.parseOptions(options); } // if options osg::notify(osg::INFO) << " readNodeFile subFileName = \"" << subFileName << "\"" << std::endl; // recursively load the subfile. osg::Node *node = osgDB::readNodeFile( subFileName, options ); if( !node ) { // propagate the read failure upwards osg::notify(osg::WARN) << "ModifyTerrain Subfile \"" << subFileName << "\" could not be loaded" << std::endl; return ReadResult::FILE_NOT_HANDLED; } // visit the subgraph, altering each heightfield's vertices ModifyTerrainUsingArgsFunctor modifyFunctor(&modifyArgs); // apply the terrain modifications via visitor ModifyTerrainVisitor modTerrain(modifyFunctor); node->accept(modTerrain); // apply the .modifyterrain suffix to all PagedLODs that might have just been loaded ModifyDatabaseSuffixVisitor pseudoLoaderFunctor(".modifyterrain"); node->accept(pseudoLoaderFunctor); osg::notify(osg::DEBUG_INFO) << "ReaderWriterMODIFYTERRAIN readNode exiting." << std::endl; return node; } // readNode /** * Modify the terrain tiles as desired and write the scene graph. * * @sideeffect In this ReaderWriter plugin, writeNode modifies the data being written, * which is in direct contradiction of the const-ness of the writeNode prototype * for node, so we must forcibly cast away the const-ness of the osg::Node argument. * There appears to be no other way to accomplish this, short of making a temporary full * in-memory copy of the scenegraph being saved, and modifying that, and that is likely * to be prohibitive for most applications. In any case, VPB typically discards the saved * scenegraph after saving and won't mind. If your usage disagrees, you'll need to make * a scartch copy of the scenegraph before calling the modifyterrain pseudosaver. * Caveat Emptor. */ virtual WriteResult writeNode(const osg::Node& node,const std::string& fileName,const Options* options =NULL) const { osg::Node& nonconstnode = const_cast<osg::Node&>(node); std::string ext = osgDB::getLowerCaseFileExtension(fileName); if( !acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED; osg::notify(osg::INFO) << "ReaderWriterMODIFYTERRAIN writeNode( \"" << fileName << "\" )" << std::endl; // strip the pseudo-saver extension std::string subFileName = osgDB::getNameLessExtension( fileName ); if( subFileName.empty()) { osg::notify(osg::WARN) << "Missing subfilename for " EXTENSION_NAME " pseudo-saver" << std::endl; return WriteResult::FILE_NOT_HANDLED; } ModifyTerrainArgs modifyArgs; if(options) { modifyArgs.parseOptions(options); } // if options // visit the subgraph, altering each heightfield's vertices ModifyTerrainUsingArgsFunctor modifyFunctor(&modifyArgs); ModifyTerrainVisitor modTerrain(modifyFunctor); nonconstnode.accept(modTerrain); osg::notify(osg::INFO) << " readNode subFileName = \"" << subFileName << "\"" << std::endl; // recursively save the subfile. if(!osgDB::writeNodeFile(nonconstnode, subFileName, options )) { // log the failure osg::notify(osg::WARN) << "ModifyTerrain Subfile \"" << subFileName << "\" could not be saved" << std::endl; return WriteResult::FILE_NOT_HANDLED; } // return success return WriteResult::FILE_SAVED; } // writeNode }; // Add ourself to the Registry to instantiate the reader/writer. REGISTER_OSGPLUGIN(modifyterrain, ReaderWriterMODIFYTERRAIN) /*EOF*/
_______________________________________________ osg-submissions mailing list [email protected] http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org
