Skip to content

Commit 6336023

Browse files
committed
initial cast benchmark
update mass options instead of automatic mass
1 parent b864f53 commit 6336023

File tree

7 files changed

+253
-18
lines changed

7 files changed

+253
-18
lines changed

include/box2d/box2d.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -466,8 +466,10 @@ B2_API b2ShapeId b2CreateCapsuleShape( b2BodyId bodyId, const b2ShapeDef* def, c
466466
/// @return the shape id for accessing the shape
467467
B2_API b2ShapeId b2CreatePolygonShape( b2BodyId bodyId, const b2ShapeDef* def, const b2Polygon* polygon );
468468

469-
/// Destroy a shape
470-
B2_API void b2DestroyShape( b2ShapeId shapeId );
469+
/// Destroy a shape. You may defer the body mass update which can improve performance if several shapes on a
470+
/// body are destroyed at once.
471+
/// @see b2Body_ApplyMassFromShapes
472+
B2_API void b2DestroyShape( b2ShapeId shapeId, bool updateBodyMass );
471473

472474
/// Shape identifier validation. Provides validation for up to 64K allocations.
473475
B2_API bool b2Shape_IsValid( b2ShapeId id );
@@ -492,9 +494,9 @@ B2_API void b2Shape_SetUserData( b2ShapeId shapeId, void* userData );
492494
B2_API void* b2Shape_GetUserData( b2ShapeId shapeId );
493495

494496
/// Set the mass density of a shape, typically in kg/m^2.
495-
/// This will not update the mass properties on the parent body.
497+
/// This will optionally update the mass properties on the parent body.
496498
/// @see b2ShapeDef::density, b2Body_ApplyMassFromShapes
497-
B2_API void b2Shape_SetDensity( b2ShapeId shapeId, float density );
499+
B2_API void b2Shape_SetDensity( b2ShapeId shapeId, float density, bool updateBodyMass );
498500

499501
/// Get the density of a shape, typically in kg/m^2
500502
B2_API float b2Shape_GetDensity( b2ShapeId shapeId );

include/box2d/types.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -220,10 +220,6 @@ typedef struct b2BodyDef
220220
/// Used to disable a body. A disabled body does not move or collide.
221221
bool isEnabled;
222222

223-
/// Automatically compute mass and related properties on this body from shapes.
224-
/// Triggers whenever a shape is add/removed/changed. Default is true.
225-
bool automaticMass;
226-
227223
/// This allows this body to bypass rotational speed limits. Should only be used
228224
/// for circular objects, like wheels.
229225
bool allowFastRotation;
@@ -367,6 +363,9 @@ typedef struct b2ShapeDef
367363
/// This is implicitly always true for sensors.
368364
bool forceContactCreation;
369365

366+
/// Should the body update the mass properties when this shape is created. Default is true.
367+
bool updateBodyMass;
368+
370369
/// Used internally to detect a valid definition. DO NOT SET.
371370
int32_t internalValue;
372371
} b2ShapeDef;

samples/sample_benchmark.cpp

Lines changed: 224 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,17 @@ class BenchmarkTumbler : public Sample
329329
polygon = b2MakeOffsetBox( 10.0f, 0.5f, { 0.0f, -10.0f }, b2Rot_identity );
330330
b2CreatePolygonShape( bodyId, &shapeDef, &polygon );
331331

332+
shapeDef.customColor = b2_colorBlueViolet;
333+
b2Circle circle = { { 5.0f, 5.0f }, 1.0f };
334+
b2CreateCircleShape( bodyId, &shapeDef, &circle );
335+
circle = { { 5.0f, -5.0f }, 1.0f };
336+
b2CreateCircleShape( bodyId, &shapeDef, &circle );
337+
circle = { { -5.0f, -5.0f }, 1.0f };
338+
b2CreateCircleShape( bodyId, &shapeDef, &circle );
339+
circle = { { -5.0f, 5.0f }, 1.0f };
340+
b2CreateCircleShape( bodyId, &shapeDef, &circle );
341+
342+
332343
// m_motorSpeed = 9.0f;
333344
m_motorSpeed = 25.0f;
334345

@@ -1436,8 +1447,8 @@ class BenchmarkCompound : public Sample
14361447
b2BodyDef bodyDef = b2DefaultBodyDef();
14371448
bodyDef.type = b2_dynamicBody;
14381449
// defer mass properties to avoid n-squared mass computations
1439-
bodyDef.automaticMass = false;
14401450
b2ShapeDef shapeDef = b2DefaultShapeDef();
1451+
shapeDef.updateBodyMass = false;
14411452

14421453
for ( int m = 0; m < count; ++m )
14431454
{
@@ -1498,13 +1509,14 @@ class BenchmarkKinematic : public Sample
14981509
b2BodyDef bodyDef = b2DefaultBodyDef();
14991510
bodyDef.type = b2_kinematicBody;
15001511
bodyDef.angularVelocity = 1.0f;
1501-
// defer mass properties to avoid n-squared mass computations
1502-
bodyDef.automaticMass = false;
15031512

15041513
b2ShapeDef shapeDef = b2DefaultShapeDef();
15051514
shapeDef.filter.categoryBits = 1;
15061515
shapeDef.filter.maskBits = 2;
15071516

1517+
// defer mass properties to avoid n-squared mass computations
1518+
shapeDef.updateBodyMass = false;
1519+
15081520
b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );
15091521

15101522
for ( int i = -span; i < span; ++i )
@@ -1529,3 +1541,212 @@ class BenchmarkKinematic : public Sample
15291541
};
15301542

15311543
static int sampleKinematic = RegisterSample( "Benchmark", "Kinematic", BenchmarkKinematic::Create );
1544+
1545+
#if 1
1546+
1547+
enum QueryType
1548+
{
1549+
e_rayCast,
1550+
e_shapeCast,
1551+
e_overlap,
1552+
};
1553+
1554+
class BenchmarkCast : public Sample
1555+
{
1556+
public:
1557+
explicit BenchmarkCast( Settings& settings )
1558+
: Sample( settings )
1559+
{
1560+
if ( settings.restart == false )
1561+
{
1562+
g_camera.m_center = { 500.0f, 500.0f };
1563+
g_camera.m_zoom = 25.0f * 21.0f;
1564+
}
1565+
1566+
m_queryType = e_rayCast;
1567+
m_ratio = 5.0f;
1568+
m_grid = 1.0f;
1569+
m_fill = 0.1f;
1570+
m_rowCount = g_sampleDebug ? 100 : 1000;
1571+
m_columnCount = g_sampleDebug ? 100 : 1000;
1572+
m_categoryBits = true;
1573+
1574+
BuildScene();
1575+
}
1576+
1577+
void BuildScene()
1578+
{
1579+
g_seed = 1234;
1580+
b2DestroyWorld( m_worldId );
1581+
b2WorldDef worldDef = b2DefaultWorldDef();
1582+
m_worldId = b2CreateWorld( &worldDef );
1583+
1584+
b2BodyDef bodyDef = b2DefaultBodyDef();
1585+
b2ShapeDef shapeDef = b2DefaultShapeDef();
1586+
1587+
float y = 0.0f;
1588+
1589+
for ( int i = 0; i < m_rowCount; ++i )
1590+
{
1591+
float x = 0.0f;
1592+
1593+
for ( int j = 0; j < m_columnCount; ++j )
1594+
{
1595+
float fillTest = RandomFloat( 0.0f, 1.0f );
1596+
if ( fillTest <= m_fill )
1597+
{
1598+
bodyDef.position = { x, y };
1599+
b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );
1600+
1601+
float ratio = RandomFloat( 1.0f, m_ratio );
1602+
float halfWidth = RandomFloat( 0.05f, 0.25f );
1603+
1604+
b2Polygon box;
1605+
if ( RandomFloat() > 0.0f )
1606+
{
1607+
box = b2MakeBox( ratio * halfWidth, halfWidth );
1608+
}
1609+
else
1610+
{
1611+
box = b2MakeBox( halfWidth, ratio * halfWidth );
1612+
}
1613+
1614+
int category = RandomInt( 1, 3 );
1615+
shapeDef.filter.categoryBits = category;
1616+
if ( category == 1 )
1617+
{
1618+
shapeDef.customColor = b2_colorBox2DBlue;
1619+
}
1620+
else if ( category == 2 )
1621+
{
1622+
shapeDef.customColor = b2_colorBox2DYellow;
1623+
}
1624+
else
1625+
{
1626+
shapeDef.customColor = b2_colorBox2DGreen;
1627+
}
1628+
1629+
b2CreatePolygonShape( bodyId, &shapeDef, &box );
1630+
}
1631+
1632+
x += m_grid;
1633+
}
1634+
1635+
y += m_grid;
1636+
}
1637+
}
1638+
1639+
void UpdateUI() override
1640+
{
1641+
float height = 320.0f;
1642+
ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once );
1643+
ImGui::SetNextWindowSize( ImVec2( 200.0f, height ) );
1644+
1645+
ImGui::Begin( "Cast", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize );
1646+
1647+
ImGui::PushItemWidth( 100.0f );
1648+
1649+
bool changed = false;
1650+
if ( ImGui::SliderInt( "rows", &m_rowCount, 0, 1000, "%d" ) )
1651+
{
1652+
changed = true;
1653+
}
1654+
1655+
if ( ImGui::SliderInt( "columns", &m_columnCount, 0, 1000, "%d" ) )
1656+
{
1657+
changed = true;
1658+
}
1659+
1660+
if ( ImGui::SliderFloat( "fill", &m_fill, 0.0f, 1.0f, "%.2f" ) )
1661+
{
1662+
changed = true;
1663+
}
1664+
1665+
if ( ImGui::SliderFloat( "grid", &m_grid, 0.5f, 2.0f, "%.2f" ) )
1666+
{
1667+
changed = true;
1668+
}
1669+
1670+
if ( ImGui::SliderFloat( "ratio", &m_ratio, 1.0f, 10.0f, "%.2f" ) )
1671+
{
1672+
changed = true;
1673+
}
1674+
1675+
if ( ImGui::Checkbox( "categories", &m_categoryBits) )
1676+
{
1677+
changed = true;
1678+
}
1679+
1680+
const char* queryTypes[] = { "Ray Cast", "Circle Cast", "Overlap" };
1681+
int queryType = int( m_queryType );
1682+
changed = changed || ImGui::Combo( "Query", &queryType, queryTypes, IM_ARRAYSIZE( queryTypes ) );
1683+
m_queryType = QueryType( queryType );
1684+
1685+
ImGui::PopItemWidth();
1686+
ImGui::End();
1687+
1688+
if ( changed )
1689+
{
1690+
BuildScene();
1691+
}
1692+
}
1693+
1694+
void Step( Settings& settings) override
1695+
{
1696+
Sample::Step( settings );
1697+
1698+
int sampleCount = g_sampleDebug ? 10 : 1000;
1699+
1700+
float extent = m_rowCount * m_grid;
1701+
b2QueryFilter filter = b2DefaultQueryFilter();
1702+
filter.maskBits = 1;
1703+
int hitCount = 0;
1704+
float ms = 0.0f;
1705+
1706+
if (m_queryType == e_rayCast)
1707+
{
1708+
b2Timer timer = b2CreateTimer();
1709+
1710+
b2Vec2 rayStart = b2Vec2_zero;
1711+
b2Vec2 rayEnd = b2Vec2_zero;
1712+
for (int i = 0; i < sampleCount; ++i)
1713+
{
1714+
rayStart = RandomVec2( 0.0f, extent );
1715+
rayEnd = RandomVec2( 0.0f, extent );
1716+
1717+
b2RayResult result = b2World_CastRayClosest( m_worldId, rayStart, b2Sub( rayEnd, rayStart ), filter );
1718+
hitCount += result.hit ? 1 : 0;
1719+
}
1720+
1721+
ms = b2GetMilliseconds( &timer );
1722+
1723+
g_draw.DrawSegment( rayStart, rayEnd, b2_colorBeige );
1724+
}
1725+
1726+
g_draw.DrawString( 5, m_textLine, "hit count = %03d", hitCount );
1727+
m_textLine += m_textIncrement;
1728+
1729+
g_draw.DrawString( 5, m_textLine, "ms = %.3f",ms );
1730+
m_textLine += m_textIncrement;
1731+
}
1732+
1733+
static Sample* Create( Settings& settings )
1734+
{
1735+
return new BenchmarkCast( settings );
1736+
}
1737+
1738+
QueryType m_queryType;
1739+
1740+
std::vector<b2Vec2> m_origins;
1741+
std::vector<b2Vec2> m_translations;
1742+
1743+
int m_rowCount, m_columnCount;
1744+
int m_updateType;
1745+
float m_fill;
1746+
float m_ratio;
1747+
float m_grid;
1748+
bool m_categoryBits;
1749+
};
1750+
1751+
static int sampleCast = RegisterSample( "Benchmark", "Cast", BenchmarkCast::Create );
1752+
#endif

samples/sample_events.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -672,9 +672,17 @@ class ContactEvent : public Sample
672672
m_debrisIds[index] = b2_nullBodyId;
673673
}
674674

675+
675676
for ( int i = 0; i < destroyCount; ++i )
676677
{
677-
b2DestroyShape( shapesToDestroy[i] );
678+
bool updateMass = false;
679+
b2DestroyShape( shapesToDestroy[i], updateMass );
680+
}
681+
682+
if (destroyCount > 0)
683+
{
684+
// Update mass just once
685+
b2Body_ApplyMassFromShapes( m_playerId );
678686
}
679687

680688
if ( settings.hertz > 0.0f && settings.pause == false )

src/body.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,6 @@ b2BodyId b2CreateBody( b2WorldId worldId, const b2BodyDef* def )
294294
body->fixedRotation = def->fixedRotation;
295295
body->isSpeedCapped = false;
296296
body->isMarked = false;
297-
body->automaticMass = def->automaticMass;
298297

299298
// dynamic and kinematic bodies that are enabled need a island
300299
if ( setId >= b2_awakeSet )

src/shape.c

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ b2ShapeId b2CreateShape( b2BodyId bodyId, const b2ShapeDef* def, const void* geo
165165

166166
b2Shape* shape = b2CreateShapeInternal( world, body, transform, def, geometry, shapeType );
167167

168-
if ( body->automaticMass == true )
168+
if ( def->updateBodyMass == true )
169169
{
170170
b2UpdateBodyMassData( world, body );
171171
}
@@ -262,7 +262,7 @@ void b2DestroyShapeInternal( b2World* world, b2Shape* shape, b2Body* body, bool
262262
b2ValidateSolverSets( world );
263263
}
264264

265-
void b2DestroyShape( b2ShapeId shapeId )
265+
void b2DestroyShape( b2ShapeId shapeId, bool updateBodyMass )
266266
{
267267
b2World* world = b2GetWorldLocked( shapeId.world0 );
268268

@@ -274,7 +274,7 @@ void b2DestroyShape( b2ShapeId shapeId )
274274
b2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId );
275275
b2DestroyShapeInternal( world, shape, body, wakeBodies );
276276

277-
if ( body->automaticMass == true )
277+
if ( updateBodyMass == true )
278278
{
279279
b2UpdateBodyMassData( world, body );
280280
}
@@ -911,7 +911,7 @@ b2CastOutput b2Shape_RayCast( b2ShapeId shapeId, const b2RayCastInput* input )
911911
return output;
912912
}
913913

914-
void b2Shape_SetDensity( b2ShapeId shapeId, float density )
914+
void b2Shape_SetDensity( b2ShapeId shapeId, float density, bool updateBodyMass )
915915
{
916916
B2_ASSERT( b2IsValid( density ) && density >= 0.0f );
917917

@@ -929,6 +929,12 @@ void b2Shape_SetDensity( b2ShapeId shapeId, float density )
929929
}
930930

931931
shape->density = density;
932+
933+
if (updateBodyMass == true)
934+
{
935+
b2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId );
936+
b2UpdateBodyMassData( world, body );
937+
}
932938
}
933939

934940
float b2Shape_GetDensity( b2ShapeId shapeId )

src/types.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ b2BodyDef b2DefaultBodyDef( void )
3737
def.enableSleep = true;
3838
def.isAwake = true;
3939
def.isEnabled = true;
40-
def.automaticMass = true;
4140
def.internalValue = B2_SECRET_COOKIE;
4241
return def;
4342
}
@@ -62,6 +61,7 @@ b2ShapeDef b2DefaultShapeDef( void )
6261
def.filter = b2DefaultFilter();
6362
def.enableSensorEvents = true;
6463
def.enableContactEvents = true;
64+
def.updateBodyMass = true;
6565
def.internalValue = B2_SECRET_COOKIE;
6666
return def;
6767
}

0 commit comments

Comments
 (0)