145 lines
4.5 KiB
Rust
145 lines
4.5 KiB
Rust
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
//! Main entry point for the Project Catalyst client.
|
|
//!
|
|
//! This crate handles window creation, input processing, and drives the
|
|
//! renderer to display the game world.
|
|
mod meshing;
|
|
|
|
use anyhow::{Context, Result};
|
|
use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
|
|
use tracing::{error, info};
|
|
use winit::application::ApplicationHandler;
|
|
use winit::event::WindowEvent;
|
|
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
|
|
use winit::window::{Window, WindowId};
|
|
|
|
#[derive(Default)]
|
|
struct App {
|
|
renderer: Option<renderer::Renderer>,
|
|
window: Option<Window>,
|
|
}
|
|
|
|
impl ApplicationHandler for App {
|
|
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
|
let attributes = Window::default_attributes().with_title("Project Catalyst");
|
|
|
|
let window = match event_loop.create_window(attributes) {
|
|
Ok(w) => w,
|
|
Err(e) => {
|
|
error!("Failed to create window: {e}");
|
|
event_loop.exit();
|
|
return;
|
|
}
|
|
};
|
|
|
|
let display_handle = match event_loop.display_handle() {
|
|
Ok(h) => h.as_raw(),
|
|
Err(e) => {
|
|
error!("Failed to get display handle: {e}");
|
|
event_loop.exit();
|
|
return;
|
|
}
|
|
};
|
|
|
|
let window_handle = match window.window_handle() {
|
|
Ok(h) => h.as_raw(),
|
|
Err(e) => {
|
|
error!("Failed to get window handle: {e}");
|
|
event_loop.exit();
|
|
return;
|
|
}
|
|
};
|
|
|
|
let required_extensions = match ash_window::enumerate_required_extensions(display_handle) {
|
|
Ok(exts) => exts,
|
|
Err(e) => {
|
|
error!("Failed to enumerate required extensions: {e}");
|
|
event_loop.exit();
|
|
return;
|
|
}
|
|
};
|
|
|
|
let size = window.inner_size();
|
|
let renderer = match renderer::Renderer::new(
|
|
display_handle,
|
|
window_handle,
|
|
size.width,
|
|
size.height,
|
|
required_extensions,
|
|
) {
|
|
Ok(r) => r,
|
|
Err(e) => {
|
|
error!("Failed to initialize Vulkan renderer: {e}");
|
|
event_loop.exit();
|
|
return;
|
|
}
|
|
};
|
|
|
|
self.window = Some(window);
|
|
self.renderer = Some(renderer);
|
|
|
|
#[allow(clippy::expect_used)]
|
|
let config_str = std::fs::read_to_string("assets/data/worldgen/default.json")
|
|
.expect("Failed to read worldgen config");
|
|
#[allow(clippy::expect_used)]
|
|
let worldgen_config: shared::generator::WorldGenConfig =
|
|
serde_json::from_str(&config_str).expect("Failed to parse worldgen config");
|
|
|
|
let seed = 4_813_530;
|
|
|
|
let generator = shared::generator::VoxelGenerator::new(worldgen_config, seed);
|
|
let chunk = generator.generate_chunk(shared::world::ChunkPos::new(0, 0, 0));
|
|
|
|
let (vertices, indices) = meshing::generate_mesh(&chunk);
|
|
tracing::info!(
|
|
"Generated Mesh with {} vertices and {} indices!",
|
|
vertices.len(),
|
|
indices.len()
|
|
);
|
|
|
|
#[allow(clippy::expect_used)]
|
|
self.renderer
|
|
.as_mut()
|
|
.expect("Renderer initialized")
|
|
.update_mesh(&vertices, &indices)
|
|
.expect("Failed to upload terrain to GPU");
|
|
}
|
|
|
|
fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) {
|
|
match event {
|
|
WindowEvent::CloseRequested => {
|
|
event_loop.exit();
|
|
}
|
|
WindowEvent::RedrawRequested => {
|
|
if let Some(Err(e)) = self.renderer.as_mut().map(renderer::Renderer::draw_frame) {
|
|
error!("Failed to draw frame: {e}");
|
|
event_loop.exit();
|
|
}
|
|
|
|
if let Some(window) = self.window.as_ref() {
|
|
window.request_redraw();
|
|
}
|
|
}
|
|
_ => (),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn main() -> Result<()> {
|
|
tracing_subscriber::fmt()
|
|
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
|
|
.init();
|
|
info!("Starting Project Catalyst client");
|
|
|
|
let event_loop = EventLoop::new().context("Failed to create event loop")?;
|
|
|
|
event_loop.set_control_flow(ControlFlow::Poll);
|
|
|
|
let mut app = App::default();
|
|
event_loop
|
|
.run_app(&mut app)
|
|
.context("Failed to run event loop")?;
|
|
Ok(())
|
|
}
|