/* OpenSceneGraph example, osganalysis. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class StripStateVisitor : public osg::NodeVisitor { public: StripStateVisitor(bool useStateSets, bool useDisplayLists, bool useVBO): osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), _useStateSets(useStateSets), _useDisplayLists(useDisplayLists), _useVBO(useVBO) {} bool _useStateSets; bool _useDisplayLists; bool _useVBO; void apply(osg::Node& node) { if (!_useStateSets && node.getStateSet()) node.setStateSet(0); traverse(node); } void apply(osg::Drawable& drawable) { if (!_useStateSets && drawable.getStateSet()) { drawable.setStateSet(0); } drawable.setUseDisplayList(_useDisplayLists); drawable.setUseVertexBufferObjects(_useVBO); } }; class OptimizeImageVisitor : public osg::NodeVisitor { public: OptimizeImageVisitor(osgDB::ImageProcessor* imageProcessor, bool compressImages, bool generateMipmaps): osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), _imageProcessor(imageProcessor), _compressImages(compressImages), _generateMipmaps(generateMipmaps) {} osg::ref_ptr _imageProcessor; bool _compressImages; bool _generateMipmaps; void apply(osg::Node& node) { processStateSet(node.getStateSet()); traverse(node); } void apply(osg::Geode& node) { processStateSet(node.getStateSet()); for(unsigned int i = 0; igetStateSet()); } traverse(node); } void processStateSet(osg::StateSet* stateset) { if (!stateset) return; for(unsigned int ti=0; tigetNumTextureAttributeLists(); ++ti) { osg::StateAttribute* sa = stateset->getTextureAttribute(ti, osg::StateAttribute::TEXTURE); osg::Texture* texture = dynamic_cast(sa); if (texture) { for(unsigned int i=0; igetNumImages(); ++i) { proccessImage(texture->getImage(i)); } } }; } void proccessImage(osg::Image* image) { if (!image) return; if (_imageProcessor.valid()) { OSG_NOTICE<<"Will be using ImageProcessor to process image "<getFileName()<getFileName()< void apply_imp(ARRAY& array) { if (array.getType()!=_array->getType()) { OSG_NOTICE<<"Arrays incompatible"<(_array)); } virtual void apply(osg::ByteArray& ba) { apply_imp(ba); } virtual void apply(osg::ShortArray& ba) { apply_imp(ba); } virtual void apply(osg::IntArray& ba) { apply_imp(ba); } virtual void apply(osg::UByteArray& ba) { apply_imp(ba); } virtual void apply(osg::UShortArray& ba) { apply_imp(ba); } virtual void apply(osg::UIntArray& ba) { apply_imp(ba); } virtual void apply(osg::Vec4ubArray& ba) { apply_imp(ba); } virtual void apply(osg::FloatArray& ba) { apply_imp(ba); } virtual void apply(osg::Vec2Array& ba) { apply_imp(ba); } virtual void apply(osg::Vec3Array& ba) { apply_imp(ba); } virtual void apply(osg::Vec4Array& ba) { apply_imp(ba); } osg::Array* _array; }; class MemoryVisitor : public osg::NodeVisitor { public: MemoryVisitor(): osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {} void reset() { _nodes.clear(); _geometryMap.clear(); _arrayMap.clear(); _primitiveSetMap.clear(); } void apply(osg::Node& node) { _nodes.insert(&node); traverse(node); } void apply(osg::Geode& geode) { _nodes.insert(&geode); for(unsigned int i=0; iasGeometry(); if (geometry) { _geometryMap[geometry].insert(geode); apply(geometry, geometry->getVertexArray()); apply(geometry, geometry->getNormalArray()); apply(geometry, geometry->getColorArray()); apply(geometry, geometry->getSecondaryColorArray()); apply(geometry, geometry->getFogCoordArray()); for(unsigned int i=0; igetNumTexCoordArrays(); ++i) { apply(geometry, geometry->getTexCoordArray(i)); } for(unsigned int i=0; igetNumVertexAttribArrays(); ++i) { apply(geometry, geometry->getVertexAttribArray(i)); } for(unsigned int i=0; igetNumPrimitiveSets(); ++i) { apply(geometry, geometry->getPrimitiveSet(i)); } } } void apply(osg::Geometry* geometry, osg::Array* array) { if (!array) return; _arrayMap[array].insert(geometry); } void apply(osg::Geometry* geometry, osg::PrimitiveSet* primitiveSet) { if (!primitiveSet) return; _primitiveSetMap[primitiveSet].insert(geometry); } void report(std::ostream& out) { out<<"Nodes "<<_nodes.size()< > GeometryVector; GeometryVector newGeometries; for(GeometryMap::iterator itr = _geometryMap.begin(); itr != _geometryMap.end(); ++itr) { osg::Geometry* geometry = itr->first; bool useVBO = geometry->getUseVertexBufferObjects(); osg::Geometry* newGeometry = osg::clone(geometry, osg::CopyOp(osg::CopyOp::DEEP_COPY_ALL)); newGeometry->setUseVertexBufferObjects(false); newGeometry->setUseVertexBufferObjects(useVBO); newGeometries.push_back(newGeometry); } GeometryVector::iterator geom_itr = newGeometries.begin(); for(GeometryMap::iterator itr = _geometryMap.begin(); itr != _geometryMap.end(); ++itr, ++geom_itr) { osg::Geometry* geometry = itr->first; Geodes& geodes = itr->second; for(Geodes::iterator gitr = geodes.begin(); gitr != geodes.end(); ++gitr) { osg::Geode* geode = const_cast(*gitr); geode->replaceDrawable(geometry, geom_itr->get()); } } } typedef std::vector< osg::ref_ptr > GeometryVector; typedef std::pair ArrayPair; typedef std::vector< ArrayPair > ArrayVector; typedef std::pair PrimitiveSetPair; typedef std::vector< PrimitiveSetPair > PrimitiveSetVector; osg::Array* cloneArray(ArrayVector& arrayVector, osg::Array* array) { if (!array) return 0; osg::Array* newArray = static_cast(array->cloneType()); arrayVector.push_back(ArrayPair(array,newArray)); return newArray; } osg::PrimitiveSet* clonePrimitiveSet(PrimitiveSetVector& psVector, osg::PrimitiveSet* ps) { if (!ps) return 0; osg::PrimitiveSet* newPS = static_cast(ps->cloneType()); psVector.push_back(PrimitiveSetPair(ps,newPS)); return newPS; } void reallocate2() { OSG_NOTICE<<"Reallocating Arrays"<first; osg::ref_ptr newGeometry = osg::clone(geometry, osg::CopyOp::SHALLOW_COPY); newGeometries.push_back(newGeometry.get()); newGeometry->setVertexArray(cloneArray(arrayVector, geometry->getVertexArray())); newGeometry->setNormalArray(cloneArray(arrayVector, geometry->getNormalArray())); newGeometry->setColorArray(cloneArray(arrayVector, geometry->getColorArray())); newGeometry->setSecondaryColorArray(cloneArray(arrayVector, geometry->getSecondaryColorArray())); newGeometry->setFogCoordArray(cloneArray(arrayVector, geometry->getFogCoordArray())); for(unsigned int i=0; igetNumTexCoordArrays(); ++i) { newGeometry->setTexCoordArray(i, cloneArray(arrayVector, geometry->getTexCoordArray(i))); } for(unsigned int i=0; igetNumVertexAttribArrays(); ++i) { newGeometry->setVertexAttribArray(i, cloneArray(arrayVector, geometry->getVertexAttribArray(i))); } for(unsigned int i=0; igetNumPrimitiveSets(); ++i) { newGeometry->setPrimitiveSet(i,clonePrimitiveSet(primitiveSetVector, geometry->getPrimitiveSet(i))); } } GeometryVector::iterator geom_itr = newGeometries.begin(); for(GeometryMap::iterator itr = _geometryMap.begin(); itr != _geometryMap.end(); ++itr, ++geom_itr) { osg::Geometry* geometry = itr->first; Geodes& geodes = itr->second; for(Geodes::iterator gitr = geodes.begin(); gitr != geodes.end(); ++gitr) { osg::Geode* geode = const_cast(*gitr); geode->replaceDrawable(geometry, geom_itr->get()); } } } protected: typedef std::set Nodes; typedef std::set Geodes; typedef std::set Geometries; typedef std::map GeometryMap; typedef std::map ArrayMap; typedef std::map PrimitiveSetMap; Nodes _nodes; GeometryMap _geometryMap; ArrayMap _arrayMap; PrimitiveSetMap _primitiveSetMap; }; class SceneGraphProcessor : public osg::Referenced { public: SceneGraphProcessor() { _init(); } SceneGraphProcessor(osg::ArgumentParser& arguments) { _init(); while (arguments.read("--vbo")) { modifyDrawableSettings = true; useVBO = true; } while (arguments.read("--dl")) { modifyDrawableSettings = true; useDisplayLists = true; } while (arguments.read("-s", simplificatioRatio)) {} while (arguments.read("--tristripper")) { useTriStripVisitor=true; } while (arguments.read("--no-tristripper")) { useTriStripVisitor=false; } while (arguments.read("--smoother")) { useSmoothingVisitor=true; } while (arguments.read("--no-smoother")) { useSmoothingVisitor=false; } while (arguments.read("--remove-duplicate-vertices") || arguments.read("--rdv")) removeDuplicateVertices = true; while (arguments.read("--optimize-vertex-cache") || arguments.read("--ovc")) optimizeVertexCache = true; while (arguments.read("--optimize-vertex-order") || arguments.read("--ovo")) optimizeVertexOrder = true; while (arguments.read("--build-mipmaps")) { modifyTextureSettings = true; buildImageMipmaps = true; } while (arguments.read("--compress")) { modifyTextureSettings = true; compressImages = true; } while (arguments.read("--disable-mipmaps")) { modifyTextureSettings = true; disableMipmaps = true; } while (arguments.read("--reallocate") || arguments.read("--ra") ) { reallocateMemory = true; } OSG_NOTICE<<"simplificatioRatio="<getName()<accept(imv); imv.makeMesh(); } if (optimizeVertexCache) { OSG_NOTICE<<"Running osgUtil::VertexCacheVisitor"<accept(vcv); vcv.optimizeVertices(); } if (optimizeVertexOrder) { OSG_NOTICE<<"Running osgUtil::VertexAccessOrderVisitor"<accept(vaov); vaov.optimizeOrder(); } if (modifyDrawableSettings) { OSG_NOTICE<<"Running StripStateVisitor"<accept(ssv); } MemoryVisitor mv; node->accept(mv); mv.report(osg::notify(osg::NOTICE)); if (reallocateMemory) { OSG_NOTICE<<"Running Reallocation of scene graph memory"<accept(mv); mv.report(osg::notify(osg::NOTICE)); return node; } protected: void _init() { modifyDrawableSettings = false; useVBO = false; useDisplayLists = false; simplificatioRatio = 1.0; useTriStripVisitor = false; useSmoothingVisitor = false; removeDuplicateVertices = false; optimizeVertexCache = false; optimizeVertexOrder = false; reallocateMemory = false; modifyTextureSettings = false; buildImageMipmaps = false; compressImages = false; disableMipmaps = false; } bool modifyDrawableSettings; bool useVBO; bool useDisplayLists; float simplificatioRatio; bool useTriStripVisitor; bool useSmoothingVisitor; bool removeDuplicateVertices; bool optimizeVertexCache; bool optimizeVertexOrder; bool reallocateMemory; bool modifyTextureSettings; bool buildImageMipmaps; bool compressImages; bool disableMipmaps; }; // class DatabasePagingOperation : public osg::Operation, public osgUtil::IncrementalCompileOperation::CompileCompletedCallback { public: DatabasePagingOperation(const std::string& filename, const std::string& outputFilename, SceneGraphProcessor* sceneGraphProcessor, osgUtil::IncrementalCompileOperation* ico): osg::Referenced(true), Operation("DatabasePaging Operation", false), _filename(filename), _outputFilename(outputFilename), _modelReadyToMerge(false), _sceneGraphProcessor(sceneGraphProcessor), _incrementalCompileOperation(ico) { } virtual void operator () (osg::Object* /*object*/) { osg::notify(osg::NOTICE)<<"LoadAndCompileOperation "<<_filename<process(_loadedModel.get()); } } if (_loadedModel.valid()) { if (!_outputFilename.empty()) { OSG_NOTICE<<"Writing out file "<<_outputFilename< compileSet = new osgUtil::IncrementalCompileOperation::CompileSet(_loadedModel.get()); compileSet->_compileCompletedCallback = this; _incrementalCompileOperation->add(compileSet.get()); } else { _modelReadyToMerge = true; } } osg::notify(osg::NOTICE)<<"done LoadAndCompileOperation "<<_filename< _loadedModel; bool _modelReadyToMerge; osg::ref_ptr _sceneGraphProcessor; osg::ref_ptr _incrementalCompileOperation; }; class TexturePoolHandler : public osgGA::GUIEventHandler { public: virtual bool handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter& /*aa*/) { if (ea.getEventType() == osgGA::GUIEventAdapter::KEYUP) { if (ea.getKey()=='r') { osg::getOrCreateContextData(0)->reportStats(osg::notify(osg::NOTICE)); } } return false; } }; struct ReportStatsAnimationCompletedCallback : public osgGA::AnimationPathManipulator::AnimationCompletedCallback { virtual void completed(const osgGA::AnimationPathManipulator*) { OSG_NOTICE<<"Animation completed"<reportStats(osg::notify(osg::NOTICE)); } }; int main(int argc, char** argv) { osg::ArgumentParser arguments(&argc, argv); // construct the viewer. osgViewer::Viewer viewer(arguments); // set up camera manipulators { osg::ref_ptr keyswitchManipulator = new osgGA::KeySwitchMatrixManipulator; keyswitchManipulator->addMatrixManipulator( '1', "Trackball", new osgGA::TrackballManipulator() ); keyswitchManipulator->addMatrixManipulator( '2', "Flight", new osgGA::FlightManipulator() ); keyswitchManipulator->addMatrixManipulator( '3', "Drive", new osgGA::DriveManipulator() ); keyswitchManipulator->addMatrixManipulator( '4', "Terrain", new osgGA::TerrainManipulator() ); char keyForAnimationPath = '8'; double animationSpeed = 1.0; while(arguments.read("--speed",animationSpeed) ) {} std::string pathfile; while (arguments.read("-p",pathfile)) { osgGA::AnimationPathManipulator* apm = new osgGA::AnimationPathManipulator(pathfile); if (apm || !apm->valid()) { apm->setTimeScale(animationSpeed); apm->setAnimationCompletedCallback(new ReportStatsAnimationCompletedCallback()); unsigned int num = keyswitchManipulator->getNumMatrixManipulators(); keyswitchManipulator->addMatrixManipulator( keyForAnimationPath, "Path", apm ); keyswitchManipulator->selectMatrixManipulator(num); ++keyForAnimationPath; } } viewer.setCameraManipulator( keyswitchManipulator.get() ); } // set up event handlers { viewer.addEventHandler( new osgViewer::StatsHandler()); viewer.addEventHandler( new osgViewer::WindowSizeHandler() ); viewer.addEventHandler( new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()) ); viewer.addEventHandler( new TexturePoolHandler() ); } ///////////////////////////////////////////////////////////////////////////////// // // IncrementalCompileOperation settings // osg::ref_ptr incrementalCompile = new osgUtil::IncrementalCompileOperation; viewer.setIncrementalCompileOperation(incrementalCompile.get()); if (arguments.read("--force") || arguments.read("-f")) { incrementalCompile->assignForceTextureDownloadGeometry(); } if (arguments.read("-a")) { incrementalCompile->setMinimumTimeAvailableForGLCompileAndDeletePerFrame(1); incrementalCompile->setConservativeTimeRatio(1); incrementalCompile->setMaximumNumOfObjectsToCompilePerFrame(100); } else if (arguments.read("-c")) { incrementalCompile->setMinimumTimeAvailableForGLCompileAndDeletePerFrame(0.0001); incrementalCompile->setConservativeTimeRatio(0.01); incrementalCompile->setMaximumNumOfObjectsToCompilePerFrame(1); } ///////////////////////////////////////////////////////////////////////////////// // // SceneGraph processing setup // osg::ref_ptr sceneGraphProcessor = new SceneGraphProcessor(arguments); ///////////////////////////////////////////////////////////////////////////////// // // Database settings // double timeBetweenMerges = 2.0; while(arguments.read("--interval",timeBetweenMerges)) {} std::string outputPostfix; while (arguments.read("-o",outputPostfix)) { OSG_NOTICE<<"Set ouputPostfix to "< FileNames; FileNames fileNames; for(int pos=1;pos databasePagingThread; osg::ref_ptr databasePagingOperation; databasePagingThread = new osg::OperationThread; databasePagingThread->startThread(); osg::ref_ptr group = new osg::Group; viewer.setSceneData(group.get()); viewer.realize(); std::string filename = fileNames[modelIndex++]; std::string outputFilename = outputPostfix.empty() ? std::string() : osgDB::getStrippedName(filename)+outputPostfix; databasePagingOperation = new DatabasePagingOperation( filename, outputFilename, sceneGraphProcessor.get(), incrementalCompile.get()); databasePagingThread->add(databasePagingOperation.get()); double timeOfLastMerge = viewer.getFrameStamp()->getReferenceTime(); while(!viewer.done()) { viewer.frame(); double currentTime = viewer.getFrameStamp()->getReferenceTime(); if (!databasePagingOperation && modelIndextimeBetweenMerges) { filename = fileNames[modelIndex++]; outputFilename = outputPostfix.empty() ? std::string() : osgDB::getStrippedName(filename)+outputPostfix; databasePagingOperation = new DatabasePagingOperation( filename, outputFilename, sceneGraphProcessor.get(), incrementalCompile.get()); databasePagingThread->add(databasePagingOperation.get()); } if (databasePagingOperation.get() && databasePagingOperation->_modelReadyToMerge) { OSG_NOTICE<<"Merging subgraph"<removeChildren(0,group->getNumChildren()); group->addChild(databasePagingOperation->_loadedModel.get()); viewer.home(); // we no longer need the paging operation as it's done it's job. databasePagingOperation = 0; viewer.home(); } } return 0; }