#include "algorithm.h"
#include "manifold.h"
#include "shape.h"
#include "box3d/base.h"
#include "box3d/collision.h"
#include "box3d/constants.h"
#include <stdbool.h>
#include <stddef.h>
static inline bool b3IsMinkowskiFaceIsolated( b3Vec3 a, b3Vec3 b, b3Vec3 n )
{
float an = b3Dot( a, n );
float bn = b3Dot( b, n );
return an * bn <= 0.0f;
}
static inline bool b3IsMinkowskiFace( b3Vec3 a, b3Vec3 b, b3Vec3 bxa, b3Vec3 c, b3Vec3 d, b3Vec3 dxc )
{
float cba = b3Dot( c, bxa );
float dba = b3Dot( d, bxa );
float adc = b3Dot( a, dxc );
float bdc = b3Dot( b, dxc );
return cba * dba < 0.0f && adc * bdc < 0.0f && cba * bdc > 0.0f;
}
static int b3ClipSegment( b3ClipVertex segment[2], b3Plane plane )
{
int vertexCount = 0;
b3ClipVertex vertex1 = segment[0];
b3ClipVertex vertex2 = segment[1];
float distance1 = b3PlaneSeparation( plane, vertex1.position );
float distance2 = b3PlaneSeparation( plane, vertex2.position );
if ( distance1 <= 0.0f )
{
segment[vertexCount++] = vertex1;
}
if ( distance2 <= 0.0f )
{
segment[vertexCount++] = vertex2;
}
if ( distance1 * distance2 < 0.0f )
{
float t = distance1 / ( distance1 - distance2 );
segment[vertexCount].position = b3Add( b3MulSV( 1.0f - t, vertex1.position ), b3MulSV( t, vertex2.position ) );
segment[vertexCount].pair = distance1 > 0.0f ? vertex1.pair : vertex2.pair;
vertexCount++;
}
return vertexCount;
}
static int b3ClipSegmentToHullFace( b3ClipVertex segment[2], const b3HullData* hull, int refFace )
{
const b3HullFace* faces = b3GetHullFaces( hull );
const b3Plane* planes = b3GetHullPlanes( hull );
const b3HullHalfEdge* edges = b3GetHullEdges( hull );
const b3Vec3* points = b3GetHullPoints( hull );
b3Plane refPlane = planes[refFace];
const b3HullFace* face = faces + refFace;
int edgeIndex = face->edge;
do
{
const b3HullHalfEdge* edge = edges + edgeIndex;
int nextEdgeIndex = edge->next;
const b3HullHalfEdge* next = edges + nextEdgeIndex;
b3Vec3 vertex1 = points[edge->origin];
b3Vec3 vertex2 = points[next->origin];
b3Vec3 tangent = b3Normalize( b3Sub( vertex2, vertex1 ) );
b3Vec3 binormal = b3Cross( tangent, refPlane.normal );
int pointCount = b3ClipSegment( segment, b3MakePlaneFromNormalAndPoint( binormal, vertex1 ) );
if ( pointCount < 2 )
{
return 0;
}
edgeIndex = nextEdgeIndex;
}
while ( edgeIndex != face->edge );
return 2;
}
static b3FaceQuery b3QueryFaceDirectionHullAndCapsule( const b3HullData* hull, const b3Capsule* capsule,
b3Transform capsuleTransform )
{
int maxFaceIndex = -1;
int maxVertexIndex = -1;
float maxFaceSeparation = -FLT_MAX;
const b3Plane* planes = b3GetHullPlanes( hull );
b3Vec3 capsulePoints[2] = {
b3TransformPoint( capsuleTransform, capsule->center1 ),
b3TransformPoint( capsuleTransform, capsule->center2 ),
};
for ( int faceIndex = 0; faceIndex < hull->faceCount; ++faceIndex )
{
b3Plane plane = planes[faceIndex];
int vertexIndex = b3GetPointSupport( capsulePoints, 2, b3Neg( plane.normal ) );
b3Vec3 support = capsulePoints[vertexIndex];
float separation = b3PlaneSeparation( plane, support );
if ( separation > maxFaceSeparation )
{
maxVertexIndex = vertexIndex;
maxFaceIndex = faceIndex;
maxFaceSeparation = separation;
}
}
return (b3FaceQuery){
.separation = maxFaceSeparation,
.faceIndex = (uint8_t)maxFaceIndex,
.vertexIndex = (uint8_t)maxVertexIndex,
};
}
static b3FaceQuery b3QueryFaceDirections( const b3HullData* hullA, const b3HullData* hullB, b3Transform relativeTransform )
{
b3Transform transform = b3InvertTransform( relativeTransform );
const b3Plane* planesA = b3GetHullPlanes( hullA );
const b3Vec3* pointsB = b3GetHullPoints( hullB );
int maxFaceIndex = -1;
int maxVertexIndex = -1;
float maxFaceSeparation = -FLT_MAX;
for ( int faceIndex = 0; faceIndex < hullA->faceCount; ++faceIndex )
{
b3Plane plane = b3TransformPlane( transform, planesA[faceIndex] );
int vertexIndex = b3FindHullSupportVertex( hullB, b3Neg( plane.normal ) );
b3Vec3 support = pointsB[vertexIndex];
float separation = b3PlaneSeparation( plane, support );
if ( separation > maxFaceSeparation )
{
maxFaceIndex = faceIndex;
maxVertexIndex = vertexIndex;
maxFaceSeparation = separation;
}
}
return (b3FaceQuery){
.separation = maxFaceSeparation,
.faceIndex = (uint8_t)maxFaceIndex,
.vertexIndex = (uint8_t)maxVertexIndex,
};
}
static b3EdgeQuery b3QueryEdgeDirectionHullAndCapsule( const b3HullData* hull, const b3Capsule* capsule,
b3Transform capsuleTransform )
{
float maxSeparation = -FLT_MAX;
int maxIndex1 = -1;
int maxIndex2 = -1;
b3Vec3 p1 = b3TransformPoint( capsuleTransform, capsule->center1 );
b3Vec3 q1 = b3TransformPoint( capsuleTransform, capsule->center2 );
b3Vec3 e1 = b3Sub( q1, p1 );
const b3HullHalfEdge* edges = b3GetHullEdges( hull );
const b3Vec3* points = b3GetHullPoints( hull );
const b3Plane* planes = b3GetHullPlanes( hull );
for ( int index = 0; index < hull->edgeCount; index += 2 )
{
const b3HullHalfEdge* edge = edges + index;
const b3HullHalfEdge* twin = edges + index + 1;
B3_ASSERT( edge->twin == index + 1 && twin->twin == index );
b3Vec3 p2 = points[edge->origin];
b3Vec3 q2 = points[twin->origin];
b3Vec3 e2 = b3Sub( q2, p2 );
b3Vec3 u2 = planes[edge->face].normal;
b3Vec3 v2 = planes[twin->face].normal;
if ( b3IsMinkowskiFaceIsolated( u2, v2, e1 ) )
{
b3Vec3 c1 = b3MulSV( 0.5f, b3Add( q1, p1 ) );
b3Vec3 c2 = hull->center;
float separation = b3EdgeEdgeSeparation( q1, e1, c1, q2, e2, c2 );
if ( separation > maxSeparation )
{
maxSeparation = separation;
maxIndex1 = 0;
maxIndex2 = index;
}
}
}
return (b3EdgeQuery){
.separation = maxSeparation,
.indexA = (uint8_t)maxIndex1,
.indexB = (uint8_t)maxIndex2,
};
}
static b3EdgeQuery b3QueryEdgeDirections( const b3HullData* hullA, const b3HullData* hullB, b3Transform transformBtoA )
{
float maxSeparation = -FLT_MAX;
int maxIndexA = B3_NULL_INDEX;
int maxIndexB = B3_NULL_INDEX;
const b3HullHalfEdge* edgesA = b3GetHullEdges( hullA );
const b3Vec3* pointsA = b3GetHullPoints( hullA );
const b3Plane* planesA = b3GetHullPlanes( hullA );
const b3HullHalfEdge* edgesB = b3GetHullEdges( hullB );
const b3Vec3* pointsB = b3GetHullPoints( hullB );
const b3Plane* planesB = b3GetHullPlanes( hullB );
b3Matrix3 matrix = b3MakeMatrixFromQuat( transformBtoA.q );
for ( int indexB = 0; indexB < hullB->edgeCount; indexB += 2 )
{
const b3HullHalfEdge* edgeB = edgesB + indexB;
const b3HullHalfEdge* twinB = edgesB + indexB + 1;
B3_ASSERT( edgeB->twin == indexB + 1 && twinB->twin == indexB );
b3Vec3 qB = pointsB[twinB->origin];
b3Vec3 eB = b3MulMV( matrix, b3Sub( qB, pointsB[edgeB->origin] ) );
qB = b3Add( b3MulMV( matrix, qB ), transformBtoA.p );
b3Vec3 uB = b3MulMV( matrix, planesB[edgeB->face].normal );
b3Vec3 vB = b3MulMV( matrix, planesB[twinB->face].normal );
for ( int indexA = 0; indexA < hullA->edgeCount; indexA += 2 )
{
const b3HullHalfEdge* edgeA = edgesA + indexA;
const b3HullHalfEdge* twinA = edgesA + indexA + 1;
B3_ASSERT( edgeA->twin == indexA + 1 && twinA->twin == indexA );
b3Vec3 qA = pointsA[twinA->origin];
b3Vec3 eA = b3Sub( qA, pointsA[edgeA->origin] );
b3Vec3 uA = planesA[edgeA->face].normal;
b3Vec3 vA = planesA[twinA->face].normal;
bool isMinkowski;
{
float cba = b3Dot( uB, eA );
float dba = b3Dot( vB, eA );
float adc = -b3Dot( uA, eB );
float bdc = -b3Dot( vA, eB );
isMinkowski = cba * dba < 0.0f && adc * bdc < 0.0f && cba * bdc > 0.0f;
}
if ( isMinkowski )
{
b3Vec3 centerA = hullA->center;
b3Vec3 centerB = b3TransformPoint( transformBtoA, hullB->center );
float separation = b3EdgeEdgeSeparation( qA, eA, centerA, qB, eB, centerB );
if ( separation > maxSeparation )
{
maxSeparation = separation;
maxIndexA = indexA;
maxIndexB = indexB;
}
}
}
}
return (b3EdgeQuery){
.separation = maxSeparation,
.indexA = maxIndexA,
.indexB = maxIndexB,
};
}
static void b3ReduceManifoldPoints( b3LocalManifold* manifold, int capacity, b3LocalManifoldPoint* points, int count )
{
if ( capacity < 4 )
{
return;
}
if ( count <= 4 )
{
for ( int i = 0; i < count; ++i )
{
manifold->points[i] = points[i];
}
manifold->pointCount = count;
return;
}
b3Vec3 normal = manifold->normal;
float speculativeDistance = B3_SPECULATIVE_DISTANCE;
float tolSqr = speculativeDistance * speculativeDistance;
float bias = 0.95f;
int bestIndex = B3_NULL_INDEX;
float bestScore = -FLT_MAX;
b3Vec3 searchDirection = b3ArbitraryPerp( normal );
for ( int index = 0; index < count; ++index )
{
b3LocalManifoldPoint* pt = points + index;
if ( pt->separation > speculativeDistance )
{
continue;
}
float score = -pt->separation + b3Dot( searchDirection, pt->point );
if ( bias * score > bestScore )
{
bestIndex = index;
bestScore = score;
}
}
B3_VALIDATE( 0 <= bestIndex && bestIndex < count );
if ( bestIndex == B3_NULL_INDEX )
{
manifold->pointCount = 0;
return;
}
manifold->points[0] = points[bestIndex];
manifold->pointCount = 1;
points[bestIndex] = points[count - 1];
count -= 1;
b3Vec3 a = manifold->points[0].point;
bestScore = 0.0f;
bestIndex = B3_NULL_INDEX;
float maxDistanceSquared = 0.0f;
for ( int index = 0; index < count; ++index )
{
b3Vec3 p = points[index].point;
b3Vec3 d = b3Sub( p, a );
b3Vec3 v = b3MulSub( d, b3Dot( d, normal ), normal );
float distanceSquared = b3LengthSquared( v );
maxDistanceSquared = b3MaxFloat( maxDistanceSquared, distanceSquared );
float separation = b3MaxFloat( 0.0f, -points[index].separation );
float score = distanceSquared + 4.0f * separation * separation;
if ( bias * score > bestScore )
{
bestScore = score;
bestIndex = index;
}
}
if ( bestScore < tolSqr )
{
return;
}
B3_ASSERT( 0 <= bestIndex && bestIndex < count );
manifold->points[1] = points[bestIndex];
manifold->pointCount = 2;
points[bestIndex] = points[count - 1];
count -= 1;
b3Vec3 b = manifold->points[1].point;
bestScore = tolSqr;
bestIndex = B3_NULL_INDEX;
float bestSignedArea = 0.0f;
b3Vec3 ba = b3Sub( b, a );
for ( int index = 0; index < count; ++index )
{
b3Vec3 p = points[index].point;
float signedArea = b3Dot( normal, b3Cross( ba, b3Sub( p, a ) ) );
float score = b3AbsFloat( signedArea );
if ( bias * score >= bestScore )
{
bestScore = score;
bestIndex = index;
bestSignedArea = signedArea;
}
}
if ( bestIndex == B3_NULL_INDEX )
{
return;
}
B3_ASSERT( bestIndex != B3_NULL_INDEX );
manifold->points[2] = points[bestIndex];
manifold->pointCount = 3;
points[bestIndex] = points[count - 1];
count -= 1;
b3Vec3 c = manifold->points[2].point;
bestScore = tolSqr;
bestIndex = B3_NULL_INDEX;
float sign = bestSignedArea < 0.0f ? -1.0f : 1.0f;
for ( int index = 0; index < count; ++index )
{
b3Vec3 p = points[index].point;
float u1 = sign * b3Dot( normal, b3Cross( b3Sub( p, a ), ba ) );
float u2 = sign * b3Dot( normal, b3Cross( b3Sub( p, b ), b3Sub( c, b ) ) );
float u3 = sign * b3Dot( normal, b3Cross( b3Sub( p, c ), b3Sub( a, c ) ) );
float score = b3MaxFloat( u1, b3MaxFloat( u2, u3 ) );
if ( bias * score > bestScore )
{
bestScore = score;
bestIndex = index;
}
}
if ( bestIndex != B3_NULL_INDEX )
{
manifold->points[manifold->pointCount] = points[bestIndex];
manifold->pointCount += 1;
}
}
void b3CollideSpheres( b3LocalManifold* manifold, int capacity, const b3Sphere* sphereA, const b3Sphere* sphereB,
b3Transform transformBtoA )
{
if ( capacity == 0 )
{
return;
}
b3Vec3 center1 = sphereA->center;
b3Vec3 center2 = b3TransformPoint( transformBtoA, sphereB->center );
float totalRadius = sphereA->radius + sphereB->radius;
b3Vec3 offset = b3Sub( center2, center1 );
float distanceSq = b3LengthSquared( offset );
if ( distanceSq > totalRadius * totalRadius )
{
return;
}
b3Vec3 normal = { 0.0f, 1.0f, 0.0f };
float distance = sqrtf( distanceSq );
if ( distance * distance > 1000.0f * FLT_MIN )
{
normal = b3MulSV( 1.0f / distance, offset );
}
b3Vec3 point =
b3MulSV( 0.5f, b3MulSub( b3Add( b3MulAdd( center1, sphereA->radius, normal ), center2 ), sphereB->radius, normal ) );
manifold->normal = normal;
manifold->pointCount = 1;
b3LocalManifoldPoint* pt = manifold->points + 0;
pt->point = point;
pt->separation = distance - totalRadius;
pt->pair = b3FeaturePair_single;
}
void b3CollideCapsuleAndSphere( b3LocalManifold* manifold, int capacity, const b3Capsule* capsuleA, const b3Sphere* sphereB,
b3Transform transformBtoA )
{
manifold->pointCount = 0;
if ( capacity < 1 )
{
return;
}
b3Vec3 center = b3TransformPoint( transformBtoA, sphereB->center );
b3Vec3 center1 = capsuleA->center1;
b3Vec3 center2 = capsuleA->center2;
float totalRadius = sphereB->radius + capsuleA->radius;
b3Vec3 closestPoint = b3PointToSegmentDistance( center1, center2, center );
b3Vec3 offset = b3Sub( center, closestPoint );
float distanceSq = b3LengthSquared( offset );
if ( distanceSq > totalRadius * totalRadius )
{
return;
}
b3Vec3 normal = { 0.0f, 1.0f, 0.0f };
float distance = sqrtf( distanceSq );
if ( distance * distance > 1000.0f * FLT_MIN )
{
normal = b3MulSV( 1.0f / distance, offset );
}
b3Vec3 point =
b3MulSV( 0.5f, b3MulAdd( b3Add( b3MulSub( center, sphereB->radius, normal ), closestPoint ), capsuleA->radius, normal ) );
manifold->normal = normal;
manifold->pointCount = 1;
b3LocalManifoldPoint* pt = manifold->points + 0;
pt->point = point;
pt->separation = distance - totalRadius;
pt->pair = b3FeaturePair_single;
}
void b3CollideHullAndSphere( b3LocalManifold* manifold, int capacity, const b3HullData* hullA, const b3Sphere* sphereB,
b3Transform transformBtoA, b3SimplexCache* cache )
{
manifold->pointCount = 0;
if ( capacity == 0 )
{
return;
}
b3Vec3 center = b3TransformPoint( transformBtoA, sphereB->center );
const float speculativeDistance = B3_SPECULATIVE_DISTANCE;
b3DistanceInput distanceInput;
distanceInput.proxyA = (b3ShapeProxy){ b3GetHullPoints( hullA ), hullA->vertexCount, 0.0f };
distanceInput.proxyB = (b3ShapeProxy){ ¢er, 1, 0.0f };
distanceInput.transform = b3Transform_identity;
distanceInput.useRadii = false;
float radiusA = 0.0f;
float radiusB = sphereB->radius;
float radius = radiusA + radiusB;
b3DistanceOutput distanceOutput = b3ShapeDistance( &distanceInput, cache, NULL, 0 );
if ( distanceOutput.distance > radius + speculativeDistance )
{
*cache = (b3SimplexCache){ 0 };
return;
}
if ( distanceOutput.distance > 100.0f * FLT_EPSILON )
{
b3Vec3 normal = b3Normalize( b3Sub( distanceOutput.pointB, distanceOutput.pointA ) );
b3Vec3 cA = b3MulAdd( center, radiusA - b3Dot( b3Sub( center, distanceOutput.pointA ), normal ), normal );
b3Vec3 cB = b3MulSub( center, radiusB, normal );
b3Vec3 point = b3Lerp( cA, cB, 0.5f );
manifold->normal = normal;
manifold->pointCount = 1;
b3LocalManifoldPoint* pt = manifold->points + 0;
pt->point = point;
pt->separation = distanceOutput.distance - radius;
pt->pair = b3FeaturePair_single;
}
else
{
int bestIndex = -1;
float bestDistance = -FLT_MAX;
const b3Plane* planes = b3GetHullPlanes( hullA );
for ( int index = 0; index < hullA->faceCount; ++index )
{
b3Plane plane = planes[index];
float distance = b3PlaneSeparation( plane, center );
if ( distance > bestDistance )
{
bestIndex = index;
bestDistance = distance;
}
}
B3_ASSERT( bestIndex >= 0 );
b3Vec3 normal = planes[bestIndex].normal;
b3Vec3 cA = b3MulAdd( center, radiusA - b3Dot( b3Sub( center, distanceOutput.pointA ), normal ), normal );
b3Vec3 cB = b3MulSub( center, radiusB, normal );
b3Vec3 point = b3Lerp( cA, cB, 0.5f );
manifold->normal = normal;
manifold->pointCount = 1;
b3LocalManifoldPoint* pt = manifold->points + 0;
pt->point = point;
pt->separation = bestDistance - radius;
pt->pair = b3FeaturePair_single;
}
}
void b3CollideCapsules( b3LocalManifold* manifold, int capacity, const b3Capsule* capsuleA, const b3Capsule* capsuleB,
b3Transform transformBtoA )
{
manifold->pointCount = 0;
if ( capacity < 2 )
{
return;
}
b3Vec3 centerA1 = capsuleA->center1;
b3Vec3 centerA2 = capsuleA->center2;
b3Vec3 centerB1 = b3TransformPoint( transformBtoA, capsuleB->center1 );
b3Vec3 centerB2 = b3TransformPoint( transformBtoA, capsuleB->center2 );
float radius = capsuleA->radius + capsuleB->radius;
float maxDistance = radius + B3_SPECULATIVE_DISTANCE;
b3SegmentDistanceResult result = b3SegmentDistance( centerA1, centerA2, centerB1, centerB2 );
b3Vec3 offset = b3Sub( result.point2, result.point1 );
float distanceSquared = b3LengthSquared( offset );
float linearSlop = B3_LINEAR_SLOP;
float minDistance = 0.01f * linearSlop;
if ( distanceSquared > maxDistance * maxDistance || distanceSquared < minDistance * minDistance )
{
return;
}
float lengthA;
b3Vec3 segmentA = b3Sub( centerA2, centerA1 );
b3Vec3 edgeA = b3GetLengthAndNormalize( &lengthA, segmentA );
if ( lengthA < B3_MIN_CAPSULE_LENGTH )
{
return;
}
float lengthB;
b3Vec3 segmentB = b3Sub( centerB2, centerB1 );
b3Vec3 edgeB = b3GetLengthAndNormalize( &lengthB, segmentB );
if ( lengthB < B3_MIN_CAPSULE_LENGTH )
{
return;
}
const float alphaTol = 0.05f;
const float alphaTolSqr = alphaTol * alphaTol;
b3Vec3 axis = b3Cross( edgeA, edgeB );
if ( b3LengthSquared( axis ) < alphaTolSqr )
{
b3Plane planesA[2];
planesA[0].normal = b3Neg( edgeA );
planesA[0].offset = -b3Dot( edgeA, capsuleA->center1 );
planesA[1].normal = edgeA;
planesA[1].offset = b3Dot( edgeA, capsuleA->center2 );
b3ClipVertex verticesB[2];
verticesB[0].position = centerB1;
verticesB[0].separation = 0.0f;
verticesB[0].pair = b3MakeFeaturePair( b3_featureShapeA, 0, b3_featureShapeA, 0 );
verticesB[1].position = centerB2;
verticesB[1].separation = 0.0f;
verticesB[1].pair = b3MakeFeaturePair( b3_featureShapeA, 1, b3_featureShapeA, 1 );
int pointCount = b3ClipSegment( verticesB, planesA[0] );
if ( pointCount == 2 )
{
pointCount = b3ClipSegment( verticesB, planesA[1] );
}
if ( pointCount == 2 )
{
b3Vec3 closestPoint1 = b3PointToSegmentDistance( centerA1, centerA2, verticesB[0].position );
b3Vec3 closestPoint2 = b3PointToSegmentDistance( centerA1, centerA2, verticesB[1].position );
float distance1 = b3Distance( closestPoint1, verticesB[0].position );
float distance2 = b3Distance( closestPoint2, verticesB[1].position );
if ( distance1 <= radius && distance2 <= radius )
{
if ( distance1 < minDistance || distance2 < minDistance )
{
return;
}
b3Vec3 normal1 = b3MulSV( 1.0f / distance1, b3Sub( verticesB[0].position, closestPoint1 ) );
b3Vec3 normal2 = b3MulSV( 1.0f / distance2, b3Sub( verticesB[1].position, closestPoint2 ) );
b3Vec3 normal = b3Normalize( b3Add( normal1, normal2 ) );
float radiusA = capsuleA->radius;
float radiusB = capsuleB->radius;
b3Vec3 point1 =
b3MulSV( 0.5f, b3MulSub( b3Add( b3MulAdd( verticesB[0].position, radiusA, normal1 ), closestPoint1 ), radiusB,
normal ) );
b3Vec3 point2 =
b3MulSV( 0.5f, b3MulSub( b3Add( b3MulAdd( verticesB[1].position, radiusA, normal2 ), closestPoint2 ), radiusB,
normal ) );
manifold->normal = normal;
manifold->pointCount = 2;
b3LocalManifoldPoint* pt1 = manifold->points + 0;
pt1->point = point1;
pt1->separation = distance1 - radius;
pt1->pair = verticesB[0].pair;
b3LocalManifoldPoint* pt2 = manifold->points + 1;
pt2->point = point2;
pt2->separation = distance2 - radius;
pt2->pair = verticesB[1].pair;
return;
}
}
}
float distance;
b3Vec3 normal = b3GetLengthAndNormalize( &distance, offset );
b3Vec3 point = b3MulSV(
0.5f, b3MulSub( b3Add( b3MulAdd( result.point1, capsuleA->radius, normal ), result.point2 ), capsuleB->radius, normal ) );
manifold->normal = normal;
manifold->pointCount = 1;
b3LocalManifoldPoint* pt = manifold->points + 0;
pt->point = point;
pt->separation = distance - radius;
pt->pair = b3FeaturePair_single;
}
static bool b3BuildHullFaceAndCapsuleContact( b3LocalManifold* manifold, const b3HullData* hullA, const b3Capsule* capsuleB,
b3Transform transformBtoA, b3FaceQuery query )
{
const b3Plane* planes = b3GetHullPlanes( hullA );
int refFace = query.faceIndex;
b3Plane refPlane = planes[refFace];
b3ClipVertex segmentB[2];
segmentB[0].position = b3TransformPoint( transformBtoA, capsuleB->center1 );
segmentB[0].separation = 0.0f;
segmentB[0].pair = b3MakeFeaturePair( b3_featureShapeA, 0, b3_featureShapeA, 0 );
segmentB[1].position = b3TransformPoint( transformBtoA, capsuleB->center2 );
segmentB[1].separation = 0.0f;
segmentB[1].pair = b3MakeFeaturePair( b3_featureShapeA, 1, b3_featureShapeA, 1 );
int pointCount = b3ClipSegmentToHullFace( segmentB, hullA, refFace );
if ( pointCount < 2 )
{
return false;
}
float distance1 = b3PlaneSeparation( refPlane, segmentB[0].position );
float distance2 = b3PlaneSeparation( refPlane, segmentB[1].position );
const float speculativeDistance = B3_SPECULATIVE_DISTANCE;
if ( distance1 <= speculativeDistance || distance2 <= speculativeDistance )
{
b3Vec3 normal = refPlane.normal;
b3Vec3 point1 = b3MulSub( segmentB[0].position, 0.5f * ( distance1 + capsuleB->radius ), normal );
b3Vec3 point2 = b3MulSub( segmentB[1].position, 0.5f * ( distance2 + capsuleB->radius ), normal );
manifold->normal = normal;
manifold->pointCount = 2;
b3LocalManifoldPoint* pt1 = manifold->points + 0;
pt1->point = point1;
pt1->separation = distance1 - capsuleB->radius;
pt1->pair = segmentB[0].pair;
b3LocalManifoldPoint* pt2 = manifold->points + 1;
pt2->point = point2;
pt2->separation = distance2 - capsuleB->radius;
pt2->pair = segmentB[1].pair;
return true;
}
return false;
}
static inline float b3DeepestPointSeparation( const b3LocalManifold* manifold )
{
float minSeparation = FLT_MAX;
int pointCount = manifold->pointCount;
for ( int i = 0; i < pointCount; ++i )
{
minSeparation = b3MinFloat( minSeparation, manifold->points[i].separation );
}
return minSeparation;
}
static bool b3BuildHullAndCapsuleEdgeContact( b3LocalManifold* manifold, int capacity, const b3HullData* hullA,
const b3Capsule* capsuleB, b3Transform transformBtoA, b3EdgeQuery query )
{
if ( capacity < 1 )
{
return false;
}
b3Vec3 pc = b3TransformPoint( transformBtoA, capsuleB->center1 );
b3Vec3 qc = b3TransformPoint( transformBtoA, capsuleB->center2 );
b3Vec3 ec = b3Sub( qc, pc );
const b3HullHalfEdge* edges = b3GetHullEdges( hullA );
const b3Vec3* points = b3GetHullPoints( hullA );
const b3HullHalfEdge* edge2 = edges + query.indexB;
const b3HullHalfEdge* twin2 = edges + edge2->twin;
b3Vec3 ch = hullA->center;
b3Vec3 ph = points[edge2->origin];
b3Vec3 qh = points[twin2->origin];
b3Vec3 eh = b3Sub( qh, ph );
b3Vec3 normal = b3Cross( ec, eh );
normal = b3Normalize( normal );
if ( b3Dot( normal, b3Sub( ph, ch ) ) < 0.0f )
{
normal = b3Neg( normal );
}
b3SegmentDistanceResult result = b3LineDistance( ph, eh, pc, ec );
if ( b3IsWithinSegments( &result ) == false )
{
return false;
}
b3Vec3 point = b3MulSV( 0.5f, b3Add( b3MulSub( result.point1, capsuleB->radius, normal ), result.point2 ) );
float separation = b3Dot( normal, b3Sub( result.point2, result.point1 ) );
B3_VALIDATE( b3AbsFloat( separation - query.separation ) < B3_LINEAR_SLOP );
manifold->normal = normal;
manifold->pointCount = 1;
b3LocalManifoldPoint* pt = manifold->points + 0;
pt->point = point;
pt->separation = separation - capsuleB->radius;
pt->pair = b3MakeFeaturePair( b3_featureShapeA, query.indexA, b3_featureShapeB, query.indexB );
return true;
}
void b3CollideHullAndCapsule( b3LocalManifold* manifold, int capacity, const b3HullData* hullA, const b3Capsule* capsuleB,
b3Transform transformBtoA, b3SimplexCache* cache )
{
manifold->pointCount = 0;
if ( capacity < 2 )
{
return;
}
b3DistanceInput distanceInput;
distanceInput.proxyA = (b3ShapeProxy){ b3GetHullPoints( hullA ), hullA->vertexCount, 0.0f };
distanceInput.proxyB = (b3ShapeProxy){ &capsuleB->center1, 2, 0.0f };
distanceInput.transform = transformBtoA;
distanceInput.useRadii = false;
b3DistanceOutput distanceOutput = b3ShapeDistance( &distanceInput, cache, NULL, 0 );
const float speculativeDistance = B3_SPECULATIVE_DISTANCE;
if ( distanceOutput.distance > capsuleB->radius + speculativeDistance )
{
*cache = (b3SimplexCache){ 0 };
return;
}
if ( distanceOutput.distance > 100.0f * FLT_EPSILON )
{
const b3Plane* planes = b3GetHullPlanes( hullA );
b3Vec3 delta = distanceOutput.normal;
int refFace = b3FindHullSupportFace( hullA, delta );
b3Plane refPlane = planes[refFace];
const float kTolerance = 0.998f;
if ( b3AbsFloat( b3Dot( refPlane.normal, delta ) ) > kTolerance )
{
b3ClipVertex verticesB[2];
verticesB[0].position = b3TransformPoint( transformBtoA, capsuleB->center1 );
verticesB[0].separation = 0.0f;
verticesB[0].pair = b3MakeFeaturePair( b3_featureShapeA, 0, b3_featureShapeA, 0 );
verticesB[1].position = b3TransformPoint( transformBtoA, capsuleB->center2 );
verticesB[1].separation = 0.0f;
verticesB[1].pair = b3MakeFeaturePair( b3_featureShapeA, 1, b3_featureShapeA, 1 );
int pointCount = b3ClipSegmentToHullFace( verticesB, hullA, refFace );
if ( pointCount == 2 )
{
float distance1 = b3PlaneSeparation( refPlane, verticesB[0].position );
float distance2 = b3PlaneSeparation( refPlane, verticesB[1].position );
if ( distance1 <= capsuleB->radius + speculativeDistance || distance2 <= capsuleB->radius + speculativeDistance )
{
b3Vec3 normal = refPlane.normal;
b3Vec3 point1 = b3MulSub( verticesB[0].position, 0.5f * ( capsuleB->radius + distance1 ), normal );
b3Vec3 point2 = b3MulSub( verticesB[1].position, 0.5f * ( capsuleB->radius + distance2 ), normal );
manifold->normal = normal;
manifold->pointCount = 2;
b3LocalManifoldPoint* pt1 = manifold->points + 0;
pt1->point = point1;
pt1->separation = distance1 - capsuleB->radius;
pt1->pair = verticesB[0].pair;
b3LocalManifoldPoint* pt2 = manifold->points + 1;
pt2->point = point2;
pt2->separation = distance2 - capsuleB->radius;
pt2->pair = verticesB[1].pair;
return;
}
}
}
b3Vec3 point =
b3MulSV( 0.5f, b3Add( b3MulSub( distanceOutput.pointA, capsuleB->radius, delta ), distanceOutput.pointB ) );
manifold->normal = delta;
manifold->pointCount = 1;
b3LocalManifoldPoint* pt = manifold->points + 0;
pt->point = point;
pt->separation = distanceOutput.distance - capsuleB->radius;
pt->pair = b3FeaturePair_single;
return;
}
b3FaceQuery faceQuery = b3QueryFaceDirectionHullAndCapsule( hullA, capsuleB, transformBtoA );
if ( faceQuery.separation > capsuleB->radius )
{
return;
}
b3EdgeQuery edgeQuery = b3QueryEdgeDirectionHullAndCapsule( hullA, capsuleB, transformBtoA );
if ( edgeQuery.separation > capsuleB->radius )
{
return;
}
float faceSeparation = faceQuery.separation - capsuleB->radius;
b3BuildHullFaceAndCapsuleContact( manifold, hullA, capsuleB, transformBtoA, faceQuery );
if ( manifold->pointCount > 1 )
{
faceSeparation = b3DeepestPointSeparation( manifold );
}
B3_VALIDATE( faceSeparation <= 0.0f );
const float kRelEdgeTolerance = 0.90f;
const float kAbsTolerance = 0.5f * B3_LINEAR_SLOP;
float edgeSeparation = edgeQuery.separation - capsuleB->radius;
if ( manifold->pointCount == 0 || edgeSeparation > kRelEdgeTolerance * faceSeparation + kAbsTolerance )
{
b3BuildHullAndCapsuleEdgeContact( manifold, capacity, hullA, capsuleB, transformBtoA, edgeQuery );
}
}
static int b3BuildPolygon( b3ClipVertex* out, b3Transform transform, const b3HullData* hull, int incFace, b3Plane refPlane )
{
const b3HullFace* faces = b3GetHullFaces( hull );
const b3HullHalfEdge* edges = b3GetHullEdges( hull );
const b3Vec3* points = b3GetHullPoints( hull );
const b3HullFace* face = faces + incFace;
int edgeIndex = face->edge;
B3_ASSERT( edges[edgeIndex].face == incFace );
int outCount = 0;
b3Matrix3 matrix = b3MakeMatrixFromQuat( transform.q );
do
{
const b3HullHalfEdge* edge = edges + edgeIndex;
int nextEdgeIndex = edge->next;
const b3HullHalfEdge* next = edges + nextEdgeIndex;
b3ClipVertex vertex;
vertex.position = b3Add( b3MulMV( matrix, points[next->origin] ), transform.p );
vertex.separation = b3PlaneSeparation( refPlane, vertex.position );
vertex.pair = b3MakeFeaturePair( b3_featureShapeB, edgeIndex, b3_featureShapeB, nextEdgeIndex );
out[outCount] = vertex;
outCount += 1;
edgeIndex = nextEdgeIndex;
}
while ( edgeIndex != face->edge && outCount < B3_MAX_CLIP_POINTS );
B3_VALIDATE( b3ValidatePolygon( out, outCount ) );
return outCount;
}
static bool b3BuildFaceAContact( b3LocalManifold* manifold, int capacity, const b3HullData* hullA, const b3HullData* hullB,
b3Transform transformBtoA, b3FaceQuery query, b3SATCache* cache )
{
const b3HullFace* facesA = b3GetHullFaces( hullA );
const b3HullHalfEdge* edgesA = b3GetHullEdges( hullA );
const b3Plane* planesA = b3GetHullPlanes( hullA );
const b3Vec3* pointsA = b3GetHullPoints( hullA );
int refFace = query.faceIndex;
b3Plane refPlane = planesA[refFace];
b3Vec3 refNormalInB = b3InvRotateVector( transformBtoA.q, refPlane.normal );
int incFace = b3FindIncidentFace( hullB, refNormalInB, query.vertexIndex );
b3ClipVertex buffer1[B3_MAX_CLIP_POINTS], buffer2[B3_MAX_CLIP_POINTS];
int pointCount = b3BuildPolygon( buffer1, transformBtoA, hullB, incFace, refPlane );
b3ClipVertex* input = buffer1;
b3ClipVertex* output = buffer2;
const b3HullFace* face = facesA + refFace;
int edgeIndex = face->edge;
do
{
const b3HullHalfEdge* edge = edgesA + edgeIndex;
int nextEdgeIndex = edge->next;
const b3HullHalfEdge* next = edgesA + nextEdgeIndex;
b3Vec3 vertex1 = pointsA[edge->origin];
b3Vec3 vertex2 = pointsA[next->origin];
b3Vec3 tangent = b3Normalize( b3Sub( vertex2, vertex1 ) );
b3Vec3 binormal = b3Cross( tangent, refPlane.normal );
b3Plane clipPlane = b3MakePlaneFromNormalAndPoint( binormal, vertex1 );
pointCount = b3ClipPolygon( output, input, pointCount, clipPlane, edgeIndex, refPlane );
B3_ASSERT( pointCount <= B3_MAX_CLIP_POINTS );
B3_SWAP( output, input );
if ( pointCount < 3 )
{
*cache = (b3SATCache){ 0 };
return false;
}
edgeIndex = nextEdgeIndex;
}
while ( edgeIndex != face->edge );
pointCount = b3MinInt( pointCount, B3_MAX_CLIP_POINTS );
b3LocalManifoldPoint points[B3_MAX_CLIP_POINTS];
float minSeparation = FLT_MAX;
manifold->normal = refPlane.normal;
for ( int i = 0; i < pointCount; ++i )
{
b3ClipVertex* clipPoint = input + i;
b3LocalManifoldPoint* pt = points + i;
*pt = (b3LocalManifoldPoint){ 0 };
b3Vec3 point = b3MulSub( clipPoint->position, 0.5f * clipPoint->separation, refPlane.normal );
pt->point = point;
pt->separation = clipPoint->separation;
pt->pair = clipPoint->pair;
minSeparation = b3MinFloat( minSeparation, clipPoint->separation );
}
if ( minSeparation >= B3_SPECULATIVE_DISTANCE )
{
*cache = (b3SATCache){ 0 };
return false;
}
b3ReduceManifoldPoints( manifold, capacity, points, pointCount );
cache->separation = minSeparation;
cache->type = (uint8_t)b3_faceAxisA;
cache->indexA = (uint8_t)query.faceIndex;
cache->indexB = (uint8_t)query.vertexIndex;
return true;
}
static bool b3BuildFaceBContact( b3LocalManifold* manifold, int capacity, const b3HullData* hullA, const b3HullData* hullB,
b3Transform transformBtoA, b3FaceQuery query, b3SATCache* cache )
{
b3Transform transformAtoB = b3InvertTransform( transformBtoA );
bool touching = b3BuildFaceAContact( manifold, capacity, hullB, hullA, transformAtoB, query, cache );
if ( touching == false )
{
return false;
}
b3Matrix3 matrix = b3MakeMatrixFromQuat( transformBtoA.q );
manifold->normal = b3Neg( b3MulMV( matrix, manifold->normal ) );
cache->type = (uint8_t)b3_faceAxisB;
cache->indexA = (uint8_t)query.vertexIndex;
cache->indexB = (uint8_t)query.faceIndex;
for ( int i = 0; i < manifold->pointCount; ++i )
{
b3LocalManifoldPoint* pt = manifold->points + i;
pt->point = b3Add( b3MulMV( matrix, pt->point ), transformBtoA.p );
pt->pair = b3FlipPair( pt->pair );
}
return true;
}
static bool b3BuildEdgeContact( b3LocalManifold* manifold, const b3HullData* hullA, const b3HullData* hullB, b3Transform transformBtoA,
b3EdgeQuery query, b3SATCache* cache )
{
const b3HullHalfEdge* edgesA = b3GetHullEdges( hullA );
const b3Vec3* pointsA = b3GetHullPoints( hullA );
const b3HullHalfEdge* edgesB = b3GetHullEdges( hullB );
const b3Vec3* pointsB = b3GetHullPoints( hullB );
const b3HullHalfEdge* edgeA = edgesA + query.indexA;
const b3HullHalfEdge* twinA = edgesA + edgeA->twin;
b3Vec3 centerA = hullA->center;
b3Vec3 pA = pointsA[edgeA->origin];
b3Vec3 qA = pointsA[twinA->origin];
b3Vec3 eA = b3Sub( qA, pA );
const b3HullHalfEdge* edgeB = edgesB + query.indexB;
const b3HullHalfEdge* twinB = edgesB + edgeB->twin;
b3Vec3 pB = b3TransformPoint( transformBtoA, pointsB[edgeB->origin] );
b3Vec3 qB = b3TransformPoint( transformBtoA, pointsB[twinB->origin] );
b3Vec3 eB = b3Sub( qB, pB );
b3Vec3 normal = b3Cross( eA, eB );
normal = b3Normalize( normal );
if ( b3Dot( normal, b3Sub( pA, centerA ) ) < 0.0f )
{
normal = b3Neg( normal );
}
b3SegmentDistanceResult result = b3LineDistance( pA, eA, pB, eB );
if ( b3IsWithinSegments( &result ) == false )
{
*cache = (b3SATCache){ 0 };
return false;
}
float separation = b3Dot( normal, b3Sub( result.point2, result.point1 ) );
b3Vec3 point = b3MulSV( 0.5f, b3Add( result.point1, result.point2 ) );
manifold->normal = normal;
manifold->pointCount = 1;
b3LocalManifoldPoint* pt = manifold->points + 0;
pt->point = point;
pt->separation = separation;
pt->pair = b3MakeFeaturePair( b3_featureShapeA, query.indexA, b3_featureShapeB, query.indexB );
cache->separation = separation;
cache->type = (uint8_t)b3_edgePairAxis;
cache->indexA = (uint8_t)query.indexA;
cache->indexB = (uint8_t)query.indexB;
return true;
}
void b3CollideHulls( b3LocalManifold* manifold, int capacity, const b3HullData* hullA, const b3HullData* hullB, b3Transform transformBtoA,
b3SATCache* cache )
{
manifold->pointCount = 0;
if ( capacity < 4 )
{
return;
}
float speculativeDistance = B3_SPECULATIVE_DISTANCE;
float linearSlop = B3_LINEAR_SLOP;
const b3HullHalfEdge* edgesA = b3GetHullEdges( hullA );
const b3Plane* planesA = b3GetHullPlanes( hullA );
const b3Vec3* pointsA = b3GetHullPoints( hullA );
const b3HullHalfEdge* edgesB = b3GetHullEdges( hullB );
const b3Plane* planesB = b3GetHullPlanes( hullB );
const b3Vec3* pointsB = b3GetHullPoints( hullB );
switch ( cache->type )
{
case b3_invalidAxis:
*cache = (b3SATCache){ 0 };
break;
case b3_faceAxisA:
{
B3_ASSERT( cache->indexA < hullA->faceCount );
b3Plane plane = planesA[cache->indexA];
b3Vec3 searchDirectionInB = b3Neg( b3InvRotateVector( transformBtoA.q, plane.normal ) );
int vertexIndex = b3FindHullSupportVertex( hullB, searchDirectionInB );
b3Vec3 support = b3TransformPoint( transformBtoA, pointsB[vertexIndex] );
float separation = b3PlaneSeparation( plane, support );
if ( separation >= speculativeDistance )
{
return;
}
{
b3FaceQuery faceQuery;
faceQuery.separation = 0.0f;
faceQuery.faceIndex = cache->indexA;
faceQuery.vertexIndex = vertexIndex;
b3SATCache localCache = { 0 };
bool touching = b3BuildFaceAContact( manifold, capacity, hullA, hullB, transformBtoA, faceQuery, &localCache );
if ( touching == true && b3AbsFloat( cache->separation - localCache.separation ) < linearSlop )
{
return;
}
}
}
break;
case b3_faceAxisB:
{
B3_ASSERT( cache->indexB < hullB->faceCount );
b3Plane plane = planesB[cache->indexB];
b3Vec3 searchDirectionInA = b3Neg( b3RotateVector( transformBtoA.q, plane.normal ) );
int vertexIndex = b3FindHullSupportVertex( hullA, searchDirectionInA );
b3Vec3 support = b3InvTransformPoint( transformBtoA, pointsA[vertexIndex] );
float separation = b3PlaneSeparation( plane, support );
if ( separation >= speculativeDistance )
{
return;
}
{
b3FaceQuery faceQuery;
faceQuery.separation = 0.0f;
faceQuery.faceIndex = cache->indexB;
faceQuery.vertexIndex = vertexIndex;
b3SATCache localCache = { 0 };
bool touching = b3BuildFaceBContact( manifold, capacity, hullA, hullB, transformBtoA, faceQuery, &localCache );
if ( touching == true && b3AbsFloat( cache->separation - localCache.separation ) < linearSlop )
{
return;
}
}
}
break;
case b3_edgePairAxis:
{
int index1 = cache->indexA;
const b3HullHalfEdge* edge1 = edgesA + index1;
const b3HullHalfEdge* twin1 = edgesA + index1 + 1;
B3_ASSERT( edge1->twin == index1 + 1 && twin1->twin == index1 );
b3Vec3 p1 = pointsA[edge1->origin];
b3Vec3 q1 = pointsA[twin1->origin];
b3Vec3 e1 = b3Sub( q1, p1 );
b3Vec3 u1 = planesA[edge1->face].normal;
b3Vec3 v1 = planesA[twin1->face].normal;
int index2 = cache->indexB;
const b3HullHalfEdge* edge2 = edgesB + index2;
const b3HullHalfEdge* twin2 = edgesB + index2 + 1;
B3_ASSERT( edge2->twin == index2 + 1 && twin2->twin == index2 );
b3Vec3 p2 = b3TransformPoint( transformBtoA, pointsB[edge2->origin] );
b3Vec3 q2 = b3TransformPoint( transformBtoA, pointsB[twin2->origin] );
b3Vec3 e2 = b3Sub( q2, p2 );
b3Vec3 u2 = b3RotateVector( transformBtoA.q, planesB[edge2->face].normal );
b3Vec3 v2 = b3RotateVector( transformBtoA.q, planesB[twin2->face].normal );
bool isMinkowski = b3IsMinkowskiFace( u1, v1, e1, b3Neg( u2 ), b3Neg( v2 ), e2 );
if ( isMinkowski == true )
{
b3Vec3 c1 = hullA->center;
b3Vec3 c2 = b3TransformPoint( transformBtoA, hullB->center );
float separation = b3EdgeEdgeSeparation( p1, e1, c1, p2, e2, c2 );
if ( separation > speculativeDistance )
{
return;
}
{
b3EdgeQuery edgeQuery;
edgeQuery.indexA = cache->indexA;
edgeQuery.indexB = cache->indexB;
edgeQuery.separation = 0.0f;
b3SATCache localCache = { 0 };
bool touching = b3BuildEdgeContact( manifold, hullA, hullB, transformBtoA, edgeQuery, &localCache );
if ( touching && b3AbsFloat( cache->separation - localCache.separation ) < linearSlop )
{
return;
}
}
}
}
break;
case b3_manualFaceAxisA:
{
b3FaceQuery faceQueryA = b3QueryFaceDirections( hullA, hullB, transformBtoA );
b3BuildFaceAContact( manifold, capacity, hullA, hullB, transformBtoA, faceQueryA, cache );
return;
}
case b3_manualFaceAxisB:
{
b3FaceQuery faceQueryB = b3QueryFaceDirections( hullB, hullA, b3InvertTransform( transformBtoA ) );
b3BuildFaceBContact( manifold, capacity, hullA, hullB, transformBtoA, faceQueryB, cache );
return;
}
case b3_manualEdgePairAxis:
{
b3EdgeQuery edgeQuery = b3QueryEdgeDirections( hullA, hullB, transformBtoA );
if ( edgeQuery.indexA != B3_NULL_INDEX )
{
b3BuildEdgeContact( manifold, hullA, hullB, transformBtoA, edgeQuery, cache );
}
return;
}
default:
B3_ASSERT( false );
break;
}
manifold->pointCount = 0;
*cache = (b3SATCache){ 0 };
b3FaceQuery faceQueryA = b3QueryFaceDirections( hullA, hullB, transformBtoA );
if ( faceQueryA.separation > speculativeDistance )
{
B3_ASSERT( faceQueryA.faceIndex < hullA->faceCount );
B3_ASSERT( faceQueryA.vertexIndex < hullB->vertexCount );
cache->separation = faceQueryA.separation;
cache->type = (uint8_t)b3_faceAxisA;
cache->indexA = (uint8_t)faceQueryA.faceIndex;
cache->indexB = (uint8_t)faceQueryA.vertexIndex;
return;
}
b3FaceQuery faceQueryB = b3QueryFaceDirections( hullB, hullA, b3InvertTransform( transformBtoA ) );
if ( faceQueryB.separation > speculativeDistance )
{
B3_ASSERT( faceQueryB.faceIndex < hullB->faceCount );
B3_ASSERT( faceQueryB.vertexIndex < hullA->vertexCount );
cache->separation = faceQueryB.separation;
cache->type = (uint8_t)b3_faceAxisB;
cache->indexA = (uint8_t)faceQueryB.vertexIndex;
cache->indexB = (uint8_t)faceQueryB.faceIndex;
return;
}
b3EdgeQuery edgeQuery = b3QueryEdgeDirections( hullA, hullB, transformBtoA );
if ( edgeQuery.separation > speculativeDistance )
{
cache->separation = edgeQuery.separation;
cache->type = (uint8_t)b3_edgePairAxis;
cache->indexA = (uint8_t)edgeQuery.indexA;
cache->indexB = (uint8_t)edgeQuery.indexB;
return;
}
float faceSeparationA = faceQueryA.separation;
float faceSeparationB = faceQueryB.separation;
B3_VALIDATE( faceSeparationA <= speculativeDistance && faceSeparationB <= speculativeDistance );
if ( faceSeparationB > faceSeparationA + 0.5f * linearSlop )
{
b3BuildFaceBContact( manifold, capacity, hullA, hullB, transformBtoA, faceQueryB, cache );
}
else
{
b3BuildFaceAContact( manifold, capacity, hullA, hullB, transformBtoA, faceQueryA, cache );
}
if ( edgeQuery.indexA == B3_NULL_INDEX )
{
return;
}
float clippedFaceSeparation = cache->separation;
B3_VALIDATE( edgeQuery.separation <= speculativeDistance );
const float kRelEdgeTolerance = 0.90f;
const float kAbsTolerance = 0.5f * linearSlop;
if ( manifold->pointCount == 0 || edgeQuery.separation > kRelEdgeTolerance * clippedFaceSeparation + kAbsTolerance )
{
b3LocalManifold edgeManifold = { 0 };
b3LocalManifoldPoint edgePoint = { 0 };
edgeManifold.points = &edgePoint;
b3BuildEdgeContact( &edgeManifold, hullA, hullB, transformBtoA, edgeQuery, cache );
if ( edgeManifold.pointCount == 1 )
{
b3LocalManifoldPoint* points = manifold->points;
*manifold = edgeManifold;
manifold->points = points;
manifold->points[0] = edgePoint;
}
}
}