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 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 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}