CrossRoad

XRを中心とした技術ブログ。 Check also "English" category.

Babylon.jsのHavokで任意の3Dモデルに物理演算を当てて、当たったことを確認する方法

引き続きHavokについて調べています。

前回書いた通り、aggregateという関数を使うことで、オブジェクトに物理演算をかけることができます。また、PhysicsViewerを使うことで、物理演算がかかった結果を確認できます。しかし、公式ドキュメントで指定されているコード記述の方法だと、基本図形でしか確認ができません。

そこで、今回は任意の3Dモデルに対して物理演算を当てる方法と、任意の3Dモデルに物理演算が当たったかを確認する方法をまとめました。

1. 任意の3Dモデルに物理演算をあてる方法

まず、ImportMesh、ImportMeshAsync、AssetManagerなどで3Dモデルを読み込みます。読み込み後、メッシュの情報を取り出してそこにPhysicsAggregateを宣言します。

◾️メッシュの名前がわかっている時

BABYLON.SceneLoader.ImportMesh("",  "", BASE_URL2+"slider_on_desk_limes_v9.glb", scene, function (newMeshes) {

        for (let i = 0; i < newMeshes.length; i++) {
            console.log(newMeshes[i].name);     
            if (newMeshes[i].name === "desk_top") {
                new BABYLON.PhysicsAggregate(newMeshes[i], BABYLON.PhysicsShapeType.BOX, {mass: 0, friction: 0.5, restitution: 0.6}, scene);
         }
       //続く

◾️メッシュの先頭の文字列がわかっている時

この場合、Eraser01、Eraser02、Eraser03など、"Eraser"で始まるすべてのメッシュにPhysicsAggregateを実行します。

BABYLON.SceneLoader.ImportMesh("",  "", BASE_URL2+"slider_on_desk_limes_v9.glb", scene, function (newMeshes) {

        for (let i = 0; i < newMeshes.length; i++) {
   if(newMeshes[i].name.startsWith("Eraser")){
               // console.log("find!!");
                new BABYLON.PhysicsAggregate(newMeshes[i], BABYLON.PhysicsShapeType.BOX, {mass: 0.4, friction: 0.5, restitution: 0.6}, scene);                
            }
       //続く

2. 物理演算が当たったことを確認する方法

公式ドキュメントでは、ソースコードで記述する方法と、Inspectorを出して虫?マークを選択、Physicsというトグルをonにする方法が紹介されています。
本来はどちらでも良いと思いますが、ソースコードで記述すると任意のメッシュに物理演算が当たっていても表示されません。

PhysicsViewer on Havok & Babylon.js

たとえば、この例は机、および机に乗っている鉛筆や消しゴムなどに物理演算をかけています。同時に机の下に試験的に表示したsphereにも物理演算を書けています。このsphereはMeshBuilder.CreateSphereで生成しました。

本来ならば机の上のオブジェクトも白い枠で表示されるはずですが、なぜかsphereしか表示されていません。

一方、全く同じソースコードでInspectorからPhysics表示を有効にした場合です。

Physics option in Inspector on Babylon.js

このように、PhysicsAggregateをかけたオブジェクトが全て白い枠表示されます。

3. Tips

3.1 PhysicsAggregateでmassを0にすると、空間内の振動などの影響を受けない

前回書きましたが、PhysicsAggregateでは物理演算をかける対象の3Dモデルの質量も指定できます。ここで質量を0にすると揺れたり落下したりがなくなります。質量が0より上の場合、他のオブジェクトが倒れた時の振動とか、そういうものでずれたり動いたりすることがあります。

以下の例では、"board"という将棋板の上部分の質量(mass)を1にしたため、鉛筆が倒れるときの力によってずれてしまっています。

An example of mass 1 on Havok & Babylon.js

massを0にすると、鉛筆が倒れるなどの力の影響を受けないので位置ずれしなくなります。

An example of mass 0 on Havok & Babylon.js

3.2 3Dモデルによっては、正しく物理演算がかからないことがある

これはよくわからないままなのですが、3DモデルをBabylon.jsのPlaygroundで作り (例:MeshBuilder)、Playgroundからglbエクスポートして、その結果を再度使う時、一部の3Dモデルで物理演算が効きませんでした。

解決できなかったので、最終的に同じ3DモデルをBlenderで作り直してからglb出力すると、物理演算が当たるようになりました。

たとえば、この例では消しゴムにPhysicsAggregateをかけているのになぜか白い枠が表示されていません。

Couldn't attach PhysicsAggregate for part of mesh with unknown issues

ただし、ソースコードは同じままで、消しゴムをBlenderで作り直した場合は物理演算が当たりました。

3.3 複数のマテリアルを持った3Dモデルは物理演算がかからない可能性がある

複数のマテリアルをもっている3DモデルをBlenderでglbエクスポートしてBabylon.jsで使用すると、個別のメッシュのように表示されます。

このモデルに対してPhysicsAggregateを当てても、当てられませんでした。

たとえば、この例では本にPhysicsAggregateをかけているのになぜか白い枠が表示されていません。

Couldn't attach PhysicsAggregate for part of mesh that contains multiple meshes

Blenderでこの本モデルを開くとマテリアルが6つに分かれていました。当初はこれをそのままglb出力したところ、6つのメッシュとして表示されてしましまいました。

当初はこのようにして、bookから始まる文字列のメッシュに対してPhysicsAggregateを当てるという処理を書いていたのですが、これだと6つのメッシュのどれにも当たりませんでした。

BABYLON.SceneLoader.ImportMesh("",  "", BASE_URL2+"slider_on_desk_limes_v9.glb", scene, function (newMeshes) {

        for (let i = 0; i < newMeshes.length; i++) {
   if(newMeshes[i].name.startsWith("book")){
               // console.log("find!!");
                new BABYLON.PhysicsAggregate(newMeshes[i], BABYLON.PhysicsShapeType.BOX, {mass: 0.4, friction: 0.5, restitution: 0.6}, scene);                
            }
       //続く

最終的に、bookモデルを1つのマテリアルでまとめてから出力したところ、ちゃんと物理演算が当たるようになりました。

4. おわりに

Tipsの辺りは他の解決策があるのかもしれませんが、ひとまず現時点でわかる解決策を書きました。