rig-c 0.0.0-stage → 2.20.4
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/.claude-plugin/marketplace.json +19 -0
- package/.claude-plugin/plugin.json +13 -0
- package/LICENSE +30 -0
- package/NOTICE.md +145 -0
- package/README.md +817 -3
- package/bin/rigc.cjs +83 -0
- package/cli.ts +61 -0
- package/cli_core.ts +46 -0
- package/docs/AUTHORING.md +9923 -0
- package/docs/FACE.md +1948 -0
- package/docs/INGEST.md +1488 -0
- package/docs/MOTION.md +1241 -0
- package/docs/PROMPTING.md +109 -0
- package/docs/RIGGING.md +1441 -0
- package/docs/SPEC_COVERAGE.md +357 -0
- package/package.json +108 -4
- package/skills/rigc/SKILL.md +133 -0
- package/skills/rigc-face/SKILL.md +60 -0
- package/skills/rigc-ingest/SKILL.md +78 -0
- package/skills/rigc-motion/SKILL.md +51 -0
- package/skills/rigc-rigging/SKILL.md +49 -0
- package/src/areaband.ts +159 -0
- package/src/assertions/bodies/a01.ts +23 -0
- package/src/assertions/bodies/a02.ts +21 -0
- package/src/assertions/bodies/a03.ts +27 -0
- package/src/assertions/bodies/a04.ts +40 -0
- package/src/assertions/bodies/a05.ts +56 -0
- package/src/assertions/bodies/a06.ts +245 -0
- package/src/assertions/bodies/a07.ts +68 -0
- package/src/assertions/bodies/a08.ts +76 -0
- package/src/assertions/bodies/a09.ts +82 -0
- package/src/assertions/bodies/a10.ts +116 -0
- package/src/assertions/bodies/a11.ts +15 -0
- package/src/assertions/bodies/a12.ts +30 -0
- package/src/assertions/bodies/a13.ts +51 -0
- package/src/assertions/bodies/a14.ts +35 -0
- package/src/assertions/bodies/a15.ts +97 -0
- package/src/assertions/bodies/a16.ts +24 -0
- package/src/assertions/bodies/a17.ts +26 -0
- package/src/assertions/bodies/a18.ts +62 -0
- package/src/assertions/bodies/a19.ts +404 -0
- package/src/assertions/bodies/a20.ts +122 -0
- package/src/assertions/bodies/a21.ts +190 -0
- package/src/assertions/bodies/a22.ts +39 -0
- package/src/assertions/bodies/a23.ts +305 -0
- package/src/assertions/bodies/a24.ts +68 -0
- package/src/assertions/bodies/a25.ts +39 -0
- package/src/assertions/bodies/a26.ts +61 -0
- package/src/assertions/bodies/a27.ts +33 -0
- package/src/assertions/bodies/a28.ts +70 -0
- package/src/assertions/bodies/a29.ts +34 -0
- package/src/assertions/bodies/a30.ts +50 -0
- package/src/assertions/bodies/a31.ts +61 -0
- package/src/assertions/bodies/a32.ts +44 -0
- package/src/assertions/bodies/a33.ts +110 -0
- package/src/assertions/bodies/a34.ts +133 -0
- package/src/assertions/bodies/a35.ts +160 -0
- package/src/assertions/bodies/a36.ts +81 -0
- package/src/assertions/bodies/a37.ts +77 -0
- package/src/assertions/bodies/a38.ts +73 -0
- package/src/assertions/bodies/a39.ts +303 -0
- package/src/assertions/bodies/a40.ts +128 -0
- package/src/assertions/bodies/a42.ts +97 -0
- package/src/assertions/bodies/a43.ts +181 -0
- package/src/assertions/bodies/a44.ts +23 -0
- package/src/assertions/bodies/a45.ts +172 -0
- package/src/assertions/bodies/a46.ts +224 -0
- package/src/assertions/bodies/a47.ts +126 -0
- package/src/assertions/bodies/a48.ts +83 -0
- package/src/assertions/bodies/a49.ts +81 -0
- package/src/assertions/bodies/a50.ts +97 -0
- package/src/assertions/constraint_words.ts +169 -0
- package/src/assertions/emitted/index.ts +148 -0
- package/src/assertions/facts/animated_bones.ts +30 -0
- package/src/assertions/facts/animation_durations.ts +37 -0
- package/src/assertions/facts/atlas_pages.ts +19 -0
- package/src/assertions/facts/atlas_regions.ts +52 -0
- package/src/assertions/facts/bone_timelines.ts +37 -0
- package/src/assertions/facts/constraint_targets.ts +56 -0
- package/src/assertions/facts/constraints.ts +155 -0
- package/src/assertions/facts/deform_survey.ts +27 -0
- package/src/assertions/facts/event_keys.ts +55 -0
- package/src/assertions/facts/linked_meshes.ts +38 -0
- package/src/assertions/facts/mesh_attachments.ts +100 -0
- package/src/assertions/facts/region_joins.ts +34 -0
- package/src/assertions/facts/sequences.ts +85 -0
- package/src/assertions/facts/skeleton_roster.ts +45 -0
- package/src/assertions/facts/skin_entries.ts +37 -0
- package/src/assertions/facts/skin_members.ts +53 -0
- package/src/assertions/facts/slider_composition.ts +78 -0
- package/src/assertions/facts/slot_colour.ts +43 -0
- package/src/assertions/facts/stage.ts +27 -0
- package/src/assertions/facts/stage_box.ts +65 -0
- package/src/assertions/facts/stepped_poses.ts +74 -0
- package/src/assertions/facts/two_colour.ts +52 -0
- package/src/assertions/facts/vertex_polygons.ts +53 -0
- package/src/assertions/footprints.ts +367 -0
- package/src/assertions/harness.ts +109 -0
- package/src/assertions/inward_advance.ts +58 -0
- package/src/assertions/kinds.ts +105 -0
- package/src/assertions/mesh_kinds.ts +56 -0
- package/src/assertions/model/animated_bones.ts +38 -0
- package/src/assertions/model/animation_durations.ts +57 -0
- package/src/assertions/model/atlas_pages.ts +15 -0
- package/src/assertions/model/atlas_regions.ts +76 -0
- package/src/assertions/model/bone_timelines.ts +58 -0
- package/src/assertions/model/constraint_targets.ts +82 -0
- package/src/assertions/model/constraints.ts +233 -0
- package/src/assertions/model/declared.ts +125 -0
- package/src/assertions/model/deform_survey.ts +24 -0
- package/src/assertions/model/event_keys.ts +45 -0
- package/src/assertions/model/given.ts +45 -0
- package/src/assertions/model/index.ts +398 -0
- package/src/assertions/model/linked_meshes.ts +24 -0
- package/src/assertions/model/mesh_attachments.ts +119 -0
- package/src/assertions/model/parse.ts +146 -0
- package/src/assertions/model/region_joins.ts +67 -0
- package/src/assertions/model/runtime_timelines.ts +78 -0
- package/src/assertions/model/sequences.ts +157 -0
- package/src/assertions/model/skeleton_roster.ts +23 -0
- package/src/assertions/model/skin_entries.ts +69 -0
- package/src/assertions/model/skin_members.ts +64 -0
- package/src/assertions/model/slider_composition.ts +193 -0
- package/src/assertions/model/slot_colour.ts +81 -0
- package/src/assertions/model/stage.ts +28 -0
- package/src/assertions/model/stage_box.ts +51 -0
- package/src/assertions/model/stepped_poses.ts +105 -0
- package/src/assertions/model/two_colour.ts +61 -0
- package/src/assertions/model/vertex_polygons.ts +72 -0
- package/src/assertions/reasons.ts +129 -0
- package/src/assertions/region_lookups.ts +61 -0
- package/src/assertions/report.ts +189 -0
- package/src/assertions/values.ts +39 -0
- package/src/atlas.ts +2870 -0
- package/src/ballot.ts +866 -0
- package/src/bonedist.ts +643 -0
- package/src/chainfit.ts +2752 -0
- package/src/chains.ts +170 -0
- package/src/check.ts +4303 -0
- package/src/checkpics.ts +295 -0
- package/src/cli/core_commands.ts +1627 -0
- package/src/cli/repack.ts +414 -0
- package/src/cli/shared.ts +2776 -0
- package/src/cli/spine_commands.ts +820 -0
- package/src/compile.ts +9414 -0
- package/src/core/additive.ts +458 -0
- package/src/core/animation.ts +1050 -0
- package/src/core/clipping.ts +696 -0
- package/src/core/constraints.ts +1876 -0
- package/src/core/constraints_path.ts +964 -0
- package/src/core/constraints_physics.ts +881 -0
- package/src/core/constraints_slider.ts +635 -0
- package/src/core/deform.ts +613 -0
- package/src/core/draw_order.ts +125 -0
- package/src/core/events.ts +135 -0
- package/src/core/hooks.ts +249 -0
- package/src/core/index.ts +1400 -0
- package/src/core/raw.ts +739 -0
- package/src/core/skins.ts +129 -0
- package/src/core/uvs.ts +469 -0
- package/src/core/vertices.ts +490 -0
- package/src/core/walk.ts +197 -0
- package/src/core/world.ts +289 -0
- package/src/correspondence.ts +15 -0
- package/src/deformbuild.ts +60 -0
- package/src/deformgen.ts +630 -0
- package/src/deformmeasure.ts +732 -0
- package/src/deformreport.ts +373 -0
- package/src/deformstructure.ts +386 -0
- package/src/deformsurvey.ts +2162 -0
- package/src/depth.ts +784 -0
- package/src/diff.ts +2252 -0
- package/src/emit.ts +134 -0
- package/src/emit_spine.ts +854 -0
- package/src/errors.ts +53 -0
- package/src/framing.ts +819 -0
- package/src/generation.ts +139 -0
- package/src/ingest.ts +2293 -0
- package/src/json-position.ts +253 -0
- package/src/keyorder.ts +587 -0
- package/src/keys.ts +486 -0
- package/src/ladder.ts +121 -0
- package/src/mesh.ts +2382 -0
- package/src/meshcompare.ts +1188 -0
- package/src/meshquality.ts +2042 -0
- package/src/meshrasters.ts +944 -0
- package/src/meshreduce.ts +1425 -0
- package/src/model.ts +1245 -0
- package/src/motion.ts +809 -0
- package/src/nonfinite.ts +54 -0
- package/src/package_meta.ts +48 -0
- package/src/png.ts +297 -0
- package/src/pose.ts +2324 -0
- package/src/preview.ts +434 -0
- package/src/region_joins.ts +54 -0
- package/src/render.ts +1013 -0
- package/src/render_core.ts +871 -0
- package/src/render_shared.ts +2958 -0
- package/src/repack.ts +495 -0
- package/src/rig.ts +2941 -0
- package/src/slots.ts +892 -0
- package/src/spine_side.ts +138 -0
- package/src/timelines.ts +837 -0
- package/src/trackgen.ts +364 -0
- package/src/transform.ts +310 -0
- package/src/types.ts +1797 -0
- package/src/validate.ts +3875 -0
- package/tools/contact.ts +126 -0
- package/tools/editor_roundtrip.ts +1641 -0
- package/tools/font5x7.ts +101 -0
- package/tools/measure_contact_depth.ts +105 -0
- package/tools/plate.ts +508 -0
- package/tools/png_probe.mjs +72 -0
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the deform survey reads of a skeleton's STRUCTURE — as opposed to what
|
|
3
|
+
* it reads off a posed one — behind one interface, and the model document's
|
|
4
|
+
* implementation of it (issue #1019, step 4b of issue #380).
|
|
5
|
+
*
|
|
6
|
+
* ## Why there are two readers
|
|
7
|
+
*
|
|
8
|
+
* The survey (`src/deformmeasure.ts`) poses a skeleton and measures what each
|
|
9
|
+
* deform key does to it, and its posing has run through rigc's own core since
|
|
10
|
+
* issue #969. Everything else it read was still spine-core's: the animations
|
|
11
|
+
* and their deform timelines, each key's time and expanded vertex array, the
|
|
12
|
+
* runtime's curve storage, which slot a timeline names and which other slots it
|
|
13
|
+
* reaches, which skin holds the mesh, and each slider's settings — so `explain`
|
|
14
|
+
* on a rigc build parsed the pair through the runtime to learn what the model
|
|
15
|
+
* document it carries already states. This module states those reads once
|
|
16
|
+
* (`SurveyStructure`), and gives the model document's reading of them
|
|
17
|
+
* (`modelStructure`). The runtime's reading stays in `src/deformmeasure.ts`,
|
|
18
|
+
* which links spine-core; the survey's body is written once, against the
|
|
19
|
+
* interface, and the survey's record names which pair it ran through.
|
|
20
|
+
*
|
|
21
|
+
* ## What the model states, and what is derived from it — each rule measured
|
|
22
|
+
*
|
|
23
|
+
* Every read below was compared with spine-core's, value for value at
|
|
24
|
+
* tolerance 0 (`tools/survey_hashes.ts structure` over the nineteen recipes
|
|
25
|
+
* `tools/emit_hashes.ts` generates; the deform-core suite's `DM12` over the
|
|
26
|
+
* gallery's builds and 80 hand-written rigs — sliders, second skins and linked
|
|
27
|
+
* meshes in slots of their own among them). Where the model holds the
|
|
28
|
+
* runtime's value as it is, the reader passes it on; where it holds the value
|
|
29
|
+
* the runtime COMPUTED from, the computation is one rigc already runs — the
|
|
30
|
+
* core's, measured against the runtime for posing, or the emitter's own order —
|
|
31
|
+
* and not a second copy. Each derivation, with how often the stated value alone
|
|
32
|
+
* would have been the runtime's on the nineteen:
|
|
33
|
+
*
|
|
34
|
+
* - **A key's time** is the float32 of the stated time (`CoreDeformKey.time`,
|
|
35
|
+
* which the core's reader stores) — the runtime's `frames` are a
|
|
36
|
+
* `Float32Array`. The stated double was the runtime's on 43 of 88 keys.
|
|
37
|
+
* - **A key's vertex array** is what the runtime holds for the key, not the
|
|
38
|
+
* run the document states: positions unweighted (each the float32 sum of the
|
|
39
|
+
* setup coordinate and the offset), offsets weighted (each float32), the run
|
|
40
|
+
* placed at its `offset` into the attachment's whole array and zeros (setup
|
|
41
|
+
* positions, unweighted) around it — `heldArray` in `./core/deform.ts`. The
|
|
42
|
+
* stated run was the runtime's array on 0 of 88.
|
|
43
|
+
* - **A Bézier segment's polyline** is the nine points the runtime samples into
|
|
44
|
+
* its curve storage: the cubic through the key's four handles, from the key's
|
|
45
|
+
* stated time at fraction 0 to the next key's at a far end of `0.99999999` —
|
|
46
|
+
* `bezierPolyline` with `DEFORM_CURVE_END`, the core's deform curve. The four
|
|
47
|
+
* handles are, of course, never the eighteen numbers (0 of 23).
|
|
48
|
+
* - **The animations' order** is the Spine file's, which is not the model's:
|
|
49
|
+
* the emitter keys `animations` in the editor's order over the model's names
|
|
50
|
+
* (`editorAnimationOrder` in `src/compile.ts`, the one call `emitAnimations`
|
|
51
|
+
* is handed), and the survey iterates the file's order. The model's own order
|
|
52
|
+
* was the file's on 14 of 19 rows. Since issue #1026 a `rigc-compiled/3`
|
|
53
|
+
* document states that order (`editorOrder.animations`, computed by the
|
|
54
|
+
* same function at compile time) and the survey reads it; a `/2` or `/1`
|
|
55
|
+
* document's is derived by that function (`fileAnimationOrder`), which
|
|
56
|
+
* since issue #1034 returns the file's order — integer-like names first, as
|
|
57
|
+
* an object keys them, where the comparator alone put `-a` before `5`.
|
|
58
|
+
* - **An animation's deform timelines** are in the order the file keys them:
|
|
59
|
+
* the model's, with each of the three levels (skin, slot, attachment) put
|
|
60
|
+
* through `keyedOrder` (`attachmentTimelinesInFileOrder`, issue #1034) —
|
|
61
|
+
* the model's own order on every name that is not integer-like. The sliders
|
|
62
|
+
* the file keeps from the model.
|
|
63
|
+
* - **The skins** are in the file's order (`fileSkinOrder`, issue #1034): the
|
|
64
|
+
* order a `/3` document states, else the emitter's `editorSkinOrder`. This
|
|
65
|
+
* said, until #1034, that the file keeps a skin's order from the model; the
|
|
66
|
+
* emitter sorts the skins (`default` first, the rest by the editor's
|
|
67
|
+
* comparator), and a probe declaring `default, 5, 10, -a, 2b, 01` read
|
|
68
|
+
* `default, -a, 01, 2b, 5, 10` off spine-core and the declared order off the
|
|
69
|
+
* model — no row had carried a set the comparator moves.
|
|
70
|
+
* - **A slider's duration** is the runtime's (`CoreAnimationTimelines.duration`:
|
|
71
|
+
* the last key time of every timeline, float32), not the document's declared
|
|
72
|
+
* one, which was the runtime's on 1 of 2.
|
|
73
|
+
* - **The slots a deform reaches besides its own** (`timelineSlots`) are the
|
|
74
|
+
* slots of every linked mesh whose source is the deformed record and which
|
|
75
|
+
* plays its timelines (`timelines: true`) — the rule `./core/deform.ts`'s
|
|
76
|
+
* *Which slot a deform timeline moves* measured. The survey reads them as a
|
|
77
|
+
* set: whether any of them draws the mesh, and their slot timelines' key
|
|
78
|
+
* times. No corpus row has one; `DM12`'s linked rigs do, on 20 of 20.
|
|
79
|
+
*
|
|
80
|
+
* ⛔ Nothing here defaults a value the document does not state: a record the
|
|
81
|
+
* timeline names that the document does not hold, or an order the editor's
|
|
82
|
+
* comparator cannot settle, is a `CoreInputError` naming it — and the survey's
|
|
83
|
+
* seam then falls back to spine-core naming why, as the posing does.
|
|
84
|
+
*
|
|
85
|
+
* ## Purity
|
|
86
|
+
*
|
|
87
|
+
* Nothing from the Spine runtime package: what the runtime reader needs to
|
|
88
|
+
* carry beside these handles (the parsed objects themselves) is kept by that
|
|
89
|
+
* reader, in `src/deformmeasure.ts`.
|
|
90
|
+
*/
|
|
91
|
+
import { CompileError } from './errors.ts';
|
|
92
|
+
import { fileAnimationOrder, fileSkinOrder, keyedOrder, skinsInFileOrder } from './compile.ts';
|
|
93
|
+
import { bezierPolyline } from './core/animation.ts';
|
|
94
|
+
import { DEFORM_CURVE_END, heldArray, type CoreAttachmentTimeline, type CoreDeformKey } from './core/deform.ts';
|
|
95
|
+
import type { DialBoneField } from './core/hooks.ts';
|
|
96
|
+
import { CoreInputError, type CompiledDocument, type CoreAnimation, type CoreAttachment } from './core/index.ts';
|
|
97
|
+
|
|
98
|
+
/** The record a mesh is in the model document — the identity a deform timeline keys and the core poses by. */
|
|
99
|
+
export interface SurveyRecord {
|
|
100
|
+
skin: string;
|
|
101
|
+
slot: string;
|
|
102
|
+
placeholder: string;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** A skin, as the survey wears it. One handle per skin: the survey keys its caches on the handle. */
|
|
106
|
+
export interface SurveySkin {
|
|
107
|
+
readonly name: string;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** A mesh a deform timeline moves, as far as the survey reads it. */
|
|
111
|
+
export interface SurveyMesh {
|
|
112
|
+
/** The attachment's name — the record's stated `name`, else its placeholder. */
|
|
113
|
+
readonly name: string;
|
|
114
|
+
readonly triangles: ArrayLike<number>;
|
|
115
|
+
/** Two numbers per vertex. */
|
|
116
|
+
readonly worldVerticesLength: number;
|
|
117
|
+
/** Whether its vertices are bound to bones (`MeshAttachment.bones !== null`). */
|
|
118
|
+
readonly weighted: boolean;
|
|
119
|
+
/** The other slots the deform reaches (`Attachment.timelineSlots`), read as a set. */
|
|
120
|
+
readonly timelineSlots: readonly number[];
|
|
121
|
+
/** The model record it is, or `null` when no skin holds it — the core then refuses to pose it, by name. */
|
|
122
|
+
readonly record: SurveyRecord | null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** How the runtime gets from one key's geometry to the next. */
|
|
126
|
+
export type SurveyCurve = { kind: 'linear' } | { kind: 'stepped' } | { kind: 'bezier'; points: ArrayLike<number> };
|
|
127
|
+
|
|
128
|
+
/** One deform timeline of one animation. */
|
|
129
|
+
export interface SurveyDeformTimeline {
|
|
130
|
+
/** The slot it is filed under, by index into `SurveyStructure.slotName`. */
|
|
131
|
+
readonly slotIndex: number;
|
|
132
|
+
/** The mesh it moves, or `null` when the attachment has a vertex array and no triangles (a bounding box, a clipping polygon, a path). */
|
|
133
|
+
readonly mesh: SurveyMesh | null;
|
|
134
|
+
/** Each key's time, as the runtime stores it. */
|
|
135
|
+
readonly frames: ArrayLike<number>;
|
|
136
|
+
/** Key `frame`'s vertex array, as the runtime holds it. */
|
|
137
|
+
vertices(frame: number): ArrayLike<number> | undefined;
|
|
138
|
+
/** The curve from key `frame` to key `frame + 1`. */
|
|
139
|
+
curve(frame: number): SurveyCurve;
|
|
140
|
+
/** The skin that holds the mesh, by name and handle (`null` when none does), and the placeholder the timeline names. */
|
|
141
|
+
placement(): { skin: string; holder: SurveySkin | null; placeholder: string };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** One animation, as far as the survey reads it. */
|
|
145
|
+
export interface SurveyAnimation {
|
|
146
|
+
readonly name: string;
|
|
147
|
+
/** Its deform timelines, in the runtime's order. */
|
|
148
|
+
readonly deforms: readonly SurveyDeformTimeline[];
|
|
149
|
+
/** Every key time of every timeline filed under one of `slots` — the times a slot's visibility can change at. */
|
|
150
|
+
slotKeyTimes(slots: ReadonlySet<number>): number[];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* One slider constraint, as far as the survey reads it.
|
|
155
|
+
*
|
|
156
|
+
* ⚠️ `stated`, `reader`, `from`, `to` and `scale` are the dial's mapping and are
|
|
157
|
+
* read only for a slider with a `bone`: a bone-less one's dial is its time. The
|
|
158
|
+
* two readers part there and nothing reads the difference — spine-core sets no
|
|
159
|
+
* property on a bone-less slider and leaves its `scale` at 0, where the model
|
|
160
|
+
* document states the parser's 1 (measured, `tools/survey_hashes.ts structure`).
|
|
161
|
+
*/
|
|
162
|
+
export interface SurveySlider {
|
|
163
|
+
readonly name: string;
|
|
164
|
+
/** Its mix at setup. */
|
|
165
|
+
readonly mix: number;
|
|
166
|
+
/** The animation it applies, by name. */
|
|
167
|
+
readonly animation: string;
|
|
168
|
+
/** That animation's duration, as the runtime computes it. */
|
|
169
|
+
readonly duration: number;
|
|
170
|
+
/** Its driving bone, or `null` on the bone-less form. */
|
|
171
|
+
readonly bone: string | null;
|
|
172
|
+
readonly local: boolean;
|
|
173
|
+
/** The bone field its property reads — `null` on a reader this file does not know. */
|
|
174
|
+
readonly stated: DialBoneField | null;
|
|
175
|
+
/** What that reader is called, for a report that has to name an unknown one. */
|
|
176
|
+
readonly reader: string;
|
|
177
|
+
/** `from` — the property value at which the animation's time is `to`. */
|
|
178
|
+
readonly from: number;
|
|
179
|
+
/** `to`. */
|
|
180
|
+
readonly to: number;
|
|
181
|
+
readonly scale: number;
|
|
182
|
+
readonly loop: boolean;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** Everything the survey reads of a skeleton's structure. */
|
|
186
|
+
export interface SurveyStructure {
|
|
187
|
+
/** Every skin, one handle each, in the skeleton's order — not read by the survey, which wears the skin a timeline names; the instruments walk it. */
|
|
188
|
+
readonly skins: readonly SurveySkin[];
|
|
189
|
+
/** In the runtime's order. */
|
|
190
|
+
readonly animations: readonly SurveyAnimation[];
|
|
191
|
+
/** In the skeleton's constraint order. */
|
|
192
|
+
readonly sliders: readonly SurveySlider[];
|
|
193
|
+
/** A slot's name, or `#<index>` for an index that is not a slot. */
|
|
194
|
+
slotName(index: number): string;
|
|
195
|
+
/** How many skins carry this name — the core wears a skin by its name, so two of one name cannot be posed. */
|
|
196
|
+
skinsNamed(name: string): number;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** The bone field each rig-spec property reads. */
|
|
200
|
+
const PROPERTY_FIELD: Record<string, DialBoneField> = { rotate: 'rotation', x: 'x', y: 'y', scaleX: 'scaleX', scaleY: 'scaleY', shearY: 'shearY' };
|
|
201
|
+
|
|
202
|
+
/** The model document's reading of the survey's structure (the header). */
|
|
203
|
+
export function modelStructure(doc: CompiledDocument): SurveyStructure {
|
|
204
|
+
const slotIndex = new Map(doc.slots.map((s, i) => [s.name, i] as const));
|
|
205
|
+
const skinHandles = new Map<string, SurveySkin>();
|
|
206
|
+
const skinOf = (name: string): SurveySkin | null => {
|
|
207
|
+
if (!doc.skins.some((k) => k.name === name)) return null;
|
|
208
|
+
let handle = skinHandles.get(name);
|
|
209
|
+
if (handle === undefined) {
|
|
210
|
+
handle = { name };
|
|
211
|
+
skinHandles.set(name, handle);
|
|
212
|
+
}
|
|
213
|
+
return handle;
|
|
214
|
+
};
|
|
215
|
+
const recordAt = (skin: string, slot: string, placeholder: string): CoreAttachment | undefined => doc.skins.find((k) => k.name === skin)?.attachments[slot]?.[placeholder];
|
|
216
|
+
/** The slots of every linked mesh that plays this record's timelines (the header's last rule). */
|
|
217
|
+
const linkedSlots = (record: SurveyRecord): number[] => {
|
|
218
|
+
const out: number[] = [];
|
|
219
|
+
for (const skin of doc.skins) {
|
|
220
|
+
for (const [slot, table] of Object.entries(skin.attachments)) {
|
|
221
|
+
for (const entry of Object.values(table)) {
|
|
222
|
+
const g = entry.geometry;
|
|
223
|
+
if (g?.kind !== 'linkedmesh' || entry.timelines !== true) continue;
|
|
224
|
+
if (g.skin !== record.skin || g.slot !== record.slot || g.source !== record.placeholder) continue;
|
|
225
|
+
const at = slotIndex.get(slot);
|
|
226
|
+
if (at !== undefined && !out.includes(at)) out.push(at);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return out;
|
|
231
|
+
};
|
|
232
|
+
const meshes = new Map<string, SurveyMesh>();
|
|
233
|
+
const meshOf = (t: CoreAttachmentTimeline): SurveyMesh | null => {
|
|
234
|
+
const key = `${t.skin}/${t.slot}/${t.attachment}`;
|
|
235
|
+
const already = meshes.get(key);
|
|
236
|
+
if (already !== undefined) return already;
|
|
237
|
+
const entry = recordAt(t.skin, t.slot, t.attachment);
|
|
238
|
+
if (entry === undefined) throw new CoreInputError(`the deform timeline ${key} names no record of the model document`);
|
|
239
|
+
const g = entry.geometry;
|
|
240
|
+
if (g?.kind !== 'mesh') return null;
|
|
241
|
+
const record = { skin: t.skin, slot: t.slot, placeholder: t.attachment };
|
|
242
|
+
const mesh: SurveyMesh = {
|
|
243
|
+
name: entry.name ?? t.attachment,
|
|
244
|
+
triangles: g.triangles,
|
|
245
|
+
worldVerticesLength: g.vertices.weighted ? 2 * g.vertices.bindings.length : g.vertices.xy.length,
|
|
246
|
+
weighted: g.vertices.weighted,
|
|
247
|
+
timelineSlots: linkedSlots(record),
|
|
248
|
+
record,
|
|
249
|
+
};
|
|
250
|
+
meshes.set(key, mesh);
|
|
251
|
+
return mesh;
|
|
252
|
+
};
|
|
253
|
+
const deformOf = (t: CoreAttachmentTimeline, keys: readonly CoreDeformKey[]): SurveyDeformTimeline => {
|
|
254
|
+
const at = slotIndex.get(t.slot);
|
|
255
|
+
if (at === undefined) throw new CoreInputError(`the deform timeline ${t.skin}/${t.slot}/${t.attachment} names slot "${t.slot}", which is not a slot of the model document`);
|
|
256
|
+
const mesh = meshOf(t);
|
|
257
|
+
const entry = recordAt(t.skin, t.slot, t.attachment);
|
|
258
|
+
const g = entry?.geometry;
|
|
259
|
+
const vertices = g !== undefined && g.kind !== 'region' && g.kind !== 'linkedmesh' ? g.vertices : null;
|
|
260
|
+
const held = new Map<number, number[]>();
|
|
261
|
+
return {
|
|
262
|
+
slotIndex: at,
|
|
263
|
+
mesh,
|
|
264
|
+
frames: keys.map((k) => k.time),
|
|
265
|
+
vertices: (frame) => {
|
|
266
|
+
const key = keys[frame];
|
|
267
|
+
if (key === undefined || vertices === null) return undefined;
|
|
268
|
+
let array = held.get(frame);
|
|
269
|
+
if (array === undefined) {
|
|
270
|
+
array = heldArray(vertices, key);
|
|
271
|
+
held.set(frame, array);
|
|
272
|
+
}
|
|
273
|
+
return array;
|
|
274
|
+
},
|
|
275
|
+
curve: (frame) => {
|
|
276
|
+
const a = keys[frame];
|
|
277
|
+
const b = keys[frame + 1];
|
|
278
|
+
if (a === undefined || b === undefined) throw new CoreInputError(`the deform timeline ${t.skin}/${t.slot}/${t.attachment} has no segment after key ${frame}`);
|
|
279
|
+
if (a.curve === 'linear') return { kind: 'linear' };
|
|
280
|
+
if (a.curve === 'stepped') return { kind: 'stepped' };
|
|
281
|
+
const c = a.curve;
|
|
282
|
+
return { kind: 'bezier', points: bezierPolyline(a.stated, 0, c[0], c[1], c[2], c[3], b.stated, DEFORM_CURVE_END) };
|
|
283
|
+
},
|
|
284
|
+
placement: () => ({ skin: t.skin, holder: skinOf(t.skin), placeholder: t.attachment }),
|
|
285
|
+
};
|
|
286
|
+
};
|
|
287
|
+
const animationOf = (a: CoreAnimation): SurveyAnimation => {
|
|
288
|
+
const deforms: SurveyDeformTimeline[] = [];
|
|
289
|
+
for (const t of attachmentTimelinesInFileOrder(a.timelines.attachments)) if (t.deform !== null) deforms.push(deformOf(t, t.deform));
|
|
290
|
+
return {
|
|
291
|
+
name: a.name,
|
|
292
|
+
deforms,
|
|
293
|
+
slotKeyTimes: (slots) => {
|
|
294
|
+
const times: number[] = [];
|
|
295
|
+
for (const target of a.timelines.slots) {
|
|
296
|
+
const at = slotIndex.get(target.name);
|
|
297
|
+
if (at === undefined || !slots.has(at)) continue;
|
|
298
|
+
for (const tl of target.timelines) for (const k of tl.keys) times.push(k.time);
|
|
299
|
+
}
|
|
300
|
+
for (const t of a.timelines.attachments) {
|
|
301
|
+
const at = slotIndex.get(t.slot);
|
|
302
|
+
if (at === undefined || !slots.has(at)) continue;
|
|
303
|
+
for (const k of [...(t.deform ?? []), ...(t.sequence ?? [])]) times.push(k.time);
|
|
304
|
+
}
|
|
305
|
+
return times;
|
|
306
|
+
},
|
|
307
|
+
};
|
|
308
|
+
};
|
|
309
|
+
// The order the Spine file lists the animations in (`fileAnimationOrder`,
|
|
310
|
+
// issue #1034): stated by a `rigc-compiled/3` document (`editorOrder`, issue
|
|
311
|
+
// #1026), and derived for a `/2` or `/1` one, which does not state it, by the
|
|
312
|
+
// emitter's own rule — integer-like names first, as the file keys them.
|
|
313
|
+
let order: readonly string[];
|
|
314
|
+
try {
|
|
315
|
+
order = fileAnimationOrder(doc);
|
|
316
|
+
} catch (err) {
|
|
317
|
+
if (!(err instanceof CompileError)) throw err;
|
|
318
|
+
throw new CoreInputError(`the animations' order in the Spine file is not settled by the model's names — ${err.message}`);
|
|
319
|
+
}
|
|
320
|
+
const animations = order.map((name) => {
|
|
321
|
+
const a = doc.animations.find((x) => x.name === name);
|
|
322
|
+
if (a === undefined) throw new CoreInputError(`the editor's order named animation "${name}", which is not in the model document`);
|
|
323
|
+
return animationOf(a);
|
|
324
|
+
});
|
|
325
|
+
const sliders: SurveySlider[] = [];
|
|
326
|
+
for (const c of doc.constraints) {
|
|
327
|
+
const r = c.record;
|
|
328
|
+
if (c.kind !== 'slider' || r?.kind !== 'slider') continue;
|
|
329
|
+
sliders.push({
|
|
330
|
+
name: r.name,
|
|
331
|
+
mix: r.mix,
|
|
332
|
+
animation: r.animation,
|
|
333
|
+
duration: r.timelines.duration,
|
|
334
|
+
bone: r.bone,
|
|
335
|
+
local: r.local,
|
|
336
|
+
stated: PROPERTY_FIELD[r.property] ?? null,
|
|
337
|
+
reader: r.property,
|
|
338
|
+
from: r.from,
|
|
339
|
+
to: r.to,
|
|
340
|
+
scale: r.scale,
|
|
341
|
+
loop: r.loop,
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
// The skins in the order the Spine file lists them (`fileSkinOrder`, issue #1034), as the runtime's structure lists its skins:
|
|
345
|
+
// stated by a `/3` document, derived for a `/2` or `/1` one by the emitter's rule, which a comparator-moved name set shows.
|
|
346
|
+
let skins: CompiledDocument['skins'];
|
|
347
|
+
try {
|
|
348
|
+
skins = skinsInFileOrder(doc.skins, fileSkinOrder(doc)).map((walk) => walk.skin);
|
|
349
|
+
} catch (err) {
|
|
350
|
+
if (!(err instanceof CompileError)) throw err;
|
|
351
|
+
throw new CoreInputError(`the skins' order in the Spine file is not settled by the model's names — ${err.message}`);
|
|
352
|
+
}
|
|
353
|
+
return {
|
|
354
|
+
skins: skins.flatMap((k) => {
|
|
355
|
+
const handle = skinOf(k.name);
|
|
356
|
+
return handle === null || skins.find((x) => x.name === k.name) !== k ? [] : [handle];
|
|
357
|
+
}),
|
|
358
|
+
animations,
|
|
359
|
+
sliders,
|
|
360
|
+
slotName: (index) => doc.slots[index]?.name ?? `#${index}`,
|
|
361
|
+
skinsNamed: (name) => doc.skins.filter((k) => k.name === name).length,
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* An animation's attachment timelines in the order the Spine file keys them
|
|
367
|
+
* (issue #1034): the emitter writes `attachments` as three nested objects —
|
|
368
|
+
* skin, slot, attachment — each filled in the model's order, and an object
|
|
369
|
+
* lists an integer-like key first (`keyedOrder`). So the model's list is
|
|
370
|
+
* grouped skin by skin and slot by slot in the order each first appears, and
|
|
371
|
+
* each level put through `keyedOrder`. Measured on a build whose deform
|
|
372
|
+
* timelines are filed under slots `10`, `5`, `-a` in the model: the file and
|
|
373
|
+
* spine-core list `5, 10, -a`.
|
|
374
|
+
*/
|
|
375
|
+
function attachmentTimelinesInFileOrder(timelines: readonly CoreAttachmentTimeline[]): CoreAttachmentTimeline[] {
|
|
376
|
+
const firsts = (names: readonly string[]): string[] => keyedOrder(names.filter((name, i) => names.indexOf(name) === i));
|
|
377
|
+
const out: CoreAttachmentTimeline[] = [];
|
|
378
|
+
for (const skin of firsts(timelines.map((t) => t.skin))) {
|
|
379
|
+
const inSkin = timelines.filter((t) => t.skin === skin);
|
|
380
|
+
for (const slot of firsts(inSkin.map((t) => t.slot))) {
|
|
381
|
+
const inSlot = inSkin.filter((t) => t.slot === slot);
|
|
382
|
+
for (const attachment of firsts(inSlot.map((t) => t.attachment))) out.push(...inSlot.filter((t) => t.attachment === attachment));
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
return out;
|
|
386
|
+
}
|