Skip to main content

hewn/wgpu/
render.rs

1use crate::ecs::Entity;
2use crate::ecs::EntityId;
3use crate::wgpu::texture;
4use cgmath::prelude::*;
5use cgmath::SquareMatrix;
6use std::f32::consts::PI;
7use std::mem;
8use std::{iter, sync::Arc};
9#[cfg(target_arch = "wasm32")]
10use wasm_bindgen::prelude::*;
11use wgpu::util::DeviceExt;
12use winit::{event_loop::ActiveEventLoop, keyboard::KeyCode, window::Window};
13
14#[derive(Default, Copy, Clone)]
15pub enum CameraStrategy {
16    #[default]
17    AllEntities,
18    CameraFollow(EntityId),
19}
20
21#[repr(C)]
22#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
23pub struct Vertex {
24    position: [f32; 3],
25}
26
27fn gen_shape_buffer(points: u16, rotation_deg: f32, size: f32) -> (Vec<Vertex>, Vec<u16>) {
28    if points < 3 {
29        return (
30            vec![
31                Vertex {
32                    position: [-0.0868241, 0.49240386, 0.0],
33                },
34                Vertex {
35                    position: [-0.49513406, 0.06958647, 0.0],
36                },
37                Vertex {
38                    position: [-0.21918549, -0.44939706, 0.0],
39                },
40                Vertex {
41                    position: [0.35966998, -0.3473291, 0.0],
42                },
43                Vertex {
44                    position: [0.44147372, 0.2347359, 0.0],
45                },
46            ],
47            vec![0, 1, 4, 1, 2, 4, 2, 3, 4],
48        );
49    }
50
51    let mut vertices = Vec::with_capacity(points as usize);
52    let rotation_rad = rotation_deg.to_radians();
53    for i in 0..points {
54        let theta = 2.0 * PI * (i as f32) / (points as f32) + rotation_rad;
55        let x = size * theta.cos();
56        let y = size * theta.sin();
57        vertices.push(Vertex {
58            position: [x, y, 0.0],
59        });
60    }
61
62    let mut indices = Vec::with_capacity((points as usize - 2) * 3);
63    for i in 1..(points - 1) {
64        indices.push(0u16);
65        indices.push(i);
66        indices.push(i + 1);
67    }
68
69    (vertices, indices)
70}
71
72impl Vertex {
73    fn desc() -> wgpu::VertexBufferLayout<'static> {
74        wgpu::VertexBufferLayout {
75            array_stride: std::mem::size_of::<Vertex>() as wgpu::BufferAddress,
76            step_mode: wgpu::VertexStepMode::Vertex,
77            attributes: &[wgpu::VertexAttribute {
78                offset: 0,
79                shader_location: 0,
80                format: wgpu::VertexFormat::Float32x3,
81            }],
82        }
83    }
84}
85
86#[rustfmt::skip]
87pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::from_cols(
88    cgmath::Vector4::new(1.0, 0.0, 0.0, 0.0),
89    cgmath::Vector4::new(0.0, 1.0, 0.0, 0.0),
90    cgmath::Vector4::new(0.0, 0.0, 0.5, 0.0),
91    cgmath::Vector4::new(0.0, 0.0, 0.5, 1.0),
92);
93
94pub(crate) struct Camera {
95    pub(crate) eye: cgmath::Point3<f32>,
96    pub(crate) target: cgmath::Point3<f32>,
97    pub(crate) up: cgmath::Vector3<f32>,
98    pub(crate) aspect: f32,
99    pub(crate) fovy: f32,
100    pub(crate) znear: f32,
101    pub(crate) zfar: f32,
102}
103
104impl Camera {
105    pub(crate) fn build_view_projection_matrix(&self) -> cgmath::Matrix4<f32> {
106        let view = cgmath::Matrix4::look_at_rh(self.eye, self.target, self.up);
107        let proj = cgmath::perspective(cgmath::Deg(self.fovy), self.aspect, self.znear, self.zfar);
108        proj * view
109    }
110}
111
112#[repr(C)]
113#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
114pub(crate) struct CameraUniform {
115    pub(crate) view_proj: [[f32; 4]; 4],
116}
117
118impl CameraUniform {
119    pub(crate) fn new() -> Self {
120        Self {
121            view_proj: cgmath::Matrix4::identity().into(),
122        }
123    }
124
125    pub(crate) fn update_view_proj(&mut self, camera: &Camera) {
126        self.view_proj = (OPENGL_TO_WGPU_MATRIX * camera.build_view_projection_matrix()).into();
127    }
128}
129
130pub(crate) struct InstancePosition {
131    pub(crate) position: cgmath::Vector3<f32>,
132    pub(crate) rotation: cgmath::Quaternion<f32>,
133}
134
135#[repr(C)]
136#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
137pub(crate) struct InstancePositionRaw {
138    pub(crate) model: [[f32; 4]; 4],
139}
140
141impl InstancePositionRaw {
142    pub(crate) fn desc() -> wgpu::VertexBufferLayout<'static> {
143        wgpu::VertexBufferLayout {
144            array_stride: mem::size_of::<InstancePositionRaw>() as wgpu::BufferAddress,
145            // We need to switch from using a step mode of Vertex to Instance
146            // This means that our shaders will only change to use the next
147            // instance when the shader starts processing a new instance
148            step_mode: wgpu::VertexStepMode::Instance,
149            attributes: &[
150                // A mat4 takes up 4 vertex slots as it is technically 4 vec4s. We need to define a slot
151                // for each vec4. We'll have to reassemble the mat4 in the shader.
152                wgpu::VertexAttribute {
153                    offset: 0,
154                    // While our vertex shader only uses locations 0, and 1 now, in later tutorials, we'll
155                    // be using 2, 3, and 4, for Vertex. We'll start at slot 5, not conflict with them later
156                    shader_location: 5,
157                    format: wgpu::VertexFormat::Float32x4,
158                },
159                wgpu::VertexAttribute {
160                    offset: mem::size_of::<[f32; 4]>() as wgpu::BufferAddress,
161                    shader_location: 6,
162                    format: wgpu::VertexFormat::Float32x4,
163                },
164                wgpu::VertexAttribute {
165                    offset: mem::size_of::<[f32; 8]>() as wgpu::BufferAddress,
166                    shader_location: 7,
167                    format: wgpu::VertexFormat::Float32x4,
168                },
169                wgpu::VertexAttribute {
170                    offset: mem::size_of::<[f32; 12]>() as wgpu::BufferAddress,
171                    shader_location: 8,
172                    format: wgpu::VertexFormat::Float32x4,
173                },
174            ],
175        }
176    }
177}
178
179impl InstancePosition {
180    pub(crate) fn to_raw(&self) -> InstancePositionRaw {
181        InstancePositionRaw {
182            model: (cgmath::Matrix4::from_translation(self.position)
183                * cgmath::Matrix4::from(self.rotation))
184            .into(),
185        }
186    }
187}
188
189pub(crate) struct InstanceColor {
190    pub(crate) color: cgmath::Vector3<f32>,
191}
192
193#[repr(C)]
194#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
195pub(crate) struct InstanceColorRaw {
196    pub(crate) model: [f32; 3],
197}
198
199impl InstanceColorRaw {
200    pub(crate) fn desc() -> wgpu::VertexBufferLayout<'static> {
201        wgpu::VertexBufferLayout {
202            array_stride: mem::size_of::<InstanceColorRaw>() as wgpu::BufferAddress,
203            step_mode: wgpu::VertexStepMode::Instance,
204            attributes: &[wgpu::VertexAttribute {
205                offset: 0,
206                shader_location: 9,
207                format: wgpu::VertexFormat::Float32x3,
208            }],
209        }
210    }
211}
212
213impl InstanceColor {
214    pub(crate) fn to_raw(&self) -> InstanceColorRaw {
215        InstanceColorRaw {
216            model: self.color.into(),
217        }
218    }
219}
220
221pub struct State {
222    surface: wgpu::Surface<'static>,
223    device: wgpu::Device,
224    queue: wgpu::Queue,
225    config: wgpu::SurfaceConfiguration,
226    is_surface_configured: bool,
227    render_pipeline: wgpu::RenderPipeline,
228    vertex_buffer: wgpu::Buffer,
229    index_buffer: wgpu::Buffer,
230    num_indices: u32,
231    #[allow(dead_code)]
232    diffuse_texture: texture::Texture,
233    diffuse_bind_group: wgpu::BindGroup,
234    instance_positions: Vec<InstancePosition>,
235    instance_colors: Vec<InstanceColor>,
236    instance_positions_buffer: wgpu::Buffer,
237    instance_colors_buffer: wgpu::Buffer,
238    camera_strategy: CameraStrategy,
239
240    vertices: Vec<Vertex>,
241    indices: Vec<u16>,
242
243    camera: Camera,
244    camera_uniform: CameraUniform,
245    camera_buffer: wgpu::Buffer,
246    camera_bind_group: wgpu::BindGroup,
247    pub(crate) window: Arc<Window>,
248    renderable_entities: Vec<Entity>,
249}
250
251impl State {
252    pub(crate) async fn new(
253        window: Arc<Window>,
254        renderable_entities: Vec<Entity>,
255        camera_strategy: CameraStrategy,
256    ) -> anyhow::Result<State> {
257        let size = window.inner_size();
258
259        // The instance is a handle to our GPU
260        // BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
261        let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
262            #[cfg(not(target_arch = "wasm32"))]
263            backends: wgpu::Backends::PRIMARY,
264            #[cfg(target_arch = "wasm32")]
265            backends: wgpu::Backends::GL,
266            ..Default::default()
267        });
268
269        let surface = instance.create_surface(window.clone()).unwrap();
270
271        let adapter = instance
272            .request_adapter(&wgpu::RequestAdapterOptions {
273                power_preference: wgpu::PowerPreference::default(),
274                compatible_surface: Some(&surface),
275                force_fallback_adapter: false,
276            })
277            .await
278            .unwrap();
279        let (device, queue) = adapter
280            .request_device(&wgpu::DeviceDescriptor {
281                label: None,
282                required_features: wgpu::Features::empty(),
283                // WebGL doesn't support all of wgpu's features, so if
284                // we're building for the web we'll have to disable some.
285                required_limits: if cfg!(target_arch = "wasm32") {
286                    wgpu::Limits::downlevel_webgl2_defaults()
287                } else {
288                    wgpu::Limits::default()
289                },
290                memory_hints: Default::default(),
291                trace: wgpu::Trace::Off,
292            })
293            .await
294            .unwrap();
295
296        let surface_caps = surface.get_capabilities(&adapter);
297        // Shader code in this tutorial assumes an Srgb surface texture. Using a different
298        // one will result all the colors comming out darker. If you want to support non
299        // Srgb surfaces, you'll need to account for that when drawing to the frame.
300        let surface_format = surface_caps
301            .formats
302            .iter()
303            .copied()
304            .find(|f| f.is_srgb())
305            .unwrap_or(surface_caps.formats[0]);
306        let config = wgpu::SurfaceConfiguration {
307            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
308            format: surface_format,
309            width: size.width,
310            height: size.height,
311            present_mode: surface_caps.present_modes[0],
312            alpha_mode: surface_caps.alpha_modes[0],
313            view_formats: vec![],
314            desired_maximum_frame_latency: 2,
315        };
316
317        let diffuse_bytes = include_bytes!("happy-tree.png");
318        let diffuse_texture =
319            texture::Texture::from_bytes(&device, &queue, diffuse_bytes, "happy-tree.png").unwrap();
320
321        let texture_bind_group_layout =
322            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
323                entries: &[
324                    wgpu::BindGroupLayoutEntry {
325                        binding: 0,
326                        visibility: wgpu::ShaderStages::FRAGMENT,
327                        ty: wgpu::BindingType::Texture {
328                            multisampled: false,
329                            view_dimension: wgpu::TextureViewDimension::D2,
330                            sample_type: wgpu::TextureSampleType::Float { filterable: true },
331                        },
332                        count: None,
333                    },
334                    wgpu::BindGroupLayoutEntry {
335                        binding: 1,
336                        visibility: wgpu::ShaderStages::FRAGMENT,
337                        ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
338                        count: None,
339                    },
340                ],
341                label: Some("texture_bind_group_layout"),
342            });
343
344        let diffuse_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
345            layout: &texture_bind_group_layout,
346            entries: &[
347                wgpu::BindGroupEntry {
348                    binding: 0,
349                    resource: wgpu::BindingResource::TextureView(&diffuse_texture.view),
350                },
351                wgpu::BindGroupEntry {
352                    binding: 1,
353                    resource: wgpu::BindingResource::Sampler(&diffuse_texture.sampler),
354                },
355            ],
356            label: Some("diffuse_bind_group"),
357        });
358
359        let camera = Camera {
360            eye: (0.0, 1.0, 10.0).into(),
361            target: (0.0, 0.0, 0.0).into(),
362            up: cgmath::Vector3::unit_y(),
363            aspect: config.width as f32 / config.height as f32,
364            fovy: 45.0,
365            znear: 0.1,
366            zfar: 100.0,
367        };
368
369        let mut camera_uniform = CameraUniform::new();
370        camera_uniform.update_view_proj(&camera);
371
372        let camera_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
373            label: Some("Camera Buffer"),
374            contents: bytemuck::cast_slice(&[camera_uniform]),
375            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
376        });
377
378        let camera_bind_group_layout =
379            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
380                entries: &[wgpu::BindGroupLayoutEntry {
381                    binding: 0,
382                    visibility: wgpu::ShaderStages::VERTEX,
383                    ty: wgpu::BindingType::Buffer {
384                        ty: wgpu::BufferBindingType::Uniform,
385                        has_dynamic_offset: false,
386                        min_binding_size: None,
387                    },
388                    count: None,
389                }],
390                label: Some("camera_bind_group_layout"),
391            });
392
393        let camera_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
394            layout: &camera_bind_group_layout,
395            entries: &[wgpu::BindGroupEntry {
396                binding: 0,
397                resource: camera_buffer.as_entire_binding(),
398            }],
399            label: Some("camera_bind_group"),
400        });
401
402        let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
403            label: Some("Shader"),
404            source: wgpu::ShaderSource::Wgsl(include_str!("shader.wgsl").into()),
405        });
406
407        let render_pipeline_layout =
408            device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
409                label: Some("Render Pipeline Layout"),
410                bind_group_layouts: &[&texture_bind_group_layout, &camera_bind_group_layout],
411                push_constant_ranges: &[],
412            });
413
414        let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
415            label: Some("Render Pipeline"),
416            layout: Some(&render_pipeline_layout),
417            vertex: wgpu::VertexState {
418                module: &shader,
419                entry_point: Some("vs_main"),
420                buffers: &[
421                    Vertex::desc(),
422                    InstancePositionRaw::desc(),
423                    InstanceColorRaw::desc(),
424                ],
425                compilation_options: Default::default(),
426            },
427            fragment: Some(wgpu::FragmentState {
428                module: &shader,
429                entry_point: Some("fs_main"),
430                targets: &[Some(wgpu::ColorTargetState {
431                    format: config.format,
432                    blend: Some(wgpu::BlendState {
433                        color: wgpu::BlendComponent::REPLACE,
434                        alpha: wgpu::BlendComponent::REPLACE,
435                    }),
436                    write_mask: wgpu::ColorWrites::ALL,
437                })],
438                compilation_options: Default::default(),
439            }),
440            primitive: wgpu::PrimitiveState {
441                topology: wgpu::PrimitiveTopology::TriangleList,
442                strip_index_format: None,
443                front_face: wgpu::FrontFace::Ccw,
444                cull_mode: Some(wgpu::Face::Back),
445                // Setting this to anything other than Fill requires Features::POLYGON_MODE_LINE
446                // or Features::POLYGON_MODE_POINT
447                polygon_mode: wgpu::PolygonMode::Fill,
448                // Requires Features::DEPTH_CLIP_CONTROL
449                unclipped_depth: false,
450                // Requires Features::CONSERVATIVE_RASTERIZATION
451                conservative: false,
452            },
453            depth_stencil: None,
454            multisample: wgpu::MultisampleState {
455                count: 1,
456                mask: !0,
457                alpha_to_coverage_enabled: false,
458            },
459            // If the pipeline will be used with a multiview render pass, this
460            // indicates how many array layers the attachments will have.
461            multiview: None,
462            // Useful for optimizing shader compilation on Android
463            cache: None,
464        });
465
466        let (vertices, indices) = gen_shape_buffer(4, 45.0, 0.08);
467
468        let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
469            label: Some("Vertex Buffer"),
470            contents: bytemuck::cast_slice(&vertices),
471            usage: wgpu::BufferUsages::VERTEX,
472        });
473        let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
474            label: Some("Index Buffer"),
475            contents: bytemuck::cast_slice(&indices),
476            usage: wgpu::BufferUsages::INDEX,
477        });
478        let num_indices = indices.len() as u32;
479
480        let (instance_positions, instance_colors): (Vec<InstancePosition>, Vec<InstanceColor>) =
481            renderable_entities
482                .iter()
483                .map(|e| {
484                    let position = e.components.position.unwrap();
485                    let rotation = cgmath::Quaternion::from_axis_angle(
486                        cgmath::Vector3::unit_z(),
487                        cgmath::Deg(0.0),
488                    );
489                    let color = e.components.render.unwrap().rgb;
490
491                    let position_3d = cgmath::Vector3 {
492                        x: position.x as f32 * 0.1,
493                        y: position.y as f32 * 0.1,
494                        z: 0.0,
495                    };
496                    (
497                        InstancePosition {
498                            position: position_3d,
499                            rotation,
500                        },
501                        InstanceColor { color },
502                    )
503                })
504                .unzip();
505        // .collect::<Vec<_>>();
506
507        let instance_positions_raw = instance_positions
508            .iter()
509            .map(InstancePosition::to_raw)
510            .collect::<Vec<_>>();
511        let instance_colors_raw = instance_colors
512            .iter()
513            .map(InstanceColor::to_raw)
514            .collect::<Vec<_>>();
515        let instance_positions_buffer =
516            device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
517                label: Some("Instance Position Buffer"),
518                contents: bytemuck::cast_slice(&instance_positions_raw),
519                usage: wgpu::BufferUsages::VERTEX,
520            });
521        let instance_colors_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
522            label: Some("Instance Color Buffer"),
523            contents: bytemuck::cast_slice(&instance_colors_raw),
524            usage: wgpu::BufferUsages::VERTEX,
525        });
526
527        Ok(Self {
528            surface,
529            device,
530            queue,
531            config,
532            is_surface_configured: false,
533            render_pipeline,
534            vertex_buffer,
535            index_buffer,
536            num_indices,
537            diffuse_texture,
538            diffuse_bind_group,
539            vertices,
540            indices,
541            camera,
542            camera_buffer,
543            camera_bind_group,
544            camera_uniform,
545            instance_positions,
546            instance_colors,
547            instance_positions_buffer,
548            instance_colors_buffer,
549            window,
550            renderable_entities,
551            camera_strategy,
552        })
553    }
554
555    pub fn window(&self) -> &Window {
556        &self.window
557    }
558
559    pub(crate) fn resize(&mut self, width: u32, height: u32) {
560        if width > 0 && height > 0 {
561            self.is_surface_configured = true;
562            self.config.width = width;
563            self.config.height = height;
564            self.surface.configure(&self.device, &self.config);
565
566            self.camera.aspect = self.config.width as f32 / self.config.height as f32;
567        }
568    }
569
570    pub(crate) fn handle_key(&mut self, event_loop: &ActiveEventLoop, key: KeyCode, pressed: bool) {
571        if key == KeyCode::Escape && pressed {
572            event_loop.exit();
573        }
574    }
575
576    pub(crate) fn update(&mut self, renderable_entities: Vec<Entity>) {
577        self.renderable_entities = renderable_entities;
578
579        let (instance_positions, instance_colors): (Vec<InstancePosition>, Vec<InstanceColor>) =
580            self.renderable_entities
581                .iter()
582                .map(|e| {
583                    let position = e.components.position.unwrap();
584                    let render = e.components.render.unwrap();
585                    let rotation = cgmath::Quaternion::from_axis_angle(
586                        cgmath::Vector3::unit_z(),
587                        cgmath::Deg(0.0),
588                    );
589
590                    let position_3d = cgmath::Vector3 {
591                        x: position.x as f32 * 0.1,
592                        y: position.y as f32 * 0.1,
593                        z: 0.0,
594                    };
595                    (
596                        InstancePosition {
597                            position: position_3d,
598                            rotation,
599                        },
600                        InstanceColor { color: render.rgb },
601                    )
602                })
603                .unzip();
604        self.instance_positions = instance_positions;
605        self.instance_colors = instance_colors;
606        let instance_position_data = self
607            .instance_positions
608            .iter()
609            .map(InstancePosition::to_raw)
610            .collect::<Vec<_>>();
611        let instance_positions_buffer =
612            self.device
613                .create_buffer_init(&wgpu::util::BufferInitDescriptor {
614                    label: Some("Instance Position Buffer"),
615                    contents: bytemuck::cast_slice(&instance_position_data),
616                    usage: wgpu::BufferUsages::VERTEX,
617                });
618
619        let instance_colors_data = self
620            .instance_colors
621            .iter()
622            .map(InstanceColor::to_raw)
623            .collect::<Vec<_>>();
624        let instance_colors_buffer =
625            self.device
626                .create_buffer_init(&wgpu::util::BufferInitDescriptor {
627                    label: Some("Instance Position Buffer"),
628                    contents: bytemuck::cast_slice(&instance_colors_data),
629                    usage: wgpu::BufferUsages::VERTEX,
630                });
631        // Log the instance positions to stdout so we can see things are moving when keys are hit.
632        self.instance_positions_buffer = instance_positions_buffer;
633        self.instance_colors_buffer = instance_colors_buffer;
634
635        let camera_points =
636            self.renderable_entities
637                .iter()
638                .fold((0.0, 0.0, 0.0, 0.0), |mut acc, e| {
639                    if let Some(position) = e.components.position {
640                        if position.x < acc.0 {
641                            acc.0 = position.x;
642                        }
643                        if position.x > acc.1 {
644                            acc.1 = position.x;
645                        }
646                        if position.y < acc.2 {
647                            acc.2 = position.y;
648                        }
649                        if position.y > acc.3 {
650                            acc.3 = position.y;
651                        }
652                    }
653                    acc
654                });
655        let camera_x_position = (camera_points.0 + camera_points.1) / 2.0;
656        let camera_y_position = (1.0 - camera_points.2 + camera_points.3) / 2.0;
657
658        match self.camera_strategy {
659            CameraStrategy::CameraFollow(entity_id) => {
660                let entity = self
661                    .renderable_entities
662                    .iter()
663                    .find(|e| e.id == entity_id)
664                    .unwrap(); // what do we do in the case the entity doesn't exist?
665                let camera_follow_position = entity.components.position.unwrap();
666                self.camera.eye = cgmath::Point3::new(
667                    camera_follow_position.x as f32 * 0.1,
668                    camera_follow_position.y as f32 * 0.1,
669                    4.0,
670                );
671                self.camera.target = cgmath::Point3::new(
672                    camera_follow_position.x as f32 * 0.1,
673                    camera_follow_position.y as f32 * 0.1,
674                    0.0,
675                );
676                self.camera_uniform.update_view_proj(&self.camera);
677            }
678            CameraStrategy::AllEntities => {
679                let game_width = camera_points.1 - camera_points.0;
680                let z_depth = game_width as f32 / 8.1;
681                self.camera.eye =
682                    cgmath::Point3::new(camera_x_position * 0.1, camera_y_position * 0.1, z_depth);
683                self.camera.target =
684                    cgmath::Point3::new(camera_x_position * 0.1, camera_y_position * 0.1, 0.0);
685                self.camera_uniform.update_view_proj(&self.camera);
686            }
687        }
688
689        self.queue.write_buffer(
690            &self.camera_buffer,
691            0,
692            bytemuck::cast_slice(&[self.camera_uniform]),
693        );
694    }
695
696    pub(crate) fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
697        self.window.request_redraw();
698
699        // We can't render unless the surface is configured
700        if !self.is_surface_configured {
701            return Ok(());
702        }
703
704        let output = self.surface.get_current_texture()?;
705        let view = output
706            .texture
707            .create_view(&wgpu::TextureViewDescriptor::default());
708
709        let mut encoder = self
710            .device
711            .create_command_encoder(&wgpu::CommandEncoderDescriptor {
712                label: Some("Render Encoder"),
713            });
714
715        {
716            let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
717                label: Some("Render Pass"),
718                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
719                    view: &view,
720                    resolve_target: None,
721                    ops: wgpu::Operations {
722                        load: wgpu::LoadOp::Clear(wgpu::Color {
723                            r: 1.0,
724                            g: 1.0,
725                            b: 1.0,
726                            a: 1.0,
727                        }),
728                        store: wgpu::StoreOp::Store,
729                    },
730                    depth_slice: None,
731                })],
732                depth_stencil_attachment: None,
733                occlusion_query_set: None,
734                timestamp_writes: None,
735            });
736
737            render_pass.set_pipeline(&self.render_pipeline);
738            render_pass.set_bind_group(0, &self.diffuse_bind_group, &[]);
739            render_pass.set_bind_group(1, &self.camera_bind_group, &[]);
740            render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
741            render_pass.set_vertex_buffer(1, self.instance_positions_buffer.slice(..));
742            render_pass.set_vertex_buffer(2, self.instance_colors_buffer.slice(..));
743            render_pass.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint16);
744            render_pass.draw_indexed(
745                0..self.num_indices,
746                0,
747                0..self.instance_positions.len() as _,
748            );
749        }
750
751        self.queue.submit(iter::once(encoder.finish()));
752        output.present();
753
754        Ok(())
755    }
756}