rework occlusion events
This commit is contained in:
+5
-6
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user