1 // model.cxx - manage a 3D aircraft model.
2 // Written by David Megginson, started 2002.
4 // This file is in the Public Domain, and comes with no warranty.
7 #include <simgear_config.h>
10 #include <osg/observer_ptr>
11 #include <osg/ref_ptr>
13 #include <osg/NodeCallback>
15 #include <osg/MatrixTransform>
16 #include <osgDB/Archive>
17 #include <osgDB/FileNameUtils>
18 #include <osgDB/FileUtils>
19 #include <osgDB/ReadFile>
20 #include <osgDB/WriteFile>
21 #include <osgDB/Registry>
22 #include <osgDB/SharedStateManager>
23 #include <osgUtil/Optimizer>
25 #include <simgear/scene/util/SGSceneFeatures.hxx>
26 #include <simgear/scene/util/SGStateAttributeVisitor.hxx>
27 #include <simgear/scene/util/SGTextureStateAttributeVisitor.hxx>
29 #include <simgear/structure/exception.hxx>
30 #include <simgear/props/props.hxx>
31 #include <simgear/props/props_io.hxx>
32 #include <simgear/props/condition.hxx>
34 #include "animation.hxx"
36 #include "particles.hxx"
40 using namespace simgear;
43 SGLoadTexture2D(bool staticTexture, const std::string& path,
44 const osgDB::ReaderWriter::Options* options,
45 bool wrapu, bool wrapv, int)
49 image = osgDB::readImageFile(path, options);
51 image = osgDB::readImageFile(path);
52 osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D;
53 texture->setImage(image);
55 texture->setDataVariance(osg::Object::STATIC);
57 texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
59 texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP);
61 texture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
63 texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP);
69 if (s <= t && 32 <= s) {
70 SGSceneFeatures::instance()->setTextureCompression(texture.get());
71 } else if (t < s && 32 <= t) {
72 SGSceneFeatures::instance()->setTextureCompression(texture.get());
76 // Make sure the texture is shared if we already have the same texture
79 osg::ref_ptr<osg::Node> tmpNode = new osg::Node;
80 osg::StateSet* stateSet = tmpNode->getOrCreateStateSet();
81 stateSet->setTextureAttribute(0, texture.get());
83 // OSGFIXME: don't forget that mutex here
84 osgDB::Registry* registry = osgDB::Registry::instance();
85 registry->getSharedStateManager()->share(tmpNode.get(), 0);
87 // should be the same, but be paranoid ...
88 stateSet = tmpNode->getStateSet();
89 osg::StateAttribute* stateAttr;
90 stateAttr = stateSet->getTextureAttribute(0, osg::StateAttribute::TEXTURE);
91 osg::Texture2D* texture2D = dynamic_cast<osg::Texture2D*>(stateAttr);
96 return texture.release();
99 class SGSwitchUpdateCallback : public osg::NodeCallback {
101 SGSwitchUpdateCallback(SGCondition* condition) :
102 mCondition(condition) {}
103 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
105 assert(dynamic_cast<osg::Switch*>(node));
106 osg::Switch* s = static_cast<osg::Switch*>(node);
108 if (mCondition && mCondition->test()) {
109 s->setAllChildrenOn();
110 // note, callback is responsible for scenegraph traversal so
111 // should always include call traverse(node,nv) to ensure
112 // that the rest of cullbacks and the scene graph are traversed.
115 s->setAllChildrenOff();
119 SGSharedPtr<SGCondition> mCondition;
123 ////////////////////////////////////////////////////////////////////////
125 ////////////////////////////////////////////////////////////////////////
128 sgLoad3DModel( const string &fg_root, const string &path,
129 SGPropertyNode *prop_root,
130 double sim_time_sec, osg::Node *(*load_panel)(SGPropertyNode *),
132 const SGPath& externalTexturePath )
134 osg::ref_ptr<osg::Node> model;
135 SGPropertyNode props;
137 // Load the 3D aircraft object itself
138 SGPath modelpath = path, texturepath = path;
139 if ( !ulIsAbsolutePathName( path.c_str() ) ) {
140 SGPath tmp = fg_root;
141 tmp.append(modelpath.str());
142 modelpath = texturepath = tmp;
145 // Check for an XML wrapper
146 if (modelpath.str().substr(modelpath.str().size() - 4, 4) == ".xml") {
147 readProperties(modelpath.str(), &props);
148 if (props.hasValue("/path")) {
149 modelpath = modelpath.dir();
150 modelpath.append(props.getStringValue("/path"));
151 if (props.hasValue("/texture-path")) {
152 texturepath = texturepath.dir();
153 texturepath.append(props.getStringValue("/texture-path"));
157 model = new osg::Switch;
161 osg::ref_ptr<osgDB::ReaderWriter::Options> options
162 = new osgDB::ReaderWriter::Options(*osgDB::Registry::instance()
165 // Assume that textures are in
166 // the same location as the XML file.
168 if (texturepath.extension() != "")
169 texturepath = texturepath.dir();
171 options->setDatabasePath(texturepath.str());
172 if (!externalTexturePath.str().empty())
173 options->getDatabasePathList().push_back(externalTexturePath.str());
175 model = osgDB::readNodeFile(modelpath.str(), options.get());
177 throw sg_io_exception("Failed to load 3D model",
178 sg_location(modelpath.str()));
181 // Set up the alignment node
182 osg::ref_ptr<osg::MatrixTransform> alignmainmodel = new osg::MatrixTransform;
183 alignmainmodel->addChild(model.get());
184 osg::Matrix res_matrix;
185 res_matrix.makeRotate(
186 props.getFloatValue("/offsets/pitch-deg", 0.0)*SG_DEGREES_TO_RADIANS,
188 props.getFloatValue("/offsets/roll-deg", 0.0)*SG_DEGREES_TO_RADIANS,
190 props.getFloatValue("/offsets/heading-deg", 0.0)*SG_DEGREES_TO_RADIANS,
194 tmat.makeTranslate(props.getFloatValue("/offsets/x-m", 0.0),
195 props.getFloatValue("/offsets/y-m", 0.0),
196 props.getFloatValue("/offsets/z-m", 0.0));
197 alignmainmodel->setMatrix(res_matrix*tmat);
200 vector<SGPropertyNode_ptr> model_nodes = props.getChildren("model");
201 for (unsigned i = 0; i < model_nodes.size(); i++) {
202 SGPropertyNode_ptr node = model_nodes[i];
203 osg::ref_ptr<osg::MatrixTransform> align = new osg::MatrixTransform;
204 res_matrix.makeIdentity();
205 res_matrix.makeRotate(
206 node->getDoubleValue("offsets/pitch-deg", 0.0)*SG_DEGREES_TO_RADIANS,
208 node->getDoubleValue("offsets/roll-deg", 0.0)*SG_DEGREES_TO_RADIANS,
210 node->getDoubleValue("offsets/heading-deg", 0.0)*SG_DEGREES_TO_RADIANS,
214 tmat.makeTranslate(node->getDoubleValue("offsets/x-m", 0),
215 node->getDoubleValue("offsets/y-m", 0),
216 node->getDoubleValue("offsets/z-m", 0));
217 align->setMatrix(res_matrix*tmat);
219 osg::ref_ptr<osg::Node> kid;
220 const char* submodel = node->getStringValue("path");
222 kid = sgLoad3DModel( fg_root, submodel, prop_root, sim_time_sec, load_panel );
224 } catch (const sg_throwable &t) {
225 SG_LOG(SG_INPUT, SG_ALERT, "Failed to load submodel: " << t.getFormattedMessage());
228 align->addChild(kid.get());
230 align->setName(node->getStringValue("name", ""));
232 SGPropertyNode *cond = node->getNode("condition", false);
234 osg::ref_ptr<osg::Switch> sw = new osg::Switch;
235 sw->setUpdateCallback(new SGSwitchUpdateCallback(sgReadCondition(prop_root, cond)));
236 alignmainmodel->addChild(sw.get());
237 sw->addChild(align.get());
238 sw->setName("submodel condition switch");
240 alignmainmodel->addChild(align.get());
246 vector<SGPropertyNode_ptr> panel_nodes = props.getChildren("panel");
247 for (unsigned i = 0; i < panel_nodes.size(); i++) {
248 SG_LOG(SG_INPUT, SG_DEBUG, "Loading a panel");
249 osg::ref_ptr<osg::Node> panel = load_panel(panel_nodes[i]);
250 if (panel_nodes[i]->hasValue("name"))
251 panel->setName((char *)panel_nodes[i]->getStringValue("name"));
252 alignmainmodel->addChild(panel.get());
256 std::vector<SGPropertyNode_ptr> particle_nodes;
257 particle_nodes = props.getChildren("particlesystem");
258 for (unsigned i = 0; i < particle_nodes.size(); ++i)
262 if (texturepath.extension() != "")
263 texturepath = texturepath.dir();
265 options->setDatabasePath(texturepath.str());
266 if (!externalTexturePath.str().empty())
267 options->getDatabasePathList().push_back(externalTexturePath.str());
269 alignmainmodel.get()->addChild(Particles::appendParticles(particle_nodes[i],
275 alignmainmodel->setUserData(data);
276 data->modelLoaded(path, &props, alignmainmodel.get());
279 std::vector<SGPropertyNode_ptr> animation_nodes;
280 animation_nodes = props.getChildren("animation");
281 for (unsigned i = 0; i < animation_nodes.size(); ++i)
282 /// OSGFIXME: duh, why not only model?????
283 SGAnimation::animate(alignmainmodel.get(), animation_nodes[i], prop_root,
286 if (props.hasChild("debug-outfile")) {
287 std::string outputfile = props.getStringValue("debug-outfile",
289 osgDB::writeNodeFile(*alignmainmodel, outputfile);
292 return alignmainmodel.release();