tirbofish/dropbear
main / crates / dropbear-engine / src / camera.rs · 9989 bytes
crates/dropbear-engine/src/camera.rs
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
//! Camera and components related to cameras.
use std::sync::Arc;
use glam::{DMat4, DQuat, DVec3, Mat4};
use serde::{Deserialize, Serialize};
use wgpu::Buffer;
use crate::{buffer::UniformBuffer, graphics::SharedGraphicsContext};
use crate::buffer::WritableBuffer;
/// Matrix that converts OpenGL (from [`glam`]) to [`wgpu`] values
#[rustfmt::skip]
pub const OPENGL_TO_WGPU_MATRIX: [[f64; 4]; 4] = [
[1.0, 0.0, 0.0, 0.0],
[0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 0.5, 0.0],
[0.0, 0.0, 0.5, 1.0],
];
/// Shared tuning data for camera movement and projection.
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct CameraSettings {
pub speed: f64,
pub sensitivity: f64,
pub fov_y: f64,
}
impl CameraSettings {
pub const fn new(speed: f64, sensitivity: f64, fov_y: f64) -> Self {
Self {
speed,
sensitivity,
fov_y,
}
}
}
impl Default for CameraSettings {
fn default() -> Self {
Self::new(1.0, 0.002, 75.0)
}
}
/// The basic values of a Camera.
#[derive(Debug, Clone)]
pub struct Camera {
/// The name of the camera
pub label: String,
/// Eye of camera / Position
pub eye: DVec3,
/// Target of camera / Looking at
pub target: DVec3,
/// Up
pub up: DVec3,
/// Aspect ratio
pub aspect: f64,
/// Near buffer?
pub znear: f64,
/// Far buffer?
pub zfar: f64,
/// Yaw (rotation)
pub yaw: f64,
/// Pitch (rotation)
pub pitch: f64,
/// Tuning values that control movement and projection
pub settings: CameraSettings,
/// Uniform/interface for Rust and the GPU
pub uniform: CameraUniform,
buffer: UniformBuffer<CameraUniform>,
pub bind_group: wgpu::BindGroup,
/// View matrix
pub view_mat: DMat4,
/// Projection Matrix
pub proj_mat: DMat4,
}
/// A simple builder/struct that allows you to build a [`Camera`]
pub struct CameraBuilder {
pub eye: DVec3,
pub target: DVec3,
pub up: DVec3,
pub aspect: f64,
pub znear: f64,
pub zfar: f64,
pub settings: CameraSettings,
}
impl Camera {
/// Creates a new camera
pub fn new(
graphics: Arc<SharedGraphicsContext>,
builder: CameraBuilder,
label: Option<&str>,
) -> Self {
let mut uniform = CameraUniform::new();
let dir = (builder.target - builder.eye).normalize();
let pitch = dir.y.clamp(-1.0, 1.0).asin();
let yaw = dir.z.atan2(dir.x);
let view = DMat4::look_at_lh(builder.eye, builder.target, builder.up);
let proj = DMat4::perspective_infinite_reverse_lh(
builder.settings.fov_y.to_radians(),
builder.aspect,
builder.znear,
);
let view_mat = view;
let proj_mat = proj;
let mvp = DMat4::from_cols_array_2d(&OPENGL_TO_WGPU_MATRIX) * proj * view;
uniform.view_proj = mvp.as_mat4().to_cols_array_2d();
let buffer = UniformBuffer::new(&graphics.device, "camera uniform");
let bind_group = graphics
.device
.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("editor camera bind group"),
layout: &graphics.layouts.camera_bind_group_layout,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: buffer.buffer().as_entire_binding(),
}],
});
let camera = Self {
eye: builder.eye,
target: builder.target,
up: builder.up,
aspect: builder.aspect,
znear: builder.znear,
zfar: builder.zfar,
uniform,
buffer,
yaw,
pitch,
settings: builder.settings,
label: if let Some(l) = label {
l.to_string()
} else {
String::from("Camera")
},
view_mat,
proj_mat,
bind_group,
};
log::debug!(
"Created new camera{}",
if let Some(l) = label {
format!(" with the label {}", l)
} else {
String::new()
}
);
camera
}
/// Creates a default camera
pub fn predetermined(graphics: Arc<SharedGraphicsContext>, label: Option<&str>) -> Self {
Self::new(
graphics.clone(),
CameraBuilder {
eye: DVec3::new(0.0, 1.0, 2.0),
target: DVec3::new(0.0, 0.0, 0.0),
up: DVec3::Y,
aspect: (graphics.window.inner_size().width / graphics.window.inner_size().height)
.into(),
znear: 0.1,
zfar: 100.0,
settings: CameraSettings::default(),
},
label,
)
}
pub fn rotation(&self) -> DQuat {
let yaw = DQuat::from_axis_angle(DVec3::Y, self.yaw);
let pitch = DQuat::from_axis_angle(DVec3::X, self.pitch);
yaw * pitch
}
pub fn buffer(&self) -> &Buffer {
self.buffer.buffer()
}
pub fn forward(&self) -> DVec3 {
(self.target - self.eye).normalize()
}
pub fn position(&self) -> DVec3 {
self.eye
}
/// Prints out the values of the camera.
pub fn debug_camera_state(&self) {
let camera = self;
log::debug!("Camera state:");
log::debug!(" Eye: {:?}", camera.eye);
log::debug!(" Target: {:?}", camera.target);
log::debug!(" Up: {:?}", camera.up);
log::debug!(" FOV Y: {}", camera.settings.fov_y);
log::debug!(" Aspect: {}", camera.aspect);
log::debug!(" Z Near: {}", camera.znear);
log::debug!(" Proj Mat finite: {}", camera.proj_mat.is_finite());
log::debug!(" View Mat finite: {}", camera.view_mat.is_finite());
}
fn build_vp(&mut self) -> DMat4 {
let view = DMat4::look_at_lh(self.eye, self.target, self.up);
let proj = DMat4::perspective_infinite_reverse_lh(
self.settings.fov_y.to_radians(),
self.aspect,
self.znear,
);
self.view_mat = view;
self.proj_mat = proj;
DMat4::from_cols_array_2d(&OPENGL_TO_WGPU_MATRIX) * proj * view
}
pub fn update(&mut self, graphics: Arc<SharedGraphicsContext>) {
puffin::profile_function!();
self.update_view_proj();
self.buffer.write(&graphics.queue, &self.uniform);
}
pub fn update_view_proj(&mut self) {
puffin::profile_function!();
let mut uniform = self.uniform;
uniform.update(self);
self.uniform = uniform;
}
pub fn move_forwards(&mut self, dt: f32) {
let forward = (self.target - self.eye).normalize();
self.eye += forward * self.settings.speed * dt as f64;
self.target += forward * self.settings.speed * dt as f64;
}
pub fn move_back(&mut self, dt: f32) {
let forward = (self.target - self.eye).normalize();
self.eye -= forward * self.settings.speed * dt as f64;
self.target -= forward * self.settings.speed * dt as f64;
}
pub fn move_right(&mut self, dt: f32) {
let forward = (self.target - self.eye).normalize();
// LH: right = up.cross(forward)
let right = self.up.cross(forward).normalize();
self.eye += right * self.settings.speed * dt as f64;
self.target += right * self.settings.speed * dt as f64;
}
pub fn move_left(&mut self, dt: f32) {
let forward = (self.target - self.eye).normalize();
let right = self.up.cross(forward).normalize();
self.eye -= right * self.settings.speed * dt as f64;
self.target -= right * self.settings.speed * dt as f64;
}
pub fn move_up(&mut self, dt: f32) {
let up = self.up.normalize();
self.eye += up * self.settings.speed * dt as f64;
self.target += up * self.settings.speed * dt as f64;
}
pub fn move_down(&mut self, dt: f32) {
let up = self.up.normalize();
self.eye -= up * self.settings.speed * dt as f64;
self.target -= up * self.settings.speed * dt as f64;
}
pub fn track_mouse_delta(&mut self, dx: f64, dy: f64) {
let sensitivity = self.settings.sensitivity;
self.yaw -= dx * sensitivity;
self.pitch -= dy * sensitivity;
let max_pitch = std::f64::consts::FRAC_PI_2 - 0.01;
self.pitch = self.pitch.clamp(-max_pitch, max_pitch);
let dir = DVec3::new(
self.yaw.cos() * self.pitch.cos(),
self.pitch.sin(),
self.yaw.sin() * self.pitch.cos(),
);
self.target = self.eye + dir;
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone, Default, bytemuck::Pod, bytemuck::Zeroable)]
pub struct CameraUniform {
pub view_position: [f32; 4],
pub view: [[f32; 4]; 4],
pub view_proj: [[f32; 4]; 4],
pub inv_proj: [[f32; 4]; 4],
pub inv_view: [[f32; 4]; 4],
}
impl CameraUniform {
pub fn new() -> Self {
Self {
view_position: [0.0; 4],
view: Mat4::IDENTITY.to_cols_array_2d(),
view_proj: Mat4::IDENTITY.to_cols_array_2d(),
inv_proj: Mat4::IDENTITY.to_cols_array_2d(),
inv_view: Mat4::IDENTITY.to_cols_array_2d(),
}
}
pub fn update(&mut self, camera: &mut Camera) {
self.view_position = camera.eye.as_vec3().extend(1.0).to_array();
let vp = camera.build_vp();
let view = camera.view_mat;
let proj = camera.proj_mat;
let wgpu_matrix = DMat4::from_cols_array_2d(&OPENGL_TO_WGPU_MATRIX);
self.view = view.as_mat4().to_cols_array_2d();
self.view_proj = vp.as_mat4().to_cols_array_2d();
self.inv_proj = (wgpu_matrix * proj).inverse().as_mat4().to_cols_array_2d();
self.inv_view = view.inverse().as_mat4().to_cols_array_2d();
}
}