Box2D-0.1.0.0: upstream/src/contact_solver.c
// SPDX-FileCopyrightText: 2026 Erin Catto
// SPDX-License-Identifier: MIT
#include "contact_solver.h"
#include "body.h"
#include "constraint_graph.h"
#include "contact.h"
#include "core.h"
#include "physics_world.h"
#include "platform.h"
#include "solver_set.h"
#include <stddef.h>
// contact separation for sub-stepping
// s = s0 + dot(cB + rB - cA - rA, normal)
// normal is held constant
// body positions c can translation and anchors r can rotate
// s(t) = s0 + dot(cB(t) + rB(t) - cA(t) - rA(t), normal)
// s(t) = s0 + dot(cB0 + dpB + rot(dqB, rB0) - cA0 - dpA - rot(dqA, rA0), normal)
// s(t) = s0 + dot(cB0 - cA0, normal) + dot(dpB - dpA + rot(dqB, rB0) - rot(dqA, rA0), normal)
// s_base = s0 + dot(cB0 - cA0, normal)
void b2PrepareContacts_Overflow( b2StepContext* context )
{
b2TracyCZoneNC( prepare_overflow_contact, "Prepare Overflow Contact", b2_colorYellow, true );
b2World* world = context->world;
b2ConstraintGraph* graph = context->graph;
b2GraphColor* color = graph->colors + B2_OVERFLOW_INDEX;
b2ContactConstraint* constraints = color->overflowConstraints;
int contactCount = color->contactSims.count;
b2ContactSim* contacts = color->contactSims.data;
b2BodyState* states = context->states;
#if B2_ENABLE_VALIDATION
b2Body* bodies = world->bodies.data;
#endif
// Stiffer for static contacts to avoid bodies getting pushed through the ground
b2Softness contactSoftness = context->contactSoftness;
b2Softness staticSoftness = context->staticSoftness;
float warmStartScale = world->enableWarmStarting ? 1.0f : 0.0f;
bool anyRestitution = false;
for ( int i = 0; i < contactCount; ++i )
{
b2ContactSim* contactSim = contacts + i;
b2Manifold* manifold = &contactSim->manifold;
int pointCount = manifold->pointCount;
B2_ASSERT( 0 < pointCount && pointCount <= 2 );
int indexA = b2DecodeAwakeIndex( contactSim->encodedBodySimA );
int indexB = b2DecodeAwakeIndex( contactSim->encodedBodySimB );
#if B2_ENABLE_VALIDATION
b2Body* bodyA = bodies + contactSim->bodyIdA;
B2_ASSERT( contactSim->encodedBodySimA == b2EncodeBodySimIndex( bodyA ) );
b2Body* bodyB = bodies + contactSim->bodyIdB;
B2_ASSERT( contactSim->encodedBodySimB == b2EncodeBodySimIndex( bodyB ) );
#endif
b2ContactConstraint* constraint = constraints + i;
// 0 is null
constraint->indexA = indexA + 1;
constraint->indexB = indexB + 1;
constraint->normal = manifold->normal;
constraint->friction = contactSim->friction;
constraint->restitution = contactSim->restitution;
constraint->rollingResistance = contactSim->rollingResistance;
constraint->rollingImpulse = warmStartScale * manifold->rollingImpulse;
constraint->tangentSpeed = contactSim->tangentSpeed;
constraint->pointCount = pointCount;
bool haveRestitution = contactSim->restitution > 0.0f;
bool hitEvents = ( contactSim->simFlags & b2_simEnableHitEvent ) != 0;
bool sampleVelocity = haveRestitution || hitEvents;
anyRestitution = anyRestitution || haveRestitution;
b2Vec2 vA = b2Vec2_zero;
float wA = 0.0f;
b2Vec2 vB = b2Vec2_zero;
float wB = 0.0f;
if ( sampleVelocity )
{
if ( indexA != B2_NULL_INDEX )
{
vA = states[indexA].linearVelocity;
wA = states[indexA].angularVelocity;
}
if ( indexB != B2_NULL_INDEX )
{
vB = states[indexB].linearVelocity;
wB = states[indexB].angularVelocity;
}
}
float mA = contactSim->invMassA;
float iA = contactSim->invIA;
float mB = contactSim->invMassB;
float iB = contactSim->invIB;
if ( indexA == B2_NULL_INDEX || indexB == B2_NULL_INDEX )
{
constraint->softness = staticSoftness;
}
else
{
constraint->softness = contactSoftness;
}
// copy mass into constraint to avoid cache misses during sub-stepping
constraint->invMassA = mA;
constraint->invIA = iA;
constraint->invMassB = mB;
constraint->invIB = iB;
{
float k = iA + iB;
constraint->rollingMass = k > 0.0f ? 1.0f / k : 0.0f;
}
b2Vec2 normal = constraint->normal;
b2Vec2 tangent = b2RightPerp( constraint->normal );
for ( int j = 0; j < pointCount; ++j )
{
b2ManifoldPoint* mp = manifold->points + j;
b2ContactConstraintPoint* cp = constraint->points + j;
cp->normalImpulse = warmStartScale * mp->normalImpulse;
cp->tangentImpulse = warmStartScale * mp->tangentImpulse;
cp->totalNormalImpulse = 0.0f;
cp->restitutionImpulse = 0.0f;
b2Vec2 rA = mp->anchorA;
b2Vec2 rB = mp->anchorB;
cp->anchorA = rA;
cp->anchorB = rB;
cp->baseSeparation = mp->separation - b2Dot( b2Sub( rB, rA ), normal );
float rnA = b2Cross( rA, normal );
float rnB = b2Cross( rB, normal );
float kNormal = mA + mB + iA * rnA * rnA + iB * rnB * rnB;
cp->normalMass = kNormal > 0.0f ? 1.0f / kNormal : 0.0f;
float rtA = b2Cross( rA, tangent );
float rtB = b2Cross( rB, tangent );
float kTangent = mA + mB + iA * rtA * rtA + iB * rtB * rtB;
cp->tangentMass = kTangent > 0.0f ? 1.0f / kTangent : 0.0f;
// Only compute the normal velocity terms if needed.
if ( sampleVelocity )
{
b2Vec2 vrA = b2Add( vA, b2CrossSV( wA, rA ) );
b2Vec2 vrB = b2Add( vB, b2CrossSV( wB, rB ) );
float vn = b2Dot( normal, b2Sub( vrB, vrA ) );
cp->relativeVelocity = vn;
mp->normalVelocity = vn;
}
else
{
cp->relativeVelocity = 0.0f;
mp->normalVelocity = 0.0f;
}
}
}
if ( anyRestitution )
{
// Check if set first? Store per task context and OR?
b2AtomicStoreInt( &context->anyRestitution, 1 );
}
b2TracyCZoneEnd( prepare_overflow_contact );
}
void b2WarmStartContacts_Overflow( b2StepContext* context )
{
b2TracyCZoneNC( warmstart_overflow_contact, "WarmStart Overflow Contact", b2_colorDarkOrange, true );
b2ConstraintGraph* graph = context->graph;
b2GraphColor* color = graph->colors + B2_OVERFLOW_INDEX;
b2ContactConstraint* constraints = color->overflowConstraints;
int contactCount = color->contactSims.count;
b2World* world = context->world;
b2SolverSet* awakeSet = b2Array_Get( world->solverSets, b2_awakeSet );
b2BodyState* states = awakeSet->bodyStates.data;
// This is a dummy state to represent a static body because static bodies don't have a solver body.
b2BodyState dummyState = b2_identityBodyState;
for ( int i = 0; i < contactCount; ++i )
{
b2ContactConstraint* constraint = constraints + i;
int indexA = constraint->indexA - 1;
int indexB = constraint->indexB - 1;
b2BodyState* stateA = indexA == B2_NULL_INDEX ? &dummyState : states + indexA;
b2BodyState* stateB = indexB == B2_NULL_INDEX ? &dummyState : states + indexB;
b2Vec2 vA = stateA->linearVelocity;
float wA = stateA->angularVelocity;
b2Vec2 vB = stateB->linearVelocity;
float wB = stateB->angularVelocity;
float mA = constraint->invMassA;
float iA = constraint->invIA;
float mB = constraint->invMassB;
float iB = constraint->invIB;
// Stiffer for static contacts to avoid bodies getting pushed through the ground
b2Vec2 normal = constraint->normal;
b2Vec2 tangent = b2RightPerp( constraint->normal );
int pointCount = constraint->pointCount;
for ( int j = 0; j < pointCount; ++j )
{
b2ContactConstraintPoint* cp = constraint->points + j;
// fixed anchors
b2Vec2 rA = cp->anchorA;
b2Vec2 rB = cp->anchorB;
b2Vec2 P = b2Add( b2MulSV( cp->normalImpulse, normal ), b2MulSV( cp->tangentImpulse, tangent ) );
cp->totalNormalImpulse += cp->normalImpulse;
wA -= iA * b2Cross( rA, P );
vA = b2MulAdd( vA, -mA, P );
wB += iB * b2Cross( rB, P );
vB = b2MulAdd( vB, mB, P );
}
wA -= iA * constraint->rollingImpulse;
wB += iB * constraint->rollingImpulse;
if ( stateA->flags & b2_dynamicFlag )
{
stateA->linearVelocity = vA;
stateA->angularVelocity = wA;
}
if ( stateB->flags & b2_dynamicFlag )
{
stateB->linearVelocity = vB;
stateB->angularVelocity = wB;
}
}
b2TracyCZoneEnd( warmstart_overflow_contact );
}
// Solve the non-penetration constraints with the soft bias. No friction and no restitution.
void b2PushContacts_Overflow( b2StepContext* context )
{
b2TracyCZoneNC( push_contact, "Push Overflow Contact", b2_colorAliceBlue, true );
b2ConstraintGraph* graph = context->graph;
b2GraphColor* color = graph->colors + B2_OVERFLOW_INDEX;
b2ContactConstraint* constraints = color->overflowConstraints;
int contactCount = color->contactSims.count;
b2World* world = context->world;
b2SolverSet* awakeSet = b2Array_Get( world->solverSets, b2_awakeSet );
b2BodyState* states = awakeSet->bodyStates.data;
float inv_h = context->inv_h;
const float contactSpeed = context->world->contactSpeed;
// This is a dummy body to represent a static body since static bodies don't have a solver body.
b2BodyState dummyState = b2_identityBodyState;
for ( int i = 0; i < contactCount; ++i )
{
b2ContactConstraint* constraint = constraints + i;
float mA = constraint->invMassA;
float iA = constraint->invIA;
float mB = constraint->invMassB;
float iB = constraint->invIB;
int indexA = constraint->indexA - 1;
int indexB = constraint->indexB - 1;
b2BodyState* stateA = indexA == B2_NULL_INDEX ? &dummyState : states + indexA;
b2Vec2 vA = stateA->linearVelocity;
float wA = stateA->angularVelocity;
b2Rot dqA = stateA->deltaRotation;
b2BodyState* stateB = indexB == B2_NULL_INDEX ? &dummyState : states + indexB;
b2Vec2 vB = stateB->linearVelocity;
float wB = stateB->angularVelocity;
b2Rot dqB = stateB->deltaRotation;
b2Vec2 dp = b2Sub( stateB->deltaPosition, stateA->deltaPosition );
b2Vec2 normal = constraint->normal;
b2Softness softness = constraint->softness;
int pointCount = constraint->pointCount;
float vnc = b2Dot( b2Sub( vB, vA ), normal );
float rnA[2], rnB[2];
float impulses[2] = { 0.0f, 0.0f };
float normalImpulse = 0.0f;
float angularA = 0.0f;
float angularB = 0.0f;
for ( int j = 0; j < pointCount; ++j )
{
b2ContactConstraintPoint* cp = constraint->points + j;
// fixed anchor points
b2Vec2 rA = cp->anchorA;
b2Vec2 rB = cp->anchorB;
// compute current separation
// this is subject to round-off error if the anchor is far from the body center of mass
b2Vec2 ds = b2Add( dp, b2Sub( b2RotateVector( dqB, rB ), b2RotateVector( dqA, rA ) ) );
float s = cp->baseSeparation + b2Dot( ds, normal );
float velocityBias;
float massScale;
float impulseScale;
if ( s > 0.0f )
{
// speculative bias is positive
velocityBias = s * inv_h;
massScale = 1.0f;
impulseScale = 0.0f;
}
else
{
// overlap bias is negative
velocityBias = b2MaxFloat( softness.massScale * softness.biasRate * s, -contactSpeed );
massScale = softness.massScale;
impulseScale = softness.impulseScale;
}
rnA[j] = b2Cross( rA, normal );
rnB[j] = b2Cross( rB, normal );
// Relative normal velocity at contact.
float vn = vnc + wB * rnB[j] - wA * rnA[j];
for ( int k = 0; k < j; ++k )
{
// Point 2 only depends on the impulse from point 1.
vn += ( mA + mB + iA * rnA[k] * rnA[j] + iB * rnB[k] * rnB[j] ) * impulses[k];
}
float impulse = b2MaxFloat( -cp->normalMass * ( massScale * vn + velocityBias ) - impulseScale * cp->normalImpulse,
-cp->normalImpulse );
cp->normalImpulse += impulse;
cp->totalNormalImpulse += impulse;
impulses[j] = impulse;
// These accumulators are not used until the loop is done.
normalImpulse += impulse;
angularA += impulse * rnA[j];
angularB += impulse * rnB[j];
}
b2Vec2 P = b2MulSV( normalImpulse, normal );
vA = b2MulSub( vA, mA, P );
wA -= iA * angularA;
vB = b2MulAdd( vB, mB, P );
wB += iB * angularB;
if ( stateA->flags & b2_dynamicFlag )
{
stateA->linearVelocity = vA;
stateA->angularVelocity = wA;
}
if ( stateB->flags & b2_dynamicFlag )
{
stateB->linearVelocity = vB;
stateB->angularVelocity = wB;
}
}
b2TracyCZoneEnd( push_contact );
}
// Solve contacts: normal, friction and rolling resistance.
void b2SolveContacts_Overflow( b2StepContext* context )
{
b2TracyCZoneNC( solve_contact, "Solve Overflow Contact", b2_colorAliceBlue, true );
b2ConstraintGraph* graph = context->graph;
b2GraphColor* color = graph->colors + B2_OVERFLOW_INDEX;
b2ContactConstraint* constraints = color->overflowConstraints;
int contactCount = color->contactSims.count;
b2World* world = context->world;
b2SolverSet* awakeSet = b2Array_Get( world->solverSets, b2_awakeSet );
b2BodyState* states = awakeSet->bodyStates.data;
float inv_h = context->inv_h;
// This is a dummy body to represent a static body since static bodies don't have a solver body.
b2BodyState dummyState = b2_identityBodyState;
for ( int i = 0; i < contactCount; ++i )
{
b2ContactConstraint* constraint = constraints + i;
float mA = constraint->invMassA;
float iA = constraint->invIA;
float mB = constraint->invMassB;
float iB = constraint->invIB;
int indexA = constraint->indexA - 1;
int indexB = constraint->indexB - 1;
b2BodyState* stateA = indexA == B2_NULL_INDEX ? &dummyState : states + indexA;
b2Vec2 vA = stateA->linearVelocity;
float wA = stateA->angularVelocity;
b2Rot dqA = stateA->deltaRotation;
b2BodyState* stateB = indexB == B2_NULL_INDEX ? &dummyState : states + indexB;
b2Vec2 vB = stateB->linearVelocity;
float wB = stateB->angularVelocity;
b2Rot dqB = stateB->deltaRotation;
b2Vec2 dp = b2Sub( stateB->deltaPosition, stateA->deltaPosition );
b2Vec2 normal = constraint->normal;
b2Vec2 tangent = b2RightPerp( normal );
float friction = constraint->friction;
int pointCount = constraint->pointCount;
float totalNormalImpulse = 0.0f;
b2Vec2 dv = b2Sub( vB, vA );
float vnc = b2Dot( dv, normal );
// vt = dot(vrB - sB * tangent - (vrA + sA * tangent), tangent)
// = dot(vrB - vrA, tangent) - (sA + sB)
float vtc = b2Dot( dv, tangent ) - constraint->tangentSpeed;
float normalImpulses[2] = { 0.0f, 0.0f };
float tangentImpulses[2] = { 0.0f, 0.0f };
// Cache cross products
float rnA[2], rnB[2], rtA[2], rtB[2];
for ( int j = 0; j < pointCount; ++j )
{
b2ContactConstraintPoint* cp = constraint->points + j;
rnA[j] = b2Cross( cp->anchorA, normal );
rnB[j] = b2Cross( cp->anchorB, normal );
rtA[j] = b2Cross( cp->anchorA, tangent );
rtB[j] = b2Cross( cp->anchorB, tangent );
}
for ( int j = 0; j < pointCount; ++j )
{
b2ContactConstraintPoint* cp = constraint->points + j;
// Fixed anchor points.
b2Vec2 rA = cp->anchorA;
b2Vec2 rB = cp->anchorB;
// Compute current separation.
// This is subject to round-off error if the anchor is far from the body center of mass.
b2Vec2 ds = b2Add( dp, b2Sub( b2RotateVector( dqB, rB ), b2RotateVector( dqA, rA ) ) );
float s = cp->baseSeparation + b2Dot( ds, normal );
// Speculative bias, zero when overlapped.
float velocityBias = s > 0.0f ? s * inv_h : 0.0f;
// Relative normal velocity at contact.
float vn = vnc + wB * rnB[j] - wA * rnA[j];
for ( int k = 0; k < j; ++k )
{
// This allows getting point 2 normal velocity with only one dependency.
vn += ( mA + mB + iA * rnA[k] * rnA[j] + iB * rnB[k] * rnB[j] ) * normalImpulses[k];
}
float impulse = b2MaxFloat( -cp->normalMass * ( vn + velocityBias ), -cp->normalImpulse );
cp->normalImpulse += impulse;
cp->totalNormalImpulse += impulse;
normalImpulses[j] = impulse;
// This is used for rolling resistance
totalNormalImpulse += cp->normalImpulse;
}
// Rolling resistance
float rollingImpulse;
{
float Cdot = wB - wA;
for ( int j = 0; j < pointCount; ++j )
{
Cdot += ( iA * rnA[j] + iB * rnB[j] ) * normalImpulses[j];
}
float lambda = constraint->rollingImpulse;
float maxLambda = constraint->rollingResistance * totalNormalImpulse;
constraint->rollingImpulse = b2ClampFloat( lambda - constraint->rollingMass * Cdot, -maxLambda, maxLambda );
rollingImpulse = constraint->rollingImpulse - lambda;
}
// Friction
for ( int j = 0; j < pointCount; ++j )
{
b2ContactConstraintPoint* cp = constraint->points + j;
// relative tangent velocity at contact
float vt = vtc + wB * rtB[j] - wA * rtA[j] + ( iA * rtA[j] + iB * rtB[j] ) * rollingImpulse;
for ( int k = 0; k < pointCount; ++k )
{
vt += ( iA * rtA[j] * rnA[k] + iB * rtB[j] * rnB[k] ) * normalImpulses[k];
}
for ( int k = 0; k < j; ++k )
{
vt += ( mA + mB + iA * rtA[k] * rtA[j] + iB * rtB[k] * rtB[j] ) * tangentImpulses[k];
}
// clamp the accumulated force
float maxFriction = friction * cp->normalImpulse;
float impulse = b2ClampFloat( -cp->tangentMass * vt, -maxFriction - cp->tangentImpulse, maxFriction - cp->tangentImpulse );
cp->tangentImpulse += impulse;
tangentImpulses[j] = impulse;
}
float normalImpulse = 0.0f;
float tangentImpulse = 0.0f;
float angularA = rollingImpulse;
float angularB = rollingImpulse;
for ( int j = 0; j < pointCount; ++j )
{
normalImpulse += normalImpulses[j];
tangentImpulse += tangentImpulses[j];
angularA += normalImpulses[j] * rnA[j] + tangentImpulses[j] * rtA[j];
angularB += normalImpulses[j] * rnB[j] + tangentImpulses[j] * rtB[j];
}
b2Vec2 P = b2Add( b2MulSV( normalImpulse, normal ), b2MulSV( tangentImpulse, tangent ) );
vA = b2MulSub( vA, mA, P );
wA -= iA * angularA;
vB = b2MulAdd( vB, mB, P );
wB += iB * angularB;
if ( stateA->flags & b2_dynamicFlag )
{
stateA->linearVelocity = vA;
stateA->angularVelocity = wA;
}
if ( stateB->flags & b2_dynamicFlag )
{
stateB->linearVelocity = vB;
stateB->angularVelocity = wB;
}
}
b2TracyCZoneEnd( solve_contact );
}
void b2ApplyRestitution_Overflow( b2StepContext* context )
{
b2TracyCZoneNC( restitution_overflow, "Restitution Overflow", b2_colorViolet, true );
b2ConstraintGraph* graph = context->graph;
b2GraphColor* color = graph->colors + B2_OVERFLOW_INDEX;
b2ContactConstraint* constraints = color->overflowConstraints;
int contactCount = color->contactSims.count;
b2World* world = context->world;
b2SolverSet* awakeSet = b2Array_Get( world->solverSets, b2_awakeSet );
b2BodyState* states = awakeSet->bodyStates.data;
float threshold = world->restitutionThreshold;
float inv_h = context->inv_h;
bool propagate = world->enableRestitutionPropagation;
b2BodyState dummyState = b2_identityBodyState;
for ( int i = 0; i < contactCount; ++i )
{
b2ContactConstraint* constraint = constraints + i;
if ( propagate == false && constraint->restitution == 0.0f )
{
continue;
}
float mA = constraint->invMassA;
float iA = constraint->invIA;
float mB = constraint->invMassB;
float iB = constraint->invIB;
int indexA = constraint->indexA - 1;
int indexB = constraint->indexB - 1;
b2BodyState* stateA = indexA == B2_NULL_INDEX ? &dummyState : states + indexA;
b2Vec2 vA = stateA->linearVelocity;
float wA = stateA->angularVelocity;
b2Rot dqA = stateA->deltaRotation;
b2BodyState* stateB = indexB == B2_NULL_INDEX ? &dummyState : states + indexB;
b2Vec2 vB = stateB->linearVelocity;
float wB = stateB->angularVelocity;
b2Rot dqB = stateB->deltaRotation;
b2Vec2 dp = b2Sub( stateB->deltaPosition, stateA->deltaPosition );
b2Vec2 normal = constraint->normal;
float restitution = constraint->restitution;
int pointCount = constraint->pointCount;
for ( int j = 0; j < pointCount; ++j )
{
b2ContactConstraintPoint* cp = constraint->points + j;
b2Vec2 rA = cp->anchorA;
b2Vec2 rB = cp->anchorB;
// The total normal impulse is 0 for speculative points.
float compressionImpulse = cp->totalNormalImpulse - cp->restitutionImpulse;
bool armed = restitution > 0.0f && cp->relativeVelocity < -threshold && compressionImpulse > 0.0f;
float velocityBias;
if ( armed )
{
velocityBias = restitution * cp->relativeVelocity;
}
else
{
b2Vec2 ds = b2Add( dp, b2Sub( b2RotateVector( dqB, rB ), b2RotateVector( dqA, rA ) ) );
float s = cp->baseSeparation + b2Dot( ds, normal );
velocityBias = s > 0.0f ? s * inv_h : 0.0f;
}
b2Vec2 vrA = b2Add( vA, b2CrossSV( wA, rA ) );
b2Vec2 vrB = b2Add( vB, b2CrossSV( wB, rB ) );
float vn = b2Dot( b2Sub( vrB, vrA ), normal );
float impulse = -cp->normalMass * ( vn + velocityBias );
float newImpulse = b2MaxFloat( cp->normalImpulse + impulse, 0.0f );
impulse = newImpulse - cp->normalImpulse;
float approachImpulse = b2MinFloat( b2MaxFloat( -cp->normalMass * vn, 0.0f ), b2MaxFloat( impulse, 0.0f ) );
if ( armed )
{
// Poisson kinetic restitution guarantees no energy gain.
float allowance = restitution * ( compressionImpulse + approachImpulse ) - cp->restitutionImpulse;
impulse = b2MinFloat( impulse, approachImpulse + b2MaxFloat( allowance, 0.0f ) );
}
cp->normalImpulse += impulse;
cp->restitutionImpulse += impulse - approachImpulse;
cp->totalNormalImpulse += impulse;
b2Vec2 P = b2MulSV( impulse, normal );
vA = b2MulSub( vA, mA, P );
wA -= iA * b2Cross( rA, P );
vB = b2MulAdd( vB, mB, P );
wB += iB * b2Cross( rB, P );
}
if ( stateA->flags & b2_dynamicFlag )
{
stateA->linearVelocity = vA;
stateA->angularVelocity = wA;
}
if ( stateB->flags & b2_dynamicFlag )
{
stateB->linearVelocity = vB;
stateB->angularVelocity = wB;
}
}
b2TracyCZoneEnd( restitution_overflow );
}
void b2StoreImpulses_Overflow( b2StepContext* context )
{
b2TracyCZoneNC( store_impulses, "Store", b2_colorFireBrick, true );
b2World* world = context->world;
b2ConstraintGraph* graph = context->graph;
b2GraphColor* color = graph->colors + B2_OVERFLOW_INDEX;
b2ContactConstraint* constraints = color->overflowConstraints;
b2ContactSim* contactSims = color->contactSims.data;
b2TaskContext* taskContext = world->taskContexts.data + 0;
b2BitSet* hitEventBitSet = &taskContext->hitEventBitSet;
int contactCount = color->contactSims.count;
float negHitThreshold = -world->hitEventThreshold;
bool hasHitEvents = taskContext->hasHitEvents;
for ( int i = 0; i < contactCount; ++i )
{
const b2ContactConstraint* constraint = constraints + i;
b2ContactSim* contactSim = contactSims + i;
b2Manifold* manifold = &contactSim->manifold;
int pointCount = manifold->pointCount;
for ( int j = 0; j < pointCount; ++j )
{
b2ManifoldPoint* mp = manifold->points + j;
mp->normalImpulse = constraint->points[j].normalImpulse;
mp->tangentImpulse = constraint->points[j].tangentImpulse;
mp->totalNormalImpulse = constraint->points[j].totalNormalImpulse;
}
if ( ( contactSim->simFlags & b2_simEnableHitEvent ) != 0 )
{
for ( int j = 0; j < contactSim->manifold.pointCount; ++j )
{
b2ManifoldPoint* mp = manifold->points + j;
// Need to check total impulse because the point may be speculative and not colliding
if ( mp->normalVelocity < negHitThreshold && mp->totalNormalImpulse > 0.0f )
{
b2SetBit( hitEventBitSet, contactSim->contactId );
hasHitEvents = true;
break;
}
}
}
manifold->rollingImpulse = constraint->rollingImpulse;
}
taskContext->hasHitEvents = hasHitEvents;
b2TracyCZoneEnd( store_impulses );
}
void b2PrepareContacts_Wide( b2SolverBlock block, b2StepContext* context )
{
#if defined( B2_SIMD_HAS_WIDTH_8 )
if ( context->world->simdWidth == 8 )
{
b2PrepareContacts_WideW8( block, context );
return;
}
#endif
b2PrepareContacts_WideW4( block, context );
}
void b2WarmStartContacts_Wide( b2SolverBlock block, b2StepContext* context )
{
#if defined( B2_SIMD_HAS_WIDTH_8 )
if ( context->world->simdWidth == 8 )
{
b2WarmStartContacts_WideW8( block, context );
return;
}
#endif
b2WarmStartContacts_WideW4( block, context );
}
void b2PushContacts_Wide( b2SolverBlock block, b2StepContext* context )
{
#if defined( B2_SIMD_HAS_WIDTH_8 )
if ( context->world->simdWidth == 8 )
{
b2PushContacts_WideW8( block, context );
return;
}
#endif
b2PushContacts_WideW4( block, context );
}
void b2SolveContacts_Wide( b2SolverBlock block, b2StepContext* context )
{
#if defined( B2_SIMD_HAS_WIDTH_8 )
if ( context->world->simdWidth == 8 )
{
b2SolveContacts_WideW8( block, context );
return;
}
#endif
b2SolveContacts_WideW4( block, context );
}
void b2ApplyRestitution_Wide( b2SolverBlock block, b2StepContext* context )
{
#if defined( B2_SIMD_HAS_WIDTH_8 )
if ( context->world->simdWidth == 8 )
{
b2ApplyRestitution_WideW8( block, context );
return;
}
#endif
b2ApplyRestitution_WideW4( block, context );
}
void b2StoreImpulses_Wide( b2SolverBlock block, b2StepContext* context, int workerIndex )
{
#if defined( B2_SIMD_HAS_WIDTH_8 )
if ( context->world->simdWidth == 8 )
{
b2StoreImpulses_WideW8( block, context, workerIndex );
return;
}
#endif
b2StoreImpulses_WideW4( block, context, workerIndex );
}
int b2GetWideContactConstraintByteCount( int simdWidth )
{
#if defined( B2_SIMD_HAS_WIDTH_8 )
if ( simdWidth == 8 )
{
return b2GetWideContactConstraintByteCountW8();
}
#else
B2_UNUSED( simdWidth );
#endif
return b2GetWideContactConstraintByteCountW4();
}