diff --git a/CHANGELOG.md b/CHANGELOG.md index 24369f911..b6522f7bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ - `InteractionGroups` default value for `memberships` is now `GROUP_1` (#706) - `ImpulseJointSet::get_mut` has a new parameter `wake_up: bool`, to wake up connected bodies. - Removed unmaintained `instant` in favor of `web-time`. This effectively removes the `wasm-bindgen` transitive dependency as it's no longer needed. +- Significantly improve performances of `QueryPipeline::intersection_with_shape`. ## v0.22.0 (20 July 2024) diff --git a/benchmarks3d/trimesh3.rs b/benchmarks3d/trimesh3.rs index ea5aaaaec..49d8c0184 100644 --- a/benchmarks3d/trimesh3.rs +++ b/benchmarks3d/trimesh3.rs @@ -38,7 +38,7 @@ pub fn init_world(testbed: &mut Testbed) { let rigid_body = RigidBodyBuilder::fixed(); let handle = bodies.insert(rigid_body); - let collider = ColliderBuilder::trimesh(vertices, indices); + let collider = ColliderBuilder::trimesh(vertices, indices).unwrap(); colliders.insert_with_parent(collider, handle, &mut bodies); /* diff --git a/crates/rapier2d-f64/Cargo.toml b/crates/rapier2d-f64/Cargo.toml index bd3a90c4a..04465ad0e 100644 --- a/crates/rapier2d-f64/Cargo.toml +++ b/crates/rapier2d-f64/Cargo.toml @@ -68,7 +68,7 @@ vec_map = { version = "0.8", optional = true } web-time = { version = "1.1", optional = true } num-traits = "0.2" nalgebra = "0.33" -parry2d-f64 = "0.17.0" +parry2d-f64 = "0.18.0" simba = "0.9" approx = "0.5" rayon = { version = "1", optional = true } diff --git a/crates/rapier2d/Cargo.toml b/crates/rapier2d/Cargo.toml index 9757186c8..c4de36a39 100644 --- a/crates/rapier2d/Cargo.toml +++ b/crates/rapier2d/Cargo.toml @@ -68,7 +68,7 @@ vec_map = { version = "0.8", optional = true } web-time = { version = "1.1", optional = true } num-traits = "0.2" nalgebra = "0.33" -parry2d = "0.17.0" +parry2d = "0.18.0" simba = "0.9" approx = "0.5" rayon = { version = "1", optional = true } diff --git a/crates/rapier3d-f64/Cargo.toml b/crates/rapier3d-f64/Cargo.toml index 592382817..0d2ce4d78 100644 --- a/crates/rapier3d-f64/Cargo.toml +++ b/crates/rapier3d-f64/Cargo.toml @@ -71,7 +71,7 @@ vec_map = { version = "0.8", optional = true } web-time = { version = "1.1", optional = true } num-traits = "0.2" nalgebra = "0.33" -parry3d-f64 = "0.17.0" +parry3d-f64 = "0.18.0" simba = "0.9" approx = "0.5" rayon = { version = "1", optional = true } diff --git a/crates/rapier3d-meshloader/Cargo.toml b/crates/rapier3d-meshloader/Cargo.toml index 211cea0b6..5505c6356 100644 --- a/crates/rapier3d-meshloader/Cargo.toml +++ b/crates/rapier3d-meshloader/Cargo.toml @@ -27,6 +27,6 @@ wavefront = ["mesh-loader/obj"] [dependencies] thiserror = "1.0.61" profiling = "1.0" -mesh-loader = { version = "0.1.12", optional = true } +mesh-loader = "0.1.12" rapier3d = { version = "0.22", path = "../rapier3d" } diff --git a/crates/rapier3d-urdf/Cargo.toml b/crates/rapier3d-urdf/Cargo.toml index df45749ea..386b6f230 100644 --- a/crates/rapier3d-urdf/Cargo.toml +++ b/crates/rapier3d-urdf/Cargo.toml @@ -19,9 +19,11 @@ license = "Apache-2.0" edition = "2021" [features] -stl = ["dep:rapier3d-meshloader", "rapier3d-meshloader/stl"] -collada = ["dep:rapier3d-meshloader", "rapier3d-meshloader/collada"] -wavefront = ["dep:rapier3d-meshloader", "rapier3d-meshloader/wavefront"] +stl = ["dep:rapier3d-meshloader", "rapier3d-meshloader/stl", "__meshloader_is_enabled"] +collada = ["dep:rapier3d-meshloader", "rapier3d-meshloader/collada", "__meshloader_is_enabled"] +wavefront = ["dep:rapier3d-meshloader", "rapier3d-meshloader/wavefront", "__meshloader_is_enabled"] +## Private feature for detecting when meshloader is enabled by any of the file type features above. +__meshloader_is_enabled = [] [dependencies] log = "0.4" diff --git a/crates/rapier3d-urdf/src/lib.rs b/crates/rapier3d-urdf/src/lib.rs index 1f93fc707..dc4614b79 100644 --- a/crates/rapier3d-urdf/src/lib.rs +++ b/crates/rapier3d-urdf/src/lib.rs @@ -37,7 +37,6 @@ use rapier3d::{ geometry::{Collider, ColliderBuilder, ColliderHandle, ColliderSet, SharedShape, TriMeshFlags}, math::{Isometry, Point, Real, Vector}, na, - prelude::MeshConverter, }; use std::collections::HashMap; use std::path::Path; @@ -462,7 +461,7 @@ fn urdf_to_rigid_body(options: &UrdfLoaderOptions, inertial: &Inertial) -> Rigid fn urdf_to_colliders( options: &UrdfLoaderOptions, - mesh_dir: &Path, + _mesh_dir: &Path, // Unused if none of the meshloader features is enabled. geometry: &Geometry, origin: &Pose, ) -> Vec { @@ -496,14 +495,20 @@ fn urdf_to_colliders( Geometry::Sphere { radius } => { colliders.push(SharedShape::ball(*radius as Real)); } + #[cfg(not(feature = "__meshloader_is_enabled"))] + Geometry::Mesh { .. } => { + log::error!("Mesh loading is disabled by default. Enable one of the format features (`stl`, `collada`, `wavefront`) of `rapier3d-urdf` for mesh support."); + } + #[cfg(feature = "__meshloader_is_enabled")] Geometry::Mesh { filename, scale } => { - let full_path = mesh_dir.join(filename); + let full_path = _mesh_dir.join(filename); let scale = scale .map(|s| Vector::new(s[0] as Real, s[1] as Real, s[2] as Real)) .unwrap_or_else(|| Vector::::repeat(1.0)); + let Ok(loaded_mesh) = rapier3d_meshloader::load_from_path( full_path, - &MeshConverter::TriMeshWithFlags(options.trimesh_flags), + &rapier3d::prelude::MeshConverter::TriMeshWithFlags(options.trimesh_flags), scale, ) else { return Vec::new(); diff --git a/crates/rapier3d/Cargo.toml b/crates/rapier3d/Cargo.toml index 7f8b4f39b..77621baab 100644 --- a/crates/rapier3d/Cargo.toml +++ b/crates/rapier3d/Cargo.toml @@ -71,7 +71,7 @@ vec_map = { version = "0.8", optional = true } web-time = { version = "1.1", optional = true } num-traits = "0.2" nalgebra = "0.33" -parry3d = "0.17.0" +parry3d = "0.18.0" simba = "0.9" approx = "0.5" rayon = { version = "1", optional = true } diff --git a/crates/rapier_testbed2d-f64/Cargo.toml b/crates/rapier_testbed2d-f64/Cargo.toml index c3e545532..7a8fa3bc1 100644 --- a/crates/rapier_testbed2d-f64/Cargo.toml +++ b/crates/rapier_testbed2d-f64/Cargo.toml @@ -34,12 +34,12 @@ default = ["dim2"] dim2 = [] parallel = ["rapier/parallel", "num_cpus"] other-backends = ["wrapped2d"] -profiling = ["dep:puffin_egui", "profiling/profile-with-puffin"] +profiler_ui = ["dep:puffin_egui", "profiling/profile-with-puffin"] # See https://github.com/dimforge/rapier/issues/760. unstable-puffin-pr-235 = [] [package.metadata.docs.rs] -features = ["parallel", "profiling"] +features = ["parallel", "profiler_ui"] [lints.clippy] needless_lifetimes = "allow" diff --git a/crates/rapier_testbed2d/Cargo.toml b/crates/rapier_testbed2d/Cargo.toml index bbfe385a8..2d6ccef7c 100644 --- a/crates/rapier_testbed2d/Cargo.toml +++ b/crates/rapier_testbed2d/Cargo.toml @@ -34,12 +34,12 @@ default = ["dim2"] dim2 = [] parallel = ["rapier/parallel", "num_cpus"] other-backends = ["wrapped2d"] -profiling = ["dep:puffin_egui", "profiling/profile-with-puffin"] +profiler_ui = ["dep:puffin_egui", "profiling/profile-with-puffin"] # See https://github.com/dimforge/rapier/issues/760. unstable-puffin-pr-235 = [] [package.metadata.docs.rs] -features = ["parallel", "other-backends", "profiling"] +features = ["parallel", "other-backends", "profiler_ui"] [lints.clippy] needless_lifetimes = "allow" diff --git a/crates/rapier_testbed3d-f64/Cargo.toml b/crates/rapier_testbed3d-f64/Cargo.toml index f698e2163..367c2a589 100644 --- a/crates/rapier_testbed3d-f64/Cargo.toml +++ b/crates/rapier_testbed3d-f64/Cargo.toml @@ -36,12 +36,12 @@ rust.unexpected_cfgs = { level = "warn", check-cfg = [ default = ["dim3"] dim3 = [] parallel = ["rapier/parallel", "num_cpus"] -profiling = ["dep:puffin_egui", "profiling/profile-with-puffin"] +profiler_ui = ["dep:puffin_egui", "profiling/profile-with-puffin"] # See https://github.com/dimforge/rapier/issues/760. unstable-puffin-pr-235 = [] [package.metadata.docs.rs] -features = ["parallel", "profiling"] +features = ["parallel", "profiler_ui"] [lints.clippy] needless_lifetimes = "allow" diff --git a/crates/rapier_testbed3d/Cargo.toml b/crates/rapier_testbed3d/Cargo.toml index 0fa594480..e87d8ec92 100644 --- a/crates/rapier_testbed3d/Cargo.toml +++ b/crates/rapier_testbed3d/Cargo.toml @@ -34,12 +34,12 @@ default = ["dim3"] dim3 = [] parallel = ["rapier/parallel", "num_cpus"] other-backends = ["physx", "physx-sys", "glam"] -profiling = ["dep:puffin_egui", "profiling/profile-with-puffin"] +profiler_ui = ["dep:puffin_egui", "profiling/profile-with-puffin"] # See https://github.com/dimforge/rapier/issues/760. unstable-puffin-pr-235 = [] [package.metadata.docs.rs] -features = ["parallel", "other-backends", "profiling"] +features = ["parallel", "other-backends", "profiler_ui"] [lints.clippy] needless_lifetimes = "allow" diff --git a/examples2d/Cargo.toml b/examples2d/Cargo.toml index dc214b3bf..a5e81f6b8 100644 --- a/examples2d/Cargo.toml +++ b/examples2d/Cargo.toml @@ -20,7 +20,7 @@ usvg = "0.14" [dependencies.rapier_testbed2d] path = "../crates/rapier_testbed2d" -features = ["profiling"] +features = ["profiler_ui"] [dependencies.rapier2d] path = "../crates/rapier2d" diff --git a/examples2d/all_examples2.rs b/examples2d/all_examples2.rs index 3d065a5c7..74bd89d0a 100644 --- a/examples2d/all_examples2.rs +++ b/examples2d/all_examples2.rs @@ -16,6 +16,7 @@ mod convex_polygons2; mod damping2; mod debug_box_ball2; mod debug_compression2; +mod debug_intersection2; mod debug_total_overlap2; mod debug_vertical_column2; mod drum2; @@ -99,6 +100,7 @@ pub fn main() { ("Joint motor position", joint_motor_position2::init_world), ("(Debug) box ball", debug_box_ball2::init_world), ("(Debug) compression", debug_compression2::init_world), + ("(Debug) intersection", debug_intersection2::init_world), ("(Debug) total overlap", debug_total_overlap2::init_world), ( "(Debug) vertical column", diff --git a/examples2d/debug_intersection2.rs b/examples2d/debug_intersection2.rs new file mode 100644 index 000000000..f54521a3b --- /dev/null +++ b/examples2d/debug_intersection2.rs @@ -0,0 +1,64 @@ +use rapier2d::prelude::*; +use rapier_testbed2d::Testbed; + +pub fn init_world(testbed: &mut Testbed) { + /* + * World + */ + let mut bodies = RigidBodySet::new(); + let mut colliders = ColliderSet::new(); + let impulse_joints = ImpulseJointSet::new(); + let multibody_joints = MultibodyJointSet::new(); + + let rad = 1.0; + let collider = ColliderBuilder::ball(rad); + + let count = 100; + for x in 0..count { + for y in 0..count { + let rigid_body = RigidBodyBuilder::fixed().translation(vector![ + (x as f32 - count as f32 / 2.0) * rad * 3.0, + (y as f32 - count as f32 / 2.0) * rad * 3.0 + ]); + let handle = bodies.insert(rigid_body); + colliders.insert_with_parent(collider.clone(), handle, &mut bodies); + testbed.set_initial_body_color( + handle, + [ + x as f32 / count as f32, + (count - y) as f32 / count as f32, + 0.5, + ], + ); + } + } + + /* + * Set up the testbed. + */ + testbed.set_world(bodies, colliders, impulse_joints, multibody_joints); + testbed.look_at(point![0.0, 0.0], 50.0); + + testbed.add_callback(move |graphics, physics, _, run| { + let slow_time = run.timestep_id as f32 / 3.0; + let intersection = physics.query_pipeline.intersection_with_shape( + &physics.bodies, + &physics.colliders, + &Isometry::translation(slow_time.cos() * 10.0, slow_time.sin() * 10.0), + &Ball::new(rad / 2.0), + QueryFilter::default(), + ); + + if let Some(graphics) = graphics { + for (handle, _) in physics.bodies.iter() { + graphics.set_body_color(handle, [0.5, 0.5, 0.5]); + } + if let Some(intersection) = intersection { + let collider = physics.colliders.get(intersection).unwrap(); + let body_handle = collider.parent().unwrap(); + + graphics.set_body_color(body_handle, [1.0, 0.0, 0.0]); + } + } + }); +} diff --git a/examples2d/trimesh2.rs b/examples2d/trimesh2.rs index a57c0fe08..1e4084ae7 100644 --- a/examples2d/trimesh2.rs +++ b/examples2d/trimesh2.rs @@ -85,6 +85,7 @@ pub fn init_world(testbed: &mut Testbed) { for k in 0..5 { let collider = ColliderBuilder::trimesh(vertices.clone(), indices.clone()) + .unwrap() .contact_skin(0.2); let rigid_body = RigidBodyBuilder::dynamic() .translation(vector![ith as f32 * 8.0 - 20.0, 20.0 + k as f32 * 11.0]) diff --git a/examples3d/Cargo.toml b/examples3d/Cargo.toml index 96ce0e074..268040b9f 100644 --- a/examples3d/Cargo.toml +++ b/examples3d/Cargo.toml @@ -23,7 +23,7 @@ bincode = "1" [dependencies.rapier_testbed3d] path = "../crates/rapier_testbed3d" -features = ["profiling"] +features = ["profiler_ui"] [dependencies.rapier3d] path = "../crates/rapier3d" diff --git a/examples3d/debug_trimesh3.rs b/examples3d/debug_trimesh3.rs index c58854508..48a9bfd58 100644 --- a/examples3d/debug_trimesh3.rs +++ b/examples3d/debug_trimesh3.rs @@ -48,7 +48,7 @@ pub fn init_world(testbed: &mut Testbed) { let rigid_body = RigidBodyBuilder::fixed().translation(vector![0.0, 0.0, 0.0]); let handle = bodies.insert(rigid_body); - let collider = ColliderBuilder::trimesh(vtx, idx); + let collider = ColliderBuilder::trimesh(vtx, idx).expect("Could not create trimesh collider."); colliders.insert_with_parent(collider, handle, &mut bodies); testbed.set_initial_body_color(handle, [0.3, 0.3, 0.3]); diff --git a/examples3d/dynamic_trimesh3.rs b/examples3d/dynamic_trimesh3.rs index 0a824769e..3f72a22d2 100644 --- a/examples3d/dynamic_trimesh3.rs +++ b/examples3d/dynamic_trimesh3.rs @@ -93,6 +93,7 @@ pub fn do_init_world(testbed: &mut Testbed, use_convex_decomposition: bool) { } else { // SharedShape::trimesh(vertices, indices) SharedShape::trimesh_with_flags(vertices, indices, TriMeshFlags::FIX_INTERNAL_EDGES) + .unwrap() }; shapes.push(decomposed_shape); diff --git a/examples3d/trimesh3.rs b/examples3d/trimesh3.rs index edbb539b3..c397a6cbf 100644 --- a/examples3d/trimesh3.rs +++ b/examples3d/trimesh3.rs @@ -42,7 +42,8 @@ pub fn init_world(testbed: &mut Testbed) { vertices, indices, TriMeshFlags::MERGE_DUPLICATE_VERTICES, - ); + ) + .unwrap(); colliders.insert_with_parent(collider, handle, &mut bodies); /* diff --git a/src/geometry/collider.rs b/src/geometry/collider.rs index c0cec6bed..4f5312f76 100644 --- a/src/geometry/collider.rs +++ b/src/geometry/collider.rs @@ -10,7 +10,7 @@ use crate::pipeline::{ActiveEvents, ActiveHooks}; use crate::prelude::ColliderEnabled; use na::Unit; use parry::bounding_volume::{Aabb, BoundingVolume}; -use parry::shape::{Shape, TriMeshFlags}; +use parry::shape::{Shape, TriMeshBuilderError, TriMeshFlags}; #[cfg(feature = "dim3")] use crate::geometry::HeightFieldFlags; @@ -685,8 +685,11 @@ impl ColliderBuilder { } /// Initializes a collider builder with a triangle mesh shape defined by its vertex and index buffers. - pub fn trimesh(vertices: Vec>, indices: Vec<[u32; 3]>) -> Self { - Self::new(SharedShape::trimesh(vertices, indices)) + pub fn trimesh( + vertices: Vec>, + indices: Vec<[u32; 3]>, + ) -> Result { + Ok(Self::new(SharedShape::trimesh(vertices, indices)?)) } /// Initializes a collider builder with a triangle mesh shape defined by its vertex and index buffers and @@ -695,8 +698,10 @@ impl ColliderBuilder { vertices: Vec>, indices: Vec<[u32; 3]>, flags: TriMeshFlags, - ) -> Self { - Self::new(SharedShape::trimesh_with_flags(vertices, indices, flags)) + ) -> Result { + Ok(Self::new(SharedShape::trimesh_with_flags( + vertices, indices, flags, + )?)) } /// Initializes a collider builder with a shape converted from the given triangle mesh index diff --git a/src/geometry/mesh_converter.rs b/src/geometry/mesh_converter.rs index cfa1391c8..13868fd84 100644 --- a/src/geometry/mesh_converter.rs +++ b/src/geometry/mesh_converter.rs @@ -1,6 +1,6 @@ use parry::bounding_volume; use parry::math::{Isometry, Point, Real}; -use parry::shape::{Cuboid, SharedShape, TriMeshFlags}; +use parry::shape::{Cuboid, SharedShape, TriMeshBuilderError, TriMeshFlags}; #[cfg(feature = "dim3")] use parry::transformation::vhacd::VHACDParameters; @@ -17,6 +17,9 @@ pub enum MeshConverterError { /// The convex hull calculation carried out by the [`MeshConverter::ConvexHull`] failed. #[error("convex-hull computation failed")] ConvexHullFailed, + /// The TriMesh building failed. + #[error("TriMesh building failed")] + TriMeshBuilderError(TriMeshBuilderError), } /// Determines how meshes (generally when loaded from a file) are converted into Rapier colliders. @@ -61,9 +64,11 @@ impl MeshConverter { ) -> Result<(SharedShape, Isometry), MeshConverterError> { let mut transform = Isometry::identity(); let shape = match self { - MeshConverter::TriMesh => SharedShape::trimesh(vertices, indices), + MeshConverter::TriMesh => SharedShape::trimesh(vertices, indices) + .map_err(MeshConverterError::TriMeshBuilderError)?, MeshConverter::TriMeshWithFlags(flags) => { SharedShape::trimesh_with_flags(vertices, indices, *flags) + .map_err(MeshConverterError::TriMeshBuilderError)? } MeshConverter::Obb => { let (pose, cuboid) = parry::utils::obb(&vertices); diff --git a/src/pipeline/query_pipeline/mod.rs b/src/pipeline/query_pipeline/mod.rs index 06a5e2fe1..95e1f002b 100644 --- a/src/pipeline/query_pipeline/mod.rs +++ b/src/pipeline/query_pipeline/mod.rs @@ -480,20 +480,17 @@ impl QueryPipeline { filter: QueryFilter, ) -> Option { let pipeline_shape = self.as_composite_shape(bodies, colliders, filter); - #[allow(deprecated)] // TODO: replace this with IntersectionCompositeShapeShapeVisitor when it // can return the shape part id. - let mut visitor = - parry::query::details::IntersectionCompositeShapeShapeBestFirstVisitor::new( - &*self.query_dispatcher, - shape_pos, - &pipeline_shape, - shape, - ); + let mut visitor = parry::query::details::IntersectionCompositeShapeShapeVisitor::new( + &*self.query_dispatcher, + shape_pos, + &pipeline_shape, + shape, + ); - self.qbvh - .traverse_best_first(&mut visitor) - .map(|h| (h.1 .0)) + self.qbvh.traverse_depth_first(&mut visitor); + visitor.found_intersection } /// Find the projection of a point on the closest collider. diff --git a/src_testbed/testbed.rs b/src_testbed/testbed.rs index c4fa6880b..8390f29d3 100644 --- a/src_testbed/testbed.rs +++ b/src_testbed/testbed.rs @@ -249,7 +249,7 @@ impl TestbedApp { } pub fn run_with_init(mut self, mut init: impl FnMut(&mut App)) { - #[cfg(feature = "profiling")] + #[cfg(feature = "profiler_ui")] puffin_egui::puffin::set_scopes_on(true); let mut args = env::args(); diff --git a/src_testbed/ui.rs b/src_testbed/ui.rs index 1c0421a0d..39ac93aff 100644 --- a/src_testbed/ui.rs +++ b/src_testbed/ui.rs @@ -22,7 +22,7 @@ pub fn update_ui( harness: &mut Harness, debug_render: &mut DebugRenderPipelineResource, ) { - #[cfg(feature = "profiling")] + #[cfg(feature = "profiler_ui")] { let window = egui::Window::new("Profiling"); let window = window.default_open(false);