From f9631a0bbd8c3ec71fb1dc3e3a87ad4055901764 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?charlotte=20=F0=9F=8C=B8?= Date: Fri, 20 Mar 2026 19:00:06 -0700 Subject: [PATCH 1/2] Add math module. --- crates/processing_ffi/src/lib.rs | 43 +- crates/processing_pyo3/src/gltf.rs | 1 - crates/processing_pyo3/src/graphics.rs | 71 +- crates/processing_pyo3/src/lib.rs | 76 ++- crates/processing_pyo3/src/material.rs | 13 + crates/processing_pyo3/src/math.rs | 643 ++++++++++++++++++ .../src/geometry/attribute.rs | 18 +- crates/processing_render/src/geometry/mod.rs | 12 +- crates/processing_render/src/lib.rs | 89 +-- .../processing_render/src/render/command.rs | 10 +- crates/processing_render/src/render/mod.rs | 4 +- crates/processing_render/src/transform.rs | 28 +- crates/processing_wasm/src/lib.rs | 35 +- examples/animated_mesh.rs | 21 +- examples/box.rs | 5 +- examples/custom_attribute.rs | 19 +- examples/custom_material.rs | 5 +- examples/gltf_load.rs | 5 +- examples/lights.rs | 17 +- examples/materials.rs | 19 +- examples/pbr.rs | 19 +- examples/stroke_3d.rs | 13 +- examples/transforms.rs | 14 +- tools/generate_stubs/src/main.rs | 3 +- 24 files changed, 938 insertions(+), 245 deletions(-) create mode 100644 crates/processing_pyo3/src/math.rs diff --git a/crates/processing_ffi/src/lib.rs b/crates/processing_ffi/src/lib.rs index 361ef65..ac2ff98 100644 --- a/crates/processing_ffi/src/lib.rs +++ b/crates/processing_ffi/src/lib.rs @@ -1,4 +1,5 @@ use bevy::{ + math::{Vec2, Vec3, Vec4}, prelude::Entity, render::render_resource::{Extent3d, TextureFormat}, }; @@ -376,7 +377,12 @@ pub extern "C" fn processing_reset_matrix(graphics_id: u64) { pub extern "C" fn processing_translate(graphics_id: u64, x: f32, y: f32) { error::clear_error(); let graphics_entity = Entity::from_bits(graphics_id); - error::check(|| graphics_record_command(graphics_entity, DrawCommand::Translate { x, y })); + error::check(|| { + graphics_record_command( + graphics_entity, + DrawCommand::Translate(bevy::math::Vec2::new(x, y)), + ) + }); } /// Rotate the coordinate system. @@ -400,7 +406,12 @@ pub extern "C" fn processing_rotate(graphics_id: u64, angle: f32) { pub extern "C" fn processing_scale(graphics_id: u64, x: f32, y: f32) { error::clear_error(); let graphics_entity = Entity::from_bits(graphics_id); - error::check(|| graphics_record_command(graphics_entity, DrawCommand::Scale { x, y })); + error::check(|| { + graphics_record_command( + graphics_entity, + DrawCommand::Scale(bevy::math::Vec2::new(x, y)), + ) + }); } /// Shear along the X axis. @@ -625,21 +636,21 @@ pub extern "C" fn processing_ortho( pub extern "C" fn processing_transform_set_position(entity_id: u64, x: f32, y: f32, z: f32) { error::clear_error(); let entity = Entity::from_bits(entity_id); - error::check(|| transform_set_position(entity, x, y, z)); + error::check(|| transform_set_position(entity, Vec3::new(x, y, z))); } #[unsafe(no_mangle)] pub extern "C" fn processing_transform_translate(entity_id: u64, x: f32, y: f32, z: f32) { error::clear_error(); let entity = Entity::from_bits(entity_id); - error::check(|| transform_translate(entity, x, y, z)); + error::check(|| transform_translate(entity, Vec3::new(x, y, z))); } #[unsafe(no_mangle)] pub extern "C" fn processing_transform_set_rotation(entity_id: u64, x: f32, y: f32, z: f32) { error::clear_error(); let entity = Entity::from_bits(entity_id); - error::check(|| transform_set_rotation(entity, x, y, z)); + error::check(|| transform_set_rotation(entity, Vec3::new(x, y, z))); } #[unsafe(no_mangle)] @@ -673,21 +684,21 @@ pub extern "C" fn processing_transform_rotate_axis( ) { error::clear_error(); let entity = Entity::from_bits(entity_id); - error::check(|| transform_rotate_axis(entity, angle, axis_x, axis_y, axis_z)); + error::check(|| transform_rotate_axis(entity, angle, Vec3::new(axis_x, axis_y, axis_z))); } #[unsafe(no_mangle)] pub extern "C" fn processing_transform_set_scale(entity_id: u64, x: f32, y: f32, z: f32) { error::clear_error(); let entity = Entity::from_bits(entity_id); - error::check(|| transform_set_scale(entity, x, y, z)); + error::check(|| transform_set_scale(entity, Vec3::new(x, y, z))); } #[unsafe(no_mangle)] pub extern "C" fn processing_transform_scale(entity_id: u64, x: f32, y: f32, z: f32) { error::clear_error(); let entity = Entity::from_bits(entity_id); - error::check(|| transform_scale(entity, x, y, z)); + error::check(|| transform_scale(entity, Vec3::new(x, y, z))); } #[unsafe(no_mangle)] @@ -699,7 +710,7 @@ pub extern "C" fn processing_transform_look_at( ) { error::clear_error(); let entity = Entity::from_bits(entity_id); - error::check(|| transform_look_at(entity, target_x, target_y, target_z)); + error::check(|| transform_look_at(entity, Vec3::new(target_x, target_y, target_z))); } #[unsafe(no_mangle)] @@ -808,14 +819,14 @@ pub extern "C" fn processing_geometry_create(topology: u8) -> u64 { pub extern "C" fn processing_geometry_normal(geo_id: u64, nx: f32, ny: f32, nz: f32) { error::clear_error(); let entity = Entity::from_bits(geo_id); - error::check(|| geometry_normal(entity, nx, ny, nz)); + error::check(|| geometry_normal(entity, Vec3::new(nx, ny, nz))); } #[unsafe(no_mangle)] pub extern "C" fn processing_geometry_color(geo_id: u64, r: f32, g: f32, b: f32, a: f32) { error::clear_error(); let entity = Entity::from_bits(geo_id); - error::check(|| geometry_color(entity, r, g, b, a)); + error::check(|| geometry_color(entity, Vec4::new(r, g, b, a))); } #[unsafe(no_mangle)] @@ -932,7 +943,7 @@ pub extern "C" fn processing_geometry_attribute_float4( pub extern "C" fn processing_geometry_vertex(geo_id: u64, x: f32, y: f32, z: f32) { error::clear_error(); let entity = Entity::from_bits(geo_id); - error::check(|| geometry_vertex(entity, x, y, z)); + error::check(|| geometry_vertex(entity, Vec3::new(x, y, z))); } #[unsafe(no_mangle)] @@ -1075,7 +1086,7 @@ pub unsafe extern "C" fn processing_geometry_get_indices( pub extern "C" fn processing_geometry_set_vertex(geo_id: u64, index: u32, x: f32, y: f32, z: f32) { error::clear_error(); let entity = Entity::from_bits(geo_id); - error::check(|| geometry_set_vertex(entity, index, x, y, z)); + error::check(|| geometry_set_vertex(entity, index, Vec3::new(x, y, z))); } #[unsafe(no_mangle)] @@ -1088,7 +1099,7 @@ pub extern "C" fn processing_geometry_set_normal( ) { error::clear_error(); let entity = Entity::from_bits(geo_id); - error::check(|| geometry_set_normal(entity, index, nx, ny, nz)); + error::check(|| geometry_set_normal(entity, index, Vec3::new(nx, ny, nz))); } #[unsafe(no_mangle)] @@ -1102,14 +1113,14 @@ pub extern "C" fn processing_geometry_set_color( ) { error::clear_error(); let entity = Entity::from_bits(geo_id); - error::check(|| geometry_set_color(entity, index, r, g, b, a)); + error::check(|| geometry_set_color(entity, index, Vec4::new(r, g, b, a))); } #[unsafe(no_mangle)] pub extern "C" fn processing_geometry_set_uv(geo_id: u64, index: u32, u: f32, v: f32) { error::clear_error(); let entity = Entity::from_bits(geo_id); - error::check(|| geometry_set_uv(entity, index, u, v)); + error::check(|| geometry_set_uv(entity, index, Vec2::new(u, v))); } #[unsafe(no_mangle)] diff --git a/crates/processing_pyo3/src/gltf.rs b/crates/processing_pyo3/src/gltf.rs index ec86a3c..6fcc6fd 100644 --- a/crates/processing_pyo3/src/gltf.rs +++ b/crates/processing_pyo3/src/gltf.rs @@ -48,4 +48,3 @@ impl Gltf { Ok(Light { entity }) } } - diff --git a/crates/processing_pyo3/src/graphics.rs b/crates/processing_pyo3/src/graphics.rs index 477d24f..1169c4a 100644 --- a/crates/processing_pyo3/src/graphics.rs +++ b/crates/processing_pyo3/src/graphics.rs @@ -5,7 +5,11 @@ use bevy::{ render::render_resource::TextureFormat, }; use processing::prelude::*; -use pyo3::{exceptions::PyRuntimeError, prelude::*, types::PyDict}; +use pyo3::{ + exceptions::PyRuntimeError, + prelude::*, + types::{PyDict, PyTuple}, +}; use crate::glfw::GlfwContext; @@ -39,13 +43,16 @@ pub struct Light { #[pymethods] impl Light { - pub fn position(&self, x: f32, y: f32, z: f32) -> PyResult<()> { - transform_set_position(self.entity, x, y, z) - .map_err(|e| PyRuntimeError::new_err(format!("{e}"))) + #[pyo3(signature = (*args))] + pub fn position(&self, args: &Bound<'_, PyTuple>) -> PyResult<()> { + let v = crate::math::extract_vec3(args)?; + transform_set_position(self.entity, v).map_err(|e| PyRuntimeError::new_err(format!("{e}"))) } - pub fn look_at(&self, x: f32, y: f32, z: f32) -> PyResult<()> { - transform_look_at(self.entity, x, y, z).map_err(|e| PyRuntimeError::new_err(format!("{e}"))) + #[pyo3(signature = (*args))] + pub fn look_at(&self, args: &Bound<'_, PyTuple>) -> PyResult<()> { + let v = crate::math::extract_vec3(args)?; + transform_look_at(self.entity, v).map_err(|e| PyRuntimeError::new_err(format!("{e}"))) } } @@ -122,26 +129,32 @@ impl Geometry { Ok(Self { entity: geometry }) } - pub fn color(&self, r: f32, g: f32, b: f32, a: f32) -> PyResult<()> { - geometry_color(self.entity, r, g, b, a).map_err(|e| PyRuntimeError::new_err(format!("{e}"))) + #[pyo3(signature = (*args))] + pub fn color(&self, args: &Bound<'_, PyTuple>) -> PyResult<()> { + let v = crate::math::extract_vec4(args)?; + geometry_color(self.entity, v).map_err(|e| PyRuntimeError::new_err(format!("{e}"))) } - pub fn normal(&self, nx: f32, ny: f32, nz: f32) -> PyResult<()> { - geometry_normal(self.entity, nx, ny, nz) - .map_err(|e| PyRuntimeError::new_err(format!("{e}"))) + #[pyo3(signature = (*args))] + pub fn normal(&self, args: &Bound<'_, PyTuple>) -> PyResult<()> { + let v = crate::math::extract_vec3(args)?; + geometry_normal(self.entity, v).map_err(|e| PyRuntimeError::new_err(format!("{e}"))) } - pub fn vertex(&self, x: f32, y: f32, z: f32) -> PyResult<()> { - geometry_vertex(self.entity, x, y, z).map_err(|e| PyRuntimeError::new_err(format!("{e}"))) + #[pyo3(signature = (*args))] + pub fn vertex(&self, args: &Bound<'_, PyTuple>) -> PyResult<()> { + let v = crate::math::extract_vec3(args)?; + geometry_vertex(self.entity, v).map_err(|e| PyRuntimeError::new_err(format!("{e}"))) } pub fn index(&self, i: u32) -> PyResult<()> { geometry_index(self.entity, i).map_err(|e| PyRuntimeError::new_err(format!("{e}"))) } - pub fn set_vertex(&self, i: u32, x: f32, y: f32, z: f32) -> PyResult<()> { - geometry_set_vertex(self.entity, i, x, y, z) - .map_err(|e| PyRuntimeError::new_err(format!("{e}"))) + #[pyo3(signature = (i, *args))] + pub fn set_vertex(&self, i: u32, args: &Bound<'_, PyTuple>) -> PyResult<()> { + let v = crate::math::extract_vec3(args)?; + geometry_set_vertex(self.entity, i, v).map_err(|e| PyRuntimeError::new_err(format!("{e}"))) } } @@ -388,8 +401,10 @@ impl Graphics { .map_err(|e| PyRuntimeError::new_err(format!("{e}"))) } - pub fn translate(&self, x: f32, y: f32) -> PyResult<()> { - graphics_record_command(self.entity, DrawCommand::Translate { x, y }) + #[pyo3(signature = (*args))] + pub fn translate(&self, args: &Bound<'_, PyTuple>) -> PyResult<()> { + let v = crate::math::extract_vec2(args)?; + graphics_record_command(self.entity, DrawCommand::Translate(v)) .map_err(|e| PyRuntimeError::new_err(format!("{e}"))) } @@ -454,8 +469,10 @@ impl Graphics { .map_err(|e| PyRuntimeError::new_err(format!("{e}"))) } - pub fn scale(&self, x: f32, y: f32) -> PyResult<()> { - graphics_record_command(self.entity, DrawCommand::Scale { x, y }) + #[pyo3(signature = (*args))] + pub fn scale(&self, args: &Bound<'_, PyTuple>) -> PyResult<()> { + let v = crate::math::extract_vec2(args)?; + graphics_record_command(self.entity, DrawCommand::Scale(v)) .map_err(|e| PyRuntimeError::new_err(format!("{e}"))) } @@ -494,14 +511,16 @@ impl Graphics { graphics_mode_2d(self.entity).map_err(|e| PyRuntimeError::new_err(format!("{e}"))) } - pub fn camera_position(&self, x: f32, y: f32, z: f32) -> PyResult<()> { - transform_set_position(self.entity, x, y, z) - .map_err(|e| PyRuntimeError::new_err(format!("{e}"))) + #[pyo3(signature = (*args))] + pub fn camera_position(&self, args: &Bound<'_, PyTuple>) -> PyResult<()> { + let v = crate::math::extract_vec3(args)?; + transform_set_position(self.entity, v).map_err(|e| PyRuntimeError::new_err(format!("{e}"))) } - pub fn camera_look_at(&self, target_x: f32, target_y: f32, target_z: f32) -> PyResult<()> { - transform_look_at(self.entity, target_x, target_y, target_z) - .map_err(|e| PyRuntimeError::new_err(format!("{e}"))) + #[pyo3(signature = (*args))] + pub fn camera_look_at(&self, args: &Bound<'_, PyTuple>) -> PyResult<()> { + let v = crate::math::extract_vec3(args)?; + transform_look_at(self.entity, v).map_err(|e| PyRuntimeError::new_err(format!("{e}"))) } pub fn perspective(&self, fov: f32, aspect: f32, near: f32, far: f32) -> PyResult<()> { diff --git a/crates/processing_pyo3/src/lib.rs b/crates/processing_pyo3/src/lib.rs index f43a1cf..3b85bcc 100644 --- a/crates/processing_pyo3/src/lib.rs +++ b/crates/processing_pyo3/src/lib.rs @@ -12,12 +12,13 @@ mod glfw; mod gltf; mod graphics; pub(crate) mod material; +pub(crate) mod math; mod midi; pub(crate) mod shader; #[cfg(feature = "webcam")] mod webcam; -use graphics::{get_graphics, get_graphics_mut, Geometry, Graphics, Image, Light, Topology}; +use graphics::{Geometry, Graphics, Image, Light, Topology, get_graphics, get_graphics_mut}; use material::Material; use pyo3::{ @@ -111,6 +112,10 @@ fn detect_environment(py: Python<'_>) -> PyResult { mod mewnala { use super::*; + #[pymodule_export] + use super::Geometry; + #[pymodule_export] + use super::Gltf; #[pymodule_export] use super::Graphics; #[pymodule_export] @@ -118,15 +123,11 @@ mod mewnala { #[pymodule_export] use super::Light; #[pymodule_export] - use super::Topology; - #[pymodule_export] use super::Material; #[pymodule_export] - use super::Gltf; - #[pymodule_export] - use super::Geometry; - #[pymodule_export] use super::Shader; + #[pymodule_export] + use super::Topology; #[pymodule_export] const ROUND: u8 = 0; @@ -139,6 +140,46 @@ mod mewnala { #[pymodule_export] const BEVEL: u8 = 2; + #[pymodule] + mod math { + use super::*; + + #[pymodule_export] + use super::super::math::PyQuat; + #[pymodule_export] + use super::super::math::PyVec2; + #[pymodule_export] + use super::super::math::PyVec3; + #[pymodule_export] + use super::super::math::PyVec4; + #[pymodule_export] + use super::super::math::PyVecIter; + + #[pyfunction] + #[pyo3(signature = (*args))] + fn vec2(args: &Bound<'_, PyTuple>) -> PyResult { + super::super::math::PyVec2::py_new(args) + } + + #[pyfunction] + #[pyo3(signature = (*args))] + fn vec3(args: &Bound<'_, PyTuple>) -> PyResult { + super::super::math::PyVec3::py_new(args) + } + + #[pyfunction] + #[pyo3(signature = (*args))] + fn vec4(args: &Bound<'_, PyTuple>) -> PyResult { + super::super::math::PyVec4::py_new(args) + } + + #[pyfunction] + #[pyo3(signature = (*args))] + fn quat(args: &Bound<'_, PyTuple>) -> PyResult { + super::super::math::PyQuat::py_new(args) + } + } + #[cfg(feature = "webcam")] #[pymodule_export] use super::webcam::Webcam; @@ -146,8 +187,8 @@ mod mewnala { #[pyfunction] #[pyo3(pass_module)] fn load_gltf(module: &Bound<'_, PyModule>, path: &str) -> PyResult { - let graphics = get_graphics(module)? - .ok_or_else(|| PyRuntimeError::new_err("call size() first"))?; + let graphics = + get_graphics(module)?.ok_or_else(|| PyRuntimeError::new_err("call size() first"))?; let entity = ::processing::prelude::gltf_load(graphics.entity, path) .map_err(|e| PyRuntimeError::new_err(format!("{e}")))?; Ok(Gltf::from_entity(entity)) @@ -352,20 +393,15 @@ mod mewnala { } #[pyfunction] - #[pyo3(pass_module)] - fn camera_position(module: &Bound<'_, PyModule>, x: f32, y: f32, z: f32) -> PyResult<()> { - graphics!(module).camera_position(x, y, z) + #[pyo3(pass_module, signature = (*args))] + fn camera_position(module: &Bound<'_, PyModule>, args: &Bound<'_, PyTuple>) -> PyResult<()> { + graphics!(module).camera_position(args) } #[pyfunction] - #[pyo3(pass_module)] - fn camera_look_at( - module: &Bound<'_, PyModule>, - target_x: f32, - target_y: f32, - target_z: f32, - ) -> PyResult<()> { - graphics!(module).camera_look_at(target_x, target_y, target_z) + #[pyo3(pass_module, signature = (*args))] + fn camera_look_at(module: &Bound<'_, PyModule>, args: &Bound<'_, PyTuple>) -> PyResult<()> { + graphics!(module).camera_look_at(args) } #[pyfunction] diff --git a/crates/processing_pyo3/src/material.rs b/crates/processing_pyo3/src/material.rs index 354abda..757d2d9 100644 --- a/crates/processing_pyo3/src/material.rs +++ b/crates/processing_pyo3/src/material.rs @@ -3,6 +3,7 @@ use processing::prelude::*; use pyo3::types::PyDict; use pyo3::{exceptions::PyRuntimeError, prelude::*}; +use crate::math::{PyVec2, PyVec3, PyVec4}; use crate::shader::Shader; #[pyclass(unsendable)] @@ -18,6 +19,18 @@ fn py_to_material_value(value: &Bound<'_, PyAny>) -> PyResult>() { + return Ok(material::MaterialValue::Float4(v.0.to_array())); + } + if let Ok(v) = value.extract::>() { + return Ok(material::MaterialValue::Float3(v.0.to_array())); + } + if let Ok(v) = value.extract::>() { + return Ok(material::MaterialValue::Float2(v.0.to_array())); + } + + // Fall back to raw arrays if let Ok(v) = value.extract::<[f32; 4]>() { return Ok(material::MaterialValue::Float4(v)); } diff --git a/crates/processing_pyo3/src/math.rs b/crates/processing_pyo3/src/math.rs new file mode 100644 index 0000000..3009688 --- /dev/null +++ b/crates/processing_pyo3/src/math.rs @@ -0,0 +1,643 @@ +use std::hash::{Hash, Hasher}; + +use bevy::math::{EulerRot, Quat, Vec2, Vec3, Vec4}; +use pyo3::{exceptions::PyTypeError, prelude::*, types::PyTuple}; + +fn hash_f32(val: f32, state: &mut impl Hasher) { + if val == 0.0 { + 0.0f32.to_bits().hash(state); + } else { + val.to_bits().hash(state); + } +} + +#[derive(FromPyObject)] +pub(crate) enum Vec2Arg { + Instance((PyVec2,)), + Components(f32, f32), +} + +impl Vec2Arg { + pub fn into_vec2(self) -> Vec2 { + match self { + Vec2Arg::Instance((v,)) => v.0, + Vec2Arg::Components(x, y) => Vec2::new(x, y), + } + } +} + +#[derive(FromPyObject)] +pub(crate) enum Vec3Arg { + Instance((PyVec3,)), + Components(f32, f32, f32), +} + +impl Vec3Arg { + pub fn into_vec3(self) -> Vec3 { + match self { + Vec3Arg::Instance((v,)) => v.0, + Vec3Arg::Components(x, y, z) => Vec3::new(x, y, z), + } + } +} + +#[derive(FromPyObject)] +pub(crate) enum Vec4Arg { + Instance((PyVec4,)), + Components(f32, f32, f32, f32), +} + +impl Vec4Arg { + pub fn into_vec4(self) -> Vec4 { + match self { + Vec4Arg::Instance((v,)) => v.0, + Vec4Arg::Components(x, y, z, w) => Vec4::new(x, y, z, w), + } + } +} + +// Vec3Like for single-object extraction (e.g., Quat::from_axis_angle(axis, angle)) +#[derive(FromPyObject)] +pub(crate) enum Vec3Like { + Instance(PyVec3), + Tuple((f32, f32, f32)), + List([f32; 3]), +} + +impl Vec3Like { + pub fn into_vec3(self) -> Vec3 { + match self { + Vec3Like::Instance(v) => v.0, + Vec3Like::Tuple((x, y, z)) => Vec3::new(x, y, z), + Vec3Like::List(arr) => Vec3::from_array(arr), + } + } +} + +pub(crate) fn extract_vec2(args: &Bound<'_, PyTuple>) -> PyResult { + Ok(args.extract::()?.into_vec2()) +} + +pub(crate) fn extract_vec3(args: &Bound<'_, PyTuple>) -> PyResult { + Ok(args.extract::()?.into_vec3()) +} + +pub(crate) fn extract_vec4(args: &Bound<'_, PyTuple>) -> PyResult { + Ok(args.extract::()?.into_vec4()) +} + +macro_rules! impl_py_vec { + ( + $name:ident, $py_name:literal, $n:literal, + [$(($field:ident, $set_field:ident, $idx:literal)),+], + $glam_ty:ty + $(, extra { $($extra:tt)* })? + ) => { + #[pyclass(name = $py_name, from_py_object)] + #[derive(Clone, Debug)] + pub struct $name(pub(crate) $glam_ty); + + impl From<$glam_ty> for $name { + fn from(v: $glam_ty) -> Self { Self(v) } + } + + impl From<$name> for $glam_ty { + fn from(v: $name) -> Self { v.0 } + } + + #[pymethods] + impl $name { + #[new] + #[pyo3(signature = (*args))] + pub fn py_new(args: &Bound<'_, PyTuple>) -> PyResult { + match args.len() { + 0 => Ok(Self(<$glam_ty>::ZERO)), + 1 => { + let first = args.get_item(0)?; + if let Ok(s) = first.extract::() { + return Ok(Self(<$glam_ty>::splat(s))); + } + if let Ok(s) = first.extract::() { + return Ok(Self(<$glam_ty>::splat(s as f32))); + } + if let Ok(v) = first.extract::>() { + return Ok(Self(v.0)); + } + if let Ok(arr) = first.extract::<[f32; $n]>() { + return Ok(Self(<$glam_ty>::from_array(arr))); + } + Err(PyTypeError::new_err(concat!( + "expected scalar, ", $py_name, ", or sequence of ", + stringify!($n), " floats" + ))) + } + $n => { + let mut arr = [0.0f32; $n]; + $(arr[$idx] = args.get_item($idx)?.extract::()?;)+ + Ok(Self(<$glam_ty>::from_array(arr))) + } + _ => Err(PyTypeError::new_err(concat!( + $py_name, " takes 0, 1, or ", stringify!($n), " arguments" + ))), + } + } + + $( + #[getter] + fn $field(&self) -> f32 { self.0[$idx] } + + #[setter] + fn $set_field(&mut self, val: f32) { self.0[$idx] = val; } + )+ + + fn __add__(&self, other: &Self) -> Self { Self(self.0 + other.0) } + fn __radd__(&self, other: &Self) -> Self { Self(other.0 + self.0) } + fn __iadd__(&mut self, other: &Self) { self.0 += other.0; } + + fn __sub__(&self, other: &Self) -> Self { Self(self.0 - other.0) } + fn __rsub__(&self, other: &Self) -> Self { Self(other.0 - self.0) } + fn __isub__(&mut self, other: &Self) { self.0 -= other.0; } + + fn __neg__(&self) -> Self { Self(-self.0) } + + fn __mul__(&self, rhs: &Bound<'_, PyAny>) -> PyResult { + if let Ok(s) = rhs.extract::() { + return Ok(Self(self.0 * s)); + } + if let Ok(s) = rhs.extract::() { + return Ok(Self(self.0 * s as f32)); + } + if let Ok(other) = rhs.extract::>() { + return Ok(Self(self.0 * other.0)); + } + Err(PyTypeError::new_err(concat!( + "unsupported operand type(s) for *: '", $py_name, "'" + ))) + } + + fn __rmul__(&self, lhs: &Bound<'_, PyAny>) -> PyResult { + if let Ok(s) = lhs.extract::() { + return Ok(Self(self.0 * s)); + } + if let Ok(s) = lhs.extract::() { + return Ok(Self(self.0 * s as f32)); + } + Err(PyTypeError::new_err(concat!( + "unsupported operand type(s) for *: '", $py_name, "'" + ))) + } + + fn __imul__(&mut self, rhs: &Bound<'_, PyAny>) -> PyResult<()> { + if let Ok(s) = rhs.extract::() { + self.0 *= s; return Ok(()); + } + if let Ok(s) = rhs.extract::() { + self.0 *= s as f32; return Ok(()); + } + if let Ok(other) = rhs.extract::>() { + self.0 *= other.0; return Ok(()); + } + Err(PyTypeError::new_err(concat!( + "unsupported operand type(s) for *=: '", $py_name, "'" + ))) + } + + fn __truediv__(&self, rhs: &Bound<'_, PyAny>) -> PyResult { + if let Ok(s) = rhs.extract::() { + return Ok(Self(self.0 / s)); + } + if let Ok(s) = rhs.extract::() { + return Ok(Self(self.0 / s as f32)); + } + if let Ok(other) = rhs.extract::>() { + return Ok(Self(self.0 / other.0)); + } + Err(PyTypeError::new_err(concat!( + "unsupported operand type(s) for /: '", $py_name, "'" + ))) + } + + fn __rtruediv__(&self, lhs: &Bound<'_, PyAny>) -> PyResult { + if let Ok(s) = lhs.extract::() { + return Ok(Self(<$glam_ty>::splat(s) / self.0)); + } + if let Ok(s) = lhs.extract::() { + return Ok(Self(<$glam_ty>::splat(s as f32) / self.0)); + } + Err(PyTypeError::new_err(concat!( + "unsupported operand type(s) for /: '", $py_name, "'" + ))) + } + + fn __itruediv__(&mut self, rhs: &Bound<'_, PyAny>) -> PyResult<()> { + if let Ok(s) = rhs.extract::() { + self.0 /= s; return Ok(()); + } + if let Ok(s) = rhs.extract::() { + self.0 /= s as f32; return Ok(()); + } + if let Ok(other) = rhs.extract::>() { + self.0 /= other.0; return Ok(()); + } + Err(PyTypeError::new_err(concat!( + "unsupported operand type(s) for /=: '", $py_name, "'" + ))) + } + + fn __eq__(&self, other: &Self) -> bool { self.0 == other.0 } + + fn __hash__(&self) -> u64 { + let mut hasher = std::collections::hash_map::DefaultHasher::new(); + for &c in self.0.to_array().iter() { + hash_f32(c, &mut hasher); + } + std::hash::Hasher::finish(&hasher) + } + + fn __repr__(&self) -> String { + let parts: Vec = self.0.to_array().iter().map(|c| format!("{c}")).collect(); + format!("{}({})", $py_name, parts.join(", ")) + } + + fn __str__(&self) -> String { self.__repr__() } + + fn __len__(&self) -> usize { $n } + + fn __getitem__(&self, idx: isize) -> PyResult { + let idx = if idx < 0 { $n as isize + idx } else { idx }; + if idx < 0 || idx >= $n { + return Err(PyTypeError::new_err("index out of range")); + } + Ok(self.0[idx as usize]) + } + + fn __setitem__(&mut self, idx: isize, val: f32) -> PyResult<()> { + let idx = if idx < 0 { $n as isize + idx } else { idx }; + if idx < 0 || idx >= $n { + return Err(PyTypeError::new_err("index out of range")); + } + self.0[idx as usize] = val; + Ok(()) + } + + fn __iter__(slf: PyRef<'_, Self>) -> PyVecIter { + PyVecIter { + values: slf.0.to_array().to_vec(), + index: 0, + } + } + + fn length(&self) -> f32 { self.0.length() } + fn length_squared(&self) -> f32 { self.0.length_squared() } + fn normalize(&self) -> Self { Self(self.0.normalize()) } + fn dot(&self, other: &Self) -> f32 { self.0.dot(other.0) } + fn distance(&self, other: &Self) -> f32 { self.0.distance(other.0) } + fn lerp(&self, other: &Self, t: f32) -> Self { Self(self.0.lerp(other.0, t)) } + + #[pyo3(name = "min")] + fn py_min(&self, other: &Self) -> Self { Self(self.0.min(other.0)) } + #[pyo3(name = "max")] + fn py_max(&self, other: &Self) -> Self { Self(self.0.max(other.0)) } + fn clamp(&self, min: &Self, max: &Self) -> Self { Self(self.0.clamp(min.0, max.0)) } + #[pyo3(name = "abs")] + fn py_abs(&self) -> Self { Self(self.0.abs()) } + + fn to_list(&self) -> Vec { self.0.to_array().to_vec() } + + fn to_tuple<'py>(&self, py: Python<'py>) -> Bound<'py, PyTuple> { + PyTuple::new(py, self.0.to_array()).unwrap() + } + + $($($extra)*)? + } + }; +} + +impl_py_vec!(PyVec2, "Vec2", 2, [(x, set_x, 0), (y, set_y, 1)], Vec2, extra { + fn angle(&self) -> f32 { + self.0.y.atan2(self.0.x) + } + + fn rotate(&self, angle: f32) -> Self { + Self(Vec2::from_angle(angle).rotate(self.0)) + } + + fn perpendicular(&self) -> Self { + Self(self.0.perp()) + } +}); + +impl_py_vec!(PyVec3, "Vec3", 3, [(x, set_x, 0), (y, set_y, 1), (z, set_z, 2)], Vec3, extra { + fn cross(&self, other: &Self) -> Self { + Self(self.0.cross(other.0)) + } +}); + +impl_py_vec!( + PyVec4, + "Vec4", + 4, + [(x, set_x, 0), (y, set_y, 1), (z, set_z, 2), (w, set_w, 3)], + Vec4 +); + +#[pyclass(name = "Quat", from_py_object)] +#[derive(Clone, Debug)] +pub struct PyQuat(pub(crate) Quat); + +impl From for PyQuat { + fn from(q: Quat) -> Self { + Self(q) + } +} + +impl From for Quat { + fn from(q: PyQuat) -> Self { + q.0 + } +} + +#[pymethods] +impl PyQuat { + #[new] + #[pyo3(signature = (*args))] + pub fn py_new(args: &Bound<'_, PyTuple>) -> PyResult { + match args.len() { + 0 => Ok(Self(Quat::IDENTITY)), + 4 => Ok(Self(Quat::from_xyzw( + args.get_item(0)?.extract()?, + args.get_item(1)?.extract()?, + args.get_item(2)?.extract()?, + args.get_item(3)?.extract()?, + ))), + _ => Err(PyTypeError::new_err("Quat takes 0 or 4 arguments")), + } + } + + #[staticmethod] + fn identity() -> Self { + Self(Quat::IDENTITY) + } + + #[staticmethod] + fn from_rotation_x(angle: f32) -> Self { + Self(Quat::from_rotation_x(angle)) + } + + #[staticmethod] + fn from_rotation_y(angle: f32) -> Self { + Self(Quat::from_rotation_y(angle)) + } + + #[staticmethod] + fn from_rotation_z(angle: f32) -> Self { + Self(Quat::from_rotation_z(angle)) + } + + #[staticmethod] + fn from_axis_angle(axis: Vec3Like, angle: f32) -> Self { + Self(Quat::from_axis_angle(axis.into_vec3().normalize(), angle)) + } + + #[staticmethod] + fn from_euler(x: f32, y: f32, z: f32) -> Self { + Self(Quat::from_euler(EulerRot::XYZ, x, y, z)) + } + + // --- Properties (using to_array/from_array for SIMD compat) --- + #[getter] + fn x(&self) -> f32 { + self.0.x + } + #[setter] + fn set_x(&mut self, val: f32) { + let [_, y, z, w] = self.0.to_array(); + self.0 = Quat::from_xyzw(val, y, z, w); + } + + #[getter] + fn y(&self) -> f32 { + self.0.y + } + #[setter] + fn set_y(&mut self, val: f32) { + let [x, _, z, w] = self.0.to_array(); + self.0 = Quat::from_xyzw(x, val, z, w); + } + + #[getter] + fn z(&self) -> f32 { + self.0.z + } + #[setter] + fn set_z(&mut self, val: f32) { + let [x, y, _, w] = self.0.to_array(); + self.0 = Quat::from_xyzw(x, y, val, w); + } + + #[getter] + fn w(&self) -> f32 { + self.0.w + } + #[setter] + fn set_w(&mut self, val: f32) { + let [x, y, z, _] = self.0.to_array(); + self.0 = Quat::from_xyzw(x, y, z, val); + } + + fn normalize(&self) -> Self { + Self(self.0.normalize()) + } + fn inverse(&self) -> Self { + Self(self.0.inverse()) + } + fn slerp(&self, other: &Self, t: f32) -> Self { + Self(self.0.slerp(other.0, t)) + } + fn length(&self) -> f32 { + self.0.length() + } + fn dot(&self, other: &Self) -> f32 { + self.0.dot(other.0) + } + + fn mul_vec3(&self, v: Vec3Like) -> PyVec3 { + PyVec3(self.0.mul_vec3(v.into_vec3())) + } + + fn to_euler(&self) -> PyVec3 { + let (x, y, z) = self.0.to_euler(EulerRot::XYZ); + PyVec3(Vec3::new(x, y, z)) + } + + fn __mul__(&self, rhs: &Bound<'_, PyAny>) -> PyResult> { + let py = rhs.py(); + if let Ok(other) = rhs.extract::>() { + return Ok(PyQuat(self.0 * other.0) + .into_pyobject(py)? + .into_any() + .unbind()); + } + if let Ok(v) = rhs.extract::>() { + return Ok(PyVec3(self.0.mul_vec3(v.0)) + .into_pyobject(py)? + .into_any() + .unbind()); + } + Err(PyTypeError::new_err( + "unsupported operand type(s) for *: 'Quat'", + )) + } + + fn __eq__(&self, other: &Self) -> bool { + self.0 == other.0 + } + + fn __hash__(&self) -> u64 { + let mut hasher = std::collections::hash_map::DefaultHasher::new(); + for &c in self.0.to_array().iter() { + hash_f32(c, &mut hasher); + } + std::hash::Hasher::finish(&hasher) + } + + fn __repr__(&self) -> String { + format!( + "Quat({}, {}, {}, {})", + self.0.x, self.0.y, self.0.z, self.0.w + ) + } + + fn __str__(&self) -> String { + self.__repr__() + } +} + +#[pyclass] +pub struct PyVecIter { + values: Vec, + index: usize, +} + +#[pymethods] +impl PyVecIter { + fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + slf + } + + fn __next__(&mut self) -> Option { + if self.index < self.values.len() { + let val = self.values[self.index]; + self.index += 1; + Some(val) + } else { + None + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::f32::consts::FRAC_PI_2; + + #[test] + fn test_vec3_basics() { + let v = PyVec3(Vec3::new(1.0, 2.0, 3.0)); + assert_eq!(v.0.x, 1.0); + assert_eq!(v.0.y, 2.0); + assert_eq!(v.0.z, 3.0); + } + + #[test] + fn test_vec3_arithmetic() { + let a = PyVec3(Vec3::new(1.0, 2.0, 3.0)); + let b = PyVec3(Vec3::new(4.0, 5.0, 6.0)); + assert_eq!((a.0 + b.0), Vec3::new(5.0, 7.0, 9.0)); + assert_eq!((a.0 - b.0), Vec3::new(-3.0, -3.0, -3.0)); + assert_eq!((-a.0), Vec3::new(-1.0, -2.0, -3.0)); + assert_eq!((a.0 * 2.0), Vec3::new(2.0, 4.0, 6.0)); + } + + #[test] + fn test_vec3_cross() { + let a = PyVec3(Vec3::X); + let b = PyVec3(Vec3::Y); + assert_eq!(a.cross(&b).0, Vec3::Z); + } + + #[test] + fn test_vec3_normalize() { + let v = PyVec3(Vec3::new(3.0, 0.0, 0.0)); + let n = v.normalize(); + assert!((n.0.length() - 1.0).abs() < 1e-6); + assert_eq!(n.0, Vec3::X); + } + + #[test] + fn test_vec3_dot() { + let a = PyVec3(Vec3::new(1.0, 2.0, 3.0)); + let b = PyVec3(Vec3::new(4.0, 5.0, 6.0)); + assert_eq!(a.dot(&b), 32.0); + } + + #[test] + fn test_vec2_angle() { + let v = PyVec2(Vec2::X); + assert!(v.angle().abs() < 1e-6); + let v = PyVec2(Vec2::Y); + assert!((v.angle() - FRAC_PI_2).abs() < 1e-6); + } + + #[test] + fn test_vec2_perpendicular() { + let v = PyVec2(Vec2::X); + let p = v.perpendicular(); + assert!((p.0.x).abs() < 1e-6); + assert!((p.0.y - 1.0).abs() < 1e-6); + } + + #[test] + fn test_quat_rotation() { + let q = PyQuat(Quat::from_rotation_z(FRAC_PI_2)); + let rotated = q.0.mul_vec3(Vec3::X); + assert!(rotated.x.abs() < 1e-5); + assert!((rotated.y - 1.0).abs() < 1e-5); + assert!(rotated.z.abs() < 1e-5); + } + + #[test] + fn test_quat_composition() { + let q1 = Quat::from_rotation_z(FRAC_PI_2); + let q2 = Quat::from_rotation_z(FRAC_PI_2); + let composed = q1 * q2; + let v = composed.mul_vec3(Vec3::X); + assert!((v.x - (-1.0)).abs() < 1e-5); + assert!(v.y.abs() < 1e-5); + } + + #[test] + fn test_vec_conversions() { + let v = PyVec3(Vec3::new(1.0, 2.0, 3.0)); + assert_eq!(v.to_list(), vec![1.0, 2.0, 3.0]); + let glam_v: Vec3 = v.into(); + assert_eq!(glam_v, Vec3::new(1.0, 2.0, 3.0)); + let back: PyVec3 = glam_v.into(); + assert_eq!(back.0, Vec3::new(1.0, 2.0, 3.0)); + } + + #[test] + fn test_vec3_lerp() { + let a = PyVec3(Vec3::ZERO); + let b = PyVec3(Vec3::new(10.0, 10.0, 10.0)); + let mid = a.lerp(&b, 0.5); + assert_eq!(mid.0, Vec3::new(5.0, 5.0, 5.0)); + } + + #[test] + fn test_vec3_distance() { + let a = PyVec3(Vec3::ZERO); + let b = PyVec3(Vec3::new(3.0, 4.0, 0.0)); + assert!((a.distance(&b) - 5.0).abs() < 1e-6); + } +} diff --git a/crates/processing_render/src/geometry/attribute.rs b/crates/processing_render/src/geometry/attribute.rs index 27395b5..ecef0b2 100644 --- a/crates/processing_render/src/geometry/attribute.rs +++ b/crates/processing_render/src/geometry/attribute.rs @@ -84,9 +84,9 @@ pub fn get_indices( } macro_rules! impl_setter { - ($name:ident, $attr:expr, $variant:ident, [$($arg:ident: $arg_ty:ty),+], $arr:expr) => { + ($name:ident, $attr:expr, $variant:ident, $vec_ty:ty) => { pub fn $name( - In((entity, index, $($arg),+)): In<(Entity, u32, $($arg_ty),+)>, + In((entity, index, value)): In<(Entity, u32, $vec_ty)>, geometries: Query<&Geometry>, mut meshes: ResMut>, ) -> Result<()> { @@ -95,11 +95,13 @@ macro_rules! impl_setter { Some(VertexAttributeValues::$variant(data)) => { let idx = index as usize; if idx < data.len() { - data[idx] = $arr; + data[idx] = value.to_array(); Ok(()) } else { Err(ProcessingError::InvalidArgument(format!( - "Index {} out of bounds (count: {})", index, data.len() + "Index {} out of bounds (count: {})", + index, + data.len() ))) } } @@ -114,10 +116,10 @@ macro_rules! impl_setter { }; } -impl_setter!(set_vertex, Mesh::ATTRIBUTE_POSITION, Float32x3, [x: f32, y: f32, z: f32], [x, y, z]); -impl_setter!(set_normal, Mesh::ATTRIBUTE_NORMAL, Float32x3, [nx: f32, ny: f32, nz: f32], [nx, ny, nz]); -impl_setter!(set_color, Mesh::ATTRIBUTE_COLOR, Float32x4, [r: f32, g: f32, b: f32, a: f32], [r, g, b, a]); -impl_setter!(set_uv, Mesh::ATTRIBUTE_UV_0, Float32x2, [u: f32, v: f32], [u, v]); +impl_setter!(set_vertex, Mesh::ATTRIBUTE_POSITION, Float32x3, Vec3); +impl_setter!(set_normal, Mesh::ATTRIBUTE_NORMAL, Float32x3, Vec3); +impl_setter!(set_color, Mesh::ATTRIBUTE_COLOR, Float32x4, Vec4); +impl_setter!(set_uv, Mesh::ATTRIBUTE_UV_0, Float32x2, Vec2); #[derive(Clone, Debug)] pub enum AttributeValue { diff --git a/crates/processing_render/src/geometry/mod.rs b/crates/processing_render/src/geometry/mod.rs index fc8d4b9..4393bcd 100644 --- a/crates/processing_render/src/geometry/mod.rs +++ b/crates/processing_render/src/geometry/mod.rs @@ -193,19 +193,19 @@ pub fn create_sphere( commands.spawn(Geometry::new(handle, layout_entity)).id() } -pub fn normal(world: &mut World, entity: Entity, nx: f32, ny: f32, nz: f32) -> Result<()> { +pub fn normal(world: &mut World, entity: Entity, normal: Vec3) -> Result<()> { let mut geometry = world .get_mut::(entity) .ok_or(ProcessingError::GeometryNotFound)?; - geometry.current_normal = [nx, ny, nz]; + geometry.current_normal = normal.to_array(); Ok(()) } -pub fn color(world: &mut World, entity: Entity, r: f32, g: f32, b: f32, a: f32) -> Result<()> { +pub fn color(world: &mut World, entity: Entity, color: Vec4) -> Result<()> { let mut geometry = world .get_mut::(entity) .ok_or(ProcessingError::GeometryNotFound)?; - geometry.current_color = [r, g, b, a]; + geometry.current_color = color.to_array(); Ok(()) } @@ -235,7 +235,7 @@ pub fn attribute( } pub fn vertex( - In((entity, x, y, z)): In<(Entity, f32, f32, f32)>, + In((entity, position)): In<(Entity, Vec3)>, geometries: Query<&Geometry>, layouts: Query<&VertexLayout>, attrs: Query<&Attribute>, @@ -258,7 +258,7 @@ pub fn vertex( if let Some(VertexAttributeValues::Float32x3(positions)) = mesh.attribute_mut(Mesh::ATTRIBUTE_POSITION) { - positions.push([x, y, z]); + positions.push(position.to_array()); } if layout.has_attribute(builtins.normal) diff --git a/crates/processing_render/src/lib.rs b/crates/processing_render/src/lib.rs index 99fece9..05c7e56 100644 --- a/crates/processing_render/src/lib.rs +++ b/crates/processing_render/src/lib.rs @@ -455,26 +455,26 @@ pub fn graphics_ortho( }) } -pub fn transform_set_position(entity: Entity, x: f32, y: f32, z: f32) -> error::Result<()> { +pub fn transform_set_position(entity: Entity, position: Vec3) -> error::Result<()> { app_mut(|app| { app.world_mut() - .run_system_cached_with(transform::set_position, (entity, x, y, z)) + .run_system_cached_with(transform::set_position, (entity, position)) .unwrap() }) } -pub fn transform_translate(entity: Entity, x: f32, y: f32, z: f32) -> error::Result<()> { +pub fn transform_translate(entity: Entity, offset: Vec3) -> error::Result<()> { app_mut(|app| { app.world_mut() - .run_system_cached_with(transform::translate, (entity, x, y, z)) + .run_system_cached_with(transform::translate, (entity, offset)) .unwrap() }) } -pub fn transform_set_rotation(entity: Entity, x: f32, y: f32, z: f32) -> error::Result<()> { +pub fn transform_set_rotation(entity: Entity, euler: Vec3) -> error::Result<()> { app_mut(|app| { app.world_mut() - .run_system_cached_with(transform::set_rotation, (entity, x, y, z)) + .run_system_cached_with(transform::set_rotation, (entity, euler)) .unwrap() }) } @@ -503,48 +503,34 @@ pub fn transform_rotate_z(entity: Entity, angle: f32) -> error::Result<()> { }) } -pub fn transform_rotate_axis( - entity: Entity, - angle: f32, - axis_x: f32, - axis_y: f32, - axis_z: f32, -) -> error::Result<()> { +pub fn transform_rotate_axis(entity: Entity, angle: f32, axis: Vec3) -> error::Result<()> { app_mut(|app| { app.world_mut() - .run_system_cached_with( - transform::rotate_axis, - (entity, angle, axis_x, axis_y, axis_z), - ) + .run_system_cached_with(transform::rotate_axis, (entity, angle, axis)) .unwrap() }) } -pub fn transform_set_scale(entity: Entity, x: f32, y: f32, z: f32) -> error::Result<()> { +pub fn transform_set_scale(entity: Entity, scale: Vec3) -> error::Result<()> { app_mut(|app| { app.world_mut() - .run_system_cached_with(transform::set_scale, (entity, x, y, z)) + .run_system_cached_with(transform::set_scale, (entity, scale)) .unwrap() }) } -pub fn transform_scale(entity: Entity, x: f32, y: f32, z: f32) -> error::Result<()> { +pub fn transform_scale(entity: Entity, factor: Vec3) -> error::Result<()> { app_mut(|app| { app.world_mut() - .run_system_cached_with(transform::scale, (entity, x, y, z)) + .run_system_cached_with(transform::scale, (entity, factor)) .unwrap() }) } -pub fn transform_look_at( - entity: Entity, - target_x: f32, - target_y: f32, - target_z: f32, -) -> error::Result<()> { +pub fn transform_look_at(entity: Entity, target: Vec3) -> error::Result<()> { app_mut(|app| { app.world_mut() - .run_system_cached_with(transform::look_at, (entity, target_x, target_y, target_z)) + .run_system_cached_with(transform::look_at, (entity, target)) .unwrap() }) } @@ -850,12 +836,12 @@ pub fn geometry_create_with_layout( }) } -pub fn geometry_normal(entity: Entity, nx: f32, ny: f32, nz: f32) -> error::Result<()> { - app_mut(|app| geometry::normal(app.world_mut(), entity, nx, ny, nz)) +pub fn geometry_normal(entity: Entity, normal: Vec3) -> error::Result<()> { + app_mut(|app| geometry::normal(app.world_mut(), entity, normal)) } -pub fn geometry_color(entity: Entity, r: f32, g: f32, b: f32, a: f32) -> error::Result<()> { - app_mut(|app| geometry::color(app.world_mut(), entity, r, g, b, a)) +pub fn geometry_color(entity: Entity, color: Vec4) -> error::Result<()> { + app_mut(|app| geometry::color(app.world_mut(), entity, color)) } pub fn geometry_uv(entity: Entity, u: f32, v: f32) -> error::Result<()> { @@ -912,10 +898,10 @@ pub fn geometry_attribute_float4( ) } -pub fn geometry_vertex(entity: Entity, x: f32, y: f32, z: f32) -> error::Result<()> { +pub fn geometry_vertex(entity: Entity, position: Vec3) -> error::Result<()> { app_mut(|app| { app.world_mut() - .run_system_cached_with(geometry::vertex, (entity, x, y, z)) + .run_system_cached_with(geometry::vertex, (entity, position)) .unwrap() }) } @@ -1004,53 +990,34 @@ pub fn geometry_destroy(entity: Entity) -> error::Result<()> { }) } -pub fn geometry_set_vertex( - entity: Entity, - index: u32, - x: f32, - y: f32, - z: f32, -) -> error::Result<()> { +pub fn geometry_set_vertex(entity: Entity, index: u32, position: Vec3) -> error::Result<()> { app_mut(|app| { app.world_mut() - .run_system_cached_with(geometry::set_vertex, (entity, index, x, y, z)) + .run_system_cached_with(geometry::set_vertex, (entity, index, position)) .unwrap() }) } -pub fn geometry_set_normal( - entity: Entity, - index: u32, - nx: f32, - ny: f32, - nz: f32, -) -> error::Result<()> { +pub fn geometry_set_normal(entity: Entity, index: u32, normal: Vec3) -> error::Result<()> { app_mut(|app| { app.world_mut() - .run_system_cached_with(geometry::set_normal, (entity, index, nx, ny, nz)) + .run_system_cached_with(geometry::set_normal, (entity, index, normal)) .unwrap() }) } -pub fn geometry_set_color( - entity: Entity, - index: u32, - r: f32, - g: f32, - b: f32, - a: f32, -) -> error::Result<()> { +pub fn geometry_set_color(entity: Entity, index: u32, color: Vec4) -> error::Result<()> { app_mut(|app| { app.world_mut() - .run_system_cached_with(geometry::set_color, (entity, index, r, g, b, a)) + .run_system_cached_with(geometry::set_color, (entity, index, color)) .unwrap() }) } -pub fn geometry_set_uv(entity: Entity, index: u32, u: f32, v: f32) -> error::Result<()> { +pub fn geometry_set_uv(entity: Entity, index: u32, uv: Vec2) -> error::Result<()> { app_mut(|app| { app.world_mut() - .run_system_cached_with(geometry::set_uv, (entity, index, u, v)) + .run_system_cached_with(geometry::set_uv, (entity, index, uv)) .unwrap() }) } diff --git a/crates/processing_render/src/render/command.rs b/crates/processing_render/src/render/command.rs index 2ebb9e7..19016e0 100644 --- a/crates/processing_render/src/render/command.rs +++ b/crates/processing_render/src/render/command.rs @@ -65,17 +65,11 @@ pub enum DrawCommand { PushMatrix, PopMatrix, ResetMatrix, - Translate { - x: f32, - y: f32, - }, + Translate(Vec2), Rotate { angle: f32, }, - Scale { - x: f32, - y: f32, - }, + Scale(Vec2), ShearX { angle: f32, }, diff --git a/crates/processing_render/src/render/mod.rs b/crates/processing_render/src/render/mod.rs index fe10a0b..f8aeb7f 100644 --- a/crates/processing_render/src/render/mod.rs +++ b/crates/processing_render/src/render/mod.rs @@ -325,9 +325,9 @@ pub fn flush_draw_commands( DrawCommand::PushMatrix => state.transform.push(), DrawCommand::PopMatrix => state.transform.pop(), DrawCommand::ResetMatrix => state.transform.reset(), - DrawCommand::Translate { x, y } => state.transform.translate(x, y), + DrawCommand::Translate(v) => state.transform.translate(v.x, v.y), DrawCommand::Rotate { angle } => state.transform.rotate(angle), - DrawCommand::Scale { x, y } => state.transform.scale(x, y), + DrawCommand::Scale(v) => state.transform.scale(v.x, v.y), DrawCommand::ShearX { angle } => state.transform.shear_x(angle), DrawCommand::ShearY { angle } => state.transform.shear_y(angle), DrawCommand::Geometry(entity) => { diff --git a/crates/processing_render/src/transform.rs b/crates/processing_render/src/transform.rs index 5b902c5..1e8c2d9 100644 --- a/crates/processing_render/src/transform.rs +++ b/crates/processing_render/src/transform.rs @@ -3,35 +3,35 @@ use bevy::prelude::*; use processing_core::error::{ProcessingError, Result}; pub fn set_position( - In((entity, x, y, z)): In<(Entity, f32, f32, f32)>, + In((entity, position)): In<(Entity, Vec3)>, mut transforms: Query<&mut Transform>, ) -> Result<()> { let mut transform = transforms .get_mut(entity) .map_err(|_| ProcessingError::TransformNotFound)?; - transform.translation = Vec3::new(x, y, z); + transform.translation = position; Ok(()) } pub fn translate( - In((entity, x, y, z)): In<(Entity, f32, f32, f32)>, + In((entity, offset)): In<(Entity, Vec3)>, mut transforms: Query<&mut Transform>, ) -> Result<()> { let mut transform = transforms .get_mut(entity) .map_err(|_| ProcessingError::TransformNotFound)?; - transform.translation += Vec3::new(x, y, z); + transform.translation += offset; Ok(()) } pub fn set_rotation( - In((entity, x, y, z)): In<(Entity, f32, f32, f32)>, + In((entity, euler)): In<(Entity, Vec3)>, mut transforms: Query<&mut Transform>, ) -> Result<()> { let mut transform = transforms .get_mut(entity) .map_err(|_| ProcessingError::TransformNotFound)?; - transform.rotation = Quat::from_euler(EulerRot::XYZ, x, y, z); + transform.rotation = Quat::from_euler(EulerRot::XYZ, euler.x, euler.y, euler.z); Ok(()) } @@ -69,47 +69,45 @@ pub fn rotate_z( } pub fn rotate_axis( - In((entity, angle, axis_x, axis_y, axis_z)): In<(Entity, f32, f32, f32, f32)>, + In((entity, angle, axis)): In<(Entity, f32, Vec3)>, mut transforms: Query<&mut Transform>, ) -> Result<()> { let mut transform = transforms .get_mut(entity) .map_err(|_| ProcessingError::TransformNotFound)?; - let axis = Vec3::new(axis_x, axis_y, axis_z).normalize(); - transform.rotate(Quat::from_axis_angle(axis, angle)); + transform.rotate(Quat::from_axis_angle(axis.normalize(), angle)); Ok(()) } pub fn set_scale( - In((entity, x, y, z)): In<(Entity, f32, f32, f32)>, + In((entity, scale)): In<(Entity, Vec3)>, mut transforms: Query<&mut Transform>, ) -> Result<()> { let mut transform = transforms .get_mut(entity) .map_err(|_| ProcessingError::TransformNotFound)?; - transform.scale = Vec3::new(x, y, z); + transform.scale = scale; Ok(()) } pub fn scale( - In((entity, x, y, z)): In<(Entity, f32, f32, f32)>, + In((entity, factor)): In<(Entity, Vec3)>, mut transforms: Query<&mut Transform>, ) -> Result<()> { let mut transform = transforms .get_mut(entity) .map_err(|_| ProcessingError::TransformNotFound)?; - transform.scale *= Vec3::new(x, y, z); + transform.scale *= factor; Ok(()) } pub fn look_at( - In((entity, target_x, target_y, target_z)): In<(Entity, f32, f32, f32)>, + In((entity, target)): In<(Entity, Vec3)>, mut transforms: Query<&mut Transform>, ) -> Result<()> { let mut transform = transforms .get_mut(entity) .map_err(|_| ProcessingError::TransformNotFound)?; - let target = Vec3::new(target_x, target_y, target_z); transform.look_at(target, Vec3::Y); Ok(()) } diff --git a/crates/processing_wasm/src/lib.rs b/crates/processing_wasm/src/lib.rs index 92090b5..a61fdd1 100644 --- a/crates/processing_wasm/src/lib.rs +++ b/crates/processing_wasm/src/lib.rs @@ -191,7 +191,7 @@ pub fn js_reset_matrix(surface_id: u64) -> Result<(), JsValue> { pub fn js_translate(surface_id: u64, x: f32, y: f32) -> Result<(), JsValue> { check(graphics_record_command( Entity::from_bits(surface_id), - DrawCommand::Translate { x, y }, + DrawCommand::Translate(bevy::math::Vec2::new(x, y)), )) } @@ -207,7 +207,7 @@ pub fn js_rotate(surface_id: u64, angle: f32) -> Result<(), JsValue> { pub fn js_scale(surface_id: u64, x: f32, y: f32) -> Result<(), JsValue> { check(graphics_record_command( Entity::from_bits(surface_id), - DrawCommand::Scale { x, y }, + DrawCommand::Scale(bevy::math::Vec2::new(x, y)), )) } @@ -279,24 +279,23 @@ pub fn js_image_destroy(image_id: u64) -> Result<(), JsValue> { pub fn js_transform_set_position(entity_id: u64, x: f32, y: f32, z: f32) -> Result<(), JsValue> { check(transform_set_position( Entity::from_bits(entity_id), - x, - y, - z, + bevy::math::Vec3::new(x, y, z), )) } #[wasm_bindgen(js_name = "transformTranslate")] pub fn js_transform_translate(entity_id: u64, x: f32, y: f32, z: f32) -> Result<(), JsValue> { - check(transform_translate(Entity::from_bits(entity_id), x, y, z)) + check(transform_translate( + Entity::from_bits(entity_id), + bevy::math::Vec3::new(x, y, z), + )) } #[wasm_bindgen(js_name = "transformSetRotation")] pub fn js_transform_set_rotation(entity_id: u64, x: f32, y: f32, z: f32) -> Result<(), JsValue> { check(transform_set_rotation( Entity::from_bits(entity_id), - x, - y, - z, + bevy::math::Vec3::new(x, y, z), )) } @@ -326,20 +325,24 @@ pub fn js_transform_rotate_axis( check(transform_rotate_axis( Entity::from_bits(entity_id), angle, - axis_x, - axis_y, - axis_z, + bevy::math::Vec3::new(axis_x, axis_y, axis_z), )) } #[wasm_bindgen(js_name = "transformSetScale")] pub fn js_transform_set_scale(entity_id: u64, x: f32, y: f32, z: f32) -> Result<(), JsValue> { - check(transform_set_scale(Entity::from_bits(entity_id), x, y, z)) + check(transform_set_scale( + Entity::from_bits(entity_id), + bevy::math::Vec3::new(x, y, z), + )) } #[wasm_bindgen(js_name = "transformScale")] pub fn js_transform_scale(entity_id: u64, x: f32, y: f32, z: f32) -> Result<(), JsValue> { - check(transform_scale(Entity::from_bits(entity_id), x, y, z)) + check(transform_scale( + Entity::from_bits(entity_id), + bevy::math::Vec3::new(x, y, z), + )) } #[wasm_bindgen(js_name = "transformLookAt")] @@ -351,9 +354,7 @@ pub fn js_transform_look_at( ) -> Result<(), JsValue> { check(transform_look_at( Entity::from_bits(entity_id), - target_x, - target_y, - target_z, + bevy::math::Vec3::new(target_x, target_y, target_z), )) } diff --git a/examples/animated_mesh.rs b/examples/animated_mesh.rs index 02d7ba2..9fd40b9 100644 --- a/examples/animated_mesh.rs +++ b/examples/animated_mesh.rs @@ -1,5 +1,6 @@ mod glfw; +use bevy::math::{Vec3, Vec4}; use glfw::GlfwContext; use processing::prelude::*; use processing_render::geometry::Topology; @@ -39,13 +40,15 @@ fn sketch() -> error::Result<()> { let pz = z as f32 * spacing - offset; geometry_color( mesh, - x as f32 / grid_size as f32, - 0.5, - z as f32 / grid_size as f32, - 1.0, + Vec4::new( + x as f32 / grid_size as f32, + 0.5, + z as f32 / grid_size as f32, + 1.0, + ), )?; - geometry_normal(mesh, 0.0, 1.0, 0.0)?; - geometry_vertex(mesh, px, 0.0, pz)?; + geometry_normal(mesh, Vec3::new(0.0, 1.0, 0.0))?; + geometry_vertex(mesh, Vec3::new(px, 0.0, pz))?; } } @@ -67,8 +70,8 @@ fn sketch() -> error::Result<()> { } graphics_mode_3d(graphics)?; - transform_set_position(graphics, 150.0, 150.0, 150.0)?; - transform_look_at(graphics, 0.0, 0.0, 0.0)?; + transform_set_position(graphics, Vec3::new(150.0, 150.0, 150.0))?; + transform_look_at(graphics, Vec3::new(0.0, 0.0, 0.0))?; let mut time = 0.0f32; @@ -79,7 +82,7 @@ fn sketch() -> error::Result<()> { let px = x as f32 * spacing - offset; let pz = z as f32 * spacing - offset; let wave = (px * 0.1 + time).sin() * (pz * 0.1 + time).cos() * 20.0; - geometry_set_vertex(mesh, idx, px, wave, pz)?; + geometry_set_vertex(mesh, idx, Vec3::new(px, wave, pz))?; } } diff --git a/examples/box.rs b/examples/box.rs index df38a17..916f5d0 100644 --- a/examples/box.rs +++ b/examples/box.rs @@ -1,5 +1,6 @@ mod glfw; +use bevy::math::Vec3; use glfw::GlfwContext; use processing::prelude::*; use processing_render::render::command::DrawCommand; @@ -28,8 +29,8 @@ fn sketch() -> error::Result<()> { let box_geo = geometry_box(100.0, 100.0, 100.0)?; graphics_mode_3d(graphics)?; - transform_set_position(graphics, 100.0, 100.0, 300.0)?; - transform_look_at(graphics, 0.0, 0.0, 0.0)?; + transform_set_position(graphics, Vec3::new(100.0, 100.0, 300.0))?; + transform_look_at(graphics, Vec3::new(0.0, 0.0, 0.0))?; let mut angle = 0.0; diff --git a/examples/custom_attribute.rs b/examples/custom_attribute.rs index 680196b..7493f82 100644 --- a/examples/custom_attribute.rs +++ b/examples/custom_attribute.rs @@ -1,5 +1,6 @@ mod glfw; +use bevy::math::{Vec3, Vec4}; use glfw::GlfwContext; use processing::prelude::*; use processing_render::geometry::{AttributeFormat, Topology}; @@ -37,18 +38,18 @@ fn sketch() -> error::Result<()> { let mesh = geometry_create_with_layout(layout, Topology::LineStrip)?; - geometry_color(mesh, 1.0, 0.0, 0.0, 1.0)?; - geometry_normal(mesh, 0.0, 0.0, 1.0)?; + geometry_color(mesh, Vec4::new(1.0, 0.0, 0.0, 1.0))?; + geometry_normal(mesh, Vec3::new(0.0, 0.0, 1.0))?; geometry_attribute_float(mesh, custom_attr, 0.0)?; - geometry_vertex(mesh, -50.0, -50.0, 0.0)?; + geometry_vertex(mesh, Vec3::new(-50.0, -50.0, 0.0))?; - geometry_color(mesh, 0.0, 1.0, 0.0, 1.0)?; + geometry_color(mesh, Vec4::new(0.0, 1.0, 0.0, 1.0))?; geometry_attribute_float(mesh, custom_attr, 0.5)?; - geometry_vertex(mesh, 50.0, -50.0, 0.0)?; + geometry_vertex(mesh, Vec3::new(50.0, -50.0, 0.0))?; - geometry_color(mesh, 0.0, 0.0, 1.0, 1.0)?; + geometry_color(mesh, Vec4::new(0.0, 0.0, 1.0, 1.0))?; geometry_attribute_float(mesh, custom_attr, 1.0)?; - geometry_vertex(mesh, 0.0, 50.0, 0.0)?; + geometry_vertex(mesh, Vec3::new(0.0, 50.0, 0.0))?; geometry_index(mesh, 0)?; geometry_index(mesh, 1)?; @@ -56,8 +57,8 @@ fn sketch() -> error::Result<()> { geometry_index(mesh, 0)?; graphics_mode_3d(graphics)?; - transform_set_position(graphics, 0.0, 0.0, 200.0)?; - transform_look_at(graphics, 0.0, 0.0, 0.0)?; + transform_set_position(graphics, Vec3::new(0.0, 0.0, 200.0))?; + transform_look_at(graphics, Vec3::new(0.0, 0.0, 0.0))?; while glfw_ctx.poll_events() { graphics_begin_draw(graphics)?; diff --git a/examples/custom_material.rs b/examples/custom_material.rs index 29664ce..d6e9fe2 100644 --- a/examples/custom_material.rs +++ b/examples/custom_material.rs @@ -1,5 +1,6 @@ mod glfw; +use bevy::math::Vec3; use glfw::GlfwContext; use processing::prelude::*; use processing_render::render::command::DrawCommand; @@ -28,8 +29,8 @@ fn sketch() -> error::Result<()> { let box_geo = geometry_box(100.0, 100.0, 100.0)?; graphics_mode_3d(graphics)?; - transform_set_position(graphics, 100.0, 100.0, 300.0)?; - transform_look_at(graphics, 0.0, 0.0, 0.0)?; + transform_set_position(graphics, Vec3::new(100.0, 100.0, 300.0))?; + transform_look_at(graphics, Vec3::new(0.0, 0.0, 0.0))?; let shader = shader_load("shaders/custom_material.wesl")?; let mat = material_create_custom(shader)?; diff --git a/examples/gltf_load.rs b/examples/gltf_load.rs index 394a12b..db3b4e7 100644 --- a/examples/gltf_load.rs +++ b/examples/gltf_load.rs @@ -1,5 +1,6 @@ mod glfw; +use bevy::math::Vec3; use glfw::GlfwContext; use processing::prelude::*; use processing_render::material::MaterialValue; @@ -44,8 +45,8 @@ fn sketch() -> error::Result<()> { let lx = t.cos() * radius; let ly = 1.5; let lz = t.sin() * radius; - transform_set_position(light, lx, ly, lz)?; - transform_look_at(light, 0.0, 0.8, 0.0)?; + transform_set_position(light, Vec3::new(lx, ly, lz))?; + transform_look_at(light, Vec3::new(0.0, 0.8, 0.0))?; let r = (t * 8.0).sin() * 0.5 + 0.5; let g = (t * 8.0 + 2.0).sin() * 0.5 + 0.5; diff --git a/examples/lights.rs b/examples/lights.rs index c5254af..32a2308 100644 --- a/examples/lights.rs +++ b/examples/lights.rs @@ -1,5 +1,6 @@ mod glfw; +use bevy::math::Vec3; use glfw::GlfwContext; use processing::prelude::*; use processing_render::render::command::DrawCommand; @@ -44,8 +45,8 @@ fn sketch() -> error::Result<()> { 200.0, 0.5, )?; - transform_set_position(point_light_a, -25.0, 5.0, 51.0)?; - transform_look_at(point_light_a, 0.0, 0.0, 0.0)?; + transform_set_position(point_light_a, Vec3::new(-25.0, 5.0, 51.0))?; + transform_look_at(point_light_a, Vec3::new(0.0, 0.0, 0.0))?; // create another point light let point_light_b = light_create_point( @@ -55,8 +56,8 @@ fn sketch() -> error::Result<()> { 200.0, 0.25, )?; - transform_set_position(point_light_b, 0.0, 5.0, 50.5)?; - transform_look_at(point_light_b, 0.0, 0.0, 0.0)?; + transform_set_position(point_light_b, Vec3::new(0.0, 5.0, 50.5))?; + transform_look_at(point_light_b, Vec3::new(0.0, 0.0, 0.0))?; // and a spot light, too! let spot_light = light_create_spot( @@ -68,12 +69,12 @@ fn sketch() -> error::Result<()> { 0.0, core::f32::consts::FRAC_PI_4, )?; - transform_set_position(spot_light, 40.0, 0.0, 70.0)?; - transform_look_at(spot_light, 0.0, 0.0, 0.0)?; + transform_set_position(spot_light, Vec3::new(40.0, 0.0, 70.0))?; + transform_look_at(spot_light, Vec3::new(0.0, 0.0, 0.0))?; graphics_mode_3d(graphics)?; - transform_set_position(graphics, 100.0, 100.0, 300.0)?; - transform_look_at(graphics, 0.0, 0.0, 0.0)?; + transform_set_position(graphics, Vec3::new(100.0, 100.0, 300.0))?; + transform_look_at(graphics, Vec3::new(0.0, 0.0, 0.0))?; let mut angle = 0.0; diff --git a/examples/materials.rs b/examples/materials.rs index 1e69e4f..1ee3119 100644 --- a/examples/materials.rs +++ b/examples/materials.rs @@ -1,5 +1,6 @@ mod glfw; +use bevy::math::{Vec2, Vec3}; use glfw::GlfwContext; use processing::prelude::*; use processing_render::render::command::DrawCommand; @@ -28,17 +29,17 @@ fn sketch() -> error::Result<()> { let sphere = geometry_sphere(30.0, 32, 18)?; graphics_mode_3d(graphics)?; - transform_set_position(graphics, 0.0, 0.0, 600.0)?; - transform_look_at(graphics, 0.0, 0.0, 0.0)?; + transform_set_position(graphics, Vec3::new(0.0, 0.0, 600.0))?; + transform_look_at(graphics, Vec3::new(0.0, 0.0, 0.0))?; let dir_light = light_create_directional(graphics, bevy::color::Color::srgb(1.0, 0.98, 0.95), 1_500.0)?; - transform_set_position(dir_light, 300.0, 400.0, 300.0)?; - transform_look_at(dir_light, 0.0, 0.0, 0.0)?; + transform_set_position(dir_light, Vec3::new(300.0, 400.0, 300.0))?; + transform_look_at(dir_light, Vec3::new(0.0, 0.0, 0.0))?; let point_light = light_create_point(graphics, bevy::color::Color::WHITE, 100_000.0, 800.0, 0.0)?; - transform_set_position(point_light, 200.0, 200.0, 400.0)?; + transform_set_position(point_light, Vec3::new(200.0, 200.0, 400.0))?; // Grid of materials varying roughness (x) and metallic (y) let cols = 11; @@ -79,10 +80,10 @@ fn sketch() -> error::Result<()> { graphics_record_command(graphics, DrawCommand::PushMatrix)?; graphics_record_command( graphics, - DrawCommand::Translate { - x: col as f32 * spacing - offset_x, - y: row as f32 * spacing - offset_y, - }, + DrawCommand::Translate(Vec2::new( + col as f32 * spacing - offset_x, + row as f32 * spacing - offset_y, + )), )?; graphics_record_command(graphics, DrawCommand::Material(mat))?; graphics_record_command(graphics, DrawCommand::Geometry(sphere))?; diff --git a/examples/pbr.rs b/examples/pbr.rs index f71d68e..6adb4bf 100644 --- a/examples/pbr.rs +++ b/examples/pbr.rs @@ -1,5 +1,6 @@ mod glfw; +use bevy::math::{Vec2, Vec3}; use glfw::GlfwContext; use processing::prelude::*; use processing_render::render::command::DrawCommand; @@ -27,17 +28,17 @@ fn sketch() -> error::Result<()> { let graphics = graphics_create(surface, width, height, TextureFormat::Rgba16Float)?; graphics_mode_3d(graphics)?; - transform_set_position(graphics, 0.0, 0.0, 600.0)?; - transform_look_at(graphics, 0.0, 0.0, 0.0)?; + transform_set_position(graphics, Vec3::new(0.0, 0.0, 600.0))?; + transform_look_at(graphics, Vec3::new(0.0, 0.0, 0.0))?; let dir_light = light_create_directional(graphics, bevy::color::Color::srgb(1.0, 0.98, 0.95), 1_500.0)?; - transform_set_position(dir_light, 300.0, 400.0, 300.0)?; - transform_look_at(dir_light, 0.0, 0.0, 0.0)?; + transform_set_position(dir_light, Vec3::new(300.0, 400.0, 300.0))?; + transform_look_at(dir_light, Vec3::new(0.0, 0.0, 0.0))?; let point_light = light_create_point(graphics, bevy::color::Color::WHITE, 100_000.0, 800.0, 0.0)?; - transform_set_position(point_light, 200.0, 200.0, 400.0)?; + transform_set_position(point_light, Vec3::new(200.0, 200.0, 400.0))?; let cols = 11; let rows = 5; @@ -67,10 +68,10 @@ fn sketch() -> error::Result<()> { graphics_record_command(graphics, DrawCommand::PushMatrix)?; graphics_record_command( graphics, - DrawCommand::Translate { - x: col as f32 * spacing - offset_x, - y: row as f32 * spacing - offset_y, - }, + DrawCommand::Translate(Vec2::new( + col as f32 * spacing - offset_x, + row as f32 * spacing - offset_y, + )), )?; graphics_record_command( graphics, diff --git a/examples/stroke_3d.rs b/examples/stroke_3d.rs index ce80ee5..1e5687b 100644 --- a/examples/stroke_3d.rs +++ b/examples/stroke_3d.rs @@ -1,5 +1,6 @@ mod glfw; +use bevy::math::{Vec2, Vec3}; use glfw::GlfwContext; use processing::prelude::*; use processing_render::render::command::DrawCommand; @@ -17,8 +18,8 @@ fn sketch() -> error::Result<()> { let graphics = graphics_create(surface, 600, 400, TextureFormat::Rgba16Float)?; graphics_mode_3d(graphics)?; - transform_set_position(graphics, 200.0, 150.0, 350.0)?; - transform_look_at(graphics, 0.0, 0.0, 0.0)?; + transform_set_position(graphics, Vec3::new(200.0, 150.0, 350.0))?; + transform_look_at(graphics, Vec3::new(0.0, 0.0, 0.0))?; let _light = light_create_directional(graphics, bevy::color::Color::srgb(0.9, 0.85, 0.8), 800.0)?; @@ -35,7 +36,7 @@ fn sketch() -> error::Result<()> { // thin wireframe box graphics_record_command(graphics, DrawCommand::PushMatrix)?; - graphics_record_command(graphics, DrawCommand::Translate { x: -80.0, y: 0.0 })?; + graphics_record_command(graphics, DrawCommand::Translate(Vec2::new(-80.0, 0.0)))?; graphics_record_command(graphics, DrawCommand::Rotate { angle })?; graphics_record_command( @@ -60,7 +61,7 @@ fn sketch() -> error::Result<()> { // thick wireframe box graphics_record_command(graphics, DrawCommand::PushMatrix)?; - graphics_record_command(graphics, DrawCommand::Translate { x: 0.0, y: 0.0 })?; + graphics_record_command(graphics, DrawCommand::Translate(Vec2::ZERO))?; graphics_record_command(graphics, DrawCommand::Rotate { angle: angle * 0.7 })?; graphics_record_command( @@ -85,7 +86,7 @@ fn sketch() -> error::Result<()> { // thick wireframe sphere graphics_record_command(graphics, DrawCommand::PushMatrix)?; - graphics_record_command(graphics, DrawCommand::Translate { x: 80.0, y: 0.0 })?; + graphics_record_command(graphics, DrawCommand::Translate(Vec2::new(80.0, 0.0)))?; graphics_record_command(graphics, DrawCommand::Rotate { angle: angle * 0.5 })?; graphics_record_command( @@ -110,7 +111,7 @@ fn sketch() -> error::Result<()> { // wireframe-only sphere (no fill) graphics_record_command(graphics, DrawCommand::PushMatrix)?; - graphics_record_command(graphics, DrawCommand::Translate { x: 160.0, y: 0.0 })?; + graphics_record_command(graphics, DrawCommand::Translate(Vec2::new(160.0, 0.0)))?; graphics_record_command( graphics, DrawCommand::Rotate { diff --git a/examples/transforms.rs b/examples/transforms.rs index 6893aa3..1ca2fe6 100644 --- a/examples/transforms.rs +++ b/examples/transforms.rs @@ -1,10 +1,10 @@ mod glfw; -use std::f32::consts::PI; - +use bevy::math::Vec2; use glfw::GlfwContext; use processing::prelude::*; use processing_render::render::command::DrawCommand; +use std::f32::consts::PI; fn main() { sketch().unwrap(); @@ -34,17 +34,17 @@ fn sketch() -> error::Result<()> { graphics_record_command( graphics, - DrawCommand::Translate { - x: 50.0 + j as f32 * 100.0, - y: 50.0 + i as f32 * 100.0, - }, + DrawCommand::Translate(Vec2::new( + 50.0 + j as f32 * 100.0, + 50.0 + i as f32 * 100.0, + )), )?; let angle = t + (i + j) as f32 * PI / 8.0; graphics_record_command(graphics, DrawCommand::Rotate { angle })?; let s = 0.8 + (t * 2.0 + (i * j) as f32).sin() * 0.2; - graphics_record_command(graphics, DrawCommand::Scale { x: s, y: s })?; + graphics_record_command(graphics, DrawCommand::Scale(Vec2::splat(s)))?; let r = j as f32 / 3.0; let g = i as f32 / 3.0; diff --git a/tools/generate_stubs/src/main.rs b/tools/generate_stubs/src/main.rs index 8852546..522bae1 100644 --- a/tools/generate_stubs/src/main.rs +++ b/tools/generate_stubs/src/main.rs @@ -41,8 +41,7 @@ fn main() { eprintln!("Introspecting: {}", cdylib_path.display()); - let module = - introspect_cdylib(&cdylib_path, "mewnala").expect("Failed to introspect cdylib"); + let module = introspect_cdylib(&cdylib_path, "mewnala").expect("Failed to introspect cdylib"); let stubs = module_stub_files(&module); From 1a02b598fa530ab27bcf60c2145e8128f2567f20 Mon Sep 17 00:00:00 2001 From: charlotte Date: Sun, 22 Mar 2026 09:27:02 -0700 Subject: [PATCH 2/2] Clean imports. --- crates/processing_ffi/src/lib.rs | 12 ++---------- crates/processing_pyo3/src/graphics.rs | 21 ++++++++++---------- crates/processing_pyo3/src/lib.rs | 26 ++++++++++++------------- crates/processing_render/src/surface.rs | 1 + crates/processing_wasm/src/lib.rs | 23 ++++++++++++---------- 5 files changed, 40 insertions(+), 43 deletions(-) diff --git a/crates/processing_ffi/src/lib.rs b/crates/processing_ffi/src/lib.rs index ac2ff98..e63e542 100644 --- a/crates/processing_ffi/src/lib.rs +++ b/crates/processing_ffi/src/lib.rs @@ -378,10 +378,7 @@ pub extern "C" fn processing_translate(graphics_id: u64, x: f32, y: f32) { error::clear_error(); let graphics_entity = Entity::from_bits(graphics_id); error::check(|| { - graphics_record_command( - graphics_entity, - DrawCommand::Translate(bevy::math::Vec2::new(x, y)), - ) + graphics_record_command(graphics_entity, DrawCommand::Translate(Vec2::new(x, y))) }); } @@ -406,12 +403,7 @@ pub extern "C" fn processing_rotate(graphics_id: u64, angle: f32) { pub extern "C" fn processing_scale(graphics_id: u64, x: f32, y: f32) { error::clear_error(); let graphics_entity = Entity::from_bits(graphics_id); - error::check(|| { - graphics_record_command( - graphics_entity, - DrawCommand::Scale(bevy::math::Vec2::new(x, y)), - ) - }); + error::check(|| graphics_record_command(graphics_entity, DrawCommand::Scale(Vec2::new(x, y)))); } /// Shear along the X axis. diff --git a/crates/processing_pyo3/src/graphics.rs b/crates/processing_pyo3/src/graphics.rs index 1169c4a..d8d8929 100644 --- a/crates/processing_pyo3/src/graphics.rs +++ b/crates/processing_pyo3/src/graphics.rs @@ -12,6 +12,7 @@ use pyo3::{ }; use crate::glfw::GlfwContext; +use crate::math::{extract_vec2, extract_vec3, extract_vec4}; #[pyclass(unsendable)] pub struct Surface { @@ -45,13 +46,13 @@ pub struct Light { impl Light { #[pyo3(signature = (*args))] pub fn position(&self, args: &Bound<'_, PyTuple>) -> PyResult<()> { - let v = crate::math::extract_vec3(args)?; + let v = extract_vec3(args)?; transform_set_position(self.entity, v).map_err(|e| PyRuntimeError::new_err(format!("{e}"))) } #[pyo3(signature = (*args))] pub fn look_at(&self, args: &Bound<'_, PyTuple>) -> PyResult<()> { - let v = crate::math::extract_vec3(args)?; + let v = extract_vec3(args)?; transform_look_at(self.entity, v).map_err(|e| PyRuntimeError::new_err(format!("{e}"))) } } @@ -131,19 +132,19 @@ impl Geometry { #[pyo3(signature = (*args))] pub fn color(&self, args: &Bound<'_, PyTuple>) -> PyResult<()> { - let v = crate::math::extract_vec4(args)?; + let v = extract_vec4(args)?; geometry_color(self.entity, v).map_err(|e| PyRuntimeError::new_err(format!("{e}"))) } #[pyo3(signature = (*args))] pub fn normal(&self, args: &Bound<'_, PyTuple>) -> PyResult<()> { - let v = crate::math::extract_vec3(args)?; + let v = extract_vec3(args)?; geometry_normal(self.entity, v).map_err(|e| PyRuntimeError::new_err(format!("{e}"))) } #[pyo3(signature = (*args))] pub fn vertex(&self, args: &Bound<'_, PyTuple>) -> PyResult<()> { - let v = crate::math::extract_vec3(args)?; + let v = extract_vec3(args)?; geometry_vertex(self.entity, v).map_err(|e| PyRuntimeError::new_err(format!("{e}"))) } @@ -153,7 +154,7 @@ impl Geometry { #[pyo3(signature = (i, *args))] pub fn set_vertex(&self, i: u32, args: &Bound<'_, PyTuple>) -> PyResult<()> { - let v = crate::math::extract_vec3(args)?; + let v = extract_vec3(args)?; geometry_set_vertex(self.entity, i, v).map_err(|e| PyRuntimeError::new_err(format!("{e}"))) } } @@ -403,7 +404,7 @@ impl Graphics { #[pyo3(signature = (*args))] pub fn translate(&self, args: &Bound<'_, PyTuple>) -> PyResult<()> { - let v = crate::math::extract_vec2(args)?; + let v = extract_vec2(args)?; graphics_record_command(self.entity, DrawCommand::Translate(v)) .map_err(|e| PyRuntimeError::new_err(format!("{e}"))) } @@ -471,7 +472,7 @@ impl Graphics { #[pyo3(signature = (*args))] pub fn scale(&self, args: &Bound<'_, PyTuple>) -> PyResult<()> { - let v = crate::math::extract_vec2(args)?; + let v = extract_vec2(args)?; graphics_record_command(self.entity, DrawCommand::Scale(v)) .map_err(|e| PyRuntimeError::new_err(format!("{e}"))) } @@ -513,13 +514,13 @@ impl Graphics { #[pyo3(signature = (*args))] pub fn camera_position(&self, args: &Bound<'_, PyTuple>) -> PyResult<()> { - let v = crate::math::extract_vec3(args)?; + let v = extract_vec3(args)?; transform_set_position(self.entity, v).map_err(|e| PyRuntimeError::new_err(format!("{e}"))) } #[pyo3(signature = (*args))] pub fn camera_look_at(&self, args: &Bound<'_, PyTuple>) -> PyResult<()> { - let v = crate::math::extract_vec3(args)?; + let v = extract_vec3(args)?; transform_look_at(self.entity, v).map_err(|e| PyRuntimeError::new_err(format!("{e}"))) } diff --git a/crates/processing_pyo3/src/lib.rs b/crates/processing_pyo3/src/lib.rs index 3b85bcc..29a0e10 100644 --- a/crates/processing_pyo3/src/lib.rs +++ b/crates/processing_pyo3/src/lib.rs @@ -145,38 +145,38 @@ mod mewnala { use super::*; #[pymodule_export] - use super::super::math::PyQuat; + use crate::math::PyQuat; #[pymodule_export] - use super::super::math::PyVec2; + use crate::math::PyVec2; #[pymodule_export] - use super::super::math::PyVec3; + use crate::math::PyVec3; #[pymodule_export] - use super::super::math::PyVec4; + use crate::math::PyVec4; #[pymodule_export] - use super::super::math::PyVecIter; + use crate::math::PyVecIter; #[pyfunction] #[pyo3(signature = (*args))] - fn vec2(args: &Bound<'_, PyTuple>) -> PyResult { - super::super::math::PyVec2::py_new(args) + fn vec2(args: &Bound<'_, PyTuple>) -> PyResult { + crate::math::PyVec2::py_new(args) } #[pyfunction] #[pyo3(signature = (*args))] - fn vec3(args: &Bound<'_, PyTuple>) -> PyResult { - super::super::math::PyVec3::py_new(args) + fn vec3(args: &Bound<'_, PyTuple>) -> PyResult { + crate::math::PyVec3::py_new(args) } #[pyfunction] #[pyo3(signature = (*args))] - fn vec4(args: &Bound<'_, PyTuple>) -> PyResult { - super::super::math::PyVec4::py_new(args) + fn vec4(args: &Bound<'_, PyTuple>) -> PyResult { + crate::math::PyVec4::py_new(args) } #[pyfunction] #[pyo3(signature = (*args))] - fn quat(args: &Bound<'_, PyTuple>) -> PyResult { - super::super::math::PyQuat::py_new(args) + fn quat(args: &Bound<'_, PyTuple>) -> PyResult { + crate::math::PyQuat::py_new(args) } } diff --git a/crates/processing_render/src/surface.rs b/crates/processing_render/src/surface.rs index 6ca44da..afeee6f 100644 --- a/crates/processing_render/src/surface.rs +++ b/crates/processing_render/src/surface.rs @@ -32,6 +32,7 @@ use raw_window_handle::{ }; use processing_core::error::{self, ProcessingError, Result}; +#[cfg(not(target_os = "windows"))] use std::ptr::NonNull; use crate::image::{Image, ImageTextures}; diff --git a/crates/processing_wasm/src/lib.rs b/crates/processing_wasm/src/lib.rs index a61fdd1..e7c94e9 100644 --- a/crates/processing_wasm/src/lib.rs +++ b/crates/processing_wasm/src/lib.rs @@ -1,6 +1,9 @@ #![cfg(target_arch = "wasm32")] -use bevy::prelude::Entity; +use bevy::{ + math::{Vec2, Vec3}, + prelude::Entity, +}; use processing_render::{ config::Config, exit, geometry_box, geometry_sphere, graphics_begin_draw, graphics_end_draw, graphics_flush, graphics_record_command, image_create, image_destroy, image_load, @@ -191,7 +194,7 @@ pub fn js_reset_matrix(surface_id: u64) -> Result<(), JsValue> { pub fn js_translate(surface_id: u64, x: f32, y: f32) -> Result<(), JsValue> { check(graphics_record_command( Entity::from_bits(surface_id), - DrawCommand::Translate(bevy::math::Vec2::new(x, y)), + DrawCommand::Translate(Vec2::new(x, y)), )) } @@ -207,7 +210,7 @@ pub fn js_rotate(surface_id: u64, angle: f32) -> Result<(), JsValue> { pub fn js_scale(surface_id: u64, x: f32, y: f32) -> Result<(), JsValue> { check(graphics_record_command( Entity::from_bits(surface_id), - DrawCommand::Scale(bevy::math::Vec2::new(x, y)), + DrawCommand::Scale(Vec2::new(x, y)), )) } @@ -279,7 +282,7 @@ pub fn js_image_destroy(image_id: u64) -> Result<(), JsValue> { pub fn js_transform_set_position(entity_id: u64, x: f32, y: f32, z: f32) -> Result<(), JsValue> { check(transform_set_position( Entity::from_bits(entity_id), - bevy::math::Vec3::new(x, y, z), + Vec3::new(x, y, z), )) } @@ -287,7 +290,7 @@ pub fn js_transform_set_position(entity_id: u64, x: f32, y: f32, z: f32) -> Resu pub fn js_transform_translate(entity_id: u64, x: f32, y: f32, z: f32) -> Result<(), JsValue> { check(transform_translate( Entity::from_bits(entity_id), - bevy::math::Vec3::new(x, y, z), + Vec3::new(x, y, z), )) } @@ -295,7 +298,7 @@ pub fn js_transform_translate(entity_id: u64, x: f32, y: f32, z: f32) -> Result< pub fn js_transform_set_rotation(entity_id: u64, x: f32, y: f32, z: f32) -> Result<(), JsValue> { check(transform_set_rotation( Entity::from_bits(entity_id), - bevy::math::Vec3::new(x, y, z), + Vec3::new(x, y, z), )) } @@ -325,7 +328,7 @@ pub fn js_transform_rotate_axis( check(transform_rotate_axis( Entity::from_bits(entity_id), angle, - bevy::math::Vec3::new(axis_x, axis_y, axis_z), + Vec3::new(axis_x, axis_y, axis_z), )) } @@ -333,7 +336,7 @@ pub fn js_transform_rotate_axis( pub fn js_transform_set_scale(entity_id: u64, x: f32, y: f32, z: f32) -> Result<(), JsValue> { check(transform_set_scale( Entity::from_bits(entity_id), - bevy::math::Vec3::new(x, y, z), + Vec3::new(x, y, z), )) } @@ -341,7 +344,7 @@ pub fn js_transform_set_scale(entity_id: u64, x: f32, y: f32, z: f32) -> Result< pub fn js_transform_scale(entity_id: u64, x: f32, y: f32, z: f32) -> Result<(), JsValue> { check(transform_scale( Entity::from_bits(entity_id), - bevy::math::Vec3::new(x, y, z), + Vec3::new(x, y, z), )) } @@ -354,7 +357,7 @@ pub fn js_transform_look_at( ) -> Result<(), JsValue> { check(transform_look_at( Entity::from_bits(entity_id), - bevy::math::Vec3::new(target_x, target_y, target_z), + Vec3::new(target_x, target_y, target_z), )) }