MONKEY (skybox): zoneSkyboxes cvar, Advanced Graphics row, skybox- capture scenarios

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
pkuzic
2026-09-25 03:23:35 +01:00
co-authored by Claude Opus 5.5
parent 88ceb9d2c2
commit bd1c6de9f8
8 changed files with 168 additions and 5 deletions
+1
View File
@@ -24,6 +24,7 @@ behind each constant.
| Moon shadows | at night the same shadow rig re-aims at the moon and casts a faint shadow; dims only the night sky term, never point lights | `moonShadowStrength` |
| Ground-effect spells | Flamestrike, Rain of Fire, Consecration, Flare and fire traps light the ground for their duration; frost and nature areas stay dark | `spellLightGain` |
| Volumetric fog | near-field haze that converges on the zone fog colour (clear within 10 yd, full by 150 yd; mistier at dawn and in bad weather, faint indoors) and sun/moon light shafts through gaps, sampled from the shadow map; own fullscreen pass after the main pass (`benilla-app/src/volumetric_fog.rs`) | Advanced Graphics → Volumetric Fog (Off/Low/High), cvar `volumetricFog`, env `WOW_VOLFOG=0\|1\|2` |
| Zone skyboxes | the painted sky a zone's `LightParams` names (clear-weather slot, storm slot lerped by the storm blend) for the living, fading in by the Light sphere falloff with the modern client's crossfade (ghost > WMO > zone); extended `LightSkybox.dbc` flags `0x1` day cycle, `0x2` keep sun/moon/stars/clouds, `0x4` fog-colour horizon cone, plus a celestial second model; every skybox now plays its bones, texture transforms, colour and alpha tracks (`benilla-world/src/skybox.rs`, `skybox_anim.rs`, `benilla-formats/src/light/skybox.rs`) | Advanced Graphics → Zone Skyboxes, cvar `zoneSkyboxes` (0 Off default, 1 On; High = 1), env `WOW_ZONE_SKYBOXES=0\|1` |
| Night and interior level | global dimming of the night sky term and of interior ambient | `nightGain`, `interiorGain`, `interiorBakeFloor` |
Players reach all of it from **Options -> Advanced Graphics** (a Lighting Quality preset Off / Low / Medium / High plus the individual rows; Off is the original client look). The dev build has a panel for all of it: **Ctrl+Shift+D → Lighting & shadows**, with Dim / Default /
+29 -1
View File
@@ -495,6 +495,8 @@
-- MONKEY (volumetric fog): localised atmospheric quality row.
volumetricFog = "Volumetric Fog",
fogOff = "Off",
-- MONKEY (skybox): the zone skybox row.
zoneSkyboxes = "Zone Skyboxes",
waterQuality = "Water Quality",
waterClassic = "Classic",
waterEnhanced = "Enhanced",
@@ -534,6 +536,12 @@
-- MONKEY (volumetric fog): explain cost and the shadow-map dependency.
VOLUMETRIC_FOG =
"Off disables volumetric fog. Low adds nearby haze and sun or moon shafts with fewer steps; High uses more steps for smoother shafts. Mist is heavier at dawn and in bad weather, and subtle indoors. Shafts require shadows; with shadows off only haze remains.",
-- MONKEY (skybox): what the zone skybox row changes.
ZONE_SKYBOXES =
"Draws the painted sky a zone's lighting names, as later expansions do: "
.. "the volcanic sky of the Burning Steppes, the alien sky of the Blasted Lands "
.. "and the starry night over Mount Hyjal, fading in at the zone's edge. "
.. "Off keeps the original client's sky everywhere but ghost and dungeon skies. Nearly free.",
WATER_QUALITY =
"Classic is the original client's water. Enhanced adds soft shorelines, "
.. "animated foam around shores and objects, moving wave highlights, sky reflection "
@@ -619,6 +627,8 @@
-- MONKEY (volumetric fog): Russian atmospheric quality row.
volumetricFog = "Объёмный туман",
fogOff = "Выкл",
-- MONKEY (skybox): the zone skybox row.
zoneSkyboxes = "Небосводы зон",
waterQuality = "Качество воды",
waterClassic = "Классическое",
waterEnhanced = "Улучшенное",
@@ -660,6 +670,12 @@
-- MONKEY (volumetric fog): localised effect and cost explanation.
VOLUMETRIC_FOG =
"Выкл отключает объёмный туман. Низкое добавляет дымку вблизи и лучи солнца или луны с меньшим числом шагов; Высокое использует больше шагов для более плавных лучей. Туман гуще на рассвете и в непогоду, а в помещениях слабее. Для лучей нужны тени; без теней остаётся только дымка.",
-- MONKEY (skybox): what the zone skybox row changes.
ZONE_SKYBOXES =
"Рисует нарисованное небо, заданное освещением зоны, как в поздних дополнениях: "
.. "вулканическое небо Пылающих степей, чужое небо Выжженных земель "
.. "и звёздную ночь над горой Хиджал, с плавным появлением на границе зоны. "
.. "«Выкл.» оставляет небо оригинального клиента везде, кроме неба призрака и подземелий. Почти бесплатно.",
WATER_QUALITY =
"«Классическое» — вода оригинального клиента. «Улучшенное» добавляет плавные переходы у берегов, "
.. "анимированную пену у берегов и вокруг объектов, подвижные блики на волнах, отражение неба "
@@ -2602,11 +2618,23 @@
})
</OnLoad></Scripts>
</Frame>
<Frame name="$parentRowCharacterShadows" inherits="OptionsCheckboxRowTemplate">
<!-- MONKEY (skybox): the zone skybox switch. -->
<Frame name="$parentRowZoneSkyboxes" inherits="OptionsCheckboxRowTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentRowVolumetricFog" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-9"/></Offset></Anchor>
<Anchor point="TOPRIGHT" relativeTo="$parentRowVolumetricFog" relativePoint="BOTTOMRIGHT"><Offset><AbsDimension x="0" y="-9"/></Offset></Anchor>
</Anchors>
<Scripts><OnLoad>
OptionsRow_OnLoad(self, "zoneSkyboxes",
BENILLA_ADVGFX.zoneSkyboxes,
"BENILLA_TOOLTIP_ZONE_SKYBOXES")
</OnLoad></Scripts>
</Frame>
<Frame name="$parentRowCharacterShadows" inherits="OptionsCheckboxRowTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentRowZoneSkyboxes" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-9"/></Offset></Anchor>
<Anchor point="TOPRIGHT" relativeTo="$parentRowZoneSkyboxes" relativePoint="BOTTOMRIGHT"><Offset><AbsDimension x="0" y="-9"/></Offset></Anchor>
</Anchors>
<Scripts><OnLoad>
OptionsRow_OnLoad(self, "characterShadows",
BENILLA_ADVGFX.characterShadows,
@@ -1378,6 +1378,72 @@ pub(super) const ON_DEMAND: &[Scenario] = &[
look: LAVA_RIVER_LOOK,
minute: 0,
ui: None,
}, // MONKEY (skybox): zone skyboxes (run with `WOW_ZONE_SKYBOXES=1`) and the two stock skybox
// lanes. Karazahn40 (Turtle map 814) is the one stock sphere naming a clear-slot skybox
// (Light 553, HellfireSkyBox); the zone shots need the sky patch.
Scenario {
name: "skybox-karazhan-noon",
map: Some(814),
eye: SKYBOX_KZ_EYE,
look: SKYBOX_KZ_LOOK,
minute: 720,
ui: None,
},
Scenario {
name: "skybox-karazhan-night",
map: Some(814),
eye: SKYBOX_KZ_EYE,
look: SKYBOX_KZ_LOOK,
minute: 0,
ui: None,
},
Scenario {
name: "skybox-steppes-noon",
map: Some(MAP_AZEROTH),
eye: SKYBOX_STEPPES_EYE,
look: SKYBOX_STEPPES_LOOK,
minute: 720,
ui: None,
},
Scenario {
name: "skybox-steppes-night",
map: Some(MAP_AZEROTH),
eye: SKYBOX_STEPPES_EYE,
look: SKYBOX_STEPPES_LOOK,
minute: 0,
ui: None,
},
Scenario {
name: "skybox-blasted-noon",
map: Some(MAP_AZEROTH),
eye: SKYBOX_BLASTED_EYE,
look: SKYBOX_BLASTED_LOOK,
minute: 720,
ui: None,
},
Scenario {
name: "skybox-blasted-night",
map: Some(MAP_AZEROTH),
eye: SKYBOX_BLASTED_EYE,
look: SKYBOX_BLASTED_LOOK,
minute: 0,
ui: None,
},
Scenario {
name: "skybox-hyjal-noon",
map: Some(MAP_KALIMDOR),
eye: SKYBOX_HYJAL_EYE,
look: SKYBOX_HYJAL_LOOK,
minute: 720,
ui: None,
},
Scenario {
name: "skybox-hyjal-night",
map: Some(MAP_KALIMDOR),
eye: SKYBOX_HYJAL_EYE,
look: SKYBOX_HYJAL_LOOK,
minute: 0,
ui: None,
},
];
@@ -1482,6 +1548,17 @@ pub(crate) fn ui_opted_in() -> bool {
std::env::var("WOW_CAPTURE_UI").as_deref() == Ok("1") || scenario_declares_ui()
}
// MONKEY (skybox): the skybox shots, eye just above the ground at a sphere's centre, pitched up so
// the upper frame is sky.
const SKYBOX_KZ_EYE: [f32; 3] = [-6474.0, -2912.0, 40.0];
const SKYBOX_KZ_LOOK: [f32; 3] = [-6300.0, -2800.0, 95.0];
const SKYBOX_STEPPES_EYE: [f32; 3] = [-7979.0, -2571.0, 260.0];
const SKYBOX_STEPPES_LOOK: [f32; 3] = [-7800.0, -2450.0, 320.0];
const SKYBOX_BLASTED_EYE: [f32; 3] = [-11300.0, -3073.0, 30.0];
const SKYBOX_BLASTED_LOOK: [f32; 3] = [-11120.0, -2960.0, 90.0];
const SKYBOX_HYJAL_EYE: [f32; 3] = [4637.0, -4461.0, 1060.0];
const SKYBOX_HYJAL_LOOK: [f32; 3] = [4800.0, -4350.0, 1120.0];
#[cfg(test)]
mod ui_opt_in_tests {
use super::*;
+6
View File
@@ -921,6 +921,12 @@ pub(crate) const REGISTERED: &[Registered] = &[
),
// MONKEY (volumetric fog): saved live tier; capture override stays session-only.
ours("volumetricFog", "1", "benilla's own: near-field volumetric fog, 0 Off / 1 Low / 2 High"),
// MONKEY (skybox): the living player's zone skybox from `LightParams`; High sets 1.
ours(
"zoneSkyboxes",
"0",
"benilla's own: draw the zone skybox LightParams names for the living, 0 Off / 1 On; the reference draws a DBC skybox only for the ghost",
),
ours(
"waterQuality",
"1",
+6 -4
View File
@@ -105,12 +105,14 @@ pub(super) fn world_section(ui: &mut egui::Ui, world: &mut WorldReadout) {
// 4-second crossfade (== the interior-fog blend, one number): `w 0.00` beside a name means
// the flood published a skybox the crossfade hasn't engaged — faithful standing outside
// the gate, not a resolve bug.
match world.skybox.0.as_deref() {
Some(path) => {
// MONKEY (skybox): the heaviest layer of the weighted list, at its own weight.
match world.skybox.primary() {
Some(layer) => {
let path = layer.path.as_str();
let leaf = path.rsplit('\\').next().unwrap_or(path);
line.push_str(&format!(
" · skybox {leaf} w {:.2}",
world.skybox_weight.0
" · skybox {leaf} w {:.2} (stand-down {:.2})",
layer.weight, world.skybox_weight.0
));
}
None => line.push_str(" · sky gradient"),
+2
View File
@@ -245,6 +245,8 @@ impl PluginGroup for GamePlugins {
.add(crate::dynamic_interior::DynamicInteriorPlugin)
// MONKEY (volumetric fog): bridge the live setting to Bevy.
.add(crate::volumetric_fog::VolumetricFogPlugin)
// MONKEY (skybox): bridge the zone-skybox setting to the world lane.
.add(crate::zone_skybox::ZoneSkyboxPlugin)
// The realmlist (decision 1667) — the logon address the login screen edits. Same reason as
// VideoPlugin above: it is a CVar knob, so its resource has to exist before `load_config`.
.add(crate::realmlist::RealmlistPlugin)
+2
View File
@@ -74,6 +74,8 @@ mod doodad_events;
/// MONKEY (dynamic interiors): the cvar → benilla-world bridge for the fixture-lit interior lane.
// MONKEY (volumetric fog): optional near-field atmosphere plugin.
mod volumetric_fog;
// MONKEY (skybox): the zoneSkyboxes cvar bridge.
mod zone_skybox;
mod dynamic_interior;
mod entities;
mod fishing_line;
+45
View File
@@ -0,0 +1,45 @@
//! MONKEY (skybox): the `zoneSkyboxes` cvar's bridge to [`benilla_world::skybox::ZoneSkyboxes`].
//! `WOW_ZONE_SKYBOXES=0|1` overrides it for the session, which is how a hermetic capture (no config
//! file, registry defaults) turns the lane on.
use bevy::prelude::*;
/// The session override, read once.
#[derive(Resource, Clone, Copy)]
struct ZoneSkyboxOverride(Option<bool>);
pub(crate) struct ZoneSkyboxPlugin;
impl Plugin for ZoneSkyboxPlugin {
fn build(&self, app: &mut App) {
let forced = std::env::var("WOW_ZONE_SKYBOXES")
.ok()
.and_then(|v| v.trim().parse::<u8>().ok())
.map(|v| v != 0);
app.insert_resource(ZoneSkyboxOverride(forced))
.add_observer(on_cvar)
.add_systems(Startup, apply_override);
}
}
fn apply_override(
forced: Res<ZoneSkyboxOverride>,
zone: Option<ResMut<benilla_world::skybox::ZoneSkyboxes>>,
) {
if let (Some(on), Some(mut zone)) = (forced.0, zone) {
zone.set_if_neq(benilla_world::skybox::ZoneSkyboxes(on));
}
}
fn on_cvar(
ev: On<crate::cvars::CvarChanged>,
forced: Res<ZoneSkyboxOverride>,
zone: Option<ResMut<benilla_world::skybox::ZoneSkyboxes>>,
) {
if !ev.is("zoneSkyboxes") || forced.0.is_some() {
return;
}
if let Some(mut zone) = zone {
zone.set_if_neq(benilla_world::skybox::ZoneSkyboxes(ev.flag()));
}
}