reze-engine 0.16.1 → 0.16.3

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.
Files changed (88) hide show
  1. package/README.md +139 -284
  2. package/dist/engine.d.ts.map +1 -1
  3. package/dist/engine.js +5 -5
  4. package/dist/model.d.ts +4 -1
  5. package/dist/model.d.ts.map +1 -1
  6. package/dist/model.js +14 -1
  7. package/dist/physics/body.d.ts +1 -0
  8. package/dist/physics/body.d.ts.map +1 -1
  9. package/dist/physics/body.js +2 -0
  10. package/dist/physics/constraint.d.ts +8 -1
  11. package/dist/physics/constraint.d.ts.map +1 -1
  12. package/dist/physics/constraint.js +13 -1
  13. package/dist/physics/physics.d.ts +15 -1
  14. package/dist/physics/physics.d.ts.map +1 -1
  15. package/dist/physics/physics.js +413 -48
  16. package/dist/physics/solver.d.ts.map +1 -1
  17. package/dist/physics/solver.js +223 -39
  18. package/dist/physics/types.d.ts +2 -1
  19. package/dist/physics/types.d.ts.map +1 -1
  20. package/dist/physics/types.js +5 -0
  21. package/dist/physics/world.d.ts +3 -0
  22. package/dist/physics/world.d.ts.map +1 -1
  23. package/dist/physics/world.js +31 -4
  24. package/dist/physics-debug.d.ts +30 -0
  25. package/dist/physics-debug.d.ts.map +1 -0
  26. package/dist/physics-debug.js +526 -0
  27. package/dist/pmx-loader.d.ts +2 -0
  28. package/dist/pmx-loader.d.ts.map +1 -1
  29. package/dist/pmx-loader.js +37 -20
  30. package/dist/shaders/materials/body.d.ts +1 -1
  31. package/dist/shaders/materials/body.d.ts.map +1 -1
  32. package/dist/shaders/materials/body.js +90 -85
  33. package/dist/shaders/materials/cloth_rough.d.ts +1 -1
  34. package/dist/shaders/materials/cloth_rough.d.ts.map +1 -1
  35. package/dist/shaders/materials/cloth_rough.js +1 -1
  36. package/dist/shaders/materials/cloth_smooth.d.ts +1 -1
  37. package/dist/shaders/materials/cloth_smooth.d.ts.map +1 -1
  38. package/dist/shaders/materials/cloth_smooth.js +2 -2
  39. package/dist/shaders/materials/common.d.ts +1 -1
  40. package/dist/shaders/materials/common.d.ts.map +1 -1
  41. package/dist/shaders/materials/common.js +10 -0
  42. package/dist/shaders/materials/default.d.ts +1 -1
  43. package/dist/shaders/materials/default.d.ts.map +1 -1
  44. package/dist/shaders/materials/default.js +37 -32
  45. package/dist/shaders/materials/eye.d.ts +1 -1
  46. package/dist/shaders/materials/eye.d.ts.map +1 -1
  47. package/dist/shaders/materials/eye.js +40 -35
  48. package/dist/shaders/materials/face.d.ts +1 -1
  49. package/dist/shaders/materials/face.d.ts.map +1 -1
  50. package/dist/shaders/materials/face.js +90 -85
  51. package/dist/shaders/materials/hair.d.ts +1 -1
  52. package/dist/shaders/materials/hair.d.ts.map +1 -1
  53. package/dist/shaders/materials/hair.js +85 -75
  54. package/dist/shaders/materials/metal.d.ts +1 -1
  55. package/dist/shaders/materials/metal.d.ts.map +1 -1
  56. package/dist/shaders/materials/metal.js +1 -1
  57. package/dist/shaders/materials/stockings.d.ts +1 -1
  58. package/dist/shaders/materials/stockings.d.ts.map +1 -1
  59. package/dist/shaders/materials/stockings.js +1 -1
  60. package/dist/shaders/passes/physics-debug.d.ts +2 -0
  61. package/dist/shaders/passes/physics-debug.d.ts.map +1 -0
  62. package/dist/shaders/passes/physics-debug.js +69 -0
  63. package/package.json +1 -1
  64. package/src/engine.ts +36 -21
  65. package/src/model.ts +18 -1
  66. package/src/physics/body.ts +5 -0
  67. package/src/physics/constraint.ts +35 -6
  68. package/src/physics/physics.ts +423 -56
  69. package/src/physics/solver.ts +213 -38
  70. package/src/physics/types.ts +10 -2
  71. package/src/physics/world.ts +33 -4
  72. package/src/pmx-loader.ts +38 -20
  73. package/src/shaders/materials/body.ts +97 -92
  74. package/src/shaders/materials/cloth_rough.ts +1 -1
  75. package/src/shaders/materials/cloth_smooth.ts +2 -2
  76. package/src/shaders/materials/common.ts +10 -0
  77. package/src/shaders/materials/default.ts +45 -40
  78. package/src/shaders/materials/eye.ts +48 -43
  79. package/src/shaders/materials/face.ts +97 -92
  80. package/src/shaders/materials/hair.ts +92 -82
  81. package/src/shaders/materials/metal.ts +1 -1
  82. package/src/shaders/materials/stockings.ts +1 -1
  83. package/dist/gpu-profile.d.ts +0 -19
  84. package/dist/gpu-profile.d.ts.map +0 -1
  85. package/dist/gpu-profile.js +0 -120
  86. package/dist/physics/profile.d.ts +0 -18
  87. package/dist/physics/profile.d.ts.map +0 -1
  88. package/dist/physics/profile.js +0 -44
package/README.md CHANGED
@@ -1,52 +1,70 @@
1
1
  # Reze Engine
2
2
 
3
- **Zero-runtime-dependency** WebGPU engine for real-time MMD/PMX rendering. Renderer, animation, IK, and physics all in TypeScript.
3
+ **Zero-runtime-dependency** WebGPU engine for real-time MMD/PMX rendering — renderer, animation, IK, and physics, all in TypeScript.
4
4
 
5
5
  ![screenshot](./screenshot.png)
6
6
 
7
- ## Install
8
-
9
7
  ```bash
10
8
  npm install reze-engine
11
9
  ```
12
10
 
13
11
  ## Features
14
12
 
15
- - **Anime/MMD-style hybrid renderer** — toon-ramp NPR diffuse mixed with PBR GGX specular (multi-scatter + LTC energy compensation)
16
- - **Per-material presets** `face` / `hair` / `body` / `eye` / `stockings` / `metal` / `cloth_smooth` / `cloth_rough` / `default`, assigned by material name
17
- - **HDR pipeline** with bloom mip pyramid, Filmic tone mapping, 4× MSAA, tile-memory-friendly on Apple Silicon
18
- - **Alpha-hashed transparency** (Wyman & McGuire 2017) for self-overlapping transparent meshes like stockings
19
- - **Screen-space outlines** on opaque + transparent materials
20
- - **See-through hair over eyes** — stencil-gated MMD post-alpha-eye so eyes read at 75% through hair silhouettes
21
- - **In-house TS physics** — sequential-impulse 6DOF spring solver, sphere/box/capsule contacts, split-impulse position correction, fixed-step substepping
22
- - **VMD animation** with IK solver
23
- - **Orbit camera** with bone-follow mode
24
- - **GPU picking** (double-click/tap)
25
- - **Ground plane** with PCF shadow mapping
26
- - **Multi-model support**
27
-
28
- ## Usage
13
+ - Anime/MMD **hybrid renderer** — toon-ramp NPR over a Principled GGX BSDF, mixed per material
14
+ - **9 per-material presets** assigned by material name (`face` / `hair` / `body` / `eye` / `stockings` / `metal` / `cloth_smooth` / `cloth_rough` / `default`)
15
+ - **HDR pipeline** bloom, Filmic tone mapping, 4× MSAA, Apple-TBDR-friendly targets
16
+ - **In-house TS physics** sequential-impulse rigid bodies for PMX rigs, no external dependency
17
+ - **VMD animation** with MMD IK, morphs (GPU compute path), and VMD export
18
+ - **Interactive editing** — GPU picking, transform gizmo, bone/material selection
19
+ - Orbit camera with bone-follow, ground + PCF shadows, multi-model
20
+
21
+ See [Physics](#physics) and [Rendering](#rendering) for the internals.
22
+
23
+ ## Codebase map
24
+
25
+ ```
26
+ engine/src/
27
+ engine.ts Engine: device/context, render loop, all passes & pipelines,
28
+ per-model GPU resources, picking, gizmo (entry point)
29
+ model.ts Model: skeleton, 4-bone skinning, morphs (CPU + GPU compute),
30
+ animation state, drives IK; per-frame update()
31
+ animation.ts AnimationClip, VMD bezier interpolation, playback/priority
32
+ ik-solver.ts MMD-style CCD IK (angle limits, solve-axis specialization)
33
+ camera.ts Orbit camera (alpha/beta/radius), bone-follow, mouse/touch
34
+ math.ts Vec3 / Quat / Mat4 (zero-alloc *Into variants for hot paths)
35
+ pmx-loader.ts PMX parser: mesh, bones, morphs, rigid bodies, joints
36
+ vmd-loader.ts VMD motion parser · vmd-writer.ts VMD export (Shift-JIS)
37
+ asset-reader.ts URL + local-folder asset resolution · folder-upload.ts
38
+ index.ts public exports
39
+
40
+ physics/ in-house rigid-body solver (~2.5k lines)
41
+ physics.ts RezePhysics: bone↔body sync, fixed-step accumulator + interpolation
42
+ solver.ts sequential-impulse PGS (joint + contact rows)
43
+ contact.ts narrowphase (analytical sphere/box/capsule pairs) + contact pool
44
+ constraint.ts 6DOF spring joints · world.ts broadphase + step
45
+ body.ts SoA rigid-body store · types.ts
46
+
47
+ shaders/
48
+ materials/ nodes.ts (Blender-node WGSL library) + common.ts (bindings, skinning VS)
49
+ + one file per preset (face, hair, body, eye, stockings, …)
50
+ passes/ shadow, morph (GPU vertex-morph compute), bloom, composite (Filmic),
51
+ outline, selection, gizmo, pick, ground, mipmap
52
+ ```
53
+
54
+ ## Quick start
29
55
 
30
56
  ```javascript
31
- import { Engine, Vec3 } from "reze-engine";
57
+ import { Engine, Vec3 } from "reze-engine"
32
58
 
33
59
  const engine = new Engine(canvas, {
34
- world: { color: new Vec3(0.4, 0.49, 0.65), strength: 1.0 },
35
- sun: {
36
- color: new Vec3(1, 1, 1),
37
- strength: 2.0,
38
- direction: new Vec3(0, -0.5, 1),
39
- },
40
- bloom: {
41
- color: new Vec3(0.9, 0.1, 0.8),
42
- intensity: 0.05,
43
- threshold: 0.5,
44
- }
60
+ world: { color: new Vec3(0.4, 0.49, 0.65), strength: 1.0 }, // environment light
61
+ sun: { color: new Vec3(1, 1, 1), strength: 2.0, direction: new Vec3(0, -0.5, 1) },
62
+ bloom: { color: new Vec3(0.9, 0.1, 0.8), intensity: 0.05, threshold: 0.5 },
45
63
  camera: { distance: 31.5, target: new Vec3(0, 11.5, 0) }, // MMD units (1 unit = 8 cm)
46
- });
47
- await engine.init();
64
+ })
65
+ await engine.init()
48
66
 
49
- const model = await engine.loadModel("hero", "/models/hero/hero.pmx");
67
+ const model = await engine.loadModel("hero", "/models/hero/hero.pmx")
50
68
 
51
69
  // Map PMX material names to NPR presets (unlisted names fall back to `default`).
52
70
  engine.setMaterialPresets("hero", {
@@ -54,341 +72,178 @@ engine.setMaterialPresets("hero", {
54
72
  body: ["skin"],
55
73
  hair: ["hair_f"],
56
74
  eye: ["eye"],
57
- cloth_smooth: ["shirt", "shorts", "dress", "shoes"],
58
- cloth_rough: ["jacket", "pants"],
75
+ cloth_smooth: ["shirt", "dress", "shoes"],
76
+ cloth_rough: ["jacket"],
59
77
  stockings: ["stockings"],
60
- metal: ["metal01", "earring"],
61
- });
78
+ metal: ["earring"],
79
+ })
62
80
 
63
- await model.loadVmd("idle", "/animations/idle.vmd");
64
- model.show("idle");
65
- model.play();
81
+ await model.loadVmd("idle", "/animations/idle.vmd")
82
+ model.show("idle")
83
+ model.play()
66
84
 
67
- engine.setCameraFollow(model, "センター", new Vec3(0, 3.5, 0));
68
- engine.addGround({ width: 160, height: 160 });
69
- engine.runRenderLoop();
85
+ engine.setCameraFollow(model, "センター", new Vec3(0, 3.5, 0))
86
+ engine.addGround({ width: 160, height: 160 })
87
+ engine.runRenderLoop()
70
88
  ```
71
89
 
72
90
  ## API
73
91
 
74
- One WebGPU **Engine** per page (singleton after `init()`). Load models via URL **or** from a user-selected folder (see [Local folder uploads](#local-folder-uploads-browser)).
92
+ One WebGPU **Engine** per page (singleton after `init()`). Models load by URL **or** from a user-selected folder ([below](#local-folder-uploads-browser)).
75
93
 
76
94
  ### Engine
77
95
 
78
96
  ```javascript
79
97
  engine.init()
80
- engine.loadModel(name, path)
81
- engine.loadModel(name, { files, pmxFile? }) // folder upload — see below
82
- engine.getModel(name)
83
- engine.getModelNames()
84
- engine.removeModel(name)
98
+ engine.loadModel(name, path) // or ({ files, pmxFile? }) for folder upload
99
+ engine.getModel(name) / getModelNames() / removeModel(name)
85
100
 
86
101
  engine.setMaterialPresets(name, presetMap) // assign NPR presets by material name
87
- engine.setMaterialVisible(name, material, visible)
88
- engine.toggleMaterialVisible(name, material)
89
- engine.isMaterialVisible(name, material)
102
+ engine.setMaterialVisible(name, material, visible) / toggleMaterialVisible / isMaterialVisible
90
103
 
91
104
  engine.setIKEnabled(enabled)
92
105
  engine.setPhysicsEnabled(enabled)
93
- engine.resetPhysics() // re-pose bodies from animation and zero velocities call when physics explodes
106
+ engine.resetPhysics() // re-pose bodies from animation + zero velocities (call if physics explodes)
94
107
 
95
- engine.setCameraFollow(model, bone?, offset?)
96
- engine.setCameraFollow(null)
97
- engine.setCameraTarget(vec3)
98
- engine.setCameraDistance(d)
99
- engine.setCameraAlpha(a)
100
- engine.setCameraBeta(b)
108
+ engine.setCameraFollow(model, bone?, offset?) / setCameraFollow(null)
109
+ engine.setCameraTarget(vec3) / setCameraDistance(d) / setCameraAlpha(a) / setCameraBeta(b)
101
110
 
111
+ engine.setWorld({ color?, strength? }) / setSun({ color?, strength?, direction? }) // runtime lighting
102
112
  engine.addGround(options?)
103
- engine.runRenderLoop(callback?)
104
- engine.stopRenderLoop()
105
- engine.getStats()
113
+ engine.runRenderLoop(callback?) / stopRenderLoop()
114
+ engine.getStats() // fps + smoothness metrics (frameTimeMax, fps1PercentLow, jitter)
106
115
  engine.dispose()
107
116
  ```
108
117
 
109
- ### Local folder uploads (browser)
110
-
111
- Use a hidden `<input type="file" webkitdirectory multiple>` (or drag/drop) and pass the resulting `FileList` or `File[]` into the engine. Textures resolve relative to the chosen PMX file inside that tree.
112
-
113
- **Important:** read `input.files` into a normal array **before** setting `input.value = ""`. The browser's `FileList` is _live_ — clearing the input empties it.
114
-
115
- 1. **`parsePmxFolderInput(fileList)`** — returns a tagged result (`empty` | `not_directory` | `no_pmx` | `single` | `multiple`). For `single`, you already have `files` and `pmxFile`. For `multiple`, show a picker (dropdown) of `pmxRelativePaths`, then resolve with **`pmxFileAtRelativePath(files, path)`**.
116
- 2. **`engine.loadModel(name, { files, pmxFile })`** — `pmxFile` selects which `.pmx` when the folder contains several.
117
-
118
- ```javascript
119
- import {
120
- Engine,
121
- parsePmxFolderInput,
122
- pmxFileAtRelativePath,
123
- } from "reze-engine";
124
-
125
- // In <input onChange>:
126
- const picked = parsePmxFolderInput(e.target.files);
127
- e.target.value = "";
128
-
129
- if (picked.status === "single") {
130
- const model = await engine.loadModel("myModel", {
131
- files: picked.files,
132
- pmxFile: picked.pmxFile,
133
- });
134
- }
135
-
136
- if (picked.status === "multiple") {
137
- // Let the user choose `chosenPath` from picked.pmxRelativePaths, then:
138
- const pmxFile = pmxFileAtRelativePath(picked.files, chosenPath);
139
- const model = await engine.loadModel("myModel", {
140
- files: picked.files,
141
- pmxFile,
142
- });
143
- }
144
- ```
145
-
146
- VMD and other assets still load by URL when the path starts with `/` or `http(s):`; relative paths are resolved against the PMX directory inside the upload.
118
+ **Options** Blender-style scene config: `world` = environment lighting, `sun` = directional lamp (`direction` points from sun into the scene), `camera` = framing (`fov` in radians). Callbacks: `onRaycast`, `onGizmoDrag`. The shadow map is cast from `sun.direction` — the same vector the shader lights with — so shading and cast shadows stay coupled.
147
119
 
148
120
  ### Model
149
121
 
150
122
  ```javascript
151
- await model.loadVmd(name, url)
152
- model.loadClip(name, clip)
123
+ await model.loadVmd(name, url) / model.loadClip(name, clip)
153
124
  model.show(name)
154
- model.play(name)
155
- model.play(name, { priority: 8 }) // higher number = higher priority (0 default/lowest)
156
- model.play(name, { loop: true }) // repeat until stop/pause or another play
157
- model.pause()
158
- model.stop()
159
- model.seek(time)
160
- model.getAnimationProgress()
161
- model.getClip(name)
162
- model.exportVmd(name) // returns ArrayBuffer
163
-
164
- model.rotateBones({ 首: quat, 頭: quat }, ms?)
165
- model.moveBones({ センター: vec3 }, ms?)
125
+ model.play(name, { priority?, loop? }) // priority: higher wins when clips compete (0 = default)
126
+ model.pause() / stop() / seek(time)
127
+ model.getAnimationProgress() // { current, duration (s), playing, paused, looping, }
128
+ model.exportVmd(name) // → ArrayBuffer (Shift-JIS bone/morph names)
129
+
130
+ model.rotateBones({ 首: quat }, ms?) / moveBones({ センター: vec3 }, ms?)
166
131
  model.setMorphWeight(name, weight, ms?)
167
- model.resetAllBones()
168
- model.resetAllMorphs()
132
+ model.resetAllBones() / resetAllMorphs()
169
133
  model.getBoneWorldPosition(name)
170
-
171
- // Direct bone local-transform accessors (used by interactive gizmo drag).
172
- // Readers return the live runtime state; snapshot with .clone() if needed.
173
- model.getBoneLocalRotation(boneIndex)
174
- model.getBoneLocalTranslation(boneIndex)
175
-
176
- // Raw absolute-local translation write. NOT the same as moveBones({ n: v }, 0)
177
- // — moveBones treats input as VMD-relative and converts. Use this when you
178
- // already have the final local translation. For rotation, rotateBones(..., 0)
179
- // is already an instant-write equivalent.
180
- model.setBoneLocalTranslation(boneIndex, vec3)
181
-
182
- // Freeze clip re-sampling so direct writes persist across frames. Auto-cleared
183
- // on play() / seek(). See "Interactive pose editing" below.
184
- model.setClipApplySuspended(suspended: boolean)
185
- model.isClipApplySuspended()
186
- ```
187
-
188
- #### Animation data
189
-
190
- `AnimationClip` holds keyframes only: bone/morph tracks keyed by `frame`, and `frameCount` (last keyframe index). Time advances at fixed `FPS` (see package export `FPS`, default 30).
191
-
192
- #### VMD Export
193
-
194
- `model.exportVmd(name)` serialises a loaded clip back to the VMD binary format and returns an `ArrayBuffer`. Bone and morph names are Shift-JIS encoded for compatibility with standard MMD tools.
195
-
196
- ```javascript
197
- const buffer = model.exportVmd("idle");
198
- const blob = new Blob([buffer], { type: "application/octet-stream" });
199
- const link = document.createElement("a");
200
- link.href = URL.createObjectURL(blob);
201
- link.download = "idle.vmd";
202
- link.click();
203
134
  ```
204
135
 
205
- #### Playback
206
-
207
- Call `model.play(name, options?)` to start or switch motion. `loop: true` makes the playhead wrap at the end of the clip until you stop, pause, or call `play` with something else. `priority` chooses which request wins when several clips compete.
136
+ `AnimationClip` holds keyframes only (bone/morph tracks keyed by `frame`, plus `frameCount`); time advances at fixed `FPS` (exported, default 30).
208
137
 
209
- #### Progress
138
+ ### Local folder uploads (browser)
210
139
 
211
- `getAnimationProgress()` reports `current` and `duration` in seconds, plus `playing`, `paused`, `looping`, and related fields.
140
+ Feed a `<input type="file" webkitdirectory>` `FileList` (or drag/drop) into the engine; textures resolve relative to the chosen PMX inside that tree.
212
141
 
213
- ### Engine Options
142
+ > **Gotcha:** copy `input.files` into an array **before** `input.value = ""` — the `FileList` is live and clearing the input empties it.
214
143
 
215
- Blender-style scene config `world` = environment lighting, `sun` = the directional lamp, `camera` = view framing.
144
+ `parsePmxFolderInput(fileList)` returns a tagged result; for `single` you get `{ files, pmxFile }` directly, for `multiple` show a picker over `pmxRelativePaths` and resolve with `pmxFileAtRelativePath(files, path)`. Then:
216
145
 
217
146
  ```javascript
218
- {
219
- world: {
220
- color: Vec3, // World > Surface > Color (linear scene-referred)
221
- strength: number, // World > Surface > Strength
222
- },
223
- sun: {
224
- color: Vec3, // Light > Color
225
- strength: number, // Light > Strength (Blender units)
226
- direction: Vec3, // direction light travels (points from sun into the scene)
227
- },
228
- camera: {
229
- distance: number,
230
- target: Vec3,
231
- fov: number, // radians
232
- },
233
- onRaycast: (modelName, material, bone, screenX, screenY) => void,
234
- onGizmoDrag: (event: GizmoDragEvent) => void,
235
- }
147
+ const picked = parsePmxFolderInput(e.target.files)
148
+ e.target.value = ""
149
+ if (picked.status === "single") await engine.loadModel("m", { files: picked.files, pmxFile: picked.pmxFile })
236
150
  ```
237
151
 
238
- The shadow map is cast from `sun.direction` same vector the shader lights with so visible shading and cast shadows stay coupled.
239
-
240
- `engine.setWorld({ color?, strength? })` and `engine.setSun({ color?, strength?, direction? })` update lighting at runtime; changing `sun.direction` refreshes the shadow VP on the next frame.
152
+ VMD and other assets still load by URL when the path starts with `/` or `http(s):`; relative paths resolve against the PMX directory.
241
153
 
242
154
  ### Interactive pose editing
243
155
 
244
- Dblclick picks a bone or material; a transform gizmo (rings + axes, local-axis aligned) drags the selection. The engine does NOT write to the skeleton on its own — it fires a callback with the computed target local transform and the host picks a write policy (runtime override, tween, clip keyframe edit).
245
-
246
- **Pick callback.** Fires on dblclick. `modelName` is `""` when the click missed the mesh. `material` and `bone` are both resolved for every hit (per-triangle dominant-joint from the GPU pick), so a single handler can serve both material-mode and bone-mode toggles:
247
-
248
- ```javascript
249
- onRaycast: (modelName, material, bone, screenX, screenY) => { ... }
156
+ Double-click picks a bone or material (per-triangle dominant-joint from the GPU pick, so one handler serves both modes); a local-axis transform gizmo drags it. **The engine only reports — it never writes the skeleton itself**, so the host chooses the write policy.
250
157
 
251
- engine.setSelectedMaterial(modelName | null, materialName | null) // orange screen-space selection outline
252
- engine.setSelectedBone(modelName | null, boneName | null) // shows the rings+axes gizmo at this bone
253
- ```
158
+ ```typescript
159
+ engine.setSelectedBone(modelName | null, boneName | null) // shows the gizmo
160
+ engine.setSelectedMaterial(modelName | null, materialName | null) // selection outline
254
161
 
255
- **Gizmo drag callback.** The engine only reports; you apply:
162
+ onRaycast: (modelName, material, bone, screenX, screenY) => { ... } // modelName "" = missed
256
163
 
257
- ```typescript
258
164
  type GizmoDragEvent = {
259
- modelName: string
260
- boneName: string
261
- boneIndex: number
262
- kind: "rotate" | "translate"
263
- localRotation: Quat // target absolute local rotation
264
- localTranslation: Vec3 // target absolute local translation
265
- phase?: "start" | "end" // undefined during drag moves
165
+ boneName: string; boneIndex: number; kind: "rotate" | "translate"
166
+ localRotation: Quat; localTranslation: Vec3 // target absolute local transform
167
+ phase?: "start" | "end" // undefined during drag moves
266
168
  }
267
169
  ```
268
170
 
269
- Fires once with `phase: "start"` on mousedown, on every mousemove (no phase), once with `phase: "end"` on mouseup. While drag is active the engine consumes any mouse input inside the gizmo's bounding sphere so camera orbit never conflicts with a drag mousedown outside the sphere routes to camera as normal.
270
-
271
- **Two write strategies**, depending on whether you keep a clip on disk:
171
+ The gizmo consumes mouse input inside its bounding sphere so drags never fight camera orbit. Apply the reported transformruntime override (below) or keyframe edit into a clip you re-`loadClip`:
272
172
 
273
173
  ```javascript
274
- // Runtime override (no clip editor — this is what web/page.tsx does).
275
174
  onGizmoDrag: (e) => {
276
175
  const model = engine.getModel(e.modelName)
277
176
  if (!model) return
278
177
  if (e.phase === "start") {
279
178
  model.pause()
280
- model.setClipApplySuspended(true) // stop clip re-sampling from wiping the edit
179
+ model.setClipApplySuspended(true)
281
180
  return
282
- }
181
+ } // stop re-sampling wiping the edit
283
182
  if (e.phase === "end") return
284
183
  if (e.kind === "rotate")
285
184
  model.rotateBones({ [e.boneName]: e.localRotation }, 0) // 0 = instant write
286
- else
287
- model.setBoneLocalTranslation(e.boneIndex, e.localTranslation)
288
- }
289
- // Pressing play/seek auto-clears the suspend flag → animation resumes, edit is lost
290
- // (expected runtime-override semantic).
291
-
292
- // Keyframe edit (animation editor — studio-style).
293
- onGizmoDrag: (e) => {
294
- if (e.phase === "start") { beginUndoGroup(); return }
295
- if (e.phase === "end") { commitUndoGroup(); return }
296
- const kf = findOrCreateKeyframe(clip, e.boneName, currentFrame)
297
- kf.rotation = e.localRotation
298
- kf.translation = e.localTranslation
299
- model.loadClip(clipName, clip)
300
- model.seek(currentTime)
301
- // The re-sampled clip now produces the edited pose — no suspend flag needed.
185
+ else model.setBoneLocalTranslation(e.boneIndex, e.localTranslation)
302
186
  }
187
+ // play()/seek() auto-clear the suspend flag (edit is lost — runtime-override semantic).
303
188
  ```
304
189
 
305
- Note the asymmetry: rotation uses `rotateBones({ name, q }, 0)` (the tween-based API reduces to an instant write when duration is 0) while translation uses `setBoneLocalTranslation(idx, v)` — `moveBones` can't be used because it converts VMD-relative input to local, and the gizmo's output is already local.
190
+ Note the asymmetry: rotation goes through `rotateBones(…, 0)`, but translation uses `setBoneLocalTranslation(idx, v)` — `moveBones` converts VMD-relativelocal, and the gizmo output is already local.
306
191
 
307
192
  ## Physics
308
193
 
309
- In-house sequential-impulse rigid-body solver, no external physics dependency. Targets PMX rigs (sphere / box / capsule colliders, 6DOF spring joints) at quality comparable to Bullet's defaults in ~1.5k lines of TypeScript.
194
+ In-house sequential-impulse rigid-body solver for PMX rigs (sphere / box / capsule colliders, 6DOF spring joints), ~2.5k lines of TypeScript, no external dependency, at quality comparable to Bullet's defaults. A fixed-timestep accumulator runs at a constant **60 Hz** (≤6 substeps/frame) so spring impulse, damping, and integration stay deterministic; dynamic bodies are **render-interpolated** between substeps to stay smooth when the display rate ≠ 60 Hz.
310
195
 
311
- ### Pipeline
196
+ **Per substep:** `predict velocities → broad + narrowphase → solve constraints (10 iters) → split-impulse position correction → integrate`.
312
197
 
313
- Per substep:
314
-
315
- ```
316
- predict velocities → broadphase + narrowphase → solve constraints (10 iters)
317
- split-impulse position correction integrate transforms
318
- ```
198
+ - **Solver** — projected Gauss-Seidel, joint rows + contact rows in one loop. Joints are 6DOF springs (3 linear + 3 angular) with stop-ERP limit correction and per-axis stiffness×error impulse. Linear rows pivot on each body's own joint-frame origin (Spring2-style, not Bullet 2.7x's shared mid-anchor), so a violated joint pulls itself back together instead of degenerating into torque and "breaking".
199
+ - **Angular limits** — hybrid: small violations (< 0.5 rad, the resting-cloth regime) use per-axis Euler stop rows, which converge cleanly and keep resting cloth still; larger violations switch to a single geodesic row toward the Euler-clamped target rotation, because per-axis Euler rows chase phantom errors near the ±90° singularity and pump energy. Ranged stops are unilateral (accumulated impulse clamped to the corrective sign) so a limit pushes back into range but never brakes natural recovery; locked axes stay bilateral equality joints. Spring rows stay per-axis, with stiffness clamped to the `k·dt² ≤ ¼` stability bound.
200
+ - **Narrowphase** — analytical sphere-sphere / -capsule / -box and capsule-capsule / -box. Capsule-capsule emits multiple contacts along near-parallel axes so cloth can't pivot around a single closest point.
201
+ - **Speculative contacts** (`margin 0.04`) fire at near-touch with a push-only clamp — inert until real overlap, but they stop fast bodies tunneling thin surfaces in one substep.
202
+ - **Split-impulse correction** resolves penetration by a mass-weighted translation _outside_ the velocity solver, so joint pulls can't fight separation.
203
+ - **Kinematic advancement** — bone-driven bodies move toward the frame's bone pose incrementally per substep, with velocities derived over the fixed step, so the solver never sees more than one 60 Hz step of anchor motion regardless of render dt.
204
+ - **Discontinuity guards** — a bone-pose jump beyond continuous motion (timeline scrub, long stall) rigidly carries each dynamic body along with its kinematic root's transform delta and zeroes momentum instead of dragging cloth across the gap; correction velocities are clamped (120 u/s linear, 30 rad/s angular), per-step travel is capped, and any body that still goes non-finite is restored to its previous substep pose.
205
+ - Sleeping is off (cloth must always react); resting bodies bleed micro-velocity via per-PMX damping.
319
206
 
320
- A fixed-timestep accumulator runs the substep at a constant **60 Hz** regardless of render rate, with up to 6 substeps per render frame. Constant `dt` keeps spring impulse, damping, and integration deterministic — without it, coupled cloth chains never reach steady state.
321
-
322
- ### Implementation notes
323
-
324
- - **Solver** — projected Gauss-Seidel sequential impulse, 10 iterations, joint rows + contact rows in the same loop. Joint constraints are 6DOF springs (3 linear, 3 angular) with stop-ERP for limit correction and a per-axis spring impulse driven by stiffness × position error.
325
- - **Narrowphase** — analytical per pair: sphere-sphere, sphere-capsule, sphere-box, capsule-capsule, capsule-box. Capsule-capsule emits multiple contact points along nearly-parallel axes for rotational stability (otherwise a single closest-point contact lets cloth pivot freely around the line through that point).
326
- - **Speculative contacts** — `CONTACT_MARGIN = 0.04` fires contacts at near-touch with signed depth. The push-only impulse clamp keeps them inert until actual overlap, but they prevent fast bodies from crossing a thin surface in a single substep.
327
- - **Split-impulse position correction** — penetration is resolved by a direct mass-weighted translation along the contact normal *outside* the velocity solver, so joint pulls in the SI loop can't fight the contact's separation.
328
- - **Kinematic velocity propagation** — bone-driven kinematic bodies have their linear + angular velocities derived from the bone-pose delta each render frame, so joints attached to a fast limb feel the actual motion instead of a position teleport.
329
- - **Body sleeping** is disabled (cloth must always respond to bone motion); resting bodies rely on per-PMX damping to bleed off micro-velocity.
330
-
331
- ### API
332
-
333
- ```javascript
334
- engine.setPhysicsEnabled(enabled)
335
- engine.resetPhysics() // re-snap bodies to bone poses, zero velocities
336
- ```
337
-
338
- That's the entire engine-level surface — physics options live on the PMX rig itself (mass, damping, friction, restitution, joint stiffness / limits, collision groups).
207
+ Engine surface is just `setPhysicsEnabled` / `resetPhysics` all tuning (mass, damping, friction, restitution, joint stiffness/limits, collision groups) lives on the PMX rig.
339
208
 
340
209
  ## Rendering
341
210
 
342
- Each surface combines an NPR stack with a Principled-style BSDF, mixed per material anime characters keep their flat illustrated look while highlights and reflections stay grounded. Per-material shaders live in `engine/src/shaders/materials/`.
343
-
344
- ### Material pipeline
345
-
346
- Every fragment shader follows the same 7-stage layout (shared stages from `nodes.ts` / `common.ts`):
211
+ Each surface mixes an NPR stack with a Principled-style BSDF per material, so characters keep a flat illustrated look while highlights and reflections stay grounded. Shaders live in `engine/src/shaders/materials/`; each fragment shader follows one 7-stage layout (shared stages from `nodes.ts` / `common.ts`):
347
212
 
348
213
  ```
349
- (A) Fragment setup → (B) Texture + alpha → (C) NPR stack → (D) Optional bump
350
- → (E) Principled BSDF → (F) NPR PBR mix → (G) FSOut
214
+ (A) setup → (B) texture + alpha → (C) NPR stack → (D) optional bump
215
+ → (E) Principled BSDF → (F) NPR↔PBR mix → (G) FSOut
351
216
  ```
352
217
 
353
- The `default` preset uses only A/B/E/G; NPR presets layer C (and sometimes D) on top, with stage F choosing how NPR-leaning the final result is.
354
-
355
- **PBR core** (`eval_principled`) — GGX microfacet with Schlick Fresnel, Walter–Smith G1, Fdez-Agüera 2019 multi-scatter compensation, Karis 2013 split-sum DFG LUT, Heitz 2016 LTC direct-spec scaling, optional sheen.
356
-
357
- **NPR toolbox** — toon ramps (constant or fwidth-AA'd), HSV warm-shadow / cool-light remaps, fresnel + layer-weight rims, value-noise bump, 3D Voronoi metallic sparkle, BT.601-luminance-gated emission.
358
-
359
- | Preset | Notes |
360
- | -------------- | ------------------------------------------------------------------------------- |
361
- | `default` | Plain Principled, metallic=0, rough=0.5 |
362
- | `eye` | Plain + post-eval emission ×1.5 |
363
- | `face` | Toon + warm rim + dual-fresnel rim + bright-tex gate, noise bump |
364
- | `body` | Toon + warm rim + fresnel + facing rim, noise bump |
365
- | `hair` | Toon + fresnel + bevel + bright-tex gate, mixed at 20% PBR |
366
- | `cloth_smooth` | Toon + bevel + emission overlay (×18) |
367
- | `cloth_rough` | Same NPR as cloth_smooth, live noise bump, rough=0.82 |
368
- | `metal` | Toon + emission overlay (×8), voronoi base, metallic=1 |
369
- | `stockings` | Gradient × facing mask + HSV emission (×5), sheen=0.7, **alpha-hashed** |
370
-
371
- Assign per-model with `engine.setMaterialPresets(name, map)`. Unlisted material names fall through to `default`.
218
+ `default` uses only A/B/E/G; NPR presets add C (and sometimes D), with F choosing how NPR-leaning the result is.
372
219
 
373
- ### Post & output
220
+ - **PBR core** (`eval_principled`) — GGX + Schlick Fresnel, Walter–Smith G1, Fdez-Agüera 2019 multi-scatter, Karis split-sum DFG LUT, Heitz 2016 LTC direct-spec, optional sheen.
221
+ - **NPR toolbox** — toon ramps (constant / fwidth-AA'd), HSV warm-shadow / cool-light remaps, fresnel + layer-weight rims, value-noise bump, Voronoi metallic sparkle, BT.601-gated emission.
374
222
 
375
- - Directional shadow map (2048², depth32float, PCF, normal + depth bias)
376
- - HDR main pass at 4× MSAA, `rg11b10ufloat` color + `rg8unorm` aux MRT (bloom mask + accumulated alpha) — fits Apple Silicon TBDR tile memory so MSAA resolves in-tile. Falls back to `rgba16float` when `rg11b10ufloat-renderable` isn't available.
377
- - Bloom: threshold + downsample/upsample mip pyramid, gated by aux bloom mask
378
- - Filmic tone mapping (LUT extracted from Blender 3.6 OCIO "Filmic / Medium High Contrast")
379
- - Inverted-hull screen-space outline pass on opaque + transparent
223
+ | Preset | Notes |
224
+ | -------------- | ----------------------------------------------------------------------- |
225
+ | `default` | Plain Principled, metallic 0, rough 0.5 |
226
+ | `eye` | Plain + post-eval emission ×1.5 |
227
+ | `face` | Toon + warm rim + dual-fresnel rim + bright-tex gate, noise bump |
228
+ | `body` | Toon + warm rim + fresnel + facing rim, noise bump |
229
+ | `hair` | Toon + fresnel + bevel + bright-tex gate, 20% PBR |
230
+ | `cloth_smooth` | Toon + bevel + emission overlay (×18) |
231
+ | `cloth_rough` | Same NPR, live noise bump, rough 0.82 |
232
+ | `metal` | Toon + emission overlay (×8), Voronoi base, metallic 1 |
233
+ | `stockings` | Gradient × facing mask + HSV emission (×5), sheen 0.7, **alpha-hashed** |
380
234
 
381
- **Alpha-hashed transparency** (`stockings` only) Wyman & McGuire 2017 derivative-aware stochastic discard with world-space hash, so self-overlapping transparent meshes resolve under MSAA with opaque-style depth writes and the dither doesn't swim with the camera.
235
+ **Post & output.** Directional shadow map (2048², depth32float, PCF) → HDR main pass at 4× MSAA (`rg11b10ufloat` color + `rg8unorm` aux MRT for bloom mask + alpha; fits Apple-Silicon TBDR tile memory so MSAA resolves in-tile, `rgba16float` fallback) bloom mip pyramid Filmic tone map (Blender 3.6 "Filmic / Medium High Contrast" LUT) → inverted-hull outline.
382
236
 
383
- **See-through hair over eyes** (MMD post-alpha-eye) — single stencil-gated extra pass: eye stamps `EYE_VALUE`, main hair skips it, an extra hair-over-eyes pass matches it and alpha-blends at 25% in linear HDR before tonemap. Outline pass also skips the stamp so the iris stays readable.
237
+ - **Alpha-hashed transparency** (`stockings`) — Wyman & McGuire 2017 derivative-aware stochastic discard in object space, so self-overlapping meshes resolve under MSAA with opaque depth writes and the dither doesn't swim.
238
+ - **See-through hair over eyes** — stencil-gated extra pass: the eye stamps `EYE_VALUE`, main hair skips it, an extra pass matches it and blends hair at 25% in linear HDR so eyes stay readable.
384
239
 
385
- ## Projects Using This Engine
240
+ ## Used by
386
241
 
387
- - **[Reze Studio](https://reze.studio)** - Web-native MMD animation editor
388
- - **[MiKaPo](https://mikapo.vercel.app)** — Real-time motion capture for MMD
389
- - **[Popo](https://popo.love)** LLM-generated MMD poses
390
- - **[MPL](https://mmd-mpl.vercel.app)** Motion programming language for MMD
391
- - **[Mixamo-MMD](https://mixamo-mmd.vercel.app)** — Retarget Mixamo FBX to VMD
242
+ - [Reze Studio](https://reze.studio) (MMD animation editor)
243
+ - [MiKaPo](https://mikapo.vercel.app) (motion capture)
244
+ - [Popo](https://popo.love) (LLM-generated poses)
245
+ - [MPL](https://mmd-mpl.vercel.app) (motion language)
246
+ - [Mixamo-MMD](https://mixamo-mmd.vercel.app) (FBX→VMD retarget)
392
247
 
393
248
  ## Tutorial
394
249
 
@@ -1 +1 @@
1
- {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
package/dist/engine.js CHANGED
@@ -290,10 +290,7 @@ export class Engine {
290
290
  const deltaAngle = currentAngle - drag.initialAngle;
291
291
  const qWorld = Quat.fromAxisAngle(drag.worldAxis, deltaAngle);
292
292
  // L_new = P_inv · Q_world · P · L_initial
293
- const lNew = drag.parentWorldRotInv
294
- .multiply(qWorld)
295
- .multiply(drag.parentWorldRot)
296
- .multiply(drag.initialLocalRot);
293
+ const lNew = drag.parentWorldRotInv.multiply(qWorld).multiply(drag.parentWorldRot).multiply(drag.initialLocalRot);
297
294
  lNew.normalize();
298
295
  nextRot = lNew;
299
296
  }
@@ -1074,7 +1071,10 @@ export class Engine {
1074
1071
  label: "ground shadow pipeline",
1075
1072
  layout: this.device.createPipelineLayout({ bindGroupLayouts: [this.groundShadowBindGroupLayout] }),
1076
1073
  shaderModule: groundShadowShader,
1077
- vertexBuffers: fullVertexBuffers,
1074
+ // Slot 0 only — the ground has no skinning, and declaring the full
1075
+ // 3-slot layout while renderGround binds one buffer is a WebGPU
1076
+ // validation error that invalidates the whole command buffer.
1077
+ vertexBuffers: [fullVertexBuffers[0]],
1078
1078
  fragmentTargets: sceneTargets,
1079
1079
  cullMode: "back",
1080
1080
  depthStencil: { format: "depth24plus-stencil8", depthWriteEnabled: true, depthCompare: "less-equal" },