]> git.mxchange.org Git - simgear.git/blob - simgear/scene/model/model.cxx
becc88c6d824cd128eeb64eb21ecf61feae617dc
[simgear.git] / simgear / scene / model / model.cxx
1 // model.cxx - manage a 3D aircraft model.
2 // Written by David Megginson, started 2002.
3 //
4 // This file is in the Public Domain, and comes with no warranty.
5
6 #ifdef HAVE_CONFIG_H
7 #include <simgear_config.h>
8 #endif
9
10 #include <osg/observer_ptr>
11 #include <osg/ref_ptr>
12 #include <osg/Group>
13 #include <osg/NodeCallback>
14 #include <osg/Switch>
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>
24
25 #include <simgear/scene/util/SGSceneFeatures.hxx>
26 #include <simgear/scene/util/SGStateAttributeVisitor.hxx>
27 #include <simgear/scene/util/SGTextureStateAttributeVisitor.hxx>
28
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>
33
34 #include "animation.hxx"
35 #include "model.hxx"
36
37 SG_USING_STD(vector);
38
39 osg::Texture2D*
40 SGLoadTexture2D(bool staticTexture, const std::string& path,
41                 const osgDB::ReaderWriter::Options* options,
42                 bool wrapu, bool wrapv, int)
43 {
44   osg::Image* image;
45   if (options)
46     image = osgDB::readImageFile(path, options);
47   else
48     image = osgDB::readImageFile(path);
49   osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D;
50   texture->setImage(image);
51   if (staticTexture)
52     texture->setDataVariance(osg::Object::STATIC);
53   if (wrapu)
54     texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
55   else
56     texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP);
57   if (wrapv)
58     texture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
59   else
60     texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP);
61
62   if (image) {
63     int s = image->s();
64     int t = image->t();
65
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());
70     }
71   }
72
73   // Make sure the texture is shared if we already have the same texture
74   // somewhere ...
75   {
76     osg::ref_ptr<osg::Node> tmpNode = new osg::Node;
77     osg::StateSet* stateSet = tmpNode->getOrCreateStateSet();
78     stateSet->setTextureAttribute(0, texture.get());
79
80     // OSGFIXME: don't forget that mutex here
81     osgDB::Registry* registry = osgDB::Registry::instance();
82     registry->getSharedStateManager()->share(tmpNode.get(), 0);
83
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);
89     if (texture2D)
90       texture = texture2D;
91   }
92
93   return texture.release();
94 }
95
96 class SGSwitchUpdateCallback : public osg::NodeCallback {
97 public:
98   SGSwitchUpdateCallback(SGCondition* condition) :
99     mCondition(condition) {}
100   virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
101   { 
102     assert(dynamic_cast<osg::Switch*>(node));
103     osg::Switch* s = static_cast<osg::Switch*>(node);
104
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.
110       traverse(node, nv);
111     } else
112       s->setAllChildrenOff();
113   }
114
115 private:
116   SGSharedPtr<SGCondition> mCondition;
117 };
118
119 \f
120 ////////////////////////////////////////////////////////////////////////
121 // Global functions.
122 ////////////////////////////////////////////////////////////////////////
123
124 osg::Node *
125 sgLoad3DModel( const string &fg_root, const string &path,
126                SGPropertyNode *prop_root,
127                double sim_time_sec, osg::Node *(*load_panel)(SGPropertyNode *),
128                SGModelData *data,
129                const SGPath& externalTexturePath )
130 {
131   osg::ref_ptr<osg::Node> model;
132   SGPropertyNode props;
133
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;
140   }
141
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"));
151       }
152     } else {
153       if (!model)
154         model = new osg::Switch;
155     }
156   }
157
158   osg::ref_ptr<osgDB::ReaderWriter::Options> options
159       = new osgDB::ReaderWriter::Options(*osgDB::Registry::instance()
160                                          ->getOptions());
161
162   // Assume that textures are in
163   // the same location as the XML file.
164   if (!model) {
165       if (texturepath.extension() != "")
166           texturepath = texturepath.dir();
167
168       options->setDatabasePath(texturepath.str());
169       if (!externalTexturePath.str().empty())
170           options->getDatabasePathList().push_back(externalTexturePath.str());
171
172       model = osgDB::readNodeFile(modelpath.str(), options.get());
173       if (model == 0)
174           throw sg_io_exception("Failed to load 3D model", 
175                                 sg_location(modelpath.str()));
176   }
177
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,
184     osg::Vec3(0, 1, 0),
185     props.getFloatValue("/offsets/roll-deg", 0.0)*SG_DEGREES_TO_RADIANS,
186     osg::Vec3(1, 0, 0),
187     props.getFloatValue("/offsets/heading-deg", 0.0)*SG_DEGREES_TO_RADIANS,
188     osg::Vec3(0, 0, 1));
189
190   osg::Matrix tmat;
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);
195
196   // Load sub-models
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,
204       osg::Vec3(0, 1, 0),
205       node->getDoubleValue("offsets/roll-deg", 0.0)*SG_DEGREES_TO_RADIANS,
206       osg::Vec3(1, 0, 0),
207       node->getDoubleValue("offsets/heading-deg", 0.0)*SG_DEGREES_TO_RADIANS,
208       osg::Vec3(0, 0, 1));
209     
210     tmat.makeIdentity();
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);
215
216     osg::ref_ptr<osg::Node> kid;
217     const char* submodel = node->getStringValue("path");
218     try {
219       kid = sgLoad3DModel( fg_root, submodel, prop_root, sim_time_sec, load_panel );
220
221     } catch (const sg_throwable &t) {
222       SG_LOG(SG_INPUT, SG_ALERT, "Failed to load submodel: " << t.getFormattedMessage());
223       throw;
224     }
225     align->addChild(kid.get());
226
227     align->setName(node->getStringValue("name", ""));
228
229     SGPropertyNode *cond = node->getNode("condition", false);
230     if (cond) {
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");
236     } else {
237       alignmainmodel->addChild(align.get());
238     }
239   }
240
241   if ( load_panel ) {
242     // Load panels
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());
250     }
251   }
252
253   if (data) {
254     alignmainmodel->setUserData(data);
255     data->modelLoaded(path, &props, alignmainmodel.get());
256   }
257
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,
263                          options.get());
264
265   if (props.hasChild("debug-outfile")) {
266     std::string outputfile = props.getStringValue("debug-outfile",
267                                                   "debug-model.osg");
268     osgDB::writeNodeFile(*alignmainmodel, outputfile);
269   }
270
271   return alignmainmodel.release();
272 }
273
274 // end of model.cxx