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