pixel_engine_backend/
decals.rs

1use crate::Vertex;
2use float_next_after::NextAfter;
3use wgpu::util::DeviceExt;
4pub type DecalTextureID = usize;
5
6mod gpu_vector;
7
8#[derive(Debug)]
9pub struct DecalInstances {
10    pub id: DecalTextureID,
11    pub pos: [(f32, f32); 4],
12    pub uv: [(f32, f32); 4],
13    pub w: [f32; 4],
14    pub tint: [f32; 4],
15}
16
17#[derive(Debug, Clone)]
18pub struct Decal {
19    id: DecalTextureID,
20    pub size: (u32, u32),
21    pub uv_scale: (f32, f32),
22}
23
24pub struct DecalContextManager {
25    id_generator: DecalIDGenerator,
26    decal_textures:
27        std::collections::HashMap<DecalTextureID, (crate::texture::Texture, wgpu::BindGroup)>,
28    pub decal_instances: Vec<DecalInstances>,
29    vertex_vector: gpu_vector::GpuVector<[Vertex; 4]>,
30    cpu_vertex_vector: Vec<[Vertex; 4]>,
31    buffer_index: wgpu::Buffer,
32}
33
34impl DecalContextManager {
35    #[must_use]
36    pub fn new(
37        device: &wgpu::Device,
38        queue: &wgpu::Queue,
39        bind_group_layout: &wgpu::BindGroupLayout,
40        spr: (&[u8], (u32, u32)),
41    ) -> (Self, wgpu::CommandBuffer) {
42        let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
43            label: Some("decal"),
44        });
45        let vertex_vector =
46            gpu_vector::GpuVector::with_capacity(128, device, wgpu::BufferUsages::VERTEX);
47        let buffer_index = {
48            let b = device.create_buffer(&wgpu::BufferDescriptor {
49                mapped_at_creation: false,
50                label: None,
51                size: std::mem::size_of::<[u16; 6]>() as u64,
52                usage: wgpu::BufferUsages::COPY_DST
53                    | wgpu::BufferUsages::INDEX
54                    | wgpu::BufferUsages::COPY_SRC,
55            });
56            encoder.copy_buffer_to_buffer(
57                &{
58                    device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
59                        label: None,
60                        contents: bytemuck::cast_slice(crate::INDICES),
61                        usage: wgpu::BufferUsages::INDEX
62                            | wgpu::BufferUsages::COPY_DST
63                            | wgpu::BufferUsages::COPY_SRC,
64                    })
65                },
66                0,
67                &b,
68                0,
69                std::mem::size_of::<[u16; 6]>() as u64,
70            );
71            b
72        };
73        (
74            Self {
75                id_generator: DecalIDGenerator(0),
76                buffer_index,
77                vertex_vector,
78                decal_textures: {
79                    let mut out = std::collections::HashMap::with_capacity(64);
80
81                    let tex = crate::texture::Texture::from_bytes(device, queue, spr);
82                    let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
83                        layout: bind_group_layout,
84                        entries: &[
85                            wgpu::BindGroupEntry {
86                                binding: 0,
87                                resource: wgpu::BindingResource::TextureView(&tex.view),
88                            },
89                            wgpu::BindGroupEntry {
90                                binding: 1,
91                                resource: wgpu::BindingResource::Sampler(&tex.sampler),
92                            },
93                        ],
94                        label: Some("decal_bindgroup"),
95                    });
96
97                    out.insert(0usize, (tex, bind_group));
98                    out
99                },
100                decal_instances: {
101                    let mut out = Vec::with_capacity(128);
102                    out.push(DecalInstances {
103                        id: 0,
104                        pos: {
105                            use crate::CORNER;
106                            [
107                                (-CORNER, CORNER),
108                                (-CORNER, -CORNER),
109                                (CORNER, -CORNER),
110                                (CORNER, CORNER),
111                            ]
112                        },
113                        uv: [(0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0)],
114                        w: [1.0; 4],
115                        tint: [1.0; 4],
116                    });
117                    out
118                },
119                cpu_vertex_vector: Vec::with_capacity(128),
120            },
121            encoder.finish(),
122        )
123    }
124    pub fn add_instance(&mut self, decal: DecalInstances) {
125        self.decal_instances.push(decal);
126    }
127
128    pub fn update_main_texture(&mut self, queue: &wgpu::Queue, data: &[u8]) {
129        (self.decal_textures[&0].0).update(queue, data);
130    }
131}
132
133#[derive(Debug, Clone)]
134struct DecalIDGenerator(DecalTextureID);
135impl DecalIDGenerator {
136    fn get(&mut self) -> usize {
137        self.0 += 1;
138        self.0
139    }
140}
141
142impl Decal {
143    pub fn create(ctx: &mut crate::Context, sprite: (&[u8], (u32, u32))) -> Self {
144        let id = ctx.dcm.id_generator.get();
145        let tex = crate::texture::Texture::from_bytes(&ctx.device, &ctx.queue, sprite);
146        let bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {
147            layout: &ctx.bind_group_layout,
148            entries: &[
149                wgpu::BindGroupEntry {
150                    binding: 0,
151                    resource: wgpu::BindingResource::TextureView(&tex.view),
152                },
153                wgpu::BindGroupEntry {
154                    binding: 1,
155                    resource: wgpu::BindingResource::Sampler(&tex.sampler),
156                },
157            ],
158            label: Some("decal_bindgroup"),
159        });
160        ctx.dcm.decal_textures.insert(id, (tex, bind_group));
161
162        Self {
163            id,
164            size: sprite.1,
165            uv_scale: (1.0 / (sprite.1).0 as f32, 1.0 / (sprite.1).1 as f32),
166        }
167    }
168
169    pub fn destroy(&self, ctx: &mut crate::Context) {
170        ctx.dcm.decal_textures.remove(&self.id);
171    }
172
173    #[must_use]
174    pub fn id(&self) -> DecalTextureID {
175        self.id
176    }
177}
178
179pub trait DrawDecals<'a, 'b>
180where
181    'b: 'a,
182{
183    fn draw_decals(
184        &mut self,
185        dcm: &'b mut DecalContextManager,
186        device: &'b mut wgpu::Device,
187        queue: &'b mut wgpu::Queue,
188    );
189}
190
191impl<'a, 'b> DrawDecals<'a, 'b> for wgpu::RenderPass<'a>
192where
193    'b: 'a,
194{
195    fn draw_decals(
196        &mut self,
197        dcm: &'b mut DecalContextManager,
198        device: &'b mut wgpu::Device,
199        queue: &'b mut wgpu::Queue,
200    ) {
201        let mut z = 0.0;
202        for decal_instance in dcm.decal_instances.iter() {
203            //z += 0.05;
204            dcm.cpu_vertex_vector.push([
205                Vertex {
206                    position: [decal_instance.pos[0].0, decal_instance.pos[0].1, z],
207                    tex_coords: [
208                        decal_instance.uv[0].0,
209                        decal_instance.uv[0].1,
210                        decal_instance.w[0],
211                    ],
212                    tint: decal_instance.tint,
213                },
214                Vertex {
215                    position: [decal_instance.pos[1].0, decal_instance.pos[1].1, z],
216                    tex_coords: [
217                        decal_instance.uv[1].0,
218                        decal_instance.uv[1].1,
219                        decal_instance.w[1],
220                    ],
221                    tint: decal_instance.tint,
222                },
223                Vertex {
224                    position: [decal_instance.pos[2].0, decal_instance.pos[2].1, z],
225                    tex_coords: [
226                        decal_instance.uv[2].0,
227                        decal_instance.uv[2].1,
228                        decal_instance.w[2],
229                    ],
230                    tint: decal_instance.tint,
231                },
232                Vertex {
233                    position: [decal_instance.pos[3].0, decal_instance.pos[3].1, z],
234                    tex_coords: [
235                        decal_instance.uv[3].0,
236                        decal_instance.uv[3].1,
237                        decal_instance.w[3],
238                    ],
239                    tint: decal_instance.tint,
240                },
241            ]);
242
243            z = z.next_after(-f32::INFINITY);
244        }
245
246        let command = dcm.vertex_vector.sync(device, &dcm.cpu_vertex_vector);
247        let buffer = dcm.vertex_vector.buffer();
248
249        self.set_index_buffer(dcm.buffer_index.slice(..), wgpu::IndexFormat::Uint16);
250        for (range, instance) in dcm
251            .vertex_vector
252            .iter_offsets()
253            .zip(dcm.decal_instances.drain(..))
254        {
255            let Some(texture) = dcm.decal_textures.get(&instance.id) else {
256                    eprintln!("You tried to use a non-valid decal");
257                    continue
258            };
259
260            // Update buffers
261
262            self.set_bind_group(0, &texture.1, &[]);
263            self.set_vertex_buffer(0, buffer.slice(range));
264            self.draw_indexed(0..(crate::INDICES.len() as u32), 0, 0..1);
265        }
266        dcm.cpu_vertex_vector.clear();
267        dcm.decal_instances.push(DecalInstances {
268            id: 0,
269            pos: {
270                use crate::CORNER;
271                [
272                    (-CORNER, CORNER),
273                    (-CORNER, -CORNER),
274                    (CORNER, -CORNER),
275                    (CORNER, CORNER),
276                ]
277            },
278            uv: [(0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0)],
279            w: [1.0; 4],
280            tint: [1.0; 4],
281        });
282        queue.submit(std::iter::once(command));
283    }
284}