|
|
|
@ -1,8 +1,10 @@
|
|
|
|
|
use cgmath::{Matrix4, Vector3, Point3, Rad, InnerSpace};
|
|
|
|
|
use winit::event::{MouseScrollDelta, VirtualKeyCode, ElementState};
|
|
|
|
|
use winit::dpi::{PhysicalPosition, LogicalPosition};
|
|
|
|
|
use std::f32::consts::{FRAC_PI_2, PI};
|
|
|
|
|
use std::time::{Duration, Instant};
|
|
|
|
|
use std::f32::consts::FRAC_PI_2;
|
|
|
|
|
|
|
|
|
|
use cgmath::{Decomposed, InnerSpace, Matrix4, Point3, Rad, Vector3};
|
|
|
|
|
use winit::dpi::{LogicalPosition, PhysicalPosition};
|
|
|
|
|
use winit::event::{ElementState, MouseScrollDelta, VirtualKeyCode};
|
|
|
|
|
|
|
|
|
|
use crate::render::OPENGL_TO_WGPU_MATRIX;
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
|
|
@ -13,11 +15,7 @@ pub struct Camera {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Camera {
|
|
|
|
|
pub fn new<
|
|
|
|
|
V: Into<Point3<f32>>,
|
|
|
|
|
Y: Into<Rad<f32>>,
|
|
|
|
|
P: Into<Rad<f32>>,
|
|
|
|
|
>(
|
|
|
|
|
pub fn new<V: Into<Point3<f32>>, Y: Into<Rad<f32>>, P: Into<Rad<f32>>>(
|
|
|
|
|
position: V,
|
|
|
|
|
yaw: Y,
|
|
|
|
|
pitch: P,
|
|
|
|
@ -30,21 +28,38 @@ impl Camera {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn calc_matrix(&self, projection: cgmath::Matrix4<f32>) -> Matrix4<f32> {
|
|
|
|
|
let mx_view = Matrix4::look_at_dir(
|
|
|
|
|
|
|
|
|
|
let view_vector = Point3::new(
|
|
|
|
|
(1.0 * self.pitch.0.sin() * self.yaw.0.sin()),
|
|
|
|
|
(1.0 * self.pitch.0.cos()),
|
|
|
|
|
(1.0 * self.pitch.0.sin() * self.yaw.0.cos()),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let mx_view = Matrix4::look_at(
|
|
|
|
|
self.position,
|
|
|
|
|
Vector3::new(
|
|
|
|
|
self.yaw.0.cos(),
|
|
|
|
|
self.pitch.0.sin(),
|
|
|
|
|
self.yaw.0.sin(),
|
|
|
|
|
).normalize(),
|
|
|
|
|
Point3::new(
|
|
|
|
|
view_vector.x + self.position.x,
|
|
|
|
|
view_vector.y + self.position.y,
|
|
|
|
|
view_vector.z + self.position.z,
|
|
|
|
|
),
|
|
|
|
|
Vector3::unit_y(),
|
|
|
|
|
);
|
|
|
|
|
// I don't know how this works, but it limits pitching to like
|
|
|
|
|
// 70 degrees. Lame
|
|
|
|
|
// let mx_view = Matrix4::look_at_dir(
|
|
|
|
|
// self.position,
|
|
|
|
|
// Vector3::new(
|
|
|
|
|
// self.yaw.0.cos(),
|
|
|
|
|
// self.pitch.0.sin(),
|
|
|
|
|
// self.yaw.0.sin(),
|
|
|
|
|
// ).normalize(),
|
|
|
|
|
// Vector3::unit_y(),
|
|
|
|
|
// );
|
|
|
|
|
let mx_correction = OPENGL_TO_WGPU_MATRIX;
|
|
|
|
|
mx_correction * projection * mx_view
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub struct CameraController {
|
|
|
|
|
amount_left: f32,
|
|
|
|
@ -79,8 +94,12 @@ impl CameraController {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn process_keyboard(&mut self, key: VirtualKeyCode, state: ElementState) -> bool{
|
|
|
|
|
let amount = if state == ElementState::Pressed { 1.0 } else { 0.0 };
|
|
|
|
|
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;
|
|
|
|
@ -111,38 +130,52 @@ impl CameraController {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn process_mouse(&mut self, mouse_dx: f64, mouse_dy: f64) {
|
|
|
|
|
self.rotate_horizontal = mouse_dx as f32;
|
|
|
|
|
self.rotate_horizontal = -mouse_dx as f32;
|
|
|
|
|
self.rotate_vertical = mouse_dy as f32;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn process_scroll(&mut self, delta: &MouseScrollDelta) {
|
|
|
|
|
self.scroll = -match delta {
|
|
|
|
|
// I'm assuming a line is about 100 pixels
|
|
|
|
|
MouseScrollDelta::LineDelta(_, scroll) => scroll * 100.0,
|
|
|
|
|
MouseScrollDelta::PixelDelta(LogicalPosition {
|
|
|
|
|
y: scroll,
|
|
|
|
|
..
|
|
|
|
|
}) => *scroll as f32,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
// pub fn process_scroll(&mut self, delta: &MouseScrollDelta) {
|
|
|
|
|
// self.scroll = -match delta {
|
|
|
|
|
// // I'm assuming a line is about 100 pixels
|
|
|
|
|
// MouseScrollDelta::LineDelta(_, scroll) => scroll * 100.0,
|
|
|
|
|
// MouseScrollDelta::PixelDelta(LogicalPosition {
|
|
|
|
|
// y: scroll,
|
|
|
|
|
// ..
|
|
|
|
|
// }) => *scroll as f32,
|
|
|
|
|
// };
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
pub fn update_camera(&mut self, camera: &mut Camera, dt: f32) {
|
|
|
|
|
|
|
|
|
|
// Move forward/backward and left/right
|
|
|
|
|
let (yaw_sin, yaw_cos) = camera.yaw.0.sin_cos();
|
|
|
|
|
let forward = Vector3::new(yaw_cos, 0.0, yaw_sin).normalize();
|
|
|
|
|
let right = Vector3::new(-yaw_sin, 0.0, yaw_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;
|
|
|
|
|
|
|
|
|
|
// Move in/out (aka. "zoom")
|
|
|
|
|
// Note: this isn't an actual zoom. The camera's position
|
|
|
|
|
// changes when zooming. I've added this to make it easier
|
|
|
|
|
// to get closer to an object you want to focus on.
|
|
|
|
|
let (pitch_sin, pitch_cos) = camera.pitch.0.sin_cos();
|
|
|
|
|
let scrollward = Vector3::new(pitch_cos * yaw_cos, pitch_sin, pitch_cos * yaw_sin).normalize();
|
|
|
|
|
camera.position += scrollward * self.scroll * self.speed * self.sensitivity * dt;
|
|
|
|
|
self.scroll = 0.0;
|
|
|
|
|
|
|
|
|
|
let view_vector = Vector3::new(
|
|
|
|
|
(1.0 * camera.pitch.0.sin() * camera.yaw.0.sin()),
|
|
|
|
|
(1.0 * camera.pitch.0.cos()),
|
|
|
|
|
(1.0 * camera.pitch.0.sin() * camera.yaw.0.cos()),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let yaw_offset = camera.yaw.0 - Rad(PI).0;
|
|
|
|
|
let right_vector = Vector3::new(
|
|
|
|
|
(1.0 * camera.pitch.0.sin() * yaw_offset.sin()),
|
|
|
|
|
(1.0 * camera.pitch.0.cos()),
|
|
|
|
|
(1.0 * camera.pitch.0.sin() * yaw_offset.cos()),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
camera.position += view_vector * (self.amount_forward - self.amount_backward) * self.speed * dt;
|
|
|
|
|
camera.position += right_vector * (self.amount_right - self.amount_left) * self.speed * dt;
|
|
|
|
|
|
|
|
|
|
// I'm not a huge fan of this
|
|
|
|
|
// // Move in/out (aka. "zoom")
|
|
|
|
|
// // Note: this isn't an actual zoom. The camera's position
|
|
|
|
|
// // changes when zooming. I've added this to make it easier
|
|
|
|
|
// // to get closer to an object you want to focus on.
|
|
|
|
|
// let (pitch_sin, pitch_cos) = camera.pitch.0.sin_cos();
|
|
|
|
|
// let scrollward = Vector3::new(pitch_cos * yaw_cos, pitch_sin, pitch_cos * yaw_sin).normalize();
|
|
|
|
|
// camera.position += scrollward * self.scroll * self.speed * self.sensitivity * dt;
|
|
|
|
|
// self.scroll = 0.0;
|
|
|
|
|
|
|
|
|
|
// Move up/down. Since we don't use roll, we can just
|
|
|
|
|
// modify the y coordinate directly.
|
|
|
|
@ -150,7 +183,7 @@ impl CameraController {
|
|
|
|
|
|
|
|
|
|
// Rotate
|
|
|
|
|
camera.yaw += Rad(self.rotate_horizontal) * self.sensitivity * dt;
|
|
|
|
|
camera.pitch += Rad(-self.rotate_vertical) * self.sensitivity * dt;
|
|
|
|
|
camera.pitch += Rad(self.rotate_vertical) * self.sensitivity * dt;
|
|
|
|
|
|
|
|
|
|
// If process_mouse isn't called every frame, these values
|
|
|
|
|
// will not get set to zero, and the camera will rotate
|
|
|
|
@ -159,15 +192,10 @@ impl CameraController {
|
|
|
|
|
self.rotate_vertical = 0.0;
|
|
|
|
|
|
|
|
|
|
// Keep the camera's angle from going too high/low.
|
|
|
|
|
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);
|
|
|
|
|
if camera.pitch < -Rad(0.0) {
|
|
|
|
|
camera.pitch = -Rad(0.0);
|
|
|
|
|
} else if camera.pitch > Rad(PI) {
|
|
|
|
|
camera.pitch = Rad(PI);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|