From f0e586642d25c52a5f6772275637d34d57f15ff6 Mon Sep 17 00:00:00 2001
From: pkuzic
Date: Sat, 3 Oct 2026 21:07:03 +0100
Subject: [PATCH] MONKEY (gfx): include terrain in directional shadow occlusion
Append resident ADT triangles to the cached directional caster and invalidate it when terrain residency changes. Hills were absent from the map used by volumetric lighting. Extend the cache refresh regression for terrain arriving independently of retained models.
Verified on Windows with the app library gate, play build, and Northshire/forest captures. The fixed caster contains terrain; the sampled views are compatibility checks, not closure of the tester's original mountain camera.
Co-Authored-By: GPT-6 Astra (Codex)
---
crates/benilla-app/src/world_shadow.rs | 42 ++++++++++++++++++++++----
1 file changed, 36 insertions(+), 6 deletions(-)
diff --git a/crates/benilla-app/src/world_shadow.rs b/crates/benilla-app/src/world_shadow.rs
index 8ab4a256..7a1ecd9b 100644
--- a/crates/benilla-app/src/world_shadow.rs
+++ b/crates/benilla-app/src/world_shadow.rs
@@ -2,7 +2,7 @@
//! of [`super::character_shadow`].
//!
//! It owns the shadows cast by the WORLD: the retained `static_gx` geometry (trees + buildings, cast
-//! solid), the alpha-tested foliage (leaf canopies, one caster per leaf texture), and the per-frame
+//! solid), resident terrain, the alpha-tested foliage (one caster per leaf texture), and the per-frame
//! ENTITY-resident environment (gameobjects, distance-faded doodads, WMO props). When `worldShadows`
//! is on it declares demand to the shared rig and, while the rig is live, builds those casters using
//! the rig's shared material + per-frame facts ([`ShadowFrame`]). It also drives the terrain
@@ -29,12 +29,16 @@ use bevy::render::render_resource::{
use bevy::shader::ShaderRef;
use benilla_assets::materials::WowModelMaterial;
+use benilla_assets::AdtTile;
use benilla_world::billboard::BillboardCard;
use benilla_world::interact::PickMesh;
use benilla_world::lighting::WorldShadowActive;
use benilla_world::model_render::{ModelPart, ShadowOccluder};
use benilla_world::rig_palette::{RigPalettes, RigPart, RigSkin};
use benilla_world::static_gx::StaticGx;
+use benilla_world::terrain_stream::{
+ append_terrain_torch_triangles, terrain_torch_generation, TerrainStreamer,
+};
use crate::shadow_core::{
collect_entity_geometry, empty_cutout_mesh, empty_shadow_mesh, restore_mesh_buffers,
@@ -58,19 +62,27 @@ impl Plugin for WorldShadowPlugin {
/// MONKEY (followups): the static caster rebuilds on 16 yd of camera drift, so a standing camera
/// never picks up trees and doodads that stream in later. Invalidate it whenever the retained
-/// scene's residency changes, while world shadows are on (Classic has them off, so it is inert).
+/// scene or terrain residency changes, while world shadows are on (Classic has them off).
fn refresh_streamed_shadows(
video: Res,
gx: Option>,
+ terrain: Option>,
+ tiles: Option>>,
mut lane: ResMut,
- mut seen: Local