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

Revision 13041, 15.1 kB (checked in by robert, 3 years ago)

Ran script to remove trailing spaces and tabs

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
2 *
3 * This library is open source and may be redistributed and/or modified under
4 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
5 * (at your option) any later version.  The full license is in LICENSE file
6 * included with this distribution, and on the openscenegraph.org website.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 * OpenSceneGraph Public License for more details.
12*/
13
14#if defined(_MSC_VER)
15    #pragma warning( disable : 4786 )
16#endif
17
18#include <stdlib.h>
19
20#include <osgGA/DriveManipulator>
21#include <osgUtil/LineSegmentIntersector>
22#include <osg/Notify>
23
24using namespace osg;
25using namespace osgGA;
26
27#define DRIVER_HEIGHT 15
28
29
30// #define ABOSULTE_PITCH 1
31// #define INCREMENTAL_PITCH 1
32#define KEYBOARD_PITCH 1
33
34static double getHeightOfDriver()
35{
36    double height = 1.5;
37    if (getenv("OSG_DRIVE_MANIPULATOR_HEIGHT"))
38    {
39        height = osg::asciiToDouble(getenv("OSG_DRIVE_MANIPULATOR_HEIGHT"));
40    }
41    OSG_INFO<<"DriveManipulator::_height set to =="<<height<<std::endl;
42    return height;
43}
44
45DriveManipulator::DriveManipulator()
46{
47    _modelScale = 1.0;
48    _velocity = 0.0;
49    _height = getHeightOfDriver();
50    _buffer = _height*2.5;
51    _pitch = 0.0;
52    //_speedMode = USE_MOUSE_Y_FOR_SPEED;
53    _speedMode = USE_MOUSE_BUTTONS_FOR_SPEED;
54
55    _pitchUpKeyPressed = false;
56    _pitchDownKeyPressed = false;
57
58}
59
60
61DriveManipulator::~DriveManipulator()
62{
63}
64
65
66void DriveManipulator::setNode(osg::Node* node)
67{
68    _node = node;
69    if (_node.get())
70    {
71        const osg::BoundingSphere& boundingSphere=_node->getBound();
72
73        _modelScale = boundingSphere._radius;
74        //_height = sqrtf(_modelScale)*0.03;
75        //_buffer = sqrtf(_modelScale)*0.05;
76
77        _height = getHeightOfDriver();
78        _buffer = _height*2.5;
79    }
80    if (getAutoComputeHomePosition()) computeHomePosition();
81}
82
83
84const osg::Node* DriveManipulator::getNode() const
85{
86    return _node.get();
87}
88
89
90osg::Node* DriveManipulator::getNode()
91{
92    return _node.get();
93}
94
95bool DriveManipulator::intersect(const osg::Vec3d& start, const osg::Vec3d& end, osg::Vec3d& intersection, osg::Vec3d& normal) const
96{
97    osg::ref_ptr<osgUtil::LineSegmentIntersector> lsi = new osgUtil::LineSegmentIntersector(start,end);
98
99    osgUtil::IntersectionVisitor iv(lsi.get());
100    iv.setTraversalMask(_intersectTraversalMask);
101
102    _node->accept(iv);
103
104    if (lsi->containsIntersections())
105    {
106        intersection = lsi->getIntersections().begin()->getWorldIntersectPoint();
107        normal = lsi->getIntersections().begin()->getWorldIntersectNormal();
108        return true;
109    }
110    return false;
111}
112
113
114void DriveManipulator::computeHomePosition()
115{
116    if(_node.get())
117    {
118        const osg::BoundingSphere& boundingSphere=_node->getBound();
119
120        osg::Vec3d ep = boundingSphere._center;
121        osg::Vec3d bp = ep;
122
123        osg::CoordinateFrame cf=getCoordinateFrame(ep);
124
125        ep -= getUpVector(cf)* _modelScale*0.0001;
126        bp -= getUpVector(cf)* _modelScale;
127
128        // check to see if any obstruction in front.
129        bool positionSet = false;
130
131        osg::Vec3d ip, np;
132        if (intersect(ep, bp, ip, np))
133        {
134            osg::Vec3d uv;
135            if (np * getUpVector(cf)>0.0) uv = np;
136            else uv = -np;
137
138            ep = ip;
139            ep += getUpVector(cf)*_height;
140            osg::Vec3d lv = uv^osg::Vec3d(1.0,0.0,0.0);
141
142            setHomePosition(ep,ep+lv,uv);
143
144            positionSet = true;
145
146        }
147
148        if (!positionSet)
149        {
150            bp = ep;
151            bp += getUpVector(cf)*_modelScale;
152
153
154            if (intersect(ep, bp, ip, np))
155            {
156
157                osg::Vec3d uv;
158                if (np*getUpVector(cf)>0.0) uv = np;
159                else uv = -np;
160
161                ep = ip;
162                ep += getUpVector(cf)*_height;
163                osg::Vec3d lv = uv^osg::Vec3d(1.0,0.0,0.0);
164                setHomePosition(ep,ep+lv,uv);
165
166                positionSet = true;
167
168            }
169        }
170
171        if (!positionSet)
172        {
173            setHomePosition(
174                boundingSphere._center+osg::Vec3d( 0.0,-2.0 * boundingSphere._radius,0.0),
175                boundingSphere._center+osg::Vec3d( 0.0,-2.0 * boundingSphere._radius,0.0)+osg::Vec3d(0.0,1.0,0.0),
176                osg::Vec3d(0.0,0.0,1.0));
177        }
178
179    }
180}
181
182void DriveManipulator::home(const GUIEventAdapter& ea,GUIActionAdapter& us)
183{
184    if (getAutoComputeHomePosition()) computeHomePosition();
185
186    computePosition(_homeEye, _homeCenter, _homeUp);
187
188    _velocity = 0.0;
189
190    _pitch = 0.0;
191
192    us.requestRedraw();
193    us.requestContinuousUpdate(false);
194
195    us.requestWarpPointer((ea.getXmin()+ea.getXmax())/2.0f,(ea.getYmin()+ea.getYmax())/2.0f);
196
197    flushMouseEventStack();
198}
199
200void DriveManipulator::init(const GUIEventAdapter& ea,GUIActionAdapter& us)
201{
202    flushMouseEventStack();
203
204    us.requestContinuousUpdate(false);
205
206    _velocity = 0.0;
207
208    osg::Vec3d ep = _eye;
209
210    osg::CoordinateFrame cf=getCoordinateFrame(ep);
211
212    Matrixd rotation_matrix;
213    rotation_matrix.makeRotate(_rotation);
214    osg::Vec3d sv = osg::Vec3d(1.0,0.0,0.0) * rotation_matrix;
215    osg::Vec3d bp = ep;
216    bp -= getUpVector(cf)*_modelScale;
217
218    bool positionSet = false;
219    osg::Vec3d ip, np;
220    if (intersect(ep, bp, ip, np))
221    {
222
223        osg::Vec3d uv;
224        if (np*getUpVector(cf)>0.0) uv = np;
225        else uv = -np;
226
227        ep = ip+uv*_height;
228        osg::Vec3d lv = uv^sv;
229
230        computePosition(ep,ep+lv,uv);
231
232        positionSet = true;
233    }
234
235    if (!positionSet)
236    {
237        bp = ep;
238        bp += getUpVector(cf)*_modelScale;
239
240        if (intersect(ep, bp, ip, np))
241        {
242
243            osg::Vec3d uv;
244            if (np*getUpVector(cf)>0.0f) uv = np;
245            else uv = -np;
246
247            ep = ip+uv*_height;
248            osg::Vec3d lv = uv^sv;
249
250            computePosition(ep,ep+lv,uv);
251
252            positionSet = true;
253        }
254    }
255
256    if (ea.getEventType()!=GUIEventAdapter::RESIZE)
257    {
258        us.requestWarpPointer((ea.getXmin()+ea.getXmax())/2.0f,(ea.getYmin()+ea.getYmax())/2.0f);
259    }
260}
261
262
263bool DriveManipulator::handle(const GUIEventAdapter& ea,GUIActionAdapter& us)
264{
265    switch(ea.getEventType())
266    {
267        case(GUIEventAdapter::FRAME):
268            addMouseEvent(ea);
269            if (calcMovement()) us.requestRedraw();
270            return false;
271
272        case(GUIEventAdapter::RESIZE):
273            init(ea,us);
274            us.requestRedraw();
275            return true;
276        default:
277            break;
278    }
279
280    if (ea.getHandled()) return false;
281
282    switch(ea.getEventType())
283    {
284        case(GUIEventAdapter::PUSH):
285        {
286
287            addMouseEvent(ea);
288            us.requestContinuousUpdate(true);
289            if (calcMovement()) us.requestRedraw();
290            return true;
291        }
292
293        case(GUIEventAdapter::RELEASE):
294        {
295
296            addMouseEvent(ea);
297            us.requestContinuousUpdate(true);
298            if (calcMovement()) us.requestRedraw();
299            return true;
300        }
301
302        case(GUIEventAdapter::DRAG):
303        {
304
305            addMouseEvent(ea);
306            us.requestContinuousUpdate(true);
307            if (calcMovement()) us.requestRedraw();
308            return true;
309        }
310
311        case(GUIEventAdapter::MOVE):
312        {
313
314            addMouseEvent(ea);
315            us.requestContinuousUpdate(true);
316            if (calcMovement()) us.requestRedraw();
317            return true;
318        }
319
320        case(GUIEventAdapter::KEYDOWN):
321        {
322            if (ea.getKey()==GUIEventAdapter::KEY_Space)
323            {
324                flushMouseEventStack();
325                home(ea,us);
326                return true;
327            }
328            else if (ea.getKey()=='q')
329            {
330                _speedMode = USE_MOUSE_Y_FOR_SPEED;
331                return true;
332            }
333            else if (ea.getKey()=='a')
334            {
335                _speedMode = USE_MOUSE_BUTTONS_FOR_SPEED;
336                return true;
337            }
338#ifdef KEYBOARD_PITCH
339            else if (ea.getKey()==osgGA::GUIEventAdapter::KEY_Up ||
340                     ea.getKey()==osgGA::GUIEventAdapter::KEY_KP_Up ||
341                     ea.getKey()=='9')
342            {
343                _pitchUpKeyPressed = true;
344                return true;
345            }
346            else if (ea.getKey()==osgGA::GUIEventAdapter::KEY_Down ||
347                     ea.getKey()==osgGA::GUIEventAdapter::KEY_KP_Down ||
348                     ea.getKey()=='6')
349            {
350                _pitchDownKeyPressed = true;
351                return true;
352            }
353#endif
354            return false;
355        }
356
357        case(GUIEventAdapter::KEYUP):
358        {
359#ifdef KEYBOARD_PITCH
360            if (ea.getKey()==osgGA::GUIEventAdapter::KEY_Up ||
361                     ea.getKey()==osgGA::GUIEventAdapter::KEY_KP_Up ||
362                     ea.getKey()=='9')
363            {
364                _pitchUpKeyPressed = false;
365                return true;
366            }
367            else if (ea.getKey()==osgGA::GUIEventAdapter::KEY_Down ||
368                     ea.getKey()==osgGA::GUIEventAdapter::KEY_KP_Down ||
369                     ea.getKey()=='6')
370            {
371                _pitchDownKeyPressed = false;
372                return true;
373            }
374#endif
375            return false;
376        }
377
378        default:
379            return false;
380    }
381}
382
383void DriveManipulator::getUsage(osg::ApplicationUsage& usage) const
384{
385    usage.addKeyboardMouseBinding("Drive: Space","Reset the viewing position to home");
386    usage.addKeyboardMouseBinding("Drive: q","Use mouse y for controlling speed");
387    usage.addKeyboardMouseBinding("Drive: a","Use mouse middle,right mouse buttons for speed");
388    usage.addKeyboardMouseBinding("Drive: Down","Cursor down key to look downwards");
389    usage.addKeyboardMouseBinding("Drive: Up","Cursor up key to look upwards");
390}
391
392
393void DriveManipulator::flushMouseEventStack()
394{
395    _ga_t1 = NULL;
396    _ga_t0 = NULL;
397}
398
399
400void DriveManipulator::addMouseEvent(const GUIEventAdapter& ea)
401{
402    _ga_t1 = _ga_t0;
403    _ga_t0 = &ea;
404}
405
406void DriveManipulator::setByMatrix(const osg::Matrixd& matrix)
407{
408    _eye = matrix.getTrans();
409    _rotation = matrix.getRotate();
410}
411
412osg::Matrixd DriveManipulator::getMatrix() const
413{
414    return osg::Matrixd::rotate(_pitch,1.0,0.0,0.0)*osg::Matrixd::rotate(_rotation)*osg::Matrixd::translate(_eye);
415}
416
417osg::Matrixd DriveManipulator::getInverseMatrix() const
418{
419    return osg::Matrixd::translate(-_eye)*osg::Matrixd::rotate(_rotation.inverse())*osg::Matrixd::rotate(-_pitch,1.0,0.0,0.0);
420}
421
422void DriveManipulator::computePosition(const osg::Vec3d& eye,const osg::Vec3d& center,const osg::Vec3d& up)
423{
424    osg::Vec3d lv = center-eye;
425
426    osg::Vec3d f(lv);
427    f.normalize();
428    osg::Vec3d s(f^up);
429    s.normalize();
430    osg::Vec3d u(s^f);
431    u.normalize();
432
433    osg::Matrixd rotation_matrix(s[0],     u[0],     -f[0],     0.0,
434                                 s[1],     u[1],     -f[1],     0.0,
435                                 s[2],     u[2],     -f[2],     0.0,
436                                 0.0,       0.0,       0.0,     1.0);
437
438    _eye = eye;
439    _rotation = rotation_matrix.getRotate().inverse();
440}
441
442
443bool DriveManipulator::calcMovement()
444{
445    // return if less then two events have been added.
446    if (_ga_t0.get()==NULL || _ga_t1.get()==NULL) return false;
447
448    double dt = _ga_t0->getTime()-_ga_t1->getTime();
449
450    if (dt<0.0f)
451    {
452        OSG_INFO << "warning dt = "<<dt<< std::endl;
453        dt = 0.0;
454    }
455
456    double accelerationFactor = _height*10.0;
457
458    switch(_speedMode)
459    {
460        case(USE_MOUSE_Y_FOR_SPEED):
461        {
462            double dy = _ga_t0->getYnormalized();
463            _velocity = _height*dy;
464            break;
465        }
466        case(USE_MOUSE_BUTTONS_FOR_SPEED):
467        {
468            unsigned int buttonMask = _ga_t1->getButtonMask();
469            if (buttonMask==GUIEventAdapter::LEFT_MOUSE_BUTTON)
470            {
471                // pan model.
472
473                _velocity += dt*accelerationFactor;
474
475            }
476            else if (buttonMask==GUIEventAdapter::MIDDLE_MOUSE_BUTTON ||
477                buttonMask==(GUIEventAdapter::LEFT_MOUSE_BUTTON|GUIEventAdapter::RIGHT_MOUSE_BUTTON))
478            {
479
480                _velocity = 0.0;
481
482            }
483            else if (buttonMask==GUIEventAdapter::RIGHT_MOUSE_BUTTON)
484            {
485
486                _velocity -= dt*accelerationFactor;
487
488            }
489            break;
490        }
491    }
492
493    osg::CoordinateFrame cf=getCoordinateFrame(_eye);
494
495    osg::Matrixd rotation_matrix;
496    rotation_matrix.makeRotate(_rotation);
497
498    osg::Vec3d up = osg::Vec3d(0.0,1.0,0.0) * rotation_matrix;
499    osg::Vec3d lv = osg::Vec3d(0.0,0.0,-1.0) * rotation_matrix;
500    osg::Vec3d sv = osg::Vec3d(1.0,0.0,0.0) * rotation_matrix;
501
502    // rotate the camera.
503    double dx = _ga_t0->getXnormalized();
504
505    double yaw = -inDegrees(dx*50.0*dt);
506
507
508#ifdef KEYBOARD_PITCH
509    double pitch_delta = 0.5;
510    if (_pitchUpKeyPressed) _pitch += pitch_delta*dt;
511    if (_pitchDownKeyPressed) _pitch -= pitch_delta*dt;
512#endif
513
514#if defined(ABOSULTE_PITCH)
515    // absolute pitch
516    double dy = _ga_t0->getYnormalized();
517    _pitch = -dy*0.5;
518#elif defined(INCREMENTAL_PITCH)
519    // incremental pitch
520    double dy = _ga_t0->getYnormalized();
521    _pitch += dy*dt;
522#endif
523
524    osg::Quat yaw_rotation;
525    yaw_rotation.makeRotate(yaw,up);
526
527    _rotation *= yaw_rotation;
528
529    rotation_matrix.makeRotate(_rotation);
530
531    sv = osg::Vec3d(1.0,0.0,0.0) * rotation_matrix;
532
533    // movement is big enough the move the eye point along the look vector.
534    if (fabs(_velocity*dt)>1e-8)
535    {
536        double distanceToMove = _velocity*dt;
537
538        double signedBuffer;
539        if (distanceToMove>=0.0) signedBuffer=_buffer;
540        else signedBuffer=-_buffer;
541
542        // check to see if any obstruction in front.
543        osg::Vec3d ip, np;
544        if (intersect(_eye,_eye+lv*(signedBuffer+distanceToMove), ip, np))
545        {
546            if (distanceToMove>=0.0)
547            {
548                distanceToMove = (ip-_eye).length()-_buffer;
549            }
550            else
551            {
552                distanceToMove = _buffer-(ip-_eye).length();
553            }
554
555            _velocity = 0.0;
556        }
557
558        // check to see if forward point is correct height above terrain.
559        osg::Vec3d fp = _eye + lv*distanceToMove;
560        osg::Vec3d lfp = fp - up*(_height*5.0);
561
562
563        if (intersect(fp, lfp, ip, np))
564        {
565            if (up*np>0.0) up = np;
566            else up = -np;
567
568            _eye = ip+up*_height;
569
570            lv = up^sv;
571
572            computePosition(_eye,_eye+lv,up);
573
574            return true;
575
576        }
577
578        // no hit on the terrain found therefore resort to a fall under
579        // under the influence of gravity.
580        osg::Vec3d dp = lfp;
581        dp -= getUpVector(cf)* (2.0*_modelScale);
582
583        if (intersect(lfp, dp, ip, np))
584        {
585
586            if (up*np>0.0) up = np;
587            else up = -np;
588
589            _eye = ip+up*_height;
590
591            lv = up^sv;
592
593            computePosition(_eye,_eye+lv,up);
594
595            return true;
596        }
597
598        // no collision with terrain has been found therefore track horizontally.
599
600        lv *= (_velocity*dt);
601
602        _eye += lv;
603    }
604
605    return true;
606}
Note: See TracBrowser for help on using the browser.