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