97 lines
2.6 KiB
Rust

mod atlasset;
use atlasset::AtlasSet;
use anyhow::{bail, Context, Result};
use image::io::Reader;
use std::path::{Path, PathBuf};
use walkdir::WalkDir;
// TODO: warning when images have extra transparency
// TODO: dynamic packing (for plugins)
// TODO: standalone cli (galactica should ask to run this)
// TODO: randomly assign sprites to textures, for efficiency
// TODO: group images by use case
fn main() -> Result<()> {
let mut files = Vec::new();
let asset_root = Path::new("./assets/render");
// Total number of pixels we want to add
let mut total_dim = 0f64;
for e in WalkDir::new(&asset_root).into_iter().filter_map(|e| e.ok()) {
if e.metadata().unwrap().is_file() {
// TODO: better warnings
match e.path().extension() {
Some(t) => {
if t.to_str() != Some("png") && t.to_str() != Some("jpg") {
println!("[WARNING] {e:#?} is not a png file, skipping.");
continue;
}
}
None => {
println!("[WARNING] {e:#?} is not a png file, skipping.");
continue;
}
}
let path = e.path().to_path_buf();
let reader = Reader::open(&path)
.with_context(|| format!("While reading file `{}`", path.display()))?;
let dim = reader.into_dimensions().with_context(|| {
format!("While reading dimensions of file `{}`", path.display())
})?;
files.push((path, [dim.0, dim.1]));
total_dim += dim.0 as f64 * dim.1 as f64;
}
}
// Sort by image size
files.sort_by(|a, b| {
let a = a.1[0] * a.1[1];
let b = b.1[0] * b.1[1];
b.cmp(&a)
});
// Make sure we have enough pixels.
// This check is conservative and imperfect:
// Our tiling algorithm usually has efficiency better than 80% (~90%, as of writing)
// We need room for error, though, since this check doesn't guarante success.
if total_dim / 0.80 >= (8192.0 * 8192.0 * 16.0) {
bail!("Texture atlas is too small")
}
// Create atlas set
let mut atlas_set = AtlasSet::new(8192, 8192, 16, &asset_root, 1);
let total = files.len();
let mut i = 0;
let mut peak_efficiency = 0f64;
for (path, _) in files {
i += 1;
let atlas_idx = atlas_set.write_image(&path)?;
println!(
"({i} of {total}, atlas {atlas_idx}, efficiency {:.02}%) Added {}",
100.0 * atlas_set.get_efficiency(),
path.display()
);
peak_efficiency = peak_efficiency.max(atlas_set.get_efficiency());
}
println!(
"Packing efficiency: {:.02}%",
100.0 * atlas_set.get_efficiency()
);
println!("Peak efficiency: {:.02}%", 100.0 * peak_efficiency);
println!("Saving files...");
atlas_set.save_files(
|x| PathBuf::from(format!("cache/atlas-{x:0.2}.bmp")),
&PathBuf::from("cache/spriteatlas.toml"),
)?;
return Ok(());
}