ModelViewerCamera,顾名思义就是用来控制物体的简单几何变换。如果使用DXUT框架来开发,可以直接使用DXUT自带有强大的DXUTCamera类。下面这是又本人自己实现的简单ModelViewerCamera类:
SPCamera.h
#pragma once
#include "SPD3D11Prerequisite.h"
namespace SP
{
//-------------------------------------------------------------------------------------
class DLL_EXPORT SPCamera
{
public:
SPCamera();
const XMMATRIX GetViewMtx() const { return XMLoadFloat4x4(&m_mView); }
const XMMATRIX GetProjMtx() const { return XMLoadFloat4x4(&m_mProj); }
const XMMATRIX GetViewProjMtx() const { return GetViewMtx()*GetProjMtx(); }
const XMMATRIX GetViewInv() const { return XMLoadFloat4x4(&m_mViewInv); }
const XMFLOAT3& GetEyePt() { return m_vEyePt; }
const XMFLOAT3& GetLookatPt() { return m_vLookatPt; }
void SetWindow( INT nWidth, INT nHeight );
protected:
XMFLOAT4X4 m_mView;
XMFLOAT4X4 m_mProj;
XMFLOAT4X4 m_mViewInv;
XMFLOAT3 m_vEyePt;
XMFLOAT3 m_vLookatPt;
UINT m_nWidth;
UINT m_nHeight;
};
//-------------------------------------------------------------------------------------
class DLL_EXPORT SPModelViewerCamera : public SPCamera
{
public:
SPModelViewerCamera();
void Closer();
void Further();
void OnBegin( int nX, int nY );
void OnMove( int nX, int nY );
void OnEnd();
const XMMATRIX GetRotationMatrix()
{
return XMMatrixRotationQuaternion(XMLoadFloat4( &m_qNow ) );
}
bool IsBeingDragged() const
{
return m_bDrag;
}
const XMFLOAT4 GetQuatNow() const
{
return m_qNow;
}
void SetQuatNow( const XMFLOAT4* q )
{
m_qNow = *q;
}
static XMVECTOR QuatFromBallPoints( const XMVECTOR vFrom, const XMVECTOR vTo );
protected:
void Update();
XMFLOAT4X4 m_mRotation;
FLOAT m_fRadius;
FLOAT m_fEyeRadius;
XMFLOAT4 m_qDown;
XMFLOAT4 m_qNow;
bool m_bDrag;
XMFLOAT3 m_vDownPt;
XMFLOAT3 m_vCurrentPt;
XMFLOAT3 ScreenToVector( float fScreenPtX, float fScreenPtY );
};
}
SPCamera.cpp
#include "SPCamera.h"
namespace SP
{
//--------------------------------------------------------------------------------------
// SPCamera
//-------------------------------------------------------------------------------------
SPCamera::SPCamera()
{
m_vEyePt = XMFLOAT3(0.0f, 0.0f, 6.0f);
m_vLookatPt = XMFLOAT3(0.0f, 0.0f, -0.0f);
}
void SPCamera::SetWindow( INT width, INT height )
{
m_nWidth = width;
m_nHeight = height;
XMMATRIX mProjXM = XMMatrixPerspectiveFovLH( XM_PIDIV4, m_nWidth / (FLOAT)m_nHeight, 0.01f, 100.0f );
XMStoreFloat4x4( &m_mProj, mProjXM );
}
//--------------------------------------------------------------------------------------
// SPModelViewerCamera
//-------------------------------------------------------------------------------------
SPModelViewerCamera::SPModelViewerCamera()
{
XMStoreFloat4( &m_qNow, XMQuaternionIdentity() );
XMStoreFloat4( &m_qDown, XMQuaternionIdentity() );
XMStoreFloat4x4( &m_mRotation, XMMatrixIdentity() );
m_bDrag = FALSE;
m_fRadius = 1.215f;
m_vDownPt = XMFLOAT3( 0.0f, 0.0f, 1.0f );
m_vCurrentPt = XMFLOAT3( 0.0f, 0.0f, 1.0f );
m_fEyeRadius = 6.0;
OnBegin( 0 ,0 );
OnMove( 1, 0 );
OnEnd();
}
XMFLOAT3 SPModelViewerCamera::ScreenToVector(float fScreenPtX, float fScreenPtY)
{
// Scale to screen
FLOAT x = - ( fScreenPtX - m_nWidth / 2) / ( m_fRadius * m_nWidth / 2 );
FLOAT y = - ( fScreenPtY - 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 vector
return XMFLOAT3( x, y, z );
}
XMVECTOR SPModelViewerCamera::QuatFromBallPoints( const XMVECTOR vFrom, const XMVECTOR vTo )
{
if ( XMVector3Equal( vFrom, vTo ) )
return XMQuaternionIdentity();
float w = XMVectorGetX( XMVector3AngleBetweenNormals( vFrom, vTo ));
XMVECTOR vPart = XMVector3Cross( vFrom, vTo );
return XMQuaternionRotationAxis( vPart, w * 2.0f );
}
void SPModelViewerCamera::OnBegin( int nX, int nY )
{
// Only enter the drag state if the click falls
// inside the click rectangle.
if( nX >= 0 &&
nX < ( int )( m_nWidth ) &&
nY >= 0 &&
nY < ( int )( m_nHeight ) )
{
m_bDrag = true;
m_qDown = m_qNow;
m_vDownPt = ScreenToVector( ( float )nX, ( float )nY );
}
}
void SPModelViewerCamera::OnMove( int nX, int nY )
{
if ( m_bDrag )
{
m_vCurrentPt = ScreenToVector( ( float )nX, ( float )nY );
XMVECTOR qDown = XMLoadFloat4( &m_qDown );
XMVECTOR vDownPt = XMLoadFloat3( &m_vDownPt );
XMVECTOR vCurrentPt = XMLoadFloat3( &m_vCurrentPt );
qDown = XMQuaternionMultiply( qDown, QuatFromBallPoints( vDownPt, vCurrentPt ) );
XMStoreFloat4( &m_qNow, qDown );
// Simply update
Update();
}
}
void SPModelViewerCamera::OnEnd()
{
m_bDrag = false;
}
void SPModelViewerCamera::Update()
{
XMVECTOR vDeterminant;
XMMATRIX mCameraRot = GetRotationMatrix();
mCameraRot = XMMatrixInverse( &vDeterminant, mCameraRot );
XMVECTOR Eye = XMVectorSet( 0.0f, 0.0f, m_fEyeRadius, 0.0f );
XMVECTOR At = XMVectorSet( 0.0f, 0.0f, 0.0f, 0.0f );
XMVECTOR Up = XMVectorSet( 0.0f, 1000.0f, 0.0f, 0.0f );
Eye = XMVector3Transform( Eye, mCameraRot );
Up = XMVector3Transform( Up, mCameraRot );
XMMATRIX mView = XMMatrixLookAtLH( Eye, At, Up );
XMStoreFloat4x4( &m_mView, mView );
// Store EyePos
XMStoreFloat3( &m_vEyePt, Eye );
// mtxViewInv
XMMATRIX mViewInv = XMMatrixInverse( &vDeterminant, mView );
XMStoreFloat4x4( &m_mViewInv, mViewInv );
}
void SPModelViewerCamera::Closer()
{
m_fEyeRadius *= 1.1f;
Update();
}
void SPModelViewerCamera::Further()
{
m_fEyeRadius *= 0.90909f;
Update();
}
}
鼠标左键被按下时调用OnBegin(),移动时调用OnMove(),释放时调用OnEnd()。
对于3D开发者,这个算法还是相当简单的,不解释了。如果这个算法满足不了你,建议去看DXUT文档,这对你自己Camera类的壮大很有帮助。