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行,未完