MONKEY (p0): MonkeyFrame block appended to the shared light buffer

16 vec4 rows (256 B) after the point-light table, so no earlier offset moves
(per-frame blob 8528 -> 8784 B, size test updated). Rust side: the
`lighting::MonkeyFrame` resource any system may write, packed each frame by
`global_light::pack_monkey_frame` (which also fills time_of_day / night).
WGSL side: `benilla::monkey_frame::MonkeyFrame`, declared as `monkey` after
`points` in every WowLight mirror (terrain, wow_model, static_gx, liquid,
enhanced_water, wow_effect, wdl). All zero = no visual change. Layout in
LIGHTING.md "Invariants".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
pkuzic
2026-09-25 01:21:19 +01:00
co-authored by Claude Opus 5.5
parent da0f1a0d80
commit f62a9eae5b
13 changed files with 301 additions and 4 deletions
+22 -2
View File
@@ -52,8 +52,28 @@ is its own module, documented in `WATER.md`.
## Invariants worth knowing before editing
- The shared light buffer stays 8528 bytes and the torch table 6416 bytes; both are mirrored in
three shaders and pinned by tests.
- The shared light buffer's per-frame blob is 8784 bytes and the torch table 6416 bytes; both are
mirrored in the shaders and pinned by tests. The blob is 21 header rows (336 B), the 256-slot
point-light table (8192 B), then the MONKEY **MonkeyFrame** block (16 rows, 256 B; nothing before
it moves). Every `WowLight` mirror (terrain, wow_model, static_gx, liquid, enhanced_water,
wow_effect, wdl) declares it as `monkey: monkey_frame::MonkeyFrame` after `points`:
| row | x | y | z | w |
|---|---|---|---|---|
| `fog0` | height_fog_density | height_fog_height | height_fog_falloff | curve_blend |
| `fog1` | sun_fog r | g | b | sun_fog_strength |
| `fog2` | end_fog r | g | b | end_fog_distance |
| `fog3` | fog_model (0 classic, 1 modern) | sun_fog_angle | 0 | 0 |
| `wind0` | dir_x | dir_y | speed | gust |
| `wind1` | time_s | sway_strength | grass_strength | tree_strength |
| `wet0` | rain_rate | wetness | ripple_time_s | snow |
| `misc` | bender_count | time_of_day 0..1 | night 0..1 | 0 |
| `benders[8]` | world x | world y | world z | radius |
Positions are Bevy world space (Y up, yards; WoW `(x, y, z)` = Bevy `(-y, z, -x)`), the wind
direction is a unit vector in world XZ. Lanes write the `MonkeyFrame` resource
(`benilla-world/src/lighting/monkey_frame.rs`); `global_light::pack_monkey_frame` packs it after
the point table and fills `time_of_day` / `night` itself. All zero = no visual change.
- World lights use upstream's `WorldPointLight`, never Bevy's `PointLight`.
- WGSL only fails at pipeline creation, so a shader edit is verified by running a world scene, not
by `cargo check`.
+3
View File
@@ -29,6 +29,9 @@ pub fn register_shaders(app: &mut App) {
// (loaded eagerly so `#import benilla::shadow_hook` resolves in terrain/model/static_gx). This is
// the ONE place the realtime directional-shadow term lives; the receivers just call into it.
load_shader_library!(app, "shaders/shadow_hook.wgsl");
// MONKEY (p0 MonkeyFrame): `benilla::monkey_frame`, the struct every `WowLight` mirror declares
// after its point table (LIGHTING.md "Invariants").
load_shader_library!(app, "shaders/monkey_frame.wgsl");
// MONKEY (enhanced water): the optional water module, `benilla::enhanced_water` (WATER.md).
load_shader_library!(app, "shaders/enhanced_water.wgsl");
}
@@ -20,6 +20,8 @@
// 106 the opaque scene COLOUR, copied beside the depth: what the refraction looks through
#import bevy_pbr::mesh_view_bindings::{view, globals}
// MONKEY (p0 MonkeyFrame): the programme block's struct, mirrored after the point table.
#import benilla::monkey_frame
@group(#{MATERIAL_BIND_GROUP}) @binding(103) var scene_depth: texture_2d<f32>;
@group(#{MATERIAL_BIND_GROUP}) @binding(106) var scene_colour: texture_2d<f32>;
@@ -55,6 +57,8 @@ struct WaterLight {
wmo_fog_params: vec4<f32>, // 19
point_count: vec4<f32>, // 20 live count + point-light controls
points: array<vec4<f32>, 512>, // 21+ position/range, colour/lane pairs
// MONKEY (p0 MonkeyFrame): the programme block after the point table (monkey_frame.wgsl).
monkey: monkey_frame::MonkeyFrame,
};
@group(#{MATERIAL_BIND_GROUP}) @binding(105) var<storage, read> water_light: WaterLight;
@@ -30,6 +30,8 @@
}
// MONKEY (enhanced water): the optional water module - see enhanced_water.wgsl and WATER.md.
#import benilla::enhanced_water::{water_active, water_swell, enhanced_water, WaterFragment}
// MONKEY (p0 MonkeyFrame): the programme block's struct, mirrored after the point table.
#import benilla::monkey_frame
@group(#{MATERIAL_BIND_GROUP}) @binding(100) var frames: texture_2d_array<f32>;
@group(#{MATERIAL_BIND_GROUP}) @binding(101) var frames_samp: sampler;
@@ -60,6 +62,10 @@ struct WowLight {
_grade: vec4<f32>, // 17
wmo_fog_color: vec4<f32>, // 18 rgb = interior fog (block 2); w = enable
wmo_fog_params: vec4<f32>, // 19 x = start yd; y = end yd
_point_count: vec4<f32>, // 20 point-light count, unread here
_points: array<vec4<f32>, 512>, // 21+ point-light table, unread here
// MONKEY (p0 MonkeyFrame): the programme block after the point table (monkey_frame.wgsl).
monkey: monkey_frame::MonkeyFrame,
};
@group(#{MATERIAL_BIND_GROUP}) @binding(90) var<storage, read> wow_light: WowLight;
@@ -0,0 +1,21 @@
#define_import_path benilla::monkey_frame
// MONKEY (p0 MonkeyFrame): the graphics programme's per-frame block, 16 rows (256 B) appended to
// the shared light buffer right after the point-light table (`lighting::global_light`, packed from
// the `MonkeyFrame` resource in `lighting/monkey_frame.rs`). Every receiver's `WowLight` mirror
// declares it as `monkey: monkey_frame::MonkeyFrame` after `points`, so the offsets of everything
// before it never move. All zero = no visual change: every reader treats zero as "feature off".
//
// Coordinates are Bevy world space (Y up, 1 unit = 1 yd), the space of the receivers'
// `world_position`: WoW `(x, y, z)` is Bevy `(-y, z, -x)` (`benilla_assets::coords::wow_to_bevy`).
struct MonkeyFrame {
fog0: vec4<f32>, // height_fog_density, height_fog_height (world Y, yd), height_fog_falloff, curve_blend
fog1: vec4<f32>, // sun_fog_rgb (gamma 0..1), sun_fog_strength
fog2: vec4<f32>, // end_fog_rgb (gamma 0..1), end_fog_distance (yd)
fog3: vec4<f32>, // fog_model (0 classic, 1 modern), sun_fog_angle (cos), 0, 0
wind0: vec4<f32>, // dir_x, dir_y (unit, world XZ: .x = world x, .y = world z), speed (yd/s), gust 0..1
wind1: vec4<f32>, // time_s, sway_strength, grass_strength, tree_strength
wet0: vec4<f32>, // rain_rate 0..1, wetness 0..1, ripple_time_s, snow 0..1
misc: vec4<f32>, // bender_count, time_of_day 0..1 (packer), night 0..1 (packer), 0
benders: array<vec4<f32>, 8>, // world xyz (Bevy space) + radius (yd); the first bender_count are live
}
@@ -18,6 +18,8 @@
}
// MONKEY (shadow hook): the realtime directional-shadow term (fetch + edge/night fade) lives here.
#import benilla::shadow_hook
// MONKEY (p0 MonkeyFrame): the programme block's struct, mirrored after the point table.
#import benilla::monkey_frame
@group(#{MATERIAL_BIND_GROUP}) @binding(100) var layer_array: texture_2d_array<f32>;
@group(#{MATERIAL_BIND_GROUP}) @binding(104) var alpha_array: texture_2d_array<f32>;
@@ -56,6 +58,8 @@ struct WowLight {
// reach in yards) for an INTERIOR one. Terrain consumes only the exterior half.
point_count: vec4<f32>,
points: array<vec4<f32>, 512>,
// MONKEY (p0 MonkeyFrame): the programme block after the point table (monkey_frame.wgsl).
monkey: monkey_frame::MonkeyFrame,
};
@group(#{MATERIAL_BIND_GROUP}) @binding(90) var<storage, read> wow_light: WowLight;
@@ -17,6 +17,9 @@
mesh_view_bindings::view,
}
// MONKEY (p0 MonkeyFrame): the programme block's struct, mirrored after the point table.
#import benilla::monkey_frame
/// How far the band reaches inside the far-clip wall: the reference's far-band near plane
/// `farclip - 33.0` (`[0x8101b0]`, about one WDL outer cell).
const WDL_OVERLAP: f32 = 33.0;
@@ -34,6 +37,11 @@ struct WowLight {
_light_spec: vec4<f32>, // 3
fog_color: vec4<f32>, // 4 rgb = Light.dbc row 7 (gamma 0..1); w = enable (>0.5 ⇒ blend)
fog_params: vec4<f32>, // 5 x/y = the SCENE fog start/end yd (unread here — the hull has its own pair); z = the signed directional-shadow weight (MONKEY moon shadows: +sun / -moon; unread here); w = farclip wall (0 ⇒ off)
// MONKEY (p0 MonkeyFrame): rows 6-20 and the point table, unread here, so the block lines up.
_rows_6_20: array<vec4<f32>, 15>,
_points: array<vec4<f32>, 512>,
// MONKEY (p0 MonkeyFrame): the programme block after the point table (monkey_frame.wgsl).
monkey: monkey_frame::MonkeyFrame,
};
@group(#{MATERIAL_BIND_GROUP}) @binding(90) var<storage, read> w: WowLight;
@@ -20,6 +20,8 @@
}
// MONKEY (shadow hook): the realtime directional-shadow term (fetch + edge/night fade) lives here.
#import benilla::shadow_hook
// MONKEY (p0 MonkeyFrame): the programme block's struct, mirrored after the point table.
#import benilla::monkey_frame
// bevy_pbr 0.18.1's `forward_io::FragmentOutput`. No depth output: a fragment depth write costs
// the pipeline early-Z, so the sky lane pins its depth in the vertex stage.
@@ -112,6 +114,8 @@ struct WowLight {
// packed reach is always ≥ 1 yd and can never be mistaken for the exterior 0.
point_count: vec4<f32>,
points: array<vec4<f32>, 512>,
// MONKEY (p0 MonkeyFrame): the programme block after the point table (monkey_frame.wgsl).
monkey: monkey_frame::MonkeyFrame,
// The interior-prop SH probes, 7 rows per slot (`MAX_PROP_PROBES` = 8192 slots). Only this
// shader declares this tail; the other shaders bind the same buffer by its prefix.
prop_probes: array<vec4<f32>, 57344>,
@@ -66,6 +66,9 @@ use crate::view::WorldCamera;
struct LightStd430 {
rows: [[f32; 4]; LIGHT_HEADER_ROWS],
points: [[f32; 4]; 2 * MAX_POINT_LIGHTS],
// MONKEY (p0 MonkeyFrame): the programme block, appended AFTER the point table so no earlier
// offset moves (8528 -> 8784 B); packed by [`pack_monkey_frame`] from [`super::MonkeyFrame`].
monkey: [[f32; 4]; super::monkey_frame::MONKEY_FRAME_ROWS],
}
/// Header row count of the layout above (rows 0..=20), which every light blob sizes against.
@@ -615,6 +618,7 @@ impl Default for WowLightData {
Self(LightStd430 {
rows: [[0.0; 4]; 21],
points: [[0.0; 4]; 2 * MAX_POINT_LIGHTS],
monkey: [[0.0; 4]; super::monkey_frame::MONKEY_FRAME_ROWS],
})
}
}
@@ -644,6 +648,8 @@ pub(super) fn register(app: &mut App) {
// MONKEY (spellLightGain): and the one on spell-effect lights, which overrides it.
.init_resource::<SpellLightGain>()
.init_resource::<super::prop_probes::PropProbeExtract>()
// MONKEY (p0 MonkeyFrame): the per-frame programme parameters any system may write.
.init_resource::<super::MonkeyFrame>()
.add_plugins(ExtractResourcePlugin::<WowLightData>::default())
.add_plugins(ExtractResourcePlugin::<RoomClaimTable>::default())
.add_plugins(ExtractResourcePlugin::<SharedLightBuffer>::default())
@@ -656,7 +662,8 @@ pub(super) fn register(app: &mut App) {
// a light that has stood for a frame is packed on this frame's verdict. Its writes go
// through `Commands`, so a NEWLY spawned light is still packed on the fail-safe
// fallback for one frame — see that fallback's note in `build_light_data`.
(classify_light_lanes, build_light_data)
// MONKEY (p0 MonkeyFrame): the programme block is packed right after the table.
(classify_light_lanes, build_light_data, pack_monkey_frame)
.chain()
.after(bevy::transform::TransformSystems::Propagate)
.after(super::update_time_lighting),
@@ -699,6 +706,22 @@ pub(super) fn per_frame_blob_bytes() -> u64 {
std::mem::size_of::<LightStd430>() as u64
}
/// MONKEY (p0 MonkeyFrame): copies [`super::MonkeyFrame`] into the block after the point table,
/// plus the two clock fields the packer owns (`misc.y` time of day 0..1, `misc.z` night 0..1, the
/// same dusk ramp `nightGain` uses). Writes through `ResMut` only when a row moved.
fn pack_monkey_frame(
frame: Res<super::MonkeyFrame>,
clock: Res<super::GameClock>,
light: Res<WowLighting>,
mut data: ResMut<WowLightData>,
) {
let night = 1.0 - sun_shadow_strength(light.celestial_dir.y);
let packed = frame.pack(clock.minute as f32 / 1440.0, night);
if data.0.monkey != packed {
data.0.monkey = packed;
}
}
/// Pack the resolved [`WowLighting`] (+ the global fog-disable toggle and the view farclip) into the
/// std430 blob. The `.w` lanes carry the faithful invariants the shaders expect (Mod2x 1.0, clamp on,
/// terrain shininess 20, fog-enable, farclip wall); the model SH coeffs and both water swatches are
@@ -2626,7 +2649,8 @@ mod tests {
assert_eq!(MAX_LIVE_POINT_LIGHTS, 255, "255 is EXT_SEL_EMPTY in the three shaders");
assert_eq!(MAX_LIVE_POINT_LIGHTS, MAX_POINT_LIGHTS - 1);
// 21 header rows + 2 x 256 point rows, 16 B each.
assert_eq!(per_frame_blob_bytes(), 8528, "the mirrored blob must not change size");
// MONKEY (p0 MonkeyFrame): 8528 + the 256-byte programme block.
assert_eq!(per_frame_blob_bytes(), 8784, "the mirrored blob must not change size");
}
/// MONKEY (enclosed day floor): `interiorDaylight` rides the FRACTION of the interior lane's
+2
View File
@@ -11,6 +11,8 @@ mod daylight; // MONKEY (daylight fixtures): the sun as an interior-lane light i
mod daynight; // the two sun directions + day/night interp + the dawn/dusk warp curve
mod flicker; // MONKEY (flame flicker): the per-light fire wobble folded in at pack time
mod global_light; // the one shared global-light storage buffer (replaces the per-material push)
mod monkey_frame; // MONKEY (p0 MonkeyFrame): the programme's per-frame block after the point table
pub use monkey_frame::{FogModel, MonkeyFrame, MAX_BENDERS, MONKEY_FRAME_ROWS};
mod lava_light; // MONKEY (lava light): magma surface fixtures and their independent gain
pub use lava_light::{LavaLight, LavaLightGain};
mod prop_probes; // the per-instance interior-prop SH probe table (slot ↔ MeshTag payload)
@@ -0,0 +1,190 @@
//! MONKEY (p0 MonkeyFrame): the graphics programme's per-frame parameter block.
//!
//! One resource, [`MonkeyFrame`], that any system may write (fog model, wind, wetness, benders).
//! The light packer (`global_light::pack_monkey_frame`) copies it every frame into 16 rows
//! appended to the shared light buffer right after the point-light table, so the offsets of every
//! row before it never move. The WGSL mirror is `benilla::monkey_frame::MonkeyFrame`
//! (`benilla-assets/src/shaders/monkey_frame.wgsl`); every receiver's `WowLight` struct declares
//! it as its `monkey` member.
//!
//! All zero (the default) is "every feature off": nothing reads a zero field as a change, so the
//! image is the same as before the block existed.
//!
//! Layout (16 × `vec4<f32>`, 256 B):
//!
//! | row | xyz / x,y,z | w |
//! |---|---|---|
//! | 0 `fog0` | height_fog_density, height_fog_height, height_fog_falloff | curve_blend |
//! | 1 `fog1` | sun_fog_rgb | sun_fog_strength |
//! | 2 `fog2` | end_fog_rgb | end_fog_distance |
//! | 3 `fog3` | fog_model (0 classic, 1 modern), sun_fog_angle, 0 | 0 |
//! | 4 `wind0` | dir_x, dir_y, speed | gust |
//! | 5 `wind1` | time_s, sway_strength, grass_strength | tree_strength |
//! | 6 `wet0` | rain_rate, wetness, ripple_time_s | snow |
//! | 7 `misc` | bender_count, time_of_day 0..1, night 0..1 | 0 |
//! | 8-15 `benders` | world x, y, z | radius |
//!
//! Coordinates are Bevy world space (Y up, 1 unit = 1 yd), the space the receivers'
//! `world_position` is in: WoW `(x, y, z)` → Bevy `(-y, z, -x)` ([`benilla_assets::coords`]).
//! The wind direction is a unit vector in the world XZ plane: `dir_x` = world x, `dir_y` = world z.
//! `time_of_day` and `night` are filled by the packer from the game clock and the celestial sun;
//! every other field belongs to the lane that owns the feature.
use bevy::prelude::*;
/// Rows in the appended block.
pub const MONKEY_FRAME_ROWS: usize = 16;
/// How many benders (grass/foliage pushers: the player and nearby units) the block carries.
pub const MAX_BENDERS: usize = 8;
/// The fog model selector, `fog3.x`.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum FogModel {
/// Today's linear 1.12 fog, byte-identical.
#[default]
Classic,
/// The FOG lane's modern model (height / sun / end-colour / curve fog).
Modern,
}
/// The per-frame programme parameters. Write the fields your lane owns through
/// `ResMut<MonkeyFrame>`; leave the rest alone. See the module docs for the packed layout.
#[derive(Resource, Clone, Copy, Debug, Default, PartialEq)]
pub struct MonkeyFrame {
// ── FOG lane (rows 0-3) ──
pub fog_model: FogModel,
pub height_fog_density: f32,
/// World Y (Bevy space, yd) of the height-fog plane.
pub height_fog_height: f32,
pub height_fog_falloff: f32,
pub fog_curve_blend: f32,
/// Gamma 0..1.
pub sun_fog_rgb: [f32; 3],
pub sun_fog_strength: f32,
/// Cosine threshold of the sun-fog lobe.
pub sun_fog_angle: f32,
/// Gamma 0..1.
pub end_fog_rgb: [f32; 3],
pub end_fog_distance: f32,
// ── WIND lane (rows 4-5) ──
/// Unit vector in the world XZ plane (`[x, z]`).
pub wind_dir: [f32; 2],
/// Yards per second.
pub wind_speed: f32,
pub wind_gust: f32,
/// The wind clock in seconds (the lane owns it, so a capture can pin it).
pub wind_time_s: f32,
pub sway_strength: f32,
pub grass_strength: f32,
pub tree_strength: f32,
// ── WET lane (row 6) ──
pub rain_rate: f32,
pub wetness: f32,
pub ripple_time_s: f32,
pub snow: f32,
// ── benders (rows 7.x, 8-15) ──
/// Bender slots; only the first `min(bender_count, MAX_BENDERS)` are packed. `[x, y, z, radius]` in
/// Bevy world space.
pub benders: [[f32; 4]; MAX_BENDERS],
pub bender_count: u32,
}
impl MonkeyFrame {
/// The 16 packed rows. `time_of_day` (0..1) and `night` (0..1) are the packer's own inputs.
pub fn pack(&self, time_of_day: f32, night: f32) -> [[f32; 4]; MONKEY_FRAME_ROWS] {
let mut rows = [[0.0f32; 4]; MONKEY_FRAME_ROWS];
let model = match self.fog_model {
FogModel::Classic => 0.0,
FogModel::Modern => 1.0,
};
rows[0] = [
self.height_fog_density,
self.height_fog_height,
self.height_fog_falloff,
self.fog_curve_blend,
];
let s = self.sun_fog_rgb;
rows[1] = [s[0], s[1], s[2], self.sun_fog_strength];
let e = self.end_fog_rgb;
rows[2] = [e[0], e[1], e[2], self.end_fog_distance];
rows[3] = [model, self.sun_fog_angle, 0.0, 0.0];
rows[4] = [self.wind_dir[0], self.wind_dir[1], self.wind_speed, self.wind_gust];
rows[5] = [self.wind_time_s, self.sway_strength, self.grass_strength, self.tree_strength];
rows[6] = [self.rain_rate, self.wetness, self.ripple_time_s, self.snow];
let n = (self.bender_count as usize).min(MAX_BENDERS);
rows[7] = [n as f32, time_of_day, night, 0.0];
rows[8..8 + n].copy_from_slice(&self.benders[..n]);
rows
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_default_frame_packs_to_zero_but_the_clock() {
let rows = MonkeyFrame::default().pack(0.0, 0.0);
assert!(rows.iter().flatten().all(|v| *v == 0.0));
let rows = MonkeyFrame::default().pack(0.5, 1.0);
assert_eq!(rows[7], [0.0, 0.5, 1.0, 0.0]);
}
#[test]
fn fields_land_in_their_documented_rows() {
let f = MonkeyFrame {
fog_model: FogModel::Modern,
height_fog_density: 1.0,
height_fog_height: 2.0,
height_fog_falloff: 3.0,
fog_curve_blend: 4.0,
sun_fog_rgb: [5.0, 6.0, 7.0],
sun_fog_strength: 8.0,
end_fog_rgb: [9.0, 10.0, 11.0],
end_fog_distance: 12.0,
sun_fog_angle: 13.0,
wind_dir: [14.0, 15.0],
wind_speed: 16.0,
wind_gust: 17.0,
wind_time_s: 18.0,
sway_strength: 19.0,
grass_strength: 20.0,
tree_strength: 21.0,
rain_rate: 22.0,
wetness: 23.0,
ripple_time_s: 24.0,
snow: 25.0,
benders: [[26.0, 27.0, 28.0, 29.0]; MAX_BENDERS],
bender_count: 20, // clamped to MAX_BENDERS
};
let r = f.pack(0.25, 0.75);
assert_eq!(r[0], [1.0, 2.0, 3.0, 4.0]);
assert_eq!(r[1], [5.0, 6.0, 7.0, 8.0]);
assert_eq!(r[2], [9.0, 10.0, 11.0, 12.0]);
assert_eq!(r[3], [1.0, 13.0, 0.0, 0.0]);
assert_eq!(r[4], [14.0, 15.0, 16.0, 17.0]);
assert_eq!(r[5], [18.0, 19.0, 20.0, 21.0]);
assert_eq!(r[6], [22.0, 23.0, 24.0, 25.0]);
assert_eq!(r[7], [8.0, 0.25, 0.75, 0.0]);
assert_eq!(r[15], [26.0, 27.0, 28.0, 29.0]);
}
/// The WGSL mirror declares the same 16 rows; a member added on one side only would shift
/// every bender.
#[test]
fn the_wgsl_mirror_has_sixteen_rows() {
let src = include_str!("../../../benilla-assets/src/shaders/monkey_frame.wgsl");
let body = src.split_once("struct MonkeyFrame {").unwrap().1;
let body = body.split_once("\n}").unwrap().0;
let mut rows = 0;
for line in body.lines().map(str::trim).filter(|l| !l.is_empty() && !l.starts_with("//")) {
if line.contains("array<vec4<f32>, 8>") {
rows += 8;
} else if line.contains("vec4<f32>") {
rows += 1;
}
}
assert_eq!(rows, MONKEY_FRAME_ROWS);
}
}
@@ -10,6 +10,8 @@
}
// MONKEY (shadow hook): the realtime directional-shadow term (fetch + edge/night fade) lives here.
#import benilla::shadow_hook
// MONKEY (p0 MonkeyFrame): the programme block's struct, mirrored after the point table.
#import benilla::monkey_frame
// Group 0 is Bevy's standard mesh-view bind group (view matrices, directional-light records and
// the shadow textures the retained pass reads).
@@ -40,6 +42,8 @@ struct WowLight {
// yards. wow_model.wgsl owns the full note.
point_count: vec4<f32>,
points: array<vec4<f32>, 512>,
// MONKEY (p0 MonkeyFrame): the programme block after the point table (monkey_frame.wgsl).
monkey: monkey_frame::MonkeyFrame,
// lighting::prop_probes: 8192 slots of 7 rows; the buffer's later regions are not mirrored.
prop_probes: array<vec4<f32>, 57344>,
}
@@ -12,6 +12,8 @@
// - BLEND_MOD2X: (rgb, 1) under (Dst, Src) = `2·src·dst`, rain's state; reads no alpha.
#import bevy_render::view::View
// MONKEY (p0 MonkeyFrame): the programme block's struct, mirrored after the point table.
#import benilla::monkey_frame
// Prefix of `lighting::global_light`'s buffer; keep in sync with wow_model.wgsl's copy.
struct WowLight {
@@ -33,6 +35,11 @@ struct WowLight {
sh_c16: vec4<f32>,
_water: array<vec4<f32>, 4>,
grade: vec4<f32>,
// MONKEY (p0 MonkeyFrame): rows 18-20 and the point table, unread here, so the block lines up.
_rows_18_20: array<vec4<f32>, 3>,
_points: array<vec4<f32>, 512>,
// MONKEY (p0 MonkeyFrame): the programme block after the point table (monkey_frame.wgsl).
monkey: monkey_frame::MonkeyFrame,
};
@group(0) @binding(0) var<uniform> view: View;