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(const std::string& path, bool wrapu, bool wrapv, int)
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);
47 texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
49 texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP);
51 texture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
53 texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP);
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());
66 // Make sure the texture is shared if we already have the same texture
69 osg::ref_ptr<osg::Node> tmpNode = new osg::Node;
70 osg::StateSet* stateSet = tmpNode->getOrCreateStateSet();
71 stateSet->setTextureAttribute(0, texture.get());
73 // OSGFIXME: don't forget that mutex here
74 osgDB::Registry* registry = osgDB::Registry::instance();
75 registry->getSharedStateManager()->share(tmpNode.get(), 0);
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);
86 return texture.release();
89 class SGSwitchUpdateCallback : public osg::NodeCallback {
91 SGSwitchUpdateCallback(SGCondition* condition) :
92 mCondition(condition) {}
93 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
95 assert(dynamic_cast<osg::Switch*>(node));
96 osg::Switch* s = static_cast<osg::Switch*>(node);
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.
105 s->setAllChildrenOff();
109 SGSharedPtr<SGCondition> mCondition;
113 ////////////////////////////////////////////////////////////////////////
115 ////////////////////////////////////////////////////////////////////////
118 sgLoad3DModel( const string &fg_root, const string &path,
119 SGPropertyNode *prop_root,
120 double sim_time_sec, osg::Node *(*load_panel)(SGPropertyNode *),
122 const SGPath& externalTexturePath )
124 osg::ref_ptr<osg::Node> model;
125 SGPropertyNode props;
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;
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"));
147 model = new osg::Switch;
151 osgDB::FilePathList pathList = osgDB::getDataFilePathList();
152 osgDB::Registry::instance()->initFilePathLists();
154 // Assume that textures are in
155 // the same location as the XML file.
157 if (texturepath.extension() != "")
158 texturepath = texturepath.dir();
160 osgDB::Registry::instance()->getDataFilePathList().push_front(texturepath.str());
162 model = osgDB::readNodeFile(modelpath.str());
164 throw sg_io_exception("Failed to load 3D model",
165 sg_location(modelpath.str()));
168 osgDB::Registry::instance()->getDataFilePathList().push_front(externalTexturePath.str());
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,
177 props.getFloatValue("/offsets/roll-deg", 0.0)*SG_DEGREES_TO_RADIANS,
179 props.getFloatValue("/offsets/heading-deg", 0.0)*SG_DEGREES_TO_RADIANS,
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);
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,
197 node->getDoubleValue("offsets/roll-deg", 0.0)*SG_DEGREES_TO_RADIANS,
199 node->getDoubleValue("offsets/heading-deg", 0.0)*SG_DEGREES_TO_RADIANS,
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);
208 osg::ref_ptr<osg::Node> kid;
209 const char* submodel = node->getStringValue("path");
211 kid = sgLoad3DModel( fg_root, submodel, prop_root, sim_time_sec, load_panel );
213 } catch (const sg_throwable &t) {
214 SG_LOG(SG_INPUT, SG_ALERT, "Failed to load submodel: " << t.getFormattedMessage());
217 align->addChild(kid.get());
219 align->setName(node->getStringValue("name", ""));
221 SGPropertyNode *cond = node->getNode("condition", false);
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");
229 alignmainmodel->addChild(align.get());
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());
246 alignmainmodel->setUserData(data);
247 data->modelLoaded(path, &props, alignmainmodel.get());
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);
256 // restore old path list
257 osgDB::setDataFilePathList(pathList);
259 if (props.hasChild("debug-outfile")) {
260 std::string outputfile = props.getStringValue("debug-outfile",
262 osgDB::writeNodeFile(*alignmainmodel, outputfile);
265 return alignmainmodel.release();