From 338246a6187e7c9673c89e42286cd07bc72b2732 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 00000000..2aae2a18 --- /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 00000000..354ce52e --- /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 00000000..23c9b253 --- /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 77d60cfb..5432bb08 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 00000000..97f0eff0 --- /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 00000000..9ec5f42c --- /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 31cc1116ae73a4817b4e92b233b09be2dd2b4be7 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 23c9b253..20ca8def 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 1e540862a0e5fdf4c72e9369d1d31e299aa3c421 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 | 32 ++++++++++++------------ include/omath/linear_algebra/vector3.hpp | 2 +- include/omath/utility/color.hpp | 2 +- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/include/omath/collision/simplex.hpp b/include/omath/collision/simplex.hpp index 20ca8def..46d7e1e6 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]; - - Vector3 ab = b - a; - Vector3 ac = c - a; - Vector3 ad = d - a; - Vector3 ao = -a; + const auto& a = simplex[0]; + const auto& b = simplex[1]; + const auto& c = simplex[2]; + const auto& d = simplex[3]; + + 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 5432bb08..4b1d3817 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 cfacee80..fd734514 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 6e59957247c8ae2b8c7db22b952c649cd2d3ad4c 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 75c4848a..18b7d710 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 e7b82f441c17938171fe0affe68f45d34a26fdaf 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 354ce52e..f422a3ef 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 ebfdd0b70e8749efbcf361597125b5959bd62eff 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 46d7e1e6..703afb46 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 cd18b088cb7a9570afb90dcb86afa067142e0de6 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 2aae2a18..cb8645ac 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 9ec5f42c..45259d41 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 043b5c588d519cd00522f6bf2b11030b3b1b324e 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 | 210 ++++++++++++---------- 2 files changed, 117 insertions(+), 101 deletions(-) diff --git a/include/omath/collision/gjk_algorithm.hpp b/include/omath/collision/gjk_algorithm.hpp index cb8645ac..b2c7a485 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 703afb46..e59c1321 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; + switch (m_points.size()) + { + case 2: + return handle_line(direction); + case 3: + return handle_triangle(direction); + case 4: + return handle_tetrahedron(direction); + default: + std::unreachable(); + } } + private: + [[nodiscard]] + constexpr bool handle_line(VectorType& direction) + { + const auto& a = m_points[0]; + const auto& b = m_points[1]; - 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); + const auto ab = b - a; + // ReSharper disable once CppTooWideScopeInitStatement + const auto ao = -a; - if (abc.cross(ac).point_to_same_direction(ao)) - { - if (ac.point_to_same_direction(ao)) + if (ab.point_to_same_direction(ao)) + direction = ab.cross(ao).cross(ab); + else { - points = {a, c}; - direction = ac.cross(ao).cross(ac); - - return false; + *this = {a}; + direction = ao; } - 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; + return false; } - else + [[nodiscard]] + constexpr bool handle_triangle(VectorType& direction) { - points = {a, c, b}; - direction = -abc; - } + const auto& a = m_points[0]; + const auto& b = m_points[1]; + const auto& c = m_points[2]; - return false; - } + const auto ab = b - a; + const auto ac = c - a; + 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]; + const auto abc = ab.cross(ac); - const Vector3 ab = b - a; - const Vector3 ac = c - a; - const Vector3 ad = d - a; - const Vector3 ao = -a; + 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); + } - const Vector3 abc = ab.cross(ac); - const Vector3 acd = ac.cross(ad); - const Vector3 adb = ad.cross(ab); + 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; + } - if (abc.point_to_same_direction(ao)) - return handle_triangle(simplex = {a, b, c}, direction); + 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]; - if (acd.point_to_same_direction(ao)) - return handle_triangle(simplex = {a, c, d}, direction); + const auto ab = b - a; + const auto ac = c - a; + const auto ad = d - a; + const auto ao = -a; - if (adb.point_to_same_direction(ao)) - return handle_triangle(simplex = {a, d, b}, direction); + const auto abc = ab.cross(ac); + const auto acd = ac.cross(ad); + const auto adb = ad.cross(ab); - return true; - } + if (abc.point_to_same_direction(ao)) + { + *this = {a, b, c}; + return handle_triangle(direction); + } - [[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; + 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 62d1a615ae6e89139fee7cd8266b6d37325c39f7 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 e59c1321..798e1f0f 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 015fc9b1e762062a207ad32ed566868b2b2b3852 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 798e1f0f..e585508d 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 afc0720f0895ddefc40ea1336f4a4df194e73a97 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 b2c7a485..d494cb1d 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 f422a3ef..e0b4ea3b 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 e585508d..140c1933 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]; + } + constexpr VectorType& operator[](std::size_t i) noexcept { return m_points[i]; } - [[nodiscard]] - constexpr std::size_t size() const + + [[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.begin() + m_size; + } + + constexpr void clear() noexcept { - return m_points.end() - (4 - m_size); + m_size = 0; } - [[nodiscard]] - constexpr bool handle(VectorType& direction) + + // GJK step: updates simplex + next search direction. + // Returns true iff the origin lies inside the tetrahedron. + [[nodiscard]] constexpr bool handle(VectorType& direction) noexcept { - switch (size()) + 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 45259d41..bd0fdac9 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 0b663b73d53817dba8993f2d4c9b1992cc7f5d4a 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 140c1933..5400afc4 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 7b0e2127dce775d376f5df7cc00a15c0cb5d132b 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 bd0fdac9..8c306cc6 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 b2a512eafe1aea429e7786b056dc1aafd5a3407f 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 e0b4ea3b..4dce3ae8 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 8c306cc6..977cbe9c 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 c4024663bbd5dbb0d788df03883523ea55e71916 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 71fda4a1..56f205a9 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 79482d56d1962601be9b6a5cb82c7b69f157efd0 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 977cbe9c..f19b57e5 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 ca3dab855be9614952ff5881ff80fc613802b773 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 4dce3ae8..c67ab441 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 e2378bfa8b6a0ef98de3d8e2148a3ad35bfa803c 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 5400afc4..9ff5c4fb 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 e5d8e1c95381b120a69fd2fa96394b3d9eb21c55 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 373c50fc..fde53486 100644 --- a/.idea/editor.xml +++ b/.idea/editor.xml @@ -201,7 +201,7 @@