X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fscene%2Fmodel%2Fmodel.cxx;h=1ea486f0d13afc5b1dae4353c449539f77df3d85;hb=d04cf4d8978866eb80a1639b6d4ddfe387338c77;hp=4a2349be603cac35bf1aa7153c83d789577647fa;hpb=7aa6fd479da920a6eb95f4b7da2010906928b619;p=simgear.git diff --git a/simgear/scene/model/model.cxx b/simgear/scene/model/model.cxx index 4a2349be..1ea486f0 100644 --- a/simgear/scene/model/model.cxx +++ b/simgear/scene/model/model.cxx @@ -7,358 +7,51 @@ #include #endif -#include +#include + +#include + #include -#include -#include -#include -#include -#include #include #include +#include #include -#include -#include #include -#include +#include +#include +#include #include -#include -#include +#include +#include +#include + #include +#include #include #include #include -#include "animation.hxx" +#include "SGReaderWriterXMLOptions.hxx" #include "model.hxx" -SG_USING_STD(vector); -SG_USING_STD(set); - -// Little helper class that holds an extra reference to a -// loaded 3d model. -// Since we clone all structural nodes from our 3d models, -// the database pager will only see one single reference to -// top node of the model and expire it relatively fast. -// We attach that extra reference to every model cloned from -// a base model in the pager. When that cloned model is deleted -// this extra reference is deleted too. So if there are no -// cloned models left the model will expire. -class SGDatabaseReference : public osg::Observer { -public: - SGDatabaseReference(osg::Referenced* referenced) : - mReferenced(referenced) - { } - virtual void objectDeleted(void*) - { - mReferenced = 0; - } -private: - osg::ref_ptr mReferenced; -}; - -// Visitor for -class SGTextureUpdateVisitor : public SGTextureStateAttributeVisitor { -public: - SGTextureUpdateVisitor(const osgDB::FilePathList& pathList) : - mPathList(pathList) - { } - osg::Texture2D* textureReplace(int unit, - osg::StateSet::RefAttributePair& refAttr) - { - osg::Texture2D* texture; - texture = dynamic_cast(refAttr.first.get()); - if (!texture) - return 0; - - osg::ref_ptr image = texture->getImage(0); - if (!image) - return 0; - - // The currently loaded file name - std::string fullFilePath = image->getFileName(); - // The short name - std::string fileName = osgDB::getSimpleFileName(fullFilePath); - // The name that should be found with the current database path - std::string fullLiveryFile = osgDB::findFileInPath(fileName, mPathList); - // If they are identical then there is nothing to do - if (fullLiveryFile == fullFilePath) - return 0; - - image = osgDB::readImageFile(fullLiveryFile); - if (!image) - return 0; - - osg::CopyOp copyOp(osg::CopyOp::DEEP_COPY_ALL & - ~osg::CopyOp::DEEP_COPY_IMAGES); - texture = static_cast(copyOp(texture)); - if (!texture) - return 0; - texture->setImage(image.get()); - return texture; - } - virtual void apply(osg::StateSet* stateSet) - { - if (!stateSet) - return; - - // get a copy that we can safely modify the statesets values. - osg::StateSet::TextureAttributeList attrList; - attrList = stateSet->getTextureAttributeList(); - for (unsigned unit = 0; unit < attrList.size(); ++unit) { - osg::StateSet::AttributeList::iterator i; - i = attrList[unit].begin(); - while (i != attrList[unit].end()) { - osg::Texture2D* texture = textureReplace(unit, i->second); - if (texture) { - stateSet->removeTextureAttribute(unit, i->second.first.get()); - stateSet->setTextureAttribute(unit, texture, i->second.second); - stateSet->setTextureMode(unit, GL_TEXTURE_2D, - osg::StateAttribute::ON); - } - ++i; - } - } - } - -private: - osgDB::FilePathList mPathList; -}; - -class SGTexCompressionVisitor : public SGTextureStateAttributeVisitor { -public: - virtual void apply(int, osg::StateSet::RefAttributePair& refAttr) - { - osg::Texture2D* texture; - texture = dynamic_cast(refAttr.first.get()); - if (!texture) - return; - - osg::Image* image = texture->getImage(0); - if (!image) - return; - - int s = image->s(); - int t = image->t(); - - if (s <= t && 32 <= s) { - SGSceneFeatures::instance()->setTextureCompression(texture); - } else if (t < s && 32 <= t) { - SGSceneFeatures::instance()->setTextureCompression(texture); - } - } -}; - -class SGTexDataVarianceVisitor : public SGTextureStateAttributeVisitor { -public: - virtual void apply(int, osg::StateSet::RefAttributePair& refAttr) - { - osg::Texture* texture; - texture = dynamic_cast(refAttr.first.get()); - if (!texture) - return; - - texture->setDataVariance(osg::Object::STATIC); - } -}; - -class SGAcMaterialCrippleVisitor : public SGStateAttributeVisitor { -public: - virtual void apply(osg::StateSet::RefAttributePair& refAttr) - { - osg::Material* material; - material = dynamic_cast(refAttr.first.get()); - if (!material) - return; - material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE); - } -}; - -class SGReadFileCallback : - public osgDB::Registry::ReadFileCallback { -public: - virtual osgDB::ReaderWriter::ReadResult - readImage(const std::string& fileName, - const osgDB::ReaderWriter::Options* opt) - { - std::string absFileName = osgDB::findDataFile(fileName); - if (!osgDB::fileExists(absFileName)) { - SG_LOG(SG_IO, SG_ALERT, "Cannot find image file \"" - << fileName << "\""); - return osgDB::ReaderWriter::ReadResult::FILE_NOT_FOUND; - } - - osgDB::Registry* registry = osgDB::Registry::instance(); - osgDB::ReaderWriter::ReadResult res; - res = registry->readImageImplementation(absFileName, opt); - if (res.loadedFromCache()) - SG_LOG(SG_IO, SG_INFO, "Returning cached image \"" - << res.getImage()->getFileName() << "\""); - else - SG_LOG(SG_IO, SG_INFO, "Reading image \"" - << res.getImage()->getFileName() << "\""); - - return res; - } - - virtual osgDB::ReaderWriter::ReadResult - readNode(const std::string& fileName, - const osgDB::ReaderWriter::Options* opt) - { - osgDB::Registry* registry = osgDB::Registry::instance(); - osgDB::ReaderWriter::ReadResult res; - // The BTG loader automatically looks for ".btg.gz" if a file with - // the .btg extension doesn't exist. Also, we don't want to add - // nodes, run the optimizer, etc. on the btg model.So, let it do - // its thing. - if (osgDB::equalCaseInsensitive(osgDB::getFileExtension(fileName), "btg")) { - return registry->readNodeImplementation(fileName, opt); - } - std::string absFileName = osgDB::findDataFile(fileName); - if (!osgDB::fileExists(absFileName)) { - SG_LOG(SG_IO, SG_ALERT, "Cannot find model file \"" - << fileName << "\""); - return osgDB::ReaderWriter::ReadResult::FILE_NOT_FOUND; - } - - res = registry->readNodeImplementation(absFileName, opt); - if (!res.validNode()) - return res; - - if (res.loadedFromCache()) { - SG_LOG(SG_IO, SG_INFO, "Returning cached model \"" - << absFileName << "\""); - } else { - SG_LOG(SG_IO, SG_INFO, "Reading model \"" - << absFileName << "\""); - - bool needTristrip = true; - if (osgDB::getLowerCaseFileExtension(absFileName) == "ac") { - // we get optimal geometry from the loader. - needTristrip = false; - osg::Matrix m(1, 0, 0, 0, - 0, 0, 1, 0, - 0, -1, 0, 0, - 0, 0, 0, 1); - - osg::ref_ptr root = new osg::Group; - osg::MatrixTransform* transform = new osg::MatrixTransform; - root->addChild(transform); - - transform->setDataVariance(osg::Object::STATIC); - transform->setMatrix(m); - transform->addChild(res.getNode()); - - res = osgDB::ReaderWriter::ReadResult(0); - - osgUtil::Optimizer optimizer; - unsigned opts = osgUtil::Optimizer::FLATTEN_STATIC_TRANSFORMS; - optimizer.optimize(root.get(), opts); - - // strip away unneeded groups - if (root->getNumChildren() == 1 && root->getName().empty()) { - res = osgDB::ReaderWriter::ReadResult(root->getChild(0)); - } else - res = osgDB::ReaderWriter::ReadResult(root.get()); - - // Ok, this step is questionable. - // It is there to have the same visual appearance of ac objects for the - // first cut. Osg's ac3d loader will correctly set materials from the - // ac file. But the old plib loader used GL_AMBIENT_AND_DIFFUSE for the - // materials that in effect igored the ambient part specified in the - // file. We emulate that for the first cut here by changing all - // ac models here. But in the long term we should use the - // unchanged model and fix the input files instead ... - SGAcMaterialCrippleVisitor matCriple; - res.getNode()->accept(matCriple); - } - - osgUtil::Optimizer optimizer; - unsigned opts = 0; - // Don't use this one. It will break animation names ... - // opts |= osgUtil::Optimizer::REMOVE_REDUNDANT_NODES; - - // opts |= osgUtil::Optimizer::REMOVE_LOADED_PROXY_NODES; - // opts |= osgUtil::Optimizer::COMBINE_ADJACENT_LODS; - // opts |= osgUtil::Optimizer::SHARE_DUPLICATE_STATE; - opts |= osgUtil::Optimizer::MERGE_GEOMETRY; - // opts |= osgUtil::Optimizer::CHECK_GEOMETRY; - // opts |= osgUtil::Optimizer::SPATIALIZE_GROUPS; - // opts |= osgUtil::Optimizer::COPY_SHARED_NODES; - opts |= osgUtil::Optimizer::FLATTEN_STATIC_TRANSFORMS; - if (needTristrip) - opts |= osgUtil::Optimizer::TRISTRIP_GEOMETRY; - // opts |= osgUtil::Optimizer::TESSELATE_GEOMETRY; - // opts |= osgUtil::Optimizer::OPTIMIZE_TEXTURE_SETTINGS; - optimizer.optimize(res.getNode(), opts); - - // Make sure the data variance of sharable objects is set to STATIC ... - SGTexDataVarianceVisitor dataVarianceVisitor; - res.getNode()->accept(dataVarianceVisitor); - // ... so that textures are now globally shared - registry->getSharedStateManager()->share(res.getNode()); - - SGTexCompressionVisitor texComp; - res.getNode()->accept(texComp); - } - - // Add an extra reference to the model stored in the database. - // That it to avoid expiring the object from the cache even if it is still - // in use. Note that the object cache will think that a model is unused - // if the reference count is 1. If we clone all structural nodes here - // we need that extra reference to the original object - SGDatabaseReference* databaseReference; - databaseReference = new SGDatabaseReference(res.getNode()); - osg::CopyOp::CopyFlags flags = osg::CopyOp::DEEP_COPY_ALL; - flags &= ~osg::CopyOp::DEEP_COPY_TEXTURES; - flags &= ~osg::CopyOp::DEEP_COPY_IMAGES; - flags &= ~osg::CopyOp::DEEP_COPY_ARRAYS; - flags &= ~osg::CopyOp::DEEP_COPY_PRIMITIVES; - // This will safe display lists ... - flags &= ~osg::CopyOp::DEEP_COPY_DRAWABLES; - flags &= ~osg::CopyOp::DEEP_COPY_SHAPES; - res = osgDB::ReaderWriter::ReadResult(osg::CopyOp(flags)(res.getNode())); - res.getNode()->addObserver(databaseReference); - - // Update liveries - SGTextureUpdateVisitor liveryUpdate(osgDB::getDataFilePathList()); - res.getNode()->accept(liveryUpdate); - - // Make sure the data variance of sharable objects is set to STATIC ... - SGTexDataVarianceVisitor dataVarianceVisitor; - res.getNode()->accept(dataVarianceVisitor); - // ... so that textures are now globally shared - registry->getOrCreateSharedStateManager()->share(res.getNode(), 0); - - return res; - } -}; - -class SGReadCallbackInstaller { -public: - SGReadCallbackInstaller() - { - osg::Referenced::setThreadSafeReferenceCounting(true); - - osgDB::Registry* registry = osgDB::Registry::instance(); - osgDB::ReaderWriter::Options* options = new osgDB::ReaderWriter::Options; - options->setObjectCacheHint(osgDB::ReaderWriter::Options::CACHE_ALL); - registry->setOptions(options); - registry->getOrCreateSharedStateManager()->setShareMode(osgDB::SharedStateManager::SHARE_TEXTURES); - registry->setReadFileCallback(new SGReadFileCallback); - } -}; - -static SGReadCallbackInstaller readCallbackInstaller; +using std::vector; osg::Texture2D* -SGLoadTexture2D(const std::string& path, bool wrapu, bool wrapv, int) +SGLoadTexture2D(bool staticTexture, const std::string& path, + const osgDB::ReaderWriter::Options* options, + bool wrapu, bool wrapv, int) { - osg::Image* image = osgDB::readImageFile(path); + osg::Image* image; + if (options) + image = osgDB::readImageFile(path, options); + else + image = osgDB::readImageFile(path); osg::ref_ptr texture = new osg::Texture2D; texture->setImage(image); + if (staticTexture) + texture->setDataVariance(osg::Object::STATIC); if (wrapu) texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT); else @@ -379,206 +72,265 @@ SGLoadTexture2D(const std::string& path, bool wrapu, bool wrapv, int) } } - // Make sure the texture is shared if we already have the same texture - // somewhere ... - { - osg::ref_ptr tmpNode = new osg::Node; - osg::StateSet* stateSet = tmpNode->getOrCreateStateSet(); - stateSet->setTextureAttribute(0, texture.get()); - - // OSGFIXME: don't forget that mutex here - osgDB::Registry* registry = osgDB::Registry::instance(); - registry->getOrCreateSharedStateManager()->share(tmpNode.get(), 0); - - // should be the same, but be paranoid ... - stateSet = tmpNode->getStateSet(); - osg::StateAttribute* stateAttr; - stateAttr = stateSet->getTextureAttribute(0, osg::StateAttribute::TEXTURE); - osg::Texture2D* texture2D = dynamic_cast(stateAttr); - if (texture2D) - texture = texture2D; - } - return texture.release(); } -class SGSwitchUpdateCallback : public osg::NodeCallback { -public: - SGSwitchUpdateCallback(SGCondition* condition) : - mCondition(condition) {} - virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) - { - assert(dynamic_cast(node)); - osg::Switch* s = static_cast(node); - - if (mCondition && mCondition->test()) { - s->setAllChildrenOn(); - // note, callback is responsible for scenegraph traversal so - // should always include call traverse(node,nv) to ensure - // that the rest of cullbacks and the scene graph are traversed. - traverse(node, nv); - } else - s->setAllChildrenOff(); - } +namespace simgear +{ +using namespace std; +using namespace osg; +using simgear::CopyOp; -private: - SGSharedPtr mCondition; -}; +Node* copyModel(Node* model) +{ + const CopyOp::CopyFlags flags = (CopyOp::DEEP_COPY_ALL + & ~CopyOp::DEEP_COPY_TEXTURES + & ~CopyOp::DEEP_COPY_IMAGES + & ~CopyOp::DEEP_COPY_STATESETS + & ~CopyOp::DEEP_COPY_STATEATTRIBUTES + & ~CopyOp::DEEP_COPY_ARRAYS + & ~CopyOp::DEEP_COPY_PRIMITIVES + // This will preserve display lists ... + & ~CopyOp::DEEP_COPY_DRAWABLES + & ~CopyOp::DEEP_COPY_SHAPES); + return (CopyOp(flags))(model); +} - -//////////////////////////////////////////////////////////////////////// -// Global functions. -//////////////////////////////////////////////////////////////////////// - -osg::Node * -sgLoad3DModel( const string &fg_root, const string &path, - SGPropertyNode *prop_root, - double sim_time_sec, osg::Node *(*load_panel)(SGPropertyNode *), - SGModelData *data, - const SGPath& externalTexturePath ) +TextureUpdateVisitor::TextureUpdateVisitor(const osgDB::FilePathList& pathList) : + NodeAndDrawableVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN), + _pathList(pathList) { - osg::ref_ptr model; - SGPropertyNode props; - - // Load the 3D aircraft object itself - SGPath modelpath = path, texturepath = path; - if ( !ulIsAbsolutePathName( path.c_str() ) ) { - SGPath tmp = fg_root; - tmp.append(modelpath.str()); - modelpath = texturepath = tmp; - } +} - // Check for an XML wrapper - if (modelpath.str().substr(modelpath.str().size() - 4, 4) == ".xml") { - readProperties(modelpath.str(), &props); - if (props.hasValue("/path")) { - modelpath = modelpath.dir(); - modelpath.append(props.getStringValue("/path")); - if (props.hasValue("/texture-path")) { - texturepath = texturepath.dir(); - texturepath.append(props.getStringValue("/texture-path")); - } - } else { - if (!model) - model = new osg::Switch; - } - } +void TextureUpdateVisitor::apply(Node& node) +{ + StateSet* stateSet = cloneStateSet(node.getStateSet()); + if (stateSet) + node.setStateSet(stateSet); + traverse(node); +} - osgDB::FilePathList pathList = osgDB::getDataFilePathList(); - osgDB::Registry::instance()->initFilePathLists(); +void TextureUpdateVisitor::apply(Drawable& drawable) +{ + StateSet* stateSet = cloneStateSet(drawable.getStateSet()); + if (stateSet) + drawable.setStateSet(stateSet); +} - // Assume that textures are in - // the same location as the XML file. - if (!model) { - if (texturepath.extension() != "") - texturepath = texturepath.dir(); +Texture2D* TextureUpdateVisitor::textureReplace(int unit, const StateAttribute* attr) +{ + using namespace osgDB; + const Texture2D* texture = dynamic_cast(attr); - osgDB::Registry::instance()->getDataFilePathList().push_front(texturepath.str()); + if (!texture) + return 0; - model = osgDB::readNodeFile(modelpath.str()); - if (model == 0) - throw sg_io_exception("Failed to load 3D model", - sg_location(modelpath.str())); - } + const Image* image = texture->getImage(); + const string* fullFilePath = 0; + if (image) { + // The currently loaded file name + fullFilePath = &image->getFileName(); - osgDB::Registry::instance()->getDataFilePathList().push_front(externalTexturePath.str()); - - // Set up the alignment node - osg::ref_ptr alignmainmodel = new osg::MatrixTransform; - alignmainmodel->addChild(model.get()); - osg::Matrix res_matrix; - res_matrix.makeRotate( - props.getFloatValue("/offsets/pitch-deg", 0.0)*SG_DEGREES_TO_RADIANS, - osg::Vec3(0, 1, 0), - props.getFloatValue("/offsets/roll-deg", 0.0)*SG_DEGREES_TO_RADIANS, - osg::Vec3(1, 0, 0), - props.getFloatValue("/offsets/heading-deg", 0.0)*SG_DEGREES_TO_RADIANS, - osg::Vec3(0, 0, 1)); - - osg::Matrix tmat; - tmat.makeTranslate(props.getFloatValue("/offsets/x-m", 0.0), - props.getFloatValue("/offsets/y-m", 0.0), - props.getFloatValue("/offsets/z-m", 0.0)); - alignmainmodel->setMatrix(res_matrix*tmat); - - // Load sub-models - vector model_nodes = props.getChildren("model"); - for (unsigned i = 0; i < model_nodes.size(); i++) { - SGPropertyNode_ptr node = model_nodes[i]; - osg::ref_ptr align = new osg::MatrixTransform; - res_matrix.makeIdentity(); - res_matrix.makeRotate( - node->getDoubleValue("offsets/pitch-deg", 0.0)*SG_DEGREES_TO_RADIANS, - osg::Vec3(0, 1, 0), - node->getDoubleValue("offsets/roll-deg", 0.0)*SG_DEGREES_TO_RADIANS, - osg::Vec3(1, 0, 0), - node->getDoubleValue("offsets/heading-deg", 0.0)*SG_DEGREES_TO_RADIANS, - osg::Vec3(0, 0, 1)); - - tmat.makeIdentity(); - tmat.makeTranslate(node->getDoubleValue("offsets/x-m", 0), - node->getDoubleValue("offsets/y-m", 0), - node->getDoubleValue("offsets/z-m", 0)); - align->setMatrix(res_matrix*tmat); - - osg::ref_ptr kid; - const char* submodel = node->getStringValue("path"); - try { - kid = sgLoad3DModel( fg_root, submodel, prop_root, sim_time_sec, load_panel ); - - } catch (const sg_throwable &t) { - SG_LOG(SG_INPUT, SG_ALERT, "Failed to load submodel: " << t.getFormattedMessage()); - throw; + } else { + fullFilePath = &texture->getName(); + } + // The short name + string fileName = getSimpleFileName(*fullFilePath); + if (fileName.empty()) + return 0; + // The name that should be found with the current database path + string fullLiveryFile = findFileInPath(fileName, _pathList); + // If it is empty or they are identical then there is nothing to do + if (fullLiveryFile.empty() || fullLiveryFile == *fullFilePath) + return 0; + Image* newImage = readImageFile(fullLiveryFile); + if (!newImage) + return 0; + CopyOp copyOp(CopyOp::DEEP_COPY_ALL & ~CopyOp::DEEP_COPY_IMAGES); + Texture2D* newTexture = static_cast(copyOp(texture)); + if (!newTexture) { + return 0; + } else { + newTexture->setImage(newImage); + return newTexture; } - align->addChild(kid.get()); +} - align->setName(node->getStringValue("name", "")); +StateSet* TextureUpdateVisitor::cloneStateSet(const StateSet* stateSet) +{ + typedef std::pair Tex2D; + vector newTextures; + StateSet* result = 0; - SGPropertyNode *cond = node->getNode("condition", false); - if (cond) { - osg::ref_ptr sw = new osg::Switch; - sw->setUpdateCallback(new SGSwitchUpdateCallback(sgReadCondition(prop_root, cond))); - alignmainmodel->addChild(sw.get()); - sw->addChild(align.get()); - sw->setName("submodel condition switch"); - } else { - alignmainmodel->addChild(align.get()); + if (!stateSet) + return 0; + int numUnits = stateSet->getTextureAttributeList().size(); + if (numUnits > 0) { + for (int i = 0; i < numUnits; ++i) { + const StateAttribute* attr + = stateSet->getTextureAttribute(i, StateAttribute::TEXTURE); + Texture2D* newTexture = textureReplace(i, attr); + if (newTexture) + newTextures.push_back(Tex2D(i, newTexture)); + } + if (!newTextures.empty()) { + result = static_cast(stateSet->clone(CopyOp())); + for (vector::iterator i = newTextures.begin(); + i != newTextures.end(); + ++i) { + result->setTextureAttribute(i->first, i->second); + } + } } - } + return result; +} + +UserDataCopyVisitor::UserDataCopyVisitor() : + NodeVisitor(NodeVisitor::NODE_VISITOR, + NodeVisitor::TRAVERSE_ALL_CHILDREN) +{ +} - if ( load_panel ) { - // Load panels - vector panel_nodes = props.getChildren("panel"); - for (unsigned i = 0; i < panel_nodes.size(); i++) { - SG_LOG(SG_INPUT, SG_DEBUG, "Loading a panel"); - osg::ref_ptr panel = load_panel(panel_nodes[i]); - if (panel_nodes[i]->hasValue("name")) - panel->setName((char *)panel_nodes[i]->getStringValue("name")); - alignmainmodel->addChild(panel.get()); +void UserDataCopyVisitor::apply(Node& node) +{ + ref_ptr userData; + userData = SGSceneUserData::getSceneUserData(&node); + if (userData.valid()) { + SGSceneUserData* newUserData = new SGSceneUserData(*userData); + newUserData->setVelocity(0); + node.setUserData(newUserData); } - } + node.traverse(*this); +} - if (data) { - alignmainmodel->setUserData(data); - data->modelLoaded(path, &props, alignmainmodel.get()); - } +namespace +{ +class MakeEffectVisitor : public SplicingVisitor +{ +public: + typedef std::map EffectMap; + using SplicingVisitor::apply; + MakeEffectVisitor(const SGReaderWriterXMLOptions* options = 0) + : _options(options) + { + } + virtual void apply(osg::Group& node); + virtual void apply(osg::Geode& geode); + EffectMap& getEffectMap() { return _effectMap; } + const EffectMap& getEffectMap() const { return _effectMap; } + void setDefaultEffect(SGPropertyNode* effect) + { + _currentEffectParent = effect; + } + SGPropertyNode* getDefaultEffect() { return _currentEffectParent; } +protected: + EffectMap _effectMap; + SGPropertyNode_ptr _currentEffectParent; + osg::ref_ptr _options; +}; + +void MakeEffectVisitor::apply(osg::Group& node) +{ + SGPropertyNode_ptr savedEffectRoot; + const string& nodeName = node.getName(); + bool restoreEffect = false; + if (!nodeName.empty()) { + EffectMap::iterator eitr = _effectMap.find(nodeName); + if (eitr != _effectMap.end()) { + savedEffectRoot = _currentEffectParent; + _currentEffectParent = eitr->second; + restoreEffect = true; + } + } + SplicingVisitor::apply(node); + // If a new node was created, copy the user data too. + ref_ptr userData = SGSceneUserData::getSceneUserData(&node); + if (userData.valid() && _childStack.back().back().get() != &node) + _childStack.back().back()->setUserData(new SGSceneUserData(*userData)); + if (restoreEffect) + _currentEffectParent = savedEffectRoot; +} - std::vector animation_nodes; - animation_nodes = props.getChildren("animation"); - for (unsigned i = 0; i < animation_nodes.size(); ++i) - /// OSGFIXME: duh, why not only model????? - SGAnimation::animate(alignmainmodel.get(), animation_nodes[i], prop_root); +void MakeEffectVisitor::apply(osg::Geode& geode) +{ + if (pushNode(getNewNode(geode))) + return; + osg::StateSet* ss = geode.getStateSet(); + if (!ss) { + pushNode(&geode); + return; + } + SGPropertyNode_ptr ssRoot = new SGPropertyNode; + makeParametersFromStateSet(ssRoot, ss); + SGPropertyNode_ptr effectRoot = new SGPropertyNode; + effect::mergePropertyTrees(effectRoot, ssRoot, _currentEffectParent); + Effect* effect = makeEffect(effectRoot, true, _options); + EffectGeode* eg = dynamic_cast(&geode); + if (eg) { + eg->setEffect(effect); + } else { + eg = new EffectGeode; + eg->setEffect(effect); + ref_ptr userData = SGSceneUserData::getSceneUserData(&geode); + if (userData.valid()) + eg->setUserData(new SGSceneUserData(*userData)); + for (int i = 0; i < geode.getNumDrawables(); ++i) + eg->addDrawable(geode.getDrawable(i)); + } + pushResultNode(&geode, eg); - // restore old path list - osgDB::setDataFilePathList(pathList); +} - if (props.hasChild("debug-outfile")) { - std::string outputfile = props.getStringValue("debug-outfile", - "debug-model.osg"); - osgDB::writeNodeFile(*alignmainmodel, outputfile); - } +} - return alignmainmodel.release(); +namespace +{ +class DefaultEffect : public simgear::Singleton +{ +public: + DefaultEffect() + { + _effect = new SGPropertyNode; + makeChild(_effect.ptr(), "inherits-from") + ->setStringValue("Effects/model-default"); + } + virtual ~DefaultEffect() {} + SGPropertyNode* getEffect() { return _effect.ptr(); } +protected: + SGPropertyNode_ptr _effect; +}; } +ref_ptr instantiateEffects(osg::Node* modelGroup, + PropertyList& effectProps, + const SGReaderWriterXMLOptions* options) +{ + SGPropertyNode_ptr defaultEffectPropRoot; + MakeEffectVisitor visitor(options); + MakeEffectVisitor::EffectMap& emap = visitor.getEffectMap(); + for (PropertyList::iterator itr = effectProps.begin(), + end = effectProps.end(); + itr != end; + ++itr) + { + SGPropertyNode_ptr configNode = *itr; + std::vector objectNames = + configNode->getChildren("object-name"); + SGPropertyNode* defaultNode = configNode->getChild("default"); + if (defaultNode && defaultNode->getValue()) + defaultEffectPropRoot = configNode; + BOOST_FOREACH(SGPropertyNode_ptr objNameNode, objectNames) { + emap.insert(make_pair(objNameNode->getStringValue(), configNode)); + } + configNode->removeChild("default"); + configNode->removeChildren("object-name"); + } + if (!defaultEffectPropRoot) + defaultEffectPropRoot = DefaultEffect::instance()->getEffect(); + visitor.setDefaultEffect(defaultEffectPropRoot.ptr()); + modelGroup->accept(visitor); + osg::NodeList& result = visitor.getResults(); + return ref_ptr(result[0].get()); +} +} // end of model.cxx