From 5d299af0e592c76c62c6347152eae2257a95c71f Mon Sep 17 00:00:00 2001 From: Micha White Date: Sat, 1 Oct 2022 17:52:50 -0400 Subject: Quick and dirty texture atlases --- src/lib.rs | 2 ++ src/renderer.rs | 2 ++ src/texture.rs | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+) create mode 100644 src/texture.rs (limited to 'src') diff --git a/src/lib.rs b/src/lib.rs index bacb53d..1453ef0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ #![feature(let_else)] #![feature(nonzero_min_max)] +#![feature(type_alias_impl_trait)] #![warn(clippy::pedantic)] #![warn(clippy::nursery)] #![allow(clippy::module_name_repetitions)] @@ -8,6 +9,7 @@ pub mod camera; pub mod config; pub mod instance; pub mod renderer; +mod texture; mod vertex; pub(crate) use camera::Camera; diff --git a/src/renderer.rs b/src/renderer.rs index 55333f4..d45233b 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -35,11 +35,13 @@ pub enum NewRendererError { #[error(transparent)] NoGpu(#[from] NoGpuError), #[error(transparent)] + // TODO better error WindowInitError(#[from] OsError), } #[derive(Debug)] pub struct Renderer { + // TODO move some of this data elsewhere surface: wgpu::Surface, surface_config: wgpu::SurfaceConfiguration, supported_present_modes: Box<[wgpu::PresentMode]>, diff --git a/src/texture.rs b/src/texture.rs new file mode 100644 index 0000000..aa6f871 --- /dev/null +++ b/src/texture.rs @@ -0,0 +1,89 @@ +use std::error::Error; +use std::sync::atomic::{AtomicUsize, Ordering}; + +use image::error::DecodingError; +use image::{DynamicImage, ImageError}; +use texture_packer::{ + exporter::{ExportResult, ImageExporter}, + MultiTexturePacker, TexturePackerConfig, +}; +use thiserror::Error; + +static NEXT_TEXTURE_ID: AtomicUsize = AtomicUsize::new(0); + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +struct TextureId(usize); + +impl TextureId { + pub fn new() -> Self { + Self(NEXT_TEXTURE_ID.fetch_add(1, Ordering::Relaxed)) + } +} + +type PackError = impl std::fmt::Debug; + +#[derive(Error, Debug)] +pub enum TextureError { + #[error("{:?}", .0)] + TextureTooLarge(PackError), // use an error with a source + #[error("{}", .0)] + BadImage(#[source] DecodingError), // TODO don't export this + #[error("Unexpected Error (this is a bug in alligator_render): {}", .0)] + Unexpected(#[source] Box), +} + +impl From for TextureError { + fn from(ie: ImageError) -> Self { + match ie { + ImageError::Decoding(de) => Self::BadImage(de), + _ => Self::Unexpected(Box::new(ie)), + } + } +} + +struct TextureAtlases<'a> { + packer: MultiTexturePacker<'a, image::RgbaImage, TextureId>, +} + +impl<'a> Default for TextureAtlases<'a> { + fn default() -> Self { + Self { + packer: MultiTexturePacker::new_skyline(TexturePackerConfig::default()), + } + } +} + +impl<'a> TextureAtlases<'a> { + /// Creates a new texture atlas, with the given size + // TODO why is this u32? + pub fn new(width: u32, height: u32) -> Self { + Self { + packer: MultiTexturePacker::new_skyline(TexturePackerConfig { + max_width: width, + max_height: height, + ..Default::default() + }), + } + } + + // TODO specify format + // TODO support RGBA16 + pub fn load_from_memory(&mut self, buf: &[u8]) -> Result { + let img = image::load_from_memory(buf)?.into_rgba8(); + let id = TextureId::new(); + self.packer + .pack_own(id, img) + .map_err(TextureError::TextureTooLarge)?; + + Ok(id) + } + + pub(crate) fn atlases(&self) -> ExportResult> { + self.packer + .get_pages() + .iter() + .map(ImageExporter::export) + .collect::>>() + .map(Vec::into_boxed_slice) + } +} -- cgit v1.2.3