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 @@
-
+
@@ -215,7 +215,7 @@
-
+
diff --git a/include/omath/3d_primitives/mesh.hpp b/include/omath/3d_primitives/mesh.hpp
new file mode 100644
index 00000000..cc09a5b5
--- /dev/null
+++ b/include/omath/3d_primitives/mesh.hpp
@@ -0,0 +1,102 @@
+//
+// Created by Vladislav on 09.11.2025.
+//
+#pragma once
+#include "omath/linear_algebra/triangle.hpp"
+#include
+#include
+#include
+#include
+
+namespace omath::primitives
+{
+ template
+ class Mesh final
+ {
+ public:
+ using NumericType = Type;
+
+ private:
+ using Vbo = std::vector>;
+ using Vao = std::vector>;
+
+ public:
+ Vbo m_vertex_buffer;
+ Vao m_vertex_array_object;
+
+ Mesh(Vbo vbo, Vao vao, const Vector3 scale = {1, 1, 1,})
+ : m_vertex_buffer(std::move(vbo)), m_vertex_array_object(std::move(vao)), m_scale(scale)
+ {
+ }
+ void set_origin(const Vector3& new_origin)
+ {
+ m_origin = new_origin;
+ m_to_world_matrix = std::nullopt;
+ }
+
+ void set_scale(const Vector3& new_scale)
+ {
+ m_scale = new_scale;
+ m_to_world_matrix = std::nullopt;
+ }
+
+ void set_rotation(const RotationAngles& new_rotation_angles)
+ {
+ m_rotation_angles = new_rotation_angles;
+ m_to_world_matrix = std::nullopt;
+ }
+
+ [[nodiscard]]
+ const Vector3& get_origin() const
+ {
+ return m_origin;
+ }
+
+ [[nodiscard]]
+ const Vector3& get_scale() const
+ {
+ return m_scale;
+ }
+
+ [[nodiscard]]
+ const RotationAngles& get_rotation_angles() const
+ {
+ return m_rotation_angles;
+ }
+
+ [[nodiscard]]
+ const Mat4X4& get_to_world_matrix() const
+ {
+ if (m_to_world_matrix)
+ return m_to_world_matrix.value();
+ m_to_world_matrix =
+ mat_translation(m_origin) * mat_scale(m_scale) * MeshTypeTrait::rotation_matrix(m_rotation_angles);
+
+ return m_to_world_matrix.value();
+ }
+
+ [[nodiscard]]
+ Vector3 vertex_to_world_space(const Vector3& vertex) const
+ {
+ auto abs_vec = get_to_world_matrix() * mat_column_from_vector(vertex);
+
+ return {abs_vec.at(0, 0), abs_vec.at(1, 0), abs_vec.at(2, 0)};
+ }
+
+ [[nodiscard]]
+ Triangle> make_face_in_world_space(const Vao::const_iterator vao_iterator) const
+ {
+ return {vertex_to_world_space(m_vertex_buffer.at(vao_iterator->x)),
+ vertex_to_world_space(m_vertex_buffer.at(vao_iterator->y)),
+ vertex_to_world_space(m_vertex_buffer.at(vao_iterator->z))};
+ }
+
+ private:
+ Vector3 m_origin;
+ Vector3 m_scale;
+
+ RotationAngles m_rotation_angles;
+
+ mutable std::optional m_to_world_matrix;
+ };
+} // namespace omath::primitives
\ No newline at end of file
diff --git a/include/omath/collision/gjk_algorithm.hpp b/include/omath/collision/gjk_algorithm.hpp
new file mode 100644
index 00000000..886e4d28
--- /dev/null
+++ b/include/omath/collision/gjk_algorithm.hpp
@@ -0,0 +1,49 @@
+//
+// 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
+{
+ template
+ class GjkAlgorithm final
+ {
+ public:
+ [[nodiscard]]
+ static ColliderType::VertexType find_support_vertex(const ColliderType& collider_a,
+ const ColliderType& collider_b,
+ const ColliderType::VertexType& direction)
+ {
+ return collider_a.find_abs_furthest_vertex(direction) - collider_b.find_abs_furthest_vertex(-direction);
+ }
+
+ [[nodiscard]]
+ static bool is_collide(const ColliderType& collider_a, const ColliderType& collider_b)
+ {
+ // Get initial support point in any direction
+ auto support = find_support_vertex(collider_a, collider_b, {1, 0, 0});
+
+ Simplex simplex;
+ simplex.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;
+
+ simplex.push_front(support);
+
+ if (simplex.handle(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..adafab3e
--- /dev/null
+++ b/include/omath/collision/mesh_collider.hpp
@@ -0,0 +1,37 @@
+//
+// Created by Vlad on 11/9/2025.
+//
+
+#pragma once
+#include "omath/linear_algebra/vector3.hpp"
+
+namespace omath::collision
+{
+ template
+ class MeshCollider
+ {
+ public:
+ using NumericType = typename MeshType::NumericType;
+
+ using VertexType = Vector3;
+ explicit MeshCollider(MeshType mesh): m_mesh(std::move(mesh))
+ {
+ }
+
+ [[nodiscard]]
+ const Vector3& find_furthest_vertex(const Vector3& direction) const
+ {
+ return *std::ranges::max_element(m_mesh.m_vertex_buffer, [&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 m_mesh.vertex_to_world_space(find_furthest_vertex(direction));
+ }
+
+ private:
+ MeshType m_mesh;
+ };
+} // 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..9ff5c4fb
--- /dev/null
+++ b/include/omath/collision/simplex.hpp
@@ -0,0 +1,257 @@
+#pragma once
+#include "omath/linear_algebra/vector3.hpp"
+#include
+#include
+#include
+#include
+
+namespace omath::collision
+{
+
+ // 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{0};
+
+ public:
+ static constexpr std::size_t capacity = 4;
+
+ constexpr Simplex() = default;
+
+ constexpr Simplex& operator=(std::initializer_list list) noexcept
+ {
+ assert(list.size() <= capacity && "Simplex can have at most 4 points");
+ m_size = 0;
+ for (const auto& p : list)
+ m_points[m_size++] = p;
+ return *this;
+ }
+
+ constexpr void push_front(const VectorType& p) noexcept
+ {
+ 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[](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 noexcept
+ {
+ return m_size;
+ }
+
+ [[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 noexcept
+ {
+ return m_points.begin() + m_size;
+ }
+
+ constexpr void clear() noexcept
+ {
+ 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:
+ return handle_triangle(direction);
+ case 4:
+ return handle_tetrahedron(direction);
+ default:
+ std::unreachable();
+ }
+ }
+
+ private:
+ [[nodiscard]] constexpr bool handle_point(VectorType& direction) noexcept
+ {
+ const auto& a = m_points[0];
+ direction = -a;
+ return false;
+ }
+
+ template
+ static constexpr bool near_zero(const V& v, const 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];
+
+ const auto ab = b - a;
+ const auto ao = -a;
+
+ if (ab.point_to_same_direction(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
+ direction = any_perp(ab);
+ }
+ else
+ {
+ direction = n.cross(ab);
+ }
+ }
+ else
+ {
+ *this = {a};
+ direction = ao;
+ }
+ return false;
+ }
+
+ [[nodiscard]] constexpr bool handle_triangle(VectorType& direction) noexcept
+ {
+ 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);
+
+ // 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}; // flip winding
+ direction = -abc;
+ }
+ return false;
+ }
+
+ [[nodiscard]] constexpr bool handle_tetrahedron(VectorType& direction) noexcept
+ {
+ 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);
+ }
+ // Origin inside tetrahedron
+ return true;
+ }
+ };
+
+} // namespace omath::collision
diff --git a/include/omath/engines/frostbite_engine/mesh.hpp b/include/omath/engines/frostbite_engine/mesh.hpp
new file mode 100644
index 00000000..a6dda9f3
--- /dev/null
+++ b/include/omath/engines/frostbite_engine/mesh.hpp
@@ -0,0 +1,12 @@
+//
+// Created by Vladislav on 09.11.2025.
+//
+#pragma once
+#include "constants.hpp"
+#include "omath/3d_primitives/mesh.hpp"
+#include "traits/mesh_trait.hpp"
+
+namespace omath::frostbite_engine
+{
+ using Mesh = primitives::Mesh;
+}
\ No newline at end of file
diff --git a/include/omath/engines/frostbite_engine/traits/mesh_trait.hpp b/include/omath/engines/frostbite_engine/traits/mesh_trait.hpp
new file mode 100644
index 00000000..e113c4ea
--- /dev/null
+++ b/include/omath/engines/frostbite_engine/traits/mesh_trait.hpp
@@ -0,0 +1,19 @@
+//
+// Created by Vladislav on 09.11.2025.
+//
+#pragma once
+#include
+#include
+
+namespace omath::frostbite_engine
+{
+ class MeshTrait final
+ {
+ public:
+ [[nodiscard]]
+ static Mat4X4 rotation_matrix(const ViewAngles& rotation)
+ {
+ return frostbite_engine::rotation_matrix(rotation);
+ }
+ };
+} // namespace omath::frostbite_engine
\ No newline at end of file
diff --git a/include/omath/engines/iw_engine/mesh.hpp b/include/omath/engines/iw_engine/mesh.hpp
new file mode 100644
index 00000000..1556b192
--- /dev/null
+++ b/include/omath/engines/iw_engine/mesh.hpp
@@ -0,0 +1,12 @@
+//
+// Created by Vladislav on 09.11.2025.
+//
+#pragma once
+#include "constants.hpp"
+#include "omath/3d_primitives/mesh.hpp"
+#include "traits/mesh_trait.hpp"
+
+namespace omath::iw_engine
+{
+ using Mesh = primitives::Mesh;
+}
\ No newline at end of file
diff --git a/include/omath/engines/iw_engine/traits/mesh_trait.hpp b/include/omath/engines/iw_engine/traits/mesh_trait.hpp
new file mode 100644
index 00000000..4cbc5c95
--- /dev/null
+++ b/include/omath/engines/iw_engine/traits/mesh_trait.hpp
@@ -0,0 +1,19 @@
+//
+// Created by Vladislav on 09.11.2025.
+//
+#pragma once
+#include
+#include
+
+namespace omath::iw_engine
+{
+ class MeshTrait final
+ {
+ public:
+ [[nodiscard]]
+ static Mat4X4 rotation_matrix(const ViewAngles& rotation)
+ {
+ return iw_engine::rotation_matrix(rotation);
+ }
+ };
+} // namespace omath::iw_engine
\ No newline at end of file
diff --git a/include/omath/engines/opengl_engine/mesh.hpp b/include/omath/engines/opengl_engine/mesh.hpp
new file mode 100644
index 00000000..d2db7e51
--- /dev/null
+++ b/include/omath/engines/opengl_engine/mesh.hpp
@@ -0,0 +1,12 @@
+//
+// Created by Vladislav on 09.11.2025.
+//
+#pragma once
+#include "constants.hpp"
+#include "omath/3d_primitives/mesh.hpp"
+#include "traits/mesh_trait.hpp"
+
+namespace omath::opengl_engine
+{
+ using Mesh = primitives::Mesh;
+}
\ No newline at end of file
diff --git a/include/omath/engines/opengl_engine/traits/mesh_trait.hpp b/include/omath/engines/opengl_engine/traits/mesh_trait.hpp
new file mode 100644
index 00000000..2de8f8ef
--- /dev/null
+++ b/include/omath/engines/opengl_engine/traits/mesh_trait.hpp
@@ -0,0 +1,19 @@
+//
+// Created by Vladislav on 09.11.2025.
+//
+#pragma once
+#include
+#include
+
+namespace omath::opengl_engine
+{
+ class MeshTrait final
+ {
+ public:
+ [[nodiscard]]
+ static Mat4X4 rotation_matrix(const ViewAngles& rotation)
+ {
+ return opengl_engine::rotation_matrix(rotation);
+ }
+ };
+} // namespace omath::opengl_engine
\ No newline at end of file
diff --git a/include/omath/engines/source_engine/collider.hpp b/include/omath/engines/source_engine/collider.hpp
new file mode 100644
index 00000000..2c8d9b98
--- /dev/null
+++ b/include/omath/engines/source_engine/collider.hpp
@@ -0,0 +1,13 @@
+//
+// Created by Vladislav on 09.11.2025.
+//
+
+#pragma once
+#include "mesh.hpp"
+#include "omath/3d_primitives/mesh.hpp"
+#include "omath/collision/mesh_collider.hpp"
+
+namespace omath::source_engine
+{
+ using MeshCollider = collision::MeshCollider;
+}
\ No newline at end of file
diff --git a/include/omath/engines/source_engine/mesh.hpp b/include/omath/engines/source_engine/mesh.hpp
new file mode 100644
index 00000000..31956b3f
--- /dev/null
+++ b/include/omath/engines/source_engine/mesh.hpp
@@ -0,0 +1,12 @@
+//
+// Created by Vladislav on 09.11.2025.
+//
+#pragma once
+#include "constants.hpp"
+#include "omath/3d_primitives/mesh.hpp"
+#include "traits/mesh_trait.hpp"
+
+namespace omath::source_engine
+{
+ using Mesh = primitives::Mesh;
+}
\ No newline at end of file
diff --git a/include/omath/engines/source_engine/traits/mesh_trait.hpp b/include/omath/engines/source_engine/traits/mesh_trait.hpp
new file mode 100644
index 00000000..2320465f
--- /dev/null
+++ b/include/omath/engines/source_engine/traits/mesh_trait.hpp
@@ -0,0 +1,19 @@
+//
+// Created by Vladislav on 09.11.2025.
+//
+#pragma once
+#include
+#include
+
+namespace omath::source_engine
+{
+ class MeshTrait final
+ {
+ public:
+ [[nodiscard]]
+ static Mat4X4 rotation_matrix(const ViewAngles& rotation)
+ {
+ return source_engine::rotation_matrix(rotation);
+ }
+ };
+} // namespace omath::source_engine
\ No newline at end of file
diff --git a/include/omath/engines/unity_engine/mesh.hpp b/include/omath/engines/unity_engine/mesh.hpp
new file mode 100644
index 00000000..6c78b2f3
--- /dev/null
+++ b/include/omath/engines/unity_engine/mesh.hpp
@@ -0,0 +1,12 @@
+//
+// Created by Vladislav on 09.11.2025.
+//
+#pragma once
+#include "constants.hpp"
+#include "omath/3d_primitives/mesh.hpp"
+#include "traits/mesh_trait.hpp"
+
+namespace omath::unity_engine
+{
+ using Mesh = primitives::Mesh;
+}
\ No newline at end of file
diff --git a/include/omath/engines/unity_engine/traits/mesh_trait.hpp b/include/omath/engines/unity_engine/traits/mesh_trait.hpp
new file mode 100644
index 00000000..56278202
--- /dev/null
+++ b/include/omath/engines/unity_engine/traits/mesh_trait.hpp
@@ -0,0 +1,19 @@
+//
+// Created by Vladislav on 09.11.2025.
+//
+#pragma once
+#include
+#include
+
+namespace omath::unity_engine
+{
+ class MeshTrait final
+ {
+ public:
+ [[nodiscard]]
+ static Mat4X4 rotation_matrix(const ViewAngles& rotation)
+ {
+ return unity_engine::rotation_matrix(rotation);
+ }
+ };
+} // namespace omath::unity_engine
\ No newline at end of file
diff --git a/include/omath/engines/unreal_engine/mesh.hpp b/include/omath/engines/unreal_engine/mesh.hpp
new file mode 100644
index 00000000..8fb73458
--- /dev/null
+++ b/include/omath/engines/unreal_engine/mesh.hpp
@@ -0,0 +1,12 @@
+//
+// Created by Vladislav on 09.11.2025.
+//
+#pragma once
+#include "constants.hpp"
+#include "omath/3d_primitives/mesh.hpp"
+#include "traits/mesh_trait.hpp"
+
+namespace omath::unreal_engine
+{
+ using Mesh = primitives::Mesh;
+}
\ No newline at end of file
diff --git a/include/omath/engines/unreal_engine/traits/mesh_trait.hpp b/include/omath/engines/unreal_engine/traits/mesh_trait.hpp
new file mode 100644
index 00000000..5da0a2d6
--- /dev/null
+++ b/include/omath/engines/unreal_engine/traits/mesh_trait.hpp
@@ -0,0 +1,19 @@
+//
+// Created by Vladislav on 09.11.2025.
+//
+#pragma once
+#include
+#include
+
+namespace omath::unreal_engine
+{
+ class MeshTrait final
+ {
+ public:
+ [[nodiscard]]
+ static Mat4X4 rotation_matrix(const ViewAngles& rotation)
+ {
+ return unreal_engine::rotation_matrix(rotation);
+ }
+ };
+} // namespace omath::unreal_engine
\ No newline at end of file
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]]
diff --git a/include/omath/linear_algebra/vector3.hpp b/include/omath/linear_algebra/vector3.hpp
index 77d60cfb..4b1d3817 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) const
+ {
+ return dot(other) > static_cast(0);
+ }
[[nodiscard]] std::expected, Vector3Error>
angle_between(const Vector3& other) const noexcept
{
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
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{};
diff --git a/tests/general/unit_test_colider.cpp b/tests/general/unit_test_colider.cpp
new file mode 100644
index 00000000..57d8b087
--- /dev/null
+++ b/tests/general/unit_test_colider.cpp
@@ -0,0 +1,28 @@
+//
+// Created by Vlad on 11/9/2025.
+//
+#include "omath/engines/source_engine/collider.hpp"
+#include
+#include
+
+TEST(UnitTestColider, CheckToWorld)
+{
+ omath::source_engine::Mesh mesh = {std::vector>{{1.f, 1.f, 1.f}, {-1.f, -1.f, -1.f}}, {}};
+ mesh.set_origin({0, 2, 0});
+ const omath::source_engine::MeshCollider collider(mesh);
+
+ 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 omath::source_engine::Mesh mesh = {{{1.f, 1.f, 1.f}, {-1.f, -1.f, -1.f}}, {}};
+ const omath::source_engine::MeshCollider collider(mesh);
+ 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..ea29f781
--- /dev/null
+++ b/tests/general/unit_test_gjk.cpp
@@ -0,0 +1,58 @@
+//
+// Created by Vlad on 11/9/2025.
+//
+#include "omath/engines/source_engine/collider.hpp"
+#include
+#include
+#include
+namespace
+{
+ const omath::source_engine::Mesh 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},
+ {1.f, 1.f, -1.f},
+ {1.f, -1.f, 1.f},
+ {1.f, -1.f, -1.f}},
+ {}};
+}
+TEST(UnitTestGjk, TestCollisionTrue)
+{
+ const omath::source_engine::MeshCollider collider_a(mesh);
+
+ auto mesh_b = mesh;
+ mesh_b.set_origin({0.f, 0.5f, 0.f});
+
+ const omath::source_engine::MeshCollider collider_b(mesh_b);
+
+ using GjkAlgorithm = omath::collision::GjkAlgorithm;
+
+ const auto result = GjkAlgorithm::is_collide(collider_a, collider_b);
+
+ EXPECT_TRUE(result);
+}
+TEST(UnitTestGjk, TestCollisionFalse)
+{
+ const omath::source_engine::MeshCollider collider_a(mesh);
+ auto mesh_b = mesh;
+ mesh_b.set_origin({0.f, 2.1f, 0.f});
+ const omath::source_engine::MeshCollider collider_b(mesh_b);
+
+ const auto result =
+ omath::collision::GjkAlgorithm::is_collide(collider_a, collider_b);
+
+ EXPECT_FALSE(result);
+}
+
+TEST(UnitTestGjk, TestCollisionEqualOrigin)
+{
+ const omath::source_engine::MeshCollider collider_a(mesh);
+ const omath::source_engine::MeshCollider collider_b(mesh);
+
+ const auto result =
+ omath::collision::GjkAlgorithm::is_collide(collider_a, collider_b);
+
+ EXPECT_TRUE(result);
+}
\ No newline at end of file