use std::path::PathBuf;
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum TileCompressionPreference {
Raw,
#[default]
FastLZ,
}
impl TileCompressionPreference {
#[must_use]
pub fn compression_strings(&self) -> (&'static [u8; 16], &'static [u8; 16]) {
match self {
Self::Raw => (
b"raw\0\0\0\0\0\0\0\0\0\0\0\0\0",
b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",
),
Self::FastLZ => (b"fastlz\0\0\0\0\0\0\0\0\0\0", b"fastlz0.1.0\0\0\0\0\0"),
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum BcFormat {
Bc1,
#[default]
Bc3,
Bc5,
Bc7,
}
impl BcFormat {
#[must_use]
pub const fn fourcc(&self) -> u32 {
match self {
Self::Bc1 => 0x3154_5844, Self::Bc3 => 0x3554_5844, Self::Bc5 => 0x3254_4341, Self::Bc7 => 0x3758_4344, }
}
#[must_use]
pub const fn block_size(&self) -> usize {
match self {
Self::Bc1 => 8,
Self::Bc3 | Self::Bc5 | Self::Bc7 => 16,
}
}
}
#[derive(Debug, Clone)]
pub struct TileSetConfiguration {
pub tile_width: u32,
pub tile_height: u32,
pub tile_border: u32,
pub page_size: u32,
pub compression: TileCompressionPreference,
pub embed_mip: bool,
pub deduplicate: bool,
}
impl Default for TileSetConfiguration {
fn default() -> Self {
Self {
tile_width: 144,
tile_height: 144,
tile_border: 8,
page_size: 0x0010_0000, compression: TileCompressionPreference::FastLZ,
embed_mip: true,
deduplicate: true,
}
}
}
impl TileSetConfiguration {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn validate(&self) -> Result<(), String> {
if self.tile_width % 4 != 0 {
return Err(format!(
"tile_width must be divisible by 4, got {}",
self.tile_width
));
}
if self.tile_height % 4 != 0 {
return Err(format!(
"tile_height must be divisible by 4, got {}",
self.tile_height
));
}
if self.tile_width < 16 || self.tile_width > 1024 {
return Err(format!(
"tile_width must be 16-1024, got {}",
self.tile_width
));
}
if self.tile_height < 16 || self.tile_height > 1024 {
return Err(format!(
"tile_height must be 16-1024, got {}",
self.tile_height
));
}
if self.tile_border % 4 != 0 {
return Err(format!(
"tile_border must be divisible by 4, got {}",
self.tile_border
));
}
if self.tile_border >= self.tile_width / 2 || self.tile_border >= self.tile_height / 2 {
return Err("tile_border must be less than half the tile dimensions".to_string());
}
let content_width = self.tile_width - 2 * self.tile_border;
let content_height = self.tile_height - 2 * self.tile_border;
if content_width == 0 || content_height == 0 {
return Err("Content area (tile - 2*border) must be positive".to_string());
}
if content_width % 4 != 0 || content_height % 4 != 0 {
return Err(format!(
"Content area must be divisible by 4, got {content_width}x{content_height}"
));
}
if self.page_size < 0x1_0000 {
return Err("page_size must be at least 64KB".to_string());
}
Ok(())
}
#[must_use]
pub const fn raw_tile_width(&self) -> u32 {
self.tile_width - 2 * self.tile_border
}
#[must_use]
pub const fn raw_tile_height(&self) -> u32 {
self.tile_height - 2 * self.tile_border
}
#[must_use]
pub const fn padded_tile_width(&self) -> u32 {
self.tile_width
}
#[must_use]
pub const fn padded_tile_height(&self) -> u32 {
self.tile_height
}
}
#[derive(Debug, Clone)]
pub struct SourceTexture {
pub name: String,
pub base_map: Option<PathBuf>,
pub normal_map: Option<PathBuf>,
pub physical_map: Option<PathBuf>,
}
impl SourceTexture {
#[must_use]
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
base_map: None,
normal_map: None,
physical_map: None,
}
}
#[must_use]
pub fn with_base_map(mut self, path: impl Into<PathBuf>) -> Self {
self.base_map = Some(path.into());
self
}
#[must_use]
pub fn with_normal_map(mut self, path: impl Into<PathBuf>) -> Self {
self.normal_map = Some(path.into());
self
}
#[must_use]
pub fn with_physical_map(mut self, path: impl Into<PathBuf>) -> Self {
self.physical_map = Some(path.into());
self
}
#[must_use]
pub fn layer_paths(&self) -> [Option<&PathBuf>; 3] {
[
self.base_map.as_ref(),
self.normal_map.as_ref(),
self.physical_map.as_ref(),
]
}
#[must_use]
pub fn has_any_layer(&self) -> bool {
self.base_map.is_some() || self.normal_map.is_some() || self.physical_map.is_some()
}
}