Add simple camera movement

This commit is contained in:
Marco Thomas
2021-08-25 19:24:32 +02:00
parent 2464e2118f
commit d841a0da12
7 changed files with 242 additions and 11 deletions

26
src/camera.rs Normal file
View File

@@ -0,0 +1,26 @@
#[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,
);
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,
}
impl Camera {
pub fn build_view_projection_matrix(&self) -> cgmath::Matrix4<f32> {
let view = cgmath::Matrix4::look_at_rh(self.eye, self.target, self.up);
let projection =
cgmath::perspective(cgmath::Deg(self.fovy), self.aspect, self.znear, self.zfar);
return OPENGL_TO_WGPU_MATRIX * projection * view;
}
}

96
src/camera_controller.rs Normal file
View File

@@ -0,0 +1,96 @@
use winit::event::*;
#[derive(Debug)]
struct IsPressed(bool);
impl From<bool> for IsPressed {
fn from(item: bool) -> Self {
IsPressed(item)
}
}
#[derive(Debug)]
enum ButtonPress {
Up(IsPressed),
Down(IsPressed),
Left(IsPressed),
Right(IsPressed),
Forward(IsPressed),
Backward(IsPressed),
}
pub struct CameraController {
speed: f32,
button_press: Option<ButtonPress>,
}
impl CameraController {
pub fn new(speed: f32) -> Self {
Self {
speed,
button_press: None,
}
}
pub fn process_events(&mut self, event: &WindowEvent) -> bool {
match event {
WindowEvent::KeyboardInput {
input:
KeyboardInput {
state,
virtual_keycode: Some(key),
..
},
..
} => {
let pressed = (*state == ElementState::Pressed).into();
match key {
VirtualKeyCode::W => {
self.button_press = Some(ButtonPress::Forward(pressed));
true
}
VirtualKeyCode::A => {
self.button_press = Some(ButtonPress::Left(pressed));
true
}
VirtualKeyCode::S => {
self.button_press = Some(ButtonPress::Backward(pressed));
true
}
VirtualKeyCode::D => {
self.button_press = Some(ButtonPress::Right(pressed));
true
}
VirtualKeyCode::Space => {
self.button_press = Some(ButtonPress::Up(pressed));
true
}
VirtualKeyCode::LShift => {
self.button_press = Some(ButtonPress::Down(pressed));
true
}
_ => false,
}
}
_ => false,
}
}
pub fn update_camera(&self, camera: &mut crate::camera::Camera) {
use cgmath::InnerSpace;
let forward = camera.target - camera.eye;
let forward_norm = forward.normalize();
let forward_mag = forward.magnitude();
match self.button_press {
Some(ButtonPress::Forward(IsPressed(true))) if forward_mag > self.speed => {
camera.eye += forward_norm * self.speed;
}
Some(ButtonPress::Backward(IsPressed(true))) => {
camera.eye -= forward_norm * self.speed;
}
_ => {}
}
// TODO add rest of the movement
}
}

View File

@@ -7,6 +7,9 @@ use winit::{
mod state;
mod vertex;
mod texture;
mod camera;
mod uniform;
mod camera_controller;
use crate::state::State;

View File

@@ -1,4 +1,12 @@
// Vertex shader
[[block]]
struct Uniform {
view_projection: mat4x4<f32>;
};
[[group(1), binding(0)]]
var<uniform> uniform: Uniform;
struct VertexInput {
[[location(0)]] position: vec3<f32>;
[[location(1)]] texture_coords: vec2<f32>;
@@ -12,7 +20,7 @@ struct VertexOutput {
[[stage(vertex)]]
fn main(model: VertexInput) -> VertexOutput {
var out: VertexOutput;
out.clip_coordinate = vec4<f32>(model.position, 1.0);
out.clip_coordinate = uniform.view_projection * vec4<f32>(model.position, 1.0);
out.texture_coords = model.texture_coords;
return out;
}

View File

@@ -7,8 +7,6 @@ use winit::{
window::Window,
};
use crate::{texture, vertex::Vertex};
/// Hold state with important information
pub struct State {
surface: wgpu::Surface,
@@ -19,7 +17,6 @@ pub struct State {
swap_chain: wgpu::SwapChain,
pub size: winit::dpi::PhysicalSize<u32>,
render_pipeline: wgpu::RenderPipeline,
vertex_buffer: wgpu::Buffer,
@@ -27,7 +24,14 @@ pub struct State {
num_indices: u32,
aqua_bind_group: wgpu::BindGroup,
aqua_texture: texture::Texture,
aqua_texture: crate::texture::Texture,
camera: crate::camera::Camera,
camera_controller: crate::camera_controller::CameraController,
uniform: crate::uniform::Uniform,
uniform_buffer: wgpu::Buffer,
uniform_bind_group: wgpu::BindGroup,
}
impl State {
@@ -79,7 +83,7 @@ impl State {
let swap_chain = device.create_swap_chain(&surface, &sc_desc);
let aqua_bytes = include_bytes!("../img/aqua.png");
let aqua_texture = texture::Texture::from_bytes(&device, &queue, aqua_bytes, "aqua").unwrap();
let aqua_texture = crate::texture::Texture::from_bytes(&device, &queue, aqua_bytes, "aqua").unwrap();
let texture_bind_group_layout = device.create_bind_group_layout(
&wgpu::BindGroupLayoutDescriptor {
@@ -127,6 +131,61 @@ impl State {
}
);
let camera = crate::camera::Camera {
// x, y, z
// 1 up, 2 back
// +z is out of the screen
eye: (0.0, 1.0, 2.0).into(),
// look at the center
target: (0.0, 0.0, 0.0).into(),
// which way is up
up: cgmath::Vector3::unit_y(),
aspect: size.width as f32 / size.height as f32,
fovy: 45.0,
znear: 0.1,
zfar: 100.0,
};
let camera_controller = crate::camera_controller::CameraController::new(0.05);
let mut uniform = crate::uniform::Uniform::new();
uniform.update_view_proj(&camera);
let uniform_buffer = device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
label: Some("Uniform Buffer"),
contents: bytemuck::cast_slice(&[uniform]),
usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST,
}
);
let uniform_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStage::VERTEX,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
}
],
label: Some("uniform_bind_group_layout"),
});
let uniform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &uniform_bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: uniform_buffer.as_entire_binding(),
}
],
label: Some("uniform_bind_group"),
});
// load shader file
let shader = device.create_shader_module(
&wgpu::ShaderModuleDescriptor {
@@ -139,7 +198,10 @@ impl State {
let render_pipeline_layout = device.create_pipeline_layout(
&wgpu::PipelineLayoutDescriptor {
label: Some("Render Pipeline Layput"),
bind_group_layouts: &[&texture_bind_group_layout],
bind_group_layouts: &[
&texture_bind_group_layout,
&uniform_bind_group_layout,
],
push_constant_ranges: &[],
}
);
@@ -154,7 +216,7 @@ impl State {
// function name in shader.wgsl for [[stage(vertex)]]
entry_point: "main",
// specify memory layout
buffers: &[Vertex::desc()],
buffers: &[crate::vertex::Vertex::desc()],
},
// needed to sotre color data to swap_chain
@@ -212,15 +274,26 @@ impl State {
surface,
device,
queue,
sc_desc, // saved, so we can create a new swap_chain later
swap_chain,
size,
render_pipeline,
vertex_buffer,
index_buffer,
num_indices,
aqua_bind_group,
aqua_texture,
camera,
camera_controller,
uniform,
uniform_buffer,
uniform_bind_group,
}
}
@@ -254,12 +327,18 @@ impl State {
// self.use_pentagon = !self.use_pentagon;
// true
// }
_ => false
_ => self.camera_controller.process_events(event)
}
}
/// TODO
/// Update State before render()
pub fn update(&mut self) {
// reposition camera
self.camera_controller.update_camera(&mut self.camera);
// update projection for uniform buffer
self.uniform.update_view_proj(&self.camera);
// write uniform buffer to queue
self.queue.write_buffer(&self.uniform_buffer, 0, bytemuck::cast_slice(&[self.uniform]));
}
/// Generate commands for gpu to render to frame
@@ -296,6 +375,7 @@ impl State {
render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
render_pass.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint16);
render_pass.set_bind_group(0, &self.aqua_bind_group, &[]);
render_pass.set_bind_group(1, &self.uniform_bind_group, &[]);
// draw triangle
render_pass.draw_indexed(0..self.num_indices, 0, 0..1);
//render_pass.draw(0..self.num_vertices, 0..1);

18
src/uniform.rs Normal file
View File

@@ -0,0 +1,18 @@
#[repr(C)]
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
pub struct Uniform {
view_projection: [[f32; 4]; 4],
}
impl Uniform {
pub fn new() -> Self {
use cgmath::SquareMatrix;
Self {
view_projection: cgmath::Matrix4::identity().into(),
}
}
pub fn update_view_proj(&mut self, camera: &crate::camera::Camera) {
self.view_projection = camera.build_view_projection_matrix().into();
}
}