baked occlusion bit mapping

This commit is contained in:
StephenAdamson
2024-12-20 00:28:28 +00:00
parent b47e6cce69
commit d78314eb51
4 changed files with 236 additions and 70 deletions
+4 -4
View File
@@ -29,7 +29,7 @@ fn setup_tilemap(commands: &mut Commands, asset_server: &Res<AssetServer>) {
for x in -50..50 { for x in -50..50 {
for y 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); let position = Vec3::new(x as f32 * TILE_SIZE, y as f32 * TILE_SIZE, -z as f32);
// Generate Perlin noise value // Generate Perlin noise value
@@ -66,9 +66,9 @@ fn setup_tilemap(commands: &mut Commands, asset_server: &Res<AssetServer>) {
} }
} }
} }
for x in -15..15 { for x in -50..50 {
for y in -15..15 { for y in -50..50 {
let position = Vec3::new(x as f32 * TILE_SIZE, y as f32 * TILE_SIZE, -39.); let position = Vec3::new(x as f32 * TILE_SIZE, y as f32 * TILE_SIZE, -150.);
commands.spawn(TilePrefab::bedrock(position, asset_server)); commands.spawn(TilePrefab::bedrock(position, asset_server));
} }
} }
+2 -2
View File
@@ -32,7 +32,7 @@ fn main() {
cursor::setup_cursor, 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( .add_systems(
FixedUpdate, FixedUpdate,
( (
@@ -46,7 +46,7 @@ fn main() {
FixedUpdate, FixedUpdate,
( (
tile::camera_z_movement, tile::camera_z_movement,
tile::tile_sprite_occlusion tile::update_tile_visibility
.run_if(|camera_moved: Res<tile::CameraMoved>| camera_moved.0), .run_if(|camera_moved: Res<tile::CameraMoved>| camera_moved.0),
), ),
) )
+208 -48
View File
@@ -1,12 +1,15 @@
use crate::item::Item;
use crate::game; use crate::game;
use crate::item::Item;
use bevy::ecs::system::ParamSet;
use bevy::prelude::*; use bevy::prelude::*;
use std::collections::HashMap;
#[derive(Component)] #[derive(Component, Clone)]
#[require(Sprite)] #[require(Sprite)]
pub struct Tile { pub struct Tile {
pub id: u32, pub id: u32,
pub opaque: bool, pub opaque: bool,
pub visible_range: [u32; 8],
} }
impl Default for Tile { impl Default for Tile {
@@ -14,6 +17,7 @@ impl Default for Tile {
Self { Self {
id: 1, id: 1,
opaque: true, opaque: true,
visible_range: [0; 8],
} }
} }
} }
@@ -44,7 +48,7 @@ pub struct WallTile {
#[derive(Component)] #[derive(Component)]
#[require(Tile)] #[require(Tile)]
pub struct HasWater { pub struct HasWater {
pub height : u8, pub height: u8,
pub astarmultiplier: f32, pub astarmultiplier: f32,
pub swimmable: bool, pub swimmable: bool,
} }
@@ -68,91 +72,247 @@ pub fn camera_z_movement(
camera_moved.0 = false; camera_moved.0 = false;
if keyboard_input.pressed(KeyCode::ShiftLeft) { if keyboard_input.pressed(KeyCode::ShiftLeft) {
z_index.0 -= 1.0; 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; camera_moved.0 = true;
} }
if keyboard_input.pressed(KeyCode::ShiftRight) { if keyboard_input.pressed(KeyCode::ShiftRight) {
z_index.0 += 1.0; 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; camera_moved.0 = true;
} }
} }
pub fn tile_sprite_occlusion( pub fn tile_sprite_generate_occlusion_map(
mut query: Query<(&mut Visibility, &Transform, &Tile), With<Tile>>, mut query_set: ParamSet<(Query<(&Transform, &Tile)>, Query<(&mut Tile, &Transform)>)>,
tile_query: Query<(&Transform, &Tile)>,
z_index: ResMut<game::ZIndex>,
) { ) {
use std::collections::HashMap; // First, create the tile map using only the first query
let tile_map: HashMap<(i32, i32, i32), (bool, u32)> = {
// Build spatial lookup let first_query = query_set.p0();
let tile_map: HashMap<(i32, i32, i32), &Tile> = tile_query first_query
.iter() .iter()
.map(|(transform, tile)| { .map(|(transform, tile)| {
( (
( (
transform.translation.x.round() as i32, transform.translation.x as i32,
transform.translation.y.round() as i32, transform.translation.y as i32,
transform.translation.z.round() as i32, transform.translation.z as i32,
), ),
tile, (tile.opaque, tile.id),
) )
}) })
.collect(); .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() .par_iter_mut()
.for_each(|(mut visibility, transform, _)| { .for_each(|(mut tile, transform)| {
*visibility = Visibility::Hidden; // Clear existing visible range
tile.visible_range = [0; 8];
// Early exit for above camera layer
if transform.translation.z > z_index.0 {
return;
}
let pos = ( let pos = (
transform.translation.x.round() as i32, transform.translation.x as i32,
transform.translation.y.round() as i32, transform.translation.y as i32,
transform.translation.z.round() as i32, transform.translation.z as i32,
); );
// Check for any opaque tiles above the current tile // Check visibility for each possible z-index
for z_offset in 1..=100 { for camera_z in -150..100 {
// Adjust the max height of the check if needed let mut is_visible = false;
// Skip if tile is above camera
if pos.2 > camera_z {
continue;
}
// 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); let above_pos = (pos.0, pos.1, pos.2 + z_offset);
// look up until camera height if above_pos.2 <= camera_z {
if above_pos.2 <= z_index.0 as i32 { if let Some(&(is_opaque, _)) = tile_map.get(&above_pos) {
// get any tiles if is_opaque {
if let Some(neighbor_tile) = tile_map.get(&above_pos) { is_occluded = true;
// If there's an opaque tile above, hide the current tile break 'vertical_check;
if neighbor_tile.opaque {
return; // No need to check further; the tile should remain hidden
} }
} }
} }
} }
// Check all neighbors around the tile if !is_occluded {
for x_offset in -1..=1 { // Check neighbors for visibility
'neighbor_check: for x_offset in -1..=1 {
for y_offset in -1..=1 { for y_offset in -1..=1 {
for z_offset in -1..=1 { for z_offset in 0..=1 {
if x_offset == 0 && y_offset == 0 && z_offset == 0 { if x_offset == 0 && y_offset == 0 && z_offset == 0 {
continue; continue;
} }
let neighbor_pos = (pos.0 + x_offset, pos.1 + y_offset, pos.2 + z_offset); 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) { if let Some(&(_, id)) = tile_map.get(&neighbor_pos) {
// id == 0 is sky if id == 0 {
if neighbor_tile.id == 0 { // sky tile
*visibility = Visibility::Visible; 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<game::ZIndex>,
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; 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( pub fn tile_item_sprite_update(
+6
View File
@@ -21,6 +21,7 @@ impl TilePrefab {
tile: Tile { tile: Tile {
id: 1, id: 1,
opaque: true, opaque: true,
visible_range: [0;8],
}, },
tile_state: TileState { tile_state: TileState {
timer: Timer::from_seconds(1.0, TimerMode::Repeating), timer: Timer::from_seconds(1.0, TimerMode::Repeating),
@@ -38,6 +39,7 @@ impl TilePrefab {
tile: Tile { tile: Tile {
id: 2, id: 2,
opaque: true, opaque: true,
visible_range: [0;8],
}, },
tile_state: TileState { tile_state: TileState {
timer: Timer::from_seconds(1.0, TimerMode::Repeating), timer: Timer::from_seconds(1.0, TimerMode::Repeating),
@@ -55,6 +57,7 @@ impl TilePrefab {
tile: Tile { tile: Tile {
id: 3, id: 3,
opaque: true, opaque: true,
visible_range: [0;8],
}, },
tile_state: TileState { tile_state: TileState {
timer: Timer::from_seconds(1.0, TimerMode::Repeating), timer: Timer::from_seconds(1.0, TimerMode::Repeating),
@@ -71,6 +74,7 @@ impl TilePrefab {
tile: Tile { tile: Tile {
id: 0, id: 0,
opaque: false, opaque: false,
visible_range: [0;8],
}, },
tile_state: TileState { tile_state: TileState {
timer: Timer::from_seconds(1.0, TimerMode::Repeating), timer: Timer::from_seconds(1.0, TimerMode::Repeating),
@@ -87,6 +91,7 @@ impl TilePrefab {
tile: Tile { tile: Tile {
id: 4, id: 4,
opaque: true, opaque: true,
visible_range: [0;8],
}, },
tile_state: TileState { tile_state: TileState {
timer: Timer::from_seconds(1.0, TimerMode::Repeating), timer: Timer::from_seconds(1.0, TimerMode::Repeating),
@@ -104,6 +109,7 @@ impl TilePrefab {
tile: Tile { tile: Tile {
id: 5, id: 5,
opaque: true, opaque: true,
visible_range: [0;8],
}, },
tile_state: TileState { tile_state: TileState {
timer: Timer::from_seconds(1.0, TimerMode::Repeating), timer: Timer::from_seconds(1.0, TimerMode::Repeating),