1 // animation.cxx - classes to manage model animation.
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 "SGMaterialAnimation.hxx"
12 #include <osg/AlphaFunc>
13 #include <osg/Drawable>
15 #include <osg/Geometry>
16 #include <osg/StateSet>
17 #include <osgDB/FileNameUtils>
18 #include <osgDB/FileUtils>
19 #include <osgDB/ReadFile>
21 #include <simgear/props/condition.hxx>
22 #include <simgear/props/props.hxx>
23 #include <simgear/scene/model/model.hxx>
25 struct SGMaterialAnimation::ColorSpec {
26 float red, green, blue;
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;
36 ColorSpec(const SGPropertyNode* configNode, SGPropertyNode* modelRoot)
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);
52 const SGPropertyNode *node;
53 node = configNode->getChild("red-prop");
55 red_prop = modelRoot->getNode(node->getStringValue(), true);
56 node = configNode->getChild("green-prop");
58 green_prop = modelRoot->getNode(node->getStringValue(), true);
59 node = configNode->getChild("blue-prop");
61 blue_prop = modelRoot->getNode(node->getStringValue(), true);
62 node = configNode->getChild("factor-prop");
64 factor_prop = modelRoot->getNode(node->getStringValue(), true);
65 node = configNode->getChild("offset-prop");
67 offset_prop = modelRoot->getNode(node->getStringValue(), true);
71 return red >= 0 || green >= 0 || blue >= 0;
74 return red_prop || green_prop || blue_prop
75 || factor_prop || offset_prop;
79 red = red_prop->getFloatValue();
81 green = green_prop->getFloatValue();
83 blue = blue_prop->getFloatValue();
85 factor = factor_prop->getFloatValue();
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);
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);
104 struct SGMaterialAnimation::PropSpec {
110 SGPropertyNode_ptr value_prop;
111 SGPropertyNode_ptr factor_prop;
112 SGPropertyNode_ptr offset_prop;
114 PropSpec(const char* valueName, const char* valuePropName,
115 const SGPropertyNode* configNode, SGPropertyNode* modelRoot)
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);
129 const SGPropertyNode *node;
130 node = configNode->getChild(valuePropName);
132 value_prop = modelRoot->getNode(node->getStringValue(), true);
133 node = configNode->getChild("factor-prop");
135 factor_prop = modelRoot->getNode(node->getStringValue(), true);
136 node = configNode->getChild("offset-prop");
138 offset_prop = modelRoot->getNode(node->getStringValue(), true);
140 bool dirty() { return value >= 0.0; }
141 bool live() { return value_prop || factor_prop || offset_prop; }
145 value = value_prop->getFloatValue();
147 offset = offset_prop->getFloatValue();
149 factor = factor_prop->getFloatValue();
150 return SGMiscf::clip(value*factor + offset, min, max);
152 float getInitialValue()
154 return SGMiscf::clip(value*factor + offset, min, max);
158 class SGMaterialAnimation::MaterialVisitor : public osg::NodeVisitor {
170 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
172 _ambient(-1, -1, -1, -1),
173 _diffuse(-1, -1, -1, -1),
174 _specular(-1, -1, -1, -1),
175 _emission(-1, -1, -1, -1),
179 setVisitorType(osg::NodeVisitor::NODE_VISITOR);
182 void setDiffuse(const SGVec4f& diffuse)
184 if (diffuse != _diffuse)
186 _updateMask |= DIFFUSE;
188 void setAmbient(const SGVec4f& ambient)
190 if (ambient != _ambient)
192 _updateMask |= AMBIENT;
194 void setSpecular(const SGVec4f& specular)
196 if (specular != _specular)
197 _specular = specular;
198 _updateMask |= SPECULAR;
200 void setEmission(const SGVec4f& emission)
202 if (emission != _emission)
203 _emission = emission;
204 _updateMask |= EMISSION;
206 void setShininess(float shininess)
208 if (shininess != _shininess)
209 _shininess = shininess;
210 _updateMask |= SHININESS;
213 void setAlpha(float alpha)
217 _updateMask |= TRANSPARENCY;
224 virtual void apply(osg::Node& node)
226 updateStateSet(node.getStateSet());
229 virtual void apply(osg::Geode& node)
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());
237 if (_updateMask&TRANSPARENCY) {
238 osg::Geometry* geometry = drawable->asGeometry();
241 osg::Array* array = geometry->getColorArray();
244 osg::Vec4Array* vec4Array = dynamic_cast<osg::Vec4Array*>(array);
248 // FIXME, according to the colormode in the material
249 // we might incorporate the apropriate color value
250 geometry->dirtyDisplayList();
252 for (unsigned k = 0; k < vec4Array->size(); ++k) {
253 (*vec4Array)[k][3] = _alpha;
258 void updateStateSet(osg::StateSet* stateSet)
262 osg::StateAttribute* stateAttribute;
263 stateAttribute = stateSet->getAttribute(osg::StateAttribute::MATERIAL);
266 osg::Material* material = dynamic_cast<osg::Material*>(stateAttribute);
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);
282 stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
283 stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
285 stateSet->setRenderingHint(osg::StateSet::DEFAULT_BIN);
290 unsigned _updateMask;
299 class SGMaterialAnimation::UpdateCallback : public osg::NodeCallback {
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)
315 const SGPropertyNode* node;
317 node = configNode->getChild("threshold-prop");
319 _thresholdProp = modelRoot->getNode(node->getStringValue(), true);
320 node = configNode->getChild("texture-prop");
322 _textureProp = modelRoot->getNode(node->getStringValue(), true);
325 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
327 if (!_condition || _condition->test()) {
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);
335 std::string textureFile;
336 textureFile = osgDB::findFileInPath(textureName, _texturePathList);
337 if (!textureFile.empty()) {
338 osg::Texture2D* texture2D = SGLoadTexture2D(textureFile);
340 stateSet->setTextureAttribute(0, texture2D);
341 stateSet->setTextureMode(0, GL_TEXTURE_2D,
342 osg::StateAttribute::ON);
343 _textureName = textureName;
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());
359 _visitor.setAmbient(_ambient.rgba());
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());
371 node->accept(_visitor);
377 SGSharedPtr<const SGCondition> _condition;
378 SGSharedPtr<const SGPropertyNode> _textureProp;
379 SGSharedPtr<const SGPropertyNode> _thresholdProp;
380 MaterialVisitor _visitor;
381 std::string _textureName;
387 PropSpec _transparency;
388 osgDB::FilePathList _texturePathList;
391 SGMaterialAnimation::SGMaterialAnimation(const SGPropertyNode* configNode,
392 SGPropertyNode* modelRoot) :
393 SGAnimation(configNode, modelRoot)
395 if (configNode->hasChild("global"))
396 SG_LOG(SG_IO, SG_ALERT, "Using global material animation that can "
401 SGMaterialAnimation::createAnimationGroup(osg::Group& parent)
403 osg::Group* group = new osg::Group;
404 group->setName("material animation group");
406 SGPropertyNode* inputRoot = getModelRoot();
407 const SGPropertyNode* node = getConfig()->getChild("property-base");
409 inputRoot = getModelRoot()->getRootNode()->getNode(node->getStringValue(),
412 osgDB::FilePathList texturePathList = osgDB::getDataFilePathList();
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);
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);
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);
441 UpdateCallback* updateCallback;
442 updateCallback = new UpdateCallback(texturePathList, getCondition(),
443 getConfig(), inputRoot);
444 group->setUpdateCallback(updateCallback);
445 parent.addChild(group);
450 SGMaterialAnimation::install(osg::Node& node)
452 SGAnimation::install(node);
453 // make sure everything (except the texture attributes)
454 // below is private to our model
455 cloneDrawables(node);
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);
463 // Remove all nested alphaFuncs
464 if (getConfig()->hasChild("threshold") ||
465 getConfig()->hasChild("threshold-prop"))
466 removeAttribute(node, osg::StateAttribute::ALPHAFUNC);
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());
477 MaterialVisitor visitor;
479 visitor.setAmbient(ambient.initialRgba());
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);