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