• 左键拖拽 - 旋转魔方
• 右键拖拽 - 变换视角
• 滚轮 - 缩放魔方

# 右键拖拽 - 变换视角

• 按下鼠标右键（此时鼠标的位置是P1）
• 拖拽右键（此时鼠标的位置是P2，注意P2是随拖拽实时变化的）
• 抬起鼠标右键（停止旋转）

## 屏幕坐标到球坐标

 1 D3DXVECTOR3 ArcBall::ScreenToVector(int screen_x, int screen_y)
2 {
3     // Scale to screen
4     float x = -(screen_x - window_width_ / 2) / (radius_ * window_width_ / 2);
5     float y = (screen_y - window_height_ / 2) / (radius_ * window_height_ / 2);
6
7     float z = 0.0f;
8     float mag = x * x + y * y;
9
10     if(mag > 1.0f)
11     {
12         float scale = 1.0f / sqrtf(mag);
13         x *= scale;
14         y *= scale;
15     }
16     else
17         z = sqrtf(1.0f - mag);
18
19     return D3DXVECTOR3(x, y, z);
20 }

10-15行，如果xy的平方和大于1，此时该点恰好位于半球球的边缘，所以令z=0

17行，如果xy平方和小于1，说明该点不位于半球边缘，计算z的值。

19行返回球体坐标对应的向量（已经单位化）。

## 旋转轴及旋转角度

q.x = sin(theta / 2) * axis.x;
q.y = sin(theta / 2) * axis.y;
q.z = sin(theta / 2) * axis.z;
q.w = cos(theta / 2);

 1 D3DXQUATERNION ArcBall::QuatFromBallPoints(D3DXVECTOR3& start_point, D3DXVECTOR3& end_point)
2 {
3     // Calculate rotate angle
4     float angle = D3DXVec3Dot(&start_point, &end_point);
5
6     // Calculate rotate axis
7     D3DXVECTOR3 axis;
8     D3DXVec3Cross(&axis, &start_point, &end_point);
9
10     // Build and Normalize the Quaternion
11     D3DXQUATERNION quat(axis.x, axis.y, axis.z, angle);
12     D3DXQuaternionNormalize(&quat, &quat);
13
14     return quat;
15 }

## Arcball的调用

Arcball可以在处理Windows消息的时候调用。

LRESULT Camera::HandleMessages(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
// update view arc ball
if(uMsg == WM_RBUTTONDOWN)
{
SetCapture(hWnd) ;

frame_need_update_ = true ;
int mouse_x = (short)LOWORD(lParam) ;
int mouse_y = (short)HIWORD(lParam) ;
view_arcball_.OnBegin(mouse_x, mouse_y) ;
}

// mouse move
if(uMsg == WM_MOUSEMOVE)
{
frame_need_update_ = true ;
int mouse_x = (short)LOWORD(lParam);
int mouse_y = (short)HIWORD(lParam);
view_arcball_.OnMove(mouse_x, mouse_y) ;
}

// right button up, terminate view arc ball rotation
if(uMsg == WM_RBUTTONUP)
{
frame_need_update_ = true ;
view_arcball_.OnEnd();
ReleaseCapture() ;
}

return TRUE ;
}

void ArcBall::OnBegin(int mouse_x, int mouse_y)
{
// enter drag state only if user click the window's client area
if(mouse_x >= 0 && mouse_x <= window_width_
&& mouse_y >= 0 && mouse_y < window_height_)
{
is_dragged_ = true ; // begin drag state
previous_quaternion_ = current_quaternion_ ;
previous_point_ = ScreenToVector(mouse_x, mouse_y) ;
old_point_ = previous_point_ ;
}
}

void ArcBall::OnMove(int mouse_x, int mouse_y)
{
if(is_dragged_)
{
current_point_ = ScreenToVector(mouse_x, mouse_y) ;
rotation_increament_ = QuatFromBallPoints( old_point_, current_point_ ) ;
current_quaternion_ = previous_quaternion_ * QuatFromBallPoints( previous_point_, current_point_ ) ;
old_point_ = current_point_ ;
}
}

void ArcBall::OnEnd()
{
is_dragged_ = false ;
}

# 鼠标滚轮 - 缩放

// Mouse wheel, zoom in/out
if(uMsg == WM_MOUSEWHEEL)
{
frame_need_update_ = true ;
mouse_wheel_delta_ += (short)HIWORD(wParam);
}

if(mouse_wheel_delta_)
{

// This can Prevent the cube became too big or too small
}

// Update the eye point based on a radius away from the lookAt position
eye_point_ = lookat_point_ - world_ahead_vector * radius_;

## Camera

Camera类是Arcball的使用者，里面的OnFrameMove函数每一帧都会被调用，该函数负责缩放和旋转，并生成新的View Matrix。

 1 void Camera::OnFrameMove()
2 {
3     // No need to handle if no drag since last frame move
4     if(!m_bDragSinceLastUpdate)
5         return ;
6     m_bDragSinceLastUpdate = false ;
7
8     if(m_nMouseWheelDelta)
9     {
11
15     }
16
17     // The mouse delta is retrieved IN every WM_MOUSE message and do not accumulate, so clear it after one frame
18     m_nMouseWheelDelta = 0 ;
19
20     // Get the inverse of the view Arcball's rotation matrix
21     D3DXMATRIX mCameraRot ;
22     D3DXMatrixInverse(&mCameraRot, NULL, m_ViewArcBall.GetRotationMatrix());
23
24     // Transform vectors based on camera's rotation matrix
25     D3DXVECTOR3 vWorldUp;
26     D3DXVECTOR3 vLocalUp = D3DXVECTOR3(0, 1, 0);
27     D3DXVec3TransformCoord(&vWorldUp, &vLocalUp, &mCameraRot);
28
30     D3DXVECTOR3 vLocalAhead = D3DXVECTOR3(0, 0, 1);
32
33     // Update the eye point based on a radius away from the lookAt position
35
36     // Update the view matrix
37     D3DXMatrixLookAtLH( &m_matView, &m_vEyePt, &m_vLookatPt, &vWorldUp );
38 }

RubikCube

# To Be Continued

posted on 2013-09-16 09:50  翰墨小生  阅读(10000)  评论(16编辑  收藏  举报