-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Inox2D can finally run on the web with WASM and WebGL! The most important use-case for Inox2D is now covered :D Thanks to @adryzz for her [inox2d-wasm](https://github.com/adryzz/inox2d-wasm) example, I took inspiration from it for this WebGL example.
- Loading branch information
Showing
15 changed files
with
432 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
[build] | ||
target = "wasm32-unknown-unknown" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
/assets | ||
/dist |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"rust-analyzer.cargo.target": "wasm32-unknown-unknown" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
[package] | ||
name = "render_webgl" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[dependencies] | ||
console_error_panic_hook = "0.1.7" | ||
glam = "0.24.0" | ||
glow = "0.12.1" | ||
inox2d = { path = "../../" } | ||
js-sys = "0.3.64" | ||
reqwest = "0.11.18" | ||
tracing = "0.1.37" | ||
tracing-subscriber = "0.3.16" | ||
tracing-wasm = "0.2.1" | ||
wasm-bindgen = "0.2.87" | ||
wasm-bindgen-futures = "0.4.37" | ||
web-time = "0.2.0" | ||
winit = "0.28.2" | ||
|
||
[dependencies.web-sys] | ||
version = "0.3.61" | ||
features = [ | ||
"Window", | ||
"Location", | ||
"Navigator", | ||
"Element", | ||
"HtmlElement", | ||
"Document", | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# Inox2D: Render WebGL example | ||
|
||
![Aka demo picture](https://user-images.githubusercontent.com/13885008/253771145-f3921ffb-6d37-481a-ad26-4a814d070209.png) | ||
|
||
## How to build and run | ||
|
||
Use [Trunk](https://trunkrs.dev/) to build and run this example. | ||
|
||
⚠️ You need to have a puppet file named `puppet.inp` in `examples/render_webgl/assets/` for the project to read. ⚠️ | ||
|
||
You can get [example models here](https://lunafoxgirlvt.itch.io/inochi-session). They're gonna be in the `.inx` format, so you'll need to convert them to `.inp` by exporting them as puppets from [Inochi Creator](https://lunafoxgirlvt.itch.io/inochi-creator). | ||
|
||
```sh | ||
cd examples/render_webgl | ||
|
||
# to build | ||
trunk build | ||
|
||
# to run directly | ||
trunk serve | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
|
||
<head> | ||
<meta content="text/html;charset=utf-8" http-equiv="Content-Type" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1"> | ||
<meta charset="UTF-8" /> | ||
<title>Inox2D WebGL Example</title> | ||
<link data-trunk rel="rust" data-wasm-opt="z" data-reference-types> | ||
<link data-trunk rel="copy-dir" href="assets/"> | ||
</head> | ||
|
||
<body> | ||
</body> | ||
|
||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
#[cfg(target_arch = "wasm32")] | ||
mod scene; | ||
|
||
#[cfg(target_arch = "wasm32")] | ||
fn create_window( | ||
event: &winit::event_loop::EventLoop<()>, | ||
) -> Result<winit::window::Window, winit::error::OsError> { | ||
use winit::dpi::PhysicalSize; | ||
use winit::platform::web::WindowExtWebSys; | ||
use winit::window::WindowBuilder; | ||
|
||
let window = WindowBuilder::new() | ||
.with_resizable(false) | ||
.with_inner_size(PhysicalSize::new(1280, 720)) | ||
.build(event)?; | ||
|
||
web_sys::window() | ||
.and_then(|win| win.document()) | ||
.and_then(|doc| doc.body()) | ||
.and_then(|body| { | ||
let canvas = web_sys::Element::from(window.canvas()); | ||
canvas.set_id("canvas"); | ||
body.append_child(&canvas).ok() | ||
}) | ||
.expect("couldn't append canvas to document body"); | ||
|
||
Ok(window) | ||
} | ||
|
||
#[cfg(target_arch = "wasm32")] | ||
fn request_animation_frame(f: &wasm_bindgen::prelude::Closure<dyn FnMut()>) { | ||
use wasm_bindgen::JsCast; | ||
web_sys::window() | ||
.unwrap() | ||
.request_animation_frame(f.as_ref().unchecked_ref()) | ||
.expect("Couldn't register `requestAnimationFrame`"); | ||
} | ||
|
||
#[cfg(target_arch = "wasm32")] | ||
pub fn base_url() -> String { | ||
web_sys::window().unwrap().location().origin().unwrap() | ||
} | ||
|
||
#[cfg(target_arch = "wasm32")] | ||
async fn run() -> Result<(), Box<dyn std::error::Error>> { | ||
use std::cell::RefCell; | ||
use std::rc::Rc; | ||
|
||
use inox2d::{formats::inp::parse_inp, render::opengl::OpenglRenderer}; | ||
|
||
use glam::{uvec2, Vec2}; | ||
use tracing::info; | ||
use wasm_bindgen::prelude::Closure; | ||
use wasm_bindgen::JsCast; | ||
use winit::event::{Event, WindowEvent}; | ||
|
||
use crate::scene::WasmSceneController; | ||
|
||
let events = winit::event_loop::EventLoop::new(); | ||
let window = create_window(&events)?; | ||
|
||
// Make sure the context has a stencil buffer | ||
let context_options = js_sys::Object::new(); | ||
js_sys::Reflect::set(&context_options, &"stencil".into(), &true.into()).unwrap(); | ||
|
||
let gl = { | ||
let canvas = web_sys::window() | ||
.unwrap() | ||
.document() | ||
.unwrap() | ||
.get_element_by_id("canvas") | ||
.unwrap() | ||
.dyn_into::<web_sys::HtmlCanvasElement>() | ||
.unwrap(); | ||
let webgl2_context = canvas | ||
.get_context_with_context_options("webgl2", &context_options) | ||
.unwrap() | ||
.unwrap() | ||
.dyn_into::<web_sys::WebGl2RenderingContext>() | ||
.unwrap(); | ||
glow::Context::from_webgl2_context(webgl2_context) | ||
}; | ||
|
||
info!("Loading puppet"); | ||
let res = reqwest::Client::new() | ||
.get(format!("{}/assets/puppet.inp", base_url())) | ||
.send() | ||
.await?; | ||
|
||
let model_bytes = res.bytes().await?; | ||
let model = parse_inp(model_bytes.as_ref())?; | ||
let puppet = model.puppet; | ||
|
||
info!("Initializing Inox2D renderer"); | ||
let window_size = window.inner_size(); | ||
let viewport = uvec2(window_size.width, window_size.height); | ||
let mut renderer = OpenglRenderer::new(gl, viewport, &puppet)?; | ||
|
||
info!("Uploading model textures"); | ||
renderer.upload_model_textures(&model.textures)?; | ||
renderer.camera.scale = Vec2::splat(0.15); | ||
info!("Inox2D renderer initialized"); | ||
|
||
let scene_ctrl = WasmSceneController::new(&renderer.camera, 0.5); | ||
|
||
// Refcells because we need to make our own continuous animation loop. | ||
// Winit won't help us :( | ||
let scene_ctrl = Rc::new(RefCell::new(scene_ctrl)); | ||
let renderer = Rc::new(RefCell::new(renderer)); | ||
let puppet = Rc::new(RefCell::new(puppet)); | ||
|
||
// Setup continuous animation loop | ||
{ | ||
let anim_loop_f = Rc::new(RefCell::new(None)); | ||
let anim_loop_g = anim_loop_f.clone(); | ||
let scene_ctrl = scene_ctrl.clone(); | ||
let renderer = renderer.clone(); | ||
let puppet = puppet.clone(); | ||
|
||
*anim_loop_g.borrow_mut() = Some(Closure::new(move || { | ||
scene_ctrl | ||
.borrow_mut() | ||
.update(&mut renderer.borrow_mut().camera); | ||
|
||
renderer.borrow().clear(); | ||
{ | ||
let mut puppet = puppet.borrow_mut(); | ||
puppet.begin_set_params(); | ||
let t = scene_ctrl.borrow().current_elapsed(); | ||
puppet.set_param("Head:: Yaw-Pitch", Vec2::new(t.cos(), t.sin())); | ||
puppet.end_set_params(); | ||
} | ||
renderer.borrow().render(&puppet.borrow()); | ||
|
||
request_animation_frame(anim_loop_f.borrow().as_ref().unwrap()); | ||
})); | ||
request_animation_frame(anim_loop_g.borrow().as_ref().unwrap()); | ||
} | ||
|
||
// Event loop | ||
events.run(move |event, _, control_flow| { | ||
// it needs to be present | ||
let _window = &window; | ||
|
||
control_flow.set_wait(); | ||
|
||
match event { | ||
Event::WindowEvent { ref event, .. } => match event { | ||
WindowEvent::Resized(physical_size) => { | ||
// Handle window resizing | ||
renderer | ||
.borrow_mut() | ||
.resize(physical_size.width, physical_size.height); | ||
window.request_redraw(); | ||
} | ||
WindowEvent::CloseRequested => control_flow.set_exit(), | ||
_ => scene_ctrl | ||
.borrow_mut() | ||
.interact(&window, event, &renderer.borrow().camera), | ||
}, | ||
Event::MainEventsCleared => { | ||
window.request_redraw(); | ||
} | ||
_ => (), | ||
} | ||
}) | ||
} | ||
|
||
#[cfg(target_arch = "wasm32")] | ||
async fn runwrap() { | ||
match run().await { | ||
Ok(_) => tracing::info!("Shutdown"), | ||
Err(e) => tracing::error!("Fatal crash: {}", e), | ||
} | ||
} | ||
|
||
#[cfg(target_arch = "wasm32")] | ||
fn main() { | ||
console_error_panic_hook::set_once(); | ||
tracing_wasm::set_as_global_default(); | ||
wasm_bindgen_futures::spawn_local(runwrap()); | ||
} | ||
|
||
#[cfg(not(target_arch = "wasm32"))] | ||
fn main() { | ||
panic!("This is a WASM example. You need to build it for the WASM target."); | ||
} |
Oops, something went wrong.