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