From 93d711588d4a47414f2c70305d2e3997e2dbf44a Mon Sep 17 00:00:00 2001 From: BobDaGithubAccount <62474433+BobDaGithubAccount@users.noreply.github.com> Date: Fri, 15 Nov 2024 18:14:00 +0000 Subject: [PATCH] Resizing capability By copying and scaling the output from an invisible canvas, I can ensure that I can always scale the context without dealing with pipeline internals (e.g. dynamic frame buffers) --- lib/Cargo.toml | 2 +- lib/src/main copy.bak | 113 ++++++++++++++++++++++++++++++++++++++++++ lib/src/main.rs | 83 ++++++++++++++++++++----------- src/index.html | 26 +++++++--- src/index.js | 68 ++++++++++++------------- 5 files changed, 220 insertions(+), 72 deletions(-) create mode 100644 lib/src/main copy.bak diff --git a/lib/Cargo.toml b/lib/Cargo.toml index d964375..8c183a5 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] wasm-bindgen = "0.2" wasm-bindgen-futures = "0.4" -web-sys = { version = "0.3", features = ["Window"] } +web-sys = { version = "0.3", features = ["Window", "Document", "HtmlCanvasElement", "CanvasRenderingContext2d"] } three-d = "0.17.0" log = "0.4" winit = "0.28" diff --git a/lib/src/main copy.bak b/lib/src/main copy.bak new file mode 100644 index 0000000..7fb7219 --- /dev/null +++ b/lib/src/main copy.bak @@ -0,0 +1,113 @@ +use three_d::{context, renderer::*, FrameInputGenerator, SurfaceSettings, WindowedContext}; +use std::sync::Mutex; + +#[cfg(target_arch = "wasm32")] +use wasm_bindgen::prelude::*; + +lazy_static::lazy_static! { + static ref CAMERA_INSTANCE: Mutex> = Mutex::new(None); +} + +#[cfg(target_arch = "wasm32")] +#[wasm_bindgen] +pub fn resize_callback(width: u32, height: u32) { + log::info!("Resized to {:?}", (width, height)); + +} + +pub fn main() { + let event_loop = winit::event_loop::EventLoop::new(); + + #[cfg(not(target_arch = "wasm32"))] + let window_builder = winit::window::WindowBuilder::new() + .with_title("Full-Screen Window") + .with_inner_size(winit::dpi::LogicalSize::new(1920, 1080)) + .with_decorations(false) + .with_maximized(true); + + #[cfg(target_arch = "wasm32")] + let window_builder = { + use wasm_bindgen::JsCast; + use winit::platform::web::WindowBuilderExtWebSys; + winit::window::WindowBuilder::new() + .with_canvas(Some( + web_sys::window() + .unwrap() + .document() + .unwrap() + .get_element_by_id("canvas") + .unwrap() + .dyn_into::() + .unwrap(), + )) + .with_inner_size(winit::dpi::LogicalSize::new(1920, 1080)) + .with_prevent_default(true) + }; + + let window = window_builder.build(&event_loop).unwrap(); + let context = WindowedContext::from_winit_window(&window, SurfaceSettings::default()).unwrap(); + + let mut camera = Camera::new_perspective( + Viewport::new_at_origo(1, 1), + vec3(0.0, 2.0, 4.0), // Camera position + vec3(0.0, 0.0, 0.0), // Target position (where the camera is looking) + vec3(0.0, 1.0, 0.0), // Up direction + degrees(45.0), // Field of view + 0.1, // Near clipping plane + 100.0, // Far clipping plane + ); + + *CAMERA_INSTANCE.lock().unwrap() = Some(camera.clone()); + + let mut control = OrbitControl::new(*camera.target(), 1.0, 100.0); + + let mut model = Gm::new( + Mesh::new(&context, &CpuMesh::cube()), + ColorMaterial { + color: Srgba::GREEN, + ..Default::default() + }, + ); + model.set_animation(|time| Mat4::from_angle_y(radians(time * 0.0005))); + + let mut frame_input_generator = FrameInputGenerator::from_winit_window(&window); + event_loop.run(move |event, _, control_flow| match event { + winit::event::Event::MainEventsCleared => { + window.request_redraw(); + } + winit::event::Event::RedrawRequested(_) => { + let mut frame_input = frame_input_generator.generate(&context); + + control.handle_events(&mut camera, &mut frame_input.events); + camera.set_viewport(frame_input.viewport); + model.animate(frame_input.accumulated_time as f32); + frame_input + .screen() + .clear(ClearState::color_and_depth(0.8, 0.8, 0.8, 1.0, 1.0)) + .render(&camera, &model, &[]); + + context.swap_buffers().unwrap(); + control_flow.set_poll(); + window.request_redraw(); + } + winit::event::Event::WindowEvent { ref event, .. } => { + frame_input_generator.handle_winit_window_event(event); + match event { + winit::event::WindowEvent::Resized(physical_size) => { + log::info!("Resized to {:?}", physical_size); + context.resize(*physical_size); + camera.set_viewport(Viewport::new_at_origo(physical_size.width, physical_size.height)); + } + winit::event::WindowEvent::ScaleFactorChanged { new_inner_size, .. } => { + context.resize(**new_inner_size); + camera.set_viewport(Viewport::new_at_origo(new_inner_size.width, new_inner_size.height)); + } + winit::event::WindowEvent::CloseRequested => { + control_flow.set_exit(); + } + _ => (), + } + } + _ => {} + }); +} diff --git a/lib/src/main.rs b/lib/src/main.rs index 52c8071..2941638 100644 --- a/lib/src/main.rs +++ b/lib/src/main.rs @@ -1,5 +1,7 @@ use three_d::{context, renderer::*, FrameInputGenerator, SurfaceSettings, WindowedContext}; use std::sync::Mutex; +use winit::dpi::{PhysicalSize, LogicalSize}; +use web_sys::{window, CanvasRenderingContext2d, Document, HtmlCanvasElement}; #[cfg(target_arch = "wasm32")] use wasm_bindgen::prelude::*; @@ -8,25 +10,42 @@ lazy_static::lazy_static! { static ref CAMERA_INSTANCE: Mutex> = Mutex::new(None); } -#[cfg(target_arch = "wasm32")] -#[wasm_bindgen] -pub fn resize_callback(width: u32, height: u32) { - log::info!("Resized to {:?}", (width, height)); - if let Some(camera) = CAMERA_INSTANCE.lock().unwrap().as_mut() { - camera.set_viewport(Viewport::new_at_origo(width, height)); - } +pub fn copy_to_visible_canvas() -> Result<(), JsValue> { + let document = window().unwrap().document().unwrap(); + + let resizable_container = document.get_element_by_id("resizable-container") + .unwrap(); + + let visible_canvas = document.get_element_by_id("visible-canvas") + .unwrap() + .dyn_into::() + .unwrap(); + + let visible_ctx = visible_canvas + .get_context("2d")? + .unwrap() + .dyn_into::() + .unwrap(); + + let hidden_canvas = document.get_element_by_id("canvas") + .unwrap() + .dyn_into::() + .unwrap(); + + let width = resizable_container.client_width(); + let height = resizable_container.client_height(); + + visible_canvas.set_width(width as u32); + visible_canvas.set_height(height as u32); + + visible_ctx.draw_image_with_html_canvas_element_and_dw_and_dh(&hidden_canvas, 0.0, 0.0, width as f64, height as f64)?; + + Ok(()) } pub fn main() { let event_loop = winit::event_loop::EventLoop::new(); - #[cfg(not(target_arch = "wasm32"))] - let window_builder = winit::window::WindowBuilder::new() - .with_title("Full-Screen Window") - .with_inner_size(winit::dpi::LogicalSize::new(1920, 1080)) - .with_decorations(false) - .with_maximized(true); - #[cfg(target_arch = "wasm32")] let window_builder = { use wasm_bindgen::JsCast; @@ -42,21 +61,21 @@ pub fn main() { .dyn_into::() .unwrap(), )) - .with_inner_size(winit::dpi::LogicalSize::new(1920, 1080)) + .with_inner_size(LogicalSize::new(1920, 1080)) .with_prevent_default(true) }; - let window = window_builder.build(&event_loop).unwrap(); - let context = WindowedContext::from_winit_window(&window, SurfaceSettings::default()).unwrap(); + let render_window = window_builder.build(&event_loop).unwrap(); + let context = WindowedContext::from_winit_window(&render_window, SurfaceSettings::default()).unwrap(); let mut camera = Camera::new_perspective( Viewport::new_at_origo(1, 1), - vec3(0.0, 2.0, 4.0), // Camera position - vec3(0.0, 0.0, 0.0), // Target position (where the camera is looking) - vec3(0.0, 1.0, 0.0), // Up direction - degrees(45.0), // Field of view - 0.1, // Near clipping plane - 100.0, // Far clipping plane + vec3(0.0, 2.0, 4.0),// Camera position + vec3(0.0, 0.0, 0.0),// Target position (where the camera is looking) + vec3(0.0, 1.0, 0.0),// Up direction + degrees(45.0),// Field of view + 0.1,// Near clipping plane + 100.0,// Far clipping plane ); *CAMERA_INSTANCE.lock().unwrap() = Some(camera.clone()); @@ -72,14 +91,14 @@ pub fn main() { ); model.set_animation(|time| Mat4::from_angle_y(radians(time * 0.0005))); - let mut frame_input_generator = FrameInputGenerator::from_winit_window(&window); + let mut frame_input_generator = FrameInputGenerator::from_winit_window(&render_window); event_loop.run(move |event, _, control_flow| match event { winit::event::Event::MainEventsCleared => { - window.request_redraw(); + render_window.request_redraw(); } winit::event::Event::RedrawRequested(_) => { let mut frame_input = frame_input_generator.generate(&context); - + copy_to_visible_canvas().unwrap(); control.handle_events(&mut camera, &mut frame_input.events); camera.set_viewport(frame_input.viewport); model.animate(frame_input.accumulated_time as f32); @@ -87,12 +106,13 @@ pub fn main() { .screen() .clear(ClearState::color_and_depth(0.8, 0.8, 0.8, 1.0, 1.0)) .render(&camera, &model, &[]); - context.swap_buffers().unwrap(); control_flow.set_poll(); - window.request_redraw(); + render_window.request_redraw(); + } winit::event::Event::WindowEvent { ref event, .. } => { + frame_input_generator.handle_winit_window_event(event); match event { winit::event::WindowEvent::Resized(physical_size) => { @@ -101,15 +121,18 @@ pub fn main() { camera.set_viewport(Viewport::new_at_origo(physical_size.width, physical_size.height)); } winit::event::WindowEvent::ScaleFactorChanged { new_inner_size, .. } => { + log::info!("Scale factor changed to {:?}", new_inner_size); context.resize(**new_inner_size); camera.set_viewport(Viewport::new_at_origo(new_inner_size.width, new_inner_size.height)); } winit::event::WindowEvent::CloseRequested => { control_flow.set_exit(); } - _ => (), + _ => + log::info!("Event: {:?}", event), } } - _ => {} + _ => { + } }); } diff --git a/src/index.html b/src/index.html index 1738f1b..45638b6 100644 --- a/src/index.html +++ b/src/index.html @@ -9,16 +9,30 @@ margin: 0; overflow: hidden; } + #resizable-container { + width: 50vw; + height: 50vh; + resize: both; + overflow: auto; + display: flex; + } #canvas { - display: block; - width: 100vw; - height: 100vh; + flex: 1; + width: 100%; + height: 100%; + visibility: hidden; + } + #visible-canvas { + width: 100%; + height: 100%; } +
+ +
- + - + \ No newline at end of file diff --git a/src/index.js b/src/index.js index e3dd8f9..e0d911e 100644 --- a/src/index.js +++ b/src/index.js @@ -1,44 +1,42 @@ import init, * as wasm_bindgen from './src.js'; -function jsInit() { - console.log("Wasm module initialized"); - - if (typeof window.resize_callback === 'undefined' && typeof wasm_bindgen.resize_callback !== 'undefined') { - console.log("Setting resize callback"); - window.resize_callback = wasm_bindgen.resize_callback; - } - - const canvas = document.getElementById('canvas'); - - function resizeCanvas() { - const canvas = document.getElementById('canvas'); - const width = window.innerWidth; - const height = window.innerHeight; - canvas.width = width; - canvas.height = height; - - if (typeof window.resize_callback === 'function') { - window.resize_callback(width, height); - } - } - - window.addEventListener('resize', resizeCanvas); - window.addEventListener('load', resizeCanvas); -} init().then(() => { - jsInit(); + console.log("Wasm module initialized without standard control flow error?"); }).catch(err => { if (err.message.includes("Using exceptions for control flow")) { - console.warn("Ignoring expected Wasm initialization exception and proceeding."); - try { - jsInit(); - } catch (err) { - console.error("Error in continuing initialisation:", err); - throw err; - } + console.warn("Ignoring expected Wasm initialisation exception and proceeding."); } else { console.error("Error initializing wasm module:", err); throw err; } -}); -console.log("Wasm module initialization call made"); +}).finally(() => { + + console.log("Wasm module initialised!"); + + // if (typeof window.resize_callback === 'undefined' && typeof wasm_bindgen.resize_callback !== 'undefined') { + // console.log("Setting resize callback"); + // window.resize_callback = wasm_bindgen.resize_callback; + // } + // const resizableContainer = document.getElementById('resizable-container'); + // function resizeCanvas() { + // const canvas = document.getElementById('canvas'); + // const width = resizableContainer.clientWidth; + // const height = resizableContainer.clientHeight; + // if (typeof window.resize_callback === 'function') { + // window.resize_callback(width, height); + // } + // canvas.width = width; + // canvas.height = height; + // } + + // if (resizableContainer) { + // const resizeObserver = new ResizeObserver(() => { + // resizeCanvas(); + // }); + + // resizeObserver.observe(resizableContainer); + // } + + // window.addEventListener('load', resizeCanvas); + +}); \ No newline at end of file