@rig-c/play 1.18.0 → 1.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -964,7 +964,8 @@ function clipHalf(input, p, q) {
964
964
  return { points: out, cut };
965
965
  }
966
966
  function clipThrough(shape, vertices, triangles, uvs, clip = clipTriangles, reading = {}) {
967
- const plan = clipShapeOf(shape);
967
+ let plan = clipShapeOf(shape);
968
+ if (plan.kind === "pieces") plan = clipShapeOf({ ...shape, polygon: shape.polygon.map(Math.fround) });
968
969
  if (plan.kind === "refused") return null;
969
970
  if (plan.kind === "convex") return clip(plan.polygon, vertices, triangles, uvs);
970
971
  const f = Math.fround;
@@ -9034,7 +9035,37 @@ function channelValue(curve, i, c, t) {
9034
9035
  const y0 = p[o + q - 1];
9035
9036
  return y0 + (t - x0) / (p[o + q] - x0) * (p[o + q + 1] - y0);
9036
9037
  }
9037
- const RUNTIME_STAGE = 1;
9038
+ function compileDeformCurve(keys) {
9039
+ const curve = compileCurve(keys.map((k) => ({ time: k.time, values: [0], curve: "linear", stated: { time: k.stated, values: [0] } })), 1);
9040
+ for (let i = 0; i < keys.length; i++) {
9041
+ const a = keys[i];
9042
+ curve.kinds[i] = a.curve === "linear" ? CURVE_LINEAR : a.curve === "stepped" ? CURVE_STEPPED : CURVE_BEZIER;
9043
+ if (typeof a.curve === "string" || i + 1 === keys.length) continue;
9044
+ const b = keys[i + 1];
9045
+ const c = a.curve;
9046
+ const inner = bezierPolyline(a.stated, 0, c[0], c[1], c[2], c[3], b.stated, DEFORM_CURVE_END);
9047
+ const o = i * POLYLINE;
9048
+ curve.polylines[o] = a.time;
9049
+ curve.polylines[o + 1] = 0;
9050
+ curve.polylines.set(inner, o + 2);
9051
+ curve.polylines[o + POLYLINE - 2] = b.time;
9052
+ curve.polylines[o + POLYLINE - 1] = 1;
9053
+ }
9054
+ return curve;
9055
+ }
9056
+ function deformFraction(curve, i, t) {
9057
+ if (curve.kinds[i] === CURVE_STEPPED) return 0;
9058
+ if (curve.kinds[i] === CURVE_LINEAR) return (t - curve.times[i]) / (curve.times[i + 1] - curve.times[i]);
9059
+ const p = curve.polylines;
9060
+ const o = i * POLYLINE;
9061
+ let q = 2;
9062
+ while (q < POLYLINE - 2 && p[o + q] < t) q += 2;
9063
+ const x0 = p[o + q - 2], y0 = p[o + q - 1], x1 = p[o + q], y1 = p[o + q + 1];
9064
+ if (q === 2) return y1 * (t - x0) / (x1 - x0);
9065
+ if (q === POLYLINE - 2) return y0 + (1 - y0) * (t - x0) / (x1 - x0);
9066
+ return y0 + (t - x0) / (x1 - x0) * (y1 - y0);
9067
+ }
9068
+ const RUNTIME_STAGE = 2;
9038
9069
  function missingText(m) {
9039
9070
  return `${m.feature}: ${m.detail}${m.animation === null ? "" : ` (animation "${m.animation}")`}`;
9040
9071
  }
@@ -9178,6 +9209,7 @@ function compileRuntime(doc, options = {}, plant = {}) {
9178
9209
  }
9179
9210
  const attachments = [];
9180
9211
  let vertexCount = 0;
9212
+ let deformCount = 0;
9181
9213
  const sourceOf = sourceOfDoc(doc);
9182
9214
  const attachmentView = plant.attachmentSkin === void 0 ? doc : underSkin(doc, plant.attachmentSkin);
9183
9215
  const resolvedBySlot = doc.slots.map(() => /* @__PURE__ */ new Map());
@@ -9198,7 +9230,7 @@ function compileRuntime(doc, options = {}, plant = {}) {
9198
9230
  if ("conflict" in shown) return { index: -1, missing: ["skin-conflict", `slot "${slot.name}" placeholder "${placeholder}" is filled by skins that disagree`] };
9199
9231
  const row = shownRow(shown);
9200
9232
  const g = shown.record.geometry;
9201
- const at = { slot: si, name: row.name, skin: shown.skin, placeholder: shown.placeholder, path: CORE_REGION_KINDS.has(shown.record.kind) ? row.path : null, bone: indexOf(slot.bone), offset: vertexCount };
9233
+ const at = { timeline: timelineIdentity(slot.name, shown), deformOffset: deformCount, slot: si, name: row.name, skin: shown.skin, placeholder: shown.placeholder, path: CORE_REGION_KINDS.has(shown.record.kind) ? row.path : null, bone: indexOf(slot.bone), offset: vertexCount };
9202
9234
  let made = null;
9203
9235
  let missing = null;
9204
9236
  if (g?.kind === "clipping") missing = ["clipping", `slot "${slot.name}" shows clipping attachment "${row.name}"`];
@@ -9240,6 +9272,7 @@ function compileRuntime(doc, options = {}, plant = {}) {
9240
9272
  if (made === null) made = vertexAttachment(at, DRAW_NONE, new Float64Array(0), 0);
9241
9273
  attachments.push(made);
9242
9274
  vertexCount += made.count;
9275
+ deformCount += made.deformCount;
9243
9276
  return { index: attachments.length - 1, missing };
9244
9277
  };
9245
9278
  const slots = doc.slots.map((slot, si) => {
@@ -9261,7 +9294,7 @@ function compileRuntime(doc, options = {}, plant = {}) {
9261
9294
  const refused = [];
9262
9295
  let cursorMax = 0;
9263
9296
  for (const anim of doc.animations) {
9264
- const compiled = compileAnimation(anim, refusals, ignoreEvents, boneIndex2, slotIndex, slots, constraints, (si, ph) => resolve(si, ph, anim.name));
9297
+ const compiled = compileAnimation(anim, refusals, ignoreEvents, boneIndex2, slotIndex, slots, constraints, doc, (si, ph) => resolve(si, ph, anim.name));
9265
9298
  const missing = refusals.byAnimation.get(anim.name);
9266
9299
  if (missing !== void 0 && missing.length > 0) refused.push({ animation: anim.name, missing });
9267
9300
  else {
@@ -9272,6 +9305,9 @@ function compileRuntime(doc, options = {}, plant = {}) {
9272
9305
  if (refusals.model.length > 0) throw new RuntimeRefusal(refusals.model);
9273
9306
  const names = doc.animations.map((a) => a.name);
9274
9307
  return {
9308
+ omitDeform: plant.omitDeform === true,
9309
+ deformAfterWeights: plant.deformAfterWeights === true,
9310
+ deformCount,
9275
9311
  eventWrapOffByOne: plant.eventWrapOffByOne === true,
9276
9312
  restoreEventsFromStart: plant.restoreEventsFromStart === true,
9277
9313
  doc,
@@ -9303,7 +9339,7 @@ function compileRuntime(doc, options = {}, plant = {}) {
9303
9339
  const NO_INTS = new Int32Array(0);
9304
9340
  const NO_NUMBERS = new Float64Array(0);
9305
9341
  function vertexAttachment(at, draw, local, count) {
9306
- return { ...at, draw, local, count, bindingCounts: NO_INTS, bindingBones: NO_INTS, bindingX: NO_NUMBERS, bindingY: NO_NUMBERS, bindingWeights: NO_NUMBERS };
9342
+ return { ...at, draw, local, count, deformCount: draw === DRAW_MESH ? count : 0, bindingCounts: NO_INTS, bindingBones: NO_INTS, bindingX: NO_NUMBERS, bindingY: NO_NUMBERS, bindingWeights: NO_NUMBERS };
9307
9343
  }
9308
9344
  function meshAttachment(at, vertices, indexOf, rewrite, bones) {
9309
9345
  if (!vertices.weighted) {
@@ -9327,7 +9363,7 @@ function meshAttachment(at, vertices, indexOf, rewrite, bones) {
9327
9363
  ws.push(Math.fround(b.weight));
9328
9364
  }
9329
9365
  }
9330
- return { ...at, draw: DRAW_WEIGHTED, local: NO_NUMBERS, count: counts.length * 2, bindingCounts: Int32Array.from(counts), bindingBones: Int32Array.from(ids), bindingX: Float64Array.from(xs), bindingY: Float64Array.from(ys), bindingWeights: Float64Array.from(ws) };
9366
+ return { ...at, deformCount: ids.length * 2, draw: DRAW_WEIGHTED, local: NO_NUMBERS, count: counts.length * 2, bindingCounts: Int32Array.from(counts), bindingBones: Int32Array.from(ids), bindingX: Float64Array.from(xs), bindingY: Float64Array.from(ys), bindingWeights: Float64Array.from(ws) };
9331
9367
  }
9332
9368
  function compileIk(r, index, applied, bones, indexOf) {
9333
9369
  return {
@@ -9387,13 +9423,12 @@ function compileTransform(r, index, applied, bones, indexOf) {
9387
9423
  clamp: r.clamp
9388
9424
  };
9389
9425
  }
9390
- function compileAnimation(anim, refusals, ignoreEvents, boneIndex2, slotIndex, slots, constraints, resolve) {
9426
+ function compileAnimation(anim, refusals, ignoreEvents, boneIndex2, slotIndex, slots, constraints, doc, resolve) {
9391
9427
  const tl = anim.timelines;
9392
9428
  const name = anim.name;
9393
9429
  let cursors = 0;
9394
9430
  const deform = tl.attachments.filter((a) => a.deform !== null);
9395
9431
  const sequence = tl.attachments.filter((a) => a.sequence !== null);
9396
- if (deform.length > 0) refusals.note("deform-timeline", `${deform.length} deform timeline(s): ${deform.slice(0, 3).map((a) => `${a.skin}/${a.slot}/${a.attachment}`).join(", ")}${deform.length > 3 ? ", …" : ""}`, name);
9397
9432
  if (sequence.length > 0) refusals.note("sequence-timeline", `${sequence.length} sequence timeline(s): ${sequence.slice(0, 3).map((a) => `${a.skin}/${a.slot}/${a.attachment}`).join(", ")}${sequence.length > 3 ? ", …" : ""}`, name);
9398
9433
  if (tl.drawOrder.length > 0) refusals.note("draw-order-timeline", `${tl.drawOrder.length} draw-order key(s)`, name);
9399
9434
  const lastBy = (targets) => new Map(targets.map((t) => [t.name, t]));
@@ -9441,7 +9476,14 @@ function compileAnimation(anim, refusals, ignoreEvents, boneIndex2, slotIndex, s
9441
9476
  constraintTimelines[c.index] = { constraint: c.index, curve: compileCurve(keyed.keys, TRANSFORM_PROPERTIES.length), cursor: cursors++, flags: null };
9442
9477
  }
9443
9478
  }
9444
- return { name, duration: tl.duration, bones, slots: slotTimelines, constraints: constraintTimelines, cursors, events: tl.events.length, eventKeys: Object.freeze(ignoreEvents ? [] : tl.events.map((key) => Object.freeze({ name: key.name, time: key.time, int: key.int, float: key.float, string: key.string, volume: key.volume ?? 0, balance: key.balance ?? 0 }))) };
9479
+ const deforms = deform.map((a) => {
9480
+ const g = doc.skins.find((s) => s.name === a.skin)?.attachments[a.slot]?.[a.attachment]?.geometry;
9481
+ if (g === void 0 || g.kind === "region" || g.kind === "linkedmesh") throw new RuntimeInputError(`deform "${a.skin}/${a.slot}/${a.attachment}" has no vertex geometry (readModel refuses it first)`);
9482
+ const keys = a.deform;
9483
+ const arrays = keys.map((k) => heldArray(g.vertices, k));
9484
+ return { identity: `${a.skin}/${a.slot}/${a.attachment}`, cursor: cursors++, curve: compileDeformCurve(keys), count: arrays[0].length, held: Float64Array.from(arrays.flat()) };
9485
+ });
9486
+ return { name, duration: tl.duration, deforms, bones, slots: slotTimelines, constraints: constraintTimelines, cursors, events: tl.events.length, eventKeys: Object.freeze(ignoreEvents ? [] : tl.events.map((key) => Object.freeze({ name: key.name, time: key.time, int: key.int, float: key.float, string: key.string, volume: key.volume ?? 0, balance: key.balance ?? 0 }))) };
9445
9487
  }
9446
9488
  const DEG = RUNTIME_DEG;
9447
9489
  const TWO_PI = 2 * RUNTIME_PI;
@@ -9474,6 +9516,8 @@ class RuntimePose {
9474
9516
  this.light = new Float64Array(ns * 4);
9475
9517
  this.shown = new Int32Array(ns);
9476
9518
  this.vertices = new Float64Array(model.vertexCount);
9519
+ this.deform = new Float64Array(model.deformCount);
9520
+ this.deformed = new Uint8Array(model.attachments.length);
9477
9521
  this.cursors = new Int32Array(Math.max(1, model.cursors)).fill(-1);
9478
9522
  this.ikMix = new Float64Array(nc);
9479
9523
  this.ikSoftness = new Float64Array(nc);
@@ -9523,6 +9567,7 @@ class RuntimePose {
9523
9567
  this.constraintValues(null, 0);
9524
9568
  this.solveConstraints();
9525
9569
  this.slotValues(null, 0);
9570
+ this.deformValues(null, 0);
9526
9571
  }
9527
9572
  /** The animation posed at its time `t`, from the setup pose (the header). */
9528
9573
  apply(t) {
@@ -9536,6 +9581,7 @@ class RuntimePose {
9536
9581
  this.constraintValues(anim, t);
9537
9582
  this.solveConstraints();
9538
9583
  this.slotValues(anim, t);
9584
+ this.deformValues(anim, t);
9539
9585
  }
9540
9586
  /** One step of a looping track: the track time moved by `dt`, the animation posed at it wrapped by the duration (`loopedTime` in src/core/raw.ts). */
9541
9587
  step(dt) {
@@ -10245,6 +10291,31 @@ class RuntimePose {
10245
10291
  }
10246
10292
  }
10247
10293
  }
10294
+ /** Reset on every pose, then match source identity after switches; no old frame's offsets survive. */
10295
+ deformValues(anim, t) {
10296
+ this.deformed.fill(0);
10297
+ this.deform.fill(0);
10298
+ if (anim === null || this.model.omitDeform) return;
10299
+ for (let k = 0; k < anim.deforms.length; k++) {
10300
+ const tl = anim.deforms[k];
10301
+ const i = keyAt(tl.curve, this.cursors, tl.cursor, t);
10302
+ if (i < 0) continue;
10303
+ const held = i + 1 === tl.curve.keys || tl.curve.kinds[i] === CURVE_STEPPED;
10304
+ const p = held ? 0 : deformFraction(tl.curve, i, t);
10305
+ const start = i * tl.count;
10306
+ for (let s = 0; s < this.shown.length; s++) {
10307
+ const index = this.shown[s];
10308
+ if (index < 0 || !this.model.slots[s].live) continue;
10309
+ const at = this.model.attachments[index];
10310
+ if (at.timeline !== tl.identity || at.deformCount === 0) continue;
10311
+ this.deformed[index] = 1;
10312
+ for (let q = 0; q < tl.count; q++) {
10313
+ const v = tl.held[start + q];
10314
+ this.deform[at.deformOffset + q] = held ? v : v + (tl.held[start + tl.count + q] - v) * p;
10315
+ }
10316
+ }
10317
+ }
10318
+ }
10248
10319
  /** Every shown attachment's world vertices into `vertices` at its offset (`regionCorners`, `worldVertices`). */
10249
10320
  computeVertices() {
10250
10321
  const m = this.model;
@@ -10266,7 +10337,8 @@ class RuntimePose {
10266
10337
  const d = W[b + W_D];
10267
10338
  const wx = W[b + W_X];
10268
10339
  const wy = W[b + W_Y];
10269
- const local = at.local;
10340
+ const local = this.deformed[index] && at.draw === DRAW_MESH ? this.deform : at.local;
10341
+ const lo = this.deformed[index] && at.draw === DRAW_MESH ? at.deformOffset : 0;
10270
10342
  if (at.draw === DRAW_REGION) {
10271
10343
  for (let q = 0; q < 8; q += 2) {
10272
10344
  const lx = local[q];
@@ -10276,8 +10348,8 @@ class RuntimePose {
10276
10348
  }
10277
10349
  } else {
10278
10350
  for (let q = 0; q < at.count; q += 2) {
10279
- const x = local[q];
10280
- const y = local[q + 1];
10351
+ const x = local[lo + q];
10352
+ const y = local[lo + q + 1];
10281
10353
  out[o++] = x * a + y * bb + wx;
10282
10354
  out[o++] = x * c + y * d + wy;
10283
10355
  }
@@ -10296,9 +10368,15 @@ class RuntimePose {
10296
10368
  const end = n + counts[v];
10297
10369
  for (; n < end; n++) {
10298
10370
  const b = bones[n] * WORLDS;
10299
- const x = xs[n];
10300
- const y = ys[n];
10301
10371
  const weight = weights[n];
10372
+ const deformed = this.deformed[index] !== 0;
10373
+ const x = deformed ? xs[n] + this.deform[at.deformOffset + n * 2] : xs[n];
10374
+ const y = deformed ? ys[n] + this.deform[at.deformOffset + n * 2 + 1] : ys[n];
10375
+ if (deformed && m.deformAfterWeights) {
10376
+ wx += (xs[n] * W[b + W_A] + ys[n] * W[b + W_B] + W[b + W_X]) * weight + this.deform[at.deformOffset + n * 2];
10377
+ wy += (xs[n] * W[b + W_C] + ys[n] * W[b + W_D] + W[b + W_Y]) * weight + this.deform[at.deformOffset + n * 2 + 1];
10378
+ continue;
10379
+ }
10302
10380
  wx += (x * W[b + W_A] + y * W[b + W_B] + W[b + W_X]) * weight;
10303
10381
  wy += (x * W[b + W_C] + y * W[b + W_D] + W[b + W_Y]) * weight;
10304
10382
  }