Merge branch 'gfx/1009-unlit' into gfx/1009-upstream
This commit is contained in:
+1
-1
@@ -15,7 +15,7 @@ behind each constant.
|
||||
| Lane | What you see | Main switch |
|
||||
|---|---|---|
|
||||
| Sun shadows, characters | real silhouettes for units instead of the oval blob | `characterShadows` |
|
||||
| Sun shadows, world | trees, buildings and alpha-tested foliage cast; baked terrain shadows step aside; the static caster cache also refreshes when streamed geometry arrives, so a standing camera picks up late trees (`world_shadow.rs::refresh_streamed_shadows`, independent of volumetric fog) | `worldShadows` |
|
||||
| Sun shadows, world | trees, buildings and alpha-tested foliage cast; baked terrain shadows step aside, and so does the MCSH 0.5 sun dim on models standing on them (`wow_model.wgsl` / `static_gx.wgsl`, terrain's gate and dusk weight); the static caster cache also refreshes when streamed geometry arrives, so a standing camera picks up late trees (`world_shadow.rs::refresh_streamed_shadows`, independent of volumetric fog) | `worldShadows` |
|
||||
| Dynamic interiors | WMO rooms are lit per fragment by their own fixtures with a soft falloff, gated per room so light does not leak through walls | `interiorLight`, `interiorRoomGate` |
|
||||
| Synthesised lights | torches, braziers, lanterns, campfires and lampposts emit a light in the colour of their flame, with flicker | `fireLightGain`, `fireFlicker` |
|
||||
| Torch shadows | cube-map shadows from point lights onto buildings, models and terrain, with a contact-hardening penumbra and a shadow floor | `interiorShadows`, `exteriorShadows`, `torchShadowStrength`, `interiorShadowSoft` |
|
||||
|
||||
@@ -716,7 +716,21 @@ fn pin_scene(
|
||||
return;
|
||||
};
|
||||
debug.lighting.follow_server_time = false;
|
||||
debug.lighting.manual_minute = scenario.minute;
|
||||
// MONKEY (capture framing): `WOW_CAPTURE_MINUTE=<0..1440>`, `WOW_CAPTURE_EYE=x,y,z` and
|
||||
// `WOW_CAPTURE_LOOK=x,y,z` (raw WoW coords) re-aim a scenario without a rebuild, for framing a
|
||||
// reported spot; a scenario's baseline never sets them.
|
||||
let triple = |var: &str| {
|
||||
std::env::var(var).ok().and_then(|s| {
|
||||
let v: Vec<f32> = s.split(',').filter_map(|x| x.trim().parse().ok()).collect();
|
||||
(v.len() == 3).then(|| [v[0], v[1], v[2]])
|
||||
})
|
||||
};
|
||||
debug.lighting.manual_minute = std::env::var("WOW_CAPTURE_MINUTE")
|
||||
.ok()
|
||||
.and_then(|s| s.trim().parse().ok())
|
||||
.unwrap_or(scenario.minute);
|
||||
let scene_eye = triple("WOW_CAPTURE_EYE").unwrap_or(scenario.eye);
|
||||
let scene_look = triple("WOW_CAPTURE_LOOK").unwrap_or(scenario.look);
|
||||
|
||||
// `WOW_MM_PROBE=x,y,z` (raw WoW coords) places an active player there, so the interior
|
||||
// minimap renders server-less, with the camera above so the WMO streams in around it.
|
||||
@@ -750,7 +764,7 @@ fn pin_scene(
|
||||
* Quat::from_rotation_x(-req.el_deg.to_radians());
|
||||
(look + orbit * (Vec3::Z * req.dist), look)
|
||||
}
|
||||
_ => (wow_to_bevy(scenario.eye), wow_to_bevy(scenario.look)),
|
||||
_ => (wow_to_bevy(scene_eye), wow_to_bevy(scene_look)),
|
||||
};
|
||||
for mut t in &mut cam {
|
||||
*t = Transform::from_translation(eye).looking_at(look, Vec3::Y);
|
||||
|
||||
@@ -1280,6 +1280,29 @@ pub(super) const ON_DEMAND: &[Scenario] = &[
|
||||
name: "redridge-lake", map: Some(MAP_AZEROTH),
|
||||
eye: [-9350.0, -2340.0, 82.0], look: [-9500.0, -2650.0, 50.0], minute: 720, ui: None,
|
||||
},
|
||||
// MONKEY (gfx-1009 unlit): Lakeshire's report sites. The sunken `lionstatue.mdx` (MDDF uid
|
||||
// 83242, (-9355.5, -2246.1, 47.9), ~8 yd under the 56.16 surface) seen from the water surface
|
||||
// and from under it, and the gryphon master's two `gryphonroost01.mdx` (uids 325784/325785).
|
||||
Scenario {
|
||||
name: "lakeshire-statue", map: Some(MAP_AZEROTH),
|
||||
eye: [-9318.0, -2238.0, 59.0], look: [-9356.0, -2247.0, 49.0], minute: 720, ui: None,
|
||||
},
|
||||
Scenario {
|
||||
name: "lakeshire-statue-dusk", map: Some(MAP_AZEROTH),
|
||||
eye: [-9318.0, -2238.0, 59.0], look: [-9356.0, -2247.0, 49.0], minute: 1170, ui: None,
|
||||
},
|
||||
Scenario {
|
||||
name: "lakeshire-statue-under", map: Some(MAP_AZEROTH),
|
||||
eye: [-9343.0, -2254.0, 52.0], look: [-9356.0, -2246.0, 48.5], minute: 720, ui: None,
|
||||
},
|
||||
Scenario {
|
||||
name: "lakeshire-gryphons", map: Some(MAP_AZEROTH),
|
||||
eye: [-9407.0, -2250.0, 72.5], look: [-9422.5, -2237.0, 69.5], minute: 720, ui: None,
|
||||
},
|
||||
Scenario {
|
||||
name: "lakeshire-gryphons-dusk", map: Some(MAP_AZEROTH),
|
||||
eye: [-9407.0, -2250.0, 72.5], look: [-9422.5, -2237.0, 69.5], minute: 1170, ui: None,
|
||||
},
|
||||
// Duskwood: the road into Darkshire, the zone's short dark fog.
|
||||
Scenario {
|
||||
name: "duskwood-road", map: Some(MAP_AZEROTH),
|
||||
|
||||
@@ -1417,7 +1417,17 @@ fn fragment(in: WowVsOut, @builtin(front_facing) is_front: bool) -> WowFragOut {
|
||||
let mat_shade = select(0.0, 1.0, m.sun_scale.x < 0.5);
|
||||
let shade_t = max(mat_shade, inst_shade);
|
||||
let mid_band = m.sun_scale.x >= 0.5 && m.sun_scale.x < 0.85;
|
||||
let intensity = min(select(mix(2.5, 0.5, shade_t), 1.0, mid_band), 1.0);
|
||||
let intensity_ref = min(select(mix(2.5, 0.5, shade_t), 1.0, mid_band), 1.0);
|
||||
// MONKEY (mcsh model shade): the 0.5 band is the MCSH bit under the model, the same baked
|
||||
// shadow `terrain.wgsl` drops while the realtime world lane is on (`sh_c16.w > 0.5`, weighted
|
||||
// by the sun lane so the bake returns at night). Keeping it here left a doodad standing on its
|
||||
// own baked shadow (Lakeshire's gryphon roosts) dimmed twice, by the bake AND the realtime map,
|
||||
// on ground that no longer shows the bake. Only that band is < 1, so lit, matte, interior and
|
||||
// rig lanes are untouched; with the lane off this is the reference value bit for bit.
|
||||
let mcsh_suppress = select(0.0, shadow_hook::sun_shadow_w(wow_light.fog_params.z),
|
||||
wow_light.sh_c16.w > 0.5);
|
||||
let intensity = select(intensity_ref, mix(intensity_ref, 1.0, mcsh_suppress),
|
||||
mcsh_suppress > 0.0);
|
||||
// One `intensity` multiply covers every sun band (never I²); the c10 `.w` ambient does not
|
||||
// scale. `pack_model_core_rows` packs the same closed form.
|
||||
let sun_dc = wow_light.grade.yzw * intensity;
|
||||
|
||||
@@ -2891,6 +2891,30 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
/// MONKEY (mcsh model shade): the model receivers drop the MCSH 0.5 sun band on exactly the
|
||||
/// gate terrain drops its MCSH on (`sh_c16.w > 0.5`, weighted by the sun lane), so a doodad on
|
||||
/// its own baked shadow is not dimmed twice once the realtime world lane has replaced the bake.
|
||||
#[test]
|
||||
fn model_receivers_drop_the_mcsh_band_on_terrains_gate() {
|
||||
const TERRAIN: &str = include_str!("../../../benilla-assets/src/shaders/terrain.wgsl");
|
||||
const MODEL: &str = include_str!("../../../benilla-assets/src/shaders/wow_model.wgsl");
|
||||
const STATIC_GX: &str = include_str!("../shaders/static_gx.wgsl");
|
||||
let squash = |s: &str| s.split_whitespace().collect::<String>();
|
||||
assert!(squash(TERRAIN).contains("letworld_shadow_lane=wow_light.sh_c16.w>0.5;"));
|
||||
let gate = "letmcsh_suppress=select(0.0,shadow_hook::sun_shadow_w(wow_light.fog_params.z),\
|
||||
wow_light.sh_c16.w>0.5);";
|
||||
let apply = "letintensity=select(intensity_ref,mix(intensity_ref,1.0,mcsh_suppress),\
|
||||
mcsh_suppress>0.0);";
|
||||
for (name, shader) in [("wow_model.wgsl", MODEL), ("static_gx.wgsl", STATIC_GX)] {
|
||||
let s = squash(shader);
|
||||
assert!(
|
||||
s.contains(gate),
|
||||
"{name} must gate the MCSH band like terrain"
|
||||
);
|
||||
assert!(s.contains(apply), "{name} must lift only through the gate");
|
||||
}
|
||||
}
|
||||
|
||||
/// MONKEY (enclosed day floor): `interiorDaylight` rides the FRACTION of the interior lane's
|
||||
/// on/off word, and every existing decode of that word must be blind to it.
|
||||
///
|
||||
|
||||
@@ -2033,10 +2033,16 @@ fn fragment(in: GxVsOut) -> @location(0) vec4<f32> {
|
||||
// Deviation: the `min(I, 1)` cap, as in wow_model.wgsl, since lifting it takes sun-facing
|
||||
// surfaces past 1.0; Matte keeps its own bit so it stays 1.0 if the cap goes.
|
||||
let shade_t = select(1.0, 0.0, (in.word & WORD_SHADE_LIT) != 0u);
|
||||
let intensity = min(
|
||||
let intensity_ref = min(
|
||||
select(mix(2.5, 0.5, shade_t), 1.0, (in.word & WORD_MATTE) != 0u),
|
||||
1.0,
|
||||
);
|
||||
// MONKEY (mcsh model shade): the MCSH 0.5 steps aside with terrain's MCSH while the world
|
||||
// shadow lane is on; see `wow_model.wgsl` (same expression, same gate).
|
||||
let mcsh_suppress = select(0.0, shadow_hook::sun_shadow_w(wow_light.fog_params.z),
|
||||
wow_light.sh_c16.w > 0.5);
|
||||
let intensity = select(intensity_ref, mix(intensity_ref, 1.0, mcsh_suppress),
|
||||
mcsh_suppress > 0.0);
|
||||
let sun_dc = wow_light.grade.yzw * intensity;
|
||||
let sun_lobe = vec3<f32>(
|
||||
wow_light.sh_c10_r.w + sun_dc.x
|
||||
|
||||
Reference in New Issue
Block a user