reze-engine 0.68.5 → 0.68.7

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.
@@ -219,34 +219,43 @@ fn _rzCastShadow(wp: vec3f) -> f32 {
219
219
  // jelly its jelly eyes (Engine.setModelJellyEyes, jelly-eyes.ts), not
220
220
  // lighting but per-model all the same, so it rides here:
221
221
  // [0] left iris centre (bind space), strength — 0 is off
222
- // [1] right iris centre, scale bounce (both eyes)
222
+ // [1] right iris centre, the highlight layers' materialIds
223
+ // (a + b·4096, 0 none)
223
224
  // [2] left iris half extents, closure, eye bone (−1 none)
224
225
  // [3] the right's
225
- // [4] left squash, 0, 0, shine [5] the right's
226
- struct ObjectLight { layers: vec4u, ambient: vec4f, sh: array<vec4f, 9>, fill: vec4f, jelly: array<vec4f, 6> }
226
+ // [4] left scale x, y, twist, highlight turn (radians)
227
+ // [5] the right's
228
+ // [6] left drift x, y, highlight's own offset x, y (bind units)
229
+ // [7] the right's
230
+ // [8] left eye bone's head (the roll's pivot), roll (radians)
231
+ // [9] the right's
232
+ // [10] left highlight scale x, y, forward push (bind units), 0
233
+ // [11] the right's
234
+ struct ObjectLight { layers: vec4u, ambient: vec4f, sh: array<vec4f, 9>, fill: vec4f, jelly: array<vec4f, 12> }
227
235
  @group(1) @binding(1) var<uniform> objectLight: ObjectLight;
228
236
 
229
- /** What the glints need to know about a fragment of the eye render class. */
230
- struct RzJelly {
231
- /** Where in the iris this fragment is, in iris radii. */
232
- src: vec2f,
233
- /** 1 inside the iris, easing to 0 at its rim (0 everywhere when off). */
234
- w: f32,
235
- closure: f32,
236
- shine: f32,
237
- };
237
+ fn _rzJellyTurn(q: vec2f, a: f32) -> vec2f {
238
+ let c = cos(a);
239
+ let s = sin(a);
240
+ return vec2f(c * q.x - s * q.y, s * q.x + c * q.y);
241
+ }
238
242
 
239
243
  /**
240
244
  * Where the jelly puts an iris vertex. The iris is the part of the eye the eye
241
245
  * bones carry, so it is moved as a whole, in bind space before skinning:
242
- * shrunk about its centre while a lid moves, a little more down than across,
243
- * grown past rest as it springs back, and swelling as a turn or her motion
244
- * knocks it — scale only, in place, inside the white, which stays where it is. Vertices take it by
245
- * their weight on the eye bones (jelly[2].w, jelly[3].w), so nothing else on
246
- * the face moves.
246
+ * scaled about its centre (the CPU composes the blink's spring, the lid's
247
+ * press and her motion's knock into one scale), turned and drifted a hair on
248
+ * the spring, brought forward a hair, and rolled about the eye bone with a
249
+ * closing expression — inside the white, which stays where it is. A highlight
250
+ * layer (this draw's material is one of jelly[1].w's) takes the iris's move,
251
+ * then its own scale, turn and tremble, before the roll. Vertices take it by their weight on the eye bones
252
+ * (jelly[2].w, jelly[3].w), so nothing else on the face moves.
247
253
  */
248
254
  fn rzJellyVertex(p: vec3f, joints: vec4<u32>, nw: vec4f) -> vec3f {
249
255
  let j = objectLight.jelly;
256
+ let ids = u32(j[1].w);
257
+ let mid = u32(material.materialId);
258
+ let hl = mid > 0u && (mid == (ids & 4095u) || mid == (ids >> 12u));
250
259
  var out = p;
251
260
  for (var e = 0u; e < 2u; e++) {
252
261
  let bone = j[2u + e].w;
@@ -258,97 +267,33 @@ fn rzJellyVertex(p: vec3f, joints: vec4<u32>, nw: vec4f) -> vec3f {
258
267
  // A rig that carries both eyes on one bone: each vertex follows its own side.
259
268
  if (j[2].w == j[3].w && e == 0u && distance(p.xy, j[1].xy) < distance(p.xy, j[0].xy)) { continue; }
260
269
  if (j[2].w == j[3].w && e == 1u && distance(p.xy, j[0].xy) <= distance(p.xy, j[1].xy)) { continue; }
261
- let st = j[4u + e];
262
- // The squash, nearly round: it shrinks both ways, a little more down than
263
- // across, so the iris keeps its circle and only leans oval; and the bounce
264
- // both eyes share (jelly[1].w). Held under 1.08 so a swell cannot rise
265
- // past the upper lid.
266
- let k = clamp(vec2f(1.0 - 0.8 * st.x, 1.0 - st.x) + j[1].w, vec2f(0.78, 0.75), vec2f(1.08, 1.08));
267
- let moved = c.xy + (p.xy - c.xy) * k;
268
- out = vec3f(mix(p.xy, moved, w), p.z);
270
+ let t = j[4u + e];
271
+ let m = j[6u + e];
272
+ let h = j[10u + e];
273
+ var q = _rzJellyTurn((p.xy - c.xy) * t.xy, t.z) + m.xy;
274
+ // Forward is −Z (MMD faces −Z).
275
+ var push = h.z;
276
+ if (hl) {
277
+ // Sliding over the iris's dome on its own, a highlight sheet a hair in
278
+ // front of it would sink into it where the dome bulges more and the two
279
+ // fight for depth; so it comes forward by as far as it slid — a slope of
280
+ // one, steeper than the dome anywhere it draws.
281
+ let before = q;
282
+ q = _rzJellyTurn(q * h.xy, t.w) + m.zw;
283
+ push += length(q - before);
284
+ }
285
+ var moved = vec3f(c.xy + q, p.z - push);
286
+ let roll = j[8u + e];
287
+ if (roll.w != 0.0) {
288
+ let d = moved - roll.xyz;
289
+ let rd = _rzJellyTurn(d.yz, roll.w);
290
+ moved = roll.xyz + vec3f(d.x, rd.x, rd.y);
291
+ }
292
+ out = mix(p, moved, w);
269
293
  }
270
294
  return out;
271
295
  }
272
296
 
273
- /**
274
- * The glints' frame for an eye fragment: its place in the iris in iris radii,
275
- * from the bind-space position, which the jelly moved the iris with — so the
276
- * glints ride the sloshing iris. Strength 0 gives w 0, which turns them off.
277
- */
278
- fn rzJellyEye(rp: vec3f) -> RzJelly {
279
- let j = objectLight.jelly;
280
- let qL = (rp.xy - j[0].xy) / max(j[2].xy, vec2f(1e-4));
281
- let qR = (rp.xy - j[1].xy) / max(j[3].xy, vec2f(1e-4));
282
- let right = dot(qR, qR) < dot(qL, qL);
283
- let a = select(qL, qR, right);
284
- let st = select(j[4], j[5], right);
285
- let w = (1.0 - smoothstep(0.85, 1.0, length(a))) * select(0.0, 1.0, j[0].w > 0.0);
286
- return RzJelly(a, w, select(j[2].z, j[3].z, right), st.w);
287
- }
288
-
289
- /**
290
- * The jelly shine, added after the graph: two hard glints where the light
291
- * would catch a bead — placed by the half vector in the head's frame, so they
292
- * hold still while the iris turns under them — and the eye brightening while
293
- * it wobbles. Flat, cel discs rather than a gradient lobe; HDR, so bloom
294
- * takes them. Shut eyes catch nothing.
295
- */
296
- fn rzJellyGlow(jj: RzJelly, tex: vec3f, alpha: f32, v: vec3f, l: vec3f, sun: vec3f, shadow: f32) -> vec3f {
297
- let strength = objectLight.jelly[0].w;
298
- var right = vec3f(1.0, 0.0, 0.0);
299
- var up = vec3f(0.0, 1.0, 0.0);
300
- var fwd = vec3f(0.0, 0.0, -1.0);
301
- if (material.headBoneIndex >= 0.0) {
302
- let hm = skinMats[u32(material.headBoneIndex)];
303
- right = normalize(hm[0].xyz);
304
- up = normalize(hm[1].xyz);
305
- fwd = -normalize(hm[2].xyz);
306
- }
307
- let h = normalize(l + v);
308
- // Held in the middle and lower iris: the top of an iris sits under the upper
309
- // lid, and a glint there — or its bloom halo — pokes out over it.
310
- var g = vec2f(dot(h, right), dot(h, up)) * 0.6;
311
- g = g * min(1.0, 0.25 / max(length(g), 1e-6)) - vec2f(0.0, 0.1);
312
- g.y = min(g.y, 0.0);
313
- let aa = max(length(fwidth(jj.src)), 1e-4);
314
- let glint = 1.0 - smoothstep(0.16 - aa, 0.16 + aa, distance(jj.src, g));
315
- let second = 1.0 - smoothstep(0.07 - aa, 0.07 + aa, distance(jj.src, -0.55 * g + vec2f(0.1, -0.12)));
316
- let facing = saturate(dot(fwd, v) * 2.0);
317
- let key = clamp(dot(sun, vec3f(0.2126, 0.7152, 0.0722)), 0.3, 2.0);
318
- let lit = facing * key * (0.35 + 0.65 * shadow);
319
- // HDR, well past 1, so bloom takes them as the hard sparkle of a gem.
320
- // Bright enough for bloom to catch, not so bright its halo spreads past the
321
- // iris onto the lid.
322
- let glints = (glint + 0.8 * second) * lit * (1.8 + 0.8 * jj.shine);
323
- // Gone as the lid starts down, not only once shut: a half-closed smile puts
324
- // the lid right over where the glint sits.
325
- let open = 1.0 - smoothstep(0.1, 0.45, jj.closure);
326
- // Only where the eye's own picture is (its alpha), never on the iris's top
327
- // rim, and gone as the lid comes down.
328
- let solid = smoothstep(0.5, 0.9, alpha);
329
- let onEye = solid * (1.0 - smoothstep(0.05, 0.35, jj.src.y));
330
- // Light through the jelly: the light enters where the glint is and comes out
331
- // the far side, so the iris glows in its own colour in a crescent opposite
332
- // the glint — the lit-from-within look of a gem, or a jelly.
333
- let r = length(jj.src);
334
- let away = -normalize(g + vec2f(0.0, -1e-3));
335
- let crescent = smoothstep(0.15, 0.7, dot(jj.src, away)) * (1.0 - smoothstep(0.8, 1.0, r));
336
- let through = tex * crescent * (2.2 + 1.0 * jj.shine) * lit;
337
- // And the colour runs deeper: the iris's own hue pushed, never its greys.
338
- let lum = dot(tex, vec3f(0.2126, 0.7152, 0.0722));
339
- let vivid = max(tex - vec3f(lum), vec3f(0.0)) * 1.2;
340
- // The glass over it: a broad soft sheen across the upper iris, under the
341
- // lid line, the dome of a bead catching the room.
342
- let dome = (1.0 - smoothstep(0.0, 0.5, distance(jj.src * vec2f(0.8, 1.4), vec2f(0.0, 0.2)))) * 0.45 * lit;
343
- // The glints keep off the iris's top; the soft light (sheen, glow through,
344
- // colour) has no halo to spill, so it only needs the eye's own picture.
345
- let gem = (vec3f(glints) * onEye + (vec3f(dome) + through + vivid) * solid) * open * jj.w;
346
- // The whole eye shines a little brighter, more while it wobbles — not just
347
- // the iris, so this one is not held to it (jj.w).
348
- let lift = tex * (0.25 + 0.35 * jj.shine) * select(0.0, 1.0, strength > 0.0);
349
- return (gem + lift) * min(strength, 1.0);
350
- }
351
-
352
297
  /** The light arriving at a surface of this object facing n: its own ambient
353
298
  * where loaded data gave it one (a stage's probes, a character's), else the
354
299
  * world's. The same polynomial rzWorldAmbient evaluates. */