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 <osg/ref_ptr>
13 #include <osgDB/FileNameUtils>
14 #include <osgDB/FileUtils>
15 #include <osgDB/ReaderWriter>
16 #include <osgDB/ReadFile>
17 #include <osgDB/SharedStateManager>
18 
19 #include <simgear/scene/material/Effect.hxx>
20 #include <simgear/scene/material/EffectGeode.hxx>
21 #include <simgear/scene/util/SGSceneFeatures.hxx>
22 #include <simgear/scene/util/SGSceneUserData.hxx>
23 #include <simgear/scene/util/CopyOp.hxx>
24 #include <simgear/scene/util/SplicingVisitor.hxx>
25 #include <simgear/scene/util/SGReaderWriterOptions.hxx>
26 
27 #include <simgear/debug/ErrorReportingCallback.hxx>
28 #include <simgear/props/condition.hxx>
29 #include <simgear/props/props.hxx>
30 #include <simgear/props/props_io.hxx>
31 #include <simgear/structure/Singleton.hxx>
32 #include <simgear/structure/exception.hxx>
33 
34 #include "model.hxx"
35 
36 using std::vector;
37 
38 osg::Texture2D*
SGLoadTexture2D(bool staticTexture,const std::string & path,const osgDB::Options * options,bool wrapu,bool wrapv,int)39 SGLoadTexture2D(bool staticTexture, const std::string& path,
40                 const osgDB::Options* options,
41                 bool wrapu, bool wrapv, int)
42 {
43   osg::ref_ptr<osg::Image> image;
44   if (options)
45 #if OSG_VERSION_LESS_THAN(3,4,0)
46       image = osgDB::readImageFile(path, options);
47 #else
48       image = osgDB::readRefImageFile(path, options);
49 #endif
50   else
51 #if OSG_VERSION_LESS_THAN(3,4,0)
52       image = osgDB::readImageFile(path);
53 #else
54       image = osgDB::readRefImageFile(path);
55 #endif
56 
57   osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D;
58   texture->setImage(image);
59   texture->setMaxAnisotropy(SGSceneFeatures::instance()->getTextureFilter());
60 
61   if (staticTexture)
62     texture->setDataVariance(osg::Object::STATIC);
63   if (wrapu)
64     texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
65   else
66     texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP);
67   if (wrapv)
68     texture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
69   else
70     texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP);
71 
72   if (image) {
73     int s = image->s();
74     int t = image->t();
75 
76     if (s <= t && 32 <= s) {
77       SGSceneFeatures::instance()->applyTextureCompression(texture.get());
78     } else if (t < s && 32 <= t) {
79       SGSceneFeatures::instance()->applyTextureCompression(texture.get());
80     }
81   }
82 
83   return texture.release();
84 }
85 
86 namespace simgear
87 {
88 using namespace std;
89 using namespace osg;
90 using simgear::CopyOp;
91 
copyModel(Node * model)92 Node* copyModel(Node* model)
93 {
94     const CopyOp::CopyFlags flags = (CopyOp::DEEP_COPY_ALL
95                                      & ~CopyOp::DEEP_COPY_TEXTURES
96                                      & ~CopyOp::DEEP_COPY_IMAGES
97                                      & ~CopyOp::DEEP_COPY_STATESETS
98                                      & ~CopyOp::DEEP_COPY_STATEATTRIBUTES
99                                      & ~CopyOp::DEEP_COPY_ARRAYS
100                                      & ~CopyOp::DEEP_COPY_PRIMITIVES
101                                      // This will preserve display lists ...
102                                      & ~CopyOp::DEEP_COPY_DRAWABLES
103                                      & ~CopyOp::DEEP_COPY_SHAPES);
104     return (CopyOp(flags))(model);
105 }
106 
TextureUpdateVisitor(const osgDB::FilePathList & pathList)107 TextureUpdateVisitor::TextureUpdateVisitor(const osgDB::FilePathList& pathList) :
108     NodeAndDrawableVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN),
109     _pathList(pathList)
110 {
111 }
112 
apply(osg::Node & node)113 void TextureUpdateVisitor::apply(osg::Node& node)
114 {
115     StateSet* stateSet = cloneStateSet(node.getStateSet());
116     if (stateSet)
117         node.setStateSet(stateSet);
118     traverse(node);
119 }
120 
apply(Drawable & drawable)121 void TextureUpdateVisitor::apply(Drawable& drawable)
122 {
123     StateSet* stateSet = cloneStateSet(drawable.getStateSet());
124     if (stateSet)
125         drawable.setStateSet(stateSet);
126 }
127 
textureReplace(int unit,const StateAttribute * attr)128 Texture2D* TextureUpdateVisitor::textureReplace(int unit, const StateAttribute* attr)
129 {
130     using namespace osgDB;
131     const Texture2D* texture = dynamic_cast<const Texture2D*>(attr);
132 
133     if (!texture)
134         return 0;
135 
136     const Image* image = texture->getImage();
137     const string* fullFilePath = 0;
138     if (image) {
139         // The currently loaded file name
140         fullFilePath = &image->getFileName();
141     } else {
142         fullFilePath = &texture->getName();
143     }
144 
145     // The short name
146     string fileName = getSimpleFileName(*fullFilePath);
147     if (fileName.empty())
148         return 0;
149 
150     // The name that should be found with the current database path
151     string fullLiveryFile = findFileInPath(fileName, _pathList);
152     // If it is empty or they are identical then there is nothing to do
153     if (fullLiveryFile.empty() || fullLiveryFile == *fullFilePath)
154         return 0;
155 
156 #if OSG_VERSION_LESS_THAN(3,4,0)
157     Image* newImage = readImageFile(fullLiveryFile);
158 #else
159     osg::ref_ptr<Image> newImage = readRefImageFile(fullLiveryFile);
160 #endif
161     if (!newImage)
162         return 0;
163 
164     CopyOp copyOp(CopyOp::DEEP_COPY_ALL & ~CopyOp::DEEP_COPY_IMAGES);
165     Texture2D* newTexture = static_cast<Texture2D*>(copyOp(texture));
166     if (!newTexture)
167         return 0;
168 
169     newTexture->setImage(newImage);
170 #if OSG_VERSION_LESS_THAN(3,4,0)
171     if (newImage->valid())
172 #else
173     if (newImage.valid())
174 #endif
175     {
176         newTexture->setMaxAnisotropy(SGSceneFeatures::instance()->getTextureFilter());
177     }
178 
179     return newTexture;
180 }
181 
cloneStateSet(const StateSet * stateSet)182 StateSet* TextureUpdateVisitor::cloneStateSet(const StateSet* stateSet)
183 {
184     typedef std::pair<int, Texture2D*> Tex2D;
185     vector<Tex2D> newTextures;
186     StateSet* result = 0;
187 
188     if (!stateSet)
189         return 0;
190     int numUnits = stateSet->getTextureAttributeList().size();
191     if (numUnits > 0) {
192         for (int i = 0; i < numUnits; ++i) {
193             const StateAttribute* attr
194                 = stateSet->getTextureAttribute(i, StateAttribute::TEXTURE);
195             Texture2D* newTexture = textureReplace(i, attr);
196             if (newTexture)
197                 newTextures.push_back(Tex2D(i, newTexture));
198         }
199         if (!newTextures.empty()) {
200             result = static_cast<StateSet*>(stateSet->clone(CopyOp()));
201             for (vector<Tex2D>::iterator i = newTextures.begin();
202                  i != newTextures.end();
203                  ++i) {
204                 result->setTextureAttribute(i->first, i->second);
205             }
206         }
207     }
208     return result;
209 }
210 
UserDataCopyVisitor()211 UserDataCopyVisitor::UserDataCopyVisitor() :
212     NodeVisitor(NodeVisitor::NODE_VISITOR,
213                 NodeVisitor::TRAVERSE_ALL_CHILDREN)
214 {
215 }
216 
apply(Node & node)217 void UserDataCopyVisitor::apply(Node& node)
218 {
219     ref_ptr<SGSceneUserData> userData;
220     userData = SGSceneUserData::getSceneUserData(&node);
221     if (userData.valid()) {
222         SGSceneUserData* newUserData  = new SGSceneUserData(*userData);
223         newUserData->setVelocity(0);
224         node.setUserData(newUserData);
225     }
226     node.traverse(*this);
227 }
228 
229 namespace
230 {
231 class MakeEffectVisitor : public SplicingVisitor
232 {
233 public:
234     typedef std::map<string, SGPropertyNode_ptr> EffectMap;
235     using SplicingVisitor::apply;
MakeEffectVisitor(const SGReaderWriterOptions * options=0)236     MakeEffectVisitor(const SGReaderWriterOptions* options = 0)
237         : _options(options)
238     {
239     }
240     virtual void apply(osg::Group& node);
241     virtual void apply(osg::Geode& geode);
getEffectMap()242     EffectMap& getEffectMap() { return _effectMap; }
getEffectMap() const243     const EffectMap& getEffectMap() const { return _effectMap; }
setDefaultEffect(SGPropertyNode * effect)244     void setDefaultEffect(SGPropertyNode* effect)
245     {
246         _currentEffectParent = effect;
247     }
getDefaultEffect()248     SGPropertyNode* getDefaultEffect() { return _currentEffectParent; }
249 protected:
250     EffectMap _effectMap;
251     SGPropertyNode_ptr _currentEffectParent;
252     osg::ref_ptr<const SGReaderWriterOptions> _options;
253 };
254 
apply(osg::Group & node)255 void MakeEffectVisitor::apply(osg::Group& node)
256 {
257     SGPropertyNode_ptr savedEffectRoot;
258     const string& nodeName = node.getName();
259     bool restoreEffect = false;
260     if (!nodeName.empty()) {
261         EffectMap::iterator eitr = _effectMap.find(nodeName);
262         if (eitr != _effectMap.end()) {
263             savedEffectRoot = _currentEffectParent;
264             _currentEffectParent = eitr->second;
265             restoreEffect = true;
266         }
267     }
268     SplicingVisitor::apply(node);
269     // If a new node was created, copy the user data too.
270     ref_ptr<SGSceneUserData> userData = SGSceneUserData::getSceneUserData(&node);
271     if (userData.valid() && _childStack.back().back().get() != &node)
272         _childStack.back().back()->setUserData(new SGSceneUserData(*userData));
273     if (restoreEffect)
274         _currentEffectParent = savedEffectRoot;
275 }
276 
apply(osg::Geode & geode)277 void MakeEffectVisitor::apply(osg::Geode& geode)
278 {
279     if (pushNode(getNewNode(geode)))
280         return;
281     osg::StateSet* ss = geode.getStateSet();
282     if (!ss) {
283         pushNode(&geode);
284         return;
285     }
286     SGPropertyNode_ptr ssRoot = new SGPropertyNode;
287     makeParametersFromStateSet(ssRoot, ss);
288     SGPropertyNode_ptr effectRoot = new SGPropertyNode;
289     effect::mergePropertyTrees(effectRoot, ssRoot, _currentEffectParent);
290     Effect* effect = makeEffect(effectRoot, true, _options.get());
291     EffectGeode* eg = dynamic_cast<EffectGeode*>(&geode);
292     if (eg) {
293         eg->setEffect(effect);
294     } else {
295         eg = new EffectGeode;
296         eg->setEffect(effect);
297         ref_ptr<SGSceneUserData> userData = SGSceneUserData::getSceneUserData(&geode);
298         if (userData.valid())
299             eg->setUserData(new SGSceneUserData(*userData));
300         for (unsigned i = 0; i < geode.getNumDrawables(); ++i) {
301             osg::Drawable *drawable = geode.getDrawable(i);
302             eg->addDrawable(drawable);
303 
304             // Generate tangent vectors etc if needed
305             osg::Geometry *geom = dynamic_cast<osg::Geometry*>(drawable);
306             if(geom) eg->runGenerators(geom);
307         }
308     }
309     pushResultNode(&geode, eg);
310 
311 }
312 
313 }
314 
315 namespace
316 {
317 class DefaultEffect : public simgear::Singleton<DefaultEffect>
318 {
319 public:
DefaultEffect()320     DefaultEffect()
321     {
322         _effect = new SGPropertyNode;
323         makeChild(_effect.ptr(), "inherits-from")
324             ->setStringValue("Effects/model-default");
325     }
~DefaultEffect()326     virtual ~DefaultEffect() {}
getEffect()327     SGPropertyNode* getEffect() { return _effect.ptr(); }
328 protected:
329     SGPropertyNode_ptr _effect;
330 };
331 }
332 
instantiateEffects(osg::Node * modelGroup,PropertyList & effectProps,const SGReaderWriterOptions * options)333 ref_ptr<Node> instantiateEffects(osg::Node* modelGroup,
334                                  PropertyList& effectProps,
335                                  const SGReaderWriterOptions* options)
336 {
337     SGPropertyNode_ptr defaultEffectPropRoot;
338     MakeEffectVisitor visitor(options);
339     MakeEffectVisitor::EffectMap& emap = visitor.getEffectMap();
340     for (PropertyList::iterator itr = effectProps.begin(),
341              end = effectProps.end();
342          itr != end;
343         ++itr)
344     {
345         SGPropertyNode_ptr configNode = *itr;
346         std::vector<SGPropertyNode_ptr> objectNames =
347             configNode->getChildren("object-name");
348         SGPropertyNode* defaultNode = configNode->getChild("default");
349         if (defaultNode && defaultNode->getValue<bool>())
350             defaultEffectPropRoot = configNode;
351         for (auto objNameNode : objectNames) {
352             emap.insert(make_pair(objNameNode->getStringValue(), configNode));
353         }
354         configNode->removeChild("default");
355         configNode->removeChildren("object-name");
356     }
357     if (!defaultEffectPropRoot)
358         defaultEffectPropRoot = DefaultEffect::instance()->getEffect();
359     visitor.setDefaultEffect(defaultEffectPropRoot.ptr());
360     modelGroup->accept(visitor);
361     osg::NodeList& result = visitor.getResults();
362     return ref_ptr<Node>(result[0].get());
363 }
364 
instantiateMaterialEffects(osg::Node * modelGroup,const SGReaderWriterOptions * options)365 ref_ptr<Node> instantiateMaterialEffects(osg::Node* modelGroup,
366                                         const SGReaderWriterOptions* options)
367 {
368 
369     SGPropertyNode_ptr effect;
370     PropertyList effectProps;
371 
372     if (options->getMaterialLib()) {
373       const SGGeod loc = SGGeod(options->getLocation());
374       SGMaterialCache* matcache = options->getMaterialLib()->generateMatCache(loc);
375       SGMaterial* mat = matcache->find(options->getMaterialName());
376       delete matcache;
377 
378       if (mat) {
379         effect = new SGPropertyNode();
380         makeChild(effect, "inherits-from")->setStringValue(mat->get_effect_name());
381       } else {
382         effect = DefaultEffect::instance()->getEffect();
383         SG_LOG( SG_TERRAIN, SG_ALERT, "Unable to get effect for " << options->getMaterialName());
384         simgear::reportFailure(simgear::LoadFailure::NotFound, simgear::ErrorCode::LoadEffectsShaders,
385                                "Unable to get effect for material:" + options->getMaterialName());
386       }
387     } else {
388       effect = DefaultEffect::instance()->getEffect();
389     }
390 
391     effect->addChild("default")->setBoolValue(true);
392     effectProps.push_back(effect);
393     return instantiateEffects(modelGroup, effectProps, options);
394 }
395 
396 }
397 // end of model.cxx
398