top of page
Search
cedarcantab

Studying Box2D-Lite in Javascript, Part 2: Contact Manifold

Updated: Mar 1





Background


In Lite, collision detection and response involved the following key classes / methods.


  • Arbiter

  • Collide

The above classes / methods were responsible for:


  1. detecting whether a collision had occurred between two boxes, using separating axis method.

  2. 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.

  3. create the contact constraint equation.

  4. 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.


12 views0 comments

Comments


記事: Blog2_Post
bottom of page