MMD ランタイム
MmdRuntimeクラス
MmdRuntime
は、babylon-mmdランタイムコンポーネントのコアクラスです。
MmdRuntime
は、他のすべてのランタイムコンポーネントを参照し、制御して、MMDモデルにアニメーションを適用します。
MmdRuntime
は以下の機能を提供します:
- 複数のMMDモデルを同時に制御
- 複数のMMDカメラを同時に制御
- カメラアニメーションの適用
- 物理シミュレーションの制御
MmdRuntime
を作成するコードは以下の通りです:
const mmdRuntime = new MmdRuntime(scene, null);
MmdRuntime
のコンストラクタは2つの引数を取ります:
scene
:Scene
オブジェクトが提供された場合、MmdRuntime
のライフタイムはScene
オブジェクトに紐付けられます。つまり、Scene
が破棄されると、MmdRuntime
も自動的に破棄されます。null
が提供された場合は、MmdRuntime
のdispose()
メソッドを手動で呼び出す必要があります。そうしないとメモリーリークが発生する可能性があります。physics
:物理シミュレーションインプリメンテーションを提供します。null
が提供された場合、物理シミュレーションは無効になります。物理シミュレーションを有効にするには、MmdBulletPhysics
、MmdAmmoPhysics
、またはMmdPhysics
などのIMmdPhysics
インターフェースを実装するクラスのインスタンスを提供する必要があります。
物理シミュレーションを処理するロジックはMmdRuntime
に含まれておらず、外部から注入されることに注意してください。
この設計により、物理エンジンインプリメンテーションを簡単に入れ替えることができ、独自のカスタム物理エンジンを実装し、使用するインプリメンテーションのみをバンドルしてバンドルサイズを削減することが可能です。
フレームアップデート
アニメーションを処理するには、アップデートファンクションMmdRuntime.beforePhysics()
とMmdRuntime.afterPhysics()
を毎フレーム呼び出す必要があります。
これら2つのメソッドは、物理シミュレーションが実行される前と後にそれぞれ呼び出される必要があります。
したがって、MmdRuntime
を使用するアプリケーションは、次のようなフレームループを持つ必要があります:
// sudo code for frame loop
for (; ;) {
mmdRuntime.beforePhysics();
simulatePhysics();
mmdRuntime.afterPhysics();
render();
}
これら2つのメソッドを毎フレーム呼び出す最も簡単な方法は、Scene
のonBeforeAnimationsObservable
とonBeforeRenderObservable
イベントにコールバックを登録することです。
MmdRuntime.register()
メソッドはScene
オブジェクトを引数として取り、内部的にこれら2つのイベントにコールバックを登録して、beforePhysics()
とafterPhysics()
が毎レンダリング時に自動的に呼び出されるようにします。
mmdRuntime.register(scene);
MmdRuntime
のアップデートを一時的に停止したい場合は、MmdRuntime.unregister()
メソッドを呼び出して、登録されたコールバックを削除できます。
mmdRuntime.unregister(scene);
再生制御
MmdRuntime
のコア機能の1つは、MMDアニメーション再生の制御です。
MmdRuntime
は、アニメーションを制御するための以下のメソッドを提供します:
playAnimation(): Promise<void>
:アニメーション再生を開始します。pauseAnimation(): void
:アニメーション再生を一時停止します。seekAnimation(frameTime: number, forceEvaluate: boolean = false): Promise<void>
:アニメーションを特定のフレームに移動します。forceEvaluate
がtrue
に設定されている場合、移動後すぐにアニメーションが評価されます。そうでない場合は、次のbeforePhysics(): void
呼び出し時に評価されます。setManualAnimationDuration(frameTimeDuration: Nullable<number>): void
:アニメーションの総フレーム時間を手動で設定します。デフォルトでは、アニメーションの総長は、評価に参加するすべてのMMDアニメーションの中で最も長いものに自動的に設定されます。このメソッドは、複数のアニメーションクリップがある場合やアニメーションクリップがない場合に便利です。null
が提供された場合、自動モードに戻ります。
MmdRuntime
は、アニメーション状態をチェックするための以下のプロパティを提供します:
isAnimationPlaying: boolean
:アニメーションが現在再生中かどうかを示すブール値。timeScale: number
:アニメーション再生速度を制御する数値。デフォルトは1.0
。currentFrameTime: number
:アニメーションの現在のフレーム時間を示す数値。currentTime: number
:アニメーションの現在時間を秒単位で示す数値。animationFrameTimeDuration: number
:アニメーションの総フレーム時間長を示す数値。animationDuration: number
:アニメーションの総長を秒単位で示す数値。
MmdRuntime
は内部的に時間を表現するためにフレーム時間を使用します。MMDアニメーションは秒間30フレームで再生されるため、1秒は30フレーム時間に対応します。例えば、currentFrameTime
が60
の場合、アニメーションが2秒間再生されたことを意味します。
アニメータブル
MmdRuntime
は、任意のアニメータブルオブジェクトを制御する機能を提供します。
MMDモデルに対しては、MmdRuntime
が直接アニメーション計算を処理しますが、MMDモデル以外のオブジェクトについては、各オブジェクトが委任されて独自のアニメーションを計算します。
これらのオブジェクトはIMmdRuntimeAnimatable
インターフェースを実装する必要があり、MmdRuntime
のaddAnimatable()
メソッドを通じて登録できます。
IMmdRuntimeAnimatable
インターフェースを実装する典型的な例は、MmdCamera
クラスです。
以下は、MmdCamera
オブジェクトをMmdRuntime
に登録し、アニメーションを再生するサンプルコードです:
// initialize MmdRuntime
const mmdRuntime = new MmdRuntime(scene, null);
mmdRuntime.register(scene);
// load VMD animation
const vmdLoader = new VmdLoader();
const mmdAnimation = await vmdLoader.loadAsync("motion", "path/to/motion.vmd");
// create MmdCamera and set animation
const camera = new MmdCamera();
const runtimeAnimation = camera.createRuntimeAnimation(mmdAnimation);
camera.setRuntimeAnimation(runtimeAnimation);
// add MmdCamera to MmdRuntime and play animation
mmdRuntime.addAnimatable(camera);
mmdRuntime.playAnimation();
MmdModelクラス
MmdModel
は、MMDモデルを表すクラスです。MmdModel
は、MMDモデルのルートメッシュ(MMDメッシュとも呼ばれる)をラップし、モデルのボーン、モーフ、物理シミュレーションなどを制御するインターフェースを提供します。
MmdModel
は基本的にMmdRuntime
によって制御され、MmdRuntime
のcreateMmdModel()
またはcreateMmdModelFromSkeleton()
メソッドを通じてのみ作成できます。
以下は、PMXモデルをロードしてMmdModel
を作成するサンプルコードです:
// initialize MmdRuntime
const mmdRuntime = new MmdRuntime(scene, null);
mmdRuntime.register(scene);
// load VMD animation
const vmdLoader = new VmdLoader();
const mmdAnimation = await vmdLoader.loadAsync("motion", "path/to/motion.vmd");
// load PMX model
const assetContainer = await LoadAssetContainerAsync("path/to/model.pmx", scene)
assetContainer.addAllToScene();
const rootMesh = assetContainer.meshes[0] as Mesh;
// create MmdModel and set animation
const mmdModel = mmdRuntime.createMmdModel(rootMesh);
const runtimeAnimation = mmdModel.createRuntimeAnimation(mmdAnimation);
mmdModel.setRuntimeAnimation(runtimeAnimation);
// play animation
mmdRuntime.playAnimation();
MmdModel
インスタンスが作成された瞬間から、MMDメッシュの様々なリソースがMmdModel
によって管理されます。これにはMesh
、Skeleton
、Bone
、Morph Target
、Material
などが含まれます。
MmdModel
によって管理されるリソースを直接アクセスしたり変更したりすることは推奨されません。
特にSkeleton
については、MmdModel
が内部的に計算メソッドをオーバーライドしているため、MmdModel
によって管理されるSkeleton
やBone
オブジェクトのメソッドを直接呼び出すと、予期しない動作を引き起こす可能性があります。
MmdModel
を破棄すると、対応するMMDメッシュがランタイムから削除され、モデルによって管理されるすべてのリソースが解放されます。
mmdRuntime.destroyMmdModel(mmdModel);
MmdModel
オブジェクトの主なプロパティは以下の通りです:
mesh: MmdSkinnedMesh | TrimmedMmdSkinnedMesh
:MMDモデルのルートメッシュ。skeleton: IMmdLinkedBoneContainer
:MMDモデルのスケルトン。worldTransformMatrices: Float32Array
:MMDモデルのワールドトランスフォームマトリックスの配列。各ボーンのワールドトランスフォームマトリックスを含みます。ikSolverStates: Uint8Array
:MMDモデルのIKソルバー状態の配列。各IKボーンのアクティベーション状態を含みます。rigidBodyStates: Uint8Array
:MMDモデルのリジッドボディ状態の配列。各リジッドボディのアクティベーション状態を含みます。runtimeBones: readonly IMmdRuntimeBone[]
:MMDモデルのボーンを表すMmdRuntimeBone
オブジェクトの配列。morph: MmdMorphController
:MMDモデルのモーフを制御するMmdMorphController
オブジェクト。
MmdModel作成オプション
MmdRuntime
のcreateMmdModel()
メソッドを使用してMmdModel
を作成する際、オプションオブジェクトを第2引数として渡して、モデルの動作をカスタマイズできます。
const mmdModel = mmdRuntime.createMmdModel(rootMesh, {
materialProxyConstructor: null,
buildPhysics: true,
trimMetadata: true
});
オプションオブジェクトには以下のプロパティがあります:
materialProxyConstructor: Nullable<IMmdMaterialProxyConstructor<TMaterial>>
:マーテリアルプロキシのコンストラクタファンクション。提供された場合、MMDモデルの各マーテリアルに対してマーテリアルプロキシが作成され、マーテリアルパラメータの操作に使用されます。これによりマーテリアルモーフィングのサポートが可能になります。詳細については、Enable Material Morphingドキュメントを参照してください。デフォルトはnull
です。buildPhysics: IMmdModelPhysicsCreationOptions | boolean
:物理シミュレーション作成のオプション。true
が提供された場合、MMDモデルのメタデータに基づいてリジッドボディとコンストレイントが作成されます。IMmdModelPhysicsCreationOptions
タイプのオブジェクトが提供された場合、リジッドボディとコンストレイント作成のオプションを設定できます。詳細については、Apply Physics To MMD Modelsドキュメントを参照してください。デフォルトはtrue
です。trimMetadata: boolean
:true
が提供された場合、MMDモデルの作成時にのみ使用される不要なメタデータが、モデル作成後にMMDメッシュから削除されます。これによりメモリー使用量を削減できます。ただし、後で同じMMDメッシュからMmdModel
を再作成したい場合は、このオプションをfalse
に設定する必要があります。デフォルトはtrue
です。
MmdRuntimeBoneクラス
MmdRuntimeBone
は、MMDモデルのボーンを表すクラスです。Babylon.jsのBone
クラスをラップし、ボーンのモーフ、IK、アペンドトランスフォームなどを制御するインターフェースを提供します。
MmdRuntimeBone
オブジェクトには、MmdModel.runtimeBones
プロパティを通じてアクセスできます。
MmdRuntimeBone
オブジェクトの主なプロパティは以下の通りです:
linkedBone: Bone
:MmdRuntimeBone
によってラップされるBabylon.jsのBone
オブジェクト。name: string
:ボーンの名前。parentBone: Nullable<MmdRuntimeBone>
:親ボーン。ルートボーンの場合はnull
。childBones: readonly MmdRuntimeBone[]
:子ボーンの配列。transformOrder: number
:ボーンのトランスフォーム順序。flag: number
:PMXボーンフラグ値。transformAfterPhysics: boolean
:物理シミュレーション後にトランスフォームが適用されるかどうか。worldMatrix: Float32Array
:ボーンのワールドトランスフォームマトリックス。これはMmdModel.worldTransformMatrices
配列の一部を参照します。ikSolverIndex: number
:ボーンのIKソルバーインデックス。IKボーンでない場合は-1
。MmdModel.ikSolverStates
配列を通じてボーンのIKアクティベーション状態をチェックできます。rigidBodyIndices: readonly number[]
:ボーンに接続されたリジッドボディのインデックス配列。各リジッドボディのアクティベーション状態はMmdModel.rigidBodyStates
配列を通じてチェックできます。
MmdRuntimeBone
は以下のメソッドも提供します:
getWorldMatrixToRef(target: Matrix): Matrix
:ボーンのワールドトランスフォームマトリックスをtarget
マトリックスにコピーします。getWorldTranslationToRef(target: Vector3): Vector3
:ボーンのワールド位置をtarget
ベクターにコピーします。setWorldTranslation(source: DeepImmutable<Vector3>): void
:ボーンのワールド位置をsource
ベクターに設定します。
MmdRuntimeBone
のこれらのプロパティとメソッドは、ボーンの状態を読み取りまたは設定するために使用できます。
以下は、MmdRuntimeBone
のメソッドを使用してMMDモデルのセンターボーンのワールド位置を出力するサンプルコードです:
const meshWorldMatrix = mmdModel.mesh.getWorldMatrix();
const boneWorldMatrix = new Matrix();
const centerBone = mmdModel.runtimeBones.find(bone => bone.name === "センター")!;
// The bone world matrix is based on model space, so you need to multiply the mesh world matrix.
centerBone.getWorldMatrixToRef(boneWorldMatrix).multiplyToRef(meshWorldMatrix, boneWorldMatrix);
const centerPosition = new Vector3();
boneWorldMatrix.getTranslationToRef(centerPosition);
console.log(`Center bone world position: ${centerPosition.toString()}`);
MmdMorphControllerクラス
MmdMorphController
は、MMDモデルのモーフを制御するクラスです。
MmdMorphController
は、バーテックスモーフ、ボーンモーフ、UVモーフ、マーテリアルモーフなどを制御するインターフェースを提供します。
MmdMorphController
オブジェクトには、MmdModel.morph
プロパティを通じてアクセスできます。
MmdMorphController
オブジェクトの主なメソッドは以下の通りです:
setMorphWeight(morphName: string, weight: number): void
:名前がmorphName
のモーフのウェイトを設定し、weight
にします。指定された名前のモーフが存在しない場合、何も起こりません。getMorphWeight(morphName: string): number
:名前がmorphName
のモーフの現在のウェイトを返します。指定された名前のモーフが存在しない場合、0
を返します。getMorphIndices(morphName: string): readonly number[] | undefined
:名前がmorphName
のモーフのインデックス配列を返します。指定された名前のモーフが存在しない場合、undefined
を返します。setMorphWeightFromIndex(morphIndex: number, weight: number): void
:インデックスmorphIndex
のモーフのウェイトをweight
に設定します。指定されたインデックスのモーフが存在しない場合、何も起こりません。getMorphWeightFromIndex(morphIndex: number): number
:インデックスmorphIndex
のモーフの現在のウェイトを返します。指定されたインデックスのモーフが存在しない場合、undefined
を返します。getMorphWeights(): Readonly<ArrayLike<number>>
:すべてのモーフのウェイト配列を返します。resetMorphWeights(): void
:すべてのモーフのウェイトを0
に初期化します。update(): void
:モーフの状態を更新します。通常はMmdRuntime
によって自動的に呼び出されるため、直接呼び出す必要はありません。
デフォルトでは、MmdMorphController
は内部的にインデックスを使用してモーフを制御します。そのため、モーフ名を使用してウェイトを設定または取得するメソッドは、内部的に名前をインデックスに変換するため、パフォーマンスが重要な状況では、インデックスを直接使用するメソッドを使用する方が良いでしょう。
フィジックス
MmdRuntime
は、物理シミュレーションのために注入された外部の物理エンジンインプリメンテーションを使用します。babylon-mmdは3つの物理エンジンインプリメンテーションを提供します:
MmdBulletPhysics
:Bullet Physicsエンジンを使用します。Bullet PhysicsはC++で書かれた物理エンジンで、babylon-mmdは最適化されたWebAssemblyコンパイル版を提供します。MmdAmmoPhysics
:Ammo.jsエンジンを使用します。MmdPhysics
:Havok Physicsエンジンを使用します。
MmdRuntime
で物理シミュレーションを有効にするには、MmdRuntime
作成時にこれらのクラスのいずれかのインスタンスを提供する必要があります。
物理シミュレーション設定の詳細については、Apply Physics To MMD Modelsドキュメントを参照してください。
WebAssemblyインプリメンテーション
MmdRuntime
でのIKソルブ、アペンドトランスフォーム、モーフ処理は、すべてTypeScriptで実装され、ブラウザのJavaScriptエンジンによって処理されます。
babylon-mmdは、より高速なパフォーマンスのためにWebAssembly (WASM)で実装されたMmdWasmRuntime
も提供します。MmdWasmRuntime
はMmdRuntime
とほぼ同じAPIを提供し、IKソルブ、アペンドトランスフォーム、モーフ、物理シミュレーションをWebAssemblyで処理して優れたパフォーマンスを実現します。
ただし、WASMインプリメンテーションは任意にカスタマイズすることが困難で、特殊なランタイム環境(例:React Native)では制限される可能性があります。
詳細については、MMD WebAssembly Runtimeドキュメントを参照してください。