Files
game-physics/DXUT11/Optional/DXUTcamera.h
2017-10-11 15:01:05 +02:00

427 lines
19 KiB
C++

//--------------------------------------------------------------------------------------
// File: Camera.h
//
// Helper functions for Direct3D programming.
//
// 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
//--------------------------------------------------------------------------------------
#pragma once
//--------------------------------------------------------------------------------------
class CD3DArcBall
{
public:
CD3DArcBall();
// Functions to change behavior
void Reset();
void SetTranslationRadius( _In_ float fRadiusTranslation )
{
m_fRadiusTranslation = fRadiusTranslation;
}
void SetWindow( _In_ INT nWidth, _In_ INT nHeight, _In_ float fRadius = 0.9f )
{
m_nWidth = nWidth;
m_nHeight = nHeight;
m_fRadius = fRadius;
m_vCenter.x = float(m_nWidth) / 2.0f;
m_vCenter.y = float(m_nHeight) / 2.0f;
}
void SetOffset( _In_ INT nX, _In_ INT nY ) { m_Offset.x = nX; m_Offset.y = nY; }
// Call these from client and use GetRotationMatrix() to read new rotation matrix
void OnBegin( _In_ int nX, _In_ int nY ); // start the rotation (pass current mouse position)
void OnMove( _In_ int nX, _In_ int nY ); // continue the rotation (pass current mouse position)
void OnEnd(); // end the rotation
// Or call this to automatically handle left, middle, right buttons
LRESULT HandleMessages( _In_ HWND hWnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam );
// Functions to get/set state
DirectX::XMMATRIX GetRotationMatrix() const
{
using namespace DirectX;
XMVECTOR q = XMLoadFloat4( &m_qNow );
return DirectX::XMMatrixRotationQuaternion( q );
}
DirectX::XMMATRIX GetTranslationMatrix() const { return DirectX::XMLoadFloat4x4( &m_mTranslation ); }
DirectX::XMMATRIX GetTranslationDeltaMatrix() const { return DirectX::XMLoadFloat4x4( &m_mTranslationDelta ); }
bool IsBeingDragged() const { return m_bDrag; }
DirectX::XMVECTOR GetQuatNow() const { return DirectX::XMLoadFloat4( &m_qNow ); }
void SetQuatNow( _In_ DirectX::FXMVECTOR& q ) { DirectX::XMStoreFloat4( &m_qNow, q ); }
static DirectX::XMVECTOR QuatFromBallPoints( _In_ DirectX::FXMVECTOR vFrom, _In_ DirectX::FXMVECTOR vTo )
{
using namespace DirectX;
XMVECTOR dot = XMVector3Dot( vFrom, vTo );
XMVECTOR vPart = XMVector3Cross( vFrom, vTo );
return XMVectorSelect( dot, vPart, g_XMSelect1110 );
}
protected:
DirectX::XMFLOAT4X4 m_mRotation; // Matrix for arc ball's orientation
DirectX::XMFLOAT4X4 m_mTranslation; // Matrix for arc ball's position
DirectX::XMFLOAT4X4 m_mTranslationDelta;// Matrix for arc ball's position
POINT m_Offset; // window offset, or upper-left corner of window
INT m_nWidth; // arc ball's window width
INT m_nHeight; // arc ball's window height
DirectX::XMFLOAT2 m_vCenter; // center of arc ball
float m_fRadius; // arc ball's radius in screen coords
float m_fRadiusTranslation; // arc ball's radius for translating the target
DirectX::XMFLOAT4 m_qDown; // Quaternion before button down
DirectX::XMFLOAT4 m_qNow; // Composite quaternion for current drag
bool m_bDrag; // Whether user is dragging arc ball
POINT m_ptLastMouse; // position of last mouse point
DirectX::XMFLOAT3 m_vDownPt; // starting point of rotation arc
DirectX::XMFLOAT3 m_vCurrentPt; // current point of rotation arc
DirectX::XMVECTOR ScreenToVector( _In_ float fScreenPtX, _In_ float fScreenPtY )
{
// Scale to screen
float x = -( fScreenPtX - m_Offset.x - m_nWidth / 2 ) / ( m_fRadius * m_nWidth / 2 );
float y = ( fScreenPtY - m_Offset.y - m_nHeight / 2 ) / ( m_fRadius * m_nHeight / 2 );
float z = 0.0f;
float mag = x * x + y * y;
if( mag > 1.0f )
{
float scale = 1.0f / sqrtf( mag );
x *= scale;
y *= scale;
}
else
z = sqrtf( 1.0f - mag );
return DirectX::XMVectorSet( x, y, z, 0 );
}
};
//--------------------------------------------------------------------------------------
// used by CCamera to map WM_KEYDOWN keys
//--------------------------------------------------------------------------------------
enum D3DUtil_CameraKeys
{
CAM_STRAFE_LEFT = 0,
CAM_STRAFE_RIGHT,
CAM_MOVE_FORWARD,
CAM_MOVE_BACKWARD,
CAM_MOVE_UP,
CAM_MOVE_DOWN,
CAM_RESET,
CAM_CONTROLDOWN,
CAM_MAX_KEYS,
CAM_UNKNOWN = 0xFF
};
#define KEY_WAS_DOWN_MASK 0x80
#define KEY_IS_DOWN_MASK 0x01
#define MOUSE_LEFT_BUTTON 0x01
#define MOUSE_MIDDLE_BUTTON 0x02
#define MOUSE_RIGHT_BUTTON 0x04
#define MOUSE_WHEEL 0x08
//--------------------------------------------------------------------------------------
// Simple base camera class that moves and rotates. The base class
// records mouse and keyboard input for use by a derived class, and
// keeps common state.
//--------------------------------------------------------------------------------------
class CBaseCamera
{
public:
CBaseCamera();
// Call these from client and use Get*Matrix() to read new matrices
virtual LRESULT HandleMessages( _In_ HWND hWnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam );
virtual void FrameMove( _In_ float fElapsedTime ) = 0;
// Functions to change camera matrices
virtual void Reset();
virtual void SetViewParams( _In_ DirectX::FXMVECTOR vEyePt, _In_ DirectX::FXMVECTOR vLookatPt );
virtual void SetProjParams( _In_ float fFOV, _In_ float fAspect, _In_ float fNearPlane, _In_ float fFarPlane );
// Functions to change behavior
virtual void SetDragRect( _In_ const RECT& rc ) { m_rcDrag = rc; }
void SetInvertPitch( _In_ bool bInvertPitch ) { m_bInvertPitch = bInvertPitch; }
void SetDrag( _In_ bool bMovementDrag, _In_ float fTotalDragTimeToZero = 0.25f )
{
m_bMovementDrag = bMovementDrag;
m_fTotalDragTimeToZero = fTotalDragTimeToZero;
}
void SetEnableYAxisMovement( _In_ bool bEnableYAxisMovement ) { m_bEnableYAxisMovement = bEnableYAxisMovement; }
void SetEnablePositionMovement( _In_ bool bEnablePositionMovement ) { m_bEnablePositionMovement = bEnablePositionMovement; }
void SetClipToBoundary( _In_ bool bClipToBoundary, _In_opt_ DirectX::XMFLOAT3* pvMinBoundary, _In_opt_ DirectX::XMFLOAT3* pvMaxBoundary )
{
m_bClipToBoundary = bClipToBoundary;
if( pvMinBoundary ) m_vMinBoundary = *pvMinBoundary;
if( pvMaxBoundary ) m_vMaxBoundary = *pvMaxBoundary;
}
void SetScalers( _In_ float fRotationScaler = 0.01f, _In_ float fMoveScaler = 5.0f )
{
m_fRotationScaler = fRotationScaler;
m_fMoveScaler = fMoveScaler;
}
void SetNumberOfFramesToSmoothMouseData( _In_ int nFrames ) { if( nFrames > 0 ) m_fFramesToSmoothMouseData = ( float )nFrames; }
void SetResetCursorAfterMove( _In_ bool bResetCursorAfterMove ) { m_bResetCursorAfterMove = bResetCursorAfterMove; }
// Functions to get state
DirectX::XMMATRIX GetViewMatrix() const { return DirectX::XMLoadFloat4x4( &m_mView ); }
DirectX::XMMATRIX GetProjMatrix() const { return DirectX::XMLoadFloat4x4( &m_mProj ); }
DirectX::XMVECTOR GetEyePt() const { return DirectX::XMLoadFloat3( &m_vEye ); }
DirectX::XMVECTOR GetLookAtPt() const { return DirectX::XMLoadFloat3( &m_vLookAt ); }
float GetNearClip() const { return m_fNearPlane; }
float GetFarClip() const { return m_fFarPlane; }
bool IsBeingDragged() const { return ( m_bMouseLButtonDown || m_bMouseMButtonDown || m_bMouseRButtonDown ); }
bool IsMouseLButtonDown() const { return m_bMouseLButtonDown; }
bool IsMouseMButtonDown() const { return m_bMouseMButtonDown; }
bool sMouseRButtonDown() const { return m_bMouseRButtonDown; }
protected:
// Functions to map a WM_KEYDOWN key to a D3DUtil_CameraKeys enum
virtual D3DUtil_CameraKeys MapKey( _In_ UINT nKey );
bool IsKeyDown( _In_ BYTE key ) const { return( ( key & KEY_IS_DOWN_MASK ) == KEY_IS_DOWN_MASK ); }
bool WasKeyDown( _In_ BYTE key ) const { return( ( key & KEY_WAS_DOWN_MASK ) == KEY_WAS_DOWN_MASK ); }
DirectX::XMVECTOR ConstrainToBoundary( _In_ DirectX::FXMVECTOR v )
{
using namespace DirectX;
XMVECTOR vMin = XMLoadFloat3( &m_vMinBoundary );
XMVECTOR vMax = XMLoadFloat3( &m_vMaxBoundary );
// Constrain vector to a bounding box
return XMVectorClamp( v, vMin, vMax );
}
void UpdateMouseDelta();
void UpdateVelocity( _In_ float fElapsedTime );
void GetInput( _In_ bool bGetKeyboardInput, _In_ bool bGetMouseInput, _In_ bool bGetGamepadInput );
DirectX::XMFLOAT4X4 m_mView; // View matrix
DirectX::XMFLOAT4X4 m_mProj; // Projection matrix
DXUT_GAMEPAD m_GamePad[DXUT_MAX_CONTROLLERS]; // XInput controller state
DirectX::XMFLOAT3 m_vGamePadLeftThumb;
DirectX::XMFLOAT3 m_vGamePadRightThumb;
double m_GamePadLastActive[DXUT_MAX_CONTROLLERS];
int m_cKeysDown; // Number of camera keys that are down.
BYTE m_aKeys[CAM_MAX_KEYS]; // State of input - KEY_WAS_DOWN_MASK|KEY_IS_DOWN_MASK
DirectX::XMFLOAT3 m_vKeyboardDirection; // Direction vector of keyboard input
POINT m_ptLastMousePosition; // Last absolute position of mouse cursor
int m_nCurrentButtonMask; // mask of which buttons are down
int m_nMouseWheelDelta; // Amount of middle wheel scroll (+/-)
DirectX::XMFLOAT2 m_vMouseDelta; // Mouse relative delta smoothed over a few frames
float m_fFramesToSmoothMouseData; // Number of frames to smooth mouse data over
DirectX::XMFLOAT3 m_vDefaultEye; // Default camera eye position
DirectX::XMFLOAT3 m_vDefaultLookAt; // Default LookAt position
DirectX::XMFLOAT3 m_vEye; // Camera eye position
DirectX::XMFLOAT3 m_vLookAt; // LookAt position
float m_fCameraYawAngle; // Yaw angle of camera
float m_fCameraPitchAngle; // Pitch angle of camera
RECT m_rcDrag; // Rectangle within which a drag can be initiated.
DirectX::XMFLOAT3 m_vVelocity; // Velocity of camera
DirectX::XMFLOAT3 m_vVelocityDrag; // Velocity drag force
float m_fDragTimer; // Countdown timer to apply drag
float m_fTotalDragTimeToZero; // Time it takes for velocity to go from full to 0
DirectX::XMFLOAT2 m_vRotVelocity; // Velocity of camera
float m_fFOV; // Field of view
float m_fAspect; // Aspect ratio
float m_fNearPlane; // Near plane
float m_fFarPlane; // Far plane
float m_fRotationScaler; // Scaler for rotation
float m_fMoveScaler; // Scaler for movement
bool m_bMouseLButtonDown; // True if left button is down
bool m_bMouseMButtonDown; // True if middle button is down
bool m_bMouseRButtonDown; // True if right button is down
bool m_bMovementDrag; // If true, then camera movement will slow to a stop otherwise movement is instant
bool m_bInvertPitch; // Invert the pitch axis
bool m_bEnablePositionMovement; // If true, then the user can translate the camera/model
bool m_bEnableYAxisMovement; // If true, then camera can move in the y-axis
bool m_bClipToBoundary; // If true, then the camera will be clipped to the boundary
bool m_bResetCursorAfterMove; // If true, the class will reset the cursor position so that the cursor always has space to move
DirectX::XMFLOAT3 m_vMinBoundary; // Min point in clip boundary
DirectX::XMFLOAT3 m_vMaxBoundary; // Max point in clip boundary
};
//--------------------------------------------------------------------------------------
// Simple first person camera class that moves and rotates.
// It allows yaw and pitch but not roll. It uses WM_KEYDOWN and
// GetCursorPos() to respond to keyboard and mouse input and updates the
// view matrix based on input.
//--------------------------------------------------------------------------------------
class CFirstPersonCamera : public CBaseCamera
{
public:
CFirstPersonCamera();
// Call these from client and use Get*Matrix() to read new matrices
virtual void FrameMove( _In_ float fElapsedTime ) override;
// Functions to change behavior
void SetRotateButtons( _In_ bool bLeft, _In_ bool bMiddle, _In_ bool bRight, _In_ bool bRotateWithoutButtonDown = false );
// Functions to get state
DirectX::XMMATRIX GetWorldMatrix() const { return DirectX::XMLoadFloat4x4( &m_mCameraWorld ); }
DirectX::XMVECTOR GetWorldRight() const { return DirectX::XMLoadFloat3( reinterpret_cast<const DirectX::XMFLOAT3*>( &m_mCameraWorld._11 ) ); }
DirectX::XMVECTOR GetWorldUp() const { return DirectX::XMLoadFloat3( reinterpret_cast<const DirectX::XMFLOAT3*>( &m_mCameraWorld._21 ) ); }
DirectX::XMVECTOR GetWorldAhead() const { return DirectX::XMLoadFloat3( reinterpret_cast<const DirectX::XMFLOAT3*>( &m_mCameraWorld._31 ) ); }
DirectX::XMVECTOR GetEyePt() const { return DirectX::XMLoadFloat3( reinterpret_cast<const DirectX::XMFLOAT3*>( &m_mCameraWorld._41 ) ); }
protected:
DirectX::XMFLOAT4X4 m_mCameraWorld; // World matrix of the camera (inverse of the view matrix)
int m_nActiveButtonMask; // Mask to determine which button to enable for rotation
bool m_bRotateWithoutButtonDown;
};
//--------------------------------------------------------------------------------------
// Simple model viewing camera class that rotates around the object.
//--------------------------------------------------------------------------------------
class CModelViewerCamera : public CBaseCamera
{
public:
CModelViewerCamera();
// Call these from client and use Get*Matrix() to read new matrices
virtual LRESULT HandleMessages( _In_ HWND hWnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam ) override;
virtual void FrameMove( _In_ float fElapsedTime ) override;
// Functions to change behavior
virtual void SetDragRect( _In_ const RECT& rc ) override;
virtual void Reset() override;
virtual void SetViewParams( _In_ DirectX::FXMVECTOR pvEyePt, _In_ DirectX::FXMVECTOR pvLookatPt ) override;
void SetButtonMasks( _In_ int nRotateModelButtonMask = MOUSE_LEFT_BUTTON, _In_ int nZoomButtonMask = MOUSE_WHEEL,
_In_ int nRotateCameraButtonMask = MOUSE_RIGHT_BUTTON )
{
m_nRotateModelButtonMask = nRotateModelButtonMask, m_nZoomButtonMask = nZoomButtonMask;
m_nRotateCameraButtonMask = nRotateCameraButtonMask;
}
void SetAttachCameraToModel( _In_ bool bEnable = false ) { m_bAttachCameraToModel = bEnable; }
void SetWindow( _In_ int nWidth, _In_ int nHeight, _In_ float fArcballRadius=0.9f )
{
m_WorldArcBall.SetWindow( nWidth, nHeight, fArcballRadius );
m_ViewArcBall.SetWindow( nWidth, nHeight, fArcballRadius );
}
void SetRadius( _In_ float fDefaultRadius=5.0f, _In_ float fMinRadius=1.0f, _In_ float fMaxRadius=FLT_MAX )
{
m_fDefaultRadius = m_fRadius = fDefaultRadius; m_fMinRadius = fMinRadius; m_fMaxRadius = fMaxRadius;
m_bDragSinceLastUpdate = true;
}
void SetModelCenter( _In_ const DirectX::XMFLOAT3& vModelCenter ) { m_vModelCenter = vModelCenter; }
void SetLimitPitch( _In_ bool bLimitPitch ) { m_bLimitPitch = bLimitPitch; }
void SetViewQuat( _In_ DirectX::FXMVECTOR q )
{
m_ViewArcBall.SetQuatNow( q );
m_bDragSinceLastUpdate = true;
}
void SetWorldQuat( _In_ DirectX::FXMVECTOR q )
{
m_WorldArcBall.SetQuatNow( q );
m_bDragSinceLastUpdate = true;
}
// Functions to get state
DirectX::XMMATRIX GetWorldMatrix() const { return DirectX::XMLoadFloat4x4( &m_mWorld ); }
void SetWorldMatrix( _In_ DirectX::CXMMATRIX mWorld )
{
XMStoreFloat4x4( &m_mWorld, mWorld );
m_bDragSinceLastUpdate = true;
}
protected:
CD3DArcBall m_WorldArcBall;
CD3DArcBall m_ViewArcBall;
DirectX::XMFLOAT3 m_vModelCenter;
DirectX::XMFLOAT4X4 m_mModelLastRot; // Last arcball rotation matrix for model
DirectX::XMFLOAT4X4 m_mModelRot; // Rotation matrix of model
DirectX::XMFLOAT4X4 m_mWorld; // World matrix of model
int m_nRotateModelButtonMask;
int m_nZoomButtonMask;
int m_nRotateCameraButtonMask;
bool m_bAttachCameraToModel;
bool m_bLimitPitch;
bool m_bDragSinceLastUpdate; // True if mouse drag has happened since last time FrameMove is called.
float m_fRadius; // Distance from the camera to model
float m_fDefaultRadius; // Distance from the camera to model
float m_fMinRadius; // Min radius
float m_fMaxRadius; // Max radius
DirectX::XMFLOAT4X4 m_mCameraRotLast;
};
//--------------------------------------------------------------------------------------
// Manages the mesh, direction, mouse events of a directional arrow that
// rotates around a radius controlled by an arcball
//--------------------------------------------------------------------------------------
class CDXUTDirectionWidget
{
public:
CDXUTDirectionWidget();
LRESULT HandleMessages( _In_ HWND hWnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam );
HRESULT OnRender( _In_ DirectX::FXMVECTOR color, _In_ DirectX::CXMMATRIX pmView, _In_ DirectX::CXMMATRIX pmProj, _In_ DirectX::FXMVECTOR vEyePt );
DirectX::XMVECTOR GetLightDirection() const { return DirectX::XMLoadFloat3( &m_vCurrentDir ); }
void SetLightDirection( _In_ DirectX::FXMVECTOR vDir )
{
DirectX::XMStoreFloat3( &m_vCurrentDir, vDir );
m_vDefaultDir = m_vCurrentDir;
}
void SetLightDirection( _In_ DirectX::XMFLOAT3 vDir )
{
m_vDefaultDir = m_vCurrentDir = vDir;
}
void SetButtonMask( _In_ int nRotate = MOUSE_RIGHT_BUTTON ) { m_nRotateMask = nRotate; }
float GetRadius() const { return m_fRadius; }
void SetRadius( _In_ float fRadius ) { m_fRadius = fRadius; }
bool IsBeingDragged() { return m_ArcBall.IsBeingDragged(); }
static HRESULT WINAPI StaticOnD3D11CreateDevice( _In_ ID3D11Device* pd3dDevice, _In_ ID3D11DeviceContext* pd3dImmediateContext );
static void WINAPI StaticOnD3D11DestroyDevice();
protected:
HRESULT UpdateLightDir();
// TODO - need support for Direct3D 11 widget
DirectX::XMFLOAT4X4 m_mRot;
DirectX::XMFLOAT4X4 m_mRotSnapshot;
float m_fRadius;
int m_nRotateMask;
CD3DArcBall m_ArcBall;
DirectX::XMFLOAT3 m_vDefaultDir;
DirectX::XMFLOAT3 m_vCurrentDir;
DirectX::XMFLOAT4X4 m_mView;
};