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

■PhysXで回転ジョイント Prev  Top  Next
関連ページ:PhysX リファレンス


サンプル画像

今回は回転軸ジョイントを設定して回転方向に制限をつけます。ついでに色々やりますが、ソース見ながら解説していきます。

今回のサンプルでは前回と同様に2つのボックスをジョイントで連結するプログラムを作成します。

---PhysX.cpp---


BOOL InitNxPhysX( NxPhysicsSDK** pPhysicsSDK, NxScene** pScene, DEBUG_RENDERER** pDebugRender )
{
      // 省略

      // デバッガに表示する内容 DebugRender.cppを追加して使用する
      (*pPhysicsSDK)->setParameter(NX_VISUALIZATION_SCALE, 2);             //アクターの3軸の表示上のスケール値
      (*pPhysicsSDK)->setParameter(NX_VISUALIZE_COLLISION_SHAPES, true);   //衝突判定の対象となってる形状(0で非表示)
      (*pPhysicsSDK)->setParameter(NX_VISUALIZE_ACTOR_AXES, true);         //アクターの3軸の表示(0で非表示)
      (*pPhysicsSDK)->setParameter(NX_VISUALIZE_JOINT_LIMITS, true);       //ジョイントの回転可能範囲

      // DebugRender
      (*pDebugRender) = new DEBUG_RENDERER;

      // 省略
}

ジョイントの回転可能範囲をデバッガに表示できるようにします。 InitNxPhysX 関数を修正します。 既出の関数なので部分的に紹介します。全部見たい方はPhysXをとりあえず使ってみるを参照してください。

---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_pPlaneMesh = NULL;
static TCHAR*                m_pPlaneMeshName = _T("Plane");
static NxActor*              m_pActorPlane = NULL;

//箱モデル
static LPD3DXMESH            m_pBoxMesh = NULL;
static TCHAR*                m_pBoxName = _T("Box");
static NxActor*              m_pActorBox0 = NULL;   // ジョイントで連結するアクター
static NxActor*              m_pActorBox1 = NULL;   // ジョイントで連結するアクター
static float                 m_Scale0 = 0.8f;       // モデルのスケーリング値
static float                 m_Scale1 = 1.0f;       // モデルのスケーリング値

//ジョイント
static NxJoint*              m_pJoint0 = NULL;
static NxJoint*              m_pJoint1 = NULL;

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("Plane.x"), D3DXMESH_MANAGED, m_pd3dDevice, NULL, NULL, NULL, NULL, &m_pPlaneMesh );
   D3DXLoadMeshFromX( _T("res\\Box.x"), D3DXMESH_MANAGED, m_pd3dDevice, NULL, NULL, NULL, NULL, &m_pBoxMesh );

   //固定機能パイプラインライティングを設定する
   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;
   }

   // マテリアル関連
   NxMaterial * defaultMaterial = m_pNxScene->getMaterialFromIndex(0);
   defaultMaterial->setRestitution(0.5f);      // 反発係数
   defaultMaterial->setDynamicFriction(0.5f);  // 動摩擦係数
   defaultMaterial->setStaticFriction(0.5f);   // 静止摩擦係数

   NxActorDesc actorDesc;

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

   // 初期化
   actorDesc.setToDefault();

   // 地面のアクターを作成する
   NxPlaneShapeDesc planeDesc;

   // 初期化
   planeDesc.setToDefault();

   // 原点からの距離. 法線ベクトルと一緒に設定し、地面の位置を決定する
   planeDesc.d = -10.0f;

   // アクターにシェイプを追加
   actorDesc.shapes.pushBack(&planeDesc);

   //アクターに名称を設定する
   actorDesc.name = m_pPlaneMeshName;

   // シーンにアクターを追加
   m_pActorPlane = m_pNxScene->createActor(actorDesc);

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

   NxBoxShapeDesc boxDesc;
   NxBodyDesc bodyDesc;
   NxVec3 BoxPos = NxVec3( 0, 5, 40 );
   NxVec3 BoxSize = NxVec3( 1, 4, 1 );

   //箱1
   actorDesc.setToDefault();
   boxDesc.setToDefault();
   // 箱の大きさ
   boxDesc.dimensions = BoxSize * m_Scale0;
   //スケーリング値
   actorDesc.userData = &m_Scale0;
   // ローカル座標系上でのシェイプの位置
   boxDesc.localPose.t = NxVec3( 0, 0, 0 );
   // アクターにシェイプを追加
   actorDesc.shapes.pushBack( &boxDesc );
   // 動的な衝突判定用モデルの設定
   bodyDesc.setToDefault();
   bodyDesc.angularDamping = 0.5f;   // 回転による減衰係数
   // ボディに設定する. なおNxScene::createActorをコールした時点でコピーされるっぽいので NxBodyDesc の内容を保持しておく必要はない
   actorDesc.body          = &bodyDesc;
   // 質量密度
   actorDesc.density = 5.0f;
   actorDesc.name = m_pBoxName;
   // シーン上の位置
   actorDesc.globalPose.t  = BoxPos;
   // シーンにアクターを追加
   m_pActorBox0 = m_pNxScene->createActor(actorDesc);

   //箱2
   actorDesc.setToDefault();
   boxDesc.setToDefault();
   // 箱の大きさ
   boxDesc.dimensions = BoxSize * m_Scale1;
   //スケーリング値
   actorDesc.userData = &m_Scale1;
   // ローカル座標系上でのシェイプの位置
   boxDesc.localPose.t = NxVec3( 0, 0, 0 );
   // アクターにシェイプを追加
   actorDesc.shapes.pushBack( &boxDesc );
   // 動的な衝突判定用モデルの設定
   bodyDesc.setToDefault();
   bodyDesc.angularDamping = 0.5f;   // 回転による減衰係数
   // ボディに設定する. なおNxScene::createActorをコールした時点でコピーされるっぽいので NxBodyDesc の内容を保持しておく必要はない
   actorDesc.body          = &bodyDesc;
   // 質量密度(ここ変更してみた)
   actorDesc.density = 10.0f;
   actorDesc.name = m_pBoxName;
   // シーン上の位置
   actorDesc.globalPose.t  = BoxPos;
   // シーンにアクターを追加
   m_pActorBox1 = m_pNxScene->createActor(actorDesc);


   //****************************************************************************
   // ジョイント
   //****************************************************************************

   NxRevoluteJointDesc  revoluteDesc;
   revoluteDesc.setToDefault();
   //ワールド空間に固定するときはNULLを設定(注意1)
   revoluteDesc.actor[0] = NULL;
   revoluteDesc.actor[1] = m_pActorBox0;
   //ワールド空間に固定するのでワールド座標系上での座標を設定する
   revoluteDesc.localAnchor[0] = NxVec3( BoxPos.x, BoxPos.y + BoxSize.y * m_Scale0, BoxPos.z );
   //アンカーポイントをローカル座標系で設定
   revoluteDesc.localAnchor[1] = NxVec3( 0, BoxSize.y * m_Scale0, 0 );
//   //回転軸をローカル座標系で設定(注意2)
//   revoluteDesc.localAxis[0] = NxVec3( 0, 0, 1 );
//   revoluteDesc.localAxis[1] = NxVec3( 0, 0, 1 );
   //回転軸をワールド座標系で設定.単位ベクトル.
   revoluteDesc.setGlobalAxis( NxVec3( 0, 0, 1 ) );
   //回転可能角度にリミットを設定する(注意3)
   revoluteDesc.flags |= NX_RJF_LIMIT_ENABLED;
   revoluteDesc.limit.high.value = 0.25f*NxPi;
   revoluteDesc.limit.high.restitution = 0.9f;    //???
   revoluteDesc.limit.low.value = -0.5f*NxPi;
   revoluteDesc.limit.low.restitution = 0.9f;     //???
   // ジョイントを作成する
   m_pJoint0 = m_pNxScene->createJoint( revoluteDesc );

   revoluteDesc.setToDefault();
   //連結する2つのアクターを設定
   revoluteDesc.actor[0] = m_pActorBox0;
   revoluteDesc.actor[1] = m_pActorBox1;
   //アンカーポイントをローカル座標系で設定
   revoluteDesc.localAnchor[0] = NxVec3( 0, -BoxSize.y * m_Scale0, 0 );
   revoluteDesc.localAnchor[1] = NxVec3( 0,  BoxSize.y * m_Scale1, 0 );
//   //回転軸をローカル座標系で設定(注意2)
//   revoluteDesc.localAxis[0] = NxVec3( 0, 0, 1 );
//   revoluteDesc.localAxis[1] = NxVec3( 0, 0, 1 );
   //回転軸をワールド座標系で設定.単位ベクトル.
   revoluteDesc.setGlobalAxis( NxVec3( 1, 0, 0 ) );


   ::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_KEYDOWN:
      // U
      if( wParam == 0x55 )
         m_pActorBox1->addForce( NxVec3( 0, 30000, 0 ) );
      if( wParam == VK_UP )
         m_pActorBox1->addForce( NxVec3( 0, 0, 15000 ) );
      if( wParam == VK_RIGHT )
         m_pActorBox1->addForce( NxVec3( 15000, 0, 0 ) );
      if( wParam == VK_LEFT )
         m_pActorBox1->addForce( NxVec3( -15000, 0, 0 ) );
      if( wParam == VK_DOWN )
         m_pActorBox1->addForce( NxVec3( 0, 0, -15000 ) );
      break;

   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 )
         {
            NxU32 actorCount = m_pNxScene->getNbActors();
            for( NxU32 i=0; i<actorCount; i++ )
            {
               NxActor* actor = m_pNxScene->getActors()[i];
               m_pNxScene->releaseActor( *actor );
            }
         
            m_pNxPhysicsSDK->releaseScene( *m_pNxScene );
            m_pNxScene = NULL;
         }

         NxReleasePhysicsSDK( m_pNxPhysicsSDK );
         m_pNxPhysicsSDK = NULL;
      }

      //Direct3D
      SafeRelease( m_pBoxMesh );
      SafeRelease( m_pPlaneMesh );
      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, matTranslate, matScale, 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, 1500.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メッシュをレンダリング
      //****************************************************************************

      m_pd3dDevice->SetRenderState( D3DRS_LIGHTING, TRUE );

      D3DMATERIAL9 Material;

      // アクター数を取得
      NxU32 actorCount = m_pNxScene->getNbActors();
      for( NxU32 i=0; i<actorCount; i++ )
      {
         //シーンに登録されているアクターのポインタを取得する
         NxActor* actor = m_pNxScene->getActors()[i];

         //アクターに登録されているシェイプ数を取得
         NxU32 shapeCount = actor->getNbShapes();

         //アクターの名称を取得する
         const TCHAR* name = actor->getName();

         if( name != NULL )
         {
            // 箱
            if( _tcscmp( name, m_pBoxName ) == 0 )
            {
               for( NxU32 j=0; j<shapeCount; j++ )
               {
                  //スケーリング
                  D3DXMatrixIdentity( &matScale );
                  float* s = (float*)actor->userData;
                  matScale._11 = *s;
                  matScale._22 = *s;
                  matScale._33 = *s;

                  //平行移動
                  //DirectXと異なりPhysXは右手座標系であることに注意する。               
                  NxF32 m[16];
                  //単位行列
                  ZeroMemory( m, sizeof( D3DXMATRIXA16 ) );
                  m[0] = m[5] = m[10] = m[15] = 1.0f;
                  actor->getGlobalPose().getColumnMajor44( m );
                  CopyMemory( &matTranslate, m, sizeof( D3DXMATRIXA16 ) );
                  matWorld = matScale * matTranslate;
                  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 );

                  m_pBoxMesh->DrawSubset(0);
               }
            }

            //地面メッシュ
            else if( _tcscmp( name, m_pPlaneMeshName ) == 0 )
            {
               for( NxU32 j=0; j<shapeCount; j++ )
               {
                  NxShape* shape = actor->getShapes()[j];

                  D3DXMatrixIdentity( &matWorld );

                  //平行移動情報を取得し、Direct3D側に設定する
                  NxPlane plane = shape->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_pPlaneMesh->DrawSubset(0);
               }
            }
         }
      }


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

      m_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE );

      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;
}

前回紹介した球面ジョイントでは NxSphericalJointDesc クラスを使用してジョイントを設定しましたが、 今回は回転軸により制限をつけるため NxRevoluteJointDesc クラスを使用します。

(注意1) アクターのポインタに NULL を設定した場合、アンカーポイントをワールド空間上に固定します。

(注意2) NxRevoluteJointDesc::localAxis 変数にローカル座標系で回転軸を設定すると、 期待通りの動きをしないので NxRevoluteJointDesc::setGlobalAxis メソッドを使用してワールド座標系で設定しました。 回転軸の設定なのでワールド座標系での設定で問題ないと思うが、イマイチわからん。

(注意3) 回転角度に制限をつけます。角度はラジアン単位で、右手座標系で設定します。 NxRevoluteJointDesc::limit::high::value には最大限界値、 NxRevoluteJointDesc::limit::low::value には最小限界値をそれぞれ設定します。
NxRevoluteJointDesc::limit::high::restitutionNxRevoluteJointDesc::limit::low::restitution の使用方法はよくわかりません。


Prev  Top  Next
inserted by FC2 system