X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fscene%2Fmodel%2FSGReaderWriterXML.cxx;h=50943dcd9e48b00a1a0f9f66e778ae4be6bbc188;hb=a592488f3e18a68bdf020a0413cd45356dfa638c;hp=7b56bc6f910f05575179c3ce276cb511fe858147;hpb=c105c5a449a2ebc214d48b6cce9500a4350b520e;p=simgear.git diff --git a/simgear/scene/model/SGReaderWriterXML.cxx b/simgear/scene/model/SGReaderWriterXML.cxx index 7b56bc6f..50943dcd 100644 --- a/simgear/scene/model/SGReaderWriterXML.cxx +++ b/simgear/scene/model/SGReaderWriterXML.cxx @@ -20,7 +20,16 @@ # include #endif +#include +//yuck +#include +#include + +#include + +#include #include +#include #include #include #include @@ -32,71 +41,56 @@ #include #include #include +#include #include "modellib.hxx" -#include "SGPagedLOD.hxx" #include "SGReaderWriterXML.hxx" -#include "SGReaderWriterXMLOptions.hxx" #include "animation.hxx" #include "particles.hxx" #include "model.hxx" +#include "SGText.hxx" +#include "SGMaterialAnimation.hxx" -#include "SGReaderWriterXMLOptions.hxx" -#include "SGReaderWriterXML.hxx" - +using namespace std; using namespace simgear; +using namespace osg; -osg::Node * -sgLoad3DModel_internal(const string &path, - SGPropertyNode *prop_root, - SGModelData *data = 0, - osg::Node *(*load_panel)(SGPropertyNode *) = 0, +static osg::Node * +sgLoad3DModel_internal(const SGPath& path, + const osgDB::Options* options, SGPropertyNode *overlay = 0); -const char* SGReaderWriterXML::className() const + +SGReaderWriterXML::SGReaderWriterXML() +{ + supportsExtension("xml", "SimGear xml database format"); +} + +SGReaderWriterXML::~SGReaderWriterXML() { - return "XML database reader"; } -bool SGReaderWriterXML::acceptsExtension(const std::string& extension) const +const char* SGReaderWriterXML::className() const { - return (osgDB::equalCaseInsensitive(extension, "xml")); + return "XML database reader"; } osgDB::ReaderWriter::ReadResult SGReaderWriterXML::readNode(const std::string& fileName, - const osgDB::ReaderWriter::Options* options) const + const osgDB::Options* options) const { - // SG_LOG(SG_GENERAL, SG_ALERT, "SGReaderWriterXML::readNode(" << fileName << ")"); - - std::string ext = osgDB::getLowerCaseFileExtension(fileName); - if (!acceptsExtension(ext)) - return ReadResult::FILE_NOT_HANDLED; - - const SGReaderWriterXMLOptions* xmlOptions - = dynamic_cast(options); - - string fg_root; - SGPropertyNode *prop_root=0; - osg::Node *(*load_panel)(SGPropertyNode *)=0; - SGModelData *model_data=0; - SGPath externalTexturePath; - - if (xmlOptions) { - prop_root = xmlOptions->getPropRoot(); - load_panel = xmlOptions->getLoadPanel(); - model_data = xmlOptions->getModelData(); - } - - fg_root=osgDB::Registry::instance()->getDataFilePathList().front(); - osg::Node *result=0; - try { - result=sgLoad3DModel_internal(fileName, prop_root, model_data, load_panel); - } catch (const sg_throwable &t) { - SG_LOG(SG_INPUT, SG_ALERT, "Failed to load model: " << t.getFormattedMessage()); + SGPath p = SGModelLib::findDataFile(fileName); + if (!p.exists()) { + return ReadResult::FILE_NOT_FOUND; + } + + result=sgLoad3DModel_internal(p, options); + } catch (const sg_exception &t) { + SG_LOG(SG_INPUT, SG_ALERT, "Failed to load model: " << t.getFormattedMessage() + << "\n\tfrom:" << fileName); result=new osg::Node; } if (result) @@ -128,47 +122,135 @@ private: SGSharedPtr mCondition; }; -osg::Node * -sgLoad3DModel_internal(const string &path, - SGPropertyNode *prop_root, - SGModelData *data, - osg::Node *(*load_panel)(SGPropertyNode *), + +// 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. +namespace { +class SGDatabaseReference : public osg::Observer +{ +public: + SGDatabaseReference(osg::Referenced* referenced) : + mReferenced(referenced) + { } + virtual void objectDeleted(void*) + { + mReferenced = 0; + } +private: + osg::ref_ptr mReferenced; +}; + +void makeEffectAnimations(PropertyList& animation_nodes, + PropertyList& effect_nodes) +{ + for (PropertyList::iterator itr = animation_nodes.begin(); + itr != animation_nodes.end(); + ++itr) { + SGPropertyNode_ptr effectProp; + SGPropertyNode* animProp = itr->ptr(); + SGPropertyNode* typeProp = animProp->getChild("type"); + if (!typeProp) + continue; + const char* typeString = typeProp->getStringValue(); + if (!strcmp(typeString, "material")) { + effectProp + = SGMaterialAnimation::makeEffectProperties(animProp); + } else if (!strcmp(typeString, "shader")) { + + SGPropertyNode* shaderProp = animProp->getChild("shader"); + if (!shaderProp || strcmp(shaderProp->getStringValue(), "chrome")) + continue; + *itr = 0; // effect replaces animation + SGPropertyNode* textureProp = animProp->getChild("texture"); + if (!textureProp) + continue; + effectProp = new SGPropertyNode(); + makeChild(effectProp.ptr(), "inherits-from") + ->setValue("Effects/chrome"); + SGPropertyNode* paramsProp = makeChild(effectProp.get(), "parameters"); + makeChild(paramsProp, "chrome-texture") + ->setValue(textureProp->getStringValue()); + } + if (effectProp.valid()) { + PropertyList objectNameNodes = animProp->getChildren("object-name"); + for (PropertyList::iterator objItr = objectNameNodes.begin(), + end = objectNameNodes.end(); + objItr != end; + ++objItr) + effectProp->addChild("object-name") + ->setStringValue((*objItr)->getStringValue()); + effect_nodes.push_back(effectProp); + + } + } + animation_nodes.erase(remove_if(animation_nodes.begin(), + animation_nodes.end(), + !boost::bind(&SGPropertyNode_ptr::valid, + _1)), + animation_nodes.end()); +} +} + +static osg::Node * +sgLoad3DModel_internal(const SGPath& path, + const osgDB::Options* dbOptions, SGPropertyNode *overlay) { - if ( !prop_root ) { - SG_LOG(SG_GENERAL, SG_ALERT, "prop_root NULL: " << path); + if (!path.exists()) { + SG_LOG(SG_INPUT, SG_ALERT, "Failed to load file: \"" << path.str() << "\""); + return NULL; } - string fg_root=osgDB::Registry::instance()->getDataFilePathList().front(); + osg::ref_ptr options; + options = SGReaderWriterOptions::copyOrCreate(dbOptions); + + SGPath modelpath(path); + SGPath texturepath(path); + SGPath modelDir(modelpath.dir()); + + SGSharedPtr prop_root = options->getPropertyNode(); + if (!prop_root.valid()) + prop_root = new SGPropertyNode; + // The model data appear to be only used in the topmost model + osg::ref_ptr data = options->getModelData(); + options->setModelData(0); + osg::ref_ptr model; osg::ref_ptr group; SGPropertyNode_ptr props = new SGPropertyNode; - // Load the 3D 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") { - try { + if (modelpath.extension() == "xml") { + try { readProperties(modelpath.str(), props); - } catch (const sg_throwable &t) { - SG_LOG(SG_INPUT, SG_ALERT, "Failed to load xml: " << t.getFormattedMessage()); + } catch (const sg_exception &t) { + SG_LOG(SG_INPUT, SG_ALERT, "Failed to load xml: " + << t.getFormattedMessage()); throw; } if (overlay) copyProperties(overlay, props); if (props->hasValue("/path")) { - modelpath = modelpath.dir(); - modelpath.append(props->getStringValue("/path")); + string modelPathStr = props->getStringValue("/path"); + modelpath = SGModelLib::findDataFile(modelPathStr, NULL, modelDir); + if (modelpath.isNull()) + throw sg_io_exception("Model file not found: '" + modelPathStr + "'", + path.str()); + if (props->hasValue("/texture-path")) { - texturepath = texturepath.dir(); - texturepath.append(props->getStringValue("/texture-path")); + string texturePathStr = props->getStringValue("/texture-path"); + texturepath = SGModelLib::findDataFile(texturePathStr, NULL, modelDir); + if (texturepath.isNull()) + throw sg_io_exception("Texture file not found: '" + texturePathStr + "'", + path.str()); } } else { model = new osg::Node; @@ -177,23 +259,41 @@ sgLoad3DModel_internal(const string &path, SGPropertyNode *mp = props->getNode("multiplay"); if (mp && prop_root && prop_root->getParent()) copyProperties(mp, prop_root); + } else { + // model without wrapper } - osg::ref_ptr options - = new osgDB::ReaderWriter::Options(*osgDB::Registry::instance() - ->getOptions()); - // Assume that textures are in // the same location as the XML file. if (!model) { - if (texturepath.extension() != "") + if (!texturepath.extension().empty()) texturepath = texturepath.dir(); options->setDatabasePath(texturepath.str()); - model = osgDB::readNodeFile(modelpath.str(), options.get()); - if (model == 0) - throw sg_io_exception("Failed to load 3D model", - sg_location(modelpath.str())); + osgDB::ReaderWriter::ReadResult modelResult; + modelResult = osgDB::readNodeFile(modelpath.str(), options.get()); + if (!modelResult.validNode()) + throw sg_io_exception("Failed to load 3D model:" + modelResult.message(), + modelpath.str()); + model = copyModel(modelResult.getNode()); + // Add an extra reference to the model stored in the database. + // That is 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(modelResult.getNode()); + model->addObserver(databaseReference); + + // Update liveries + TextureUpdateVisitor liveryUpdate(options->getDatabasePathList()); + model->accept(liveryUpdate); + + // Copy the userdata fields, still sharing the boundingvolumes, + // but introducing new data for velocities. + UserDataCopyVisitor userDataCopyVisitor; + model->accept(userDataCopyVisitor); } model->setName(modelpath.str()); @@ -203,6 +303,7 @@ sgLoad3DModel_internal(const string &path, if (offsets) { needTransform=true; osg::MatrixTransform *alignmainmodel = new osg::MatrixTransform; + alignmainmodel->setDataVariance(osg::Object::STATIC); osg::Matrix res_matrix; res_matrix.makeRotate( offsets->getFloatValue("pitch-deg", 0.0)*SG_DEGREES_TO_RADIANS, @@ -231,26 +332,31 @@ sgLoad3DModel_internal(const string &path, SGPath submodelpath; osg::ref_ptr submodel; - string submodelFileName = sub_props->getStringValue("path"); - if ( submodelFileName.size() > 2 && submodelFileName.substr( 0, 2 ) == "./" ) { - submodelpath = modelpath.dir(); - submodelpath.append( submodelFileName.substr( 2 ) ); - } else { - submodelpath = submodelFileName; + + string subPathStr = sub_props->getStringValue("path"); + SGPath submodelPath = SGModelLib::findDataFile(subPathStr, + NULL, modelDir); + + if (submodelPath.isNull()) { + SG_LOG(SG_INPUT, SG_ALERT, "Failed to load file: \"" << subPathStr << "\""); + continue; } + try { - submodel = sgLoad3DModel_internal(submodelpath.str(), prop_root, 0, load_panel, + submodel = sgLoad3DModel_internal(submodelPath, options.get(), sub_props->getNode("overlay")); - } catch (const sg_throwable &t) { - SG_LOG(SG_INPUT, SG_ALERT, "Failed to load submodel: " << t.getFormattedMessage()); - throw; + } catch (const sg_exception &t) { + SG_LOG(SG_INPUT, SG_ALERT, "Failed to load submodel: " << t.getFormattedMessage() + << "\n\tfrom:" << t.getOrigin()); + continue; } - osg::ref_ptr submodel_final=submodel.get(); + osg::ref_ptr submodel_final = submodel; SGPropertyNode *offs = sub_props->getNode("offsets", false); if (offs) { osg::Matrix res_matrix; osg::ref_ptr align = new osg::MatrixTransform; + align->setDataVariance(osg::Object::STATIC); res_matrix.makeIdentity(); res_matrix.makeRotate( offs->getDoubleValue("pitch-deg", 0.0)*SG_DEGREES_TO_RADIANS, @@ -267,7 +373,7 @@ sgLoad3DModel_internal(const string &path, offs->getDoubleValue("z-m", 0)); align->setMatrix(res_matrix*tmat); align->addChild(submodel.get()); - submodel_final=align.get(); + submodel_final = align; } submodel_final->setName(sub_props->getStringValue("name", "")); @@ -283,6 +389,7 @@ sgLoad3DModel_internal(const string &path, } } // end of submodel loading + osg::Node *(*load_panel)(SGPropertyNode *) = options->getLoadPanel(); if ( load_panel ) { // Load panels vector panel_nodes = props->getChildren("panel"); @@ -290,50 +397,65 @@ sgLoad3DModel_internal(const string &path, 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")); + panel->setName(panel_nodes[i]->getStringValue("name")); group->addChild(panel.get()); } } - std::vector particle_nodes; - particle_nodes = props->getChildren("particlesystem"); - for (unsigned i = 0; i < particle_nodes.size(); ++i) { - if (i==0) { - if (texturepath.extension() != "") - texturepath = texturepath.dir(); - - options->setDatabasePath(texturepath.str()); + if (dbOptions->getPluginStringData("SimGear::PARTICLESYSTEM") != "OFF") { + std::vector particle_nodes; + particle_nodes = props->getChildren("particlesystem"); + for (unsigned i = 0; i < particle_nodes.size(); ++i) { + osg::ref_ptr options2; + options2 = new SGReaderWriterOptions(*options); + if (i==0) { + if (!texturepath.extension().empty()) + texturepath = texturepath.dir(); + + options2->setDatabasePath(texturepath.str()); + } + group->addChild(Particles::appendParticles(particle_nodes[i], + prop_root, + options2.get())); } - group->addChild(Particles::appendParticles(particle_nodes[i], + } + + std::vector text_nodes; + text_nodes = props->getChildren("text"); + for (unsigned i = 0; i < text_nodes.size(); ++i) { + group->addChild(SGText::appendText(text_nodes[i], prop_root, options.get())); } - - if (data) { - SGPropertyNode *nasal = props->getNode("nasal", false); - data->setProps(nasal); - group->setUserData(data); - //data->modelLoaded(path, nasal, group.get()); + PropertyList effect_nodes = props->getChildren("effect"); + PropertyList animation_nodes = props->getChildren("animation"); + PropertyList light_nodes = props->getChildren("light"); + // Some material animations (eventually all) are actually effects. + makeEffectAnimations(animation_nodes, effect_nodes); + { + ref_ptr modelWithEffects + = instantiateEffects(group.get(), effect_nodes, options.get()); + group = static_cast(modelWithEffects.get()); } - - 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(group.get(), animation_nodes[i], prop_root, - options.get()); + options.get(), path.str(), i); - if (props->hasChild("debug-outfile")) { - std::string outputfile = props->getStringValue("debug-outfile", - "debug-model.osg"); - osgDB::writeNodeFile(*group, outputfile); - } if (!needTransform && group->getNumChildren() < 2) { model = group->getChild(0); group->removeChild(model.get()); - model->setUserData(group->getUserData()); + if (data.valid()) + data->modelLoaded(modelpath.str(), props, model.get()); return model.release(); } + if (data.valid()) + data->modelLoaded(modelpath.str(), props, group.get()); + if (props->hasChild("debug-outfile")) { + std::string outputfile = props->getStringValue("debug-outfile", + "debug-model.osg"); + osgDB::writeNodeFile(*group, outputfile); + } return group.release(); }