UNPKG

babylon-mmd

Version:
336 lines (335 loc) 12.4 kB
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; }