twgpu/textures/
mapres.rs

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
14// TODO: maybe make texture optional as well
15pub 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
23/// Contains all textures used in a map
24pub struct MapresStorage {
25    /// All mapres contained in the map, in order
26    pub mapres: Vec<Mapres>,
27    /// White texture used when a layer doesn't specify an image
28    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    /// Generates the array texture for this mapres, if it isn't already generated
98    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    /// Accessor to the array texture of this mapres
189    /// Panics it `generate_array_texture` wasn't called previously
190    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        // There are at max 64 mapres.
206        // This array stores, which of the mapres also need to be turned into array textures.
207        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    /// Generates array texture on first use
276    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            //.create_view(&TextureViewDescriptor::default()),
287            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            //.create_view(&TextureViewDescriptor::default()),
295        }
296    }
297}