rig-play 1.5.0 → 1.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/README.md +25 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +55 -8
- package/dist/index.js.map +1 -1
- package/dist/player.d.ts +29 -2
- package/dist/player.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -263,6 +263,30 @@ box, an inverse clip removes nothing, and bounding boxes, paths and points do
|
|
|
263
263
|
not draw and do not count. Both cost nothing until called and read the same
|
|
264
264
|
numbers in `dom` and `canvas` mode.
|
|
265
265
|
|
|
266
|
+
### Drawing some slots only
|
|
267
|
+
|
|
268
|
+
`slots` is a draw filter — an allow list of slot names or a predicate — settable up
|
|
269
|
+
front and on a live player. The pose is the whole rig's whatever the filter:
|
|
270
|
+
bones, constraints, physics, `bone()`, `adjust`, events and tracks see no filter;
|
|
271
|
+
only the draw does, in both modes, and `bounds()` reads the drawn slots. A
|
|
272
|
+
clipping slot the filter leaves out does not clip. An allow list naming a slot
|
|
273
|
+
the document does not have is refused by name.
|
|
274
|
+
|
|
275
|
+
```ts
|
|
276
|
+
const hand = new RigPlayer(frontRoot, assets, { slots: ['hand_f', 'arm_f_lo'] });
|
|
277
|
+
player.slots = (slot) => !slot.startsWith('weapon');
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
Two players over one `ModelAssets` share the load but each poses the rig. To
|
|
281
|
+
pose it once and draw it twice — a hand drawn again in front of another
|
|
282
|
+
character — give a second renderer this player's pose under another filter:
|
|
283
|
+
|
|
284
|
+
```ts
|
|
285
|
+
const front = new DomRenderer(frontRoot, assets.regionImages, assets.pageImages);
|
|
286
|
+
// each frame, after player.frame(dt):
|
|
287
|
+
front.render(player.posedFrame(['hand_f', 'arm_f_lo']));
|
|
288
|
+
```
|
|
289
|
+
|
|
266
290
|
### Several tracks
|
|
267
291
|
|
|
268
292
|
A base on track 0, a layer over it, a one-shot played once and mixed back out —
|
|
@@ -628,6 +652,7 @@ name, and unloading is still elements first, bitmaps second.
|
|
|
628
652
|
| `DomTexture` | none | Not exported; there is no atlas page to attach a texture to. |
|
|
629
653
|
| `new Skeleton(assets.data)` and `new AnimationState(new AnimationStateData(assets.data))` | `new RigPlayer(root, assets, options?)` | The player holds the pose and the track. |
|
|
630
654
|
| `state.setAnimation(0, 'walk', true)` | `player.play('walk', { loop: true })` | `loop` defaults to true. `play` opens the animation at its first pose with the physics reset there. `player.current` names what is playing. |
|
|
655
|
+
| `skeleton.findSlot('x').attachment = null` to hide a slot, or a second skeleton with slots emptied | `RigPlayerOptions.slots`, `player.slots`, `player.posedFrame(slots)` | A draw filter since 1.6.0 — the pose is the whole rig's; a clipping slot left out does not clip. See *Drawing some slots only*. |
|
|
631
656
|
| `state.setAnimation(1, 'smile', false)` with a mix, `addAnimation`, `setEmptyAnimation`, `addEmptyAnimation`, `getCurrent(track)` | `player.play(name, { track, loop, mix })`, `player.queue(name, { track, loop, mix, delay })`, `player.clear(track, { mix })`, `player.queueClear(track, { mix, delay })`, `player.entry(track)` | Since 1.5.0, rig-c's live tracks: the runtime's mixing at tolerance 0. Constraint, sequence, draw-order and path-deform timelines are not mixed and are refused on the tracks. See *Several tracks*. |
|
|
632
657
|
| `state.update(delta)`, `state.apply(skeleton)`, `skeleton.update(delta)`, `skeleton.updateWorldTransform(Physics.update)`, `renderer.render(skeleton)` | `player.frame(dt)` | Or `player.advance(dt)` then `player.draw()`, to time the two apart. |
|
|
633
658
|
| time scale (the caller's own, on the spine-core side) | `player.timeScale` | Multiplies every step. 0 holds the pose without re-posing it; a negative value is not stepped. |
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,6 @@ export { type CanvasBackend, CanvasRenderer, type CanvasViewport } from './Canva
|
|
|
2
2
|
export { DomRenderer, type MeshBackend } 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, type RegionLoading } from './loadModel.js';
|
|
5
|
-
export { type AdjustableBones, type BoneAdjust, type BoneLocals, type BoneWorld, type PlayOptions, type PoseBounds, type QueueOptions, type RenderMode, type RigComplete, type RigEvent, RigPlayer, type RigPlayerOptions, type TrackEntry } from './player.js';
|
|
5
|
+
export { type AdjustableBones, type BoneAdjust, type BoneLocals, type BoneWorld, type PlayOptions, type PoseBounds, type QueueOptions, type RenderMode, type RigComplete, type RigEvent, RigPlayer, type RigPlayerOptions, type SlotFilter, 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,MAAM,kBAAkB,CAAC;AACjE,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,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC5H,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,QAAQ,EAAE,SAAS,EAAE,KAAK,gBAAgB,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,MAAM,kBAAkB,CAAC;AACjE,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,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC5H,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,QAAQ,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AACjR,YAAY,EACV,SAAS,EACT,SAAS,EACT,UAAU,EACV,SAAS,EACT,WAAW,EACX,SAAS,GACV,MAAM,iBAAiB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -8216,7 +8216,7 @@ function trianglesOf(source, drawn) {
|
|
|
8216
8216
|
}
|
|
8217
8217
|
return kept;
|
|
8218
8218
|
}
|
|
8219
|
-
function clipCover(pose, active) {
|
|
8219
|
+
function clipCover(pose, active, draws = () => true) {
|
|
8220
8220
|
const polygons = new Map(pose.clips.map((c) => [c[0], { end: c[2], polygon: c[3] }]));
|
|
8221
8221
|
const shownOf = new Map(pose.shown.map((s) => [s.slot, s]));
|
|
8222
8222
|
const cover = /* @__PURE__ */ new Map();
|
|
@@ -8227,7 +8227,7 @@ function clipCover(pose, active) {
|
|
|
8227
8227
|
const clip = g?.kind === "clipping" ? polygons.get(slot) : void 0;
|
|
8228
8228
|
if (shown !== void 0 && g?.kind === "clipping" && clip !== void 0) {
|
|
8229
8229
|
if (current !== null && current.end === slot) current = null;
|
|
8230
|
-
if (active.has(shown.bone) && current === null) {
|
|
8230
|
+
if (active.has(shown.bone) && current === null && draws(slot)) {
|
|
8231
8231
|
current = { end: clip.end, clip: { polygon: clip.polygon, inverse: g.inverse, convex: g.convex } };
|
|
8232
8232
|
}
|
|
8233
8233
|
continue;
|
|
@@ -8237,16 +8237,17 @@ function clipCover(pose, active) {
|
|
|
8237
8237
|
}
|
|
8238
8238
|
return cover;
|
|
8239
8239
|
}
|
|
8240
|
-
function frameOf(source, pose, color = [1, 1, 1, 1]) {
|
|
8240
|
+
function frameOf(source, pose, color = [1, 1, 1, 1], draws = () => true) {
|
|
8241
8241
|
const { drawn: regions, why } = drawnRegions(source.doc, pose.shown, pose.drawOrder, source.uv);
|
|
8242
8242
|
if (regions === null) throw new CoreInputError(`the page UVs are not posed: ${why}`);
|
|
8243
8243
|
const regionOf = new Map(regions.map((r) => [r.slot, r]));
|
|
8244
8244
|
const rows = new Map(pose.slots.map((row) => [row[0], row]));
|
|
8245
8245
|
const z = new Map(pose.drawOrder.map((slot, index) => [slot, index]));
|
|
8246
|
-
const cover = clipCover(pose, source.active);
|
|
8246
|
+
const cover = clipCover(pose, source.active, draws);
|
|
8247
8247
|
const [sr, sg, sb, sa] = color;
|
|
8248
8248
|
const order = [];
|
|
8249
8249
|
for (const d of pose.drawn) {
|
|
8250
|
+
if (!draws(d.slot)) continue;
|
|
8250
8251
|
const bone2 = source.boneOf.get(d.slot);
|
|
8251
8252
|
if (bone2 === void 0 || !source.active.has(bone2)) continue;
|
|
8252
8253
|
const found = regionOf.get(d.slot);
|
|
@@ -8294,6 +8295,10 @@ function frameOf(source, pose, color = [1, 1, 1, 1]) {
|
|
|
8294
8295
|
}
|
|
8295
8296
|
return { order };
|
|
8296
8297
|
}
|
|
8298
|
+
function predicateOf(names) {
|
|
8299
|
+
const allowed = new Set(names);
|
|
8300
|
+
return (slot) => allowed.has(slot);
|
|
8301
|
+
}
|
|
8297
8302
|
class RigPlayer {
|
|
8298
8303
|
/**
|
|
8299
8304
|
* The renderer this player draws through — its knobs and counters are the
|
|
@@ -8341,6 +8346,9 @@ class RigPlayer {
|
|
|
8341
8346
|
pose = null;
|
|
8342
8347
|
setup = null;
|
|
8343
8348
|
adjustFn = null;
|
|
8349
|
+
slotFilter = null;
|
|
8350
|
+
/** `slotFilter` as a predicate; every slot when null. */
|
|
8351
|
+
draws = () => true;
|
|
8344
8352
|
/** Handed to every track the player opens; reads the current `adjust` so setting it later needs no reopen. */
|
|
8345
8353
|
adjustHook = (locals) => {
|
|
8346
8354
|
if (this.adjustFn !== null) this.adjustFn(locals);
|
|
@@ -8361,6 +8369,7 @@ class RigPlayer {
|
|
|
8361
8369
|
this.assets = assets;
|
|
8362
8370
|
this.meshBackend = options.meshBackend;
|
|
8363
8371
|
this.adjustFn = options.adjust ?? null;
|
|
8372
|
+
if (options.slots !== void 0 && options.slots !== null) this.slots = options.slots;
|
|
8364
8373
|
this.active = this.create(options.mode ?? "dom");
|
|
8365
8374
|
}
|
|
8366
8375
|
/**
|
|
@@ -8619,6 +8628,37 @@ class RigPlayer {
|
|
|
8619
8628
|
this.stepTo(time);
|
|
8620
8629
|
}
|
|
8621
8630
|
}
|
|
8631
|
+
/**
|
|
8632
|
+
* Which slots this player draws (`SlotFilter`), or null for every slot.
|
|
8633
|
+
* Settable on a live player: the next `draw` shows it, and `bounds()` reads
|
|
8634
|
+
* the drawn slots. An allow list naming a slot the document does not have
|
|
8635
|
+
* is refused by name. The pose is the whole rig's: `bone()`, `adjust`, the
|
|
8636
|
+
* events and the tracks see no filter.
|
|
8637
|
+
*/
|
|
8638
|
+
get slots() {
|
|
8639
|
+
return this.slotFilter;
|
|
8640
|
+
}
|
|
8641
|
+
set slots(filter) {
|
|
8642
|
+
if (filter === null) {
|
|
8643
|
+
this.slotFilter = null;
|
|
8644
|
+
this.draws = () => true;
|
|
8645
|
+
return;
|
|
8646
|
+
}
|
|
8647
|
+
if (typeof filter === "function") {
|
|
8648
|
+
this.slotFilter = filter;
|
|
8649
|
+
this.draws = filter;
|
|
8650
|
+
return;
|
|
8651
|
+
}
|
|
8652
|
+
if (!Array.isArray(filter)) throw new Error(`slots must be an array of slot names, a function, or null, got ${typeof filter}`);
|
|
8653
|
+
const known = new Set(this.read.doc.slots.map((slot) => slot.name));
|
|
8654
|
+
const unknown = filter.filter((name) => !known.has(name));
|
|
8655
|
+
if (unknown.length > 0) {
|
|
8656
|
+
throw new Error(`no slot named ${unknown.map((n) => JSON.stringify(n)).join(", ")} in this document (slots: ${[...known].join(", ")})`);
|
|
8657
|
+
}
|
|
8658
|
+
const allowed = new Set(filter);
|
|
8659
|
+
this.slotFilter = [...filter];
|
|
8660
|
+
this.draws = (slot) => allowed.has(slot);
|
|
8661
|
+
}
|
|
8622
8662
|
/**
|
|
8623
8663
|
* The per-pose adjustment of bone locals (`BoneAdjust`), or null for none.
|
|
8624
8664
|
* It is applied by every track the player opens — `play`, the reopen inside
|
|
@@ -8684,7 +8724,7 @@ class RigPlayer {
|
|
|
8684
8724
|
* mode; nothing is computed until called.
|
|
8685
8725
|
*/
|
|
8686
8726
|
bounds() {
|
|
8687
|
-
const frame2 = this.pose === null ? frameOf(this.source, this.posed(), this.color) : this.posedFrame();
|
|
8727
|
+
const frame2 = this.pose === null ? frameOf(this.source, this.posed(), this.color, this.draws) : this.posedFrame();
|
|
8688
8728
|
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
8689
8729
|
for (const slot of frame2.order) {
|
|
8690
8730
|
const points = slot.kind === "region" ? slot.corners : slot.vertices;
|
|
@@ -8720,10 +8760,17 @@ class RigPlayer {
|
|
|
8720
8760
|
if (this.setup === null) this.setup = poseRawSetup(this.view);
|
|
8721
8761
|
return this.setup;
|
|
8722
8762
|
}
|
|
8723
|
-
/**
|
|
8724
|
-
|
|
8763
|
+
/**
|
|
8764
|
+
* The pose the player holds, as the frame record the renderer draws — under
|
|
8765
|
+
* this player's `slots` filter, or under `slots` given here instead (a
|
|
8766
|
+
* second renderer over the same assets drawing this player's pose on
|
|
8767
|
+
* another root: `other.render(player.posedFrame(['hand_f']))`, the one rig
|
|
8768
|
+
* posed once).
|
|
8769
|
+
*/
|
|
8770
|
+
posedFrame(slots) {
|
|
8725
8771
|
if (this.pose === null) return { order: [] };
|
|
8726
|
-
|
|
8772
|
+
const draws = slots === void 0 ? this.draws : slots === null ? () => true : typeof slots === "function" ? slots : predicateOf(slots);
|
|
8773
|
+
return frameOf(this.source, this.pose, this.color, draws);
|
|
8727
8774
|
}
|
|
8728
8775
|
/** Removes the renderer's elements and hands its GPU pages back. The assets stay the caller's. */
|
|
8729
8776
|
dispose() {
|