From bdd5ca140d52318b46ca99707661a467c590e32f Mon Sep 17 00:00:00 2001 From: frohlich Date: Sun, 3 Dec 2006 16:57:20 +0000 Subject: [PATCH] Modified Files: simgear/scene/model/Makefile.am simgear/scene/model/animation.cxx simgear/scene/model/animation.hxx simgear/scene/model/model.cxx simgear/scene/model/persparam.cxx simgear/scene/model/persparam.hxx simgear/scene/model/shadanim.cxx Added Files: simgear/scene/model/SGMaterialAnimation.cxx simgear/scene/model/SGMaterialAnimation.hxx Big animation overhaul. Improoves animation correctness. --- simgear/scene/model/Makefile.am | 6 +- simgear/scene/model/SGMaterialAnimation.cxx | 492 +++ simgear/scene/model/SGMaterialAnimation.hxx | 33 + simgear/scene/model/animation.cxx | 3012 +++++++++++-------- simgear/scene/model/animation.hxx | 684 ++--- simgear/scene/model/model.cxx | 282 +- simgear/scene/model/persparam.cxx | 2 +- simgear/scene/model/persparam.hxx | 10 +- simgear/scene/model/shadanim.cxx | 140 +- 9 files changed, 2670 insertions(+), 1991 deletions(-) create mode 100644 simgear/scene/model/SGMaterialAnimation.cxx create mode 100644 simgear/scene/model/SGMaterialAnimation.hxx diff --git a/simgear/scene/model/Makefile.am b/simgear/scene/model/Makefile.am index 70b8449c..b1ff2734 100644 --- a/simgear/scene/model/Makefile.am +++ b/simgear/scene/model/Makefile.am @@ -11,7 +11,8 @@ include_HEADERS = \ modellib.hxx \ persparam.hxx \ placement.hxx \ - placementtrans.hxx + placementtrans.hxx \ + SGMaterialAnimation.hxx libsgmodel_a_SOURCES = \ animation.cxx \ @@ -21,6 +22,7 @@ libsgmodel_a_SOURCES = \ persparam.cxx \ placement.cxx \ placementtrans.cxx \ - shadanim.cxx + shadanim.cxx \ + SGMaterialAnimation.cxx INCLUDES = -I$(top_srcdir) diff --git a/simgear/scene/model/SGMaterialAnimation.cxx b/simgear/scene/model/SGMaterialAnimation.cxx new file mode 100644 index 00000000..f099be0c --- /dev/null +++ b/simgear/scene/model/SGMaterialAnimation.cxx @@ -0,0 +1,492 @@ +// animation.cxx - classes to manage model animation. +// Written by David Megginson, started 2002. +// +// This file is in the Public Domain, and comes with no warranty. + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "SGMaterialAnimation.hxx" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +struct SGMaterialAnimation::ColorSpec { + float red, green, blue; + float factor; + float offset; + SGPropertyNode_ptr red_prop; + SGPropertyNode_ptr green_prop; + SGPropertyNode_ptr blue_prop; + SGPropertyNode_ptr factor_prop; + SGPropertyNode_ptr offset_prop; + SGVec4f v; + + ColorSpec(const SGPropertyNode* configNode, SGPropertyNode* modelRoot) + { + red = -1.0; + green = -1.0; + blue = -1.0; + if (!configNode) + return; + + red = configNode->getFloatValue("red", -1.0); + green = configNode->getFloatValue("green", -1.0); + blue = configNode->getFloatValue("blue", -1.0); + factor = configNode->getFloatValue("factor", 1.0); + offset = configNode->getFloatValue("offset", 0.0); + + if (!modelRoot) + return; + const SGPropertyNode *node; + node = configNode->getChild("red-prop"); + if (node) + red_prop = modelRoot->getNode(node->getStringValue(), true); + node = configNode->getChild("green-prop"); + if (node) + green_prop = modelRoot->getNode(node->getStringValue(), true); + node = configNode->getChild("blue-prop"); + if (node) + blue_prop = modelRoot->getNode(node->getStringValue(), true); + node = configNode->getChild("factor-prop"); + if (node) + factor_prop = modelRoot->getNode(node->getStringValue(), true); + node = configNode->getChild("offset-prop"); + if (node) + offset_prop = modelRoot->getNode(node->getStringValue(), true); + } + + bool dirty() { + return red >= 0 || green >= 0 || blue >= 0; + } + bool live() { + return red_prop || green_prop || blue_prop + || factor_prop || offset_prop; + } + SGVec4f &rgba() { + if (red_prop) + red = red_prop->getFloatValue(); + if (green_prop) + green = green_prop->getFloatValue(); + if (blue_prop) + blue = blue_prop->getFloatValue(); + if (factor_prop) + factor = factor_prop->getFloatValue(); + if (offset_prop) + offset = offset_prop->getFloatValue(); + v[0] = SGMiscf::clip(red*factor + offset, 0, 1); + v[1] = SGMiscf::clip(green*factor + offset, 0, 1); + v[2] = SGMiscf::clip(blue*factor + offset, 0, 1); + v[3] = 1; + return v; + } + SGVec4f &initialRgba() { + v[0] = SGMiscf::clip(red*factor + offset, 0, 1); + v[1] = SGMiscf::clip(green*factor + offset, 0, 1); + v[2] = SGMiscf::clip(blue*factor + offset, 0, 1); + v[3] = 1; + return v; + } +}; + + +struct SGMaterialAnimation::PropSpec { + float value; + float factor; + float offset; + float min; + float max; + SGPropertyNode_ptr value_prop; + SGPropertyNode_ptr factor_prop; + SGPropertyNode_ptr offset_prop; + + PropSpec(const char* valueName, const char* valuePropName, + const SGPropertyNode* configNode, SGPropertyNode* modelRoot) + { + value = -1; + if (!configNode) + return; + + value = configNode->getFloatValue(valueName, -1); + factor = configNode->getFloatValue("factor", 1); + offset = configNode->getFloatValue("offset", 0); + min = configNode->getFloatValue("min", 0); + max = configNode->getFloatValue("max", 1); + + if (!modelRoot) + return; + const SGPropertyNode *node; + node = configNode->getChild(valuePropName); + if (node) + value_prop = modelRoot->getNode(node->getStringValue(), true); + node = configNode->getChild("factor-prop"); + if (node) + factor_prop = modelRoot->getNode(node->getStringValue(), true); + node = configNode->getChild("offset-prop"); + if (node) + offset_prop = modelRoot->getNode(node->getStringValue(), true); + } + bool dirty() { return value >= 0.0; } + bool live() { return value_prop || factor_prop || offset_prop; } + float getValue() + { + if (value_prop) + value = value_prop->getFloatValue(); + if (offset_prop) + offset = offset_prop->getFloatValue(); + if (factor_prop) + factor = factor_prop->getFloatValue(); + return SGMiscf::clip(value*factor + offset, min, max); + } + float getInitialValue() + { + return SGMiscf::clip(value*factor + offset, min, max); + } +}; + +class SGMaterialAnimation::MaterialVisitor : public osg::NodeVisitor { +public: + enum { + DIFFUSE = 1, + AMBIENT = 2, + SPECULAR = 4, + EMISSION = 8, + SHININESS = 16, + TRANSPARENCY = 32 + }; + + MaterialVisitor() : + osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), + _updateMask(0), + _ambient(-1, -1, -1, -1), + _diffuse(-1, -1, -1, -1), + _specular(-1, -1, -1, -1), + _emission(-1, -1, -1, -1), + _shininess(-1), + _alpha(-1) + { + setVisitorType(osg::NodeVisitor::NODE_VISITOR); + } + + void setDiffuse(const SGVec4f& diffuse) + { + if (diffuse != _diffuse) + _diffuse = diffuse; + _updateMask |= DIFFUSE; + } + void setAmbient(const SGVec4f& ambient) + { + if (ambient != _ambient) + _ambient = ambient; + _updateMask |= AMBIENT; + } + void setSpecular(const SGVec4f& specular) + { + if (specular != _specular) + _specular = specular; + _updateMask |= SPECULAR; + } + void setEmission(const SGVec4f& emission) + { + if (emission != _emission) + _emission = emission; + _updateMask |= EMISSION; + } + void setShininess(float shininess) + { + if (shininess != _shininess) + _shininess = shininess; + _updateMask |= SHININESS; + } + + void setAlpha(float alpha) + { + if (alpha != _alpha) + _alpha = alpha; + _updateMask |= TRANSPARENCY; + } + + virtual void reset() + { + _updateMask = 0; + } + virtual void apply(osg::Node& node) + { + updateStateSet(node.getStateSet()); + traverse(node); + } + virtual void apply(osg::Geode& node) + { + apply((osg::Node&)node); + unsigned nDrawables = node.getNumDrawables(); + for (unsigned i = 0; i < nDrawables; ++i) { + osg::Drawable* drawable = node.getDrawable(i); + updateStateSet(drawable->getStateSet()); + + if (_updateMask&TRANSPARENCY) { + osg::Geometry* geometry = drawable->asGeometry(); + if (!geometry) + continue; + osg::Array* array = geometry->getColorArray(); + if (!array) + continue; + osg::Vec4Array* vec4Array = dynamic_cast(array); + if (!vec4Array) + continue; + + // FIXME, according to the colormode in the material + // we might incorporate the apropriate color value + geometry->dirtyDisplayList(); + vec4Array->dirty(); + for (unsigned k = 0; k < vec4Array->size(); ++k) { + (*vec4Array)[k][3] = _alpha; + } + } + } + } + void updateStateSet(osg::StateSet* stateSet) + { + if (!stateSet) + return; + osg::StateAttribute* stateAttribute; + stateAttribute = stateSet->getAttribute(osg::StateAttribute::MATERIAL); + if (!stateAttribute) + return; + osg::Material* material = dynamic_cast(stateAttribute); + if (!material) + return; + if (_updateMask&AMBIENT) + material->setAmbient(osg::Material::FRONT_AND_BACK, _ambient.osg()); + if (_updateMask&DIFFUSE) + material->setDiffuse(osg::Material::FRONT_AND_BACK, _diffuse.osg()); + if (_updateMask&SPECULAR) + material->setSpecular(osg::Material::FRONT_AND_BACK, _specular.osg()); + if (_updateMask&EMISSION) + material->setEmission(osg::Material::FRONT_AND_BACK, _emission.osg()); + if (_updateMask&SHININESS) + material->setShininess(osg::Material::FRONT_AND_BACK, _shininess); + if (_updateMask&TRANSPARENCY) { + material->setAlpha(osg::Material::FRONT_AND_BACK, _alpha); + if (_alpha < 1) { + stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); + stateSet->setMode(GL_BLEND, osg::StateAttribute::ON); + } else { + stateSet->setRenderingHint(osg::StateSet::DEFAULT_BIN); + } + } + } +private: + unsigned _updateMask; + SGVec4f _ambient; + SGVec4f _diffuse; + SGVec4f _specular; + SGVec4f _emission; + float _shininess; + float _alpha; +}; + +class SGMaterialAnimation::UpdateCallback : public osg::NodeCallback { +public: + UpdateCallback(const osgDB::FilePathList& texturePathList, + const SGCondition* condition, + const SGPropertyNode* configNode, SGPropertyNode* modelRoot) : + _condition(condition), + _ambient(configNode->getChild("ambient"), modelRoot), + _diffuse(configNode->getChild("diffuse"), modelRoot), + _specular(configNode->getChild("specular"), modelRoot), + _emission(configNode->getChild("emission"), modelRoot), + _shininess("shininess", "shininess-prop", + configNode->getChild("shininess"), modelRoot), + _transparency("alpha", "alpha-prop", + configNode->getChild("transparency"), modelRoot), + _texturePathList(texturePathList) + { + const SGPropertyNode* node; + + node = configNode->getChild("threshold-prop"); + if (node) + _thresholdProp = modelRoot->getNode(node->getStringValue(), true); + node = configNode->getChild("texture-prop"); + if (node) + _textureProp = modelRoot->getNode(node->getStringValue(), true); + } + + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + if (!_condition || _condition->test()) { + if (_textureProp) { + std::string textureName = _textureProp->getStringValue(); + if (_textureName != textureName) { + osg::StateSet* stateSet = node->getOrCreateStateSet(); + while (stateSet->getTextureAttribute(0, osg::StateAttribute::TEXTURE)) { + stateSet->removeTextureAttribute(0, osg::StateAttribute::TEXTURE); + } + std::string textureFile; + textureFile = osgDB::findFileInPath(textureName, _texturePathList); + if (!textureFile.empty()) { + osg::Texture2D* texture2D = SGLoadTexture2D(textureFile); + if (texture2D) { + stateSet->setTextureAttribute(0, texture2D); + stateSet->setTextureMode(0, GL_TEXTURE_2D, + osg::StateAttribute::ON); + _textureName = textureName; + } + } + } + } + if (_thresholdProp) { + osg::StateSet* stateSet = node->getOrCreateStateSet(); + osg::StateAttribute* stateAttribute; + stateAttribute = stateSet->getAttribute(osg::StateAttribute::ALPHAFUNC); + assert(dynamic_cast(stateAttribute)); + osg::AlphaFunc* alphaFunc = static_cast(stateAttribute); + alphaFunc->setReferenceValue(_thresholdProp->getFloatValue()); + } + + _visitor.reset(); + if (_ambient.live()) + _visitor.setAmbient(_ambient.rgba()); + if (_diffuse.live()) + _visitor.setDiffuse(_diffuse.rgba()); + if (_specular.live()) + _visitor.setSpecular(_specular.rgba()); + if (_emission.live()) + _visitor.setEmission(_emission.rgba()); + if (_shininess.live()) + _visitor.setShininess(_shininess.getValue()); + if (_transparency.live()) + _visitor.setAlpha(_transparency.getValue()); + + node->accept(_visitor); + } + + traverse(node, nv); + } +private: + SGSharedPtr _condition; + SGSharedPtr _textureProp; + SGSharedPtr _thresholdProp; + MaterialVisitor _visitor; + std::string _textureName; + ColorSpec _ambient; + ColorSpec _diffuse; + ColorSpec _specular; + ColorSpec _emission; + PropSpec _shininess; + PropSpec _transparency; + osgDB::FilePathList _texturePathList; +}; + +SGMaterialAnimation::SGMaterialAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot) : + SGAnimation(configNode, modelRoot) +{ + if (configNode->hasChild("global")) + SG_LOG(SG_IO, SG_ALERT, "Using global material animation that can " + "no longer work"); +} + +osg::Group* +SGMaterialAnimation::createAnimationGroup(osg::Group& parent) +{ + osg::Group* group = new osg::Group; + group->setName("material animation group"); + + SGPropertyNode* inputRoot = getModelRoot(); + const SGPropertyNode* node = getConfig()->getChild("property-base"); + if (node) + inputRoot = getModelRoot()->getRootNode()->getNode(node->getStringValue(), + true); + + osgDB::FilePathList texturePathList = osgDB::getDataFilePathList(); + + if (getConfig()->hasChild("texture")) { + std::string textureName = getConfig()->getStringValue("texture"); + std::string textureFile; + textureFile = osgDB::findFileInPath(textureName, texturePathList); + if (!textureFile.empty()) { + osg::StateSet* stateSet = group->getOrCreateStateSet(); + osg::Texture2D* texture2D = SGLoadTexture2D(textureFile); + if (texture2D) { + stateSet->setTextureAttribute(0, texture2D); + stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::ON); + if (texture2D->getImage()->isImageTranslucent()) { + stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); + stateSet->setMode(GL_BLEND, osg::StateAttribute::ON); + } + } + } + } + if (getConfig()->hasChild("threshold-prop") || + getConfig()->hasChild("threshold")) { + osg::StateSet* stateSet = group->getOrCreateStateSet(); + osg::AlphaFunc* alphaFunc = new osg::AlphaFunc; + alphaFunc->setFunction(osg::AlphaFunc::GREATER); + float threshold = getConfig()->getFloatValue("threshold", 0); + alphaFunc->setReferenceValue(threshold); + stateSet->setAttributeAndModes(alphaFunc); + } + + UpdateCallback* updateCallback; + updateCallback = new UpdateCallback(texturePathList, getCondition(), + getConfig(), inputRoot); + group->setUpdateCallback(updateCallback); + parent.addChild(group); + return group; +} + +void +SGMaterialAnimation::install(osg::Node& node) +{ + SGAnimation::install(node); + // make sure everything (except the texture attributes) + // below is private to our model + cloneDrawables(node); + + // Remove all textures if required, they get replaced later on + if (getConfig()->hasChild("texture") || + getConfig()->hasChild("texture-prop")) { + removeTextureAttribute(node, 0, osg::StateAttribute::TEXTURE); + removeTextureMode(node, 0, GL_TEXTURE_2D); + } + // Remove all nested alphaFuncs + if (getConfig()->hasChild("threshold") || + getConfig()->hasChild("threshold-prop")) + removeAttribute(node, osg::StateAttribute::ALPHAFUNC); + + ColorSpec ambient(getConfig()->getChild("ambient"), getModelRoot()); + ColorSpec diffuse(getConfig()->getChild("diffuse"), getModelRoot()); + ColorSpec specular(getConfig()->getChild("specular"), getModelRoot()); + ColorSpec emission(getConfig()->getChild("emission"), getModelRoot()); + PropSpec shininess("shininess", "shininess-prop", + getConfig()->getChild("shininess"), getModelRoot()); + PropSpec transparency("alpha", "alpha-prop", + getConfig()->getChild("transparency"), getModelRoot()); + + MaterialVisitor visitor; + if (ambient.dirty()) + visitor.setAmbient(ambient.initialRgba()); + if (diffuse.dirty()) + visitor.setDiffuse(diffuse.initialRgba()); + if (specular.dirty()) + visitor.setSpecular(specular.initialRgba()); + if (emission.dirty()) + visitor.setEmission(emission.initialRgba()); + if (shininess.dirty()) + visitor.setShininess(shininess.getInitialValue()); + if (transparency.dirty()) + visitor.setAlpha(transparency.getInitialValue()); + node.accept(visitor); +} + diff --git a/simgear/scene/model/SGMaterialAnimation.hxx b/simgear/scene/model/SGMaterialAnimation.hxx new file mode 100644 index 00000000..808858e2 --- /dev/null +++ b/simgear/scene/model/SGMaterialAnimation.hxx @@ -0,0 +1,33 @@ + +// animation.hxx - classes to manage model animation. +// Written by David Megginson, started 2002. +// +// This file is in the Public Domain, and comes with no warranty. + +#ifndef _SG_MATERIALANIMATION_HXX +#define _SG_MATERIALANIMATION_HXX 1 + +#ifndef __cplusplus +# error This library requires C++ +#endif + +#include "animation.hxx" + +////////////////////////////////////////////////////////////////////// +// Material animation +////////////////////////////////////////////////////////////////////// + +class SGMaterialAnimation : public SGAnimation { +public: + SGMaterialAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot); + virtual osg::Group* createAnimationGroup(osg::Group& parent); + virtual void install(osg::Node& node); +private: + struct ColorSpec; + struct PropSpec; + class MaterialVisitor; + class UpdateCallback; +}; + +#endif // _SG_MATERIALANIMATION_HXX diff --git a/simgear/scene/model/animation.cxx b/simgear/scene/model/animation.cxx index e11a580e..e9aa3433 100644 --- a/simgear/scene/model/animation.cxx +++ b/simgear/scene/model/animation.cxx @@ -11,27 +11,28 @@ #include #include -#include #include #include #include #include -#include #include #include #include #include +#include #include #include #include #include -#include #include +#include #include "animation.hxx" #include "model.hxx" +#include "SGMaterialAnimation.hxx" + //////////////////////////////////////////////////////////////////////// // Static utility functions. @@ -42,71 +43,59 @@ */ static void set_rotation (osg::Matrix &matrix, double position_deg, - const osg::Vec3 ¢er, const osg::Vec3 &axis) + const SGVec3d ¢er, const SGVec3d &axis) { - float temp_angle = -position_deg * SG_DEGREES_TO_RADIANS ; - - float s = (float) sin ( temp_angle ) ; - float c = (float) cos ( temp_angle ) ; - float t = SG_ONE - c ; - - // axis was normalized at load time - // hint to the compiler to put these into FP registers - float x = axis[0]; - float y = axis[1]; - float z = axis[2]; - - matrix(0, 0) = t * x * x + c ; - matrix(0, 1) = t * y * x - s * z ; - matrix(0, 2) = t * z * x + s * y ; - matrix(0, 3) = SG_ZERO; - - matrix(1, 0) = t * x * y + s * z ; - matrix(1, 1) = t * y * y + c ; - matrix(1, 2) = t * z * y - s * x ; - matrix(1, 3) = SG_ZERO; - - matrix(2, 0) = t * x * z - s * y ; - matrix(2, 1) = t * y * z + s * x ; - matrix(2, 2) = t * z * z + c ; - matrix(2, 3) = SG_ZERO; - + double temp_angle = -SGMiscd::deg2rad(position_deg); + + double s = sin(temp_angle); + double c = cos(temp_angle); + double t = 1 - c; + + // axis was normalized at load time + // hint to the compiler to put these into FP registers + double x = axis[0]; + double y = axis[1]; + double z = axis[2]; + + matrix(0, 0) = t * x * x + c ; + matrix(0, 1) = t * y * x - s * z ; + matrix(0, 2) = t * z * x + s * y ; + matrix(0, 3) = 0; + + matrix(1, 0) = t * x * y + s * z ; + matrix(1, 1) = t * y * y + c ; + matrix(1, 2) = t * z * y - s * x ; + matrix(1, 3) = 0; + + matrix(2, 0) = t * x * z - s * y ; + matrix(2, 1) = t * y * z + s * x ; + matrix(2, 2) = t * z * z + c ; + matrix(2, 3) = 0; + // hint to the compiler to put these into FP registers - x = center[0]; - y = center[1]; - z = center[2]; - - matrix(3, 0) = x - x*matrix(0, 0) - y*matrix(1, 0) - z*matrix(2, 0); - matrix(3, 1) = y - x*matrix(0, 1) - y*matrix(1, 1) - z*matrix(2, 1); - matrix(3, 2) = z - x*matrix(0, 2) - y*matrix(1, 2) - z*matrix(2, 2); - matrix(3, 3) = SG_ONE; + x = center[0]; + y = center[1]; + z = center[2]; + + matrix(3, 0) = x - x*matrix(0, 0) - y*matrix(1, 0) - z*matrix(2, 0); + matrix(3, 1) = y - x*matrix(0, 1) - y*matrix(1, 1) - z*matrix(2, 1); + matrix(3, 2) = z - x*matrix(0, 2) - y*matrix(1, 2) - z*matrix(2, 2); + matrix(3, 3) = 1; } /** * Set up the transform matrix for a translation. */ static void -set_translation (osg::Matrix &matrix, double position_m, const osg::Vec3 &axis) +set_translation (osg::Matrix &matrix, double position_m, const SGVec3d &axis) { - osg::Vec3 xyz = axis * position_m; + SGVec3d xyz = axis * position_m; matrix.makeIdentity(); matrix(3, 0) = xyz[0]; matrix(3, 1) = xyz[1]; matrix(3, 2) = xyz[2]; } -/** - * Set up the transform matrix for a scale operation. - */ -static void -set_scale (osg::Matrix &matrix, double x, double y, double z) -{ - matrix.makeIdentity(); - matrix(0, 0) = x; - matrix(1, 1) = y; - matrix(2, 2) = z; -} - /** * Modify property value by step and scroll settings in texture translations */ @@ -140,1338 +129,1148 @@ apply_mods(double property, double step, double scroll) * Read an interpolation table from properties. */ static SGInterpTable * -read_interpolation_table (SGPropertyNode_ptr props) +read_interpolation_table(const SGPropertyNode* props) { - SGPropertyNode_ptr table_node = props->getNode("interpolation"); - if (table_node != 0) { - SGInterpTable * table = new SGInterpTable(); - vector entries = table_node->getChildren("entry"); - for (unsigned int i = 0; i < entries.size(); i++) - table->addEntry(entries[i]->getDoubleValue("ind", 0.0), - entries[i]->getDoubleValue("dep", 0.0)); - return table; - } else { + const SGPropertyNode* table_node = props->getNode("interpolation"); + if (!table_node) return 0; - } + return new SGInterpTable(table_node); } - - //////////////////////////////////////////////////////////////////////// -// Implementation of SGAnimation +// Utility value classes //////////////////////////////////////////////////////////////////////// +class SGScaleOffsetValue : public SGDoubleValue { +public: + SGScaleOffsetValue(SGPropertyNode const* propertyNode) : + _propertyNode(propertyNode), + _scale(1), + _offset(0), + _min(-SGLimitsd::max()), + _max(SGLimitsd::max()) + { } + void setScale(double scale) + { _scale = scale; } + void setOffset(double offset) + { _offset = offset; } + void setMin(double min) + { _min = min; } + void setMax(double max) + { _max = max; } + + virtual double getValue() const + { + double value = _propertyNode ? _propertyNode->getDoubleValue() : 0; + return std::min(_max, std::max(_min, _offset + _scale*value)); + } +private: + SGSharedPtr _propertyNode; + double _scale; + double _offset; + double _min; + double _max; +}; -SGAnimation::SGAnimation (SGPropertyNode_ptr props, osg::Group * branch) - : _branch(branch), - animation_type(0) -{ - _branch->setName(props->getStringValue("name", "Animation")); - if ( props->getBoolValue( "enable-hot", true ) ) { - _branch->setNodeMask(SG_NODEMASK_TERRAIN_BIT|_branch->getNodeMask()); - } else { - _branch->setNodeMask(~SG_NODEMASK_TERRAIN_BIT&_branch->getNodeMask()); - } -} +class SGPersScaleOffsetValue : public SGDoubleValue { +public: + SGPersScaleOffsetValue(SGPropertyNode const* propertyNode, + SGPropertyNode const* config, + const char* scalename, const char* offsetname, + double defScale = 1, double defOffset = 0) : + _propertyNode(propertyNode), + _scale(config, scalename, defScale), + _offset(config, offsetname, defOffset), + _min(-SGLimitsd::max()), + _max(SGLimitsd::max()) + { } + void setScale(double scale) + { _scale = scale; } + void setOffset(double offset) + { _offset = offset; } + void setMin(double min) + { _min = min; } + void setMax(double max) + { _max = max; } + + virtual double getValue() const + { + _offset.shuffle(); + _scale.shuffle(); + double value = _propertyNode ? _propertyNode->getDoubleValue() : 0; + return SGMiscd::clip(_offset + _scale*value, _min, _max); + } +private: + SGSharedPtr _propertyNode; + mutable SGPersonalityParameter _scale; + mutable SGPersonalityParameter _offset; + double _min; + double _max; +}; -SGAnimation::~SGAnimation () -{ -} +class SGInterpTableValue : public SGDoubleValue { +public: + SGInterpTableValue(SGPropertyNode const* propertyNode, + SGInterpTable const* interpTable) : + _propertyNode(propertyNode), + _interpTable(interpTable) + { } + virtual double getValue() const + { return _interpTable->interpolate(_propertyNode->getDoubleValue()); } +private: + SGSharedPtr _propertyNode; + SGSharedPtr _interpTable; +}; -void -SGAnimation::init () -{ -} +class SGTexScaleOffsetValue : public SGDoubleValue { +public: + SGTexScaleOffsetValue(const SGPropertyNode* propertyNode) : + _propertyNode(propertyNode), + _scale(1), + _offset(0), + _step(0), + _scroll(0), + _min(-SGLimitsd::max()), + _max(SGLimitsd::max()) + { } + void setScale(double scale) + { _scale = scale; } + void setOffset(double offset) + { _offset = offset; } + void setStep(double step) + { _step = step; } + void setScroll(double scroll) + { _scroll = scroll; } + void setMin(double min) + { _min = min; } + void setMax(double max) + { _max = max; } + + virtual double getValue() const + { + double value = _propertyNode ? _propertyNode->getDoubleValue() : 0; + value = apply_mods(value, _step, _scroll); + return SGMiscd::clip(_scale*(_offset + value), _min, _max); + } +private: + SGSharedPtr _propertyNode; + double _scale; + double _offset; + double _step; + double _scroll; + double _min; + double _max; +}; -void -SGAnimation::restore() +class SGTexTableValue : public SGDoubleValue { +public: + SGTexTableValue(const SGPropertyNode* propertyNode, + const SGInterpTable* interpTable) : + _propertyNode(propertyNode), + _interpTable(interpTable) + { } + void setStep(double step) + { _step = step; } + void setScroll(double scroll) + { _scroll = scroll; } + virtual double getValue() const + { + double value = _propertyNode ? _propertyNode->getDoubleValue() : 0; + value = apply_mods(value, _step, _scroll); + return _interpTable->interpolate(value); + } +private: + SGSharedPtr _propertyNode; + SGSharedPtr _interpTable; + double _step; + double _scroll; +}; + +static std::string +unit_string(const char* value, const char* unit) { + return std::string(value) + unit; } -void -SGAnimation::operator()(osg::Node* node, osg::NodeVisitor* nv) +static SGDoubleValue* +read_value(const SGPropertyNode* configNode, SGPropertyNode* modelRoot, + const char* unit, double defMin, double defMax) { - traverse(node, nv); + std::string inputPropertyName; + inputPropertyName = configNode->getStringValue("property", ""); + if (!inputPropertyName.empty()) { + SGPropertyNode* inputProperty; + inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true); + SGInterpTable* interpTable = read_interpolation_table(configNode); + if (interpTable) { + SGInterpTableValue* value; + value = new SGInterpTableValue(inputProperty, interpTable); + return value; + } else { + std::string offset = unit_string("offset", unit); + std::string min = unit_string("min", unit); + std::string max = unit_string("max", unit); + + if (configNode->getBoolValue("use-personality", false)) { + SGPersScaleOffsetValue* value; + value = new SGPersScaleOffsetValue(inputProperty, configNode, + "factor", offset.c_str()); + value->setMin(configNode->getDoubleValue(min.c_str(), defMin)); + value->setMax(configNode->getDoubleValue(max.c_str(), defMax)); + return value; + } else { + SGScaleOffsetValue* value = new SGScaleOffsetValue(inputProperty); + value->setScale(configNode->getDoubleValue("factor", 1)); + value->setOffset(configNode->getDoubleValue(offset.c_str(), 0)); + value->setMin(configNode->getDoubleValue(min.c_str(), defMin)); + value->setMax(configNode->getDoubleValue(max.c_str(), defMax)); + return value; + } + } + } + return 0; } //////////////////////////////////////////////////////////////////////// -// Implementation of SGNullAnimation +// Animation installer //////////////////////////////////////////////////////////////////////// -SGNullAnimation::SGNullAnimation (SGPropertyNode_ptr props) - : SGAnimation(props, new osg::Group) -{ -} - -SGNullAnimation::~SGNullAnimation () -{ -} +class SGAnimation::RemoveModeVisitor : public SGStateAttributeVisitor { +public: + RemoveModeVisitor(osg::StateAttribute::GLMode mode) : + _mode(mode) + { } + virtual void apply(osg::StateSet* stateSet) + { + if (!stateSet) + return; + stateSet->removeMode(_mode); + } +private: + osg::StateAttribute::GLMode _mode; +}; +class SGAnimation::RemoveAttributeVisitor : public SGStateAttributeVisitor { +public: + RemoveAttributeVisitor(osg::StateAttribute::Type type) : + _type(type) + { } + virtual void apply(osg::StateSet* stateSet) + { + if (!stateSet) + return; + while (stateSet->getAttribute(_type)) { + stateSet->removeAttribute(_type); + } + } +private: + osg::StateAttribute::Type _type; +}; - -//////////////////////////////////////////////////////////////////////// -// Implementation of SGRangeAnimation -//////////////////////////////////////////////////////////////////////// +class SGAnimation::RemoveTextureModeVisitor : public SGStateAttributeVisitor { +public: + RemoveTextureModeVisitor(unsigned unit, osg::StateAttribute::GLMode mode) : + _unit(unit), + _mode(mode) + { } + virtual void apply(osg::StateSet* stateSet) + { + if (!stateSet) + return; + stateSet->removeTextureMode(_unit, _mode); + } +private: + unsigned _unit; + osg::StateAttribute::GLMode _mode; +}; -SGRangeAnimation::SGRangeAnimation (SGPropertyNode *prop_root, - SGPropertyNode_ptr props) - : SGAnimation(props, new osg::LOD), - _min(0.0), _max(0.0), _min_factor(1.0), _max_factor(1.0), - _condition(0) -{ - SGPropertyNode_ptr node = props->getChild("condition"); - if (node != 0) - _condition = sgReadCondition(prop_root, node); +class SGAnimation::RemoveTextureAttributeVisitor : + public SGStateAttributeVisitor { +public: + RemoveTextureAttributeVisitor(unsigned unit, + osg::StateAttribute::Type type) : + _unit(unit), + _type(type) + { } + virtual void apply(osg::StateSet* stateSet) + { + if (!stateSet) + return; + while (stateSet->getTextureAttribute(_unit, _type)) { + stateSet->removeTextureAttribute(_unit, _type); + } + } +private: + unsigned _unit; + osg::StateAttribute::Type _type; +}; - float ranges[2]; +class SGAnimation::BinToInheritVisitor : public SGStateAttributeVisitor { +public: + virtual void apply(osg::StateSet* stateSet) + { + if (!stateSet) + return; + stateSet->setRenderBinToInherit(); + } +}; - node = props->getChild( "min-factor" ); - if (node != 0) { - _min_factor = props->getFloatValue("min-factor", 1.0); - } - node = props->getChild( "max-factor" ); - if (node != 0) { - _max_factor = props->getFloatValue("max-factor", 1.0); - } - node = props->getChild( "min-property" ); - if (node != 0) { - _min_prop = (SGPropertyNode *)prop_root->getNode(node->getStringValue(), true); - ranges[0] = _min_prop->getFloatValue() * _min_factor; - } else { - _min = props->getFloatValue("min-m", 0); - ranges[0] = _min * _min_factor; - } - node = props->getChild( "max-property" ); - if (node != 0) { - _max_prop = (SGPropertyNode *)prop_root->getNode(node->getStringValue(), true); - ranges[1] = _max_prop->getFloatValue() * _max_factor; - } else { - _max = props->getFloatValue("max-m", 0); - ranges[1] = _max * _max_factor; +class SGAnimation::DrawableCloneVisitor : public osg::NodeVisitor { +public: + DrawableCloneVisitor() : + osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) + {} + void apply(osg::Geode& geode) + { + for (unsigned i = 0 ; i < geode.getNumDrawables(); ++i) { + osg::CopyOp copyOp(osg::CopyOp::DEEP_COPY_ALL & + ~osg::CopyOp::DEEP_COPY_TEXTURES); + geode.setDrawable(i, copyOp(geode.getDrawable(i))); } - static_cast(_branch)->setRange(0, ranges[0], ranges[1]); -} + } +}; -SGRangeAnimation::~SGRangeAnimation () -{ -} +SGAnimation::SGAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot) : + osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), + _found(false), + _configNode(configNode), + _modelRoot(modelRoot) +{ + _name = configNode->getStringValue("name", ""); + _enableHOT = configNode->getBoolValue("enable-hot", true); + _disableShadow = configNode->getBoolValue("disable-shadow", false); + std::vector objectNames = + configNode->getChildren("object-name"); + for (unsigned i = 0; i < objectNames.size(); ++i) + _objectNames.push_back(objectNames[i]->getStringValue()); +} + +SGAnimation::~SGAnimation() +{ + if (_found) + return; + + SG_LOG(SG_IO, SG_ALERT, "Could not find at least one of the following" + " objects for animation:\n"); + std::list::const_iterator i; + for (i = _objectNames.begin(); i != _objectNames.end(); ++i) + SG_LOG(SG_IO, SG_ALERT, *i << "\n"); +} + +bool +SGAnimation::animate(osg::Node* node, const SGPropertyNode* configNode, + SGPropertyNode* modelRoot) +{ + std::string type = configNode->getStringValue("type", "none"); + if (type == "alpha-test") { + SGAlphaTestAnimation animInst(configNode, modelRoot); + animInst.apply(node); + } else if (type == "billboard") { + SGBillboardAnimation animInst(configNode, modelRoot); + animInst.apply(node); + } else if (type == "blend") { + SGBlendAnimation animInst(configNode, modelRoot); + animInst.apply(node); + } else if (type == "dist-scale") { + SGDistScaleAnimation animInst(configNode, modelRoot); + animInst.apply(node); + } else if (type == "flash") { + SGFlashAnimation animInst(configNode, modelRoot); + animInst.apply(node); + } else if (type == "material") { + SGMaterialAnimation animInst(configNode, modelRoot); + animInst.apply(node); + } else if (type == "noshadow") { + SGShadowAnimation animInst(configNode, modelRoot); + animInst.apply(node); + } else if (type == "range") { + SGRangeAnimation animInst(configNode, modelRoot); + animInst.apply(node); + } else if (type == "rotate" || type == "spin") { + SGRotateAnimation animInst(configNode, modelRoot); + animInst.apply(node); + } else if (type == "scale") { + SGScaleAnimation animInst(configNode, modelRoot); + animInst.apply(node); + } else if (type == "select") { + SGSelectAnimation animInst(configNode, modelRoot); + animInst.apply(node); + } else if (type == "shader") { + SGShaderAnimation animInst(configNode, modelRoot); + animInst.apply(node); + } else if (type == "textranslate" || type == "texrotate" || + type == "texmultiple") { + SGTexTransformAnimation animInst(configNode, modelRoot); + animInst.apply(node); + } else if (type == "timed") { + SGTimedAnimation animInst(configNode, modelRoot); + animInst.apply(node); + } else if (type == "translate") { + SGTranslateAnimation animInst(configNode, modelRoot); + animInst.apply(node); + } else if (type == "null" || type == "none" || type.empty()) { + SGGroupAnimation animInst(configNode, modelRoot); + animInst.apply(node); + } else + return false; + + return true; +} + + void -SGRangeAnimation::operator()(osg::Node* node, osg::NodeVisitor* nv) +SGAnimation::apply(osg::Node* node) { - float ranges[2]; - if ( _condition == 0 || _condition->test() ) { - if (_min_prop != 0) { - ranges[0] = _min_prop->getFloatValue() * _min_factor; - } else { - ranges[0] = _min * _min_factor; - } - if (_max_prop != 0) { - ranges[1] = _max_prop->getFloatValue() * _max_factor; - } else { - ranges[1] = _max * _max_factor; + // duh what a special case ... + if (_objectNames.empty()) { + osg::Group* group = node->asGroup(); + if (group) { + osg::ref_ptr animationGroup; + installInGroup(std::string(), *group, animationGroup); } - } else { - ranges[0] = 0.f; - ranges[1] = 1000000000.f; - } - static_cast(_branch)->setRange(0, ranges[0], ranges[1]); - traverse(node, nv); + } else + node->accept(*this); } - - -//////////////////////////////////////////////////////////////////////// -// Implementation of SGBillboardAnimation -//////////////////////////////////////////////////////////////////////// - -SGBillboardAnimation::SGBillboardAnimation (SGPropertyNode_ptr props) - : SGAnimation(props, new osg::AutoTransform) +void +SGAnimation::install(osg::Node& node) { -//OSGFIXME: verify - bool spherical = props->getBoolValue("spherical", true); - osg::AutoTransform* autoTrans = static_cast(_branch); - if (spherical) { - autoTrans->setAutoRotateMode(osg::AutoTransform::ROTATE_TO_SCREEN); - } else { - autoTrans->setAutoRotateMode(osg::AutoTransform::NO_ROTATION); - autoTrans->setReferenceFrame(osg::Transform::ABSOLUTE_RF); - } - autoTrans->setAutoScaleToScreen(false); + _found = true; + if (_enableHOT) + node.setNodeMask( SG_NODEMASK_TERRAIN_BIT | node.getNodeMask()); + else + node.setNodeMask(~SG_NODEMASK_TERRAIN_BIT & node.getNodeMask()); + if (!_disableShadow) + node.setNodeMask( SG_NODEMASK_SHADOW_BIT | node.getNodeMask()); + else + node.setNodeMask(~SG_NODEMASK_SHADOW_BIT & node.getNodeMask()); } -SGBillboardAnimation::~SGBillboardAnimation () +osg::Group* +SGAnimation::createAnimationGroup(osg::Group& parent) { + // default implementation, we do not need a new group + // for every animation type. Usually animations that just change + // the StateSet of some parts of the model + return 0; } - - -//////////////////////////////////////////////////////////////////////// -// Implementation of SGSelectAnimation -//////////////////////////////////////////////////////////////////////// - -SGSelectAnimation::SGSelectAnimation( SGPropertyNode *prop_root, - SGPropertyNode_ptr props ) - : SGAnimation(props, new osg::Switch), - _condition(0) +void +SGAnimation::apply(osg::Group& group) { - SGPropertyNode_ptr node = props->getChild("condition"); - if (node != 0) - _condition = sgReadCondition(prop_root, node); -} + // the trick is to first traverse the children and then + // possibly splice in a new group node if required. + // Else we end up in a recursive loop where we infinitly insert new + // groups in between + traverse(group); -SGSelectAnimation::~SGSelectAnimation () -{ + // Note that this algorithm preserves the order of the child objects + // like they appear in the object-name tags. + // The timed animations require this + osg::ref_ptr animationGroup; + std::list::const_iterator nameIt; + for (nameIt = _objectNames.begin(); nameIt != _objectNames.end(); ++nameIt) + installInGroup(*nameIt, group, animationGroup); } void -SGSelectAnimation::operator()(osg::Node* node, osg::NodeVisitor* nv) -{ - if (_condition != 0 && _condition->test()) - static_cast(_branch)->setAllChildrenOn(); - else - static_cast(_branch)->setAllChildrenOff(); - traverse(node, nv); +SGAnimation::installInGroup(const std::string& name, osg::Group& group, + osg::ref_ptr& animationGroup) +{ + int i = group.getNumChildren() - 1; + for (; 0 <= i; --i) { + osg::Node* child = group.getChild(i); + if (name.empty() || child->getName() == name) { + // fire the installation of the animation + install(*child); + + // create a group node on demand + if (!animationGroup.valid()) { + animationGroup = createAnimationGroup(group); + // Animation type that does not require a new group, + // in this case we can stop and look for the next object + if (animationGroup.valid() && !_name.empty()) + animationGroup->setName(_name); + } + if (animationGroup.valid()) { + animationGroup->addChild(child); + group.removeChild(i); + } + } + } } - -//////////////////////////////////////////////////////////////////////// -// Implementation of SGSpinAnimation -//////////////////////////////////////////////////////////////////////// - -SGSpinAnimation::SGSpinAnimation( SGPropertyNode *prop_root, - SGPropertyNode_ptr props, - double sim_time_sec ) - : SGAnimation(props, new osg::MatrixTransform), - _use_personality( props->getBoolValue("use-personality",false) ), - _prop((SGPropertyNode *)prop_root->getNode(props->getStringValue("property", "/null"), true)), - _factor( props, "factor", 1.0 ), - _position_deg( props, "starting-position-deg", 0.0 ), - _last_time_sec( sim_time_sec ), - _condition(0) +void +SGAnimation::removeMode(osg::Node& node, osg::StateAttribute::GLMode mode) { - SGPropertyNode_ptr node = props->getChild("condition"); - if (node != 0) - _condition = sgReadCondition(prop_root, node); - - _center[0] = 0; - _center[1] = 0; - _center[2] = 0; - if (props->hasValue("axis/x1-m")) { - double x1,y1,z1,x2,y2,z2; - x1 = props->getFloatValue("axis/x1-m"); - y1 = props->getFloatValue("axis/y1-m"); - z1 = props->getFloatValue("axis/z1-m"); - x2 = props->getFloatValue("axis/x2-m"); - y2 = props->getFloatValue("axis/y2-m"); - z2 = props->getFloatValue("axis/z2-m"); - _center[0] = (x1+x2)/2; - _center[1]= (y1+y2)/2; - _center[2] = (z1+z2)/2; - float vector_length = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) + (z2-z1)*(z2-z1)); - _axis[0] = (x2-x1)/vector_length; - _axis[1] = (y2-y1)/vector_length; - _axis[2] = (z2-z1)/vector_length; - } else { - _axis[0] = props->getFloatValue("axis/x", 0); - _axis[1] = props->getFloatValue("axis/y", 0); - _axis[2] = props->getFloatValue("axis/z", 0); - } - if (props->hasValue("center/x-m")) { - _center[0] = props->getFloatValue("center/x-m", 0); - _center[1] = props->getFloatValue("center/y-m", 0); - _center[2] = props->getFloatValue("center/z-m", 0); - } - - _axis.normalize(); - - if ( _use_personality ) { - _factor.shuffle(); - _position_deg.shuffle(); - } + RemoveModeVisitor visitor(mode); + node.accept(visitor); } -SGSpinAnimation::~SGSpinAnimation () +void +SGAnimation::removeAttribute(osg::Node& node, osg::StateAttribute::Type type) { + RemoveAttributeVisitor visitor(type); + node.accept(visitor); } void -SGSpinAnimation::operator()(osg::Node* node, osg::NodeVisitor* nv) +SGAnimation::removeTextureMode(osg::Node& node, unsigned unit, + osg::StateAttribute::GLMode mode) { - double sim_time_sec = nv->getFrameStamp()->getReferenceTime(); - if ( _condition == 0 || _condition->test() ) { - double dt; - float velocity_rpms; - dt = sim_time_sec - _last_time_sec; - _last_time_sec = sim_time_sec; - - velocity_rpms = (_prop->getDoubleValue() * _factor / 60.0); - _position_deg += (dt * velocity_rpms * 360); - _position_deg -= 360*floor(_position_deg/360); - - osg::Matrix _matrix; - set_rotation(_matrix, _position_deg, _center, _axis); - static_cast(_branch)->setMatrix(_matrix); - } - traverse(node, nv); + RemoveTextureModeVisitor visitor(unit, mode); + node.accept(visitor); } - - -//////////////////////////////////////////////////////////////////////// -// Implementation of SGTimedAnimation -//////////////////////////////////////////////////////////////////////// - -SGTimedAnimation::SGTimedAnimation (SGPropertyNode_ptr props) - : SGAnimation(props, new osg::Switch), - _use_personality( props->getBoolValue("use-personality",false) ), - _duration_sec(props->getDoubleValue("duration-sec", 1.0)), - _last_time_sec( 0 ), - _total_duration_sec( 0 ), - _step( 0 ) - +void +SGAnimation::removeTextureAttribute(osg::Node& node, unsigned unit, + osg::StateAttribute::Type type) { - vector nodes = props->getChildren( "branch-duration-sec" ); - size_t nb = nodes.size(); - for ( size_t i = 0; i < nb; i++ ) { - size_t ind = nodes[ i ]->getIndex(); - while ( ind >= _branch_duration_specs.size() ) { - _branch_duration_specs.push_back( DurationSpec( _duration_sec ) ); - } - SGPropertyNode_ptr rNode = nodes[ i ]->getChild("random"); - if ( rNode == 0 ) { - _branch_duration_specs[ ind ] = DurationSpec( nodes[ i ]->getDoubleValue() ); - } else { - _branch_duration_specs[ ind ] = DurationSpec( rNode->getDoubleValue( "min", 0.0 ), - rNode->getDoubleValue( "max", 1.0 ) ); - } - } + RemoveTextureAttributeVisitor visitor(unit, type); + node.accept(visitor); } -SGTimedAnimation::~SGTimedAnimation () +void +SGAnimation::setRenderBinToInherit(osg::Node& node) { + BinToInheritVisitor visitor; + node.accept(visitor); } void -SGTimedAnimation::init() +SGAnimation::cloneDrawables(osg::Node& node) { - if ( !_use_personality ) { - for ( unsigned i = 0; i < getBranch()->getNumChildren(); i++ ) { - double v; - if ( i < _branch_duration_specs.size() ) { - DurationSpec &sp = _branch_duration_specs[ i ]; - v = sp._min + sg_random() * ( sp._max - sp._min ); - } else { - v = _duration_sec; - } - _branch_duration_sec.push_back( v ); - _total_duration_sec += v; - } - } - // Sanity check : total duration shouldn't equal zero - if (_duration_sec < 0.01) - _duration_sec = 0.01; - if ( _total_duration_sec < 0.01 ) - _total_duration_sec = 0.01; - - static_cast(getBranch())->setSingleChildOn(_step); + DrawableCloneVisitor visitor; + node.accept(visitor); } -void -SGTimedAnimation::operator()(osg::Node* node, osg::NodeVisitor* nv) +const SGCondition* +SGAnimation::getCondition() const { - double sim_time_sec = nv->getFrameStamp()->getReferenceTime(); - _last_time_sec -= _total_duration_sec*floor((sim_time_sec - _last_time_sec)/_total_duration_sec); - double duration = _duration_sec; - if ( _step < _branch_duration_sec.size() ) { - duration = _branch_duration_sec[ _step ]; - } - if ( ( sim_time_sec - _last_time_sec ) >= duration ) { - _last_time_sec += duration; - _step += 1; - if ( _step >= getBranch()->getNumChildren() ) - _step = 0; - static_cast(getBranch())->setSingleChildOn(_step); - } - traverse(node, nv); + const SGPropertyNode* conditionNode = _configNode->getChild("condition"); + if (!conditionNode) + return 0; + return sgReadCondition(_modelRoot, conditionNode); } //////////////////////////////////////////////////////////////////////// -// Implementation of SGRotateAnimation +// Implementation of null animation //////////////////////////////////////////////////////////////////////// -SGRotateAnimation::SGRotateAnimation( SGPropertyNode *prop_root, - SGPropertyNode_ptr props ) - : SGAnimation(props, new osg::MatrixTransform), - _prop((SGPropertyNode *)prop_root->getNode(props->getStringValue("property", "/null"), true)), - _offset_deg(props->getDoubleValue("offset-deg", 0.0)), - _factor(props->getDoubleValue("factor", 1.0)), - _table(read_interpolation_table(props)), - _has_min(props->hasValue("min-deg")), - _min_deg(props->getDoubleValue("min-deg")), - _has_max(props->hasValue("max-deg")), - _max_deg(props->getDoubleValue("max-deg")), - _position_deg(props->getDoubleValue("starting-position-deg", 0)), - _condition(0) -{ - SGPropertyNode_ptr node = props->getChild("condition"); - if (node != 0) - _condition = sgReadCondition(prop_root, node); - - _center[0] = 0; - _center[1] = 0; - _center[2] = 0; - if (props->hasValue("axis/x") || props->hasValue("axis/y") || props->hasValue("axis/z")) { - _axis[0] = props->getFloatValue("axis/x", 0); - _axis[1] = props->getFloatValue("axis/y", 0); - _axis[2] = props->getFloatValue("axis/z", 0); - } else { - double x1,y1,z1,x2,y2,z2; - x1 = props->getFloatValue("axis/x1-m", 0); - y1 = props->getFloatValue("axis/y1-m", 0); - z1 = props->getFloatValue("axis/z1-m", 0); - x2 = props->getFloatValue("axis/x2-m", 0); - y2 = props->getFloatValue("axis/y2-m", 0); - z2 = props->getFloatValue("axis/z2-m", 0); - _center[0] = (x1+x2)/2; - _center[1]= (y1+y2)/2; - _center[2] = (z1+z2)/2; - float vector_length = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) + (z2-z1)*(z2-z1)); - _axis[0] = (x2-x1)/vector_length; - _axis[1] = (y2-y1)/vector_length; - _axis[2] = (z2-z1)/vector_length; - } - if (props->hasValue("center/x-m") || props->hasValue("center/y-m") - || props->hasValue("center/z-m")) { - _center[0] = props->getFloatValue("center/x-m", 0); - _center[1] = props->getFloatValue("center/y-m", 0); - _center[2] = props->getFloatValue("center/z-m", 0); - } - _axis.normalize(); -} - -SGRotateAnimation::~SGRotateAnimation () +// Ok, that is to build a subgraph from different other +// graph nodes. I guess that this stems from the time where modellers +// could not build hierarchical trees ... +SGGroupAnimation::SGGroupAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot): + SGAnimation(configNode, modelRoot) { } -void -SGRotateAnimation::operator()(osg::Node* node, osg::NodeVisitor* nv) +osg::Group* +SGGroupAnimation::createAnimationGroup(osg::Group& parent) { - if (_condition == 0 || _condition->test()) { - if (_table == 0) { - _position_deg = _prop->getDoubleValue() * _factor + _offset_deg; - if (_has_min && _position_deg < _min_deg) - _position_deg = _min_deg; - if (_has_max && _position_deg > _max_deg) - _position_deg = _max_deg; - } else { - _position_deg = _table->interpolate(_prop->getDoubleValue()); - } - osg::Matrix _matrix; - set_rotation(_matrix, _position_deg, _center, _axis); - static_cast(_branch)->setMatrix(_matrix); - } - traverse(node, nv); + osg::Group* group = new osg::Group; + parent.addChild(group); + return group; } //////////////////////////////////////////////////////////////////////// -// Implementation of SGBlendAnimation +// Implementation of translate animation //////////////////////////////////////////////////////////////////////// -class SGBlendAnimationVisitor : public osg::NodeVisitor { +class SGTranslateAnimation::Transform : public osg::Transform { public: - SGBlendAnimationVisitor(float blend) : - osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), - _blend(blend) + Transform() : + _axis(0, 0, 0), + _value(0) + { setReferenceFrame(RELATIVE_RF); } + void setAxis(const SGVec3d& axis) + { _axis = axis; dirtyBound(); } + void setValue(double value) + { _value = value; dirtyBound(); } + virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix, + osg::NodeVisitor* nv) const { - setVisitorType(osg::NodeVisitor::NODE_VISITOR); + assert(_referenceFrame == RELATIVE_RF); + osg::Matrix tmp; + set_translation(tmp, _value, _axis); + matrix.preMult(tmp); + return true; } - virtual void apply(osg::Node& node) + virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix, + osg::NodeVisitor* nv) const { - updateStateSet(node.getStateSet()); - traverse(node); + assert(_referenceFrame == RELATIVE_RF); + osg::Matrix tmp; + set_translation(tmp, -_value, _axis); + matrix.postMult(tmp); + return true; } - virtual void apply(osg::Geode& node) +private: + SGVec3d _axis; + double _value; +}; + +class SGTranslateAnimation::UpdateCallback : public osg::NodeCallback { +public: + UpdateCallback(SGCondition const* condition, + SGDoubleValue const* animationValue) : + _condition(condition), + _animationValue(animationValue) + { } + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) { - apply((osg::Node&)node); - unsigned nDrawables = node.getNumDrawables(); - for (unsigned i = 0; i < nDrawables; ++i) { - osg::Drawable* drawable = node.getDrawable(i); - updateStateSet(drawable->getStateSet()); - osg::Geometry* geometry = drawable->asGeometry(); - if (!geometry) - continue; - osg::Array* array = geometry->getColorArray(); - if (!array) - continue; - osg::Vec4Array* vec4Array = dynamic_cast(array); - if (!vec4Array) - continue; - geometry->dirtyDisplayList(); - vec4Array->dirty(); - for (unsigned k = 0; k < vec4Array->size(); ++k) { - (*vec4Array)[k][3] = _blend; - } + if (!_condition || _condition->test()) { + SGTranslateAnimation::Transform* transform; + transform = static_cast(node); + transform->setValue(_animationValue->getValue()); } + traverse(node, nv); } - void updateStateSet(osg::StateSet* stateSet) - { - if (!stateSet) - return; - osg::StateAttribute* stateAttribute = stateSet->getAttribute(osg::StateAttribute::MATERIAL); - if (!stateAttribute) - return; - osg::Material* material = dynamic_cast(stateAttribute); - if (!material) - return; - material->setAlpha(osg::Material::FRONT_AND_BACK, _blend); - } -private: - float _blend; +public: + SGSharedPtr _condition; + SGSharedPtr _animationValue; }; - -SGBlendAnimation::SGBlendAnimation( SGPropertyNode *prop_root, - SGPropertyNode_ptr props ) - : SGAnimation(props, new osg::Group), - _use_personality( props->getBoolValue("use-personality",false) ), - _prop((SGPropertyNode *)prop_root->getNode(props->getStringValue("property", "/null"), true)), - _table(read_interpolation_table(props)), - _prev_value(1.0), - _offset(props,"offset",0.0), - _factor(props,"factor",1.0), - _min(props->getDoubleValue("min", 0.0)), - _max(props->getDoubleValue("max", 1.0)) +SGTranslateAnimation::SGTranslateAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot) : + SGAnimation(configNode, modelRoot) { - if ( _use_personality ) { - _factor.shuffle(); - _offset.shuffle(); - } -} + _condition = getCondition(); + _animationValue = read_value(configNode, modelRoot, "-m", + -SGLimitsd::max(), SGLimitsd::max()); + _axis[0] = configNode->getDoubleValue("axis/x", 0); + _axis[1] = configNode->getDoubleValue("axis/y", 0); + _axis[2] = configNode->getDoubleValue("axis/z", 0); + if (8*SGLimitsd::min() < norm(_axis)) + _axis = normalize(_axis); -SGBlendAnimation::~SGBlendAnimation () -{ + _initialValue = configNode->getDoubleValue("starting-position-m", 0); + _initialValue *= configNode->getDoubleValue("factor", 1); + _initialValue += configNode->getDoubleValue("offset-m", 0); } -void -SGBlendAnimation::operator()(osg::Node* node, osg::NodeVisitor* nv) +osg::Group* +SGTranslateAnimation::createAnimationGroup(osg::Group& parent) { - double _blend; - - if (_table == 0) { - _blend = 1.0 - (_prop->getDoubleValue() * _factor + _offset); - } else { - _blend = _table->interpolate(_prop->getDoubleValue()); + Transform* transform = new Transform; + transform->setName("translate animation"); + if (_animationValue) { + UpdateCallback* uc = new UpdateCallback(_condition, _animationValue); + transform->setUpdateCallback(uc); } - if (_blend < _min) - _blend = _min; - if (_blend > _max) - _blend = _max; - - if (_blend != _prev_value) { - _prev_value = _blend; - SGBlendAnimationVisitor visitor(1-_blend); - _branch->accept(visitor); - } - traverse(node, nv); -} - + transform->setAxis(_axis); + transform->setValue(_initialValue); + parent.addChild(transform); + return transform; +} //////////////////////////////////////////////////////////////////////// -// Implementation of SGTranslateAnimation +// Implementation of rotate/spin animation //////////////////////////////////////////////////////////////////////// -SGTranslateAnimation::SGTranslateAnimation( SGPropertyNode *prop_root, - SGPropertyNode_ptr props ) - : SGAnimation(props, new osg::MatrixTransform), - _use_personality( props->getBoolValue("use-personality",false) ), - _prop((SGPropertyNode *)prop_root->getNode(props->getStringValue("property", "/null"), true)), - _offset_m( props, "offset-m", 0.0 ), - _factor( props, "factor", 1.0 ), - _table(read_interpolation_table(props)), - _has_min(props->hasValue("min-m")), - _min_m(props->getDoubleValue("min-m")), - _has_max(props->hasValue("max-m")), - _max_m(props->getDoubleValue("max-m")), - _position_m(props->getDoubleValue("starting-position-m", 0)), - _condition(0) -{ - SGPropertyNode_ptr node = props->getChild("condition"); - if (node != 0) - _condition = sgReadCondition(prop_root, node); - - _axis[0] = props->getFloatValue("axis/x", 0); - _axis[1] = props->getFloatValue("axis/y", 0); - _axis[2] = props->getFloatValue("axis/z", 0); - _axis.normalize(); - - if ( _use_personality ) { - _factor.shuffle(); - _offset_m.shuffle(); +class SGRotateAnimation::Transform : public osg::Transform { +public: + Transform() + { setReferenceFrame(RELATIVE_RF); } + void setCenter(const SGVec3d& center) + { _center = center; dirtyBound(); } + void setAxis(const SGVec3d& axis) + { _axis = axis; dirtyBound(); } + void setAngle(double angle) + { _angle = angle; } + double getAngle() const + { return _angle; } + virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix, + osg::NodeVisitor* nv) const + { + // This is the fast path, optimize a bit + assert(_referenceFrame == RELATIVE_RF); + // FIXME optimize + osg::Matrix tmp; + set_rotation(tmp, _angle, _center, _axis); + matrix.preMult(tmp); + return true; + } + virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix, + osg::NodeVisitor* nv) const + { + assert(_referenceFrame == RELATIVE_RF); + // FIXME optimize + osg::Matrix tmp; + set_rotation(tmp, -_angle, _center, _axis); + matrix.postMult(tmp); + return true; + } + virtual osg::BoundingSphere computeBound() const + { + osg::BoundingSphere bs = osg::Group::computeBound(); + osg::BoundingSphere centerbs(_center.osg(), bs.radius()); + centerbs.expandBy(bs); + return centerbs; } -} -SGTranslateAnimation::~SGTranslateAnimation () -{ -} +private: + SGVec3d _center; + SGVec3d _axis; + double _angle; +}; -void -SGTranslateAnimation::operator()(osg::Node* node, osg::NodeVisitor* nv) -{ - if (_condition == 0 || _condition->test()) { +class SGRotateAnimation::UpdateCallback : public osg::NodeCallback { +public: + UpdateCallback(SGCondition const* condition, + SGDoubleValue const* animationValue) : + _condition(condition), + _animationValue(animationValue) + { } + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + if (!_condition || _condition->test()) { + SGRotateAnimation::Transform* transform; + transform = static_cast(node); + transform->setAngle(_animationValue->getValue()); + } + traverse(node, nv); + } +public: + SGSharedPtr _condition; + SGSharedPtr _animationValue; +}; - if (_table == 0) { - _position_m = (_prop->getDoubleValue() * _factor) + _offset_m; - if (_has_min && _position_m < _min_m) - _position_m = _min_m; - if (_has_max && _position_m > _max_m) - _position_m = _max_m; - } else { - _position_m = _table->interpolate(_prop->getDoubleValue()); +class SGRotateAnimation::SpinUpdateCallback : public osg::NodeCallback { +public: + SpinUpdateCallback(SGCondition const* condition, + SGDoubleValue const* animationValue) : + _condition(condition), + _animationValue(animationValue), + _lastTime(-1) + { } + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + if (!_condition || _condition->test()) { + SGRotateAnimation::Transform* transform; + transform = static_cast(node); + + double t = nv->getFrameStamp()->getReferenceTime(); + double dt = 0; + if (0 <= _lastTime) + dt = t - _lastTime; + _lastTime = t; + double velocity_rpms = _animationValue->getValue()/60; + double angle = transform->getAngle(); + angle += dt*velocity_rpms*360; + angle -= 360*floor(angle/360); + transform->setAngle(angle); } + traverse(node, nv); + } +public: + SGSharedPtr _condition; + SGSharedPtr _animationValue; + double _lastTime; +}; - osg::Matrix _matrix; - set_translation(_matrix, _position_m, _axis); - static_cast(_branch)->setMatrix(_matrix); +SGRotateAnimation::SGRotateAnimation(const SGPropertyNode* configNode, SGPropertyNode* modelRoot) : + SGAnimation(configNode, modelRoot) +{ + std::string type = configNode->getStringValue("type", ""); + _isSpin = (type == "spin"); + + _condition = getCondition(); + _animationValue = read_value(configNode, modelRoot, "-deg", + -SGLimitsd::max(), SGLimitsd::max()); + _initialValue = configNode->getDoubleValue("starting-position-deg", 0); + _initialValue *= configNode->getDoubleValue("factor", 1); + _initialValue += configNode->getDoubleValue("offset-deg", 0); + + _center = SGVec3d::zeros(); + if (configNode->hasValue("axis/x1-m")) { + SGVec3d v1, v2; + v1[0] = configNode->getDoubleValue("axis/x1-m", 0); + v1[1] = configNode->getDoubleValue("axis/y1-m", 0); + v1[2] = configNode->getDoubleValue("axis/z1-m", 0); + v2[0] = configNode->getDoubleValue("axis/x2-m", 0); + v2[1] = configNode->getDoubleValue("axis/y2-m", 0); + v2[2] = configNode->getDoubleValue("axis/z2-m", 0); + _center = 0.5*(v1+v2); + _axis = v2 - v1; + } else { + _axis[0] = configNode->getDoubleValue("axis/x", 0); + _axis[1] = configNode->getDoubleValue("axis/y", 0); + _axis[2] = configNode->getDoubleValue("axis/z", 0); + } + if (8*SGLimitsd::min() < norm(_axis)) + _axis = normalize(_axis); + + _center[0] = configNode->getDoubleValue("center/x-m", _center[0]); + _center[1] = configNode->getDoubleValue("center/y-m", _center[1]); + _center[2] = configNode->getDoubleValue("center/z-m", _center[2]); +} + +osg::Group* +SGRotateAnimation::createAnimationGroup(osg::Group& parent) +{ + Transform* transform = new Transform; + transform->setName("rotate animation"); + if (_isSpin) { + SpinUpdateCallback* uc; + uc = new SpinUpdateCallback(_condition, _animationValue); + transform->setUpdateCallback(uc); + } else if (_animationValue) { + UpdateCallback* uc = new UpdateCallback(_condition, _animationValue); + transform->setUpdateCallback(uc); } - traverse(node, nv); + transform->setCenter(_center); + transform->setAxis(_axis); + transform->setAngle(_initialValue); + parent.addChild(transform); + return transform; } - //////////////////////////////////////////////////////////////////////// -// Implementation of SGScaleAnimation +// Implementation of scale animation //////////////////////////////////////////////////////////////////////// -SGScaleAnimation::SGScaleAnimation( SGPropertyNode *prop_root, - SGPropertyNode_ptr props ) - : SGAnimation(props, new osg::MatrixTransform), - _use_personality( props->getBoolValue("use-personality",false) ), - _prop((SGPropertyNode *)prop_root->getNode(props->getStringValue("property", "/null"), true)), - _x_factor(props,"x-factor",1.0), - _y_factor(props,"y-factor",1.0), - _z_factor(props,"z-factor",1.0), - _x_offset(props,"x-offset",1.0), - _y_offset(props,"y-offset",1.0), - _z_offset(props,"z-offset",1.0), - _table(read_interpolation_table(props)), - _has_min_x(props->hasValue("x-min")), - _has_min_y(props->hasValue("y-min")), - _has_min_z(props->hasValue("z-min")), - _min_x(props->getDoubleValue("x-min")), - _min_y(props->getDoubleValue("y-min")), - _min_z(props->getDoubleValue("z-min")), - _has_max_x(props->hasValue("x-max")), - _has_max_y(props->hasValue("y-max")), - _has_max_z(props->hasValue("z-max")), - _max_x(props->getDoubleValue("x-max")), - _max_y(props->getDoubleValue("y-max")), - _max_z(props->getDoubleValue("z-max")) -{ - if ( _use_personality ) { - _x_factor.shuffle(); - _x_offset.shuffle(); - _y_factor.shuffle(); - _y_offset.shuffle(); - _z_factor.shuffle(); - _z_offset.shuffle(); +class SGScaleAnimation::Transform : public osg::Transform { +public: + Transform() : + _center(0, 0, 0), + _scaleFactor(1, 1, 1), + _boundScale(0) + { + setReferenceFrame(RELATIVE_RF); } -} - -SGScaleAnimation::~SGScaleAnimation () -{ -} - -void -SGScaleAnimation::operator()(osg::Node* node, osg::NodeVisitor* nv) -{ - if (_table == 0) { - _x_scale = _prop->getDoubleValue() * _x_factor + _x_offset; - if (_has_min_x && _x_scale < _min_x) - _x_scale = _min_x; - if (_has_max_x && _x_scale > _max_x) - _x_scale = _max_x; - } else { - _x_scale = _table->interpolate(_prop->getDoubleValue()); + void setCenter(const SGVec3d& center) + { + _center = center; + dirtyBound(); } - - if (_table == 0) { - _y_scale = _prop->getDoubleValue() * _y_factor + _y_offset; - if (_has_min_y && _y_scale < _min_y) - _y_scale = _min_y; - if (_has_max_y && _y_scale > _max_y) - _y_scale = _max_y; - } else { - _y_scale = _table->interpolate(_prop->getDoubleValue()); + void setScaleFactor(const SGVec3d& scaleFactor) + { + if (_boundScale < normI(scaleFactor)) + dirtyBound(); + _scaleFactor = scaleFactor; } - - if (_table == 0) { - _z_scale = _prop->getDoubleValue() * _z_factor + _z_offset; - if (_has_min_z && _z_scale < _min_z) - _z_scale = _min_z; - if (_has_max_z && _z_scale > _max_z) - _z_scale = _max_z; - } else { - _z_scale = _table->interpolate(_prop->getDoubleValue()); + void setScaleFactor(double scaleFactor) + { + if (_boundScale < fabs(scaleFactor)) + dirtyBound(); + _scaleFactor = SGVec3d(scaleFactor, scaleFactor, scaleFactor); } - - osg::Matrix _matrix; - set_scale(_matrix, _x_scale, _y_scale, _z_scale ); - static_cast(_branch)->setMatrix(_matrix); - traverse(node, nv); -} - - -//////////////////////////////////////////////////////////////////////// -// Implementation of SGTexRotateAnimation -//////////////////////////////////////////////////////////////////////// - -SGTexRotateAnimation::SGTexRotateAnimation( SGPropertyNode *prop_root, - SGPropertyNode_ptr props ) - : SGAnimation(props, new osg::Group), - _prop((SGPropertyNode *)prop_root->getNode(props->getStringValue("property", "/null"), true)), - _offset_deg(props->getDoubleValue("offset-deg", 0.0)), - _factor(props->getDoubleValue("factor", 1.0)), - _table(read_interpolation_table(props)), - _has_min(props->hasValue("min-deg")), - _min_deg(props->getDoubleValue("min-deg")), - _has_max(props->hasValue("max-deg")), - _max_deg(props->getDoubleValue("max-deg")), - _position_deg(props->getDoubleValue("starting-position-deg", 0)), - _condition(0) -{ - SGPropertyNode *node = props->getChild("condition"); - if (node != 0) - _condition = sgReadCondition(prop_root, node); - - _center[0] = props->getFloatValue("center/x", 0); - _center[1] = props->getFloatValue("center/y", 0); - _center[2] = props->getFloatValue("center/z", 0); - _axis[0] = props->getFloatValue("axis/x", 0); - _axis[1] = props->getFloatValue("axis/y", 0); - _axis[2] = props->getFloatValue("axis/z", 0); - _axis.normalize(); - - osg::StateSet* stateSet = _branch->getOrCreateStateSet(); - _texMat = new osg::TexMat; - stateSet->setTextureAttribute(0, _texMat.get()); -} - -SGTexRotateAnimation::~SGTexRotateAnimation () -{ -} - -void -SGTexRotateAnimation::operator()(osg::Node* node, osg::NodeVisitor* nv) -{ - if (!_condition || _condition->test()) { - if (_table == 0) { - _position_deg = _prop->getDoubleValue() * _factor + _offset_deg; - if (_has_min && _position_deg < _min_deg) - _position_deg = _min_deg; - if (_has_max && _position_deg > _max_deg) - _position_deg = _max_deg; - } else { - _position_deg = _table->interpolate(_prop->getDoubleValue()); - } - osg::Matrix _matrix; - set_rotation(_matrix, _position_deg, _center, _axis); - _texMat->setMatrix(_matrix); + virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix, + osg::NodeVisitor* nv) const + { + assert(_referenceFrame == RELATIVE_RF); + osg::Matrix transform; + transform(0,0) = _scaleFactor[0]; + transform(1,1) = _scaleFactor[1]; + transform(2,2) = _scaleFactor[2]; + transform(3,0) = _center[0]*(1 - _scaleFactor[0]); + transform(3,1) = _center[1]*(1 - _scaleFactor[1]); + transform(3,2) = _center[2]*(1 - _scaleFactor[2]); + matrix.preMult(transform); + return true; } - traverse(node, nv); -} - - -//////////////////////////////////////////////////////////////////////// -// Implementation of SGTexTranslateAnimation -//////////////////////////////////////////////////////////////////////// - -SGTexTranslateAnimation::SGTexTranslateAnimation( SGPropertyNode *prop_root, - SGPropertyNode_ptr props ) - : SGAnimation(props, new osg::Group), - _prop((SGPropertyNode *)prop_root->getNode(props->getStringValue("property", "/null"), true)), - _offset(props->getDoubleValue("offset", 0.0)), - _factor(props->getDoubleValue("factor", 1.0)), - _step(props->getDoubleValue("step",0.0)), - _scroll(props->getDoubleValue("scroll",0.0)), - _table(read_interpolation_table(props)), - _has_min(props->hasValue("min")), - _min(props->getDoubleValue("min")), - _has_max(props->hasValue("max")), - _max(props->getDoubleValue("max")), - _position(props->getDoubleValue("starting-position", 0)), - _condition(0) -{ - SGPropertyNode *node = props->getChild("condition"); - if (node != 0) - _condition = sgReadCondition(prop_root, node); - - _axis[0] = props->getFloatValue("axis/x", 0); - _axis[1] = props->getFloatValue("axis/y", 0); - _axis[2] = props->getFloatValue("axis/z", 0); - _axis.normalize(); - - osg::StateSet* stateSet = _branch->getOrCreateStateSet(); - _texMat = new osg::TexMat; - stateSet->setTextureAttribute(0, _texMat.get()); -} - -SGTexTranslateAnimation::~SGTexTranslateAnimation () -{ -} - -void -SGTexTranslateAnimation::operator()(osg::Node* node, osg::NodeVisitor* nv) -{ - if (!_condition || _condition->test()) { - if (_table == 0) { - _position = (apply_mods(_prop->getDoubleValue(), _step, _scroll) + _offset) * _factor; - if (_has_min && _position < _min) - _position = _min; - if (_has_max && _position > _max) - _position = _max; - } else { - _position = _table->interpolate(apply_mods(_prop->getDoubleValue(), _step, _scroll)); - } - osg::Matrix _matrix; - set_translation(_matrix, _position, _axis); - _texMat->setMatrix(_matrix); + virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix, + osg::NodeVisitor* nv) const + { + assert(_referenceFrame == RELATIVE_RF); + if (fabs(_scaleFactor[0]) < SGLimitsd::min()) + return false; + if (fabs(_scaleFactor[1]) < SGLimitsd::min()) + return false; + if (fabs(_scaleFactor[2]) < SGLimitsd::min()) + return false; + SGVec3d rScaleFactor(1/_scaleFactor[0], + 1/_scaleFactor[1], + 1/_scaleFactor[2]); + osg::Matrix transform; + transform(0,0) = rScaleFactor[0]; + transform(1,1) = rScaleFactor[1]; + transform(2,2) = rScaleFactor[2]; + transform(3,0) = _center[0]*(1 - rScaleFactor[0]); + transform(3,1) = _center[1]*(1 - rScaleFactor[1]); + transform(3,2) = _center[2]*(1 - rScaleFactor[2]); + matrix.postMult(transform); + return true; + } + virtual osg::BoundingSphere computeBound() const + { + osg::BoundingSphere bs = osg::Group::computeBound(); + _boundScale = normI(_scaleFactor); + bs.radius() *= _boundScale; + return bs; } - traverse(node, nv); -} - -//////////////////////////////////////////////////////////////////////// -// Implementation of SGTexMultipleAnimation -//////////////////////////////////////////////////////////////////////// +private: + SGVec3d _center; + SGVec3d _scaleFactor; + mutable double _boundScale; +}; -SGTexMultipleAnimation::SGTexMultipleAnimation( SGPropertyNode *prop_root, - SGPropertyNode_ptr props ) - : SGAnimation(props, new osg::Group), - _prop((SGPropertyNode *)prop_root->getNode(props->getStringValue("property", "/null"), true)) -{ - unsigned int i; - // Load animations - vector transform_nodes = props->getChildren("transform"); - _transform = new TexTransform [transform_nodes.size()]; - _num_transforms = 0; - for (i = 0; i < transform_nodes.size(); i++) { - SGPropertyNode_ptr transform_props = transform_nodes[i]; - - if (!strcmp("textranslate",transform_props->getStringValue("subtype", 0))) { - - // transform is a translation - _transform[i].subtype = 0; - - _transform[i].prop = (SGPropertyNode *)prop_root->getNode(transform_props->getStringValue("property", "/null"), true); - - _transform[i].offset = transform_props->getDoubleValue("offset", 0.0); - _transform[i].factor = transform_props->getDoubleValue("factor", 1.0); - _transform[i].step = transform_props->getDoubleValue("step",0.0); - _transform[i].scroll = transform_props->getDoubleValue("scroll",0.0); - _transform[i].table = read_interpolation_table(transform_props); - _transform[i].has_min = transform_props->hasValue("min"); - _transform[i].min = transform_props->getDoubleValue("min"); - _transform[i].has_max = transform_props->hasValue("max"); - _transform[i].max = transform_props->getDoubleValue("max"); - _transform[i].position = transform_props->getDoubleValue("starting-position", 0); - - _transform[i].axis[0] = transform_props->getFloatValue("axis/x", 0); - _transform[i].axis[1] = transform_props->getFloatValue("axis/y", 0); - _transform[i].axis[2] = transform_props->getFloatValue("axis/z", 0); - _transform[i].axis.normalize(); - _num_transforms++; - } else if (!strcmp("texrotate",transform_nodes[i]->getStringValue("subtype", 0))) { - - // transform is a rotation - _transform[i].subtype = 1; - - _transform[i].prop = (SGPropertyNode *)prop_root->getNode(transform_props->getStringValue("property", "/null"), true); - _transform[i].offset = transform_props->getDoubleValue("offset-deg", 0.0); - _transform[i].factor = transform_props->getDoubleValue("factor", 1.0); - _transform[i].table = read_interpolation_table(transform_props); - _transform[i].has_min = transform_props->hasValue("min-deg"); - _transform[i].min = transform_props->getDoubleValue("min-deg"); - _transform[i].has_max = transform_props->hasValue("max-deg"); - _transform[i].max = transform_props->getDoubleValue("max-deg"); - _transform[i].position = transform_props->getDoubleValue("starting-position-deg", 0); - - _transform[i].center[0] = transform_props->getFloatValue("center/x", 0); - _transform[i].center[1] = transform_props->getFloatValue("center/y", 0); - _transform[i].center[2] = transform_props->getFloatValue("center/z", 0); - _transform[i].axis[0] = transform_props->getFloatValue("axis/x", 0); - _transform[i].axis[1] = transform_props->getFloatValue("axis/y", 0); - _transform[i].axis[2] = transform_props->getFloatValue("axis/z", 0); - _transform[i].axis.normalize(); - _num_transforms++; +class SGScaleAnimation::UpdateCallback : public osg::NodeCallback { +public: + UpdateCallback(const SGCondition* condition, + SGSharedPtr animationValue[3]) : + _condition(condition) + { + _animationValue[0] = animationValue[0]; + _animationValue[1] = animationValue[1]; + _animationValue[2] = animationValue[2]; + } + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + if (!_condition || _condition->test()) { + SGScaleAnimation::Transform* transform; + transform = static_cast(node); + SGVec3d scale(_animationValue[0]->getValue(), + _animationValue[1]->getValue(), + _animationValue[2]->getValue()); + transform->setScaleFactor(scale); } + traverse(node, nv); } - osg::StateSet* stateSet = _branch->getOrCreateStateSet(); - _texMat = new osg::TexMat; - stateSet->setTextureAttribute(0, _texMat.get()); -} - -SGTexMultipleAnimation::~SGTexMultipleAnimation () -{ - delete [] _transform; -} - -void -SGTexMultipleAnimation::operator()(osg::Node* node, osg::NodeVisitor* nv) -{ - int i; - osg::Matrix tmatrix; - tmatrix.makeIdentity(); - for (i = 0; i < _num_transforms; i++) { - - if(_transform[i].subtype == 0) { - - // subtype 0 is translation - if (_transform[i].table == 0) { - _transform[i].position = (apply_mods(_transform[i].prop->getDoubleValue(), _transform[i].step,_transform[i].scroll) + _transform[i].offset) * _transform[i].factor; - if (_transform[i].has_min && _transform[i].position < _transform[i].min) - _transform[i].position = _transform[i].min; - if (_transform[i].has_max && _transform[i].position > _transform[i].max) - _transform[i].position = _transform[i].max; - } else { - _transform[i].position = _transform[i].table->interpolate(apply_mods(_transform[i].prop->getDoubleValue(), _transform[i].step,_transform[i].scroll)); - } - osg::Matrix matrix; - set_translation(matrix, _transform[i].position, _transform[i].axis); - tmatrix = matrix*tmatrix; - - } else if (_transform[i].subtype == 1) { - - // subtype 1 is rotation - - if (_transform[i].table == 0) { - _transform[i].position = _transform[i].prop->getDoubleValue() * _transform[i].factor + _transform[i].offset; - if (_transform[i].has_min && _transform[i].position < _transform[i].min) - _transform[i].position = _transform[i].min; - if (_transform[i].has_max && _transform[i].position > _transform[i].max) - _transform[i].position = _transform[i].max; - } else { - _transform[i].position = _transform[i].table->interpolate(_transform[i].prop->getDoubleValue()); - } +public: + SGSharedPtr _condition; + SGSharedPtr _animationValue[3]; +}; - osg::Matrix matrix; - set_rotation(matrix, _transform[i].position, _transform[i].center, _transform[i].axis); - tmatrix = matrix*tmatrix; +SGScaleAnimation::SGScaleAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot) : + SGAnimation(configNode, modelRoot) +{ + _condition = getCondition(); + + // default offset/factor for all directions + double offset = configNode->getDoubleValue("offset", 1); + double factor = configNode->getDoubleValue("factor", 1); + + std::string inputPropertyName; + inputPropertyName = configNode->getStringValue("property", ""); + if (!inputPropertyName.empty()) { + SGPropertyNode* inputProperty; + inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true); + SGInterpTable* interpTable = read_interpolation_table(configNode); + if (interpTable) { + SGInterpTableValue* value; + value = new SGInterpTableValue(inputProperty, interpTable); + _animationValue[0] = value; + _animationValue[1] = value; + _animationValue[2] = value; + } else if (configNode->getBoolValue("use-personality", false)) { + SGPersScaleOffsetValue* value; + value = new SGPersScaleOffsetValue(inputProperty, configNode, + "x-factor", "x-offset", + factor, offset); + value->setMin(configNode->getDoubleValue("x-min", 0)); + value->setMax(configNode->getDoubleValue("x-max", SGLimitsd::max())); + _animationValue[0] = value; + value = new SGPersScaleOffsetValue(inputProperty, configNode, + "y-factor", "y-offset", + factor, offset); + value->setMin(configNode->getDoubleValue("y-min", 0)); + value->setMax(configNode->getDoubleValue("y-max", SGLimitsd::max())); + _animationValue[1] = value; + value = new SGPersScaleOffsetValue(inputProperty, configNode, + "z-factor", "z-offset", + factor, offset); + value->setMin(configNode->getDoubleValue("z-min", 0)); + value->setMax(configNode->getDoubleValue("z-max", SGLimitsd::max())); + _animationValue[2] = value; + } else { + SGScaleOffsetValue* value = new SGScaleOffsetValue(inputProperty); + value->setScale(configNode->getDoubleValue("x-factor", factor)); + value->setOffset(configNode->getDoubleValue("x-offset", offset)); + value->setMin(configNode->getDoubleValue("x-min", 0)); + value->setMax(configNode->getDoubleValue("x-max", SGLimitsd::max())); + _animationValue[0] = value; + value = new SGScaleOffsetValue(inputProperty); + value->setScale(configNode->getDoubleValue("y-factor", factor)); + value->setOffset(configNode->getDoubleValue("y-offset", offset)); + value->setMin(configNode->getDoubleValue("y-min", 0)); + value->setMax(configNode->getDoubleValue("y-max", SGLimitsd::max())); + _animationValue[1] = value; + value = new SGScaleOffsetValue(inputProperty); + value->setScale(configNode->getDoubleValue("z-factor", factor)); + value->setOffset(configNode->getDoubleValue("z-offset", offset)); + value->setMin(configNode->getDoubleValue("z-min", 0)); + value->setMax(configNode->getDoubleValue("z-max", SGLimitsd::max())); + _animationValue[2] = value; } } - _texMat->setMatrix(tmatrix); - traverse(node, nv); + _initialValue[0] = configNode->getDoubleValue("x-starting-scale", 1); + _initialValue[0] *= configNode->getDoubleValue("x-factor", factor); + _initialValue[0] += configNode->getDoubleValue("x-offset", offset); + _initialValue[1] = configNode->getDoubleValue("y-starting-scale", 1); + _initialValue[1] *= configNode->getDoubleValue("y-factor", factor); + _initialValue[1] += configNode->getDoubleValue("y-offset", offset); + _initialValue[2] = configNode->getDoubleValue("z-starting-scale", 1); + _initialValue[2] *= configNode->getDoubleValue("z-factor", factor); + _initialValue[2] += configNode->getDoubleValue("z-offset", offset); + _center[0] = configNode->getDoubleValue("center/x-m", 0); + _center[1] = configNode->getDoubleValue("center/y-m", 0); + _center[2] = configNode->getDoubleValue("center/z-m", 0); +} + +osg::Group* +SGScaleAnimation::createAnimationGroup(osg::Group& parent) +{ + Transform* transform = new Transform; + transform->setName("scale animation"); + transform->setCenter(_center); + transform->setScaleFactor(_initialValue); + UpdateCallback* uc = new UpdateCallback(_condition, _animationValue); + transform->setUpdateCallback(uc); + parent.addChild(transform); + return transform; } - //////////////////////////////////////////////////////////////////////// -// Implementation of SGAlphaTestAnimation +// Implementation of dist scale animation //////////////////////////////////////////////////////////////////////// -SGAlphaTestAnimation::SGAlphaTestAnimation(SGPropertyNode_ptr props) - : SGAnimation(props, new osg::Group) -{ - _alpha_clamp = props->getFloatValue("alpha-factor", 0.0); -} - -SGAlphaTestAnimation::~SGAlphaTestAnimation () -{ -} - -void SGAlphaTestAnimation::init() -{ - osg::StateSet* stateSet = _branch->getOrCreateStateSet(); - osg::AlphaFunc* alphaFunc = new osg::AlphaFunc; - alphaFunc->setFunction(osg::AlphaFunc::GREATER); - alphaFunc->setReferenceValue(_alpha_clamp); - stateSet->setAttribute(alphaFunc); - stateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::ON); - stateSet->setMode(GL_BLEND, osg::StateAttribute::ON); - stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); -} - - - -//////////////////////////////////////////////////////////////////////// -// Implementation of SGMaterialAnimation -//////////////////////////////////////////////////////////////////////// - -SGMaterialAnimation::SGMaterialAnimation( SGPropertyNode *prop_root, - SGPropertyNode_ptr props, const SGPath &texture_path) - : SGAnimation(props, new osg::Group), - _last_condition(false), - _prop_root(prop_root), - _prop_base(""), - _texture_base(texture_path), - _read(0), - _update(0), - _global(props->getBoolValue("global", false)) -{ - SGPropertyNode_ptr n; - n = props->getChild("condition"); - _condition = n ? sgReadCondition(_prop_root, n) : 0; - n = props->getChild("property-base"); - if (n) { - _prop_base = n->getStringValue(); - if (!_prop_base.empty() && _prop_base.end()[-1] != '/') - _prop_base += '/'; - } - - initColorGroup(props->getChild("diffuse"), &_diff, DIFFUSE); - initColorGroup(props->getChild("ambient"), &_amb, AMBIENT); - initColorGroup(props->getChild("emission"), &_emis, EMISSION); - initColorGroup(props->getChild("specular"), &_spec, SPECULAR); - - _shi = props->getFloatValue("shininess", -1.0); - if (_shi >= 0.0) - _update |= SHININESS; - - SGPropertyNode_ptr group = props->getChild("transparency"); - if (group) { - _trans.value = group->getFloatValue("alpha", -1.0); - _trans.factor = group->getFloatValue("factor", 1.0); - _trans.offset = group->getFloatValue("offset", 0.0); - _trans.min = group->getFloatValue("min", 0.0); - if (_trans.min < 0.0) - _trans.min = 0.0; - _trans.max = group->getFloatValue("max", 1.0); - if (_trans.max > 1.0) - _trans.max = 1.0; - if (_trans.dirty()) - _update |= TRANSPARENCY; - - n = group->getChild("alpha-prop"); - _trans.value_prop = n ? _prop_root->getNode(path(n->getStringValue()), true) : 0; - n = group->getChild("factor-prop"); - _trans.factor_prop = n ? _prop_root->getNode(path(n->getStringValue()), true) : 0; - n = group->getChild("offset-prop"); - _trans.offset_prop = n ? _prop_root->getNode(path(n->getStringValue()), true) : 0; - if (_trans.live()) - _read |= TRANSPARENCY; - } - - _thresh = props->getFloatValue("threshold", -1.0); - if (_thresh >= 0.0) - _update |= THRESHOLD; - - string _texture_str = props->getStringValue("texture", ""); - if (!_texture_str.empty()) { - _texture = _texture_base; - _texture.append(_texture_str); - _texture2D = SGLoadTexture2D(_texture); - _update |= TEXTURE; - } - - n = props->getChild("shininess-prop"); - _shi_prop = n ? _prop_root->getNode(path(n->getStringValue()), true) : 0; - n = props->getChild("threshold-prop"); - _thresh_prop = n ? _prop_root->getNode(path(n->getStringValue()), true) : 0; - n = props->getChild("texture-prop"); - _tex_prop = n ? _prop_root->getNode(path(n->getStringValue()), true) : 0; - - _static_update = _update; - - _alphaFunc = new osg::AlphaFunc(osg::AlphaFunc::GREATER, 0); -} - -SGMaterialAnimation::~SGMaterialAnimation() -{ -} - -void SGMaterialAnimation::initColorGroup(SGPropertyNode_ptr group, ColorSpec *col, int flag) -{ - if (!group) - return; - - col->red = group->getFloatValue("red", -1.0); - col->green = group->getFloatValue("green", -1.0); - col->blue = group->getFloatValue("blue", -1.0); - col->factor = group->getFloatValue("factor", 1.0); - col->offset = group->getFloatValue("offset", 0.0); - if (col->dirty()) - _update |= flag; - - SGPropertyNode *n; - n = group->getChild("red-prop"); - col->red_prop = n ? _prop_root->getNode(path(n->getStringValue()), true) : 0; - n = group->getChild("green-prop"); - col->green_prop = n ? _prop_root->getNode(path(n->getStringValue()), true) : 0; - n = group->getChild("blue-prop"); - col->blue_prop = n ? _prop_root->getNode(path(n->getStringValue()), true) : 0; - n = group->getChild("factor-prop"); - col->factor_prop = n ? _prop_root->getNode(path(n->getStringValue()), true) : 0; - n = group->getChild("offset-prop"); - col->offset_prop = n ? _prop_root->getNode(path(n->getStringValue()), true) : 0; - if (col->live()) - _read |= flag; -} - -void SGMaterialAnimation::init() -{ - if (!_global) - cloneMaterials(_branch); - - // OSGFIXME - osg::StateSet* stateSet = _branch->getOrCreateStateSet(); - if (_update & THRESHOLD) { - stateSet->setAttribute(_alphaFunc.get(), osg::StateAttribute::OVERRIDE); - stateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); - } - if ((_update & TEXTURE) && _texture2D.valid()) { - stateSet->setTextureAttribute(0, _texture2D.get(), osg::StateAttribute::OVERRIDE); - stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); - } -} - -void -SGMaterialAnimation::operator()(osg::Node* node, osg::NodeVisitor* nv) -{ - if (_condition) { - bool cond = _condition->test(); - if (cond && !_last_condition) - _update |= _static_update; - - _last_condition = cond; - if (!cond) { - traverse(node, nv); - return; - } - } - - if (_read & DIFFUSE) - updateColorGroup(&_diff, DIFFUSE); - if (_read & AMBIENT) - updateColorGroup(&_amb, AMBIENT); - if (_read & EMISSION) - updateColorGroup(&_emis, EMISSION); - if (_read & SPECULAR) - updateColorGroup(&_spec, SPECULAR); - - float f; - if (_shi_prop) { - f = _shi; - _shi = _shi_prop->getFloatValue(); - if (_shi != f) - _update |= SHININESS; - } - if (_read & TRANSPARENCY) { - PropSpec tmp = _trans; - if (_trans.value_prop) - _trans.value = _trans.value_prop->getFloatValue(); - if (_trans.factor_prop) - _trans.factor = _trans.factor_prop->getFloatValue(); - if (_trans.offset_prop) - _trans.offset = _trans.offset_prop->getFloatValue(); - if (_trans != tmp) - _update |= TRANSPARENCY; - } - if (_thresh_prop) { - f = _thresh; - _thresh = _thresh_prop->getFloatValue(); - if (_thresh != f) - _update |= THRESHOLD; - } - if (_tex_prop) { - string t = _tex_prop->getStringValue(); - if (!t.empty() && t != _texture_str) { - _texture_str = t; - _texture = _texture_base; - _texture.append(t); - _update |= TEXTURE; - } - } - if (_update) { - setMaterialBranch(_branch); - _update = 0; - } - traverse(node, nv); -} - -void SGMaterialAnimation::updateColorGroup(ColorSpec *col, int flag) -{ - ColorSpec tmp = *col; - if (col->red_prop) - col->red = col->red_prop->getFloatValue(); - if (col->green_prop) - col->green = col->green_prop->getFloatValue(); - if (col->blue_prop) - col->blue = col->blue_prop->getFloatValue(); - if (col->factor_prop) - col->factor = col->factor_prop->getFloatValue(); - if (col->offset_prop) - col->offset = col->offset_prop->getFloatValue(); - if (*col != tmp) - _update |= flag; -} - -class SGMaterialAnimationCloneVisitor : public osg::NodeVisitor { +class SGDistScaleAnimation::Transform : public osg::Transform { public: - SGMaterialAnimationCloneVisitor() : - osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) + Transform(const SGPropertyNode* configNode) { - setVisitorType(osg::NodeVisitor::NODE_VISITOR); + setName(configNode->getStringValue("name", "dist scale animation")); + setReferenceFrame(RELATIVE_RF); + getOrCreateStateSet()->setMode(GL_NORMALIZE, osg::StateAttribute::ON); + _factor = configNode->getFloatValue("factor", 1); + _offset = configNode->getFloatValue("offset", 0); + _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon()); + _max_v = configNode->getFloatValue("max", SGLimitsf::max()); + _table = read_interpolation_table(configNode); + _center[0] = configNode->getFloatValue("center/x-m", 0); + _center[1] = configNode->getFloatValue("center/y-m", 0); + _center[2] = configNode->getFloatValue("center/z-m", 0); } - virtual void apply(osg::Node& node) + virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix, + osg::NodeVisitor* nv) const { - traverse(node); - osg::StateSet* stateSet = node.getStateSet(); - if (!stateSet) - return; - if (1 < stateSet->referenceCount()) { - osg::CopyOp copyOp(osg::CopyOp::DEEP_COPY_STATESETS); - osg::Object* object = stateSet->clone(copyOp); - stateSet = static_cast(object); - node.setStateSet(stateSet); - } - cloneMaterial(stateSet); + osg::Matrix transform; + double scale_factor = computeScaleFactor(nv); + transform(0,0) = scale_factor; + transform(1,1) = scale_factor; + transform(2,2) = scale_factor; + transform(3,0) = _center[0]*(1 - scale_factor); + transform(3,1) = _center[1]*(1 - scale_factor); + transform(3,2) = _center[2]*(1 - scale_factor); + matrix.preMult(transform); + return true; } - virtual void apply(osg::Geode& node) + + virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix, + osg::NodeVisitor* nv) const { - apply((osg::Node&)node); - traverse(node); - unsigned nDrawables = node.getNumDrawables(); - for (unsigned i = 0; i < nDrawables; ++i) { - osg::Drawable* drawable = node.getDrawable(i); - osg::StateSet* stateSet = drawable->getStateSet(); - if (!stateSet) - continue; - if (1 < stateSet->referenceCount()) { - osg::CopyOp copyOp(osg::CopyOp::DEEP_COPY_STATESETS); - osg::Object* object = stateSet->clone(copyOp); - stateSet = static_cast(object); - drawable->setStateSet(stateSet); - } - cloneMaterial(stateSet); - } + double scale_factor = computeScaleFactor(nv); + if (fabs(scale_factor) <= SGLimits::min()) + return false; + osg::Matrix transform; + double rScaleFactor = 1/scale_factor; + transform(0,0) = rScaleFactor; + transform(1,1) = rScaleFactor; + transform(2,2) = rScaleFactor; + transform(3,0) = _center[0]*(1 - rScaleFactor); + transform(3,1) = _center[1]*(1 - rScaleFactor); + transform(3,2) = _center[2]*(1 - rScaleFactor); + matrix.postMult(transform); + return true; } - void cloneMaterial(osg::StateSet* stateSet) + +private: + double computeScaleFactor(osg::NodeVisitor* nv) const { - osg::StateAttribute* stateAttr; - stateAttr = stateSet->getAttribute(osg::StateAttribute::MATERIAL); - if (!stateAttr) - return; - osg::CopyOp copyOp(osg::CopyOp::DEEP_COPY_STATEATTRIBUTES); - osg::Object* object = stateAttr->clone(copyOp); - osg::Material* material = static_cast(object); - materialList.push_back(material); - while (stateSet->getAttribute(osg::StateAttribute::MATERIAL)) { - stateSet->removeAttribute(osg::StateAttribute::MATERIAL); + if (!nv) + return 1; + + double scale_factor = (_center.osg() - nv->getEyePoint()).length(); + if (_table == 0) { + scale_factor = _factor * scale_factor + _offset; + } else { + scale_factor = _table->interpolate( scale_factor ); } - stateSet->setAttribute(material); + if (scale_factor < _min_v) + scale_factor = _min_v; + if (scale_factor > _max_v) + scale_factor = _max_v; + + return scale_factor; } - std::vector > materialList; + + SGSharedPtr _table; + SGVec3d _center; + double _min_v; + double _max_v; + double _factor; + double _offset; }; -void SGMaterialAnimation::cloneMaterials(osg::Group *b) + +SGDistScaleAnimation::SGDistScaleAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot) : + SGAnimation(configNode, modelRoot) { - SGMaterialAnimationCloneVisitor cloneVisitor; - b->accept(cloneVisitor); - _materialList.swap(cloneVisitor.materialList); } -void SGMaterialAnimation::setMaterialBranch(osg::Group *b) +osg::Group* +SGDistScaleAnimation::createAnimationGroup(osg::Group& parent) { - std::vector >::iterator i; - for (i = _materialList.begin(); i != _materialList.end(); ++i) { - osg::Material* material = i->get(); - if (_update & DIFFUSE) { - osg::Vec4 v = _diff.rgba(); - float alpha = material->getDiffuse(osg::Material::FRONT_AND_BACK)[3]; - material->setColorMode(osg::Material::DIFFUSE); - material->setDiffuse(osg::Material::FRONT_AND_BACK, - osg::Vec4(v[0], v[1], v[2], alpha)); - } - if (_update & AMBIENT) { - material->setColorMode(osg::Material::AMBIENT); - material->setDiffuse(osg::Material::FRONT_AND_BACK, _amb.rgba()); - } - if (_update & EMISSION) - material->setEmission(osg::Material::FRONT_AND_BACK, _emis.rgba()); - if (_update & SPECULAR) - material->setSpecular(osg::Material::FRONT_AND_BACK, _spec.rgba()); - if (_update & SHININESS) - material->setShininess(osg::Material::FRONT_AND_BACK, - clamp(_shi, 0.0, 128.0)); - if (_update & THRESHOLD) - _alphaFunc->setReferenceValue(clamp(_thresh)); - } - - if (_update & TRANSPARENCY) { - float trans = _trans.value * _trans.factor + _trans.offset; - trans = trans < _trans.min ? _trans.min : trans > _trans.max ? _trans.max : trans; - SGBlendAnimationVisitor visitor(trans); - _branch->accept(visitor); - } - if (_update & TEXTURE) { - if (!_texture2D) { - _texture2D = new osg::Texture2D; - osg::StateSet* stateSet = _branch->getOrCreateStateSet(); - stateSet->setTextureAttribute(0, _texture2D.get(), osg::StateAttribute::OVERRIDE); - stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); - } - osg::Image* image = osgDB::readImageFile(_texture.str()); - if (image) { - _texture2D->setImage(image); - if (image->isImageTranslucent()) { - osg::StateSet* stateSet = _branch->getOrCreateStateSet(); - stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); - stateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::ON); - stateSet->setMode(GL_BLEND, osg::StateAttribute::ON); - } - } - } + Transform* transform = new Transform(getConfig()); + parent.addChild(transform); + return transform; } - //////////////////////////////////////////////////////////////////////// -// Implementation of SGFlashAnimation +// Implementation of flash animation //////////////////////////////////////////////////////////////////////// -class SGFlashAnimationTransform : public osg::Transform { + +class SGFlashAnimation::Transform : public osg::Transform { public: - SGFlashAnimationTransform(SGPropertyNode* props) + Transform(const SGPropertyNode* configNode) { + setReferenceFrame(RELATIVE_RF); + setName(configNode->getStringValue("name", "flash animation")); getOrCreateStateSet()->setMode(GL_NORMALIZE, osg::StateAttribute::ON); - _axis[0] = props->getFloatValue("axis/x", 0); - _axis[1] = props->getFloatValue("axis/y", 0); - _axis[2] = props->getFloatValue("axis/z", 1); + _axis[0] = configNode->getFloatValue("axis/x", 0); + _axis[1] = configNode->getFloatValue("axis/y", 0); + _axis[2] = configNode->getFloatValue("axis/z", 1); _axis.normalize(); - _center[0] = props->getFloatValue("center/x-m", 0); - _center[1] = props->getFloatValue("center/y-m", 0); - _center[2] = props->getFloatValue("center/z-m", 0); + _center[0] = configNode->getFloatValue("center/x-m", 0); + _center[1] = configNode->getFloatValue("center/y-m", 0); + _center[2] = configNode->getFloatValue("center/z-m", 0); - _offset = props->getFloatValue("offset", 0.0); - _factor = props->getFloatValue("factor", 1.0); - _power = props->getFloatValue("power", 1.0); - _two_sides = props->getBoolValue("two-sides", false); + _offset = configNode->getFloatValue("offset", 0); + _factor = configNode->getFloatValue("factor", 1); + _power = configNode->getFloatValue("power", 1); + _two_sides = configNode->getBoolValue("two-sides", false); - _min_v = props->getFloatValue("min", 0.0); - _max_v = props->getFloatValue("max", 1.0); + _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon()); + _max_v = configNode->getFloatValue("max", 1); } - virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix, osg::NodeVisitor* nv) const { - double scale_factor = computeScaleFactor(nv); osg::Matrix transform; + double scale_factor = computeScaleFactor(nv); transform(0,0) = scale_factor; transform(1,1) = scale_factor; transform(2,2) = scale_factor; - transform(3,0) = _center[0] * ( 1 - scale_factor ); - transform(3,1) = _center[1] * ( 1 - scale_factor ); - transform(3,2) = _center[2] * ( 1 - scale_factor ); - if (_referenceFrame == RELATIVE_RF) - matrix.preMult(transform); - else - matrix = transform; - + transform(3,0) = _center[0]*(1 - scale_factor); + transform(3,1) = _center[1]*(1 - scale_factor); + transform(3,2) = _center[2]*(1 - scale_factor); + matrix.preMult(transform); return true; } @@ -1479,23 +1278,21 @@ public: osg::NodeVisitor* nv) const { double scale_factor = computeScaleFactor(nv); - if (fabs(scale_factor) <= std::numeric_limits::min()) + if (fabs(scale_factor) <= SGLimits::min()) return false; osg::Matrix transform; double rScaleFactor = 1/scale_factor; transform(0,0) = rScaleFactor; transform(1,1) = rScaleFactor; transform(2,2) = rScaleFactor; - transform(3,0) = rScaleFactor*_center[0] * ( scale_factor - 1 ); - transform(3,1) = rScaleFactor*_center[1] * ( scale_factor - 1 ); - transform(3,2) = rScaleFactor*_center[2] * ( scale_factor - 1 ); - if (_referenceFrame == RELATIVE_RF) - matrix.postMult(transform); - else - matrix = transform; + transform(3,0) = _center[0]*(1 - rScaleFactor); + transform(3,1) = _center[1]*(1 - rScaleFactor); + transform(3,2) = _center[2]*(1 - rScaleFactor); + matrix.postMult(transform); return true; } +private: double computeScaleFactor(osg::NodeVisitor* nv) const { if (!nv) @@ -1519,161 +1316,750 @@ public: return scale_factor; } + virtual osg::BoundingSphere computeBound() const + { + // avoid being culled away by small feature culling + osg::BoundingSphere bs = osg::Group::computeBound(); + bs.radius() *= _max_v; + return bs; + } + private: - osg::Vec3 _axis, _center; + osg::Vec3 _center; + osg::Vec3 _axis; double _power, _factor, _offset, _min_v, _max_v; bool _two_sides; }; -SGFlashAnimation::SGFlashAnimation(SGPropertyNode_ptr props) - : SGAnimation( props, new SGFlashAnimationTransform(props) ) + +SGFlashAnimation::SGFlashAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot) : + SGAnimation(configNode, modelRoot) { } -SGFlashAnimation::~SGFlashAnimation() +osg::Group* +SGFlashAnimation::createAnimationGroup(osg::Group& parent) { + Transform* transform = new Transform(getConfig()); + parent.addChild(transform); + return transform; } - //////////////////////////////////////////////////////////////////////// -// Implementation of SGDistScaleAnimation +// Implementation of flash animation //////////////////////////////////////////////////////////////////////// -class SGDistScaleTransform : public osg::Transform { -public: - SGDistScaleTransform(SGPropertyNode* props) - { - getOrCreateStateSet()->setMode(GL_NORMALIZE, osg::StateAttribute::ON); - _factor = props->getFloatValue("factor", 1.0); - _offset = props->getFloatValue("offset", 0.0); - _min_v = props->getFloatValue("min", 0.0); - _max_v = props->getFloatValue("max", 1.0); - _has_min = props->hasValue("min"); - _has_max = props->hasValue("max"); - _table = read_interpolation_table(props); - _center[0] = props->getFloatValue("center/x-m", 0); - _center[1] = props->getFloatValue("center/y-m", 0); - _center[2] = props->getFloatValue("center/z-m", 0); - } - ~SGDistScaleTransform() +class SGBillboardAnimation::Transform : public osg::Transform { +public: + Transform(const SGPropertyNode* configNode) : + _spherical(configNode->getBoolValue("spherical", true)) { + setReferenceFrame(RELATIVE_RF); + setName(configNode->getStringValue("name", "billboard animation")); } - virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix, osg::NodeVisitor* nv) const { - osg::Matrix transform; - double scale_factor = computeScaleFactor(nv); - transform(0,0) = scale_factor; - transform(1,1) = scale_factor; - transform(2,2) = scale_factor; - transform(3,0) = _center[0] * ( 1 - scale_factor ); - transform(3,1) = _center[1] * ( 1 - scale_factor ); - transform(3,2) = _center[2] * ( 1 - scale_factor ); - if (_referenceFrame == RELATIVE_RF) - matrix.preMult(transform); - else - matrix = transform; + // More or less taken from plibs ssgCutout + if (_spherical) { + matrix(0,0) = 1; matrix(0,1) = 0; matrix(0,2) = 0; + matrix(1,0) = 0; matrix(1,1) = 0; matrix(1,2) = -1; + matrix(2,0) = 0; matrix(2,1) = 1; matrix(2,2) = 0; + } else { + osg::Vec3 zAxis(matrix(2, 0), matrix(2, 1), matrix(2, 2)); + osg::Vec3 xAxis = osg::Vec3(0, 0, -1)^zAxis; + osg::Vec3 yAxis = zAxis^xAxis; + + xAxis.normalize(); + yAxis.normalize(); + zAxis.normalize(); + + matrix(0,0) = xAxis[0]; matrix(0,1) = xAxis[1]; matrix(0,2) = xAxis[2]; + matrix(1,0) = yAxis[0]; matrix(1,1) = yAxis[1]; matrix(1,2) = yAxis[2]; + matrix(2,0) = zAxis[0]; matrix(2,1) = zAxis[1]; matrix(2,2) = zAxis[2]; + } return true; } virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix, osg::NodeVisitor* nv) const { - double scale_factor = computeScaleFactor(nv); - if (fabs(scale_factor) <= std::numeric_limits::min()) - return false; - osg::Matrix transform; - double rScaleFactor = 1/scale_factor; - transform(0,0) = rScaleFactor; - transform(1,1) = rScaleFactor; - transform(2,2) = rScaleFactor; - transform(3,0) = rScaleFactor*_center[0] * ( scale_factor - 1 ); - transform(3,1) = rScaleFactor*_center[1] * ( scale_factor - 1 ); - transform(3,2) = rScaleFactor*_center[2] * ( scale_factor - 1 ); - if (_referenceFrame == RELATIVE_RF) - matrix.postMult(transform); - else - matrix = transform; - return true; + // Hmm, don't yet know how to get that back ... + return false; } - double computeScaleFactor(osg::NodeVisitor* nv) const - { - if (!nv) - return 1; +private: + bool _spherical; +}; - osg::Vec3 localEyeToCenter = _center - nv->getEyePoint(); - double scale_factor = localEyeToCenter.length(); - if (_table == 0) { - scale_factor = _factor * scale_factor + _offset; - if ( _has_min && scale_factor < _min_v ) - scale_factor = _min_v; - if ( _has_max && scale_factor > _max_v ) - scale_factor = _max_v; + +SGBillboardAnimation::SGBillboardAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot) : + SGAnimation(configNode, modelRoot) +{ +} + +osg::Group* +SGBillboardAnimation::createAnimationGroup(osg::Group& parent) +{ + Transform* transform = new Transform(getConfig()); + parent.addChild(transform); + return transform; +} + + +//////////////////////////////////////////////////////////////////////// +// Implementation of a range animation +//////////////////////////////////////////////////////////////////////// + +class SGRangeAnimation::UpdateCallback : public osg::NodeCallback { +public: + UpdateCallback(const SGCondition* condition, + const SGDoubleValue* minAnimationValue, + const SGDoubleValue* maxAnimationValue, + double minValue, double maxValue) : + _condition(condition), + _minAnimationValue(minAnimationValue), + _maxAnimationValue(maxAnimationValue), + _minStaticValue(minValue), + _maxStaticValue(maxValue) + {} + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + osg::LOD* lod = static_cast(node); + if (!_condition || _condition->test()) { + double minRange; + if (_minAnimationValue) + minRange = _minAnimationValue->getValue(); + else + minRange = _minStaticValue; + double maxRange; + if (_maxAnimationValue) + maxRange = _maxAnimationValue->getValue(); + else + maxRange = _maxStaticValue; + lod->setRange(0, minRange, maxRange); } else { - scale_factor = _table->interpolate( scale_factor ); + lod->setRange(0, 0, SGLimitsf::max()); } + traverse(node, nv); + } - return scale_factor; +private: + SGSharedPtr _condition; + SGSharedPtr _minAnimationValue; + SGSharedPtr _maxAnimationValue; + double _minStaticValue; + double _maxStaticValue; +}; + +SGRangeAnimation::SGRangeAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot) : + SGAnimation(configNode, modelRoot) +{ + _condition = getCondition(); + + std::string inputPropertyName; + inputPropertyName = configNode->getStringValue("min-property", ""); + if (!inputPropertyName.empty()) { + SGPropertyNode* inputProperty; + inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true); + SGScaleOffsetValue* value = new SGScaleOffsetValue(inputProperty); + value->setScale(configNode->getDoubleValue("min-factor", 1)); + _minAnimationValue = value; + } + inputPropertyName = configNode->getStringValue("max-property", ""); + if (!inputPropertyName.empty()) { + SGPropertyNode* inputProperty; + inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true); + SGScaleOffsetValue* value = new SGScaleOffsetValue(inputProperty); + value->setScale(configNode->getDoubleValue("max-factor", 1)); + _maxAnimationValue = value; } + _initialValue[0] = configNode->getDoubleValue("min-m", 0); + _initialValue[0] *= configNode->getDoubleValue("min-factor", 1); + _initialValue[1] = configNode->getDoubleValue("max-m", SGLimitsf::max()); + _initialValue[1] *= configNode->getDoubleValue("max-factor", 1); +} + +osg::Group* +SGRangeAnimation::createAnimationGroup(osg::Group& parent) +{ + osg::Group* group = new osg::Group; + group->setName("range animation group"); + + osg::LOD* lod = new osg::LOD; + lod->setName("range animation node"); + parent.addChild(lod); + + lod->addChild(group, _initialValue[0], _initialValue[1]); + lod->setCenterMode(osg::LOD::USE_BOUNDING_SPHERE_CENTER); + lod->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT); + if (_minAnimationValue || _maxAnimationValue || _condition) { + UpdateCallback* uc; + uc = new UpdateCallback(_condition, _minAnimationValue, _maxAnimationValue, + _initialValue[0], _initialValue[1]); + lod->setUpdateCallback(uc); + } + return group; +} + + +//////////////////////////////////////////////////////////////////////// +// Implementation of a select animation +//////////////////////////////////////////////////////////////////////// + +class SGSelectAnimation::UpdateCallback : public osg::NodeCallback { +public: + UpdateCallback(const SGCondition* condition) : + _condition(condition) + {} + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + osg::Switch* sw = static_cast(node); + if (_condition->test()) + sw->setAllChildrenOn(); + else + sw->setAllChildrenOff(); + traverse(node, nv); + } private: - osg::Vec3 _center; - float _factor, _offset, _min_v, _max_v; - bool _has_min, _has_max; - SGSharedPtr _table; + SGSharedPtr _condition; }; -SGDistScaleAnimation::SGDistScaleAnimation(SGPropertyNode_ptr props) - : SGAnimation( props, new SGDistScaleTransform(props) ) +SGSelectAnimation::SGSelectAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot) : + SGAnimation(configNode, modelRoot) { } -SGDistScaleAnimation::~SGDistScaleAnimation() +osg::Group* +SGSelectAnimation::createAnimationGroup(osg::Group& parent) { + // if no condition given, this is a noop. + SGSharedPtr condition = getCondition(); + // trick, gets deleted with all its 'animated' children + // when the animation installer returns + if (!condition) + return new osg::Group; + + osg::Switch* sw = new osg::Switch; + sw->setName("select animation node"); + sw->setUpdateCallback(new UpdateCallback(condition)); + parent.addChild(sw); + return sw; } + + //////////////////////////////////////////////////////////////////////// -// Implementation of SGShadowAnimation +// Implementation of alpha test animation //////////////////////////////////////////////////////////////////////// -SGShadowAnimation::SGShadowAnimation ( SGPropertyNode *prop_root, - SGPropertyNode_ptr props ) - : SGAnimation(props, new osg::Group), - _condition(0), - _condition_value(true) +SGAlphaTestAnimation::SGAlphaTestAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot) : + SGAnimation(configNode, modelRoot) +{ +} + +void +SGAlphaTestAnimation::install(osg::Node& node) { - animation_type = 1; - SGPropertyNode_ptr node = props->getChild("condition"); - if (node != 0) { - _condition = sgReadCondition(prop_root, node); - _condition_value = false; + SGAnimation::install(node); + + cloneDrawables(node); + removeMode(node, GL_ALPHA_TEST); + removeAttribute(node, osg::StateAttribute::ALPHAFUNC); + + osg::StateSet* stateSet = node.getOrCreateStateSet(); + osg::AlphaFunc* alphaFunc = new osg::AlphaFunc; + alphaFunc->setFunction(osg::AlphaFunc::GREATER); + float alphaClamp = getConfig()->getFloatValue("alpha-factor", 0); + alphaFunc->setReferenceValue(alphaClamp); + stateSet->setAttribute(alphaFunc); + stateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::ON); +} + + + +////////////////////////////////////////////////////////////////////// +// Blend animation installer +////////////////////////////////////////////////////////////////////// + +class SGBlendAnimation::BlendVisitor : public osg::NodeVisitor { +public: + BlendVisitor(float blend) : + osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), + _blend(blend) + { setVisitorType(osg::NodeVisitor::NODE_VISITOR); } + virtual void apply(osg::Node& node) + { + updateStateSet(node.getStateSet()); + traverse(node); + } + virtual void apply(osg::Geode& node) + { + apply((osg::Node&)node); + unsigned nDrawables = node.getNumDrawables(); + for (unsigned i = 0; i < nDrawables; ++i) { + osg::Drawable* drawable = node.getDrawable(i); + updateStateSet(drawable->getStateSet()); + osg::Geometry* geometry = drawable->asGeometry(); + if (!geometry) + continue; + osg::Array* array = geometry->getColorArray(); + if (!array) + continue; + osg::Vec4Array* vec4Array = dynamic_cast(array); + if (!vec4Array) + continue; + geometry->dirtyDisplayList(); + vec4Array->dirty(); + for (unsigned k = 0; k < vec4Array->size(); ++k) { + (*vec4Array)[k][3] = _blend; + } + } + } + void updateStateSet(osg::StateSet* stateSet) + { + if (!stateSet) + return; + osg::StateAttribute* stateAttribute; + stateAttribute = stateSet->getAttribute(osg::StateAttribute::MATERIAL); + if (!stateAttribute) + return; + osg::Material* material = dynamic_cast(stateAttribute); + if (!material) + return; + material->setAlpha(osg::Material::FRONT_AND_BACK, _blend); + if (_blend < 1) { + stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); + stateSet->setMode(GL_BLEND, osg::StateAttribute::ON); + } else { + stateSet->setRenderingHint(osg::StateSet::DEFAULT_BIN); + } + } +private: + float _blend; +}; + +class SGBlendAnimation::UpdateCallback : public osg::NodeCallback { +public: + UpdateCallback(const SGPropertyNode* configNode, const SGDoubleValue* v) : + _prev_value(-1), + _animationValue(v) + { } + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + double blend = _animationValue->getValue(); + if (blend != _prev_value) { + _prev_value = blend; + BlendVisitor visitor(1-blend); + node->accept(visitor); } + traverse(node, nv); + } +public: + double _prev_value; + SGSharedPtr _animationValue; +}; + + +SGBlendAnimation::SGBlendAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot) + : SGAnimation(configNode, modelRoot), + _animationValue(read_value(configNode, modelRoot, "", 0, 1)) +{ } -SGShadowAnimation::~SGShadowAnimation () +osg::Group* +SGBlendAnimation::createAnimationGroup(osg::Group& parent) { + if (!_animationValue) + return 0; + + osg::Group* group = new osg::Switch; + group->setName("blend animation node"); + group->setUpdateCallback(new UpdateCallback(getConfig(), _animationValue)); + parent.addChild(group); + return group; } void -SGShadowAnimation::operator()(osg::Node* node, osg::NodeVisitor* nv) +SGBlendAnimation::install(osg::Node& node) { - if (_condition) - _condition_value = _condition->test(); + SGAnimation::install(node); + // make sure we do not change common geometries, + // that also creates new display lists for these subgeometries. + cloneDrawables(node); +} + + +////////////////////////////////////////////////////////////////////// +// Timed animation installer +////////////////////////////////////////////////////////////////////// + + + +class SGTimedAnimation::UpdateCallback : public osg::NodeCallback { +public: + UpdateCallback(const SGPropertyNode* configNode) : + _current_index(0), + _reminder(0), + _duration_sec(configNode->getDoubleValue("duration-sec", 1)), + _last_time_sec(SGLimitsd::max()), + _use_personality(configNode->getBoolValue("use-personality", false)) + { + std::vector > nodes; + nodes = configNode->getChildren("branch-duration-sec"); + for (size_t i = 0; i < nodes.size(); ++i) { + unsigned ind = nodes[ i ]->getIndex(); + while ( ind >= _durations.size() ) { + _durations.push_back(DurationSpec(_duration_sec)); + } + SGPropertyNode_ptr rNode = nodes[i]->getChild("random"); + if ( rNode == 0 ) { + _durations[ind] = DurationSpec(nodes[ i ]->getDoubleValue()); + } else { + _durations[ind] = DurationSpec(rNode->getDoubleValue( "min", 0), + rNode->getDoubleValue( "max", 1)); + } + } + } + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + assert(dynamic_cast(node)); + osg::Switch* sw = static_cast(node); + + unsigned nChildren = sw->getNumChildren(); + + // blow up the durations vector to the required size + while (_durations.size() < nChildren) { + _durations.push_back(_duration_sec); + } + // make sure the current index is an duration that really exists + _current_index = _current_index % nChildren; - if ( _condition_value ) { - _branch->setNodeMask(SG_NODEMASK_SHADOW_BIT|_branch->getNodeMask()); + // update the time and compute the current systems time value + double t = nv->getFrameStamp()->getReferenceTime(); + if (_last_time_sec == SGLimitsd::max()) { + _last_time_sec = t; } else { - _branch->setNodeMask(~SG_NODEMASK_SHADOW_BIT&_branch->getNodeMask()); + double dt = t - _last_time_sec; + if (_use_personality) + dt *= 1 + 0.2*(0.5 - sg_random()); + _reminder += dt; + _last_time_sec = t; + } + + double currentDuration = _durations[_current_index].get(); + while (currentDuration < _reminder) { + _reminder -= currentDuration; + _current_index = (_current_index + 1) % nChildren; + currentDuration = _durations[_current_index].get(); } + + sw->setSingleChildOn(_current_index); + + traverse(node, nv); + } + +private: + struct DurationSpec { + DurationSpec(double t) : + minTime(SGMiscd::max(0.01, t)), + maxTime(SGMiscd::max(0.01, t)) + {} + DurationSpec(double t0, double t1) : + minTime(SGMiscd::max(0.01, t0)), + maxTime(SGMiscd::max(0.01, t1)) + {} + double get() const + { return minTime + sg_random()*(maxTime - minTime); } + double minTime; + double maxTime; + }; + std::vector _durations; + unsigned _current_index; + double _reminder; + double _duration_sec; + double _last_time_sec; + bool _use_personality; +}; + + +SGTimedAnimation::SGTimedAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot) + : SGAnimation(configNode, modelRoot) +{ +} + +osg::Group* +SGTimedAnimation::createAnimationGroup(osg::Group& parent) +{ + osg::Switch* sw = new osg::Switch; + sw->setName("timed animation node"); + sw->setUpdateCallback(new UpdateCallback(getConfig())); + parent.addChild(sw); + return sw; +} + + +//////////////////////////////////////////////////////////////////////// +// dynamically switch on/off shadows +//////////////////////////////////////////////////////////////////////// + +class SGShadowAnimation::UpdateCallback : public osg::NodeCallback { +public: + UpdateCallback(const SGCondition* condition) : + _condition(condition) + {} + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + if (_condition->test()) + node->setNodeMask( SG_NODEMASK_SHADOW_BIT | node->getNodeMask()); + else + node->setNodeMask(~SG_NODEMASK_SHADOW_BIT & node->getNodeMask()); traverse(node, nv); + } + +private: + SGSharedPtr _condition; +}; + +SGShadowAnimation::SGShadowAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot) : + SGAnimation(configNode, modelRoot) +{ +} + +osg::Group* +SGShadowAnimation::createAnimationGroup(osg::Group& parent) +{ + SGSharedPtr condition = getCondition(); + if (!condition) + return 0; + + osg::Group* group = new osg::Group; + group->setName("shadow animation"); + group->setUpdateCallback(new UpdateCallback(condition)); + parent.addChild(group); + return group; +} + + +//////////////////////////////////////////////////////////////////////// +// Implementation of SGTexTransformAnimation +//////////////////////////////////////////////////////////////////////// + +class SGTexTransformAnimation::Transform : public SGReferenced { +public: + Transform() : + _value(0) + {} + virtual ~Transform() + { } + void setValue(double value) + { _value = value; } + virtual bool transform(osg::Matrix&) = 0; +protected: + double _value; +}; + +class SGTexTransformAnimation::Translation : + public SGTexTransformAnimation::Transform { +public: + Translation(const SGVec3d& axis) : + _axis(axis) + { } + void setValue(double value) + { _value = value; } + virtual bool transform(osg::Matrix& matrix) + { + osg::Matrix tmp; + set_translation(tmp, _value, _axis); + matrix.preMult(tmp); + } +private: + SGVec3d _axis; +}; + +class SGTexTransformAnimation::Rotation : + public SGTexTransformAnimation::Transform { +public: + Rotation(const SGVec3d& axis, const SGVec3d& center) : + _axis(axis), + _center(center) + { } + virtual bool transform(osg::Matrix& matrix) + { + osg::Matrix tmp; + set_rotation(tmp, _value, _center, _axis); + matrix.preMult(tmp); + } +private: + SGVec3d _axis; + SGVec3d _center; +}; + +class SGTexTransformAnimation::UpdateCallback : + public osg::StateAttribute::Callback { +public: + UpdateCallback(const SGCondition* condition) : + _condition(condition) + { } + virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor*) + { + if (!_condition || _condition->test()) { + TransformList::const_iterator i; + for (i = _transforms.begin(); i != _transforms.end(); ++i) + i->transform->setValue(i->value->getValue()); + } + assert(dynamic_cast(sa)); + osg::TexMat* texMat = static_cast(sa); + texMat->getMatrix().makeIdentity(); + TransformList::const_iterator i; + for (i = _transforms.begin(); i != _transforms.end(); ++i) + i->transform->transform(texMat->getMatrix()); + } + void appendTransform(Transform* transform, SGDoubleValue* value) + { + Entry entry = { transform, value }; + transform->transform(_matrix); + _transforms.push_back(entry); + } + +private: + struct Entry { + SGSharedPtr transform; + SGSharedPtr value; + }; + typedef std::vector TransformList; + TransformList _transforms; + SGSharedPtr _condition; + osg::Matrix _matrix; +}; + +SGTexTransformAnimation::SGTexTransformAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot) : + SGAnimation(configNode, modelRoot) +{ +} + +osg::Group* +SGTexTransformAnimation::createAnimationGroup(osg::Group& parent) +{ + osg::Group* group = new osg::Group; + group->setName("texture transform group"); + osg::StateSet* stateSet = group->getOrCreateStateSet(); + osg::TexMat* texMat = new osg::TexMat; + UpdateCallback* updateCallback = new UpdateCallback(getCondition()); + // interpret the configs ... + std::string type = getType(); + + if (type == "textranslate") { + appendTexTranslate(getConfig(), updateCallback); + } else if (type == "texrotate") { + appendTexRotate(getConfig(), updateCallback); + } else if (type == "texmultiple") { + std::vector > transformConfigs; + transformConfigs = getConfig()->getChildren("transform"); + for (unsigned i = 0; i < transformConfigs.size(); ++i) { + std::string subtype = transformConfigs[i]->getStringValue("subtype", ""); + if (subtype == "textranslate") + appendTexTranslate(transformConfigs[i], updateCallback); + else if (subtype == "texrotate") + appendTexRotate(transformConfigs[i], updateCallback); + else + SG_LOG(SG_INPUT, SG_ALERT, + "Ignoring unknown texture transform subtype"); + } + } else { + SG_LOG(SG_INPUT, SG_ALERT, "Ignoring unknown texture transform type"); + } + + texMat->setUpdateCallback(updateCallback); + stateSet->setTextureAttribute(0, texMat); + parent.addChild(group); + return group; } -bool SGShadowAnimation::get_condition_value(void) { - return _condition_value; +void +SGTexTransformAnimation::appendTexTranslate(const SGPropertyNode* config, + UpdateCallback* updateCallback) +{ + std::string propertyName = config->getStringValue("property", "/null"); + SGPropertyNode* inputNode; + inputNode = getModelRoot()->getNode(propertyName.c_str(), true); + + SGDoubleValue* animationValue; + SGInterpTable* table = read_interpolation_table(config); + if (table) { + SGTexTableValue* value; + value = new SGTexTableValue(inputNode, table); + value->setStep(config->getDoubleValue("step", 0)); + value->setScroll(config->getDoubleValue("scroll", 0)); + animationValue = value; + } else { + SGTexScaleOffsetValue* value; + value = new SGTexScaleOffsetValue(inputNode); + value->setScale(config->getDoubleValue("factor", 1)); + value->setOffset(config->getDoubleValue("offset", 0)); + value->setStep(config->getDoubleValue("step", 0)); + value->setScroll(config->getDoubleValue("scroll", 0)); + value->setMin(config->getDoubleValue("min", -SGLimitsd::max())); + value->setMax(config->getDoubleValue("max", SGLimitsd::max())); + animationValue = value; + } + SGVec3d axis(getConfig()->getDoubleValue("axis/x", 0), + getConfig()->getDoubleValue("axis/y", 0), + getConfig()->getDoubleValue("axis/z", 0)); + Translation* translation; + translation = new Translation(normalize(axis)); + translation->setValue(config->getDoubleValue("starting-position", 0)); + updateCallback->appendTransform(translation, animationValue); +} + +void +SGTexTransformAnimation::appendTexRotate(const SGPropertyNode* config, + UpdateCallback* updateCallback) +{ + std::string propertyName = config->getStringValue("property", "/null"); + SGPropertyNode* inputNode; + inputNode = getModelRoot()->getNode(propertyName.c_str(), true); + + SGDoubleValue* animationValue; + SGInterpTable* table = read_interpolation_table(config); + if (table) { + SGTexTableValue* value; + value = new SGTexTableValue(inputNode, table); + value->setStep(config->getDoubleValue("step", 0)); + value->setScroll(config->getDoubleValue("scroll", 0)); + animationValue = value; + } else { + SGTexScaleOffsetValue* value; + value = new SGTexScaleOffsetValue(inputNode); + value->setScale(config->getDoubleValue("factor", 1)); + value->setOffset(config->getDoubleValue("offset-deg", 0)); + value->setStep(config->getDoubleValue("step", 0)); + value->setScroll(config->getDoubleValue("scroll", 0)); + value->setMin(config->getDoubleValue("min-deg", -SGLimitsd::max())); + value->setMax(config->getDoubleValue("max-deg", SGLimitsd::max())); + animationValue = value; + } + SGVec3d axis(getConfig()->getDoubleValue("axis/x", 0), + getConfig()->getDoubleValue("axis/y", 0), + getConfig()->getDoubleValue("axis/z", 0)); + SGVec3d center(getConfig()->getDoubleValue("center/x", 0), + getConfig()->getDoubleValue("center/y", 0), + getConfig()->getDoubleValue("center/z", 0)); + Rotation* rotation; + rotation = new Rotation(normalize(axis), center); + rotation->setValue(config->getDoubleValue("starting-position-deg", 0)); + updateCallback->appendTransform(rotation, animationValue); } -// end of animation.cxx diff --git a/simgear/scene/model/animation.hxx b/simgear/scene/model/animation.hxx index 4e83468b..f23b6923 100644 --- a/simgear/scene/model/animation.hxx +++ b/simgear/scene/model/animation.hxx @@ -31,9 +31,11 @@ #include #include +#include #include #include + SG_USING_STD(vector); SG_USING_STD(map); @@ -52,554 +54,310 @@ class SGCondition; ////////////////////////////////////////////////////////////////////// -// Animation classes +// Helper classes, FIXME: factor out ////////////////////////////////////////////////////////////////////// -/** - * Abstract base class for all animations. - */ -class SGAnimation : public osg::NodeCallback -{ +class SGDoubleValue : public SGReferenced { public: - SGAnimation (SGPropertyNode_ptr props, osg::Group * branch); - - virtual ~SGAnimation (); - - /** - * Get the SSG branch holding the animation. - */ - virtual osg::Group * getBranch () { return _branch; } - - /** - * Initialize the animation, after children have been added. - */ - virtual void init (); + virtual ~SGDoubleValue() {} + virtual double getValue() const = 0; +}; - /** - * Update the animation. - */ - virtual void operator()(osg::Node* node, osg::NodeVisitor* nv); + +////////////////////////////////////////////////////////////////////// +// Base class for animation installers +////////////////////////////////////////////////////////////////////// - /** - * Restore the state after the animation. - */ - virtual void restore(); +class SGAnimation : protected osg::NodeVisitor { +public: + SGAnimation(const SGPropertyNode* configNode, SGPropertyNode* modelRoot); + virtual ~SGAnimation(); - int get_animation_type(void) { return animation_type; } + static bool animate(osg::Node* node, const SGPropertyNode* configNode, + SGPropertyNode* modelRoot); protected: + void apply(osg::Node* node); - osg::Group* _branch; + virtual void install(osg::Node& node); + virtual osg::Group* createAnimationGroup(osg::Group& parent); - int animation_type; -}; + virtual void apply(osg::Group& group); + void removeMode(osg::Node& node, osg::StateAttribute::GLMode mode); + void removeAttribute(osg::Node& node, osg::StateAttribute::Type type); + void removeTextureMode(osg::Node& node, unsigned unit, + osg::StateAttribute::GLMode mode); + void removeTextureAttribute(osg::Node& node, unsigned unit, + osg::StateAttribute::Type type); + void setRenderBinToInherit(osg::Node& node); + void cloneDrawables(osg::Node& node); -/** - * A no-op animation. - */ -class SGNullAnimation : public SGAnimation -{ -public: - SGNullAnimation (SGPropertyNode_ptr props); - virtual ~SGNullAnimation (); -}; + std::string getType() const + { return std::string(_configNode->getStringValue("type", "")); } + const SGPropertyNode* getConfig() const + { return _configNode; } + SGPropertyNode* getModelRoot() const + { return _modelRoot; } + + const SGCondition* getCondition() const; -/** - * A range, or level-of-detail (LOD) animation. - */ -class SGRangeAnimation : public SGAnimation -{ -public: - SGRangeAnimation (SGPropertyNode *prop_root, - SGPropertyNode_ptr props); - virtual ~SGRangeAnimation (); - virtual void operator()(osg::Node* node, osg::NodeVisitor* nv); private: - SGPropertyNode_ptr _min_prop; - SGPropertyNode_ptr _max_prop; - float _min; - float _max; - float _min_factor; - float _max_factor; - SGSharedPtr _condition; + void installInGroup(const std::string& name, osg::Group& group, + osg::ref_ptr& animationGroup); + + class RemoveModeVisitor; + class RemoveAttributeVisitor; + class RemoveTextureModeVisitor; + class RemoveTextureAttributeVisitor; + class BinToInheritVisitor; + class DrawableCloneVisitor; + + bool _found; + std::string _name; + SGSharedPtr _configNode; + SGPropertyNode* _modelRoot; + std::list _objectNames; + bool _enableHOT; + bool _disableShadow; }; + +////////////////////////////////////////////////////////////////////// +// Null animation installer +////////////////////////////////////////////////////////////////////// -/** - * Animation to turn and face the screen. - */ -class SGBillboardAnimation : public SGAnimation -{ +class SGGroupAnimation : public SGAnimation { public: - SGBillboardAnimation (SGPropertyNode_ptr props); - virtual ~SGBillboardAnimation (); + SGGroupAnimation(const SGPropertyNode*, SGPropertyNode*); + virtual osg::Group* createAnimationGroup(osg::Group& parent); }; + +////////////////////////////////////////////////////////////////////// +// Translate animation installer +////////////////////////////////////////////////////////////////////// -/** - * Animation to select alternative versions of the same object. - */ -class SGSelectAnimation : public SGAnimation -{ +class SGTranslateAnimation : public SGAnimation { public: - SGSelectAnimation( SGPropertyNode *prop_root, - SGPropertyNode_ptr props ); - virtual ~SGSelectAnimation (); - virtual void operator()(osg::Node* node, osg::NodeVisitor* nv); + SGTranslateAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot); + virtual osg::Group* createAnimationGroup(osg::Group& parent); private: - SGSharedPtr _condition; + class UpdateCallback; + class Transform; + SGSharedPtr _condition; + SGSharedPtr _animationValue; + SGVec3d _axis; + double _initialValue; }; + +////////////////////////////////////////////////////////////////////// +// Rotate/Spin animation installer +////////////////////////////////////////////////////////////////////// -/** - * Animation to spin an object around a center point. - * - * This animation rotates at a specific velocity. - */ -class SGSpinAnimation : public SGAnimation -{ +class SGRotateAnimation : public SGAnimation { public: - SGSpinAnimation( SGPropertyNode *prop_root, - SGPropertyNode_ptr props, - double sim_time_sec ); - virtual ~SGSpinAnimation (); - virtual void operator()(osg::Node* node, osg::NodeVisitor* nv); + SGRotateAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot); + virtual osg::Group* createAnimationGroup(osg::Group& parent); private: - bool _use_personality; - SGPropertyNode_ptr _prop; - SGPersonalityParameter _factor; - SGPersonalityParameter _position_deg; - double _last_time_sec; - osg::Vec3 _center; - osg::Vec3 _axis; - SGSharedPtr _condition; + class UpdateCallback; + class SpinUpdateCallback; + class Transform; + SGSharedPtr _condition; + SGSharedPtr _animationValue; + SGVec3d _axis; + SGVec3d _center; + double _initialValue; + bool _isSpin; }; + +////////////////////////////////////////////////////////////////////// +// Scale animation installer +////////////////////////////////////////////////////////////////////// -/** - * Animation to draw objects for a specific amount of time each. - */ -class SGTimedAnimation : public SGAnimation -{ +class SGScaleAnimation : public SGAnimation { public: - SGTimedAnimation (SGPropertyNode_ptr props); - virtual ~SGTimedAnimation (); - virtual void init(); - virtual void operator()(osg::Node* node, osg::NodeVisitor* nv); + SGScaleAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot); + virtual osg::Group* createAnimationGroup(osg::Group& parent); private: - bool _use_personality; - double _duration_sec; - double _last_time_sec; - double _total_duration_sec; - unsigned _step; - struct DurationSpec { - DurationSpec( double m = 0.0 ) : _min(m), _max(m) {} - DurationSpec( double m1, double m2 ) : _min(m1), _max(m2) {} - double _min, _max; - }; - vector _branch_duration_specs; - vector _branch_duration_sec; + class UpdateCallback; + class Transform; + SGSharedPtr _condition; + SGSharedPtr _animationValue[3]; + SGVec3d _initialValue; + SGVec3d _center; }; + +////////////////////////////////////////////////////////////////////// +// dist scale animation installer +////////////////////////////////////////////////////////////////////// -/** - * Animation to rotate an object around a center point. - * - * This animation rotates to a specific position. - */ -class SGRotateAnimation : public SGAnimation -{ +class SGDistScaleAnimation : public SGAnimation { public: - SGRotateAnimation( SGPropertyNode *prop_root, SGPropertyNode_ptr props ); - virtual ~SGRotateAnimation (); - virtual void operator()(osg::Node* node, osg::NodeVisitor* nv); + SGDistScaleAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot); + virtual osg::Group* createAnimationGroup(osg::Group& parent); private: - SGPropertyNode_ptr _prop; - double _offset_deg; - double _factor; - SGSharedPtr _table; - bool _has_min; - double _min_deg; - bool _has_max; - double _max_deg; - double _position_deg; - osg::Vec3 _center; - osg::Vec3 _axis; - SGSharedPtr _condition; + class Transform; }; + +////////////////////////////////////////////////////////////////////// +// dist scale animation installer +////////////////////////////////////////////////////////////////////// -/** - * Animation to slide along an axis. - */ -class SGTranslateAnimation : public SGAnimation -{ +class SGFlashAnimation : public SGAnimation { public: - SGTranslateAnimation( SGPropertyNode *prop_root, - SGPropertyNode_ptr props ); - virtual ~SGTranslateAnimation (); - virtual void operator()(osg::Node* node, osg::NodeVisitor* nv); + SGFlashAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot); + virtual osg::Group* createAnimationGroup(osg::Group& parent); private: - bool _use_personality; - SGPropertyNode_ptr _prop; - SGPersonalityParameter _offset_m; - SGPersonalityParameter _factor; - SGSharedPtr _table; - bool _has_min; - double _min_m; - bool _has_max; - double _max_m; - double _position_m; - osg::Vec3 _axis; - SGSharedPtr _condition; + class Transform; }; -/** - * Animation to blend an object. - */ -class SGBlendAnimation : public SGAnimation -{ -public: - SGBlendAnimation( SGPropertyNode *prop_root, - SGPropertyNode_ptr props ); - virtual ~SGBlendAnimation (); - virtual void operator()(osg::Node* node, osg::NodeVisitor* nv); -private: - bool _use_personality; - SGPropertyNode_ptr _prop; - SGSharedPtr _table; - double _prev_value; - SGPersonalityParameter _offset; - SGPersonalityParameter _factor; - double _min; - double _max; -}; + +////////////////////////////////////////////////////////////////////// +// dist scale animation installer +////////////////////////////////////////////////////////////////////// -/** - * Animation to scale an object. - */ -class SGScaleAnimation : public SGAnimation -{ +class SGBillboardAnimation : public SGAnimation { public: - SGScaleAnimation( SGPropertyNode *prop_root, - SGPropertyNode_ptr props ); - virtual ~SGScaleAnimation (); - virtual void operator()(osg::Node* node, osg::NodeVisitor* nv); + SGBillboardAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot); + virtual osg::Group* createAnimationGroup(osg::Group& parent); private: - bool _use_personality; - SGPropertyNode_ptr _prop; - SGPersonalityParameter _x_factor; - SGPersonalityParameter _y_factor; - SGPersonalityParameter _z_factor; - SGPersonalityParameter _x_offset; - SGPersonalityParameter _y_offset; - SGPersonalityParameter _z_offset; - SGSharedPtr _table; - bool _has_min_x; - bool _has_min_y; - bool _has_min_z; - double _min_x; - double _min_y; - double _min_z; - bool _has_max_x; - bool _has_max_y; - bool _has_max_z; - double _max_x; - double _max_y; - double _max_z; - double _x_scale; - double _y_scale; - double _z_scale; + class Transform; }; -/** - * Animation to rotate texture mappings around a center point. - * - * This animation rotates to a specific position. - */ -class SGTexRotateAnimation : public SGAnimation -{ + +////////////////////////////////////////////////////////////////////// +// Range animation installer +////////////////////////////////////////////////////////////////////// + +class SGRangeAnimation : public SGAnimation { public: - SGTexRotateAnimation( SGPropertyNode *prop_root, SGPropertyNode_ptr props ); - virtual ~SGTexRotateAnimation (); - virtual void operator()(osg::Node* node, osg::NodeVisitor* nv); + SGRangeAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot); + virtual osg::Group* createAnimationGroup(osg::Group& parent); private: - SGPropertyNode_ptr _prop; - double _offset_deg; - double _factor; - SGSharedPtr _table; - bool _has_min; - double _min_deg; - bool _has_max; - double _max_deg; - double _position_deg; - osg::Vec3 _center; - osg::Vec3 _axis; - SGSharedPtr _condition; - osg::ref_ptr _texMat; + class UpdateCallback; + SGSharedPtr _condition; + SGSharedPtr _minAnimationValue; + SGSharedPtr _maxAnimationValue; + SGVec2d _initialValue; }; + +////////////////////////////////////////////////////////////////////// +// Select animation installer +////////////////////////////////////////////////////////////////////// -/** - * Animation to slide texture mappings along an axis. - */ -class SGTexTranslateAnimation : public SGAnimation -{ +class SGSelectAnimation : public SGAnimation { public: - SGTexTranslateAnimation( SGPropertyNode *prop_root, - SGPropertyNode_ptr props ); - virtual ~SGTexTranslateAnimation (); - virtual void operator()(osg::Node* node, osg::NodeVisitor* nv); + SGSelectAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot); + virtual osg::Group* createAnimationGroup(osg::Group& parent); private: - SGPropertyNode_ptr _prop; - double _offset; - double _factor; - double _step; - double _scroll; - SGSharedPtr _table; - bool _has_min; - double _min; - bool _has_max; - double _max; - double _position; - osg::Vec3 _axis; - SGSharedPtr _condition; - osg::ref_ptr _texMat; + class UpdateCallback; }; + +////////////////////////////////////////////////////////////////////// +// Alpha test animation installer +////////////////////////////////////////////////////////////////////// - -/** - * Classes for handling multiple types of Texture translations on one object - */ - -class SGTexMultipleAnimation : public SGAnimation -{ +class SGAlphaTestAnimation : public SGAnimation { public: - SGTexMultipleAnimation( SGPropertyNode *prop_root, - SGPropertyNode_ptr props ); - virtual ~SGTexMultipleAnimation (); - virtual void operator()(osg::Node* node, osg::NodeVisitor* nv); -private: - class TexTransform - { - public: - SGPropertyNode_ptr prop; - int subtype; // 0=translation, 1=rotation - double offset; - double factor; - double step; - double scroll; - SGSharedPtr table; - bool has_min; - double min; - bool has_max; - double max; - double position; - osg::Vec3 center; - osg::Vec3 axis; - }; - SGPropertyNode_ptr _prop; - TexTransform* _transform; - int _num_transforms; - osg::ref_ptr _texMat; + SGAlphaTestAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot); + virtual void install(osg::Node& node); }; + +////////////////////////////////////////////////////////////////////// +// Blend animation installer +////////////////////////////////////////////////////////////////////// -/** - * An "animation" to enable the alpha test - */ -class SGAlphaTestAnimation : public SGAnimation -{ +class SGBlendAnimation : public SGAnimation { public: - SGAlphaTestAnimation(SGPropertyNode_ptr props); - virtual ~SGAlphaTestAnimation (); - virtual void init(); + SGBlendAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot); + virtual osg::Group* createAnimationGroup(osg::Group& parent); + virtual void install(osg::Node& node); private: - float _alpha_clamp; + class BlendVisitor; + class UpdateCallback; + SGSharedPtr _animationValue; }; + +////////////////////////////////////////////////////////////////////// +// Timed animation installer +////////////////////////////////////////////////////////////////////// -/** - * An "animation" to modify material properties - */ -class SGMaterialAnimation : public SGAnimation -{ +class SGTimedAnimation : public SGAnimation { public: - SGMaterialAnimation(SGPropertyNode *prop_root, SGPropertyNode_ptr props, - const SGPath &texpath); - virtual ~SGMaterialAnimation(); - virtual void init(); - virtual void operator()(osg::Node* node, osg::NodeVisitor* nv); + SGTimedAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot); + virtual osg::Group* createAnimationGroup(osg::Group& parent); private: - enum { - DIFFUSE = 1, - AMBIENT = 2, - SPECULAR = 4, - EMISSION = 8, - SHININESS = 16, - TRANSPARENCY = 32, - THRESHOLD = 64, - TEXTURE = 128, - }; - struct ColorSpec { - float red, green, blue; - float factor; - float offset; - SGPropertyNode_ptr red_prop; - SGPropertyNode_ptr green_prop; - SGPropertyNode_ptr blue_prop; - SGPropertyNode_ptr factor_prop; - SGPropertyNode_ptr offset_prop; - osg::Vec4 v; - inline bool dirty() { - return red >= 0.0 || green >= 0.0 || blue >= 0.0; - } - inline bool live() { - return red_prop || green_prop || blue_prop - || factor_prop || offset_prop; - } - inline bool operator!=(ColorSpec& a) { - return red != a.red || green != a.green || blue != a.blue - || factor != a.factor || offset != a.offset; - } - osg::Vec4 &rgba() { - v[0] = clamp(red * factor + offset); - v[1] = clamp(green * factor + offset); - v[2] = clamp(blue * factor + offset); - v[3] = 1.0; - return v; - } - inline float clamp(float val) { - return val < 0.0 ? 0.0 : val > 1.0 ? 1.0 : val; - } - }; - struct PropSpec { - float value; - float factor; - float offset; - float min; - float max; - SGPropertyNode_ptr value_prop; - SGPropertyNode_ptr factor_prop; - SGPropertyNode_ptr offset_prop; - inline bool dirty() { return value >= 0.0; } - inline bool live() { return value_prop || factor_prop || offset_prop; } - inline bool operator!=(PropSpec& a) { - return value != a.value || factor != a.factor || offset != a.offset; - } - }; - SGSharedPtr _condition; - bool _last_condition; - SGPropertyNode_ptr _prop_root; - string _prop_base; - SGPath _texture_base; - SGPath _texture; - string _texture_str; - unsigned _read; - unsigned _update; - unsigned _static_update; - bool _global; - ColorSpec _diff; - ColorSpec _amb; - ColorSpec _emis; - ColorSpec _spec; - float _shi; - PropSpec _trans; - float _thresh; // alpha_clamp (see man glAlphaFunc) - string _tex; - string _tmpstr; - SGPropertyNode_ptr _shi_prop; - SGPropertyNode_ptr _thresh_prop; - SGPropertyNode_ptr _tex_prop; - std::vector > _materialList; - osg::ref_ptr _alphaFunc; - osg::ref_ptr _texture2D; - - void cloneMaterials(osg::Group *b); - void setMaterialBranch(osg::Group *b); - void initColorGroup(SGPropertyNode_ptr, ColorSpec *, int flag); - void updateColorGroup(ColorSpec *, int flag); - inline float clamp(float val, float min = 0.0, float max = 1.0) { - return val < min ? min : val > max ? max : val; - } - const char *path(const char *rel) { - return (_tmpstr = _prop_base + rel).c_str(); - } + class UpdateCallback; }; + +////////////////////////////////////////////////////////////////////// +// Shadow animation installer +////////////////////////////////////////////////////////////////////// -/** - * An "animation" that compute a scale according to - * the angle between an axis and the view direction - */ -class SGFlashAnimation : public SGAnimation -{ +class SGShadowAnimation : public SGAnimation { public: - SGFlashAnimation(SGPropertyNode_ptr props); - virtual ~SGFlashAnimation (); + SGShadowAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot); + virtual osg::Group* createAnimationGroup(osg::Group& parent); +private: + class UpdateCallback; }; + +////////////////////////////////////////////////////////////////////// +// TextureTransform animation +////////////////////////////////////////////////////////////////////// -/** - * An animation that compute a scale according to - * the distance from a point and the viewer - */ -class SGDistScaleAnimation : public SGAnimation -{ -public: - SGDistScaleAnimation(SGPropertyNode_ptr props); - virtual ~SGDistScaleAnimation (); -}; - -/** - * An animation to tell wich objects don't cast shadows. - */ -class SGShadowAnimation : public SGAnimation -{ +class SGTexTransformAnimation : public SGAnimation { public: - SGShadowAnimation ( SGPropertyNode *prop_root, - SGPropertyNode_ptr props ); - virtual ~SGShadowAnimation (); - virtual void operator()(osg::Node* node, osg::NodeVisitor* nv); - bool get_condition_value(void); + SGTexTransformAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot); + virtual osg::Group* createAnimationGroup(osg::Group& parent); private: - SGSharedPtr _condition; - bool _condition_value; + class Transform; + class Translation; + class Rotation; + class UpdateCallback; + void appendTexTranslate(const SGPropertyNode* config, + UpdateCallback* updateCallback); + void appendTexRotate(const SGPropertyNode* config, + UpdateCallback* updateCallback); }; -/** -+ * An "animation" that replace fixed opengl pipeline by shaders -+ */ -class SGShaderAnimation : public SGAnimation -{ + +////////////////////////////////////////////////////////////////////// +// Shader animation +////////////////////////////////////////////////////////////////////// + +class SGShaderAnimation : public SGAnimation { public: - SGShaderAnimation ( SGPropertyNode *prop_root, - SGPropertyNode_ptr props ); - virtual ~SGShaderAnimation (); - virtual void init(); - virtual void operator()(osg::Node* node, osg::NodeVisitor* nv); - bool get_condition_value(void); + SGShaderAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot); + virtual osg::Group* createAnimationGroup(osg::Group& parent); private: - SGSharedPtr _condition; - bool _condition_value; - int _shader_type; - float _param_1; - osg::Vec4 _param_color; -public: - bool _depth_test; - float _factor; - SGPropertyNode_ptr _factor_prop; - float _speed; - float totalTime; - SGPropertyNode_ptr _speed_prop; - osg::ref_ptr _effectTexture; - unsigned char *_textureData; - GLint _texWidth, _texHeight; - osg::Vec4 _envColor; + class UpdateCallback; }; - #endif // _SG_ANIMATION_HXX diff --git a/simgear/scene/model/model.cxx b/simgear/scene/model/model.cxx index 8be60e92..f1d61444 100644 --- a/simgear/scene/model/model.cxx +++ b/simgear/scene/model/model.cxx @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -65,9 +66,11 @@ public: SGTextureUpdateVisitor(const osgDB::FilePathList& pathList) : mPathList(pathList) { } - osg::Texture2D* textureReplace(int unit, osg::StateSet::RefAttributePair& refAttr) + osg::Texture2D* textureReplace(int unit, + osg::StateSet::RefAttributePair& refAttr) { - osg::Texture2D* texture = dynamic_cast(refAttr.first.get()); + osg::Texture2D* texture; + texture = dynamic_cast(refAttr.first.get()); if (!texture) return 0; @@ -113,7 +116,8 @@ public: if (texture) { stateSet->removeTextureAttribute(unit, i->second.first.get()); stateSet->setTextureAttribute(unit, texture, i->second.second); - stateSet->setTextureMode(unit, GL_TEXTURE_2D, osg::StateAttribute::ON); + stateSet->setTextureMode(unit, GL_TEXTURE_2D, + osg::StateAttribute::ON); } ++i; } @@ -132,7 +136,8 @@ public: virtual void apply(int, osg::StateSet::RefAttributePair& refAttr) { - osg::Texture2D* texture = dynamic_cast(refAttr.first.get()); + osg::Texture2D* texture; + texture = dynamic_cast(refAttr.first.get()); if (!texture) return; @@ -157,7 +162,8 @@ class SGAcMaterialCrippleVisitor : public SGStateAttributeVisitor { public: virtual void apply(osg::StateSet::RefAttributePair& refAttr) { - osg::Material* material = dynamic_cast(refAttr.first.get()); + osg::Material* material; + material = dynamic_cast(refAttr.first.get()); if (!material) return; material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE); @@ -168,7 +174,8 @@ class SGReadFileCallback : public osgDB::Registry::ReadFileCallback { public: virtual osgDB::ReaderWriter::ReadResult - readImage(const std::string& fileName, const osgDB::ReaderWriter::Options* opt) + readImage(const std::string& fileName, + const osgDB::ReaderWriter::Options* opt) { std::string absFileName = osgDB::findDataFile(fileName); if (!osgDB::fileExists(absFileName)) { @@ -178,7 +185,8 @@ public: } osgDB::Registry* registry = osgDB::Registry::instance(); - osgDB::ReaderWriter::ReadResult res = registry->readImageImplementation(absFileName, opt); + osgDB::ReaderWriter::ReadResult res; + res = registry->readImageImplementation(absFileName, opt); if (res.loadedFromCache()) SG_LOG(SG_IO, SG_INFO, "Returning cached image \"" << res.getImage()->getFileName() << "\""); @@ -190,7 +198,8 @@ public: } virtual osgDB::ReaderWriter::ReadResult - readNode(const std::string& fileName, const osgDB::ReaderWriter::Options* opt) + readNode(const std::string& fileName, + const osgDB::ReaderWriter::Options* opt) { std::string absFileName = osgDB::findDataFile(fileName); if (!osgDB::fileExists(absFileName)) { @@ -234,19 +243,23 @@ public: osgUtil::Optimizer optimizer; unsigned opts = osgUtil::Optimizer::FLATTEN_STATIC_TRANSFORMS; optimizer.optimize(root.get(), opts); + + // strip away unneeded groups + if (root->getNumChildren() == 1 && root->getName().empty()) { + res = osgDB::ReaderWriter::ReadResult(root->getChild(0)); + } else + res = osgDB::ReaderWriter::ReadResult(root.get()); // Ok, this step is questionable. // It is there to have the same visual appearance of ac objects for the // first cut. Osg's ac3d loader will correctly set materials from the // ac file. But the old plib loader used GL_AMBIENT_AND_DIFFUSE for the // materials that in effect igored the ambient part specified in the - // file. We emulate that for the first cut here by changing all ac models - // here. But in the long term we should use the unchanged model and fix - // the input files instead ... + // file. We emulate that for the first cut here by changing all + // ac models here. But in the long term we should use the + // unchanged model and fix the input files instead ... SGAcMaterialCrippleVisitor matCriple; - root->accept(matCriple); - - res = osgDB::ReaderWriter::ReadResult(root.get()); + res.getNode()->accept(matCriple); } osgUtil::Optimizer optimizer; @@ -261,6 +274,7 @@ public: // opts |= osgUtil::Optimizer::CHECK_GEOMETRY; // opts |= osgUtil::Optimizer::SPATIALIZE_GROUPS; // opts |= osgUtil::Optimizer::COPY_SHARED_NODES; + opts |= osgUtil::Optimizer::FLATTEN_STATIC_TRANSFORMS; if (needTristrip) opts |= osgUtil::Optimizer::TRISTRIP_GEOMETRY; // opts |= osgUtil::Optimizer::TESSELATE_GEOMETRY; @@ -271,7 +285,8 @@ public: registry->getSharedStateManager()->share(res.getNode()); // OSGFIXME: guard that with a flag - // OSGFIXME: in the long term it is unclear if we have an OpenGL context here... + // OSGFIXME: in the long term it is unclear if we have an OpenGL + // context here... osg::Texture::Extensions* e = osg::Texture::getExtensions(0, true); if (e->isTextureCompressionARBSupported()) { SGTexCompressionVisitor texComp(osg::Texture::USE_ARB_COMPRESSION); @@ -284,10 +299,11 @@ public: // Add an extra reference to the model stored in the database. // That it to avoid expiring the object from the cache even if it is still - // in use. Note that the object cache will think that a model is unused if the - // reference count is 1. If we clone all structural nodes here we need that extra - // reference to the original object - SGDatabaseReference* databaseReference = new SGDatabaseReference(res.getNode()); + // in use. Note that the object cache will think that a model is unused + // if the reference count is 1. If we clone all structural nodes here + // we need that extra reference to the original object + SGDatabaseReference* databaseReference; + databaseReference = new SGDatabaseReference(res.getNode()); osg::CopyOp::CopyFlags flags = osg::CopyOp::DEEP_COPY_ALL; flags &= ~osg::CopyOp::DEEP_COPY_TEXTURES; flags &= ~osg::CopyOp::DEEP_COPY_IMAGES; @@ -383,157 +399,9 @@ public: } private: - SGCondition* mCondition; + SGSharedPtr mCondition; }; -/** - * Locate a named node in a branch. - */ -class NodeFinder : public osg::NodeVisitor { -public: - NodeFinder(const std::string& nameToFind) : - osg::NodeVisitor(osg::NodeVisitor::NODE_VISITOR, - osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), - mName(nameToFind), - mNode(0) - { } - virtual void apply(osg::Node& node) - { - if (mNode) - return; - if (mName == node.getName()) { - mNode = &node; - return; - } - traverse(node); - } - - osg::Node* getNode() const - { return mNode; } -private: - std::string mName; - osg::Node* mNode; -}; - -/** - * Splice a branch in between all child nodes and their parents. - */ -static void -splice_branch(osg::Group* group, osg::Node* child) -{ - osg::Node::ParentList parents = child->getParents(); - group->addChild(child); - osg::Node::ParentList::iterator i; - for (i = parents.begin(); i != parents.end(); ++i) - (*i)->replaceChild(child, group); -} - -void -sgMakeAnimation( osg::Node * model, - const char * name, - vector &name_nodes, - SGPropertyNode *prop_root, - SGPropertyNode_ptr node, - double sim_time_sec, - SGPath &texture_path, - set &ignore_branches ) -{ - bool ignore = false; - SGAnimation * animation = 0; - const char * type = node->getStringValue("type", "none"); - if (!strcmp("none", type)) { - animation = new SGNullAnimation(node); - } else if (!strcmp("range", type)) { - animation = new SGRangeAnimation(prop_root, node); - } else if (!strcmp("billboard", type)) { - animation = new SGBillboardAnimation(node); - } else if (!strcmp("select", type)) { - animation = new SGSelectAnimation(prop_root, node); - } else if (!strcmp("spin", type)) { - animation = new SGSpinAnimation(prop_root, node, sim_time_sec ); - } else if (!strcmp("timed", type)) { - animation = new SGTimedAnimation(node); - } else if (!strcmp("rotate", type)) { - animation = new SGRotateAnimation(prop_root, node); - } else if (!strcmp("translate", type)) { - animation = new SGTranslateAnimation(prop_root, node); - } else if (!strcmp("scale", type)) { - animation = new SGScaleAnimation(prop_root, node); - } else if (!strcmp("texrotate", type)) { - animation = new SGTexRotateAnimation(prop_root, node); - } else if (!strcmp("textranslate", type)) { - animation = new SGTexTranslateAnimation(prop_root, node); - } else if (!strcmp("texmultiple", type)) { - animation = new SGTexMultipleAnimation(prop_root, node); - } else if (!strcmp("blend", type)) { - animation = new SGBlendAnimation(prop_root, node); - ignore = true; - } else if (!strcmp("alpha-test", type)) { - animation = new SGAlphaTestAnimation(node); - } else if (!strcmp("material", type)) { - animation = new SGMaterialAnimation(prop_root, node, texture_path); - } else if (!strcmp("flash", type)) { - animation = new SGFlashAnimation(node); - } else if (!strcmp("dist-scale", type)) { - animation = new SGDistScaleAnimation(node); - } else if (!strcmp("noshadow", type)) { - animation = new SGShadowAnimation(prop_root, node); - } else if (!strcmp("shader", type)) { - animation = new SGShaderAnimation(prop_root, node); - } else { - animation = new SGNullAnimation(node); - SG_LOG(SG_INPUT, SG_WARN, "Unknown animation type " << type); - } - - if (name != 0) - animation->setName(name); - - osg::Node * object = 0; - if (!name_nodes.empty()) { - const char * name = name_nodes[0]->getStringValue(); - NodeFinder nodeFinder(name); - model->accept(nodeFinder); - object = nodeFinder.getNode(); - if (object == 0) { - SG_LOG(SG_INPUT, SG_ALERT, "Object " << name << " not found"); - delete animation; - animation = 0; - } - } else { - object = model; - } - - if ( animation == 0 ) - return; - - osg::Group* branch = animation->getBranch(); - splice_branch(branch, object); - - for (unsigned int i = 1; i < name_nodes.size(); i++) { - const char * name = name_nodes[i]->getStringValue(); - NodeFinder nodeFinder(name); - model->accept(nodeFinder); - object = nodeFinder.getNode(); - if (object == 0) { - SG_LOG(SG_INPUT, SG_ALERT, "Object " << name << " not found"); - delete animation; - animation = 0; - } else { - osg::Group* oldParent = object->getParent(0); - branch->addChild(object); - oldParent->removeChild(object); - } - } - - if ( animation != 0 ) { - animation->init(); - branch->setUpdateCallback(animation); - if ( ignore ) { - ignore_branches.insert( branch ); - } - } -} - //////////////////////////////////////////////////////////////////////// // Global functions. @@ -546,10 +414,10 @@ sgLoad3DModel( const string &fg_root, const string &path, SGModelData *data, const SGPath& externalTexturePath ) { - osg::Switch* model = 0; + osg::Node* model = 0; SGPropertyNode props; - // Load the 3D aircraft object itself + // Load the 3D aircraft object itself SGPath modelpath = path, texturepath = path; if ( !ulIsAbsolutePathName( path.c_str() ) ) { SGPath tmp = fg_root; @@ -557,7 +425,7 @@ sgLoad3DModel( const string &fg_root, const string &path, modelpath = texturepath = tmp; } - // Check for an XML wrapper + // Check for an XML wrapper if (modelpath.str().substr(modelpath.str().size() - 4, 4) == ".xml") { readProperties(modelpath.str(), &props); if (props.hasValue("/path")) { @@ -576,25 +444,23 @@ sgLoad3DModel( const string &fg_root, const string &path, osgDB::FilePathList pathList = osgDB::getDataFilePathList(); osgDB::Registry::instance()->initFilePathLists(); - // Assume that textures are in - // the same location as the XML file. + // Assume that textures are in + // the same location as the XML file. if (model == 0) { if (texturepath.extension() != "") texturepath = texturepath.dir(); osgDB::Registry::instance()->getDataFilePathList().push_front(texturepath.str()); - osg::Node* node = osgDB::readNodeFile(modelpath.str()); - if (node == 0) + model = osgDB::readNodeFile(modelpath.str()); + if (model == 0) throw sg_io_exception("Failed to load 3D model", sg_location(modelpath.str())); - model = new osg::Switch; - model->addChild(node, true); } osgDB::Registry::instance()->getDataFilePathList().push_front(externalTexturePath.str()); - // Set up the alignment node + // Set up the alignment node osg::MatrixTransform* alignmainmodel = new osg::MatrixTransform; alignmainmodel->addChild(model); osg::Matrix res_matrix; @@ -612,30 +478,28 @@ sgLoad3DModel( const string &fg_root, const string &path, props.getFloatValue("/offsets/z-m", 0.0)); alignmainmodel->setMatrix(res_matrix*tmat); - unsigned int i; - - // Load sub-models + // Load sub-models vector model_nodes = props.getChildren("model"); - for (i = 0; i < model_nodes.size(); i++) { + for (unsigned i = 0; i < model_nodes.size(); i++) { SGPropertyNode_ptr node = model_nodes[i]; osg::MatrixTransform* align = new osg::MatrixTransform; res_matrix.makeIdentity(); res_matrix.makeRotate( - node->getFloatValue("offsets/heading-deg", 0.0)*SG_DEGREES_TO_RADIANS, + node->getDoubleValue("offsets/heading-deg", 0.0)*SG_DEGREES_TO_RADIANS, osg::Vec3(0, 0, 1), - node->getFloatValue("offsets/roll-deg", 0.0)*SG_DEGREES_TO_RADIANS, + node->getDoubleValue("offsets/roll-deg", 0.0)*SG_DEGREES_TO_RADIANS, osg::Vec3(1, 0, 0), - node->getFloatValue("offsets/pitch-deg", 0.0)*SG_DEGREES_TO_RADIANS, + node->getDoubleValue("offsets/pitch-deg", 0.0)*SG_DEGREES_TO_RADIANS, osg::Vec3(0, 1, 0)); tmat.makeIdentity(); - tmat.makeTranslate(node->getFloatValue("offsets/x-m", 0.0), - node->getFloatValue("offsets/y-m", 0.0), - node->getFloatValue("offsets/z-m", 0.0)); + tmat.makeTranslate(node->getDoubleValue("offsets/x-m", 0), + node->getDoubleValue("offsets/y-m", 0), + node->getDoubleValue("offsets/z-m", 0)); align->setMatrix(res_matrix*tmat); osg::Node* kid; - const char * submodel = node->getStringValue("path"); + const char* submodel = node->getStringValue("path"); try { kid = sgLoad3DModel( fg_root, submodel, prop_root, sim_time_sec, load_panel ); @@ -646,22 +510,28 @@ sgLoad3DModel( const string &fg_root, const string &path, align->addChild(kid); align->setName(node->getStringValue("name", "")); - model->addChild(align); SGPropertyNode *cond = node->getNode("condition", false); - if (cond) - model->setUpdateCallback(new SGSwitchUpdateCallback(sgReadCondition(prop_root, cond))); + if (cond) { + osg::Switch* sw = new osg::Switch; + sw->setUpdateCallback(new SGSwitchUpdateCallback(sgReadCondition(prop_root, cond))); + alignmainmodel->addChild(sw); + sw->addChild(align); + sw->setName("submodel condition switch"); + } else { + alignmainmodel->addChild(align); + } } if ( load_panel ) { - // Load panels + // Load panels vector panel_nodes = props.getChildren("panel"); - for (i = 0; i < panel_nodes.size(); i++) { + for (unsigned i = 0; i < panel_nodes.size(); i++) { SG_LOG(SG_INPUT, SG_DEBUG, "Loading a panel"); osg::Node * panel = load_panel(panel_nodes[i]); if (panel_nodes[i]->hasValue("name")) panel->setName((char *)panel_nodes[i]->getStringValue("name")); - model->addChild(panel); + alignmainmodel->addChild(panel); } } @@ -669,20 +539,22 @@ sgLoad3DModel( const string &fg_root, const string &path, alignmainmodel->setUserData(data); data->modelLoaded(path, &props, alignmainmodel); } - // Load animations - set ignore_branches; - vector animation_nodes = props.getChildren("animation"); - for (i = 0; i < animation_nodes.size(); i++) { - const char * name = animation_nodes[i]->getStringValue("name", 0); - vector name_nodes = - animation_nodes[i]->getChildren("object-name"); - sgMakeAnimation( model, name, name_nodes, prop_root, animation_nodes[i], - sim_time_sec, texturepath, ignore_branches); - } + + std::vector animation_nodes; + animation_nodes = props.getChildren("animation"); + for (unsigned i = 0; i < animation_nodes.size(); ++i) + /// OSGFIXME: duh, why not only model????? + SGAnimation::animate(alignmainmodel, animation_nodes[i], prop_root); // restore old path list osgDB::setDataFilePathList(pathList); + if (props.hasChild("debug-outfile")) { + std::string outputfile = props.getStringValue("debug-outfile", + "debug-model.osg"); + osgDB::writeNodeFile(*alignmainmodel, outputfile); + } + return alignmainmodel; } diff --git a/simgear/scene/model/persparam.cxx b/simgear/scene/model/persparam.cxx index 858963e3..e855152c 100755 --- a/simgear/scene/model/persparam.cxx +++ b/simgear/scene/model/persparam.cxx @@ -6,7 +6,7 @@ #include "persparam.hxx" template <> double -SGPersonalityParameter::getNodeValue( SGPropertyNode *props, +SGPersonalityParameter::getNodeValue( const SGPropertyNode *props, const char *name, double defval ) const { diff --git a/simgear/scene/model/persparam.hxx b/simgear/scene/model/persparam.hxx index 776e0c9c..e8b571fc 100755 --- a/simgear/scene/model/persparam.hxx +++ b/simgear/scene/model/persparam.hxx @@ -11,11 +11,11 @@ template class SGPersonalityParameter { public: - SGPersonalityParameter( SGPropertyNode *props, const char *name, T defval ) + SGPersonalityParameter(const SGPropertyNode *props, const char *name, T defval ) : _var( defval ), _min( defval ), _max( defval ) { - SGPropertyNode_ptr node = props->getNode( name ); + const SGPropertyNode* node = props->getNode( name ); if ( node != 0 ) { - SGPropertyNode_ptr rand_n = node->getNode( "random" ); + const SGPropertyNode* rand_n = node->getNode( "random" ); if ( rand_n != 0 ) { _min = getNodeValue( rand_n, "min", (T)0 ); _max = getNodeValue( rand_n, "max", (T)1 ); @@ -30,7 +30,7 @@ public: SGPersonalityParameter &operator-=( T v ) { _var -= v; return *this; } T shuffle() { return ( _var = _min + sg_random() * ( _max - _min ) ); } T value() const { return _var; } - T getNodeValue( SGPropertyNode *props, const char *name, T defval ) const; + T getNodeValue(const SGPropertyNode *props, const char *name, T defval ) const; operator T() const { return _var; } private: @@ -40,7 +40,7 @@ private: }; template <> double -SGPersonalityParameter::getNodeValue( SGPropertyNode *props, +SGPersonalityParameter::getNodeValue( const SGPropertyNode *props, const char *name, double defval ) const; diff --git a/simgear/scene/model/shadanim.cxx b/simgear/scene/model/shadanim.cxx index ab76f23f..cffae0ff 100644 --- a/simgear/scene/model/shadanim.cxx +++ b/simgear/scene/model/shadanim.cxx @@ -24,11 +24,22 @@ # include #endif -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include #include #include -#include #include @@ -66,6 +77,130 @@ */ + + +class SGMapGenCallback : + public osg::StateAttribute::Callback { +public: + virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor* nv) + { + SGUpdateVisitor* updateVisitor = dynamic_cast(nv); + if (!updateVisitor) + return; + + if (distSqr(_lastLightDirection, updateVisitor->getLightDirection()) < 1e-4 + && distSqr(_lastLightColor, updateVisitor->getAmbientLight()) < 1e-4) + return; + + _lastLightDirection = updateVisitor->getLightDirection(); + _lastLightColor = updateVisitor->getAmbientLight(); + + osg::TextureCubeMap *tcm = static_cast(sa); + + // FIXME: need an update or callback ... + // generate the six highlight map images (light direction = [1, 1, -1]) + osg::ref_ptr mapgen; + mapgen = new osgUtil::HighlightMapGenerator(_lastLightDirection.osg(), + _lastLightColor.osg(), 5); + mapgen->generateMap(); + + // assign the six images to the texture object + tcm->setImage(osg::TextureCubeMap::POSITIVE_X, + mapgen->getImage(osg::TextureCubeMap::POSITIVE_X)); + tcm->setImage(osg::TextureCubeMap::NEGATIVE_X, + mapgen->getImage(osg::TextureCubeMap::NEGATIVE_X)); + tcm->setImage(osg::TextureCubeMap::POSITIVE_Y, + mapgen->getImage(osg::TextureCubeMap::POSITIVE_Y)); + tcm->setImage(osg::TextureCubeMap::NEGATIVE_Y, + mapgen->getImage(osg::TextureCubeMap::NEGATIVE_Y)); + tcm->setImage(osg::TextureCubeMap::POSITIVE_Z, + mapgen->getImage(osg::TextureCubeMap::POSITIVE_Z)); + tcm->setImage(osg::TextureCubeMap::NEGATIVE_Z, + mapgen->getImage(osg::TextureCubeMap::NEGATIVE_Z)); + } +private: + SGVec3f _lastLightDirection; + SGVec4f _lastLightColor; +}; + +static osg::TextureCubeMap* +getOrCreateTextureCubeMap() +{ + static osg::TextureCubeMap* textureCubeMap = 0; + if (textureCubeMap) + return textureCubeMap; + + static SGMutex mutex; + SGGuard locker(mutex); + if (textureCubeMap) + return textureCubeMap; + + // create and setup the texture object + textureCubeMap = new osg::TextureCubeMap; + + textureCubeMap->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP); + textureCubeMap->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP); + textureCubeMap->setWrap(osg::Texture::WRAP_R, osg::Texture::CLAMP); + textureCubeMap->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR); + textureCubeMap->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); + + textureCubeMap->setUpdateCallback(new SGMapGenCallback); + + return textureCubeMap; +} + +static void create_specular_highlights(osg::Node *node) +{ + osg::StateSet *ss = node->getOrCreateStateSet(); + + // create and setup the texture object + osg::TextureCubeMap *tcm = getOrCreateTextureCubeMap(); + + // enable texturing, replacing any textures in the subgraphs + ss->setTextureAttributeAndModes(0, tcm, osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON); + + // texture coordinate generation + osg::TexGen *tg = new osg::TexGen; + tg->setMode(osg::TexGen::REFLECTION_MAP); + ss->setTextureAttributeAndModes(0, tg, osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON); + + // use TexEnvCombine to add the highlights to the original lighting + osg::TexEnvCombine *te = new osg::TexEnvCombine; + te->setCombine_RGB(osg::TexEnvCombine::ADD); + te->setSource0_RGB(osg::TexEnvCombine::TEXTURE); + te->setOperand0_RGB(osg::TexEnvCombine::SRC_COLOR); + te->setSource1_RGB(osg::TexEnvCombine::PRIMARY_COLOR); + te->setOperand1_RGB(osg::TexEnvCombine::SRC_COLOR); + ss->setTextureAttributeAndModes(0, te, osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON); +} + + +SGShaderAnimation::SGShaderAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot) : + SGAnimation(configNode, modelRoot) +{ +} + +osg::Group* +SGShaderAnimation::createAnimationGroup(osg::Group& parent) +{ + osg::Group* group = new osg::Group; + group->setName("shader animation"); + parent.addChild(group); + + std::string shader_name = getConfig()->getStringValue("shader", ""); +// if( shader_name == "fresnel" || shader_name == "reflection" ) +// _shader_type = 1; +// else if( shader_name == "heat-haze" ) +// _shader_type = 2; +// else + if( shader_name == "chrome") + create_specular_highlights(group); + + return group; +} + +#if 0 // static Shader *shFresnel=NULL; // static GLuint texFresnel = 0; @@ -621,3 +756,4 @@ SGShaderAnimation::operator()(osg::Node* node, osg::NodeVisitor* nv) // that the rest of cullbacks and the scene graph are traversed. traverse(node, nv); } +#endif -- 2.39.5