2
votes

I'm just curious, in the case that I have a body that has 2 or more fixtures in it that are not "joined together", how can I determine this definitively in code? Here is an example of what I mean:

Example of a single body with more than 1 fixture, two are touching, one is not.

I marked the vertices for each distinctive fixture just to make it completely clear that these are separate shapes which do not share vertices with each other. They are however combined into a single body. As you can see, two fixtures are within very close proximity to each other or "touching", and one is set apart by itself. I'm wondering how I can query the fixtures of a body in Box2D to be able to find this out at runtime.

In order to put this all into perspective, it's for creating and processing destructible bodies. The image is a rough representation of what will happen after a hole has been punched through a set of fixtures in a body. I need to query to see which fixtures touch each other so that I can split the one body into two, as naturally they should be at that point.

4

4 Answers

3
votes

If you already have the new fixtures prepared you could use the b2TestOverlap function to check if they are overlapping. The function is found in b2Collision.h:

/// Determine if two generic shapes overlap.
bool b2TestOverlap( const b2Shape* shapeA, int32 indexA,
                    const b2Shape* shapeB, int32 indexB,
                    const b2Transform& xfA, const b2Transform& xfB );

The shapes can be found by fixture->GetShape(), and the index parameters will be zero for polygons. As the fixtures are on the same body, the last two parameters can both be body->GetTransform().

I have never used this myself so I couldn't tell you for sure, but noting that the name of this function is 'overlap', it may not return a positive for the case of your A and B fixtures in the diagram since technically I don't think they are overlapping. If this is the case, I think you are on your own because Box2D doesn't offer any 'touching' tests.

btw the contact list mentioned by Constantinius is only valid for fixtures on different bodies colliding - fixtures on the same body don't collide.

3
votes

In the Box2D manual under "9.3 Accessing Contacts" it is written, that you can access all contacts of a body using the function GetContactList:

for (b2ContactEdge* ce = myBody->GetContactList(); ce; ce = ce->next)
{
    b2Contact* c = ce->contact;
    ...
}

In the b2ContactEdge and the b2Contact you can find the actual shapes and a flag if they are actually touching (b2Contact::IsTouching).

1
votes

I would suggest doing something like this:

  • to get an accurate result you really have to test for intersections. In a first pass you could compare only the centers of gravity of the fixtures and then only compute the intersections of all lines if the COGs lie within small distance.

  • another approach might be to rasterize the scene. then you could paint the rasterized pixels using something like a stencil buffer.

  • the stencil would then be an array of size(w*h) where w and h are width and height of your rasterized image and holds an integer per pixel.

  • initialize the stencil buffer with 0 for each pixel.

  • for each rasterized fixture that you 'paint' you just increment the buffer value for all of its pixel positions.

  • so finally the stencil buffer contains 0 at positions which are not occupied by any fixture, 1 where only one fixture lies at the position and for any greater value you have intersections.

the complexity of this approach then mainly depends on the chosen rasterization resolution.

0
votes

Another way to do this (as an alternative to using b2TestOverlap as suggested previously by someone else), would be to use the b2CollidePolygons function (from b2Collision.h). You would use this on the polygon shapes of the fixtures and then see whether the b2Manifold's pointCount field is greater than zero.

Here's some example pre-C++11 code for you (which I have not compiled and tested so hopefully isn't too foobar'ed):

bool isTouching(const b2PolygonShape& polygonA, const b2Transform& xfA, const b2PolygonShape& polygonB, const b2Transform& xfb)
{
    b2Manifold manifold;
    b2CollidePolygons(&manifold, &polygonA, xfA, &polygonB, xfB);
    return manifold.pointCount > 0;
}

bool isPolygonFixturesTouching(const b2Fixture& fixtureA, const b2Fixture& fixtureB)
{
    const b2Shape* shapeA = fixtureA.GetShape();
    const b2Shape* shapeB = fixtureB.GetShape();
    if (shapeA->GetType() == b2Shape::e_polygon && shapeB->GetType() == b2Shape::e_polygon)
    {
        const b2Body* bodyA = fixtureA.GetBody();
        const b2Transform& xfA = bodyA->GetTransform();
        const b2Body* bodyB = fixtureB.GetBody();
        const b2Transform& xfB = bodyB->GetTransform();
        return isTouching(*(static_cast<b2PolygonShape*>(shapeA)), xfA, *(static_cast<b2PolygonShape*>(shapeB)), xfB);
    }
    return false;
}

int main()
{
    ...
    if (isPolygonFixturesTouching(fixtureA, fixtureB))
    {
        std::cout << "hooray!" << std::endl;
    }
}

Note: Some crude timing tests which I've just tried that compare b2TestOverlap vs. b2CollidePolygons show b2TestOverlap to be 4 or 5 times faster (than b2CollidePolygons). Given that the former is only looking for any overlap while the later is calculating a collision manifold, I suppose the the speed difference isn't surprising. Anyways, it seems the moral of this story is that b2TestOverlap is likely the preferable function to use if you just want to know whether 2 fixtures are touching while b2CollidePolygons is the more useful if you also want to know additional stuff like how the 2 fixtures are touching.