3 // Copyright (C) 2013 Thomas Geymayer <tomgey@gmail.com>
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Library General Public
7 // License as published by the Free Software Foundation; either
8 // version 2 of the License, or (at your option) any later version.
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Library General Public License for more details.
15 // You should have received a copy of the GNU Library General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #include "SGRotateTransform.hxx"
20 #include "SGTrackToAnimation.hxx"
22 #include <simgear/scene/util/OsgMath.hxx>
23 #include <osg/Transform>
29 static osg::NodePath requireNodePath( osg::Node* node,
30 osg::Node* haltTraversalAtNode = 0 )
32 const osg::NodePathList node_paths =
33 node->getParentalNodePaths(haltTraversalAtNode);
34 return node_paths.at(0);
38 * Get a subpath of an osg::NodePath
40 * @param path Path to extract subpath from
41 * @param start Number of elements to skip from start of #path
43 * @return Subpath starting with node at position #start
45 static osg::NodePath subPath( const osg::NodePath& path,
48 if( start >= path.size() )
49 return osg::NodePath();
51 osg::NodePath np(path.size() - start);
52 for(size_t i = start; i < path.size(); ++i)
53 np[i - start] = path[i];
59 * Visitor to find a group by its name.
61 class FindGroupVisitor:
62 public osg::NodeVisitor
66 FindGroupVisitor(const std::string& name):
67 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
72 SG_LOG(SG_IO, SG_WARN, "FindGroupVisitor: empty name provided");
75 osg::Group* getGroup() const
80 virtual void apply(osg::Group& group)
82 if( _name != group.getName() )
83 return traverse(group);
88 SG_LOG(SG_IO, SG_WARN, "FindGroupVisitor: name not unique");
97 //------------------------------------------------------------------------------
98 class SGTrackToAnimation::UpdateCallback:
99 public osg::NodeCallback
102 UpdateCallback( osg::Group* target,
103 const SGTrackToAnimation* anim ):
106 setName("SGTrackToAnimation::UpdateCallback");
108 _node_center = toOsg( anim->readVec3("center", "-m") );
109 _target_center = toOsg( anim->readVec3("target-center", "-m") );
110 _lock_axis = toOsg( anim->readVec3("lock-axis") );
111 _track_axis = toOsg( anim->readVec3("track-axis") );
113 if( _lock_axis.normalize() == 0.0 )
115 anim->log(SG_WARN, "invalid lock-axis");
116 _lock_axis.set(0, 1, 0);
121 float proj = _lock_axis * _track_axis;
124 anim->log(SG_WARN, "track-axis not perpendicular to lock-axis");
126 // Make tracking axis perpendicular to locked axis
127 _track_axis -= _lock_axis * proj;
130 if( _track_axis.normalize() == 0.0 )
132 anim->log(SG_WARN, "invalid track-axis");
133 if( std::fabs(_lock_axis.x()) < 0.1 )
134 _track_axis.set(1, 0, 0);
136 _track_axis.set(0, 1, 0);
142 _up_axis = _lock_axis ^ _track_axis;
145 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
147 SGRotateTransform* tf = static_cast<SGRotateTransform*>(node);
151 // Get path to animated node and calculated simplified paths to the
152 // nearest common parent node of both the animated node and the target
155 // start at parent to not get false results by
156 // also including animation transformation
157 osg::NodePath node_path = requireNodePath(node->getParent(0)),
158 target_path = requireNodePath(_target);
159 size_t tp_size = target_path.size(),
160 np_size = node_path.size();
161 size_t common_parents = 0;
163 for(; common_parents < std::min(tp_size, np_size); ++common_parents)
165 if( target_path[common_parents] != node_path[common_parents] )
169 _node_path = subPath(node_path, common_parents);
170 _target_path = subPath(target_path, common_parents);
173 tf->setCenter( toSG(_node_center) );
174 tf->setAxis( toSG(_lock_axis) );
177 osg::Vec3 target_pos = ( osg::computeLocalToWorld(_target_path)
178 * osg::computeWorldToLocal(_node_path)
179 ).preMult(_target_center),
180 dir = target_pos - _node_center;
182 // Ensure direction is perpendicular to lock axis
183 float proj = _lock_axis * dir;
185 dir -= _lock_axis * proj;
187 float x = dir * _track_axis,
189 tf->setAngleRad( std::atan2(y, x) );
194 osg::Vec3 _node_center,
200 osg::NodePath _node_path,
204 //------------------------------------------------------------------------------
205 SGTrackToAnimation::SGTrackToAnimation( osg::Node* node,
206 const SGPropertyNode* configNode,
207 SGPropertyNode* modelRoot ):
208 SGAnimation(configNode, modelRoot),
211 std::string target = configNode->getStringValue("target-name");
212 FindGroupVisitor target_finder(target);
213 node->accept(target_finder);
215 if( !(_target_group = target_finder.getGroup()) )
216 log(SG_ALERT, "target not found: '" + target + '\'');
219 //------------------------------------------------------------------------------
220 osg::Group* SGTrackToAnimation::createAnimationGroup(osg::Group& parent)
225 SGRotateTransform* transform = new SGRotateTransform;
226 transform->setName("locked-track animation");
227 transform->setUpdateCallback(new UpdateCallback(_target_group, this));
228 parent.addChild(transform);
232 //------------------------------------------------------------------------------
233 void SGTrackToAnimation::log(sgDebugPriority p, const std::string& msg) const
239 // TODO handle multiple object-names?
240 "SGTrackToAnimation(" << getConfig()->getStringValue("object-name") << "): "