1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
use cgmath::*;
use wgpu::util::DeviceExt;
use crate::render::{OPENGL_TO_WGPU_MATRIX, render_engine::*};
/// A representation of all information needed to properly use a camera in WGPU.
#[derive(Debug)]
pub struct Camera {
pub uniform: CameraUniform,
pub buffer: wgpu::Buffer,
pub bind_layout: wgpu::BindGroupLayout,
pub bind_group: wgpu::BindGroup,
pub position: cgmath::Vector3<f32>,
pub rotation: cgmath::Quaternion<f32>,
pub aspect: f32,
pub fovy: f32,
pub znear: f32,
pub zfar: f32
}
impl Camera {
/// The bind group layout to be used to use this camera in any shader. This is hear to promote consistency across implementations.
pub const BIND_LAYOUT: wgpu::BindGroupLayoutDescriptor<'static> = wgpu::BindGroupLayoutDescriptor {
label: Some("camera_bind_layout"),
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
count: None,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None
}
}
]
};
/// Creates a new camera.
///
/// Arguments:
/// * engine: &RenderEngine - The render engine that this camera will belong too.
/// * aspect: f32 - The aspect ratio of this camera
/// * fovy: f32 - The field of view on the vertical axis of the camera.
/// * znear: f32 - The distance to the nearest clipping plane where the rendering will stop.
/// * zfar: f32 - The distance to the farthest clipping plane where the rendering will stop.
///
/// Note: only objects between the two mentioned clipping planes will be rendered.
///
/// Returns a new camera.
pub fn new(
engine: &RenderEngine,
aspect: f32, fovy: f32,
znear: f32, zfar: f32
) -> Self {
let uniform = CameraUniform::new();
// create camera uniform and buffer
let buffer = engine.device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
label: Some("Camera Buffer"),
contents: bytemuck::cast_slice(&[uniform]),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST
}
);
// create camera bind layout and group
let bind_layout = engine.device.create_bind_group_layout(&Self::BIND_LAYOUT);
let bind_group = engine.device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("camera_bind_group"),
layout: &bind_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: buffer.as_entire_binding()
}
]
});
Self {
uniform, buffer, bind_layout, bind_group,
position: (0.0, 0.0, 0.0).into(),
rotation: (0.0, 0.0, 0.0, 1.0).into(),
aspect, fovy, znear, zfar
}
}
/// Builds a view projection matrix for this camera.
///
/// Returns the view and projection matrix for this camera.
pub fn build_view_projection_matrix(&self, engine: &RenderEngine) -> cgmath::Matrix4<f32> {
let view = Matrix4::from(self.rotation) * Matrix4::from_translation(-self.position);
let proj = cgmath::perspective(cgmath::Deg(self.fovy), engine.config.width as f32 / engine.config.height as f32, self.znear, self.zfar);
// println!("Build")
return OPENGL_TO_WGPU_MATRIX * proj * view;
}
/// Updates the camera and its WGPU buffers with the given render engine.
///
/// Arguments:
/// * engine: &mut RenderEngine - The render engine to which the buffers will be updated.
pub fn update(&mut self, engine: &RenderEngine) {
self.uniform.view_position = [self.position.x, self.position.y, self.position.z, 0.0];
self.uniform.view_proj = self.build_view_projection_matrix(engine).into();
engine.queue.write_buffer(&self.buffer, 0, bytemuck::cast_slice(&[self.uniform]));
}
/// Binds this camera to the given render pass at the given bind group index.
///
/// Arguments:
/// * &self - The camera to be bound.
/// * pass: &mut wgpu::RenderPass - The render pass to bind too.
/// * index: u32 - The index in the bind group to bind too.
pub fn bind<'rpass>(
&'rpass mut self,
pass: &mut wgpu::RenderPass<'rpass>,
engine: &RenderEngine,
index: u32
) {
self.update(engine);
pass.set_bind_group(index, &self.bind_group, &[]);
}
}
/// The camera uniform, this is the rust representation of the camera data is passed to the shaders.
#[repr(C)]
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
pub struct CameraUniform {
pub view_position: [f32; 4],
pub view_proj: [[f32; 4]; 4]
}
impl CameraUniform {
/// Creates a new empty shader uniform with empty matrices.
pub fn new() -> Self {
Self { view_position: [0.0; 4], view_proj: cgmath::Matrix4::identity().into() }
}
}