zenyx-engine/engine/src/core/render/mod.rs

226 lines
7.8 KiB
Rust

use std::collections::HashMap;
use std::ops::Deref;
use std::sync::Arc;
use ctx::WgpuCtx;
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: WgpuCtx<'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>>,
}
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 WgpuCtx::new_blocking(window.clone()) {
Ok(wgpu_ctx) => {
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.change_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 WgpuCtx::new_blocking(window.clone()) {
Ok(wgpu_ctx) => {
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);
}
}