Skip to content

Commit

Permalink
manifold points now store the total normal impulse instead of the max…
Browse files Browse the repository at this point in the history
…imum
  • Loading branch information
erincatto committed Feb 22, 2025
1 parent d671b72 commit febfd55
Show file tree
Hide file tree
Showing 9 changed files with 66 additions and 72 deletions.
13 changes: 6 additions & 7 deletions include/box2d/collision.h
Original file line number Diff line number Diff line change
Expand Up @@ -382,10 +382,9 @@ typedef struct b2DistanceInput
/// Output for b2ShapeDistance
typedef struct b2DistanceOutput
{
b2Vec2 pointA; ///< Closest point on shapeA
b2Vec2 pointB; ///< Closest point on shapeB
// todo_erin implement this
// b2Vec2 normal; ///< Normal vector that points from A to B
b2Vec2 pointA; ///< Closest point on shapeA
b2Vec2 pointB; ///< Closest point on shapeB
b2Vec2 normal; ///< Normal vector that points from A to B
float distance; ///< The final distance, zero if overlapped
int iterations; ///< Number of GJK iterations used
int simplexCount; ///< The number of simplexes stored in the simplex array
Expand Down Expand Up @@ -491,7 +490,7 @@ B2_API b2TOIOutput b2TimeOfImpact( const b2TOIInput* input );
/// A manifold point is a contact point belonging to a contact manifold.
/// It holds details related to the geometry and dynamics of the contact points.
/// Box2D uses speculative collision so some contact points may be separated.
/// You may use the maxNormalImpulse to determine if there was an interaction during
/// You may use the totalNormalImpulse to determine if there was an interaction during
/// the time step.
typedef struct b2ManifoldPoint
{
Expand All @@ -516,9 +515,9 @@ typedef struct b2ManifoldPoint
/// The friction impulse
float tangentImpulse;

/// The maximum normal impulse applied during sub-stepping. This is important
/// The total normal impulse applied across sub-stepping and restitution. This is important
/// to identify speculative contact points that had an interaction in the time step.
float maxNormalImpulse;
float totalNormalImpulse;

/// Relative normal velocity pre-solve. Used for hit events. If the normal impulse is
/// zero then there was no hit. Negative means shapes are approaching.
Expand Down
26 changes: 15 additions & 11 deletions samples/sample_collision.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1229,7 +1229,7 @@ struct ShapeUserData
};

// Context for ray cast callbacks. Do what you want with this.
struct RayCastContext
struct CastContext
{
b2Vec2 points[3];
b2Vec2 normals[3];
Expand All @@ -1240,7 +1240,7 @@ struct RayCastContext
// This callback finds the closest hit. This is the most common callback used in games.
static float RayCastClosestCallback( b2ShapeId shapeId, b2Vec2 point, b2Vec2 normal, float fraction, void* context )
{
RayCastContext* rayContext = (RayCastContext*)context;
CastContext* rayContext = (CastContext*)context;

ShapeUserData* userData = (ShapeUserData*)b2Shape_GetUserData( shapeId );
if ( userData != nullptr && userData->ignore )
Expand All @@ -1266,7 +1266,7 @@ static float RayCastClosestCallback( b2ShapeId shapeId, b2Vec2 point, b2Vec2 nor
// NOTE: shape hits are not ordered, so this may not return the closest hit
static float RayCastAnyCallback( b2ShapeId shapeId, b2Vec2 point, b2Vec2 normal, float fraction, void* context )
{
RayCastContext* rayContext = (RayCastContext*)context;
CastContext* rayContext = (CastContext*)context;

ShapeUserData* userData = (ShapeUserData*)b2Shape_GetUserData( shapeId );
if ( userData != nullptr && userData->ignore )
Expand Down Expand Up @@ -1294,7 +1294,7 @@ static float RayCastAnyCallback( b2ShapeId shapeId, b2Vec2 point, b2Vec2 normal,
// behavior in the sample.
static float RayCastMultipleCallback( b2ShapeId shapeId, b2Vec2 point, b2Vec2 normal, float fraction, void* context )
{
RayCastContext* rayContext = (RayCastContext*)context;
CastContext* rayContext = (CastContext*)context;

ShapeUserData* userData = (ShapeUserData*)b2Shape_GetUserData( shapeId );
if ( userData != nullptr && userData->ignore )
Expand Down Expand Up @@ -1326,7 +1326,7 @@ static float RayCastMultipleCallback( b2ShapeId shapeId, b2Vec2 point, b2Vec2 no
// This ray cast collects multiple hits along the ray and sorts them.
static float RayCastSortedCallback( b2ShapeId shapeId, b2Vec2 point, b2Vec2 normal, float fraction, void* context )
{
RayCastContext* rayContext = (RayCastContext*)context;
CastContext* rayContext = (CastContext*)context;

ShapeUserData* userData = (ShapeUserData*)b2Shape_GetUserData( shapeId );
if ( userData != nullptr && userData->ignore )
Expand Down Expand Up @@ -1380,7 +1380,7 @@ static float RayCastSortedCallback( b2ShapeId shapeId, b2Vec2 point, b2Vec2 norm
return 1.0f;
}

class RayCastWorld : public Sample
class CastWorld : public Sample
{
public:
enum Mode
Expand All @@ -1404,7 +1404,7 @@ class RayCastWorld : public Sample
e_maxCount = 64
};

explicit RayCastWorld( Settings& settings )
explicit CastWorld( Settings& settings )
: Sample( settings )
{
if ( settings.restart == false )
Expand Down Expand Up @@ -1602,7 +1602,7 @@ class RayCastWorld : public Sample

void UpdateGui() override
{
float height = 300.0f;
float height = 320.0f;
ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once );
ImGui::SetNextWindowSize( ImVec2( 200.0f, height ) );

Expand Down Expand Up @@ -1739,6 +1739,10 @@ class RayCastWorld : public Sample
case e_sorted:
g_draw.DrawString( 5, m_textLine, "Cast mode: sorted - gather up to 3 shapes sorted by closeness" );
break;

default:
assert( false );
break;
}

m_textLine += m_textIncrement;
Expand All @@ -1747,7 +1751,7 @@ class RayCastWorld : public Sample
RayCastSortedCallback };
b2CastResultFcn* modeFcn = fcns[m_mode];

RayCastContext context = { };
CastContext context = { };

// Must initialize fractions for sorting
context.fractions[0] = FLT_MAX;
Expand Down Expand Up @@ -1847,7 +1851,7 @@ class RayCastWorld : public Sample

static Sample* Create( Settings& settings )
{
return new RayCastWorld( settings );
return new CastWorld( settings );
}

int m_bodyIndex;
Expand Down Expand Up @@ -1876,7 +1880,7 @@ class RayCastWorld : public Sample
bool m_dragging;
};

static int sampleRayCastWorld = RegisterSample( "Collision", "Ray Cast World", RayCastWorld::Create );
static int sampleRayCastWorld = RegisterSample( "Collision", "Ray Cast World", CastWorld::Create );

class OverlapWorld : public Sample
{
Expand Down
4 changes: 2 additions & 2 deletions samples/sample_events.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1016,7 +1016,7 @@ class ContactEvent : public Sample
for ( int k = 0; k < manifold.pointCount; ++k )
{
b2ManifoldPoint point = manifold.points[k];
g_draw.DrawSegment( point.point, point.point + point.maxNormalImpulse * normal, b2_colorBlueViolet );
g_draw.DrawSegment( point.point, point.point + point.totalNormalImpulse * normal, b2_colorBlueViolet );
g_draw.DrawPoint( point.point, 10.0f, b2_colorWhite );
}
}
Expand Down Expand Up @@ -1046,7 +1046,7 @@ class ContactEvent : public Sample
for ( int k = 0; k < manifold.pointCount; ++k )
{
b2ManifoldPoint point = manifold.points[k];
g_draw.DrawSegment( point.point, point.point + point.maxNormalImpulse * normal, b2_colorYellowGreen );
g_draw.DrawSegment( point.point, point.point + point.totalNormalImpulse * normal, b2_colorYellowGreen );
g_draw.DrawPoint( point.point, 10.0f, b2_colorWhite );
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/contact.c
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,7 @@ bool b2UpdateContact( b2World* world, b2ContactSim* contactSim, b2Shape* shapeA,

mp2->normalImpulse = 0.0f;
mp2->tangentImpulse = 0.0f;
mp2->maxNormalImpulse = 0.0f;
mp2->totalNormalImpulse = 0.0f;
mp2->normalVelocity = 0.0f;
mp2->persisted = false;

Expand Down
42 changes: 20 additions & 22 deletions src/contact_solver.c
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ void b2PrepareOverflowContacts( b2StepContext* context )

cp->normalImpulse = warmStartScale * mp->normalImpulse;
cp->tangentImpulse = warmStartScale * mp->tangentImpulse;
cp->maxNormalImpulse = 0.0f;
cp->totalNormalImpulse = 0.0f;

b2Vec2 rA = mp->anchorA;
b2Vec2 rB = mp->anchorB;
Expand Down Expand Up @@ -311,7 +311,7 @@ void b2SolveOverflowContacts( b2StepContext* context, bool useBias )
float newImpulse = b2MaxFloat( cp->normalImpulse + impulse, 0.0f );
impulse = newImpulse - cp->normalImpulse;
cp->normalImpulse = newImpulse;
cp->maxNormalImpulse = b2MaxFloat( cp->maxNormalImpulse, impulse );
cp->totalNormalImpulse += newImpulse;
totalNormalImpulse += newImpulse;

// apply normal impulse
Expand Down Expand Up @@ -433,7 +433,7 @@ void b2ApplyOverflowRestitution( b2StepContext* context )
// if the normal impulse is zero then there was no collision
// this skips speculative contact points that didn't generate an impulse
// The max normal impulse is used in case there was a collision that moved away within the sub-step process
if ( cp->relativeVelocity > -threshold || cp->maxNormalImpulse == 0.0f )
if ( cp->relativeVelocity > -threshold || cp->totalNormalImpulse == 0.0f )
{
continue;
}
Expand All @@ -455,7 +455,7 @@ void b2ApplyOverflowRestitution( b2StepContext* context )
float newImpulse = b2MaxFloat( cp->normalImpulse + impulse, 0.0f );
impulse = newImpulse - cp->normalImpulse;
cp->normalImpulse = newImpulse;
cp->maxNormalImpulse = b2MaxFloat( cp->maxNormalImpulse, impulse );
cp->totalNormalImpulse += impulse;

// apply contact impulse
b2Vec2 P = b2MulSV( impulse, normal );
Expand Down Expand Up @@ -485,8 +485,6 @@ void b2StoreOverflowImpulses( b2StepContext* context )
b2ContactSim* contacts = color->contactSims.data;
int contactCount = color->contactSims.count;

// float hitEventThreshold = context->world->hitEventThreshold;

for ( int i = 0; i < contactCount; ++i )
{
const b2ContactConstraint* constraint = constraints + i;
Expand All @@ -498,7 +496,7 @@ void b2StoreOverflowImpulses( b2StepContext* context )
{
manifold->points[j].normalImpulse = constraint->points[j].normalImpulse;
manifold->points[j].tangentImpulse = constraint->points[j].tangentImpulse;
manifold->points[j].maxNormalImpulse = constraint->points[j].maxNormalImpulse;
manifold->points[j].totalNormalImpulse = constraint->points[j].totalNormalImpulse;
manifold->points[j].normalVelocity = constraint->points[j].relativeVelocity;
}

Expand Down Expand Up @@ -1001,12 +999,12 @@ typedef struct b2ContactConstraintSIMD
b2FloatW normalMass1, tangentMass1;
b2FloatW baseSeparation1;
b2FloatW normalImpulse1;
b2FloatW maxNormalImpulse1;
b2FloatW totalNormalImpulse1;
b2FloatW tangentImpulse1;
b2Vec2W anchorA2, anchorB2;
b2FloatW baseSeparation2;
b2FloatW normalImpulse2;
b2FloatW maxNormalImpulse2;
b2FloatW totalNormalImpulse2;
b2FloatW tangentImpulse2;
b2FloatW normalMass2, tangentMass2;
b2FloatW restitution;
Expand Down Expand Up @@ -1492,7 +1490,7 @@ void b2PrepareContactsTask( int startIndex, int endIndex, b2StepContext* context

( (float*)&constraint->normalImpulse1 )[j] = warmStartScale * mp->normalImpulse;
( (float*)&constraint->tangentImpulse1 )[j] = warmStartScale * mp->tangentImpulse;
( (float*)&constraint->maxNormalImpulse1 )[j] = 0.0f;
( (float*)&constraint->totalNormalImpulse1 )[j] = 0.0f;

float rnA = b2Cross( rA, normal );
float rnB = b2Cross( rB, normal );
Expand Down Expand Up @@ -1529,7 +1527,7 @@ void b2PrepareContactsTask( int startIndex, int endIndex, b2StepContext* context

( (float*)&constraint->normalImpulse2 )[j] = warmStartScale * mp->normalImpulse;
( (float*)&constraint->tangentImpulse2 )[j] = warmStartScale * mp->tangentImpulse;
( (float*)&constraint->maxNormalImpulse2 )[j] = 0.0f;
( (float*)&constraint->totalNormalImpulse2 )[j] = 0.0f;

float rnA = b2Cross( rA, normal );
float rnB = b2Cross( rB, normal );
Expand All @@ -1552,7 +1550,7 @@ void b2PrepareContactsTask( int startIndex, int endIndex, b2StepContext* context
( (float*)&constraint->baseSeparation2 )[j] = 0.0f;
( (float*)&constraint->normalImpulse2 )[j] = 0.0f;
( (float*)&constraint->tangentImpulse2 )[j] = 0.0f;
( (float*)&constraint->maxNormalImpulse2 )[j] = 0.0f;
( (float*)&constraint->totalNormalImpulse2 )[j] = 0.0f;
( (float*)&constraint->anchorA2.X )[j] = 0.0f;
( (float*)&constraint->anchorA2.Y )[j] = 0.0f;
( (float*)&constraint->anchorB2.X )[j] = 0.0f;
Expand Down Expand Up @@ -1591,7 +1589,7 @@ void b2PrepareContactsTask( int startIndex, int endIndex, b2StepContext* context
( (float*)&constraint->baseSeparation1 )[j] = 0.0f;
( (float*)&constraint->normalImpulse1 )[j] = 0.0f;
( (float*)&constraint->tangentImpulse1 )[j] = 0.0f;
( (float*)&constraint->maxNormalImpulse1 )[j] = 0.0f;
( (float*)&constraint->totalNormalImpulse1 )[j] = 0.0f;
( (float*)&constraint->normalMass1 )[j] = 0.0f;
( (float*)&constraint->tangentMass1 )[j] = 0.0f;

Expand All @@ -1602,7 +1600,7 @@ void b2PrepareContactsTask( int startIndex, int endIndex, b2StepContext* context
( (float*)&constraint->baseSeparation2 )[j] = 0.0f;
( (float*)&constraint->normalImpulse2 )[j] = 0.0f;
( (float*)&constraint->tangentImpulse2 )[j] = 0.0f;
( (float*)&constraint->maxNormalImpulse2 )[j] = 0.0f;
( (float*)&constraint->totalNormalImpulse2 )[j] = 0.0f;
( (float*)&constraint->normalMass2 )[j] = 0.0f;
( (float*)&constraint->tangentMass2 )[j] = 0.0f;

Expand Down Expand Up @@ -1749,7 +1747,7 @@ void b2SolveContactsTask( int startIndex, int endIndex, b2StepContext* context,
b2FloatW newImpulse = b2MaxW( b2SubW( c->normalImpulse1, negImpulse ), b2ZeroW() );
b2FloatW impulse = b2SubW( newImpulse, c->normalImpulse1 );
c->normalImpulse1 = newImpulse;
c->maxNormalImpulse1 = b2MaxW( c->maxNormalImpulse1, newImpulse );
c->totalNormalImpulse1 = b2AddW( c->totalNormalImpulse1, newImpulse );

totalNormalImpulse = b2AddW( totalNormalImpulse, newImpulse );

Expand Down Expand Up @@ -1801,7 +1799,7 @@ void b2SolveContactsTask( int startIndex, int endIndex, b2StepContext* context,
b2FloatW newImpulse = b2MaxW( b2SubW( c->normalImpulse2, negImpulse ), b2ZeroW() );
b2FloatW impulse = b2SubW( newImpulse, c->normalImpulse2 );
c->normalImpulse2 = newImpulse;
c->maxNormalImpulse2 = b2MaxW( c->maxNormalImpulse2, newImpulse );
c->totalNormalImpulse2 = b2AddW( c->totalNormalImpulse2, newImpulse );

totalNormalImpulse = b2AddW( totalNormalImpulse, newImpulse );

Expand Down Expand Up @@ -1934,7 +1932,7 @@ void b2ApplyRestitutionTask( int startIndex, int endIndex, b2StepContext* contex
{
// Set effective mass to zero if restitution should not be applied
b2FloatW mask1 = b2GreaterThanW( b2AddW( c->relativeVelocity1, threshold ), zero );
b2FloatW mask2 = b2EqualsW( c->maxNormalImpulse1, zero );
b2FloatW mask2 = b2EqualsW( c->totalNormalImpulse1, zero );
b2FloatW mask = b2OrW( mask1, mask2 );
b2FloatW mass = b2BlendW( c->normalMass1, zero, mask );

Expand Down Expand Up @@ -1972,7 +1970,7 @@ void b2ApplyRestitutionTask( int startIndex, int endIndex, b2StepContext* contex
{
// Set effective mass to zero if restitution should not be applied
b2FloatW mask1 = b2GreaterThanW( b2AddW( c->relativeVelocity2, threshold ), zero );
b2FloatW mask2 = b2EqualsW( c->maxNormalImpulse2, zero );
b2FloatW mask2 = b2EqualsW( c->totalNormalImpulse2, zero );
b2FloatW mask = b2OrW( mask1, mask2 );
b2FloatW mass = b2BlendW( c->normalMass2, zero, mask );

Expand Down Expand Up @@ -2030,8 +2028,8 @@ void b2StoreImpulsesTask( int startIndex, int endIndex, b2StepContext* context )
const float* normalImpulse2 = (float*)&c->normalImpulse2;
const float* tangentImpulse1 = (float*)&c->tangentImpulse1;
const float* tangentImpulse2 = (float*)&c->tangentImpulse2;
const float* maxNormalImpulse1 = (float*)&c->maxNormalImpulse1;
const float* maxNormalImpulse2 = (float*)&c->maxNormalImpulse2;
const float* totalNormalImpulse1 = (float*)&c->totalNormalImpulse1;
const float* totalNormalImpulse2 = (float*)&c->totalNormalImpulse2;
const float* normalVelocity1 = (float*)&c->relativeVelocity1;
const float* normalVelocity2 = (float*)&c->relativeVelocity2;

Expand All @@ -2044,12 +2042,12 @@ void b2StoreImpulsesTask( int startIndex, int endIndex, b2StepContext* context )

m->points[0].normalImpulse = normalImpulse1[laneIndex];
m->points[0].tangentImpulse = tangentImpulse1[laneIndex];
m->points[0].maxNormalImpulse = maxNormalImpulse1[laneIndex];
m->points[0].totalNormalImpulse = totalNormalImpulse1[laneIndex];
m->points[0].normalVelocity = normalVelocity1[laneIndex];

m->points[1].normalImpulse = normalImpulse2[laneIndex];
m->points[1].tangentImpulse = tangentImpulse2[laneIndex];
m->points[1].maxNormalImpulse = maxNormalImpulse2[laneIndex];
m->points[1].totalNormalImpulse = totalNormalImpulse2[laneIndex];
m->points[1].normalVelocity = normalVelocity2[laneIndex];
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/contact_solver.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ typedef struct b2ContactConstraintPoint
float relativeVelocity;
float normalImpulse;
float tangentImpulse;
float maxNormalImpulse;
float totalNormalImpulse;
float normalMass;
float tangentMass;
} b2ContactConstraintPoint;
Expand Down
Loading

0 comments on commit febfd55

Please sign in to comment.