FmVec3 GetClosestPointTriangle(FmVec3 p, FmVec3 a, FmVec3 b, FmVec3 c)
{
FmVec3 result;
FmVec3 ab, ac, ap;
ab = b - a;
ac = c - a;
ap = p - a;
float d1 = ab.dot(ap);
float d2 = ac.dot(ap);
if (d1 <= 0.0f && d2 <= 0.0f)
{
result = a;
return result;
}
FmVec3 bp;
bp = p - b;
float d3 = ab.dot(bp);
float d4 = ac.dot(bp);
if (d3 >= 0.0f && d4 <= d3)
{
// barycentric coordinates (0,1,0)
result = b;
return result;
}
// Check if P in edge region of AB, if so return projection of P onto AB
float vc = d1 * d4 - d3 * d2;
if (vc <= 0.0f && d1 >= 0.0f && d3 <= 0.0f)
{
// barycentric coordinates (1-v,v,0)
float v = d1 / (d1 - d3);
result.x = a.x + v * ab.x;
result.y = a.y + v * ab.y;
result.z = a.z + v * ab.z;
return result;
}
FmVec3 cp = p - c;
float d5 = ab.dot(cp);
float d6 = ac.dot(cp);
if (d6 >= 0.0f && d5 <= d6)
{
// barycentric coordinates (0,0,1)
result = c;
return result;
}
// Check if P in edge region of AC, if so return projection of P onto AC
float vb = d5 * d2 - d1 * d6;
if (vb <= 0.0f && d2 >= 0.0f && d6 <= 0.0f)
{
// barycentric coordinates (1-w,0,w)
float w = d2 / (d2 - d6);
result.x = a.x + w * ac.x;
result.y = a.y + w * ac.y;
result.z = a.z + w * ac.z;
return result;
}
// Check if P in edge region of BC, if so return projection of P onto BC
float va = d3 * d6 - d5 * d4;
if (va <= 0.0f && (d4 - d3) >= 0.0f && (d5 - d6) >= 0.0f)
{
// barycentric coordinates (0,1-w,w)
float w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
result.x = b.x + w * (c.x - b.x);
result.y = b.y + w * (c.y - b.y);
result.z = b.z + w * (c.z - b.z);
return result;
}
// P inside face region. Compute Q through its barycentric coordinates (u,v,w)
float denom = 1.0f / (va + vb + vc);
float v = vb * denom;
float w = vc * denom;
result.x = a.x + ab.x * v + ac.x * w;
result.y = a.y + ab.y * v + ac.y * w;
result.z = a.z + ab.z * v + ac.z * w;
return result;
}