Skip to content

Commit e7cb9f0

Browse files
authored
Explosion features (#810)
set weld joint reference angle fixed some angle wrapping bugs
1 parent 67b9835 commit e7cb9f0

File tree

13 files changed

+306
-39
lines changed

13 files changed

+306
-39
lines changed

include/box2d/box2d.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,10 +143,8 @@ B2_API b2Vec2 b2World_GetGravity( b2WorldId worldId );
143143

144144
/// Apply a radial explosion
145145
/// @param worldId The world id
146-
/// @param position The center of the explosion
147-
/// @param radius The radius of the explosion
148-
/// @param impulse The impulse of the explosion, typically in kg * m / s or N * s.
149-
B2_API void b2World_Explode( b2WorldId worldId, b2Vec2 position, float radius, float impulse );
146+
/// @param explosionDef The explosion definition
147+
B2_API void b2World_Explode( b2WorldId worldId, const b2ExplosionDef* explosionDef );
150148

151149
/// Adjust contact tuning parameters
152150
/// @param worldId The world id
@@ -1006,6 +1004,12 @@ B2_API float b2RevoluteJoint_GetMaxMotorTorque( b2JointId jointId );
10061004
/// @see b2WeldJointDef for details
10071005
B2_API b2JointId b2CreateWeldJoint( b2WorldId worldId, const b2WeldJointDef* def );
10081006

1007+
/// Get the weld joint reference angle in radians
1008+
B2_API float b2WeldJoint_GetReferenceAngle( b2JointId jointId );
1009+
1010+
/// Set the weld joint reference angle in radians, must be in [-pi,pi].
1011+
B2_API void b2WeldJoint_SetReferenceAngle( b2JointId jointId, float angleInRadians );
1012+
10091013
/// Set the weld joint linear stiffness in Hertz. 0 is rigid.
10101014
B2_API void b2WeldJoint_SetLinearHertz( b2JointId jointId, float hertz );
10111015

include/box2d/types.h

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@
1111
#include <stdbool.h>
1212
#include <stdint.h>
1313

14-
/// Task interface
14+
#define B2_DEFAULT_CATEGORY_BITS 0x0001ULL
15+
#define B2_DEFAULT_MASK_BITS UINT64_MAX
16+
17+
/// Task interface
1518
/// This is prototype for a Box2D task. Your task system is expected to invoke the Box2D task with these arguments.
1619
/// The task spans a range of the parallel-for: [startIndex, endIndex)
1720
/// The worker index must correctly identify each worker in the user thread pool, expected in [0, workerCount).
@@ -391,6 +394,9 @@ typedef struct b2ChainDef
391394
/// Contact filtering data.
392395
b2Filter filter;
393396

397+
/// Custom debug draw color.
398+
uint32_t customColor;
399+
394400
/// Indicates a closed chain formed by connecting the first and last points
395401
bool isLoop;
396402

@@ -865,6 +871,33 @@ typedef struct b2WheelJointDef
865871
/// @ingroup wheel_joint
866872
B2_API b2WheelJointDef b2DefaultWheelJointDef( void );
867873

874+
/// The explosion definition is used to configure options for explosions. Explosions
875+
/// consider shape geometry when computing the impulse.
876+
/// @ingroup world
877+
typedef struct b2ExplosionDef
878+
{
879+
/// Mask bits to filter shapes
880+
uint64_t maskBits;
881+
882+
/// The center of the explosion in world space
883+
b2Vec2 position;
884+
885+
/// The radius of the explosion
886+
float radius;
887+
888+
/// The falloff distance beyond the radius. Impulse is reduced to zero at this distance.
889+
float falloff;
890+
891+
/// Impulse per unit length. This applies an impulse according to the shape perimeter that
892+
/// is facing the explosion. Explosions only apply to circles, capsules, and polygons. This
893+
/// may be negative for implosions.
894+
float impulsePerLength;
895+
} b2ExplosionDef;
896+
897+
/// Use this to initialize your explosion definition
898+
/// @ingroup world
899+
B2_API b2ExplosionDef b2DefaultExplosionDef( void );
900+
868901
/**
869902
* @defgroup events Events
870903
* World event types.

samples/sample_bodies.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -533,7 +533,12 @@ class Weeble : public Sample
533533

534534
if ( ImGui::Button( "Explode" ) )
535535
{
536-
b2World_Explode( m_worldId, m_explosionPosition, m_explosionRadius, m_explosionMagnitude );
536+
b2ExplosionDef def = b2DefaultExplosionDef();
537+
def.position = m_explosionPosition;
538+
def.radius = m_explosionRadius;
539+
def.falloff = 0.1f;
540+
def.impulsePerLength = m_explosionMagnitude;
541+
b2World_Explode( m_worldId, &def );
537542
}
538543
ImGui::PushItemWidth( 100.0f );
539544

samples/sample_events.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -990,7 +990,7 @@ class BodyMove : public Sample
990990

991991
m_explosionPosition = { 0.0f, -5.0f };
992992
m_explosionRadius = 10.0f;
993-
m_explosionMagnitude = 6.0f;
993+
m_explosionMagnitude = 10.0f;
994994
}
995995

996996
void CreateBodies()
@@ -1046,10 +1046,15 @@ class BodyMove : public Sample
10461046

10471047
if ( ImGui::Button( "Explode" ) )
10481048
{
1049-
b2World_Explode( m_worldId, m_explosionPosition, m_explosionRadius, m_explosionMagnitude );
1049+
b2ExplosionDef def = b2DefaultExplosionDef();
1050+
def.position = m_explosionPosition;
1051+
def.radius = m_explosionRadius;
1052+
def.falloff = 0.1f;
1053+
def.impulsePerLength = m_explosionMagnitude;
1054+
b2World_Explode( m_worldId, &def );
10501055
}
10511056

1052-
ImGui::SliderFloat( "Magnitude", &m_explosionMagnitude, -8.0f, 8.0f, "%.1f" );
1057+
ImGui::SliderFloat( "Magnitude", &m_explosionMagnitude, -20.0f, 20.0f, "%.1f" );
10531058

10541059
ImGui::End();
10551060
}

samples/sample_shapes.cpp

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ class ChainShape : public Sample
107107
b2ChainDef chainDef = b2DefaultChainDef();
108108
chainDef.points = points;
109109
chainDef.count = count;
110+
chainDef.customColor = b2_colorSteelBlue;
110111
chainDef.isLoop = true;
111112
chainDef.friction = 0.2f;
112113

@@ -1302,3 +1303,118 @@ class OffsetShapes : public Sample
13021303
};
13031304

13041305
static int sampleOffsetShapes = RegisterSample( "Shapes", "Offset", OffsetShapes::Create );
1306+
1307+
// This shows how to use explosions and demonstrates the projected perimeter
1308+
class Explosion : public Sample
1309+
{
1310+
public:
1311+
1312+
explicit Explosion( Settings& settings )
1313+
: Sample( settings )
1314+
{
1315+
if ( settings.restart == false )
1316+
{
1317+
g_camera.m_center = { 0.0f, 0.0f };
1318+
g_camera.m_zoom = 14.0f;
1319+
}
1320+
1321+
b2BodyDef bodyDef = b2DefaultBodyDef();
1322+
b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );
1323+
1324+
bodyDef.type = b2_dynamicBody;
1325+
bodyDef.gravityScale = 0.0f;
1326+
b2ShapeDef shapeDef = b2DefaultShapeDef();
1327+
1328+
m_referenceAngle = 0.0f;
1329+
1330+
b2WeldJointDef weldDef = b2DefaultWeldJointDef();
1331+
weldDef.referenceAngle = m_referenceAngle;
1332+
weldDef.angularHertz = 0.5f;
1333+
weldDef.angularDampingRatio = 0.7f;
1334+
weldDef.linearHertz = 0.5f;
1335+
weldDef.linearDampingRatio = 0.7f;
1336+
weldDef.bodyIdA = groundId;
1337+
weldDef.localAnchorB = b2Vec2_zero;
1338+
1339+
float r = 8.0f;
1340+
for (float angle = 0.0f; angle < 360.0f; angle += 30.0f)
1341+
{
1342+
b2CosSin cosSin = b2ComputeCosSin( angle * b2_pi / 180.0f );
1343+
bodyDef.position = { r * cosSin.cosine, r * cosSin.sine };
1344+
b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );
1345+
1346+
b2Polygon box = b2MakeBox( 1.0f, 0.1f );
1347+
b2CreatePolygonShape( bodyId, &shapeDef, &box );
1348+
1349+
weldDef.localAnchorA = bodyDef.position;
1350+
weldDef.bodyIdB = bodyId;
1351+
b2JointId jointId = b2CreateWeldJoint( m_worldId, &weldDef );
1352+
m_jointIds.push_back( jointId );
1353+
}
1354+
1355+
m_radius = 7.0f;
1356+
m_falloff = 3.0f;
1357+
m_impulse = 10.0f;
1358+
}
1359+
1360+
void UpdateUI() override
1361+
{
1362+
float height = 160.0f;
1363+
ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once );
1364+
ImGui::SetNextWindowSize( ImVec2( 240.0f, height ) );
1365+
1366+
ImGui::Begin( "Explosion", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize );
1367+
1368+
if ( ImGui::Button( "Explode" ) )
1369+
{
1370+
b2ExplosionDef def = b2DefaultExplosionDef();
1371+
def.position = b2Vec2_zero;
1372+
def.radius = m_radius;
1373+
def.falloff = m_falloff;
1374+
def.impulsePerLength = m_impulse;
1375+
b2World_Explode( m_worldId, &def );
1376+
}
1377+
1378+
ImGui::SliderFloat( "radius", &m_radius, 0.0f, 20.0f, "%.1f" );
1379+
ImGui::SliderFloat( "falloff", &m_falloff, 0.0f, 20.0f, "%.1f" );
1380+
ImGui::SliderFloat( "impulse", &m_impulse, -20.0f, 20.0f, "%.1f" );
1381+
1382+
ImGui::End();
1383+
}
1384+
1385+
void Step( Settings& settings ) override
1386+
{
1387+
if (settings.pause == false || settings.singleStep == true)
1388+
{
1389+
m_referenceAngle += settings.hertz > 0.0f ? 60.0f * b2_pi / 180.0f / settings.hertz : 0.0f;
1390+
m_referenceAngle = b2UnwindAngle( m_referenceAngle );
1391+
1392+
int count = m_jointIds.size();
1393+
for (int i = 0; i < count; ++i)
1394+
{
1395+
b2WeldJoint_SetReferenceAngle( m_jointIds[i], m_referenceAngle );
1396+
}
1397+
}
1398+
1399+
Sample::Step( settings );
1400+
1401+
g_draw.DrawString( 5, m_textLine, "reference angle = %g", m_referenceAngle );
1402+
m_textLine += m_textIncrement;
1403+
1404+
g_draw.DrawCircle( b2Vec2_zero, m_radius + m_falloff, b2_colorBox2DBlue );
1405+
g_draw.DrawCircle( b2Vec2_zero, m_radius, b2_colorBox2DYellow );
1406+
}
1407+
1408+
static Sample* Create( Settings& settings )
1409+
{
1410+
return new Explosion( settings );
1411+
}
1412+
1413+
std::vector<b2JointId> m_jointIds;
1414+
float m_radius;
1415+
float m_falloff;
1416+
float m_impulse;
1417+
float m_referenceAngle;
1418+
};
1419+
1420+
static int sampleBodyMove = RegisterSample( "Shapes", "Explosion", Explosion::Create );

samples/sample_world.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,14 @@ class LargeWorld : public Sample
193193
if ( ( m_stepCount & 0x1 ) == 0x1 && m_explode )
194194
{
195195
m_explosionPosition.x = ( 0.5f + m_cycleIndex ) * m_period - span;
196-
b2World_Explode( m_worldId, m_explosionPosition, radius, 1.0f );
196+
197+
b2ExplosionDef def = b2DefaultExplosionDef();
198+
def.position = m_explosionPosition;
199+
def.radius = radius;
200+
def.falloff = 0.1f;
201+
def.impulsePerLength = 1.0f;
202+
b2World_Explode( m_worldId, &def );
203+
197204
m_cycleIndex = ( m_cycleIndex + 1 ) % m_cycleCount;
198205
}
199206

src/joint.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,13 @@ b2WheelJointDef b2DefaultWheelJointDef( void )
8383
return def;
8484
}
8585

86+
b2ExplosionDef b2DefaultExplosionDef(void)
87+
{
88+
b2ExplosionDef def = { 0 };
89+
def.maskBits = B2_DEFAULT_MASK_BITS;
90+
return def;
91+
}
92+
8693
static b2Joint* b2GetJointFullId( b2World* world, b2JointId jointId )
8794
{
8895
int id = jointId.index1 - 1;

src/prismatic_joint.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ void b2PreparePrismaticJoint( b2JointSim* base, b2StepContext* context )
254254
joint->axisA = b2RotateVector( qA, joint->localAxisA );
255255
joint->deltaCenter = b2Sub( bodySimB->center, bodySimA->center );
256256
joint->deltaAngle = b2RelativeAngle( qB, qA ) - joint->referenceAngle;
257+
joint->deltaAngle = b2UnwindAngle( joint->deltaAngle );
257258

258259
b2Vec2 rA = joint->anchorA;
259260
b2Vec2 rB = joint->anchorB;

src/shape.c

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ b2ChainId b2CreateChain( b2BodyId bodyId, const b2ChainDef* def )
320320
shapeDef.restitution = def->restitution;
321321
shapeDef.friction = def->friction;
322322
shapeDef.filter = def->filter;
323+
shapeDef.customColor = def->customColor;
323324
shapeDef.enableContactEvents = false;
324325
shapeDef.enableHitEvents = false;
325326
shapeDef.enableSensorEvents = false;
@@ -546,6 +547,58 @@ float b2GetShapePerimeter( const b2Shape* shape )
546547
}
547548
}
548549

550+
// This projects the the shape perimeter onto an infinite line
551+
float b2GetShapeProjectedPerimeter( const b2Shape* shape, b2Vec2 line )
552+
{
553+
switch ( shape->type )
554+
{
555+
case b2_capsuleShape:
556+
{
557+
b2Vec2 axis = b2Sub( shape->capsule.center2, shape->capsule.center1 );
558+
float projectedLength = b2AbsFloat( b2Dot( axis, line ) );
559+
return projectedLength + 2.0f * shape->capsule.radius;
560+
}
561+
562+
case b2_circleShape:
563+
return 2.0f * shape->circle.radius;
564+
565+
case b2_polygonShape:
566+
{
567+
const b2Vec2* points = shape->polygon.vertices;
568+
int count = shape->polygon.count;
569+
B2_ASSERT( count > 0 );
570+
float value = b2Dot( points[0], line );
571+
float lower = value;
572+
float upper = value;
573+
for ( int i = 1; i < count; ++i )
574+
{
575+
value = b2Dot( points[i], line );
576+
lower = b2MinFloat( lower, value );
577+
upper = b2MaxFloat( upper, value );
578+
}
579+
580+
return (upper - lower) + 2.0f * shape->polygon.radius;
581+
}
582+
583+
case b2_segmentShape:
584+
{
585+
float value1 = b2Dot( shape->segment.point1, line );
586+
float value2 = b2Dot( shape->segment.point2, line );
587+
return b2AbsFloat( value2 - value1 );
588+
}
589+
590+
case b2_chainSegmentShape:
591+
{
592+
float value1 = b2Dot( shape->chainSegment.segment.point1, line );
593+
float value2 = b2Dot( shape->chainSegment.segment.point2, line );
594+
return b2AbsFloat( value2 - value1 );
595+
}
596+
597+
default:
598+
return 0.0f;
599+
}
600+
}
601+
549602
b2MassData b2ComputeShapeMass( const b2Shape* shape )
550603
{
551604
switch ( shape->type )

src/shape.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ b2ShapeExtent b2ComputeShapeExtent( const b2Shape* shape, b2Vec2 localCenter );
7373
b2AABB b2ComputeShapeAABB( const b2Shape* shape, b2Transform transform );
7474
b2Vec2 b2GetShapeCentroid( const b2Shape* shape );
7575
float b2GetShapePerimeter( const b2Shape* shape );
76+
float b2GetShapeProjectedPerimeter( const b2Shape* shape, b2Vec2 line );
7677

7778
b2DistanceProxy b2MakeShapeDistanceProxy( const b2Shape* shape );
7879

0 commit comments

Comments
 (0)