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

Reply via email to