@rig-c/play 1.20.0 → 1.21.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1880 -123
- package/dist/index.js.map +1 -1
- package/dist/liveFrame.d.ts +2 -2
- package/dist/liveFrame.d.ts.map +1 -1
- package/dist/player.d.ts +28 -0
- package/dist/player.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1556,19 +1556,19 @@ function readPathTimelines(value, label, declared, problems) {
|
|
|
1556
1556
|
function pathWorld(state, c, g) {
|
|
1557
1557
|
return worldVertices(g.vertices, state.world.get(c.slotBone), state.world, g.exact === true ? EXACT_COORDS : Math.fround);
|
|
1558
1558
|
}
|
|
1559
|
-
function onLineBefore(p, x1, y1, x2, y2, out, o) {
|
|
1559
|
+
function onLineBefore$1(p, x1, y1, x2, y2, out, o) {
|
|
1560
1560
|
const r = Math.atan2(y2 - y1, x2 - x1);
|
|
1561
1561
|
out[o] = x1 + p * Math.cos(r);
|
|
1562
1562
|
out[o + 1] = y1 + p * Math.sin(r);
|
|
1563
1563
|
out[o + 2] = r;
|
|
1564
1564
|
}
|
|
1565
|
-
function onLinePast(p, x0, y0, x1, y1, out, o) {
|
|
1565
|
+
function onLinePast$1(p, x0, y0, x1, y1, out, o) {
|
|
1566
1566
|
const r = Math.atan2(y1 - y0, x1 - x0);
|
|
1567
1567
|
out[o] = x1 + p * Math.cos(r);
|
|
1568
1568
|
out[o + 1] = y1 + p * Math.sin(r);
|
|
1569
1569
|
out[o + 2] = r;
|
|
1570
1570
|
}
|
|
1571
|
-
function onCurve(p, x1, y1, cx1, cy1, cx2, cy2, x2, y2, out, o, wantsTangent) {
|
|
1571
|
+
function onCurve$1(p, x1, y1, cx1, cy1, cx2, cy2, x2, y2, out, o, wantsTangent) {
|
|
1572
1572
|
if (Number.isNaN(p)) {
|
|
1573
1573
|
out[o] = x1;
|
|
1574
1574
|
out[o + 1] = y1;
|
|
@@ -1619,12 +1619,12 @@ function positionsOf(state, c, g, spaces, wantsTangent) {
|
|
|
1619
1619
|
} else if (p < 0) {
|
|
1620
1620
|
const [x13, y13] = P(1);
|
|
1621
1621
|
const [x23, y23] = P(2);
|
|
1622
|
-
onLineBefore(p, x13, y13, x23, y23, out, o);
|
|
1622
|
+
onLineBefore$1(p, x13, y13, x23, y23, out, o);
|
|
1623
1623
|
continue;
|
|
1624
1624
|
} else if (p > total2) {
|
|
1625
1625
|
const [x0, y0] = P(n - 3);
|
|
1626
1626
|
const [x13, y13] = P(n - 2);
|
|
1627
|
-
onLinePast(p - total2, x0, y0, x13, y13, out, o);
|
|
1627
|
+
onLinePast$1(p - total2, x0, y0, x13, y13, out, o);
|
|
1628
1628
|
continue;
|
|
1629
1629
|
}
|
|
1630
1630
|
for (; ; curve++) {
|
|
@@ -1643,7 +1643,7 @@ function positionsOf(state, c, g, spaces, wantsTangent) {
|
|
|
1643
1643
|
const [cx12, cy12] = q(1);
|
|
1644
1644
|
const [cx22, cy22] = q(2);
|
|
1645
1645
|
const [x22, y22] = q(3);
|
|
1646
|
-
onCurve(p, x12, y12, cx12, cy12, cx22, cy22, x22, y22, out, o, wantsTangent || i > 0 && space === 0);
|
|
1646
|
+
onCurve$1(p, x12, y12, cx12, cy12, cx22, cy22, x22, y22, out, o, wantsTangent || i > 0 && space === 0);
|
|
1647
1647
|
}
|
|
1648
1648
|
return out;
|
|
1649
1649
|
}
|
|
@@ -1671,11 +1671,11 @@ function positionsOf(state, c, g, spaces, wantsTangent) {
|
|
|
1671
1671
|
curve = 0;
|
|
1672
1672
|
segment = 0;
|
|
1673
1673
|
} else if (p < 0) {
|
|
1674
|
-
onLineBefore(p, chain[0], chain[1], chain[2], chain[3], out, o);
|
|
1674
|
+
onLineBefore$1(p, chain[0], chain[1], chain[2], chain[3], out, o);
|
|
1675
1675
|
continue;
|
|
1676
1676
|
} else if (p > total) {
|
|
1677
1677
|
const e = chain.length;
|
|
1678
|
-
onLinePast(p - total, chain[e - 4], chain[e - 3], chain[e - 2], chain[e - 1], out, o);
|
|
1678
|
+
onLinePast$1(p - total, chain[e - 4], chain[e - 3], chain[e - 2], chain[e - 1], out, o);
|
|
1679
1679
|
continue;
|
|
1680
1680
|
}
|
|
1681
1681
|
for (; ; curve++) {
|
|
@@ -1738,7 +1738,7 @@ function positionsOf(state, c, g, spaces, wantsTangent) {
|
|
|
1738
1738
|
}
|
|
1739
1739
|
break;
|
|
1740
1740
|
}
|
|
1741
|
-
onCurve(p * 0.1, x1, y1, cx1, cy1, cx2, cy2, x2, y2, out, o, wantsTangent || i > 0 && space === 0);
|
|
1741
|
+
onCurve$1(p * 0.1, x1, y1, cx1, cy1, cx2, cy2, x2, y2, out, o, wantsTangent || i > 0 && space === 0);
|
|
1742
1742
|
}
|
|
1743
1743
|
return out;
|
|
1744
1744
|
}
|
|
@@ -2114,10 +2114,10 @@ function posedPhysics(records, timelines, t, last, active, plant = {}, wrapped =
|
|
|
2114
2114
|
function resetCrossed(keys, last, t, wrapped = false) {
|
|
2115
2115
|
return wrapped ? keys.some((k) => k.time > last || k.time <= t) : keys.some((k) => last < k.time && k.time <= t);
|
|
2116
2116
|
}
|
|
2117
|
-
function freshPhysicsState() {
|
|
2117
|
+
function freshPhysicsState$1() {
|
|
2118
2118
|
return { pending: true, lastTime: 0, remaining: 0, ux: 0, uy: 0, cx: 0, cy: 0, tx: 0, ty: 0, xOffset: 0, xLag: 0, xVelocity: 0, yOffset: 0, yLag: 0, yVelocity: 0, rotateOffset: 0, rotateLag: 0, rotateVelocity: 0, scaleOffset: 0, scaleLag: 0, scaleVelocity: 0 };
|
|
2119
2119
|
}
|
|
2120
|
-
function resetPhysicsState(s, time) {
|
|
2120
|
+
function resetPhysicsState$1(s, time) {
|
|
2121
2121
|
s.remaining = 0;
|
|
2122
2122
|
s.lastTime = time;
|
|
2123
2123
|
s.pending = true;
|
|
@@ -2127,19 +2127,21 @@ function resetPhysicsState(s, time) {
|
|
|
2127
2127
|
s.scaleOffset = s.scaleLag = s.scaleVelocity = 0;
|
|
2128
2128
|
}
|
|
2129
2129
|
const DEFAULT_PHYSICS_ENVIRONMENT = Object.freeze({ wind: Object.freeze([1, 0]), gravity: Object.freeze([0, 1]) });
|
|
2130
|
-
function translatePhysics(ctx, x, y) {
|
|
2131
|
-
for (const s of ctx.states
|
|
2130
|
+
function translatePhysics(ctx, x, y, scope) {
|
|
2131
|
+
for (const [name, s] of ctx.states) {
|
|
2132
|
+
if (scope !== void 0 && !scope.has(name)) continue;
|
|
2132
2133
|
s.ux -= x;
|
|
2133
2134
|
s.uy -= y;
|
|
2134
2135
|
s.cx -= x;
|
|
2135
2136
|
s.cy -= y;
|
|
2136
2137
|
}
|
|
2137
2138
|
}
|
|
2138
|
-
function rotatePhysics(ctx, x, y, degrees) {
|
|
2139
|
+
function rotatePhysics(ctx, x, y, degrees, scope) {
|
|
2139
2140
|
const r = degrees * RUNTIME_RAD;
|
|
2140
2141
|
const cos = Math.cos(r);
|
|
2141
2142
|
const sin = Math.sin(r);
|
|
2142
|
-
for (const s of ctx.states
|
|
2143
|
+
for (const [name, s] of ctx.states) {
|
|
2144
|
+
if (scope !== void 0 && !scope.has(name)) continue;
|
|
2143
2145
|
const dx = s.cx - x;
|
|
2144
2146
|
const dy = s.cy - y;
|
|
2145
2147
|
const tx = dx * cos - dy * sin - dx;
|
|
@@ -2153,13 +2155,13 @@ function rotatePhysics(ctx, x, y, degrees) {
|
|
|
2153
2155
|
function physicsState(ctx, name) {
|
|
2154
2156
|
let s = ctx.states.get(name);
|
|
2155
2157
|
if (s === void 0) {
|
|
2156
|
-
s = freshPhysicsState();
|
|
2158
|
+
s = freshPhysicsState$1();
|
|
2157
2159
|
ctx.states.set(name, s);
|
|
2158
2160
|
}
|
|
2159
2161
|
return s;
|
|
2160
2162
|
}
|
|
2161
|
-
const PI2 = RUNTIME_PI * 2;
|
|
2162
|
-
const INV_PI2 = 1 / PI2;
|
|
2163
|
+
const PI2$1 = RUNTIME_PI * 2;
|
|
2164
|
+
const INV_PI2$1 = 1 / PI2$1;
|
|
2163
2165
|
function physicsDrives(r) {
|
|
2164
2166
|
return { x: r.x > 0, y: r.y > 0, rotateOrShearX: r.rotate > 0 || r.shearX > 0, scaleX: r.scaleX > 0 };
|
|
2165
2167
|
}
|
|
@@ -2169,7 +2171,7 @@ function stepPhysics(r, w, length, ctx) {
|
|
|
2169
2171
|
const s = physicsState(ctx, r.name);
|
|
2170
2172
|
const { x, y, rotateOrShearX, scaleX } = physicsDrives(r);
|
|
2171
2173
|
const l = length;
|
|
2172
|
-
if (ctx.phase === "reset") resetPhysicsState(s, ctx.time);
|
|
2174
|
+
if (ctx.phase === "reset") resetPhysicsState$1(s, ctx.time);
|
|
2173
2175
|
const delta = Math.max(ctx.time - s.lastTime, 0);
|
|
2174
2176
|
const shown = Math.max(0, 1 - s.remaining / r.step);
|
|
2175
2177
|
s.remaining += delta;
|
|
@@ -2246,7 +2248,7 @@ function stepPhysics(r, w, length, ctx) {
|
|
|
2246
2248
|
mr = (r.rotate + r.shearX) * mix;
|
|
2247
2249
|
const lagged = s.rotateLag * shown;
|
|
2248
2250
|
let rr = Math.atan2(dy + s.ty, dx + s.tx) - ca - (s.rotateOffset - lagged) * mr;
|
|
2249
|
-
s.rotateOffset += (rr - Math.ceil(rr * INV_PI2 - 0.5) * PI2) * i;
|
|
2251
|
+
s.rotateOffset += (rr - Math.ceil(rr * INV_PI2$1 - 0.5) * PI2$1) * i;
|
|
2250
2252
|
rr = (s.rotateOffset - lagged) * mr + ca;
|
|
2251
2253
|
c = Math.cos(rr);
|
|
2252
2254
|
sn = Math.sin(rr);
|
|
@@ -2353,7 +2355,7 @@ function stepPhysicsRecords(records, keyed, t, active, ctx, before) {
|
|
|
2353
2355
|
ctx.last = t;
|
|
2354
2356
|
ctx.setupRecords = new Map(physics.map((r) => [r.name, r]));
|
|
2355
2357
|
ctx.keyedNow = new Set(keyed.flatMap((tl) => tl.kind === "reset" ? [] : (tl.name === EVERY_GLOBAL_PHYSICS ? unnamedPhysicsTargets(physics, tl.kind, (r) => physicsActive(r, active)).map((r) => r.name) : [tl.name]).map((n) => `${n}/${tl.kind === "mass" ? "massInverse" : tl.kind}`)));
|
|
2356
|
-
for (const name of posed.reset) resetPhysicsState(physicsState(ctx, name), before);
|
|
2358
|
+
for (const name of posed.reset) resetPhysicsState$1(physicsState(ctx, name), before);
|
|
2357
2359
|
return records.map((r) => r.kind === "physics" ? posed.records.get(r.name) : r);
|
|
2358
2360
|
}
|
|
2359
2361
|
function freshStepContext(step) {
|
|
@@ -2638,7 +2640,7 @@ function applySliderPhysics(r, at, alpha, target) {
|
|
|
2638
2640
|
const named = target.records.get(tl.name);
|
|
2639
2641
|
const targets = tl.name === EVERY_GLOBAL_PHYSICS ? unnamedPhysicsTargets([...target.records.values()], tl.kind, target.active) : named !== void 0 && target.active(named) ? [named] : [];
|
|
2640
2642
|
if (tl.kind === "reset") {
|
|
2641
|
-
if (!rules.sliderPhysicsResetIsDead && tl.keys.some((k) => k.time <= at)) for (const p of targets) resetPhysicsState(physicsState(target.ctx, p.name), target.ctx.time);
|
|
2643
|
+
if (!rules.sliderPhysicsResetIsDead && tl.keys.some((k) => k.time <= at)) for (const p of targets) resetPhysicsState$1(physicsState(target.ctx, p.name), target.ctx.time);
|
|
2642
2644
|
continue;
|
|
2643
2645
|
}
|
|
2644
2646
|
const i = keyIndexAt(tl.keys, at);
|
|
@@ -5248,17 +5250,30 @@ function setEnvironmentOn(ctx, env, who) {
|
|
|
5248
5250
|
function environmentOf(ctx) {
|
|
5249
5251
|
return ctx.env ?? DEFAULT_PHYSICS_ENVIRONMENT;
|
|
5250
5252
|
}
|
|
5251
|
-
function
|
|
5253
|
+
function checkedPhysicsScope(doc, scope, who) {
|
|
5254
|
+
if (scope === void 0) return void 0;
|
|
5255
|
+
if (!Array.isArray(scope)) throw new CoreInputError(`${who} physics movement scope is ${String(scope)}, not an array of physics constraint names`);
|
|
5256
|
+
const selected = /* @__PURE__ */ new Set();
|
|
5257
|
+
for (const name of scope) {
|
|
5258
|
+
if (typeof name !== "string") throw new CoreInputError(`${who} physics movement scope name ${String(name)} is not a string`);
|
|
5259
|
+
const constraint = doc.constraints.find((c) => c.kind === "physics" && c.name === name) ?? doc.constraints.find((c) => c.name === name);
|
|
5260
|
+
if (constraint === void 0) throw new CoreInputError(`${who} physics movement scope: no constraint "${name}"; this document declares [${doc.constraints.map((c) => c.name).join(", ")}]`);
|
|
5261
|
+
if (constraint.kind !== "physics") throw new CoreInputError(`${who} physics movement scope: constraint "${name}" is ${constraint.kind}, not physics`);
|
|
5262
|
+
selected.add(name);
|
|
5263
|
+
}
|
|
5264
|
+
return selected;
|
|
5265
|
+
}
|
|
5266
|
+
function translateOn(ctx, x, y, who, scope) {
|
|
5252
5267
|
for (const [k, v] of [["x", x], ["y", y]]) {
|
|
5253
5268
|
if (typeof v !== "number" || !Number.isFinite(v)) throw new CoreInputError(`${who} physicsTranslate's ${k} is ${String(v)}, not a finite distance in the skeleton's world`);
|
|
5254
5269
|
}
|
|
5255
|
-
translatePhysics(ctx, x, y);
|
|
5270
|
+
translatePhysics(ctx, x, y, scope);
|
|
5256
5271
|
}
|
|
5257
|
-
function rotateOn(ctx, x, y, degrees, who) {
|
|
5272
|
+
function rotateOn(ctx, x, y, degrees, who, scope) {
|
|
5258
5273
|
for (const [k, v] of [["x", x], ["y", y], ["degrees", degrees]]) {
|
|
5259
5274
|
if (typeof v !== "number" || !Number.isFinite(v)) throw new CoreInputError(`${who} physicsRotate's ${k} is ${String(v)}, not a finite number`);
|
|
5260
5275
|
}
|
|
5261
|
-
rotatePhysics(ctx, x, y, degrees);
|
|
5276
|
+
rotatePhysics(ctx, x, y, degrees, scope);
|
|
5262
5277
|
}
|
|
5263
5278
|
function loopedTime(trackTime, duration) {
|
|
5264
5279
|
return duration === 0 ? 0 : trackTime % duration;
|
|
@@ -5468,13 +5483,13 @@ class Track {
|
|
|
5468
5483
|
this.live("clearPhysicsParameters");
|
|
5469
5484
|
this.parameters.clear(name);
|
|
5470
5485
|
}
|
|
5471
|
-
physicsTranslate(x, y) {
|
|
5486
|
+
physicsTranslate(x, y, scope) {
|
|
5472
5487
|
this.live("physicsTranslate");
|
|
5473
|
-
translateOn(this.track.ctx, x, y, "the live track's");
|
|
5488
|
+
translateOn(this.track.ctx, x, y, "the live track's", checkedPhysicsScope(this.track.doc, scope, "the live track's"));
|
|
5474
5489
|
}
|
|
5475
|
-
physicsRotate(x, y, degrees) {
|
|
5490
|
+
physicsRotate(x, y, degrees, scope) {
|
|
5476
5491
|
this.live("physicsRotate");
|
|
5477
|
-
rotateOn(this.track.ctx, x, y, degrees, "the live track's");
|
|
5492
|
+
rotateOn(this.track.ctx, x, y, degrees, "the live track's", checkedPhysicsScope(this.track.doc, scope, "the live track's"));
|
|
5478
5493
|
}
|
|
5479
5494
|
/** A refused track refuses every call that would move it, naming the step it was refused at. */
|
|
5480
5495
|
live(call) {
|
|
@@ -6426,13 +6441,13 @@ class Tracks {
|
|
|
6426
6441
|
this.live("clearPhysicsParameters");
|
|
6427
6442
|
this.parameters.clear(name);
|
|
6428
6443
|
}
|
|
6429
|
-
physicsTranslate(x, y) {
|
|
6444
|
+
physicsTranslate(x, y, scope) {
|
|
6430
6445
|
this.live("physicsTranslate");
|
|
6431
|
-
translateOn(this.path.ctx, x, y, "the live tracks'");
|
|
6446
|
+
translateOn(this.path.ctx, x, y, "the live tracks'", checkedPhysicsScope(this.doc, scope, "the live tracks'"));
|
|
6432
6447
|
}
|
|
6433
|
-
physicsRotate(x, y, degrees) {
|
|
6448
|
+
physicsRotate(x, y, degrees, scope) {
|
|
6434
6449
|
this.live("physicsRotate");
|
|
6435
|
-
rotateOn(this.path.ctx, x, y, degrees, "the live tracks'");
|
|
6450
|
+
rotateOn(this.path.ctx, x, y, degrees, "the live tracks'", checkedPhysicsScope(this.doc, scope, "the live tracks'"));
|
|
6436
6451
|
}
|
|
6437
6452
|
live(call) {
|
|
6438
6453
|
if (this.refused !== null) throw new CoreInputError(`the live tracks were refused at ${this.refused}, after their entries and physics had moved; open them again (${call} not made)`);
|
|
@@ -9030,26 +9045,30 @@ function compileCurve(keys, channels) {
|
|
|
9030
9045
|
for (let i = 0; i < n; i++) {
|
|
9031
9046
|
const a = keys[i];
|
|
9032
9047
|
times2[i] = a.time;
|
|
9033
|
-
|
|
9034
|
-
|
|
9048
|
+
const av = a.values;
|
|
9049
|
+
if (av !== void 0) for (let c = 0; c < channels; c++) values2[i * channels + c] = av[c];
|
|
9050
|
+
const curve = a.curve ?? "stepped";
|
|
9035
9051
|
kinds[i] = curve === "linear" ? CURVE_LINEAR : curve === "stepped" ? CURVE_STEPPED : CURVE_BEZIER;
|
|
9036
9052
|
if (typeof curve === "string" || i + 1 >= n) continue;
|
|
9037
9053
|
const b = keys[i + 1];
|
|
9054
|
+
const aStated = a.stated;
|
|
9055
|
+
const bStated = b.stated;
|
|
9056
|
+
const bValues = b.values;
|
|
9038
9057
|
for (let c = 0; c < channels; c++) {
|
|
9039
|
-
const inner = bezierPolyline(
|
|
9058
|
+
const inner = bezierPolyline(aStated.time, aStated.values[c], curve[c * 4], curve[c * 4 + 1], curve[c * 4 + 2], curve[c * 4 + 3], bStated.time, bStated.values[c]);
|
|
9040
9059
|
const o = (i * channels + c) * POLYLINE;
|
|
9041
9060
|
polylines[o] = a.time;
|
|
9042
|
-
polylines[o + 1] =
|
|
9061
|
+
polylines[o + 1] = av[c];
|
|
9043
9062
|
for (let q = 0; q < inner.length; q++) polylines[o + 2 + q] = inner[q];
|
|
9044
9063
|
polylines[o + POLYLINE - 2] = b.time;
|
|
9045
|
-
polylines[o + POLYLINE - 1] =
|
|
9064
|
+
polylines[o + POLYLINE - 1] = bValues[c];
|
|
9046
9065
|
}
|
|
9047
9066
|
}
|
|
9048
9067
|
return { keys: n, channels, times: times2, values: values2, kinds, polylines };
|
|
9049
9068
|
}
|
|
9050
9069
|
function keyAt(curve, cursors, slot, t) {
|
|
9051
9070
|
const times2 = curve.times;
|
|
9052
|
-
let i = cursors[slot];
|
|
9071
|
+
let i = Math.min(cursors[slot], curve.keys - 1);
|
|
9053
9072
|
while (i >= 0 && times2[i] > t) i--;
|
|
9054
9073
|
while (i + 1 < curve.keys && times2[i + 1] <= t) i++;
|
|
9055
9074
|
cursors[slot] = i;
|
|
@@ -9104,7 +9123,7 @@ function deformFraction(curve, i, t) {
|
|
|
9104
9123
|
if (q === POLYLINE - 2) return y0 + (1 - y0) * (t - x0) / (x1 - x0);
|
|
9105
9124
|
return y0 + (t - x0) / (x1 - x0) * (y1 - y0);
|
|
9106
9125
|
}
|
|
9107
|
-
const RUNTIME_STAGE =
|
|
9126
|
+
const RUNTIME_STAGE = 9;
|
|
9108
9127
|
function missingText(m) {
|
|
9109
9128
|
return `${m.feature}: ${m.detail}${m.animation === null ? "" : ` (animation "${m.animation}")`}`;
|
|
9110
9129
|
}
|
|
@@ -9147,6 +9166,15 @@ const SCALE_Y_VOLUME = 2;
|
|
|
9147
9166
|
const IK_BEND_POSITIVE = 1;
|
|
9148
9167
|
const IK_COMPRESS = 2;
|
|
9149
9168
|
const IK_STRETCH = 4;
|
|
9169
|
+
const PATH_POSITION_FIXED = 0;
|
|
9170
|
+
const PATH_POSITION_PERCENT = 1;
|
|
9171
|
+
const PATH_SPACING_LENGTH = 0;
|
|
9172
|
+
const PATH_SPACING_FIXED = 1;
|
|
9173
|
+
const PATH_SPACING_PERCENT = 2;
|
|
9174
|
+
const PATH_SPACING_PROPORTIONAL = 3;
|
|
9175
|
+
const PATH_ROTATE_TANGENT = 0;
|
|
9176
|
+
const PATH_ROTATE_CHAIN = 1;
|
|
9177
|
+
const PATH_ROTATE_CHAIN_SCALE = 2;
|
|
9150
9178
|
const RAD = RUNTIME_PI / 180;
|
|
9151
9179
|
const BONE_KINDS = {
|
|
9152
9180
|
rotate: [[L_ROTATION, 0]],
|
|
@@ -9160,7 +9188,7 @@ const BONE_KINDS = {
|
|
|
9160
9188
|
shearx: [[L_SHEAR_X, 0]],
|
|
9161
9189
|
sheary: [[L_SHEAR_Y, 0]]
|
|
9162
9190
|
};
|
|
9163
|
-
const COLOUR_CHANNELS = { rgba: [0, 1, 2, 3], rgb: [0, 1, 2], alpha: [3] };
|
|
9191
|
+
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] };
|
|
9164
9192
|
class Refusals {
|
|
9165
9193
|
constructor(admitted) {
|
|
9166
9194
|
this.admitted = admitted;
|
|
@@ -9182,7 +9210,7 @@ class Refusals {
|
|
|
9182
9210
|
function compileRuntime(doc, options = {}, plant = {}) {
|
|
9183
9211
|
const refusals = new Refusals(new Set(plant.admit ?? []));
|
|
9184
9212
|
const ignoreEvents = (options.ignore ?? []).includes("events");
|
|
9185
|
-
const absentWhy = constraintsAbsentWhy(doc);
|
|
9213
|
+
const absentWhy = constraintsAbsentWhy(doc) ?? pathAnimationsWhy(doc) ?? steppedPreviousPassWhy(doc);
|
|
9186
9214
|
if (absentWhy !== null) refusals.note("oracle-absent", `the oracle leaves the bones out: ${absentWhy}`);
|
|
9187
9215
|
const nb = doc.bones.length;
|
|
9188
9216
|
const boneIndex2 = new Map(doc.bones.map((b, i) => [b.name, i]));
|
|
@@ -9191,6 +9219,12 @@ function compileRuntime(doc, options = {}, plant = {}) {
|
|
|
9191
9219
|
if (i === void 0) throw new RuntimeInputError(`bone "${name}" is not a bone of this document (readModel refuses it first)`);
|
|
9192
9220
|
return i;
|
|
9193
9221
|
};
|
|
9222
|
+
const slotIndex = new Map(doc.slots.map((s, i) => [s.name, i]));
|
|
9223
|
+
const slotIndexOf = (name) => {
|
|
9224
|
+
const i = slotIndex.get(name);
|
|
9225
|
+
if (i === void 0) throw new RuntimeInputError(`slot "${name}" is not a slot of this document (readModel refuses it first)`);
|
|
9226
|
+
return i;
|
|
9227
|
+
};
|
|
9194
9228
|
const active = activeBones(doc);
|
|
9195
9229
|
const parents = new Int32Array(nb);
|
|
9196
9230
|
const modes = new Uint8Array(nb);
|
|
@@ -9225,27 +9259,35 @@ function compileRuntime(doc, options = {}, plant = {}) {
|
|
|
9225
9259
|
}
|
|
9226
9260
|
subtreeStart[nb] = flat.length;
|
|
9227
9261
|
const constraints = [];
|
|
9228
|
-
|
|
9229
|
-
|
|
9230
|
-
|
|
9262
|
+
const records = constraintRecords(doc);
|
|
9263
|
+
const skipped = /* @__PURE__ */ new Set();
|
|
9264
|
+
records.forEach((r, i) => {
|
|
9265
|
+
if (constraintInactiveWhy(r, active) !== null) skipped.add(i);
|
|
9266
|
+
});
|
|
9267
|
+
const pathPlan = records.some((r) => r.kind === "path") ? slotBonePlan(doc.bones, active, records, skipped) : /* @__PURE__ */ new Map();
|
|
9268
|
+
for (const r of records) {
|
|
9231
9269
|
const applied = constraintInactiveWhy(r, active) === null;
|
|
9232
|
-
const bones = Int32Array.from(r.bones.map(indexOf));
|
|
9233
|
-
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`);
|
|
9270
|
+
const bones = Int32Array.from((r.kind === "physics" ? [r.bone] : r.kind === "slider" ? r.timelines.bones.filter((t) => t.timelines.length > 0).map((t) => t.name) : r.bones).map(indexOf));
|
|
9234
9271
|
const index = constraints.length;
|
|
9272
|
+
if (r.kind === "physics") {
|
|
9273
|
+
constraints.push({ kind: "physics", name: r.name, index, applied, bones, setup: r });
|
|
9274
|
+
continue;
|
|
9275
|
+
}
|
|
9276
|
+
if (r.kind === "slider") {
|
|
9277
|
+
constraints.push({ kind: "slider", name: r.name, index, applied, bones, setup: r, bone: r.bone === null ? -1 : indexOf(r.bone), property: TRANSFORM_PROPERTIES.indexOf(r.property), animation: null });
|
|
9278
|
+
continue;
|
|
9279
|
+
}
|
|
9280
|
+
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`);
|
|
9235
9281
|
if (r.kind === "ik") constraints.push(compileIk(r, index, applied, bones, indexOf));
|
|
9236
|
-
else {
|
|
9237
|
-
|
|
9238
|
-
|
|
9239
|
-
const
|
|
9240
|
-
|
|
9241
|
-
for (let k = 0; k < bones.length; k++) {
|
|
9242
|
-
const m = modes[bones[k]];
|
|
9243
|
-
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`);
|
|
9244
|
-
}
|
|
9245
|
-
}
|
|
9282
|
+
else if (r.kind === "path") {
|
|
9283
|
+
constraints.push(compilePath(r, index, applied, bones, indexOf, slotIndexOf, pathPlan.get(index) ?? null));
|
|
9284
|
+
} else {
|
|
9285
|
+
const noScaleWorld = !r.localTarget && bones.some((b) => parents[b] >= 0 && (modes[b] === MODE_NO_SCALE || modes[b] === MODE_NO_SCALE_OR_REFLECTION));
|
|
9286
|
+
const t = compileTransform(r, index, applied && !(plant.omitLocalTransform && (r.localSource || r.localTarget)) && !(plant.omitNoScaleWorldTransform && noScaleWorld) && !(plant.omitAdditiveTransform && r.additive), bones, indexOf);
|
|
9246
9287
|
constraints.push(t);
|
|
9247
9288
|
}
|
|
9248
9289
|
}
|
|
9290
|
+
const hasClipping = doc.skins.some((s) => Object.values(s.attachments).some((table) => Object.values(table).some((at) => at.geometry?.kind === "clipping")));
|
|
9249
9291
|
const attachments = [];
|
|
9250
9292
|
let vertexCount = 0;
|
|
9251
9293
|
let deformCount = 0;
|
|
@@ -9272,8 +9314,9 @@ function compileRuntime(doc, options = {}, plant = {}) {
|
|
|
9272
9314
|
const at = { timeline: timelineIdentity(slot.name, shown), deformOffset: deformCount, slot: si, name: row.name, skin: shown.skin, placeholder: shown.placeholder, path: CORE_REGION_KINDS.has(shown.record.kind) ? row.path : null, bone: indexOf(slot.bone), offset: vertexCount };
|
|
9273
9315
|
let made = null;
|
|
9274
9316
|
let missing = null;
|
|
9275
|
-
if (g?.kind === "clipping")
|
|
9276
|
-
|
|
9317
|
+
if (g?.kind === "clipping") {
|
|
9318
|
+
made = { ...meshAttachment(at, g.vertices, indexOf, plant.bindingIndex, nb), clip: { end: g.end, inverse: g.inverse, convex: g.convex } };
|
|
9319
|
+
} else if (g?.kind === "region") {
|
|
9277
9320
|
const r = g.region;
|
|
9278
9321
|
if (r.atlas === null) missing = ["null-atlas-region", `slot "${slot.name}" shows region "${row.name}", whose atlas rectangle the model states as null`];
|
|
9279
9322
|
else {
|
|
@@ -9307,8 +9350,18 @@ function compileRuntime(doc, options = {}, plant = {}) {
|
|
|
9307
9350
|
const source = g.kind === "mesh" ? g.vertices : sourceOf(g.skin, g.slot, g.source);
|
|
9308
9351
|
if (typeof source === "string") throw new RuntimeInputError(`slot "${slot.name}": ${source} (readModel refuses it first)`);
|
|
9309
9352
|
made = meshAttachment(at, source, indexOf, plant.bindingIndex, nb);
|
|
9353
|
+
} else if (g?.kind === "path") {
|
|
9354
|
+
made = pathAttachment(at, g.vertices, indexOf, plant.bindingIndex, nb);
|
|
9310
9355
|
}
|
|
9311
9356
|
if (made === null) made = vertexAttachment(at, DRAW_NONE, new Float64Array(0), 0);
|
|
9357
|
+
if (hasClipping) {
|
|
9358
|
+
if (g?.kind === "region") made = { ...made, clipTopology: { triangles: new Int32Array([0, 1, 2, 2, 3, 0]), uvs: new Float64Array([0, 1, 0, 0, 1, 0, 1, 1]) } };
|
|
9359
|
+
else if (g?.kind === "mesh" || g?.kind === "linkedmesh") {
|
|
9360
|
+
const mesh = g.kind === "mesh" ? g : doc.skins.find((s) => s.name === g.skin)?.attachments[g.slot]?.[g.source]?.geometry;
|
|
9361
|
+
if (mesh?.kind !== "mesh") throw new RuntimeInputError("clipping topology: linked source is not a mesh (readModel refuses it first)");
|
|
9362
|
+
made = { ...made, clipTopology: { triangles: Int32Array.from(mesh.triangles), uvs: Float64Array.from(mesh.uvs) } };
|
|
9363
|
+
}
|
|
9364
|
+
}
|
|
9312
9365
|
attachments.push(made);
|
|
9313
9366
|
vertexCount += made.count;
|
|
9314
9367
|
deformCount += made.deformCount;
|
|
@@ -9328,7 +9381,21 @@ function compileRuntime(doc, options = {}, plant = {}) {
|
|
|
9328
9381
|
blend: readBlend(slot)
|
|
9329
9382
|
};
|
|
9330
9383
|
});
|
|
9331
|
-
|
|
9384
|
+
let hasPath = false;
|
|
9385
|
+
let maxPathSpaces = 0;
|
|
9386
|
+
let maxPathVertices = 0;
|
|
9387
|
+
for (const c of constraints) {
|
|
9388
|
+
if (c.kind !== "path") continue;
|
|
9389
|
+
hasPath = true;
|
|
9390
|
+
c.attachment = c.setup.path === null ? -1 : slots[c.slot].setupAttachment;
|
|
9391
|
+
const spaceCount = c.rotateMode === PATH_ROTATE_TANGENT ? c.bones.length : c.bones.length + 1;
|
|
9392
|
+
if (spaceCount > maxPathSpaces) maxPathSpaces = spaceCount;
|
|
9393
|
+
if (c.attachment >= 0) {
|
|
9394
|
+
const at = attachments[c.attachment];
|
|
9395
|
+
c.pathVertices = at.bindingCounts.length > 0 ? at.bindingCounts.length * 2 : at.local.length;
|
|
9396
|
+
if (c.pathVertices > maxPathVertices) maxPathVertices = c.pathVertices;
|
|
9397
|
+
}
|
|
9398
|
+
}
|
|
9332
9399
|
const animations = /* @__PURE__ */ new Map();
|
|
9333
9400
|
const refused = [];
|
|
9334
9401
|
let cursorMax = 0;
|
|
@@ -9341,11 +9408,25 @@ function compileRuntime(doc, options = {}, plant = {}) {
|
|
|
9341
9408
|
cursorMax = Math.max(cursorMax, compiled.cursors);
|
|
9342
9409
|
}
|
|
9343
9410
|
}
|
|
9411
|
+
for (const c of constraints) if (c.kind === "slider") {
|
|
9412
|
+
const a = animations.get(c.setup.animation);
|
|
9413
|
+
if (a !== void 0) c.animation = a;
|
|
9414
|
+
else for (const m of refusals.byAnimation.get(c.setup.animation) ?? []) refusals.note(m.feature, `slider "${c.name}" applies refused animation "${c.setup.animation}": ${m.detail}`);
|
|
9415
|
+
}
|
|
9344
9416
|
if (refusals.model.length > 0) throw new RuntimeRefusal(refusals.model);
|
|
9345
9417
|
const names = doc.animations.map((a) => a.name);
|
|
9346
9418
|
return {
|
|
9419
|
+
omitPhysics: plant.omitPhysics === true,
|
|
9420
|
+
sliderAsTrack: plant.sliderAsTrack === true,
|
|
9347
9421
|
omitDeform: plant.omitDeform === true,
|
|
9348
9422
|
deformAfterWeights: plant.deformAfterWeights === true,
|
|
9423
|
+
omitPath: plant.omitPath === true,
|
|
9424
|
+
ignorePhysicsScope: plant.ignorePhysicsScope === true,
|
|
9425
|
+
omitDrawOrder: plant.omitDrawOrder === true,
|
|
9426
|
+
omitDarkColour: plant.omitDarkColour === true,
|
|
9427
|
+
hasPath,
|
|
9428
|
+
maxPathSpaces,
|
|
9429
|
+
maxPathVertices,
|
|
9349
9430
|
deformCount,
|
|
9350
9431
|
eventWrapOffByOne: plant.eventWrapOffByOne === true,
|
|
9351
9432
|
restoreEventsFromStart: plant.restoreEventsFromStart === true,
|
|
@@ -9404,6 +9485,10 @@ function meshAttachment(at, vertices, indexOf, rewrite, bones) {
|
|
|
9404
9485
|
}
|
|
9405
9486
|
return { ...at, deformCount: ids.length * 2, draw: DRAW_WEIGHTED, local: NO_NUMBERS, count: counts.length * 2, bindingCounts: Int32Array.from(counts), bindingBones: Int32Array.from(ids), bindingX: Float64Array.from(xs), bindingY: Float64Array.from(ys), bindingWeights: Float64Array.from(ws) };
|
|
9406
9487
|
}
|
|
9488
|
+
function pathAttachment(at, vertices, indexOf, rewrite, bones) {
|
|
9489
|
+
const m = meshAttachment(at, vertices, indexOf, rewrite, bones);
|
|
9490
|
+
return { ...m, draw: DRAW_NONE, count: 0 };
|
|
9491
|
+
}
|
|
9407
9492
|
function compileIk(r, index, applied, bones, indexOf) {
|
|
9408
9493
|
return {
|
|
9409
9494
|
kind: "ik",
|
|
@@ -9421,6 +9506,34 @@ function compileIk(r, index, applied, bones, indexOf) {
|
|
|
9421
9506
|
function ikFlags(bendPositive, compress, stretch) {
|
|
9422
9507
|
return (bendPositive ? IK_BEND_POSITIVE : 0) | (compress ? IK_COMPRESS : 0) | (stretch ? IK_STRETCH : 0);
|
|
9423
9508
|
}
|
|
9509
|
+
function compilePath(r, index, applied, bones, indexOf, slotIndexOf, ev) {
|
|
9510
|
+
return {
|
|
9511
|
+
kind: "path",
|
|
9512
|
+
name: r.name,
|
|
9513
|
+
index,
|
|
9514
|
+
applied,
|
|
9515
|
+
bones,
|
|
9516
|
+
slot: slotIndexOf(r.slot),
|
|
9517
|
+
slotBone: indexOf(r.slotBone),
|
|
9518
|
+
attachment: -1,
|
|
9519
|
+
pathVertices: 0,
|
|
9520
|
+
closed: r.path?.closed ?? false,
|
|
9521
|
+
constantSpeed: r.path?.constantSpeed ?? true,
|
|
9522
|
+
lengths: Float64Array.from(r.path?.lengths ?? []),
|
|
9523
|
+
positionMode: r.positionMode === "fixed" ? PATH_POSITION_FIXED : PATH_POSITION_PERCENT,
|
|
9524
|
+
spacingMode: r.spacingMode === "fixed" ? PATH_SPACING_FIXED : r.spacingMode === "percent" ? PATH_SPACING_PERCENT : r.spacingMode === "proportional" ? PATH_SPACING_PROPORTIONAL : PATH_SPACING_LENGTH,
|
|
9525
|
+
rotateMode: r.rotateMode === "chain" ? PATH_ROTATE_CHAIN : r.rotateMode === "chainScale" ? PATH_ROTATE_CHAIN_SCALE : PATH_ROTATE_TANGENT,
|
|
9526
|
+
offsetRotation: r.offsetRotation,
|
|
9527
|
+
position: r.position,
|
|
9528
|
+
spacing: r.spacing,
|
|
9529
|
+
mixRotate: r.mixRotate,
|
|
9530
|
+
mixX: r.mixX,
|
|
9531
|
+
mixY: r.mixY,
|
|
9532
|
+
slotEventAt: ev === null ? -1 : ev.at,
|
|
9533
|
+
slotEventAfter: ev?.when === "after",
|
|
9534
|
+
setup: r
|
|
9535
|
+
};
|
|
9536
|
+
}
|
|
9424
9537
|
function compileTransform(r, index, applied, bones, indexOf) {
|
|
9425
9538
|
const fromProperty = [];
|
|
9426
9539
|
const fromOffset = [];
|
|
@@ -9459,7 +9572,10 @@ function compileTransform(r, index, applied, bones, indexOf) {
|
|
|
9459
9572
|
toMax: Float64Array.from(toMax),
|
|
9460
9573
|
offsets: Float64Array.from(TRANSFORM_PROPERTIES.map((p) => r.offsets[p])),
|
|
9461
9574
|
mixes: Float64Array.from(TRANSFORM_PROPERTIES.map((p) => r.mixes[p])),
|
|
9462
|
-
clamp: r.clamp
|
|
9575
|
+
clamp: r.clamp,
|
|
9576
|
+
localSource: r.localSource,
|
|
9577
|
+
localTarget: r.localTarget,
|
|
9578
|
+
additive: r.additive
|
|
9463
9579
|
};
|
|
9464
9580
|
}
|
|
9465
9581
|
function compileAnimation(anim, refusals, ignoreEvents, boneIndex2, slotIndex, slots, constraints, doc, resolve) {
|
|
@@ -9469,21 +9585,52 @@ function compileAnimation(anim, refusals, ignoreEvents, boneIndex2, slotIndex, s
|
|
|
9469
9585
|
const deform = tl.attachments.filter((a) => a.deform !== null);
|
|
9470
9586
|
const sequence = tl.attachments.filter((a) => a.sequence !== null);
|
|
9471
9587
|
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);
|
|
9472
|
-
|
|
9588
|
+
let drawOrder = null;
|
|
9589
|
+
if (tl.drawOrder.length > 0) {
|
|
9590
|
+
const ns = slots.length;
|
|
9591
|
+
const orders = new Int32Array(tl.drawOrder.length * ns).fill(-1);
|
|
9592
|
+
const moved = new Uint8Array(ns);
|
|
9593
|
+
for (let i = 0; i < tl.drawOrder.length; i++) {
|
|
9594
|
+
const base = i * ns;
|
|
9595
|
+
moved.fill(0);
|
|
9596
|
+
for (const m of tl.drawOrder[i].moves) {
|
|
9597
|
+
orders[base + m.slot + m.offset] = m.slot;
|
|
9598
|
+
moved[m.slot] = 1;
|
|
9599
|
+
}
|
|
9600
|
+
for (let s = 0, d = 0; s < ns; s++) {
|
|
9601
|
+
if (moved[s] !== 0) continue;
|
|
9602
|
+
while (orders[base + d] !== -1) d++;
|
|
9603
|
+
orders[base + d] = s;
|
|
9604
|
+
d++;
|
|
9605
|
+
}
|
|
9606
|
+
}
|
|
9607
|
+
drawOrder = { curve: compileCurve(tl.drawOrder, 0), cursor: cursors++, orders };
|
|
9608
|
+
}
|
|
9473
9609
|
const lastBy = (targets) => new Map(targets.map((t) => [t.name, t]));
|
|
9474
9610
|
const bones = [];
|
|
9475
|
-
|
|
9611
|
+
const byTarget = /* @__PURE__ */ new Map();
|
|
9612
|
+
const compileBones = (target, forSlider) => {
|
|
9613
|
+
const cached = byTarget.get(target);
|
|
9614
|
+
if (cached !== void 0) return cached;
|
|
9615
|
+
const keyed = [];
|
|
9616
|
+
byTarget.set(target, keyed);
|
|
9476
9617
|
const bone2 = boneIndex2.get(target.name);
|
|
9477
|
-
if (bone2 === void 0)
|
|
9618
|
+
if (bone2 === void 0) return keyed;
|
|
9478
9619
|
for (const t of target.timelines) {
|
|
9479
9620
|
if (t.kind === "inherit") {
|
|
9480
|
-
refusals.note("inherit-timeline", `bone "${target.name}" keys its inherit mode`, name);
|
|
9621
|
+
if (!forSlider) refusals.note("inherit-timeline", `bone "${target.name}" keys its inherit mode`, name);
|
|
9622
|
+
else for (const c of constraints) if (c.kind === "slider" && c.setup.animation === name) {
|
|
9623
|
+
refusals.note("inherit-timeline", `slider "${c.name}" applies animation "${name}" whose bone "${target.name}" keys its inherit mode`);
|
|
9624
|
+
}
|
|
9481
9625
|
continue;
|
|
9482
9626
|
}
|
|
9483
9627
|
const spec = BONE_KINDS[t.kind];
|
|
9484
|
-
|
|
9628
|
+
keyed.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])) });
|
|
9485
9629
|
}
|
|
9486
|
-
|
|
9630
|
+
return keyed;
|
|
9631
|
+
};
|
|
9632
|
+
for (const target of lastBy(tl.bones).values()) bones.push(...compileBones(target, false));
|
|
9633
|
+
const sliderBones = constraints.some((c) => c.kind === "slider" && c.setup.animation === name) ? tl.bones.flatMap((target) => compileBones(target, true)) : [];
|
|
9487
9634
|
const slotTimelines = [];
|
|
9488
9635
|
const keyedSlots = [...lastBy(tl.slots).values()].flatMap((target) => {
|
|
9489
9636
|
const si = slotIndex.get(target.name);
|
|
@@ -9495,8 +9642,6 @@ function compileAnimation(anim, refusals, ignoreEvents, boneIndex2, slotIndex, s
|
|
|
9495
9642
|
if (t.kind === "attachment") {
|
|
9496
9643
|
const ids = Int32Array.from(t.keys.map((k) => resolve(si, k.name ?? null)));
|
|
9497
9644
|
slotTimelines.push({ slot: si, curve: compileCurve(t.keys, 0), cursor: cursors++, attachments: ids, channels: null });
|
|
9498
|
-
} else if (t.kind === "rgba2" || t.kind === "rgb2") {
|
|
9499
|
-
refusals.note("dark-colour-timeline", `slot "${target.name}" keys an ${t.kind} timeline`, name);
|
|
9500
9645
|
} else {
|
|
9501
9646
|
const channels = COLOUR_CHANNELS[t.kind];
|
|
9502
9647
|
slotTimelines.push({ slot: si, curve: compileCurve(t.keys, channels.length), cursor: cursors++, attachments: null, channels: Int32Array.from(channels) });
|
|
@@ -9509,12 +9654,37 @@ function compileAnimation(anim, refusals, ignoreEvents, boneIndex2, slotIndex, s
|
|
|
9509
9654
|
const keyed = anim.constraints.ik.find((x) => x.name === c.name);
|
|
9510
9655
|
if (keyed === void 0) continue;
|
|
9511
9656
|
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))) };
|
|
9512
|
-
} else {
|
|
9657
|
+
} else if (c.kind === "transform") {
|
|
9513
9658
|
const keyed = anim.constraints.transform.find((x) => x.name === c.name);
|
|
9514
9659
|
if (keyed === void 0) continue;
|
|
9515
9660
|
constraintTimelines[c.index] = { constraint: c.index, curve: compileCurve(keyed.keys, TRANSFORM_PROPERTIES.length), cursor: cursors++, flags: null };
|
|
9516
9661
|
}
|
|
9517
9662
|
}
|
|
9663
|
+
const sliders = constraints.map((c) => {
|
|
9664
|
+
if (c.kind !== "slider") return null;
|
|
9665
|
+
const keyed = anim.constraints.slider.find((t) => t.name === c.name);
|
|
9666
|
+
if (keyed === void 0) return null;
|
|
9667
|
+
return { time: keyed.time === null ? null : compileCurve(keyed.time, 1), mix: keyed.mix === null ? null : compileCurve(keyed.mix, 1), timeCursor: cursors++, mixCursor: cursors++ };
|
|
9668
|
+
});
|
|
9669
|
+
const paths = constraints.map((c) => {
|
|
9670
|
+
if (c.kind !== "path") return null;
|
|
9671
|
+
const keyed = anim.constraints.path.find((t) => t.name === c.name);
|
|
9672
|
+
if (keyed === void 0) return null;
|
|
9673
|
+
return {
|
|
9674
|
+
position: keyed.position === void 0 ? null : compileCurve(keyed.position, 1),
|
|
9675
|
+
spacing: keyed.spacing === void 0 ? null : compileCurve(keyed.spacing, 1),
|
|
9676
|
+
mix: keyed.mix === void 0 ? null : compileCurve(keyed.mix, 3),
|
|
9677
|
+
positionCursor: cursors++,
|
|
9678
|
+
spacingCursor: cursors++,
|
|
9679
|
+
mixCursor: cursors++
|
|
9680
|
+
};
|
|
9681
|
+
});
|
|
9682
|
+
const physics = (anim.constraints.physicsKeyed ?? []).map((t) => ({
|
|
9683
|
+
kind: t.kind,
|
|
9684
|
+
cursor: cursors++,
|
|
9685
|
+
curve: compileCurve(t.keys, t.kind === "reset" ? 0 : 1),
|
|
9686
|
+
targets: Int32Array.from(constraints.filter((c) => c.kind === "physics" && c.applied && (t.name === "*" ? t.kind === "reset" || c.setup.global[t.kind] : c.name === t.name)).map((c) => c.index))
|
|
9687
|
+
}));
|
|
9518
9688
|
const deforms = deform.map((a) => {
|
|
9519
9689
|
const g = doc.skins.find((s) => s.name === a.skin)?.attachments[a.slot]?.[a.attachment]?.geometry;
|
|
9520
9690
|
if (g === void 0 || g.kind === "region" || g.kind === "linkedmesh") throw new RuntimeInputError(`deform "${a.skin}/${a.slot}/${a.attachment}" has no vertex geometry (readModel refuses it first)`);
|
|
@@ -9522,15 +9692,613 @@ function compileAnimation(anim, refusals, ignoreEvents, boneIndex2, slotIndex, s
|
|
|
9522
9692
|
const arrays = keys.map((k) => heldArray(g.vertices, k));
|
|
9523
9693
|
return { identity: `${a.skin}/${a.slot}/${a.attachment}`, cursor: cursors++, curve: compileDeformCurve(keys), count: arrays[0].length, held: Float64Array.from(arrays.flat()) };
|
|
9524
9694
|
});
|
|
9525
|
-
return { name, duration: tl.duration, deforms, bones, slots: slotTimelines, constraints: constraintTimelines, cursors, events: tl.events.length, eventKeys: Object.freeze(ignoreEvents ? [] : tl.events.map((key) => Object.freeze({ name: key.name, time: key.time, int: key.int, float: key.float, string: key.string, volume: key.volume ?? 0, balance: key.balance ?? 0 }))) };
|
|
9695
|
+
return { name, duration: tl.duration, physics, sliders, paths, deforms, bones, sliderBones, slots: slotTimelines, drawOrder, 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 }))) };
|
|
9696
|
+
}
|
|
9697
|
+
class RuntimeClipBuffer {
|
|
9698
|
+
constructor(triangles, polygonVertices) {
|
|
9699
|
+
this.vertexCount = 0;
|
|
9700
|
+
this.triangleCount = 0;
|
|
9701
|
+
this.clipped = false;
|
|
9702
|
+
const points = triangles * Math.max(3, polygonVertices * (polygonVertices + 3));
|
|
9703
|
+
if (!Number.isSafeInteger(points) || points < 0) throw new Error("runtime clip: output capacity is not a safe non-negative integer");
|
|
9704
|
+
this.vertices = new Float32Array(points * 2);
|
|
9705
|
+
this.uvs = new Float32Array(points * 2);
|
|
9706
|
+
this.triangles = new Int32Array(points * 3);
|
|
9707
|
+
this.sources = new Int32Array(points);
|
|
9708
|
+
}
|
|
9709
|
+
}
|
|
9710
|
+
class RuntimeClipper {
|
|
9711
|
+
constructor(maxPolygonVertices) {
|
|
9712
|
+
this.maxPolygonVertices = maxPolygonVertices;
|
|
9713
|
+
this.nativeRows = false;
|
|
9714
|
+
this.tri = new Float64Array(6);
|
|
9715
|
+
this.uv = new Float64Array(6);
|
|
9716
|
+
this.regionsCount = 0;
|
|
9717
|
+
this.inverse = false;
|
|
9718
|
+
this.cut = false;
|
|
9719
|
+
this.n = 0;
|
|
9720
|
+
const n = Math.max(3, maxPolygonVertices);
|
|
9721
|
+
this.points = new Float64Array(n * 2);
|
|
9722
|
+
this.ring = new Int32Array(n);
|
|
9723
|
+
this.work = new Int32Array(n);
|
|
9724
|
+
this.sorted = new Int32Array(n);
|
|
9725
|
+
this.hull = new Int32Array(n * 2);
|
|
9726
|
+
this.pieces = new Int32Array(n * n);
|
|
9727
|
+
this.sizes = new Int32Array(n);
|
|
9728
|
+
this.merged = new Int32Array(n * 2);
|
|
9729
|
+
this.regions = new Float64Array(n * n * 2);
|
|
9730
|
+
this.regionSizes = new Int32Array(n);
|
|
9731
|
+
this.a = new Float64Array((n * 2 + 16) * 2);
|
|
9732
|
+
this.b = new Float64Array(this.a.length);
|
|
9733
|
+
this.outside = new Float64Array(this.a.length);
|
|
9734
|
+
}
|
|
9735
|
+
cross(a, b, c) {
|
|
9736
|
+
const p = this.points;
|
|
9737
|
+
return (p[b * 2] - p[a * 2]) * (p[c * 2 + 1] - p[a * 2 + 1]) - (p[b * 2 + 1] - p[a * 2 + 1]) * (p[c * 2] - p[a * 2]);
|
|
9738
|
+
}
|
|
9739
|
+
area(indices, n) {
|
|
9740
|
+
const p = this.points;
|
|
9741
|
+
let area = 0;
|
|
9742
|
+
for (let i = 0; i < n; i++) {
|
|
9743
|
+
const a = indices[i] * 2, b = indices[(i + 1) % n] * 2;
|
|
9744
|
+
area += p[a] * p[b + 1] - p[b] * p[a + 1];
|
|
9745
|
+
}
|
|
9746
|
+
return area;
|
|
9747
|
+
}
|
|
9748
|
+
convex(indices, n, start = 0) {
|
|
9749
|
+
let sign = 0;
|
|
9750
|
+
for (let i = 0; i < n; i++) {
|
|
9751
|
+
const p = this.points, a = indices[start + i] * 2, b = indices[start + (i + 1) % n] * 2, c = indices[start + (i + 2) % n] * 2;
|
|
9752
|
+
const cross = (p[b] - p[a]) * (p[c + 1] - p[b + 1]) - (p[b + 1] - p[a + 1]) * (p[c] - p[b]);
|
|
9753
|
+
if (cross === 0) return false;
|
|
9754
|
+
const next = Math.sign(cross);
|
|
9755
|
+
if (sign === 0) sign = next;
|
|
9756
|
+
else if (sign !== next) return false;
|
|
9757
|
+
}
|
|
9758
|
+
return true;
|
|
9759
|
+
}
|
|
9760
|
+
on(a, b, r) {
|
|
9761
|
+
const p = this.points;
|
|
9762
|
+
return Math.min(p[a * 2], p[b * 2]) <= p[r * 2] && p[r * 2] <= Math.max(p[a * 2], p[b * 2]) && Math.min(p[a * 2 + 1], p[b * 2 + 1]) <= p[r * 2 + 1] && p[r * 2 + 1] <= Math.max(p[a * 2 + 1], p[b * 2 + 1]);
|
|
9763
|
+
}
|
|
9764
|
+
meet(a, b, c, d) {
|
|
9765
|
+
const d1 = this.cross(c, d, a), d2 = this.cross(c, d, b), d3 = this.cross(a, b, c), d4 = this.cross(a, b, d);
|
|
9766
|
+
if ((d1 > 0 && d2 < 0 || d1 < 0 && d2 > 0) && (d3 > 0 && d4 < 0 || d3 < 0 && d4 > 0)) return true;
|
|
9767
|
+
return d1 === 0 && this.on(c, d, a) || d2 === 0 && this.on(c, d, b) || d3 === 0 && this.on(a, b, c) || d4 === 0 && this.on(a, b, d);
|
|
9768
|
+
}
|
|
9769
|
+
region(indices, n, r, start = 0) {
|
|
9770
|
+
const p = this.points;
|
|
9771
|
+
let area = 0;
|
|
9772
|
+
for (let i = 0; i < n; i++) {
|
|
9773
|
+
const a = indices[start + i] * 2, b = indices[start + (i + 1) % n] * 2;
|
|
9774
|
+
area += p[a] * p[b + 1] - p[b] * p[a + 1];
|
|
9775
|
+
}
|
|
9776
|
+
const base = r * this.maxPolygonVertices * 2;
|
|
9777
|
+
for (let i = 0; i < n; i++) {
|
|
9778
|
+
const j = indices[start + (area > 0 ? n - 1 - i : i)] * 2;
|
|
9779
|
+
this.regions[base + i * 2] = p[j];
|
|
9780
|
+
this.regions[base + i * 2 + 1] = p[j + 1];
|
|
9781
|
+
}
|
|
9782
|
+
this.regionSizes[r] = n;
|
|
9783
|
+
}
|
|
9784
|
+
makeHull(count) {
|
|
9785
|
+
const idx = this.sorted, p = this.points, h = this.hull;
|
|
9786
|
+
for (let i = 0; i < count; i++) {
|
|
9787
|
+
const v = this.ring[i];
|
|
9788
|
+
let j = i;
|
|
9789
|
+
while (j > 0) {
|
|
9790
|
+
const u = idx[j - 1];
|
|
9791
|
+
if (p[u * 2] < p[v * 2] || p[u * 2] === p[v * 2] && p[u * 2 + 1] <= p[v * 2 + 1]) break;
|
|
9792
|
+
idx[j] = u;
|
|
9793
|
+
j--;
|
|
9794
|
+
}
|
|
9795
|
+
idx[j] = v;
|
|
9796
|
+
}
|
|
9797
|
+
let used = 0;
|
|
9798
|
+
for (let i = 0; i < count; i++) {
|
|
9799
|
+
const v = idx[i];
|
|
9800
|
+
while (used >= 2 && this.cross(h[used - 2], h[used - 1], v) <= 0) used--;
|
|
9801
|
+
h[used++] = v;
|
|
9802
|
+
}
|
|
9803
|
+
used--;
|
|
9804
|
+
const lower = used;
|
|
9805
|
+
for (let i = count - 1; i >= 0; i--) {
|
|
9806
|
+
const v = idx[i];
|
|
9807
|
+
while (used >= lower + 2 && this.cross(h[used - 2], h[used - 1], v) <= 0) used--;
|
|
9808
|
+
h[used++] = v;
|
|
9809
|
+
}
|
|
9810
|
+
return used - 1;
|
|
9811
|
+
}
|
|
9812
|
+
plan(inverse, convex) {
|
|
9813
|
+
this.nativeRows = false;
|
|
9814
|
+
const n = this.n, p = this.points, ring = this.ring;
|
|
9815
|
+
let repeats = 0, distinct = 0;
|
|
9816
|
+
for (let i = 0; i < n; i++) {
|
|
9817
|
+
ring[i] = i;
|
|
9818
|
+
for (let j2 = i + 1; j2 < n; j2++) if (p[i * 2] === p[j2 * 2] && p[i * 2 + 1] === p[j2 * 2 + 1]) repeats++;
|
|
9819
|
+
const j = (i + 1) % n;
|
|
9820
|
+
if (p[i * 2] !== p[j * 2] || p[i * 2 + 1] !== p[j * 2 + 1]) this.work[distinct++] = i;
|
|
9821
|
+
}
|
|
9822
|
+
if (repeats > 0) {
|
|
9823
|
+
if (!inverse && distinct === n - repeats && distinct >= 3 && this.convex(this.work, distinct)) {
|
|
9824
|
+
this.regionsCount = 1;
|
|
9825
|
+
this.inverse = false;
|
|
9826
|
+
this.region(ring, n, 0);
|
|
9827
|
+
return false;
|
|
9828
|
+
}
|
|
9829
|
+
throw new Error("runtime clip: polygon has repeated vertices unsupported by clipShapeOf");
|
|
9830
|
+
}
|
|
9831
|
+
for (let i = 0; i < n; i++) {
|
|
9832
|
+
const j = (i + 1) % n, k = (i + 2) % n;
|
|
9833
|
+
if (this.cross(i, j, k) === 0 && (p[k * 2] - p[j * 2]) * (p[i * 2] - p[j * 2]) + (p[k * 2 + 1] - p[j * 2 + 1]) * (p[i * 2 + 1] - p[j * 2 + 1]) > 0) throw new Error("runtime clip: neighbouring polygon edges fold back");
|
|
9834
|
+
for (let q = i + 2; q < n; q++) if (!(i === 0 && q === n - 1) && this.meet(i, j, q, (q + 1) % n)) throw new Error("runtime clip: polygon edges cross or touch");
|
|
9835
|
+
}
|
|
9836
|
+
const originalConvex = this.convex(ring, n);
|
|
9837
|
+
this.nativeRows = !inverse && originalConvex;
|
|
9838
|
+
let count = n;
|
|
9839
|
+
for (let changed = true; changed && count > 3; ) {
|
|
9840
|
+
changed = false;
|
|
9841
|
+
for (let i = 0; i < count; i++) if (this.cross(ring[(i + count - 1) % count], ring[i], ring[(i + 1) % count]) === 0) {
|
|
9842
|
+
ring.copyWithin(i, i + 1, count);
|
|
9843
|
+
count--;
|
|
9844
|
+
changed = true;
|
|
9845
|
+
break;
|
|
9846
|
+
}
|
|
9847
|
+
}
|
|
9848
|
+
if (count === 3 && this.cross(ring[0], ring[1], ring[2]) === 0) throw new Error("runtime clip: every polygon vertex lies on one line");
|
|
9849
|
+
this.inverse = inverse;
|
|
9850
|
+
if (inverse || !originalConvex && convex) {
|
|
9851
|
+
this.regionsCount = 1;
|
|
9852
|
+
this.region(this.hull, this.makeHull(count), 0);
|
|
9853
|
+
return false;
|
|
9854
|
+
}
|
|
9855
|
+
if (originalConvex) {
|
|
9856
|
+
for (let i = 0; i < n; i++) ring[i] = i;
|
|
9857
|
+
this.regionsCount = 1;
|
|
9858
|
+
this.region(ring, n, 0);
|
|
9859
|
+
return false;
|
|
9860
|
+
}
|
|
9861
|
+
if (this.area(ring, count) < 0) for (let i = 0; i < count / 2; i++) {
|
|
9862
|
+
const j = count - 1 - i, v = ring[i];
|
|
9863
|
+
ring[i] = ring[j];
|
|
9864
|
+
ring[j] = v;
|
|
9865
|
+
}
|
|
9866
|
+
this.work.set(ring);
|
|
9867
|
+
let left = count, pieces = 0;
|
|
9868
|
+
while (left > 3) {
|
|
9869
|
+
let found = false;
|
|
9870
|
+
for (let k = 0; k < left; k++) {
|
|
9871
|
+
const a = this.work[(k + left - 1) % left], b = this.work[k], c = this.work[(k + 1) % left];
|
|
9872
|
+
if (this.cross(a, b, c) <= 0) continue;
|
|
9873
|
+
let inside = false;
|
|
9874
|
+
for (let j = 0; j < left; j++) {
|
|
9875
|
+
const v = this.work[j];
|
|
9876
|
+
if (v !== a && v !== b && v !== c && this.cross(a, b, v) >= 0 && this.cross(b, c, v) >= 0 && this.cross(c, a, v) >= 0) {
|
|
9877
|
+
inside = true;
|
|
9878
|
+
break;
|
|
9879
|
+
}
|
|
9880
|
+
}
|
|
9881
|
+
if (inside) continue;
|
|
9882
|
+
const base = pieces * this.maxPolygonVertices;
|
|
9883
|
+
this.pieces[base] = a;
|
|
9884
|
+
this.pieces[base + 1] = b;
|
|
9885
|
+
this.pieces[base + 2] = c;
|
|
9886
|
+
this.sizes[pieces++] = 3;
|
|
9887
|
+
this.work.copyWithin(k, k + 1, left);
|
|
9888
|
+
left--;
|
|
9889
|
+
found = true;
|
|
9890
|
+
break;
|
|
9891
|
+
}
|
|
9892
|
+
if (!found) throw new Error("runtime clip: no ear left in a polygon held simple");
|
|
9893
|
+
}
|
|
9894
|
+
for (let i = 0; i < 3; i++) this.pieces[pieces * this.maxPolygonVertices + i] = this.work[i];
|
|
9895
|
+
this.sizes[pieces++] = 3;
|
|
9896
|
+
for (let changed = true; changed; ) {
|
|
9897
|
+
changed = false;
|
|
9898
|
+
search: for (let a = 0; a < pieces; a++) for (let b = a + 1; b < pieces; b++) {
|
|
9899
|
+
const an = this.sizes[a], bn = this.sizes[b], ab = a * this.maxPolygonVertices, bb = b * this.maxPolygonVertices;
|
|
9900
|
+
for (let i = 0; i < an; i++) {
|
|
9901
|
+
const u = this.pieces[ab + i], v = this.pieces[ab + (i + 1) % an];
|
|
9902
|
+
let j = -1;
|
|
9903
|
+
for (let k = 0; k < bn; k++) if (this.pieces[bb + k] === v) {
|
|
9904
|
+
j = k;
|
|
9905
|
+
break;
|
|
9906
|
+
}
|
|
9907
|
+
if (j < 0 || this.pieces[bb + (j + 1) % bn] !== u) continue;
|
|
9908
|
+
let size = 0;
|
|
9909
|
+
for (let k = 0; k <= i; k++) this.merged[size++] = this.pieces[ab + k];
|
|
9910
|
+
for (let k = (j + 2) % bn; k !== j; k = (k + 1) % bn) this.merged[size++] = this.pieces[bb + k];
|
|
9911
|
+
for (let k = i + 1; k < an; k++) this.merged[size++] = this.pieces[ab + k];
|
|
9912
|
+
let strict = true;
|
|
9913
|
+
for (let k = 0; k < size; k++) if (this.cross(this.merged[(k + size - 1) % size], this.merged[k], this.merged[(k + 1) % size]) <= 0) {
|
|
9914
|
+
strict = false;
|
|
9915
|
+
break;
|
|
9916
|
+
}
|
|
9917
|
+
if (!strict) continue;
|
|
9918
|
+
for (let k = 0; k < size; k++) this.pieces[ab + k] = this.merged[k];
|
|
9919
|
+
this.sizes[a] = size;
|
|
9920
|
+
this.pieces.copyWithin(bb, bb + this.maxPolygonVertices, pieces * this.maxPolygonVertices);
|
|
9921
|
+
this.sizes.copyWithin(b, b + 1, pieces);
|
|
9922
|
+
pieces--;
|
|
9923
|
+
changed = true;
|
|
9924
|
+
break search;
|
|
9925
|
+
}
|
|
9926
|
+
}
|
|
9927
|
+
}
|
|
9928
|
+
this.regionsCount = pieces;
|
|
9929
|
+
for (let i = 0; i < pieces; i++) this.region(this.pieces, this.sizes[i], i, i * this.maxPolygonVertices);
|
|
9930
|
+
return pieces > 1;
|
|
9931
|
+
}
|
|
9932
|
+
/** Plan again after float32 conversion exactly when clipThrough first finds pieces. */
|
|
9933
|
+
prepare(polygon, offset, count, inverse, convex) {
|
|
9934
|
+
this.n = count / 2;
|
|
9935
|
+
if (!Number.isInteger(this.n) || this.n < 3 || this.n > this.maxPolygonVertices) throw new Error("runtime clip: polygon vertex count exceeds its load-time capacity");
|
|
9936
|
+
for (let i = 0; i < count; i++) {
|
|
9937
|
+
const value = polygon[offset + i];
|
|
9938
|
+
if (!Number.isFinite(value)) throw new Error("runtime clip: polygon contains a non-finite coordinate");
|
|
9939
|
+
this.points[i] = value;
|
|
9940
|
+
}
|
|
9941
|
+
const replan = this.plan(inverse, convex), nativeRows = this.nativeRows;
|
|
9942
|
+
if (replan) {
|
|
9943
|
+
for (let i = 0; i < count; i++) this.points[i] = Math.fround(polygon[offset + i]);
|
|
9944
|
+
this.plan(inverse, convex);
|
|
9945
|
+
}
|
|
9946
|
+
this.nativeRows = nativeRows;
|
|
9947
|
+
}
|
|
9948
|
+
half(input, n, px, py, qx, qy, out) {
|
|
9949
|
+
let size = 0;
|
|
9950
|
+
this.cut = false;
|
|
9951
|
+
const ex = qx - px, ey = qy - py;
|
|
9952
|
+
for (let i = 0; i < n; i++) {
|
|
9953
|
+
const j = (i + 1) % n, sx = input[i * 2], sy = input[i * 2 + 1], tx = input[j * 2], ty = input[j * 2 + 1];
|
|
9954
|
+
const si = ex * (sy - py) - ey * (sx - px) < 0, ei = ex * (ty - py) - ey * (tx - px) < 0;
|
|
9955
|
+
if (!si || !ei) this.cut = true;
|
|
9956
|
+
if (si !== ei) {
|
|
9957
|
+
const dx = tx - sx, dy = ty - sy, t = (ex * (sy - py) - ey * (sx - px)) / (ey * dx - ex * dy);
|
|
9958
|
+
if (size * 2 + 2 > out.length) throw new Error("runtime clip: scratch polygon exceeds its load-time capacity");
|
|
9959
|
+
out[size * 2] = sx + t * dx;
|
|
9960
|
+
out[size * 2 + 1] = sy + t * dy;
|
|
9961
|
+
size++;
|
|
9962
|
+
}
|
|
9963
|
+
if (ei) {
|
|
9964
|
+
if (size * 2 + 2 > out.length) throw new Error("runtime clip: scratch polygon exceeds its load-time capacity");
|
|
9965
|
+
out[size * 2] = tx;
|
|
9966
|
+
out[size * 2 + 1] = ty;
|
|
9967
|
+
size++;
|
|
9968
|
+
}
|
|
9969
|
+
}
|
|
9970
|
+
return size;
|
|
9971
|
+
}
|
|
9972
|
+
emit(points, count, cut, source, out) {
|
|
9973
|
+
if (count < 3) return;
|
|
9974
|
+
if ((out.vertexCount + count) * 2 > out.vertices.length || out.triangleCount + count - 2 > out.sources.length) throw new Error("runtime clip: output exceeds its load-time capacity");
|
|
9975
|
+
const tri = this.tri, uv = this.uv, base = out.vertexCount;
|
|
9976
|
+
const d0 = tri[3] - tri[5], d1 = tri[4] - tri[2], d2 = tri[0] - tri[4], d4 = tri[5] - tri[1], d = 1 / (d0 * d2 + d1 * (tri[1] - tri[5]));
|
|
9977
|
+
for (let i = 0; i < count; i++) {
|
|
9978
|
+
const x = cut ? points[i * 2] : tri[i * 2], y = cut ? points[i * 2 + 1] : tri[i * 2 + 1], at = out.vertexCount++ * 2;
|
|
9979
|
+
out.vertices[at] = x;
|
|
9980
|
+
out.vertices[at + 1] = y;
|
|
9981
|
+
if (!cut) {
|
|
9982
|
+
out.uvs[at] = uv[i * 2];
|
|
9983
|
+
out.uvs[at + 1] = uv[i * 2 + 1];
|
|
9984
|
+
} else {
|
|
9985
|
+
const a = (d0 * (x - tri[4]) + d1 * (y - tri[5])) * d, b = (d4 * (x - tri[4]) + d2 * (y - tri[5])) * d, c = 1 - a - b;
|
|
9986
|
+
out.uvs[at] = uv[0] * a + uv[2] * b + uv[4] * c;
|
|
9987
|
+
out.uvs[at + 1] = uv[1] * a + uv[3] * b + uv[5] * c;
|
|
9988
|
+
}
|
|
9989
|
+
}
|
|
9990
|
+
for (let i = base * 2; i < out.vertexCount * 2; i++) if (!Number.isFinite(out.vertices[i]) || !Number.isFinite(out.uvs[i])) throw new Error(`runtime clip: triangle ${source} produced a non-finite clipped coordinate or UV`);
|
|
9991
|
+
for (let i = 1; i + 1 < count; i++) {
|
|
9992
|
+
const at = out.triangleCount * 3;
|
|
9993
|
+
out.triangles[at] = base;
|
|
9994
|
+
out.triangles[at + 1] = base + i;
|
|
9995
|
+
out.triangles[at + 2] = base + i + 1;
|
|
9996
|
+
out.sources[out.triangleCount++] = source;
|
|
9997
|
+
}
|
|
9998
|
+
}
|
|
9999
|
+
clip(vertices, offset, triangles, uvs, out) {
|
|
10000
|
+
out.vertexCount = 0;
|
|
10001
|
+
out.triangleCount = 0;
|
|
10002
|
+
out.clipped = this.inverse;
|
|
10003
|
+
for (let k = 0; k < triangles.length; k += 3) {
|
|
10004
|
+
for (let j = 0; j < 3; j++) {
|
|
10005
|
+
const n = triangles[k + j] * 2;
|
|
10006
|
+
this.tri[j * 2] = vertices[offset + n];
|
|
10007
|
+
this.tri[j * 2 + 1] = vertices[offset + n + 1];
|
|
10008
|
+
this.uv[j * 2] = Math.fround(uvs[n]);
|
|
10009
|
+
this.uv[j * 2 + 1] = Math.fround(uvs[n + 1]);
|
|
10010
|
+
}
|
|
10011
|
+
if (!this.inverse) for (let r = 0; r < this.regionsCount; r++) {
|
|
10012
|
+
let input = this.a, output = this.b, count = 3, cut = false;
|
|
10013
|
+
for (let i = 0; i < 6; i++) input[i] = this.tri[i];
|
|
10014
|
+
const n = this.regionSizes[r], base = r * this.maxPolygonVertices * 2;
|
|
10015
|
+
for (let e = 0; e < n && count > 0; e++) {
|
|
10016
|
+
const p = base + e * 2, q = base + (e + 1) % n * 2;
|
|
10017
|
+
count = this.half(input, count, this.regions[p], this.regions[p + 1], this.regions[q], this.regions[q + 1], output);
|
|
10018
|
+
cut ||= this.cut;
|
|
10019
|
+
const swap = input;
|
|
10020
|
+
input = output;
|
|
10021
|
+
output = swap;
|
|
10022
|
+
}
|
|
10023
|
+
out.clipped ||= cut;
|
|
10024
|
+
this.emit(input, count, cut, k / 3, out);
|
|
10025
|
+
}
|
|
10026
|
+
else {
|
|
10027
|
+
let input = this.a, output = this.b, count = 3, cutSoFar = false;
|
|
10028
|
+
for (let i = 0; i < 6; i++) input[i] = this.tri[i];
|
|
10029
|
+
const n = this.regionSizes[0];
|
|
10030
|
+
for (let e = 0; e < n && count > 0; e++) {
|
|
10031
|
+
const p = e * 2, q = (e + 1) % n * 2, px = this.regions[p], py = this.regions[p + 1], qx = this.regions[q], qy = this.regions[q + 1];
|
|
10032
|
+
const outside = this.half(input, count, qx, qy, px, py, this.outside), cut = this.cut || cutSoFar;
|
|
10033
|
+
this.emit(this.outside, outside, cut, k / 3, out);
|
|
10034
|
+
count = this.half(input, count, px, py, qx, qy, output);
|
|
10035
|
+
cutSoFar ||= this.cut;
|
|
10036
|
+
const swap = input;
|
|
10037
|
+
input = output;
|
|
10038
|
+
output = swap;
|
|
10039
|
+
}
|
|
10040
|
+
}
|
|
10041
|
+
}
|
|
10042
|
+
}
|
|
10043
|
+
}
|
|
10044
|
+
class RuntimeDrawClipper {
|
|
10045
|
+
constructor(model) {
|
|
10046
|
+
this.model = model;
|
|
10047
|
+
this.nativeRows = true;
|
|
10048
|
+
this.clipCount = 0;
|
|
10049
|
+
this.drawCount = 0;
|
|
10050
|
+
const triangles = [], uvs = [], maxTriangles = new Int32Array(model.slots.length);
|
|
10051
|
+
let maxPolygon = 3;
|
|
10052
|
+
for (const at of model.attachments) {
|
|
10053
|
+
const t = at.clipTopology?.triangles ?? null, uv = at.clipTopology?.uvs ?? null;
|
|
10054
|
+
if (at.clip !== void 0) maxPolygon = Math.max(maxPolygon, at.count / 2);
|
|
10055
|
+
triangles.push(t);
|
|
10056
|
+
uvs.push(uv);
|
|
10057
|
+
if (t !== null) maxTriangles[at.slot] = Math.max(maxTriangles[at.slot], t.length / 3);
|
|
10058
|
+
}
|
|
10059
|
+
this.triangles = triangles;
|
|
10060
|
+
this.uvs = uvs;
|
|
10061
|
+
this.clipper = new RuntimeClipper(maxPolygon);
|
|
10062
|
+
this.clipSlots = new Int32Array(model.slots.length);
|
|
10063
|
+
this.drawSlots = new Int32Array(model.slots.length);
|
|
10064
|
+
this.outputs = Array.from(maxTriangles, (n) => new RuntimeClipBuffer(n, maxPolygon));
|
|
10065
|
+
}
|
|
10066
|
+
/** pose.computeVertices() must precede this walk. Omission is a diagnostic plant only. */
|
|
10067
|
+
frame(pose, omit = false) {
|
|
10068
|
+
if (pose.model !== this.model) throw new Error("runtime clip draw: pose differs from the prepared model");
|
|
10069
|
+
this.clipCount = 0;
|
|
10070
|
+
this.drawCount = 0;
|
|
10071
|
+
this.nativeRows = true;
|
|
10072
|
+
let active = false, end = null;
|
|
10073
|
+
for (let i = 0; i < pose.drawOrder.length; i++) {
|
|
10074
|
+
const s = pose.drawOrder[i], slot = this.model.slots[s], shown = pose.shown[s], at = shown < 0 ? null : this.model.attachments[shown];
|
|
10075
|
+
if (at?.clip !== void 0) {
|
|
10076
|
+
this.clipSlots[this.clipCount++] = s;
|
|
10077
|
+
if (active && end === slot.name) active = false;
|
|
10078
|
+
if (!omit && !active && this.model.active[at.bone] === 1) {
|
|
10079
|
+
this.clipper.prepare(pose.vertices, at.offset, at.count, at.clip.inverse, at.clip.convex);
|
|
10080
|
+
this.nativeRows &&= this.clipper.nativeRows;
|
|
10081
|
+
active = true;
|
|
10082
|
+
end = at.clip.end;
|
|
10083
|
+
}
|
|
10084
|
+
continue;
|
|
10085
|
+
}
|
|
10086
|
+
if (active && at !== null) {
|
|
10087
|
+
const triangles = this.triangles[shown], uvs = this.uvs[shown];
|
|
10088
|
+
if (triangles !== null && uvs !== null) {
|
|
10089
|
+
this.clipper.clip(pose.vertices, at.offset, triangles, uvs, this.outputs[s]);
|
|
10090
|
+
this.drawSlots[this.drawCount++] = s;
|
|
10091
|
+
}
|
|
10092
|
+
}
|
|
10093
|
+
if (active && end === slot.name) active = false;
|
|
10094
|
+
}
|
|
10095
|
+
}
|
|
10096
|
+
}
|
|
10097
|
+
const PI2 = RUNTIME_PI * 2;
|
|
10098
|
+
const INV_PI2 = 1 / PI2;
|
|
10099
|
+
function freshPhysicsState() {
|
|
10100
|
+
return { pending: true, lastTime: 0, remaining: 0, ux: 0, uy: 0, cx: 0, cy: 0, tx: 0, ty: 0, xOffset: 0, xLag: 0, xVelocity: 0, yOffset: 0, yLag: 0, yVelocity: 0, rotateOffset: 0, rotateLag: 0, rotateVelocity: 0, scaleOffset: 0, scaleLag: 0, scaleVelocity: 0 };
|
|
10101
|
+
}
|
|
10102
|
+
function resetPhysicsState(s, time) {
|
|
10103
|
+
s.remaining = 0;
|
|
10104
|
+
s.lastTime = time;
|
|
10105
|
+
s.pending = true;
|
|
10106
|
+
s.xOffset = s.xLag = s.xVelocity = 0;
|
|
10107
|
+
s.yOffset = s.yLag = s.yVelocity = 0;
|
|
10108
|
+
s.rotateOffset = s.rotateLag = s.rotateVelocity = 0;
|
|
10109
|
+
s.scaleOffset = s.scaleLag = s.scaleVelocity = 0;
|
|
10110
|
+
}
|
|
10111
|
+
function stepRuntimePhysics(r, s, w, boneOffset, length, time, reset, env) {
|
|
10112
|
+
const mix = r.mix;
|
|
10113
|
+
if (mix === 0) return false;
|
|
10114
|
+
const x = r.x > 0, y = r.y > 0, rotateOrShearX = r.rotate > 0 || r.shearX > 0, scaleX = r.scaleX > 0;
|
|
10115
|
+
const l = length;
|
|
10116
|
+
if (reset) resetPhysicsState(s, time);
|
|
10117
|
+
const delta = Math.max(time - s.lastTime, 0);
|
|
10118
|
+
const shown = Math.max(0, 1 - s.remaining / r.step);
|
|
10119
|
+
s.remaining += delta;
|
|
10120
|
+
s.lastTime = time;
|
|
10121
|
+
const bx = w[boneOffset + 4];
|
|
10122
|
+
const by = w[boneOffset + 5];
|
|
10123
|
+
let z = 0;
|
|
10124
|
+
if (s.pending) {
|
|
10125
|
+
s.pending = false;
|
|
10126
|
+
s.ux = bx;
|
|
10127
|
+
s.uy = by;
|
|
10128
|
+
} else {
|
|
10129
|
+
let a = s.remaining;
|
|
10130
|
+
const i = r.inertia;
|
|
10131
|
+
const t = r.step;
|
|
10132
|
+
const f = r.referenceScale;
|
|
10133
|
+
let d = -1;
|
|
10134
|
+
const qx = r.limit * delta;
|
|
10135
|
+
const qy = qx;
|
|
10136
|
+
if (x || y) {
|
|
10137
|
+
if (x) {
|
|
10138
|
+
const u = (s.ux - bx) * i;
|
|
10139
|
+
s.xOffset += u > qx ? qx : u < -qx ? -qx : u;
|
|
10140
|
+
s.ux = bx;
|
|
10141
|
+
}
|
|
10142
|
+
if (y) {
|
|
10143
|
+
const u = (s.uy - by) * i;
|
|
10144
|
+
s.yOffset += u > qy ? qy : u < -qy ? -qy : u;
|
|
10145
|
+
s.uy = by;
|
|
10146
|
+
}
|
|
10147
|
+
if (a >= t) {
|
|
10148
|
+
d = Math.pow(r.damping, 60 * t);
|
|
10149
|
+
const m = r.massInverse * t;
|
|
10150
|
+
const e = r.strength;
|
|
10151
|
+
const wind = r.wind * f;
|
|
10152
|
+
const g = r.gravity * f;
|
|
10153
|
+
const ax = wind * env[0] + g * env[2];
|
|
10154
|
+
const ay = wind * env[1] + g * env[3];
|
|
10155
|
+
const xs = s.xOffset;
|
|
10156
|
+
const ys = s.yOffset;
|
|
10157
|
+
do {
|
|
10158
|
+
if (x) {
|
|
10159
|
+
s.xVelocity += (ax - s.xOffset * e) * m;
|
|
10160
|
+
s.xOffset += s.xVelocity * t;
|
|
10161
|
+
s.xVelocity *= d;
|
|
10162
|
+
}
|
|
10163
|
+
if (y) {
|
|
10164
|
+
s.yVelocity -= (ay + s.yOffset * e) * m;
|
|
10165
|
+
s.yOffset += s.yVelocity * t;
|
|
10166
|
+
s.yVelocity *= d;
|
|
10167
|
+
}
|
|
10168
|
+
a -= t;
|
|
10169
|
+
} while (a >= t);
|
|
10170
|
+
s.xLag = s.xOffset - xs;
|
|
10171
|
+
s.yLag = s.yOffset - ys;
|
|
10172
|
+
}
|
|
10173
|
+
z = Math.max(0, 1 - a / t);
|
|
10174
|
+
if (x) w[boneOffset + 4] += (s.xOffset - s.xLag * z) * mix * r.x;
|
|
10175
|
+
if (y) w[boneOffset + 5] += (s.yOffset - s.yLag * z) * mix * r.y;
|
|
10176
|
+
}
|
|
10177
|
+
if (rotateOrShearX || scaleX) {
|
|
10178
|
+
const ca = Math.atan2(w[boneOffset + 2], w[boneOffset + 0]);
|
|
10179
|
+
let c = 0;
|
|
10180
|
+
let sn = 0;
|
|
10181
|
+
let mr = 0;
|
|
10182
|
+
let dx = s.cx - w[boneOffset + 4];
|
|
10183
|
+
let dy = s.cy - w[boneOffset + 5];
|
|
10184
|
+
if (dx > qx) dx = qx;
|
|
10185
|
+
else if (dx < -qx) dx = -qx;
|
|
10186
|
+
if (dy > qy) dy = qy;
|
|
10187
|
+
else if (dy < -qy) dy = -qy;
|
|
10188
|
+
if (rotateOrShearX) {
|
|
10189
|
+
mr = (r.rotate + r.shearX) * mix;
|
|
10190
|
+
const lagged = s.rotateLag * shown;
|
|
10191
|
+
let rr = Math.atan2(dy + s.ty, dx + s.tx) - ca - (s.rotateOffset - lagged) * mr;
|
|
10192
|
+
s.rotateOffset += (rr - Math.ceil(rr * INV_PI2 - 0.5) * PI2) * i;
|
|
10193
|
+
rr = (s.rotateOffset - lagged) * mr + ca;
|
|
10194
|
+
c = Math.cos(rr);
|
|
10195
|
+
sn = Math.sin(rr);
|
|
10196
|
+
if (scaleX) {
|
|
10197
|
+
rr = l * Math.sqrt(w[boneOffset + 0] * w[boneOffset + 0] + w[boneOffset + 2] * w[boneOffset + 2]);
|
|
10198
|
+
if (rr > 0) s.scaleOffset += (dx * c + dy * sn) * i / rr;
|
|
10199
|
+
}
|
|
10200
|
+
} else {
|
|
10201
|
+
c = Math.cos(ca);
|
|
10202
|
+
sn = Math.sin(ca);
|
|
10203
|
+
const rr = l * Math.sqrt(w[boneOffset + 0] * w[boneOffset + 0] + w[boneOffset + 2] * w[boneOffset + 2]) - s.scaleLag * shown;
|
|
10204
|
+
if (rr > 0) s.scaleOffset += (dx * c + dy * sn) * i / rr;
|
|
10205
|
+
}
|
|
10206
|
+
a = s.remaining;
|
|
10207
|
+
if (a >= t) {
|
|
10208
|
+
if (d === -1) d = Math.pow(r.damping, 60 * t);
|
|
10209
|
+
const m = r.massInverse * t;
|
|
10210
|
+
const e = r.strength;
|
|
10211
|
+
const ax = r.wind * env[0] + r.gravity * env[2];
|
|
10212
|
+
const ay = r.wind * env[1] + r.gravity * env[3];
|
|
10213
|
+
const h = l / f;
|
|
10214
|
+
const rs = s.rotateOffset;
|
|
10215
|
+
const ss = s.scaleOffset;
|
|
10216
|
+
for (; ; ) {
|
|
10217
|
+
a -= t;
|
|
10218
|
+
if (scaleX) {
|
|
10219
|
+
s.scaleVelocity += (ax * c - ay * sn - s.scaleOffset * e) * m;
|
|
10220
|
+
s.scaleOffset += s.scaleVelocity * t;
|
|
10221
|
+
s.scaleVelocity *= d;
|
|
10222
|
+
}
|
|
10223
|
+
if (rotateOrShearX) {
|
|
10224
|
+
s.rotateVelocity -= ((ax * sn + ay * c) * h + s.rotateOffset * e) * m;
|
|
10225
|
+
s.rotateOffset += s.rotateVelocity * t;
|
|
10226
|
+
s.rotateVelocity *= d;
|
|
10227
|
+
if (a < t) break;
|
|
10228
|
+
const rr = s.rotateOffset * mr + ca;
|
|
10229
|
+
c = Math.cos(rr);
|
|
10230
|
+
sn = Math.sin(rr);
|
|
10231
|
+
} else if (a < t) break;
|
|
10232
|
+
}
|
|
10233
|
+
s.rotateLag = s.rotateOffset - rs;
|
|
10234
|
+
s.scaleLag = s.scaleOffset - ss;
|
|
10235
|
+
}
|
|
10236
|
+
z = Math.max(0, 1 - a / t);
|
|
10237
|
+
}
|
|
10238
|
+
s.remaining = a;
|
|
10239
|
+
}
|
|
10240
|
+
s.cx = w[boneOffset + 4];
|
|
10241
|
+
s.cy = w[boneOffset + 5];
|
|
10242
|
+
if (rotateOrShearX) {
|
|
10243
|
+
let angle = (s.rotateOffset - s.rotateLag * z) * mix;
|
|
10244
|
+
let sn = 0;
|
|
10245
|
+
let c = 0;
|
|
10246
|
+
let a = 0;
|
|
10247
|
+
if (r.shearX > 0) {
|
|
10248
|
+
let rr = 0;
|
|
10249
|
+
if (r.rotate > 0) {
|
|
10250
|
+
rr = angle * r.rotate;
|
|
10251
|
+
sn = Math.sin(rr);
|
|
10252
|
+
c = Math.cos(rr);
|
|
10253
|
+
a = w[boneOffset + 1];
|
|
10254
|
+
w[boneOffset + 1] = c * a - sn * w[boneOffset + 3];
|
|
10255
|
+
w[boneOffset + 3] = sn * a + c * w[boneOffset + 3];
|
|
10256
|
+
}
|
|
10257
|
+
rr += angle * r.shearX;
|
|
10258
|
+
sn = Math.sin(rr);
|
|
10259
|
+
c = Math.cos(rr);
|
|
10260
|
+
a = w[boneOffset + 0];
|
|
10261
|
+
w[boneOffset + 0] = c * a - sn * w[boneOffset + 2];
|
|
10262
|
+
w[boneOffset + 2] = sn * a + c * w[boneOffset + 2];
|
|
10263
|
+
} else {
|
|
10264
|
+
angle *= r.rotate;
|
|
10265
|
+
sn = Math.sin(angle);
|
|
10266
|
+
c = Math.cos(angle);
|
|
10267
|
+
a = w[boneOffset + 0];
|
|
10268
|
+
w[boneOffset + 0] = c * a - sn * w[boneOffset + 2];
|
|
10269
|
+
w[boneOffset + 2] = sn * a + c * w[boneOffset + 2];
|
|
10270
|
+
a = w[boneOffset + 1];
|
|
10271
|
+
w[boneOffset + 1] = c * a - sn * w[boneOffset + 3];
|
|
10272
|
+
w[boneOffset + 3] = sn * a + c * w[boneOffset + 3];
|
|
10273
|
+
}
|
|
10274
|
+
}
|
|
10275
|
+
if (scaleX) {
|
|
10276
|
+
const k = 1 + (s.scaleOffset - s.scaleLag * z) * mix * r.scaleX;
|
|
10277
|
+
w[boneOffset + 0] *= k;
|
|
10278
|
+
w[boneOffset + 2] *= k;
|
|
10279
|
+
if (r.scaleYMode === "Uniform") {
|
|
10280
|
+
w[boneOffset + 1] *= k;
|
|
10281
|
+
w[boneOffset + 3] *= k;
|
|
10282
|
+
} else if (r.scaleYMode === "Volume") {
|
|
10283
|
+
const magnitude = Math.abs(k);
|
|
10284
|
+
const transverse = magnitude >= 0.7 ? 1 / magnitude : 4 - 3.67347 * magnitude;
|
|
10285
|
+
w[boneOffset + 1] *= transverse;
|
|
10286
|
+
w[boneOffset + 3] *= transverse;
|
|
10287
|
+
}
|
|
10288
|
+
}
|
|
10289
|
+
s.tx = l * w[boneOffset + 0];
|
|
10290
|
+
s.ty = l * w[boneOffset + 2];
|
|
10291
|
+
return true;
|
|
9526
10292
|
}
|
|
9527
10293
|
const DEG = RUNTIME_DEG;
|
|
9528
10294
|
const TWO_PI = 2 * RUNTIME_PI;
|
|
10295
|
+
const PATH_EPSILON = 1e-5;
|
|
9529
10296
|
const P_ROTATE = 0;
|
|
9530
10297
|
const P_X = 1;
|
|
9531
10298
|
const P_Y = 2;
|
|
9532
10299
|
const P_SCALE_X = 3;
|
|
9533
10300
|
const P_SCALE_Y = 4;
|
|
10301
|
+
const PROPERTY_LOCAL = new Int32Array([L_ROTATION, L_X, L_Y, L_SCALE_X, L_SCALE_Y, L_SHEAR_Y]);
|
|
9534
10302
|
function wrap180(r) {
|
|
9535
10303
|
return r > 180 ? r - 360 : r <= -180 ? r + 360 : r;
|
|
9536
10304
|
}
|
|
@@ -9539,21 +10307,39 @@ function volumeScaleY(scaleY, s) {
|
|
|
9539
10307
|
}
|
|
9540
10308
|
const clamp01 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
|
|
9541
10309
|
class RuntimePose {
|
|
9542
|
-
constructor(model) {
|
|
10310
|
+
constructor(model, options = {}) {
|
|
9543
10311
|
this.trackTime = 0;
|
|
9544
10312
|
this.time = 0;
|
|
9545
10313
|
this.fired = [];
|
|
9546
10314
|
this.eventTime = -1;
|
|
9547
10315
|
this.current = null;
|
|
10316
|
+
this.pathSegmentEnds = new Float64Array(10);
|
|
9548
10317
|
this.w = new Float64Array(WORLDS);
|
|
10318
|
+
this.physicsTime = 0;
|
|
10319
|
+
this.physicsLast = -1;
|
|
10320
|
+
this.physicsPhase = "none";
|
|
10321
|
+
this.physicsReady = false;
|
|
10322
|
+
this.environment = new Float64Array([1, 0, 0, 1]);
|
|
9549
10323
|
this.model = model;
|
|
10324
|
+
this.clipping = model.attachments.some((at) => at.clip !== void 0) ? new RuntimeDrawClipper(model) : null;
|
|
10325
|
+
this.clock = options.clock;
|
|
10326
|
+
this.hasPhysics = model.constraints.some((c) => c.kind === "physics");
|
|
10327
|
+
this.physicsStates = model.constraints.map(() => freshPhysicsState());
|
|
10328
|
+
this.physicsRecords = model.constraints.map((c) => c.kind === "physics" ? { ...c.setup } : null);
|
|
10329
|
+
this.sliderTime = new Float64Array(model.constraints.length);
|
|
10330
|
+
this.sliderMix = new Float64Array(model.constraints.length);
|
|
10331
|
+
this.sliderAt = new Float64Array(model.constraints.length);
|
|
9550
10332
|
const nb = model.boneNames.length;
|
|
9551
10333
|
const ns = model.slots.length;
|
|
9552
10334
|
const nc = model.constraints.length;
|
|
10335
|
+
this.physicsScope = new Uint8Array(nc);
|
|
9553
10336
|
this.locals = new Float64Array(nb * LOCALS);
|
|
9554
10337
|
this.world = new Float64Array(nb * WORLDS);
|
|
9555
10338
|
this.light = new Float64Array(ns * 4);
|
|
10339
|
+
this.dark = new Float64Array(ns * 3);
|
|
9556
10340
|
this.shown = new Int32Array(ns);
|
|
10341
|
+
this.drawOrder = new Int32Array(ns);
|
|
10342
|
+
for (let s = 0; s < ns; s++) this.drawOrder[s] = s;
|
|
9557
10343
|
this.vertices = new Float64Array(model.vertexCount);
|
|
9558
10344
|
this.deform = new Float64Array(model.deformCount);
|
|
9559
10345
|
this.deformed = new Uint8Array(model.attachments.length);
|
|
@@ -9562,6 +10348,16 @@ class RuntimePose {
|
|
|
9562
10348
|
this.ikSoftness = new Float64Array(nc);
|
|
9563
10349
|
this.ikFlags = new Uint8Array(nc);
|
|
9564
10350
|
this.mixes = new Float64Array(nc * 6);
|
|
10351
|
+
this.pathValues = new Float64Array(nc * 5);
|
|
10352
|
+
this.pathSlotSign = new Int8Array(nc);
|
|
10353
|
+
const ms = model.maxPathSpaces;
|
|
10354
|
+
const mv = model.maxPathVertices;
|
|
10355
|
+
this.pathSpaces = new Float64Array(ms);
|
|
10356
|
+
this.pathLengths = new Float64Array(ms);
|
|
10357
|
+
this.pathPositions = new Float64Array(ms > 0 ? ms * 3 + 2 : 0);
|
|
10358
|
+
this.pathWorldPts = new Float64Array(mv);
|
|
10359
|
+
this.pathChain = new Float64Array(mv > 0 ? mv + 4 : 0);
|
|
10360
|
+
this.pathCurveEnds = new Float64Array(mv > 0 ? Math.ceil(mv / 6) + 1 : 0);
|
|
9565
10361
|
this.changed = new Int32Array(model.scratch);
|
|
9566
10362
|
this.inWorld = new Int32Array(model.scratch);
|
|
9567
10363
|
this.changedMark = new Uint8Array(nb);
|
|
@@ -9576,6 +10372,10 @@ class RuntimePose {
|
|
|
9576
10372
|
/** The animation this pose plays (a covered one: `model.animation` throws a `RuntimeRefusal` for a refused one), from track time 0. */
|
|
9577
10373
|
setAnimation(name) {
|
|
9578
10374
|
this.current = this.model.animation(name);
|
|
10375
|
+
this.physicsTime = 0;
|
|
10376
|
+
this.physicsLast = -1;
|
|
10377
|
+
this.physicsReady = false;
|
|
10378
|
+
for (let k = 0; k < this.physicsStates.length; k++) resetPhysicsState(this.physicsStates[k], 0);
|
|
9579
10379
|
this.cursors.fill(-1);
|
|
9580
10380
|
this.trackTime = 0;
|
|
9581
10381
|
this.time = 0;
|
|
@@ -9596,38 +10396,57 @@ class RuntimePose {
|
|
|
9596
10396
|
this.apply(animationTime);
|
|
9597
10397
|
}
|
|
9598
10398
|
/** The setup pose: every constraint applied with its own values, every slot as it is set up. */
|
|
9599
|
-
setupPose() {
|
|
10399
|
+
setupPose(physics = "none") {
|
|
10400
|
+
this.physicsPhase = physics;
|
|
9600
10401
|
this.current = null;
|
|
9601
10402
|
this.time = 0;
|
|
9602
10403
|
this.eventTime = -1;
|
|
9603
10404
|
this.fired.length = 0;
|
|
10405
|
+
this.physicsValues(null, 0, false);
|
|
9604
10406
|
this.locals.set(this.model.setup);
|
|
9605
10407
|
this.hierarchy();
|
|
9606
|
-
this.constraintValues(null, 0);
|
|
9607
|
-
this.solveConstraints();
|
|
9608
10408
|
this.slotValues(null, 0);
|
|
9609
10409
|
this.deformValues(null, 0);
|
|
10410
|
+
this.constraintValues(null, 0);
|
|
10411
|
+
this.solveConstraints();
|
|
10412
|
+
this.sliderAttachments();
|
|
9610
10413
|
}
|
|
9611
10414
|
/** The animation posed at its time `t`, from the setup pose (the header). */
|
|
9612
|
-
apply(t) {
|
|
10415
|
+
apply(t, physics = "none", delta = 0) {
|
|
9613
10416
|
const anim = this.current;
|
|
9614
10417
|
if (anim === null) throw new RuntimeInputError("apply: no animation is set (setAnimation first)");
|
|
9615
10418
|
if (!Number.isFinite(t)) throw new RuntimeInputError(`apply: the time ${t} is not finite`);
|
|
10419
|
+
if (!Number.isFinite(delta) || delta < 0) throw new RuntimeInputError(`apply: physics delta ${delta} is not finite and at or above 0`);
|
|
10420
|
+
this.physicsPhase = physics;
|
|
10421
|
+
this.physicsValues(anim, t, physics !== "none");
|
|
10422
|
+
if (physics !== "none") {
|
|
10423
|
+
this.physicsTime += delta;
|
|
10424
|
+
this.physicsReady = true;
|
|
10425
|
+
}
|
|
9616
10426
|
this.collectEvents(anim, t);
|
|
9617
10427
|
this.time = t;
|
|
9618
10428
|
this.boneTimelines(anim, t);
|
|
9619
10429
|
this.hierarchy();
|
|
9620
|
-
this.constraintValues(anim, t);
|
|
9621
|
-
this.solveConstraints();
|
|
9622
10430
|
this.slotValues(anim, t);
|
|
9623
10431
|
this.deformValues(anim, t);
|
|
10432
|
+
this.constraintValues(anim, t);
|
|
10433
|
+
this.solveConstraints();
|
|
10434
|
+
this.sliderAttachments();
|
|
9624
10435
|
}
|
|
9625
10436
|
/** 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). */
|
|
9626
10437
|
step(dt) {
|
|
9627
10438
|
const anim = this.current;
|
|
9628
10439
|
if (anim === null) throw new RuntimeInputError("step: no animation is set (setAnimation first)");
|
|
10440
|
+
if (!Number.isFinite(dt) || dt < 0) throw new RuntimeInputError(`step: delta ${dt} is not finite and at or above 0`);
|
|
10441
|
+
if (!this.physicsReady && this.hasPhysics) {
|
|
10442
|
+
const lastEventTime = this.eventTime;
|
|
10443
|
+
this.apply(this.time, "reset", 0);
|
|
10444
|
+
this.eventTime = lastEventTime;
|
|
10445
|
+
}
|
|
10446
|
+
const moved = this.clock === void 0 ? dt : this.clock(dt);
|
|
10447
|
+
if (!Number.isFinite(moved) || moved < 0) throw new RuntimeInputError(`step: physics clock moved by ${moved}, not finite and at or above 0`);
|
|
9629
10448
|
this.trackTime += dt;
|
|
9630
|
-
this.apply(anim.duration === 0 ? 0 : this.trackTime % anim.duration);
|
|
10449
|
+
this.apply(anim.duration === 0 ? 0 : this.trackTime % anim.duration, "update", moved);
|
|
9631
10450
|
}
|
|
9632
10451
|
/** The oracle interval (last,t], tail then head on a wrap; never copies a payload. */
|
|
9633
10452
|
collectEvents(anim, t) {
|
|
@@ -9824,20 +10643,738 @@ class RuntimePose {
|
|
|
9824
10643
|
this.ikSoftness[k] = c.softness + (channelValue(tl.curve, i, 1, t) - c.softness) * 1;
|
|
9825
10644
|
this.ikFlags[k] = tl.flags[i];
|
|
9826
10645
|
}
|
|
9827
|
-
} else {
|
|
10646
|
+
} else if (c.kind === "transform") {
|
|
9828
10647
|
const o = k * 6;
|
|
9829
10648
|
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;
|
|
10649
|
+
} else if (c.kind === "slider") {
|
|
10650
|
+
const keyed = anim === null ? null : anim.sliders[k];
|
|
10651
|
+
let time = c.setup.time, mix = c.setup.mix;
|
|
10652
|
+
if (keyed !== null) {
|
|
10653
|
+
if (keyed.time !== null) {
|
|
10654
|
+
const q = keyAt(keyed.time, this.cursors, keyed.timeCursor, t);
|
|
10655
|
+
if (q >= 0) time += (channelValue(keyed.time, q, 0, t) - time) * 1;
|
|
10656
|
+
}
|
|
10657
|
+
if (keyed.mix !== null) {
|
|
10658
|
+
const q = keyAt(keyed.mix, this.cursors, keyed.mixCursor, t);
|
|
10659
|
+
if (q >= 0) mix += (channelValue(keyed.mix, q, 0, t) - mix) * 1;
|
|
10660
|
+
}
|
|
10661
|
+
}
|
|
10662
|
+
this.sliderTime[k] = time;
|
|
10663
|
+
this.sliderMix[k] = mix;
|
|
10664
|
+
} else if (c.kind === "path") {
|
|
10665
|
+
const keyed = anim === null ? null : anim.paths[k];
|
|
10666
|
+
let position = c.position;
|
|
10667
|
+
let spacing = c.spacing;
|
|
10668
|
+
let mixRotate = c.mixRotate;
|
|
10669
|
+
let mixX = c.mixX;
|
|
10670
|
+
let mixY = c.mixY;
|
|
10671
|
+
if (keyed !== null) {
|
|
10672
|
+
if (keyed.position !== null) {
|
|
10673
|
+
const q = keyAt(keyed.position, this.cursors, keyed.positionCursor, t);
|
|
10674
|
+
if (q >= 0) position = c.position + (channelValue(keyed.position, q, 0, t) - c.position) * 1;
|
|
10675
|
+
}
|
|
10676
|
+
if (keyed.spacing !== null) {
|
|
10677
|
+
const q = keyAt(keyed.spacing, this.cursors, keyed.spacingCursor, t);
|
|
10678
|
+
if (q >= 0) spacing = c.spacing + (channelValue(keyed.spacing, q, 0, t) - c.spacing) * 1;
|
|
10679
|
+
}
|
|
10680
|
+
if (keyed.mix !== null) {
|
|
10681
|
+
const q = keyAt(keyed.mix, this.cursors, keyed.mixCursor, t);
|
|
10682
|
+
if (q >= 0) {
|
|
10683
|
+
mixRotate = c.mixRotate + (channelValue(keyed.mix, q, 0, t) - c.mixRotate) * 1;
|
|
10684
|
+
mixX = c.mixX + (channelValue(keyed.mix, q, 1, t) - c.mixX) * 1;
|
|
10685
|
+
mixY = c.mixY + (channelValue(keyed.mix, q, 2, t) - c.mixY) * 1;
|
|
10686
|
+
}
|
|
10687
|
+
}
|
|
10688
|
+
}
|
|
10689
|
+
const o = k * 5;
|
|
10690
|
+
this.pathValues[o] = position;
|
|
10691
|
+
this.pathValues[o + 1] = spacing;
|
|
10692
|
+
this.pathValues[o + 2] = mixRotate;
|
|
10693
|
+
this.pathValues[o + 3] = mixX;
|
|
10694
|
+
this.pathValues[o + 4] = mixY;
|
|
9830
10695
|
}
|
|
9831
10696
|
}
|
|
9832
10697
|
}
|
|
9833
10698
|
/** Every applied constraint in the document's order, each followed by the update order's repose (`applyConstraints`). */
|
|
9834
10699
|
solveConstraints() {
|
|
9835
10700
|
const cs = this.model.constraints;
|
|
10701
|
+
const hasPath = this.model.hasPath;
|
|
9836
10702
|
for (let k = 0; k < cs.length; k++) {
|
|
10703
|
+
if (hasPath) this.snapSlotBones(k, false);
|
|
9837
10704
|
const c = cs[k];
|
|
9838
10705
|
if (!c.applied) continue;
|
|
9839
10706
|
if (c.kind === "ik") this.solveIk(c, k);
|
|
9840
|
-
else this.solveTransform(c, k);
|
|
10707
|
+
else if (c.kind === "transform") this.solveTransform(c, k);
|
|
10708
|
+
else if (c.kind === "slider") this.solveSlider(c, k);
|
|
10709
|
+
else if (c.kind === "path") this.solvePath(c, k);
|
|
10710
|
+
else if (this.physicsPhase !== "none" && !this.model.omitPhysics) {
|
|
10711
|
+
const b = c.bones[0];
|
|
10712
|
+
if (stepRuntimePhysics(this.physicsRecords[k], this.physicsStates[k], this.world, b * WORLDS, this.model.lengths[b], this.physicsTime, this.physicsPhase === "reset", this.environment)) {
|
|
10713
|
+
this.changed[0] = this.inWorld[0] = b;
|
|
10714
|
+
this.repose(1, 1);
|
|
10715
|
+
}
|
|
10716
|
+
}
|
|
10717
|
+
if (hasPath) this.snapSlotBones(k, true);
|
|
10718
|
+
}
|
|
10719
|
+
}
|
|
10720
|
+
snapSlotBones(at, after) {
|
|
10721
|
+
const cs = this.model.constraints;
|
|
10722
|
+
const W = this.world;
|
|
10723
|
+
for (let k = 0; k < cs.length; k++) {
|
|
10724
|
+
const c = cs[k];
|
|
10725
|
+
if (c.kind !== "path" || c.slotEventAt !== at || c.slotEventAfter !== after || k === at && !after) continue;
|
|
10726
|
+
const sb = c.slotBone * WORLDS;
|
|
10727
|
+
this.pathSlotSign[k] = W[sb + W_A] * W[sb + W_D] - W[sb + W_B] * W[sb + W_C] > 0 ? 1 : -1;
|
|
10728
|
+
}
|
|
10729
|
+
}
|
|
10730
|
+
/** Reset where the consumer starts: the setup pose or animation pose zero. */
|
|
10731
|
+
resetPhysics(at = "animation") {
|
|
10732
|
+
this.physicsTime = 0;
|
|
10733
|
+
this.physicsLast = -1;
|
|
10734
|
+
for (let k = 0; k < this.physicsStates.length; k++) resetPhysicsState(this.physicsStates[k], 0);
|
|
10735
|
+
if (at === "animation") this.apply(0, "reset", 0);
|
|
10736
|
+
else {
|
|
10737
|
+
const current = this.current;
|
|
10738
|
+
this.setupPose("reset");
|
|
10739
|
+
this.current = current;
|
|
10740
|
+
}
|
|
10741
|
+
this.physicsReady = true;
|
|
10742
|
+
}
|
|
10743
|
+
checkPhysicsScope(scope) {
|
|
10744
|
+
if (scope === void 0) return null;
|
|
10745
|
+
if (!Array.isArray(scope)) throw new RuntimeInputError(`physics movement scope is ${String(scope)}, not an array of physics constraint names`);
|
|
10746
|
+
const selected = this.physicsScope;
|
|
10747
|
+
selected.fill(0);
|
|
10748
|
+
const cs = this.model.constraints;
|
|
10749
|
+
for (let i = 0; i < scope.length; i++) {
|
|
10750
|
+
const name = scope[i];
|
|
10751
|
+
if (typeof name !== "string") throw new RuntimeInputError(`physics movement scope name ${String(name)} is not a string`);
|
|
10752
|
+
let found;
|
|
10753
|
+
for (let k = 0; k < cs.length; k++) {
|
|
10754
|
+
if (cs[k].kind === "physics" && cs[k].name === name) {
|
|
10755
|
+
found = cs[k];
|
|
10756
|
+
break;
|
|
10757
|
+
}
|
|
10758
|
+
}
|
|
10759
|
+
if (found === void 0) {
|
|
10760
|
+
for (let k = 0; k < cs.length; k++) {
|
|
10761
|
+
if (cs[k].name === name) {
|
|
10762
|
+
found = cs[k];
|
|
10763
|
+
break;
|
|
10764
|
+
}
|
|
10765
|
+
}
|
|
10766
|
+
}
|
|
10767
|
+
if (found === void 0) throw new RuntimeInputError(`physics movement scope: no constraint "${name}"; this document declares [${cs.map((c) => c.name).join(", ")}]`);
|
|
10768
|
+
if (found.kind !== "physics") throw new RuntimeInputError(`physics movement scope: constraint "${name}" is ${found.kind}, not physics`);
|
|
10769
|
+
selected[found.index] = 1;
|
|
10770
|
+
}
|
|
10771
|
+
return this.model.ignorePhysicsScope ? null : selected;
|
|
10772
|
+
}
|
|
10773
|
+
/** External motion changes the previous origin, never the authored locals. */
|
|
10774
|
+
physicsTranslate(x, y, scope) {
|
|
10775
|
+
const selected = this.checkPhysicsScope(scope);
|
|
10776
|
+
if (!Number.isFinite(x) || !Number.isFinite(y)) throw new RuntimeInputError("physicsTranslate: x and y must be finite");
|
|
10777
|
+
for (let k = 0; k < this.physicsStates.length; k++) {
|
|
10778
|
+
if (selected !== null && selected[k] === 0) continue;
|
|
10779
|
+
const s = this.physicsStates[k];
|
|
10780
|
+
s.ux -= x;
|
|
10781
|
+
s.uy -= y;
|
|
10782
|
+
s.cx -= x;
|
|
10783
|
+
s.cy -= y;
|
|
10784
|
+
}
|
|
10785
|
+
}
|
|
10786
|
+
physicsRotate(x, y, degrees, scope) {
|
|
10787
|
+
const selected = this.checkPhysicsScope(scope);
|
|
10788
|
+
if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(degrees)) throw new RuntimeInputError("physicsRotate: x, y and degrees must be finite");
|
|
10789
|
+
const r = degrees * RAD, cos = Math.cos(r), sin = Math.sin(r);
|
|
10790
|
+
for (let k = 0; k < this.physicsStates.length; k++) {
|
|
10791
|
+
if (selected !== null && selected[k] === 0) continue;
|
|
10792
|
+
const s = this.physicsStates[k];
|
|
10793
|
+
const dx = s.cx - x, dy = s.cy - y;
|
|
10794
|
+
const tx = dx * cos - dy * sin - dx, ty = dx * sin + dy * cos - dy;
|
|
10795
|
+
s.ux -= tx;
|
|
10796
|
+
s.uy -= ty;
|
|
10797
|
+
s.cx -= tx;
|
|
10798
|
+
s.cy -= ty;
|
|
10799
|
+
}
|
|
10800
|
+
}
|
|
10801
|
+
setEnvironment(env) {
|
|
10802
|
+
if (!Number.isFinite(env.wind[0]) || !Number.isFinite(env.wind[1]) || !Number.isFinite(env.gravity[0]) || !Number.isFinite(env.gravity[1])) throw new RuntimeInputError("setEnvironment: wind and gravity must be finite");
|
|
10803
|
+
this.environment[0] = env.wind[0];
|
|
10804
|
+
this.environment[1] = env.wind[1];
|
|
10805
|
+
this.environment[2] = env.gravity[0];
|
|
10806
|
+
this.environment[3] = env.gravity[1];
|
|
10807
|
+
}
|
|
10808
|
+
/** The per-pass values start at setup, before any slider writes. */
|
|
10809
|
+
physicsValues(anim, t, resets) {
|
|
10810
|
+
const cs = this.model.constraints;
|
|
10811
|
+
for (let k = 0; k < cs.length; k++) {
|
|
10812
|
+
const c = cs[k];
|
|
10813
|
+
if (c.kind !== "physics") continue;
|
|
10814
|
+
const r = this.physicsRecords[k];
|
|
10815
|
+
r.inertia = c.setup.inertia;
|
|
10816
|
+
r.strength = c.setup.strength;
|
|
10817
|
+
r.damping = c.setup.damping;
|
|
10818
|
+
r.massInverse = c.setup.massInverse;
|
|
10819
|
+
r.wind = c.setup.wind;
|
|
10820
|
+
r.gravity = c.setup.gravity;
|
|
10821
|
+
r.mix = c.setup.mix;
|
|
10822
|
+
}
|
|
10823
|
+
if (anim !== null) this.physicsTimelines(anim.physics, t, 1, false, true, resets);
|
|
10824
|
+
if (resets) this.physicsLast = t;
|
|
10825
|
+
}
|
|
10826
|
+
physicsTimelines(timelines, t, alpha, additive, setup, resets = false) {
|
|
10827
|
+
for (let q = 0; q < timelines.length; q++) {
|
|
10828
|
+
const tl = timelines[q];
|
|
10829
|
+
if (tl.kind === "reset") {
|
|
10830
|
+
if (!resets) continue;
|
|
10831
|
+
let crossed = false;
|
|
10832
|
+
const last = this.physicsLast;
|
|
10833
|
+
for (let i2 = 0; i2 < tl.curve.keys; i2++) {
|
|
10834
|
+
const time = tl.curve.times[i2];
|
|
10835
|
+
if (t < last ? time > last || time <= t : time > last && time <= t) crossed = true;
|
|
10836
|
+
}
|
|
10837
|
+
if (crossed) for (let j = 0; j < tl.targets.length; j++) resetPhysicsState(this.physicsStates[tl.targets[j]], this.physicsTime);
|
|
10838
|
+
continue;
|
|
10839
|
+
}
|
|
10840
|
+
const i = keyAt(tl.curve, this.cursors, tl.cursor, t);
|
|
10841
|
+
if (i < 0) {
|
|
10842
|
+
if (setup) for (let j = 0; j < tl.targets.length; j++) {
|
|
10843
|
+
const k = tl.targets[j], r = this.physicsRecords[k];
|
|
10844
|
+
const original = this.model.constraints[k].setup;
|
|
10845
|
+
if (tl.kind === "mass") r.massInverse = original.massInverse;
|
|
10846
|
+
else r[tl.kind] = original[tl.kind];
|
|
10847
|
+
}
|
|
10848
|
+
continue;
|
|
10849
|
+
}
|
|
10850
|
+
const v = channelValue(tl.curve, i, 0, t);
|
|
10851
|
+
for (let j = 0; j < tl.targets.length; j++) {
|
|
10852
|
+
const k = tl.targets[j], r = this.physicsRecords[k];
|
|
10853
|
+
const from = setup ? this.model.constraints[k].setup : r;
|
|
10854
|
+
if (tl.kind === "mass") {
|
|
10855
|
+
const mass = 1 / from.massInverse;
|
|
10856
|
+
r.massInverse = 1 / (mass + (v - mass) * alpha);
|
|
10857
|
+
} else {
|
|
10858
|
+
r[tl.kind] = additive && (tl.kind === "wind" || tl.kind === "gravity") ? r[tl.kind] + v * alpha : from[tl.kind] + (v - from[tl.kind]) * alpha;
|
|
10859
|
+
}
|
|
10860
|
+
}
|
|
10861
|
+
}
|
|
10862
|
+
}
|
|
10863
|
+
/** Read the dial where previous constraints left it; this value is never track time. */
|
|
10864
|
+
solveSlider(c, k) {
|
|
10865
|
+
const alpha = this.sliderMix[k];
|
|
10866
|
+
if (alpha === 0) return;
|
|
10867
|
+
let time = this.sliderTime[k];
|
|
10868
|
+
const r = c.setup, a = c.animation;
|
|
10869
|
+
if (c.bone >= 0) {
|
|
10870
|
+
const b = c.bone, w = b * WORLDS, l = b * LOCALS;
|
|
10871
|
+
let value;
|
|
10872
|
+
if (r.local) {
|
|
10873
|
+
const field = c.property === 0 ? L_ROTATION : c.property === 1 ? L_X : c.property === 2 ? L_Y : c.property === 3 ? L_SCALE_X : c.property === 4 ? L_SCALE_Y : L_SHEAR_Y;
|
|
10874
|
+
value = this.locals[l + field] + 0;
|
|
10875
|
+
} else {
|
|
10876
|
+
const W = this.world, x = W[w], y = W[w + 2], bx = W[w + 1], by = W[w + 3];
|
|
10877
|
+
switch (c.property) {
|
|
10878
|
+
case 0:
|
|
10879
|
+
value = Math.atan2(y, x) * DEG + (x * by - bx * y > 0 ? 0 : -0);
|
|
10880
|
+
if (value < 0) value += 360;
|
|
10881
|
+
break;
|
|
10882
|
+
case 1:
|
|
10883
|
+
value = x * 0 + bx * 0 + W[w + W_X];
|
|
10884
|
+
break;
|
|
10885
|
+
case 2:
|
|
10886
|
+
value = y * 0 + by * 0 + W[w + W_Y];
|
|
10887
|
+
break;
|
|
10888
|
+
case 3:
|
|
10889
|
+
value = Math.sqrt(x * x + y * y) + 0;
|
|
10890
|
+
break;
|
|
10891
|
+
case 4:
|
|
10892
|
+
value = Math.sqrt(bx * bx + by * by) + 0;
|
|
10893
|
+
break;
|
|
10894
|
+
default:
|
|
10895
|
+
value = (Math.atan2(by, bx) - Math.atan2(y, x)) * DEG - 90 + 0;
|
|
10896
|
+
}
|
|
10897
|
+
}
|
|
10898
|
+
time = r.to + (value - r.from) * r.scale;
|
|
10899
|
+
time = r.loop ? a.duration === 0 ? 0 : a.duration + time % a.duration : Math.max(0, time);
|
|
10900
|
+
}
|
|
10901
|
+
const at = this.model.sliderAsTrack ? this.time : r.loop && a.duration !== 0 ? time % a.duration : time;
|
|
10902
|
+
this.sliderAt[k] = at;
|
|
10903
|
+
if (this.physicsPhase !== "none") this.physicsTimelines(a.physics, at, alpha, r.additive, false);
|
|
10904
|
+
const L = this.locals, S = this.model.setup;
|
|
10905
|
+
for (let q = 0; q < a.sliderBones.length; q++) {
|
|
10906
|
+
const tl = a.sliderBones[q], i = keyAt(tl.curve, this.cursors, tl.cursor, at);
|
|
10907
|
+
if (i < 0) continue;
|
|
10908
|
+
const o = tl.bone * LOCALS;
|
|
10909
|
+
for (let f = 0; f < tl.fields.length; f++) {
|
|
10910
|
+
const n = o + tl.fields[f], v = channelValue(tl.curve, i, f, at), current = L[n], setup = S[n];
|
|
10911
|
+
if (tl.multiply[f]) {
|
|
10912
|
+
if (r.additive) L[n] = current + (v * setup - setup) * alpha;
|
|
10913
|
+
else {
|
|
10914
|
+
const target = setup * v, from = Math.abs(current) * Math.sign(target);
|
|
10915
|
+
L[n] = alpha === 1 ? target : from + (target - from) * alpha;
|
|
10916
|
+
}
|
|
10917
|
+
} else L[n] = r.additive ? current + v * alpha : current + (setup + v - current) * alpha;
|
|
10918
|
+
}
|
|
10919
|
+
}
|
|
10920
|
+
for (let q = 0; q < c.bones.length; q++) this.changed[q] = c.bones[q];
|
|
10921
|
+
if (c.bones.length > 0) this.repose(c.bones.length, 0);
|
|
10922
|
+
}
|
|
10923
|
+
/** A path constraint (`solvePath` in `src/core/constraints_path.ts`), every buffer overwritten in place. */
|
|
10924
|
+
solvePath(c, k) {
|
|
10925
|
+
if (c.attachment < 0 || this.model.omitPath) return;
|
|
10926
|
+
const o5 = k * 5;
|
|
10927
|
+
const position = this.pathValues[o5];
|
|
10928
|
+
const spacing = this.pathValues[o5 + 1];
|
|
10929
|
+
const mixRotate = this.pathValues[o5 + 2];
|
|
10930
|
+
const mixX = this.pathValues[o5 + 3];
|
|
10931
|
+
const mixY = this.pathValues[o5 + 4];
|
|
10932
|
+
if (mixRotate === 0 && mixX === 0 && mixY === 0) return;
|
|
10933
|
+
const wantsTangent = c.rotateMode === PATH_ROTATE_TANGENT;
|
|
10934
|
+
const scale = c.rotateMode === PATH_ROTATE_CHAIN_SCALE;
|
|
10935
|
+
const bones = c.bones;
|
|
10936
|
+
const boneCount = bones.length;
|
|
10937
|
+
const spaceCount = wantsTangent ? boneCount : boneCount + 1;
|
|
10938
|
+
const spaces = this.pathSpaces;
|
|
10939
|
+
const lengths = this.pathLengths;
|
|
10940
|
+
const W = this.world;
|
|
10941
|
+
const modelLengths = this.model.lengths;
|
|
10942
|
+
spaces[0] = 0;
|
|
10943
|
+
if (c.spacingMode === PATH_SPACING_PERCENT) {
|
|
10944
|
+
if (scale) {
|
|
10945
|
+
for (let i = 0; i < spaceCount - 1; i++) {
|
|
10946
|
+
const bi = bones[i];
|
|
10947
|
+
const boneLength = modelLengths[bi];
|
|
10948
|
+
const w = bi * WORLDS;
|
|
10949
|
+
const x = boneLength * W[w + W_A];
|
|
10950
|
+
const y = boneLength * W[w + W_C];
|
|
10951
|
+
lengths[i] = Math.sqrt(x * x + y * y);
|
|
10952
|
+
}
|
|
10953
|
+
}
|
|
10954
|
+
for (let i = 1; i < spaceCount; i++) spaces[i] = spacing;
|
|
10955
|
+
} else if (c.spacingMode === PATH_SPACING_PROPORTIONAL) {
|
|
10956
|
+
let sum = 0;
|
|
10957
|
+
for (let i = 0, n = spaceCount - 1; i < n; ) {
|
|
10958
|
+
const bi = bones[i];
|
|
10959
|
+
const boneLength = modelLengths[bi];
|
|
10960
|
+
if (boneLength < PATH_EPSILON) {
|
|
10961
|
+
if (scale) lengths[i] = 0;
|
|
10962
|
+
spaces[++i] = spacing;
|
|
10963
|
+
} else {
|
|
10964
|
+
const w = bi * WORLDS;
|
|
10965
|
+
const x = boneLength * W[w + W_A];
|
|
10966
|
+
const y = boneLength * W[w + W_C];
|
|
10967
|
+
const length = Math.sqrt(x * x + y * y);
|
|
10968
|
+
if (scale) lengths[i] = length;
|
|
10969
|
+
spaces[++i] = length;
|
|
10970
|
+
sum += length;
|
|
10971
|
+
}
|
|
10972
|
+
}
|
|
10973
|
+
if (sum > 0) {
|
|
10974
|
+
sum = spaceCount / sum * spacing;
|
|
10975
|
+
for (let i = 1; i < spaceCount; i++) spaces[i] *= sum;
|
|
10976
|
+
}
|
|
10977
|
+
} else {
|
|
10978
|
+
const addsLength = c.spacingMode === PATH_SPACING_LENGTH;
|
|
10979
|
+
for (let i = 0, n = spaceCount - 1; i < n; ) {
|
|
10980
|
+
const bi = bones[i];
|
|
10981
|
+
const boneLength = modelLengths[bi];
|
|
10982
|
+
if (boneLength < PATH_EPSILON) {
|
|
10983
|
+
if (scale) lengths[i] = 0;
|
|
10984
|
+
spaces[++i] = spacing;
|
|
10985
|
+
} else {
|
|
10986
|
+
const w = bi * WORLDS;
|
|
10987
|
+
const x = boneLength * W[w + W_A];
|
|
10988
|
+
const y = boneLength * W[w + W_C];
|
|
10989
|
+
const length = Math.sqrt(x * x + y * y);
|
|
10990
|
+
if (scale) lengths[i] = length;
|
|
10991
|
+
spaces[++i] = (addsLength ? Math.max(0, boneLength + spacing) : spacing) * length / boneLength;
|
|
10992
|
+
}
|
|
10993
|
+
}
|
|
10994
|
+
}
|
|
10995
|
+
this.positionsOfPath(c, position, spaceCount, wantsTangent);
|
|
10996
|
+
const positions = this.pathPositions;
|
|
10997
|
+
let atX = positions[0];
|
|
10998
|
+
let atY = positions[1];
|
|
10999
|
+
let offsetRotation = c.offsetRotation;
|
|
11000
|
+
let fromTip;
|
|
11001
|
+
if (offsetRotation === 0) fromTip = c.rotateMode === PATH_ROTATE_CHAIN;
|
|
11002
|
+
else {
|
|
11003
|
+
fromTip = false;
|
|
11004
|
+
let positive;
|
|
11005
|
+
if (c.slotEventAt === k && !c.slotEventAfter) {
|
|
11006
|
+
const sb = c.slotBone * WORLDS;
|
|
11007
|
+
positive = W[sb + W_A] * W[sb + W_D] - W[sb + W_B] * W[sb + W_C] > 0;
|
|
11008
|
+
} else {
|
|
11009
|
+
positive = c.slotEventAt >= 0 && this.pathSlotSign[k] > 0;
|
|
11010
|
+
}
|
|
11011
|
+
offsetRotation *= positive ? RAD : -RAD;
|
|
11012
|
+
}
|
|
11013
|
+
for (let i = 0, p = 3; i < boneCount; i++, p += 3) {
|
|
11014
|
+
const bi = bones[i];
|
|
11015
|
+
const o = bi * WORLDS;
|
|
11016
|
+
let wa = W[o + W_A];
|
|
11017
|
+
let wb = W[o + W_B];
|
|
11018
|
+
let wc = W[o + W_C];
|
|
11019
|
+
let wd = W[o + W_D];
|
|
11020
|
+
const wx = W[o + W_X] + (atX - W[o + W_X]) * mixX;
|
|
11021
|
+
const wy = W[o + W_Y] + (atY - W[o + W_Y]) * mixY;
|
|
11022
|
+
const x = positions[p];
|
|
11023
|
+
const y = positions[p + 1];
|
|
11024
|
+
const dx = x - atX;
|
|
11025
|
+
const dy = y - atY;
|
|
11026
|
+
if (scale) {
|
|
11027
|
+
const length = lengths[i];
|
|
11028
|
+
if (length >= PATH_EPSILON) {
|
|
11029
|
+
const s = (Math.sqrt(dx * dx + dy * dy) / length - 1) * mixRotate + 1;
|
|
11030
|
+
wa *= s;
|
|
11031
|
+
wc *= s;
|
|
11032
|
+
}
|
|
11033
|
+
}
|
|
11034
|
+
atX = x;
|
|
11035
|
+
atY = y;
|
|
11036
|
+
if (mixRotate > 0) {
|
|
11037
|
+
const a = wa;
|
|
11038
|
+
const bb = wb;
|
|
11039
|
+
const cc = wc;
|
|
11040
|
+
const d = wd;
|
|
11041
|
+
let r;
|
|
11042
|
+
if (wantsTangent) r = positions[p - 1];
|
|
11043
|
+
else if (spaces[i + 1] === 0) r = positions[p + 2];
|
|
11044
|
+
else r = Math.atan2(dy, dx);
|
|
11045
|
+
r -= Math.atan2(cc, a);
|
|
11046
|
+
if (fromTip) {
|
|
11047
|
+
const cos2 = Math.cos(r);
|
|
11048
|
+
const sin2 = Math.sin(r);
|
|
11049
|
+
const length = modelLengths[bi];
|
|
11050
|
+
atX += (length * (cos2 * a - sin2 * cc) - dx) * mixRotate;
|
|
11051
|
+
atY += (length * (sin2 * a + cos2 * cc) - dy) * mixRotate;
|
|
11052
|
+
} else r += offsetRotation;
|
|
11053
|
+
if (r > RUNTIME_PI) r -= TWO_PI;
|
|
11054
|
+
else if (r < -RUNTIME_PI) r += TWO_PI;
|
|
11055
|
+
r *= mixRotate;
|
|
11056
|
+
const cos = Math.cos(r);
|
|
11057
|
+
const sin = Math.sin(r);
|
|
11058
|
+
wa = cos * a - sin * cc;
|
|
11059
|
+
wb = cos * bb - sin * d;
|
|
11060
|
+
wc = sin * a + cos * cc;
|
|
11061
|
+
wd = sin * bb + cos * d;
|
|
11062
|
+
}
|
|
11063
|
+
W[o + W_A] = wa;
|
|
11064
|
+
W[o + W_B] = wb;
|
|
11065
|
+
W[o + W_C] = wc;
|
|
11066
|
+
W[o + W_D] = wd;
|
|
11067
|
+
W[o + W_X] = wx;
|
|
11068
|
+
W[o + W_Y] = wy;
|
|
11069
|
+
this.changed[i] = bi;
|
|
11070
|
+
this.inWorld[i] = bi;
|
|
11071
|
+
}
|
|
11072
|
+
if (boneCount > 0) this.repose(boneCount, boneCount);
|
|
11073
|
+
}
|
|
11074
|
+
computePathWorld(c) {
|
|
11075
|
+
const at = this.model.attachments[c.attachment];
|
|
11076
|
+
const W = this.world;
|
|
11077
|
+
const pts = this.pathWorldPts;
|
|
11078
|
+
const deformed = this.deformed[c.attachment] !== 0;
|
|
11079
|
+
if (at.bindingCounts.length === 0) {
|
|
11080
|
+
const b = c.slotBone * WORLDS;
|
|
11081
|
+
const a = W[b + W_A];
|
|
11082
|
+
const bb = W[b + W_B];
|
|
11083
|
+
const cc = W[b + W_C];
|
|
11084
|
+
const d = W[b + W_D];
|
|
11085
|
+
const wx = W[b + W_X];
|
|
11086
|
+
const wy = W[b + W_Y];
|
|
11087
|
+
const local = deformed ? this.deform : at.local;
|
|
11088
|
+
const lo = deformed ? at.deformOffset : 0;
|
|
11089
|
+
for (let q = 0; q < c.pathVertices; q += 2) {
|
|
11090
|
+
const x = local[lo + q];
|
|
11091
|
+
const y = local[lo + q + 1];
|
|
11092
|
+
pts[q] = x * a + y * bb + wx;
|
|
11093
|
+
pts[q + 1] = x * cc + y * d + wy;
|
|
11094
|
+
}
|
|
11095
|
+
return;
|
|
11096
|
+
}
|
|
11097
|
+
const counts = at.bindingCounts;
|
|
11098
|
+
const bones = at.bindingBones;
|
|
11099
|
+
const xs = at.bindingX;
|
|
11100
|
+
const ys = at.bindingY;
|
|
11101
|
+
const weights = at.bindingWeights;
|
|
11102
|
+
let n = 0;
|
|
11103
|
+
let o = 0;
|
|
11104
|
+
for (let v = 0; v < counts.length; v++) {
|
|
11105
|
+
let wx = 0;
|
|
11106
|
+
let wy = 0;
|
|
11107
|
+
const end = n + counts[v];
|
|
11108
|
+
for (; n < end; n++) {
|
|
11109
|
+
const b = bones[n] * WORLDS;
|
|
11110
|
+
const weight = weights[n];
|
|
11111
|
+
const x = deformed ? xs[n] + this.deform[at.deformOffset + n * 2] : xs[n];
|
|
11112
|
+
const y = deformed ? ys[n] + this.deform[at.deformOffset + n * 2 + 1] : ys[n];
|
|
11113
|
+
wx += (x * W[b + W_A] + y * W[b + W_B] + W[b + W_X]) * weight;
|
|
11114
|
+
wy += (x * W[b + W_C] + y * W[b + W_D] + W[b + W_Y]) * weight;
|
|
11115
|
+
}
|
|
11116
|
+
pts[o++] = wx;
|
|
11117
|
+
pts[o++] = wy;
|
|
11118
|
+
}
|
|
11119
|
+
}
|
|
11120
|
+
positionsOfPath(c, position, spaceCount, wantsTangent) {
|
|
11121
|
+
this.computePathWorld(c);
|
|
11122
|
+
const pts = this.pathWorldPts;
|
|
11123
|
+
const vc = c.pathVertices;
|
|
11124
|
+
const n = vc / 2;
|
|
11125
|
+
const spaces = this.pathSpaces;
|
|
11126
|
+
const out = this.pathPositions;
|
|
11127
|
+
const outLen = spaceCount * 3 + 2;
|
|
11128
|
+
for (let i = 0; i < outLen; i++) out[i] = 0;
|
|
11129
|
+
const closed = c.closed;
|
|
11130
|
+
if (!c.constantSpeed) {
|
|
11131
|
+
const lengths = c.lengths;
|
|
11132
|
+
const curveCount2 = n / 3 - (closed ? 1 : 2);
|
|
11133
|
+
const total2 = lengths[curveCount2];
|
|
11134
|
+
if (c.positionMode === PATH_POSITION_PERCENT) position *= total2;
|
|
11135
|
+
const spaceScale2 = c.spacingMode === PATH_SPACING_PERCENT ? total2 : c.spacingMode === PATH_SPACING_PROPORTIONAL ? total2 / spaceCount : 1;
|
|
11136
|
+
let curve = 0;
|
|
11137
|
+
for (let i = 0, o = 0; i < spaceCount; i++, o += 3) {
|
|
11138
|
+
const space = spaces[i] * spaceScale2;
|
|
11139
|
+
position += space;
|
|
11140
|
+
let p = position;
|
|
11141
|
+
if (closed) {
|
|
11142
|
+
p %= total2;
|
|
11143
|
+
if (p < 0) p += total2;
|
|
11144
|
+
curve = 0;
|
|
11145
|
+
} else if (p < 0) {
|
|
11146
|
+
onLineBefore(p, pts[2], pts[3], pts[4], pts[5], out, o);
|
|
11147
|
+
continue;
|
|
11148
|
+
} else if (p > total2) {
|
|
11149
|
+
const m0 = 2 * (n - 3);
|
|
11150
|
+
const m1 = 2 * (n - 2);
|
|
11151
|
+
onLinePast(p - total2, pts[m0], pts[m0 + 1], pts[m1], pts[m1 + 1], out, o);
|
|
11152
|
+
continue;
|
|
11153
|
+
}
|
|
11154
|
+
for (; ; curve++) {
|
|
11155
|
+
const length = lengths[curve];
|
|
11156
|
+
if (p > length) continue;
|
|
11157
|
+
if (curve === 0) p /= length;
|
|
11158
|
+
else {
|
|
11159
|
+
const prev = lengths[curve - 1];
|
|
11160
|
+
p = (p - prev) / (length - prev);
|
|
11161
|
+
}
|
|
11162
|
+
break;
|
|
11163
|
+
}
|
|
11164
|
+
const kk = 3 * curve + 1;
|
|
11165
|
+
const i0 = kk % n * 2;
|
|
11166
|
+
const i1 = (kk + 1) % n * 2;
|
|
11167
|
+
const i2 = (kk + 2) % n * 2;
|
|
11168
|
+
const i3 = (kk + 3) % n * 2;
|
|
11169
|
+
onCurve(p, pts[i0], pts[i0 + 1], pts[i1], pts[i1 + 1], pts[i2], pts[i2 + 1], pts[i3], pts[i3 + 1], out, o, wantsTangent || i > 0 && space === 0);
|
|
11170
|
+
}
|
|
11171
|
+
return;
|
|
11172
|
+
}
|
|
11173
|
+
const chain = this.pathChain;
|
|
11174
|
+
let chainLen = 0;
|
|
11175
|
+
if (closed) {
|
|
11176
|
+
for (let q = 2; q < vc; q++) chain[chainLen++] = pts[q];
|
|
11177
|
+
chain[chainLen++] = pts[0];
|
|
11178
|
+
chain[chainLen++] = pts[1];
|
|
11179
|
+
chain[chainLen++] = pts[2];
|
|
11180
|
+
chain[chainLen++] = pts[3];
|
|
11181
|
+
} else {
|
|
11182
|
+
for (let q = 2; q < vc - 2; q++) chain[chainLen++] = pts[q];
|
|
11183
|
+
}
|
|
11184
|
+
const curveCount = closed ? n / 3 : n / 3 - 1;
|
|
11185
|
+
const curveEnds = this.pathCurveEnds;
|
|
11186
|
+
let total = 0;
|
|
11187
|
+
let cx0 = chain[0];
|
|
11188
|
+
let cy0 = chain[1];
|
|
11189
|
+
for (let i = 0, w = 2; i < curveCount; i++, w += 6) {
|
|
11190
|
+
const cx12 = chain[w];
|
|
11191
|
+
const cy12 = chain[w + 1];
|
|
11192
|
+
const cx22 = chain[w + 2];
|
|
11193
|
+
const cy22 = chain[w + 3];
|
|
11194
|
+
const x22 = chain[w + 4];
|
|
11195
|
+
const y22 = chain[w + 5];
|
|
11196
|
+
const h2x = (cx0 - cx12 * 2 + cx22) * 0.1875;
|
|
11197
|
+
const h2y = (cy0 - cy12 * 2 + cy22) * 0.1875;
|
|
11198
|
+
const d3x = ((cx12 - cx22) * 3 - cx0 + x22) * 0.09375;
|
|
11199
|
+
const d3y = ((cy12 - cy22) * 3 - cy0 + y22) * 0.09375;
|
|
11200
|
+
let d2x = h2x * 2 + d3x;
|
|
11201
|
+
let d2y = h2y * 2 + d3y;
|
|
11202
|
+
let d1x = (cx12 - cx0) * 0.75 + h2x + d3x * 0.16666667;
|
|
11203
|
+
let d1y = (cy12 - cy0) * 0.75 + h2y + d3y * 0.16666667;
|
|
11204
|
+
total += Math.sqrt(d1x * d1x + d1y * d1y);
|
|
11205
|
+
d1x += d2x;
|
|
11206
|
+
d1y += d2y;
|
|
11207
|
+
d2x += d3x;
|
|
11208
|
+
d2y += d3y;
|
|
11209
|
+
total += Math.sqrt(d1x * d1x + d1y * d1y);
|
|
11210
|
+
d1x += d2x;
|
|
11211
|
+
d1y += d2y;
|
|
11212
|
+
total += Math.sqrt(d1x * d1x + d1y * d1y);
|
|
11213
|
+
d1x += d2x + d3x;
|
|
11214
|
+
d1y += d2y + d3y;
|
|
11215
|
+
total += Math.sqrt(d1x * d1x + d1y * d1y);
|
|
11216
|
+
curveEnds[i] = total;
|
|
11217
|
+
cx0 = x22;
|
|
11218
|
+
cy0 = y22;
|
|
11219
|
+
}
|
|
11220
|
+
const pathTotal = curveCount > 0 ? curveEnds[curveCount - 1] : 0;
|
|
11221
|
+
if (c.positionMode === PATH_POSITION_PERCENT) position *= pathTotal;
|
|
11222
|
+
const spaceScale = c.spacingMode === PATH_SPACING_PERCENT ? pathTotal : c.spacingMode === PATH_SPACING_PROPORTIONAL ? pathTotal / spaceCount : 1;
|
|
11223
|
+
const segmentEnds = this.pathSegmentEnds;
|
|
11224
|
+
for (let s = 0; s < 10; s++) segmentEnds[s] = 0;
|
|
11225
|
+
let curveTotal = 0;
|
|
11226
|
+
let lastCurve = -1;
|
|
11227
|
+
let x1 = 0, y1 = 0, cx1 = 0, cy1 = 0, cx2 = 0, cy2 = 0, x2 = 0, y2 = 0;
|
|
11228
|
+
for (let i = 0, o = 0, curve = 0, segment = 0; i < spaceCount; i++, o += 3) {
|
|
11229
|
+
const space = spaces[i] * spaceScale;
|
|
11230
|
+
position += space;
|
|
11231
|
+
let p = position;
|
|
11232
|
+
if (closed) {
|
|
11233
|
+
p %= pathTotal;
|
|
11234
|
+
if (p < 0) p += pathTotal;
|
|
11235
|
+
curve = 0;
|
|
11236
|
+
segment = 0;
|
|
11237
|
+
} else if (p < 0) {
|
|
11238
|
+
onLineBefore(p, chain[0], chain[1], chain[2], chain[3], out, o);
|
|
11239
|
+
continue;
|
|
11240
|
+
} else if (p > pathTotal) {
|
|
11241
|
+
onLinePast(p - pathTotal, chain[chainLen - 4], chain[chainLen - 3], chain[chainLen - 2], chain[chainLen - 1], out, o);
|
|
11242
|
+
continue;
|
|
11243
|
+
}
|
|
11244
|
+
for (; ; curve++) {
|
|
11245
|
+
const length = curveEnds[curve];
|
|
11246
|
+
if (p > length) continue;
|
|
11247
|
+
if (curve === 0) p /= length;
|
|
11248
|
+
else {
|
|
11249
|
+
const prev = curveEnds[curve - 1];
|
|
11250
|
+
p = (p - prev) / (length - prev);
|
|
11251
|
+
}
|
|
11252
|
+
break;
|
|
11253
|
+
}
|
|
11254
|
+
if (curve !== lastCurve) {
|
|
11255
|
+
lastCurve = curve;
|
|
11256
|
+
const ii = curve * 6;
|
|
11257
|
+
x1 = chain[ii];
|
|
11258
|
+
y1 = chain[ii + 1];
|
|
11259
|
+
cx1 = chain[ii + 2];
|
|
11260
|
+
cy1 = chain[ii + 3];
|
|
11261
|
+
cx2 = chain[ii + 4];
|
|
11262
|
+
cy2 = chain[ii + 5];
|
|
11263
|
+
x2 = chain[ii + 6];
|
|
11264
|
+
y2 = chain[ii + 7];
|
|
11265
|
+
const h2x = (x1 - cx1 * 2 + cx2) * 0.03;
|
|
11266
|
+
const h2y = (y1 - cy1 * 2 + cy2) * 0.03;
|
|
11267
|
+
const d3x = ((cx1 - cx2) * 3 - x1 + x2) * 6e-3;
|
|
11268
|
+
const d3y = ((cy1 - cy2) * 3 - y1 + y2) * 6e-3;
|
|
11269
|
+
let d2x = h2x * 2 + d3x;
|
|
11270
|
+
let d2y = h2y * 2 + d3y;
|
|
11271
|
+
let d1x = (cx1 - x1) * 0.3 + h2x + d3x * 0.16666667;
|
|
11272
|
+
let d1y = (cy1 - y1) * 0.3 + h2y + d3y * 0.16666667;
|
|
11273
|
+
curveTotal = Math.sqrt(d1x * d1x + d1y * d1y);
|
|
11274
|
+
segmentEnds[0] = curveTotal;
|
|
11275
|
+
for (let s = 1; s < 8; s++) {
|
|
11276
|
+
d1x += d2x;
|
|
11277
|
+
d1y += d2y;
|
|
11278
|
+
d2x += d3x;
|
|
11279
|
+
d2y += d3y;
|
|
11280
|
+
curveTotal += Math.sqrt(d1x * d1x + d1y * d1y);
|
|
11281
|
+
segmentEnds[s] = curveTotal;
|
|
11282
|
+
}
|
|
11283
|
+
d1x += d2x;
|
|
11284
|
+
d1y += d2y;
|
|
11285
|
+
curveTotal += Math.sqrt(d1x * d1x + d1y * d1y);
|
|
11286
|
+
segmentEnds[8] = curveTotal;
|
|
11287
|
+
d1x += d2x + d3x;
|
|
11288
|
+
d1y += d2y + d3y;
|
|
11289
|
+
curveTotal += Math.sqrt(d1x * d1x + d1y * d1y);
|
|
11290
|
+
segmentEnds[9] = curveTotal;
|
|
11291
|
+
segment = 0;
|
|
11292
|
+
}
|
|
11293
|
+
p *= curveTotal;
|
|
11294
|
+
for (; ; segment++) {
|
|
11295
|
+
const length = segmentEnds[segment];
|
|
11296
|
+
if (p > length) continue;
|
|
11297
|
+
if (segment === 0) p /= length;
|
|
11298
|
+
else {
|
|
11299
|
+
const prev = segmentEnds[segment - 1];
|
|
11300
|
+
p = segment + (p - prev) / (length - prev);
|
|
11301
|
+
}
|
|
11302
|
+
break;
|
|
11303
|
+
}
|
|
11304
|
+
onCurve(p * 0.1, x1, y1, cx1, cy1, cx2, cy2, x2, y2, out, o, wantsTangent || i > 0 && space === 0);
|
|
11305
|
+
}
|
|
11306
|
+
}
|
|
11307
|
+
sliderAttachments() {
|
|
11308
|
+
for (let k = 0; k < this.model.constraints.length; k++) {
|
|
11309
|
+
this.sliderSlots(k);
|
|
11310
|
+
this.sliderDeforms(k);
|
|
11311
|
+
}
|
|
11312
|
+
}
|
|
11313
|
+
sliderSlots(index) {
|
|
11314
|
+
const cs = this.model.constraints;
|
|
11315
|
+
{
|
|
11316
|
+
const k = index, c = cs[k];
|
|
11317
|
+
if (c.kind !== "slider" || !c.applied || this.sliderMix[k] === 0) return;
|
|
11318
|
+
const a = c.animation, t = this.sliderAt[k], alpha = this.sliderMix[k];
|
|
11319
|
+
if (!this.model.omitDrawOrder && a.drawOrder !== null) {
|
|
11320
|
+
const di = keyAt(a.drawOrder.curve, this.cursors, a.drawOrder.cursor, t);
|
|
11321
|
+
if (di >= 0) {
|
|
11322
|
+
const ns = this.drawOrder.length, orders = a.drawOrder.orders, base = di * ns;
|
|
11323
|
+
for (let s = 0; s < ns; s++) this.drawOrder[s] = orders[base + s];
|
|
11324
|
+
}
|
|
11325
|
+
}
|
|
11326
|
+
for (let q = 0; q < a.slots.length; q++) {
|
|
11327
|
+
const tl = a.slots[q], i = keyAt(tl.curve, this.cursors, tl.cursor, t);
|
|
11328
|
+
if (i < 0) continue;
|
|
11329
|
+
if (tl.attachments !== null) {
|
|
11330
|
+
const next = tl.attachments[i];
|
|
11331
|
+
if (next !== this.shown[tl.slot] && next >= 0) this.deformed[next] = 0;
|
|
11332
|
+
this.shown[tl.slot] = next;
|
|
11333
|
+
} else {
|
|
11334
|
+
const channels = tl.channels;
|
|
11335
|
+
const slot = this.model.slots[tl.slot];
|
|
11336
|
+
const o = tl.slot * 4, d = tl.slot * 3;
|
|
11337
|
+
for (let f = 0; f < channels.length; f++) {
|
|
11338
|
+
const at = channels[f], v = channelValue(tl.curve, i, f, t);
|
|
11339
|
+
if (at < 4) {
|
|
11340
|
+
const n = o + at;
|
|
11341
|
+
this.light[n] = clamp01(alpha === 1 ? v : this.light[n] + (v - this.light[n]) * alpha);
|
|
11342
|
+
} else if (slot.dark !== null && !this.model.omitDarkColour) {
|
|
11343
|
+
const n = d + (at - 4);
|
|
11344
|
+
this.dark[n] = clamp01(alpha === 1 ? v : this.dark[n] + (v - this.dark[n]) * alpha);
|
|
11345
|
+
}
|
|
11346
|
+
}
|
|
11347
|
+
}
|
|
11348
|
+
}
|
|
11349
|
+
}
|
|
11350
|
+
}
|
|
11351
|
+
sliderDeforms(index) {
|
|
11352
|
+
if (this.model.omitDeform) return;
|
|
11353
|
+
const cs = this.model.constraints;
|
|
11354
|
+
{
|
|
11355
|
+
const k = index, c = cs[k];
|
|
11356
|
+
if (c.kind !== "slider" || !c.applied || this.sliderMix[k] === 0) return;
|
|
11357
|
+
const a = c.animation, t = this.sliderAt[k], alpha = this.sliderMix[k];
|
|
11358
|
+
for (let q = 0; q < a.deforms.length; q++) {
|
|
11359
|
+
const tl = a.deforms[q], i = keyAt(tl.curve, this.cursors, tl.cursor, t);
|
|
11360
|
+
if (i < 0) continue;
|
|
11361
|
+
const held = i + 1 === tl.curve.keys || tl.curve.kinds[i] === CURVE_STEPPED;
|
|
11362
|
+
const p = held ? 0 : deformFraction(tl.curve, i, t), start = i * tl.count;
|
|
11363
|
+
for (let slot = 0; slot < this.shown.length; slot++) {
|
|
11364
|
+
const index2 = this.shown[slot];
|
|
11365
|
+
if (index2 < 0 || !this.model.slots[slot].live) continue;
|
|
11366
|
+
const at = this.model.attachments[index2];
|
|
11367
|
+
if (at.timeline !== tl.identity || at.deformCount === 0) continue;
|
|
11368
|
+
const current = this.deformed[index2] !== 0;
|
|
11369
|
+
for (let f = 0; f < tl.count; f++) {
|
|
11370
|
+
const v = tl.held[start + f], target = held ? v : v + (tl.held[start + tl.count + f] - v) * p;
|
|
11371
|
+
const setup = at.draw === DRAW_MESH ? at.local[f] : 0, n = at.deformOffset + f;
|
|
11372
|
+
const from = current ? this.deform[n] : setup;
|
|
11373
|
+
this.deform[n] = alpha === 1 && (!c.setup.additive || !current) ? target : c.setup.additive ? from + (target - setup) * alpha : from + (target - from) * alpha;
|
|
11374
|
+
}
|
|
11375
|
+
this.deformed[index2] = 1;
|
|
11376
|
+
}
|
|
11377
|
+
}
|
|
9841
11378
|
}
|
|
9842
11379
|
}
|
|
9843
11380
|
solveIk(c, k) {
|
|
@@ -10094,7 +11631,10 @@ class RuntimePose {
|
|
|
10094
11631
|
L[cl + L_ROTATION] = childRotation + wrap180(childTarget - childRotation) * mix;
|
|
10095
11632
|
return true;
|
|
10096
11633
|
}
|
|
10097
|
-
/**
|
|
11634
|
+
/** Absolute/additive transform, matching `solveTransform` in src/core/constraints.ts:
|
|
11635
|
+
* read the source per target/property, edit locals or the scratch world,
|
|
11636
|
+
* then repose the union in bone order (`repose` in that module). No read-back for a local target.
|
|
11637
|
+
*/
|
|
10098
11638
|
solveTransform(c, k) {
|
|
10099
11639
|
const W = this.world;
|
|
10100
11640
|
const w = this.w;
|
|
@@ -10124,24 +11664,31 @@ class RuntimePose {
|
|
|
10124
11664
|
v = v < lo ? lo : v > hi ? hi : v;
|
|
10125
11665
|
}
|
|
10126
11666
|
applied = true;
|
|
10127
|
-
|
|
11667
|
+
if (c.localTarget) {
|
|
11668
|
+
const at = bi * LOCALS + PROPERTY_LOCAL[property];
|
|
11669
|
+
const current = this.locals[at];
|
|
11670
|
+
this.locals[at] = c.additive ? property === P_SCALE_X || property === P_SCALE_Y ? current * (1 + (v - 1) * mix) : current + v * mix : current + (v - current) * mix;
|
|
11671
|
+
} else applyWorld(w, property, v, mix, c.additive);
|
|
10128
11672
|
}
|
|
10129
11673
|
}
|
|
10130
11674
|
if (!applied) continue;
|
|
10131
|
-
|
|
10132
|
-
|
|
10133
|
-
|
|
10134
|
-
|
|
10135
|
-
|
|
10136
|
-
|
|
11675
|
+
if (!c.localTarget) {
|
|
11676
|
+
W[o + W_A] = w[W_A];
|
|
11677
|
+
W[o + W_B] = w[W_B];
|
|
11678
|
+
W[o + W_C] = w[W_C];
|
|
11679
|
+
W[o + W_D] = w[W_D];
|
|
11680
|
+
W[o + W_X] = w[W_X];
|
|
11681
|
+
W[o + W_Y] = w[W_Y];
|
|
11682
|
+
}
|
|
10137
11683
|
this.changed[n] = bi;
|
|
10138
11684
|
this.inWorld[n] = bi;
|
|
10139
11685
|
n++;
|
|
10140
11686
|
}
|
|
10141
|
-
if (n > 0) this.repose(n, n);
|
|
11687
|
+
if (n > 0) this.repose(n, c.localTarget ? 0 : n);
|
|
10142
11688
|
}
|
|
10143
|
-
/** The source's
|
|
11689
|
+
/** The source's current local or world value (`sourceValue` in src/core/constraints.ts). */
|
|
10144
11690
|
sourceValue(c, property) {
|
|
11691
|
+
if (c.localSource) return this.locals[c.source * LOCALS + PROPERTY_LOCAL[property]] + c.offsets[property];
|
|
10145
11692
|
const W = this.world;
|
|
10146
11693
|
const s = c.source * WORLDS;
|
|
10147
11694
|
const o = c.offsets;
|
|
@@ -10221,7 +11768,10 @@ class RuntimePose {
|
|
|
10221
11768
|
if (p >= 0 && below[p] === 1 && (active[p] === 1 || this.changedMark[p] === 1)) below[b] = 1;
|
|
10222
11769
|
if (below[b] === 1 && this.inWorldMark[b] === 0) this.poseBone(b, false);
|
|
10223
11770
|
}
|
|
10224
|
-
/** A bone's
|
|
11771
|
+
/** A bone's locals read back from its world transform (`localFromWorld` in src/core/constraints.ts).
|
|
11772
|
+
* All five inherit modes: the no-scale forms read rotation first, rebuild
|
|
11773
|
+
* `noScaleDirection`, and retain the x-angle residual as shearX in `decompose`.
|
|
11774
|
+
*/
|
|
10225
11775
|
localFromWorld(bi) {
|
|
10226
11776
|
const m = this.model;
|
|
10227
11777
|
const W = this.world;
|
|
@@ -10255,6 +11805,21 @@ class RuntimePose {
|
|
|
10255
11805
|
const wb = W[w + W_B];
|
|
10256
11806
|
const wc = W[w + W_C];
|
|
10257
11807
|
const wd = W[w + W_D];
|
|
11808
|
+
if (mode === MODE_NO_SCALE || mode === MODE_NO_SCALE_OR_REFLECTION) {
|
|
11809
|
+
const det = pa * pd - pb * pc;
|
|
11810
|
+
const flip = mode === MODE_NO_SCALE && det < 0 ? -1 : 1;
|
|
11811
|
+
const sign = det < 0 ? -1 : 1;
|
|
11812
|
+
const rotation = Math.atan2((pa * wc - pc * wa) * sign, (pd * wa - pb * wc) * sign) * DEG;
|
|
11813
|
+
const rr = rotation * RAD;
|
|
11814
|
+
const cos = Math.cos(rr), sin = Math.sin(rr);
|
|
11815
|
+
let ux = pa * cos + pb * sin, uy = pc * cos + pd * sin;
|
|
11816
|
+
const length = 1 / Math.sqrt(ux * ux + uy * uy);
|
|
11817
|
+
ux *= length;
|
|
11818
|
+
uy *= length;
|
|
11819
|
+
this.decompose(l, ux * wa + uy * wc, (ux * wc - uy * wa) * flip, ux * wb + uy * wd, (ux * wd - uy * wb) * flip, true);
|
|
11820
|
+
L[l + L_ROTATION] = rotation;
|
|
11821
|
+
return;
|
|
11822
|
+
}
|
|
10258
11823
|
if (mode === MODE_ONLY_TRANSLATION) {
|
|
10259
11824
|
this.decompose(l, wa, wc, wb, wd);
|
|
10260
11825
|
return;
|
|
@@ -10273,7 +11838,7 @@ class RuntimePose {
|
|
|
10273
11838
|
this.decompose(l, ia * wa - ib * wc, id * wc - ic * wa, ia * wb - ib * wd, id * wd - ic * wb);
|
|
10274
11839
|
}
|
|
10275
11840
|
/** A local matrix with columns `(la, lc)` and `(lb, ld)` as rotation, scales and shear (`decompose`). */
|
|
10276
|
-
decompose(l, la, lc, lb, ld) {
|
|
11841
|
+
decompose(l, la, lc, lb, ld, shearX = false) {
|
|
10277
11842
|
const L = this.locals;
|
|
10278
11843
|
const sx = Math.sqrt(la * la + lc * lc);
|
|
10279
11844
|
const yLength = Math.sqrt(lb * lb + ld * ld);
|
|
@@ -10287,11 +11852,11 @@ class RuntimePose {
|
|
|
10287
11852
|
const xAngle = Math.atan2(lc, la) * DEG;
|
|
10288
11853
|
const det = la * ld - lb * lc;
|
|
10289
11854
|
const yAngle = Math.atan2(ld, lb) * DEG;
|
|
10290
|
-
L[l + L_ROTATION] = xAngle;
|
|
11855
|
+
L[l + (shearX ? L_SHEAR_X : L_ROTATION)] = xAngle;
|
|
10291
11856
|
L[l + L_SCALE_X] = sx;
|
|
10292
11857
|
L[l + L_SCALE_Y] = det < 0 ? -yLength : yLength;
|
|
10293
11858
|
const right = det < 0 ? -90 : 90;
|
|
10294
|
-
let shear = yAngle - (xAngle + right);
|
|
11859
|
+
let shear = yAngle - ((shearX ? 0 : xAngle) + right);
|
|
10295
11860
|
shear = shear > 180 ? shear - 360 : shear < -180 ? shear + 360 : shear;
|
|
10296
11861
|
L[l + L_SHEAR_Y] = yLength > 1e-5 ? shear : 0;
|
|
10297
11862
|
}
|
|
@@ -10302,16 +11867,32 @@ class RuntimePose {
|
|
|
10302
11867
|
slotValues(anim, t) {
|
|
10303
11868
|
const slots = this.model.slots;
|
|
10304
11869
|
const light = this.light;
|
|
11870
|
+
const dark = this.dark;
|
|
10305
11871
|
for (let s = 0; s < slots.length; s++) {
|
|
10306
11872
|
const slot = slots[s];
|
|
10307
11873
|
this.shown[s] = slot.setupAttachment;
|
|
11874
|
+
this.drawOrder[s] = s;
|
|
10308
11875
|
const o = s * 4;
|
|
10309
11876
|
light[o] = slot.light[0];
|
|
10310
11877
|
light[o + 1] = slot.light[1];
|
|
10311
11878
|
light[o + 2] = slot.light[2];
|
|
10312
11879
|
light[o + 3] = slot.light[3];
|
|
11880
|
+
const setupDark = slot.dark;
|
|
11881
|
+
if (setupDark !== null) {
|
|
11882
|
+
const d = s * 3;
|
|
11883
|
+
dark[d] = setupDark[0];
|
|
11884
|
+
dark[d + 1] = setupDark[1];
|
|
11885
|
+
dark[d + 2] = setupDark[2];
|
|
11886
|
+
}
|
|
10313
11887
|
}
|
|
10314
11888
|
if (anim === null) return;
|
|
11889
|
+
if (!this.model.omitDrawOrder && anim.drawOrder !== null) {
|
|
11890
|
+
const di = keyAt(anim.drawOrder.curve, this.cursors, anim.drawOrder.cursor, t);
|
|
11891
|
+
if (di >= 0) {
|
|
11892
|
+
const ns = slots.length, orders = anim.drawOrder.orders, base = di * ns;
|
|
11893
|
+
for (let s = 0; s < ns; s++) this.drawOrder[s] = orders[base + s];
|
|
11894
|
+
}
|
|
11895
|
+
}
|
|
10315
11896
|
const timelines = anim.slots;
|
|
10316
11897
|
for (let k = 0; k < timelines.length; k++) {
|
|
10317
11898
|
const tl = timelines[k];
|
|
@@ -10322,11 +11903,18 @@ class RuntimePose {
|
|
|
10322
11903
|
continue;
|
|
10323
11904
|
}
|
|
10324
11905
|
const channels = tl.channels;
|
|
10325
|
-
const
|
|
11906
|
+
const slot = slots[tl.slot];
|
|
11907
|
+
const setup = slot.light;
|
|
11908
|
+
const setupDark = slot.dark;
|
|
10326
11909
|
const o = tl.slot * 4;
|
|
11910
|
+
const d = tl.slot * 3;
|
|
10327
11911
|
for (let c = 0; c < channels.length; c++) {
|
|
10328
11912
|
const at = channels[c];
|
|
10329
|
-
|
|
11913
|
+
if (at < 4) {
|
|
11914
|
+
light[o + at] = i < 0 ? setup[at] : clamp01(channelValue(tl.curve, i, c, t));
|
|
11915
|
+
} else if (setupDark !== null && !this.model.omitDarkColour) {
|
|
11916
|
+
dark[d + (at - 4)] = i < 0 ? setupDark[at - 4] : clamp01(channelValue(tl.curve, i, c, t));
|
|
11917
|
+
}
|
|
10330
11918
|
}
|
|
10331
11919
|
}
|
|
10332
11920
|
}
|
|
@@ -10355,6 +11943,13 @@ class RuntimePose {
|
|
|
10355
11943
|
}
|
|
10356
11944
|
}
|
|
10357
11945
|
}
|
|
11946
|
+
/** Recompute vertices and overwrite clipping output, without per-frame allocation.
|
|
11947
|
+
* Concave/inverse rows describe the core's drawn coverage, not Spine's native triangle list.
|
|
11948
|
+
*/
|
|
11949
|
+
computeClipped() {
|
|
11950
|
+
this.computeVertices();
|
|
11951
|
+
this.clipping?.frame(this);
|
|
11952
|
+
}
|
|
10358
11953
|
/** Every shown attachment's world vertices into `vertices` at its offset (`regionCorners`, `worldVertices`). */
|
|
10359
11954
|
computeVertices() {
|
|
10360
11955
|
const m = this.model;
|
|
@@ -10425,10 +12020,10 @@ class RuntimePose {
|
|
|
10425
12020
|
}
|
|
10426
12021
|
}
|
|
10427
12022
|
}
|
|
10428
|
-
function applyWorld(w, property, v, mix) {
|
|
12023
|
+
function applyWorld(w, property, v, mix, additive) {
|
|
10429
12024
|
switch (property) {
|
|
10430
12025
|
case P_ROTATE: {
|
|
10431
|
-
let r = v * RAD - Math.atan2(w[W_C], w[W_A]);
|
|
12026
|
+
let r = additive ? v * RAD : v * RAD - Math.atan2(w[W_C], w[W_A]);
|
|
10432
12027
|
if (r > RUNTIME_PI) r -= TWO_PI;
|
|
10433
12028
|
else if (r < -RUNTIME_PI) r += TWO_PI;
|
|
10434
12029
|
r *= mix;
|
|
@@ -10443,10 +12038,10 @@ function applyWorld(w, property, v, mix) {
|
|
|
10443
12038
|
return;
|
|
10444
12039
|
}
|
|
10445
12040
|
case P_X:
|
|
10446
|
-
w[W_X] = w[W_X] + (v - w[W_X]) * mix;
|
|
12041
|
+
w[W_X] = additive ? w[W_X] + v * mix : w[W_X] + (v - w[W_X]) * mix;
|
|
10447
12042
|
return;
|
|
10448
12043
|
case P_Y:
|
|
10449
|
-
w[W_Y] = w[W_Y] + (v - w[W_Y]) * mix;
|
|
12044
|
+
w[W_Y] = additive ? w[W_Y] + v * mix : w[W_Y] + (v - w[W_Y]) * mix;
|
|
10450
12045
|
return;
|
|
10451
12046
|
case P_SCALE_X:
|
|
10452
12047
|
case P_SCALE_Y: {
|
|
@@ -10454,7 +12049,7 @@ function applyWorld(w, property, v, mix) {
|
|
|
10454
12049
|
const q = property === P_SCALE_X ? W_C : W_D;
|
|
10455
12050
|
const s = Math.sqrt(w[p] * w[p] + w[q] * w[q]);
|
|
10456
12051
|
if (s === 0) return;
|
|
10457
|
-
const k = 1 + (v - s) * mix / s;
|
|
12052
|
+
const k = additive ? 1 + (v - 1) * mix : 1 + (v - s) * mix / s;
|
|
10458
12053
|
w[p] *= k;
|
|
10459
12054
|
w[q] *= k;
|
|
10460
12055
|
return;
|
|
@@ -10462,8 +12057,8 @@ function applyWorld(w, property, v, mix) {
|
|
|
10462
12057
|
default: {
|
|
10463
12058
|
const yAngle = Math.atan2(w[W_D], w[W_B]);
|
|
10464
12059
|
const xAngle = Math.atan2(w[W_C], w[W_A]);
|
|
10465
|
-
let r = (v + 90) * RAD - (yAngle - xAngle);
|
|
10466
|
-
r = r > RUNTIME_PI ? r - TWO_PI : r < -RUNTIME_PI ? r + TWO_PI : r;
|
|
12060
|
+
let r = (v + 90) * RAD - (additive ? 90 * RAD : yAngle - xAngle);
|
|
12061
|
+
if (!additive) r = r > RUNTIME_PI ? r - TWO_PI : r < -RUNTIME_PI ? r + TWO_PI : r;
|
|
10467
12062
|
const angle = yAngle + r * mix;
|
|
10468
12063
|
const s = Math.sqrt(w[W_B] * w[W_B] + w[W_D] * w[W_D]);
|
|
10469
12064
|
w[W_B] = Math.cos(angle) * s;
|
|
@@ -10471,6 +12066,43 @@ function applyWorld(w, property, v, mix) {
|
|
|
10471
12066
|
}
|
|
10472
12067
|
}
|
|
10473
12068
|
}
|
|
12069
|
+
function onLineBefore(p, x1, y1, x2, y2, out, o) {
|
|
12070
|
+
const r = Math.atan2(y2 - y1, x2 - x1);
|
|
12071
|
+
out[o] = x1 + p * Math.cos(r);
|
|
12072
|
+
out[o + 1] = y1 + p * Math.sin(r);
|
|
12073
|
+
out[o + 2] = r;
|
|
12074
|
+
}
|
|
12075
|
+
function onLinePast(p, x0, y0, x1, y1, out, o) {
|
|
12076
|
+
const r = Math.atan2(y1 - y0, x1 - x0);
|
|
12077
|
+
out[o] = x1 + p * Math.cos(r);
|
|
12078
|
+
out[o + 1] = y1 + p * Math.sin(r);
|
|
12079
|
+
out[o + 2] = r;
|
|
12080
|
+
}
|
|
12081
|
+
function onCurve(p, x1, y1, cx1, cy1, cx2, cy2, x2, y2, out, o, wantsTangent) {
|
|
12082
|
+
if (Number.isNaN(p)) {
|
|
12083
|
+
out[o] = x1;
|
|
12084
|
+
out[o + 1] = y1;
|
|
12085
|
+
out[o + 2] = Math.atan2(cy1 - y1, cx1 - x1);
|
|
12086
|
+
return;
|
|
12087
|
+
}
|
|
12088
|
+
const t2 = p * p;
|
|
12089
|
+
const t3 = t2 * p;
|
|
12090
|
+
const u = 1 - p;
|
|
12091
|
+
const u2 = u * u;
|
|
12092
|
+
const u3 = u2 * u;
|
|
12093
|
+
const ut = u * p;
|
|
12094
|
+
const ut3x = ut * 3;
|
|
12095
|
+
const w1 = u * ut3x;
|
|
12096
|
+
const w2 = ut3x * p;
|
|
12097
|
+
const x = x1 * u3 + cx1 * w1 + cx2 * w2 + x2 * t3;
|
|
12098
|
+
const y = y1 * u3 + cy1 * w1 + cy2 * w2 + y2 * t3;
|
|
12099
|
+
out[o] = x;
|
|
12100
|
+
out[o + 1] = y;
|
|
12101
|
+
if (wantsTangent) {
|
|
12102
|
+
if (p < 1e-3) out[o + 2] = Math.atan2(cy1 - y1, cx1 - x1);
|
|
12103
|
+
else out[o + 2] = Math.atan2(y - (y1 * u2 + cy1 * ut * 2 + cy2 * t2), x - (x1 * u2 + cx1 * ut * 2 + cx2 * t2));
|
|
12104
|
+
}
|
|
12105
|
+
}
|
|
10474
12106
|
function frameSource(doc, parsed) {
|
|
10475
12107
|
if (doc.pages === null) {
|
|
10476
12108
|
throw new CoreInputError(
|
|
@@ -10734,6 +12366,10 @@ function checkedLiveFrame(frame2, current) {
|
|
|
10734
12366
|
if (drawing) liveDrawings.set(lease, drawing);
|
|
10735
12367
|
return lease;
|
|
10736
12368
|
}
|
|
12369
|
+
function withLiveClip(metadata, template) {
|
|
12370
|
+
Object.defineProperty(metadata, "clip", { get: () => template?.clip ?? null });
|
|
12371
|
+
return metadata;
|
|
12372
|
+
}
|
|
10737
12373
|
class RuntimeFrames {
|
|
10738
12374
|
model;
|
|
10739
12375
|
pose;
|
|
@@ -10742,6 +12378,7 @@ class RuntimeFrames {
|
|
|
10742
12378
|
order;
|
|
10743
12379
|
tint;
|
|
10744
12380
|
templates;
|
|
12381
|
+
clips;
|
|
10745
12382
|
colours;
|
|
10746
12383
|
/** Incremented when a draw refresh changes colour without a pose step. */
|
|
10747
12384
|
revision = 0;
|
|
@@ -10755,6 +12392,11 @@ class RuntimeFrames {
|
|
|
10755
12392
|
this.tint = new Float64Array(this.model.slots.length * 4);
|
|
10756
12393
|
const colours = [];
|
|
10757
12394
|
const metadata = [];
|
|
12395
|
+
this.clips = this.model.attachments.map((attachment) => attachment.clip === void 0 ? null : {
|
|
12396
|
+
polygon: numbers(this.pose.vertices, attachment.offset, attachment.count),
|
|
12397
|
+
inverse: attachment.clip.inverse,
|
|
12398
|
+
convex: attachment.clip.convex
|
|
12399
|
+
});
|
|
10758
12400
|
this.templates = this.model.attachments.map((attachment, index) => {
|
|
10759
12401
|
const identity = attachment;
|
|
10760
12402
|
if (identity.skin === void 0 || identity.placeholder === void 0) {
|
|
@@ -10772,8 +12414,9 @@ class RuntimeFrames {
|
|
|
10772
12414
|
if (template.kind === "region") template.corners = vertices;
|
|
10773
12415
|
else template.vertices = vertices;
|
|
10774
12416
|
template.tint = numbers(this.tint, attachment.slot * 4, 4);
|
|
12417
|
+
if (slot.dark !== null) template.dark = numbers(this.pose.dark, attachment.slot * 3, 3);
|
|
10775
12418
|
}
|
|
10776
|
-
|
|
12419
|
+
const item = {
|
|
10777
12420
|
slot: slot.name,
|
|
10778
12421
|
slotIndex: attachment.slot,
|
|
10779
12422
|
attachment: attachment.name,
|
|
@@ -10788,9 +12431,10 @@ class RuntimeFrames {
|
|
|
10788
12431
|
count: attachment.count,
|
|
10789
12432
|
uvs: Object.freeze(template?.uvs ?? []),
|
|
10790
12433
|
triangles: Object.freeze(template?.kind === "mesh" ? template.triangles : []),
|
|
10791
|
-
dark: template?.dark
|
|
12434
|
+
dark: template?.dark ?? null,
|
|
10792
12435
|
clip: null
|
|
10793
|
-
}
|
|
12436
|
+
};
|
|
12437
|
+
metadata.push(Object.freeze(this.pose.clipping === null ? item : withLiveClip(item, template)));
|
|
10794
12438
|
return template;
|
|
10795
12439
|
});
|
|
10796
12440
|
this.colours = colours;
|
|
@@ -10818,32 +12462,76 @@ class RuntimeFrames {
|
|
|
10818
12462
|
}
|
|
10819
12463
|
if (colorChanged) this.revision++;
|
|
10820
12464
|
let count = 0;
|
|
10821
|
-
|
|
12465
|
+
let clip = null;
|
|
12466
|
+
let end = null;
|
|
12467
|
+
for (let z = 0; z < this.pose.drawOrder.length; z++) {
|
|
12468
|
+
const si = this.pose.drawOrder[z];
|
|
10822
12469
|
const ai = this.pose.shown[si];
|
|
12470
|
+
const slot = this.model.slots[si];
|
|
12471
|
+
const attachment = ai < 0 ? null : this.model.attachments[ai];
|
|
12472
|
+
if (attachment?.clip !== void 0) {
|
|
12473
|
+
if (clip !== null && end === slot.name) clip = null;
|
|
12474
|
+
if (clip === null && this.model.active[attachment.bone] && draws(slot.name)) {
|
|
12475
|
+
clip = this.clips[ai];
|
|
12476
|
+
end = attachment.clip.end;
|
|
12477
|
+
}
|
|
12478
|
+
continue;
|
|
12479
|
+
}
|
|
10823
12480
|
const template = ai < 0 ? null : this.templates[ai];
|
|
10824
|
-
if (
|
|
10825
|
-
|
|
10826
|
-
|
|
10827
|
-
|
|
10828
|
-
|
|
10829
|
-
|
|
10830
|
-
|
|
12481
|
+
if (template && this.model.active[slot.bone]) {
|
|
12482
|
+
template.z = z;
|
|
12483
|
+
if (clip === null) {
|
|
12484
|
+
if (template.clip !== void 0) delete template.clip;
|
|
12485
|
+
} else template.clip = clip;
|
|
12486
|
+
const offset = si * 4;
|
|
12487
|
+
const colour = this.colours[ai];
|
|
12488
|
+
for (let c = 0; c < 4; c++) this.tint[offset + c] = this.pose.light[offset + c] * colour[c] * color[c];
|
|
12489
|
+
if (draws(template.slot)) {
|
|
12490
|
+
this.order[count] = ai;
|
|
12491
|
+
this.renderFrame.order[count++] = template;
|
|
12492
|
+
}
|
|
12493
|
+
}
|
|
12494
|
+
if (clip !== null && end === slot.name) clip = null;
|
|
10831
12495
|
}
|
|
10832
12496
|
this.renderFrame.order.length = count;
|
|
10833
12497
|
this.drawn = count;
|
|
10834
12498
|
this.epoch = generation;
|
|
10835
12499
|
}
|
|
10836
12500
|
snapshot(draws) {
|
|
10837
|
-
const
|
|
10838
|
-
|
|
12501
|
+
const order = [];
|
|
12502
|
+
const copies = /* @__PURE__ */ new Map();
|
|
12503
|
+
let clip = null;
|
|
12504
|
+
let end = null;
|
|
12505
|
+
for (let z = 0; z < this.pose.drawOrder.length; z++) {
|
|
12506
|
+
const si = this.pose.drawOrder[z];
|
|
10839
12507
|
const ai = this.pose.shown[si];
|
|
12508
|
+
const attachment = ai < 0 ? null : this.model.attachments[ai];
|
|
12509
|
+
const name = this.model.slots[si].name;
|
|
12510
|
+
if (attachment?.clip !== void 0) {
|
|
12511
|
+
if (clip !== null && end === name) clip = null;
|
|
12512
|
+
if (clip === null && this.model.active[attachment.bone] && (draws === void 0 || draws(name))) {
|
|
12513
|
+
clip = this.clips[ai];
|
|
12514
|
+
end = attachment.clip.end;
|
|
12515
|
+
}
|
|
12516
|
+
continue;
|
|
12517
|
+
}
|
|
10840
12518
|
const slot = ai < 0 ? null : this.templates[ai];
|
|
10841
|
-
if (slot && this.model.active[this.model.slots[si].bone] && (draws === void 0 || draws(
|
|
12519
|
+
if (slot && this.model.active[this.model.slots[si].bone] && (draws === void 0 || draws(name))) {
|
|
12520
|
+
const common = { ...slot, z, tint: slot.tint.slice() };
|
|
12521
|
+
if (slot.dark) common.dark = slot.dark.slice();
|
|
12522
|
+
delete common.clip;
|
|
12523
|
+
if (clip !== null) {
|
|
12524
|
+
let copy = copies.get(clip);
|
|
12525
|
+
if (!copy) {
|
|
12526
|
+
copy = { ...clip, polygon: clip.polygon.slice() };
|
|
12527
|
+
copies.set(clip, copy);
|
|
12528
|
+
}
|
|
12529
|
+
common.clip = copy;
|
|
12530
|
+
}
|
|
12531
|
+
order.push(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 });
|
|
12532
|
+
}
|
|
12533
|
+
if (clip !== null && end === name) clip = null;
|
|
10842
12534
|
}
|
|
10843
|
-
const order = selected.map((slot) => {
|
|
10844
|
-
const common = { ...slot, tint: slot.tint.slice() };
|
|
10845
|
-
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 };
|
|
10846
|
-
});
|
|
10847
12535
|
return { order };
|
|
10848
12536
|
}
|
|
10849
12537
|
}
|
|
@@ -11575,6 +13263,70 @@ class RigPlayer {
|
|
|
11575
13263
|
if (x !== 0 || y !== 0) live.physicsTranslate(x, y);
|
|
11576
13264
|
if (motion.rotation) live.physicsRotate(0, 0, motion.rotation);
|
|
11577
13265
|
}
|
|
13266
|
+
/**
|
|
13267
|
+
* Extra apparent motion for selected physics, synchronously before evaluation.
|
|
13268
|
+
* World distances are X right/Y up; rotation is XY roll around an explicit
|
|
13269
|
+
* world pivot, not yaw/pitch. Does not move bones or draw. A step(0), reset,
|
|
13270
|
+
* seek/skin replay or track promotion may consume/discard external history.
|
|
13271
|
+
* Writes outside the selection are untouched; solver dependencies still apply.
|
|
13272
|
+
*/
|
|
13273
|
+
physicsMoved(scope, motion) {
|
|
13274
|
+
const object = (value, fields, where) => {
|
|
13275
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) throw new Error(`physicsMoved: ${where} must be an object`);
|
|
13276
|
+
for (const key of Reflect.ownKeys(value)) if (typeof key !== "string" || !fields.includes(key)) throw new Error(`physicsMoved: unknown ${where} field ${String(key)}`);
|
|
13277
|
+
return value;
|
|
13278
|
+
};
|
|
13279
|
+
const finite2 = (value, field) => {
|
|
13280
|
+
if (typeof value !== "number" || !Number.isFinite(value)) throw new Error(`physicsMoved: ${field} must be a finite number, got ${String(value)}`);
|
|
13281
|
+
return value;
|
|
13282
|
+
};
|
|
13283
|
+
const selection = object(scope, ["bone", "subtree", "constraints"], "scope");
|
|
13284
|
+
const keys = Object.keys(selection);
|
|
13285
|
+
if (keys.length !== 1) throw new Error("physicsMoved: scope must name exactly one of bone, subtree, constraints");
|
|
13286
|
+
const kind = keys[0];
|
|
13287
|
+
let names;
|
|
13288
|
+
if (kind === "constraints") {
|
|
13289
|
+
if (!Array.isArray(selection.constraints)) throw new Error("physicsMoved: constraints must be an array of exact physics names");
|
|
13290
|
+
names = [];
|
|
13291
|
+
for (const name of selection.constraints) {
|
|
13292
|
+
if (typeof name !== "string") throw new Error(`physicsMoved: constraint name ${String(name)} must be a string`);
|
|
13293
|
+
const c = this.view.constraints.find((c2) => c2.kind === "physics" && c2.name === name) ?? this.view.constraints.find((c2) => c2.name === name);
|
|
13294
|
+
if (c === void 0) throw new Error(`physicsMoved: no constraint ${JSON.stringify(name)}`);
|
|
13295
|
+
if (c.kind !== "physics") throw new Error(`physicsMoved: constraint ${JSON.stringify(name)} is ${c.kind}, not physics`);
|
|
13296
|
+
if (!names.includes(name)) names.push(name);
|
|
13297
|
+
}
|
|
13298
|
+
} else {
|
|
13299
|
+
const name = selection[kind];
|
|
13300
|
+
if (typeof name !== "string" || !this.view.bones.some((b) => b.name === name)) throw new Error(`physicsMoved: no bone ${JSON.stringify(name)}`);
|
|
13301
|
+
const parents = new Map(this.view.bones.map((b) => [b.name, b.parent]));
|
|
13302
|
+
const includes = (bone2) => {
|
|
13303
|
+
if (kind === "bone") return bone2 === name;
|
|
13304
|
+
for (let at = bone2; at !== void 0; at = parents.get(at)) if (at === name) return true;
|
|
13305
|
+
return false;
|
|
13306
|
+
};
|
|
13307
|
+
names = this.view.constraints.filter((c) => c.kind === "physics" && includes(c.record?.bone)).map((c) => c.name);
|
|
13308
|
+
}
|
|
13309
|
+
const input = object(motion, ["translation", "rotation"], "motion");
|
|
13310
|
+
if (Object.keys(input).length !== 1) throw new Error("physicsMoved: motion must name exactly one of translation, rotation");
|
|
13311
|
+
let x, y, degrees;
|
|
13312
|
+
if ("translation" in input) {
|
|
13313
|
+
const t = object(input.translation, ["x", "y"], "translation");
|
|
13314
|
+
x = finite2(t.x, "translation.x");
|
|
13315
|
+
y = finite2(t.y, "translation.y");
|
|
13316
|
+
} else {
|
|
13317
|
+
const r = object(input.rotation, ["degrees", "pivot"], "rotation");
|
|
13318
|
+
degrees = finite2(r.degrees, "rotation.degrees");
|
|
13319
|
+
if (!Array.isArray(r.pivot) || r.pivot.length !== 2) throw new Error("physicsMoved: rotation.pivot must be two finite world coordinates");
|
|
13320
|
+
x = finite2(r.pivot[0], "rotation.pivot[0]");
|
|
13321
|
+
y = finite2(r.pivot[1], "rotation.pivot[1]");
|
|
13322
|
+
}
|
|
13323
|
+
if (names.length === 0 || (degrees === void 0 ? x === 0 && y === 0 : degrees === 0)) return;
|
|
13324
|
+
const live = this.stepper();
|
|
13325
|
+
if (live === null) return;
|
|
13326
|
+
if (degrees === void 0) live.physicsTranslate(x, y, names);
|
|
13327
|
+
else live.physicsRotate(x, y, degrees, names);
|
|
13328
|
+
this.clearSeekCache();
|
|
13329
|
+
}
|
|
11578
13330
|
/** Stops stepping; `frame` keeps drawing the pose it holds. */
|
|
11579
13331
|
pause() {
|
|
11580
13332
|
this.paused = true;
|
|
@@ -12455,6 +14207,11 @@ class RigPlayer {
|
|
|
12455
14207
|
this.poserLabel = "oracle: explicitly selected";
|
|
12456
14208
|
return;
|
|
12457
14209
|
}
|
|
14210
|
+
const physics = this.view.constraints.find((constraint) => constraint.kind === "physics");
|
|
14211
|
+
if (physics) {
|
|
14212
|
+
this.poserLabel = `oracle: physics constraint "${physics.name}" requires player clock, seek and movement integration`;
|
|
14213
|
+
return;
|
|
14214
|
+
}
|
|
12458
14215
|
try {
|
|
12459
14216
|
this.runtimeModel = compileRuntime(this.view);
|
|
12460
14217
|
this.poserLabel = this.trackMode ? "oracle: track composition" : this.adjustFn !== null ? "oracle: local adjustment" : "runtime";
|