@laplace.live/pixijs-live2d 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +103 -2
- package/dist/index.js +276 -29
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -3240,6 +3240,14 @@ export declare enum MotionPreloadStrategy {
|
|
|
3240
3240
|
/** No preload. */
|
|
3241
3241
|
NONE = "NONE"
|
|
3242
3242
|
}
|
|
3243
|
+
/**
|
|
3244
|
+
* What plays when nothing else does: a motion to loop, `'idle-group'` for a random motion of
|
|
3245
|
+
* the idle group (the default), or `null` to hold still.
|
|
3246
|
+
*/
|
|
3247
|
+
export type IdleMotion = {
|
|
3248
|
+
group: string;
|
|
3249
|
+
index: number;
|
|
3250
|
+
} | 'idle-group' | null;
|
|
3243
3251
|
/**
|
|
3244
3252
|
* Handles the motion playback for Live2D models.
|
|
3245
3253
|
* Responsible for loading, playing, and managing motion states and audio.
|
|
@@ -3322,6 +3330,22 @@ export declare abstract class MotionManager<Motion = unknown, MotionSpec = unkno
|
|
|
3322
3330
|
* @type {boolean}
|
|
3323
3331
|
*/
|
|
3324
3332
|
destroyed: boolean;
|
|
3333
|
+
/** The idle selection; see {@link setIdleMotion}. */
|
|
3334
|
+
idleMotion: IdleMotion;
|
|
3335
|
+
/**
|
|
3336
|
+
* Choose what plays when nothing else does. A running idle gives way at once and a loading one
|
|
3337
|
+
* is dropped; a higher-priority motion in flight hands over to the new selection when it ends.
|
|
3338
|
+
*/
|
|
3339
|
+
setIdleMotion(motion: IdleMotion): void;
|
|
3340
|
+
/**
|
|
3341
|
+
* List a motion file after load, under `group`, so a file written beside the model plays
|
|
3342
|
+
* without a reload. A new group is created; an existing one grows by one index.
|
|
3343
|
+
*/
|
|
3344
|
+
addMotion(group: string, definition: MotionSpec): void;
|
|
3345
|
+
/** Stop the idle so the next update starts the selection. Cubism eases its parameters home too. */
|
|
3346
|
+
protected releaseIdle(): void;
|
|
3347
|
+
/** Start the idle selection at IDLE priority; a chosen motion loops regardless of its own flag. */
|
|
3348
|
+
protected startIdleMotion(): Promise<boolean>;
|
|
3325
3349
|
/**
|
|
3326
3350
|
* Reference to the parent InternalModel.
|
|
3327
3351
|
* @type {InternalModel}
|
|
@@ -3541,7 +3565,9 @@ export declare abstract class ExpressionManager<Expression = unknown, Expression
|
|
|
3541
3565
|
*/
|
|
3542
3566
|
tag: string;
|
|
3543
3567
|
/**
|
|
3544
|
-
* Expression definitions copied from ModelSettings.
|
|
3568
|
+
* Expression definitions copied from ModelSettings. An index is not stable across an `await`:
|
|
3569
|
+
* {@link removeDefinition} shifts the ones above it, so a load or activation in flight
|
|
3570
|
+
* re-resolves its definition afterwards rather than trusting the index it started with.
|
|
3545
3571
|
*/
|
|
3546
3572
|
abstract readonly definitions: ExpressionSpec[];
|
|
3547
3573
|
/**
|
|
@@ -3574,6 +3600,14 @@ export declare abstract class ExpressionManager<Expression = unknown, Expression
|
|
|
3574
3600
|
* Active set remembered by {@link resetExpression} for {@link restoreExpression}.
|
|
3575
3601
|
*/
|
|
3576
3602
|
protected suppressedIndices: number[] | null;
|
|
3603
|
+
/**
|
|
3604
|
+
* Indices whose parameters are skipped while they stay active — an editor previewing an
|
|
3605
|
+
* expression's own edit needs the real one silent without touching the active set anyone
|
|
3606
|
+
* persists or observes.
|
|
3607
|
+
*/
|
|
3608
|
+
protected muted: Set<number>;
|
|
3609
|
+
/** Every entry silenced at once (the editor's Solo), on top of {@link muted}. */
|
|
3610
|
+
protected muteAll: boolean;
|
|
3577
3611
|
/**
|
|
3578
3612
|
* Flags the instance has been destroyed.
|
|
3579
3613
|
*/
|
|
@@ -3593,6 +3627,40 @@ export declare abstract class ExpressionManager<Expression = unknown, Expression
|
|
|
3593
3627
|
* @ignore
|
|
3594
3628
|
*/
|
|
3595
3629
|
private _loadExpression;
|
|
3630
|
+
/**
|
|
3631
|
+
* Lists a definition after load, so a file written beside the model activates without a reload.
|
|
3632
|
+
* @return The new index.
|
|
3633
|
+
*/
|
|
3634
|
+
addDefinition(definition: ExpressionSpec): number;
|
|
3635
|
+
/**
|
|
3636
|
+
* Replaces the loaded Expression at `index` — a file the editor just rewrote — keeping an active one
|
|
3637
|
+
* active at its current weight.
|
|
3638
|
+
*/
|
|
3639
|
+
replaceExpression(index: number, expression: Expression): void;
|
|
3640
|
+
/**
|
|
3641
|
+
* Forgets a definition (its file was deleted or renamed away): deactivates it, then shifts every
|
|
3642
|
+
* index above it so the remaining expressions keep their state. An activation of it still loading
|
|
3643
|
+
* settles as a no-op.
|
|
3644
|
+
*/
|
|
3645
|
+
removeDefinition(index: number): void;
|
|
3646
|
+
/**
|
|
3647
|
+
* Mutes or unmutes an Expression: an active one keeps its state and fade but contributes nothing
|
|
3648
|
+
* until unmuted. Unknown indices are ignored.
|
|
3649
|
+
*/
|
|
3650
|
+
setExpressionMuted(index: number | string, muted: boolean): void;
|
|
3651
|
+
/**
|
|
3652
|
+
* Mutes or unmutes every Expression at once, active state untouched; the per-entry mutes stay
|
|
3653
|
+
* as they are underneath.
|
|
3654
|
+
*/
|
|
3655
|
+
setAllMuted(muted: boolean): void;
|
|
3656
|
+
/**
|
|
3657
|
+
* A loaded Expression was swapped for another at `index`; a subclass holding it live re-points.
|
|
3658
|
+
*/
|
|
3659
|
+
protected onExpressionReplaced(_index: number, _expression: Expression): void;
|
|
3660
|
+
/**
|
|
3661
|
+
* The definition at `index` is gone and every index above it moved down by one.
|
|
3662
|
+
*/
|
|
3663
|
+
protected onDefinitionRemoved(_index: number): void;
|
|
3596
3664
|
/**
|
|
3597
3665
|
* Activates a random inactive Expression.
|
|
3598
3666
|
* @return Promise that resolves with true if succeeded, with false otherwise.
|
|
@@ -4656,7 +4724,8 @@ export declare class Live2DFactory {
|
|
|
4656
4724
|
/**
|
|
4657
4725
|
* Load tasks of each expression.
|
|
4658
4726
|
*/
|
|
4659
|
-
|
|
4727
|
+
/** Keyed by the definition object: a removal shifts indices, never the object. */
|
|
4728
|
+
static expressionTasksMap: WeakMap<ExpressionManager<unknown, unknown>, Map<unknown, Promise<any>>>;
|
|
4660
4729
|
/**
|
|
4661
4730
|
* Registers a Live2DRuntime.
|
|
4662
4731
|
*/
|
|
@@ -5742,9 +5811,15 @@ export declare class CubismModelSettings extends ModelSettings {
|
|
|
5742
5811
|
export declare class CubismExpressionManager extends ExpressionManager<CubismExpressionMotion, Expression> {
|
|
5743
5812
|
readonly expressionDataType: "arraybuffer";
|
|
5744
5813
|
readonly definitions: Expression[];
|
|
5814
|
+
/** The settings the listings read; `definitions` was filtered out of them, so both are kept in step. */
|
|
5815
|
+
private readonly cubismSettings;
|
|
5745
5816
|
/** Active fade entries in activation order; deactivated ones stay until fully faded out. */
|
|
5746
5817
|
private entries;
|
|
5747
5818
|
constructor(settings: CubismModelSettings, options?: MotionManagerOptions);
|
|
5819
|
+
addDefinition(definition: Expression): number;
|
|
5820
|
+
removeDefinition(index: number): void;
|
|
5821
|
+
protected onExpressionReplaced(index: number, expression: CubismExpressionMotion): void;
|
|
5822
|
+
protected onDefinitionRemoved(index: number): void;
|
|
5748
5823
|
isFinished(): boolean;
|
|
5749
5824
|
getExpressionIndex(name: string): number;
|
|
5750
5825
|
getExpressionFile(definition: Expression): string;
|
|
@@ -6917,15 +6992,41 @@ export declare class CubismMotionManager extends MotionManager<CubismMotion, Mot
|
|
|
6917
6992
|
expressionManager?: CubismExpressionManager;
|
|
6918
6993
|
eyeBlinkIds: string[];
|
|
6919
6994
|
lipSyncIds: string[];
|
|
6995
|
+
/** What each motion's curves write, read once when it is created. */
|
|
6996
|
+
private readonly meta;
|
|
6997
|
+
/** Eases the parameters a released idle animated back to rest; see {@link releaseIdle}. */
|
|
6998
|
+
private release;
|
|
6920
6999
|
constructor(parent: CubismInternalModel);
|
|
6921
7000
|
protected init(options?: MotionManagerOptions): void;
|
|
6922
7001
|
isFinished(): boolean;
|
|
6923
7002
|
protected _startMotion(motion: CubismMotion, onFinish?: (motion: CubismMotion) => void, ignoreParamIds?: string[], loop?: boolean): number;
|
|
6924
7003
|
protected _stopAllMotions(): void;
|
|
7004
|
+
/**
|
|
7005
|
+
* Fade the queue's motions out over their own fade-out time instead of cutting them; each
|
|
7006
|
+
* ends like a natural finish (idle restart, `motionFinish`), which is how a looping one stops.
|
|
7007
|
+
* The motion's sound stops at once, as it does on {@link stopAllMotions}.
|
|
7008
|
+
*/
|
|
7009
|
+
fadeOutMotions(): void;
|
|
6925
7010
|
createMotion(data: ArrayBuffer, group: string, definition: Motion): CubismMotion;
|
|
6926
7011
|
getMotionFile(definition: Motion): string;
|
|
6927
7012
|
protected getMotionName(definition: Motion): string;
|
|
6928
7013
|
protected getSoundFile(definition: Motion): string | undefined;
|
|
7014
|
+
/** Whether the playing motion animates the eyes itself, so the engine's auto-blink must yield to it. */
|
|
7015
|
+
currentMotionBlinks(): boolean;
|
|
7016
|
+
/** The parameters the playing motion writes, for a host driver handing them over to it; null when none plays. */
|
|
7017
|
+
currentMotionParameters(): ReadonlySet<string> | null;
|
|
7018
|
+
/**
|
|
7019
|
+
* The parameters a playing one-shot (anything above IDLE) writes — what a host's tracking
|
|
7020
|
+
* yields to while it plays, as VTS ranks an animation above tracking. Null under the idle.
|
|
7021
|
+
*/
|
|
7022
|
+
oneShotParameters(): ReadonlySet<string> | null;
|
|
7023
|
+
/** A motion above idle priority is current — one the player started, never the idle. */
|
|
7024
|
+
oneShotPlaying(): boolean;
|
|
7025
|
+
private currentMotion;
|
|
7026
|
+
private currentMeta;
|
|
7027
|
+
protected releaseIdle(): void;
|
|
7028
|
+
/** One frame of the ease-to-rest, ahead of the motions so a new idle's fade-in blends from it. */
|
|
7029
|
+
private updateRelease;
|
|
6929
7030
|
protected updateParameters(model: CubismModel, now: DOMHighResTimeStamp): boolean;
|
|
6930
7031
|
destroy(): void;
|
|
6931
7032
|
motionLastFrame(group: string, index: number, { expression }?: {
|
package/dist/index.js
CHANGED
|
@@ -2734,6 +2734,14 @@ var ExpressionManager = class extends EventEmitter {
|
|
|
2734
2734
|
*/
|
|
2735
2735
|
suppressedIndices = null;
|
|
2736
2736
|
/**
|
|
2737
|
+
* Indices whose parameters are skipped while they stay active — an editor previewing an
|
|
2738
|
+
* expression's own edit needs the real one silent without touching the active set anyone
|
|
2739
|
+
* persists or observes.
|
|
2740
|
+
*/
|
|
2741
|
+
muted = /* @__PURE__ */ new Set();
|
|
2742
|
+
/** Every entry silenced at once (the editor's Solo), on top of {@link muted}. */
|
|
2743
|
+
muteAll = false;
|
|
2744
|
+
/**
|
|
2737
2745
|
* Flags the instance has been destroyed.
|
|
2738
2746
|
*/
|
|
2739
2747
|
destroyed = false;
|
|
@@ -2751,7 +2759,8 @@ var ExpressionManager = class extends EventEmitter {
|
|
|
2751
2759
|
* @emits {@link ExpressionManagerEvents.expressionLoadError}
|
|
2752
2760
|
*/
|
|
2753
2761
|
async loadExpression(index) {
|
|
2754
|
-
|
|
2762
|
+
const definition = this.definitions[index];
|
|
2763
|
+
if (!definition) {
|
|
2755
2764
|
logger.warn(this.tag, `Undefined expression at [${index}]`);
|
|
2756
2765
|
return;
|
|
2757
2766
|
}
|
|
@@ -2762,7 +2771,11 @@ var ExpressionManager = class extends EventEmitter {
|
|
|
2762
2771
|
if (this.expressions[index]) return this.expressions[index];
|
|
2763
2772
|
const expression = await this._loadExpression(index);
|
|
2764
2773
|
if (this.destroyed) return;
|
|
2765
|
-
|
|
2774
|
+
const slot = this.definitions.indexOf(definition);
|
|
2775
|
+
if (slot < 0) return;
|
|
2776
|
+
const replaced = this.expressions[slot];
|
|
2777
|
+
if (replaced) return replaced;
|
|
2778
|
+
this.expressions[slot] = expression;
|
|
2766
2779
|
return expression;
|
|
2767
2780
|
}
|
|
2768
2781
|
/**
|
|
@@ -2773,6 +2786,66 @@ var ExpressionManager = class extends EventEmitter {
|
|
|
2773
2786
|
throw new Error("Not implemented.");
|
|
2774
2787
|
}
|
|
2775
2788
|
/**
|
|
2789
|
+
* Lists a definition after load, so a file written beside the model activates without a reload.
|
|
2790
|
+
* @return The new index.
|
|
2791
|
+
*/
|
|
2792
|
+
addDefinition(definition) {
|
|
2793
|
+
this.definitions.push(definition);
|
|
2794
|
+
return this.definitions.length - 1;
|
|
2795
|
+
}
|
|
2796
|
+
/**
|
|
2797
|
+
* Replaces the loaded Expression at `index` — a file the editor just rewrote — keeping an active one
|
|
2798
|
+
* active at its current weight.
|
|
2799
|
+
*/
|
|
2800
|
+
replaceExpression(index, expression) {
|
|
2801
|
+
if (!this.definitions[index]) return;
|
|
2802
|
+
this.expressions[index] = expression;
|
|
2803
|
+
this.onExpressionReplaced(index, expression);
|
|
2804
|
+
}
|
|
2805
|
+
/**
|
|
2806
|
+
* Forgets a definition (its file was deleted or renamed away): deactivates it, then shifts every
|
|
2807
|
+
* index above it so the remaining expressions keep their state. An activation of it still loading
|
|
2808
|
+
* settles as a no-op.
|
|
2809
|
+
*/
|
|
2810
|
+
removeDefinition(index) {
|
|
2811
|
+
if (!(index >= 0 && index < this.definitions.length)) return;
|
|
2812
|
+
this.unsetExpression(index);
|
|
2813
|
+
this.definitions.splice(index, 1);
|
|
2814
|
+
this.expressions.splice(index, 1);
|
|
2815
|
+
const shift = (i) => i > index ? i - 1 : i;
|
|
2816
|
+
this.activeIndices = this.activeIndices.map(shift);
|
|
2817
|
+
this.pendingIndices = new Set([...this.pendingIndices].map(shift));
|
|
2818
|
+
this.requestSeqs = new Map([...this.requestSeqs].map(([i, seq]) => [shift(i), seq]));
|
|
2819
|
+
if (this.suppressedIndices) this.suppressedIndices = this.suppressedIndices.filter((i) => i !== index).map(shift);
|
|
2820
|
+
this.muted = new Set([...this.muted].filter((i) => i !== index).map(shift));
|
|
2821
|
+
this.onDefinitionRemoved(index);
|
|
2822
|
+
}
|
|
2823
|
+
/**
|
|
2824
|
+
* Mutes or unmutes an Expression: an active one keeps its state and fade but contributes nothing
|
|
2825
|
+
* until unmuted. Unknown indices are ignored.
|
|
2826
|
+
*/
|
|
2827
|
+
setExpressionMuted(index, muted) {
|
|
2828
|
+
if (typeof index !== "number") index = this.getExpressionIndex(index);
|
|
2829
|
+
if (!this.definitions[index]) return;
|
|
2830
|
+
if (muted) this.muted.add(index);
|
|
2831
|
+
else this.muted.delete(index);
|
|
2832
|
+
}
|
|
2833
|
+
/**
|
|
2834
|
+
* Mutes or unmutes every Expression at once, active state untouched; the per-entry mutes stay
|
|
2835
|
+
* as they are underneath.
|
|
2836
|
+
*/
|
|
2837
|
+
setAllMuted(muted) {
|
|
2838
|
+
this.muteAll = muted;
|
|
2839
|
+
}
|
|
2840
|
+
/**
|
|
2841
|
+
* A loaded Expression was swapped for another at `index`; a subclass holding it live re-points.
|
|
2842
|
+
*/
|
|
2843
|
+
onExpressionReplaced(_index, _expression) {}
|
|
2844
|
+
/**
|
|
2845
|
+
* The definition at `index` is gone and every index above it moved down by one.
|
|
2846
|
+
*/
|
|
2847
|
+
onDefinitionRemoved(_index) {}
|
|
2848
|
+
/**
|
|
2776
2849
|
* Activates a random inactive Expression.
|
|
2777
2850
|
* @return Promise that resolves with true if succeeded, with false otherwise.
|
|
2778
2851
|
*/
|
|
@@ -2837,14 +2910,17 @@ var ExpressionManager = class extends EventEmitter {
|
|
|
2837
2910
|
*/
|
|
2838
2911
|
async setExpression(index, options) {
|
|
2839
2912
|
if (typeof index !== "number") index = this.getExpressionIndex(index);
|
|
2840
|
-
|
|
2913
|
+
const definition = this.definitions[index];
|
|
2914
|
+
if (!definition) return false;
|
|
2841
2915
|
if (this.activeIndices.includes(index) || this.pendingIndices.has(index)) return false;
|
|
2842
2916
|
this.pendingIndices.add(index);
|
|
2843
2917
|
const seq = this.nextRequestSeq++;
|
|
2844
2918
|
this.requestSeqs.set(index, seq);
|
|
2845
2919
|
this.emit("expressionsChanged");
|
|
2846
2920
|
const expression = await this.loadExpression(index);
|
|
2847
|
-
if (this.destroyed
|
|
2921
|
+
if (this.destroyed) return false;
|
|
2922
|
+
index = this.definitions.indexOf(definition);
|
|
2923
|
+
if (index < 0 || this.requestSeqs.get(index) !== seq || !this.pendingIndices.has(index)) return false;
|
|
2848
2924
|
if (!expression) {
|
|
2849
2925
|
this.pendingIndices.delete(index);
|
|
2850
2926
|
this.requestSeqs.delete(index);
|
|
@@ -2911,6 +2987,8 @@ var ExpressionManager = class extends EventEmitter {
|
|
|
2911
2987
|
this.requestSeqs.clear();
|
|
2912
2988
|
this.activeIndices = [];
|
|
2913
2989
|
this.suppressedIndices = null;
|
|
2990
|
+
this.muted.clear();
|
|
2991
|
+
this.muteAll = false;
|
|
2914
2992
|
this.stopAllExpressions();
|
|
2915
2993
|
const self = this;
|
|
2916
2994
|
self.definitions = void 0;
|
|
@@ -3567,6 +3645,10 @@ var MotionState = class {
|
|
|
3567
3645
|
*/
|
|
3568
3646
|
start(motion, group, index, priority) {
|
|
3569
3647
|
if (priority === 1) {
|
|
3648
|
+
if (group !== this.reservedIdleGroup || index !== this.reservedIdleIndex) {
|
|
3649
|
+
logger.log(this.tag, "Cannot start idle motion because it is no longer reserved.", this.dump(group, index));
|
|
3650
|
+
return false;
|
|
3651
|
+
}
|
|
3570
3652
|
this.setReservedIdle(void 0, void 0);
|
|
3571
3653
|
if (this.currentPriority !== 0) {
|
|
3572
3654
|
logger.log(this.tag, "Cannot start idle motion because another motion is playing.", this.dump(group, index));
|
|
@@ -3912,6 +3994,10 @@ let MotionPreloadStrategy = /* @__PURE__ */ function(MotionPreloadStrategy) {
|
|
|
3912
3994
|
MotionPreloadStrategy["NONE"] = "NONE";
|
|
3913
3995
|
return MotionPreloadStrategy;
|
|
3914
3996
|
}({});
|
|
3997
|
+
function sameIdleMotion(a, b) {
|
|
3998
|
+
if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) return a === b;
|
|
3999
|
+
return a.group === b.group && a.index === b.index;
|
|
4000
|
+
}
|
|
3915
4001
|
/**
|
|
3916
4002
|
* Handles the motion playback for Live2D models.
|
|
3917
4003
|
* Responsible for loading, playing, and managing motion states and audio.
|
|
@@ -3966,6 +4052,39 @@ var MotionManager = class extends EventEmitter {
|
|
|
3966
4052
|
* @type {boolean}
|
|
3967
4053
|
*/
|
|
3968
4054
|
destroyed = false;
|
|
4055
|
+
/** The idle selection; see {@link setIdleMotion}. */
|
|
4056
|
+
idleMotion = "idle-group";
|
|
4057
|
+
/**
|
|
4058
|
+
* Choose what plays when nothing else does. A running idle gives way at once and a loading one
|
|
4059
|
+
* is dropped; a higher-priority motion in flight hands over to the new selection when it ends.
|
|
4060
|
+
*/
|
|
4061
|
+
setIdleMotion(motion) {
|
|
4062
|
+
if (sameIdleMotion(this.idleMotion, motion)) return;
|
|
4063
|
+
this.idleMotion = motion;
|
|
4064
|
+
this.state.setReservedIdle(void 0, void 0);
|
|
4065
|
+
if (this.state.currentPriority === 1) this.releaseIdle();
|
|
4066
|
+
}
|
|
4067
|
+
/**
|
|
4068
|
+
* List a motion file after load, under `group`, so a file written beside the model plays
|
|
4069
|
+
* without a reload. A new group is created; an existing one grows by one index.
|
|
4070
|
+
*/
|
|
4071
|
+
addMotion(group, definition) {
|
|
4072
|
+
const definitions = this.definitions[group] ?? [];
|
|
4073
|
+
definitions.push(definition);
|
|
4074
|
+
this.definitions[group] = definitions;
|
|
4075
|
+
this.motionGroups[group] ??= [];
|
|
4076
|
+
}
|
|
4077
|
+
/** Stop the idle so the next update starts the selection. Cubism eases its parameters home too. */
|
|
4078
|
+
releaseIdle() {
|
|
4079
|
+
this.stopAllMotions();
|
|
4080
|
+
}
|
|
4081
|
+
/** Start the idle selection at IDLE priority; a chosen motion loops regardless of its own flag. */
|
|
4082
|
+
startIdleMotion() {
|
|
4083
|
+
const idle = this.idleMotion;
|
|
4084
|
+
if (idle === null) return Promise.resolve(false);
|
|
4085
|
+
if (idle === "idle-group") return this.startRandomMotion(this.groups.idle, 1);
|
|
4086
|
+
return this.startMotion(idle.group, idle.index, 1, { loop: true });
|
|
4087
|
+
}
|
|
3969
4088
|
/**
|
|
3970
4089
|
* Reference to the parent InternalModel.
|
|
3971
4090
|
* @type {InternalModel}
|
|
@@ -4264,7 +4383,7 @@ var MotionManager = class extends EventEmitter {
|
|
|
4264
4383
|
}
|
|
4265
4384
|
if (this.state.shouldOverrideExpression()) this.expressionManager?.restoreExpression();
|
|
4266
4385
|
this.state.complete();
|
|
4267
|
-
if (this.state.shouldRequestIdleMotion()) this.
|
|
4386
|
+
if (this.idleMotion !== null && this.state.shouldRequestIdleMotion()) this.startIdleMotion();
|
|
4268
4387
|
}
|
|
4269
4388
|
return this.updateParameters(model, now);
|
|
4270
4389
|
}
|
|
@@ -4840,6 +4959,7 @@ var Live2DFactory = class Live2DFactory {
|
|
|
4840
4959
|
/**
|
|
4841
4960
|
* Load tasks of each expression.
|
|
4842
4961
|
*/
|
|
4962
|
+
/** Keyed by the definition object: a removal shifts indices, never the object. */
|
|
4843
4963
|
static expressionTasksMap = /* @__PURE__ */ new WeakMap();
|
|
4844
4964
|
/**
|
|
4845
4965
|
* Registers a Live2DRuntime.
|
|
@@ -4938,28 +5058,33 @@ var Live2DFactory = class Live2DFactory {
|
|
|
4938
5058
|
expressionManager.once("destroy", () => Live2DFactory.releaseExpressionTasks(expressionManager));
|
|
4939
5059
|
let tasks = Live2DFactory.expressionTasksMap.get(expressionManager);
|
|
4940
5060
|
if (!tasks) {
|
|
4941
|
-
tasks =
|
|
5061
|
+
tasks = /* @__PURE__ */ new Map();
|
|
4942
5062
|
Live2DFactory.expressionTasksMap.set(expressionManager, tasks);
|
|
4943
5063
|
}
|
|
4944
5064
|
const path = expressionManager.getExpressionFile(definition);
|
|
4945
5065
|
const responseType = expressionManager.expressionDataType ?? "json";
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
5066
|
+
let task = tasks.get(definition);
|
|
5067
|
+
if (!task) {
|
|
5068
|
+
task = Live2DLoader.load({
|
|
5069
|
+
url: path,
|
|
5070
|
+
settings: expressionManager.settings,
|
|
5071
|
+
type: responseType,
|
|
5072
|
+
target: expressionManager
|
|
5073
|
+
}).then((data) => {
|
|
5074
|
+
const expressionData = data;
|
|
5075
|
+
const expression = expressionManager.createExpression(expressionData, definition);
|
|
5076
|
+
const current = expressionManager.definitions.indexOf(definition);
|
|
5077
|
+
if (current >= 0) expressionManager.emit("expressionLoaded", current, expression);
|
|
5078
|
+
return expression;
|
|
5079
|
+
}).catch((e) => {
|
|
5080
|
+
logger.warn(expressionManager.tag, `Failed to load expression: ${path}\n`, e);
|
|
5081
|
+
handleError(e);
|
|
5082
|
+
}).finally(() => {
|
|
5083
|
+
Live2DFactory.expressionTasksMap.get(expressionManager)?.delete(definition);
|
|
5084
|
+
});
|
|
5085
|
+
tasks.set(definition, task);
|
|
5086
|
+
}
|
|
5087
|
+
return task;
|
|
4963
5088
|
} catch (e) {
|
|
4964
5089
|
logger.warn(expressionManager.tag, `Failed to load expression at [${index}]\n`, e);
|
|
4965
5090
|
handleError(e);
|
|
@@ -14569,12 +14694,39 @@ let Live2DCubismFramework$2;
|
|
|
14569
14694
|
var CubismExpressionManager = class extends ExpressionManager {
|
|
14570
14695
|
expressionDataType = "arraybuffer";
|
|
14571
14696
|
definitions;
|
|
14697
|
+
/** The settings the listings read; `definitions` was filtered out of them, so both are kept in step. */
|
|
14698
|
+
cubismSettings;
|
|
14572
14699
|
/** Active fade entries in activation order; deactivated ones stay until fully faded out. */
|
|
14573
14700
|
entries = [];
|
|
14574
14701
|
constructor(settings, options) {
|
|
14575
14702
|
super(settings, options);
|
|
14703
|
+
this.cubismSettings = settings;
|
|
14576
14704
|
this.definitions = settings.expressions?.filter((expression) => !!expression && typeof expression.File === "string" && typeof expression.Name === "string") ?? [];
|
|
14577
14705
|
}
|
|
14706
|
+
addDefinition(definition) {
|
|
14707
|
+
const index = super.addDefinition(definition);
|
|
14708
|
+
this.cubismSettings.expressions ??= [];
|
|
14709
|
+
this.cubismSettings.expressions.push(definition);
|
|
14710
|
+
return index;
|
|
14711
|
+
}
|
|
14712
|
+
removeDefinition(index) {
|
|
14713
|
+
const definition = this.definitions[index];
|
|
14714
|
+
super.removeDefinition(index);
|
|
14715
|
+
const listed = this.cubismSettings.expressions;
|
|
14716
|
+
const at = definition && listed ? listed.indexOf(definition) : -1;
|
|
14717
|
+
if (listed && at >= 0) listed.splice(at, 1);
|
|
14718
|
+
}
|
|
14719
|
+
onExpressionReplaced(index, expression) {
|
|
14720
|
+
for (const entry of this.entries) if (entry.index === index) {
|
|
14721
|
+
entry.motion = expression;
|
|
14722
|
+
entry.fadeStartWeight = entry.weight;
|
|
14723
|
+
entry.fadeStartTime = -1;
|
|
14724
|
+
}
|
|
14725
|
+
}
|
|
14726
|
+
onDefinitionRemoved(index) {
|
|
14727
|
+
this.entries = this.entries.filter((entry) => entry.index !== index);
|
|
14728
|
+
for (const entry of this.entries) if (entry.index > index) entry.index -= 1;
|
|
14729
|
+
}
|
|
14578
14730
|
isFinished() {
|
|
14579
14731
|
return this.entries.length === 0;
|
|
14580
14732
|
}
|
|
@@ -14631,6 +14783,7 @@ var CubismExpressionManager = class extends ExpressionManager {
|
|
|
14631
14783
|
if (!this.entries.length) return false;
|
|
14632
14784
|
const states = /* @__PURE__ */ new Map();
|
|
14633
14785
|
for (const entry of this.entries) {
|
|
14786
|
+
if (this.muteAll || this.muted.has(entry.index)) continue;
|
|
14634
14787
|
const weight = entry.weight;
|
|
14635
14788
|
for (const parameter of entry.motion.getExpressionParameters()) {
|
|
14636
14789
|
if (parameter.parameterId == null) continue;
|
|
@@ -14693,9 +14846,14 @@ var CubismMotionManager = class extends MotionManager {
|
|
|
14693
14846
|
expressionManager;
|
|
14694
14847
|
eyeBlinkIds;
|
|
14695
14848
|
lipSyncIds = ["ParamMouthOpenY"];
|
|
14849
|
+
/** What each motion's curves write, read once when it is created. */
|
|
14850
|
+
meta = /* @__PURE__ */ new WeakMap();
|
|
14851
|
+
/** Eases the parameters a released idle animated back to rest; see {@link releaseIdle}. */
|
|
14852
|
+
release = null;
|
|
14696
14853
|
constructor(parent) {
|
|
14697
14854
|
super(parent);
|
|
14698
|
-
|
|
14855
|
+
parent.settings.motions ??= {};
|
|
14856
|
+
this.definitions = parent.settings.motions;
|
|
14699
14857
|
this.eyeBlinkIds = parent.settings.getEyeBlinkParameters() || [];
|
|
14700
14858
|
const lipSyncIds = parent.settings.getLipSyncParameters();
|
|
14701
14859
|
if (lipSyncIds?.length) this.lipSyncIds = lipSyncIds;
|
|
@@ -14703,7 +14861,7 @@ var CubismMotionManager = class extends MotionManager {
|
|
|
14703
14861
|
}
|
|
14704
14862
|
init(options) {
|
|
14705
14863
|
super.init(options);
|
|
14706
|
-
|
|
14864
|
+
this.expressionManager = new CubismExpressionManager(this.settings, options);
|
|
14707
14865
|
this.queueManager.setEventCallback((_caller, eventValue) => {
|
|
14708
14866
|
const value = eventValue == null ? "undefined" : String(eventValue);
|
|
14709
14867
|
this.emit(`motion:${value}`);
|
|
@@ -14722,6 +14880,7 @@ var CubismMotionManager = class extends MotionManager {
|
|
|
14722
14880
|
});
|
|
14723
14881
|
motion._motionData.curves = filtered;
|
|
14724
14882
|
motion._motionData.curveCount = filtered.length;
|
|
14883
|
+
this.meta.set(motion, motionMeta(motion, this.eyeBlinkIds));
|
|
14725
14884
|
}
|
|
14726
14885
|
this.queueManager.stopAllMotions();
|
|
14727
14886
|
return this.queueManager.startMotion(motion, false);
|
|
@@ -14729,14 +14888,24 @@ var CubismMotionManager = class extends MotionManager {
|
|
|
14729
14888
|
_stopAllMotions() {
|
|
14730
14889
|
this.queueManager.stopAllMotions();
|
|
14731
14890
|
}
|
|
14891
|
+
/**
|
|
14892
|
+
* Fade the queue's motions out over their own fade-out time instead of cutting them; each
|
|
14893
|
+
* ends like a natural finish (idle restart, `motionFinish`), which is how a looping one stops.
|
|
14894
|
+
* The motion's sound stops at once, as it does on {@link stopAllMotions}.
|
|
14895
|
+
*/
|
|
14896
|
+
fadeOutMotions() {
|
|
14897
|
+
for (const entry of this.queueManager.getCubismMotionQueueEntries()) entry.setFadeOut(entry._motion.getFadeOutTime());
|
|
14898
|
+
this.stopSpeaking();
|
|
14899
|
+
}
|
|
14732
14900
|
createMotion(data, group, definition) {
|
|
14733
14901
|
const shouldCheckMotionConsistency = !!this.parent.options.checkMotionConsistency;
|
|
14734
14902
|
const motion = CubismMotion.create(data, data.byteLength, void 0, void 0, shouldCheckMotionConsistency);
|
|
14735
14903
|
const json = new CubismMotionJson(data, data.byteLength);
|
|
14736
14904
|
motion.setLoop(json.isMotionLoop());
|
|
14905
|
+
this.meta.set(motion, motionMeta(motion, this.eyeBlinkIds));
|
|
14737
14906
|
const defaultFadingDuration = (group === this.groups.idle ? config.idleMotionFadingDuration : config.motionFadingDuration) / 1e3;
|
|
14738
|
-
if (json.
|
|
14739
|
-
if (json.
|
|
14907
|
+
if (!json.isExistMotionFadeInTime()) motion.setFadeInTime(definition.FadeInTime > 0 ? definition.FadeInTime : defaultFadingDuration);
|
|
14908
|
+
if (!json.isExistMotionFadeOutTime()) motion.setFadeOutTime(definition.FadeOutTime > 0 ? definition.FadeOutTime : defaultFadingDuration);
|
|
14740
14909
|
motion.setEffectIds(this.createIdList(this.eyeBlinkIds), this.createIdList(this.lipSyncIds));
|
|
14741
14910
|
return motion;
|
|
14742
14911
|
}
|
|
@@ -14749,7 +14918,68 @@ var CubismMotionManager = class extends MotionManager {
|
|
|
14749
14918
|
getSoundFile(definition) {
|
|
14750
14919
|
return definition.Sound;
|
|
14751
14920
|
}
|
|
14921
|
+
/** Whether the playing motion animates the eyes itself, so the engine's auto-blink must yield to it. */
|
|
14922
|
+
currentMotionBlinks() {
|
|
14923
|
+
return this.currentMeta()?.blinks ?? false;
|
|
14924
|
+
}
|
|
14925
|
+
/** The parameters the playing motion writes, for a host driver handing them over to it; null when none plays. */
|
|
14926
|
+
currentMotionParameters() {
|
|
14927
|
+
return this.currentMeta()?.animated ?? null;
|
|
14928
|
+
}
|
|
14929
|
+
/**
|
|
14930
|
+
* The parameters a playing one-shot (anything above IDLE) writes — what a host's tracking
|
|
14931
|
+
* yields to while it plays, as VTS ranks an animation above tracking. Null under the idle.
|
|
14932
|
+
*/
|
|
14933
|
+
oneShotParameters() {
|
|
14934
|
+
return this.oneShotPlaying() ? this.currentMotionParameters() : null;
|
|
14935
|
+
}
|
|
14936
|
+
/** A motion above idle priority is current — one the player started, never the idle. */
|
|
14937
|
+
oneShotPlaying() {
|
|
14938
|
+
return this.state.currentPriority > 1;
|
|
14939
|
+
}
|
|
14940
|
+
currentMotion() {
|
|
14941
|
+
const { currentGroup, currentIndex } = this.state;
|
|
14942
|
+
if (currentGroup === void 0 || currentIndex === void 0) return null;
|
|
14943
|
+
return this.motionGroups[currentGroup]?.[currentIndex] ?? null;
|
|
14944
|
+
}
|
|
14945
|
+
currentMeta() {
|
|
14946
|
+
const motion = this.currentMotion();
|
|
14947
|
+
return motion ? this.meta.get(motion) ?? null : null;
|
|
14948
|
+
}
|
|
14949
|
+
releaseIdle() {
|
|
14950
|
+
const motion = this.currentMotion();
|
|
14951
|
+
super.releaseIdle();
|
|
14952
|
+
if (motion) this.release = {
|
|
14953
|
+
ids: this.meta.get(motion)?.parameterIds ?? [],
|
|
14954
|
+
seconds: motion.getFadeOutTime(),
|
|
14955
|
+
ramp: null
|
|
14956
|
+
};
|
|
14957
|
+
}
|
|
14958
|
+
/** One frame of the ease-to-rest, ahead of the motions so a new idle's fade-in blends from it. */
|
|
14959
|
+
updateRelease(model, now) {
|
|
14960
|
+
const r = this.release;
|
|
14961
|
+
if (!r) return;
|
|
14962
|
+
if (r.ramp === null) {
|
|
14963
|
+
const count = model.getParameterCount();
|
|
14964
|
+
const indices = r.ids.map((id) => model.getParameterIndex(id)).filter((index) => index < count);
|
|
14965
|
+
r.ramp = {
|
|
14966
|
+
indices,
|
|
14967
|
+
from: indices.map((index) => model.getParameterValueByIndex(index)),
|
|
14968
|
+
startedAt: now
|
|
14969
|
+
};
|
|
14970
|
+
}
|
|
14971
|
+
const { indices, from, startedAt } = r.ramp;
|
|
14972
|
+
const t = r.seconds > 0 ? Math.min(1, (now - startedAt) / r.seconds) : 1;
|
|
14973
|
+
const w = CubismMath.getEasingSine(t);
|
|
14974
|
+
for (let i = 0; i < indices.length; i++) {
|
|
14975
|
+
const index = indices[i];
|
|
14976
|
+
const start = from[i];
|
|
14977
|
+
model.setParameterValueByIndex(index, start + (model.getParameterDefaultValue(index) - start) * w);
|
|
14978
|
+
}
|
|
14979
|
+
if (t >= 1) this.release = null;
|
|
14980
|
+
}
|
|
14752
14981
|
updateParameters(model, now) {
|
|
14982
|
+
this.updateRelease(model, now);
|
|
14753
14983
|
return this.queueManager.doUpdateMotion(model, now);
|
|
14754
14984
|
}
|
|
14755
14985
|
destroy() {
|
|
@@ -14770,6 +15000,23 @@ var CubismMotionManager = class extends MotionManager {
|
|
|
14770
15000
|
return ids.map((id) => idManager.getId(id));
|
|
14771
15001
|
}
|
|
14772
15002
|
};
|
|
15003
|
+
function motionMeta(motion, eyeBlinkIds) {
|
|
15004
|
+
const parameterIds = [];
|
|
15005
|
+
const animated = /* @__PURE__ */ new Set();
|
|
15006
|
+
let blinks = false;
|
|
15007
|
+
for (const curve of motion._motionData.curves) {
|
|
15008
|
+
const id = getCurveId$1(curve);
|
|
15009
|
+
if (curve.type === 1) {
|
|
15010
|
+
parameterIds.push(curve.id);
|
|
15011
|
+
if (id !== void 0) animated.add(id);
|
|
15012
|
+
} else if (curve.type === 0 && id === "EyeBlink") blinks = true;
|
|
15013
|
+
}
|
|
15014
|
+
return {
|
|
15015
|
+
parameterIds,
|
|
15016
|
+
animated,
|
|
15017
|
+
blinks: blinks || eyeBlinkIds.some((id) => animated.has(id))
|
|
15018
|
+
};
|
|
15019
|
+
}
|
|
14773
15020
|
function getCurveId$1(curve) {
|
|
14774
15021
|
if (!curve || typeof curve !== "object") return;
|
|
14775
15022
|
const id = curve.id;
|
|
@@ -15322,10 +15569,10 @@ var CubismInternalModel = class extends InternalModel {
|
|
|
15322
15569
|
dt /= 1e3;
|
|
15323
15570
|
now /= 1e3;
|
|
15324
15571
|
const model = this.coreModel;
|
|
15325
|
-
|
|
15572
|
+
this.updateMotions(model, now);
|
|
15326
15573
|
model.saveParameters();
|
|
15327
15574
|
this.motionManager.expressionManager?.update(model, now);
|
|
15328
|
-
if (!
|
|
15575
|
+
if (!this.motionManager.currentMotionBlinks()) this.eyeBlink?.updateParameters?.(model, dt);
|
|
15329
15576
|
this.updateFocus();
|
|
15330
15577
|
this.updateNaturalMovements(dt * 1e3, now * 1e3);
|
|
15331
15578
|
if (this.lipSync && this.motionManager.currentAudio) {
|