Add camera movement
This commit is contained in:
@@ -0,0 +1,54 @@
|
|||||||
|
use std::mem;
|
||||||
|
|
||||||
|
use winit::{
|
||||||
|
event::{ElementState, KeyEvent},
|
||||||
|
keyboard::{KeyCode, PhysicalKey},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct InputState {
|
||||||
|
mouse_delta_x: f64,
|
||||||
|
mouse_delta_y: f64,
|
||||||
|
|
||||||
|
pub forward: bool,
|
||||||
|
pub backwards: bool,
|
||||||
|
pub left: bool,
|
||||||
|
pub right: bool,
|
||||||
|
pub up: bool,
|
||||||
|
pub down: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InputState {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_mouse_motion(&mut self, dx: f64, dy: f64) {
|
||||||
|
self.mouse_delta_x += dx;
|
||||||
|
self.mouse_delta_y += dy;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mouse_delta(&mut self) -> (f64, f64) {
|
||||||
|
let dx = mem::replace(&mut self.mouse_delta_x, 0.0);
|
||||||
|
let dy = mem::replace(&mut self.mouse_delta_y, 0.0);
|
||||||
|
(dx, dy)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn key_event(&mut self, event: KeyEvent) {
|
||||||
|
let PhysicalKey::Code(key) = event.physical_key else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let pressed = event.state == ElementState::Pressed;
|
||||||
|
|
||||||
|
match key {
|
||||||
|
KeyCode::KeyW => self.forward = pressed,
|
||||||
|
KeyCode::KeyS => self.backwards = pressed,
|
||||||
|
KeyCode::KeyA => self.left = pressed,
|
||||||
|
KeyCode::KeyD => self.right = pressed,
|
||||||
|
KeyCode::Space => self.up = pressed,
|
||||||
|
KeyCode::ShiftLeft => self.down = pressed,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
+63
-17
@@ -5,6 +5,7 @@ use std::{process::ExitCode, sync::Arc, time::Instant};
|
|||||||
|
|
||||||
use error::Error;
|
use error::Error;
|
||||||
use glam::Vec3;
|
use glam::Vec3;
|
||||||
|
use input::InputState;
|
||||||
use render::{
|
use render::{
|
||||||
asset::{AssetLoader, TextureLoadStage},
|
asset::{AssetLoader, TextureLoadStage},
|
||||||
Renderer,
|
Renderer,
|
||||||
@@ -14,11 +15,12 @@ use winit::{
|
|||||||
dpi::PhysicalSize,
|
dpi::PhysicalSize,
|
||||||
event::{DeviceEvent, DeviceId, StartCause, WindowEvent},
|
event::{DeviceEvent, DeviceId, StartCause, WindowEvent},
|
||||||
event_loop::{ActiveEventLoop, EventLoop},
|
event_loop::{ActiveEventLoop, EventLoop},
|
||||||
window::{Window, WindowId},
|
window::{CursorGrabMode, Window, WindowId},
|
||||||
};
|
};
|
||||||
use world::{Chunk, ChunkGenerator, NoiseChunkGenerator, World};
|
use world::{camera::Camera, ChunkGenerator, NoiseChunkGenerator, World};
|
||||||
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
pub mod input;
|
||||||
pub mod render;
|
pub mod render;
|
||||||
pub mod world;
|
pub mod world;
|
||||||
|
|
||||||
@@ -27,9 +29,10 @@ pub struct Engine {
|
|||||||
renderer: Option<Renderer>,
|
renderer: Option<Renderer>,
|
||||||
assets: Option<AssetLoader<TextureLoadStage>>,
|
assets: Option<AssetLoader<TextureLoadStage>>,
|
||||||
world: Option<World>,
|
world: Option<World>,
|
||||||
camera_position: Vec3,
|
input: InputState,
|
||||||
|
camera: Camera,
|
||||||
generator: Box<dyn ChunkGenerator>,
|
generator: Box<dyn ChunkGenerator>,
|
||||||
start: Instant,
|
last_frame: Instant,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Engine {
|
impl Engine {
|
||||||
@@ -41,6 +44,10 @@ impl Engine {
|
|||||||
.with_inner_size(PhysicalSize::new(800, 600));
|
.with_inner_size(PhysicalSize::new(800, 600));
|
||||||
|
|
||||||
let window = Arc::new(event_loop.create_window(window_attributes)?);
|
let window = Arc::new(event_loop.create_window(window_attributes)?);
|
||||||
|
|
||||||
|
window.set_cursor_grab(CursorGrabMode::Locked).ok();
|
||||||
|
window.set_cursor_visible(false);
|
||||||
|
|
||||||
let render = Renderer::from_window(window.clone(), assets)?;
|
let render = Renderer::from_window(window.clone(), assets)?;
|
||||||
|
|
||||||
self.window = Some(window);
|
self.window = Some(window);
|
||||||
@@ -49,6 +56,42 @@ impl Engine {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update_camera(&mut self, dt: f64) {
|
||||||
|
const SENSITIVITY: f64 = 0.15;
|
||||||
|
const HORIZONTAL_SPEED: f32 = 17.5;
|
||||||
|
const VERTICAL_SPEED: f32 = 15.0;
|
||||||
|
|
||||||
|
let (mouse_dx, mouse_dy) = self.input.mouse_delta();
|
||||||
|
let mouse_dx = mouse_dx * dt * SENSITIVITY;
|
||||||
|
let mouse_dy = mouse_dy * dt * SENSITIVITY;
|
||||||
|
|
||||||
|
self.camera.turn(mouse_dy as f32, mouse_dx as f32);
|
||||||
|
|
||||||
|
let want_dz = self.input.forward as i32 - self.input.backwards as i32;
|
||||||
|
let want_dx = self.input.right as i32 - self.input.left as i32;
|
||||||
|
let want_dy = self.input.up as i32 - self.input.down as i32;
|
||||||
|
|
||||||
|
if want_dx == 0 && want_dy == 0 && want_dz == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let want_horizontal_vector = if want_dx != 0 || want_dz != 0 {
|
||||||
|
let look_vector = self.camera.horizontal_look_vector();
|
||||||
|
let right_vector = look_vector.cross(Vec3::Y);
|
||||||
|
((want_dx as f32) * right_vector + (want_dz as f32) * look_vector).normalize()
|
||||||
|
} else {
|
||||||
|
Vec3::ZERO
|
||||||
|
};
|
||||||
|
|
||||||
|
let want_vertical_vector = (want_dy as f32) * Vec3::Y;
|
||||||
|
|
||||||
|
let horizontal_vector = want_horizontal_vector * dt as f32 * HORIZONTAL_SPEED;
|
||||||
|
let vertical_vector = want_vertical_vector * dt as f32 * VERTICAL_SPEED;
|
||||||
|
|
||||||
|
self.camera
|
||||||
|
.move_towards(horizontal_vector + vertical_vector);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ApplicationHandler for Engine {
|
impl ApplicationHandler for Engine {
|
||||||
@@ -99,24 +142,23 @@ impl ApplicationHandler for Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
WindowEvent::RedrawRequested => {
|
WindowEvent::RedrawRequested => {
|
||||||
|
let frame_delta = self.last_frame.elapsed();
|
||||||
|
self.last_frame = Instant::now();
|
||||||
|
let frame_dt = frame_delta.as_secs_f64();
|
||||||
|
self.update_camera(frame_dt);
|
||||||
|
|
||||||
if let (Some(render), Some(world)) = (self.renderer.as_mut(), self.world.as_mut()) {
|
if let (Some(render), Some(world)) = (self.renderer.as_mut(), self.world.as_mut()) {
|
||||||
let delta = self.start.elapsed();
|
let camera_position = self.camera.position();
|
||||||
let dt = delta.as_secs_f64();
|
|
||||||
|
|
||||||
self.camera_position = Vec3::new(
|
world.update_with_camera(camera_position, &mut *self.generator);
|
||||||
(dt.cos() * 32.0) as f32 + (Chunk::SIZE / 2) as f32,
|
|
||||||
48.0,
|
|
||||||
(dt.sin() * 32.0) as f32 + (Chunk::SIZE / 2) as f32,
|
|
||||||
);
|
|
||||||
|
|
||||||
world.update_with_camera(self.camera_position, &mut *self.generator);
|
if let Err(error) = render.render(&mut self.camera, world) {
|
||||||
|
|
||||||
if let Err(error) = render.render(self.camera_position, world) {
|
|
||||||
log::error!("Render error: {error}");
|
log::error!("Render error: {error}");
|
||||||
event_loop.exit();
|
event_loop.exit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
WindowEvent::KeyboardInput { event, .. } => self.input.key_event(event),
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,7 +171,10 @@ impl ApplicationHandler for Engine {
|
|||||||
) {
|
) {
|
||||||
let _ = event_loop;
|
let _ = event_loop;
|
||||||
let _ = device_id;
|
let _ = device_id;
|
||||||
let _ = event;
|
|
||||||
|
if let DeviceEvent::MouseMotion { delta } = event {
|
||||||
|
self.input.add_mouse_motion(delta.0, delta.1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
|
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
|
||||||
@@ -156,9 +201,10 @@ fn run() -> Result<(), Error> {
|
|||||||
renderer: None,
|
renderer: None,
|
||||||
assets: Some(loader),
|
assets: Some(loader),
|
||||||
world: Some(world),
|
world: Some(world),
|
||||||
camera_position: Vec3::ZERO,
|
camera: Camera::new(),
|
||||||
|
input: InputState::new(),
|
||||||
generator: Box::new(NoiseChunkGenerator::from_seed(1234)),
|
generator: Box::new(NoiseChunkGenerator::from_seed(1234)),
|
||||||
start: Instant::now(),
|
last_frame: Instant::now(),
|
||||||
};
|
};
|
||||||
|
|
||||||
event_loop.run_app(&mut engine)?;
|
event_loop.run_app(&mut engine)?;
|
||||||
|
|||||||
+12
-10
@@ -4,7 +4,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use asset::{AssetLoader, TextureLoadStage};
|
use asset::{AssetLoader, TextureLoadStage};
|
||||||
use glam::{Mat4, Vec3};
|
use glam::Mat4;
|
||||||
use mesh::WorldMeshState;
|
use mesh::WorldMeshState;
|
||||||
use util::{Allocators, ViewportExt};
|
use util::{Allocators, ViewportExt};
|
||||||
use vertex::InputVertex;
|
use vertex::InputVertex;
|
||||||
@@ -38,7 +38,7 @@ use winit::window::Window;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::Error,
|
error::Error,
|
||||||
world::{Chunk, ChunkCoords, World},
|
world::{camera::Camera, Chunk, ChunkCoords, World},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod asset;
|
pub mod asset;
|
||||||
@@ -241,7 +241,7 @@ impl Renderer {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(&mut self, camera_position: Vec3, world: &mut World) -> Result<(), Error> {
|
pub fn render(&mut self, camera: &mut Camera, world: &mut World) -> Result<(), Error> {
|
||||||
self.fps.new_frame();
|
self.fps.new_frame();
|
||||||
self.fps.update();
|
self.fps.update();
|
||||||
|
|
||||||
@@ -270,15 +270,16 @@ impl Renderer {
|
|||||||
|
|
||||||
let model = Mat4::IDENTITY;
|
let model = Mat4::IDENTITY;
|
||||||
let projection = Mat4::perspective_rh_gl(45.0f32.to_radians(), aspect, 0.01, 1000.0);
|
let projection = Mat4::perspective_rh_gl(45.0f32.to_radians(), aspect, 0.01, 1000.0);
|
||||||
|
let view = camera.transform();
|
||||||
|
|
||||||
fn make_view(camera: Vec3, target: Vec3) -> Mat4 {
|
// fn make_view(camera: Vec3, target: Vec3) -> Mat4 {
|
||||||
Mat4::look_at_rh(camera, target, Vec3::Y)
|
// Mat4::look_at_rh(camera, target, Vec3::Y)
|
||||||
}
|
// }
|
||||||
|
|
||||||
let view = make_view(
|
// let view = make_view(
|
||||||
camera_position,
|
// camera_position,
|
||||||
Vec3::new((Chunk::SIZE / 2) as f32, 32.0, (Chunk::SIZE / 2) as f32),
|
// Vec3::new((Chunk::SIZE / 2) as f32, 32.0, (Chunk::SIZE / 2) as f32),
|
||||||
);
|
// );
|
||||||
|
|
||||||
let set0 = {
|
let set0 = {
|
||||||
let uniform_buffer = self
|
let uniform_buffer = self
|
||||||
@@ -352,6 +353,7 @@ impl Renderer {
|
|||||||
(x as i32 + Chunk::SIZE as i32 / 2) / Chunk::SIZE as i32
|
(x as i32 + Chunk::SIZE as i32 / 2) / Chunk::SIZE as i32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let camera_position = camera.position();
|
||||||
let camera_cx = cpos(camera_position.x);
|
let camera_cx = cpos(camera_position.x);
|
||||||
let camera_cz = cpos(camera_position.z);
|
let camera_cz = cpos(camera_position.z);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
use glam::{EulerRot, Mat4, Vec3, Vec4, Vec4Swizzles};
|
||||||
|
|
||||||
|
pub struct Camera {
|
||||||
|
position: Vec3,
|
||||||
|
yaw: f32,
|
||||||
|
pitch: f32,
|
||||||
|
|
||||||
|
transform: Mat4,
|
||||||
|
dirty: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Camera {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
position: Vec3::ZERO,
|
||||||
|
yaw: 0.0,
|
||||||
|
pitch: 0.0,
|
||||||
|
transform: Mat4::IDENTITY,
|
||||||
|
dirty: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn position(&self) -> Vec3 {
|
||||||
|
self.position
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn move_to(&mut self, position: Vec3) {
|
||||||
|
self.position = position;
|
||||||
|
self.dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn move_towards(&mut self, delta: Vec3) {
|
||||||
|
self.position += delta;
|
||||||
|
self.dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn turn(&mut self, pitch: f32, yaw: f32) {
|
||||||
|
self.pitch += pitch;
|
||||||
|
self.yaw += yaw;
|
||||||
|
self.dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn horizontal_look_vector(&self) -> Vec3 {
|
||||||
|
Vec3::new(self.yaw.cos(), 0.0, self.yaw.sin())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn look_vector(&self) -> Vec3 {
|
||||||
|
(Mat4::from_euler(EulerRot::XYZ, 0.0, -self.yaw, -self.pitch)
|
||||||
|
* Vec4::new(1.0, 0.0, 0.0, 0.0))
|
||||||
|
.xyz()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn transform(&mut self) -> &Mat4 {
|
||||||
|
if self.dirty {
|
||||||
|
let look_vector = self.look_vector();
|
||||||
|
self.transform = Mat4::look_to_rh(self.position, look_vector, Vec3::Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
&self.transform
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
|
|
||||||
|
pub mod camera;
|
||||||
pub mod chunk;
|
pub mod chunk;
|
||||||
pub mod feature;
|
pub mod feature;
|
||||||
pub mod generator;
|
pub mod generator;
|
||||||
|
|||||||
Reference in New Issue
Block a user