spine-html 1.12.1 → 1.13.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 +37 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +230 -74
- package/dist/index.js.map +1 -1
- package/dist/loadModel.d.ts +25 -0
- package/dist/loadModel.d.ts.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -450,6 +450,42 @@ box, an inverse clip removes nothing, and bounding boxes, paths and points do
|
|
|
450
450
|
not draw and do not count. Both cost nothing until called and read the same
|
|
451
451
|
numbers in `dom` and `canvas` mode.
|
|
452
452
|
|
|
453
|
+
`readSetupMesh` reads a named mesh from loaded assets before or during playback,
|
|
454
|
+
including an attachment the setup pose does not select:
|
|
455
|
+
|
|
456
|
+
```ts
|
|
457
|
+
import { readSetupMesh } from '@rig-c/play';
|
|
458
|
+
|
|
459
|
+
const mesh = readSetupMesh(assets, { slot: 'flag', attachment: 'alternate' });
|
|
460
|
+
// mesh: { slot, attachment, skin, vertices, triangles, hull }
|
|
461
|
+
// vertices: flat world x,y pairs, Y up; hull counts vertices, or is null.
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
Names are exact slot names and attachment table keys, not display names or atlas
|
|
465
|
+
regions. Omitted or `null` skin means no skin set, as for a fresh player. A named
|
|
466
|
+
`skin` uses that skin's activation lists with default-table fallback; the returned
|
|
467
|
+
`skin` names the supplying table. `skin: 'all'` uses all activation lists and
|
|
468
|
+
refuses differing definitions rather than guessing a merge order. Linked meshes
|
|
469
|
+
retain the requested identity and borrow source topology/bindings; unweighted
|
|
470
|
+
vertices use the queried slot's bone. Inactive bones retain core's zero matrix.
|
|
471
|
+
The result and its arrays are detached and mutable. Missing slot/attachment/skin,
|
|
472
|
+
non-mesh and ambiguous targets throw core's named `CoreInputError` messages;
|
|
473
|
+
setup/non-finite refusals propagate unchanged. The read creates no player and
|
|
474
|
+
changes no document, existing player, time, generation or rendered frame.
|
|
475
|
+
|
|
476
|
+
**Setup-slider boundary (core 2.66.0):** selected meshes include setup-slider
|
|
477
|
+
deform, exactly as core's setup pose does. Unselected meshes use stored geometry
|
|
478
|
+
through the same setup constraint/reset-physics matrices, without switching the
|
|
479
|
+
attachment or borrowing another selection's deform. Core measured **four of six
|
|
480
|
+
nonselected official-runtime comparisons differing by up to
|
|
481
|
+
`2.9999999999999982` world units (about 3.0)**: spine-core 4.3.13's
|
|
482
|
+
`computeWorldVertices` borrows the selected slot's deform, including for a linked
|
|
483
|
+
mesh with `timelines: false`. The other two nonselected calls and all three
|
|
484
|
+
selected reads agree exactly. This wrapper passes core's output through; it does
|
|
485
|
+
not correct or round it. Stored-only formula comparisons therefore have a
|
|
486
|
+
different domain from selected geometry with setup deform. See
|
|
487
|
+
[core AUTHORING §14, including S1538_09](https://github.com/rig-c/core/blob/v2.66.0/docs/AUTHORING.md#14-read-a-named-mesh-at-setup).
|
|
488
|
+
|
|
453
489
|
### Drawing some slots only
|
|
454
490
|
|
|
455
491
|
`slots` is a draw filter — an allow list of slot names or a predicate — settable up
|
|
@@ -1009,6 +1045,7 @@ name, and unloading is still elements first, bitmaps second.
|
|
|
1009
1045
|
| `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. |
|
|
1010
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. |
|
|
1011
1047
|
| `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. |
|
|
1048
|
+
| 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*. |
|
|
1012
1049
|
|
|
1013
1050
|
### No counterpart in 1.0.0
|
|
1014
1051
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { type CanvasBackend, CanvasRenderer, type CanvasViewport } from './CanvasRenderer.js';
|
|
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
|
-
export { type LoadModelOptions, loadModel, type ModelAssets, type ModelDocument, type RegionLoading, type RigSet } from './loadModel.js';
|
|
4
|
+
export { type LoadModelOptions, loadModel, type ModelAssets, type ModelDocument, readSetupMesh, type RegionLoading, type RigSet, type SetupMesh, type SetupMeshTarget } from './loadModel.js';
|
|
5
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';
|
|
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,KAAK,aAAa,EAAE,KAAK,MAAM,EAAE,MAAM,gBAAgB,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,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;AAC7W,YAAY,EACV,SAAS,EACT,SAAS,EACT,UAAU,EACV,SAAS,EACT,WAAW,EACX,SAAS,GACV,MAAM,iBAAiB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -377,6 +377,83 @@ function timelineIdentity(slot, shown) {
|
|
|
377
377
|
return `${shown.skin}/${slot}/${shown.placeholder}`;
|
|
378
378
|
}
|
|
379
379
|
const nameOf = (a) => `${a.skin}/${a.slot}/${a.attachment}`;
|
|
380
|
+
const identitiesOfView = /* @__PURE__ */ new WeakMap();
|
|
381
|
+
function readIdentity(doc, resolve, slot, placeholder) {
|
|
382
|
+
const s = resolve(doc, { ...slot, setup: placeholder });
|
|
383
|
+
if (s === null || "conflict" in s || s.record.geometry === void 0) return null;
|
|
384
|
+
const g = s.record.geometry;
|
|
385
|
+
const source = g.kind === "linkedmesh" ? doc.skins.find((k) => k.name === g.skin)?.attachments[g.slot]?.[g.source] : s.record;
|
|
386
|
+
const sg = source?.geometry;
|
|
387
|
+
return Object.freeze({
|
|
388
|
+
entry: Object.freeze({ slot: slot.name, bone: slot.bone, name: shownRow(s).name, placeholder: s.placeholder, skin: s.skin, geometry: g }),
|
|
389
|
+
identity: timelineIdentity(slot.name, s),
|
|
390
|
+
vertices: sg !== void 0 && sg.kind !== "region" && sg.kind !== "linkedmesh" ? sg.vertices : null,
|
|
391
|
+
count: source?.sequenceCount,
|
|
392
|
+
fillers: Object.freeze(fillingSkins(doc, slot.name, s.placeholder).map((k) => `"${k}"`))
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
function identityOf(doc, resolve, slot, placeholder) {
|
|
396
|
+
if (placeholder === null) return null;
|
|
397
|
+
if (resolve !== shownAttachment) return readIdentity(doc, resolve, slot, placeholder);
|
|
398
|
+
let slots = identitiesOfView.get(doc);
|
|
399
|
+
if (slots === void 0) identitiesOfView.set(doc, slots = /* @__PURE__ */ new Map());
|
|
400
|
+
let placeholders = slots.get(slot.name);
|
|
401
|
+
if (placeholders === void 0) slots.set(slot.name, placeholders = /* @__PURE__ */ new Map());
|
|
402
|
+
if (placeholders.has(placeholder)) return placeholders.get(placeholder);
|
|
403
|
+
const identity = readIdentity(doc, resolve, slot, placeholder);
|
|
404
|
+
placeholders.set(placeholder, identity);
|
|
405
|
+
return identity;
|
|
406
|
+
}
|
|
407
|
+
const attachmentsByIdentity = /* @__PURE__ */ new WeakMap();
|
|
408
|
+
const switchesBySlot = /* @__PURE__ */ new WeakMap();
|
|
409
|
+
function matchingAttachments(timelines, identity) {
|
|
410
|
+
let index = attachmentsByIdentity.get(timelines);
|
|
411
|
+
if (index === void 0) {
|
|
412
|
+
const rows = /* @__PURE__ */ new Map();
|
|
413
|
+
for (const a of timelines) {
|
|
414
|
+
const name = nameOf(a);
|
|
415
|
+
const found = rows.get(name);
|
|
416
|
+
if (found === void 0) rows.set(name, [a]);
|
|
417
|
+
else found.push(a);
|
|
418
|
+
}
|
|
419
|
+
attachmentsByIdentity.set(timelines, index = rows);
|
|
420
|
+
}
|
|
421
|
+
return index.get(identity);
|
|
422
|
+
}
|
|
423
|
+
function slotSwitches(slots, slot) {
|
|
424
|
+
let index = switchesBySlot.get(slots);
|
|
425
|
+
if (index === void 0) {
|
|
426
|
+
const rows = /* @__PURE__ */ new Map();
|
|
427
|
+
for (const target of slots) {
|
|
428
|
+
for (const tl of target.timelines) {
|
|
429
|
+
if (tl.kind !== "attachment" || tl.keys === void 0) continue;
|
|
430
|
+
const found = rows.get(target.name);
|
|
431
|
+
if (found === void 0) rows.set(target.name, [tl.keys]);
|
|
432
|
+
else found.push(tl.keys);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
switchesBySlot.set(slots, index = rows);
|
|
436
|
+
}
|
|
437
|
+
return index.get(slot);
|
|
438
|
+
}
|
|
439
|
+
function applyAttachment(now, values, timelines, t, blend, evalDeform, blendOf2, evalFrame) {
|
|
440
|
+
if (now === null || now.identity === null) return values;
|
|
441
|
+
const matching = matchingAttachments(timelines, now.identity);
|
|
442
|
+
if (matching === void 0) return values;
|
|
443
|
+
values ??= { deform: null, frame: null, seen: [] };
|
|
444
|
+
if (!values.seen.includes(now.identity)) values.seen.push(now.identity);
|
|
445
|
+
for (const a of matching) {
|
|
446
|
+
if (a.deform !== null && now.vertices !== null) {
|
|
447
|
+
const target = evalDeform(now.vertices, a.deform, t);
|
|
448
|
+
if (target !== null) values.deform = blend === null ? target : blendOf2(now.vertices, values.deform, target, blend.alpha, blend.additive);
|
|
449
|
+
}
|
|
450
|
+
if (a.sequence !== null && now.count !== void 0) {
|
|
451
|
+
const f = evalFrame(a.sequence, now.count, t);
|
|
452
|
+
if (f !== null) values.frame = f;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
return values;
|
|
456
|
+
}
|
|
380
457
|
function attachmentStates(doc, resolve, placeholders, sample, sliders, plant = {}, mixed) {
|
|
381
458
|
const evalDeform = plant.deform ?? deformAt;
|
|
382
459
|
const blendOf2 = plant.deformBlend ?? blendDeform;
|
|
@@ -389,64 +466,37 @@ function attachmentStates(doc, resolve, placeholders, sample, sliders, plant = {
|
|
|
389
466
|
const live = gate(doc, slot, active);
|
|
390
467
|
let placeholder = placeholders.get(slot.name) ?? null;
|
|
391
468
|
const base = live ? mixed?.get(slot.name) : void 0;
|
|
392
|
-
let
|
|
393
|
-
let
|
|
394
|
-
|
|
395
|
-
const
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
const apply = (timelines, t, blend) => {
|
|
406
|
-
const now2 = state();
|
|
407
|
-
if (now2 === null || now2.identity === null) return;
|
|
408
|
-
for (const a of timelines) {
|
|
409
|
-
if (nameOf(a) !== now2.identity) continue;
|
|
410
|
-
if (!seen.includes(now2.identity)) seen.push(now2.identity);
|
|
411
|
-
if (a.deform !== null && now2.vertices !== null) {
|
|
412
|
-
const target = evalDeform(now2.vertices, a.deform, t);
|
|
413
|
-
if (target !== null) deform = blend === null ? target : blendOf2(now2.vertices, deform, target, blend.alpha, blend.additive);
|
|
414
|
-
}
|
|
415
|
-
if (a.sequence !== null && now2.count !== void 0) {
|
|
416
|
-
const f = evalFrame(a.sequence, now2.count, t);
|
|
417
|
-
if (f !== null) frame2 = f;
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
};
|
|
421
|
-
if (sample !== null && live) apply(sample.timelines.attachments, sample.t, null);
|
|
422
|
-
for (const app of live ? sliders : []) {
|
|
423
|
-
for (const target of app.timelines.slots) {
|
|
424
|
-
if (target.name !== slot.name) continue;
|
|
425
|
-
for (const tl of target.timelines) {
|
|
426
|
-
if (tl.kind !== "attachment" || tl.keys === void 0) continue;
|
|
427
|
-
const i = keyIndexAt(tl.keys, app.at);
|
|
428
|
-
if (i < 0) continue;
|
|
429
|
-
const next = tl.keys[i].name ?? null;
|
|
430
|
-
if (next !== placeholder) {
|
|
431
|
-
deform = null;
|
|
432
|
-
frame2 = null;
|
|
469
|
+
let values = base === void 0 || base.deform === null && base.seen.length === 0 ? null : { deform: base.deform, frame: null, seen: [...base.seen] };
|
|
470
|
+
let now = identityOf(doc, resolve, slot, placeholder);
|
|
471
|
+
if (sample !== null && live) values = applyAttachment(now, values, sample.timelines.attachments, sample.t, null, evalDeform, blendOf2, evalFrame);
|
|
472
|
+
if (live) for (const app of sliders) {
|
|
473
|
+
const switches = slotSwitches(app.timelines.slots, slot.name);
|
|
474
|
+
if (switches !== void 0) for (const keys of switches) {
|
|
475
|
+
const i = keyIndexAt(keys, app.at);
|
|
476
|
+
if (i < 0) continue;
|
|
477
|
+
const next = keys[i].name ?? null;
|
|
478
|
+
if (next !== placeholder) {
|
|
479
|
+
if (values !== null) {
|
|
480
|
+
values.deform = null;
|
|
481
|
+
values.frame = null;
|
|
433
482
|
}
|
|
434
483
|
placeholder = next;
|
|
484
|
+
now = identityOf(doc, resolve, slot, placeholder);
|
|
435
485
|
}
|
|
436
486
|
}
|
|
437
|
-
|
|
487
|
+
values = applyAttachment(now, values, app.timelines.attachments, app.at, app, evalDeform, blendOf2, evalFrame);
|
|
438
488
|
}
|
|
439
|
-
const now = state();
|
|
440
489
|
if (now === null) continue;
|
|
441
|
-
const entry =
|
|
490
|
+
const entry = values === null || values.deform === null && values.frame === null ? now.entry : { ...now.entry };
|
|
442
491
|
shown.push(entry);
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
why.push(`slot "${slot.name}" shows placeholder "${now.s.placeholder}", which skins ${fillers.join(", ")} fill, and ${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`);
|
|
492
|
+
if (values !== null && values.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 ${values.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`);
|
|
446
494
|
continue;
|
|
447
495
|
}
|
|
448
|
-
if (
|
|
449
|
-
|
|
496
|
+
if (values !== null) {
|
|
497
|
+
if (values.deform !== null) entry.deform = values.deform;
|
|
498
|
+
if (values.frame !== null) entry.frame = values.frame;
|
|
499
|
+
}
|
|
450
500
|
}
|
|
451
501
|
return { shown, why };
|
|
452
502
|
}
|
|
@@ -712,8 +762,8 @@ function clipTriangles(polygon, vertices, triangles, uvs, reading = {}) {
|
|
|
712
762
|
}
|
|
713
763
|
return { clipped, vertices: outV, uvs: outUv, triangles: outT, sources };
|
|
714
764
|
}
|
|
715
|
-
const REGION_UVS = [0, 1, 0, 0, 1, 0, 1, 1];
|
|
716
|
-
const REGION_TRIANGLES = [0, 1, 2, 2, 3, 0];
|
|
765
|
+
const REGION_UVS = Object.freeze([0, 1, 0, 0, 1, 0, 1, 1]);
|
|
766
|
+
const REGION_TRIANGLES = Object.freeze([0, 1, 2, 2, 3, 0]);
|
|
717
767
|
function poseClipped(walk, round, clip = clipTriangles, through = (shape, v, t, uv) => clipThrough(shape, v, t, uv, clip)) {
|
|
718
768
|
const rows = [];
|
|
719
769
|
const refused = [];
|
|
@@ -968,6 +1018,15 @@ function clipThrough(shape, vertices, triangles, uvs, clip = clipTriangles, read
|
|
|
968
1018
|
return { clipped, vertices: outV, uvs: outUv, triangles: outT, sources };
|
|
969
1019
|
}
|
|
970
1020
|
const RAD$2 = RUNTIME_PI / 180;
|
|
1021
|
+
const meshArraysByGeometry = /* @__PURE__ */ new WeakMap();
|
|
1022
|
+
function meshArraysOf(mesh) {
|
|
1023
|
+
let kept = meshArraysByGeometry.get(mesh);
|
|
1024
|
+
if (kept === void 0) {
|
|
1025
|
+
kept = Object.freeze({ uvs: Object.freeze([...mesh.uvs]), triangles: Object.freeze([...mesh.triangles]) });
|
|
1026
|
+
meshArraysByGeometry.set(mesh, kept);
|
|
1027
|
+
}
|
|
1028
|
+
return kept;
|
|
1029
|
+
}
|
|
971
1030
|
const EXACT_COORDS = (v) => v;
|
|
972
1031
|
function regionCorners(region, bone2) {
|
|
973
1032
|
const { width: W, height: H } = region;
|
|
@@ -1019,6 +1078,34 @@ function worldVertices(vertices, bone2, world, coords = Math.fround) {
|
|
|
1019
1078
|
}
|
|
1020
1079
|
return out;
|
|
1021
1080
|
}
|
|
1081
|
+
function deformedWorldVertices(vertices, deform, bone2, world) {
|
|
1082
|
+
const out = [];
|
|
1083
|
+
if (!vertices.weighted) {
|
|
1084
|
+
for (let i = 0; i + 1 < deform.length; i += 2) {
|
|
1085
|
+
const x = deform[i];
|
|
1086
|
+
const y = deform[i + 1];
|
|
1087
|
+
out.push(x * bone2.a + y * bone2.b + bone2.worldX, x * bone2.c + y * bone2.d + bone2.worldY);
|
|
1088
|
+
}
|
|
1089
|
+
return out;
|
|
1090
|
+
}
|
|
1091
|
+
let k = 0;
|
|
1092
|
+
for (const influences of vertices.bindings) {
|
|
1093
|
+
let wx = 0;
|
|
1094
|
+
let wy = 0;
|
|
1095
|
+
for (const binding of influences) {
|
|
1096
|
+
const t = world.get(binding.bone);
|
|
1097
|
+
if (t === void 0) throw new Error(`a binding names bone "${binding.bone}", which has no world transform (readModel refuses it first)`);
|
|
1098
|
+
const x = Math.fround(binding.x) + deform[k];
|
|
1099
|
+
const y = Math.fround(binding.y) + deform[k + 1];
|
|
1100
|
+
k += 2;
|
|
1101
|
+
const weight = Math.fround(binding.weight);
|
|
1102
|
+
wx += (x * t.a + y * t.b + t.worldX) * weight;
|
|
1103
|
+
wy += (x * t.c + y * t.d + t.worldY) * weight;
|
|
1104
|
+
}
|
|
1105
|
+
out.push(wx, wy);
|
|
1106
|
+
}
|
|
1107
|
+
return out;
|
|
1108
|
+
}
|
|
1022
1109
|
const isRecord$7 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
1023
1110
|
const finite$1 = (v) => typeof v === "number" && Number.isFinite(v);
|
|
1024
1111
|
const RECT_FIELDS = ["width", "height", "offsetX", "offsetY", "originalWidth", "originalHeight"];
|
|
@@ -1149,42 +1236,55 @@ function readPathGeometry(raw, vertices, where, problems) {
|
|
|
1149
1236
|
if (problems.length !== before) return void 0;
|
|
1150
1237
|
return { kind: "path", vertices, closed: raw.closed === true, constantSpeed: raw.constantSpeed !== false, lengths };
|
|
1151
1238
|
}
|
|
1239
|
+
function roundedRow(values, round) {
|
|
1240
|
+
for (let i = 0; i < values.length; i++) {
|
|
1241
|
+
const r = round(values[i]);
|
|
1242
|
+
if (Object.is(r, values[i])) continue;
|
|
1243
|
+
const out = values.slice(0, i);
|
|
1244
|
+
out.push(r);
|
|
1245
|
+
for (let j = i + 1; j < values.length; j++) out.push(round(values[j]));
|
|
1246
|
+
return out;
|
|
1247
|
+
}
|
|
1248
|
+
return values;
|
|
1249
|
+
}
|
|
1152
1250
|
function poseGeometry(shown, world, sourceOf, round, plant = {}, draw) {
|
|
1153
1251
|
const region = plant.region ?? regionCorners;
|
|
1154
1252
|
const vertices = plant.vertices ?? worldVertices;
|
|
1155
1253
|
const attachments = [];
|
|
1156
1254
|
const clips = [];
|
|
1157
1255
|
const nulls = [];
|
|
1256
|
+
const walked = draw !== void 0 && shown.some((s) => s.geometry.kind === "clipping");
|
|
1158
1257
|
const steps = /* @__PURE__ */ new Map();
|
|
1159
1258
|
for (const s of shown) {
|
|
1160
1259
|
const bone2 = world.get(s.bone);
|
|
1161
1260
|
if (bone2 === void 0) throw new Error(`slot "${s.slot}": bone "${s.bone}" has no world transform`);
|
|
1162
1261
|
const g = s.geometry;
|
|
1163
|
-
const
|
|
1262
|
+
const deform = s.deform;
|
|
1263
|
+
const drawn = (v) => deform === void 0 ? vertices(v, bone2, world) : plant.vertices === void 0 ? deformedWorldVertices(v, deform, bone2, world) : vertices(deformedVertices(v, deform), bone2, world, EXACT_COORDS);
|
|
1164
1264
|
if (g.kind === "region") {
|
|
1165
1265
|
const rect = s.frame === void 0 ? g.region.atlas : g.region.frames?.[s.frame] ?? g.region.atlas;
|
|
1166
1266
|
if (rect === null) {
|
|
1167
1267
|
nulls.push(`slot "${s.slot}" shows region "${s.name}" (skin "${s.skin}", placeholder "${s.placeholder}")`);
|
|
1168
1268
|
continue;
|
|
1169
1269
|
}
|
|
1170
|
-
const corners = region({ ...g.region, atlas: rect }, bone2);
|
|
1171
|
-
attachments.push([s.slot, s.name, "region", corners
|
|
1172
|
-
steps.set(s.slot, { slot: s.slot, kind: "draw", attachment: s.name, vertices: corners, triangles:
|
|
1270
|
+
const corners = region(rect === g.region.atlas ? g.region : { ...g.region, atlas: rect }, bone2);
|
|
1271
|
+
attachments.push([s.slot, s.name, "region", roundedRow(corners, round)]);
|
|
1272
|
+
if (walked) steps.set(s.slot, { slot: s.slot, kind: "draw", attachment: s.name, vertices: corners, triangles: REGION_TRIANGLES, uvs: REGION_UVS });
|
|
1173
1273
|
} else if (g.kind === "mesh") {
|
|
1174
1274
|
const world2 = drawn(g.vertices);
|
|
1175
|
-
attachments.push([s.slot, s.name, "mesh", world2
|
|
1176
|
-
steps.set(s.slot, { slot: s.slot, kind: "draw", attachment: s.name, vertices: world2, triangles: g.triangles, uvs: g.uvs });
|
|
1275
|
+
attachments.push([s.slot, s.name, "mesh", roundedRow(world2, round)]);
|
|
1276
|
+
if (walked) steps.set(s.slot, { slot: s.slot, kind: "draw", attachment: s.name, vertices: world2, triangles: g.triangles, uvs: g.uvs });
|
|
1177
1277
|
} else if (g.kind === "linkedmesh") {
|
|
1178
1278
|
const source = sourceOf(g.skin, g.slot, g.source);
|
|
1179
1279
|
if (typeof source === "string") throw new Error(`slot "${s.slot}": ${source} (readModel refuses it first)`);
|
|
1180
1280
|
const world2 = drawn(source);
|
|
1181
|
-
attachments.push([s.slot, s.name, "mesh", world2
|
|
1281
|
+
attachments.push([s.slot, s.name, "mesh", roundedRow(world2, round)]);
|
|
1182
1282
|
const mesh = draw?.meshOf(g.skin, g.slot, g.source);
|
|
1183
1283
|
if (draw !== void 0 && mesh === void 0) throw new Error(`slot "${s.slot}": the linked mesh's source "${g.source}" carries no triangles (readModel refuses it first)`);
|
|
1184
|
-
if (mesh !== void 0) steps.set(s.slot, { slot: s.slot, kind: "draw", attachment: s.name, vertices: world2, triangles: mesh.triangles, uvs: mesh.uvs });
|
|
1284
|
+
if (walked && mesh !== void 0) steps.set(s.slot, { slot: s.slot, kind: "draw", attachment: s.name, vertices: world2, triangles: mesh.triangles, uvs: mesh.uvs });
|
|
1185
1285
|
} else if (g.kind === "clipping") {
|
|
1186
1286
|
const polygon = drawn(g.vertices);
|
|
1187
|
-
clips.push([s.slot, s.name, g.end, polygon
|
|
1287
|
+
clips.push([s.slot, s.name, g.end, roundedRow(polygon, round)]);
|
|
1188
1288
|
steps.set(s.slot, { slot: s.slot, kind: "clip", attachment: s.name, active: draw?.active.has(s.bone) ?? false, end: g.end, polygon, inverse: g.inverse, convex: g.convex });
|
|
1189
1289
|
}
|
|
1190
1290
|
}
|
|
@@ -1193,7 +1293,12 @@ function poseGeometry(shown, world, sourceOf, round, plant = {}, draw) {
|
|
|
1193
1293
|
let clippedWhy = draw === void 0 ? "the draw order was not given" : attachmentsWhy;
|
|
1194
1294
|
let drawnClipped = null;
|
|
1195
1295
|
let drawnClippedWhy = clippedWhy;
|
|
1196
|
-
if (draw !== void 0 && attachmentsWhy === null) {
|
|
1296
|
+
if (draw !== void 0 && attachmentsWhy === null && !walked) {
|
|
1297
|
+
clipped = [];
|
|
1298
|
+
clippedWhy = null;
|
|
1299
|
+
drawnClipped = [];
|
|
1300
|
+
drawnClippedWhy = null;
|
|
1301
|
+
} else if (draw !== void 0 && attachmentsWhy === null) {
|
|
1197
1302
|
const walk = draw.order.map((slot) => steps.get(slot) ?? { slot, kind: "none" });
|
|
1198
1303
|
const posed = poseClipped(walk, round, draw.clip, draw.through);
|
|
1199
1304
|
clipped = posed.rows;
|
|
@@ -2157,6 +2262,15 @@ function stepPhysics(r, w, length, ctx) {
|
|
|
2157
2262
|
const k = 1 + (s.scaleOffset - s.scaleLag * z) * mix * r.scaleX;
|
|
2158
2263
|
w.a *= k;
|
|
2159
2264
|
w.c *= k;
|
|
2265
|
+
if (r.scaleYMode === "Uniform") {
|
|
2266
|
+
w.b *= k;
|
|
2267
|
+
w.d *= k;
|
|
2268
|
+
} else if (r.scaleYMode === "Volume") {
|
|
2269
|
+
const magnitude = Math.abs(k);
|
|
2270
|
+
const transverse = magnitude >= 0.7 ? 1 / magnitude : 4 - 3.67347 * magnitude;
|
|
2271
|
+
w.b *= transverse;
|
|
2272
|
+
w.d *= transverse;
|
|
2273
|
+
}
|
|
2160
2274
|
}
|
|
2161
2275
|
s.tx = l * w.a;
|
|
2162
2276
|
s.ty = l * w.c;
|
|
@@ -4702,8 +4816,8 @@ function shownRow(shown) {
|
|
|
4702
4816
|
return { name, path: CORE_REGION_KINDS.has(shown.record.kind) ? shown.record.path ?? name : null };
|
|
4703
4817
|
}
|
|
4704
4818
|
const shownOfView = /* @__PURE__ */ new WeakMap();
|
|
4705
|
-
function shownAttachment(doc, slot) {
|
|
4706
|
-
if (
|
|
4819
|
+
function shownAttachment(doc, slot, placeholder = slot.setup) {
|
|
4820
|
+
if (placeholder === null) return null;
|
|
4707
4821
|
let bySlot = shownOfView.get(doc);
|
|
4708
4822
|
if (bySlot === void 0) {
|
|
4709
4823
|
bySlot = /* @__PURE__ */ new Map();
|
|
@@ -4714,9 +4828,9 @@ function shownAttachment(doc, slot) {
|
|
|
4714
4828
|
byPlaceholder = /* @__PURE__ */ new Map();
|
|
4715
4829
|
bySlot.set(slot.name, byPlaceholder);
|
|
4716
4830
|
}
|
|
4717
|
-
if (byPlaceholder.has(
|
|
4718
|
-
const resolved = resolveShown(doc, slot.name,
|
|
4719
|
-
byPlaceholder.set(
|
|
4831
|
+
if (byPlaceholder.has(placeholder)) return byPlaceholder.get(placeholder);
|
|
4832
|
+
const resolved = resolveShown(doc, slot.name, placeholder);
|
|
4833
|
+
byPlaceholder.set(placeholder, resolved);
|
|
4720
4834
|
return resolved;
|
|
4721
4835
|
}
|
|
4722
4836
|
function resolveShown(doc, slotName, placeholder) {
|
|
@@ -4907,6 +5021,13 @@ function drawnGeometry(doc, shown, world, order, plant, mode) {
|
|
|
4907
5021
|
if (geometry.drawnClipped === null) throw new CoreInputError(`the raw pose leaves the clipped triangles out: ${geometry.drawnClippedWhy}`);
|
|
4908
5022
|
return { rows: geometry.attachments, clips: geometry.clips, drawnClipped: geometry.drawnClipped };
|
|
4909
5023
|
}
|
|
5024
|
+
function numbersOf(values, what, mode) {
|
|
5025
|
+
return allNumbers(values) ? values : values.map((v) => numberOf(v, what, mode));
|
|
5026
|
+
}
|
|
5027
|
+
function allNumbers(values) {
|
|
5028
|
+
for (const v of values) if (v === null) return false;
|
|
5029
|
+
return true;
|
|
5030
|
+
}
|
|
4910
5031
|
function rawDrawn(doc, shown, world, order, plant, mode = RAW_MODE) {
|
|
4911
5032
|
const geometry = drawnGeometry(doc, shown, world, order, plant, mode);
|
|
4912
5033
|
const rows = geometry.rows;
|
|
@@ -4923,25 +5044,56 @@ function rawDrawn(doc, shown, world, order, plant, mode = RAW_MODE) {
|
|
|
4923
5044
|
let triangles;
|
|
4924
5045
|
let hull = null;
|
|
4925
5046
|
if (g.kind === "region") {
|
|
4926
|
-
uvs =
|
|
4927
|
-
triangles =
|
|
5047
|
+
uvs = REGION_UVS;
|
|
5048
|
+
triangles = REGION_TRIANGLES;
|
|
4928
5049
|
} else {
|
|
4929
5050
|
const source = g.kind === "mesh" ? g : doc.skins.find((x) => x.name === g.skin)?.attachments[g.slot]?.[g.source]?.geometry;
|
|
4930
5051
|
if (source === void 0 || source.kind !== "mesh") throw new CoreInputError(`slot "${s.slot}": the linked mesh's source carries no mesh`);
|
|
4931
|
-
uvs =
|
|
4932
|
-
triangles = [...source.triangles];
|
|
5052
|
+
({ uvs, triangles } = (plant.meshArrays ?? meshArraysOf)(source));
|
|
4933
5053
|
hull = source.hull ?? null;
|
|
4934
5054
|
}
|
|
4935
5055
|
const sequenceIndex = s.frame ?? record?.sequenceSetup ?? 0;
|
|
4936
|
-
drawn.push({ slot: s.slot, attachment: row[1], kind: row[2], vertices: row[3]
|
|
5056
|
+
drawn.push({ slot: s.slot, attachment: row[1], kind: row[2], vertices: numbersOf(row[3], `vertex of slot "${s.slot}"`, mode), uvs, triangles, hull, sequenceIndex, colour });
|
|
4937
5057
|
}
|
|
4938
|
-
const clips = geometry.clips.map((c) => [c[0], c[1], c[2], c[3]
|
|
4939
|
-
const clipped = geometry.drawnClipped.map((c) => [c[0], c[1], c[2], c[3]
|
|
5058
|
+
const clips = geometry.clips.map((c) => [c[0], c[1], c[2], numbersOf(c[3], `clip vertex of slot "${c[0]}"`, mode)]);
|
|
5059
|
+
const clipped = geometry.drawnClipped.map((c) => [c[0], c[1], c[2], numbersOf(c[3], `clipped vertex of slot "${c[0]}"`, mode), numbersOf(c[4], `clipped uv of slot "${c[0]}"`, mode), c[5]]);
|
|
4940
5060
|
return { drawn, clips, clipped };
|
|
4941
5061
|
}
|
|
4942
5062
|
function poseRawSetup(doc, plant = {}) {
|
|
4943
5063
|
return setupPoseIn(doc, plant, RAW_MODE);
|
|
4944
5064
|
}
|
|
5065
|
+
function sameSetupMeshDefinition(a, b) {
|
|
5066
|
+
if (Object.is(a, b)) return true;
|
|
5067
|
+
if (typeof a !== "object" || a === null || typeof b !== "object" || b === null) return false;
|
|
5068
|
+
if (Array.isArray(a) !== Array.isArray(b)) return false;
|
|
5069
|
+
if (Array.isArray(a) && Array.isArray(b) && a.length !== b.length) return false;
|
|
5070
|
+
const left = a;
|
|
5071
|
+
const right = b;
|
|
5072
|
+
const keys = Object.keys(left);
|
|
5073
|
+
return keys.length === Object.keys(right).length && keys.every((key) => Object.prototype.hasOwnProperty.call(right, key) && sameSetupMeshDefinition(left[key], right[key]));
|
|
5074
|
+
}
|
|
5075
|
+
function readSetupMesh$1(doc, target) {
|
|
5076
|
+
const view = target.skin === void 0 ? doc : target.skin === null ? underNoSkin(doc) : underSkin(doc, target.skin);
|
|
5077
|
+
const where = `setup mesh slot ${JSON.stringify(target.slot)} attachment ${JSON.stringify(target.attachment)} under skin ${JSON.stringify(view.skin)}`;
|
|
5078
|
+
const slot = view.slots.find((s) => s.name === target.slot);
|
|
5079
|
+
if (slot === void 0) throw new CoreInputError(`${where}: no such slot`);
|
|
5080
|
+
const resolved = shownAttachment(view, slot, target.attachment);
|
|
5081
|
+
if (resolved === null) throw new CoreInputError(`${where}: no such attachment`);
|
|
5082
|
+
const fills = fillingSkins(view, slot.name, target.attachment);
|
|
5083
|
+
const definitions = fills.map((name) => view.skins.find((k) => k.name === name)?.attachments[slot.name]?.[target.attachment]);
|
|
5084
|
+
if ("conflict" in resolved || definitions.some((r) => !sameSetupMeshDefinition(r, definitions[0]))) {
|
|
5085
|
+
throw new CoreInputError(`${where}: skins [${fills.join(", ")}] fill this attachment differently; choose a named skin`);
|
|
5086
|
+
}
|
|
5087
|
+
const geometry = resolved.record.geometry;
|
|
5088
|
+
const mesh = geometry?.kind === "linkedmesh" ? view.skins.find((s) => s.name === geometry.skin)?.attachments[geometry.slot]?.[geometry.source]?.geometry : geometry;
|
|
5089
|
+
if (mesh?.kind !== "mesh") throw new CoreInputError(`${where}: ${resolved.record.kind} is not a mesh`);
|
|
5090
|
+
const { world, shown, drawOrder } = setupPosed(view, {}, RAW_MODE);
|
|
5091
|
+
const selected = shown.find((s) => s.slot === slot.name && s.placeholder === target.attachment && s.skin === resolved.skin);
|
|
5092
|
+
const bone2 = world.get(slot.bone);
|
|
5093
|
+
if (bone2 === void 0) throw new CoreInputError(`${where}: no setup transform for bone ${JSON.stringify(slot.bone)}`);
|
|
5094
|
+
const vertices = selected === void 0 ? numbersOf(worldVertices(mesh.vertices, bone2, world).map(rawNumber), `${where} vertex`, RAW_MODE) : rawDrawn(view, [selected], world, drawOrder, {}).drawn[0].vertices;
|
|
5095
|
+
return { slot: slot.name, attachment: target.attachment, skin: resolved.skin, vertices: [...vertices], triangles: [...mesh.triangles], hull: mesh.hull ?? null };
|
|
5096
|
+
}
|
|
4945
5097
|
function setupPoseIn(doc, plant, mode) {
|
|
4946
5098
|
const { world, shown, slots, drawOrder } = setupPosed(doc, plant, mode);
|
|
4947
5099
|
const drawn = rawDrawn(doc, shown, world, drawOrder, plant, mode);
|
|
@@ -5015,7 +5167,7 @@ function animationOf(doc, animation) {
|
|
|
5015
5167
|
if (anim === void 0) throw new CoreInputError(`animation "${animation}" is not one of this document's [${doc.animations.map((a) => a.name).join(", ")}]`);
|
|
5016
5168
|
return anim;
|
|
5017
5169
|
}
|
|
5018
|
-
function openWalk(doc, anim, plant, reset, mode, shape, adjust) {
|
|
5170
|
+
function openWalk(doc, anim, plant, reset, mode, shape, adjust, before) {
|
|
5019
5171
|
const why = constraintsAbsentWhy(doc) ?? pathAnimationsWhy(doc) ?? steppedPreviousPassWhy(doc);
|
|
5020
5172
|
if (why !== null) throw new CoreInputError(`the raw walk leaves the bones out: ${why}`);
|
|
5021
5173
|
const ctx = freshStepContext(plant.physicsStep);
|
|
@@ -8343,6 +8495,9 @@ function affine(u0, v0, u1, v1, u2, v2, x0, y0, x1, y1, x2, y2) {
|
|
|
8343
8495
|
const d = (eu1 * ey2 - eu2 * ey1) * inv;
|
|
8344
8496
|
return [a, b, c, d, x0 - a * u0 - c * v0, y0 - b * u0 - d * v0];
|
|
8345
8497
|
}
|
|
8498
|
+
function readSetupMesh(assets, target) {
|
|
8499
|
+
return readSetupMesh$1(underNoSkin(assets.document.doc), target);
|
|
8500
|
+
}
|
|
8346
8501
|
const SETS_SPECS = ["spine-parts-sets/1", "rig-parts-sets/1"];
|
|
8347
8502
|
function isRecord(value) {
|
|
8348
8503
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -9867,6 +10022,7 @@ export {
|
|
|
9867
10022
|
DomRenderer,
|
|
9868
10023
|
RigPlayer,
|
|
9869
10024
|
loadModel,
|
|
10025
|
+
readSetupMesh,
|
|
9870
10026
|
revokeRegions,
|
|
9871
10027
|
unpackRegions
|
|
9872
10028
|
};
|