From 186c7de6f4bf158aed82948dd5c187c041dcf5fe Mon Sep 17 00:00:00 2001 From: mchughalex <25671999+mchughalex@users.noreply.github.com> Date: Wed, 22 Jul 2026 17:49:59 +0100 Subject: [PATCH] render: stand the static sun shadows down in venues with no sane world lighting capture --- src/skate3_native_scene.cpp | 5 +++++ src/skate3_native_scene.h | 7 +++++++ src/skate3_native_scene_gpu.cpp | 13 +++++++++++-- src/skate3_native_scene_state.h | 6 ++++++ 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/skate3_native_scene.cpp b/src/skate3_native_scene.cpp index 9cea3a9..adce87e 100644 --- a/src/skate3_native_scene.cpp +++ b/src/skate3_native_scene.cpp @@ -4853,6 +4853,7 @@ void OnDrawDone(uint8_t* base, uint32_t func, uint32_t r4, uint32_t r5, uint32_t std::memcpy(g_shadow_rows, rows, sizeof(rows)); g_shadow_have = true; g_shadow_frame_done = true; + g_shadow_rows_frame = g_guest_frame; } } // tree / proxyworld frame rows (see FrameScene::family_rows): their PS @@ -9815,6 +9816,10 @@ void BuildFrameScene(uint8_t* base, const SubmitRecord* records, size_t count) { if (g_shadow_have) { std::memcpy(scene.shadow_rows, g_shadow_rows, sizeof(g_shadow_rows)); scene.shadow_valid = true; + // Same freshness pattern as the char CSM biases below: outdoor scenes + // re-capture every frame, so anything past a couple of seconds means the + // current venue's environment shader never produces a sane bank. + scene.shadow_fresh = g_guest_frame - g_shadow_rows_frame <= 120; } // Character CSM receive biases (see the header comment): frame-coherent, // served while fresh enough that a few capture-less frames don't flap the diff --git a/src/skate3_native_scene.h b/src/skate3_native_scene.h index 693d9f1..f90267e 100644 --- a/src/skate3_native_scene.h +++ b/src/skate3_native_scene.h @@ -321,6 +321,13 @@ struct FrameScene { // c5.x = depth bias, c8 = shadow color + floor; c6 = sun dir, c7 = camera, // captured for validation only). bool shadow_valid = false; + // True while the rows above were captured within the last ~2 s of guest + // frames. shadow_valid alone latches forever, so interior venues whose + // environment banks never pass the capture sanity gate (park-editor + // warehouses: near-zero sun, exposure ~0.001) serve stale outdoor rows; + // sun-derived features (the native static sun-shadow map) must stand down + // there instead of shading a sunless interior with an outdoor sun. + bool shadow_fresh = false; // Extended to c0..c11: c10.x = scene exposure (2.5), c11.y = the material // multiplier (1.0), consumed by the exact world-shading tone chain. float shadow_rows[48] = {}; diff --git a/src/skate3_native_scene_gpu.cpp b/src/skate3_native_scene_gpu.cpp index 22a361d..47afee4 100644 --- a/src/skate3_native_scene_gpu.cpp +++ b/src/skate3_native_scene_gpu.cpp @@ -7970,8 +7970,17 @@ bool RenderScene(const NativeGuestOutputRenderContext& context, void* /*user_dat REXCVAR_GET(skate3_native_render_scene_shadow_pcss_min_texel)); } // Native static sun-shadow rows (nsm_x/y/z + params, cb[144..163]); - // nsm_p.x = 0 disables the term in every branch. - if (g_r.static_sun_valid) { + // nsm_p.x = 0 disables the term in every branch (the scene receive, the + // character branch and the volumetric shafts all read this slice). + // Gated on FRESH world rows, not just shadow_valid: interior venues + // (park-editor warehouses) never produce a sane environment bank, so the + // sun direction here would be a stale outdoor capture. The map then + // shades the whole interior as roof-shadowed, and with no game shadow + // pass this frame sh_color is zero, so the exact env families clamp + // min(lm^2, s + sh_color) to pure black - the missing walls/floor. + // The game itself never sun-shadows static world geometry indoors; + // standing the map down restores its exact lm^2 shading. + if (g_r.static_sun_valid && scene.shadow_fresh) { std::memcpy(cb + 144, g_r.nsm_rows, sizeof(g_r.nsm_rows)); cb[156] = std::clamp( float(REXCVAR_GET(skate3_native_render_scene_shadow_static_strength)), diff --git a/src/skate3_native_scene_state.h b/src/skate3_native_scene_state.h index b821257..df1e1fc 100644 --- a/src/skate3_native_scene_state.h +++ b/src/skate3_native_scene_state.h @@ -291,6 +291,12 @@ inline float g_outline_color[4] = {0.21569f, 0.64706f, 1.0f, 1.0f}; inline float g_shadow_rows[48] = {}; inline bool g_shadow_have = false; inline bool g_shadow_frame_done = false; +// Guest frame of the last SANE capture. Interior venues (the park-editor +// warehouses) run a different environment shader whose bank never passes the +// sanity gate (near-zero sun, exposure ~0.001), so the rows latch stale +// outdoor state there; consumers that must not act on stale sun state key +// off this freshness stamp (see FrameScene::shadow_fresh). +inline uint64_t g_shadow_rows_frame = 0; // Frame-global rows of the tree / proxyworld shader families (see // FrameScene::family_rows), captured from their PS banks when a draw with // the matching debug path runs. Guest render thread only.