@woosh/meep-engine 3.14.3 → 3.14.5
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/package.json +1 -1
- package/src/core/geom/3d/mat4/m4_rigidity_defect.d.ts +30 -0
- package/src/core/geom/3d/mat4/m4_rigidity_defect.d.ts.map +1 -0
- package/src/core/geom/3d/mat4/m4_rigidity_defect.js +71 -0
- package/src/engine/Engine.d.ts.map +1 -1
- package/src/engine/Engine.js +733 -726
- package/src/engine/network/NetworkSession.d.ts +24 -4
- package/src/engine/network/NetworkSession.d.ts.map +1 -1
- package/src/engine/network/NetworkSession.js +28 -4
- package/src/engine/network/orchestrator/NetworkPeer.d.ts +96 -7
- package/src/engine/network/orchestrator/NetworkPeer.d.ts.map +1 -1
- package/src/engine/network/orchestrator/NetworkPeer.js +392 -68
- package/src/engine/network/orchestrator/ServerAuthoritativeClient.d.ts +6 -1
- package/src/engine/network/orchestrator/ServerAuthoritativeClient.d.ts.map +1 -1
- package/src/engine/network/orchestrator/ServerAuthoritativeClient.js +6 -0
- package/src/engine/network/orchestrator/ServerAuthoritativeServer.d.ts +105 -13
- package/src/engine/network/orchestrator/ServerAuthoritativeServer.d.ts.map +1 -1
- package/src/engine/network/orchestrator/ServerAuthoritativeServer.js +306 -59
- package/src/engine/network/replication/Replicator.d.ts +43 -7
- package/src/engine/network/replication/Replicator.d.ts.map +1 -1
- package/src/engine/network/replication/Replicator.js +309 -111
- package/src/engine/network/sim/ActionLog.d.ts +23 -3
- package/src/engine/network/sim/ActionLog.d.ts.map +1 -1
- package/src/engine/network/sim/ActionLog.js +26 -3
- package/src/engine/network/sim/ActionRecordCursor.d.ts +82 -0
- package/src/engine/network/sim/ActionRecordCursor.d.ts.map +1 -0
- package/src/engine/network/sim/ActionRecordCursor.js +120 -0
- package/src/engine/network/sim/RewindEngine.d.ts.map +1 -1
- package/src/engine/network/sim/RewindEngine.js +13 -29
- package/src/engine/network/sim/SimAction.d.ts +15 -0
- package/src/engine/network/sim/SimAction.d.ts.map +1 -1
- package/src/engine/network/sim/SimAction.js +15 -0
- package/src/engine/network/sim/SimActionExecutor.d.ts +51 -2
- package/src/engine/network/sim/SimActionExecutor.d.ts.map +1 -1
- package/src/engine/network/sim/SimActionExecutor.js +74 -7
- package/src/engine/network/transport/Channel.d.ts +1 -1
- package/src/engine/network/transport/Channel.js +1 -1
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
- package/src/engine/simulation/Ticker.d.ts.map +1 -1
- package/src/engine/simulation/Ticker.js +4 -1
- package/src/engine/ui/GUIEngine.d.ts.map +1 -1
- package/src/engine/ui/GUIEngine.js +6 -0
- package/src/shade/playground/particle_system/README.md +98 -0
- package/src/shade/playground/particle_system/index.html +124 -0
- package/src/shade/playground/particle_system/main.d.ts +2 -0
- package/src/shade/playground/particle_system/main.d.ts.map +1 -0
- package/src/shade/playground/particle_system/main.js +180 -0
- package/src/shade/playground/particle_system/particle_scene.d.ts +52 -0
- package/src/shade/playground/particle_system/particle_scene.d.ts.map +1 -0
- package/src/shade/playground/particle_system/particle_scene.js +553 -0
- package/src/shade/playground/skinned_mesh_soup/README.md +143 -0
- package/src/shade/playground/skinned_mesh_soup/index.html +202 -0
- package/src/shade/playground/skinned_mesh_soup/main.d.ts +7 -0
- package/src/shade/playground/skinned_mesh_soup/main.d.ts.map +1 -0
- package/src/shade/playground/skinned_mesh_soup/main.js +567 -0
- package/src/shade/playground/skinned_mesh_soup/verify_clone_vertices.d.ts +29 -0
- package/src/shade/playground/skinned_mesh_soup/verify_clone_vertices.d.ts.map +1 -0
- package/src/shade/playground/skinned_mesh_soup/verify_clone_vertices.js +428 -0
- package/src/shade/renderer/animation/GPUAnimationManager.d.ts.map +1 -1
- package/src/shade/renderer/animation/GPUAnimationManager.js +1958 -1796
- package/src/shade/renderer/animation/SKINNING_FRAME_PLAN.md +357 -0
- package/src/shade/renderer/animation/skin_bind_transform.d.ts +25 -0
- package/src/shade/renderer/animation/skin_bind_transform.d.ts.map +1 -0
- package/src/shade/renderer/animation/skin_bind_transform.js +33 -0
- package/src/shade/renderer/animation/skin_clip_scale_conflicts.d.ts +67 -0
- package/src/shade/renderer/animation/skin_clip_scale_conflicts.d.ts.map +1 -0
- package/src/shade/renderer/animation/skin_clip_scale_conflicts.js +199 -0
- package/src/shade/renderer/animation/skin_frame_defect.d.ts +34 -0
- package/src/shade/renderer/animation/skin_frame_defect.d.ts.map +1 -0
- package/src/shade/renderer/animation/skin_frame_defect.js +132 -0
- package/src/shade/renderer/animation/skin_joint_matrix.d.ts +24 -0
- package/src/shade/renderer/animation/skin_joint_matrix.d.ts.map +1 -0
- package/src/shade/renderer/animation/skin_joint_matrix.js +46 -0
- package/src/shade/renderer/animation/skin_normalize_mesh_frame.d.ts +70 -0
- package/src/shade/renderer/animation/skin_normalize_mesh_frame.d.ts.map +1 -0
- package/src/shade/renderer/animation/skin_normalize_mesh_frame.js +216 -0
- package/src/shade/renderer/animation/skin_test_fixtures.d.ts +86 -0
- package/src/shade/renderer/animation/skin_test_fixtures.d.ts.map +1 -0
- package/src/shade/renderer/animation/skin_test_fixtures.js +250 -0
- package/src/shade/renderer/animation/skinning/chunk_skin_blend_mesh_local.d.ts +18 -4
- package/src/shade/renderer/animation/skinning/chunk_skin_blend_mesh_local.d.ts.map +1 -1
- package/src/shade/renderer/animation/skinning/chunk_skin_blend_mesh_local.js +18 -4
- package/src/shade/renderer/particles/DESIGN.md +5 -5
- package/src/shade/renderer/particles/cull/chunk_particle_cull.d.ts.map +1 -1
- package/src/shade/renderer/particles/cull/chunk_particle_cull.js +3 -1
- package/src/shade/renderer/particles/data/chunk_particle_emitter.js +1 -1
- package/src/shade/renderer/particles/graph/ParticleNodeDescription.d.ts +43 -0
- package/src/shade/renderer/particles/graph/ParticleNodeDescription.d.ts.map +1 -0
- package/src/shade/renderer/particles/graph/ParticleNodeDescription.js +44 -0
- package/src/shade/renderer/particles/graph/ParticleNodeRegistry.d.ts +33 -59
- package/src/shade/renderer/particles/graph/ParticleNodeRegistry.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/ParticleNodeRegistry.js +185 -118
- package/src/shade/renderer/particles/graph/compile_particle_graph.d.ts +15 -10
- package/src/shade/renderer/particles/graph/compile_particle_graph.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/compile_particle_graph.js +205 -170
- package/src/shade/renderer/particles/graph/particle_graph_authoring.d.ts +44 -0
- package/src/shade/renderer/particles/graph/particle_graph_authoring.d.ts.map +1 -0
- package/src/shade/renderer/particles/graph/particle_graph_authoring.js +108 -0
- package/src/shade/renderer/particles/isa/InstructionStream.d.ts +89 -0
- package/src/shade/renderer/particles/isa/InstructionStream.d.ts.map +1 -0
- package/src/shade/renderer/particles/isa/InstructionStream.js +162 -0
- package/src/shade/renderer/particles/isa/ParticleAssembler.d.ts +1 -48
- package/src/shade/renderer/particles/isa/ParticleAssembler.d.ts.map +1 -1
- package/src/shade/renderer/particles/isa/ParticleAssembler.js +25 -114
- package/src/shade/renderer/particles/particle_prototype.d.ts.map +1 -1
- package/src/shade/renderer/particles/particle_prototype.js +181 -180
- package/src/shade/renderer/particles/runtime/create_particle_effect.d.ts +4 -4
- package/src/shade/renderer/particles/runtime/create_particle_effect.d.ts.map +1 -1
- package/src/shade/renderer/particles/runtime/create_particle_effect.js +19 -19
- package/src/shade/renderer/particles/shaders/chunk_particle_curve_animation.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/chunk_particle_curve_animation.js +4 -2
- package/src/shade/renderer/particles/shaders/chunk_particle_lighting.d.ts +4 -4
- package/src/shade/renderer/particles/shaders/chunk_particle_lighting.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/chunk_particle_lighting.js +7 -5
- package/src/shade/renderer/particles/shaders/chunk_particle_render_math.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/chunk_particle_render_math.js +3 -1
- package/src/shade/renderer/particles/shaders/shader_particle_emit.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/shader_particle_emit.js +3 -1
- package/src/shade/renderer/particles/shaders/shader_particle_finalize.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/shader_particle_finalize.js +5 -2
- package/src/shade/renderer/particles/shaders/shader_particle_render.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/shader_particle_render.js +9 -2
- package/src/shade/renderer/particles/shaders/shader_particle_simulate.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/shader_particle_simulate.js +3 -1
- package/src/shade/renderer/particles/vm/chunk_particle_vm.js +1 -1
- package/src/shade/renderer/scene/SkinnedMesh.d.ts +11 -0
- package/src/shade/renderer/scene/SkinnedMesh.d.ts.map +1 -1
- package/src/shade/renderer/scene/SkinnedMesh.js +11 -0
- package/src/shade/renderer/shader/chunk/geometry/quaternion/dual/chunk_dual_quat_from_m4.d.ts +33 -3
- package/src/shade/renderer/shader/chunk/geometry/quaternion/dual/chunk_dual_quat_from_m4.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/geometry/quaternion/dual/chunk_dual_quat_from_m4.js +58 -37
- package/src/shade/renderer/particles/graph/ParticleGraph.d.ts +0 -54
- package/src/shade/renderer/particles/graph/ParticleGraph.d.ts.map +0 -1
- package/src/shade/renderer/particles/graph/ParticleGraph.js +0 -99
- package/src/shade/renderer/particles/prototypeParticleSystem.d.ts +0 -2
- package/src/shade/renderer/particles/prototypeParticleSystem.d.ts.map +0 -1
- package/src/shade/renderer/particles/prototypeParticleSystem.js +0 -448
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# skinned mesh soup — repro
|
|
2
|
+
|
|
3
|
+
The character from `skinned_blas_refit`, loaded the way the game loads one, with the pieces that
|
|
4
|
+
could be making soup on switches.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
npm run dev
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
then open `/src/shade/playground/skinned_mesh_soup/index.html`.
|
|
11
|
+
|
|
12
|
+
`?count=N` for N characters, `?skin=off|bind|animated`, `?trace=1` for the path tracer. Every switch
|
|
13
|
+
is in the url so a state worth showing someone can be sent as a link.
|
|
14
|
+
|
|
15
|
+
## What it turned out to be
|
|
16
|
+
|
|
17
|
+
**`chunk_skin_blend_mesh_local` requires `inverse(mesh.global) × joint.global × inverse_bind` to be
|
|
18
|
+
rigid. For `Michelle.glb` it carries a uniform scale of exactly 100, and a dual quaternion cannot
|
|
19
|
+
carry a scale.**
|
|
20
|
+
|
|
21
|
+
The chunk's own doc comment states the precondition — *"the four products must be rigid, which holds
|
|
22
|
+
whenever the joints and the mesh node share the scaling ancestors"* — and names the shape it was
|
|
23
|
+
written for: an FBX-derived glTF that puts its unit conversion on a root node and authors the
|
|
24
|
+
inverse-bind matrices in **pre-scale** mesh space, so `joint.global × inverse_bind` carries the same
|
|
25
|
+
scale `mesh.global` does and the fold cancels it.
|
|
26
|
+
|
|
27
|
+
Michelle is the other legal shape. Her POSITION data and her inverse-bind matrices are authored in
|
|
28
|
+
the **already-scaled** space — 1.66 m tall, Y-up, as they stand — so
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
joint.global × inverse_bind = I for every one of her 65 joints
|
|
32
|
+
mesh.global = 0.01 scale × the up-axis rotation, from her "Character" root
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
and the fold is `inverse(mesh.global) × I`, a bare **100×**. Measured, at bind and under the dance:
|
|
36
|
+
every folded matrix's column length is `100.0000`, where rigid is 1.
|
|
37
|
+
|
|
38
|
+
`dual_quat_from_m4` builds its quaternion from `sqrt(max(0, 1 ± m00 ± m11 ± m22))`. Those terms are
|
|
39
|
+
`1 + 100·(…)`, so the `1` is swamped and every component whose sum goes negative is **clamped to
|
|
40
|
+
zero** by the `max`. The rotation it extracts is not the matrix's rotation, and how wrong it is
|
|
41
|
+
depends on the matrix — so it is wrong differently for every joint. That is the soup: not one
|
|
42
|
+
transform applied badly, but sixty-five of them disagreeing.
|
|
43
|
+
|
|
44
|
+
The two modes made the mechanism visible:
|
|
45
|
+
|
|
46
|
+
| `?skin=` | what it did | why |
|
|
47
|
+
| --- | --- | --- |
|
|
48
|
+
| `bind` | the right character, exactly 1/100 the size | every joint folds to the *same* matrix, `100·Rx(90°)`. Its `w` and `x` terms are both `1 + 100`, its `y` and `z` both clamp to zero, and the normalize recovers `Rx(90°)` — by luck. Only the scale is lost. Sampled output was `expected / 100` to the digit. |
|
|
49
|
+
| `animated` | soup | the joints' matrices differ, each loses a different amount, and the mesh comes apart. Mean error 121 in a mesh-local space where the body is 224 across; worst 165, on vertices bound to **a single joint at weight 1** — so the blend was not even involved. |
|
|
50
|
+
|
|
51
|
+
`__soup_blend_probe.spec.js` is that measurement: the WGSL of `dual_quat_from_m4`,
|
|
52
|
+
`dual_quat_blend4` and `dual_quat_transform_point` ported to JS, run over the character's own
|
|
53
|
+
matrices at bind and at `t = 0.5 s` of `SambaDance`, against the position the glTF formula puts each
|
|
54
|
+
vertex at. No device.
|
|
55
|
+
|
|
56
|
+
### What it is not
|
|
57
|
+
|
|
58
|
+
`__soup_probe.spec.js` is the ruling-out, and every one of these passes on the parsed asset before a
|
|
59
|
+
GPU is involved:
|
|
60
|
+
|
|
61
|
+
- the geometry is a surface, not a soup — 28 106 triangles, no degenerates, longest edge 0.093
|
|
62
|
+
against a 2.24 diagonal, every meshlet index in range;
|
|
63
|
+
- the skinning attributes are sound — all 19 106 vertices land back on their rest position under
|
|
64
|
+
`joint.global × inverse_bind`, no zero-weight vertices, no out-of-range joint;
|
|
65
|
+
- the source BLAS is exact — 56 211 nodes all reachable, 28 106 leaves each naming a distinct
|
|
66
|
+
triangle whose box matches to the bit, all 28 105 internal nodes exactly their children's union.
|
|
67
|
+
|
|
68
|
+
So it is not the loader, not the meshlet build, not the tree, and not the refit. `skinned_blas_refit`'s
|
|
69
|
+
`verify()` cannot see this at all, and that is worth saying plainly: it holds the tree's root box
|
|
70
|
+
against the bounds chain's box, and **both are derived from the clone's vertex region**. Vertices
|
|
71
|
+
that are wrong in the same way agree with each other perfectly. A green `verify()` and a soup
|
|
72
|
+
picture are consistent.
|
|
73
|
+
|
|
74
|
+
## What was done about it
|
|
75
|
+
|
|
76
|
+
`renderer/animation/SKINNING_FRAME_PLAN.md`. The short version: glTF says a skinned mesh's node
|
|
77
|
+
transform is ignored, so the frame is not the asset's to choose — it is the engine's.
|
|
78
|
+
`register_skin` now sets it to the skin's own bind transform (`skin_normalize_mesh_frame`), which
|
|
79
|
+
makes the fold the identity at bind and a rigid displacement under a clip, for **both** shapes a
|
|
80
|
+
glTF file arrives in. One matrix per skin, once, at registration. No shader change and no
|
|
81
|
+
per-vertex cost, and a file that was already right is not touched.
|
|
82
|
+
|
|
83
|
+
Same measurement, after:
|
|
84
|
+
|
|
85
|
+
| | folded scale | mean error | worst | worst on a single-joint vertex |
|
|
86
|
+
| --- | --- | --- | --- | --- |
|
|
87
|
+
| bind, mesh node as the file gave it | 100.0000 | 121.2470 | 165.4281 | 165.4281 |
|
|
88
|
+
| posed at 0.5 s, the same | 100.0000 | 121.6197 | 165.5332 | — |
|
|
89
|
+
| bind, on the skin frame | 1.0000 | 0.0002 | 0.0016 | 0.0014 |
|
|
90
|
+
| posed at 0.5 s, on the skin frame | 1.0000 | 0.0001 | 0.0062 | 0.000014 |
|
|
91
|
+
| bind, skin frame and §9 | 1.0000 | 0.0000 | 0.0000 | **0** |
|
|
92
|
+
| posed at 0.5 s, skin frame and §9 | 1.0000 | 0.0001 | 0.0062 | **0.000001** |
|
|
93
|
+
|
|
94
|
+
Neither residual was the frame. The blended ones are dual-quaternion skinning declining to be a
|
|
95
|
+
linear blend, which is what it is for. The `0.0014` at bind was `dual_quat_from_m4`'s square-root
|
|
96
|
+
conditioning near the identity — pre-existing, and maximally exposed by a fold that now lands every
|
|
97
|
+
joint *on* the identity. Plan §9 moved the conversion onto `quat_from_m3`'s branch-picking form,
|
|
98
|
+
which was already in the tree and used by nothing, and it went to zero.
|
|
99
|
+
|
|
100
|
+
Confirmed on an NVIDIA RTX 4090: the frame correction fires and logs, `?skin=bind` draws a full-size
|
|
101
|
+
character, `?skin=animated` dances, and §9's WGSL — which nothing offline type-checks — compiles.
|
|
102
|
+
|
|
103
|
+
So the modes now read the other way round:
|
|
104
|
+
|
|
105
|
+
| `?skin=` | what it should be |
|
|
106
|
+
| --- | --- |
|
|
107
|
+
| `off` | a 1.7 cm figure on her side — nothing deforms the clone, so the mesh node's own transform is all there is. Unchanged by the fix, and still the control for "is the geometry upload sound". |
|
|
108
|
+
| `bind` | a full-size character standing in bind pose, and `await diagnose()` clean |
|
|
109
|
+
| `animated` | dancing, with the traced and rasterized silhouettes the same silhouette |
|
|
110
|
+
|
|
111
|
+
## The switches
|
|
112
|
+
|
|
113
|
+
- **`?skin=off`** — no skin registered. A `SkinnedMesh` still gets a vertex clone
|
|
114
|
+
(`GPUMeshSkinningContext#obtain_geometry_index` allocates one for every skinned mesh), but nothing
|
|
115
|
+
deforms it, so what is drawn is the memcpy of the source: the rest pose under the mesh node's own
|
|
116
|
+
transform. For Michelle a *correct* result is a 1.7 cm figure on her side. Soup here would be the
|
|
117
|
+
geometry upload — the meshlet copy or its address patch — and nothing to do with skinning.
|
|
118
|
+
- **`?skin=bind`** — the skin registered, no clip. `await diagnose()` in the console reads the
|
|
119
|
+
clone's vertices back off the GPU and holds them against `inverse(mesh.global) × rest`, which is
|
|
120
|
+
what the bind pose must produce. It reports which vertices are wrong and in what shape: a
|
|
121
|
+
vertex-slot range says the workgroup stride loop, whole meshlets say addressing, and a uniform
|
|
122
|
+
factor says this bug. It checks the clone's meshlet records address a region of their own first —
|
|
123
|
+
a clone still pointing at the source's vertex data would be deforming the shared rest pose in
|
|
124
|
+
place. Since the frame is now the skin's, `inverse(mesh.global)` is the identity here and the
|
|
125
|
+
clone should come back holding the source's own positions.
|
|
126
|
+
- **`?skin=animated`** — the file's dance, as `skinned_blas_refit` runs it.
|
|
127
|
+
- **Path tracer** — off by default. The rasterizer reads the deformed vertices directly, with no
|
|
128
|
+
tree in the way, so it is the reading to establish the answer on: soup in *both* is the vertices,
|
|
129
|
+
soup in the tracer alone is the tree.
|
|
130
|
+
|
|
131
|
+
## Why through `GLTFSceneBundleAssetLoader`
|
|
132
|
+
|
|
133
|
+
`skinned_blas_refit` calls `load_gltf` directly and parses the file once per character. The asset
|
|
134
|
+
loader is what the game and the editor register, and it hands out **the same bundle** to every
|
|
135
|
+
caller — so a second character is not a second parse, it is `instantiate_scene_bundle`: its own node
|
|
136
|
+
tree, its own skins bound to that tree's joints, its own clips retargeted onto it, sharing the
|
|
137
|
+
geometry and the inverse-bind matrices. Running the repro on that path is what makes a fix found
|
|
138
|
+
here a fix for the engine rather than for one prototype page.
|
|
139
|
+
|
|
140
|
+
## Assets
|
|
141
|
+
|
|
142
|
+
`/test_assets/Michelle.glb`, gitignored and unpublished — see `test_assets/README.md`. The two
|
|
143
|
+
probes skip themselves when it is absent.
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<title>meep — skinned mesh soup</title>
|
|
6
|
+
<style>
|
|
7
|
+
:root {
|
|
8
|
+
color-scheme: dark;
|
|
9
|
+
--panel: rgba(20, 22, 26, 0.88);
|
|
10
|
+
--line: #2b3038;
|
|
11
|
+
--text: #d6dae0;
|
|
12
|
+
--muted: #8b939f;
|
|
13
|
+
--pass: #55c46a;
|
|
14
|
+
--fail: #ff6b6b;
|
|
15
|
+
--warn: #e5b54a;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
* { box-sizing: border-box; }
|
|
19
|
+
|
|
20
|
+
html, body { margin: 0; height: 100%; overflow: hidden; background: #0d0f12; }
|
|
21
|
+
|
|
22
|
+
/* body carries tabindex so the camera controller's keyboard device has something focusable
|
|
23
|
+
to listen on; the focus ring around the whole viewport is not wanted */
|
|
24
|
+
body:focus { outline: none; }
|
|
25
|
+
|
|
26
|
+
#view { display: block; width: 100vw; height: 100vh; }
|
|
27
|
+
|
|
28
|
+
#panel {
|
|
29
|
+
position: fixed;
|
|
30
|
+
top: 12px;
|
|
31
|
+
left: 12px;
|
|
32
|
+
width: 340px;
|
|
33
|
+
padding: 12px 14px;
|
|
34
|
+
background: var(--panel);
|
|
35
|
+
border: 1px solid var(--line);
|
|
36
|
+
border-radius: 8px;
|
|
37
|
+
backdrop-filter: blur(6px);
|
|
38
|
+
color: var(--text);
|
|
39
|
+
font: 13px/1.5 system-ui, -apple-system, Segoe UI, sans-serif;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
#panel h1 { font-size: 13px; font-weight: 600; margin: 0 0 8px; }
|
|
43
|
+
|
|
44
|
+
label { display: flex; align-items: center; gap: 8px; padding: 3px 0; cursor: pointer; }
|
|
45
|
+
label input { margin: 0; }
|
|
46
|
+
label .hint { color: var(--muted); font-size: 11.5px; }
|
|
47
|
+
|
|
48
|
+
hr { border: 0; border-top: 1px solid var(--line); margin: 10px 0; }
|
|
49
|
+
|
|
50
|
+
button, select, input[type=number] {
|
|
51
|
+
font: inherit;
|
|
52
|
+
padding: 5px 9px;
|
|
53
|
+
border-radius: 6px;
|
|
54
|
+
border: 1px solid var(--line);
|
|
55
|
+
background: #1c1f25;
|
|
56
|
+
color: var(--text);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
button { cursor: pointer; }
|
|
60
|
+
button:hover:not(:disabled) { border-color: #3d4552; }
|
|
61
|
+
button:disabled { opacity: 0.45; cursor: default; }
|
|
62
|
+
|
|
63
|
+
select { width: 100%; cursor: pointer; }
|
|
64
|
+
|
|
65
|
+
.row { display: flex; gap: 6px; align-items: center; margin-top: 6px; }
|
|
66
|
+
.row button { flex: 0 0 auto; }
|
|
67
|
+
|
|
68
|
+
#character_count {
|
|
69
|
+
flex: 1 1 auto;
|
|
70
|
+
min-width: 0;
|
|
71
|
+
font: 12px ui-monospace, SFMono-Regular, Consolas, monospace;
|
|
72
|
+
background: #14161a;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
#status { font-size: 12px; color: var(--muted); word-break: break-word; }
|
|
76
|
+
#status.pass { color: var(--pass); }
|
|
77
|
+
#status.fail { color: var(--fail); }
|
|
78
|
+
#status.warn { color: var(--warn); }
|
|
79
|
+
|
|
80
|
+
#stats {
|
|
81
|
+
margin-top: 6px;
|
|
82
|
+
font: 12px ui-monospace, SFMono-Regular, Consolas, monospace;
|
|
83
|
+
color: var(--text);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
p.note { margin: 8px 0 0; font-size: 11.5px; color: var(--muted); }
|
|
87
|
+
|
|
88
|
+
code { font: 11.5px ui-monospace, SFMono-Regular, Consolas, monospace; color: var(--text); }
|
|
89
|
+
|
|
90
|
+
dl { margin: 6px 0 0; font-size: 11.5px; color: var(--muted); }
|
|
91
|
+
dt { color: var(--text); font-weight: 600; margin-top: 6px; }
|
|
92
|
+
dd { margin: 0; }
|
|
93
|
+
</style>
|
|
94
|
+
</head>
|
|
95
|
+
<body tabindex="0">
|
|
96
|
+
|
|
97
|
+
<canvas id="view"></canvas>
|
|
98
|
+
|
|
99
|
+
<div id="panel">
|
|
100
|
+
<h1>Skinned mesh soup</h1>
|
|
101
|
+
|
|
102
|
+
<p class="note">
|
|
103
|
+
The same character <code>skinned_blas_refit</code> draws, loaded through
|
|
104
|
+
<code>GLTFSceneBundleAssetLoader</code> — one bundle, one instance per placement — with the
|
|
105
|
+
pieces that could be making soup on switches.
|
|
106
|
+
</p>
|
|
107
|
+
<p class="note">
|
|
108
|
+
<strong>The answer, for the record.</strong> <code>chunk_skin_blend_mesh_local</code> needs
|
|
109
|
+
<code>inverse(mesh.global) × joint.global × inverse_bind</code> to be rigid; for this file
|
|
110
|
+
it carried a uniform <strong>100×</strong>, and a dual quaternion cannot carry a scale.
|
|
111
|
+
<code>register_skin</code> now puts the mesh on the skin's own bind transform, so the fold
|
|
112
|
+
is the identity at bind and a rigid displacement under a clip. The README and
|
|
113
|
+
<code>SKINNING_FRAME_PLAN.md</code> have the measurement; this page is where it is confirmed
|
|
114
|
+
on a device.
|
|
115
|
+
</p>
|
|
116
|
+
|
|
117
|
+
<hr>
|
|
118
|
+
|
|
119
|
+
<label for="skin_mode"><span>How the skin is driven</span></label>
|
|
120
|
+
<select id="skin_mode">
|
|
121
|
+
<option value="off">off — no skin registered, nothing deforms the clone</option>
|
|
122
|
+
<option value="bind">bind — skin registered, no clip</option>
|
|
123
|
+
<option value="animated">animated — the file's dance, playing</option>
|
|
124
|
+
</select>
|
|
125
|
+
|
|
126
|
+
<dl>
|
|
127
|
+
<dt>off</dt>
|
|
128
|
+
<dd>
|
|
129
|
+
What is drawn is the clone's memcpy of the source: the rest pose under the mesh node's
|
|
130
|
+
own transform. Michelle's carries the file's centimetre scale and its up-axis rotation,
|
|
131
|
+
so <em>correct</em> here is a 1.7 cm figure on her side. Soup here is the geometry
|
|
132
|
+
upload, not skinning.
|
|
133
|
+
</dd>
|
|
134
|
+
<dt>bind</dt>
|
|
135
|
+
<dd>
|
|
136
|
+
The skinning pass runs every frame over a pose that never moves — and that pose has an
|
|
137
|
+
answer the CPU knows exactly. <code>await diagnose()</code> reads the clone's vertices
|
|
138
|
+
back and says which are wrong, and whether the wrong ones are a vertex-slot range (the
|
|
139
|
+
workgroup stride loop) or whole meshlets (addressing). Should be a full-size character
|
|
140
|
+
standing still, and a clean report.
|
|
141
|
+
</dd>
|
|
142
|
+
<dt>animated</dt>
|
|
143
|
+
<dd>
|
|
144
|
+
Soup only in this mode is the pose: the joint matrices, their range, or the order the
|
|
145
|
+
skin's joints resolved in. Should be dancing, with the traced and rasterized silhouettes
|
|
146
|
+
the same silhouette.
|
|
147
|
+
</dd>
|
|
148
|
+
</dl>
|
|
149
|
+
|
|
150
|
+
<hr>
|
|
151
|
+
|
|
152
|
+
<label>
|
|
153
|
+
<input type="checkbox" id="path_tracing">
|
|
154
|
+
<span>Path tracer <span class="hint">— traverses the BLAS</span></span>
|
|
155
|
+
</label>
|
|
156
|
+
<p class="note">
|
|
157
|
+
Off by default. The rasterizer reads the deformed vertices directly, with no tree in the
|
|
158
|
+
way, so it is the reading to establish the answer on first: soup in <em>both</em> is the
|
|
159
|
+
vertices, soup in the tracer alone is the tree.
|
|
160
|
+
</p>
|
|
161
|
+
|
|
162
|
+
<div class="row">
|
|
163
|
+
<button id="animation">Freeze pose</button>
|
|
164
|
+
</div>
|
|
165
|
+
|
|
166
|
+
<hr>
|
|
167
|
+
|
|
168
|
+
<div class="row">
|
|
169
|
+
<input type="number" id="character_count" value="1" min="1" max="64" step="1">
|
|
170
|
+
<button id="respawn">Characters</button>
|
|
171
|
+
</div>
|
|
172
|
+
<p class="note">
|
|
173
|
+
One parse whatever the count — the asset hands out the model itself, and each placement gets
|
|
174
|
+
its own node tree, skins and clips from <code>instantiate_scene_bundle</code>.
|
|
175
|
+
</p>
|
|
176
|
+
|
|
177
|
+
<hr>
|
|
178
|
+
|
|
179
|
+
<div id="status"></div>
|
|
180
|
+
<div id="stats"></div>
|
|
181
|
+
|
|
182
|
+
<p class="note">
|
|
183
|
+
Every switch is in the url — <code>?count=1&skin=bind&trace=1</code> — so a state
|
|
184
|
+
worth showing someone can be sent as a link.
|
|
185
|
+
</p>
|
|
186
|
+
</div>
|
|
187
|
+
|
|
188
|
+
<script type="module">
|
|
189
|
+
import { start } from './main.js';
|
|
190
|
+
|
|
191
|
+
start().catch(error => {
|
|
192
|
+
const status = document.getElementById('status');
|
|
193
|
+
|
|
194
|
+
status.className = 'fail';
|
|
195
|
+
status.textContent = String(error?.message ?? error);
|
|
196
|
+
|
|
197
|
+
console.error(error);
|
|
198
|
+
});
|
|
199
|
+
</script>
|
|
200
|
+
|
|
201
|
+
</body>
|
|
202
|
+
</html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../../../src/shade/playground/skinned_mesh_soup/main.js"],"names":[],"mappings":"AAiQA;;;;GAIG;AACH,yBAFa,QAAQ,IAAI,CAAC,CAkTzB"}
|