@rig-c/play 1.16.0 → 1.17.1
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/README.md +40 -1302
- package/dist/MeshGlBlitter.d.ts +1 -0
- package/dist/MeshGlBlitter.d.ts.map +1 -1
- package/dist/index.js +198 -21
- package/dist/index.js.map +1 -1
- package/dist/loadModel.d.ts.map +1 -1
- package/dist/player.d.ts +81 -3
- package/dist/player.d.ts.map +1 -1
- package/package.json +2 -1
package/dist/MeshGlBlitter.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MeshGlBlitter.d.ts","sourceRoot":"","sources":["../src/MeshGlBlitter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,MAAM,WAAW,WAAW;IAC1B,2EAA2E;IAC3E,MAAM,EAAE,iBAAiB,CAAC;IAC1B,+EAA+E;IAC/E,IAAI,EAAE,gBAAgB,CAAC;IACvB;;;;;OAKG;IACH,GAAG,EAAE,OAAO,CAAC;IACb,mFAAmF;IACnF,QAAQ,EAAE,YAAY,CAAC;IACvB,mDAAmD;IACnD,GAAG,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;IACvB,gDAAgD;IAChD,SAAS,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;IAC7B,uCAAuC;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,iFAAiF;IACjF,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,gBAAgB,CAAC;IACvB,sCAAsC;IACtC,GAAG,EAAE,OAAO,CAAC;IACb,KAAK,EAAE,SAAS,CAAC;IACjB,8DAA8D;IAC9D,KAAK,EAAE,MAAM,CAAC;IACd,gDAAgD;IAChD,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,YAAY,CAAC;IACnB,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC;IACf,IAAI,EAAE,QAAQ,EAAE,CAAC;CAClB;
|
|
1
|
+
{"version":3,"file":"MeshGlBlitter.d.ts","sourceRoot":"","sources":["../src/MeshGlBlitter.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -6790,11 +6790,12 @@ const VERTEX_SHADER = `
|
|
|
6790
6790
|
attribute vec2 aPos;
|
|
6791
6791
|
attribute vec2 aUV;
|
|
6792
6792
|
uniform vec2 uResolution;
|
|
6793
|
+
uniform vec2 uOrigin;
|
|
6793
6794
|
varying vec2 vUV;
|
|
6794
6795
|
void main() {
|
|
6795
6796
|
// aPos is in offscreen-canvas device px, top-left origin (matching the
|
|
6796
6797
|
// top-left-origin drawImage read of the same rect).
|
|
6797
|
-
vec2 clip = aPos / uResolution * 2.0 - 1.0;
|
|
6798
|
+
vec2 clip = (aPos - uOrigin) / uResolution * 2.0 - 1.0;
|
|
6798
6799
|
gl_Position = vec4(clip.x, -clip.y, 0.0, 1.0);
|
|
6799
6800
|
vUV = aUV;
|
|
6800
6801
|
}`;
|
|
@@ -6818,6 +6819,7 @@ class MeshGlBlitter {
|
|
|
6818
6819
|
canvas = document.createElement("canvas");
|
|
6819
6820
|
gl;
|
|
6820
6821
|
uResolution;
|
|
6822
|
+
uOrigin;
|
|
6821
6823
|
meshProgram;
|
|
6822
6824
|
meshPos;
|
|
6823
6825
|
meshUV;
|
|
@@ -6860,8 +6862,10 @@ class MeshGlBlitter {
|
|
|
6860
6862
|
this.meshProgram = program;
|
|
6861
6863
|
gl.useProgram(program);
|
|
6862
6864
|
const uResolution = gl.getUniformLocation(program, "uResolution");
|
|
6863
|
-
|
|
6865
|
+
const uOrigin = gl.getUniformLocation(program, "uOrigin");
|
|
6866
|
+
if (!uResolution || !uOrigin) throw new Error("mesh program uniforms not found");
|
|
6864
6867
|
this.uResolution = uResolution;
|
|
6868
|
+
this.uOrigin = uOrigin;
|
|
6865
6869
|
gl.uniform1i(gl.getUniformLocation(program, "uTex"), 0);
|
|
6866
6870
|
this.meshPos = gl.getAttribLocation(program, "aPos");
|
|
6867
6871
|
this.meshUV = gl.getAttribLocation(program, "aUV");
|
|
@@ -6924,11 +6928,8 @@ class MeshGlBlitter {
|
|
|
6924
6928
|
Math.ceil(Math.max(height, this.canvas.height) / GROW_STEP) * GROW_STEP
|
|
6925
6929
|
);
|
|
6926
6930
|
}
|
|
6927
|
-
const bufW = this.canvas.width;
|
|
6928
6931
|
const bufH = this.canvas.height;
|
|
6929
|
-
gl.viewport(0, 0, bufW, bufH);
|
|
6930
6932
|
this.bindMesh();
|
|
6931
|
-
gl.uniform2f(this.uResolution, bufW, bufH);
|
|
6932
6933
|
let floats = 0;
|
|
6933
6934
|
for (const job of jobs) floats += job.triangles.length * 4;
|
|
6934
6935
|
if (this.vertexData.length < floats) {
|
|
@@ -6957,6 +6958,9 @@ class MeshGlBlitter {
|
|
|
6957
6958
|
gl.bindTexture(gl.TEXTURE_2D, this.textureFor(job.page, job.pma));
|
|
6958
6959
|
boundPage = job.page;
|
|
6959
6960
|
}
|
|
6961
|
+
gl.viewport(packX[i], bufH - packY[i] - job.height, job.width, job.height);
|
|
6962
|
+
gl.uniform2f(this.uResolution, job.width, job.height);
|
|
6963
|
+
gl.uniform2f(this.uOrigin, packX[i], packY[i]);
|
|
6960
6964
|
gl.scissor(packX[i], bufH - packY[i] - job.height, job.width, job.height);
|
|
6961
6965
|
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
6962
6966
|
gl.drawArrays(gl.TRIANGLES, first, job.triangles.length);
|
|
@@ -8579,7 +8583,7 @@ function readSetupMesh(assets, target) {
|
|
|
8579
8583
|
return readSetupMesh$1(underNoSkin(assets.document.doc), target);
|
|
8580
8584
|
}
|
|
8581
8585
|
const SETS_SPECS = ["spine-parts-sets/1", "rig-parts-sets/1"];
|
|
8582
|
-
const SET_KEYS_NOT_POSED = ["drawnLength", "registration", "threshold"];
|
|
8586
|
+
const SET_KEYS_NOT_POSED = ["drawnLength", "registration", "threshold", "focus"];
|
|
8583
8587
|
const SET_KEYS = ["slots", "hides", "drawn", ...SET_KEYS_NOT_POSED];
|
|
8584
8588
|
const SETS_DOCUMENT_KEYS = ["spec", "sets", "threshold"];
|
|
8585
8589
|
function isRecord(value) {
|
|
@@ -8701,6 +8705,11 @@ function readSetsText(text, where, doc) {
|
|
|
8701
8705
|
drawn[boneName] = degrees;
|
|
8702
8706
|
}
|
|
8703
8707
|
}
|
|
8708
|
+
if (item.focus !== void 0) {
|
|
8709
|
+
if (!Array.isArray(item.focus) || item.focus.length !== 2 || typeof item.focus[0] !== "number" || !Number.isFinite(item.focus[0]) || typeof item.focus[1] !== "number" || !Number.isFinite(item.focus[1])) {
|
|
8710
|
+
throw new Error(`${where}: set "${name}" "focus" must be [x, y] of two finite numbers`);
|
|
8711
|
+
}
|
|
8712
|
+
}
|
|
8704
8713
|
out.push(
|
|
8705
8714
|
Object.freeze({
|
|
8706
8715
|
name,
|
|
@@ -8962,6 +8971,20 @@ function frameOf(source, pose, color = [1, 1, 1, 1], draws = () => true) {
|
|
|
8962
8971
|
return { order };
|
|
8963
8972
|
}
|
|
8964
8973
|
const DEFAULT_ENVIRONMENT = Object.freeze({ wind: Object.freeze([1, 0]), gravity: Object.freeze([0, 1]) });
|
|
8974
|
+
function setEaseOption(call, options) {
|
|
8975
|
+
const ease = options?.ease;
|
|
8976
|
+
if (ease === void 0) return 0;
|
|
8977
|
+
if (typeof ease !== "number" || !Number.isFinite(ease) || ease < 0) {
|
|
8978
|
+
throw new Error(`${call}: options.ease must be a finite number of seconds at or above 0, got ${typeof ease === "number" ? String(ease) : JSON.stringify(ease)}`);
|
|
8979
|
+
}
|
|
8980
|
+
return ease;
|
|
8981
|
+
}
|
|
8982
|
+
function easeProgress(ease) {
|
|
8983
|
+
return Math.min(Math.max(ease.elapsed / ease.duration, 0), 1);
|
|
8984
|
+
}
|
|
8985
|
+
function smoothstep(u) {
|
|
8986
|
+
return u * u * (3 - 2 * u);
|
|
8987
|
+
}
|
|
8965
8988
|
function setPhysicsOption(call, options) {
|
|
8966
8989
|
if (options === void 0) return void 0;
|
|
8967
8990
|
if (options === null || typeof options !== "object") {
|
|
@@ -9285,6 +9308,12 @@ class RigPlayer {
|
|
|
9285
9308
|
drawnPose = null;
|
|
9286
9309
|
/** Test-only (#173's planted control): `carry` maps every state through the identity instead of the anchor's turn. Not API. */
|
|
9287
9310
|
carryPlant = false;
|
|
9311
|
+
/** An eased set switch in flight (#174), or null. */
|
|
9312
|
+
easing = null;
|
|
9313
|
+
/** The last positive step `advance` took: what predicts the next one when an ease starts. */
|
|
9314
|
+
lastStep = 0;
|
|
9315
|
+
/** Test-only (#174's planted controls): swap the attachments at u = 0, or ease by w = 1.2u (1 at u = 1). Not API. */
|
|
9316
|
+
easePlant = null;
|
|
9288
9317
|
adjustFn = null;
|
|
9289
9318
|
/** Current preview patches, reapplied through the public core API on replay; no edit history. */
|
|
9290
9319
|
physicsPatches = /* @__PURE__ */ new Map();
|
|
@@ -9295,7 +9324,8 @@ class RigPlayer {
|
|
|
9295
9324
|
draws = () => true;
|
|
9296
9325
|
/** Handed to every track the player opens; reads the current `adjust` so setting it later needs no reopen. */
|
|
9297
9326
|
adjustHook = (locals, ctx) => {
|
|
9298
|
-
if (this.
|
|
9327
|
+
if (this.easing !== null) this.applyEase(this.easing, locals);
|
|
9328
|
+
else if (this.drawnPose !== null) for (const [bone2, rotation] of this.drawnPose) locals.bone(bone2).rotation = rotation;
|
|
9299
9329
|
if (this.adjustFn !== null) this.adjustFn(locals, ctx);
|
|
9300
9330
|
};
|
|
9301
9331
|
listeners = {
|
|
@@ -9504,11 +9534,12 @@ class RigPlayer {
|
|
|
9504
9534
|
if (time > 0) this.stepTo(time);
|
|
9505
9535
|
return tracks;
|
|
9506
9536
|
}
|
|
9507
|
-
/** Stores `raw` and applies `activeSet
|
|
9537
|
+
/** Stores `raw` and applies the shown set (`activeSet`, or before an ease's swap the composition it eases from). Zero work when none. */
|
|
9508
9538
|
setRawPose(raw) {
|
|
9509
9539
|
this.rawPose = raw;
|
|
9510
9540
|
this.heldParameters = null;
|
|
9511
|
-
|
|
9541
|
+
const set = this.shownSet();
|
|
9542
|
+
this.pose = set === null ? raw : applySetToPose(this.view, raw, set);
|
|
9512
9543
|
this.poseGeneration++;
|
|
9513
9544
|
this.cachedIdentity = null;
|
|
9514
9545
|
}
|
|
@@ -9626,6 +9657,7 @@ class RigPlayer {
|
|
|
9626
9657
|
if (this.animation === null) throw new Error("seek before play: no animation is open");
|
|
9627
9658
|
if (!(time >= 0) || !Number.isFinite(time)) throw new Error(`seek time must be a finite time at or above 0, got ${String(time)}`);
|
|
9628
9659
|
if (this.beyondBase) throw new Error("seek is defined for a single animation on track 0; after a mix, another track, a queue or a clear the player cannot replay to a time — step it forward with frame/advance instead");
|
|
9660
|
+
this.easing = null;
|
|
9629
9661
|
const cache = this.ensureSeekCache();
|
|
9630
9662
|
const stepper = this.tracks ?? this.track;
|
|
9631
9663
|
const exactHit = cache.exact.get(time);
|
|
@@ -9781,6 +9813,8 @@ class RigPlayer {
|
|
|
9781
9813
|
if (this.pose === null) return;
|
|
9782
9814
|
const step = dt * this.timeScale;
|
|
9783
9815
|
if (this.paused || !(step > 0)) return;
|
|
9816
|
+
this.lastStep = step;
|
|
9817
|
+
if (this.easing !== null) this.advanceEase(this.easing, step);
|
|
9784
9818
|
const tracks = this.tracks;
|
|
9785
9819
|
if (tracks !== null) {
|
|
9786
9820
|
const before2 = [];
|
|
@@ -9823,6 +9857,7 @@ class RigPlayer {
|
|
|
9823
9857
|
this.source = frameSource(view, this.read.parsed);
|
|
9824
9858
|
this.skinName = name;
|
|
9825
9859
|
this.setup = null;
|
|
9860
|
+
this.easing = null;
|
|
9826
9861
|
this.clearSeekCache();
|
|
9827
9862
|
if (this.animation !== null && (this.track !== null || this.tracks !== null)) {
|
|
9828
9863
|
const time = this.time;
|
|
@@ -9881,7 +9916,8 @@ class RigPlayer {
|
|
|
9881
9916
|
throw new Error(`cannot setSet(${JSON.stringify(name)}): this model has no sets.json`);
|
|
9882
9917
|
}
|
|
9883
9918
|
const physics = setPhysicsOption("setSet", options);
|
|
9884
|
-
|
|
9919
|
+
const ease = setEaseOption("setSet", options);
|
|
9920
|
+
this.activate(name === null ? [] : [this.namedSet(name)], physics, ease);
|
|
9885
9921
|
}
|
|
9886
9922
|
/**
|
|
9887
9923
|
* Makes exactly `names` the active attachment sets (#168), together: each
|
|
@@ -9903,19 +9939,42 @@ class RigPlayer {
|
|
|
9903
9939
|
* under a re-posed bone. A later `seek` replays from the start with the sets
|
|
9904
9940
|
* then active and does not reproduce a carried switch. `setSet`, `addSet`
|
|
9905
9941
|
* and `removeSet` take the same option.
|
|
9942
|
+
*
|
|
9943
|
+
* `options.ease` (#174, seconds, a finite number at or above 0) eases the
|
|
9944
|
+
* bones the call re-poses instead of snapping them. Each bone whose drawn
|
|
9945
|
+
* rotation the call adds, removes or changes turns from what it shows to
|
|
9946
|
+
* its new local rotation along the shorter way, as
|
|
9947
|
+
* `start + shortestΔ(start, end) · smoothstep(elapsed / ease)`: going on,
|
|
9948
|
+
* from the clip's value at the time to the drawn local; going off, from the
|
|
9949
|
+
* drawn local to the clip's value. The attachments the pose draws stay the
|
|
9950
|
+
* previous ones until the frame whose `elapsed / ease` is nearest ½ (the
|
|
9951
|
+
* next step predicted from the current one; a tie goes to the later frame),
|
|
9952
|
+
* then become the call's; `activeSets` and `slotIdentity().sets` name the
|
|
9953
|
+
* call's sets at once, while `slotIdentity().slots` follows what is drawn.
|
|
9954
|
+
* On the frame the elapsed time reaches `ease` the player is in exactly the
|
|
9955
|
+
* instant switch's state. The elapsed time is the player's step
|
|
9956
|
+
* (`dt × timeScale`), so a paused player or a zero step holds the ease;
|
|
9957
|
+
* before `play`, or when no bone's rotation changes, the switch is instant.
|
|
9958
|
+
* A call during an ease eases again from the angles shown (a call without
|
|
9959
|
+
* `ease` lands it at once), and `seek` or a skin change lands it at once.
|
|
9960
|
+
* With `physics: 'carry'` the chains under the eased bones are carried on
|
|
9961
|
+
* every eased frame, by the turn that frame's blend makes; with `'reset'`
|
|
9962
|
+
* they restart once, at the call. `0` or absent is the instant switch on
|
|
9963
|
+
* the same code path; anything else throws by name before anything changes.
|
|
9906
9964
|
*/
|
|
9907
9965
|
setSets(names, options) {
|
|
9908
9966
|
if (this.setList.length === 0) {
|
|
9909
9967
|
throw new Error(`cannot setSets(${JSON.stringify(names)}): this model has no sets.json`);
|
|
9910
9968
|
}
|
|
9911
9969
|
const physics = setPhysicsOption("setSets", options);
|
|
9970
|
+
const ease = setEaseOption("setSets", options);
|
|
9912
9971
|
const sets = names.map((name) => this.namedSet(name));
|
|
9913
9972
|
for (let i = 0; i < sets.length; i++) {
|
|
9914
9973
|
for (let j = 0; j < i; j++) {
|
|
9915
9974
|
if (sets[i] === sets[j]) throw new Error(`set ${JSON.stringify(sets[i].name)} is named twice in setSets`);
|
|
9916
9975
|
}
|
|
9917
9976
|
}
|
|
9918
|
-
this.activate(sets, physics);
|
|
9977
|
+
this.activate(sets, physics, ease);
|
|
9919
9978
|
}
|
|
9920
9979
|
/**
|
|
9921
9980
|
* Activates `name` beside the sets already active (#168): a set already
|
|
@@ -9927,9 +9986,10 @@ class RigPlayer {
|
|
|
9927
9986
|
throw new Error(`cannot addSet(${JSON.stringify(name)}): this model has no sets.json`);
|
|
9928
9987
|
}
|
|
9929
9988
|
const physics = setPhysicsOption("addSet", options);
|
|
9989
|
+
const ease = setEaseOption("addSet", options);
|
|
9930
9990
|
const found = this.namedSet(name);
|
|
9931
9991
|
if (this.activeList.includes(found)) return;
|
|
9932
|
-
this.activate([...this.activeList, found], physics);
|
|
9992
|
+
this.activate([...this.activeList, found], physics, ease);
|
|
9933
9993
|
}
|
|
9934
9994
|
/**
|
|
9935
9995
|
* Deactivates `name`, keeping every other active set (#168). An unknown name
|
|
@@ -9940,9 +10000,10 @@ class RigPlayer {
|
|
|
9940
10000
|
throw new Error(`cannot removeSet(${JSON.stringify(name)}): this model has no sets.json`);
|
|
9941
10001
|
}
|
|
9942
10002
|
const physics = setPhysicsOption("removeSet", options);
|
|
10003
|
+
const ease = setEaseOption("removeSet", options);
|
|
9943
10004
|
const found = this.namedSet(name);
|
|
9944
10005
|
if (!this.activeList.includes(found)) return;
|
|
9945
|
-
this.activate(this.activeList.filter((s) => s !== found), physics);
|
|
10006
|
+
this.activate(this.activeList.filter((s) => s !== found), physics, ease);
|
|
9946
10007
|
}
|
|
9947
10008
|
/** The declared set named `name`, or a throw naming it and the available sets. */
|
|
9948
10009
|
namedSet(name) {
|
|
@@ -9990,16 +10051,16 @@ class RigPlayer {
|
|
|
9990
10051
|
}
|
|
9991
10052
|
/**
|
|
9992
10053
|
* The physics under the bones a set switch re-poses (#173), before the
|
|
9993
|
-
* repose step: the
|
|
9994
|
-
*
|
|
10054
|
+
* repose step: the `changed` bones' rotations went from what the held pose
|
|
10055
|
+
* shows to what the player now applies (`drawnPose`, or an ease's next
|
|
10056
|
+
* blend, #174), and every physics constraint whose bone is one of the
|
|
9995
10057
|
* changed bones or a descendant of one is carried or reset. Nothing to do
|
|
9996
10058
|
* before `play` (the setup pose carries no physics state) or when no
|
|
9997
10059
|
* constraint hangs under a changed bone.
|
|
9998
10060
|
*/
|
|
9999
|
-
handOverPhysics(
|
|
10061
|
+
handOverPhysics(changed, mode) {
|
|
10000
10062
|
const stepper = this.tracks ?? this.track;
|
|
10001
10063
|
if (stepper === null || this.pose === null || this.rawPose === null) return;
|
|
10002
|
-
const changed = drawnChanged(before, after);
|
|
10003
10064
|
if (changed.size === 0) return;
|
|
10004
10065
|
const parentOf = new Map(this.view.bones.map((b) => [b.name, b.parent]));
|
|
10005
10066
|
const anchorOf = (bone2) => {
|
|
@@ -10079,11 +10140,112 @@ class RigPlayer {
|
|
|
10079
10140
|
s.ty = c * tx + d * s.ty;
|
|
10080
10141
|
}
|
|
10081
10142
|
}
|
|
10143
|
+
/**
|
|
10144
|
+
* The composition the held pose draws: `activeSet`, except before an ease's
|
|
10145
|
+
* swap (#174), when it is the composition the ease started from.
|
|
10146
|
+
*/
|
|
10147
|
+
shownSet() {
|
|
10148
|
+
const easing = this.easing;
|
|
10149
|
+
return easing === null || easing.swapped ? this.activeSet : easing.fromSet;
|
|
10150
|
+
}
|
|
10151
|
+
/** The local an eased bone shows at the ease's current progress, given the clip's local `clip`. */
|
|
10152
|
+
easedLocal(easing, from, to, clip) {
|
|
10153
|
+
const start = from ?? clip;
|
|
10154
|
+
const end = to ?? clip;
|
|
10155
|
+
const u = easeProgress(easing);
|
|
10156
|
+
const w = this.easePlant === "overshoot" ? u < 1 ? 1.2 * u : 1 : smoothstep(u);
|
|
10157
|
+
return start + normaliseDegrees(end - start) * w;
|
|
10158
|
+
}
|
|
10159
|
+
/**
|
|
10160
|
+
* The ease's part of `adjustHook` (#174): every drawn bone the ease does not
|
|
10161
|
+
* turn takes its drawn local, as without an ease; every eased bone reads
|
|
10162
|
+
* the clip's local (what the timelines left) and then takes its blend.
|
|
10163
|
+
*/
|
|
10164
|
+
applyEase(easing, locals) {
|
|
10165
|
+
for (const bone2 of easing.bones.keys()) easing.clip.set(bone2, locals.bone(bone2).rotation);
|
|
10166
|
+
if (this.drawnPose !== null) {
|
|
10167
|
+
for (const [bone2, rotation] of this.drawnPose) if (!easing.bones.has(bone2)) locals.bone(bone2).rotation = rotation;
|
|
10168
|
+
}
|
|
10169
|
+
for (const [bone2, { from, to }] of easing.bones) {
|
|
10170
|
+
locals.bone(bone2).rotation = this.easedLocal(easing, from, to, easing.clip.get(bone2));
|
|
10171
|
+
}
|
|
10172
|
+
}
|
|
10173
|
+
/**
|
|
10174
|
+
* Starts an eased switch (#174) to the sets already made active, from what
|
|
10175
|
+
* the held pose shows: an eased bone's start is the local it shows (fixed),
|
|
10176
|
+
* or the clip's value for a bone that showed the clip's; its end is the new
|
|
10177
|
+
* drawn local, or the clip's value for a bone whose set went. Returns false,
|
|
10178
|
+
* changing nothing, when there is nothing to ease — before `play` (no step
|
|
10179
|
+
* to ease over) or when no bone's rotation changes — and the call is then
|
|
10180
|
+
* an instant one.
|
|
10181
|
+
*/
|
|
10182
|
+
startEase(duration, shownBefore, drawn, physics) {
|
|
10183
|
+
const stepper = this.tracks ?? this.track;
|
|
10184
|
+
if (stepper === null || this.pose === null) return false;
|
|
10185
|
+
const before = this.drawnPose;
|
|
10186
|
+
const after = drawn === void 0 ? null : drawnLocals(this.view, drawn);
|
|
10187
|
+
const previous = this.easing;
|
|
10188
|
+
const candidates = drawnChanged(before, after);
|
|
10189
|
+
if (previous !== null) for (const bone2 of previous.bones.keys()) candidates.add(bone2);
|
|
10190
|
+
const bones = /* @__PURE__ */ new Map();
|
|
10191
|
+
for (const bone2 of candidates) {
|
|
10192
|
+
const to = after?.get(bone2) ?? null;
|
|
10193
|
+
let from;
|
|
10194
|
+
const eased = previous?.bones.get(bone2);
|
|
10195
|
+
if (previous !== null && eased !== void 0) {
|
|
10196
|
+
from = previous.elapsed > 0 ? this.easedLocal(previous, eased.from, eased.to, previous.clip.get(bone2)) : eased.from;
|
|
10197
|
+
} else {
|
|
10198
|
+
from = before?.get(bone2) ?? null;
|
|
10199
|
+
}
|
|
10200
|
+
if (from === to) continue;
|
|
10201
|
+
bones.set(bone2, { from, to });
|
|
10202
|
+
}
|
|
10203
|
+
if (bones.size === 0) return false;
|
|
10204
|
+
const easing = {
|
|
10205
|
+
duration,
|
|
10206
|
+
elapsed: 0,
|
|
10207
|
+
bones,
|
|
10208
|
+
clip: /* @__PURE__ */ new Map(),
|
|
10209
|
+
fromSet: shownBefore,
|
|
10210
|
+
swapped: false,
|
|
10211
|
+
physics
|
|
10212
|
+
};
|
|
10213
|
+
if (this.easePlant === "swap-at-0" || this.swapsNow(0, this.lastStep / duration)) easing.swapped = true;
|
|
10214
|
+
this.easing = easing;
|
|
10215
|
+
this.drawnPose = after;
|
|
10216
|
+
if (physics === "reset") this.handOverPhysics(new Set(bones.keys()), "reset");
|
|
10217
|
+
this.reposeDrawn();
|
|
10218
|
+
return true;
|
|
10219
|
+
}
|
|
10220
|
+
/**
|
|
10221
|
+
* Whether the frame at progress `u` is the one nearest ½, the next frame
|
|
10222
|
+
* being at `next`: it is when `u` reached ½ or the next one would be
|
|
10223
|
+
* farther from it; a tie (within 1e-9) goes to the later frame.
|
|
10224
|
+
*/
|
|
10225
|
+
swapsNow(u, next) {
|
|
10226
|
+
if (u >= 0.5) return true;
|
|
10227
|
+
return next > u && 0.5 - u < next - 0.5 - 1e-9;
|
|
10228
|
+
}
|
|
10229
|
+
/**
|
|
10230
|
+
* One step of an eased switch (#174), before the track steps: the elapsed
|
|
10231
|
+
* time advances by the step, the attachments swap when this is the frame
|
|
10232
|
+
* nearest ½ (`swapsNow`, the next step predicted by this one), and the
|
|
10233
|
+
* ease ends — the instant switch's state, `drawnPose` alone — on the frame
|
|
10234
|
+
* it reaches its duration. With `physics: 'carry'`, the chains under the
|
|
10235
|
+
* eased bones are carried by the turn this frame's blend makes, read by a
|
|
10236
|
+
* trial `step(0)` at the previous time (`handOverPhysics`).
|
|
10237
|
+
*/
|
|
10238
|
+
advanceEase(easing, step) {
|
|
10239
|
+
easing.elapsed += step;
|
|
10240
|
+
if (easing.elapsed >= easing.duration) this.easing = null;
|
|
10241
|
+
else if (!easing.swapped && this.swapsNow(easing.elapsed / easing.duration, (easing.elapsed + step) / easing.duration)) easing.swapped = true;
|
|
10242
|
+
if (easing.physics === "carry") this.handOverPhysics(new Set(easing.bones.keys()), "carry");
|
|
10243
|
+
}
|
|
10082
10244
|
/**
|
|
10083
10245
|
* Makes `sets` (distinct) the active sets: refuses any two touching one slot
|
|
10084
10246
|
* before changing anything, then applies their composition to the held pose.
|
|
10085
10247
|
*/
|
|
10086
|
-
activate(sets, physics) {
|
|
10248
|
+
activate(sets, physics, ease = 0) {
|
|
10087
10249
|
for (let i = 0; i < sets.length; i++) {
|
|
10088
10250
|
for (let j = 0; j < i; j++) {
|
|
10089
10251
|
const other = this.touched(sets[j]);
|
|
@@ -10105,14 +10267,27 @@ class RigPlayer {
|
|
|
10105
10267
|
}
|
|
10106
10268
|
}
|
|
10107
10269
|
const drawnBefore = this.activeSet?.drawn;
|
|
10270
|
+
const shownBefore = this.shownSet();
|
|
10108
10271
|
this.activeList = this.setList.filter((s) => sets.includes(s));
|
|
10109
10272
|
this.activeSet = composeSets(this.activeList);
|
|
10110
10273
|
this.clearSeekCache();
|
|
10111
10274
|
const drawn = this.activeSet?.drawn;
|
|
10275
|
+
if (ease > 0 && this.startEase(ease, shownBefore, drawn, physics)) return;
|
|
10276
|
+
const easing = this.easing;
|
|
10277
|
+
if (easing !== null) {
|
|
10278
|
+
this.easing = null;
|
|
10279
|
+
const posedBefore = this.drawnPose;
|
|
10280
|
+
this.drawnPose = drawn === void 0 ? null : drawnLocals(this.view, drawn);
|
|
10281
|
+
const changed = drawnChanged(posedBefore, this.drawnPose);
|
|
10282
|
+
for (const bone2 of easing.bones.keys()) changed.add(bone2);
|
|
10283
|
+
if (physics !== void 0) this.handOverPhysics(changed, physics);
|
|
10284
|
+
this.reposeDrawn();
|
|
10285
|
+
return;
|
|
10286
|
+
}
|
|
10112
10287
|
if (drawn !== drawnBefore && !(drawn === void 0 && drawnBefore === void 0)) {
|
|
10113
10288
|
const posedBefore = this.drawnPose;
|
|
10114
10289
|
this.drawnPose = drawn === void 0 ? null : drawnLocals(this.view, drawn);
|
|
10115
|
-
if (physics !== void 0) this.handOverPhysics(posedBefore, this.drawnPose, physics);
|
|
10290
|
+
if (physics !== void 0) this.handOverPhysics(drawnChanged(posedBefore, this.drawnPose), physics);
|
|
10116
10291
|
this.reposeDrawn();
|
|
10117
10292
|
return;
|
|
10118
10293
|
}
|
|
@@ -10304,9 +10479,10 @@ class RigPlayer {
|
|
|
10304
10479
|
const setupShownSlots = new Set(setupPose.shown.map((s) => s.slot));
|
|
10305
10480
|
const rawRowBySlot = new Map(raw.slots.map((r) => [r[0], r]));
|
|
10306
10481
|
const slots = [];
|
|
10482
|
+
const shownSet = this.shownSet();
|
|
10307
10483
|
for (let z = 0; z < pose.drawOrder.length; z++) {
|
|
10308
10484
|
const slotName = pose.drawOrder[z];
|
|
10309
|
-
if (
|
|
10485
|
+
if (shownSet !== null && hiddenBySet(slotName, shownSet)) {
|
|
10310
10486
|
slots.push(Object.freeze({
|
|
10311
10487
|
slot: slotName,
|
|
10312
10488
|
z,
|
|
@@ -10341,7 +10517,7 @@ class RigPlayer {
|
|
|
10341
10517
|
continue;
|
|
10342
10518
|
}
|
|
10343
10519
|
const rawRow = rawRowBySlot.get(slotName);
|
|
10344
|
-
const overriddenBySet =
|
|
10520
|
+
const overriddenBySet = shownSet !== null && slotName in shownSet.slots;
|
|
10345
10521
|
const setupHadAttachment = setupShownSlots.has(slotName);
|
|
10346
10522
|
const isTimelineNull = !overriddenBySet && (setupHadAttachment && rawRow?.[1] === null || this.isSlotKeyedNullByTimeline(slotName, raw.animationTime));
|
|
10347
10523
|
slots.push(Object.freeze({
|
|
@@ -10406,6 +10582,7 @@ class RigPlayer {
|
|
|
10406
10582
|
this.renderer.dispose();
|
|
10407
10583
|
this.clearSeekCache();
|
|
10408
10584
|
this.cachedIdentity = null;
|
|
10585
|
+
this.easing = null;
|
|
10409
10586
|
this.tracks = null;
|
|
10410
10587
|
this.track = null;
|
|
10411
10588
|
this.pose = null;
|