reze-engine 0.12.0 → 0.12.2

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 (100) hide show
  1. package/README.md +99 -58
  2. package/dist/bezier-interpolate.d.ts +15 -0
  3. package/dist/bezier-interpolate.d.ts.map +1 -0
  4. package/dist/bezier-interpolate.js +40 -0
  5. package/dist/engine_ts.d.ts +143 -0
  6. package/dist/engine_ts.d.ts.map +1 -0
  7. package/dist/engine_ts.js +1575 -0
  8. package/dist/ik.d.ts +32 -0
  9. package/dist/ik.d.ts.map +1 -0
  10. package/dist/ik.js +337 -0
  11. package/dist/player.d.ts +64 -0
  12. package/dist/player.d.ts.map +1 -0
  13. package/dist/player.js +220 -0
  14. package/dist/pool-scene.d.ts +52 -0
  15. package/dist/pool-scene.d.ts.map +1 -0
  16. package/dist/pool-scene.js +1122 -0
  17. package/dist/pool.d.ts +38 -0
  18. package/dist/pool.d.ts.map +1 -0
  19. package/dist/pool.js +422 -0
  20. package/dist/rzm-converter.d.ts +12 -0
  21. package/dist/rzm-converter.d.ts.map +1 -0
  22. package/dist/rzm-converter.js +40 -0
  23. package/dist/rzm-loader.d.ts +24 -0
  24. package/dist/rzm-loader.d.ts.map +1 -0
  25. package/dist/rzm-loader.js +488 -0
  26. package/dist/rzm-writer.d.ts +27 -0
  27. package/dist/rzm-writer.d.ts.map +1 -0
  28. package/dist/rzm-writer.js +701 -0
  29. package/dist/shaders/body.d.ts +1 -1
  30. package/dist/shaders/body.d.ts.map +1 -1
  31. package/dist/shaders/body.js +28 -7
  32. package/dist/shaders/cloth_rough.d.ts +1 -1
  33. package/dist/shaders/cloth_rough.d.ts.map +1 -1
  34. package/dist/shaders/cloth_rough.js +16 -4
  35. package/dist/shaders/cloth_smooth.d.ts +1 -1
  36. package/dist/shaders/cloth_smooth.d.ts.map +1 -1
  37. package/dist/shaders/cloth_smooth.js +17 -5
  38. package/dist/shaders/default.d.ts +1 -1
  39. package/dist/shaders/default.d.ts.map +1 -1
  40. package/dist/shaders/eye.d.ts +1 -1
  41. package/dist/shaders/eye.d.ts.map +1 -1
  42. package/dist/shaders/face.d.ts +1 -1
  43. package/dist/shaders/face.d.ts.map +1 -1
  44. package/dist/shaders/face.js +57 -21
  45. package/dist/shaders/hair.d.ts +1 -1
  46. package/dist/shaders/hair.d.ts.map +1 -1
  47. package/dist/shaders/hair.js +27 -7
  48. package/dist/shaders/materials/body.d.ts +1 -1
  49. package/dist/shaders/materials/body.d.ts.map +1 -1
  50. package/dist/shaders/materials/body.js +86 -197
  51. package/dist/shaders/materials/cloth_rough.d.ts +1 -1
  52. package/dist/shaders/materials/cloth_rough.d.ts.map +1 -1
  53. package/dist/shaders/materials/cloth_rough.js +10 -121
  54. package/dist/shaders/materials/cloth_smooth.d.ts +1 -1
  55. package/dist/shaders/materials/cloth_smooth.d.ts.map +1 -1
  56. package/dist/shaders/materials/cloth_smooth.js +59 -172
  57. package/dist/shaders/materials/common.d.ts +6 -0
  58. package/dist/shaders/materials/common.d.ts.map +1 -0
  59. package/dist/shaders/materials/common.js +144 -0
  60. package/dist/shaders/materials/default.d.ts +1 -1
  61. package/dist/shaders/materials/default.d.ts.map +1 -1
  62. package/dist/shaders/materials/default.js +12 -145
  63. package/dist/shaders/materials/eye.d.ts +1 -1
  64. package/dist/shaders/materials/eye.d.ts.map +1 -1
  65. package/dist/shaders/materials/eye.js +12 -117
  66. package/dist/shaders/materials/face.d.ts +1 -1
  67. package/dist/shaders/materials/face.d.ts.map +1 -1
  68. package/dist/shaders/materials/face.js +85 -197
  69. package/dist/shaders/materials/hair.d.ts +1 -1
  70. package/dist/shaders/materials/hair.d.ts.map +1 -1
  71. package/dist/shaders/materials/hair.js +78 -183
  72. package/dist/shaders/materials/metal.d.ts +1 -1
  73. package/dist/shaders/materials/metal.d.ts.map +1 -1
  74. package/dist/shaders/materials/metal.js +15 -121
  75. package/dist/shaders/materials/nodes.d.ts +1 -1
  76. package/dist/shaders/materials/nodes.d.ts.map +1 -1
  77. package/dist/shaders/materials/nodes.js +77 -0
  78. package/dist/shaders/materials/stockings.d.ts +1 -1
  79. package/dist/shaders/materials/stockings.d.ts.map +1 -1
  80. package/dist/shaders/materials/stockings.js +26 -152
  81. package/dist/shaders/metal.d.ts +1 -1
  82. package/dist/shaders/metal.d.ts.map +1 -1
  83. package/dist/shaders/metal.js +17 -4
  84. package/dist/shaders/nodes.d.ts +1 -1
  85. package/dist/shaders/nodes.d.ts.map +1 -1
  86. package/dist/shaders/nodes.js +9 -0
  87. package/dist/shaders/stockings.d.ts +1 -1
  88. package/dist/shaders/stockings.d.ts.map +1 -1
  89. package/package.json +1 -1
  90. package/src/shaders/materials/body.ts +90 -201
  91. package/src/shaders/materials/cloth_rough.ts +10 -121
  92. package/src/shaders/materials/cloth_smooth.ts +63 -176
  93. package/src/shaders/materials/common.ts +155 -0
  94. package/src/shaders/materials/default.ts +12 -145
  95. package/src/shaders/materials/eye.ts +12 -117
  96. package/src/shaders/materials/face.ts +89 -201
  97. package/src/shaders/materials/hair.ts +82 -187
  98. package/src/shaders/materials/metal.ts +15 -121
  99. package/src/shaders/materials/nodes.ts +77 -0
  100. package/src/shaders/materials/stockings.ts +27 -153
package/README.md CHANGED
@@ -17,6 +17,7 @@ npm install reze-engine
17
17
  - **HDR pipeline** with bloom mip pyramid, Filmic tone mapping, 4× MSAA
18
18
  - **Alpha-hashed transparency** (Wyman & McGuire 2017) for self-overlapping transparent meshes like stockings
19
19
  - **Screen-space outlines** on opaque + transparent materials
20
+ - **See-through hair over eyes** — stencil-gated MMD post-alpha-eye so eyes read at 50% through hair silhouettes
20
21
  - **VMD animation** with IK solver and Bullet physics
21
22
  - **Orbit camera** with bone-follow mode
22
23
  - **GPU picking** (double-click/tap)
@@ -26,7 +27,7 @@ npm install reze-engine
26
27
  ## Usage
27
28
 
28
29
  ```javascript
29
- import { Engine, Vec3 } from "reze-engine";
30
+ import { Engine, Vec3 } from "reze-engine"
30
31
 
31
32
  const engine = new Engine(canvas, {
32
33
  world: { color: new Vec3(0.4, 0.49, 0.65), strength: 1.0 },
@@ -36,16 +37,16 @@ const engine = new Engine(canvas, {
36
37
  direction: new Vec3(0, -0.5, 1),
37
38
  },
38
39
  camera: { distance: 31.5, target: new Vec3(0, 11.5, 0) }, // MMD units (1 unit = 8 cm)
39
- });
40
- await engine.init();
40
+ })
41
+ await engine.init()
41
42
 
42
43
  engine.setBloomOptions({
43
44
  color: new Vec3(0.9, 0.1, 0.8),
44
45
  intensity: 0.05,
45
46
  threshold: 0.5,
46
- });
47
+ })
47
48
 
48
- const model = await engine.loadModel("hero", "/models/hero/hero.pmx");
49
+ const model = await engine.loadModel("hero", "/models/hero/hero.pmx")
49
50
 
50
51
  // Map PMX material names to NPR presets (unlisted names fall back to `default`).
51
52
  engine.setMaterialPresets("hero", {
@@ -57,15 +58,15 @@ engine.setMaterialPresets("hero", {
57
58
  cloth_rough: ["jacket", "pants"],
58
59
  stockings: ["stockings"],
59
60
  metal: ["metal01", "earring"],
60
- });
61
+ })
61
62
 
62
- await model.loadVmd("idle", "/animations/idle.vmd");
63
- model.show("idle");
64
- model.play();
63
+ await model.loadVmd("idle", "/animations/idle.vmd")
64
+ model.show("idle")
65
+ model.play()
65
66
 
66
- engine.setCameraFollow(model, "センター", new Vec3(0, 3.5, 0));
67
- engine.addGround({ width: 160, height: 160 });
68
- engine.runRenderLoop();
67
+ engine.setCameraFollow(model, "センター", new Vec3(0, 3.5, 0))
68
+ engine.addGround({ width: 160, height: 160 })
69
+ engine.runRenderLoop()
69
70
  ```
70
71
 
71
72
  ## API
@@ -114,30 +115,26 @@ Use a hidden `<input type="file" webkitdirectory multiple>` (or drag/drop) and p
114
115
  2. **`engine.loadModel(name, { files, pmxFile })`** — `pmxFile` selects which `.pmx` when the folder contains several.
115
116
 
116
117
  ```javascript
117
- import {
118
- Engine,
119
- parsePmxFolderInput,
120
- pmxFileAtRelativePath,
121
- } from "reze-engine";
118
+ import { Engine, parsePmxFolderInput, pmxFileAtRelativePath } from "reze-engine"
122
119
 
123
120
  // In <input onChange>:
124
- const picked = parsePmxFolderInput(e.target.files);
125
- e.target.value = "";
121
+ const picked = parsePmxFolderInput(e.target.files)
122
+ e.target.value = ""
126
123
 
127
124
  if (picked.status === "single") {
128
125
  const model = await engine.loadModel("myModel", {
129
126
  files: picked.files,
130
127
  pmxFile: picked.pmxFile,
131
- });
128
+ })
132
129
  }
133
130
 
134
131
  if (picked.status === "multiple") {
135
132
  // Let the user choose `chosenPath` from picked.pmxRelativePaths, then:
136
- const pmxFile = pmxFileAtRelativePath(picked.files, chosenPath);
133
+ const pmxFile = pmxFileAtRelativePath(picked.files, chosenPath)
137
134
  const model = await engine.loadModel("myModel", {
138
135
  files: picked.files,
139
136
  pmxFile,
140
- });
137
+ })
141
138
  }
142
139
  ```
143
140
 
@@ -176,12 +173,12 @@ model.getBoneWorldPosition(name)
176
173
  `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.
177
174
 
178
175
  ```javascript
179
- const buffer = model.exportVmd("idle");
180
- const blob = new Blob([buffer], { type: "application/octet-stream" });
181
- const link = document.createElement("a");
182
- link.href = URL.createObjectURL(blob);
183
- link.download = "idle.vmd";
184
- link.click();
176
+ const buffer = model.exportVmd("idle")
177
+ const blob = new Blob([buffer], { type: "application/octet-stream" })
178
+ const link = document.createElement("a")
179
+ link.href = URL.createObjectURL(blob)
180
+ link.download = "idle.vmd"
181
+ link.click()
185
182
  ```
186
183
 
187
184
  #### Playback
@@ -227,55 +224,99 @@ The shadow map is cast from `sun.direction` — same vector the shader lights wi
227
224
 
228
225
  ## Rendering
229
226
 
230
- The renderer pairs stylised toon shading with a physically-based specular core, so anime characters keep their flat illustrated look while highlights and reflections still feel grounded. Each surface runs through a per-material preset that mixes an NPR closure with a Principled-style BSDF.
227
+ Each surface combines an NPR stack with a Principled-style BSDF, mixed per material — so anime characters keep their flat illustrated look while highlights and reflections stay grounded. Every per-material shader is ~40–120 lines of distinctive code standing on a small set of shared WGSL primitives (`engine/src/shaders/materials/`).
228
+
229
+ ### Anatomy of a material shader
230
+
231
+ Every material's fragment shader is the same 7-stage pipeline. The order is fixed; the content is per-material:
232
+
233
+ ```
234
+ (A) Fragment setup → n, v, l, sun, amb, shadow ← shared
235
+ (B) Texture + alpha → tex_rgb, discard ← shared shape
236
+ (C) NPR stack → toon + rim + warm + … ← UNIQUE per material
237
+ (D) Optional bump → noise → bump_lh ← 3 presets
238
+ (E) Principled BSDF → eval_principled(...) ← shared helper
239
+ (F) NPR ↔ PBR mix → mix(npr, principled, fac) ← per-material fac
240
+ (G) FSOut → color + bloom mask ← shared
241
+ ```
242
+
243
+ The simplest material (`default`) uses only A/B/E/G — no NPR stack at all:
244
+
245
+ ```wgsl
246
+ let color = eval_principled(
247
+ PrincipledIn(albedo, 0.0, 0.5, 0.5, 1e30, 0.0, 0.0), // metallic, spec, rough, clamp, sheen, sheen_tint
248
+ n, l, v, sun, amb, shadow
249
+ );
250
+ ```
251
+
252
+ NPR presets add stage C (and sometimes D) on top, and stage F chooses how NPR-leaning the surface is. The Blender preset this engine targets is authored with `MixShader(NPR, Principled, fac)` throughout — the 7-stage layout is a direct port of that topology.
253
+
254
+ ### Shared WGSL foundations
255
+
256
+ - **`nodes.ts`** — WGSL mirrors of the Blender shader nodes the presets use: `hue_sat`, `bright_contrast`, `ramp_constant/linear/cardinal`, `mix_overlay/lighten/linear_light`, `fresnel`, `layer_weight_fresnel/facing`, `tex_noise`, `tex_voronoi`, `mapping_point`, `bump_lh`, `normal_map`. Plus one combined DFG + LTC 64×64 RGBA8 LUT baked at `engine.init()`, one `eval_principled(PrincipledIn, N, L, V, sun, amb, shadow)` EEVEE Principled port, and `principled_sheen`.
257
+ - **`common.ts`** — Uniform structs, bind-group layout (same for every material pipeline), 3×3 PCF shadow sampler, skinning vertex shader, shared `FSOut`.
258
+ - **Per-material files** — constants + NPR stack + optional bump + `eval_principled` call + final mix. That's it.
231
259
 
232
260
  ### PBR specular core
233
261
 
234
- - GGX microfacet specular with Schlick Fresnel and Walter–Smith G1
262
+ Inside `eval_principled`:
263
+
264
+ - GGX microfacet specular with Schlick Fresnel, Walter–Smith G1 (reciprocal form collapses the `4·NL·NV` denominator)
235
265
  - **Multi-scatter compensation** (Fdez-Agüera 2019, `F_brdf_multi_scatter`) — restores energy at high roughness so metals don't darken
236
- - **Split-sum DFG LUT** (Karis 2013) baked at `engine.init()` — drives indirect specular and acts as the denominator of the direct-spec energy correction
237
- - **LTC direct-spec scale** (Heitz 2016 magnitude LUT) — keeps analytic-light specular in the same energy budget as image-based lighting
238
- - Sheen coarse approximation (`f³·0.077 + f·0.01 + 0.00026`) on cloth and stockings
266
+ - **Split-sum DFG LUT** (Karis 2013) — drives indirect specular, packed into the `.rg` channels of the baked LUT
267
+ - **LTC direct-spec scale** (Heitz 2016 magnitude LUT) — keeps analytic-light specular in the same energy budget as image-based lighting; packed into `.ba` so a single LUT tap serves both paths
268
+ - **Sheen coarse curve** (`f³·0.077 + f·0.01 + 0.00026`) gated by the `sheen` field on `PrincipledIn` used by `stockings`
239
269
 
240
- ### Stylised diffuse (NPR toolbox)
270
+ ### NPR toolbox
241
271
 
242
- Every preset is built out of the same NPR primitives:
272
+ Every preset's stage C is built from these primitives:
243
273
 
244
- - **Toon ramps** — quantised NdotL through constant or anti-aliased step ramps for hard cel-shaded transitions
245
- - **HSV remaps** — separate hue/sat/value tints for shadow vs lit zones, then layered with mix-overlay against AO masks for warm-shadow / cool-light shifts
246
- - **Fresnel rim & layer-weight wrap** — Fresnel × layer-weight feeds a MixShader against an emissive backdrop for anime-style back-light
247
- - **Procedural micro-detail** — fBM noise (PCG hash, three octaves) plus bump-from-height for fabric weave, skin micro-roughness, and metallic Voronoi sparkle in reflection-coord space
248
- - **Selective emission** — texture-gated emission boosts (eye iris, stockings pattern) that survive into bloom
274
+ - **Toon ramps** — quantised NdotL through constant or `ramp_constant_edge_aa` (fwidth-based step anti-alias) for cel-shaded shadow terminators
275
+ - **HSV remaps** — separate hue/sat/value tints for shadow vs lit zones, layered with mix-overlay against the lit texture for warm-shadow / cool-light shifts
276
+ - **Fresnel rim & layer-weight wrap** — `fresnel × layer_weight_facing` (or two stacked fresnels) feeds a MixShader against an emissive backdrop for anime back-light
277
+ - **Procedural micro-detail** — 3-octave value noise (PCG hash, fully unrolled) drives bump-from-height for skin and fabric; 3D Voronoi in reflection-coord space drives metallic sparkle
278
+ - **Selective emission** — BT.601-luminance-gated boosts (eye iris, face highlights, stockings pattern) that survive into bloom
249
279
 
250
- ### Material presets
280
+ ### Per-material NPR stacks
251
281
 
252
- Each PMX material is dispatched to one of these shaders:
282
+ Each PMX material is assigned to one of these shaders. The NPR stack column is what's actually in stage C of that file; the Principled column is what gets passed to `eval_principled`.
253
283
 
254
- | Preset | Look |
255
- | -------------- | ------------------------------------------------------------------------------ |
256
- | `face` | toon ramp + rim + warm subsurface bleed + Principled mix |
257
- | `hair` | layered hair toon + Fresnel rim + Principled spec mix |
258
- | `body` | toon ramp + AO modulation + rim + Principled mix |
259
- | `eye` | iris with emission boost (drives bloom) |
260
- | `stockings` | NPR-tinted Principled with sheen, alpha-hashed |
261
- | `metal` | full-metallic Principled + reflection-coord Voronoi sparkle + NPR toon overlay |
262
- | `cloth_smooth` | Principled cloth with sheen, smoother variant |
263
- | `cloth_rough` | rougher cloth variant |
264
- | `default` | plain Principled BSDF (unmapped fallback) |
284
+ | Preset | NPR stack (stage C) | Principled (stage E) |
285
+ | -------------- | ----------------------------------------------------------- | ------------------------------------------------------------- |
286
+ | `default` | — | metallic=0, spec=0.5, rough=0.5 |
287
+ | `eye` | (emission = albedo × 1.5 added post-eval) | same as default |
288
+ | `face` | toon + warm rim + dual-fresnel rim + BT.601 bright-tex gate | spec=0.5, rough=0.3, noise bump, spec clamp=10 |
289
+ | `body` | toon + warm rim + fresnel rim + facing rim | spec=0.5, rough=0.3, noise bump, spec clamp=10 |
290
+ | `hair` | toon + fresnel rim + bevel (n.y) + bright-tex gate | spec=1.0, rough=0.3, mixed at 20% PBR |
291
+ | `cloth_smooth` | toon + bevel + mix-overlay emission (×18) | spec=0.8, rough=0.5 |
292
+ | `cloth_rough` | same NPR as `cloth_smooth` | spec=0.8, rough=0.82, live noise bump, spec clamp=10 |
293
+ | `metal` | toon + mix-overlay emission (×8) | metallic=1, voronoi-driven base (reflection-coord), rough=0.3 |
294
+ | `stockings` | gradient × facing mask + HSV-boosted emission (×5) | metallic=0.1, spec=1, rough=0.5, **sheen=0.7**, hashed alpha |
295
+
296
+ Assign presets per-model with `engine.setMaterialPresets(name, map)` (see the [Usage](#usage) example). Material names not listed fall through to `default`.
265
297
 
266
298
  ### Shadows, post, output
267
299
 
268
300
  - Directional shadow map (2048², depth32float, 3×3 PCF unrolled, normal + depth bias)
269
301
  - HDR (rgba16f) main pass with 4× MSAA, resolved before tonemap
270
- - Bloom via threshold + downsample/upsample mip pyramid, gated by an MRT mask channel emitted from emissive presets
271
- - Filmic tone mapping (LUT sampled from the same view-transform curve used by "Filmic / Medium High Contrast"), exposure baked in
302
+ - Bloom via threshold + downsample/upsample mip pyramid, gated by an MRT mask channel from emissive presets
303
+ - Filmic tone mapping (LUT extracted from Blender 3.6 OCIO "Filmic / Medium High Contrast", exposure baked in)
272
304
  - Screen-space outline pass (inverted-hull) on opaque and transparent materials
273
305
 
274
- Assign presets per-model with `engine.setMaterialPresets(name, map)` (see the [Usage](#usage) example). Material names not listed fall through to the `default` Principled BSDF.
275
-
276
306
  ### Alpha-hashed transparency
277
307
 
278
- `stockings` uses the Wyman & McGuire 2017 derivative-aware stochastic discard instead of alpha blend. Self-overlapping transparent meshes (stockings wrap the leg — front and back surfaces share screen pixels) can't be sorted per-fragment in one draw call, so blend produces "cracks". Hashed alpha keeps opaque-style depth writes and resolves cleanly under MSAA.
308
+ `stockings` uses the Wyman & McGuire 2017 derivative-aware stochastic discard instead of alpha blend. Self-overlapping transparent meshes (stockings wrap the leg — front and back surfaces share screen pixels) can't be sorted per-fragment in one draw call, so blend produces "cracks". Hashed alpha keeps opaque-style depth writes and resolves cleanly under MSAA. The hash is derived from world-space position, not screen-space, so the dither pattern doesn't swim when the camera moves.
309
+
310
+ ### See-through hair over eyes (MMD post-alpha-eye)
311
+
312
+ The classic MMD effect where hair strands covering the eye are rendered at 50% so the iris stays readable — implemented as a single extra pass driven by the stencil buffer, not a two-texture composite.
313
+
314
+ - **Eye pipeline** stamps `stencil = EYE_VALUE` on every fragment it writes, and uses `cullMode: "front"` + a small negative `depthBias` so only the back half of the eye mesh renders (the MMD trick that prevents eyes from leaking through the back of the head without a per-model skull occluder).
315
+ - **Main hair pipeline** stencil-tests `not-equal EYE_VALUE` and skips those fragments entirely — depth and color stay as the eye wrote them.
316
+ - **Hair-over-eyes pipeline** re-issues the hair draws through the same shader compiled with `override IS_OVER_EYES = true` (pipeline-override constant, so the dead branch is dropped at compile time). Stencil-tests `equal EYE_VALUE`, `depthWriteEnabled: false`, and alpha blends at 50% — so eye-stamped pixels end up `0.5·hair + 0.5·eye` in linear HDR before tonemap.
317
+ - **Outline pipeline** also stencil-tests `not-equal EYE_VALUE` so the edge color doesn't overwrite the freshly blended eye pixels (otherwise the near-black outline paints a dark almond shape over the see-through area).
318
+
319
+ Draw order within a model: non-eye/non-hair opaque → eye (stamp) → hair (skip stamp) → outlines (skip stamp) → hair-over-eyes (match stamp). One `setStencilReference(EYE_VALUE)` per model covers all four stencil-aware pipelines.
279
320
 
280
321
  ## Projects Using This Engine
281
322
 
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Bezier interpolation for VMD animations
3
+ * Based on the reference implementation from babylon-mmd
4
+ */
5
+ /**
6
+ * Bezier interpolation function
7
+ * @param x1 First control point X (0-127, normalized to 0-1)
8
+ * @param x2 Second control point X (0-127, normalized to 0-1)
9
+ * @param y1 First control point Y (0-127, normalized to 0-1)
10
+ * @param y2 Second control point Y (0-127, normalized to 0-1)
11
+ * @param t Interpolation parameter (0-1)
12
+ * @returns Interpolated value (0-1)
13
+ */
14
+ export declare function bezierInterpolate(x1: number, x2: number, y1: number, y2: number, t: number): number;
15
+ //# sourceMappingURL=bezier-interpolate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bezier-interpolate.d.ts","sourceRoot":"","sources":["../src/bezier-interpolate.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAgCnG"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Bezier interpolation for VMD animations
3
+ * Based on the reference implementation from babylon-mmd
4
+ */
5
+ /**
6
+ * Bezier interpolation function
7
+ * @param x1 First control point X (0-127, normalized to 0-1)
8
+ * @param x2 Second control point X (0-127, normalized to 0-1)
9
+ * @param y1 First control point Y (0-127, normalized to 0-1)
10
+ * @param y2 Second control point Y (0-127, normalized to 0-1)
11
+ * @param t Interpolation parameter (0-1)
12
+ * @returns Interpolated value (0-1)
13
+ */
14
+ export function bezierInterpolate(x1, x2, y1, y2, t) {
15
+ // Clamp t to [0, 1]
16
+ t = Math.max(0, Math.min(1, t));
17
+ // Binary search for the t value that gives us the desired x
18
+ // We're solving for t in the Bezier curve: x(t) = 3*(1-t)^2*t*x1 + 3*(1-t)*t^2*x2 + t^3
19
+ let start = 0;
20
+ let end = 1;
21
+ let mid = 0.5;
22
+ // Iterate until we find the t value that gives us the desired x
23
+ for (let i = 0; i < 15; i++) {
24
+ // Evaluate Bezier curve at mid point
25
+ const x = 3 * (1 - mid) * (1 - mid) * mid * x1 + 3 * (1 - mid) * mid * mid * x2 + mid * mid * mid;
26
+ if (Math.abs(x - t) < 0.0001) {
27
+ break;
28
+ }
29
+ if (x < t) {
30
+ start = mid;
31
+ }
32
+ else {
33
+ end = mid;
34
+ }
35
+ mid = (start + end) / 2;
36
+ }
37
+ // Now evaluate the y value at this t
38
+ const y = 3 * (1 - mid) * (1 - mid) * mid * y1 + 3 * (1 - mid) * mid * mid * y2 + mid * mid * mid;
39
+ return y;
40
+ }
@@ -0,0 +1,143 @@
1
+ import { Quat, Vec3 } from "reze-mmd";
2
+ export type EngineOptions = {
3
+ ambientColor?: Vec3;
4
+ bloomIntensity?: number;
5
+ rimLightIntensity?: number;
6
+ cameraDistance?: number;
7
+ cameraTarget?: Vec3;
8
+ };
9
+ export interface EngineStats {
10
+ fps: number;
11
+ frameTime: number;
12
+ }
13
+ export declare class Engine {
14
+ private canvas;
15
+ private device;
16
+ private context;
17
+ private presentationFormat;
18
+ private camera;
19
+ private cameraUniformBuffer;
20
+ private cameraMatrixData;
21
+ private cameraDistance;
22
+ private cameraTarget;
23
+ private lightUniformBuffer;
24
+ private lightData;
25
+ private vertexBuffer;
26
+ private indexBuffer?;
27
+ private resizeObserver;
28
+ private depthTexture;
29
+ private modelPipeline;
30
+ private eyePipeline;
31
+ private hairPipelineOverEyes;
32
+ private hairPipelineOverNonEyes;
33
+ private hairDepthPipeline;
34
+ private outlinePipeline;
35
+ private hairOutlinePipeline;
36
+ private mainBindGroupLayout;
37
+ private outlineBindGroupLayout;
38
+ private jointsBuffer;
39
+ private weightsBuffer;
40
+ private skinMatrixBuffer?;
41
+ private multisampleTexture;
42
+ private readonly sampleCount;
43
+ private renderPassDescriptor;
44
+ private readonly STENCIL_EYE_VALUE;
45
+ private readonly BLOOM_DOWNSCALE_FACTOR;
46
+ private static readonly DEFAULT_BLOOM_THRESHOLD;
47
+ private static readonly DEFAULT_BLOOM_INTENSITY;
48
+ private static readonly DEFAULT_RIM_LIGHT_INTENSITY;
49
+ private static readonly DEFAULT_CAMERA_DISTANCE;
50
+ private static readonly DEFAULT_CAMERA_TARGET;
51
+ private static readonly TRANSPARENCY_EPSILON;
52
+ private static readonly STATS_FPS_UPDATE_INTERVAL_MS;
53
+ private static readonly STATS_FRAME_TIME_ROUNDING;
54
+ private ambientColor;
55
+ private sceneRenderTexture;
56
+ private sceneRenderTextureView;
57
+ private bloomExtractTexture;
58
+ private bloomBlurTexture1;
59
+ private bloomBlurTexture2;
60
+ private bloomExtractPipeline;
61
+ private bloomBlurPipeline;
62
+ private bloomComposePipeline;
63
+ private blurDirectionBuffer;
64
+ private bloomIntensityBuffer;
65
+ private bloomThresholdBuffer;
66
+ private linearSampler;
67
+ private bloomExtractBindGroup?;
68
+ private bloomBlurHBindGroup?;
69
+ private bloomBlurVBindGroup?;
70
+ private bloomComposeBindGroup?;
71
+ private bloomThreshold;
72
+ private bloomIntensity;
73
+ private rimLightIntensity;
74
+ private currentModel;
75
+ private modelDir;
76
+ private physics;
77
+ private materialSampler;
78
+ private textureCache;
79
+ private vertexBufferNeedsUpdate;
80
+ private drawCalls;
81
+ private lastFpsUpdate;
82
+ private framesSinceLastUpdate;
83
+ private lastFrameTime;
84
+ private frameTimeSum;
85
+ private frameTimeCount;
86
+ private stats;
87
+ private animationFrameId;
88
+ private renderLoopCallback;
89
+ private player;
90
+ private hasAnimation;
91
+ constructor(canvas: HTMLCanvasElement, options?: EngineOptions);
92
+ init(): Promise<void>;
93
+ private createRenderPipeline;
94
+ private createPipelines;
95
+ private createBloomPipelines;
96
+ private setupBloom;
97
+ private setupResize;
98
+ private handleResize;
99
+ private setupCamera;
100
+ private setupLighting;
101
+ private setAmbientColor;
102
+ loadAnimation(url: string): Promise<void>;
103
+ playAnimation(): void;
104
+ stopAnimation(): void;
105
+ pauseAnimation(): void;
106
+ seekAnimation(time: number): void;
107
+ getAnimationProgress(): import("./player").AnimationProgress;
108
+ /**
109
+ * Apply animation pose to model
110
+ */
111
+ private applyPose;
112
+ /**
113
+ * Reset bones and physics to match a given pose
114
+ * @param pose The pose to apply
115
+ * @param resetBonesWithoutKeyframes If true, reset bones that don't have keyframes in the pose to identity
116
+ */
117
+ private resetBonesAndPhysics;
118
+ getStats(): EngineStats;
119
+ runRenderLoop(callback?: () => void): void;
120
+ stopRenderLoop(): void;
121
+ dispose(): void;
122
+ loadModel(path: string): Promise<void>;
123
+ rotateBones(bones: string[], rotations: Quat[], durationMs?: number): void;
124
+ moveBones(bones: string[], relativeTranslations: Vec3[], durationMs?: number): void;
125
+ setMorphWeight(name: string, weight: number, durationMs?: number): void;
126
+ private updateVertexBuffer;
127
+ private setupModelBuffers;
128
+ private setupMaterials;
129
+ private createMaterialUniformBuffer;
130
+ private createUniformBuffer;
131
+ private createTextureFromPath;
132
+ private renderEyes;
133
+ private renderHair;
134
+ render(): void;
135
+ private applyBloom;
136
+ private updateCameraUniforms;
137
+ private updateRenderTarget;
138
+ private updateModelPose;
139
+ private computeSkinMatrices;
140
+ private drawOutlines;
141
+ private updateStats;
142
+ }
143
+ //# sourceMappingURL=engine_ts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine_ts.d.ts","sourceRoot":"","sources":["../src/engine_ts.ts"],"names":[],"mappings":"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"}