reze-engine 0.68.3 → 0.68.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.
Files changed (55) hide show
  1. package/dist/_dbg-loader.mjs +1100 -0
  2. package/dist/engine.d.ts +40 -2
  3. package/dist/engine.d.ts.map +1 -1
  4. package/dist/engine.js +119 -8
  5. package/dist/graph/slots.d.ts.map +1 -1
  6. package/dist/graph/slots.js +12 -4
  7. package/dist/index.d.ts +1 -0
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/jelly-eyes.d.ts +37 -0
  10. package/dist/jelly-eyes.d.ts.map +1 -0
  11. package/dist/jelly-eyes.js +381 -0
  12. package/dist/shaders/materials/common.d.ts.map +1 -1
  13. package/dist/shaders/materials/common.js +133 -2
  14. package/package.json +1 -1
  15. package/src/engine.ts +121 -8
  16. package/src/graph/slots.ts +13 -4
  17. package/src/index.ts +1 -0
  18. package/src/jelly-eyes.ts +434 -0
  19. package/src/shaders/materials/common.ts +133 -2
  20. package/dist/graph/presets/pool_floor.d.ts +0 -3
  21. package/dist/graph/presets/pool_floor.d.ts.map +0 -1
  22. package/dist/graph/presets/pool_floor.js +0 -42
  23. package/dist/graph/presets/water.d.ts +0 -3
  24. package/dist/graph/presets/water.d.ts.map +0 -1
  25. package/dist/graph/presets/water.js +0 -31
  26. package/dist/physics/autofit.d.ts +0 -147
  27. package/dist/physics/autofit.d.ts.map +0 -1
  28. package/dist/physics/autofit.js +0 -501
  29. package/dist/shaders/agx-lut.d.ts +0 -11
  30. package/dist/shaders/agx-lut.d.ts.map +0 -1
  31. package/dist/shaders/agx-lut.js +0 -29
  32. package/dist/shaders/dfg_lut.d.ts +0 -3
  33. package/dist/shaders/dfg_lut.d.ts.map +0 -1
  34. package/dist/shaders/dfg_lut.js +0 -129
  35. package/dist/shaders/ltc_mag_lut.d.ts +0 -3
  36. package/dist/shaders/ltc_mag_lut.d.ts.map +0 -1
  37. package/dist/shaders/ltc_mag_lut.js +0 -1033
  38. package/dist/shaders/passes/field-blit.d.ts +0 -26
  39. package/dist/shaders/passes/field-blit.d.ts.map +0 -1
  40. package/dist/shaders/passes/field-blit.js +0 -65
  41. package/dist/shaders/passes/ghosts.d.ts +0 -22
  42. package/dist/shaders/passes/ghosts.d.ts.map +0 -1
  43. package/dist/shaders/passes/ghosts.js +0 -75
  44. package/dist/shaders/passes/ground-noise.d.ts +0 -7
  45. package/dist/shaders/passes/ground-noise.d.ts.map +0 -1
  46. package/dist/shaders/passes/ground-noise.js +0 -88
  47. package/dist/shaders/passes/sim.d.ts +0 -34
  48. package/dist/shaders/passes/sim.d.ts.map +0 -1
  49. package/dist/shaders/passes/sim.js +0 -169
  50. package/dist/shaders/score-api.d.ts +0 -10
  51. package/dist/shaders/score-api.d.ts.map +0 -1
  52. package/dist/shaders/score-api.js +0 -114
  53. package/dist/shaders/shadow-api.d.ts +0 -29
  54. package/dist/shaders/shadow-api.d.ts.map +0 -1
  55. package/dist/shaders/shadow-api.js +0 -112
package/src/engine.ts CHANGED
@@ -43,6 +43,7 @@ import {
43
43
  type SimClock,
44
44
  } from "./effect-schedule"
45
45
  import { parentKeySpan, type ModelParentKey } from "./parent-keys"
46
+ import { JellyEyes, clampStrength, clearJellyEyes, type JellyEyeOptions } from "./jelly-eyes"
46
47
  import { NativeHost } from "./unity/host"
47
48
  import { NativeLooks, type NativeLook } from "./unity/looks"
48
49
  import { NativeStage, type NativeStageLights, type NativeStagePackage, type NativeStageReader } from "./unity/stage"
@@ -427,6 +428,11 @@ export interface MidiNote {
427
428
  /** How many character positions an effect can read (viewU[11..14]). Defined
428
429
  * beside the shader that reads them — the layout arithmetic has to agree. */
429
430
  const MAX_EFFECT_SUBJECTS = EFFECT_SUBJECTS
431
+ /** The frame's half height (model units) at and past which a camera follow
432
+ * takes its full smoothing; closer shots follow more rigidly — see the
433
+ * follow in renderWithDelta(). About a full-body shot. */
434
+ const FOLLOW_LAG_FULL_HALF = 8
435
+
430
436
  /** Where a character IS, for an effect that follows them. センター carries a
431
437
  * motion's root movement — walking, jumping — where the model transform only
432
438
  * carries where the model was placed; 全ての親 is the fallback for a model that
@@ -1004,6 +1010,9 @@ interface ModelInstance {
1004
1010
  * Off drops every draw from the shadow list; on hands it back to the PMX
1005
1011
  * flag on each material. */
1006
1012
  castShadow: boolean
1013
+ /** Its jelly eyes' springs, or null while its eyes are still — see
1014
+ * setModelJellyEyes. */
1015
+ jellyEyes: JellyEyes | null
1007
1016
  basePath: string
1008
1017
  assetReader: AssetReader
1009
1018
  gpuBuffers: GPUBuffer[]
@@ -3038,6 +3047,12 @@ export class Engine {
3038
3047
  private cameraFollowSmoothing = 0
3039
3048
  private cameraFollowSeeded = false
3040
3049
  private readonly cameraFollowPos = new Vec3(0, 0, 0)
3050
+ // Turning with the bone (setCameraFollow's `turn`): the bone's facing as yaw
3051
+ // and elevation, eased like the position, whose change each frame is added
3052
+ // to the orbit — so the shot keeps its angle to her face as she turns.
3053
+ private cameraFollowTurn = false
3054
+ private cameraFollowYaw = 0
3055
+ private cameraFollowPitch = 0
3041
3056
  private cameraTargetOffset: Vec3 = new Vec3(0, 0, 0)
3042
3057
 
3043
3058
  private lastFrameTime = performance.now()
@@ -8311,8 +8326,9 @@ export class Engine {
8311
8326
  visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
8312
8327
  buffer: { type: "read-only-storage" },
8313
8328
  },
8314
- // The model's own light — see setModelFill and setModelSun.
8315
- { binding: 1, visibility: GPUShaderStage.FRAGMENT, buffer: { type: "uniform" } },
8329
+ // The model's own light — see setModelFill and setModelSun. VERTEX
8330
+ // too: jelly eyes move the iris in the vertex shader (jelly[] in it).
8331
+ { binding: 1, visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT, buffer: { type: "uniform" } },
8316
8332
  ],
8317
8333
  })
8318
8334
  // group 2: per-material (textures + material uniforms) — bound per draw call.
@@ -10222,8 +10238,13 @@ export class Engine {
10222
10238
  this.cameraTargetOffset.z = offset?.z ?? 0
10223
10239
  }
10224
10240
 
10225
- /** Souls-style follow cam: orbit center tracks a model bone each frame. Shorthand for setCameraTarget(model, boneName, offset). */
10226
- setCameraFollow(model: Model | null, boneName?: string, offset?: Vec3, smoothing?: number): void {
10241
+ /**
10242
+ * Souls-style follow cam: orbit center tracks a model bone each frame.
10243
+ * Shorthand for setCameraTarget(model, boneName, offset). With `turn`, the
10244
+ * orbit also turns with her face (the head bone) — the camera keeps its
10245
+ * angle to her face as she turns and nods, and a drag still orbits on top.
10246
+ */
10247
+ setCameraFollow(model: Model | null, boneName?: string, offset?: Vec3, smoothing?: number, turn?: boolean): void {
10227
10248
  // Panning sets the very thing the follow overwrites each frame, so it is
10228
10249
  // goes to the offset for as long as the shot is riding a bone. See Camera.panSink.
10229
10250
  this.camera.panSink = model !== null ? this.cameraTargetOffset : null
@@ -10239,6 +10260,7 @@ export class Engine {
10239
10260
  // Handheld feel: seconds for the camera to close ~63% of the gap to the
10240
10261
  // bone (exponential, frame-rate independent). 0 = rigid instant follow.
10241
10262
  this.cameraFollowSmoothing = Math.max(0, smoothing ?? 0)
10263
+ this.cameraFollowTurn = turn ?? false
10242
10264
  this.cameraFollowSeeded = false
10243
10265
  }
10244
10266
 
@@ -10400,6 +10422,49 @@ export class Engine {
10400
10422
  }
10401
10423
  }
10402
10424
 
10425
+ /**
10426
+ * The follow's turn: her FACE's direction — the head bone (頭), whatever bone
10427
+ * the position rides, since an eye under eye tracking turns back toward the
10428
+ * camera as the face turns and so never points anywhere new. MMD rests facing
10429
+ * −Z, so forward is its negated third column, placed by the model's
10430
+ * rotation (the followed bone stands in on a rig with no 頭) — as yaw and
10431
+ * elevation, eased by the follow's own lag, and each frame's change added to
10432
+ * the orbit (alpha turns with the yaw, beta with the elevation) and to the
10433
+ * offset (turned about the vertical, so a framing off to her side stays at
10434
+ * her side). Seeds without moving the camera, on the frame the follow is
10435
+ * (re)bound — cameraFollowSeeded, cleared there, is set right after this.
10436
+ */
10437
+ private turnWithFollowBone(m: Model, tau: number, deltaTime: number): void {
10438
+ const w = m.getBoneWorldMatrix("頭") ?? m.getBoneWorldMatrix(this.cameraTargetBoneName)
10439
+ if (!w) return
10440
+ const fwd = new Vec3(-w[8], -w[9], -w[10])
10441
+ Quat.rotateVecInto(m.rotation, fwd, fwd)
10442
+ const len = Math.hypot(fwd.x, fwd.y, fwd.z)
10443
+ if (len < 1e-6) return
10444
+ const yaw = Math.atan2(fwd.x, fwd.z)
10445
+ const pitch = Math.asin(Math.max(-1, Math.min(1, fwd.y / len)))
10446
+ if (!this.cameraFollowSeeded) {
10447
+ this.cameraFollowYaw = yaw
10448
+ this.cameraFollowPitch = pitch
10449
+ return
10450
+ }
10451
+ const wrap = (a: number) => a - 2 * Math.PI * Math.round(a / (2 * Math.PI))
10452
+ const k = tau > 0 ? 1 - Math.exp(-deltaTime / tau) : 1
10453
+ const dYaw = wrap(yaw - this.cameraFollowYaw) * k
10454
+ const dPitch = (pitch - this.cameraFollowPitch) * k
10455
+ this.cameraFollowYaw = wrap(this.cameraFollowYaw + dYaw)
10456
+ this.cameraFollowPitch += dPitch
10457
+ const cam = this.camera
10458
+ cam.alpha += dYaw
10459
+ cam.beta = Math.max(0.01, Math.min(Math.PI - 0.01, cam.beta - dPitch))
10460
+ const o = this.cameraTargetOffset
10461
+ const c = Math.cos(dYaw)
10462
+ const sn = Math.sin(dYaw)
10463
+ const ox = o.x
10464
+ o.x = ox * c + o.z * sn
10465
+ o.z = -ox * sn + o.z * c
10466
+ }
10467
+
10403
10468
  /** True while the orbit target is riding a model bone (setCameraFollow). */
10404
10469
  isCameraFollowing(): boolean {
10405
10470
  return this.cameraTargetModel !== null
@@ -13730,6 +13795,39 @@ export class Engine {
13730
13795
  return true
13731
13796
  }
13732
13797
 
13798
+ /**
13799
+ * Jelly eyes: her irises shrink and spring back as her lids move, swell as her
13800
+ * head turns or moves, and wobble back, and shine like a gem — two hard
13801
+ * glints, light glowing through the iris, deeper colour. MMD's 果冻眼, on any
13802
+ * PMX and under any eye graph. Null stills them again.
13803
+ *
13804
+ * Driven by her closing morphs (まばたき, the winks) and her head bone, never
13805
+ * her eyes' own turn, so eye tracking following the camera leaves it still.
13806
+ * The springs step on the scene clock, so an export bounces exactly as the
13807
+ * screen did. The iris only scales in place (see jelly-eyes.ts).
13808
+ */
13809
+ setModelJellyEyes(modelName: string, options: JellyEyeOptions | null): boolean {
13810
+ const inst = this.modelInstances.get(modelName)
13811
+ if (!inst || !this.device) return false
13812
+ if (!options) {
13813
+ inst.jellyEyes = null
13814
+ clearJellyEyes(inst.objectLight)
13815
+ this.device.queue.writeBuffer(inst.lightBuffer, 0, inst.objectLight)
13816
+ return true
13817
+ }
13818
+ if (inst.jellyEyes) {
13819
+ inst.jellyEyes.strength = clampStrength(options.strength)
13820
+ inst.jellyEyes.idle = options.idle ?? true
13821
+ }
13822
+ else inst.jellyEyes = new JellyEyes(inst.model, options)
13823
+ return true
13824
+ }
13825
+
13826
+ getModelJellyEyes(modelName: string): Required<JellyEyeOptions> | null {
13827
+ const jelly = this.modelInstances.get(modelName)?.jellyEyes
13828
+ return jelly ? { strength: jelly.strength, idle: jelly.idle } : null
13829
+ }
13830
+
13733
13831
  getModelCastShadow(modelName: string): boolean {
13734
13832
  return this.modelInstances.get(modelName)?.castShadow ?? true
13735
13833
  }
@@ -13800,6 +13898,12 @@ export class Engine {
13800
13898
  verticesChanged = inst.model.update(deltaTime, inst.isStage || inst.isPlane ? false : this.ikEnabled)
13801
13899
  inst.skinMatricesDirty = true
13802
13900
  }
13901
+ // After the pose, so the springs read this frame's blink and eye turn.
13902
+ if (inst.jellyEyes) {
13903
+ inst.jellyEyes.step(deltaTime, inst.model)
13904
+ inst.jellyEyes.write(inst.objectLight)
13905
+ this.device.queue.writeBuffer(inst.lightBuffer, 48 * 4, inst.objectLight, 48, 24)
13906
+ }
13803
13907
  animMs += performance.now() - tAnim
13804
13908
  // Material morphs ride the same weight change as vertex morphs but land in
13805
13909
  // uniform buffers, so they consume their own flag — a model whose only
@@ -15104,9 +15208,9 @@ export class Engine {
15104
15208
 
15105
15209
  // Its ObjectLight: the world's ambient, and the layers its kind draws on —
15106
15210
  // the cast on the character layer as well as the default one. Words:
15107
- // layers [0..3], ambient flag [4..7], SH [8..43], fill [44..47] — see
15108
- // materials/common.ts.
15109
- const objectLight = new Float32Array(48)
15211
+ // layers [0..3], ambient flag [4..7], SH [8..43], fill [44..47], jelly
15212
+ // eyes [48..71] — see materials/common.ts.
15213
+ const objectLight = new Float32Array(72)
15110
15214
  new Uint32Array(objectLight.buffer)[0] =
15111
15215
  isStage || isPlane || isProp ? RENDERING_LAYER_DEFAULT : (RENDERING_LAYER_DEFAULT | RENDERING_LAYER_CHARACTER) >>> 0
15112
15216
  const lightBuffer = this.device.createBuffer({
@@ -15142,6 +15246,7 @@ export class Engine {
15142
15246
  model,
15143
15247
  simulateWhileHidden: false,
15144
15248
  castShadow: true,
15249
+ jellyEyes: null,
15145
15250
  basePath,
15146
15251
  assetReader,
15147
15252
  gpuBuffers,
@@ -17397,8 +17502,16 @@ export class Engine {
17397
17502
  py += pos.y
17398
17503
  pz += pos.z
17399
17504
  }
17400
- const tau = this.cameraFollowSmoothing
17505
+ // The lag scales with how much the shot holds. Eased, the pivot trails
17506
+ // the bone by its speed × tau — nothing in a full-body shot, but in a
17507
+ // close-up of her eye it is most of the frame, and the eye swims out
17508
+ // of it as she moves. So: the full handheld lag once the frame is
17509
+ // FOLLOW_LAG_FULL_HALF tall (half height, model units — about a
17510
+ // full-body shot), shrinking linearly to rigid as the shot closes in.
17511
+ const half = this.camera.radius * Math.tan(this.camera.fov / 2)
17512
+ const tau = this.cameraFollowSmoothing * Math.min(1, half / FOLLOW_LAG_FULL_HALF)
17401
17513
  const f = this.cameraFollowPos
17514
+ if (this.cameraFollowTurn) this.turnWithFollowBone(m, tau, deltaTime)
17402
17515
  if (tau > 0 && this.cameraFollowSeeded) {
17403
17516
  // Exponential lag toward the bone: the handheld-camera feel. The
17404
17517
  // orbit pivot trails the target and eases in, never snapping.
@@ -57,6 +57,16 @@ const EYE_REAR_GATE = `
57
57
  }
58
58
  `
59
59
 
60
+ // Eye's jelly (Engine.setModelJellyEyes): the vertex shader moves the iris
61
+ // itself; the fragment only adds the glints, computed once the lighting context
62
+ // exists and added in the epilogue after the graph. Off, they are zero.
63
+ const PLAIN_SAMPLE = ` let tex_s = textureSample(diffuseTexture, diffuseSampler, input.uv);
64
+ `
65
+ const EYE_JELLY_SAMPLE = ` let rz_jelly = rzJellyEye(input.restPos);
66
+ ${PLAIN_SAMPLE}`
67
+ const EYE_JELLY_GLOW = ` let rz_jelly_glow = rzJellyGlow(rz_jelly, tex_color, tex_s.a, v, l, sun, shadow);
68
+ `
69
+
60
70
  // ── Adjust-tier uniforms — fixed 16-vec4f block (256 B) shared by the single material
61
71
  // bind group layout; non-graph draws bind a zero buffer. ──
62
72
  const STYLE_UNIFORMS_WGSL = `struct StyleUniforms { p: array<vec4f, 16> };
@@ -104,8 +114,7 @@ function prelude(renderClass: RenderClass, alphaMode: AlphaMode, blend: StyleBle
104
114
  const flip =
105
115
  renderClass === "eye" ? "" : "\n n = select(-n, n, dot(n, v) >= 0.0);"
106
116
  return `@fragment fn fs(input: VertexOutput) -> FSOut {
107
- let tex_s = textureSample(diffuseTexture, diffuseSampler, input.uv);
108
- // MMD alpha semantics: material alpha × texture alpha.
117
+ ${renderClass === "eye" ? EYE_JELLY_SAMPLE : PLAIN_SAMPLE} // MMD alpha semantics: material alpha × texture alpha.
109
118
  let alpha = material.alpha * tex_s.a;
110
119
  ${discard}
111
120
 
@@ -152,7 +161,7 @@ ${gate}
152
161
  // The normal the lamps' diffuse layer is lit by, for a principled walk to
153
162
  // compute it on the way (rzLightsDiffuseOnce). n is final from here on.
154
163
  _rzLampN = n;
155
-
164
+ ${renderClass === "eye" ? EYE_JELLY_GLOW : ""}
156
165
  `
157
166
  }
158
167
 
@@ -205,7 +214,7 @@ function epilogue(renderClass: RenderClass, alphaMode: AlphaMode, hasOpacity: bo
205
214
  // it is emission, not a surface colour, so nothing may ramp or shadow it. Zero
206
215
  // on every fragment of every material that is not dissolving, which is what
207
216
  // keeps this out of the way of a scene that never uses it.
208
- const BURN = ` + RZ_BURN_COLOR * rz_burn`
217
+ const BURN = ` + RZ_BURN_COLOR * rz_burn${renderClass === "eye" ? " + rz_jelly_glow" : ""}`
209
218
  // The scene fog, last, as the game lays it over a lit surface; an
210
219
  // emission-only graph takes none (the game's effect shaders have no fog).
211
220
  // A premultiplied colour takes its fog premultiplied too: the haze a pane
package/src/index.ts CHANGED
@@ -220,6 +220,7 @@ export {
220
220
  */
221
221
  export { EFFECT_MATH_API, PARTICLE_STRUCT_WGSL } from "./shaders/passes/hosted-api"
222
222
  export type { EyeTrackingOptions } from "./model"
223
+ export type { JellyEyeOptions } from "./jelly-eyes"
223
224
 
224
225
  /**
225
226
  * The game's own materials (src/unity): a stage package drawn by its translated