]> git.mxchange.org Git - simgear.git/blob - simgear/scene/model/SGReaderWriterXML.cxx
Provide something more sensible for the properties root
[simgear.git] / simgear / scene / model / SGReaderWriterXML.cxx
1 // Copyright (C) 2007 Tim Moore timoore@redhat.com
2 // Copyright (C) 2008 Till Busch buti@bux.at
3 //
4 // This program is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License as
6 // published by the Free Software Foundation; either version 2 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful, but
10 // WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 // General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 //
18
19 #ifdef HAVE_CONFIG_H
20 #  include <simgear_config.h>
21 #endif
22
23 #include <osg/MatrixTransform>
24 #include <osgDB/WriteFile>
25 #include <osgDB/Registry>
26 #include <osg/Switch>
27 #include <osgDB/FileNameUtils>
28
29 #include <simgear/compiler.h>
30 #include <simgear/structure/exception.hxx>
31 #include <simgear/props/props.hxx>
32 #include <simgear/props/props_io.hxx>
33 #include <simgear/props/condition.hxx>
34 #include <simgear/scene/util/SGNodeMasks.hxx>
35
36 #include "modellib.hxx"
37 #include "SGPagedLOD.hxx"
38 #include "SGReaderWriterXML.hxx"
39 #include "SGReaderWriterXMLOptions.hxx"
40
41 #include "animation.hxx"
42 #include "particles.hxx"
43 #include "model.hxx"
44
45 #include "SGReaderWriterXMLOptions.hxx"
46 #include "SGReaderWriterXML.hxx"
47
48 using namespace simgear;
49
50 static osg::Node *
51 sgLoad3DModel_internal(const string &path,
52                        SGPropertyNode *prop_root,
53                        SGModelData *data = 0,
54                        osg::Node *(*load_panel)(SGPropertyNode *) = 0,
55                        SGPropertyNode *overlay = 0);
56
57
58 SGReaderWriterXML::SGReaderWriterXML()
59 {
60     supportsExtension("xml", "SimGear xml database format");
61 }
62
63 SGReaderWriterXML::~SGReaderWriterXML()
64 {
65 }
66
67 const char* SGReaderWriterXML::className() const
68 {
69     return "XML database reader";
70 }
71
72 osgDB::ReaderWriter::ReadResult
73 SGReaderWriterXML::readNode(const std::string& fileName,
74                             const osgDB::ReaderWriter::Options* options) const
75 {
76     // SG_LOG(SG_GENERAL, SG_ALERT, "SGReaderWriterXML::readNode(" << fileName << ")");
77
78     const SGReaderWriterXMLOptions* xmlOptions
79     = dynamic_cast<const SGReaderWriterXMLOptions*>(options);
80
81     SGSharedPtr<SGPropertyNode> prop_root;
82     osg::Node *(*load_panel)(SGPropertyNode *)=0;
83     SGModelData *model_data=0;
84
85     if (xmlOptions) {
86         prop_root = xmlOptions->getPropRoot();
87         load_panel = xmlOptions->getLoadPanel();
88         model_data = xmlOptions->getModelData();
89     }
90     if (!prop_root) {
91         prop_root = new SGPropertyNode;
92     }
93
94     osg::Node *result=0;
95
96     try {
97         result=sgLoad3DModel_internal(fileName, prop_root, model_data, load_panel);
98     } catch (const sg_throwable &t) {
99         SG_LOG(SG_INPUT, SG_ALERT, "Failed to load model: " << t.getFormattedMessage());
100         result=new osg::Node;
101     }
102     if (result)
103         return result;
104     else
105         return ReadResult::FILE_NOT_HANDLED;
106 }
107
108 class SGSwitchUpdateCallback : public osg::NodeCallback
109 {
110 public:
111     SGSwitchUpdateCallback(SGCondition* condition) :
112             mCondition(condition) {}
113     virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) {
114         assert(dynamic_cast<osg::Switch*>(node));
115         osg::Switch* s = static_cast<osg::Switch*>(node);
116
117         if (mCondition && mCondition->test()) {
118             s->setAllChildrenOn();
119             // note, callback is responsible for scenegraph traversal so
120             // should always include call traverse(node,nv) to ensure
121             // that the rest of cullbacks and the scene graph are traversed.
122             traverse(node, nv);
123         } else
124             s->setAllChildrenOff();
125     }
126
127 private:
128     SGSharedPtr<SGCondition> mCondition;
129 };
130
131 static osg::Node *
132 sgLoad3DModel_internal(const string &path,
133                        SGPropertyNode *prop_root,
134                        SGModelData *data,
135                        osg::Node *(*load_panel)(SGPropertyNode *),
136                        SGPropertyNode *overlay)
137 {
138     if ( !prop_root ) {
139         SG_LOG(SG_GENERAL, SG_ALERT, "prop_root NULL: " << path);
140     }
141
142     osgDB::FilePathList filePathList;
143     filePathList = osgDB::Registry::instance()->getDataFilePathList();
144     filePathList.push_front(osgDB::convertFileNameToNativeStyle("/"));
145
146     SGPath modelpath = osgDB::findFileInPath(path, filePathList);
147     if (modelpath.str().empty()) {
148         SG_LOG(SG_INPUT, SG_ALERT, "Failed to load file: \"" << path << "\"");
149         return 0;
150     }
151     SGPath texturepath = modelpath;
152
153     osg::ref_ptr<osg::Node> model;
154     osg::ref_ptr<osg::Group> group;
155     SGPropertyNode_ptr props = new SGPropertyNode;
156
157     // Check for an XML wrapper
158     if (modelpath.extension() == "xml") {
159        try {
160             readProperties(modelpath.str(), props);
161         } catch (const sg_throwable &t) {
162             SG_LOG(SG_INPUT, SG_ALERT, "Failed to load xml: "
163                    << t.getFormattedMessage());
164             throw;
165         }
166         if (overlay)
167             copyProperties(overlay, props);
168
169         if (props->hasValue("/path")) {
170             modelpath = modelpath.dir();
171             modelpath.append(props->getStringValue("/path"));
172             if (props->hasValue("/texture-path")) {
173                 texturepath = texturepath.dir();
174                 texturepath.append(props->getStringValue("/texture-path"));
175             }
176         } else {
177             model = new osg::Node;
178         }
179
180         SGPropertyNode *mp = props->getNode("multiplay");
181         if (mp && prop_root && prop_root->getParent())
182             copyProperties(mp, prop_root);
183     }
184
185     osg::ref_ptr<SGReaderWriterXMLOptions> options
186     = new SGReaderWriterXMLOptions(*osgDB::Registry::instance()->getOptions());
187
188     // Assume that textures are in
189     // the same location as the XML file.
190     if (!model) {
191         if (!texturepath.extension().empty())
192             texturepath = texturepath.dir();
193
194         options->setDatabasePath(texturepath.str());
195         model = osgDB::readNodeFile(modelpath.str(), options.get());
196         if (model == 0)
197             throw sg_io_exception("Failed to load 3D model",
198                                   sg_location(modelpath.str()));
199     }
200     model->setName(modelpath.str());
201
202     bool needTransform=false;
203     // Set up the alignment node if needed
204     SGPropertyNode *offsets = props->getNode("offsets", false);
205     if (offsets) {
206         needTransform=true;
207         osg::MatrixTransform *alignmainmodel = new osg::MatrixTransform;
208         osg::Matrix res_matrix;
209         res_matrix.makeRotate(
210             offsets->getFloatValue("pitch-deg", 0.0)*SG_DEGREES_TO_RADIANS,
211             osg::Vec3(0, 1, 0),
212             offsets->getFloatValue("roll-deg", 0.0)*SG_DEGREES_TO_RADIANS,
213             osg::Vec3(1, 0, 0),
214             offsets->getFloatValue("heading-deg", 0.0)*SG_DEGREES_TO_RADIANS,
215             osg::Vec3(0, 0, 1));
216
217         osg::Matrix tmat;
218         tmat.makeTranslate(offsets->getFloatValue("x-m", 0.0),
219                            offsets->getFloatValue("y-m", 0.0),
220                            offsets->getFloatValue("z-m", 0.0));
221         alignmainmodel->setMatrix(res_matrix*tmat);
222         group = alignmainmodel;
223     }
224     if (!group) {
225         group = new osg::Group;
226     }
227     group->addChild(model.get());
228
229     // Load sub-models
230     vector<SGPropertyNode_ptr> model_nodes = props->getChildren("model");
231     for (unsigned i = 0; i < model_nodes.size(); i++) {
232         SGPropertyNode_ptr sub_props = model_nodes[i];
233
234         SGPath submodelpath;
235         osg::ref_ptr<osg::Node> submodel;
236         string submodelFileName = sub_props->getStringValue("path");
237         if ( submodelFileName.size() > 2 && submodelFileName.substr( 0, 2 ) == "./" ) {
238             submodelpath = modelpath.dir();
239             submodelpath.append( submodelFileName.substr( 2 ) );
240         } else {
241             submodelpath = submodelFileName;
242         }
243         try {
244             submodel = sgLoad3DModel_internal(submodelpath.str(), prop_root, 0, load_panel,
245                                               sub_props->getNode("overlay"));
246         } catch (const sg_throwable &t) {
247             SG_LOG(SG_INPUT, SG_ALERT, "Failed to load submodel: " << t.getFormattedMessage());
248             throw;
249         }
250
251         osg::ref_ptr<osg::Node> submodel_final=submodel.get();
252         SGPropertyNode *offs = sub_props->getNode("offsets", false);
253         if (offs) {
254             osg::Matrix res_matrix;
255             osg::ref_ptr<osg::MatrixTransform> align = new osg::MatrixTransform;
256             res_matrix.makeIdentity();
257             res_matrix.makeRotate(
258                 offs->getDoubleValue("pitch-deg", 0.0)*SG_DEGREES_TO_RADIANS,
259                 osg::Vec3(0, 1, 0),
260                 offs->getDoubleValue("roll-deg", 0.0)*SG_DEGREES_TO_RADIANS,
261                 osg::Vec3(1, 0, 0),
262                 offs->getDoubleValue("heading-deg", 0.0)*SG_DEGREES_TO_RADIANS,
263                 osg::Vec3(0, 0, 1));
264
265             osg::Matrix tmat;
266             tmat.makeIdentity();
267             tmat.makeTranslate(offs->getDoubleValue("x-m", 0),
268                                offs->getDoubleValue("y-m", 0),
269                                offs->getDoubleValue("z-m", 0));
270             align->setMatrix(res_matrix*tmat);
271             align->addChild(submodel.get());
272             submodel_final=align.get();
273         }
274         submodel_final->setName(sub_props->getStringValue("name", ""));
275
276         SGPropertyNode *cond = sub_props->getNode("condition", false);
277         if (cond) {
278             osg::ref_ptr<osg::Switch> sw = new osg::Switch;
279             sw->setUpdateCallback(new SGSwitchUpdateCallback(sgReadCondition(prop_root, cond)));
280             group->addChild(sw.get());
281             sw->addChild(submodel_final.get());
282             sw->setName("submodel condition switch");
283         } else {
284             group->addChild(submodel_final.get());
285         }
286     } // end of submodel loading
287
288     if ( load_panel ) {
289         // Load panels
290         vector<SGPropertyNode_ptr> panel_nodes = props->getChildren("panel");
291         for (unsigned i = 0; i < panel_nodes.size(); i++) {
292             SG_LOG(SG_INPUT, SG_DEBUG, "Loading a panel");
293             osg::ref_ptr<osg::Node> panel = load_panel(panel_nodes[i]);
294             if (panel_nodes[i]->hasValue("name"))
295                 panel->setName((char *)panel_nodes[i]->getStringValue("name"));
296             group->addChild(panel.get());
297         }
298     }
299
300     std::vector<SGPropertyNode_ptr> particle_nodes;
301     particle_nodes = props->getChildren("particlesystem");
302     for (unsigned i = 0; i < particle_nodes.size(); ++i) {
303         if (i==0) {
304             if (!texturepath.extension().empty())
305                 texturepath = texturepath.dir();
306
307             options->setDatabasePath(texturepath.str());
308         }
309         group->addChild(Particles::appendParticles(particle_nodes[i],
310                         prop_root,
311                         options.get()));
312     }
313
314     if (data) {
315         options->setModelData(data);
316     }
317
318     std::vector<SGPropertyNode_ptr> animation_nodes;
319     animation_nodes = props->getChildren("animation");
320     for (unsigned i = 0; i < animation_nodes.size(); ++i)
321         /// OSGFIXME: duh, why not only model?????
322         SGAnimation::animate(group.get(), animation_nodes[i], prop_root,
323                              options.get());
324
325     if (!needTransform && group->getNumChildren() < 2) {
326         model = group->getChild(0);
327         group->removeChild(model.get());
328         model->setUserData(group->getUserData());
329         return model.release();
330     }
331     if (props->hasChild("debug-outfile")) {
332         std::string outputfile = props->getStringValue("debug-outfile",
333                                  "debug-model.osg");
334         osgDB::writeNodeFile(*group, outputfile);
335     }
336
337     return group.release();
338 }
339