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

Revision 10415, 15.2 kB (checked in by robert, 5 years ago)

From Mathias Froehlich, "We are currently getting issues with locale settings and some osg plugins.
Therefore I have changed all the occurances of atof by asciiToFloat or
asciiToDouble.

I believe that it is safe to do so at least for all the plugins.
Included here are also asciiToFloat conversion of environment variables. One
might argue that these should be locale dependent. But IMO these should be
set and interpreted by osg independent of the current locale.
"

  • 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::notify(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::Vec3 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::Vec3 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::Vec3 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        notify(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.