Background
In Lite, collision detection and response involved the following key classes / methods.
Arbiter
Collide
The above classes / methods were responsible for:
detecting whether a collision had occurred between two boxes, using separating axis method.
if a collision is detected, identify the contact points (one or two), including the contact features that made up the contact points, using clipping method.
create the contact constraint equation.
solve the contact constraint equation.
By allowing shapes other than boxes:
different collision detection methods need to be created for different combination of shapes.
different methods for identifying contact points.
once the contact points have been identified, the basic algorithm for calculating the collision response remains much the same, but the overall code structure becomes considerably more complex.
In this post we will talk about the basic classes that are involved in holding the contact information, namely the contact manifold.
Important Classes for holding contact information
There are many different classes / objects involved in holding the various bits of information relating to contacts. In this post we list out the following classes and comment on their main properties:
Contact
Manifold
ManifoldPoint
ContactID
ContactFeature
Contact
To start with the objective - we want to end up with the contact class, which will contain the basic information about the contact. This object contains the following key information of a contact between two fixtures:
the contact manifold (details to follow) - note this may contain information about one or two contact points
two colliding fixtures
constraints
friction
restitution
constraint equations for each of the contact points
class b2Contact {
constructor(manifold, fA, fB) {
this.manifold = manifold;
this.pointCount = manifold.pointCount;
this.fixtureA = fA;
this.fixtureB = fB;
this.constraints = [];
for (let i = 0; i < this.pointCount; i++) {
this.constraints.push(new b2ContactConstraint( ));
}
this.friction = b2MixFriction(this.fixtureA.friction, this.fixtureB.friction);
this.restitution = b2MixRestitution(this.fixtureA.restitution, this.fixtureB.restitution);
this.restitutionThreshold = b2MixRestitutionThreshold(this.fixtureA.restitutionThreshold, this.fixtureB.restitutionThreshold);
}
update(oldArb, timeStep) {
////
}
}
And some associated functions for mixing friction and restitution coefficients of the two fixtures.
function b2MixFriction(friction1, friction2) {
return Math.sqrt(friction1 * friction2)
}
function b2MixRestitution( restitution1, restitution2) {
return restitution1 > restitution2 ? restitution1 : restitution2;
}
Manifold
This is the high level object used to information regarding a contact between two convex shapes, including the actual contact points, and the collision normal, and data to identify the type of contact.
As noted by the comments of the original code, Box2D supports multiple types of contact:
clip point versus plane with radius
point versus point with radius (circles)
Note the names of the key properties: localNormal and localPoint. The collision normal and the contact point are basically generated in local space of the reference body.
What this actually means will come clearer as we work through the contact point generation code for different geometries. Specifically, the comments of the original code explains:
The local point usage depends on the manifold type:
-e_circles: the local center of circleA
-e_faceA: the center of faceA
-e_faceB: the center of faceB
Similarly the local normal usage:
-e_circles: not used
-e_faceA: the normal on polygonA
-e_faceB: the normal on polygonB
The class makes heavy use of other classes, which we are briefly commented on below.
class b2Manifold {
constructor() {
this.type; // e_circles, e_faceA, or e_faceB
this.points = [new b2ManifoldPoint(), new b2ManifoldPoint()]; /// Array of points of contact (each contact = b2ManifoldPoint )
this.localNormal = new b2Vec2(); ///b2Vec2< not use for Type::e_points
this.localPoint = new b2Vec2(); ///b2Vec2< usage depends on manifold type
this.pointCount; ///< the number of manifold points
}
ManifoldPoint
This is used to hold the details of a contact point belonging to a contact manifold. It is structured as follows:
It should be noted that the "point" is held in local space coordinates. We will delve into this in more details when we look at the contact point generation part of Box2D, but for now note the following, as per the comments of the original code.]
The local point usage depends on the manifold type:
-e_circles: the local center of circleB
-e_faceA: the local center of cirlceB or the clip point of polygonB
-e_faceB: the clip point of polygonA
class b2ManifoldPoint {
constructor() {
this.localPoint = new Vec2(); ///b2Vec2 < usage depends on manifold type
this.normalImpulse; ///float< the non-penetration impulse
this.tangentImpulse; ///float< the friction impulse
this.id = new b2ContactID(); ///b2ContactID< uniquely identifies a contact point between two shapes
}
};
ContactID
This is used to identify a particular contact point. It is just a pointer to another object called ContactFeature (see below)
/// Contact ids to facilitate warm starting.
class b2ContactID {
constructor() {
this.cf = new b2ContactFeature(); //b2ContactFeature
}
///uint32< Used to quickly compare contact ids.
get key() {
return this.cf.indexA + (this.cf.indexB << 8) + (this.cf.typeA << 16) + (this.cf.typeB << 24);
}
};
ContactFeature
This is the object that is used to hold the contact features that define a particular contact point. For each fixture, this object will hold:
the index of the feature (ie which edge)
the type of feature (eg is it an edge? vertex?)
/// The features that intersect to form the contact point
/// This must be 4 bytes or less.
class b2ContactFeature {
constructor() {
this.indexA; ///< Feature index on shapeA
this.indexB; ///< Feature index on shapeB
this.typeA; ///< The feature type on shapeA
this.typeB; ///< The feature type on shapeB
}
// Type
static e_vertex = 0;
static e_face = 1
};
For this post, there is no sample code - this is just to aid understanding of the collision detection and contact point generation code which we will delve into later.
Comments