]> git.mxchange.org Git - simgear.git/blob - simgear/scene/model/SGTrackToAnimation.cxx
Fix animation tests for low precision floating point
[simgear.git] / simgear / scene / model / SGTrackToAnimation.cxx
1 // TrackTo animation
2 //
3 // Copyright (C) 2013  Thomas Geymayer <tomgey@gmail.com>
4 //
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.
9 //
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.
14 //
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
18
19 #include "SGRotateTransform.hxx"
20 #include "SGTrackToAnimation.hxx"
21
22 #include <simgear/scene/util/OsgMath.hxx>
23 #include <osg/Transform>
24 #include <cassert>
25
26 /**
27  *
28  */
29 static osg::NodePath requireNodePath( osg::Node* node,
30                                       osg::Node* haltTraversalAtNode = 0 )
31 {
32   const osg::NodePathList node_paths =
33     node->getParentalNodePaths(haltTraversalAtNode);
34   return node_paths.at(0);
35 }
36
37 /**
38  * Get a subpath of an osg::NodePath
39  *
40  * @param path  Path to extract subpath from
41  * @param start Number of elements to skip from start of #path
42  *
43  * @return Subpath starting with node at position #start
44  */
45 static osg::NodePath subPath( const osg::NodePath& path,
46                               size_t start )
47 {
48   if( start >= path.size() )
49     return osg::NodePath();
50
51   osg::NodePath np(path.size() - start);
52   for(size_t i = start; i < path.size(); ++i)
53     np[i - start] = path[i];
54
55   return np;
56 }
57
58 /**
59  * Visitor to find a group by its name.
60  */
61 class FindGroupVisitor:
62   public osg::NodeVisitor
63 {
64   public:
65
66     FindGroupVisitor(const std::string& name):
67         osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
68         _name(name),
69         _group(0)
70     {
71       if( name.empty() )
72         SG_LOG(SG_IO, SG_WARN, "FindGroupVisitor: empty name provided");
73     }
74
75     osg::Group* getGroup() const
76     {
77       return _group;
78     }
79
80     virtual void apply(osg::Group& group)
81     {
82       if( _name != group.getName() )
83         return traverse(group);
84
85       if( !_group )
86         _group = &group;
87       else
88         SG_LOG(SG_IO, SG_WARN, "FindGroupVisitor: name not unique");
89     }
90
91   protected:
92
93     std::string _name;
94     osg::Group *_group;
95 };
96
97 //------------------------------------------------------------------------------
98 class SGTrackToAnimation::UpdateCallback:
99   public osg::NodeCallback
100 {
101   public:
102     UpdateCallback( osg::Group* target,
103                     const SGTrackToAnimation* anim ):
104       _target(target)
105     {
106       setName("SGTrackToAnimation::UpdateCallback");
107
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") );
112
113       if( _lock_axis.normalize() == 0.0 )
114       {
115         anim->log(SG_WARN, "invalid lock-axis");
116         _lock_axis.set(0, 1, 0);
117       }
118
119       for(;;)
120       {
121         float proj = _lock_axis * _track_axis;
122         if( proj != 0.0 )
123         {
124           anim->log(SG_WARN, "track-axis not perpendicular to lock-axis");
125
126           // Make tracking axis perpendicular to locked axis
127           _track_axis -= _lock_axis * proj;
128         }
129
130         if( _track_axis.normalize() == 0.0 )
131         {
132           anim->log(SG_WARN, "invalid track-axis");
133           if( std::fabs(_lock_axis.x()) < 0.1 )
134             _track_axis.set(1, 0, 0);
135           else
136             _track_axis.set(0, 1, 0);
137         }
138         else
139           break;
140       }
141
142       _up_axis = _lock_axis ^ _track_axis;
143     }
144
145     virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
146     {
147       SGRotateTransform* tf = static_cast<SGRotateTransform*>(node);
148
149       if( _target )
150       {
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
153         // node
154
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;
162
163         for(; common_parents < std::min(tp_size, np_size); ++common_parents)
164         {
165           if( target_path[common_parents] != node_path[common_parents] )
166             break;
167         }
168
169         _node_path = subPath(node_path, common_parents);
170         _target_path = subPath(target_path, common_parents);
171         _target = 0;
172
173         tf->setCenter( toSG(_node_center) );
174         tf->setAxis( toSG(_lock_axis) );
175       }
176
177       osg::Vec3 target_pos = ( osg::computeLocalToWorld(_target_path)
178                              * osg::computeWorldToLocal(_node_path)
179                              ).preMult(_target_center),
180                        dir = target_pos - _node_center;
181
182       // Ensure direction is perpendicular to lock axis
183       float proj = _lock_axis * dir;
184       if( proj != 0.0 )
185         dir -= _lock_axis * proj;
186
187       float x = dir * _track_axis,
188             y = dir * _up_axis;
189       tf->setAngleRad( std::atan2(y, x) );
190
191       traverse(node, nv);
192     }
193   protected:
194     osg::Vec3       _node_center,
195                     _target_center,
196                     _lock_axis,
197                     _track_axis,
198                     _up_axis;
199     osg::Group     *_target;
200     osg::NodePath   _node_path,
201                     _target_path;
202 };
203
204 //------------------------------------------------------------------------------
205 SGTrackToAnimation::SGTrackToAnimation( osg::Node* node,
206                                         const SGPropertyNode* configNode,
207                                         SGPropertyNode* modelRoot ):
208   SGAnimation(configNode, modelRoot),
209   _target_group(0)
210 {
211   std::string target = configNode->getStringValue("target-name");
212   FindGroupVisitor target_finder(target);
213   node->accept(target_finder);
214
215   if( !(_target_group = target_finder.getGroup()) )
216     log(SG_ALERT, "target not found: '" + target + '\'');
217 }
218
219 //------------------------------------------------------------------------------
220 osg::Group* SGTrackToAnimation::createAnimationGroup(osg::Group& parent)
221 {
222   if( !_target_group )
223     return 0;
224
225   SGRotateTransform* transform = new SGRotateTransform;
226   transform->setName("locked-track animation");
227   transform->setUpdateCallback(new UpdateCallback(_target_group, this));
228   parent.addChild(transform);
229   return transform;
230 }
231
232 //------------------------------------------------------------------------------
233 void SGTrackToAnimation::log(sgDebugPriority p, const std::string& msg) const
234 {
235   SG_LOG
236   (
237     SG_IO,
238     p,
239     // TODO handle multiple object-names?
240     "SGTrackToAnimation(" << getConfig()->getStringValue("object-name") << "): "
241     << msg
242   );
243 }