From 8f054316fc126b0bdf6243a8a3bbf9276df5e09e Mon Sep 17 00:00:00 2001 From: Orange Date: Sun, 9 Nov 2025 14:04:01 +0300 Subject: [PATCH 01/25] Implements GJK collision detection Adds GJK algorithm implementation for detecting collisions between mesh colliders. Includes mesh collider definition and unit tests for basic collision detection. Provides a foundation for more complex collision handling and physics interactions. --- include/omath/collision/gjk_algorithm.hpp | 47 +++++++ include/omath/collision/mesh_collider.hpp | 50 +++++++ include/omath/collision/simplex.hpp | 160 ++++++++++++++++++++++ include/omath/linear_algebra/vector3.hpp | 5 + tests/general/unit_test_colider.cpp | 29 ++++ tests/general/unit_test_gjk.cpp | 24 ++++ 6 files changed, 315 insertions(+) create mode 100644 include/omath/collision/gjk_algorithm.hpp create mode 100644 include/omath/collision/mesh_collider.hpp create mode 100644 include/omath/collision/simplex.hpp create mode 100644 tests/general/unit_test_colider.cpp create mode 100644 tests/general/unit_test_gjk.cpp diff --git a/include/omath/collision/gjk_algorithm.hpp b/include/omath/collision/gjk_algorithm.hpp new file mode 100644 index 0000000..2aae2a1 --- /dev/null +++ b/include/omath/collision/gjk_algorithm.hpp @@ -0,0 +1,47 @@ +// +// Created by Vlad on 11/9/2025. +// + +#pragma once +#include "mesh_collider.hpp" +#include "omath/linear_algebra/vector3.hpp" +#include "simplex.hpp" + +namespace omath::collision +{ + class GjkAlgorithm final + { + public: + [[nodiscard]] + static Vector3 find_support_vertex(const MeshCollider& collider_a, const MeshCollider& collider_b, + const Vector3& direction) + { + return collider_a.find_abs_furthest_vertex(direction) - collider_b.find_abs_furthest_vertex(-direction); + } + + [[nodiscard]] + static bool check_collision(const MeshCollider& collider_a, const MeshCollider& collider_b) + { + // Get initial support point in any direction + auto support = find_support_vertex(collider_a, collider_b, {1, 0, 0}); + + Simplex points; + points.push_front(support); + + auto direction = -support; + + while (true) + { + support = find_support_vertex(collider_a, collider_b, direction); + + if (support.dot(direction) <= 0.f) + return false; + + points.push_front(support); + + if (handle_simplex(points, direction)) + return true; + } + } + }; +}// namespace omath::collision \ No newline at end of file diff --git a/include/omath/collision/mesh_collider.hpp b/include/omath/collision/mesh_collider.hpp new file mode 100644 index 0000000..354ce52 --- /dev/null +++ b/include/omath/collision/mesh_collider.hpp @@ -0,0 +1,50 @@ +// +// Created by Vlad on 11/9/2025. +// + +#pragma once +#include "omath/engines/source_engine/traits/pred_engine_trait.hpp" +#include "omath/linear_algebra/vector3.hpp" +#include + +namespace omath::collision +{ + class MeshCollider + { + public: + MeshCollider(const std::vector>& vertexes, const Vector3 origin) + : m_vertexes(vertexes), m_origin(origin) + { + if (m_vertexes.empty()) + throw std::runtime_error("Collider cannot have 0 vertexes"); + } + std::vector> m_vertexes; + Vector3 m_origin; + source_engine::ViewAngles m_rotation; + + [[nodiscard]] + source_engine::Mat4X4 to_world() const + { + return mat_translation(m_origin) * source_engine::rotation_matrix(m_rotation); + } + + [[nodiscard]] + const Vector3& find_furthest_vertex(const Vector3& direction) const + { + return *std::ranges::max_element(m_vertexes, [&direction](const auto& first, const auto& second) + { return first.dot(direction) < second.dot(direction); }); + } + [[nodiscard]] + Vector3 find_abs_furthest_vertex(const Vector3& direction) const + { + return vertex_to_world_space(find_furthest_vertex(direction)); + + } + [[nodiscard]] Vector3 vertex_to_world_space( const Vector3& local_vertex) const + { + auto abs_vec = to_world() * mat_column_from_vector(local_vertex); + + return {abs_vec.at(0, 0), abs_vec.at(1, 0), abs_vec.at(2, 0)}; + } + }; +} // namespace omath::collision \ No newline at end of file diff --git a/include/omath/collision/simplex.hpp b/include/omath/collision/simplex.hpp new file mode 100644 index 0000000..23c9b25 --- /dev/null +++ b/include/omath/collision/simplex.hpp @@ -0,0 +1,160 @@ +// +// Created by Vlad on 11/9/2025. +// + +#pragma once +#include "omath/linear_algebra/vector3.hpp" +#include + +namespace omath::collision +{ + class Simplex + { + std::array, 4> m_points; + int m_size; + + public: + Simplex(): m_size(0) + { + } + + Simplex& operator=(const std::initializer_list> list) + { + m_size = 0; + + for (const Vector3& point : list) + m_points[m_size++] = point; + + return *this; + } + + void push_front(const Vector3& point) + { + m_points = {point, m_points[0], m_points[1], m_points[2]}; + m_size = std::min(m_size + 1, 4); + } + + Vector3& operator[](const int i) + { + return m_points[i]; + } + size_t size() const + { + return m_size; + } + + auto begin() const + { + return m_points.begin(); + } + auto end() const + { + return m_points.end() - (4 - m_size); + } + }; + + bool handle_line(Simplex& points, Vector3& direction) + { + Vector3 a = points[0]; + const Vector3 b = points[1]; + + Vector3 ab = b - a; + const Vector3 ao = -a; + + if (ab.point_to_same_direction(ao)) + direction = ab.cross(ao).cross(ab); + else + { + points = {a}; + direction = ao; + } + + return false; + } + + bool handle_triangle(Simplex& points, Vector3& direction) + { + Vector3 a = points[0]; + Vector3 b = points[1]; + Vector3 c = points[2]; + + Vector3 ab = b - a; + Vector3 ac = c - a; + Vector3 ao = -a; + + Vector3 abc = ab.cross(ac); + + if (abc.cross(ac).point_to_same_direction(ao)) + { + if (ac.point_to_same_direction(ao)) + { + points = {a, c}; + direction = ac.cross(ao).cross(ac); + + return false; + } + return handle_line(points = {a, b}, direction); + } + + if (ab.cross(abc).point_to_same_direction(ao)) + return handle_line(points = {a, b}, direction); + + + if (abc.point_to_same_direction(ao)) + { + direction = abc; + } + else + { + points = {a, c, b}; + direction = -abc; + } + + return false; + } + + bool handle_tetrahedron(Simplex& points, Vector3& direction) + { + Vector3 a = points[0]; + Vector3 b = points[1]; + Vector3 c = points[2]; + Vector3 d = points[3]; + + Vector3 ab = b - a; + Vector3 ac = c - a; + Vector3 ad = d - a; + Vector3 ao = -a; + + Vector3 abc = ab.cross(ac); + Vector3 acd = ac.cross(ad); + Vector3 adb = ad.cross(ab); + + if (abc.point_to_same_direction(ao)) + return handle_triangle(points = {a, b, c}, direction); + + if (acd.point_to_same_direction(ao)) + return handle_triangle(points = {a, c, d}, direction); + + if (adb.point_to_same_direction(ao)) + return handle_triangle(points = {a, d, b}, direction); + + + return true; + } + + [[nodiscard]] + bool handle_simplex(Simplex& points, Vector3& direction) + { + switch (points.size()) + { + case 2: + return handle_line(points, direction); + case 3: + return handle_triangle(points, direction); + case 4: + return handle_tetrahedron(points, direction); + default: + return false; + } + } +} // namespace omath::collision \ No newline at end of file diff --git a/include/omath/linear_algebra/vector3.hpp b/include/omath/linear_algebra/vector3.hpp index 77d60cf..5432bb0 100644 --- a/include/omath/linear_algebra/vector3.hpp +++ b/include/omath/linear_algebra/vector3.hpp @@ -216,6 +216,11 @@ namespace omath return sum_2d() + z; } + [[nodiscard]] + bool point_to_same_direction(const Vector3& other) + { + return dot(other) > static_cast(0); + } [[nodiscard]] std::expected, Vector3Error> angle_between(const Vector3& other) const noexcept { diff --git a/tests/general/unit_test_colider.cpp b/tests/general/unit_test_colider.cpp new file mode 100644 index 0000000..97f0eff --- /dev/null +++ b/tests/general/unit_test_colider.cpp @@ -0,0 +1,29 @@ +// +// Created by Vlad on 11/9/2025. +// +#include +#include + + + +TEST(UnitTestColider, CheckToWorld) +{ + const std::vector> mesh = {{1.f, 1.f, 1.f}, {-1.f, -1.f, -1.f}}; + + const omath::collision::MeshCollider collider(mesh, {0.f, 2.f, 0.f}); + + const auto vertex = collider.find_abs_furthest_vertex({1.f, 0.f, 0.f}); + + EXPECT_EQ(vertex, omath::Vector3(1.f, 3.f, 1.f)); +} + +TEST(UnitTestColider, FindFurthestVertex) +{ + const std::vector> mesh = {{1.f, 1.f, 1.f}, {-1.f, -1.f, -1.f}}; + const omath::collision::MeshCollider collider(mesh, {0.f, 0.f, 0.f}); + const auto vertex = collider.find_furthest_vertex({1.f, 0.f, 0.f}); + EXPECT_EQ(vertex, omath::Vector3(1.f, 1.f, 1.f)); +} + + + diff --git a/tests/general/unit_test_gjk.cpp b/tests/general/unit_test_gjk.cpp new file mode 100644 index 0000000..9ec5f42 --- /dev/null +++ b/tests/general/unit_test_gjk.cpp @@ -0,0 +1,24 @@ +// +// Created by Vlad on 11/9/2025. +// +#include +#include + +TEST(UnitTestGjk, TestCollision) +{ + const std::vector> mesh = { + {-1.f, -1.f, -1.f}, + {-1.f, -1.f, 1.f}, + {-1.f, 1.f, -1.f}, + {-1.f, 1.f, 1.f}, + { 1.f, 1.f, 1.f}, // x = +1 vertices (put {1,1,1} first in case your support breaks ties by first-hit) + { 1.f, 1.f, -1.f}, + { 1.f, -1.f, 1.f}, + { 1.f, -1.f, -1.f} + }; + + const omath::collision::MeshCollider collider_a(mesh, {0.f, 0.f, 0.f}); + const omath::collision::MeshCollider collider_b(mesh, {0.f, 3.f, 0.f}); + + const auto result = omath::collision::GjkAlgorithm::check_collision(collider_a, collider_b); +} \ No newline at end of file From 5c81533c06ae8161bf5249378d8b246d3413e986 Mon Sep 17 00:00:00 2001 From: Orange Date: Sun, 9 Nov 2025 14:05:46 +0300 Subject: [PATCH 02/25] Updates simplex iterator and size access Changes the index type for accessing simplex points to `std::size_t` for consistency and safety. Adds `[[nodiscard]]` attribute to `size()` and iterator functions to signal potential misuse and enable static analysis. These updates are part of the GJK algorithm implementation. --- include/omath/collision/simplex.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/omath/collision/simplex.hpp b/include/omath/collision/simplex.hpp index 23c9b25..20ca8de 100644 --- a/include/omath/collision/simplex.hpp +++ b/include/omath/collision/simplex.hpp @@ -34,20 +34,20 @@ namespace omath::collision m_size = std::min(m_size + 1, 4); } - Vector3& operator[](const int i) + Vector3& operator[](const std::size_t i) { return m_points[i]; } - size_t size() const + [[nodiscard]] std::size_t size() const { return m_size; } - auto begin() const + [[nodiscard]] auto begin() const { return m_points.begin(); } - auto end() const + [[nodiscard]] auto end() const { return m_points.end() - (4 - m_size); } From 4c525d5c3123378625a92cf894f6316f92d3d5f7 Mon Sep 17 00:00:00 2001 From: Orange Date: Sun, 9 Nov 2025 14:15:32 +0300 Subject: [PATCH 03/25] Refactors GJK tetrahedron handling Updates the `handle_tetrahedron` function to use const references for simplex points, improving efficiency and readability. Corrects a potential bug where the `simplex` variable wasn't being correctly updated when recursively calling `handle_triangle`. Also, const-qualifies `point_to_same_direction` for better safety. --- include/omath/collision/simplex.hpp | 30 ++++++++++++------------ include/omath/linear_algebra/vector3.hpp | 2 +- include/omath/utility/color.hpp | 2 +- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/include/omath/collision/simplex.hpp b/include/omath/collision/simplex.hpp index 20ca8de..46d7e1e 100644 --- a/include/omath/collision/simplex.hpp +++ b/include/omath/collision/simplex.hpp @@ -113,30 +113,30 @@ namespace omath::collision return false; } - bool handle_tetrahedron(Simplex& points, Vector3& direction) + bool handle_tetrahedron(Simplex& simplex, Vector3& direction) { - Vector3 a = points[0]; - Vector3 b = points[1]; - Vector3 c = points[2]; - Vector3 d = points[3]; + const auto& a = simplex[0]; + const auto& b = simplex[1]; + const auto& c = simplex[2]; + const auto& d = simplex[3]; - Vector3 ab = b - a; - Vector3 ac = c - a; - Vector3 ad = d - a; - Vector3 ao = -a; + const Vector3 ab = b - a; + const Vector3 ac = c - a; + const Vector3 ad = d - a; + const Vector3 ao = -a; - Vector3 abc = ab.cross(ac); - Vector3 acd = ac.cross(ad); - Vector3 adb = ad.cross(ab); + const Vector3 abc = ab.cross(ac); + const Vector3 acd = ac.cross(ad); + const Vector3 adb = ad.cross(ab); if (abc.point_to_same_direction(ao)) - return handle_triangle(points = {a, b, c}, direction); + return handle_triangle(simplex = {a, b, c}, direction); if (acd.point_to_same_direction(ao)) - return handle_triangle(points = {a, c, d}, direction); + return handle_triangle(simplex = {a, c, d}, direction); if (adb.point_to_same_direction(ao)) - return handle_triangle(points = {a, d, b}, direction); + return handle_triangle(simplex = {a, d, b}, direction); return true; diff --git a/include/omath/linear_algebra/vector3.hpp b/include/omath/linear_algebra/vector3.hpp index 5432bb0..4b1d381 100644 --- a/include/omath/linear_algebra/vector3.hpp +++ b/include/omath/linear_algebra/vector3.hpp @@ -217,7 +217,7 @@ namespace omath } [[nodiscard]] - bool point_to_same_direction(const Vector3& other) + bool point_to_same_direction(const Vector3& other) const { return dot(other) > static_cast(0); } diff --git a/include/omath/utility/color.hpp b/include/omath/utility/color.hpp index cfacee8..fd73451 100644 --- a/include/omath/utility/color.hpp +++ b/include/omath/utility/color.hpp @@ -9,7 +9,7 @@ namespace omath { - struct Hsv + struct Hsv final { float hue{}; float saturation{}; From b33288555acaa1355a477d2ab3054e148907e800 Mon Sep 17 00:00:00 2001 From: Orange Date: Sun, 9 Nov 2025 14:19:08 +0300 Subject: [PATCH 04/25] Adds mat_scale function Introduces a utility function to create a scaling matrix from a Vector3. This simplifies the creation of scale transformations, particularly useful for the GJK algorithm implementation. --- include/omath/linear_algebra/mat.hpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/include/omath/linear_algebra/mat.hpp b/include/omath/linear_algebra/mat.hpp index 75c4848..18b7d71 100644 --- a/include/omath/linear_algebra/mat.hpp +++ b/include/omath/linear_algebra/mat.hpp @@ -586,6 +586,17 @@ namespace omath {0, 0, 0, 1}, }; } + template + [[nodiscard]] + constexpr Mat<4, 4, Type, St> mat_scale(const Vector3& scale) noexcept + { + return { + {scale.x, 0, 0, 0}, + {0, scale.y, 0, 0}, + {0, 0, scale.z, 0}, + {0, 0, 0, 1}, + }; + } template [[nodiscard]] From 727a9cc07eb6530253de1e1e3c9044f561ba329b Mon Sep 17 00:00:00 2001 From: Orange Date: Sun, 9 Nov 2025 14:23:34 +0300 Subject: [PATCH 05/25] patch --- include/omath/collision/mesh_collider.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/include/omath/collision/mesh_collider.hpp b/include/omath/collision/mesh_collider.hpp index 354ce52..f422a3e 100644 --- a/include/omath/collision/mesh_collider.hpp +++ b/include/omath/collision/mesh_collider.hpp @@ -38,7 +38,6 @@ namespace omath::collision Vector3 find_abs_furthest_vertex(const Vector3& direction) const { return vertex_to_world_space(find_furthest_vertex(direction)); - } [[nodiscard]] Vector3 vertex_to_world_space( const Vector3& local_vertex) const { From da2d51be6dc2d61007a3d5541ea0426faeeef84a Mon Sep 17 00:00:00 2001 From: Orange Date: Sun, 9 Nov 2025 14:40:22 +0300 Subject: [PATCH 06/25] Refactors simplex handling in GJK algorithm Updates simplex handling to use references for vertex access, avoiding unnecessary copies. This improves performance and clarity within the GJK algorithm. --- include/omath/collision/simplex.hpp | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/include/omath/collision/simplex.hpp b/include/omath/collision/simplex.hpp index 46d7e1e..703afb4 100644 --- a/include/omath/collision/simplex.hpp +++ b/include/omath/collision/simplex.hpp @@ -55,10 +55,11 @@ namespace omath::collision bool handle_line(Simplex& points, Vector3& direction) { - Vector3 a = points[0]; - const Vector3 b = points[1]; + const Vector3& a = points[0]; + const Vector3& b = points[1]; - Vector3 ab = b - a; + const Vector3 ab = b - a; + // ReSharper disable once CppTooWideScopeInitStatement const Vector3 ao = -a; if (ab.point_to_same_direction(ao)) @@ -74,15 +75,15 @@ namespace omath::collision bool handle_triangle(Simplex& points, Vector3& direction) { - Vector3 a = points[0]; - Vector3 b = points[1]; - Vector3 c = points[2]; + const Vector3& a = points[0]; + const Vector3& b = points[1]; + const Vector3& c = points[2]; - Vector3 ab = b - a; - Vector3 ac = c - a; - Vector3 ao = -a; + const Vector3 ab = b - a; + const Vector3 ac = c - a; + const Vector3 ao = -a; - Vector3 abc = ab.cross(ac); + const Vector3 abc = ab.cross(ac); if (abc.cross(ac).point_to_same_direction(ao)) { @@ -99,7 +100,6 @@ namespace omath::collision if (ab.cross(abc).point_to_same_direction(ao)) return handle_line(points = {a, b}, direction); - if (abc.point_to_same_direction(ao)) { direction = abc; @@ -138,7 +138,6 @@ namespace omath::collision if (adb.point_to_same_direction(ao)) return handle_triangle(simplex = {a, d, b}, direction); - return true; } From c0353cd9be7e7d10a060a586b50dd11313ee9072 Mon Sep 17 00:00:00 2001 From: Orange Date: Sun, 9 Nov 2025 15:38:38 +0300 Subject: [PATCH 07/25] updated test --- include/omath/collision/gjk_algorithm.hpp | 2 +- tests/general/unit_test_gjk.cpp | 28 ++++++++++++++++++++--- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/include/omath/collision/gjk_algorithm.hpp b/include/omath/collision/gjk_algorithm.hpp index 2aae2a1..cb8645a 100644 --- a/include/omath/collision/gjk_algorithm.hpp +++ b/include/omath/collision/gjk_algorithm.hpp @@ -20,7 +20,7 @@ namespace omath::collision } [[nodiscard]] - static bool check_collision(const MeshCollider& collider_a, const MeshCollider& collider_b) + static bool is_collide(const MeshCollider& collider_a, const MeshCollider& collider_b) { // Get initial support point in any direction auto support = find_support_vertex(collider_a, collider_b, {1, 0, 0}); diff --git a/tests/general/unit_test_gjk.cpp b/tests/general/unit_test_gjk.cpp index 9ec5f42..45259d4 100644 --- a/tests/general/unit_test_gjk.cpp +++ b/tests/general/unit_test_gjk.cpp @@ -4,7 +4,7 @@ #include #include -TEST(UnitTestGjk, TestCollision) +TEST(UnitTestGjk, TestCollisionTrue) { const std::vector> mesh = { {-1.f, -1.f, -1.f}, @@ -18,7 +18,29 @@ TEST(UnitTestGjk, TestCollision) }; const omath::collision::MeshCollider collider_a(mesh, {0.f, 0.f, 0.f}); - const omath::collision::MeshCollider collider_b(mesh, {0.f, 3.f, 0.f}); + const omath::collision::MeshCollider collider_b(mesh, {0.f, 0.5f, 0.f}); - const auto result = omath::collision::GjkAlgorithm::check_collision(collider_a, collider_b); + const auto result = omath::collision::GjkAlgorithm::is_collide(collider_a, collider_b); + + EXPECT_TRUE(result); +} +TEST(UnitTestGjk, TestCollisionFalse) +{ + const std::vector> mesh = { + {-1.f, -1.f, -1.f}, + {-1.f, -1.f, 1.f}, + {-1.f, 1.f, -1.f}, + {-1.f, 1.f, 1.f}, + { 1.f, 1.f, 1.f}, // x = +1 vertices (put {1,1,1} first in case your support breaks ties by first-hit) + { 1.f, 1.f, -1.f}, + { 1.f, -1.f, 1.f}, + { 1.f, -1.f, -1.f} + }; + + const omath::collision::MeshCollider collider_a(mesh, {0.f, 0.f, 0.f}); + const omath::collision::MeshCollider collider_b(mesh, {0.f, 4.1f, 0.f}); + + const auto result = omath::collision::GjkAlgorithm::is_collide(collider_a, collider_b); + + EXPECT_FALSE(result); } \ No newline at end of file From bbd181f12f0bf8da90d1e1911ec57243c43ce34d Mon Sep 17 00:00:00 2001 From: Orange Date: Sun, 9 Nov 2025 15:39:11 +0300 Subject: [PATCH 08/25] updated --- include/omath/collision/gjk_algorithm.hpp | 8 +- include/omath/collision/simplex.hpp | 228 ++++++++++++---------- 2 files changed, 126 insertions(+), 110 deletions(-) diff --git a/include/omath/collision/gjk_algorithm.hpp b/include/omath/collision/gjk_algorithm.hpp index cb8645a..b2c7a48 100644 --- a/include/omath/collision/gjk_algorithm.hpp +++ b/include/omath/collision/gjk_algorithm.hpp @@ -25,8 +25,8 @@ namespace omath::collision // Get initial support point in any direction auto support = find_support_vertex(collider_a, collider_b, {1, 0, 0}); - Simplex points; - points.push_front(support); + Simplex simplex; + simplex.push_front(support); auto direction = -support; @@ -37,9 +37,9 @@ namespace omath::collision if (support.dot(direction) <= 0.f) return false; - points.push_front(support); + simplex.push_front(support); - if (handle_simplex(points, direction)) + if (simplex.handle(direction)) return true; } } diff --git a/include/omath/collision/simplex.hpp b/include/omath/collision/simplex.hpp index 703afb4..e59c132 100644 --- a/include/omath/collision/simplex.hpp +++ b/include/omath/collision/simplex.hpp @@ -8,152 +8,168 @@ namespace omath::collision { + template> class Simplex { - std::array, 4> m_points; - int m_size; + std::array m_points; + std::size_t m_size; public: - Simplex(): m_size(0) + constexpr Simplex(): m_size(0) { } - Simplex& operator=(const std::initializer_list> list) + constexpr Simplex& operator=(const std::initializer_list& list) { m_size = 0; - for (const Vector3& point : list) + for (const VectorType& point : list) m_points[m_size++] = point; return *this; } - void push_front(const Vector3& point) + constexpr void push_front(const VectorType& point) { m_points = {point, m_points[0], m_points[1], m_points[2]}; - m_size = std::min(m_size + 1, 4); + m_size = std::min(m_size + 1, 4); } - Vector3& operator[](const std::size_t i) + constexpr const VectorType& operator[](const std::size_t i) const { return m_points[i]; } - [[nodiscard]] std::size_t size() const + [[nodiscard]] + constexpr std::size_t size() const { return m_size; } - [[nodiscard]] auto begin() const + [[nodiscard]] + constexpr auto begin() const { return m_points.begin(); } - [[nodiscard]] auto end() const + [[nodiscard]] + constexpr auto end() const { return m_points.end() - (4 - m_size); } - }; - - bool handle_line(Simplex& points, Vector3& direction) - { - const Vector3& a = points[0]; - const Vector3& b = points[1]; - - const Vector3 ab = b - a; - // ReSharper disable once CppTooWideScopeInitStatement - const Vector3 ao = -a; - - if (ab.point_to_same_direction(ao)) - direction = ab.cross(ao).cross(ab); - else + [[nodiscard]] + constexpr bool handle(VectorType& direction) { - points = {a}; - direction = ao; - } - - return false; - } - - bool handle_triangle(Simplex& points, Vector3& direction) - { - const Vector3& a = points[0]; - const Vector3& b = points[1]; - const Vector3& c = points[2]; - - const Vector3 ab = b - a; - const Vector3 ac = c - a; - const Vector3 ao = -a; - - const Vector3 abc = ab.cross(ac); - - if (abc.cross(ac).point_to_same_direction(ao)) - { - if (ac.point_to_same_direction(ao)) + switch (m_points.size()) { - points = {a, c}; - direction = ac.cross(ao).cross(ac); - - return false; + case 2: + return handle_line(direction); + case 3: + return handle_triangle(direction); + case 4: + return handle_tetrahedron(direction); + default: + std::unreachable(); } - return handle_line(points = {a, b}, direction); } - - if (ab.cross(abc).point_to_same_direction(ao)) - return handle_line(points = {a, b}, direction); - - if (abc.point_to_same_direction(ao)) + private: + [[nodiscard]] + constexpr bool handle_line(VectorType& direction) { - direction = abc; - } - else - { - points = {a, c, b}; - direction = -abc; - } + const auto& a = m_points[0]; + const auto& b = m_points[1]; - return false; - } + const auto ab = b - a; + // ReSharper disable once CppTooWideScopeInitStatement + const auto ao = -a; - bool handle_tetrahedron(Simplex& simplex, Vector3& direction) - { - const auto& a = simplex[0]; - const auto& b = simplex[1]; - const auto& c = simplex[2]; - const auto& d = simplex[3]; + if (ab.point_to_same_direction(ao)) + direction = ab.cross(ao).cross(ab); + else + { + *this = {a}; + direction = ao; + } - const Vector3 ab = b - a; - const Vector3 ac = c - a; - const Vector3 ad = d - a; - const Vector3 ao = -a; - - const Vector3 abc = ab.cross(ac); - const Vector3 acd = ac.cross(ad); - const Vector3 adb = ad.cross(ab); - - if (abc.point_to_same_direction(ao)) - return handle_triangle(simplex = {a, b, c}, direction); - - if (acd.point_to_same_direction(ao)) - return handle_triangle(simplex = {a, c, d}, direction); - - if (adb.point_to_same_direction(ao)) - return handle_triangle(simplex = {a, d, b}, direction); - - return true; - } - - [[nodiscard]] - bool handle_simplex(Simplex& points, Vector3& direction) - { - switch (points.size()) - { - case 2: - return handle_line(points, direction); - case 3: - return handle_triangle(points, direction); - case 4: - return handle_tetrahedron(points, direction); - default: return false; } - } + [[nodiscard]] + constexpr bool handle_triangle(VectorType& direction) + { + const auto& a = m_points[0]; + const auto& b = m_points[1]; + const auto& c = m_points[2]; + + const auto ab = b - a; + const auto ac = c - a; + const auto ao = -a; + + const auto abc = ab.cross(ac); + + if (abc.cross(ac).point_to_same_direction(ao)) + { + if (ac.point_to_same_direction(ao)) + { + *this = {a, c}; + direction = ac.cross(ao).cross(ac); + + return false; + } + *this = {a, b}; + return handle_line(direction); + } + + if (ab.cross(abc).point_to_same_direction(ao)) + { + *this = {a, b}; + return handle_line(direction); + } + if (abc.point_to_same_direction(ao)) + { + direction = abc; + } + else + { + *this = {a, c, b}; + direction = -abc; + } + + return false; + } + [[nodiscard]] + constexpr bool handle_tetrahedron(VectorType& direction) + { + const auto& a = m_points[0]; + const auto& b = m_points[1]; + const auto& c = m_points[2]; + const auto& d = m_points[3]; + + const auto ab = b - a; + const auto ac = c - a; + const auto ad = d - a; + const auto ao = -a; + + const auto abc = ab.cross(ac); + const auto acd = ac.cross(ad); + const auto adb = ad.cross(ab); + + if (abc.point_to_same_direction(ao)) + { + *this = {a, b, c}; + return handle_triangle(direction); + } + + if (acd.point_to_same_direction(ao)) + { + *this = {a, c, d}; + return handle_triangle(direction); + } + + if (adb.point_to_same_direction(ao)) + { + *this = {a, d, b}; + return handle_triangle(direction); + } + return true; + } + }; + } // namespace omath::collision \ No newline at end of file From 1aa0360ac4432675247bfc3a4ae0c0a22e63f74f Mon Sep 17 00:00:00 2001 From: Orange Date: Sun, 9 Nov 2025 15:50:07 +0300 Subject: [PATCH 09/25] fix --- include/omath/collision/simplex.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/omath/collision/simplex.hpp b/include/omath/collision/simplex.hpp index e59c132..798e1f0 100644 --- a/include/omath/collision/simplex.hpp +++ b/include/omath/collision/simplex.hpp @@ -58,7 +58,7 @@ namespace omath::collision [[nodiscard]] constexpr bool handle(VectorType& direction) { - switch (m_points.size()) + switch (size()) { case 2: return handle_line(direction); From 82e78016e3eb9a6ee8b1c2a11965a518d6482775 Mon Sep 17 00:00:00 2001 From: Orange Date: Sun, 9 Nov 2025 15:51:28 +0300 Subject: [PATCH 10/25] made final --- include/omath/collision/simplex.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/omath/collision/simplex.hpp b/include/omath/collision/simplex.hpp index 798e1f0..e585508 100644 --- a/include/omath/collision/simplex.hpp +++ b/include/omath/collision/simplex.hpp @@ -9,7 +9,7 @@ namespace omath::collision { template> - class Simplex + class Simplex final { std::array m_points; std::size_t m_size; From 8875157c79c6314233931472dda12d042384b985 Mon Sep 17 00:00:00 2001 From: Orange Date: Sun, 9 Nov 2025 16:02:13 +0300 Subject: [PATCH 11/25] Refactor: Simplify GJK simplex handling Removes the separate `Simplex` class and integrates its functionality directly into the `GjkAlgorithm`. This simplifies the code and reduces unnecessary overhead. Updates tests to align with refactored implementation. --- include/omath/collision/gjk_algorithm.hpp | 10 +- include/omath/collision/mesh_collider.hpp | 1 + include/omath/collision/simplex.hpp | 143 +++++++++++++++------- tests/general/unit_test_gjk.cpp | 6 +- 4 files changed, 110 insertions(+), 50 deletions(-) diff --git a/include/omath/collision/gjk_algorithm.hpp b/include/omath/collision/gjk_algorithm.hpp index b2c7a48..d494cb1 100644 --- a/include/omath/collision/gjk_algorithm.hpp +++ b/include/omath/collision/gjk_algorithm.hpp @@ -9,12 +9,14 @@ namespace omath::collision { + template class GjkAlgorithm final { public: [[nodiscard]] - static Vector3 find_support_vertex(const MeshCollider& collider_a, const MeshCollider& collider_b, - const Vector3& direction) + static MeshCollider::VertexType find_support_vertex(const ColliderType& collider_a, + const ColliderType& collider_b, + const MeshCollider::VertexType& direction) { return collider_a.find_abs_furthest_vertex(direction) - collider_b.find_abs_furthest_vertex(-direction); } @@ -25,7 +27,7 @@ namespace omath::collision // Get initial support point in any direction auto support = find_support_vertex(collider_a, collider_b, {1, 0, 0}); - Simplex simplex; + Simplex simplex; simplex.push_front(support); auto direction = -support; @@ -44,4 +46,4 @@ namespace omath::collision } } }; -}// namespace omath::collision \ No newline at end of file +} // namespace omath::collision \ No newline at end of file diff --git a/include/omath/collision/mesh_collider.hpp b/include/omath/collision/mesh_collider.hpp index f422a3e..e0b4ea3 100644 --- a/include/omath/collision/mesh_collider.hpp +++ b/include/omath/collision/mesh_collider.hpp @@ -12,6 +12,7 @@ namespace omath::collision class MeshCollider { public: + using VertexType = Vector3; MeshCollider(const std::vector>& vertexes, const Vector3 origin) : m_vertexes(vertexes), m_origin(origin) { diff --git a/include/omath/collision/simplex.hpp b/include/omath/collision/simplex.hpp index e585508..140c193 100644 --- a/include/omath/collision/simplex.hpp +++ b/include/omath/collision/simplex.hpp @@ -1,65 +1,114 @@ -// -// Created by Vlad on 11/9/2025. -// - #pragma once #include "omath/linear_algebra/vector3.hpp" #include +#include +#include +#include namespace omath::collision { - template> + + // Minimal structural contract for the vector type used by GJK. + template + concept GjkVector = requires(const V& a, const V& b) { + { -a } -> std::same_as; + { a - b } -> std::same_as; + { a.cross(b) } -> std::same_as; + { a.point_to_same_direction(b) } -> std::same_as; + }; + + template> class Simplex final { - std::array m_points; - std::size_t m_size; + std::array m_points{}; // value-initialized + std::size_t m_size{0}; public: - constexpr Simplex(): m_size(0) - { - } + static constexpr std::size_t capacity = 4; - constexpr Simplex& operator=(const std::initializer_list& list) + constexpr Simplex() = default; + + // Keep your convenient "{a, b, c}" assignments, but guard size. + constexpr Simplex& operator=(std::initializer_list list) noexcept { + assert(list.size() <= capacity && "Simplex can have at most 4 points"); m_size = 0; - - for (const VectorType& point : list) - m_points[m_size++] = point; - + for (const auto& p : list) + m_points[m_size++] = p; return *this; } - constexpr void push_front(const VectorType& point) + // Safe push_front: only shifts the valid range; no reads from uninitialized slots. + constexpr void push_front(const VectorType& p) noexcept { - m_points = {point, m_points[0], m_points[1], m_points[2]}; - m_size = std::min(m_size + 1, 4); + const std::size_t limit = (m_size < capacity) ? m_size : capacity - 1; + for (std::size_t i = limit; i > 0; --i) + m_points[i] = m_points[i - 1]; + m_points[0] = p; + if (m_size < capacity) + ++m_size; } - constexpr const VectorType& operator[](const std::size_t i) const + // Accessors + constexpr const VectorType& operator[](std::size_t i) const noexcept { return m_points[i]; } - [[nodiscard]] - constexpr std::size_t size() const + constexpr VectorType& operator[](std::size_t i) noexcept + { + return m_points[i]; + } + + [[nodiscard]] constexpr std::size_t size() const noexcept { return m_size; } - [[nodiscard]] - constexpr auto begin() const + [[nodiscard]] constexpr bool empty() const noexcept + { + return m_size == 0; + } + + [[nodiscard]] constexpr const VectorType& front() const noexcept + { + return m_points[0]; + } + + [[nodiscard]] constexpr const VectorType& back() const noexcept + { + return m_points[m_size - 1]; + } + + [[nodiscard]] constexpr const VectorType* data() const noexcept + { + return m_points.data(); + } + + [[nodiscard]] constexpr auto begin() const noexcept { return m_points.begin(); } - [[nodiscard]] - constexpr auto end() const + + [[nodiscard]] constexpr auto end() const noexcept { - return m_points.end() - (4 - m_size); + return m_points.begin() + m_size; } - [[nodiscard]] - constexpr bool handle(VectorType& direction) + + constexpr void clear() noexcept { - switch (size()) + m_size = 0; + } + + // GJK step: updates simplex + next search direction. + // Returns true iff the origin lies inside the tetrahedron. + [[nodiscard]] constexpr bool handle(VectorType& direction) noexcept + { + switch (m_size) { + case 0: + return false; + case 1: + return handle_point(direction); case 2: return handle_line(direction); case 3: @@ -70,29 +119,36 @@ namespace omath::collision std::unreachable(); } } + private: - [[nodiscard]] - constexpr bool handle_line(VectorType& direction) + [[nodiscard]] constexpr bool handle_point(VectorType& direction) noexcept + { + const auto& a = m_points[0]; + direction = -a; + return false; + } + + [[nodiscard]] constexpr bool handle_line(VectorType& direction) noexcept { const auto& a = m_points[0]; const auto& b = m_points[1]; const auto ab = b - a; - // ReSharper disable once CppTooWideScopeInitStatement const auto ao = -a; if (ab.point_to_same_direction(ao)) + { direction = ab.cross(ao).cross(ab); + } else { *this = {a}; direction = ao; } - return false; } - [[nodiscard]] - constexpr bool handle_triangle(VectorType& direction) + + [[nodiscard]] constexpr bool handle_triangle(VectorType& direction) noexcept { const auto& a = m_points[0]; const auto& b = m_points[1]; @@ -104,38 +160,40 @@ namespace omath::collision const auto abc = ab.cross(ac); + // Region AC if (abc.cross(ac).point_to_same_direction(ao)) { if (ac.point_to_same_direction(ao)) { *this = {a, c}; direction = ac.cross(ao).cross(ac); - return false; } *this = {a, b}; return handle_line(direction); } + // Region AB if (ab.cross(abc).point_to_same_direction(ao)) { *this = {a, b}; return handle_line(direction); } + + // Above or below triangle if (abc.point_to_same_direction(ao)) { direction = abc; } else { - *this = {a, c, b}; + *this = {a, c, b}; // flip winding direction = -abc; } - return false; } - [[nodiscard]] - constexpr bool handle_tetrahedron(VectorType& direction) + + [[nodiscard]] constexpr bool handle_tetrahedron(VectorType& direction) noexcept { const auto& a = m_points[0]; const auto& b = m_points[1]; @@ -156,20 +214,19 @@ namespace omath::collision *this = {a, b, c}; return handle_triangle(direction); } - if (acd.point_to_same_direction(ao)) { *this = {a, c, d}; return handle_triangle(direction); } - if (adb.point_to_same_direction(ao)) { *this = {a, d, b}; return handle_triangle(direction); } + // Origin inside tetrahedron return true; } }; -} // namespace omath::collision \ No newline at end of file +} // namespace omath::collision diff --git a/tests/general/unit_test_gjk.cpp b/tests/general/unit_test_gjk.cpp index 45259d4..bd0fdac 100644 --- a/tests/general/unit_test_gjk.cpp +++ b/tests/general/unit_test_gjk.cpp @@ -20,7 +20,7 @@ TEST(UnitTestGjk, TestCollisionTrue) const omath::collision::MeshCollider collider_a(mesh, {0.f, 0.f, 0.f}); const omath::collision::MeshCollider collider_b(mesh, {0.f, 0.5f, 0.f}); - const auto result = omath::collision::GjkAlgorithm::is_collide(collider_a, collider_b); + const auto result = omath::collision::GjkAlgorithm<>::is_collide(collider_a, collider_b); EXPECT_TRUE(result); } @@ -38,9 +38,9 @@ TEST(UnitTestGjk, TestCollisionFalse) }; const omath::collision::MeshCollider collider_a(mesh, {0.f, 0.f, 0.f}); - const omath::collision::MeshCollider collider_b(mesh, {0.f, 4.1f, 0.f}); + const omath::collision::MeshCollider collider_b(mesh, {0.f, 2.1f, 0.f}); - const auto result = omath::collision::GjkAlgorithm::is_collide(collider_a, collider_b); + const auto result = omath::collision::GjkAlgorithm<>::is_collide(collider_a, collider_b); EXPECT_FALSE(result); } \ No newline at end of file From 1dbaa4d53b076cc127b24c44c22765c7634b433b Mon Sep 17 00:00:00 2001 From: Orange Date: Sun, 9 Nov 2025 16:56:38 +0300 Subject: [PATCH 12/25] Handles collinear cases in simplex collision Adds helper functions to address near-zero vectors and find perpendicular directions. This prevents potential crashes and improves robustness when the origin lies on the line defined by the simplex edges during GJK collision detection. --- include/omath/collision/simplex.hpp | 31 +++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/include/omath/collision/simplex.hpp b/include/omath/collision/simplex.hpp index 140c193..5400afc 100644 --- a/include/omath/collision/simplex.hpp +++ b/include/omath/collision/simplex.hpp @@ -128,7 +128,25 @@ namespace omath::collision return false; } - [[nodiscard]] constexpr bool handle_line(VectorType& direction) noexcept + template + static constexpr bool near_zero(const V& v, float eps = 1e-7f) + { + return v.dot(v) <= eps * eps; + } + + template + static constexpr V any_perp(const V& v) + { + // try cross with axes until non-zero + V d = v.cross(V{1, 0, 0}); + if (near_zero(d)) + d = v.cross(V{0, 1, 0}); + if (near_zero(d)) + d = v.cross(V{0, 0, 1}); + return d; + } + + constexpr bool handle_line(VectorType& direction) { const auto& a = m_points[0]; const auto& b = m_points[1]; @@ -138,7 +156,16 @@ namespace omath::collision if (ab.point_to_same_direction(ao)) { - direction = ab.cross(ao).cross(ab); + auto n = ab.cross(ao); + if (near_zero(n)) + { + // collinear: origin lies on ray AB (often on segment), pick any perp to escape + direction = any_perp(ab); + } + else + { + direction = n.cross(ab); + } } else { From c20bfe1f8cea2ca24f41b6b8b02fa31e31331943 Mon Sep 17 00:00:00 2001 From: Orange Date: Sun, 9 Nov 2025 16:57:52 +0300 Subject: [PATCH 13/25] Adds GJK collision test with equal origins Implements a new test case for the GJK algorithm to verify collision detection when colliders share the same origin. This enhances the robustness of collision detection in scenarios where objects are positioned at the same location. --- tests/general/unit_test_gjk.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/general/unit_test_gjk.cpp b/tests/general/unit_test_gjk.cpp index bd0fdac..8c306cc 100644 --- a/tests/general/unit_test_gjk.cpp +++ b/tests/general/unit_test_gjk.cpp @@ -43,4 +43,25 @@ TEST(UnitTestGjk, TestCollisionFalse) const auto result = omath::collision::GjkAlgorithm<>::is_collide(collider_a, collider_b); EXPECT_FALSE(result); +} + +TEST(UnitTestGjk, TestCollisionEqualOrigin) +{ + const std::vector> mesh = { + {-1.f, -1.f, -1.f}, + {-1.f, -1.f, 1.f}, + {-1.f, 1.f, -1.f}, + {-1.f, 1.f, 1.f}, + { 1.f, 1.f, 1.f}, // x = +1 vertices (put {1,1,1} first in case your support breaks ties by first-hit) + { 1.f, 1.f, -1.f}, + { 1.f, -1.f, 1.f}, + { 1.f, -1.f, -1.f} + }; + + const omath::collision::MeshCollider collider_a(mesh, {0.f, 0.f, 0.f}); + const omath::collision::MeshCollider collider_b(mesh, {0.f, 0.f, 0.f}); + + const auto result = omath::collision::GjkAlgorithm<>::is_collide(collider_a, collider_b); + + EXPECT_TRUE(result); } \ No newline at end of file From e1ff9efc9160d702d915adb4c3666dd8abf0b027 Mon Sep 17 00:00:00 2001 From: Orange Date: Sun, 9 Nov 2025 17:02:07 +0300 Subject: [PATCH 14/25] Adds mesh scaling to mesh collider Updates the mesh collider to include a scale parameter, allowing for non-uniform scaling of the collision mesh. This provides more flexibility in defining collision shapes and supports a wider range of scenarios. --- include/omath/collision/mesh_collider.hpp | 8 ++++--- tests/general/unit_test_gjk.cpp | 27 ++++------------------- 2 files changed, 9 insertions(+), 26 deletions(-) diff --git a/include/omath/collision/mesh_collider.hpp b/include/omath/collision/mesh_collider.hpp index e0b4ea3..4dce3ae 100644 --- a/include/omath/collision/mesh_collider.hpp +++ b/include/omath/collision/mesh_collider.hpp @@ -13,20 +13,22 @@ namespace omath::collision { public: using VertexType = Vector3; - MeshCollider(const std::vector>& vertexes, const Vector3 origin) - : m_vertexes(vertexes), m_origin(origin) + MeshCollider(const std::vector& vertexes, const VertexType& origin, const VertexType& scale = {1.f, 1.f, 1.f}) + : m_vertexes(vertexes),m_scale(scale), m_origin(origin) { if (m_vertexes.empty()) throw std::runtime_error("Collider cannot have 0 vertexes"); } std::vector> m_vertexes; + Vector3 m_scale; + Vector3 m_origin; source_engine::ViewAngles m_rotation; [[nodiscard]] source_engine::Mat4X4 to_world() const { - return mat_translation(m_origin) * source_engine::rotation_matrix(m_rotation); + return mat_scale(m_scale) * mat_translation(m_origin) * source_engine::rotation_matrix(m_rotation); } [[nodiscard]] diff --git a/tests/general/unit_test_gjk.cpp b/tests/general/unit_test_gjk.cpp index 8c306cc..977cbe9 100644 --- a/tests/general/unit_test_gjk.cpp +++ b/tests/general/unit_test_gjk.cpp @@ -4,7 +4,7 @@ #include #include -TEST(UnitTestGjk, TestCollisionTrue) +namespace { const std::vector> mesh = { {-1.f, -1.f, -1.f}, @@ -16,6 +16,9 @@ TEST(UnitTestGjk, TestCollisionTrue) { 1.f, -1.f, 1.f}, { 1.f, -1.f, -1.f} }; +} +TEST(UnitTestGjk, TestCollisionTrue) +{ const omath::collision::MeshCollider collider_a(mesh, {0.f, 0.f, 0.f}); const omath::collision::MeshCollider collider_b(mesh, {0.f, 0.5f, 0.f}); @@ -26,17 +29,6 @@ TEST(UnitTestGjk, TestCollisionTrue) } TEST(UnitTestGjk, TestCollisionFalse) { - const std::vector> mesh = { - {-1.f, -1.f, -1.f}, - {-1.f, -1.f, 1.f}, - {-1.f, 1.f, -1.f}, - {-1.f, 1.f, 1.f}, - { 1.f, 1.f, 1.f}, // x = +1 vertices (put {1,1,1} first in case your support breaks ties by first-hit) - { 1.f, 1.f, -1.f}, - { 1.f, -1.f, 1.f}, - { 1.f, -1.f, -1.f} - }; - const omath::collision::MeshCollider collider_a(mesh, {0.f, 0.f, 0.f}); const omath::collision::MeshCollider collider_b(mesh, {0.f, 2.1f, 0.f}); @@ -47,17 +39,6 @@ TEST(UnitTestGjk, TestCollisionFalse) TEST(UnitTestGjk, TestCollisionEqualOrigin) { - const std::vector> mesh = { - {-1.f, -1.f, -1.f}, - {-1.f, -1.f, 1.f}, - {-1.f, 1.f, -1.f}, - {-1.f, 1.f, 1.f}, - { 1.f, 1.f, 1.f}, // x = +1 vertices (put {1,1,1} first in case your support breaks ties by first-hit) - { 1.f, 1.f, -1.f}, - { 1.f, -1.f, 1.f}, - { 1.f, -1.f, -1.f} - }; - const omath::collision::MeshCollider collider_a(mesh, {0.f, 0.f, 0.f}); const omath::collision::MeshCollider collider_b(mesh, {0.f, 0.f, 0.f}); From 7f62cb6db35132f22f245f5e1a969e80f5720278 Mon Sep 17 00:00:00 2001 From: Orange Date: Sun, 9 Nov 2025 17:08:57 +0300 Subject: [PATCH 15/25] Auto stash before checking out "origin/feature/gjk_algorithm" --- include/omath/rev_eng/internal_rev_object.hpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/include/omath/rev_eng/internal_rev_object.hpp b/include/omath/rev_eng/internal_rev_object.hpp index 71fda4a..56f205a 100644 --- a/include/omath/rev_eng/internal_rev_object.hpp +++ b/include/omath/rev_eng/internal_rev_object.hpp @@ -23,27 +23,26 @@ namespace omath::rev_eng return *reinterpret_cast(reinterpret_cast(this) + offset); } - template - ReturnType call_virtual_method(Args&&... arg_list) + template + ReturnType call_virtual_method(auto... arg_list) { #ifdef _MSC_VER using VirtualMethodType = ReturnType(__thiscall*)(void*, decltype(arg_list)...); #else using VirtualMethodType = ReturnType (*)(void*, decltype(arg_list)...); #endif - return (*reinterpret_cast(this))[id](this, std::forward(arg_list)...); + return (*reinterpret_cast(this))[id](this, arg_list...); } - template - ReturnType call_virtual_method(Args&&... arg_list) const + template + ReturnType call_virtual_method(auto... arg_list) const { - using This = std::remove_cv_t>; #ifdef _MSC_VER - using VirtualMethodType = ReturnType(__thiscall*)(const void*, decltype(arg_list)...); + using VirtualMethodType = ReturnType(__thiscall*)(void*, decltype(arg_list)...); #else using VirtualMethodType = ReturnType (*)(void*, decltype(arg_list)...); #endif - return (*reinterpret_cast(const_cast(this)))[id]( - const_cast(static_cast(this)), std::forward(arg_list)...); + return (*static_cast((void*)(this)))[id]( + const_cast(static_cast(this)), arg_list...); } }; } // namespace omath::rev_eng From 14296e45c4b0dc157571ede86b5cff71117d486b Mon Sep 17 00:00:00 2001 From: Orange Date: Sun, 9 Nov 2025 17:09:46 +0300 Subject: [PATCH 16/25] fix --- tests/general/unit_test_gjk.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/general/unit_test_gjk.cpp b/tests/general/unit_test_gjk.cpp index 977cbe9..f19b57e 100644 --- a/tests/general/unit_test_gjk.cpp +++ b/tests/general/unit_test_gjk.cpp @@ -19,7 +19,6 @@ namespace } TEST(UnitTestGjk, TestCollisionTrue) { - const omath::collision::MeshCollider collider_a(mesh, {0.f, 0.f, 0.f}); const omath::collision::MeshCollider collider_b(mesh, {0.f, 0.5f, 0.f}); From a9d60675f301bba47a49016f40dc003ad382378d Mon Sep 17 00:00:00 2001 From: Orange Date: Sun, 9 Nov 2025 17:15:01 +0300 Subject: [PATCH 17/25] Corrects transform order for collider MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reorders the transform application to translation, scale, then rotation. This ensures the collider’s world matrix is constructed correctly, preventing potential scaling or rotation issues in the GJK algorithm being developed on this branch. --- include/omath/collision/mesh_collider.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/omath/collision/mesh_collider.hpp b/include/omath/collision/mesh_collider.hpp index 4dce3ae..c67ab44 100644 --- a/include/omath/collision/mesh_collider.hpp +++ b/include/omath/collision/mesh_collider.hpp @@ -28,7 +28,7 @@ namespace omath::collision [[nodiscard]] source_engine::Mat4X4 to_world() const { - return mat_scale(m_scale) * mat_translation(m_origin) * source_engine::rotation_matrix(m_rotation); + return mat_translation(m_origin) * mat_scale(m_scale) * source_engine::rotation_matrix(m_rotation); } [[nodiscard]] From 71f59406a9a0f6eba71bd95f8b892640dd7599ee Mon Sep 17 00:00:00 2001 From: Orange Date: Sun, 9 Nov 2025 17:29:50 +0300 Subject: [PATCH 18/25] Refactors Simplex class for GJK algorithm Simplifies Simplex initialization and accessors. Ensures correct handling of collinearity within the simplex calculation, preventing issues when colliders share the same origin. This improves stability and reliability of the GJK algorithm. --- include/omath/collision/simplex.hpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/include/omath/collision/simplex.hpp b/include/omath/collision/simplex.hpp index 5400afc..9ff5c4f 100644 --- a/include/omath/collision/simplex.hpp +++ b/include/omath/collision/simplex.hpp @@ -20,7 +20,7 @@ namespace omath::collision template> class Simplex final { - std::array m_points{}; // value-initialized + std::array m_points{}; std::size_t m_size{0}; public: @@ -28,7 +28,6 @@ namespace omath::collision constexpr Simplex() = default; - // Keep your convenient "{a, b, c}" assignments, but guard size. constexpr Simplex& operator=(std::initializer_list list) noexcept { assert(list.size() <= capacity && "Simplex can have at most 4 points"); @@ -38,7 +37,6 @@ namespace omath::collision return *this; } - // Safe push_front: only shifts the valid range; no reads from uninitialized slots. constexpr void push_front(const VectorType& p) noexcept { const std::size_t limit = (m_size < capacity) ? m_size : capacity - 1; @@ -49,7 +47,6 @@ namespace omath::collision ++m_size; } - // Accessors constexpr const VectorType& operator[](std::size_t i) const noexcept { return m_points[i]; @@ -129,7 +126,7 @@ namespace omath::collision } template - static constexpr bool near_zero(const V& v, float eps = 1e-7f) + static constexpr bool near_zero(const V& v, const float eps = 1e-7f) { return v.dot(v) <= eps * eps; } @@ -156,7 +153,8 @@ namespace omath::collision if (ab.point_to_same_direction(ao)) { - auto n = ab.cross(ao); + // ReSharper disable once CppTooWideScopeInitStatement + auto n = ab.cross(ao); // Needed to valid handle collision if colliders placed at same origin pos if (near_zero(n)) { // collinear: origin lies on ray AB (often on segment), pick any perp to escape From 4fe001b1021c161094f84469b0b645f22e880863 Mon Sep 17 00:00:00 2001 From: Orange Date: Sun, 9 Nov 2025 21:28:00 +0300 Subject: [PATCH 19/25] add mesh class --- .idea/editor.xml | 4 +- include/omath/3d_primitives/mesh.hpp | 97 +++++++++++++++++++++++ include/omath/collision/gjk_algorithm.hpp | 10 +-- include/omath/collision/mesh_collider.hpp | 12 ++- 4 files changed, 112 insertions(+), 11 deletions(-) create mode 100644 include/omath/3d_primitives/mesh.hpp diff --git a/.idea/editor.xml b/.idea/editor.xml index 373c50f..fde5348 100644 --- a/.idea/editor.xml +++ b/.idea/editor.xml @@ -201,7 +201,7 @@