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