Skip to content

Commit d3d2b92

Browse files
authored
Added b2ContactId for events and access functions (#943)
`b2ContactBeginTouchEvent` now contains a `b2ContactId` you can hold onto safely and query for the manifold. This lets you get the contact impulses. You can use the end touch event to clear the handle.
1 parent 30b6000 commit d3d2b92

File tree

8 files changed

+285
-37
lines changed

8 files changed

+285
-37
lines changed

include/box2d/box2d.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1230,3 +1230,20 @@ B2_API float b2WheelJoint_GetMotorTorque( b2JointId jointId );
12301230
/**@}*/
12311231

12321232
/**@}*/
1233+
1234+
/**
1235+
* @defgroup contact Contact
1236+
* Access to contacts
1237+
* @{
1238+
*/
1239+
1240+
/// Contact identifier validation. Provides validation for up to 2^32 allocations.
1241+
B2_API bool b2Contact_IsValid( b2ContactId id );
1242+
1243+
/// Get manifold for a contact. The manifold may have no points if the contact is not touching.
1244+
B2_API b2Manifold b2Contact_GetManifold( b2ContactId contactId );
1245+
1246+
/// Get the shapes associated with a contact.
1247+
B2_API void b2Contact_GetShapeIds( b2ContactId contactId, b2ShapeId* shapeIdA, b2ShapeId* shapeIdB );
1248+
1249+
/**@}*/

include/box2d/id.h

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,21 +72,31 @@ typedef struct b2JointId
7272
uint16_t generation;
7373
} b2JointId;
7474

75+
/// Contact id references a contact instance. This should be treated as an opaque handled.
76+
typedef struct b2ContactId
77+
{
78+
int32_t index1;
79+
uint16_t world0;
80+
int16_t padding;
81+
uint32_t generation;
82+
} b2ContactId;
83+
7584
/// Use these to make your identifiers null.
7685
/// You may also use zero initialization to get null.
7786
static const b2WorldId b2_nullWorldId = B2_ZERO_INIT;
7887
static const b2BodyId b2_nullBodyId = B2_ZERO_INIT;
7988
static const b2ShapeId b2_nullShapeId = B2_ZERO_INIT;
8089
static const b2ChainId b2_nullChainId = B2_ZERO_INIT;
8190
static const b2JointId b2_nullJointId = B2_ZERO_INIT;
91+
static const b2ContactId b2_nullContactId = B2_ZERO_INIT;
8292

8393
/// Macro to determine if any id is null.
8494
#define B2_IS_NULL( id ) ( id.index1 == 0 )
8595

8696
/// Macro to determine if any id is non-null.
8797
#define B2_IS_NON_NULL( id ) ( id.index1 != 0 )
8898

89-
/// Compare two ids for equality. Doesn't work for b2WorldId.
99+
/// Compare two ids for equality. Doesn't work for b2WorldId. Don't mix types.
90100
#define B2_ID_EQUALS( id1, id2 ) ( id1.index1 == id2.index1 && id1.world0 == id2.world0 && id1.generation == id2.generation )
91101

92102
/// Store a world id into a uint32_t.
@@ -154,4 +164,23 @@ B2_INLINE b2JointId b2LoadJointId( uint64_t x )
154164
return id;
155165
}
156166

167+
/// Store a contact id into 16 bytes
168+
B2_INLINE void b2StoreContactId( b2ContactId id, uint32_t values[3] )
169+
{
170+
values[0] = (uint32_t)id.index1;
171+
values[1] = (uint32_t)id.world0;
172+
values[2] = (uint32_t)id.generation;
173+
}
174+
175+
/// Load a two uint64_t into a contact id.
176+
B2_INLINE b2ContactId b2LoadContactId( uint32_t values[3] )
177+
{
178+
b2ContactId id;
179+
id.index1 = (int32_t)values[0];
180+
id.world0 = (uint16_t)values[1];
181+
id.padding = 0;
182+
id.generation = (uint32_t)values[2];
183+
return id;
184+
}
185+
157186
/**@}*/

include/box2d/types.h

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -607,7 +607,7 @@ typedef struct b2DistanceJointDef
607607
B2_API b2DistanceJointDef b2DefaultDistanceJointDef( void );
608608

609609
/// A motor joint is used to control the relative motion between two bodies
610-
///
610+
/// You may move local frame A to change the target transform.
611611
/// A typical usage is to control the movement of a dynamic body with respect to the ground.
612612
/// @ingroup motor_joint
613613
typedef struct b2MotorJointDef
@@ -633,7 +633,7 @@ typedef struct b2MotorJointDef
633633
B2_API b2MotorJointDef b2DefaultMotorJointDef( void );
634634

635635
/// A mouse joint is used to make a point on a body track a specified world point.
636-
///
636+
/// You may move local frame A to change the target point.
637637
/// This a soft constraint and allows the constraint to stretch without
638638
/// applying huge forces. This also applies rotation constraint heuristic to improve control.
639639
/// @ingroup mouse_joint
@@ -677,10 +677,8 @@ B2_API b2FilterJointDef b2DefaultFilterJointDef( void );
677677

678678
/// Prismatic joint definition
679679
///
680-
/// This requires defining a line of motion using an axis and an anchor point.
681-
/// The definition uses local anchor points and a local axis so that the initial
682-
/// configuration can violate the constraint slightly. The joint translation is zero
683-
/// when the local anchor points coincide in world space.
680+
/// Body B may slide along the x-axis in local frame A. Body B cannot rotate relative to body A.
681+
/// The joint translation is zero when the local frame origins coincide in world space.
684682
/// @ingroup prismatic_joint
685683
typedef struct b2PrismaticJointDef
686684
{
@@ -733,8 +731,7 @@ B2_API b2PrismaticJointDef b2DefaultPrismaticJointDef( void );
733731
/// initial configuration can violate the constraint slightly. You also need to
734732
/// specify the initial relative angle for joint limits. This helps when saving
735733
/// and loading a game.
736-
/// The local anchor points are measured from the body's origin
737-
/// rather than the center of mass because:
734+
/// The local anchor points are measured from the body's origin rather than the center of mass because:
738735
/// 1. you might not know where the center of mass will be
739736
/// 2. if you add/remove shapes from a body and recompute the mass, the joints will be broken
740737
/// @ingroup revolute_joint
@@ -815,10 +812,8 @@ B2_API b2WeldJointDef b2DefaultWeldJointDef( void );
815812

816813
/// Wheel joint definition
817814
///
818-
/// This requires defining a line of motion using an axis and an anchor point.
819-
/// The definition uses local anchor points and a local axis so that the initial
820-
/// configuration can violate the constraint slightly. The joint translation is zero
821-
/// when the local anchor points coincide in world space.
815+
/// Body B is a wheel that may rotate freely and slide along the local x-axis in frame A.
816+
/// The joint translation is zero when the local frame origins coincide in world space.
822817
/// @ingroup wheel_joint
823818
typedef struct b2WheelJointDef
824819
{
@@ -959,8 +954,13 @@ typedef struct b2ContactBeginTouchEvent
959954
/// Id of the second shape
960955
b2ShapeId shapeIdB;
961956

957+
/// The transient contact id. This contact maybe destroyed automatically by Box2D when the world is modified or simulated.
958+
/// Used b2Contact_IsValid before using this id.
959+
b2ContactId contactId;
960+
962961
/// The initial contact manifold. This is recorded before the solver is called,
963-
/// so all the impulses will be zero.
962+
/// so all the impulses will be zero. You can use the contact id to access the manifold impulses
963+
/// using b2Contact_GetManifold.
964964
b2Manifold manifold;
965965
} b2ContactBeginTouchEvent;
966966

@@ -979,6 +979,9 @@ typedef struct b2ContactEndTouchEvent
979979
/// @warning this shape may have been destroyed
980980
/// @see b2Shape_IsValid
981981
b2ShapeId shapeIdB;
982+
983+
/// Id of the contact
984+
b2ContactId contactId;
982985
} b2ContactEndTouchEvent;
983986

984987
/// A hit touch event is generated when two shapes collide with a speed faster than the hit speed threshold.

samples/sample_events.cpp

Lines changed: 104 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -806,7 +806,7 @@ class FootSensor : public Sample
806806
int m_overlapCount;
807807
};
808808

809-
static int sampleCharacterSensor = RegisterSample( "Events", "Foot Sensor", FootSensor::Create );
809+
static int sampleFootSensor = RegisterSample( "Events", "Foot Sensor", FootSensor::Create );
810810

811811
struct BodyUserData
812812
{
@@ -2082,3 +2082,106 @@ class JointEvent : public Sample
20822082
};
20832083

20842084
static int sampleBreakableJoint = RegisterSample( "Events", "Joint", JointEvent::Create );
2085+
2086+
class PersistentContact : public Sample
2087+
{
2088+
public:
2089+
explicit PersistentContact( SampleContext* context )
2090+
: Sample( context )
2091+
{
2092+
if ( m_context->restart == false )
2093+
{
2094+
m_context->camera.m_center = { 0.0f, 6.0f };
2095+
m_context->camera.m_zoom = 7.5f;
2096+
}
2097+
2098+
{
2099+
b2BodyDef bodyDef = b2DefaultBodyDef();
2100+
b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );
2101+
2102+
b2Vec2 points[22];
2103+
float x = 10.0f;
2104+
for ( int i = 0; i < 20; ++i )
2105+
{
2106+
points[i] = { x, 0.0f };
2107+
x -= 1.0f;
2108+
}
2109+
2110+
points[20] = { -9.0f, 10.0f };
2111+
points[21] = { 10.0f, 10.0f };
2112+
2113+
b2ChainDef chainDef = b2DefaultChainDef();
2114+
chainDef.points = points;
2115+
chainDef.count = 22;
2116+
chainDef.isLoop = true;
2117+
2118+
b2CreateChain( groundId, &chainDef );
2119+
}
2120+
2121+
{
2122+
b2BodyDef bodyDef = b2DefaultBodyDef();
2123+
bodyDef.type = b2_dynamicBody;
2124+
bodyDef.position = { -8.0f, 1.0f };
2125+
bodyDef.linearVelocity = { 2.0f, 0.0f };
2126+
2127+
b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );
2128+
2129+
b2ShapeDef shapeDef = b2DefaultShapeDef();
2130+
shapeDef.enableContactEvents = true;
2131+
b2Circle circle = { { 0.0f, 0.0f }, 0.5f };
2132+
b2CreateCircleShape( bodyId, &shapeDef, &circle );
2133+
}
2134+
2135+
m_contactId = b2_nullContactId;
2136+
}
2137+
2138+
void Step() override
2139+
{
2140+
Sample::Step();
2141+
2142+
b2ContactEvents events = b2World_GetContactEvents( m_worldId );
2143+
for ( int i = 0; i < events.beginCount && i < 1; ++i )
2144+
{
2145+
b2ContactBeginTouchEvent event = events.beginEvents[i];
2146+
m_contactId = events.beginEvents[i].contactId;
2147+
}
2148+
2149+
for ( int i = 0; i < events.endCount; ++i )
2150+
{
2151+
if ( B2_ID_EQUALS( m_contactId, events.endEvents[i].contactId ) )
2152+
{
2153+
m_contactId = b2_nullContactId;
2154+
break;
2155+
}
2156+
}
2157+
2158+
if (B2_IS_NON_NULL(m_contactId) && b2Contact_IsValid(m_contactId))
2159+
{
2160+
b2Manifold manifold = b2Contact_GetManifold( m_contactId );
2161+
2162+
for (int i = 0; i < manifold.pointCount; ++i)
2163+
{
2164+
const b2ManifoldPoint* manifoldPoint = manifold.points + i;
2165+
b2Vec2 p1 = manifoldPoint->point;
2166+
b2Vec2 p2 = p1 + manifoldPoint->totalNormalImpulse * manifold.normal;
2167+
m_draw->DrawSegment( p1, p2, b2_colorCrimson );
2168+
m_draw->DrawPoint( p1, 6.0f, b2_colorCrimson );
2169+
m_draw->DrawString( p1, "%.2f", manifoldPoint->totalNormalImpulse );
2170+
}
2171+
}
2172+
else
2173+
{
2174+
m_contactId = b2_nullContactId;
2175+
}
2176+
2177+
}
2178+
2179+
static Sample* Create( SampleContext* context )
2180+
{
2181+
return new PersistentContact( context );
2182+
}
2183+
2184+
b2ContactId m_contactId;
2185+
};
2186+
2187+
static int samplePersistentContact = RegisterSample( "Events", "Persistent Contact", PersistentContact::Create );

0 commit comments

Comments
 (0)