root/OpenSceneGraph/trunk/src/osgGA/SphericalManipulator.cpp @ 13041

Revision 13041, 12.9 kB (checked in by robert, 2 years ago)

Ran script to remove trailing spaces and tabs

  • Property svn:eol-style set to native
Line 
1
2#include <osgGA/SphericalManipulator>
3#include <osg/Quat>
4#include <osg/Notify>
5#include <osg/BoundsChecking>
6
7using namespace osg;
8using namespace osgGA;
9
10//--------------------------------------------------------------------------------------------------
11SphericalManipulator::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//--------------------------------------------------------------------------------------------------
28SphericalManipulator::~SphericalManipulator()
29{
30}
31//--------------------------------------------------------------------------------------------------
32void 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//--------------------------------------------------------------------------------------------------
43const osg::Node* SphericalManipulator::getNode() const
44{
45    return _node.get();
46}
47//--------------------------------------------------------------------------------------------------
48osg::Node* SphericalManipulator::getNode()
49{
50    return _node.get();
51}
52//--------------------------------------------------------------------------------------------------
53void 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//--------------------------------------------------------------------------------------------------
64bool SphericalManipulator::setDistance(double distance)
65{
66    if(distance <= 0)
67        return false;
68
69    _distance=distance;
70
71    return true;
72}
73
74//--------------------------------------------------------------------------------------------------
75void 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//--------------------------------------------------------------------------------------------------
88void SphericalManipulator::home(const GUIEventAdapter& ea ,GUIActionAdapter& us)
89{
90    home(ea.getTime());
91    us.requestRedraw();
92    us.requestContinuousUpdate(false);
93}
94//--------------------------------------------------------------------------------------------------
95void SphericalManipulator::init(const GUIEventAdapter& ,GUIActionAdapter& )
96{
97    flushMouseEventStack();
98}
99//--------------------------------------------------------------------------------------------------
100void 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//--------------------------------------------------------------------------------------------------
107void SphericalManipulator::zoomOn(const osg::BoundingSphere& bound)
108{
109    computeViewPosition(bound,_modelScale,_distance,_center);
110    _thrown = false;
111}
112//--------------------------------------------------------------------------------------------------
113bool 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//--------------------------------------------------------------------------------------------------
222bool 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//--------------------------------------------------------------------------------------------------
236void SphericalManipulator::flushMouseEventStack()
237{
238    _ga_t1 = NULL;
239    _ga_t0 = NULL;
240}
241//--------------------------------------------------------------------------------------------------
242void SphericalManipulator::addMouseEvent(const GUIEventAdapter& ea)
243{
244    _ga_t1 = _ga_t0;
245    _ga_t0 = &ea;
246}
247//--------------------------------------------------------------------------------------------------
248void 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//--------------------------------------------------------------------------------------------------
260osg::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//--------------------------------------------------------------------------------------------------
268osg::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//--------------------------------------------------------------------------------------------------
277bool 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//--------------------------------------------------------------------------------------------------
413void SphericalManipulator::computeHomePosition()
414{
415    if(getNode())
416        computeViewPosition(getNode()->getBound(),_modelScale,_homeDistance,_homeCenter);
417}
418//--------------------------------------------------------------------------------------------------
419void 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}
Note: See TracBrowser for help on using the browser.