zenyx-engine/engine/src/core/render/mod.rs
2025-04-21 20:13:17 -04:00

420 lines
13 KiB
Rust

use std::collections::HashMap;
use std::ops::Deref;
use std::sync::Arc;
use ctx::{Renderer, Vertex};
use tobj::LoadOptions;
use tracing::{debug, error, info, trace, warn};
use wgpu::rwh::HasWindowHandle;
use winit::application::ApplicationHandler;
use winit::event::{KeyEvent, WindowEvent};
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
use winit::window::Window;
use winit::window::WindowId;
pub mod ctx;
struct WindowContext<'window> {
window: Arc<Window>,
ctx: Renderer<'window>,
main_window: bool,
}
impl Deref for WindowContext<'_> {
type Target = winit::window::Window;
fn deref(&self) -> &Self::Target {
self.window.as_ref()
}
}
impl WindowContext<'_> {
pub fn is_main_window(&self) -> bool {
self.main_window
}
}
#[derive(Default)]
pub struct App<'window> {
windows: HashMap<WindowId, WindowContext<'window>>,
}
static CUBE_VERTICES: &[Vertex] = &[
Vertex {
position: [-0.5, -0.5, 0.5],
normal: [0.0, 0.0, 1.0],
},
Vertex {
position: [0.5, -0.5, 0.5],
normal: [0.0, 0.0, 1.0],
},
Vertex {
position: [0.5, 0.5, 0.5],
normal: [0.0, 0.0, 1.0],
},
Vertex {
position: [0.5, 0.5, 0.5],
normal: [0.0, 0.0, 1.0],
},
Vertex {
position: [-0.5, 0.5, 0.5],
normal: [0.0, 0.0, 1.0],
},
Vertex {
position: [-0.5, -0.5, 0.5],
normal: [0.0, 0.0, 1.0],
},
Vertex {
position: [0.5, -0.5, -0.5],
normal: [0.0, 0.0, -1.0],
},
Vertex {
position: [-0.5, -0.5, -0.5],
normal: [0.0, 0.0, -1.0],
},
Vertex {
position: [-0.5, 0.5, -0.5],
normal: [0.0, 0.0, -1.0],
},
Vertex {
position: [-0.5, 0.5, -0.5],
normal: [0.0, 0.0, -1.0],
},
Vertex {
position: [0.5, 0.5, -0.5],
normal: [0.0, 0.0, -1.0],
},
Vertex {
position: [0.5, -0.5, -0.5],
normal: [0.0, 0.0, -1.0],
},
Vertex {
position: [0.5, -0.5, 0.5],
normal: [1.0, 0.0, 0.0],
},
Vertex {
position: [0.5, -0.5, -0.5],
normal: [1.0, 0.0, 0.0],
},
Vertex {
position: [0.5, 0.5, -0.5],
normal: [1.0, 0.0, 0.0],
},
Vertex {
position: [0.5, 0.5, -0.5],
normal: [1.0, 0.0, 0.0],
},
Vertex {
position: [0.5, 0.5, 0.5],
normal: [1.0, 0.0, 0.0],
},
Vertex {
position: [0.5, -0.5, 0.5],
normal: [1.0, 0.0, 0.0],
},
Vertex {
position: [-0.5, -0.5, -0.5],
normal: [-1.0, 0.0, 0.0],
},
Vertex {
position: [-0.5, -0.5, 0.5],
normal: [-1.0, 0.0, 0.0],
},
Vertex {
position: [-0.5, 0.5, 0.5],
normal: [-1.0, 0.0, 0.0],
},
Vertex {
position: [-0.5, 0.5, 0.5],
normal: [-1.0, 0.0, 0.0],
},
Vertex {
position: [-0.5, 0.5, -0.5],
normal: [-1.0, 0.0, 0.0],
},
Vertex {
position: [-0.5, -0.5, -0.5],
normal: [-1.0, 0.0, 0.0],
},
Vertex {
position: [-0.5, 0.5, 0.5],
normal: [0.0, 1.0, 0.0],
},
Vertex {
position: [0.5, 0.5, 0.5],
normal: [0.0, 1.0, 0.0],
},
Vertex {
position: [0.5, 0.5, -0.5],
normal: [0.0, 1.0, 0.0],
},
Vertex {
position: [0.5, 0.5, -0.5],
normal: [0.0, 1.0, 0.0],
},
Vertex {
position: [-0.5, 0.5, -0.5],
normal: [0.0, 1.0, 0.0],
},
Vertex {
position: [-0.5, 0.5, 0.5],
normal: [0.0, 1.0, 0.0],
},
Vertex {
position: [-0.5, -0.5, -0.5],
normal: [0.0, -1.0, 0.0],
},
Vertex {
position: [0.5, -0.5, -0.5],
normal: [0.0, -1.0, 0.0],
},
Vertex {
position: [0.5, -0.5, 0.5],
normal: [0.0, -1.0, 0.0],
},
Vertex {
position: [0.5, -0.5, 0.5],
normal: [0.0, -1.0, 0.0],
},
Vertex {
position: [-0.5, -0.5, 0.5],
normal: [0.0, -1.0, 0.0],
},
Vertex {
position: [-0.5, -0.5, -0.5],
normal: [0.0, -1.0, 0.0],
},
];
const CUBE_INDICES: &[u32] = &[
0, 1, 2, 2, 3, 0,
4, 5, 6, 6, 7, 4,
8, 9, 10, 10, 11, 8,
12, 13, 14, 14, 15, 12,
16, 17, 18, 18, 19, 16,
20, 21, 22, 22, 23, 20,
];
impl App<'_> {
fn create_main_window(&mut self, event_loop: &ActiveEventLoop) {
let win_attr = Window::default_attributes().with_title("Zenyx");
match event_loop.create_window(win_attr) {
Ok(window) => {
let window = Arc::new(window);
let window_id = window.id();
match Renderer::new_blocking(window.clone()) {
Ok(mut wgpu_ctx) => {
let obj = match tobj::load_obj(
"test.obj",
&LoadOptions {
triangulate: true,
single_index: true,
..Default::default()
},
) {
Ok(obj) => obj,
Err(e) => {
error!("{e}");
panic!()
}
};
let mesh = obj.0.get(0).unwrap().mesh.clone();
let vertices: Vec<Vertex> = (0..mesh.positions.len() / 3)
.map(|i| Vertex {
position: [
mesh.positions[i * 3],
mesh.positions[i * 3 + 1],
mesh.positions[i * 3 + 2],
],
normal: if !mesh.normals.is_empty() {
[
mesh.normals[i * 3],
mesh.normals[i * 3 + 1],
mesh.normals[i * 3 + 2],
]
} else {
[0.0; 3]
},
})
.collect();
wgpu_ctx.add_model(&vertices, &mesh.indices);
// wgpu_ctx.add_model(CUBE_VERTICES,CUBE_INDICES);
self.windows.insert(
window_id,
WindowContext {
window,
ctx: wgpu_ctx,
main_window: true,
},
);
info!("Main window created: {:?}", window_id);
}
Err(e) => error!("Failed to create WGPU context: {:?}", e),
}
}
Err(e) => error!("Failed to create main window: {:?}", e),
}
}
fn handle_close_requested(&mut self, window_id: WindowId) {
if self.windows.remove(&window_id).is_some() {
debug!("Window {:?} closed", window_id);
} else {
warn!("Tried to close non-existent window {:?}", window_id);
}
}
fn handle_keyboard_input(
&mut self,
event_loop: &ActiveEventLoop,
window_id: WindowId,
key_event: KeyEvent,
) {
if !key_event.state.is_pressed() {
return;
}
match key_event.physical_key {
winit::keyboard::PhysicalKey::Code(code) => match code {
winit::keyboard::KeyCode::Space => {
self.toggle_background(window_id);
}
winit::keyboard::KeyCode::Escape => {
self.spawn_child_window(event_loop);
}
other => error!("Unimplemented keycode: {:?}", other),
},
_ => debug!("Received a keyboard event with no physical key"),
}
}
fn toggle_background(&mut self, window_id: WindowId) {
if let Some(window_context) = self.windows.get_mut(&window_id) {
let current_color = window_context.ctx.bg_color();
let new_color = match current_color {
wgpu::Color::WHITE => wgpu::Color::BLACK,
wgpu::Color::BLACK => wgpu::Color::WHITE,
_ => wgpu::Color::WHITE,
};
window_context.ctx.set_bg_color(new_color);
debug!("Toggled background color for window {:?}", window_id);
} else {
warn!("No window context for toggling background: {:?}", window_id);
}
}
fn spawn_child_window(&mut self, event_loop: &ActiveEventLoop) {
if let Some(main_ctx) = self.windows.values().find(|ctx| ctx.is_main_window()) {
let title = format!("Zenyx - New Window {}", self.windows.len());
// TODO: Verify that this is safe instead of matching on it
let win_attr = unsafe {
let base = Window::default_attributes().with_title(title);
match main_ctx.window_handle() {
Ok(handle) => base.with_parent_window(Some(handle.as_raw())),
Err(_) => base,
}
};
match event_loop.create_window(win_attr) {
Ok(window) => {
let window = Arc::new(window);
let window_id = window.id();
match Renderer::new_blocking(window.clone()) {
Ok(mut wgpu_ctx) => {
wgpu_ctx.add_model(CUBE_VERTICES,CUBE_INDICES);
self.windows.insert(
window_id,
WindowContext {
window,
ctx: wgpu_ctx,
main_window: false,
},
);
debug!("Spawned new child window: {:?}", window_id);
}
Err(e) => error!("Failed to create WGPU context for child window: {:?}", e),
}
}
Err(e) => error!("Failed to create child window: {:?}", e),
}
} else {
error!("No main window found. Cannot spawn a child window.");
}
}
fn handle_redraw_requested(&mut self, window_id: WindowId) {
if let Some(window_context) = self.windows.get_mut(&window_id) {
window_context.ctx.draw();
window_context.request_redraw();
trace!(
"Redrew window {:?} with title: {}",
window_id,
window_context.window.title()
);
} else {
warn!("Received redraw for unknown window {:?}", window_id);
}
}
fn handle_resize(&mut self, window_id: WindowId, new_size: winit::dpi::PhysicalSize<u32>) {
if let Some(window_context) = self.windows.get_mut(&window_id) {
window_context.ctx.resize(new_size.into());
window_context.window.request_redraw();
debug!(
"Resized window {:?} to {}x{}",
window_id, new_size.width, new_size.height
);
} else {
warn!("Received resize for unknown window {:?}", window_id);
}
}
fn handle_destroyed(&mut self, event_loop: &ActiveEventLoop) {
if self.windows.is_empty() || !self.windows.iter().any(|(_, ctx)| ctx.is_main_window()) {
self.windows.clear();
debug!("All main windows are closed. Exiting event loop.");
event_loop.exit();
}
}
}
impl ApplicationHandler for App<'_> {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
if self.windows.is_empty() {
self.create_main_window(event_loop);
}
}
fn window_event(
&mut self,
event_loop: &ActiveEventLoop,
window_id: WindowId,
event: WindowEvent,
) {
match event {
WindowEvent::CloseRequested => {
self.handle_close_requested(window_id);
}
WindowEvent::KeyboardInput {
event: key_event, ..
} => {
self.handle_keyboard_input(event_loop, window_id, key_event);
}
WindowEvent::RedrawRequested => {
self.handle_redraw_requested(window_id);
}
WindowEvent::Resized(new_size) => {
self.handle_resize(window_id, new_size);
}
WindowEvent::Destroyed => {
self.handle_destroyed(event_loop);
}
_ => trace!("Unhandled window event for window {:?}", window_id),
}
}
}
pub fn init_renderer(event_loop: EventLoop<()>) {
event_loop.set_control_flow(ControlFlow::Wait);
let mut app = App::default();
if let Err(e) = event_loop.run_app(&mut app) {
error!("Failed to run application: {:?}", e);
}
}