Skip to content

Commit

Permalink
64 bit filtering
Browse files Browse the repository at this point in the history
fixed island bug
  • Loading branch information
erincatto committed Aug 24, 2024
1 parent 8039d61 commit e7a3687
Show file tree
Hide file tree
Showing 10 changed files with 91 additions and 27 deletions.
2 changes: 1 addition & 1 deletion benchmark/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ int main( int argc, char** argv )

b2WorldDef worldDef = b2DefaultWorldDef();
worldDef.enableSleep = false;
worldDef.enableContinous = enableContinuous;
worldDef.enableContinuous = enableContinuous;
worldDef.enqueueTask = EnqueueTask;
worldDef.finishTask = FinishTask;
worldDef.workerCount = threadCount;
Expand Down
20 changes: 9 additions & 11 deletions include/box2d/collision.h
Original file line number Diff line number Diff line change
Expand Up @@ -589,21 +589,20 @@ B2_API b2Manifold b2CollideSmoothSegmentAndPolygon( const b2SmoothSegment* smoot
*/

/// The default category bit for a tree proxy. Used for collision filtering.
#define b2_defaultCategoryBits ( 0x00000001 )
#define b2_defaultCategoryBits ( 0x00000001ui64 )

/// Convenience mask bits to use when you don't need collision filtering and just want
/// all results.
#define b2_defaultMaskBits ( 0xFFFFFFFF )
#define b2_defaultMaskBits ( UINT64_MAX )

/// A node in the dynamic tree. This is private data placed here for performance reasons.
/// 16 + 16 + 8 + pad(8)
typedef struct b2TreeNode
{
/// The node bounding box
b2AABB aabb; // 16

/// Category bits for collision filtering
uint32_t categoryBits; // 4
uint64_t categoryBits; // 8

union
{
Expand Down Expand Up @@ -631,7 +630,7 @@ typedef struct b2TreeNode
bool enlarged; // 1

/// Padding for clarity
char pad[9];
char pad[5];
} b2TreeNode;

/// The dynamic tree structure. This should be considered private data.
Expand Down Expand Up @@ -679,7 +678,7 @@ B2_API b2DynamicTree b2DynamicTree_Create( void );
B2_API void b2DynamicTree_Destroy( b2DynamicTree* tree );

/// Create a proxy. Provide an AABB and a userData value.
B2_API int32_t b2DynamicTree_CreateProxy( b2DynamicTree* tree, b2AABB aabb, uint32_t categoryBits, int32_t userData );
B2_API int32_t b2DynamicTree_CreateProxy( b2DynamicTree* tree, b2AABB aabb, uint64_t categoryBits, int32_t userData );

/// Destroy a proxy. This asserts if the id is invalid.
B2_API void b2DynamicTree_DestroyProxy( b2DynamicTree* tree, int32_t proxyId );
Expand All @@ -694,9 +693,8 @@ B2_API void b2DynamicTree_EnlargeProxy( b2DynamicTree* tree, int32_t proxyId, b2
/// @return true if the query should continue
typedef bool b2TreeQueryCallbackFcn( int32_t proxyId, int32_t userData, void* context );

/// Query an AABB for overlapping proxies. The callback class
/// is called for each proxy that overlaps the supplied AABB.
B2_API void b2DynamicTree_Query( const b2DynamicTree* tree, b2AABB aabb, uint32_t maskBits, b2TreeQueryCallbackFcn* callback,
/// Query an AABB for overlapping proxies. The callback class is called for each proxy that overlaps the supplied AABB.
B2_API void b2DynamicTree_Query( const b2DynamicTree* tree, b2AABB aabb, uint64_t maskBits, b2TreeQueryCallbackFcn* callback,
void* context );

/// This function receives clipped raycast input for a proxy. The function
Expand All @@ -717,7 +715,7 @@ typedef float b2TreeRayCastCallbackFcn( const b2RayCastInput* input, int32_t pro
/// @param maskBits filter bits: `bool accept = (maskBits & node->categoryBits) != 0;`
/// @param callback a callback class that is called for each proxy that is hit by the ray
/// @param context user context that is passed to the callback
B2_API void b2DynamicTree_RayCast( const b2DynamicTree* tree, const b2RayCastInput* input, uint32_t maskBits,
B2_API void b2DynamicTree_RayCast( const b2DynamicTree* tree, const b2RayCastInput* input, uint64_t maskBits,
b2TreeRayCastCallbackFcn* callback, void* context );

/// This function receives clipped ray-cast input for a proxy. The function
Expand All @@ -737,7 +735,7 @@ typedef float b2TreeShapeCastCallbackFcn( const b2ShapeCastInput* input, int32_t
/// @param maskBits filter bits: `bool accept = (maskBits & node->categoryBits) != 0;`
/// @param callback a callback class that is called for each proxy that is hit by the shape
/// @param context user context that is passed to the callback
B2_API void b2DynamicTree_ShapeCast( const b2DynamicTree* tree, const b2ShapeCastInput* input, uint32_t maskBits,
B2_API void b2DynamicTree_ShapeCast( const b2DynamicTree* tree, const b2ShapeCastInput* input, uint64_t maskBits,
b2TreeShapeCastCallbackFcn* callback, void* context );

/// Validate this tree. For testing.
Expand Down
2 changes: 2 additions & 0 deletions include/box2d/math_functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ static const b2Rot b2Rot_identity = { 1.0f, 0.0f };
static const b2Transform b2Transform_identity = { { 0.0f, 0.0f }, { 1.0f, 0.0f } };
static const b2Mat22 b2Mat22_zero = { { 0.0f, 0.0f }, { 0.0f, 0.0f } };

/// Compute an approximate arctangent in the range [-pi, pi]
/// This is hand coded for cross platform determinism
B2_API float b2Atan2( float y, float x );

/// @return the minimum of two floats
Expand Down
10 changes: 6 additions & 4 deletions include/box2d/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ typedef struct b2Filter
/// // etc
/// };
/// @endcode
uint32_t categoryBits;
uint64_t categoryBits;

/// The collision mask bits. This states the categories that this
/// shape would accept for collision.
Expand All @@ -243,7 +243,7 @@ typedef struct b2Filter
/// @code{.c}
/// maskBits = Static | Player;
/// @endcode
uint32_t maskBits;
uint64_t maskBits;

/// Collision groups allow a certain group of objects to never collide (negative)
/// or always collide (positive). A group index of zero has no effect. Non-zero group filtering
Expand All @@ -265,11 +265,11 @@ B2_API b2Filter b2DefaultFilter( void );
typedef struct b2QueryFilter
{
/// The collision category bits of this query. Normally you would just set one bit.
uint32_t categoryBits;
uint64_t categoryBits;

/// The collision mask bits. This states the shape categories that this
/// query would accept for collision.
uint32_t maskBits;
uint64_t maskBits;
} b2QueryFilter;

/// Use this to initialize your query filter
Expand Down Expand Up @@ -325,6 +325,8 @@ typedef struct b2ShapeDef
uint32_t customColor;

/// A sensor shape generates overlap events but never generates a collision response.
/// Sensors do not collide with other sensors and do not have continuous collision.
/// Instead use a ray or shape cast for those scenarios.
bool isSensor;

/// Enable sensor events for this shape. Only applies to kinematic and dynamic bodies. Ignored for sensors.
Expand Down
4 changes: 3 additions & 1 deletion src/broad_phase.c
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,8 @@ void b2FindPairsTask( int startIndex, int endIndex, uint32_t threadIndex, void*
b2AABB fatAABB = b2DynamicTree_GetAABB( baseTree, proxyId );
queryContext.queryShapeIndex = b2DynamicTree_GetUserData( baseTree, proxyId );

// Query trees. Only dynamic proxies collide with kinematic and static proxies
// Query trees. Only dynamic proxies collide with kinematic and static proxies.
// Using b2_defaultMaskBits so that b2Filter::groupIndex works.
if ( proxyType == b2_dynamicBody )
{
queryContext.queryTreeType = b2_kinematicBody;
Expand All @@ -323,6 +324,7 @@ void b2FindPairsTask( int startIndex, int endIndex, uint32_t threadIndex, void*
}

// All proxies collide with dynamic proxies
// Using b2_defaultMaskBits so that b2Filter::groupIndex works.
queryContext.queryTreeType = b2_dynamicBody;
b2DynamicTree_Query( bp->trees + b2_dynamicBody, fatAABB, b2_defaultMaskBits, b2PairQueryCallback, &queryContext );
}
Expand Down
12 changes: 6 additions & 6 deletions src/dynamic_tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

static b2TreeNode b2_defaultTreeNode = {
{ { 0.0f, 0.0f }, { 0.0f, 0.0f } }, 0, { B2_NULL_INDEX }, B2_NULL_INDEX, B2_NULL_INDEX, -1, -2, false,
{ 0, 0, 0, 0, 0, 0, 0, 0, 0 } };
{ 0, 0, 0, 0, 0 } };

static inline bool b2IsLeaf( const b2TreeNode* node )
{
Expand Down Expand Up @@ -726,7 +726,7 @@ static void b2RemoveLeaf( b2DynamicTree* tree, int32_t leaf )

// Create a proxy in the tree as a leaf node. We return the index of the node instead of a pointer so that we can grow
// the node pool.
int32_t b2DynamicTree_CreateProxy( b2DynamicTree* tree, b2AABB aabb, uint32_t categoryBits, int32_t userData )
int32_t b2DynamicTree_CreateProxy( b2DynamicTree* tree, b2AABB aabb, uint64_t categoryBits, int32_t userData )
{
B2_ASSERT( -b2_huge < aabb.lowerBound.x && aabb.lowerBound.x < b2_huge );
B2_ASSERT( -b2_huge < aabb.lowerBound.y && aabb.lowerBound.y < b2_huge );
Expand Down Expand Up @@ -961,7 +961,7 @@ static void b2ValidateMetrics( const b2DynamicTree* tree, int32_t index )
// B2_ASSERT(aabb.upperBound.x == node->aabb.upperBound.x);
// B2_ASSERT(aabb.upperBound.y == node->aabb.upperBound.y);

uint32_t categoryBits = tree->nodes[child1].categoryBits | tree->nodes[child2].categoryBits;
uint64_t categoryBits = tree->nodes[child1].categoryBits | tree->nodes[child2].categoryBits;
B2_ASSERT( node->categoryBits == categoryBits );

b2ValidateMetrics( tree, child1 );
Expand Down Expand Up @@ -1118,7 +1118,7 @@ int b2DynamicTree_GetByteCount( const b2DynamicTree* tree )
return (int)size;
}

void b2DynamicTree_Query( const b2DynamicTree* tree, b2AABB aabb, uint32_t maskBits, b2TreeQueryCallbackFcn* callback,
void b2DynamicTree_Query( const b2DynamicTree* tree, b2AABB aabb, uint64_t maskBits, b2TreeQueryCallbackFcn* callback,
void* context )
{
int32_t stack[b2_treeStackSize];
Expand Down Expand Up @@ -1159,7 +1159,7 @@ void b2DynamicTree_Query( const b2DynamicTree* tree, b2AABB aabb, uint32_t maskB
}
}

void b2DynamicTree_RayCast( const b2DynamicTree* tree, const b2RayCastInput* input, uint32_t maskBits,
void b2DynamicTree_RayCast( const b2DynamicTree* tree, const b2RayCastInput* input, uint64_t maskBits,
b2TreeRayCastCallbackFcn* callback, void* context )
{
b2Vec2 p1 = input->origin;
Expand Down Expand Up @@ -1248,7 +1248,7 @@ void b2DynamicTree_RayCast( const b2DynamicTree* tree, const b2RayCastInput* inp
}
}

void b2DynamicTree_ShapeCast( const b2DynamicTree* tree, const b2ShapeCastInput* input, uint32_t maskBits,
void b2DynamicTree_ShapeCast( const b2DynamicTree* tree, const b2ShapeCastInput* input, uint64_t maskBits,
b2TreeShapeCastCallbackFcn* callback, void* context )
{
if ( input->count == 0 )
Expand Down
5 changes: 3 additions & 2 deletions src/island.c
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,9 @@ void b2LinkJoint( b2World* world, b2Joint* joint )
{
b2AddJointToIsland( world, islandIdB, joint );
}

// Joints need to have islands merged immediately to keep the island graph valid.
b2MergeAwakeIslands( world );
}

void b2UnlinkJoint( b2World* world, b2Joint* joint )
Expand Down Expand Up @@ -599,8 +602,6 @@ void b2MergeAwakeIslands( b2World* world )
b2DestroyIsland( world, islandId );
}

b2ValidateConnectivity( world );

b2TracyCZoneEnd( merge_islands );
}

Expand Down
2 changes: 2 additions & 0 deletions src/solver.c
Original file line number Diff line number Diff line change
Expand Up @@ -1130,6 +1130,8 @@ void b2Solve( b2World* world, b2StepContext* stepContext )

b2MergeAwakeIslands( world );

b2ValidateConnectivity( world );

world->profile.buildIslands = b2GetMillisecondsAndReset( &timer );

b2SolverSet* awakeSet = world->solverSetArray + b2_awakeSet;
Expand Down
4 changes: 2 additions & 2 deletions src/types.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,13 @@ b2BodyDef b2DefaultBodyDef( void )

b2Filter b2DefaultFilter( void )
{
b2Filter filter = { 0x00000001, 0xFFFFFFFF, 0 };
b2Filter filter = { 0x0001ui64, UINT64_MAX, 0 };
return filter;
}

b2QueryFilter b2DefaultQueryFilter( void )
{
b2QueryFilter filter = { 0x00000001, 0xFFFFFFFF };
b2QueryFilter filter = { 0x0001ui64, UINT64_MAX };
return filter;
}

Expand Down
57 changes: 57 additions & 0 deletions test/test_world.c
Original file line number Diff line number Diff line change
Expand Up @@ -198,8 +198,65 @@ static int TestIsValid( void )
return 0;
}

int TestForAmy( void )
{
b2WorldDef worldDef = b2DefaultWorldDef();
b2WorldId world_id = b2CreateWorld( &worldDef );

b2BodyDef body_def = b2DefaultBodyDef();

body_def.type = b2_staticBody;
body_def.position = ( b2Vec2 ){ 0., 0. };
b2BodyId body_id = b2CreateBody( world_id, &body_def );
b2Polygon polygon = b2MakeBox( 1., 1. );
b2ShapeDef shape_def = b2DefaultShapeDef();
b2CreatePolygonShape( body_id, &shape_def, &polygon );

b2BodyDef simulon_body_def = b2DefaultBodyDef();

simulon_body_def.position = ( b2Vec2 ){ 0., -7.5 };
simulon_body_def.type = b2_dynamicBody;

b2BodyId simulon_body_id = b2CreateBody( world_id, &simulon_body_def );
b2Circle ball = { { 0.0, 0.35 }, 0.5 };

b2ShapeDef simulon_shape_def = b2DefaultShapeDef();
b2CreateCircleShape( simulon_body_id, &simulon_shape_def, &ball );

b2Polygon the_box = b2MakeRoundedBox( 0.1, 0.1, 0.01 );
b2CreatePolygonShape( simulon_body_id, &simulon_shape_def, &the_box );
b2BodyDef head_body_def = b2DefaultBodyDef();
head_body_def.position = ( b2Vec2 ){ 0., 6. };
head_body_def.type = b2_dynamicBody;
b2BodyId head_body_id = b2CreateBody( world_id, &head_body_def );
b2RevoluteJointDef joint_def5 = b2DefaultRevoluteJointDef();
joint_def5.bodyIdA = simulon_body_id;
joint_def5.bodyIdB = head_body_id;
joint_def5.localAnchorA = ( b2Vec2 ){ 0.0, 0.8 };
joint_def5.localAnchorB = ( b2Vec2 ){ 0.0, -0.17 / 2.0 };

b2JointId revolute_joint_id = b2CreateRevoluteJoint( world_id, &joint_def5 );
b2DistanceJointDef joint_def6 = b2DefaultDistanceJointDef();
joint_def6.bodyIdA = simulon_body_id;
joint_def6.bodyIdB = head_body_id;
joint_def6.localAnchorA = ( b2Vec2 ){ 0.0, 1.7 };
joint_def6.localAnchorB = ( b2Vec2 ){ 0.0, 0.8 };
joint_def6.length = 0.005;
joint_def6.hertz = 1.;
b2CreateDistanceJoint( world_id, &joint_def6 );

b2DestroyBody( simulon_body_id );

b2World_Step( world_id, 1. / 60., 4 );

b2DestroyWorld( world_id );

return 0;
}

int WorldTest( void )
{
RUN_SUBTEST( TestForAmy );
RUN_SUBTEST( HelloWorld );
RUN_SUBTEST( EmptyWorld );
RUN_SUBTEST( DestroyAllBodiesWorld );
Expand Down

0 comments on commit e7a3687

Please sign in to comment.