[osg][osgEarth]EarthManipulator关于oe漫游器的handle部分解读

Posted 南水之源

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[osg][osgEarth]EarthManipulator关于oe漫游器的handle部分解读相关的知识,希望对你有一定的参考价值。

bool
EarthManipulator::handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
{
    bool handled = false;

    // first order of business: make sure the CSN is established.
    if ( !established() )
        return false;

    // make sure the camera projection is up to date:
    osg::View* view = aa.asView();
    updateProjection( view->getCamera() );

    double time_s_now = osg::Timer::instance()->time_s();

    if ( ea.getEventType() == osgGA::GUIEventAdapter::FRAME )
    {
        _time_s_last_frame = _time_s_now;
        _time_s_now = time_s_now;
        _delta_t = _time_s_now - _time_s_last_frame;

        if ( _node.valid() )
        {
            if ( _pendingViewpoint.isSet() )
            {
                setViewpoint( _pendingViewpoint.get(), _pendingViewpointDuration.as(Units::SECONDS) );
                _pendingViewpoint.unset();
                aa.requestRedraw();
            }

            else if ( isSettingViewpoint() && !isTethering() )
            {
                if ( _frameCount < 2 )
                    _setVPStartTime->set(_time_s_now, Units::SECONDS);

                setViewpointFrame( time_s_now );
            }

            if (_thrown)
            {
                double decayFactor = 1.0 - _settings->getThrowDecayRate();

                _throw_dx = osg::absolute(_throw_dx) > osg::absolute(_dx * 0.01) ? _throw_dx * decayFactor : 0.0;
                _throw_dy = osg::absolute(_throw_dy) > osg::absolute(_dy * 0.01) ? _throw_dy * decayFactor : 0.0;

                if (_throw_dx == 0.0 && _throw_dy == 0.0)
                    _thrown = false;
                else
                    handleMovementAction(_last_action._type, _throw_dx, _throw_dy, aa.asView());
            }

            aa.requestContinuousUpdate( isSettingViewpoint() || _thrown );

            if ( _continuous )
            {
                handleContinuousAction( _last_action, aa.asView() );
                aa.requestRedraw();
            }
            else
            {
                _continuous_dx = 0.0;
                _continuous_dy = 0.0;
            }

            if ( _task.valid() && _task->_type != TASK_NONE )
            {
                bool stillRunning = serviceTask();
                if ( stillRunning )
                {
                    aa.requestContinuousUpdate( true );
                }
                else
                {
                    // turn off the continuous, but we still need one last redraw
                    // to process the final state.
                    aa.requestContinuousUpdate( false );
                    aa.requestRedraw();
                }
            }
        }

        _frameCount++;

        return false;
    }


    // the camera manipulator runs last after any other event handlers. So bail out
    // if the incoming event has already been handled by another handler.
    if ( ea.getHandled() )
    {
        return false;
    }

    // form the current Action based on the event type:
    Action action = ACTION_NULL;

    // if tethering is active, check to see whether the incoming event
    // will break the tether.
    if ( isTethering() )
    {
        const ActionTypeVector& atv = _settings->getBreakTetherActions();
        if ( atv.size() > 0 )
        {
            const Action& action = _settings->getAction( ea.getEventType(), ea.getButtonMask(), ea.getModKeyMask() );
            if ( std::find(atv.begin(), atv.end(), action._type) != atv.end() )
            {
                clearViewpoint();
            }
        }
    }


    if ( ea.isMultiTouchEvent() )
    {
        // not a mouse event; clear the mouse queue.
        resetMouse( aa, false );

        // queue up a touch event set and figure out the current state:
        addTouchEvents(ea);
        TouchEvents te;
        if ( parseTouchEvents(te) )
        {
            for( TouchEvents::iterator i = te.begin(); i != te.end(); ++i )
            {
                action = _settings->getAction(i->_eventType, i->_mbmask, 0);

                if (action._type != ACTION_NULL)
                {
                    _last_event = i->_eventType;

                    // here we adjust for action scale, global sensitivy
                    double dx = i->_dx, dy = i->_dy;
                    dx *= _settings->getMouseSensitivity();
                    dy *= _settings->getMouseSensitivity();
                    applyOptionsToDeltas( action, dx, dy );

                    _dx = dx;
                    _dy = dy;

                    if (action._type == ACTION_GOTO)
                        handlePointAction(action, ea.getX(), ea.getY(), view);
                    else
                        handleMovementAction(action._type, dx, dy, view);

                    aa.requestRedraw();
                }
            }

            handled = true;
        }
        else
        {
            // The only multitouch event we want passed on if not handled is a release
            handled = ea.getEventType() != osgGA::GUIEventAdapter::RELEASE;

            // if a new push occurs we want to reset the dx/dy values to stop/prevent throwing
            if (ea.getEventType() == osgGA::GUIEventAdapter::PUSH)
                _dx = _dy = 0.0;
        }
    }

    if ( !handled )
    {
        // not a touch event; clear the touch queue.
        //_touchPointQueue.clear();

        switch( ea.getEventType() )
        {
            case osgGA::GUIEventAdapter::PUSH:
                resetMouse( aa );
                addMouseEvent( ea );
                _mouse_down_event = &ea;
                aa.requestRedraw();
                handled = true;
                break;

            case osgGA::GUIEventAdapter::RELEASE:
                if ( _continuous )
                {
                    // bail out of continuous mode if necessary:
                    _continuous = false;
                    aa.requestContinuousUpdate( false );
                }
                else
                {
                    action = _last_action;

                    _throw_dx = fabs(_dx) > 0.01 ? _dx : 0.0;
                    _throw_dy = fabs(_dy) > 0.01 ? _dy : 0.0;

                    if (_settings->getThrowingEnabled() && ( time_s_now - _time_s_last_event < 0.05 ) && (_throw_dx != 0.0 || _throw_dy != 0.0))
                    {
                        _thrown = true;
                        aa.requestRedraw();
                        aa.requestContinuousUpdate( true );
                    }
                    else if ( isMouseClick( &ea ) )
                    {
                        addMouseEvent( ea );
                        if ( _mouse_down_event )
                        {
                            action = _settings->getAction( EVENT_MOUSE_CLICK, _mouse_down_event->getButtonMask(), _mouse_down_event->getModKeyMask() );
                            if ( handlePointAction( action, ea.getX(), ea.getY(), aa.asView() ))
                                aa.requestRedraw();
                        }
                        resetMouse( aa );
                    }
                    else
                    {
                        resetMouse( aa );
                        addMouseEvent( ea );
                    }
                }
                handled = true;
                break;

            case osgGA::GUIEventAdapter::DOUBLECLICK:
                // bail out of continuous mode if necessary:
                _continuous = false;
                addMouseEvent( ea );
                if (_mouse_down_event)
                {
                    action = _settings->getAction( ea.getEventType(), _mouse_down_event->getButtonMask(), _mouse_down_event->getModKeyMask() );
                    if ( handlePointAction( action, ea.getX(), ea.getY(), aa.asView() ) )
                        aa.requestRedraw();
                    resetMouse( aa );
                    handled = true;
                }
                break;

            case osgGA::GUIEventAdapter::MOVE: // MOVE not currently bindable
                //NOP
                break;

            case osgGA::GUIEventAdapter::DRAG:
                {
                    action = _settings->getAction( ea.getEventType(), ea.getButtonMask(), ea.getModKeyMask() );
                    addMouseEvent( ea );
                    bool wasContinuous = _continuous;
                    _continuous = action.getBoolOption(OPTION_CONTINUOUS, false);
                    if ( handleMouseAction( action, aa.asView() ) )
                        aa.requestRedraw();

                    if ( _continuous && !wasContinuous )
                        _last_continuous_action_time = time_s_now; //_time_s_now;

                    aa.requestContinuousUpdate(_continuous);
                    _thrown = false;
                    handled = true;
                }
                break;

            case osgGA::GUIEventAdapter::KEYDOWN:
                if ( ea.getKey() < osgGA::GUIEventAdapter::KEY_Shift_L )
                {
                    resetMouse( aa );
                    action = _settings->getAction( ea.getEventType(), ea.getKey(), ea.getModKeyMask() );
                    if ( handleKeyboardAction( action ) )
                        aa.requestRedraw();
                    handled = true;
                }
                break;

            case osgGA::GUIEventAdapter::KEYUP:
                resetMouse( aa );
                _task->_type = TASK_NONE;
                handled = true;
                break;

            case osgGA::GUIEventAdapter::SCROLL:
                resetMouse( aa );
                addMouseEvent( ea );
                action = _settings->getAction( ea.getEventType(), ea.getScrollingMotion(), ea.getModKeyMask() );
                if ( handleScrollAction( action, action.getDoubleOption(OPTION_DURATION, 0.2) ) )
                    aa.requestRedraw();
                handled = true;
                break;
            default: break;
        }
    }

    // if a new task was started, request continuous updates.
    if ( _task.valid() && _task->_type != TASK_NONE )
    {
        aa.requestContinuousUpdate( true );
    }

    if ( handled && action._type != ACTION_NULL )
    {
        _last_action = action;
        _time_s_last_event = time_s_now;
    }

    return handled;
}

 

 

仔细分析:

            else if ( isSettingViewpoint() && !isTethering() )
            {
                if ( _frameCount < 2 )
                    _setVPStartTime->set(_time_s_now, Units::SECONDS);

                setViewpointFrame( time_s_now );
            }

这是需要调整相机漫游器位置了

isSettingViewpoint

这个函数主要是判断当前_setVP0和_setVP1是否有值

有值就从当前_setVP0视点经过一条弧线飞到_setVP1视点下

 

飞行过程调用的函数就是setViewpointFrame()

// returns "t" [0..1], the interpolation coefficient.
double
CVRECameraManipulator::setViewpointFrame(double time_s)
{
    if ( !_setVPStartTime.isSet() )
    {
        _setVPStartTime->set( time_s, Units::SECONDS );
        return 0.0;
    }
    else
    {
        // Start point is the current manipulator center:
        osg::Vec3d startWorld;
        osg::ref_ptr<osg::Node> startNode;
        if ( _setVP0->getNode(startNode) )
            startWorld = computeWorld(startNode);
        else
            _setVP0->focalPoint()->transform( _srs.get() ).toWorld(startWorld);

        // End point is the world coordinates of the target viewpoint:
        osg::Vec3d endWorld;
        osg::ref_ptr<osg::Node> endNode;
        if ( _setVP1->getNode(endNode) )
            endWorld = computeWorld(endNode);
        else
            _setVP1->focalPoint()->transform( _srs.get() ).toWorld(endWorld);

        // Remaining time is the full duration minus the time since initiation:
        double elapsed = time_s - _setVPStartTime->as(Units::SECONDS);
        double t = std::min(1.0, elapsed / _setVPDuration.as(Units::SECONDS));

        double tp = t;

        if ( _setVPArcHeight > 0.0 )
        {
            if ( tp <= 0.5 )
            {
                double t2 = 2.0*tp;
                tp = 0.5*t2;
            }
            else
            {
                double t2 = 2.0*(tp-0.5);
                tp = 0.5+(0.5*t2);
            }

            // the more smoothsteps you do, the more pronounced the fade-in/out effect
            tp = smoothStepInterp( tp );
        }
        else if ( t > 0.0 )
        {
            tp = smoothStepInterp( tp );
        }

        osg::Vec3d newCenter =
            _srs->isGeographic() ? nlerp(startWorld, endWorld, tp) : lerp(startWorld, endWorld, tp);

        // Calculate the delta-heading, and make sure we are going in the shortest direction:
        Angle d_azim = _setVP1->heading().get() - _setVP0->heading().get();
        if ( d_azim.as(Units::RADIANS) > osg::PI )
            d_azim = d_azim - Angle(2.0*osg::PI, Units::RADIANS);
        else if ( d_azim.as(Units::RADIANS) < -osg::PI )
            d_azim = d_azim + Angle(2.0*osg::PI, Units::RADIANS);
        double newAzim = _setVP0->heading()->as(Units::RADIANS) + tp*d_azim.as(Units::RADIANS);

        // Calculate the new pitch:
        Angle d_pitch = _setVP1->pitch().get() - _setVP0->pitch().get();
        double newPitch = _setVP0->pitch()->as(Units::RADIANS) + tp*d_pitch.as(Units::RADIANS);

        // Calculate the new range:
        Distance d_range = _setVP1->range().get() - _setVP0->range().get();
        double newRange =
            _setVP0->range()->as(Units::METERS) +
            d_range.as(Units::METERS)*tp + sin(osg::PI*tp)*_setVPArcHeight;

        // Calculate the offsets
        osg::Vec3d offset0 = _setVP0->positionOffset().getOrUse(osg::Vec3d(0,0,0));
        osg::Vec3d offset1 = _setVP1->positionOffset().getOrUse(osg::Vec3d(0,0,0));
        osg::Vec3d newOffset = offset0 + (offset1-offset0)*tp;

        // Activate.
        setLookAt( newCenter, newAzim, newPitch, newRange, newOffset );

        // interpolate tether rotation:
        _tetherRotation.slerp(tp, _tetherRotationVP0, _tetherRotationVP1);

        // At t=1 the transition is complete.
        if ( t >= 1.0 )
        {
            _setVP0.unset();

            // If this was a transition into a tether, keep the endpoint around so we can
            // continue tracking it.
            if ( !isTethering() )
            {
                _setVP1.unset();
            }
        }

        return tp;
    }
}

setViewpointFrame函数返回的就是0到1之间的插值系数。

 

        // Remaining time is the full duration minus the time since initiation:
        double elapsed = time_s - _setVPStartTime->as(Units::SECONDS);
        double t = std::min(1.0, elapsed / _setVPDuration.as(Units::SECONDS));

剩余时间是整个时间减去自启动以来的时间:

  elapsed      是间隔时间

  t | tp      是间隔系数

_setVPArcHeight   是弧顶高(初始和结束两点之间如果,有一个是最高点则,弧顶高为0)

 

到vrecameraManipulator.cpp  1172行,未完

 

以上是关于[osg][osgEarth]EarthManipulator关于oe漫游器的handle部分解读的主要内容,如果未能解决你的问题,请参考以下文章

osgearth 只移动

[原][译][osg][osgEarth]飞行模拟软件JSBSim的操作(FGFCS类)

[osg][osgEarth]EarthManipulator关于oe漫游器的handle部分解读

ubuntu20.04 osg3.6.4 osgearth2.10

[原][osg][osgEarth]关于在OE中使用物理引擎的调研

OsgEarth开发笔记:Qt5.15.2在QtCreator集成Osg3.6.3+OsgEarth3.1+OsgQt的vs2019x64版本开发环境搭建