The first part finds the intersection point:
private float? GetIntersectionY(Vector3 a, Vector3 b, Vector3 c, Vector3 pp) { //First, define a ray that will definitely intersect if the triangle lies beneath the point. var p = new Vector3(pp.X, 100, pp.Z); var q = new Vector3(p.X, p.Y - 100, p.Z); //Then, find the intersection. var normal = Vector3.CrossProduct(b - a, c - a).Normalized(); float dist1 = Vector3.DotProduct(p - a, normal); float dist2 = Vector3.DotProduct(q - a, normal); if (dist1 == dist2 || dist1 * dist2 >= 0) //Parallel; no intersection. return null; var intersection = p + (q - p) * (-dist1 / (dist2 - dist1)); //Verify that this point lies inside the triangle. if (SameSide(p, a, b, c) && SameSide(p, b, a, c) && SameSide(p, c, a, b)) return intersection.Y; else return null; }
The second part, as the above function already indicates, determines that the point really lies within the triangle, and not just within the mathematical plane. The linked sited explains well how this works.
private static bool SameSide(Vector3 p1, Vector3 p2, Vector3 a, Vector3 b) { //It only worked right for me when all the points had the same height though. var _p1 = new Vector3(p1.X, 0, p1.Z); var _p2 = new Vector3(p2.X, 0, p2.Z); var _a = new Vector3(a.X, 0, a.Z); var _b = new Vector3(b.X, 0, b.Z); //The check. var cp1 = Vector3.CrossProduct(_b - _a, _p1 - _a); var cp2 = Vector3.CrossProduct(_b - _a, _p2 - _a); return Vector3.DotProduct(cp1, cp2) >= 0; }