1 
2 #include <osgGA/SphericalManipulator>
3 #include <osg/Quat>
4 #include <osg/Notify>
5 #include <osg/BoundsChecking>
6 
7 using namespace osg;
8 using namespace osgGA;
9 
10 //--------------------------------------------------------------------------------------------------
SphericalManipulator()11 SphericalManipulator::SphericalManipulator()
12 {
13     _modelScale = 0.01;
14     _minimumZoomScale = 0.1;
15     _thrown = false;
16     _allowThrow = true;
17 
18     _distance=1.0;
19     _homeDistance=1.0;
20 
21     _zoomDelta = 0.1;
22     _heading=0.0;
23     _elevation=osg::PI_2;
24 
25     _rotationMode = ELEVATION_HEADING;
26 }
27 //--------------------------------------------------------------------------------------------------
~SphericalManipulator()28 SphericalManipulator::~SphericalManipulator()
29 {
30 }
31 //--------------------------------------------------------------------------------------------------
setNode(osg::Node * node)32 void SphericalManipulator::setNode(osg::Node* node)
33 {
34     _node = node;
35     if (_node.get())
36     {
37         const osg::BoundingSphere& boundingSphere=_node->getBound();
38         _modelScale = boundingSphere._radius;
39     }
40     if (getAutoComputeHomePosition()) computeHomePosition();
41 }
42 //--------------------------------------------------------------------------------------------------
getNode() const43 const osg::Node* SphericalManipulator::getNode() const
44 {
45     return _node.get();
46 }
47 //--------------------------------------------------------------------------------------------------
getNode()48 osg::Node* SphericalManipulator::getNode()
49 {
50     return _node.get();
51 }
52 //--------------------------------------------------------------------------------------------------
setRotationMode(RotationMode mode)53 void SphericalManipulator::setRotationMode(RotationMode mode)
54 {
55      if(_rotationMode == mode)
56          return;
57 
58      _rotationMode=mode;
59 
60     if(_rotationMode == MAP)
61         _elevation=PI_2;
62 }
63 //--------------------------------------------------------------------------------------------------
setDistance(double distance)64 bool SphericalManipulator::setDistance(double distance)
65 {
66     if(distance <= 0)
67         return false;
68 
69     _distance=distance;
70 
71     return true;
72 }
73 
74 //--------------------------------------------------------------------------------------------------
home(double)75 void SphericalManipulator::home(double /*currentTime*/)
76 {
77     if(getAutoComputeHomePosition())
78         computeHomePosition();
79 
80     _heading=3*PI_2;
81     _elevation=0.0;
82     _center=_homeCenter;
83     _distance=_homeDistance;
84 
85     _thrown = false;
86 }
87 //--------------------------------------------------------------------------------------------------
home(const GUIEventAdapter & ea,GUIActionAdapter & us)88 void SphericalManipulator::home(const GUIEventAdapter& ea ,GUIActionAdapter& us)
89 {
90     home(ea.getTime());
91     us.requestRedraw();
92     us.requestContinuousUpdate(false);
93 }
94 //--------------------------------------------------------------------------------------------------
init(const GUIEventAdapter &,GUIActionAdapter &)95 void SphericalManipulator::init(const GUIEventAdapter& ,GUIActionAdapter& )
96 {
97     flushMouseEventStack();
98 }
99 //--------------------------------------------------------------------------------------------------
getUsage(osg::ApplicationUsage & usage) const100 void SphericalManipulator::getUsage(osg::ApplicationUsage& usage) const
101 {
102     usage.addKeyboardMouseBinding("Spherical: Space","Reset the viewing position to home");
103     usage.addKeyboardMouseBinding("Spherical: SHIFT","Rotates vertically only");
104     usage.addKeyboardMouseBinding("Spherical: ALT","Rotates horizontally only");
105 }
106 //--------------------------------------------------------------------------------------------------
zoomOn(const osg::BoundingSphere & bound)107 void SphericalManipulator::zoomOn(const osg::BoundingSphere& bound)
108 {
109     computeViewPosition(bound,_modelScale,_distance,_center);
110     _thrown = false;
111 }
112 //--------------------------------------------------------------------------------------------------
handle(const GUIEventAdapter & ea,GUIActionAdapter & us)113 bool SphericalManipulator::handle(const GUIEventAdapter& ea,GUIActionAdapter& us)
114 {
115     switch(ea.getEventType())
116     {
117     case(GUIEventAdapter::FRAME):
118         {
119             double current_frame_time = ea.getTime();
120 
121             _delta_frame_time = current_frame_time - _last_frame_time;
122             _last_frame_time = current_frame_time;
123 
124             if (_thrown)
125             {
126                 if (calcMovement()) us.requestRedraw();
127             }
128             return false;
129         }
130     default:
131         break;
132     }
133 
134     if (ea.getHandled()) return false;
135 
136     switch(ea.getEventType())
137     {
138     case(GUIEventAdapter::PUSH):
139         {
140             flushMouseEventStack();
141             addMouseEvent(ea);
142             us.requestContinuousUpdate(false);
143             _thrown = false;
144             return true;
145         }
146 
147     case(GUIEventAdapter::RELEASE):
148         {
149             if (ea.getButtonMask()==0)
150             {
151                 double timeSinceLastRecordEvent = _ga_t0.valid() ? (ea.getTime() - _ga_t0->getTime()) : DBL_MAX;
152                 if (timeSinceLastRecordEvent>0.02) flushMouseEventStack();
153 
154                 if (isMouseMoving())
155                 {
156                     if (calcMovement())
157                     {
158                         us.requestRedraw();
159                         us.requestContinuousUpdate(true);
160                         _thrown = _allowThrow;
161                     }
162                 }
163                 else
164                 {
165                     flushMouseEventStack();
166                     addMouseEvent(ea);
167                     if (calcMovement()) us.requestRedraw();
168                     us.requestContinuousUpdate(false);
169                     _thrown = false;
170                 }
171 
172             }
173             else
174             {
175                 flushMouseEventStack();
176                 addMouseEvent(ea);
177                 if (calcMovement()) us.requestRedraw();
178                 us.requestContinuousUpdate(false);
179                 _thrown = false;
180             }
181             return true;
182         }
183 
184     case(GUIEventAdapter::DRAG):
185     case(GUIEventAdapter::SCROLL):
186         {
187             addMouseEvent(ea);
188             if (calcMovement()) us.requestRedraw();
189             us.requestContinuousUpdate(false);
190             _thrown = false;
191             return true;
192         }
193 
194     case(GUIEventAdapter::MOVE):
195         {
196             return false;
197         }
198 
199     case(GUIEventAdapter::KEYDOWN):
200         if (ea.getKey()== GUIEventAdapter::KEY_Space)
201         {
202             flushMouseEventStack();
203             _thrown = false;
204             home(ea,us);
205             return true;
206         }
207         return false;
208 
209     case(GUIEventAdapter::FRAME):
210         if (_thrown)
211         {
212             if (calcMovement()) us.requestRedraw();
213         }
214         return false;
215 
216     default:
217         return false;
218     }
219     return false;
220 }
221 //--------------------------------------------------------------------------------------------------
isMouseMoving()222 bool SphericalManipulator::isMouseMoving()
223 {
224     if (_ga_t0.get()==NULL || _ga_t1.get()==NULL) return false;
225 
226     const float velocity = 0.1f;
227 
228     float dx = _ga_t0->getXnormalized()-_ga_t1->getXnormalized();
229     float dy = _ga_t0->getYnormalized()-_ga_t1->getYnormalized();
230     float len = sqrtf(dx*dx+dy*dy);
231     float dt = _ga_t0->getTime()-_ga_t1->getTime();
232 
233     return (len>dt*velocity);
234 }
235 //--------------------------------------------------------------------------------------------------
flushMouseEventStack()236 void SphericalManipulator::flushMouseEventStack()
237 {
238     _ga_t1 = NULL;
239     _ga_t0 = NULL;
240 }
241 //--------------------------------------------------------------------------------------------------
addMouseEvent(const GUIEventAdapter & ea)242 void SphericalManipulator::addMouseEvent(const GUIEventAdapter& ea)
243 {
244     _ga_t1 = _ga_t0;
245     _ga_t0 = &ea;
246 }
247 //--------------------------------------------------------------------------------------------------
setByMatrix(const osg::Matrixd & matrix)248 void SphericalManipulator::setByMatrix(const osg::Matrixd& matrix)
249 {
250     _center=osg::Vec3d(0,0,-_distance)*matrix;
251 
252     _heading=atan2(-matrix(0,0),matrix(0,1));
253 
254     if(_rotationMode != MAP)
255     {
256         _elevation=asin(matrix(2,2));
257     }
258 }
259 //--------------------------------------------------------------------------------------------------
getMatrix() const260 osg::Matrixd SphericalManipulator::getMatrix() const
261 {
262     return osg::Matrixd::translate(osg::Vec3d(0.0,0.0,_distance))*
263            osg::Matrixd::rotate(PI_2-_elevation,1.0,0.0,0.0)*
264            osg::Matrixd::rotate(PI_2+_heading,0.0,0.0,1.0)*
265            osg::Matrixd::translate(_center);
266 }
267 //--------------------------------------------------------------------------------------------------
getInverseMatrix() const268 osg::Matrixd SphericalManipulator::getInverseMatrix() const
269 {
270     return osg::Matrixd::translate(-_center)*
271            osg::Matrixd::rotate(PI_2+_heading,0.0,0.0,-1.0)*
272            osg::Matrixd::rotate(PI_2-_elevation,-1.0,0.0,0.0)*
273            osg::Matrixd::translate(osg::Vec3d(0.0,0.0,-_distance));
274 }
275 
276 //--------------------------------------------------------------------------------------------------
calcMovement()277 bool SphericalManipulator::calcMovement()
278 {
279     // mouse scroll is only a single event
280     if (_ga_t0.get()==NULL) return false;
281 
282     float dx=0.0f;
283     float dy=0.0f;
284     unsigned int buttonMask=osgGA::GUIEventAdapter::NONE;
285 
286     if (_ga_t0->getEventType()==GUIEventAdapter::SCROLL)
287     {
288         dy = _ga_t0->getScrollingMotion() == osgGA::GUIEventAdapter::SCROLL_UP ? _zoomDelta : -_zoomDelta;
289         buttonMask=GUIEventAdapter::SCROLL;
290     }
291     else
292     {
293 
294         if (_ga_t1.get()==NULL) return false;
295         dx = _ga_t0->getXnormalized()-_ga_t1->getXnormalized();
296         dy = _ga_t0->getYnormalized()-_ga_t1->getYnormalized();
297         float distance = sqrtf(dx*dx + dy*dy);
298 
299         // return if movement is too fast, indicating an error in event values or change in screen.
300         if (distance>0.5)
301         {
302             return false;
303         }
304 
305         // return if there is no movement.
306         if (distance==0.0f)
307         {
308             return false;
309         }
310 
311         buttonMask = _ga_t1->getButtonMask();
312     }
313 
314     double throwScale =  (_thrown && _ga_t0.valid() && _ga_t1.valid()) ?
315         _delta_frame_time / (_ga_t0->getTime() - _ga_t1->getTime()) : 1.0;
316 
317     if (buttonMask==GUIEventAdapter::LEFT_MOUSE_BUTTON)
318     {
319         // rotate camera.
320 
321         if(_rotationMode == MAP)
322         {
323             float pxc = (_ga_t0->getXmax()+_ga_t0->getXmin())/2;
324             float pyc = (_ga_t0->getYmax()+_ga_t0->getYmin())/2;
325 
326             float px0 = _ga_t0->getX();
327             float py0 = _ga_t0->getY();
328 
329             float px1 = _ga_t1->getX();
330             float py1 = _ga_t1->getY();
331 
332             float angle=atan2(py1-pyc,px1-pxc)-atan2(py0-pyc,px0-pxc);
333 
334             _heading+=throwScale*angle;
335             if(_heading < -PI)
336                 _heading+=2*PI;
337             else if(_heading > PI)
338                 _heading-=2*PI;
339         }
340         else
341         {
342             if((_rotationMode != ELEVATION) && ((_ga_t1->getModKeyMask() & GUIEventAdapter::MODKEY_SHIFT) == 0))
343             {
344                 _heading-=throwScale*dx*PI_2;
345 
346                 if(_heading < 0)
347                     _heading+=2*PI;
348                 else if(_heading > 2*PI)
349                     _heading-=2*PI;
350             }
351 
352             if((_rotationMode != HEADING) && ((_ga_t1->getModKeyMask() & GUIEventAdapter::MODKEY_ALT) == 0))
353             {
354                 _elevation-=throwScale*dy*osg::PI_4;
355 
356                 // Only allows vertical rotation of 180deg
357                 if(_elevation < -osg::PI_2)
358                     _elevation=-osg::PI_2;
359                 else if(_elevation > osg::PI_2)
360                     _elevation=osg::PI_2;
361             }
362         }
363 
364         return true;
365     }
366     else if (buttonMask==GUIEventAdapter::MIDDLE_MOUSE_BUTTON ||
367         buttonMask==(GUIEventAdapter::LEFT_MOUSE_BUTTON|GUIEventAdapter::RIGHT_MOUSE_BUTTON))
368     {
369         // pan model.
370 
371         float scale = -0.3f*_distance;
372 
373         osg::Matrix rotation_matrix;
374         rotation_matrix=osg::Matrixd::rotate(_elevation,-1,0,0)*osg::Matrixd::rotate(PI_2+_heading,0,0,1);
375 
376         osg::Vec3d dv(throwScale*dx*scale,0,throwScale*dy*scale);
377         _center += dv*rotation_matrix;
378 
379         return true;
380 
381     }
382     else if (buttonMask==GUIEventAdapter::RIGHT_MOUSE_BUTTON || _ga_t0->getEventType()==GUIEventAdapter::SCROLL)
383     {
384 
385         // zoom model.
386 
387         double fd = _distance;
388         double scale = 1.0+throwScale*dy;
389         if(fd*scale > _modelScale*_minimumZoomScale)
390         {
391             _distance *= scale;
392         }
393         else
394         {
395             OSG_DEBUG << "Pushing forward"<<std::endl;
396             // push the camera forward.
397             scale = -fd;
398 
399             osg::Matrix rotation_matrix=osg::Matrixd::rotate(_elevation,-1,0,0)*
400                 osg::Matrixd::rotate(PI_2+_heading,0,0,1);
401 
402             osg::Vec3d dv = (osg::Vec3d(0.0f,0.0f,-1.0f)*rotation_matrix)*(dy*scale);
403 
404             _center += dv;
405         }
406 
407         return true;
408     }
409 
410     return false;
411 }
412 //--------------------------------------------------------------------------------------------------
computeHomePosition()413 void SphericalManipulator::computeHomePosition()
414 {
415     if(getNode())
416         computeViewPosition(getNode()->getBound(),_modelScale,_homeDistance,_homeCenter);
417 }
418 //--------------------------------------------------------------------------------------------------
computeViewPosition(const osg::BoundingSphere & bound,double & scale,double & distance,osg::Vec3d & center)419 void SphericalManipulator::computeViewPosition(const osg::BoundingSphere& bound,
420     double& scale,double& distance,osg::Vec3d& center)
421 {
422     scale=bound._radius;
423     distance=3.5*bound._radius;
424     if(distance <= 0)
425         distance=1;
426     center=bound._center;
427 }
428