X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fscene%2Fmodel%2Fanimation.cxx;h=b6748fe036c0bee7df286f530aab5401f1fad7f9;hb=5a96b283f63cfacd3a3a90a82246defb038242d3;hp=30acb661707fe2c8ff1d1631f79b3ba11fa307d5;hpb=942b474ad78889944d2bd509bbb899f3cb8df322;p=simgear.git diff --git a/simgear/scene/model/animation.cxx b/simgear/scene/model/animation.cxx index 30acb661..b6748fe0 100644 --- a/simgear/scene/model/animation.cxx +++ b/simgear/scene/model/animation.cxx @@ -3,23 +3,67 @@ // // This file is in the Public Domain, and comes with no warranty. +#ifdef HAVE_CONFIG_H +# include +#endif #include // for strcmp() #include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include -#include -#include -#include #include #include #include -#include +#include +#include +#include +#include +#include +#include +#include #include "animation.hxx" -#include "custtrans.hxx" -#include "personality.hxx" +#include "model.hxx" +#include "SGTranslateTransform.hxx" +#include "SGMaterialAnimation.hxx" +#include "SGRotateTransform.hxx" +#include "SGScaleTransform.hxx" +#include "SGInteractionAnimation.hxx" + +#include "ConditionNode.hxx" + +using OpenThreads::Mutex; +using OpenThreads::ReentrantMutex; +using OpenThreads::ScopedLock; + +using namespace simgear; //////////////////////////////////////////////////////////////////////// // Static utility functions. @@ -29,1512 +73,2301 @@ * Set up the transform matrix for a spin or rotation. */ static void -set_rotation (sgMat4 &matrix, double position_deg, - sgVec3 ¢er, sgVec3 &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; - +set_rotation (osg::Matrix &matrix, double position_deg, + const SGVec3d ¢er, const SGVec3d &axis) +{ + 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 - 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; + 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) = 1; } /** * Set up the transform matrix for a translation. */ static void -set_translation (sgMat4 &matrix, double position_m, sgVec3 &axis) +set_translation (osg::Matrix &matrix, double position_m, const SGVec3d &axis) { - sgVec3 xyz; - sgScaleVec3(xyz, axis, position_m); - sgMakeTransMat4(matrix, xyz); + 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. + * Read an interpolation table from properties. */ -static void -set_scale (sgMat4 &matrix, double x, double y, double z) +static SGInterpTable * +read_interpolation_table(const SGPropertyNode* props) { - sgMakeIdentMat4( matrix ); - matrix[0][0] = x; - matrix[1][1] = y; - matrix[2][2] = z; + const SGPropertyNode* table_node = props->getNode("interpolation"); + if (!table_node) + return 0; + return new SGInterpTable(table_node); } -/** - * Recursively process all kids to change the alpha values - */ -static void -change_alpha( ssgBase *_branch, float _blend ) +static std::string +unit_string(const char* value, const char* unit) { - int i; - - for (i = 0; i < ((ssgBranch *)_branch)->getNumKids(); i++) - change_alpha( ((ssgBranch *)_branch)->getKid(i), _blend ); - - if ( !_branch->isAKindOf(ssgTypeLeaf()) - && !_branch->isAKindOf(ssgTypeVtxTable()) - && !_branch->isAKindOf(ssgTypeVTable()) ) - return; + return std::string(value) + unit; +} - int num_colors = ((ssgLeaf *)_branch)->getNumColours(); -// unsigned int select_ = (_blend == 1.0) ? false : true; +class SGPersonalityScaleOffsetExpression : public SGUnaryExpression { +public: + SGPersonalityScaleOffsetExpression(SGExpression* expr, + SGPropertyNode const* config, + const std::string& scalename, + const std::string& offsetname, + double defScale = 1, + double defOffset = 0) : + SGUnaryExpression(expr), + _scale(config, scalename.c_str(), defScale), + _offset(config, offsetname.c_str(), defOffset) + { } + void setScale(double scale) + { _scale = scale; } + void setOffset(double offset) + { _offset = offset; } - for (i = 0; i < num_colors; i++) + virtual void eval(double& value, const simgear::expression::Binding* b) const { -// ((ssgSelector *)_branch)->select( select_ ); - float *color = ((ssgLeaf *)_branch)->getColour(i); - color[3] = _blend; + _offset.shuffle(); + _scale.shuffle(); + value = _offset + _scale*getOperand()->getValue(b); } + + virtual bool isConst() const { return false; } + +private: + mutable SGPersonalityParameter _scale; + mutable SGPersonalityParameter _offset; +}; + + +static SGExpressiond* +read_factor_offset(const SGPropertyNode* configNode, SGExpressiond* expr, + const std::string& factor, const std::string& offset) +{ + double factorValue = configNode->getDoubleValue(factor, 1); + if (factorValue != 1) + expr = new SGScaleExpression(expr, factorValue); + double offsetValue = configNode->getDoubleValue(offset, 0); + if (offsetValue != 0) + expr = new SGBiasExpression(expr, offsetValue); + return expr; } -/** - * Modify property value by step and scroll settings in texture translations - */ -static double -apply_mods(double property, double step, double scroll) -{ - - double modprop; - if(step > 0) { - double scrollval = 0.0; - if(scroll > 0) { - // calculate scroll amount (for odometer like movement) - double remainder = step - fmod(fabs(property), step); - if (remainder < scroll) { - scrollval = (scroll - remainder) / scroll * step; - } - } - // apply stepping of input value - if(property > 0) - modprop = ((floor(property/step) * step) + scrollval); - else - modprop = ((ceil(property/step) * step) + scrollval); +static SGExpressiond* +read_offset_factor(const SGPropertyNode* configNode, SGExpressiond* expr, + const std::string& factor, const std::string& offset) +{ + double offsetValue = configNode->getDoubleValue(offset, 0); + if (offsetValue != 0) + expr = new SGBiasExpression(expr, offsetValue); + double factorValue = configNode->getDoubleValue(factor, 1); + if (factorValue != 1) + expr = new SGScaleExpression(expr, factorValue); + return expr; +} + +SGExpressiond* +read_value(const SGPropertyNode* configNode, SGPropertyNode* modelRoot, + const char* unit, double defMin, double defMax) +{ + const SGPropertyNode * expression = configNode->getNode( "expression" ); + if( expression != NULL ) + return SGReadDoubleExpression( modelRoot, expression->getChild(0) ); + + SGExpression* value = 0; + + std::string inputPropertyName = configNode->getStringValue("property", ""); + if (inputPropertyName.empty()) { + std::string spos = unit_string("starting-position", unit); + double initPos = configNode->getDoubleValue(spos, 0); + value = new SGConstExpression(initPos); } else { - modprop = property; + SGPropertyNode* inputProperty; + inputProperty = modelRoot->getNode(inputPropertyName, true); + value = new SGPropertyExpression(inputProperty); } - return modprop; -} - -/** - * Read an interpolation table from properties. - */ -static SGInterpTable * -read_interpolation_table (SGPropertyNode_ptr 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; + SGInterpTable* interpTable = read_interpolation_table(configNode); + if (interpTable) { + return new SGInterpTableExpression(value, interpTable); } else { - return 0; + 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)) { + value = new SGPersonalityScaleOffsetExpression(value, configNode, + "factor", offset); + } else { + value = read_factor_offset(configNode, value, "factor", offset); + } + + double minClip = configNode->getDoubleValue(min, defMin); + double maxClip = configNode->getDoubleValue(max, defMax); + if (minClip > SGMiscd::min(SGLimitsd::min(), -SGLimitsd::max()) || + maxClip < SGLimitsd::max()) + value = new SGClipExpression(value, minClip, maxClip); + + return value; } + return 0; } - //////////////////////////////////////////////////////////////////////// -// Implementation of SGAnimation +// Animation installer //////////////////////////////////////////////////////////////////////// -// Initialize the static data member -double SGAnimation::sim_time_sec = 0.0; -SGPersonalityBranch *SGAnimation::current_object = 0; +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; +}; + +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; +}; + +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; +}; + +class SGAnimation::BinToInheritVisitor : public SGStateAttributeVisitor { +public: + virtual void apply(osg::StateSet* stateSet) + { + if (!stateSet) + return; + stateSet->setRenderBinToInherit(); + } +}; + +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))); + } + } +}; + +namespace +{ +// Set all drawables to not use display lists. OSG will use +// glDrawArrays instead. +struct DoDrawArraysVisitor : public osg::NodeVisitor { + DoDrawArraysVisitor() : + osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) + {} + void apply(osg::Geode& geode) + { + using namespace osg; + using namespace std; + + for (int i = 0; i < (int)geode.getNumDrawables(); ++i) + geode.getDrawable(i)->setUseDisplayList(false); + } +}; +} + +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 (SGPropertyNode_ptr props, ssgBranch * branch) - : _branch(branch), - animation_type(0) +SGAnimation::~SGAnimation() { - _branch->setName(props->getStringValue("name", 0)); - if ( props->getBoolValue( "enable-hot", true ) ) { - _branch->setTraversalMaskBits( SSGTRAV_HOT ); - } else { - _branch->clrTraversalMaskBits( SSGTRAV_HOT ); - } + if (!_found) + { + std::list::const_iterator i; + string info; + for (i = _objectNames.begin(); i != _objectNames.end(); ++i) + { + if (!info.empty()) + info.append(", "); + info.append("'"); + info.append(*i); + info.append("'"); + } + if (!info.empty()) + { + SG_LOG(SG_IO, SG_ALERT, "Could not find at least one of the following" + " objects for animation: " << info); + } + } } -SGAnimation::~SGAnimation () +bool +SGAnimation::animate(osg::Node* node, const SGPropertyNode* configNode, + SGPropertyNode* modelRoot, + const osgDB::Options* options) +{ + 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 == "interaction") { + SGInteractionAnimation animInst(configNode, modelRoot); + animInst.apply(node); + } else if (type == "material") { + SGMaterialAnimation animInst(configNode, modelRoot, options); + animInst.apply(node); + } else if (type == "noshadow") { + SGShadowAnimation animInst(configNode, modelRoot); + animInst.apply(node); + } else if (type == "pick") { + SGPickAnimation 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, options); + 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 == "light") { + SGLightAnimation 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 +SGAnimation::apply(osg::Node* node) { + // 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 + node->accept(*this); } void -SGAnimation::init () +SGAnimation::install(osg::Node& node) { + _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_CASTSHADOW_BIT | node.getNodeMask()); + else + node.setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node.getNodeMask()); } -int -SGAnimation::update() +osg::Group* +SGAnimation::createAnimationGroup(osg::Group& parent) { - return 1; + // 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; } void -SGAnimation::restore() +SGAnimation::apply(osg::Group& group) { + // 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); + + // 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 +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); + + // Check if this one is already processed + if (std::find(_installedAnimations.begin(), + _installedAnimations.end(), child) + != _installedAnimations.end()) + continue; + + 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 SGNullAnimation -//////////////////////////////////////////////////////////////////////// + // store that we already have processed this child node + // We can hit this one twice if an animation references some + // part of a subtree twice + _installedAnimations.push_back(child); + } + } +} -SGNullAnimation::SGNullAnimation (SGPropertyNode_ptr props) - : SGAnimation(props, new ssgBranch) +void +SGAnimation::removeMode(osg::Node& node, osg::StateAttribute::GLMode mode) { + RemoveModeVisitor visitor(mode); + node.accept(visitor); } -SGNullAnimation::~SGNullAnimation () +void +SGAnimation::removeAttribute(osg::Node& node, osg::StateAttribute::Type type) { + RemoveAttributeVisitor visitor(type); + node.accept(visitor); } - - -//////////////////////////////////////////////////////////////////////// -// Implementation of SGRangeAnimation -//////////////////////////////////////////////////////////////////////// - -SGRangeAnimation::SGRangeAnimation (SGPropertyNode *prop_root, - SGPropertyNode_ptr props) - : SGAnimation(props, new ssgRangeSelector), - _min(0.0), _max(0.0), _min_factor(1.0), _max_factor(1.0), - _condition(0) +void +SGAnimation::removeTextureMode(osg::Node& node, unsigned unit, + osg::StateAttribute::GLMode mode) { - SGPropertyNode_ptr node = props->getChild("condition"); - if (node != 0) - _condition = sgReadCondition(prop_root, node); + RemoveTextureModeVisitor visitor(unit, mode); + node.accept(visitor); +} - float ranges[2]; +void +SGAnimation::removeTextureAttribute(osg::Node& node, unsigned unit, + osg::StateAttribute::Type type) +{ + RemoveTextureAttributeVisitor visitor(unit, type); + node.accept(visitor); +} - 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; - } - ((ssgRangeSelector *)_branch)->setRanges(ranges, 2); +void +SGAnimation::setRenderBinToInherit(osg::Node& node) +{ + BinToInheritVisitor visitor; + node.accept(visitor); } -SGRangeAnimation::~SGRangeAnimation () +void +SGAnimation::cloneDrawables(osg::Node& node) { + DrawableCloneVisitor visitor; + node.accept(visitor); } -int -SGRangeAnimation::update() +const SGCondition* +SGAnimation::getCondition() const { - 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; - } - } else { - ranges[0] = 0.f; - ranges[1] = 1000000000.f; - } - ((ssgRangeSelector *)_branch)->setRanges(ranges, 2); - return 2; + const SGPropertyNode* conditionNode = _configNode->getChild("condition"); + if (!conditionNode) + return 0; + return sgReadCondition(_modelRoot, conditionNode); } //////////////////////////////////////////////////////////////////////// -// Implementation of SGBillboardAnimation +// Implementation of null animation //////////////////////////////////////////////////////////////////////// -SGBillboardAnimation::SGBillboardAnimation (SGPropertyNode_ptr props) - : SGAnimation(props, new ssgCutout(props->getBoolValue("spherical", true))) +// 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) { } -SGBillboardAnimation::~SGBillboardAnimation () +osg::Group* +SGGroupAnimation::createAnimationGroup(osg::Group& parent) { + osg::Group* group = new osg::Group; + parent.addChild(group); + return group; } - //////////////////////////////////////////////////////////////////////// -// Implementation of SGSelectAnimation +// Implementation of translate animation //////////////////////////////////////////////////////////////////////// -SGSelectAnimation::SGSelectAnimation( SGPropertyNode *prop_root, - SGPropertyNode_ptr props ) - : SGAnimation(props, new ssgSelector), - _condition(0) -{ - SGPropertyNode_ptr node = props->getChild("condition"); - if (node != 0) - _condition = sgReadCondition(prop_root, node); -} - -SGSelectAnimation::~SGSelectAnimation () -{ - delete _condition; +class SGTranslateAnimation::UpdateCallback : public osg::NodeCallback { +public: + UpdateCallback(SGCondition const* condition, + SGExpressiond const* animationValue) : + _condition(condition), + _animationValue(animationValue) + { } + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + if (!_condition || _condition->test()) { + SGTranslateTransform* transform; + transform = static_cast(node); + transform->setValue(_animationValue->getValue()); + } + traverse(node, nv); + } +public: + SGSharedPtr _condition; + SGSharedPtr _animationValue; +}; + +SGTranslateAnimation::SGTranslateAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot) : + SGAnimation(configNode, modelRoot) +{ + _condition = getCondition(); + SGSharedPtr value; + value = read_value(configNode, modelRoot, "-m", + -SGLimitsd::max(), SGLimitsd::max()); + _animationValue = value->simplify(); + if (_animationValue) + _initialValue = _animationValue->getValue(); + else + _initialValue = 0; + + 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); + _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); } -int -SGSelectAnimation::update() +osg::Group* +SGTranslateAnimation::createAnimationGroup(osg::Group& parent) { - if (_condition != 0 && _condition->test()) - ((ssgSelector *)_branch)->select(0xffff); - else - ((ssgSelector *)_branch)->select(0x0000); - return 2; + SGTranslateTransform* transform = new SGTranslateTransform; + transform->setName("translate animation"); + if (_animationValue && !_animationValue->isConst()) { + UpdateCallback* uc = new UpdateCallback(_condition, _animationValue); + transform->setUpdateCallback(uc); + } + transform->setAxis(_axis); + transform->setValue(_initialValue); + parent.addChild(transform); + return transform; } - //////////////////////////////////////////////////////////////////////// -// Implementation of SGSpinAnimation +// Implementation of rotate/spin animation //////////////////////////////////////////////////////////////////////// -SGSpinAnimation::SGSpinAnimation( SGPropertyNode *prop_root, - SGPropertyNode_ptr props, - double sim_time_sec ) - : SGAnimation(props, new ssgTransform), - _use_personality( props->getBoolValue("use-personality",false) ), - _prop((SGPropertyNode *)prop_root->getNode(props->getStringValue("property", "/null"), true)), - _last_time_sec( sim_time_sec ), - _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/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); - } - sgNormalizeVec3(_axis); - - //_factor(props->getDoubleValue("factor", 1.0)), - _factor = 1.0; - _factor_min = 1.0; - _factor_max = 1.0; - SGPropertyNode_ptr factor_n = props->getNode( "factor" ); - if ( factor_n != 0 ) { - SGPropertyNode_ptr rand_n = factor_n->getNode( "random" ); - if ( rand_n != 0 ) { - _factor_min = rand_n->getDoubleValue( "min", 0.0 ); - _factor_max = rand_n->getDoubleValue( "max", 1.0 ); - _factor = _factor_min + sg_random() * ( _factor_max - _factor_min ); - } else { - _factor = _factor_min = _factor_max = props->getDoubleValue("factor", 1.0); - } +class SGRotateAnimation::UpdateCallback : public osg::NodeCallback { +public: + UpdateCallback(SGCondition const* condition, + SGExpressiond const* animationValue) : + _condition(condition), + _animationValue(animationValue) + { } + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + if (!_condition || _condition->test()) { + SGRotateTransform* transform; + transform = static_cast(node); + transform->setAngleDeg(_animationValue->getValue()); } - //_position_deg(props->getDoubleValue("starting-position-deg", 0)), - _position_deg = 0.0; - _position_deg_min = 0.0; - _position_deg_max = 0.0; - SGPropertyNode_ptr position_deg_n = props->getNode( "starting-position-deg" ); - if ( position_deg_n != 0 ) { - SGPropertyNode_ptr rand_n = position_deg_n->getNode( "random" ); - if ( rand_n != 0 ) { - _position_deg_min = rand_n->getDoubleValue( "min", 0.0 ); - _position_deg_max = rand_n->getDoubleValue( "max", 1.0 ); - _position_deg = _position_deg_min + sg_random() * ( _position_deg_max - _position_deg_min ); - } else { - _position_deg = _position_deg_min = _position_deg_max = - props->getDoubleValue("starting-position-deg", 1.0); - } + traverse(node, nv); + } +public: + SGSharedPtr _condition; + SGSharedPtr _animationValue; +}; + +class SGRotateAnimation::SpinUpdateCallback : public osg::NodeCallback { +public: + SpinUpdateCallback(SGCondition const* condition, + SGExpressiond const* animationValue) : + _condition(condition), + _animationValue(animationValue), + _lastTime(-1) + { } + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + if (!_condition || _condition->test()) { + SGRotateTransform* 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->getAngleDeg(); + angle += dt*velocity_rpms*360; + angle -= 360*floor(angle/360); + transform->setAngleDeg(angle); } + traverse(node, nv); + } +public: + SGSharedPtr _condition; + SGSharedPtr _animationValue; + double _lastTime; +}; + +SGRotateAnimation::SGRotateAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot) : + SGAnimation(configNode, modelRoot) +{ + std::string type = configNode->getStringValue("type", ""); + _isSpin = (type == "spin"); + + _condition = getCondition(); + SGSharedPtr value; + value = read_value(configNode, modelRoot, "-deg", + -SGLimitsd::max(), SGLimitsd::max()); + _animationValue = value->simplify(); + if (_animationValue) + _initialValue = _animationValue->getValue(); + else + _initialValue = 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) +{ + SGRotateTransform* transform = new SGRotateTransform; + transform->setName("rotate animation"); + if (_isSpin) { + SpinUpdateCallback* uc; + uc = new SpinUpdateCallback(_condition, _animationValue); + transform->setUpdateCallback(uc); + } else if (_animationValue || !_animationValue->isConst()) { + UpdateCallback* uc = new UpdateCallback(_condition, _animationValue); + transform->setUpdateCallback(uc); + } + transform->setCenter(_center); + transform->setAxis(_axis); + transform->setAngleDeg(_initialValue); + parent.addChild(transform); + return transform; } -SGSpinAnimation::~SGSpinAnimation () -{ -} + +//////////////////////////////////////////////////////////////////////// +// Implementation of scale animation +//////////////////////////////////////////////////////////////////////// + +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()) { + SGScaleTransform* transform; + transform = static_cast(node); + SGVec3d scale(_animationValue[0]->getValue(), + _animationValue[1]->getValue(), + _animationValue[2]->getValue()); + transform->setScaleFactor(scale); + } + traverse(node, nv); + } +public: + SGSharedPtr _condition; + SGSharedPtr _animationValue[3]; +}; -int -SGSpinAnimation::update() +SGScaleAnimation::SGScaleAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot) : + SGAnimation(configNode, modelRoot) { - if ( _condition == 0 || _condition->test() ) { - double dt; - float velocity_rpms; - if ( _use_personality ) { - SGPersonalityBranch *key = current_object; - if ( !key->getIntValue( this, INIT_SPIN ) ) { - double v = _factor_min + sg_random() * ( _factor_max - _factor_min ); - key->setDoubleValue( v, this, FACTOR_SPIN ); + _condition = getCondition(); - key->setDoubleValue( sim_time_sec, this, LAST_TIME_SEC_SPIN ); - key->setIntValue( 1, this, INIT_SPIN ); + // default offset/factor for all directions + double offset = configNode->getDoubleValue("offset", 0); + double factor = configNode->getDoubleValue("factor", 1); - v = _position_deg_min + sg_random() * ( _position_deg_max - _position_deg_min ); - key->setDoubleValue( v, this, POSITION_DEG_SPIN ); - } + SGSharedPtr inPropExpr; - _factor = key->getDoubleValue( this, FACTOR_SPIN ); - _position_deg = key->getDoubleValue( this, POSITION_DEG_SPIN ); - _last_time_sec = key->getDoubleValue( this, LAST_TIME_SEC_SPIN ); - dt = sim_time_sec - _last_time_sec; - _last_time_sec = sim_time_sec; - key->setDoubleValue( _last_time_sec, this, LAST_TIME_SEC_SPIN ); - - velocity_rpms = (_prop->getDoubleValue() * _factor / 60.0); - _position_deg += (dt * velocity_rpms * 360); - while (_position_deg < 0) - _position_deg += 360.0; - while (_position_deg >= 360.0) - _position_deg -= 360.0; - key->setDoubleValue( _position_deg, this, POSITION_DEG_SPIN ); - } else { - 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); - while (_position_deg < 0) - _position_deg += 360.0; - while (_position_deg >= 360.0) - _position_deg -= 360.0; - } + std::string inputPropertyName; + inputPropertyName = configNode->getStringValue("property", ""); + if (inputPropertyName.empty()) { + inPropExpr = new SGConstExpression(0); + } else { + SGPropertyNode* inputProperty; + inputProperty = modelRoot->getNode(inputPropertyName, true); + inPropExpr = new SGPropertyExpression(inputProperty); + } - set_rotation(_matrix, _position_deg, _center, _axis); - ((ssgTransform *)_branch)->setTransform(_matrix); + SGInterpTable* interpTable = read_interpolation_table(configNode); + if (interpTable) { + SGSharedPtr value; + value = new SGInterpTableExpression(inPropExpr, interpTable); + _animationValue[0] = value->simplify(); + _animationValue[1] = value->simplify(); + _animationValue[2] = value->simplify(); + } else if (configNode->getBoolValue("use-personality", false)) { + SGSharedPtr value; + value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode, + "x-factor", "x-offset", + factor, offset); + double minClip = configNode->getDoubleValue("x-min", 0); + double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max()); + value = new SGClipExpression(value, minClip, maxClip); + _animationValue[0] = value->simplify(); + + value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode, + "y-factor", "y-offset", + factor, offset); + minClip = configNode->getDoubleValue("y-min", 0); + maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max()); + value = new SGClipExpression(value, minClip, maxClip); + _animationValue[1] = value->simplify(); + + value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode, + "z-factor", "z-offset", + factor, offset); + minClip = configNode->getDoubleValue("z-min", 0); + maxClip = configNode->getDoubleValue("z-max", SGLimitsd::max()); + value = new SGClipExpression(value, minClip, maxClip); + _animationValue[2] = value->simplify(); + } else { + SGSharedPtr value; + value = read_factor_offset(configNode, inPropExpr, "x-factor", "x-offset"); + double minClip = configNode->getDoubleValue("x-min", 0); + double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max()); + value = new SGClipExpression(value, minClip, maxClip); + _animationValue[0] = value->simplify(); + + value = read_factor_offset(configNode, inPropExpr, "y-factor", "y-offset"); + minClip = configNode->getDoubleValue("y-min", 0); + maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max()); + value = new SGClipExpression(value, minClip, maxClip); + _animationValue[1] = value->simplify(); + + value = read_factor_offset(configNode, inPropExpr, "z-factor", "z-offset"); + minClip = configNode->getDoubleValue("z-min", 0); + maxClip = configNode->getDoubleValue("z-max", SGLimitsd::max()); + value = new SGClipExpression(value, minClip, maxClip); + _animationValue[2] = value->simplify(); } - return 1; + _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) +{ + SGScaleTransform* transform = new SGScaleTransform; + 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 SGTimedAnimation -//////////////////////////////////////////////////////////////////////// +// Don't create a new state state everytime we need GL_NORMALIZE! -SGTimedAnimation::SGTimedAnimation (SGPropertyNode_ptr props) - : SGAnimation(props, new ssgSelector), - _use_personality( props->getBoolValue("use-personality",false) ), - _duration_sec(props->getDoubleValue("duration-sec", 1.0)), - _last_time_sec( sim_time_sec ), - _total_duration_sec( 0 ), - _step( 0 ) - +namespace { - 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 ) ); - } - } -} +Mutex normalizeMutex; -SGTimedAnimation::~SGTimedAnimation () +osg::StateSet* getNormalizeStateSet() { -} - -void -SGTimedAnimation::init() -{ - if ( !_use_personality ) { - for ( int i = 0; i < getBranch()->getNumKids(); i++ ) { - double v; - if ( i < (int)_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 ( _total_duration_sec < 0.01 ) { - _total_duration_sec = 0.01; - } - } - ((ssgSelector *)getBranch())->selectStep(_step); -} - -int -SGTimedAnimation::update() -{ - if ( _use_personality ) { - SGPersonalityBranch *key = current_object; - if ( !key->getIntValue( this, INIT_TIMED ) ) { - double total = 0; - double offset = 1.0; - for ( size_t i = 0; i < _branch_duration_specs.size(); i++ ) { - DurationSpec &sp = _branch_duration_specs[ i ]; - double v = sp._min + sg_random() * ( sp._max - sp._min ); - key->setDoubleValue( v, this, BRANCH_DURATION_SEC_TIMED, i ); - if ( i == 0 ) - offset = v; - total += v; - } - // Sanity check : total duration shouldn't equal zero - if ( total < 0.01 ) { - total = 0.01; - } - offset *= sg_random(); - key->setDoubleValue( sim_time_sec - offset, this, LAST_TIME_SEC_TIMED ); - key->setDoubleValue( total, this, TOTAL_DURATION_SEC_TIMED ); - key->setIntValue( 0, this, STEP_TIMED ); - key->setIntValue( 1, this, INIT_TIMED ); - } - - _step = key->getIntValue( this, STEP_TIMED ); - _last_time_sec = key->getDoubleValue( this, LAST_TIME_SEC_TIMED ); - _total_duration_sec = key->getDoubleValue( this, TOTAL_DURATION_SEC_TIMED ); - while ( ( sim_time_sec - _last_time_sec ) >= _total_duration_sec ) { - _last_time_sec += _total_duration_sec; - } - double duration = _duration_sec; - if ( _step < (int)_branch_duration_specs.size() ) { - duration = key->getDoubleValue( this, BRANCH_DURATION_SEC_TIMED, _step ); - } - if ( ( sim_time_sec - _last_time_sec ) >= duration ) { - _last_time_sec += duration; - _step += 1; - if ( _step >= getBranch()->getNumKids() ) - _step = 0; - } - ((ssgSelector *)getBranch())->selectStep( _step ); - key->setDoubleValue( _last_time_sec, this, LAST_TIME_SEC_TIMED ); - key->setIntValue( _step, this, STEP_TIMED ); - } else { - while ( ( sim_time_sec - _last_time_sec ) >= _total_duration_sec ) { - _last_time_sec += _total_duration_sec; - } - double duration = _duration_sec; - if ( _step < (int)_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()->getNumKids() ) - _step = 0; - ((ssgSelector *)getBranch())->selectStep( _step ); - } + static osg::ref_ptr normalizeStateSet; + ScopedLock lock(normalizeMutex); + if (!normalizeStateSet.valid()) { + normalizeStateSet = new osg::StateSet; + normalizeStateSet->setMode(GL_NORMALIZE, osg::StateAttribute::ON); + normalizeStateSet->setDataVariance(osg::Object::STATIC); } - return 1; + return normalizeStateSet.get(); +} } - - //////////////////////////////////////////////////////////////////////// -// Implementation of SGRotateAnimation +// Implementation of dist scale animation //////////////////////////////////////////////////////////////////////// -SGRotateAnimation::SGRotateAnimation( SGPropertyNode *prop_root, - SGPropertyNode_ptr props ) - : SGAnimation(props, new ssgTransform), - _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); - } - sgNormalizeVec3(_axis); -} +class SGDistScaleAnimation::Transform : public osg::Transform { +public: + Transform() : _min_v(0.0), _max_v(0.0), _factor(0.0), _offset(0.0) {} + Transform(const Transform& rhs, + const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY) + : osg::Transform(rhs, copyOp), _table(rhs._table), _center(rhs._center), + _min_v(rhs._min_v), _max_v(rhs._max_v), _factor(rhs._factor), + _offset(rhs._offset) + { + } + META_Node(simgear, SGDistScaleAnimation::Transform); + Transform(const SGPropertyNode* configNode) + { + setName(configNode->getStringValue("name", "dist scale animation")); + setReferenceFrame(RELATIVE_RF); + setStateSet(getNormalizeStateSet()); + _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 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); + matrix.preMult(transform); + return true; + } + + virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix, + osg::NodeVisitor* nv) const + { + 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; + } -SGRotateAnimation::~SGRotateAnimation () -{ - delete _table; -} + static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw) + { + const Transform& trans = static_cast(obj); + fw.indent() << "center " << trans._center << "\n"; + fw.indent() << "min_v " << trans._min_v << "\n"; + fw.indent() << "max_v " << trans._max_v << "\n"; + fw.indent() << "factor " << trans._factor << "\n"; + fw.indent() << "offset " << trans._offset << "\n"; + return true; + } +private: + double computeScaleFactor(osg::NodeVisitor* nv) const + { + if (!nv) + return 1; -int -SGRotateAnimation::update() -{ - if (_condition == 0 || _condition->test()) { + double scale_factor = (toOsg(_center) - nv->getEyePoint()).length(); 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; + scale_factor = _factor * scale_factor + _offset; } else { - _position_deg = _table->interpolate(_prop->getDoubleValue()); + scale_factor = _table->interpolate( scale_factor ); } - set_rotation(_matrix, _position_deg, _center, _axis); - ((ssgTransform *)_branch)->setTransform(_matrix); + if (scale_factor < _min_v) + scale_factor = _min_v; + if (scale_factor > _max_v) + scale_factor = _max_v; + + return scale_factor; } - return 2; -} - -//////////////////////////////////////////////////////////////////////// -// Implementation of SGBlendAnimation -//////////////////////////////////////////////////////////////////////// + SGSharedPtr _table; + SGVec3d _center; + double _min_v; + double _max_v; + double _factor; + double _offset; +}; -SGBlendAnimation::SGBlendAnimation( SGPropertyNode *prop_root, - SGPropertyNode_ptr props ) - : SGAnimation(props, new ssgTransform), - _prop((SGPropertyNode *)prop_root->getNode(props->getStringValue("property", "/null"), true)), - _table(read_interpolation_table(props)), - _prev_value(1.0), - _offset(props->getDoubleValue("offset", 0.0)), - _factor(props->getDoubleValue("factor", 1.0)), - _has_min(props->hasValue("min")), - _min(props->getDoubleValue("min", 0.0)), - _has_max(props->hasValue("max")), - _max(props->getDoubleValue("max", 1.0)) + +SGDistScaleAnimation::SGDistScaleAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot) : + SGAnimation(configNode, modelRoot) { } -SGBlendAnimation::~SGBlendAnimation () +osg::Group* +SGDistScaleAnimation::createAnimationGroup(osg::Group& parent) { - delete _table; + Transform* transform = new Transform(getConfig()); + parent.addChild(transform); + return transform; } -int -SGBlendAnimation::update() +namespace { - double _blend; + osgDB::RegisterDotOsgWrapperProxy distScaleAnimationTransformProxy + ( + new SGDistScaleAnimation::Transform, + "SGDistScaleAnimation::Transform", + "Object Node Transform SGDistScaleAnimation::Transform Group", + 0, + &SGDistScaleAnimation::Transform::writeLocalData + ); +} + +//////////////////////////////////////////////////////////////////////// +// Implementation of flash animation +//////////////////////////////////////////////////////////////////////// - if (_table == 0) { - _blend = 1.0 - (_prop->getDoubleValue() * _factor + _offset); +class SGFlashAnimation::Transform : public osg::Transform { +public: + Transform() : _power(0.0), _factor(0.0), _offset(0.0), _min_v(0.0), + _max_v(0.0), _two_sides(false) + {} + + Transform(const Transform& rhs, + const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY) + : osg::Transform(rhs, copyOp), _center(rhs._center), _axis(rhs._axis), + _power(rhs._power), _factor(rhs._factor), _offset(rhs._offset), + _min_v(rhs._min_v), _max_v(rhs._max_v), _two_sides(rhs._two_sides) + { + } + META_Node(simgear, SGFlashAnimation::Transform); - if (_has_min && (_blend < _min)) - _blend = _min; - if (_has_max && (_blend > _max)) - _blend = _max; - } else { - _blend = _table->interpolate(_prop->getDoubleValue()); + Transform(const SGPropertyNode* configNode) + { + setReferenceFrame(RELATIVE_RF); + setName(configNode->getStringValue("name", "flash animation")); + setStateSet(getNormalizeStateSet()); + + _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] = configNode->getFloatValue("center/x-m", 0); + _center[1] = configNode->getFloatValue("center/y-m", 0); + _center[2] = configNode->getFloatValue("center/z-m", 0); + + _offset = configNode->getFloatValue("offset", 0); + _factor = configNode->getFloatValue("factor", 1); + _power = configNode->getFloatValue("power", 1); + _two_sides = configNode->getBoolValue("two-sides", false); + + _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon()); + _max_v = configNode->getFloatValue("max", 1); + } + 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); + matrix.preMult(transform); + return true; + } + + virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix, + osg::NodeVisitor* nv) const + { + 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; } - if (_blend != _prev_value) { - _prev_value = _blend; - change_alpha( _branch, _blend ); + static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw) + { + const Transform& trans = static_cast(obj); + fw.indent() << "center " << trans._center[0] << " " + << trans._center[1] << " " << trans._center[2] << " " << "\n"; + fw.indent() << "axis " << trans._axis[0] << " " + << trans._axis[1] << " " << trans._axis[2] << " " << "\n"; + fw.indent() << "power " << trans._power << " \n"; + fw.indent() << "min_v " << trans._min_v << "\n"; + fw.indent() << "max_v " << trans._max_v << "\n"; + fw.indent() << "factor " << trans._factor << "\n"; + fw.indent() << "offset " << trans._offset << "\n"; + fw.indent() << "twosides " << (trans._two_sides ? "true" : "false") << "\n"; + return true; + } +private: + double computeScaleFactor(osg::NodeVisitor* nv) const + { + if (!nv) + return 1; + + osg::Vec3 localEyeToCenter = nv->getEyePoint() - _center; + localEyeToCenter.normalize(); + + double cos_angle = localEyeToCenter*_axis; + double scale_factor = 0; + if ( _two_sides && cos_angle < 0 ) + scale_factor = _factor * pow( -cos_angle, _power ) + _offset; + else if ( cos_angle > 0 ) + scale_factor = _factor * pow( cos_angle, _power ) + _offset; + + if ( scale_factor < _min_v ) + scale_factor = _min_v; + if ( scale_factor > _max_v ) + scale_factor = _max_v; + + return scale_factor; } - return 1; -} + 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; + } - -//////////////////////////////////////////////////////////////////////// -// Implementation of SGTranslateAnimation -//////////////////////////////////////////////////////////////////////// +private: + osg::Vec3 _center; + osg::Vec3 _axis; + double _power, _factor, _offset, _min_v, _max_v; + bool _two_sides; +}; -SGTranslateAnimation::SGTranslateAnimation( SGPropertyNode *prop_root, - SGPropertyNode_ptr props ) - : SGAnimation(props, new ssgTransform), - _prop((SGPropertyNode *)prop_root->getNode(props->getStringValue("property", "/null"), true)), - _offset_m(props->getDoubleValue("offset-m", 0.0)), - _factor(props->getDoubleValue("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); - sgNormalizeVec3(_axis); +SGFlashAnimation::SGFlashAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot) : + SGAnimation(configNode, modelRoot) +{ } -SGTranslateAnimation::~SGTranslateAnimation () +osg::Group* +SGFlashAnimation::createAnimationGroup(osg::Group& parent) { - delete _table; + Transform* transform = new Transform(getConfig()); + parent.addChild(transform); + return transform; } -int -SGTranslateAnimation::update() +namespace { - if (_condition == 0 || _condition->test()) { - 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()); - } - set_translation(_matrix, _position_m, _axis); - ((ssgTransform *)_branch)->setTransform(_matrix); - } - return 2; + osgDB::RegisterDotOsgWrapperProxy flashAnimationTransformProxy + ( + new SGFlashAnimation::Transform, + "SGFlashAnimation::Transform", + "Object Node Transform SGFlashAnimation::Transform Group", + 0, + &SGFlashAnimation::Transform::writeLocalData + ); } - - //////////////////////////////////////////////////////////////////////// -// Implementation of SGScaleAnimation +// Implementation of billboard animation //////////////////////////////////////////////////////////////////////// -SGScaleAnimation::SGScaleAnimation( SGPropertyNode *prop_root, - SGPropertyNode_ptr props ) - : SGAnimation(props, new ssgTransform), - _prop((SGPropertyNode *)prop_root->getNode(props->getStringValue("property", "/null"), true)), - _x_factor(props->getDoubleValue("x-factor", 1.0)), - _y_factor(props->getDoubleValue("y-factor", 1.0)), - _z_factor(props->getDoubleValue("z-factor", 1.0)), - _x_offset(props->getDoubleValue("x-offset", 1.0)), - _y_offset(props->getDoubleValue("y-offset", 1.0)), - _z_offset(props->getDoubleValue("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")) -{ -} - -SGScaleAnimation::~SGScaleAnimation () -{ - delete _table; -} - -int -SGScaleAnimation::update() -{ - 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()); +class SGBillboardAnimation::Transform : public osg::Transform { +public: + Transform() : _spherical(true) {} + Transform(const Transform& rhs, + const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY) + : osg::Transform(rhs, copyOp), _spherical(rhs._spherical) {} + META_Node(simgear, SGBillboardAnimation::Transform); + Transform(const SGPropertyNode* configNode) : + _spherical(configNode->getBoolValue("spherical", true)) + { + setReferenceFrame(RELATIVE_RF); + setName(configNode->getStringValue("name", "billboard animation")); } - - 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()); + virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix, + osg::NodeVisitor* nv) const + { + // 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 + { + // Hmm, don't yet know how to get that back ... + return false; + } + static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw) + { + const Transform& trans = static_cast(obj); - 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()); + fw.indent() << (trans._spherical ? "true" : "false") << "\n"; + return true; } +private: + bool _spherical; +}; - set_scale(_matrix, _x_scale, _y_scale, _z_scale ); - ((ssgTransform *)_branch)->setTransform(_matrix); - return 2; + +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; +} +namespace +{ + osgDB::RegisterDotOsgWrapperProxy billboardAnimationTransformProxy + ( + new SGBillboardAnimation::Transform, + "SGBillboardAnimation::Transform", + "Object Node Transform SGBillboardAnimation::Transform Group", + 0, + &SGBillboardAnimation::Transform::writeLocalData + ); +} + //////////////////////////////////////////////////////////////////////// -// Implementation of SGTexRotateAnimation +// Implementation of a range animation //////////////////////////////////////////////////////////////////////// -SGTexRotateAnimation::SGTexRotateAnimation( SGPropertyNode *prop_root, - SGPropertyNode_ptr props ) - : SGAnimation(props, new ssgTexTrans), - _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)) -{ - _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); - sgNormalizeVec3(_axis); -} - -SGTexRotateAnimation::~SGTexRotateAnimation () -{ - delete _table; -} - -int -SGTexRotateAnimation::update() -{ - 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()); +class SGRangeAnimation::UpdateCallback : public osg::NodeCallback { +public: + UpdateCallback(const SGCondition* condition, + const SGExpressiond* minAnimationValue, + const SGExpressiond* 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 { + lod->setRange(0, 0, SGLimitsf::max()); + } + traverse(node, nv); } - set_rotation(_matrix, _position_deg, _center, _axis); - ((ssgTexTrans *)_branch)->setTransform(_matrix); - return 2; -} +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, true); + SGSharedPtr value; + value = new SGPropertyExpression(inputProperty); + + value = read_factor_offset(configNode, value, "min-factor", "min-offset"); + _minAnimationValue = value->simplify(); + } + inputPropertyName = configNode->getStringValue("max-property", ""); + if (!inputPropertyName.empty()) { + SGPropertyNode* inputProperty; + inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true); -//////////////////////////////////////////////////////////////////////// -// Implementation of SGTexTranslateAnimation -//////////////////////////////////////////////////////////////////////// + SGSharedPtr value; + value = new SGPropertyExpression(inputProperty); -SGTexTranslateAnimation::SGTexTranslateAnimation( SGPropertyNode *prop_root, - SGPropertyNode_ptr props ) - : SGAnimation(props, new ssgTexTrans), - _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)) -{ - _axis[0] = props->getFloatValue("axis/x", 0); - _axis[1] = props->getFloatValue("axis/y", 0); - _axis[2] = props->getFloatValue("axis/z", 0); - sgNormalizeVec3(_axis); -} - -SGTexTranslateAnimation::~SGTexTranslateAnimation () -{ - delete _table; -} - -int -SGTexTranslateAnimation::update() -{ - 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)); + value = read_factor_offset(configNode, value, "max-factor", "max-offset"); + _maxAnimationValue = value->simplify(); } - set_translation(_matrix, _position, _axis); - ((ssgTexTrans *)_branch)->setTransform(_matrix); - return 2; + + _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"); -//////////////////////////////////////////////////////////////////////// -// Implementation of SGTexMultipleAnimation -//////////////////////////////////////////////////////////////////////// + osg::LOD* lod = new osg::LOD; + lod->setName("range animation node"); + parent.addChild(lod); -SGTexMultipleAnimation::SGTexMultipleAnimation( SGPropertyNode *prop_root, - SGPropertyNode_ptr props ) - : SGAnimation(props, new ssgTexTrans), - _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); - sgNormalizeVec3(_transform[i].axis); - _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); - sgNormalizeVec3(_transform[i].axis); - _num_transforms++; - } + 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; } -SGTexMultipleAnimation::~SGTexMultipleAnimation () + +//////////////////////////////////////////////////////////////////////// +// Implementation of a select animation +//////////////////////////////////////////////////////////////////////// + +SGSelectAnimation::SGSelectAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot) : + SGAnimation(configNode, modelRoot) { - delete [] _transform; } -int -SGTexMultipleAnimation::update() +osg::Group* +SGSelectAnimation::createAnimationGroup(osg::Group& parent) { - int i; - sgMat4 tmatrix; - sgMakeIdentMat4(tmatrix); - 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)); - } - set_translation(_transform[i].matrix, _transform[i].position, _transform[i].axis); - sgPreMultMat4(tmatrix, _transform[i].matrix); - - } 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()); - } - set_rotation(_transform[i].matrix, _transform[i].position, _transform[i].center, _transform[i].axis); - sgPreMultMat4(tmatrix, _transform[i].matrix); - } - } - ((ssgTexTrans *)_branch)->setTransform(tmatrix); - return 2; + // 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; + simgear::ConditionNode* cn = new simgear::ConditionNode; + cn->setName("select animation node"); + cn->setCondition(condition.ptr()); + osg::Group* grp = new osg::Group; + cn->addChild(grp); + parent.addChild(cn); + return grp; } //////////////////////////////////////////////////////////////////////// -// Implementation of SGAlphaTestAnimation +// Implementation of alpha test animation //////////////////////////////////////////////////////////////////////// -SGAlphaTestAnimation::SGAlphaTestAnimation(SGPropertyNode_ptr props) - : SGAnimation(props, new ssgBranch) +SGAlphaTestAnimation::SGAlphaTestAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot) : + SGAnimation(configNode, modelRoot) { - _alpha_clamp = props->getFloatValue("alpha-factor", 0.0); } -SGAlphaTestAnimation::~SGAlphaTestAnimation () +namespace { -} +// Keep one copy of the most common alpha test its state set. +ReentrantMutex alphaTestMutex; +osg::ref_ptr standardAlphaFunc; +osg::ref_ptr alphaFuncStateSet; -void SGAlphaTestAnimation::init() +osg::AlphaFunc* makeAlphaFunc(float clamp) { - setAlphaClampToBranch(_branch,_alpha_clamp); + ScopedLock lock(alphaTestMutex); + if (osg::equivalent(clamp, 0.01f)) { + if (standardAlphaFunc.valid()) + return standardAlphaFunc.get(); + clamp = .01; + } + osg::AlphaFunc* alphaFunc = new osg::AlphaFunc; + alphaFunc->setFunction(osg::AlphaFunc::GREATER); + alphaFunc->setReferenceValue(clamp); + alphaFunc->setDataVariance(osg::Object::STATIC); + if (osg::equivalent(clamp, 0.01f)) + standardAlphaFunc = alphaFunc; + return alphaFunc; +} + +osg::StateSet* makeAlphaTestStateSet(float clamp) +{ + using namespace OpenThreads; + ScopedLock lock(alphaTestMutex); + if (osg::equivalent(clamp, 0.01f)) { + if (alphaFuncStateSet.valid()) + return alphaFuncStateSet.get(); + } + osg::AlphaFunc* alphaFunc = makeAlphaFunc(clamp); + osg::StateSet* stateSet = new osg::StateSet; + stateSet->setAttributeAndModes(alphaFunc, + (osg::StateAttribute::ON + | osg::StateAttribute::OVERRIDE)); + stateSet->setDataVariance(osg::Object::STATIC); + if (osg::equivalent(clamp, 0.01f)) + alphaFuncStateSet = stateSet; + return stateSet; } - -void SGAlphaTestAnimation::setAlphaClampToBranch(ssgBranch *b, float clamp) +} +void +SGAlphaTestAnimation::install(osg::Node& node) { - int nb = b->getNumKids(); - for (int i = 0; igetKid(i); - if (e->isAKindOf(ssgTypeLeaf())) { - ssgSimpleState*s = (ssgSimpleState*)((ssgLeaf*)e)->getState(); - s->enable( GL_ALPHA_TEST ); - s->setAlphaClamp( clamp ); - } else if (e->isAKindOf(ssgTypeBranch())) { - setAlphaClampToBranch( (ssgBranch*)e, clamp ); - } + SGAnimation::install(node); + + float alphaClamp = getConfig()->getFloatValue("alpha-factor", 0); + osg::StateSet* stateSet = node.getStateSet(); + if (!stateSet) { + node.setStateSet(makeAlphaTestStateSet(alphaClamp)); + } else { + stateSet->setAttributeAndModes(makeAlphaFunc(alphaClamp), + (osg::StateAttribute::ON + | osg::StateAttribute::OVERRIDE)); } } - -//////////////////////////////////////////////////////////////////////// -// Implementation of SGMaterialAnimation -//////////////////////////////////////////////////////////////////////// - -SGMaterialAnimation::SGMaterialAnimation( SGPropertyNode *prop_root, - SGPropertyNode_ptr props, const SGPath &texture_path) - : SGAnimation(props, new ssgBranch), - _prop_root(prop_root), - _prop_base(""), - _texture_base(texture_path), - _cached_material(0), - _cloned_material(0), - _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 += '/'; +////////////////////////////////////////////////////////////////////// +// Blend animation installer +////////////////////////////////////////////////////////////////////// + +// XXX This needs to be replaced by something using TexEnvCombine to +// change the blend factor. Changing the alpha values in the geometry +// is bogus. +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); + 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; + for (unsigned k = 0; k < vec4Array->size(); ++k) { + (*vec4Array)[k][3] = _blend; + } + vec4Array->dirty(); + updateStateSet(drawable->getStateSet()); } - - 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; + } + 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); } - - _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); - _update |= TEXTURE; + } +private: + float _blend; +}; + +class SGBlendAnimation::UpdateCallback : public osg::NodeCallback { +public: + UpdateCallback(const SGPropertyNode* configNode, const SGExpressiond* 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; +}; - 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; -} - -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); -} - -int SGMaterialAnimation::update() -{ - if (_condition && !_condition->test()) - return 2; - - 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; - } - return 2; -} -void SGMaterialAnimation::updateColorGroup(ColorSpec *col, int flag) +SGBlendAnimation::SGBlendAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot) + : SGAnimation(configNode, modelRoot), + _animationValue(read_value(configNode, modelRoot, "", 0, 1)) { - 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; } -void SGMaterialAnimation::cloneMaterials(ssgBranch *b) +osg::Group* +SGBlendAnimation::createAnimationGroup(osg::Group& parent) { - for (int i = 0; i < b->getNumKids(); i++) - cloneMaterials((ssgBranch *)b->getKid(i)); - - if (!b->isAKindOf(ssgTypeLeaf()) || !((ssgLeaf *)b)->hasState()) - return; + if (!_animationValue) + return 0; - ssgSimpleState *s = (ssgSimpleState *)((ssgLeaf *)b)->getState(); - if (!_cached_material || _cached_material != s) { - _cached_material = s; - _cloned_material = (ssgSimpleState *)s->clone(SSG_CLONE_STATE); - } - ((ssgLeaf *)b)->setState(_cloned_material); + osg::Group* group = new osg::Switch; + group->setName("blend animation node"); + group->setUpdateCallback(new UpdateCallback(getConfig(), _animationValue)); + parent.addChild(group); + return group; } -void SGMaterialAnimation::setMaterialBranch(ssgBranch *b) +void +SGBlendAnimation::install(osg::Node& node) { - for (int i = 0; i < b->getNumKids(); i++) - setMaterialBranch((ssgBranch *)b->getKid(i)); + SGAnimation::install(node); + // make sure we do not change common geometries, + // that also creates new display lists for these subgeometries. + cloneDrawables(node); + DoDrawArraysVisitor visitor; + node.accept(visitor); +} - if (!b->isAKindOf(ssgTypeLeaf()) || !((ssgLeaf *)b)->hasState()) - return; + +////////////////////////////////////////////////////////////////////// +// Timed animation installer +////////////////////////////////////////////////////////////////////// - ssgSimpleState *s = (ssgSimpleState *)((ssgLeaf *)b)->getState(); - if (_update & DIFFUSE) { - float *v = _diff.rgba(); - SGfloat alpha = s->getMaterial(GL_DIFFUSE)[3]; - s->setColourMaterial(GL_DIFFUSE); - s->enable(GL_COLOR_MATERIAL); - s->setMaterial(GL_DIFFUSE, v[0], v[1], v[2], alpha); - s->disable(GL_COLOR_MATERIAL); + +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)); + } } - if (_update & AMBIENT) { - s->setColourMaterial(GL_AMBIENT); - s->enable(GL_COLOR_MATERIAL); - s->setMaterial(GL_AMBIENT, _amb.rgba()); - s->disable(GL_COLOR_MATERIAL); + } + 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); } - if (_update & EMISSION) - s->setMaterial(GL_EMISSION, _emis.rgba()); - if (_update & SPECULAR) - s->setMaterial(GL_SPECULAR, _spec.rgba()); - if (_update & SHININESS) - s->setShininess(clamp(_shi, 0.0, 128.0)); - if (_update & TRANSPARENCY) { - SGfloat *v = s->getMaterial(GL_DIFFUSE); - float trans = _trans.value * _trans.factor + _trans.offset; - trans = trans < _trans.min ? _trans.min : trans > _trans.max ? _trans.max : trans; - s->setMaterial(GL_DIFFUSE, v[0], v[1], v[2], trans); + // make sure the current index is an duration that really exists + _current_index = _current_index % nChildren; + + // 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 { + double dt = t - _last_time_sec; + if (_use_personality) + dt *= 1 + 0.2*(0.5 - sg_random()); + _reminder += dt; + _last_time_sec = t; } - if (_update & THRESHOLD) - s->setAlphaClamp(clamp(_thresh)); - if (_update & TEXTURE) - s->setTexture(_texture.c_str()); - if (_update & (TEXTURE|TRANSPARENCY)) { - SGfloat alpha = s->getMaterial(GL_DIFFUSE)[3]; - ssgTexture *tex = s->getTexture(); - if ((tex && tex->hasAlpha()) || alpha < 0.999) { - s->setColourMaterial(GL_DIFFUSE); - s->enable(GL_COLOR_MATERIAL); - s->enable(GL_BLEND); - s->enable(GL_ALPHA_TEST); - s->setTranslucent(); - s->disable(GL_COLOR_MATERIAL); - } else { - s->disable(GL_BLEND); - s->disable(GL_ALPHA_TEST); - s->setOpaque(); - } + + double currentDuration = _durations[_current_index].get(); + while (currentDuration < _reminder) { + _reminder -= currentDuration; + _current_index = (_current_index + 1) % nChildren; + currentDuration = _durations[_current_index].get(); } - s->force(); -} + 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; +} //////////////////////////////////////////////////////////////////////// -// Implementation of SGFlashAnimation +// dynamically switch on/off shadows //////////////////////////////////////////////////////////////////////// -SGFlashAnimation::SGFlashAnimation(SGPropertyNode_ptr props) - : SGAnimation( props, new SGCustomTransform ) -{ - _axis[0] = props->getFloatValue("axis/x", 0); - _axis[1] = props->getFloatValue("axis/y", 0); - _axis[2] = props->getFloatValue("axis/z", 1); - - _center[0] = props->getFloatValue("center/x-m", 0); - _center[1] = props->getFloatValue("center/y-m", 0); - _center[2] = props->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); - _min_v = props->getFloatValue("min", 0.0); - _max_v = props->getFloatValue("max", 1.0); +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_CASTSHADOW_BIT | node->getNodeMask()); + else + node->setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node->getNodeMask()); + traverse(node, nv); + } - ((SGCustomTransform *)_branch)->setTransCallback( &SGFlashAnimation::flashCallback, this ); -} +private: + SGSharedPtr _condition; +}; -SGFlashAnimation::~SGFlashAnimation() +SGShadowAnimation::SGShadowAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot) : + SGAnimation(configNode, modelRoot) { } -void SGFlashAnimation::flashCallback( sgMat4 r, sgFrustum *f, sgMat4 m, void *d ) +osg::Group* +SGShadowAnimation::createAnimationGroup(osg::Group& parent) { - ((SGFlashAnimation *)d)->flashCallback( r, f, m ); + 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; } -void SGFlashAnimation::flashCallback( sgMat4 r, sgFrustum *f, sgMat4 m ) -{ - sgVec3 transformed_axis; - sgXformVec3( transformed_axis, _axis, m ); - sgNormalizeVec3( transformed_axis ); + +//////////////////////////////////////////////////////////////////////// +// Implementation of SGTexTransformAnimation +//////////////////////////////////////////////////////////////////////// - sgVec3 view; - sgFullXformPnt3( view, _center, m ); - sgNormalizeVec3( view ); +class SGTexTransformAnimation::Transform : public SGReferenced { +public: + Transform() : + _value(0) + {} + virtual ~Transform() + { } + void setValue(double value) + { _value = value; } + virtual void transform(osg::Matrix&) = 0; +protected: + double _value; +}; + +class SGTexTransformAnimation::Translation : + public SGTexTransformAnimation::Transform { +public: + Translation(const SGVec3d& axis) : + _axis(axis) + { } + virtual void 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 void 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, SGExpressiond* value) + { + Entry entry = { transform, value }; + transform->transform(_matrix); + _transforms.push_back(entry); + } - float cos_angle = -sgScalarProductVec3( transformed_axis, view ); - float scale_factor = 0.f; - if ( _two_sides && cos_angle < 0 ) - scale_factor = _factor * (float)pow( -cos_angle, _power ) + _offset; - else if ( cos_angle > 0 ) - scale_factor = _factor * (float)pow( cos_angle, _power ) + _offset; +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(); + stateSet->setDataVariance(osg::Object::DYNAMIC); + 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"); + } - if ( scale_factor < _min_v ) - scale_factor = _min_v; - if ( scale_factor > _max_v ) - scale_factor = _max_v; + texMat->setUpdateCallback(updateCallback); + stateSet->setTextureAttribute(0, texMat); + parent.addChild(group); + return group; +} - sgMat4 transform; - sgMakeIdentMat4( transform ); - 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 ); +void +SGTexTransformAnimation::appendTexTranslate(const SGPropertyNode* config, + UpdateCallback* updateCallback) +{ + std::string propertyName = config->getStringValue("property", ""); + SGSharedPtr value; + if (propertyName.empty()) + value = new SGConstExpression(0); + else { + SGPropertyNode* inputProperty = getModelRoot()->getNode(propertyName, true); + value = new SGPropertyExpression(inputProperty); + } - sgCopyMat4( r, m ); - sgPreMultMat4( r, transform ); + SGInterpTable* table = read_interpolation_table(config); + if (table) { + value = new SGInterpTableExpression(value, table); + double biasValue = config->getDoubleValue("bias", 0); + if (biasValue != 0) + value = new SGBiasExpression(value, biasValue); + value = new SGStepExpression(value, + config->getDoubleValue("step", 0), + config->getDoubleValue("scroll", 0)); + value = value->simplify(); + } else { + double biasValue = config->getDoubleValue("bias", 0); + if (biasValue != 0) + value = new SGBiasExpression(value, biasValue); + value = new SGStepExpression(value, + config->getDoubleValue("step", 0), + config->getDoubleValue("scroll", 0)); + value = read_offset_factor(config, value, "factor", "offset"); + + if (config->hasChild("min") || config->hasChild("max")) { + double minClip = config->getDoubleValue("min", -SGLimitsd::max()); + double maxClip = config->getDoubleValue("max", SGLimitsd::max()); + value = new SGClipExpression(value, minClip, maxClip); + } + value = value->simplify(); + } + SGVec3d axis(config->getDoubleValue("axis/x", 0), + config->getDoubleValue("axis/y", 0), + config->getDoubleValue("axis/z", 0)); + Translation* translation; + translation = new Translation(normalize(axis)); + translation->setValue(config->getDoubleValue("starting-position", 0)); + updateCallback->appendTransform(translation, value); +} + +void +SGTexTransformAnimation::appendTexRotate(const SGPropertyNode* config, + UpdateCallback* updateCallback) +{ + std::string propertyName = config->getStringValue("property", ""); + SGSharedPtr value; + if (propertyName.empty()) + value = new SGConstExpression(0); + else { + SGPropertyNode* inputProperty = getModelRoot()->getNode(propertyName, true); + value = new SGPropertyExpression(inputProperty); + } + + SGInterpTable* table = read_interpolation_table(config); + if (table) { + value = new SGInterpTableExpression(value, table); + double biasValue = config->getDoubleValue("bias", 0); + if (biasValue != 0) + value = new SGBiasExpression(value, biasValue); + value = new SGStepExpression(value, + config->getDoubleValue("step", 0), + config->getDoubleValue("scroll", 0)); + value = value->simplify(); + } else { + double biasValue = config->getDoubleValue("bias", 0); + if (biasValue != 0) + value = new SGBiasExpression(value, biasValue); + value = new SGStepExpression(value, + config->getDoubleValue("step", 0), + config->getDoubleValue("scroll", 0)); + value = read_offset_factor(config, value, "factor", "offset-deg"); + + if (config->hasChild("min-deg") || config->hasChild("max-deg")) { + double minClip = config->getDoubleValue("min-deg", -SGLimitsd::max()); + double maxClip = config->getDoubleValue("max-deg", SGLimitsd::max()); + value = new SGClipExpression(value, minClip, maxClip); + } + value = value->simplify(); + } + SGVec3d axis(config->getDoubleValue("axis/x", 0), + config->getDoubleValue("axis/y", 0), + config->getDoubleValue("axis/z", 0)); + SGVec3d center(config->getDoubleValue("center/x", 0), + config->getDoubleValue("center/y", 0), + config->getDoubleValue("center/z", 0)); + Rotation* rotation; + rotation = new Rotation(normalize(axis), center); + rotation->setValue(config->getDoubleValue("starting-position-deg", 0)); + updateCallback->appendTransform(rotation, value); } - //////////////////////////////////////////////////////////////////////// -// Implementation of SGDistScaleAnimation +// Implementation of SGPickAnimation //////////////////////////////////////////////////////////////////////// -SGDistScaleAnimation::SGDistScaleAnimation(SGPropertyNode_ptr props) - : SGAnimation( props, new SGCustomTransform ), - _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); - ((SGCustomTransform *)_branch)->setTransCallback( &SGDistScaleAnimation::distScaleCallback, this ); -} +class SGPickAnimation::PickCallback : public SGPickCallback { +public: + PickCallback(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot) : + _repeatable(configNode->getBoolValue("repeatable", false)), + _repeatInterval(configNode->getDoubleValue("interval-sec", 0.1)) + { + SG_LOG(SG_INPUT, SG_DEBUG, "Reading all bindings"); + std::vector bindings; -SGDistScaleAnimation::~SGDistScaleAnimation() -{ -} + bindings = configNode->getChildren("button"); + for (unsigned int i = 0; i < bindings.size(); ++i) { + _buttons.push_back( bindings[i]->getIntValue() ); + } + bindings = configNode->getChildren("binding"); + for (unsigned int i = 0; i < bindings.size(); ++i) { + _bindingsDown.push_back(new SGBinding(bindings[i], modelRoot)); + } -void SGDistScaleAnimation::distScaleCallback( sgMat4 r, sgFrustum *f, sgMat4 m, void *d ) -{ - ((SGDistScaleAnimation *)d)->distScaleCallback( r, f, m ); -} + const SGPropertyNode* upNode = configNode->getChild("mod-up"); + if (!upNode) + return; + bindings = upNode->getChildren("binding"); + for (unsigned int i = 0; i < bindings.size(); ++i) { + _bindingsUp.push_back(new SGBinding(bindings[i], modelRoot)); + } + } + virtual bool buttonPressed(int button, const Info&) + { + bool found = false; + for( std::vector::iterator it = _buttons.begin(); it != _buttons.end(); ++it ) { + if( *it == button ) { + found = true; + break; + } + } + if (!found ) + return false; + SGBindingList::const_iterator i; + for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i) + (*i)->fire(); + _repeatTime = -_repeatInterval; // anti-bobble: delay start of repeat + return true; + } + virtual void buttonReleased(void) + { + SGBindingList::const_iterator i; + for (i = _bindingsUp.begin(); i != _bindingsUp.end(); ++i) + (*i)->fire(); + } + virtual void update(double dt) + { + if (!_repeatable) + return; + + _repeatTime += dt; + while (_repeatInterval < _repeatTime) { + _repeatTime -= _repeatInterval; + SGBindingList::const_iterator i; + for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i) + (*i)->fire(); + } + } +private: + SGBindingList _bindingsDown; + SGBindingList _bindingsUp; + std::vector _buttons; + bool _repeatable; + double _repeatInterval; + double _repeatTime; +}; + +class VncVisitor : public osg::NodeVisitor { + public: + VncVisitor(double x, double y, int mask) : + osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), + _texX(x), _texY(y), _mask(mask), _done(false) + { + SG_LOG(SG_INPUT, SG_DEBUG, "VncVisitor constructor " + << x << "," << y << " mask " << mask); + } -void SGDistScaleAnimation::distScaleCallback( sgMat4 r, sgFrustum *f, sgMat4 m ) -{ - sgVec3 view; - sgFullXformPnt3( view, _center, m ); + virtual void apply(osg::Node &node) + { + // Some nodes have state sets attached + touchStateSet(node.getStateSet()); + if (!_done) + traverse(node); + if (_done) return; + // See whether we are a geode worth exploring + osg::Geode *g = dynamic_cast(&node); + if (!g) return; + // Go find all its drawables + int i = g->getNumDrawables(); + while (--i >= 0) { + osg::Drawable *d = g->getDrawable(i); + if (d) touchDrawable(*d); + } + // Out of optimism, do the same for EffectGeode + simgear::EffectGeode *eg = dynamic_cast(&node); + if (!eg) return; + for (simgear::EffectGeode::DrawablesIterator di = eg->drawablesBegin(); + di != eg->drawablesEnd(); di++) { + touchDrawable(**di); + } + // Now see whether the EffectGeode has an Effect + simgear::Effect *e = eg->getEffect(); + if (e) { + touchStateSet(e->getDefaultStateSet()); + } + } - float scale_factor = sgLengthVec3( view ); - 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; - } else { - scale_factor = _table->interpolate( scale_factor ); + inline void touchDrawable(osg::Drawable &d) + { + osg::StateSet *ss = d.getStateSet(); + touchStateSet(ss); + } + + void touchStateSet(osg::StateSet *ss) + { + if (!ss) return; + osg::StateAttribute *sa = ss->getTextureAttribute(0, + osg::StateAttribute::TEXTURE); + if (!sa) return; + osg::Texture *t = sa->asTexture(); + if (!t) return; + osg::Image *img = t->getImage(0); + if (!img) return; + if (!_done) { + int pixX = _texX * img->s(); + int pixY = _texY * img->t(); + _done = img->sendPointerEvent(pixX, pixY, _mask); + SG_LOG(SG_INPUT, SG_DEBUG, "VncVisitor image said " << _done + << " to coord " << pixX << "," << pixY); + } } - sgMat4 transform; - sgMakeIdentMat4( transform ); - 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 ); + inline bool wasSuccessful() + { + return _done; + } - sgCopyMat4( r, m ); - sgPreMultMat4( r, transform ); -} + private: + double _texX, _texY; + int _mask; + bool _done; +}; -//////////////////////////////////////////////////////////////////////// -// Implementation of SGShadowAnimation -//////////////////////////////////////////////////////////////////////// -SGShadowAnimation::SGShadowAnimation ( SGPropertyNode *prop_root, - SGPropertyNode_ptr props ) - : SGAnimation(props, new ssgBranch), - _condition(0), - _condition_value(true) -{ - animation_type = 1; - SGPropertyNode_ptr node = props->getChild("condition"); - if (node != 0) { - _condition = sgReadCondition(prop_root, node); - _condition_value = false; - } -} +class SGPickAnimation::VncCallback : public SGPickCallback { +public: + VncCallback(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot, + osg::Group *node) + : _node(node) + { + SG_LOG(SG_INPUT, SG_DEBUG, "Configuring VNC callback"); + const char *cornernames[3] = {"top-left", "top-right", "bottom-left"}; + SGVec3d *cornercoords[3] = {&_topLeft, &_toRight, &_toDown}; + for (int c =0; c < 3; c++) { + const SGPropertyNode* cornerNode = configNode->getChild(cornernames[c]); + *cornercoords[c] = SGVec3d( + cornerNode->getDoubleValue("x"), + cornerNode->getDoubleValue("y"), + cornerNode->getDoubleValue("z")); + } + _toRight -= _topLeft; + _toDown -= _topLeft; + _squaredRight = dot(_toRight, _toRight); + _squaredDown = dot(_toDown, _toDown); + } -SGShadowAnimation::~SGShadowAnimation () -{ - delete _condition; -} + virtual bool buttonPressed(int button, const Info& info) + { + SGVec3d loc(info.local); + SG_LOG(SG_INPUT, SG_DEBUG, "VNC pressed " << button << ": " << loc); + loc -= _topLeft; + _x = dot(loc, _toRight) / _squaredRight; + _y = dot(loc, _toDown) / _squaredDown; + if (_x<0) _x = 0; else if (_x > 1) _x = 1; + if (_y<0) _y = 0; else if (_y > 1) _y = 1; + VncVisitor vv(_x, _y, 1 << button); + _node->accept(vv); + return vv.wasSuccessful(); -int -SGShadowAnimation::update() -{ - if (_condition) - _condition_value = _condition->test(); - return 2; -} + } + virtual void buttonReleased(void) + { + SG_LOG(SG_INPUT, SG_DEBUG, "VNC release"); + VncVisitor vv(_x, _y, 0); + _node->accept(vv); + } + virtual void update(double dt) + { + } +private: + double _x, _y; + osg::ref_ptr _node; + SGVec3d _topLeft, _toRight, _toDown; + double _squaredRight, _squaredDown; +}; + +SGPickAnimation::SGPickAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot) : + SGAnimation(configNode, modelRoot) +{ +} + +namespace +{ +Mutex colorModeUniformMutex; +osg::ref_ptr colorModeUniform; +} + +osg::Group* +SGPickAnimation::createAnimationGroup(osg::Group& parent) +{ + osg::Group* commonGroup = new osg::Group; + + // Contains the normal geometry that is interactive + osg::ref_ptr normalGroup = new osg::Group; + normalGroup->setName("pick normal group"); + normalGroup->addChild(commonGroup); + + // Used to render the geometry with just yellow edges + osg::Group* highlightGroup = new osg::Group; + highlightGroup->setName("pick highlight group"); + highlightGroup->setNodeMask(SG_NODEMASK_PICK_BIT); + highlightGroup->addChild(commonGroup); + SGSceneUserData* ud; + ud = SGSceneUserData::getOrCreateSceneUserData(commonGroup); + + // add actions that become macro and command invocations + std::vector actions; + actions = getConfig()->getChildren("action"); + for (unsigned int i = 0; i < actions.size(); ++i) + ud->addPickCallback(new PickCallback(actions[i], getModelRoot())); + // Look for the VNC sessions that want raw mouse input + actions = getConfig()->getChildren("vncaction"); + for (unsigned int i = 0; i < actions.size(); ++i) + ud->addPickCallback(new VncCallback(actions[i], getModelRoot(), + &parent)); + + // prepare a state set that paints the edges of this object yellow + // The material and texture attributes are set with + // OVERRIDE|PROTECTED in case there is a material animation on a + // higher node in the scene graph, which would have its material + // attribute set with OVERRIDE. + osg::StateSet* stateSet = highlightGroup->getOrCreateStateSet(); + osg::Texture2D* white = StateAttributeFactory::instance()->getWhiteTexture(); + stateSet->setTextureAttributeAndModes(0, white, + (osg::StateAttribute::ON + | osg::StateAttribute::OVERRIDE + | osg::StateAttribute::PROTECTED)); + osg::PolygonOffset* polygonOffset = new osg::PolygonOffset; + polygonOffset->setFactor(-1); + polygonOffset->setUnits(-1); + stateSet->setAttribute(polygonOffset, osg::StateAttribute::OVERRIDE); + stateSet->setMode(GL_POLYGON_OFFSET_LINE, + osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); + osg::PolygonMode* polygonMode = new osg::PolygonMode; + polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK, + osg::PolygonMode::LINE); + stateSet->setAttribute(polygonMode, osg::StateAttribute::OVERRIDE); + osg::Material* material = new osg::Material; + material->setColorMode(osg::Material::OFF); + material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 1)); + // XXX Alpha < 1.0 in the diffuse material value is a signal to the + // default shader to take the alpha value from the material value + // and not the glColor. In many cases the pick animation geometry is + // transparent, so the outline would not be visible without this hack. + material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, .95)); + material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1)); + material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 0)); + stateSet->setAttribute( + material, osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED); + // The default shader has a colorMode uniform that mimics the + // behavior of Material color mode. + osg::Uniform* cmUniform = 0; + { + ScopedLock lock(colorModeUniformMutex); + if (!colorModeUniform.valid()) { + colorModeUniform = new osg::Uniform(osg::Uniform::INT, "colorMode"); + colorModeUniform->set(0); // MODE_OFF + colorModeUniform->setDataVariance(osg::Object::STATIC); + } + cmUniform = colorModeUniform.get(); + } + stateSet->addUniform(cmUniform, + osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON); + // Only add normal geometry if configured + if (getConfig()->getBoolValue("visible", true)) + parent.addChild(normalGroup.get()); + parent.addChild(highlightGroup); -bool SGShadowAnimation::get_condition_value(void) { - return _condition_value; + return commonGroup; } - -// end of animation.cxx