wgpu_shapes/
shape_renderer.rs

1use std::collections::hash_map::Entry;
2use std::collections::{BTreeMap, HashMap};
3use std::f32::consts::PI;
4
5use image::{DynamicImage, GenericImage, GenericImageView, ImageBuffer};
6use rectangle_pack::{
7    contains_smallest_box, pack_rects, volume_heuristic, GroupedRectsToPlace, RectToInsert,
8    TargetBin,
9};
10use wgpu::util::DeviceExt;
11use wgpu::{
12    BindGroup, BindGroupLayout, Color, CommandEncoder, Device, Queue, RenderPipeline,
13    SamplerDescriptor, SurfaceConfiguration, TextureView,
14};
15use wgpu_noboiler::buffer::{BufferCreator, SimpleBuffer};
16use wgpu_noboiler::render_pass::RenderPassCreator;
17use wgpu_noboiler::render_pipeline::RenderPipelineCreator;
18use wgpu_noboiler::vertex::Vertex;
19
20use crate::render::depth_buffer::DepthBuffer;
21use crate::render::instance::{Instance, TextureInstance};
22use crate::render::vertex::Vertex as OwnVertex;
23use crate::shape::image::Image;
24use crate::shape::oval::Oval;
25use crate::shape::rect::Rect;
26use crate::shape::shapes::BasicShape;
27
28/// helps to draw basic [BasicShapes](BasicShape)
29pub struct ShapeRenderer {
30    shape_render_pipeline: RenderPipeline,
31    texture_render_pipeline: RenderPipeline,
32
33    recs: Vec<Rect>,
34    ovals: Vec<Oval>,
35    images: Vec<Image>,
36
37    frame_group_layout: BindGroupLayout,
38    frame_size: (f32, f32),
39    frame_offset: (f32, f32),
40
41    background_color: Color,
42
43    depth_texture: DepthBuffer,
44
45    texture_group_layout: BindGroupLayout,
46    texture: Option<TextureView>,
47    texture_size: u32,
48    textures: Vec<DynamicImage>,
49    textures_cords: Vec<((f32, f32), (f32, f32))>,
50
51    rect_vertex_buffer: SimpleBuffer,
52    rect_indices_buffer: SimpleBuffer,
53}
54
55impl ShapeRenderer {
56    /// creates a new [ShapeRenderer] which can render [BasicShape].
57    /// these can be created with [ShapeRenderer::rect], [ShapeRenderer::oval]
58    ///
59    /// can be reused with [ShapeRenderer::clear] function
60    pub fn new(device: &Device, config: &SurfaceConfiguration) -> ShapeRenderer {
61        let frame_size_group_layout =
62            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
63                entries: &[
64                    wgpu::BindGroupLayoutEntry {
65                        binding: 0,
66                        visibility: wgpu::ShaderStages::VERTEX,
67                        ty: wgpu::BindingType::Buffer {
68                            ty: wgpu::BufferBindingType::Uniform,
69                            has_dynamic_offset: false,
70                            min_binding_size: None,
71                        },
72                        count: None,
73                    },
74                    wgpu::BindGroupLayoutEntry {
75                        binding: 1,
76                        visibility: wgpu::ShaderStages::VERTEX,
77                        ty: wgpu::BindingType::Buffer {
78                            ty: wgpu::BufferBindingType::Uniform,
79                            has_dynamic_offset: false,
80                            min_binding_size: None,
81                        },
82                        count: None,
83                    },
84                ],
85                label: Some("Frame Bind group"),
86            });
87
88        let texture_bind_group_layout =
89            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
90                entries: &[
91                    wgpu::BindGroupLayoutEntry {
92                        binding: 0,
93                        visibility: wgpu::ShaderStages::FRAGMENT,
94                        ty: wgpu::BindingType::Texture {
95                            multisampled: false,
96                            view_dimension: wgpu::TextureViewDimension::D2,
97                            sample_type: wgpu::TextureSampleType::Float { filterable: true },
98                        },
99                        count: None,
100                    },
101                    wgpu::BindGroupLayoutEntry {
102                        binding: 1,
103                        visibility: wgpu::ShaderStages::FRAGMENT,
104                        // This should match the filterable field of the
105                        // corresponding Texture entry above.
106                        ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
107                        count: None,
108                    },
109                ],
110                label: Some("texture_bind_group_layout"),
111            });
112
113        let shape_render_pipeline = RenderPipelineCreator::from_shader_code(
114            include_str!("../resources/shape_shader.wgsl"),
115            device,
116            config,
117        )
118        .add_bind_group(&frame_size_group_layout)
119        .add_vertex_buffer(OwnVertex::descriptor())
120        .add_vertex_buffer(Instance::descriptor())
121        .depth_stencil(wgpu::DepthStencilState {
122            format: DepthBuffer::DEPTH_FORMAT,
123            depth_write_enabled: true,
124            depth_compare: wgpu::CompareFunction::Less,
125            stencil: wgpu::StencilState::default(),
126            bias: wgpu::DepthBiasState::default(),
127        })
128        .build();
129
130        let texture_render_pipeline = RenderPipelineCreator::from_shader_code(
131            include_str!("../resources/texture_shader.wgsl"),
132            device,
133            config,
134        )
135        .add_bind_group(&frame_size_group_layout)
136        .add_bind_group(&texture_bind_group_layout)
137        .add_vertex_buffer(OwnVertex::descriptor())
138        .add_vertex_buffer(TextureInstance::descriptor())
139        .depth_stencil(wgpu::DepthStencilState {
140            format: DepthBuffer::DEPTH_FORMAT,
141            depth_write_enabled: true,
142            depth_compare: wgpu::CompareFunction::Less,
143            stencil: wgpu::StencilState::default(),
144            bias: wgpu::DepthBiasState::default(),
145        })
146        .build();
147
148        let rect_vertex_buffer = BufferCreator::vertex(device)
149            .label("Rect VertexBuffer")
150            .data(vec![
151                OwnVertex {
152                    position: [1.0, 1.0],
153                },
154                OwnVertex {
155                    position: [-1.0, 1.0],
156                },
157                OwnVertex {
158                    position: [-1.0, -1.0],
159                },
160                OwnVertex {
161                    position: [1.0, -1.0],
162                },
163            ])
164            .build();
165
166        let rect_indices_buffer = BufferCreator::indices(device)
167            .label("Rect IndicesBuffer")
168            .data(vec![0, 1, 2, 0, 2, 3])
169            .build();
170
171        ShapeRenderer {
172            shape_render_pipeline,
173            texture_render_pipeline,
174
175            recs: vec![],
176            ovals: vec![],
177            images: vec![],
178
179            frame_group_layout: frame_size_group_layout,
180            frame_size: (800.0, 600.0),
181            frame_offset: (0.0, 0.0),
182
183            background_color: Color::WHITE,
184
185            depth_texture: DepthBuffer::create_depth_texture(device, config, "depth_texture"),
186
187            texture_group_layout: texture_bind_group_layout,
188            texture: None,
189            texture_size: 512,
190            textures: vec![],
191            textures_cords: vec![],
192            rect_vertex_buffer,
193            rect_indices_buffer,
194        }
195    }
196
197    fn frame_bind_group(&self, device: &Device) -> BindGroup {
198        let frame_size_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
199            label: Some("Frame size Buffer"),
200            contents: bytemuck::cast_slice(&[self.frame_size.0, self.frame_size.1]),
201            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
202        });
203
204        let frame_offset_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
205            label: Some("Frame offset Buffer"),
206            contents: bytemuck::cast_slice(&[self.frame_offset.0, self.frame_offset.1]),
207            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
208        });
209
210        device.create_bind_group(&wgpu::BindGroupDescriptor {
211            layout: &self.frame_group_layout,
212            entries: &[
213                wgpu::BindGroupEntry {
214                    binding: 0,
215                    resource: frame_size_buffer.as_entire_binding(),
216                },
217                wgpu::BindGroupEntry {
218                    binding: 1,
219                    resource: frame_offset_buffer.as_entire_binding(),
220                },
221            ],
222            label: Some("frame_size_bind_group"),
223        })
224    }
225
226    fn texture_bind_group(&self, device: &Device) -> Option<BindGroup> {
227        self.texture.as_ref()?;
228
229        Some(device.create_bind_group(&wgpu::BindGroupDescriptor {
230            layout: &self.texture_group_layout,
231            entries: &[
232                wgpu::BindGroupEntry {
233                    binding: 0,
234                    resource: wgpu::BindingResource::TextureView(self.texture.as_ref().unwrap()),
235                },
236                wgpu::BindGroupEntry {
237                    binding: 1,
238                    resource: wgpu::BindingResource::Sampler(
239                        &device.create_sampler(&SamplerDescriptor::default()),
240                    ),
241                },
242            ],
243            label: Some("diffuse_bind_group"),
244        }))
245    }
246
247    /// renders the current [BasicShapes](BasicShape) which can be added with [ShapeRenderer::rect], [ShapeRenderer::oval], ...
248    pub fn render<'a, 'b: 'a>(
249        &'b self,
250        encoder: &mut CommandEncoder,
251        texture_view: &TextureView,
252        device: &Device,
253    ) {
254        let rect_instance_buffer = self.generate_rect_buffer(device);
255        let oval_buffers = self.generate_oval_buffer(device);
256        let image_instance_buffer = self.generate_image_buffer(device);
257
258        let frame_bind_group = self.frame_bind_group(device);
259        let texture_bind_group = self.texture_bind_group(device);
260
261        let mut render_pass = RenderPassCreator::new(texture_view)
262            .depth_stencil_attachment(wgpu::RenderPassDepthStencilAttachment {
263                view: &self.depth_texture.view,
264                depth_ops: Some(wgpu::Operations {
265                    load: wgpu::LoadOp::Clear(1.0),
266                    store: true,
267                }),
268                stencil_ops: None,
269            })
270            .clear_color(self.background_color)
271            .build(encoder);
272
273        render_pass.set_pipeline(&self.shape_render_pipeline);
274        render_pass.set_bind_group(0, &frame_bind_group, &[]);
275
276        //rects
277        render_pass.set_vertex_buffer(0, self.rect_vertex_buffer.slice());
278        render_pass.set_index_buffer(self.rect_indices_buffer.slice(), wgpu::IndexFormat::Uint32);
279
280        render_pass.set_vertex_buffer(1, rect_instance_buffer.slice());
281
282        render_pass.draw_indexed(
283            0..self.rect_indices_buffer.size(),
284            0,
285            0..rect_instance_buffer.size(),
286        );
287
288        //ovals
289
290        for InstanceBufferGroup(oval_vertex_buffer, oval_indices_buffer, oval_instance_buffer) in
291            &oval_buffers
292        {
293            render_pass.set_vertex_buffer(0, oval_vertex_buffer.slice());
294            render_pass.set_index_buffer(oval_indices_buffer.slice(), wgpu::IndexFormat::Uint32);
295
296            render_pass.set_vertex_buffer(1, oval_instance_buffer.slice());
297
298            render_pass.draw_indexed(
299                0..oval_indices_buffer.size(),
300                0,
301                0..oval_instance_buffer.size(),
302            );
303        }
304
305        //texture
306
307        if texture_bind_group.is_none() {
308            return;
309        }
310
311        render_pass.set_pipeline(&self.texture_render_pipeline);
312        render_pass.set_bind_group(0, &frame_bind_group, &[]);
313        render_pass.set_bind_group(1, texture_bind_group.as_ref().unwrap(), &[]);
314
315        render_pass.set_vertex_buffer(0, self.rect_vertex_buffer.slice());
316        render_pass.set_index_buffer(self.rect_indices_buffer.slice(), wgpu::IndexFormat::Uint32);
317
318        render_pass.set_vertex_buffer(1, image_instance_buffer.slice());
319
320        render_pass.draw_indexed(
321            0..self.rect_indices_buffer.size(),
322            0,
323            0..image_instance_buffer.size(),
324        );
325    }
326
327    /// clears the current drawn [BasicShapes](BasicShape) which can be added with [ShapeRenderer::rect], [ShapeRenderer::oval], ...
328    pub fn clear(&mut self) {
329        self.recs.clear();
330        self.ovals.clear();
331        self.images.clear();
332    }
333
334    /// sets the current [frame_size](ShapeRenderer::frame_size)
335    pub fn set_frame_size(&mut self, frame_size: (f32, f32)) -> &mut Self {
336        self.frame_size = frame_size;
337        self
338    }
339
340    /// frame_size.0 is the with in which [BasicShape] get displayed.
341    ///
342    /// frame_size.1 is the height in which [BasicShape] get displayed
343    ///
344    /// (0,0) -> center of screen
345    pub fn frame_size(&self) -> (f32, f32) {
346        self.frame_size
347    }
348
349    /// sets the current [frame_offset](ShapeRenderer::frame_offset)
350    pub fn set_frame_offset(&mut self, frame_offset: (f32, f32)) -> &mut Self {
351        self.frame_offset = frame_offset;
352        self
353    }
354
355    /// frame_offset are values which get added to all [BasicShape]
356    pub fn frame_offset(&self) -> (f32, f32) {
357        self.frame_offset
358    }
359
360    /// resizes the depthBuffer should be called on every window resize
361    pub fn resize(&mut self, device: &Device, config: &SurfaceConfiguration) -> &mut Self {
362        self.depth_texture = DepthBuffer::create_depth_texture(device, config, "depth_texture");
363        self
364    }
365
366    /// sets the clearColor/ backgroundColor
367    pub fn background_color(&mut self, background_color: Color) -> &mut Self {
368        self.background_color = background_color;
369        self
370    }
371
372    /// renders [Rect] and returns a Ref to it
373    pub fn rect(&mut self) -> &mut Rect {
374        self.recs.push(Rect::default());
375        self.recs.last_mut().unwrap()
376    }
377
378    fn generate_rect_buffer(&self, device: &Device) -> SimpleBuffer {
379        let instances: Vec<_> = self.recs.iter().map(|rect| rect.to_instance()).collect();
380
381        let instances_buffer = BufferCreator::vertex(device)
382            .label("Rect InstanceBuffer")
383            .data(instances)
384            .build();
385
386        instances_buffer
387    }
388
389    /// renders [Oval] and returns a Ref to it
390    pub fn oval(&mut self) -> &mut Oval {
391        self.ovals.push(Oval::default());
392        self.ovals.last_mut().unwrap()
393    }
394
395    fn generate_oval_buffer(&self, device: &Device) -> Vec<InstanceBufferGroup> {
396        let mut ovals = HashMap::<u32, Vec<&Oval>>::new();
397
398        for oval in &self.ovals {
399            if let Entry::Vacant(e) = ovals.entry(oval.detail) {
400                e.insert(vec![oval]);
401            } else {
402                ovals.get_mut(&oval.detail).unwrap().push(oval);
403            }
404        }
405
406        let mut instance_buffer_groups = vec![];
407
408        for (detail, ovals) in ovals {
409            let vertices: Vec<_> = (0..detail)
410                .map(|i| {
411                    let angle = PI * 2.0 / detail as f32 * i as f32;
412
413                    OwnVertex {
414                        position: [angle.cos(), angle.sin()],
415                    }
416                })
417                .collect();
418
419            let vertex_buffer = BufferCreator::vertex(device)
420                .label("Rect VertexBuffer")
421                .data(vertices)
422                .build();
423
424            let indices: Vec<_> = (0..(detail as i32 - 2))
425                .flat_map(|i| [0, i + 1, i + 2])
426                .collect();
427
428            let indices_buffer = BufferCreator::indices(device)
429                .label("Rect IndicesBuffer")
430                .data(indices)
431                .build();
432
433            let instances: Vec<_> = ovals.iter().map(|oval| oval.to_instance()).collect();
434
435            let instances_buffer = BufferCreator::vertex(device)
436                .label("Rect InstanceBuffer")
437                .data(instances)
438                .build();
439
440            instance_buffer_groups.push(InstanceBufferGroup(
441                vertex_buffer,
442                indices_buffer,
443                instances_buffer,
444            ));
445        }
446
447        instance_buffer_groups
448    }
449
450    /// renders [Image] and returns a Ref to it
451    pub fn image(&mut self, texture_index: usize) -> &mut Image {
452        let mut image = Image::default();
453        match self.textures_cords.get(texture_index) {
454            None => {
455                println!("No texture with the id: {} could be found", texture_index);
456            }
457            Some(cords) => {
458                image.texture_pos = cords.0;
459                image.texture_scale = cords.1;
460            }
461        };
462
463        self.images.push(image);
464        self.images.last_mut().unwrap()
465    }
466
467    fn generate_image_buffer(&self, device: &Device) -> SimpleBuffer {
468        let instances: Vec<_> = self
469            .images
470            .iter()
471            .map(|texture| texture.to_instance())
472            .collect();
473
474        let instances_buffer = BufferCreator::vertex(device)
475            .label("Rect InstanceBuffer")
476            .data(instances)
477            .build();
478
479        instances_buffer
480    }
481
482    pub fn add_texture_from_bytes(
483        &mut self,
484        bytes: &[u8],
485        device: &Device,
486        queue: &Queue,
487    ) -> &mut Self {
488        self.textures.push(image::load_from_memory(bytes).unwrap());
489
490        self.upload_textures(device, queue);
491
492        self
493    }
494
495    /// adds a texture to the usable texturePool
496    pub fn add_textures_from_bytes(
497        &mut self,
498        bytes: &Vec<&[u8]>,
499        device: &Device,
500        queue: &Queue,
501    ) -> &mut Self {
502        for bytes in bytes {
503            self.textures.push(image::load_from_memory(bytes).unwrap());
504        }
505
506        self.upload_textures(device, queue);
507
508        self
509    }
510
511    /// adds multiple textures to the usable texturePool
512    fn upload_textures(&mut self, device: &Device, queue: &Queue) {
513        let mut rects_to_place: GroupedRectsToPlace<usize, usize> = GroupedRectsToPlace::new();
514
515        for (index, image) in self.textures.iter().enumerate() {
516            let dimensions = image.dimensions();
517
518            rects_to_place.push_rect(
519                index,
520                None,
521                RectToInsert::new(dimensions.0, dimensions.1, 1),
522            );
523        }
524
525        let mut target_bins = BTreeMap::new();
526        target_bins.insert(0, TargetBin::new(self.texture_size, self.texture_size, 1));
527
528        let rectangle_placements = pack_rects(
529            &rects_to_place,
530            &mut target_bins,
531            &volume_heuristic,
532            &contains_smallest_box,
533        );
534
535        let rectangle_placements = match rectangle_placements {
536            Ok(rectangle_pack) => rectangle_pack,
537            Err(_) => {
538                self.texture_size *= 2;
539                self.upload_textures(device, queue);
540
541                return;
542            }
543        };
544
545        self.textures_cords.clear();
546        let mut buffer = ImageBuffer::new(self.texture_size, self.texture_size);
547
548        for (index, (_, location)) in rectangle_placements.packed_locations() {
549            buffer
550                .copy_from(
551                    self.textures.get(*index).unwrap(),
552                    location.x(),
553                    location.y(),
554                )
555                .expect("TODO: panic message");
556
557            self.textures_cords.push((
558                (
559                    location.x() as f32 / self.texture_size as f32,
560                    location.y() as f32 / self.texture_size as f32,
561                ),
562                (
563                    location.width() as f32 / self.texture_size as f32,
564                    location.height() as f32 / self.texture_size as f32,
565                ),
566            ))
567        }
568
569        let dimensions = buffer.dimensions();
570
571        let texture_size = wgpu::Extent3d {
572            width: dimensions.0,
573            height: dimensions.1,
574            depth_or_array_layers: 1,
575        };
576
577        let diffuse_texture = device.create_texture(&wgpu::TextureDescriptor {
578            // All textures are stored as 3D, we represent our 2D texture
579            // by setting depth to 1.
580            size: texture_size,
581            mip_level_count: 1, // We'll talk about this a little later
582            sample_count: 1,
583            dimension: wgpu::TextureDimension::D2,
584            // Most images are stored using sRGB so we need to reflect that here.
585            format: wgpu::TextureFormat::Rgba8Unorm,
586            // TEXTURE_BINDING tells wgpu that we want to use this texture in shaders
587            // COPY_DST means that we want to copy data to this texture
588            usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
589            label: Some("diffuse_texture"),
590            view_formats: &[],
591        });
592
593        queue.write_texture(
594            // Tells wgpu where to copy the pixel data
595            wgpu::ImageCopyTexture {
596                texture: &diffuse_texture,
597                mip_level: 0,
598                origin: wgpu::Origin3d::ZERO,
599                aspect: wgpu::TextureAspect::All,
600            },
601            // The actual pixel data
602            &buffer,
603            // The layout of the texture
604            wgpu::ImageDataLayout {
605                offset: 0,
606                bytes_per_row: std::num::NonZeroU32::new(4 * dimensions.0),
607                rows_per_image: std::num::NonZeroU32::new(dimensions.1),
608            },
609            texture_size,
610        );
611
612        let diffuse_texture_view =
613            diffuse_texture.create_view(&wgpu::TextureViewDescriptor::default());
614
615        self.texture = Some(diffuse_texture_view);
616    }
617}
618
619struct InstanceBufferGroup(SimpleBuffer, SimpleBuffer, SimpleBuffer);