spine-html 1.12.1 → 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 +110 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +399 -102
- package/dist/index.js.map +1 -1
- package/dist/loadModel.d.ts +25 -0
- package/dist/loadModel.d.ts.map +1 -1
- package/dist/player.d.ts +47 -1
- package/dist/player.d.ts.map +1 -1
- package/package.json +2 -2
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, values2, timelines, t, blend, evalDeform, blendOf2, evalFrame) {
|
|
440
|
+
if (now === null || now.identity === null) return values2;
|
|
441
|
+
const matching = matchingAttachments(timelines, now.identity);
|
|
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
|
+
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) values2.deform = blend === null ? target : blendOf2(now.vertices, values2.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) values2.frame = f;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
return values2;
|
|
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 values2 = 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) values2 = applyAttachment(now, values2, 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 (values2 !== null) {
|
|
480
|
+
values2.deform = null;
|
|
481
|
+
values2.frame = null;
|
|
433
482
|
}
|
|
434
483
|
placeholder = next;
|
|
484
|
+
now = identityOf(doc, resolve, slot, placeholder);
|
|
435
485
|
}
|
|
436
486
|
}
|
|
437
|
-
|
|
487
|
+
values2 = applyAttachment(now, values2, 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 = values2 === null || values2.deform === null && values2.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 (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`);
|
|
446
494
|
continue;
|
|
447
495
|
}
|
|
448
|
-
if (
|
|
449
|
-
|
|
496
|
+
if (values2 !== null) {
|
|
497
|
+
if (values2.deform !== null) entry.deform = values2.deform;
|
|
498
|
+
if (values2.frame !== null) entry.frame = values2.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(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
|
+
out.push(r);
|
|
1245
|
+
for (let j = i + 1; j < values2.length; j++) out.push(round(values2[j]));
|
|
1246
|
+
return out;
|
|
1247
|
+
}
|
|
1248
|
+
return values2;
|
|
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;
|
|
@@ -3317,7 +3431,8 @@ function applyConstraints(bones, world, active, records, previous = null, applie
|
|
|
3317
3431
|
if (c.kind === "physics") {
|
|
3318
3432
|
if (physics !== void 0) {
|
|
3319
3433
|
const w = { ...state.world.get(c.bone) };
|
|
3320
|
-
const
|
|
3434
|
+
const authored = physicsPose?.records.get(c.name) ?? c;
|
|
3435
|
+
const posed = physics.parameters?.apply(authored) ?? authored;
|
|
3321
3436
|
if ((physics.step ?? stepPhysics)(posed, w, bone(state, c.bone).length ?? 0, physics)) {
|
|
3322
3437
|
state.world.set(c.bone, w);
|
|
3323
3438
|
changed = [c.bone];
|
|
@@ -3598,7 +3713,7 @@ function readBoneTimeline(kind, keysValue, where, problems) {
|
|
|
3598
3713
|
}
|
|
3599
3714
|
const channels = BONE_TIMELINE_CHANNELS[kind];
|
|
3600
3715
|
unknownKeyFields(raw, ["time", ...channels, "curve"], at, problems);
|
|
3601
|
-
const
|
|
3716
|
+
const values2 = channels.map((c) => {
|
|
3602
3717
|
const v = raw[c];
|
|
3603
3718
|
if (typeof v !== "number" || !Number.isFinite(v)) {
|
|
3604
3719
|
problems.push(`${at}: ${c} is ${JSON.stringify(v)}, not a finite number — the writer states every channel on every key`);
|
|
@@ -3606,7 +3721,7 @@ function readBoneTimeline(kind, keysValue, where, problems) {
|
|
|
3606
3721
|
}
|
|
3607
3722
|
return v;
|
|
3608
3723
|
});
|
|
3609
|
-
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) };
|
|
3610
3725
|
});
|
|
3611
3726
|
}
|
|
3612
3727
|
function hexChannels(hex, count) {
|
|
@@ -3623,22 +3738,22 @@ function readSlotTimeline(kind, keysValue, where, problems) {
|
|
|
3623
3738
|
return { time: Math.fround(time), values: [], stated: { time, values: [] }, curve: "stepped", name: typeof raw.name === "string" ? raw.name : null };
|
|
3624
3739
|
}
|
|
3625
3740
|
const shape = SLOT_COLOUR_TIMELINES[kind];
|
|
3626
|
-
let
|
|
3741
|
+
let values2 = [];
|
|
3627
3742
|
if (kind === "alpha") {
|
|
3628
3743
|
unknownKeyFields(raw, ["time", "value", "curve"], at, problems);
|
|
3629
3744
|
const v = raw.value;
|
|
3630
3745
|
if (typeof v !== "number" || !Number.isFinite(v)) problems.push(`${at}: value is ${JSON.stringify(v)}, not a finite number`);
|
|
3631
|
-
else
|
|
3746
|
+
else values2 = [v];
|
|
3632
3747
|
} else {
|
|
3633
3748
|
unknownKeyFields(raw, ["time", ...shape.fields.map(([f]) => f), "curve"], at, problems);
|
|
3634
3749
|
for (const [field, digits] of shape.fields) {
|
|
3635
3750
|
const v = raw[field];
|
|
3636
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}`);
|
|
3637
|
-
else
|
|
3752
|
+
else values2.push(...hexChannels(v, field === "dark" || digits === 6 ? 3 : 4));
|
|
3638
3753
|
}
|
|
3639
3754
|
}
|
|
3640
|
-
if (
|
|
3641
|
-
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) };
|
|
3642
3757
|
});
|
|
3643
3758
|
}
|
|
3644
3759
|
function constraintKeyTimes(value, where, problems) {
|
|
@@ -3934,7 +4049,7 @@ class StepLocals {
|
|
|
3934
4049
|
this.write("shearY", v);
|
|
3935
4050
|
}
|
|
3936
4051
|
}
|
|
3937
|
-
function adjustLocals(doc, bones, adjust) {
|
|
4052
|
+
function adjustLocals(doc, bones, adjust, context) {
|
|
3938
4053
|
const views = /* @__PURE__ */ new Map();
|
|
3939
4054
|
const names = doc.bones.map((b) => b.name);
|
|
3940
4055
|
adjust({
|
|
@@ -3949,7 +4064,7 @@ function adjustLocals(doc, bones, adjust) {
|
|
|
3949
4064
|
}
|
|
3950
4065
|
return view;
|
|
3951
4066
|
}
|
|
3952
|
-
});
|
|
4067
|
+
}, context);
|
|
3953
4068
|
}
|
|
3954
4069
|
const clamp01$1 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
|
|
3955
4070
|
function posedSlots(doc, timelines, t, plant = {}, sliders = [], placeholders) {
|
|
@@ -4036,7 +4151,7 @@ function posedBoneStep(doc, timelines, t, plant, constraints, sliders, step) {
|
|
|
4036
4151
|
const bones = posedBones(doc, timelines, t, plant);
|
|
4037
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);
|
|
4038
4153
|
const active = activeBones(doc);
|
|
4039
|
-
if (step?.
|
|
4154
|
+
if (step?.adjustment !== void 0) adjustLocals(doc, bones, step.adjustment.callback, step.adjustment.context);
|
|
4040
4155
|
let world = (plant.evaluate ?? worldTransforms)(bones, active);
|
|
4041
4156
|
if (constraints !== void 0) {
|
|
4042
4157
|
const setupRecords = constraintRecords(doc);
|
|
@@ -4049,7 +4164,7 @@ function posedBoneStep(doc, timelines, t, plant, constraints, sliders, step) {
|
|
|
4049
4164
|
}
|
|
4050
4165
|
function steppedLocals(doc, bones, plant, records, physicsKeyed, t, sliders, step) {
|
|
4051
4166
|
const active = activeBones(doc);
|
|
4052
|
-
if (step.
|
|
4167
|
+
if (step.adjustment !== void 0) adjustLocals(doc, bones, step.adjustment.callback, step.adjustment.context);
|
|
4053
4168
|
let world = (plant.evaluate ?? worldTransforms)(bones, active);
|
|
4054
4169
|
const stepped = stepPhysicsRecords(records(), physicsKeyed, t, active, step.ctx, step.before);
|
|
4055
4170
|
world = applyConstraints(bones, world, active, plant.constraints ? plant.constraints(stepped) : stepped, null, sliders, step.ctx, void 0, solverRules(plant.solver));
|
|
@@ -4702,8 +4817,8 @@ function shownRow(shown) {
|
|
|
4702
4817
|
return { name, path: CORE_REGION_KINDS.has(shown.record.kind) ? shown.record.path ?? name : null };
|
|
4703
4818
|
}
|
|
4704
4819
|
const shownOfView = /* @__PURE__ */ new WeakMap();
|
|
4705
|
-
function shownAttachment(doc, slot) {
|
|
4706
|
-
if (
|
|
4820
|
+
function shownAttachment(doc, slot, placeholder = slot.setup) {
|
|
4821
|
+
if (placeholder === null) return null;
|
|
4707
4822
|
let bySlot = shownOfView.get(doc);
|
|
4708
4823
|
if (bySlot === void 0) {
|
|
4709
4824
|
bySlot = /* @__PURE__ */ new Map();
|
|
@@ -4714,9 +4829,9 @@ function shownAttachment(doc, slot) {
|
|
|
4714
4829
|
byPlaceholder = /* @__PURE__ */ new Map();
|
|
4715
4830
|
bySlot.set(slot.name, byPlaceholder);
|
|
4716
4831
|
}
|
|
4717
|
-
if (byPlaceholder.has(
|
|
4718
|
-
const resolved = resolveShown(doc, slot.name,
|
|
4719
|
-
byPlaceholder.set(
|
|
4832
|
+
if (byPlaceholder.has(placeholder)) return byPlaceholder.get(placeholder);
|
|
4833
|
+
const resolved = resolveShown(doc, slot.name, placeholder);
|
|
4834
|
+
byPlaceholder.set(placeholder, resolved);
|
|
4720
4835
|
return resolved;
|
|
4721
4836
|
}
|
|
4722
4837
|
function resolveShown(doc, slotName, placeholder) {
|
|
@@ -4907,6 +5022,13 @@ function drawnGeometry(doc, shown, world, order, plant, mode) {
|
|
|
4907
5022
|
if (geometry.drawnClipped === null) throw new CoreInputError(`the raw pose leaves the clipped triangles out: ${geometry.drawnClippedWhy}`);
|
|
4908
5023
|
return { rows: geometry.attachments, clips: geometry.clips, drawnClipped: geometry.drawnClipped };
|
|
4909
5024
|
}
|
|
5025
|
+
function numbersOf(values2, what, mode) {
|
|
5026
|
+
return allNumbers(values2) ? values2 : values2.map((v) => numberOf(v, what, mode));
|
|
5027
|
+
}
|
|
5028
|
+
function allNumbers(values2) {
|
|
5029
|
+
for (const v of values2) if (v === null) return false;
|
|
5030
|
+
return true;
|
|
5031
|
+
}
|
|
4910
5032
|
function rawDrawn(doc, shown, world, order, plant, mode = RAW_MODE) {
|
|
4911
5033
|
const geometry = drawnGeometry(doc, shown, world, order, plant, mode);
|
|
4912
5034
|
const rows = geometry.rows;
|
|
@@ -4923,25 +5045,56 @@ function rawDrawn(doc, shown, world, order, plant, mode = RAW_MODE) {
|
|
|
4923
5045
|
let triangles;
|
|
4924
5046
|
let hull = null;
|
|
4925
5047
|
if (g.kind === "region") {
|
|
4926
|
-
uvs =
|
|
4927
|
-
triangles =
|
|
5048
|
+
uvs = REGION_UVS;
|
|
5049
|
+
triangles = REGION_TRIANGLES;
|
|
4928
5050
|
} else {
|
|
4929
5051
|
const source = g.kind === "mesh" ? g : doc.skins.find((x) => x.name === g.skin)?.attachments[g.slot]?.[g.source]?.geometry;
|
|
4930
5052
|
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];
|
|
5053
|
+
({ uvs, triangles } = (plant.meshArrays ?? meshArraysOf)(source));
|
|
4933
5054
|
hull = source.hull ?? null;
|
|
4934
5055
|
}
|
|
4935
5056
|
const sequenceIndex = s.frame ?? record?.sequenceSetup ?? 0;
|
|
4936
|
-
drawn.push({ slot: s.slot, attachment: row[1], kind: row[2], vertices: row[3]
|
|
5057
|
+
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
5058
|
}
|
|
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]
|
|
5059
|
+
const clips = geometry.clips.map((c) => [c[0], c[1], c[2], numbersOf(c[3], `clip vertex of slot "${c[0]}"`, mode)]);
|
|
5060
|
+
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
5061
|
return { drawn, clips, clipped };
|
|
4941
5062
|
}
|
|
4942
5063
|
function poseRawSetup(doc, plant = {}) {
|
|
4943
5064
|
return setupPoseIn(doc, plant, RAW_MODE);
|
|
4944
5065
|
}
|
|
5066
|
+
function sameSetupMeshDefinition(a, b) {
|
|
5067
|
+
if (Object.is(a, b)) return true;
|
|
5068
|
+
if (typeof a !== "object" || a === null || typeof b !== "object" || b === null) return false;
|
|
5069
|
+
if (Array.isArray(a) !== Array.isArray(b)) return false;
|
|
5070
|
+
if (Array.isArray(a) && Array.isArray(b) && a.length !== b.length) return false;
|
|
5071
|
+
const left = a;
|
|
5072
|
+
const right = b;
|
|
5073
|
+
const keys = Object.keys(left);
|
|
5074
|
+
return keys.length === Object.keys(right).length && keys.every((key) => Object.prototype.hasOwnProperty.call(right, key) && sameSetupMeshDefinition(left[key], right[key]));
|
|
5075
|
+
}
|
|
5076
|
+
function readSetupMesh$1(doc, target) {
|
|
5077
|
+
const view = target.skin === void 0 ? doc : target.skin === null ? underNoSkin(doc) : underSkin(doc, target.skin);
|
|
5078
|
+
const where = `setup mesh slot ${JSON.stringify(target.slot)} attachment ${JSON.stringify(target.attachment)} under skin ${JSON.stringify(view.skin)}`;
|
|
5079
|
+
const slot = view.slots.find((s) => s.name === target.slot);
|
|
5080
|
+
if (slot === void 0) throw new CoreInputError(`${where}: no such slot`);
|
|
5081
|
+
const resolved = shownAttachment(view, slot, target.attachment);
|
|
5082
|
+
if (resolved === null) throw new CoreInputError(`${where}: no such attachment`);
|
|
5083
|
+
const fills = fillingSkins(view, slot.name, target.attachment);
|
|
5084
|
+
const definitions = fills.map((name) => view.skins.find((k) => k.name === name)?.attachments[slot.name]?.[target.attachment]);
|
|
5085
|
+
if ("conflict" in resolved || definitions.some((r) => !sameSetupMeshDefinition(r, definitions[0]))) {
|
|
5086
|
+
throw new CoreInputError(`${where}: skins [${fills.join(", ")}] fill this attachment differently; choose a named skin`);
|
|
5087
|
+
}
|
|
5088
|
+
const geometry = resolved.record.geometry;
|
|
5089
|
+
const mesh = geometry?.kind === "linkedmesh" ? view.skins.find((s) => s.name === geometry.skin)?.attachments[geometry.slot]?.[geometry.source]?.geometry : geometry;
|
|
5090
|
+
if (mesh?.kind !== "mesh") throw new CoreInputError(`${where}: ${resolved.record.kind} is not a mesh`);
|
|
5091
|
+
const { world, shown, drawOrder } = setupPosed(view, {}, RAW_MODE);
|
|
5092
|
+
const selected = shown.find((s) => s.slot === slot.name && s.placeholder === target.attachment && s.skin === resolved.skin);
|
|
5093
|
+
const bone2 = world.get(slot.bone);
|
|
5094
|
+
if (bone2 === void 0) throw new CoreInputError(`${where}: no setup transform for bone ${JSON.stringify(slot.bone)}`);
|
|
5095
|
+
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;
|
|
5096
|
+
return { slot: slot.name, attachment: target.attachment, skin: resolved.skin, vertices: [...vertices], triangles: [...mesh.triangles], hull: mesh.hull ?? null };
|
|
5097
|
+
}
|
|
4945
5098
|
function setupPoseIn(doc, plant, mode) {
|
|
4946
5099
|
const { world, shown, slots, drawOrder } = setupPosed(doc, plant, mode);
|
|
4947
5100
|
const drawn = rawDrawn(doc, shown, world, drawOrder, plant, mode);
|
|
@@ -5015,10 +5168,11 @@ function animationOf(doc, animation) {
|
|
|
5015
5168
|
if (anim === void 0) throw new CoreInputError(`animation "${animation}" is not one of this document's [${doc.animations.map((a) => a.name).join(", ")}]`);
|
|
5016
5169
|
return anim;
|
|
5017
5170
|
}
|
|
5018
|
-
function openWalk(doc, anim, plant, reset, mode, shape, adjust) {
|
|
5171
|
+
function openWalk(doc, anim, plant, reset, mode, shape, adjust, before) {
|
|
5019
5172
|
const why = constraintsAbsentWhy(doc) ?? pathAnimationsWhy(doc) ?? steppedPreviousPassWhy(doc);
|
|
5020
5173
|
if (why !== null) throw new CoreInputError(`the raw walk leaves the bones out: ${why}`);
|
|
5021
5174
|
const ctx = freshStepContext(plant.physicsStep);
|
|
5175
|
+
before?.(ctx);
|
|
5022
5176
|
if (mode.loop && mode.wrapResets !== false) ctx.loop = true;
|
|
5023
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 };
|
|
5024
5178
|
if (reset === "setup") {
|
|
@@ -5041,8 +5195,9 @@ function stepWalk(track, dt, first = false) {
|
|
|
5041
5195
|
ctx.time += mode.clock === void 0 ? dt : mode.clock(dt, t - (first ? 0 : Math.max(last, 0)));
|
|
5042
5196
|
const fire = plant.events ?? eventsFired;
|
|
5043
5197
|
const keys = anim.timelines.events;
|
|
5198
|
+
const adjustment = adjust === void 0 ? void 0 : { callback: adjust, context: { trackTime, animation: anim.name, globalTime: trackTime } };
|
|
5044
5199
|
const pose = poseStepFrom(doc, plant, resolve, shape, mode, ctx, first, trackTime, t, {
|
|
5045
|
-
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 }),
|
|
5046
5201
|
slots: (sliders, placeholders) => posedSlots(doc, anim.timelines, t, raw, sliders, placeholders),
|
|
5047
5202
|
drawOrder: anim.timelines.drawOrder,
|
|
5048
5203
|
at: t,
|
|
@@ -5090,12 +5245,56 @@ function openRawTracksPath(doc, plant) {
|
|
|
5090
5245
|
step: (first, trackTime, animationTime, posers) => poseStepFrom(doc, plant, resolve, RAW_SHAPE, RAW_MODE, ctx, first, trackTime, animationTime, posers)
|
|
5091
5246
|
};
|
|
5092
5247
|
}
|
|
5093
|
-
function openRawTrack(doc, animation, plant, reset, mode, adjust) {
|
|
5094
|
-
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);
|
|
5095
5250
|
}
|
|
5096
5251
|
function stepRawTrack(track, dt) {
|
|
5097
5252
|
return stepWalk(track, dt);
|
|
5098
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
|
+
}
|
|
5099
5298
|
const HELD = { loop: false, keep: false };
|
|
5100
5299
|
const LOOPED = { loop: true, keep: false, live: true };
|
|
5101
5300
|
function openTrack(doc, animation, options, plant = {}) {
|
|
@@ -5105,11 +5304,15 @@ function openTrack(doc, animation, options, plant = {}) {
|
|
|
5105
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`);
|
|
5106
5305
|
const mode = options.loop ? LOOPED : HELD;
|
|
5107
5306
|
const clock = options.clock === void 0 ? void 0 : checkedClock(options.clock, "the live track's");
|
|
5108
|
-
const
|
|
5109
|
-
|
|
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);
|
|
5110
5312
|
}
|
|
5111
5313
|
class Track {
|
|
5112
|
-
constructor(track, first) {
|
|
5314
|
+
constructor(track, first, parameters) {
|
|
5315
|
+
this.parameters = parameters;
|
|
5113
5316
|
this.steps = 0;
|
|
5114
5317
|
this.refused = null;
|
|
5115
5318
|
this.track = track;
|
|
@@ -5132,6 +5335,17 @@ class Track {
|
|
|
5132
5335
|
this.live("setEnvironment");
|
|
5133
5336
|
setEnvironmentOn(this.track.ctx, environment, "the live track's");
|
|
5134
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
|
+
}
|
|
5135
5349
|
physicsTranslate(x, y) {
|
|
5136
5350
|
this.live("physicsTranslate");
|
|
5137
5351
|
translateOn(this.track.ctx, x, y, "the live track's");
|
|
@@ -6063,6 +6277,8 @@ class Tracks {
|
|
|
6063
6277
|
this.time = 0;
|
|
6064
6278
|
this.refused = null;
|
|
6065
6279
|
this.mixer = new Mixer(doc, plant);
|
|
6280
|
+
this.parameters = new LivePhysicsParameters(doc);
|
|
6281
|
+
this.path.ctx.parameters = this.parameters;
|
|
6066
6282
|
}
|
|
6067
6283
|
get trackTime() {
|
|
6068
6284
|
return this.time;
|
|
@@ -6074,6 +6290,17 @@ class Tracks {
|
|
|
6074
6290
|
this.live("setEnvironment");
|
|
6075
6291
|
setEnvironmentOn(this.path.ctx, environment, "the live tracks'");
|
|
6076
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
|
+
}
|
|
6077
6304
|
physicsTranslate(x, y) {
|
|
6078
6305
|
this.live("physicsTranslate");
|
|
6079
6306
|
translateOn(this.path.ctx, x, y, "the live tracks'");
|
|
@@ -6178,9 +6405,14 @@ class Tracks {
|
|
|
6178
6405
|
ctx.time += this.clock === void 0 ? dt : this.clock(dt);
|
|
6179
6406
|
const pose = new Pose(doc, this.plant);
|
|
6180
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
|
+
};
|
|
6181
6413
|
const mixed = new Map(doc.slots.map((s, k) => [s.name, { deform: pose.slots[k].deform, seen: pose.slots[k].seen }]));
|
|
6182
6414
|
return path.step(first, this.time, animationTime, {
|
|
6183
|
-
world: (sliders) => steppedLocalsWorld(doc, pose.bones, path.raw, sliders, { ctx, before,
|
|
6415
|
+
world: (sliders) => steppedLocalsWorld(doc, pose.bones, path.raw, sliders, { ctx, before, adjustment }),
|
|
6184
6416
|
slots: (sliders, placeholders) => slotRowsFrom(doc, path.raw, sliders, placeholders, (_slot, _live) => {
|
|
6185
6417
|
const s = pose.slots[doc.slots.indexOf(_slot)];
|
|
6186
6418
|
return { placeholder: s.placeholder, light: s.light, dark: s.dark };
|
|
@@ -8343,6 +8575,9 @@ function affine(u0, v0, u1, v1, u2, v2, x0, y0, x1, y1, x2, y2) {
|
|
|
8343
8575
|
const d = (eu1 * ey2 - eu2 * ey1) * inv;
|
|
8344
8576
|
return [a, b, c, d, x0 - a * u0 - c * v0, y0 - b * u0 - d * v0];
|
|
8345
8577
|
}
|
|
8578
|
+
function readSetupMesh(assets, target) {
|
|
8579
|
+
return readSetupMesh$1(underNoSkin(assets.document.doc), target);
|
|
8580
|
+
}
|
|
8346
8581
|
const SETS_SPECS = ["spine-parts-sets/1", "rig-parts-sets/1"];
|
|
8347
8582
|
function isRecord(value) {
|
|
8348
8583
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -8949,12 +9184,16 @@ class RigPlayer {
|
|
|
8949
9184
|
setsByName;
|
|
8950
9185
|
activeSet = null;
|
|
8951
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;
|
|
8952
9191
|
slotFilter = null;
|
|
8953
9192
|
/** `slotFilter` as a predicate; every slot when null. */
|
|
8954
9193
|
draws = () => true;
|
|
8955
9194
|
/** Handed to every track the player opens; reads the current `adjust` so setting it later needs no reopen. */
|
|
8956
|
-
adjustHook = (locals) => {
|
|
8957
|
-
if (this.adjustFn !== null) this.adjustFn(locals);
|
|
9195
|
+
adjustHook = (locals, ctx) => {
|
|
9196
|
+
if (this.adjustFn !== null) this.adjustFn(locals, ctx);
|
|
8958
9197
|
};
|
|
8959
9198
|
listeners = {
|
|
8960
9199
|
event: /* @__PURE__ */ new Set(),
|
|
@@ -9051,7 +9290,7 @@ class RigPlayer {
|
|
|
9051
9290
|
this.animation = name;
|
|
9052
9291
|
this.loop = loop;
|
|
9053
9292
|
this.track = openTrack(this.view, name, { loop, reset: "animation", adjust: this.adjustHook, clock: physicsClockStep });
|
|
9054
|
-
this.setRawPose(this.track
|
|
9293
|
+
this.setRawPose(this.initializeSingle(this.track));
|
|
9055
9294
|
this.applyEnvironment(this.track);
|
|
9056
9295
|
return;
|
|
9057
9296
|
}
|
|
@@ -9157,6 +9396,7 @@ class RigPlayer {
|
|
|
9157
9396
|
this.tracks = tracks;
|
|
9158
9397
|
this.track = null;
|
|
9159
9398
|
this.setRawPose(tracks.start());
|
|
9399
|
+
this.applyPhysicsParameters(tracks);
|
|
9160
9400
|
this.applyEnvironment(tracks);
|
|
9161
9401
|
if (time > 0) this.stepTo(time);
|
|
9162
9402
|
return tracks;
|
|
@@ -9164,10 +9404,54 @@ class RigPlayer {
|
|
|
9164
9404
|
/** Stores `raw` and applies `activeSet` when one is active. Zero work when `activeSet === null`. */
|
|
9165
9405
|
setRawPose(raw) {
|
|
9166
9406
|
this.rawPose = raw;
|
|
9407
|
+
this.heldParameters = null;
|
|
9167
9408
|
this.pose = this.activeSet === null ? raw : applySetToPose(this.view, raw, this.activeSet);
|
|
9168
9409
|
this.poseGeneration++;
|
|
9169
9410
|
this.cachedIdentity = null;
|
|
9170
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
|
+
}
|
|
9171
9455
|
/**
|
|
9172
9456
|
* The world the rig's physics answers (#121): `wind` and `gravity`, as
|
|
9173
9457
|
* spine-core's skeleton-level `windX/windY` and `gravityX/gravityY` — x to
|
|
@@ -9246,6 +9530,7 @@ class RigPlayer {
|
|
|
9246
9530
|
restoreStepperState(stepper, exactHit.state);
|
|
9247
9531
|
this.rawPose = exactHit.rawPose;
|
|
9248
9532
|
this.pose = exactHit.pose;
|
|
9533
|
+
this.heldParameters = exactHit.parameters;
|
|
9249
9534
|
this.poseGeneration++;
|
|
9250
9535
|
this.cachedIdentity = null;
|
|
9251
9536
|
return;
|
|
@@ -9257,24 +9542,32 @@ class RigPlayer {
|
|
|
9257
9542
|
if (cache.frontierSteps <= SEEK_FINE_STEP_LIMIT || cache.frontierSteps % SEEK_COARSE_STRIDE === 0) {
|
|
9258
9543
|
cache.grid.set(cache.frontierSteps, {
|
|
9259
9544
|
state: captureStepperState(cache.frontier),
|
|
9260
|
-
rawPose: raw
|
|
9545
|
+
rawPose: raw,
|
|
9546
|
+
parameters: this.readParameters(cache.frontier)
|
|
9261
9547
|
});
|
|
9262
9548
|
}
|
|
9263
9549
|
}
|
|
9550
|
+
let parameters;
|
|
9264
9551
|
const gridHit = cache.grid.get(steps);
|
|
9265
9552
|
if (gridHit !== void 0) {
|
|
9266
9553
|
restoreStepperState(stepper, gridHit.state);
|
|
9267
9554
|
this.rawPose = gridHit.rawPose;
|
|
9555
|
+
parameters = gridHit.parameters;
|
|
9268
9556
|
} else {
|
|
9269
9557
|
const baseStep = steps - steps % SEEK_COARSE_STRIDE;
|
|
9270
9558
|
const baseSnap = cache.grid.get(baseStep);
|
|
9271
9559
|
restoreStepperState(stepper, baseSnap.state);
|
|
9272
9560
|
this.rawPose = baseSnap.rawPose;
|
|
9273
9561
|
for (let i = baseStep; i < steps; i++) this.rawPose = stepper.step(this.seekStep);
|
|
9562
|
+
parameters = this.readParameters(stepper);
|
|
9274
9563
|
}
|
|
9275
9564
|
const rest = time - stepper.trackTime;
|
|
9276
|
-
if (rest > 0)
|
|
9565
|
+
if (rest > 0) {
|
|
9566
|
+
this.rawPose = stepper.step(rest);
|
|
9567
|
+
parameters = this.readParameters(stepper);
|
|
9568
|
+
}
|
|
9277
9569
|
if (this.rawPose !== null) this.setRawPose(this.rawPose);
|
|
9570
|
+
this.heldParameters = parameters;
|
|
9278
9571
|
if (this.rawPose !== null && this.pose !== null) {
|
|
9279
9572
|
if (cache.exact.size >= SEEK_EXACT_LIMIT) {
|
|
9280
9573
|
const oldest = cache.exact.keys().next().value;
|
|
@@ -9283,7 +9576,8 @@ class RigPlayer {
|
|
|
9283
9576
|
cache.exact.set(time, {
|
|
9284
9577
|
state: captureStepperState(stepper),
|
|
9285
9578
|
rawPose: this.rawPose,
|
|
9286
|
-
pose: this.pose
|
|
9579
|
+
pose: this.pose,
|
|
9580
|
+
parameters
|
|
9287
9581
|
});
|
|
9288
9582
|
}
|
|
9289
9583
|
}
|
|
@@ -9304,16 +9598,17 @@ class RigPlayer {
|
|
|
9304
9598
|
const tracks = openTracks(this.view, { reset: "animation", adjust: this.adjustHook, clock: physicsClockStep });
|
|
9305
9599
|
tracks.setAnimation(0, this.animation, { loop: this.loop, mixDuration: 0 });
|
|
9306
9600
|
firstPose = tracks.start();
|
|
9601
|
+
this.applyPhysicsParameters(tracks);
|
|
9307
9602
|
this.applyEnvironment(tracks);
|
|
9308
9603
|
frontier = tracks;
|
|
9309
9604
|
} else {
|
|
9310
9605
|
const track = openTrack(this.view, this.animation, { loop: this.loop, reset: "animation", adjust: this.adjustHook, clock: physicsClockStep });
|
|
9311
|
-
firstPose = track
|
|
9606
|
+
firstPose = this.initializeSingle(track);
|
|
9312
9607
|
this.applyEnvironment(track);
|
|
9313
9608
|
frontier = track;
|
|
9314
9609
|
}
|
|
9315
9610
|
const grid = /* @__PURE__ */ new Map();
|
|
9316
|
-
grid.set(0, { state: captureStepperState(frontier), rawPose: firstPose });
|
|
9611
|
+
grid.set(0, { state: captureStepperState(frontier), rawPose: firstPose, parameters: this.readParameters(frontier) });
|
|
9317
9612
|
this.seekCache = {
|
|
9318
9613
|
mode,
|
|
9319
9614
|
frontier,
|
|
@@ -9331,11 +9626,12 @@ class RigPlayer {
|
|
|
9331
9626
|
this.trackCount = 1;
|
|
9332
9627
|
this.tracks = tracks;
|
|
9333
9628
|
this.setRawPose(tracks.start());
|
|
9629
|
+
this.applyPhysicsParameters(tracks);
|
|
9334
9630
|
this.applyEnvironment(tracks);
|
|
9335
9631
|
return;
|
|
9336
9632
|
}
|
|
9337
9633
|
this.track = openTrack(this.view, this.animation, { loop: this.loop, reset: "animation", adjust: this.adjustHook, clock: physicsClockStep });
|
|
9338
|
-
this.setRawPose(this.track
|
|
9634
|
+
this.setRawPose(this.initializeSingle(this.track));
|
|
9339
9635
|
this.applyEnvironment(this.track);
|
|
9340
9636
|
}
|
|
9341
9637
|
/** What is open: the tracks, the single track, or nothing before `play`. */
|
|
@@ -9867,6 +10163,7 @@ export {
|
|
|
9867
10163
|
DomRenderer,
|
|
9868
10164
|
RigPlayer,
|
|
9869
10165
|
loadModel,
|
|
10166
|
+
readSetupMesh,
|
|
9870
10167
|
revokeRegions,
|
|
9871
10168
|
unpackRegions
|
|
9872
10169
|
};
|