diff options
Diffstat (limited to 'alligator_resources/src')
| -rw-r--r-- | alligator_resources/src/lib.rs | 10 | ||||
| -rw-r--r-- | alligator_resources/src/texture.rs | 160 |
2 files changed, 125 insertions, 45 deletions
diff --git a/alligator_resources/src/lib.rs b/alligator_resources/src/lib.rs index b85eab1..0f7b544 100644 --- a/alligator_resources/src/lib.rs +++ b/alligator_resources/src/lib.rs @@ -5,4 +5,12 @@ pub mod texture; -struct ResourceManager {} +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum Priority { + Unnecessary, + Possible(u8), + Eventual(u8), + Urgent, +} + +pub struct ResourceManager {} diff --git a/alligator_resources/src/texture.rs b/alligator_resources/src/texture.rs index f1d34fc..9aca56d 100644 --- a/alligator_resources/src/texture.rs +++ b/alligator_resources/src/texture.rs @@ -2,12 +2,15 @@ use std::collections::HashMap; use std::mem::{self, MaybeUninit}; use std::path::Path; use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Arc; use image::{GenericImage, ImageBuffer}; use texture_packer::exporter::ImageExporter; use texture_packer::{Frame, TexturePacker, TexturePackerConfig}; use thiserror::Error; +use crate::Priority; + static NEXT_TEXTURE_ID: AtomicUsize = AtomicUsize::new(0); #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] @@ -82,12 +85,12 @@ fn texture_size(image: &Rgba16Texture) -> usize { image.len() * mem::size_of::<image::Rgba<u16>>() } -struct ImageFile { +struct TextureFile { path: Box<Path>, - texture: Option<Rgba16Texture>, + texture: Option<Arc<Rgba16Texture>>, } -impl ImageFile { +impl TextureFile { #[allow(clippy::missing_const_for_fn)] fn new(path: impl AsRef<Path>) -> Self { Self { @@ -97,21 +100,21 @@ impl ImageFile { } fn open(path: impl AsRef<Path>) -> Result<Self, LoadError> { - let texture = image::open(&path).map_err(convert_image_load_error)?; - let texture = texture.to_rgba16(); - let texture = vec_image_to_box(texture); + let mut this = Self::new(path); + this.load()?; - Ok(Self { - path: path.as_ref().into(), - texture: Some(texture), - }) + Ok(this) + } + + const fn is_loaded(&self) -> bool { + self.texture.is_some() } fn load(&mut self) -> Result<&Rgba16Texture, LoadError> { if self.texture.is_none() { let texture = image::open(&self.path).map_err(convert_image_load_error)?; let texture = texture.to_rgba16(); - let texture = vec_image_to_box(texture); + let texture = Arc::new(vec_image_to_box(texture)); self.texture = Some(texture); } @@ -123,14 +126,99 @@ impl ImageFile { } fn allocated_size(&self) -> usize { - self.texture - .as_ref() - .map_or(0, |texture| texture.len() * mem::size_of::<u16>()) + self.texture.as_ref().map_or(0, |t| texture_size(t)) + } +} + +enum TextureBuffer { + Memory(Arc<Rgba16Texture>), + Disk(TextureFile), +} + +struct Texture { + priority: Priority, + buffer: TextureBuffer, +} + +impl Texture { + fn from_buffer(texture: Rgba16Texture) -> Self { + Self { + priority: Priority::Urgent, + buffer: TextureBuffer::Memory(Arc::new(texture)), + } + } + + fn from_path(path: impl AsRef<Path>, priority: Priority) -> Self { + Self { + priority, + buffer: TextureBuffer::Disk(TextureFile::new(path)), + } + } + + const fn priority(&self) -> Priority { + self.priority + } + + fn set_priority(&mut self, priority: Priority) { + self.priority = priority; + } + + fn load_texture(&mut self) -> Result<&Rgba16Texture, LoadError> { + match &mut self.buffer { + TextureBuffer::Memory(ref texture) => Ok(texture), + TextureBuffer::Disk(file) => file.load(), + } + } +} + +pub struct TextureRef<'a> { + id: TextureId, + texture: Arc<Rgba16Texture>, + manager: &'a TextureManager, +} + +impl<'a> TextureRef<'a> { + fn texture_width(&self) -> u32 { + self.texture.width() + } + + fn texture_height(&self) -> u32 { + self.texture.height() + } + + #[allow(clippy::missing_const_for_fn)] + fn texture(&self) -> &Rgba16Texture { + &self.texture + } + + // TODO: it's safer to replace this with a position thingy + #[must_use] + pub fn atlas_x(&self) -> f32 { + self.manager + .subtexture_x(self.id) + .expect("not in texture atlas") + } + + #[must_use] + pub fn atlas_y(&self) -> f32 { + self.manager + .subtexture_y(self.id) + .expect("not in texture atlas") + } + + #[must_use] + pub fn width(&self) -> f32 { + self.manager.texture_width(self) + } + + #[must_use] + pub fn height(&self) -> f32 { + self.manager.texture_height(self) } } pub struct TextureManager { - textures: HashMap<TextureId, Rgba16Texture>, + textures: HashMap<TextureId, Texture>, packer: TexturePacker<'static, Rgba16Texture, TextureId>, atlas: Rgba16Texture, // cached texture atlas width: u32, @@ -179,32 +267,22 @@ impl TextureManager { } } - pub fn load_to_atlas(&mut self, id: TextureId) { - let texture = self.texture(id); - if self.packer.pack_own(id, texture.clone()).is_err() { - let texture = self.texture(id); + pub fn load_to_atlas(&mut self, id: TextureId, texture: &TextureRef) { + let get_texture = || texture.texture().clone(); + + if self.packer.pack_own(id, get_texture()).is_err() { + let texture = get_texture(); self.resize_atlas(self.width + texture.width(), self.height + texture.height()); - let texture = self.texture(id); self.packer - .pack_own(id, texture.clone()) + .pack_own(id, get_texture()) .expect("packer is still too small after resizing"); } } - /// Resize the texture atlas + /// Clear and resize the texture atlas pub fn resize_atlas(&mut self, width: u32, height: u32) { - let old_packer = &self.packer; - let mut new_packer = packer(width, height); - - for id in old_packer.get_frames().keys() { - let texture = self.texture(*id).clone(); - new_packer - .pack_own(*id, texture) - .expect("resized packer is too small to hold subtextures"); - } - - self.packer = new_packer; + self.packer = packer(width, height); } /// Clear the texture atlas @@ -228,6 +306,7 @@ impl TextureManager { let texture = texture.map_err(convert_image_decoding)?; let texture = texture.into_rgba16(); let texture = vec_image_to_box(texture); + let texture = Texture::from_buffer(texture); self.textures.insert(id, texture); @@ -237,7 +316,6 @@ impl TextureManager { pub fn atlas(&mut self) -> &Rgba16Texture { let atlas = { profiling::scope!("export atlas"); - // TODO unexpect_msg? ImageExporter::export(&self.packer).expect("ImageExporter error?") }; @@ -249,10 +327,6 @@ impl TextureManager { &self.atlas } - fn texture(&self, id: TextureId) -> &Rgba16Texture { - self.textures.get(&id).expect("invalid TextureId") - } - /// Get the subtexture in the texture atlas fn subtexture(&self, id: TextureId) -> Option<&Frame<TextureId>> { self.packer.get_frame(&id) @@ -277,16 +351,14 @@ impl TextureManager { /// Get the width of a texture #[must_use] #[allow(clippy::cast_precision_loss)] - pub fn texture_width(&self, id: TextureId) -> f32 { - let width = self.texture(id).width(); - width as f32 / self.width as f32 + pub fn texture_width(&self, texture: &TextureRef) -> f32 { + texture.texture_width() as f32 / self.width as f32 } /// Get the height of a texture #[must_use] #[allow(clippy::cast_precision_loss)] - pub fn texture_height(&self, id: TextureId) -> f32 { - let height = self.texture(id).height(); - height as f32 / self.height as f32 + pub fn texture_height(&self, texture: &TextureRef) -> f32 { + texture.texture_height() as f32 / self.height as f32 } } |
