各処理の作成(1/2)
アクターの登録
アクターとは、PhysXにおいて物理演算の対象になるオブジェクトのことを指します。今回のサンプルのアクターは、地面の平面、球とボックスの3つです。
地面(平面)
HRESULT InitGround() { HRESULT hr = S_OK; // 地面のセット NxPlaneShapeDesc PlaneDesc; NxActorDesc ActorDesc; ActorDesc.shapes.pushBack( &PlaneDesc ); g_pScene->createActor(ActorDesc); // 摩擦関連 NxMaterial * defaultMaterial = g_pScene->getMaterialFromIndex(0); defaultMaterial->setRestitution(0.0f); // 反発係数 defaultMaterial->setStaticFriction(0.5f); // 静止摩擦係数 defaultMaterial->setDynamicFriction(0.5f); // 動摩擦係数 return hr; }
アクター登録の例の最初は平面のセットです。
今回は、地面を真っ平らな平面とします。NxPlaneShapeDesc
が平面のための構造体で、今回はデフォルト値でそのままセットします。createActor
することでシーンにアクターが登録されます。
Nx~ShapeDesc系のクラスは、PhysXでは形状を扱うためのもので、ここではPlaneなので平面ということになります。
NxActorDesc
はアクタークラスです。アクター自体の形状は、球やボックス、平面、カプセルなどさまざまなものがあります。この形状は衝突判定を行うための形状になりますので、単純な形状ほど計算の負荷は小さくなります。ActorDesc.shapes.pushBack()
関数では、アクターに衝突形状を登録します。
摩擦関連というコメントの下は、このアクターの摩擦に関する係数のセットと衝突時の反発に関するパラメータのセットをしています。
球
VOID CreateSphere( const NxVec3* vPosition, const int nSize, const NxVec3* vVelocity ) { NxBodyDesc BodyDesc; BodyDesc.linearVelocity = *vVelocity; // 球型 NxSphereShapeDesc SphereDesc; SphereDesc.radius = nSize; // 半径 NxActorDesc ActorDesc; ActorDesc.shapes.pushBack( &SphereDesc ); ActorDesc.body = &BodyDesc; ActorDesc.density = 50; // 質量 ActorDesc.globalPose.t = *vPosition; // 初期位置 ActorDesc.flags = 0; g_pScene->createActor( ActorDesc )->userData = (void*)nSize; // int numActors = g_pScene->getNbActors(); NxMaterial * defaultMaterial = g_pScene->getMaterialFromIndex((NxMaterialIndex)numActors); defaultMaterial->setRestitution( 0.6 ); // 反発係数 defaultMaterial->setStaticFriction(0.5f); // 静運動摩擦 defaultMaterial->setDynamicFriction(0.5f); // 動運動摩擦 }
球や後述のボックスは、今回のように単純に球やボックスのモデルのための衝突形状として使うだけでなく、メッシュの境界球や境界ボックスでの衝突でよく使う形状です。衝突応答の精度は悪くなりますが、計算負荷が高く、PhysXハードウェアで高速に演算できる基本的な形状だと思います。
球は、NxSphereShapeDesc
が形状を決めるクラスになります。球の場合は、半径を関数の引数のnSize
で指定しています。
BodyDesc
では、初速となるベクトルをセットしています。ActorDesc
では、質量や初期位置などをセットします。
ボックス
VOID CreateBox( const NxVec3* vPosition, const int nSize, const NxVec3* vVelocity ) { NxBodyDesc BodyDesc; BodyDesc.linearVelocity = *vVelocity; // 速度 // 箱型 NxBoxShapeDesc BoxDesc; // 縦、横、奥行きのサイズ指定 BoxDesc.dimensions = NxVec3( float(nSize), float(nSize), float(nSize) ); NxActorDesc ActorDesc; ActorDesc.shapes.pushBack( &BoxDesc ); ActorDesc.body = &BodyDesc; ActorDesc.density = 100; // 質量 ActorDesc.globalPose.t = *vPosition; // 初期位置 ActorDesc.flags = 0; g_pScene->createActor( ActorDesc )->userData = (void*)nSize; // int numActors = g_pScene->getNbActors(); NxMaterial * defaultMaterial = g_pScene->getMaterialFromIndex((NxMaterialIndex)numActors); defaultMaterial->setRestitution( 0.8 ); // 反発係数 defaultMaterial->setStaticFriction(0.5f); // 静運動摩擦 defaultMaterial->setDynamicFriction(0.5f); // 動運動摩擦 }
今度は、ボックスです。
ボックスでは、BoxDesc
を使います。dimensions
メンバでは、幅、高さ、奥行きでサイズ指定します。その他は、球や平面のときと同じです。
ピラミッド形に積み上げる
void SetupPyramid() { NxVec3 vVelocity(0,0,0); // ピラミッド // 最下段(1F) for(int i=-3;i<=3;i++) { for(int j=-3;j<=3;j++) { NxVec3 v; v.set(i*2,1.0,j*2); CreateBox(&v,1,&vVelocity); } } // 下から2段目(2F) for(int i=-2;i<=2;i++) { for(int j=-2;j<=2;j++) { NxVec3 v; v.set(i*2,3,j*2); CreateBox(&v,1,&vVelocity); } } // 上から2段目(3F) for(int i=-1;i<=1;i++) { for(int j=-1;j<=1;j++) { NxVec3 v; v.set(i*2,5,j*2); CreateBox(&v,1,&vVelocity); } } // 最上段(4F) NxVec3 v; v.set(0,7,0); CreateBox( &v,1,&vVelocity ); }
ここでは、ボックスをピラミッドのように配置する処理を行っています。
DXUTのUIの処理
void CALLBACK OnGUIEvent( UINT nEvent, int nControlID, CDXUTControl* pControl, void* pUserContext ) { NxVec3 v(0,15,0); // オブジェクトの発生位置 NxVec3 vVelocity(0,0,0); // オブジェクトに与える初期速度 switch( nControlID ) { case IDC_TOGGLEFULLSCREEN: DXUTToggleFullScreen(); break; case IDC_TOGGLEREF: DXUTToggleREF(); break; case IDC_CHANGEDEVICE: g_SettingsDlg.SetActive( !g_SettingsDlg.IsActive() ); break; case IDC_BUTTON_BOX: // 箱オブジェクトの生成 CreateBox( &v, 1, &vVelocity ); break; case IDC_BUTTON_SPHERE: // 球のオブジェクトの生成 // 発生位置は視点 v = NxVec3(g_Camera.GetEyePt()->x, g_Camera.GetEyePt()->y, g_Camera.GetEyePt()->z); // 注視点方向へ50の速度を持つ vVelocity = -v; vVelocity.normalize(); // 正規化 vVelocity*=50; CreateSphere( &v, 1, &vVelocity ); break; } }
ここでは、画面上の[BOX]や[SPHERE]といったボタンが押された時の処理を記述しています。
[BOX]が押された場合、世界の(0,15,0)の地点に新しいボックスが生成されます。これには速度が0なので、そのまま重力方向に落ちます。
[SPHERE]の方は、3D空間中のカメラ位置(つまり視点)から注視点(0,0,0)方向に50という速度を与えています。
毎フレームごとに呼び出す必要のある処理
void CALLBACK OnFrameMove( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext ) { // Update the camera's position based on user input g_Camera.FrameMove( fElapsedTime ); // シミュレーションの演算 if( g_pScene && !g_bPause ) g_pScene->simulate( 1.0f/(float)DXUTGetFPS() ); }
ここでは、毎フレームごとに呼び出す必要のある処理を記述しています。
g_pScene->simulate()
では、シミュレーション計算時の時間を指定します。