baked occlusion bit mapping
This commit is contained in:
+224
-64
@@ -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>>,
|
||||
tile_query: Query<(&Transform, &Tile)>,
|
||||
z_index: ResMut<game::ZIndex>,
|
||||
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<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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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<Time>,
|
||||
mut query_tile: Query<(Entity, &Children, &mut TileState), With<Tile>>,
|
||||
|
||||
Reference in New Issue
Block a user