use crate::blit::Blit;
use image::{Rgba, RgbaImage};
use twmap::{Image, Layer, TwMap};
use vek::az::UnwrappedAs;
use wgpu::{
BindGroupLayoutEntry, BindingType, CommandEncoderDescriptor, Device, Extent3d, Origin3d, Queue,
ShaderStages, Texture, TextureAspect, TextureDescriptor, TextureDimension, TextureFormat,
TextureSampleType, TextureUsages, TextureView, TextureViewDescriptor, TextureViewDimension,
};
use wgpu::{CommandEncoder, TexelCopyBufferLayout, TexelCopyTextureInfo};
const LABEL: Option<&str> = Some("Mapres");
pub struct Mapres {
pub name: String,
pub width: u32,
pub height: u32,
pub texture: Texture,
pub array_texture: Option<Texture>,
}
pub struct MapresStorage {
pub mapres: Vec<Mapres>,
pub blank: Mapres,
_blit: Blit,
}
impl Mapres {
pub fn texture_layout_entry(
binding: u32,
view_dimension: TextureViewDimension,
) -> BindGroupLayoutEntry {
BindGroupLayoutEntry {
binding,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Texture {
sample_type: TextureSampleType::Float { filterable: true },
view_dimension,
multisampled: false,
},
count: None,
}
}
fn from_rgba(
name: String,
image: &RgbaImage,
blit: &mut Blit,
encoder: &mut CommandEncoder,
device: &Device,
queue: &Queue,
) -> Self {
let width = image.width();
let height = image.height();
let size = Extent3d {
width,
height,
depth_or_array_layers: 1,
};
let texture = device.create_texture(&TextureDescriptor {
label: Some(&format!("Image {name}")),
size,
mip_level_count: size.max_mips(TextureDimension::D2),
sample_count: 1,
dimension: TextureDimension::D2,
format: TextureFormat::Rgba8Unorm,
usage: TextureUsages::TEXTURE_BINDING
| TextureUsages::COPY_SRC
| TextureUsages::COPY_DST
| TextureUsages::RENDER_ATTACHMENT,
view_formats: &[],
});
queue.write_texture(
texture.as_image_copy(),
image.as_raw(),
TexelCopyBufferLayout {
offset: 0,
bytes_per_row: Some(4 * width),
rows_per_image: Some(height),
},
size,
);
blit.generate_mipmaps(encoder, &texture, device);
Self {
name,
width,
height,
texture,
array_texture: None,
}
}
pub fn generate_array_texture(
&mut self,
blit: &mut Blit,
encoder: &mut CommandEncoder,
device: &Device,
queue: &Queue,
) {
if self.array_texture.is_some() {
return;
}
assert_eq!(self.width % 16, 0);
assert_eq!(self.height % 16, 0);
let tile_width = self.width / 16;
let tile_height = self.height / 16;
let tile_row_data_size = tile_width.unwrapped_as::<usize>() * 4;
let tile_data_size: usize = tile_row_data_size * tile_height.unwrapped_as::<usize>() * 4;
let size = Extent3d {
width: tile_width,
height: tile_height,
depth_or_array_layers: 256,
};
let array_texture = device.create_texture(&TextureDescriptor {
label: Some(&format!("Array Image {}", self.name)),
size,
mip_level_count: size.max_mips(TextureDimension::D2),
sample_count: 1,
dimension: TextureDimension::D2,
format: TextureFormat::Rgba8Unorm,
usage: TextureUsages::TEXTURE_BINDING
| TextureUsages::COPY_SRC
| TextureUsages::COPY_DST
| TextureUsages::RENDER_ATTACHMENT,
view_formats: &[],
});
let copy_size = Extent3d {
width: tile_width,
height: tile_height,
depth_or_array_layers: 1,
};
let top_left_tile = vec![0; tile_data_size];
queue.write_texture(
TexelCopyTextureInfo {
texture: &array_texture,
mip_level: 0,
origin: Origin3d { x: 0, y: 0, z: 0 },
aspect: TextureAspect::All,
},
&top_left_tile,
TexelCopyBufferLayout {
bytes_per_row: Some(tile_row_data_size.unwrapped_as()),
..TexelCopyBufferLayout::default()
},
copy_size,
);
for y in 0..16 {
for x in 0..16 {
if (x, y) == (0, 0) {
continue;
}
encoder.copy_texture_to_texture(
TexelCopyTextureInfo {
texture: &self.texture,
mip_level: 0,
origin: Origin3d {
x: x * tile_width,
y: y * tile_height,
z: 0,
},
aspect: TextureAspect::All,
},
TexelCopyTextureInfo {
texture: &array_texture,
mip_level: 0,
origin: Origin3d {
x: 0,
y: 0,
z: y * 16 + x,
},
aspect: TextureAspect::All,
},
copy_size,
);
}
}
blit.generate_mipmaps(encoder, &array_texture, device);
self.array_texture = Some(array_texture);
}
pub fn array_texture(&self) -> &Texture {
match &self.array_texture {
None => {
panic!("Accessed array texture was not generated, use `generate_array_texture`")
}
Some(texture) => texture,
}
}
}
impl MapresStorage {
pub fn upload(map: &TwMap, device: &Device, queue: &Queue) -> Self {
let mut mapres = Vec::new();
let mut blit = Blit::new(device);
let mut tilemap_textures = [false; 64];
for group in &map.groups {
for layer in &group.layers {
if let Layer::Tiles(layer) = layer {
if let Some(image) = layer.image {
tilemap_textures[usize::from(image)] = true;
}
}
}
}
let mut encoder = device.create_command_encoder(&CommandEncoderDescriptor { label: LABEL });
for image in &map.images {
let new_mapres = match image {
Image::External(_) => panic!("External images can't be rendered, embed them!"),
Image::Embedded(emb) => Mapres::from_rgba(
image.name().clone(),
emb.image.unwrap_ref(),
&mut blit,
&mut encoder,
device,
queue,
),
};
mapres.push(new_mapres);
}
queue.submit([encoder.finish()]);
for (i, mapres) in mapres.iter_mut().enumerate() {
if tilemap_textures[i] {
let mut encoder =
device.create_command_encoder(&CommandEncoderDescriptor { label: LABEL });
mapres.generate_array_texture(&mut blit, &mut encoder, device, queue);
queue.submit([encoder.finish()]);
}
}
let mut encoder = device.create_command_encoder(&CommandEncoderDescriptor { label: LABEL });
let mut blank = Mapres::from_rgba(
"Blank Image".into(),
&RgbaImage::from_pixel(16, 16, Rgba([255; 4])),
&mut blit,
&mut encoder,
device,
queue,
);
blank.generate_array_texture(&mut blit, &mut encoder, device, queue);
queue.submit([encoder.finish()]);
Self {
mapres,
blank,
_blit: blit,
}
}
pub fn view_texture(&self, index: Option<u16>) -> TextureView {
match index {
None => self
.blank
.texture
.create_view(&TextureViewDescriptor::default()),
Some(index) => self.mapres[index.unwrapped_as::<usize>()]
.texture
.create_view(&TextureViewDescriptor::default()),
}
}
pub fn view_array_texture(&self, index: Option<u16>) -> TextureView {
match index {
None => self
.blank
.array_texture()
.create_view(&TextureViewDescriptor {
array_layer_count: Some(256),
base_array_layer: 0,
..TextureViewDescriptor::default()
}),
Some(index) => self.mapres[index.unwrapped_as::<usize>()]
.array_texture()
.create_view(&TextureViewDescriptor {
array_layer_count: Some(256),
base_array_layer: 0,
..TextureViewDescriptor::default()
}),
}
}
}