クォータニオンカメラ
入門書にクォータニオンのことは書いてあるけど、実際にカメラに使うときにどうしたらいいのという声を聞くので、とりあえずのっけます。改造してください。もちろん、修正BSDライセンスなんで、お気軽にご利用ください。
まずは、DirectInput等、Direct3D以外のものを使わない設定にしてから吐き出し、ティーポットが表示されることとを確認しておいてください。次に、Direct3D9 の AppWizard から吐き出される CMyD3DApplication::FrameMove()を、以下のように書き換えてください。事前に置換前と置換後で動作はあまり変わらないと思いますが、置換前はワールド行列を書き換えていたのに対し、置換後はビュー行列を書き換えています。これがでかい!
ちなみに、関数の中にドデンとたたずむ QuatCam がクォータニオンカメラクラスです。
4/22
あ、boost/assert.hpp のインクルードが必要です。BOOST_ASSERT を使わないのであれば、必要ありません。
HRESULT CMyD3DApplication::FrameMove() { // TODO: update world // Update user input state UpdateInput( &m_UserInput ); // Update the world state according to user input /* D3DXMATRIX matWorld; D3DXMATRIX matRotY; D3DXMATRIX matRotX; /* if( m_UserInput.bRotateLeft && !m_UserInput.bRotateRight ) m_fWorldRotY += m_fElapsedTime; else if( m_UserInput.bRotateRight && !m_UserInput.bRotateLeft ) m_fWorldRotY -= m_fElapsedTime; if( m_UserInput.bRotateUp && !m_UserInput.bRotateDown ) m_fWorldRotX += m_fElapsedTime; else if( m_UserInput.bRotateDown && !m_UserInput.bRotateUp ) m_fWorldRotX -= m_fElapsedTime; D3DXMatrixRotationX( &matRotX, m_fWorldRotX ); D3DXMatrixRotationY( &matRotY, m_fWorldRotY ); D3DXMatrixMultiply( &matWorld, &matRotX, &matRotY );*/ D3DXMATRIX matWorld; D3DXMatrixIdentity( &matWorld ); m_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld ); // クォータニオンカメラクラス class QuatCam { typedef D3DXQUATERNION Quat; typedef D3DXMATRIX Mtx; float dist_; Quat quat_; Quat rot( const Quat& q ) { Quat conj; D3DXQuaternionConjugate( &conj, &quat_ ); return conj * q * quat_; } void rotate( float x, float y, float theta ) { Quat axis = rot( Quat( x, y, 0, 0 ) ); Quat q = sinf( theta ) * axis; q.w = cosf( theta ); quat_ *= q; D3DXQuaternionNormalize( &quat_, &quat_ ); // 保険 } public: QuatCam( float dist ) : dist_( dist ), quat_( 0, 0, 0, 1 ) { BOOST_ASSERT( dist_ > 0.0f ); } float getDist() const { return dist_; } void setDist( float dist ) { BOOST_ASSERT( dist > 0.0f ); dist_ = dist; } /// 行列を取得 Mtx calc() const { Quat right( 1, 0, 0, 0 ); Quat up( 0, 1, 0, 0 ); Quat pos( 0, 0, -dist_, 0 ); Quat conj; D3DXQuaternionConjugate( &conj, &quat_ ); #define SANDWICH( q ) q = conj * q * quat_ SANDWICH( right ); SANDWICH( up ); SANDWICH( pos ); #undef SANDWICH Quat dir = -pos; // 右手系のときは pos にする D3DXQuaternionNormalize( &dir, &dir ); Mtx result( right.x, right.y, right.z, 0.0f, up.x, up.y, up.z, 0.0f, dir.x, dir.y, dir.z, 0.0f, pos.x, pos.y, pos.z, 1.0f ); // 右手系のときは...よきにはからって D3DXMatrixInverse( &result, 0, &result ); return result; } /// 回転を加える /** * @param rotX X 軸 ( 右 ) を中心に回る回転角度 ( ラジアン ) * @param rotY Y 軸 ( 上 ) を中心に回る回転角度 ( ラジアン ) */ void rotate( float rotX, float rotY ) { rotate( 1, 0, rotX ); rotate( 0, 1, rotY ); } }; static QuatCam cam( 5.0f ); // これは手抜きです。勝手に変えてください。 float rotX = 0.0f, rotY = 0.0f; if( m_UserInput.bRotateLeft && !m_UserInput.bRotateRight ) rotY += m_fElapsedTime; else if( m_UserInput.bRotateRight && !m_UserInput.bRotateLeft ) rotY -= m_fElapsedTime; if( m_UserInput.bRotateUp && !m_UserInput.bRotateDown ) rotX += m_fElapsedTime; else if( m_UserInput.bRotateDown && !m_UserInput.bRotateUp ) rotX -= m_fElapsedTime; cam.rotate( rotX, rotY ); m_pd3dDevice->SetTransform( D3DTS_VIEW, &cam.calc() ); return S_OK; }