From c32d0077fc7f4be04f522270d5e0e22a717d8b94 Mon Sep 17 00:00:00 2001
From: pkuzic
Date: Thu, 10 Sep 2026 22:50:17 +0100
Subject: [PATCH] =?UTF-8?q?MONKEY:=20lighting=20round=202=20=E2=80=94=20se?=
=?UTF-8?q?ams,=20flicker,=20outdoor=20torch=20shadows,=20perf=20knobs?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Seams
- An exterior-class MOBA batch inside an interior group (the inn's cellar stair) took the
night sky; its authored day weight is now scaled by the sun, so it joins the room law after
dark. Exterior-class SHELL groups keep the sky law (moving them to the room law painted
facades black at dusk).
- Containment claims cross portals larger than 28 yd² (floor splits, gallery voids) at full
weight, so a floor cut by a group boundary lights continuously. interiorDebug 4 colours WMO
fragments by lane. wmolights prints group names, lane, portal areas.
Fire
- Flame lights flicker: band-limited sines + smoothed noise per fixture, candle fast/light to
bonfire slow/deep, seeded by position, mean exactly 1; lamps never; authored fixtures only
when warm. Cvar fireFlicker.
Outdoor torch shadows (step a)
- At night exterior fire lights become cube-map casters (half the map budget), WMO exteriors
and models receive them with a world-distance fade; held torches never cast; a fire's own
model is excluded from its map; daylight is bit-identical. Cvar exteriorShadows.
Performance
- Torch lane: static-scene fingerprint walk gated on a residency/part census (was 4 full
walks per frame), entity caster rebuilt at interiorShadowEntityRate Hz (30) and gathered
only within each fixture's reach, receiver shadow taps skipped at zero contribution, table
and claim uploads and the torch bind group skipped when unchanged. Live bank sized to 8
dynamic slots (VRAM 192 -> 144 MiB).
- Sun lanes: live cvars shadowMapSize (default 2048, was 4096), shadowFilter,
characterShadowRate / worldShadowRate (30 Hz proxy rebuilds), shadowCasterReach.
- Measured (inn, 1080p): debug 45 -> 61 FPS, release 83 FPS with everything on.
Co-Authored-By: Claude Fable 5.1
---
crates/benilla-app/src/character_shadow.rs | 28 +-
crates/benilla-app/src/cvars.rs | 128 +++-
crates/benilla-app/src/dynamic_interior.rs | 4 +
crates/benilla-app/src/entities.rs | 3 +
crates/benilla-app/src/entities/attach/mod.rs | 8 +-
.../benilla-app/src/entities/carried_light.rs | 79 ++-
.../src/entities/equipment/spawn.rs | 4 +-
crates/benilla-app/src/entities/wmo_props.rs | 18 +-
crates/benilla-app/src/portrait/glue_booth.rs | 1 +
crates/benilla-app/src/shadow_core.rs | 267 ++++++-
crates/benilla-app/src/torch_shadow.rs | 670 ++++++++++++++++--
crates/benilla-app/src/video.rs | 82 ++-
crates/benilla-app/src/world_shadow.rs | 24 +-
crates/benilla-assets/src/m2.rs | 24 +-
.../src/shaders/shadow_hook.wgsl | 22 +-
.../benilla-assets/src/shaders/wow_model.wgsl | 212 +++++-
.../src/bin/benilla-extract/scan/lighting.rs | 73 +-
crates/benilla-formats/src/room_claim.rs | 195 ++++-
crates/benilla-world/src/lighting/flicker.rs | 447 ++++++++++++
.../src/lighting/global_light.rs | 151 +++-
crates/benilla-world/src/lighting/mod.rs | 7 +-
.../benilla-world/src/shaders/static_gx.wgsl | 383 +++++++++-
crates/benilla-world/src/static_gx/bake.rs | 2 +
crates/benilla-world/src/static_gx/mod.rs | 18 +-
crates/benilla-world/src/static_gx/render.rs | 133 +++-
.../src/static_gx/torch_depth.rs | 266 ++++++-
.../src/terrain_stream/spawn/assemble.rs | 14 +-
.../src/terrain_stream/spawn/fx.rs | 25 +
.../src/terrain_stream/spawn/mod.rs | 3 +
29 files changed, 3084 insertions(+), 207 deletions(-)
create mode 100644 crates/benilla-world/src/lighting/flicker.rs
diff --git a/crates/benilla-app/src/character_shadow.rs b/crates/benilla-app/src/character_shadow.rs
index 56a8209a..149dda4a 100644
--- a/crates/benilla-app/src/character_shadow.rs
+++ b/crates/benilla-app/src/character_shadow.rs
@@ -17,7 +17,7 @@ use benilla_world::rig_palette::{RigPalettes, RigPart, RigSkin};
use crate::shadow_core::{
collect_entity_geometry, empty_shadow_mesh, restore_mesh_buffers, shadow_trace,
- spawn_solid_caster, take_mesh_buffers, ShadowDemand, ShadowFrame, ShadowSet,
+ spawn_solid_caster, take_mesh_buffers, RebuildRate, ShadowDemand, ShadowFrame, ShadowSet,
};
use crate::video::VideoConfig;
@@ -37,11 +37,15 @@ struct CharacterLane {
mesh: Option>,
/// [`shadow_trace`]'s change detector: (tris, admitted, rejected).
traced: (u32, u32, u32),
+ /// MONKEY (sun shadow perf): the `characterShadowRate` cadence gate — see the rebuild comment
+ /// in [`collect_character_shadows`].
+ rate: RebuildRate,
}
#[allow(clippy::too_many_arguments)]
fn collect_character_shadows(
video: Res,
+ time: Res