Merge pull request #102 from orange-cpp/feature/gjk_algorithm

Feature/gjk algorithm
This commit is contained in:
2025-11-09 23:27:03 +03:00
committed by GitHub
24 changed files with 757 additions and 12 deletions

4
.idea/editor.xml generated
View File

@@ -201,7 +201,7 @@
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStaticDataMemberInUnnamedStruct/@EntryIndexedValue" value="WARNING" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStaticDataMemberInUnnamedStruct/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStaticSpecifierOnAnonymousNamespaceMember/@EntryIndexedValue" value="SUGGESTION" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStaticSpecifierOnAnonymousNamespaceMember/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStringLiteralToCharPointerConversion/@EntryIndexedValue" value="WARNING" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStringLiteralToCharPointerConversion/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTabsAreDisallowed/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTabsAreDisallowed/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTemplateArgumentsCanBeDeduced/@EntryIndexedValue" value="HINT" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTemplateArgumentsCanBeDeduced/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTemplateParameterNeverUsed/@EntryIndexedValue" value="HINT" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTemplateParameterNeverUsed/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTemplateParameterShadowing/@EntryIndexedValue" value="WARNING" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTemplateParameterShadowing/@EntryIndexedValue" value="WARNING" type="string" />
@@ -215,7 +215,7 @@
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnmatchedPragmaEndRegionDirective/@EntryIndexedValue" value="WARNING" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnmatchedPragmaEndRegionDirective/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnmatchedPragmaRegionDirective/@EntryIndexedValue" value="WARNING" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnmatchedPragmaRegionDirective/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnnamedNamespaceInHeaderFile/@EntryIndexedValue" value="WARNING" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnnamedNamespaceInHeaderFile/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnnecessaryWhitespace/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnnecessaryWhitespace/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnsignedZeroComparison/@EntryIndexedValue" value="WARNING" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnsignedZeroComparison/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnusedIncludeDirective/@EntryIndexedValue" value="WARNING" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnusedIncludeDirective/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseAlgorithmWithCount/@EntryIndexedValue" value="SUGGESTION" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseAlgorithmWithCount/@EntryIndexedValue" value="SUGGESTION" type="string" />

View File

@@ -0,0 +1,102 @@
//
// Created by Vladislav on 09.11.2025.
//
#pragma once
#include "omath/linear_algebra/triangle.hpp"
#include <omath/linear_algebra/mat.hpp>
#include <omath/linear_algebra/vector3.hpp>
#include <utility>
#include <vector>
namespace omath::primitives
{
template<class Mat4X4, class RotationAngles, class MeshTypeTrait, class Type = float>
class Mesh final
{
public:
using NumericType = Type;
private:
using Vbo = std::vector<Vector3<NumericType>>;
using Vao = std::vector<Vector3<std::size_t>>;
public:
Vbo m_vertex_buffer;
Vao m_vertex_array_object;
Mesh(Vbo vbo, Vao vao, const Vector3<NumericType> 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<NumericType>& new_origin)
{
m_origin = new_origin;
m_to_world_matrix = std::nullopt;
}
void set_scale(const Vector3<NumericType>& 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<NumericType>& get_origin() const
{
return m_origin;
}
[[nodiscard]]
const Vector3<NumericType>& 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<float> vertex_to_world_space(const Vector3<float>& 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<Vector3<float>> 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<NumericType> m_origin;
Vector3<NumericType> m_scale;
RotationAngles m_rotation_angles;
mutable std::optional<Mat4X4> m_to_world_matrix;
};
} // namespace omath::primitives

View File

@@ -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 ColliderType>
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<typename ColliderType::VertexType> 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

View File

@@ -0,0 +1,37 @@
//
// Created by Vlad on 11/9/2025.
//
#pragma once
#include "omath/linear_algebra/vector3.hpp"
namespace omath::collision
{
template<class MeshType>
class MeshCollider
{
public:
using NumericType = typename MeshType::NumericType;
using VertexType = Vector3<NumericType>;
explicit MeshCollider(MeshType mesh): m_mesh(std::move(mesh))
{
}
[[nodiscard]]
const Vector3<float>& find_furthest_vertex(const Vector3<float>& 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<float> find_abs_furthest_vertex(const Vector3<float>& direction) const
{
return m_mesh.vertex_to_world_space(find_furthest_vertex(direction));
}
private:
MeshType m_mesh;
};
} // namespace omath::collision

View File

@@ -0,0 +1,257 @@
#pragma once
#include "omath/linear_algebra/vector3.hpp"
#include <array>
#include <cassert>
#include <initializer_list>
#include <type_traits>
namespace omath::collision
{
// Minimal structural contract for the vector type used by GJK.
template<class V>
concept GjkVector = requires(const V& a, const V& b) {
{ -a } -> std::same_as<V>;
{ a - b } -> std::same_as<V>;
{ a.cross(b) } -> std::same_as<V>;
{ a.point_to_same_direction(b) } -> std::same_as<bool>;
};
template<GjkVector VectorType = Vector3<float>>
class Simplex final
{
std::array<VectorType, 4> 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<VectorType> 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<class V>
static constexpr bool near_zero(const V& v, const float eps = 1e-7f)
{
return v.dot(v) <= eps * eps;
}
template<class V>
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

View File

@@ -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<Mat4X4, ViewAngles, MeshTrait, float>;
}

View File

@@ -0,0 +1,19 @@
//
// Created by Vladislav on 09.11.2025.
//
#pragma once
#include <omath/engines/frostbite_engine/constants.hpp>
#include <omath/engines/frostbite_engine/formulas.hpp>
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

View File

@@ -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<Mat4X4, ViewAngles, MeshTrait, float>;
}

View File

@@ -0,0 +1,19 @@
//
// Created by Vladislav on 09.11.2025.
//
#pragma once
#include <omath/engines/iw_engine/constants.hpp>
#include <omath/engines/iw_engine/formulas.hpp>
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

View File

@@ -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<Mat4X4, ViewAngles, MeshTrait, float>;
}

View File

@@ -0,0 +1,19 @@
//
// Created by Vladislav on 09.11.2025.
//
#pragma once
#include <omath/engines/opengl_engine/constants.hpp>
#include <omath/engines/opengl_engine/formulas.hpp>
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

View File

@@ -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<Mesh>;
}

View File

@@ -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<Mat4X4, ViewAngles, MeshTrait, float>;
}

View File

@@ -0,0 +1,19 @@
//
// Created by Vladislav on 09.11.2025.
//
#pragma once
#include <omath/engines/source_engine/constants.hpp>
#include <omath/engines/source_engine/formulas.hpp>
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

View File

@@ -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<Mat4X4, ViewAngles, MeshTrait, float>;
}

View File

@@ -0,0 +1,19 @@
//
// Created by Vladislav on 09.11.2025.
//
#pragma once
#include <omath/engines/unity_engine/constants.hpp>
#include <omath/engines/unity_engine/formulas.hpp>
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

View File

@@ -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<Mat4X4, ViewAngles, MeshTrait, float>;
}

View File

@@ -0,0 +1,19 @@
//
// Created by Vladislav on 09.11.2025.
//
#pragma once
#include <omath/engines/unreal_engine/constants.hpp>
#include <omath/engines/unreal_engine/formulas.hpp>
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

View File

@@ -586,6 +586,17 @@ namespace omath
{0, 0, 0, 1}, {0, 0, 0, 1},
}; };
} }
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR>
[[nodiscard]]
constexpr Mat<4, 4, Type, St> mat_scale(const Vector3<Type>& scale) noexcept
{
return {
{scale.x, 0, 0, 0},
{0, scale.y, 0, 0},
{0, 0, scale.z, 0},
{0, 0, 0, 1},
};
}
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR, class Angle> template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR, class Angle>
[[nodiscard]] [[nodiscard]]

View File

@@ -216,6 +216,11 @@ namespace omath
return sum_2d() + z; return sum_2d() + z;
} }
[[nodiscard]]
bool point_to_same_direction(const Vector3& other) const
{
return dot(other) > static_cast<Type>(0);
}
[[nodiscard]] std::expected<Angle<float, 0.f, 180.f, AngleFlags::Clamped>, Vector3Error> [[nodiscard]] std::expected<Angle<float, 0.f, 180.f, AngleFlags::Clamped>, Vector3Error>
angle_between(const Vector3& other) const noexcept angle_between(const Vector3& other) const noexcept
{ {

View File

@@ -23,27 +23,26 @@ namespace omath::rev_eng
return *reinterpret_cast<Type*>(reinterpret_cast<std::uintptr_t>(this) + offset); return *reinterpret_cast<Type*>(reinterpret_cast<std::uintptr_t>(this) + offset);
} }
template<std::size_t id, class ReturnType, class... Args> template<std::size_t id, class ReturnType>
ReturnType call_virtual_method(Args&&... arg_list) ReturnType call_virtual_method(auto... arg_list)
{ {
#ifdef _MSC_VER #ifdef _MSC_VER
using VirtualMethodType = ReturnType(__thiscall*)(void*, decltype(arg_list)...); using VirtualMethodType = ReturnType(__thiscall*)(void*, decltype(arg_list)...);
#else #else
using VirtualMethodType = ReturnType (*)(void*, decltype(arg_list)...); using VirtualMethodType = ReturnType (*)(void*, decltype(arg_list)...);
#endif #endif
return (*reinterpret_cast<VirtualMethodType**>(this))[id](this, std::forward<Args>(arg_list)...); return (*reinterpret_cast<VirtualMethodType**>(this))[id](this, arg_list...);
} }
template<std::size_t id, class ReturnType, class... Args> template<std::size_t id, class ReturnType>
ReturnType call_virtual_method(Args&&... arg_list) const ReturnType call_virtual_method(auto... arg_list) const
{ {
using This = std::remove_cv_t<std::remove_pointer_t<decltype(this)>>;
#ifdef _MSC_VER #ifdef _MSC_VER
using VirtualMethodType = ReturnType(__thiscall*)(const void*, decltype(arg_list)...); using VirtualMethodType = ReturnType(__thiscall*)(void*, decltype(arg_list)...);
#else #else
using VirtualMethodType = ReturnType (*)(void*, decltype(arg_list)...); using VirtualMethodType = ReturnType (*)(void*, decltype(arg_list)...);
#endif #endif
return (*reinterpret_cast<VirtualMethodType**>(const_cast<This*>(this)))[id]( return (*static_cast<VirtualMethodType**>((void*)(this)))[id](
const_cast<void*>(static_cast<const void*>(this)), std::forward<Args>(arg_list)...); const_cast<void*>(static_cast<const void*>(this)), arg_list...);
} }
}; };
} // namespace omath::rev_eng } // namespace omath::rev_eng

View File

@@ -9,7 +9,7 @@
namespace omath namespace omath
{ {
struct Hsv struct Hsv final
{ {
float hue{}; float hue{};
float saturation{}; float saturation{};

View File

@@ -0,0 +1,28 @@
//
// Created by Vlad on 11/9/2025.
//
#include "omath/engines/source_engine/collider.hpp"
#include <gtest/gtest.h>
#include <omath/collision/mesh_collider.hpp>
TEST(UnitTestColider, CheckToWorld)
{
omath::source_engine::Mesh mesh = {std::vector<omath::Vector3<float>>{{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<float>(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<float>(1.f, 1.f, 1.f));
}

View File

@@ -0,0 +1,58 @@
//
// Created by Vlad on 11/9/2025.
//
#include "omath/engines/source_engine/collider.hpp"
#include <gtest/gtest.h>
#include <omath/collision/gjk_algorithm.hpp>
#include <omath/engines/source_engine/mesh.hpp>
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<omath::source_engine::MeshCollider>;
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<omath::source_engine::MeshCollider>::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<omath::source_engine::MeshCollider>::is_collide(collider_a, collider_b);
EXPECT_TRUE(result);
}