From b00113ed2f9e4824a254027b57c9b4a07c4c2307 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Sat, 23 Mar 2024 10:15:07 +0100 Subject: [PATCH 01/71] fix: implement linear-coupled-motor constraint between two dynamic bodies Fix #602 --- .../contact_constraints_set.rs | 1 + .../joint_constraint_builder.rs | 76 +++++++++++++++++++ .../joint_velocity_constraint.rs | 43 +++++------ 3 files changed, 98 insertions(+), 22 deletions(-) diff --git a/src/dynamics/solver/contact_constraint/contact_constraints_set.rs b/src/dynamics/solver/contact_constraint/contact_constraints_set.rs index b5fdb6d..4d88949 100644 --- a/src/dynamics/solver/contact_constraint/contact_constraints_set.rs +++ b/src/dynamics/solver/contact_constraint/contact_constraints_set.rs @@ -25,6 +25,7 @@ use { crate::math::SIMD_WIDTH, }; +#[derive(Debug)] pub struct ConstraintsCounts { pub num_constraints: usize, pub num_jacobian_lines: usize, diff --git a/src/dynamics/solver/joint_constraint/joint_constraint_builder.rs b/src/dynamics/solver/joint_constraint/joint_constraint_builder.rs index 00bead1..44716b2 100644 --- a/src/dynamics/solver/joint_constraint/joint_constraint_builder.rs +++ b/src/dynamics/solver/joint_constraint/joint_constraint_builder.rs @@ -551,6 +551,82 @@ impl JointTwoBodyConstraintHelper { constraint } + pub fn motor_linear_coupled( + &self, + params: &IntegrationParameters, + joint_id: [JointIndex; LANES], + body1: &JointSolverBody, + body2: &JointSolverBody, + coupled_axes: u8, + motor_params: &MotorParameters, + limits: Option<[N; 2]>, + writeback_id: WritebackId, + ) -> JointTwoBodyConstraint { + let inv_dt = N::splat(params.inv_dt()); + + let mut lin_jac = Vector::zeros(); + let mut ang_jac1: AngVector = na::zero(); + let mut ang_jac2: AngVector = na::zero(); + + for i in 0..DIM { + if coupled_axes & (1 << i) != 0 { + let coeff = self.basis.column(i).dot(&self.lin_err); + lin_jac += self.basis.column(i) * coeff; + #[cfg(feature = "dim2")] + { + ang_jac1 += self.cmat1_basis[i] * coeff; + ang_jac2 += self.cmat2_basis[i] * coeff; + } + #[cfg(feature = "dim3")] + { + ang_jac1 += self.cmat1_basis.column(i) * coeff; + ang_jac2 += self.cmat2_basis.column(i) * coeff; + } + } + } + + let dist = lin_jac.norm(); + let inv_dist = crate::utils::simd_inv(dist); + lin_jac *= inv_dist; + ang_jac1 *= inv_dist; + ang_jac2 *= inv_dist; + + let mut rhs_wo_bias = N::zero(); + if motor_params.erp_inv_dt != N::zero() { + rhs_wo_bias += (dist - motor_params.target_pos) * motor_params.erp_inv_dt; + } + + let mut target_vel = motor_params.target_vel; + if let Some(limits) = limits { + target_vel = + target_vel.simd_clamp((limits[0] - dist) * inv_dt, (limits[1] - dist) * inv_dt); + }; + + rhs_wo_bias += -target_vel; + + ang_jac1 = body1.sqrt_ii * ang_jac1; + ang_jac2 = body2.sqrt_ii * ang_jac2; + + JointTwoBodyConstraint { + joint_id, + solver_vel1: body1.solver_vel, + solver_vel2: body2.solver_vel, + im1: body1.im, + im2: body2.im, + impulse: N::zero(), + impulse_bounds: [-motor_params.max_impulse, motor_params.max_impulse], + lin_jac, + ang_jac1, + ang_jac2, + inv_lhs: N::zero(), // Will be set during ortogonalization. + cfm_coeff: motor_params.cfm_coeff, + cfm_gain: motor_params.cfm_gain, + rhs: rhs_wo_bias, + rhs_wo_bias, + writeback_id, + } + } + pub fn lock_linear( &self, params: &IntegrationParameters, diff --git a/src/dynamics/solver/joint_constraint/joint_velocity_constraint.rs b/src/dynamics/solver/joint_constraint/joint_velocity_constraint.rs index e184e3d..60c42d3 100644 --- a/src/dynamics/solver/joint_constraint/joint_velocity_constraint.rs +++ b/src/dynamics/solver/joint_constraint/joint_velocity_constraint.rs @@ -217,28 +217,26 @@ impl JointTwoBodyConstraint { } if (motor_axes & coupled_axes) & JointAxesMask::LIN_AXES.bits() != 0 { - // if (motor_axes & !coupled_axes) & (1 << first_coupled_lin_axis_id) != 0 { - // let limits = if limit_axes & (1 << first_coupled_lin_axis_id) != 0 { - // Some([ - // joint.limits[first_coupled_lin_axis_id].min, - // joint.limits[first_coupled_lin_axis_id].max, - // ]) - // } else { - // None - // }; - // - // out[len] = builder.motor_linear_coupled - // params, - // [joint_id], - // body1, - // body2, - // coupled_axes, - // &joint.motors[first_coupled_lin_axis_id].motor_params(params.dt), - // limits, - // WritebackId::Motor(first_coupled_lin_axis_id), - // ); - // len += 1; - // } + let limits = if (limit_axes & (1 << first_coupled_lin_axis_id)) != 0 { + Some([ + joint.limits[first_coupled_lin_axis_id].min, + joint.limits[first_coupled_lin_axis_id].max, + ]) + } else { + None + }; + + out[len] = builder.motor_linear_coupled( + params, + [joint_id], + body1, + body2, + coupled_axes, + &joint.motors[first_coupled_lin_axis_id].motor_params(params.dt), + limits, + WritebackId::Motor(first_coupled_lin_axis_id), + ); + len += 1; } JointTwoBodyConstraintHelper::finalize_constraints(&mut out[start..len]); @@ -350,6 +348,7 @@ impl JointTwoBodyConstraint { } } } + #[cfg(feature = "simd-is-enabled")] impl JointTwoBodyConstraint { pub fn lock_axes( From 6507b7f4fa15e8d09c8c7b62613d0fd561231c0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Sat, 23 Mar 2024 10:16:59 +0100 Subject: [PATCH 02/71] chore: update changelog --- CHANGELOG.md | 239 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 180 insertions(+), 59 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f57869..6515925 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,16 @@ +## Unreleased + +### Fix + +- Fix crash when simulating a spring joint between two dynamic bodies. + ## v0.18.0 (24 Jan. 2024) + The main highlight of this release is the implementation of a new non-linear constraints solver for better stability and increased convergence rates. See [#579](https://github.com/dimforge/rapier/pull/579) for additional information. -In order to adjust the number of iterations of the new solver, simply adjust `IntegrationParameters::num_solver_iterations`. +In order to adjust the number of iterations of the new solver, simply +adjust `IntegrationParameters::num_solver_iterations`. If recovering the old solver behavior is useful to you, call `IntegrationParameters::switch_to_standard_pgs_solver()`. It is now possible to specify some additional solver iteration for specific rigid-bodies (and everything interacting @@ -11,11 +19,15 @@ with it directly or indirectly through contacts and joints): `RigidBodyBuilder:: without affecting performance of the other parts of the simulation. ### Fix -- Fix bug causing angular joint limits and motor to sometimes only take into account half of the angles specified by the user. + +- Fix bug causing angular joint limits and motor to sometimes only take into account half of the angles specified by the + user. - Fix bug where collisions would not be re-computed after a collider was re-enabled. ### Added -- Add a `SpringJoint` and `SpringJointBuilder` for simulating springs with customizable stiffness and damping coefficients. + +- Add a `SpringJoint` and `SpringJointBuilder` for simulating springs with customizable stiffness and damping + coefficients. - Add `SphericalJoint::local_frame1/2`, `::set_local_frame1/2`, and `SphericalJointBuilder::local_frame1/2` to set both the joint’s anchor and reference orientation. - Add `EffectiveCharacterMovement::is_sliding_down_slope` to indicate if the character controlled by the kinematic @@ -27,6 +39,7 @@ without affecting performance of the other parts of the simulation. - Fix debug-renderer showing moved kinematic rigid-bodies only at their initial position. ### Modified + - Make `Wheel::friction_slip` public to customize the front friction applied to the vehicle controller’s wheels. - Add the `DebugRenderBackend::filter_object` predicate that can be implemented to apply custom filtering rules on the objects being rendered. @@ -35,19 +48,24 @@ without affecting performance of the other parts of the simulation. - Rename `NarrowPhase::intersections_with` to `NarrowPhase::intersection_pairs_with`. ## v0.17.2 (26 Feb. 2023) + ### Fix + - Fix issue with convex polyhedron jitter due to missing contacts. - Fix character controller getting stuck against vertical walls. - Fix character controller’s snapping to ground not triggering sometimes. - Fix character controller’s horizontal offset being mostly ignored and some instances of vertical offset being ignored. ## v0.17.1 (22 Jan. 2023) + ### Fix + - Fix bug resulting in dynamic rigid-bodies acting as kinematic bodies after being disabled and then re-enabled. - ## v0.17.0 (15 Jan. 2023) + ### Added + - Add `RigidBody::set_enabled`, `RigidBody::is_enabled`, `RigidBodyBuilder::enabled` to enable/disable a rigid-body without having to delete it. Disabling a rigid-body attached to a multibody joint isn’t supported yet. - Add `Collider::set_enabled`, `Collider::is_enabled`, `ColliderBuilder::enabled` to enable/disable a collider @@ -61,37 +79,48 @@ without affecting performance of the other parts of the simulation. - Add `RigidBody::locked_axes` to get the rigid-body axes that were locked by the user. ### Modified + - Add the `QueryPipeline` as an optional argument to `PhysicsPipeline::step` and `CollisionPipeline::step`. If this - argument is specified, then the query pipeline will be incrementally (i.e. more efficiently) update at the same time as + argument is specified, then the query pipeline will be incrementally (i.e. more efficiently) update at the same time + as these other pipelines. In that case, calling `QueryPipeline::update` a `PhysicsPipeline::step` isn’t needed. - `RigidBody::set_body_type` now takes an extra boolean argument indicating if the rigid-body should be woken-up (if it becomes dynamic). - `RigidBody::mass_properties` now also returns the world-space mass-properties of the rigid-body. ### Fix + - Fix bug resulting in rigid-bodies being awakened after they are created, even if they are created sleeping. ## v0.16.1 (10 Nov. 2022) + ### Fix + - Fixed docs build on `docs.rs`. ## v0.16.0 (30 Oct. 2022) + ### Added + - Implement `Copy` for `CharacterCollision`. - Implement conversion (`From` trait) between `Group` and `u32`. - Add `ColliderBuilder::trimesh_with_flags` to build a triangle mesh with specific flags controlling its initialization. ### Modified + - Rename `AABB` to `Aabb` to comply with Rust’s style guide. - Switch to `parry 0.11`. ### Fix + - Fix internal edges of 3D triangle meshes or 3D heightfields generating invalid contacts preventing balls from moving straight. ## v0.15.0 (02 Oct. 2022) + ### Added + - Add a **kinematic character** controller implementation. See the `control` module. The character controller currently supports the following features: - Slide on uneven terrains @@ -104,8 +133,8 @@ without affecting performance of the other parts of the simulation. - Report information on the obstacles it hit on its path. - Implement `serde` serialization/deserialization for `CollisionEvents` when the `serde-serialize` feature is enabled - ### Modified + - The methods `Collider::set_rotation`, `RigidBody::set_rotation`, and `RigidBody::set_next_kinematic_rotation` now take a rotation (`UnitQuaternion` or `UnitComplex`) instead of a vector/angle. - The method `QueryFilter::exclude_dynamic` is now a static method (the `self` argument was removed). @@ -117,7 +146,9 @@ without affecting performance of the other parts of the simulation. position. ## v0.14.0 (09 July 2022) + ### Fixed + - Fix unpredictable broad-phase panic when using small colliders in the simulation. - Fix collision events being incorrectly generated for any shape that produces multiple contact manifolds (like triangle meshes). @@ -125,6 +156,7 @@ without affecting performance of the other parts of the simulation. to `CollisionPipeline::step`. ### Modified + - The `RigidBodyBuilder::additional_mass` method will now result in the additional angular inertia being automatically computed based on the shapes of the colliders attached to the rigid-body. - Remove the deprecated methods `RigidBodyBuilder::mass`, `::principal_angular_inertia`, `::principal_inertia`. @@ -137,6 +169,7 @@ without affecting performance of the other parts of the simulation. `RigidBodyBuilder::enabled_rotations` and `RigidBodyBuilder::enabled_translations`. ### Added + - Add `RigidBody::recompute_mass_properties_from_colliders` to force the immediate computation of a rigid-body’s mass properties (instead of waiting for them to be recomputed during the next timestep). This is useful to be able to read immediately the result of a change of a rigid-body @@ -149,7 +182,8 @@ without affecting performance of the other parts of the simulation. - Add `ColliderBuilder::mass` to set the mass of the collider instead of its density. Its angular inertia tensor will be automatically computed based on this mass and its shape. - Add `Collider::mass` and `Collider::volume` to retrieve the mass or volume of a collider. -- Add the `QueryFilter` that is now used by all the scene queries instead of the `CollisionGroups` and `Fn(ColliderHandle) -> bool` +- Add the `QueryFilter` that is now used by all the scene queries instead of the `CollisionGroups` + and `Fn(ColliderHandle) -> bool` closure. This `QueryFilter` provides easy access to most common filtering strategies (e.g. dynamic bodies only, excluding one particular collider, etc.) for scene queries. - Add force reporting based on contact force events. The `EventHandler` trait has been modified to include @@ -161,12 +195,15 @@ without affecting performance of the other parts of the simulation. contact force events. ## v0.13.0 (31 May 2022) + ### Fixed + - Fix incorrect sensor events being generated after collider removal. - Fix bug where the CCD thickness wasn’t initialized properly. - Fix bug where the contact compliance would result in undesired tunneling, despite CCD being enabled. ### Modified + - Add a `wake_up: bool` argument to the `ImpulseJointSet::insert` and `MultibodyJointSet::insert` to automatically wake-up the rigid-bodies attached to the inserted joint. - The methods `ImpulseJointSet::remove/remove_joints_attached_to_rigid_body`, @@ -180,11 +217,14 @@ without affecting performance of the other parts of the simulation. by default. ### Added + - Debug-renderer: add rendering of contacts, solver contacts, and collider Aabbs - Add `MultibodyJointSet::attached_joints` to return all the multibody joints attached to a given rigid-body. ## v0.12.0 (30 Apr. 2022) + ### Fixed + - Fix the simulation when the `parallel` feature is enabled. - Fix bug where damping would not be applied properly to some bodies. - Fix panics caused by various situations (contact or joints) involving rigid-bodies with locked translations/rotations. @@ -194,6 +234,7 @@ without affecting performance of the other parts of the simulation. - Fix the broad-phase becoming potentially invalid after a change of collision groups. ### Modified + - Switch to `nalgebra` 0.31. - Switch to `parry` 0.9. - Rename `JointHandle` to `ImpulseJointHandle`. @@ -210,27 +251,30 @@ without affecting performance of the other parts of the simulation. - The `ActiveEvents::CONTACT_EVENTS` and `ActiveEvents::INTERSECTION_EVENTS` flags have been replaced by a single flag `ActiveEvents::COLLISION_EVENTS`. - Joint motors no longer have a `VelocityBased` model. The new choices are `AccelerationBased` and `ForceBased` - which are more stable. + which are more stable. - Calling the `.build()` function from builders (`RigidBodyBuilder`, `ColliderBuilder`, etc.) is no longer necessary - whan adding them to sets. It is automatically called thanks to `Into<_>` implementations. + whan adding them to sets. It is automatically called thanks to `Into<_>` implementations. - The `ComponentSet` abstractions (and related `_generic` methods like `PhysicsPipeline::step_generic`) have been removed. Custom storage for colliders and rigid-bodies are no longer possible: use the built-in `RigidBodySet` and `ColliderSet` instead. ### Semantic modifications + These are changes in the behavior of the physics engine that are not necessarily reflected by an API change. See [#304](https://github.com/dimforge/rapier/pull/304) for extensive details. + - `RigidBody::set_linvel` and `RigidBody::set_angvel` no longer modify the velocity of static bodies. - `RigidBody::set_body_type` will reset the velocity of a rigid-body to zero if it is static. - Don’t automatically clear forces at the end of a timestep. - Don’t reset the velocity of kinematic bodies to zero at the end of the timestep. -- Events `CollisionEvent::Stopped` are now generated after a collider is removed. +- Events `CollisionEvent::Stopped` are now generated after a collider is removed. ### Added + - Significantly improve the API of joints by adding: - * Builders based on the builder pattern. - * Getters and setters for all joints. - * Method to convert a `GenericJoint` to one of the more specific joint type. + * Builders based on the builder pattern. + * Getters and setters for all joints. + * Method to convert a `GenericJoint` to one of the more specific joint type. - Improve stability of joint motors. - Adds a `bool` argument to `RigidBodySet::remove`. If set to `false`, the colliders attached to the rigid-body won’t be automatically deleted (they will only be detached from the deleted rigid-body instead). @@ -240,9 +284,13 @@ reflected by an API change. See [#304](https://github.com/dimforge/rapier/pull/3 renderer to debug the state of the physics engine. ## v0.12.0-alpha.0 (2 Jan. 2022) + ### Fixed + - Fixed `RigidBody::restrict_rotations` to properly take into account the axes to lock. + ### Modified + - All the impulse-based joints have been replaced by a single generic 6-Dofs joint in 3D (or 3-Dofs joint in 2D) named `ImpulseJoint`. The `RevoluteJoint, PrismaticJoint, FixedJoint`, and `SphericalJoint` (formely named `BallJoint`) structures still exist but are just convenient @@ -253,63 +301,81 @@ reflected by an API change. See [#304](https://github.com/dimforge/rapier/pull/3 code significantly while offering stiffer results. ### Added -- Added multibody joints: joints based on the reduced-coordinates modeling. These joints can’t + +- Added multibody joints: joints based on the reduced-coordinates modeling. These joints can’t violate their positional constraint. - Implement `Default` for most of the struct that supports it. ## v0.11.1 + ### Fixed + - Fix a bug causing large moving colliders to miss some collisions after some time. - Fix invalid forces generated by contacts with position-based kinematic bodies. - Fix a bug where two colliders without parent would not have their collision computed even if the appropriate flags were set. ## v0.11.0 + Check out the user-guide for the JS/Typescript bindings for rapier. It has been fully rewritten and is now exhaustive! Check it out on [rapier.rs](https://www.rapier.rs/docs/user_guides/javascript/getting_started_js) ### Added + - Joint limits are now implemented for all joints that can support them (prismatic, revolute, and ball joints). ### Modified + - Switch to `nalgebra 0.29`. ### Fixed + - Fix the build of Rapier when targeting emscripten. ## v0.10.1 + ### Added -- Add `Collider::set_translation_wrt_parent` to change the translation of a collider with respect to its parent rigid-body. + +- Add `Collider::set_translation_wrt_parent` to change the translation of a collider with respect to its parent + rigid-body. - Add `Collider::set_rotation_wrt_parent` to change the translation of a collider with respect to its parent rigid-body. - ## v0.10.0 + ### Added + - Implement `Clone` for `IslandManager`. ### Modified + - `JointSet::insert` no longer takes the rigid-body set in its arguments. - Modify the testbed's plugin system to let plugins interact with the rendering. - Implement `PartialEq` for most collider and rigid-body components. ## v0.9.2 + ### Added + - Make the method JointSet::remove_joints_attached_to_rigid_body public so that it can can be called externally for letting component-based Rapier integration call it to cleanup joints after a rigid-body removal. ### Fixed + - Fix a panic that could happen when the same collider is listed twice in the removed_colliders array. - ## v0.9.1 + ### Added + - Add `rapier::prelude::nalgebra` so that the `vector!` and `point!` macros work out-of-the-box after importing the prelude: `use rapier::prelude::*` ## v0.9.0 + The user-guide has been fully rewritten and is now exhaustive! Check it out on [rapier.rs](https://rapier.rs/) ### Added + - A prelude has been added in order to simplify the most common imports. For example: `use rapier3d::prelude::*` - Add `RigidBody::set_translation` and `RigidBody.translation()`. - Add `RigidBody::set_rotation` and `RigidBody.rotation()`. @@ -322,6 +388,7 @@ The user-guide has been fully rewritten and is now exhaustive! Check it out on [ some SIMD code do generate NaNs which are filtered out by lane-wise selection). ### Modified + The use of `RigidBodySet, ColliderSet, RigidBody, Collider` is no longer mandatory. Rigid-bodies and colliders have been split into multiple components that can be stored in a user-defined set. This is useful for integrating Rapier with other engines (for example this allows us to use Bevy's Query as our rigid-body/collider sets). @@ -330,6 +397,7 @@ The `RigidBodySet, ColliderSet, RigidBody, Collider` are still the best option f provide their own component sets. #### Rigid-bodies + - Renamed `BodyStatus` to `RigidBodyType`. - `RigidBodyBuilder::translation` now takes a vector instead of individual components. - `RigidBodyBuilder::linvel` now takes a vector instead of individual components. @@ -337,8 +405,9 @@ provide their own component sets. `RigidBodyBuilder::new_kinematic_velocity_based` constructors. - The `RigidBodyType::Kinematic` variant has been replaced by two variants: `RigidBodyType::KinematicVelocityBased` and `RigidBodyType::KinematicPositionBased`. - + #### Colliders + - `Colliderbuilder::translation` now takes a vector instead of individual components. - The way `PhysicsHooks` are enabled changed. Now, a physics hooks is executed if any of the two colliders involved in the contact/intersection pair contains the related `PhysicsHooksFlag`. @@ -360,115 +429,139 @@ provide their own component sets. - Fixed a bug where collision groups were ignored by CCD. #### Joints + - The fields `FixedJoint::local_anchor1` and `FixedJoint::local_anchor2` have been renamed to `FixedJoint::local_frame1` and `FixedJoint::local_frame2`. - + #### Pipelines and others + - The field `ContactPair::pair` (which contained two collider handles) has been replaced by two fields: `ContactPair::collider1` and `ContactPair::collider2`. - The list of active dynamic bodies is now retrieved with `IslandManager::active_dynamic_bodies` instead of `RigidBodySet::iter_active_dynamic`. - The list of active kinematic bodies is now retrieved with `IslandManager::active_kinematic_bodies` instead of `RigidBodySet::iter_active_kinematic`. -- `NarrowPhase::contacts_with` now returns an `impl Iterator` instead of +- `NarrowPhase::contacts_with` now returns an `impl Iterator` instead of an `Option>`. The colliders handles can be read from the contact-pair itself. - `NarrowPhase::intersections_with` now returns an iterator directly instead of an `Option`. - Rename `PhysicsHooksFlags` to `ActiveHooks`. - Add the contact pair as an argument to `EventHandler::handle_contact_event` - ## v0.8.0 + ### Modified + - Switch to nalgebra 0.26. ## v0.7.2 + ### Added + - Implement `Serialize` and `Deserialize` for the `CCDSolver`. ### Fixed + - Fix a crash that could happen after adding and then removing a collider right away, -before stepping the simulation. + before stepping the simulation. ## v0.7.1 + ### Fixed + - Fixed a bug in the broad-phase that could cause non-determinism after snapshot restoration. ## v0.7.0 + ### Added + - Add the support of **Continuous Collision Detection** (CCD) to -make sure that some fast-moving objects (chosen by the user) don't miss any contacts. -This is done by using motion-clamping, i.e., each fast-moving rigid-body with CCD enabled will -be stopped at the time where their first contact happen. This will result in some "time loss" for that -rigid-body. This loss of time can be reduced by increasing the maximum number of CCD substeps executed -(the default being 1). + make sure that some fast-moving objects (chosen by the user) don't miss any contacts. + This is done by using motion-clamping, i.e., each fast-moving rigid-body with CCD enabled will + be stopped at the time where their first contact happen. This will result in some "time loss" for that + rigid-body. This loss of time can be reduced by increasing the maximum number of CCD substeps executed + (the default being 1). - Add the support of **collider modification**. Now, most of the characteristics of a collider can be -modified after the collider has been created. + modified after the collider has been created. - We now use an **implicit friction cone** for handling friction, instead of a pyramidal approximation -of the friction cone. This means that friction will now behave in a more isotropic way (i.e. more realistic -Coulomb friction). + of the friction cone. This means that friction will now behave in a more isotropic way (i.e. more realistic + Coulomb friction). - Add the support of **custom filters** for the `QueryPipeline`. So far, interaction groups (bit masks) -had to be used to exclude from colliders from a query made with the `QueryPipeline`. Now it is also -possible to provide a custom closures to apply arbitrary user-defined filters. + had to be used to exclude from colliders from a query made with the `QueryPipeline`. Now it is also + possible to provide a custom closures to apply arbitrary user-defined filters. - It is now possible to solve penetrations using the velocity solver instead of (or alongside) the -position solver (this is disabled by default, set `IntegrationParameters::velocity_based_erp` to + position solver (this is disabled by default, set `IntegrationParameters::velocity_based_erp` to a value `> 0.0` to enable.). Added the methods: + - `ColliderBuilder::halfspace` to create a collider with an unbounded plane shape. - `Collider::shape_mut` to get a mutable reference to its shape. - `Collider::set_shape`, `::set_restitution_combine_rule`, `::set_position_wrt_parent`, `::set_collision_groups` -`::set_solver_groups` to change various properties of a collider after its creation. + `::set_solver_groups` to change various properties of a collider after its creation. - `RigidBodyBuilder::ccd_enabled` to enable CCD for a rigid-body. ### Modified + - The `target_dist` argument of `QueryPipeline::cast_shape` was removed. - `RigidBodyBuilder::mass_properties` has been deprecated, replaced by `::additional_mass_properties`. - `RigidBodyBuilder::mass` has been deprecated, replaced by `::additional_mass`. -- `RigidBodyBuilder::principal_angular_inertia` has been deprecated, replaced by `::additional_principal_angular_inertia`. -- The field `SolveContact::data` has been replaced by the fields `SolverContact::warmstart_impulse`, +- `RigidBodyBuilder::principal_angular_inertia` has been deprecated, replaced + by `::additional_principal_angular_inertia`. +- The field `SolveContact::data` has been replaced by the fields `SolverContact::warmstart_impulse`, `SolverContact::warmstart_tangent_impulse`, and `SolverContact::prev_rhs`. - All the fields of `IntegrationParameters` that we don't use have been removed. - `NarrowPhase::maintain` has been renamed to `NarrowPhase::handle_user_changes`. - `BroadPhase::maintain` has been removed. Use ` BroadPhase::update` directly. ### Fixed + - The Broad-Phase algorithm has been completely reworked to support large colliders properly (until now -they could result in very large memory and CPU usage). + they could result in very large memory and CPU usage). ## v0.6.1 + ### Fixed + - Fix a determinism problem that may happen after snapshot restoration, if a rigid-body is sleeping at the time the snapshot is taken. ## v0.6.0 + ### Added + - The support of **dominance groups** have been added. Each rigid-body is part of a dominance group in [-127; 127] -(the default is 0). If two rigid-body are in contact, the one with the highest dominance will act as if it has -an infinite mass, making it immune to the forces the other body would apply on it. See [#122](https://github.com/dimforge/rapier/pull/122) -for further details. + (the default is 0). If two rigid-body are in contact, the one with the highest dominance will act as if it has + an infinite mass, making it immune to the forces the other body would apply on it. + See [#122](https://github.com/dimforge/rapier/pull/122) + for further details. - The support for **contact modification** has been added. This can bee used to simulate conveyor belts, -one-way platforms and other non-physical effects. It can also be used to simulate materials with -variable friction and restitution coefficient on a single collider. See [#120](https://github.com/dimforge/rapier/pull/120) -for further details. + one-way platforms and other non-physical effects. It can also be used to simulate materials with + variable friction and restitution coefficient on a single collider. + See [#120](https://github.com/dimforge/rapier/pull/120) + for further details. - The support for **joint motors** have been added. This can be used to control the position and/or -velocity of a joint based on a spring-like equation. See [#119](https://github.com/dimforge/rapier/pull/119) -for further details. + velocity of a joint based on a spring-like equation. See [#119](https://github.com/dimforge/rapier/pull/119) + for further details. ### Removed + - The `ContactPairFilter` and `IntersectionPairFilter` traits have been removed. They are both combined in a single new trait: `PhysicsHooks`. ## v0.5.0 + In this release we are dropping `ncollide` and use our new crate [`parry`](https://parry.rs) instead! This comes with a lot of new features, as well as two new crates: `rapier2d-f64` and `rapier3d-f64` for physics simulation with 64-bits floats. ### Added + - Added a `RAPIER.version()` function at the root of the package to retrieve the version of Rapier as a string. Several geometric queries have been added to the `QueryPipeline`: + - `QueryPipeline::intersections_with_ray`: get all colliders intersecting a ray. - `QueryPipeline::intersection_with_shape`: get one collider intersecting a shape. - `QueryPipeline::project_point`: get the projection of a point on the closest collider. @@ -478,66 +571,83 @@ Several geometric queries have been added to the `QueryPipeline`: - `QueryPipeline::intersections_with_shape`: get all the colliders intersecting a shape. Several new shape types are now supported: + - `RoundCuboid`, `Segment`, `Triangle`, `RoundTriangle`, `Polyline`, `ConvexPolygon` (2D only), `RoundConvexPolygon` (2D only), `ConvexPolyhedron` (3D only), `RoundConvexPolyhedron` (3D only), `RoundCone` (3D only). It is possible to build `ColliderDesc` using these new shapes: + - `ColliderBuilder::round_cuboid`, `ColliderBuilder::segment`, `ColliderBuilder::triangle`, `ColliderBuilder::round_triangle`, `ColliderBuilder::convex_hull`, `ColliderBuilder::round_convex_hull`, `ColliderBuilder::polyline`, `ColliderBuilder::convex_decomposition`, `ColliderBuilder::round_convex_decomposition`, `ColliderBuilder::convex_polyline` (2D only), `ColliderBuilder::round_convex_polyline` (2D only), - `ColliderBuilder::convex_mesh` (3D only),`ColliderBuilder::round_convex_mesh` (3D only), `ColliderBuilder::round_cone` (3D only). + `ColliderBuilder::convex_mesh` (3D only),`ColliderBuilder::round_convex_mesh` (3D + only), `ColliderBuilder::round_cone` (3D only). It is possible to specify different rules for combining friction and restitution coefficients of the two colliders involved in a contact with: + - `ColliderDesc::friction_combine_rule`, and `ColliderDesc::restitution_combine_rule`. Various RigidBody-related getter and setters have been added: + - `RigidBodyBuilder::gravity_scale`, `RigidBody::gravity_scale`, `RigidBody::set_gravity_scale` to get/set the scale factor applied to the gravity affecting a rigid-body. Setting this to 0.0 will make the rigid-body ignore gravity. - `RigidBody::set_linear_damping` and `RigidBody::set_angular_damping` to set the linear and angular damping of the rigid-body. - `RigidBodyBuilder::restrict_rotations` to prevent rotations along specific coordinate axes. This replaces the three boolean arguments previously passed to `.set_principal_angular_inertia`. - + ### Breaking changes + Breaking changes related to contacts: -- The way contacts are represented changed. Refer to the documentation of `parry::query::ContactManifold`, `parry::query::TrackedContact` + +- The way contacts are represented changed. Refer to the documentation + of `parry::query::ContactManifold`, `parry::query::TrackedContact` and `rapier::geometry::ContactManifoldData` and `rapier::geometry::ContactData` for details. Breaking changes related to rigid-bodies: -- The `RigidBodyDesc.setMass` takes only one argument now. Use `RigidBodyDesc.lockTranslations` to lock the translational + +- The `RigidBodyDesc.setMass` takes only one argument now. Use `RigidBodyDesc.lockTranslations` to lock the + translational motion of the rigid-body. - The `RigidBodyDesc.setPrincipalAngularInertia` no longer have boolean parameters to lock rotations. - Use `RigidBodyDesc.lockRotations` or `RigidBodyDesc.restrictRotations` to lock the rotational motion of the rigid-body. + Use `RigidBodyDesc.lockRotations` or `RigidBodyDesc.restrictRotations` to lock the rotational motion of the + rigid-body. Breaking changes related to colliders: + - The collider shape type has been renamed from `ColliderShape` to `SharedShape` (now part of the Parry crate). - The `Polygon` shape no longer exists. For a 2D convex polygon, use a `ConvexPolygon` instead. - All occurrences of `Trimesh` have been replaced by `TriMesh` (note the change in case). Breaking changes related to events: + - Rename all occurrences of `Proximity` to `Intersection`. - The `Proximity` enum has been removed, it's replaced by a boolean. ## v0.4.2 + - Fix a bug in angular inertia tensor computation that could cause rotations not to work properly. - Add `RigidBody::set_mass_properties` to set the mass properties of an already-constructed rigid-body. ## v0.4.1 + - The `RigidBodyBuilder::principal_inertia` method has been deprecated and renamed to `principal_angular_inertia` for clarity. ## v0.4.0 + - The rigid-body `linvel`, `angvel`, and `position` fields are no longer public. Access using their corresponding getters/setters. For example: `rb.linvel()`, `rb.set_linvel(vel, true)`. - Add `RigidBodyBuilder::sleeping(true)` to allow the creation of a rigid-body that is asleep at initialization-time. #### Locking translation and rotations of a rigid-body + - Add `RigidBodyBuilder::lock_rotations` to prevent a rigid-body from rotating because of forces. - Add `RigidBodyBuilder::lock_translations` to prevent a rigid-body from translating because of forces. - Add `RigidBodyBuilder::principal_inertia` for setting the principal inertia of a rigid-body, and/or @@ -546,6 +656,7 @@ Breaking changes related to events: contributions should be taken into account in the future too. #### Reading contact and proximity information + - Add `NarrowPhase::contacts_with` and `NarrowPhase::proximities_with` to retrieve all the contact pairs and proximity pairs involving a specific collider. - Add `NarrowPhase::contact_pair` and `NarrowPhase::proximity_pair` to retrieve one specific contact @@ -554,6 +665,7 @@ Breaking changes related to events: proximity pairs detected by the narrow-phase. ## v0.3.2 + - Add linear and angular damping. The damping factor can be set with `RigidBodyBuilder::linear_damping` and `RigidBodyBuilder::angular_damping`. - Implement `Clone` for almost everything that can be worth cloning. @@ -562,34 +674,43 @@ Breaking changes related to events: - The restitution coefficient of colliders is now taken into account by the physics solver. ## v0.3.1 + - Fix non-determinism problem when using triangle-meshes, cone, cylinders, or capsules. - Add `JointSet::remove(...)` to remove a joint from the `JointSet`. ## v0.3.0 + - Collider shapes are now trait-objects instead of a `Shape` enum. - Add a user-defined `u128` to each colliders and rigid-bodies for storing user data. - Add the support for `Cylinder`, `RoundCylinder`, and `Cone` shapes. - Added the support for collision filtering based on bit masks (often known as collision groups, collision masks, or - collision layers in other physics engines). Each collider has two groups. Their `collision_groups` is used for filtering - what pair of colliders should have their contacts computed by the narrow-phase. Their `solver_groups` is used for filtering + collision layers in other physics engines). Each collider has two groups. Their `collision_groups` is used for + filtering + what pair of colliders should have their contacts computed by the narrow-phase. Their `solver_groups` is used for + filtering what pair of colliders should have their contact forces computed by the constraints solver. -- Collision groups can also be used to filter what collider should be hit by a ray-cast performed by the `QueryPipeline`. +- Collision groups can also be used to filter what collider should be hit by a ray-cast performed by + the `QueryPipeline`. - Added collision filters based on user-defined trait-objects. This adds two traits `ContactPairFilter` and `ProximityPairFilter` that allows user-defined logic for determining if two colliders/sensors are allowed to interact. -- The `PhysicsPipeline::step` method now takes two additional arguments: the optional `&ContactPairFilter` and `&ProximityPairFilter` -for filtering contact and proximity pairs. +- The `PhysicsPipeline::step` method now takes two additional arguments: the optional `&ContactPairFilter` + and `&ProximityPairFilter` + for filtering contact and proximity pairs. ## v0.2.1 + - Fix panic in TriMesh construction and QueryPipeline update caused by a stack overflow or a subtraction underflow. ## v0.2.0 + The most significant change on this version is the addition of the `QueryPipeline` responsible for performing scene-wide queries. So far only ray-casting has been implemented. - Add `ColliderSet::remove(...)` to remove a collider from the `ColliderSet`. - Replace `PhysicsPipeline::remove_rigid_body` by `RigidBodySet::remove`. - The `JointSet.iter()` now returns an iterator yielding `(JointHandle, &Joint)` instead of just `&Joint`. -- Add `ColliderDesc::translation(...)` to set the translation of a collider relative to the rigid-body it is attached to. +- Add `ColliderDesc::translation(...)` to set the translation of a collider relative to the rigid-body it is attached + to. - Add `ColliderDesc::rotation(...)` to set the rotation of a collider relative to the rigid-body it is attached to. - Add `ColliderDesc::position(...)` to set the position of a collider relative to the rigid-body it is attached to. - Add `Collider::position_wrt_parent()` to get the position of a collider relative to the rigid-body it is attached to. @@ -597,8 +718,8 @@ scene-wide queries. So far only ray-casting has been implemented. - Deprecate `Collider::delta()` in favor of the new `Collider::position_wrt_parent()`. - Fix multiple issues occurring when having colliders resulting in a non-zero center-of-mass. - Fix a crash happening when removing a rigid-body with a collider, stepping the simulation, adding another rigid-body - with a collider, and stepping the simulation again. + with a collider, and stepping the simulation again. - Fix NaN when detection contacts between two polygonal faces where one has a normal perfectly perpendicular to the -separating vector. + separating vector. - Fix bug collision detection between trimeshes and other shapes. The bug appeared depending on whether the trimesh -collider was added before the other shape's collider or after. + collider was added before the other shape's collider or after. From 6b6c349cfa02486c25639fdc09d343145e8da899 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Fri, 1 Mar 2024 20:18:19 +0100 Subject: [PATCH 03/71] Fix testbed snapshot restore system --- src_testbed/physics/mod.rs | 16 +++++++++++++++- src_testbed/testbed.rs | 33 +++++++++++++++++++++++++++------ 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/src_testbed/physics/mod.rs b/src_testbed/physics/mod.rs index 26a55c7..bc00327 100644 --- a/src_testbed/physics/mod.rs +++ b/src_testbed/physics/mod.rs @@ -14,6 +14,8 @@ pub struct PhysicsSnapshot { bodies: Vec, colliders: Vec, impulse_joints: Vec, + multibody_joints: Vec, + island_manager: Vec, } impl PhysicsSnapshot { @@ -21,17 +23,21 @@ impl PhysicsSnapshot { timestep_id: usize, broad_phase: &BroadPhase, narrow_phase: &NarrowPhase, + island_manager: &IslandManager, bodies: &RigidBodySet, colliders: &ColliderSet, impulse_joints: &ImpulseJointSet, + multibody_joints: &MultibodyJointSet, ) -> bincode::Result { Ok(Self { timestep_id, broad_phase: bincode::serialize(broad_phase)?, narrow_phase: bincode::serialize(narrow_phase)?, + island_manager: bincode::serialize(island_manager)?, bodies: bincode::serialize(bodies)?, colliders: bincode::serialize(colliders)?, impulse_joints: bincode::serialize(impulse_joints)?, + multibody_joints: bincode::serialize(multibody_joints)?, }) } @@ -41,32 +47,40 @@ impl PhysicsSnapshot { usize, BroadPhase, NarrowPhase, + IslandManager, RigidBodySet, ColliderSet, ImpulseJointSet, + MultibodyJointSet, )> { Ok(( self.timestep_id, bincode::deserialize(&self.broad_phase)?, bincode::deserialize(&self.narrow_phase)?, + bincode::deserialize(&self.island_manager)?, bincode::deserialize(&self.bodies)?, bincode::deserialize(&self.colliders)?, bincode::deserialize(&self.impulse_joints)?, + bincode::deserialize(&self.multibody_joints)?, )) } pub fn print_snapshot_len(&self) { let total = self.broad_phase.len() + self.narrow_phase.len() + + self.island_manager.len() + self.bodies.len() + self.colliders.len() - + self.impulse_joints.len(); + + self.impulse_joints.len() + + self.multibody_joints.len(); println!("Snapshot length: {}B", total); println!("|_ broad_phase: {}B", self.broad_phase.len()); println!("|_ narrow_phase: {}B", self.narrow_phase.len()); + println!("|_ island_manager: {}B", self.island_manager.len()); println!("|_ bodies: {}B", self.bodies.len()); println!("|_ colliders: {}B", self.colliders.len()); println!("|_ impulse_joints: {}B", self.impulse_joints.len()); + println!("|_ multibody_joints: {}B", self.multibody_joints.len()); } } diff --git a/src_testbed/testbed.rs b/src_testbed/testbed.rs index e5a5f13..5430384 100644 --- a/src_testbed/testbed.rs +++ b/src_testbed/testbed.rs @@ -24,7 +24,7 @@ use rapier::dynamics::{ use rapier::geometry::Ray; use rapier::geometry::{ColliderHandle, ColliderSet, NarrowPhase}; use rapier::math::{Real, Vector}; -use rapier::pipeline::{PhysicsHooks, QueryFilter}; +use rapier::pipeline::{PhysicsHooks, QueryFilter, QueryPipeline}; #[cfg(all(feature = "dim2", feature = "other-backends"))] use crate::box2d_backend::Box2dWorld; @@ -1250,9 +1250,11 @@ fn update_testbed( harness.state.timestep_id, &harness.physics.broad_phase, &harness.physics.narrow_phase, + &harness.physics.islands, &harness.physics.bodies, &harness.physics.colliders, &harness.physics.impulse_joints, + &harness.physics.multibody_joints, ) .ok(); @@ -1269,17 +1271,36 @@ fn update_testbed( .action_flags .set(TestbedActionFlags::RESTORE_SNAPSHOT, false); if let Some(snapshot) = &state.snapshot { - if let Ok(w) = snapshot.restore() { + if let Ok(( + timestep_id, + broad_phase, + narrow_phase, + island_manager, + bodies, + colliders, + impulse_joints, + multibody_joints, + )) = snapshot.restore() + { clear(&mut commands, &mut state, &mut graphics, &mut plugins); for plugin in &mut plugins.0 { plugin.clear_graphics(&mut graphics, &mut commands); } - // set_world(w.3, w.4, w.5); - harness.physics.broad_phase = w.1; - harness.physics.narrow_phase = w.2; - harness.state.timestep_id = w.0; + harness.state.timestep_id = timestep_id; + harness.physics.broad_phase = broad_phase; + harness.physics.narrow_phase = narrow_phase; + harness.physics.islands = island_manager; + harness.physics.bodies = bodies; + harness.physics.colliders = colliders; + harness.physics.impulse_joints = impulse_joints; + harness.physics.multibody_joints = multibody_joints; + harness.physics.query_pipeline = QueryPipeline::new(); + + state + .action_flags + .set(TestbedActionFlags::RESET_WORLD_GRAPHICS, true); } } } From f9663f894c022e25ae6c6bc572dc305fab1adb76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Sat, 23 Mar 2024 10:25:56 +0100 Subject: [PATCH 04/71] chore: clippy fix --- src_testbed/physics/mod.rs | 44 +++++++++++++++++++------------------- src_testbed/testbed.rs | 6 +++--- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src_testbed/physics/mod.rs b/src_testbed/physics/mod.rs index bc00327..c3b750f 100644 --- a/src_testbed/physics/mod.rs +++ b/src_testbed/physics/mod.rs @@ -18,6 +18,17 @@ pub struct PhysicsSnapshot { island_manager: Vec, } +pub struct DeserializedPhysicsSnapshot { + pub timestep_id: usize, + pub broad_phase: BroadPhase, + pub narrow_phase: NarrowPhase, + pub island_manager: IslandManager, + pub bodies: RigidBodySet, + pub colliders: ColliderSet, + pub impulse_joints: ImpulseJointSet, + pub multibody_joints: MultibodyJointSet, +} + impl PhysicsSnapshot { pub fn new( timestep_id: usize, @@ -41,28 +52,17 @@ impl PhysicsSnapshot { }) } - pub fn restore( - &self, - ) -> bincode::Result<( - usize, - BroadPhase, - NarrowPhase, - IslandManager, - RigidBodySet, - ColliderSet, - ImpulseJointSet, - MultibodyJointSet, - )> { - Ok(( - self.timestep_id, - bincode::deserialize(&self.broad_phase)?, - bincode::deserialize(&self.narrow_phase)?, - bincode::deserialize(&self.island_manager)?, - bincode::deserialize(&self.bodies)?, - bincode::deserialize(&self.colliders)?, - bincode::deserialize(&self.impulse_joints)?, - bincode::deserialize(&self.multibody_joints)?, - )) + pub fn restore(&self) -> bincode::Result { + Ok(DeserializedPhysicsSnapshot { + timestep_id: self.timestep_id, + broad_phase: bincode::deserialize(&self.broad_phase)?, + narrow_phase: bincode::deserialize(&self.narrow_phase)?, + island_manager: bincode::deserialize(&self.island_manager)?, + bodies: bincode::deserialize(&self.bodies)?, + colliders: bincode::deserialize(&self.colliders)?, + impulse_joints: bincode::deserialize(&self.impulse_joints)?, + multibody_joints: bincode::deserialize(&self.multibody_joints)?, + }) } pub fn print_snapshot_len(&self) { diff --git a/src_testbed/testbed.rs b/src_testbed/testbed.rs index 5430384..780e23e 100644 --- a/src_testbed/testbed.rs +++ b/src_testbed/testbed.rs @@ -7,7 +7,7 @@ use std::num::NonZeroUsize; use bevy::prelude::*; use crate::debug_render::{DebugRenderPipelineResource, RapierDebugRenderPlugin}; -use crate::physics::{PhysicsEvents, PhysicsSnapshot, PhysicsState}; +use crate::physics::{DeserializedPhysicsSnapshot, PhysicsEvents, PhysicsSnapshot, PhysicsState}; use crate::plugin::TestbedPlugin; use crate::ui; use crate::{graphics::GraphicsManager, harness::RunState}; @@ -1271,7 +1271,7 @@ fn update_testbed( .action_flags .set(TestbedActionFlags::RESTORE_SNAPSHOT, false); if let Some(snapshot) = &state.snapshot { - if let Ok(( + if let Ok(DeserializedPhysicsSnapshot { timestep_id, broad_phase, narrow_phase, @@ -1280,7 +1280,7 @@ fn update_testbed( colliders, impulse_joints, multibody_joints, - )) = snapshot.restore() + }) = snapshot.restore() { clear(&mut commands, &mut state, &mut graphics, &mut plugins); From 3fd18f4da8476a29945fa6f0c49e0af08d1f7363 Mon Sep 17 00:00:00 2001 From: Max Whitehead Date: Sun, 10 Mar 2024 23:44:26 -0700 Subject: [PATCH 05/71] fix(user_changes): Fix RigidBodyType changed to Dynamic not updating active dynamic set. --- src/pipeline/physics_pipeline.rs | 71 +++++++++++++++++++++++++++++++- src/pipeline/user_changes.rs | 4 +- 2 files changed, 72 insertions(+), 3 deletions(-) diff --git a/src/pipeline/physics_pipeline.rs b/src/pipeline/physics_pipeline.rs index 6dbc4e5..893882d 100644 --- a/src/pipeline/physics_pipeline.rs +++ b/src/pipeline/physics_pipeline.rs @@ -653,7 +653,7 @@ mod test { use crate::geometry::{BroadPhase, ColliderBuilder, ColliderSet, NarrowPhase}; use crate::math::Vector; use crate::pipeline::PhysicsPipeline; - use crate::prelude::MultibodyJointSet; + use crate::prelude::{MultibodyJointSet, RigidBodyType}; #[test] fn kinematic_and_fixed_contact_crash() { @@ -852,4 +852,73 @@ mod test { ); } } + + #[test] + fn rigid_body_type_changed_dynamic_is_in_active_set() { + let mut colliders = ColliderSet::new(); + let mut impulse_joints = ImpulseJointSet::new(); + let mut multibody_joints = MultibodyJointSet::new(); + let mut pipeline = PhysicsPipeline::new(); + let mut bf = BroadPhase::new(); + let mut nf = NarrowPhase::new(); + let mut islands = IslandManager::new(); + + let mut bodies = RigidBodySet::new(); + + // Initialize body as kinematic with mass + let rb = RigidBodyBuilder::kinematic_position_based() + .additional_mass(1.0) + .build(); + let h = bodies.insert(rb.clone()); + + // Step once + let gravity = Vector::y() * -9.81; + pipeline.step( + &gravity, + &IntegrationParameters::default(), + &mut islands, + &mut bf, + &mut nf, + &mut bodies, + &mut colliders, + &mut impulse_joints, + &mut multibody_joints, + &mut CCDSolver::new(), + None, + &(), + &(), + ); + + // Switch body type to Dynamic + bodies + .get_mut(h) + .unwrap() + .set_body_type(RigidBodyType::Dynamic, true); + + // Step again + pipeline.step( + &gravity, + &IntegrationParameters::default(), + &mut islands, + &mut bf, + &mut nf, + &mut bodies, + &mut colliders, + &mut impulse_joints, + &mut multibody_joints, + &mut CCDSolver::new(), + None, + &(), + &(), + ); + + let body = bodies.get(h).unwrap(); + let h_y = body.pos.position.translation.y; + + // Expect gravity to be applied on second step after switching to Dynamic + assert!(h_y < 0.0); + + // Expect body to now be in active_dynamic_set + assert!(islands.active_dynamic_set.contains(&h)); + } } diff --git a/src/pipeline/user_changes.rs b/src/pipeline/user_changes.rs index 7c3f1ac..9aa93a5 100644 --- a/src/pipeline/user_changes.rs +++ b/src/pipeline/user_changes.rs @@ -121,8 +121,8 @@ pub(crate) fn handle_user_changes_to_rigid_bodies( } // Push the body to the active set if it is not - // sleeping and if it is not already inside of the active set. - if changes.contains(RigidBodyChanges::SLEEP) + // sleeping and if it is not already inside of the active set, or if type changed to dynamic. + if (changes.contains(RigidBodyChanges::SLEEP) || changes.contains(RigidBodyChanges::TYPE)) && rb.is_enabled() && !rb.activation.sleeping // May happen if the body was put to sleep manually. && rb.is_dynamic() // Only dynamic bodies are in the active dynamic set. From 3020d442ea518c957ca094a79bd0e134f906d363 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Sat, 23 Mar 2024 10:36:07 +0100 Subject: [PATCH 06/71] chore: minor comment fix --- src/pipeline/user_changes.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pipeline/user_changes.rs b/src/pipeline/user_changes.rs index 9aa93a5..a2b00a1 100644 --- a/src/pipeline/user_changes.rs +++ b/src/pipeline/user_changes.rs @@ -120,8 +120,8 @@ pub(crate) fn handle_user_changes_to_rigid_bodies( islands.active_kinematic_set.push(*handle); } - // Push the body to the active set if it is not - // sleeping and if it is not already inside of the active set, or if type changed to dynamic. + // Push the body to the active set if it is not inside the active set yet, and + // is either not longer sleeping or became dynamic. if (changes.contains(RigidBodyChanges::SLEEP) || changes.contains(RigidBodyChanges::TYPE)) && rb.is_enabled() && !rb.activation.sleeping // May happen if the body was put to sleep manually. From f943fd99734d39de3f45c22e9a06652371446917 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Sat, 23 Mar 2024 10:36:39 +0100 Subject: [PATCH 07/71] chore: update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6515925..2b2647e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Fix - Fix crash when simulating a spring joint between two dynamic bodies. +- Fix kinematic bodies not being affected by gravity after being switched back to dynamic. ## v0.18.0 (24 Jan. 2024) From 6886f8f2073b3251e3b080523d8032961b68e52a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Sat, 23 Mar 2024 10:51:50 +0100 Subject: [PATCH 08/71] feat: add RigidBody::predict_position_using_velocity Fix #601 --- CHANGELOG.md | 5 +++++ src/dynamics/rigid_body.rs | 11 +++++++++++ 2 files changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b2647e..bf37c06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ - Fix crash when simulating a spring joint between two dynamic bodies. - Fix kinematic bodies not being affected by gravity after being switched back to dynamic. +### Added + +- Add `RigidBody::predict_position_using_velocity` to predict the next position of the rigid-body + based only on its current velocity. + ## v0.18.0 (24 Jan. 2024) The main highlight of this release is the implementation of a new non-linear constraints solver for better stability diff --git a/src/dynamics/rigid_body.rs b/src/dynamics/rigid_body.rs index b27b58e..2b07f37 100644 --- a/src/dynamics/rigid_body.rs +++ b/src/dynamics/rigid_body.rs @@ -817,6 +817,17 @@ impl RigidBody { .integrate_forces_and_velocities(dt, &self.forces, &self.vels, &self.mprops) } + /// Predicts the next position of this rigid-body, by integrating only its velocity + /// by a time of `dt`. + /// + /// The forces that were applied to this rigid-body since the last physics step will + /// be ignored by this function. Use [`Self::predict_position_using_velocity_and_forces`] + /// instead to take forces into account. + pub fn predict_position_using_velocity(&self, dt: Real) -> Isometry { + self.vels + .integrate(dt, &self.pos.position, &self.mprops.local_mprops.local_com) + } + pub(crate) fn update_world_mass_properties(&mut self) { self.mprops.update_world_mass_properties(&self.pos.position); } From cd9fb8342d115bad557ca05aa16fb961fe28b1b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Sat, 23 Mar 2024 13:11:37 +0100 Subject: [PATCH 09/71] =?UTF-8?q?feat:=E2=80=AFadd=20RigidBody::copy=5Ffro?= =?UTF-8?q?m=20and=20Collider::copy=5Ffrom?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #595 --- CHANGELOG.md | 2 ++ src/dynamics/rigid_body.rs | 55 ++++++++++++++++++++++++++++++++++++++ src/geometry/collider.rs | 47 ++++++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf37c06..b99f8d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ - Add `RigidBody::predict_position_using_velocity` to predict the next position of the rigid-body based only on its current velocity. +- Add `Collider::copy_from` to copy most collider attributes to an existing collider. +- Add `RigidBody::copy_from` to copy most rigid-body attributes to an existing rigid-body. ## v0.18.0 (24 Jan. 2024) diff --git a/src/dynamics/rigid_body.rs b/src/dynamics/rigid_body.rs index 2b07f37..5af4131 100644 --- a/src/dynamics/rigid_body.rs +++ b/src/dynamics/rigid_body.rs @@ -74,6 +74,61 @@ impl RigidBody { self.ids = Default::default(); } + /// Copy all the characteristics from `other` to `self`. + /// + /// If you have a mutable reference to a rigid-body `rigid_body: &mut RigidBody`, attempting to + /// assign it a whole new rigid-body instance, e.g., `*rigid_body = RigidBodyBuilder::dynamic().build()`, + /// will crash due to some internal indices being overwritten. Instead, use + /// `rigid_body.copy_from(&RigidBodyBuilder::dynamic().build())`. + /// + /// This method will allow you to set most characteristics of this rigid-body from another + /// rigid-body instance without causing any breakage. + /// + /// This method **cannot** be used for editing the list of colliders attached to this rigid-body. + /// Therefore, the list of colliders attached to `self` won’t be replaced by the one attached + /// to `other`. + /// + /// The pose of `other` will only copied into `self` if `self` doesn’t have a parent (if it has + /// a parent, its position is directly controlled by the parent rigid-body). + pub fn copy_from(&mut self, other: &RigidBody) { + // NOTE: we deconstruct the rigid-body struct to be sure we don’t forget to + // add some copies here if we add more field to RigidBody in the future. + let RigidBody { + pos, + mprops, + integrated_vels, + vels, + damping, + forces, + ccd, + ids: _ids, // Internal ids must not be overwritten. + colliders: _colliders, // This function cannot be used to edit collider sets. + activation, + changes: _changes, // Will be set to ALL. + body_type, + dominance, + enabled, + additional_solver_iterations, + user_data, + } = other; + + self.pos = *pos; + self.mprops = mprops.clone(); + self.integrated_vels = *integrated_vels; + self.vels = *vels; + self.damping = *damping; + self.forces = *forces; + self.ccd = *ccd; + self.activation = *activation; + self.body_type = *body_type; + self.dominance = *dominance; + self.enabled = *enabled; + self.additional_solver_iterations = *additional_solver_iterations; + self.user_data = *user_data; + + self.changes = RigidBodyChanges::all(); + } + /// Set the additional number of solver iterations run for this rigid-body and /// everything interacting with it. /// diff --git a/src/geometry/collider.rs b/src/geometry/collider.rs index 2a31afa..4e7fbc7 100644 --- a/src/geometry/collider.rs +++ b/src/geometry/collider.rs @@ -60,6 +60,53 @@ impl Collider { self.coll_type.is_sensor() } + /// Copy all the characteristics from `other` to `self`. + /// + /// If you have a mutable reference to a collider `collider: &mut Collider`, attempting to + /// assign it a whole new collider instance, e.g., `*collider = ColliderBuilder::ball(0.5).build()`, + /// will crash due to some internal indices being overwritten. Instead, use + /// `collider.copy_from(&ColliderBuilder::ball(0.5).build())`. + /// + /// This method will allow you to set most characteristics of this collider from another + /// collider instance without causing any breakage. + /// + /// This method **cannot** be used for reparenting a collider. Therefore, the parent of the + /// `other` (if any), as well as its relative position to that parent will not be copied into + /// `self`. + /// + /// The pose of `other` will only copied into `self` if `self` doesn’t have a parent (if it has + /// a parent, its position is directly controlled by the parent rigid-body). + pub fn copy_from(&mut self, other: &Collider) { + // NOTE: we deconstruct the collider struct to be sure we don’t forget to + // add some copies here if we add more field to Collider in the future. + let Collider { + coll_type, + shape, + mprops, + changes: _changes, // Will be set to ALL. + parent: _parent, // This function cannot be used to reparent the collider. + pos, + material, + flags, + bf_data: _bf_data, // Internal ids must not be overwritten. + contact_force_event_threshold, + user_data, + } = other; + + if self.parent.is_none() { + self.pos = *pos; + } + + self.coll_type = *coll_type; + self.shape = shape.clone(); + self.mprops = mprops.clone(); + self.material = *material; + self.contact_force_event_threshold = *contact_force_event_threshold; + self.user_data = *user_data; + self.flags = *flags; + self.changes = ColliderChanges::all(); + } + /// The physics hooks enabled for this collider. pub fn active_hooks(&self) -> ActiveHooks { self.flags.active_hooks From cfb2c2c93e39e1f59557c4f32fde4a68dc4cd6fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Sat, 23 Mar 2024 13:26:53 +0100 Subject: [PATCH 10/71] feat!: rename BroadPhase to BroadPhaseMultiSap --- examples3d-f64/debug_serialized3.rs | 2 +- examples3d/debug_deserialize3.rs | 2 +- .../{broad_phase.rs => broad_phase_multi_sap.rs} | 14 +++++++------- src/geometry/broad_phase_multi_sap/mod.rs | 4 ++-- src/geometry/broad_phase_multi_sap/sap_layer.rs | 4 ++-- src/geometry/broad_phase_qbvh.rs | 6 +++--- src/geometry/mod.rs | 4 ++-- src/pipeline/collision_pipeline.rs | 11 ++++++----- src/pipeline/physics_pipeline.rs | 16 ++++++++-------- src_testbed/harness/mod.rs | 4 ++-- src_testbed/physics/mod.rs | 12 +++++++----- 11 files changed, 41 insertions(+), 38 deletions(-) rename src/geometry/broad_phase_multi_sap/{broad_phase.rs => broad_phase_multi_sap.rs} (98%) diff --git a/examples3d-f64/debug_serialized3.rs b/examples3d-f64/debug_serialized3.rs index e2fa747..86ff2a8 100644 --- a/examples3d-f64/debug_serialized3.rs +++ b/examples3d-f64/debug_serialized3.rs @@ -4,7 +4,7 @@ use rapier_testbed3d::Testbed; #[derive(serde::Deserialize)] struct State { pub islands: IslandManager, - pub broad_phase: BroadPhase, + pub broad_phase: BroadPhaseMultiSap, pub narrow_phase: NarrowPhase, pub bodies: RigidBodySet, pub colliders: ColliderSet, diff --git a/examples3d/debug_deserialize3.rs b/examples3d/debug_deserialize3.rs index b8b79fb..e1ea083 100644 --- a/examples3d/debug_deserialize3.rs +++ b/examples3d/debug_deserialize3.rs @@ -6,7 +6,7 @@ struct PhysicsState { pub gravity: Vector, pub integration_parameters: IntegrationParameters, pub islands: IslandManager, - pub broad_phase: BroadPhase, + pub broad_phase: BroadPhaseMultiSap, pub narrow_phase: NarrowPhase, pub bodies: RigidBodySet, pub colliders: ColliderSet, diff --git a/src/geometry/broad_phase_multi_sap/broad_phase.rs b/src/geometry/broad_phase_multi_sap/broad_phase_multi_sap.rs similarity index 98% rename from src/geometry/broad_phase_multi_sap/broad_phase.rs rename to src/geometry/broad_phase_multi_sap/broad_phase_multi_sap.rs index 9e1bc06..24bc72d 100644 --- a/src/geometry/broad_phase_multi_sap/broad_phase.rs +++ b/src/geometry/broad_phase_multi_sap/broad_phase_multi_sap.rs @@ -74,7 +74,7 @@ use parry::utils::hashmap::HashMap; /// broad-phase, as well as the Aabbs of all the regions part of this broad-phase. #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[derive(Clone)] -pub struct BroadPhase { +pub struct BroadPhaseMultiSap { proxies: SAPProxies, layers: Vec, smallest_layer: u8, @@ -114,16 +114,16 @@ pub struct BroadPhase { reporting: HashMap<(u32, u32), bool>, // Workspace } -impl Default for BroadPhase { +impl Default for BroadPhaseMultiSap { fn default() -> Self { Self::new() } } -impl BroadPhase { +impl BroadPhaseMultiSap { /// Create a new empty broad-phase. pub fn new() -> Self { - BroadPhase { + BroadPhaseMultiSap { proxies: SAPProxies::new(), layers: Vec::new(), smallest_layer: 0, @@ -138,7 +138,7 @@ impl BroadPhase { /// /// For each colliders marked as removed, we make their containing layer mark /// its proxy as pre-deleted. The actual proxy removal will happen at the end - /// of the `BroadPhase::update`. + /// of the `BroadPhaseMultiSap::update`. fn handle_removed_colliders(&mut self, removed_colliders: &[ColliderHandle]) { // For each removed collider, remove the corresponding proxy. for removed in removed_colliders { @@ -623,11 +623,11 @@ mod test { use crate::dynamics::{ ImpulseJointSet, IslandManager, MultibodyJointSet, RigidBodyBuilder, RigidBodySet, }; - use crate::geometry::{BroadPhase, ColliderBuilder, ColliderSet}; + use crate::geometry::{BroadPhaseMultiSap, ColliderBuilder, ColliderSet}; #[test] fn test_add_update_remove() { - let mut broad_phase = BroadPhase::new(); + let mut broad_phase = BroadPhaseMultiSap::new(); let mut bodies = RigidBodySet::new(); let mut colliders = ColliderSet::new(); let mut impulse_joints = ImpulseJointSet::new(); diff --git a/src/geometry/broad_phase_multi_sap/mod.rs b/src/geometry/broad_phase_multi_sap/mod.rs index 0f85e96..a1951c7 100644 --- a/src/geometry/broad_phase_multi_sap/mod.rs +++ b/src/geometry/broad_phase_multi_sap/mod.rs @@ -1,4 +1,4 @@ -pub use self::broad_phase::BroadPhase; +pub use self::broad_phase_multi_sap::BroadPhaseMultiSap; pub use self::broad_phase_pair_event::{BroadPhasePairEvent, ColliderPair}; pub use self::sap_proxy::SAPProxyIndex; @@ -9,7 +9,7 @@ use self::sap_proxy::*; use self::sap_region::*; use self::sap_utils::*; -mod broad_phase; +mod broad_phase_multi_sap; mod broad_phase_pair_event; mod sap_axis; mod sap_endpoint; diff --git a/src/geometry/broad_phase_multi_sap/sap_layer.rs b/src/geometry/broad_phase_multi_sap/sap_layer.rs index 2266d56..12dc7f7 100644 --- a/src/geometry/broad_phase_multi_sap/sap_layer.rs +++ b/src/geometry/broad_phase_multi_sap/sap_layer.rs @@ -71,7 +71,7 @@ impl SAPLayer { /// /// This method must be called in a bottom-up loop, propagating new regions from the /// smallest layer, up to the largest layer. That loop is done by the Phase 3 of the - /// BroadPhase::update. + /// BroadPhaseMultiSap::update. pub fn propagate_created_regions( &mut self, larger_layer: &mut Self, @@ -182,7 +182,7 @@ impl SAPLayer { /// If the region with the given region key does not exist yet, it is created. /// When a region is created, it creates a new proxy for that region, and its /// proxy ID is added to `self.created_region` so it can be propagated during - /// the Phase 3 of `BroadPhase::update`. + /// the Phase 3 of `BroadPhaseMultiSap::update`. /// /// This returns the proxy ID of the already existing region if it existed, or /// of the new region if it did not exist and has been created by this method. diff --git a/src/geometry/broad_phase_qbvh.rs b/src/geometry/broad_phase_qbvh.rs index 22ca562..56c2b6f 100644 --- a/src/geometry/broad_phase_qbvh.rs +++ b/src/geometry/broad_phase_qbvh.rs @@ -7,20 +7,20 @@ use parry::query::visitors::BoundingVolumeIntersectionsSimultaneousVisitor; #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[derive(Clone)] -pub struct BroadPhase { +pub struct BroadPhaseQbvh { qbvh: Qbvh, stack: Vec<(u32, u32)>, #[cfg_attr(feature = "serde-serialize", serde(skip))] workspace: QbvhUpdateWorkspace, } -impl Default for BroadPhase { +impl Default for BroadPhaseQbvh { fn default() -> Self { Self::new() } } -impl BroadPhase { +impl BroadPhaseQbvh { pub fn new() -> Self { Self { qbvh: Qbvh::new(), diff --git a/src/geometry/mod.rs b/src/geometry/mod.rs index 1525211..e3a868f 100644 --- a/src/geometry/mod.rs +++ b/src/geometry/mod.rs @@ -2,8 +2,8 @@ pub use self::broad_phase_multi_sap::{BroadPhasePairEvent, ColliderPair}; -pub use self::broad_phase_multi_sap::BroadPhase; -// pub use self::broad_phase_qbvh::BroadPhase; +pub use self::broad_phase_multi_sap::BroadPhaseMultiSap; +// pub use self::broad_phase_qbvh::BroadPhaseMultiSap; pub use self::collider_components::*; pub use self::contact_pair::{ ContactData, ContactManifoldData, ContactPair, IntersectionPair, SolverContact, SolverFlags, diff --git a/src/pipeline/collision_pipeline.rs b/src/pipeline/collision_pipeline.rs index 03396ed..504ad8f 100644 --- a/src/pipeline/collision_pipeline.rs +++ b/src/pipeline/collision_pipeline.rs @@ -2,7 +2,8 @@ use crate::dynamics::{ImpulseJointSet, MultibodyJointSet}; use crate::geometry::{ - BroadPhase, BroadPhasePairEvent, ColliderChanges, ColliderHandle, ColliderPair, NarrowPhase, + BroadPhaseMultiSap, BroadPhasePairEvent, ColliderChanges, ColliderHandle, ColliderPair, + NarrowPhase, }; use crate::math::Real; use crate::pipeline::{EventHandler, PhysicsHooks, QueryPipeline}; @@ -43,7 +44,7 @@ impl CollisionPipeline { fn detect_collisions( &mut self, prediction_distance: Real, - broad_phase: &mut BroadPhase, + broad_phase: &mut BroadPhaseMultiSap, narrow_phase: &mut NarrowPhase, bodies: &mut RigidBodySet, colliders: &mut ColliderSet, @@ -107,7 +108,7 @@ impl CollisionPipeline { pub fn step( &mut self, prediction_distance: Real, - broad_phase: &mut BroadPhase, + broad_phase: &mut BroadPhaseMultiSap, narrow_phase: &mut NarrowPhase, bodies: &mut RigidBodySet, colliders: &mut ColliderSet, @@ -192,7 +193,7 @@ mod tests { let _ = collider_set.insert(collider_b); let integration_parameters = IntegrationParameters::default(); - let mut broad_phase = BroadPhase::new(); + let mut broad_phase = BroadPhaseMultiSap::new(); let mut narrow_phase = NarrowPhase::new(); let mut collision_pipeline = CollisionPipeline::new(); let physics_hooks = (); @@ -244,7 +245,7 @@ mod tests { let _ = collider_set.insert(collider_b); let integration_parameters = IntegrationParameters::default(); - let mut broad_phase = BroadPhase::new(); + let mut broad_phase = BroadPhaseMultiSap::new(); let mut narrow_phase = NarrowPhase::new(); let mut collision_pipeline = CollisionPipeline::new(); let physics_hooks = (); diff --git a/src/pipeline/physics_pipeline.rs b/src/pipeline/physics_pipeline.rs index 893882d..6b4d11d 100644 --- a/src/pipeline/physics_pipeline.rs +++ b/src/pipeline/physics_pipeline.rs @@ -10,7 +10,7 @@ use crate::dynamics::{ RigidBodyChanges, RigidBodyHandle, RigidBodyPosition, RigidBodyType, }; use crate::geometry::{ - BroadPhase, BroadPhasePairEvent, ColliderChanges, ColliderHandle, ColliderPair, + BroadPhaseMultiSap, BroadPhasePairEvent, ColliderChanges, ColliderHandle, ColliderPair, ContactManifoldIndex, NarrowPhase, TemporaryInteractionIndex, }; use crate::math::{Real, Vector}; @@ -93,7 +93,7 @@ impl PhysicsPipeline { &mut self, integration_parameters: &IntegrationParameters, islands: &mut IslandManager, - broad_phase: &mut BroadPhase, + broad_phase: &mut BroadPhaseMultiSap, narrow_phase: &mut NarrowPhase, bodies: &mut RigidBodySet, colliders: &mut ColliderSet, @@ -406,7 +406,7 @@ impl PhysicsPipeline { gravity: &Vector, integration_parameters: &IntegrationParameters, islands: &mut IslandManager, - broad_phase: &mut BroadPhase, + broad_phase: &mut BroadPhaseMultiSap, narrow_phase: &mut NarrowPhase, bodies: &mut RigidBodySet, colliders: &mut ColliderSet, @@ -650,7 +650,7 @@ mod test { CCDSolver, ImpulseJointSet, IntegrationParameters, IslandManager, RigidBodyBuilder, RigidBodySet, }; - use crate::geometry::{BroadPhase, ColliderBuilder, ColliderSet, NarrowPhase}; + use crate::geometry::{BroadPhaseMultiSap, ColliderBuilder, ColliderSet, NarrowPhase}; use crate::math::Vector; use crate::pipeline::PhysicsPipeline; use crate::prelude::{MultibodyJointSet, RigidBodyType}; @@ -661,7 +661,7 @@ mod test { let mut impulse_joints = ImpulseJointSet::new(); let mut multibody_joints = MultibodyJointSet::new(); let mut pipeline = PhysicsPipeline::new(); - let mut bf = BroadPhase::new(); + let mut bf = BroadPhaseMultiSap::new(); let mut nf = NarrowPhase::new(); let mut bodies = RigidBodySet::new(); let mut islands = IslandManager::new(); @@ -699,7 +699,7 @@ mod test { let mut impulse_joints = ImpulseJointSet::new(); let mut multibody_joints = MultibodyJointSet::new(); let mut pipeline = PhysicsPipeline::new(); - let mut bf = BroadPhase::new(); + let mut bf = BroadPhaseMultiSap::new(); let mut nf = NarrowPhase::new(); let mut islands = IslandManager::new(); @@ -809,7 +809,7 @@ mod test { let mut pipeline = PhysicsPipeline::new(); let gravity = Vector::y() * -9.81; let integration_parameters = IntegrationParameters::default(); - let mut broad_phase = BroadPhase::new(); + let mut broad_phase = BroadPhaseMultiSap::new(); let mut narrow_phase = NarrowPhase::new(); let mut bodies = RigidBodySet::new(); let mut colliders = ColliderSet::new(); @@ -859,7 +859,7 @@ mod test { let mut impulse_joints = ImpulseJointSet::new(); let mut multibody_joints = MultibodyJointSet::new(); let mut pipeline = PhysicsPipeline::new(); - let mut bf = BroadPhase::new(); + let mut bf = BroadPhaseMultiSap::new(); let mut nf = NarrowPhase::new(); let mut islands = IslandManager::new(); diff --git a/src_testbed/harness/mod.rs b/src_testbed/harness/mod.rs index e27a03a..458f02f 100644 --- a/src_testbed/harness/mod.rs +++ b/src_testbed/harness/mod.rs @@ -9,7 +9,7 @@ use rapier::dynamics::{ CCDSolver, ImpulseJointSet, IntegrationParameters, IslandManager, MultibodyJointSet, RigidBodySet, }; -use rapier::geometry::{BroadPhase, ColliderSet, NarrowPhase}; +use rapier::geometry::{BroadPhaseMultiSap, ColliderSet, NarrowPhase}; use rapier::math::{Real, Vector}; use rapier::pipeline::{ChannelEventCollector, PhysicsHooks, PhysicsPipeline, QueryPipeline}; @@ -179,7 +179,7 @@ impl Harness { self.physics.hooks = Box::new(hooks); self.physics.islands = IslandManager::new(); - self.physics.broad_phase = BroadPhase::new(); + self.physics.broad_phase = BroadPhaseMultiSap::new(); self.physics.narrow_phase = NarrowPhase::new(); self.state.timestep_id = 0; self.state.time = 0.0; diff --git a/src_testbed/physics/mod.rs b/src_testbed/physics/mod.rs index c3b750f..38c9da0 100644 --- a/src_testbed/physics/mod.rs +++ b/src_testbed/physics/mod.rs @@ -3,7 +3,9 @@ use rapier::dynamics::{ CCDSolver, ImpulseJointSet, IntegrationParameters, IslandManager, MultibodyJointSet, RigidBodySet, }; -use rapier::geometry::{BroadPhase, ColliderSet, CollisionEvent, ContactForceEvent, NarrowPhase}; +use rapier::geometry::{ + BroadPhaseMultiSap, ColliderSet, CollisionEvent, ContactForceEvent, NarrowPhase, +}; use rapier::math::{Real, Vector}; use rapier::pipeline::{PhysicsHooks, PhysicsPipeline, QueryPipeline}; @@ -20,7 +22,7 @@ pub struct PhysicsSnapshot { pub struct DeserializedPhysicsSnapshot { pub timestep_id: usize, - pub broad_phase: BroadPhase, + pub broad_phase: BroadPhaseMultiSap, pub narrow_phase: NarrowPhase, pub island_manager: IslandManager, pub bodies: RigidBodySet, @@ -32,7 +34,7 @@ pub struct DeserializedPhysicsSnapshot { impl PhysicsSnapshot { pub fn new( timestep_id: usize, - broad_phase: &BroadPhase, + broad_phase: &BroadPhaseMultiSap, narrow_phase: &NarrowPhase, island_manager: &IslandManager, bodies: &RigidBodySet, @@ -86,7 +88,7 @@ impl PhysicsSnapshot { pub struct PhysicsState { pub islands: IslandManager, - pub broad_phase: BroadPhase, + pub broad_phase: BroadPhaseMultiSap, pub narrow_phase: NarrowPhase, pub bodies: RigidBodySet, pub colliders: ColliderSet, @@ -110,7 +112,7 @@ impl PhysicsState { pub fn new() -> Self { Self { islands: IslandManager::new(), - broad_phase: BroadPhase::new(), + broad_phase: BroadPhaseMultiSap::new(), narrow_phase: NarrowPhase::new(), bodies: RigidBodySet::new(), colliders: ColliderSet::new(), From 3d112287b9fd5aa3ddf8c17bc1e6065fa721a9ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Sat, 23 Mar 2024 14:36:27 +0100 Subject: [PATCH 11/71] feat: add a BroadPhaseTrait for allowing custom broad-phases --- CHANGELOG.md | 6 + src/geometry/broad_phase.rs | 47 +++++++ .../broad_phase_multi_sap.rs | 130 +++++++++--------- src/geometry/broad_phase_multi_sap/mod.rs | 1 - .../broad_phase_multi_sap/sap_axis.rs | 4 +- .../broad_phase_multi_sap/sap_layer.rs | 16 +-- .../broad_phase_multi_sap/sap_proxy.rs | 24 ++-- .../broad_phase_multi_sap/sap_region.rs | 18 ++- src/geometry/collider.rs | 21 ++- src/geometry/collider_components.rs | 4 +- src/geometry/mod.rs | 9 +- src/pipeline/collision_pipeline.rs | 6 +- src/pipeline/physics_pipeline.rs | 6 +- 13 files changed, 181 insertions(+), 111 deletions(-) create mode 100644 src/geometry/broad_phase.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index b99f8d4..0a41b43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,12 @@ based only on its current velocity. - Add `Collider::copy_from` to copy most collider attributes to an existing collider. - Add `RigidBody::copy_from` to copy most rigid-body attributes to an existing rigid-body. +- Add the `BroadPhase` trait and expect an implementor of this trait as input to `PhysicsPipeline::step`. + +### Modified + +- Renamed `BroadPhase` to `BroadPhaseMultiSap`. The `BroadPhase` is no a trait that can be + implemented for providing a custom broad-phase to rapier. ## v0.18.0 (24 Jan. 2024) diff --git a/src/geometry/broad_phase.rs b/src/geometry/broad_phase.rs new file mode 100644 index 0000000..e51c351 --- /dev/null +++ b/src/geometry/broad_phase.rs @@ -0,0 +1,47 @@ +use crate::geometry::{BroadPhasePairEvent, ColliderHandle, ColliderSet}; +use parry::math::Real; + +/// An internal index stored in colliders by some broad-phase algorithms. +pub type BroadPhaseProxyIndex = u32; + +/// Trait implemented by broad-phase algorithms supported by Rapier. +/// +/// The task of a broad-phase algorithm is to detect potential collision pairs, usually based on +/// bounding volumes. The pairs must be concervative: it is OK to create a collision pair if +/// two objects don’t actually touch, but it is incorrect to remove a pair between two objects +/// that are still touching. In other words, it can have false-positive (though these induce +/// some computational overhead on the narrow-phase), but cannot have false-negative. +pub trait BroadPhase { + /// Updates the broad-phase. + /// + /// The results must be output through the `events` struct. The broad-phase algorithm is only + /// required to generate new events (i.e. no need to re-send an `AddPair` event if it was already + /// sent previously and no `RemovePair` happened since then). Sending redundant events is allowed + /// but can result in a slight computational overhead. + /// + /// The `colliders` set is mutable only to provide access to + /// [`collider.set_internal_broad_phase_proxy_index`]. Other properties of the collider should + /// **not** be modified during the broad-phase update. + /// + /// # Parameters + /// - `prediction_distance`: colliders that are not exactly touching, but closer to this + /// distance must form a collision pair. + /// - `colliders`: the set of colliders. Change detection with `collider.needs_broad_phase_update()` + /// can be relied on at this stage. + /// - `modified_colliders`: colliders that are know to be modified since the last update. + /// - `removed_colliders`: colliders that got removed since the last update. Any associated data + /// in the broad-phase should be removed by this call to `update`. + /// - `events`: the broad-phase’s output. They indicate what collision pairs need to be created + /// and what pairs need to be removed. It is OK to create pairs for colliders that don’t + /// actually collide (though this can increase computational overhead in the narrow-phase) + /// but it is important not to indicate removal of a collision pair if the underlying colliders + /// are still touching or closer than `prediction_distance`. + fn update( + &mut self, + prediction_distance: Real, + colliders: &mut ColliderSet, + modified_colliders: &[ColliderHandle], + removed_colliders: &[ColliderHandle], + events: &mut Vec, + ); +} diff --git a/src/geometry/broad_phase_multi_sap/broad_phase_multi_sap.rs b/src/geometry/broad_phase_multi_sap/broad_phase_multi_sap.rs index 24bc72d..c4661e3 100644 --- a/src/geometry/broad_phase_multi_sap/broad_phase_multi_sap.rs +++ b/src/geometry/broad_phase_multi_sap/broad_phase_multi_sap.rs @@ -1,12 +1,12 @@ use super::{ BroadPhasePairEvent, ColliderPair, SAPLayer, SAPProxies, SAPProxy, SAPProxyData, SAPRegionPool, }; -use crate::geometry::broad_phase_multi_sap::SAPProxyIndex; use crate::geometry::{ - ColliderBroadPhaseData, ColliderChanges, ColliderHandle, ColliderPosition, ColliderSet, - ColliderShape, + BroadPhaseProxyIndex, ColliderBroadPhaseData, ColliderChanges, ColliderHandle, + ColliderPosition, ColliderSet, ColliderShape, }; use crate::math::Real; +use crate::prelude::BroadPhase; use crate::utils::IndexMut2; use parry::bounding_volume::BoundingVolume; use parry::utils::hashmap::HashMap; @@ -90,7 +90,7 @@ pub struct BroadPhaseMultiSap { // Another alternative would be to remove ColliderProxyId and // just use a Coarena. But this seems like it could use too // much memory. - colliders_proxy_ids: HashMap, + colliders_proxy_ids: HashMap, #[cfg_attr(feature = "serde-serialize", serde(skip))] region_pool: SAPRegionPool, // To avoid repeated allocations. // We could think serializing this workspace is useless. @@ -156,7 +156,7 @@ impl BroadPhaseMultiSap { /// remove, the `complete_removal` method MUST be called to /// complete the removal of these proxies, by actually removing them /// from all the relevant layers/regions/axes. - fn predelete_proxy(&mut self, proxy_index: SAPProxyIndex) { + fn predelete_proxy(&mut self, proxy_index: BroadPhaseProxyIndex) { if proxy_index == crate::INVALID_U32 { // This collider has not been added to the broad-phase yet. return; @@ -449,65 +449,6 @@ impl BroadPhaseMultiSap { !layer.created_regions.is_empty() } - /// Updates the broad-phase, taking into account the new collider positions. - pub fn update( - &mut self, - prediction_distance: Real, - colliders: &mut ColliderSet, - modified_colliders: &[ColliderHandle], - removed_colliders: &[ColliderHandle], - events: &mut Vec, - ) { - // Phase 1: pre-delete the collisions that have been deleted. - self.handle_removed_colliders(removed_colliders); - - let mut need_region_propagation = false; - - // Phase 2: pre-delete the collisions that have been deleted. - for handle in modified_colliders { - // NOTE: we use `get` because the collider may no longer - // exist if it has been removed. - if let Some(co) = colliders.get_mut_internal(*handle) { - if !co.is_enabled() || !co.changes.needs_broad_phase_update() { - continue; - } - - let mut new_proxy_id = co.bf_data.proxy_index; - - if self.handle_modified_collider( - prediction_distance, - *handle, - &mut new_proxy_id, - (&co.pos, &co.shape, &co.changes), - ) { - need_region_propagation = true; - } - - if co.bf_data.proxy_index != new_proxy_id { - self.colliders_proxy_ids.insert(*handle, new_proxy_id); - - // Make sure we have the new proxy index in case - // the collider was added for the first time. - co.bf_data = ColliderBroadPhaseData { - proxy_index: new_proxy_id, - }; - } - } - } - - // Phase 3: bottom-up pass to propagate new regions from smaller layers to larger layers. - if need_region_propagation { - self.propagate_created_regions(); - } - - // Phase 4: top-down pass to propagate proxies from larger layers to smaller layers. - self.update_layers_and_find_pairs(events); - - // Phase 5: bottom-up pass to remove proxies, and propagate region removed from smaller - // layers to possible remove regions from larger layers that would become empty that way. - self.complete_removals(colliders, removed_colliders); - } - /// Propagate regions from the smallest layers up to the larger layers. /// /// Whenever a region is created on a layer `n`, then its Aabb must be @@ -618,6 +559,67 @@ impl BroadPhaseMultiSap { } } +impl BroadPhase for BroadPhaseMultiSap { + /// Updates the broad-phase, taking into account the new collider positions. + fn update( + &mut self, + prediction_distance: Real, + colliders: &mut ColliderSet, + modified_colliders: &[ColliderHandle], + removed_colliders: &[ColliderHandle], + events: &mut Vec, + ) { + // Phase 1: pre-delete the collisions that have been deleted. + self.handle_removed_colliders(removed_colliders); + + let mut need_region_propagation = false; + + // Phase 2: pre-delete the collisions that have been deleted. + for handle in modified_colliders { + // NOTE: we use `get` because the collider may no longer + // exist if it has been removed. + if let Some(co) = colliders.get_mut_internal(*handle) { + if !co.is_enabled() || !co.changes.needs_broad_phase_update() { + continue; + } + + let mut new_proxy_id = co.bf_data.proxy_index; + + if self.handle_modified_collider( + prediction_distance, + *handle, + &mut new_proxy_id, + (&co.pos, &co.shape, &co.changes), + ) { + need_region_propagation = true; + } + + if co.bf_data.proxy_index != new_proxy_id { + self.colliders_proxy_ids.insert(*handle, new_proxy_id); + + // Make sure we have the new proxy index in case + // the collider was added for the first time. + co.bf_data = ColliderBroadPhaseData { + proxy_index: new_proxy_id, + }; + } + } + } + + // Phase 3: bottom-up pass to propagate new regions from smaller layers to larger layers. + if need_region_propagation { + self.propagate_created_regions(); + } + + // Phase 4: top-down pass to propagate proxies from larger layers to smaller layers. + self.update_layers_and_find_pairs(events); + + // Phase 5: bottom-up pass to remove proxies, and propagate region removed from smaller + // layers to possible remove regions from larger layers that would become empty that way. + self.complete_removals(colliders, removed_colliders); + } +} + #[cfg(test)] mod test { use crate::dynamics::{ diff --git a/src/geometry/broad_phase_multi_sap/mod.rs b/src/geometry/broad_phase_multi_sap/mod.rs index a1951c7..b9b3097 100644 --- a/src/geometry/broad_phase_multi_sap/mod.rs +++ b/src/geometry/broad_phase_multi_sap/mod.rs @@ -1,6 +1,5 @@ pub use self::broad_phase_multi_sap::BroadPhaseMultiSap; pub use self::broad_phase_pair_event::{BroadPhasePairEvent, ColliderPair}; -pub use self::sap_proxy::SAPProxyIndex; use self::sap_axis::*; use self::sap_endpoint::*; diff --git a/src/geometry/broad_phase_multi_sap/sap_axis.rs b/src/geometry/broad_phase_multi_sap/sap_axis.rs index 2452148..f1afdee 100644 --- a/src/geometry/broad_phase_multi_sap/sap_axis.rs +++ b/src/geometry/broad_phase_multi_sap/sap_axis.rs @@ -1,6 +1,6 @@ use super::{SAPEndpoint, SAPProxies, NUM_SENTINELS}; use crate::geometry::broad_phase_multi_sap::DELETED_AABB_VALUE; -use crate::geometry::SAPProxyIndex; +use crate::geometry::BroadPhaseProxyIndex; use crate::math::Real; use bit_vec::BitVec; use parry::bounding_volume::BoundingVolume; @@ -39,7 +39,7 @@ impl SAPAxis { pub fn batch_insert( &mut self, dim: usize, - new_proxies: &[SAPProxyIndex], + new_proxies: &[BroadPhaseProxyIndex], proxies: &SAPProxies, reporting: Option<&mut HashMap<(u32, u32), bool>>, ) { diff --git a/src/geometry/broad_phase_multi_sap/sap_layer.rs b/src/geometry/broad_phase_multi_sap/sap_layer.rs index 12dc7f7..f1a3a31 100644 --- a/src/geometry/broad_phase_multi_sap/sap_layer.rs +++ b/src/geometry/broad_phase_multi_sap/sap_layer.rs @@ -1,6 +1,6 @@ use super::{SAPProxies, SAPProxy, SAPRegion, SAPRegionPool}; use crate::geometry::broad_phase_multi_sap::DELETED_AABB_VALUE; -use crate::geometry::{Aabb, SAPProxyIndex}; +use crate::geometry::{Aabb, BroadPhaseProxyIndex}; use crate::math::{Point, Real}; use parry::bounding_volume::BoundingVolume; use parry::utils::hashmap::{Entry, HashMap}; @@ -13,11 +13,11 @@ pub(crate) struct SAPLayer { pub smaller_layer: Option, pub larger_layer: Option, region_width: Real, - pub regions: HashMap, SAPProxyIndex>, + pub regions: HashMap, BroadPhaseProxyIndex>, #[cfg_attr(feature = "serde-serialize", serde(skip))] regions_to_potentially_remove: Vec>, // Workspace #[cfg_attr(feature = "serde-serialize", serde(skip))] - pub created_regions: Vec, + pub created_regions: Vec, } impl SAPLayer { @@ -103,7 +103,7 @@ impl SAPLayer { /// one region on its parent "larger" layer. fn register_subregion( &mut self, - proxy_id: SAPProxyIndex, + proxy_id: BroadPhaseProxyIndex, proxies: &mut SAPProxies, pool: &mut SAPRegionPool, ) { @@ -140,7 +140,7 @@ impl SAPLayer { fn unregister_subregion( &mut self, - proxy_id: SAPProxyIndex, + proxy_id: BroadPhaseProxyIndex, proxy_region: &SAPRegion, proxies: &mut SAPProxies, ) { @@ -191,7 +191,7 @@ impl SAPLayer { region_key: Point, proxies: &mut SAPProxies, pool: &mut SAPRegionPool, - ) -> SAPProxyIndex { + ) -> BroadPhaseProxyIndex { match self.regions.entry(region_key) { // Yay, the region already exists! Entry::Occupied(occupied) => *occupied.get(), @@ -266,7 +266,7 @@ impl SAPLayer { } } - pub fn predelete_proxy(&mut self, proxies: &mut SAPProxies, proxy_index: SAPProxyIndex) { + pub fn predelete_proxy(&mut self, proxies: &mut SAPProxies, proxy_index: BroadPhaseProxyIndex) { // Discretize the Aabb to find the regions that need to be invalidated. let proxy_aabb = &mut proxies[proxy_index].aabb; let start = super::point_key(proxy_aabb.mins, self.region_width); @@ -379,7 +379,7 @@ impl SAPLayer { pub fn proper_proxy_moved_to_bigger_layer( &mut self, proxies: &mut SAPProxies, - proxy_id: SAPProxyIndex, + proxy_id: BroadPhaseProxyIndex, ) { for (point, region_id) in &self.regions { let region = &mut proxies[*region_id].data.as_region_mut(); diff --git a/src/geometry/broad_phase_multi_sap/sap_proxy.rs b/src/geometry/broad_phase_multi_sap/sap_proxy.rs index 4d5d79e..ccc172f 100644 --- a/src/geometry/broad_phase_multi_sap/sap_proxy.rs +++ b/src/geometry/broad_phase_multi_sap/sap_proxy.rs @@ -1,11 +1,9 @@ use super::NEXT_FREE_SENTINEL; use crate::geometry::broad_phase_multi_sap::SAPRegion; -use crate::geometry::ColliderHandle; +use crate::geometry::{BroadPhaseProxyIndex, ColliderHandle}; use parry::bounding_volume::Aabb; use std::ops::{Index, IndexMut}; -pub type SAPProxyIndex = u32; - #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[derive(Clone)] pub enum SAPProxyData { @@ -49,7 +47,7 @@ impl SAPProxyData { pub struct SAPProxy { pub data: SAPProxyData, pub aabb: Aabb, - pub next_free: SAPProxyIndex, + pub next_free: BroadPhaseProxyIndex, // TODO: pack the layer_id and layer_depth into a single u16? pub layer_id: u8, pub layer_depth: i8, @@ -81,7 +79,7 @@ impl SAPProxy { #[derive(Clone)] pub struct SAPProxies { pub elements: Vec, - pub first_free: SAPProxyIndex, + pub first_free: BroadPhaseProxyIndex, } impl Default for SAPProxies { @@ -98,7 +96,7 @@ impl SAPProxies { } } - pub fn insert(&mut self, proxy: SAPProxy) -> SAPProxyIndex { + pub fn insert(&mut self, proxy: SAPProxy) -> BroadPhaseProxyIndex { if self.first_free != NEXT_FREE_SENTINEL { let proxy_id = self.first_free; self.first_free = self.elements[proxy_id as usize].next_free; @@ -110,31 +108,31 @@ impl SAPProxies { } } - pub fn remove(&mut self, proxy_id: SAPProxyIndex) { + pub fn remove(&mut self, proxy_id: BroadPhaseProxyIndex) { let proxy = &mut self.elements[proxy_id as usize]; proxy.next_free = self.first_free; self.first_free = proxy_id; } // NOTE: this must not take holes into account. - pub fn get_mut(&mut self, i: SAPProxyIndex) -> Option<&mut SAPProxy> { + pub fn get_mut(&mut self, i: BroadPhaseProxyIndex) -> Option<&mut SAPProxy> { self.elements.get_mut(i as usize) } // NOTE: this must not take holes into account. - pub fn get(&self, i: SAPProxyIndex) -> Option<&SAPProxy> { + pub fn get(&self, i: BroadPhaseProxyIndex) -> Option<&SAPProxy> { self.elements.get(i as usize) } } -impl Index for SAPProxies { +impl Index for SAPProxies { type Output = SAPProxy; - fn index(&self, i: SAPProxyIndex) -> &SAPProxy { + fn index(&self, i: BroadPhaseProxyIndex) -> &SAPProxy { self.elements.index(i as usize) } } -impl IndexMut for SAPProxies { - fn index_mut(&mut self, i: SAPProxyIndex) -> &mut SAPProxy { +impl IndexMut for SAPProxies { + fn index_mut(&mut self, i: BroadPhaseProxyIndex) -> &mut SAPProxy { self.elements.index_mut(i as usize) } } diff --git a/src/geometry/broad_phase_multi_sap/sap_region.rs b/src/geometry/broad_phase_multi_sap/sap_region.rs index 21ebca5..7e38eaa 100644 --- a/src/geometry/broad_phase_multi_sap/sap_region.rs +++ b/src/geometry/broad_phase_multi_sap/sap_region.rs @@ -1,5 +1,5 @@ use super::{SAPAxis, SAPProxies}; -use crate::geometry::SAPProxyIndex; +use crate::geometry::BroadPhaseProxyIndex; use crate::math::DIM; use bit_vec::BitVec; use parry::bounding_volume::Aabb; @@ -13,8 +13,8 @@ pub struct SAPRegion { pub axes: [SAPAxis; DIM], pub existing_proxies: BitVec, #[cfg_attr(feature = "serde-serialize", serde(skip))] - pub to_insert: Vec, // Workspace - pub subregions: Vec, + pub to_insert: Vec, // Workspace + pub subregions: Vec, pub id_in_parent_subregion: u32, pub update_count: u8, pub needs_update_after_subregion_removal: bool, @@ -90,7 +90,7 @@ impl SAPRegion { /// If this region contains the given proxy, this will decrement this region's proxy count. /// /// Returns `true` if this region contained the proxy. Returns `false` otherwise. - pub fn proper_proxy_moved_to_a_bigger_layer(&mut self, proxy_id: SAPProxyIndex) -> bool { + pub fn proper_proxy_moved_to_a_bigger_layer(&mut self, proxy_id: BroadPhaseProxyIndex) -> bool { if self.existing_proxies.get(proxy_id as usize) == Some(true) { // NOTE: we are just registering the fact that that proxy isn't a // subproper proxy anymore. But it is still part of this region @@ -142,7 +142,7 @@ impl SAPRegion { self.subproper_proxy_count -= num_deleted_subregion_endpoints[0] / 2; } - pub fn predelete_proxy(&mut self, _proxy_id: SAPProxyIndex) { + pub fn predelete_proxy(&mut self, _proxy_id: BroadPhaseProxyIndex) { // We keep the proxy_id as argument for uniformity with the "preupdate" // method. However we don't actually need it because the deletion will be // handled transparently during the next update. @@ -153,14 +153,18 @@ impl SAPRegion { self.update_count = self.update_count.max(1); } - pub fn register_subregion(&mut self, proxy_id: SAPProxyIndex) -> usize { + pub fn register_subregion(&mut self, proxy_id: BroadPhaseProxyIndex) -> usize { let subregion_index = self.subregions.len(); self.subregions.push(proxy_id); self.preupdate_proxy(proxy_id, true); subregion_index } - pub fn preupdate_proxy(&mut self, proxy_id: SAPProxyIndex, is_subproper_proxy: bool) -> bool { + pub fn preupdate_proxy( + &mut self, + proxy_id: BroadPhaseProxyIndex, + is_subproper_proxy: bool, + ) -> bool { let mask_len = self.existing_proxies.len(); if proxy_id as usize >= mask_len { self.existing_proxies diff --git a/src/geometry/collider.rs b/src/geometry/collider.rs index 4e7fbc7..5d2fa77 100644 --- a/src/geometry/collider.rs +++ b/src/geometry/collider.rs @@ -1,8 +1,8 @@ use crate::dynamics::{CoefficientCombineRule, MassProperties, RigidBodyHandle}; use crate::geometry::{ - ActiveCollisionTypes, ColliderBroadPhaseData, ColliderChanges, ColliderFlags, - ColliderMassProps, ColliderMaterial, ColliderParent, ColliderPosition, ColliderShape, - ColliderType, InteractionGroups, SharedShape, + ActiveCollisionTypes, BroadPhaseProxyIndex, ColliderBroadPhaseData, ColliderChanges, + ColliderFlags, ColliderMassProps, ColliderMaterial, ColliderParent, ColliderPosition, + ColliderShape, ColliderType, InteractionGroups, SharedShape, }; use crate::math::{AngVector, Isometry, Point, Real, Rotation, Vector, DIM}; use crate::parry::transformation::vhacd::VHACDParameters; @@ -50,6 +50,21 @@ impl Collider { } } + /// An internal index associated to this collider by the broad-phase algorithm. + pub fn internal_broad_phase_proxy_index(&self) -> BroadPhaseProxyIndex { + self.bf_data.proxy_index + } + + /// Sets the internal index associated to this collider by the broad-phase algorithm. + /// + /// This must **not** be called, unless you are implementing your own custom broad-phase + /// that require storing an index in the collider struct. + /// Modifying that index outside of a custom broad-phase code will most certainly break + /// the physics engine. + pub fn set_internal_broad_phase_proxy_index(&mut self, id: BroadPhaseProxyIndex) { + self.bf_data.proxy_index = id; + } + /// The rigid body this collider is attached to. pub fn parent(&self) -> Option { self.parent.map(|parent| parent.handle) diff --git a/src/geometry/collider_components.rs b/src/geometry/collider_components.rs index b71a7ed..6493cca 100644 --- a/src/geometry/collider_components.rs +++ b/src/geometry/collider_components.rs @@ -1,5 +1,5 @@ use crate::dynamics::{CoefficientCombineRule, MassProperties, RigidBodyHandle, RigidBodyType}; -use crate::geometry::{InteractionGroups, SAPProxyIndex, Shape, SharedShape}; +use crate::geometry::{BroadPhaseProxyIndex, InteractionGroups, Shape, SharedShape}; use crate::math::{Isometry, Real}; use crate::parry::partitioning::IndexedData; use crate::pipeline::{ActiveEvents, ActiveHooks}; @@ -118,7 +118,7 @@ impl ColliderType { #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] /// Data associated to a collider that takes part to a broad-phase algorithm. pub struct ColliderBroadPhaseData { - pub(crate) proxy_index: SAPProxyIndex, + pub(crate) proxy_index: BroadPhaseProxyIndex, } impl Default for ColliderBroadPhaseData { diff --git a/src/geometry/mod.rs b/src/geometry/mod.rs index e3a868f..565f9a3 100644 --- a/src/geometry/mod.rs +++ b/src/geometry/mod.rs @@ -1,9 +1,7 @@ //! Structures related to geometry: colliders, shapes, etc. -pub use self::broad_phase_multi_sap::{BroadPhasePairEvent, ColliderPair}; - -pub use self::broad_phase_multi_sap::BroadPhaseMultiSap; -// pub use self::broad_phase_qbvh::BroadPhaseMultiSap; +pub use self::broad_phase::BroadPhase; +pub use self::broad_phase_multi_sap::{BroadPhaseMultiSap, BroadPhasePairEvent, ColliderPair}; pub use self::collider_components::*; pub use self::contact_pair::{ ContactData, ContactManifoldData, ContactPair, IntersectionPair, SolverContact, SolverFlags, @@ -180,7 +178,7 @@ impl ContactForceEvent { } } -pub(crate) use self::broad_phase_multi_sap::SAPProxyIndex; +pub(crate) use self::broad_phase::BroadPhaseProxyIndex; pub(crate) use self::narrow_phase::ContactManifoldIndex; pub(crate) use parry::partitioning::Qbvh; pub use parry::shape::*; @@ -203,6 +201,7 @@ mod interaction_graph; mod interaction_groups; mod narrow_phase; +mod broad_phase; mod broad_phase_qbvh; mod collider; mod collider_set; diff --git a/src/pipeline/collision_pipeline.rs b/src/pipeline/collision_pipeline.rs index 504ad8f..52f68a3 100644 --- a/src/pipeline/collision_pipeline.rs +++ b/src/pipeline/collision_pipeline.rs @@ -2,8 +2,8 @@ use crate::dynamics::{ImpulseJointSet, MultibodyJointSet}; use crate::geometry::{ - BroadPhaseMultiSap, BroadPhasePairEvent, ColliderChanges, ColliderHandle, ColliderPair, - NarrowPhase, + BroadPhase, BroadPhaseMultiSap, BroadPhasePairEvent, ColliderChanges, ColliderHandle, + ColliderPair, NarrowPhase, }; use crate::math::Real; use crate::pipeline::{EventHandler, PhysicsHooks, QueryPipeline}; @@ -44,7 +44,7 @@ impl CollisionPipeline { fn detect_collisions( &mut self, prediction_distance: Real, - broad_phase: &mut BroadPhaseMultiSap, + broad_phase: &mut dyn BroadPhase, narrow_phase: &mut NarrowPhase, bodies: &mut RigidBodySet, colliders: &mut ColliderSet, diff --git a/src/pipeline/physics_pipeline.rs b/src/pipeline/physics_pipeline.rs index 6b4d11d..e921062 100644 --- a/src/pipeline/physics_pipeline.rs +++ b/src/pipeline/physics_pipeline.rs @@ -10,7 +10,7 @@ use crate::dynamics::{ RigidBodyChanges, RigidBodyHandle, RigidBodyPosition, RigidBodyType, }; use crate::geometry::{ - BroadPhaseMultiSap, BroadPhasePairEvent, ColliderChanges, ColliderHandle, ColliderPair, + BroadPhase, BroadPhasePairEvent, ColliderChanges, ColliderHandle, ColliderPair, ContactManifoldIndex, NarrowPhase, TemporaryInteractionIndex, }; use crate::math::{Real, Vector}; @@ -93,7 +93,7 @@ impl PhysicsPipeline { &mut self, integration_parameters: &IntegrationParameters, islands: &mut IslandManager, - broad_phase: &mut BroadPhaseMultiSap, + broad_phase: &mut dyn BroadPhase, narrow_phase: &mut NarrowPhase, bodies: &mut RigidBodySet, colliders: &mut ColliderSet, @@ -406,7 +406,7 @@ impl PhysicsPipeline { gravity: &Vector, integration_parameters: &IntegrationParameters, islands: &mut IslandManager, - broad_phase: &mut BroadPhaseMultiSap, + broad_phase: &mut dyn BroadPhase, narrow_phase: &mut NarrowPhase, bodies: &mut RigidBodySet, colliders: &mut ColliderSet, From b3a00b4123c85d047b5ab6b89d4d500cc2b92ba6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Sat, 23 Mar 2024 14:47:05 +0100 Subject: [PATCH 12/71] feat: add the DefaultBroadPhase type alias --- CHANGELOG.md | 3 ++- examples3d-f64/debug_serialized3.rs | 2 +- examples3d/debug_deserialize3.rs | 2 +- src/geometry/mod.rs | 6 ++++-- src/pipeline/collision_pipeline.rs | 5 ++--- src_testbed/harness/mod.rs | 4 ++-- src_testbed/physics/mod.rs | 10 +++++----- 7 files changed, 17 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a41b43..c2fb8a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,8 @@ ### Modified - Renamed `BroadPhase` to `BroadPhaseMultiSap`. The `BroadPhase` is no a trait that can be - implemented for providing a custom broad-phase to rapier. + implemented for providing a custom broad-phase to rapier. Equivalently, the `DefaultBroadPhase` type + alias can be used in place of `BroadPhaseMultiSap`. ## v0.18.0 (24 Jan. 2024) diff --git a/examples3d-f64/debug_serialized3.rs b/examples3d-f64/debug_serialized3.rs index 86ff2a8..3801d37 100644 --- a/examples3d-f64/debug_serialized3.rs +++ b/examples3d-f64/debug_serialized3.rs @@ -4,7 +4,7 @@ use rapier_testbed3d::Testbed; #[derive(serde::Deserialize)] struct State { pub islands: IslandManager, - pub broad_phase: BroadPhaseMultiSap, + pub broad_phase: DefaultBroadPhase, pub narrow_phase: NarrowPhase, pub bodies: RigidBodySet, pub colliders: ColliderSet, diff --git a/examples3d/debug_deserialize3.rs b/examples3d/debug_deserialize3.rs index e1ea083..0762b4d 100644 --- a/examples3d/debug_deserialize3.rs +++ b/examples3d/debug_deserialize3.rs @@ -6,7 +6,7 @@ struct PhysicsState { pub gravity: Vector, pub integration_parameters: IntegrationParameters, pub islands: IslandManager, - pub broad_phase: BroadPhaseMultiSap, + pub broad_phase: DefaultBroadPhase, pub narrow_phase: NarrowPhase, pub bodies: RigidBodySet, pub colliders: ColliderSet, diff --git a/src/geometry/mod.rs b/src/geometry/mod.rs index 565f9a3..5d2fb2f 100644 --- a/src/geometry/mod.rs +++ b/src/geometry/mod.rs @@ -49,10 +49,12 @@ pub type Aabb = parry::bounding_volume::Aabb; pub type Ray = parry::query::Ray; /// The intersection between a ray and a collider. pub type RayIntersection = parry::query::RayIntersection; -/// The the projection of a point on a collider. +/// The projection of a point on a collider. pub type PointProjection = parry::query::PointProjection; -/// The the time of impact between two shapes. +/// The time of impact between two shapes. pub type TOI = parry::query::TOI; +/// The default broad-phase implementation provided by Rapier. +pub type DefaultBroadPhase = BroadPhaseMultiSap; bitflags::bitflags! { /// Flags providing more information regarding a collision event. diff --git a/src/pipeline/collision_pipeline.rs b/src/pipeline/collision_pipeline.rs index 52f68a3..66bd9ad 100644 --- a/src/pipeline/collision_pipeline.rs +++ b/src/pipeline/collision_pipeline.rs @@ -2,8 +2,7 @@ use crate::dynamics::{ImpulseJointSet, MultibodyJointSet}; use crate::geometry::{ - BroadPhase, BroadPhaseMultiSap, BroadPhasePairEvent, ColliderChanges, ColliderHandle, - ColliderPair, NarrowPhase, + BroadPhase, BroadPhasePairEvent, ColliderChanges, ColliderHandle, ColliderPair, NarrowPhase, }; use crate::math::Real; use crate::pipeline::{EventHandler, PhysicsHooks, QueryPipeline}; @@ -108,7 +107,7 @@ impl CollisionPipeline { pub fn step( &mut self, prediction_distance: Real, - broad_phase: &mut BroadPhaseMultiSap, + broad_phase: &mut dyn BroadPhase, narrow_phase: &mut NarrowPhase, bodies: &mut RigidBodySet, colliders: &mut ColliderSet, diff --git a/src_testbed/harness/mod.rs b/src_testbed/harness/mod.rs index 458f02f..9c41472 100644 --- a/src_testbed/harness/mod.rs +++ b/src_testbed/harness/mod.rs @@ -9,7 +9,7 @@ use rapier::dynamics::{ CCDSolver, ImpulseJointSet, IntegrationParameters, IslandManager, MultibodyJointSet, RigidBodySet, }; -use rapier::geometry::{BroadPhaseMultiSap, ColliderSet, NarrowPhase}; +use rapier::geometry::{ColliderSet, DefaultBroadPhase, NarrowPhase}; use rapier::math::{Real, Vector}; use rapier::pipeline::{ChannelEventCollector, PhysicsHooks, PhysicsPipeline, QueryPipeline}; @@ -179,7 +179,7 @@ impl Harness { self.physics.hooks = Box::new(hooks); self.physics.islands = IslandManager::new(); - self.physics.broad_phase = BroadPhaseMultiSap::new(); + self.physics.broad_phase = DefaultBroadPhase::new(); self.physics.narrow_phase = NarrowPhase::new(); self.state.timestep_id = 0; self.state.time = 0.0; diff --git a/src_testbed/physics/mod.rs b/src_testbed/physics/mod.rs index 38c9da0..3c69f6e 100644 --- a/src_testbed/physics/mod.rs +++ b/src_testbed/physics/mod.rs @@ -4,7 +4,7 @@ use rapier::dynamics::{ RigidBodySet, }; use rapier::geometry::{ - BroadPhaseMultiSap, ColliderSet, CollisionEvent, ContactForceEvent, NarrowPhase, + ColliderSet, CollisionEvent, ContactForceEvent, DefaultBroadPhase, NarrowPhase, }; use rapier::math::{Real, Vector}; use rapier::pipeline::{PhysicsHooks, PhysicsPipeline, QueryPipeline}; @@ -22,7 +22,7 @@ pub struct PhysicsSnapshot { pub struct DeserializedPhysicsSnapshot { pub timestep_id: usize, - pub broad_phase: BroadPhaseMultiSap, + pub broad_phase: DefaultBroadPhase, pub narrow_phase: NarrowPhase, pub island_manager: IslandManager, pub bodies: RigidBodySet, @@ -34,7 +34,7 @@ pub struct DeserializedPhysicsSnapshot { impl PhysicsSnapshot { pub fn new( timestep_id: usize, - broad_phase: &BroadPhaseMultiSap, + broad_phase: &DefaultBroadPhase, narrow_phase: &NarrowPhase, island_manager: &IslandManager, bodies: &RigidBodySet, @@ -88,7 +88,7 @@ impl PhysicsSnapshot { pub struct PhysicsState { pub islands: IslandManager, - pub broad_phase: BroadPhaseMultiSap, + pub broad_phase: DefaultBroadPhase, pub narrow_phase: NarrowPhase, pub bodies: RigidBodySet, pub colliders: ColliderSet, @@ -112,7 +112,7 @@ impl PhysicsState { pub fn new() -> Self { Self { islands: IslandManager::new(), - broad_phase: BroadPhaseMultiSap::new(), + broad_phase: DefaultBroadPhase::new(), narrow_phase: NarrowPhase::new(), bodies: RigidBodySet::new(), colliders: ColliderSet::new(), From 6f8bd99eab40ef259366c52352f61789c11bb3b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Sat, 23 Mar 2024 15:02:36 +0100 Subject: [PATCH 13/71] Fix compilation of tests --- src/geometry/broad_phase_multi_sap/broad_phase_multi_sap.rs | 2 +- src/geometry/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/geometry/broad_phase_multi_sap/broad_phase_multi_sap.rs b/src/geometry/broad_phase_multi_sap/broad_phase_multi_sap.rs index c4661e3..1d026ce 100644 --- a/src/geometry/broad_phase_multi_sap/broad_phase_multi_sap.rs +++ b/src/geometry/broad_phase_multi_sap/broad_phase_multi_sap.rs @@ -625,7 +625,7 @@ mod test { use crate::dynamics::{ ImpulseJointSet, IslandManager, MultibodyJointSet, RigidBodyBuilder, RigidBodySet, }; - use crate::geometry::{BroadPhaseMultiSap, ColliderBuilder, ColliderSet}; + use crate::geometry::{BroadPhase, BroadPhaseMultiSap, ColliderBuilder, ColliderSet}; #[test] fn test_add_update_remove() { diff --git a/src/geometry/mod.rs b/src/geometry/mod.rs index 5d2fb2f..be2d9d5 100644 --- a/src/geometry/mod.rs +++ b/src/geometry/mod.rs @@ -53,7 +53,7 @@ pub type RayIntersection = parry::query::RayIntersection; pub type PointProjection = parry::query::PointProjection; /// The time of impact between two shapes. pub type TOI = parry::query::TOI; -/// The default broad-phase implementation provided by Rapier. +/// The default broad-phase implementation recommended for general-purpose usage. pub type DefaultBroadPhase = BroadPhaseMultiSap; bitflags::bitflags! { From 59dc9bfe00b831a9376bf409054505fc36283efe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Sat, 23 Mar 2024 16:03:26 +0100 Subject: [PATCH 14/71] Update Readme --- README.md | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 8e576fc..1174a1c 100644 --- a/README.md +++ b/README.md @@ -35,24 +35,28 @@ ## What is Rapier? -Rapier is a set of 2D and 3D physics engines for games, animation, and robotics. These crates +Rapier is a set of 2D and 3D physics engines for games, animation, and robotics. These crates are `rapier2d`, `rapier3d`, `rapier2d-f64`, and `rapier3d-f64`. They are written with the Rust programming language, by the [Dimforge](https://dimforge.com) organization. It is forever free and open-source! ## Roadmap + We update our roadmap at the beginning of each year. Our 2021 roadmap can be seen [there](https://www.dimforge.com/blog/2021/01/01/physics-simulation-with-rapier-2021-roadmap/#rapier-roadmap-for-2021). We regularly give updates about our progress on [our blog](https://www.dimforge.com/blog). ## Getting started + The easiest way to get started with Rapier is to: + 1. Read the [user-guides](https://www.rapier.rs/docs/). 2. Play with the examples: `cargo run --release --bin all_examples2` and `cargo run --release --bin all_examples3`. Their source code are available on the `examples2d/` and `examples3d/` directory. 3. Don't hesitate to ask for help on [Discord](https://discord.gg/vt9DJSW), or by opening an issue on GitHub. - + ## Resources and discussions + - [Dimforge](https://dimforge.com): See all the open-source projects we are working on! Follow our announcements on our [blog](https://www.dimforge.com/blog). - [User guide](https://www.rapier.rs/docs/): Learn to use Rapier in your project by reading the official User Guides. @@ -63,23 +67,3 @@ The easiest way to get started with Rapier is to: Please make sure to familiarize yourself with our [Code of Conduct](CODE_OF_CONDUCT.md) and our [Contribution Guidelines](CONTRIBUTING.md) before contributing or participating in discussions with the community. - - -## Acknowledgements -Rapier is supported by our **platinum** sponsors: -

- - - -

- -And our gold sponsors: - -

- - - - - - -

\ No newline at end of file From 09af4313fa650664792d43def4dc8cda4efa9731 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Sat, 23 Mar 2024 16:00:19 +0100 Subject: [PATCH 15/71] Fix reported contact impulse in the contact force event Address https://github.com/dimforge/rapier.js/issues/261 --- .../generic_one_body_constraint.rs | 2 +- .../generic_two_body_constraint.rs | 2 +- .../contact_constraint/one_body_constraint.rs | 14 ++++----- .../one_body_constraint_element.rs | 30 +++++++++++++++---- .../one_body_constraint_simd.rs | 14 ++++----- .../contact_constraint/two_body_constraint.rs | 14 ++++----- .../two_body_constraint_element.rs | 30 +++++++++++++++---- .../two_body_constraint_simd.rs | 14 ++++----- 8 files changed, 80 insertions(+), 40 deletions(-) diff --git a/src/dynamics/solver/contact_constraint/generic_one_body_constraint.rs b/src/dynamics/solver/contact_constraint/generic_one_body_constraint.rs index 7b1f8ea..cfdff55 100644 --- a/src/dynamics/solver/contact_constraint/generic_one_body_constraint.rs +++ b/src/dynamics/solver/contact_constraint/generic_one_body_constraint.rs @@ -153,7 +153,7 @@ impl GenericOneBodyConstraintBuilder { rhs: na::zero(), rhs_wo_bias: na::zero(), impulse: na::zero(), - total_impulse: na::zero(), + impulse_accumulator: na::zero(), r, }; } diff --git a/src/dynamics/solver/contact_constraint/generic_two_body_constraint.rs b/src/dynamics/solver/contact_constraint/generic_two_body_constraint.rs index 073f585..f0a7d64 100644 --- a/src/dynamics/solver/contact_constraint/generic_two_body_constraint.rs +++ b/src/dynamics/solver/contact_constraint/generic_two_body_constraint.rs @@ -201,7 +201,7 @@ impl GenericTwoBodyConstraintBuilder { gcross2, rhs: na::zero(), rhs_wo_bias: na::zero(), - total_impulse: na::zero(), + impulse_accumulator: na::zero(), impulse: na::zero(), r, }; diff --git a/src/dynamics/solver/contact_constraint/one_body_constraint.rs b/src/dynamics/solver/contact_constraint/one_body_constraint.rs index be108a4..c9b35f3 100644 --- a/src/dynamics/solver/contact_constraint/one_body_constraint.rs +++ b/src/dynamics/solver/contact_constraint/one_body_constraint.rs @@ -149,7 +149,7 @@ impl OneBodyConstraintBuilder { rhs: na::zero(), rhs_wo_bias: na::zero(), impulse: na::zero(), - total_impulse: na::zero(), + impulse_accumulator: na::zero(), r: projected_mass, }; } @@ -270,18 +270,17 @@ impl OneBodyConstraintBuilder { * (dist + params.allowed_linear_error) .clamp(-params.max_penetration_correction, 0.0); let new_rhs = rhs_wo_bias + rhs_bias; - let total_impulse = element.normal_part.total_impulse + element.normal_part.impulse; is_fast_contact = is_fast_contact || (-new_rhs * params.dt > ccd_thickness * 0.5); element.normal_part.rhs_wo_bias = rhs_wo_bias; element.normal_part.rhs = new_rhs; - element.normal_part.total_impulse = total_impulse; + element.normal_part.impulse_accumulator += element.normal_part.impulse; element.normal_part.impulse = na::zero(); } // Tangent part. { - element.tangent_part.total_impulse += element.tangent_part.impulse; + element.tangent_part.impulse_accumulator += element.tangent_part.impulse; element.tangent_part.impulse = na::zero(); for j in 0..DIM - 1 { @@ -359,15 +358,16 @@ impl OneBodyConstraint { for k in 0..self.num_contacts as usize { let contact_id = self.manifold_contact_id[k]; let active_contact = &mut manifold.points[contact_id as usize]; - active_contact.data.impulse = self.elements[k].normal_part.impulse; + active_contact.data.impulse = self.elements[k].normal_part.total_impulse(); #[cfg(feature = "dim2")] { - active_contact.data.tangent_impulse = self.elements[k].tangent_part.impulse[0]; + active_contact.data.tangent_impulse = + self.elements[k].tangent_part.total_impulse()[0]; } #[cfg(feature = "dim3")] { - active_contact.data.tangent_impulse = self.elements[k].tangent_part.impulse; + active_contact.data.tangent_impulse = self.elements[k].tangent_part.total_impulse(); } } } diff --git a/src/dynamics/solver/contact_constraint/one_body_constraint_element.rs b/src/dynamics/solver/contact_constraint/one_body_constraint_element.rs index d9ff7f4..79b207b 100644 --- a/src/dynamics/solver/contact_constraint/one_body_constraint_element.rs +++ b/src/dynamics/solver/contact_constraint/one_body_constraint_element.rs @@ -12,9 +12,9 @@ pub(crate) struct OneBodyConstraintTangentPart { #[cfg(feature = "dim3")] pub impulse: na::Vector2, #[cfg(feature = "dim2")] - pub total_impulse: na::Vector1, + pub impulse_accumulator: na::Vector1, #[cfg(feature = "dim3")] - pub total_impulse: na::Vector2, + pub impulse_accumulator: na::Vector2, #[cfg(feature = "dim2")] pub r: [N; 1], #[cfg(feature = "dim3")] @@ -28,7 +28,7 @@ impl OneBodyConstraintTangentPart { rhs: [na::zero(); DIM - 1], rhs_wo_bias: [na::zero(); DIM - 1], impulse: na::zero(), - total_impulse: na::zero(), + impulse_accumulator: na::zero(), #[cfg(feature = "dim2")] r: [na::zero(); 1], #[cfg(feature = "dim3")] @@ -36,6 +36,20 @@ impl OneBodyConstraintTangentPart { } } + /// Total impulse applied across all the solver substeps. + #[inline] + #[cfg(feature = "dim2")] + pub fn total_impulse(&self) -> na::Vector1 { + self.impulse_accumulator + self.impulse + } + + /// Total impulse applied across all the solver substeps. + #[inline] + #[cfg(feature = "dim3")] + pub fn total_impulse(&self) -> na::Vector2 { + self.impulse_accumulator + self.impulse + } + #[inline] pub fn apply_limit( &mut self, @@ -137,7 +151,7 @@ pub(crate) struct OneBodyConstraintNormalPart { pub rhs: N, pub rhs_wo_bias: N, pub impulse: N, - pub total_impulse: N, + pub impulse_accumulator: N, pub r: N, } @@ -148,11 +162,17 @@ impl OneBodyConstraintNormalPart { rhs: na::zero(), rhs_wo_bias: na::zero(), impulse: na::zero(), - total_impulse: na::zero(), + impulse_accumulator: na::zero(), r: na::zero(), } } + /// Total impulse applied across all the solver substeps. + #[inline] + pub fn total_impulse(&self) -> N { + self.impulse_accumulator + self.impulse + } + #[inline] pub fn solve( &mut self, diff --git a/src/dynamics/solver/contact_constraint/one_body_constraint_simd.rs b/src/dynamics/solver/contact_constraint/one_body_constraint_simd.rs index cd2ea56..03c1abe 100644 --- a/src/dynamics/solver/contact_constraint/one_body_constraint_simd.rs +++ b/src/dynamics/solver/contact_constraint/one_body_constraint_simd.rs @@ -154,7 +154,7 @@ impl SimdOneBodyConstraintBuilder { rhs: na::zero(), rhs_wo_bias: na::zero(), impulse: na::zero(), - total_impulse: na::zero(), + impulse_accumulator: na::zero(), r: projected_mass, }; } @@ -259,19 +259,18 @@ impl SimdOneBodyConstraintBuilder { .simd_clamp(-max_penetration_correction, SimdReal::zero()) * erp_inv_dt; let new_rhs = rhs_wo_bias + rhs_bias; - let total_impulse = element.normal_part.total_impulse + element.normal_part.impulse; is_fast_contact = is_fast_contact | (-new_rhs * dt).simd_gt(ccd_thickness * SimdReal::splat(0.5)); element.normal_part.rhs_wo_bias = rhs_wo_bias; element.normal_part.rhs = new_rhs; - element.normal_part.total_impulse = total_impulse; + element.normal_part.impulse_accumulator += element.normal_part.impulse; element.normal_part.impulse = na::zero(); } // tangent parts. { - element.tangent_part.total_impulse += element.tangent_part.impulse; + element.tangent_part.impulse_accumulator += element.tangent_part.impulse; element.tangent_part.impulse = na::zero(); for j in 0..DIM - 1 { @@ -334,11 +333,12 @@ impl OneBodyConstraintSimd { // FIXME: duplicated code. This is exactly the same as in the two-body velocity constraint. pub fn writeback_impulses(&self, manifolds_all: &mut [&mut ContactManifold]) { for k in 0..self.num_contacts as usize { - let impulses: [_; SIMD_WIDTH] = self.elements[k].normal_part.impulse.into(); + let impulses: [_; SIMD_WIDTH] = self.elements[k].normal_part.total_impulse().into(); #[cfg(feature = "dim2")] - let tangent_impulses: [_; SIMD_WIDTH] = self.elements[k].tangent_part.impulse[0].into(); + let tangent_impulses: [_; SIMD_WIDTH] = + self.elements[k].tangent_part.total_impulse()[0].into(); #[cfg(feature = "dim3")] - let tangent_impulses = self.elements[k].tangent_part.impulse; + let tangent_impulses = self.elements[k].tangent_part.total_impulse(); for ii in 0..SIMD_WIDTH { let manifold = &mut manifolds_all[self.manifold_id[ii]]; diff --git a/src/dynamics/solver/contact_constraint/two_body_constraint.rs b/src/dynamics/solver/contact_constraint/two_body_constraint.rs index 0a0ebd6..8abf5c5 100644 --- a/src/dynamics/solver/contact_constraint/two_body_constraint.rs +++ b/src/dynamics/solver/contact_constraint/two_body_constraint.rs @@ -223,7 +223,7 @@ impl TwoBodyConstraintBuilder { rhs: na::zero(), rhs_wo_bias: na::zero(), impulse: na::zero(), - total_impulse: na::zero(), + impulse_accumulator: na::zero(), r: projected_mass, }; } @@ -348,18 +348,17 @@ impl TwoBodyConstraintBuilder { * (dist + params.allowed_linear_error) .clamp(-params.max_penetration_correction, 0.0); let new_rhs = rhs_wo_bias + rhs_bias; - let total_impulse = element.normal_part.total_impulse + element.normal_part.impulse; is_fast_contact = is_fast_contact || (-new_rhs * params.dt > ccd_thickness * 0.5); element.normal_part.rhs_wo_bias = rhs_wo_bias; element.normal_part.rhs = new_rhs; - element.normal_part.total_impulse = total_impulse; + element.normal_part.impulse_accumulator += element.normal_part.impulse; element.normal_part.impulse = na::zero(); } // Tangent part. { - element.tangent_part.total_impulse += element.tangent_part.impulse; + element.tangent_part.impulse_accumulator += element.tangent_part.impulse; element.tangent_part.impulse = na::zero(); for j in 0..DIM - 1 { @@ -408,15 +407,16 @@ impl TwoBodyConstraint { for k in 0..self.num_contacts as usize { let contact_id = self.manifold_contact_id[k]; let active_contact = &mut manifold.points[contact_id as usize]; - active_contact.data.impulse = self.elements[k].normal_part.impulse; + active_contact.data.impulse = self.elements[k].normal_part.total_impulse(); #[cfg(feature = "dim2")] { - active_contact.data.tangent_impulse = self.elements[k].tangent_part.impulse[0]; + active_contact.data.tangent_impulse = + self.elements[k].tangent_part.total_impulse()[0]; } #[cfg(feature = "dim3")] { - active_contact.data.tangent_impulse = self.elements[k].tangent_part.impulse; + active_contact.data.tangent_impulse = self.elements[k].tangent_part.total_impulse(); } } } diff --git a/src/dynamics/solver/contact_constraint/two_body_constraint_element.rs b/src/dynamics/solver/contact_constraint/two_body_constraint_element.rs index 8c720b9..75fc739 100644 --- a/src/dynamics/solver/contact_constraint/two_body_constraint_element.rs +++ b/src/dynamics/solver/contact_constraint/two_body_constraint_element.rs @@ -13,9 +13,9 @@ pub(crate) struct TwoBodyConstraintTangentPart { #[cfg(feature = "dim3")] pub impulse: na::Vector2, #[cfg(feature = "dim2")] - pub total_impulse: na::Vector1, + pub impulse_accumulator: na::Vector1, #[cfg(feature = "dim3")] - pub total_impulse: na::Vector2, + pub impulse_accumulator: na::Vector2, #[cfg(feature = "dim2")] pub r: [N; 1], #[cfg(feature = "dim3")] @@ -30,7 +30,7 @@ impl TwoBodyConstraintTangentPart { rhs: [na::zero(); DIM - 1], rhs_wo_bias: [na::zero(); DIM - 1], impulse: na::zero(), - total_impulse: na::zero(), + impulse_accumulator: na::zero(), #[cfg(feature = "dim2")] r: [na::zero(); 1], #[cfg(feature = "dim3")] @@ -38,6 +38,20 @@ impl TwoBodyConstraintTangentPart { } } + /// Total impulse applied across all the solver substeps. + #[inline] + #[cfg(feature = "dim2")] + pub fn total_impulse(&self) -> na::Vector1 { + self.impulse_accumulator + self.impulse + } + + /// Total impulse applied across all the solver substeps. + #[inline] + #[cfg(feature = "dim3")] + pub fn total_impulse(&self) -> na::Vector2 { + self.impulse_accumulator + self.impulse + } + #[inline] pub fn apply_limit( &mut self, @@ -166,7 +180,7 @@ pub(crate) struct TwoBodyConstraintNormalPart { pub rhs: N, pub rhs_wo_bias: N, pub impulse: N, - pub total_impulse: N, + pub impulse_accumulator: N, pub r: N, } @@ -178,11 +192,17 @@ impl TwoBodyConstraintNormalPart { rhs: na::zero(), rhs_wo_bias: na::zero(), impulse: na::zero(), - total_impulse: na::zero(), + impulse_accumulator: na::zero(), r: na::zero(), } } + /// Total impulse applied across all the solver substeps. + #[inline] + pub fn total_impulse(&self) -> N { + self.impulse_accumulator + self.impulse + } + #[inline] pub fn solve( &mut self, diff --git a/src/dynamics/solver/contact_constraint/two_body_constraint_simd.rs b/src/dynamics/solver/contact_constraint/two_body_constraint_simd.rs index ee176d6..2de6ee2 100644 --- a/src/dynamics/solver/contact_constraint/two_body_constraint_simd.rs +++ b/src/dynamics/solver/contact_constraint/two_body_constraint_simd.rs @@ -138,7 +138,7 @@ impl TwoBodyConstraintBuilderSimd { rhs: na::zero(), rhs_wo_bias: na::zero(), impulse: SimdReal::splat(0.0), - total_impulse: SimdReal::splat(0.0), + impulse_accumulator: SimdReal::splat(0.0), r: projected_mass, }; } @@ -241,19 +241,18 @@ impl TwoBodyConstraintBuilderSimd { .simd_clamp(-max_penetration_correction, SimdReal::zero()) * erp_inv_dt; let new_rhs = rhs_wo_bias + rhs_bias; - let total_impulse = element.normal_part.total_impulse + element.normal_part.impulse; is_fast_contact = is_fast_contact | (-new_rhs * dt).simd_gt(ccd_thickness * SimdReal::splat(0.5)); element.normal_part.rhs_wo_bias = rhs_wo_bias; element.normal_part.rhs = new_rhs; - element.normal_part.total_impulse = total_impulse; + element.normal_part.impulse_accumulator += element.normal_part.impulse; element.normal_part.impulse = na::zero(); } // tangent parts. { - element.tangent_part.total_impulse += element.tangent_part.impulse; + element.tangent_part.impulse_accumulator += element.tangent_part.impulse; element.tangent_part.impulse = na::zero(); for j in 0..DIM - 1 { @@ -328,11 +327,12 @@ impl TwoBodyConstraintSimd { pub fn writeback_impulses(&self, manifolds_all: &mut [&mut ContactManifold]) { for k in 0..self.num_contacts as usize { - let impulses: [_; SIMD_WIDTH] = self.elements[k].normal_part.impulse.into(); + let impulses: [_; SIMD_WIDTH] = self.elements[k].normal_part.total_impulse().into(); #[cfg(feature = "dim2")] - let tangent_impulses: [_; SIMD_WIDTH] = self.elements[k].tangent_part.impulse[0].into(); + let tangent_impulses: [_; SIMD_WIDTH] = + self.elements[k].tangent_part.total_impulse()[0].into(); #[cfg(feature = "dim3")] - let tangent_impulses = self.elements[k].tangent_part.impulse; + let tangent_impulses = self.elements[k].tangent_part.total_impulse(); for ii in 0..SIMD_WIDTH { let manifold = &mut manifolds_all[self.manifold_id[ii]]; From f351dad9387d5d3d1eef475e217862008eea3725 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Sat, 23 Mar 2024 16:14:41 +0100 Subject: [PATCH 16/71] chore: update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c2fb8a3..9ce31b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Fix crash when simulating a spring joint between two dynamic bodies. - Fix kinematic bodies not being affected by gravity after being switched back to dynamic. +- Fix regression on contact force reporting from contact force events. ### Added From 2df7258570ce59aedd7fb4c0dea3d3290eb02a47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Sun, 24 Mar 2024 17:25:42 +0100 Subject: [PATCH 17/71] feat: update testbeds to bevy 0.13 --- crates/rapier_testbed2d-f64/Cargo.toml | 50 +++++++-------- crates/rapier_testbed2d/Cargo.toml | 50 +++++++-------- crates/rapier_testbed3d-f64/Cargo.toml | 48 +++++++-------- crates/rapier_testbed3d/Cargo.toml | 54 ++++++++--------- examples3d/vehicle_joints3.rs | 8 +-- src_testbed/camera2d.rs | 2 +- src_testbed/camera3d.rs | 2 +- src_testbed/debug_render.rs | 6 +- src_testbed/graphics.rs | 26 +++++--- src_testbed/objects/node.rs | 84 ++++++++++++++------------ src_testbed/plugin.rs | 6 +- src_testbed/testbed.rs | 80 +++++++++++++----------- 12 files changed, 222 insertions(+), 194 deletions(-) diff --git a/crates/rapier_testbed2d-f64/Cargo.toml b/crates/rapier_testbed2d-f64/Cargo.toml index 2212a03..933ddfe 100644 --- a/crates/rapier_testbed2d-f64/Cargo.toml +++ b/crates/rapier_testbed2d-f64/Cargo.toml @@ -1,12 +1,12 @@ [package] -name = "rapier_testbed2d-f64" +name = "rapier_testbed2d-f64" version = "0.18.0" -authors = [ "Sébastien Crozet " ] +authors = ["Sébastien Crozet "] description = "Testbed for the Rapier 2-dimensional physics engine in Rust." homepage = "http://rapier.org" repository = "https://github.com/dimforge/rapier" -categories = [ "science", "game-development", "mathematics", "simulation", "wasm"] -keywords = [ "physics", "dynamics", "rigid", "real-time", "impulse_joints" ] +categories = ["science", "game-development", "mathematics", "simulation", "wasm"] +keywords = ["physics", "dynamics", "rigid", "real-time", "impulse_joints"] license = "Apache-2.0" edition = "2021" @@ -16,48 +16,48 @@ maintenance = { status = "actively-developed" } [lib] name = "rapier_testbed2d" path = "../../src_testbed/lib.rs" -required-features = [ "dim2" ] +required-features = ["dim2"] [features] -default = [ "dim2" ] -dim2 = [ ] -parallel = [ "rapier/parallel", "num_cpus" ] -other-backends = [ "wrapped2d" ] +default = ["dim2"] +dim2 = [] +parallel = ["rapier/parallel", "num_cpus"] +other-backends = ["wrapped2d"] [package.metadata.docs.rs] features = ["parallel", "other-backends"] [dependencies] -nalgebra = { version = "0.32", features = [ "rand" ] } -rand = "0.8" -rand_pcg = "0.3" -instant = { version = "0.1", features = [ "web-sys", "now" ]} -bitflags = "1" -num_cpus = { version = "1", optional = true } -wrapped2d = { version = "0.4", optional = true } +nalgebra = { version = "0.32", features = ["rand"] } +rand = "0.8" +rand_pcg = "0.3" +instant = { version = "0.1", features = ["web-sys", "now"] } +bitflags = "1" +num_cpus = { version = "1", optional = true } +wrapped2d = { version = "0.4", optional = true } crossbeam = "0.8" bincode = "1" -Inflector = "0.11" +Inflector = "0.11" md5 = "0.7" -bevy_egui = "0.23" -bevy_ecs = "0.12" -bevy_core_pipeline = "0.12" -bevy_pbr = "0.12" -bevy_sprite = "0.12" +bevy_egui = "0.26" +bevy_ecs = "0.13" +bevy_core_pipeline = "0.13" +bevy_pbr = "0.13" +bevy_sprite = "0.13" #bevy_prototype_debug_lines = "0.7" # Dependencies for native only. [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -bevy = {version = "0.12", default-features = false, features = ["bevy_asset", "bevy_winit", "x11", "tonemapping_luts", "ktx2", "zstd", "bevy_render", "bevy_pbr", "bevy_gizmos"]} +bevy = { version = "0.13", default-features = false, features = ["bevy_asset", "bevy_winit", "x11", "tonemapping_luts", "ktx2", "zstd", "bevy_render", "bevy_pbr", "bevy_gizmos"] } # Dependencies for WASM only. [target.'cfg(target_arch = "wasm32")'.dependencies] -bevy = {version = "0.12", default-features = false, features = ["bevy_asset", "bevy_winit", "tonemapping_luts", "ktx2", "zstd", "bevy_render", "bevy_pbr", "bevy_gizmos"]} +bevy = { version = "0.13", default-features = false, features = ["bevy_asset", "bevy_winit", "tonemapping_luts", "ktx2", "zstd", "bevy_render", "bevy_pbr", "bevy_gizmos"] } #bevy_webgl2 = "0.5" [dependencies.rapier] package = "rapier2d-f64" path = "../rapier2d-f64" version = "0.18.0" -features = [ "serde-serialize", "debug-render", "profiler" ] +features = ["serde-serialize", "debug-render", "profiler"] diff --git a/crates/rapier_testbed2d/Cargo.toml b/crates/rapier_testbed2d/Cargo.toml index 06c32e3..9cbbe4a 100644 --- a/crates/rapier_testbed2d/Cargo.toml +++ b/crates/rapier_testbed2d/Cargo.toml @@ -1,12 +1,12 @@ [package] -name = "rapier_testbed2d" +name = "rapier_testbed2d" version = "0.18.0" -authors = [ "Sébastien Crozet " ] +authors = ["Sébastien Crozet "] description = "Testbed for the Rapier 2-dimensional physics engine in Rust." homepage = "http://rapier.org" repository = "https://github.com/dimforge/rapier" -categories = [ "science", "game-development", "mathematics", "simulation", "wasm"] -keywords = [ "physics", "dynamics", "rigid", "real-time", "impulse_joints" ] +categories = ["science", "game-development", "mathematics", "simulation", "wasm"] +keywords = ["physics", "dynamics", "rigid", "real-time", "impulse_joints"] license = "Apache-2.0" edition = "2021" @@ -16,48 +16,48 @@ maintenance = { status = "actively-developed" } [lib] name = "rapier_testbed2d" path = "../../src_testbed/lib.rs" -required-features = [ "dim2" ] +required-features = ["dim2"] [features] -default = [ "dim2" ] -dim2 = [ ] -parallel = [ "rapier/parallel", "num_cpus" ] -other-backends = [ "wrapped2d" ] +default = ["dim2"] +dim2 = [] +parallel = ["rapier/parallel", "num_cpus"] +other-backends = ["wrapped2d"] [package.metadata.docs.rs] features = ["parallel", "other-backends"] [dependencies] -nalgebra = { version = "0.32", features = [ "rand" ] } -rand = "0.8" -rand_pcg = "0.3" -instant = { version = "0.1", features = [ "web-sys", "now" ]} -bitflags = "1" -num_cpus = { version = "1", optional = true } -wrapped2d = { version = "0.4", optional = true } +nalgebra = { version = "0.32", features = ["rand"] } +rand = "0.8" +rand_pcg = "0.3" +instant = { version = "0.1", features = ["web-sys", "now"] } +bitflags = "1" +num_cpus = { version = "1", optional = true } +wrapped2d = { version = "0.4", optional = true } crossbeam = "0.8" bincode = "1" -Inflector = "0.11" +Inflector = "0.11" md5 = "0.7" -bevy_egui = "0.23" -bevy_ecs = "0.12" -bevy_core_pipeline = "0.12" -bevy_pbr = "0.12" -bevy_sprite = "0.12" +bevy_egui = "0.26" +bevy_ecs = "0.13" +bevy_core_pipeline = "0.13" +bevy_pbr = "0.13" +bevy_sprite = "0.13" #bevy_prototype_debug_lines = "0.7" # Dependencies for native only. [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -bevy = {version = "0.12", default-features = false, features = ["bevy_sprite", "bevy_winit", "x11", "tonemapping_luts", "ktx2", "zstd", "bevy_render", "bevy_pbr", "bevy_gizmos"]} +bevy = { version = "0.13", default-features = false, features = ["bevy_sprite", "bevy_winit", "x11", "tonemapping_luts", "ktx2", "zstd", "bevy_render", "bevy_pbr", "bevy_gizmos"] } # Dependencies for WASM only. [target.'cfg(target_arch = "wasm32")'.dependencies] -bevy = {version = "0.12", default-features = false, features = ["bevy_sprite", "bevy_winit", "tonemapping_luts", "ktx2", "zstd", "bevy_render", "bevy_pbr", "bevy_gizmos"]} +bevy = { version = "0.13", default-features = false, features = ["bevy_sprite", "bevy_winit", "tonemapping_luts", "ktx2", "zstd", "bevy_render", "bevy_pbr", "bevy_gizmos"] } #bevy_webgl2 = "0.5" [dependencies.rapier] package = "rapier2d" path = "../rapier2d" version = "0.18.0" -features = [ "serde-serialize", "debug-render", "profiler" ] +features = ["serde-serialize", "debug-render", "profiler"] diff --git a/crates/rapier_testbed3d-f64/Cargo.toml b/crates/rapier_testbed3d-f64/Cargo.toml index ca3c95c..1ceaa44 100644 --- a/crates/rapier_testbed3d-f64/Cargo.toml +++ b/crates/rapier_testbed3d-f64/Cargo.toml @@ -1,12 +1,12 @@ [package] -name = "rapier_testbed3d-f64" +name = "rapier_testbed3d-f64" version = "0.18.0" -authors = [ "Sébastien Crozet " ] +authors = ["Sébastien Crozet "] description = "Testbed for the Rapier 3-dimensional physics engine in Rust." homepage = "http://rapier.org" repository = "https://github.com/dimforge/rapier" -categories = [ "science", "game-development", "mathematics", "simulation", "wasm"] -keywords = [ "physics", "dynamics", "rigid", "real-time", "impulse_joints" ] +categories = ["science", "game-development", "mathematics", "simulation", "wasm"] +keywords = ["physics", "dynamics", "rigid", "real-time", "impulse_joints"] license = "Apache-2.0" edition = "2021" @@ -16,47 +16,47 @@ maintenance = { status = "actively-developed" } [lib] name = "rapier_testbed3d" path = "../../src_testbed/lib.rs" -required-features = [ "dim3" ] +required-features = ["dim3"] [features] -default = [ "dim3" ] -dim3 = [ ] -parallel = [ "rapier/parallel", "num_cpus" ] +default = ["dim3"] +dim3 = [] +parallel = ["rapier/parallel", "num_cpus"] [package.metadata.docs.rs] features = ["parallel"] [dependencies] -nalgebra = { version = "0.32", features = [ "rand" ] } -rand = "0.8" -rand_pcg = "0.3" -instant = { version = "0.1", features = [ "web-sys", "now" ]} -bitflags = "1" -num_cpus = { version = "1", optional = true } +nalgebra = { version = "0.32", features = ["rand"] } +rand = "0.8" +rand_pcg = "0.3" +instant = { version = "0.1", features = ["web-sys", "now"] } +bitflags = "1" +num_cpus = { version = "1", optional = true } crossbeam = "0.8" bincode = "1" md5 = "0.7" -Inflector = "0.11" -serde = { version = "1", features = [ "derive" ] } +Inflector = "0.11" +serde = { version = "1", features = ["derive"] } -bevy_egui = "0.23" -bevy_ecs = "0.12" -bevy_core_pipeline = "0.12" -bevy_pbr = "0.12" -bevy_sprite = "0.12" +bevy_egui = "0.26" +bevy_ecs = "0.13" +bevy_core_pipeline = "0.13" +bevy_pbr = "0.13" +bevy_sprite = "0.13" #bevy_prototype_debug_lines = { version = "0.7", features = [ "3d" ] } # Dependencies for native only. [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -bevy = {version = "0.12", default-features = false, features = ["bevy_winit", "x11", "tonemapping_luts", "ktx2", "zstd", "bevy_render", "bevy_pbr", "bevy_gizmos"]} +bevy = { version = "0.13", default-features = false, features = ["bevy_winit", "x11", "tonemapping_luts", "ktx2", "zstd", "bevy_render", "bevy_pbr", "bevy_gizmos"] } # Dependencies for WASM only. [target.'cfg(target_arch = "wasm32")'.dependencies] -bevy = {version = "0.12", default-features = false, features = ["bevy_winit", "tonemapping_luts", "ktx2", "zstd", "bevy_render", "bevy_pbr", "bevy_gizmos"]} +bevy = { version = "0.13", default-features = false, features = ["bevy_winit", "tonemapping_luts", "ktx2", "zstd", "bevy_render", "bevy_pbr", "bevy_gizmos"] } #bevy_webgl2 = "0.5" [dependencies.rapier] package = "rapier3d-f64" path = "../rapier3d-f64" version = "0.18.0" -features = [ "serde-serialize", "debug-render", "profiler" ] +features = ["serde-serialize", "debug-render", "profiler"] diff --git a/crates/rapier_testbed3d/Cargo.toml b/crates/rapier_testbed3d/Cargo.toml index 3b3bdeb..dd2a637 100644 --- a/crates/rapier_testbed3d/Cargo.toml +++ b/crates/rapier_testbed3d/Cargo.toml @@ -1,12 +1,12 @@ [package] -name = "rapier_testbed3d" +name = "rapier_testbed3d" version = "0.18.0" -authors = [ "Sébastien Crozet " ] +authors = ["Sébastien Crozet "] description = "Testbed for the Rapier 3-dimensional physics engine in Rust." homepage = "http://rapier.org" repository = "https://github.com/dimforge/rapier" -categories = [ "science", "game-development", "mathematics", "simulation", "wasm"] -keywords = [ "physics", "dynamics", "rigid", "real-time", "impulse_joints" ] +categories = ["science", "game-development", "mathematics", "simulation", "wasm"] +keywords = ["physics", "dynamics", "rigid", "real-time", "impulse_joints"] license = "Apache-2.0" edition = "2021" @@ -16,51 +16,51 @@ maintenance = { status = "actively-developed" } [lib] name = "rapier_testbed3d" path = "../../src_testbed/lib.rs" -required-features = [ "dim3" ] +required-features = ["dim3"] [features] -default = [ "dim3" ] -dim3 = [ ] -parallel = [ "rapier/parallel", "num_cpus" ] -other-backends = [ "physx", "physx-sys", "glam" ] +default = ["dim3"] +dim3 = [] +parallel = ["rapier/parallel", "num_cpus"] +other-backends = ["physx", "physx-sys", "glam"] [package.metadata.docs.rs] features = ["parallel", "other-backends"] [dependencies] -nalgebra = { version = "0.32", features = [ "rand" ] } -rand = "0.8" -rand_pcg = "0.3" -instant = { version = "0.1", features = [ "web-sys", "now" ]} -bitflags = "1" -glam = { version = "0.24", optional = true } # For Physx -num_cpus = { version = "1", optional = true } -physx = { version = "0.19", features = [ "glam" ], optional = true } +nalgebra = { version = "0.32", features = ["rand"] } +rand = "0.8" +rand_pcg = "0.3" +instant = { version = "0.1", features = ["web-sys", "now"] } +bitflags = "1" +glam = { version = "0.24", optional = true } # For Physx +num_cpus = { version = "1", optional = true } +physx = { version = "0.19", features = ["glam"], optional = true } physx-sys = { version = "0.11", optional = true } crossbeam = "0.8" bincode = "1" md5 = "0.7" -Inflector = "0.11" -serde = { version = "1", features = [ "derive" ] } +Inflector = "0.11" +serde = { version = "1", features = ["derive"] } -bevy_egui = "0.23" -bevy_ecs = "0.12" -bevy_core_pipeline = "0.12" -bevy_pbr = "0.12" -bevy_sprite = "0.12" +bevy_egui = "0.26" +bevy_ecs = "0.13" +bevy_core_pipeline = "0.13" +bevy_pbr = "0.13" +bevy_sprite = "0.13" #bevy_prototype_debug_lines = { version = "0.7", features = [ "3d" ] } # Dependencies for native only. [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -bevy = {version = "0.12", default-features = false, features = ["bevy_winit", "x11", "tonemapping_luts", "ktx2", "zstd", "bevy_render", "bevy_pbr", "bevy_gizmos"]} +bevy = { version = "0.13", default-features = false, features = ["bevy_winit", "x11", "tonemapping_luts", "ktx2", "zstd", "bevy_render", "bevy_pbr", "bevy_gizmos"] } # Dependencies for WASM only. [target.'cfg(target_arch = "wasm32")'.dependencies] -bevy = {version = "0.12", default-features = false, features = ["bevy_winit", "tonemapping_luts", "ktx2", "zstd", "bevy_render", "bevy_pbr", "bevy_gizmos"]} +bevy = { version = "0.13", default-features = false, features = ["bevy_winit", "tonemapping_luts", "ktx2", "zstd", "bevy_render", "bevy_pbr", "bevy_gizmos"] } #bevy_webgl2 = "0.5" [dependencies.rapier] package = "rapier3d" path = "../rapier3d" version = "0.18.0" -features = [ "serde-serialize", "debug-render", "profiler" ] +features = ["serde-serialize", "debug-render", "profiler"] diff --git a/examples3d/vehicle_joints3.rs b/examples3d/vehicle_joints3.rs index ddd672c..5eba608 100644 --- a/examples3d/vehicle_joints3.rs +++ b/examples3d/vehicle_joints3.rs @@ -138,16 +138,16 @@ pub fn init_world(testbed: &mut Testbed) { for key in gfx.keys().get_pressed() { match *key { - KeyCode::Right => { + KeyCode::ArrowRight => { steering = -1.0; } - KeyCode::Left => { + KeyCode::ArrowLeft => { steering = 1.0; } - KeyCode::Up => { + KeyCode::ArrowUp => { thrust = -drive_strength; } - KeyCode::Down => { + KeyCode::ArrowDown => { thrust = drive_strength; } KeyCode::ShiftRight => { diff --git a/src_testbed/camera2d.rs b/src_testbed/camera2d.rs index 6ba17a1..ae5c163 100644 --- a/src_testbed/camera2d.rs +++ b/src_testbed/camera2d.rs @@ -48,7 +48,7 @@ impl OrbitCameraPlugin { fn mouse_motion_system( _time: Res