UNPKG

babylon-mmd

Version:
328 lines (327 loc) 12.3 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 { IMmdMaterialProxyConstructor } from "./IMmdMaterialProxy"; import type { IMmdRuntime } from "./IMmdRuntime"; import type { IMmdRuntimeAnimatable } from "./IMmdRuntimeAnimatable"; import type { IMmdLinkedBoneContainer } from "./IMmdRuntimeLinkedBone"; import type { MmdSkinnedMesh } from "./mmdMesh"; import { MmdModel } from "./mmdModel"; import type { IMmdPhysics } from "./Physics/IMmdPhysics"; /** * Options for constructing MMD model physics */ export interface IMmdModelPhysicsCreationOptions { /** * Physics world ID (default: MmdWasmRuntime.physics.nextWorldId or MmdBulletPhysics.nextWorldId) * * value must be range of unsigned 32-bit integer * * By default, the physics world ID is automatically assigned and incremented * * If you need physics interaction between models, you can specify the same world ID * * Separating physics worlds can improve performance and enable parallel processing when using multi-threaded physics runtime (e.g. MmdWasmInstanceTypeMPR) */ worldId?: number; /** * Physics world IDs that share kinematic and static physics objects (default: []) * * value must be range of unsigned 32-bit integer * * If you need physics interaction by across multiple physics worlds, you can specify the world IDs that share only kinematic and static physics objects * * Dynamic physics objects are not shared between physics worlds. Only kinematic objects are shared */ kinematicSharedWorldIds?: number[]; /** * If true, the offset for the constraint frame is forcibly disabled. (Default: false) * * This is useful for reproducing the behavior of MMD physics. * But the constraint can be broken if the offset is forcibly disabled. * * For handling the constraint breaking, you can set fixedTimeStep to 1 / 120 or lower. */ disableOffsetForConstraintFrame?: boolean; } /** * Options for creating MMD model * * Generic type `TMaterial` is used to specify the material type of the model */ export interface IMmdModelCreationOptions<TMaterial extends Material = Material> { /** * Material proxy constructor is required if you want to use material morphing (default: null) */ materialProxyConstructor?: Nullable<IMmdMaterialProxyConstructor<TMaterial>>; /** * Whether to build physics (default: true) */ buildPhysics?: IMmdModelPhysicsCreationOptions | boolean; /** * Whether to trim metadata (default: true) * * If true, bone, morph, rigidbody and joint metadata are trimmed when you create `MmdModel` instance from `MmdSkinnedMesh` * * This is useful for reducing memory usage * * You can't recreate `MmdModel` instance from `MmdSkinnedMesh` if you set this to true */ trimMetadata?: boolean; } /** * MMD runtime orchestrates several MMD components (models, camera, audio) * * MMD runtime handles updates and synchronization of MMD components * * It can also create and remove runtime components */ export declare class MmdRuntime implements IMmdRuntime<MmdModel> { private readonly _physics; 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 _currentFrameTime; private _animationTimeScale; private _animationPaused; private _animationFrameTimeDuration; private _useManualAnimationDuration; private readonly _needToInitializePhysicsModels; private _beforePhysicsBinded; private readonly _afterPhysicsBinded; private readonly _bindedDispose; private readonly _disposeObservableObject; /** * Creates a new MMD runtime * @param scene Objects that limit the lifetime of this instance * @param physics Physics builder */ constructor(scene?: Nullable<Scene>, physics?: Nullable<IMmdPhysics>); /** * Dispose MMD runtime * * Destroy all MMD models and unregister this runtime from scene * @param scene Scene */ dispose(scene: Scene): void; /** * 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>): MmdModel; /** * Create MMD model from humanoid mesh and virtual skeleton * * this method is useful for supporting humanoid models, usually used by `HumanoidMmd` * @param mmdSkinnedMesh MmdSkinnedMesh * @param skeleton Skeleton or Virtualized skeleton * @param options Creation options */ createMmdModelFromSkeleton<TMaterial extends Material>(mmdSkinnedMesh: MmdSkinnedMesh, skeleton: IMmdLinkedBoneContainer, options?: IMmdModelCreationOptions<TMaterial>): MmdModel; /** * 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: MmdModel): void; /** * Queue MMD model to initialize physics * * Actual physics initialization is done by the before physics stage * @param mmdModel MMD model */ initializeMmdModelPhysics(mmdModel: MmdModel): 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 MmdModel[]; /** * 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; /** * 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; }