root/OpenSceneGraph/trunk/src/osgGA/TrackballManipulator.cpp @ 10002

Revision 10002, 11.6 kB (checked in by robert, 6 years ago)

From Cory Riddell, "I added an _allowThrow bool and get / set accessors to
TrackballManipulator?. The purpose of this is to disable throwing when
you release the mouse button while moving the mouse. The default
settings is true (ie, allow throw). The two source files are attached.
"

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1#include <osgGA/TrackballManipulator>
2#include <osg/Quat>
3#include <osg/Notify>
4#include <osg/BoundsChecking>
5
6using namespace osg;
7using namespace osgGA;
8
9TrackballManipulator::TrackballManipulator()
10{
11    _modelScale = 0.01f;
12    _minimumZoomScale = 0.05f;
13    _allowThrow = true;
14    _thrown = false;
15
16    _distance = 1.0f;
17    _trackballSize = 0.8f;
18    _zoomDelta = 0.1f;
19}
20
21
22TrackballManipulator::~TrackballManipulator()
23{
24}
25
26
27void TrackballManipulator::setNode(osg::Node* node)
28{
29    _node = node;
30    if (_node.get())
31    {
32        const osg::BoundingSphere& boundingSphere=_node->getBound();
33        _modelScale = boundingSphere._radius;
34    }
35    if (getAutoComputeHomePosition()) computeHomePosition();
36}
37
38
39const osg::Node* TrackballManipulator::getNode() const
40{
41    return _node.get();
42}
43
44
45osg::Node* TrackballManipulator::getNode()
46{
47    return _node.get();
48}
49
50
51void TrackballManipulator::home(double /*currentTime*/)
52{
53    if (getAutoComputeHomePosition()) computeHomePosition();
54    computePosition(_homeEye, _homeCenter, _homeUp);
55    _thrown = false;
56}
57
58void TrackballManipulator::home(const GUIEventAdapter& ea ,GUIActionAdapter& us)
59{
60    home(ea.getTime());
61    us.requestRedraw();
62    us.requestContinuousUpdate(false);
63}
64
65
66void TrackballManipulator::init(const GUIEventAdapter& ,GUIActionAdapter& )
67{
68    flushMouseEventStack();
69}
70
71
72void TrackballManipulator::getUsage(osg::ApplicationUsage& usage) const
73{
74    usage.addKeyboardMouseBinding("Trackball: Space","Reset the viewing position to home");
75    usage.addKeyboardMouseBinding("Trackball: +","When in stereo, increase the fusion distance");
76    usage.addKeyboardMouseBinding("Trackball: -","When in stereo, reduce the fusion distance");
77}
78
79bool TrackballManipulator::handle(const GUIEventAdapter& ea,GUIActionAdapter& us)
80{
81    switch(ea.getEventType())
82    {
83        case(GUIEventAdapter::FRAME):
84            if (_thrown && _allowThrow)
85            {
86                if (calcMovement()) us.requestRedraw();
87            }
88            return false;
89        default:
90            break;
91    }
92
93    if (ea.getHandled()) return false;
94
95    switch(ea.getEventType())
96    {
97        case(GUIEventAdapter::PUSH):
98        {
99            flushMouseEventStack();
100            addMouseEvent(ea);
101            if (calcMovement()) us.requestRedraw();
102            us.requestContinuousUpdate(false);
103            _thrown = false;
104            return true;
105        }
106
107        case(GUIEventAdapter::RELEASE):
108        {
109            if (ea.getButtonMask()==0)
110            {
111           
112                double timeSinceLastRecordEvent = _ga_t0.valid() ? (ea.getTime() - _ga_t0->getTime()) : DBL_MAX;
113                if (timeSinceLastRecordEvent>0.02) flushMouseEventStack();
114
115                if (isMouseMoving())
116                {
117                    if (calcMovement())
118                    {
119                        us.requestRedraw();
120                        us.requestContinuousUpdate(true);
121                        _thrown = _allowThrow;
122                    }
123                }
124                else
125                {
126                    flushMouseEventStack();
127                    addMouseEvent(ea);
128                    if (calcMovement()) us.requestRedraw();
129                    us.requestContinuousUpdate(false);
130                    _thrown = false;
131                }
132
133            }
134            else
135            {
136                flushMouseEventStack();
137                addMouseEvent(ea);
138                if (calcMovement()) us.requestRedraw();
139                us.requestContinuousUpdate(false);
140                _thrown = false;
141            }
142            return true;
143        }
144
145        case(GUIEventAdapter::DRAG):
146        case(GUIEventAdapter::SCROLL):
147        {
148            addMouseEvent(ea);
149            if (calcMovement()) us.requestRedraw();
150            us.requestContinuousUpdate(false);
151            _thrown = false;
152            return true;
153        }
154
155        case(GUIEventAdapter::MOVE):
156        {
157            return false;
158        }
159
160        case(GUIEventAdapter::KEYDOWN):
161            if (ea.getKey()== GUIEventAdapter::KEY_Space)
162            {
163                flushMouseEventStack();
164                _thrown = false;
165                home(ea,us);
166                return true;
167            }
168            return false;
169        case(GUIEventAdapter::FRAME):
170            if (_thrown)
171            {
172                if (calcMovement()) us.requestRedraw();
173            }
174            return false;
175        default:
176            return false;
177    }
178}
179
180
181bool TrackballManipulator::isMouseMoving()
182{
183    if (_ga_t0.get()==NULL || _ga_t1.get()==NULL) return false;
184
185    static const float velocity = 0.1f;
186
187    float dx = _ga_t0->getXnormalized()-_ga_t1->getXnormalized();
188    float dy = _ga_t0->getYnormalized()-_ga_t1->getYnormalized();
189    float len = sqrtf(dx*dx+dy*dy);
190    float dt = _ga_t0->getTime()-_ga_t1->getTime();
191
192    return (len>dt*velocity);
193}
194
195
196void TrackballManipulator::flushMouseEventStack()
197{
198    _ga_t1 = NULL;
199    _ga_t0 = NULL;
200}
201
202
203void TrackballManipulator::addMouseEvent(const GUIEventAdapter& ea)
204{
205    _ga_t1 = _ga_t0;
206    _ga_t0 = &ea;
207}
208
209void TrackballManipulator::setByMatrix(const osg::Matrixd& matrix)
210{
211    _center = osg::Vec3(0.0f,0.0f,-_distance)*matrix;
212    _rotation = matrix.getRotate();
213}
214
215osg::Matrixd TrackballManipulator::getMatrix() const
216{
217    return osg::Matrixd::translate(0.0,0.0,_distance)*osg::Matrixd::rotate(_rotation)*osg::Matrixd::translate(_center);
218}
219
220osg::Matrixd TrackballManipulator::getInverseMatrix() const
221{
222    return osg::Matrixd::translate(-_center)*osg::Matrixd::rotate(_rotation.inverse())*osg::Matrixd::translate(0.0,0.0,-_distance);
223}
224
225void TrackballManipulator::computePosition(const osg::Vec3& eye,const osg::Vec3& center,const osg::Vec3& up)
226{
227
228    osg::Vec3 lv(center-eye);
229
230    osg::Vec3 f(lv);
231    f.normalize();
232    osg::Vec3 s(f^up);
233    s.normalize();
234    osg::Vec3 u(s^f);
235    u.normalize();
236   
237    osg::Matrix rotation_matrix(s[0],     u[0],     -f[0],     0.0f,
238                                s[1],     u[1],     -f[1],     0.0f,
239                                s[2],     u[2],     -f[2],     0.0f,
240                                0.0f,     0.0f,     0.0f,      1.0f);
241                   
242    _center = center;
243    _distance = lv.length();
244    _rotation = rotation_matrix.getRotate().inverse();
245}
246
247
248bool TrackballManipulator::calcMovement()
249{
250    // mouse scroll is only a single event
251    if (_ga_t0.get()==NULL) return false;
252
253    float dx=0.0f;
254    float dy=0.0f;
255    unsigned int buttonMask=osgGA::GUIEventAdapter::NONE;
256
257    if (_ga_t0->getEventType()==GUIEventAdapter::SCROLL)
258    {
259        dy = _ga_t0->getScrollingMotion() == osgGA::GUIEventAdapter::SCROLL_UP ? _zoomDelta : -_zoomDelta;
260        buttonMask=GUIEventAdapter::SCROLL;
261    }
262    else 
263    {
264
265        if (_ga_t1.get()==NULL) return false;
266        dx = _ga_t0->getXnormalized()-_ga_t1->getXnormalized();
267        dy = _ga_t0->getYnormalized()-_ga_t1->getYnormalized();
268        float distance = sqrtf(dx*dx + dy*dy);
269       
270        // return if movement is too fast, indicating an error in event values or change in screen.
271        if (distance>0.5)
272        {
273            return false;
274        }
275       
276        // return if there is no movement.
277        if (distance==0.0f)
278        {
279            return false;
280        }
281
282        buttonMask = _ga_t1->getButtonMask();
283    }
284   
285    if (buttonMask==GUIEventAdapter::LEFT_MOUSE_BUTTON)
286    {
287
288        // rotate camera.
289
290        osg::Vec3 axis;
291        float angle;
292
293        float px0 = _ga_t0->getXnormalized();
294        float py0 = _ga_t0->getYnormalized();
295       
296        float px1 = _ga_t1->getXnormalized();
297        float py1 = _ga_t1->getYnormalized();
298       
299
300        trackball(axis,angle,px1,py1,px0,py0);
301
302        osg::Quat new_rotate;
303        new_rotate.makeRotate(angle,axis);
304       
305        _rotation = _rotation*new_rotate;
306
307        return true;
308
309    }
310    else if (buttonMask==GUIEventAdapter::MIDDLE_MOUSE_BUTTON ||
311        buttonMask==(GUIEventAdapter::LEFT_MOUSE_BUTTON|GUIEventAdapter::RIGHT_MOUSE_BUTTON))
312    {
313
314        // pan model.
315
316        float scale = -0.3f*_distance;
317
318        osg::Matrix rotation_matrix;
319        rotation_matrix.makeRotate(_rotation);
320
321        osg::Vec3 dv(dx*scale,dy*scale,0.0f);
322
323        _center += dv*rotation_matrix;
324       
325        return true;
326
327    }
328    else if ((buttonMask==GUIEventAdapter::RIGHT_MOUSE_BUTTON) || (buttonMask==GUIEventAdapter::SCROLL))
329    {
330
331        // zoom model.
332
333        float fd = _distance;
334        float scale = 1.0f+dy;
335        if (fd*scale>_modelScale*_minimumZoomScale)
336        {
337
338            _distance *= scale;
339
340        }
341        else
342        {
343
344            // notify(DEBUG_INFO) << "Pushing forward"<<std::endl;
345            // push the camera forward.
346            float scale = -fd;
347
348            osg::Matrix rotation_matrix(_rotation);
349
350            osg::Vec3 dv = (osg::Vec3(0.0f,0.0f,-1.0f)*rotation_matrix)*(dy*scale);
351
352            _center += dv;
353
354        }
355
356        return true;
357
358    }
359
360    return false;
361}
362
363
364/*
365 * This size should really be based on the distance from the center of
366 * rotation to the point on the object underneath the mouse.  That
367 * point would then track the mouse as closely as possible.  This is a
368 * simple example, though, so that is left as an Exercise for the
369 * Programmer.
370 */
371void TrackballManipulator::setTrackballSize(float size)
372{
373    _trackballSize = size;
374     osg::clampBetweenRange(_trackballSize,0.1f,1.0f,"TrackballManipulator::setTrackballSize(float)");
375}
376
377/*
378 * Ok, simulate a track-ball.  Project the points onto the virtual
379 * trackball, then figure out the axis of rotation, which is the cross
380 * product of P1 P2 and O P1 (O is the center of the ball, 0,0,0)
381 * Note:  This is a deformed trackball-- is a trackball in the center,
382 * but is deformed into a hyperbolic sheet of rotation away from the
383 * center.  This particular function was chosen after trying out
384 * several variations.
385 *
386 * It is assumed that the arguments to this routine are in the range
387 * (-1.0 ... 1.0)
388 */
389void TrackballManipulator::trackball(osg::Vec3& axis,float& angle, float p1x, float p1y, float p2x, float p2y)
390{
391    /*
392     * First, figure out z-coordinates for projection of P1 and P2 to
393     * deformed sphere
394     */
395
396    osg::Matrix rotation_matrix(_rotation);
397
398
399    osg::Vec3 uv = osg::Vec3(0.0f,1.0f,0.0f)*rotation_matrix;
400    osg::Vec3 sv = osg::Vec3(1.0f,0.0f,0.0f)*rotation_matrix;
401    osg::Vec3 lv = osg::Vec3(0.0f,0.0f,-1.0f)*rotation_matrix;
402
403    osg::Vec3 p1 = sv * p1x + uv * p1y - lv * tb_project_to_sphere(_trackballSize, p1x, p1y);
404    osg::Vec3 p2 = sv * p2x + uv * p2y - lv * tb_project_to_sphere(_trackballSize, p2x, p2y);
405
406    /*
407     *  Now, we want the cross product of P1 and P2
408     */
409
410// Robert,
411//
412// This was the quick 'n' dirty  fix to get the trackball doing the right
413// thing after fixing the Quat rotations to be right-handed.  You may want
414// to do something more elegant.
415//   axis = p1^p2;
416axis = p2^p1;
417    axis.normalize();
418
419    /*
420     *  Figure out how much to rotate around that axis.
421     */
422    float t = (p2 - p1).length() / (2.0 * _trackballSize);
423
424    /*
425     * Avoid problems with out-of-control values...
426     */
427    if (t > 1.0) t = 1.0;
428    if (t < -1.0) t = -1.0;
429    angle = inRadians(asin(t));
430
431}
432
433
434/*
435 * Project an x,y pair onto a sphere of radius r OR a hyperbolic sheet
436 * if we are away from the center of the sphere.
437 */
438float TrackballManipulator::tb_project_to_sphere(float r, float x, float y)
439{
440    float d, t, z;
441
442    d = sqrt(x*x + y*y);
443                                 /* Inside sphere */
444    if (d < r * 0.70710678118654752440)
445    {
446        z = sqrt(r*r - d*d);
447    }                            /* On hyperbola */
448    else
449    {
450        t = r / 1.41421356237309504880;
451        z = t*t / d;
452    }
453    return z;
454}
Note: See TracBrowser for help on using the browser.