Galactica/crates/render/src/texturearray.rs

200 lines
5.2 KiB
Rust
Raw Normal View History

2024-01-09 21:45:30 -08:00
use crate::globaluniform::{AtlasArray, AtlasImageLocation, SpriteData, SpriteDataArray};
2023-12-24 16:18:36 -08:00
use anyhow::Result;
use bytemuck::Zeroable;
2024-01-04 22:17:34 -08:00
use galactica_constants::ASSET_CACHE;
2024-01-09 21:45:30 -08:00
use galactica_content::Content;
use galactica_packer::SpriteAtlasImage;
2023-12-30 11:25:51 -08:00
use image::GenericImageView;
2024-01-04 22:17:34 -08:00
use std::{fs::File, io::Read, num::NonZeroU32, path::Path};
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: &[],
});
2024-01-09 21:45:30 -08:00
let view = texture.create_view(&Default::default());
2023-12-30 11:25:51 -08:00
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)]
2023-12-30 11:25:51 -08:00
pub struct Texture {
2024-01-07 12:16:07 -08:00
pub index: u32, // Index in texture array
pub len: u32, // Number of frames
pub frame_duration: f32, // Frames per second
pub aspect: f32, // width / height
pub repeat: u32, // How to re-play this texture
pub location: Vec<SpriteAtlasImage>,
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,
pub image_locations: AtlasArray,
2024-01-04 18:15:30 -08:00
pub sprite_data: SpriteDataArray,
2023-12-24 16:18:36 -08:00
}
impl TextureArray {
2024-01-09 21:45:30 -08:00
pub fn new(device: &wgpu::Device, queue: &wgpu::Queue, ct: &Content) -> Result<Self> {
2023-12-24 16:18:36 -08:00
// Load all textures
let mut texture_data = Vec::new();
2024-01-04 22:17:34 -08:00
for a in ct.atlas_files() {
println!("opening {a}");
let p = Path::new(ASSET_CACHE);
let mut f = File::open(p.join(a))?;
let mut bytes = Vec::new();
f.read_to_end(&mut bytes)?;
texture_data.push(RawTexture::from_bytes(
&device,
&queue,
&bytes,
&format!("Atlas `{a}`"),
)?);
}
let mut image_locations = AtlasArray::zeroed();
2024-01-04 18:15:30 -08:00
let mut sprite_data = SpriteDataArray::zeroed();
println!("sending to gpu");
2024-01-04 18:15:30 -08:00
let mut image_counter = 0;
2024-01-04 19:18:47 -08:00
let mut sprite_counter = 0;
for t in &ct.sprites {
2024-01-04 19:18:47 -08:00
sprite_data.data[sprite_counter] = SpriteData {
2024-01-04 18:15:30 -08:00
frame_count: t.frames.len() as u32,
repeatmode: t.repeat.as_int(),
aspect: t.aspect,
2024-01-07 12:16:07 -08:00
frame_duration: t.frame_duration,
2024-01-04 18:15:30 -08:00
first_frame: image_counter,
_padding: Default::default(),
};
2024-01-04 19:18:47 -08:00
sprite_counter += 1;
// Insert texture location data
for path in &t.frames {
let image = ct.get_image(&path);
2024-01-06 14:02:50 -08:00
image_locations.data[image_counter as usize] = AtlasImageLocation {
2024-01-04 18:15:30 -08:00
xpos: image.x,
ypos: image.y,
width: image.w,
height: image.h,
2024-01-04 22:17:34 -08:00
atlas_texture: image.atlas,
_padding: Default::default(),
};
2024-01-04 18:15:30 -08:00
image_counter += 1;
}
}
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::VERTEX_FRAGMENT,
2023-12-24 16:18:36 -08:00
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::VERTEX_FRAGMENT,
2023-12-24 16:18:36 -08:00
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,
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,
2024-01-04 18:15:30 -08:00
image_locations,
sprite_data,
2023-12-24 16:18:36 -08:00
});
}
}