diff options
| author | Micha White <botahamec@outlook.com> | 2023-02-13 00:24:07 -0500 |
|---|---|---|
| committer | Micha White <botahamec@outlook.com> | 2023-02-13 00:24:07 -0500 |
| commit | 861b467b95be55db3a42182b77dba944869bf49f (patch) | |
| tree | f58057d5ab9ad53601332981a31553b0ad05fe1b /alligator_render/src/texture.rs | |
| parent | c31d51487082c6cf243ecd10da71a15a78d41add (diff) | |
Rename the subdirectories
Diffstat (limited to 'alligator_render/src/texture.rs')
| -rw-r--r-- | alligator_render/src/texture.rs | 250 |
1 files changed, 0 insertions, 250 deletions
diff --git a/alligator_render/src/texture.rs b/alligator_render/src/texture.rs deleted file mode 100644 index 76e77a8..0000000 --- a/alligator_render/src/texture.rs +++ /dev/null @@ -1,250 +0,0 @@ -use std::error::Error; -use std::num::NonZeroU32; -use std::sync::Arc; - -use alligator_resources::texture::{LoadError, Rgba16Texture, TextureId, TextureManager}; -use image::{EncodableLayout, GenericImage, RgbaImage}; -use texture_packer::TexturePacker; -use texture_packer::{ - exporter::{ExportResult, ImageExporter}, - TexturePackerConfig, -}; -use thiserror::Error; - -/// The texture did not fit in the texture atlas -#[derive(Debug, Error)] -#[error("{:?}", .0)] -pub struct PackError(PackErrorInternal); - -// TODO this can be removed when a new texture packer is made -type PackErrorInternal = impl std::fmt::Debug; - -#[derive(Error, Debug)] -pub enum TextureError { - #[error("{:?}", .0)] - TextureTooLarge(#[from] PackError), - #[error("{}", .0)] - BadImage(#[from] LoadError), - #[error("Unexpected Error (this is a bug in alligator_render): {}", .0)] - Unexpected(#[source] Box<dyn Error>), -} - -/// Simpler constructor for a wgpu extent3d -const fn extent_3d(width: u32, height: u32) -> wgpu::Extent3d { - wgpu::Extent3d { - width, - height, - depth_or_array_layers: 1, - } -} - -/// A texture atlas, usable by the renderer -// TODO make this Debug -// TODO make these resizable -pub struct TextureAtlas { - textures: Arc<TextureManager>, - packer: TexturePacker<'static, Rgba16Texture, TextureId>, - diffuse_texture: wgpu::Texture, - diffuse_bind_group: wgpu::BindGroup, - image: RgbaImage, - width: u32, - height: u32, - changed: bool, -} - -macro_rules! texture_info { - ($name: ident, $prop: ident, $divisor: ident) => { - pub fn $name(&mut self, id: TextureId) -> Result<f32, TextureError> { - let frame = match self.texture_frame(id) { - Some(frame) => frame, - None => { - self.load_texture(id)?; - self.texture_frame(id).unwrap() - } - }; - let property = frame.frame.$prop; - let value = property as f32 / self.$divisor as f32; - Ok(value) - } - }; -} - -impl TextureAtlas { - /// Creates a new texture atlas, with the given size - // TODO why is this u32? - // TODO this is still too large - pub fn new( - device: &wgpu::Device, - textures: Arc<TextureManager>, - width: u32, - height: u32, - ) -> (Self, wgpu::BindGroupLayout) { - let atlas_size = extent_3d(width, height); - let diffuse_texture = device.create_texture(&wgpu::TextureDescriptor { - label: Some("Diffuse Texture"), - size: atlas_size, - mip_level_count: 1, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: wgpu::TextureFormat::Rgba8Unorm, - usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, - view_formats: &[wgpu::TextureFormat::Rgba8UnormSrgb], - }); - - // TODO I don't think this refreshes anything - let diffuse_texture_view = - diffuse_texture.create_view(&wgpu::TextureViewDescriptor::default()); - - let diffuse_sampler = device.create_sampler(&wgpu::SamplerDescriptor::default()); - - let texture_bind_group_layout = - device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: Some("Texture Bind Group Layout"), - entries: &[ - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Texture { - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - view_dimension: wgpu::TextureViewDimension::D2, - multisampled: false, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), - count: None, - }, - ], - }); - - let diffuse_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - label: Some("Diffuse Bind Group"), - layout: &texture_bind_group_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::TextureView(&diffuse_texture_view), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::Sampler(&diffuse_sampler), - }, - ], - }); - - ( - Self { - textures, - packer: TexturePacker::new_skyline(TexturePackerConfig { - max_width: width, - max_height: height, - allow_rotation: false, - trim: false, - texture_padding: 0, - ..Default::default() - }), - diffuse_texture, - diffuse_bind_group, - width, - height, - image: RgbaImage::from_raw( - width, - height, - vec![0; 4 * width as usize * height as usize], - ) - .unwrap(), - changed: true, - }, - texture_bind_group_layout, - ) - } - - /// get the bind group for the texture - pub(crate) const fn bind_group(&self) -> &wgpu::BindGroup { - &self.diffuse_bind_group - } - - /// Load a new subtexture from memory - pub fn load_texture(&mut self, id: TextureId) -> Result<TextureId, TextureError> { - self.changed = true; - let img = self.textures.load_texture(id)?; - self.packer.pack_own(id, img).map_err(PackError)?; - Ok(id) - } - - /// Get the frame for s particular subtexture - fn texture_frame(&self, id: TextureId) -> Option<&texture_packer::Frame<TextureId>> { - self.packer.get_frame(&id) - } - - texture_info!(texture_width, w, width); - texture_info!(texture_height, h, height); - texture_info!(texture_x, x, width); - texture_info!(texture_y, y, height); - - /// Fill the cached image - fn fill_image(&mut self) -> ExportResult<()> { - let atlas = { - profiling::scope!("export atlas"); - ImageExporter::export(&self.packer)? - }; - profiling::scope!("copy image"); - self.image - .copy_from(&atlas, 0, 0) - .expect("image cache is too small"); - Ok(()) - } - - /// Clear the texture atlas, and give it a new size - pub fn clear(&mut self, width: u32, height: u32) { - self.changed = true; - self.width = width; - self.height = height; - self.packer = TexturePacker::new_skyline(TexturePackerConfig { - max_width: self.width, - max_height: self.height, - ..Default::default() - }); - } - - /// Fill the GPU texture atlas - #[profiling::function] - pub(crate) fn fill_textures(&mut self, queue: &wgpu::Queue) { - // saves time if nothing changed since the last time we did this - // FIXME This doesn't do much good once we get procedurally generated animation - // We'll have to create our own texture packer, with mutable subtextures, - // and more efficient algorithms. This'll also make frame times more consistent - if !self.changed { - return; - } - - let atlas_size = extent_3d(self.width, self.height); - - // put the packed texture into the base image - if let Err(e) = self.fill_image() { - log::error!("{}", e); - } - - // copy that to the gpu - queue.write_texture( - wgpu::ImageCopyTexture { - texture: &self.diffuse_texture, - mip_level: 0, - origin: wgpu::Origin3d::ZERO, - aspect: wgpu::TextureAspect::All, - }, - self.image.as_bytes(), - wgpu::ImageDataLayout { - offset: 0, - bytes_per_row: NonZeroU32::new(atlas_size.width * 4), - rows_per_image: NonZeroU32::new(atlas_size.height), - }, - atlas_size, - ); - - self.changed = false; - } -} |
