161 lines
4.5 KiB
Rust
161 lines
4.5 KiB
Rust
use {
|
|
log::info,
|
|
std::{rc::Rc, sync::Mutex},
|
|
wasm_bindgen::prelude::*,
|
|
web_sys::HtmlCanvasElement,
|
|
wgpu::SurfaceTarget,
|
|
};
|
|
|
|
use crate::{
|
|
app::{App, Model},
|
|
mvu,
|
|
mvu::{Event, 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<Mutex<App>>,
|
|
model: Rc<Model>,
|
|
}
|
|
|
|
#[wasm_bindgen(getter_with_clone)]
|
|
#[derive(Clone)]
|
|
pub struct PteropusInitError {
|
|
pub message: String,
|
|
}
|
|
|
|
impl PteropusInitError {
|
|
fn new<M: Into<String>>(message: M) -> Self {
|
|
Self {
|
|
message: message.into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
impl PteropusCanvas {
|
|
async fn init(&self) -> Result<(), PteropusInitError> {
|
|
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).map_err(|_| {
|
|
PteropusInitError::new(
|
|
"Could not initialize canvas. This browser may not support WebGPU.",
|
|
)
|
|
})?;
|
|
self.app
|
|
.lock()
|
|
.expect("get app mutex")
|
|
.init(&instance, surface, size, Box::new(FrameTimer::new()))
|
|
.await;
|
|
Ok(())
|
|
}
|
|
|
|
#[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<Model>) -> Result<PteropusCanvas, PteropusInitError> {
|
|
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?;
|
|
Ok(pteropus_canvas)
|
|
}
|