rework occlusion events

This commit is contained in:
2025-05-23 14:01:28 +01:00
parent a245f35332
commit ac033de136
5 changed files with 107 additions and 128 deletions
+5 -6
View File
@@ -17,7 +17,7 @@ impl Citizen {
pub fn new(asset_server: &Res<AssetServer>, 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<TileMap>,
z_index: Res<game::ZIndex>,
) {
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 {
+2 -7
View File
@@ -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::<camera::CameraMoved>()
.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::CameraMoved>| camera_moved.0),
),
)
.run();
+6 -6
View File
@@ -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<game::ZIndex>,
mut query: Query<(&tiles::TerrainSprite, &mut Visibility)>,
mut cwss: ResMut<tiles::CurrentWorldSpriteState>,
) {
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());
}
+87 -61
View File
@@ -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<TileMap>,
mut query_set: ParamSet<(
Query<(&mut FloorTile, &Transform, &mut NeedsOccluded)>,
Query<(&mut FixtureTile, &Transform, &mut NeedsOccluded)>,
Query<(Entity, &mut NeedsOccluded)>,
)>,
mut tilemap: ResMut<TileMap>,
mut floor_tiles: Query<(Entity, &mut FloorTile, &Transform)>,
mut cwss: ResMut<CurrentWorldSpriteState>,
mut events: EventReader<TileOcclusionEvent>,
) {
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::<NeedsOccluded>();
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<IVec3, [u32; 8]> = 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<ChunkWeatheringAndPrecipitationEvent>,
mut foliage_event_writer: EventWriter<ChunkFoliageEvent>,
mut fauna_event_writer: EventWriter<ChunkFaunaEvent>,
mut cwss: ResMut<CurrentWorldSpriteState>,
) {
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<ChunkMap>,
mut tilemap: ResMut<TileMap>,
mut forrestry_event_writer: EventWriter<ChunkForrestryEvent>,
mut occlusion_event_writer: EventWriter<TileOcclusionEvent>,
) {
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<GenerateChunkEvent>) {
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::<ChunkForrestryEvent>()
.add_event::<ChunkFoliageEvent>()
.add_event::<ChunkFaunaEvent>()
.add_systems(Startup, (setup_chunk_system, setup_initial_chunks))
.add_event::<TileOcclusionEvent>()
.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<tiles::CurrentWorldSpriteState>,
camera_moved: Res<camera::CameraMoved>| {
cwss.state == tiles::TerrainSpriteState::RenderReady
|| camera_moved.0
},
),
)
.chain(),
),
);
}
}
+7 -48
View File
@@ -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<CurrentWorldSpriteState>,
textures: Res<Textures>,
texture_ids: Res<TextureIDs>,
query_sprites: Query<Entity, With<TerrainSprite>>,
query_terrain_sprites: Query<Entity, With<TerrainSprite>>,
mut images: ResMut<Assets<Image>>,
mut quilt_cache: ResMut<QuiltCache>,
) {
@@ -136,7 +135,7 @@ pub fn build_quilted_terrain_sprites(
cwss.state = TerrainSpriteState::InProgress;
// Despawn existing terrain sprites
let despawn_entities: Vec<Entity> = query_sprites.iter().collect();
let despawn_entities: Vec<Entity> = 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<usize, Vec<(Vec2, &FloorTile)>> = 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()
}
}