From ac033de136a0b02bf76a96fb0bfe5d3ca6fcfacb Mon Sep 17 00:00:00 2001 From: popertots Date: Fri, 23 May 2025 14:01:28 +0100 Subject: [PATCH] rework occlusion events --- src/citizen.rs | 11 ++-- src/main.rs | 9 +-- src/tile.rs | 12 ++-- src/tilemap.rs | 148 +++++++++++++++++++++++++++++-------------------- src/tiles.rs | 55 +++--------------- 5 files changed, 107 insertions(+), 128 deletions(-) diff --git a/src/citizen.rs b/src/citizen.rs index a0377f7..4fab67a 100644 --- a/src/citizen.rs +++ b/src/citizen.rs @@ -17,7 +17,7 @@ impl Citizen { pub fn new(asset_server: &Res, position: Vec3) -> Self { Citizen { ambulatory: Ambulatory { - walk_speed: 8., + walk_speed: 0., run_speed: 6., target: None, current_path: None, @@ -140,10 +140,10 @@ pub fn citizen_movement( &mut Sprite, )>, tilemap: Res, - z_index: Res, ) { - query.par_iter_mut().for_each( - |(mut ambulatory, mut transform, mut visibility, mut sprite)| { + query + .par_iter_mut() + .for_each(|(mut ambulatory, mut transform, _, _)| { let current_pos = transform.translation; let below_pos = Vec3::new( current_pos.x, @@ -200,8 +200,7 @@ pub fn citizen_movement( } } } - }, - ); + }); } fn is_standable_tile(tilemap: &TileMap, pos: IVec3) -> bool { diff --git a/src/main.rs b/src/main.rs index f4628b4..72049cd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,6 +16,7 @@ fn main() { .insert_resource(tiles::CurrentWorldSpriteState { state: tiles::TerrainSpriteState::Inactive, }) + .insert_resource(camera::CameraMoved(true)) .insert_resource(tiles::QuiltCache::default()) .add_systems(PreStartup, tiles::initialize_textures) .add_plugins( @@ -40,19 +41,13 @@ fn main() { citizen::spawn_citizens, ), ) - .add_systems( - FixedUpdate, - (tiles::build_quilted_terrain_sprites, camera::scroll_events), - ) - .init_resource::() .add_systems( Update, ( + camera::scroll_events, camera::camera_z_movement, camera::camera_movement, cursor::move_cursor, - tile::update_tile_visibility - .run_if(|camera_moved: Res| camera_moved.0), ), ) .run(); diff --git a/src/tile.rs b/src/tile.rs index 29de2af..79143a8 100644 --- a/src/tile.rs +++ b/src/tile.rs @@ -1,6 +1,9 @@ use std::time::Instant; -use crate::{game, tilemap, tiles}; +use crate::{ + game, tilemap, + tiles::{self, TerrainSpriteState}, +}; use bevy::prelude::*; use bevy_platform::collections::hash_map::HashMap; @@ -32,11 +35,6 @@ pub struct FixtureTile { pub visible_range: [u32; 8], } -#[derive(Component)] -pub struct NeedsOccluded { - pub has_been_occluded: bool, -} - impl Default for FixtureTile { fn default() -> Self { Self { @@ -56,6 +54,7 @@ pub struct TileMap { pub fn update_tile_visibility( z_index: Res, mut query: Query<(&tiles::TerrainSprite, &mut Visibility)>, + mut cwss: ResMut, ) { let now = Instant::now(); @@ -69,6 +68,7 @@ pub fn update_tile_visibility( Visibility::Hidden }; } + cwss.state = TerrainSpriteState::Inactive; println!("Visibility update: {:.2?}", now.elapsed()); } diff --git a/src/tilemap.rs b/src/tilemap.rs index ef9de0f..56a3f87 100644 --- a/src/tilemap.rs +++ b/src/tilemap.rs @@ -2,20 +2,20 @@ use std::sync::Mutex; use std::time::Instant; use crate::constants::{ITILE_SIZE, TILE_SIZE}; -use crate::game; -use crate::tile::{FixtureTile, FloorTile, NeedsOccluded, TileMap}; +use crate::tile::{update_tile_visibility, FloorTile, TileMap}; use crate::tiles::{ - CurrentWorldSpriteState, FixtureTilePrefab, FloorTilePrefab, TerrainSpriteState, TextureIDs, - Textures, + self, build_quilted_terrain_sprites, CurrentWorldSpriteState, FixtureTilePrefab, + FloorTilePrefab, TerrainSpriteState, TextureIDs, Textures, }; +use crate::{camera, game}; use bevy::prelude::*; use bevy_platform::collections::hash_map::HashMap; use noise::{NoiseFn, Perlin}; pub const CHUNK_SIZE: i32 = 8; -pub const Z_BELOW: f32 = 6.0; -pub const Z_ABOVE: f32 = 6.0; +pub const Z_BELOW: f32 = 10.0; +pub const Z_ABOVE: f32 = 8.0; pub const Z_TOTAL: f32 = Z_ABOVE + Z_BELOW; // MAX 255 DO NOT EXCEED pub const SEED: u32 = 420; @@ -65,48 +65,52 @@ fn setup_chunk_system(mut commands: Commands) { commands.insert_resource(ChunkMap::default()); } +#[derive(Event)] +pub struct TileOcclusionEvent { + pub tile_position: IVec3, +} + pub fn handle_tile_occlusion_updates( - mut commands: Commands, - tilemap: Res, - mut query_set: ParamSet<( - Query<(&mut FloorTile, &Transform, &mut NeedsOccluded)>, - Query<(&mut FixtureTile, &Transform, &mut NeedsOccluded)>, - Query<(Entity, &mut NeedsOccluded)>, - )>, + mut tilemap: ResMut, + mut floor_tiles: Query<(Entity, &mut FloorTile, &Transform)>, mut cwss: ResMut, + mut events: EventReader, ) { let start = Instant::now(); - query_set - .p0() - .par_iter_mut() - .for_each(|(mut tile, transform, mut needs_occluded)| { - if needs_occluded.has_been_occluded { - return; - } - tile.visible_range = calculate_visibility(transform.translation.as_ivec3(), &tilemap); - needs_occluded.has_been_occluded = true; - }); - query_set - .p1() - .par_iter_mut() - .for_each(|(mut fixture, transform, mut needs_occluded)| { - if needs_occluded.has_been_occluded { - return; - } - fixture.visible_range = calculate_visibility( - transform.translation.as_ivec3() - IVec3::new(0, 0, 1), - &tilemap, - ); - needs_occluded.has_been_occluded = true; - }); - for (entity, occ) in query_set.p2().iter_mut() { - if occ.has_been_occluded { - commands.entity(entity).remove::(); + let count = events.len(); + + // Process events in parallel + let updates: Vec<(IVec3, [u32; 8])> = events + .par_read() + .into_iter() + .map(|event| { + let pos = event.0.tile_position; + let visibility = calculate_visibility(pos, &tilemap); + (pos, visibility) + }) + .collect(); + + // Map updates for efficient lookup + let update_map: HashMap = updates.into_iter().collect(); + + // Update only the relevant FloorTile components + for (_, mut tile, pos) in floor_tiles.iter_mut() { + if let Some(visibility) = update_map.get(&pos.translation.as_ivec3()) { + tile.visible_range = *visibility; + if let Some(tile_data) = tilemap.floor_tiles.get_mut(&pos.translation.as_ivec3()) { + tile_data.4 = *visibility; + } } } - cwss.state = TerrainSpriteState::WaitingForRender; - println!("Tile occlusion updated in {:.2?}", start.elapsed()); + if count > 0 { + cwss.state = TerrainSpriteState::WaitingForRender; + println!( + "Tile occlusion updated {} tiles in {:.2?}", + count, + start.elapsed() + ); + } } pub fn calculate_visibility(pos: IVec3, tilemap: &TileMap) -> [u32; 8] { @@ -189,7 +193,9 @@ fn generate_chunks_from_algo( mut weathering_event_writer: EventWriter, mut foliage_event_writer: EventWriter, mut fauna_event_writer: EventWriter, + mut cwss: ResMut, ) { + let count = chunk_events.len(); // Fire each terrain pass. They will all fire sequentially. for event in chunk_events.read() { let chunk_pos = event.chunk_position; @@ -206,6 +212,9 @@ fn generate_chunks_from_algo( chunk_position: chunk_pos, }); } + if count > 0 { + cwss.state = TerrainSpriteState::WaitingForRender; + } } fn generate_chunk_terrain( @@ -214,6 +223,7 @@ fn generate_chunk_terrain( mut chunk_map: ResMut, mut tilemap: ResMut, mut forrestry_event_writer: EventWriter, + mut occlusion_event_writer: EventWriter, ) { let is_empty = events.is_empty(); let start = Instant::now(); @@ -341,6 +351,7 @@ fn generate_chunk_terrain( for (pos, data) in tilemap_updates.into_inner().unwrap() { tilemap.floor_tiles.insert(pos, data); + occlusion_event_writer.write(TileOcclusionEvent { tile_position: pos }); } // Send all forrestry events @@ -434,11 +445,13 @@ fn generate_chunk_forrestry( for (ivec, data) in collected_updates { tilemap.fixture_tiles.insert(ivec, data); } - println!( - "Forrestry update for {:?} chunks in {:.2?}", - count, - start.elapsed() - ); + if count > 0 { + println!( + "Forrestry update for {:?} chunks in {:.2?}", + count, + start.elapsed() + ); + } } fn generate_chunk_foliage( @@ -458,8 +471,8 @@ fn generate_chunk_fauna( } fn setup_initial_chunks(mut event_writer: EventWriter) { - for x in -16..=16 { - for y in -9..=9 { + for x in -8..=8 { + for y in -5..=5 { event_writer.write(GenerateChunkEvent { chunk_position: IVec2::new(x, y), }); @@ -526,24 +539,37 @@ impl Plugin for TilemapPlugin { .add_event::() .add_event::() .add_event::() - .add_systems(Startup, (setup_chunk_system, setup_initial_chunks)) + .add_event::() + .add_systems(Startup, (setup_chunk_system, setup_initial_chunks).chain()) .add_systems( - PostStartup, + FixedUpdate, ( generate_chunks_from_algo, - ( - generate_chunk_terrain, - generate_chunk_weathering_and_precipitation, - generate_chunk_forrestry, - generate_chunk_foliage, - generate_chunk_fauna, - ) - .chain(), - handle_tile_occlusion_updates, + generate_chunk_terrain, + generate_chunk_weathering_and_precipitation, + generate_chunk_forrestry, + generate_chunk_foliage, + generate_chunk_fauna, ) .chain(), ) - .add_systems(FixedUpdate, generate_chunks_from_algo) - .add_systems(Update, compute_visibility_of_game_entities); + .add_systems( + Update, + ( + compute_visibility_of_game_entities, + ( + handle_tile_occlusion_updates, + build_quilted_terrain_sprites, + update_tile_visibility.run_if( + |cwss: Res, + camera_moved: Res| { + cwss.state == tiles::TerrainSpriteState::RenderReady + || camera_moved.0 + }, + ), + ) + .chain(), + ), + ); } } diff --git a/src/tiles.rs b/src/tiles.rs index c5db2c4..fc637ba 100644 --- a/src/tiles.rs +++ b/src/tiles.rs @@ -1,5 +1,4 @@ -use crate::tile::{FixtureTile, FloorTile, NeedsOccluded, TileState}; -use crate::tilemap::Z_BELOW; +use crate::tile::{FixtureTile, FloorTile, TileState}; use crate::{constants::*, tilemap}; use bevy::asset::RenderAssetUsages; use bevy::prelude::*; @@ -120,12 +119,12 @@ impl Default for QuiltCache { // here be dragons :( pub fn build_quilted_terrain_sprites( - query: Query<(&FloorTile, &Transform)>, + query_tiles: Query<(&FloorTile, &Transform)>, commands: ParallelCommands<'_, '_>, mut cwss: ResMut, textures: Res, texture_ids: Res, - query_sprites: Query>, + query_terrain_sprites: Query>, mut images: ResMut>, mut quilt_cache: ResMut, ) { @@ -136,7 +135,7 @@ pub fn build_quilted_terrain_sprites( cwss.state = TerrainSpriteState::InProgress; // Despawn existing terrain sprites - let despawn_entities: Vec = query_sprites.iter().collect(); + let despawn_entities: Vec = query_terrain_sprites.iter().collect(); for entity in despawn_entities { commands.command_scope(|mut cmd| { cmd.entity(entity).despawn(); @@ -147,7 +146,7 @@ pub fn build_quilted_terrain_sprites( let mut tiles_by_z: HashMap> = HashMap::new(); // Calculate bounds for all visible tiles - for (floortile, transform) in query.iter() { + for (floortile, transform) in query_tiles.iter() { let position = Vec2::new(transform.translation.x, transform.translation.y); for z_index in 0..=tilemap::Z_TOTAL as usize { @@ -410,7 +409,6 @@ pub struct FloorTilePrefab { tile: FloorTile, tile_state: TileState, visibility: Visibility, - needs_occluded: NeedsOccluded, } impl FloorTilePrefab { @@ -426,9 +424,6 @@ impl FloorTilePrefab { timer: Timer::from_seconds(1.0, TimerMode::Repeating), }, visibility: Visibility::Hidden, - needs_occluded: NeedsOccluded { - has_been_occluded: false, - }, } } @@ -444,9 +439,6 @@ impl FloorTilePrefab { timer: Timer::from_seconds(1.0, TimerMode::Repeating), }, visibility: Visibility::Hidden, - needs_occluded: NeedsOccluded { - has_been_occluded: false, - }, } } @@ -462,9 +454,6 @@ impl FloorTilePrefab { timer: Timer::from_seconds(1.0, TimerMode::Repeating), }, visibility: Visibility::Hidden, - needs_occluded: NeedsOccluded { - has_been_occluded: false, - }, } } @@ -481,9 +470,6 @@ impl FloorTilePrefab { timer: Timer::from_seconds(1.0, TimerMode::Repeating), }, visibility: Visibility::Hidden, - needs_occluded: NeedsOccluded { - has_been_occluded: false, - }, } } @@ -499,20 +485,11 @@ impl FloorTilePrefab { timer: Timer::from_seconds(1.0, TimerMode::Repeating), }, visibility: Visibility::Hidden, - needs_occluded: NeedsOccluded { - has_been_occluded: false, - }, } } pub fn spawn(self, commands: &mut Commands) { - commands.spawn(( - self.tile, - self.transform, - self.tile_state, - self.visibility, - self.needs_occluded, - )); + commands.spawn((self.tile, self.transform, self.tile_state, self.visibility)); } } @@ -521,7 +498,6 @@ pub struct FixtureTilePrefab { transform: Transform, tile: FixtureTile, visibility: Visibility, - needs_occluded: NeedsOccluded, } impl FixtureTilePrefab { @@ -533,9 +509,6 @@ impl FixtureTilePrefab { ..Default::default() }, visibility: Visibility::Hidden, - needs_occluded: NeedsOccluded { - has_been_occluded: false, - }, } } @@ -547,9 +520,6 @@ impl FixtureTilePrefab { ..Default::default() }, visibility: Visibility::Hidden, - needs_occluded: NeedsOccluded { - has_been_occluded: false, - }, } } @@ -561,9 +531,6 @@ impl FixtureTilePrefab { ..Default::default() }, visibility: Visibility::Hidden, - needs_occluded: NeedsOccluded { - has_been_occluded: false, - }, } } @@ -575,20 +542,12 @@ impl FixtureTilePrefab { ..Default::default() }, visibility: Visibility::Hidden, - needs_occluded: NeedsOccluded { - has_been_occluded: false, - }, } } pub fn spawn(self, commands: &mut Commands) -> Entity { commands - .spawn(( - self.tile, - self.transform, - self.visibility, - self.needs_occluded, - )) + .spawn((self.tile, self.transform, self.visibility)) .id() } }