#include "draw.h"
#include "human.h"
#include "sample.h"
#include "settings.h"
#include "box2d/box2d.h"
#include "box2d/math_functions.h"
#include <GLFW/glfw3.h>
#include <imgui.h>
class BenchmarkBarrel : public Sample
{
public:
enum ShapeType
{
e_circleShape = 0,
e_caspuleShape,
e_mixShape,
e_compoundShape,
e_humanShape,
};
enum
{
e_maxColumns = 26,
e_maxRows = 130,
};
explicit BenchmarkBarrel( Settings& settings )
: Sample( settings )
{
if ( settings.restart == false )
{
g_camera.m_center = { 8.0f, 53.0f };
g_camera.m_zoom = 25.0f * 2.35f;
}
settings.drawJoints = false;
float groundSize = 25.0f;
{
b2BodyDef bodyDef = b2DefaultBodyDef();
b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );
b2Polygon box = b2MakeBox( groundSize, 1.2f );
b2ShapeDef shapeDef = b2DefaultShapeDef();
b2CreatePolygonShape( groundId, &shapeDef, &box );
box = b2MakeOffsetBox( 1.2f, 2.0f * groundSize, { -groundSize, 2.0f * groundSize }, b2Rot_identity );
b2CreatePolygonShape( groundId, &shapeDef, &box );
box = b2MakeOffsetBox( 1.2f, 2.0f * groundSize, { groundSize, 2.0f * groundSize }, b2Rot_identity );
b2CreatePolygonShape( groundId, &shapeDef, &box );
box = b2MakeOffsetBox( 800.0f, 10.0f, { 0.0f, -80.0f }, b2Rot_identity );
b2CreatePolygonShape( groundId, &shapeDef, &box );
}
for ( int i = 0; i < e_maxRows * e_maxColumns; ++i )
{
m_bodies[i] = b2_nullBodyId;
}
m_shapeType = e_circleShape;
CreateScene();
}
void CreateScene()
{
g_seed = 42;
for ( int i = 0; i < e_maxRows * e_maxColumns; ++i )
{
if ( B2_IS_NON_NULL( m_bodies[i] ) )
{
b2DestroyBody( m_bodies[i] );
m_bodies[i] = b2_nullBodyId;
}
if ( m_humans[i].m_isSpawned )
{
m_humans[i].Despawn();
}
}
m_columnCount = g_sampleDebug ? 10 : e_maxColumns;
m_rowCount = g_sampleDebug ? 40 : e_maxRows;
if ( m_shapeType == e_compoundShape )
{
if constexpr ( g_sampleDebug == false )
{
m_columnCount = 20;
}
}
else if ( m_shapeType == e_humanShape )
{
if constexpr ( g_sampleDebug )
{
m_rowCount = 5;
m_columnCount = 10;
}
else
{
m_columnCount = 15;
m_rowCount = 50;
}
}
float rad = 0.5f;
float shift = 1.15f;
float centerx = shift * m_columnCount / 2.0f;
float centery = shift / 2.0f;
b2BodyDef bodyDef = b2DefaultBodyDef();
bodyDef.type = b2_dynamicBody;
b2ShapeDef shapeDef = b2DefaultShapeDef();
shapeDef.density = 1.0f;
shapeDef.friction = 0.5f;
b2Capsule capsule = { { 0.0f, -0.25f }, { 0.0f, 0.25f }, rad };
b2Circle circle = { { 0.0f, 0.0f }, rad };
b2Vec2 points[3] = { { -0.1f, -0.5f }, { 0.1f, -0.5f }, { 0.0f, 0.5f } };
b2Hull wedgeHull = b2ComputeHull( points, 3 );
b2Polygon wedge = b2MakePolygon( &wedgeHull, 0.0f );
b2Vec2 vertices[3];
vertices[0] = { -1.0f, 0.0f };
vertices[1] = { 0.5f, 1.0f };
vertices[2] = { 0.0f, 2.0f };
b2Hull hull = b2ComputeHull( vertices, 3 );
b2Polygon left = b2MakePolygon( &hull, 0.0f );
vertices[0] = { 1.0f, 0.0f };
vertices[1] = { -0.5f, 1.0f };
vertices[2] = { 0.0f, 2.0f };
hull = b2ComputeHull( vertices, 3 );
b2Polygon right = b2MakePolygon( &hull, 0.0f );
float side = -0.1f;
float extray = 0.5f;
if ( m_shapeType == e_compoundShape )
{
extray = 0.25f;
side = 0.25f;
shift = 2.0f;
centerx = shift * m_columnCount / 2.0f - 1.0f;
}
else if ( m_shapeType == e_humanShape )
{
extray = 0.5f;
side = 0.55f;
shift = 2.5f;
centerx = shift * m_columnCount / 2.0f;
}
int index = 0;
for ( int i = 0; i < m_columnCount; ++i )
{
float x = i * shift - centerx;
for ( int j = 0; j < m_rowCount; ++j )
{
float y = j * ( shift + extray ) + centery + 2.0f;
bodyDef.position = { x + side, y };
side = -side;
if ( m_shapeType == e_circleShape )
{
m_bodies[index] = b2CreateBody( m_worldId, &bodyDef );
circle.radius = RandomFloat( 0.25f, 0.75f );
b2CreateCircleShape( m_bodies[index], &shapeDef, &circle );
}
else if ( m_shapeType == e_caspuleShape )
{
m_bodies[index] = b2CreateBody( m_worldId, &bodyDef );
capsule.radius = RandomFloat( 0.25f, 0.5f );
float length = RandomFloat( 0.25f, 1.0f );
capsule.center1 = { 0.0f, -0.5f * length };
capsule.center2 = { 0.0f, 0.5f * length };
b2CreateCapsuleShape( m_bodies[index], &shapeDef, &capsule );
}
else if ( m_shapeType == e_mixShape )
{
m_bodies[index] = b2CreateBody( m_worldId, &bodyDef );
int mod = index % 3;
if ( mod == 0 )
{
circle.radius = RandomFloat( 0.25f, 0.75f );
b2CreateCircleShape( m_bodies[index], &shapeDef, &circle );
}
else if ( mod == 1 )
{
capsule.radius = RandomFloat( 0.25f, 0.5f );
float length = RandomFloat( 0.25f, 1.0f );
capsule.center1 = { 0.0f, -0.5f * length };
capsule.center2 = { 0.0f, 0.5f * length };
b2CreateCapsuleShape( m_bodies[index], &shapeDef, &capsule );
}
else if ( mod == 2 )
{
float width = RandomFloat( 0.1f, 0.5f );
float height = RandomFloat( 0.5f, 0.75f );
b2Polygon box = b2MakeBox( width, height );
float value = RandomFloat( -1.0f, 1.0f );
box.radius = 0.25f * b2MaxFloat( 0.0f, value );
b2CreatePolygonShape( m_bodies[index], &shapeDef, &box );
}
else
{
wedge.radius = RandomFloat( 0.1f, 0.25f );
b2CreatePolygonShape( m_bodies[index], &shapeDef, &wedge );
}
}
else if ( m_shapeType == e_compoundShape )
{
m_bodies[index] = b2CreateBody( m_worldId, &bodyDef );
b2CreatePolygonShape( m_bodies[index], &shapeDef, &left );
b2CreatePolygonShape( m_bodies[index], &shapeDef, &right );
}
else if ( m_shapeType == e_humanShape )
{
m_humans[index].Spawn( m_worldId, bodyDef.position, 3.5f, 0.05f, 0.0f, 0.0f, index + 1, nullptr, false );
}
index += 1;
}
}
}
void UpdateUI() override
{
float height = 80.0f;
ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once );
ImGui::SetNextWindowSize( ImVec2( 220.0f, height ) );
ImGui::Begin( "Benchmark: Barrel", nullptr, ImGuiWindowFlags_NoResize );
bool changed = false;
const char* shapeTypes[] = { "Circle", "Capsule", "Mix", "Compound", "Human" };
int shapeType = int( m_shapeType );
changed = changed || ImGui::Combo( "Shape", &shapeType, shapeTypes, IM_ARRAYSIZE( shapeTypes ) );
m_shapeType = ShapeType( shapeType );
changed = changed || ImGui::Button( "Reset Scene" );
if ( changed )
{
CreateScene();
}
ImGui::End();
}
static Sample* Create( Settings& settings )
{
return new BenchmarkBarrel( settings );
}
b2BodyId m_bodies[e_maxRows * e_maxColumns];
Human m_humans[e_maxRows * e_maxColumns];
int m_columnCount;
int m_rowCount;
ShapeType m_shapeType;
};
static int benchmarkBarrel = RegisterSample( "Benchmark", "Barrel", BenchmarkBarrel::Create );
class BenchmarkTumbler : public Sample
{
public:
explicit BenchmarkTumbler( Settings& settings )
: Sample( settings )
{
if ( settings.restart == false )
{
g_camera.m_center = { 1.5f, 10.0f };
g_camera.m_zoom = 25.0f * 0.6f;
}
b2BodyId groundId;
{
b2BodyDef bodyDef = b2DefaultBodyDef();
groundId = b2CreateBody( m_worldId, &bodyDef );
}
{
b2BodyDef bodyDef = b2DefaultBodyDef();
bodyDef.type = b2_dynamicBody;
bodyDef.enableSleep = true;
bodyDef.position = { 0.0f, 10.0f };
b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );
b2ShapeDef shapeDef = b2DefaultShapeDef();
shapeDef.density = 50.0f;
b2Polygon polygon;
polygon = b2MakeOffsetBox( 0.5f, 10.0f, { 10.0f, 0.0f }, b2Rot_identity );
b2CreatePolygonShape( bodyId, &shapeDef, &polygon );
polygon = b2MakeOffsetBox( 0.5f, 10.0f, { -10.0f, 0.0f }, b2Rot_identity );
b2CreatePolygonShape( bodyId, &shapeDef, &polygon );
polygon = b2MakeOffsetBox( 10.0f, 0.5f, { 0.0f, 10.0f }, b2Rot_identity );
b2CreatePolygonShape( bodyId, &shapeDef, &polygon );
polygon = b2MakeOffsetBox( 10.0f, 0.5f, { 0.0f, -10.0f }, b2Rot_identity );
b2CreatePolygonShape( bodyId, &shapeDef, &polygon );
m_motorSpeed = 25.0f;
b2RevoluteJointDef jd = b2DefaultRevoluteJointDef();
jd.bodyIdA = groundId;
jd.bodyIdB = bodyId;
jd.localAnchorA = { 0.0f, 10.0f };
jd.localAnchorB = { 0.0f, 0.0f };
jd.referenceAngle = 0.0f;
jd.motorSpeed = ( b2_pi / 180.0f ) * m_motorSpeed;
jd.maxMotorTorque = 1e8f;
jd.enableMotor = true;
m_jointId = b2CreateRevoluteJoint( m_worldId, &jd );
}
int gridCount = g_sampleDebug ? 20 : 45;
b2Polygon polygon = b2MakeBox( 0.125f, 0.125f );
b2BodyDef bodyDef = b2DefaultBodyDef();
bodyDef.type = b2_dynamicBody;
b2ShapeDef shapeDef = b2DefaultShapeDef();
float y = -0.2f * gridCount + 10.0f;
for ( int i = 0; i < gridCount; ++i )
{
float x = -0.2f * gridCount;
for ( int j = 0; j < gridCount; ++j )
{
bodyDef.position = { x, y };
b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );
b2CreatePolygonShape( bodyId, &shapeDef, &polygon );
x += 0.4f;
}
y += 0.4f;
}
}
void UpdateUI() override
{
float height = 60.0f;
ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once );
ImGui::SetNextWindowSize( ImVec2( 200.0f, height ) );
ImGui::Begin( "Benchmark: Tumbler", nullptr, ImGuiWindowFlags_NoResize );
ImGui::PushItemWidth( 120.0f );
if ( ImGui::SliderFloat( "Speed", &m_motorSpeed, 0.0f, 100.0f, "%.f" ) )
{
b2RevoluteJoint_SetMotorSpeed( m_jointId, ( b2_pi / 180.0f ) * m_motorSpeed );
if ( m_motorSpeed > 0.0f )
{
b2Joint_WakeBodies( m_jointId );
}
}
ImGui::PopItemWidth();
ImGui::End();
}
static Sample* Create( Settings& settings )
{
return new BenchmarkTumbler( settings );
}
b2JointId m_jointId;
float m_motorSpeed;
};
static int benchmarkTumbler = RegisterSample( "Benchmark", "Tumbler", BenchmarkTumbler::Create );
class BenchmarkManyTumblers : public Sample
{
public:
explicit BenchmarkManyTumblers( Settings& settings )
: Sample( settings )
{
if ( settings.restart == false )
{
g_camera.m_center = { 1.0f, -5.5 };
g_camera.m_zoom = 25.0f * 3.4f;
settings.drawJoints = false;
}
b2BodyDef bodyDef = b2DefaultBodyDef();
m_groundId = b2CreateBody( m_worldId, &bodyDef );
m_rowCount = g_sampleDebug ? 2 : 19;
m_columnCount = g_sampleDebug ? 2 : 19;
m_tumblerIds = nullptr;
m_positions = nullptr;
m_tumblerCount = 0;
m_bodyIds = nullptr;
m_bodyCount = 0;
m_bodyIndex = 0;
m_angularSpeed = 25.0f;
CreateScene();
}
~BenchmarkManyTumblers() override
{
free( m_tumblerIds );
free( m_positions );
free( m_bodyIds );
}
void CreateTumbler( b2Vec2 position, int index )
{
b2BodyDef bodyDef = b2DefaultBodyDef();
bodyDef.type = b2_kinematicBody;
bodyDef.position = { position.x, position.y };
bodyDef.angularVelocity = ( b2_pi / 180.0f ) * m_angularSpeed;
b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );
m_tumblerIds[index] = bodyId;
b2ShapeDef shapeDef = b2DefaultShapeDef();
shapeDef.density = 50.0f;
b2Polygon polygon;
polygon = b2MakeOffsetBox( 0.25f, 2.0f, { 2.0f, 0.0f }, b2Rot_identity );
b2CreatePolygonShape( bodyId, &shapeDef, &polygon );
polygon = b2MakeOffsetBox( 0.25f, 2.0f, { -2.0f, 0.0f }, b2Rot_identity );
b2CreatePolygonShape( bodyId, &shapeDef, &polygon );
polygon = b2MakeOffsetBox( 2.0f, 0.25f, { 0.0f, 2.0f }, b2Rot_identity );
b2CreatePolygonShape( bodyId, &shapeDef, &polygon );
polygon = b2MakeOffsetBox( 2.0f, 0.25f, { 0.0f, -2.0f }, b2Rot_identity );
b2CreatePolygonShape( bodyId, &shapeDef, &polygon );
}
void CreateScene()
{
for ( int i = 0; i < m_bodyCount; ++i )
{
if ( B2_IS_NON_NULL( m_bodyIds[i] ) )
{
b2DestroyBody( m_bodyIds[i] );
}
}
for ( int i = 0; i < m_tumblerCount; ++i )
{
b2DestroyBody( m_tumblerIds[i] );
}
free( m_tumblerIds );
free( m_positions );
m_tumblerCount = m_rowCount * m_columnCount;
m_tumblerIds = static_cast<b2BodyId*>( malloc( m_tumblerCount * sizeof( b2BodyId ) ) );
m_positions = static_cast<b2Vec2*>( malloc( m_tumblerCount * sizeof( b2Vec2 ) ) );
int index = 0;
float x = -4.0f * m_rowCount;
for ( int i = 0; i < m_rowCount; ++i )
{
float y = -4.0f * m_columnCount;
for ( int j = 0; j < m_columnCount; ++j )
{
m_positions[index] = { x, y };
CreateTumbler( m_positions[index], index );
++index;
y += 8.0f;
}
x += 8.0f;
}
free( m_bodyIds );
int bodiesPerTumbler = g_sampleDebug ? 8 : 50;
m_bodyCount = bodiesPerTumbler * m_tumblerCount;
m_bodyIds = static_cast<b2BodyId*>( malloc( m_bodyCount * sizeof( b2BodyId ) ) );
memset( m_bodyIds, 0, m_bodyCount * sizeof( b2BodyId ) );
m_bodyIndex = 0;
}
void UpdateUI() override
{
float height = 110.0f;
ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once );
ImGui::SetNextWindowSize( ImVec2( 200.0f, height ) );
ImGui::Begin( "Benchmark: Many Tumblers", nullptr, ImGuiWindowFlags_NoResize );
ImGui::PushItemWidth( 100.0f );
bool changed = false;
changed = changed || ImGui::SliderInt( "Row Count", &m_rowCount, 1, 32 );
changed = changed || ImGui::SliderInt( "Column Count", &m_columnCount, 1, 32 );
if ( changed )
{
CreateScene();
}
if ( ImGui::SliderFloat( "Speed", &m_angularSpeed, 0.0f, 100.0f, "%.f" ) )
{
for ( int i = 0; i < m_tumblerCount; ++i )
{
b2Body_SetAngularVelocity( m_tumblerIds[i], ( b2_pi / 180.0f ) * m_angularSpeed );
b2Body_SetAwake( m_tumblerIds[i], true );
}
}
ImGui::PopItemWidth();
ImGui::End();
}
void Step( Settings& settings ) override
{
Sample::Step( settings );
if ( m_bodyIndex < m_bodyCount && ( m_stepCount & 0x7 ) == 0 )
{
b2ShapeDef shapeDef = b2DefaultShapeDef();
b2Capsule capsule = { { -0.1f, 0.0f }, { 0.1f, 0.0f }, 0.075f };
for ( int i = 0; i < m_tumblerCount; ++i )
{
assert( m_bodyIndex < m_bodyCount );
b2BodyDef bodyDef = b2DefaultBodyDef();
bodyDef.type = b2_dynamicBody;
bodyDef.position = m_positions[i];
m_bodyIds[m_bodyIndex] = b2CreateBody( m_worldId, &bodyDef );
b2CreateCapsuleShape( m_bodyIds[m_bodyIndex], &shapeDef, &capsule );
m_bodyIndex += 1;
}
}
}
static Sample* Create( Settings& settings )
{
return new BenchmarkManyTumblers( settings );
}
b2BodyId m_groundId;
int m_rowCount;
int m_columnCount;
b2BodyId* m_tumblerIds;
b2Vec2* m_positions;
int m_tumblerCount;
b2BodyId* m_bodyIds;
int m_bodyCount;
int m_bodyIndex;
float m_angularSpeed;
};
static int benchmarkManyTumblers = RegisterSample( "Benchmark", "Many Tumblers", BenchmarkManyTumblers::Create );
class BenchmarkLargePyramid : public Sample
{
public:
explicit BenchmarkLargePyramid( Settings& settings )
: Sample( settings )
{
if ( settings.restart == false )
{
g_camera.m_center = { 0.0f, 50.0f };
g_camera.m_zoom = 25.0f * 2.2f;
}
#ifdef NDEBUG
int baseCount = 100;
#else
int baseCount = 40;
#endif
{
b2BodyDef bodyDef = b2DefaultBodyDef();
bodyDef.position = { 0.0f, -1.0f };
b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );
b2Polygon box = b2MakeBox( 100.0f, 1.0f );
b2ShapeDef shapeDef = b2DefaultShapeDef();
b2CreatePolygonShape( groundId, &shapeDef, &box );
}
b2BodyDef bodyDef = b2DefaultBodyDef();
bodyDef.type = b2_dynamicBody;
b2ShapeDef shapeDef = b2DefaultShapeDef();
shapeDef.density = 1.0f;
float h = 0.5f;
b2Polygon box = b2MakeRoundedBox( h - 0.05f, h - 0.05f, 0.05f );
float shift = 1.0f * h;
for ( int i = 0; i < baseCount; ++i )
{
float y = ( 2.0f * i + 1.0f ) * shift;
for ( int j = i; j < baseCount; ++j )
{
float x = ( i + 1.0f ) * shift + 2.0f * ( j - i ) * shift - h * baseCount;
bodyDef.position = { x, y };
b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );
b2CreatePolygonShape( bodyId, &shapeDef, &box );
}
}
}
static Sample* Create( Settings& settings )
{
return new BenchmarkLargePyramid( settings );
}
};
static int benchmarkLargePyramid = RegisterSample( "Benchmark", "Large Pyramid", BenchmarkLargePyramid::Create );
class BenchmarkManyPyramids : public Sample
{
public:
explicit BenchmarkManyPyramids( Settings& settings )
: Sample( settings )
{
if ( settings.restart == false )
{
g_camera.m_center = { 16.0f, 110.0f };
g_camera.m_zoom = 25.0f * 5.0f;
}
m_extent = 0.5f;
m_round = 0.0f;
m_baseCount = 10;
m_rowCount = g_sampleDebug ? 4 : 20;
m_columnCount = g_sampleDebug ? 4 : 20;
m_groundId = b2_nullBodyId;
m_bodyIds = nullptr;
m_bodyCount = 0;
m_bodyIndex = 0;
CreateScene();
}
~BenchmarkManyPyramids() override
{
free( m_bodyIds );
}
void CreatePyramid( float centerX, float baseY )
{
b2BodyDef bodyDef = b2DefaultBodyDef();
bodyDef.type = b2_dynamicBody;
b2ShapeDef shapeDef = b2DefaultShapeDef();
shapeDef.density = 1.0f;
float h = m_extent - m_round;
b2Polygon box = b2MakeRoundedBox( h, h, m_round );
float shift = 1.0f * h;
for ( int i = 0; i < m_baseCount; ++i )
{
float y = ( 2.0f * i + 1.0f ) * shift + baseY;
for ( int j = i; j < m_baseCount; ++j )
{
float x = ( i + 1.0f ) * shift + 2.0f * ( j - i ) * shift + centerX - 0.5f;
bodyDef.position = { x, y };
assert( m_bodyIndex < m_bodyCount );
m_bodyIds[m_bodyIndex] = b2CreateBody( m_worldId, &bodyDef );
b2CreatePolygonShape( m_bodyIds[m_bodyIndex], &shapeDef, &box );
m_bodyIndex += 1;
}
}
}
void CreateScene()
{
if ( B2_IS_NON_NULL( m_groundId ) )
{
b2DestroyBody( m_groundId );
}
for ( int i = 0; i < m_bodyCount; ++i )
{
b2DestroyBody( m_bodyIds[i] );
}
free( m_bodyIds );
m_bodyCount = m_rowCount * m_columnCount * m_baseCount * ( m_baseCount + 1 ) / 2;
m_bodyIds = (b2BodyId*)malloc( m_bodyCount * sizeof( b2BodyId ) );
m_bodyIndex = 0;
b2BodyDef bodyDef = b2DefaultBodyDef();
m_groundId = b2CreateBody( m_worldId, &bodyDef );
float groundDeltaY = 2.0f * m_extent * ( m_baseCount + 1.0f );
float groundWidth = 2.0f * m_extent * m_columnCount * ( m_baseCount + 1.0f );
b2ShapeDef shapeDef = b2DefaultShapeDef();
float groundY = 0.0f;
for ( int i = 0; i < m_rowCount; ++i )
{
b2Segment segment = { { -0.5f * 2.0f * groundWidth, groundY }, { 0.5f * 2.0f * groundWidth, groundY } };
b2CreateSegmentShape( m_groundId, &shapeDef, &segment );
groundY += groundDeltaY;
}
float baseWidth = 2.0f * m_extent * m_baseCount;
float baseY = 0.0f;
for ( int i = 0; i < m_rowCount; ++i )
{
for ( int j = 0; j < m_columnCount; ++j )
{
float centerX = -0.5f * groundWidth + j * ( baseWidth + 2.0f * m_extent ) + m_extent;
CreatePyramid( centerX, baseY );
}
baseY += groundDeltaY;
}
assert( m_bodyIndex == m_bodyCount );
}
void UpdateUI() override
{
float height = 160.0f;
ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once );
ImGui::SetNextWindowSize( ImVec2( 200.0f, height ) );
ImGui::Begin( "Benchmark: Many Pyramids", nullptr, ImGuiWindowFlags_NoResize );
ImGui::PushItemWidth( 100.0f );
bool changed = false;
changed = changed || ImGui::SliderInt( "Row Count", &m_rowCount, 1, 32 );
changed = changed || ImGui::SliderInt( "Column Count", &m_columnCount, 1, 32 );
changed = changed || ImGui::SliderInt( "Base Count", &m_baseCount, 1, 30 );
changed = changed || ImGui::SliderFloat( "Round", &m_round, 0.0f, 0.4f, "%.1f" );
changed = changed || ImGui::Button( "Reset Scene" );
if ( changed )
{
CreateScene();
}
ImGui::PopItemWidth();
ImGui::End();
}
static Sample* Create( Settings& settings )
{
return new BenchmarkManyPyramids( settings );
}
b2BodyId m_groundId;
b2BodyId* m_bodyIds;
int m_bodyCount;
int m_bodyIndex;
int m_baseCount;
int m_rowCount;
int m_columnCount;
float m_round;
float m_extent;
};
static int benchmarkManyPyramids = RegisterSample( "Benchmark", "Many Pyramids", BenchmarkManyPyramids::Create );
class BenchmarkCreateDestroy : public Sample
{
public:
enum
{
e_maxBaseCount = 100,
e_maxBodyCount = e_maxBaseCount * ( e_maxBaseCount + 1 ) / 2
};
explicit BenchmarkCreateDestroy( Settings& settings )
: Sample( settings )
{
if ( settings.restart == false )
{
g_camera.m_center = { 0.0f, 50.0f };
g_camera.m_zoom = 25.0f * 2.2f;
}
float groundSize = 100.0f;
b2BodyDef bodyDef = b2DefaultBodyDef();
b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );
b2Polygon box = b2MakeBox( groundSize, 1.0f );
b2ShapeDef shapeDef = b2DefaultShapeDef();
b2CreatePolygonShape( groundId, &shapeDef, &box );
for ( int i = 0; i < e_maxBodyCount; ++i )
{
m_bodies[i] = b2_nullBodyId;
}
m_baseCount = g_sampleDebug ? 40 : 100;
m_iterations = g_sampleDebug ? 1 : 10;
m_bodyCount = 0;
}
void CreateScene()
{
for ( int i = 0; i < e_maxBodyCount; ++i )
{
if ( B2_IS_NON_NULL( m_bodies[i] ) )
{
b2DestroyBody( m_bodies[i] );
m_bodies[i] = b2_nullBodyId;
}
}
int count = m_baseCount;
float rad = 0.5f;
float shift = rad * 2.0f;
float centerx = shift * count / 2.0f;
float centery = shift / 2.0f + 1.0f;
b2BodyDef bodyDef = b2DefaultBodyDef();
bodyDef.type = b2_dynamicBody;
b2ShapeDef shapeDef = b2DefaultShapeDef();
shapeDef.density = 1.0f;
shapeDef.friction = 0.5f;
float h = 0.5f;
b2Polygon box = b2MakeRoundedBox( h, h, 0.0f );
int index = 0;
for ( int i = 0; i < count; ++i )
{
float y = i * shift + centery;
for ( int j = i; j < count; ++j )
{
float x = 0.5f * i * shift + ( j - i ) * shift - centerx;
bodyDef.position = { x, y };
assert( index < e_maxBodyCount );
m_bodies[index] = b2CreateBody( m_worldId, &bodyDef );
b2CreatePolygonShape( m_bodies[index], &shapeDef, &box );
index += 1;
}
}
m_bodyCount = index;
}
void Step( Settings& settings ) override
{
b2Timer timer = b2CreateTimer();
for ( int i = 0; i < m_iterations; ++i )
{
CreateScene();
}
float ms = b2GetMilliseconds( &timer );
g_draw.DrawString( 5, m_textLine, "milliseconds = %g", ms );
m_textLine += m_textIncrement;
Sample::Step( settings );
}
static Sample* Create( Settings& settings )
{
return new BenchmarkCreateDestroy( settings );
}
b2BodyId m_bodies[e_maxBodyCount];
int m_bodyCount;
int m_baseCount;
int m_iterations;
};
static int benchmarkCreateDestroy = RegisterSample( "Benchmark", "CreateDestroy", BenchmarkCreateDestroy::Create );
class BenchmarkSleep : public Sample
{
public:
enum
{
e_maxBaseCount = 100,
e_maxBodyCount = e_maxBaseCount * ( e_maxBaseCount + 1 ) / 2
};
explicit BenchmarkSleep( Settings& settings )
: Sample( settings )
{
if ( settings.restart == false )
{
g_camera.m_center = { 0.0f, 50.0f };
g_camera.m_zoom = 25.0f * 2.2f;
}
float groundSize = 100.0f;
b2BodyDef bodyDef = b2DefaultBodyDef();
b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );
b2Polygon box = b2MakeBox( groundSize, 1.0f );
b2ShapeDef shapeDef = b2DefaultShapeDef();
b2CreatePolygonShape( groundId, &shapeDef, &box );
for ( int i = 0; i < e_maxBodyCount; ++i )
{
m_bodies[i] = b2_nullBodyId;
}
m_baseCount = g_sampleDebug ? 40 : 100;
m_iterations = g_sampleDebug ? 1 : 41;
m_bodyCount = 0;
m_awake = false;
m_wakeTotal = 0.0f;
m_wakeCount = 0;
m_sleepTotal = 0.0f;
m_sleepCount = 0;
CreateScene();
}
void CreateScene()
{
for ( int i = 0; i < e_maxBodyCount; ++i )
{
if ( B2_IS_NON_NULL( m_bodies[i] ) )
{
b2DestroyBody( m_bodies[i] );
m_bodies[i] = b2_nullBodyId;
}
}
int count = m_baseCount;
float rad = 0.5f;
float shift = rad * 2.0f;
float centerx = shift * count / 2.0f;
float centery = shift / 2.0f + 1.0f;
b2BodyDef bodyDef = b2DefaultBodyDef();
bodyDef.type = b2_dynamicBody;
b2ShapeDef shapeDef = b2DefaultShapeDef();
shapeDef.density = 1.0f;
shapeDef.friction = 0.5f;
float h = 0.5f;
b2Polygon box = b2MakeRoundedBox( h, h, 0.0f );
int index = 0;
for ( int i = 0; i < count; ++i )
{
float y = i * shift + centery;
for ( int j = i; j < count; ++j )
{
float x = 0.5f * i * shift + ( j - i ) * shift - centerx;
bodyDef.position = { x, y };
assert( index < e_maxBodyCount );
m_bodies[index] = b2CreateBody( m_worldId, &bodyDef );
b2CreatePolygonShape( m_bodies[index], &shapeDef, &box );
index += 1;
}
}
m_bodyCount = index;
}
void Step( Settings& settings ) override
{
float timeStep = settings.hertz > 0.0f ? 1.0f / settings.hertz : float( 0.0f );
b2Timer timer = b2CreateTimer();
for ( int i = 0; i < m_iterations; ++i )
{
b2Body_SetAwake( m_bodies[0], m_awake );
if ( m_awake )
{
m_wakeTotal += b2GetMillisecondsAndReset( &timer );
m_wakeCount += 1;
}
else
{
m_sleepTotal += b2GetMillisecondsAndReset( &timer );
m_sleepCount += 1;
}
m_awake = !m_awake;
}
if ( m_wakeCount > 0 )
{
g_draw.DrawString( 5, m_textLine, "wake ave = %g ms", m_wakeTotal / m_wakeCount );
m_textLine += m_textIncrement;
}
if ( m_sleepCount > 0 )
{
g_draw.DrawString( 5, m_textLine, "sleep ave = %g ms", m_sleepTotal / m_sleepCount );
m_textLine += m_textIncrement;
}
Sample::Step( settings );
}
static Sample* Create( Settings& settings )
{
return new BenchmarkSleep( settings );
}
b2BodyId m_bodies[e_maxBodyCount];
int m_bodyCount;
int m_baseCount;
int m_iterations;
float m_wakeTotal;
float m_sleepTotal;
int m_wakeCount;
int m_sleepCount;
bool m_awake;
};
static int benchmarkSleep = RegisterSample( "Benchmark", "Sleep", BenchmarkSleep::Create );
class BenchmarkJointGrid : public Sample
{
public:
explicit BenchmarkJointGrid( Settings& settings )
: Sample( settings )
{
if ( settings.restart == false )
{
g_camera.m_center = { 60.0f, -57.0f };
g_camera.m_zoom = 25.0f * 2.5f;
}
constexpr int N = g_sampleDebug ? 10 : 100;
b2BodyId* bodies = static_cast<b2BodyId*>( malloc( N * N * sizeof( b2BodyId ) ) );
int index = 0;
b2ShapeDef shapeDef = b2DefaultShapeDef();
shapeDef.density = 1.0f;
shapeDef.filter.categoryBits = 2;
shapeDef.filter.maskBits = ~2u;
b2Circle circle = { { 0.0f, 0.0f }, 0.4f };
b2RevoluteJointDef jd = b2DefaultRevoluteJointDef();
b2BodyDef bodyDef = b2DefaultBodyDef();
for ( int k = 0; k < N; ++k )
{
for ( int i = 0; i < N; ++i )
{
float fk = (float)k;
float fi = (float)i;
if ( k >= N / 2 - 3 && k <= N / 2 + 3 && i == 0 )
{
bodyDef.type = b2_staticBody;
}
else
{
bodyDef.type = b2_dynamicBody;
}
bodyDef.position = { fk, -fi };
b2BodyId body = b2CreateBody( m_worldId, &bodyDef );
b2CreateCircleShape( body, &shapeDef, &circle );
if ( i > 0 )
{
jd.bodyIdA = bodies[index - 1];
jd.bodyIdB = body;
jd.localAnchorA = { 0.0f, -0.5f };
jd.localAnchorB = { 0.0f, 0.5f };
b2CreateRevoluteJoint( m_worldId, &jd );
}
if ( k > 0 )
{
jd.bodyIdA = bodies[index - N];
jd.bodyIdB = body;
jd.localAnchorA = { 0.5f, 0.0f };
jd.localAnchorB = { -0.5f, 0.0f };
b2CreateRevoluteJoint( m_worldId, &jd );
}
bodies[index++] = body;
}
}
free( bodies );
m_gravity = 10.0f;
}
void UpdateUI() override
{
float height = 60.0f;
ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once );
ImGui::SetNextWindowSize( ImVec2( 240.0f, height ) );
ImGui::Begin( "Benchmark: Joint Grid", nullptr, ImGuiWindowFlags_NoResize );
if ( ImGui::SliderFloat( "gravity", &m_gravity, 0.0f, 20.0f, "%.1f" ) )
{
b2World_SetGravity( m_worldId, { 0.0f, -m_gravity } );
}
ImGui::End();
}
static Sample* Create( Settings& settings )
{
return new BenchmarkJointGrid( settings );
}
float m_gravity;
};
static int benchmarkJointGridIndex = RegisterSample( "Benchmark", "Joint Grid", BenchmarkJointGrid::Create );
class BenchmarkSmash : public Sample
{
public:
explicit BenchmarkSmash( Settings& settings )
: Sample( settings )
{
if ( settings.restart == false )
{
g_camera.m_center = { 60.0f, 6.0f };
g_camera.m_zoom = 25.0f * 1.6f;
}
b2World_SetGravity( m_worldId, b2Vec2_zero );
{
b2Polygon box = b2MakeBox( 4.0f, 4.0f );
b2BodyDef bodyDef = b2DefaultBodyDef();
bodyDef.type = b2_dynamicBody;
bodyDef.position = { -20.0f, 0.0f };
bodyDef.linearVelocity = { 40.0f, 0.0f };
b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );
b2ShapeDef shapeDef = b2DefaultShapeDef();
shapeDef.density = 8.0f;
b2CreatePolygonShape( bodyId, &shapeDef, &box );
}
m_created = false;
}
void CreateScene1()
{
ImGuiIO& io = ImGui::GetIO();
if ( io.Fonts->Fonts.size() == 0 )
{
return;
}
const ImFont* font = io.Fonts->Fonts[0];
const unsigned char* pixels = font->ContainerAtlas->TexPixelsAlpha8;
int width = font->ContainerAtlas->TexWidth;
int height = font->ContainerAtlas->TexHeight;
float scale = 0.1f;
b2BodyDef bodyDef = b2DefaultBodyDef();
bodyDef.type = b2_dynamicBody;
bodyDef.isAwake = false;
b2ShapeDef shapeDef = b2DefaultShapeDef();
for ( int i = 0; i < height; ++i )
{
for ( int j = 0; j < width; ++j )
{
unsigned char value = pixels[i * width + j];
if ( value != 0 && value != 0xFF )
{
value += 0;
}
if ( value > 50 )
{
b2Polygon square = b2MakeSquare( 0.95f * scale * ( value / 255.0f ) );
bodyDef.position = { 2.0f * j * scale, 2.0f * ( height - i ) * scale - 10.0f };
b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );
b2CreatePolygonShape( bodyId, &shapeDef, &square );
}
}
}
m_created = true;
}
void CreateScene2()
{
ImGuiIO& io = ImGui::GetIO();
if ( io.Fonts->Fonts.size() == 0 )
{
return;
}
const ImFont* font = io.Fonts->Fonts.back();
const unsigned char* pixels = font->ContainerAtlas->TexPixelsAlpha8;
int width = font->ContainerAtlas->TexWidth;
int height = font->ContainerAtlas->TexHeight;
int fontSize = font->Ascent;
float scale = 0.1f;
b2BodyDef bodyDef = b2DefaultBodyDef();
bodyDef.type = b2_dynamicBody;
bodyDef.isAwake = false;
b2ShapeDef shapeDef = b2DefaultShapeDef();
const char* text = "I";
int n = (int)strlen( text );
float zoom = 1.0f;
float x = 0.0f;
for ( int k = 0; k < n; ++k )
{
const ImFontGlyph* glyph = font->FindGlyph( text[k] );
float x1 = glyph->X0;
float x2 = glyph->X1;
float y1 = glyph->Y0;
float y2 = glyph->Y1;
float u1 = glyph->U0;
float v1 = glyph->V0;
float u2 = glyph->U1;
float v2 = glyph->V1;
float w = zoom * ( x2 - x1 );
float h = zoom * ( y2 - y1 );
int gridx = int( w );
int gridy = int( h );
for ( int i = 0; i < gridy; ++i )
{
float v = v1 + i / h * ( v2 - v1 );
int iy = int( v * height );
for ( int j = 0; j < gridx; ++j )
{
float u = u1 + j / w * ( u2 - u1 );
int ix = int( u * width );
unsigned char value = pixels[iy * width + ix];
if ( value > 50 )
{
b2Polygon square = b2MakeSquare( 0.9f * scale * value / 255.0f );
bodyDef.position = { x + 2.0f * ( zoom * x1 + j ) * scale, -2.0f * ( zoom * y1 + i ) * scale + 13.0f };
b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );
b2CreatePolygonShape( bodyId, &shapeDef, &square );
}
}
}
x += 2.0f * zoom * scale * glyph->AdvanceX;
}
m_created = true;
}
void CreateScene3()
{
float d = 0.4f;
b2Polygon box = b2MakeSquare( 0.5f * d );
b2BodyDef bodyDef = b2DefaultBodyDef();
bodyDef.type = b2_dynamicBody;
bodyDef.isAwake = false;
b2ShapeDef shapeDef = b2DefaultShapeDef();
int columns = g_sampleDebug ? 20 : 120;
int rows = g_sampleDebug ? 10 : 80;
for ( int i = 0; i < columns; ++i )
{
for ( int j = 0; j < rows; ++j )
{
bodyDef.position.x = i * d + 30.0f;
bodyDef.position.y = ( j - rows / 2.0f ) * d;
b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );
b2CreatePolygonShape( bodyId, &shapeDef, &box );
}
}
m_created = true;
}
void Step( Settings& settings ) override
{
Sample::Step( settings );
if ( m_created == false )
{
CreateScene3();
}
}
static Sample* Create( Settings& settings )
{
return new BenchmarkSmash( settings );
}
bool m_created;
};
static int sampleSmash = RegisterSample( "Benchmark", "Smash", BenchmarkSmash::Create );
class BenchmarkCompound : public Sample
{
public:
explicit BenchmarkCompound( Settings& settings )
: Sample( settings )
{
if ( settings.restart == false )
{
g_camera.m_center = { 18.0f, 115.0f };
g_camera.m_zoom = 25.0f * 5.5f;
}
float grid = 1.0f;
#ifdef NDEBUG
int height = 200;
int width = 200;
#else
int height = 100;
int width = 100;
#endif
{
b2BodyDef bodyDef = b2DefaultBodyDef();
b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );
b2ShapeDef shapeDef = b2DefaultShapeDef();
for ( int i = 0; i < height; ++i )
{
float y = grid * i;
for ( int j = i; j < width; ++j )
{
float x = grid * j;
b2Polygon square = b2MakeOffsetBox( 0.5f * grid, 0.5f * grid, { x, y }, b2Rot_identity );
b2CreatePolygonShape( groundId, &shapeDef, &square );
}
}
for ( int i = 0; i < height; ++i )
{
float y = grid * i;
for ( int j = i; j < width; ++j )
{
float x = -grid * j;
b2Polygon square = b2MakeOffsetBox( 0.5f * grid, 0.5f * grid, { x, y }, b2Rot_identity );
b2CreatePolygonShape( groundId, &shapeDef, &square );
}
}
}
{
#ifdef NDEBUG
int span = 20;
int count = 5;
#else
int span = 5;
int count = 5;
#endif
b2BodyDef bodyDef = b2DefaultBodyDef();
bodyDef.type = b2_dynamicBody;
bodyDef.automaticMass = false;
b2ShapeDef shapeDef = b2DefaultShapeDef();
for ( int m = 0; m < count; ++m )
{
float ybody = ( 100.0f + m * span ) * grid;
for ( int n = 0; n < count; ++n )
{
float xbody = -0.5f * grid * count * span + n * span * grid;
bodyDef.position = { xbody, ybody };
b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );
for ( int i = 0; i < span; ++i )
{
float y = i * grid;
for ( int j = 0; j < span; ++j )
{
float x = j * grid;
b2Polygon square = b2MakeOffsetBox( 0.5f * grid, 0.5f * grid, { x, y }, b2Rot_identity );
b2CreatePolygonShape( bodyId, &shapeDef, &square );
}
}
b2Body_ApplyMassFromShapes( bodyId );
}
}
}
}
static Sample* Create( Settings& settings )
{
return new BenchmarkCompound( settings );
}
};
static int sampleCompound = RegisterSample( "Benchmark", "Compound", BenchmarkCompound::Create );
class BenchmarkKinematic : public Sample
{
public:
explicit BenchmarkKinematic( Settings& settings )
: Sample( settings )
{
if ( settings.restart == false )
{
g_camera.m_center = { 0.0f, 0.0f };
g_camera.m_zoom = 150.0f;
}
float grid = 1.0f;
#ifdef NDEBUG
int span = 100;
#else
int span = 20;
#endif
b2BodyDef bodyDef = b2DefaultBodyDef();
bodyDef.type = b2_kinematicBody;
bodyDef.angularVelocity = 1.0f;
bodyDef.automaticMass = false;
b2ShapeDef shapeDef = b2DefaultShapeDef();
shapeDef.filter.categoryBits = 1;
shapeDef.filter.maskBits = 2;
b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );
for ( int i = -span; i < span; ++i )
{
float y = i * grid;
for ( int j = -span; j < span; ++j )
{
float x = j * grid;
b2Polygon square = b2MakeOffsetBox( 0.5f * grid, 0.5f * grid, { x, y }, b2Rot_identity );
b2CreatePolygonShape( bodyId, &shapeDef, &square );
}
}
b2Body_ApplyMassFromShapes( bodyId );
}
static Sample* Create( Settings& settings )
{
return new BenchmarkKinematic( settings );
}
};
static int sampleKinematic = RegisterSample( "Benchmark", "Kinematic", BenchmarkKinematic::Create );