首先我们需要定义一些变量来储存所需信息
D3DXQUATERNION g_qNow, g_qDown; //分别是 现在的旋转用四元数 按下鼠标时的旋转用四元数
D3DXVECTOR3 g_vDownPt, g_vCurrentPt; //分别是 按下鼠标时的球面上的位置向量 当前的位置向量
bool g_bDrag = false; //是否按下鼠标左键 用来判断按键拖拽
然后初始化这些变量
D3DXQuaternionIdentity(&g_qNow);
D3DXQuaternionIdentity(&g_qDown);
g_bDrag = false;
好了,初始化工作做完了,然后具体讲下算法。
在鼠标按下处理过程
case WM_LBUTTONDOWN:
{
int iMouseX = ( short )LOWORD( lParam );
int iMouseY = ( short )HIWORD( lParam ); //得到屏幕坐标
g_bDrag = true;
g_qDown = g_qNow;
g_vDownPt = ScreenToVector( ( float )iMouseX, ( float )iMouseY ); //得到投影窗口下的坐标
}
在鼠标移动处理过程
case WM_MOUSEMOVE:
{
if( g_bDrag )
{
int iMouseX = ( short )LOWORD( lParam );
int iMouseY = ( short )HIWORD( lParam ); //得到屏幕坐标
g_vCurrentPt = ScreenToVector( ( float )iMouseX, ( float )iMouseY );//得到投影窗口下的坐标
g_qNow = g_qDown * QuatFromBallPoints( g_vDownPt, g_vCurrentPt );
}
}
//看看QuatFromBallPoints函数
D3DXQUATERNION QuatFromBallPoints( const D3DXVECTOR3& vFrom, const D3DXVECTOR3& vTo )
{
D3DXVECTOR3 vPart;
float fDot = D3DXVec3Dot( &vFrom, &vTo ); //取得两向量的点乘,因为两个都是单位向量,所以fDot等于cos theta
D3DXVec3Cross( &vPart, &vFrom, &vTo );//叉乘,获得的是垂直于两个向量的一个向量,即旋转轴。其模等于|a||b|sin theta等于sin theta
return D3DXQUATERNION( vPart.x, vPart.y, vPart.z, fDot );//正好构成一个旋转2*theta角度的四元数
}
首先说一下旋转四元数,一个(x*sin theta, y*sin theta, z*sin theta, cos theta)的四元数被用来旋转2*theta角度。在上面的代码中通过两个单位向量得到了一个旋转四元数。从效果上来说就是鼠标在球体上做过theta角度,物体就旋转2*theta角度。这是一个很方便的解决方法,不仅省却了换算,也使得物体能够在一次拖拽中旋转360度。现在大部分事情都清楚了,接下来只要在绘制之前把世界矩阵按所得的旋转四元数旋转之后绘制物体就可以了。
我们使用D3DXMatrixRotationQuaternion这个函数从四元数得到一个旋转矩阵作为世界矩阵。
D3DXMATRIXA16 matWorld;
D3DXMatrixRotationQuaternion( &matWorld, &g_qNow );
g_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );
至此完成,我们只需要在每一次绘制的时候按当前的旋转四元数即g_qNow来设置世界矩阵即可。