use super::config::TileSetConfiguration;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TileCoord {
pub layer: u8,
pub level: u8,
pub x: u16,
pub y: u16,
}
impl TileCoord {
#[must_use]
pub const fn new(layer: u8, level: u8, x: u16, y: u16) -> Self {
Self { layer, level, x, y }
}
#[must_use]
pub fn to_packed_id(self) -> u32 {
((self.x as u32) << 20)
| ((self.y as u32) << 8)
| ((self.level as u32) << 4)
| (self.layer as u32)
}
}
#[derive(Debug, Clone)]
pub struct LevelInfo {
pub width_tiles: u32,
pub height_tiles: u32,
pub width_pixels: u32,
pub height_pixels: u32,
}
#[derive(Debug, Clone)]
pub struct GeometryResult {
pub total_width: u32,
pub total_height: u32,
pub levels: Vec<LevelInfo>,
pub tiles_per_layer: [Vec<TileCoord>; 3],
}
pub fn calculate_geometry(
textures: &[(String, u32, u32)],
layers_present: [bool; 3],
config: &TileSetConfiguration,
max_mip_levels: Option<u32>,
) -> GeometryResult {
if textures.is_empty() {
return GeometryResult {
total_width: 0,
total_height: 0,
levels: Vec::new(),
tiles_per_layer: [Vec::new(), Vec::new(), Vec::new()],
};
}
let raw_tile_width = config.raw_tile_width();
let raw_tile_height = config.raw_tile_height();
let (_name, tex_width, tex_height) = &textures[0];
let total_width = *tex_width;
let total_height = *tex_height;
let calculated_mips = calculate_mip_levels(
total_width,
total_height,
raw_tile_width.min(raw_tile_height),
);
let mip_levels = max_mip_levels.map_or(calculated_mips, |max| max.min(calculated_mips));
let mut levels = Vec::with_capacity(mip_levels as usize);
let mut tiles_per_layer: [Vec<TileCoord>; 3] = [Vec::new(), Vec::new(), Vec::new()];
let mut level_width = total_width;
let mut level_height = total_height;
for level in 0..mip_levels {
let width_tiles = tiles_for_dimension(level_width, raw_tile_width);
let height_tiles = tiles_for_dimension(level_height, raw_tile_height);
levels.push(LevelInfo {
width_tiles,
height_tiles,
width_pixels: level_width,
height_pixels: level_height,
});
for (layer_idx, present) in layers_present.iter().enumerate() {
if *present {
for ty in 0..height_tiles {
for tx in 0..width_tiles {
tiles_per_layer[layer_idx].push(TileCoord::new(
layer_idx as u8,
level as u8,
tx as u16,
ty as u16,
));
}
}
}
}
level_width = (level_width / 2).max(1);
level_height = (level_height / 2).max(1);
if level_width < raw_tile_width && level_height < raw_tile_height {
break;
}
}
GeometryResult {
total_width,
total_height,
levels,
tiles_per_layer,
}
}
#[must_use]
pub fn calculate_mip_levels(width: u32, height: u32, min_size: u32) -> u32 {
let max_dim = width.max(height);
let min_dim = min_size.max(1);
let mut levels = 1u32;
let mut dim = max_dim;
while dim > min_dim {
dim /= 2;
levels += 1;
}
levels
}
#[must_use]
pub fn tiles_for_dimension(pixels: u32, tile_size: u32) -> u32 {
pixels.div_ceil(tile_size)
}