Add simple camera movement
This commit is contained in:
@@ -14,4 +14,4 @@ log = "0.4"
|
|||||||
wgpu = "0.9"
|
wgpu = "0.9"
|
||||||
pollster = "0.2"
|
pollster = "0.2"
|
||||||
bytemuck = { version = "1.4", features = [ "derive" ] }
|
bytemuck = { version = "1.4", features = [ "derive" ] }
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
|
|||||||
26
src/camera.rs
Normal file
26
src/camera.rs
Normal 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
96
src/camera_controller.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,9 @@ use winit::{
|
|||||||
mod state;
|
mod state;
|
||||||
mod vertex;
|
mod vertex;
|
||||||
mod texture;
|
mod texture;
|
||||||
|
mod camera;
|
||||||
|
mod uniform;
|
||||||
|
mod camera_controller;
|
||||||
|
|
||||||
use crate::state::State;
|
use crate::state::State;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,12 @@
|
|||||||
// Vertex shader
|
// Vertex shader
|
||||||
|
[[block]]
|
||||||
|
struct Uniform {
|
||||||
|
view_projection: mat4x4<f32>;
|
||||||
|
};
|
||||||
|
|
||||||
|
[[group(1), binding(0)]]
|
||||||
|
var<uniform> uniform: Uniform;
|
||||||
|
|
||||||
struct VertexInput {
|
struct VertexInput {
|
||||||
[[location(0)]] position: vec3<f32>;
|
[[location(0)]] position: vec3<f32>;
|
||||||
[[location(1)]] texture_coords: vec2<f32>;
|
[[location(1)]] texture_coords: vec2<f32>;
|
||||||
@@ -12,7 +20,7 @@ struct VertexOutput {
|
|||||||
[[stage(vertex)]]
|
[[stage(vertex)]]
|
||||||
fn main(model: VertexInput) -> VertexOutput {
|
fn main(model: VertexInput) -> VertexOutput {
|
||||||
var out: 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;
|
out.texture_coords = model.texture_coords;
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|||||||
98
src/state.rs
98
src/state.rs
@@ -7,8 +7,6 @@ use winit::{
|
|||||||
window::Window,
|
window::Window,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{texture, vertex::Vertex};
|
|
||||||
|
|
||||||
/// Hold state with important information
|
/// Hold state with important information
|
||||||
pub struct State {
|
pub struct State {
|
||||||
surface: wgpu::Surface,
|
surface: wgpu::Surface,
|
||||||
@@ -19,7 +17,6 @@ pub struct State {
|
|||||||
swap_chain: wgpu::SwapChain,
|
swap_chain: wgpu::SwapChain,
|
||||||
pub size: winit::dpi::PhysicalSize<u32>,
|
pub size: winit::dpi::PhysicalSize<u32>,
|
||||||
|
|
||||||
|
|
||||||
render_pipeline: wgpu::RenderPipeline,
|
render_pipeline: wgpu::RenderPipeline,
|
||||||
|
|
||||||
vertex_buffer: wgpu::Buffer,
|
vertex_buffer: wgpu::Buffer,
|
||||||
@@ -27,7 +24,14 @@ pub struct State {
|
|||||||
num_indices: u32,
|
num_indices: u32,
|
||||||
|
|
||||||
aqua_bind_group: wgpu::BindGroup,
|
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 {
|
impl State {
|
||||||
@@ -79,7 +83,7 @@ impl State {
|
|||||||
let swap_chain = device.create_swap_chain(&surface, &sc_desc);
|
let swap_chain = device.create_swap_chain(&surface, &sc_desc);
|
||||||
|
|
||||||
let aqua_bytes = include_bytes!("../img/aqua.png");
|
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(
|
let texture_bind_group_layout = device.create_bind_group_layout(
|
||||||
&wgpu::BindGroupLayoutDescriptor {
|
&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
|
// load shader file
|
||||||
let shader = device.create_shader_module(
|
let shader = device.create_shader_module(
|
||||||
&wgpu::ShaderModuleDescriptor {
|
&wgpu::ShaderModuleDescriptor {
|
||||||
@@ -139,7 +198,10 @@ impl State {
|
|||||||
let render_pipeline_layout = device.create_pipeline_layout(
|
let render_pipeline_layout = device.create_pipeline_layout(
|
||||||
&wgpu::PipelineLayoutDescriptor {
|
&wgpu::PipelineLayoutDescriptor {
|
||||||
label: Some("Render Pipeline Layput"),
|
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: &[],
|
push_constant_ranges: &[],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -154,7 +216,7 @@ impl State {
|
|||||||
// function name in shader.wgsl for [[stage(vertex)]]
|
// function name in shader.wgsl for [[stage(vertex)]]
|
||||||
entry_point: "main",
|
entry_point: "main",
|
||||||
// specify memory layout
|
// specify memory layout
|
||||||
buffers: &[Vertex::desc()],
|
buffers: &[crate::vertex::Vertex::desc()],
|
||||||
|
|
||||||
},
|
},
|
||||||
// needed to sotre color data to swap_chain
|
// needed to sotre color data to swap_chain
|
||||||
@@ -212,15 +274,26 @@ impl State {
|
|||||||
surface,
|
surface,
|
||||||
device,
|
device,
|
||||||
queue,
|
queue,
|
||||||
|
|
||||||
sc_desc, // saved, so we can create a new swap_chain later
|
sc_desc, // saved, so we can create a new swap_chain later
|
||||||
swap_chain,
|
swap_chain,
|
||||||
size,
|
size,
|
||||||
|
|
||||||
render_pipeline,
|
render_pipeline,
|
||||||
|
|
||||||
vertex_buffer,
|
vertex_buffer,
|
||||||
index_buffer,
|
index_buffer,
|
||||||
num_indices,
|
num_indices,
|
||||||
|
|
||||||
aqua_bind_group,
|
aqua_bind_group,
|
||||||
aqua_texture,
|
aqua_texture,
|
||||||
|
|
||||||
|
camera,
|
||||||
|
camera_controller,
|
||||||
|
|
||||||
|
uniform,
|
||||||
|
uniform_buffer,
|
||||||
|
uniform_bind_group,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,12 +327,18 @@ impl State {
|
|||||||
// self.use_pentagon = !self.use_pentagon;
|
// self.use_pentagon = !self.use_pentagon;
|
||||||
// true
|
// true
|
||||||
// }
|
// }
|
||||||
_ => false
|
_ => self.camera_controller.process_events(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TODO
|
/// Update State before render()
|
||||||
pub fn update(&mut self) {
|
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
|
/// 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_vertex_buffer(0, self.vertex_buffer.slice(..));
|
||||||
render_pass.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint16);
|
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(0, &self.aqua_bind_group, &[]);
|
||||||
|
render_pass.set_bind_group(1, &self.uniform_bind_group, &[]);
|
||||||
// draw triangle
|
// draw triangle
|
||||||
render_pass.draw_indexed(0..self.num_indices, 0, 0..1);
|
render_pass.draw_indexed(0..self.num_indices, 0, 0..1);
|
||||||
//render_pass.draw(0..self.num_vertices, 0..1);
|
//render_pass.draw(0..self.num_vertices, 0..1);
|
||||||
|
|||||||
18
src/uniform.rs
Normal file
18
src/uniform.rs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user