From b132fefaa6835d77e45655c0fc55c92deabd02bf Mon Sep 17 00:00:00 2001 From: Andy Wong Date: Wed, 13 Mar 2024 22:01:22 -0700 Subject: [PATCH] add quat wasm methods --- package.json | 6 +- src/lib.rs | 11 +-- src/matrix.rs | 4 +- src/quat.rs | 22 +++++- src/wasm/mod.rs | 9 +++ src/wasm/ptr.rs | 17 ++++- src/wasm/quat.rs | 176 +++++++++++++++++++++++++++++++++++++++++++++++ src/wasm/vec.rs | 23 ++++++- wit/world.wit | 4 +- 9 files changed, 252 insertions(+), 20 deletions(-) create mode 100644 src/wasm/quat.rs diff --git a/package.json b/package.json index 167b7e2..c52bb5b 100644 --- a/package.json +++ b/package.json @@ -25,8 +25,10 @@ "scripts": { "prepublishOnly": "npm run build && npm test && npm run docs", "clean": "rimraf coverage/ docs/ dist/ target/ **/__tests__/**/*.spec.map **/__tests__/**/*.spec.wat", - "build:rust": "cargo rustc -r --target wasm32-unknown-unknown --crate-type cdylib -F jsmath,wasm --no-default-features", - "postbuild:rust": "wasm-opt -Oz -o dist/munum.wasm target/wasm32-unknown-unknown/release/munum.wasm", + "build:wasm": "cargo rustc -r --target wasm32-unknown-unknown --crate-type cdylib -F jsmath,wasm,std --no-default-features", + "postbuild:wasm": "npm run build:wit && npm run build:wasm-opt", + "build:wit": "wasm-tools component embed -o dist/munum.wasm -w root wit/ target/wasm32-unknown-unknown/release/munum.wasm", + "build:wasm-opt": "wasm-opt -Oz -o dist/munum.wasm dist/munum.wasm", "prebuild": "npm run lint", "build": "npm run tsc", "lint": "eslint assembly --ext .ts,.tsx", diff --git a/src/lib.rs b/src/lib.rs index fd5edf7..48fbc8d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,11 +1,10 @@ //! Micro Library for 3D Math -#![cfg_attr(not(test), no_std)] +#![no_std] -#[cfg(test)] extern crate alloc; -#[cfg(feature = "std")] +#[cfg(any(feature = "std", test))] extern crate std; mod matrix; @@ -25,9 +24,3 @@ pub use quat::{quat, Quaternion}; #[cfg(target_arch = "wasm32")] mod wasm; - -#[cfg(all(target_arch = "wasm32", feature = "wasm", not(test)))] -#[panic_handler] -fn panic(_panic: &core::panic::PanicInfo<'_>) -> ! { - core::arch::wasm32::unreachable() -} diff --git a/src/matrix.rs b/src/matrix.rs index c556f66..c95d293 100644 --- a/src/matrix.rs +++ b/src/matrix.rs @@ -117,14 +117,14 @@ impl Index for Matri #[inline] fn index(&self, index: usize) -> &Self::Output { - &self.0[index / R][index % R] + &self.0[(index / R) % C][index % R] } } impl IndexMut for Matrix { #[inline] fn index_mut(&mut self, index: usize) -> &mut Self::Output { - &mut self.0[index / R][index % R] + &mut self.0[(index / R) % C][index % R] } } diff --git a/src/quat.rs b/src/quat.rs index cb48b54..383581e 100644 --- a/src/quat.rs +++ b/src/quat.rs @@ -377,14 +377,17 @@ impl Quaternion { /// ``` /// # use munum::Quaternion; /// let mut q = ::from_slice(&[2., 5., 14., 8.]); - /// q.invert(); + /// assert!(q.invert()); /// assert_eq!(*q.as_ref(), [-2. / 289., -5. / 289., -14. / 289., 8. / 289.]); /// ``` - pub fn invert(&mut self) { + pub fn invert(&mut self) -> bool { self.conj(); let len2 = self.sqr_len(); if len2 != T::zero() { *self /= len2; + true + } else { + false } } @@ -434,6 +437,21 @@ impl Quaternion { self.0.normalize() } + /// Returns a normalized version of this vector. + /// + /// # Examples + /// ``` + /// # use munum::{Quaternion, assert_float_eq}; + /// let q = ::from_slice(&[2., 5., 14., 8.]).normalized(); + /// assert_float_eq!(q.as_ref(), &[2./17., 5./17., 14./17., 8./17.]); + /// ``` + #[inline] + pub fn normalized(&self) -> Self { + let mut q = *self; + q.normalize(); + q + } + /// Linear interpolates between 2 unit `Quaternion`s. /// /// # Examples diff --git a/src/wasm/mod.rs b/src/wasm/mod.rs index e2e30db..2321367 100644 --- a/src/wasm/mod.rs +++ b/src/wasm/mod.rs @@ -7,3 +7,12 @@ mod jsmath; #[cfg(feature = "wasm")] mod vec; + +#[cfg(feature = "wasm")] +mod quat; + +#[cfg(all(feature = "wasm", not(any(feature = "std", test))))] +#[panic_handler] +fn panic(_panic: &core::panic::PanicInfo<'_>) -> ! { + core::arch::wasm32::unreachable() +} diff --git a/src/wasm/ptr.rs b/src/wasm/ptr.rs index ecb0404..5fd78ce 100644 --- a/src/wasm/ptr.rs +++ b/src/wasm/ptr.rs @@ -1,4 +1,4 @@ -use crate::Matrix; +use crate::{Matrix, Quaternion}; use num::traits::NumAssign; /// Trait for loading a value from a pointer. @@ -15,7 +15,20 @@ impl Load> if let Some(&m) = unsafe { self.as_ref() } { m } else { - Matrix::::default() + Matrix::default() + } + } +} + +impl Load> + for *const Quaternion +{ + #[inline] + fn load(&self) -> Quaternion { + if let Some(&q) = unsafe { self.as_ref() } { + q + } else { + Quaternion::default() } } } diff --git a/src/wasm/quat.rs b/src/wasm/quat.rs new file mode 100644 index 0000000..67fb71a --- /dev/null +++ b/src/wasm/quat.rs @@ -0,0 +1,176 @@ +use super::ptr::Load; +use crate::{Quaternion, Vec3}; +use alloc::boxed::Box; + +#[export_name = concat!("munum:wasm/quat#create")] +pub extern "C" fn quat_create(x: f64, y: f64, z: f64, w: f64) -> *const Quaternion { + Box::into_raw(Box::new(Quaternion::new(x, y, z, w))) +} + +#[export_name = concat!("munum:wasm/quat#from-unit-vecs")] +pub extern "C" fn quat_from_unit_vecs( + from: *const Vec3, + to: *const Vec3, +) -> *const Quaternion { + Box::into_raw(Box::new(Quaternion::from_unit_vecs(from.load(), to.load()))) +} + +#[export_name = concat!("munum:wasm/quat#from-axis-angle")] +pub extern "C" fn quat_from_axis_angle( + axis: *const Vec3, + angle: f64, +) -> *const Quaternion { + Box::into_raw(Box::new(Quaternion::from_axis_angle(axis.load(), angle))) +} + +#[export_name = concat!("munum:wasm/quat#from-angle-x")] +pub extern "C" fn quat_from_angle_x(angle: f64) -> *const Quaternion { + Box::into_raw(Box::new(Quaternion::from_angle_x(angle))) +} + +#[export_name = concat!("munum:wasm/quat#from-angle-y")] +pub extern "C" fn quat_from_angle_y(angle: f64) -> *const Quaternion { + Box::into_raw(Box::new(Quaternion::from_angle_y(angle))) +} + +#[export_name = concat!("munum:wasm/quat#from-angle-z")] +pub extern "C" fn quat_from_angle_z(angle: f64) -> *const Quaternion { + Box::into_raw(Box::new(Quaternion::from_angle_z(angle))) +} + +#[export_name = concat!("munum:wasm/quat#free")] +pub extern "C" fn quat_free(ptr: *mut Quaternion) { + drop(unsafe { Box::from_raw(ptr) }) +} + +#[export_name = "munum:wasm/quat#set"] +pub extern "C" fn quat_set( + out: *mut Quaternion, + x: f64, + y: f64, + z: f64, + w: f64, +) -> *const Quaternion { + if let Some(q) = unsafe { out.as_mut() } { + q[0] = x; + q[1] = y; + q[2] = z; + q[3] = w; + } + out +} + +#[export_name = concat!("munum:wasm/quat#get")] +pub extern "C" fn quat_get(ptr: *const Quaternion) -> *const Quaternion { + ptr +} + +#[export_name = concat!("munum:wasm/quat#copy")] +pub extern "C" fn quat_copy( + dst: *mut Quaternion, + src: *const Quaternion, +) -> *const Quaternion { + if let Some(o) = unsafe { dst.as_mut() } { + *o = src.load(); + } + dst +} + +#[export_name = concat!("munum:wasm/quat#mul")] +pub extern "C" fn quat_mul( + out: *mut Quaternion, + a: *const Quaternion, + b: *const Quaternion, +) -> *const Quaternion { + if let Some(o) = unsafe { out.as_mut() } { + *o = a.load() * b.load(); + } + out +} + +#[export_name = concat!("munum:wasm/quat#rotate-vec3")] +pub extern "C" fn quat_rotate_vec3( + out: *mut Vec3, + q: *const Quaternion, + v: *const Vec3, +) -> *const Vec3 { + if let Some(o) = unsafe { out.as_mut() } { + *o = q.load().rotate_vec3(v.load()) + } + out +} + +#[export_name = concat!("munum:wasm/quat#norm")] +pub extern "C" fn quat_norm( + out: *mut Quaternion, + q: *const Quaternion, +) -> *const Quaternion { + if let Some(o) = unsafe { out.as_mut() } { + *o = q.load(); + o.normalize(); + } + out +} + +#[export_name = concat!("munum:wasm/quat#dot")] +pub extern "C" fn quat_dot(a: *const Quaternion, b: *const Quaternion) -> f64 { + a.load().dot(b.load()) +} + +#[export_name = concat!("munum:wasm/quat#sqr-len")] +pub extern "C" fn quat_sqr_len(q: *const Quaternion) -> f64 { + q.load().sqr_len() +} + +#[export_name = concat!("munum:wasm/quat#len")] +pub extern "C" fn quat_len(q: *const Quaternion) -> f64 { + q.load().len() +} + +#[export_name = concat!("munum:wasm/quat#conj")] +pub extern "C" fn quat_conj( + out: *mut Quaternion, + q: *const Quaternion, +) -> *const Quaternion { + if let Some(o) = unsafe { out.as_mut() } { + *o = q.load(); + o.conj(); + } + out +} + +#[export_name = concat!("munum:wasm/quat#invert")] +pub extern "C" fn quat_invert(out: *mut Quaternion, q: *const Quaternion) -> bool { + if let Some(o) = unsafe { out.as_mut() } { + *o = q.load(); + o.invert() + } else { + false + } +} + +#[export_name = concat!("munum:wasm/quat#lerp")] +pub extern "C" fn quat_lerp( + out: *mut Quaternion, + a: *const Quaternion, + b: *const Quaternion, + t: f64, +) -> *const Quaternion { + if let Some(o) = unsafe { out.as_mut() } { + *o = a.load().lerp(b.load(), t); + } + out +} + +#[export_name = concat!("munum:wasm/quat#slerp")] +pub extern "C" fn quat_slerp( + out: *mut Quaternion, + a: *const Quaternion, + b: *const Quaternion, + t: f64, +) -> *const Quaternion { + if let Some(o) = unsafe { out.as_mut() } { + *o = a.load().slerp(b.load(), t); + } + out +} diff --git a/src/wasm/vec.rs b/src/wasm/vec.rs index a4450ae..327e554 100644 --- a/src/wasm/vec.rs +++ b/src/wasm/vec.rs @@ -1,12 +1,18 @@ use super::ptr::Load; use crate::{Mat2, Mat3, Mat4, Vec2, Vec3, Vec4}; +use alloc::boxed::Box; use paste::paste; macro_rules! export_vec { ($name:ident, $vec_type:ty, $mat_type:ty, $t:ty) => { paste! { + #[export_name = concat!("munum:wasm/", stringify!($name), "#free")] + pub extern "C" fn [<$name _free>](ptr: *mut $vec_type) { + drop(unsafe { Box::from_raw(ptr) }) + } + #[export_name = concat!("munum:wasm/", stringify!($name), "#get")] - pub extern "C" fn [<$name _get>](ptr: *const $t) -> *const $t { + pub extern "C" fn [<$name _get>](ptr: *const $vec_type) -> *const $vec_type { ptr } @@ -105,6 +111,11 @@ export_vec!(vec2, Vec2, Mat2, f64); export_vec!(vec3, Vec3, Mat3, f64); export_vec!(vec4, Vec4, Mat4, f64); +#[export_name = "munum:wasm/vec2#create"] +pub extern "C" fn vec2_create(x: f64, y: f64) -> *const Vec2 { + Box::into_raw(Box::new(Vec2::new([[x, y]]))) +} + #[export_name = "munum:wasm/vec2#set"] pub extern "C" fn vec2_set(out: *mut Vec2, x: f64, y: f64) -> *const Vec2 { if let Some(v) = unsafe { out.as_mut() } { @@ -114,6 +125,11 @@ pub extern "C" fn vec2_set(out: *mut Vec2, x: f64, y: f64) -> *const Vec2 *const Vec3 { + Box::into_raw(Box::new(Vec3::new([[x, y, z]]))) +} + #[export_name = "munum:wasm/vec3#set"] pub extern "C" fn vec3_set(out: *mut Vec3, x: f64, y: f64, z: f64) -> *const Vec3 { if let Some(v) = unsafe { out.as_mut() } { @@ -124,6 +140,11 @@ pub extern "C" fn vec3_set(out: *mut Vec3, x: f64, y: f64, z: f64) -> *cons out } +#[export_name = "munum:wasm/vec4#create"] +pub extern "C" fn vec4_create(x: f64, y: f64, z: f64, w: f64) -> *const Vec4 { + Box::into_raw(Box::new(Vec4::new([[x, y, z, w]]))) +} + #[export_name = "munum:wasm/vec4#set"] pub extern "C" fn vec4_set( out: *mut Vec4, diff --git a/wit/world.wit b/wit/world.wit index 01fed70..841d018 100644 --- a/wit/world.wit +++ b/wit/world.wit @@ -1,4 +1,4 @@ -package munum/wasm; +package munum:wasm; interface vec2 { type pvec2 = u32; @@ -90,7 +90,7 @@ interface quat { sqr-len: func(a: pquat) -> f64; len: func(a: pquat) -> f64; conj: func(out: pquat, a: pquat) -> pquat; - invert: func(out: pquat, a: pquat) -> pquat; + invert: func(out: pquat, a: pquat) -> bool; lerp: func(out: pquat, a: pquat, b: pquat, t: f64) -> pquat; slerp: func(out: pquat, a: pquat, b: pquat, t: f64) -> pquat; }