#include "draw.h"
#include "sample.h"
#include "settings.h"
#include "box2d/box2d.h"
#include "box2d/math_functions.h"
#include <GLFW/glfw3.h>
#include <imgui.h>
struct HitEvent
{
b2Vec2 point;
float speed;
int stepIndex;
};
class BounceHouse : public Sample
{
public:
enum ShapeType
{
e_circleShape = 0,
e_capsuleShape,
e_boxShape
};
explicit BounceHouse( Settings& settings )
: Sample( settings )
{
if ( settings.restart == false )
{
g_camera.m_center = { 0.0f, 0.0f };
g_camera.m_zoom = 25.0f * 0.45f;
}
b2BodyDef bodyDef = b2DefaultBodyDef();
b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );
b2ShapeDef shapeDef = b2DefaultShapeDef();
{
b2Segment segment = { { -10.0f, -10.0f }, { 10.0f, -10.0f } };
b2CreateSegmentShape( groundId, &shapeDef, &segment );
}
{
b2Segment segment = { { 10.0f, -10.0f }, { 10.0f, 10.0f } };
b2CreateSegmentShape( groundId, &shapeDef, &segment );
}
{
b2Segment segment = { { 10.0f, 10.0f }, { -10.0f, 10.0f } };
b2CreateSegmentShape( groundId, &shapeDef, &segment );
}
{
b2Segment segment = { { -10.0f, 10.0f }, { -10.0f, -10.0f } };
b2CreateSegmentShape( groundId, &shapeDef, &segment );
}
m_shapeType = e_circleShape;
m_bodyId = b2_nullBodyId;
m_enableHitEvents = true;
memset( m_hitEvents, 0, sizeof( m_hitEvents ) );
Launch();
}
void Launch()
{
if ( B2_IS_NON_NULL( m_bodyId ) )
{
b2DestroyBody( m_bodyId );
}
b2BodyDef bodyDef = b2DefaultBodyDef();
bodyDef.type = b2_dynamicBody;
bodyDef.linearVelocity = { 10.0f, 20.0f };
bodyDef.position = { 0.0f, 0.0f };
bodyDef.gravityScale = 0.0f;
bodyDef.allowFastRotation = m_shapeType == e_circleShape;
m_bodyId = b2CreateBody( m_worldId, &bodyDef );
b2ShapeDef shapeDef = b2DefaultShapeDef();
shapeDef.density = 1.0f;
shapeDef.restitution = 1.2f;
shapeDef.friction = 0.3f;
shapeDef.enableHitEvents = m_enableHitEvents;
if ( m_shapeType == e_circleShape )
{
b2Circle circle = { { 0.0f, 0.0f }, 0.5f };
b2CreateCircleShape( m_bodyId, &shapeDef, &circle );
}
else if ( m_shapeType == e_capsuleShape )
{
b2Capsule capsule = { { -0.5f, 0.0f }, { 0.5f, 0.0 }, 0.25f };
b2CreateCapsuleShape( m_bodyId, &shapeDef, &capsule );
}
else
{
float h = 0.1f;
b2Polygon box = b2MakeBox( 20.0f * h, h );
b2CreatePolygonShape( m_bodyId, &shapeDef, &box );
}
}
void UpdateUI() override
{
float height = 100.0f;
ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once );
ImGui::SetNextWindowSize( ImVec2( 240.0f, height ) );
ImGui::Begin( "Bounce House", nullptr, ImGuiWindowFlags_NoResize );
const char* shapeTypes[] = { "Circle", "Capsule", "Box" };
int shapeType = int( m_shapeType );
if ( ImGui::Combo( "Shape", &shapeType, shapeTypes, IM_ARRAYSIZE( shapeTypes ) ) )
{
m_shapeType = ShapeType( shapeType );
Launch();
}
if ( ImGui::Checkbox( "hit events", &m_enableHitEvents ) )
{
b2Body_EnableHitEvents( m_bodyId, m_enableHitEvents );
}
ImGui::End();
}
void Step( Settings& settings ) override
{
Sample::Step( settings );
b2ContactEvents events = b2World_GetContactEvents( m_worldId );
for ( int i = 0; i < events.hitCount; ++i )
{
b2ContactHitEvent* event = events.hitEvents + i;
HitEvent* e = m_hitEvents + 0;
for ( int j = 1; j < 4; ++j )
{
if ( m_hitEvents[j].stepIndex < e->stepIndex )
{
e = m_hitEvents + j;
}
}
e->point = event->point;
e->speed = event->approachSpeed;
e->stepIndex = m_stepCount;
}
for ( int i = 0; i < 4; ++i )
{
HitEvent* e = m_hitEvents + i;
if ( e->stepIndex > 0 && m_stepCount <= e->stepIndex + 30 )
{
g_draw.DrawCircle( e->point, 0.1f, b2_colorOrangeRed );
g_draw.DrawString( e->point, "%.1f", e->speed );
}
}
}
static Sample* Create( Settings& settings )
{
return new BounceHouse( settings );
}
HitEvent m_hitEvents[4];
b2BodyId m_bodyId;
ShapeType m_shapeType;
bool m_enableHitEvents;
};
static int sampleBounceHouse = RegisterSample( "Continuous", "Bounce House", BounceHouse::Create );
class FastChain : public Sample
{
public:
explicit FastChain( Settings& settings )
: Sample( settings )
{
if ( settings.restart == false )
{
g_camera.m_center = { 0.0f, 0.0f };
g_camera.m_zoom = 25.0f * 0.35f;
}
b2BodyDef bodyDef = b2DefaultBodyDef();
bodyDef.position = { 0.0f, -6.0f };
b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );
b2Vec2 points[4] = { { -10.0f, -2.0f }, { 10.0f, -2.0f }, { 10.0f, 1.0f }, { -10.0f, 1.0f } };
b2ChainDef chainDef = b2DefaultChainDef();
chainDef.points = points;
chainDef.count = 4;
chainDef.isLoop = true;
b2CreateChain( groundId, &chainDef );
m_bodyId = b2_nullBodyId;
Launch();
}
void Launch()
{
if ( B2_IS_NON_NULL( m_bodyId ) )
{
b2DestroyBody( m_bodyId );
}
b2BodyDef bodyDef = b2DefaultBodyDef();
bodyDef.type = b2_dynamicBody;
bodyDef.linearVelocity = { 0.0f, -200.0f };
bodyDef.position = { 0.0f, 10.0f };
bodyDef.gravityScale = 1.0f;
m_bodyId = b2CreateBody( m_worldId, &bodyDef );
b2ShapeDef shapeDef = b2DefaultShapeDef();
b2Circle circle = { { 0.0f, 0.0f }, 0.5f };
b2CreateCircleShape( m_bodyId, &shapeDef, &circle );
}
void UpdateUI() override
{
float height = 70.0f;
ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once );
ImGui::SetNextWindowSize( ImVec2( 240.0f, height ) );
ImGui::Begin( "Fast Chain", nullptr, ImGuiWindowFlags_NoResize );
if ( ImGui::Button( "Launch" ) )
{
Launch();
}
ImGui::End();
}
static Sample* Create( Settings& settings )
{
return new FastChain( settings );
}
b2BodyId m_bodyId;
};
static int sampleFastChainHouse = RegisterSample( "Continuous", "Fast Chain", FastChain::Create );
class SkinnyBox : public Sample
{
public:
explicit SkinnyBox( Settings& settings )
: Sample( settings )
{
if ( settings.restart == false )
{
g_camera.m_center = { 1.0f, 5.0f };
g_camera.m_zoom = 25.0f * 0.25f;
}
{
b2BodyDef bodyDef = b2DefaultBodyDef();
b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );
b2Segment segment = { { -10.0f, 0.0f }, { 10.0f, 0.0f } };
b2ShapeDef shapeDef = b2DefaultShapeDef();
shapeDef.friction = 0.9f;
b2CreateSegmentShape( groundId, &shapeDef, &segment );
b2Polygon box = b2MakeOffsetBox( 0.1f, 1.0f, { 0.0f, 1.0f }, b2Rot_identity );
b2CreatePolygonShape( groundId, &shapeDef, &box );
}
m_autoTest = false;
m_bullet = false;
m_capsule = false;
m_bodyId = b2_nullBodyId;
m_bulletId = b2_nullBodyId;
Launch();
}
void Launch()
{
if ( B2_IS_NON_NULL( m_bodyId ) )
{
b2DestroyBody( m_bodyId );
}
if ( B2_IS_NON_NULL( m_bulletId ) )
{
b2DestroyBody( m_bulletId );
}
m_angularVelocity = RandomFloat( -50.0f, 50.0f );
b2BodyDef bodyDef = b2DefaultBodyDef();
bodyDef.type = b2_dynamicBody;
bodyDef.position = { 0.0f, 8.0f };
bodyDef.angularVelocity = m_angularVelocity;
bodyDef.linearVelocity = { 0.0f, -100.0f };
b2ShapeDef shapeDef = b2DefaultShapeDef();
shapeDef.density = 1.0f;
shapeDef.friction = 0.9f;
m_bodyId = b2CreateBody( m_worldId, &bodyDef );
if ( m_capsule )
{
b2Capsule capsule = { { 0.0f, -1.0f }, { 0.0f, 1.0f }, 0.1f };
b2CreateCapsuleShape( m_bodyId, &shapeDef, &capsule );
}
else
{
b2Polygon polygon = b2MakeBox( 2.0f, 0.05f );
b2CreatePolygonShape( m_bodyId, &shapeDef, &polygon );
}
if ( m_bullet )
{
b2Polygon polygon = b2MakeBox( 0.25f, 0.25f );
m_x = RandomFloat( -1.0f, 1.0f );
bodyDef.position = { m_x, 10.0f };
bodyDef.linearVelocity = { 0.0f, -50.0f };
m_bulletId = b2CreateBody( m_worldId, &bodyDef );
b2CreatePolygonShape( m_bulletId, &shapeDef, &polygon );
}
}
void UpdateUI() override
{
float height = 110.0f;
ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once );
ImGui::SetNextWindowSize( ImVec2( 140.0f, height ) );
ImGui::Begin( "Skinny Box", nullptr, ImGuiWindowFlags_NoResize );
ImGui::Checkbox( "Capsule", &m_capsule );
if ( ImGui::Button( "Launch" ) )
{
Launch();
}
ImGui::Checkbox( "Auto Test", &m_autoTest );
ImGui::End();
}
void Step( Settings& settings ) override
{
Sample::Step( settings );
if ( m_autoTest && m_stepCount % 60 == 0 )
{
Launch();
}
}
static Sample* Create( Settings& settings )
{
return new SkinnyBox( settings );
}
b2BodyId m_bodyId, m_bulletId;
float m_angularVelocity;
float m_x;
bool m_capsule;
bool m_autoTest;
bool m_bullet;
};
static int sampleSkinnyBox = RegisterSample( "Continuous", "Skinny Box", SkinnyBox::Create );
class SpeculativeBug : public Sample
{
public:
explicit SpeculativeBug( Settings& settings )
: Sample( settings )
{
if ( settings.restart == false )
{
g_camera.m_center = { 1.0f, 5.0f };
g_camera.m_zoom = 25.0f * 0.25f;
}
{
b2BodyDef bodyDef = b2DefaultBodyDef();
b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );
b2Segment segment = { { -10.0f, 0.0f }, { 10.0f, 0.0f } };
b2ShapeDef shapeDef = b2DefaultShapeDef();
b2CreateSegmentShape( groundId, &shapeDef, &segment );
shapeDef.friction = 0.0f;
b2Polygon box = b2MakeOffsetBox( 0.05f, 1.0f, { 0.0f, 1.0f }, b2Rot_identity );
b2CreatePolygonShape( groundId, &shapeDef, &box );
}
b2BodyDef bodyDef = b2DefaultBodyDef();
bodyDef.type = b2_dynamicBody;
for (int i = 0; i < 2; ++i)
{
if (i == 0)
{
bodyDef.position = { -0.8f, 0.25f };
bodyDef.isAwake = false;
}
else
{
bodyDef.position = { 0.8f, 2.0f };
bodyDef.isAwake = true;
}
b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );
b2ShapeDef shapeDef = b2DefaultShapeDef();
b2Capsule capsule = { { -0.5f, 0.0f }, { 0.5f, 0.0f }, 0.25f };
b2CreateCapsuleShape( bodyId, &shapeDef, &capsule );
}
}
static Sample* Create( Settings& settings )
{
return new SpeculativeBug( settings );
}
};
static int sampleSpeculativeBug = RegisterSample( "Continuous", "Speculative Bug", SpeculativeBug::Create );
class GhostCollision : public Sample
{
public:
enum ShapeType
{
e_circleShape = 0,
e_capsuleShape,
e_boxShape
};
explicit GhostCollision( Settings& settings )
: Sample( settings )
{
if ( settings.restart == false )
{
g_camera.m_center = { 1.5f, 16.0f };
g_camera.m_zoom = 25.0f * 0.8f;
}
m_groundId = b2_nullBodyId;
m_bodyId = b2_nullBodyId;
m_shapeId = b2_nullShapeId;
m_shapeType = e_circleShape;
m_round = 0.0f;
m_friction = 0.2f;
m_bevel = 0.0f;
m_useChain = true;
CreateScene();
Launch();
}
void CreateScene()
{
if ( B2_IS_NON_NULL( m_groundId ) )
{
b2DestroyBody( m_groundId );
}
m_shapeId = b2_nullShapeId;
b2BodyDef bodyDef = b2DefaultBodyDef();
m_groundId = b2CreateBody( m_worldId, &bodyDef );
float m = 1.0f / sqrt( 2.0f );
float mm = 2.0f * ( sqrt( 2.0f ) - 1.0f );
float hx = 4.0f, hy = 0.25f;
if ( m_useChain )
{
b2Vec2 points[20];
points[0] = { -3.0f * hx, hy };
points[1] = b2Add( points[0], { -2.0f * hx * m, 2.0f * hx * m } );
points[2] = b2Add( points[1], { -2.0f * hx * m, 2.0f * hx * m } );
points[3] = b2Add( points[2], { -2.0f * hx * m, 2.0f * hx * m } );
points[4] = b2Add( points[3], { -2.0f * hy * m, -2.0f * hy * m } );
points[5] = b2Add( points[4], { 2.0f * hx * m, -2.0f * hx * m } );
points[6] = b2Add( points[5], { 2.0f * hx * m, -2.0f * hx * m } );
points[7] =
b2Add( points[6], { 2.0f * hx * m + 2.0f * hy * ( 1.0f - m ), -2.0f * hx * m - 2.0f * hy * ( 1.0f - m ) } );
points[8] = b2Add( points[7], { 2.0f * hx + hy * mm, 0.0f } );
points[9] = b2Add( points[8], { 2.0f * hx, 0.0f } );
points[10] = b2Add( points[9], { 2.0f * hx + hy * mm, 0.0f } );
points[11] =
b2Add( points[10], { 2.0f * hx * m + 2.0f * hy * ( 1.0f - m ), 2.0f * hx * m + 2.0f * hy * ( 1.0f - m ) } );
points[12] = b2Add( points[11], { 2.0f * hx * m, 2.0f * hx * m } );
points[13] = b2Add( points[12], { 2.0f * hx * m, 2.0f * hx * m } );
points[14] = b2Add( points[13], { -2.0f * hy * m, 2.0f * hy * m } );
points[15] = b2Add( points[14], { -2.0f * hx * m, -2.0f * hx * m } );
points[16] = b2Add( points[15], { -2.0f * hx * m, -2.0f * hx * m } );
points[17] = b2Add( points[16], { -2.0f * hx * m, -2.0f * hx * m } );
points[18] = b2Add( points[17], { -2.0f * hx, 0.0f } );
points[19] = b2Add( points[18], { -2.0f * hx, 0.0f } );
b2ChainDef chainDef = b2DefaultChainDef();
chainDef.points = points;
chainDef.count = 20;
chainDef.isLoop = true;
chainDef.friction = m_friction;
b2CreateChain( m_groundId, &chainDef );
}
else
{
b2ShapeDef shapeDef = b2DefaultShapeDef();
shapeDef.friction = m_friction;
b2Hull hull = { 0 };
if ( m_bevel > 0.0f )
{
float hb = m_bevel;
b2Vec2 vs[8] = { { hx + hb, hy - 0.05f }, { hx, hy }, { -hx, hy }, { -hx - hb, hy - 0.05f },
{ -hx - hb, -hy + 0.05f }, { -hx, -hy }, { hx, -hy }, { hx + hb, -hy + 0.05f } };
hull = b2ComputeHull( vs, 8 );
}
else
{
b2Vec2 vs[4] = { { hx, hy }, { -hx, hy }, { -hx, -hy }, { hx, -hy } };
hull = b2ComputeHull( vs, 4 );
}
b2Transform transform;
float x, y;
x = -3.0f * hx - m * hx - m * hy;
y = hy + m * hx - m * hy;
transform.q = b2MakeRot( -0.25f * b2_pi );
{
transform.p = { x, y };
b2Polygon polygon = b2MakeOffsetPolygon( &hull, 0.0f, transform );
b2CreatePolygonShape( m_groundId, &shapeDef, &polygon );
x -= 2.0f * m * hx;
y += 2.0f * m * hx;
}
{
transform.p = { x, y };
b2Polygon polygon = b2MakeOffsetPolygon( &hull, 0.0f, transform );
b2CreatePolygonShape( m_groundId, &shapeDef, &polygon );
x -= 2.0f * m * hx;
y += 2.0f * m * hx;
}
{
transform.p = { x, y };
b2Polygon polygon = b2MakeOffsetPolygon( &hull, 0.0f, transform );
b2CreatePolygonShape( m_groundId, &shapeDef, &polygon );
x -= 2.0f * m * hx;
y += 2.0f * m * hx;
}
x = -2.0f * hx;
y = 0.0f;
transform.q = b2MakeRot( 0.0f );
{
transform.p = { x, y };
b2Polygon polygon = b2MakeOffsetPolygon( &hull, 0.0f, transform );
b2CreatePolygonShape( m_groundId, &shapeDef, &polygon );
x += 2.0f * hx;
}
{
transform.p = { x, y };
b2Polygon polygon = b2MakeOffsetPolygon( &hull, 0.0f, transform );
b2CreatePolygonShape( m_groundId, &shapeDef, &polygon );
x += 2.0f * hx;
}
{
transform.p = { x, y };
b2Polygon polygon = b2MakeOffsetPolygon( &hull, 0.0f, transform );
b2CreatePolygonShape( m_groundId, &shapeDef, &polygon );
x += 2.0f * hx;
}
x = 3.0f * hx + m * hx + m * hy;
y = hy + m * hx - m * hy;
transform.q = b2MakeRot( 0.25f * b2_pi );
{
transform.p = { x, y };
b2Polygon polygon = b2MakeOffsetPolygon( &hull, 0.0f, transform );
b2CreatePolygonShape( m_groundId, &shapeDef, &polygon );
x += 2.0f * m * hx;
y += 2.0f * m * hx;
}
{
transform.p = { x, y };
b2Polygon polygon = b2MakeOffsetPolygon( &hull, 0.0f, transform );
b2CreatePolygonShape( m_groundId, &shapeDef, &polygon );
x += 2.0f * m * hx;
y += 2.0f * m * hx;
}
{
transform.p = { x, y };
b2Polygon polygon = b2MakeOffsetPolygon( &hull, 0.0f, transform );
b2CreatePolygonShape( m_groundId, &shapeDef, &polygon );
x += 2.0f * m * hx;
y += 2.0f * m * hx;
}
}
}
void Launch()
{
if ( B2_IS_NON_NULL( m_bodyId ) )
{
b2DestroyBody( m_bodyId );
m_shapeId = b2_nullShapeId;
}
b2BodyDef bodyDef = b2DefaultBodyDef();
bodyDef.type = b2_dynamicBody;
bodyDef.position = { -28.0f, 18.0f };
bodyDef.linearVelocity = { 0.0f, 0.0f };
m_bodyId = b2CreateBody( m_worldId, &bodyDef );
b2ShapeDef shapeDef = b2DefaultShapeDef();
shapeDef.density = 1.0f;
shapeDef.friction = m_friction;
if ( m_shapeType == e_circleShape )
{
b2Circle circle = { { 0.0f, 0.0f }, 0.5f };
m_shapeId = b2CreateCircleShape( m_bodyId, &shapeDef, &circle );
}
else if ( m_shapeType == e_capsuleShape )
{
b2Capsule capsule = { { -0.5f, 0.0f }, { 0.5f, 0.0 }, 0.25f };
m_shapeId = b2CreateCapsuleShape( m_bodyId, &shapeDef, &capsule );
}
else
{
float h = 0.5f - m_round;
b2Polygon box = b2MakeRoundedBox( h, 2.0f * h, m_round );
m_shapeId = b2CreatePolygonShape( m_bodyId, &shapeDef, &box );
}
}
void UpdateUI() override
{
float height = 140.0f;
ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once );
ImGui::SetNextWindowSize( ImVec2( 180.0f, height ) );
ImGui::Begin( "Ghost Collision", nullptr, ImGuiWindowFlags_NoResize );
ImGui::PushItemWidth( 100.0f );
if ( ImGui::Checkbox( "Chain", &m_useChain ) )
{
CreateScene();
}
if ( m_useChain == false )
{
if ( ImGui::SliderFloat( "Bevel", &m_bevel, 0.0f, 1.0f, "%.2f" ) )
{
CreateScene();
}
}
{
const char* shapeTypes[] = { "Circle", "Capsule", "Box" };
int shapeType = int( m_shapeType );
ImGui::Combo( "Shape", &shapeType, shapeTypes, IM_ARRAYSIZE( shapeTypes ) );
m_shapeType = ShapeType( shapeType );
}
if ( m_shapeType == e_boxShape )
{
ImGui::SliderFloat( "Round", &m_round, 0.0f, 0.4f, "%.1f" );
}
if ( ImGui::SliderFloat( "Friction", &m_friction, 0.0f, 1.0f, "%.1f" ) )
{
if ( B2_IS_NON_NULL( m_shapeId ) )
{
b2Shape_SetFriction( m_shapeId, m_friction );
}
CreateScene();
}
if ( ImGui::Button( "Launch" ) )
{
Launch();
}
ImGui::PopItemWidth();
ImGui::End();
}
static Sample* Create( Settings& settings )
{
return new GhostCollision( settings );
}
b2BodyId m_groundId;
b2BodyId m_bodyId;
b2ShapeId m_shapeId;
ShapeType m_shapeType;
float m_round;
float m_friction;
float m_bevel;
bool m_useChain;
};
static int sampleGhostCollision = RegisterSample( "Continuous", "Ghost Collision", GhostCollision::Create );
class SpeculativeFallback : public Sample
{
public:
explicit SpeculativeFallback( Settings& settings )
: Sample( settings )
{
if ( settings.restart == false )
{
g_camera.m_center = { 1.0f, 5.0f };
g_camera.m_zoom = 25.0f * 0.25f;
}
{
b2BodyDef bodyDef = b2DefaultBodyDef();
b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );
b2ShapeDef shapeDef = b2DefaultShapeDef();
b2Segment segment = { { -10.0f, 0.0f }, { 10.0f, 0.0f } };
b2CreateSegmentShape( groundId, &shapeDef, &segment );
b2Vec2 points[5] = { { -2.0f, 4.0f }, { 2.0f, 4.0f }, { 2.0f, 4.1f }, { -0.5f, 4.2f }, { -2.0f, 4.2f } };
b2Hull hull = b2ComputeHull( points, 5 );
b2Polygon poly = b2MakePolygon( &hull, 0.0f );
b2CreatePolygonShape( groundId, &shapeDef, &poly );
}
{
float offset = 8.0f;
b2BodyDef bodyDef = b2DefaultBodyDef();
bodyDef.type = b2_dynamicBody;
bodyDef.position = { offset, 12.0f };
bodyDef.linearVelocity = { 0.0f, -100.0f };
b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );
b2ShapeDef shapeDef = b2DefaultShapeDef();
b2Polygon box = b2MakeOffsetBox( 2.0f, 0.05f, { -offset, 0.0f }, b2MakeRot(b2_pi) );
b2CreatePolygonShape( bodyId, &shapeDef, &box );
}
}
static Sample* Create( Settings& settings )
{
return new SpeculativeFallback( settings );
}
};
static int sampleSpeculativeFallback = RegisterSample( "Continuous", "Speculative Fallback", SpeculativeFallback::Create );
class Pinball : public Sample
{
public:
explicit Pinball( Settings& settings )
: Sample( settings )
{
if ( settings.restart == false )
{
g_camera.m_center = { 0.0f, 9.0f };
g_camera.m_zoom = 25.0f * 0.5f;
}
settings.drawJoints = false;
b2BodyId groundId = {};
{
b2BodyDef bodyDef = b2DefaultBodyDef();
groundId = b2CreateBody( m_worldId, &bodyDef );
b2Vec2 vs[5] = { { -8.0f, 6.0f }, { -8.0f, 20.0f }, { 8.0f, 20.0f }, { 8.0f, 6.0f }, { 0.0f, -2.0f } };
b2ChainDef chainDef = b2DefaultChainDef();
chainDef.points = vs;
chainDef.count = 5;
chainDef.isLoop = true;
b2CreateChain( groundId, &chainDef );
}
{
b2Vec2 p1 = { -2.0f, 0.0f }, p2 = { 2.0f, 0.0f };
b2BodyDef bodyDef = b2DefaultBodyDef();
bodyDef.type = b2_dynamicBody;
bodyDef.enableSleep = false;
bodyDef.position = p1;
b2BodyId leftFlipperId = b2CreateBody( m_worldId, &bodyDef );
bodyDef.position = p2;
b2BodyId rightFlipperId = b2CreateBody( m_worldId, &bodyDef );
b2Polygon box = b2MakeBox( 1.75f, 0.2f );
b2ShapeDef shapeDef = b2DefaultShapeDef();
b2CreatePolygonShape( leftFlipperId, &shapeDef, &box );
b2CreatePolygonShape( rightFlipperId, &shapeDef, &box );
b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();
jointDef.bodyIdA = groundId;
jointDef.localAnchorB = b2Vec2_zero;
jointDef.enableMotor = true;
jointDef.maxMotorTorque = 1000.0f;
jointDef.enableLimit = true;
jointDef.motorSpeed = 0.0f;
jointDef.localAnchorA = p1;
jointDef.bodyIdB = leftFlipperId;
jointDef.lowerAngle = -30.0f * b2_pi / 180.0f;
jointDef.upperAngle = 5.0f * b2_pi / 180.0f;
m_leftJointId = b2CreateRevoluteJoint( m_worldId, &jointDef );
jointDef.motorSpeed = 0.0f;
jointDef.localAnchorA = p2;
jointDef.bodyIdB = rightFlipperId;
jointDef.lowerAngle = -5.0f * b2_pi / 180.0f;
jointDef.upperAngle = 30.0f * b2_pi / 180.0f;
m_rightJointId = b2CreateRevoluteJoint( m_worldId, &jointDef );
}
{
b2BodyDef bodyDef = b2DefaultBodyDef();
bodyDef.type = b2_dynamicBody;
bodyDef.position = { -4.0f, 17.0f };
b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );
b2ShapeDef shapeDef = b2DefaultShapeDef();
b2Polygon box1 = b2MakeBox( 1.5f, 0.125f );
b2Polygon box2 = b2MakeBox( 0.125f, 1.5f );
b2CreatePolygonShape( bodyId, &shapeDef, &box1 );
b2CreatePolygonShape( bodyId, &shapeDef, &box2 );
b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();
jointDef.bodyIdA = groundId;
jointDef.bodyIdB = bodyId;
jointDef.localAnchorA = bodyDef.position;
jointDef.localAnchorB = b2Vec2_zero;
jointDef.enableMotor = true;
jointDef.maxMotorTorque = 0.1f;
b2CreateRevoluteJoint( m_worldId, &jointDef );
bodyDef.position = { 4.0f, 8.0f };
bodyId = b2CreateBody( m_worldId, &bodyDef );
b2CreatePolygonShape( bodyId, &shapeDef, &box1 );
b2CreatePolygonShape( bodyId, &shapeDef, &box2 );
jointDef.localAnchorA = bodyDef.position;
jointDef.bodyIdB = bodyId;
b2CreateRevoluteJoint( m_worldId, &jointDef );
}
{
b2BodyDef bodyDef = b2DefaultBodyDef();
bodyDef.position = { -4.0f, 8.0f };
b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );
b2ShapeDef shapeDef = b2DefaultShapeDef();
shapeDef.restitution = 1.5f;
b2Circle circle = { { 0.0f, 0.0f }, 1.0f };
b2CreateCircleShape( bodyId, &shapeDef, &circle );
bodyDef.position = { 4.0f, 17.0f };
bodyId = b2CreateBody( m_worldId, &bodyDef );
b2CreateCircleShape( bodyId, &shapeDef, &circle );
}
{
b2BodyDef bodyDef = b2DefaultBodyDef();
bodyDef.position = { 1.0f, 15.0f };
bodyDef.type = b2_dynamicBody;
bodyDef.isBullet = true;
m_ballId = b2CreateBody( m_worldId, &bodyDef );
b2ShapeDef shapeDef = b2DefaultShapeDef();
b2Circle circle = { { 0.0f, 0.0f }, 0.2f };
b2CreateCircleShape( m_ballId, &shapeDef, &circle );
}
}
void Step( Settings& settings ) override
{
Sample::Step( settings );
if ( glfwGetKey( g_mainWindow, GLFW_KEY_SPACE ) == GLFW_PRESS )
{
b2RevoluteJoint_SetMotorSpeed( m_leftJointId, 20.0f );
b2RevoluteJoint_SetMotorSpeed( m_rightJointId, -20.0f );
}
else
{
b2RevoluteJoint_SetMotorSpeed( m_leftJointId, -10.0f );
b2RevoluteJoint_SetMotorSpeed( m_rightJointId, 10.0f );
}
}
static Sample* Create( Settings& settings )
{
return new Pinball( settings );
}
b2JointId m_leftJointId;
b2JointId m_rightJointId;
b2BodyId m_ballId;
};
static int samplePinball = RegisterSample( "Continuous", "Pinball", Pinball::Create );