diff --git a/include/omath/collision/LineTracer.hpp b/include/omath/collision/LineTracer.hpp index aef9a99..eb1f8fc 100644 --- a/include/omath/collision/LineTracer.hpp +++ b/include/omath/collision/LineTracer.hpp @@ -2,14 +2,25 @@ // Created by Orange on 11/13/2024. // #pragma once +#include + #include "omath/Vector3.hpp" #include "omath/Triangle3d.hpp" + + namespace omath::collision { - struct Ray + class Ray { + public: Vector3 start; Vector3 end; + + [[nodiscard]] + Vector3 DirectionVector() const; + + [[nodiscard]] + Vector3 DirectionVectorNormalized() const; }; class LineTracer { @@ -18,5 +29,11 @@ namespace omath::collision [[nodiscard]] static bool CanTraceLine(const Ray& ray, const Triangle3d& triangle); + + + // Realization of Möller–Trumbore intersection algorithm + // https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm + [[nodiscard]] + static std::optional GetRayHitPoint(const Ray& ray, const Triangle3d& triangle); }; } diff --git a/source/collision/LineTracer.cpp b/source/collision/LineTracer.cpp index 7daefdc..602b92f 100644 --- a/source/collision/LineTracer.cpp +++ b/source/collision/LineTracer.cpp @@ -8,33 +8,56 @@ namespace omath::collision { bool LineTracer::CanTraceLine(const Ray &ray, const Triangle3d &triangle) { + return GetRayHitPoint(ray, triangle) == ray.end; + } + Vector3 Ray::DirectionVector() const + { + return end - start; + } + + Vector3 Ray::DirectionVectorNormalized() const + { + return DirectionVector().Normalized(); + } + + std::optional LineTracer::GetRayHitPoint(const Ray &ray, const Triangle3d &triangle) + { + constexpr float kEpsilon = std::numeric_limits::epsilon(); + const auto sideA = triangle.SideAVector(); const auto sideB = triangle.SideBVector(); - const auto rayDir = ray.end - ray.start; + + const auto rayDir = ray.DirectionVector(); const auto p = rayDir.Cross(sideB); - const auto det = sideA.Dot(p); - if (std::abs(det) < 1e-6) - return true; - const auto invDet = 1 / det; + if (std::abs(det) < kEpsilon) + return ray.end; + const auto invDet = 1.0f / det; const auto t = ray.start - triangle.m_vertex2; - const auto u = t.Dot(p) * invDet; - if (u < 0.f || u > 1.f) - return true; + + if ((u < 0 && std::abs(u) > kEpsilon) || (u > 1 && std::abs(u-1) > kEpsilon)) + return ray.end; const auto q = t.Cross(sideA); const auto v = rayDir.Dot(q) * invDet; - if (v < 0.f || u + v > 1.f) - return true; - return sideB.Dot(q) * invDet <= 0.f; + if ((v < 0 && std::abs(v) > kEpsilon) || (u + v > 1 && std::abs(u + v - 1) > kEpsilon)) + return ray.end; + + const auto tHit = sideB.Dot(q) * invDet; + + + if (tHit <= kEpsilon) + return ray.end; + + return ray.start + rayDir * tHit; } } diff --git a/tests/UnitTestLineTrace.cpp b/tests/UnitTestLineTrace.cpp index 722a150..67884d9 100644 --- a/tests/UnitTestLineTrace.cpp +++ b/tests/UnitTestLineTrace.cpp @@ -51,17 +51,30 @@ TEST_F(LineTracerTest, RayInPlaneNotIntersecting) EXPECT_TRUE(LineTracer::CanTraceLine(ray, triangle)); } -// Test edge case where the ray exactly intersects one of the triangle's vertices, expecting false + TEST_F(LineTracerTest, RayIntersectsVertex) { const Ray ray{{-1.0f, -1.0f, -1.0f}, vertex1}; // Intersecting at vertex1 - EXPECT_FALSE(LineTracer::CanTraceLine(ray, triangle)); + EXPECT_TRUE(LineTracer::CanTraceLine(ray, triangle)); } -// Test edge case where the ray exactly intersects one of the triangle's edges, expecting false TEST_F(LineTracerTest, RayIntersectsEdge) { constexpr Ray ray{{-1.0f, 0.0f, -1.0f}, {0.5f, 0.0f, 0.0f}}; // Intersecting on the edge between vertex1 and vertex2 - EXPECT_FALSE(LineTracer::CanTraceLine(ray, triangle)); + EXPECT_TRUE(LineTracer::CanTraceLine(ray, triangle)); +} + +TEST_F(LineTracerTest, TriangleFarBeyondRayEndPoint) +{ + // Define a ray with a short length + constexpr Ray ray{{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f}}; + + // Define a triangle far beyond the ray's endpoint + const Triangle3d distantTriangle{ + {1000.0f, 1000.0f, 1000.0f}, {1001.0f, 1000.0f, 1000.0f}, {1000.0f, 1001.0f, 1000.0f} + }; + + // Expect true because the ray ends long before it could reach the distant triangle + EXPECT_TRUE(LineTracer::CanTraceLine(ray, distantTriangle)); }