spine-html 1.13.0 → 1.14.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/README.md +73 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +200 -59
- package/dist/index.js.map +1 -1
- package/dist/player.d.ts +47 -1
- package/dist/player.d.ts.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -685,6 +685,77 @@ firing events or drawing. A non-finite value, a bone the document does not have,
|
|
|
685
685
|
or an error the function throws refuses that step in the core's words, and the
|
|
686
686
|
track is reopened by the next `play` or `seek`.
|
|
687
687
|
|
|
688
|
+
The callback also receives a `BoneAdjustContext` from the core:
|
|
689
|
+
|
|
690
|
+
```ts
|
|
691
|
+
player.adjust = (bones, ctx) => {
|
|
692
|
+
bones.bone('head').rotation = 12 * (ctx.trackTime ?? 0);
|
|
693
|
+
};
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
`ctx.trackTime` is the **current track-0 entry's unwrapped seconds**, starting at
|
|
697
|
+
0 for each new entry. Loops do not wrap it, and a nonlooping entry keeps counting
|
|
698
|
+
past its duration. `ctx.animation` names that entry. Without a track-0 entry both
|
|
699
|
+
are null; an explicit empty entry has **numeric trackTime and null animation**.
|
|
700
|
+
Upper tracks supply no fallback. `ctx.globalTime` is the core walk's running sum
|
|
701
|
+
of steps in seconds, so it can differ from a new entry's time. Neither clock is
|
|
702
|
+
wall time, wrapped animation time, nor the physics clock's small lead. Unary
|
|
703
|
+
callbacks remain compatible. The hook runs once after composition, including a
|
|
704
|
+
crossfade, rather than once for each contributing entry.
|
|
705
|
+
|
|
706
|
+
Seek evaluates its replay times (`seekStep` plus any final remainder), independent
|
|
707
|
+
of the currently displayed player time. Exact cache hits may reuse a pose without
|
|
708
|
+
calling the hook. A continuous walk and seek are bit-identical for an **unchanged
|
|
709
|
+
patch and matched history**: the same animation, environment, adjustment, reset
|
|
710
|
+
and step partition. Closed-over callback state still needs `repose()` or the
|
|
711
|
+
adjust setter to invalidate cached poses. Pause, zero advance and renderer mode
|
|
712
|
+
changes add no evaluations; explicit `repose()` keeps the same context time.
|
|
713
|
+
|
|
714
|
+
### Live physics parameters
|
|
715
|
+
|
|
716
|
+
```ts
|
|
717
|
+
player.play('hang');
|
|
718
|
+
player.setPhysicsParameters('link_1_phys', { damping: 0.4, wind: 8 });
|
|
719
|
+
player.readPhysicsParameters('link_1_phys'); // { override, effective }
|
|
720
|
+
player.clearPhysicsParameters('link_1_phys');
|
|
721
|
+
```
|
|
722
|
+
|
|
723
|
+
`setPhysicsParameters(name, patch)` forwards a per-player patch of `strength`,
|
|
724
|
+
`damping`, `inertia`, `wind`, `gravity`, and `mix` to the core. Omitted fields keep
|
|
725
|
+
their standing override. Values are runtime scalars with no clamping, conversion,
|
|
726
|
+
new clock or solver. The core applies them **after authored/keyed values and
|
|
727
|
+
sliders, before the integrator**. Unknown constraints, a non-physics constraint,
|
|
728
|
+
unknown fields and non-finite/non-number values are refused atomically by name.
|
|
729
|
+
These methods require a live track: before `play`/`queue`, set, read and clear
|
|
730
|
+
refuse with `no live track` instead of creating one. The model and other players
|
|
731
|
+
sharing its assets are untouched.
|
|
732
|
+
|
|
733
|
+
`PhysicsParameterRead` contains detached readonly snapshots: `override` is the
|
|
734
|
+
pending patch or null; `effective` is the six values from the last active
|
|
735
|
+
evaluation, or null until evaluated. An inactive constraint can retain its last
|
|
736
|
+
active values within that live core instance. Setting or clearing changes no
|
|
737
|
+
pose, time or physics state, and **does not evaluate**: effective keeps the last
|
|
738
|
+
values until a normal evaluation (or an explicitly requested `repose()`). Clear
|
|
739
|
+
removes the patch; authored keys resume at the next evaluation. It preserves
|
|
740
|
+
integrated history and does not instantly undo the motion caused by old values.
|
|
741
|
+
|
|
742
|
+
The player retains the **current standing patch** across new play, seek, skin
|
|
743
|
+
replay and promotion to composed tracks. Like `environment()`, it records no
|
|
744
|
+
historical changes. **After a mid-run patch change, a backward seek recomputes the
|
|
745
|
+
past interval with the patch standing now**, under the current environment; it
|
|
746
|
+
does not reproduce the old trajectory across that change. New single tracks and
|
|
747
|
+
new composed tracks first evaluate their ordinary authored first pose, then
|
|
748
|
+
receive the standing patch as pending, with no extra adjustment callback. At
|
|
749
|
+
seek time 0, effective can therefore still be the initial authored value while
|
|
750
|
+
override already reports the patch. The next normal core evaluation uses it.
|
|
751
|
+
Changing entries on existing composed tracks follows that core instance's
|
|
752
|
+
lifetime. Existing seek/skin restrictions after composition remain unchanged.
|
|
753
|
+
|
|
754
|
+
Set/clear invalidate seek caches without re-posing. Seek snapshots retain the
|
|
755
|
+
public effective read alongside their held pose, so backward and exact cache
|
|
756
|
+
hits report that pose's evaluation rather than the frontier's latest evaluation.
|
|
757
|
+
Readback itself never advances, evaluates or resets physics.
|
|
758
|
+
|
|
688
759
|
### One load, several players
|
|
689
760
|
|
|
690
761
|
One `loadModel` backs any number of players: the regions are cut once, and
|
|
@@ -1043,8 +1114,9 @@ name, and unloading is still elements first, bitmaps second.
|
|
|
1043
1114
|
| dark (two-colour) tint: unsupported | `DomRenderer`: unsupported. `CanvasRenderer`: drawn | Carried as `dark` on a `PosedSlot`. The canvas mode's canvas2d fallback draws alpha only. |
|
|
1044
1115
|
| `renderer.dispose()` | `player.dispose()` | Then `assets.dispose()`, as before. `DomRenderer.dispose()` and `CanvasRenderer.dispose()` exist for a renderer you constructed yourself. |
|
|
1045
1116
|
| `skeleton.findBone('head').worldX`, `.worldY`, `.a` … `.d` | `player.bone('head')` | `{ x, y, a, b, c, d, rotationX, rotationY, scaleX, scaleY, active }`, the core's world transform for the pose the player holds (the setup pose before `play`). An unknown name is refused by name. Since 1.1.0. |
|
|
1046
|
-
| `bone.scaleY = …` on a `Skeleton` between `state.apply` and `updateWorldTransform` | `RigPlayerOptions.adjust`, `player.adjust`, `player.repose()` | A function called once per pose with the pose's bone locals (`bones.bone('mouth').scaleY = v`). A write lasts one pose: the core poses every step from the setup pose, so write the value you want, every pose — nothing compounds. See *Adjusting bones*. Since 1.2.0. |
|
|
1117
|
+
| `bone.scaleY = …` on a `Skeleton` between `state.apply` and `updateWorldTransform` | `RigPlayerOptions.adjust`, `player.adjust`, `player.repose()` | A function `(bones, ctx)` called once per pose with the pose's bone locals and current-entry context (unary callbacks remain valid) (`bones.bone('mouth').scaleY = v`). A write lasts one pose: the core poses every step from the setup pose, so write the value you want, every pose — nothing compounds. See *Adjusting bones*. Since 1.2.0. |
|
|
1047
1118
|
| `skeleton.getBounds(offset, size)` | `player.bounds()` | `{ x, y, width, height }` or null, world units, Y up: the box of every drawn region's corners and mesh's vertices, a slot under a clip counted inside the clip's own box. `ModelAssets.stage` remains the declared framing, not this box. Since 1.1.0. |
|
|
1119
|
+
| live named physics constraint tuning | `player.setPhysicsParameters(name, patch)`, `readPhysicsParameters(name)`, `clearPhysicsParameters(name)` | Six scalar fields, per-player pending patch plus last evaluated effective values. Clear resumes authored values without resetting history. Replay uses the current standing patch, with no historical journal. See *Live physics parameters*. |
|
|
1048
1120
|
| named setup mesh geometry | `readSetupMesh(assets, { slot, attachment, skin? })` | Detached full-double world x/y (Y up), triangles and hull for any named mesh/linked mesh, including non-setup selections; no player/time mutation. Selected geometry includes setup-slider deform; unselected geometry is stored geometry through unchanged setup matrices. See the measured official-runtime distinction under *Reading the pose*. |
|
|
1049
1121
|
|
|
1050
1122
|
### No counterpart in 1.0.0
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,6 @@ export { type CanvasBackend, CanvasRenderer, type CanvasViewport } from './Canva
|
|
|
2
2
|
export { DomRenderer, type MeshBackend, type MeshSourceRect } from './DomRenderer.js';
|
|
3
3
|
export { type PageLayout, type PageRegion, type RegionEncoding, type RegionImage, revokeRegions, type UnpackRegionsOptions, unpackRegions } from './DomTexture.js';
|
|
4
4
|
export { type LoadModelOptions, loadModel, type ModelAssets, type ModelDocument, readSetupMesh, type RegionLoading, type RigSet, type SetupMesh, type SetupMeshTarget } from './loadModel.js';
|
|
5
|
-
export { type AdjustableBones, type BoneAdjust, type BoneLocals, type BoneWorld, type PlayOptions, type PoseBounds, type QueueOptions, type RenderMode, type RigComplete, type RigEnvironment, type RigEvent, RigPlayer, type RigPlayerOptions, type SlotFilter, type SlotIdentity, type SlotIdentityReason, type SlotIdentitySnapshot, type TrackEntry } from './player.js';
|
|
5
|
+
export { type AdjustableBones, type BoneAdjust, type BoneAdjustContext, type BoneLocals, type BoneWorld, type PhysicsParameters, type PhysicsParameterPatch, type PhysicsParameterRead, type PlayOptions, type PoseBounds, type QueueOptions, type RenderMode, type RigComplete, type RigEnvironment, type RigEvent, RigPlayer, type RigPlayerOptions, type SlotFilter, type SlotIdentity, type SlotIdentityReason, type SlotIdentitySnapshot, type TrackEntry } from './player.js';
|
|
6
6
|
export type { BlendMode, PosedClip, PosedFrame, PosedMesh, PosedRegion, PosedSlot, } from './posedFrame.js';
|
|
7
7
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC9F,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACtF,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,cAAc,EAAE,KAAK,WAAW,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACnK,OAAO,EAAE,KAAK,gBAAgB,EAAE,SAAS,EAAE,KAAK,WAAW,EAAE,KAAK,aAAa,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,KAAK,MAAM,EAAE,KAAK,SAAS,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC9L,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,SAAS,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,KAAK,cAAc,EAAE,KAAK,QAAQ,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,KAAK,UAAU,EAAE,KAAK,YAAY,EAAE,KAAK,kBAAkB,EAAE,KAAK,oBAAoB,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC9F,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACtF,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,cAAc,EAAE,KAAK,WAAW,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACnK,OAAO,EAAE,KAAK,gBAAgB,EAAE,SAAS,EAAE,KAAK,WAAW,EAAE,KAAK,aAAa,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,KAAK,MAAM,EAAE,KAAK,SAAS,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC9L,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAAE,KAAK,iBAAiB,EAAE,KAAK,UAAU,EAAE,KAAK,SAAS,EAAE,KAAK,iBAAiB,EAAE,KAAK,qBAAqB,EAAE,KAAK,oBAAoB,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,KAAK,cAAc,EAAE,KAAK,QAAQ,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,KAAK,UAAU,EAAE,KAAK,YAAY,EAAE,KAAK,kBAAkB,EAAE,KAAK,oBAAoB,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AACpd,YAAY,EACV,SAAS,EACT,SAAS,EACT,UAAU,EACV,SAAS,EACT,WAAW,EACX,SAAS,GACV,MAAM,iBAAiB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -436,23 +436,23 @@ function slotSwitches(slots, slot) {
|
|
|
436
436
|
}
|
|
437
437
|
return index.get(slot);
|
|
438
438
|
}
|
|
439
|
-
function applyAttachment(now,
|
|
440
|
-
if (now === null || now.identity === null) return
|
|
439
|
+
function applyAttachment(now, values2, timelines, t, blend, evalDeform, blendOf2, evalFrame) {
|
|
440
|
+
if (now === null || now.identity === null) return values2;
|
|
441
441
|
const matching = matchingAttachments(timelines, now.identity);
|
|
442
|
-
if (matching === void 0) return
|
|
443
|
-
|
|
444
|
-
if (!
|
|
442
|
+
if (matching === void 0) return values2;
|
|
443
|
+
values2 ??= { deform: null, frame: null, seen: [] };
|
|
444
|
+
if (!values2.seen.includes(now.identity)) values2.seen.push(now.identity);
|
|
445
445
|
for (const a of matching) {
|
|
446
446
|
if (a.deform !== null && now.vertices !== null) {
|
|
447
447
|
const target = evalDeform(now.vertices, a.deform, t);
|
|
448
|
-
if (target !== null)
|
|
448
|
+
if (target !== null) values2.deform = blend === null ? target : blendOf2(now.vertices, values2.deform, target, blend.alpha, blend.additive);
|
|
449
449
|
}
|
|
450
450
|
if (a.sequence !== null && now.count !== void 0) {
|
|
451
451
|
const f = evalFrame(a.sequence, now.count, t);
|
|
452
|
-
if (f !== null)
|
|
452
|
+
if (f !== null) values2.frame = f;
|
|
453
453
|
}
|
|
454
454
|
}
|
|
455
|
-
return
|
|
455
|
+
return values2;
|
|
456
456
|
}
|
|
457
457
|
function attachmentStates(doc, resolve, placeholders, sample, sliders, plant = {}, mixed) {
|
|
458
458
|
const evalDeform = plant.deform ?? deformAt;
|
|
@@ -466,9 +466,9 @@ function attachmentStates(doc, resolve, placeholders, sample, sliders, plant = {
|
|
|
466
466
|
const live = gate(doc, slot, active);
|
|
467
467
|
let placeholder = placeholders.get(slot.name) ?? null;
|
|
468
468
|
const base = live ? mixed?.get(slot.name) : void 0;
|
|
469
|
-
let
|
|
469
|
+
let values2 = base === void 0 || base.deform === null && base.seen.length === 0 ? null : { deform: base.deform, frame: null, seen: [...base.seen] };
|
|
470
470
|
let now = identityOf(doc, resolve, slot, placeholder);
|
|
471
|
-
if (sample !== null && live)
|
|
471
|
+
if (sample !== null && live) values2 = applyAttachment(now, values2, sample.timelines.attachments, sample.t, null, evalDeform, blendOf2, evalFrame);
|
|
472
472
|
if (live) for (const app of sliders) {
|
|
473
473
|
const switches = slotSwitches(app.timelines.slots, slot.name);
|
|
474
474
|
if (switches !== void 0) for (const keys of switches) {
|
|
@@ -476,26 +476,26 @@ function attachmentStates(doc, resolve, placeholders, sample, sliders, plant = {
|
|
|
476
476
|
if (i < 0) continue;
|
|
477
477
|
const next = keys[i].name ?? null;
|
|
478
478
|
if (next !== placeholder) {
|
|
479
|
-
if (
|
|
480
|
-
|
|
481
|
-
|
|
479
|
+
if (values2 !== null) {
|
|
480
|
+
values2.deform = null;
|
|
481
|
+
values2.frame = null;
|
|
482
482
|
}
|
|
483
483
|
placeholder = next;
|
|
484
484
|
now = identityOf(doc, resolve, slot, placeholder);
|
|
485
485
|
}
|
|
486
486
|
}
|
|
487
|
-
|
|
487
|
+
values2 = applyAttachment(now, values2, app.timelines.attachments, app.at, app, evalDeform, blendOf2, evalFrame);
|
|
488
488
|
}
|
|
489
489
|
if (now === null) continue;
|
|
490
|
-
const entry =
|
|
490
|
+
const entry = values2 === null || values2.deform === null && values2.frame === null ? now.entry : { ...now.entry };
|
|
491
491
|
shown.push(entry);
|
|
492
|
-
if (
|
|
493
|
-
why.push(`slot "${slot.name}" shows placeholder "${now.entry.placeholder}", which skins ${now.fillers.join(", ")} fill, and ${
|
|
492
|
+
if (values2 !== null && values2.seen.length > 0 && now.fillers.length > 1) {
|
|
493
|
+
why.push(`slot "${slot.name}" shows placeholder "${now.entry.placeholder}", which skins ${now.fillers.join(", ")} fill, and ${values2.seen.map((x) => `"${x}"`).join(", ")} key(s) it — which record --skin all shows, and so whether the timeline moves it, is the Spine file's skin order, not the model's`);
|
|
494
494
|
continue;
|
|
495
495
|
}
|
|
496
|
-
if (
|
|
497
|
-
if (
|
|
498
|
-
if (
|
|
496
|
+
if (values2 !== null) {
|
|
497
|
+
if (values2.deform !== null) entry.deform = values2.deform;
|
|
498
|
+
if (values2.frame !== null) entry.frame = values2.frame;
|
|
499
499
|
}
|
|
500
500
|
}
|
|
501
501
|
return { shown, why };
|
|
@@ -1236,16 +1236,16 @@ function readPathGeometry(raw, vertices, where, problems) {
|
|
|
1236
1236
|
if (problems.length !== before) return void 0;
|
|
1237
1237
|
return { kind: "path", vertices, closed: raw.closed === true, constantSpeed: raw.constantSpeed !== false, lengths };
|
|
1238
1238
|
}
|
|
1239
|
-
function roundedRow(
|
|
1240
|
-
for (let i = 0; i <
|
|
1241
|
-
const r = round(
|
|
1242
|
-
if (Object.is(r,
|
|
1243
|
-
const out =
|
|
1239
|
+
function roundedRow(values2, round) {
|
|
1240
|
+
for (let i = 0; i < values2.length; i++) {
|
|
1241
|
+
const r = round(values2[i]);
|
|
1242
|
+
if (Object.is(r, values2[i])) continue;
|
|
1243
|
+
const out = values2.slice(0, i);
|
|
1244
1244
|
out.push(r);
|
|
1245
|
-
for (let j = i + 1; j <
|
|
1245
|
+
for (let j = i + 1; j < values2.length; j++) out.push(round(values2[j]));
|
|
1246
1246
|
return out;
|
|
1247
1247
|
}
|
|
1248
|
-
return
|
|
1248
|
+
return values2;
|
|
1249
1249
|
}
|
|
1250
1250
|
function poseGeometry(shown, world, sourceOf, round, plant = {}, draw) {
|
|
1251
1251
|
const region = plant.region ?? regionCorners;
|
|
@@ -3431,7 +3431,8 @@ function applyConstraints(bones, world, active, records, previous = null, applie
|
|
|
3431
3431
|
if (c.kind === "physics") {
|
|
3432
3432
|
if (physics !== void 0) {
|
|
3433
3433
|
const w = { ...state.world.get(c.bone) };
|
|
3434
|
-
const
|
|
3434
|
+
const authored = physicsPose?.records.get(c.name) ?? c;
|
|
3435
|
+
const posed = physics.parameters?.apply(authored) ?? authored;
|
|
3435
3436
|
if ((physics.step ?? stepPhysics)(posed, w, bone(state, c.bone).length ?? 0, physics)) {
|
|
3436
3437
|
state.world.set(c.bone, w);
|
|
3437
3438
|
changed = [c.bone];
|
|
@@ -3712,7 +3713,7 @@ function readBoneTimeline(kind, keysValue, where, problems) {
|
|
|
3712
3713
|
}
|
|
3713
3714
|
const channels = BONE_TIMELINE_CHANNELS[kind];
|
|
3714
3715
|
unknownKeyFields(raw, ["time", ...channels, "curve"], at, problems);
|
|
3715
|
-
const
|
|
3716
|
+
const values2 = channels.map((c) => {
|
|
3716
3717
|
const v = raw[c];
|
|
3717
3718
|
if (typeof v !== "number" || !Number.isFinite(v)) {
|
|
3718
3719
|
problems.push(`${at}: ${c} is ${JSON.stringify(v)}, not a finite number — the writer states every channel on every key`);
|
|
@@ -3720,7 +3721,7 @@ function readBoneTimeline(kind, keysValue, where, problems) {
|
|
|
3720
3721
|
}
|
|
3721
3722
|
return v;
|
|
3722
3723
|
});
|
|
3723
|
-
return { time: Math.fround(time), values:
|
|
3724
|
+
return { time: Math.fround(time), values: values2.map(Math.fround), stated: { time, values: values2 }, curve: readCurve(raw, channels.length, i === listed.length - 1, at, problems) };
|
|
3724
3725
|
});
|
|
3725
3726
|
}
|
|
3726
3727
|
function hexChannels(hex, count) {
|
|
@@ -3737,22 +3738,22 @@ function readSlotTimeline(kind, keysValue, where, problems) {
|
|
|
3737
3738
|
return { time: Math.fround(time), values: [], stated: { time, values: [] }, curve: "stepped", name: typeof raw.name === "string" ? raw.name : null };
|
|
3738
3739
|
}
|
|
3739
3740
|
const shape = SLOT_COLOUR_TIMELINES[kind];
|
|
3740
|
-
let
|
|
3741
|
+
let values2 = [];
|
|
3741
3742
|
if (kind === "alpha") {
|
|
3742
3743
|
unknownKeyFields(raw, ["time", "value", "curve"], at, problems);
|
|
3743
3744
|
const v = raw.value;
|
|
3744
3745
|
if (typeof v !== "number" || !Number.isFinite(v)) problems.push(`${at}: value is ${JSON.stringify(v)}, not a finite number`);
|
|
3745
|
-
else
|
|
3746
|
+
else values2 = [v];
|
|
3746
3747
|
} else {
|
|
3747
3748
|
unknownKeyFields(raw, ["time", ...shape.fields.map(([f]) => f), "curve"], at, problems);
|
|
3748
3749
|
for (const [field, digits] of shape.fields) {
|
|
3749
3750
|
const v = raw[field];
|
|
3750
3751
|
if (!isHex(v, digits)) problems.push(`${at}: ${field} is ${JSON.stringify(v)}, not ${digits} hex digits — the one spelling the writer gives a ${kind} key's ${field}`);
|
|
3751
|
-
else
|
|
3752
|
+
else values2.push(...hexChannels(v, field === "dark" || digits === 6 ? 3 : 4));
|
|
3752
3753
|
}
|
|
3753
3754
|
}
|
|
3754
|
-
if (
|
|
3755
|
-
return { time: Math.fround(time), values:
|
|
3755
|
+
if (values2.length !== shape.channels.length) values2 = shape.channels.map(() => 0);
|
|
3756
|
+
return { time: Math.fround(time), values: values2.map(Math.fround), stated: { time, values: values2 }, curve: readCurve(raw, shape.channels.length, i === listed.length - 1, at, problems) };
|
|
3756
3757
|
});
|
|
3757
3758
|
}
|
|
3758
3759
|
function constraintKeyTimes(value, where, problems) {
|
|
@@ -4048,7 +4049,7 @@ class StepLocals {
|
|
|
4048
4049
|
this.write("shearY", v);
|
|
4049
4050
|
}
|
|
4050
4051
|
}
|
|
4051
|
-
function adjustLocals(doc, bones, adjust) {
|
|
4052
|
+
function adjustLocals(doc, bones, adjust, context) {
|
|
4052
4053
|
const views = /* @__PURE__ */ new Map();
|
|
4053
4054
|
const names = doc.bones.map((b) => b.name);
|
|
4054
4055
|
adjust({
|
|
@@ -4063,7 +4064,7 @@ function adjustLocals(doc, bones, adjust) {
|
|
|
4063
4064
|
}
|
|
4064
4065
|
return view;
|
|
4065
4066
|
}
|
|
4066
|
-
});
|
|
4067
|
+
}, context);
|
|
4067
4068
|
}
|
|
4068
4069
|
const clamp01$1 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
|
|
4069
4070
|
function posedSlots(doc, timelines, t, plant = {}, sliders = [], placeholders) {
|
|
@@ -4150,7 +4151,7 @@ function posedBoneStep(doc, timelines, t, plant, constraints, sliders, step) {
|
|
|
4150
4151
|
const bones = posedBones(doc, timelines, t, plant);
|
|
4151
4152
|
if (constraints !== void 0 && step !== void 0) return steppedLocals(doc, bones, plant, () => pathDeformed(doc, posedRecords(constraintRecords(doc), constraints, t), timelines, t, plant), constraints.physicsKeyed ?? [], t, sliders, step);
|
|
4152
4153
|
const active = activeBones(doc);
|
|
4153
|
-
if (step?.
|
|
4154
|
+
if (step?.adjustment !== void 0) adjustLocals(doc, bones, step.adjustment.callback, step.adjustment.context);
|
|
4154
4155
|
let world = (plant.evaluate ?? worldTransforms)(bones, active);
|
|
4155
4156
|
if (constraints !== void 0) {
|
|
4156
4157
|
const setupRecords = constraintRecords(doc);
|
|
@@ -4163,7 +4164,7 @@ function posedBoneStep(doc, timelines, t, plant, constraints, sliders, step) {
|
|
|
4163
4164
|
}
|
|
4164
4165
|
function steppedLocals(doc, bones, plant, records, physicsKeyed, t, sliders, step) {
|
|
4165
4166
|
const active = activeBones(doc);
|
|
4166
|
-
if (step.
|
|
4167
|
+
if (step.adjustment !== void 0) adjustLocals(doc, bones, step.adjustment.callback, step.adjustment.context);
|
|
4167
4168
|
let world = (plant.evaluate ?? worldTransforms)(bones, active);
|
|
4168
4169
|
const stepped = stepPhysicsRecords(records(), physicsKeyed, t, active, step.ctx, step.before);
|
|
4169
4170
|
world = applyConstraints(bones, world, active, plant.constraints ? plant.constraints(stepped) : stepped, null, sliders, step.ctx, void 0, solverRules(plant.solver));
|
|
@@ -5021,11 +5022,11 @@ function drawnGeometry(doc, shown, world, order, plant, mode) {
|
|
|
5021
5022
|
if (geometry.drawnClipped === null) throw new CoreInputError(`the raw pose leaves the clipped triangles out: ${geometry.drawnClippedWhy}`);
|
|
5022
5023
|
return { rows: geometry.attachments, clips: geometry.clips, drawnClipped: geometry.drawnClipped };
|
|
5023
5024
|
}
|
|
5024
|
-
function numbersOf(
|
|
5025
|
-
return allNumbers(
|
|
5025
|
+
function numbersOf(values2, what, mode) {
|
|
5026
|
+
return allNumbers(values2) ? values2 : values2.map((v) => numberOf(v, what, mode));
|
|
5026
5027
|
}
|
|
5027
|
-
function allNumbers(
|
|
5028
|
-
for (const v of
|
|
5028
|
+
function allNumbers(values2) {
|
|
5029
|
+
for (const v of values2) if (v === null) return false;
|
|
5029
5030
|
return true;
|
|
5030
5031
|
}
|
|
5031
5032
|
function rawDrawn(doc, shown, world, order, plant, mode = RAW_MODE) {
|
|
@@ -5171,6 +5172,7 @@ function openWalk(doc, anim, plant, reset, mode, shape, adjust, before) {
|
|
|
5171
5172
|
const why = constraintsAbsentWhy(doc) ?? pathAnimationsWhy(doc) ?? steppedPreviousPassWhy(doc);
|
|
5172
5173
|
if (why !== null) throw new CoreInputError(`the raw walk leaves the bones out: ${why}`);
|
|
5173
5174
|
const ctx = freshStepContext(plant.physicsStep);
|
|
5175
|
+
before?.(ctx);
|
|
5174
5176
|
if (mode.loop && mode.wrapResets !== false) ctx.loop = true;
|
|
5175
5177
|
const track = { doc, anim, duration: anim.timelines.duration, mode, plant, raw: { ...plant, ...roundPlant(mode) }, resolve: plant.shown ?? shownAttachment, shape, ctx, adjust, trackTime: 0, last: -1 };
|
|
5176
5178
|
if (reset === "setup") {
|
|
@@ -5193,8 +5195,9 @@ function stepWalk(track, dt, first = false) {
|
|
|
5193
5195
|
ctx.time += mode.clock === void 0 ? dt : mode.clock(dt, t - (first ? 0 : Math.max(last, 0)));
|
|
5194
5196
|
const fire = plant.events ?? eventsFired;
|
|
5195
5197
|
const keys = anim.timelines.events;
|
|
5198
|
+
const adjustment = adjust === void 0 ? void 0 : { callback: adjust, context: { trackTime, animation: anim.name, globalTime: trackTime } };
|
|
5196
5199
|
const pose = poseStepFrom(doc, plant, resolve, shape, mode, ctx, first, trackTime, t, {
|
|
5197
|
-
world: (sliders) => shape.rows ? posedBoneWorld(doc, anim.timelines, t, raw, anim.constraints, sliders, { ctx, before,
|
|
5200
|
+
world: (sliders) => shape.rows ? posedBoneWorld(doc, anim.timelines, t, raw, anim.constraints, sliders, { ctx, before, adjustment }).world : posedBoneWorldAlone(doc, anim.timelines, t, raw, anim.constraints, sliders, { ctx, before, adjustment }),
|
|
5198
5201
|
slots: (sliders, placeholders) => posedSlots(doc, anim.timelines, t, raw, sliders, placeholders),
|
|
5199
5202
|
drawOrder: anim.timelines.drawOrder,
|
|
5200
5203
|
at: t,
|
|
@@ -5242,12 +5245,56 @@ function openRawTracksPath(doc, plant) {
|
|
|
5242
5245
|
step: (first, trackTime, animationTime, posers) => poseStepFrom(doc, plant, resolve, RAW_SHAPE, RAW_MODE, ctx, first, trackTime, animationTime, posers)
|
|
5243
5246
|
};
|
|
5244
5247
|
}
|
|
5245
|
-
function openRawTrack(doc, animation, plant, reset, mode, adjust) {
|
|
5246
|
-
return openWalk(doc, animationOf(doc, animation), plant, reset, mode, RAW_SHAPE, adjust);
|
|
5248
|
+
function openRawTrack(doc, animation, plant, reset, mode, adjust, before) {
|
|
5249
|
+
return openWalk(doc, animationOf(doc, animation), plant, reset, mode, RAW_SHAPE, adjust, before);
|
|
5247
5250
|
}
|
|
5248
5251
|
function stepRawTrack(track, dt) {
|
|
5249
5252
|
return stepWalk(track, dt);
|
|
5250
5253
|
}
|
|
5254
|
+
const FIELDS = ["strength", "damping", "inertia", "wind", "gravity", "mix"];
|
|
5255
|
+
const values = (r) => ({ strength: r.strength, damping: r.damping, inertia: r.inertia, wind: r.wind, gravity: r.gravity, mix: r.mix });
|
|
5256
|
+
class LivePhysicsParameters {
|
|
5257
|
+
constructor(doc) {
|
|
5258
|
+
this.doc = doc;
|
|
5259
|
+
this.overrides = /* @__PURE__ */ new Map();
|
|
5260
|
+
this.evaluated = /* @__PURE__ */ new Map();
|
|
5261
|
+
}
|
|
5262
|
+
named(name) {
|
|
5263
|
+
const constraint = this.doc.constraints.find((c) => c.name === name);
|
|
5264
|
+
if (constraint === void 0) throw new CoreInputError(`physics parameters: no constraint "${String(name)}"; this document declares [${this.doc.constraints.map((c) => c.name).join(", ")}]`);
|
|
5265
|
+
if (constraint.kind !== "physics") throw new CoreInputError(`physics parameters: constraint "${name}" is ${constraint.kind}, not physics`);
|
|
5266
|
+
}
|
|
5267
|
+
set(name, patch) {
|
|
5268
|
+
this.named(name);
|
|
5269
|
+
if (typeof patch !== "object" || patch === null || Array.isArray(patch)) throw new CoreInputError(`physics parameters: constraint "${name}" patch is ${String(patch)}, not an object of finite fields [${FIELDS.join(", ")}]`);
|
|
5270
|
+
const checked = {};
|
|
5271
|
+
for (const key of Reflect.ownKeys(patch)) {
|
|
5272
|
+
if (typeof key !== "string" || !FIELDS.includes(key)) throw new CoreInputError(`physics parameters: constraint "${name}" field "${String(key)}" is unknown; expected [${FIELDS.join(", ")}]`);
|
|
5273
|
+
const field = key;
|
|
5274
|
+
const value = patch[field];
|
|
5275
|
+
if (typeof value !== "number" || !Number.isFinite(value)) throw new CoreInputError(`physics parameters: constraint "${name}" field "${field}" is ${String(value)}, not a finite number`);
|
|
5276
|
+
checked[field] = value;
|
|
5277
|
+
}
|
|
5278
|
+
if (Reflect.ownKeys(checked).length > 0) this.overrides.set(name, { ...this.overrides.get(name), ...checked });
|
|
5279
|
+
}
|
|
5280
|
+
read(name) {
|
|
5281
|
+
this.named(name);
|
|
5282
|
+
const patch = this.overrides.get(name);
|
|
5283
|
+
const effective = this.evaluated.get(name);
|
|
5284
|
+
return { override: patch === void 0 ? null : { ...patch }, effective: effective === void 0 ? null : { ...effective } };
|
|
5285
|
+
}
|
|
5286
|
+
clear(name) {
|
|
5287
|
+
this.named(name);
|
|
5288
|
+
this.overrides.delete(name);
|
|
5289
|
+
}
|
|
5290
|
+
/** Called only for an active constraint, after the sliders and immediately before its step. */
|
|
5291
|
+
apply(record) {
|
|
5292
|
+
const patch = this.overrides.get(record.name);
|
|
5293
|
+
const posed = patch === void 0 ? record : { ...record, ...patch };
|
|
5294
|
+
this.evaluated.set(record.name, values(posed));
|
|
5295
|
+
return posed;
|
|
5296
|
+
}
|
|
5297
|
+
}
|
|
5251
5298
|
const HELD = { loop: false, keep: false };
|
|
5252
5299
|
const LOOPED = { loop: true, keep: false, live: true };
|
|
5253
5300
|
function openTrack(doc, animation, options, plant = {}) {
|
|
@@ -5257,11 +5304,15 @@ function openTrack(doc, animation, options, plant = {}) {
|
|
|
5257
5304
|
if (options.clock !== void 0 && typeof options.clock !== "function") throw new CoreInputError(`the live track's options.clock is ${typeof options.clock}, not a function`);
|
|
5258
5305
|
const mode = options.loop ? LOOPED : HELD;
|
|
5259
5306
|
const clock = options.clock === void 0 ? void 0 : checkedClock(options.clock, "the live track's");
|
|
5260
|
-
const
|
|
5261
|
-
|
|
5307
|
+
const parameters = new LivePhysicsParameters(doc);
|
|
5308
|
+
const { track, first } = openRawTrack(doc, animation, plant, options.reset, clock === void 0 ? mode : { ...mode, clock: (dt) => clock(dt) }, options.adjust, (ctx) => {
|
|
5309
|
+
ctx.parameters = parameters;
|
|
5310
|
+
});
|
|
5311
|
+
return new Track(track, first, parameters);
|
|
5262
5312
|
}
|
|
5263
5313
|
class Track {
|
|
5264
|
-
constructor(track, first) {
|
|
5314
|
+
constructor(track, first, parameters) {
|
|
5315
|
+
this.parameters = parameters;
|
|
5265
5316
|
this.steps = 0;
|
|
5266
5317
|
this.refused = null;
|
|
5267
5318
|
this.track = track;
|
|
@@ -5284,6 +5335,17 @@ class Track {
|
|
|
5284
5335
|
this.live("setEnvironment");
|
|
5285
5336
|
setEnvironmentOn(this.track.ctx, environment, "the live track's");
|
|
5286
5337
|
}
|
|
5338
|
+
setPhysicsParameters(name, patch) {
|
|
5339
|
+
this.live("setPhysicsParameters");
|
|
5340
|
+
this.parameters.set(name, patch);
|
|
5341
|
+
}
|
|
5342
|
+
readPhysicsParameters(name) {
|
|
5343
|
+
return this.parameters.read(name);
|
|
5344
|
+
}
|
|
5345
|
+
clearPhysicsParameters(name) {
|
|
5346
|
+
this.live("clearPhysicsParameters");
|
|
5347
|
+
this.parameters.clear(name);
|
|
5348
|
+
}
|
|
5287
5349
|
physicsTranslate(x, y) {
|
|
5288
5350
|
this.live("physicsTranslate");
|
|
5289
5351
|
translateOn(this.track.ctx, x, y, "the live track's");
|
|
@@ -6215,6 +6277,8 @@ class Tracks {
|
|
|
6215
6277
|
this.time = 0;
|
|
6216
6278
|
this.refused = null;
|
|
6217
6279
|
this.mixer = new Mixer(doc, plant);
|
|
6280
|
+
this.parameters = new LivePhysicsParameters(doc);
|
|
6281
|
+
this.path.ctx.parameters = this.parameters;
|
|
6218
6282
|
}
|
|
6219
6283
|
get trackTime() {
|
|
6220
6284
|
return this.time;
|
|
@@ -6226,6 +6290,17 @@ class Tracks {
|
|
|
6226
6290
|
this.live("setEnvironment");
|
|
6227
6291
|
setEnvironmentOn(this.path.ctx, environment, "the live tracks'");
|
|
6228
6292
|
}
|
|
6293
|
+
setPhysicsParameters(name, patch) {
|
|
6294
|
+
this.live("setPhysicsParameters");
|
|
6295
|
+
this.parameters.set(name, patch);
|
|
6296
|
+
}
|
|
6297
|
+
readPhysicsParameters(name) {
|
|
6298
|
+
return this.parameters.read(name);
|
|
6299
|
+
}
|
|
6300
|
+
clearPhysicsParameters(name) {
|
|
6301
|
+
this.live("clearPhysicsParameters");
|
|
6302
|
+
this.parameters.clear(name);
|
|
6303
|
+
}
|
|
6229
6304
|
physicsTranslate(x, y) {
|
|
6230
6305
|
this.live("physicsTranslate");
|
|
6231
6306
|
translateOn(this.path.ctx, x, y, "the live tracks'");
|
|
@@ -6330,9 +6405,14 @@ class Tracks {
|
|
|
6330
6405
|
ctx.time += this.clock === void 0 ? dt : this.clock(dt);
|
|
6331
6406
|
const pose = new Pose(doc, this.plant);
|
|
6332
6407
|
const animationTime = this.mixer.apply(pose);
|
|
6408
|
+
const entry = this.mixer.tracks[0] ?? null;
|
|
6409
|
+
const adjustment = this.adjust === void 0 ? void 0 : {
|
|
6410
|
+
callback: this.adjust,
|
|
6411
|
+
context: { trackTime: entry?.trackTime ?? null, animation: entry?.anim.name ?? null, globalTime: this.time }
|
|
6412
|
+
};
|
|
6333
6413
|
const mixed = new Map(doc.slots.map((s, k) => [s.name, { deform: pose.slots[k].deform, seen: pose.slots[k].seen }]));
|
|
6334
6414
|
return path.step(first, this.time, animationTime, {
|
|
6335
|
-
world: (sliders) => steppedLocalsWorld(doc, pose.bones, path.raw, sliders, { ctx, before,
|
|
6415
|
+
world: (sliders) => steppedLocalsWorld(doc, pose.bones, path.raw, sliders, { ctx, before, adjustment }),
|
|
6336
6416
|
slots: (sliders, placeholders) => slotRowsFrom(doc, path.raw, sliders, placeholders, (_slot, _live) => {
|
|
6337
6417
|
const s = pose.slots[doc.slots.indexOf(_slot)];
|
|
6338
6418
|
return { placeholder: s.placeholder, light: s.light, dark: s.dark };
|
|
@@ -9104,12 +9184,16 @@ class RigPlayer {
|
|
|
9104
9184
|
setsByName;
|
|
9105
9185
|
activeSet = null;
|
|
9106
9186
|
adjustFn = null;
|
|
9187
|
+
/** Current preview patches, reapplied through the public core API on replay; no edit history. */
|
|
9188
|
+
physicsPatches = /* @__PURE__ */ new Map();
|
|
9189
|
+
/** Public read snapshots belonging to a restored seek pose, not the core's private evaluation cache. */
|
|
9190
|
+
heldParameters = null;
|
|
9107
9191
|
slotFilter = null;
|
|
9108
9192
|
/** `slotFilter` as a predicate; every slot when null. */
|
|
9109
9193
|
draws = () => true;
|
|
9110
9194
|
/** Handed to every track the player opens; reads the current `adjust` so setting it later needs no reopen. */
|
|
9111
|
-
adjustHook = (locals) => {
|
|
9112
|
-
if (this.adjustFn !== null) this.adjustFn(locals);
|
|
9195
|
+
adjustHook = (locals, ctx) => {
|
|
9196
|
+
if (this.adjustFn !== null) this.adjustFn(locals, ctx);
|
|
9113
9197
|
};
|
|
9114
9198
|
listeners = {
|
|
9115
9199
|
event: /* @__PURE__ */ new Set(),
|
|
@@ -9206,7 +9290,7 @@ class RigPlayer {
|
|
|
9206
9290
|
this.animation = name;
|
|
9207
9291
|
this.loop = loop;
|
|
9208
9292
|
this.track = openTrack(this.view, name, { loop, reset: "animation", adjust: this.adjustHook, clock: physicsClockStep });
|
|
9209
|
-
this.setRawPose(this.track
|
|
9293
|
+
this.setRawPose(this.initializeSingle(this.track));
|
|
9210
9294
|
this.applyEnvironment(this.track);
|
|
9211
9295
|
return;
|
|
9212
9296
|
}
|
|
@@ -9312,6 +9396,7 @@ class RigPlayer {
|
|
|
9312
9396
|
this.tracks = tracks;
|
|
9313
9397
|
this.track = null;
|
|
9314
9398
|
this.setRawPose(tracks.start());
|
|
9399
|
+
this.applyPhysicsParameters(tracks);
|
|
9315
9400
|
this.applyEnvironment(tracks);
|
|
9316
9401
|
if (time > 0) this.stepTo(time);
|
|
9317
9402
|
return tracks;
|
|
@@ -9319,10 +9404,54 @@ class RigPlayer {
|
|
|
9319
9404
|
/** Stores `raw` and applies `activeSet` when one is active. Zero work when `activeSet === null`. */
|
|
9320
9405
|
setRawPose(raw) {
|
|
9321
9406
|
this.rawPose = raw;
|
|
9407
|
+
this.heldParameters = null;
|
|
9322
9408
|
this.pose = this.activeSet === null ? raw : applySetToPose(this.view, raw, this.activeSet);
|
|
9323
9409
|
this.poseGeneration++;
|
|
9324
9410
|
this.cachedIdentity = null;
|
|
9325
9411
|
}
|
|
9412
|
+
/**
|
|
9413
|
+
* Sets a named physics constraint's pending patch, after keyed/slider values
|
|
9414
|
+
* and before the integrator on the next evaluation. No pose or clock change.
|
|
9415
|
+
* Unknown names/kinds/fields and non-finite values are refused atomically by
|
|
9416
|
+
* the core. Requires a live track (call play/queue first).
|
|
9417
|
+
*/
|
|
9418
|
+
setPhysicsParameters(name, patch) {
|
|
9419
|
+
const live = this.requirePhysicsTrack("setPhysicsParameters", name);
|
|
9420
|
+
live.setPhysicsParameters(name, patch);
|
|
9421
|
+
const standing = live.readPhysicsParameters(name).override;
|
|
9422
|
+
if (standing !== null) this.physicsPatches.set(name, standing);
|
|
9423
|
+
this.clearSeekCache();
|
|
9424
|
+
}
|
|
9425
|
+
/** Pending override and the held pose's last active evaluation, detached. Does not evaluate a pose. */
|
|
9426
|
+
readPhysicsParameters(name) {
|
|
9427
|
+
const read = this.requirePhysicsTrack("readPhysicsParameters", name).readPhysicsParameters(name);
|
|
9428
|
+
if (this.heldParameters === null) return read;
|
|
9429
|
+
const effective = this.heldParameters.get(name)?.effective ?? null;
|
|
9430
|
+
return { override: read.override, effective: effective === null ? null : { ...effective } };
|
|
9431
|
+
}
|
|
9432
|
+
/** Removes only the patch; authored values resume next evaluation. Physics history is retained. */
|
|
9433
|
+
clearPhysicsParameters(name) {
|
|
9434
|
+
this.requirePhysicsTrack("clearPhysicsParameters", name).clearPhysicsParameters(name);
|
|
9435
|
+
this.physicsPatches.delete(name);
|
|
9436
|
+
this.clearSeekCache();
|
|
9437
|
+
}
|
|
9438
|
+
requirePhysicsTrack(operation, name) {
|
|
9439
|
+
const live = this.stepper();
|
|
9440
|
+
if (live === null) throw new Error(`${operation}(${JSON.stringify(name)}) before play: no live track`);
|
|
9441
|
+
return live;
|
|
9442
|
+
}
|
|
9443
|
+
/** No private parameter state is copied into the core's seek snapshots. */
|
|
9444
|
+
readParameters(live) {
|
|
9445
|
+
return new Map(this.view.constraints.filter((c) => c.kind === "physics").map((c) => [c.name, live.readPhysicsParameters(c.name)]));
|
|
9446
|
+
}
|
|
9447
|
+
applyPhysicsParameters(live) {
|
|
9448
|
+
for (const [name, patch] of this.physicsPatches) live.setPhysicsParameters(name, patch);
|
|
9449
|
+
}
|
|
9450
|
+
/** Like the environment, standing patches are pending after the ordinary first pose; no extra evaluation. */
|
|
9451
|
+
initializeSingle(live) {
|
|
9452
|
+
this.applyPhysicsParameters(live);
|
|
9453
|
+
return live.first;
|
|
9454
|
+
}
|
|
9326
9455
|
/**
|
|
9327
9456
|
* The world the rig's physics answers (#121): `wind` and `gravity`, as
|
|
9328
9457
|
* spine-core's skeleton-level `windX/windY` and `gravityX/gravityY` — x to
|
|
@@ -9401,6 +9530,7 @@ class RigPlayer {
|
|
|
9401
9530
|
restoreStepperState(stepper, exactHit.state);
|
|
9402
9531
|
this.rawPose = exactHit.rawPose;
|
|
9403
9532
|
this.pose = exactHit.pose;
|
|
9533
|
+
this.heldParameters = exactHit.parameters;
|
|
9404
9534
|
this.poseGeneration++;
|
|
9405
9535
|
this.cachedIdentity = null;
|
|
9406
9536
|
return;
|
|
@@ -9412,24 +9542,32 @@ class RigPlayer {
|
|
|
9412
9542
|
if (cache.frontierSteps <= SEEK_FINE_STEP_LIMIT || cache.frontierSteps % SEEK_COARSE_STRIDE === 0) {
|
|
9413
9543
|
cache.grid.set(cache.frontierSteps, {
|
|
9414
9544
|
state: captureStepperState(cache.frontier),
|
|
9415
|
-
rawPose: raw
|
|
9545
|
+
rawPose: raw,
|
|
9546
|
+
parameters: this.readParameters(cache.frontier)
|
|
9416
9547
|
});
|
|
9417
9548
|
}
|
|
9418
9549
|
}
|
|
9550
|
+
let parameters;
|
|
9419
9551
|
const gridHit = cache.grid.get(steps);
|
|
9420
9552
|
if (gridHit !== void 0) {
|
|
9421
9553
|
restoreStepperState(stepper, gridHit.state);
|
|
9422
9554
|
this.rawPose = gridHit.rawPose;
|
|
9555
|
+
parameters = gridHit.parameters;
|
|
9423
9556
|
} else {
|
|
9424
9557
|
const baseStep = steps - steps % SEEK_COARSE_STRIDE;
|
|
9425
9558
|
const baseSnap = cache.grid.get(baseStep);
|
|
9426
9559
|
restoreStepperState(stepper, baseSnap.state);
|
|
9427
9560
|
this.rawPose = baseSnap.rawPose;
|
|
9428
9561
|
for (let i = baseStep; i < steps; i++) this.rawPose = stepper.step(this.seekStep);
|
|
9562
|
+
parameters = this.readParameters(stepper);
|
|
9429
9563
|
}
|
|
9430
9564
|
const rest = time - stepper.trackTime;
|
|
9431
|
-
if (rest > 0)
|
|
9565
|
+
if (rest > 0) {
|
|
9566
|
+
this.rawPose = stepper.step(rest);
|
|
9567
|
+
parameters = this.readParameters(stepper);
|
|
9568
|
+
}
|
|
9432
9569
|
if (this.rawPose !== null) this.setRawPose(this.rawPose);
|
|
9570
|
+
this.heldParameters = parameters;
|
|
9433
9571
|
if (this.rawPose !== null && this.pose !== null) {
|
|
9434
9572
|
if (cache.exact.size >= SEEK_EXACT_LIMIT) {
|
|
9435
9573
|
const oldest = cache.exact.keys().next().value;
|
|
@@ -9438,7 +9576,8 @@ class RigPlayer {
|
|
|
9438
9576
|
cache.exact.set(time, {
|
|
9439
9577
|
state: captureStepperState(stepper),
|
|
9440
9578
|
rawPose: this.rawPose,
|
|
9441
|
-
pose: this.pose
|
|
9579
|
+
pose: this.pose,
|
|
9580
|
+
parameters
|
|
9442
9581
|
});
|
|
9443
9582
|
}
|
|
9444
9583
|
}
|
|
@@ -9459,16 +9598,17 @@ class RigPlayer {
|
|
|
9459
9598
|
const tracks = openTracks(this.view, { reset: "animation", adjust: this.adjustHook, clock: physicsClockStep });
|
|
9460
9599
|
tracks.setAnimation(0, this.animation, { loop: this.loop, mixDuration: 0 });
|
|
9461
9600
|
firstPose = tracks.start();
|
|
9601
|
+
this.applyPhysicsParameters(tracks);
|
|
9462
9602
|
this.applyEnvironment(tracks);
|
|
9463
9603
|
frontier = tracks;
|
|
9464
9604
|
} else {
|
|
9465
9605
|
const track = openTrack(this.view, this.animation, { loop: this.loop, reset: "animation", adjust: this.adjustHook, clock: physicsClockStep });
|
|
9466
|
-
firstPose = track
|
|
9606
|
+
firstPose = this.initializeSingle(track);
|
|
9467
9607
|
this.applyEnvironment(track);
|
|
9468
9608
|
frontier = track;
|
|
9469
9609
|
}
|
|
9470
9610
|
const grid = /* @__PURE__ */ new Map();
|
|
9471
|
-
grid.set(0, { state: captureStepperState(frontier), rawPose: firstPose });
|
|
9611
|
+
grid.set(0, { state: captureStepperState(frontier), rawPose: firstPose, parameters: this.readParameters(frontier) });
|
|
9472
9612
|
this.seekCache = {
|
|
9473
9613
|
mode,
|
|
9474
9614
|
frontier,
|
|
@@ -9486,11 +9626,12 @@ class RigPlayer {
|
|
|
9486
9626
|
this.trackCount = 1;
|
|
9487
9627
|
this.tracks = tracks;
|
|
9488
9628
|
this.setRawPose(tracks.start());
|
|
9629
|
+
this.applyPhysicsParameters(tracks);
|
|
9489
9630
|
this.applyEnvironment(tracks);
|
|
9490
9631
|
return;
|
|
9491
9632
|
}
|
|
9492
9633
|
this.track = openTrack(this.view, this.animation, { loop: this.loop, reset: "animation", adjust: this.adjustHook, clock: physicsClockStep });
|
|
9493
|
-
this.setRawPose(this.track
|
|
9634
|
+
this.setRawPose(this.initializeSingle(this.track));
|
|
9494
9635
|
this.applyEnvironment(this.track);
|
|
9495
9636
|
}
|
|
9496
9637
|
/** What is open: the tracks, the single track, or nothing before `play`. */
|