PMXのモーフ(表情)の種別と、パーサがずれる原因

モーフは「頂点を動かすもの」だけではありません。材質の色を変えるもの、他のモーフをまとめるもの、剛体に力を加えるものまであり、種別ごとにレコードの長さが違います。 この長さを1つでも間違えると、モーフより後ろのブロックが全部ずれます。

最終更新: 2026-09-13 · 実モデル3体(アリシア / ad_org8 / ad_ik2)のPMXを自前で走査して種別ごとに数えた値。three r171 同梱の mmdparser のソースも実際に確認

モーフの種別は11種類

種別名前1件のバイト数(indexを除く)何をするか
0グループモーフindex + 4他のモーフをまとめて動かす
1頂点頂点index + 12頂点を動かす。表情の大半はこれ
2ボーンボーンindex + 12 + 16ボーンを移動・回転させる
3UV頂点index + 16UV座標をずらす(目の位置を動かす等)
4〜7追加UV1〜4頂点index + 16追加UVをずらす
8材質材質index + 113色・エッジ・各種係数を変える
9フリップモーフindex + 4PMX 2.1。モーフを切り替える
10インパルス剛体index + 25PMX 2.1。剛体に力を加える

index のバイト数はファイルのヘッダに書かれています(頂点・材質・ボーン・剛体・モーフでそれぞれ別に指定され、1・2・4バイトのいずれか)。モデルの規模によって変わるので、固定値で書くと大きいモデルで壊れます。

材質モーフの113バイトの内訳は、適用対象1 + 拡散色16 + 反射色12 + 反射強度4 + 環境色12 + エッジ色16 + エッジサイズ4 + テクスチャ係数16 + スフィア係数16 + Toon係数16 です。

実モデルでの内訳

モデルモーフ合計グループ頂点ボーンUV材質
アリシア63059004
ad_org81596864855
ad_ik21576774862

材質モーフが意外に多いのが実データの特徴です。ad_org8 は159件のうち55件が材質モーフで、これは「この材質を隠す」という使い方(涙・汗・ハイライトの出し入れ、衣装のパーツ切り替え)が定番だからです。頂点モーフだけを見ていると、表情の半分を取りこぼします。

なお、three.js(r171)の MMDLoader が実際に当てるのは頂点モーフだけです(材質・ボーン・UVは未実装)。このサイトは材質モーフの色・不透明度・輪郭線を自前で当てています(材質モーフの記事)。

追加UVモーフ(4〜7)を読み飛ばし損ねる

three.js r171 が内部で使う mmdparser は、種別4〜7の分岐が // TODO: implement のまま空です。 実際のソースがこうなっています。

} else if ( p.type === 4 ) {  // additional uv1

    // TODO: implement

} else if ( p.type === 5 ) {  // additional uv2

    // TODO: implement

これは「その情報が取れない」で済む話ではありません。 読み進める位置(バイトカーソル)が1バイトも進まないので、そのモーフのデータが次のモーフの先頭として読まれます。 以降のモーフ・表示枠・剛体・ジョイントが全部ずれます。

壊れ方は「読み込めません」という素直な失敗にならず、とんでもない位置に剛体が置かれる・意味不明な値で例外が出るという形になります。原因がモーフにあると気づくまでに時間がかかる種類の不具合です。種別9(フリップ)・10(インパルス)にも分岐がありません。

読み飛ばすだけなら実装は1行です。 追加UVモーフはUVモーフと同じ並び(頂点index + Float32×4)なので、skip(頂点indexのバイト数 + 16) で済みます。自前でPMXを歩くなら、使わない種別も長さだけは正しく進めてください。

実測した3体には追加UVモーフは含まれていませんでした(UVモーフは ad_org8 / ad_ik2 に8件)。当たらないから気づかない類の問題で、当たったときだけ突然壊れます。

VMD側の表情は名前とウェイトだけ

モーションファイル(VMD)の表情キーフレームは23バイトで、モーフ名15バイト + フレーム番号4バイト + ウェイト4バイト(float、0.0〜1.0)しかありません。

  • 補間曲線を持ちません。 キーフレーム間は線形に繋ぐのが仕様です。ボーンと違って緩急は付けられません。
  • モーフ名も15バイトのShift_JIS固定長です。日本語なら7文字まで(名前の記事)。実測した425種のうち7種が上限に達していました。
  • モデルに無いモーフ名は無視されるだけです。表情モーションが他のモデルで「一部だけ効かない」のはこれで、名前が完全一致しないと当たりません。

表情名の命名は標準化されていません。実測した425種には ウィンク ウィンク2右(半角カナ) まつ毛-まばたき のような揺れがあります。「ウィンク」が左目を指すという慣習があるため、ウィンク右 の反対側は ウィンク左 ではなく無印の ウィンク になっていることが普通です。

ウェイトの種別(モーフとは別物だが同じ落とし穴がある)

モーフと並んで種別で長さが変わるのが頂点のウェイトです。BDEF1(0)・BDEF2(1)・BDEF4(2)・SDEF(3)・QDEF(4)で、それぞれボーン番号とウェイトの個数が違います。実測:

モデルBDEF1BDEF2BDEF4SDEF合計
アリシア8,85810,1213,332022,311
ad_org819,20016,8131,5433,13240,688

SDEF(球状変形)は実データに普通にあります。 ad_org8 は3,132頂点がSDEFでした。three.js の MMDLoader はSDEFをBDEF2として扱う割り切りをしており、このサイトも同じです。肩とひじの潰れ方が本家MMDと厳密には一致しません。