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<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 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()),
);
}
}
|