【NoisyHeart的炼成】Part1:ModelViewerCamera


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类的壮大很有帮助。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值