rig-play 1.10.0 → 1.11.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/NOTICE.md +14 -10
- package/README.md +211 -35
- package/dist/DomRenderer.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +396 -99
- package/dist/index.js.map +1 -1
- package/dist/loadModel.d.ts +25 -0
- package/dist/loadModel.d.ts.map +1 -1
- package/dist/player.d.ts +32 -1
- package/dist/player.d.ts.map +1 -1
- package/package.json +22 -2
package/NOTICE.md
CHANGED
|
@@ -11,20 +11,24 @@ document `rigc build` writes. No part of the
|
|
|
11
11
|
[Spine Runtimes](https://github.com/EsotericSoftware/spine-runtimes) is in it,
|
|
12
12
|
imported by it, or required beside it.
|
|
13
13
|
|
|
14
|
-
**
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
**Development tooling uses the official Spine WebGL runtime.** Issue #39
|
|
15
|
+
reintroduces `@esotericsoftware/spine-webgl` 4.3.13 as a pinned devDependency,
|
|
16
|
+
with its matching `@esotericsoftware/spine-core` dependency, for the independent
|
|
17
|
+
pixel oracle and comparison/benchmark tooling. These are licensed under the
|
|
18
|
+
[Spine Runtimes License Agreement](https://esotericsoftware.com/spine-runtimes-license),
|
|
19
|
+
updated April 5, 2025, copyright 2013–2025 Esoteric Software LLC; they are not
|
|
20
|
+
covered by this project's MIT licence. Their licence and copyright notices
|
|
21
|
+
apply to use and redistribution of that tooling. The earlier removal in #63
|
|
22
|
+
remains the history of the production player: no runtime is added back to its
|
|
23
|
+
public API or published library. `tests/package.spec.ts` checks the exact
|
|
24
|
+
development exception and the runtime-free shipped code and declarations.
|
|
22
25
|
|
|
23
26
|
One thing in this repository is Spine data, and it is worth stating where it
|
|
24
27
|
sits. `rigc build`, which `scripts/fetch-assets.sh` runs to make the fixtures,
|
|
25
28
|
writes **Spine skeleton data** (`skeleton.json` and `skeleton.atlas`) beside
|
|
26
|
-
each model document, into the gitignored `public/rigs/`.
|
|
27
|
-
|
|
29
|
+
each model document, into the gitignored `public/rigs/`. The development oracle
|
|
30
|
+
and comparison tooling read that paired export independently of the model
|
|
31
|
+
reader. These generated files are neither committed nor published in the package.
|
|
28
32
|
|
|
29
33
|
### What that means, as a chain of facts
|
|
30
34
|
|
package/README.md
CHANGED
|
@@ -38,6 +38,81 @@ links to either earlier name keep working.
|
|
|
38
38
|
|
|
39
39
|
### Measured
|
|
40
40
|
|
|
41
|
+
**Standing comparison against `@esotericsoftware/spine-webgl 4.3.13` (`npm run bench`, #39)** —
|
|
42
|
+
measured on 2026-10-17 in **headed** Chromium `151.0.7922.34` on an Apple M4 GPU
|
|
43
|
+
(`ANGLE (Apple, ANGLE Metal Renderer: Apple M4, Unspecified Version)`, macOS `Darwin 25.6.0`,
|
|
44
|
+
10 cores, 32 GiB RAM, `1280×720` viewport, `devicePixelRatio: 1`, 100 warm-up frames +
|
|
45
|
+
600 measured frames per row, `loadPerCoreBefore: 0.33`, `loadPerCoreAfter: 0.33` under the
|
|
46
|
+
`1.0` load guard; each row is its own page load with only that runtime animating, and grid
|
|
47
|
+
scenes batch the official skeletons through one shared WebGL canvas while `small players`
|
|
48
|
+
mounts independent player instances):
|
|
49
|
+
|
|
50
|
+
| scene | runtime | rig | clipped to | fps | pose ms | render ms | drawn players | triangles | draw calls | origin Δpx | live contexts |
|
|
51
|
+
| --- | --- | --- | --- | --: | --: | --: | --: | --: | --: | --: | --: |
|
|
52
|
+
| 1 meshed rig animating | dom | `portrait-packed` `turn` | pane | 59.99 | 0.17 | 0.16 | —/1 | 48 | — | 0.007 | —/1 |
|
|
53
|
+
| 1 meshed rig animating | canvas | `portrait-packed` `turn` | pane | 59.99 | 0.23 | 0.18 | —/1 | 0 | 0 | 0.007 | —/1 |
|
|
54
|
+
| 1 meshed rig animating | official | `portrait-packed` `turn` | pane | 60.00 | 0.05 | 0.13 | 1/1 | 88 | 1 | 0.000 | 1/1 |
|
|
55
|
+
| 4 meshed rigs animating | dom | `portrait-packed` `turn` | pane | 60.01 | 0.46 | 0.48 | —/4 | 96 | — | 0.009 | —/1 |
|
|
56
|
+
| 4 meshed rigs animating | canvas | `portrait-packed` `turn` | pane | 60.00 | 0.60 | 0.49 | —/4 | 176 | 2 | 0.009 | —/1 |
|
|
57
|
+
| 4 meshed rigs animating | official | `portrait-packed` `turn` | pane | 59.99 | 0.13 | 0.17 | 4/4 | 352 | 1 | 0.000 | 1/1 |
|
|
58
|
+
| 16 meshed rigs animating | dom | `portrait-packed` `turn` | pane | 60.00 | 0.66 | 0.74 | —/16 | 384 | — | 0.010 | —/1 |
|
|
59
|
+
| 16 meshed rigs animating | canvas | `portrait-packed` `turn` | pane | 59.87 | 0.84 | 0.50 | —/16 | 704 | 8 | 0.010 | —/1 |
|
|
60
|
+
| 16 meshed rigs animating | official | `portrait-packed` `turn` | pane | 60.00 | 0.21 | 0.30 | 16/16 | 1408 | 1 | 0.000 | 1/1 |
|
|
61
|
+
| 36 meshed rigs animating | dom | `portrait-packed` `turn` | pane | 48.28 | 1.24 | 1.42 | —/36 | 1104 | — | 0.010 | —/1 |
|
|
62
|
+
| 36 meshed rigs animating | canvas | `portrait-packed` `turn` | pane | 60.11 | 1.15 | 0.85 | —/36 | 1760 | 20 | 0.010 | —/1 |
|
|
63
|
+
| 36 meshed rigs animating | official | `portrait-packed` `turn` | pane | 60.00 | 0.43 | 0.49 | 36/36 | 3168 | 1 | 0.000 | 1/1 |
|
|
64
|
+
| 64 meshed rigs animating | dom | `portrait-packed` `turn` | pane | 29.07 | 2.13 | 2.69 | —/64 | 1824 | — | 0.010 | —/1 |
|
|
65
|
+
| 64 meshed rigs animating | canvas | `portrait-packed` `turn` | pane | 53.99 | 1.62 | 1.17 | —/64 | 3168 | 36 | 0.010 | —/1 |
|
|
66
|
+
| 64 meshed rigs animating | official | `portrait-packed` `turn` | pane | 59.99 | 0.42 | 0.54 | 64/64 | 5632 | 1 | 0.000 | 1/1 |
|
|
67
|
+
| 1 meshed rig animating (canvas2d) | dom | `portrait-packed` `turn` | pane | 60.00 | 0.18 | 0.28 | —/1 | 48 | — | 0.007 | —/0 |
|
|
68
|
+
| 1 meshed rig animating (canvas2d) | canvas | `portrait-packed` `turn` | pane | 60.00 | 0.20 | 0.18 | —/1 | 88 | 1 | 0.007 | —/1 |
|
|
69
|
+
| 1 meshed rig animating (canvas2d) | official | `portrait-packed` `turn` | pane | 59.99 | 0.06 | 0.14 | 1/1 | 88 | 1 | 0.000 | 1/1 |
|
|
70
|
+
| 16 meshed rigs animating (canvas2d) | dom | `portrait-packed` `turn` | pane | 59.89 | 0.65 | 0.99 | —/16 | 384 | — | 0.010 | —/0 |
|
|
71
|
+
| 16 meshed rigs animating (canvas2d) | canvas | `portrait-packed` `turn` | pane | 60.00 | 0.93 | 0.59 | —/16 | 792 | 9 | 0.010 | —/1 |
|
|
72
|
+
| 16 meshed rigs animating (canvas2d) | official | `portrait-packed` `turn` | pane | 60.00 | 0.18 | 0.31 | 16/16 | 1408 | 1 | 0.000 | 1/1 |
|
|
73
|
+
| 1 held pose | dom | `portrait-packed` `turn` | pane | 60.11 | 0.00 | 0.15 | —/1 | 0 | — | 0.007 | —/1 |
|
|
74
|
+
| 1 held pose | canvas | `portrait-packed` `turn` | pane | 60.02 | 0.01 | 0.11 | —/1 | 0 | 0 | 0.007 | —/1 |
|
|
75
|
+
| 1 held pose | official | `portrait-packed` `turn` | pane | 60.00 | 0.00 | 0.13 | 1/1 | 88 | 1 | 0.000 | 1/1 |
|
|
76
|
+
| 16 held poses | dom | `portrait-packed` `turn` | pane | 60.09 | 0.00 | 0.43 | —/16 | 0 | — | 0.010 | —/1 |
|
|
77
|
+
| 16 held poses | canvas | `portrait-packed` `turn` | pane | 59.99 | 0.01 | 0.55 | —/16 | 0 | 0 | 0.010 | —/1 |
|
|
78
|
+
| 16 held poses | official | `portrait-packed` `turn` | pane | 60.08 | 0.00 | 0.35 | 16/16 | 1408 | 1 | 0.000 | 1/1 |
|
|
79
|
+
| 64 held poses | dom | `portrait-packed` `turn` | pane | 59.99 | 0.02 | 2.43 | —/64 | 0 | — | 0.010 | —/1 |
|
|
80
|
+
| 64 held poses | canvas | `portrait-packed` `turn` | pane | 60.10 | 0.02 | 2.56 | —/64 | 0 | 0 | 0.010 | —/1 |
|
|
81
|
+
| 64 held poses | official | `portrait-packed` `turn` | pane | 60.10 | 0.00 | 0.90 | 64/64 | 5632 | 1 | 0.000 | 1/1 |
|
|
82
|
+
| 1 rigid rig animating | dom | `walk-packed` `walk` | pane | 59.99 | 0.30 | 0.15 | —/1 | 0 | — | 0.007 | —/1 |
|
|
83
|
+
| 1 rigid rig animating | canvas | `walk-packed` `walk` | pane | 59.99 | 0.33 | 0.21 | —/1 | 30 | 1 | 0.007 | —/1 |
|
|
84
|
+
| 1 rigid rig animating | official | `walk-packed` `walk` | pane | 60.00 | 0.04 | 0.16 | 1/1 | 30 | 1 | 0.000 | 1/1 |
|
|
85
|
+
| 16 rigid rigs animating | dom | `walk-packed` `walk` | pane | 60.00 | 0.77 | 0.65 | —/16 | 0 | — | 0.010 | —/1 |
|
|
86
|
+
| 16 rigid rigs animating | canvas | `walk-packed` `walk` | pane | 60.00 | 0.62 | 0.47 | —/16 | 480 | 16 | 0.010 | —/1 |
|
|
87
|
+
| 16 rigid rigs animating | official | `walk-packed` `walk` | pane | 60.00 | 0.15 | 0.22 | 16/16 | 480 | 1 | 0.000 | 1/1 |
|
|
88
|
+
| 64 rigid rigs animating | dom | `walk-packed` `walk` | pane | 59.89 | 1.58 | 1.63 | —/64 | 0 | — | 0.010 | —/1 |
|
|
89
|
+
| 64 rigid rigs animating | canvas | `walk-packed` `walk` | pane | 42.00 | 1.50 | 1.21 | —/64 | 1920 | 64 | 0.010 | —/1 |
|
|
90
|
+
| 64 rigid rigs animating | official | `walk-packed` `walk` | pane | 60.00 | 0.14 | 0.13 | 64/64 | 1920 | 1 | 0.000 | 1/1 |
|
|
91
|
+
| 8 small players | dom | `walk-packed` `walk` | cell | 60.00 | 0.24 | 0.18 | —/8 | 0 | — | 0.012 | —/1 |
|
|
92
|
+
| 8 small players | canvas | `walk-packed` `walk` | cell | 60.00 | 0.33 | 0.30 | —/8 | 240 | 8 | 0.012 | —/1 |
|
|
93
|
+
| 8 small players | official | `walk-packed` `walk` | cell | 60.00 | 0.09 | 0.24 | 8/8 | 240 | 8 | 0.007 | 8/8 |
|
|
94
|
+
| 20 small players | dom | `walk-packed` `walk` | cell | 60.00 | 1.03 | 0.84 | —/20 | 0 | — | 0.015 | —/1 |
|
|
95
|
+
| 20 small players | canvas | `walk-packed` `walk` | cell | 59.99 | 0.69 | 0.54 | —/20 | 600 | 20 | 0.015 | —/1 |
|
|
96
|
+
| 20 small players | official | `walk-packed` `walk` | cell | 60.00 | 0.13 | 0.24 | 16/20 | 480 | 16 | 0.006 | 16/20 |
|
|
97
|
+
|
|
98
|
+
Where each side wins and loses on that table:
|
|
99
|
+
|
|
100
|
+
- **High-count meshed grids (`36` and `64 meshed rigs animating`) — `official` wins**: batching
|
|
101
|
+
all 64 skeletons into one WebGL canvas holds `59.99 fps` (`5,632 triangles` in `1 draw call`),
|
|
102
|
+
while `canvas` mode drops to `53.99 fps` (per-player `drawImage` blits from the shared WebGL
|
|
103
|
+
context) and `dom` mode drops to `48.28 fps` at 36 rigs and `29.07 fps` at 64 rigs (per-part
|
|
104
|
+
mesh canvas blits + DOM compositing). Up to 16 animating meshed rigs all three hold `60 fps`.
|
|
105
|
+
- **Held poses (`1`, `16`, `64 held poses`) — `dom` and `canvas` skip rasterization**: both
|
|
106
|
+
modes reuse their rasters (`0 triangles`, `0 draw calls`), while `official` redraws
|
|
107
|
+
`88` / `1,408` / `5,632 triangles` every frame.
|
|
108
|
+
- **Rigid-heavy grids (`64 rigid rigs animating`) — `dom` and batched `official` hold `60 fps`**:
|
|
109
|
+
`dom` updates `<img>` CSS transforms (`0 triangles`, `59.89 fps`), whereas `canvas` pays 64
|
|
110
|
+
per-player `drawImage` blits (`42.00 fps`).
|
|
111
|
+
- **Many independent players on one page (`20 small players`) — `dom` and `canvas` draw all 20;
|
|
112
|
+
`official` loses 4 to the browser's 16-context WebGL cap**: both `@rig-c/play` modes share at
|
|
113
|
+
most one WebGL context across the page, while mounting 20 independent `spine-webgl` players
|
|
114
|
+
evicts 4 contexts (`16/20 drawn players`, `16/20 live contexts`).
|
|
115
|
+
|
|
41
116
|
**The element mode's mesh backend is `'webgl'` by default since 1.9 (#109)**, read off
|
|
42
117
|
macOS Safari on 2026-10-13 (1.8.2, gallery rig `look-packed`: 4 meshes, 464
|
|
43
118
|
triangles, `sweep`; the demo's stats line after a 30 s settle; the Mac was also
|
|
@@ -131,9 +206,11 @@ default stays `dom`.
|
|
|
131
206
|
not expressible this way and stays out of scope
|
|
132
207
|
- ✅ DPR-aware mesh canvas backing store: `renderer.pixelRatio` (defaults to
|
|
133
208
|
`devicePixelRatio`; if you scale the root element, fold that scale in)
|
|
134
|
-
- ✅ Dirty-skip: a mesh whose canvas-space vertices
|
|
135
|
-
|
|
136
|
-
|
|
209
|
+
- ✅ Dirty-skip: a mesh whose canvas-space vertices match the last raster reuses it;
|
|
210
|
+
whole-pixel translations allow at most half a Float32 ULP at the current world
|
|
211
|
+
coordinate, strictly less than half a pixel. CSS still tracks the translation, while
|
|
212
|
+
stationary axes stay exact and the comparison anchor changes only on a redraw.
|
|
213
|
+
10 frozen spineboys: WebKit ~141 → ~0.5 ms/frame
|
|
137
214
|
- ✅ Clipping attachments as element-level CSS `clip-path`: spine-core's semantics
|
|
138
215
|
(one active clip at a time, applied from the clip's slot through its end slot
|
|
139
216
|
inclusive), with the polygon expressed in each element's own local frame — the
|
|
@@ -294,6 +371,11 @@ layer answers the same world, and a `seek` replays under the environment as it
|
|
|
294
371
|
stands — no history is kept. A rig with no physics ignores both.
|
|
295
372
|
`tests/readme-page.spec.ts` runs this block in the minimal page.
|
|
296
373
|
|
|
374
|
+
The demo's `wind.html` is the same thing with controls: wind and gravity
|
|
375
|
+
sliders, a drag on the stage, and the rig named by `?model=` (the `cloth` test
|
|
376
|
+
rig by default; `bun run dev`, then `/wind.html?model=rigs/<name>/skeleton.model.json`).
|
|
377
|
+
`tests/wind-page.spec.ts` holds it.
|
|
378
|
+
|
|
297
379
|
The player owns one track: `play(name, { loop })` opens an animation at its first
|
|
298
380
|
pose, `frame(dt)` steps it (or `advance(dt)` then `draw()`, to time the two
|
|
299
381
|
apart), `pause()` / `resume()` hold and release it, `timeScale` scales every
|
|
@@ -392,6 +474,52 @@ const front = new DomRenderer(frontRoot, assets.regionImages, assets.pageImages)
|
|
|
392
474
|
front.render(player.posedFrame(['hand_f', 'arm_f_lo']));
|
|
393
475
|
```
|
|
394
476
|
|
|
477
|
+
### Attachment sets (`sets.json`)
|
|
478
|
+
|
|
479
|
+
A build from `@rig-c/parts` (or authored alongside a rig) may ship a `sets.json`
|
|
480
|
+
sidecar beside `skeleton.model.json` declaring named attachment sets — an
|
|
481
|
+
expression, a viseme, a side view — under spec `"rig-parts-sets/1"` or
|
|
482
|
+
`"spine-parts-sets/1"`:
|
|
483
|
+
|
|
484
|
+
```json
|
|
485
|
+
{
|
|
486
|
+
"spec": "rig-parts-sets/1",
|
|
487
|
+
"sets": {
|
|
488
|
+
"smile": {
|
|
489
|
+
"slots": { "face": "face_smile", "prop": "prop_star" },
|
|
490
|
+
"hides": []
|
|
491
|
+
},
|
|
492
|
+
"wink": {
|
|
493
|
+
"slots": { "face": "face_wink" },
|
|
494
|
+
"hides": ["prop"]
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
`loadModel` probes `sets.json` in the model document's directory by default
|
|
501
|
+
(treating a 404 as "no sets"; pass `setsUrl: null` to skip the probe, or a URL
|
|
502
|
+
string to require one at another path) and validates every slot and attachment
|
|
503
|
+
name against the model document. `player.sets()` (and `assets.sets`) returns the
|
|
504
|
+
declared `readonly RigSet[]` (`{ name, slots, hides }`), and `player.setSet(name)`
|
|
505
|
+
switches the active set at runtime (#134):
|
|
506
|
+
|
|
507
|
+
```ts
|
|
508
|
+
player.sets(); // [{ name: 'smile', slots: { ... }, hides: [] }, ...]
|
|
509
|
+
player.setSet('smile'); // covered slots take the set's attachments; <slot>_still and hides are hidden
|
|
510
|
+
player.set; // 'smile'
|
|
511
|
+
player.setSet(null); // restore base / track attachments
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
The active set is applied immediately to the held pose (including the setup pose
|
|
515
|
+
before `play`) and after track evaluation on every subsequent `advance`, so a
|
|
516
|
+
track key on a covered slot is overridden while the set is active and restored
|
|
517
|
+
on the next frame when cleared with `setSet(null)`. Uncovered slots keep their
|
|
518
|
+
track attachments, mesh deforms and sequence frames untouched; when no set is
|
|
519
|
+
active (`null`), track evaluation runs with zero extra work and produces
|
|
520
|
+
byte-identical poses. An unknown set name throws by name; on a build without
|
|
521
|
+
`sets.json`, `sets()` is empty (`[]`) and `setSet` throws.
|
|
522
|
+
|
|
395
523
|
### Several tracks
|
|
396
524
|
|
|
397
525
|
A base on track 0, a layer over it, a one-shot played once and mixed back out —
|
|
@@ -937,23 +1065,17 @@ deterministic captures.
|
|
|
937
1065
|
bun run test # builds + serves the demo, then runs chromium + webkit
|
|
938
1066
|
```
|
|
939
1067
|
|
|
940
|
-
Playwright drives
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
across platforms/GPUs) — instead the same deterministic pose (`?time` +
|
|
952
|
-
`?timescale=0`) is screenshotted twice in the same engine and the buffers are
|
|
953
|
-
diffed directly with a shift-tolerant comparison, so missing parts, wrong
|
|
954
|
-
colors, and tint/blend/clip divergence fail regardless of platform.
|
|
955
|
-
|
|
956
|
-
**The picture gate** is the two render modes drawing the same picture: every
|
|
1068
|
+
Playwright drives rigc-built gallery and procedural test rigs through the
|
|
1069
|
+
production `RigPlayer`; its pose comes from `rig-c/core`. CI runs the same
|
|
1070
|
+
suite on Ubuntu with Chromium and WebKit. Internal parity captures the same
|
|
1071
|
+
deterministic pose in two modes/backends within one engine. An independent
|
|
1072
|
+
development oracle also reads the paired Spine export through the official
|
|
1073
|
+
WebGL runtime. No snapshot goldens are committed. Residue limits are measured
|
|
1074
|
+
per engine, case and renderer; an A/B design alone does not make a measured
|
|
1075
|
+
budget portable to another platform. Pose discrepancies remain rigc issues;
|
|
1076
|
+
the independent oracle checks the resulting raster.
|
|
1077
|
+
|
|
1078
|
+
**Internal picture parity** checks the two render modes drawing the same picture: every
|
|
957
1079
|
scene captured in the DOM mode and in the canvas mode and diffed — bad pixels at
|
|
958
1080
|
most 1.5% of drawn content on the test-rig scenes (measured floors 0.007–0.524%,
|
|
959
1081
|
macOS Chromium; each scene's own feature dropped from the canvas mode — tint,
|
|
@@ -989,16 +1111,58 @@ resolving, so blob ownership — every unpacked URL freed, nothing the caller
|
|
|
989
1111
|
owns touched, nothing stranded by a failed load — is asserted rather than
|
|
990
1112
|
assumed.
|
|
991
1113
|
|
|
992
|
-
|
|
1114
|
+
### Independent official raster oracle
|
|
1115
|
+
|
|
1116
|
+
`tests/oracle.spec.ts` compares real DOM WebGL, DOM canvas2d and whole-canvas
|
|
1117
|
+
output with `@esotericsoftware/spine-webgl` 4.3.13. The official reader parses
|
|
1118
|
+
the same build's Spine JSON, atlas and original PNGs independently; it never
|
|
1119
|
+
receives a `PosedFrame` or the player's alpha-corrected textures. JSON SHA-256
|
|
1120
|
+
binds the export to the compiled model, and generation/page dimensions fail
|
|
1121
|
+
closed. Both posers receive the same fixed-step history, crop, origin, scale
|
|
1122
|
+
and DPR 1; world geometry must agree exactly before raster acceptance.
|
|
1123
|
+
|
|
1124
|
+
Captures include the actual images, element canvases, CSS transforms, tint
|
|
1125
|
+
and clipping. Transparent RGB remains diagnostic, except for additional narrow
|
|
1126
|
+
raw checks on the whole-canvas PMA case. Acceptance compares those
|
|
1127
|
+
same captured bytes on black and white backgrounds with the shared pixel
|
|
1128
|
+
differ, and separately constrains alpha coverage, sum, unequal-pixel count
|
|
1129
|
+
and maximum. `tests/oracleLimits.ts` stores measured engine/case/mode floors:
|
|
1130
|
+
zero stays exact; nonzero counts receive 10% plus two units, and nonzero byte
|
|
1131
|
+
maxima one byte. macOS and Linux have separately measured tables; other
|
|
1132
|
+
platforms are refused as uncalibrated. Tests never regenerate limits from the
|
|
1133
|
+
image under test.
|
|
1134
|
+
|
|
1135
|
+
Cases cover geometry, tint, clipping, draw order with multiply/screen,
|
|
1136
|
+
rotated/named regions, additive glow and an original-PMA twin. Named UV and
|
|
1137
|
+
rotation coverage uses native scale: measured half-scale checker sampling
|
|
1138
|
+
has additional residue, so arbitrary-scale pixel identity is not claimed.
|
|
1139
|
+
The shared #37 negative control serves false PMA flags only in the player's
|
|
1140
|
+
model response, leaving original files and the official reader unchanged.
|
|
1141
|
+
Each renderer must first pass its healthy capture, then exceed the same
|
|
1142
|
+
healthy black-composite budget with the altered response. This runs on both
|
|
1143
|
+
CI browser projects. Original PMA PNG bytes are encoded before either reader
|
|
1144
|
+
sees them.
|
|
1145
|
+
|
|
1146
|
+
The reference uses `antialias:false`, `preserveDrawingBuffer:true` and the
|
|
1147
|
+
official `pmaAdditiveBatching:false` option to retain additive coverage in
|
|
1148
|
+
transparent captures. Initialization finishes model/region extraction before
|
|
1149
|
+
official page uploads, avoiding a measured concurrent WebKit initialization
|
|
1150
|
+
artifact. Use the required nova-run guard and a unique `TEST_PORT` for a
|
|
1151
|
+
focused `bun run test tests/oracle.spec.ts --workers=1`; full-suite acceptance
|
|
1152
|
+
remains CI. Headless results are raster evidence, never device performance.
|
|
1153
|
+
|
|
1154
|
+
## Benchmark: DOM, canvas and official WebGL
|
|
993
1155
|
|
|
994
1156
|
```bash
|
|
995
1157
|
bun run bench:dev # the page, for a person to watch (and for Safari)
|
|
996
1158
|
bun run bench # drives it in a headed Chromium and writes a report
|
|
1159
|
+
bun run bench --headless # counters only; all timing fields are null
|
|
997
1160
|
```
|
|
998
1161
|
|
|
999
1162
|
`bench/` is a separate vite project — not part of the library, and not part of
|
|
1000
|
-
what `bun run build` emits — that
|
|
1001
|
-
|
|
1163
|
+
what `bun run build` emits — that compares DOM, whole-canvas and the independent
|
|
1164
|
+
official WebGL reader, each with its own stats line. The paired JSON hash must
|
|
1165
|
+
match the model before an official comparison can publish readings.
|
|
1002
1166
|
Scenes by query string: `?scene=mesh` (`portrait-packed` turning its head, the
|
|
1003
1167
|
deform tier working every frame), `?scene=held` (the same pose frozen, where
|
|
1004
1168
|
unchanged meshes reuse their raster and an unchanged frame is reused),
|
|
@@ -1009,7 +1173,7 @@ something you can see), laid out on one grid whose per-rig scale shrinks with
|
|
|
1009
1173
|
the column count, so a step up in count adds rigs rather than cropping the ones
|
|
1010
1174
|
already there.
|
|
1011
1175
|
|
|
1012
|
-
**The
|
|
1176
|
+
**The comparison uses the same framing.** All paths place rigs with one derivation and
|
|
1013
1177
|
clip each to the same rect — the pane for a grid, the rig's own cell for `many`
|
|
1014
1178
|
— with `overflow: hidden` on a box, the way a page embeds a player, and then
|
|
1015
1179
|
*check* it: each side reports `origin Δpx`, the largest gap between where the
|
|
@@ -1017,9 +1181,12 @@ grid puts a rig and where that side drew it, and the report table carries it per
|
|
|
1017
1181
|
row. (`contain: layout paint` is deliberately not used: paint containment clips
|
|
1018
1182
|
to the same edge `overflow: hidden` does, and what it adds past the clip — a
|
|
1019
1183
|
stacking context, a containing block, an independent formatting context —
|
|
1020
|
-
changes how one side is composited and buys the comparison nothing.)
|
|
1021
|
-
|
|
1022
|
-
|
|
1184
|
+
changes how one side is composited and buys the comparison nothing.) Production
|
|
1185
|
+
modes retain the package's shared WebGL context. Official grid scenes batch
|
|
1186
|
+
all skeletons through one context; only `many` uses independent contexts.
|
|
1187
|
+
Official surviving contexts and drawn players are counted separately from
|
|
1188
|
+
requested players. Production survival counters are null where its public
|
|
1189
|
+
API cannot measure them, rather than treating requested counts as survivors.
|
|
1023
1190
|
|
|
1024
1191
|
`scripts/bench.mjs` walks `bench/scenes.json`, opens each scene in a **headed**
|
|
1025
1192
|
Chromium (a hidden page throttles `requestAnimationFrame`, so a headless
|
|
@@ -1027,15 +1194,20 @@ reading would be the browser's power policy rather than either mode's cost),
|
|
|
1027
1194
|
runs a fixed number of frames per mode — one at a time, because rAF has a single
|
|
1028
1195
|
cadence per document — and writes JSON plus a markdown table with the
|
|
1029
1196
|
conditions attached: browser version, device pixel ratio, window size, GPU
|
|
1030
|
-
string, and the load average before and after.
|
|
1197
|
+
string, and the load average before and after. A software or unidentified GPU
|
|
1198
|
+
invalidates a headed timing report. Over a load threshold it stamps the
|
|
1031
1199
|
whole report `INVALID: load average too high` and exits non-zero, because a
|
|
1032
1200
|
benchmark on a busy machine measures the machine. `--corpus <dir>` points it at
|
|
1033
|
-
local rigc builds that never enter this repository.
|
|
1201
|
+
local rigc builds that never enter this repository. `--headless` instead steps
|
|
1202
|
+
fixed frames and records counters only: FPS, pose/render timing and GPU fields
|
|
1203
|
+
are null, with “측정 불가: 헤드리스” in JSON and the table. The official
|
|
1204
|
+
additive-batching and drawing-buffer settings are recorded as conditions.
|
|
1034
1205
|
|
|
1035
|
-
> **
|
|
1206
|
+
> **Performance numbers pending.** Counter reports establish the instrument,
|
|
1207
|
+
> not a performance comparison. A timing reading taken on
|
|
1036
1208
|
> a loaded machine, a CI runner or a headless browser would not be worth
|
|
1037
1209
|
> printing. The table will be taken the way every other number in this README
|
|
1038
|
-
> was: the
|
|
1210
|
+
> was: the stats lines read on a real device, on a quiet machine, per scene
|
|
1039
1211
|
> and per engine — including Safari, which stays manual because no automation
|
|
1040
1212
|
> preserves what is being measured there. Until then the instrument exists and
|
|
1041
1213
|
> the measurement does not.
|
|
@@ -1079,12 +1251,16 @@ so here is what is true of this package and this repository, stated plainly:
|
|
|
1079
1251
|
`rig-c/core` (MIT), which poses rigc's model document; no part of the
|
|
1080
1252
|
[Spine Runtimes](https://github.com/EsotericSoftware/spine-runtimes) is in it or
|
|
1081
1253
|
required beside it.
|
|
1082
|
-
2.
|
|
1083
|
-
|
|
1084
|
-
|
|
1254
|
+
2. Development tooling pins `@esotericsoftware/spine-webgl` 4.3.13 and its matching
|
|
1255
|
+
spine-core dependency for the independent oracle and comparison/benchmark.
|
|
1256
|
+
The [Spine Runtimes License Agreement](https://esotericsoftware.com/spine-runtimes-license)
|
|
1257
|
+
covers those runtimes, not this project's MIT license. They remain outside
|
|
1258
|
+
production source, the public API and the library package; package guards
|
|
1259
|
+
and the library-only pack allowlist hold that boundary.
|
|
1085
1260
|
3. `rigc build`, which this repository runs to make its fixtures, also writes Spine
|
|
1086
1261
|
skeleton data (`skeleton.json` and `skeleton.atlas`) beside each model document.
|
|
1087
|
-
|
|
1262
|
+
Development tooling reads this paired export independently. Generated fixtures
|
|
1263
|
+
remain gitignored and excluded from the library package. See [NOTICE.md](NOTICE.md).
|
|
1088
1264
|
4. A product that plays that Spine data with a Spine Runtime has integrated the Spine
|
|
1089
1265
|
Runtimes, whose licence permits it **under Section 2 of the
|
|
1090
1266
|
[Spine Editor License Agreement](https://esotericsoftware.com/spine-editor-license)**,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DomRenderer.d.ts","sourceRoot":"","sources":["../src/DomRenderer.ts"],"names":[],"mappings":"AACA,OAAO,EAAmB,KAAK,WAAW,EAAuB,MAAM,iBAAiB,CAAC;AAEzF,OAAO,KAAK,EAAwB,UAAU,EAAqC,MAAM,iBAAiB,CAAC;AAE3G,kDAAkD;AAClD,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,OAAO,CAAC;AAE/C;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"DomRenderer.d.ts","sourceRoot":"","sources":["../src/DomRenderer.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { type CanvasBackend, CanvasRenderer, type CanvasViewport } from './CanvasRenderer.js';
|
|
2
2
|
export { DomRenderer, type MeshBackend, type MeshSourceRect } from './DomRenderer.js';
|
|
3
3
|
export { type PageLayout, type PageRegion, type RegionEncoding, type RegionImage, revokeRegions, type UnpackRegionsOptions, unpackRegions } from './DomTexture.js';
|
|
4
|
-
export { type LoadModelOptions, loadModel, type ModelAssets, type ModelDocument, type RegionLoading } from './loadModel.js';
|
|
4
|
+
export { type LoadModelOptions, loadModel, type ModelAssets, type ModelDocument, type RegionLoading, type RigSet } from './loadModel.js';
|
|
5
5
|
export { type AdjustableBones, type BoneAdjust, type BoneLocals, type BoneWorld, type PlayOptions, type PoseBounds, type QueueOptions, type RenderMode, type RigComplete, type RigEnvironment, type RigEvent, RigPlayer, type RigPlayerOptions, type SlotFilter, type TrackEntry } from './player.js';
|
|
6
6
|
export type { BlendMode, PosedClip, PosedFrame, PosedMesh, PosedRegion, PosedSlot, } from './posedFrame.js';
|
|
7
7
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC9F,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACtF,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,cAAc,EAAE,KAAK,WAAW,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACnK,OAAO,EAAE,KAAK,gBAAgB,EAAE,SAAS,EAAE,KAAK,WAAW,EAAE,KAAK,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC9F,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACtF,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,cAAc,EAAE,KAAK,WAAW,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACnK,OAAO,EAAE,KAAK,gBAAgB,EAAE,SAAS,EAAE,KAAK,WAAW,EAAE,KAAK,aAAa,EAAE,KAAK,aAAa,EAAE,KAAK,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACzI,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,SAAS,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,KAAK,cAAc,EAAE,KAAK,QAAQ,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AACtS,YAAY,EACV,SAAS,EACT,SAAS,EACT,UAAU,EACV,SAAS,EACT,WAAW,EACX,SAAS,GACV,MAAM,iBAAiB,CAAC"}
|