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,357 @@
|
|
|
1
|
+
# Spine 4.3 export-format surface
|
|
2
|
+
|
|
3
|
+
This page is the Spine 4.3 skeleton JSON and atlas text surface, read off the `spine-core`
|
|
4
|
+
this package pins (`@esotericsoftware/spine-core@4.3.13`): every field the parser reads, what it
|
|
5
|
+
defaults to, and the parser line each row cites. rigc's refusals point at its rows by part
|
|
6
|
+
number — `part 1-6` is §1.6. It is not a statement of what rigc emits; that is
|
|
7
|
+
[AUTHORING.md](AUTHORING.md).
|
|
8
|
+
|
|
9
|
+
**Sources.** S1 is [`spine-core/src/SkeletonJson.ts`](https://github.com/EsotericSoftware/spine-runtimes/blob/4.3/spine-ts/spine-core/src/SkeletonJson.ts),
|
|
10
|
+
S2 [`SkeletonBinary.ts`](https://github.com/EsotericSoftware/spine-runtimes/blob/4.3/spine-ts/spine-core/src/SkeletonBinary.ts)
|
|
11
|
+
and S3 [`TextureAtlas.ts`](https://github.com/EsotericSoftware/spine-runtimes/blob/4.3/spine-ts/spine-core/src/TextureAtlas.ts),
|
|
12
|
+
all on branch `4.3`; S6 is `SkeletonJson.ts` on branches `4.0`, `4.1` and `4.2`, for the
|
|
13
|
+
timeline below. Bare `:NNN` line numbers are into those TypeScript sources — ⚠️ the published npm
|
|
14
|
+
package ships **`dist/` only**, so the local copy to read is
|
|
15
|
+
`node_modules/@esotericsoftware/spine-core/dist/<File>.js`, whose line numbers differ; the
|
|
16
|
+
parser's full `case` label set is identical in both. 📘 = the field is described on an official
|
|
17
|
+
Esoteric docs page. 🔍 = source-only; the parser reads it but no public doc page describes it.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Format-change timeline, verified from the four parsers (S1, S6)
|
|
22
|
+
|
|
23
|
+
| Change | 4.0 | 4.1 | 4.2 | 4.3 |
|
|
24
|
+
| --- | --- | --- | --- | --- |
|
|
25
|
+
| Constraints location | `root.ik` / `root.transform` / `root.path` arrays | same | same **+ `root.physics`** | **single `root.constraints[]`, `type` discriminator** |
|
|
26
|
+
| Bone inheritance field | `"transform"` | `"transform"` | **`"inherit"`** | `"inherit"` |
|
|
27
|
+
| `sequence` on region/mesh | ✗ | **✓** | ✓ | ✓ |
|
|
28
|
+
| `skeleton.referenceScale` | ✗ | ✗ | **✓** | ✓ |
|
|
29
|
+
| `physics` constraint + timelines | ✗ | ✗ | **✓** | ✓ |
|
|
30
|
+
| `slider` constraint + timelines | ✗ | ✗ | ✗ | **✓** |
|
|
31
|
+
| `drawOrderFolder` timeline | ✗ | ✗ | ✗ | **✓** |
|
|
32
|
+
| Bone `icon`/`iconSize`/`iconRotation` (JSON) | ✗ | ✗ | ✗ | **✓** |
|
|
33
|
+
| Transform constraint model | `target` + `local`/`relative` + `offsetRotation` + mix\* | same | same | **`source` + `properties{from→to}` + `localSource`/`localTarget`/`additive`/`clamp`** |
|
|
34
|
+
| IK `uniform` bool | ✓ | ✓ | ✓ | **replaced by `scaleY` (`ScaleYMode`)** |
|
|
35
|
+
|
|
36
|
+
Evidence: `root.ik` present in 4.0/4.1/4.2 and absent in 4.3; `root.constraints` absent in
|
|
37
|
+
4.0/4.1/4.2 and present in 4.3; `uniform` at `4.2 SkeletonJson.ts:161`, gone in 4.3; `target` at
|
|
38
|
+
`4.2:182,222` vs `source` at `4.3:188`.
|
|
39
|
+
|
|
40
|
+
⇒ **A file exported by a pre-4.3 editor carries the *legacy* shape, and the 4.3 parser drops
|
|
41
|
+
those constraints on the floor with no error.**
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Part 1 — the full 4.3 JSON surface
|
|
46
|
+
|
|
47
|
+
All line numbers refer to **S1**, `SkeletonJson.ts` (branch `4.3`, 1406 lines), unless prefixed.
|
|
48
|
+
Everything reaches the parser through `getValue(map, property, defaultValue)` at `:1404-1406`, so
|
|
49
|
+
**"required" below means "the parser dereferences it unconditionally and produces `NaN`/`undefined`
|
|
50
|
+
if absent"** — never "the parser complains".
|
|
51
|
+
|
|
52
|
+
### 1.1 Skeleton header — `root.skeleton` (`:75-87`)
|
|
53
|
+
|
|
54
|
+
| Field | Default | Notes | Doc |
|
|
55
|
+
| --- | --- | --- | --- |
|
|
56
|
+
| `hash` | `undefined` | opaque; tools use it for change detection | 📘 |
|
|
57
|
+
| `spine` | `undefined` | stored as `skeletonData.version`, **never compared** in spine-ts JSON | 📘 |
|
|
58
|
+
| `x`, `y`, `width`, `height` | `undefined` | the setup-pose bounding box | 📘 |
|
|
59
|
+
| `referenceScale` | `100` (× `scale`) | **4.2+**; drives runtime physics/scale reference | 🔍 |
|
|
60
|
+
| `fps` | `undefined` → `SkeletonData.fps` stays `30` | nonessential | 📘 |
|
|
61
|
+
| `images` | `null` | nonessential | 📘 |
|
|
62
|
+
| `audio` | `null` | nonessential | 📘 |
|
|
63
|
+
|
|
64
|
+
The whole `skeleton` block is optional (`if (skeletonMap)` at `:76`).
|
|
65
|
+
|
|
66
|
+
### 1.2 Bones — `root.bones[]` (`:90-118`)
|
|
67
|
+
|
|
68
|
+
| Field | Default | Notes | Doc |
|
|
69
|
+
| --- | --- | --- | --- |
|
|
70
|
+
| `name` | — | required in practice (`new BoneData(…, boneMap.name, …)`, `:97`) | 📘 |
|
|
71
|
+
| `parent` | `null` | resolved by name; **must be declared earlier in the array** | 📘 |
|
|
72
|
+
| `length` | `0` (× scale) | | 📘 |
|
|
73
|
+
| `x`, `y` | `0` (× scale) | local to parent | 📘 |
|
|
74
|
+
| `rotation` | `0` | degrees, CCW, y-up | 📘 |
|
|
75
|
+
| `scaleX`, `scaleY` | `1` | | 📘 |
|
|
76
|
+
| `shearX`, `shearY` | `0` | | 📘 |
|
|
77
|
+
| `inherit` | `"Normal"` | enum `Normal \| OnlyTranslation \| NoRotationOrReflection \| NoScale \| NoScaleOrReflection` (`BoneData.ts:80`). Resolved by `Utils.enumValue` which upper-cases the first letter (`Utils.ts:392-394`), so `"noScale"` and `"NoScale"` both work. **4.2+ name; 4.0/4.1 called it `transform`** | 🔍 |
|
|
78
|
+
| `skin` | `false` | → `data.skinRequired` | 📘 (as "skin") |
|
|
79
|
+
| `color` | none | hex string `rrggbbaa`, nonessential | 📘 |
|
|
80
|
+
| `icon` | `undefined` | **4.3, editor-only affordance** | 🔍 |
|
|
81
|
+
| `iconSize` | `1` | 4.3 | 🔍 |
|
|
82
|
+
| `iconRotation` | `0` | 4.3 | 🔍 |
|
|
83
|
+
| ~~`visible`~~ | — | **NOT read from JSON.** `SkeletonBinary.ts:126` reads it (nonessential); the JSON parser has no equivalent. See §1.11. | 🔍 |
|
|
84
|
+
|
|
85
|
+
### 1.3 Slots — `root.slots[]` (`:121-141`)
|
|
86
|
+
|
|
87
|
+
| Field | Default | Notes | Doc |
|
|
88
|
+
| --- | --- | --- | --- |
|
|
89
|
+
| `name` | — | required | 📘 |
|
|
90
|
+
| `bone` | — | **required**; a miss throws `Couldn't find bone … for slot …` (`:127`) | 📘 |
|
|
91
|
+
| `color` | white | `rrggbbaa` | 📘 |
|
|
92
|
+
| `dark` | none | two-colour tint; only set when present, and `darkColor` stays `null` otherwise (`:133-134`) | 📘 |
|
|
93
|
+
| `attachment` | `null` | setup-pose attachment name | 📘 |
|
|
94
|
+
| `blend` | `"normal"` | enum `Normal \| Additive \| Multiply \| Screen` (`SlotData.ts:64`) | 📘 |
|
|
95
|
+
| `visible` | `true` | 4.3, nonessential-ish; **JSON reads this one** (`:138`) | 🔍 |
|
|
96
|
+
|
|
97
|
+
**Draw order is the array order of `slots`.** There is no separate setup draw-order field.
|
|
98
|
+
|
|
99
|
+
### 1.4 Constraints — `root.constraints[]` (`:144-369`) — **4.3 shape**
|
|
100
|
+
|
|
101
|
+
Common to every entry: `name`, `type`, `skin` (default `false` → `skinRequired`, `:147`).
|
|
102
|
+
`type` is read with `getValue(constraintMap, "type", false)`, so **an entry with no `type` matches
|
|
103
|
+
no case and is silently dropped** (`:148-367`, no `default:` branch).
|
|
104
|
+
|
|
105
|
+
**`type: "ik"`** (`:149-176`) 📘 (3.8 shape only)
|
|
106
|
+
|
|
107
|
+
| Field | Default |
|
|
108
|
+
| --- | --- |
|
|
109
|
+
| `bones[]` | required, ≥1, resolved by name (throws on miss) |
|
|
110
|
+
| `target` | required (throws on miss) |
|
|
111
|
+
| `scaleY` | absent → `ScaleYMode.None`; enum `None \| Uniform \| Volume` (`ConstraintData.ts:50`). **4.3 replacement for 4.2's `uniform: bool`** |
|
|
112
|
+
| `mix` | `1` |
|
|
113
|
+
| `softness` | `0` (× scale) |
|
|
114
|
+
| `bendPositive` | `true` → `bendDirection = ±1` |
|
|
115
|
+
| `compress` | `false` |
|
|
116
|
+
| `stretch` | `false` |
|
|
117
|
+
|
|
118
|
+
**`type: "transform"`** (`:177-268`) 🔍 — completely rebuilt in 4.3
|
|
119
|
+
|
|
120
|
+
| Field | Default |
|
|
121
|
+
| --- | --- |
|
|
122
|
+
| `bones[]` | required |
|
|
123
|
+
| `source` | required (4.2 called this `target`) |
|
|
124
|
+
| `localSource`, `localTarget`, `additive`, `clamp` | `false` |
|
|
125
|
+
| `properties` | `{}` — a map `fromName → { offset, to: { toName → { offset, max, scale } } }`. `fromName`/`toName` ∈ `rotate \| x \| y \| scaleX \| scaleY \| shearY`; anything else **throws** (`:241`, `:521`). `x`/`y` offsets are scaled (`propertyScale`, `:526-532`) |
|
|
126
|
+
| `rotation`, `x`, `y`, `scaleX`, `scaleY`, `shearY` | `0` — the constraint's offsets array |
|
|
127
|
+
| `mixRotate`, `mixX`, `mixScaleX`, `mixShearY` | `1`; `mixY` defaults to `mixX`, `mixScaleY` to `mixScaleX`. **Each mix is only read if the matching `to` property was declared** (`:259-264`) |
|
|
128
|
+
|
|
129
|
+
**`type: "path"`** (`:269-300`) 📘 (3.8 shape)
|
|
130
|
+
|
|
131
|
+
| Field | Default |
|
|
132
|
+
| --- | --- |
|
|
133
|
+
| `bones[]`, `slot` | required |
|
|
134
|
+
| `positionMode` | `"Percent"` — `Fixed \| Percent` (`PathConstraintData.ts:77`) |
|
|
135
|
+
| `spacingMode` | `"Length"` — `Length \| Fixed \| Percent \| Proportional` (`:82`) |
|
|
136
|
+
| `rotateMode` | `"Tangent"` — `Tangent \| Chain \| ChainScale` (`:87`) |
|
|
137
|
+
| `rotation` | `0` → `offsetRotation` |
|
|
138
|
+
| `position` | `0`; × scale iff `positionMode == Fixed` |
|
|
139
|
+
| `spacing` | `0`; × scale iff `spacingMode ∈ {Length, Fixed}` |
|
|
140
|
+
| `mixRotate`, `mixX` | `1`; `mixY` defaults to `mixX` |
|
|
141
|
+
|
|
142
|
+
**`type: "physics"`** (`:301-339`) 🔍 — 4.2+
|
|
143
|
+
|
|
144
|
+
| Field | Default |
|
|
145
|
+
| --- | --- |
|
|
146
|
+
| `bone` | required (throws) |
|
|
147
|
+
| `x`, `y`, `rotate`, `scaleX`, `shearX` | `0` — **the components. All zero = a constraint that parses and does nothing** |
|
|
148
|
+
| `scaleY` | absent → `ScaleYMode.None` (4.3) |
|
|
149
|
+
| `limit` | `5000` (× scale) |
|
|
150
|
+
| `fps` | `60` → `step = 1/fps` |
|
|
151
|
+
| `inertia` | `0.5` |
|
|
152
|
+
| `strength` | `100` |
|
|
153
|
+
| `damping` | `0.85` |
|
|
154
|
+
| `mass` | `1` → stored as `massInverse = 1/mass` |
|
|
155
|
+
| `wind`, `gravity` | `0` |
|
|
156
|
+
| `mix` | `1` |
|
|
157
|
+
| `inertiaGlobal`, `strengthGlobal`, `dampingGlobal`, `massGlobal`, `windGlobal`, `gravityGlobal`, `mixGlobal` | `false` |
|
|
158
|
+
|
|
159
|
+
**`type: "slider"`** (`:340-366`) 🔍 — **new in 4.3, undocumented anywhere public**
|
|
160
|
+
|
|
161
|
+
| Field | Default |
|
|
162
|
+
| --- | --- |
|
|
163
|
+
| `additive`, `loop` | `false` |
|
|
164
|
+
| `mix` | `1` |
|
|
165
|
+
| `bone` | optional. **Presence switches the whole model**: with a bone it is a property-driven slider, without one it is a time slider (`time`, default `0`, `:361`) |
|
|
166
|
+
| `property` | required when `bone` is set; same six `from` names as the transform constraint |
|
|
167
|
+
| `from` | `0` (× propertyScale) → `data.property.offset` |
|
|
168
|
+
| `to` | `0` → `data.offset` |
|
|
169
|
+
| `scale` | `1` ÷ propertyScale |
|
|
170
|
+
| `max` | `0` |
|
|
171
|
+
| `local` | `false` |
|
|
172
|
+
| `animation` | resolved in a **second pass over `root.constraints`** after animations are read (`:495-507`); a miss throws `Slider animation not found` |
|
|
173
|
+
|
|
174
|
+
### 1.5 Skins — `root.skins[]` (`:372-443`)
|
|
175
|
+
|
|
176
|
+
| Field | Default | Notes |
|
|
177
|
+
| --- | --- | --- |
|
|
178
|
+
| `name` | — | the skin named `"default"` becomes `skeletonData.defaultSkin` (`:441`) |
|
|
179
|
+
| `bones[]` | none | bone names this skin activates (`:377-384`) |
|
|
180
|
+
| `ik[]`, `transform[]`, `path[]`, `physics[]`, `slider[]` | none | **constraint names, still split per type inside a skin** even though the top-level array was unified (`:386-429`) |
|
|
181
|
+
| `attachments` | `{}` | `slotName → { placeholderName → attachmentMap }` (`:431-439`) |
|
|
182
|
+
| ~~`color`~~ | — | **not read from JSON.** `Skin.color` exists with a default of `fe9e4fff` (`Skin.ts:71-72`) and only `SkeletonBinary.ts:448` sets it. |
|
|
183
|
+
|
|
184
|
+
The **placeholder** (the key) and the attachment's own `name` are different things: `name` defaults
|
|
185
|
+
to the placeholder (`:537`), and `path` defaults to `name` (`:541`, `:570`). Three-level indirection:
|
|
186
|
+
placeholder → name → path → atlas region.
|
|
187
|
+
|
|
188
|
+
### 1.6 Attachments (`readAttachment`, `:535-654`) — `type` defaults to `"region"` (`:539`)
|
|
189
|
+
|
|
190
|
+
| Type | Fields (default) | Line | Doc |
|
|
191
|
+
| --- | --- | --- | --- |
|
|
192
|
+
| `region` | `path`(=name), `sequence`(null), `x`(0×s), `y`(0×s), `scaleX`(1), `scaleY`(1), `rotation`(0), **`width`/`height` (no default — `map.width * scale`, `undefined` → `NaN`)**, `color` | `:540-559` | 📘 |
|
|
193
|
+
| `boundingbox` | `vertexCount` (no default), `vertices`, `color` | `:560-567` | 📘 |
|
|
194
|
+
| `mesh` | `path`(=name), `sequence`, `color`, `width`(0), `height`(0), `uvs` (no default — **its length defines `worldVerticesLength`**), `triangles` (no default — `undefined` if missing), `vertices`, `edges`(null), `hull`(0, **stored ×2** as `hullLength`) | `:568-605` | 📘 |
|
|
195
|
+
| `linkedmesh` | same head, then `source` (required to make it linked), `slot`(null), `skin`(null), `timelines`(true). **A map with `type:"mesh"` and a `source` key is also a linked mesh** — the two cases share one branch (`:568-569`) and the `source` check at `:582` is what decides | `:568-605` | 📘 |
|
|
196
|
+
| `path` | `closed`(false), `constantSpeed`(true), `vertexCount` (no default), `vertices`, `lengths` (no default — `map.lengths.length` is dereferenced), `color` | `:606-623` | 📘 |
|
|
197
|
+
| `point` | `x`(0×s), `y`(0×s), `rotation`(0), `color` | `:624-634` | 📘 |
|
|
198
|
+
| `clipping` | `end`(null → slot name), `convex`(false, **4.3**), `inverse`(false, **4.3**), `vertexCount`, `vertices`, `color` | `:635-651` | 📘 (convex/inverse 🔍) |
|
|
199
|
+
|
|
200
|
+
**Any other `type` string returns `null`** (`:653`) — the attachment vanishes with no error.
|
|
201
|
+
|
|
202
|
+
**`sequence`** (`readSequence`, `:656-663`) 🔍 — region and mesh only:
|
|
203
|
+
`count` (0), `start` (1), `digits` (0), `setup` (0). Absent → `new Sequence(1, false)`.
|
|
204
|
+
|
|
205
|
+
**Vertex encoding** (`readVertices`, `:666-693`) — **the highest-risk field in the format.**
|
|
206
|
+
There is no flag. If `vertices.length === verticesLength` (i.e. `uvs.length`, or `vertexCount<<1`)
|
|
207
|
+
it is read as **unweighted** x/y pairs; otherwise as the **weighted** run-length encoding
|
|
208
|
+
`boneCount, (boneIndex, bindX, bindY, weight) × boneCount, …`. A coincidental length match reads
|
|
209
|
+
weight data as coordinates. Editor exports carry both encodings.
|
|
210
|
+
|
|
211
|
+
🚨 The second risk in the same field is `boneIndex`: it is a position in the emitted bone array,
|
|
212
|
+
so the run means something different the moment the bone list changes, and nothing in the file
|
|
213
|
+
records what it used to mean. A rig spec therefore writes `weights` — the same data with the bones
|
|
214
|
+
**named** — and rigc encodes this run on emit. The raw form stays reachable behind
|
|
215
|
+
`"boneIndexing": "raw"` for transcribing an export verbatim.
|
|
216
|
+
|
|
217
|
+
### 1.7 Events — `root.events` (object, not array) (`:469-484`)
|
|
218
|
+
|
|
219
|
+
`eventName → { int (0), float (0), string (""), audio (null), volume, balance }`.
|
|
220
|
+
**`volume` and `balance` are only read when `audio` is set** (`:478-481`) — otherwise the setup values
|
|
221
|
+
stand. 📘
|
|
222
|
+
|
|
223
|
+
### 1.8 Animation timelines — `root.animations[animName]` (`readAnimation`, `:696-1272`)
|
|
224
|
+
|
|
225
|
+
Top-level groups inside one animation: `slots`, `bones`, `ik`, `transform`, `path`, `physics`,
|
|
226
|
+
`slider`, `attachments`, `drawOrder`, `drawOrderFolder`, `events`, plus a nonessential `color`
|
|
227
|
+
(`:1268-1269`). Anything else is ignored.
|
|
228
|
+
|
|
229
|
+
| Group | Timeline | Value fields per key | Curve channels | Line | Doc |
|
|
230
|
+
| --- | --- | --- | --- | --- | --- |
|
|
231
|
+
| `slots.<slot>` | `attachment` | `name` (nullable) | **none** | `:713-721` | 📘 |
|
|
232
|
+
| | `rgba` | `color` (`rrggbbaa`) | 4 | `:722-751` | 📘 |
|
|
233
|
+
| | `rgb` | `color` (`rrggbb`) | 3 | `:752-780` | 🔍 |
|
|
234
|
+
| | `alpha` | `value` | 1 | `:781-784` | 🔍 |
|
|
235
|
+
| | `rgba2` | `light`, `dark` | 7 | `:785-821` | 🔍 |
|
|
236
|
+
| | `rgb2` | `light`, `dark` | 6 | `:822-857` | 🔍 |
|
|
237
|
+
| | *anything else* | — | — | **throws** `Invalid timeline type for a slot` (`:858-859`) | |
|
|
238
|
+
| `bones.<bone>` | `rotate` | `value` | 1 | `:878` | 📘 |
|
|
239
|
+
| | `translate` | `x`, `y` | 2 | `:879` | 📘 |
|
|
240
|
+
| | `translatex` / `translatey` | `value` | 1 | `:880-881` | 🔍 |
|
|
241
|
+
| | `scale` | `x`, `y` | 2 | `:882` | 📘 |
|
|
242
|
+
| | `scalex` / `scaley` | `value` | 1 | `:883-884` | 🔍 |
|
|
243
|
+
| | `shear` | `x`, `y` | 2 | `:885` | 📘 |
|
|
244
|
+
| | `shearx` / `sheary` | `value` | 1 | `:886-887` | 🔍 |
|
|
245
|
+
| | `inherit` | `inherit` (enum string) | **none** | `:888-896` | 🔍 |
|
|
246
|
+
| | *anything else* | — | — | **throws** `Invalid timeline type for a bone` (`:897-898`) | |
|
|
247
|
+
| `ik.<constraint>` | (one array, no sub-name) | `mix`(1), `softness`(0×s), `bendPositive`(true), `compress`(false), `stretch`(false) | 2 (mix, softness) | `:906-945` | 📘 |
|
|
248
|
+
| `transform.<constraint>` | (one array) | `mixRotate`(1), `mixX`(1), `mixY`(=mixX), `mixScaleX`(1), `mixScaleY`(1), `mixShearY`(1) | 6 | `:948-999` | 📘 |
|
|
249
|
+
| `path.<constraint>` | `position` | `value` | 1 | `:1015-1019` | 📘 |
|
|
250
|
+
| | `spacing` | `value` | 1 | `:1020-1024` | 📘 |
|
|
251
|
+
| | `mix` | `mixRotate`, `mixX`, `mixY` | 3 | `:1025-1056` | 📘 |
|
|
252
|
+
| `physics.<constraint>` | `inertia`/`strength`/`damping`/`mass`/`wind`/`gravity` | `value` (default 0) | 1 each | `:1088-1093` | 🔍 |
|
|
253
|
+
| | `mix` | `value` (default **1**) | 1 | `:1094-1098` | 🔍 |
|
|
254
|
+
| | `reset` | *no value* — time only | **none** | `:1080-1086` | 🔍 |
|
|
255
|
+
| | *anything else* | — | — | silently `continue`d (`:1099`) — **no throw** | |
|
|
256
|
+
| `slider.<constraint>` | `time` | `value` (default 1) | 1 | `:1121` | 🔍 |
|
|
257
|
+
| | `mix` | `value` (default 1) | 1 | `:1122` | 🔍 |
|
|
258
|
+
| `attachments.<skin>.<slot>.<attachment>` | `deform` | `offset`(0), `vertices[]` | 1 | `:1149-1187` | 📘 |
|
|
259
|
+
| | `sequence` | `time`, `mode`(`"hold"`), `index`(0), `delay`(inherits previous) | **none** | `:1188-1201` | 🔍 |
|
|
260
|
+
| | *anything else* | — | — | silently ignored | |
|
|
261
|
+
| `drawOrder` | (array of keys) | `time`, `offsets[]` of `{slot, offset}`. **No `offsets` = reset to setup order** (`:1352-1353`) | **none** | `:1209-1217` | 📘 (spelled `draworder`) |
|
|
262
|
+
| `drawOrderFolder` | (array of folders) | `slots[]` (slot names in the folder), `keys[]` of the same `{time, offsets}` shape, resolved *within the folder* | **none** | `:1220-1239` | 🔍 **4.3 only** |
|
|
263
|
+
| `events` | (array) | `name` (required, throws on miss), `time`(0), `int`/`float`/`string` (default = event's setup), `volume`/`balance` **only when the event has an audio path** | **none** | `:1242-1261` | 📘 |
|
|
264
|
+
|
|
265
|
+
Important asymmetries worth writing down:
|
|
266
|
+
|
|
267
|
+
- **The physics group's constraint name may be the empty string** (`:1067`), which yields `index = -1`
|
|
268
|
+
and applies to *all* physics constraints. There is no documentation of this.
|
|
269
|
+
- **A `deform` key with no `vertices`** resets to the setup mesh (weighted → zeros, unweighted → the
|
|
270
|
+
base vertices) — `:1158-1160`.
|
|
271
|
+
- **`drawOrder` produces exactly one timeline for the whole animation**; `drawOrderFolder` produces
|
|
272
|
+
one per folder entry.
|
|
273
|
+
- **Empty timeline arrays**: bones `continue` on `frames === 0` (`:875`); ik/transform/path/physics/
|
|
274
|
+
slider `continue` when `[0]` is missing; **slots do not** — an empty `rgba` array reaches
|
|
275
|
+
`timelineMap[0]` and dereferences `keyMap.color` → `TypeError`.
|
|
276
|
+
|
|
277
|
+
### 1.9 Curve encoding (`readCurve`, `:1388-1401`; `readTimeline1/2`, `:1296-1346`)
|
|
278
|
+
|
|
279
|
+
Three encodings, on the **key that starts the interval** (`keyMap.curve`, never the destination key):
|
|
280
|
+
|
|
281
|
+
| Encoding | JSON | Meaning |
|
|
282
|
+
| --- | --- | --- |
|
|
283
|
+
| linear | `curve` absent | straight interpolation |
|
|
284
|
+
| stepped | `"curve": "stepped"` | `timeline.setStepped(frame)` (`:1391`) |
|
|
285
|
+
| bezier | `"curve": [ … ]` | array of **exactly 4 numbers per value channel**, concatenated in channel order |
|
|
286
|
+
|
|
287
|
+
Layout, exactly: for channel index `value`, `i = value << 2` and the four numbers are
|
|
288
|
+
`[cx1, cy1, cx2, cy2]` (`:1394-1398`). These are **absolute (time, value) control points**, not
|
|
289
|
+
normalised graph-view handles — `cy1`/`cy2` are multiplied by the timeline's `scale` factor, `cx1`/`cx2`
|
|
290
|
+
are not. So the array length per timeline is `4 × channels` from the table in §1.8:
|
|
291
|
+
rotate 4, translate 8, scale 8, shear 8, rgb 12, rgba 16, rgb2 24, rgba2 28, ik 8, transform 24,
|
|
292
|
+
path mix 12, alpha/scalex/deform/physics/slider 4.
|
|
293
|
+
|
|
294
|
+
**A short array is the format's nastiest silent failure**: `curve[i+3]` is `undefined`, the product
|
|
295
|
+
is `NaN`, and nothing throws. Timelines with no curve at all (`attachment`,
|
|
296
|
+
`inherit`, `sequence`, `drawOrder`, `events`, `physics reset`) ignore a `curve` key entirely.
|
|
297
|
+
|
|
298
|
+
The X axis is **seconds** in 4.x. It was frames in 3.8 — one more reason the 3.8 doc is actively
|
|
299
|
+
dangerous as a spec.
|
|
300
|
+
|
|
301
|
+
### 1.10 Atlas text format (`TextureAtlas.ts`, S3)
|
|
302
|
+
|
|
303
|
+
Reader rules (`:194-227`): entries are `key: v1, v2, v3, v4` with **at most four values**
|
|
304
|
+
(`:224`); a line with no colon terminates the current block (`:214`). A **blank line closes a page
|
|
305
|
+
block** (`:119-121`). Page names are `line.trim()` (`:123`) but **region names are the raw line**
|
|
306
|
+
(`:131`) — leading whitespace becomes part of the name.
|
|
307
|
+
|
|
308
|
+
**Header entries before the first page are read and silently discarded** (`:106-111` — the comment
|
|
309
|
+
says so literally).
|
|
310
|
+
|
|
311
|
+
| Page field | Parsed into | Doc |
|
|
312
|
+
| --- | --- | --- |
|
|
313
|
+
| `size: w, h` | `page.width/height` (`:43-46`) — **used to compute every UV**, so a wrong value collapses all of them | 📘 |
|
|
314
|
+
| `format: …` | **parsed and thrown away** (`:47-49`, "we don't need format in WebGL") | 📘 |
|
|
315
|
+
| `filter: min, mag` | `minFilter`/`magFilter` (`:50-53`) | 📘 |
|
|
316
|
+
| `repeat: x\|y\|xy\|none` | `uWrap`/`vWrap` (`:54-57`) | 📘 |
|
|
317
|
+
| `pma: true\|false` | `page.pma` (`:58-60`) | 📘 |
|
|
318
|
+
| `scale: n` | 🚨 **emitted by the Spine texture packer, documented nowhere, and silently discarded by the reader.** There is no `pageFields.scale`, so `if (field) field(page)` at `:126-127` is a no-op. It records the export-time downscale factor; the runtime is expected to compensate via `SkeletonJson.scale` or `referenceScale`, not to read this line. | ❌ undocumented |
|
|
319
|
+
|
|
320
|
+
| Region field | Parsed into | Doc |
|
|
321
|
+
| --- | --- | --- |
|
|
322
|
+
| `bounds: x, y, w, h` | 4.x compact form (`:71-76`) | 📘 |
|
|
323
|
+
| `offsets: ox, oy, ow, oh` | 4.x compact form (`:85-90`) | 📘 |
|
|
324
|
+
| `xy: x, y` | **deprecated** alias (`:63-66`) | 📘 |
|
|
325
|
+
| `size: w, h` | **deprecated** alias (`:67-70`) | 📘 |
|
|
326
|
+
| `offset: ox, oy` | **deprecated** (`:77-80`) | 📘 |
|
|
327
|
+
| `orig: w, h` | **deprecated** (`:81-84`) | 📘 |
|
|
328
|
+
| `rotate: true \| false \| <degrees>` | `true` → 90; `false` → 0; anything else `parseInt` (`:91-97`). **90 swaps width/height in the UV computation** (`:161-167`) | 📘 |
|
|
329
|
+
| `index: n` | frame index for sequential regions (`:98-100`) | 📘 |
|
|
330
|
+
| `split: l, r, t, b` | **no dedicated field.** Falls through to the generic bucket → `region.names`/`region.values` (`:139-147`) | 📘 |
|
|
331
|
+
| `pad: l, r, t, b` | same generic bucket | 📘 |
|
|
332
|
+
| *any other key* | same generic bucket, `parseInt`-ed | 🔍 |
|
|
333
|
+
|
|
334
|
+
If `orig`/`offsets` never set an original size, it falls back to the packed size (`:149-152`).
|
|
335
|
+
|
|
336
|
+
### 1.11 What the binary `.skel` adds or drops relative to JSON (S2)
|
|
337
|
+
|
|
338
|
+
Same feature set — same five constraint types (`SkeletonBinary.ts:1427-1431`), same attachment types,
|
|
339
|
+
same timeline catalogue (it imports the identical list, `:30`). Differences that matter to a JSON-only
|
|
340
|
+
emitter:
|
|
341
|
+
|
|
342
|
+
| Aspect | JSON | Binary |
|
|
343
|
+
| --- | --- | --- |
|
|
344
|
+
| Strings | inline | string table, index-referenced (`:93-100`) |
|
|
345
|
+
| Hash | string field | two int32s, joined as hex (`:76-78`) |
|
|
346
|
+
| Nonessential gate | per-field presence | **one boolean** (`:86`), then `fps`/`images`/`audio` (`:87-90`) |
|
|
347
|
+
| Bone `color`/`icon`/`iconSize`/`iconRotation` | JSON reads all four | nonessential block (`:121-126`) |
|
|
348
|
+
| **Bone `visible`** | ❌ **not readable from JSON** | `:126` |
|
|
349
|
+
| **Skin `color`** | ❌ **not readable from JSON** | `:448` |
|
|
350
|
+
| Slot `visible` | `:138` ✓ | `:145` |
|
|
351
|
+
| Animation `color` | `:1268` ✓ | `:1211` |
|
|
352
|
+
| Attachment colours | always read when present | only when nonessential (`:513`, `:600`, `:617`, `:630`) |
|
|
353
|
+
|
|
354
|
+
⇒ **Two things are JSON-inexpressible in 4.3 spine-ts: `bone.visible` and `skin.color`.** Both are
|
|
355
|
+
editor-affordance data with zero rendering effect: there is no *rendering-relevant* feature that
|
|
356
|
+
binary can express and JSON cannot.
|
|
357
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,110 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rig-c",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "2.20.4",
|
|
4
|
+
"description": "AI-authored Spine 2D rigging and animation, verified before it is written. rigc compiles a rig spec into Spine 4.3 skeleton data, gates it with named assertions held to a spine-core round trip's verdicts, and emits nothing that fails. Output imports into the Spine editor. Ships as an agent skill.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"rigc": "./bin/rigc.cjs"
|
|
8
|
+
},
|
|
9
|
+
"exports": {
|
|
10
|
+
"./plate": "./tools/plate.ts",
|
|
11
|
+
"./font5x7": "./tools/font5x7.ts",
|
|
12
|
+
"./transform": "./src/transform.ts",
|
|
13
|
+
"./render": "./src/render.ts",
|
|
14
|
+
"./png": "./src/png.ts",
|
|
15
|
+
"./compile": "./src/compile.ts",
|
|
16
|
+
"./rig": "./src/rig.ts",
|
|
17
|
+
"./mesh": "./src/mesh.ts",
|
|
18
|
+
"./errors": "./src/errors.ts",
|
|
19
|
+
"./meshcompare": "./src/meshcompare.ts",
|
|
20
|
+
"./cli": "./cli.ts",
|
|
21
|
+
"./package.json": "./package.json",
|
|
22
|
+
"./*.ts": "./*.ts",
|
|
23
|
+
"./*.mjs": "./*.mjs",
|
|
24
|
+
"./*.cjs": "./*.cjs",
|
|
25
|
+
"./*.json": "./*.json",
|
|
26
|
+
"./*.md": "./*.md",
|
|
27
|
+
"./LICENSE": "./LICENSE",
|
|
28
|
+
"./bin/rigc": "./bin/rigc.cjs",
|
|
29
|
+
"./tools/png_probe": "./tools/png_probe.mjs",
|
|
30
|
+
"./*": "./*.ts"
|
|
31
|
+
},
|
|
32
|
+
"engines": {
|
|
33
|
+
"bun": ">=1.2.0"
|
|
34
|
+
},
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"author": "firejune",
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "https://github.com/firejune/rigc.git"
|
|
40
|
+
},
|
|
41
|
+
"homepage": "https://github.com/firejune/rigc#readme",
|
|
42
|
+
"bugs": "https://github.com/firejune/rigc/issues",
|
|
43
|
+
"keywords": [
|
|
44
|
+
"spine",
|
|
45
|
+
"spine2d",
|
|
46
|
+
"skeletal-animation",
|
|
47
|
+
"rig",
|
|
48
|
+
"compiler",
|
|
49
|
+
"skeleton-json",
|
|
50
|
+
"atlas",
|
|
51
|
+
"validator",
|
|
52
|
+
"codegen",
|
|
53
|
+
"ai-agents",
|
|
54
|
+
"cli",
|
|
55
|
+
"spine-2d",
|
|
56
|
+
"ai",
|
|
57
|
+
"animation",
|
|
58
|
+
"rigging",
|
|
59
|
+
"agent-skill",
|
|
60
|
+
"claude"
|
|
61
|
+
],
|
|
62
|
+
"files": [
|
|
63
|
+
"bin/rigc.cjs",
|
|
64
|
+
"cli.ts",
|
|
65
|
+
"cli_core.ts",
|
|
66
|
+
"src",
|
|
67
|
+
"tools/plate.ts",
|
|
68
|
+
"tools/font5x7.ts",
|
|
69
|
+
"tools/contact.ts",
|
|
70
|
+
"tools/png_probe.mjs",
|
|
71
|
+
"tools/measure_contact_depth.ts",
|
|
72
|
+
"tools/editor_roundtrip.ts",
|
|
73
|
+
"docs/AUTHORING.md",
|
|
74
|
+
"docs/SPEC_COVERAGE.md",
|
|
75
|
+
"docs/PROMPTING.md",
|
|
76
|
+
"docs/RIGGING.md",
|
|
77
|
+
"docs/MOTION.md",
|
|
78
|
+
"docs/INGEST.md",
|
|
79
|
+
"docs/FACE.md",
|
|
80
|
+
"NOTICE.md",
|
|
81
|
+
".claude-plugin",
|
|
82
|
+
"skills"
|
|
83
|
+
],
|
|
84
|
+
"publishConfig": {
|
|
85
|
+
"access": "public"
|
|
86
|
+
},
|
|
87
|
+
"scripts": {
|
|
88
|
+
"build": "bun cli.ts build",
|
|
89
|
+
"validate": "bun cli.ts validate",
|
|
90
|
+
"explain": "bun cli.ts explain",
|
|
91
|
+
"selftest": "bun selftest.ts",
|
|
92
|
+
"smoke": "bun scripts/install_smoke.ts",
|
|
93
|
+
"typecheck": "bunx tsc --noEmit",
|
|
94
|
+
"typecheck:runs": "bunx tsc --noEmit -p tsconfig.runs.json",
|
|
95
|
+
"lint": "bunx eslint .",
|
|
96
|
+
"fetch-examples": "bash scripts/fetch-examples.sh",
|
|
97
|
+
"bench:usage": "bun bench/count_features.ts",
|
|
98
|
+
"viewer": "vite --config viewer/vite.config.ts",
|
|
99
|
+
"prepublishOnly": "bun scripts/prepublish_gate.ts"
|
|
100
|
+
},
|
|
101
|
+
"devDependencies": {
|
|
102
|
+
"@esotericsoftware/spine-core": "4.3.13",
|
|
103
|
+
"@types/bun": "^1.4.0",
|
|
104
|
+
"eslint": "^10.9.0",
|
|
105
|
+
"spine-html": "^0.4.1",
|
|
106
|
+
"typescript": "^5.9.3",
|
|
107
|
+
"typescript-eslint": "^8.67.0",
|
|
108
|
+
"vite": "^8.2.2"
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rigc
|
|
3
|
+
description: Author, build and validate Spine 4.3 skeleton data (skeleton.json plus its .atlas) from loose part PNGs with rigc, the rig compiler that verifies its own output with named assertions before writing it. Use for any request to make a Spine rig or Spine animation from PNG parts, or where the source is a Live2D, Unity or video model whose pictures you can render, to run or read rigc build, validate, render, preview, check or vote, or to write or fix a *.rig.json or *.motion.json spec; it says which shipped guide to open for the need at hand. Not for Live2D conversion, cutting an illustration into parts, or real-time face tracking.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: Requires Bun 1.2 or later. The tool is the npm package spine-rigc (bunx spine-rigc, or bun add -d spine-rigc); the command it installs is rigc.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# rigc — a rig compiler for agents
|
|
9
|
+
|
|
10
|
+
rigc compiles a rig spec and a motion spec into Spine 4.3 skeleton data and an
|
|
11
|
+
atlas, runs its named assertions — rigc's own validator in the published package,
|
|
12
|
+
held to a `@esotericsoftware/spine-core` round trip's verdicts in its CI, all but
|
|
13
|
+
the parse itself — and writes **only if every one is green**. You cannot see the
|
|
14
|
+
rig you are authoring. The validator's messages and the shipped guides are the
|
|
15
|
+
whole interface, and this skill only says which of them to open.
|
|
16
|
+
|
|
17
|
+
## Non-negotiables
|
|
18
|
+
|
|
19
|
+
- **Validation is never bypassed.** `build` writes nothing on a red gate; there is
|
|
20
|
+
no `--no-validate`, and none may be added — AUTHORING §0 says so in as many
|
|
21
|
+
words. Correctness is the whole of the reason: the assertions are what make the
|
|
22
|
+
output trustworthy, and they are held to the official parser's verdicts in the
|
|
23
|
+
repository's CI — all but the parse itself, which runs only where the runtime is
|
|
24
|
+
installed and is reported as a SKIP elsewhere.
|
|
25
|
+
- **The compiler never invents a value.** A field the spec leaves out is a
|
|
26
|
+
`CompileError` naming that field. Fill the spec; do not expect a default read off
|
|
27
|
+
the art — AUTHORING §2 and §5.
|
|
28
|
+
- **The validator's messages are the instructions.** Each names the object, the
|
|
29
|
+
value found and the value required, and AUTHORING §5 maps every named failure to
|
|
30
|
+
the file that has to change.
|
|
31
|
+
- **A green gate cannot see a wrong animation.** If you were given pictures,
|
|
32
|
+
`check` is the half of the loop that can; if not, `render` or `preview` and look —
|
|
33
|
+
AUTHORING §0 and §9.
|
|
34
|
+
|
|
35
|
+
## Install
|
|
36
|
+
|
|
37
|
+
```shell
|
|
38
|
+
bunx spine-rigc --help # run it without installing
|
|
39
|
+
bun add -d spine-rigc # or pin it in the project; the command is `rigc`
|
|
40
|
+
bun rigc skills install # then link these skills into .agents/skills
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Codex, Gemini CLI and Antigravity read skills from `.agents/skills/` in the
|
|
44
|
+
workspace and none of them reads `node_modules`; `rigc skills install` puts every
|
|
45
|
+
skill the package ships there, and `rigc skills --help` says what it refuses.
|
|
46
|
+
|
|
47
|
+
The package links no Spine runtime. `validate`, `preview` and `vote` below run
|
|
48
|
+
through `@esotericsoftware/spine-core`: install it beside the package
|
|
49
|
+
(`bun add -d @esotericsoftware/spine-core`) and the same `rigc` runs them, and
|
|
50
|
+
`build` runs the round trip as well. Without it they are refused by name, and
|
|
51
|
+
`build` writes the same files gated without the parse — `rigc --version` names the
|
|
52
|
+
entry that ran. In the parse's place that `build` runs two rules of its own over
|
|
53
|
+
rigc's model document, `A00_MODEL_READ` and `A00_MODEL_REGIONS_ON_PAGES`; they are
|
|
54
|
+
not registry assertions, so its summary counts two more, and AUTHORING §5.2 has a
|
|
55
|
+
row for each.
|
|
56
|
+
|
|
57
|
+
## The loop
|
|
58
|
+
|
|
59
|
+
1. `rigc build --rig <spec> --motion <spec> --images <dir> --out <dir>` compiles,
|
|
60
|
+
gates, and writes only on green.
|
|
61
|
+
2. Read the report. Every red line names the file to change; fix the spec and
|
|
62
|
+
build again.
|
|
63
|
+
3. `rigc explain --rig <spec> --motion <spec> --out <dir>` when the red line is
|
|
64
|
+
not enough — the compiled rig as a table: every bone with its resolved parent,
|
|
65
|
+
the slots in draw order, every timeline key by key, and a `DEFORM` block per
|
|
66
|
+
deform key. It takes no `--profile`, it never gates, and it writes nothing, so
|
|
67
|
+
the figures are readable on a build the gate is refusing — AUTHORING §0 and
|
|
68
|
+
§4.11.2.
|
|
69
|
+
4. `rigc render --candidate <out>` or `rigc preview --candidate <out>` — look at
|
|
70
|
+
it. A rig with its head off its torso passes the gate; looking is what catches it.
|
|
71
|
+
Open one frame at full size, not only `contact.png`: the sheet is for spacing
|
|
72
|
+
across frames, and a defect is read on a frame. Ask it three things — is any
|
|
73
|
+
picture drawn twice, is there a straight edge where the art has none, does a part
|
|
74
|
+
cover a feature the art shows — and answer each with `render --hide <slot>` (or
|
|
75
|
+
`--slot <slot,…>`), which draws the frame again without that part on the same
|
|
76
|
+
grid, so the two frames say which part a pixel is. AUTHORING §0 holds the three.
|
|
77
|
+
5. `rigc check --candidate <out> --frames <dir>` when you have reference pictures
|
|
78
|
+
(`--out <dir>` writes the picture each of its numbers came from — open the worst).
|
|
79
|
+
Read its output from the top: the framing block says whether the figures under
|
|
80
|
+
it are about placement or motion. A figure is where the loop starts, not where it
|
|
81
|
+
ends — keep the first green build's figure, check every later build against the
|
|
82
|
+
same frames, and stop when the figure stops moving, not when it exists; AUTHORING
|
|
83
|
+
§9.2 says how to read the framing block and the floor to read a figure against.
|
|
84
|
+
`rigc vote --candidate <a> --candidate <b>` when several candidates are green and
|
|
85
|
+
only a person can choose between them.
|
|
86
|
+
6. `rigc validate <out>` re-gates artifacts already on disk, and
|
|
87
|
+
`rigc <command> --help` is each command's own flag table. `rigc repack <out>
|
|
88
|
+
--out <dir>` packs a build again under `build --pack`'s packing flags when its
|
|
89
|
+
parts were not kept, and writes only after every region and the skeleton are
|
|
90
|
+
shown unchanged (`--accept-skeleton-differences` writes a rebuild whose skeleton
|
|
91
|
+
differs, every difference printed; `--stage-box <slot>` reads a build's stage box)
|
|
92
|
+
— AUTHORING §0.4.
|
|
93
|
+
7. Every finished unit ends with `rigc preview --candidate <out>`, and the report
|
|
94
|
+
names the `.html` it wrote. The hand-off to a person is part of the work.
|
|
95
|
+
|
|
96
|
+
The loop in full, with `pose` before it and `chainfit` after the first build:
|
|
97
|
+
AUTHORING §0.
|
|
98
|
+
|
|
99
|
+
## When the source is not loose PNGs
|
|
100
|
+
|
|
101
|
+
A Live2D model, a Unity scene or a video is a player, not a set of parts. Make the
|
|
102
|
+
reference frames first: render the source at the rate you will check at, into one
|
|
103
|
+
directory of `f0000.png`, `f0001.png`…, and `rigc check --frames <that dir> --fps <rate>`
|
|
104
|
+
reads them with no `frames.json`. A port with no reference frames is unmeasured,
|
|
105
|
+
not finished. The parts are the source's own texture cut along its drawables, never
|
|
106
|
+
a screenshot: a screenshot is the composed result, and a part cut from it carries
|
|
107
|
+
every part under it. Where a part goes is its drawable's geometry — its vertices in
|
|
108
|
+
model space, its UVs, its triangles — which is a mesh attachment with those three
|
|
109
|
+
(AUTHORING §3.4, *Mesh attachment*); a region at the drawable's bounding-box centre
|
|
110
|
+
keeps only the box. rigc reads none of those formats — FACE §11, the paragraph
|
|
111
|
+
that opens *No Live2D file is read or written*
|
|
112
|
+
([FACE.md](https://github.com/firejune/rigc/blob/main/docs/FACE.md#11-non-goals--stated-so-nobody-proposes-them-as-gaps)) —
|
|
113
|
+
only the pictures they produce. The rule, the background those frames need and
|
|
114
|
+
what a wrong one costs: AUTHORING §0, *When the source is a foreign player*.
|
|
115
|
+
|
|
116
|
+
## Which guide, for which need
|
|
117
|
+
|
|
118
|
+
Read [AUTHORING.md](https://github.com/firejune/rigc/blob/main/docs/AUTHORING.md) first, whatever the need: the two
|
|
119
|
+
spec files field by field, the emission rules, the loop, and the failure map. Then:
|
|
120
|
+
|
|
121
|
+
| The request is… | Open | Skill |
|
|
122
|
+
| --- | --- | --- |
|
|
123
|
+
| a **skeleton** — how many bones, where each pivot sits, what hangs off what | [RIGGING.md](https://github.com/firejune/rigc/blob/main/docs/RIGGING.md) | `rigc-rigging` |
|
|
124
|
+
| a **movement** — an idle, a loop, from this pose to that one | [MOTION.md](https://github.com/firejune/rigc/blob/main/docs/MOTION.md) | `rigc-motion` |
|
|
125
|
+
| a **face** — a blink, a gaze, a head turn a few degrees off axis | [FACE.md](https://github.com/firejune/rigc/blob/main/docs/FACE.md) | `rigc-face` |
|
|
126
|
+
| a **skeleton.json somebody else authored** — read it, repair it, extend it | [INGEST.md](https://github.com/firejune/rigc/blob/main/docs/INGEST.md) | `rigc-ingest` |
|
|
127
|
+
| you are the **person operating** the agent rather than the agent | [PROMPTING.md](https://github.com/firejune/rigc/blob/main/docs/PROMPTING.md) | — |
|
|
128
|
+
|
|
129
|
+
Every guide linked here is in the installed package at `node_modules/spine-rigc/docs/`,
|
|
130
|
+
which is the copy that matches the rigc you run; the links go to the repository's
|
|
131
|
+
`main`. Inside the Claude Code plugin the same files are at `${CLAUDE_PLUGIN_ROOT}/docs/`.
|
|
132
|
+
Formats, the CLI reference and the licence chain:
|
|
133
|
+
[README.md](https://github.com/firejune/rigc/blob/main/README.md), installed at `node_modules/spine-rigc/README.md`.
|