Add camera movement

This commit is contained in:
2025-04-28 09:21:27 +03:00
parent 3d8e7e23a6
commit d75c274a09
5 changed files with 192 additions and 27 deletions
+54
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+62
View File
@@ -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
View File
@@ -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;