]> git.mxchange.org Git - simgear.git/blob - simgear/scene/model/model.cxx
Move SGReadFileCallback from model.cxx to public class ModelRegistry
[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(const std::string& path, bool wrapu, bool wrapv, int)
41 {
42   osg::Image* image = osgDB::readImageFile(path);
43   osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D;
44   texture->setImage(image);
45   texture->setDataVariance(osg::Object::STATIC);
46   if (wrapu)
47     texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
48   else
49     texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP);
50   if (wrapv)
51     texture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
52   else
53     texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP);
54
55   if (image) {
56     int s = image->s();
57     int t = image->t();
58
59     if (s <= t && 32 <= s) {
60       SGSceneFeatures::instance()->setTextureCompression(texture.get());
61     } else if (t < s && 32 <= t) {
62       SGSceneFeatures::instance()->setTextureCompression(texture.get());
63     }
64   }
65
66   // Make sure the texture is shared if we already have the same texture
67   // somewhere ...
68   {
69     osg::ref_ptr<osg::Node> tmpNode = new osg::Node;
70     osg::StateSet* stateSet = tmpNode->getOrCreateStateSet();
71     stateSet->setTextureAttribute(0, texture.get());
72
73     // OSGFIXME: don't forget that mutex here
74     osgDB::Registry* registry = osgDB::Registry::instance();
75     registry->getSharedStateManager()->share(tmpNode.get(), 0);
76
77     // should be the same, but be paranoid ...
78     stateSet = tmpNode->getStateSet();
79     osg::StateAttribute* stateAttr;
80     stateAttr = stateSet->getTextureAttribute(0, osg::StateAttribute::TEXTURE);
81     osg::Texture2D* texture2D = dynamic_cast<osg::Texture2D*>(stateAttr);
82     if (texture2D)
83       texture = texture2D;
84   }
85
86   return texture.release();
87 }
88
89 class SGSwitchUpdateCallback : public osg::NodeCallback {
90 public:
91   SGSwitchUpdateCallback(SGCondition* condition) :
92     mCondition(condition) {}
93   virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
94   { 
95     assert(dynamic_cast<osg::Switch*>(node));
96     osg::Switch* s = static_cast<osg::Switch*>(node);
97
98     if (mCondition && mCondition->test()) {
99       s->setAllChildrenOn();
100       // note, callback is responsible for scenegraph traversal so
101       // should always include call traverse(node,nv) to ensure 
102       // that the rest of cullbacks and the scene graph are traversed.
103       traverse(node, nv);
104     } else
105       s->setAllChildrenOff();
106   }
107
108 private:
109   SGSharedPtr<SGCondition> mCondition;
110 };
111
112 \f
113 ////////////////////////////////////////////////////////////////////////
114 // Global functions.
115 ////////////////////////////////////////////////////////////////////////
116
117 osg::Node *
118 sgLoad3DModel( const string &fg_root, const string &path,
119                SGPropertyNode *prop_root,
120                double sim_time_sec, osg::Node *(*load_panel)(SGPropertyNode *),
121                SGModelData *data,
122                const SGPath& externalTexturePath )
123 {
124   osg::ref_ptr<osg::Node> model;
125   SGPropertyNode props;
126
127   // Load the 3D aircraft object itself
128   SGPath modelpath = path, texturepath = path;
129   if ( !ulIsAbsolutePathName( path.c_str() ) ) {
130     SGPath tmp = fg_root;
131     tmp.append(modelpath.str());
132     modelpath = texturepath = tmp;
133   }
134
135   // Check for an XML wrapper
136   if (modelpath.str().substr(modelpath.str().size() - 4, 4) == ".xml") {
137     readProperties(modelpath.str(), &props);
138     if (props.hasValue("/path")) {
139       modelpath = modelpath.dir();
140       modelpath.append(props.getStringValue("/path"));
141       if (props.hasValue("/texture-path")) {
142         texturepath = texturepath.dir();
143         texturepath.append(props.getStringValue("/texture-path"));
144       }
145     } else {
146       if (!model)
147         model = new osg::Switch;
148     }
149   }
150
151   osgDB::FilePathList pathList = osgDB::getDataFilePathList();
152   osgDB::Registry::instance()->initFilePathLists();
153
154   // Assume that textures are in
155   // the same location as the XML file.
156   if (!model) {
157     if (texturepath.extension() != "")
158           texturepath = texturepath.dir();
159
160     osgDB::Registry::instance()->getDataFilePathList().push_front(texturepath.str());
161
162     model = osgDB::readNodeFile(modelpath.str());
163     if (model == 0)
164       throw sg_io_exception("Failed to load 3D model", 
165                             sg_location(modelpath.str()));
166   }
167
168   osgDB::Registry::instance()->getDataFilePathList().push_front(externalTexturePath.str());
169
170   // Set up the alignment node
171   osg::ref_ptr<osg::MatrixTransform> alignmainmodel = new osg::MatrixTransform;
172   alignmainmodel->addChild(model.get());
173   osg::Matrix res_matrix;
174   res_matrix.makeRotate(
175     props.getFloatValue("/offsets/pitch-deg", 0.0)*SG_DEGREES_TO_RADIANS,
176     osg::Vec3(0, 1, 0),
177     props.getFloatValue("/offsets/roll-deg", 0.0)*SG_DEGREES_TO_RADIANS,
178     osg::Vec3(1, 0, 0),
179     props.getFloatValue("/offsets/heading-deg", 0.0)*SG_DEGREES_TO_RADIANS,
180     osg::Vec3(0, 0, 1));
181
182   osg::Matrix tmat;
183   tmat.makeTranslate(props.getFloatValue("/offsets/x-m", 0.0),
184                      props.getFloatValue("/offsets/y-m", 0.0),
185                      props.getFloatValue("/offsets/z-m", 0.0));
186   alignmainmodel->setMatrix(res_matrix*tmat);
187
188   // Load sub-models
189   vector<SGPropertyNode_ptr> model_nodes = props.getChildren("model");
190   for (unsigned i = 0; i < model_nodes.size(); i++) {
191     SGPropertyNode_ptr node = model_nodes[i];
192     osg::ref_ptr<osg::MatrixTransform> align = new osg::MatrixTransform;
193     res_matrix.makeIdentity();
194     res_matrix.makeRotate(
195       node->getDoubleValue("offsets/pitch-deg", 0.0)*SG_DEGREES_TO_RADIANS,
196       osg::Vec3(0, 1, 0),
197       node->getDoubleValue("offsets/roll-deg", 0.0)*SG_DEGREES_TO_RADIANS,
198       osg::Vec3(1, 0, 0),
199       node->getDoubleValue("offsets/heading-deg", 0.0)*SG_DEGREES_TO_RADIANS,
200       osg::Vec3(0, 0, 1));
201     
202     tmat.makeIdentity();
203     tmat.makeTranslate(node->getDoubleValue("offsets/x-m", 0),
204                        node->getDoubleValue("offsets/y-m", 0),
205                        node->getDoubleValue("offsets/z-m", 0));
206     align->setMatrix(res_matrix*tmat);
207
208     osg::ref_ptr<osg::Node> kid;
209     const char* submodel = node->getStringValue("path");
210     try {
211       kid = sgLoad3DModel( fg_root, submodel, prop_root, sim_time_sec, load_panel );
212
213     } catch (const sg_throwable &t) {
214       SG_LOG(SG_INPUT, SG_ALERT, "Failed to load submodel: " << t.getFormattedMessage());
215       throw;
216     }
217     align->addChild(kid.get());
218
219     align->setName(node->getStringValue("name", ""));
220
221     SGPropertyNode *cond = node->getNode("condition", false);
222     if (cond) {
223       osg::ref_ptr<osg::Switch> sw = new osg::Switch;
224       sw->setUpdateCallback(new SGSwitchUpdateCallback(sgReadCondition(prop_root, cond)));
225       alignmainmodel->addChild(sw.get());
226       sw->addChild(align.get());
227       sw->setName("submodel condition switch");
228     } else {
229       alignmainmodel->addChild(align.get());
230     }
231   }
232
233   if ( load_panel ) {
234     // Load panels
235     vector<SGPropertyNode_ptr> panel_nodes = props.getChildren("panel");
236     for (unsigned i = 0; i < panel_nodes.size(); i++) {
237         SG_LOG(SG_INPUT, SG_DEBUG, "Loading a panel");
238         osg::ref_ptr<osg::Node> panel = load_panel(panel_nodes[i]);
239         if (panel_nodes[i]->hasValue("name"))
240             panel->setName((char *)panel_nodes[i]->getStringValue("name"));
241         alignmainmodel->addChild(panel.get());
242     }
243   }
244
245   if (data) {
246     alignmainmodel->setUserData(data);
247     data->modelLoaded(path, &props, alignmainmodel.get());
248   }
249
250   std::vector<SGPropertyNode_ptr> animation_nodes;
251   animation_nodes = props.getChildren("animation");
252   for (unsigned i = 0; i < animation_nodes.size(); ++i)
253     /// OSGFIXME: duh, why not only model?????
254     SGAnimation::animate(alignmainmodel.get(), animation_nodes[i], prop_root);
255
256   // restore old path list
257   osgDB::setDataFilePathList(pathList);
258
259   if (props.hasChild("debug-outfile")) {
260     std::string outputfile = props.getStringValue("debug-outfile",
261                                                   "debug-model.osg");
262     osgDB::writeNodeFile(*alignmainmodel, outputfile);
263   }
264
265   return alignmainmodel.release();
266 }
267
268 // end of model.cxx