Rigid Body Dynamics

In this chapter we cover a number of topics that are also important to understand once you are comfortable with setting up a basic rigid body simulation world.

Applying Forces and Torques

The most physics-friendly way to interact with a body is to apply a force to it. In classical mechanics, most interactions between bodies are typically solved by using forces. Because of the law:

f = m*a (force = mass * acceleration)

Forces directly control a body's acceleration, but its velocity and position only indirectly. For this reason control by force may be inconvenient if you need immediate response. The advantage of forces is that regardless of what forces you apply to the bodies in the scene, the simulation will be able to keep all the defined constraints (joints and contacts) satisfied. For example gravity works by applying a force to bodies.

Unfortunately applying large forces to articulated bodies at the resonant frequency of a system may lead to ever increasing velocities, and eventually to the failure of the solver to maintain the joint constraints. This is not unlike a real world system, where the joints would ultimately break.

The forces acting on a body are accumulated before each simulation frame, applied to the simulation, and then reset to zero in preparation for the next frame. The relevant methods of PxRigidBody and PxRigidBodyExt are listed below. Please refer to the API reference for more detail:

void PxRigidBody::addForce(const PxVec3& force, PxForceMode::Enum mode, bool autowake);
void PxRigidBody::addTorque(const PxVec3& torque, PxForceMode::Enum mode, bool autowake);

void PxRigidBodyExt::addForceAtPos(PxRigidBody& body, const PxVec3& force, const PxVec3& pos, PxForceMode::Enum mode, bool wakeup);
void PxRigidBodyExt::addForceAtLocalPos(PxRigidBody& body, const PxVec3& force, const PxVec3& pos, PxForceMode::Enum mode, bool wakeup);
void PxRigidBodyExt::addLocalForceAtPos(PxRigidBody& body, const PxVec3& force, const PxVec3& pos, PxForceMode::Enum mode, bool wakeup);
void PxRigidBodyExt::addLocalForceAtLocalPos(PxRigidBody& body, const PxVec3& force, const PxVec3& pos, PxForceMode::Enum mode, bool wakeup);

The PxForceMode member defaults to PxForceMode::eFORCE to apply simple forces. There are other possibilities. For example PxForceMode::eIMPULSE will apply an impulsive force. PxForceMode::eVELOCITY_CHANGE will do the same, but also ignore the mass of the body, effectively leading to an instantaneous velocity change. See the API documentation of PxForceMode for the other possibilities.

Note

The methods in PxRigidBodyExt support only the force modes eFORCE and eIMPULSE.

There are further extension functions that compute the linear and angular velocity changes that would arise in the next simulation frame if an impulsive force or impulsive torque were to be applied:

void PxRigidBodyExt::computeVelocityDeltaFromImpulse(const PxRigidBody& body, const PxVec3& impulsiveForce, const PxVec3& impulsiveTorque, PxVec3& deltaLinearVelocity, PxVec3& deltaAngularVelocity);

A use case for this function might be to predict an updated velocity for a game oject so that asset loading may be initiated in advance of the simulation frame if the body is likely to exceed a threshold velocity at the end of the frame. The impulsive force and torque are simply the force and torque that are to be applied to the body multiplied by the timestep of the simulation frame. Neglecting the effect of constraint and contact forces, the change in linear and angular velocity that are expected to arise in the next simulation frame are returned in deltaLinearVelocity and deltaAngularVelocity. The predicted linear velocity can then be computed with body.getLinearVelocity() + deltaLinearVelocity, while the predicted angular velocity can be computed wtih body.getAngularVelocity() + deltaAngularVelocity. If required, it is possible to immediately update the velocity of the body using body.setLinearVelocity(body.getLinearVelocity() + deltaLinearVelocity) and body.setAngularVelocity(body.getAngularVelocity() + deltaAngularVelocity).

Gravity

Gravity is such a common force in simulations that PhysX makes it particularly simple to apply. For a scene-wide gravity effect, or any other uniform force field, set the PxScene class' gravity vector using PxScene::setGravity().

The parameter is the acceleration due to gravity. In meters and seconds, this works out to have a magnitude of about 9.8 on earth, and should point downwards. The force that will be applied at the center of mass of each body in the scene is this acceleration vector times the actor's mass.

Certain special effects can require that some dynamic actors are not influenced by gravity. To specify this set the flag:

PxActor::setActorFlag(PxActorFlag::eDISABLE_GRAVITY, true);

Note

Be careful when changing gravity (or enabling/disabling it) during the simulation. For performance reasons the change will not wake up sleeping actors automatically. Thus it may be necessary to iterate through all actors and call PxRigidDynamic::wakeUp() manually.

An alternative to PxActorFlag::eDISABLE_GRAVITY is to use a zero gravity vector for the whole scene, then apply your own gravity force to rigid bodies, each frame. This can be used to create radial gravity fields, as demonstrated in SampleCustomGravity.

Setting the Velocity

To immediately get a body moving in a certain direction, set its velocity:

void PxRigidBody::setLinearVelocity(const PxVec3& linVel, bool autowake);
void PxRigidBody::setAngularVelocity(const PxVec3& angVel, bool autowake);

During simulation, PhysX will modify the velocity of an object in accordance with gravity and other applied forces, and override the velocity if it is in conflict with a joint or a collision constraint. For example, if a ball is resting on a table and has a downward velocity, this will be clamped to 0. If you set the velocity of a chain link that is jointed to a bunch of other chain links, that velocity will be diminished, and the other chain links' velocity will be increased, to permit the chain to stay together.

Kinematic Actors

Sometimes controlling an actor using forces or constraints is not sufficiently robust, precise or flexible. For example moving platforms or character controllers often need to manipulate an actor's position or have it exactly follow a specific path. Such a control scheme is provided by kinematic actors.

A kinematic actor is controlled using the PxRigidDynamic::setKinematicTarget() function. Each simulation step PhysX moves the actor to its target position, regardless of external forces, gravity, collision, etc. Thus one must continually call setKinematicTarget(), every time step, for each kinematic actor, to make them move along their desired paths. The movement of a kinematic actor affects dynamic actors with which it collides or to which it is constrained with a joint. The actor will appear to have infinite mass and will push regular dynamic actors out of the way.

To create a kinematic actor, simply create a regular dynamic actor then set its kinematic flag:

PxRigidBody::setRigidBodyFlag(PxRigidBodyFlag::eKINEMATIC, true);

Use the same function to transform a kinematic actor back to a regular dynamic actor. While you do need to provide a mass for the kinematic actor as for all dynamic actors, this mass will not actually be used for anything while the actor is in kinematic mode.

Caveats:

  • It is important to understand the difference between PxRigidDynamic::setKinematicTarget() and PxRigidActor::setGlobalPose() here. While setGlobalPose() would also move the actor to the desired position, it would not make that actor properly interact with other objects. In particular, with setGlobalPose() the kinematic actor would not push away other dynamic actors in its path, instead it would go right through them. The setGlobalPose() function can still be used though, if one simply wants to teleport a kinematic actor to a new position.
  • A kinematic actor can push away dynamic objects, but nothing pushes it back. As a result, a kinematic can easily squish a dynamic actor against a static actor, or against another kinematic actor. As a result, the squished dynamic object can deeply penetrate the geometry it has been pushed into.
  • There is no interaction or collision between kinematic actors and static actors. However, it is possible to request contact information for these cases if PxSceneFlag::eENABLE_KINEMATIC_PAIRS or ::eENABLE_KINEMATIC_STATIC_PAIRS gets set.

Sleeping

When an actor does not move for a period of time, it is assumed that it will not move in the future either until some external force acts on it that throws it out of equilibrium. Until then it is no longer simulated in order to save resources. This state is called sleeping. You can query an actor's sleep state with the following method:

bool PxRigidDynamic::isSleeping() const;

It is however often more convenient to listen for events that the SDK sends when actors fall asleep or wake up. To receive the following events, PxActorFlag::eSEND_SLEEP_NOTIFIES must be set for the actor:

void PxSimulationEventCallback::onWake(PxActor** actors, PxU32 count) = 0;
void PxSimulationEventCallback::onSleep(PxActor** actors, PxU32 count) = 0;

See the section Callbacks and Customization for more information.

An actor goes to sleep when its kinetic energy is below a given threshold for a certain time. Basically, every dynamic rigid actor has a wake counter which gets decremented by the simulation time step when the kinetic energy of the actor is below the specified threshold. However, if the energy is above the threshold after a simulation step, the counter gets reset to a minimum default value and the whole process starts anew. Once the wake counter reaches zero, it does not get decremented any further and the actor is ready to go to sleep. Please note that a zero wake counter does not mean that the actor has to be asleep, it only indicates that it is ready to go to sleep. There are other factors that might keep an actor awake for a while longer.

The energy threshold as well as the minimum amount of time an actor will stay awake can be manipulated using the following methods:

void PxRigidDynamic::setSleepThreshold(PxReal threshold);
PxReal PxRigidDynamic::getSleepThreshold() const;

void PxRigidDynamic::setWakeCounter(PxReal wakeCounterValue);
PxReal PxRigidDynamic::getWakeCounter() const;

Note

For kinematic actors, special sleep rules apply. A kinematic actor is asleep unless a target pose has been set (in which case it will stay awake until the end of the next simulation step where no target pose has been set anymore). As a consequence, it is not allowed to use setWakeCounter() for kinematic actors. The wake counter of a kinematic actor is solely defined based on whether a target pose has been set.

If a dynamic rigid actor is sleeping, the following state is guaranteed:

  • The wake counter is zero.
  • The linear and angular velocity is zero.
  • There is no force update pending.

When an actor gets inserted into a scene, it will be considered asleep if all the points above hold, else it will be treated as awake.

In general, a dynamic rigid actor is guaranteed to be awake if at least one of the following holds:

  • The wake counter is positive.
  • The linear or angular velocity is non-zero.
  • A non-zero force or torque has been applied.

As a consequence, the following calls will wake the actor up automatically:

  • PxRigidDynamic::setWakeCounter(), if the wake counter value is larger than zero.
  • PxRigidBody::setLinearVelocity(), ::setAngularVelocity(), if the velocity is non-zero.
  • PxRigidBody::addForce(), ::addTorque(), if the torque is non-zero.

In addition, the following calls and events wake an actor up:

  • PxRigidDynamic::setKinematicTarget() in the case of a kinematic actor (because this also sets the wake counter to a positive value).
  • PxRigidActor::setGlobalPose(), if the autowake parameter is set to true (default).
  • Simulation gets disabled for a PxRigidActor by raising PxActorFlag::eDISABLE_SIMULATION.
  • PxScene::resetFiltering().
  • PxShape::setSimulationFilterData(), if the subsequent re-filtering causes the type of the shape pair to transition between suppressed, trigger and contact.
  • Touch with an actor that is awake.
  • A touching rigid actor gets removed from the scene (this is the default behavior but it can be specified by the user, see note further below).
  • Contact with a static rigid actor is lost.
  • Contact with a dynamic rigid actor is lost and this actor is awake in the next simulation step.
  • The actor gets hit by a two-way interaction particle.

Note

When removing a rigid actor from the scene or a shape from an actor, it is possible to specify whether to wake up the objects that were touching the removed object in the previous simulation step. See the API comments in PxScene::removeActor() and PxRigidActor::detachShape() for details.

To explicitly wake up a sleeping object, or force an object to sleep, use:

void PxRigidDynamic::wakeUp();
void PxRigidDynamic::putToSleep();

Note

It is not allowed to use these methods for kinematic actors. The sleep state of a kinematic actor is solely defined based on whether a target pose has been set.

The API reference documents exactly which methods cause an actor to be woken up.

Solver Accuracy

When the motion of a rigid body is constrained either by contacts or joints, the constraint solver comes into play. The solver satisfies the constraints on the bodies by iterating over all the constraints restricting the motion of the body a certain number of times. The more iterations, the more accurate the results become. The solver iteration count defaults to 4 position iterations and 1 velocity iteration. Those counts may be set individually for each body using the following function:

void PxRigidDynamic::setSolverIterationCounts(PxU32 minPositionIters, PxU32 minVelocityIters);

Typically it is only necessary to significantly increase these values for objects with lots of joints and a small tolerance for joint error. If you find a need to use a setting higher than 30, you may wish to reconsider the configuration of your simulation.

The solver groups contacts into friction patches; friction patches are groups of contacts which share the same materials and have similar contact normals. However, the solver permits a maximum of 32 friction patches per contact manager (pair of shapes). If more than 32 friction patches are produced, which may be due to very complex collision geometry or very large contact offsets, the solver will ignore the remaining friction patches. A warning will be issues in checked/debug builds when this happens.

Fast Rotation

Objects shaped like a pencil are difficult to simulate because they can store a lot of energy while rotating around a short axis, which is then converted to a very high rotational velocity when they start to rotate around a longer axis. High rotational velocities can lead to problems because certain linear approximations of the rotational motion fail to hold. For this reason the SDK automatically limits the rotational velocity of a body to a user definable maximum value. Because this may prevent intentional fast rotation in objects such as wheels, the user can override it on an per body basis:

void PxRigidDynamic::setMaxAngularVelocity(PxReal maxAngVel);

Mass Properties

A dynamic actor needs mass properties: the mass, moment of inertia, and the center of mass frame which specifies the position of the actor's center of mass and its principal inertia axes. The easiest way to calculate mass properties is to use the PxRigidBodyExt::updateMassAndInertia() helper function, which will set all three properties based on the actor's shapes and a uniform density value. Variants of this function allow combinations of per-shape densites and manual specification of some mass properties. See the reference for PxRigidBodyExt for more details.

The Wobbly Snowmen in the North Pole Sample illustrate the use of different mass properties. The snowmen act like roly-poly toys, which are usually just an empty shell with the bottom filled with some heavy material. The low centers of mass cause them to move back to an upright position after they have been tilted. They come in different flavors, depending on how the mass properties are set:

The first is basically massless. There is just a little sphere with a relatively high mass at the bottom of the Actor. This results in a quite rapid movement due to the small resulting moments of inertia. The snowman feels light.

The second uses the mass of the bottom snowball only, resulting in a bigger inertia. Later on, the center of mass is moved to the bottom of the actor. This approximation is by no means physically correct, but the resulting snowman feels a bit more filled.

The third and fourth snowman use shapes to calculate the mass. The difference is that one calculates the moments of inertia first (from the real center of mass) and then the center of mass is moved to the bottom. The other calculates the moments of inertia about the low center of mass that we pass to the calculation routine. Note how much slower the wobbling is for the second case although both have the same mass. This is because the head accounts for much more in the moment of inertia (the distance from the center of mass squared).

The last snowman's mass properties are set up manually. The sample uses rough values for the moment of inertia to create a specific desired behavior. The diagonal tensor has a low value in X, and high values in Y and Z, producing a low resistance to rotation around the X-axis and high resistance around Y and Z. As a consequence, the snowman will wobble back and forth only around the X axis.

If you have a 3x3 inertia matrix (for example, you have real-life inertia tensors for your objects) use the PxDiagonalize() function to obtain principal axes and diagonal inertia tensors to initialize PxRigidDynamic actors.

Damping

The rates at which rigid bodies dissipate angular and linear momentum are governed by damping rates. In the PhysX SDK two damping rates may be specified for a rigid body:

void PxRigidDynamic::setLinearDamping(PxReal linDamp);
void PxRigidDynamic::setAngularDamping(PxReal angDamp);

With a linear damping value of linDamp a rigid body will experience a damping force each update equal to -linDamp*velocity. Similarly, a rigid body with damping rate angDamp will experience a damping torque -angDamp*angularVelocity. The damping forces and torques always act against the velocity and angular velocity and are applied in a way that ensures that a moving and rotating rigid body will asymptotically approach the rest state in the absence of other forces.

Table Of Contents

Previous topic

Joints

Next topic

Data Access and Buffering