From 9c7e5fa4dae859ca4dbd6f6dc59765a7a91d1320 Mon Sep 17 00:00:00 2001
From: pkuzic
Date: Fri, 25 Sep 2026 16:56:30 +0100
Subject: [PATCH] MONKEY (fix-wet): seamless ripple wrap, no dry-weather churn,
no sheen NaN, windows stay lit
Review B3: floor(cycle) % 50 keeps the ring roll continuous across the 1000 s
wrap. B8: publish the ripple clock only while it rains. B7: nudge the wet
sheen half-vector off zero. B6: WORD_WINDOW batches are excluded from wet
darkening.
Co-Authored-By: Claude Opus 5.5
---
crates/benilla-assets/src/shaders/enhanced_water.wgsl | 4 +++-
crates/benilla-assets/src/shaders/wet_hook.wgsl | 3 ++-
crates/benilla-world/src/shaders/static_gx.wgsl | 4 +++-
crates/benilla-world/src/weather/wetness.rs | 5 ++++-
4 files changed, 12 insertions(+), 4 deletions(-)
diff --git a/crates/benilla-assets/src/shaders/enhanced_water.wgsl b/crates/benilla-assets/src/shaders/enhanced_water.wgsl
index 990bb420..411ffc0f 100644
--- a/crates/benilla-assets/src/shaders/enhanced_water.wgsl
+++ b/crates/benilla-assets/src/shaders/enhanced_water.wgsl
@@ -279,7 +279,9 @@ fn rain_ripple_normal(world_xz: vec2, footprint: f32, distance: f32, room:
let h_alt = ripple_hash(seed + 19.19);
// Each period re-rolls whether this cell rains, so the pattern does not repeat.
let cycle = wet_row.z * rate + h.x;
- let roll = ripple_hash(seed + floor(cycle) * 7.31).x;
+ // MONKEY (fix-wet): `% 50` keeps the roll sequence continuous across the 1000 s
+ // clock wrap (cycle drops by 800 / 650, both multiples of 50).
+ let roll = ripple_hash(seed + (floor(cycle) % 50.0) * 7.31).x;
if roll > rain * 0.85 {
continue;
}
diff --git a/crates/benilla-assets/src/shaders/wet_hook.wgsl b/crates/benilla-assets/src/shaders/wet_hook.wgsl
index cb108adf..4bf3b73b 100644
--- a/crates/benilla-assets/src/shaders/wet_hook.wgsl
+++ b/crates/benilla-assets/src/shaders/wet_hook.wgsl
@@ -86,7 +86,8 @@ fn wet_sheen(boost: f32, n: vec3, to_view: vec3, to_light: vec3,
if (boost <= 0.0) {
return vec3(0.0);
}
- let h = normalize(to_light + to_view);
+ // MONKEY (fix-wet): nudged so opposite light/view vectors never normalize a zero vector (NaN).
+ let h = normalize(to_light + to_view + vec3(0.0, 1.0e-4, 0.0));
let nl = max(dot(n, to_light), 0.0);
let spec = pow(max(dot(n, h), 0.0), 80.0) * smoothstep(0.0, 0.15, nl);
let fres = 0.03 + 0.97 * pow(1.0 - clamp(dot(n, to_view), 0.0, 1.0), 5.0);
diff --git a/crates/benilla-world/src/shaders/static_gx.wgsl b/crates/benilla-world/src/shaders/static_gx.wgsl
index ad217a91..9009163e 100644
--- a/crates/benilla-world/src/shaders/static_gx.wgsl
+++ b/crates/benilla-world/src/shaders/static_gx.wgsl
@@ -1995,7 +1995,9 @@ fn fragment(in: GxVsOut) -> @location(0) vec4 {
}
// MONKEY (wet): rain on sky-exposed surfaces (wet_hook.wgsl); interior, unlit and dry = untouched.
let wet = wet_hook::wet_surface(rgb, n_lit, in.world_position.xyz,
- select(1.0, 0.0, (in.word & (WORD_INTERIOR | WORD_UNLIT)) != 0u), wow_light.monkey);
+ // MONKEY (fix-wet): lit WMO windows (WORD_WINDOW) are not darkened by rain either.
+ select(1.0, 0.0, (in.word & (WORD_INTERIOR | WORD_UNLIT | WORD_WINDOW)) != 0u),
+ wow_light.monkey);
rgb = wet.albedo;
if (wet.boost > 0.0) {
rgb = min(rgb + wet_hook::wet_sheen(wet.boost, n_lit, normalize(view.world_position - in.world_position.xyz),
diff --git a/crates/benilla-world/src/weather/wetness.rs b/crates/benilla-world/src/weather/wetness.rs
index caa9c4e2..ebcf73df 100644
--- a/crates/benilla-world/src/weather/wetness.rs
+++ b/crates/benilla-world/src/weather/wetness.rs
@@ -104,7 +104,10 @@ fn wetness_tick(
(
rain,
env.wetness.unwrap_or(wet.wetness),
- env.ripple_t.unwrap_or(wet.ripple_time_s),
+ // MONKEY (fix-wet): the rings read the clock only while it rains; publishing it when
+ // dry rewrote the shared light buffer every frame in clear weather.
+ env.ripple_t
+ .unwrap_or(if rain > 0.0 { wet.ripple_time_s } else { 0.0 }),
)
} else {
(0.0, 0.0, 0.0)