226 lines
6.6 KiB
Rust
226 lines
6.6 KiB
Rust
#![feature(duration_constants)]
|
|
#![allow(clippy::new_without_default, clippy::let_unit_value)]
|
|
|
|
use std::{process::ExitCode, sync::Arc, time::Instant};
|
|
|
|
use error::Error;
|
|
use glam::Vec3;
|
|
use input::InputState;
|
|
use render::{
|
|
asset::{AssetLoader, TextureLoadStage},
|
|
Renderer,
|
|
};
|
|
use winit::{
|
|
application::ApplicationHandler,
|
|
dpi::PhysicalSize,
|
|
event::{DeviceEvent, DeviceId, StartCause, WindowEvent},
|
|
event_loop::{ActiveEventLoop, EventLoop},
|
|
window::{CursorGrabMode, Window, WindowId},
|
|
};
|
|
use world::{camera::Camera, ChunkGenerator, NoiseChunkGenerator, World};
|
|
|
|
pub mod error;
|
|
pub mod input;
|
|
pub mod render;
|
|
pub mod world;
|
|
|
|
pub struct Engine {
|
|
window: Option<Arc<Window>>,
|
|
renderer: Option<Renderer>,
|
|
assets: Option<AssetLoader<TextureLoadStage>>,
|
|
world: Option<World>,
|
|
input: InputState,
|
|
camera: Camera,
|
|
generator: Box<dyn ChunkGenerator>,
|
|
last_frame: Instant,
|
|
}
|
|
|
|
impl Engine {
|
|
pub fn ensure_renderer(&mut self, event_loop: &ActiveEventLoop) -> Result<(), Error> {
|
|
if self.window.is_none() {
|
|
let assets = self.assets.take().expect("asset loader");
|
|
let window_attributes = Window::default_attributes()
|
|
.with_resizable(false)
|
|
.with_inner_size(PhysicalSize::new(800, 600));
|
|
|
|
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)?;
|
|
|
|
self.window = Some(window);
|
|
self.renderer = Some(render);
|
|
}
|
|
|
|
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 {
|
|
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
|
if let Err(error) = self.ensure_renderer(event_loop) {
|
|
log::error!("Renderer setup error: {error}");
|
|
event_loop.exit();
|
|
}
|
|
}
|
|
|
|
fn exiting(&mut self, event_loop: &ActiveEventLoop) {
|
|
let _ = event_loop;
|
|
log::info!("Exiting");
|
|
self.renderer = None;
|
|
}
|
|
|
|
fn suspended(&mut self, event_loop: &ActiveEventLoop) {
|
|
let _ = event_loop;
|
|
}
|
|
|
|
fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) {
|
|
let _ = event_loop;
|
|
let _ = cause;
|
|
}
|
|
|
|
fn user_event(&mut self, event_loop: &ActiveEventLoop, event: ()) {
|
|
let _ = event_loop;
|
|
let _ = event;
|
|
}
|
|
|
|
fn window_event(
|
|
&mut self,
|
|
event_loop: &ActiveEventLoop,
|
|
window_id: WindowId,
|
|
event: WindowEvent,
|
|
) {
|
|
let _ = window_id;
|
|
|
|
match event {
|
|
WindowEvent::CloseRequested => {
|
|
log::info!("Close requested");
|
|
event_loop.exit();
|
|
}
|
|
WindowEvent::Resized(size) => {
|
|
log::info!("Window resized: {size:?}");
|
|
if let Some(render) = self.renderer.as_mut() {
|
|
render.set_swapchain_dirty();
|
|
}
|
|
}
|
|
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()) {
|
|
let camera_position = self.camera.position();
|
|
|
|
world.update_with_camera(camera_position, &mut *self.generator);
|
|
|
|
if let Err(error) = render.render(&mut self.camera, world) {
|
|
log::error!("Render error: {error}");
|
|
event_loop.exit();
|
|
}
|
|
}
|
|
}
|
|
WindowEvent::KeyboardInput { event, .. } => self.input.key_event(event),
|
|
_ => (),
|
|
}
|
|
}
|
|
|
|
fn device_event(
|
|
&mut self,
|
|
event_loop: &ActiveEventLoop,
|
|
device_id: DeviceId,
|
|
event: DeviceEvent,
|
|
) {
|
|
let _ = event_loop;
|
|
let _ = device_id;
|
|
|
|
if let DeviceEvent::MouseMotion { delta } = event {
|
|
self.input.add_mouse_motion(delta.0, delta.1);
|
|
}
|
|
}
|
|
|
|
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
|
|
let _ = event_loop;
|
|
|
|
if let Some(window) = self.window.as_ref() {
|
|
window.request_redraw();
|
|
}
|
|
}
|
|
|
|
fn memory_warning(&mut self, event_loop: &ActiveEventLoop) {
|
|
let _ = event_loop;
|
|
}
|
|
}
|
|
|
|
fn run() -> Result<(), Error> {
|
|
let loader = AssetLoader::new();
|
|
let (loader, block_registry) = loader.load_block_definitions()?;
|
|
let world = World::new(block_registry);
|
|
|
|
let event_loop = EventLoop::new()?;
|
|
let mut engine = Engine {
|
|
window: None,
|
|
renderer: None,
|
|
assets: Some(loader),
|
|
world: Some(world),
|
|
camera: Camera::new(),
|
|
input: InputState::new(),
|
|
generator: Box::new(NoiseChunkGenerator::from_seed(1234)),
|
|
last_frame: Instant::now(),
|
|
};
|
|
|
|
event_loop.run_app(&mut engine)?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn main() -> ExitCode {
|
|
env_logger::init();
|
|
match run() {
|
|
Ok(()) => ExitCode::SUCCESS,
|
|
Err(error) => {
|
|
log::error!("Exited with error: {error}");
|
|
ExitCode::FAILURE
|
|
}
|
|
}
|
|
}
|
|
|