@rig-c/core 2.72.0 → 2.74.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/README.md +10 -0
- package/docs/AUTHORING.md +106 -6
- package/docs/FACE.md +19 -0
- package/package.json +1 -1
- package/src/alpha_clip.ts +255 -0
- package/src/cli/core_commands.ts +2 -1
- package/src/cli/shared.ts +14 -1
- package/src/compile.ts +286 -3
- package/src/core/clipping.ts +5 -1
- package/src/mesh.ts +3 -1
- package/src/rig.ts +25 -3
- package/src/runtime/curves.ts +35 -0
- package/src/runtime/index.ts +2 -2
- package/src/runtime/model.ts +47 -14
- package/src/runtime/pose.ts +48 -7
- package/src/types.ts +26 -0
package/src/runtime/model.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The runtime poser's load-time half (issue #1591, stage
|
|
2
|
+
* The runtime poser's load-time half (issue #1591, stage 2): a skin view of
|
|
3
3
|
* the compiled model (`readModel`, then `underSkin` / `underNoSkin` — the
|
|
4
4
|
* oracle's reader and its views, read and never written) compiled once into
|
|
5
5
|
* index-resolved bones and slots, a constraint plan, compiled timelines and
|
|
@@ -21,13 +21,13 @@
|
|
|
21
21
|
* differently, or a block the oracle itself leaves out;
|
|
22
22
|
* - **refuses an animation** (`RuntimeModel.refused`; `animation(name)`
|
|
23
23
|
* throws the same `RuntimeRefusal`) when the construct is the animation's
|
|
24
|
-
* — an `inherit` timeline, a
|
|
24
|
+
* — an `inherit` timeline, a sequence timeline, a draw-order
|
|
25
25
|
* timeline, a dark-colour (`rgba2`, `rgb2`) timeline, or an
|
|
26
26
|
* attachment key showing a clipping attachment or a region with no atlas
|
|
27
27
|
* rectangle. Every other animation of the model is posed.
|
|
28
28
|
*
|
|
29
|
-
* `RUNTIME_FEATURES` is the closed list of what can be named. Stage
|
|
30
|
-
* named/no-skin views and
|
|
29
|
+
* `RUNTIME_FEATURES` is the closed list of what can be named. Stage 2 adds mesh deform timelines to the
|
|
30
|
+
* named/no-skin views and event keys, with immutable payloads compiled
|
|
31
31
|
* at load. `ignore: ['events']` remains an explicit diagnostic opt-out; it is
|
|
32
32
|
* never the default. A merged view with conflicting attachments still refuses
|
|
33
33
|
* by name, as the oracle itself cannot resolve that view without file order.
|
|
@@ -49,10 +49,11 @@ import type { ModelVertices } from '../model.ts';
|
|
|
49
49
|
import { activeBones, constraintRecords, CORE_REGION_KINDS, foldInheritMode, readBlend, readColour, shownAttachment, shownRow, sourceOfDoc, underSkin, type CompiledDocument, type CoreAnimation, type CoreBlendMode } from '../core/index.ts';
|
|
50
50
|
import { constraintInactiveWhy, constraintsAbsentWhy, TRANSFORM_PROPERTIES, type CoreIkRecord, type CoreTransformRecord } from '../core/constraints.ts';
|
|
51
51
|
import { RUNTIME_PI } from '../core/world.ts';
|
|
52
|
-
import {
|
|
52
|
+
import { heldArray, timelineIdentity } from '../core/deform.ts';
|
|
53
|
+
import { compileCurve, compileDeformCurve, type RuntimeCurve } from './curves.ts';
|
|
53
54
|
|
|
54
55
|
/** The stage of issue #1591 this poser implements. */
|
|
55
|
-
export const RUNTIME_STAGE =
|
|
56
|
+
export const RUNTIME_STAGE = 2;
|
|
56
57
|
|
|
57
58
|
/** Every construct the runtime poser can refuse, by the name its refusals carry. */
|
|
58
59
|
export const RUNTIME_FEATURES = [
|
|
@@ -68,7 +69,6 @@ export const RUNTIME_FEATURES = [
|
|
|
68
69
|
'skin-conflict',
|
|
69
70
|
'oracle-absent',
|
|
70
71
|
'inherit-timeline',
|
|
71
|
-
'deform-timeline',
|
|
72
72
|
'sequence-timeline',
|
|
73
73
|
'draw-order-timeline',
|
|
74
74
|
'dark-colour-timeline',
|
|
@@ -125,6 +125,9 @@ export interface RuntimePlant {
|
|
|
125
125
|
readonly eventWrapOffByOne?: boolean;
|
|
126
126
|
/** The old restore behavior, which repeats keys up to the restored time. */
|
|
127
127
|
readonly restoreEventsFromStart?: boolean;
|
|
128
|
+
/** Omit deform / multiply offsets by weights too early: negative controls only. */
|
|
129
|
+
readonly omitDeform?: boolean;
|
|
130
|
+
readonly deformAfterWeights?: boolean;
|
|
128
131
|
}
|
|
129
132
|
|
|
130
133
|
// A bone's local values, seven per bone.
|
|
@@ -184,6 +187,10 @@ export const RAD = RUNTIME_PI / 180;
|
|
|
184
187
|
* bindings (bone index, float32 coordinates, float32 weight) in their order.
|
|
185
188
|
*/
|
|
186
189
|
export interface RuntimeAttachment {
|
|
190
|
+
/** Source timeline identity, including linked-mesh inheritance, resolved once. */
|
|
191
|
+
readonly timeline: string | null;
|
|
192
|
+
readonly deformOffset: number;
|
|
193
|
+
readonly deformCount: number;
|
|
187
194
|
readonly slot: number;
|
|
188
195
|
readonly name: string;
|
|
189
196
|
/** Exact resolved source table identity; display names need not be unique. */
|
|
@@ -289,11 +296,21 @@ export interface RuntimeEvent {
|
|
|
289
296
|
readonly balance: number;
|
|
290
297
|
}
|
|
291
298
|
|
|
299
|
+
/** One source record's deform, its held arrays by key, matched after attachment switches. */
|
|
300
|
+
export interface RuntimeDeformTimeline {
|
|
301
|
+
readonly identity: string;
|
|
302
|
+
readonly curve: RuntimeCurve;
|
|
303
|
+
readonly cursor: number;
|
|
304
|
+
readonly held: Float64Array;
|
|
305
|
+
readonly count: number;
|
|
306
|
+
}
|
|
307
|
+
|
|
292
308
|
/** One covered animation, compiled. */
|
|
293
309
|
export interface RuntimeAnimation {
|
|
294
310
|
readonly name: string;
|
|
295
311
|
readonly duration: number;
|
|
296
312
|
readonly bones: readonly RuntimeBoneTimeline[];
|
|
313
|
+
readonly deforms: readonly RuntimeDeformTimeline[];
|
|
297
314
|
readonly slots: readonly RuntimeSlotTimeline[];
|
|
298
315
|
/** Per constraint of the plan, its timeline in this animation or `null`. */
|
|
299
316
|
readonly constraints: ReadonlyArray<RuntimeConstraintTimeline | null>;
|
|
@@ -307,6 +324,9 @@ export interface RuntimeAnimation {
|
|
|
307
324
|
/** The compiled model a pose is made of (`compileRuntime`). Read-only once built. */
|
|
308
325
|
export interface RuntimeModel {
|
|
309
326
|
/** Control-only boundary defect; fixed at load. */
|
|
327
|
+
readonly omitDeform: boolean;
|
|
328
|
+
readonly deformAfterWeights: boolean;
|
|
329
|
+
readonly deformCount: number;
|
|
310
330
|
readonly eventWrapOffByOne: boolean;
|
|
311
331
|
readonly restoreEventsFromStart: boolean;
|
|
312
332
|
readonly doc: CompiledDocument;
|
|
@@ -454,6 +474,7 @@ export function compileRuntime(doc: CompiledDocument, options: RuntimeOptions =
|
|
|
454
474
|
// --- slots and the records they show ---
|
|
455
475
|
const attachments: RuntimeAttachment[] = [];
|
|
456
476
|
let vertexCount = 0;
|
|
477
|
+
let deformCount = 0;
|
|
457
478
|
const sourceOf = sourceOfDoc(doc);
|
|
458
479
|
const attachmentView = plant.attachmentSkin === undefined ? doc : underSkin(doc, plant.attachmentSkin);
|
|
459
480
|
/** Per slot, each placeholder's attachment index (−1 shows nothing) and what it misses, resolved once and its refusal noted for every animation that shows it. */
|
|
@@ -475,7 +496,7 @@ export function compileRuntime(doc: CompiledDocument, options: RuntimeOptions =
|
|
|
475
496
|
if ('conflict' in shown) return { index: -1, missing: ['skin-conflict', `slot "${slot.name}" placeholder "${placeholder}" is filled by skins that disagree`] };
|
|
476
497
|
const row = shownRow(shown);
|
|
477
498
|
const g = shown.record.geometry;
|
|
478
|
-
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 };
|
|
499
|
+
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 };
|
|
479
500
|
let made: RuntimeAttachment | null = null;
|
|
480
501
|
let missing: readonly [RuntimeFeature, string] | null = null;
|
|
481
502
|
if (g?.kind === 'clipping') missing = ['clipping', `slot "${slot.name}" shows clipping attachment "${row.name}"`];
|
|
@@ -519,6 +540,7 @@ export function compileRuntime(doc: CompiledDocument, options: RuntimeOptions =
|
|
|
519
540
|
if (made === null) made = vertexAttachment(at, DRAW_NONE, new Float64Array(0), 0);
|
|
520
541
|
attachments.push(made);
|
|
521
542
|
vertexCount += made.count;
|
|
543
|
+
deformCount += made.deformCount;
|
|
522
544
|
return { index: attachments.length - 1, missing };
|
|
523
545
|
};
|
|
524
546
|
const slots: RuntimeSlot[] = doc.slots.map((slot, si) => {
|
|
@@ -542,7 +564,7 @@ export function compileRuntime(doc: CompiledDocument, options: RuntimeOptions =
|
|
|
542
564
|
const refused: Array<{ animation: string; missing: RuntimeMissing[] }> = [];
|
|
543
565
|
let cursorMax = 0;
|
|
544
566
|
for (const anim of doc.animations) {
|
|
545
|
-
const compiled = compileAnimation(anim, refusals, ignoreEvents, boneIndex, slotIndex, slots, constraints, (si, ph) => resolve(si, ph, anim.name));
|
|
567
|
+
const compiled = compileAnimation(anim, refusals, ignoreEvents, boneIndex, slotIndex, slots, constraints, doc, (si, ph) => resolve(si, ph, anim.name));
|
|
546
568
|
const missing = refusals.byAnimation.get(anim.name);
|
|
547
569
|
if (missing !== undefined && missing.length > 0) refused.push({ animation: anim.name, missing });
|
|
548
570
|
else {
|
|
@@ -554,6 +576,9 @@ export function compileRuntime(doc: CompiledDocument, options: RuntimeOptions =
|
|
|
554
576
|
if (refusals.model.length > 0) throw new RuntimeRefusal(refusals.model);
|
|
555
577
|
const names = doc.animations.map((a) => a.name);
|
|
556
578
|
return {
|
|
579
|
+
omitDeform: plant.omitDeform === true,
|
|
580
|
+
deformAfterWeights: plant.deformAfterWeights === true,
|
|
581
|
+
deformCount,
|
|
557
582
|
eventWrapOffByOne: plant.eventWrapOffByOne === true,
|
|
558
583
|
restoreEventsFromStart: plant.restoreEventsFromStart === true,
|
|
559
584
|
doc,
|
|
@@ -583,13 +608,13 @@ export function compileRuntime(doc: CompiledDocument, options: RuntimeOptions =
|
|
|
583
608
|
};
|
|
584
609
|
}
|
|
585
610
|
|
|
586
|
-
type AttachmentHead = { slot: number; name: string; skin: string; placeholder: string; path: string | null; bone: number; offset: number };
|
|
611
|
+
type AttachmentHead = { timeline: string | null; deformOffset: number; slot: number; name: string; skin: string; placeholder: string; path: string | null; bone: number; offset: number };
|
|
587
612
|
|
|
588
613
|
const NO_INTS = new Int32Array(0);
|
|
589
614
|
const NO_NUMBERS = new Float64Array(0);
|
|
590
615
|
|
|
591
616
|
function vertexAttachment(at: AttachmentHead, draw: number, local: Float64Array, count: number): RuntimeAttachment {
|
|
592
|
-
return { ...at, draw, local, count, bindingCounts: NO_INTS, bindingBones: NO_INTS, bindingX: NO_NUMBERS, bindingY: NO_NUMBERS, bindingWeights: NO_NUMBERS };
|
|
617
|
+
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 };
|
|
593
618
|
}
|
|
594
619
|
|
|
595
620
|
/** A mesh's (or a linked mesh's source's) vertices: every stored number through `Math.fround` (src/core/vertices.ts, *A mesh's vertices*). */
|
|
@@ -615,7 +640,7 @@ function meshAttachment(at: AttachmentHead, vertices: ModelVertices, indexOf: (n
|
|
|
615
640
|
ws.push(Math.fround(b.weight));
|
|
616
641
|
}
|
|
617
642
|
}
|
|
618
|
-
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) };
|
|
643
|
+
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) };
|
|
619
644
|
}
|
|
620
645
|
|
|
621
646
|
function compileIk(r: CoreIkRecord, index: number, applied: boolean, bones: Int32Array, indexOf: (name: string) => number): RuntimeIk {
|
|
@@ -675,6 +700,7 @@ function compileAnimation(
|
|
|
675
700
|
slotIndex: ReadonlyMap<string, number>,
|
|
676
701
|
slots: readonly RuntimeSlot[],
|
|
677
702
|
constraints: readonly RuntimeConstraint[],
|
|
703
|
+
doc: CompiledDocument,
|
|
678
704
|
resolve: (slot: number, placeholder: string | null) => number,
|
|
679
705
|
): RuntimeAnimation {
|
|
680
706
|
const tl = anim.timelines;
|
|
@@ -682,7 +708,7 @@ function compileAnimation(
|
|
|
682
708
|
let cursors = 0;
|
|
683
709
|
const deform = tl.attachments.filter((a) => a.deform !== null);
|
|
684
710
|
const sequence = tl.attachments.filter((a) => a.sequence !== null);
|
|
685
|
-
|
|
711
|
+
|
|
686
712
|
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);
|
|
687
713
|
if (tl.drawOrder.length > 0) refusals.note('draw-order-timeline', `${tl.drawOrder.length} draw-order key(s)`, name);
|
|
688
714
|
|
|
@@ -732,5 +758,12 @@ function compileAnimation(
|
|
|
732
758
|
constraintTimelines[c.index] = { constraint: c.index, curve: compileCurve(keyed.keys, TRANSFORM_PROPERTIES.length), cursor: cursors++, flags: null };
|
|
733
759
|
}
|
|
734
760
|
}
|
|
735
|
-
|
|
761
|
+
const deforms: RuntimeDeformTimeline[] = deform.map(a => {
|
|
762
|
+
const g = doc.skins.find(s => s.name === a.skin)?.attachments[a.slot]?.[a.attachment]?.geometry;
|
|
763
|
+
if (g === undefined || g.kind === 'region' || g.kind === 'linkedmesh') throw new RuntimeInputError(`deform "${a.skin}/${a.slot}/${a.attachment}" has no vertex geometry (readModel refuses it first)`);
|
|
764
|
+
const keys = a.deform!;
|
|
765
|
+
const arrays = keys.map(k => heldArray(g.vertices, k));
|
|
766
|
+
return { identity: `${a.skin}/${a.slot}/${a.attachment}`, cursor: cursors++, curve: compileDeformCurve(keys), count: arrays[0].length, held: Float64Array.from(arrays.flat()) };
|
|
767
|
+
});
|
|
768
|
+
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 }))) };
|
|
736
769
|
}
|
package/src/runtime/pose.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The runtime poser's per-instance half (issue #1591, stage
|
|
2
|
+
* The runtime poser's per-instance half (issue #1591, stage 2): one
|
|
3
3
|
* `RuntimePose` per skeleton on screen, every buffer allocated when it is
|
|
4
4
|
* made and overwritten in place on every pose, so that posing a frame
|
|
5
5
|
* allocates nothing.
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
* hierarchy and never posed again by a constraint, nor the bones below it.
|
|
39
39
|
*/
|
|
40
40
|
import { COLLAPSED_X_AXIS_SQ, RUNTIME_DEG, RUNTIME_PI } from '../core/world.ts';
|
|
41
|
-
import { channelValue, keyAt } from './curves.ts';
|
|
41
|
+
import { channelValue, keyAt, deformFraction, CURVE_STEPPED } from './curves.ts';
|
|
42
42
|
import {
|
|
43
43
|
DRAW_MESH,
|
|
44
44
|
DRAW_NONE,
|
|
@@ -111,6 +111,9 @@ export class RuntimePose {
|
|
|
111
111
|
readonly shown: Int32Array;
|
|
112
112
|
/** Every attachment's world vertices at its `offset`, written by `computeVertices` for the attachments shown. */
|
|
113
113
|
readonly vertices: Float64Array;
|
|
114
|
+
/** Held/interpolated deform values, one pair per binding when weighted. */
|
|
115
|
+
readonly deform: Float64Array;
|
|
116
|
+
private readonly deformed: Uint8Array;
|
|
114
117
|
/** The running sum of `step`'s steps, and the animation time the last pose was posed at. */
|
|
115
118
|
trackTime = 0;
|
|
116
119
|
time = 0;
|
|
@@ -146,6 +149,8 @@ export class RuntimePose {
|
|
|
146
149
|
this.light = new Float64Array(ns * 4);
|
|
147
150
|
this.shown = new Int32Array(ns);
|
|
148
151
|
this.vertices = new Float64Array(model.vertexCount);
|
|
152
|
+
this.deform = new Float64Array(model.deformCount);
|
|
153
|
+
this.deformed = new Uint8Array(model.attachments.length);
|
|
149
154
|
this.cursors = new Int32Array(Math.max(1, model.cursors)).fill(-1);
|
|
150
155
|
this.ikMix = new Float64Array(nc);
|
|
151
156
|
this.ikSoftness = new Float64Array(nc);
|
|
@@ -195,6 +200,7 @@ export class RuntimePose {
|
|
|
195
200
|
this.constraintValues(null, 0);
|
|
196
201
|
this.solveConstraints();
|
|
197
202
|
this.slotValues(null, 0);
|
|
203
|
+
this.deformValues(null, 0);
|
|
198
204
|
}
|
|
199
205
|
|
|
200
206
|
/** The animation posed at its time `t`, from the setup pose (the header). */
|
|
@@ -209,6 +215,7 @@ export class RuntimePose {
|
|
|
209
215
|
this.constraintValues(anim, t);
|
|
210
216
|
this.solveConstraints();
|
|
211
217
|
this.slotValues(anim, t);
|
|
218
|
+
this.deformValues(anim, t);
|
|
212
219
|
}
|
|
213
220
|
|
|
214
221
|
/** 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). */
|
|
@@ -940,6 +947,32 @@ export class RuntimePose {
|
|
|
940
947
|
}
|
|
941
948
|
}
|
|
942
949
|
|
|
950
|
+
/** Reset on every pose, then match source identity after switches; no old frame's offsets survive. */
|
|
951
|
+
private deformValues(anim: RuntimeAnimation | null, t: number): void {
|
|
952
|
+
this.deformed.fill(0);
|
|
953
|
+
this.deform.fill(0);
|
|
954
|
+
if (anim === null || this.model.omitDeform) return;
|
|
955
|
+
for (let k = 0; k < anim.deforms.length; k++) {
|
|
956
|
+
const tl = anim.deforms[k];
|
|
957
|
+
const i = keyAt(tl.curve, this.cursors, tl.cursor, t);
|
|
958
|
+
if (i < 0) continue;
|
|
959
|
+
const held = i + 1 === tl.curve.keys || tl.curve.kinds[i] === CURVE_STEPPED;
|
|
960
|
+
const p = held ? 0 : deformFraction(tl.curve, i, t);
|
|
961
|
+
const start = i * tl.count;
|
|
962
|
+
for (let s = 0; s < this.shown.length; s++) {
|
|
963
|
+
const index = this.shown[s];
|
|
964
|
+
if (index < 0 || !this.model.slots[s].live) continue;
|
|
965
|
+
const at = this.model.attachments[index];
|
|
966
|
+
if (at.timeline !== tl.identity || at.deformCount === 0) continue;
|
|
967
|
+
this.deformed[index] = 1;
|
|
968
|
+
for (let q = 0; q < tl.count; q++) {
|
|
969
|
+
const v = tl.held[start + q];
|
|
970
|
+
this.deform[at.deformOffset + q] = held ? v : v + (tl.held[start + tl.count + q] - v) * p;
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
|
|
943
976
|
/** Every shown attachment's world vertices into `vertices` at its offset (`regionCorners`, `worldVertices`). */
|
|
944
977
|
computeVertices(): void {
|
|
945
978
|
const m = this.model;
|
|
@@ -961,7 +994,8 @@ export class RuntimePose {
|
|
|
961
994
|
const d = W[b + W_D];
|
|
962
995
|
const wx = W[b + W_X];
|
|
963
996
|
const wy = W[b + W_Y];
|
|
964
|
-
const local = at.local;
|
|
997
|
+
const local = this.deformed[index] && at.draw === DRAW_MESH ? this.deform : at.local;
|
|
998
|
+
const lo = this.deformed[index] && at.draw === DRAW_MESH ? at.deformOffset : 0;
|
|
965
999
|
if (at.draw === DRAW_REGION) {
|
|
966
1000
|
for (let q = 0; q < 8; q += 2) {
|
|
967
1001
|
const lx = local[q];
|
|
@@ -971,8 +1005,8 @@ export class RuntimePose {
|
|
|
971
1005
|
}
|
|
972
1006
|
} else {
|
|
973
1007
|
for (let q = 0; q < at.count; q += 2) {
|
|
974
|
-
const x = local[q];
|
|
975
|
-
const y = local[q + 1];
|
|
1008
|
+
const x = local[lo + q];
|
|
1009
|
+
const y = local[lo + q + 1];
|
|
976
1010
|
out[o++] = x * a + y * bb + wx;
|
|
977
1011
|
out[o++] = x * c + y * d + wy;
|
|
978
1012
|
}
|
|
@@ -992,9 +1026,16 @@ export class RuntimePose {
|
|
|
992
1026
|
const end = n + counts[v];
|
|
993
1027
|
for (; n < end; n++) {
|
|
994
1028
|
const b = bones[n] * WORLDS;
|
|
995
|
-
const x = xs[n];
|
|
996
|
-
const y = ys[n];
|
|
997
1029
|
const weight = weights[n];
|
|
1030
|
+
// The pair belongs to this influence, before its transform and weight, never to the logical vertex.
|
|
1031
|
+
const deformed = this.deformed[index] !== 0;
|
|
1032
|
+
const x = deformed ? xs[n] + this.deform[at.deformOffset + n * 2] : xs[n];
|
|
1033
|
+
const y = deformed ? ys[n] + this.deform[at.deformOffset + n * 2 + 1] : ys[n];
|
|
1034
|
+
if (deformed && m.deformAfterWeights) {
|
|
1035
|
+
wx += (xs[n] * W[b + W_A] + ys[n] * W[b + W_B] + W[b + W_X]) * weight + this.deform[at.deformOffset + n * 2];
|
|
1036
|
+
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];
|
|
1037
|
+
continue;
|
|
1038
|
+
}
|
|
998
1039
|
wx += (x * W[b + W_A] + y * W[b + W_B] + W[b + W_X]) * weight;
|
|
999
1040
|
wy += (x * W[b + W_C] + y * W[b + W_D] + W[b + W_Y]) * weight;
|
|
1000
1041
|
}
|
package/src/types.ts
CHANGED
|
@@ -1562,7 +1562,33 @@ export interface DroppedState {
|
|
|
1562
1562
|
why?: string;
|
|
1563
1563
|
}
|
|
1564
1564
|
|
|
1565
|
+
/** Alpha-derived clipping fit, on the source plate's pixel-centre grid. */
|
|
1566
|
+
export interface AlphaClipReport {
|
|
1567
|
+
target: string;
|
|
1568
|
+
source: string;
|
|
1569
|
+
alpha: number;
|
|
1570
|
+
tolerance: number;
|
|
1571
|
+
maxVertices: number;
|
|
1572
|
+
tracedVertices: number;
|
|
1573
|
+
simplifiedVertices: number;
|
|
1574
|
+
vertices: number;
|
|
1575
|
+
artPixels: number;
|
|
1576
|
+
coveredArt: number;
|
|
1577
|
+
coverage: number;
|
|
1578
|
+
falsePositive: number;
|
|
1579
|
+
falseNegative: number;
|
|
1580
|
+
/** Original trace-corner distance to simplified outline, in drawing pixels, before binding. */
|
|
1581
|
+
maxTraceDeviation: number;
|
|
1582
|
+
/** Maximum setup-world displacement caused by final float32 bind/world rounding, in world units. */
|
|
1583
|
+
maxBindingDeviationWorld: number;
|
|
1584
|
+
overshoot: number;
|
|
1585
|
+
/** source normalized UVs in the emitted clipping vertex order, including carrier crossings. */
|
|
1586
|
+
uvs: Array<[number, number]>;
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1565
1589
|
export interface CompileResult {
|
|
1590
|
+
/** Present only for generated clipping; does not alter emitted model/skeleton geometry. */
|
|
1591
|
+
alphaClips?: AlphaClipReport[];
|
|
1566
1592
|
/**
|
|
1567
1593
|
* The compiled model (`src/model.ts`): what the skeleton below was emitted
|
|
1568
1594
|
* from. This cut fills its `bones` and `setupWorld`; every other field is
|