mirror of https://github.com/sotrh/learn-wgpu
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
161 lines
4.2 KiB
Rust
161 lines
4.2 KiB
Rust
use cgmath::prelude::*;
|
|
use cgmath::*;
|
|
use std::f32::consts::FRAC_PI_2;
|
|
use std::time::Duration;
|
|
use winit::event::*;
|
|
|
|
#[rustfmt::skip]
|
|
pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new(
|
|
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,
|
|
);
|
|
|
|
#[derive(Debug)]
|
|
pub struct Camera {
|
|
position: Point3<f32>,
|
|
yaw: Rad<f32>,
|
|
pitch: Rad<f32>,
|
|
}
|
|
|
|
impl Camera {
|
|
pub fn new<V: Into<Point3<f32>>, Y: Into<Rad<f32>>, P: Into<Rad<f32>>>(
|
|
position: V,
|
|
yaw: Y,
|
|
pitch: P,
|
|
) -> Self {
|
|
Self {
|
|
position: position.into(),
|
|
yaw: yaw.into(),
|
|
pitch: pitch.into(),
|
|
}
|
|
}
|
|
|
|
pub fn calc_matrix(&self) -> Matrix4<f32> {
|
|
Matrix4::look_at_dir(
|
|
self.position,
|
|
Vector3::new(self.yaw.0.cos(), self.pitch.0.sin(), self.yaw.0.sin()).normalize(),
|
|
Vector3::unit_y(),
|
|
)
|
|
}
|
|
}
|
|
|
|
pub struct Projection {
|
|
aspect: f32,
|
|
fovy: Rad<f32>,
|
|
znear: f32,
|
|
zfar: f32,
|
|
}
|
|
|
|
impl Projection {
|
|
pub fn new<F: Into<Rad<f32>>>(width: u32, height: u32, fovy: F, znear: f32, zfar: f32) -> Self {
|
|
Self {
|
|
aspect: width as f32 / height as f32,
|
|
fovy: fovy.into(),
|
|
znear,
|
|
zfar,
|
|
}
|
|
}
|
|
|
|
pub fn resize(&mut self, width: u32, height: u32) {
|
|
self.aspect = width as f32 / height as f32;
|
|
}
|
|
|
|
pub fn calc_matrix(&self) -> Matrix4<f32> {
|
|
OPENGL_TO_WGPU_MATRIX * perspective(self.fovy, self.aspect, self.znear, self.zfar)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct CameraController {
|
|
amount_left: f32,
|
|
amount_right: f32,
|
|
amount_forward: f32,
|
|
amount_backward: f32,
|
|
rotate_horizontal: f32,
|
|
rotate_vertical: f32,
|
|
speed: f32,
|
|
is_dirty: bool,
|
|
}
|
|
|
|
impl CameraController {
|
|
pub fn new(speed: f32) -> Self {
|
|
Self {
|
|
amount_left: 0.0,
|
|
amount_right: 0.0,
|
|
amount_forward: 0.0,
|
|
amount_backward: 0.0,
|
|
rotate_horizontal: 0.0,
|
|
rotate_vertical: 0.0,
|
|
speed,
|
|
is_dirty: false,
|
|
}
|
|
}
|
|
|
|
pub fn process_keyboard(&mut self, key: VirtualKeyCode, state: ElementState) -> bool {
|
|
let amount = if state == ElementState::Pressed {
|
|
1.0
|
|
} else {
|
|
0.0
|
|
};
|
|
match key {
|
|
VirtualKeyCode::W | VirtualKeyCode::Up => {
|
|
self.amount_forward = amount;
|
|
self.is_dirty = true;
|
|
true
|
|
}
|
|
VirtualKeyCode::S | VirtualKeyCode::Down => {
|
|
self.amount_backward = amount;
|
|
self.is_dirty = true;
|
|
true
|
|
}
|
|
VirtualKeyCode::A | VirtualKeyCode::Left => {
|
|
self.amount_left = amount;
|
|
self.is_dirty = true;
|
|
true
|
|
}
|
|
VirtualKeyCode::D | VirtualKeyCode::Right => {
|
|
self.amount_right = amount;
|
|
self.is_dirty = true;
|
|
true
|
|
}
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn process_mouse(&mut self, mouse_dx: f64, mouse_dy: f64) {
|
|
self.rotate_horizontal = mouse_dx as f32;
|
|
self.rotate_vertical = mouse_dy as f32;
|
|
self.is_dirty = true;
|
|
}
|
|
|
|
pub fn update_camera(&mut self, camera: &mut Camera, dt: Duration) {
|
|
self.is_dirty = false;
|
|
let dt = dt.as_secs_f32();
|
|
|
|
let (sin, cos) = camera.yaw.0.sin_cos();
|
|
|
|
let forward = Vector3::new(cos, 0.0, sin).normalize();
|
|
let right = Vector3::new(sin, 0.0, cos).normalize();
|
|
|
|
camera.position += forward * (self.amount_forward - self.amount_backward) * self.speed * dt;
|
|
camera.position += right * (self.amount_right - self.amount_left) * self.speed * dt;
|
|
camera.yaw += Rad(-self.rotate_horizontal) * dt;
|
|
camera.pitch += Rad(self.rotate_vertical) * dt;
|
|
|
|
self.rotate_horizontal = 0.0;
|
|
self.rotate_vertical = 0.0;
|
|
|
|
if camera.pitch < -Rad(FRAC_PI_2) {
|
|
camera.pitch = -Rad(FRAC_PI_2);
|
|
} else if camera.pitch > Rad(FRAC_PI_2) {
|
|
camera.pitch = Rad(FRAC_PI_2);
|
|
}
|
|
}
|
|
|
|
pub fn is_dirty(&self) -> bool {
|
|
self.is_dirty
|
|
}
|
|
}
|