]> git.mxchange.org Git - simgear.git/blob - simgear/scene/model/SGMaterialAnimation.cxx
Modified Files:
[simgear.git] / simgear / scene / model / SGMaterialAnimation.cxx
1 // animation.cxx - classes to manage model animation.
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 "SGMaterialAnimation.hxx"
11
12 #include <osg/AlphaFunc>
13 #include <osg/Drawable>
14 #include <osg/Geode>
15 #include <osg/Geometry>
16 #include <osg/StateSet>
17 #include <osgDB/FileNameUtils>
18 #include <osgDB/FileUtils>
19 #include <osgDB/ReadFile>
20
21 #include <simgear/props/condition.hxx>
22 #include <simgear/props/props.hxx>
23 #include <simgear/scene/model/model.hxx>
24
25 struct SGMaterialAnimation::ColorSpec {
26   float red, green, blue;
27   float factor;
28   float offset;
29   SGPropertyNode_ptr red_prop;
30   SGPropertyNode_ptr green_prop;
31   SGPropertyNode_ptr blue_prop;
32   SGPropertyNode_ptr factor_prop;
33   SGPropertyNode_ptr offset_prop;
34   SGVec4f v;
35   
36   ColorSpec(const SGPropertyNode* configNode, SGPropertyNode* modelRoot)
37   {
38     red = -1.0;
39     green = -1.0;
40     blue = -1.0;
41     if (!configNode)
42       return;
43     
44     red = configNode->getFloatValue("red", -1.0);
45     green = configNode->getFloatValue("green", -1.0);
46     blue = configNode->getFloatValue("blue", -1.0);
47     factor = configNode->getFloatValue("factor", 1.0);
48     offset = configNode->getFloatValue("offset", 0.0);
49     
50     if (!modelRoot)
51       return;
52     const SGPropertyNode *node;
53     node = configNode->getChild("red-prop");
54     if (node)
55       red_prop = modelRoot->getNode(node->getStringValue(), true);
56     node = configNode->getChild("green-prop");
57     if (node)
58       green_prop = modelRoot->getNode(node->getStringValue(), true);
59     node = configNode->getChild("blue-prop");
60     if (node)
61       blue_prop = modelRoot->getNode(node->getStringValue(), true);
62     node = configNode->getChild("factor-prop");
63     if (node)
64       factor_prop = modelRoot->getNode(node->getStringValue(), true);
65     node = configNode->getChild("offset-prop");
66     if (node)
67       offset_prop = modelRoot->getNode(node->getStringValue(), true);
68   }
69   
70   bool dirty() {
71     return red >= 0 || green >= 0 || blue >= 0;
72   }
73   bool live() {
74     return red_prop || green_prop || blue_prop
75       || factor_prop || offset_prop;
76   }
77   SGVec4f &rgba() {
78     if (red_prop)
79       red = red_prop->getFloatValue();
80     if (green_prop)
81       green = green_prop->getFloatValue();
82     if (blue_prop)
83       blue = blue_prop->getFloatValue();
84     if (factor_prop)
85       factor = factor_prop->getFloatValue();
86     if (offset_prop)
87       offset = offset_prop->getFloatValue();
88     v[0] = SGMiscf::clip(red*factor + offset, 0, 1);
89     v[1] = SGMiscf::clip(green*factor + offset, 0, 1);
90     v[2] = SGMiscf::clip(blue*factor + offset, 0, 1);
91     v[3] = 1;
92     return v;
93   }
94   SGVec4f &initialRgba() {
95     v[0] = SGMiscf::clip(red*factor + offset, 0, 1);
96     v[1] = SGMiscf::clip(green*factor + offset, 0, 1);
97     v[2] = SGMiscf::clip(blue*factor + offset, 0, 1);
98     v[3] = 1;
99     return v;
100   }
101 };
102
103
104 struct SGMaterialAnimation::PropSpec {
105   float value;
106   float factor;
107   float offset;
108   float min;
109   float max;
110   SGPropertyNode_ptr value_prop;
111   SGPropertyNode_ptr factor_prop;
112   SGPropertyNode_ptr offset_prop;
113   
114   PropSpec(const char* valueName, const char* valuePropName,
115            const SGPropertyNode* configNode, SGPropertyNode* modelRoot)
116   {
117     value = -1;
118     if (!configNode)
119       return;
120     
121     value = configNode->getFloatValue(valueName, -1);
122     factor = configNode->getFloatValue("factor", 1);
123     offset = configNode->getFloatValue("offset", 0);
124     min = configNode->getFloatValue("min", 0);
125     max = configNode->getFloatValue("max", 1);
126     
127     if (!modelRoot)
128       return;
129     const SGPropertyNode *node;
130     node = configNode->getChild(valuePropName);
131     if (node)
132       value_prop = modelRoot->getNode(node->getStringValue(), true);
133     node = configNode->getChild("factor-prop");
134     if (node)
135       factor_prop = modelRoot->getNode(node->getStringValue(), true);
136     node = configNode->getChild("offset-prop");
137     if (node)
138       offset_prop = modelRoot->getNode(node->getStringValue(), true);
139   }
140   bool dirty() { return value >= 0.0; }
141   bool live() { return value_prop || factor_prop || offset_prop; }
142   float getValue()
143   {
144     if (value_prop)
145       value = value_prop->getFloatValue();
146     if (offset_prop)
147       offset = offset_prop->getFloatValue();
148     if (factor_prop)
149       factor = factor_prop->getFloatValue();
150     return SGMiscf::clip(value*factor + offset, min, max);
151   }
152   float getInitialValue()
153   {
154     return SGMiscf::clip(value*factor + offset, min, max);
155   }
156 };
157
158 class SGMaterialAnimation::MaterialVisitor : public osg::NodeVisitor {
159 public:
160   enum {
161     DIFFUSE = 1,
162     AMBIENT = 2,
163     SPECULAR = 4,
164     EMISSION = 8,
165     SHININESS = 16,
166     TRANSPARENCY = 32
167   };
168
169   MaterialVisitor() :
170     osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
171     _updateMask(0),
172     _ambient(-1, -1, -1, -1),
173     _diffuse(-1, -1, -1, -1),
174     _specular(-1, -1, -1, -1),
175     _emission(-1, -1, -1, -1),
176     _shininess(-1),
177     _alpha(-1)
178   {
179     setVisitorType(osg::NodeVisitor::NODE_VISITOR);
180   }
181
182   void setDiffuse(const SGVec4f& diffuse)
183   {
184     if (diffuse != _diffuse)
185       _diffuse = diffuse;
186     _updateMask |= DIFFUSE;
187   }
188   void setAmbient(const SGVec4f& ambient)
189   {
190     if (ambient != _ambient)
191       _ambient = ambient;
192     _updateMask |= AMBIENT;
193   }
194   void setSpecular(const SGVec4f& specular)
195   {
196     if (specular != _specular)
197       _specular = specular;
198     _updateMask |= SPECULAR;
199   }
200   void setEmission(const SGVec4f& emission)
201   {
202     if (emission != _emission)
203       _emission = emission;
204     _updateMask |= EMISSION;
205   }
206   void setShininess(float shininess)
207   {
208     if (shininess != _shininess)
209       _shininess = shininess;
210     _updateMask |= SHININESS;
211   }
212
213   void setAlpha(float alpha)
214   {
215     if (alpha != _alpha)
216       _alpha = alpha;
217     _updateMask |= TRANSPARENCY;
218   }
219
220   virtual void reset()
221   {
222     _updateMask = 0;
223   }
224   virtual void apply(osg::Node& node)
225   {
226     updateStateSet(node.getStateSet());
227     traverse(node);
228   }
229   virtual void apply(osg::Geode& node)
230   {
231     apply((osg::Node&)node);
232     unsigned nDrawables = node.getNumDrawables();
233     for (unsigned i = 0; i < nDrawables; ++i) {
234       osg::Drawable* drawable = node.getDrawable(i);
235       updateStateSet(drawable->getStateSet());
236
237       if (_updateMask&TRANSPARENCY) {
238         osg::Geometry* geometry = drawable->asGeometry();
239         if (!geometry)
240           continue;
241         osg::Array* array = geometry->getColorArray();
242         if (!array)
243           continue;
244         osg::Vec4Array* vec4Array = dynamic_cast<osg::Vec4Array*>(array);
245         if (!vec4Array)
246           continue;
247
248         // FIXME, according to the colormode in the material
249         // we might incorporate the apropriate color value
250         geometry->dirtyDisplayList();
251         vec4Array->dirty();
252         for (unsigned k = 0; k < vec4Array->size(); ++k) {
253           (*vec4Array)[k][3] = _alpha;
254         }
255       }
256     }
257   }
258   void updateStateSet(osg::StateSet* stateSet)
259   {
260     if (!stateSet)
261       return;
262     osg::StateAttribute* stateAttribute;
263     stateAttribute = stateSet->getAttribute(osg::StateAttribute::MATERIAL);
264     if (!stateAttribute)
265       return;
266     osg::Material* material = dynamic_cast<osg::Material*>(stateAttribute);
267     if (!material)
268       return;
269     if (_updateMask&AMBIENT)
270       material->setAmbient(osg::Material::FRONT_AND_BACK, _ambient.osg());
271     if (_updateMask&DIFFUSE)
272       material->setDiffuse(osg::Material::FRONT_AND_BACK, _diffuse.osg());
273     if (_updateMask&SPECULAR)
274       material->setSpecular(osg::Material::FRONT_AND_BACK, _specular.osg());
275     if (_updateMask&EMISSION)
276       material->setEmission(osg::Material::FRONT_AND_BACK, _emission.osg());
277     if (_updateMask&SHININESS)
278       material->setShininess(osg::Material::FRONT_AND_BACK, _shininess);
279     if (_updateMask&TRANSPARENCY) {
280       material->setAlpha(osg::Material::FRONT_AND_BACK, _alpha);
281       if (_alpha < 1) {
282         stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
283         stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
284       } else {
285         stateSet->setRenderingHint(osg::StateSet::DEFAULT_BIN);
286       }
287     }
288   }
289 private:
290   unsigned _updateMask;
291   SGVec4f _ambient;
292   SGVec4f _diffuse;
293   SGVec4f _specular;
294   SGVec4f _emission;
295   float _shininess;
296   float _alpha;
297 };
298
299 class SGMaterialAnimation::UpdateCallback : public osg::NodeCallback {
300 public:
301   UpdateCallback(const osgDB::FilePathList& texturePathList,
302                  const SGCondition* condition,
303                  const SGPropertyNode* configNode, SGPropertyNode* modelRoot) :
304     _condition(condition),
305     _ambient(configNode->getChild("ambient"), modelRoot),
306     _diffuse(configNode->getChild("diffuse"), modelRoot),
307     _specular(configNode->getChild("specular"), modelRoot),
308     _emission(configNode->getChild("emission"), modelRoot),
309     _shininess("shininess", "shininess-prop",
310                configNode->getChild("shininess"), modelRoot),
311     _transparency("alpha", "alpha-prop",
312                   configNode->getChild("transparency"), modelRoot),
313     _texturePathList(texturePathList)
314   {
315     const SGPropertyNode* node;
316
317     node = configNode->getChild("threshold-prop");
318     if (node)
319       _thresholdProp = modelRoot->getNode(node->getStringValue(), true);
320     node = configNode->getChild("texture-prop");
321     if (node)
322       _textureProp = modelRoot->getNode(node->getStringValue(), true);
323   }
324
325   virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
326   {
327     if (!_condition || _condition->test()) {
328       if (_textureProp) {
329         std::string textureName = _textureProp->getStringValue();
330         if (_textureName != textureName) {
331           osg::StateSet* stateSet = node->getOrCreateStateSet();
332           while (stateSet->getTextureAttribute(0, osg::StateAttribute::TEXTURE)) {
333             stateSet->removeTextureAttribute(0, osg::StateAttribute::TEXTURE);
334           }
335           std::string textureFile;
336           textureFile = osgDB::findFileInPath(textureName, _texturePathList);
337           if (!textureFile.empty()) {
338             osg::Texture2D* texture2D = SGLoadTexture2D(textureFile);
339             if (texture2D) {
340               stateSet->setTextureAttribute(0, texture2D);
341               stateSet->setTextureMode(0, GL_TEXTURE_2D,
342                                        osg::StateAttribute::ON);
343               _textureName = textureName;
344             }
345           }
346         }
347       }
348       if (_thresholdProp) {
349         osg::StateSet* stateSet = node->getOrCreateStateSet();
350         osg::StateAttribute* stateAttribute;
351         stateAttribute = stateSet->getAttribute(osg::StateAttribute::ALPHAFUNC);
352         assert(dynamic_cast<osg::AlphaFunc*>(stateAttribute));
353         osg::AlphaFunc* alphaFunc = static_cast<osg::AlphaFunc*>(stateAttribute);
354         alphaFunc->setReferenceValue(_thresholdProp->getFloatValue());
355       }
356       
357       _visitor.reset();
358       if (_ambient.live())
359         _visitor.setAmbient(_ambient.rgba());
360       if (_diffuse.live())
361         _visitor.setDiffuse(_diffuse.rgba());
362       if (_specular.live())
363         _visitor.setSpecular(_specular.rgba());
364       if (_emission.live())
365         _visitor.setEmission(_emission.rgba());
366       if (_shininess.live())
367         _visitor.setShininess(_shininess.getValue());
368       if (_transparency.live())
369         _visitor.setAlpha(_transparency.getValue());
370       
371       node->accept(_visitor);
372     }
373
374     traverse(node, nv);
375   }
376 private:
377   SGSharedPtr<const SGCondition> _condition;
378   SGSharedPtr<const SGPropertyNode> _textureProp;
379   SGSharedPtr<const SGPropertyNode> _thresholdProp;
380   MaterialVisitor _visitor;
381   std::string _textureName;
382   ColorSpec _ambient;
383   ColorSpec _diffuse;
384   ColorSpec _specular;
385   ColorSpec _emission;
386   PropSpec _shininess;
387   PropSpec _transparency;
388   osgDB::FilePathList _texturePathList;
389 };
390
391 SGMaterialAnimation::SGMaterialAnimation(const SGPropertyNode* configNode,
392                                          SGPropertyNode* modelRoot) :
393   SGAnimation(configNode, modelRoot)
394 {
395   if (configNode->hasChild("global"))
396     SG_LOG(SG_IO, SG_ALERT, "Using global material animation that can "
397            "no longer work");
398 }
399
400 osg::Group*
401 SGMaterialAnimation::createAnimationGroup(osg::Group& parent)
402 {
403   osg::Group* group = new osg::Group;
404   group->setName("material animation group");
405
406   SGPropertyNode* inputRoot = getModelRoot();
407   const SGPropertyNode* node = getConfig()->getChild("property-base");
408   if (node)
409     inputRoot = getModelRoot()->getRootNode()->getNode(node->getStringValue(),
410                                                        true);
411
412   osgDB::FilePathList texturePathList = osgDB::getDataFilePathList();
413
414   if (getConfig()->hasChild("texture")) {
415     std::string textureName = getConfig()->getStringValue("texture");
416     std::string textureFile;
417     textureFile = osgDB::findFileInPath(textureName, texturePathList);
418     if (!textureFile.empty()) {
419       osg::StateSet* stateSet = group->getOrCreateStateSet();
420       osg::Texture2D* texture2D = SGLoadTexture2D(textureFile);
421       if (texture2D) {
422         stateSet->setTextureAttribute(0, texture2D);
423         stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::ON);
424         if (texture2D->getImage()->isImageTranslucent()) {
425           stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
426           stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
427         }
428       }
429     }
430   }
431   if (getConfig()->hasChild("threshold-prop") ||
432       getConfig()->hasChild("threshold")) {
433     osg::StateSet* stateSet = group->getOrCreateStateSet();
434     osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
435     alphaFunc->setFunction(osg::AlphaFunc::GREATER);
436     float threshold = getConfig()->getFloatValue("threshold", 0);
437     alphaFunc->setReferenceValue(threshold);
438     stateSet->setAttributeAndModes(alphaFunc);
439   }
440
441   UpdateCallback* updateCallback;
442   updateCallback = new UpdateCallback(texturePathList, getCondition(),
443                                       getConfig(), inputRoot);
444   group->setUpdateCallback(updateCallback);
445   parent.addChild(group);
446   return group;
447 }
448
449 void
450 SGMaterialAnimation::install(osg::Node& node)
451 {
452   SGAnimation::install(node);
453   // make sure everything (except the texture attributes)
454   // below is private to our model
455   cloneDrawables(node);
456
457   // Remove all textures if required, they get replaced later on
458   if (getConfig()->hasChild("texture") ||
459       getConfig()->hasChild("texture-prop")) {
460     removeTextureAttribute(node, 0, osg::StateAttribute::TEXTURE);
461     removeTextureMode(node, 0, GL_TEXTURE_2D);
462   }
463   // Remove all nested alphaFuncs
464   if (getConfig()->hasChild("threshold") ||
465       getConfig()->hasChild("threshold-prop"))
466     removeAttribute(node, osg::StateAttribute::ALPHAFUNC);
467
468   ColorSpec ambient(getConfig()->getChild("ambient"), getModelRoot());
469   ColorSpec diffuse(getConfig()->getChild("diffuse"), getModelRoot());
470   ColorSpec specular(getConfig()->getChild("specular"), getModelRoot());
471   ColorSpec emission(getConfig()->getChild("emission"), getModelRoot());
472   PropSpec shininess("shininess", "shininess-prop",
473                      getConfig()->getChild("shininess"), getModelRoot());
474   PropSpec transparency("alpha", "alpha-prop",
475                         getConfig()->getChild("transparency"), getModelRoot());
476
477   MaterialVisitor visitor;
478   if (ambient.dirty())
479     visitor.setAmbient(ambient.initialRgba());
480   if (diffuse.dirty())
481     visitor.setDiffuse(diffuse.initialRgba());
482   if (specular.dirty())
483     visitor.setSpecular(specular.initialRgba());
484   if (emission.dirty())
485     visitor.setEmission(emission.initialRgba());
486   if (shininess.dirty())
487     visitor.setShininess(shininess.getInitialValue());
488   if (transparency.dirty())
489     visitor.setAlpha(transparency.getInitialValue());
490   node.accept(visitor);
491 }
492