玩过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); } } 实现效果如下:

评论