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