summaryrefslogtreecommitdiff
path: root/src/texture.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/texture.rs')
-rw-r--r--src/texture.rs287
1 files changed, 0 insertions, 287 deletions
diff --git a/src/texture.rs b/src/texture.rs
deleted file mode 100644
index e343508..0000000
--- a/src/texture.rs
+++ /dev/null
@@ -1,287 +0,0 @@
-use std::error::Error;
-use std::num::NonZeroU32;
-use std::sync::atomic::{AtomicUsize, Ordering};
-
-use image::error::DecodingError;
-use image::{EncodableLayout, GenericImage, ImageError, RgbaImage};
-use texture_packer::TexturePacker;
-use texture_packer::{
- exporter::{ExportResult, ImageExporter},
- TexturePackerConfig,
-};
-use thiserror::Error;
-
-static NEXT_TEXTURE_ID: AtomicUsize = AtomicUsize::new(0);
-
-/// The unique ID for a subtexture
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-pub struct TextureId(usize);
-
-impl TextureId {
- #[allow(clippy::new_without_default)]
- pub fn new() -> Self {
- Self(NEXT_TEXTURE_ID.fetch_add(1, Ordering::Relaxed))
- }
-}
-
-/// These are the formats supported by the renderer.
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-#[non_exhaustive]
-pub enum ImageFormat {
- Bmp,
- Ico,
- Farbfeld,
-}
-
-impl ImageFormat {
- const fn format(self) -> image::ImageFormat {
- match self {
- Self::Bmp => image::ImageFormat::Bmp,
- Self::Ico => image::ImageFormat::Ico,
- Self::Farbfeld => image::ImageFormat::Farbfeld,
- }
- }
-}
-
-/// 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(
- #[source]
- #[from]
- PackError,
- ),
- #[error("{}", .0)]
- BadImage(
- #[source]
- #[from]
- DecodingError,
- ), // TODO don't export this
- #[error("Unexpected Error (this is a bug in alligator_render): {}", .0)]
- Unexpected(#[source] Box<dyn Error>),
-}
-
-impl From<ImageError> for TextureError {
- fn from(ie: ImageError) -> Self {
- match ie {
- ImageError::Decoding(de) => de.into(),
- _ => Self::Unexpected(Box::new(ie)),
- }
- }
-}
-
-/// 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 {
- packer: TexturePacker<'static, image::RgbaImage, 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(&self, id: TextureId) -> Option<f32> {
- let frame = self.texture_frame(id)?;
- let property = frame.frame.$prop;
- let value = property as f32 / self.$divisor as f32;
- Some(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, 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::Rgba8UnormSrgb,
- usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
- });
-
- // 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 {
- 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
- // TODO support RGBA16
- pub fn load_from_memory(
- &mut self,
- buf: &[u8],
- format: ImageFormat,
- ) -> Result<TextureId, TextureError> {
- let img = image::load_from_memory_with_format(buf, format.format())?.into_rgba8();
- let id = TextureId::new();
- 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
- pub fn clear(&mut self) {
- self.packer = TexturePacker::new_skyline(TexturePackerConfig {
- max_width: self.width,
- max_height: self.height,
- ..Default::default()
- });
- }
-
- /// Fill the GPU texture atlas
- #[profiling::function]
- pub 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;
- }
-}