MONKEY (fix-daylight): gate the district window split; per-slot terrain torch keys
Review B4: the Stormwind window split changed the default image with no switch. New cvar daylightWindowSplit (default 1 = merged behaviour, a future High-only preset member), bridged to daylight::set_window_split. Review B5: each settled exterior torch slot is keyed by the resident tiles whose footprint reaches its sphere (terrain_torch_generation_near), not a global stamp that re-rendered every slot on any stream event. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
bc7dc6509b
commit
7808f34cb2
@@ -1188,6 +1188,12 @@ pub(crate) const REGISTERED: &[Registered] = &[
|
||||
"0.0",
|
||||
"benilla's own: daylight floor for rooms inside a building, 0..1 (0 = none, the old look)",
|
||||
),
|
||||
// MONKEY (fix-daylight): the district window split, gateable (default on = merged behaviour).
|
||||
ours(
|
||||
"daylightWindowSplit",
|
||||
"1",
|
||||
"benilla's own: split city window batches into window-sized daylight apertures (applies to newly loaded buildings)",
|
||||
),
|
||||
// MONKEY (bake floor): the share of an interior batch's own MOCV bake that survives the live-
|
||||
// fixture lane. The lane throws the bake away and lets the fixtures decide, which leaves a room
|
||||
// no fixture reaches (the Lion's Pride Inn's east vestibule: MOLR 0, no claims, one faded
|
||||
|
||||
@@ -83,6 +83,8 @@ fn bridge(
|
||||
if moon.0 != video.moon_shadow_strength {
|
||||
moon.0 = video.moon_shadow_strength;
|
||||
}
|
||||
// MONKEY (fix-daylight): `daylightWindowSplit` (an atomic store; seeds read it at load).
|
||||
benilla_world::lighting::set_window_split(video.daylight_window_split);
|
||||
let want = DynamicInteriors {
|
||||
enabled: video.interior_light,
|
||||
ambient: video.interior_ambient,
|
||||
|
||||
@@ -1244,7 +1244,14 @@ fn update_torch_shadows(
|
||||
// fixture's room already occludes the ground, and a moving slot regathers every frame.
|
||||
let with_terrain = terrain_gen != 0 && slot.exterior && !moving;
|
||||
if with_terrain {
|
||||
terrain_gen.hash(&mut hash);
|
||||
// MONKEY (fix-daylight): only the tiles near THIS slot key it; the global stamp
|
||||
// re-rendered every exterior slot on any stream event anywhere.
|
||||
if let (Some(t), Some(a)) = (&ents.terrain, &ents.adt_tiles) {
|
||||
(benilla_world::terrain_stream::terrain_torch_generation_near(
|
||||
t, a, slot.pos, TORCH_RANGE,
|
||||
) ^ 0x7e44_a1d0)
|
||||
.hash(&mut hash);
|
||||
}
|
||||
}
|
||||
let key = hash.finish();
|
||||
let dirty = slot.geometry_key != Some(key);
|
||||
|
||||
@@ -394,6 +394,10 @@ pub(crate) struct VideoConfig {
|
||||
/// record table flags as enclosed. `0` restores the pre-feature look exactly; the night look is
|
||||
/// unaffected at any value (the term is scaled by the sun's own day envelope).
|
||||
pub(crate) interior_daylight: f32,
|
||||
/// MONKEY (fix-daylight): split a district's oversized window batch into window-sized
|
||||
/// daylight apertures (`daylightWindowSplit`, default on = the merged behaviour; a future
|
||||
/// High-only preset member). Applies to WMOs loaded after a change.
|
||||
pub(crate) daylight_window_split: bool,
|
||||
/// MONKEY (bake floor): the share of a WMO interior batch's OWN MOCV bake every interior-lane
|
||||
/// fragment keeps whether or not a fixture reaches it (`interiorBakeFloor`, 0..1, default
|
||||
/// **0.12**). Bridged to [`benilla_world::lighting::DynamicInteriors::bake_floor`], packed
|
||||
@@ -472,6 +476,7 @@ impl Default for VideoConfig {
|
||||
// MONKEY (enclosed day floor): calibrated so the Goldshire inn's entry floor reads
|
||||
// ~50 % of the sunlit threshold beside it — see `lighting::DAYLIGHT_LANE_SCALE`.
|
||||
interior_daylight: 0.0,
|
||||
daylight_window_split: true,
|
||||
// MONKEY (bake floor): an eighth of the authored bake — measured to lift the inn's
|
||||
// black door band from 0.019 to 0.108 x tex while moving candle-lit surfaces by
|
||||
// under 10 % (see `lighting::DynamicInteriors::bake_floor`).
|
||||
@@ -659,6 +664,8 @@ pub(crate) fn on_cvar(
|
||||
// MONKEY (enclosed day floor): 0 IS meaningful here (it restores the pre-feature look
|
||||
// exactly), unlike the two dim dials above whose 0 would be a broken-looking world.
|
||||
"interiordaylight" => cfg.interior_daylight = v.clamp(0.0, 1.0),
|
||||
// MONKEY (fix-daylight)
|
||||
"daylightwindowsplit" => cfg.daylight_window_split = ev.flag(),
|
||||
// MONKEY (bake floor): 0 IS meaningful here too (it restores the pre-feature look exactly).
|
||||
// The upper clamp matters more than usual: the packer multiplies this by `interiorGain`
|
||||
// (up to 1.5) and rides the product in a lane fraction that must stay under 0.5 after
|
||||
|
||||
@@ -802,6 +802,15 @@ where
|
||||
/// authors ALL its stained-glass windows as ONE EXT batch per wall — `g135 b11` spans
|
||||
/// 15 x 44 x 20 yd (diag 51), `g146 b10` 30 x 44 x 10 yd (diag 54) — so [`APERTURE_MAX_DIAG`]
|
||||
/// refused the batch whole and the nave took no daylight through a single window. Its windows are
|
||||
/// MONKEY (fix-daylight): the district window split's gate (default on: the merged behaviour).
|
||||
static WINDOW_SPLIT: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(true);
|
||||
|
||||
/// MONKEY (fix-daylight): `daylightWindowSplit`. Read when a placement's seeds are built, so a
|
||||
/// change applies to WMOs loaded afterwards.
|
||||
pub fn set_window_split(on: bool) {
|
||||
WINDOW_SPLIT.store(on, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// separate panes between pillars, so the batch's vertices fall into separate clusters at the
|
||||
/// plan linkage of [`plan_clusters`], and each window-sized cluster is an aperture.
|
||||
///
|
||||
@@ -812,6 +821,9 @@ fn window_boxes(positions: &[[f32; 3]], district: bool) -> Vec<([f32; 3], [f32;
|
||||
let Some((lo, hi)) = bounds(positions.iter().copied()) else {
|
||||
return Vec::new();
|
||||
};
|
||||
// MONKEY (fix-daylight): the split changes the default image (Stormwind 25 -> 33 apertures),
|
||||
// so it has its own gate, the `daylightWindowSplit` cvar ([`set_window_split`]).
|
||||
let district = district && WINDOW_SPLIT.load(std::sync::atomic::Ordering::Relaxed);
|
||||
if !district || diagonal(lo, hi) <= APERTURE_MAX_DIAG {
|
||||
return vec![(lo, hi)];
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ pub use blob::LightBlob;
|
||||
// calls now -- daylight seeds and interior<->interior doorway seeds share the per-placement budget,
|
||||
// so neither can be ranked without the other. `BleedFixture`/`BleedSeed` are the doorway lane's own
|
||||
// two types; the fixture itself still wears `DaylightFixture`.
|
||||
pub use daylight::set_window_split; // MONKEY (fix-daylight)
|
||||
pub use daylight::{
|
||||
daylight_claims, daylight_intensity, daylight_lane, daylight_point_light, daylight_reach,
|
||||
daylight_rooms, daylight_seeds, daylight_target, bleed_seeds, placement_openings, BleedFixture,
|
||||
|
||||
@@ -36,7 +36,9 @@ mod weld;
|
||||
pub(crate) mod window;
|
||||
// MONKEY (daylight: terrain torch casters): resident MCNK chunks as torch cube-map casters.
|
||||
mod torch_terrain;
|
||||
pub use torch_terrain::{append_terrain_torch_triangles, terrain_torch_generation};
|
||||
pub use torch_terrain::{
|
||||
append_terrain_torch_triangles, terrain_torch_generation, terrain_torch_generation_near,
|
||||
};
|
||||
|
||||
use collider::{finish_colliders, impassable_wall_data, terrain_collider_data};
|
||||
use furnish::furnish_tile_cells;
|
||||
|
||||
@@ -102,6 +102,38 @@ pub fn terrain_torch_generation(streamer: &TerrainStreamer, adt_tiles: &Assets<A
|
||||
sum
|
||||
}
|
||||
|
||||
/// MONKEY (fix-daylight): [`terrain_torch_generation`] over only the tiles whose footprint
|
||||
/// reaches the sphere (`center` in Bevy space), so a tile streaming far away does not re-key (and
|
||||
/// re-render) every settled exterior torch slot. Keys are `(tile_x, tile_y)`: tile_x from world y,
|
||||
/// tile_y from world x, both counted down from `32 * TILE_SIZE`.
|
||||
pub fn terrain_torch_generation_near(
|
||||
streamer: &TerrainStreamer,
|
||||
adt_tiles: &Assets<AdtTile>,
|
||||
center: Vec3,
|
||||
reach: f32,
|
||||
) -> u64 {
|
||||
use std::hash::{Hash, Hasher};
|
||||
let c = Vec3::from(benilla_assets::coords::bevy_to_wow(center));
|
||||
let t = benilla_formats::TILE_SIZE;
|
||||
let offset = 32.0 * t;
|
||||
let mut sum = 0u64;
|
||||
for (key, ts) in &streamer.tiles {
|
||||
let (tx, ty) = (key.0 as f32, key.1 as f32);
|
||||
let (x_hi, y_hi) = (offset - ty * t, offset - tx * t);
|
||||
let lo = Vec3::new(x_hi - t, y_hi - t, c.z);
|
||||
let hi = Vec3::new(x_hi, y_hi, c.z);
|
||||
if box_sphere_d2(lo, hi, c) > reach * reach {
|
||||
continue;
|
||||
}
|
||||
let mut h = std::collections::hash_map::DefaultHasher::new();
|
||||
key.hash(&mut h);
|
||||
ts.handle.id().hash(&mut h);
|
||||
adt_tiles.contains(&ts.handle).hash(&mut h);
|
||||
sum = sum.wrapping_add(h.finish());
|
||||
}
|
||||
sum
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
Reference in New Issue
Block a user