420 lines
13 KiB
Rust
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);
|
|
}
|
|
}
|