|
@@ -1,10 +1,10 @@
|
|
|
[TOC]
|
|
|
|
|
|
-# Architecture of Jolt Physics
|
|
|
+# Architecture of Jolt Physics {#architecture-jolt-physics}
|
|
|
|
|
|
For demos and videos go to the [Samples](Samples.md) section.
|
|
|
|
|
|
-## Bodies
|
|
|
+## Bodies {#bodies}
|
|
|
|
|
|
We use a pretty traditional physics engine setup. We have rigid bodies ([Body](@ref Body)) that have attached collision volumes ([Shape](@ref Shape)). Bodies can either be static (not simulating), dynamic (moved by forces) or kinematic (moved by velocities only). Each moving body has a [MotionProperties](@ref MotionProperties) object that contains information about the movement of the object. Static bodies do not have this to save space (but they can be configured to have it if a static body needs to become dynamic during its lifetime by setting [BodyCreationSettings::mAllowDynamicOrKinematic](@ref BodyCreationSettings::mAllowDynamicOrKinematic)).
|
|
|
|
|
@@ -33,7 +33,7 @@ You cannot use BodyLockRead to lock multiple bodies (if two threads lock the sam
|
|
|
|
|
|
Note that a lot of convenience functions are exposed through the BodyInterface, but not all functionality is available, so you may need to lock the body to get the pointer and then call the function directly on the body.
|
|
|
|
|
|
-### Body Pointers vs Body ID's
|
|
|
+### Body Pointers vs Body ID's {#body-pointers-vs-body-ids}
|
|
|
|
|
|
If you're only accessing the physics system from a single thread, you can use Body pointers instead of BodyID's. In this case you can also use the non-locking variant of the body interface.
|
|
|
|
|
@@ -46,7 +46,7 @@ Note that there are still some restrictions:
|
|
|
|
|
|
If you are accessing the physics system from multiple threads, you should probably use BodyID's and the locking variant of the body interface. It is however still possible to use Body pointers if you're really careful. E.g. if there is a clear owner of a Body and you ensure that this owner does not read/write state during PhysicsSystem::Update or while other threads are reading the Body there will not be any race conditions.
|
|
|
|
|
|
-## Shapes
|
|
|
+## Shapes {#shapes}
|
|
|
|
|
|
Each body has a shape attached that determines the collision volume. The following shapes are available (in order of computational complexity):
|
|
|
|
|
@@ -67,7 +67,7 @@ Next to this there are a number of decorator shapes that change the behavior of
|
|
|
* [RotatedTranslatedShape](@ref RotatedTranslatedShape) - This shape can rotate and translate a child shape, it can e.g. be used to offset a sphere from the origin.
|
|
|
* [OffsetCenterOfMassShape](@ref OffsetCenterOfMassShape) - This shape does not change its child shape but it does shift the calculated center of mass for that shape. It allows you to e.g. shift the center of mass of a vehicle down to improve its handling.
|
|
|
|
|
|
-### Creating Shapes
|
|
|
+### Creating Shapes {#creating-shapes}
|
|
|
|
|
|
Simple shapes like spheres and boxes can be constructed immediately by simply new-ing them. Other shapes need to be converted into an optimized format in order to be usable in the physics simulation. The uncooked data is usually stored in a [ShapeSettings](@ref ShapeSettings) object and then converted to cooked format by a [Create](@ref ShapeSettings::Create) function that returns a [Result](@ref Result) object that indicates success or failure and provides the cooked object.
|
|
|
|
|
@@ -94,7 +94,7 @@ Creating a convex hull for example looks like:
|
|
|
|
|
|
Note that after you call Create, the shape is cached and ShapeSettings keeps a reference to your shape. If you call Create again, the same shape will be returned regardless of what changed to the settings object.
|
|
|
|
|
|
-### Saving Shapes
|
|
|
+### Saving Shapes {#saving-shapes}
|
|
|
|
|
|
There are two ways of serializing data:
|
|
|
|
|
@@ -135,7 +135,7 @@ An example of saving a shape in binary format:
|
|
|
|
|
|
As the library does not offer an exporter from content creation packages and since most games will have their own content pipeline, we encourage you to store data in your own format, cook data while cooking the game data and store the result using the SaveBinaryState interface (and provide a way to force a re-cook when the library is updated).
|
|
|
|
|
|
-### Convex Radius
|
|
|
+### Convex Radius {#convex-radius}
|
|
|
|
|
|
In order to speed up the collision detection system, all convex shapes use a convex radius. The provided shape will first be shrunken by the convex radius and then inflated again by the same amount, resulting in a rounded off shape:
|
|
|
|
|
@@ -143,7 +143,7 @@ In order to speed up the collision detection system, all convex shapes use a con
|
|
|
|:-|
|
|
|
|*In this example a box (green) was created with a fairly large convex radius. The shape is shrunken first (dashed green line) and then inflated again equally on all sides. The resulting shape as seen by the collision detection system is shown in blue. A larger convex radius results in better performance but a less accurate simulation. A convex radius of 0 is allowed.*|
|
|
|
|
|
|
-### Center of Mass
|
|
|
+### Center of Mass {#center-of-mass}
|
|
|
|
|
|
__Beware: When a shape is created, it will automatically recenter itself around its center of mass.__ The center of mass can be obtained by calling [Shape::GetCenterOfMass](@ref Shape::GetCenterOfMass) and most functions operate in this Center of Mass (COM) space. Some functions work in the original space the shape was created in, they usually have World Space (WS) or Shape Space (SS) in their name (or documentation).
|
|
|
|
|
@@ -188,7 +188,7 @@ will return a box of size 2x2x2 centered around the origin, so in order to get i
|
|
|
|
|
|
Note that when you work with interface of [BroadPhaseQuery](@ref BroadPhaseQuery), [NarrowPhaseQuery](@ref NarrowPhaseQuery) or [TransformedShape](@ref TransformedShape) this transformation is done for you.
|
|
|
|
|
|
-### Creating Custom Shapes
|
|
|
+### Creating Custom Shapes {#creating-custom-shapes}
|
|
|
|
|
|
If the defined Shape classes are not sufficient, or if your application can make a more efficient implementation because it has specific domain knowledge, it is possible to create a custom collision shape:
|
|
|
|
|
@@ -206,7 +206,7 @@ If the defined Shape classes are not sufficient, or if your application can make
|
|
|
* If your shape is triangle based, you can forward the testing of a shape vs a single triangle to the [CollideConvexVsTriangles](@ref CollideConvexVsTriangles) and [CastConvexVsTriangles](@ref CastConvexVsTriangles) classes.
|
|
|
* If your shape contains sub shapes and you have determined that the shape intersects with one of the sub shapes you can forward the sub shape to the collision dispatch again through [CollisionDispatch::sCollideShapeVsShape](@ref CollisionDispatch::sCollideShapeVsShape) and [CollisionDispatch::sCastShapeVsShapeLocalSpace](@ref CollisionDispatch::sCastShapeVsShapeLocalSpace).
|
|
|
|
|
|
-## Sensors
|
|
|
+## Sensors {#sensors}
|
|
|
|
|
|
Sensors are normal rigid bodies that report contacts with other Dynamic or Kinematic bodies through the [ContactListener](@ref ContactListener) interface. Any detected penetrations will however not be resolved. Sensors can be used to implement triggers that detect when an object enters their area.
|
|
|
|
|
@@ -218,7 +218,7 @@ To create a sensor, either set [BodyCreationSettings::mIsSensor](@ref BodyCreati
|
|
|
|
|
|
To make sensors detect collisions with static objects, set the [BodyCreationSettings::mCollideKinematicVsNonDynamic](@ref BodyCreationSettings::mCollideKinematicVsNonDynamic) to true or call [Body::SetCollideKinematicVsNonDynamic](@ref Body::SetCollideKinematicVsNonDynamic). Note that it can place a large burden on the collision detection system if you have a large sensor intersect with e.g. a large mesh terrain or a height field as you will get many contact callbacks and these contacts will take up a lot of space in the contact cache. Ensure that your sensor is in an object layer that collides with as few static bodies as possible.
|
|
|
|
|
|
-## Constraints
|
|
|
+## Constraints {#constraints}
|
|
|
|
|
|
Bodies can be connected to each other using constraints ([Constraint](@ref Constraint)).
|
|
|
|
|
@@ -246,7 +246,7 @@ Adding and removing constraints can be done from multiple threads, but the const
|
|
|
|
|
|
Contact constraints (when bodies collide) are not handled through the [Constraint](@ref Constraint) class but through the [ContactConstraintManager](@ref ContactConstraintManager) which is considered an internal class.
|
|
|
|
|
|
-### Constraint Motors
|
|
|
+### Constraint Motors {#constraint-motors}
|
|
|
|
|
|
Most of the constraints support motors (see [MotorSettings](@ref MotorSettings)) which allow you to apply forces/torques on two constrained bodies to drive them to a relative position/orientation. There are two types of motors:
|
|
|
* Linear motors: These motors drive the relative position between two bodies. A linear motor would, for example, slide a body along a straight line when you use a slider constraint.
|
|
@@ -285,11 +285,11 @@ Sensible values for damping are [0, 1] but higher values are also possible. When
|
|
|
|
|
|
Because Jolt Physics uses a Symplectic Euler integrator, there will still be a small amount of damping when damping is 0, so you cannot get infinite oscillation (allowing this would make it very likely for the system to become unstable).
|
|
|
|
|
|
-### Breakable Constraints
|
|
|
+### Breakable Constraints {#breakable-constraints}
|
|
|
|
|
|
Constraints can be turned on / off by calling Constraint::SetEnabled. After every simulation step, check the total 'lambda' applied on each constraint and disable the constraint if the value goes over a certain threshold. Use e.g. SliderConstraint::GetTotalLambdaPosition / HingeConstraint::GetTotalLambdaRotation. You can see 'lambda' as the linear/angular impulse applied at the constraint in the last physics step to keep the constraint together.
|
|
|
|
|
|
-## Soft Bodies
|
|
|
+## Soft Bodies {#soft-bodies}
|
|
|
|
|
|
Soft bodies (also known as deformable bodies) can be used to create e.g. a soft ball or a piece of cloth. They are created in a very similar way to normal rigid bodies:
|
|
|
|
|
@@ -310,9 +310,9 @@ Soft bodies are currently in development, please note the following:
|
|
|
* Constraints cannot operate on soft bodies, set the inverse mass of a particle to zero and move it by setting a velocity to constrain a soft body to something else.
|
|
|
* When calculating friction / restitution an empty SubShapeID will be passed to the ContactConstraintManager::CombineFunction because this is called once per body pair rather than once per sub shape as is common for rigid bodies.
|
|
|
|
|
|
-## Collision Detection
|
|
|
+## Collision Detection {#collision-detection}
|
|
|
|
|
|
-### Broad Phase
|
|
|
+### Broad Phase {#broad-phase}
|
|
|
|
|
|
When bodies are added to the PhysicsSystem, they are inserted in the broad phase ([BroadPhaseQuadTree](@ref BroadPhaseQuadTree)). This provides quick coarse collision detection based on the axis aligned bounding box (AABB) of a body.
|
|
|
|
|
@@ -320,9 +320,9 @@ The broad phase is divided in layers (BroadPhaseLayer), each layer has an AABB q
|
|
|
|
|
|
Since we want to access bodies concurrently the broad phase has special behavior. When a body moves, all nodes in the AABB tree from root to the node where the body resides will be expanded using a lock-free approach. This way multiple threads can move bodies at the same time without requiring a lock on the broad phase. Nodes that have been expanded are marked and during the next physics step a new tight-fitting tree will be built in the background while the physics step is running. This new tree will replace the old tree before the end of the simulation step. This is possible since no bodies can be added/removed during the physics step.
|
|
|
|
|
|
-When doing a query against the broad phase ([BroadPhaseQuery](@ref BroadPhaseQuery)), you generally will get a body ID for intersecting objects. If a collision query takes a long time to process the resulting bodies (e.g. across multiple simulation steps), you can safely keep using the body ID's as specified in the "Bodies" section.
|
|
|
+When doing a query against the broad phase ([BroadPhaseQuery](@ref BroadPhaseQuery)), you generally will get a body ID for intersecting objects. If a collision query takes a long time to process the resulting bodies (e.g. across multiple simulation steps), you can safely keep using the body ID's as specified in the @ref bodies section.
|
|
|
|
|
|
-### Narrow Phase
|
|
|
+### Narrow Phase {#narrow-phase}
|
|
|
|
|
|
A narrow phase query ([NarrowPhaseQuery](@ref NarrowPhaseQuery)) will first query the broad phase for intersecting bodies and will under the protection of a body lock construct a transformed shape ([TransformedShape](@ref TransformedShape)) object. This object contains the transform, a reference counted shape and a body ID. Since the shape will not be deleted until you destroy the TransformedShape object, it is a consistent snapshot of the collision information of the body. This ensures that the body is only locked for a short time frame and makes it possible to do the bulk of the collision detection work outside the protection of a lock.
|
|
|
|
|
@@ -330,11 +330,11 @@ For very long running jobs (e.g. navigation mesh creation) it is possible to que
|
|
|
|
|
|
The narrow phase queries are all handled through the [GJK](@ref GJKClosestPoint) and [EPA](@ref EPAPenetrationDepth) algorithms.
|
|
|
|
|
|
-### Collision filtering
|
|
|
+### Collision Filtering {#collision-filtering}
|
|
|
|
|
|
-As touched upon in the Broad Phase section, there are various collision filtering functions:
|
|
|
+As touched upon in the @ref broad-phase section, there are various collision filtering functions:
|
|
|
|
|
|
-* Broadphase layer: Each broad phase layer will result in it's own quad tree so you should not have too many of them. See the Broad Phase section for more information. At this stage, the object layer is tested against the broad phase trees that are relevant by checking the [ObjectVsBroadPhaseLayerFilter](@ref ObjectVsBroadPhaseLayerFilter).
|
|
|
+* Broadphase layer: Each broad phase layer will result in it's own quad tree so you should not have too many of them. See the @ref broad-phase section for more information. At this stage, the object layer is tested against the broad phase trees that are relevant by checking the [ObjectVsBroadPhaseLayerFilter](@ref ObjectVsBroadPhaseLayerFilter).
|
|
|
* Object layer: Once the broad phase layer test succeeds, we will test object layers vs object layers through [ObjectLayerPairFilter](@ref ObjectLayerPairFilter) (used for simulation) and [ObjectLayerFilter](@ref ObjectLayerFilter) (used for collision queries).
|
|
|
* Group filter: Most expensive filtering (bounding boxes already overlap), used only during simulation. Allows you fine tune collision e.g. by discarding collisions between bodies connected by a constraint. See [GroupFilter](@ref GroupFilter) and implementation for ragdolls [GroupFilterTable](@ref GroupFilterTable).
|
|
|
* Body filter: This filter is used instead of the group filter if you do collision queries like CastRay. See [BodyFilter](@ref BodyFilter).
|
|
@@ -346,7 +346,7 @@ For convenience two filtering implementations are provided:
|
|
|
* ObjectLayerPairFilterTable, ObjectVsBroadPhaseLayerFilterTable and BroadPhaseLayerInterfaceTable: These three implement collision layers as a simple table. You construct ObjectLayerPairFilterTable with a fixed number of object layers and then call ObjectLayerPairFilterTable::EnableCollision or ObjectLayerPairFilterTable::DisableCollision to selectively enable or disable collisions between layers. BroadPhaseLayerInterfaceTable is constructed with a number of broad phase layers. You can then map each object layer to a broad phase layer through BroadPhaseLayerInterfaceTable::MapObjectToBroadPhaseLayer.
|
|
|
* ObjectLayerPairFilterMask, ObjectVsBroadPhaseLayerFilterMask and BroadPhaseLayerInterfaceMask: These split an ObjectLayer in an equal amount of bits for group and mask. Two objects collide if (object1.group & object2.mask) != 0 && (object2.group & object1.mask) != 0. This behavior is similar to e.g. Bullet. In order to map groups to broad phase layers, you call BroadPhaseLayerInterfaceMask::ConfigureLayer for each broad phase layer. You determine which groups can be put in that layer and which group must be excluded from that layer. E.g. a broad phase layer could include everything that has the STATIC group but should exclude everything that has the SENSOR group, so that if an object has both STATIC and SENSOR bits set, this broad phase layer will not be used. The broad phase layers are checked one by one and the first one that meets the condition is the one that the body will be put in. If you use this implementation, consider setting the cmake option OBJECT_LAYER_BITS to 32 to get a 32-bit ObjectLayer instead of a 16-bit one.
|
|
|
|
|
|
-### Continuous Collision Detection
|
|
|
+### Continuous Collision Detection {#continous-collision-detection}
|
|
|
|
|
|
Each body has a motion quality setting ([EMotionQuality](@ref EMotionQuality)). By default the motion quality is [Discrete](@ref Discrete). This means that at the beginning of each simulation step we will perform collision detection and if no collision is found, the body is free to move according to its velocity. This usually works fine for big or slow moving objects. Fast and small objects can easily 'tunnel' through thin objects because they can completely move through them in a single time step. For these objects there is the motion quality [LinearCast](@ref LinearCast). Objects that have this motion quality setting will do the same collision detection at the beginning of the simulation step, but once their new position is known, they will do an additional CastShape to check for any collisions that may have been missed. If this is the case, the object is placed back to where the collision occurred and will remain there until the next time step. This is called 'time stealing' and has the disadvantage that an object may appear to move much slower for a single time step and then speed up again. The alternative, back stepping the entire simulation, is computationally heavy so was not implemented.
|
|
|
|
|
@@ -360,7 +360,7 @@ Fast rotating long objects are also to be avoided, as the LinearCast motion qual
|
|
|
|:-|
|
|
|
|*Even with the LinearCast motion quality the blue object rotates through the green object in a single time step.*|
|
|
|
|
|
|
-## The Simulation Step
|
|
|
+## The Simulation Step {#the-simulation-step}
|
|
|
|
|
|
The simulation step [PhysicsSystem::Update](@ref PhysicsSystem::Update) uses jobs ([JobSystem](@ref JobSystem)) to perform the needed work. This allows spreading the workload across multiple CPU's. We use a Sequential Impulse solver with warm starting as described in [Modeling and Solving Constraints - Erin Catto](https://box2d.org/files/ErinCatto_ModelingAndSolvingConstraints_GDC2009.pdf)
|
|
|
|
|
@@ -373,7 +373,7 @@ Each physics step can be divided into multiple collision steps. So if you run th
|
|
|
|
|
|
In general, the system is stable when running at 60 Hz with 1 collision step.
|
|
|
|
|
|
-## Conventions and limits
|
|
|
+## Conventions and Limits {#conventions-and-limits}
|
|
|
|
|
|
Jolt Physics uses a right handed coordinate system with Y-up. It is easy to use another axis as up axis by changing the gravity vector using [PhysicsSystem::SetGravity](@ref PhysicsSystem::SetGravity). Some shapes like the [HeightFieldShape](@ref HeightFieldShapeSettings) will need an additional [RotatedTranslatedShape](@ref RotatedTranslatedShapeSettings) to rotate it to the new up axis and vehicles ([VehicleConstraint](@ref VehicleConstraintSettings)) and characters ([CharacterBaseSettings](@ref CharacterBaseSettings)) will need their new up-axis specified too.
|
|
|
|
|
@@ -381,7 +381,7 @@ We use column-major vectors and matrices, this means that to transform a point y
|
|
|
|
|
|
Note that the physics simulation works best if you use SI units (meters, radians, seconds, kg). In order for the simluation to be accurate, dynamic objects should be in the order [0.1, 10] meters long and have speeds in the order of [0, 500] m/s. Static object should be in the order [0.1, 2000] meter long. If you are using different units, consider scaling the objects before passing them on to the physics simulation.
|
|
|
|
|
|
-## Big Worlds
|
|
|
+## Big Worlds {#big-worlds}
|
|
|
|
|
|
By default the library compiles using floats. This means that the simulation gets less accurate the further you go from the origin. If all simulation takes place within roughly 5 km from the origin, floating point precision is accurate enough.
|
|
|
|
|
@@ -391,13 +391,13 @@ Calculations with doubles are much slower than calculations with floats. A naive
|
|
|
|
|
|
Keep in mind that:
|
|
|
|
|
|
-* There are a lot of 'epsilons' in the code that have been tuned for objects of sizes/speeds as described in the previous section. Try to keep the individual objects to the specified scale even if they're really far from the origin.
|
|
|
+* There are a lot of 'epsilons' in the code that have been tuned for objects of sizes/speeds as described in the @ref conventions-and-limits section. Try to keep the individual objects to the specified scale even if they're really far from the origin.
|
|
|
* When the collision results of a single query are kilometers apart, precision will suffer as they will be far away from the 'base offset'.
|
|
|
* The effectiveness of the broad phase (which works in floats) will become less at large distances from the origin, e.g. at 10000 km from the origin, the resolution of the broad phase is reduced to 1 m which means that everything that's closer than 1 m will be considered colliding. This will not impact the quality of the simulation but it will result in extra collision tests in the narrow phase so will hurt performance.
|
|
|
|
|
|
Because of the minimal use of doubles, the simulation runs 5-10% slower in double precision mode compared to float precision mode.
|
|
|
|
|
|
-## Deterministic Simulation
|
|
|
+## Deterministic Simulation {#deterministic-simulation}
|
|
|
|
|
|
The physics simulation is deterministic provided that:
|
|
|
|
|
@@ -433,13 +433,13 @@ The most important things to look out for in your own application:
|
|
|
|
|
|
When running the Samples Application you can press ESC, Physics Settings and check the 'Check Determinism' checkbox. Before every simulation step we will record the state using the [StateRecorder](@ref StateRecorder) interface, rewind the simulation and do the step again to validate that the simulation runs deterministically. Some of the tests (e.g. the MultiThreaded) test will explicitly disable the check because they randomly add/remove bodies from different threads. This violates the rule that the API calls must be done in the same order so will not result in a deterministic simulation.
|
|
|
|
|
|
-## Rolling Back a Simulation
|
|
|
+## Rolling Back a Simulation {#rolling-back-a-simulation}
|
|
|
|
|
|
When synchronizing two simulations via a network, it is possible that a change that needed to be applied at frame N is received at frame N + M. This will require rolling back the simulation to the state of frame N and repeating the simulation with the new inputs. This can be implemented by saving the physics state using [SaveState](@ref PhysicsSystem::SaveState) at every frame. To roll back, call [RestoreState](@ref PhysicsSystem::RestoreState) with the state at frame N. SaveState only records the state that the physics engine modifies during its update step (positions, velocities etc.), so if you change anything else you need to restore this yourself. E.g. if you did a [SetFriction](@ref Body::SetFriction) on frame N + 2 then, when rewinding, you need to restore the friction to what is was on frame N and update it again on frame N + 2 when you replay. If you start adding/removing objects (e.g. bodies or constraints) during these frames, the RestoreState function will not work. If you added a body on frame N + 1, you'll need to remove it when rewinding and then add it back on frame N + 1 again (with the proper initial position/velocity etc. because it won't be contained in the snapshot at frame N).
|
|
|
|
|
|
If you wish to share saved state between server and client, you need to ensure that all APIs that modify the state of the world are called in the exact same order. So if the client creates physics objects for player 1 then 2 and the server creates the objects for 2 then 1 you already have a problem (the body IDs will be different, which will render the save state snapshots incompatible). When rolling back a simulation, you'll also need to ensure that the BodyIDs are kept the same, so you need to remove/add the body from/to the physics system instead of destroy/re-create them or you need to create bodies with the same ID on both sides using [BodyInterface::CreateBodyWithID](@ref BodyInterface::CreateBodyWithID).
|
|
|
|
|
|
-## Working With Multiple Physics Systems
|
|
|
+## Working With Multiple Physics Systems {#working-with-multiple-physics-systems}
|
|
|
|
|
|
You can create, simulate and interact with multiple PhysicsSystems at the same time provided that you do not share any objects (bodies, constraints) between the systems.
|
|
|
When a Body is created it receives a BodyID that is unique for the PhysicsSystem that it was created for, so it cannot be shared. The only object that can be shared between PhysicsSystems is a Shape.
|
|
@@ -455,7 +455,7 @@ PhysicsSystems are not completely independent:
|
|
|
|
|
|
These functions / systems need to be registered in advance.
|
|
|
|
|
|
-## The Simulation Step in Detail
|
|
|
+## The Simulation Step in Detail {#the-simulation-step-in-detail}
|
|
|
|
|
|
The job graph looks like this:
|
|
|
|
|
@@ -463,33 +463,33 @@ The job graph looks like this:
|
|
|
|
|
|
Note that each job indicates if it reads/writes positions/velocities and if it deactivates/activates bodies. We do not allow jobs to read/write the same data concurrently. The arrows indicate the order in which jobs are executed. Yellow blocks mean that there are multiple jobs of this type. Dotted arrows have special meaning and are explained below.
|
|
|
|
|
|
-### Broad Phase Update Prepare
|
|
|
+### Broad Phase Update Prepare {#broad-phase-update-prepare}
|
|
|
|
|
|
-This job will refit the AABBs of the broad phase. It does this by building a new tree while keeping the old one available as described in the "Broad Phase" section.
|
|
|
+This job will refit the AABBs of the broad phase. It does this by building a new tree while keeping the old one available as described in the @ref broad-phase section.
|
|
|
|
|
|
-### Broad Phase Update Finalize
|
|
|
+### Broad Phase Update Finalize {#broad-phase-update-finalize}
|
|
|
|
|
|
This job will simply swap the new tree with the old tree. The old tree will be discarded at the beginning of the next PhysicsSystem::Update call so that any broad phase query can continue to run.
|
|
|
|
|
|
-### Step Listeners
|
|
|
+### Step Listeners {#step-listeners-update}
|
|
|
|
|
|
You can register one or more step listeners (See [PhysicsSystem::AddStepListener](@ref PhysicsSystem::AddStepListener)). This job will call [PhysicsStepListener::OnStep](@ref PhysicsStepListener::OnStep) for every listener. This can be used to do work that needs to be done at the beginning of each step, e.g. set velocities on ragdoll bodies.
|
|
|
|
|
|
-### Apply Gravity
|
|
|
+### Apply Gravity {#apply-gravity-update}
|
|
|
|
|
|
A number of these jobs run in parallel. Each job takes a batch of active bodies and applies gravity and damping (updating linear and angular velocity).
|
|
|
|
|
|
-### Determine Active Constraints
|
|
|
+### Determine Active Constraints {#determine-active-constraints}
|
|
|
|
|
|
This job will go through all non-contact constraints and determine which constraints are active based on if the bodies that the constraint connects to are active.
|
|
|
|
|
|
-### Build Islands from Constraints
|
|
|
+### Build Islands from Constraints {#build-islands-from-constraints}
|
|
|
|
|
|
This job will go through all non-contact constraints and assign the involved bodies and constraint to the same island. Since we allow concurrent insertion/removal of bodies we do not want to keep island data across multiple simulation steps, so we recreate the islands from scratch every simulation step. The operation is lock-free and O(N) where N is the number of constraints.
|
|
|
|
|
|
If a constraint connects an active and a non-active body, the non-active body is woken up. One find collisions job will not start until this job has finished in order to pick up any collision testing for newly activated bodies.
|
|
|
|
|
|
-### Find Collisions
|
|
|
+### Find Collisions {#find-collisions}
|
|
|
|
|
|
This job will do broad and narrow phase checks. Initially a number of jobs are started based on the amount of active bodies. The job will do the following:
|
|
|
|
|
@@ -506,60 +506,60 @@ The contact points between the two bodies will be determined by the [GJK](@ref G
|
|
|
|
|
|
Multiple contact manifolds with similar normals are merged together (PhysicsSystem::ProcessBodyPair::ReductionCollideShapeCollector). After this the contact constraints are created in the [ContactConstraintManager](@ref ContactConstraintManager) and their Jacobians / effective masses calculated.
|
|
|
|
|
|
-Contacting bodies are also linked together to form islands. This is the same operation as described in the "Build Islands From Constraints" section.
|
|
|
+Contacting bodies are also linked together to form islands. This is the same operation as described in the @ref build-islands-from-constraints section.
|
|
|
|
|
|
The narrow phase makes use of a lock free contact cache. We have 2 caches, one that is used for reading (which contains the contacts from the previous step) and one for writing new contact pairs. When a contact point is preserved from the last simulation step, it will be copied from the read cache to the write cache.
|
|
|
|
|
|
-### Setup Velocity Constraints
|
|
|
+### Setup Velocity Constraints {#setup-velocity-constraints}
|
|
|
|
|
|
This job will go through all non-contact constraints and prepare them for execution. This involves calculating Jacobians and effective masses for each constraint part.
|
|
|
|
|
|
-### Finalize Islands
|
|
|
+### Finalize Islands {#finalize-islands}
|
|
|
|
|
|
This job will finalize the building of the simulation islands. Each island contains bodies that interact with each other through a contact point or through a constraint. These islands will be simulated separately in different jobs later. The finalization of the islands is an O(N) operation where N is the amount of active bodies (see [IslandBuilder::Finalize](@ref IslandBuilder::Finalize)).
|
|
|
|
|
|
-### Set Body Island Idx
|
|
|
+### Set Body Island Idx {#set-body-island-idx}
|
|
|
|
|
|
This job does some housekeeping work that can be executed concurrent to the solver:
|
|
|
|
|
|
* It will assign the island ID to all bodies (which is mainly used for debugging purposes)
|
|
|
|
|
|
-### Solve Velocity Constraints
|
|
|
+### Solve Velocity Constraints {#solve-velocity-constraints}
|
|
|
|
|
|
A number of these jobs will run in parallel. Each job takes the next unprocessed island and will run the iterative constraint solver for that island. It will first apply the impulses applied from the previous simulation step (which are stored in the contact cache) to warm start the solver. It will then repeatedly iterate over all contact and non-contact constraints until either the applied impulses are too small or a max iteration count is reached ([PhysicsSettings::mNumVelocitySteps](@ref PhysicsSettings::mNumVelocitySteps)). The result will be that the new velocities are known for all active bodies. The applied impulses are stored in the contact cache for the next step.
|
|
|
|
|
|
When an island consists of more than LargeIslandSplitter::cLargeIslandTreshold contacts plus constraints it is considered a large island. In order to not do all work on a single thread, this island will be split up by the LargeIslandSplitter. This follows an algorithm described in High-Performance Physical Simulations on Next-Generation Architecture with Many Cores by Chen et al. This is basically a greedy algorithm that tries to group contacts and constraints into groups where no contact or constraint affects the same body. Within a group, the order of execution does not matter since every memory location is only read/written once, so we can parallelize the update. At the end of each group, we need to synchronize the CPU cores before starting on the next group. When the number of groups becomes too large, a final group is created that contains all other contacts and constraints and these are solved on a single thread. The groups are processed PhysicsSettings::mNumVelocitySteps times so the end result is almost the same as an island that was not split up (only the evalutation order changes in a consistent way).
|
|
|
|
|
|
-### Pre Integrate
|
|
|
+### Pre Integrate {#pre-integrate}
|
|
|
|
|
|
This job prepares the CCD buffers.
|
|
|
|
|
|
-### Integrate & Clamp Velocities
|
|
|
+### Integrate & Clamp Velocities {#integrate-and-clamp-velocities}
|
|
|
|
|
|
This job will integrate the velocity and update the position. It will clamp the velocity to the max velocity.
|
|
|
|
|
|
Depending on the motion quality ([EMotionQuality](@ref EMotionQuality)) of the body, it will schedule a body for continuous collision detection (CCD) if its movement is bigger than some treshold based on the [inner radius](@ref Shape::GetInnerRadius)) of the shape.
|
|
|
|
|
|
-### Post Integrate
|
|
|
+### Post Integrate {#post-integrate}
|
|
|
|
|
|
Find CCD Contact jobs are created on the fly depending on how many CCD bodies were found. If there are no CCD bodies it will immediately start Resolve CCD Contacts.
|
|
|
|
|
|
-### Find CCD Contacts
|
|
|
+### Find CCD Contacts {#find-ccd-contacts}
|
|
|
|
|
|
A number of jobs wil run in parallel and pick up bodies that have been scheduled for CCD and will do a linear cast to detect the first collision. It always allows movement of the object by a fraction if its inner radius in order to prevent it from getting fully stuck.
|
|
|
|
|
|
-### Resolve CCD Contacts
|
|
|
+### Resolve CCD Contacts {#resolve-ccd-contacts}
|
|
|
|
|
|
This job will take the collision results from the previous job and update position and velocity of the involved bodies. If an object hits another object, its time will be 'stolen' (it will move less far than it should according to its velocity).
|
|
|
|
|
|
-### Finalize Contact Cache, Contact Removed Callbacks
|
|
|
+### Finalize Contact Cache, Contact Removed Callbacks {#finalize-contact-cache}
|
|
|
|
|
|
This job will:
|
|
|
|
|
|
* Swap the read/write contact cache and prepare the contact cache for the next step.
|
|
|
* It will detect all contacts that existed previous step and do not exist anymore to fire callbacks for them through the [ContactListener](@ref ContactListener) interface.
|
|
|
|
|
|
-### Solve Position Constraints, Update Bodies Broad Phase
|
|
|
+### Solve Position Constraints, Update Bodies Broad Phase {#solve-position-constraints}
|
|
|
|
|
|
A number of these jobs will run in parallel. Each job takes the next unprocessed island and run the position based constraint solver. This fixes numerical drift that may have caused constrained bodies to separate (remember that the constraints are solved in the velocity domain, so errors get introduced when doing a linear integration step). It will run until either the applied position corrections are too small or until the max amount of iterations is reached ([PhysicsSettings::mNumPositionSteps](@ref PhysicsSettings::mNumPositionSteps)). Here there is also support for large islands, the island splits that were calculated in the Solve Velocity Constraints job are reused to solve partial islands in the same way as before.
|
|
|
|
|
@@ -567,18 +567,18 @@ It will also notify the broad phase of the new body positions / AABBs.
|
|
|
|
|
|
When objects move too little the body will be put to sleep. This is detected by taking the biggest two axis of the local space bounding box of the shape together with the center of mass of the shape (all points in world space) and keep track of 3 bounding spheres for those points over time. If the bounding spheres become too big, the bounding spheres are reset and the timer restarted. When the timer reaches a certain time, the object has is considered non-moving and is put to sleep.
|
|
|
|
|
|
-### Soft Body Prepare
|
|
|
+### Soft Body Prepare {#soft-body-prepare}
|
|
|
|
|
|
If there are any active soft bodies, this job will create the Soft Body Collide, Simulate and Finalize Jobs. It will also create a list of sorted SoftBodyUpdateContext objects that forms the context for those jobs.
|
|
|
|
|
|
-### Soft Body Collide
|
|
|
+### Soft Body Collide {#soft-body-collide}
|
|
|
|
|
|
These jobs will do broadphase checks for all of the soft bodies. A thread picks up a single soft body and uses the bounding box of the soft body to find intersecting rigid bodies. Once found, information will be collected about that rigid body so that Simulate can run in parallel.
|
|
|
|
|
|
-### Soft Body Simulate
|
|
|
+### Soft Body Simulate {#soft-body-simulate}
|
|
|
|
|
|
These jobs will do the actual simulation of the soft bodies. They first collide batches of soft body vertices with the rigid bodies found during during the Collide job (multiple threads can work on a single soft body) and then perform the simulation using XPBD (also partially distributing a single soft body on multiple threads).
|
|
|
|
|
|
-### Soft Body Finalize
|
|
|
+### Soft Body Finalize {#soft-body-finalize}
|
|
|
|
|
|
This job writes back all the rigid body velocity changes and updates the positions and velocities of the soft bodies. It can activate/deactivate bodies as needed.
|