Skip to content

Commit

Permalink
Implemented kinematic tree and custom filter callback (#164)
Browse files Browse the repository at this point in the history
Added sample with custom filter
  • Loading branch information
erincatto committed May 29, 2024
1 parent 67fe82f commit 25f40d8
Show file tree
Hide file tree
Showing 18 changed files with 259 additions and 142 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ include(FetchContent)

project(box2d
VERSION 3.0.0
DESCRIPTION "A 2D physics engine"
DESCRIPTION "A 2D physics engine for games"
HOMEPAGE_URL "https://box2d.org"
LANGUAGES C CXX
)
Expand Down
3 changes: 3 additions & 0 deletions include/box2d/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@ typedef enum b2BodyType

/// positive mass, velocity determined by forces, moved by solver
b2_dynamicBody = 2,

/// number of body types
b2_bodyTypeCount,
} b2BodyType;

/// A body definition holds all the data needed to construct a rigid body.
Expand Down
6 changes: 3 additions & 3 deletions samples/car.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ void Car::Spawn(b2WorldId worldId, b2Vec2 position, float scale, float hertz,

for (int i = 0; i < 6; ++i)
{
vertices[i].x *= scale;
vertices[i].y *= scale;
vertices[i].x *= 0.85f * scale;
vertices[i].y *= 0.85f * scale;
}

b2Hull hull = b2ComputeHull(vertices, 6);
b2Polygon chassis = b2MakePolygon(&hull, 0.0f);
b2Polygon chassis = b2MakePolygon(&hull, 0.15f * scale);

b2ShapeDef shapeDef = b2DefaultShapeDef();
shapeDef.density = 1.0f / scale;
Expand Down
4 changes: 2 additions & 2 deletions samples/draw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1451,9 +1451,9 @@ void Draw::DrawString(int x, int y, const char* string, ...)
va_end(arg);
}

void Draw::DrawString(b2Vec2 pw, const char* string, ...)
void Draw::DrawString(b2Vec2 p, const char* string, ...)
{
b2Vec2 ps = g_camera.ConvertWorldToScreen(pw);
b2Vec2 ps = g_camera.ConvertWorldToScreen(p);

va_list arg;
va_start(arg, string);
Expand Down
2 changes: 1 addition & 1 deletion samples/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -566,7 +566,7 @@ int main(int, char**)
glfwWindowHint(GLFW_SAMPLES, 4);

b2Version version = b2GetVersion();
snprintf(buffer, 128, "Box2D Version %d.%d.%d (alpha)", version.major, version.minor, version.revision);
snprintf(buffer, 128, "Box2D Version %d.%d.%d (beta)", version.major, version.minor, version.revision);

if (GLFWmonitor* primaryMonitor = glfwGetPrimaryMonitor())
{
Expand Down
13 changes: 7 additions & 6 deletions samples/sample_continuous.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -669,11 +669,12 @@ class GhostCollision : public Sample

static int sampleGhostCollision = RegisterSample("Continuous", "Ghost Collision", GhostCollision::Create);

// Speculative collision failure case suggested by Dirk Gregorius
class SpeculativeFail : public Sample
// Speculative collision failure case suggested by Dirk Gregorius. This uses
// a simple fallback scheme to prevent tunneling.
class SpeculativeFallback : public Sample
{
public:
explicit SpeculativeFail(Settings& settings)
explicit SpeculativeFallback(Settings& settings)
: Sample(settings)
{
if (settings.restart == false)
Expand Down Expand Up @@ -701,7 +702,7 @@ class SpeculativeFail : public Sample
float offset = 8.0f;
b2BodyDef bodyDef = b2DefaultBodyDef();
bodyDef.type = b2_dynamicBody;
bodyDef.position = {offset, 8.0f};
bodyDef.position = {offset, 12.0f};
bodyDef.linearVelocity = {0.0f, -100.0f};
b2BodyId bodyId = b2CreateBody(m_worldId, &bodyDef);

Expand All @@ -713,11 +714,11 @@ class SpeculativeFail : public Sample

static Sample* Create(Settings& settings)
{
return new SpeculativeFail(settings);
return new SpeculativeFallback(settings);
}
};

static int sampleSpeculativeFail = RegisterSample("Continuous", "Speculative Fail", SpeculativeFail::Create);
static int sampleSpeculativeFallback = RegisterSample("Continuous", "Speculative Fallback", SpeculativeFallback::Create);

// This shows a fast moving body that uses continuous collision versus static and dynamic bodies.
// This is achieved by setting the ball body as a *bullet*.
Expand Down
2 changes: 1 addition & 1 deletion samples/sample_events.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@ class ContactEvent : public Sample
bodyDef.gravityScale = 0.0f;
bodyDef.linearDamping = 0.5f;
bodyDef.angularDamping = 0.5f;
bodyDef.isBullet = true;
m_playerId = b2CreateBody(m_worldId, &bodyDef);

b2Circle circle = {{0.0f, 0.0f}, 1.0f};
Expand Down Expand Up @@ -1012,7 +1013,6 @@ class BodyMove : public Sample
ImGui::End();
}


void Step(Settings& settings) override
{
if (settings.pause == false && (m_stepCount & 15) == 15 && m_count < e_count)
Expand Down
95 changes: 95 additions & 0 deletions samples/sample_shapes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,101 @@ class ShapeFilter : public Sample

static int sampleShapeFilter = RegisterSample("Shapes", "Filter", ShapeFilter::Create);

// This shows how to use custom filtering
class CustomFilter : public Sample
{
public:
enum
{
e_count = 10
};

explicit CustomFilter(Settings& settings)
: Sample(settings)
{
if (settings.restart == false)
{
g_camera.m_center = {0.0f, 5.0f};
g_camera.m_zoom = 10.0f;
}

// Register custom filter
b2World_SetCustomFilterCallback(m_worldId, CustomFilterStatic, this);

{
b2BodyDef bodyDef = b2DefaultBodyDef();
b2BodyId groundId = b2CreateBody(m_worldId, &bodyDef);
b2Segment segment = {{-40.0f, 0.0f}, {40.0f, 0.0f}};

b2ShapeDef shapeDef = b2DefaultShapeDef();

b2CreateSegmentShape(groundId, &shapeDef, &segment);
}

b2BodyDef bodyDef = b2DefaultBodyDef();
bodyDef.type = b2_dynamicBody;
b2ShapeDef shapeDef = b2DefaultShapeDef();
b2Polygon box = b2MakeSquare(1.0f);
float x = -e_count;

for (int i = 0; i < e_count; ++i)
{
bodyDef.position = {x, 5.0f};
m_bodyIds[i] = b2CreateBody(m_worldId, &bodyDef);

shapeDef.userData = reinterpret_cast<void*>(intptr_t(i + 1));
m_shapeIds[i] = b2CreatePolygonShape(m_bodyIds[i], &shapeDef, &box);
x += 2.0f;
}
}

void Step(Settings& settings) override
{
g_draw.DrawString(5, m_textLine, "Custom filter disables collision between odd and even shapes");
m_textLine += m_textIncrement;

Sample::Step(settings);

for (int i = 0; i < e_count; ++i)
{
b2Vec2 p = b2Body_GetPosition(m_bodyIds[i]);
g_draw.DrawString({p.x, p.y}, "%d", i);
}
}

bool ShouldCollide(b2ShapeId shapeIdA, b2ShapeId shapeIdB)
{
void* userDataA = b2Shape_GetUserData(shapeIdA);
void* userDataB = b2Shape_GetUserData(shapeIdB);

if (userDataA == NULL || userDataB == NULL)
{
return true;
}

int indexA = reinterpret_cast<intptr_t>(userDataA);
int indexB = reinterpret_cast<intptr_t>(userDataB);

return ((indexA & 1) + (indexB & 1)) != 1;
}

static bool CustomFilterStatic(b2ShapeId shapeIdA, b2ShapeId shapeIdB, void* context)
{
CustomFilter* customFilter = static_cast<CustomFilter*>(context);
return customFilter->ShouldCollide(shapeIdA, shapeIdB);
}

static Sample* Create(Settings& settings)
{
return new CustomFilter(settings);
}

b2BodyId m_bodyIds[e_count];
b2ShapeId m_shapeIds[e_count];
};

static int sampleCustomFilter = RegisterSample("Shapes", "Custom Filter", CustomFilter::Create);

// Restitution is approximate since Box2D uses speculative collision
class Restitution : public Sample
{
Expand Down
2 changes: 1 addition & 1 deletion samples/sample_world.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ class LargeWorld : public Sample
for (int i = 0; i < 5; ++i)
{
Donut donut;
donut.Spawn(m_worldId, position, 0.75f, donutIndex + 1, NULL);
donut.Spawn(m_worldId, position, 0.75f, 0, NULL);
donutIndex += 1;
position.x += 2.0f;
}
Expand Down
19 changes: 11 additions & 8 deletions src/body.c
Original file line number Diff line number Diff line change
Expand Up @@ -725,7 +725,7 @@ void b2Body_SetTransform(b2BodyId bodyId, b2Vec2 position, float angle)
// They body could be disabled
if (shape->proxyKey != B2_NULL_INDEX)
{
b2BroadPhase_MoveProxy(broadPhase, shape->proxyKey, fatAABB, shape->filter.maskBits);
b2BroadPhase_MoveProxy(broadPhase, shape->proxyKey, fatAABB);
}
}

Expand Down Expand Up @@ -1040,7 +1040,8 @@ void b2Body_SetType(b2BodyId bodyId, b2BodyType type)
shapeId = shape->nextShapeId;
b2DestroyShapeProxy(shape, &world->broadPhase);
bool forcePairCreation = true;
b2CreateShapeProxy(shape, &world->broadPhase, b2_movableProxy, transform, forcePairCreation);
b2BodyType proxyType = type;
b2CreateShapeProxy(shape, &world->broadPhase, proxyType, transform, forcePairCreation);
}
}
else if (type == b2_staticBody)
Expand Down Expand Up @@ -1114,23 +1115,25 @@ void b2Body_SetType(b2BodyId bodyId, b2BodyType type)
shapeId = shape->nextShapeId;
b2DestroyShapeProxy(shape, &world->broadPhase);
bool forcePairCreation = true;
b2CreateShapeProxy(shape, &world->broadPhase, b2_staticProxy, transform, forcePairCreation);
b2CreateShapeProxy(shape, &world->broadPhase, b2_staticBody, transform, forcePairCreation);
}
}
else
{
B2_ASSERT(originalType == b2_dynamicBody || originalType == b2_kinematicBody);
B2_ASSERT(type == b2_dynamicBody || type == b2_kinematicBody);

// Converting between kinematic and dynamic is much simpler

// Touch the broad-phase proxies to ensure the correct contacts get created
// Recreate shape proxies in static tree.
b2Transform transform = b2GetBodyTransformQuick(world, body);
int shapeId = body->headShapeId;
while (shapeId != B2_NULL_INDEX)
{
b2Shape* shape = world->shapeArray + shapeId;
b2BufferMove(&world->broadPhase, (b2MovedProxy){shape->proxyKey, shape->filter.maskBits});
shapeId = shape->nextShapeId;
b2DestroyShapeProxy(shape, &world->broadPhase);
b2BodyType proxyType = type;
bool forcePairCreation = true;
b2CreateShapeProxy(shape, &world->broadPhase, proxyType, transform, forcePairCreation);
}
}

Expand Down Expand Up @@ -1513,7 +1516,7 @@ void b2Body_Enable(b2BodyId bodyId)
b2Transform transform = b2GetBodyTransformQuick(world, body);

// Add shapes to broad-phase
b2ProxyType proxyType = setId == b2_staticSet ? b2_staticProxy : b2_movableProxy;
b2BodyType proxyType = body->type;
bool forcePairCreation = true;
int shapeId = body->headShapeId;
while (shapeId != B2_NULL_INDEX)
Expand Down
Loading

0 comments on commit 25f40d8

Please sign in to comment.