@rig-c/play 1.17.1 → 1.18.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/NOTICE.md +15 -4
- package/README.md +5 -1
- package/dist/CanvasRenderer.d.ts +2 -1
- package/dist/CanvasRenderer.d.ts.map +1 -1
- package/dist/DomRenderer.d.ts +2 -1
- package/dist/DomRenderer.d.ts.map +1 -1
- package/dist/MeshGlBlitter.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2143 -154
- package/dist/index.js.map +1 -1
- package/dist/liveFrame.d.ts +47 -0
- package/dist/liveFrame.d.ts.map +1 -0
- package/dist/liveFrameSource.d.ts +2 -0
- package/dist/liveFrameSource.d.ts.map +1 -0
- package/dist/player.d.ts +24 -0
- package/dist/player.d.ts.map +1 -1
- package/package.json +7 -3
package/dist/index.js
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
const liveDrawings = /* @__PURE__ */ new WeakMap();
|
|
2
|
+
function renderFrameOf(frame2) {
|
|
3
|
+
if ("order" in frame2) return frame2;
|
|
4
|
+
void frame2.generation;
|
|
5
|
+
const drawing = liveDrawings.get(frame2);
|
|
6
|
+
if (!drawing) throw new Error("LIVE_FRAME_SOURCE: renderer needs a liveFrame() borrowed from a player");
|
|
7
|
+
return drawing;
|
|
8
|
+
}
|
|
1
9
|
const SCALE_PROBE_PX = 100;
|
|
2
10
|
const PIXEL_RATIO_DEADBAND = 1e-3;
|
|
3
11
|
function syncedPixelRatio(root, current) {
|
|
@@ -27,15 +35,15 @@ function syncedPixelRatio(root, current) {
|
|
|
27
35
|
return next;
|
|
28
36
|
}
|
|
29
37
|
const RUNTIME_PI = 3.1415927;
|
|
30
|
-
const RAD$
|
|
38
|
+
const RAD$4 = RUNTIME_PI / 180;
|
|
31
39
|
const RUNTIME_DEG = 180 / RUNTIME_PI;
|
|
32
40
|
const COLLAPSED_X_AXIS_SQ = 1e-5 * 1e-5;
|
|
33
41
|
const RUNTIME_ARITHMETIC = {
|
|
34
|
-
radiansPerDegree: RAD$
|
|
42
|
+
radiansPerDegree: RAD$4,
|
|
35
43
|
degreesOf: (radians) => radians * RUNTIME_DEG,
|
|
36
44
|
rotationOnlyFrame: false
|
|
37
45
|
};
|
|
38
|
-
function frame(rotation, shearX, shearY, scaleX, scaleY, rad = RAD$
|
|
46
|
+
function frame(rotation, shearX, shearY, scaleX, scaleY, rad = RAD$4) {
|
|
39
47
|
const xAngle = (rotation + shearX) * rad;
|
|
40
48
|
const yAngle = (rotation + 90 + shearY) * rad;
|
|
41
49
|
return [Math.cos(xAngle) * scaleX, Math.cos(yAngle) * scaleY, Math.sin(xAngle) * scaleX, Math.sin(yAngle) * scaleY];
|
|
@@ -52,7 +60,7 @@ function localFrame(arithmetic, rotation, shearX, shearY, scaleX, scaleY) {
|
|
|
52
60
|
function times(p, q) {
|
|
53
61
|
return [p[0] * q[0] + p[1] * q[2], p[0] * q[1] + p[1] * q[3], p[2] * q[0] + p[3] * q[2], p[2] * q[1] + p[3] * q[3]];
|
|
54
62
|
}
|
|
55
|
-
function noScaleDirection(p, rotation, rad = RAD$
|
|
63
|
+
function noScaleDirection(p, rotation, rad = RAD$4) {
|
|
56
64
|
const r = rotation * rad;
|
|
57
65
|
const cos = Math.cos(r);
|
|
58
66
|
const sin = Math.sin(r);
|
|
@@ -584,7 +592,10 @@ function payload(raw, at, problems) {
|
|
|
584
592
|
if (typeof raw.string !== "string") problems.push(`${at}: string is ${JSON.stringify(raw.string)}, not a string`);
|
|
585
593
|
else out.string = raw.string;
|
|
586
594
|
}
|
|
587
|
-
for (const key of ["volume", "balance"]) if (raw[key] !== void 0
|
|
595
|
+
for (const key of ["volume", "balance"]) if (raw[key] !== void 0) {
|
|
596
|
+
if (typeof raw[key] !== "number" || !Number.isFinite(raw[key])) problems.push(`${at}: ${key} is ${JSON.stringify(raw[key])}, not a finite number`);
|
|
597
|
+
else out[key] = raw[key];
|
|
598
|
+
}
|
|
588
599
|
return out;
|
|
589
600
|
}
|
|
590
601
|
function readEventDefs(value, problems) {
|
|
@@ -603,7 +614,7 @@ function readEventDefs(value, problems) {
|
|
|
603
614
|
if (raw.audio !== void 0 && typeof raw.audio !== "string") problems.push(`${at} "${raw.name}": audio is ${JSON.stringify(raw.audio)}, not a string`);
|
|
604
615
|
if (out.has(raw.name)) problems.push(`${at}: event "${raw.name}" is declared twice`);
|
|
605
616
|
const p = payload(raw, `${at} "${raw.name}"`, problems);
|
|
606
|
-
out.set(raw.name, { name: raw.name, int: p.int ?? 0, float: p.float ?? 0, string: p.string ?? "" });
|
|
617
|
+
out.set(raw.name, { audio: typeof raw.audio === "string" ? raw.audio : "", volume: p.volume ?? 0, balance: p.balance ?? 0, name: raw.name, int: p.int ?? 0, float: p.float ?? 0, string: p.string ?? "" });
|
|
607
618
|
});
|
|
608
619
|
return out;
|
|
609
620
|
}
|
|
@@ -632,7 +643,7 @@ function readEventKeys(value, label, defs, problems) {
|
|
|
632
643
|
return;
|
|
633
644
|
}
|
|
634
645
|
const p = payload(raw, at, problems);
|
|
635
|
-
out.push({ time: Math.fround(typeof t === "number" && Number.isFinite(t) ? t : 0), name: def.name, int: p.int ?? def.int, float: p.float ?? def.float, string: p.string ?? def.string });
|
|
646
|
+
out.push({ volume: def.audio ? p.volume ?? def.volume ?? 0 : 0, balance: def.audio ? p.balance ?? def.volume ?? 0 : 0, time: Math.fround(typeof t === "number" && Number.isFinite(t) ? t : 0), name: def.name, int: p.int ?? def.int, float: p.float ?? def.float, string: p.string ?? def.string });
|
|
636
647
|
});
|
|
637
648
|
return out;
|
|
638
649
|
}
|
|
@@ -1017,7 +1028,7 @@ function clipThrough(shape, vertices, triangles, uvs, clip = clipTriangles, read
|
|
|
1017
1028
|
}
|
|
1018
1029
|
return { clipped, vertices: outV, uvs: outUv, triangles: outT, sources };
|
|
1019
1030
|
}
|
|
1020
|
-
const RAD$
|
|
1031
|
+
const RAD$3 = RUNTIME_PI / 180;
|
|
1021
1032
|
const meshArraysByGeometry = /* @__PURE__ */ new WeakMap();
|
|
1022
1033
|
function meshArraysOf(mesh) {
|
|
1023
1034
|
let kept = meshArraysByGeometry.get(mesh);
|
|
@@ -1039,7 +1050,7 @@ function regionCorners(region, bone2) {
|
|
|
1039
1050
|
const y1 = -H / 2 * sy + r.offsetY * ky;
|
|
1040
1051
|
const x2 = x1 + r.width * kx;
|
|
1041
1052
|
const y2 = y1 + r.height * ky;
|
|
1042
|
-
const angle = (region.rotation ?? 0) * RAD$
|
|
1053
|
+
const angle = (region.rotation ?? 0) * RAD$3;
|
|
1043
1054
|
const cos = Math.cos(angle);
|
|
1044
1055
|
const sin = Math.sin(angle);
|
|
1045
1056
|
const x = region.x ?? 0;
|
|
@@ -1052,25 +1063,64 @@ function regionCorners(region, bone2) {
|
|
|
1052
1063
|
}
|
|
1053
1064
|
return out;
|
|
1054
1065
|
}
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1066
|
+
const compiledWeightedByVertices = /* @__PURE__ */ new WeakMap();
|
|
1067
|
+
function compileWeightedVertices(vertices, mapIndex) {
|
|
1068
|
+
const bones = [];
|
|
1069
|
+
const indexByName = /* @__PURE__ */ new Map();
|
|
1070
|
+
for (const influences of vertices.bindings) {
|
|
1071
|
+
for (const binding of influences) {
|
|
1072
|
+
if (!indexByName.has(binding.bone)) {
|
|
1073
|
+
indexByName.set(binding.bone, bones.length);
|
|
1074
|
+
bones.push(binding.bone);
|
|
1075
|
+
}
|
|
1062
1076
|
}
|
|
1063
|
-
return out;
|
|
1064
1077
|
}
|
|
1065
|
-
|
|
1078
|
+
bones.length;
|
|
1079
|
+
const bindings = vertices.bindings.map(
|
|
1080
|
+
(influences) => influences.map((binding) => {
|
|
1081
|
+
const idx = indexByName.get(binding.bone);
|
|
1082
|
+
return {
|
|
1083
|
+
bone: idx,
|
|
1084
|
+
x: binding.x,
|
|
1085
|
+
y: binding.y,
|
|
1086
|
+
fx: Math.fround(binding.x),
|
|
1087
|
+
fy: Math.fround(binding.y),
|
|
1088
|
+
weight: Math.fround(binding.weight)
|
|
1089
|
+
};
|
|
1090
|
+
})
|
|
1091
|
+
);
|
|
1092
|
+
return { bones, bindings };
|
|
1093
|
+
}
|
|
1094
|
+
function compiledWeightedOf(vertices) {
|
|
1095
|
+
let kept = compiledWeightedByVertices.get(vertices);
|
|
1096
|
+
if (kept === void 0) {
|
|
1097
|
+
kept = compileWeightedVertices(vertices);
|
|
1098
|
+
compiledWeightedByVertices.set(vertices, kept);
|
|
1099
|
+
}
|
|
1100
|
+
return kept;
|
|
1101
|
+
}
|
|
1102
|
+
function resolveBindingBones(bones, world) {
|
|
1103
|
+
const transforms = new Array(bones.length);
|
|
1104
|
+
for (let i = 0; i < bones.length; i++) {
|
|
1105
|
+
const name = bones[i];
|
|
1106
|
+
const t = world.get(name);
|
|
1107
|
+
if (t === void 0) throw new Error(`a binding names bone "${name}", which has no world transform (readModel refuses it first)`);
|
|
1108
|
+
transforms[i] = t;
|
|
1109
|
+
}
|
|
1110
|
+
return transforms;
|
|
1111
|
+
}
|
|
1112
|
+
function compiledWorldVertices(compiled, world, coords = Math.fround) {
|
|
1113
|
+
const transforms = resolveBindingBones(compiled.bones, world);
|
|
1114
|
+
const out = [];
|
|
1115
|
+
const fround = coords === Math.fround;
|
|
1116
|
+
for (const influences of compiled.bindings) {
|
|
1066
1117
|
let wx = 0;
|
|
1067
1118
|
let wy = 0;
|
|
1068
1119
|
for (const binding of influences) {
|
|
1069
|
-
const t =
|
|
1070
|
-
|
|
1071
|
-
const
|
|
1072
|
-
const
|
|
1073
|
-
const weight = Math.fround(binding.weight);
|
|
1120
|
+
const t = transforms[binding.bone];
|
|
1121
|
+
const x = fround ? binding.fx : coords(binding.x);
|
|
1122
|
+
const y = fround ? binding.fy : coords(binding.y);
|
|
1123
|
+
const weight = binding.weight;
|
|
1074
1124
|
wx += (x * t.a + y * t.b + t.worldX) * weight;
|
|
1075
1125
|
wy += (x * t.c + y * t.d + t.worldY) * weight;
|
|
1076
1126
|
}
|
|
@@ -1078,27 +1128,19 @@ function worldVertices(vertices, bone2, world, coords = Math.fround) {
|
|
|
1078
1128
|
}
|
|
1079
1129
|
return out;
|
|
1080
1130
|
}
|
|
1081
|
-
function
|
|
1131
|
+
function compiledDeformedWorldVertices(compiled, deform, world) {
|
|
1132
|
+
const transforms = resolveBindingBones(compiled.bones, world);
|
|
1082
1133
|
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
1134
|
let k = 0;
|
|
1092
|
-
for (const influences of
|
|
1135
|
+
for (const influences of compiled.bindings) {
|
|
1093
1136
|
let wx = 0;
|
|
1094
1137
|
let wy = 0;
|
|
1095
1138
|
for (const binding of influences) {
|
|
1096
|
-
const t =
|
|
1097
|
-
|
|
1098
|
-
const
|
|
1099
|
-
const y = Math.fround(binding.y) + deform[k + 1];
|
|
1139
|
+
const t = transforms[binding.bone];
|
|
1140
|
+
const x = binding.fx + deform[k];
|
|
1141
|
+
const y = binding.fy + deform[k + 1];
|
|
1100
1142
|
k += 2;
|
|
1101
|
-
const weight =
|
|
1143
|
+
const weight = binding.weight;
|
|
1102
1144
|
wx += (x * t.a + y * t.b + t.worldX) * weight;
|
|
1103
1145
|
wy += (x * t.c + y * t.d + t.worldY) * weight;
|
|
1104
1146
|
}
|
|
@@ -1106,6 +1148,30 @@ function deformedWorldVertices(vertices, deform, bone2, world) {
|
|
|
1106
1148
|
}
|
|
1107
1149
|
return out;
|
|
1108
1150
|
}
|
|
1151
|
+
function worldVertices(vertices, bone2, world, coords = Math.fround) {
|
|
1152
|
+
if (!vertices.weighted) {
|
|
1153
|
+
const out = [];
|
|
1154
|
+
for (let i = 0; i + 1 < vertices.xy.length; i += 2) {
|
|
1155
|
+
const x = coords(vertices.xy[i]);
|
|
1156
|
+
const y = coords(vertices.xy[i + 1]);
|
|
1157
|
+
out.push(x * bone2.a + y * bone2.b + bone2.worldX, x * bone2.c + y * bone2.d + bone2.worldY);
|
|
1158
|
+
}
|
|
1159
|
+
return out;
|
|
1160
|
+
}
|
|
1161
|
+
return compiledWorldVertices(compiledWeightedOf(vertices), world, coords);
|
|
1162
|
+
}
|
|
1163
|
+
function deformedWorldVertices(vertices, deform, bone2, world) {
|
|
1164
|
+
if (!vertices.weighted) {
|
|
1165
|
+
const out = [];
|
|
1166
|
+
for (let i = 0; i + 1 < deform.length; i += 2) {
|
|
1167
|
+
const x = deform[i];
|
|
1168
|
+
const y = deform[i + 1];
|
|
1169
|
+
out.push(x * bone2.a + y * bone2.b + bone2.worldX, x * bone2.c + y * bone2.d + bone2.worldY);
|
|
1170
|
+
}
|
|
1171
|
+
return out;
|
|
1172
|
+
}
|
|
1173
|
+
return compiledDeformedWorldVertices(compiledWeightedOf(vertices), deform, world);
|
|
1174
|
+
}
|
|
1109
1175
|
const isRecord$7 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
1110
1176
|
const finite$1 = (v) => typeof v === "number" && Number.isFinite(v);
|
|
1111
1177
|
const RECT_FIELDS = ["width", "height", "offsetX", "offsetY", "originalWidth", "originalHeight"];
|
|
@@ -1150,7 +1216,10 @@ function readVertices(value, where, bones, problems) {
|
|
|
1150
1216
|
for (const key of ["x", "y", "weight"]) if (!finite$1(b[key])) problems.push(`${at}: ${key} is ${JSON.stringify(b[key]) ?? "absent"}, not a finite number`);
|
|
1151
1217
|
});
|
|
1152
1218
|
});
|
|
1153
|
-
|
|
1219
|
+
if (problems.length !== before) return void 0;
|
|
1220
|
+
const weighted = { weighted: true, bindings };
|
|
1221
|
+
compiledWeightedByVertices.set(weighted, compileWeightedVertices(weighted));
|
|
1222
|
+
return weighted;
|
|
1154
1223
|
}
|
|
1155
1224
|
function readGeometry(raw, kind, where, bones, slots, problems) {
|
|
1156
1225
|
const before = problems.length;
|
|
@@ -1250,6 +1319,7 @@ function roundedRow(values2, round) {
|
|
|
1250
1319
|
function poseGeometry(shown, world, sourceOf, round, plant = {}, draw) {
|
|
1251
1320
|
const region = plant.region ?? regionCorners;
|
|
1252
1321
|
const vertices = plant.vertices ?? worldVertices;
|
|
1322
|
+
const deformPoser = plant.deformedVertices ?? deformedWorldVertices;
|
|
1253
1323
|
const attachments = [];
|
|
1254
1324
|
const clips = [];
|
|
1255
1325
|
const nulls = [];
|
|
@@ -1260,7 +1330,7 @@ function poseGeometry(shown, world, sourceOf, round, plant = {}, draw) {
|
|
|
1260
1330
|
if (bone2 === void 0) throw new Error(`slot "${s.slot}": bone "${s.bone}" has no world transform`);
|
|
1261
1331
|
const g = s.geometry;
|
|
1262
1332
|
const deform = s.deform;
|
|
1263
|
-
const drawn = (v) => deform === void 0 ? vertices(v, bone2, world) : plant.vertices === void 0 ?
|
|
1333
|
+
const drawn = (v) => deform === void 0 ? vertices(v, bone2, world) : plant.vertices === void 0 || plant.deformedVertices !== void 0 ? deformPoser(v, deform, bone2, world) : vertices(deformedVertices(v, deform), bone2, world, EXACT_COORDS);
|
|
1264
1334
|
if (g.kind === "region") {
|
|
1265
1335
|
const rect = s.frame === void 0 ? g.region.atlas : g.region.frames?.[s.frame] ?? g.region.atlas;
|
|
1266
1336
|
if (rect === null) {
|
|
@@ -1308,7 +1378,7 @@ function poseGeometry(shown, world, sourceOf, round, plant = {}, draw) {
|
|
|
1308
1378
|
}
|
|
1309
1379
|
return { attachments: attachmentsWhy === null ? attachments : null, attachmentsWhy, clips, clipped, clippedWhy, drawnClipped, drawnClippedWhy };
|
|
1310
1380
|
}
|
|
1311
|
-
const RAD$
|
|
1381
|
+
const RAD$2 = RUNTIME_PI / 180;
|
|
1312
1382
|
function curveLengthTable(chain, curveCount) {
|
|
1313
1383
|
const curveEnds = [];
|
|
1314
1384
|
let total = 0;
|
|
@@ -1747,7 +1817,7 @@ function solvePath(state, c, slotWorld) {
|
|
|
1747
1817
|
else {
|
|
1748
1818
|
fromTip = false;
|
|
1749
1819
|
const p = slotWorld;
|
|
1750
|
-
offsetRotation *= p.a * p.d - p.b * p.c > 0 ? RAD$
|
|
1820
|
+
offsetRotation *= p.a * p.d - p.b * p.c > 0 ? RAD$2 : -RAD$2;
|
|
1751
1821
|
}
|
|
1752
1822
|
const changed = [];
|
|
1753
1823
|
for (let i = 0, p = 3; i < boneCount; i++, p += 3) {
|
|
@@ -2593,8 +2663,8 @@ function applySliderPhysics(r, at, alpha, target) {
|
|
|
2593
2663
|
}
|
|
2594
2664
|
}
|
|
2595
2665
|
}
|
|
2596
|
-
const clamp01$
|
|
2597
|
-
const COLOUR_CHANNELS = { rgba: [0, 1, 2, 3], rgb: [0, 1, 2], alpha: [3], rgba2: [0, 1, 2, 3, 4, 5, 6], rgb2: [0, 1, 2, 4, 5, 6] };
|
|
2666
|
+
const clamp01$3 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
|
|
2667
|
+
const COLOUR_CHANNELS$1 = { rgba: [0, 1, 2, 3], rgb: [0, 1, 2], alpha: [3], rgba2: [0, 1, 2, 3, 4, 5, 6], rgb2: [0, 1, 2, 4, 5, 6] };
|
|
2598
2668
|
function applySliderSlots(slot, pose, applications) {
|
|
2599
2669
|
for (const app of applications) {
|
|
2600
2670
|
for (const target of app.timelines.slots) {
|
|
@@ -2607,9 +2677,9 @@ function applySliderSlots(slot, pose, applications) {
|
|
|
2607
2677
|
}
|
|
2608
2678
|
const v = valuesAt$1(tl.keys, app.at);
|
|
2609
2679
|
if (v === null) continue;
|
|
2610
|
-
COLOUR_CHANNELS[tl.kind].forEach((at, i) => {
|
|
2611
|
-
if (at < 4) pose.light[at] = app.alpha === 1 ? clamp01$
|
|
2612
|
-
else if (pose.dark !== null) pose.dark[at - 4] = app.alpha === 1 ? clamp01$
|
|
2680
|
+
COLOUR_CHANNELS$1[tl.kind].forEach((at, i) => {
|
|
2681
|
+
if (at < 4) pose.light[at] = app.alpha === 1 ? clamp01$3(v[i]) : clamp01$3(pose.light[at] + (v[i] - pose.light[at]) * app.alpha);
|
|
2682
|
+
else if (pose.dark !== null) pose.dark[at - 4] = app.alpha === 1 ? clamp01$3(v[i]) : clamp01$3(pose.dark[at - 4] + (v[i] - pose.dark[at - 4]) * app.alpha);
|
|
2613
2683
|
});
|
|
2614
2684
|
}
|
|
2615
2685
|
}
|
|
@@ -2632,8 +2702,8 @@ function sliderBonesWhy(doc) {
|
|
|
2632
2702
|
}
|
|
2633
2703
|
return found.length === 0 ? null : found.join("; ");
|
|
2634
2704
|
}
|
|
2635
|
-
const DEG = 180 / RUNTIME_PI;
|
|
2636
|
-
const RAD = RUNTIME_PI / 180;
|
|
2705
|
+
const DEG$1 = 180 / RUNTIME_PI;
|
|
2706
|
+
const RAD$1 = RUNTIME_PI / 180;
|
|
2637
2707
|
const ADMITTED_CONSTRAINT_KINDS = ["ik", "transform", "path", "physics", "slider"];
|
|
2638
2708
|
const TRANSFORM_PROPERTIES = ["rotate", "x", "y", "scaleX", "scaleY", "shearY"];
|
|
2639
2709
|
const TRANSFORM_MIXES = { rotate: "mixRotate", x: "mixX", y: "mixY", scaleX: "mixScaleX", scaleY: "mixScaleY", shearY: "mixShearY" };
|
|
@@ -2900,7 +2970,7 @@ function posedRecords(records, timelines, t, plant = {}) {
|
|
|
2900
2970
|
return { ...r, mixes };
|
|
2901
2971
|
});
|
|
2902
2972
|
}
|
|
2903
|
-
function wrap180(r) {
|
|
2973
|
+
function wrap180$1(r) {
|
|
2904
2974
|
return r > 180 ? r - 360 : r <= -180 ? r + 360 : r;
|
|
2905
2975
|
}
|
|
2906
2976
|
const IDENTITY = { a: 1, b: 0, c: 0, d: 1, worldX: 0, worldY: 0 };
|
|
@@ -2939,7 +3009,7 @@ const RUNTIME_SOLVER_RULES = {
|
|
|
2939
3009
|
function solverRules(plant) {
|
|
2940
3010
|
return plant === void 0 ? RUNTIME_SOLVER_RULES : { ...RUNTIME_SOLVER_RULES, ...plant };
|
|
2941
3011
|
}
|
|
2942
|
-
function volumeScaleY(scaleY, s) {
|
|
3012
|
+
function volumeScaleY$1(scaleY, s) {
|
|
2943
3013
|
return scaleY / (s < 0.7 ? 0.25 + s * 0.642857 : s);
|
|
2944
3014
|
}
|
|
2945
3015
|
function bone(state, name) {
|
|
@@ -2978,11 +3048,11 @@ function localFromWorld(b, p, w, rules = RUNTIME_SOLVER_RULES) {
|
|
|
2978
3048
|
const length = Math.sqrt(w.a * w.a + w.c * w.c);
|
|
2979
3049
|
const [ux2, uy2] = [w.a / length, w.c / length];
|
|
2980
3050
|
decompose(b, length, 0, ux2 * w.b + uy2 * w.d, (ux2 * w.d - uy2 * w.b) * flip, rules);
|
|
2981
|
-
b.rotation = Math.atan2((uy2 * p.a - ux2 * p.c) / det, (ux2 * p.d - uy2 * p.b) / det) * DEG;
|
|
3051
|
+
b.rotation = Math.atan2((uy2 * p.a - ux2 * p.c) / det, (ux2 * p.d - uy2 * p.b) / det) * DEG$1;
|
|
2982
3052
|
return;
|
|
2983
3053
|
}
|
|
2984
3054
|
const sign = det < 0 ? -1 : 1;
|
|
2985
|
-
const rotation = Math.atan2((p.a * w.c - p.c * w.a) * sign, (p.d * w.a - p.b * w.c) * sign) * DEG;
|
|
3055
|
+
const rotation = Math.atan2((p.a * w.c - p.c * w.a) * sign, (p.d * w.a - p.b * w.c) * sign) * DEG$1;
|
|
2986
3056
|
const [ux, uy] = noScaleDirection([p.a, p.b, p.c, p.d], rotation);
|
|
2987
3057
|
decompose(b, ux * w.a + uy * w.c, (ux * w.c - uy * w.a) * flip, ux * w.b + uy * w.d, (ux * w.d - uy * w.b) * flip, rules, "shearX");
|
|
2988
3058
|
b.rotation = rotation;
|
|
@@ -3006,7 +3076,7 @@ function localFromWorld(b, p, w, rules = RUNTIME_SOLVER_RULES) {
|
|
|
3006
3076
|
[la, lb, lc, ld] = [(cd * w.a - cb * w.c) / cdet, (cd * w.b - cb * w.d) / cdet, (ca * w.c - cc * w.a) / cdet, (ca * w.d - cc * w.b) / cdet];
|
|
3007
3077
|
}
|
|
3008
3078
|
decompose(b, la, lc, lb, ld, rules);
|
|
3009
|
-
b.rotation = (b.rotation ?? 0) + Math.atan2(p.c, p.a) * DEG;
|
|
3079
|
+
b.rotation = (b.rotation ?? 0) + Math.atan2(p.c, p.a) * DEG$1;
|
|
3010
3080
|
return;
|
|
3011
3081
|
} else {
|
|
3012
3082
|
const [ia, ib, ic, id] = [p.d * pid, p.b * pid, p.c * pid, p.a * pid];
|
|
@@ -3021,12 +3091,12 @@ function decompose(b, la, lc, lb, ld, rules, xAngleIn = "rotation") {
|
|
|
3021
3091
|
b.scaleX = 0;
|
|
3022
3092
|
b.scaleY = yLength;
|
|
3023
3093
|
b.shearY = 0;
|
|
3024
|
-
b.rotation = !rules.readBackCollapsed || yLength > 1e-5 ? Math.atan2(ld, lb) * DEG - 90 : 0;
|
|
3094
|
+
b.rotation = !rules.readBackCollapsed || yLength > 1e-5 ? Math.atan2(ld, lb) * DEG$1 - 90 : 0;
|
|
3025
3095
|
return;
|
|
3026
3096
|
}
|
|
3027
|
-
const xAngle = Math.atan2(lc, la) * DEG;
|
|
3097
|
+
const xAngle = Math.atan2(lc, la) * DEG$1;
|
|
3028
3098
|
const det = la * ld - lb * lc;
|
|
3029
|
-
let yAngle = Math.atan2(ld, lb) * DEG;
|
|
3099
|
+
let yAngle = Math.atan2(ld, lb) * DEG$1;
|
|
3030
3100
|
if (xAngleIn === "rotation") b.rotation = xAngle;
|
|
3031
3101
|
else b.shearX = xAngle;
|
|
3032
3102
|
b.scaleX = sx;
|
|
@@ -3055,7 +3125,7 @@ function solveOne(state, c, frame2 = null) {
|
|
|
3055
3125
|
const k = Math.abs(pa * pd - pb * pc) / Math.max(state.rules.ikXAxisFloor, pa * pa + pc * pc);
|
|
3056
3126
|
pb = -pc * k;
|
|
3057
3127
|
pd = pa * k;
|
|
3058
|
-
turn = Math.atan2(pc, pa) * DEG;
|
|
3128
|
+
turn = Math.atan2(pc, pa) * DEG$1;
|
|
3059
3129
|
const det = pa * pd - pb * pc;
|
|
3060
3130
|
const x = t.worldX - p.worldX;
|
|
3061
3131
|
const y = t.worldY - p.worldY;
|
|
@@ -3073,9 +3143,9 @@ function solveOne(state, c, frame2 = null) {
|
|
|
3073
3143
|
}
|
|
3074
3144
|
const rotation = b.rotation ?? 0;
|
|
3075
3145
|
const scaleX = b.scaleX ?? 1;
|
|
3076
|
-
let r = -(b.shearX ?? 0) - rotation + turn + Math.atan2(ly, lx) * DEG;
|
|
3146
|
+
let r = -(b.shearX ?? 0) - rotation + turn + Math.atan2(ly, lx) * DEG$1;
|
|
3077
3147
|
if (scaleX < 0) r += 180;
|
|
3078
|
-
b.rotation = rotation + wrap180(r) * c.mix;
|
|
3148
|
+
b.rotation = rotation + wrap180$1(r) * c.mix;
|
|
3079
3149
|
const len = (b.length ?? 0) * scaleX;
|
|
3080
3150
|
if ((c.compress || c.stretch) && len > state.rules.ikStretchMinLength) {
|
|
3081
3151
|
const wx = t.worldX - bw.worldX;
|
|
@@ -3085,7 +3155,7 @@ function solveOne(state, c, frame2 = null) {
|
|
|
3085
3155
|
const s = (d / len - 1) * c.mix + 1;
|
|
3086
3156
|
b.scaleX = scaleX * s;
|
|
3087
3157
|
if (c.scaleY === "uniform") b.scaleY = (b.scaleY ?? 1) * s;
|
|
3088
|
-
else if (c.scaleY === "volume") b.scaleY = volumeScaleY(b.scaleY ?? 1, s);
|
|
3158
|
+
else if (c.scaleY === "volume") b.scaleY = volumeScaleY$1(b.scaleY ?? 1, s);
|
|
3089
3159
|
}
|
|
3090
3160
|
}
|
|
3091
3161
|
return [b.name];
|
|
@@ -3158,7 +3228,7 @@ function solveTwo(state, c, frame2 = null) {
|
|
|
3158
3228
|
const f = (Math.sqrt(d2) / (l1 + l2) - 1) * c.mix + 1;
|
|
3159
3229
|
sx *= f;
|
|
3160
3230
|
if (c.scaleY === "uniform") sy *= f;
|
|
3161
|
-
else if (c.scaleY === "volume") sy = volumeScaleY(sy, f);
|
|
3231
|
+
else if (c.scaleY === "volume") sy = volumeScaleY$1(sy, f);
|
|
3162
3232
|
}
|
|
3163
3233
|
} else a2 = Math.acos(cos) * bend;
|
|
3164
3234
|
const along = l1 + l2 * cos;
|
|
@@ -3211,14 +3281,14 @@ function solveTwo(state, c, frame2 = null) {
|
|
|
3211
3281
|
const signs = state.rules.ikZeroScaleSignPositive ? (psx < 0 ? -1 : 1) * (psy < 0 ? -1 : 1) : Math.sign(psx) * Math.sign(psy);
|
|
3212
3282
|
const offset = Math.atan2(cy, cx);
|
|
3213
3283
|
const parentRotation = parent.rotation ?? 0;
|
|
3214
|
-
const parentChange = (a1 - signs * offset) * DEG + (psx < 0 ? 180 : 0) - parentRotation;
|
|
3215
|
-
parent.rotation = parentRotation + wrap180(parentChange) * c.mix;
|
|
3284
|
+
const parentChange = (a1 - signs * offset) * DEG$1 + (psx < 0 ? 180 : 0) - parentRotation;
|
|
3285
|
+
parent.rotation = parentRotation + wrap180$1(parentChange) * c.mix;
|
|
3216
3286
|
parent.scaleX = sx;
|
|
3217
3287
|
parent.scaleY = sy;
|
|
3218
|
-
const childTarget = ((a2 + signs * offset) * DEG - (child.shearX ?? 0)) * signs + (csx < 0 ? 180 : 0);
|
|
3288
|
+
const childTarget = ((a2 + signs * offset) * DEG$1 - (child.shearX ?? 0)) * signs + (csx < 0 ? 180 : 0);
|
|
3219
3289
|
const childRotation = child.rotation ?? 0;
|
|
3220
3290
|
child.y = cy;
|
|
3221
|
-
child.rotation = childRotation + wrap180(childTarget - childRotation) * c.mix;
|
|
3291
|
+
child.rotation = childRotation + wrap180$1(childTarget - childRotation) * c.mix;
|
|
3222
3292
|
return [parent.name];
|
|
3223
3293
|
}
|
|
3224
3294
|
function sourceValue(state, c, property) {
|
|
@@ -3244,7 +3314,7 @@ function sourceValue(state, c, property) {
|
|
|
3244
3314
|
switch (property) {
|
|
3245
3315
|
case "rotate": {
|
|
3246
3316
|
const det = w.a * w.d - w.b * w.c;
|
|
3247
|
-
let r = Math.atan2(w.c, w.a) * DEG + ((state.rules.offsetNegatedAtZeroDet ? det > 0 : det >= 0) ? o.rotate : -o.rotate);
|
|
3317
|
+
let r = Math.atan2(w.c, w.a) * DEG$1 + ((state.rules.offsetNegatedAtZeroDet ? det > 0 : det >= 0) ? o.rotate : -o.rotate);
|
|
3248
3318
|
if (r < 0) r += 360;
|
|
3249
3319
|
return r;
|
|
3250
3320
|
}
|
|
@@ -3257,13 +3327,13 @@ function sourceValue(state, c, property) {
|
|
|
3257
3327
|
case "scaleY":
|
|
3258
3328
|
return Math.sqrt(w.b * w.b + w.d * w.d) + o.scaleY;
|
|
3259
3329
|
case "shearY":
|
|
3260
|
-
return (Math.atan2(w.d, w.b) - Math.atan2(w.c, w.a)) * DEG - 90 + o.shearY;
|
|
3330
|
+
return (Math.atan2(w.d, w.b) - Math.atan2(w.c, w.a)) * DEG$1 - 90 + o.shearY;
|
|
3261
3331
|
}
|
|
3262
3332
|
}
|
|
3263
|
-
function applyWorld(w, property, v, mix, additive, rules) {
|
|
3333
|
+
function applyWorld$1(w, property, v, mix, additive, rules) {
|
|
3264
3334
|
switch (property) {
|
|
3265
3335
|
case "rotate": {
|
|
3266
|
-
let r = additive ? v * RAD : v * RAD - Math.atan2(w.c, w.a);
|
|
3336
|
+
let r = additive ? v * RAD$1 : v * RAD$1 - Math.atan2(w.c, w.a);
|
|
3267
3337
|
if (r > RUNTIME_PI) r -= 2 * RUNTIME_PI;
|
|
3268
3338
|
else if (r < -RUNTIME_PI) r += 2 * RUNTIME_PI;
|
|
3269
3339
|
r *= mix;
|
|
@@ -3295,7 +3365,7 @@ function applyWorld(w, property, v, mix, additive, rules) {
|
|
|
3295
3365
|
case "shearY": {
|
|
3296
3366
|
const yAngle = Math.atan2(w.d, w.b);
|
|
3297
3367
|
const xAngle = Math.atan2(w.c, w.a);
|
|
3298
|
-
let r = additive && !rules.additiveShearRightAngle ? v * RAD : (v + 90) * RAD - (additive ? 90 * RAD : yAngle - xAngle);
|
|
3368
|
+
let r = additive && !rules.additiveShearRightAngle ? v * RAD$1 : (v + 90) * RAD$1 - (additive ? 90 * RAD$1 : yAngle - xAngle);
|
|
3299
3369
|
if (!additive) r = r > RUNTIME_PI ? r - 2 * RUNTIME_PI : r < -RUNTIME_PI ? r + 2 * RUNTIME_PI : r;
|
|
3300
3370
|
const angle = yAngle + r * mix;
|
|
3301
3371
|
const s = Math.sqrt(w.b * w.b + w.d * w.d);
|
|
@@ -3333,7 +3403,7 @@ function solveTransform(state, c) {
|
|
|
3333
3403
|
}
|
|
3334
3404
|
applied = true;
|
|
3335
3405
|
if (c.localTarget) applyLocal(b, to.property, v, mix, c.additive);
|
|
3336
|
-
else applyWorld(w, to.property, v, mix, c.additive, state.rules);
|
|
3406
|
+
else applyWorld$1(w, to.property, v, mix, c.additive, state.rules);
|
|
3337
3407
|
}
|
|
3338
3408
|
}
|
|
3339
3409
|
if (!applied) continue;
|
|
@@ -3917,7 +3987,7 @@ function valuesAt(keys, t, plant) {
|
|
|
3917
3987
|
const channel2 = plant.channel ?? channelAt;
|
|
3918
3988
|
return keys[index].values.map((_v, c) => channel2(keys, index, c, t));
|
|
3919
3989
|
}
|
|
3920
|
-
function keyAt(keys, t, plant) {
|
|
3990
|
+
function keyAt$1(keys, t, plant) {
|
|
3921
3991
|
const index = (plant.search ?? keyIndexAt)(keys, t);
|
|
3922
3992
|
return index < 0 ? null : keys[index];
|
|
3923
3993
|
}
|
|
@@ -3938,7 +4008,7 @@ function posedBones(doc, timelines, t, plant = {}) {
|
|
|
3938
4008
|
const bone2 = { ...setup };
|
|
3939
4009
|
for (const tl of target.timelines) {
|
|
3940
4010
|
if (tl.kind === "inherit") {
|
|
3941
|
-
const key = keyAt(tl.keys, t, plant);
|
|
4011
|
+
const key = keyAt$1(tl.keys, t, plant);
|
|
3942
4012
|
if (key === null) {
|
|
3943
4013
|
if (setup.inheritMode === void 0) delete bone2.inheritMode;
|
|
3944
4014
|
else bone2.inheritMode = setup.inheritMode;
|
|
@@ -4066,28 +4136,29 @@ function adjustLocals(doc, bones, adjust, context) {
|
|
|
4066
4136
|
}
|
|
4067
4137
|
}, context);
|
|
4068
4138
|
}
|
|
4069
|
-
const clamp01$
|
|
4139
|
+
const clamp01$2 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
|
|
4070
4140
|
function posedSlots(doc, timelines, t, plant = {}, sliders = [], placeholders) {
|
|
4071
|
-
const
|
|
4141
|
+
const stated = viewColours(doc);
|
|
4142
|
+
const colour = plant.colour ?? stated;
|
|
4072
4143
|
const byName = new Map(timelines.slots.map((s) => [s.name, s]));
|
|
4073
4144
|
return slotRowsFrom(doc, plant, sliders, placeholders, (slot, live) => {
|
|
4074
4145
|
let placeholder = slot.setup;
|
|
4075
|
-
const
|
|
4076
|
-
const
|
|
4146
|
+
const setupLight = slot.color === void 0 ? WHITE : colour(slot.color);
|
|
4147
|
+
const light = [setupLight[0], setupLight[1], setupLight[2], setupLight[3]];
|
|
4148
|
+
const setupDark = slot.dark === void 0 ? null : stated(slot.dark);
|
|
4077
4149
|
const dark = setupDark === null ? null : [setupDark[0], setupDark[1], setupDark[2]];
|
|
4078
4150
|
for (const tl of live ? byName.get(slot.name)?.timelines ?? [] : []) {
|
|
4079
4151
|
if (tl.kind === "attachment") {
|
|
4080
|
-
const key = keyAt(tl.keys, t, plant);
|
|
4152
|
+
const key = keyAt$1(tl.keys, t, plant);
|
|
4081
4153
|
placeholder = key === null ? slot.setup : key.name ?? null;
|
|
4082
4154
|
continue;
|
|
4083
4155
|
}
|
|
4084
4156
|
const v = valuesAt(tl.keys, t, plant);
|
|
4085
|
-
const setupLight = slot.color === void 0 ? [1, 1, 1, 1] : colour(slot.color);
|
|
4086
4157
|
const channels = SLOT_COLOUR_TIMELINES[tl.kind].channels;
|
|
4087
4158
|
channels.forEach((name, i) => {
|
|
4088
4159
|
const at = { r: 0, g: 1, b: 2, a: 3, r2: 4, g2: 5, b2: 6 }[name];
|
|
4089
|
-
if (at < 4) light[at] = v === null ? setupLight[at] : clamp01$
|
|
4090
|
-
else if (dark !== null && setupDark !== null) dark[at - 4] = v === null ? setupDark[at - 4] : clamp01$
|
|
4160
|
+
if (at < 4) light[at] = v === null ? setupLight[at] : clamp01$2(v[i]);
|
|
4161
|
+
else if (dark !== null && setupDark !== null) dark[at - 4] = v === null ? setupDark[at - 4] : clamp01$2(v[i]);
|
|
4091
4162
|
});
|
|
4092
4163
|
}
|
|
4093
4164
|
return { placeholder, light, dark };
|
|
@@ -4131,50 +4202,60 @@ function slotRowsFrom(doc, plant, sliders, placeholders, baseOf) {
|
|
|
4131
4202
|
}
|
|
4132
4203
|
return { rows, conflicts };
|
|
4133
4204
|
}
|
|
4134
|
-
function posedBoneWorld(doc, timelines, t, plant = {}, constraints, sliders, step) {
|
|
4135
|
-
const posed = posedBoneStep(doc, timelines, t, plant, constraints, sliders, step);
|
|
4136
|
-
return { rows: stepRows(posed), world: posed.world };
|
|
4137
|
-
}
|
|
4138
4205
|
function posedBoneWorldAlone(doc, timelines, t, plant, constraints, sliders, step) {
|
|
4139
4206
|
return posedBoneStep(doc, timelines, t, plant, constraints, sliders, step).world;
|
|
4140
4207
|
}
|
|
4141
|
-
function stepRows(posed) {
|
|
4142
|
-
if (posed.rows !== void 0) return posed.rows;
|
|
4143
|
-
const { world, active, round } = posed;
|
|
4144
|
-
posed.rows = posed.bones.map((b) => {
|
|
4145
|
-
const w = world.get(b.name);
|
|
4146
|
-
return [b.name, round(w.worldX), round(w.worldY), round(w.a), round(w.b), round(w.c), round(w.d), active.has(b.name) ? 1 : 0, b.parent ?? null];
|
|
4147
|
-
});
|
|
4148
|
-
return posed.rows;
|
|
4149
|
-
}
|
|
4150
4208
|
function posedBoneStep(doc, timelines, t, plant, constraints, sliders, step) {
|
|
4209
|
+
const stage = plant.stage;
|
|
4210
|
+
if (stage !== void 0) stage.begin("timeline");
|
|
4151
4211
|
const bones = posedBones(doc, timelines, t, plant);
|
|
4152
|
-
if (
|
|
4212
|
+
if (stage !== void 0) stage.end("timeline");
|
|
4213
|
+
if (constraints !== void 0 && step !== void 0) {
|
|
4214
|
+
return steppedLocals(doc, bones, plant, () => pathDeformed(doc, posedRecords(constraintRecords(doc), constraints, t), timelines, t, plant), constraints.physicsKeyed ?? [], t, sliders, step);
|
|
4215
|
+
}
|
|
4153
4216
|
const active = activeBones(doc);
|
|
4154
|
-
if (step?.adjustment !== void 0)
|
|
4155
|
-
|
|
4217
|
+
if (step?.adjustment !== void 0) {
|
|
4218
|
+
if (stage !== void 0) stage.begin("timeline");
|
|
4219
|
+
adjustLocals(doc, bones, step.adjustment.callback, step.adjustment.context);
|
|
4220
|
+
if (stage !== void 0) stage.end("timeline");
|
|
4221
|
+
}
|
|
4222
|
+
if (stage !== void 0) stage.begin("world");
|
|
4223
|
+
const initialWorld = (plant.evaluate ?? worldTransforms)(bones, active);
|
|
4224
|
+
if (stage !== void 0) stage.end("world");
|
|
4225
|
+
let world = initialWorld;
|
|
4156
4226
|
if (constraints !== void 0) {
|
|
4227
|
+
if (stage !== void 0) stage.begin("constraints");
|
|
4157
4228
|
const setupRecords = constraintRecords(doc);
|
|
4158
4229
|
const records = pathDeformed(doc, posedRecords(setupRecords, constraints, t), timelines, t, plant);
|
|
4159
4230
|
const previous = setupRecords.some((r) => r.kind === "path") ? applyConstraints(doc.bones, (plant.evaluate ?? worldTransforms)(doc.bones, active), active, plant.constraints ? plant.constraints(setupRecords) : setupRecords, null, void 0, void 0, void 0, solverRules(plant.solver)) : null;
|
|
4160
|
-
world = applyConstraints(bones,
|
|
4231
|
+
world = applyConstraints(bones, initialWorld, active, plant.constraints ? plant.constraints(records) : records, previous, sliders, void 0, void 0, solverRules(plant.solver));
|
|
4232
|
+
if (stage !== void 0) stage.end("constraints");
|
|
4161
4233
|
}
|
|
4162
4234
|
for (const b of bones) if (world.get(b.name) === void 0) throw new CoreInputError(`the evaluator returned no transform for bone "${b.name}"`);
|
|
4163
4235
|
return { bones, world, active, round: plant.round ?? gridRound };
|
|
4164
4236
|
}
|
|
4165
4237
|
function steppedLocals(doc, bones, plant, records, physicsKeyed, t, sliders, step) {
|
|
4238
|
+
const stage = plant.stage;
|
|
4166
4239
|
const active = activeBones(doc);
|
|
4167
|
-
if (step.adjustment !== void 0)
|
|
4168
|
-
|
|
4240
|
+
if (step.adjustment !== void 0) {
|
|
4241
|
+
if (stage !== void 0) stage.begin("timeline");
|
|
4242
|
+
adjustLocals(doc, bones, step.adjustment.callback, step.adjustment.context);
|
|
4243
|
+
if (stage !== void 0) stage.end("timeline");
|
|
4244
|
+
}
|
|
4245
|
+
if (stage !== void 0) stage.begin("world");
|
|
4246
|
+
const initialWorld = (plant.evaluate ?? worldTransforms)(bones, active);
|
|
4247
|
+
if (stage !== void 0) {
|
|
4248
|
+
stage.end("world");
|
|
4249
|
+
stage.begin("constraints");
|
|
4250
|
+
}
|
|
4169
4251
|
const stepped = stepPhysicsRecords(records(), physicsKeyed, t, active, step.ctx, step.before);
|
|
4170
|
-
world = applyConstraints(bones,
|
|
4252
|
+
const world = applyConstraints(bones, initialWorld, active, plant.constraints ? plant.constraints(stepped) : stepped, null, sliders, step.ctx, void 0, solverRules(plant.solver));
|
|
4253
|
+
if (stage !== void 0) stage.end("constraints");
|
|
4171
4254
|
for (const b of bones) if (world.get(b.name) === void 0) throw new CoreInputError(`the evaluator returned no transform for bone "${b.name}"`);
|
|
4172
4255
|
return { bones, world, active, round: plant.round ?? gridRound };
|
|
4173
4256
|
}
|
|
4174
|
-
function steppedLocalsWorld(doc, bones, plant, sliders, step
|
|
4175
|
-
|
|
4176
|
-
stepRows(posed);
|
|
4177
|
-
return posed.world;
|
|
4257
|
+
function steppedLocalsWorld(doc, bones, plant, sliders, step) {
|
|
4258
|
+
return steppedLocals(doc, bones, plant, () => constraintRecords(doc), [], 0, sliders, step).world;
|
|
4178
4259
|
}
|
|
4179
4260
|
function pathDeformed(doc, records, timelines, t, plant) {
|
|
4180
4261
|
if (timelines.attachments.every((a) => a.deform === null)) return records;
|
|
@@ -4806,6 +4887,24 @@ function readColour(hex) {
|
|
|
4806
4887
|
const channel2 = (i) => Number.parseInt(hex.slice(2 * i, 2 * i + 2), 16) / 255;
|
|
4807
4888
|
return [channel2(0), channel2(1), channel2(2), hex.length === 8 ? channel2(3) : 1];
|
|
4808
4889
|
}
|
|
4890
|
+
const coloursOfView = /* @__PURE__ */ new WeakMap();
|
|
4891
|
+
function viewColours(doc) {
|
|
4892
|
+
let kept = coloursOfView.get(doc);
|
|
4893
|
+
if (kept === void 0) {
|
|
4894
|
+
kept = /* @__PURE__ */ new Map();
|
|
4895
|
+
coloursOfView.set(doc, kept);
|
|
4896
|
+
}
|
|
4897
|
+
const colours = kept;
|
|
4898
|
+
return (hex) => {
|
|
4899
|
+
let c = colours.get(hex);
|
|
4900
|
+
if (c === void 0) {
|
|
4901
|
+
c = readColour(hex);
|
|
4902
|
+
colours.set(hex, c);
|
|
4903
|
+
}
|
|
4904
|
+
return c;
|
|
4905
|
+
};
|
|
4906
|
+
}
|
|
4907
|
+
const WHITE = [1, 1, 1, 1];
|
|
4809
4908
|
function readBlend(slot) {
|
|
4810
4909
|
if (slot.blend === void 0) return "Normal";
|
|
4811
4910
|
const mode = foldBlend(slot.blend);
|
|
@@ -5017,7 +5116,7 @@ function rawBones(doc, world) {
|
|
|
5017
5116
|
}
|
|
5018
5117
|
function drawnGeometry(doc, shown, world, order, plant, mode) {
|
|
5019
5118
|
const draw = drawWalkOf(doc, order, plant);
|
|
5020
|
-
const geometry = poseGeometry(shown, world, sourceOfDoc(doc), mode.keep ? keptNumber : rawNumber, { region: plant.region, vertices: plant.vertices }, mode.loop && mode.live !== true && plant.through === void 0 ? { ...draw, through: NO_CLIPPED_ROWS } : draw);
|
|
5119
|
+
const geometry = poseGeometry(shown, world, sourceOfDoc(doc), mode.keep ? keptNumber : rawNumber, { region: plant.region, vertices: plant.vertices, deformedVertices: plant.deformedVertices }, mode.loop && mode.live !== true && plant.through === void 0 ? { ...draw, through: NO_CLIPPED_ROWS } : draw);
|
|
5021
5120
|
if (geometry.attachments === null) throw new CoreInputError(`the raw pose leaves the attachments out: ${geometry.attachmentsWhy}`);
|
|
5022
5121
|
if (geometry.drawnClipped === null) throw new CoreInputError(`the raw pose leaves the clipped triangles out: ${geometry.drawnClippedWhy}`);
|
|
5023
5122
|
return { rows: geometry.attachments, clips: geometry.clips, drawnClipped: geometry.drawnClipped };
|
|
@@ -5030,9 +5129,16 @@ function allNumbers(values2) {
|
|
|
5030
5129
|
return true;
|
|
5031
5130
|
}
|
|
5032
5131
|
function rawDrawn(doc, shown, world, order, plant, mode = RAW_MODE) {
|
|
5132
|
+
const stage = plant.stage;
|
|
5133
|
+
if (stage !== void 0) stage.begin("geometry");
|
|
5033
5134
|
const geometry = drawnGeometry(doc, shown, world, order, plant, mode);
|
|
5135
|
+
if (stage !== void 0) {
|
|
5136
|
+
stage.end("geometry");
|
|
5137
|
+
stage.begin("readout");
|
|
5138
|
+
}
|
|
5034
5139
|
const rows = geometry.rows;
|
|
5035
5140
|
const drawn = [];
|
|
5141
|
+
const stated = viewColours(doc);
|
|
5036
5142
|
let k = 0;
|
|
5037
5143
|
for (const s of shown) {
|
|
5038
5144
|
const g = s.geometry;
|
|
@@ -5040,7 +5146,8 @@ function rawDrawn(doc, shown, world, order, plant, mode = RAW_MODE) {
|
|
|
5040
5146
|
const row = rows[k++];
|
|
5041
5147
|
if (row === void 0 || row[0] !== s.slot) throw new CoreInputError(`slot "${s.slot}": the attachment rows are not in the shown records' order`);
|
|
5042
5148
|
const record = doc.skins.find((x) => x.name === s.skin)?.attachments[s.slot]?.[s.placeholder];
|
|
5043
|
-
const
|
|
5149
|
+
const read = record?.color === void 0 ? WHITE : stated(record.color);
|
|
5150
|
+
const colour = [read[0], read[1], read[2], read[3]];
|
|
5044
5151
|
let uvs;
|
|
5045
5152
|
let triangles;
|
|
5046
5153
|
let hull = null;
|
|
@@ -5058,6 +5165,7 @@ function rawDrawn(doc, shown, world, order, plant, mode = RAW_MODE) {
|
|
|
5058
5165
|
}
|
|
5059
5166
|
const clips = geometry.clips.map((c) => [c[0], c[1], c[2], numbersOf(c[3], `clip vertex of slot "${c[0]}"`, mode)]);
|
|
5060
5167
|
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]]);
|
|
5168
|
+
if (stage !== void 0) stage.end("readout");
|
|
5061
5169
|
return { drawn, clips, clipped };
|
|
5062
5170
|
}
|
|
5063
5171
|
function poseRawSetup(doc, plant = {}) {
|
|
@@ -5155,12 +5263,15 @@ function loopedTime(trackTime, duration) {
|
|
|
5155
5263
|
return duration === 0 ? 0 : trackTime % duration;
|
|
5156
5264
|
}
|
|
5157
5265
|
const RAW_SHAPE = {
|
|
5158
|
-
rows: true,
|
|
5159
5266
|
setup: (doc, world, slots, drawOrder, shown, plant, mode) => ({ trackTime: 0, animationTime: 0, bones: rawBones(doc, world), slots, drawOrder, ...rawDrawn(doc, shown, world, drawOrder, plant, mode), events: [], shown: drawOrderOf(shown, drawOrder) }),
|
|
5160
5267
|
step: (doc, p, plant, mode) => {
|
|
5161
5268
|
const drawn = rawDrawn(doc, p.shown, p.world, p.drawOrder, plant, mode);
|
|
5269
|
+
const stage = plant.stage;
|
|
5270
|
+
if (stage !== void 0) stage.begin("readout");
|
|
5162
5271
|
const events = p.events();
|
|
5163
|
-
|
|
5272
|
+
const pose = { trackTime: p.trackTime, animationTime: p.animationTime, bones: rawBones(doc, p.world), slots: p.slots, drawOrder: p.drawOrder, ...drawn, events, shown: p.shown };
|
|
5273
|
+
if (stage !== void 0) stage.end("readout");
|
|
5274
|
+
return pose;
|
|
5164
5275
|
}
|
|
5165
5276
|
};
|
|
5166
5277
|
function animationOf(doc, animation) {
|
|
@@ -5197,7 +5308,7 @@ function stepWalk(track, dt, first = false) {
|
|
|
5197
5308
|
const keys = anim.timelines.events;
|
|
5198
5309
|
const adjustment = adjust === void 0 ? void 0 : { callback: adjust, context: { trackTime, animation: anim.name, globalTime: trackTime } };
|
|
5199
5310
|
const pose = poseStepFrom(doc, plant, resolve, shape, mode, ctx, first, trackTime, t, {
|
|
5200
|
-
world: (sliders) =>
|
|
5311
|
+
world: (sliders) => posedBoneWorldAlone(doc, anim.timelines, t, raw, anim.constraints, sliders, { ctx, before, adjustment }),
|
|
5201
5312
|
slots: (sliders, placeholders) => posedSlots(doc, anim.timelines, t, raw, sliders, placeholders),
|
|
5202
5313
|
drawOrder: anim.timelines.drawOrder,
|
|
5203
5314
|
at: t,
|
|
@@ -5208,20 +5319,30 @@ function stepWalk(track, dt, first = false) {
|
|
|
5208
5319
|
return pose;
|
|
5209
5320
|
}
|
|
5210
5321
|
function poseStepFrom(doc, plant, resolve, shape, mode, ctx, first, trackTime, animationTime, posers) {
|
|
5322
|
+
const stage = plant.stage;
|
|
5211
5323
|
const sliders = [];
|
|
5212
5324
|
const world = posers.world(sliders);
|
|
5213
5325
|
if (first) ctx.phase = "update";
|
|
5326
|
+
if (stage !== void 0) stage.begin("readout");
|
|
5214
5327
|
const placeholders = /* @__PURE__ */ new Map();
|
|
5215
5328
|
const slots = posers.slots(sliders, placeholders);
|
|
5216
5329
|
if (slots.conflicts.length > 0) throw new CoreInputError(`the raw walk leaves the slots out: ${slots.conflicts.join("; ")}`);
|
|
5217
5330
|
const evalOrder = plant.drawOrder ?? drawOrderAt;
|
|
5218
|
-
|
|
5331
|
+
const baseOrder = evalOrder(doc.slots.length, posers.drawOrder, posers.at) ?? doc.slots.map((_s, k) => k);
|
|
5332
|
+
let order = baseOrder;
|
|
5219
5333
|
for (const app of sliders) order = evalOrder(doc.slots.length, app.timelines.drawOrder, app.at) ?? order;
|
|
5220
5334
|
const drawOrder = order.map((k) => doc.slots[k].name);
|
|
5335
|
+
if (stage !== void 0) {
|
|
5336
|
+
stage.end("readout");
|
|
5337
|
+
stage.begin("timeline");
|
|
5338
|
+
}
|
|
5221
5339
|
const states = attachmentStates(doc, resolve, placeholders, posers.sample, sliders, plant, posers.mixed);
|
|
5340
|
+
if (stage !== void 0) stage.end("timeline");
|
|
5222
5341
|
if (states.why.length > 0) throw new CoreInputError(`the raw walk leaves the attachments out: ${states.why.join("; ")}`);
|
|
5342
|
+
if (stage !== void 0) stage.begin("readout");
|
|
5223
5343
|
const rank = new Map(drawOrder.map((n, r) => [n, r]));
|
|
5224
5344
|
const shown = [...states.shown].sort((a, b) => (rank.get(a.slot) ?? 0) - (rank.get(b.slot) ?? 0));
|
|
5345
|
+
if (stage !== void 0) stage.end("readout");
|
|
5225
5346
|
const events = () => posers.events().map((e) => [e[0], finite(e[1], "event time"), e[2], finite(e[3], "event float"), e[4]]);
|
|
5226
5347
|
return shape.step(doc, { trackTime, animationTime, world, slots: slots.rows, drawOrder, shown, events }, plant, mode);
|
|
5227
5348
|
}
|
|
@@ -5474,7 +5595,8 @@ function trackComplete(e) {
|
|
|
5474
5595
|
return e.trackTime;
|
|
5475
5596
|
}
|
|
5476
5597
|
const signum = (v) => v > 0 ? 1 : v < 0 ? -1 : 0;
|
|
5477
|
-
const clamp01 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
|
|
5598
|
+
const clamp01$1 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
|
|
5599
|
+
const NO_DARK = [0, 0, 0];
|
|
5478
5600
|
const ATTACH_NONE = 0;
|
|
5479
5601
|
const ATTACH_SETUP = 1;
|
|
5480
5602
|
const ATTACH_RETAIN = 2;
|
|
@@ -5486,12 +5608,15 @@ class Pose {
|
|
|
5486
5608
|
this.resolved = /* @__PURE__ */ new Map();
|
|
5487
5609
|
this.bones = [...doc.bones];
|
|
5488
5610
|
this.active = activeBones(doc);
|
|
5489
|
-
const
|
|
5611
|
+
const stated = viewColours(doc);
|
|
5612
|
+
this.stated = stated;
|
|
5613
|
+
const colour = plant.colour ?? stated;
|
|
5490
5614
|
const gate = plant.slotTimelines ?? slotTimelinesApply;
|
|
5491
5615
|
this.live = doc.slots.map((s) => gate(doc, s, this.active));
|
|
5492
5616
|
this.slots = doc.slots.map((slot) => {
|
|
5493
|
-
const setupDark = slot.dark === void 0 ? null :
|
|
5494
|
-
|
|
5617
|
+
const setupDark = slot.dark === void 0 ? null : stated(slot.dark);
|
|
5618
|
+
const setupLight = slot.color === void 0 ? WHITE : colour(slot.color);
|
|
5619
|
+
return { placeholder: slot.setup, light: [setupLight[0], setupLight[1], setupLight[2], setupLight[3]], dark: setupDark === null ? null : [setupDark[0], setupDark[1], setupDark[2]], deform: null, seen: [], attachmentState: ATTACH_NONE };
|
|
5495
5620
|
});
|
|
5496
5621
|
}
|
|
5497
5622
|
/** Bone `i`'s locals to write: the setup bone copied on its first write, as `posedBones` copies a keyed one. */
|
|
@@ -6068,11 +6193,10 @@ class Mixer {
|
|
|
6068
6193
|
const s = pose.slots[tl.slot];
|
|
6069
6194
|
const l = s.light;
|
|
6070
6195
|
const d = s.dark;
|
|
6071
|
-
const sl = slot.color === void 0 ?
|
|
6072
|
-
const
|
|
6073
|
-
const sd = sdFull === null ? [0, 0, 0] : [sdFull[0], sdFull[1], sdFull[2]];
|
|
6196
|
+
const sl = slot.color === void 0 ? WHITE : (this.plant.colour ?? pose.stated)(slot.color);
|
|
6197
|
+
const sd = slot.dark === void 0 ? NO_DARK : pose.stated(slot.dark);
|
|
6074
6198
|
const clampLight = () => {
|
|
6075
|
-
for (let k = 0; k < 4; k++) l[k] = clamp01(l[k]);
|
|
6199
|
+
for (let k = 0; k < 4; k++) l[k] = clamp01$1(l[k]);
|
|
6076
6200
|
};
|
|
6077
6201
|
const v = this.values(tl.keys, time);
|
|
6078
6202
|
const channels = SLOT_COLOUR_TIMELINES[tl.kind].channels.length;
|
|
@@ -6100,7 +6224,7 @@ class Mixer {
|
|
|
6100
6224
|
}
|
|
6101
6225
|
const rgb = [v[0], v[1], v[2]];
|
|
6102
6226
|
if (alpha !== 1) for (let k = 0; k < 3; k++) rgb[k] = from === SETUP ? sl[k] + (rgb[k] - sl[k]) * alpha : l[k] + (rgb[k] - l[k]) * alpha;
|
|
6103
|
-
for (let k = 0; k < 3; k++) l[k] = clamp01(rgb[k]);
|
|
6227
|
+
for (let k = 0; k < 3; k++) l[k] = clamp01$1(rgb[k]);
|
|
6104
6228
|
return;
|
|
6105
6229
|
}
|
|
6106
6230
|
case "alpha": {
|
|
@@ -6111,7 +6235,7 @@ class Mixer {
|
|
|
6111
6235
|
}
|
|
6112
6236
|
let a = v[0];
|
|
6113
6237
|
if (alpha !== 1) a = from === SETUP ? sl[3] + (a - sl[3]) * alpha : l[3] + (a - l[3]) * alpha;
|
|
6114
|
-
l[3] = clamp01(a);
|
|
6238
|
+
l[3] = clamp01$1(a);
|
|
6115
6239
|
return;
|
|
6116
6240
|
}
|
|
6117
6241
|
case "rgba2":
|
|
@@ -6149,9 +6273,9 @@ class Mixer {
|
|
|
6149
6273
|
dark[k] = from === SETUP ? sd[k] + (dark[k] - sd[k]) * alpha : d[k] + (dark[k] - d[k]) * alpha;
|
|
6150
6274
|
}
|
|
6151
6275
|
}
|
|
6152
|
-
for (let k = 0; k < 3; k++) l[k] = clamp01(light[k]);
|
|
6276
|
+
for (let k = 0; k < 3; k++) l[k] = clamp01$1(light[k]);
|
|
6153
6277
|
}
|
|
6154
|
-
for (let k = 0; k < 3; k++) d[k] = clamp01(dark[k]);
|
|
6278
|
+
for (let k = 0; k < 3; k++) d[k] = clamp01$1(dark[k]);
|
|
6155
6279
|
return;
|
|
6156
6280
|
}
|
|
6157
6281
|
}
|
|
@@ -6382,7 +6506,10 @@ class Tracks {
|
|
|
6382
6506
|
if (typeof dt !== "number" || !Number.isFinite(dt) || dt < 0) throw new CoreInputError(`the live tracks' step ${n} is ${String(dt)}, not a finite time at or above 0`);
|
|
6383
6507
|
return this.guard(`step ${n}`, () => {
|
|
6384
6508
|
this.time += dt;
|
|
6509
|
+
const stage = this.plant.stage;
|
|
6510
|
+
if (stage !== void 0) stage.begin("timeline");
|
|
6385
6511
|
this.mixer.update(dt);
|
|
6512
|
+
if (stage !== void 0) stage.end("timeline");
|
|
6386
6513
|
const pose = this.pose(dt, false);
|
|
6387
6514
|
this.steps = n;
|
|
6388
6515
|
return pose;
|
|
@@ -6403,6 +6530,8 @@ class Tracks {
|
|
|
6403
6530
|
const ctx = path.ctx;
|
|
6404
6531
|
const before = ctx.time;
|
|
6405
6532
|
ctx.time += this.clock === void 0 ? dt : this.clock(dt);
|
|
6533
|
+
const stage = this.plant.stage;
|
|
6534
|
+
if (stage !== void 0) stage.begin("timeline");
|
|
6406
6535
|
const pose = new Pose(doc, this.plant);
|
|
6407
6536
|
const animationTime = this.mixer.apply(pose);
|
|
6408
6537
|
const entry = this.mixer.tracks[0] ?? null;
|
|
@@ -6411,6 +6540,7 @@ class Tracks {
|
|
|
6411
6540
|
context: { trackTime: entry?.trackTime ?? null, animation: entry?.anim.name ?? null, globalTime: this.time }
|
|
6412
6541
|
};
|
|
6413
6542
|
const mixed = new Map(doc.slots.map((s, k) => [s.name, { deform: pose.slots[k].deform, seen: pose.slots[k].seen }]));
|
|
6543
|
+
if (stage !== void 0) stage.end("timeline");
|
|
6414
6544
|
return path.step(first, this.time, animationTime, {
|
|
6415
6545
|
world: (sliders) => steppedLocalsWorld(doc, pose.bones, path.raw, sliders, { ctx, before, adjustment }),
|
|
6416
6546
|
slots: (sliders, placeholders) => slotRowsFrom(doc, path.raw, sliders, placeholders, (_slot, _live) => {
|
|
@@ -6737,6 +6867,15 @@ async function unpackRegions(pages, pageImages, options = {}) {
|
|
|
6737
6867
|
function revokeRegions(images) {
|
|
6738
6868
|
for (const image of images.values()) revokeOwned(image.url);
|
|
6739
6869
|
}
|
|
6870
|
+
const triangleReadSources = /* @__PURE__ */ new WeakMap();
|
|
6871
|
+
function triangleReadSource(source) {
|
|
6872
|
+
let kept = triangleReadSources.get(source);
|
|
6873
|
+
if (kept === void 0) {
|
|
6874
|
+
kept = Object.isFrozen(source) ? Array.from(source) : source;
|
|
6875
|
+
triangleReadSources.set(source, kept);
|
|
6876
|
+
}
|
|
6877
|
+
return kept;
|
|
6878
|
+
}
|
|
6740
6879
|
const BLEND_FACTORS = {
|
|
6741
6880
|
Normal: [
|
|
6742
6881
|
1,
|
|
@@ -6938,7 +7077,8 @@ class MeshGlBlitter {
|
|
|
6938
7077
|
const data = this.vertexData;
|
|
6939
7078
|
let f = 0;
|
|
6940
7079
|
for (let i = 0; i < jobs.length; i++) {
|
|
6941
|
-
const { vertices, uvs,
|
|
7080
|
+
const { vertices, uvs, ratio } = jobs[i];
|
|
7081
|
+
const triangles = triangleReadSource(jobs[i].triangles);
|
|
6942
7082
|
const ox = packX[i];
|
|
6943
7083
|
const oy = packY[i];
|
|
6944
7084
|
for (let t = 0; t < triangles.length; t++) {
|
|
@@ -7461,7 +7601,8 @@ class DomRenderer {
|
|
|
7461
7601
|
* clipping attachment, an inactive bone, a kind the DOM tier does not draw —
|
|
7462
7602
|
* are hidden, their elements kept for when they draw again.
|
|
7463
7603
|
*/
|
|
7464
|
-
render(
|
|
7604
|
+
render(input) {
|
|
7605
|
+
const frame2 = renderFrameOf(input);
|
|
7465
7606
|
this.clipCount = 0;
|
|
7466
7607
|
this.clipSkipCount = 0;
|
|
7467
7608
|
this.clipWriteCount = 0;
|
|
@@ -7703,6 +7844,7 @@ class DomRenderer {
|
|
|
7703
7844
|
ctx.setTransform(ratio, 0, 0, ratio, 0, 0);
|
|
7704
7845
|
const uw = page.width;
|
|
7705
7846
|
const uh = page.height;
|
|
7847
|
+
triangles = triangleReadSource(triangles);
|
|
7706
7848
|
for (let t = 0; t < triangles.length; t += 3) {
|
|
7707
7849
|
const i0 = triangles[t] * 2;
|
|
7708
7850
|
const i1 = triangles[t + 1] * 2;
|
|
@@ -8215,7 +8357,8 @@ class CanvasRenderer {
|
|
|
8215
8357
|
this.growTo(box.x, -(box.y + box.height), box.x + box.width, -box.y);
|
|
8216
8358
|
}
|
|
8217
8359
|
/** Draws one frame. */
|
|
8218
|
-
render(
|
|
8360
|
+
render(input) {
|
|
8361
|
+
const frame2 = renderFrameOf(input);
|
|
8219
8362
|
this.drawCalls = 0;
|
|
8220
8363
|
this.blendFlushes = 0;
|
|
8221
8364
|
this.trianglesDrawn = 0;
|
|
@@ -8444,7 +8587,8 @@ class CanvasRenderer {
|
|
|
8444
8587
|
if (!page) continue;
|
|
8445
8588
|
const piece = this.piecesOf(slot, clips);
|
|
8446
8589
|
if (!piece) continue;
|
|
8447
|
-
const { vertices, uvs
|
|
8590
|
+
const { vertices, uvs } = piece;
|
|
8591
|
+
const triangles = triangleReadSource(piece.triangles);
|
|
8448
8592
|
const count = triangles.length;
|
|
8449
8593
|
if (count === 0) continue;
|
|
8450
8594
|
if (!this.glPages.has(page) && blitter) {
|
|
@@ -8519,7 +8663,8 @@ class CanvasRenderer {
|
|
|
8519
8663
|
}
|
|
8520
8664
|
ctx.globalAlpha = Math.max(0, Math.min(1, slot.tint[3]));
|
|
8521
8665
|
this.drawCalls++;
|
|
8522
|
-
const { vertices, uvs
|
|
8666
|
+
const { vertices, uvs } = piece;
|
|
8667
|
+
const triangles = triangleReadSource(piece.triangles);
|
|
8523
8668
|
if (slot.kind === "region" && vertices === slot.corners) {
|
|
8524
8669
|
const c = vertices;
|
|
8525
8670
|
const sx0 = uvs[2] * uw, sy0 = uvs[3] * uh;
|
|
@@ -8832,6 +8977,1383 @@ async function loadModel(options) {
|
|
|
8832
8977
|
}
|
|
8833
8978
|
};
|
|
8834
8979
|
}
|
|
8980
|
+
const CURVE_LINEAR = 0;
|
|
8981
|
+
const CURVE_STEPPED = 1;
|
|
8982
|
+
const CURVE_BEZIER = 2;
|
|
8983
|
+
const POLYLINE = 2 * (BEZIER_PIECES + 1);
|
|
8984
|
+
function compileCurve(keys, channels) {
|
|
8985
|
+
const n = keys.length;
|
|
8986
|
+
const times2 = new Float64Array(n);
|
|
8987
|
+
const values2 = new Float64Array(n * channels);
|
|
8988
|
+
const kinds = new Uint8Array(n);
|
|
8989
|
+
const polylines = new Float64Array(n * channels * POLYLINE);
|
|
8990
|
+
for (let i = 0; i < n; i++) {
|
|
8991
|
+
const a = keys[i];
|
|
8992
|
+
times2[i] = a.time;
|
|
8993
|
+
for (let c = 0; c < channels; c++) values2[i * channels + c] = a.values[c];
|
|
8994
|
+
const curve = a.curve;
|
|
8995
|
+
kinds[i] = curve === "linear" ? CURVE_LINEAR : curve === "stepped" ? CURVE_STEPPED : CURVE_BEZIER;
|
|
8996
|
+
if (typeof curve === "string" || i + 1 >= n) continue;
|
|
8997
|
+
const b = keys[i + 1];
|
|
8998
|
+
for (let c = 0; c < channels; c++) {
|
|
8999
|
+
const inner = bezierPolyline(a.stated.time, a.stated.values[c], curve[c * 4], curve[c * 4 + 1], curve[c * 4 + 2], curve[c * 4 + 3], b.stated.time, b.stated.values[c]);
|
|
9000
|
+
const o = (i * channels + c) * POLYLINE;
|
|
9001
|
+
polylines[o] = a.time;
|
|
9002
|
+
polylines[o + 1] = a.values[c];
|
|
9003
|
+
for (let q = 0; q < inner.length; q++) polylines[o + 2 + q] = inner[q];
|
|
9004
|
+
polylines[o + POLYLINE - 2] = b.time;
|
|
9005
|
+
polylines[o + POLYLINE - 1] = b.values[c];
|
|
9006
|
+
}
|
|
9007
|
+
}
|
|
9008
|
+
return { keys: n, channels, times: times2, values: values2, kinds, polylines };
|
|
9009
|
+
}
|
|
9010
|
+
function keyAt(curve, cursors, slot, t) {
|
|
9011
|
+
const times2 = curve.times;
|
|
9012
|
+
let i = cursors[slot];
|
|
9013
|
+
while (i >= 0 && times2[i] > t) i--;
|
|
9014
|
+
while (i + 1 < curve.keys && times2[i + 1] <= t) i++;
|
|
9015
|
+
cursors[slot] = i;
|
|
9016
|
+
return i;
|
|
9017
|
+
}
|
|
9018
|
+
function channelValue(curve, i, c, t) {
|
|
9019
|
+
const ch = curve.channels;
|
|
9020
|
+
const values2 = curve.values;
|
|
9021
|
+
const v0 = values2[i * ch + c];
|
|
9022
|
+
if (i + 1 >= curve.keys) return v0;
|
|
9023
|
+
const kind = curve.kinds[i];
|
|
9024
|
+
if (kind === CURVE_STEPPED) return v0;
|
|
9025
|
+
if (kind === CURVE_LINEAR) {
|
|
9026
|
+
const t0 = curve.times[i];
|
|
9027
|
+
return v0 + (t - t0) / (curve.times[i + 1] - t0) * (values2[(i + 1) * ch + c] - v0);
|
|
9028
|
+
}
|
|
9029
|
+
const p = curve.polylines;
|
|
9030
|
+
const o = (i * ch + c) * POLYLINE;
|
|
9031
|
+
let q = 2;
|
|
9032
|
+
while (q < POLYLINE - 2 && p[o + q] < t) q += 2;
|
|
9033
|
+
const x0 = p[o + q - 2];
|
|
9034
|
+
const y0 = p[o + q - 1];
|
|
9035
|
+
return y0 + (t - x0) / (p[o + q] - x0) * (p[o + q + 1] - y0);
|
|
9036
|
+
}
|
|
9037
|
+
const RUNTIME_STAGE = 1;
|
|
9038
|
+
function missingText(m) {
|
|
9039
|
+
return `${m.feature}: ${m.detail}${m.animation === null ? "" : ` (animation "${m.animation}")`}`;
|
|
9040
|
+
}
|
|
9041
|
+
class RuntimeRefusal extends Error {
|
|
9042
|
+
constructor(missing) {
|
|
9043
|
+
super(`not posed by the runtime poser (stage ${RUNTIME_STAGE}), ${missing.length} construct(s) missing: ${missing.map(missingText).join("; ")}`);
|
|
9044
|
+
this.missing = missing;
|
|
9045
|
+
}
|
|
9046
|
+
}
|
|
9047
|
+
class RuntimeInputError extends Error {
|
|
9048
|
+
}
|
|
9049
|
+
const L_X = 0;
|
|
9050
|
+
const L_Y = 1;
|
|
9051
|
+
const L_ROTATION = 2;
|
|
9052
|
+
const L_SCALE_X = 3;
|
|
9053
|
+
const L_SCALE_Y = 4;
|
|
9054
|
+
const L_SHEAR_X = 5;
|
|
9055
|
+
const L_SHEAR_Y = 6;
|
|
9056
|
+
const LOCALS = 7;
|
|
9057
|
+
const W_A = 0;
|
|
9058
|
+
const W_B = 1;
|
|
9059
|
+
const W_C = 2;
|
|
9060
|
+
const W_D = 3;
|
|
9061
|
+
const W_X = 4;
|
|
9062
|
+
const W_Y = 5;
|
|
9063
|
+
const WORLDS = 6;
|
|
9064
|
+
const MODE_NORMAL = 0;
|
|
9065
|
+
const MODE_ONLY_TRANSLATION = 1;
|
|
9066
|
+
const MODE_NO_ROTATION_OR_REFLECTION = 2;
|
|
9067
|
+
const MODE_NO_SCALE = 3;
|
|
9068
|
+
const MODE_NO_SCALE_OR_REFLECTION = 4;
|
|
9069
|
+
const MODE_CODES = { normal: MODE_NORMAL, onlyTranslation: MODE_ONLY_TRANSLATION, noRotationOrReflection: MODE_NO_ROTATION_OR_REFLECTION, noScale: MODE_NO_SCALE, noScaleOrReflection: MODE_NO_SCALE_OR_REFLECTION };
|
|
9070
|
+
const DRAW_NONE = 0;
|
|
9071
|
+
const DRAW_REGION = 1;
|
|
9072
|
+
const DRAW_MESH = 2;
|
|
9073
|
+
const DRAW_WEIGHTED = 3;
|
|
9074
|
+
const SCALE_Y_NONE = 0;
|
|
9075
|
+
const SCALE_Y_UNIFORM = 1;
|
|
9076
|
+
const SCALE_Y_VOLUME = 2;
|
|
9077
|
+
const IK_BEND_POSITIVE = 1;
|
|
9078
|
+
const IK_COMPRESS = 2;
|
|
9079
|
+
const IK_STRETCH = 4;
|
|
9080
|
+
const RAD = RUNTIME_PI / 180;
|
|
9081
|
+
const BONE_KINDS = {
|
|
9082
|
+
rotate: [[L_ROTATION, 0]],
|
|
9083
|
+
translate: [[L_X, 0], [L_Y, 0]],
|
|
9084
|
+
translatex: [[L_X, 0]],
|
|
9085
|
+
translatey: [[L_Y, 0]],
|
|
9086
|
+
scale: [[L_SCALE_X, 1], [L_SCALE_Y, 1]],
|
|
9087
|
+
scalex: [[L_SCALE_X, 1]],
|
|
9088
|
+
scaley: [[L_SCALE_Y, 1]],
|
|
9089
|
+
shear: [[L_SHEAR_X, 0], [L_SHEAR_Y, 0]],
|
|
9090
|
+
shearx: [[L_SHEAR_X, 0]],
|
|
9091
|
+
sheary: [[L_SHEAR_Y, 0]]
|
|
9092
|
+
};
|
|
9093
|
+
const COLOUR_CHANNELS = { rgba: [0, 1, 2, 3], rgb: [0, 1, 2], alpha: [3] };
|
|
9094
|
+
class Refusals {
|
|
9095
|
+
constructor(admitted) {
|
|
9096
|
+
this.admitted = admitted;
|
|
9097
|
+
this.model = [];
|
|
9098
|
+
this.byAnimation = /* @__PURE__ */ new Map();
|
|
9099
|
+
}
|
|
9100
|
+
/** Records the construct unless a plant admits it. The caller poses the construct as absent either way. */
|
|
9101
|
+
note(feature, detail, animation = null) {
|
|
9102
|
+
if (this.admitted.has(feature)) return;
|
|
9103
|
+
const m = { feature, detail, animation };
|
|
9104
|
+
if (animation === null) this.model.push(m);
|
|
9105
|
+
else {
|
|
9106
|
+
const list = this.byAnimation.get(animation) ?? [];
|
|
9107
|
+
list.push(m);
|
|
9108
|
+
this.byAnimation.set(animation, list);
|
|
9109
|
+
}
|
|
9110
|
+
}
|
|
9111
|
+
}
|
|
9112
|
+
function compileRuntime(doc, options = {}, plant = {}) {
|
|
9113
|
+
const refusals = new Refusals(new Set(plant.admit ?? []));
|
|
9114
|
+
const ignoreEvents = (options.ignore ?? []).includes("events");
|
|
9115
|
+
const absentWhy = constraintsAbsentWhy(doc);
|
|
9116
|
+
if (absentWhy !== null) refusals.note("oracle-absent", `the oracle leaves the bones out: ${absentWhy}`);
|
|
9117
|
+
const nb = doc.bones.length;
|
|
9118
|
+
const boneIndex2 = new Map(doc.bones.map((b, i) => [b.name, i]));
|
|
9119
|
+
const indexOf = (name) => {
|
|
9120
|
+
const i = boneIndex2.get(name);
|
|
9121
|
+
if (i === void 0) throw new RuntimeInputError(`bone "${name}" is not a bone of this document (readModel refuses it first)`);
|
|
9122
|
+
return i;
|
|
9123
|
+
};
|
|
9124
|
+
const active = activeBones(doc);
|
|
9125
|
+
const parents = new Int32Array(nb);
|
|
9126
|
+
const modes = new Uint8Array(nb);
|
|
9127
|
+
const activeFlags = new Uint8Array(nb);
|
|
9128
|
+
const lengths = new Float64Array(nb);
|
|
9129
|
+
const setup = new Float64Array(nb * LOCALS);
|
|
9130
|
+
doc.bones.forEach((b, i) => {
|
|
9131
|
+
parents[i] = b.parent === void 0 ? -1 : indexOf(b.parent);
|
|
9132
|
+
if (parents[i] >= i) throw new RuntimeInputError(`bone "${b.name}" is listed before its parent (readModel refuses it first)`);
|
|
9133
|
+
const mode = b.inheritMode === void 0 ? "normal" : foldInheritMode(b.inheritMode);
|
|
9134
|
+
if (mode === null) throw new RuntimeInputError(`bone "${b.name}": inheritMode ${JSON.stringify(b.inheritMode)} is no mode (readModel refuses it first)`);
|
|
9135
|
+
modes[i] = MODE_CODES[mode];
|
|
9136
|
+
activeFlags[i] = active.has(b.name) ? 1 : 0;
|
|
9137
|
+
lengths[i] = b.length ?? 0;
|
|
9138
|
+
const o = i * LOCALS;
|
|
9139
|
+
setup[o + L_X] = b.x ?? 0;
|
|
9140
|
+
setup[o + L_Y] = b.y ?? 0;
|
|
9141
|
+
setup[o + L_ROTATION] = b.rotation ?? 0;
|
|
9142
|
+
setup[o + L_SCALE_X] = b.scaleX ?? 1;
|
|
9143
|
+
setup[o + L_SCALE_Y] = b.scaleY ?? 1;
|
|
9144
|
+
setup[o + L_SHEAR_X] = b.shearX ?? 0;
|
|
9145
|
+
setup[o + L_SHEAR_Y] = b.shearY ?? 0;
|
|
9146
|
+
});
|
|
9147
|
+
if (plant.setupNudge !== void 0) setup[indexOf(plant.setupNudge.bone) * LOCALS + plant.setupNudge.field] += plant.setupNudge.delta;
|
|
9148
|
+
const lists = doc.bones.map(() => []);
|
|
9149
|
+
for (let k = 0; k < nb; k++) for (let at = k; at >= 0; at = parents[at]) lists[at].push(k);
|
|
9150
|
+
const subtreeStart = new Int32Array(nb + 1);
|
|
9151
|
+
const flat = [];
|
|
9152
|
+
for (let k = 0; k < nb; k++) {
|
|
9153
|
+
subtreeStart[k] = flat.length;
|
|
9154
|
+
for (const x of lists[k]) flat.push(x);
|
|
9155
|
+
}
|
|
9156
|
+
subtreeStart[nb] = flat.length;
|
|
9157
|
+
const constraints = [];
|
|
9158
|
+
for (const c of doc.constraints) if (c.kind === "path" || c.kind === "physics" || c.kind === "slider") refusals.note(`${c.kind}-constraint`, `${c.kind} constraint "${c.name}"`);
|
|
9159
|
+
for (const r of constraintRecords(doc)) {
|
|
9160
|
+
if (r.kind !== "ik" && r.kind !== "transform") continue;
|
|
9161
|
+
const applied = constraintInactiveWhy(r, active) === null;
|
|
9162
|
+
const bones = Int32Array.from(r.bones.map(indexOf));
|
|
9163
|
+
if (applied && r.bones.some((b) => !active.has(b))) refusals.note("inactive-constrained-bone", `${r.kind} constraint "${r.name}" names bone(s) ${r.bones.filter((b) => !active.has(b)).map((b) => `"${b}"`).join(", ")}, which the view leaves inactive — what the oracle writes there depends on the pass before`);
|
|
9164
|
+
const index = constraints.length;
|
|
9165
|
+
if (r.kind === "ik") constraints.push(compileIk(r, index, applied, bones, indexOf));
|
|
9166
|
+
else {
|
|
9167
|
+
if (r.localSource || r.localTarget) refusals.note("local-transform", `transform constraint "${r.name}" ${[r.localSource ? "reads local values (localSource)" : "", r.localTarget ? "writes local values (localTarget)" : ""].filter((x) => x !== "").join(" and ")}`);
|
|
9168
|
+
if (r.additive) refusals.note("additive-transform", `transform constraint "${r.name}" is additive`);
|
|
9169
|
+
const t = compileTransform(r, index, applied, bones, indexOf);
|
|
9170
|
+
if (applied) {
|
|
9171
|
+
for (let k = 0; k < bones.length; k++) {
|
|
9172
|
+
const m = modes[bones[k]];
|
|
9173
|
+
if (parents[bones[k]] >= 0 && (m === MODE_NO_SCALE || m === MODE_NO_SCALE_OR_REFLECTION)) refusals.note("noscale-world-target", `transform constraint "${r.name}" moves bone "${r.bones[k]}" in world space, and its local values are read back in a noScale mode`);
|
|
9174
|
+
}
|
|
9175
|
+
}
|
|
9176
|
+
constraints.push(t);
|
|
9177
|
+
}
|
|
9178
|
+
}
|
|
9179
|
+
const attachments = [];
|
|
9180
|
+
let vertexCount = 0;
|
|
9181
|
+
const sourceOf = sourceOfDoc(doc);
|
|
9182
|
+
const attachmentView = plant.attachmentSkin === void 0 ? doc : underSkin(doc, plant.attachmentSkin);
|
|
9183
|
+
const resolvedBySlot = doc.slots.map(() => /* @__PURE__ */ new Map());
|
|
9184
|
+
const resolve = (si, placeholder, animation) => {
|
|
9185
|
+
const cache = resolvedBySlot[si];
|
|
9186
|
+
let kept = cache.get(placeholder);
|
|
9187
|
+
if (kept === void 0) {
|
|
9188
|
+
kept = resolveRecord(si, placeholder);
|
|
9189
|
+
cache.set(placeholder, kept);
|
|
9190
|
+
}
|
|
9191
|
+
if (kept.missing !== null) refusals.note(kept.missing[0], kept.missing[1], animation);
|
|
9192
|
+
return kept.index;
|
|
9193
|
+
};
|
|
9194
|
+
const resolveRecord = (si, placeholder) => {
|
|
9195
|
+
const slot = doc.slots[si];
|
|
9196
|
+
const shown = placeholder === null ? null : shownAttachment(attachmentView, slot, placeholder);
|
|
9197
|
+
if (shown === null) return { index: -1, missing: null };
|
|
9198
|
+
if ("conflict" in shown) return { index: -1, missing: ["skin-conflict", `slot "${slot.name}" placeholder "${placeholder}" is filled by skins that disagree`] };
|
|
9199
|
+
const row = shownRow(shown);
|
|
9200
|
+
const g = shown.record.geometry;
|
|
9201
|
+
const at = { slot: si, name: row.name, skin: shown.skin, placeholder: shown.placeholder, path: CORE_REGION_KINDS.has(shown.record.kind) ? row.path : null, bone: indexOf(slot.bone), offset: vertexCount };
|
|
9202
|
+
let made = null;
|
|
9203
|
+
let missing = null;
|
|
9204
|
+
if (g?.kind === "clipping") missing = ["clipping", `slot "${slot.name}" shows clipping attachment "${row.name}"`];
|
|
9205
|
+
else if (g?.kind === "region") {
|
|
9206
|
+
const r = g.region;
|
|
9207
|
+
if (r.atlas === null) missing = ["null-atlas-region", `slot "${slot.name}" shows region "${row.name}", whose atlas rectangle the model states as null`];
|
|
9208
|
+
else {
|
|
9209
|
+
const W = r.width;
|
|
9210
|
+
const H = r.height;
|
|
9211
|
+
const sx = r.scaleX ?? 1;
|
|
9212
|
+
const sy = r.scaleY ?? 1;
|
|
9213
|
+
const rect = r.atlas;
|
|
9214
|
+
const kx = W / rect.originalWidth * sx;
|
|
9215
|
+
const ky = H / rect.originalHeight * sy;
|
|
9216
|
+
const x1 = -W / 2 * sx + rect.offsetX * kx;
|
|
9217
|
+
const y1 = -H / 2 * sy + rect.offsetY * ky;
|
|
9218
|
+
const x2 = x1 + rect.width * kx;
|
|
9219
|
+
const y2 = y1 + rect.height * ky;
|
|
9220
|
+
const angle = (r.rotation ?? 0) * RAD;
|
|
9221
|
+
const cos = Math.cos(angle);
|
|
9222
|
+
const sin = Math.sin(angle);
|
|
9223
|
+
const x = r.x ?? 0;
|
|
9224
|
+
const y = r.y ?? 0;
|
|
9225
|
+
const local = new Float64Array(8);
|
|
9226
|
+
const corners = [x1, y1, x1, y2, x2, y2, x2, y1];
|
|
9227
|
+
for (let k = 0; k < 8; k += 2) {
|
|
9228
|
+
const px = corners[k];
|
|
9229
|
+
const py = corners[k + 1];
|
|
9230
|
+
local[k] = px * cos + x - py * sin;
|
|
9231
|
+
local[k + 1] = py * cos + y + px * sin;
|
|
9232
|
+
}
|
|
9233
|
+
made = vertexAttachment(at, DRAW_REGION, local, 8);
|
|
9234
|
+
}
|
|
9235
|
+
} else if (g?.kind === "mesh" || g?.kind === "linkedmesh") {
|
|
9236
|
+
const source = g.kind === "mesh" ? g.vertices : sourceOf(g.skin, g.slot, g.source);
|
|
9237
|
+
if (typeof source === "string") throw new RuntimeInputError(`slot "${slot.name}": ${source} (readModel refuses it first)`);
|
|
9238
|
+
made = meshAttachment(at, source, indexOf, plant.bindingIndex, nb);
|
|
9239
|
+
}
|
|
9240
|
+
if (made === null) made = vertexAttachment(at, DRAW_NONE, new Float64Array(0), 0);
|
|
9241
|
+
attachments.push(made);
|
|
9242
|
+
vertexCount += made.count;
|
|
9243
|
+
return { index: attachments.length - 1, missing };
|
|
9244
|
+
};
|
|
9245
|
+
const slots = doc.slots.map((slot, si) => {
|
|
9246
|
+
const bone2 = indexOf(slot.bone);
|
|
9247
|
+
const light = slot.color === void 0 ? [1, 1, 1, 1] : readColour(slot.color);
|
|
9248
|
+
return {
|
|
9249
|
+
name: slot.name,
|
|
9250
|
+
bone: bone2,
|
|
9251
|
+
// A slot on a bone the view leaves inactive is not animated (`slotTimelinesApply` in src/core/skins.ts).
|
|
9252
|
+
live: active.has(slot.bone),
|
|
9253
|
+
setupAttachment: resolve(si, slot.setup, null),
|
|
9254
|
+
light: Float64Array.from(light),
|
|
9255
|
+
dark: slot.dark === void 0 ? null : Float64Array.from(readColour(slot.dark).slice(0, 3)),
|
|
9256
|
+
blend: readBlend(slot)
|
|
9257
|
+
};
|
|
9258
|
+
});
|
|
9259
|
+
const slotIndex = new Map(doc.slots.map((s, i) => [s.name, i]));
|
|
9260
|
+
const animations = /* @__PURE__ */ new Map();
|
|
9261
|
+
const refused = [];
|
|
9262
|
+
let cursorMax = 0;
|
|
9263
|
+
for (const anim of doc.animations) {
|
|
9264
|
+
const compiled = compileAnimation(anim, refusals, ignoreEvents, boneIndex2, slotIndex, slots, constraints, (si, ph) => resolve(si, ph, anim.name));
|
|
9265
|
+
const missing = refusals.byAnimation.get(anim.name);
|
|
9266
|
+
if (missing !== void 0 && missing.length > 0) refused.push({ animation: anim.name, missing });
|
|
9267
|
+
else {
|
|
9268
|
+
animations.set(anim.name, compiled);
|
|
9269
|
+
cursorMax = Math.max(cursorMax, compiled.cursors);
|
|
9270
|
+
}
|
|
9271
|
+
}
|
|
9272
|
+
if (refusals.model.length > 0) throw new RuntimeRefusal(refusals.model);
|
|
9273
|
+
const names = doc.animations.map((a) => a.name);
|
|
9274
|
+
return {
|
|
9275
|
+
eventWrapOffByOne: plant.eventWrapOffByOne === true,
|
|
9276
|
+
restoreEventsFromStart: plant.restoreEventsFromStart === true,
|
|
9277
|
+
doc,
|
|
9278
|
+
boneNames: doc.bones.map((b) => b.name),
|
|
9279
|
+
parents,
|
|
9280
|
+
modes,
|
|
9281
|
+
active: activeFlags,
|
|
9282
|
+
lengths,
|
|
9283
|
+
setup,
|
|
9284
|
+
subtreeStart,
|
|
9285
|
+
subtrees: Int32Array.from(flat),
|
|
9286
|
+
slots,
|
|
9287
|
+
attachments,
|
|
9288
|
+
vertexCount,
|
|
9289
|
+
constraints,
|
|
9290
|
+
animations,
|
|
9291
|
+
refused,
|
|
9292
|
+
cursors: cursorMax,
|
|
9293
|
+
scratch: plant.scratchByBones === true ? Math.max(1, nb) : Math.max(1, nb, ...constraints.map((c) => c.bones.length)),
|
|
9294
|
+
animation(name) {
|
|
9295
|
+
const found = animations.get(name);
|
|
9296
|
+
if (found !== void 0) return found;
|
|
9297
|
+
const no = refused.find((r) => r.animation === name);
|
|
9298
|
+
if (no !== void 0) throw new RuntimeRefusal(no.missing);
|
|
9299
|
+
throw new RuntimeInputError(`animation ${JSON.stringify(name)} is not one of this document's [${names.join(", ")}]`);
|
|
9300
|
+
}
|
|
9301
|
+
};
|
|
9302
|
+
}
|
|
9303
|
+
const NO_INTS = new Int32Array(0);
|
|
9304
|
+
const NO_NUMBERS = new Float64Array(0);
|
|
9305
|
+
function vertexAttachment(at, draw, local, count) {
|
|
9306
|
+
return { ...at, draw, local, count, bindingCounts: NO_INTS, bindingBones: NO_INTS, bindingX: NO_NUMBERS, bindingY: NO_NUMBERS, bindingWeights: NO_NUMBERS };
|
|
9307
|
+
}
|
|
9308
|
+
function meshAttachment(at, vertices, indexOf, rewrite, bones) {
|
|
9309
|
+
if (!vertices.weighted) {
|
|
9310
|
+
const n = vertices.xy.length - vertices.xy.length % 2;
|
|
9311
|
+
const local = new Float64Array(n);
|
|
9312
|
+
for (let i = 0; i < n; i++) local[i] = Math.fround(vertices.xy[i]);
|
|
9313
|
+
return vertexAttachment(at, DRAW_MESH, local, n);
|
|
9314
|
+
}
|
|
9315
|
+
const counts = [];
|
|
9316
|
+
const ids = [];
|
|
9317
|
+
const xs = [];
|
|
9318
|
+
const ys = [];
|
|
9319
|
+
const ws = [];
|
|
9320
|
+
for (const influences of vertices.bindings) {
|
|
9321
|
+
counts.push(influences.length);
|
|
9322
|
+
for (const b of influences) {
|
|
9323
|
+
const i = indexOf(b.bone);
|
|
9324
|
+
ids.push(rewrite === void 0 ? i : rewrite(i, bones));
|
|
9325
|
+
xs.push(Math.fround(b.x));
|
|
9326
|
+
ys.push(Math.fround(b.y));
|
|
9327
|
+
ws.push(Math.fround(b.weight));
|
|
9328
|
+
}
|
|
9329
|
+
}
|
|
9330
|
+
return { ...at, draw: DRAW_WEIGHTED, local: NO_NUMBERS, count: counts.length * 2, bindingCounts: Int32Array.from(counts), bindingBones: Int32Array.from(ids), bindingX: Float64Array.from(xs), bindingY: Float64Array.from(ys), bindingWeights: Float64Array.from(ws) };
|
|
9331
|
+
}
|
|
9332
|
+
function compileIk(r, index, applied, bones, indexOf) {
|
|
9333
|
+
return {
|
|
9334
|
+
kind: "ik",
|
|
9335
|
+
name: r.name,
|
|
9336
|
+
index,
|
|
9337
|
+
applied,
|
|
9338
|
+
bones,
|
|
9339
|
+
target: indexOf(r.target),
|
|
9340
|
+
scaleY: r.scaleY === "uniform" ? SCALE_Y_UNIFORM : r.scaleY === "volume" ? SCALE_Y_VOLUME : SCALE_Y_NONE,
|
|
9341
|
+
mix: r.mix,
|
|
9342
|
+
softness: r.softness,
|
|
9343
|
+
flags: ikFlags(r.bendPositive, r.compress, r.stretch)
|
|
9344
|
+
};
|
|
9345
|
+
}
|
|
9346
|
+
function ikFlags(bendPositive, compress, stretch) {
|
|
9347
|
+
return (bendPositive ? IK_BEND_POSITIVE : 0) | (compress ? IK_COMPRESS : 0) | (stretch ? IK_STRETCH : 0);
|
|
9348
|
+
}
|
|
9349
|
+
function compileTransform(r, index, applied, bones, indexOf) {
|
|
9350
|
+
const fromProperty = [];
|
|
9351
|
+
const fromOffset = [];
|
|
9352
|
+
const toStart = [];
|
|
9353
|
+
const toEnd = [];
|
|
9354
|
+
const toProperty = [];
|
|
9355
|
+
const toOffset = [];
|
|
9356
|
+
const toScale = [];
|
|
9357
|
+
const toMax = [];
|
|
9358
|
+
for (const from of r.properties) {
|
|
9359
|
+
fromProperty.push(TRANSFORM_PROPERTIES.indexOf(from.property));
|
|
9360
|
+
fromOffset.push(from.offset);
|
|
9361
|
+
toStart.push(toProperty.length);
|
|
9362
|
+
for (const to of from.to) {
|
|
9363
|
+
toProperty.push(TRANSFORM_PROPERTIES.indexOf(to.property));
|
|
9364
|
+
toOffset.push(to.offset);
|
|
9365
|
+
toScale.push(to.scale);
|
|
9366
|
+
toMax.push(to.max);
|
|
9367
|
+
}
|
|
9368
|
+
toEnd.push(toProperty.length);
|
|
9369
|
+
}
|
|
9370
|
+
return {
|
|
9371
|
+
kind: "transform",
|
|
9372
|
+
name: r.name,
|
|
9373
|
+
index,
|
|
9374
|
+
applied,
|
|
9375
|
+
bones,
|
|
9376
|
+
source: indexOf(r.source),
|
|
9377
|
+
fromProperty: Int32Array.from(fromProperty),
|
|
9378
|
+
fromOffset: Float64Array.from(fromOffset),
|
|
9379
|
+
toStart: Int32Array.from(toStart),
|
|
9380
|
+
toEnd: Int32Array.from(toEnd),
|
|
9381
|
+
toProperty: Int32Array.from(toProperty),
|
|
9382
|
+
toOffset: Float64Array.from(toOffset),
|
|
9383
|
+
toScale: Float64Array.from(toScale),
|
|
9384
|
+
toMax: Float64Array.from(toMax),
|
|
9385
|
+
offsets: Float64Array.from(TRANSFORM_PROPERTIES.map((p) => r.offsets[p])),
|
|
9386
|
+
mixes: Float64Array.from(TRANSFORM_PROPERTIES.map((p) => r.mixes[p])),
|
|
9387
|
+
clamp: r.clamp
|
|
9388
|
+
};
|
|
9389
|
+
}
|
|
9390
|
+
function compileAnimation(anim, refusals, ignoreEvents, boneIndex2, slotIndex, slots, constraints, resolve) {
|
|
9391
|
+
const tl = anim.timelines;
|
|
9392
|
+
const name = anim.name;
|
|
9393
|
+
let cursors = 0;
|
|
9394
|
+
const deform = tl.attachments.filter((a) => a.deform !== null);
|
|
9395
|
+
const sequence = tl.attachments.filter((a) => a.sequence !== null);
|
|
9396
|
+
if (deform.length > 0) refusals.note("deform-timeline", `${deform.length} deform timeline(s): ${deform.slice(0, 3).map((a) => `${a.skin}/${a.slot}/${a.attachment}`).join(", ")}${deform.length > 3 ? ", …" : ""}`, name);
|
|
9397
|
+
if (sequence.length > 0) refusals.note("sequence-timeline", `${sequence.length} sequence timeline(s): ${sequence.slice(0, 3).map((a) => `${a.skin}/${a.slot}/${a.attachment}`).join(", ")}${sequence.length > 3 ? ", …" : ""}`, name);
|
|
9398
|
+
if (tl.drawOrder.length > 0) refusals.note("draw-order-timeline", `${tl.drawOrder.length} draw-order key(s)`, name);
|
|
9399
|
+
const lastBy = (targets) => new Map(targets.map((t) => [t.name, t]));
|
|
9400
|
+
const bones = [];
|
|
9401
|
+
for (const target of lastBy(tl.bones).values()) {
|
|
9402
|
+
const bone2 = boneIndex2.get(target.name);
|
|
9403
|
+
if (bone2 === void 0) continue;
|
|
9404
|
+
for (const t of target.timelines) {
|
|
9405
|
+
if (t.kind === "inherit") {
|
|
9406
|
+
refusals.note("inherit-timeline", `bone "${target.name}" keys its inherit mode`, name);
|
|
9407
|
+
continue;
|
|
9408
|
+
}
|
|
9409
|
+
const spec = BONE_KINDS[t.kind];
|
|
9410
|
+
bones.push({ bone: bone2, curve: compileCurve(t.keys, spec.length), cursor: cursors++, fields: Int32Array.from(spec.map((s) => s[0])), multiply: Uint8Array.from(spec.map((s) => s[1])) });
|
|
9411
|
+
}
|
|
9412
|
+
}
|
|
9413
|
+
const slotTimelines = [];
|
|
9414
|
+
const keyedSlots = [...lastBy(tl.slots).values()].flatMap((target) => {
|
|
9415
|
+
const si = slotIndex.get(target.name);
|
|
9416
|
+
return si === void 0 ? [] : [{ si, target }];
|
|
9417
|
+
}).sort((a, b) => a.si - b.si);
|
|
9418
|
+
for (const { si, target } of keyedSlots) {
|
|
9419
|
+
if (!slots[si].live) continue;
|
|
9420
|
+
for (const t of target.timelines) {
|
|
9421
|
+
if (t.kind === "attachment") {
|
|
9422
|
+
const ids = Int32Array.from(t.keys.map((k) => resolve(si, k.name ?? null)));
|
|
9423
|
+
slotTimelines.push({ slot: si, curve: compileCurve(t.keys, 0), cursor: cursors++, attachments: ids, channels: null });
|
|
9424
|
+
} else if (t.kind === "rgba2" || t.kind === "rgb2") {
|
|
9425
|
+
refusals.note("dark-colour-timeline", `slot "${target.name}" keys an ${t.kind} timeline`, name);
|
|
9426
|
+
} else {
|
|
9427
|
+
const channels = COLOUR_CHANNELS[t.kind];
|
|
9428
|
+
slotTimelines.push({ slot: si, curve: compileCurve(t.keys, channels.length), cursor: cursors++, attachments: null, channels: Int32Array.from(channels) });
|
|
9429
|
+
}
|
|
9430
|
+
}
|
|
9431
|
+
}
|
|
9432
|
+
const constraintTimelines = constraints.map(() => null);
|
|
9433
|
+
for (const c of constraints) {
|
|
9434
|
+
if (c.kind === "ik") {
|
|
9435
|
+
const keyed = anim.constraints.ik.find((x) => x.name === c.name);
|
|
9436
|
+
if (keyed === void 0) continue;
|
|
9437
|
+
constraintTimelines[c.index] = { constraint: c.index, curve: compileCurve(keyed.keys, 2), cursor: cursors++, flags: Uint8Array.from(keyed.keys.map((k) => ikFlags(k.flags.bendPositive, k.flags.compress, k.flags.stretch))) };
|
|
9438
|
+
} else {
|
|
9439
|
+
const keyed = anim.constraints.transform.find((x) => x.name === c.name);
|
|
9440
|
+
if (keyed === void 0) continue;
|
|
9441
|
+
constraintTimelines[c.index] = { constraint: c.index, curve: compileCurve(keyed.keys, TRANSFORM_PROPERTIES.length), cursor: cursors++, flags: null };
|
|
9442
|
+
}
|
|
9443
|
+
}
|
|
9444
|
+
return { name, duration: tl.duration, bones, slots: slotTimelines, constraints: constraintTimelines, cursors, events: tl.events.length, eventKeys: Object.freeze(ignoreEvents ? [] : tl.events.map((key) => Object.freeze({ name: key.name, time: key.time, int: key.int, float: key.float, string: key.string, volume: key.volume ?? 0, balance: key.balance ?? 0 }))) };
|
|
9445
|
+
}
|
|
9446
|
+
const DEG = RUNTIME_DEG;
|
|
9447
|
+
const TWO_PI = 2 * RUNTIME_PI;
|
|
9448
|
+
const P_ROTATE = 0;
|
|
9449
|
+
const P_X = 1;
|
|
9450
|
+
const P_Y = 2;
|
|
9451
|
+
const P_SCALE_X = 3;
|
|
9452
|
+
const P_SCALE_Y = 4;
|
|
9453
|
+
function wrap180(r) {
|
|
9454
|
+
return r > 180 ? r - 360 : r <= -180 ? r + 360 : r;
|
|
9455
|
+
}
|
|
9456
|
+
function volumeScaleY(scaleY, s) {
|
|
9457
|
+
return scaleY / (s < 0.7 ? 0.25 + s * 0.642857 : s);
|
|
9458
|
+
}
|
|
9459
|
+
const clamp01 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
|
|
9460
|
+
class RuntimePose {
|
|
9461
|
+
constructor(model) {
|
|
9462
|
+
this.trackTime = 0;
|
|
9463
|
+
this.time = 0;
|
|
9464
|
+
this.fired = [];
|
|
9465
|
+
this.eventTime = -1;
|
|
9466
|
+
this.current = null;
|
|
9467
|
+
this.w = new Float64Array(WORLDS);
|
|
9468
|
+
this.model = model;
|
|
9469
|
+
const nb = model.boneNames.length;
|
|
9470
|
+
const ns = model.slots.length;
|
|
9471
|
+
const nc = model.constraints.length;
|
|
9472
|
+
this.locals = new Float64Array(nb * LOCALS);
|
|
9473
|
+
this.world = new Float64Array(nb * WORLDS);
|
|
9474
|
+
this.light = new Float64Array(ns * 4);
|
|
9475
|
+
this.shown = new Int32Array(ns);
|
|
9476
|
+
this.vertices = new Float64Array(model.vertexCount);
|
|
9477
|
+
this.cursors = new Int32Array(Math.max(1, model.cursors)).fill(-1);
|
|
9478
|
+
this.ikMix = new Float64Array(nc);
|
|
9479
|
+
this.ikSoftness = new Float64Array(nc);
|
|
9480
|
+
this.ikFlags = new Uint8Array(nc);
|
|
9481
|
+
this.mixes = new Float64Array(nc * 6);
|
|
9482
|
+
this.changed = new Int32Array(model.scratch);
|
|
9483
|
+
this.inWorld = new Int32Array(model.scratch);
|
|
9484
|
+
this.changedMark = new Uint8Array(nb);
|
|
9485
|
+
this.inWorldMark = new Uint8Array(nb);
|
|
9486
|
+
this.below = new Uint8Array(nb);
|
|
9487
|
+
this.visit = new Uint8Array(nb);
|
|
9488
|
+
}
|
|
9489
|
+
/** Borrowed immutable event records, in delivery order, until the next apply/step. */
|
|
9490
|
+
get events() {
|
|
9491
|
+
return this.fired;
|
|
9492
|
+
}
|
|
9493
|
+
/** The animation this pose plays (a covered one: `model.animation` throws a `RuntimeRefusal` for a refused one), from track time 0. */
|
|
9494
|
+
setAnimation(name) {
|
|
9495
|
+
this.current = this.model.animation(name);
|
|
9496
|
+
this.cursors.fill(-1);
|
|
9497
|
+
this.trackTime = 0;
|
|
9498
|
+
this.time = 0;
|
|
9499
|
+
this.eventTime = -1;
|
|
9500
|
+
this.fired.length = 0;
|
|
9501
|
+
}
|
|
9502
|
+
/**
|
|
9503
|
+
* Restore one animation's two clocks without delivering keys already passed.
|
|
9504
|
+
* A skin/view replacement uses the old pose's trackTime and wrapped time.
|
|
9505
|
+
* Unlike setAnimation + apply, restoration never replays the initial interval.
|
|
9506
|
+
*/
|
|
9507
|
+
restore(name, trackTime, animationTime) {
|
|
9508
|
+
if (!Number.isFinite(trackTime)) throw new RuntimeInputError(`restore: track time ${trackTime} is not finite`);
|
|
9509
|
+
if (!Number.isFinite(animationTime)) throw new RuntimeInputError(`restore: animation time ${animationTime} is not finite`);
|
|
9510
|
+
this.setAnimation(name);
|
|
9511
|
+
this.trackTime = trackTime;
|
|
9512
|
+
if (!this.model.restoreEventsFromStart) this.eventTime = animationTime;
|
|
9513
|
+
this.apply(animationTime);
|
|
9514
|
+
}
|
|
9515
|
+
/** The setup pose: every constraint applied with its own values, every slot as it is set up. */
|
|
9516
|
+
setupPose() {
|
|
9517
|
+
this.current = null;
|
|
9518
|
+
this.time = 0;
|
|
9519
|
+
this.eventTime = -1;
|
|
9520
|
+
this.fired.length = 0;
|
|
9521
|
+
this.locals.set(this.model.setup);
|
|
9522
|
+
this.hierarchy();
|
|
9523
|
+
this.constraintValues(null, 0);
|
|
9524
|
+
this.solveConstraints();
|
|
9525
|
+
this.slotValues(null, 0);
|
|
9526
|
+
}
|
|
9527
|
+
/** The animation posed at its time `t`, from the setup pose (the header). */
|
|
9528
|
+
apply(t) {
|
|
9529
|
+
const anim = this.current;
|
|
9530
|
+
if (anim === null) throw new RuntimeInputError("apply: no animation is set (setAnimation first)");
|
|
9531
|
+
if (!Number.isFinite(t)) throw new RuntimeInputError(`apply: the time ${t} is not finite`);
|
|
9532
|
+
this.collectEvents(anim, t);
|
|
9533
|
+
this.time = t;
|
|
9534
|
+
this.boneTimelines(anim, t);
|
|
9535
|
+
this.hierarchy();
|
|
9536
|
+
this.constraintValues(anim, t);
|
|
9537
|
+
this.solveConstraints();
|
|
9538
|
+
this.slotValues(anim, t);
|
|
9539
|
+
}
|
|
9540
|
+
/** One step of a looping track: the track time moved by `dt`, the animation posed at it wrapped by the duration (`loopedTime` in src/core/raw.ts). */
|
|
9541
|
+
step(dt) {
|
|
9542
|
+
const anim = this.current;
|
|
9543
|
+
if (anim === null) throw new RuntimeInputError("step: no animation is set (setAnimation first)");
|
|
9544
|
+
this.trackTime += dt;
|
|
9545
|
+
this.apply(anim.duration === 0 ? 0 : this.trackTime % anim.duration);
|
|
9546
|
+
}
|
|
9547
|
+
/** The oracle interval (last,t], tail then head on a wrap; never copies a payload. */
|
|
9548
|
+
collectEvents(anim, t) {
|
|
9549
|
+
const last = this.eventTime;
|
|
9550
|
+
const wrapped = t < last;
|
|
9551
|
+
const keys = anim.eventKeys;
|
|
9552
|
+
let count = 0;
|
|
9553
|
+
if (wrapped) {
|
|
9554
|
+
for (let i = 0; i < keys.length; i++) if (keys[i].time > last) this.fired[count++] = keys[i];
|
|
9555
|
+
}
|
|
9556
|
+
for (let i = 0; i < keys.length; i++) {
|
|
9557
|
+
const key = keys[i];
|
|
9558
|
+
if (key.time <= t && (wrapped ? !this.model.eventWrapOffByOne || key.time > 0 : key.time > last)) this.fired[count++] = key;
|
|
9559
|
+
}
|
|
9560
|
+
this.fired.length = count;
|
|
9561
|
+
this.eventTime = t;
|
|
9562
|
+
}
|
|
9563
|
+
// ---------------------------------------------------------------------------
|
|
9564
|
+
// the bones
|
|
9565
|
+
// ---------------------------------------------------------------------------
|
|
9566
|
+
/** The setup locals with the bone timelines' values at `t` (`posedBones`): added, or multiplied for a scale; a channel before its timeline's first key at setup plus 0, or times 1. */
|
|
9567
|
+
boneTimelines(anim, t) {
|
|
9568
|
+
const L = this.locals;
|
|
9569
|
+
const S = this.model.setup;
|
|
9570
|
+
L.set(S);
|
|
9571
|
+
const timelines = anim.bones;
|
|
9572
|
+
for (let k = 0; k < timelines.length; k++) {
|
|
9573
|
+
const tl = timelines[k];
|
|
9574
|
+
const i = keyAt(tl.curve, this.cursors, tl.cursor, t);
|
|
9575
|
+
const o = tl.bone * LOCALS;
|
|
9576
|
+
const fields = tl.fields;
|
|
9577
|
+
for (let c = 0; c < fields.length; c++) {
|
|
9578
|
+
const at = o + fields[c];
|
|
9579
|
+
if (tl.multiply[c] === 1) L[at] = S[at] * (i < 0 ? 1 : channelValue(tl.curve, i, c, t));
|
|
9580
|
+
else L[at] = S[at] + (i < 0 ? 0 : channelValue(tl.curve, i, c, t));
|
|
9581
|
+
}
|
|
9582
|
+
}
|
|
9583
|
+
}
|
|
9584
|
+
/** Every bone's world transform from its locals, parents first (`worldTransforms`): an inactive bone as zeros. */
|
|
9585
|
+
hierarchy() {
|
|
9586
|
+
const nb = this.model.boneNames.length;
|
|
9587
|
+
for (let i = 0; i < nb; i++) this.poseBone(i, true);
|
|
9588
|
+
}
|
|
9589
|
+
/**
|
|
9590
|
+
* One bone's world transform from its locals under its parent's current
|
|
9591
|
+
* world (`worldTransforms` and the update order's `poseBone`). An inactive
|
|
9592
|
+
* bone is zeroed by the hierarchy (`fresh`) and left as it is by a repose.
|
|
9593
|
+
*/
|
|
9594
|
+
poseBone(i, fresh) {
|
|
9595
|
+
const m = this.model;
|
|
9596
|
+
const W = this.world;
|
|
9597
|
+
const o = i * WORLDS;
|
|
9598
|
+
if (m.active[i] === 0) {
|
|
9599
|
+
if (fresh) {
|
|
9600
|
+
W[o + W_A] = 0;
|
|
9601
|
+
W[o + W_B] = 0;
|
|
9602
|
+
W[o + W_C] = 0;
|
|
9603
|
+
W[o + W_D] = 0;
|
|
9604
|
+
W[o + W_X] = 0;
|
|
9605
|
+
W[o + W_Y] = 0;
|
|
9606
|
+
}
|
|
9607
|
+
return;
|
|
9608
|
+
}
|
|
9609
|
+
const L = this.locals;
|
|
9610
|
+
const l = i * LOCALS;
|
|
9611
|
+
const x = L[l + L_X];
|
|
9612
|
+
const y = L[l + L_Y];
|
|
9613
|
+
const rotation = L[l + L_ROTATION];
|
|
9614
|
+
const scaleX = L[l + L_SCALE_X];
|
|
9615
|
+
const scaleY = L[l + L_SCALE_Y];
|
|
9616
|
+
const shearX = L[l + L_SHEAR_X];
|
|
9617
|
+
const shearY = L[l + L_SHEAR_Y];
|
|
9618
|
+
const parent = m.parents[i];
|
|
9619
|
+
if (parent < 0) {
|
|
9620
|
+
const xAngle = (rotation + shearX) * RAD;
|
|
9621
|
+
const yAngle = (rotation + 90 + shearY) * RAD;
|
|
9622
|
+
W[o + W_A] = Math.cos(xAngle) * scaleX;
|
|
9623
|
+
W[o + W_B] = Math.cos(yAngle) * scaleY;
|
|
9624
|
+
W[o + W_C] = Math.sin(xAngle) * scaleX;
|
|
9625
|
+
W[o + W_D] = Math.sin(yAngle) * scaleY;
|
|
9626
|
+
W[o + W_X] = x;
|
|
9627
|
+
W[o + W_Y] = y;
|
|
9628
|
+
return;
|
|
9629
|
+
}
|
|
9630
|
+
const q = parent * WORLDS;
|
|
9631
|
+
const pa = W[q + W_A];
|
|
9632
|
+
const pb = W[q + W_B];
|
|
9633
|
+
const pc = W[q + W_C];
|
|
9634
|
+
const pd = W[q + W_D];
|
|
9635
|
+
const px = W[q + W_X];
|
|
9636
|
+
const py = W[q + W_Y];
|
|
9637
|
+
switch (m.modes[i]) {
|
|
9638
|
+
case MODE_NORMAL: {
|
|
9639
|
+
const xAngle = (rotation + shearX) * RAD;
|
|
9640
|
+
const yAngle = (rotation + 90 + shearY) * RAD;
|
|
9641
|
+
const la = Math.cos(xAngle) * scaleX;
|
|
9642
|
+
const lb = Math.cos(yAngle) * scaleY;
|
|
9643
|
+
const lc = Math.sin(xAngle) * scaleX;
|
|
9644
|
+
const ld = Math.sin(yAngle) * scaleY;
|
|
9645
|
+
W[o + W_A] = pa * la + pb * lc;
|
|
9646
|
+
W[o + W_B] = pa * lb + pb * ld;
|
|
9647
|
+
W[o + W_C] = pc * la + pd * lc;
|
|
9648
|
+
W[o + W_D] = pc * lb + pd * ld;
|
|
9649
|
+
break;
|
|
9650
|
+
}
|
|
9651
|
+
case MODE_ONLY_TRANSLATION: {
|
|
9652
|
+
const xAngle = (rotation + shearX) * RAD;
|
|
9653
|
+
const yAngle = (rotation + 90 + shearY) * RAD;
|
|
9654
|
+
W[o + W_A] = Math.cos(xAngle) * scaleX;
|
|
9655
|
+
W[o + W_B] = Math.cos(yAngle) * scaleY;
|
|
9656
|
+
W[o + W_C] = Math.sin(xAngle) * scaleX;
|
|
9657
|
+
W[o + W_D] = Math.sin(yAngle) * scaleY;
|
|
9658
|
+
break;
|
|
9659
|
+
}
|
|
9660
|
+
case MODE_NO_ROTATION_OR_REFLECTION: {
|
|
9661
|
+
const xLengthSq = pa * pa + pc * pc;
|
|
9662
|
+
let c0;
|
|
9663
|
+
let c1;
|
|
9664
|
+
let c2;
|
|
9665
|
+
let c3;
|
|
9666
|
+
let r;
|
|
9667
|
+
if (xLengthSq > COLLAPSED_X_AXIS_SQ) {
|
|
9668
|
+
const k = Math.abs(pa * pd - pb * pc) / xLengthSq;
|
|
9669
|
+
c0 = pa;
|
|
9670
|
+
c1 = -pc * k;
|
|
9671
|
+
c2 = pc;
|
|
9672
|
+
c3 = pa * k;
|
|
9673
|
+
r = rotation - Math.atan2(pc, pa) * DEG;
|
|
9674
|
+
} else {
|
|
9675
|
+
c0 = 0;
|
|
9676
|
+
c1 = -pb;
|
|
9677
|
+
c2 = 0;
|
|
9678
|
+
c3 = pd;
|
|
9679
|
+
r = rotation - 90 + Math.atan2(pd, pb) * DEG;
|
|
9680
|
+
}
|
|
9681
|
+
const xAngle = (r + shearX) * RAD;
|
|
9682
|
+
const yAngle = (r + shearY + 90) * RAD;
|
|
9683
|
+
const la = Math.cos(xAngle) * scaleX;
|
|
9684
|
+
const lb = Math.cos(yAngle) * scaleY;
|
|
9685
|
+
const lc = Math.sin(xAngle) * scaleX;
|
|
9686
|
+
const ld = Math.sin(yAngle) * scaleY;
|
|
9687
|
+
W[o + W_A] = c0 * la + c1 * lc;
|
|
9688
|
+
W[o + W_B] = c0 * lb + c1 * ld;
|
|
9689
|
+
W[o + W_C] = c2 * la + c3 * lc;
|
|
9690
|
+
W[o + W_D] = c2 * lb + c3 * ld;
|
|
9691
|
+
break;
|
|
9692
|
+
}
|
|
9693
|
+
default: {
|
|
9694
|
+
const rr = rotation * RAD;
|
|
9695
|
+
const cos = Math.cos(rr);
|
|
9696
|
+
const sin = Math.sin(rr);
|
|
9697
|
+
const ux0 = pa * cos + pb * sin;
|
|
9698
|
+
const uy0 = pc * cos + pd * sin;
|
|
9699
|
+
const inverse = 1 / Math.sqrt(ux0 * ux0 + uy0 * uy0);
|
|
9700
|
+
const ux = ux0 * inverse;
|
|
9701
|
+
const uy = uy0 * inverse;
|
|
9702
|
+
const flip = m.modes[i] === MODE_NO_SCALE && pa * pd - pb * pc < 0 ? -1 : 1;
|
|
9703
|
+
const t0 = ux;
|
|
9704
|
+
const t1 = -uy * flip;
|
|
9705
|
+
const t2 = uy;
|
|
9706
|
+
const t3 = ux * flip;
|
|
9707
|
+
const xAngle = (0 + shearX) * RAD;
|
|
9708
|
+
const yAngle = (0 + 90 + shearY) * RAD;
|
|
9709
|
+
const la = Math.cos(xAngle) * scaleX;
|
|
9710
|
+
const lb = Math.cos(yAngle) * scaleY;
|
|
9711
|
+
const lc = Math.sin(xAngle) * scaleX;
|
|
9712
|
+
const ld = Math.sin(yAngle) * scaleY;
|
|
9713
|
+
W[o + W_A] = t0 * la + t1 * lc;
|
|
9714
|
+
W[o + W_B] = t0 * lb + t1 * ld;
|
|
9715
|
+
W[o + W_C] = t2 * la + t3 * lc;
|
|
9716
|
+
W[o + W_D] = t2 * lb + t3 * ld;
|
|
9717
|
+
}
|
|
9718
|
+
}
|
|
9719
|
+
W[o + W_X] = pa * x + pb * y + px;
|
|
9720
|
+
W[o + W_Y] = pc * x + pd * y + py;
|
|
9721
|
+
}
|
|
9722
|
+
// ---------------------------------------------------------------------------
|
|
9723
|
+
// the constraints
|
|
9724
|
+
// ---------------------------------------------------------------------------
|
|
9725
|
+
/** Each constraint's values at `t` (`posedRecords`): its own before its timeline's first key, else the keyed values through the setup blend and the key's flags. */
|
|
9726
|
+
constraintValues(anim, t) {
|
|
9727
|
+
const cs = this.model.constraints;
|
|
9728
|
+
for (let k = 0; k < cs.length; k++) {
|
|
9729
|
+
const c = cs[k];
|
|
9730
|
+
const tl = anim === null ? null : anim.constraints[k];
|
|
9731
|
+
const i = tl === null ? -1 : keyAt(tl.curve, this.cursors, tl.cursor, t);
|
|
9732
|
+
if (c.kind === "ik") {
|
|
9733
|
+
if (tl === null || i < 0) {
|
|
9734
|
+
this.ikMix[k] = c.mix;
|
|
9735
|
+
this.ikSoftness[k] = c.softness;
|
|
9736
|
+
this.ikFlags[k] = c.flags;
|
|
9737
|
+
} else {
|
|
9738
|
+
this.ikMix[k] = c.mix + (channelValue(tl.curve, i, 0, t) - c.mix) * 1;
|
|
9739
|
+
this.ikSoftness[k] = c.softness + (channelValue(tl.curve, i, 1, t) - c.softness) * 1;
|
|
9740
|
+
this.ikFlags[k] = tl.flags[i];
|
|
9741
|
+
}
|
|
9742
|
+
} else {
|
|
9743
|
+
const o = k * 6;
|
|
9744
|
+
for (let p = 0; p < 6; p++) this.mixes[o + p] = tl === null || i < 0 ? c.mixes[p] : c.mixes[p] + (channelValue(tl.curve, i, p, t) - c.mixes[p]) * 1;
|
|
9745
|
+
}
|
|
9746
|
+
}
|
|
9747
|
+
}
|
|
9748
|
+
/** Every applied constraint in the document's order, each followed by the update order's repose (`applyConstraints`). */
|
|
9749
|
+
solveConstraints() {
|
|
9750
|
+
const cs = this.model.constraints;
|
|
9751
|
+
for (let k = 0; k < cs.length; k++) {
|
|
9752
|
+
const c = cs[k];
|
|
9753
|
+
if (!c.applied) continue;
|
|
9754
|
+
if (c.kind === "ik") this.solveIk(c, k);
|
|
9755
|
+
else this.solveTransform(c, k);
|
|
9756
|
+
}
|
|
9757
|
+
}
|
|
9758
|
+
solveIk(c, k) {
|
|
9759
|
+
const mix = this.ikMix[k];
|
|
9760
|
+
if (mix === 0) return;
|
|
9761
|
+
const flags = this.ikFlags[k];
|
|
9762
|
+
if (c.bones.length === 1) {
|
|
9763
|
+
this.solveOne(c.bones[0], c.target, mix, (flags & IK_COMPRESS) !== 0, (flags & IK_STRETCH) !== 0, c.scaleY);
|
|
9764
|
+
this.changed[0] = c.bones[0];
|
|
9765
|
+
this.repose(1, 0);
|
|
9766
|
+
} else if (this.solveTwo(c, mix, this.ikSoftness[k], flags)) {
|
|
9767
|
+
this.changed[0] = c.bones[0];
|
|
9768
|
+
this.repose(1, 0);
|
|
9769
|
+
}
|
|
9770
|
+
}
|
|
9771
|
+
/** A one-bone ik (`solveOne`). */
|
|
9772
|
+
solveOne(bi, ti, mix, compress, stretch, scaleYMode) {
|
|
9773
|
+
const m = this.model;
|
|
9774
|
+
const L = this.locals;
|
|
9775
|
+
const W = this.world;
|
|
9776
|
+
const parent = m.parents[bi];
|
|
9777
|
+
const q = parent * WORLDS;
|
|
9778
|
+
let pa = 1;
|
|
9779
|
+
let pb = 0;
|
|
9780
|
+
let pc = 0;
|
|
9781
|
+
let pd = 1;
|
|
9782
|
+
let pwx = 0;
|
|
9783
|
+
let pwy = 0;
|
|
9784
|
+
if (parent >= 0) {
|
|
9785
|
+
pa = W[q + W_A];
|
|
9786
|
+
pb = W[q + W_B];
|
|
9787
|
+
pc = W[q + W_C];
|
|
9788
|
+
pd = W[q + W_D];
|
|
9789
|
+
pwx = W[q + W_X];
|
|
9790
|
+
pwy = W[q + W_Y];
|
|
9791
|
+
}
|
|
9792
|
+
const bw = bi * WORLDS;
|
|
9793
|
+
const tw = ti * WORLDS;
|
|
9794
|
+
const l = bi * LOCALS;
|
|
9795
|
+
const mode = m.modes[bi];
|
|
9796
|
+
let turn = 0;
|
|
9797
|
+
let lx;
|
|
9798
|
+
let ly;
|
|
9799
|
+
if (mode === MODE_NO_ROTATION_OR_REFLECTION) {
|
|
9800
|
+
const k = Math.abs(pa * pd - pb * pc) / Math.max(1e-5, pa * pa + pc * pc);
|
|
9801
|
+
pb = -pc * k;
|
|
9802
|
+
pd = pa * k;
|
|
9803
|
+
turn = Math.atan2(pc, pa) * DEG;
|
|
9804
|
+
const det = pa * pd - pb * pc;
|
|
9805
|
+
const x = W[tw + W_X] - pwx;
|
|
9806
|
+
const y = W[tw + W_Y] - pwy;
|
|
9807
|
+
if (Math.abs(det) <= 1e-5) {
|
|
9808
|
+
lx = 0;
|
|
9809
|
+
ly = 0;
|
|
9810
|
+
} else {
|
|
9811
|
+
lx = (x * pd - y * pb) / det - L[l + L_X];
|
|
9812
|
+
ly = (y * pa - x * pc) / det - L[l + L_Y];
|
|
9813
|
+
}
|
|
9814
|
+
} else if (mode === MODE_ONLY_TRANSLATION) {
|
|
9815
|
+
lx = W[tw + W_X] - W[bw + W_X];
|
|
9816
|
+
ly = W[tw + W_Y] - W[bw + W_Y];
|
|
9817
|
+
} else {
|
|
9818
|
+
const det = pa * pd - pb * pc;
|
|
9819
|
+
const x = W[tw + W_X] - pwx;
|
|
9820
|
+
const y = W[tw + W_Y] - pwy;
|
|
9821
|
+
if (Math.abs(det) <= 1e-5) {
|
|
9822
|
+
lx = 0;
|
|
9823
|
+
ly = 0;
|
|
9824
|
+
} else {
|
|
9825
|
+
lx = (x * pd - y * pb) / det - L[l + L_X];
|
|
9826
|
+
ly = (y * pa - x * pc) / det - L[l + L_Y];
|
|
9827
|
+
}
|
|
9828
|
+
}
|
|
9829
|
+
const rotation = L[l + L_ROTATION];
|
|
9830
|
+
const scaleX = L[l + L_SCALE_X];
|
|
9831
|
+
let r = -L[l + L_SHEAR_X] - rotation + turn + Math.atan2(ly, lx) * DEG;
|
|
9832
|
+
if (scaleX < 0) r += 180;
|
|
9833
|
+
L[l + L_ROTATION] = rotation + wrap180(r) * mix;
|
|
9834
|
+
const len = m.lengths[bi] * scaleX;
|
|
9835
|
+
if ((compress || stretch) && len > 1e-5) {
|
|
9836
|
+
const wx = W[tw + W_X] - W[bw + W_X];
|
|
9837
|
+
const wy = W[tw + W_Y] - W[bw + W_Y];
|
|
9838
|
+
const d = mode === MODE_NO_SCALE || mode === MODE_NO_SCALE_OR_REFLECTION ? Math.sqrt(wx * wx + wy * wy) : Math.sqrt(lx * lx + ly * ly);
|
|
9839
|
+
if (compress && d < len || stretch && d > len) {
|
|
9840
|
+
const s = (d / len - 1) * mix + 1;
|
|
9841
|
+
L[l + L_SCALE_X] = scaleX * s;
|
|
9842
|
+
if (scaleYMode === SCALE_Y_UNIFORM) L[l + L_SCALE_Y] = L[l + L_SCALE_Y] * s;
|
|
9843
|
+
else if (scaleYMode === SCALE_Y_VOLUME) L[l + L_SCALE_Y] = volumeScaleY(L[l + L_SCALE_Y], s);
|
|
9844
|
+
}
|
|
9845
|
+
}
|
|
9846
|
+
}
|
|
9847
|
+
/** A two-bone ik (`solveTwo`): whether it changed the parent. */
|
|
9848
|
+
solveTwo(c, mix, softness, flags) {
|
|
9849
|
+
const m = this.model;
|
|
9850
|
+
const L = this.locals;
|
|
9851
|
+
const W = this.world;
|
|
9852
|
+
const pi = c.bones[0];
|
|
9853
|
+
const ci = c.bones[1];
|
|
9854
|
+
if (m.modes[pi] !== MODE_NORMAL || m.modes[ci] !== MODE_NORMAL) return false;
|
|
9855
|
+
const stretch = (flags & IK_STRETCH) !== 0;
|
|
9856
|
+
const g = m.parents[pi] * WORLDS;
|
|
9857
|
+
const pw = pi * WORLDS;
|
|
9858
|
+
const tw = c.target * WORLDS;
|
|
9859
|
+
const pl = pi * LOCALS;
|
|
9860
|
+
const cl = ci * LOCALS;
|
|
9861
|
+
const ga = W[g + W_A];
|
|
9862
|
+
const gb = W[g + W_B];
|
|
9863
|
+
const gc = W[g + W_C];
|
|
9864
|
+
const gd = W[g + W_D];
|
|
9865
|
+
const gx = W[g + W_X];
|
|
9866
|
+
const gy = W[g + W_Y];
|
|
9867
|
+
const gdet = ga * gd - gb * gc;
|
|
9868
|
+
const gid = Math.abs(gdet) <= 1e-5 ? 0 : 1 / gdet;
|
|
9869
|
+
const px = L[pl + L_X];
|
|
9870
|
+
const py = L[pl + L_Y];
|
|
9871
|
+
const psx = L[pl + L_SCALE_X];
|
|
9872
|
+
const psy = L[pl + L_SCALE_Y];
|
|
9873
|
+
const csx = L[cl + L_SCALE_X];
|
|
9874
|
+
let sx = psx;
|
|
9875
|
+
let sy = psy;
|
|
9876
|
+
const cx = L[cl + L_X];
|
|
9877
|
+
let cy = L[cl + L_Y];
|
|
9878
|
+
const uniform = Math.abs(Math.abs(psx) - Math.abs(psy)) <= 1e-5;
|
|
9879
|
+
if (!uniform || stretch) cy = 0;
|
|
9880
|
+
let x = W[pw + W_A] * cx + W[pw + W_B] * cy + W[pw + W_X] - gx;
|
|
9881
|
+
let y = W[pw + W_C] * cx + W[pw + W_D] * cy + W[pw + W_Y] - gy;
|
|
9882
|
+
const ox = (x * gd - y * gb) * gid;
|
|
9883
|
+
const oy = (y * ga - x * gc) * gid;
|
|
9884
|
+
const dx = ox - px;
|
|
9885
|
+
const dy = oy - py;
|
|
9886
|
+
const l1 = Math.sqrt(dx * dx + dy * dy);
|
|
9887
|
+
let l2 = m.lengths[ci] * Math.abs(csx);
|
|
9888
|
+
if (l1 < 1e-5) {
|
|
9889
|
+
this.solveOne(pi, c.target, mix, false, stretch, 0);
|
|
9890
|
+
L[cl + L_ROTATION] = 0;
|
|
9891
|
+
L[cl + L_Y] = cy;
|
|
9892
|
+
return true;
|
|
9893
|
+
}
|
|
9894
|
+
x = W[tw + W_X] - gx;
|
|
9895
|
+
y = W[tw + W_Y] - gy;
|
|
9896
|
+
let tx = (x * gd - y * gb) * gid;
|
|
9897
|
+
let ty = (y * ga - x * gc) * gid;
|
|
9898
|
+
tx -= px;
|
|
9899
|
+
ty -= py;
|
|
9900
|
+
let d2 = tx * tx + ty * ty;
|
|
9901
|
+
const bend = (flags & IK_BEND_POSITIVE) !== 0 ? 1 : -1;
|
|
9902
|
+
if (softness !== 0) {
|
|
9903
|
+
const soft = softness * (Math.abs(psx) * (Math.abs(csx) + 1) * 0.5);
|
|
9904
|
+
const d = Math.sqrt(d2);
|
|
9905
|
+
const sd = d - l1 - l2 * Math.abs(psx) + soft;
|
|
9906
|
+
if (sd > 0) {
|
|
9907
|
+
const q = Math.min(1, sd / (soft * 2)) - 1;
|
|
9908
|
+
const pull = (sd - soft * (1 - q * q)) / d;
|
|
9909
|
+
tx -= pull * tx;
|
|
9910
|
+
ty -= pull * ty;
|
|
9911
|
+
d2 = tx * tx + ty * ty;
|
|
9912
|
+
}
|
|
9913
|
+
}
|
|
9914
|
+
let a1;
|
|
9915
|
+
let a2;
|
|
9916
|
+
if (uniform) {
|
|
9917
|
+
l2 *= Math.abs(psx);
|
|
9918
|
+
let cos = (d2 - l1 * l1 - l2 * l2) / (2 * l1 * l2);
|
|
9919
|
+
if (cos < -1) {
|
|
9920
|
+
cos = -1;
|
|
9921
|
+
a2 = Math.PI * bend;
|
|
9922
|
+
} else if (cos > 1) {
|
|
9923
|
+
cos = 1;
|
|
9924
|
+
a2 = 0;
|
|
9925
|
+
if (stretch) {
|
|
9926
|
+
const f = (Math.sqrt(d2) / (l1 + l2) - 1) * mix + 1;
|
|
9927
|
+
sx *= f;
|
|
9928
|
+
if (c.scaleY === SCALE_Y_UNIFORM) sy *= f;
|
|
9929
|
+
else if (c.scaleY === SCALE_Y_VOLUME) sy = volumeScaleY(sy, f);
|
|
9930
|
+
}
|
|
9931
|
+
} else a2 = Math.acos(cos) * bend;
|
|
9932
|
+
const along = l1 + l2 * cos;
|
|
9933
|
+
const across = l2 * Math.sin(a2);
|
|
9934
|
+
a1 = Math.atan2(ty * along - tx * across, tx * along + ty * across);
|
|
9935
|
+
} else {
|
|
9936
|
+
const ea = Math.abs(psx) * l2;
|
|
9937
|
+
const eb = Math.abs(psy) * l2;
|
|
9938
|
+
const aa = ea * ea;
|
|
9939
|
+
const bb = eb * eb;
|
|
9940
|
+
const qa = bb - aa;
|
|
9941
|
+
const qb = -2 * bb * l1;
|
|
9942
|
+
const qc = bb * l1 * l1 + aa * d2 - aa * bb;
|
|
9943
|
+
const disc = qb * qb - 4 * qa * qc;
|
|
9944
|
+
let solved = false;
|
|
9945
|
+
a1 = 0;
|
|
9946
|
+
a2 = 0;
|
|
9947
|
+
if (disc >= 0) {
|
|
9948
|
+
let q = Math.sqrt(disc);
|
|
9949
|
+
if (qb < 0) q = -q;
|
|
9950
|
+
q = -(qb + q) * 0.5;
|
|
9951
|
+
const r0 = q / qa;
|
|
9952
|
+
const r1 = qc / q;
|
|
9953
|
+
const X = Math.abs(r0) < Math.abs(r1) ? r0 : r1;
|
|
9954
|
+
const y2 = d2 - X * X;
|
|
9955
|
+
if (y2 >= 0) {
|
|
9956
|
+
const Y = Math.sqrt(y2) * bend;
|
|
9957
|
+
a1 = Math.atan2(ty, tx) - Math.atan2(Y, X);
|
|
9958
|
+
a2 = Math.atan2(Y / Math.abs(psy), (X - l1) / Math.abs(psx));
|
|
9959
|
+
solved = true;
|
|
9960
|
+
}
|
|
9961
|
+
}
|
|
9962
|
+
if (!solved) {
|
|
9963
|
+
let nearAngle = RUNTIME_PI;
|
|
9964
|
+
let nearX = l1 - ea;
|
|
9965
|
+
let nearY = 0;
|
|
9966
|
+
let nearDist = (l1 - ea) * (l1 - ea);
|
|
9967
|
+
let farAngle = 0;
|
|
9968
|
+
let farX = l1 + ea;
|
|
9969
|
+
let farY = 0;
|
|
9970
|
+
let farDist = (l1 + ea) * (l1 + ea);
|
|
9971
|
+
const vertex = -ea * l1 / (aa - bb);
|
|
9972
|
+
if (vertex >= -1 && vertex <= 1) {
|
|
9973
|
+
const angle = Math.acos(vertex);
|
|
9974
|
+
const vx = ea * Math.cos(angle) + l1;
|
|
9975
|
+
const vy = eb * Math.sin(angle);
|
|
9976
|
+
const dist = vx * vx + vy * vy;
|
|
9977
|
+
if (dist < nearDist) {
|
|
9978
|
+
nearAngle = angle;
|
|
9979
|
+
nearX = vx;
|
|
9980
|
+
nearY = vy;
|
|
9981
|
+
nearDist = dist;
|
|
9982
|
+
}
|
|
9983
|
+
if (dist > farDist) {
|
|
9984
|
+
farAngle = angle;
|
|
9985
|
+
farX = vx;
|
|
9986
|
+
farY = vy;
|
|
9987
|
+
farDist = dist;
|
|
9988
|
+
}
|
|
9989
|
+
}
|
|
9990
|
+
if (d2 <= (nearDist + farDist) * 0.5) {
|
|
9991
|
+
a1 = Math.atan2(ty, tx) - Math.atan2(nearY * bend, nearX);
|
|
9992
|
+
a2 = nearAngle * bend;
|
|
9993
|
+
} else {
|
|
9994
|
+
a1 = Math.atan2(ty, tx) - Math.atan2(farY * bend, farX);
|
|
9995
|
+
a2 = farAngle * bend;
|
|
9996
|
+
}
|
|
9997
|
+
}
|
|
9998
|
+
}
|
|
9999
|
+
const signs = (psx < 0 ? -1 : 1) * (psy < 0 ? -1 : 1);
|
|
10000
|
+
const offset = Math.atan2(cy, cx);
|
|
10001
|
+
const parentRotation = L[pl + L_ROTATION];
|
|
10002
|
+
const parentChange = (a1 - signs * offset) * DEG + (psx < 0 ? 180 : 0) - parentRotation;
|
|
10003
|
+
L[pl + L_ROTATION] = parentRotation + wrap180(parentChange) * mix;
|
|
10004
|
+
L[pl + L_SCALE_X] = sx;
|
|
10005
|
+
L[pl + L_SCALE_Y] = sy;
|
|
10006
|
+
const childTarget = ((a2 + signs * offset) * DEG - L[cl + L_SHEAR_X]) * signs + (csx < 0 ? 180 : 0);
|
|
10007
|
+
const childRotation = L[cl + L_ROTATION];
|
|
10008
|
+
L[cl + L_Y] = cy;
|
|
10009
|
+
L[cl + L_ROTATION] = childRotation + wrap180(childTarget - childRotation) * mix;
|
|
10010
|
+
return true;
|
|
10011
|
+
}
|
|
10012
|
+
/** A world-space, absolute transform (`solveTransform`): each bone's world edited in turn and written back, then the repose. */
|
|
10013
|
+
solveTransform(c, k) {
|
|
10014
|
+
const W = this.world;
|
|
10015
|
+
const w = this.w;
|
|
10016
|
+
const mixes = this.mixes;
|
|
10017
|
+
const o6 = k * 6;
|
|
10018
|
+
let n = 0;
|
|
10019
|
+
for (let bk = 0; bk < c.bones.length; bk++) {
|
|
10020
|
+
const bi = c.bones[bk];
|
|
10021
|
+
const o = bi * WORLDS;
|
|
10022
|
+
w[W_A] = W[o + W_A];
|
|
10023
|
+
w[W_B] = W[o + W_B];
|
|
10024
|
+
w[W_C] = W[o + W_C];
|
|
10025
|
+
w[W_D] = W[o + W_D];
|
|
10026
|
+
w[W_X] = W[o + W_X];
|
|
10027
|
+
w[W_Y] = W[o + W_Y];
|
|
10028
|
+
let applied = false;
|
|
10029
|
+
for (let f = 0; f < c.fromProperty.length; f++) {
|
|
10030
|
+
const value = this.sourceValue(c, c.fromProperty[f]) - c.fromOffset[f];
|
|
10031
|
+
for (let q = c.toStart[f]; q < c.toEnd[f]; q++) {
|
|
10032
|
+
const property = c.toProperty[q];
|
|
10033
|
+
const mix = mixes[o6 + property];
|
|
10034
|
+
if (mix === 0) continue;
|
|
10035
|
+
let v = value * c.toScale[q] + c.toOffset[q];
|
|
10036
|
+
if (c.clamp) {
|
|
10037
|
+
const lo = Math.min(c.toOffset[q], c.toMax[q]);
|
|
10038
|
+
const hi = Math.max(c.toOffset[q], c.toMax[q]);
|
|
10039
|
+
v = v < lo ? lo : v > hi ? hi : v;
|
|
10040
|
+
}
|
|
10041
|
+
applied = true;
|
|
10042
|
+
applyWorld(w, property, v, mix);
|
|
10043
|
+
}
|
|
10044
|
+
}
|
|
10045
|
+
if (!applied) continue;
|
|
10046
|
+
W[o + W_A] = w[W_A];
|
|
10047
|
+
W[o + W_B] = w[W_B];
|
|
10048
|
+
W[o + W_C] = w[W_C];
|
|
10049
|
+
W[o + W_D] = w[W_D];
|
|
10050
|
+
W[o + W_X] = w[W_X];
|
|
10051
|
+
W[o + W_Y] = w[W_Y];
|
|
10052
|
+
this.changed[n] = bi;
|
|
10053
|
+
this.inWorld[n] = bi;
|
|
10054
|
+
n++;
|
|
10055
|
+
}
|
|
10056
|
+
if (n > 0) this.repose(n, n);
|
|
10057
|
+
}
|
|
10058
|
+
/** The source's world value of a property (`sourceValue`, world source). */
|
|
10059
|
+
sourceValue(c, property) {
|
|
10060
|
+
const W = this.world;
|
|
10061
|
+
const s = c.source * WORLDS;
|
|
10062
|
+
const o = c.offsets;
|
|
10063
|
+
const a = W[s + W_A];
|
|
10064
|
+
const b = W[s + W_B];
|
|
10065
|
+
const cc = W[s + W_C];
|
|
10066
|
+
const d = W[s + W_D];
|
|
10067
|
+
switch (property) {
|
|
10068
|
+
case P_ROTATE: {
|
|
10069
|
+
const det = a * d - b * cc;
|
|
10070
|
+
let r = Math.atan2(cc, a) * DEG + (det > 0 ? o[P_ROTATE] : -o[P_ROTATE]);
|
|
10071
|
+
if (r < 0) r += 360;
|
|
10072
|
+
return r;
|
|
10073
|
+
}
|
|
10074
|
+
case P_X:
|
|
10075
|
+
return a * o[P_X] + b * o[P_Y] + W[s + W_X];
|
|
10076
|
+
case P_Y:
|
|
10077
|
+
return cc * o[P_X] + d * o[P_Y] + W[s + W_Y];
|
|
10078
|
+
case P_SCALE_X:
|
|
10079
|
+
return Math.sqrt(a * a + cc * cc) + o[P_SCALE_X];
|
|
10080
|
+
case P_SCALE_Y:
|
|
10081
|
+
return Math.sqrt(b * b + d * d) + o[P_SCALE_Y];
|
|
10082
|
+
default:
|
|
10083
|
+
return (Math.atan2(d, b) - Math.atan2(cc, a)) * DEG - 90 + o[5];
|
|
10084
|
+
}
|
|
10085
|
+
}
|
|
10086
|
+
/**
|
|
10087
|
+
* After a constraint (`repose`): the `nInWorld` bones it moved in world space
|
|
10088
|
+
* (the first of `inWorld`) read back into local values, then every bone below
|
|
10089
|
+
* one of the `nChanged` it changed (`changed`) posed again, in bone order —
|
|
10090
|
+
* but not through an inactive bone it did not move itself, and not a bone it
|
|
10091
|
+
* moved in world space.
|
|
10092
|
+
*/
|
|
10093
|
+
repose(nChanged, nInWorld) {
|
|
10094
|
+
const m = this.model;
|
|
10095
|
+
const changed = this.changed;
|
|
10096
|
+
const changedMark = this.changedMark;
|
|
10097
|
+
const inWorldMark = this.inWorldMark;
|
|
10098
|
+
const below = this.below;
|
|
10099
|
+
for (let k = 0; k < nInWorld; k++) {
|
|
10100
|
+
this.localFromWorld(this.inWorld[k]);
|
|
10101
|
+
inWorldMark[this.inWorld[k]] = 1;
|
|
10102
|
+
}
|
|
10103
|
+
for (let k = 0; k < nChanged; k++) {
|
|
10104
|
+
changedMark[changed[k]] = 1;
|
|
10105
|
+
below[changed[k]] = 1;
|
|
10106
|
+
}
|
|
10107
|
+
const starts = m.subtreeStart;
|
|
10108
|
+
const list = m.subtrees;
|
|
10109
|
+
const parents = m.parents;
|
|
10110
|
+
const active = m.active;
|
|
10111
|
+
if (nChanged === 1) {
|
|
10112
|
+
const e = starts[changed[0] + 1];
|
|
10113
|
+
for (let q = starts[changed[0]]; q < e; q++) this.reposeOne(list[q], parents, active);
|
|
10114
|
+
for (let q = starts[changed[0]]; q < e; q++) below[list[q]] = 0;
|
|
10115
|
+
} else {
|
|
10116
|
+
const visit = this.visit;
|
|
10117
|
+
let first = parents.length;
|
|
10118
|
+
for (let k = 0; k < nChanged; k++) {
|
|
10119
|
+
const b = changed[k];
|
|
10120
|
+
if (b < first) first = b;
|
|
10121
|
+
const e = starts[b + 1];
|
|
10122
|
+
for (let q = starts[b]; q < e; q++) visit[list[q]] = 1;
|
|
10123
|
+
}
|
|
10124
|
+
for (let b = first; b < parents.length; b++) if (visit[b] === 1) this.reposeOne(b, parents, active);
|
|
10125
|
+
for (let b = first; b < parents.length; b++) {
|
|
10126
|
+
visit[b] = 0;
|
|
10127
|
+
below[b] = 0;
|
|
10128
|
+
}
|
|
10129
|
+
}
|
|
10130
|
+
for (let k = 0; k < nChanged; k++) changedMark[changed[k]] = 0;
|
|
10131
|
+
for (let k = 0; k < nInWorld; k++) inWorldMark[this.inWorld[k]] = 0;
|
|
10132
|
+
}
|
|
10133
|
+
reposeOne(b, parents, active) {
|
|
10134
|
+
const below = this.below;
|
|
10135
|
+
const p = parents[b];
|
|
10136
|
+
if (p >= 0 && below[p] === 1 && (active[p] === 1 || this.changedMark[p] === 1)) below[b] = 1;
|
|
10137
|
+
if (below[b] === 1 && this.inWorldMark[b] === 0) this.poseBone(b, false);
|
|
10138
|
+
}
|
|
10139
|
+
/** A bone's local values read back from its world transform (`localFromWorld`) — `normal`, `onlyTranslation` and `noRotationOrReflection`; a world target in a `noScale` mode is refused at load. */
|
|
10140
|
+
localFromWorld(bi) {
|
|
10141
|
+
const m = this.model;
|
|
10142
|
+
const W = this.world;
|
|
10143
|
+
const L = this.locals;
|
|
10144
|
+
const w = bi * WORLDS;
|
|
10145
|
+
const l = bi * LOCALS;
|
|
10146
|
+
const parent = m.parents[bi];
|
|
10147
|
+
const mode = parent < 0 ? MODE_NORMAL : m.modes[bi];
|
|
10148
|
+
let pa = 1;
|
|
10149
|
+
let pb = 0;
|
|
10150
|
+
let pc = 0;
|
|
10151
|
+
let pd = 1;
|
|
10152
|
+
let px = 0;
|
|
10153
|
+
let py = 0;
|
|
10154
|
+
if (parent >= 0) {
|
|
10155
|
+
const q = parent * WORLDS;
|
|
10156
|
+
pa = W[q + W_A];
|
|
10157
|
+
pb = W[q + W_B];
|
|
10158
|
+
pc = W[q + W_C];
|
|
10159
|
+
pd = W[q + W_D];
|
|
10160
|
+
px = W[q + W_X];
|
|
10161
|
+
py = W[q + W_Y];
|
|
10162
|
+
}
|
|
10163
|
+
const pid = 1 / (pa * pd - pb * pc);
|
|
10164
|
+
const dx = W[w + W_X] - px;
|
|
10165
|
+
const dy = W[w + W_Y] - py;
|
|
10166
|
+
L[l + L_X] = dx * (pd * pid) - dy * (pb * pid);
|
|
10167
|
+
L[l + L_Y] = dy * (pa * pid) - dx * (pc * pid);
|
|
10168
|
+
L[l + L_SHEAR_X] = 0;
|
|
10169
|
+
const wa = W[w + W_A];
|
|
10170
|
+
const wb = W[w + W_B];
|
|
10171
|
+
const wc = W[w + W_C];
|
|
10172
|
+
const wd = W[w + W_D];
|
|
10173
|
+
if (mode === MODE_ONLY_TRANSLATION) {
|
|
10174
|
+
this.decompose(l, wa, wc, wb, wd);
|
|
10175
|
+
return;
|
|
10176
|
+
}
|
|
10177
|
+
if (mode === MODE_NO_ROTATION_OR_REFLECTION) {
|
|
10178
|
+
const xLengthInverse = 1 / (pa * pa + pc * pc);
|
|
10179
|
+
const detInverse = 1 / Math.abs(pa * pd - pb * pc);
|
|
10180
|
+
this.decompose(l, (pa * wa + pc * wc) * xLengthInverse, (pa * wc - pc * wa) * detInverse, (pa * wb + pc * wd) * xLengthInverse, (pa * wd - pc * wb) * detInverse);
|
|
10181
|
+
L[l + L_ROTATION] = L[l + L_ROTATION] + Math.atan2(pc, pa) * DEG;
|
|
10182
|
+
return;
|
|
10183
|
+
}
|
|
10184
|
+
const ia = pd * pid;
|
|
10185
|
+
const ib = pb * pid;
|
|
10186
|
+
const ic = pc * pid;
|
|
10187
|
+
const id = pa * pid;
|
|
10188
|
+
this.decompose(l, ia * wa - ib * wc, id * wc - ic * wa, ia * wb - ib * wd, id * wd - ic * wb);
|
|
10189
|
+
}
|
|
10190
|
+
/** A local matrix with columns `(la, lc)` and `(lb, ld)` as rotation, scales and shear (`decompose`). */
|
|
10191
|
+
decompose(l, la, lc, lb, ld) {
|
|
10192
|
+
const L = this.locals;
|
|
10193
|
+
const sx = Math.sqrt(la * la + lc * lc);
|
|
10194
|
+
const yLength = Math.sqrt(lb * lb + ld * ld);
|
|
10195
|
+
if (!(sx > 1e-4)) {
|
|
10196
|
+
L[l + L_SCALE_X] = 0;
|
|
10197
|
+
L[l + L_SCALE_Y] = yLength;
|
|
10198
|
+
L[l + L_SHEAR_Y] = 0;
|
|
10199
|
+
L[l + L_ROTATION] = yLength > 1e-5 ? Math.atan2(ld, lb) * DEG - 90 : 0;
|
|
10200
|
+
return;
|
|
10201
|
+
}
|
|
10202
|
+
const xAngle = Math.atan2(lc, la) * DEG;
|
|
10203
|
+
const det = la * ld - lb * lc;
|
|
10204
|
+
const yAngle = Math.atan2(ld, lb) * DEG;
|
|
10205
|
+
L[l + L_ROTATION] = xAngle;
|
|
10206
|
+
L[l + L_SCALE_X] = sx;
|
|
10207
|
+
L[l + L_SCALE_Y] = det < 0 ? -yLength : yLength;
|
|
10208
|
+
const right = det < 0 ? -90 : 90;
|
|
10209
|
+
let shear = yAngle - (xAngle + right);
|
|
10210
|
+
shear = shear > 180 ? shear - 360 : shear < -180 ? shear + 360 : shear;
|
|
10211
|
+
L[l + L_SHEAR_Y] = yLength > 1e-5 ? shear : 0;
|
|
10212
|
+
}
|
|
10213
|
+
// ---------------------------------------------------------------------------
|
|
10214
|
+
// the slots and the vertices
|
|
10215
|
+
// ---------------------------------------------------------------------------
|
|
10216
|
+
/** Every slot's colour and what it shows (`posedSlots`): the setup's, then each of its timelines at `t` in order. */
|
|
10217
|
+
slotValues(anim, t) {
|
|
10218
|
+
const slots = this.model.slots;
|
|
10219
|
+
const light = this.light;
|
|
10220
|
+
for (let s = 0; s < slots.length; s++) {
|
|
10221
|
+
const slot = slots[s];
|
|
10222
|
+
this.shown[s] = slot.setupAttachment;
|
|
10223
|
+
const o = s * 4;
|
|
10224
|
+
light[o] = slot.light[0];
|
|
10225
|
+
light[o + 1] = slot.light[1];
|
|
10226
|
+
light[o + 2] = slot.light[2];
|
|
10227
|
+
light[o + 3] = slot.light[3];
|
|
10228
|
+
}
|
|
10229
|
+
if (anim === null) return;
|
|
10230
|
+
const timelines = anim.slots;
|
|
10231
|
+
for (let k = 0; k < timelines.length; k++) {
|
|
10232
|
+
const tl = timelines[k];
|
|
10233
|
+
const i = keyAt(tl.curve, this.cursors, tl.cursor, t);
|
|
10234
|
+
const attachments = tl.attachments;
|
|
10235
|
+
if (attachments !== null) {
|
|
10236
|
+
this.shown[tl.slot] = i < 0 ? slots[tl.slot].setupAttachment : attachments[i];
|
|
10237
|
+
continue;
|
|
10238
|
+
}
|
|
10239
|
+
const channels = tl.channels;
|
|
10240
|
+
const setup = slots[tl.slot].light;
|
|
10241
|
+
const o = tl.slot * 4;
|
|
10242
|
+
for (let c = 0; c < channels.length; c++) {
|
|
10243
|
+
const at = channels[c];
|
|
10244
|
+
light[o + at] = i < 0 ? setup[at] : clamp01(channelValue(tl.curve, i, c, t));
|
|
10245
|
+
}
|
|
10246
|
+
}
|
|
10247
|
+
}
|
|
10248
|
+
/** Every shown attachment's world vertices into `vertices` at its offset (`regionCorners`, `worldVertices`). */
|
|
10249
|
+
computeVertices() {
|
|
10250
|
+
const m = this.model;
|
|
10251
|
+
const W = this.world;
|
|
10252
|
+
const out = this.vertices;
|
|
10253
|
+
const shown = this.shown;
|
|
10254
|
+
const attachments = m.attachments;
|
|
10255
|
+
for (let s = 0; s < shown.length; s++) {
|
|
10256
|
+
const index = shown[s];
|
|
10257
|
+
if (index < 0) continue;
|
|
10258
|
+
const at = attachments[index];
|
|
10259
|
+
if (at.draw === DRAW_NONE) continue;
|
|
10260
|
+
let o = at.offset;
|
|
10261
|
+
if (at.draw === DRAW_REGION || at.draw === DRAW_MESH) {
|
|
10262
|
+
const b = at.bone * WORLDS;
|
|
10263
|
+
const a = W[b + W_A];
|
|
10264
|
+
const bb = W[b + W_B];
|
|
10265
|
+
const c = W[b + W_C];
|
|
10266
|
+
const d = W[b + W_D];
|
|
10267
|
+
const wx = W[b + W_X];
|
|
10268
|
+
const wy = W[b + W_Y];
|
|
10269
|
+
const local = at.local;
|
|
10270
|
+
if (at.draw === DRAW_REGION) {
|
|
10271
|
+
for (let q = 0; q < 8; q += 2) {
|
|
10272
|
+
const lx = local[q];
|
|
10273
|
+
const ly = local[q + 1];
|
|
10274
|
+
out[o++] = a * lx + bb * ly + wx;
|
|
10275
|
+
out[o++] = c * lx + d * ly + wy;
|
|
10276
|
+
}
|
|
10277
|
+
} else {
|
|
10278
|
+
for (let q = 0; q < at.count; q += 2) {
|
|
10279
|
+
const x = local[q];
|
|
10280
|
+
const y = local[q + 1];
|
|
10281
|
+
out[o++] = x * a + y * bb + wx;
|
|
10282
|
+
out[o++] = x * c + y * d + wy;
|
|
10283
|
+
}
|
|
10284
|
+
}
|
|
10285
|
+
continue;
|
|
10286
|
+
}
|
|
10287
|
+
const counts = at.bindingCounts;
|
|
10288
|
+
const bones = at.bindingBones;
|
|
10289
|
+
const xs = at.bindingX;
|
|
10290
|
+
const ys = at.bindingY;
|
|
10291
|
+
const weights = at.bindingWeights;
|
|
10292
|
+
let n = 0;
|
|
10293
|
+
for (let v = 0; v < counts.length; v++) {
|
|
10294
|
+
let wx = 0;
|
|
10295
|
+
let wy = 0;
|
|
10296
|
+
const end = n + counts[v];
|
|
10297
|
+
for (; n < end; n++) {
|
|
10298
|
+
const b = bones[n] * WORLDS;
|
|
10299
|
+
const x = xs[n];
|
|
10300
|
+
const y = ys[n];
|
|
10301
|
+
const weight = weights[n];
|
|
10302
|
+
wx += (x * W[b + W_A] + y * W[b + W_B] + W[b + W_X]) * weight;
|
|
10303
|
+
wy += (x * W[b + W_C] + y * W[b + W_D] + W[b + W_Y]) * weight;
|
|
10304
|
+
}
|
|
10305
|
+
out[o++] = wx;
|
|
10306
|
+
out[o++] = wy;
|
|
10307
|
+
}
|
|
10308
|
+
}
|
|
10309
|
+
}
|
|
10310
|
+
}
|
|
10311
|
+
function applyWorld(w, property, v, mix) {
|
|
10312
|
+
switch (property) {
|
|
10313
|
+
case P_ROTATE: {
|
|
10314
|
+
let r = v * RAD - Math.atan2(w[W_C], w[W_A]);
|
|
10315
|
+
if (r > RUNTIME_PI) r -= TWO_PI;
|
|
10316
|
+
else if (r < -RUNTIME_PI) r += TWO_PI;
|
|
10317
|
+
r *= mix;
|
|
10318
|
+
const cos = Math.cos(r);
|
|
10319
|
+
const sin = Math.sin(r);
|
|
10320
|
+
const a = w[W_A];
|
|
10321
|
+
const b = w[W_B];
|
|
10322
|
+
w[W_A] = cos * a - sin * w[W_C];
|
|
10323
|
+
w[W_B] = cos * b - sin * w[W_D];
|
|
10324
|
+
w[W_C] = sin * a + cos * w[W_C];
|
|
10325
|
+
w[W_D] = sin * b + cos * w[W_D];
|
|
10326
|
+
return;
|
|
10327
|
+
}
|
|
10328
|
+
case P_X:
|
|
10329
|
+
w[W_X] = w[W_X] + (v - w[W_X]) * mix;
|
|
10330
|
+
return;
|
|
10331
|
+
case P_Y:
|
|
10332
|
+
w[W_Y] = w[W_Y] + (v - w[W_Y]) * mix;
|
|
10333
|
+
return;
|
|
10334
|
+
case P_SCALE_X:
|
|
10335
|
+
case P_SCALE_Y: {
|
|
10336
|
+
const p = property === P_SCALE_X ? W_A : W_B;
|
|
10337
|
+
const q = property === P_SCALE_X ? W_C : W_D;
|
|
10338
|
+
const s = Math.sqrt(w[p] * w[p] + w[q] * w[q]);
|
|
10339
|
+
if (s === 0) return;
|
|
10340
|
+
const k = 1 + (v - s) * mix / s;
|
|
10341
|
+
w[p] *= k;
|
|
10342
|
+
w[q] *= k;
|
|
10343
|
+
return;
|
|
10344
|
+
}
|
|
10345
|
+
default: {
|
|
10346
|
+
const yAngle = Math.atan2(w[W_D], w[W_B]);
|
|
10347
|
+
const xAngle = Math.atan2(w[W_C], w[W_A]);
|
|
10348
|
+
let r = (v + 90) * RAD - (yAngle - xAngle);
|
|
10349
|
+
r = r > RUNTIME_PI ? r - TWO_PI : r < -RUNTIME_PI ? r + TWO_PI : r;
|
|
10350
|
+
const angle = yAngle + r * mix;
|
|
10351
|
+
const s = Math.sqrt(w[W_B] * w[W_B] + w[W_D] * w[W_D]);
|
|
10352
|
+
w[W_B] = Math.cos(angle) * s;
|
|
10353
|
+
w[W_D] = Math.sin(angle) * s;
|
|
10354
|
+
}
|
|
10355
|
+
}
|
|
10356
|
+
}
|
|
8835
10357
|
function frameSource(doc, parsed) {
|
|
8836
10358
|
if (doc.pages === null) {
|
|
8837
10359
|
throw new CoreInputError(
|
|
@@ -8839,12 +10361,20 @@ function frameSource(doc, parsed) {
|
|
|
8839
10361
|
);
|
|
8840
10362
|
}
|
|
8841
10363
|
const pages = new Map(doc.pages.map((page) => [page.name, page]));
|
|
10364
|
+
const boneOf = /* @__PURE__ */ new Map();
|
|
10365
|
+
const slotIndex = /* @__PURE__ */ new Map();
|
|
10366
|
+
for (let i = 0; i < doc.slots.length; i++) {
|
|
10367
|
+
const slot = doc.slots[i];
|
|
10368
|
+
boneOf.set(slot.name, slot.bone);
|
|
10369
|
+
slotIndex.set(slot.name, i);
|
|
10370
|
+
}
|
|
8842
10371
|
return {
|
|
8843
10372
|
doc,
|
|
8844
10373
|
uv: { lookup: documentPageLookup(doc.pages), sequences: readUvSequences(parsed) },
|
|
8845
10374
|
pages,
|
|
8846
10375
|
active: activeBones(doc),
|
|
8847
|
-
boneOf
|
|
10376
|
+
boneOf,
|
|
10377
|
+
slotIndex,
|
|
8848
10378
|
uvs: /* @__PURE__ */ new WeakMap(),
|
|
8849
10379
|
regionUvs: /* @__PURE__ */ new WeakMap(),
|
|
8850
10380
|
triangles: /* @__PURE__ */ new Map()
|
|
@@ -8877,24 +10407,57 @@ function regionUvs(source, found) {
|
|
|
8877
10407
|
return kept;
|
|
8878
10408
|
}
|
|
8879
10409
|
function trianglesOf(source, drawn) {
|
|
8880
|
-
|
|
8881
|
-
|
|
8882
|
-
|
|
8883
|
-
|
|
8884
|
-
|
|
10410
|
+
let byAttachment = source.triangles.get(drawn.slot);
|
|
10411
|
+
if (byAttachment === void 0) {
|
|
10412
|
+
byAttachment = /* @__PURE__ */ new Map();
|
|
10413
|
+
source.triangles.set(drawn.slot, byAttachment);
|
|
10414
|
+
}
|
|
10415
|
+
let cache = byAttachment.get(drawn.attachment);
|
|
10416
|
+
if (cache === void 0) {
|
|
10417
|
+
cache = { byRef: /* @__PURE__ */ new WeakMap(), lists: [] };
|
|
10418
|
+
byAttachment.set(drawn.attachment, cache);
|
|
8885
10419
|
}
|
|
8886
10420
|
const wanted = drawn.triangles;
|
|
8887
|
-
|
|
10421
|
+
const fast = cache.byRef.get(wanted);
|
|
10422
|
+
if (fast !== void 0) return fast;
|
|
10423
|
+
const frozen = Object.isFrozen(wanted);
|
|
10424
|
+
let kept = cache.lists.find((list) => list.length === wanted.length && list.every((v, i) => v === wanted[i]));
|
|
8888
10425
|
if (kept === void 0) {
|
|
8889
|
-
kept = [...wanted];
|
|
8890
|
-
lists.push(kept);
|
|
10426
|
+
kept = frozen ? wanted : Object.freeze([...wanted]);
|
|
10427
|
+
cache.lists.push(kept);
|
|
10428
|
+
cache.byRef.set(kept, kept);
|
|
8891
10429
|
}
|
|
10430
|
+
if (frozen) cache.byRef.set(wanted, kept);
|
|
8892
10431
|
return kept;
|
|
8893
10432
|
}
|
|
10433
|
+
function regionMap(regions) {
|
|
10434
|
+
const map = /* @__PURE__ */ new Map();
|
|
10435
|
+
for (let i = 0; i < regions.length; i++) map.set(regions[i].slot, regions[i]);
|
|
10436
|
+
return map;
|
|
10437
|
+
}
|
|
10438
|
+
function slotRowMap(slots) {
|
|
10439
|
+
const map = /* @__PURE__ */ new Map();
|
|
10440
|
+
for (let i = 0; i < slots.length; i++) map.set(slots[i][0], slots[i]);
|
|
10441
|
+
return map;
|
|
10442
|
+
}
|
|
10443
|
+
function drawOrderMap(drawOrder) {
|
|
10444
|
+
const map = /* @__PURE__ */ new Map();
|
|
10445
|
+
for (let i = 0; i < drawOrder.length; i++) map.set(drawOrder[i], i);
|
|
10446
|
+
return map;
|
|
10447
|
+
}
|
|
8894
10448
|
function clipCover(pose, active, draws = () => true) {
|
|
8895
|
-
const polygons = new Map(pose.clips.map((c) => [c[0], { end: c[2], polygon: c[3] }]));
|
|
8896
|
-
const shownOf = new Map(pose.shown.map((s) => [s.slot, s]));
|
|
8897
10449
|
const cover = /* @__PURE__ */ new Map();
|
|
10450
|
+
if (pose.clips.length === 0) return cover;
|
|
10451
|
+
const polygons = /* @__PURE__ */ new Map();
|
|
10452
|
+
for (let i = 0; i < pose.clips.length; i++) {
|
|
10453
|
+
const c = pose.clips[i];
|
|
10454
|
+
polygons.set(c[0], { end: c[2], polygon: c[3] });
|
|
10455
|
+
}
|
|
10456
|
+
const shownOf = /* @__PURE__ */ new Map();
|
|
10457
|
+
for (let i = 0; i < pose.shown.length; i++) {
|
|
10458
|
+
const s = pose.shown[i];
|
|
10459
|
+
shownOf.set(s.slot, s);
|
|
10460
|
+
}
|
|
8898
10461
|
let current = null;
|
|
8899
10462
|
for (const slot of pose.drawOrder) {
|
|
8900
10463
|
const shown = shownOf.get(slot);
|
|
@@ -8915,20 +10478,25 @@ function clipCover(pose, active, draws = () => true) {
|
|
|
8915
10478
|
function frameOf(source, pose, color = [1, 1, 1, 1], draws = () => true) {
|
|
8916
10479
|
const { drawn: regions, why } = drawnRegions(source.doc, pose.shown, pose.drawOrder, source.uv);
|
|
8917
10480
|
if (regions === null) throw new CoreInputError(`the page UVs are not posed: ${why}`);
|
|
8918
|
-
|
|
8919
|
-
|
|
8920
|
-
|
|
8921
|
-
const cover = clipCover(pose, source.active, draws);
|
|
10481
|
+
let regionOf = null;
|
|
10482
|
+
let rows = null;
|
|
10483
|
+
let z = null;
|
|
10484
|
+
const cover = pose.clips.length === 0 ? null : clipCover(pose, source.active, draws);
|
|
8922
10485
|
const [sr, sg, sb, sa] = color;
|
|
8923
10486
|
const order = [];
|
|
8924
|
-
for (
|
|
10487
|
+
for (let i = 0; i < pose.drawn.length; i++) {
|
|
10488
|
+
const d = pose.drawn[i];
|
|
8925
10489
|
if (!draws(d.slot)) continue;
|
|
8926
10490
|
const bone2 = source.boneOf.get(d.slot);
|
|
8927
10491
|
if (bone2 === void 0 || !source.active.has(bone2)) continue;
|
|
8928
|
-
const
|
|
10492
|
+
const directRegion = regions[i];
|
|
10493
|
+
const found = directRegion !== void 0 && directRegion.slot === d.slot ? directRegion : (regionOf ??= regionMap(regions)).get(d.slot);
|
|
8929
10494
|
if (found === void 0) throw new CoreInputError(`slot "${d.slot}" drew "${d.attachment}", and no atlas region was resolved for it`);
|
|
8930
|
-
const
|
|
10495
|
+
const slotIdx = source.slotIndex.get(d.slot);
|
|
10496
|
+
const directRow = slotIdx !== void 0 ? pose.slots[slotIdx] : void 0;
|
|
10497
|
+
const row = directRow !== void 0 && directRow[0] === d.slot ? directRow : (rows ??= slotRowMap(pose.slots)).get(d.slot);
|
|
8931
10498
|
if (row === void 0) throw new CoreInputError(`slot "${d.slot}" drew and has no slot row`);
|
|
10499
|
+
const slotZ = slotIdx !== void 0 && pose.drawOrder[slotIdx] === d.slot ? slotIdx : (z ??= drawOrderMap(pose.drawOrder)).get(d.slot);
|
|
8932
10500
|
const tint = [
|
|
8933
10501
|
channel(row[2], d.slot) * d.colour[0] * sr,
|
|
8934
10502
|
channel(row[3], d.slot) * d.colour[1] * sg,
|
|
@@ -8936,13 +10504,14 @@ function frameOf(source, pose, color = [1, 1, 1, 1], draws = () => true) {
|
|
|
8936
10504
|
channel(row[5], d.slot) * d.colour[3] * sa
|
|
8937
10505
|
];
|
|
8938
10506
|
const darkRow = row[6];
|
|
10507
|
+
const clip = cover === null ? void 0 : cover.get(d.slot);
|
|
8939
10508
|
const common = {
|
|
8940
10509
|
slot: d.slot,
|
|
8941
|
-
z:
|
|
10510
|
+
z: slotZ,
|
|
8942
10511
|
blend: row[8] ?? "Normal",
|
|
8943
10512
|
tint,
|
|
8944
10513
|
...darkRow === null ? {} : { dark: [channel(darkRow[0], d.slot), channel(darkRow[1], d.slot), channel(darkRow[2], d.slot)] },
|
|
8945
|
-
...
|
|
10514
|
+
...clip === void 0 ? {} : { clip }
|
|
8946
10515
|
};
|
|
8947
10516
|
const page = source.pages.get(found.found.page.name);
|
|
8948
10517
|
if (d.kind === "region") {
|
|
@@ -8970,6 +10539,238 @@ function frameOf(source, pose, color = [1, 1, 1, 1], draws = () => true) {
|
|
|
8970
10539
|
}
|
|
8971
10540
|
return { order };
|
|
8972
10541
|
}
|
|
10542
|
+
function numbers(buffer, offset, count) {
|
|
10543
|
+
const result = new Array(count);
|
|
10544
|
+
for (let i = 0; i < count; i++) Object.defineProperty(result, i, {
|
|
10545
|
+
enumerable: true,
|
|
10546
|
+
get: () => buffer[offset + i]
|
|
10547
|
+
});
|
|
10548
|
+
return Object.freeze(result);
|
|
10549
|
+
}
|
|
10550
|
+
function checkedLiveFrame(frame2, current) {
|
|
10551
|
+
const generation = current();
|
|
10552
|
+
const frameGeneration = frame2.generation;
|
|
10553
|
+
const proxies = /* @__PURE__ */ new WeakMap();
|
|
10554
|
+
const check = () => {
|
|
10555
|
+
if (current() !== generation) throw new Error(`LIVE_FRAME_STALE: borrowed generation ${frameGeneration} was invalidated; call liveFrame() again`);
|
|
10556
|
+
};
|
|
10557
|
+
const wrap = (value) => {
|
|
10558
|
+
const found = proxies.get(value);
|
|
10559
|
+
if (found) return found;
|
|
10560
|
+
const proxy = new Proxy(Array.isArray(value) ? [] : {}, {
|
|
10561
|
+
get(_target, property) {
|
|
10562
|
+
check();
|
|
10563
|
+
if (property === "buffer" || property === "set" || property === "fill" || property === "copyWithin" || property === "reverse" || property === "sort") {
|
|
10564
|
+
throw new Error(`LIVE_FRAME_READONLY: ${String(property)} does not expose the live backing store`);
|
|
10565
|
+
}
|
|
10566
|
+
const item = Reflect.get(value, property, value);
|
|
10567
|
+
if (typeof item === "function") {
|
|
10568
|
+
if (ArrayBuffer.isView(value) && property !== Symbol.iterator) throw new Error(`LIVE_FRAME_READONLY: ${String(property)} is not part of the indexed live buffer interface`);
|
|
10569
|
+
return (...args) => {
|
|
10570
|
+
check();
|
|
10571
|
+
const returned = Reflect.apply(item, Array.isArray(value) ? proxy : value, args);
|
|
10572
|
+
return returned !== null && typeof returned === "object" ? wrap(returned) : returned;
|
|
10573
|
+
};
|
|
10574
|
+
}
|
|
10575
|
+
return item !== null && typeof item === "object" ? wrap(item) : item;
|
|
10576
|
+
},
|
|
10577
|
+
set() {
|
|
10578
|
+
check();
|
|
10579
|
+
throw new Error("LIVE_FRAME_READONLY: live frame values cannot be assigned");
|
|
10580
|
+
},
|
|
10581
|
+
defineProperty() {
|
|
10582
|
+
check();
|
|
10583
|
+
throw new Error("LIVE_FRAME_READONLY: live frame values cannot be redefined");
|
|
10584
|
+
},
|
|
10585
|
+
deleteProperty() {
|
|
10586
|
+
check();
|
|
10587
|
+
throw new Error("LIVE_FRAME_READONLY: live frame values cannot be deleted");
|
|
10588
|
+
},
|
|
10589
|
+
preventExtensions() {
|
|
10590
|
+
check();
|
|
10591
|
+
throw new Error("LIVE_FRAME_READONLY: live frame objects cannot be reconfigured");
|
|
10592
|
+
},
|
|
10593
|
+
setPrototypeOf() {
|
|
10594
|
+
check();
|
|
10595
|
+
throw new Error("LIVE_FRAME_READONLY: live frame prototypes cannot be changed");
|
|
10596
|
+
},
|
|
10597
|
+
has(_target, property) {
|
|
10598
|
+
check();
|
|
10599
|
+
return Reflect.has(value, property);
|
|
10600
|
+
},
|
|
10601
|
+
ownKeys() {
|
|
10602
|
+
check();
|
|
10603
|
+
return Reflect.ownKeys(value);
|
|
10604
|
+
},
|
|
10605
|
+
getOwnPropertyDescriptor(_target, property) {
|
|
10606
|
+
check();
|
|
10607
|
+
if (property === "length" && Array.isArray(value)) return { value: value.length, writable: true, configurable: false, enumerable: false };
|
|
10608
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(value, property);
|
|
10609
|
+
return descriptor ? { configurable: true, enumerable: descriptor.enumerable, get: () => Reflect.get(proxy, property) } : void 0;
|
|
10610
|
+
}
|
|
10611
|
+
});
|
|
10612
|
+
proxies.set(value, proxy);
|
|
10613
|
+
return proxy;
|
|
10614
|
+
};
|
|
10615
|
+
const lease = wrap(frame2);
|
|
10616
|
+
const drawing = liveDrawings.get(frame2);
|
|
10617
|
+
if (drawing) liveDrawings.set(lease, drawing);
|
|
10618
|
+
return lease;
|
|
10619
|
+
}
|
|
10620
|
+
class RuntimeFrames {
|
|
10621
|
+
model;
|
|
10622
|
+
pose;
|
|
10623
|
+
renderFrame = { order: [] };
|
|
10624
|
+
live;
|
|
10625
|
+
order;
|
|
10626
|
+
tint;
|
|
10627
|
+
templates;
|
|
10628
|
+
colours;
|
|
10629
|
+
/** Incremented when a draw refresh changes colour without a pose step. */
|
|
10630
|
+
revision = 0;
|
|
10631
|
+
previousColor = new Float64Array(4).fill(Number.NaN);
|
|
10632
|
+
drawn = 0;
|
|
10633
|
+
epoch = 0;
|
|
10634
|
+
constructor(source, model) {
|
|
10635
|
+
this.model = model ?? compileRuntime(source.doc);
|
|
10636
|
+
this.pose = new RuntimePose(this.model);
|
|
10637
|
+
this.order = new Int32Array(this.model.slots.length);
|
|
10638
|
+
this.tint = new Float64Array(this.model.slots.length * 4);
|
|
10639
|
+
const colours = [];
|
|
10640
|
+
const metadata = [];
|
|
10641
|
+
this.templates = this.model.attachments.map((attachment, index) => {
|
|
10642
|
+
const identity = attachment;
|
|
10643
|
+
if (identity.skin === void 0 || identity.placeholder === void 0) {
|
|
10644
|
+
throw new Error(`RUNTIME_DRAW_IDENTITY: attachment ${index} on slot "${this.model.slots[attachment.slot].name}" needs resolved skin and placeholder from rig-c/runtime`);
|
|
10645
|
+
}
|
|
10646
|
+
const slot = this.model.slots[attachment.slot];
|
|
10647
|
+
const doc = { ...source.doc, slots: source.doc.slots.map((value, si) => ({ ...value, setup: si === attachment.slot ? identity.placeholder : null })) };
|
|
10648
|
+
const setup = poseRawSetup(doc);
|
|
10649
|
+
const raw = setup.drawn.find((value) => value.slot === slot.name);
|
|
10650
|
+
const template = frameOf(source, setup).order.find((value) => value.slot === slot.name) ?? null;
|
|
10651
|
+
const colour = raw?.colour ?? [1, 1, 1, 1];
|
|
10652
|
+
colours.push(colour);
|
|
10653
|
+
if (template) {
|
|
10654
|
+
const vertices = numbers(this.pose.vertices, attachment.offset, attachment.count);
|
|
10655
|
+
if (template.kind === "region") template.corners = vertices;
|
|
10656
|
+
else template.vertices = vertices;
|
|
10657
|
+
template.tint = numbers(this.tint, attachment.slot * 4, 4);
|
|
10658
|
+
}
|
|
10659
|
+
metadata.push(Object.freeze({
|
|
10660
|
+
slot: slot.name,
|
|
10661
|
+
slotIndex: attachment.slot,
|
|
10662
|
+
attachment: attachment.name,
|
|
10663
|
+
skin: identity.skin,
|
|
10664
|
+
placeholder: identity.placeholder,
|
|
10665
|
+
kind: template?.kind ?? "none",
|
|
10666
|
+
region: template?.kind === "region" ? template.region : null,
|
|
10667
|
+
page: template?.page ?? null,
|
|
10668
|
+
pma: template?.pma ?? false,
|
|
10669
|
+
blend: slot.blend,
|
|
10670
|
+
offset: attachment.offset,
|
|
10671
|
+
count: attachment.count,
|
|
10672
|
+
uvs: Object.freeze(template?.uvs ?? []),
|
|
10673
|
+
triangles: Object.freeze(template?.kind === "mesh" ? template.triangles : []),
|
|
10674
|
+
dark: template?.dark ? Object.freeze(template.dark) : null,
|
|
10675
|
+
clip: null
|
|
10676
|
+
}));
|
|
10677
|
+
return template;
|
|
10678
|
+
});
|
|
10679
|
+
this.colours = colours;
|
|
10680
|
+
const owner = this;
|
|
10681
|
+
this.live = Object.freeze({
|
|
10682
|
+
get generation() {
|
|
10683
|
+
return owner.epoch;
|
|
10684
|
+
},
|
|
10685
|
+
get count() {
|
|
10686
|
+
return owner.drawn;
|
|
10687
|
+
},
|
|
10688
|
+
drawOrder: this.order,
|
|
10689
|
+
vertices: this.pose.vertices,
|
|
10690
|
+
tint: this.tint,
|
|
10691
|
+
attachments: Object.freeze(metadata)
|
|
10692
|
+
});
|
|
10693
|
+
liveDrawings.set(this.live, this.renderFrame);
|
|
10694
|
+
}
|
|
10695
|
+
update(generation, color, draws, poseChanged = true) {
|
|
10696
|
+
if (poseChanged) this.pose.computeVertices();
|
|
10697
|
+
let colorChanged = false;
|
|
10698
|
+
for (let c = 0; c < 4; c++) {
|
|
10699
|
+
if (!Object.is(this.previousColor[c], color[c])) colorChanged = true;
|
|
10700
|
+
this.previousColor[c] = color[c];
|
|
10701
|
+
}
|
|
10702
|
+
if (colorChanged) this.revision++;
|
|
10703
|
+
let count = 0;
|
|
10704
|
+
for (let si = 0; si < this.pose.shown.length; si++) {
|
|
10705
|
+
const ai = this.pose.shown[si];
|
|
10706
|
+
const template = ai < 0 ? null : this.templates[ai];
|
|
10707
|
+
if (!template || !this.model.active[this.model.slots[si].bone]) continue;
|
|
10708
|
+
const offset = si * 4;
|
|
10709
|
+
const colour = this.colours[ai];
|
|
10710
|
+
for (let c = 0; c < 4; c++) this.tint[offset + c] = this.pose.light[offset + c] * colour[c] * color[c];
|
|
10711
|
+
if (!draws(template.slot)) continue;
|
|
10712
|
+
this.order[count] = ai;
|
|
10713
|
+
this.renderFrame.order[count++] = template;
|
|
10714
|
+
}
|
|
10715
|
+
this.renderFrame.order.length = count;
|
|
10716
|
+
this.drawn = count;
|
|
10717
|
+
this.epoch = generation;
|
|
10718
|
+
}
|
|
10719
|
+
snapshot(draws) {
|
|
10720
|
+
const selected = [];
|
|
10721
|
+
for (let si = 0; si < this.pose.shown.length; si++) {
|
|
10722
|
+
const ai = this.pose.shown[si];
|
|
10723
|
+
const slot = ai < 0 ? null : this.templates[ai];
|
|
10724
|
+
if (slot && this.model.active[this.model.slots[si].bone] && (draws === void 0 || draws(slot.slot))) selected.push(slot);
|
|
10725
|
+
}
|
|
10726
|
+
const order = selected.map((slot) => {
|
|
10727
|
+
const common = { ...slot, tint: slot.tint.slice() };
|
|
10728
|
+
return slot.kind === "region" ? { ...common, kind: "region", region: slot.region, corners: slot.corners.slice(), page: slot.page, pma: slot.pma, uvs: slot.uvs } : { ...common, kind: "mesh", vertices: slot.vertices.slice(), page: slot.page, pma: slot.pma, uvs: slot.uvs, triangles: slot.triangles, sequence: slot.sequence };
|
|
10729
|
+
});
|
|
10730
|
+
return { order };
|
|
10731
|
+
}
|
|
10732
|
+
}
|
|
10733
|
+
function oracleLiveFrame(frame2, identities, indices) {
|
|
10734
|
+
let size = 0;
|
|
10735
|
+
for (const slot of frame2.order) size += (slot.kind === "region" ? slot.corners : slot.vertices).length;
|
|
10736
|
+
const vertices = new Float64Array(size);
|
|
10737
|
+
const tint = new Float64Array(indices.size * 4);
|
|
10738
|
+
const drawOrder = new Int32Array(frame2.order.length);
|
|
10739
|
+
let offset = 0;
|
|
10740
|
+
const attachments = frame2.order.map((slot, index) => {
|
|
10741
|
+
const points = slot.kind === "region" ? slot.corners : slot.vertices;
|
|
10742
|
+
const slotIndex = indices.get(slot.slot);
|
|
10743
|
+
if (slotIndex === void 0) throw new Error(`LIVE_FRAME_SLOT: drawn slot "${slot.slot}" has no setup index`);
|
|
10744
|
+
vertices.set(points, offset);
|
|
10745
|
+
tint.set(slot.tint, slotIndex * 4);
|
|
10746
|
+
drawOrder[index] = index;
|
|
10747
|
+
const identity = identities.get(slot.slot);
|
|
10748
|
+
if (!identity) throw new Error(`LIVE_FRAME_IDENTITY: drawn slot "${slot.slot}" has no coherent identity`);
|
|
10749
|
+
const metadata = {
|
|
10750
|
+
slot: slot.slot,
|
|
10751
|
+
slotIndex,
|
|
10752
|
+
attachment: identity.attachment,
|
|
10753
|
+
skin: identity.skin,
|
|
10754
|
+
placeholder: null,
|
|
10755
|
+
kind: slot.kind,
|
|
10756
|
+
region: slot.kind === "region" ? slot.region : null,
|
|
10757
|
+
page: slot.page,
|
|
10758
|
+
pma: slot.pma,
|
|
10759
|
+
blend: slot.blend,
|
|
10760
|
+
offset,
|
|
10761
|
+
count: points.length,
|
|
10762
|
+
uvs: slot.uvs,
|
|
10763
|
+
triangles: slot.kind === "mesh" ? slot.triangles : [],
|
|
10764
|
+
dark: slot.dark ?? null,
|
|
10765
|
+
clip: slot.clip ?? null
|
|
10766
|
+
};
|
|
10767
|
+
offset += points.length;
|
|
10768
|
+
return metadata;
|
|
10769
|
+
});
|
|
10770
|
+
const live = { generation: frame2.generation ?? 0, count: attachments.length, drawOrder, vertices, tint, attachments };
|
|
10771
|
+
liveDrawings.set(live, frame2);
|
|
10772
|
+
return live;
|
|
10773
|
+
}
|
|
8973
10774
|
const DEFAULT_ENVIRONMENT = Object.freeze({ wind: Object.freeze([1, 0]), gravity: Object.freeze([0, 1]) });
|
|
8974
10775
|
function setEaseOption(call, options) {
|
|
8975
10776
|
const ease = options?.ease;
|
|
@@ -9296,6 +11097,14 @@ class RigPlayer {
|
|
|
9296
11097
|
setup = null;
|
|
9297
11098
|
seekCache = null;
|
|
9298
11099
|
poseGeneration = 0;
|
|
11100
|
+
runtimeModel = null;
|
|
11101
|
+
runtimeFrames = null;
|
|
11102
|
+
runtimeReadback = null;
|
|
11103
|
+
runtimeCompletionClock = { trackTime: 0 };
|
|
11104
|
+
poserLabel = "oracle: not selected";
|
|
11105
|
+
oracleOnly;
|
|
11106
|
+
liveChecks;
|
|
11107
|
+
liveDisposed = false;
|
|
9299
11108
|
cachedIdentity = null;
|
|
9300
11109
|
cachedIdentityGen = -1;
|
|
9301
11110
|
setList;
|
|
@@ -9335,6 +11144,9 @@ class RigPlayer {
|
|
|
9335
11144
|
constructor(root, assets, options = {}) {
|
|
9336
11145
|
const read = assets.document;
|
|
9337
11146
|
this.read = read;
|
|
11147
|
+
if (options.poser !== void 0 && options.poser !== "auto" && options.poser !== "oracle") throw new Error(`poser must be auto or oracle, got ${String(options.poser)}`);
|
|
11148
|
+
this.oracleOnly = options.poser === "oracle";
|
|
11149
|
+
this.liveChecks = options.liveFrameChecks ?? false;
|
|
9338
11150
|
this.skinName = options.skin;
|
|
9339
11151
|
this.view = options.skin === void 0 ? underNoSkin(read.doc) : underSkin(read.doc, options.skin);
|
|
9340
11152
|
this.source = frameSource(this.view, read.parsed);
|
|
@@ -9351,6 +11163,7 @@ class RigPlayer {
|
|
|
9351
11163
|
this.poseGeneration = 0;
|
|
9352
11164
|
this.cachedIdentity = null;
|
|
9353
11165
|
this.active = this.create(options.mode ?? "dom");
|
|
11166
|
+
this.selectRuntime();
|
|
9354
11167
|
}
|
|
9355
11168
|
/**
|
|
9356
11169
|
* How the player draws. Setting it swaps the renderer without reloading
|
|
@@ -9395,7 +11208,7 @@ class RigPlayer {
|
|
|
9395
11208
|
}
|
|
9396
11209
|
/** The player's time — the running sum of the steps since `play` (or the last `seek`). */
|
|
9397
11210
|
get time() {
|
|
9398
|
-
return this.tracks?.trackTime ?? this.track?.trackTime ?? 0;
|
|
11211
|
+
return this.runtimeFrames?.pose.trackTime ?? this.tracks?.trackTime ?? this.track?.trackTime ?? 0;
|
|
9399
11212
|
}
|
|
9400
11213
|
/**
|
|
9401
11214
|
* Plays `name` on a track — track 0 by default, which is what `play(name)`
|
|
@@ -9420,6 +11233,8 @@ class RigPlayer {
|
|
|
9420
11233
|
const plain = track === 0 && mix === 0 && alpha === 1 && !additive;
|
|
9421
11234
|
if (!plain) this.beyondBase = true;
|
|
9422
11235
|
if (this.tracks === null && plain && !this.trackMode) {
|
|
11236
|
+
if (this.tryRuntimePlay(name, loop)) return;
|
|
11237
|
+
this.oracle("animation not supported by the runtime");
|
|
9423
11238
|
this.animation = name;
|
|
9424
11239
|
this.loop = loop;
|
|
9425
11240
|
this.track = openTrack(this.view, name, { loop, reset: "animation", adjust: this.adjustHook, clock: physicsClockStep });
|
|
@@ -9494,6 +11309,11 @@ class RigPlayer {
|
|
|
9494
11309
|
}
|
|
9495
11310
|
/** `track`'s current entry, or null where it plays nothing (and before `play`). */
|
|
9496
11311
|
entry(track = 0) {
|
|
11312
|
+
if (this.runtimeFrames !== null) {
|
|
11313
|
+
if (track !== 0 || this.animation === null) return null;
|
|
11314
|
+
const pose = this.runtimeFrames.pose;
|
|
11315
|
+
return { animation: this.animation, loop: this.loop, trackTime: pose.trackTime, animationTime: pose.time, duration: this.runtimeFrames.model.animation(this.animation).duration, alpha: 1, additive: false };
|
|
11316
|
+
}
|
|
9497
11317
|
if (this.tracks !== null) {
|
|
9498
11318
|
const view = this.tracks.current(track);
|
|
9499
11319
|
return view === null ? null : { animation: view.animation, loop: view.loop, trackTime: view.trackTime, animationTime: view.animationTime, duration: view.duration, alpha: view.alpha, additive: view.additive };
|
|
@@ -9517,6 +11337,7 @@ class RigPlayer {
|
|
|
9517
11337
|
* `seekStep` of time played — which `tracks: true` avoids entirely.
|
|
9518
11338
|
*/
|
|
9519
11339
|
compose() {
|
|
11340
|
+
this.oracle("track composition");
|
|
9520
11341
|
if (this.tracks !== null) return this.tracks;
|
|
9521
11342
|
this.clearSeekCache();
|
|
9522
11343
|
const tracks = openTracks(this.view, { reset: "animation", adjust: this.adjustHook, clock: physicsClockStep });
|
|
@@ -9629,6 +11450,7 @@ class RigPlayer {
|
|
|
9629
11450
|
* there is nothing to move.
|
|
9630
11451
|
*/
|
|
9631
11452
|
moved(motion) {
|
|
11453
|
+
if (this.runtimeFrames !== null && Number.isFinite(motion.x ?? 0) && Number.isFinite(motion.y ?? 0) && Number.isFinite(motion.rotation ?? 0)) return;
|
|
9632
11454
|
const live = this.stepper();
|
|
9633
11455
|
if (!live) return;
|
|
9634
11456
|
const x = motion.x ?? 0;
|
|
@@ -9657,6 +11479,16 @@ class RigPlayer {
|
|
|
9657
11479
|
if (this.animation === null) throw new Error("seek before play: no animation is open");
|
|
9658
11480
|
if (!(time >= 0) || !Number.isFinite(time)) throw new Error(`seek time must be a finite time at or above 0, got ${String(time)}`);
|
|
9659
11481
|
if (this.beyondBase) throw new Error("seek is defined for a single animation on track 0; after a mix, another track, a queue or a clear the player cannot replay to a time — step it forward with frame/advance instead");
|
|
11482
|
+
if (this.runtimeFrames !== null) {
|
|
11483
|
+
const steps2 = Math.floor(time / this.seekStep);
|
|
11484
|
+
let replayTime = 0;
|
|
11485
|
+
for (let i = 0; i < steps2; i++) replayTime += this.seekStep;
|
|
11486
|
+
const rest2 = time - replayTime;
|
|
11487
|
+
if (rest2 > 0) replayTime += rest2;
|
|
11488
|
+
this.runtimeFrames.pose.trackTime = replayTime;
|
|
11489
|
+
this.applyRuntimeTime();
|
|
11490
|
+
return;
|
|
11491
|
+
}
|
|
9660
11492
|
this.easing = null;
|
|
9661
11493
|
const cache = this.ensureSeekCache();
|
|
9662
11494
|
const stepper = this.tracks ?? this.track;
|
|
@@ -9771,6 +11603,7 @@ class RigPlayer {
|
|
|
9771
11603
|
}
|
|
9772
11604
|
/** What is open: the tracks, the single track, or nothing before `play`. */
|
|
9773
11605
|
stepper() {
|
|
11606
|
+
this.oracle("oracle track operation");
|
|
9774
11607
|
return this.tracks ?? this.track;
|
|
9775
11608
|
}
|
|
9776
11609
|
/** A newly opened track or tracks answer the environment the player stands in; one never set leaves it untouched. */
|
|
@@ -9810,6 +11643,33 @@ class RigPlayer {
|
|
|
9810
11643
|
* the events and `time` stay on the steps themselves (#103).
|
|
9811
11644
|
*/
|
|
9812
11645
|
advance(dt) {
|
|
11646
|
+
if (this.runtimeFrames !== null) {
|
|
11647
|
+
const step2 = dt * this.timeScale;
|
|
11648
|
+
if (this.paused || !(step2 > 0)) return;
|
|
11649
|
+
if (!Number.isFinite(step2)) throw new Error(`advance step must be finite, got ${String(step2)}`);
|
|
11650
|
+
const before2 = this.runtimeFrames.pose.trackTime;
|
|
11651
|
+
const duration = this.runtimeFrames.model.animations.get(this.animation).duration;
|
|
11652
|
+
this.runtimeFrames.pose.trackTime += step2;
|
|
11653
|
+
this.lastStep = step2;
|
|
11654
|
+
this.applyRuntimeTime();
|
|
11655
|
+
const completionClock = this.runtimeCompletionClock;
|
|
11656
|
+
if (this.listeners.event.size > 0) {
|
|
11657
|
+
const events = this.runtimeFrames.pose.events.slice();
|
|
11658
|
+
for (const key of events) {
|
|
11659
|
+
const payload2 = { name: key.name, time: key.time, int: key.int, float: key.float, string: key.string, track: 0 };
|
|
11660
|
+
for (const listener of this.listeners.event) listener(payload2);
|
|
11661
|
+
}
|
|
11662
|
+
}
|
|
11663
|
+
if (this.listeners.complete.size > 0) {
|
|
11664
|
+
const after = completionClock.trackTime;
|
|
11665
|
+
const complete = this.loop ? duration === 0 || Math.floor(after / duration) > Math.floor(before2 / duration) : before2 < duration && after >= duration;
|
|
11666
|
+
if (complete) {
|
|
11667
|
+
const payload2 = { animation: this.animation, track: 0 };
|
|
11668
|
+
for (const listener of this.listeners.complete) listener(payload2);
|
|
11669
|
+
}
|
|
11670
|
+
}
|
|
11671
|
+
return;
|
|
11672
|
+
}
|
|
9813
11673
|
if (this.pose === null) return;
|
|
9814
11674
|
const step = dt * this.timeScale;
|
|
9815
11675
|
if (this.paused || !(step > 0)) return;
|
|
@@ -9833,6 +11693,11 @@ class RigPlayer {
|
|
|
9833
11693
|
}
|
|
9834
11694
|
/** Draws the pose the player holds. */
|
|
9835
11695
|
draw() {
|
|
11696
|
+
if (this.runtimeFrames !== null) {
|
|
11697
|
+
this.runtimeFrames.update(this.poseGeneration, this.color, this.draws, false);
|
|
11698
|
+
this.renderer.render(this.runtimeFrames.live);
|
|
11699
|
+
return;
|
|
11700
|
+
}
|
|
9836
11701
|
if (this.pose === null) return;
|
|
9837
11702
|
this.renderer.render(this.posedFrame());
|
|
9838
11703
|
}
|
|
@@ -9853,14 +11718,27 @@ class RigPlayer {
|
|
|
9853
11718
|
if (name === this.skinName) return;
|
|
9854
11719
|
if (this.beyondBase) throw new Error("skin cannot change after a mix, another track, a queue or a clear: the player cannot replay those to the current time — set it before composing, or play again");
|
|
9855
11720
|
const view = name === void 0 ? underNoSkin(this.read.doc) : underSkin(this.read.doc, name);
|
|
11721
|
+
this.oracle("skin change");
|
|
9856
11722
|
this.view = view;
|
|
9857
11723
|
this.source = frameSource(view, this.read.parsed);
|
|
11724
|
+
this.selectRuntime();
|
|
9858
11725
|
this.skinName = name;
|
|
9859
11726
|
this.setup = null;
|
|
9860
11727
|
this.easing = null;
|
|
9861
11728
|
this.clearSeekCache();
|
|
9862
11729
|
if (this.animation !== null && (this.track !== null || this.tracks !== null)) {
|
|
9863
11730
|
const time = this.time;
|
|
11731
|
+
const animation = this.animation;
|
|
11732
|
+
const animationTime = this.entry(0).animationTime;
|
|
11733
|
+
if (this.tryRuntimePlay(animation, this.loop)) {
|
|
11734
|
+
const frames = this.runtimeFrames;
|
|
11735
|
+
frames.pose.restore(animation, time, animationTime);
|
|
11736
|
+
this.poseGeneration++;
|
|
11737
|
+
this.cachedIdentity = null;
|
|
11738
|
+
this.runtimeReadback = null;
|
|
11739
|
+
frames.update(this.poseGeneration, this.color, this.draws);
|
|
11740
|
+
return;
|
|
11741
|
+
}
|
|
9864
11742
|
this.reopen();
|
|
9865
11743
|
this.stepTo(time);
|
|
9866
11744
|
} else if (this.rawPose !== null) {
|
|
@@ -10246,6 +12124,7 @@ class RigPlayer {
|
|
|
10246
12124
|
* before changing anything, then applies their composition to the held pose.
|
|
10247
12125
|
*/
|
|
10248
12126
|
activate(sets, physics, ease = 0) {
|
|
12127
|
+
this.oracle("attachment sets");
|
|
10249
12128
|
for (let i = 0; i < sets.length; i++) {
|
|
10250
12129
|
for (let j = 0; j < i; j++) {
|
|
10251
12130
|
const other = this.touched(sets[j]);
|
|
@@ -10347,6 +12226,7 @@ class RigPlayer {
|
|
|
10347
12226
|
}
|
|
10348
12227
|
set adjust(fn) {
|
|
10349
12228
|
if (fn !== null && typeof fn !== "function") throw new Error(`adjust must be a function or null, got ${typeof fn}`);
|
|
12229
|
+
this.oracle("local adjustment");
|
|
10350
12230
|
this.adjustFn = fn;
|
|
10351
12231
|
this.clearSeekCache();
|
|
10352
12232
|
this.repose();
|
|
@@ -10359,6 +12239,10 @@ class RigPlayer {
|
|
|
10359
12239
|
* changed, then `draw`. Nothing before `play`.
|
|
10360
12240
|
*/
|
|
10361
12241
|
repose() {
|
|
12242
|
+
if (this.runtimeFrames !== null) {
|
|
12243
|
+
this.applyRuntimeTime();
|
|
12244
|
+
return;
|
|
12245
|
+
}
|
|
10362
12246
|
this.clearSeekCache();
|
|
10363
12247
|
const stepper = this.tracks ?? this.track;
|
|
10364
12248
|
if (stepper === null || this.pose === null) return;
|
|
@@ -10401,7 +12285,7 @@ class RigPlayer {
|
|
|
10401
12285
|
* mode; nothing is computed until called.
|
|
10402
12286
|
*/
|
|
10403
12287
|
bounds() {
|
|
10404
|
-
const frame2 = this.pose === null ? frameOf(this.source, this.posed(), this.color, this.draws) : this.posedFrame();
|
|
12288
|
+
const frame2 = this.runtimeFrames !== null ? this.posedFrame() : this.pose === null ? frameOf(this.source, this.posed(), this.color, this.draws) : this.posedFrame();
|
|
10405
12289
|
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
10406
12290
|
for (const slot of frame2.order) {
|
|
10407
12291
|
const points = slot.kind === "region" ? slot.corners : slot.vertices;
|
|
@@ -10433,10 +12317,110 @@ class RigPlayer {
|
|
|
10433
12317
|
}
|
|
10434
12318
|
/** The pose the reads above see: the track's, or the setup pose before `play` (posed once). */
|
|
10435
12319
|
posed() {
|
|
12320
|
+
if (this.runtimeFrames !== null && this.animation !== null) {
|
|
12321
|
+
if (this.runtimeReadback === null) {
|
|
12322
|
+
const track = openTrack(this.view, this.animation, { loop: this.loop, reset: "animation", clock: physicsClockStep });
|
|
12323
|
+
this.runtimeReadback = this.time > 0 ? track.step(this.time) : track.first;
|
|
12324
|
+
}
|
|
12325
|
+
return this.runtimeReadback;
|
|
12326
|
+
}
|
|
10436
12327
|
if (this.pose !== null) return this.pose;
|
|
10437
12328
|
if (this.setup === null) this.setup = poseRawSetup(this.view);
|
|
10438
12329
|
return this.activeSet === null ? this.setup : applySetToPose(this.view, this.setup, this.activeSet);
|
|
10439
12330
|
}
|
|
12331
|
+
/** Selected poser, including the named reason when the oracle is required. */
|
|
12332
|
+
get poser() {
|
|
12333
|
+
return this.runtimeFrames !== null ? "runtime" : this.poserLabel === "runtime" ? "oracle: no runtime animation is active" : this.poserLabel;
|
|
12334
|
+
}
|
|
12335
|
+
selectRuntime() {
|
|
12336
|
+
this.runtimeModel = null;
|
|
12337
|
+
if (this.oracleOnly) {
|
|
12338
|
+
this.poserLabel = "oracle: explicitly selected";
|
|
12339
|
+
return;
|
|
12340
|
+
}
|
|
12341
|
+
try {
|
|
12342
|
+
this.runtimeModel = compileRuntime(this.view);
|
|
12343
|
+
this.poserLabel = this.trackMode ? "oracle: track composition" : this.adjustFn !== null ? "oracle: local adjustment" : "runtime";
|
|
12344
|
+
} catch (error) {
|
|
12345
|
+
if (!(error instanceof RuntimeRefusal)) throw error;
|
|
12346
|
+
this.poserLabel = `oracle: ${error.message}`;
|
|
12347
|
+
}
|
|
12348
|
+
}
|
|
12349
|
+
tryRuntimePlay(name, loop) {
|
|
12350
|
+
if (this.runtimeModel === null) return false;
|
|
12351
|
+
const blocked = this.trackMode ? "track composition" : this.adjustFn !== null ? "local adjustment" : this.activeSet !== null ? "attachment sets" : this.easing !== null ? "set easing" : this.env !== null ? "environment configuration" : null;
|
|
12352
|
+
if (blocked !== null) {
|
|
12353
|
+
this.poserLabel = `oracle: ${blocked}`;
|
|
12354
|
+
return false;
|
|
12355
|
+
}
|
|
12356
|
+
if (!this.runtimeModel.animations.has(name)) {
|
|
12357
|
+
const refused = this.runtimeModel.refused.find((value) => value.animation === name);
|
|
12358
|
+
if (refused) this.poserLabel = `oracle: ${new RuntimeRefusal(refused.missing).message}`;
|
|
12359
|
+
return false;
|
|
12360
|
+
}
|
|
12361
|
+
const frames = this.runtimeFrames ?? new RuntimeFrames(this.source, this.runtimeModel);
|
|
12362
|
+
frames.pose.setAnimation(name);
|
|
12363
|
+
frames.pose.apply(0);
|
|
12364
|
+
this.runtimeFrames = frames;
|
|
12365
|
+
this.runtimeCompletionClock = { trackTime: 0 };
|
|
12366
|
+
this.animation = name;
|
|
12367
|
+
this.loop = loop;
|
|
12368
|
+
this.track = null;
|
|
12369
|
+
this.tracks = null;
|
|
12370
|
+
this.pose = null;
|
|
12371
|
+
this.rawPose = null;
|
|
12372
|
+
this.poserLabel = "runtime";
|
|
12373
|
+
this.poseGeneration++;
|
|
12374
|
+
this.cachedIdentity = null;
|
|
12375
|
+
this.runtimeReadback = null;
|
|
12376
|
+
frames.update(this.poseGeneration, this.color, this.draws);
|
|
12377
|
+
return true;
|
|
12378
|
+
}
|
|
12379
|
+
applyRuntimeTime() {
|
|
12380
|
+
const frames = this.runtimeFrames;
|
|
12381
|
+
const duration = frames.model.animations.get(this.animation).duration;
|
|
12382
|
+
const time = frames.pose.trackTime;
|
|
12383
|
+
this.runtimeCompletionClock.trackTime = time;
|
|
12384
|
+
frames.pose.apply(duration === 0 ? 0 : this.loop ? time % duration : Math.min(time, duration));
|
|
12385
|
+
this.poseGeneration++;
|
|
12386
|
+
this.cachedIdentity = null;
|
|
12387
|
+
this.runtimeReadback = null;
|
|
12388
|
+
frames.update(this.poseGeneration, this.color, this.draws);
|
|
12389
|
+
}
|
|
12390
|
+
/** Materialize the old track only at an operation stage 0 cannot execute. */
|
|
12391
|
+
oracle(reason) {
|
|
12392
|
+
if (this.runtimeFrames === null || this.animation === null) return;
|
|
12393
|
+
const time = this.runtimeFrames.pose.trackTime;
|
|
12394
|
+
const generation = this.poseGeneration;
|
|
12395
|
+
this.runtimeFrames = null;
|
|
12396
|
+
this.runtimeReadback = null;
|
|
12397
|
+
if (this.poserLabel === "runtime") this.poserLabel = `oracle: ${reason}`;
|
|
12398
|
+
this.track = openTrack(this.view, this.animation, { loop: this.loop, reset: "animation", adjust: this.adjustHook, clock: physicsClockStep });
|
|
12399
|
+
this.setRawPose(this.track.first);
|
|
12400
|
+
if (time > 0) this.setRawPose(this.track.step(time));
|
|
12401
|
+
this.poseGeneration = generation;
|
|
12402
|
+
this.applyEnvironment(this.track);
|
|
12403
|
+
}
|
|
12404
|
+
/** Borrow buffers until the next pose mutation. Request posedFrame() to retain a snapshot. */
|
|
12405
|
+
liveFrame() {
|
|
12406
|
+
if (this.liveDisposed) throw new Error("LIVE_FRAME_DISPOSED: cannot borrow a frame after dispose");
|
|
12407
|
+
let frame2;
|
|
12408
|
+
if (this.runtimeFrames !== null) {
|
|
12409
|
+
this.runtimeFrames.update(this.poseGeneration, this.color, this.draws, false);
|
|
12410
|
+
frame2 = this.runtimeFrames.live;
|
|
12411
|
+
} else {
|
|
12412
|
+
const identities = new Map(this.slotIdentity().slots.map((slot) => [slot.slot, slot]));
|
|
12413
|
+
frame2 = oracleLiveFrame(this.posedFrame(), identities, this.source.slotIndex);
|
|
12414
|
+
}
|
|
12415
|
+
return this.liveChecks ? this.checkedFrame(frame2) : frame2;
|
|
12416
|
+
}
|
|
12417
|
+
/** Keep lease closures out of the production borrow's call environment. */
|
|
12418
|
+
checkedFrame(frame2) {
|
|
12419
|
+
const generation = this.poseGeneration;
|
|
12420
|
+
const state = this.runtimeFrames;
|
|
12421
|
+
const revision = state?.revision;
|
|
12422
|
+
return checkedLiveFrame(frame2, () => this.poseGeneration === generation && (state === null || state.revision === revision) ? generation : -1);
|
|
12423
|
+
}
|
|
10440
12424
|
/** Monotonic pose generation counter matching `posedFrame().generation` and `slotIdentity().generation`. */
|
|
10441
12425
|
get generation() {
|
|
10442
12426
|
return this.poseGeneration;
|
|
@@ -10450,7 +12434,8 @@ class RigPlayer {
|
|
|
10450
12434
|
*/
|
|
10451
12435
|
posedFrame(slots) {
|
|
10452
12436
|
const draws = slots === void 0 ? this.draws : slots === null ? () => true : typeof slots === "function" ? slots : predicateOf(slots);
|
|
10453
|
-
|
|
12437
|
+
if (this.runtimeFrames !== null) this.runtimeFrames.update(this.poseGeneration, this.color, this.draws, false);
|
|
12438
|
+
const frame2 = this.runtimeFrames !== null ? this.runtimeFrames.snapshot(draws) : this.pose === null ? { order: [] } : frameOf(this.source, this.pose, this.color, draws);
|
|
10454
12439
|
Object.defineProperty(frame2, "generation", {
|
|
10455
12440
|
value: this.poseGeneration,
|
|
10456
12441
|
enumerable: false,
|
|
@@ -10474,7 +12459,7 @@ class RigPlayer {
|
|
|
10474
12459
|
}
|
|
10475
12460
|
const setupPose = this.setup ?? (this.setup = poseRawSetup(this.view));
|
|
10476
12461
|
const pose = this.posed();
|
|
10477
|
-
const raw = this.rawPose ?? setupPose;
|
|
12462
|
+
const raw = this.runtimeFrames !== null ? pose : this.rawPose ?? setupPose;
|
|
10478
12463
|
const shownBySlot = new Map(pose.shown.map((s) => [s.slot, s]));
|
|
10479
12464
|
const setupShownSlots = new Set(setupPose.shown.map((s) => s.slot));
|
|
10480
12465
|
const rawRowBySlot = new Map(raw.slots.map((r) => [r[0], r]));
|
|
@@ -10572,13 +12557,17 @@ class RigPlayer {
|
|
|
10572
12557
|
}
|
|
10573
12558
|
return result;
|
|
10574
12559
|
}
|
|
10575
|
-
if (this.track !== null && this.animation !== null) {
|
|
12560
|
+
if ((this.track !== null || this.runtimeFrames !== null) && this.animation !== null) {
|
|
10576
12561
|
return checkAnimation(this.animation, animationTime) === true;
|
|
10577
12562
|
}
|
|
10578
12563
|
return false;
|
|
10579
12564
|
}
|
|
10580
12565
|
/** Removes the renderer's elements and hands its GPU pages back. The assets stay the caller's. */
|
|
10581
12566
|
dispose() {
|
|
12567
|
+
this.runtimeFrames = null;
|
|
12568
|
+
this.runtimeReadback = null;
|
|
12569
|
+
this.liveDisposed = true;
|
|
12570
|
+
this.poseGeneration++;
|
|
10582
12571
|
this.renderer.dispose();
|
|
10583
12572
|
this.clearSeekCache();
|
|
10584
12573
|
this.cachedIdentity = null;
|