diff --git a/src/game.rs b/src/game.rs index c644ec2..1180ec5 100644 --- a/src/game.rs +++ b/src/game.rs @@ -29,7 +29,7 @@ fn setup_tilemap(commands: &mut Commands, asset_server: &Res) { for x in -50..50 { for y in -50..50 { - for z in -5..40 { + for z in -50..150 { let position = Vec3::new(x as f32 * TILE_SIZE, y as f32 * TILE_SIZE, -z as f32); // Generate Perlin noise value @@ -66,9 +66,9 @@ fn setup_tilemap(commands: &mut Commands, asset_server: &Res) { } } } - for x in -15..15 { - for y in -15..15 { - let position = Vec3::new(x as f32 * TILE_SIZE, y as f32 * TILE_SIZE, -39.); + for x in -50..50 { + for y in -50..50 { + let position = Vec3::new(x as f32 * TILE_SIZE, y as f32 * TILE_SIZE, -150.); commands.spawn(TilePrefab::bedrock(position, asset_server)); } } diff --git a/src/main.rs b/src/main.rs index b622ed1..21c5396 100644 --- a/src/main.rs +++ b/src/main.rs @@ -32,7 +32,7 @@ fn main() { cursor::setup_cursor, ), ) - .add_systems(PostStartup, tile::tile_sprite_occlusion) + .add_systems(PostStartup, (tile::tile_sprite_generate_occlusion_map,tile::update_tile_visibility).chain()) .add_systems( FixedUpdate, ( @@ -46,7 +46,7 @@ fn main() { FixedUpdate, ( tile::camera_z_movement, - tile::tile_sprite_occlusion + tile::update_tile_visibility .run_if(|camera_moved: Res| camera_moved.0), ), ) diff --git a/src/tile.rs b/src/tile.rs index 1bdd98a..3c644ef 100644 --- a/src/tile.rs +++ b/src/tile.rs @@ -1,12 +1,15 @@ -use crate::item::Item; use crate::game; +use crate::item::Item; +use bevy::ecs::system::ParamSet; use bevy::prelude::*; +use std::collections::HashMap; -#[derive(Component)] +#[derive(Component, Clone)] #[require(Sprite)] pub struct Tile { pub id: u32, pub opaque: bool, + pub visible_range: [u32; 8], } impl Default for Tile { @@ -14,6 +17,7 @@ impl Default for Tile { Self { id: 1, opaque: true, + visible_range: [0; 8], } } } @@ -44,7 +48,7 @@ pub struct WallTile { #[derive(Component)] #[require(Tile)] pub struct HasWater { - pub height : u8, + pub height: u8, pub astarmultiplier: f32, pub swimmable: bool, } @@ -68,93 +72,249 @@ pub fn camera_z_movement( camera_moved.0 = false; if keyboard_input.pressed(KeyCode::ShiftLeft) { z_index.0 -= 1.0; - z_index.0 = z_index.0.clamp(-39.0, 5.0); + z_index.0 = z_index.0.clamp(-149.0, 5.0); camera_moved.0 = true; } if keyboard_input.pressed(KeyCode::ShiftRight) { z_index.0 += 1.0; - z_index.0 = z_index.0.clamp(-39.0, 5.0); + z_index.0 = z_index.0.clamp(-149.0, 5.0); camera_moved.0 = true; } } -pub fn tile_sprite_occlusion( - mut query: Query<(&mut Visibility, &Transform, &Tile), With>, - tile_query: Query<(&Transform, &Tile)>, - z_index: ResMut, +pub fn tile_sprite_generate_occlusion_map( + mut query_set: ParamSet<(Query<(&Transform, &Tile)>, Query<(&mut Tile, &Transform)>)>, ) { - use std::collections::HashMap; - - // Build spatial lookup - let tile_map: HashMap<(i32, i32, i32), &Tile> = tile_query - .iter() - .map(|(transform, tile)| { - ( + // First, create the tile map using only the first query + let tile_map: HashMap<(i32, i32, i32), (bool, u32)> = { + let first_query = query_set.p0(); + first_query + .iter() + .map(|(transform, tile)| { ( - transform.translation.x.round() as i32, - transform.translation.y.round() as i32, - transform.translation.z.round() as i32, - ), - tile, - ) - }) - .collect(); + ( + transform.translation.x as i32, + transform.translation.y as i32, + transform.translation.z as i32, + ), + (tile.opaque, tile.id), + ) + }) + .collect() + }; - query + // Then, process tiles using the second query and our collected tile_map + let mut second_query = query_set.p1(); + second_query .par_iter_mut() - .for_each(|(mut visibility, transform, _)| { - *visibility = Visibility::Hidden; - - // Early exit for above camera layer - if transform.translation.z > z_index.0 { - return; - } + .for_each(|(mut tile, transform)| { + // Clear existing visible range + tile.visible_range = [0; 8]; let pos = ( - transform.translation.x.round() as i32, - transform.translation.y.round() as i32, - transform.translation.z.round() as i32, + transform.translation.x as i32, + transform.translation.y as i32, + transform.translation.z as i32, ); - // Check for any opaque tiles above the current tile - for z_offset in 1..=100 { - // Adjust the max height of the check if needed - let above_pos = (pos.0, pos.1, pos.2 + z_offset); - // look up until camera height - if above_pos.2 <= z_index.0 as i32 { - // get any tiles - if let Some(neighbor_tile) = tile_map.get(&above_pos) { - // If there's an opaque tile above, hide the current tile - if neighbor_tile.opaque { - return; // No need to check further; the tile should remain hidden - } - } + // Check visibility for each possible z-index + for camera_z in -150..100 { + let mut is_visible = false; + + // Skip if tile is above camera + if pos.2 > camera_z { + continue; } - } - // Check all neighbors around the tile - for x_offset in -1..=1 { - for y_offset in -1..=1 { - for z_offset in -1..=1 { - if x_offset == 0 && y_offset == 0 && z_offset == 0 { - continue; - } - - let neighbor_pos = (pos.0 + x_offset, pos.1 + y_offset, pos.2 + z_offset); - - if let Some(neighbor_tile) = tile_map.get(&neighbor_pos) { - // id == 0 is sky - if neighbor_tile.id == 0 { - *visibility = Visibility::Visible; - break; + // Check for opaque tiles above + let mut is_occluded = false; + 'vertical_check: for z_offset in 1..=20 { + let above_pos = (pos.0, pos.1, pos.2 + z_offset); + if above_pos.2 <= camera_z { + if let Some(&(is_opaque, _)) = tile_map.get(&above_pos) { + if is_opaque { + is_occluded = true; + break 'vertical_check; } } } } + + if !is_occluded { + // Check neighbors for visibility + 'neighbor_check: for x_offset in -1..=1 { + for y_offset in -1..=1 { + for z_offset in 0..=1 { + if x_offset == 0 && y_offset == 0 && z_offset == 0 { + continue; + } + + let neighbor_pos = + (pos.0 + x_offset, pos.1 + y_offset, pos.2 + z_offset); + + if let Some(&(_, id)) = tile_map.get(&neighbor_pos) { + if id == 0 { + // sky tile + is_visible = true; + break 'neighbor_check; + } + } + } + } + } + } + + if is_visible { + let z2 = (camera_z + 150) as usize; + tile.visible_range[z2 / 32] |= 1 << ((z2 % 32) as u32); // let value = (visible_range[164/32] & (1 << (164 % 32))) != 0; + } } }); } +pub fn update_tile_visibility( + z_index: ResMut, + mut query: Query<(&Tile, &mut Visibility)>, +) { + let z_index = (z_index.0 as i32 + 150) as usize; + + query.par_iter_mut().for_each(|(tile, mut visibility)| { + let is_visible = (tile.visible_range[z_index / 32] & (1 << (z_index % 32) as u32)) != 0; + *visibility = if is_visible { + Visibility::Visible + } else { + Visibility::Hidden + }; + }); +} + +// TODO CHECK THIS +pub fn update_occlusion_map_for_tile( + updated_pos: Vec3, + mut query_set: ParamSet<( + Query<(&Transform, &Tile)>, + Query<(&mut Visibility, &mut Tile, &Transform)>, + )>, +) { + use std::collections::HashMap; + + // Convert the updated position into integer coordinates + let updated_pos = ( + updated_pos.x as i32, + updated_pos.y as i32, + updated_pos.z as i32, + ); + + // Build a tile map as before, but only for relevant tiles + let tile_map: HashMap<(i32, i32, i32), (bool, u32)> = { + let first_query = query_set.p0(); + first_query + .iter() + .map(|(transform, tile)| { + ( + ( + transform.translation.x as i32, + transform.translation.y as i32, + transform.translation.z as i32, + ), + (tile.opaque, tile.id), + ) + }) + .collect() + }; + + // Get the second query for mutable access to tiles + let mut second_query = query_set.p1(); + + // Iterate over a 3×3 area centered around the updated tile + for x_offset in -1..=1 { + for y_offset in -1..=1 { + for z_offset in -1..=1 { + let current_pos = ( + updated_pos.0 + x_offset, + updated_pos.1 + y_offset, + updated_pos.2 + z_offset, + ); + + // Find the tile at the current position + if let Some((mut visibility, mut tile, _)) = + second_query.iter_mut().find(|(_, _, transform)| { + let pos = ( + transform.translation.x as i32, + transform.translation.y as i32, + transform.translation.z as i32, + ); + pos == current_pos + }) + { + // Clear the visible range for the current tile + tile.visible_range = [0; 8]; + + // Recalculate visibility for each possible z-index + for camera_z in current_pos.2 - 20..=current_pos.1 + 1 { + let mut is_visible = false; + + // Skip if the tile is above the camera + if current_pos.2 > camera_z { + continue; + } + + // Check for occluding tiles above + let mut is_occluded = false; + for z_offset in 1..=20 { + let above_pos = + (current_pos.0, current_pos.1, current_pos.2 + z_offset); + if above_pos.2 <= camera_z { + if let Some(&(is_opaque, _)) = tile_map.get(&above_pos) { + if is_opaque { + is_occluded = true; + break; + } + } + } + } + + if !is_occluded { + // Check neighbors for visibility + 'neighbor_check: for x_offset in -1..=1 { + for y_offset in -1..=1 { + for z_offset in 0..=1 { + if x_offset == 0 && y_offset == 0 && z_offset == 0 { + continue; + } + + let neighbor_pos = ( + current_pos.0 + x_offset, + current_pos.1 + y_offset, + current_pos.2 + z_offset, + ); + + if let Some(&(_, id)) = tile_map.get(&neighbor_pos) { + if id == 0 { + // sky tile + is_visible = true; + break 'neighbor_check; + } + } + } + } + } + } + + if is_visible { + tile.visible_range[((camera_z + 150) / 32) as usize] |= + 1 << (((camera_z + 150) % 32) as u32); + } + } + + // Update visibility state + *visibility = Visibility::Hidden; + } + } + } + } +} + pub fn tile_item_sprite_update( time: Res