From 55040a63eaab5d7961dd2bcf3e30257dd24b7b97 Mon Sep 17 00:00:00 2001 From: chairmanpow Date: Sat, 3 Oct 2026 17:14:49 +0100 Subject: [PATCH] Revert "shadow: skin the character caster on the GPU, so a crowd costs no CPU skin" The GPU-skinned character caster made realtime shadows render wrong, and the Stormwind lag it was meant to fix does not come from the shadow lanes anyway (character shadows are off there, so the rig is torn down). Restore the CPU-skinned merged proxy exactly as it was. --- crates/benilla-app/src/character_shadow.rs | 208 +++++++++--------- crates/benilla-app/src/shaders.rs | 3 +- .../src/shaders/shadow_caster_skinned.wgsl | 84 ------- crates/benilla-app/src/shadow_core.rs | 100 +-------- crates/benilla-world/src/rig_palette.rs | 6 +- 5 files changed, 115 insertions(+), 286 deletions(-) delete mode 100644 crates/benilla-app/src/shaders/shadow_caster_skinned.wgsl diff --git a/crates/benilla-app/src/character_shadow.rs b/crates/benilla-app/src/character_shadow.rs index b0477447..cdb5452f 100644 --- a/crates/benilla-app/src/character_shadow.rs +++ b/crates/benilla-app/src/character_shadow.rs @@ -1,27 +1,24 @@ //! The CHARACTER shadow lane — a thin plug-in on top of [`super::shadow_core`]. //! -//! It owns the per-unit caster for players, NPCs, creatures and mounts. When `characterShadows` is -//! on it declares demand to the shared rig ([`ShadowDemand`]); while the rig is live it keeps one -//! GPU-skinned caster per admitted creature part, drawn through [`SkinnedShadowCasterMaterial`]. -//! -//! The caster carries the part's own skinned mesh and `MeshTag`, so it skins in the shadow pass's -//! VERTEX stage from the shared rig palette the forward model already uploads — no per-frame CPU -//! skinning, no merged mesh, no re-upload. Pose changes are free: only membership (spawn/despawn, -//! range, occluder) is reconciled here. It does not know [`super::world_shadow`] exists. +//! It owns nothing but its own per-frame caster (players, NPCs, creatures, mounts). When +//! `characterShadows` is on it declares demand to the shared rig ([`ShadowDemand`]); while the rig +//! is live it collects the CREATURE entity parts into its caster using the rig's shared material and +//! the per-frame facts the rig publishes ([`ShadowFrame`]). It does not know [`super::world_shadow`] +//! exists — remove either lane and the other is untouched. use bevy::ecs::entity::EntityHashSet; -use bevy::mesh::MeshTag; -use bevy::platform::collections::HashMap; +use bevy::pbr::MeshMaterial3d; use bevy::prelude::*; +use benilla_assets::materials::WowModelMaterial; use benilla_world::billboard::BillboardCard; -use benilla_world::lighting::SharedLightBuffer; +use benilla_world::interact::PickMesh; use benilla_world::model_render::{ModelPart, ShadowOccluder}; -use benilla_world::rig_palette::RigPart; +use benilla_world::rig_palette::{RigPalettes, RigPart, RigSkin}; use crate::shadow_core::{ - casts_realtime_shadow, shadow_trace, spawn_skinned_caster, ShadowDemand, ShadowFrame, - ShadowSet, SkinnedShadowCasterMaterial, + collect_entity_geometry, empty_shadow_mesh, restore_mesh_buffers, shadow_trace, + spawn_solid_caster, take_mesh_buffers, RebuildRate, ShadowDemand, ShadowFrame, ShadowSet, }; use crate::video::VideoConfig; @@ -35,132 +32,141 @@ impl Plugin for CharacterShadowPlugin { } } -/// MONKEY (moon shadows): roots/ancestors with a live caster in the LAST completed reconciliation. -/// Blobs consume this in PostUpdate, before this frame's Last pass: a newly spawned caster gets a -/// render frame before its oval yields. +/// MONKEY (moon shadows): roots/ancestors with triangles in the LAST completed caster build. +/// Blobs consume this in PostUpdate, before this frame's Last rebuild: a newly uploaded proxy +/// gets a render frame before its oval yields. Cadence skips retain the same geometry/verdict. #[derive(Resource, Default)] pub(crate) struct CharacterShadowReady(pub EntityHashSet); -/// The character lane's retained casters: part entity → its shadow-caster entity. +/// The character lane's retained per-frame caster. #[derive(Resource, Default)] struct CharacterLane { - casters: HashMap, - material: Option>, - /// [`shadow_trace`]'s change detector: (admitted, rejected, casters). + caster: Option, + mesh: Option>, + /// [`shadow_trace`]'s change detector: (tris, admitted, rejected). traced: (u32, u32, u32), -} - -/// Drop every caster and the shared material handle. -fn teardown(commands: &mut Commands, lane: &mut CharacterLane) { - for (_, caster) in lane.casters.drain() { - commands.entity(caster).despawn(); - } - lane.material = None; + /// MONKEY (sun shadow perf): the `characterShadowRate` cadence gate — see the rebuild comment + /// in [`collect_character_shadows`]. + rate: RebuildRate, } #[allow(clippy::too_many_arguments)] fn collect_character_shadows( video: Res, + time: Res