Managing collisions
During each simulation step, Bullet performs collision detection in 2 phases: broadphase and narrowphase.
-
Broadphase enumerates pairs of objects in close proximity, based on overlaps between their axis-aligned bounding boxes (AABBs).
-
Narrowphase uses detailed collision shapes to test each pair found during broadphase, resulting in a list of manifolds where the shapes intersect.
Each manifold is composed of up to 4 contact points. Bullet’s rigid-body dynamics uses manifolds and contact points to implement contact response, including friction and restitution.
Libbulletjme provides mechanisms to:
-
count or enumerate overlaps,
-
customize which overlaps are handled and which are ignored,
-
access the manifolds and contact points, and
-
perform custom actions when Bullet creates or destroys a manifold or when it processes a contact point.
It also provides mechanisms:
-
to test for intersection between 2 collision objects or
-
to count or enumerate collision objects that intersect with:
-
a specific line segment,
-
a shape in a specific position, or
-
a shape sweeping from one position to another.
-
Such intersection tests are typically performed between simulation steps. They are useful (for instance) when you want to add a collision object to a space without causing an immediate collision.
Ghosts
A ghost is a collision object without contact response,
created solely to detect overlaps with other collision objects.
You can create a ghost by invoking the
PhysicsGhostObject
constructor.
In order to work, the ghost must be added to a space.
The constructor allows you to specify a collision shape. However, the details of the shape are unimportant. The key property of a ghost is its axis-aligned bounding box (AABB), which is determined by the ghost’s size and position.
The AABBs generated by Bullet aren’t minimal. Approximations in Bullet make them somewhat larger than necessary. However, they are usually accurate enough to be useful. |
HelloGhost
(also in Kotlin) is a SPORT app
that illustrates how a ghost could be used
to detect a character entering/leaving a box-shaped zone.
Things notice while running the app:
-
The ghost has a spherical shape, but its AABB (shown here in yellow) is box-shaped.
-
The character has a capsule shape.
-
To clarify what’s going on, the character’s AABB is outlined in white.
-
Press Right arrow to walk the character toward the ghost.
-
As soon as the character’s AABB overlaps the ghost’s AABB, the ghost turns red, even though their collision shapes haven’t intersected yet.
-
Press Space bar to jump.
-
The character can jump high enough to leave the ghost’s AABB, at which time the ghost will momentarily turn yellow again.
In addition the counting overlapping objects,
you can enumerate them using ghost.getOverlappingObjects()
.
Filtering AABB overlaps
You might want to prevent collisions from occurring between certain objects, perhaps under specific conditions. Filtering out an overlap during broadphase prevents collisions between the overlapping objects. Libbulletjme provides several mechanisms to implement such filtering.
The wheels of a PhysicsVehicle aren’t collision objects,
so these mechanisms don’t affect them.
|
Using collision groups
You can filter overlaps using collision groups. This is the most efficient filtering mechanism.
16 groups are defined,
and each collision object belongs to exactly one group.
By default, that group is COLLISION_GROUP_01
.
To assign an object to a different group,
invoke collisionObject.setCollisionGroup()
.
By default, collision objects collide only with objects in COLLISION_GROUP_01
.
To alter an object’s collides-with set,
invoke collisionObject.setCollideWithGroup()
.
For a collision to occur between objects X and Y, either X must collide with Y’s group OR Y must collide with X’s group.
Using ignore lists
You can filter AABB overlaps on a pair-by-pair basis by creating ignore lists for specific collision objects. Ignore lists are more flexible than collision groups, but less efficient.
Each collision object is created with an empty ignore list.
You can disable collisions between two objects X and Y by invoking
x.addToIgnoreList(y)
.
There’s no need to also invoke y.addToIgnoreList(x) .
That would be redundant.
|
Ignore lists are used internally by physics joints to disable collisions between the ends of a joint. |
Using a dynamic filter
Libbulletjme invokes space.needsCollision()
for each AABB overlap
that isn’t suppressed by collision groups or ignore lists.
The default implementation returns true
, meaning "process this overlap".
You can dynamically filter AABB overlaps
by overriding the needsCollision()
method
during the instantiation of the space.
Return true
for overlaps you want Bullet to handle.
Return false
for overlaps you want Bullet to ignore.
needsCollision()
can also be used
to trigger arbitrary actions when 2 AABBs overlap.
needsCollision()
is the most flexible mechanism for filtering collisions.
Access to manifolds and contact points
The physicsSpace.countManifolds()
method
returns the number of contact manifolds in the space.
Each manifold has a native ID, as does each contact point.
-
The
physicsSpace.listManifoldIds()
method enumerates the IDs of all manifolds in the space. -
To obtain detailed information about a specific manifold, use the methods in
PersistentManifolds
. -
The
PersistentManifolds.listPointIds()
method enumerates the IDs of the contact points in a particular manifold. -
To obtain detailed information about a specific contact point, use the methods in
ManifoldPoints
.
A manifold may persist for a while after the bodies no longer intersect.
To confirm intersection,
use ManifoldPoints.getDistance1() to get the separation distance.
When the distance is positive, there’s no intersection.
|
ConveyorDemo
is a SPORT app
that implements conveyor belts using contact-point modification.
Custom contact handling
The physicsSpace.update()
method has optional arguments
to enable callbacks from Bullet during contact processing:
-
If the
doStarted
flag is true, thenonContactStarted()
will be invoked each time a manifold is created. -
If the
doProcessed
flag is true, thenonContactProcessed()
will be invoked each time a contact point is processed. -
If the
doEnded
flag is true, thenonContactEnded()
will be invoked each time a manifold is destroyed.
By default, doEnded
, doProcessed
, and doStarted
are false
and the callbacks are no-ops.
To customize the callbacks,
override the handlers during the instantiation of the PhysicsSpace
.
A mechanism exists that implements contact handling using listeners. That mechanism is now deprecated. |
Intersection tests
Pair test
The space.pairTest()
method
performs a pair test between 2 collision objects,
returning true
if they intersect.
Although a space is required, the objects needn’t be added to any space. |
You can request a callback for each contact point that would be created if both collision objects were added to the space.
Ray test
The space.rayTest()
method performs a ray test against a space,
returning a list of objects in the space
that intersect with the specified line segment.
Unlike a mathematical ray, the "ray" used in a ray test has both a starting point and an ending point. |
To configure details of how ray tests are performed,
use the space.setRayTestFlags()
method.
Contact test
The contactTest()
method performs a contact test against a space,
returning the number of contact points that would be created
if a specified collision object were added to the space.
To obtain more information about the contacts, you can request a callback for each point.
Contact testing doesn’t detect contacts involving soft bodies. |
Summary
-
Overlaps, intersections, manifolds, and contact points are distinct concepts.
-
Libbulletjme provides filtering mechanisms to control which overlaps should be handled and which should be ignored.
-
Libbulletjme provides methods to enumerate overlaps, manifolds, and contact points.
-
You can trigger custom actions during each stage of collision processing.
-
Between simulation steps, you can perform pair tests, ray tests, contact tests, and sweep tests against a space.