1 /* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2010 Robert Osfield
2  *
3  * This library is open source and may be redistributed and/or modified under
4  * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
5  * (at your option) any later version.  The full license is in LICENSE file
6  * included with this distribution, and on the openscenegraph.org website.
7  *
8  * This library is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * OpenSceneGraph Public License for more details.
12  *
13  * FirstPersonManipulator code Copyright (C) 2010 PCJohn (Jan Peciva)
14  * while some pieces of code were taken from OSG.
15  * Thanks to company Cadwork (www.cadwork.ch) and
16  * Brno University of Technology (www.fit.vutbr.cz) for open-sourcing this work.
17 */
18 
19 #include <osgGA/FirstPersonManipulator>
20 #include <cassert>
21 
22 using namespace osg;
23 using namespace osgGA;
24 
25 
26 
27 int FirstPersonManipulator::_accelerationFlagIndex = allocateRelativeFlag();
28 int FirstPersonManipulator::_maxVelocityFlagIndex = allocateRelativeFlag();
29 int FirstPersonManipulator::_wheelMovementFlagIndex = allocateRelativeFlag();
30 
31 
32 /// Constructor.
FirstPersonManipulator(int flags)33 FirstPersonManipulator::FirstPersonManipulator( int flags )
34    : inherited( flags ),
35      _velocity( 0. )
36 {
37    setAcceleration( 1.0, true );
38    setMaxVelocity( 0.25, true );
39    setWheelMovement( 0.05, true );
40    if( _flags & SET_CENTER_ON_WHEEL_FORWARD_MOVEMENT )
41       setAnimationTime( 0.2 );
42 }
43 
44 
45 /// Constructor.
FirstPersonManipulator(const FirstPersonManipulator & fpm,const CopyOp & copyOp)46 FirstPersonManipulator::FirstPersonManipulator( const FirstPersonManipulator& fpm, const CopyOp& copyOp )
47     : osg::Object(fpm, copyOp),
48      osg::Callback(fpm, copyOp),
49      inherited( fpm, copyOp ),
50      _eye( fpm._eye ),
51      _rotation( fpm._rotation ),
52      _velocity( fpm._velocity ),
53      _acceleration( fpm._acceleration ),
54      _maxVelocity( fpm._maxVelocity ),
55      _wheelMovement( fpm._wheelMovement )
56 {
57 }
58 
59 
60 /** Set the position of the manipulator using a 4x4 matrix.*/
setByMatrix(const Matrixd & matrix)61 void FirstPersonManipulator::setByMatrix( const Matrixd& matrix )
62 {
63    // set variables
64    _eye = matrix.getTrans();
65    _rotation = matrix.getRotate();
66 
67    // fix current rotation
68    if( getVerticalAxisFixed() )
69       fixVerticalAxis( _eye, _rotation, true );
70 }
71 
72 
73 /** Set the position of the manipulator using a 4x4 matrix.*/
setByInverseMatrix(const Matrixd & matrix)74 void FirstPersonManipulator::setByInverseMatrix( const Matrixd& matrix )
75 {
76    setByMatrix( Matrixd::inverse( matrix ) );
77 }
78 
79 
80 /** Get the position of the manipulator as 4x4 matrix.*/
getMatrix() const81 Matrixd FirstPersonManipulator::getMatrix() const
82 {
83    return Matrixd::rotate( _rotation ) * Matrixd::translate( _eye );
84 }
85 
86 
87 /** Get the position of the manipulator as a inverse matrix of the manipulator,
88     typically used as a model view matrix.*/
getInverseMatrix() const89 Matrixd FirstPersonManipulator::getInverseMatrix() const
90 {
91    return Matrixd::translate( -_eye ) * Matrixd::rotate( _rotation.inverse() );
92 }
93 
94 
95 // doc in parent
setTransformation(const osg::Vec3d & eye,const osg::Quat & rotation)96 void FirstPersonManipulator::setTransformation( const osg::Vec3d& eye, const osg::Quat& rotation )
97 {
98    // set variables
99    _eye = eye;
100    _rotation = rotation;
101 
102    // fix current rotation
103    if( getVerticalAxisFixed() )
104       fixVerticalAxis( _eye, _rotation, true );
105 }
106 
107 
108 // doc in parent
getTransformation(osg::Vec3d & eye,osg::Quat & rotation) const109 void FirstPersonManipulator::getTransformation( osg::Vec3d& eye, osg::Quat& rotation ) const
110 {
111    eye = _eye;
112    rotation = _rotation;
113 }
114 
115 
116 // doc in parent
setTransformation(const osg::Vec3d & eye,const osg::Vec3d & center,const osg::Vec3d & up)117 void FirstPersonManipulator::setTransformation( const osg::Vec3d& eye, const osg::Vec3d& center, const osg::Vec3d& up )
118 {
119    // set variables
120    osg::Matrixd m( osg::Matrixd::lookAt( eye, center, up ) );
121    _eye = eye;
122    _rotation = m.getRotate().inverse();
123 
124    // fix current rotation
125    if( getVerticalAxisFixed() )
126       fixVerticalAxis( _eye, _rotation, true );
127 }
128 
129 
130 // doc in parent
getTransformation(osg::Vec3d & eye,osg::Vec3d & center,osg::Vec3d & up) const131 void FirstPersonManipulator::getTransformation( osg::Vec3d& eye, osg::Vec3d& center, osg::Vec3d& up ) const
132 {
133    center = _eye + _rotation * osg::Vec3d( 0.,0.,-1. );
134    eye = _eye;
135    up = _rotation * osg::Vec3d( 0.,1.,0. );
136 }
137 
138 
139 /** Sets velocity.
140  *
141  *  There are no checks for maximum velocity applied.
142  */
setVelocity(const double & velocity)143 void FirstPersonManipulator::setVelocity( const double& velocity )
144 {
145    _velocity = velocity;
146 }
147 
148 
149 /** Sets acceleration.
150  *
151  *  If acceleration effect is unwanted, it can be set to DBL_MAX.
152  *  Then, there will be no acceleration and object will reach its
153  *  maximum velocity immediately.
154  */
setAcceleration(const double & acceleration,bool relativeToModelSize)155 void FirstPersonManipulator::setAcceleration( const double& acceleration, bool relativeToModelSize )
156 {
157    _acceleration = acceleration;
158    setRelativeFlag( _accelerationFlagIndex, relativeToModelSize );
159 }
160 
161 
162 /// Returns acceleration speed.
getAcceleration(bool * relativeToModelSize) const163 double FirstPersonManipulator::getAcceleration( bool *relativeToModelSize ) const
164 {
165    if( relativeToModelSize )
166       *relativeToModelSize = getRelativeFlag( _accelerationFlagIndex );
167 
168    return _acceleration;
169 }
170 
171 
172 /** Sets maximum velocity.
173  *
174  *  If acceleration is set to DBL_MAX, there is no speeding up.
175  *  Instead, maximum velocity is used for velocity at once without acceleration.
176  */
setMaxVelocity(const double & maxVelocity,bool relativeToModelSize)177 void FirstPersonManipulator::setMaxVelocity( const double& maxVelocity, bool relativeToModelSize )
178 {
179    _maxVelocity = maxVelocity;
180    setRelativeFlag( _maxVelocityFlagIndex, relativeToModelSize );
181 }
182 
183 
184 /// Returns maximum velocity.
getMaxVelocity(bool * relativeToModelSize) const185 double FirstPersonManipulator::getMaxVelocity( bool *relativeToModelSize ) const
186 {
187    if( relativeToModelSize )
188       *relativeToModelSize = getRelativeFlag( _maxVelocityFlagIndex );
189 
190    return _maxVelocity;
191 }
192 
193 
194 /// Sets movement size on single wheel step.
setWheelMovement(const double & wheelMovement,bool relativeToModelSize)195 void FirstPersonManipulator::setWheelMovement( const double& wheelMovement, bool relativeToModelSize )
196 {
197    _wheelMovement = wheelMovement;
198    setRelativeFlag( _wheelMovementFlagIndex, relativeToModelSize );
199 }
200 
201 
202 /// Returns movement size on single wheel step.
getWheelMovement(bool * relativeToModelSize) const203 double FirstPersonManipulator::getWheelMovement( bool *relativeToModelSize ) const
204 {
205    if( relativeToModelSize )
206       *relativeToModelSize = getRelativeFlag( _wheelMovementFlagIndex );
207 
208    return _wheelMovement;
209 }
210 
211 
home(double currentTime)212 void FirstPersonManipulator::home( double currentTime )
213 {
214    inherited::home( currentTime );
215    _velocity = 0.;
216 }
217 
218 
home(const GUIEventAdapter & ea,GUIActionAdapter & us)219 void FirstPersonManipulator::home( const GUIEventAdapter& ea, GUIActionAdapter& us )
220 {
221    inherited::home( ea, us );
222    _velocity = 0.;
223 }
224 
225 
init(const GUIEventAdapter & ea,GUIActionAdapter & us)226 void FirstPersonManipulator::init( const GUIEventAdapter& ea, GUIActionAdapter& us )
227 {
228    inherited::init( ea, us );
229 
230    // stop movement
231    _velocity = 0.;
232 }
233 
234 
235 // doc in parent
handleMouseWheel(const GUIEventAdapter & ea,GUIActionAdapter & us)236 bool FirstPersonManipulator::handleMouseWheel( const GUIEventAdapter& ea, GUIActionAdapter& us )
237 {
238     osgGA::GUIEventAdapter::ScrollingMotion sm = ea.getScrollingMotion();
239 
240     // handle centering
241     if( _flags & SET_CENTER_ON_WHEEL_FORWARD_MOVEMENT )
242     {
243 
244         if( ((sm == GUIEventAdapter::SCROLL_DOWN) && (_wheelMovement > 0.)) ||
245             ((sm == GUIEventAdapter::SCROLL_UP)   && (_wheelMovement < 0.)) )
246         {
247 
248             // stop thrown animation
249             _thrown = false;
250 
251             if( getAnimationTime() <= 0. )
252 
253                 // center by mouse intersection (no animation)
254                 setCenterByMousePointerIntersection( ea, us );
255 
256             else {
257 
258                 // start new animation only if there is no animation in progress
259                 if( !isAnimating() )
260                     startAnimationByMousePointerIntersection( ea, us );
261 
262             }
263         }
264     }
265 
266     FirstPersonAnimationData *ad = dynamic_cast< FirstPersonAnimationData*>( _animationData.get() );
267     if (!ad) return false;
268 
269     switch( sm )
270     {
271 
272         // mouse scroll up event
273         case GUIEventAdapter::SCROLL_UP:
274         {
275             // move forward
276             moveForward( isAnimating() ? ad->_targetRot : _rotation,
277                          -_wheelMovement * (getRelativeFlag( _wheelMovementFlagIndex ) ? _modelSize : 1. ));
278             us.requestRedraw();
279             us.requestContinuousUpdate( isAnimating() || _thrown );
280             return true;
281         }
282 
283         // mouse scroll down event
284         case GUIEventAdapter::SCROLL_DOWN:
285         {
286             // move backward
287             moveForward( _wheelMovement * (getRelativeFlag( _wheelMovementFlagIndex ) ? _modelSize : 1. ));
288             _thrown = false;
289             us.requestRedraw();
290             us.requestContinuousUpdate( isAnimating() || _thrown );
291             return true;
292         }
293 
294         // unhandled mouse scrolling motion
295         default:
296             return false;
297     }
298 }
299 
300 
301 // doc in parent
performMovementLeftMouseButton(const double,const double dx,const double dy)302 bool FirstPersonManipulator::performMovementLeftMouseButton( const double /*eventTimeDelta*/, const double dx, const double dy )
303 {
304    // world up vector
305    CoordinateFrame coordinateFrame = getCoordinateFrame( _eye );
306    Vec3d localUp = getUpVector( coordinateFrame );
307 
308    rotateYawPitch( _rotation, dx, dy, localUp );
309 
310    return true;
311 }
312 
313 
performMouseDeltaMovement(const float dx,const float dy)314 bool FirstPersonManipulator::performMouseDeltaMovement( const float dx, const float dy )
315 {
316    // rotate camera
317    if( getVerticalAxisFixed() ) {
318 
319       // world up vector
320       CoordinateFrame coordinateFrame = getCoordinateFrame( _eye );
321       Vec3d localUp = getUpVector( coordinateFrame );
322 
323       rotateYawPitch( _rotation, dx, dy, localUp );
324 
325    } else
326 
327       rotateYawPitch( _rotation, dx, dy );
328 
329    return true;
330 }
331 
332 
333 /// Move camera forward by distance parameter.
moveForward(const double distance)334 void FirstPersonManipulator::moveForward( const double distance )
335 {
336    moveForward( _rotation, distance );
337 }
338 
339 
340 /// Move camera forward by distance parameter.
moveForward(const Quat & rotation,const double distance)341 void FirstPersonManipulator::moveForward( const Quat& rotation, const double distance )
342 {
343    _eye += rotation * Vec3d( 0., 0., -distance );
344 }
345 
346 
347 /// Move camera right by distance parameter.
moveRight(const double distance)348 void FirstPersonManipulator::moveRight( const double distance )
349 {
350    _eye += _rotation * Vec3d( distance, 0., 0. );
351 }
352 
353 
354 /// Move camera up by distance parameter.
moveUp(const double distance)355 void FirstPersonManipulator::moveUp( const double distance )
356 {
357    _eye += _rotation * Vec3d( 0., distance, 0. );
358 }
359 
360 
applyAnimationStep(const double currentProgress,const double)361 void FirstPersonManipulator::applyAnimationStep( const double currentProgress, const double /*prevProgress*/ )
362 {
363    FirstPersonAnimationData *ad = dynamic_cast< FirstPersonAnimationData* >( _animationData.get() );
364    if (!ad) return;
365 
366    // compute new rotation
367    _rotation.slerp( currentProgress, ad->_startRot, ad->_targetRot );
368 
369    // fix vertical axis
370    if( getVerticalAxisFixed() )
371       fixVerticalAxis( _eye, _rotation, false );
372 }
373 
374 
375 // doc in parent
startAnimationByMousePointerIntersection(const osgGA::GUIEventAdapter & ea,osgGA::GUIActionAdapter & us)376 bool FirstPersonManipulator::startAnimationByMousePointerIntersection(
377       const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us )
378 {
379    // get current transformation
380    osg::Vec3d prevEye;
381    osg::Quat prevRot;
382    getTransformation( prevEye, prevRot );
383 
384    // center by mouse intersection
385    if( !setCenterByMousePointerIntersection( ea, us ) )
386       return false;
387 
388    FirstPersonAnimationData *ad = dynamic_cast< FirstPersonAnimationData*>( _animationData.get() );
389    if (!ad) return false;
390 
391    // setup animation data and restore original transformation
392    ad->start( prevRot, _rotation, ea.getTime() );
393    setTransformation( _eye, prevRot );
394 
395    return true;
396 }
397 
398 
start(const Quat & startRotation,const Quat & targetRotation,const double startTime)399 void FirstPersonManipulator::FirstPersonAnimationData::start( const Quat& startRotation, const Quat& targetRotation,
400                                                               const double startTime )
401 {
402    AnimationData::start( startTime );
403 
404    _startRot = startRotation;
405    _targetRot = targetRotation;
406 }
407