user1609509
user1609509

Reputation: 21

Slow-Moving 3D Sphere to 3D Triangle Collision

So, I wrote this function to check collisions between a triangle and sphere class in 3D space. These objects collide 1 in a million times when they are supposed to. I was wondering if someone could double check my math? I'm coding in C++. My triangle class has a union where you can access the vertices using A, B, and C, or P0, P1, and P2. The method used is to create a plane from a triangle and project the sphere's center onto the plane. Once this is done, the projected point is checked against the three planar edges of the triangle. I've been looking at this forever! Any and all help is extremely appreciated.

// create the edges
float3 TriEdge0 = tVerts.m_P1 - tVerts.m_P0;// 0 -> 1
float3 TriEdge1 = tVerts.m_P2 - tVerts.m_P1;// 1 -> 2
float3 TriEdge2 = tVerts.m_P0 - tVerts.m_P2;// 2 -> 0
float3 TriPlaneNormal = TriEdge1.Cross(TriEdge2);

// normalize
TriPlaneNormal /= TriPlaneNormal.Magnitude();

float fTriPlaneOffset = tVerts.m_A.Dot(TriPlaneNormal);

// get vector from point on plane to sphere center
float3 TriToSphereCenter = pSphere->GetCenter() - tVerts.m_A;

// distance between the sphere and the triangle plane
float fDistanceToPlane = TriToSphereCenter.Dot(TriPlaneNormal);

// project the sphere's center onto the triangle plane
float3 ProjectionVector = TriPlaneNormal * -fDistanceToPlane;
float3 ProjectedSphereOnPlane = pSphere->GetCenter() + ProjectionVector;


// use the point in triangle test to see if the projected point is in the triangle
    // if it is, then it is the closest point between the shapes

float3 TriEdge0Normal = TriEdge0.Cross(TriPlaneNormal);
float fTriPlane0Offset = tVerts.m_P0.Dot(TriEdge0Normal);

float3 TriEdge1Normal = TriEdge1.Cross(TriPlaneNormal);
float fTriPlane1Offset = tVerts.m_P1.Dot(TriEdge1Normal);

float3 TriEdge2Normal = TriEdge2.Cross(TriPlaneNormal);
float fTriPlane2Offset = tVerts.m_P2.Dot(TriEdge2Normal);

float3 ClosestPointToSphere;
float PositionOfProjectedPoint0 = TriEdge0Normal.Dot(ProjectedSphereOnPlane) - fTriPlane0Offset;
float PositionOfProjectedPoint1 = TriEdge1Normal.Dot(ProjectedSphereOnPlane) - fTriPlane1Offset;
float PositionOfProjectedPoint2 = TriEdge2Normal.Dot(ProjectedSphereOnPlane) - fTriPlane2Offset;

// if the point is in the triangle, it is the closest point
if (PositionOfProjectedPoint0 < 0.0f && PositionOfProjectedPoint1 < 0.0f 
    && PositionOfProjectedPoint2 < 0.0f )
{
    ClosestPointToSphere = ProjectedSphereOnPlane;
}
else    // find the closest edge
{
    // check each edge to see if it is in front of the plane 
        // if so, it is the closest edge
    float3 StartPoint;
    float3 EndPoint;
    float3 LineNormal;
    float3 LineSegment;

    if(PositionOfProjectedPoint0 > 0.0f)        // edge 0
    {
        StartPoint = tVerts.m_P0;
        EndPoint = tVerts.m_P1;
        LineNormal = TriEdge0Normal;
        LineSegment = TriEdge0;
    }
    else if (PositionOfProjectedPoint1 > 0.0f)  // edge 1
    {
        StartPoint = tVerts.m_P1;
        EndPoint = tVerts.m_P2;
        LineNormal = TriEdge1Normal;
        LineSegment = TriEdge1;
    }
    else if (PositionOfProjectedPoint2 > 0.0f)  // edge 2
    {
        StartPoint = tVerts.m_P2;
        EndPoint = tVerts.m_P0;
        LineNormal = TriEdge2Normal;
        LineSegment = TriEdge2;
    }

    // calculate a vector between the test point and the start point
        // and scale it by the distance between them
    float3 LineToTestPoint = ClosestPointToSphere - StartPoint;
    float fDistance = LineNormal.Dot(LineToTestPoint);

    // limit the closest point on the line to the given segment
    if (fDistance < 0.0f)
    {
        ClosestPointToSphere = StartPoint;
    }
    else if (fDistance > LineSegment.Magnitude())
    {
        ClosestPointToSphere = EndPoint;
    }
    else    // closest point is within line segment
    {
        float3 ScaledNormal = LineToTestPoint * fDistance;
        ClosestPointToSphere = StartPoint + ScaledNormal;
    }
}

// distance test between the closest point and the sphere's radius
float fDistance = (pSphere->GetCenter() - ClosestPointToSphere).Dot(
    pSphere->GetCenter() - ClosestPointToSphere);

// if squared radius is less than the distance between the projected point and the sphere, 
    // there is collision
if (fDistance < pSphere->GetRadius() * pSphere->GetRadius())
{
    return true;
}

// triangle is not colliding
return false;

Upvotes: 2

Views: 1574

Answers (1)

axon
axon

Reputation: 1200

Take a look at XNACollision.cpp in the Collision Sample of the DirectX SDK. The source-code for this collision test (and many others) is provided. Also, I just noticed that DirectXCollisions.h has recently replaced XNAMath.h etc. See here http://msdn.microsoft.com/en-us/library/windows/desktop/ee418730(v=vs.85).aspx

Upvotes: 0

Related Questions