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"
40 SGLoadTexture2D(bool staticTexture, const std::string& path,
41 const osgDB::ReaderWriter::Options* options,
42 bool wrapu, bool wrapv, int)
46 image = osgDB::readImageFile(path, options);
48 image = osgDB::readImageFile(path);
49 osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D;
50 texture->setImage(image);
52 texture->setDataVariance(osg::Object::STATIC);
54 texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
56 texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP);
58 texture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
60 texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP);
66 if (s <= t && 32 <= s) {
67 SGSceneFeatures::instance()->setTextureCompression(texture.get());
68 } else if (t < s && 32 <= t) {
69 SGSceneFeatures::instance()->setTextureCompression(texture.get());
73 // Make sure the texture is shared if we already have the same texture
76 osg::ref_ptr<osg::Node> tmpNode = new osg::Node;
77 osg::StateSet* stateSet = tmpNode->getOrCreateStateSet();
78 stateSet->setTextureAttribute(0, texture.get());
80 // OSGFIXME: don't forget that mutex here
81 osgDB::Registry* registry = osgDB::Registry::instance();
82 registry->getSharedStateManager()->share(tmpNode.get(), 0);
84 // should be the same, but be paranoid ...
85 stateSet = tmpNode->getStateSet();
86 osg::StateAttribute* stateAttr;
87 stateAttr = stateSet->getTextureAttribute(0, osg::StateAttribute::TEXTURE);
88 osg::Texture2D* texture2D = dynamic_cast<osg::Texture2D*>(stateAttr);
93 return texture.release();
96 class SGSwitchUpdateCallback : public osg::NodeCallback {
98 SGSwitchUpdateCallback(SGCondition* condition) :
99 mCondition(condition) {}
100 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
102 assert(dynamic_cast<osg::Switch*>(node));
103 osg::Switch* s = static_cast<osg::Switch*>(node);
105 if (mCondition && mCondition->test()) {
106 s->setAllChildrenOn();
107 // note, callback is responsible for scenegraph traversal so
108 // should always include call traverse(node,nv) to ensure
109 // that the rest of cullbacks and the scene graph are traversed.
112 s->setAllChildrenOff();
116 SGSharedPtr<SGCondition> mCondition;
120 ////////////////////////////////////////////////////////////////////////
122 ////////////////////////////////////////////////////////////////////////
125 sgLoad3DModel( const string &fg_root, const string &path,
126 SGPropertyNode *prop_root,
127 double sim_time_sec, osg::Node *(*load_panel)(SGPropertyNode *),
129 const SGPath& externalTexturePath )
131 osg::ref_ptr<osg::Node> model;
132 SGPropertyNode props;
134 // Load the 3D aircraft object itself
135 SGPath modelpath = path, texturepath = path;
136 if ( !ulIsAbsolutePathName( path.c_str() ) ) {
137 SGPath tmp = fg_root;
138 tmp.append(modelpath.str());
139 modelpath = texturepath = tmp;
142 // Check for an XML wrapper
143 if (modelpath.str().substr(modelpath.str().size() - 4, 4) == ".xml") {
144 readProperties(modelpath.str(), &props);
145 if (props.hasValue("/path")) {
146 modelpath = modelpath.dir();
147 modelpath.append(props.getStringValue("/path"));
148 if (props.hasValue("/texture-path")) {
149 texturepath = texturepath.dir();
150 texturepath.append(props.getStringValue("/texture-path"));
154 model = new osg::Switch;
158 osg::ref_ptr<osgDB::ReaderWriter::Options> options
159 = new osgDB::ReaderWriter::Options(*osgDB::Registry::instance()
162 // Assume that textures are in
163 // the same location as the XML file.
165 if (texturepath.extension() != "")
166 texturepath = texturepath.dir();
168 options->setDatabasePath(texturepath.str());
169 if (!externalTexturePath.str().empty())
170 options->getDatabasePathList().push_back(externalTexturePath.str());
172 model = osgDB::readNodeFile(modelpath.str(), options.get());
174 throw sg_io_exception("Failed to load 3D model",
175 sg_location(modelpath.str()));
178 // Set up the alignment node
179 osg::ref_ptr<osg::MatrixTransform> alignmainmodel = new osg::MatrixTransform;
180 alignmainmodel->addChild(model.get());
181 osg::Matrix res_matrix;
182 res_matrix.makeRotate(
183 props.getFloatValue("/offsets/pitch-deg", 0.0)*SG_DEGREES_TO_RADIANS,
185 props.getFloatValue("/offsets/roll-deg", 0.0)*SG_DEGREES_TO_RADIANS,
187 props.getFloatValue("/offsets/heading-deg", 0.0)*SG_DEGREES_TO_RADIANS,
191 tmat.makeTranslate(props.getFloatValue("/offsets/x-m", 0.0),
192 props.getFloatValue("/offsets/y-m", 0.0),
193 props.getFloatValue("/offsets/z-m", 0.0));
194 alignmainmodel->setMatrix(res_matrix*tmat);
197 vector<SGPropertyNode_ptr> model_nodes = props.getChildren("model");
198 for (unsigned i = 0; i < model_nodes.size(); i++) {
199 SGPropertyNode_ptr node = model_nodes[i];
200 osg::ref_ptr<osg::MatrixTransform> align = new osg::MatrixTransform;
201 res_matrix.makeIdentity();
202 res_matrix.makeRotate(
203 node->getDoubleValue("offsets/pitch-deg", 0.0)*SG_DEGREES_TO_RADIANS,
205 node->getDoubleValue("offsets/roll-deg", 0.0)*SG_DEGREES_TO_RADIANS,
207 node->getDoubleValue("offsets/heading-deg", 0.0)*SG_DEGREES_TO_RADIANS,
211 tmat.makeTranslate(node->getDoubleValue("offsets/x-m", 0),
212 node->getDoubleValue("offsets/y-m", 0),
213 node->getDoubleValue("offsets/z-m", 0));
214 align->setMatrix(res_matrix*tmat);
216 osg::ref_ptr<osg::Node> kid;
217 const char* submodel = node->getStringValue("path");
219 kid = sgLoad3DModel( fg_root, submodel, prop_root, sim_time_sec, load_panel );
221 } catch (const sg_throwable &t) {
222 SG_LOG(SG_INPUT, SG_ALERT, "Failed to load submodel: " << t.getFormattedMessage());
225 align->addChild(kid.get());
227 align->setName(node->getStringValue("name", ""));
229 SGPropertyNode *cond = node->getNode("condition", false);
231 osg::ref_ptr<osg::Switch> sw = new osg::Switch;
232 sw->setUpdateCallback(new SGSwitchUpdateCallback(sgReadCondition(prop_root, cond)));
233 alignmainmodel->addChild(sw.get());
234 sw->addChild(align.get());
235 sw->setName("submodel condition switch");
237 alignmainmodel->addChild(align.get());
243 vector<SGPropertyNode_ptr> panel_nodes = props.getChildren("panel");
244 for (unsigned i = 0; i < panel_nodes.size(); i++) {
245 SG_LOG(SG_INPUT, SG_DEBUG, "Loading a panel");
246 osg::ref_ptr<osg::Node> panel = load_panel(panel_nodes[i]);
247 if (panel_nodes[i]->hasValue("name"))
248 panel->setName((char *)panel_nodes[i]->getStringValue("name"));
249 alignmainmodel->addChild(panel.get());
254 alignmainmodel->setUserData(data);
255 data->modelLoaded(path, &props, alignmainmodel.get());
258 std::vector<SGPropertyNode_ptr> animation_nodes;
259 animation_nodes = props.getChildren("animation");
260 for (unsigned i = 0; i < animation_nodes.size(); ++i)
261 /// OSGFIXME: duh, why not only model?????
262 SGAnimation::animate(alignmainmodel.get(), animation_nodes[i], prop_root,
265 if (props.hasChild("debug-outfile")) {
266 std::string outputfile = props.getStringValue("debug-outfile",
268 osgDB::writeNodeFile(*alignmainmodel, outputfile);
271 return alignmainmodel.release();