// adaptation of
// https://discussions.unity.com/t/475294
//
// Moller/Trumbore intersection algorithm
static private bool rayTriangleIntersection_impl(
in Vector3 rayOrigin, in Vector3 rayDirection,
in Vector3 point, in Vector3 edge1, in Vector3 edge2,
out float distance, out Vector2 baryCoord,
bool backfaceCulling = true) {
baryCoord = VectorEx.Vec2NaN;
distance = Mathf.Infinity;
var p = rayDirection.CrossWith(edge2);
var det = edge1.DotWith(p);
if(backfaceCulling) {
if(det.IsZeroOrNegative()) return false;
var t = point.ToPoint(rayOrigin);
var u = t.DotWith(p);
if(!u.IsWithin(0f, det)) return false; // if(u < 0f || u > det)
var q = t.CrossWith(edge1);
var v = rayDirection.DotWith(q);
if(!v.IsWithin(0f, det - u)) return false; // if(v < 0f || u + v > det)
var invDet = 1f / det;
distance = edge2.DotWith(q) * invDet;
baryCoord.x = u * invDet;
baryCoord.y = v * invDet;
} else {
if(det.IsCloseToZero()) return false;
var invDet = 1f / det;
var t = point.ToPoint(rayOrigin);
var u = t.DotWith(p) * invDet;
if(!u.IsWithin01()) return false; // if(u < 0f || u > 1f)
var q = t.CrossWith(edge1);
var v = rayDirection.DotWith(q) * invDet;
if(!v.IsWithin(0f, 1f - u)) return false; // if(v < 0f || u + v > 1f)
distance = edge2.DotWith(q) * invDet;
baryCoord.x = u;
baryCoord.y = v;
}
return true;
}
static public bool RayTriangleIntersection(
in Vector3 rayOrigin, in Vector3 rayDirection,
in Vector3 point1, in Vector3 point2, in Vector3 point3,
out float distance, out Vector2 baryCoord, bool backfaceCulling = true) {
var edge1 = point1.ToPoint(point2);
var edge2 = point1.ToPoint(point3);
return rayTriangleIntersection_impl(
rayOrigin, rayDirection,
point1, edge1, edge2,
out distance, out baryCoord,
backfaceCulling
);
}
static public bool RayTriangleIntersection(
Ray ray,
in (Vector3 a, Vector3 b, Vector3 c) triangle,
out float distance, out Vector3 hitPoint,
bool backfaceCulling = true) {
var edge1 = triangle.a.ToPoint(triangle.b);
var edge2 = triangle.a.ToPoint(triangle.c);
bool hit = rayTriangleIntersection_impl(
ray.origin, ray.direction,
triangle.a, edge1, edge2,
out distance, out var bary,
backfaceCulling
);
hitPoint = triangle.a + bary.x * edge1 + bary.y * edge2;
return hit;
}
In case you need it.
Vec2NaN
means new Vector2(float.NaN, float.NaN)
a.ToPoint(b)
means (b - a)
a.IsCloseToZero()
means Mathf.Abs(a) < 1E-6f
a.IsZeroOrNegative()
means a < 0f || a.IsCloseToZero()
the rest is obvious, I hope.