optimize visibility calcs
This commit is contained in:
@@ -146,8 +146,18 @@ pub fn generate_chunk_terrain(
|
||||
});
|
||||
});
|
||||
|
||||
for (pos, data) in tilemap_updates.into_inner().unwrap() {
|
||||
let new_positions: Vec<IVec3> = tilemap_updates
|
||||
.into_inner()
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(|(pos, data)| {
|
||||
tilemap.floor_tiles.insert(pos, data);
|
||||
pos
|
||||
})
|
||||
.collect();
|
||||
|
||||
// All tiles now in tilemap — safe to calculate visibility
|
||||
for pos in new_positions {
|
||||
occlusion_event_writer.write(TileOcclusionEvent { tile_position: pos });
|
||||
}
|
||||
|
||||
|
||||
@@ -209,39 +209,42 @@ pub fn build_quilted_terrain_sprites(
|
||||
_ => return,
|
||||
};
|
||||
|
||||
// Bounds are clamped to the chunk grid so textures are always exactly
|
||||
// CHUNK_TILES wide/tall (or smaller at world edges), keeping sizes
|
||||
// predictable and buffer reuse rates high.
|
||||
let chunk_world_min_x = key.chunk_x as f32 * TILE_SIZE * CHUNK_TILES as f32;
|
||||
let chunk_world_min_y = key.chunk_y as f32 * TILE_SIZE * CHUNK_TILES as f32;
|
||||
|
||||
// Actual tile extent within this chunk (may be smaller than full chunk
|
||||
// at world edges or sparse z-levels).
|
||||
// Actual tile extent within this chunk, padded by one tile on each side
|
||||
// so the rendered quad always shows one tile of context beyond the edge
|
||||
// (prevents the hard black cutoff at chunk/world boundaries).
|
||||
let min_x_aligned: f32 = ((tiles.iter().map(|(p, _)| p.x).reduce(f32::min).unwrap()
|
||||
- TILE_SIZE / 2.0)
|
||||
/ TILE_SIZE)
|
||||
.floor()
|
||||
* TILE_SIZE;
|
||||
* TILE_SIZE
|
||||
- TILE_SIZE;
|
||||
let min_y_aligned: f32 = ((tiles.iter().map(|(p, _)| p.y).reduce(f32::min).unwrap()
|
||||
- TILE_SIZE / 2.0)
|
||||
/ TILE_SIZE)
|
||||
.floor()
|
||||
* TILE_SIZE;
|
||||
* TILE_SIZE
|
||||
- TILE_SIZE;
|
||||
let max_x_aligned: f32 = ((tiles.iter().map(|(p, _)| p.x).reduce(f32::max).unwrap()
|
||||
+ TILE_SIZE / 2.0)
|
||||
/ TILE_SIZE)
|
||||
.ceil()
|
||||
* TILE_SIZE;
|
||||
* TILE_SIZE
|
||||
+ TILE_SIZE;
|
||||
let max_y_aligned: f32 = ((tiles.iter().map(|(p, _)| p.y).reduce(f32::max).unwrap()
|
||||
+ TILE_SIZE / 2.0)
|
||||
/ TILE_SIZE)
|
||||
.ceil()
|
||||
* TILE_SIZE;
|
||||
* TILE_SIZE
|
||||
+ TILE_SIZE;
|
||||
|
||||
let width_tiles = ((max_x_aligned - min_x_aligned) / TILE_SIZE) as u32;
|
||||
let height_tiles = ((max_y_aligned - min_y_aligned) / TILE_SIZE) as u32;
|
||||
let width_px = width_tiles * TILE_PIXELS;
|
||||
let height_px = height_tiles * TILE_PIXELS;
|
||||
|
||||
// Add 2px bleed (1px each side) so adjacent chunk quads overlap by 1px
|
||||
// at any zoom level. Without this, sub-pixel gaps appear between chunks
|
||||
// at fractional orthographic scales.
|
||||
let width_px = width_tiles * TILE_PIXELS + 2;
|
||||
let height_px = height_tiles * TILE_PIXELS + 2;
|
||||
let required_len = (width_px * height_px * 4) as usize;
|
||||
|
||||
dimensions_results
|
||||
@@ -281,8 +284,8 @@ pub fn build_quilted_terrain_sprites(
|
||||
src_height,
|
||||
width_px,
|
||||
height_px,
|
||||
tile_x * TILE_PIXELS,
|
||||
tile_y * TILE_PIXELS,
|
||||
tile_x * TILE_PIXELS + 1, // +1 to account for 1px bleed border
|
||||
tile_y * TILE_PIXELS + 1,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -328,13 +331,13 @@ pub fn build_quilted_terrain_sprites(
|
||||
..Default::default()
|
||||
},
|
||||
Transform::from_xyz(
|
||||
center_x - TILE_SIZE / 2.0,
|
||||
center_y - TILE_SIZE / 2.0,
|
||||
center_x - TILE_SIZE / 2.0 - PIXEL_RATIO,
|
||||
center_y - TILE_SIZE / 2.0 + PIXEL_RATIO,
|
||||
-Z_BELOW * TILE_SIZE,
|
||||
)
|
||||
.with_scale(Vec3::splat(PIXEL_RATIO)),
|
||||
Visibility::Hidden,
|
||||
TerrainSprite { key },
|
||||
TerrainSprite { key }, // access z_index via .key.z_index
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -118,34 +118,8 @@ pub fn handle_tile_occlusion_updates(
|
||||
pub fn calculate_visibility(pos: IVec3, tilemap: &TileMap) -> [u32; 8] {
|
||||
let mut visible_range = [0u32; 8];
|
||||
|
||||
for mut camera_z in -Z_BELOW as i32..=Z_ABOVE as i32 {
|
||||
camera_z *= ITILE_SIZE;
|
||||
let mut is_visible = false;
|
||||
|
||||
if pos.z > camera_z {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut is_occluded = false;
|
||||
|
||||
let v_check_height: i32 = Z_TOTAL as i32 - (camera_z / ITILE_SIZE) + Z_BELOW as i32 + 1;
|
||||
'vertical_check: for z_offset in 1..v_check_height {
|
||||
let above_pos = IVec3::new(pos.x, pos.y, pos.z + (z_offset * ITILE_SIZE));
|
||||
if above_pos.z <= camera_z {
|
||||
if let Some(&(_, _, _, visibly_transparent, _, _)) =
|
||||
tilemap.floor_tiles.get(&above_pos)
|
||||
{
|
||||
if !visibly_transparent {
|
||||
is_occluded = true;
|
||||
break 'vertical_check;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break 'vertical_check;
|
||||
}
|
||||
}
|
||||
if !is_occluded {
|
||||
'neighbor_check: for x_offset in -1..=1 {
|
||||
let touches_air = '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 {
|
||||
@@ -156,23 +130,61 @@ pub fn calculate_visibility(pos: IVec3, tilemap: &TileMap) -> [u32; 8] {
|
||||
pos.y + y_offset * ITILE_SIZE,
|
||||
pos.z + z_offset * ITILE_SIZE,
|
||||
);
|
||||
match tilemap.floor_tiles.get(&neighbor_pos) {
|
||||
Some(&(id, _, _, _, _, _)) if id == 0 => break 'neighbor_check true,
|
||||
None => {}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
};
|
||||
|
||||
if let Some(&(id, _, _, _, _, _)) = tilemap.floor_tiles.get(&neighbor_pos) {
|
||||
if id == 0 {
|
||||
// id 0 = air tile
|
||||
is_visible = true;
|
||||
break 'neighbor_check;
|
||||
}
|
||||
}
|
||||
}
|
||||
if !touches_air {
|
||||
return visible_range;
|
||||
}
|
||||
|
||||
let base_camera_index = (pos.z / ITILE_SIZE) + Z_BELOW as i32;
|
||||
|
||||
if base_camera_index >= 0 && base_camera_index <= Z_TOTAL as i32 {
|
||||
if touches_air {
|
||||
let z2 = base_camera_index as usize;
|
||||
visible_range[z2 / 32] |= 1 << (z2 % 32);
|
||||
}
|
||||
}
|
||||
|
||||
if is_visible {
|
||||
let z2 = ((camera_z / ITILE_SIZE) + Z_BELOW as i32) as usize;
|
||||
let mut occluded = false;
|
||||
|
||||
for z_offset in 1..=(Z_TOTAL as i32 + Z_BELOW as i32 + 1) {
|
||||
let check_z = pos.z + z_offset * ITILE_SIZE;
|
||||
let camera_z_index = (check_z / ITILE_SIZE) + Z_BELOW as i32;
|
||||
|
||||
if camera_z_index < 0 {
|
||||
match tilemap.floor_tiles.get(&IVec3::new(pos.x, pos.y, check_z)) {
|
||||
Some(&(_, _, _, visibly_transparent, _, _)) => occluded = !visibly_transparent,
|
||||
None => occluded = false,
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if camera_z_index > Z_TOTAL as i32 {
|
||||
break;
|
||||
}
|
||||
|
||||
if !occluded {
|
||||
let z2 = camera_z_index as usize;
|
||||
visible_range[z2 / 32] |= 1 << ((z2 % 32) as u32);
|
||||
}
|
||||
|
||||
let tile = tilemap.floor_tiles.get(&IVec3::new(pos.x, pos.y, check_z));
|
||||
|
||||
match tile {
|
||||
Some(&(_, _, _, visibly_transparent, _, _)) => {
|
||||
occluded = !visibly_transparent;
|
||||
}
|
||||
None => occluded = false,
|
||||
}
|
||||
}
|
||||
|
||||
visible_range
|
||||
|
||||
Reference in New Issue
Block a user