]> git.mxchange.org Git - simgear.git/blob - simgear/scene/model/model.cxx
Work around apparent OSG 3.2.0 normal binding bug.
[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 <utility>
11
12 #include <boost/foreach.hpp>
13
14 #include <osg/ref_ptr>
15 #include <osgDB/FileNameUtils>
16 #include <osgDB/FileUtils>
17 #include <osgDB/ReaderWriter>
18 #include <osgDB/ReadFile>
19 #include <osgDB/SharedStateManager>
20
21 #include <simgear/scene/material/Effect.hxx>
22 #include <simgear/scene/material/EffectGeode.hxx>
23 #include <simgear/scene/util/SGSceneFeatures.hxx>
24 #include <simgear/scene/util/SGSceneUserData.hxx>
25 #include <simgear/scene/util/CopyOp.hxx>
26 #include <simgear/scene/util/SplicingVisitor.hxx>
27 #include <simgear/scene/util/SGReaderWriterOptions.hxx>
28
29 #include <simgear/structure/exception.hxx>
30 #include <simgear/structure/Singleton.hxx>
31 #include <simgear/props/props.hxx>
32 #include <simgear/props/props_io.hxx>
33 #include <simgear/props/condition.hxx>
34
35 #include "model.hxx"
36
37 using std::vector;
38
39 osg::Texture2D*
40 SGLoadTexture2D(bool staticTexture, const std::string& path,
41                 const osgDB::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   return texture.release();
74 }
75
76 namespace simgear
77 {
78 using namespace std;
79 using namespace osg;
80 using simgear::CopyOp;
81
82 Node* copyModel(Node* model)
83 {
84     const CopyOp::CopyFlags flags = (CopyOp::DEEP_COPY_ALL
85                                      & ~CopyOp::DEEP_COPY_TEXTURES
86                                      & ~CopyOp::DEEP_COPY_IMAGES
87                                      & ~CopyOp::DEEP_COPY_STATESETS
88                                      & ~CopyOp::DEEP_COPY_STATEATTRIBUTES
89                                      & ~CopyOp::DEEP_COPY_ARRAYS
90                                      & ~CopyOp::DEEP_COPY_PRIMITIVES
91                                      // This will preserve display lists ...
92                                      & ~CopyOp::DEEP_COPY_DRAWABLES
93                                      & ~CopyOp::DEEP_COPY_SHAPES);
94     return (CopyOp(flags))(model);
95 }
96
97 TextureUpdateVisitor::TextureUpdateVisitor(const osgDB::FilePathList& pathList) :
98     NodeAndDrawableVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN),
99     _pathList(pathList)
100 {
101 }
102
103 void TextureUpdateVisitor::apply(Node& node)
104 {
105     StateSet* stateSet = cloneStateSet(node.getStateSet());
106     if (stateSet)
107         node.setStateSet(stateSet);
108     traverse(node);
109 }
110
111 void TextureUpdateVisitor::apply(Drawable& drawable)
112 {
113     StateSet* stateSet = cloneStateSet(drawable.getStateSet());
114     if (stateSet)
115         drawable.setStateSet(stateSet);
116 }
117
118 Texture2D* TextureUpdateVisitor::textureReplace(int unit, const StateAttribute* attr)
119 {
120     using namespace osgDB;
121     const Texture2D* texture = dynamic_cast<const Texture2D*>(attr);
122
123     if (!texture)
124         return 0;
125
126     const Image* image = texture->getImage();
127     const string* fullFilePath = 0;
128     if (image) {
129         // The currently loaded file name
130         fullFilePath = &image->getFileName();
131
132     } else {
133         fullFilePath = &texture->getName();
134     }
135     // The short name
136     string fileName = getSimpleFileName(*fullFilePath);
137     if (fileName.empty())
138         return 0;
139     // The name that should be found with the current database path
140     string fullLiveryFile = findFileInPath(fileName, _pathList);
141     // If it is empty or they are identical then there is nothing to do
142     if (fullLiveryFile.empty() || fullLiveryFile == *fullFilePath)
143         return 0;
144     Image* newImage = readImageFile(fullLiveryFile);
145     if (!newImage)
146         return 0;
147     CopyOp copyOp(CopyOp::DEEP_COPY_ALL & ~CopyOp::DEEP_COPY_IMAGES);
148     Texture2D* newTexture = static_cast<Texture2D*>(copyOp(texture));
149     if (!newTexture) {
150         return 0;
151     } else {
152         newTexture->setImage(newImage);
153         return newTexture;
154     }
155 }
156
157 StateSet* TextureUpdateVisitor::cloneStateSet(const StateSet* stateSet)
158 {
159     typedef std::pair<int, Texture2D*> Tex2D;
160     vector<Tex2D> newTextures;
161     StateSet* result = 0;
162
163     if (!stateSet)
164         return 0;
165     int numUnits = stateSet->getTextureAttributeList().size();
166     if (numUnits > 0) {
167         for (int i = 0; i < numUnits; ++i) {
168             const StateAttribute* attr
169                 = stateSet->getTextureAttribute(i, StateAttribute::TEXTURE);
170             Texture2D* newTexture = textureReplace(i, attr);
171             if (newTexture)
172                 newTextures.push_back(Tex2D(i, newTexture));
173         }
174         if (!newTextures.empty()) {
175             result = static_cast<StateSet*>(stateSet->clone(CopyOp()));
176             for (vector<Tex2D>::iterator i = newTextures.begin();
177                  i != newTextures.end();
178                  ++i) {
179                 result->setTextureAttribute(i->first, i->second);
180             }
181         }
182     }
183     return result;
184 }
185
186 UserDataCopyVisitor::UserDataCopyVisitor() :
187     NodeVisitor(NodeVisitor::NODE_VISITOR,
188                 NodeVisitor::TRAVERSE_ALL_CHILDREN)
189 {
190 }
191
192 void UserDataCopyVisitor::apply(Node& node)
193 {
194     ref_ptr<SGSceneUserData> userData;
195     userData = SGSceneUserData::getSceneUserData(&node);
196     if (userData.valid()) {
197         SGSceneUserData* newUserData  = new SGSceneUserData(*userData);
198         newUserData->setVelocity(0);
199         node.setUserData(newUserData);
200     }
201     node.traverse(*this);
202 }
203
204 namespace
205 {
206 class MakeEffectVisitor : public SplicingVisitor
207 {
208 public:
209     typedef std::map<string, SGPropertyNode_ptr> EffectMap;
210     using SplicingVisitor::apply;
211     MakeEffectVisitor(const SGReaderWriterOptions* options = 0)
212         : _options(options)
213     {
214     }
215     virtual void apply(osg::Group& node);
216     virtual void apply(osg::Geode& geode);
217     EffectMap& getEffectMap() { return _effectMap; }
218     const EffectMap& getEffectMap() const { return _effectMap; }
219     void setDefaultEffect(SGPropertyNode* effect)
220     {
221         _currentEffectParent = effect;
222     }
223     SGPropertyNode* getDefaultEffect() { return _currentEffectParent; }
224 protected:
225     EffectMap _effectMap;
226     SGPropertyNode_ptr _currentEffectParent;
227     osg::ref_ptr<const SGReaderWriterOptions> _options;
228 };
229
230 void MakeEffectVisitor::apply(osg::Group& node)
231 {
232     SGPropertyNode_ptr savedEffectRoot;
233     const string& nodeName = node.getName();
234     bool restoreEffect = false;
235     if (!nodeName.empty()) {
236         EffectMap::iterator eitr = _effectMap.find(nodeName);
237         if (eitr != _effectMap.end()) {
238             savedEffectRoot = _currentEffectParent;
239             _currentEffectParent = eitr->second;
240             restoreEffect = true;
241         }
242     }
243     SplicingVisitor::apply(node);
244     // If a new node was created, copy the user data too.
245     ref_ptr<SGSceneUserData> userData = SGSceneUserData::getSceneUserData(&node);
246     if (userData.valid() && _childStack.back().back().get() != &node)
247         _childStack.back().back()->setUserData(new SGSceneUserData(*userData));
248     if (restoreEffect)
249         _currentEffectParent = savedEffectRoot;
250 }
251
252 void MakeEffectVisitor::apply(osg::Geode& geode)
253 {
254     if (pushNode(getNewNode(geode)))
255         return;
256     osg::StateSet* ss = geode.getStateSet();
257     if (!ss) {
258         pushNode(&geode);
259         return;
260     }
261     SGPropertyNode_ptr ssRoot = new SGPropertyNode;
262     makeParametersFromStateSet(ssRoot, ss);
263     SGPropertyNode_ptr effectRoot = new SGPropertyNode;
264     effect::mergePropertyTrees(effectRoot, ssRoot, _currentEffectParent);
265     Effect* effect = makeEffect(effectRoot, true, _options.get());
266     EffectGeode* eg = dynamic_cast<EffectGeode*>(&geode);
267     if (eg) {
268         eg->setEffect(effect);
269     } else {
270         eg = new EffectGeode;
271         eg->setEffect(effect);
272         ref_ptr<SGSceneUserData> userData = SGSceneUserData::getSceneUserData(&geode);
273         if (userData.valid())
274             eg->setUserData(new SGSceneUserData(*userData));
275         for (unsigned i = 0; i < geode.getNumDrawables(); ++i) {
276             osg::Drawable *drawable = geode.getDrawable(i);
277             eg->addDrawable(drawable);
278
279             // Generate tangent vectors etc if needed
280             osg::Geometry *geom = dynamic_cast<osg::Geometry*>(drawable);
281             if(geom) eg->runGenerators(geom);
282         }
283     }
284     pushResultNode(&geode, eg);
285
286 }
287
288 }
289
290 namespace
291 {
292 class DefaultEffect : public simgear::Singleton<DefaultEffect>
293 {
294 public:
295     DefaultEffect()
296     {
297         _effect = new SGPropertyNode;
298         makeChild(_effect.ptr(), "inherits-from")
299             ->setStringValue("Effects/model-default");
300     }
301     virtual ~DefaultEffect() {}
302     SGPropertyNode* getEffect() { return _effect.ptr(); }
303 protected:
304     SGPropertyNode_ptr _effect;
305 };
306 }
307
308 ref_ptr<Node> instantiateEffects(osg::Node* modelGroup,
309                                  PropertyList& effectProps,
310                                  const SGReaderWriterOptions* options)
311 {
312     SGPropertyNode_ptr defaultEffectPropRoot;
313     MakeEffectVisitor visitor(options);
314     MakeEffectVisitor::EffectMap& emap = visitor.getEffectMap();
315     for (PropertyList::iterator itr = effectProps.begin(),
316              end = effectProps.end();
317          itr != end;
318         ++itr)
319     {
320         SGPropertyNode_ptr configNode = *itr;
321         std::vector<SGPropertyNode_ptr> objectNames =
322             configNode->getChildren("object-name");
323         SGPropertyNode* defaultNode = configNode->getChild("default");
324         if (defaultNode && defaultNode->getValue<bool>())
325             defaultEffectPropRoot = configNode;
326         BOOST_FOREACH(SGPropertyNode_ptr objNameNode, objectNames) {
327             emap.insert(make_pair(objNameNode->getStringValue(), configNode));
328         }
329         configNode->removeChild("default");
330         configNode->removeChildren("object-name");
331     }
332     if (!defaultEffectPropRoot)
333         defaultEffectPropRoot = DefaultEffect::instance()->getEffect();
334     visitor.setDefaultEffect(defaultEffectPropRoot.ptr());
335     modelGroup->accept(visitor);
336     osg::NodeList& result = visitor.getResults();
337     return ref_ptr<Node>(result[0].get());
338 }
339 }
340 // end of model.cxx