1use crate::blit::Blit;
2use image::{Rgba, RgbaImage};
3use twmap::{Image, Layer, TwMap};
4use vek::az::UnwrappedAs;
5use wgpu::{
6 BindGroupLayoutEntry, BindingType, CommandEncoderDescriptor, Device, Extent3d, Origin3d, Queue,
7 ShaderStages, Texture, TextureAspect, TextureDescriptor, TextureDimension, TextureFormat,
8 TextureSampleType, TextureUsages, TextureView, TextureViewDescriptor, TextureViewDimension,
9};
10use wgpu::{CommandEncoder, TexelCopyBufferLayout, TexelCopyTextureInfo};
11
12const LABEL: Option<&str> = Some("Mapres");
13
14pub struct Mapres {
16 pub name: String,
17 pub width: u32,
18 pub height: u32,
19 pub texture: Texture,
20 pub array_texture: Option<Texture>,
21}
22
23pub struct MapresStorage {
25 pub mapres: Vec<Mapres>,
27 pub blank: Mapres,
29 _blit: Blit,
30}
31
32impl Mapres {
33 pub fn texture_layout_entry(
34 binding: u32,
35 view_dimension: TextureViewDimension,
36 ) -> BindGroupLayoutEntry {
37 BindGroupLayoutEntry {
38 binding,
39 visibility: ShaderStages::FRAGMENT,
40 ty: BindingType::Texture {
41 sample_type: TextureSampleType::Float { filterable: true },
42 view_dimension,
43 multisampled: false,
44 },
45 count: None,
46 }
47 }
48
49 fn from_rgba(
50 name: String,
51 image: &RgbaImage,
52 blit: &mut Blit,
53 encoder: &mut CommandEncoder,
54 device: &Device,
55 queue: &Queue,
56 ) -> Self {
57 let width = image.width();
58 let height = image.height();
59 let size = Extent3d {
60 width,
61 height,
62 depth_or_array_layers: 1,
63 };
64 let texture = device.create_texture(&TextureDescriptor {
65 label: Some(&format!("Image {name}")),
66 size,
67 mip_level_count: size.max_mips(TextureDimension::D2),
68 sample_count: 1,
69 dimension: TextureDimension::D2,
70 format: TextureFormat::Rgba8Unorm,
71 usage: TextureUsages::TEXTURE_BINDING
72 | TextureUsages::COPY_SRC
73 | TextureUsages::COPY_DST
74 | TextureUsages::RENDER_ATTACHMENT,
75 view_formats: &[],
76 });
77 queue.write_texture(
78 texture.as_image_copy(),
79 image.as_raw(),
80 TexelCopyBufferLayout {
81 offset: 0,
82 bytes_per_row: Some(4 * width),
83 rows_per_image: Some(height),
84 },
85 size,
86 );
87 blit.generate_mipmaps(encoder, &texture, device);
88 Self {
89 name,
90 width,
91 height,
92 texture,
93 array_texture: None,
94 }
95 }
96
97 pub fn generate_array_texture(
99 &mut self,
100 blit: &mut Blit,
101 encoder: &mut CommandEncoder,
102 device: &Device,
103 queue: &Queue,
104 ) {
105 if self.array_texture.is_some() {
106 return;
107 }
108 assert_eq!(self.width % 16, 0);
109 assert_eq!(self.height % 16, 0);
110 let tile_width = self.width / 16;
111 let tile_height = self.height / 16;
112 let tile_row_data_size = tile_width.unwrapped_as::<usize>() * 4;
113 let tile_data_size: usize = tile_row_data_size * tile_height.unwrapped_as::<usize>() * 4;
114 let size = Extent3d {
115 width: tile_width,
116 height: tile_height,
117 depth_or_array_layers: 256,
118 };
119 let array_texture = device.create_texture(&TextureDescriptor {
120 label: Some(&format!("Array Image {}", self.name)),
121 size,
122 mip_level_count: size.max_mips(TextureDimension::D2),
123 sample_count: 1,
124 dimension: TextureDimension::D2,
125 format: TextureFormat::Rgba8Unorm,
126 usage: TextureUsages::TEXTURE_BINDING
127 | TextureUsages::COPY_SRC
128 | TextureUsages::COPY_DST
129 | TextureUsages::RENDER_ATTACHMENT,
130 view_formats: &[],
131 });
132 let copy_size = Extent3d {
133 width: tile_width,
134 height: tile_height,
135 depth_or_array_layers: 1,
136 };
137
138 let top_left_tile = vec![0; tile_data_size];
139 queue.write_texture(
140 TexelCopyTextureInfo {
141 texture: &array_texture,
142 mip_level: 0,
143 origin: Origin3d { x: 0, y: 0, z: 0 },
144 aspect: TextureAspect::All,
145 },
146 &top_left_tile,
147 TexelCopyBufferLayout {
148 bytes_per_row: Some(tile_row_data_size.unwrapped_as()),
149 ..TexelCopyBufferLayout::default()
150 },
151 copy_size,
152 );
153
154 for y in 0..16 {
155 for x in 0..16 {
156 if (x, y) == (0, 0) {
157 continue;
158 }
159 encoder.copy_texture_to_texture(
160 TexelCopyTextureInfo {
161 texture: &self.texture,
162 mip_level: 0,
163 origin: Origin3d {
164 x: x * tile_width,
165 y: y * tile_height,
166 z: 0,
167 },
168 aspect: TextureAspect::All,
169 },
170 TexelCopyTextureInfo {
171 texture: &array_texture,
172 mip_level: 0,
173 origin: Origin3d {
174 x: 0,
175 y: 0,
176 z: y * 16 + x,
177 },
178 aspect: TextureAspect::All,
179 },
180 copy_size,
181 );
182 }
183 }
184 blit.generate_mipmaps(encoder, &array_texture, device);
185 self.array_texture = Some(array_texture);
186 }
187
188 pub fn array_texture(&self) -> &Texture {
191 match &self.array_texture {
192 None => {
193 panic!("Accessed array texture was not generated, use `generate_array_texture`")
194 }
195 Some(texture) => texture,
196 }
197 }
198}
199
200impl MapresStorage {
201 pub fn upload(map: &TwMap, device: &Device, queue: &Queue) -> Self {
202 let mut mapres = Vec::new();
203 let mut blit = Blit::new(device);
204
205 let mut tilemap_textures = [false; 64];
208
209 for group in &map.groups {
210 for layer in &group.layers {
211 if let Layer::Tiles(layer) = layer {
212 if let Some(image) = layer.image {
213 tilemap_textures[usize::from(image)] = true;
214 }
215 }
216 }
217 }
218
219 let mut encoder = device.create_command_encoder(&CommandEncoderDescriptor { label: LABEL });
220 for image in &map.images {
221 let new_mapres = match image {
222 Image::External(_) => panic!("External images can't be rendered, embed them!"),
223 Image::Embedded(emb) => Mapres::from_rgba(
224 image.name().clone(),
225 emb.image.unwrap_ref(),
226 &mut blit,
227 &mut encoder,
228 device,
229 queue,
230 ),
231 };
232 mapres.push(new_mapres);
233 }
234 queue.submit([encoder.finish()]);
235
236 for (i, mapres) in mapres.iter_mut().enumerate() {
237 if tilemap_textures[i] {
238 let mut encoder =
239 device.create_command_encoder(&CommandEncoderDescriptor { label: LABEL });
240 mapres.generate_array_texture(&mut blit, &mut encoder, device, queue);
241 queue.submit([encoder.finish()]);
242 }
243 }
244
245 let mut encoder = device.create_command_encoder(&CommandEncoderDescriptor { label: LABEL });
246 let mut blank = Mapres::from_rgba(
247 "Blank Image".into(),
248 &RgbaImage::from_pixel(16, 16, Rgba([255; 4])),
249 &mut blit,
250 &mut encoder,
251 device,
252 queue,
253 );
254 blank.generate_array_texture(&mut blit, &mut encoder, device, queue);
255 queue.submit([encoder.finish()]);
256 Self {
257 mapres,
258 blank,
259 _blit: blit,
260 }
261 }
262
263 pub fn view_texture(&self, index: Option<u16>) -> TextureView {
264 match index {
265 None => self
266 .blank
267 .texture
268 .create_view(&TextureViewDescriptor::default()),
269 Some(index) => self.mapres[index.unwrapped_as::<usize>()]
270 .texture
271 .create_view(&TextureViewDescriptor::default()),
272 }
273 }
274
275 pub fn view_array_texture(&self, index: Option<u16>) -> TextureView {
277 match index {
278 None => self
279 .blank
280 .array_texture()
281 .create_view(&TextureViewDescriptor {
282 array_layer_count: Some(256),
283 base_array_layer: 0,
284 ..TextureViewDescriptor::default()
285 }),
286 Some(index) => self.mapres[index.unwrapped_as::<usize>()]
288 .array_texture()
289 .create_view(&TextureViewDescriptor {
290 array_layer_count: Some(256),
291 base_array_layer: 0,
292 ..TextureViewDescriptor::default()
293 }),
294 }
296 }
297}