use { log::info, std::{rc::Rc, sync::Mutex}, wasm_bindgen::prelude::*, web_sys::HtmlCanvasElement, wgpu::SurfaceTarget, }; use crate::{ app::{App, Model}, mvu, mvu::{Event, File, MvuApp, Size2i}, }; struct FrameTimer { performance: web_sys::Performance, last_frame_start_milliseconds: f64, current_frame_start_milliseconds: f64, } impl FrameTimer { fn new() -> Self { let performance = web_sys::window() .expect("get window") .performance() .expect("window has performance object"); let last_frame_start_milliseconds = performance.now(); let current_frame_start_milliseconds = performance.now(); Self { performance, last_frame_start_milliseconds, current_frame_start_milliseconds, } } } impl mvu::FrameTimer for FrameTimer { fn mark_frame_start(&mut self) { self.last_frame_start_milliseconds = self.current_frame_start_milliseconds; self.current_frame_start_milliseconds = self.performance.now(); } fn get_frame_time_seconds(&self) -> f64 { (self.current_frame_start_milliseconds - self.last_frame_start_milliseconds) * 0.001 } } #[wasm_bindgen] #[derive(Clone)] pub struct PteropusCanvas { app: Rc>, model: Rc, } #[wasm_bindgen] impl PteropusCanvas { async fn init(&self) { let html_canvas = self.get_canvas(); let size = Size2i { width: html_canvas.client_width() as u32, height: html_canvas.client_height() as u32, }; let instance = wgpu::Instance::default(); let surface_target = SurfaceTarget::Canvas(html_canvas); let surface = instance .create_surface(surface_target) .expect("create graphics surface for canvas"); self.app .lock() .expect("get app mutex") .init(&instance, surface, size, Box::new(FrameTimer::new())) .await; } #[wasm_bindgen] pub async fn render(&self) { self.app .lock() .expect("get app mutex") .view(Rc::clone(&self.model)) .await .expect("view succeeds"); } #[wasm_bindgen] pub async fn on_resize(&self) { let html_canvas = self.get_canvas(); self.app .lock() .expect("get app mutex") .resize(Size2i { width: html_canvas.client_width() as u32, height: html_canvas.client_height() as u32, }) .await; } #[wasm_bindgen] pub async fn load_file(&mut self, file: web_sys::File) { let data = gloo::file::futures::read_as_bytes(&file.into()) .await .expect("read data from dragged file"); self.model = self .app .lock() .expect("get app mutex") .update( Rc::clone(&self.model), Event::OpenTestFile(Box::new(std::io::Cursor::new(data))), ) .await; } #[wasm_bindgen] pub async fn clear_scene(&mut self) { self.model = self .app .lock() .expect("get app mutex") .update(Rc::clone(&self.model), Event::ClearScene) .await; } pub fn get_canvas(&self) -> HtmlCanvasElement { let window = web_sys::window().expect("get window"); let document = window.document().expect("get HTML document"); let canvas_element = document .get_element_by_id("pteropus-canvas") .expect("document contains element with id \"pteropus-canvas\""); canvas_element .dyn_into() .expect("pteropus-canvas element is a canvas") } } pub async fn run(app: App, model: Rc) -> PteropusCanvas { let app = Rc::new(Mutex::new(app)); std::panic::set_hook(Box::new(console_error_panic_hook::hook)); console_log::init_with_level(log::Level::Info).expect("Couldn't initialize logger"); let pteropus_canvas = PteropusCanvas { app, model }; pteropus_canvas.init().await; pteropus_canvas }