Galactica/crates/render/src/texturearray.rs

182 lines
4.7 KiB
Rust
Raw Normal View History

2023-12-31 18:48:35 -08:00
use crate::content;
2023-12-24 16:18:36 -08:00
use anyhow::Result;
2023-12-30 11:25:51 -08:00
use image::GenericImageView;
use std::{collections::HashMap, fs::File, io::Read, num::NonZeroU32};
2023-12-24 16:18:36 -08:00
use wgpu::BindGroupLayout;
2023-12-31 18:48:35 -08:00
pub(crate) struct RawTexture {
pub(crate) view: wgpu::TextureView,
2023-12-30 11:25:51 -08:00
}
impl RawTexture {
2023-12-31 18:48:35 -08:00
pub(crate) fn from_bytes(
2023-12-30 11:25:51 -08:00
device: &wgpu::Device,
queue: &wgpu::Queue,
bytes: &[u8],
label: &str,
) -> Result<Self> {
let img = image::load_from_memory(bytes)?;
Self::from_image(device, queue, &img, Some(label))
}
2023-12-31 18:48:35 -08:00
pub(crate) fn from_image(
2023-12-30 11:25:51 -08:00
device: &wgpu::Device,
queue: &wgpu::Queue,
img: &image::DynamicImage,
label: Option<&str>,
) -> Result<Self> {
let rgba = img.to_rgba8();
let dimensions = img.dimensions();
let size = wgpu::Extent3d {
width: dimensions.0,
height: dimensions.1,
depth_or_array_layers: 1,
};
let texture = device.create_texture(&wgpu::TextureDescriptor {
label,
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,
view_formats: &[],
});
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
queue.write_texture(
wgpu::ImageCopyTexture {
aspect: wgpu::TextureAspect::All,
texture: &texture,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
},
&rgba,
wgpu::ImageDataLayout {
offset: 0,
bytes_per_row: Some(4 * dimensions.0),
rows_per_image: Some(dimensions.1),
},
size,
);
Ok(Self { view })
}
}
#[derive(Debug, Clone, Copy)]
pub struct Texture {
pub index: u32, // Index in texture array
2024-01-03 07:46:27 -08:00
pub len: u32, // Number of frames
pub fps: f32, // Frames per second
2023-12-30 11:25:51 -08:00
pub aspect: f32, // width / height
2024-01-03 07:46:27 -08:00
pub repeat: u32, // How to re-play this texture
2023-12-30 11:25:51 -08:00
}
2023-12-24 18:45:39 -08:00
2023-12-24 16:18:36 -08:00
pub struct TextureArray {
pub bind_group: wgpu::BindGroup,
pub bind_group_layout: BindGroupLayout,
2023-12-31 18:48:35 -08:00
starfield_handle: content::TextureHandle,
textures: HashMap<content::TextureHandle, Texture>,
2023-12-24 16:18:36 -08:00
}
impl TextureArray {
2023-12-24 18:45:39 -08:00
pub fn get_starfield_texture(&self) -> Texture {
*self.textures.get(&self.starfield_handle).unwrap()
2023-12-24 16:18:36 -08:00
}
2023-12-31 18:48:35 -08:00
pub fn get_texture(&self, handle: content::TextureHandle) -> Texture {
match self.textures.get(&handle) {
2023-12-24 16:18:36 -08:00
Some(x) => *x,
None => unreachable!("Tried to get a texture that doesn't exist"),
2023-12-24 16:18:36 -08:00
}
}
2023-12-31 18:48:35 -08:00
pub fn new(device: &wgpu::Device, queue: &wgpu::Queue, ct: &content::Content) -> Result<Self> {
2023-12-24 16:18:36 -08:00
// Load all textures
let mut texture_data = Vec::new();
let mut textures = HashMap::new();
for t in &ct.textures {
2024-01-03 07:46:27 -08:00
let index = texture_data.len() as u32;
for f in &t.frames {
let mut f = File::open(&f)?;
let mut bytes = Vec::new();
f.read_to_end(&mut bytes)?;
texture_data.push(RawTexture::from_bytes(&device, &queue, &bytes, &t.name)?);
}
textures.insert(
t.handle,
Texture {
2024-01-03 07:46:27 -08:00
index,
aspect: t.handle.aspect,
2024-01-03 07:46:27 -08:00
fps: t.fps,
len: t.frames.len() as u32,
repeat: t.repeat.as_int(),
},
);
}
2023-12-24 16:18:36 -08:00
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge,
mag_filter: wgpu::FilterMode::Linear,
min_filter: wgpu::FilterMode::Nearest,
mipmap_filter: wgpu::FilterMode::Nearest,
..Default::default()
});
let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
2023-12-27 17:57:11 -08:00
label: Some("TextureArray bind group layout"),
2023-12-24 16:18:36 -08:00
entries: &[
// Texture data
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
multisampled: false,
view_dimension: wgpu::TextureViewDimension::D2,
sample_type: wgpu::TextureSampleType::Float { filterable: true },
},
count: NonZeroU32::new(texture_data.len() as u32),
2023-12-24 16:18:36 -08:00
},
// Texture sampler
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
2023-12-25 18:24:55 -08:00
count: NonZeroU32::new(1),
2023-12-24 16:18:36 -08:00
},
],
});
let views: Vec<&wgpu::TextureView> = texture_data.iter().map(|x| &x.view).collect();
2023-12-24 16:18:36 -08:00
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
2023-12-27 17:57:11 -08:00
label: Some("TextureArray bind group"),
2023-12-24 16:18:36 -08:00
layout: &bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
// Array of all views
resource: wgpu::BindingResource::TextureViewArray(&views),
},
wgpu::BindGroupEntry {
binding: 1,
2023-12-25 18:24:55 -08:00
resource: wgpu::BindingResource::SamplerArray(&[&sampler]),
2023-12-24 16:18:36 -08:00
},
],
});
return Ok(Self {
bind_group,
bind_group_layout,
textures: textures,
starfield_handle: ct.get_starfield_handle(),
2023-12-24 16:18:36 -08:00
});
}
}