本例实现,鼠标点击场景中任何一点,场景中的正方体即移动到鼠标指定点,右键按住不放,旋转场景,鼠标滚轮实现场景缩放
核心实现:
1,相机类 (摄像机根据输入事件改变自己的观察矩阵)
//相机类
class CELL3RDCamera
{
public:
float3 _eye;
float3 _up;
float3 _right;
float3 _target;
float3 _dir;
float _radius;
matrix4 _matView;
matrix4 _matProj;
matrix4 _matWorld;
float2 _viewSize;
float _yaw;//偏离度
public:
CELL3RDCamera()
{
_radius = 400;
_yaw = 0;
_viewSize = float2(100,100);
_matView.identify();//初始化为单位矩阵
_matProj.identify();
_matWorld.identify();
}
float getRadius() const
{
return _radius;
}
void setRadius(float val)
{
_radius = val;
}
CELL::float3 getEye() const
{
return _eye;
}
//设置眼睛位置
void setEye(CELL::float3 val)
{
_eye = val;
}
//计算方向
void calcDir()
{
_dir = _target - _eye;//得到运动方向向量
_dir = normalize(_dir);//归一化
}
CELL::float3 getTarget() const
{
return _target;
}
void setTarget(CELL::float3 val)
{
_target = val;
}
CELL::float3 getUp() const
{
return _up;
}
void setUp(CELL::float3 val)
{
_up = val;
}
float3 getDir() const
{
return _dir;
}
float3 getRight() const
{
return _right;
}
void update()
{
//归一化
float3 upDir = normalize(_up);
_eye = _target - _dir*_radius;
_right = normalize(cross(_dir,upDir));
//设置摄像机观察矩阵
_matView = CELL::lookAt(_eye,_target,_up);
}
void setViewSize(const float2& viewSize)
{
_viewSize = viewSize;
}
void setViewSize(float x, float y)
{
_viewSize = float2(x, y);
}
float2 getViewSize()
{
return _viewSize;
}
void setProject(const matrix4& proj)
{
_matProj = proj;
}
const matrix4& getProject() const
{
return _matProj;
}
const matrix4& getView() const
{
return _matView;
}
void perspective(float fovy, float aspect, float zNear, float zFar)
{
_matProj = CELL::perspective<float>(fovy,aspect,zNear,zFar);
}
/**
* 世界坐标转化为窗口坐标
*/
bool project(const float4& world, float4& screen)
{
screen = (_matProj * _matView * _matWorld) * world;
if (screen.w == 0.0f)
{
return false;
}
screen.x /= screen.w;
screen.y /= screen.w;
screen.z /= screen.w;
// map to range 0 - 1
screen.x = screen.x * 0.5f + 0.5f;
screen.y = screen.y * 0.5f + 0.5f;
screen.z = screen.z * 0.5f + 0.5f;
// map to viewport
screen.x = screen.x * _viewSize.x;
screen.y = _viewSize.y - (screen.y * _viewSize.y);
return true;
}
/**
* 世界坐标转化为窗口坐标
*/
float2 worldToScreen(const float3& world)
{
float4 worlds(world.x, world.y, world.z, 1);
float4 screens;
project(worlds, screens);
return float2(screens.x, screens.y);
}
/**
* 窗口坐标转化为世界坐标
*/
float3 screenToWorld(const float2& screen)
{
float4 screens(screen.x, screen.y, 0, 1);
float4 world;
unProject(screens, world);
return float3(world.x, world.y, world.z);
}
float3 screenToWorld(float x, float y)
{
float4 screens(x, y, 0, 1);
float4 world;
unProject(screens, world);
return float3(world.x, world.y, world.z);
}
/**
* 窗口坐标转化为世界坐标
*/
bool unProject(const float4& screen, float4& world)
{
float4 v;
v.x = screen.x;
v.y = screen.y;
v.z = screen.z;
v.w = 1.0;
// map from viewport to 0 - 1
v.x = (v.x) / _viewSize.x;
v.y = (_viewSize.y - v.y) / _viewSize.y;
//v.y = (v.y - _viewPort.Y) / _viewPort.Height;
// map to range -1 to 1
v.x = v.x * 2.0f - 1.0f;
v.y = v.y * 2.0f - 1.0f;
v.z = v.z * 2.0f - 1.0f;
CELL::matrix4 inverse = (_matProj * _matView * _matWorld).inverse();
v = v * inverse;
if (v.w == 0.0f)
{
return false;
}
world = v / v.w;
return true;
}
Ray createRayFromScreen(int x, int y)
{
float4 minWorld;
float4 maxWorld;
float4 screen(float(x), float(y), 0, 1);
float4 screen1(float(x), float(y), 1, 1);
unProject(screen, minWorld);
unProject(screen1, maxWorld);
Ray ray;
ray.setOrigin(float3(minWorld.x, minWorld.y, minWorld.z));
float3 dir(maxWorld.x - minWorld.x, maxWorld.y - minWorld.y, maxWorld.z - minWorld.z);
ray.setDirection(normalize(dir));
return ray;
}
/**
* 下面的函数的功能是将摄像机的观察方向绕某个方向轴旋转一定的角度
* 改变观察者的位置,目标的位置不变化
*/
void rotateView(float angle)
{
_dir = rotateY<float>(_dir, angle);
}
};
场景角色类(响应输入事件的角色)
//场景中的角色类
class Role
{
public:
float3 _pos;//当前位置
float3 _target;//目的位置
float _speed;//速度
public:
Role()
{
_speed = 5;
}
//设置移动的目标点
void setTarget(float3 target)
{
_target = target;
}
//更新位置
void setPosition(float3 pos)
{
_pos = pos;
_pos.y = 1;
}
void moveCheck(const float elasped)
{
//目标位置不能是当前位置
if (_target==_pos)
{
return;
}
//获取当前位置与目标位置的偏移量
float3 offset = _target - _pos;
//获取移动方向
float3 dir = normalize(offset);//单位向量
if (distance(_target,_pos)>1)
{
float speed = elasped*_speed;//时间乘以速度
//最新位置=当前位置+移动的偏移量
_pos += float3(dir.x*speed, 0, dir.z*speed);
}
else
{
_target = _pos;
}
}
void render(float fElapsed)
{
moveCheck(fElapsed);
}
};
事件响应代码()
/*
鼠标移动,含鼠标滚轮
*/
virtual void onMouseMove(int absx, int absy, int absz)
{
if (absz>0)
{
_camera._radius = _camera._radius*1.1f;//相机焦距变大,观察的物体缩小
}
else if (absz<0)
{
_camera._radius = _camera._radius*0.9f;//相机焦距变小,观察的物体变大
}
else if (_rightButtonDown)//按下右键不放,移动鼠标,旋转整个场景,相当于旋转摄像机
{
float2 curPos(absx,absy);
float2 offset = curPos - _mousePos;
_mousePos = curPos;
_camera.rotateView(offset.x*0.1f);
_camera.update();
}
}
/*
鼠标按下
*/
virtual void onMousePress(int absx, int absy, CELL::MouseButton id)
{
if (id==CELL::MouseButton::Left)//如果按下是是左键 则开始运动
{
//创建一条由物体当前位置到目标位置的射线(即以角色当前位置为起点,鼠标按下点为终点的向量)
Ray ray = _camera.createRayFromScreen(absx,absy);
//射线的起点
float3 pos = ray.getOrigin();
//获取起始点和结束点坐标值的比例系数
float tm = abs((pos.y)/ray.getDirection().y);
//目标点向量,射线的终点,也就是物体要运动到的终点
float3 target = -ray.getPoint(tm);
//设置当前角色的运动目的地
_role.setTarget(float3(target.x,0,target.z));
}
else if (id==CELL::MouseButton::Right) //如果按下的是右键,右键保持现状
{
_mousePos = float2(absx,absy);
_rightButtonDown = true;
}
}
/**
* 鼠标释放
*/
virtual void onMouseRelease(int /*absx*/, int /*absy*/, CELL::MouseButton id)
{
if( id == CELL::MouseButton::Right)
{
_rightButtonDown = false;
}
}
完整代码参考:http://download.csdn.net/detail/hb707934728/9853933