Skip to content

Commit

Permalink
Correctly expose physics parameters. (#5)
Browse files Browse the repository at this point in the history
  • Loading branch information
distransient authored Jun 14, 2019
1 parent 9c379c0 commit d924441
Show file tree
Hide file tree
Showing 9 changed files with 377 additions and 109 deletions.
1 change: 1 addition & 0 deletions clippy.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
type-complexity-threshold = 500
90 changes: 64 additions & 26 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ extern crate nphysics3d as nphysics;
use std::collections::HashMap;

pub use nalgebra as math;
use nalgebra::{RealField, Scalar};
use nphysics::{
counters::Counters,
material::MaterialsCoefficientsTable,
object::{BodyHandle, ColliderHandle},
solver::IntegrationParameters,
world::World,
};
pub use shrev;
Expand All @@ -23,32 +25,82 @@ use specs_hierarchy::Parent;

use self::{
bodies::Position,
math::Vector3,
math::{RealField, Vector3},
systems::{
PhysicsStepperSystem,
SyncBodiesToPhysicsSystem,
SyncCollidersToPhysicsSystem,
SyncGravityToPhysicsSystem,
SyncParametersToPhysicsSystem,
SyncPositionsFromPhysicsSystem,
},
};

pub mod bodies;
pub mod colliders;
pub mod events;
pub mod parameters;
pub mod systems;

/// The `Physics` `Resource` contains the nphysics `World` and a set of
/// `HashMap`s containing handles to objects of the `World`. These are necessary
/// so we can properly react to removed `Component`s and clean up our `World`
/// accordingly.
/// Resource holding the internal fields where physics computation occurs.
/// Some inspection methods are exposed to allow debugging.
pub struct Physics<N: RealField> {
/// Core structure where physics computation and synchronization occurs.
/// Also contains ColliderWorld.
pub(crate) world: World<N>,

/// Hashmap of Entities to internal Physics bodies.
/// Necessary for reacting to removed Components.
pub(crate) body_handles: HashMap<Index, BodyHandle>,
/// Hashmap of Entities to internal Collider handles.
/// Necessary for reacting to removed Components.
pub(crate) collider_handles: HashMap<Index, ColliderHandle>,
}

// Some non-mutating methods for diagnostics and testing
impl<N: RealField> Physics<N> {
/// Creates a new instance of the physics structure.
pub fn new() -> Self {
Self::default()
}

/// Reports the internal value for the timestep.
/// See also `TimeStep` for setting this value.
pub fn timestep(&self) -> N {
self.world.timestep()
}

/// Reports the internal value for the gravity.
/// See also `Gravity` for setting this value.
pub fn gravity(&self) -> &Vector3<N> {
self.world.gravity()
}

/// Reports the internal value for prediction distance in collision
/// detection. This cannot change and will normally be `0.002m`
pub fn prediction(&self) -> N {
self.world.prediction()
}

/// Retrieves the performance statistics for the last simulated timestep.
/// Profiling is disabled by default.
/// See also `PhysicsProfilingEnabled` for enabling performance counters.
pub fn performance_counters(&self) -> &Counters {
self.world.performance_counters()
}

/// Retrieves the internal parameters for integration.
/// See also `PhysicsIntegrationParameters` for setting these parameters.
pub fn integration_parameters(&self) -> &IntegrationParameters<N> {
self.world.integration_parameters()
}

/// Retrieves the internal lookup table for friction and restitution
/// constants. Exposing this for modification is TODO.
pub fn materials_coefficients_table(&self) -> &MaterialsCoefficientsTable<N> {
self.world.materials_coefficients_table()
}
}

impl<N: RealField> Default for Physics<N> {
fn default() -> Self {
Self {
Expand All @@ -59,20 +111,6 @@ impl<N: RealField> Default for Physics<N> {
}
}

/// `Gravity` is a newtype for `Vector3`. It represents a constant
/// acceleration affecting all physical objects in the scene.
pub struct Gravity<N: RealField + Scalar>(Vector3<N>);

impl<N: RealField + Scalar> Default for Gravity<N> {
fn default() -> Self {
Self(Vector3::repeat(N::zero()))
}
}

/// The `TimeStep` is used to set the timestep of the nphysics integration, see
/// `nphysics::world::World::set_timestep(..)`.
pub struct TimeStep<N: RealField>(N);

/// The `PhysicsParent` `Component` is used to represent a parent/child
/// relationship between physics based `Entity`s.
#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)]
Expand Down Expand Up @@ -127,12 +165,12 @@ where
&["sync_bodies_to_physics_system"],
);

// add SyncGravityToPhysicsSystem; this System can be added at any point in time
// as it merely handles the gravity value of the nphysics World, thus it has no
// other dependencies
// add SyncParametersToPhysicsSystem; this System can be added at any point in
// time as it merely synchronizes the simulation parameters of the world,
// thus it has no other dependencies.
dispatcher_builder.add(
SyncGravityToPhysicsSystem::<N>::default(),
"sync_gravity_to_physics_system",
SyncParametersToPhysicsSystem::<N>::default(),
"sync_parameters_to_physics_system",
&[],
);

Expand Down
201 changes: 201 additions & 0 deletions src/parameters.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
//! # Parameters module
//! Resources for modifying the various simulation parameters of the
//! nphysics World.

use crate::math::{self as na, RealField, Scalar, Vector3};
use nphysics::solver::IntegrationParameters;
use std::ops::{Deref, DerefMut};

/// The `TimeStep` is used to set the timestep of the nphysics integration, see
/// `nphysics::world::World::set_timestep(..)`.
///
/// Warning: Do **NOT** change this value every frame, doing so will destabilize
/// the simulation. The stepping system itself should be called in a "fixed"
/// update which maintains a running delta. See [this blog post][gaffer]
/// by Glenn Fiedler to learn more about timesteps.
///
/// [gaffer]: https://gafferongames.com/game-physics/fix-your-timestep/%22
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct TimeStep<N: RealField>(pub N);

impl<N: RealField> Deref for TimeStep<N> {
type Target = N;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl<N: RealField> DerefMut for TimeStep<N> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

impl<N: RealField> Default for TimeStep<N> {
fn default() -> Self {
Self(na::convert(1.0 / 60.0))
}
}

/// `Gravity` is a newtype for `Vector3`. It represents a constant
/// acceleration affecting all physical objects in the scene.
#[derive(Debug, PartialEq)]
pub struct Gravity<N: RealField + Scalar>(pub Vector3<N>);

impl<N: RealField + Scalar> Deref for Gravity<N> {
type Target = Vector3<N>;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl<N: RealField + Scalar> DerefMut for Gravity<N> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

impl<N: RealField + Scalar> Default for Gravity<N> {
fn default() -> Self {
Self(Vector3::<N>::zeros())
}
}

/// Enables reporting of `nphysics::counters`,
/// which can be read via `Physics::performance_counters`
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct PhysicsProfilingEnabled(pub bool);

impl Deref for PhysicsProfilingEnabled {
type Target = bool;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl DerefMut for PhysicsProfilingEnabled {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

impl Default for PhysicsProfilingEnabled {
fn default() -> Self {
Self(false)
}
}

/// Essentially identical to the nphysics IntegrationParameters struct except
/// without the t and dt fields. Manages the details of physics integration.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct PhysicsIntegrationParameters<N: RealField> {
/// The `[0,1]` proportion of the positional error to be corrected at each
/// time step.
///
/// default: `0.2`
pub error_reduction_parameter: N,

/// Each cached impulse are multiplied by this `[0, 1]` coefficient when
/// they are re-used to initialize the solver.
///
/// default: `1.0`
pub warmstart_coefficient: N,

/// Contacts at points where the involved bodies have a relative velocity
/// smaller than this threshold won't be affected by the restitution force.
///
/// default: `1.0`
pub restitution_velocity_threshold: N,

/// Ammount of penetration the engine won't attempt to correct.
///
/// default: `0.001m`
pub allowed_linear_error: N,

/// Ammount of angular drift of joint limits the engine won't attempt to
/// correct.
///
/// default: `0.001rad`
pub allowed_angular_error: N,

/// Maximum linear correction during one step of the non-linear position
/// solver.
///
/// default: `100.0`
pub max_linear_correction: N,

/// Maximum angular correction during one step of the non-linear position
/// solver.
///
/// default: `0.2`
pub max_angular_correction: N,

/// Maximum nonlinera SOR-prox scaling parameter when the constraint
/// correction direction is close to the kernel of the involved multibody's
/// jacobian.
///
/// default: `0.2`
pub max_stabilization_multiplier: N,

/// Maximum number of iterations performed by the velocity constraints
/// solver.
///
/// default: `8`
pub max_velocity_iterations: usize,

/// Maximum number of iterations performed by the position-based constraints
/// solver.
///
/// default: `3`
pub max_position_iterations: usize,
}

impl<N: RealField> PhysicsIntegrationParameters<N> {
pub(crate) fn apply(&self, to: &mut IntegrationParameters<N>) {
to.erp = self.error_reduction_parameter;
to.warmstart_coeff = self.warmstart_coefficient;
to.restitution_velocity_threshold = self.restitution_velocity_threshold;
to.allowed_linear_error = self.allowed_linear_error;
to.allowed_angular_error = self.allowed_angular_error;
to.max_linear_correction = self.max_linear_correction;
to.max_angular_correction = self.max_angular_correction;
to.max_stabilization_multiplier = self.max_stabilization_multiplier;
to.max_velocity_iterations = self.max_velocity_iterations;
to.max_position_iterations = self.max_position_iterations;
}
}

impl<N: RealField> PartialEq<IntegrationParameters<N>> for PhysicsIntegrationParameters<N> {
fn eq(&self, other: &IntegrationParameters<N>) -> bool {
self.error_reduction_parameter == other.erp
&& self.warmstart_coefficient == other.warmstart_coeff
&& self.restitution_velocity_threshold == other.restitution_velocity_threshold
&& self.allowed_linear_error == other.allowed_linear_error
&& self.allowed_angular_error == other.allowed_angular_error
&& self.max_linear_correction == other.max_linear_correction
&& self.max_angular_correction == other.max_angular_correction
&& self.max_stabilization_multiplier == other.max_stabilization_multiplier
&& self.max_velocity_iterations == other.max_velocity_iterations
&& self.max_position_iterations == other.max_position_iterations
}
}

impl<N: RealField> Default for PhysicsIntegrationParameters<N> {
fn default() -> Self {
PhysicsIntegrationParameters {
error_reduction_parameter: na::convert(0.2),
warmstart_coefficient: na::convert(1.0),
restitution_velocity_threshold: na::convert(1.0),
allowed_linear_error: na::convert(0.001),
allowed_angular_error: na::convert(0.001),
max_linear_correction: na::convert(100.0),
max_angular_correction: na::convert(0.2),
max_stabilization_multiplier: na::convert(0.2),
max_velocity_iterations: 8,
max_position_iterations: 3,
}
}
}
4 changes: 2 additions & 2 deletions src/systems/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ pub use self::{
physics_stepper::PhysicsStepperSystem,
sync_bodies_to_physics::SyncBodiesToPhysicsSystem,
sync_colliders_to_physics::SyncCollidersToPhysicsSystem,
sync_gravity_to_physics::SyncGravityToPhysicsSystem,
sync_parameters_to_physics::SyncParametersToPhysicsSystem,
sync_positions_from_physics::SyncPositionsFromPhysicsSystem,
};

mod physics_stepper;
mod sync_bodies_to_physics;
mod sync_colliders_to_physics;
mod sync_gravity_to_physics;
mod sync_parameters_to_physics;
mod sync_positions_from_physics;

/// Iterated over the `ComponentEvent::Inserted`s of a given, tracked `Storage`
Expand Down
2 changes: 1 addition & 1 deletion src/systems/physics_stepper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ use std::marker::PhantomData;

use crate::{
events::{ContactEvent, ContactEvents, ContactType, ProximityEvent, ProximityEvents},
parameters::TimeStep,
Physics,
TimeStep,
};

/// The `PhysicsStepperSystem` progresses the nphysics `World`.
Expand Down
4 changes: 2 additions & 2 deletions src/systems/sync_bodies_to_physics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ fn add_rigid_body<N, P>(
.build(&mut physics.world)
.handle();

physics_body.handle = Some(handle.clone());
physics_body.handle = Some(handle);
physics.body_handles.insert(id, handle);

info!(
Expand Down Expand Up @@ -205,7 +205,7 @@ fn update_rigid_body<N, P>(
));
rigid_body.set_angular_inertia(physics_body.angular_inertia);
rigid_body.set_mass(physics_body.mass);
rigid_body.set_local_center_of_mass(physics_body.local_center_of_mass.clone());
rigid_body.set_local_center_of_mass(physics_body.local_center_of_mass);
}

// the Position was modified, update the position directly
Expand Down
Loading

0 comments on commit d924441

Please sign in to comment.