@rig-c/core 2.76.0 → 2.78.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/docs/AUTHORING.md +19 -16
- package/package.json +1 -1
- package/src/runtime/curves.ts +16 -7
- package/src/runtime/index.ts +6 -5
- package/src/runtime/model.ts +204 -29
- package/src/runtime/pose.ts +613 -25
package/docs/AUTHORING.md
CHANGED
|
@@ -12647,7 +12647,7 @@ verdict on those figures.
|
|
|
12647
12647
|
| <a id="CE_COMPILE_ALPHA_CLIP_CONFLICT"></a>`CE_COMPILE_ALPHA_CLIP_CONFLICT` | Names authored fields or convex/inverse flags conflicting with generated inside-clipping geometry. Use one authored/generated form.<br>Exact fixed diagnostic chunks (named values between chunks vary): ⟦: alphaClip conflicts with [⟧<br>⟦vertexCount⟧<br>⟦vertices⟧<br>⟦weights⟧<br>⟦boneIndexing⟧<br>⟦, ⟧<br>⟦], convex=⟧<br>⟦, inverse=⟧<br>⟦; required ordinary inside clipping with generated geometry only. Remove authored geometry/flags.⟧ |
|
|
12648
12648
|
| <a id="CE_COMPILE_ALPHA_CLIP_DEFORM"></a>`CE_COMPILE_ALPHA_CLIP_DEFORM` | Names animation and source/clip key/frame counts that cannot share the existing projection contract. Give matching explicit supported transform keys in compatible actual frames, with constant world shares where needed, or author the corresponding clip geometry and motion; weighted binding does not copy source deform.<br>Exact fixed diagnostic chunks (named values between chunks vary): ⟦ alphaClip from "⟧<br>⟦"⟧<br>⟦: animation "⟧<br>⟦" has ⟧<br>⟦ source and ⟧<br>⟦ clip keys; source frame=⟧<br>⟦no vertex geometry⟧<br>⟦clip frame=⟧<br>⟦, constant world influence shares=⟧<br>⟦Required matching explicit affine/depth-yaw/pitch transforms in compatible DeformGeometry frames, ⟧<br>⟦with constant shares for world-space transfer. Fix the named frame/keys or author corresponding clip motion.⟧ |
|
|
12649
12649
|
|
|
12650
|
-
## 15. Posing in place for a player — the runtime poser (stage
|
|
12650
|
+
## 15. Posing in place for a player — the runtime poser (stage 5)
|
|
12651
12651
|
|
|
12652
12652
|
rigc poses a model document twice over, on purpose (issue #1591). The **oracle**
|
|
12653
12653
|
(`src/core/`) is the reference: each pose is a pure function of the document,
|
|
@@ -12673,8 +12673,8 @@ pose.setAnimation('walk'); // a refused animat
|
|
|
12673
12673
|
pose.step(1 / 60); // or pose.apply(t) at an animation time
|
|
12674
12674
|
pose.computeVertices();
|
|
12675
12675
|
// pose.world: 6 numbers per bone (a, b, c, d, worldX, worldY — W_A … W_Y), y up
|
|
12676
|
-
// pose.light: 4 per slot; pose.shown: per slot an index into model.attachments, or −1
|
|
12677
|
-
// pose.vertices: model.attachments[i].count numbers at model.attachments[i].offset
|
|
12676
|
+
// pose.light: 4 per slot; pose.dark: 3 per slot; pose.shown: per slot an index into model.attachments, or −1
|
|
12677
|
+
// pose.drawOrder: slot indices in the live frame's draw order; pose.vertices: model.attachments[i].count numbers at model.attachments[i].offset
|
|
12678
12678
|
```
|
|
12679
12679
|
|
|
12680
12680
|
Each `model.attachments[i]` also carries readonly `skin` and `placeholder`: the
|
|
@@ -12682,13 +12682,13 @@ exact resolved table identity `(skin, model.slots[attachment.slot].name, placeho
|
|
|
12682
12682
|
The display `name` may be shared by different placeholders; it is never a table
|
|
12683
12683
|
key to guess the source from. These load-time strings do not change the pose path.
|
|
12684
12684
|
|
|
12685
|
-
| | what stage
|
|
12685
|
+
| | what stage 5 poses |
|
|
12686
12686
|
| --- | --- |
|
|
12687
12687
|
| bones | local and world transforms, all five inherit modes, skin-required bones under the view's skin (an inactive bone is zeros, as the oracle poses it) |
|
|
12688
|
-
| timelines | `rotate`, `translate(x/y)`, `scale(x/y)`, `shear(x/y)`; a slot's `attachment`, `rgba`, `rgb` and `
|
|
12689
|
-
| constraints | physics with its exact clock, reset and parameter timelines; value-mapped sliders with bone/slot/deform/physics composition; one- and two-bone ik; world-space, absolute transform constraints; the document's order, each followed by the bones below what it moved posed again |
|
|
12688
|
+
| timelines | `rotate`, `translate(x/y)`, `scale(x/y)`, `shear(x/y)`; a slot's `attachment`, light colour (`rgba`, `rgb`, `alpha`) and dark colour (`rgba2`, `rgb2`); `drawOrder`; ik, transform and path timelines; linear, stepped and Bézier keys, each read from a per-pose key cursor |
|
|
12689
|
+
| constraints | physics with its exact clock, reset and parameter timelines, and optional constraint-scoped external translate/rotate; value-mapped sliders with bone/slot/draw-order/deform/physics composition; path constraints across all position, spacing and rotate modes on open or closed, constant-speed or arc-length, weighted or unweighted, deformed curves; one- and two-bone ik; world-space, absolute transform constraints; the document's order, each followed by the bones below what it moved posed again |
|
|
12690
12690
|
| vertices | regions, meshes, weighted meshes and linked meshes, written into the pose's buffer |
|
|
12691
|
-
| deform | weighted and unweighted mesh timelines, linear/stepped/Bézier, per-pose key cursors and in-place buffers; linked meshes inherit their source's timelines only with `timelines: true` |
|
|
12691
|
+
| deform | weighted and unweighted mesh and path timelines, linear/stepped/Bézier, per-pose key cursors and in-place buffers; linked meshes inherit their source's timelines only with `timelines: true` |
|
|
12692
12692
|
| skins and events | named/no-skin views with their skin-required bones and constraints; event delivery in key order over `(last, time]`, tail then head across a wrap |
|
|
12693
12693
|
| time | one animation at full weight from the setup pose, at a time (`apply`) or stepped as a looping track (`step`, the track time wrapped by the duration as `openTrack` wraps it) |
|
|
12694
12694
|
|
|
@@ -12740,8 +12740,12 @@ looping track; it takes the initial reset at animation zero if needed.
|
|
|
12740
12740
|
The physics clock is a running sum, never the animation time; constructor option
|
|
12741
12741
|
`{ clock: physicsClockStep }` can select the render clock without changing track
|
|
12742
12742
|
time. `physicsTime` exposes that sum. A non-finite or negative delta/clock result
|
|
12743
|
-
throws `RuntimeInputError` naming the call. `physicsTranslate(x, y)` and
|
|
12744
|
-
`physicsRotate(x, y, degrees)` shift the previous origins
|
|
12743
|
+
throws `RuntimeInputError` naming the call. `physicsTranslate(x, y, scope?)` and
|
|
12744
|
+
`physicsRotate(x, y, degrees, scope?)` shift the previous origins across every
|
|
12745
|
+
physics constraint when `scope` is omitted, or only across the named physics
|
|
12746
|
+
constraints (`scope: []` moves none; duplicate names apply once; an unknown or
|
|
12747
|
+
non-physics constraint name throws `RuntimeInputError` before mutating any
|
|
12748
|
+
state); `setEnvironment({
|
|
12745
12749
|
wind: [x, y], gravity: [x, y] })` supplies the skeleton directions (positive y
|
|
12746
12750
|
points down), all numbers finite. Offsets, velocities, lags and buffers belong
|
|
12747
12751
|
to each pose; a zero physics mix does not consume its constraint's clock.
|
|
@@ -12757,15 +12761,15 @@ values and blends from the current pose. Ordinary tracks still read only the
|
|
|
12757
12761
|
last target for each bone. An earlier unsupported inherit target is refused
|
|
12758
12762
|
when a slider applies it, even if the track's last target would hide it.
|
|
12759
12763
|
Absolute/additive bone and
|
|
12760
|
-
deform blends and slot switches follow the oracle, including direct scale and
|
|
12761
|
-
colour targets at mix one. Slider physics writes last one pass and affect only
|
|
12764
|
+
deform blends, draw-order keys and slot switches follow the oracle, including direct scale and
|
|
12765
|
+
light/dark colour targets at mix one. Slider physics writes last one pass and affect only
|
|
12762
12766
|
constraints later in update order. An unsupported timeline in a slider's
|
|
12763
12767
|
animation refuses the whole model under that timeline's existing feature name,
|
|
12764
12768
|
so an unselected track cannot hide a construct the slider always applies.
|
|
12765
12769
|
|
|
12766
12770
|
**Everything else is refused at load, by name.** `RuntimeRefusal.missing` lists
|
|
12767
12771
|
every construct met, each `{ feature, detail, animation }`, and the message
|
|
12768
|
-
reads `not posed by the runtime poser (stage
|
|
12772
|
+
reads `not posed by the runtime poser (stage 5), N construct(s) missing:
|
|
12769
12773
|
<feature>: <detail>; …`. A model-level feature refuses the whole model; an
|
|
12770
12774
|
animation-level one refuses that animation alone (`model.refused`;
|
|
12771
12775
|
`model.animation(name)` and `setAnimation(name)` throw its refusal) and every
|
|
@@ -12774,14 +12778,13 @@ refused: nothing is posed approximately.
|
|
|
12774
12778
|
|
|
12775
12779
|
| feature | refuses | what it is |
|
|
12776
12780
|
| --- | --- | --- |
|
|
12777
|
-
| `path-constraint` | the model | a constraint of that kind |
|
|
12778
12781
|
| `local-transform`, `additive-transform` | the model | a transform constraint with `localSource` or `localTarget`, or `additive` |
|
|
12779
|
-
| `inactive-constrained-bone` | the model | an applied ik or
|
|
12782
|
+
| `inactive-constrained-bone` | the model | an applied ik, transform or path constraint naming a bone the view leaves inactive (what the oracle writes there depends on the pass before) |
|
|
12780
12783
|
| `noscale-world-target` | the model | a world-space transform onto a `noScale` or `noScaleOrReflection` bone, whose local values would be read back in that mode |
|
|
12781
12784
|
| `clipping`, `null-atlas-region` | the model when shown at setup, else the animation that keys it | a clipping attachment; a region whose atlas rectangle the model states as `null` |
|
|
12782
12785
|
| `skin-conflict` | as above | a placeholder the skins fill differently |
|
|
12783
|
-
| `oracle-absent` | the model | a pose the oracle itself leaves out (`constraintsAbsentWhy`) |
|
|
12784
|
-
| `inherit-timeline`, `sequence-timeline
|
|
12786
|
+
| `oracle-absent` | the model | a pose the oracle itself leaves out (`constraintsAbsentWhy`, `pathAnimationsWhy`, `steppedPreviousPassWhy`, `sliderDeformsWalkedPathWhy`) |
|
|
12787
|
+
| `inherit-timeline`, `sequence-timeline` | the animation | a timeline of that kind |
|
|
12785
12788
|
|
|
12786
12789
|
A call it cannot answer throws `RuntimeInputError`, naming the call:
|
|
12787
12790
|
`apply: no animation is set (setAnimation first)`, `animation "x" is not one of
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rig-c/core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.78.0",
|
|
4
4
|
"description": "AI-authored 2D rigging and animation, verified before it is written. rigc compiles a rig spec into skeleton data in the Spine 4.3 format, gates it with named assertions held to a spine-core round trip's verdicts, and emits nothing that fails. Ships as an agent skill.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/runtime/curves.ts
CHANGED
|
@@ -46,8 +46,13 @@ export interface RuntimeCurve {
|
|
|
46
46
|
readonly polylines: Float64Array;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
/** A timeline's keys compiled; `channels` is how many values each key carries (0 for an attachment timeline). */
|
|
50
|
-
export function compileCurve(keys: readonly
|
|
49
|
+
/** A timeline's keys compiled; `channels` is how many values each key carries (0 for an attachment or draw-order timeline). */
|
|
50
|
+
export function compileCurve(keys: readonly { readonly time: number }[], channels: 0): RuntimeCurve;
|
|
51
|
+
export function compileCurve(keys: readonly CoreKey[], channels: number): RuntimeCurve;
|
|
52
|
+
export function compileCurve(
|
|
53
|
+
keys: readonly { readonly time: number; readonly values?: readonly number[]; readonly stated?: CoreKey['stated']; readonly curve?: CoreKey['curve'] }[],
|
|
54
|
+
channels: number,
|
|
55
|
+
): RuntimeCurve {
|
|
51
56
|
const n = keys.length;
|
|
52
57
|
const times = new Float64Array(n);
|
|
53
58
|
const values = new Float64Array(n * channels);
|
|
@@ -56,20 +61,24 @@ export function compileCurve(keys: readonly CoreKey[], channels: number): Runtim
|
|
|
56
61
|
for (let i = 0; i < n; i++) {
|
|
57
62
|
const a = keys[i];
|
|
58
63
|
times[i] = a.time;
|
|
59
|
-
|
|
60
|
-
|
|
64
|
+
const av = a.values;
|
|
65
|
+
if (av !== undefined) for (let c = 0; c < channels; c++) values[i * channels + c] = av[c];
|
|
66
|
+
const curve = a.curve ?? 'stepped';
|
|
61
67
|
kinds[i] = curve === 'linear' ? CURVE_LINEAR : curve === 'stepped' ? CURVE_STEPPED : CURVE_BEZIER;
|
|
62
68
|
if (typeof curve === 'string' || i + 1 >= n) continue;
|
|
63
69
|
const b = keys[i + 1];
|
|
70
|
+
const aStated = a.stated!;
|
|
71
|
+
const bStated = b.stated!;
|
|
72
|
+
const bValues = b.values!;
|
|
64
73
|
for (let c = 0; c < channels; c++) {
|
|
65
74
|
// The oracle's polyline (`channelAt`): the segment's float32 ends around the recurrence over the numbers as the document states them.
|
|
66
|
-
const inner = bezierPolyline(
|
|
75
|
+
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]);
|
|
67
76
|
const o = (i * channels + c) * POLYLINE;
|
|
68
77
|
polylines[o] = a.time;
|
|
69
|
-
polylines[o + 1] =
|
|
78
|
+
polylines[o + 1] = av![c];
|
|
70
79
|
for (let q = 0; q < inner.length; q++) polylines[o + 2 + q] = inner[q];
|
|
71
80
|
polylines[o + POLYLINE - 2] = b.time;
|
|
72
|
-
polylines[o + POLYLINE - 1] =
|
|
81
|
+
polylines[o + POLYLINE - 1] = bValues[c];
|
|
73
82
|
}
|
|
74
83
|
}
|
|
75
84
|
return { keys: n, channels, times, values, kinds, polylines };
|
package/src/runtime/index.ts
CHANGED
|
@@ -21,12 +21,13 @@
|
|
|
21
21
|
* corpus through both at the oracle's samples and compares full doubles at
|
|
22
22
|
* tolerance 0 (*The runtime poser* there). The oracle is not changed for it.
|
|
23
23
|
*
|
|
24
|
-
* ## Stage
|
|
24
|
+
* ## Stage 5: what it poses, and what it refuses
|
|
25
25
|
*
|
|
26
26
|
* Bones and their five inherit modes; the rotate, translate, scale and shear
|
|
27
|
-
* timelines; a slot's attachment
|
|
28
|
-
*
|
|
29
|
-
*
|
|
27
|
+
* timelines; a slot's attachment, light colour (`rgba`, `rgb`, `alpha`) and
|
|
28
|
+
* dark colour (`rgba2`, `rgb2`) timelines; draw-order timelines (including
|
|
29
|
+
* slider draw-order application); weighted/unweighted deform timelines; region, mesh, weighted mesh and linked mesh vertices; one- and
|
|
30
|
+
* two-bone ik, world-space absolute transforms, path constraints, physics (with optional movement scope) and value-mapped sliders with their
|
|
30
31
|
* timelines; skin-required bones and constraints under the view's skin. One
|
|
31
32
|
* animation at full weight from the setup pose, at a time or stepped as a
|
|
32
33
|
* looping track. Everything else is refused at load by name with the list of
|
|
@@ -43,7 +44,7 @@
|
|
|
43
44
|
* const pose = new RuntimePose(model);
|
|
44
45
|
* pose.setAnimation('walk');
|
|
45
46
|
* pose.step(1 / 60); // or pose.apply(t)
|
|
46
|
-
* pose.computeVertices(); // pose.world, pose.light, pose.shown, pose.vertices
|
|
47
|
+
* pose.computeVertices(); // pose.world, pose.light, pose.dark, pose.shown, pose.drawOrder, pose.vertices
|
|
47
48
|
* ```
|
|
48
49
|
*
|
|
49
50
|
* This module is the `rig-c/runtime` entry (`exports` in package.json), which
|
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 5): 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
|
|
@@ -12,22 +12,20 @@
|
|
|
12
12
|
* (`RuntimeMissing`), and:
|
|
13
13
|
*
|
|
14
14
|
* - **refuses the model** (`RuntimeRefusal`, every missing feature listed)
|
|
15
|
-
* when one is a property of the whole model — a
|
|
15
|
+
* when one is a property of the whole model — a transform constraint reading or
|
|
16
16
|
* writing local values or adding (`localSource`, `localTarget`,
|
|
17
|
-
* `additive`), an ik or
|
|
17
|
+
* `additive`), an ik, transform or path constraint over a bone the view leaves
|
|
18
18
|
* inactive, a world-space transform onto a `noScale` or
|
|
19
19
|
* `noScaleOrReflection` bone, a clipping attachment or a region with no
|
|
20
20
|
* atlas rectangle shown at setup, a placeholder the skins fill
|
|
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 sequence timeline,
|
|
25
|
-
* timeline, a dark-colour (`rgba2`, `rgb2`) timeline, or an
|
|
24
|
+
* — an `inherit` timeline, a sequence timeline, or an
|
|
26
25
|
* attachment key showing a clipping attachment or a region with no atlas
|
|
27
26
|
* rectangle. Every other animation of the model is posed.
|
|
28
27
|
*
|
|
29
|
-
* `RUNTIME_FEATURES` is the closed list of what can be named. Stage
|
|
30
|
-
* parameter timelines and per-pass writes. Mesh deforms, named/no-skin views
|
|
28
|
+
* `RUNTIME_FEATURES` is the closed list of what can be named. Stage 5 adds draw-order timelines (including slider draw-order application) and dark-colour (`rgba2`, `rgb2`) timelines on top of Stage 4's path constraints and scoped physics movement. Mesh and path deforms, named/no-skin views
|
|
31
29
|
* and immutable event deliveries remain covered. `ignore: ['events']` remains an explicit diagnostic opt-out; it is
|
|
32
30
|
* never the default. A merged view with conflicting attachments still refuses
|
|
33
31
|
* by name, as the oracle itself cannot resolve that view without file order.
|
|
@@ -47,19 +45,19 @@
|
|
|
47
45
|
*/
|
|
48
46
|
import type { ModelVertices } from '../model.ts';
|
|
49
47
|
import { activeBones, constraintRecords, CORE_REGION_KINDS, foldInheritMode, readBlend, readColour, shownAttachment, shownRow, sourceOfDoc, underSkin, type CompiledDocument, type CoreAnimation, type CoreBlendMode } from '../core/index.ts';
|
|
50
|
-
import { constraintInactiveWhy, constraintsAbsentWhy, TRANSFORM_PROPERTIES, type CoreIkRecord, type CoreTransformRecord } from '../core/constraints.ts';
|
|
48
|
+
import { constraintInactiveWhy, constraintsAbsentWhy, pathAnimationsWhy, TRANSFORM_PROPERTIES, type CoreIkRecord, type CoreTransformRecord } from '../core/constraints.ts';
|
|
49
|
+
import { slotBonePlan, type CorePathRecord, type SlotBoneEvent } from '../core/constraints_path.ts';
|
|
51
50
|
import { RUNTIME_PI } from '../core/world.ts';
|
|
52
51
|
import { heldArray, timelineIdentity } from '../core/deform.ts';
|
|
53
|
-
import type
|
|
52
|
+
import { steppedPreviousPassWhy, type CorePhysicsRecord, type PhysicsTimelineKind } from '../core/constraints_physics.ts';
|
|
54
53
|
import type { CoreSliderRecord } from '../core/constraints_slider.ts';
|
|
55
54
|
import { compileCurve, compileDeformCurve, type RuntimeCurve } from './curves.ts';
|
|
56
55
|
|
|
57
56
|
/** The stage of issue #1591 this poser implements. */
|
|
58
|
-
export const RUNTIME_STAGE =
|
|
57
|
+
export const RUNTIME_STAGE = 5;
|
|
59
58
|
|
|
60
59
|
/** Every construct the runtime poser can refuse, by the name its refusals carry. */
|
|
61
60
|
export const RUNTIME_FEATURES = [
|
|
62
|
-
'path-constraint',
|
|
63
61
|
'local-transform',
|
|
64
62
|
'additive-transform',
|
|
65
63
|
'inactive-constrained-bone',
|
|
@@ -70,8 +68,6 @@ export const RUNTIME_FEATURES = [
|
|
|
70
68
|
'oracle-absent',
|
|
71
69
|
'inherit-timeline',
|
|
72
70
|
'sequence-timeline',
|
|
73
|
-
'draw-order-timeline',
|
|
74
|
-
'dark-colour-timeline',
|
|
75
71
|
] as const;
|
|
76
72
|
export type RuntimeFeature = (typeof RUNTIME_FEATURES)[number];
|
|
77
73
|
|
|
@@ -125,11 +121,15 @@ export interface RuntimePlant {
|
|
|
125
121
|
readonly eventWrapOffByOne?: boolean;
|
|
126
122
|
/** The old restore behavior, which repeats keys up to the restored time. */
|
|
127
123
|
readonly restoreEventsFromStart?: boolean;
|
|
128
|
-
/** Omit deform / multiply offsets by weights too early: negative controls only. */
|
|
124
|
+
/** Omit deform / multiply offsets by weights too early / omit path / ignore physics scope: negative controls only. */
|
|
129
125
|
readonly omitPhysics?: boolean;
|
|
130
126
|
readonly sliderAsTrack?: boolean;
|
|
131
127
|
readonly omitDeform?: boolean;
|
|
132
128
|
readonly deformAfterWeights?: boolean;
|
|
129
|
+
readonly omitPath?: boolean;
|
|
130
|
+
readonly ignorePhysicsScope?: boolean;
|
|
131
|
+
readonly omitDrawOrder?: boolean;
|
|
132
|
+
readonly omitDarkColour?: boolean;
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
// A bone's local values, seven per bone.
|
|
@@ -177,6 +177,17 @@ export const IK_BEND_POSITIVE = 1;
|
|
|
177
177
|
export const IK_COMPRESS = 2;
|
|
178
178
|
export const IK_STRETCH = 4;
|
|
179
179
|
|
|
180
|
+
// A path constraint's position, spacing and rotate modes.
|
|
181
|
+
export const PATH_POSITION_FIXED = 0;
|
|
182
|
+
export const PATH_POSITION_PERCENT = 1;
|
|
183
|
+
export const PATH_SPACING_LENGTH = 0;
|
|
184
|
+
export const PATH_SPACING_FIXED = 1;
|
|
185
|
+
export const PATH_SPACING_PERCENT = 2;
|
|
186
|
+
export const PATH_SPACING_PROPORTIONAL = 3;
|
|
187
|
+
export const PATH_ROTATE_TANGENT = 0;
|
|
188
|
+
export const PATH_ROTATE_CHAIN = 1;
|
|
189
|
+
export const PATH_ROTATE_CHAIN_SCALE = 2;
|
|
190
|
+
|
|
180
191
|
/** Degrees to radians, with the runtime's pi (`src/core/world.ts`). */
|
|
181
192
|
export const RAD = RUNTIME_PI / 180;
|
|
182
193
|
|
|
@@ -281,6 +292,36 @@ export interface RuntimeSlider {
|
|
|
281
292
|
animation: RuntimeAnimation;
|
|
282
293
|
}
|
|
283
294
|
|
|
295
|
+
export interface RuntimePath {
|
|
296
|
+
readonly kind: 'path';
|
|
297
|
+
readonly name: string;
|
|
298
|
+
readonly index: number;
|
|
299
|
+
readonly applied: boolean;
|
|
300
|
+
readonly bones: Int32Array;
|
|
301
|
+
readonly slot: number;
|
|
302
|
+
readonly slotBone: number;
|
|
303
|
+
/** Setup path attachment index into `model.attachments`, or −1 when the slot shows no path. */
|
|
304
|
+
attachment: number;
|
|
305
|
+
/** Number of vertex coordinates (`2 * vertexCount`) in the setup path attachment. */
|
|
306
|
+
pathVertices: number;
|
|
307
|
+
readonly closed: boolean;
|
|
308
|
+
readonly constantSpeed: boolean;
|
|
309
|
+
readonly lengths: Float64Array;
|
|
310
|
+
readonly positionMode: number;
|
|
311
|
+
readonly spacingMode: number;
|
|
312
|
+
readonly rotateMode: number;
|
|
313
|
+
readonly offsetRotation: number;
|
|
314
|
+
readonly position: number;
|
|
315
|
+
readonly spacing: number;
|
|
316
|
+
readonly mixRotate: number;
|
|
317
|
+
readonly mixX: number;
|
|
318
|
+
readonly mixY: number;
|
|
319
|
+
/** Constraint index whose `before` or `after` state sets the slot bone's determinant sign (`slotBonePlan`), or −1. */
|
|
320
|
+
readonly slotEventAt: number;
|
|
321
|
+
readonly slotEventAfter: boolean;
|
|
322
|
+
readonly setup: CorePathRecord;
|
|
323
|
+
}
|
|
324
|
+
|
|
284
325
|
export interface RuntimePhysicsTimeline {
|
|
285
326
|
readonly kind: PhysicsTimelineKind;
|
|
286
327
|
readonly targets: Int32Array;
|
|
@@ -293,7 +334,15 @@ export interface RuntimeSliderTimeline {
|
|
|
293
334
|
readonly timeCursor: number;
|
|
294
335
|
readonly mixCursor: number;
|
|
295
336
|
}
|
|
296
|
-
export
|
|
337
|
+
export interface RuntimePathTimeline {
|
|
338
|
+
readonly position: RuntimeCurve | null;
|
|
339
|
+
readonly spacing: RuntimeCurve | null;
|
|
340
|
+
readonly mix: RuntimeCurve | null;
|
|
341
|
+
readonly positionCursor: number;
|
|
342
|
+
readonly spacingCursor: number;
|
|
343
|
+
readonly mixCursor: number;
|
|
344
|
+
}
|
|
345
|
+
export type RuntimeConstraint = RuntimeIk | RuntimeTransform | RuntimePhysics | RuntimeSlider | RuntimePath;
|
|
297
346
|
|
|
298
347
|
/** A bone timeline: the bone, its curve, and per channel the local field it writes and whether it multiplies the setup value (scale) or adds to it. */
|
|
299
348
|
export interface RuntimeBoneTimeline {
|
|
@@ -304,7 +353,7 @@ export interface RuntimeBoneTimeline {
|
|
|
304
353
|
readonly multiply: Uint8Array;
|
|
305
354
|
}
|
|
306
355
|
|
|
307
|
-
/** A slot timeline: an attachment timeline (each key's attachment index, −1 nothing) or a colour timeline (each channel's index into the light colour). */
|
|
356
|
+
/** A slot timeline: an attachment timeline (each key's attachment index, −1 nothing) or a colour timeline (each channel's index into the light or dark colour). */
|
|
308
357
|
export interface RuntimeSlotTimeline {
|
|
309
358
|
readonly slot: number;
|
|
310
359
|
readonly curve: RuntimeCurve;
|
|
@@ -341,6 +390,13 @@ export interface RuntimeDeformTimeline {
|
|
|
341
390
|
readonly count: number;
|
|
342
391
|
}
|
|
343
392
|
|
|
393
|
+
/** An animation's draw-order timeline: each key's slot-index permutation of `slots.length` (`orderOf` in `src/core/draw_order.ts`), expanded once at load. */
|
|
394
|
+
export interface RuntimeDrawOrderTimeline {
|
|
395
|
+
readonly curve: RuntimeCurve;
|
|
396
|
+
readonly cursor: number;
|
|
397
|
+
readonly orders: Int32Array;
|
|
398
|
+
}
|
|
399
|
+
|
|
344
400
|
/** One covered animation, compiled. */
|
|
345
401
|
export interface RuntimeAnimation {
|
|
346
402
|
readonly name: string;
|
|
@@ -351,7 +407,9 @@ export interface RuntimeAnimation {
|
|
|
351
407
|
readonly deforms: readonly RuntimeDeformTimeline[];
|
|
352
408
|
readonly physics: readonly RuntimePhysicsTimeline[];
|
|
353
409
|
readonly sliders: ReadonlyArray<RuntimeSliderTimeline | null>;
|
|
410
|
+
readonly paths: ReadonlyArray<RuntimePathTimeline | null>;
|
|
354
411
|
readonly slots: readonly RuntimeSlotTimeline[];
|
|
412
|
+
readonly drawOrder: RuntimeDrawOrderTimeline | null;
|
|
355
413
|
/** Per constraint of the plan, its timeline in this animation or `null`. */
|
|
356
414
|
readonly constraints: ReadonlyArray<RuntimeConstraintTimeline | null>;
|
|
357
415
|
/** How many key cursors a pose keeps for it. */
|
|
@@ -368,6 +426,13 @@ export interface RuntimeModel {
|
|
|
368
426
|
readonly deformAfterWeights: boolean;
|
|
369
427
|
readonly omitPhysics: boolean;
|
|
370
428
|
readonly sliderAsTrack: boolean;
|
|
429
|
+
readonly omitPath: boolean;
|
|
430
|
+
readonly ignorePhysicsScope: boolean;
|
|
431
|
+
readonly omitDrawOrder: boolean;
|
|
432
|
+
readonly omitDarkColour: boolean;
|
|
433
|
+
readonly hasPath: boolean;
|
|
434
|
+
readonly maxPathSpaces: number;
|
|
435
|
+
readonly maxPathVertices: number;
|
|
371
436
|
readonly deformCount: number;
|
|
372
437
|
readonly eventWrapOffByOne: boolean;
|
|
373
438
|
readonly restoreEventsFromStart: boolean;
|
|
@@ -416,8 +481,8 @@ const BONE_KINDS: Record<string, ReadonlyArray<readonly [number, 0 | 1]>> = {
|
|
|
416
481
|
sheary: [[L_SHEAR_Y, 0]],
|
|
417
482
|
};
|
|
418
483
|
|
|
419
|
-
/** The light colour
|
|
420
|
-
const COLOUR_CHANNELS: Record<string, readonly number[]> = { rgba: [0, 1, 2, 3], rgb: [0, 1, 2], alpha: [3] };
|
|
484
|
+
/** The light (`0 .. 3`) and dark (`4 .. 6`) colour channels each colour timeline sets, in its curve's channel order (`COLOUR_CHANNELS` in `src/core/animation.ts` and `src/core/constraints_slider.ts`). */
|
|
485
|
+
const COLOUR_CHANNELS: Record<string, readonly number[]> = { 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] };
|
|
421
486
|
|
|
422
487
|
/** Where refusals are gathered while a model compiles. */
|
|
423
488
|
class Refusals {
|
|
@@ -443,7 +508,7 @@ export function compileRuntime(doc: CompiledDocument, options: RuntimeOptions =
|
|
|
443
508
|
const refusals = new Refusals(new Set(plant.admit ?? []));
|
|
444
509
|
const ignoreEvents = (options.ignore ?? []).includes('events');
|
|
445
510
|
|
|
446
|
-
const absentWhy = constraintsAbsentWhy(doc);
|
|
511
|
+
const absentWhy = constraintsAbsentWhy(doc) ?? pathAnimationsWhy(doc) ?? steppedPreviousPassWhy(doc);
|
|
447
512
|
if (absentWhy !== null) refusals.note('oracle-absent', `the oracle leaves the bones out: ${absentWhy}`);
|
|
448
513
|
|
|
449
514
|
// --- bones ---
|
|
@@ -454,6 +519,12 @@ export function compileRuntime(doc: CompiledDocument, options: RuntimeOptions =
|
|
|
454
519
|
if (i === undefined) throw new RuntimeInputError(`bone "${name}" is not a bone of this document (readModel refuses it first)`);
|
|
455
520
|
return i;
|
|
456
521
|
};
|
|
522
|
+
const slotIndex = new Map(doc.slots.map((s, i) => [s.name, i]));
|
|
523
|
+
const slotIndexOf = (name: string): number => {
|
|
524
|
+
const i = slotIndex.get(name);
|
|
525
|
+
if (i === undefined) throw new RuntimeInputError(`slot "${name}" is not a slot of this document (readModel refuses it first)`);
|
|
526
|
+
return i;
|
|
527
|
+
};
|
|
457
528
|
const active = activeBones(doc);
|
|
458
529
|
const parents = new Int32Array(nb);
|
|
459
530
|
const modes = new Uint8Array(nb);
|
|
@@ -491,9 +562,13 @@ export function compileRuntime(doc: CompiledDocument, options: RuntimeOptions =
|
|
|
491
562
|
|
|
492
563
|
// --- constraints, in the document's order ---
|
|
493
564
|
const constraints: RuntimeConstraint[] = [];
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
565
|
+
const records = constraintRecords(doc);
|
|
566
|
+
const skipped = new Set<number>();
|
|
567
|
+
records.forEach((r, i) => {
|
|
568
|
+
if (constraintInactiveWhy(r, active) !== null) skipped.add(i);
|
|
569
|
+
});
|
|
570
|
+
const pathPlan = records.some((r) => r.kind === 'path') ? slotBonePlan(doc.bones, active, records, skipped) : new Map<number, SlotBoneEvent | null>();
|
|
571
|
+
for (const r of records) {
|
|
497
572
|
const applied = constraintInactiveWhy(r, active) === null;
|
|
498
573
|
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));
|
|
499
574
|
const index = constraints.length;
|
|
@@ -507,7 +582,9 @@ export function compileRuntime(doc: CompiledDocument, options: RuntimeOptions =
|
|
|
507
582
|
}
|
|
508
583
|
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`);
|
|
509
584
|
if (r.kind === 'ik') constraints.push(compileIk(r, index, applied, bones, indexOf));
|
|
510
|
-
else {
|
|
585
|
+
else if (r.kind === 'path') {
|
|
586
|
+
constraints.push(compilePath(r, index, applied, bones, indexOf, slotIndexOf, pathPlan.get(index) ?? null));
|
|
587
|
+
} else {
|
|
511
588
|
if (r.localSource || r.localTarget) refusals.note('local-transform', `transform constraint "${r.name}" ${[r.localSource ? 'reads local values (localSource)' : '', r.localTarget ? 'writes local values (localTarget)' : ''].filter((x) => x !== '').join(' and ')}`);
|
|
512
589
|
if (r.additive) refusals.note('additive-transform', `transform constraint "${r.name}" is additive`);
|
|
513
590
|
const t = compileTransform(r, index, applied, bones, indexOf);
|
|
@@ -585,8 +662,10 @@ export function compileRuntime(doc: CompiledDocument, options: RuntimeOptions =
|
|
|
585
662
|
const source = g.kind === 'mesh' ? g.vertices : sourceOf(g.skin, g.slot, g.source);
|
|
586
663
|
if (typeof source === 'string') throw new RuntimeInputError(`slot "${slot.name}": ${source} (readModel refuses it first)`);
|
|
587
664
|
made = meshAttachment(at, source, indexOf, plant.bindingIndex, nb);
|
|
665
|
+
} else if (g?.kind === 'path') {
|
|
666
|
+
made = pathAttachment(at, g.vertices, indexOf, plant.bindingIndex, nb);
|
|
588
667
|
}
|
|
589
|
-
// A bounding box, a
|
|
668
|
+
// A bounding box, a refused record admitted by a plant: the row's name and path, and nothing drawn.
|
|
590
669
|
if (made === null) made = vertexAttachment(at, DRAW_NONE, new Float64Array(0), 0);
|
|
591
670
|
attachments.push(made);
|
|
592
671
|
vertexCount += made.count;
|
|
@@ -607,7 +686,21 @@ export function compileRuntime(doc: CompiledDocument, options: RuntimeOptions =
|
|
|
607
686
|
blend: readBlend(slot),
|
|
608
687
|
};
|
|
609
688
|
});
|
|
610
|
-
|
|
689
|
+
let hasPath = false;
|
|
690
|
+
let maxPathSpaces = 0;
|
|
691
|
+
let maxPathVertices = 0;
|
|
692
|
+
for (const c of constraints) {
|
|
693
|
+
if (c.kind !== 'path') continue;
|
|
694
|
+
hasPath = true;
|
|
695
|
+
c.attachment = c.setup.path === null ? -1 : slots[c.slot].setupAttachment;
|
|
696
|
+
const spaceCount = c.rotateMode === PATH_ROTATE_TANGENT ? c.bones.length : c.bones.length + 1;
|
|
697
|
+
if (spaceCount > maxPathSpaces) maxPathSpaces = spaceCount;
|
|
698
|
+
if (c.attachment >= 0) {
|
|
699
|
+
const at = attachments[c.attachment];
|
|
700
|
+
c.pathVertices = at.bindingCounts.length > 0 ? at.bindingCounts.length * 2 : at.local.length;
|
|
701
|
+
if (c.pathVertices > maxPathVertices) maxPathVertices = c.pathVertices;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
611
704
|
|
|
612
705
|
// --- animations ---
|
|
613
706
|
const animations = new Map<string, RuntimeAnimation>();
|
|
@@ -635,6 +728,13 @@ export function compileRuntime(doc: CompiledDocument, options: RuntimeOptions =
|
|
|
635
728
|
sliderAsTrack: plant.sliderAsTrack === true,
|
|
636
729
|
omitDeform: plant.omitDeform === true,
|
|
637
730
|
deformAfterWeights: plant.deformAfterWeights === true,
|
|
731
|
+
omitPath: plant.omitPath === true,
|
|
732
|
+
ignorePhysicsScope: plant.ignorePhysicsScope === true,
|
|
733
|
+
omitDrawOrder: plant.omitDrawOrder === true,
|
|
734
|
+
omitDarkColour: plant.omitDarkColour === true,
|
|
735
|
+
hasPath,
|
|
736
|
+
maxPathSpaces,
|
|
737
|
+
maxPathVertices,
|
|
638
738
|
deformCount,
|
|
639
739
|
eventWrapOffByOne: plant.eventWrapOffByOne === true,
|
|
640
740
|
restoreEventsFromStart: plant.restoreEventsFromStart === true,
|
|
@@ -700,6 +800,12 @@ function meshAttachment(at: AttachmentHead, vertices: ModelVertices, indexOf: (n
|
|
|
700
800
|
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) };
|
|
701
801
|
}
|
|
702
802
|
|
|
803
|
+
/** A path attachment's vertices and bindings: stored and deformed like a mesh, never drawn into `RuntimePose.vertices`. */
|
|
804
|
+
function pathAttachment(at: AttachmentHead, vertices: ModelVertices, indexOf: (name: string) => number, rewrite: RuntimePlant['bindingIndex'], bones: number): RuntimeAttachment {
|
|
805
|
+
const m = meshAttachment(at, vertices, indexOf, rewrite, bones);
|
|
806
|
+
return { ...m, draw: DRAW_NONE, count: 0 };
|
|
807
|
+
}
|
|
808
|
+
|
|
703
809
|
function compileIk(r: CoreIkRecord, index: number, applied: boolean, bones: Int32Array, indexOf: (name: string) => number): RuntimeIk {
|
|
704
810
|
return {
|
|
705
811
|
kind: 'ik', name: r.name, index, applied, bones, target: indexOf(r.target),
|
|
@@ -712,6 +818,43 @@ function ikFlags(bendPositive: boolean, compress: boolean, stretch: boolean): nu
|
|
|
712
818
|
return (bendPositive ? IK_BEND_POSITIVE : 0) | (compress ? IK_COMPRESS : 0) | (stretch ? IK_STRETCH : 0);
|
|
713
819
|
}
|
|
714
820
|
|
|
821
|
+
function compilePath(
|
|
822
|
+
r: CorePathRecord,
|
|
823
|
+
index: number,
|
|
824
|
+
applied: boolean,
|
|
825
|
+
bones: Int32Array,
|
|
826
|
+
indexOf: (name: string) => number,
|
|
827
|
+
slotIndexOf: (name: string) => number,
|
|
828
|
+
ev: SlotBoneEvent | null,
|
|
829
|
+
): RuntimePath {
|
|
830
|
+
return {
|
|
831
|
+
kind: 'path',
|
|
832
|
+
name: r.name,
|
|
833
|
+
index,
|
|
834
|
+
applied,
|
|
835
|
+
bones,
|
|
836
|
+
slot: slotIndexOf(r.slot),
|
|
837
|
+
slotBone: indexOf(r.slotBone),
|
|
838
|
+
attachment: -1,
|
|
839
|
+
pathVertices: 0,
|
|
840
|
+
closed: r.path?.closed ?? false,
|
|
841
|
+
constantSpeed: r.path?.constantSpeed ?? true,
|
|
842
|
+
lengths: Float64Array.from(r.path?.lengths ?? []),
|
|
843
|
+
positionMode: r.positionMode === 'fixed' ? PATH_POSITION_FIXED : PATH_POSITION_PERCENT,
|
|
844
|
+
spacingMode: r.spacingMode === 'fixed' ? PATH_SPACING_FIXED : r.spacingMode === 'percent' ? PATH_SPACING_PERCENT : r.spacingMode === 'proportional' ? PATH_SPACING_PROPORTIONAL : PATH_SPACING_LENGTH,
|
|
845
|
+
rotateMode: r.rotateMode === 'chain' ? PATH_ROTATE_CHAIN : r.rotateMode === 'chainScale' ? PATH_ROTATE_CHAIN_SCALE : PATH_ROTATE_TANGENT,
|
|
846
|
+
offsetRotation: r.offsetRotation,
|
|
847
|
+
position: r.position,
|
|
848
|
+
spacing: r.spacing,
|
|
849
|
+
mixRotate: r.mixRotate,
|
|
850
|
+
mixX: r.mixX,
|
|
851
|
+
mixY: r.mixY,
|
|
852
|
+
slotEventAt: ev === null ? -1 : ev.at,
|
|
853
|
+
slotEventAfter: ev?.when === 'after',
|
|
854
|
+
setup: r,
|
|
855
|
+
};
|
|
856
|
+
}
|
|
857
|
+
|
|
715
858
|
function compileTransform(r: CoreTransformRecord, index: number, applied: boolean, bones: Int32Array, indexOf: (name: string) => number): RuntimeTransform {
|
|
716
859
|
const fromProperty: number[] = [];
|
|
717
860
|
const fromOffset: number[] = [];
|
|
@@ -767,7 +910,28 @@ function compileAnimation(
|
|
|
767
910
|
const sequence = tl.attachments.filter((a) => a.sequence !== null);
|
|
768
911
|
|
|
769
912
|
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);
|
|
770
|
-
|
|
913
|
+
|
|
914
|
+
let drawOrder: RuntimeDrawOrderTimeline | null = null;
|
|
915
|
+
if (tl.drawOrder.length > 0) {
|
|
916
|
+
const ns = slots.length;
|
|
917
|
+
const orders = new Int32Array(tl.drawOrder.length * ns).fill(-1);
|
|
918
|
+
const moved = new Uint8Array(ns);
|
|
919
|
+
for (let i = 0; i < tl.drawOrder.length; i++) {
|
|
920
|
+
const base = i * ns;
|
|
921
|
+
moved.fill(0);
|
|
922
|
+
for (const m of tl.drawOrder[i].moves) {
|
|
923
|
+
orders[base + m.slot + m.offset] = m.slot;
|
|
924
|
+
moved[m.slot] = 1;
|
|
925
|
+
}
|
|
926
|
+
for (let s = 0, d = 0; s < ns; s++) {
|
|
927
|
+
if (moved[s] !== 0) continue;
|
|
928
|
+
while (orders[base + d] !== -1) d++;
|
|
929
|
+
orders[base + d] = s;
|
|
930
|
+
d++;
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
drawOrder = { curve: compileCurve(tl.drawOrder, 0), cursor: cursors++, orders };
|
|
934
|
+
}
|
|
771
935
|
|
|
772
936
|
const lastBy = <T extends { name: string }>(targets: readonly T[]): Map<string, T> => new Map(targets.map((t) => [t.name, t]));
|
|
773
937
|
const bones: RuntimeBoneTimeline[] = [];
|
|
@@ -809,8 +973,6 @@ function compileAnimation(
|
|
|
809
973
|
if (t.kind === 'attachment') {
|
|
810
974
|
const ids = Int32Array.from(t.keys.map((k) => resolve(si, k.name ?? null)));
|
|
811
975
|
slotTimelines.push({ slot: si, curve: compileCurve(t.keys, 0), cursor: cursors++, attachments: ids, channels: null });
|
|
812
|
-
} else if (t.kind === 'rgba2' || t.kind === 'rgb2') {
|
|
813
|
-
refusals.note('dark-colour-timeline', `slot "${target.name}" keys an ${t.kind} timeline`, name);
|
|
814
976
|
} else {
|
|
815
977
|
const channels = COLOUR_CHANNELS[t.kind];
|
|
816
978
|
slotTimelines.push({ slot: si, curve: compileCurve(t.keys, channels.length), cursor: cursors++, attachments: null, channels: Int32Array.from(channels) });
|
|
@@ -835,6 +997,19 @@ function compileAnimation(
|
|
|
835
997
|
if (keyed === undefined) return null;
|
|
836
998
|
return { time: keyed.time === null ? null : compileCurve(keyed.time, 1), mix: keyed.mix === null ? null : compileCurve(keyed.mix, 1), timeCursor: cursors++, mixCursor: cursors++ };
|
|
837
999
|
});
|
|
1000
|
+
const paths = constraints.map((c): RuntimePathTimeline | null => {
|
|
1001
|
+
if (c.kind !== 'path') return null;
|
|
1002
|
+
const keyed = anim.constraints.path.find((t) => t.name === c.name);
|
|
1003
|
+
if (keyed === undefined) return null;
|
|
1004
|
+
return {
|
|
1005
|
+
position: keyed.position === undefined ? null : compileCurve(keyed.position, 1),
|
|
1006
|
+
spacing: keyed.spacing === undefined ? null : compileCurve(keyed.spacing, 1),
|
|
1007
|
+
mix: keyed.mix === undefined ? null : compileCurve(keyed.mix, 3),
|
|
1008
|
+
positionCursor: cursors++,
|
|
1009
|
+
spacingCursor: cursors++,
|
|
1010
|
+
mixCursor: cursors++,
|
|
1011
|
+
};
|
|
1012
|
+
});
|
|
838
1013
|
const physics: RuntimePhysicsTimeline[] = (anim.constraints.physicsKeyed ?? []).map(t => ({
|
|
839
1014
|
kind: t.kind, cursor: cursors++, curve: compileCurve(t.keys, t.kind === 'reset' ? 0 : 1),
|
|
840
1015
|
targets: Int32Array.from(constraints.filter((c): c is RuntimePhysics => c.kind === 'physics' && c.applied && (t.name === '*' ? t.kind === 'reset' || c.setup.global[t.kind] : c.name === t.name)).map(c => c.index)),
|
|
@@ -846,5 +1021,5 @@ function compileAnimation(
|
|
|
846
1021
|
const arrays = keys.map(k => heldArray(g.vertices, k));
|
|
847
1022
|
return { identity: `${a.skin}/${a.slot}/${a.attachment}`, cursor: cursors++, curve: compileDeformCurve(keys), count: arrays[0].length, held: Float64Array.from(arrays.flat()) };
|
|
848
1023
|
});
|
|
849
|
-
return { name, duration: tl.duration, physics, sliders, deforms, bones, sliderBones, 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 }))) };
|
|
1024
|
+
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 }))) };
|
|
850
1025
|
}
|