PMXの剛体とジョイントの中身|衝突グループ・ばね・回転の順番
髪やスカートの揺れは、PMXに書かれた剛体(当たり判定のある形)とジョイント(剛体どうしをつなぐばね)で決まります。中身を実物で数えると、three.jsでの扱いと食い違う点が1つ見つかりました。剛体の回転の読み方です。
このサイトの再生は、2026-10-07から剛体とジョイントの回転を YXZ の順で読んでいます(読み込んだ直後に、three.js が読む XYZ の角度へ書き換えています)。
剛体に書かれているもの
| 項目 | 中身 |
|---|---|
| 形 | 球・箱・カプセルの3種類と、その大きさ |
| 種類 | 0 = ボーンに付いていく / 1 = 物理で動く / 2 = 物理で動くが、位置はボーンに合わせる |
| グループ | 0〜15の番号。自分がどのグループか |
| 衝突するグループ | 16ビット。どのグループと当たるか(下で説明) |
| 質量・移動減衰・回転減衰・反発・摩擦 | 揺れ方と、当たったときの跳ね方 |
| 位置・回転 | つながるボーンからではなく、モデルの原点からの位置と向き |
| 人物A | アリシア | |
|---|---|---|
| 剛体 | 141(球8・箱67・カプセル66) | 79(球4・箱1・カプセル74) |
| 種類 0 / 1 / 2 | 39 / 50 / 52 | 26 / 0 / 53 |
| ジョイント | 236 | 53 |
| 反発 | 全部0 | 全部0 |
| 摩擦 | 0が94、0.5が47 | 全部0.5 |
ステージ(15ボーン)には剛体もジョイントもありませんでした。アリシアは物理で動く剛体が全部「種類2」で、位置はボーンに合わせ、回転だけを物理に任せる作りです。
衝突するグループは「立っているビットと当たる」
16ビットの値は、実物では fffe・fffa・ffdb のように、ほとんどのビットが立った形でした(アリシアは79件すべてがこの形)。three.js の MMDPhysics は、自分のグループを 1 << 番号、この値をそのまま「衝突する相手」の条件として物理エンジン(Bullet)に渡します。立っているビットのグループとは当たり、落ちているビットのグループとは当たりません。
たとえばグループ0の剛体の値が fffe なら、0番のビットだけが落ちているので、同じグループ0どうしでは当たりません。髪の剛体どうしが絡まないようにする、よくある設定です。人物Aには値が 0(どのグループとも当たらない)の剛体が1つありました。
ジョイントに書かれているもの
ジョイントは2つの剛体をつなぎ、動ける範囲(移動の下限・上限、回転の下限・上限)と、ばねの強さ(移動3軸・回転3軸)を持ちます。実物のジョイントは全部「6自由度ばね」の種類でした。
| 人物A(236) | アリシア(53) | |
|---|---|---|
| 移動の範囲が0〜0(動かない) | 176 | 53 |
| 回転の範囲が全部0(固定) | 2 | 0 |
| 移動のばねが0でない | 16 | 0 |
| 回転のばねが0でない | 40 | 22 |
- three.js は Bullet の
btGeneric6DofSpringConstraintを使い、ばねが0でない軸だけばねを有効にします - MMDに近い動きにするための設定(
setParam)は、ソースのコメントでは「改造した Ammo.js にしか無い」とされています。このサイトが配っている Ammo.js には入っていたので、効いています - 剛体の種類0はボーンの動きをそのまま写し、1はボーンの回転と位置を物理の結果で上書きし、2は回転だけを上書きして位置はボーンに戻します
剛体の回転は YXZ の順で読むと合う
剛体とジョイントの回転は、3つの角度(オイラー角)で書かれています。オイラー角は「どの軸から回すか」で結果が変わりますが、PMXには順番が書かれていません。three.js の MMDPhysics は、three.js の既定の XYZ の順で読んでいます。
そこで、カプセル形の剛体を使って確かめました。カプセルは髪やスカートの骨(ボーンから、つながる先のボーンへの向き)に沿わせて置くのが普通なので、正しい順で読めばカプセルの軸が骨の向きに揃うはずです。回転が2軸以上にまたがるカプセル(1軸だけなら順番は関係ない)で、軸と骨の向きの角度を6通りの順番で測りました。
| 読む順番 | 人物A(25本) | アリシア(30本) |
|---|---|---|
| XYZ(three.jsの既定) | 中央値2.00°・5°以内16本 | 中央値2.54°・5°以内24本 |
| YXZ | 中央値1.03°・5°以内21本 | 中央値1.18°・5°以内30本 |
| ZXY | 中央値2.37°・5°以内18本 | 中央値2.61°・5°以内24本 |
| ZYX | 中央値2.00°・5°以内18本 | 中央値2.39°・5°以内24本 |
| YZX | 中央値2.00°・5°以内18本 | 中央値2.80°・5°以内22本 |
| XZY | 中央値2.69°・5°以内18本 | 中央値4.13°・5°以内16本 |
2体とも YXZ がいちばんよく合い、アリシアは30本すべてが5°以内に収まりました。回転が2軸以上にまたがる剛体は人物Aで141中95、アリシアで79中48あり、ジョイントは人物Aで236中198あります。読む順番が違うと、これらの向きが少しずつずれます。
このサイトでは、読み込んだ直後に「YXZ で表された回転」を「同じ回転を XYZ で表した角度」へ書き換えてから three.js に渡しています(回転そのものは変わらず、three.js のコードにも手を入れていません)。
見た目の違いは小さく、物理の揺らぎと区別できませんでした。 物理を入れたまま4秒置いて、物理で動くボーンの位置を直す前と後で比べると、ずれは最大0.23ユニット(人物Aのスカート)・0.06ユニット(アリシアの髪)でした。ところが同じ設定で2回動かしただけでも0.14・0.03ずれます。直したのは剛体の置き方が骨に合うようになった点で、揺れ方がどれだけ変わるかは測れていません。