From 78a23dc4469644fba24301392f1045ec5c8ce114 Mon Sep 17 00:00:00 2001
From: pkuzic
Date: Fri, 9 Oct 2026 12:42:52 +0100
Subject: [PATCH] MONKEY (r19, Brill interior light): strict room-visibility
test for the torch shell in static_gx
Shader part of benilla-everwood 20e19334 (the rest of that commit is non-graphics).
(cherry picked from commit 20e19334, static_gx.wgsl only)
---
crates/benilla-world/src/shaders/static_gx.wgsl | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/crates/benilla-world/src/shaders/static_gx.wgsl b/crates/benilla-world/src/shaders/static_gx.wgsl
index caf191bf..5a69f584 100644
--- a/crates/benilla-world/src/shaders/static_gx.wgsl
+++ b/crates/benilla-world/src/shaders/static_gx.wgsl
@@ -446,6 +446,16 @@ fn torch_map_at(light_pos: vec3, P: vec3, N: vec3, fade_radius: f
let fixture = torch_table.positions[i].xyz;
let face = torch_face(P - fixture);
let layer = i * 6u + face;
+ // MONKEY (room visibility): the shell needs proven visibility; the shared shadow
+ // helper fails open outside its projection because ordinary shadows fade away there.
+ if (strict) {
+ let clip = torch_table.view_projs[layer] * vec4(P, 1.0);
+ if (clip.w <= 0.0) { return 0.0; }
+ let ndc = clip.xyz / clip.w;
+ if (any(abs(ndc.xy) > vec2(1.0)) || ndc.z < 0.0 || ndc.z > 1.0) {
+ return 0.0;
+ }
+ }
// MONKEY (live bank rank): count.z is CPU-ready-filtered; holes must not consume
// live cubes. Keep the projection on the static slot while compacting depth only.
let rank = countOneBits(torch_table.count.z & ((1u << i) - 1u));
@@ -460,7 +470,9 @@ fn torch_map_at(light_pos: vec3, P: vec3, N: vec3, fade_radius: f
);
let s = shadow_hook::torch_map_shadow(
torch_table.view_projs[layer], i32(depth_layer), P, N, torch_depth, torch_samp,
- TORCH_BIAS, torch_soft(), fade);
+ // MONKEY (room visibility): a shadow's artistic fade restores blocked light;
+ // a visibility test must use raw PCF, even far from the hearth behind a wall.
+ TORCH_BIAS, torch_soft(), select(fade, 1.0, strict));
// MONKEY (torch caster selection): `.w` is the slot's FADE WEIGHT. A slot ramps 0 -> 1
// over ~1/3 s when it is promoted and 1 -> 0 before it is reused, and `mix` turns that
// into the shadow appearing/dissolving instead of switching. Without it the selection