PhysXを試してみる

# 初めに
　この記事はPhysX4.1を四苦八苦しながら導入&使い方を試した記事です。他の人の為にもこの記事を書くことにしました。ビルド環境はVS2019(x64)のC++17です。

# PhysXとは
　物理演算を実行してくれるエンジンです。導入手順などは[この記事](https://qiita.com/hama1080/items/399e60f4c9c1bfd0640c "とても参考になります")を見ながらしたので、詳細は記事を見て下さい。

# 使い方
　軽い使い方は恐らく上記の記事の書かれた方がPDFとしてBoothで売られているので、それで解決するかとは思います。自分もそれを参考にしているので、著作権的にも実際のコードは載せません。
　ですがこの記事の意味がなくなりそうなので手順だけ書いています。といっても、[リファレンス](https://gameworksdocs.nvidia.com/PhysX/4.1/documentation/physxguide/Index.html#)を見れば済む気がしますが（笑）

## 初期化
1. physx::PXFoundationクラスの作成（バージョンはPX_PHYSICS_VERSIONを指定してください）
2. physx::PxPvdクラスことPVD（PhysX Visual Debugger）の作成（PVDのダウンロードは[このサイト](https://developer.nvidia.com/physx-visual-debugger)から[^0]）
3. physx::PxPvdTransportクラスの作成
4. physx::PxPvdとの接続(3で作成したクラスを使います)
5. physx::PxPhysicsクラスの作成
6. 拡張ライブラリを初期化(PxInitExtensions)
7. physx::PxDefaultCpuDispatcherクラスの作成(物理演算に使うCPUのスレッド数を指定します)
8. physx::PxSceneDescの設定
9. physx::PxSceneの作成（重力・7のクラス・physx::PxDefaultSimulationFilterShader）
10. physx::PxPvdSceneClientの作成
11. physx::PxPvdSceneClient::setScenePvdFlag関数の設定(eTRANSMIT_CONSTRAINTS, eTRANSMIT_CONTACTS, eTRANSMIT_SCENEqUERIES のフラグをtrueに設定して下さい)
12. physx::PxTolerancesScaleの設定（恐らくシーン上の単位を管理（メートルは１、センチメートルは100））
13. physx::PxCookingクラスの作成

以下はGPUで物理演算をさせる時に設定する項目です。

1. 8の時にphysx::PxCudaContextManagerDescをphysx::PxSceneDesc.cudaContextManagerに設定
2. physx::PxSceneDesc.flags |= physx::PxSceneFlag::eENABLE_GPU_DYNAMICSに設定
3. physx::PxSceneDesc.broadPhaseType = physx::PxBroadPhaseType::eGPU;に設定

[^0]: PVDは物理演算の結果を視覚的に確認する事が出来ます。そもそもPhysX自体は物理演算をするだけなので、描画は一切関与していません。なので、正しく描画出来ているかを確認するソフトが必要なわけです。

## 更新
1. physx::PxScene::simulate関数を呼ぶ（この更新頻度が高いと精度は上がりますが、負荷も上がります）
2. physx::PxScene::fetchResults関数を呼ぶ（1の関数で物理シミュレーションを開始し、この関数で結果の完了を待ちます[^1]）

[^1]: このような形にしているのは非同期で物理演算を実行するからです。

# ジオメトリ
　作成出来るジオメトリの種類です。詳細は[リファレンス](https://gameworksdocs.nvidia.com/PhysX/4.1/documentation/physxguide/Manual/Geometry.html)を見てほしいのですが、軽く説明をします。

## 球
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540257/70abfb3e-5ab9-fc8f-f79f-7330e01650a1.png)
クラスはphysx::PxSphereGeometryです。

## カプセル
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540257/fd7de3a8-6525-0a37-207d-243f57b74883.png)
クラスはphysx::PxCapsuleGeometryです。

## ボックス
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540257/da6bacac-0468-be3a-1cef-3119f6e85720.png)
クラスはphysx::PxBoxGeometryです。

## プレーン
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540257/be9b02a4-9c0d-b33f-867d-d1a3ae12ec72.png)
クラスはphysx::PxPlaneGeometryです。

## 凸メッシュ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540257/b4758769-f52f-04e8-ae1e-bd55b519a2fe.png)

そもそも凸メッシュの説明を[このサイト](http://springhead.info/doc/SprManual/main_html/sect0038.html)の言い方で言うと
> 凸メッシュとは凹みや穴を持たない多面体です． 頂点座標を指定することで自由な形を作成することができます．

になります。このオブジェクトを作成しようとして、色々と穴にハマったので作り方の一例を紹介します。

```c++:main.cpp
    namespace px = physx;

	static const px::PxVec3 convexVerts[]{ px::PxVec3(0,1,0),px::PxVec3(1,0,0),px::PxVec3(-1,0,0),px::PxVec3(0,0,1), px::PxVec3(0,0,-1) };

    physx::PxConvexMeshDesc desc;

	mesh.setToDefault(); // 初期化

	desc.points.count = 5; // 頂点の個数
	desc.points.stride = sizeof(px::PxVec3); // 頂点のバイト単位のオフセット
	desc.points.data = convexVerts; // 頂点配列の先頭のポインター
	desc.flags = px::PxConvexFlag::eCOMPUTE_CONVEX;

	assert(desc.isValid());

	px::PxDefaultMemoryOutputStream buf;
	px::PxConvexMeshCookingResult::Enum result;

	assert(cooking->cookConvexMesh(desc, buf, &result));
	assert(result == px::PxConvexMeshCookingResult::Enum::eSUCCESS);

	px::PxDefaultMemoryInputData input(buf.getData(), buf.getSize());
	px::PxConvexMesh* convexMesh{ physics->createConvexMesh(input) };
    assert(convexMesh);
	px::PxConvexMeshGeometry mesh{};

	mesh.convexMesh = convexMesh;

	px::PxShape* mesh_shape{ physics->createShape(mesh, physics->createMaterial(0.5f, 0.5f, 0.5f)) };
	px::PxRigidDynamic* rigid_static
    { px::PxCreateDynamic(*physics, physx::PxTransform(physx::PxIdentity), *mesh_shape, 10.f) };

	scene->addActor(*rigid_static);
```
リファレンスをみていると``aConvexActor``ってなに？ となったので、直ぐに出来なかったり...。
デモにあるはずの関数がなかったりして、色々と苦労しました。

## 三角形メッシュ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540257/2fa7dbfc-2507-824d-49bc-607077b7705e.png)
　[三角形メッシュ](https://gigazine.net/news/20190111-why-game-tiny-triangle/)と分かりにくい言い方ですが、.Fbxや.Objなどのファイルを読み込んで物理演算をさせる時に使うのがこのメッシュです。
これも同じく苦労したので、作り方を紹介します。というより作り方が凸メッシュと非常に似ているのですが（笑）

```c++:main.cpp
    namespace px = physx;

    physx::PxTriangleMeshDesc desc;

    desc.setToDefault(); // 初期化

	desc.points.data = /*頂点配列の先頭へのポインター*/;
	desc.points.count = /*頂点の個数*/;
	desc.points.stride = /*頂点のバイト単位のオフセット*/;

	desc.triangles.data = /*三角形配列の先頭へのポインター*/;
	desc.triangles.count = /*三角形の個数*/;
	desc.triangles.stride = /*三角形のバイト単位のオフセット*/;

	desc.flags = px::PxMeshFlags();

	assert(desc.isValid());

	px::PxDefaultMemoryOutputStream write_buffer{};
	bool status{ cooking->cookTriangleMesh(desc, write_buffer) };
	assert(status);

	px::PxDefaultMemoryInputData read_buffer(write_buffer.getData(), write_buffer.getSize());
	px::PxTriangleMesh* triangle_mesh{ physics->createTriangleMesh(read_buffer) };
	assert(triangle_mesh);

	px::PxTriangleMeshGeometry mesh{};

	mesh.triangleMesh = triangle_mesh;

	px::PxShape* mesh_shape{ physics->createShape(mesh, physics->createMaterial(0.5f, 0.5f, 0.5f)) };
	px::PxRigidStatic* rigid_static{ px::PxCreateStatic(*physics, physx::PxTransform(physx::PxIdentity), *mesh_shape) };

	scene->addActor(*rigid_static);
```

## ハイトフィールド
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540257/33fd22d3-e2c6-90b1-df7a-cbaf42255e74.png)
そもそもハイトフィールドの説明を[このサイト](http://www.arch.oita-u.ac.jp/a-kse/povjp/povjp/html/pov35ref_object1c.html)の言い方で言うと

> ハイト・フィールドは非常に多くの小さな３角形によって凹凸のある面を表現するものである。 凹凸の形状は濃淡画像（またはパレット・インデックス）を高さに変換することによって生成される。 Paint Shopなどの画像ソフトを使って、地形図を等高線で色分けして実際の地形を表現するといった使い方もできる。

になります。詳細は[リファレンス](https://gameworksdocs.nvidia.com/PhysX/4.1/documentation/physxguide/Manual/Geometry.html#height-fields)を見て下さい。[^2]

[^2]: 実際に使うことになったらコードを記述したいと思います。

# アクターと物理演算
　PhysXで扱うオブジェクトの事をカッコつけて「アクター」と呼んでいます。

## 動的・静的なアクター
　この欄はUnityなどを触っている方なら、分かり易いと思います。

+ **Dynamic Actor**: 物理演算を行うアクターです。通常はこのアクターを使用することになると思います。
+ **Static Actor**: イメージ的には物理演算を行わないアクターです。正確には物理演算を行いますが、一切動かないアクターになるので、通常は壁や地面になると思います。
+ **Kinematic Actor** 物理演算を行うアクターです。ただし、Dynamic Actorに対して一方的に影響を与え、自身については影響を受けません。

## 追加
　<font color="Red">ジオメトリは``physx::PxCreateDynamic``などで作成しただけでは**自動で物理演算してくれません**。</font>そのためには``physx::PxScene``に追加しなければなりません。逆に言えば、あらかじめ作成しておいて後でシーンに追加するといったことも可能です。

## 情報のセット
``Dynamic Actor``に対し

+ 速度: ``setLinearVelocity``
+ 加速度: ``setAngularVelocity``
+ 位置+角度: ``setKinematicTarget``（<font color="red">変更する前に``setRigidBodyFlag``で**Kinematic Actor**に変更する必要があります</font>）
<details><summary>``setGlobalPose``について</summary>
この関数は強制的に座標を変更する関数で、コメントにも書いてある通り、使用については<font color="red">**非推奨**</font>です。これは、静的アクターの場合はパフォーマンスが低下する場合があり、動的アクターの場合は挙動がおかしくなる（他のアクターに重なった時やジョイント部分を引き離した時）からです。
</details>

## 情報の取得
+ 速度: ``getLinearVelocity``
+ 加速度: ``getAngularVelocity``
+ 位置+角度 ``getkinematicTarget``か``getGlobalPose``

## 無効状態
　Dynamic Actorを無効化させることで、処理負荷を大幅に軽減させることが出来ます。無効化状態だと完全に静止状態になり、他のActorとの衝突があるまで処理が無効化されます。

+ 無効化: ``putToSleep``
+ 有効化: ``wakeUp``

## 削除
　ジオメトリを削除するには``physx::PxScene``で削除することで自動で削除出来ます。PhysXのメモリー管理は[参照カウント](https://qiita.com/pseudo_foxkeh/items/a15e4a5fd6ba679d4841)を使っているためです。ですが、参照カウントにはあまりいい思い出は無いので、出来る限り手動で削除したいとは思いますが...。

# 終了処理
　かならず、[初期化](#初期化)で作成した全てのクラスは削除する（releaseを呼ぶ）ようにしてください。特に``physx::PxPhysics``は削除した際に、シーンに存在する全てのジオメトリを削除するのですが、トラブルを避ける為にもPhysX側に任せるのではなく、事前にジオメトリの削除を自分で実行した方が良いと思います。

# 最後に
　物理演算エンジンは入れたいけど難易度が高そうと思っている方がいるかもしれませんが、導入さえしてしまえば使うのは意外と難しくありません。他に難しくしている要素は日本語の記事がほとんどない事でしょうか？その問題もGoogle先生にかかれば割とどうにかなるので、試しに導入してみるのもありだと思います。