Rework the realtime shadow feature into two INDEPENDENT, plug-and-play modules over one shared rig, plus alpha-tested foliage: - character_shadow.rs (was player_shadow.rs): owns the shared rig — one directional "sun" on private layer 31, the camera-layer plumbing, the shadow map, the invisible proxy material — and the per-frame CHARACTER lane. The rig lights up whenever EITHER lane is on. - world_shadow.rs (NEW, self-contained): the WORLD lane — the retained static_gx world (trees + buildings, cast solid) and alpha-tested foliage (leaf canopies, one caster per leaf texture). Removing the feature is deleting this module + its two calls + the cvar. Two separate cvars, each on its own Options row: `characterShadows` (players/NPCs/creatures/mounts, replacing the oval blob) and `worldShadows` (the static world + foliage, and MCSH terrain-shadow suppression). Either alone drives the shared sun; the per-frame entity caster admits Creature parts under characterShadows and entity-resident environment under worldShadows. Alpha-tested foliage caster (leaf-shaped canopy shadows): a CutoutShadowCasterMaterial (AlphaMode::Mask + an explicit prepass fragment, shadow_caster_cutout_prepass.wgsl) samples the leaf sheet and discards transparent texels in the directional-shadow pass, so a canopy casts its leaves instead of a solid box. Grouped one caster per leaf texture; StaticGx::collect_cutout_shadow_triangles buckets by AssetId and reuses the foliage's own image handle so the sampler inherits the correct clamp mode. MCSH terrain-shadow suppression now keys on the WORLD lane only (packed into the free sh_c16.w light lane via a new WorldShadowActive resource), not on the mere presence of a shadow sun — so character-only shadows leave the world's baked shadows intact. blob_shadow now hides the oval on characterShadows, not worldShadows. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
67 lines
3.2 KiB
Rust
67 lines
3.2 KiB
Rust
//! **The game's WGSL, compiled into the binary** (decision 1175).
|
|
//!
|
|
//! The five shaders under `src/shaders/` draw the *game's* own surface — the UI quad lane, the
|
|
//! glue screen's additive pass, and the three gamma passes. 1171 moved them out of the engine's
|
|
//! asset root and into this crate; 1175 stops serving them from a directory at all.
|
|
//!
|
|
//! They used to reach the asset server through a `game://` source registered with
|
|
//! `concat!(env!("CARGO_MANIFEST_DIR"), "/assets")` — the build machine's source tree, baked into
|
|
//! the binary. `embedded_asset!` keeps 1171's line (it is per-crate by construction) and drops the
|
|
//! path: the interface cannot go missing because there is nothing to miss.
|
|
//!
|
|
//! The registration lives here, at the root of `src/`, for the path-arithmetic reason spelled out
|
|
//! in `benilla_world::shaders` — the macro does not normalize, so a call from a submodule would
|
|
//! bake that submodule's directory into the served path.
|
|
|
|
use bevy::prelude::*;
|
|
|
|
/// Compile the game's five WGSL files in and register them under
|
|
/// `embedded://benilla_app/shaders/…`. Runs before anything can ask for one — added with the
|
|
/// client's other plugins, after `DefaultPlugins` has created the registry it fills.
|
|
pub(crate) fn plugin(app: &mut App) {
|
|
bevy::asset::embedded_asset!(app, "shaders/ui_quad.wgsl");
|
|
bevy::asset::embedded_asset!(app, "shaders/ui_add.wgsl");
|
|
bevy::asset::embedded_asset!(app, "shaders/ui_gamma.wgsl");
|
|
bevy::asset::embedded_asset!(app, "shaders/ui_node_gamma.wgsl");
|
|
bevy::asset::embedded_asset!(app, "shaders/ui_slice_gamma.wgsl");
|
|
bevy::asset::embedded_asset!(app, "shaders/shadow_caster.wgsl");
|
|
bevy::asset::embedded_asset!(app, "shaders/shadow_caster_cutout_prepass.wgsl");
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use bevy::asset::io::AssetSourceId;
|
|
use bevy::prelude::*;
|
|
|
|
/// The game's twin of `benilla_world::shaders`' test — see it for why the path is read back
|
|
/// rather than reasoned about, and why the loop is driven off the directory listing.
|
|
#[test]
|
|
fn every_game_shader_answers_at_its_embedded_path() {
|
|
let mut app = App::new();
|
|
app.add_plugins(bevy::asset::AssetPlugin::default());
|
|
app.add_plugins(super::plugin);
|
|
let server = app.world().resource::<AssetServer>();
|
|
let source = server.get_source(AssetSourceId::from("embedded")).unwrap();
|
|
|
|
let dir = concat!(env!("CARGO_MANIFEST_DIR"), "/src/shaders");
|
|
let mut found = 0;
|
|
for entry in std::fs::read_dir(dir).unwrap() {
|
|
let name = entry.unwrap().file_name().to_string_lossy().into_owned();
|
|
if !name.ends_with(".wgsl") {
|
|
continue;
|
|
}
|
|
found += 1;
|
|
let path = format!("benilla_app/shaders/{name}");
|
|
let read = bevy::tasks::block_on(async {
|
|
source.reader().read(std::path::Path::new(&path)).await
|
|
});
|
|
assert!(
|
|
read.is_ok(),
|
|
"embedded://{path} does not resolve — {name} is on disk but not registered in \
|
|
`shaders::plugin`, or was registered from a file that is not directly under src/"
|
|
);
|
|
}
|
|
assert_eq!(found, 7, "the game's shader set changed size");
|
|
}
|
|
}
|