babylon-mmd
Version:
babylon.js mmd loader and runtime
336 lines (335 loc) • 12.4 kB
TypeScript
import type { Material } from "@babylonjs/core/Materials/material";
import type { Mesh } from "@babylonjs/core/Meshes/mesh";
import { Observable } from "@babylonjs/core/Misc/observable";
import type { Scene } from "@babylonjs/core/scene";
import type { Nullable } from "@babylonjs/core/types";
import type { IPlayer } from "../Audio/IAudioPlayer";
import type { IMmdRuntime } from "../IMmdRuntime";
import type { IMmdRuntimeAnimatable } from "../IMmdRuntimeAnimatable";
import type { IMmdLinkedBoneContainer } from "../IMmdRuntimeLinkedBone";
import type { MmdSkinnedMesh } from "../mmdMesh";
import type { IMmdModelCreationOptions } from "../mmdRuntime";
import type { IMmdPhysics } from "../Physics/IMmdPhysics";
import { WasmSpinlock } from "./Misc/wasmSpinlock";
import type { IMmdWasmInstance } from "./mmdWasmInstance";
import { MmdWasmModel } from "./mmdWasmModel";
import type { IMmdWasmPhysicsRuntime } from "./Physics/IMmdWasmPhysicsRuntime";
import type { MmdWasmPhysics } from "./Physics/mmdWasmPhysics";
/**
* MMD WASM runtime animation evaluation type
*/
export declare enum MmdWasmRuntimeAnimationEvaluationType {
/**
* Immediate animation evaluation for the current frame
*/
Immediate = 0,
/**
* Buffered animation evaluation for the next frame
*
* Asynchronous Multi-thread optimization applies when possible
*
* If you are using havok or ammo.js physics, only beforePhysics process is asynchronous
*/
Buffered = 1
}
/**
* @internal
*/
export interface IPhysicsInitializeSet {
add(model: MmdWasmModel): void;
}
/**
* MMD WASM runtime orchestrates several MMD components (models, camera, audio)
*
* MMD WASM runtime handles updates and synchronization of MMD components
*
* It can also create and remove runtime components
*/
export declare class MmdWasmRuntime implements IMmdRuntime<MmdWasmModel> {
/**
* @internal
*/
readonly wasmInstance: IMmdWasmInstance;
/**
* @internal
*/
readonly wasmInternal: ReturnType<IMmdWasmInstance["createMmdRuntime"]>;
/**
* Spinlock for MMD WASM runtime to synchronize animation evaluation
* @internal
*/
readonly lock: WasmSpinlock;
private _usingWasmBackBuffer;
private _lastRequestAnimationFrameTime;
private _needToSyncEvaluate;
private readonly _externalPhysics;
private readonly _physicsRuntime;
private readonly _physicsClock;
private readonly _mmdMetadataEncoder;
private readonly _models;
private readonly _animatables;
private _audioPlayer;
/**
* Whether to automatically initialize rigid bodies transform and velocity (default: true)
*
* auto physics initialization is triggered when
* - animation seek is far from current frame time (more than 2 seconds)
* - browser tab is stop rendering and resumed
* - animation is played from the frame 0
*/
autoPhysicsInitialization: boolean;
private _loggingEnabled;
/** @internal */
log: (message: string) => void;
/** @internal */
warn: (message: string) => void;
/** @internal */
error: (message: string) => void;
private _isRegistered;
/**
* This observable is notified when animation duration is changed
*/
readonly onAnimationDurationChangedObservable: Observable<void>;
/**
* This observable is notified when animation is played
*/
readonly onPlayAnimationObservable: Observable<void>;
/**
* This observable is notified when animation is paused
*/
readonly onPauseAnimationObservable: Observable<void>;
/**
* This observable is notified when animation is seeked
*/
readonly onSeekAnimationObservable: Observable<void>;
/**
* This observable is notified when animation is evaluated (usually every frame)
*/
readonly onAnimationTickObservable: Observable<void>;
private _evaluationType;
private _currentFrameTime;
private _animationTimeScale;
private _animationPaused;
private _animationFrameTimeDuration;
private _useManualAnimationDuration;
private readonly _needToInitializePhysicsModels;
private readonly _needToInitializePhysicsModelsBuffer;
private _beforePhysicsBinded;
private readonly _afterPhysicsBinded;
private readonly _bindedDispose;
private readonly _disposeObservableObject;
/**
* Creates a new MMD web assembly runtime
*
* For use external physics engine like ammo.js or havok, you need to set `physics` to instance of `IMmdPhysics`
*
* If you want to use the wasm binary built-in physics engine, you can set `physics` to `MmdWasmPhysics` and you should use physics version of the wasm instance (e.g. `MmdWasmInstanceTypeMPR`)
* @param wasmInstance MMD WASM instance
* @param scene Objects that limit the lifetime of this instance
* @param physics IMmdPhysics instance or MmdWasmPhysics instance
*/
constructor(wasmInstance: IMmdWasmInstance, scene?: Nullable<Scene>, physics?: Nullable<IMmdPhysics | MmdWasmPhysics>);
/**
* Dispose MMD WASM runtime
*
* Destroy all MMD models and unregister this runtime from scene
* @param scene Scene
*/
dispose(scene: Scene): void;
private static readonly _TextDecoder;
private _flushWasmDiagnosticFromResultPtr;
private _flushWasmDiagnosticLog;
private static readonly _NullPhysicsInitializeSet;
private _getPhysicsInitializeSet;
/**
* Create MMD model from mesh that has MMD metadata
*
* The skeletons in the mesh where the MmdModel was created no longer follow the usual matrix update policy
* @param mmdSkinnedMesh MmdSkinnedMesh
* @param options Creation options
* @returns MMD model
* @throws {Error} if mesh is not `MmdSkinnedMesh`
*/
createMmdModel<TMaterial extends Material>(mmdSkinnedMesh: Mesh, options?: IMmdModelCreationOptions<TMaterial>): MmdWasmModel;
/**
* Create MMD model from humanoid mesh and virtual skeleton
*
* this method is useful for supporting humanoid models, usually used by `HumanoidMmd`
* @param mmdMesh MmdSkinnedMesh
* @param skeleton Skeleton or Virtualized skeleton
* @param options Creation options
*/
createMmdModelFromSkeleton<TMaterial extends Material>(mmdMesh: MmdSkinnedMesh, skeleton: IMmdLinkedBoneContainer, options?: IMmdModelCreationOptions<TMaterial>): MmdWasmModel;
/**
* Destroy MMD model
*
* Dispose all resources used at MMD runtime and restores the skeleton to the usual matrix update policy
*
* After calling the `destroyMmdModel` once, the mesh is no longer able to `createMmdModel` because the metadata is lost
* @param mmdModel MMD model to destroy
* @throws {Error} if model is not found
*/
destroyMmdModel(mmdModel: MmdWasmModel): void;
/**
* Queue MMD model to initialize physics
*
* Actual physics initialization is done by the before physics stage
* @param mmdModel MMD model
*/
initializeMmdModelPhysics(mmdModel: MmdWasmModel): void;
/**
* Queue all MMD models to initialize physics
*
* Actual physics initialization is done by the before physics stage
*
* If you set onlyAnimated true, it only initializes physics for animated models
*/
initializeAllMmdModelsPhysics(onlyAnimated: boolean): void;
/**
* Add Animatable object to the runtime
*
* Usually this is MMD camera, but you can add any object that implements `IMmdRuntimeAnimatable`
* @param animatable Animatable object
* @return true if the animatable is added, false if the animatable is already added
*/
addAnimatable(animatable: IMmdRuntimeAnimatable): boolean;
/**
* Remove Animatable object from the runtime
* @param animatable Animatable object
* @return true if the animatable is removed, false if the animatable is not found
*/
removeAnimatable(animatable: IMmdRuntimeAnimatable): boolean;
private _setAudioPlayerLastValue;
/**
* Set audio player to sync with animation
*
* If you set up audio Player while playing an animation, it try to play the audio from the current animation time
* And returns Promise because this operation is asynchronous. In most cases, you don't have to await this Promise
* @param audioPlayer Audio player
* @returns Promise
*/
setAudioPlayer(audioPlayer: Nullable<IPlayer>): Promise<void>;
/**
* Register MMD runtime to scene
*
* register `beforePhysics` and `afterPhysics` to scene Observables
*
* If you need a more complex update method you can call `beforePhysics` and `afterPhysics` manually
* @param scene Scene
*/
register(scene: Scene): void;
/**
* Unregister MMD runtime from scene
* @param scene Scene
*/
unregister(scene: Scene): void;
/**
* Before the physics stage, update animations and run MMD runtime solvers
*
* @param deltaTime Delta time in milliseconds
*/
beforePhysics(deltaTime: number): void;
/**
* After the physics stage, update physics and run MMD runtime solvers
*/
afterPhysics(): void;
private readonly _onAnimationDurationChanged;
private _computeAnimationDuration;
private readonly _onAudioDurationChanged;
private readonly _onAudioPlaybackRateChanged;
private readonly _onAudioPlay;
private readonly _onAudioPause;
private readonly _onAudioSeek;
private _playAnimationInternal;
/**
* Play animation from the current animation time
*
* If audio player is set, it try to play the audio from the current animation time
*
* It returns Promise because playing audio is asynchronous
* @returns Promise
*/
playAnimation(): Promise<void>;
/**
* Pause animation
*/
pauseAnimation(): void;
private _seekAnimationInternal;
/**
* Seek animation to the specified frame time
*
* If you set forceEvaluate true, the animation is evaluated even if the animation is not playing
*
* If audio player is set and not paused, it try to play the audio from the seek time so it returns Promise
* @param frameTime Time in 30fps frame
* @param forceEvaluate Whether to force evaluate animation
* @returns Promise
*/
seekAnimation(frameTime: number, forceEvaluate?: boolean): Promise<void>;
/**
* Whether animation is playing
*/
get isAnimationPlaying(): boolean;
/**
* MMD models created by this runtime
*/
get models(): readonly MmdWasmModel[];
/**
* Animatable objects that added to this runtime
*/
get animatables(): readonly IMmdRuntimeAnimatable[];
/**
* Audio player
*/
get audioPlayer(): Nullable<IPlayer>;
/**
* Current animation time scale (default: 1)
*/
get timeScale(): number;
set timeScale(value: number);
/**
* Current animation time in 30fps frame
*/
get currentFrameTime(): number;
/**
* Current animation time in seconds
*/
get currentTime(): number;
/**
* Current animation duration in 30fps frame
*/
get animationFrameTimeDuration(): number;
/**
* Current animation duration in seconds
*/
get animationDuration(): number;
/**
* Set animation duration manually
*
* When the difference between the length of the song and the length of the animation is large, it can be helpful to adjust the animation duration manually
* @param frameTimeDuration Time in 30fps frame
*/
setManualAnimationDuration(frameTimeDuration: Nullable<number>): void;
/**
* Animation evaluation type
*/
get evaluationType(): MmdWasmRuntimeAnimationEvaluationType;
set evaluationType(value: MmdWasmRuntimeAnimationEvaluationType);
/**
* get physics runtime
*
* If you don't set physics as `MmdWasmPhysics`, it returns null
*/
get physics(): Nullable<IMmdWasmPhysicsRuntime>;
/**
* Enable or disable debug logging (default: false)
*/
get loggingEnabled(): boolean;
set loggingEnabled(value: boolean);
private _logEnabled;
private _logDisabled;
private _warnEnabled;
private _warnDisabled;
private _errorEnabled;
private _errorDisabled;
}