1
0
Fork 0

tutorial 12 a better camera

This commit is contained in:
Adrian Hedqvist 2020-11-26 21:01:35 +01:00
parent 7a8b9a973c
commit 10a19247f7
2 changed files with 175 additions and 124 deletions

View file

@ -1,121 +1,152 @@
use cgmath::prelude::*;
use winit::event::{ElementState, KeyboardInput, VirtualKeyCode, WindowEvent};
use std::f32::consts::FRAC_PI_2;
use cgmath::{prelude::*, Matrix4, Point3, Rad, Vector3};
use winit::event::{ElementState, VirtualKeyCode};
#[rustfmt::skip]
pub const OPENGL_TO_WGPU_MATRIX: Matrix4<f32> = 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,
);
pub struct Camera {
pub eye: cgmath::Point3<f32>,
pub target: cgmath::Point3<f32>,
pub up: cgmath::Vector3<f32>,
pub aspect: f32,
pub fovy: f32,
pub znear: f32,
pub zfar: f32,
pub position: Point3<f32>,
pub yaw: Rad<f32>,
pub pitch: Rad<f32>,
}
impl Camera {
pub fn build_view_projection_matrix(&self) -> cgmath::Matrix4<f32> {
let view = cgmath::Matrix4::look_at(self.eye, self.target, self.up);
let proj = cgmath::perspective(cgmath::Deg(self.fovy), self.aspect, self.znear, self.zfar);
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(),
}
}
proj * view
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 CameraController {
speed: f32,
is_up_pressed: bool,
is_down_pressed: bool,
is_forward_pressed: bool,
is_backward_pressed: bool,
is_left_pressed: bool,
is_right_pressed: bool,
sensivity: f32,
amount_up: f32,
amount_down: f32,
amount_forward: f32,
amount_backward: f32,
amount_left: f32,
amount_right: f32,
rotate_horizontal: f32,
rotate_vertical: f32,
}
impl CameraController {
pub fn new(speed: f32) -> Self {
pub fn new(speed: f32, sensivity: f32) -> Self {
Self {
speed,
is_up_pressed: false,
is_down_pressed: false,
is_forward_pressed: false,
is_backward_pressed: false,
is_left_pressed: false,
is_right_pressed: false,
sensivity,
amount_up: 0.0,
amount_down: 0.0,
amount_forward: 0.0,
amount_backward: 0.0,
amount_left: 0.0,
amount_right: 0.0,
rotate_horizontal: 0.0,
rotate_vertical: 0.0,
}
}
pub fn process_events(&mut self, event: &WindowEvent) -> bool {
match event {
WindowEvent::KeyboardInput {
input:
KeyboardInput {
state,
virtual_keycode: Some(keycode),
..
},
..
} => {
let is_pressed = *state == ElementState::Pressed;
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;
}
match keycode {
VirtualKeyCode::Space => {
self.is_up_pressed = is_pressed;
true
}
VirtualKeyCode::LControl => {
self.is_down_pressed = is_pressed;
true
}
VirtualKeyCode::W | VirtualKeyCode::Up => {
self.is_forward_pressed = is_pressed;
true
}
VirtualKeyCode::A | VirtualKeyCode::Left => {
self.is_left_pressed = is_pressed;
true
}
VirtualKeyCode::S | VirtualKeyCode::Down => {
self.is_backward_pressed = is_pressed;
true
}
VirtualKeyCode::D | VirtualKeyCode::Right => {
self.is_right_pressed = is_pressed;
true
}
_ => 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::Space => {
self.amount_up = amount;
true
}
VirtualKeyCode::LControl => {
self.amount_down = amount;
true
}
VirtualKeyCode::W | VirtualKeyCode::Up => {
self.amount_forward = amount;
true
}
VirtualKeyCode::A | VirtualKeyCode::Left => {
self.amount_left = amount;
true
}
VirtualKeyCode::S | VirtualKeyCode::Down => {
self.amount_backward = amount;
true
}
VirtualKeyCode::D | VirtualKeyCode::Right => {
self.amount_right = amount;
true
}
_ => false,
}
}
pub fn update_camera(&self, camera: &mut Camera, dt: f32) {
let mut forward = camera.target - camera.eye;
let mut forward_norm = forward.normalize();
let mut forward_mag = forward.magnitude();
pub fn update_camera(&mut self, camera: &mut Camera, dt: f32) {
let (yaw_sin, yaw_cos) = camera.yaw.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;
if self.is_forward_pressed != self.is_backward_pressed {
// Prevents glitching when camera gets too close to the
// center of the scene.
let f_spd = (self.speed * dt).min(forward_mag - 8.0);
if self.is_forward_pressed && forward_mag > f_spd {
camera.eye += forward_norm * f_spd;
} else if self.is_backward_pressed {
camera.eye -= forward_norm * self.speed * dt;
}
forward = camera.target - camera.eye;
forward_norm = forward.normalize();
forward_mag = forward.magnitude();
}
camera.position.y += (self.amount_up - self.amount_down) * self.speed * dt;
let right = forward_norm.cross(camera.up);
camera.yaw += Rad(self.rotate_horizontal) * self.sensivity * dt;
camera.pitch += Rad(-self.rotate_vertical) * self.sensivity * dt;
self.rotate_horizontal = 0.0;
self.rotate_vertical = 0.0;
if self.is_right_pressed != self.is_left_pressed {
if self.is_right_pressed {
camera.eye =
camera.target - (forward - right * self.speed * dt).normalize() * forward_mag;
} else if self.is_left_pressed {
camera.eye =
camera.target - (forward + right * self.speed * dt).normalize() * forward_mag;
}
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 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 * cgmath::perspective(self.fovy, self.aspect, self.znear, self.zfar)
}
}

View file

@ -1,11 +1,7 @@
use imgui::im_str;
use model::{DrawLight, DrawModel, Model, Vertex};
use wgpu::util::DeviceExt;
use winit::{
event::*,
event_loop::{ControlFlow, EventLoop},
window::{Window, WindowBuilder},
};
use winit::{dpi::PhysicalPosition, event::*, event_loop::{ControlFlow, EventLoop}, window::{Window, WindowBuilder}};
use cgmath::prelude::*;
@ -107,9 +103,9 @@ impl Uniforms {
}
}
fn update_view_proj(&mut self, camera: &camera::Camera) {
self.view_position = camera.eye.to_homogeneous().into();
self.view_proj = (OPENGL_TO_WGPU_MATRIX * camera.build_view_projection_matrix()).into();
fn update_view_proj(&mut self, camera: &camera::Camera, projection: &camera::Projection) {
self.view_position = camera.position.to_homogeneous().into();
self.view_proj = (projection.calc_matrix() * camera.calc_matrix()).into();
}
}
@ -186,6 +182,7 @@ struct State {
depth_texture: texture::Texture,
camera: camera::Camera,
projection: camera::Projection,
camera_controller: camera::CameraController,
uniforms: Uniforms,
@ -210,6 +207,9 @@ struct State {
imgui_renderer: imgui_wgpu::Renderer,
rotation_speed: f32,
last_mouse_pos: PhysicalPosition<f64>,
mouse_pressed: bool
}
impl State {
@ -324,20 +324,15 @@ impl State {
usage: wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::COPY_DST,
});
let camera = camera::Camera {
eye: (0.0, 5.0, -10.0).into(),
target: (0.0, 0.0, 0.0).into(),
up: cgmath::Vector3::unit_y(),
aspect: sc_desc.width as f32 / sc_desc.height as f32,
fovy: 45.0,
znear: 0.1,
zfar: 100.0,
};
let projection = camera::Projection::new(sc_desc.width, sc_desc.height, cgmath::Deg(45.0), 0.1, 100.0);
let camera_controller = camera::CameraController::new(20.0);
let camera = camera::Camera::new((0.0, 5.0, -10.0), cgmath::Deg(90.0), cgmath::Deg(-20.0));
let camera_controller = camera::CameraController::new(4.0, 0.4);
let mut uniforms = Uniforms::new();
uniforms.update_view_proj(&camera);
uniforms.update_view_proj(&camera, &projection);
let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("uniform_buffer"),
@ -369,7 +364,7 @@ impl State {
});
// Lights
let light = light::Light::new((2.0, 2.0, 2.0).into(), (1.0, 1.0, 1.0).into());
let light = light::Light::new((8.0, 2.0, 8.0).into(), (1.0, 1.0, 1.0).into());
let light_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Light VB"),
@ -488,6 +483,7 @@ impl State {
sc_desc,
swap_chain,
camera,
projection,
camera_controller,
depth_texture,
uniforms,
@ -506,6 +502,8 @@ impl State {
imgui_platform,
imgui_renderer,
rotation_speed: 1.0,
last_mouse_pos: (0.0, 0.0).into(),
mouse_pressed: false,
}
}
@ -515,27 +513,49 @@ impl State {
self.sc_desc.height = new_size.height;
self.depth_texture =
texture::Texture::create_depth_texture(&self.device, &self.sc_desc, "depth_texture");
self.camera.aspect = new_size.width as f32 / new_size.height as f32;
self.projection.resize(new_size.width, new_size.height);
self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc);
}
fn input(&mut self, event: &WindowEvent) -> bool {
if let WindowEvent::KeyboardInput { .. } = event {
if self.imgui.io().want_capture_keyboard {
return true;
}
} else if let WindowEvent::MouseInput { .. } = event {
if self.imgui.io().want_capture_mouse {
return true;
}
}
self.camera_controller.process_events(event)
match event {
WindowEvent::KeyboardInput { input: winit::event::KeyboardInput {
virtual_keycode: Some(key),
state,
..
}, .. } => {
if self.imgui.io().want_capture_keyboard {
true
} else {
self.camera_controller.process_keyboard(*key, *state)
}
}
WindowEvent::CursorMoved { position, .. } => {
if !self.imgui.io().want_capture_mouse {
let mouse_dx = position.x - self.last_mouse_pos.x;
let mouse_dy = position.y - self.last_mouse_pos.y;
if self.mouse_pressed {
self.camera_controller.process_mouse(mouse_dx, mouse_dy);
}
self.last_mouse_pos = *position;
}
true
}
WindowEvent::MouseInput { state, button, .. } => {
if !self.imgui.io().want_capture_mouse && *button == MouseButton::Left {
self.mouse_pressed = *state == ElementState::Pressed;
}
true
}
_ => false,
}
}
fn update(&mut self, dt: f32) {
self.camera_controller.update_camera(&mut self.camera, dt);
self.uniforms.update_view_proj(&self.camera);
self.uniforms.update_view_proj(&self.camera, &self.projection);
let mut encoder = self
.device
@ -569,7 +589,7 @@ impl State {
let deg = cgmath::Deg(30.0 * dt * self.rotation_speed);
for instance in &mut self.instances {
let rot = cgmath::Quaternion::from(cgmath::Euler::new(deg, deg*1.1, deg*1.2));
let rot = cgmath::Quaternion::from(cgmath::Euler::new(deg, deg * 1.1, deg * 1.2));
instance.rotation = instance.rotation * rot;
}