rayTriangle function

double rayTriangle(
  1. Ray ray,
  2. Vector3 a,
  3. Vector3 b,
  4. Vector3 c, {
  5. Vector2? outUv,
  6. bool cullBackFace = false,
})

Möller–Trumbore ray/triangle intersection.

Returns the distance along ray, or kNoHit. When it hits, outUv receives the barycentric coordinates (u, v) of the second and third vertices, which is what interpolating a normal or a texture coordinate at the hit needs.

The classic formulation: no plane equation, no separate inside test, and no precomputed per-triangle data — which matters because the alternative would mean building and invalidating an acceleration structure per mesh.

Implementation

double rayTriangle(
  Ray ray,
  Vector3 a,
  Vector3 b,
  Vector3 c, {
  Vector2? outUv,
  bool cullBackFace = false,
}) {
  final e1x = b.x - a.x, e1y = b.y - a.y, e1z = b.z - a.z;
  final e2x = c.x - a.x, e2y = c.y - a.y, e2z = c.z - a.z;

  final d = ray.direction;
  // p = direction x edge2
  final px = d.y * e2z - d.z * e2y;
  final py = d.z * e2x - d.x * e2z;
  final pz = d.x * e2y - d.y * e2x;

  final determinant = e1x * px + e1y * py + e1z * pz;

  // A determinant at zero means the ray is parallel to the triangle's plane, or
  // the triangle is degenerate. Both are misses, and both would divide by zero.
  if (cullBackFace) {
    if (determinant < 1e-12) return kNoHit;
  } else if (determinant.abs() < 1e-12) {
    return kNoHit;
  }

  final inverse = 1.0 / determinant;

  final tx = ray.origin.x - a.x;
  final ty = ray.origin.y - a.y;
  final tz = ray.origin.z - a.z;

  final u = (tx * px + ty * py + tz * pz) * inverse;
  if (u < 0.0 || u > 1.0) return kNoHit;

  // q = t x edge1
  final qx = ty * e1z - tz * e1y;
  final qy = tz * e1x - tx * e1z;
  final qz = tx * e1y - ty * e1x;

  final v = (d.x * qx + d.y * qy + d.z * qz) * inverse;
  if (v < 0.0 || u + v > 1.0) return kNoHit;

  final t = (e2x * qx + e2y * qy + e2z * qz) * inverse;
  if (t < 0.0) return kNoHit;

  outUv?.setValues(u, v);
  return t;
}