From bc2ae4b512b8bc7a2b61dd24d9685289453681c5 Mon Sep 17 00:00:00 2001 From: Sébastien Crozet Date: Fri, 22 Apr 2022 15:45:53 +0200 Subject: Add a basic lines-based debug-renderer --- .../debug_render_pipeline/debug_render_backend.rs | 66 +++ .../debug_render_pipeline/debug_render_pipeline.rs | 450 +++++++++++++++++++++ .../debug_render_pipeline/debug_render_style.rs | 37 ++ src/pipeline/debug_render_pipeline/mod.rs | 8 + src/pipeline/debug_render_pipeline/outlines.rs | 36 ++ src/pipeline/mod.rs | 9 + src/pipeline/physics_pipeline.rs | 11 + 7 files changed, 617 insertions(+) create mode 100644 src/pipeline/debug_render_pipeline/debug_render_backend.rs create mode 100644 src/pipeline/debug_render_pipeline/debug_render_pipeline.rs create mode 100644 src/pipeline/debug_render_pipeline/debug_render_style.rs create mode 100644 src/pipeline/debug_render_pipeline/mod.rs create mode 100644 src/pipeline/debug_render_pipeline/outlines.rs (limited to 'src') diff --git a/src/pipeline/debug_render_pipeline/debug_render_backend.rs b/src/pipeline/debug_render_pipeline/debug_render_backend.rs new file mode 100644 index 0000000..27a67e4 --- /dev/null +++ b/src/pipeline/debug_render_pipeline/debug_render_backend.rs @@ -0,0 +1,66 @@ +use crate::dynamics::{ + ImpulseJoint, ImpulseJointHandle, Multibody, MultibodyLink, RigidBody, RigidBodyHandle, +}; +use crate::geometry::Collider; +use crate::math::{Isometry, Point, Real, Vector}; +use crate::prelude::{ColliderHandle, MultibodyJointHandle}; +use na::Scale; + +#[derive(Copy, Clone)] +pub enum DebugRenderObject<'a> { + RigidBody(RigidBodyHandle, &'a RigidBody), + Collider(ColliderHandle, &'a Collider), + ImpulseJoint(ImpulseJointHandle, &'a ImpulseJoint), + MultibodyJoint(MultibodyJointHandle, &'a Multibody, &'a MultibodyLink), + Other, +} + +pub trait DebugRenderBackend { + fn draw_line( + &mut self, + object: DebugRenderObject, + a: Point, + b: Point, + color: [f32; 4], + ); + + fn draw_polyline( + &mut self, + object: DebugRenderObject, + vertices: &[Point], + indices: &[[u32; 2]], + transform: &Isometry, + scale: &Vector, + color: [f32; 4], + ) { + for idx in indices { + let a = transform * (Scale::from(*scale) * vertices[idx[0] as usize]); + let b = transform * (Scale::from(*scale) * vertices[idx[1] as usize]); + self.draw_line(object, a, b, color); + } + } + + fn draw_line_strip( + &mut self, + object: DebugRenderObject, + vertices: &[Point], + transform: &Isometry, + scale: &Vector, + color: [f32; 4], + closed: bool, + ) { + for vtx in vertices.windows(2) { + let a = transform * (Scale::from(*scale) * vtx[0]); + let b = transform * (Scale::from(*scale) * vtx[1]); + self.draw_line(object, a, b, color); + } + + if closed { + if vertices.len() > 2 { + let a = transform * (Scale::from(*scale) * vertices[0]); + let b = transform * (Scale::from(*scale) * vertices.last().unwrap()); + self.draw_line(object, a, b, color); + } + } + } +} diff --git a/src/pipeline/debug_render_pipeline/debug_render_pipeline.rs b/src/pipeline/debug_render_pipeline/debug_render_pipeline.rs new file mode 100644 index 0000000..36593ce --- /dev/null +++ b/src/pipeline/debug_render_pipeline/debug_render_pipeline.rs @@ -0,0 +1,450 @@ +use super::{outlines, DebugRenderBackend}; +use crate::dynamics::{ + GenericJoint, ImpulseJointSet, MultibodyJointSet, RigidBodySet, RigidBodyType, +}; +use crate::geometry::{Ball, ColliderSet, Cuboid, Shape, TypedShape}; +#[cfg(feature = "dim3")] +use crate::geometry::{Cone, Cylinder}; +use crate::math::{Isometry, Point, Real, Vector, DIM}; +use crate::pipeline::debug_render_pipeline::debug_render_backend::DebugRenderObject; +use crate::pipeline::debug_render_pipeline::DebugRenderStyle; +use crate::utils::WBasis; +use std::any::TypeId; +use std::collections::HashMap; + +bitflags::bitflags! { + pub struct DebugRenderMode: u32 { + const COLLIDER_SHAPES = 1 << 0; + const RIGID_BODY_AXES = 1 << 1; + const MULTIBODY_JOINTS = 1 << 2; + const IMPULSE_JOINTS = 1 << 3; + } +} + +pub struct DebugRenderPipeline { + #[cfg(feature = "dim2")] + instances: HashMap>>, + #[cfg(feature = "dim3")] + instances: HashMap>, Vec<[u32; 2]>)>, + pub style: DebugRenderStyle, + pub mode: DebugRenderMode, +} + +impl Default for DebugRenderPipeline { + fn default() -> Self { + Self::render_all(DebugRenderStyle::default()) + } +} + +impl DebugRenderPipeline { + pub fn new(style: DebugRenderStyle, mode: DebugRenderMode) -> Self { + Self { + instances: outlines::instances(style.subdivisions), + style, + mode, + } + } + + pub fn render_all(style: DebugRenderStyle) -> Self { + Self::new(style, DebugRenderMode::all()) + } + + pub fn render( + &mut self, + backend: &mut impl DebugRenderBackend, + bodies: &RigidBodySet, + colliders: &ColliderSet, + impulse_joints: &ImpulseJointSet, + multibody_joints: &MultibodyJointSet, + ) { + self.render_bodies(backend, bodies); + self.render_colliders(backend, bodies, colliders); + self.render_joints(backend, bodies, impulse_joints, multibody_joints); + } + + pub fn render_joints( + &mut self, + backend: &mut impl DebugRenderBackend, + bodies: &RigidBodySet, + impulse_joints: &ImpulseJointSet, + multibody_joints: &MultibodyJointSet, + ) { + let mut render_joint = |body1, + body2, + data: &GenericJoint, + mut anchor_color: [f32; 4], + mut separation_color: [f32; 4], + object| { + if let (Some(rb1), Some(rb2)) = (bodies.get(body1), bodies.get(body2)) { + let coeff = if (rb1.is_fixed() || rb1.is_sleeping()) + && (rb2.is_fixed() || rb2.is_sleeping()) + { + self.style.sleep_color_multiplier + } else { + [1.0; 4] + }; + + let frame1 = rb1.position() * data.local_frame1; + let frame2 = rb2.position() * data.local_frame2; + + let a = *rb1.translation(); + let b = frame1.translation.vector; + let c = frame2.translation.vector; + let d = *rb2.translation(); + + for k in 0..4 { + anchor_color[k] *= coeff[k]; + separation_color[k] *= coeff[k]; + } + + backend.draw_line(object, a.into(), b.into(), anchor_color); + backend.draw_line(object, b.into(), c.into(), separation_color); + backend.draw_line(object, c.into(), d.into(), anchor_color); + } + }; + + if self.mode.contains(DebugRenderMode::IMPULSE_JOINTS) { + for (handle, joint) in impulse_joints.iter() { + let anc_color = self.style.impulse_joint_anchor_color; + let sep_color = self.style.impulse_joint_separation_color; + let object = DebugRenderObject::ImpulseJoint(handle, joint); + render_joint( + joint.body1, + joint.body2, + &joint.data, + anc_color, + sep_color, + object, + ); + } + } + + if self.mode.contains(DebugRenderMode::MULTIBODY_JOINTS) { + for (handle, multibody, link) in multibody_joints.iter() { + let anc_color = self.style.multibody_joint_anchor_color; + let sep_color = self.style.multibody_joint_separation_color; + let parent = multibody.link(link.parent_id().unwrap()).unwrap(); + let object = DebugRenderObject::MultibodyJoint(handle, multibody, link); + render_joint( + parent.rigid_body_handle(), + link.rigid_body_handle(), + &link.joint.data, + anc_color, + sep_color, + object, + ); + } + } + } + + pub fn render_bodies(&mut self, backend: &mut impl DebugRenderBackend, bodies: &RigidBodySet) { + for (handle, rb) in bodies.iter() { + let object = DebugRenderObject::RigidBody(handle, rb); + + if self.style.rigid_body_axes_length != 0.0 + && self.mode.contains(DebugRenderMode::RIGID_BODY_AXES) + { + let basis = rb.rotation().to_rotation_matrix().into_inner(); + let coeff = if rb.is_sleeping() { + self.style.sleep_color_multiplier + } else { + [1.0; 4] + }; + let colors = [ + [0.0 * coeff[0], 1.0 * coeff[1], 0.25 * coeff[2], coeff[3]], + [120.0 * coeff[0], 1.0 * coeff[1], 0.1 * coeff[2], coeff[3]], + [240.0 * coeff[0], 1.0 * coeff[1], 0.2 * coeff[2], coeff[3]], + ]; + let com = rb.mprops.world_com; + + for k in 0..DIM { + let axis = basis.column(k) * self.style.rigid_body_axes_length; + backend.draw_line(object, com, com + axis, colors[k]); + } + } + } + } + + pub fn render_colliders( + &mut self, + backend: &mut impl DebugRenderBackend, + bodies: &RigidBodySet, + colliders: &ColliderSet, + ) { + if self.mode.contains(DebugRenderMode::COLLIDER_SHAPES) { + for (h, co) in colliders.iter() { + let object = DebugRenderObject::Collider(h, co); + let color = if let Some(parent) = co.parent().and_then(|p| bodies.get(p)) { + let coeff = if parent.is_sleeping() { + self.style.sleep_color_multiplier + } else { + [1.0; 4] + }; + let c = match parent.body_type { + RigidBodyType::Fixed => self.style.collider_fixed_color, + RigidBodyType::Dynamic => self.style.collider_dynamic_color, + RigidBodyType::KinematicPositionBased + | RigidBodyType::KinematicVelocityBased => { + self.style.collider_kinematic_color + } + }; + + [ + c[0] * coeff[0], + c[1] * coeff[1], + c[2] * coeff[2], + c[3] * coeff[3], + ] + } else { + self.style.collider_parentless_color + }; + + self.render_shape(object, backend, co.shape(), co.position(), color) + } + } + } + + #[cfg(feature = "dim2")] + fn render_shape( + &mut self, + object: DebugRenderObject, + backend: &mut impl DebugRenderBackend, + shape: &dyn Shape, + pos: &Isometry, + color: [f32; 4], + ) { + match shape.as_typed_shape() { + TypedShape::Ball(s) => { + let vtx = &self.instances[&TypeId::of::()]; + backend.draw_line_strip( + object, + vtx, + pos, + &Vector::repeat(s.radius * 2.0), + color, + true, + ) + } + TypedShape::Cuboid(s) => { + let vtx = &self.instances[&TypeId::of::()]; + backend.draw_line_strip(object, vtx, pos, &(s.half_extents * 2.0), color, true) + } + TypedShape::Capsule(s) => { + let vtx = s.to_polyline(self.style.subdivisions); + backend.draw_line_strip(object, &vtx, pos, &Vector::repeat(1.0), color, true) + } + TypedShape::Segment(s) => backend.draw_line_strip( + object, + &[s.a, s.b], + pos, + &Vector::repeat(1.0), + color, + false, + ), + TypedShape::Triangle(s) => backend.draw_line_strip( + object, + &[s.a, s.b, s.c], + pos, + &Vector::repeat(1.0), + color, + true, + ), + TypedShape::TriMesh(s) => { + for tri in s.triangles() { + self.render_shape(object, backend, &tri, pos, color) + } + } + TypedShape::Polyline(s) => backend.draw_polyline( + object, + s.vertices(), + s.indices(), + pos, + &Vector::repeat(1.0), + color, + ), + TypedShape::HalfSpace(s) => { + let basis = s.normal.orthonormal_basis()[0]; + let a = Point::from(basis) * 10_000.0; + let b = Point::from(basis) * -10_000.0; + backend.draw_line_strip(object, &[a, b], pos, &Vector::repeat(1.0), color, false) + } + TypedShape::HeightField(s) => { + for seg in s.segments() { + self.render_shape(object, backend, &seg, pos, color) + } + } + TypedShape::Compound(s) => { + for (sub_pos, shape) in s.shapes() { + self.render_shape(object, backend, &**shape, &(pos * sub_pos), color) + } + } + TypedShape::ConvexPolygon(s) => { + backend.draw_line_strip(object, s.points(), pos, &Vector::repeat(1.0), color, true) + } + /* + * Round shapes. + */ + TypedShape::RoundCuboid(s) => { + self.render_shape(object, backend, &s.base_shape, pos, color) + } + TypedShape::RoundTriangle(s) => { + self.render_shape(object, backend, &s.base_shape, pos, color) + } + // TypedShape::RoundTriMesh(s) => self.render_shape(backend, &s.base_shape, pos, color), + // TypedShape::RoundHeightField(s) => { + // self.render_shape(backend, &s.base_shape, pos, color) + // } + TypedShape::RoundConvexPolygon(s) => { + self.render_shape(object, backend, &s.base_shape, pos, color) + } + TypedShape::Custom(_) => {} + } + } + + #[cfg(feature = "dim3")] + fn render_shape( + &mut self, + object: DebugRenderObject, + backend: &mut impl DebugRenderBackend, + shape: &dyn Shape, + pos: &Isometry, + color: [f32; 4], + ) { + match shape.as_typed_shape() { + TypedShape::Ball(s) => { + let (vtx, idx) = &self.instances[&TypeId::of::()]; + backend.draw_polyline( + object, + vtx, + idx, + pos, + &Vector::repeat(s.radius * 2.0), + color, + ) + } + TypedShape::Cuboid(s) => { + let (vtx, idx) = &self.instances[&TypeId::of::()]; + backend.draw_polyline(object, vtx, idx, pos, &(s.half_extents * 2.0), color) + } + #[cfg(feature = "dim3")] + TypedShape::Capsule(s) => { + let (vtx, idx) = s.to_outline(self.style.subdivisions); + backend.draw_polyline(object, &vtx, &idx, pos, &Vector::repeat(1.0), color) + } + TypedShape::Segment(s) => backend.draw_polyline( + object, + &[s.a, s.b], + &[[0, 1]], + pos, + &Vector::repeat(1.0), + color, + ), + TypedShape::Triangle(s) => backend.draw_line_strip( + object, + &[s.a, s.b, s.c], + pos, + &Vector::repeat(1.0), + color, + true, + ), + TypedShape::TriMesh(s) => { + for tri in s.triangles() { + self.render_shape(object, backend, &tri, pos, color) + } + } + TypedShape::Polyline(s) => backend.draw_polyline( + object, + s.vertices(), + s.indices(), + pos, + &Vector::repeat(1.0), + color, + ), + TypedShape::HalfSpace(s) => { + let basis = s.normal.orthonormal_basis(); + let a = Point::from(basis[0]) * 10_000.0; + let b = Point::from(basis[0]) * -10_000.0; + let c = Point::from(basis[1]) * 10_000.0; + let d = Point::from(basis[1]) * -10_000.0; + backend.draw_polyline( + object, + &[a, b, c, d], + &[[0, 1], [2, 3]], + pos, + &Vector::repeat(1.0), + color, + ) + } + TypedShape::HeightField(s) => { + for tri in s.triangles() { + self.render_shape(object, backend, &tri, pos, color) + } + } + TypedShape::Compound(s) => { + for (sub_pos, shape) in s.shapes() { + self.render_shape(object, backend, &**shape, &(pos * sub_pos), color) + } + } + TypedShape::ConvexPolyhedron(s) => { + let indices: Vec<_> = s + .edges() + .iter() + .map(|e| [e.vertices.x, e.vertices.y]) + .collect(); + backend.draw_polyline( + object, + s.points(), + &indices, + pos, + &Vector::repeat(1.0), + color, + ) + } + TypedShape::Cylinder(s) => { + let (vtx, idx) = &self.instances[&TypeId::of::()]; + backend.draw_polyline( + object, + vtx, + idx, + pos, + &(Vector::new(s.radius, s.half_height, s.radius) * 2.0), + color, + ) + } + TypedShape::Cone(s) => { + let (vtx, idx) = &self.instances[&TypeId::of::()]; + backend.draw_polyline( + object, + vtx, + idx, + pos, + &(Vector::new(s.radius, s.half_height, s.radius) * 2.0), + color, + ) + } + /* + * Round shapes. + */ + TypedShape::RoundCuboid(s) => { + self.render_shape(object, backend, &s.base_shape, pos, color) + } + TypedShape::RoundTriangle(s) => { + self.render_shape(object, backend, &s.base_shape, pos, color) + } + // TypedShape::RoundTriMesh(s) => self.render_shape(object, backend, &s.base_shape, pos, color), + // TypedShape::RoundHeightField(s) => { + // self.render_shape(object, backend, &s.base_shape, pos, color) + // } + TypedShape::RoundCylinder(s) => { + self.render_shape(object, backend, &s.base_shape, pos, color) + } + TypedShape::RoundCone(s) => { + self.render_shape(object, backend, &s.base_shape, pos, color) + } + TypedShape::RoundConvexPolyhedron(s) => { + self.render_shape(object, backend, &s.base_shape, pos, color) + } + TypedShape::Custom(_) => {} + } + } +} diff --git a/src/pipeline/debug_render_pipeline/debug_render_style.rs b/src/pipeline/debug_render_pipeline/debug_render_style.rs new file mode 100644 index 0000000..9d4b6cc --- /dev/null +++ b/src/pipeline/debug_render_pipeline/debug_render_style.rs @@ -0,0 +1,37 @@ +/// A color for debug-rendering. +/// +/// The default colors are provided in HSLA (Hue Saturation Lightness Alpha) format. +pub type DebugColor = [f32; 4]; + +#[derive(Clone, Debug, PartialEq)] +pub struct DebugRenderStyle { + pub subdivisions: u32, + pub collider_dynamic_color: DebugColor, + pub collider_fixed_color: DebugColor, + pub collider_kinematic_color: DebugColor, + pub collider_parentless_color: DebugColor, + pub impulse_joint_anchor_color: DebugColor, + pub impulse_joint_separation_color: DebugColor, + pub multibody_joint_anchor_color: DebugColor, + pub multibody_joint_separation_color: DebugColor, + pub sleep_color_multiplier: [f32; 4], + pub rigid_body_axes_length: f32, +} + +impl Default for DebugRenderStyle { + fn default() -> Self { + Self { + subdivisions: 20, + collider_dynamic_color: [340.0, 1.0, 0.3, 1.0], + collider_kinematic_color: [20.0, 1.0, 0.3, 1.0], + collider_fixed_color: [30.0, 1.0, 0.4, 1.0], + collider_parentless_color: [30.0, 1.0, 0.4, 1.0], + impulse_joint_anchor_color: [240.0, 0.5, 0.4, 1.0], + impulse_joint_separation_color: [0.0, 0.5, 0.4, 1.0], + multibody_joint_anchor_color: [300.0, 1.0, 0.4, 1.0], + multibody_joint_separation_color: [0.0, 1.0, 0.4, 1.0], + sleep_color_multiplier: [1.0, 1.0, 0.2, 1.0], + rigid_body_axes_length: 0.5, + } + } +} diff --git a/src/pipeline/debug_render_pipeline/mod.rs b/src/pipeline/debug_render_pipeline/mod.rs new file mode 100644 index 0000000..104fd9f --- /dev/null +++ b/src/pipeline/debug_render_pipeline/mod.rs @@ -0,0 +1,8 @@ +pub use self::debug_render_backend::{DebugRenderBackend, DebugRenderObject}; +pub use self::debug_render_pipeline::{DebugRenderMode, DebugRenderPipeline}; +pub use self::debug_render_style::{DebugColor, DebugRenderStyle}; + +mod debug_render_backend; +mod debug_render_pipeline; +mod debug_render_style; +pub(self) mod outlines; diff --git a/src/pipeline/debug_render_pipeline/outlines.rs b/src/pipeline/debug_render_pipeline/outlines.rs new file mode 100644 index 0000000..01a441e --- /dev/null +++ b/src/pipeline/debug_render_pipeline/outlines.rs @@ -0,0 +1,36 @@ +use crate::geometry::{Ball, Cuboid}; +#[cfg(feature = "dim3")] +use crate::geometry::{Cone, Cylinder}; +use crate::math::{Point, Real, Vector}; +use std::any::TypeId; +use std::collections::HashMap; + +#[cfg(feature = "dim2")] +pub fn instances(nsubdivs: u32) -> HashMap>> { + let mut result = HashMap::new(); + result.insert( + TypeId::of::(), + Cuboid::new(Vector::repeat(0.5)).to_polyline(), + ); + result.insert(TypeId::of::(), Ball::new(0.5).to_polyline(nsubdivs)); + result +} + +#[cfg(feature = "dim3")] +pub fn instances(nsubdivs: u32) -> HashMap>, Vec<[u32; 2]>)> { + let mut result = HashMap::new(); + result.insert( + TypeId::of::(), + Cuboid::new(Vector::repeat(0.5)).to_outline(), + ); + result.insert(TypeId::of::(), Ball::new(0.5).to_outline(nsubdivs)); + result.insert( + TypeId::of::(), + Cone::new(0.5, 0.5).to_outline(nsubdivs), + ); + result.insert( + TypeId::of::(), + Cylinder::new(0.5, 0.5).to_outline(nsubdivs), + ); + result +} diff --git a/src/pipeline/mod.rs b/src/pipeline/mod.rs index 2c0393e..fb14e4c 100644 --- a/src/pipeline/mod.rs +++ b/src/pipeline/mod.rs @@ -6,9 +6,18 @@ pub use physics_hooks::{ActiveHooks, ContactModificationContext, PairFilterConte pub use physics_pipeline::PhysicsPipeline; pub use query_pipeline::{QueryPipeline, QueryPipelineMode}; +#[cfg(feature = "debug-render")] +pub use self::debug_render_pipeline::{ + DebugColor, DebugRenderBackend, DebugRenderMode, DebugRenderObject, DebugRenderPipeline, + DebugRenderStyle, +}; + mod collision_pipeline; mod event_handler; mod physics_hooks; mod physics_pipeline; mod query_pipeline; mod user_changes; + +#[cfg(feature = "debug-render")] +mod debug_render_pipeline; diff --git a/src/pipeline/physics_pipeline.rs b/src/pipeline/physics_pipeline.rs index 5727592..012f40f 100644 --- a/src/pipeline/physics_pipeline.rs +++ b/src/pipeline/physics_pipeline.rs @@ -541,6 +541,17 @@ impl PhysicsPipeline { self.clear_modified_colliders(colliders, &mut modified_colliders); } + // Finally, make sure we update the world mass-properties of the rigid-bodies + // that moved. Otherwise, users may end up applying forces wrt. an outdated + // center of mass. + // TODO: avoid updating the world mass properties twice (here, and + // at the beginning of the next timestep) for bodies that were + // not modified by the user in the mean time. + for handle in islands.active_dynamic_bodies() { + let rb = bodies.index_mut_internal(*handle); + rb.mprops.update_world_mass_properties(&rb.pos.position); + } + self.counters.step_completed(); } } -- cgit From ae40f4cd7e55dd81955cd329f4d45bba040ba012 Mon Sep 17 00:00:00 2001 From: Sébastien Crozet Date: Thu, 28 Apr 2022 13:02:43 +0200 Subject: Add collision event flags --- src/dynamics/ccd/ccd_solver.rs | 16 +++++++++--- src/geometry/contact_pair.rs | 45 ++++++++++++++++++++++++++++------ src/geometry/mod.rs | 55 ++++++++++++++++++++++++++++-------------- src/geometry/narrow_phase.rs | 46 ++++++++++++++++++++++++++++------- src/pipeline/event_handler.rs | 30 ++++++++++++++++++++--- 5 files changed, 150 insertions(+), 42 deletions(-) (limited to 'src') diff --git a/src/dynamics/ccd/ccd_solver.rs b/src/dynamics/ccd/ccd_solver.rs index 77a1ff7..bdde135 100644 --- a/src/dynamics/ccd/ccd_solver.rs +++ b/src/dynamics/ccd/ccd_solver.rs @@ -4,7 +4,7 @@ use crate::geometry::{ColliderParent, ColliderSet, CollisionEvent, NarrowPhase}; use crate::math::Real; use crate::parry::utils::SortedPair; use crate::pipeline::{EventHandler, QueryPipeline, QueryPipelineMode}; -use crate::prelude::ActiveEvents; +use crate::prelude::{ActiveEvents, CollisionEventFlags}; use parry::query::{DefaultQueryDispatcher, QueryDispatcher}; use parry::utils::hashmap::HashMap; use std::collections::BinaryHeap; @@ -529,8 +529,18 @@ impl CCDSolver { .contains(ActiveEvents::COLLISION_EVENTS) { // Emit one intersection-started and one intersection-stopped event. - events.handle_collision_event(CollisionEvent::Started(toi.c1, toi.c2), None); - events.handle_collision_event(CollisionEvent::Stopped(toi.c1, toi.c2, false), None); + events.handle_collision_event( + bodies, + colliders, + CollisionEvent::Started(toi.c1, toi.c2, CollisionEventFlags::SENSOR), + None, + ); + events.handle_collision_event( + bodies, + colliders, + CollisionEvent::Stopped(toi.c1, toi.c2, CollisionEventFlags::SENSOR), + None, + ); } } diff --git a/src/geometry/contact_pair.rs b/src/geometry/contact_pair.rs index a75d58d..551ffde 100644 --- a/src/geometry/contact_pair.rs +++ b/src/geometry/contact_pair.rs @@ -1,7 +1,8 @@ -use crate::dynamics::RigidBodyHandle; -use crate::geometry::{ColliderHandle, Contact, ContactManifold}; +use crate::dynamics::{RigidBodyHandle, RigidBodySet}; +use crate::geometry::{ColliderHandle, ColliderSet, Contact, ContactManifold}; use crate::math::{Point, Real, Vector}; use crate::pipeline::EventHandler; +use crate::prelude::CollisionEventFlags; use parry::query::ContactManifoldsWorkspace; use super::CollisionEvent; @@ -69,22 +70,36 @@ impl IntersectionPair { pub(crate) fn emit_start_event( &mut self, + bodies: &RigidBodySet, + colliders: &ColliderSet, collider1: ColliderHandle, collider2: ColliderHandle, events: &dyn EventHandler, ) { self.start_event_emited = true; - events.handle_collision_event(CollisionEvent::new(collider1, collider2, true), None); + events.handle_collision_event( + bodies, + colliders, + CollisionEvent::Started(collider1, collider2, CollisionEventFlags::SENSOR), + None, + ); } pub(crate) fn emit_stop_event( &mut self, + bodies: &RigidBodySet, + colliders: &ColliderSet, collider1: ColliderHandle, collider2: ColliderHandle, events: &dyn EventHandler, ) { self.start_event_emited = false; - events.handle_collision_event(CollisionEvent::new(collider1, collider2, false), None); + events.handle_collision_event( + bodies, + colliders, + CollisionEvent::Stopped(collider1, collider2, CollisionEventFlags::SENSOR), + None, + ); } } @@ -155,20 +170,34 @@ impl ContactPair { deepest } - pub(crate) fn emit_start_event(&mut self, events: &dyn EventHandler) { + pub(crate) fn emit_start_event( + &mut self, + bodies: &RigidBodySet, + colliders: &ColliderSet, + events: &dyn EventHandler, + ) { self.start_event_emited = true; events.handle_collision_event( - CollisionEvent::new(self.collider1, self.collider2, true), + bodies, + colliders, + CollisionEvent::Started(self.collider1, self.collider2, CollisionEventFlags::empty()), Some(self), ); } - pub(crate) fn emit_stop_event(&mut self, events: &dyn EventHandler) { + pub(crate) fn emit_stop_event( + &mut self, + bodies: &RigidBodySet, + colliders: &ColliderSet, + events: &dyn EventHandler, + ) { self.start_event_emited = false; events.handle_collision_event( - CollisionEvent::new(self.collider1, self.collider2, false), + bodies, + colliders, + CollisionEvent::Stopped(self.collider1, self.collider2, CollisionEventFlags::empty()), Some(self), ); } diff --git a/src/geometry/mod.rs b/src/geometry/mod.rs index 7733ad2..d64c270 100644 --- a/src/geometry/mod.rs +++ b/src/geometry/mod.rs @@ -50,27 +50,28 @@ pub type PointProjection = parry::query::PointProjection; pub type TOI = parry::query::TOI; pub use parry::shape::SharedShape; +bitflags::bitflags! { + #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] + pub struct CollisionEventFlags: u32 { + /// Flag set if at least one of the colliders involved in the + /// collision was a sensor when the event was fired. + const SENSOR = 0b0001; + /// Flag set if a `CollisionEvent::Stopped` was fired because + /// at least one of the colliders was removed. + const REMOVED = 0b0010; + } +} + #[derive(Copy, Clone, Hash, Debug)] -/// Events occurring when two colliders start or stop being in contact (or intersecting) +/// Events occurring when two colliders start or stop colliding pub enum CollisionEvent { - /// Event occurring when two colliders start being in contact (or intersecting) - Started(ColliderHandle, ColliderHandle), - /// Event occurring when two colliders stop being in contact (or intersecting). - /// - /// The boolean is set to `true` of this event originates from at least one of - /// the colliders being removed from the `ColliderSet`. - Stopped(ColliderHandle, ColliderHandle, bool), + /// Event occurring when two colliders start colliding + Started(ColliderHandle, ColliderHandle, CollisionEventFlags), + /// Event occurring when two colliders stop colliding. + Stopped(ColliderHandle, ColliderHandle, CollisionEventFlags), } impl CollisionEvent { - pub(crate) fn new(h1: ColliderHandle, h2: ColliderHandle, start: bool) -> Self { - if start { - Self::Started(h1, h2) - } else { - Self::Stopped(h1, h2, false) - } - } - /// Is this a `Started` collision event? pub fn started(self) -> bool { matches!(self, CollisionEvent::Started(..)) @@ -84,14 +85,32 @@ impl CollisionEvent { /// The handle of the first collider involved in this collision event. pub fn collider1(self) -> ColliderHandle { match self { - Self::Started(h, _) | Self::Stopped(h, _, _) => h, + Self::Started(h, _, _) | Self::Stopped(h, _, _) => h, } } /// The handle of the second collider involved in this collision event. pub fn collider2(self) -> ColliderHandle { match self { - Self::Started(_, h) | Self::Stopped(_, h, _) => h, + Self::Started(_, h, _) | Self::Stopped(_, h, _) => h, + } + } + + /// Was at least one of the colliders involved in the collision a sensor? + pub fn sensor(self) -> bool { + match self { + Self::Started(_, _, f) | Self::Stopped(_, _, f) => { + f.contains(CollisionEventFlags::SENSOR) + } + } + } + + /// Was at least one of the colliders involved in the collision removed? + pub fn removed(self) -> bool { + match self { + Self::Started(_, _, f) | Self::Stopped(_, _, f) => { + f.contains(CollisionEventFlags::REMOVED) + } } } } diff --git a/src/geometry/narrow_phase.rs b/src/geometry/narrow_phase.rs index f4e8c58..3e7a13d 100644 --- a/src/geometry/narrow_phase.rs +++ b/src/geometry/narrow_phase.rs @@ -15,6 +15,7 @@ use crate::pipeline::{ ActiveEvents, ActiveHooks, ContactModificationContext, EventHandler, PairFilterContext, PhysicsHooks, }; +use crate::prelude::CollisionEventFlags; use parry::query::{DefaultQueryDispatcher, PersistentQueryDispatcher}; use parry::utils::IsometryOpt; use std::collections::HashMap; @@ -317,14 +318,24 @@ impl NarrowPhase { } if pair.start_event_emited { - events.handle_collision_event(CollisionEvent::Stopped(a, b, true), Some(pair)); + events.handle_collision_event( + bodies, + colliders, + CollisionEvent::Stopped(a, b, CollisionEventFlags::REMOVED), + Some(pair), + ); } } } else { // If there is no island, don’t wake-up bodies, but do send the Stopped collision event. for (a, b, pair) in self.contact_graph.interactions_with(contact_graph_id) { if pair.start_event_emited { - events.handle_collision_event(CollisionEvent::Stopped(a, b, true), Some(pair)); + events.handle_collision_event( + bodies, + colliders, + CollisionEvent::Stopped(a, b, CollisionEventFlags::REMOVED), + Some(pair), + ); } } } @@ -332,7 +343,16 @@ impl NarrowPhase { // Generate Stopped collision events for intersections. for (a, b, pair) in self.intersection_graph.interactions_with(contact_graph_id) { if pair.start_event_emited { - events.handle_collision_event(CollisionEvent::Stopped(a, b, true), None); + events.handle_collision_event( + bodies, + colliders, + CollisionEvent::Stopped( + a, + b, + CollisionEventFlags::REMOVED | CollisionEventFlags::SENSOR, + ), + None, + ); } } @@ -495,7 +515,13 @@ impl NarrowPhase { if (co1.flags.active_events | co2.flags.active_events) .contains(ActiveEvents::COLLISION_EVENTS) { - intersection.emit_stop_event(pair.collider1, pair.collider2, events) + intersection.emit_stop_event( + bodies, + colliders, + pair.collider1, + pair.collider2, + events, + ) } } } @@ -521,7 +547,7 @@ impl NarrowPhase { if (co1.flags.active_events | co2.flags.active_events) .contains(ActiveEvents::COLLISION_EVENTS) { - ctct.emit_stop_event(events); + ctct.emit_stop_event(bodies, colliders, events); } } } @@ -724,9 +750,11 @@ impl NarrowPhase { && had_intersection != edge.weight.intersecting { if edge.weight.intersecting { - edge.weight.emit_start_event(handle1, handle2, events); + edge.weight + .emit_start_event(bodies, colliders, handle1, handle2, events); } else { - edge.weight.emit_stop_event(handle1, handle2, events); + edge.weight + .emit_stop_event(bodies, colliders, handle1, handle2, events); } } }); @@ -928,9 +956,9 @@ impl NarrowPhase { if pair.has_any_active_contact != had_any_active_contact { if active_events.contains(ActiveEvents::COLLISION_EVENTS) { if pair.has_any_active_contact { - pair.emit_start_event(events); + pair.emit_start_event(bodies, colliders, events); } else { - pair.emit_stop_event(events); + pair.emit_stop_event(bodies, colliders, events); } } } diff --git a/src/pipeline/event_handler.rs b/src/pipeline/event_handler.rs index f08a383..f6c33ec 100644 --- a/src/pipeline/event_handler.rs +++ b/src/pipeline/event_handler.rs @@ -1,4 +1,5 @@ -use crate::geometry::{CollisionEvent, ContactPair}; +use crate::dynamics::RigidBodySet; +use crate::geometry::{ColliderSet, CollisionEvent, ContactPair}; use crossbeam::channel::Sender; bitflags::bitflags! { @@ -27,14 +28,29 @@ pub trait EventHandler: Send + Sync { /// /// # Parameters /// * `event` - The collision event. + /// * `bodies` - The set of rigid-bodies. + /// * `colliders` - The set of colliders. /// * `contact_pair` - The current state of contacts between the two colliders. This is set ot `None` /// if at least one of the collider is a sensor (in which case no contact information /// is ever computed). - fn handle_collision_event(&self, event: CollisionEvent, contact_pair: Option<&ContactPair>); + fn handle_collision_event( + &self, + bodies: &RigidBodySet, + colliders: &ColliderSet, + event: CollisionEvent, + contact_pair: Option<&ContactPair>, + ); } impl EventHandler for () { - fn handle_collision_event(&self, _event: CollisionEvent, _contact_pair: Option<&ContactPair>) {} + fn handle_collision_event( + &self, + _bodies: &RigidBodySet, + _colliders: &ColliderSet, + _event: CollisionEvent, + _contact_pair: Option<&ContactPair>, + ) { + } } /// A collision event handler that collects events into a crossbeam channel. @@ -50,7 +66,13 @@ impl ChannelEventCollector { } impl EventHandler for ChannelEventCollector { - fn handle_collision_event(&self, event: CollisionEvent, _: Option<&ContactPair>) { + fn handle_collision_event( + &self, + _bodies: &RigidBodySet, + _colliders: &ColliderSet, + event: CollisionEvent, + _: Option<&ContactPair>, + ) { let _ = self.event_sender.send(event); } } -- cgit From 95418c218b387dacbeaa66325e5a321e5106d273 Mon Sep 17 00:00:00 2001 From: Sébastien Crozet Date: Thu, 28 Apr 2022 13:03:55 +0200 Subject: Rename RigidBody::set_mass_properties -> set_additional_mass_properties --- src/dynamics/rigid_body.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/dynamics/rigid_body.rs b/src/dynamics/rigid_body.rs index cf52c1f..24e9754 100644 --- a/src/dynamics/rigid_body.rs +++ b/src/dynamics/rigid_body.rs @@ -305,20 +305,26 @@ impl RigidBody { self.ccd.ccd_active } - /// Sets the rigid-body's initial mass properties. + /// Sets the rigid-body's additional mass properties. /// /// If `wake_up` is `true` then the rigid-body will be woken up if it was /// put to sleep because it did not move for a while. #[inline] - pub fn set_mass_properties(&mut self, props: MassProperties, wake_up: bool) { - if self.mprops.local_mprops != props { - if self.is_dynamic() && wake_up { - self.wake_up(true); - } + pub fn set_additional_mass_properties(&mut self, props: MassProperties, wake_up: bool) { + if let Some(add_mprops) = &mut self.mprops.additional_local_mprops { + self.mprops.local_mprops += props; + self.mprops.local_mprops -= **add_mprops; + **add_mprops = props; + } else { + self.mprops.additional_local_mprops = Some(Box::new(props)); + self.mprops.local_mprops += props; + } - self.mprops.local_mprops = props; - self.update_world_mass_properties(); + if self.is_dynamic() && wake_up { + self.wake_up(true); } + + self.update_world_mass_properties(); } /// The handles of colliders attached to this rigid body. -- cgit From fd12d76102884150a40b24ca812fffe35d26d09d Mon Sep 17 00:00:00 2001 From: Sébastien Crozet Date: Thu, 28 Apr 2022 13:04:14 +0200 Subject: Fix panic when the world is stepped with dt = 0 --- src/dynamics/integration_parameters.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/dynamics/integration_parameters.rs b/src/dynamics/integration_parameters.rs index 84c8117..6a86a2a 100644 --- a/src/dynamics/integration_parameters.rs +++ b/src/dynamics/integration_parameters.rs @@ -93,12 +93,12 @@ impl IntegrationParameters { /// The ERP coefficient, multiplied by the inverse timestep length. pub fn erp_inv_dt(&self) -> Real { - self.erp / self.dt + self.erp * self.inv_dt() } /// The joint ERP coefficient, multiplied by the inverse timestep length. pub fn joint_erp_inv_dt(&self) -> Real { - self.joint_erp / self.dt + self.joint_erp * self.inv_dt() } /// The CFM factor to be used in the constraints resolution. -- cgit From 8ffb0d1658d448074f5ca2b77aee33f755761e24 Mon Sep 17 00:00:00 2001 From: Sébastien Crozet Date: Thu, 28 Apr 2022 13:05:00 +0200 Subject: Take round shapes into account in 2D debug render --- .../joint/multibody_joint/multibody_joint_set.rs | 10 +++++++ .../debug_render_pipeline/debug_render_pipeline.rs | 35 ++++++++++++++-------- .../debug_render_pipeline/debug_render_style.rs | 4 ++- 3 files changed, 35 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/dynamics/joint/multibody_joint/multibody_joint_set.rs b/src/dynamics/joint/multibody_joint/multibody_joint_set.rs index 748530f..06dff5d 100644 --- a/src/dynamics/joint/multibody_joint/multibody_joint_set.rs +++ b/src/dynamics/joint/multibody_joint/multibody_joint_set.rs @@ -299,10 +299,20 @@ impl MultibodyJointSet { } /// Gets a mutable reference to the multibody identified by its `handle`. + pub fn get_mut(&mut self, handle: MultibodyJointHandle) -> Option<(&mut Multibody, usize)> { + let link = self.rb2mb.get(handle.0)?; + let multibody = self.multibodies.get_mut(link.multibody.0)?; + Some((multibody, link.id)) + } + + /// Gets a mutable reference to the multibody identified by its `handle`. + /// + /// This method will bypass any modification-detection automatically done by the MultibodyJointSet. pub fn get_mut_internal( &mut self, handle: MultibodyJointHandle, ) -> Option<(&mut Multibody, usize)> { + // TODO: modification tracking? let link = self.rb2mb.get(handle.0)?; let multibody = self.multibodies.get_mut(link.multibody.0)?; Some((multibody, link.id)) diff --git a/src/pipeline/debug_render_pipeline/debug_render_pipeline.rs b/src/pipeline/debug_render_pipeline/debug_render_pipeline.rs index 36593ce..1c929c2 100644 --- a/src/pipeline/debug_render_pipeline/debug_render_pipeline.rs +++ b/src/pipeline/debug_render_pipeline/debug_render_pipeline.rs @@ -285,17 +285,20 @@ impl DebugRenderPipeline { * Round shapes. */ TypedShape::RoundCuboid(s) => { - self.render_shape(object, backend, &s.base_shape, pos, color) + let vtx = s.to_polyline(self.style.border_subdivisions); + backend.draw_line_strip(object, &vtx, pos, &Vector::repeat(1.0), color, true) } TypedShape::RoundTriangle(s) => { - self.render_shape(object, backend, &s.base_shape, pos, color) + // TODO: take roundness into account. + self.render_shape(object, backend, &s.inner_shape, pos, color) } - // TypedShape::RoundTriMesh(s) => self.render_shape(backend, &s.base_shape, pos, color), + // TypedShape::RoundTriMesh(s) => self.render_shape(backend, &s.inner_shape, pos, color), // TypedShape::RoundHeightField(s) => { - // self.render_shape(backend, &s.base_shape, pos, color) + // self.render_shape(backend, &s.inner_shape, pos, color) // } TypedShape::RoundConvexPolygon(s) => { - self.render_shape(object, backend, &s.base_shape, pos, color) + let vtx = s.to_polyline(self.style.border_subdivisions); + backend.draw_line_strip(object, &vtx, pos, &Vector::repeat(1.0), color, true) } TypedShape::Custom(_) => {} } @@ -326,7 +329,6 @@ impl DebugRenderPipeline { let (vtx, idx) = &self.instances[&TypeId::of::()]; backend.draw_polyline(object, vtx, idx, pos, &(s.half_extents * 2.0), color) } - #[cfg(feature = "dim3")] TypedShape::Capsule(s) => { let (vtx, idx) = s.to_outline(self.style.subdivisions); backend.draw_polyline(object, &vtx, &idx, pos, &Vector::repeat(1.0), color) @@ -426,23 +428,30 @@ impl DebugRenderPipeline { * Round shapes. */ TypedShape::RoundCuboid(s) => { - self.render_shape(object, backend, &s.base_shape, pos, color) + let (vtx, idx) = s.to_outline(self.style.border_subdivisions); + backend.draw_polyline(object, &vtx, &idx, pos, &Vector::repeat(1.0), color) } TypedShape::RoundTriangle(s) => { - self.render_shape(object, backend, &s.base_shape, pos, color) + // TODO: take roundness into account. + self.render_shape(object, backend, &s.inner_shape, pos, color) } - // TypedShape::RoundTriMesh(s) => self.render_shape(object, backend, &s.base_shape, pos, color), + // TypedShape::RoundTriMesh(s) => self.render_shape(object, backend, &s.inner_shape, pos, color), // TypedShape::RoundHeightField(s) => { - // self.render_shape(object, backend, &s.base_shape, pos, color) + // self.render_shape(object, backend, &s.inner_shape, pos, color) // } TypedShape::RoundCylinder(s) => { - self.render_shape(object, backend, &s.base_shape, pos, color) + let (vtx, idx) = + s.to_outline(self.style.subdivisions, self.style.border_subdivisions); + backend.draw_polyline(object, &vtx, &idx, pos, &Vector::repeat(1.0), color) } TypedShape::RoundCone(s) => { - self.render_shape(object, backend, &s.base_shape, pos, color) + let (vtx, idx) = + s.to_outline(self.style.subdivisions, self.style.border_subdivisions); + backend.draw_polyline(object, &vtx, &idx, pos, &Vector::repeat(1.0), color) } TypedShape::RoundConvexPolyhedron(s) => { - self.render_shape(object, backend, &s.base_shape, pos, color) + let (vtx, idx) = s.to_outline(self.style.border_subdivisions); + backend.draw_polyline(object, &vtx, &idx, pos, &Vector::repeat(1.0), color) } TypedShape::Custom(_) => {} } diff --git a/src/pipeline/debug_render_pipeline/debug_render_style.rs b/src/pipeline/debug_render_pipeline/debug_render_style.rs index 9d4b6cc..5401596 100644 --- a/src/pipeline/debug_render_pipeline/debug_render_style.rs +++ b/src/pipeline/debug_render_pipeline/debug_render_style.rs @@ -3,9 +3,10 @@ /// The default colors are provided in HSLA (Hue Saturation Lightness Alpha) format. pub type DebugColor = [f32; 4]; -#[derive(Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq)] pub struct DebugRenderStyle { pub subdivisions: u32, + pub border_subdivisions: u32, pub collider_dynamic_color: DebugColor, pub collider_fixed_color: DebugColor, pub collider_kinematic_color: DebugColor, @@ -22,6 +23,7 @@ impl Default for DebugRenderStyle { fn default() -> Self { Self { subdivisions: 20, + border_subdivisions: 5, collider_dynamic_color: [340.0, 1.0, 0.3, 1.0], collider_kinematic_color: [20.0, 1.0, 0.3, 1.0], collider_fixed_color: [30.0, 1.0, 0.4, 1.0], -- cgit From 65824e74f3a949343059b3bf5ab51bc7920f416d Mon Sep 17 00:00:00 2001 From: Sébastien Crozet Date: Thu, 28 Apr 2022 17:30:35 +0200 Subject: Add comments for the debug-renderer --- src/geometry/mod.rs | 1 + .../debug_render_pipeline/debug_render_backend.rs | 17 ++++++++++++++ .../debug_render_pipeline/debug_render_pipeline.rs | 26 ++++++++++++++++++++-- .../debug_render_pipeline/debug_render_style.rs | 19 ++++++++++++++++ 4 files changed, 61 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/geometry/mod.rs b/src/geometry/mod.rs index d64c270..34d3707 100644 --- a/src/geometry/mod.rs +++ b/src/geometry/mod.rs @@ -51,6 +51,7 @@ pub type TOI = parry::query::TOI; pub use parry::shape::SharedShape; bitflags::bitflags! { + /// Flags providing more information regarding a collision event. #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] pub struct CollisionEventFlags: u32 { /// Flag set if at least one of the colliders involved in the diff --git a/src/pipeline/debug_render_pipeline/debug_render_backend.rs b/src/pipeline/debug_render_pipeline/debug_render_backend.rs index 27a67e4..4fa86e4 100644 --- a/src/pipeline/debug_render_pipeline/debug_render_backend.rs +++ b/src/pipeline/debug_render_pipeline/debug_render_backend.rs @@ -6,16 +6,31 @@ use crate::math::{Isometry, Point, Real, Vector}; use crate::prelude::{ColliderHandle, MultibodyJointHandle}; use na::Scale; +/// The object currently being rendered by the debug-renderer. #[derive(Copy, Clone)] pub enum DebugRenderObject<'a> { + /// A rigid-body is being rendered. RigidBody(RigidBodyHandle, &'a RigidBody), + /// A collider is being rendered. Collider(ColliderHandle, &'a Collider), + /// An impulse-joint is being rendered. ImpulseJoint(ImpulseJointHandle, &'a ImpulseJoint), + /// A multibody joint is being rendered. MultibodyJoint(MultibodyJointHandle, &'a Multibody, &'a MultibodyLink), + /// Another element is being rendered. Other, } +/// Trait implemented by graphics backends responsible for rendering the physics scene. +/// +/// The only thing that is required from the graphics backend is to be able to render +/// a colored line. Note that the color is only a suggestion and is computed from the +/// `DebugRenderStyle`. The backend is free to apply its own style, for example based on +/// the `object` being rendered. pub trait DebugRenderBackend { + /// Draws a colored line. + /// + /// Note that this method can be called multiple time for the same `object`. fn draw_line( &mut self, object: DebugRenderObject, @@ -24,6 +39,7 @@ pub trait DebugRenderBackend { color: [f32; 4], ); + /// Draws a set of line. fn draw_polyline( &mut self, object: DebugRenderObject, @@ -40,6 +56,7 @@ pub trait DebugRenderBackend { } } + /// Draws a chain of line. fn draw_line_strip( &mut self, object: DebugRenderObject, diff --git a/src/pipeline/debug_render_pipeline/debug_render_pipeline.rs b/src/pipeline/debug_render_pipeline/debug_render_pipeline.rs index 1c929c2..14da40d 100644 --- a/src/pipeline/debug_render_pipeline/debug_render_pipeline.rs +++ b/src/pipeline/debug_render_pipeline/debug_render_pipeline.rs @@ -13,20 +13,31 @@ use std::any::TypeId; use std::collections::HashMap; bitflags::bitflags! { + /// Flags indicating what part of the physics engine should be rendered + /// by the debug-renderer. pub struct DebugRenderMode: u32 { + /// If this flag is set, the collider shapes will be rendered. const COLLIDER_SHAPES = 1 << 0; + /// If this flag is set, the local coordinate axes of rigid-bodies will be rendered. const RIGID_BODY_AXES = 1 << 1; + /// If this flag is set, the multibody joints will be rendered. const MULTIBODY_JOINTS = 1 << 2; + /// If this flag is set, the impulse joints will be rendered. const IMPULSE_JOINTS = 1 << 3; } } +/// Pipeline responsible for rendering the state of the physics engine for debugging purpose. pub struct DebugRenderPipeline { #[cfg(feature = "dim2")] instances: HashMap>>, #[cfg(feature = "dim3")] instances: HashMap>, Vec<[u32; 2]>)>, + /// The style used to compute the line colors for each element + /// to render. pub style: DebugRenderStyle, + /// Flags controlling what part of the physics engine need to + /// be rendered. pub mode: DebugRenderMode, } @@ -37,6 +48,7 @@ impl Default for DebugRenderPipeline { } impl DebugRenderPipeline { + /// Creates a new debug-render pipeline from a given style and flags. pub fn new(style: DebugRenderStyle, mode: DebugRenderMode) -> Self { Self { instances: outlines::instances(style.subdivisions), @@ -45,10 +57,13 @@ impl DebugRenderPipeline { } } + /// Creates a new debug-render pipeline that renders everything + /// it can from the physics state. pub fn render_all(style: DebugRenderStyle) -> Self { Self::new(style, DebugRenderMode::all()) } + /// Render the scene. pub fn render( &mut self, backend: &mut impl DebugRenderBackend, @@ -57,11 +72,12 @@ impl DebugRenderPipeline { impulse_joints: &ImpulseJointSet, multibody_joints: &MultibodyJointSet, ) { - self.render_bodies(backend, bodies); + self.render_rigid_bodies(backend, bodies); self.render_colliders(backend, bodies, colliders); self.render_joints(backend, bodies, impulse_joints, multibody_joints); } + /// Render only the joints from the scene. pub fn render_joints( &mut self, backend: &mut impl DebugRenderBackend, @@ -137,7 +153,12 @@ impl DebugRenderPipeline { } } - pub fn render_bodies(&mut self, backend: &mut impl DebugRenderBackend, bodies: &RigidBodySet) { + /// Render only the rigid-bodies from the scene. + pub fn render_rigid_bodies( + &mut self, + backend: &mut impl DebugRenderBackend, + bodies: &RigidBodySet, + ) { for (handle, rb) in bodies.iter() { let object = DebugRenderObject::RigidBody(handle, rb); @@ -165,6 +186,7 @@ impl DebugRenderPipeline { } } + /// Render only the colliders from the scene. pub fn render_colliders( &mut self, backend: &mut impl DebugRenderBackend, diff --git a/src/pipeline/debug_render_pipeline/debug_render_style.rs b/src/pipeline/debug_render_pipeline/debug_render_style.rs index 5401596..0b6fa46 100644 --- a/src/pipeline/debug_render_pipeline/debug_render_style.rs +++ b/src/pipeline/debug_render_pipeline/debug_render_style.rs @@ -3,19 +3,38 @@ /// The default colors are provided in HSLA (Hue Saturation Lightness Alpha) format. pub type DebugColor = [f32; 4]; +/// Style used for computing colors when rendering the scene. #[derive(Copy, Clone, Debug, PartialEq)] pub struct DebugRenderStyle { + /// The number of subdivision used to approximate the curved + /// parts of a shape with smooth faces. pub subdivisions: u32, + /// The number of subdivision used to approimate the curved + /// borders of round shapes. pub border_subdivisions: u32, + /// The color of colliders attached to dynamic rigid-bodies. pub collider_dynamic_color: DebugColor, + /// The color of colliders attached to fixed rigid-bodies. pub collider_fixed_color: DebugColor, + /// The color of colliders attached to kinematic rigid-bodies. pub collider_kinematic_color: DebugColor, + /// The color of colliders not attached to any rigid-body. pub collider_parentless_color: DebugColor, + /// The color of the line between a rigid-body’s center-of-mass and the + /// anchors of its attached impulse joints. pub impulse_joint_anchor_color: DebugColor, + /// The color of the line between the two anchors of an impulse joint. pub impulse_joint_separation_color: DebugColor, + /// The color of the line between a rigid-body’s center-of-mass and the + /// anchors of its attached multibody joints. pub multibody_joint_anchor_color: DebugColor, + /// The color of the line between the two anchors of a multibody joint. pub multibody_joint_separation_color: DebugColor, + /// If a rigid-body is sleeping, its attached entities will have their colors + /// multiplied by this array. (For a joint, both attached rigid-bodies must be sleeping + /// or non-dynamic for this multiplier to be applied). pub sleep_color_multiplier: [f32; 4], + /// The length of the local coordinate axes rendered for a rigid-body. pub rigid_body_axes_length: f32, } -- cgit From 7dc038aec66783d72abda446d6251385e6ad30f4 Mon Sep 17 00:00:00 2001 From: Sébastien Crozet Date: Thu, 28 Apr 2022 17:51:17 +0200 Subject: Fix test build --- src/pipeline/debug_render_pipeline/debug_render_style.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/pipeline/debug_render_pipeline/debug_render_style.rs b/src/pipeline/debug_render_pipeline/debug_render_style.rs index 0b6fa46..ac2b3a3 100644 --- a/src/pipeline/debug_render_pipeline/debug_render_style.rs +++ b/src/pipeline/debug_render_pipeline/debug_render_style.rs @@ -1,3 +1,5 @@ +use crate::math::Real; + /// A color for debug-rendering. /// /// The default colors are provided in HSLA (Hue Saturation Lightness Alpha) format. @@ -35,7 +37,7 @@ pub struct DebugRenderStyle { /// or non-dynamic for this multiplier to be applied). pub sleep_color_multiplier: [f32; 4], /// The length of the local coordinate axes rendered for a rigid-body. - pub rigid_body_axes_length: f32, + pub rigid_body_axes_length: Real, } impl Default for DebugRenderStyle { -- cgit