X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fscene%2Fmodel%2Fmodel.cxx;h=4a2349be603cac35bf1aa7153c83d789577647fa;hb=7aa6fd479da920a6eb95f4b7da2010906928b619;hp=edb91f27a8e1bae88fc28c3072f612d530b899a2;hpb=f72b3882c36c83f3f83c9523a82f7d0c5ed519da;p=simgear.git diff --git a/simgear/scene/model/model.cxx b/simgear/scene/model/model.cxx index edb91f27..4a2349be 100644 --- a/simgear/scene/model/model.cxx +++ b/simgear/scene/model/model.cxx @@ -7,18 +7,29 @@ #include #endif -#include // for strcmp() - -#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 "model.hxx" @@ -26,236 +37,410 @@ SG_USING_STD(vector); SG_USING_STD(set); -bool sgUseDisplayList = true; - -//////////////////////////////////////////////////////////////////////// -// Global state -//////////////////////////////////////////////////////////////////////// -static bool -model_filter = true; - - -//////////////////////////////////////////////////////////////////////// -// Static utility functions. -//////////////////////////////////////////////////////////////////////// - -static int -model_filter_callback (ssgEntity * entity, int mask) -{ - return model_filter ? 1 : 0; -} - -/** - * Callback to update an animation. - */ -static int -animation_callback (ssgEntity * entity, int mask) -{ - return ((SGAnimation *)entity->getUserData())->update(); -} +// 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; +}; -/** - * Callback to restore the state after an animation. - */ -static int -restore_callback (ssgEntity * entity, int mask) -{ - ((SGAnimation *)entity->getUserData())->restore(); - return 1; -} +// 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; +}; -/** - * Locate a named SSG node in a branch. - */ -static ssgEntity * -find_named_node (ssgEntity * node, const char * name) -{ - char * node_name = node->getName(); - if (node_name != 0 && !strcmp(name, node_name)) - return node; - else if (node->isAKindOf(ssgTypeBranch())) { - int nKids = node->getNumKids(); - for (int i = 0; i < nKids; i++) { - ssgEntity * result = - find_named_node(((ssgBranch*)node)->getKid(i), name); - if (result != 0) - return result; +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); } - } - return 0; -} + } +}; -/** - * Splice a branch in between all child nodes and their parents. - */ -static void -splice_branch (ssgBranch * branch, ssgEntity * child) -{ - int nParents = child->getNumParents(); - branch->addKid(child); - for (int i = 0; i < nParents; i++) { - ssgBranch * parent = child->getParent(i); - parent->replaceKid(child, branch); +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); } -} +}; -/** - * Make an offset matrix from rotations and position offset. - */ -void -sgMakeOffsetsMatrix( sgMat4 * result, double h_rot, double p_rot, double r_rot, - double x_off, double y_off, double z_off ) -{ - sgMat4 rot_matrix; - sgMat4 pos_matrix; - sgMakeRotMat4(rot_matrix, h_rot, p_rot, r_rot); - sgMakeTransMat4(pos_matrix, x_off, y_off, z_off); - sgMultMat4(*result, pos_matrix, rot_matrix); -} +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; + } -void -sgMakeAnimation( ssgBranch * model, - const char * name, - vector &name_nodes, - SGPropertyNode *prop_root, - SGPropertyNode_ptr node, - double sim_time_sec, - SGPath &texture_path, - set &ignore_branches ) -{ - bool ignore = false; - SGAnimation * animation = 0; - const char * type = node->getStringValue("type", "none"); - if (!strcmp("none", type)) { - animation = new SGNullAnimation(node); - } else if (!strcmp("range", type)) { - animation = new SGRangeAnimation(prop_root, node); - } else if (!strcmp("billboard", type)) { - animation = new SGBillboardAnimation(node); - } else if (!strcmp("select", type)) { - animation = new SGSelectAnimation(prop_root, node); - } else if (!strcmp("spin", type)) { - animation = new SGSpinAnimation(prop_root, node, sim_time_sec ); - } else if (!strcmp("timed", type)) { - animation = new SGTimedAnimation(node); - } else if (!strcmp("rotate", type)) { - animation = new SGRotateAnimation(prop_root, node); - } else if (!strcmp("translate", type)) { - animation = new SGTranslateAnimation(prop_root, node); - } else if (!strcmp("scale", type)) { - animation = new SGScaleAnimation(prop_root, node); - } else if (!strcmp("texrotate", type)) { - animation = new SGTexRotateAnimation(prop_root, node); - } else if (!strcmp("textranslate", type)) { - animation = new SGTexTranslateAnimation(prop_root, node); - } else if (!strcmp("texmultiple", type)) { - animation = new SGTexMultipleAnimation(prop_root, node); - } else if (!strcmp("blend", type)) { - animation = new SGBlendAnimation(prop_root, node); - ignore = true; - } else if (!strcmp("alpha-test", type)) { - animation = new SGAlphaTestAnimation(node); - } else if (!strcmp("material", type)) { - animation = new SGMaterialAnimation(prop_root, node, texture_path); - } else if (!strcmp("flash", type)) { - animation = new SGFlashAnimation(node); - } else if (!strcmp("dist-scale", type)) { - animation = new SGDistScaleAnimation(node); - } else if (!strcmp("noshadow", type)) { - animation = new SGShadowAnimation(prop_root, node); - } else if (!strcmp("shader", type)) { - animation = new SGShaderAnimation(prop_root, node); - } else { - animation = new SGNullAnimation(node); - SG_LOG(SG_INPUT, SG_WARN, "Unknown animation type " << type); + 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; } - if (name != 0) - animation->setName((char *)name); - - ssgEntity * object; - if (name_nodes.size() > 0) { - object = find_named_node(model, name_nodes[0]->getStringValue()); - if (object == 0) { - SG_LOG(SG_INPUT, SG_ALERT, "Object " << name_nodes[0]->getStringValue() - << " not found"); - delete animation; - animation = 0; + 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); } - } else { - object = model; - } + 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 ( animation == 0 ) - return; - - ssgBranch * branch = animation->getBranch(); - splice_branch(branch, object); - - for (unsigned int i = 1; i < name_nodes.size(); i++) { - const char * name = name_nodes[i]->getStringValue(); - object = find_named_node(model, name); - if (object == 0) { - SG_LOG(SG_INPUT, SG_ALERT, "Object " << name << " not found"); - delete animation; - animation = 0; - } else { - ssgBranch * oldParent = object->getParent(0); - branch->addKid(object); - oldParent->removeKid(object); + 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; } +}; - if ( animation != 0 ) { - animation->init(); - branch->setUserData(animation); - branch->setTravCallback(SSG_CALLBACK_PRETRAV, animation_callback); - branch->setTravCallback(SSG_CALLBACK_POSTTRAV, restore_callback); - if ( ignore ) { - ignore_branches.insert( branch ); - } +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; -static void makeDList( ssgBranch *b, const set &ignore ) +osg::Texture2D* +SGLoadTexture2D(const std::string& path, bool wrapu, bool wrapv, int) { - int nb = b->getNumKids(); - for (int i = 0; igetKid(i); - if (e->isAKindOf(ssgTypeLeaf())) { - if( ((ssgLeaf*)e)->getNumVertices() > 0) - ((ssgLeaf*)e)->makeDList(); - } else if (e->isAKindOf(ssgTypeBranch()) && ignore.find((ssgBranch *)e) == ignore.end()) { - makeDList( (ssgBranch*)e, ignore ); + osg::Image* image = osgDB::readImageFile(path); + osg::ref_ptr texture = new osg::Texture2D; + texture->setImage(image); + if (wrapu) + texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT); + else + texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP); + if (wrapv) + texture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT); + else + texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP); + + if (image) { + int s = image->s(); + int t = image->t(); + + if (s <= t && 32 <= s) { + SGSceneFeatures::instance()->setTextureCompression(texture.get()); + } else if (t < s && 32 <= t) { + SGSceneFeatures::instance()->setTextureCompression(texture.get()); } } + + // 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 sgLoaderOptions : public ssgLoaderOptions { +class SGSwitchUpdateCallback : public osg::NodeCallback { public: - void endLoad() {} // Avoid clearing the texture cache after every model load -}; + 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(); + } -static sgLoaderOptions loaderOptions; +private: + SGSharedPtr mCondition; +}; //////////////////////////////////////////////////////////////////////// // Global functions. //////////////////////////////////////////////////////////////////////// -ssgBranch * +osg::Node * sgLoad3DModel( const string &fg_root, const string &path, SGPropertyNode *prop_root, - double sim_time_sec, ssgEntity *(*load_panel)(SGPropertyNode *), - SGModelData *data ) + double sim_time_sec, osg::Node *(*load_panel)(SGPropertyNode *), + SGModelData *data, + const SGPath& externalTexturePath ) { - ssgBranch * model = 0; + osg::ref_ptr model; SGPropertyNode props; - // Load the 3D aircraft object itself + // Load the 3D aircraft object itself SGPath modelpath = path, texturepath = path; if ( !ulIsAbsolutePathName( path.c_str() ) ) { SGPath tmp = fg_root; @@ -263,7 +448,7 @@ sgLoad3DModel( const string &fg_root, const string &path, modelpath = texturepath = tmp; } - // Check for an XML wrapper + // Check for an XML wrapper if (modelpath.str().substr(modelpath.str().size() - 4, 4) == ".xml") { readProperties(modelpath.str(), &props); if (props.hasValue("/path")) { @@ -274,57 +459,70 @@ sgLoad3DModel( const string &fg_root, const string &path, texturepath.append(props.getStringValue("/texture-path")); } } else { - if (model == 0) - model = new ssgBranch; + if (!model) + model = new osg::Switch; } } - // Assume that textures are in - // the same location as the XML file. - if (model == 0) { + osgDB::FilePathList pathList = osgDB::getDataFilePathList(); + osgDB::Registry::instance()->initFilePathLists(); + + // Assume that textures are in + // the same location as the XML file. + if (!model) { if (texturepath.extension() != "") texturepath = texturepath.dir(); - ssgTexturePath((char *)texturepath.c_str()); - model = (ssgBranch *)ssgLoad((char *)modelpath.c_str(), &loaderOptions); + osgDB::Registry::instance()->getDataFilePathList().push_front(texturepath.str()); + + model = osgDB::readNodeFile(modelpath.str()); if (model == 0) throw sg_io_exception("Failed to load 3D model", - sg_location(modelpath.str())); + sg_location(modelpath.str())); } - // Set up the alignment node - ssgTransform * alignmainmodel = new ssgTransform; - if ( load_panel == 0 ) - alignmainmodel->setTravCallback( SSG_CALLBACK_PRETRAV, model_filter_callback ); - alignmainmodel->addKid(model); - sgMat4 res_matrix; - sgMakeOffsetsMatrix(&res_matrix, - props.getFloatValue("/offsets/heading-deg", 0.0), - props.getFloatValue("/offsets/roll-deg", 0.0), - props.getFloatValue("/offsets/pitch-deg", 0.0), - props.getFloatValue("/offsets/x-m", 0.0), - props.getFloatValue("/offsets/y-m", 0.0), - props.getFloatValue("/offsets/z-m", 0.0)); - alignmainmodel->setTransform(res_matrix); - - unsigned int i; - - // Load sub-models + + 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 (i = 0; i < model_nodes.size(); i++) { + for (unsigned i = 0; i < model_nodes.size(); i++) { SGPropertyNode_ptr node = model_nodes[i]; - ssgTransform * align = new ssgTransform; - sgMat4 res_matrix; - sgMakeOffsetsMatrix(&res_matrix, - node->getFloatValue("offsets/heading-deg", 0.0), - node->getFloatValue("offsets/roll-deg", 0.0), - node->getFloatValue("offsets/pitch-deg", 0.0), - node->getFloatValue("offsets/x-m", 0.0), - node->getFloatValue("offsets/y-m", 0.0), - node->getFloatValue("offsets/z-m", 0.0)); - align->setTransform(res_matrix); - - ssgBranch * kid; - const char * submodel = node->getStringValue("path"); + 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 ); @@ -332,63 +530,55 @@ sgLoad3DModel( const string &fg_root, const string &path, SG_LOG(SG_INPUT, SG_ALERT, "Failed to load submodel: " << t.getFormattedMessage()); throw; } - align->addKid(kid); + align->addChild(kid.get()); + align->setName(node->getStringValue("name", "")); - model->addKid(align); + + 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 ( load_panel ) { - // Load panels + // Load panels vector panel_nodes = props.getChildren("panel"); - for (i = 0; i < panel_nodes.size(); i++) { + for (unsigned i = 0; i < panel_nodes.size(); i++) { SG_LOG(SG_INPUT, SG_DEBUG, "Loading a panel"); - ssgEntity * panel = load_panel(panel_nodes[i]); + osg::ref_ptr panel = load_panel(panel_nodes[i]); if (panel_nodes[i]->hasValue("name")) panel->setName((char *)panel_nodes[i]->getStringValue("name")); - model->addKid(panel); + alignmainmodel->addChild(panel.get()); } } if (data) { alignmainmodel->setUserData(data); - data->modelLoaded(path, &props, alignmainmodel); - } - // Load animations - set ignore_branches; - vector animation_nodes = props.getChildren("animation"); - for (i = 0; i < animation_nodes.size(); i++) { - const char * name = animation_nodes[i]->getStringValue("name", 0); - vector name_nodes = - animation_nodes[i]->getChildren("object-name"); - sgMakeAnimation( model, name, name_nodes, prop_root, animation_nodes[i], - sim_time_sec, texturepath, ignore_branches); + data->modelLoaded(path, &props, alignmainmodel.get()); } -#if PLIB_VERSION > 183 - if ( model != 0 && sgUseDisplayList ) { - makeDList( model, ignore_branches ); - } -#endif + 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); - int m = props.getIntValue("dump", 0); - if (m > 0) - model->print(stderr, "", m - 1); - - return alignmainmodel; -} + // restore old path list + osgDB::setDataFilePathList(pathList); -bool -sgSetModelFilter( bool filter ) -{ - bool old = model_filter; - model_filter = filter; - return old; -} + if (props.hasChild("debug-outfile")) { + std::string outputfile = props.getStringValue("debug-outfile", + "debug-model.osg"); + osgDB::writeNodeFile(*alignmainmodel, outputfile); + } -bool -sgCheckAnimationBranch (ssgEntity * entity) -{ - return entity->getTravCallback(SSG_CALLBACK_PRETRAV) == animation_callback; + return alignmainmodel.release(); } // end of model.cxx