Chris 'Xenon' Hanson wrote:
>   I could refactor this so that recursive osgconv can move forward without 
> pseudoloader
> suffix support. My latest suffix-support proposal in "PagedLOD/ProxyNode 
> design
> discussion" would have a getCombinedFileName() method, so maybe we should 
> decide that
> issue first, and osgconv would basically slide in right afterward.

  Ok, here is an osgconv that incorporates the recursion feature and does not 
require any
changes to PagedLOD/ProxyNode to function.

-- 
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
#include <stdio.h>
#include <queue>

#include <osg/ArgumentParser>
#include <osg/ApplicationUsage>
#include <osg/Group>
#include <osg/Notify>
#include <osg/Vec3>
#include <osg/ProxyNode>
#include <osg/Geometry>
#include <osg/Texture2D>
#include <osg/Texture3D>
#include <osg/BlendFunc>
#include <osg/Timer>

#include <osgDB/Registry>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgDB/FileNameUtils>
#include <osgDB/FileUtils>
#include <osgDB/ReaderWriter>
#include <osgDB/PluginQuery>

#include <osgUtil/Optimizer>
#include <osgUtil/Simplifier>
#include <osgUtil/SmoothingVisitor>

#include <osgViewer/GraphicsWindow>
#include <osgViewer/Version>

#include <iostream>

#include "OrientationConverter.h"

typedef std::vector<std::string> FileNameList;



class ConvertBatch {
public:
        void setInputBasePath(const std::string newInputBasePath) 
{inputBasePath = newInputBasePath;}
        void addInputFileName(const std::string newInputFileName) 
{inputNames.push_back(newInputFileName);}
        void setOutputPath(const std::string newOutputPath) {outputPath = 
newOutputPath;}
        void setOutputFileName(const std::string newOutputFileName, const 
std::string optionalSuffix = "", bool stripAllExtensions = true)
        {
                std::string outNameWork = newOutputFileName; // make a 
modifiable copy
                // replace extension/suffix if needed
                if(!optionalSuffix.empty())
                {
                        outNameWork = osgDB::getNameLessExtension(outNameWork); 
// take off the extension
                        if(stripAllExtensions)
                        {
                                
while(!osgDB::getFileExtension(outNameWork).empty())
                                {
                                        outNameWork = 
osgDB::getNameLessExtension(outNameWork); // take off remaining extension
                                } // while
                        } // if
                        outNameWork += optionalSuffix; // append new 
suffix/extension
                } // if
                outputFileName = outNameWork;
        }
        // ensure folder isn't already in set to simplify processing later
        void addFolderToCreate(const std::string newFolderToCreate)
        {
                // is the new value NOT in the set yet?
                if(find(foldersToCreate.begin(), foldersToCreate.end(), 
newFolderToCreate) == foldersToCreate.end())
                {
                        foldersToCreate.push_back(newFolderToCreate);
                } // if not in set
        }
        std::string removeInputFromBack(void) {const std::string result = 
inputNames.back(); inputNames.pop_back(); return(result);} // I don't think 
this can be done any cleaner

        std::string getOutputFileName(void) const {return(outputFileName);}
        std::string getOutputPath(void) const {return(outputPath);}
        std::string getInputBasePath(void) const {return(inputBasePath);}

        std::string inputBasePath;
        FileNameList inputNames;
        std::string outputPath;
        std::string outputFileName;
        FileNameList foldersToCreate;
}; // ConvertBatch

typedef std::queue<ConvertBatch> ConvertQueue;


class MyGraphicsContext {
    public:
        MyGraphicsContext()
        {
            osg::ref_ptr<osg::GraphicsContext::Traits> traits = new 
osg::GraphicsContext::Traits;
            traits->x = 0;
            traits->y = 0;
            traits->width = 1;
            traits->height = 1;
            traits->windowDecoration = false;
            traits->doubleBuffer = false;
            traits->sharedContext = 0;
            traits->pbuffer = true;

            _gc = osg::GraphicsContext::createGraphicsContext(traits.get());

            if (!_gc)
            {
                osg::notify(osg::NOTICE)<<"Failed to create pbuffer, failing 
back to normal graphics window."<<std::endl;
                
                traits->pbuffer = false;
                _gc = osg::GraphicsContext::createGraphicsContext(traits.get());
            }

            if (_gc.valid()) 
            {
                _gc->realize();
                _gc->makeCurrent();
                if (dynamic_cast<osgViewer::GraphicsWindow*>(_gc.get()))
                {
                    std::cout<<"Realized graphics window for OpenGL 
operations."<<std::endl;
                }
                else
                {
                    std::cout<<"Realized pbuffer for OpenGL 
operations."<<std::endl;
                }
            }
        }
        
        bool valid() const { return _gc.valid() && _gc->isRealized(); }
        
    private:
        osg::ref_ptr<osg::GraphicsContext> _gc;
};


class RecursiveConversionVisitor
    : public osg::NodeVisitor
{
public:

    RecursiveConversionVisitor(ConvertQueue &masterConversionSet, std::string 
parentFileInputFolderPath, std::string parentFileOutputFolderPath, std::string 
baseNameSuffix)
        : osg::NodeVisitor( osg::NodeVisitor::TRAVERSE_ALL_CHILDREN ), 
_masterConversionSet(masterConversionSet),
                _parentFileInputFolderPath(parentFileInputFolderPath), 
_parentFileOutputFolderPath(parentFileOutputFolderPath),
                _baseNameSuffix(baseNameSuffix) {
    }

    virtual void apply( osg::ProxyNode & Node )
    {
                osg::notify(osg::DEBUG_INFO) << "RecursiveConversionVisitor 
ProxyNode DatabasePath=" << Node.getDatabasePath() << std::endl;
                osg::notify(osg::DEBUG_INFO) << " Children=" << 
Node.getNumFileNames() << std::endl;
                // The DatabasePath must be cleared during a recursive convert 
otherwise
                // it will embed a hard, fully-rooted path into the output 
file, making it
                // non-portable
                Node.setDatabasePath("");
                // we're not allowed to mess with the 
FileNameDatabaseRequestList object so we iterate by subscript
                for(unsigned int childLoop=0; childLoop<Node.getNumFileNames(); 
childLoop++)
                {
                        const std::string childFileName = 
Node.getFileName(childLoop);
                        if(!childFileName.empty())
                        {
                                osg::notify(osg::DEBUG_INFO) << "  Adding child 
" << childLoop << ": " << childFileName << std::endl;
                                // add this child file to the list of dependent 
input filenames yet to convert
                                ConvertBatch dependentFileNameList;
                                
dependentFileNameList.addInputFileName(childFileName); // only add one filename 
to the convert batch
                                const std::string childBaseName = 
osgDB::getSimpleFileName(childFileName); // just the filename, no path
                                // set input path
                                
dependentFileNameList.setInputBasePath(_parentFileInputFolderPath); // load 
dependents relative to their parent
                                // determine if input file had a path portion
                                std::string childFolderName = 
osgDB::concatPaths(_parentFileOutputFolderPath, 
osgDB::getFilePath(childFileName)); // combine parent-output path with child's 
input path
                                // set output path
                                
dependentFileNameList.setOutputPath(childFolderName);
                                // set output name
                                
dependentFileNameList.setOutputFileName(childBaseName, _baseNameSuffix); // 
apply suffix modification while adding output filename
                                // apply possible suffix name change to 
original node here
                                const std::string originalNodePath = 
osgDB::getFilePath(childFileName); // preserve original path
                                // combine original path and suffix-modified 
output filename
                                Node.setFileName(childLoop, 
osgDB::concatPaths(originalNodePath, 
dependentFileNameList.getOutputFileName()));
                                // if this child is in a subfolder of some 
sort, add the folder to the list of folders to create
                                osg::notify(osg::DEBUG_INFO) << "  Adding child 
folder: " << childFolderName << std::endl;
                                
dependentFileNameList.addFolderToCreate(childFolderName);
                                
_masterConversionSet.push(dependentFileNameList); // add the 
dependentFileNameList to the queue-of-FileNameLists (which adds a copy)
                        } // if
                } // for
    } // apply proxyNode

    virtual void apply( osg::PagedLOD & Node )
    {
                osg::notify(osg::DEBUG_INFO) << "RecursiveConversionVisitor 
PagedLOD DatabasePath=" << Node.getDatabasePath() << std::endl;
                osg::notify(osg::DEBUG_INFO) << " Children=" << 
Node.getNumFileNames() << std::endl;
                // The DatabasePath must be cleared during a recursive convert 
otherwise
                // it will embed a hard, fully-rooted path into the output 
file, making it
                // non-portable
                Node.setDatabasePath("");
                // we're not allowed to mess with the PerRangeData object so we 
iterate by subscript
                // entire loop copied from Proxynode, above
                for(unsigned int childLoop=0; childLoop<Node.getNumFileNames(); 
childLoop++)
                {
                        const std::string childFileName = 
Node.getFileName(childLoop);
                        if(!childFileName.empty())
                        {
                                osg::notify(osg::DEBUG_INFO) << "  Adding child 
" << childLoop << ": " << childFileName << std::endl;
                                // add this child file to the list of dependent 
input filenames yet to convert
                                ConvertBatch dependentFileNameList;
                                
dependentFileNameList.addInputFileName(childFileName); // only add one filename 
to the convert batch
                                const std::string childBaseName = 
osgDB::getSimpleFileName(childFileName); // just the filename, no path
                                // set input path
                                
dependentFileNameList.setInputBasePath(_parentFileInputFolderPath); // load 
dependents relative to their parent
                                // determine if input file had a path portion
                                std::string childFolderName = 
osgDB::concatPaths(_parentFileOutputFolderPath, 
osgDB::getFilePath(childFileName)); // combine parent-output path with child's 
input path
                                // set output path
                                
dependentFileNameList.setOutputPath(childFolderName);
                                // set output name
                                
dependentFileNameList.setOutputFileName(childBaseName, _baseNameSuffix); // 
apply suffix modification while adding output filename
                                // apply possible suffix name change to 
original node here
                                const std::string originalNodePath = 
osgDB::getFilePath(childFileName); // preserve original path
                                // combine original path and suffix-modified 
output filename
                                Node.setFileName(childLoop, 
osgDB::concatPaths(originalNodePath, 
dependentFileNameList.getOutputFileName()));
                                // if this child is in a subfolder of some 
sort, add the folder to the list of folders to create
                                osg::notify(osg::DEBUG_INFO) << "  Adding child 
folder: " << childFolderName << std::endl;
                                
dependentFileNameList.addFolderToCreate(childFolderName);
                                
_masterConversionSet.push(dependentFileNameList); // add the 
dependentFileNameList to the queue-of-FileNameLists (which adds a copy)
                        } // if
                } // for
    } // apply pagedLOD

    virtual void apply( osg::Node & node )
    {
        traverse( node );
    }
private:
        ConvertQueue &_masterConversionSet;
        std::string _parentFileInputFolderPath, _parentFileOutputFolderPath;
        std::string _baseNameSuffix;
}; // RecursiveConversionVisitor

class DefaultNormalsGeometryVisitor
    : public osg::NodeVisitor
{
public:

    DefaultNormalsGeometryVisitor()
        : osg::NodeVisitor( osg::NodeVisitor::TRAVERSE_ALL_CHILDREN ) {
    }

    virtual void apply( osg::Geode & geode )
    {
        for( unsigned int ii = 0; ii < geode.getNumDrawables(); ++ii )
        {
            osg::ref_ptr< osg::Geometry > geometry = dynamic_cast< 
osg::Geometry * >( geode.getDrawable( ii ) );
            if( geometry.valid() )
            {
                osg::ref_ptr< osg::Vec3Array > newnormals = new osg::Vec3Array;
                newnormals->push_back( osg::Z_AXIS );
                geometry->setNormalArray( newnormals.get() );
                geometry->setNormalBinding( osg::Geometry::BIND_OVERALL );
            }
        }
    }

    virtual void apply( osg::Node & node )
    {
        traverse( node );
    }

};

class CompressTexturesVisitor : public osg::NodeVisitor
{
public:

    CompressTexturesVisitor(osg::Texture::InternalFormatMode 
internalFormatMode):
        osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
        _internalFormatMode(internalFormatMode) {}

    virtual void apply(osg::Node& node)
    {
        if (node.getStateSet()) apply(*node.getStateSet());
        traverse(node);
    }
    
    virtual void apply(osg::Geode& node)
    {
        if (node.getStateSet()) apply(*node.getStateSet());
        
        for(unsigned int i=0;i<node.getNumDrawables();++i)
        {
            osg::Drawable* drawable = node.getDrawable(i);
            if (drawable && drawable->getStateSet()) 
apply(*drawable->getStateSet());
        }
        
        traverse(node);
    }
    
    virtual void apply(osg::StateSet& stateset)
    {
        // search for the existence of any texture object attributes
        for(unsigned int i=0;i<stateset.getTextureAttributeList().size();++i)
        {
            osg::Texture* texture = 
dynamic_cast<osg::Texture*>(stateset.getTextureAttribute(i,osg::StateAttribute::TEXTURE));
            if (texture)
            {
                _textureSet.insert(texture);
            }
        }
    }
    
    void compress()
    {
        MyGraphicsContext context;
        if (!context.valid())
        {
            osg::notify(osg::NOTICE)<<"Error: Unable to create graphics 
context, problem with running osgViewer-"<<osgViewerGetVersion()<<", cannot run 
compression."<<std::endl;
            return;
        }

        osg::ref_ptr<osg::State> state = new osg::State;

        for(TextureSet::iterator itr=_textureSet.begin();
            itr!=_textureSet.end();
            ++itr)
        {
            osg::Texture* texture = const_cast<osg::Texture*>(itr->get());
            
            osg::Texture2D* texture2D = dynamic_cast<osg::Texture2D*>(texture);
            osg::Texture3D* texture3D = dynamic_cast<osg::Texture3D*>(texture);
            
            osg::ref_ptr<osg::Image> image = texture2D ? texture2D->getImage() 
: (texture3D ? texture3D->getImage() : 0);
            if (image.valid() && 
                (image->getPixelFormat()==GL_RGB || 
image->getPixelFormat()==GL_RGBA) &&
                (image->s()>=32 && image->t()>=32))
            {
                texture->setInternalFormatMode(_internalFormatMode);
                
                // need to disable the unref after apply, other the image could 
go out of scope.
                bool unrefImageDataAfterApply = 
texture->getUnRefImageDataAfterApply();
                texture->setUnRefImageDataAfterApply(false);
                
                // get OpenGL driver to create texture from image.
                texture->apply(*state);

                // restore the original setting
                texture->setUnRefImageDataAfterApply(unrefImageDataAfterApply);

                image->readImageFromCurrentTexture(0,true);

                
texture->setInternalFormatMode(osg::Texture::USE_IMAGE_DATA_FORMAT);
            }
        }
    }

    void write(const std::string &dir)
    {
        for(TextureSet::iterator itr=_textureSet.begin();
            itr!=_textureSet.end();
            ++itr)
        {
            osg::Texture* texture = const_cast<osg::Texture*>(itr->get());
            
            osg::Texture2D* texture2D = dynamic_cast<osg::Texture2D*>(texture);
            osg::Texture3D* texture3D = dynamic_cast<osg::Texture3D*>(texture);
            
            osg::ref_ptr<osg::Image> image = texture2D ? texture2D->getImage() 
: (texture3D ? texture3D->getImage() : 0);
            if (image.valid())
            {
                std::string name = osgDB::getStrippedName(image->getFileName());
                name += ".dds";
                image->setFileName(name);
                std::string path = dir.empty() ? name : osgDB::concatPaths(dir, 
name);
                osgDB::writeImageFile(*image, path);
                osg::notify(osg::NOTICE) << "Image written to '" << path << 
"'." << std::endl;
            }
        }
    }
    
    typedef std::set< osg::ref_ptr<osg::Texture> > TextureSet;
    TextureSet                          _textureSet;
    osg::Texture::InternalFormatMode    _internalFormatMode;
    
};


class FixTransparencyVisitor : public osg::NodeVisitor
{
public:

    enum FixTransparencyMode
    {
        NO_TRANSPARANCY_FIXING,
        MAKE_OPAQUE_TEXTURE_STATESET_OPAQUE,
        MAKE_ALL_STATESET_OPAQUE
    };

    FixTransparencyVisitor(FixTransparencyMode 
mode=MAKE_OPAQUE_TEXTURE_STATESET_OPAQUE):
        osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
        _numTransparent(0),
        _numOpaque(0),
        _numTransparentMadeOpaque(0),
        _mode(mode)
    {
        std::cout<<"Running FixTransparencyVisitor..."<<std::endl;
    }
        
    ~FixTransparencyVisitor()
    {
        std::cout<<"  Number of Transparent StateSet 
"<<_numTransparent<<std::endl;
        std::cout<<"  Number of Opaque StateSet "<<_numOpaque<<std::endl;
        std::cout<<"  Number of Transparent State made Opaque 
"<<_numTransparentMadeOpaque<<std::endl;
    }

    virtual void apply(osg::Node& node)
    {
        if (node.getStateSet()) isTransparent(*node.getStateSet());
        traverse(node);
    }
    
    virtual void apply(osg::Geode& node)
    {
        if (node.getStateSet()) isTransparent(*node.getStateSet());
        
        for(unsigned int i=0;i<node.getNumDrawables();++i)
        {
            osg::Drawable* drawable = node.getDrawable(i);
            if (drawable && drawable->getStateSet()) 
isTransparent(*drawable->getStateSet());
        }
        
        traverse(node);
    }
    
    virtual bool isTransparent(osg::StateSet& stateset)
    {
        bool hasTranslucentTexture = false;
        bool hasBlendFunc = 
dynamic_cast<osg::BlendFunc*>(stateset.getAttribute(osg::StateAttribute::BLENDFUNC))!=0;
        bool hasTransparentRenderingHint = 
stateset.getRenderingHint()==osg::StateSet::TRANSPARENT_BIN;
        bool hasDepthSortBin = 
(stateset.getRenderBinMode()==osg::StateSet::USE_RENDERBIN_DETAILS)?(stateset.getBinName()=="DepthSortedBin"):false;
        bool hasTexture = false;


        // search for the existence of any texture object attributes
        for(unsigned int i=0;i<stateset.getTextureAttributeList().size();++i)
        {
            osg::Texture* texture = 
dynamic_cast<osg::Texture*>(stateset.getTextureAttribute(i,osg::StateAttribute::TEXTURE));
            if (texture)
            {
                hasTexture = true;
                for (unsigned int im=0;im<texture->getNumImages();++im)
                {
                    osg::Image* image = texture->getImage(im);
                    if (image && image->isImageTranslucent()) 
hasTranslucentTexture = true;   
                }
            }
        }
        
        if (hasTranslucentTexture || hasBlendFunc || 
hasTransparentRenderingHint || hasDepthSortBin)
        {
            ++_numTransparent;
            
            bool makeNonTransparent = false;

            switch(_mode)
            {
            case(MAKE_OPAQUE_TEXTURE_STATESET_OPAQUE):
                if (hasTexture && !hasTranslucentTexture)
                {
                    makeNonTransparent = true;
                }
                break;
            case(MAKE_ALL_STATESET_OPAQUE):
                makeNonTransparent = true;
                break;
            default:
                makeNonTransparent = false;
                break;
            }
        
            if (makeNonTransparent)
            {
                stateset.removeAttribute(osg::StateAttribute::BLENDFUNC);
                stateset.removeMode(GL_BLEND);
                stateset.setRenderingHint(osg::StateSet::DEFAULT_BIN);
                ++_numTransparentMadeOpaque;
            }


            return true;
        }
        else
        {
            ++_numOpaque;
            return false;
        }
    }

    unsigned int _numTransparent;
    unsigned int _numOpaque;    
    unsigned int _numTransparentMadeOpaque;
    FixTransparencyMode _mode;
};

class PruneStateSetVisitor : public osg::NodeVisitor
{
public:

    PruneStateSetVisitor():
        osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
        _numStateSetRemoved(0)
    {
        std::cout<<"Running PruneStateSet..."<<std::endl;
    }
        
    ~PruneStateSetVisitor()
    {
        std::cout<<"  Number of StateState removed 
"<<_numStateSetRemoved<<std::endl;
    }

    virtual void apply(osg::Node& node)
    {
        if (node.getStateSet())
        {
            node.setStateSet(0);
            ++_numStateSetRemoved;
        }
        traverse(node);
    }
    
    virtual void apply(osg::Geode& node)
    {
        if (node.getStateSet())
        {
            node.setStateSet(0);
            ++_numStateSetRemoved;
        }
        
        for(unsigned int i=0;i<node.getNumDrawables();++i)
        {
            osg::Drawable* drawable = node.getDrawable(i);
            if (drawable && drawable->getStateSet())
            {
                drawable->setStateSet(0);
                ++_numStateSetRemoved;
            }
        }
        
        traverse(node);
    }
    
    unsigned int _numStateSetRemoved;
};

/** Add missing colours to osg::Geometry.*/
class AddMissingColoursToGeometryVisitor : public osg::NodeVisitor
{
public:

    
AddMissingColoursToGeometryVisitor():osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
 {}

    virtual void apply(osg::Geode& geode)
    {
        for(unsigned int i=0;i<geode.getNumDrawables();++i)
        {
            osg::Geometry* geometry = 
dynamic_cast<osg::Geometry*>(geode.getDrawable(i));
            if (geometry)
            {
                if (geometry->getColorArray()==0 || 
geometry->getColorArray()->getNumElements()==0)
                {
                    osg::Vec4Array* colours = new osg::Vec4Array(1);
                    (*colours)[0].set(1.0f,1.0f,1.0f,1.0f);
                    geometry->setColorArray(colours);
                    geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
                }
            }
        }
    }

    virtual void apply(osg::Node& node) { traverse(node); }

};


static void usage( const char *prog, const char *msg )
{
    if (msg)
    {
        osg::notify(osg::NOTICE)<< std::endl;
        osg::notify(osg::NOTICE) << msg << std::endl;
    }

    // basic usage
    osg::notify(osg::NOTICE)<< std::endl;
    osg::notify(osg::NOTICE)<<"usage:"<< std::endl;
    osg::notify(osg::NOTICE)<<"    " << prog << " [options] infile1 [infile2 
...] outfile"<< std::endl;
    osg::notify(osg::NOTICE)<< std::endl;

    // print env options - especially since optimizer is always _on_
    osg::notify(osg::NOTICE)<<"environment:" << std::endl;
    osg::ApplicationUsage::UsageMap um = 
osg::ApplicationUsage::instance()->getEnvironmentalVariables();
    std::string envstring;
    osg::ApplicationUsage::instance()->getFormattedString( envstring, um );
    osg::notify(osg::NOTICE)<<envstring << std::endl;

    // print tool options
    osg::notify(osg::NOTICE)<<"options:"<< std::endl;
    osg::notify(osg::NOTICE)<<"    -R suffix          - Recursively convert 
files referred to by PagedLOD/ProxyNode, appending suffix to output name before 
extension."<< std::endl;
    osg::notify(osg::NOTICE)<< std::endl;
    osg::notify(osg::NOTICE)<<"    -O option          - ReaderWriter option"<< 
std::endl;
    osg::notify(osg::NOTICE)<< std::endl;
    osg::notify(osg::NOTICE)<<"    --compressed       - Enable the usage of 
compressed textures,"<< std::endl;
    osg::notify(osg::NOTICE)<<"                         defaults to OpenGL ARB 
compressed textures."<< std::endl;
    osg::notify(osg::NOTICE)<<"    --compressed-arb   - Enable the usage of 
OpenGL ARB compressed textures"<< std::endl;
    osg::notify(osg::NOTICE)<<"    --compressed-dxt1  - Enable the usage of 
S3TC DXT1 compressed textures"<< std::endl;
    osg::notify(osg::NOTICE)<<"    --compressed-dxt3  - Enable the usage of 
S3TC DXT3 compressed textures"<< std::endl;
    osg::notify(osg::NOTICE)<<"    --compressed-dxt5  - Enable the usage of 
S3TC DXT5 compressed textures"<< std::endl;
    osg::notify(osg::NOTICE)<< std::endl;
    osg::notify(osg::NOTICE)<<"    --fix-transparency  - fix stateset which are 
curerntly declared as transprent,"<< std::endl;
    osg::notify(osg::NOTICE)<<"                         but should be opaque. 
Defaults to using the "<< std::endl;
    osg::notify(osg::NOTICE)<<"                         fixTranspancyMode 
MAKE_OPAQUE_TEXTURE_STATESET_OPAQUE."<< std::endl;
    osg::notify(osg::NOTICE)<<"    --fix-transparency-mode <mode_string>  - fix 
stateset which are curerntly declared as"<< std::endl;
    osg::notify(osg::NOTICE)<<"                         transprent but should 
be opaque. The mode_string determines"<< std::endl;
    osg::notify(osg::NOTICE)<<"                         algorithm is used to 
fix the transparency, options are:    "<< std::endl;
    osg::notify(osg::NOTICE)<<"                                 
MAKE_OPAQUE_TEXTURE_STATESET_OPAQUE,"<<std::endl;
    osg::notify(osg::NOTICE)<<"                                 
MAKE_ALL_STATESET_OPAQUE."<<std::endl;

    osg::notify(osg::NOTICE)<< std::endl;
    osg::notify(osg::NOTICE)<<"    -l libraryName     - load plugin of name 
libraryName"<< std::endl;
    osg::notify(osg::NOTICE)<<"                         i.e. -l osgdb_pfb"<< 
std::endl;
    osg::notify(osg::NOTICE)<<"                         Useful for loading 
reader/writers which can load"<< std::endl;
    osg::notify(osg::NOTICE)<<"                         other file formats in 
addition to its extension."<< std::endl;
    osg::notify(osg::NOTICE)<<"    -e extensionName   - load reader/wrter 
plugin for file extension"<< std::endl;
    osg::notify(osg::NOTICE)<<"                         i.e. -e pfb"<< 
std::endl;
    osg::notify(osg::NOTICE)<<"                         Useful short hand for 
specifying full library name as"<< std::endl;
    osg::notify(osg::NOTICE)<<"                         done with -l above, as 
it automatically expands to the"<< std::endl;
    osg::notify(osg::NOTICE)<<"                         full library name 
appropriate for each platform."<< std::endl;
    osg::notify(osg::NOTICE)<<"    -o orientation     - Convert geometry from 
input files to output files."<< std::endl;
    osg::notify(osg::NOTICE)<<
                              "                         Format of orientation 
argument must be the following:\n"
                              "\n"
                              "                             X1,Y1,Z1-X2,Y2,Z2\n"
                              "                         or\n"
                              "                             degrees-A0,A1,A2\n"
                              "\n"
                              "                         where X1,Y1,Z1 
represent the UP vector in the input\n"
                              "                         files and X2,Y2,Z2 
represent the UP vector of the\n"
                              "                         output file, or degrees 
is the rotation angle in degrees\n"
                              "                         around axis (A0,A1,A2). 
 For example, to convert a model\n"
                              "                         built in a Y-Up 
coordinate system to a model with a Z-up\n"
                              "                         coordinate system, the 
argument may look like\n"
                              "\n"
                              "                             0,1,0-0,0,1"
                              "\n"
                              "                          or\n"
                              "                             -90-1,0,0\n"
                              "\n" << std::endl;
    osg::notify(osg::NOTICE)<<"    -t translation     - Convert spatial 
position of output files.  Format of\n"
                              "                         translation argument 
must be the following :\n"
                              "\n"
                              "                             X,Y,Z\n"
                              "\n"
                              "                         where X, Y, and Z 
represent the coordinates of the\n"
                              "                         absolute position in 
world space\n"
                              << std::endl;
    osg::notify(osg::NOTICE)<<"    --simplify n       - Run simplifier on prior 
to output. Argument must be a" << std::endl
                            <<"                         normalized value for 
the resultant percentage reduction." << std::endl
                            <<"                         Example: --simplify .5 
will produce an 50 reduced model." << std::endl
                            << std::endl;
    osg::notify(osg::NOTICE)<<"    -s scale           - Scale size of model.  
Scale argument must be the \n"
                              "                         following :\n"
                              "\n"
                              "                             SX,SY,SZ\n"
                              "\n"
                              "                         where SX, SY, and SZ 
represent the scale factors\n"
                              "                         Caution: Scaling will 
be done in destination orientation\n"
                              << std::endl;
    osg::notify(osg::NOTICE)<<"    --smooth           - Smooth the surface by 
regenerating surface normals on\n"
                              "                         all geometry"<< 
std::endl;
    osg::notify(osg::NOTICE)<<"    --addMissingColors - Adding a white color 
value to all geometry that don't have\n"
                              "                         their own color values 
(--addMissingColours also accepted)."<< std::endl;
    osg::notify(osg::NOTICE)<<"    --overallNormal    - Replace normals with a 
single overall normal."<< std::endl;
    osg::notify(osg::NOTICE)<<"    --enable-object-cache - Enable caching of 
objects, images, etc."<< std::endl;

    osg::notify( osg::NOTICE ) << std::endl;
    osg::notify( osg::NOTICE ) <<
        "    --formats          - Lists all supported formats and their 
supported options." << std::endl;
    osg::notify( osg::NOTICE ) <<
        "    --format <format>  - Displays information about the specified 
<format>, where <format>\n"
        "                         is the file extension, such as \"flt\"." << 
std::endl;
    osg::notify( osg::NOTICE ) <<
        "    --plugins          - Lists all supported plugin files." << 
std::endl;
    osg::notify( osg::NOTICE ) <<
        "    --plugin >plugin>  - Displays information about the specified 
<plugin>, where <plugin>\n"
        "                         is the plugin's full path and file name." << 
std::endl;
}


int main( int argc, char **argv )
{
        // record original source directory
        std::string originalCurrentWorkingDirectory;
        originalCurrentWorkingDirectory = osgDB::getCurrentWorkingDirectory();
        osg::notify(osg::DEBUG_INFO) << "osgconv 
originalCurrentWorkingDirectory=" << originalCurrentWorkingDirectory << 
std::endl;

        // use an ArgumentParser object to manage the program arguments.
    osg::ArgumentParser arguments(&argc,argv);
    
    // set up the usage document, in case we need to print out how to use this 
program.
    
arguments.getApplicationUsage()->setApplicationName(arguments.getApplicationName());
    
arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+"
 is a utility for converting between various input and output databases 
formats.");
    
arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+"
 [options] filename ...");
    arguments.getApplicationUsage()->addCommandLineOption("-h or 
--help","Display command line parameters");
    arguments.getApplicationUsage()->addCommandLineOption("--help-env","Display 
environmental variables available");
    //arguments.getApplicationUsage()->addCommandLineOption("--formats","List 
supported file formats");
    //arguments.getApplicationUsage()->addCommandLineOption("--plugins","List 
database olugins");


    // if user request help write it out to cout.
    if (arguments.read("-h") || arguments.read("--help"))
    { 
        usage( arguments.getApplicationName().c_str(), 0 );
        //arguments.getApplicationUsage()->write(std::cout);
        return 1;
    }
    
    if (arguments.read("--help-env"))
    { 
        arguments.getApplicationUsage()->write(std::cout, 
osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE);
        return 1;
    }
    
    if (arguments.read("--plugins"))
    {
        osgDB::FileNameList plugins = osgDB::listAllAvailablePlugins();
        for(osgDB::FileNameList::iterator itr = plugins.begin();
            itr != plugins.end();
            ++itr)
        {
            std::cout<<"Plugin "<<*itr<<std::endl;
        }
        return 0;
    }    
    
    std::string plugin;
    if (arguments.read("--plugin", plugin))
    {
        osgDB::outputPluginDetails(std::cout, plugin);
        return 0;
    }    
    
    std::string ext;
    if (arguments.read("--format", ext))
    {
        plugin = 
osgDB::Registry::instance()->createLibraryNameForExtension(ext);
        osgDB::outputPluginDetails(std::cout, plugin);
        return 0;
    }    
    
    if (arguments.read("--formats"))
    {
        osgDB::FileNameList plugins = osgDB::listAllAvailablePlugins();
        for(osgDB::FileNameList::iterator itr = plugins.begin();
            itr != plugins.end();
            ++itr)
        {
            osgDB::outputPluginDetails(std::cout,*itr);
        }
        return 0;
    }    
    
    if (arguments.argc()<=1)
    {
        
arguments.getApplicationUsage()->write(std::cout,osg::ApplicationUsage::COMMAND_LINE_OPTION);
        return 1;
    }

    ConvertQueue conversionSet;
        ConvertBatch initialConversion;
    OrientationConverter oc;
    bool do_convert = false, do_recurse = false;

    std::string str, baseSuffixString;

    // parse recursion/suffix option
        while(arguments.read("-R",baseSuffixString))
    {
         do_recurse = true;
    };

    while (arguments.read("-O",str))
    {
        osgDB::ReaderWriter::Options* options = new 
osgDB::ReaderWriter::Options;
        options->setOptionString(str);
        osgDB::Registry::instance()->setOptions(options);
    }

    while (arguments.read("-e",ext))
    {
        std::string libName = 
osgDB::Registry::instance()->createLibraryNameForExtension(ext);
        osgDB::Registry::instance()->loadLibrary(libName);
    }
    
    std::string libName;
    while (arguments.read("-l",libName))
    {
        osgDB::Registry::instance()->loadLibrary(libName);
    }

    while (arguments.read("-o",str))
    {
        osg::Vec3 from, to;
        if( sscanf( str.c_str(), "%f,%f,%f-%f,%f,%f",
                &from[0], &from[1], &from[2],
                &to[0], &to[1], &to[2]  )
            != 6 )
        {
            float degrees;
            osg::Vec3 axis;
            // Try deg-axis format
            if( sscanf( str.c_str(), "%f-%f,%f,%f",
                    &degrees, &axis[0], &axis[1], &axis[2]  ) != 4 )
            {
                usage( argv[0], "Orientation argument format incorrect." );
                return 1;
            }
            else
            {
                oc.setRotation( degrees, axis );
                do_convert = true;
            }
        }
        else
        {
            oc.setRotation( from, to );
            do_convert = true;
        }
    }    

    while (arguments.read("-s",str))
    {
        osg::Vec3 scale(0,0,0);
        if( sscanf( str.c_str(), "%f,%f,%f",
                &scale[0], &scale[1], &scale[2] ) != 3 )
        {
            usage( argv[0], "Scale argument format incorrect." );
            return 1;
        }
        oc.setScale( scale );
        do_convert = true;
    }

    float simplifyPercent = 1.0;
    bool do_simplify = false;
    while ( arguments.read( "--simplify",str ) )
    {
        float nsimp = 1.0;
        if( sscanf( str.c_str(), "%f",
                &nsimp ) != 1 )
        {
            usage( argv[0], "Scale argument format incorrect." );
            return 1;
        }
        std::cout << str << " " << nsimp << std::endl;
        simplifyPercent = nsimp;
        osg::notify( osg::INFO ) << "Simplifying with percentage: " << 
simplifyPercent << std::endl;
        do_simplify = true;
    }

    while (arguments.read("-t",str))
    {
        osg::Vec3 trans(0,0,0);
        if( sscanf( str.c_str(), "%f,%f,%f",
                &trans[0], &trans[1], &trans[2] ) != 3 )
        {
            usage( argv[0], "Translation argument format incorrect." );
            return 1;
        }
        oc.setTranslation( trans );
        do_convert = true;
    }

    
    FixTransparencyVisitor::FixTransparencyMode fixTransparencyMode = 
FixTransparencyVisitor::NO_TRANSPARANCY_FIXING;
    std::string fixString;
    while(arguments.read("--fix-transparency")) fixTransparencyMode = 
FixTransparencyVisitor::MAKE_OPAQUE_TEXTURE_STATESET_OPAQUE;
    while(arguments.read("--fix-transparency-mode",fixString))
    {
         if (fixString=="MAKE_OPAQUE_TEXTURE_STATESET_OPAQUE") 
fixTransparencyMode = 
FixTransparencyVisitor::MAKE_OPAQUE_TEXTURE_STATESET_OPAQUE;
         if (fixString=="MAKE_ALL_STATESET_OPAQUE") fixTransparencyMode = 
FixTransparencyVisitor::MAKE_ALL_STATESET_OPAQUE;
    };
    
    bool pruneStateSet = false;
    while(arguments.read("--prune-StateSet")) pruneStateSet = true;

    osg::Texture::InternalFormatMode internalFormatMode = 
osg::Texture::USE_IMAGE_DATA_FORMAT;
    while(arguments.read("--compressed") || arguments.read("--compressed-arb")) 
{ internalFormatMode = osg::Texture::USE_ARB_COMPRESSION; }

    while(arguments.read("--compressed-dxt1")) { internalFormatMode = 
osg::Texture::USE_S3TC_DXT1_COMPRESSION; }
    while(arguments.read("--compressed-dxt3")) { internalFormatMode = 
osg::Texture::USE_S3TC_DXT3_COMPRESSION; }
    while(arguments.read("--compressed-dxt5")) { internalFormatMode = 
osg::Texture::USE_S3TC_DXT5_COMPRESSION; }

    bool smooth = false;
    while(arguments.read("--smooth")) { smooth = true; }
    
    bool addMissingColours = false;
    while(arguments.read("--addMissingColours") || 
arguments.read("--addMissingColors")) { addMissingColours = true; }

    bool do_overallNormal = false;
    while(arguments.read("--overallNormal") || 
arguments.read("--overallNormal")) { do_overallNormal = true; }

    bool enableObjectCache = false;
    while(arguments.read("--enable-object-cache")) { enableObjectCache = true; }

    // any option left unread are converted into errors to write out later.
    arguments.reportRemainingOptionsAsUnrecognized();

    // report any errors if they have occurred when parsing the program 
arguments.
    if (arguments.errors())
    {
        arguments.writeErrorMessages(std::cout);
        return 1;
    }

    // add all non-option arguments to the initialConversion container
        for(int pos=1;pos<arguments.argc();++pos)
    {
        if (!arguments.isOption(pos))
        {
                        initialConversion.addInputFileName(arguments[pos]);
                        
initialConversion.setInputBasePath(originalCurrentWorkingDirectory);
        }
    } // for

    // If more than one filename argument was provided, use last one as output 
filename, otherwise use converted.osg
        if (initialConversion.inputNames.size()>1) // was more than one 
filename specified on the command line?
    {
                // remove the last-supplied filename from the initial filenames
                const std::string initialFileNameOut = 
initialConversion.removeInputFromBack();
                // obtain destination path from it
                
initialConversion.setOutputPath(osgDB::getFilePath(initialFileNameOut));
                // and get the path-less filename
                
initialConversion.setOutputFileName(osgDB::getSimpleFileName(initialFileNameOut));
        } // if
        else
        { // only one filename provided, use default output filename
                initialConversion.setOutputFileName("converted.osg");
                
initialConversion.setOutputPath(originalCurrentWorkingDirectory);
        } // else

        // add the initialConversion batch to the set of ConvertBatches named 
conversionSet
        conversionSet.push(initialConversion);

    if (enableObjectCache)
    {
        if (osgDB::Registry::instance()->getOptions()==0) 
osgDB::Registry::instance()->setOptions(new osgDB::Options());
        
osgDB::Registry::instance()->getOptions()->setObjectCacheHint(osgDB::Options::CACHE_ALL);
    }



    osg::Timer_t conversionStartTick = osg::Timer::instance()->tick();

    while(!conversionSet.empty())
        {
                osg::Timer_t startTick = osg::Timer::instance()->tick();

                // get the first set of filenames in the queue
                ConvertBatch &workingConvertBatch = conversionSet.front();

                osg::notify(osg::DEBUG_INFO)<<"workingConvertBatch 
("<<workingConvertBatch.inputNames.size() << " items)" <<std::endl;
                osg::notify(osg::DEBUG_INFO)<<" inputBasePath " << 
workingConvertBatch.inputBasePath <<std::endl;
                osg::notify(osg::DEBUG_INFO)<<" outputName " << 
workingConvertBatch.outputFileName <<std::endl;
                osg::notify(osg::DEBUG_INFO)<<" outputPath " << 
workingConvertBatch.outputPath <<std::endl;
                for(FileNameList::iterator FileNameListIterator = 
workingConvertBatch.inputNames.begin(); FileNameListIterator != 
workingConvertBatch.inputNames.end(); FileNameListIterator++)
                        osg::notify(osg::INFO)<<"  File: 
"<<*FileNameListIterator <<std::endl;

                // change to the specified base directory for loading
                osg::notify(osg::DEBUG_INFO) << "Changing to folder " << 
workingConvertBatch.inputBasePath << std::endl;
                
osgDB::setCurrentWorkingDirectory(workingConvertBatch.inputBasePath);

                // load all the input files in current set
                osg::ref_ptr<osg::Node> root = 
osgDB::readNodeFiles(workingConvertBatch.inputNames);

                if (root.valid())
                {
                        osg::Timer_t endTick = osg::Timer::instance()->tick();
                        osg::notify(osg::INFO)<<"Time to load files in set 
"<<osg::Timer::instance()->delta_m(startTick, endTick)<<" ms"<<std::endl;
                }


                if (pruneStateSet)
                {
                        PruneStateSetVisitor pssv;
                        root->accept(pssv);
                }

                if (fixTransparencyMode != 
FixTransparencyVisitor::NO_TRANSPARANCY_FIXING)
                {
                        FixTransparencyVisitor atv(fixTransparencyMode);
                        root->accept(atv);
                }

                if ( root.valid() )
                {
            
                        if (smooth)
                        {
                                osgUtil::SmoothingVisitor sv;
                                root->accept(sv);
                        }    
            
                        if (addMissingColours)
                        {
                                AddMissingColoursToGeometryVisitor av;
                                root->accept(av);
                        }

                        // optimize the scene graph, remove rendundent nodes 
and state etc.
                        osgUtil::Optimizer optimizer;
                        optimizer.optimize(root.get());
                
                        if( do_convert )
                                root = oc.convert( root.get() );
                    
                        if (internalFormatMode != 
osg::Texture::USE_IMAGE_DATA_FORMAT)
                        {
                                std::string ext = 
osgDB::getFileExtension(workingConvertBatch.outputFileName);
                                CompressTexturesVisitor ctv(internalFormatMode);
                                root->accept(ctv);
                                ctv.compress();

                                osgDB::ReaderWriter::Options *options = 
osgDB::Registry::instance()->getOptions();
                                if (ext!="ive" || (options && 
options->getOptionString().find("noTexturesInIVEFile")!=std::string::npos))
                                {
                                        
ctv.write(osgDB::getFilePath(workingConvertBatch.outputFileName));
                                }
                        }

                        // scrub normals
                        if ( do_overallNormal )
                        {
                                DefaultNormalsGeometryVisitor dngv;
                                root->accept( dngv );
                        }

                        // apply any user-specified simplification
                        if ( do_simplify )
                        {
                                osgUtil::Simplifier simple;
                                simple.setSmoothing( smooth );
                                osg::notify( osg::ALWAYS ) << " smoothing: " << 
smooth << std::endl;
                                simple.setSampleRatio( simplifyPercent );
                                root->accept( simple );
                        }

                        // handle recursive conversion of external files 
through PagedLOD/proxyNode
                        if ( do_recurse )
                        {
                                // gather the list of external filenames from 
all PagedLOD/ProxyNode children's filenames
                                // and apply user-specified suffix to each
                                osg::notify( osg::ALWAYS ) << " Recursively 
searching for dependent files to convert." << std::endl;
                                // since there are potentially multiple files 
involved recursion paths only work with a single file processed at a time
                                // if we have loaded multiple nodes in 
readNodeFiles, the name of the node may contain the path loaded from
                                std::string inputFileBasePath = 
osgDB::getFilePath(osgDB::getRealPath(workingConvertBatch.inputNames.front()));
                                RecursiveConversionVisitor rcv(conversionSet, 
inputFileBasePath, workingConvertBatch.outputPath, baseSuffixString);
                                root->accept(rcv);
                                }
                        
                        // try to create destination directory if not present
                        if(!osgDB::fileExists(workingConvertBatch.outputPath))
                        {
                                
osgDB::makeDirectory(workingConvertBatch.outputPath);
                        } // if
                        // change to destination directory
                        osg::notify(osg::DEBUG_INFO) << "Changing to folder " 
<< workingConvertBatch.outputPath << std::endl;
                        
osgDB::setCurrentWorkingDirectory(workingConvertBatch.outputPath);
                        // write the scenegraph
                        osg::notify(osg::DEBUG_INFO) << "writing output file " 
<< workingConvertBatch.outputFileName << std::endl;
                        osgDB::ReaderWriter::WriteResult result = 
osgDB::Registry::instance()->writeNode(*root,workingConvertBatch.outputFileName,osgDB::Registry::instance()->getOptions());
                        // change back to original current working directory
                        osg::notify(osg::DEBUG_INFO) << "Changing to original 
folder " << originalCurrentWorkingDirectory << std::endl;
                        
osgDB::setCurrentWorkingDirectory(originalCurrentWorkingDirectory);
                        if (result.success())
                        {
                                osg::notify(osg::NOTICE)<<"Data written to 
'"<<workingConvertBatch.outputFileName<<"'."<< std::endl;
                        }
                        else if  (result.message().empty())
                        {
                                osg::notify(osg::NOTICE)<<"Warning: file write 
to '"<<workingConvertBatch.outputFileName<<"' no supported."<< std::endl;
                        }
                        else
                        {
                                osg::notify(osg::NOTICE)<<result.message()<< 
std::endl;
                        }
                }
                else
                {
                        osg::notify(osg::NOTICE)<<"Error no data loaded."<< 
std::endl;
                        return 1;
                }
                conversionSet.pop(); // remove just-used set of filenames from 
the queue
        } // while any FileNameLists

        osg::Timer_t conversionEndTick = osg::Timer::instance()->tick();
    osg::notify(osg::INFO)<<"Time to convert all files 
"<<osg::Timer::instance()->delta_s(conversionStartTick, conversionEndTick)<<" 
seconds"<<std::endl;

    return 0;
}
_______________________________________________
osg-submissions mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org

Reply via email to