diff --git a/apps/kbve/isometric/src-tauri/src/game/mod.rs b/apps/kbve/isometric/src-tauri/src/game/mod.rs index e5c354114..1376db415 100644 --- a/apps/kbve/isometric/src-tauri/src/game/mod.rs +++ b/apps/kbve/isometric/src-tauri/src/game/mod.rs @@ -14,6 +14,7 @@ pub mod terrain; pub mod tilemap; pub mod trees; pub mod water; +pub mod weather; use bevy::app::{PluginGroup, PluginGroupBuilder}; @@ -30,6 +31,7 @@ use terrain::TerrainPlugin; use tilemap::TilemapPlugin; use trees::TreesPlugin; use water::WaterPlugin; +use weather::WeatherPlugin; /// All game-logic plugins bundled together. /// Used by both desktop (main.rs) and WASM (lib.rs) entry points. @@ -48,6 +50,7 @@ impl PluginGroup for GamePluginGroup { .add(TreesPlugin) .add(WaterPlugin) .add(InventoryPlugin) + .add(WeatherPlugin) .add(ActionsPlugin) } } diff --git a/apps/kbve/isometric/src-tauri/src/game/scene_objects.rs b/apps/kbve/isometric/src-tauri/src/game/scene_objects.rs index eb59de784..ae9a87f6a 100644 --- a/apps/kbve/isometric/src-tauri/src/game/scene_objects.rs +++ b/apps/kbve/isometric/src-tauri/src/game/scene_objects.rs @@ -281,7 +281,7 @@ fn raycast_hover_detection_desktop( bevy::camera::ScalingMode::FixedVertical { viewport_height } => viewport_height, _ => return, }; - let half_h = viewport_height / 2.0; + let half_h = (viewport_height / 2.0) * ortho.scale; let aspect = window.width() / window.height(); let half_w = half_h * aspect; @@ -361,7 +361,7 @@ fn raycast_hover_detection_wasm( bevy::camera::ScalingMode::FixedVertical { viewport_height } => viewport_height, _ => return, }; - let half_h = viewport_height / 2.0; + let half_h = (viewport_height / 2.0) * ortho.scale; let aspect = window.width() / window.height(); let half_w = half_h * aspect; diff --git a/apps/kbve/isometric/src-tauri/src/game/tilemap.rs b/apps/kbve/isometric/src-tauri/src/game/tilemap.rs index 55ffc24be..c271b76cc 100644 --- a/apps/kbve/isometric/src-tauri/src/game/tilemap.rs +++ b/apps/kbve/isometric/src-tauri/src/game/tilemap.rs @@ -1,8 +1,5 @@ use bevy::asset::RenderAssetUsages; use bevy::image::ImageSampler; -use bevy::light::{ - CascadeShadowConfigBuilder, Cascades, DirectionalLightShadowMap, SimulationLightSystems, -}; use bevy::mesh::{Indices, PrimitiveTopology}; use bevy::prelude::*; @@ -17,6 +14,7 @@ use super::scene_objects::{ }; use super::terrain::{CHUNK_SIZE, TerrainMap, hash2d}; use super::water::{WATER_LEVEL, WaterMaterial}; +use super::weather::{BlobShadow, BlobShadowAssets, WindSway}; pub const TILE_SIZE: f32 = 1.0; /// Thin cap on top of each column — bright surface that contrasts with darker body. @@ -895,48 +893,12 @@ struct VegBuffers { } /// Global wind state. Speed in MPH drives all sway amplitudes. -#[derive(Resource)] -pub(super) struct WindState { - pub(super) speed_mph: f32, // 0 = calm, 5 = gentle breeze, 15 = moderate, 30 = strong - pub(super) direction: (f32, f32), // normalized XZ direction -} - -impl Default for WindState { - fn default() -> Self { - Self { - speed_mph: 8.0, // gentle breeze - direction: (0.707, 0.707), // NE - } - } -} - -/// Attached to small vegetation (flowers, grass) for gentle translation sway. -#[derive(Component)] -struct WindSway { - base_translation: Vec3, - phase: f32, -} - pub struct TilemapPlugin; impl Plugin for TilemapPlugin { fn build(&self, app: &mut App) { - app.init_resource::(); - app.init_resource::(); - app.add_systems(Startup, (setup_tile_materials, spawn_lighting)); - app.add_systems( - Update, - ( - process_chunk_spawns_and_despawns, - animate_veg_wind, - spawn_wind_streaks, - animate_wind_streaks, - ), - ); - app.add_systems( - PostUpdate, - stabilize_shadow_cascades.after(SimulationLightSystems::UpdateDirectionalLightCascades), - ); + app.add_systems(Startup, setup_tile_materials); + app.add_systems(Update, process_chunk_spawns_and_despawns); } } @@ -1031,256 +993,6 @@ fn setup_tile_materials( }); } -fn spawn_lighting(mut commands: Commands) { - commands.insert_resource(GlobalAmbientLight { - color: Color::WHITE, - brightness: 200.0, - ..default() - }); - // Single cascade + pixelated shadow map + texel-snapping stabilisation. - // Shadow map sized so each shadow texel ≈ 1 scene pixel (32 px/unit). - // 1024 over 80 units = ~12.8 texels/unit → shadows are ~2.5× chunkier than - // scene pixels, giving that crisp pixel-art shadow look that blends in. - commands.insert_resource(DirectionalLightShadowMap { size: 1024 }); - commands.spawn(( - DirectionalLight { - illuminance: 6000.0, - shadows_enabled: true, - ..default() - }, - Transform::from_xyz(12.0, 15.0, -5.0).looking_at(Vec3::ZERO, Vec3::Y), - CascadeShadowConfigBuilder { - num_cascades: 1, - minimum_distance: 0.1, - maximum_distance: 80.0, - ..default() - } - .build(), - )); -} - -/// Stabilise directional shadow maps by texel-snapping the cascade projection. -/// -/// Bevy recomputes the cascade frustum from the camera every frame. When the -/// frustum centre moves by sub-texel amounts, shadow edges land on different -/// texels → visible 1-2 px "swimming" on the low-res render target. -/// -/// Fix: snap the clip-space translation of each cascade's `clip_from_world` -/// matrix to shadow-texel boundaries so the shadow grid stays locked to the -/// world. -fn stabilize_shadow_cascades( - shadow_map: Res, - mut query: Query<&mut Cascades, With>, -) { - let texel_clip = 2.0 / shadow_map.size as f32; - for mut cascades in query.iter_mut() { - for cascade_list in cascades.cascades.values_mut() { - for cascade in cascade_list.iter_mut() { - let mut m = cascade.clip_from_world; - m.w_axis.x = (m.w_axis.x / texel_clip).floor() * texel_clip; - m.w_axis.y = (m.w_axis.y / texel_clip).floor() * texel_clip; - cascade.clip_from_world = m; - } - } - } -} - -fn animate_veg_wind( - time: Res