Skip to content

Commit 87e13e4

Browse files
authored
Fixed bug with static shape recreation (#822)
Added translation and speed functions for prismatic joint. Fix missing sensor end touch event for fast moving shapes. Null joint to disable collision between two specific bodies. World user data. More consistent functions to make polygons. Save and load uint64 from ids.
1 parent aee18c7 commit 87e13e4

38 files changed

+1404
-144
lines changed

benchmark/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ add_executable(benchmark
66
large_pyramid.c
77
many_pyramids.c
88
smash.c
9+
spinner.c
910
tumbler.c
1011
)
1112

benchmark/main.c

+4
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ extern b2WorldId JointGrid( b2WorldDef* worldDef );
3030
extern b2WorldId LargePyramid( b2WorldDef* worldDef );
3131
extern b2WorldId ManyPyramids( b2WorldDef* worldDef );
3232
extern b2WorldId Smash( b2WorldDef* worldDef );
33+
extern b2WorldId Spinner( b2WorldDef* worldDef );
3334
extern b2WorldId Tumbler( b2WorldDef* worldDef );
3435

3536
typedef struct Benchmark
@@ -115,13 +116,16 @@ static void FinishTask( void* userTask, void* userContext )
115116
}
116117

117118
// Box2D benchmark application. On Windows I recommend running this in an administrator command prompt. Don't use Windows Terminal.
119+
// Or use affinity. [0x01 0x02 0x04 0x08 0x10 0x20 0x40 0x80]
120+
// start /affinity 0x5555 .\build\bin\Release\benchmark.exe -t=4 -w=4
118121
int main( int argc, char** argv )
119122
{
120123
Benchmark benchmarks[] = {
121124
{ "joint_grid", JointGrid, 500 },
122125
{ "large_pyramid", LargePyramid, 500 },
123126
{ "many_pyramids", ManyPyramids, 200 },
124127
{ "smash", Smash, 300 },
128+
{ "spinner", Spinner, 1400 },
125129
{ "tumbler", Tumbler, 750 },
126130
};
127131

benchmark/spinner.c

+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// SPDX-FileCopyrightText: 2024 Erin Catto
2+
// SPDX-License-Identifier: MIT
3+
4+
#include "box2d/box2d.h"
5+
#include "box2d/math_functions.h"
6+
7+
enum
8+
{
9+
e_count = 3038,
10+
e_pointCount = 360,
11+
};
12+
13+
b2WorldId Spinner( b2WorldDef* worldDef )
14+
{
15+
b2WorldId worldId = b2CreateWorld( worldDef );
16+
17+
b2BodyId groundId;
18+
{
19+
b2BodyDef bodyDef = b2DefaultBodyDef();
20+
groundId = b2CreateBody( worldId, &bodyDef );
21+
22+
b2Vec2 points[e_pointCount];
23+
24+
b2Rot q = b2MakeRot( -2.0f * b2_pi / e_pointCount );
25+
b2Vec2 p = { 40.0f, 0.0f };
26+
for ( int i = 0; i < e_pointCount; ++i )
27+
{
28+
points[i] = (b2Vec2){ p.x, p.y + 32.0f };
29+
p = b2RotateVector( q, p );
30+
}
31+
32+
b2ChainDef chainDef = b2DefaultChainDef();
33+
chainDef.points = points;
34+
chainDef.count = e_pointCount;
35+
chainDef.isLoop = true;
36+
chainDef.friction = 0.1f;
37+
38+
b2CreateChain( groundId, &chainDef );
39+
}
40+
41+
{
42+
b2BodyDef bodyDef = b2DefaultBodyDef();
43+
bodyDef.type = b2_dynamicBody;
44+
bodyDef.position = (b2Vec2){ 0.0, 12.0f };
45+
bodyDef.enableSleep = false;
46+
47+
b2BodyId spinnerId = b2CreateBody( worldId, &bodyDef );
48+
49+
b2Polygon box = b2MakeRoundedBox( 0.4f, 20.0f, 0.2f );
50+
b2ShapeDef shapeDef = b2DefaultShapeDef();
51+
shapeDef.friction = 0.0f;
52+
b2CreatePolygonShape( spinnerId, &shapeDef, &box );
53+
54+
float motorSpeed = 5.0f;
55+
float maxMotorTorque = 40000.0f;
56+
b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();
57+
jointDef.bodyIdA = groundId;
58+
jointDef.bodyIdB = spinnerId;
59+
jointDef.localAnchorA = bodyDef.position;
60+
jointDef.enableMotor = true;
61+
jointDef.motorSpeed = motorSpeed;
62+
jointDef.maxMotorTorque = maxMotorTorque;
63+
}
64+
65+
b2Capsule capsule = { { -0.25f, 0.0f }, { 0.25f, 0.0f }, 0.25f };
66+
b2Circle circle = { { 0.0f, 0.0f }, 0.35f };
67+
b2Polygon square = b2MakeSquare( 0.35f );
68+
69+
b2BodyDef bodyDef = b2DefaultBodyDef();
70+
bodyDef.type = b2_dynamicBody;
71+
b2ShapeDef shapeDef = b2DefaultShapeDef();
72+
shapeDef.friction = 0.1f;
73+
shapeDef.restitution = 0.1f;
74+
shapeDef.density = 0.25f;
75+
76+
#ifdef NDEBUG
77+
int bodyCount = e_count;
78+
#else
79+
int bodyCount = 499;
80+
#endif
81+
82+
float x = -24.0f, y = 2.0f;
83+
for ( int i = 0; i < bodyCount; ++i )
84+
{
85+
bodyDef.position = (b2Vec2){ x, y };
86+
b2BodyId bodyId = b2CreateBody( worldId, &bodyDef );
87+
88+
int remainder = i % 3;
89+
if ( remainder == 0 )
90+
{
91+
b2CreateCapsuleShape( bodyId, &shapeDef, &capsule );
92+
}
93+
else if ( remainder == 1 )
94+
{
95+
b2CreateCircleShape( bodyId, &shapeDef, &circle );
96+
}
97+
else if ( remainder == 2 )
98+
{
99+
b2CreatePolygonShape( bodyId, &shapeDef, &square );
100+
}
101+
102+
x += 1.0f;
103+
104+
if ( x > 24.0f )
105+
{
106+
x = -24.0f;
107+
y += 1.0f;
108+
}
109+
}
110+
111+
return worldId;
112+
}

docs/simulation.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -881,7 +881,11 @@ body and you can have any mix of sensors and solid shapes. Also,
881881
sensors only form contacts when at least one body is dynamic, so you
882882
will not get sensors overlap detection for kinematic versus kinematic,
883883
kinematic versus static, or static versus static. Finally sensors do not
884-
detect other sensors.
884+
detect other sensors.
885+
886+
Sensors do not detect objects that pass through the sensor shape within
887+
one time step. If you have fast moving object and/or small sensors then you
888+
should use a ray or shape cast to detect these events.
885889

886890
Sensor overlap detection is achieved using events, which are described
887891
below.

include/box2d/box2d.h

+27
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,12 @@ B2_API b2Profile b2World_GetProfile( b2WorldId worldId );
189189
/// Get world counters and sizes
190190
B2_API b2Counters b2World_GetCounters( b2WorldId worldId );
191191

192+
/// Set the user data pointer.
193+
B2_API void b2World_SetUserData( b2WorldId worldId, void* userData );
194+
195+
/// Get the user data pointer.
196+
B2_API void* b2World_GetUserData( b2WorldId worldId );
197+
192198
/// Dump memory stats to box2d_memory.txt
193199
B2_API void b2World_DumpMemoryStats( b2WorldId worldId );
194200

@@ -858,6 +864,21 @@ B2_API float b2MouseJoint_GetMaxForce( b2JointId jointId );
858864

859865
/**@}*/
860866

867+
/**
868+
* @defgroup null_joint Null Joint
869+
* @brief Functions for the null joint.
870+
*
871+
* The null joint is used to disable collision between two bodies. As a side effect of being a joint, it also
872+
* keeps the two bodies in the same simulation island.
873+
* @{
874+
*/
875+
876+
/// Create a null joint.
877+
/// @see b2NullJointDef for details
878+
B2_API b2JointId b2CreateNullJoint( b2WorldId worldId, const b2NullJointDef* def );
879+
880+
/**@}*/
881+
861882
/**
862883
* @defgroup prismatic_joint Prismatic Joint
863884
* @brief A prismatic joint allows for translation along a single axis with no rotation.
@@ -927,6 +948,12 @@ B2_API float b2PrismaticJoint_GetMaxMotorForce( b2JointId jointId );
927948
/// Get the prismatic joint current motor force, typically in newtons
928949
B2_API float b2PrismaticJoint_GetMotorForce( b2JointId jointId );
929950

951+
/// Get the current joint translation, usually in meters.
952+
B2_API float b2PrismaticJoint_GetTranslation( b2JointId jointId );
953+
954+
/// Get the current joint translation speed, usually in meters per second.
955+
B2_API float b2PrismaticJoint_GetSpeed( b2JointId jointId );
956+
930957
/** @} */
931958

932959
/**

include/box2d/collision.h

+17-5
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,11 @@ B2_API b2Polygon b2MakePolygon( const b2Hull* hull, float radius );
180180

181181
/// Make an offset convex polygon from a convex hull. This will assert if the hull is not valid.
182182
/// @warning Do not manually fill in the hull data, it must come directly from b2ComputeHull
183-
B2_API b2Polygon b2MakeOffsetPolygon( const b2Hull* hull, float radius, b2Transform transform );
183+
B2_API b2Polygon b2MakeOffsetPolygon( const b2Hull* hull, b2Vec2 position, b2Rot rotation );
184+
185+
/// Make an offset convex polygon from a convex hull. This will assert if the hull is not valid.
186+
/// @warning Do not manually fill in the hull data, it must come directly from b2ComputeHull
187+
B2_API b2Polygon b2MakeOffsetRoundedPolygon( const b2Hull* hull, b2Vec2 position, b2Rot rotation, float radius );
184188

185189
/// Make a square polygon, bypassing the need for a convex hull.
186190
/// @param h the half-width
@@ -200,10 +204,18 @@ B2_API b2Polygon b2MakeRoundedBox( float hx, float hy, float radius );
200204
/// Make an offset box, bypassing the need for a convex hull.
201205
/// @param hx the half-width
202206
/// @param hy the half-height
203-
/// @param center the local position of the center of the box
207+
/// @param center the local center of the box
204208
/// @param rotation the local rotation of the box
205209
B2_API b2Polygon b2MakeOffsetBox( float hx, float hy, b2Vec2 center, b2Rot rotation );
206210

211+
/// Make an offset rounded box, bypassing the need for a convex hull.
212+
/// @param hx the half-width
213+
/// @param hy the half-height
214+
/// @param center the local center of the box
215+
/// @param rotation the local rotation of the box
216+
/// @param radius the radius of the rounded extension
217+
B2_API b2Polygon b2MakeOffsetRoundedBox( float hx, float hy, b2Vec2 center, b2Rot rotation, float radius );
218+
207219
/// Transform a polygon. This is useful for transferring a shape from one body to another.
208220
B2_API b2Polygon b2TransformPolygon( b2Transform transform, const b2Polygon* polygon );
209221

@@ -712,7 +724,7 @@ typedef bool b2TreeQueryCallbackFcn( int32_t proxyId, int32_t userData, void* co
712724
/// Query an AABB for overlapping proxies. The callback class is called for each proxy that overlaps the supplied AABB.
713725
/// @return performance data
714726
B2_API b2TreeStats b2DynamicTree_Query( const b2DynamicTree* tree, b2AABB aabb, uint64_t maskBits,
715-
b2TreeQueryCallbackFcn* callback, void* context );
727+
b2TreeQueryCallbackFcn* callback, void* context );
716728

717729
/// This function receives clipped ray cast input for a proxy. The function
718730
/// returns the new ray fraction.
@@ -735,7 +747,7 @@ typedef float b2TreeRayCastCallbackFcn( const b2RayCastInput* input, int32_t pro
735747
/// @param context user context that is passed to the callback
736748
/// @return performance data
737749
B2_API b2TreeStats b2DynamicTree_RayCast( const b2DynamicTree* tree, const b2RayCastInput* input, uint64_t maskBits,
738-
b2TreeRayCastCallbackFcn* callback, void* context );
750+
b2TreeRayCastCallbackFcn* callback, void* context );
739751

740752
/// This function receives clipped ray cast input for a proxy. The function
741753
/// returns the new ray fraction.
@@ -756,7 +768,7 @@ typedef float b2TreeShapeCastCallbackFcn( const b2ShapeCastInput* input, int32_t
756768
/// @param context user context that is passed to the callback
757769
/// @return performance data
758770
B2_API b2TreeStats b2DynamicTree_ShapeCast( const b2DynamicTree* tree, const b2ShapeCastInput* input, uint64_t maskBits,
759-
b2TreeShapeCastCallbackFcn* callback, void* context );
771+
b2TreeShapeCastCallbackFcn* callback, void* context );
760772

761773
/// Validate this tree. For testing.
762774
B2_API void b2DynamicTree_Validate( const b2DynamicTree* tree );

include/box2d/id.h

+59-7
Original file line numberDiff line numberDiff line change
@@ -56,29 +56,29 @@ typedef struct b2ShapeId
5656
uint16_t revision;
5757
} b2ShapeId;
5858

59-
/// Joint id references a joint instance. This should be treated as an opaque handle.
60-
typedef struct b2JointId
59+
/// Chain id references a chain instances. This should be treated as an opaque handle.
60+
typedef struct b2ChainId
6161
{
6262
int32_t index1;
6363
uint16_t world0;
6464
uint16_t revision;
65-
} b2JointId;
65+
} b2ChainId;
6666

67-
/// Chain id references a chain instances. This should be treated as an opaque handle.
68-
typedef struct b2ChainId
67+
/// Joint id references a joint instance. This should be treated as an opaque handle.
68+
typedef struct b2JointId
6969
{
7070
int32_t index1;
7171
uint16_t world0;
7272
uint16_t revision;
73-
} b2ChainId;
73+
} b2JointId;
7474

7575
/// Use these to make your identifiers null.
7676
/// You may also use zero initialization to get null.
7777
static const b2WorldId b2_nullWorldId = B2_ZERO_INIT;
7878
static const b2BodyId b2_nullBodyId = B2_ZERO_INIT;
7979
static const b2ShapeId b2_nullShapeId = B2_ZERO_INIT;
80-
static const b2JointId b2_nullJointId = B2_ZERO_INIT;
8180
static const b2ChainId b2_nullChainId = B2_ZERO_INIT;
81+
static const b2JointId b2_nullJointId = B2_ZERO_INIT;
8282

8383
/// Macro to determine if any id is null.
8484
#define B2_IS_NULL( id ) ( id.index1 == 0 )
@@ -89,4 +89,56 @@ static const b2ChainId b2_nullChainId = B2_ZERO_INIT;
8989
/// Compare two ids for equality. Doesn't work for b2WorldId.
9090
#define B2_ID_EQUALS( id1, id2 ) ( id1.index1 == id2.index1 && id1.world0 == id2.world0 && id1.revision == id2.revision )
9191

92+
/// Store a body id into a uint64_t.
93+
B2_INLINE uint64_t b2StoreBodyId( b2BodyId id )
94+
{
95+
return ( (uint64_t)id.index1 << 32 ) | ( (uint64_t)id.world0 ) << 16 | (uint64_t)id.revision;
96+
}
97+
98+
/// Load a uint64_t into a body id.
99+
B2_INLINE b2BodyId b2LoadBodyId( uint64_t x )
100+
{
101+
b2BodyId id = { (int32_t)( x >> 32 ), (uint16_t)( x >> 16 ), (uint16_t)( x ) };
102+
return id;
103+
}
104+
105+
/// Store a shape id into a uint64_t.
106+
B2_INLINE uint64_t b2StoreShapeId( b2ShapeId id )
107+
{
108+
return ( (uint64_t)id.index1 << 32 ) | ( (uint64_t)id.world0 ) << 16 | (uint64_t)id.revision;
109+
}
110+
111+
/// Load a uint64_t into a shape id.
112+
B2_INLINE b2ShapeId b2LoadShapeId( uint64_t x )
113+
{
114+
b2ShapeId id = { (int32_t)( x >> 32 ), (uint16_t)( x >> 16 ), (uint16_t)( x ) };
115+
return id;
116+
}
117+
118+
/// Store a chain id into a uint64_t.
119+
B2_INLINE uint64_t b2StoreChainId( b2ChainId id )
120+
{
121+
return ( (uint64_t)id.index1 << 32 ) | ( (uint64_t)id.world0 ) << 16 | (uint64_t)id.revision;
122+
}
123+
124+
/// Load a uint64_t into a chain id.
125+
B2_INLINE b2ChainId b2LoadChainId( uint64_t x )
126+
{
127+
b2ChainId id = { (int32_t)( x >> 32 ), (uint16_t)( x >> 16 ), (uint16_t)( x ) };
128+
return id;
129+
}
130+
131+
/// Store a joint id into a uint64_t.
132+
B2_INLINE uint64_t b2StoreJointId( b2JointId id )
133+
{
134+
return ( (uint64_t)id.index1 << 32 ) | ( (uint64_t)id.world0 ) << 16 | (uint64_t)id.revision;
135+
}
136+
137+
/// Load a uint64_t into a joint id.
138+
B2_INLINE b2JointId b2LoadJointId( uint64_t x )
139+
{
140+
b2JointId id = { (int32_t)( x >> 32 ), (uint16_t)( x >> 16 ), (uint16_t)( x ) };
141+
return id;
142+
}
143+
92144
/**@}*/

0 commit comments

Comments
 (0)