Microsoft Visual Studio .NET 2003
 NVIDIA PhysX SDK v2.8.3
 Microsoft DirectX 9.0 SDK (December 2004)

■PhysXから行列を取得 Prev  Top  Next
関連ページ:PhysX リファレンス

無料レンタルサーバー

サンプル2

今回はXファイル形式の3Dモデルを使用します。 前にも書いたとおり、描画処理はDirectXで処理するのでXファイルの読み込みやレンダリングは問題ないと思います。 しかし、衝突判定により3Dモデルを移動させたり回転させたりする処理はPhysX側で行うため、 PhysXから3Dモデルをレンダリングするときに使用するワールド行列を取得する必要があります。 これをやります。

---main.cpp---


//Direct3D
static LPDIRECT3D9           m_pdirect3d9    = NULL;
static LPDIRECT3DDEVICE9     m_pd3dDevice    = NULL;
static D3DPRESENT_PARAMETERS m_d3dParameters;

//PhysX
static NxPhysicsSDK*         m_pNxPhysicsSDK = NULL;
static NxScene*              m_pNxScene      = NULL;

// PhysXデバッグ用クラス
static DEBUG_RENDERER*       m_pDebugRenderer = NULL;

//箱モデル
static LPD3DXMESH            m_pMeshBox = NULL;
//箱モデルの名称
static char*                 m_pMeshNameBox = "Box";

//地面モデル
static LPD3DXMESH            m_pMeshPlane = NULL;
//地面モデルの名称
static char*                 m_pMeshNamePlane = "Plane";

static UINT nWidth = 1024;       //ウィンドウの横幅
static UINT nHeight = 768;       //ウィンドウの縦幅

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE /*hPrevInstance*/,
                     LPSTR     /*lpCmpLine*/,
                     INT       /*nCmdShow*/)
{
   HRESULT hr;
   MSG msg;
   ZeroMemory( &msg, sizeof(MSG) );
   HWND hWnd = NULL;
   BOOL Windowed = TRUE;
   TCHAR* AppName = _T("PhysX Tutrial 01");

   //****************************************************************************
   // ウィンドウ作成
   //****************************************************************************

   // 省略。。。

   //****************************************************************************
   // Direct3Dの初期化
   //****************************************************************************

   // 省略。。。


   //****************************************************************************
   // 3Dメッシュをロードする
   //****************************************************************************

   D3DXLoadMeshFromX( _T("Box.x"), D3DXMESH_MANAGED, m_pd3dDevice, NULL, NULL, NULL, NULL, &m_pMeshBox );
   D3DXLoadMeshFromX( _T("Plane.x"), D3DXMESH_MANAGED, m_pd3dDevice, NULL, NULL, NULL, NULL, &m_pMeshPlane );

   //固定機能パイプラインライティングを設定する
   D3DLIGHT9 Light;
   ZeroMemory(&Light, sizeof(D3DLIGHT9));
   Light.Type = D3DLIGHT_DIRECTIONAL;
   Light.Direction = D3DXVECTOR3( -1.0f, -1.0f, 1.0f );
   Light.Ambient   = D3DXCOLOR( 1.0f, 1.0f, 1.0f, 1.0f );
   Light.Diffuse   = D3DXCOLOR( 1.0f, 1.0f, 1.0f, 1.0f );
   Light.Specular  = D3DXCOLOR( 0.0f, 0.0f, 0.0f, 0.0f );
   m_pd3dDevice->SetLight(0, &Light);
   m_pd3dDevice->LightEnable(0, TRUE);


   //****************************************************************************
   // PhysXの初期化
   //****************************************************************************

   // この関数については「PhysXをとりあえず使ってみる」ページを参照すること
   hr = InitNxPhysX( &m_pNxPhysicsSDK, &m_pNxScene, &m_pDebugRenderer );
   if( FAILED( hr ) )
   {
      ::DestroyWindow( hWnd );
      return 0;
   }

   //****************************************************************************
   // 静的な衝突判定用オブジェクトを作成する
   //****************************************************************************

   NxActorDesc actorDesc;

   // 初期化
   actorDesc.setToDefault();

   // 地面のアクターを作成する
   NxPlaneShapeDesc planeDesc;
   // 初期化
   planeDesc.setToDefault();
   // 原点からの距離 法線ベクトルと一緒に設定し、地面の位置を決定する
   planeDesc.d = -5.0f;

   actorDesc.shapes.pushBack(&planeDesc);

   // デフォルトのマテリアルを取得する
   NxMaterial * defaultMaterial = m_pNxScene->getMaterialFromIndex(0);
   defaultMaterial->setRestitution(0.5f);      // 反発係数
   defaultMaterial->setDynamicFriction(0.5f);  // 動摩擦係数
   defaultMaterial->setStaticFriction(0.5f);   // 静止摩擦係数

   // アクターにモデルを識別するための名称を設定する
   actorDesc.name = m_pMeshNamePlane;

   // シーンに対して、指定のアクターを追加
   m_pNxScene->createActor(actorDesc);

   //****************************************************************************
   // 動的な衝突判定用オブジェクトを作成する
   //****************************************************************************

   // 初期化
   actorDesc.setToDefault();

   NxBoxShapeDesc boxDesc;
   boxDesc.setToDefault();
   //Boxの大きさを[ X, Y, X ]各辺の半分の長さで指定する
   boxDesc.dimensions = NxVec3( 2.0f, 4.0f, 2.0f );

   // この辺で動的な設定をするらしい
   NxBodyDesc bodyDesc;
   // 初期化
   bodyDesc.setToDefault();
   // 回転による減衰係数
   bodyDesc.angularDamping = 0.5f;

   // ボックスをアクターに追加
   actorDesc.shapes.pushBack(&boxDesc);
   // ここで動的情報を指定
   actorDesc.body          = &bodyDesc;
   // 質量密度
   actorDesc.density         = 0.01f;

   // ワールド空間上での位置
   actorDesc.globalPose.t  = NxVec3(0.5f, 20.0f, 30.0f);

   // ワールド空間上での回転
   float r = 10.0f;
   // 左手座標系でZ軸回転の3X3行列を作成する
   NxF32 m[] = { cosf( D3DXToRadian( r ) ) , sin( D3DXToRadian( r ) ), 0.0f,
                 -sinf( D3DXToRadian( r ) ), cos( D3DXToRadian( r ) ), 0.0f,
                 0.0f                      , 0.0f                    , 1.0f };
   // PhysX内では右手座標系なので転置行列をセットする
   actorDesc.globalPose.M.setColumnMajor( m );

   // アクターにモデルを識別するための名称を設定する
   actorDesc.name = m_pMeshNameBox;

   // シーンに対して、指定のアクターを追加
   m_pNxScene->createActor(actorDesc);


   ::ShowWindow(hWnd, SW_SHOW);
   ::UpdateWindow(hWnd);

   //****************************************************************************
   // メッセージループ
   //****************************************************************************

   do
   { 
      if( ::PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) )
      {
         ::TranslateMessage(&msg); 
         ::DispatchMessage(&msg); 
      }
      else
      {
         if( MainLoop(hWnd) == FALSE )
            ::DestroyWindow( hWnd );
      }
   }while( msg.message != WM_QUIT );

   ::UnregisterClass( AppName, hInstance );

   return msg.wParam;
}

LRESULT CALLBACK WndProc( HWND hWnd, UINT msg, UINT wParam, LONG lParam )
{
   switch (msg)
   {
   case WM_KEYUP:
      if( wParam == VK_ESCAPE )
         ::DestroyWindow( hWnd );
      break;

   // 終了処理
   case WM_DESTROY:
      //PhysX
      SafeDelete( m_pDebugRenderer );
      if( m_pNxPhysicsSDK != NULL )
      {
         if( m_pNxScene != NULL )
            m_pNxPhysicsSDK->releaseScene( *m_pNxScene );
         m_pNxScene = NULL;

         NxReleasePhysicsSDK( m_pNxPhysicsSDK );
         m_pNxPhysicsSDK = NULL;
      }

      //Direct3D
      SafeRelease( m_pMeshPlane );
      SafeRelease( m_pMeshBox );
      SafeRelease( m_pd3dDevice );
      SafeRelease( m_pdirect3d9 );
      ::PostQuitMessage(0);
      break;

   default:
      return ::DefWindowProc( hWnd, msg, wParam, lParam );
   }

   return 0L;
}

BOOL MainLoop( HWND hWnd )
{   
   D3DXMATRIX matWVP, matWorld, matView, matProj;

   //ワールド行列
   D3DXMatrixIdentity( &matWorld );
   m_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );

   //ビュー行列
   D3DXMatrixTranslation( &matView, 0.0f, 0.0f, 0.0f );
   m_pd3dDevice->SetTransform( D3DTS_VIEW, &matView );

   //遠近射影行列
   //クリップ面はアプリケーションごとに調整すること
   D3DXMatrixPerspectiveFovLH( &matProj,
                               D3DX_PI/4.0f,
                               4.0f / 3.0f,
                               0.01f, 150.0f );
   m_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );

   if( SUCCEEDED( m_pd3dDevice->BeginScene() ) )
   {
      m_pd3dDevice->Clear( 0L,
                           NULL, 
                           D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
                           0x0,
                           1.0f,
                           0L );

      //****************************************************************************
      // 3Dメッシュをレンダリング
      //****************************************************************************

      D3DMATERIAL9 Material;

      // アクター数を取得
      NxU32 actorCount = m_pNxScene->getNbActors();
      for( NxU32 i=0; i<actorCount; i++ )
      {
         NxActor* actor = m_pNxScene->getActors()[i];
         
         const char* name = actor->getName();
         if( name != NULL )
         {
            //Boxメッシュを探す
            if( strcmp( name, m_pMeshNameBox ) == 0 )
            {
               D3DXMatrixIdentity( &matWorld );

               //平行移動情報を取得
               matWorld._41 = actor->getGlobalPose().t.x;
               matWorld._42 = actor->getGlobalPose().t.y;
               matWorld._43 = actor->getGlobalPose().t.z;

               //回転情報を取得
               //DirectXと異なりPhysXは右手座標系なので転置行列を取得する
               NxF32 m[3 * 3];
               actor->getGlobalPose().M.getColumnMajor( m );
               matWorld._11 = m[0];
               matWorld._12 = m[1];
               matWorld._13 = m[2];
               matWorld._21 = m[3];
               matWorld._22 = m[4];
               matWorld._23 = m[5];
               matWorld._31 = m[6];
               matWorld._32 = m[7];
               matWorld._33 = m[8];

               m_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );

               //マテリアル設定
               ZeroMemory( &Material, sizeof( Material ) );
               Material.Ambient.r = 0.1f;
               Material.Ambient.g = 0.1f;
               Material.Ambient.b = 0.1f;
               Material.Ambient.a = 1.0f;
               Material.Diffuse.r = 0.0f;
               Material.Diffuse.g = 0.0f;
               Material.Diffuse.b = 1.0f;
               Material.Diffuse.a = 1.0f;
               m_pd3dDevice->SetMaterial( &Material );

               //Boxメッシュをレンダリングする
               m_pMeshBox->DrawSubset(0);
            }

            //地面メッシュを探す
            else if( strcmp( name, m_pMeshNamePlane ) == 0 )
            {
               D3DXMatrixIdentity( &matWorld );

               //平行移動情報を取得し、Direct3D側に設定する
               NxPlane plane = actor->getShapes()[0]->isPlane()->getPlane();
               matWorld._41 = 0.0f;
               matWorld._42 = plane.d;
               matWorld._43 = 0.0f;
               m_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );

               //マテリアル設定
               ZeroMemory( &Material, sizeof( Material ) );
               Material.Ambient.r = 0.1f;
               Material.Ambient.g = 0.1f;
               Material.Ambient.b = 0.1f;
               Material.Ambient.a = 1.0f;
               Material.Diffuse.r = 0.2f;
               Material.Diffuse.g = 1.0f;
               Material.Diffuse.b = 0.0f;
               Material.Diffuse.a = 1.0f;
               m_pd3dDevice->SetMaterial( &Material );

               //Planeメッシュをレンダリングする
               m_pMeshPlane->DrawSubset(0);
            }
         }
      }

      //****************************************************************************
      // デバッグ用のワイヤーフレームを描画
      //****************************************************************************

      if(m_pDebugRenderer)
         m_pDebugRenderer->RenderData( m_pd3dDevice, m_pNxScene->getDebugRenderable() );

      m_pd3dDevice->EndScene();

      // 1FPSの時間を指定
      // 遅延が発生した場合を考慮しないとだめだろう
      m_pNxScene->simulate( 1.0f / 60.0f );

      //よくわからんが下記の2関数をコールして実際に時間を進めるらしい
      m_pNxScene->flushStream();
      m_pNxScene->fetchResults( NX_RIGID_BODY_FINISHED, true );
   }

   m_pd3dDevice->Present( NULL, NULL, NULL, NULL );

   return TRUE;
}

今回わかったのはDirectX内では左手座標系ですが、PhysXでは右手座標系らしいことです。 右手座標系などについて詳しく知りたい方は他のサイトを参照してください。 ここでは簡単に説明します。

DirectXとPhysX両者の座標系が異なるので、互いに使用している行列をそのまま使用することはできません。必ず変換する必要があります。 といっても変換自体は簡単で転置行列を作成して、それを渡してあげるだけです。 転置行列についても他のサイトを参照してください。 さてソースをみるとsetColumnMajorやgetColumnMajorという関数を使用して互いに行列の受け渡しを行っています。 この関数内で転置行列を作成しています。 setColumnMajorのソースを見てみます。

---NxMat33.h---


NX_INLINE void NxMat33::setColumnMajor(const NxF32* d)
   {
   //we are column major, so copy transposed.
   data.s._11 = (NxReal)d[0];
   data.s._12 = (NxReal)d[3];
   data.s._13 = (NxReal)d[6];

   data.s._21 = (NxReal)d[1];
   data.s._22 = (NxReal)d[4];
   data.s._23 = (NxReal)d[7];

   data.s._31 = (NxReal)d[2];
   data.s._32 = (NxReal)d[5];
   data.s._33 = (NxReal)d[8];
   }

となっています。コメントにtransposedとあるのでこれだけでも転置行列を作成しているのはわかりますよね。


Prev  Top  Next
inserted by FC2 system