アクター_PhysX
Actor - 物理空間上のアクター(2006/10/04 更新)
形状を実際に物理空間に配置するときの「入れ物」がアクター(Actor)になります。
アクターは複数の形状の集まり表現されることもあります。個々のアクターはシーン上での回転行列と位置、質量密度、ユーザデータを持ちます。ユーザデータとは、任意に割り当てることができる識別用の情報です。これを割り当てることで、どの形状が目的のものか、というのをすぐに引き出せるようになります。
アクターには大きく2つの種類があり、
- 「静的(static)なオブジェクト(これは、衝突判定があっても微動だにしない)」
- 「動的(dynamic)なオブジェクト(剛体)」
に分かれます。
地面は静的なオブジェクトでY = 0で水平方向に無限に延びる平面だとすると、「NxPlaneShape」であらわされるアクターということになります。これは、重力や他のアクターの衝突で動くことはありません。
その地面よりも上空に存在する球体があるとすると、それは重力に引っ張られて自由落下します。
PhysXでの物理計算は、あくまでもゲームなどの「それらしく」見せるレベルです。 精度はそんなに高いわけではありません。 衝突がない場合は、ニュートンの万有引力の法則(F = ma)で運動します。 また、空気抵抗・浮力などの力が必要な場合は別途外力として 加えてあげる必要があります。
実際にソースを書いたほうが分かりやすいと思われますので、以下ソースでアクターを作りつつ説明を入れていきます。
Y = 0での平面(地面)を配置
シーンを作成した段階では、物理空間には何も存在していない無の状態です。ここで、とりあえず Y = 0での地面を配置します。
NxPlaneShapeDesc planeDesc; NxActorDesc actorDesc; // NxActorDescの入れ物に対して、planeDescで表される平面を格納 actorDesc.shapes.pushBack(&planeDesc); // シーンに対して、指定のアクターを追加 g_pScene->createActor(actorDesc);
NxPlaneShapeDescは平面を管理するクラスです。生成段階で平面の法線(normal)と原点からの距離(d)は、以下の値が入っています。
planeDesc.normal = NxVec3(0.0f, 1.0f, 0.0f); planeDesc.d = 0.0f;
アクターに形状を格納するために「actorDesc.shapes.pushBack(&planeDesc);」を呼んでいます。1アクターに複数の形状を割り当てる場合は、このpushBackにて複数回の登録を行うことになります。
そして、情報が入ったNxActorDescを「g_pScene->createActor(actorDesc);」としてシーンに配置します。この手順で球体や直方体も登録できます。ここで使用した「NxPlaneShapeDesc」「NxActorDesc」は、あくまでも物理空間に形状を登録するための入れ物にすぎず、一時的な情報、ということを覚えておいてください。
ただし、このやり方での登録は「静的なオブジェクト」となり、衝突判定は行われますが、このアクターが動くことはありません。動的なアクターは「ボディー」という概念と関連づける必要があります。
動的な球体を配置
NxActorDesc actorDesc; NxBodyDesc bodyDesc; NxSphereShapeDesc sphereDesc; // デフォルト値で初期化(クリア) actorDesc.setToDefault(); // アクターの一時的な入れ物を初期化 bodyDesc.setToDefault(); // ボディーの一時的な入れ物を初期化 sphereDesc.setToDefault(); // 球形状の一時的な入れ物を初期化 bodyDesc.angularDamping = 0.5f; // 回転による減衰係数 sphereDesc.radius = 1.0f; // 球の半径(m)を指定 actorDesc.shapes.pushBack(&sphereDesc); // 球をアクターに追加 actorDesc.body = &bodyDesc; // ここで動的情報を指定 actorDesc.density = 0.01f; // 質量密度(kg/m^3) actorDesc.globalPose.t = NxVec3(0.0f, 10.0f, 0.0f); // シーン上の位置 // シーンに対して、指定のアクターを追加 g_pScene->createActor(actorDesc);
actorDesc / bodyDesc / sphereDescで「setToDefault();」を指定している箇所があります。これは、宣言したactorDesc(NxActorDesc)またはbodyDesc(NxBodyDesc)、sphereDesc(NxSphereShapeDesc)を使い回す場合の内容の初期化処理です。これらはシーンにアクターを登録するための入れ物にすぎない、と前章で書いた通り、一時的なものです。ですので、使い回す場面も多いかと思います。「actorDesc.shapes.pushBack(&xxx);」みたいにした場合は、内部的には配列が拡張されることになりますので、クリアせずにそのまま使用すると不正な結果になります。ですので、改めて再利用する場合は「setToDefault();」にて一度クリアしてから使うことをおすすめします。
bodyDesc.angularDampingは、回転による減衰の係数を指定します。デフォルトは0.05fになっています。そのほかに以下のようなパラメータを指定できます。「ボディー」情報は、動的な物理運動をする場合のパラメータを持つと思ってもいいかもしれません。
変数名 | 型の種類 | 内容 |
---|---|---|
massLocalPose | NxMat34 | 質量の中心での回転と位置を表す3x4行列 |
mass | NxReal | 質量(kg)。デフォルトは0.0。 |
linearVelocity | NxVec3 | 線形な速度(m/s)。初速度はこれに値する。デフォルトはゼロのベクトル |
angularVelocity | NxVec3 | 角速度(単位?)。デフォルトはゼロのベクトル。 |
linearDamping | NxReal | 移動による減衰係数。デフォルトは0.0。 |
angularDamping | NxReal | 回転による減衰係数デフォルトは0.05。 |
ほかいろいろパラメータがありますが、すみません、まだ掘っていません。というように、ボディー情報はNxBodyDescのメンバに格納していきます。
次に、NxSphereShapeDescクラスにて球の形状情報を指定します。球の場合は球の半径しか情報を持ちませんので「sphereDesc.radius = 1.0f;」のように球の半径をm単位で指定します。
「actorDesc.shapes.pushBack(&sphereDesc);」にて、球をアクターの入れ物に一時的に格納します。この格納したアクターに対して、パラメータを指定していきます。
「actorDesc.body = &bodyDesc;」にて、アクターにボディー情報クラスのポインタを指定します。これがNULLの場合は、このアクターは静的となります。指定がある場合は動的なアクターになります。
「actorDesc.density = 1.0f;」にて、質量密度(kg/m^3)を指定します。これを指定することで、形状内は一定の密度で満たされているとして自動的に質量が計算されます。「質量 = 質量密度 x 体積」で球や直方体の場合は簡単に計算できるのですが、複雑な計算の場合は面倒なのでSDKに任せてしまいます。
「actorDesc.globalPose.t = NxVec3(0.0f, 10.0f, 0.0f);」は、シーン上でのアクターの中心位置を指定しています。この場合は、地面から10.0m上空の位置に球を配置することになります。「actorDesc.globalPose」はNxMat34で表される3x4行列ですので、当然ながら回転も加えることができます。行列については「数学関数」を参照してください。
そして最後「g_pScene->createActor(actorDesc);」とすることで、actorDescであらわされるアクターをシーンに配置しています。
球体・直方体・カプセル・平面で衝突判定を行う場合は、こんな感じで静的・動的なアクターとして物理空間に配置していきます。もっと複雑な例えばトライアングルメッシュの場合は、もう少し手順が増えることになります。
動的なボックスを配置
同様にボックスを配置します。基本は「アクター」という単位で、そこに形状を割り当てることになります。球を配置する場合とほとんど差はありません。
NxActorDesc actorDesc; NxVec3 v; NxBodyDesc bodyDesc; NxBoxShapeDesc boxDesc; NxActor *pActor; actorDesc.setToDefault(); bodyDesc.setToDefault(); boxDesc.setToDefault(); bodyDesc.angularDamping = 0.5f; // ボックスのXYZ方向のサイズの半分を指定する // この場合は、x = 1.0f * 2.0f // y = 2.0f * 2.0f // z = 3.0f * 2.0f // がボックスのサイズになる。 boxDesc.dimensions = NxVec3(1.0f, 2.0f, 3.0f); actorDesc.shapes.pushBack(&boxDesc); actorDesc.body = &bodyDesc; actorDesc.density = 0.01f; // 質量密度(kg / m^3) actorDesc.globalPose.t = NxVec3(0.0f, 10.0f, 0.0f); // シーン上の位置 pActor = g_pScene->createActor(actorDesc);
「NxBoxShapeDesc」の使用が唯一球と違うところです。boxDesc.dimensionsにてNxVec3型でボックスのXYZ方向のサイズを半分にした値を指定します。「actorDesc.shapes.pushBack(&boxDesc);」にて引数にボックスのクラスを渡します。createActor関数が成功したかどうかは、戻り値のNxActorのポインタを調べてNULLかどうか見るといいです。
NxScene::createActorの戻り値について
処理に成功すればNULL以外の値が返されます。失敗した場合はNULLが返されます。では、失敗する可能性があるのはどのようなときでしょうか?
引数として渡すNxActorDescの内容が不正あればNULLが返ります。ほかに、ハードウェア(PPU)の上限以上のアクターを作成しようとした場合にNULLが返されるようです。PhysX SDKのドキュメントでは、PPUの場合は「Limits on numbers and types of actors」とのことでアクターの数や種類により上限は変わるみたいですね。
登録したアクターを探す
アクターの登録は一時的な情報を作成してシーンに突っ込む、といった流れになります。実際は「じゃあ、どのアクターがどの位置・回転を持つのか」というのが分からないと描画すらできません。これらはシーン上のアクターの数およびそのアクター情報へのポインタを取得することで、情報取得を行うことができます。
アクターの数を取得
int actorCount; actorCount = g_pScene->getNbActors();
「g_pScene->getNbActors();」とすることにより、シーン上のすべてのアクター数を取得します。ここでは、静的・動的なアクターもすべて含まれます。
アクター情報の取得
int actorCount, loop; NxActor **ppActors; NxActor *pActor; NxVec3 vPos, vVelocity; NxMat34 m; // アクター数を取得 actorCount = g_pScene->getNbActors(); // アクター情報へのダブルポインタを取得 ppActors = g_pScene->getActors(); for(loop = 0; loop < actorCount; loop++) { pActor = *ppActors++; // シーン上でのアクターの回転と位置を示す行列 m = pActor->getGlobalPose(); // シーン上でのアクターの位置 vPos = pActor->getGlobalPose().t; // アクターが動的かどうか if(pActor->isDynamic()) { // 動的である場合 } // 速度(m/s) vVelocity = pActor->getLinearVelocity(); }
「g_pScene->getActors();」にて、アクター情報へのダブルポインタを取得して、先ほど取得したアクター数によりループで回していきます。剛体アニメーションでは回転と位置を表す行列さえ取得できれば動きは表現できますので、「pActor->getGlobalPose();」で取得したNxMat34の行列が重要となります。
また、getができるのならばsetもできるわけで、シーンから取得したNxActorから位置・回転行列・速度・外力(Force)、などを指定することも可能です。外力は空気抵抗や浮力を加える場合に利用できます。
シーンから指定のアクターを削除する
不要になったアクターは「g_pScene->releaseActor(*pActor);」にて削除することができます。時間がたって形状が消える場合や、衝突時に形状を消したい場合、アクターをシーンから除外します。
int actorCount; NxActor **ppActors; actorCount = g_pScene->getNbActors(); ppActors = g_pScene->getActors(); if(actorCount >= 1) { g_pScene->releaseActor(*ppActors[0]); }
この場合は、一番先頭に登録されているアクターを削除しています。なお「g_pScene->getActors();」で取得できるアクターのダブルポインタはその都度変わりますので、一度何かしらのアクションを起こした場合は(シミュレーションステップを呼んだ場合、アクターを削除した場合)再度取得し直すようにしてほうが安全かと思われます。
ここではさらっとですがアクターについての説明を行いました。さらに詳しい情報(任意のユーザデータの関連づけなど)は、別途ページにて記載していきます。
Future's Laboratory 技術格納庫 2004-2013 Yutaka Yoshisaka.