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