Skip to content
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
# Changelog

## Unreleased

### Added

- Add a `PhysicsContext` struct containing top level rapier structs to help with reducing boilerplate.

## v0.23.0 (08 Jan 2025)

### Fix
Expand All @@ -16,7 +24,6 @@
- `RigidBodySet` and `ColliderSet` have a new constructor `with_capacity`.
- Use `profiling` crate to provide helpful profiling information in different tools.
- The testbeds have been updated to use `puffin_egui`

### Modified

- `InteractionGroups` default value for `memberships` is now `GROUP_1` (#706)
Expand Down
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ pub mod geometry;
pub mod pipeline;
pub mod utils;

mod physics_context;

/// Elementary mathematical entities (vectors, matrices, isometries, etc).
pub mod math {
pub use parry::math::*;
Expand Down Expand Up @@ -215,6 +217,7 @@ pub mod prelude {
pub use crate::dynamics::*;
pub use crate::geometry::*;
pub use crate::math::*;
pub use crate::physics_context::PhysicsContext;
pub use crate::pipeline::*;
pub use na::{point, vector, DMatrix, DVector};
pub extern crate nalgebra;
Expand Down
311 changes: 311 additions & 0 deletions src/physics_context.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,311 @@
use parry::query::{NonlinearRigidMotion, ShapeCastOptions};

use crate::prelude::*;

/// Contains all arguments to be passed to [`PhysicsPipeline::step`]
#[allow(missing_docs)]
pub struct PhysicsContext {
pub gravity: Vector<Real>,
pub integration_parameters: IntegrationParameters,
pub physics_pipeline: PhysicsPipeline,
pub island_manager: IslandManager,
pub broad_phase: DefaultBroadPhase,
pub narrow_phase: NarrowPhase,
pub bodies: RigidBodySet,
pub colliders: ColliderSet,
pub impulse_joints: ImpulseJointSet,
pub multibody_joints: MultibodyJointSet,
pub ccd_solver: CCDSolver,
pub query_pipeline: Option<QueryPipeline>,
}

impl Default for PhysicsContext {
fn default() -> Self {
Self {
gravity: Vector::<Real>::default(),
integration_parameters: IntegrationParameters::default(),
physics_pipeline: PhysicsPipeline::new(),
island_manager: IslandManager::new(),
broad_phase: BroadPhaseMultiSap::new(),
narrow_phase: NarrowPhase::new(),
bodies: RigidBodySet::new(),
colliders: ColliderSet::new(),
impulse_joints: ImpulseJointSet::new(),
multibody_joints: MultibodyJointSet::new(),
ccd_solver: CCDSolver::new(),
query_pipeline: Some(QueryPipeline::new()),
}
}
}

impl PhysicsContext {
/// Creates a default physics context without a query pipeline.
pub fn default_without_query_pipeline() -> Self {
Self {
query_pipeline: None,
..PhysicsContext::default()
}
}

/// Shortcut to [`PhysicsPipeline::step`]
pub fn step(&mut self, hooks: &dyn PhysicsHooks, events: &dyn EventHandler) {
self.physics_pipeline.step(
&self.gravity,
&self.integration_parameters,
&mut self.island_manager,
&mut self.broad_phase,
&mut self.narrow_phase,
&mut self.bodies,
&mut self.colliders,
&mut self.impulse_joints,
&mut self.multibody_joints,
&mut self.ccd_solver,
self.query_pipeline.as_mut(),
hooks,
events,
);
}

/// Shortcut to [`ColliderSet::insert_with_parent`]
pub fn insert_rigidbody(
&mut self,
parent_handle: RigidBodyHandle,
collider: impl Into<Collider>,
) -> ColliderHandle {
self.colliders
.insert_with_parent(collider, parent_handle, &mut self.bodies)
}

/// Shortcut to [`RigidBodySet::insert`] and [`ColliderSet::insert_with_parent`]
pub fn insert_body_and_collider(
&mut self,
collider: impl Into<Collider>,
rb: impl Into<RigidBody>,
) -> (RigidBodyHandle, ColliderHandle) {
let parent_handle = self.bodies.insert(rb);
(
parent_handle,
self.colliders
.insert_with_parent(collider, parent_handle, &mut self.bodies),
)
}
}

/// Shortcuts to [`QueryPipeline`]
impl PhysicsContext {
/// Shortcut to [`QueryPipeline::update_incremental`]
pub fn update_incremental(
&mut self,
modified_colliders: &[ColliderHandle],
removed_colliders: &[ColliderHandle],
refit_and_rebalance: bool,
) {
let Some(query_pipeline) = &mut self.query_pipeline else {
return;
};
query_pipeline.update_incremental(
&self.colliders,
modified_colliders,
removed_colliders,
refit_and_rebalance,
);
}

/// Shortcut to [`QueryPipeline::update`]
pub fn update(&mut self) {
let Some(query_pipeline) = &mut self.query_pipeline else {
return;
};
query_pipeline.update(&self.colliders)
}

/// Shortcut to [`QueryPipeline::cast_ray`]
pub fn cast_ray(
&self,
ray: &Ray,
max_toi: Real,
solid: bool,
filter: QueryFilter,
) -> Option<(ColliderHandle, Real)> {
let Some(query_pipeline) = &self.query_pipeline else {
return None;
};
query_pipeline.cast_ray(&self.bodies, &self.colliders, ray, max_toi, solid, filter)
}

/// Shortcut to [`QueryPipeline::cast_ray_and_get_normal`]
pub fn cast_ray_and_get_normal(
&self,
ray: &Ray,
max_toi: Real,
solid: bool,
filter: QueryFilter,
) -> Option<(ColliderHandle, RayIntersection)> {
let Some(query_pipeline) = &self.query_pipeline else {
return None;
};
query_pipeline.cast_ray_and_get_normal(
&self.bodies,
&self.colliders,
ray,
max_toi,
solid,
filter,
)
}

/// Shortcut to [`QueryPipeline::intersections_with_ray`]
pub fn intersections_with_ray(
&self,
ray: &Ray,
max_toi: Real,
solid: bool,
filter: QueryFilter,
callback: impl FnMut(ColliderHandle, RayIntersection) -> bool,
) {
let Some(query_pipeline) = &self.query_pipeline else {
return;
};
query_pipeline.intersections_with_ray(
&self.bodies,
&self.colliders,
ray,
max_toi,
solid,
filter,
callback,
)
}

/// Shortcut to [`QueryPipeline::intersection_with_shape`]
pub fn intersection_with_shape(
&self,
shape_pos: &Isometry<Real>,
shape: &dyn Shape,
filter: QueryFilter,
) -> Option<ColliderHandle> {
let Some(query_pipeline) = &self.query_pipeline else {
return None;
};
query_pipeline.intersection_with_shape(
&self.bodies,
&self.colliders,
shape_pos,
shape,
filter,
)
}

/// Shortcut to [`QueryPipeline::project_point`]
pub fn project_point(
&self,
point: &Point<Real>,
solid: bool,
filter: QueryFilter,
) -> Option<(ColliderHandle, PointProjection)> {
let Some(query_pipeline) = &self.query_pipeline else {
return None;
};
query_pipeline.project_point(&self.bodies, &self.colliders, point, solid, filter)
}

/// Shortcut to [`QueryPipeline::intersections_with_point`]
pub fn intersections_with_point(
&self,
point: &Point<Real>,
filter: QueryFilter,
callback: impl FnMut(ColliderHandle) -> bool,
) {
let Some(query_pipeline) = &self.query_pipeline else {
return;
};
query_pipeline.intersections_with_point(
&self.bodies,
&self.colliders,
point,
filter,
callback,
)
}

/// Shortcut to [`QueryPipeline::project_point_and_get_feature`]
pub fn project_point_and_get_feature(
&self,
point: &Point<Real>,
filter: QueryFilter,
) -> Option<(ColliderHandle, PointProjection, FeatureId)> {
let Some(query_pipeline) = &self.query_pipeline else {
return None;
};
query_pipeline.project_point_and_get_feature(&self.bodies, &self.colliders, point, filter)
}

/// Shortcut to [`QueryPipeline::cast_shape`]
pub fn cast_shape(
&self,
shape_pos: &Isometry<Real>,
shape_vel: &Vector<Real>,
shape: &dyn Shape,
options: ShapeCastOptions,
filter: QueryFilter,
) -> Option<(ColliderHandle, ShapeCastHit)> {
let Some(query_pipeline) = &self.query_pipeline else {
return None;
};
query_pipeline.cast_shape(
&self.bodies,
&self.colliders,
shape_pos,
shape_vel,
shape,
options,
filter,
)
}

/// Shortcut to [`QueryPipeline::nonlinear_cast_shape`]
pub fn nonlinear_cast_shape(
&self,
shape_motion: &NonlinearRigidMotion,
shape: &dyn Shape,
start_time: Real,
end_time: Real,
stop_at_penetration: bool,
filter: QueryFilter,
) -> Option<(ColliderHandle, ShapeCastHit)> {
let Some(query_pipeline) = &self.query_pipeline else {
return None;
};
query_pipeline.nonlinear_cast_shape(
&self.bodies,
&self.colliders,
shape_motion,
shape,
start_time,
end_time,
stop_at_penetration,
filter,
)
}

/// Shortcut to [`QueryPipeline::intersections_with_shape`]
pub fn intersections_with_shape(
&self,
shape_pos: &Isometry<Real>,
shape: &dyn Shape,
filter: QueryFilter,
callback: impl FnMut(ColliderHandle) -> bool,
) {
let Some(query_pipeline) = &self.query_pipeline else {
return;
};
query_pipeline.intersections_with_shape(
&self.bodies,
&self.colliders,
shape_pos,
shape,
filter,
callback,
)
}
}
Loading