diff --git a/include/omath/Triangle3d.hpp b/include/omath/Triangle3d.hpp new file mode 100644 index 0000000..5f6df62 --- /dev/null +++ b/include/omath/Triangle3d.hpp @@ -0,0 +1,32 @@ +// +// Created by Orange on 11/13/2024. +// +#pragma once +#include "omath/Vector3.hpp" + +namespace omath +{ + class Triangle3d + { + public: + Triangle3d(const Vector3& vertex1, const Vector3& vertex2, const Vector3& vertex3); + Vector3 m_vertex1; + Vector3 m_vertex2; + Vector3 m_vertex3; + + [[nodiscard]] + Vector3 CalculateNormal() const; + + [[nodiscard]] + float SideALength() const; + + [[nodiscard]] + float SideBLength() const; + + [[nodiscard]] + Vector3 SideAVector() const; + + [[nodiscard]] + Vector3 SideBVector() const; + }; +} \ No newline at end of file diff --git a/include/omath/collision/LineTracer.hpp b/include/omath/collision/LineTracer.hpp new file mode 100644 index 0000000..aef9a99 --- /dev/null +++ b/include/omath/collision/LineTracer.hpp @@ -0,0 +1,22 @@ +// +// Created by Orange on 11/13/2024. +// +#pragma once +#include "omath/Vector3.hpp" +#include "omath/Triangle3d.hpp" +namespace omath::collision +{ + struct Ray + { + Vector3 start; + Vector3 end; + }; + class LineTracer + { + public: + LineTracer() = delete; + + [[nodiscard]] + static bool CanTraceLine(const Ray& ray, const Triangle3d& triangle); + }; +} diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 9d79f16..ab4e824 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -3,8 +3,11 @@ target_sources(omath PRIVATE Matrix.cpp color.cpp Vector4.cpp - Vector2.cpp) + Vector2.cpp + Triangle3d.cpp +) add_subdirectory(prediction) add_subdirectory(pathfinding) -add_subdirectory(projection) \ No newline at end of file +add_subdirectory(projection) +add_subdirectory(collision) \ No newline at end of file diff --git a/source/Triangle3d.cpp b/source/Triangle3d.cpp new file mode 100644 index 0000000..54dcf75 --- /dev/null +++ b/source/Triangle3d.cpp @@ -0,0 +1,36 @@ +#include "omath/Triangle3d.hpp" + + +namespace omath +{ + Triangle3d::Triangle3d(const Vector3 &vertex1, const Vector3 &vertex2, const Vector3 &vertex3) + : m_vertex1(vertex1), m_vertex2(vertex2), m_vertex3(vertex3) + { + + } + + Vector3 Triangle3d::CalculateNormal() const + { + return (m_vertex1 - m_vertex2).Cross(m_vertex3 - m_vertex1).Normalized(); + } + + float Triangle3d::SideALength() const + { + return m_vertex1.DistTo(m_vertex2); + } + + float Triangle3d::SideBLength() const + { + return m_vertex3.DistTo(m_vertex2); + } + + Vector3 Triangle3d::SideAVector() const + { + return m_vertex1 - m_vertex2; + } + + Vector3 Triangle3d::SideBVector() const + { + return m_vertex3 - m_vertex2; + } +} diff --git a/source/collision/CMakeLists.txt b/source/collision/CMakeLists.txt new file mode 100644 index 0000000..2904603 --- /dev/null +++ b/source/collision/CMakeLists.txt @@ -0,0 +1 @@ +target_sources(omath PRIVATE LineTracer.cpp) diff --git a/source/collision/LineTracer.cpp b/source/collision/LineTracer.cpp new file mode 100644 index 0000000..7daefdc --- /dev/null +++ b/source/collision/LineTracer.cpp @@ -0,0 +1,40 @@ +// +// Created by Orange on 11/13/2024. +// +#pragma once +#include "omath/collision/LineTracer.hpp" + +namespace omath::collision +{ + bool LineTracer::CanTraceLine(const Ray &ray, const Triangle3d &triangle) + { + const auto sideA = triangle.SideAVector(); + const auto sideB = triangle.SideBVector(); + + const auto rayDir = ray.end - ray.start; + + 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; + + const auto t = ray.start - triangle.m_vertex2; + + const auto u = t.Dot(p) * invDet; + + if (u < 0.f || u > 1.f) + return true; + + 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; + } +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 90f0bea..1c604c1 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -13,7 +13,9 @@ add_executable(unit-tests UnitTestVector3.cpp UnitTestVector2.cpp UnitTestColor.cpp - UnitTestVector4.cpp) + UnitTestVector4.cpp + UnitTestLineTrace.cpp +) target_link_libraries(unit-tests PRIVATE gtest gtest_main omath) diff --git a/tests/UnitTestLineTrace.cpp b/tests/UnitTestLineTrace.cpp new file mode 100644 index 0000000..722a150 --- /dev/null +++ b/tests/UnitTestLineTrace.cpp @@ -0,0 +1,67 @@ +#include "gtest/gtest.h" +#include "omath/collision/LineTracer.hpp" +#include "omath/Triangle3d.hpp" +#include "omath/Vector3.hpp" + +using namespace omath; +using namespace omath::collision; + +class LineTracerTest : public ::testing::Test +{ +protected: + // Set up common variables for use in each test + Vector3 vertex1{0.0f, 0.0f, 0.0f}; + Vector3 vertex2{1.0f, 0.0f, 0.0f}; + Vector3 vertex3{0.0f, 1.0f, 0.0f}; + Triangle3d triangle{vertex1, vertex2, vertex3}; +}; + +// Test that a ray intersecting the triangle returns false for CanTraceLine +TEST_F(LineTracerTest, RayIntersectsTriangle) +{ + constexpr Ray ray{{0.3f, 0.3f, -1.0f}, {0.3f, 0.3f, 1.0f}}; + EXPECT_FALSE(LineTracer::CanTraceLine(ray, triangle)); +} + +// Test that a ray parallel to the triangle plane returns true for CanTraceLine +TEST_F(LineTracerTest, RayParallelToTriangle) +{ + constexpr Ray ray{{0.3f, 0.3f, 1.0f}, {0.3f, 0.3f, 2.0f}}; + EXPECT_TRUE(LineTracer::CanTraceLine(ray, triangle)); +} + +// Test that a ray starting inside the triangle but pointing away returns true +TEST_F(LineTracerTest, RayStartsInTriangleButDoesNotIntersect) +{ + constexpr Ray ray{{0.3f, 0.3f, 0.0f}, {0.3f, 0.3f, -1.0f}}; + EXPECT_TRUE(LineTracer::CanTraceLine(ray, triangle)); +} + +// Test that a ray not intersecting the triangle plane returns true +TEST_F(LineTracerTest, RayMissesTriangle) +{ + constexpr Ray ray{{2.0f, 2.0f, -1.0f}, {2.0f, 2.0f, 1.0f}}; + EXPECT_TRUE(LineTracer::CanTraceLine(ray, triangle)); +} + +// Test that a ray lying exactly in the plane of the triangle without intersecting returns true +TEST_F(LineTracerTest, RayInPlaneNotIntersecting) +{ + constexpr Ray ray{{-1.0f, -1.0f, 0.0f}, {1.5f, 1.5f, 0.0f}}; + 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)); +} + +// 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)); +}