下图是初始的Opengl坐标系,及相机右转,并往前走时的分析:

上图中,当按下右箭头时,相机向右转,此时vRotation[2]由0变为-10,即上图的下半部分绕Z轴旋转了-10度,vRotation[2]=-10,图中角度A=90-10=90+ vRotation[2],此时ES=m_fMoveSpeed,M为S在X 轴上的投影点,SM=ES*Sin(A)= ES*Sin(90+ vRotation[2]),EM=ES*Cos(A)=ES*Cos(90+ vRotation[2]),故当按下相机往前走的键时,实际SE往前走的距离投影到Opengl的X、Y轴上的距离分别为EM、SM(注意视线方向D与Y轴平行),视点的坐标增量为(EM,SM,0)=( ES*Cos(90+ vRotation[2]), ES*Sin(90+ vRotation[2]),0),也即下面代码中的:
ChangePosition(osg::Vec3 (0, m_fMoveSpeed*sinf(osg::PI_2+m_vRotation._v[2]),0));
ChangePosition(osg::Vec3 (m_fMoveSpeed*cosf(osg::PI_2+m_vRotation._v[2]),0,0));
――――――――――――――――
完整源代码(3个文件:“ManipulatorTravel.h”,“TravelManipulator.cpp”,“cjmy.cpp”):
1. “ManipulatorTravel.h”:
#pragma once
#include <osgViewer/Viewer>
#include <osg/LineSegment>
#include <osg/Point>
#include <osg/Geometry>
#include <osg/Node>
#include <osg/Geode>
#include <osg/Group>
#include <osgGA/MatrixManipulator>
#include <osgUtil/IntersectVisitor>
#include <vector>
class TravelManipulator :public osgGA::MatrixManipulator
{public: TravelManipulator();
~TravelManipulator(void);
static TravelManipulator* TravelToScene(osg::ref_ptr<osgViewer::Viewer> viewer);
private:
osg::ref_ptr<osgViewer::Viewer> m_pHostViewer;
float m_fMoveSpeed;
osg::Vec3 m_vPosition;//相机的坐标
osg::Vec3 m_vRotation;//分别绕X、Y、Z轴旋转的角度
public:
bool m_bLeftButtonDown ;
float m_fpushY;//鼠标在屏幕中的坐标
float m_fpushX;
virtual void setByMatrix(const osg::Matrixd& matrix);
virtual void setByInverseMatrix(const osg::Matrixd& matrix);
virtual osg::Matrixd getMatrix(void) const;
virtual osg::Matrixd getInverseMatrix(void)const;
virtual bool handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter& us);
float m_fAngle;
void ChangePosition(osg::Vec3& delta);
bool m_bPeng;
float getSpeed();
void setSpeed(float &);
void SetPosition(osg::Vec3 &position);
osg::Vec3 GetPosition();
};
――――――――――――
2. “TravelManipulator.cpp”:
#include "stdafx.h"
#include "ManipulatorTravel.h"
TravelManipulator::TravelManipulator():m_fMoveSpeed(1.0f)
,m_bLeftButtonDown(false)
,m_fpushX(0)
,m_fAngle(2.5)
,m_bPeng(true)
,m_fpushY(0)
{ m_vPosition = osg::Vec3(-22.0f,-274.0f,100.0f);//相机位置
m_vRotation = osg::Vec3(osg::PI_2,0.0f,0.0f);
//初始将视点坐标系绕X轴旋转90度
}
TravelManipulator::~TravelManipulator()
{}
TravelManipulator * TravelManipulator::TravelToScene(osg::ref_ptr<osgViewer::Viewer> viewer)
{ TravelManipulator* camera = new TravelManipulator;
viewer->setCameraManipulator(camera);
//viewer中添加的相机操作器为当前TravelManipulator对象
camera->m_pHostViewer = viewer;//保存viewer
return camera;
}
void TravelManipulator::setByMatrix(const osg::Matrixd& matrix)
{}
void TravelManipulator::setByInverseMatrix(const osg::Matrixd& matrix)
{}
osg::Matrixd TravelManipulator::getMatrix(void) const
{ osg::Matrixd mat;
mat.makeRotate(m_vRotation._v[0],osg::Vec3(1.0f,0.0f,0.0f),
m_vRotation._v[1],osg::Vec3(0.0f,1.0f,0.0f),
m_vRotation._v[2],osg::Vec3(0.0f,0.0f,1.0f));
//先后绕Z、Y、X轴旋转对应的角度
return mat * osg::Matrixd::translate(m_vPosition);
//先平移视点坐标系,再旋转
}
osg::Matrixd TravelManipulator::getInverseMatrix(void) const
{ osg::Matrixd mat;
mat.makeRotate(m_vRotation._v[0],osg::Vec3(1.0f,0.0f,0.0f),
m_vRotation._v[1],osg::Vec3(0.0f,1.0f,0.0f),
m_vRotation._v[2],osg::Vec3(0.0f,0.0f,1.0f));
return osg::Matrixd::inverse(mat * osg::Matrixd::translate(m_vPosition));
//上一函数的逆,也是最终被调用的逆矩阵
}
bool TravelManipulator::handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter& us)
{ float mouseX = ea.getX();
float mouseY = ea.getY();
switch(ea.getEventType())
{case(osgGA::GUIEventAdapter::KEYDOWN):
{ if(ea.getKey() == 0x20)
{ us.requestRedraw();
us.requestContinuousUpdate(false);
return true;
}
if(ea.getKey() == 0xFF50) //相机向上移动
{ ChangePosition(osg::Vec3(0,0,m_fMoveSpeed));
return true;
}
if(ea.getKey() == 0xFF57)
{ ChangePosition(osg::Vec3(0,0,-m_fMoveSpeed));
return true;
}
if(ea.getKey() == 0x2B)
{ m_fMoveSpeed += 1.0f;
return true;
}
if(ea.getKey() == 0x2D)
{ m_fMoveSpeed -= 1.0f;
if(m_fMoveSpeed < 1.0f)
{ m_fMoveSpeed = 1.0f;
}
return true;
}
if(ea.getKey() == 0xFF52 || ea.getKey() == 0x57 || ea.getKey() == 0x77)//up,则相机往前走
{ ChangePosition(osg::Vec3 (0, m_fMoveSpeed*sinf(osg::PI_2+m_vRotation._v[2]),0));
ChangePosition(osg::Vec3 (m_fMoveSpeed*cosf(osg::PI_2+m_vRotation._v[2]),0,0));
return true;
}
if(ea.getKey() == 0xFF54 || ea.getKey() == 0x53 || ea.getKey() == 0x73)//down
{ ChangePosition(osg::Vec3(0,-m_fMoveSpeed*sinf(osg::PI_2+m_vRotation._v[2]),0)); ChangePosition(osg::Vec3(-m_fMoveSpeed*cosf(osg::PI_2+m_vRotation._v[2]),0,0));
return true;
}
if(ea.getKey() == 0x41 || ea.getKey() == 0x61)
{
ChangePosition(osg::Vec3 (0,m_fMoveSpeed*cosf(osg::PI_2+m_vRotation._v[2]),0));
ChangePosition(osg::Vec3 (-m_fMoveSpeed*sinf(osg::PI_2+m_vRotation._v[2]),0,0));
return true;
}
if(ea.getKey() == 0x44||ea.getKey() == 0x64)
{ ChangePosition(osg::Vec3 (0,-m_fMoveSpeed*cosf(osg::PI_2+m_vRotation._v[2]),0));
ChangePosition(osg::Vec3 (m_fMoveSpeed*sinf(osg::PI_2+m_vRotation._v[2]),0,0));
return true;
}
if(ea.getKey() == 0xFF53)//按下右箭头,则相机向右转
{ m_vRotation._v[2]-=osg::DegreesToRadians(m_fAngle);
//绕Z轴旋转的角度减小,如由0变为-10
}
if(ea.getKey() == 0xFF51)
{ m_vRotation._v[2] += osg::DegreesToRadians(m_fAngle);
}
if(ea.getKey() == 0x46 || ea.getKey() == 0x66)//F
{ m_fAngle -= 0.2;
return true;
}
if(ea.getKey() == 0x47 || ea.getKey() == 0x67)//G
{ m_fAngle += 0.2;
return true;
}
return false;
}
case(osgGA::GUIEventAdapter::PUSH):
if(ea.getButton() == 1)
{ m_fpushX = mouseX;
m_fpushY = mouseY;
m_bLeftButtonDown = true;
}
return false;
case(osgGA::GUIEventAdapter::DRAG):
if(m_bLeftButtonDown)
{//水平旋转相机,跟按下左、右箭头来旋转相机一样
m_vRotation._v[2] -= osg::DegreesToRadians(m_fAngle * (mouseX-m_fpushX));
m_vRotation._v[0] += osg::DegreesToRadians(1.1*(mouseY-m_fpushY));
//俯、仰角度(即抬头、低头,绕X 轴旋转):初始为90度,表示在OSG坐标下垂直平面向里看;完全低头为0,完全仰头为180度(即弧度3.14);且不能往后看。
if(m_vRotation._v[0] >= 3.14)
{ m_vRotation._v[0] = 3.14;
}
if(m_vRotation._v[0] <= 0)
{ m_vRotation._v[0] = 0;
}
}
return false;
case(osgGA::GUIEventAdapter::RELEASE):
if(ea.getButton() ==1)
{ m_bLeftButtonDown = false;
}
return false;
default:
return false;
}
}
void TravelManipulator::ChangePosition(osg::Vec3& delta)
{ if(m_bPeng)
{ osg::Vec3 newPosl = m_vPosition + delta;
osgUtil::IntersectVisitor ivXY;
osg::ref_ptr<osg::LineSegment> lineXY = new osg::LineSegment(newPosl, m_vPosition);
osg::ref_ptr<osg::LineSegment> lineZ = new osg::LineSegment(newPosl+osg::Vec3(0.0f,0.0f,
10.0f),newPosl-osg::Vec3(0.0f,0.0f,-10.0f));
ivXY.addLineSegment(lineZ.get());
ivXY.addLineSegment(lineXY.get());
m_pHostViewer->getSceneData()->accept(ivXY);
if(!ivXY.hits())
{ m_vPosition += delta;
}
}
else
{ m_vPosition += delta;//如无碰撞,则直接到新位置
}
}
void TravelManipulator::setSpeed(float &sp)
{ m_fMoveSpeed = sp;
}
float TravelManipulator::getSpeed()
{ return m_fMoveSpeed;
}
void TravelManipulator::SetPosition(osg::Vec3 &position)
{ m_vPosition = position;
}
osg::Vec3 TravelManipulator::GetPosition()
{ return m_vPosition;
}
――――――――――――
3. “cjmy.cpp”:
#include"stdafx.h"
#include<osgViewer/Viewer>
#include<osg/Node>
#include<osg/Geode>
#include<osg/Group>
#include<osgDB/ReadFile>
#include<osgDB/WriteFile>
#include<osgUtil/Optimizer>
#include "ManipulatorTravel.h"
int _tmain(int argc, _TCHAR* argv[])
{ osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
TravelManipulator::TravelToScene(viewer.get());
//在Viewer中添加相机操作器
osg::ref_ptr<osg::Group> root = new osg::Group();
osg::ref_ptr<osg::Node> node = osgDB::readNodeFile("lz.osg");
root->addChild(node.get());
osgUtil::Optimizer optimizer;
optimizer.optimize(root.get());
viewer->setSceneData(root.get());
viewer->realize();
viewer->run();
return 0;
}
本文详细解析了CSouth漫游器的工作原理和技术实现细节,包括相机操作、坐标系旋转和平移等关键技术点,适用于使用OpenGL进行三维场景漫游开发的技术人员。
1万+

被折叠的 条评论
为什么被折叠?



