use std::error::Error; use image::{EncodableLayout, RgbaImage}; use thiserror::Error; #[derive(Error, Debug)] pub enum TextureError { #[error("Unexpected Error (this is a bug in alligator_render): {}", .0)] Unexpected(#[source] Box), } /// 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 these resizable #[derive(Debug)] pub struct TextureAtlas { diffuse_texture: wgpu::Texture, diffuse_bind_group: wgpu::BindGroup, image: RgbaImage, } impl TextureAtlas { /// Creates a new texture atlas, with the given size // TODO this is still too large pub fn new(device: &wgpu::Device, 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 { diffuse_texture, diffuse_bind_group, image: RgbaImage::from_raw( width, height, vec![0; 4 * width as usize * height as usize], ) .unwrap(), }, texture_bind_group_layout, ) } /// get the bind group for the texture pub(crate) const fn bind_group(&self) -> &wgpu::BindGroup { &self.diffuse_bind_group } pub(crate) fn fill_textures(&self, queue: &wgpu::Queue) { 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: Some(self.image.width() * 4), rows_per_image: Some(self.image.height()), }, extent_3d(self.image.width(), self.image.height()), ); } }