玩过Google Earth操作的人都知道,其中键操作非常方便。以中键按下的点为不动点,鼠标操作的方向进行旋转,与仅仅绕场景中心旋转的方式要灵活许多。
本文主要介绍了简单的几何实现。
如图所示,E为视点,C为场景中心,P为中键按下点。此时如果鼠标沿屏幕X方向移动,表示,场景沿水平方向转动;若沿屏幕Y方向移动,场景在CEE’竖直平面内转动。
1、场景沿水平方向转动比较简单,E和C分别以旋转轴为Z轴,以P为旋转中心,转动的角度按照屏幕X方向移动的距离比例计算出来,通过四元数计算新视点和新的场景中心。
2、场景在CEE’竖直平面内的转动要复杂一点。首先我们要计算P在这个平面内的投影P’。此时E和C分别以P’为旋转中心,PP’为旋转轴,转动的角度按照屏幕Y方向移动的距离比例计算出来,通过四元数计算新视点和新的场景中心。
相关代码如下:
void CCamera::RotateScene(double alpha_add, double beta_add,Vertex3d NonMovePoint)
{
// 绕不动点旋转
// 用四元数来实现modified by XWP
Vertex3d vZAxis(0,0,1);
Quaternion qq(-alpha_add*3.1415926,vZAxis);
// 先绕平面上的角度转动
Vertex3d curOrient = NonMovePoint - m_Eye;
Vertex3d curOrient2 = m_Cen - NonMovePoint;
Vertex3d newOrient = qq * curOrient;
Vertex3d newOrient2 = qq * curOrient2;
newOrient.Normailize();
newOrient = newOrient * curOrient.Length();
newOrient2.Normailize();
newOrient2 = newOrient2 * curOrient2.Length();
// 得到新的视点和中心
Vertex3d newEye = NonMovePoint - newOrient;
Vertex3d newCen = NonMovePoint + newOrient2;
Vertex3d vAxis = (newEye - newCen) ^ vZAxis;
vAxis.Normailize();
// 再次在竖直平面内旋转
// 首先求出不动点在竖直平面的垂足
PLANE3D plane;
GetPlaneEquation(vAxis,newEye,plane);
Vertex3d tempFootPt;
IsPointInPlane(NonMovePoint,plane,&tempFootPt);
// 连接垂足与视点、垂足与中心,绕竖直平面的法向量进行旋转
newOrient = tempFootPt - newEye;
double length = newOrient.Length();
vAxis = newOrient ^ vZAxis;
vAxis.Normailize();
Quaternion qq1(-beta_add*3.1415926,vAxis);
newOrient = qq1 * newOrient;
newOrient.Normailize();
newOrient = newOrient * length;
newEye = tempFootPt - newOrient;
newOrient2 = newCen - tempFootPt;
length = newOrient2.Length();
vAxis = newOrient2 ^ vZAxis;
vAxis.Normailize();
Quaternion qq2(-beta_add*3.1415926,vAxis);
newOrient2 = qq2 * newOrient2;
newOrient2.Normailize();
newOrient2 = newOrient2 * length;
newCen = tempFootPt + newOrient2;
//下面是用来限制不超过度的旋转
Vertex3d newLine = newCen - newEye;
newLine.Normailize();
Vertex3d up = GetUp();
double dtheta = newLine*up;
if (fabs(dtheta) -0.98 < 0.000001 && dtheta <0)
{
SetEye(newEye);
SetCenter(newCen);
}
if (dtheta >0 && dtheta < 0.98)
{
SetEye(newEye);
SetCenter(newCen);
}
}
实现效果如下:
评论