SGRotateTransform.hxx
SGScaleTransform.hxx
SGText.hxx
+ SGTrackToAnimation.hxx
SGTranslateTransform.hxx
animation.hxx
model.hxx
SGRotateTransform.cxx
SGScaleTransform.cxx
SGText.cxx
+ SGTrackToAnimation.cxx
SGTranslateTransform.cxx
animation.cxx
model.cxx
)
simgear_scene_component(model scene/model "${SOURCES}" "${HEADERS}")
+
+if(ENABLE_TESTS)
+
+ add_executable(test_animations animation_test.cxx)
+ target_link_libraries(test_animations ${TEST_LIBS} ${OPENSCENEGRAPH_LIBRARIES})
+ add_test(test_animations ${EXECUTABLE_OUTPUT_PATH}/test_animations)
+
+endif(ENABLE_TESTS)
\ No newline at end of file
_animationValue = value->simplify();
- readRotationCenterAndAxis(configNode, _center, _axis);
+ readRotationCenterAndAxis(_center, _axis);
}
osg::Group*
--- /dev/null
+// TrackTo animation
+//
+// Copyright (C) 2013 Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+
+#include "SGRotateTransform.hxx"
+#include "SGTrackToAnimation.hxx"
+
+#include <simgear/scene/util/OsgMath.hxx>
+#include <osg/Transform>
+#include <cassert>
+
+/**
+ *
+ */
+static osg::NodePath requireNodePath( osg::Node* node,
+ osg::Node* haltTraversalAtNode = 0 )
+{
+ const osg::NodePathList node_paths =
+ node->getParentalNodePaths(haltTraversalAtNode);
+ return node_paths.at(0);
+}
+
+/**
+ * Get a subpath of an osg::NodePath
+ *
+ * @param path Path to extract subpath from
+ * @param start Number of elements to skip from start of #path
+ *
+ * @return Subpath starting with node at position #start
+ */
+static osg::NodePath subPath( const osg::NodePath& path,
+ size_t start )
+{
+ if( start >= path.size() )
+ return osg::NodePath();
+
+ osg::NodePath np(path.size() - start);
+ for(size_t i = start; i < path.size(); ++i)
+ np[i - start] = path[i];
+
+ return np;
+}
+
+/**
+ * Visitor to find a group by its name.
+ */
+class FindGroupVisitor:
+ public osg::NodeVisitor
+{
+ public:
+
+ FindGroupVisitor(const std::string& name):
+ osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
+ _name(name),
+ _group(0)
+ {
+ if( name.empty() )
+ SG_LOG(SG_IO, SG_WARN, "FindGroupVisitor: empty name provided");
+ }
+
+ osg::Group* getGroup() const
+ {
+ return _group;
+ }
+
+ virtual void apply(osg::Group& group)
+ {
+ if( _name != group.getName() )
+ return traverse(group);
+
+ if( !_group )
+ _group = &group;
+ else
+ SG_LOG(SG_IO, SG_WARN, "FindGroupVisitor: name not unique");
+ }
+
+ protected:
+
+ std::string _name;
+ osg::Group *_group;
+};
+
+//------------------------------------------------------------------------------
+class SGTrackToAnimation::UpdateCallback:
+ public osg::NodeCallback
+{
+ public:
+ UpdateCallback( osg::Group* target,
+ const SGTrackToAnimation* anim ):
+ _target(target)
+ {
+ setName("SGTrackToAnimation::UpdateCallback");
+
+ _node_center = toOsg( anim->readVec3("center", "-m") );
+ _target_center = toOsg( anim->readVec3("target-center", "-m") );
+ _lock_axis = toOsg( anim->readVec3("lock-axis") );
+ _track_axis = toOsg( anim->readVec3("track-axis") );
+
+ if( _lock_axis.normalize() == 0.0 )
+ {
+ anim->log(SG_WARN, "invalid lock-axis");
+ _lock_axis.set(0, 1, 0);
+ }
+
+ for(;;)
+ {
+ float proj = _lock_axis * _track_axis;
+ if( proj != 0.0 )
+ {
+ anim->log(SG_WARN, "track-axis not perpendicular to lock-axis");
+
+ // Make tracking axis perpendicular to locked axis
+ _track_axis -= _lock_axis * proj;
+ }
+
+ if( _track_axis.normalize() == 0.0 )
+ {
+ anim->log(SG_WARN, "invalid track-axis");
+ if( std::fabs(_lock_axis.x()) < 0.1 )
+ _track_axis.set(1, 0, 0);
+ else
+ _track_axis.set(0, 1, 0);
+ }
+ else
+ break;
+ }
+
+ _up_axis = _lock_axis ^ _track_axis;
+ }
+
+ virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
+ {
+ SGRotateTransform* tf = static_cast<SGRotateTransform*>(node);
+
+ if( _target )
+ {
+ // Get path to animated node and calculated simplified paths to the
+ // nearest common parent node of both the animated node and the target
+ // node
+
+ // start at parent to not get false results by
+ // also including animation transformation
+ osg::NodePath node_path = requireNodePath(node->getParent(0)),
+ target_path = requireNodePath(_target);
+ size_t tp_size = target_path.size(),
+ np_size = node_path.size();
+ size_t common_parents = 0;
+
+ for(; common_parents < std::min(tp_size, np_size); ++common_parents)
+ {
+ if( target_path[common_parents] != node_path[common_parents] )
+ break;
+ }
+
+ _node_path = subPath(node_path, common_parents);
+ _target_path = subPath(target_path, common_parents);
+ _target = 0;
+
+ tf->setCenter( toSG(_node_center) );
+ tf->setAxis( toSG(_lock_axis) );
+ }
+
+ osg::Vec3 target_pos = ( osg::computeLocalToWorld(_target_path)
+ * osg::computeWorldToLocal(_node_path)
+ ).preMult(_target_center),
+ dir = target_pos - _node_center;
+
+ // Ensure direction is perpendicular to lock axis
+ float proj = _lock_axis * dir;
+ if( proj != 0.0 )
+ dir -= _lock_axis * proj;
+
+ float x = dir * _track_axis,
+ y = dir * _up_axis;
+ tf->setAngleRad( std::atan2(y, x) );
+
+ traverse(node, nv);
+ }
+ protected:
+ osg::Vec3 _node_center,
+ _target_center,
+ _lock_axis,
+ _track_axis,
+ _up_axis;
+ osg::Group *_target;
+ osg::NodePath _node_path,
+ _target_path;
+};
+
+//------------------------------------------------------------------------------
+SGTrackToAnimation::SGTrackToAnimation( osg::Node* node,
+ const SGPropertyNode* configNode,
+ SGPropertyNode* modelRoot ):
+ SGAnimation(configNode, modelRoot),
+ _target_group(0)
+{
+ std::string target = configNode->getStringValue("target-name");
+ std::cout << "track " << target << std::endl;
+ configNode->printOn(std::cout);
+
+ FindGroupVisitor target_finder(target);
+ node->accept(target_finder);
+
+ if( !(_target_group = target_finder.getGroup()) )
+ log(SG_ALERT, "target not found: '" + target + '\'');
+}
+
+//------------------------------------------------------------------------------
+osg::Group* SGTrackToAnimation::createAnimationGroup(osg::Group& parent)
+{
+ if( !_target_group )
+ return 0;
+
+ SGRotateTransform* transform = new SGRotateTransform;
+ transform->setName("locked-track animation");
+ transform->setUpdateCallback(new UpdateCallback(_target_group, this));
+ parent.addChild(transform);
+ return transform;
+}
+
+//------------------------------------------------------------------------------
+void SGTrackToAnimation::log(sgDebugPriority p, const std::string& msg) const
+{
+ SG_LOG
+ (
+ SG_IO,
+ p,
+ // TODO handle multiple object-names?
+ "SGTrackToAnimation(" << getConfig()->getStringValue("object-name") << "): "
+ << msg
+ );
+}
--- /dev/null
+// TrackTo animation
+//
+// http://wiki.blender.org/index.php/Doc:2.6/Manual/Constraints/Tracking/Locked_Track
+// TODO: http://wiki.blender.org/index.php/Doc:2.6/Manual/Constraints/Tracking/Track_To
+//
+// Copyright (C) 2013 Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+
+#ifndef SG_TRACK_TO_ANIMATION_HXX_
+#define SG_TRACK_TO_ANIMATION_HXX_
+
+#include <simgear/scene/model/animation.hxx>
+
+class SGTrackToAnimation:
+ public SGAnimation
+{
+ public:
+ SGTrackToAnimation( osg::Node* node,
+ const SGPropertyNode* configNode,
+ SGPropertyNode* modelRoot );
+
+ virtual osg::Group* createAnimationGroup(osg::Group& parent);
+
+ protected:
+ class UpdateCallback;
+
+ osg::Group *_target_group;
+
+ void log(sgDebugPriority p, const std::string& msg) const;
+};
+
+#endif /* SG_TRACK_TO_ANIMATION_HXX_ */
#include "SGScaleTransform.hxx"
#include "SGInteractionAnimation.hxx"
#include "SGPickAnimation.hxx"
+#include "SGTrackToAnimation.hxx"
#include "ConditionNode.hxx"
using OpenThreads::ScopedLock;
using namespace simgear;
-\f
////////////////////////////////////////////////////////////////////////
// Static utility functions.
////////////////////////////////////////////////////////////////////////
return 0;
}
-\f
////////////////////////////////////////////////////////////////////////
// Animation installer
////////////////////////////////////////////////////////////////////////
} else if (type == "timed") {
SGTimedAnimation animInst(configNode, modelRoot);
animInst.apply(node);
+ } else if (type == "track-to" || type == "locked-track") {
+ SGTrackToAnimation animInst(node, configNode, modelRoot);
+ animInst.apply(node);
} else if (type == "translate") {
SGTranslateAnimation animInst(configNode, modelRoot);
animInst.apply(node);
}
}
+//------------------------------------------------------------------------------
+SGVec3d SGAnimation::readVec3( const std::string& name,
+ const std::string& suffix,
+ const SGVec3d& def ) const
+{
+ SGVec3d vec;
+ vec[0] = _configNode->getDoubleValue(name + "/x" + suffix, def.x());
+ vec[1] = _configNode->getDoubleValue(name + "/y" + suffix, def.y());
+ vec[2] = _configNode->getDoubleValue(name + "/z" + suffix, def.z());
+ return vec;
+}
+
+//------------------------------------------------------------------------------
+// factored out to share with SGKnobAnimation
+void SGAnimation::readRotationCenterAndAxis( SGVec3d& center,
+ SGVec3d& axis ) const
+{
+ center = SGVec3d::zeros();
+ if( _configNode->hasValue("axis/x1-m") )
+ {
+ SGVec3d v1 = readVec3("axis", "1-m"), // axis/[xyz]1-m
+ v2 = readVec3("axis", "2-m"); // axis/[xyz]2-m
+ center = 0.5*(v1+v2);
+ axis = v2 - v1;
+ }
+ else
+ {
+ axis = readVec3("axis");
+ }
+ if( 8 * SGLimitsd::min() < norm(axis) )
+ axis = normalize(axis);
+
+ center = readVec3("center", "-m", center);
+}
+
void
SGAnimation::removeMode(osg::Node& node, osg::StateAttribute::GLMode mode)
{
}
-\f
+
////////////////////////////////////////////////////////////////////////
// Implementation of null animation
////////////////////////////////////////////////////////////////////////
return group;
}
-\f
+
////////////////////////////////////////////////////////////////////////
// Implementation of translate animation
////////////////////////////////////////////////////////////////////////
return transform;
}
-\f
+
////////////////////////////////////////////////////////////////////////
// Implementation of rotate/spin animation
////////////////////////////////////////////////////////////////////////
}
}
-// factored out to share with SGKnobAnimation
-void readRotationCenterAndAxis(const SGPropertyNode* configNode, SGVec3d& center, SGVec3d& axis)
-{
- 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]);
-}
-
SGVec3d readTranslateAxis(const SGPropertyNode* configNode)
{
SGVec3d axis;
else
_initialValue = 0;
- readRotationCenterAndAxis(configNode, _center, _axis);
+ readRotationCenterAndAxis(_center, _axis);
}
osg::Group*
}
}
-\f
+
////////////////////////////////////////////////////////////////////////
// Implementation of scale animation
////////////////////////////////////////////////////////////////////////
return transform;
}
-\f
+
// Don't create a new state state everytime we need GL_NORMALIZE!
namespace
&SGDistScaleAnimation::Transform::writeLocalData
);
}
-\f
+
////////////////////////////////////////////////////////////////////////
// Implementation of flash animation
////////////////////////////////////////////////////////////////////////
&SGFlashAnimation::Transform::writeLocalData
);
}
-\f
+
////////////////////////////////////////////////////////////////////////
// Implementation of billboard animation
////////////////////////////////////////////////////////////////////////
&SGBillboardAnimation::Transform::writeLocalData
);
}
-\f
+
////////////////////////////////////////////////////////////////////////
// Implementation of a range animation
////////////////////////////////////////////////////////////////////////
return group;
}
-\f
+
////////////////////////////////////////////////////////////////////////
// Implementation of a select animation
////////////////////////////////////////////////////////////////////////
}
-\f
+
////////////////////////////////////////////////////////////////////////
// Implementation of alpha test animation
////////////////////////////////////////////////////////////////////////
}
}
-\f
+
//////////////////////////////////////////////////////////////////////
// Blend animation installer
//////////////////////////////////////////////////////////////////////
node.accept(visitor);
}
-\f
+
//////////////////////////////////////////////////////////////////////
// Timed animation installer
//////////////////////////////////////////////////////////////////////
return sw;
}
-\f
+
////////////////////////////////////////////////////////////////////////
// dynamically switch on/off shadows
////////////////////////////////////////////////////////////////////////
return group;
}
-\f
+
////////////////////////////////////////////////////////////////////////
// Implementation of SGTexTransformAnimation
////////////////////////////////////////////////////////////////////////
read_value(const SGPropertyNode* configNode, SGPropertyNode* modelRoot,
const char* unit, double defMin, double defMax);
-void readRotationCenterAndAxis(const SGPropertyNode* configNode, SGVec3d& center, SGVec3d& axis);
SGVec3d readTranslateAxis(const SGPropertyNode* configNode);
-//////////////////////////////////////////////////////////////////////
-// Base class for animation installers
-//////////////////////////////////////////////////////////////////////
-
+/**
+ * Base class for animation installers
+ */
class SGAnimation : protected osg::NodeVisitor {
public:
SGAnimation(const SGPropertyNode* configNode, SGPropertyNode* modelRoot);
virtual void apply(osg::Group& group);
+ /**
+ * Read a 3d vector from the configuration property node.
+ *
+ * Reads values from @a name/[xyz]@a prefix and defaults to the according
+ * value of @a def for each value which is not set.
+ *
+ * @param name Name of the root node containing all coordinates
+ * @param suffix Suffix appended to each child node (x,y,z)
+ * @param def Vector containing default values
+ */
+ SGVec3d readVec3( const std::string& name,
+ const std::string& suffix = "",
+ const SGVec3d& def = SGVec3d() ) const;
+ void readRotationCenterAndAxis(SGVec3d& center, SGVec3d& axis) const;
+
void removeMode(osg::Node& node, osg::StateAttribute::GLMode mode);
void removeAttribute(osg::Node& node, osg::StateAttribute::Type type);
void removeTextureMode(osg::Node& node, unsigned unit,
bool _enableHOT;
};
-\f
+
//////////////////////////////////////////////////////////////////////
// Null animation installer
//////////////////////////////////////////////////////////////////////
virtual osg::Group* createAnimationGroup(osg::Group& parent);
};
-\f
+
//////////////////////////////////////////////////////////////////////
// Translate animation installer
//////////////////////////////////////////////////////////////////////
double _initialValue;
};
-\f
+
//////////////////////////////////////////////////////////////////////
// Rotate/Spin animation installer
//////////////////////////////////////////////////////////////////////
bool _isSpin;
};
-\f
+
//////////////////////////////////////////////////////////////////////
// Scale animation installer
//////////////////////////////////////////////////////////////////////
SGVec3d _center;
};
-\f
+
//////////////////////////////////////////////////////////////////////
// dist scale animation installer
//////////////////////////////////////////////////////////////////////
class Transform;
};
-\f
+
//////////////////////////////////////////////////////////////////////
// dist scale animation installer
//////////////////////////////////////////////////////////////////////
class Transform;
};
-\f
+
//////////////////////////////////////////////////////////////////////
// dist scale animation installer
//////////////////////////////////////////////////////////////////////
class Transform;
};
-\f
+
//////////////////////////////////////////////////////////////////////
// Range animation installer
//////////////////////////////////////////////////////////////////////
SGVec2d _initialValue;
};
-\f
+
//////////////////////////////////////////////////////////////////////
// Select animation installer
//////////////////////////////////////////////////////////////////////
virtual osg::Group* createAnimationGroup(osg::Group& parent);
};
-\f
+
//////////////////////////////////////////////////////////////////////
// Alpha test animation installer
//////////////////////////////////////////////////////////////////////
virtual void install(osg::Node& node);
};
-\f
+
//////////////////////////////////////////////////////////////////////
// Blend animation installer
//////////////////////////////////////////////////////////////////////
SGSharedPtr<SGExpressiond> _animationValue;
};
-\f
+
//////////////////////////////////////////////////////////////////////
// Timed animation installer
//////////////////////////////////////////////////////////////////////
class UpdateCallback;
};
-\f
+
//////////////////////////////////////////////////////////////////////
// Shadow animation installer
//////////////////////////////////////////////////////////////////////
class UpdateCallback;
};
-\f
+
//////////////////////////////////////////////////////////////////////
// TextureTransform animation
//////////////////////////////////////////////////////////////////////
UpdateCallback* updateCallback);
};
-\f
+
//////////////////////////////////////////////////////////////////////
// Shader animation
//////////////////////////////////////////////////////////////////////
class UpdateCallback;
osg::ref_ptr<osg::Texture2D> _effect_texture;
};
-\f
+
//////////////////////////////////////////////////////////////////////
// Light animation
//////////////////////////////////////////////////////////////////////
--- /dev/null
+#include "animation.hxx"
+
+#include <cstring>
+#include <iostream>
+
+#define COMPARE(a, b) \
+ if( (a) != (b) ) \
+ { \
+ std::cerr << "line " << __LINE__ << ": failed: "\
+ << #a << " != " << #b\
+ << " [" << (a) << " != " << (b) << "]" << std::endl; \
+ return 1; \
+ }
+
+#define VERIFY(a) \
+ if( !(a) ) \
+ { \
+ std::cerr << "failed: line " << __LINE__ << ": " << #a << std::endl; \
+ return 1; \
+ }
+
+struct AnimationTest:
+ public SGAnimation
+{
+ AnimationTest(const SGPropertyNode* n):
+ SGAnimation(n, 0)
+ {}
+
+ void readConfig()
+ {
+ readRotationCenterAndAxis(center, axis);
+ }
+
+ SGVec3d center,
+ axis;
+};
+
+int main(int argc, char* argv[])
+{
+ SGPropertyNode_ptr config = new SGPropertyNode;
+ AnimationTest anim(config);
+
+ SGVec3d v1(1, 2, 3),
+ v2(-1, 4, 0);
+ config->setDoubleValue("axis/x1-m", v1.x());
+ config->setDoubleValue("axis/y1-m", v1.y());
+ config->setDoubleValue("axis/z1-m", v1.z());
+ config->setDoubleValue("axis/x2-m", v2.x());
+ config->setDoubleValue("axis/y2-m", v2.y());
+ config->setDoubleValue("axis/z2-m", v2.z());
+ anim.readConfig();
+
+ COMPARE(anim.center, (v1 + v2) * 0.5)
+ COMPARE(anim.axis, normalize(v2 - v1))
+
+ config->removeChild("axis", 0, false);
+ config->setDoubleValue("center/x-m", v1.x());
+ config->setDoubleValue("center/y-m", v1.y());
+ config->setDoubleValue("center/z-m", v1.z());
+ config->setDoubleValue("axis/x", v2.x());
+ config->setDoubleValue("axis/y", v2.y());
+ config->setDoubleValue("axis/z", v2.z());
+ anim.readConfig();
+
+ COMPARE(anim.center, v1)
+ COMPARE(anim.axis, normalize(v2))
+
+ return 0;
+}