1229 lines
44 KiB
C++
1229 lines
44 KiB
C++
//--------------------------------------------------------------------------------------
|
|
// File: DXUTcamera.cpp
|
|
//
|
|
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
// PARTICULAR PURPOSE.
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// http://go.microsoft.com/fwlink/?LinkId=320437
|
|
//--------------------------------------------------------------------------------------
|
|
#include "DXUT.h"
|
|
#include "DXUTcamera.h"
|
|
#include "DXUTres.h"
|
|
|
|
using namespace DirectX;
|
|
|
|
//======================================================================================
|
|
// CD3DArcBall
|
|
//======================================================================================
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
CD3DArcBall::CD3DArcBall()
|
|
{
|
|
Reset();
|
|
|
|
m_vDownPt = XMFLOAT3( 0, 0, 0 );
|
|
m_vCurrentPt = XMFLOAT3( 0, 0, 0 );
|
|
m_Offset.x = m_Offset.y = 0;
|
|
|
|
RECT rc;
|
|
GetClientRect( GetForegroundWindow(), &rc );
|
|
SetWindow( rc.right, rc.bottom );
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
void CD3DArcBall::Reset()
|
|
{
|
|
XMVECTOR qid = XMQuaternionIdentity();
|
|
XMStoreFloat4( &m_qDown, qid );
|
|
XMStoreFloat4( &m_qNow, qid );
|
|
|
|
XMMATRIX id = XMMatrixIdentity();
|
|
XMStoreFloat4x4( &m_mRotation, id );
|
|
XMStoreFloat4x4( &m_mTranslation, id );
|
|
XMStoreFloat4x4( &m_mTranslationDelta, id );
|
|
|
|
m_bDrag = false;
|
|
m_fRadiusTranslation = 1.0f;
|
|
m_fRadius = 1.0f;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
void CD3DArcBall::OnBegin( _In_ int nX, _In_ int nY )
|
|
{
|
|
// Only enter the drag state if the click falls
|
|
// inside the click rectangle.
|
|
if( nX >= m_Offset.x &&
|
|
nX < m_Offset.x + m_nWidth &&
|
|
nY >= m_Offset.y &&
|
|
nY < m_Offset.y + m_nHeight )
|
|
{
|
|
m_bDrag = true;
|
|
m_qDown = m_qNow;
|
|
XMVECTOR v = ScreenToVector( float(nX), float(nY) );
|
|
XMStoreFloat3( &m_vDownPt, v );
|
|
}
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
void CD3DArcBall::OnMove( _In_ int nX, _In_ int nY )
|
|
{
|
|
if( m_bDrag )
|
|
{
|
|
XMVECTOR curr = ScreenToVector( ( float )nX, ( float )nY );
|
|
XMStoreFloat3( &m_vCurrentPt, curr );
|
|
|
|
XMVECTOR down = XMLoadFloat3( &m_vDownPt );
|
|
XMVECTOR qdown = XMLoadFloat4( &m_qDown );
|
|
|
|
XMVECTOR result = XMQuaternionMultiply( qdown, QuatFromBallPoints( down, curr ) );
|
|
XMStoreFloat4( &m_qNow, result );
|
|
}
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
void CD3DArcBall::OnEnd()
|
|
{
|
|
m_bDrag = false;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
_Use_decl_annotations_
|
|
LRESULT CD3DArcBall::HandleMessages( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
|
|
{
|
|
// Current mouse position
|
|
int iMouseX = ( short )LOWORD( lParam );
|
|
int iMouseY = ( short )HIWORD( lParam );
|
|
|
|
switch( uMsg )
|
|
{
|
|
case WM_LBUTTONDOWN:
|
|
case WM_LBUTTONDBLCLK:
|
|
SetCapture( hWnd );
|
|
OnBegin( iMouseX, iMouseY );
|
|
return TRUE;
|
|
|
|
case WM_LBUTTONUP:
|
|
ReleaseCapture();
|
|
OnEnd();
|
|
return TRUE;
|
|
case WM_CAPTURECHANGED:
|
|
if( ( HWND )lParam != hWnd )
|
|
{
|
|
ReleaseCapture();
|
|
OnEnd();
|
|
}
|
|
return TRUE;
|
|
|
|
case WM_RBUTTONDOWN:
|
|
case WM_RBUTTONDBLCLK:
|
|
case WM_MBUTTONDOWN:
|
|
case WM_MBUTTONDBLCLK:
|
|
SetCapture( hWnd );
|
|
// Store off the position of the cursor when the button is pressed
|
|
m_ptLastMouse.x = iMouseX;
|
|
m_ptLastMouse.y = iMouseY;
|
|
return TRUE;
|
|
|
|
case WM_RBUTTONUP:
|
|
case WM_MBUTTONUP:
|
|
ReleaseCapture();
|
|
return TRUE;
|
|
|
|
case WM_MOUSEMOVE:
|
|
if( MK_LBUTTON & wParam )
|
|
{
|
|
OnMove( iMouseX, iMouseY );
|
|
}
|
|
else if( ( MK_RBUTTON & wParam ) || ( MK_MBUTTON & wParam ) )
|
|
{
|
|
// Normalize based on size of window and bounding sphere radius
|
|
float fDeltaX = ( m_ptLastMouse.x - iMouseX ) * m_fRadiusTranslation / m_nWidth;
|
|
float fDeltaY = ( m_ptLastMouse.y - iMouseY ) * m_fRadiusTranslation / m_nHeight;
|
|
|
|
XMMATRIX mTranslationDelta;
|
|
XMMATRIX mTranslation = XMLoadFloat4x4( &m_mTranslation );
|
|
if( wParam & MK_RBUTTON )
|
|
{
|
|
mTranslationDelta = XMMatrixTranslation( -2 * fDeltaX, 2 * fDeltaY, 0.0f );
|
|
mTranslation = XMMatrixMultiply( mTranslation, mTranslationDelta );
|
|
}
|
|
else // wParam & MK_MBUTTON
|
|
{
|
|
mTranslationDelta = XMMatrixTranslation( 0.0f, 0.0f, 5 * fDeltaY );
|
|
mTranslation = XMMatrixMultiply( mTranslation, mTranslationDelta );
|
|
}
|
|
|
|
XMStoreFloat4x4( &m_mTranslationDelta, mTranslationDelta );
|
|
XMStoreFloat4x4( &m_mTranslation, mTranslation );
|
|
|
|
// Store mouse coordinate
|
|
m_ptLastMouse.x = iMouseX;
|
|
m_ptLastMouse.y = iMouseY;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//======================================================================================
|
|
// CBaseCamera
|
|
//======================================================================================
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Constructor
|
|
//--------------------------------------------------------------------------------------
|
|
CBaseCamera::CBaseCamera() :
|
|
m_cKeysDown(0),
|
|
m_nCurrentButtonMask(0),
|
|
m_nMouseWheelDelta(0),
|
|
m_fFramesToSmoothMouseData(2.0f),
|
|
m_fCameraYawAngle(0.0f),
|
|
m_fCameraPitchAngle(0.0f),
|
|
m_fDragTimer(0.0f),
|
|
m_fTotalDragTimeToZero(0.25),
|
|
m_fRotationScaler(0.01f),
|
|
m_fMoveScaler(5.0f),
|
|
m_bMouseLButtonDown(false),
|
|
m_bMouseMButtonDown(false),
|
|
m_bMouseRButtonDown(false),
|
|
m_bMovementDrag(false),
|
|
m_bInvertPitch(false),
|
|
m_bEnablePositionMovement(true),
|
|
m_bEnableYAxisMovement(true),
|
|
m_bClipToBoundary(false),
|
|
m_bResetCursorAfterMove(false)
|
|
{
|
|
ZeroMemory( m_aKeys, sizeof( BYTE ) * CAM_MAX_KEYS );
|
|
ZeroMemory( m_GamePad, sizeof( DXUT_GAMEPAD ) * DXUT_MAX_CONTROLLERS );
|
|
|
|
// Setup the view matrix
|
|
SetViewParams( g_XMZero, g_XMIdentityR2 );
|
|
|
|
// Setup the projection matrix
|
|
SetProjParams( XM_PI / 4, 1.0f, 1.0f, 1000.0f );
|
|
|
|
GetCursorPos( &m_ptLastMousePosition );
|
|
|
|
SetRect( &m_rcDrag, LONG_MIN, LONG_MIN, LONG_MAX, LONG_MAX );
|
|
m_vVelocity = XMFLOAT3( 0, 0, 0 );
|
|
m_vVelocityDrag = XMFLOAT3( 0, 0, 0 );
|
|
m_vRotVelocity = XMFLOAT2( 0, 0 );
|
|
|
|
m_vMouseDelta = XMFLOAT2( 0, 0 );
|
|
|
|
m_vMinBoundary = XMFLOAT3( -1, -1, -1 );
|
|
m_vMaxBoundary = XMFLOAT3( 1, 1, 1 );
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Client can call this to change the position and direction of camera
|
|
//--------------------------------------------------------------------------------------
|
|
_Use_decl_annotations_
|
|
void CBaseCamera::SetViewParams( FXMVECTOR vEyePt, FXMVECTOR vLookatPt )
|
|
{
|
|
XMStoreFloat3( &m_vEye, vEyePt );
|
|
XMStoreFloat3( &m_vDefaultEye, vEyePt );
|
|
|
|
XMStoreFloat3( &m_vLookAt, vLookatPt );
|
|
XMStoreFloat3( &m_vDefaultLookAt , vLookatPt );
|
|
|
|
// Calc the view matrix
|
|
XMMATRIX mView = XMMatrixLookAtLH( vEyePt, vLookatPt, g_XMIdentityR1 );
|
|
XMStoreFloat4x4( &m_mView, mView );
|
|
|
|
XMMATRIX mInvView = XMMatrixInverse( nullptr, mView );
|
|
|
|
// The axis basis vectors and camera position are stored inside the
|
|
// position matrix in the 4 rows of the camera's world matrix.
|
|
// To figure out the yaw/pitch of the camera, we just need the Z basis vector
|
|
XMFLOAT3 zBasis;
|
|
XMStoreFloat3( &zBasis, mInvView.r[2] );
|
|
|
|
m_fCameraYawAngle = atan2f( zBasis.x, zBasis.z );
|
|
float fLen = sqrtf( zBasis.z * zBasis.z + zBasis.x * zBasis.x );
|
|
m_fCameraPitchAngle = -atan2f( zBasis.y, fLen );
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Calculates the projection matrix based on input params
|
|
//--------------------------------------------------------------------------------------
|
|
_Use_decl_annotations_
|
|
void CBaseCamera::SetProjParams( float fFOV, float fAspect, float fNearPlane, float fFarPlane )
|
|
{
|
|
// Set attributes for the projection matrix
|
|
m_fFOV = fFOV;
|
|
m_fAspect = fAspect;
|
|
m_fNearPlane = fNearPlane;
|
|
m_fFarPlane = fFarPlane;
|
|
|
|
XMMATRIX mProj = XMMatrixPerspectiveFovLH( fFOV, fAspect, fNearPlane, fFarPlane );
|
|
XMStoreFloat4x4( &m_mProj, mProj );
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Call this from your message proc so this class can handle window messages
|
|
//--------------------------------------------------------------------------------------
|
|
_Use_decl_annotations_
|
|
LRESULT CBaseCamera::HandleMessages( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
|
|
{
|
|
UNREFERENCED_PARAMETER( hWnd );
|
|
UNREFERENCED_PARAMETER( lParam );
|
|
|
|
switch( uMsg )
|
|
{
|
|
case WM_KEYDOWN:
|
|
{
|
|
// Map this key to a D3DUtil_CameraKeys enum and update the
|
|
// state of m_aKeys[] by adding the KEY_WAS_DOWN_MASK|KEY_IS_DOWN_MASK mask
|
|
// only if the key is not down
|
|
D3DUtil_CameraKeys mappedKey = MapKey( ( UINT )wParam );
|
|
if( mappedKey != CAM_UNKNOWN )
|
|
{
|
|
_Analysis_assume_( mappedKey < CAM_MAX_KEYS );
|
|
if( FALSE == IsKeyDown( m_aKeys[mappedKey] ) )
|
|
{
|
|
m_aKeys[ mappedKey ] = KEY_WAS_DOWN_MASK | KEY_IS_DOWN_MASK;
|
|
++m_cKeysDown;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_KEYUP:
|
|
{
|
|
// Map this key to a D3DUtil_CameraKeys enum and update the
|
|
// state of m_aKeys[] by removing the KEY_IS_DOWN_MASK mask.
|
|
D3DUtil_CameraKeys mappedKey = MapKey( ( UINT )wParam );
|
|
if( mappedKey != CAM_UNKNOWN && ( DWORD )mappedKey < 8 )
|
|
{
|
|
m_aKeys[ mappedKey ] &= ~KEY_IS_DOWN_MASK;
|
|
--m_cKeysDown;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_RBUTTONDOWN:
|
|
case WM_MBUTTONDOWN:
|
|
case WM_LBUTTONDOWN:
|
|
case WM_RBUTTONDBLCLK:
|
|
case WM_MBUTTONDBLCLK:
|
|
case WM_LBUTTONDBLCLK:
|
|
{
|
|
// Compute the drag rectangle in screen coord.
|
|
POINT ptCursor =
|
|
{
|
|
( short )LOWORD( lParam ), ( short )HIWORD( lParam )
|
|
};
|
|
|
|
// Update member var state
|
|
if( ( uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONDBLCLK ) && PtInRect( &m_rcDrag, ptCursor ) )
|
|
{
|
|
m_bMouseLButtonDown = true; m_nCurrentButtonMask |= MOUSE_LEFT_BUTTON;
|
|
}
|
|
if( ( uMsg == WM_MBUTTONDOWN || uMsg == WM_MBUTTONDBLCLK ) && PtInRect( &m_rcDrag, ptCursor ) )
|
|
{
|
|
m_bMouseMButtonDown = true; m_nCurrentButtonMask |= MOUSE_MIDDLE_BUTTON;
|
|
}
|
|
if( ( uMsg == WM_RBUTTONDOWN || uMsg == WM_RBUTTONDBLCLK ) && PtInRect( &m_rcDrag, ptCursor ) )
|
|
{
|
|
m_bMouseRButtonDown = true; m_nCurrentButtonMask |= MOUSE_RIGHT_BUTTON;
|
|
}
|
|
|
|
// Capture the mouse, so if the mouse button is
|
|
// released outside the window, we'll get the WM_LBUTTONUP message
|
|
SetCapture( hWnd );
|
|
GetCursorPos( &m_ptLastMousePosition );
|
|
return TRUE;
|
|
}
|
|
|
|
case WM_RBUTTONUP:
|
|
case WM_MBUTTONUP:
|
|
case WM_LBUTTONUP:
|
|
{
|
|
// Update member var state
|
|
if( uMsg == WM_LBUTTONUP )
|
|
{
|
|
m_bMouseLButtonDown = false; m_nCurrentButtonMask &= ~MOUSE_LEFT_BUTTON;
|
|
}
|
|
if( uMsg == WM_MBUTTONUP )
|
|
{
|
|
m_bMouseMButtonDown = false; m_nCurrentButtonMask &= ~MOUSE_MIDDLE_BUTTON;
|
|
}
|
|
if( uMsg == WM_RBUTTONUP )
|
|
{
|
|
m_bMouseRButtonDown = false; m_nCurrentButtonMask &= ~MOUSE_RIGHT_BUTTON;
|
|
}
|
|
|
|
// Release the capture if no mouse buttons down
|
|
if( !m_bMouseLButtonDown &&
|
|
!m_bMouseRButtonDown &&
|
|
!m_bMouseMButtonDown )
|
|
{
|
|
ReleaseCapture();
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_CAPTURECHANGED:
|
|
{
|
|
if( ( HWND )lParam != hWnd )
|
|
{
|
|
if( ( m_nCurrentButtonMask & MOUSE_LEFT_BUTTON ) ||
|
|
( m_nCurrentButtonMask & MOUSE_MIDDLE_BUTTON ) ||
|
|
( m_nCurrentButtonMask & MOUSE_RIGHT_BUTTON ) )
|
|
{
|
|
m_bMouseLButtonDown = false;
|
|
m_bMouseMButtonDown = false;
|
|
m_bMouseRButtonDown = false;
|
|
m_nCurrentButtonMask &= ~MOUSE_LEFT_BUTTON;
|
|
m_nCurrentButtonMask &= ~MOUSE_MIDDLE_BUTTON;
|
|
m_nCurrentButtonMask &= ~MOUSE_RIGHT_BUTTON;
|
|
ReleaseCapture();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_MOUSEWHEEL:
|
|
// Update member var state
|
|
m_nMouseWheelDelta += ( short )HIWORD( wParam );
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Figure out the velocity based on keyboard input & drag if any
|
|
//--------------------------------------------------------------------------------------
|
|
_Use_decl_annotations_
|
|
void CBaseCamera::GetInput( bool bGetKeyboardInput, bool bGetMouseInput, bool bGetGamepadInput )
|
|
{
|
|
m_vKeyboardDirection = XMFLOAT3( 0, 0, 0 );
|
|
if( bGetKeyboardInput )
|
|
{
|
|
// Update acceleration vector based on keyboard state
|
|
if( IsKeyDown( m_aKeys[CAM_MOVE_FORWARD] ) )
|
|
m_vKeyboardDirection.z += 1.0f;
|
|
if( IsKeyDown( m_aKeys[CAM_MOVE_BACKWARD] ) )
|
|
m_vKeyboardDirection.z -= 1.0f;
|
|
if( m_bEnableYAxisMovement )
|
|
{
|
|
if( IsKeyDown( m_aKeys[CAM_MOVE_UP] ) )
|
|
m_vKeyboardDirection.y += 1.0f;
|
|
if( IsKeyDown( m_aKeys[CAM_MOVE_DOWN] ) )
|
|
m_vKeyboardDirection.y -= 1.0f;
|
|
}
|
|
if( IsKeyDown( m_aKeys[CAM_STRAFE_RIGHT] ) )
|
|
m_vKeyboardDirection.x += 1.0f;
|
|
if( IsKeyDown( m_aKeys[CAM_STRAFE_LEFT] ) )
|
|
m_vKeyboardDirection.x -= 1.0f;
|
|
}
|
|
|
|
if( bGetMouseInput )
|
|
{
|
|
UpdateMouseDelta();
|
|
}
|
|
|
|
if( bGetGamepadInput )
|
|
{
|
|
m_vGamePadLeftThumb = XMFLOAT3( 0, 0, 0 );
|
|
m_vGamePadRightThumb = XMFLOAT3( 0, 0, 0 );
|
|
|
|
// Get controller state
|
|
for( DWORD iUserIndex = 0; iUserIndex < DXUT_MAX_CONTROLLERS; iUserIndex++ )
|
|
{
|
|
DXUTGetGamepadState( iUserIndex, &m_GamePad[iUserIndex], true, true );
|
|
|
|
// Mark time if the controller is in a non-zero state
|
|
if( m_GamePad[iUserIndex].wButtons ||
|
|
m_GamePad[iUserIndex].sThumbLX || m_GamePad[iUserIndex].sThumbLY ||
|
|
m_GamePad[iUserIndex].sThumbRX || m_GamePad[iUserIndex].sThumbRY ||
|
|
m_GamePad[iUserIndex].bLeftTrigger || m_GamePad[iUserIndex].bRightTrigger )
|
|
{
|
|
m_GamePadLastActive[iUserIndex] = DXUTGetTime();
|
|
}
|
|
}
|
|
|
|
// Find out which controller was non-zero last
|
|
int iMostRecentlyActive = -1;
|
|
double fMostRecentlyActiveTime = 0.0f;
|
|
for( DWORD iUserIndex = 0; iUserIndex < DXUT_MAX_CONTROLLERS; iUserIndex++ )
|
|
{
|
|
if( m_GamePadLastActive[iUserIndex] > fMostRecentlyActiveTime )
|
|
{
|
|
fMostRecentlyActiveTime = m_GamePadLastActive[iUserIndex];
|
|
iMostRecentlyActive = iUserIndex;
|
|
}
|
|
}
|
|
|
|
// Use the most recent non-zero controller if its connected
|
|
if( iMostRecentlyActive >= 0 && m_GamePad[iMostRecentlyActive].bConnected )
|
|
{
|
|
m_vGamePadLeftThumb.x = m_GamePad[iMostRecentlyActive].fThumbLX;
|
|
m_vGamePadLeftThumb.y = 0.0f;
|
|
m_vGamePadLeftThumb.z = m_GamePad[iMostRecentlyActive].fThumbLY;
|
|
|
|
m_vGamePadRightThumb.x = m_GamePad[iMostRecentlyActive].fThumbRX;
|
|
m_vGamePadRightThumb.y = 0.0f;
|
|
m_vGamePadRightThumb.z = m_GamePad[iMostRecentlyActive].fThumbRY;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Figure out the mouse delta based on mouse movement
|
|
//--------------------------------------------------------------------------------------
|
|
void CBaseCamera::UpdateMouseDelta()
|
|
{
|
|
// Get current position of mouse
|
|
POINT ptCurMousePos;
|
|
GetCursorPos( &ptCurMousePos );
|
|
|
|
// Calc how far it's moved since last frame
|
|
POINT ptCurMouseDelta;
|
|
ptCurMouseDelta.x = ptCurMousePos.x - m_ptLastMousePosition.x;
|
|
ptCurMouseDelta.y = ptCurMousePos.y - m_ptLastMousePosition.y;
|
|
|
|
// Record current position for next time
|
|
m_ptLastMousePosition = ptCurMousePos;
|
|
|
|
if( m_bResetCursorAfterMove && DXUTIsActive() )
|
|
{
|
|
// Set position of camera to center of desktop,
|
|
// so it always has room to move. This is very useful
|
|
// if the cursor is hidden. If this isn't done and cursor is hidden,
|
|
// then invisible cursor will hit the edge of the screen
|
|
// and the user can't tell what happened
|
|
POINT ptCenter;
|
|
|
|
// Get the center of the current monitor
|
|
MONITORINFO mi;
|
|
mi.cbSize = sizeof( MONITORINFO );
|
|
DXUTGetMonitorInfo( DXUTMonitorFromWindow( DXUTGetHWND(), MONITOR_DEFAULTTONEAREST ), &mi );
|
|
ptCenter.x = ( mi.rcMonitor.left + mi.rcMonitor.right ) / 2;
|
|
ptCenter.y = ( mi.rcMonitor.top + mi.rcMonitor.bottom ) / 2;
|
|
SetCursorPos( ptCenter.x, ptCenter.y );
|
|
m_ptLastMousePosition = ptCenter;
|
|
}
|
|
|
|
// Smooth the relative mouse data over a few frames so it isn't
|
|
// jerky when moving slowly at low frame rates.
|
|
float fPercentOfNew = 1.0f / m_fFramesToSmoothMouseData;
|
|
float fPercentOfOld = 1.0f - fPercentOfNew;
|
|
m_vMouseDelta.x = m_vMouseDelta.x * fPercentOfOld + ptCurMouseDelta.x * fPercentOfNew;
|
|
m_vMouseDelta.y = m_vMouseDelta.y * fPercentOfOld + ptCurMouseDelta.y * fPercentOfNew;
|
|
|
|
m_vRotVelocity.x = m_vMouseDelta.x * m_fRotationScaler;
|
|
m_vRotVelocity.y = m_vMouseDelta.y * m_fRotationScaler;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Figure out the velocity based on keyboard input & drag if any
|
|
//--------------------------------------------------------------------------------------
|
|
void CBaseCamera::UpdateVelocity( _In_ float fElapsedTime )
|
|
{
|
|
XMVECTOR vGamePadRightThumb = XMVectorSet( m_vGamePadRightThumb.x, -m_vGamePadRightThumb.z, 0, 0 );
|
|
|
|
XMVECTOR vMouseDelta = XMLoadFloat2( &m_vMouseDelta );
|
|
XMVECTOR vRotVelocity = vMouseDelta * m_fRotationScaler + vGamePadRightThumb * 0.02f;
|
|
|
|
XMStoreFloat2( &m_vRotVelocity, vRotVelocity );
|
|
|
|
XMVECTOR vKeyboardDirection = XMLoadFloat3( &m_vKeyboardDirection );
|
|
XMVECTOR vGamePadLeftThumb = XMLoadFloat3( &m_vGamePadLeftThumb );
|
|
XMVECTOR vAccel = vKeyboardDirection + vGamePadLeftThumb;
|
|
|
|
// Normalize vector so if moving 2 dirs (left & forward),
|
|
// the camera doesn't move faster than if moving in 1 dir
|
|
vAccel = XMVector3Normalize( vAccel );
|
|
|
|
// Scale the acceleration vector
|
|
vAccel *= m_fMoveScaler;
|
|
|
|
if( m_bMovementDrag )
|
|
{
|
|
// Is there any acceleration this frame?
|
|
if( XMVectorGetX( XMVector3LengthSq( vAccel ) ) > 0 )
|
|
{
|
|
// If so, then this means the user has pressed a movement key
|
|
// so change the velocity immediately to acceleration
|
|
// upon keyboard input. This isn't normal physics
|
|
// but it will give a quick response to keyboard input
|
|
XMStoreFloat3( &m_vVelocity, vAccel );
|
|
|
|
m_fDragTimer = m_fTotalDragTimeToZero;
|
|
|
|
XMStoreFloat3( &m_vVelocityDrag, vAccel / m_fDragTimer );
|
|
}
|
|
else
|
|
{
|
|
// If no key being pressed, then slowly decrease velocity to 0
|
|
if( m_fDragTimer > 0 )
|
|
{
|
|
// Drag until timer is <= 0
|
|
XMVECTOR vVelocity = XMLoadFloat3( &m_vVelocity );
|
|
XMVECTOR vVelocityDrag = XMLoadFloat3( &m_vVelocityDrag );
|
|
|
|
vVelocity -= vVelocityDrag * fElapsedTime;
|
|
|
|
XMStoreFloat3( &m_vVelocity, vVelocity );
|
|
|
|
m_fDragTimer -= fElapsedTime;
|
|
}
|
|
else
|
|
{
|
|
// Zero velocity
|
|
m_vVelocity = XMFLOAT3( 0, 0, 0 );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No drag, so immediately change the velocity
|
|
XMStoreFloat3( &m_vVelocity, vAccel );
|
|
}
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Maps a windows virtual key to an enum
|
|
//--------------------------------------------------------------------------------------
|
|
D3DUtil_CameraKeys CBaseCamera::MapKey( _In_ UINT nKey )
|
|
{
|
|
// This could be upgraded to a method that's user-definable but for
|
|
// simplicity, we'll use a hardcoded mapping.
|
|
switch( nKey )
|
|
{
|
|
case VK_CONTROL:
|
|
return CAM_CONTROLDOWN;
|
|
case VK_LEFT:
|
|
return CAM_STRAFE_LEFT;
|
|
case VK_RIGHT:
|
|
return CAM_STRAFE_RIGHT;
|
|
case VK_UP:
|
|
return CAM_MOVE_FORWARD;
|
|
case VK_DOWN:
|
|
return CAM_MOVE_BACKWARD;
|
|
case VK_PRIOR:
|
|
return CAM_MOVE_UP; // pgup
|
|
case VK_NEXT:
|
|
return CAM_MOVE_DOWN; // pgdn
|
|
|
|
case 'A':
|
|
return CAM_STRAFE_LEFT;
|
|
case 'D':
|
|
return CAM_STRAFE_RIGHT;
|
|
case 'W':
|
|
return CAM_MOVE_FORWARD;
|
|
case 'S':
|
|
return CAM_MOVE_BACKWARD;
|
|
case 'Q':
|
|
return CAM_MOVE_DOWN;
|
|
case 'E':
|
|
return CAM_MOVE_UP;
|
|
|
|
case VK_NUMPAD4:
|
|
return CAM_STRAFE_LEFT;
|
|
case VK_NUMPAD6:
|
|
return CAM_STRAFE_RIGHT;
|
|
case VK_NUMPAD8:
|
|
return CAM_MOVE_FORWARD;
|
|
case VK_NUMPAD2:
|
|
return CAM_MOVE_BACKWARD;
|
|
case VK_NUMPAD9:
|
|
return CAM_MOVE_UP;
|
|
case VK_NUMPAD3:
|
|
return CAM_MOVE_DOWN;
|
|
|
|
case VK_HOME:
|
|
return CAM_RESET;
|
|
}
|
|
|
|
return CAM_UNKNOWN;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Reset the camera's position back to the default
|
|
//--------------------------------------------------------------------------------------
|
|
void CBaseCamera::Reset()
|
|
{
|
|
XMVECTOR vDefaultEye = XMLoadFloat3( &m_vDefaultEye );
|
|
XMVECTOR vDefaultLookAt = XMLoadFloat3( &m_vDefaultLookAt );
|
|
|
|
SetViewParams( vDefaultEye, vDefaultLookAt );
|
|
}
|
|
|
|
|
|
//======================================================================================
|
|
// CFirstPersonCamera
|
|
//======================================================================================
|
|
|
|
CFirstPersonCamera::CFirstPersonCamera() :
|
|
m_nActiveButtonMask( 0x07 ),
|
|
m_bRotateWithoutButtonDown(false)
|
|
{
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Update the view matrix based on user input & elapsed time
|
|
//--------------------------------------------------------------------------------------
|
|
void CFirstPersonCamera::FrameMove( _In_ float fElapsedTime )
|
|
{
|
|
if( DXUTGetGlobalTimer()->IsStopped() )
|
|
{
|
|
if (DXUTGetFPS() == 0.0f)
|
|
fElapsedTime = 0;
|
|
else
|
|
fElapsedTime = 1.0f / DXUTGetFPS();
|
|
}
|
|
|
|
if( IsKeyDown( m_aKeys[CAM_RESET] ) )
|
|
{
|
|
Reset();
|
|
}
|
|
|
|
// Get keyboard/mouse/gamepad input
|
|
GetInput( m_bEnablePositionMovement, ( m_nActiveButtonMask & m_nCurrentButtonMask ) || m_bRotateWithoutButtonDown, true );
|
|
|
|
//// Get the mouse movement (if any) if the mouse button are down
|
|
//if( (m_nActiveButtonMask & m_nCurrentButtonMask) || m_bRotateWithoutButtonDown )
|
|
// UpdateMouseDelta( fElapsedTime );
|
|
|
|
// Get amount of velocity based on the keyboard input and drag (if any)
|
|
UpdateVelocity( fElapsedTime );
|
|
|
|
// Simple euler method to calculate position delta
|
|
XMVECTOR vVelocity = XMLoadFloat3( &m_vVelocity );
|
|
XMVECTOR vPosDelta = vVelocity * fElapsedTime;
|
|
|
|
// If rotating the camera
|
|
if( ( m_nActiveButtonMask & m_nCurrentButtonMask )
|
|
|| m_bRotateWithoutButtonDown
|
|
|| m_vGamePadRightThumb.x != 0
|
|
|| m_vGamePadRightThumb.z != 0 )
|
|
{
|
|
// Update the pitch & yaw angle based on mouse movement
|
|
float fYawDelta = m_vRotVelocity.x;
|
|
float fPitchDelta = m_vRotVelocity.y;
|
|
|
|
// Invert pitch if requested
|
|
if( m_bInvertPitch )
|
|
fPitchDelta = -fPitchDelta;
|
|
|
|
m_fCameraPitchAngle += fPitchDelta;
|
|
m_fCameraYawAngle += fYawDelta;
|
|
|
|
// Limit pitch to straight up or straight down
|
|
m_fCameraPitchAngle = std::max( -XM_PI / 2.0f, m_fCameraPitchAngle );
|
|
m_fCameraPitchAngle = std::min( +XM_PI / 2.0f, m_fCameraPitchAngle );
|
|
}
|
|
|
|
// Make a rotation matrix based on the camera's yaw & pitch
|
|
XMMATRIX mCameraRot = XMMatrixRotationRollPitchYaw( m_fCameraPitchAngle, m_fCameraYawAngle, 0 );
|
|
|
|
// Transform vectors based on camera's rotation matrix
|
|
XMVECTOR vWorldUp = XMVector3TransformCoord( g_XMIdentityR1, mCameraRot );
|
|
XMVECTOR vWorldAhead = XMVector3TransformCoord( g_XMIdentityR2, mCameraRot );
|
|
|
|
// Transform the position delta by the camera's rotation
|
|
if( !m_bEnableYAxisMovement )
|
|
{
|
|
// If restricting Y movement, do not include pitch
|
|
// when transforming position delta vector.
|
|
mCameraRot = XMMatrixRotationRollPitchYaw( 0.0f, m_fCameraYawAngle, 0.0f );
|
|
}
|
|
XMVECTOR vPosDeltaWorld = XMVector3TransformCoord( vPosDelta, mCameraRot );
|
|
|
|
// Move the eye position
|
|
XMVECTOR vEye = XMLoadFloat3( &m_vEye );
|
|
vEye += vPosDeltaWorld;
|
|
if( m_bClipToBoundary )
|
|
vEye = ConstrainToBoundary( vEye );
|
|
XMStoreFloat3( &m_vEye, vEye );
|
|
|
|
// Update the lookAt position based on the eye position
|
|
XMVECTOR vLookAt = vEye + vWorldAhead;
|
|
XMStoreFloat3( &m_vLookAt, vLookAt );
|
|
|
|
// Update the view matrix
|
|
XMMATRIX mView = XMMatrixLookAtLH( vEye, vLookAt, vWorldUp );
|
|
XMStoreFloat4x4( &m_mView, mView );
|
|
|
|
XMMATRIX mCameraWorld = XMMatrixInverse( nullptr, mView );
|
|
XMStoreFloat4x4( &m_mCameraWorld, mCameraWorld );
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Enable or disable each of the mouse buttons for rotation drag.
|
|
//--------------------------------------------------------------------------------------
|
|
_Use_decl_annotations_
|
|
void CFirstPersonCamera::SetRotateButtons( bool bLeft, bool bMiddle, bool bRight, bool bRotateWithoutButtonDown )
|
|
{
|
|
m_nActiveButtonMask = ( bLeft ? MOUSE_LEFT_BUTTON : 0 ) |
|
|
( bMiddle ? MOUSE_MIDDLE_BUTTON : 0 ) |
|
|
( bRight ? MOUSE_RIGHT_BUTTON : 0 );
|
|
m_bRotateWithoutButtonDown = bRotateWithoutButtonDown;
|
|
}
|
|
|
|
|
|
|
|
//======================================================================================
|
|
// CModelViewerCamera
|
|
//======================================================================================
|
|
|
|
CModelViewerCamera::CModelViewerCamera() :
|
|
m_nRotateModelButtonMask(MOUSE_LEFT_BUTTON),
|
|
m_nZoomButtonMask(MOUSE_WHEEL),
|
|
m_nRotateCameraButtonMask(MOUSE_RIGHT_BUTTON),
|
|
m_bAttachCameraToModel(false),
|
|
m_bLimitPitch(false),
|
|
m_bDragSinceLastUpdate(true),
|
|
m_fRadius(5.0f),
|
|
m_fDefaultRadius(5.0f),
|
|
m_fMinRadius(1.0f),
|
|
m_fMaxRadius(FLT_MAX)
|
|
{
|
|
XMMATRIX id = XMMatrixIdentity();
|
|
|
|
XMStoreFloat4x4( &m_mWorld, id );
|
|
XMStoreFloat4x4( &m_mModelRot, id );
|
|
XMStoreFloat4x4( &m_mModelLastRot, id );
|
|
XMStoreFloat4x4( &m_mCameraRotLast, id );
|
|
m_vModelCenter = XMFLOAT3( 0, 0, 0 );
|
|
|
|
m_bEnablePositionMovement = false;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Update the view matrix & the model's world matrix based
|
|
// on user input & elapsed time
|
|
//--------------------------------------------------------------------------------------
|
|
void CModelViewerCamera::FrameMove( _In_ float fElapsedTime )
|
|
{
|
|
if( IsKeyDown( m_aKeys[CAM_RESET] ) )
|
|
Reset();
|
|
|
|
// If no dragged has happend since last time FrameMove is called,
|
|
// and no camera key is held down, then no need to handle again.
|
|
if( !m_bDragSinceLastUpdate && 0 == m_cKeysDown )
|
|
return;
|
|
|
|
m_bDragSinceLastUpdate = false;
|
|
|
|
//// If no mouse button is held down,
|
|
//// Get the mouse movement (if any) if the mouse button are down
|
|
//if( m_nCurrentButtonMask != 0 )
|
|
// UpdateMouseDelta( fElapsedTime );
|
|
|
|
GetInput( m_bEnablePositionMovement, m_nCurrentButtonMask != 0, true );
|
|
|
|
// Get amount of velocity based on the keyboard input and drag (if any)
|
|
UpdateVelocity( fElapsedTime );
|
|
|
|
// Simple euler method to calculate position delta
|
|
XMVECTOR vPosDelta = XMLoadFloat3( &m_vVelocity ) * fElapsedTime;
|
|
|
|
// Change the radius from the camera to the model based on wheel scrolling
|
|
if( m_nMouseWheelDelta && m_nZoomButtonMask == MOUSE_WHEEL )
|
|
m_fRadius -= m_nMouseWheelDelta * m_fRadius * 0.1f / 120.0f;
|
|
m_fRadius = std::min( m_fMaxRadius, m_fRadius );
|
|
m_fRadius = std::max( m_fMinRadius, m_fRadius );
|
|
m_nMouseWheelDelta = 0;
|
|
|
|
// Get the inverse of the arcball's rotation matrix
|
|
XMMATRIX mCameraRot = XMMatrixInverse( nullptr, m_ViewArcBall.GetRotationMatrix() );
|
|
|
|
// Transform vectors based on camera's rotation matrix
|
|
XMVECTOR vWorldUp = XMVector3TransformCoord( g_XMIdentityR1, mCameraRot );
|
|
XMVECTOR vWorldAhead = XMVector3TransformCoord( g_XMIdentityR2, mCameraRot );
|
|
|
|
// Transform the position delta by the camera's rotation
|
|
XMVECTOR vPosDeltaWorld = XMVector3TransformCoord( vPosDelta, mCameraRot );
|
|
|
|
// Move the lookAt position
|
|
XMVECTOR vLookAt = XMLoadFloat3( &m_vLookAt );
|
|
vLookAt += vPosDeltaWorld;
|
|
if( m_bClipToBoundary )
|
|
vLookAt = ConstrainToBoundary( vLookAt );
|
|
XMStoreFloat3( &m_vLookAt, vLookAt );
|
|
|
|
// Update the eye point based on a radius away from the lookAt position
|
|
XMVECTOR vEye = vLookAt - vWorldAhead * m_fRadius;
|
|
XMStoreFloat3( &m_vEye, vEye );
|
|
|
|
// Update the view matrix
|
|
XMMATRIX mView = XMMatrixLookAtLH( vEye, vLookAt, vWorldUp );
|
|
XMStoreFloat4x4( &m_mView, mView );
|
|
|
|
XMMATRIX mInvView = XMMatrixInverse( nullptr, mView );
|
|
mInvView.r[3] = XMVectorSelect( mInvView.r[3], g_XMZero, g_XMSelect1110 );
|
|
|
|
XMMATRIX mModelLastRot = XMLoadFloat4x4( &m_mModelLastRot );
|
|
XMMATRIX mModelLastRotInv = XMMatrixInverse( nullptr, mModelLastRot );
|
|
|
|
// Accumulate the delta of the arcball's rotation in view space.
|
|
// Note that per-frame delta rotations could be problematic over long periods of time.
|
|
XMMATRIX mModelRot0 = m_WorldArcBall.GetRotationMatrix();
|
|
XMMATRIX mModelRot = XMLoadFloat4x4( &m_mModelRot );
|
|
mModelRot *= mView * mModelLastRotInv * mModelRot0 * mInvView;
|
|
|
|
if( m_ViewArcBall.IsBeingDragged() && m_bAttachCameraToModel && !IsKeyDown( m_aKeys[CAM_CONTROLDOWN] ) )
|
|
{
|
|
// Attach camera to model by inverse of the model rotation
|
|
XMMATRIX mCameraRotLast = XMLoadFloat4x4( &m_mCameraRotLast );
|
|
XMMATRIX mCameraLastRotInv = XMMatrixInverse( nullptr, mCameraRotLast );
|
|
XMMATRIX mCameraRotDelta = mCameraLastRotInv * mCameraRot; // local to world matrix
|
|
mModelRot *= mCameraRotDelta;
|
|
}
|
|
|
|
XMStoreFloat4x4( &m_mModelLastRot, mModelRot0 );
|
|
XMStoreFloat4x4( &m_mCameraRotLast, mCameraRot );
|
|
|
|
// Since we're accumulating delta rotations, we need to orthonormalize
|
|
// the matrix to prevent eventual matrix skew
|
|
XMVECTOR xBasis = XMVector3Normalize( mModelRot.r[0] );
|
|
XMVECTOR yBasis = XMVector3Cross( mModelRot.r[2], xBasis );
|
|
yBasis = XMVector3Normalize( yBasis );
|
|
XMVECTOR zBasis = XMVector3Cross( xBasis, yBasis );
|
|
|
|
mModelRot.r[0] = XMVectorSelect( mModelRot.r[0], xBasis, g_XMSelect1110 );
|
|
mModelRot.r[1] = XMVectorSelect( mModelRot.r[1], yBasis, g_XMSelect1110 );
|
|
mModelRot.r[2] = XMVectorSelect( mModelRot.r[2], zBasis, g_XMSelect1110 );
|
|
|
|
// Translate the rotation matrix to the same position as the lookAt position
|
|
mModelRot.r[3] = XMVectorSelect( mModelRot.r[3], vLookAt, g_XMSelect1110 );
|
|
|
|
XMStoreFloat4x4( &m_mModelRot, mModelRot );
|
|
|
|
// Translate world matrix so its at the center of the model
|
|
XMMATRIX mTrans = XMMatrixTranslation( -m_vModelCenter.x, -m_vModelCenter.y, -m_vModelCenter.z );
|
|
XMMATRIX mWorld = mTrans * mModelRot;
|
|
XMStoreFloat4x4( &m_mWorld, mWorld );
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
void CModelViewerCamera::SetDragRect( _In_ const RECT& rc )
|
|
{
|
|
CBaseCamera::SetDragRect( rc );
|
|
|
|
m_WorldArcBall.SetOffset( rc.left, rc.top );
|
|
m_ViewArcBall.SetOffset( rc.left, rc.top );
|
|
|
|
SetWindow( rc.right - rc.left, rc.bottom - rc.top );
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Reset the camera's position back to the default
|
|
//--------------------------------------------------------------------------------------
|
|
void CModelViewerCamera::Reset()
|
|
{
|
|
CBaseCamera::Reset();
|
|
|
|
XMMATRIX id = XMMatrixIdentity();
|
|
XMStoreFloat4x4( &m_mWorld, id );
|
|
XMStoreFloat4x4( &m_mModelRot, id );
|
|
XMStoreFloat4x4( &m_mModelLastRot, id );
|
|
XMStoreFloat4x4( &m_mCameraRotLast, id );
|
|
|
|
m_fRadius = m_fDefaultRadius;
|
|
m_WorldArcBall.Reset();
|
|
m_ViewArcBall.Reset();
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Override for setting the view parameters
|
|
//--------------------------------------------------------------------------------------
|
|
_Use_decl_annotations_
|
|
void CModelViewerCamera::SetViewParams( FXMVECTOR vEyePt, FXMVECTOR vLookatPt )
|
|
{
|
|
CBaseCamera::SetViewParams( vEyePt, vLookatPt );
|
|
|
|
// Propogate changes to the member arcball
|
|
XMMATRIX mRotation = XMMatrixLookAtLH( vEyePt, vLookatPt, g_XMIdentityR1 );
|
|
XMVECTOR quat = XMQuaternionRotationMatrix( mRotation );
|
|
m_ViewArcBall.SetQuatNow( quat );
|
|
|
|
// Set the radius according to the distance
|
|
XMVECTOR vEyeToPoint = XMVectorSubtract( vLookatPt, vEyePt );
|
|
float len = XMVectorGetX( XMVector3Length( vEyeToPoint ) );
|
|
SetRadius( len );
|
|
|
|
// View information changed. FrameMove should be called.
|
|
m_bDragSinceLastUpdate = true;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Call this from your message proc so this class can handle window messages
|
|
//--------------------------------------------------------------------------------------
|
|
_Use_decl_annotations_
|
|
LRESULT CModelViewerCamera::HandleMessages( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
|
|
{
|
|
CBaseCamera::HandleMessages( hWnd, uMsg, wParam, lParam );
|
|
|
|
if( ( ( uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONDBLCLK ) && m_nRotateModelButtonMask & MOUSE_LEFT_BUTTON ) ||
|
|
( ( uMsg == WM_MBUTTONDOWN || uMsg == WM_MBUTTONDBLCLK ) && m_nRotateModelButtonMask & MOUSE_MIDDLE_BUTTON ) ||
|
|
( ( uMsg == WM_RBUTTONDOWN || uMsg == WM_RBUTTONDBLCLK ) && m_nRotateModelButtonMask & MOUSE_RIGHT_BUTTON ) )
|
|
{
|
|
int iMouseX = ( short )LOWORD( lParam );
|
|
int iMouseY = ( short )HIWORD( lParam );
|
|
m_WorldArcBall.OnBegin( iMouseX, iMouseY );
|
|
}
|
|
|
|
if( ( ( uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONDBLCLK ) && m_nRotateCameraButtonMask & MOUSE_LEFT_BUTTON ) ||
|
|
( ( uMsg == WM_MBUTTONDOWN || uMsg == WM_MBUTTONDBLCLK ) &&
|
|
m_nRotateCameraButtonMask & MOUSE_MIDDLE_BUTTON ) ||
|
|
( ( uMsg == WM_RBUTTONDOWN || uMsg == WM_RBUTTONDBLCLK ) && m_nRotateCameraButtonMask & MOUSE_RIGHT_BUTTON ) )
|
|
{
|
|
int iMouseX = ( short )LOWORD( lParam );
|
|
int iMouseY = ( short )HIWORD( lParam );
|
|
m_ViewArcBall.OnBegin( iMouseX, iMouseY );
|
|
}
|
|
|
|
if( uMsg == WM_MOUSEMOVE )
|
|
{
|
|
int iMouseX = ( short )LOWORD( lParam );
|
|
int iMouseY = ( short )HIWORD( lParam );
|
|
m_WorldArcBall.OnMove( iMouseX, iMouseY );
|
|
m_ViewArcBall.OnMove( iMouseX, iMouseY );
|
|
}
|
|
|
|
if( ( uMsg == WM_LBUTTONUP && m_nRotateModelButtonMask & MOUSE_LEFT_BUTTON ) ||
|
|
( uMsg == WM_MBUTTONUP && m_nRotateModelButtonMask & MOUSE_MIDDLE_BUTTON ) ||
|
|
( uMsg == WM_RBUTTONUP && m_nRotateModelButtonMask & MOUSE_RIGHT_BUTTON ) )
|
|
{
|
|
m_WorldArcBall.OnEnd();
|
|
}
|
|
|
|
if( ( uMsg == WM_LBUTTONUP && m_nRotateCameraButtonMask & MOUSE_LEFT_BUTTON ) ||
|
|
( uMsg == WM_MBUTTONUP && m_nRotateCameraButtonMask & MOUSE_MIDDLE_BUTTON ) ||
|
|
( uMsg == WM_RBUTTONUP && m_nRotateCameraButtonMask & MOUSE_RIGHT_BUTTON ) )
|
|
{
|
|
m_ViewArcBall.OnEnd();
|
|
}
|
|
|
|
if( uMsg == WM_CAPTURECHANGED )
|
|
{
|
|
if( ( HWND )lParam != hWnd )
|
|
{
|
|
if( ( m_nRotateModelButtonMask & MOUSE_LEFT_BUTTON ) ||
|
|
( m_nRotateModelButtonMask & MOUSE_MIDDLE_BUTTON ) ||
|
|
( m_nRotateModelButtonMask & MOUSE_RIGHT_BUTTON ) )
|
|
{
|
|
m_WorldArcBall.OnEnd();
|
|
}
|
|
|
|
if( ( m_nRotateCameraButtonMask & MOUSE_LEFT_BUTTON ) ||
|
|
( m_nRotateCameraButtonMask & MOUSE_MIDDLE_BUTTON ) ||
|
|
( m_nRotateCameraButtonMask & MOUSE_RIGHT_BUTTON ) )
|
|
{
|
|
m_ViewArcBall.OnEnd();
|
|
}
|
|
}
|
|
}
|
|
|
|
if( uMsg == WM_LBUTTONDOWN ||
|
|
uMsg == WM_LBUTTONDBLCLK ||
|
|
uMsg == WM_MBUTTONDOWN ||
|
|
uMsg == WM_MBUTTONDBLCLK ||
|
|
uMsg == WM_RBUTTONDOWN ||
|
|
uMsg == WM_RBUTTONDBLCLK ||
|
|
uMsg == WM_LBUTTONUP ||
|
|
uMsg == WM_MBUTTONUP ||
|
|
uMsg == WM_RBUTTONUP ||
|
|
uMsg == WM_MOUSEWHEEL ||
|
|
uMsg == WM_MOUSEMOVE )
|
|
{
|
|
m_bDragSinceLastUpdate = true;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//======================================================================================
|
|
// CDXUTDirectionWidget
|
|
//======================================================================================
|
|
|
|
CDXUTDirectionWidget::CDXUTDirectionWidget() :
|
|
m_fRadius(1.0f),
|
|
m_nRotateMask(MOUSE_RIGHT_BUTTON)
|
|
{
|
|
m_vDefaultDir = XMFLOAT3( 0, 1, 0 );
|
|
m_vCurrentDir = m_vDefaultDir;
|
|
|
|
XMMATRIX id = XMMatrixIdentity();
|
|
|
|
XMStoreFloat4x4( &m_mView, id );
|
|
XMStoreFloat4x4( &m_mRot, id );
|
|
XMStoreFloat4x4( &m_mRotSnapshot, id );
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
_Use_decl_annotations_
|
|
LRESULT CDXUTDirectionWidget::HandleMessages( HWND hWnd, UINT uMsg,
|
|
WPARAM wParam, LPARAM lParam )
|
|
{
|
|
UNREFERENCED_PARAMETER(wParam);
|
|
|
|
switch( uMsg )
|
|
{
|
|
case WM_LBUTTONDOWN:
|
|
case WM_MBUTTONDOWN:
|
|
case WM_RBUTTONDOWN:
|
|
{
|
|
if( ( ( m_nRotateMask & MOUSE_LEFT_BUTTON ) != 0 && uMsg == WM_LBUTTONDOWN ) ||
|
|
( ( m_nRotateMask & MOUSE_MIDDLE_BUTTON ) != 0 && uMsg == WM_MBUTTONDOWN ) ||
|
|
( ( m_nRotateMask & MOUSE_RIGHT_BUTTON ) != 0 && uMsg == WM_RBUTTONDOWN ) )
|
|
{
|
|
int iMouseX = ( int )( short )LOWORD( lParam );
|
|
int iMouseY = ( int )( short )HIWORD( lParam );
|
|
m_ArcBall.OnBegin( iMouseX, iMouseY );
|
|
SetCapture( hWnd );
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
case WM_MOUSEMOVE:
|
|
{
|
|
if( m_ArcBall.IsBeingDragged() )
|
|
{
|
|
int iMouseX = ( int )( short )LOWORD( lParam );
|
|
int iMouseY = ( int )( short )HIWORD( lParam );
|
|
m_ArcBall.OnMove( iMouseX, iMouseY );
|
|
UpdateLightDir();
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
case WM_LBUTTONUP:
|
|
case WM_MBUTTONUP:
|
|
case WM_RBUTTONUP:
|
|
{
|
|
if( ( ( m_nRotateMask & MOUSE_LEFT_BUTTON ) != 0 && uMsg == WM_LBUTTONUP ) ||
|
|
( ( m_nRotateMask & MOUSE_MIDDLE_BUTTON ) != 0 && uMsg == WM_MBUTTONUP ) ||
|
|
( ( m_nRotateMask & MOUSE_RIGHT_BUTTON ) != 0 && uMsg == WM_RBUTTONUP ) )
|
|
{
|
|
m_ArcBall.OnEnd();
|
|
ReleaseCapture();
|
|
}
|
|
|
|
UpdateLightDir();
|
|
return TRUE;
|
|
}
|
|
|
|
case WM_CAPTURECHANGED:
|
|
{
|
|
if( ( HWND )lParam != hWnd )
|
|
{
|
|
if( ( m_nRotateMask & MOUSE_LEFT_BUTTON ) ||
|
|
( m_nRotateMask & MOUSE_MIDDLE_BUTTON ) ||
|
|
( m_nRotateMask & MOUSE_RIGHT_BUTTON ) )
|
|
{
|
|
m_ArcBall.OnEnd();
|
|
ReleaseCapture();
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
HRESULT CDXUTDirectionWidget::UpdateLightDir()
|
|
{
|
|
XMMATRIX mView = XMLoadFloat4x4( &m_mView );
|
|
|
|
XMMATRIX mInvView = XMMatrixInverse( nullptr, mView );
|
|
mInvView.r[3] = XMVectorSelect( mInvView.r[3], g_XMZero, g_XMSelect1110 );
|
|
|
|
XMMATRIX mRotSnapshot = XMLoadFloat4x4( &m_mRotSnapshot );
|
|
XMMATRIX mLastRotInv = XMMatrixInverse( nullptr, mRotSnapshot );
|
|
|
|
XMMATRIX mRot0 = m_ArcBall.GetRotationMatrix();
|
|
XMStoreFloat4x4( &m_mRotSnapshot, mRot0 );
|
|
|
|
// Accumulate the delta of the arcball's rotation in view space.
|
|
// Note that per-frame delta rotations could be problematic over long periods of time.
|
|
XMMATRIX mRot = XMLoadFloat4x4( &m_mRot );
|
|
mRot *= mView * mLastRotInv * mRot0 * mInvView;
|
|
|
|
// Since we're accumulating delta rotations, we need to orthonormalize
|
|
// the matrix to prevent eventual matrix skew
|
|
XMVECTOR xBasis = XMVector3Normalize( mRot.r[0] );
|
|
XMVECTOR yBasis = XMVector3Cross( mRot.r[2], xBasis );
|
|
yBasis = XMVector3Normalize( yBasis );
|
|
XMVECTOR zBasis = XMVector3Cross( xBasis, yBasis );
|
|
mRot.r[0] = XMVectorSelect( mRot.r[0], xBasis, g_XMSelect1110 );
|
|
mRot.r[1] = XMVectorSelect( mRot.r[1], yBasis, g_XMSelect1110 );
|
|
mRot.r[2] = XMVectorSelect( mRot.r[2], zBasis, g_XMSelect1110 );
|
|
XMStoreFloat4x4( &m_mRot, mRot );
|
|
|
|
// Transform the default direction vector by the light's rotation matrix
|
|
XMVECTOR vDefaultDir = XMLoadFloat3( &m_vDefaultDir );
|
|
XMVECTOR vCurrentDir = XMVector3TransformNormal( vDefaultDir, mRot );
|
|
XMStoreFloat3( &m_vCurrentDir, vCurrentDir );
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
_Use_decl_annotations_
|
|
HRESULT CDXUTDirectionWidget::OnRender( FXMVECTOR color, CXMMATRIX mView, CXMMATRIX mProj, FXMVECTOR vEyePt )
|
|
{
|
|
UNREFERENCED_PARAMETER(color);
|
|
UNREFERENCED_PARAMETER(mView);
|
|
UNREFERENCED_PARAMETER(mProj);
|
|
UNREFERENCED_PARAMETER(vEyePt);
|
|
// TODO -
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
_Use_decl_annotations_
|
|
HRESULT CDXUTDirectionWidget::StaticOnD3D11CreateDevice( ID3D11Device* pd3dDevice, ID3D11DeviceContext* pd3dImmediateContext )
|
|
{
|
|
UNREFERENCED_PARAMETER(pd3dDevice);
|
|
UNREFERENCED_PARAMETER(pd3dImmediateContext);
|
|
// TODO -
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
void CDXUTDirectionWidget::StaticOnD3D11DestroyDevice()
|
|
{
|
|
// TODO -
|
|
}
|