mirror of
https://github.com/orange-cpp/omath.git
synced 2026-06-08 16:24:35 +00:00
Merge pull request #192 from orange-cpp/feature/lua
added more lua stuff
This commit is contained in:
@@ -124,7 +124,7 @@ namespace omath
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static consteval MatSize size() noexcept
|
||||
static constexpr MatSize size() noexcept
|
||||
{
|
||||
return {Rows, Columns};
|
||||
}
|
||||
|
||||
@@ -15,8 +15,13 @@ namespace omath::lua
|
||||
static void register_vec2(sol::table& omath_table);
|
||||
static void register_vec3(sol::table& omath_table);
|
||||
static void register_vec4(sol::table& omath_table);
|
||||
static void register_matrices(sol::table& omath_table);
|
||||
static void register_quaternion(sol::table& omath_table);
|
||||
static void register_color(sol::table& omath_table);
|
||||
static void register_hud(sol::table& omath_table);
|
||||
static void register_triangle(sol::table& omath_table);
|
||||
static void register_3d_primitives(sol::table& omath_table);
|
||||
static void register_collision(sol::table& omath_table);
|
||||
static void register_shared_types(sol::table& omath_table);
|
||||
static void register_engines(sol::table& omath_table);
|
||||
static void register_pattern_scan(sol::table& omath_table);
|
||||
|
||||
@@ -107,7 +107,8 @@ namespace omath::projection
|
||||
// m[1,1] == 1 / tan(fov/2) => fov = 2 * atan(1 / m[1,1])
|
||||
const auto f = proj_matrix.at(1, 1);
|
||||
// m[0,0] == m[1,1] / aspect_ratio => aspect = m[1,1] / m[0,0]
|
||||
return {FieldOfView::from_radians(NumericType{2} * std::atan(NumericType{1} / f)),
|
||||
const auto fov_radians = NumericType{2} * std::atan(NumericType{1} / f);
|
||||
return {FieldOfView::from_radians(static_cast<typename FieldOfView::ArithmeticType>(fov_radians)),
|
||||
f / proj_matrix.at(0, 0)};
|
||||
}
|
||||
|
||||
|
||||
@@ -17,8 +17,13 @@ namespace omath::lua
|
||||
register_vec2(omath_table);
|
||||
register_vec3(omath_table);
|
||||
register_vec4(omath_table);
|
||||
register_matrices(omath_table);
|
||||
register_quaternion(omath_table);
|
||||
register_color(omath_table);
|
||||
register_hud(omath_table);
|
||||
register_triangle(omath_table);
|
||||
register_3d_primitives(omath_table);
|
||||
register_collision(omath_table);
|
||||
register_shared_types(omath_table);
|
||||
register_engines(omath_table);
|
||||
register_pattern_scan(omath_table);
|
||||
|
||||
@@ -0,0 +1,304 @@
|
||||
//
|
||||
// Created by orange on 07.03.2026.
|
||||
//
|
||||
#ifdef OMATH_ENABLE_LUA
|
||||
#include "omath/lua/lua.hpp"
|
||||
#include <omath/3d_primitives/aabb.hpp>
|
||||
#include <omath/3d_primitives/obb.hpp>
|
||||
#include <omath/collision/collider_interface.hpp>
|
||||
#include <omath/collision/epa_algorithm.hpp>
|
||||
#include <omath/collision/gjk_algorithm.hpp>
|
||||
#include <omath/collision/line_tracer.hpp>
|
||||
#include <omath/linear_algebra/triangle.hpp>
|
||||
#include <omath/linear_algebra/vector3.hpp>
|
||||
#include <sol/sol.hpp>
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
namespace
|
||||
{
|
||||
using Vec3f = omath::Vector3<float>;
|
||||
using Triangle3f = omath::Triangle<Vec3f>;
|
||||
using Ray3f = omath::collision::Ray<Vec3f>;
|
||||
using LineTracer3f = omath::collision::LineTracer<Ray3f>;
|
||||
using Aabbf = omath::primitives::Aabb<float>;
|
||||
using Obbf = omath::primitives::Obb<float>;
|
||||
|
||||
template<class Object, class Value>
|
||||
auto lua_field(Value Object::* member)
|
||||
{
|
||||
return sol::property(
|
||||
[member](const Object& object) -> const Value&
|
||||
{
|
||||
return object.*member;
|
||||
},
|
||||
[member](Object& object, const Value& value)
|
||||
{
|
||||
object.*member = value;
|
||||
});
|
||||
}
|
||||
|
||||
class LuaConvexCollider final : public omath::collision::ColliderInterface<Vec3f>
|
||||
{
|
||||
public:
|
||||
using VectorType = Vec3f;
|
||||
|
||||
explicit LuaConvexCollider(std::vector<Vec3f> vertices, const Vec3f& origin = {})
|
||||
: m_vertices(std::move(vertices)), m_origin(origin)
|
||||
{
|
||||
if (m_vertices.empty())
|
||||
throw std::invalid_argument("convex collider must contain at least one vertex");
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
Vec3f find_abs_furthest_vertex_position(const Vec3f& direction) const override
|
||||
{
|
||||
const auto furthest = std::ranges::max_element(
|
||||
m_vertices,
|
||||
[&direction](const Vec3f& first, const Vec3f& second)
|
||||
{
|
||||
return first.dot(direction) < second.dot(direction);
|
||||
});
|
||||
|
||||
return m_origin + *furthest;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
const Vec3f& get_origin() const override
|
||||
{
|
||||
return m_origin;
|
||||
}
|
||||
|
||||
void set_origin(const Vec3f& new_origin) override
|
||||
{
|
||||
m_origin = new_origin;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
std::size_t vertex_count() const noexcept
|
||||
{
|
||||
return m_vertices.size();
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
const std::vector<Vec3f>& vertices() const noexcept
|
||||
{
|
||||
return m_vertices;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<Vec3f> m_vertices;
|
||||
Vec3f m_origin;
|
||||
};
|
||||
|
||||
std::vector<Vec3f> vec3_table_to_vector(const sol::table& points)
|
||||
{
|
||||
std::vector<Vec3f> result;
|
||||
for (std::size_t i = 1;; ++i)
|
||||
{
|
||||
const auto point = points[i].get<sol::optional<Vec3f>>();
|
||||
if (!point)
|
||||
break;
|
||||
result.push_back(*point);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
sol::table vec3_array_to_table(const auto& points, sol::this_state state)
|
||||
{
|
||||
sol::state_view lua(state);
|
||||
sol::table result = lua.create_table(static_cast<int>(points.size()), 0);
|
||||
for (std::size_t i = 0; i < points.size(); ++i)
|
||||
result[i + 1] = points[i];
|
||||
return result;
|
||||
}
|
||||
|
||||
Vec3f aabb_top(const Aabbf& aabb, const omath::primitives::UpAxis axis)
|
||||
{
|
||||
switch (axis)
|
||||
{
|
||||
case omath::primitives::UpAxis::X:
|
||||
return aabb.top<omath::primitives::UpAxis::X>();
|
||||
case omath::primitives::UpAxis::Y:
|
||||
return aabb.top<omath::primitives::UpAxis::Y>();
|
||||
case omath::primitives::UpAxis::Z:
|
||||
return aabb.top<omath::primitives::UpAxis::Z>();
|
||||
}
|
||||
std::unreachable();
|
||||
}
|
||||
|
||||
Vec3f aabb_bottom(const Aabbf& aabb, const omath::primitives::UpAxis axis)
|
||||
{
|
||||
switch (axis)
|
||||
{
|
||||
case omath::primitives::UpAxis::X:
|
||||
return aabb.bottom<omath::primitives::UpAxis::X>();
|
||||
case omath::primitives::UpAxis::Y:
|
||||
return aabb.bottom<omath::primitives::UpAxis::Y>();
|
||||
case omath::primitives::UpAxis::Z:
|
||||
return aabb.bottom<omath::primitives::UpAxis::Z>();
|
||||
}
|
||||
std::unreachable();
|
||||
}
|
||||
|
||||
bool ray_hits_triangle(const Ray3f& ray, const Triangle3f& triangle)
|
||||
{
|
||||
return !(LineTracer3f::get_ray_hit_point(ray, triangle) == ray.end);
|
||||
}
|
||||
|
||||
bool ray_hits_aabb(const Ray3f& ray, const Aabbf& aabb)
|
||||
{
|
||||
return !(LineTracer3f::get_ray_hit_point(ray, aabb) == ray.end);
|
||||
}
|
||||
|
||||
bool ray_hits_obb(const Ray3f& ray, const Obbf& obb)
|
||||
{
|
||||
return !(LineTracer3f::get_ray_hit_point(ray, obb) == ray.end);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace omath::lua
|
||||
{
|
||||
void LuaInterpreter::register_3d_primitives(sol::table& omath_table)
|
||||
{
|
||||
auto primitives_table = omath_table["primitives"].get_or_create<sol::table>();
|
||||
|
||||
primitives_table.new_enum("UpAxis", "X", omath::primitives::UpAxis::X, "Y", omath::primitives::UpAxis::Y, "Z",
|
||||
omath::primitives::UpAxis::Z);
|
||||
|
||||
primitives_table.new_usertype<Aabbf>(
|
||||
"Aabb",
|
||||
sol::factories([]() { return Aabbf{}; },
|
||||
[](const Vec3f& min, const Vec3f& max) { return Aabbf{min, max}; }),
|
||||
|
||||
"min", lua_field(&Aabbf::min), "max", lua_field(&Aabbf::max), "center", &Aabbf::center, "extents",
|
||||
&Aabbf::extents,
|
||||
"top",
|
||||
[](const Aabbf& aabb, sol::optional<omath::primitives::UpAxis> axis)
|
||||
{
|
||||
return aabb_top(aabb, axis.value_or(omath::primitives::UpAxis::Y));
|
||||
},
|
||||
"bottom",
|
||||
[](const Aabbf& aabb, sol::optional<omath::primitives::UpAxis> axis)
|
||||
{
|
||||
return aabb_bottom(aabb, axis.value_or(omath::primitives::UpAxis::Y));
|
||||
},
|
||||
"is_collide", &Aabbf::is_collide,
|
||||
"as_table",
|
||||
[](const Aabbf& aabb, sol::this_state state) -> sol::table
|
||||
{
|
||||
sol::state_view lua(state);
|
||||
sol::table result = lua.create_table();
|
||||
result["min"] = aabb.min;
|
||||
result["max"] = aabb.max;
|
||||
return result;
|
||||
});
|
||||
|
||||
primitives_table.new_usertype<Obbf>(
|
||||
"Obb",
|
||||
sol::factories(
|
||||
[]()
|
||||
{
|
||||
return Obbf{};
|
||||
},
|
||||
[](const Vec3f& center, const Vec3f& axis_x, const Vec3f& axis_y, const Vec3f& axis_z,
|
||||
const Vec3f& half_extents)
|
||||
{
|
||||
return Obbf{center, axis_x, axis_y, axis_z, half_extents};
|
||||
}),
|
||||
|
||||
"center", lua_field(&Obbf::center), "axis_x", lua_field(&Obbf::axis_x), "axis_y",
|
||||
lua_field(&Obbf::axis_y), "axis_z", lua_field(&Obbf::axis_z), "half_extents",
|
||||
lua_field(&Obbf::half_extents),
|
||||
"vertices",
|
||||
[](const Obbf& obb, sol::this_state state)
|
||||
{
|
||||
return vec3_array_to_table(obb.vertices(), state);
|
||||
});
|
||||
}
|
||||
|
||||
void LuaInterpreter::register_collision(sol::table& omath_table)
|
||||
{
|
||||
auto collision_table = omath_table["collision"].get_or_create<sol::table>();
|
||||
|
||||
collision_table.new_usertype<Ray3f>(
|
||||
"Ray",
|
||||
sol::factories([]() { return Ray3f{}; },
|
||||
[](const Vec3f& start, const Vec3f& end) { return Ray3f{start, end}; },
|
||||
[](const Vec3f& start, const Vec3f& end, const bool infinite_length)
|
||||
{ return Ray3f{start, end, infinite_length}; }),
|
||||
|
||||
"start", lua_field(&Ray3f::start), "end", lua_field(&Ray3f::end), "infinite_length",
|
||||
lua_field(&Ray3f::infinite_length),
|
||||
"direction_vector", &Ray3f::direction_vector, "direction_vector_normalized",
|
||||
&Ray3f::direction_vector_normalized);
|
||||
|
||||
collision_table.new_usertype<LuaConvexCollider>(
|
||||
"ConvexCollider",
|
||||
sol::factories([](const sol::table& vertices) { return LuaConvexCollider(vec3_table_to_vector(vertices)); },
|
||||
[](const sol::table& vertices, const Vec3f& origin)
|
||||
{ return LuaConvexCollider(vec3_table_to_vector(vertices), origin); }),
|
||||
|
||||
"find_abs_furthest_vertex_position", &LuaConvexCollider::find_abs_furthest_vertex_position,
|
||||
"get_origin", &LuaConvexCollider::get_origin, "set_origin", &LuaConvexCollider::set_origin,
|
||||
"vertex_count", &LuaConvexCollider::vertex_count,
|
||||
"vertices",
|
||||
[](const LuaConvexCollider& collider, sol::this_state state)
|
||||
{
|
||||
return vec3_array_to_table(collider.vertices(), state);
|
||||
});
|
||||
|
||||
collision_table.new_usertype<LineTracer3f>(
|
||||
"LineTracer", sol::no_constructor, "can_trace_line", &LineTracer3f::can_trace_line,
|
||||
"get_ray_hit_point",
|
||||
sol::overload(
|
||||
[](const Ray3f& ray, const Triangle3f& triangle)
|
||||
{
|
||||
return LineTracer3f::get_ray_hit_point(ray, triangle);
|
||||
},
|
||||
[](const Ray3f& ray, const Aabbf& aabb)
|
||||
{
|
||||
return LineTracer3f::get_ray_hit_point(ray, aabb);
|
||||
},
|
||||
[](const Ray3f& ray, const Obbf& obb)
|
||||
{
|
||||
return LineTracer3f::get_ray_hit_point(ray, obb);
|
||||
}),
|
||||
"ray_hits_triangle", &ray_hits_triangle, "ray_hits_aabb", &ray_hits_aabb, "ray_hits_obb",
|
||||
&ray_hits_obb);
|
||||
|
||||
collision_table["gjk_support_vertex"] =
|
||||
[](const LuaConvexCollider& collider_a, const LuaConvexCollider& collider_b, const Vec3f& direction)
|
||||
{ return omath::collision::GjkAlgorithm<LuaConvexCollider>::find_support_vertex(collider_a, collider_b, direction); };
|
||||
|
||||
collision_table["gjk_collide"] = [](const LuaConvexCollider& collider_a, const LuaConvexCollider& collider_b)
|
||||
{ return omath::collision::GjkAlgorithm<LuaConvexCollider>::is_collide(collider_a, collider_b); };
|
||||
|
||||
collision_table["epa_solve"] = [](const LuaConvexCollider& collider_a, const LuaConvexCollider& collider_b,
|
||||
sol::this_state state) -> sol::object
|
||||
{
|
||||
using Gjk = omath::collision::GjkAlgorithm<LuaConvexCollider>;
|
||||
using Epa = omath::collision::Epa<LuaConvexCollider>;
|
||||
|
||||
sol::state_view lua(state);
|
||||
const auto gjk = Gjk::is_collide_with_simplex_info(collider_a, collider_b);
|
||||
if (!gjk.hit)
|
||||
return sol::nil;
|
||||
|
||||
const auto epa = Epa::solve(collider_a, collider_b, gjk.simplex);
|
||||
if (!epa)
|
||||
return sol::nil;
|
||||
|
||||
sol::table result = lua.create_table();
|
||||
result["normal"] = epa->normal;
|
||||
result["penetration_vector"] = epa->penetration_vector;
|
||||
result["depth"] = epa->depth;
|
||||
result["iterations"] = epa->iterations;
|
||||
result["num_vertices"] = epa->num_vertices;
|
||||
result["num_faces"] = epa->num_faces;
|
||||
return sol::make_object(lua, result);
|
||||
};
|
||||
}
|
||||
} // namespace omath::lua
|
||||
#endif
|
||||
+57
-27
@@ -14,6 +14,8 @@
|
||||
#include <omath/engines/unreal_engine/camera.hpp>
|
||||
#include <sol/sol.hpp>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -85,44 +87,72 @@ namespace
|
||||
using PitchAngle = typename EngineTraits::PitchAngle;
|
||||
using ViewAngles = typename EngineTraits::ViewAngles;
|
||||
using Camera = typename EngineTraits::Camera;
|
||||
using Mat4X4 = std::remove_cvref_t<decltype(std::declval<const Camera&>().get_view_matrix())>;
|
||||
|
||||
auto engine_table = omath_table[subtable_name].get_or_create<sol::table>();
|
||||
auto types = omath_table["_types"].get<sol::table>();
|
||||
|
||||
set_engine_aliases<PitchAngle, ViewAngles>(engine_table, types);
|
||||
|
||||
engine_table.new_usertype<Camera>(
|
||||
auto camera_type = engine_table.new_usertype<Camera>(
|
||||
"Camera",
|
||||
sol::constructors<Camera(const omath::Vector3<ArithmeticType>&, const ViewAngles&,
|
||||
const omath::projection::ViewPort&, const omath::projection::FieldOfView&,
|
||||
ArithmeticType, ArithmeticType)>(),
|
||||
"look_at", &Camera::look_at, "get_forward", &Camera::get_forward, "get_right", &Camera::get_right,
|
||||
"get_up", &Camera::get_up, "get_origin", &Camera::get_origin, "get_view_angles",
|
||||
&Camera::get_view_angles, "get_near_plane", &Camera::get_near_plane, "get_far_plane",
|
||||
&Camera::get_far_plane, "get_field_of_view", &Camera::get_field_of_view, "set_origin",
|
||||
&Camera::set_origin, "set_view_angles", &Camera::set_view_angles, "set_view_port",
|
||||
&Camera::set_view_port, "set_field_of_view", &Camera::set_field_of_view, "set_near_plane",
|
||||
&Camera::set_near_plane, "set_far_plane", &Camera::set_far_plane,
|
||||
ArithmeticType, ArithmeticType)>());
|
||||
|
||||
"world_to_screen",
|
||||
[](const Camera& cam, const omath::Vector3<ArithmeticType>& pos)
|
||||
-> std::tuple<sol::optional<omath::Vector3<ArithmeticType>>, sol::optional<std::string>>
|
||||
{
|
||||
auto result = cam.world_to_screen(pos);
|
||||
if (result)
|
||||
return {*result, sol::nullopt};
|
||||
return {sol::nullopt, projection_error_to_string(result.error())};
|
||||
},
|
||||
camera_type["look_at"] = &Camera::look_at;
|
||||
camera_type["get_forward"] = &Camera::get_forward;
|
||||
camera_type["get_right"] = &Camera::get_right;
|
||||
camera_type["get_up"] = &Camera::get_up;
|
||||
camera_type["get_origin"] = &Camera::get_origin;
|
||||
camera_type["get_view_angles"] = &Camera::get_view_angles;
|
||||
camera_type["get_near_plane"] = &Camera::get_near_plane;
|
||||
camera_type["get_far_plane"] = &Camera::get_far_plane;
|
||||
camera_type["get_field_of_view"] = &Camera::get_field_of_view;
|
||||
camera_type["set_origin"] = &Camera::set_origin;
|
||||
camera_type["set_view_angles"] = &Camera::set_view_angles;
|
||||
camera_type["set_view_port"] = &Camera::set_view_port;
|
||||
camera_type["set_field_of_view"] = &Camera::set_field_of_view;
|
||||
camera_type["set_near_plane"] = &Camera::set_near_plane;
|
||||
camera_type["set_far_plane"] = &Camera::set_far_plane;
|
||||
|
||||
"screen_to_world",
|
||||
[](const Camera& cam, const omath::Vector3<ArithmeticType>& pos)
|
||||
-> std::tuple<sol::optional<omath::Vector3<ArithmeticType>>, sol::optional<std::string>>
|
||||
{
|
||||
auto result = cam.screen_to_world(pos);
|
||||
if (result)
|
||||
return {*result, sol::nullopt};
|
||||
return {sol::nullopt, projection_error_to_string(result.error())};
|
||||
});
|
||||
camera_type["get_view_matrix"] = [](const Camera& cam) -> Mat4X4
|
||||
{
|
||||
return cam.get_view_matrix();
|
||||
};
|
||||
camera_type["get_projection_matrix"] = [](const Camera& cam) -> Mat4X4
|
||||
{
|
||||
return cam.get_projection_matrix();
|
||||
};
|
||||
camera_type["get_view_projection_matrix"] = [](const Camera& cam) -> Mat4X4
|
||||
{
|
||||
return cam.get_view_projection_matrix();
|
||||
};
|
||||
camera_type["extract_projection_params"] = [](const Mat4X4& projection_matrix)
|
||||
{
|
||||
const auto params = Camera::extract_projection_params(projection_matrix);
|
||||
return std::make_tuple(params.fov, params.aspect_ratio);
|
||||
};
|
||||
camera_type["calc_view_angles_from_view_matrix"] = &Camera::calc_view_angles_from_view_matrix;
|
||||
camera_type["calc_origin_from_view_matrix"] = &Camera::calc_origin_from_view_matrix;
|
||||
|
||||
camera_type["world_to_screen"] = [](const Camera& cam, const omath::Vector3<ArithmeticType>& pos)
|
||||
-> std::tuple<sol::optional<omath::Vector3<ArithmeticType>>, sol::optional<std::string>>
|
||||
{
|
||||
auto result = cam.world_to_screen(pos);
|
||||
if (result)
|
||||
return {*result, sol::nullopt};
|
||||
return {sol::nullopt, projection_error_to_string(result.error())};
|
||||
};
|
||||
|
||||
camera_type["screen_to_world"] = [](const Camera& cam, const omath::Vector3<ArithmeticType>& pos)
|
||||
-> std::tuple<sol::optional<omath::Vector3<ArithmeticType>>, sol::optional<std::string>>
|
||||
{
|
||||
auto result = cam.screen_to_world(pos);
|
||||
if (result)
|
||||
return {*result, sol::nullopt};
|
||||
return {sol::nullopt, projection_error_to_string(result.error())};
|
||||
};
|
||||
}
|
||||
|
||||
// ---- Engine trait structs -----------------------------------------------
|
||||
|
||||
@@ -0,0 +1,516 @@
|
||||
//
|
||||
// Created by orange on 07.03.2026.
|
||||
//
|
||||
#ifdef OMATH_ENABLE_LUA
|
||||
#include "omath/lua/lua.hpp"
|
||||
#include <omath/hud/canvas_box.hpp>
|
||||
#include <omath/hud/entity_overlay.hpp>
|
||||
#include <sol/sol.hpp>
|
||||
#include <any>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace
|
||||
{
|
||||
[[noreturn]]
|
||||
void throw_lua_error(sol::protected_function_result& result)
|
||||
{
|
||||
sol::error err = result;
|
||||
throw err;
|
||||
}
|
||||
|
||||
class LuaHudRenderer final : public omath::hud::HudRendererInterface
|
||||
{
|
||||
public:
|
||||
explicit LuaHudRenderer(sol::table callbacks): m_callbacks(std::move(callbacks))
|
||||
{
|
||||
}
|
||||
|
||||
void add_line(const omath::Vector2<float>& line_start, const omath::Vector2<float>& line_end,
|
||||
const omath::Color& color, const float thickness) override
|
||||
{
|
||||
call_optional("add_line", line_start, line_end, color, thickness);
|
||||
}
|
||||
|
||||
void add_polyline(const std::span<const omath::Vector2<float>>& vertexes, const omath::Color& color,
|
||||
const float thickness) override
|
||||
{
|
||||
call_optional("add_polyline", make_points_table(vertexes), color, thickness);
|
||||
}
|
||||
|
||||
void add_filled_polyline(const std::span<const omath::Vector2<float>>& vertexes,
|
||||
const omath::Color& color) override
|
||||
{
|
||||
call_optional("add_filled_polyline", make_points_table(vertexes), color);
|
||||
}
|
||||
|
||||
void add_rectangle(const omath::Vector2<float>& min, const omath::Vector2<float>& max,
|
||||
const omath::Color& color) override
|
||||
{
|
||||
call_optional("add_rectangle", min, max, color);
|
||||
}
|
||||
|
||||
void add_filled_rectangle(const omath::Vector2<float>& min, const omath::Vector2<float>& max,
|
||||
const omath::Color& color) override
|
||||
{
|
||||
call_optional("add_filled_rectangle", min, max, color);
|
||||
}
|
||||
|
||||
void add_circle(const omath::Vector2<float>& center, const float radius, const omath::Color& color,
|
||||
const float thickness, const int segments) override
|
||||
{
|
||||
call_optional("add_circle", center, radius, color, thickness, segments);
|
||||
}
|
||||
|
||||
void add_filled_circle(const omath::Vector2<float>& center, const float radius, const omath::Color& color,
|
||||
const int segments) override
|
||||
{
|
||||
call_optional("add_filled_circle", center, radius, color, segments);
|
||||
}
|
||||
|
||||
void add_arc(const omath::Vector2<float>& center, const float radius, const float a_min, const float a_max,
|
||||
const omath::Color& color, const float thickness, const int segments) override
|
||||
{
|
||||
call_optional("add_arc", center, radius, a_min, a_max, color, thickness, segments);
|
||||
}
|
||||
|
||||
void add_image(const std::any& texture_id, const omath::Vector2<float>& min, const omath::Vector2<float>& max,
|
||||
const omath::Color& tint) override
|
||||
{
|
||||
const auto callback = callback_for("add_image");
|
||||
if (!callback)
|
||||
return;
|
||||
|
||||
sol::object texture = sol::nil;
|
||||
if (const auto lua_object = std::any_cast<sol::object>(&texture_id))
|
||||
texture = *lua_object;
|
||||
|
||||
auto result = (*callback)(texture, min, max, tint);
|
||||
if (!result.valid())
|
||||
throw_lua_error(result);
|
||||
}
|
||||
|
||||
void add_text(const omath::Vector2<float>& position, const omath::Color& color,
|
||||
const std::string_view& text) override
|
||||
{
|
||||
call_optional("add_text", position, color, std::string{text});
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
omath::Vector2<float> calc_text_size(const std::string_view& text) override
|
||||
{
|
||||
const auto callback = callback_for("calc_text_size");
|
||||
if (!callback)
|
||||
return {};
|
||||
|
||||
auto result = (*callback)(std::string{text});
|
||||
if (!result.valid())
|
||||
throw_lua_error(result);
|
||||
|
||||
return result.get<omath::Vector2<float>>();
|
||||
}
|
||||
|
||||
private:
|
||||
sol::main_table m_callbacks;
|
||||
|
||||
[[nodiscard]]
|
||||
sol::optional<sol::protected_function> callback_for(const char* name) const
|
||||
{
|
||||
const sol::object callback = m_callbacks[name];
|
||||
if (!callback.valid() || callback == sol::nil)
|
||||
return sol::nullopt;
|
||||
return callback.as<sol::protected_function>();
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
void call_optional(const char* name, Args&&... args) const
|
||||
{
|
||||
const auto callback = callback_for(name);
|
||||
if (!callback)
|
||||
return;
|
||||
|
||||
auto result = (*callback)(std::forward<Args>(args)...);
|
||||
if (!result.valid())
|
||||
throw_lua_error(result);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
sol::table make_points_table(const std::span<const omath::Vector2<float>>& vertexes) const
|
||||
{
|
||||
sol::state_view lua(m_callbacks.lua_state());
|
||||
sol::table points = lua.create_table(static_cast<int>(vertexes.size()), 0);
|
||||
for (std::size_t i = 0; i < vertexes.size(); ++i)
|
||||
points[i + 1] = vertexes[i];
|
||||
return points;
|
||||
}
|
||||
};
|
||||
|
||||
[[nodiscard]]
|
||||
omath::Color transparent_black()
|
||||
{
|
||||
return {0.f, 0.f, 0.f, 0.f};
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
omath::Color opaque_white()
|
||||
{
|
||||
return {1.f, 1.f, 1.f, 1.f};
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
omath::hud::EntityOverlay make_overlay(const omath::Vector2<float>& top, const omath::Vector2<float>& bottom,
|
||||
const std::shared_ptr<LuaHudRenderer>& renderer)
|
||||
{
|
||||
if (!renderer)
|
||||
throw std::invalid_argument("hud renderer must not be nil");
|
||||
return {top, bottom, renderer};
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
std::any texture_id_from_lua_object(const sol::object& texture_id)
|
||||
{
|
||||
return texture_id;
|
||||
}
|
||||
|
||||
std::vector<omath::Vector2<float>> points_from_table(const sol::table& points)
|
||||
{
|
||||
std::vector<omath::Vector2<float>> result;
|
||||
for (std::size_t i = 1;; ++i)
|
||||
{
|
||||
const auto point = points[i].get<sol::optional<omath::Vector2<float>>>();
|
||||
if (!point)
|
||||
break;
|
||||
result.push_back(*point);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<class Object, class Value>
|
||||
auto lua_field(Value Object::* member)
|
||||
{
|
||||
return sol::property(
|
||||
[member](const Object& object) -> const Value&
|
||||
{
|
||||
return object.*member;
|
||||
},
|
||||
[member](Object& object, const Value& value)
|
||||
{
|
||||
object.*member = value;
|
||||
});
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace omath::lua
|
||||
{
|
||||
void LuaInterpreter::register_hud(sol::table& omath_table)
|
||||
{
|
||||
auto hud_table = omath_table["hud"].get_or_create<sol::table>();
|
||||
|
||||
hud_table.new_usertype<omath::hud::CanvasBox>(
|
||||
"CanvasBox",
|
||||
sol::factories([](const omath::Vector2<float>& top, const omath::Vector2<float>& bottom)
|
||||
{ return omath::hud::CanvasBox(top, bottom); },
|
||||
[](const omath::Vector2<float>& top, const omath::Vector2<float>& bottom,
|
||||
const float ratio) { return omath::hud::CanvasBox(top, bottom, ratio); }),
|
||||
|
||||
"top_left_corner", lua_field(&omath::hud::CanvasBox::top_left_corner), "top_right_corner",
|
||||
lua_field(&omath::hud::CanvasBox::top_right_corner), "bottom_left_corner",
|
||||
lua_field(&omath::hud::CanvasBox::bottom_left_corner), "bottom_right_corner",
|
||||
lua_field(&omath::hud::CanvasBox::bottom_right_corner),
|
||||
|
||||
"as_table",
|
||||
[](const omath::hud::CanvasBox& box, sol::this_state s) -> sol::table
|
||||
{
|
||||
sol::state_view lua(s);
|
||||
sol::table t = lua.create_table(4, 0);
|
||||
const auto points = box.as_array();
|
||||
for (std::size_t i = 0; i < points.size(); ++i)
|
||||
t[i + 1] = points[i];
|
||||
return t;
|
||||
});
|
||||
|
||||
hud_table.new_usertype<LuaHudRenderer>(
|
||||
"Renderer",
|
||||
sol::factories([](const sol::table& callbacks)
|
||||
{ return std::make_shared<LuaHudRenderer>(callbacks); }),
|
||||
|
||||
"add_line", &LuaHudRenderer::add_line,
|
||||
"add_polyline",
|
||||
[](LuaHudRenderer& renderer, const sol::table& points, const omath::Color& color,
|
||||
const float thickness)
|
||||
{
|
||||
const auto vertices = points_from_table(points);
|
||||
renderer.add_polyline({vertices.data(), vertices.size()}, color, thickness);
|
||||
},
|
||||
"add_filled_polyline",
|
||||
[](LuaHudRenderer& renderer, const sol::table& points, const omath::Color& color)
|
||||
{
|
||||
const auto vertices = points_from_table(points);
|
||||
renderer.add_filled_polyline({vertices.data(), vertices.size()}, color);
|
||||
},
|
||||
"add_rectangle", &LuaHudRenderer::add_rectangle, "add_filled_rectangle",
|
||||
&LuaHudRenderer::add_filled_rectangle, "add_circle",
|
||||
[](LuaHudRenderer& renderer, const omath::Vector2<float>& center, const float radius,
|
||||
const omath::Color& color, const float thickness, sol::optional<int> segments)
|
||||
{
|
||||
renderer.add_circle(center, radius, color, thickness, segments.value_or(0));
|
||||
},
|
||||
"add_filled_circle",
|
||||
[](LuaHudRenderer& renderer, const omath::Vector2<float>& center, const float radius,
|
||||
const omath::Color& color, sol::optional<int> segments)
|
||||
{
|
||||
renderer.add_filled_circle(center, radius, color, segments.value_or(0));
|
||||
},
|
||||
"add_arc",
|
||||
[](LuaHudRenderer& renderer, const omath::Vector2<float>& center, const float radius,
|
||||
const float a_min, const float a_max, const omath::Color& color, const float thickness,
|
||||
sol::optional<int> segments)
|
||||
{
|
||||
renderer.add_arc(center, radius, a_min, a_max, color, thickness, segments.value_or(0));
|
||||
},
|
||||
"add_image",
|
||||
[](LuaHudRenderer& renderer, const sol::object& texture_id, const omath::Vector2<float>& min,
|
||||
const omath::Vector2<float>& max, sol::optional<omath::Color> tint)
|
||||
{
|
||||
renderer.add_image(texture_id_from_lua_object(texture_id), min, max,
|
||||
tint.value_or(opaque_white()));
|
||||
},
|
||||
"add_text", &LuaHudRenderer::add_text, "calc_text_size", &LuaHudRenderer::calc_text_size);
|
||||
|
||||
hud_table.new_usertype<omath::hud::EntityOverlay>(
|
||||
"EntityOverlay",
|
||||
sol::factories(&make_overlay),
|
||||
|
||||
"add_2d_box",
|
||||
[](omath::hud::EntityOverlay& overlay, const omath::Color& box_color,
|
||||
sol::optional<omath::Color> fill_color, sol::optional<float> thickness) -> omath::hud::EntityOverlay&
|
||||
{
|
||||
return overlay.add_2d_box(box_color, fill_color.value_or(transparent_black()),
|
||||
thickness.value_or(1.f));
|
||||
},
|
||||
"add_cornered_2d_box",
|
||||
[](omath::hud::EntityOverlay& overlay, const omath::Color& box_color,
|
||||
sol::optional<omath::Color> fill_color, sol::optional<float> corner_ratio_len,
|
||||
sol::optional<float> thickness) -> omath::hud::EntityOverlay&
|
||||
{
|
||||
return overlay.add_cornered_2d_box(box_color, fill_color.value_or(transparent_black()),
|
||||
corner_ratio_len.value_or(0.2f), thickness.value_or(1.f));
|
||||
},
|
||||
"add_dashed_box",
|
||||
[](omath::hud::EntityOverlay& overlay, const omath::Color& color, sol::optional<float> dash_len,
|
||||
sol::optional<float> gap_len, sol::optional<float> thickness) -> omath::hud::EntityOverlay&
|
||||
{
|
||||
return overlay.add_dashed_box(color, dash_len.value_or(8.f), gap_len.value_or(5.f),
|
||||
thickness.value_or(1.f));
|
||||
},
|
||||
|
||||
"add_right_bar",
|
||||
[](omath::hud::EntityOverlay& overlay, const omath::Color& color, const omath::Color& outline_color,
|
||||
const omath::Color& bg_color, const float width, const float ratio,
|
||||
sol::optional<float> offset) -> omath::hud::EntityOverlay&
|
||||
{
|
||||
return overlay.add_right_bar(color, outline_color, bg_color, width, ratio, offset.value_or(5.f));
|
||||
},
|
||||
"add_left_bar",
|
||||
[](omath::hud::EntityOverlay& overlay, const omath::Color& color, const omath::Color& outline_color,
|
||||
const omath::Color& bg_color, const float width, const float ratio,
|
||||
sol::optional<float> offset) -> omath::hud::EntityOverlay&
|
||||
{
|
||||
return overlay.add_left_bar(color, outline_color, bg_color, width, ratio, offset.value_or(5.f));
|
||||
},
|
||||
"add_top_bar",
|
||||
[](omath::hud::EntityOverlay& overlay, const omath::Color& color, const omath::Color& outline_color,
|
||||
const omath::Color& bg_color, const float height, const float ratio,
|
||||
sol::optional<float> offset) -> omath::hud::EntityOverlay&
|
||||
{
|
||||
return overlay.add_top_bar(color, outline_color, bg_color, height, ratio, offset.value_or(5.f));
|
||||
},
|
||||
"add_bottom_bar",
|
||||
[](omath::hud::EntityOverlay& overlay, const omath::Color& color, const omath::Color& outline_color,
|
||||
const omath::Color& bg_color, const float height, const float ratio,
|
||||
sol::optional<float> offset) -> omath::hud::EntityOverlay&
|
||||
{
|
||||
return overlay.add_bottom_bar(color, outline_color, bg_color, height, ratio, offset.value_or(5.f));
|
||||
},
|
||||
"add_right_dashed_bar",
|
||||
[](omath::hud::EntityOverlay& overlay, const omath::Color& color, const omath::Color& outline_color,
|
||||
const omath::Color& bg_color, const float width, const float ratio, const float dash_len,
|
||||
const float gap_len, sol::optional<float> offset) -> omath::hud::EntityOverlay&
|
||||
{
|
||||
return overlay.add_right_dashed_bar(color, outline_color, bg_color, width, ratio, dash_len, gap_len,
|
||||
offset.value_or(5.f));
|
||||
},
|
||||
"add_left_dashed_bar",
|
||||
[](omath::hud::EntityOverlay& overlay, const omath::Color& color, const omath::Color& outline_color,
|
||||
const omath::Color& bg_color, const float width, const float ratio, const float dash_len,
|
||||
const float gap_len, sol::optional<float> offset) -> omath::hud::EntityOverlay&
|
||||
{
|
||||
return overlay.add_left_dashed_bar(color, outline_color, bg_color, width, ratio, dash_len, gap_len,
|
||||
offset.value_or(5.f));
|
||||
},
|
||||
"add_top_dashed_bar",
|
||||
[](omath::hud::EntityOverlay& overlay, const omath::Color& color, const omath::Color& outline_color,
|
||||
const omath::Color& bg_color, const float height, const float ratio, const float dash_len,
|
||||
const float gap_len, sol::optional<float> offset) -> omath::hud::EntityOverlay&
|
||||
{
|
||||
return overlay.add_top_dashed_bar(color, outline_color, bg_color, height, ratio, dash_len, gap_len,
|
||||
offset.value_or(5.f));
|
||||
},
|
||||
"add_bottom_dashed_bar",
|
||||
[](omath::hud::EntityOverlay& overlay, const omath::Color& color, const omath::Color& outline_color,
|
||||
const omath::Color& bg_color, const float height, const float ratio, const float dash_len,
|
||||
const float gap_len, sol::optional<float> offset) -> omath::hud::EntityOverlay&
|
||||
{
|
||||
return overlay.add_bottom_dashed_bar(color, outline_color, bg_color, height, ratio, dash_len,
|
||||
gap_len, offset.value_or(5.f));
|
||||
},
|
||||
|
||||
"add_right_label",
|
||||
[](omath::hud::EntityOverlay& overlay, const omath::Color& color, const float offset,
|
||||
const bool outlined, const std::string& text) -> omath::hud::EntityOverlay&
|
||||
{
|
||||
return overlay.add_right_label(color, offset, outlined, text);
|
||||
},
|
||||
"add_left_label",
|
||||
[](omath::hud::EntityOverlay& overlay, const omath::Color& color, const float offset,
|
||||
const bool outlined, const std::string& text) -> omath::hud::EntityOverlay&
|
||||
{
|
||||
return overlay.add_left_label(color, offset, outlined, text);
|
||||
},
|
||||
"add_top_label",
|
||||
[](omath::hud::EntityOverlay& overlay, const omath::Color& color, const float offset,
|
||||
const bool outlined, const std::string& text) -> omath::hud::EntityOverlay&
|
||||
{
|
||||
return overlay.add_top_label(color, offset, outlined, text);
|
||||
},
|
||||
"add_bottom_label",
|
||||
[](omath::hud::EntityOverlay& overlay, const omath::Color& color, const float offset,
|
||||
const bool outlined, const std::string& text) -> omath::hud::EntityOverlay&
|
||||
{
|
||||
return overlay.add_bottom_label(color, offset, outlined, text);
|
||||
},
|
||||
"add_centered_top_label",
|
||||
[](omath::hud::EntityOverlay& overlay, const omath::Color& color, const float offset,
|
||||
const bool outlined, const std::string& text) -> omath::hud::EntityOverlay&
|
||||
{
|
||||
return overlay.add_centered_top_label(color, offset, outlined, text);
|
||||
},
|
||||
"add_centered_bottom_label",
|
||||
[](omath::hud::EntityOverlay& overlay, const omath::Color& color, const float offset,
|
||||
const bool outlined, const std::string& text) -> omath::hud::EntityOverlay&
|
||||
{
|
||||
return overlay.add_centered_bottom_label(color, offset, outlined, text);
|
||||
},
|
||||
|
||||
"add_right_space_vertical", &omath::hud::EntityOverlay::add_right_space_vertical,
|
||||
"add_right_space_horizontal", &omath::hud::EntityOverlay::add_right_space_horizontal,
|
||||
"add_left_space_vertical", &omath::hud::EntityOverlay::add_left_space_vertical,
|
||||
"add_left_space_horizontal", &omath::hud::EntityOverlay::add_left_space_horizontal,
|
||||
"add_top_space_vertical", &omath::hud::EntityOverlay::add_top_space_vertical,
|
||||
"add_top_space_horizontal", &omath::hud::EntityOverlay::add_top_space_horizontal,
|
||||
"add_bottom_space_vertical", &omath::hud::EntityOverlay::add_bottom_space_vertical,
|
||||
"add_bottom_space_horizontal", &omath::hud::EntityOverlay::add_bottom_space_horizontal,
|
||||
|
||||
"add_right_progress_ring",
|
||||
[](omath::hud::EntityOverlay& overlay, const omath::Color& color, const omath::Color& bg,
|
||||
const float radius, const float ratio, sol::optional<float> thickness, sol::optional<float> offset,
|
||||
sol::optional<int> segments) -> omath::hud::EntityOverlay&
|
||||
{
|
||||
return overlay.add_right_progress_ring(color, bg, radius, ratio, thickness.value_or(2.f),
|
||||
offset.value_or(5.f), segments.value_or(0));
|
||||
},
|
||||
"add_left_progress_ring",
|
||||
[](omath::hud::EntityOverlay& overlay, const omath::Color& color, const omath::Color& bg,
|
||||
const float radius, const float ratio, sol::optional<float> thickness, sol::optional<float> offset,
|
||||
sol::optional<int> segments) -> omath::hud::EntityOverlay&
|
||||
{
|
||||
return overlay.add_left_progress_ring(color, bg, radius, ratio, thickness.value_or(2.f),
|
||||
offset.value_or(5.f), segments.value_or(0));
|
||||
},
|
||||
"add_top_progress_ring",
|
||||
[](omath::hud::EntityOverlay& overlay, const omath::Color& color, const omath::Color& bg,
|
||||
const float radius, const float ratio, sol::optional<float> thickness, sol::optional<float> offset,
|
||||
sol::optional<int> segments) -> omath::hud::EntityOverlay&
|
||||
{
|
||||
return overlay.add_top_progress_ring(color, bg, radius, ratio, thickness.value_or(2.f),
|
||||
offset.value_or(5.f), segments.value_or(0));
|
||||
},
|
||||
"add_bottom_progress_ring",
|
||||
[](omath::hud::EntityOverlay& overlay, const omath::Color& color, const omath::Color& bg,
|
||||
const float radius, const float ratio, sol::optional<float> thickness, sol::optional<float> offset,
|
||||
sol::optional<int> segments) -> omath::hud::EntityOverlay&
|
||||
{
|
||||
return overlay.add_bottom_progress_ring(color, bg, radius, ratio, thickness.value_or(2.f),
|
||||
offset.value_or(5.f), segments.value_or(0));
|
||||
},
|
||||
|
||||
"add_right_icon",
|
||||
[](omath::hud::EntityOverlay& overlay, const sol::object& texture_id, const float width,
|
||||
const float height, sol::optional<omath::Color> tint,
|
||||
sol::optional<float> offset) -> omath::hud::EntityOverlay&
|
||||
{
|
||||
return overlay.add_right_icon(texture_id_from_lua_object(texture_id), width, height,
|
||||
tint.value_or(opaque_white()), offset.value_or(5.f));
|
||||
},
|
||||
"add_left_icon",
|
||||
[](omath::hud::EntityOverlay& overlay, const sol::object& texture_id, const float width,
|
||||
const float height, sol::optional<omath::Color> tint,
|
||||
sol::optional<float> offset) -> omath::hud::EntityOverlay&
|
||||
{
|
||||
return overlay.add_left_icon(texture_id_from_lua_object(texture_id), width, height,
|
||||
tint.value_or(opaque_white()), offset.value_or(5.f));
|
||||
},
|
||||
"add_top_icon",
|
||||
[](omath::hud::EntityOverlay& overlay, const sol::object& texture_id, const float width,
|
||||
const float height, sol::optional<omath::Color> tint,
|
||||
sol::optional<float> offset) -> omath::hud::EntityOverlay&
|
||||
{
|
||||
return overlay.add_top_icon(texture_id_from_lua_object(texture_id), width, height,
|
||||
tint.value_or(opaque_white()), offset.value_or(5.f));
|
||||
},
|
||||
"add_bottom_icon",
|
||||
[](omath::hud::EntityOverlay& overlay, const sol::object& texture_id, const float width,
|
||||
const float height, sol::optional<omath::Color> tint,
|
||||
sol::optional<float> offset) -> omath::hud::EntityOverlay&
|
||||
{
|
||||
return overlay.add_bottom_icon(texture_id_from_lua_object(texture_id), width, height,
|
||||
tint.value_or(opaque_white()), offset.value_or(5.f));
|
||||
},
|
||||
|
||||
"add_snap_line", &omath::hud::EntityOverlay::add_snap_line, "add_skeleton",
|
||||
[](omath::hud::EntityOverlay& overlay, const omath::Color& color,
|
||||
sol::optional<float> thickness) -> omath::hud::EntityOverlay&
|
||||
{
|
||||
return overlay.add_skeleton(color, thickness.value_or(1.f));
|
||||
},
|
||||
"add_scan_marker",
|
||||
[](omath::hud::EntityOverlay& overlay, const omath::Color& color,
|
||||
sol::optional<omath::Color> outline, sol::optional<float> outline_thickness)
|
||||
-> omath::hud::EntityOverlay&
|
||||
{
|
||||
return overlay.contents(omath::hud::widget::ScanMarker{
|
||||
color, outline.value_or(transparent_black()), outline_thickness.value_or(1.f)});
|
||||
},
|
||||
"add_aim_dot",
|
||||
[](omath::hud::EntityOverlay& overlay, const omath::Vector2<float>& position,
|
||||
const omath::Color& color, sol::optional<float> radius) -> omath::hud::EntityOverlay&
|
||||
{
|
||||
return overlay.contents(omath::hud::widget::AimDot{position, color, radius.value_or(3.f)});
|
||||
},
|
||||
"add_projectile_aim",
|
||||
[](omath::hud::EntityOverlay& overlay, const omath::Vector2<float>& position,
|
||||
const omath::Color& color, sol::optional<float> size, sol::optional<float> line_size,
|
||||
sol::optional<omath::hud::widget::ProjectileAim::Figure> figure) -> omath::hud::EntityOverlay&
|
||||
{
|
||||
return overlay.contents(omath::hud::widget::ProjectileAim{
|
||||
position, color, size.value_or(3.f), line_size.value_or(1.f),
|
||||
figure.value_or(omath::hud::widget::ProjectileAim::Figure::SQUARE)});
|
||||
});
|
||||
|
||||
hud_table.new_enum("ProjectileAimFigure", "CIRCLE", omath::hud::widget::ProjectileAim::Figure::CIRCLE,
|
||||
"SQUARE", omath::hud::widget::ProjectileAim::Figure::SQUARE);
|
||||
}
|
||||
} // namespace omath::lua
|
||||
#endif
|
||||
@@ -0,0 +1,156 @@
|
||||
//
|
||||
// Created by orange on 07.03.2026.
|
||||
//
|
||||
#ifdef OMATH_ENABLE_LUA
|
||||
#include "omath/lua/lua.hpp"
|
||||
#include <omath/linear_algebra/mat.hpp>
|
||||
#include <sol/sol.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace
|
||||
{
|
||||
template<std::size_t Limit>
|
||||
std::size_t checked_index(const int index)
|
||||
{
|
||||
if (index < 0 || index >= static_cast<int>(Limit))
|
||||
throw std::out_of_range("matrix index is out of range");
|
||||
return static_cast<std::size_t>(index);
|
||||
}
|
||||
|
||||
template<std::size_t Rows, std::size_t Columns, class Type, omath::MatStoreType StoreType>
|
||||
omath::Mat<Rows, Columns, Type, StoreType> mat_from_rows(const sol::table& rows)
|
||||
{
|
||||
omath::Mat<Rows, Columns, Type, StoreType> result;
|
||||
|
||||
for (std::size_t row = 0; row < Rows; ++row)
|
||||
{
|
||||
const auto row_table = rows[row + 1].get<sol::optional<sol::table>>();
|
||||
if (!row_table)
|
||||
throw std::invalid_argument("matrix rows must be tables");
|
||||
|
||||
for (std::size_t column = 0; column < Columns; ++column)
|
||||
{
|
||||
const auto value = (*row_table)[column + 1].get<sol::optional<Type>>();
|
||||
if (!value)
|
||||
throw std::invalid_argument("matrix row has missing value");
|
||||
result.at(row, column) = *value;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<std::size_t Rows, std::size_t Columns, class Type, omath::MatStoreType StoreType>
|
||||
sol::table mat_as_table(const omath::Mat<Rows, Columns, Type, StoreType>& mat, sol::this_state state)
|
||||
{
|
||||
sol::state_view lua(state);
|
||||
sol::table rows = lua.create_table();
|
||||
|
||||
for (std::size_t row = 0; row < Rows; ++row)
|
||||
{
|
||||
sol::table row_table = lua.create_table();
|
||||
for (std::size_t column = 0; column < Columns; ++column)
|
||||
row_table[column + 1] = mat.at(row, column);
|
||||
rows[row + 1] = row_table;
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
template<std::size_t Size, class Type, omath::MatStoreType StoreType, bool RegisterMat4Helpers = false>
|
||||
void register_square_mat(sol::table& omath_table, const char* name)
|
||||
{
|
||||
using MatType = omath::Mat<Size, Size, Type, StoreType>;
|
||||
|
||||
auto type = omath_table.new_usertype<MatType>(
|
||||
name, sol::constructors<MatType()>(),
|
||||
|
||||
"from_rows", &mat_from_rows<Size, Size, Type, StoreType>, "row_count",
|
||||
[]()
|
||||
{
|
||||
return Size;
|
||||
},
|
||||
"columns_count",
|
||||
[]()
|
||||
{
|
||||
return Size;
|
||||
},
|
||||
|
||||
"at",
|
||||
[](const MatType& mat, const int row, const int column)
|
||||
{
|
||||
return mat.at(checked_index<Size>(row), checked_index<Size>(column));
|
||||
},
|
||||
"set_at",
|
||||
[](MatType& mat, const int row, const int column, const Type value)
|
||||
{
|
||||
mat.at(checked_index<Size>(row), checked_index<Size>(column)) = value;
|
||||
return mat;
|
||||
},
|
||||
|
||||
sol::meta_function::multiplication,
|
||||
sol::overload(
|
||||
[](const MatType& lhs, const MatType& rhs)
|
||||
{
|
||||
return lhs * rhs;
|
||||
},
|
||||
[](const MatType& mat, const Type scalar)
|
||||
{
|
||||
return mat * scalar;
|
||||
},
|
||||
[](const Type scalar, const MatType& mat)
|
||||
{
|
||||
return mat * scalar;
|
||||
}),
|
||||
sol::meta_function::division,
|
||||
[](const MatType& mat, const Type scalar)
|
||||
{
|
||||
return mat / scalar;
|
||||
},
|
||||
sol::meta_function::equal_to, &MatType::operator==, sol::meta_function::to_string, &MatType::to_string,
|
||||
|
||||
"transposed", &MatType::transposed, "determinant", &MatType::determinant, "sum", &MatType::sum, "clear",
|
||||
&MatType::clear, "set", &MatType::set, "inverted",
|
||||
[](const MatType& mat) -> sol::optional<MatType>
|
||||
{
|
||||
auto result = mat.inverted();
|
||||
if (!result)
|
||||
return sol::nullopt;
|
||||
return *result;
|
||||
},
|
||||
"as_table", &mat_as_table<Size, Size, Type, StoreType>);
|
||||
|
||||
if constexpr (RegisterMat4Helpers)
|
||||
{
|
||||
type["to_screen_mat"] = [](const Type screen_width, const Type screen_height)
|
||||
{
|
||||
return MatType{
|
||||
{screen_width / Type{2}, Type{0}, Type{0}, Type{0}},
|
||||
{Type{0}, -screen_height / Type{2}, Type{0}, Type{0}},
|
||||
{Type{0}, Type{0}, Type{1}, Type{0}},
|
||||
{screen_width / Type{2}, screen_height / Type{2}, Type{0}, Type{1}},
|
||||
};
|
||||
};
|
||||
type["translation"] = &omath::mat_translation<Type, StoreType>;
|
||||
type["scale"] = &omath::mat_scale<Type, StoreType>;
|
||||
type["look_at_left_handed"] = &omath::mat_look_at_left_handed<Type, StoreType>;
|
||||
type["look_at_right_handed"] = &omath::mat_look_at_right_handed<Type, StoreType>;
|
||||
type["perspective_left_handed_vertical_fov"] =
|
||||
&omath::mat_perspective_left_handed_vertical_fov<Type, StoreType>;
|
||||
type["perspective_right_handed_vertical_fov"] =
|
||||
&omath::mat_perspective_right_handed_vertical_fov<Type, StoreType>;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace omath::lua
|
||||
{
|
||||
void LuaInterpreter::register_matrices(sol::table& omath_table)
|
||||
{
|
||||
register_square_mat<3, float, omath::MatStoreType::ROW_MAJOR>(omath_table, "Mat3");
|
||||
register_square_mat<4, float, omath::MatStoreType::ROW_MAJOR, true>(omath_table, "Mat4");
|
||||
register_square_mat<4, float, omath::MatStoreType::COLUMN_MAJOR, true>(omath_table, "Mat4ColumnMajor");
|
||||
register_square_mat<4, double, omath::MatStoreType::ROW_MAJOR>(omath_table, "Mat4d");
|
||||
}
|
||||
} // namespace omath::lua
|
||||
#endif
|
||||
@@ -0,0 +1,95 @@
|
||||
//
|
||||
// Created by orange on 07.03.2026.
|
||||
//
|
||||
#ifdef OMATH_ENABLE_LUA
|
||||
#include "omath/lua/lua.hpp"
|
||||
#include <omath/linear_algebra/quaternion.hpp>
|
||||
#include <sol/sol.hpp>
|
||||
|
||||
namespace omath::lua
|
||||
{
|
||||
void LuaInterpreter::register_quaternion(sol::table& omath_table)
|
||||
{
|
||||
using Quatf = omath::Quaternion<float>;
|
||||
using Vec3f = omath::Vector3<float>;
|
||||
|
||||
omath_table.new_usertype<Quatf>(
|
||||
"Quaternion", sol::constructors<Quatf(), Quatf(float, float, float, float)>(),
|
||||
|
||||
"from_axis_angle", &Quatf::from_axis_angle,
|
||||
|
||||
"x",
|
||||
sol::property(
|
||||
[](const Quatf& q)
|
||||
{
|
||||
return q.x;
|
||||
},
|
||||
[](Quatf& q, float val)
|
||||
{
|
||||
q.x = val;
|
||||
}),
|
||||
"y",
|
||||
sol::property(
|
||||
[](const Quatf& q)
|
||||
{
|
||||
return q.y;
|
||||
},
|
||||
[](Quatf& q, float val)
|
||||
{
|
||||
q.y = val;
|
||||
}),
|
||||
"z",
|
||||
sol::property(
|
||||
[](const Quatf& q)
|
||||
{
|
||||
return q.z;
|
||||
},
|
||||
[](Quatf& q, float val)
|
||||
{
|
||||
q.z = val;
|
||||
}),
|
||||
"w",
|
||||
sol::property(
|
||||
[](const Quatf& q)
|
||||
{
|
||||
return q.w;
|
||||
},
|
||||
[](Quatf& q, float val)
|
||||
{
|
||||
q.w = val;
|
||||
}),
|
||||
|
||||
sol::meta_function::addition, sol::resolve<Quatf(const Quatf&) const>(&Quatf::operator+),
|
||||
sol::meta_function::multiplication,
|
||||
sol::overload(sol::resolve<Quatf(const Quatf&) const>(&Quatf::operator*),
|
||||
sol::resolve<Quatf(const float&) const>(&Quatf::operator*),
|
||||
[](float s, const Quatf& q)
|
||||
{
|
||||
return q * s;
|
||||
}),
|
||||
sol::meta_function::unary_minus, sol::resolve<Quatf() const>(&Quatf::operator-),
|
||||
sol::meta_function::equal_to, &Quatf::operator==, sol::meta_function::to_string,
|
||||
[](const Quatf& q)
|
||||
{
|
||||
return std::format("Quaternion({}, {}, {}, {})", q.x, q.y, q.z, q.w);
|
||||
},
|
||||
|
||||
"conjugate", &Quatf::conjugate, "dot", &Quatf::dot, "length", &Quatf::length, "length_sqr",
|
||||
&Quatf::length_sqr, "normalized", &Quatf::normalized, "inverse", &Quatf::inverse, "rotate",
|
||||
sol::resolve<Vec3f(const Vec3f&) const>(&Quatf::rotate), "to_rotation_matrix3",
|
||||
&Quatf::to_rotation_matrix3, "to_rotation_matrix4", &Quatf::to_rotation_matrix4,
|
||||
|
||||
"as_table",
|
||||
[](const Quatf& q, sol::this_state s) -> sol::table
|
||||
{
|
||||
sol::state_view lua(s);
|
||||
sol::table t = lua.create_table();
|
||||
t["x"] = q.x;
|
||||
t["y"] = q.y;
|
||||
t["z"] = q.z;
|
||||
t["w"] = q.w;
|
||||
return t;
|
||||
});
|
||||
}
|
||||
} // namespace omath::lua
|
||||
#endif
|
||||
@@ -0,0 +1,169 @@
|
||||
local function approx(a, b, eps) return math.abs(a - b) < (eps or 1e-4) end
|
||||
|
||||
local function cube_points()
|
||||
return {
|
||||
omath.Vec3.new(-1, -1, -1),
|
||||
omath.Vec3.new(1, -1, -1),
|
||||
omath.Vec3.new(-1, 1, -1),
|
||||
omath.Vec3.new(1, 1, -1),
|
||||
omath.Vec3.new(-1, -1, 1),
|
||||
omath.Vec3.new(1, -1, 1),
|
||||
omath.Vec3.new(-1, 1, 1),
|
||||
omath.Vec3.new(1, 1, 1),
|
||||
}
|
||||
end
|
||||
|
||||
function Collision_Aabb_constructor_and_fields()
|
||||
local aabb = omath.primitives.Aabb.new(omath.Vec3.new(-1, -2, -3), omath.Vec3.new(1, 2, 3))
|
||||
assert(aabb.min.x == -1 and aabb.max.z == 3)
|
||||
aabb.max = omath.Vec3.new(2, 3, 4)
|
||||
assert(aabb.max.x == 2 and aabb.max.y == 3 and aabb.max.z == 4)
|
||||
end
|
||||
|
||||
function Collision_Aabb_center_extents()
|
||||
local aabb = omath.primitives.Aabb.new(omath.Vec3.new(-1, -2, -3), omath.Vec3.new(3, 6, 9))
|
||||
local center = aabb:center()
|
||||
local extents = aabb:extents()
|
||||
assert(center.x == 1 and center.y == 2 and center.z == 3)
|
||||
assert(extents.x == 2 and extents.y == 4 and extents.z == 6)
|
||||
end
|
||||
|
||||
function Collision_Aabb_top_bottom_default_axis()
|
||||
local aabb = omath.primitives.Aabb.new(omath.Vec3.new(-1, -2, -3), omath.Vec3.new(3, 6, 9))
|
||||
assert(aabb:top().y == 6)
|
||||
assert(aabb:bottom().y == -2)
|
||||
end
|
||||
|
||||
function Collision_Aabb_top_bottom_explicit_axis()
|
||||
local aabb = omath.primitives.Aabb.new(omath.Vec3.new(-1, -2, -3), omath.Vec3.new(3, 6, 9))
|
||||
assert(aabb:top(omath.primitives.UpAxis.X).x == 3)
|
||||
assert(aabb:bottom(omath.primitives.UpAxis.Z).z == -3)
|
||||
end
|
||||
|
||||
function Collision_Aabb_is_collide()
|
||||
local a = omath.primitives.Aabb.new(omath.Vec3.new(-1, -1, -1), omath.Vec3.new(1, 1, 1))
|
||||
local b = omath.primitives.Aabb.new(omath.Vec3.new(0, 0, 0), omath.Vec3.new(2, 2, 2))
|
||||
local c = omath.primitives.Aabb.new(omath.Vec3.new(3, 3, 3), omath.Vec3.new(4, 4, 4))
|
||||
assert(a:is_collide(b))
|
||||
assert(not a:is_collide(c))
|
||||
end
|
||||
|
||||
function Collision_Aabb_as_table()
|
||||
local aabb = omath.primitives.Aabb.new(omath.Vec3.new(-1, -2, -3), omath.Vec3.new(1, 2, 3))
|
||||
local t = aabb:as_table()
|
||||
assert(t.min.x == -1 and t.max.z == 3)
|
||||
end
|
||||
|
||||
function Collision_Obb_constructor_and_vertices()
|
||||
local obb = omath.primitives.Obb.new(
|
||||
omath.Vec3.new(0, 0, 0),
|
||||
omath.Vec3.new(1, 0, 0),
|
||||
omath.Vec3.new(0, 1, 0),
|
||||
omath.Vec3.new(0, 0, 1),
|
||||
omath.Vec3.new(1, 2, 3)
|
||||
)
|
||||
local vertices = obb:vertices()
|
||||
assert(#vertices == 8)
|
||||
assert(vertices[1].x == -1 and vertices[1].y == -2 and vertices[1].z == -3)
|
||||
assert(vertices[8].x == 1 and vertices[8].y == 2 and vertices[8].z == 3)
|
||||
end
|
||||
|
||||
function Collision_Ray_constructor_and_direction()
|
||||
local ray = omath.collision.Ray.new(omath.Vec3.new(1, 2, 3), omath.Vec3.new(4, 6, 3), true)
|
||||
local dir = ray:direction_vector()
|
||||
local normal = ray:direction_vector_normalized()
|
||||
assert(ray.infinite_length)
|
||||
assert(dir.x == 3 and dir.y == 4 and dir.z == 0)
|
||||
assert(approx(normal:length(), 1))
|
||||
end
|
||||
|
||||
function Collision_LineTracer_triangle_hit()
|
||||
local tri = omath.Triangle.new(omath.Vec3.new(0, 0, 0), omath.Vec3.new(2, 0, 0), omath.Vec3.new(0, 2, 0))
|
||||
local ray = omath.collision.Ray.new(omath.Vec3.new(0.5, 0.5, -1), omath.Vec3.new(0.5, 0.5, 1))
|
||||
local hit = omath.collision.LineTracer.get_ray_hit_point(ray, tri)
|
||||
assert(omath.collision.LineTracer.ray_hits_triangle(ray, tri))
|
||||
assert(not omath.collision.LineTracer.can_trace_line(ray, tri))
|
||||
assert(approx(hit.x, 0.5) and approx(hit.y, 0.5) and approx(hit.z, 0))
|
||||
end
|
||||
|
||||
function Collision_LineTracer_triangle_miss()
|
||||
local tri = omath.Triangle.new(omath.Vec3.new(0, 0, 0), omath.Vec3.new(1, 0, 0), omath.Vec3.new(0, 1, 0))
|
||||
local ray = omath.collision.Ray.new(omath.Vec3.new(2, 2, -1), omath.Vec3.new(2, 2, 1))
|
||||
local hit = omath.collision.LineTracer.get_ray_hit_point(ray, tri)
|
||||
assert(not omath.collision.LineTracer.ray_hits_triangle(ray, tri))
|
||||
assert(omath.collision.LineTracer.can_trace_line(ray, tri))
|
||||
assert(hit == ray["end"])
|
||||
end
|
||||
|
||||
function Collision_LineTracer_aabb_hit()
|
||||
local aabb = omath.primitives.Aabb.new(omath.Vec3.new(-1, -1, -1), omath.Vec3.new(1, 1, 1))
|
||||
local ray = omath.collision.Ray.new(omath.Vec3.new(0, 0, -3), omath.Vec3.new(0, 0, 3))
|
||||
local hit = omath.collision.LineTracer.get_ray_hit_point(ray, aabb)
|
||||
assert(omath.collision.LineTracer.ray_hits_aabb(ray, aabb))
|
||||
assert(approx(hit.x, 0) and approx(hit.y, 0) and approx(hit.z, -1))
|
||||
end
|
||||
|
||||
function Collision_LineTracer_aabb_miss()
|
||||
local aabb = omath.primitives.Aabb.new(omath.Vec3.new(-1, -1, -1), omath.Vec3.new(1, 1, 1))
|
||||
local ray = omath.collision.Ray.new(omath.Vec3.new(0, 3, -3), omath.Vec3.new(0, 3, 3))
|
||||
local hit = omath.collision.LineTracer.get_ray_hit_point(ray, aabb)
|
||||
assert(not omath.collision.LineTracer.ray_hits_aabb(ray, aabb))
|
||||
assert(hit == ray["end"])
|
||||
end
|
||||
|
||||
function Collision_LineTracer_obb_hit()
|
||||
local obb = omath.primitives.Obb.new(
|
||||
omath.Vec3.new(0, 0, 0),
|
||||
omath.Vec3.new(1, 0, 0),
|
||||
omath.Vec3.new(0, 1, 0),
|
||||
omath.Vec3.new(0, 0, 1),
|
||||
omath.Vec3.new(1, 1, 1)
|
||||
)
|
||||
local ray = omath.collision.Ray.new(omath.Vec3.new(0, 0, -3), omath.Vec3.new(0, 0, 3))
|
||||
local hit = omath.collision.LineTracer.get_ray_hit_point(ray, obb)
|
||||
assert(omath.collision.LineTracer.ray_hits_obb(ray, obb))
|
||||
assert(approx(hit.z, -1))
|
||||
end
|
||||
|
||||
function Collision_ConvexCollider_vertices_and_support()
|
||||
local collider = omath.collision.ConvexCollider.new(cube_points(), omath.Vec3.new(2, 0, 0))
|
||||
assert(collider:vertex_count() == 8)
|
||||
assert(#collider:vertices() == 8)
|
||||
local support = collider:find_abs_furthest_vertex_position(omath.Vec3.new(1, 0, 0))
|
||||
assert(support.x == 3)
|
||||
end
|
||||
|
||||
function Collision_Gjk_support_vertex()
|
||||
local a = omath.collision.ConvexCollider.new(cube_points())
|
||||
local b = omath.collision.ConvexCollider.new(cube_points())
|
||||
local support = omath.collision.gjk_support_vertex(a, b, omath.Vec3.new(1, 0, 0))
|
||||
assert(support.x == 2 and support.y == 0 and support.z == 0)
|
||||
end
|
||||
|
||||
function Collision_Gjk_collide_true()
|
||||
local a = omath.collision.ConvexCollider.new(cube_points())
|
||||
local b = omath.collision.ConvexCollider.new(cube_points(), omath.Vec3.new(1, 0, 0))
|
||||
assert(omath.collision.gjk_collide(a, b))
|
||||
end
|
||||
|
||||
function Collision_Gjk_collide_false()
|
||||
local a = omath.collision.ConvexCollider.new(cube_points())
|
||||
local b = omath.collision.ConvexCollider.new(cube_points(), omath.Vec3.new(5, 0, 0))
|
||||
assert(not omath.collision.gjk_collide(a, b))
|
||||
end
|
||||
|
||||
function Collision_Epa_solve_hit()
|
||||
local a = omath.collision.ConvexCollider.new(cube_points())
|
||||
local b = omath.collision.ConvexCollider.new(cube_points(), omath.Vec3.new(1.5, 0, 0))
|
||||
local result = omath.collision.epa_solve(a, b)
|
||||
assert(result ~= nil)
|
||||
assert(result.depth > 0)
|
||||
assert(approx(result.normal:length(), 1))
|
||||
assert(approx(result.penetration_vector:length(), result.depth))
|
||||
end
|
||||
|
||||
function Collision_Epa_solve_miss()
|
||||
local a = omath.collision.ConvexCollider.new(cube_points())
|
||||
local b = omath.collision.ConvexCollider.new(cube_points(), omath.Vec3.new(5, 0, 0))
|
||||
assert(omath.collision.epa_solve(a, b) == nil)
|
||||
end
|
||||
@@ -0,0 +1,244 @@
|
||||
local function approx(a, b, eps) return math.abs(a - b) < (eps or 1e-4) end
|
||||
|
||||
local function color() return omath.Color.new(1, 0, 0, 1) end
|
||||
local function fill() return omath.Color.new(0, 0, 0, 0.5) end
|
||||
local function outline() return omath.Color.new(0, 0, 0, 1) end
|
||||
local function bg() return omath.Color.new(0.2, 0.2, 0.2, 1) end
|
||||
|
||||
local function renderer_with(commands)
|
||||
return omath.hud.Renderer.new({
|
||||
add_line = function(a, b, c, thickness)
|
||||
table.insert(commands, { kind = "line", a = a, b = b, color = c, thickness = thickness })
|
||||
end,
|
||||
add_polyline = function(points, c, thickness)
|
||||
table.insert(commands, { kind = "polyline", points = points, color = c, thickness = thickness })
|
||||
end,
|
||||
add_filled_polyline = function(points, c)
|
||||
table.insert(commands, { kind = "filled_polyline", points = points, color = c })
|
||||
end,
|
||||
add_rectangle = function(min, max, c)
|
||||
table.insert(commands, { kind = "rectangle", min = min, max = max, color = c })
|
||||
end,
|
||||
add_filled_rectangle = function(min, max, c)
|
||||
table.insert(commands, { kind = "filled_rectangle", min = min, max = max, color = c })
|
||||
end,
|
||||
add_circle = function(center, radius, c, thickness, segments)
|
||||
table.insert(commands, {
|
||||
kind = "circle",
|
||||
center = center,
|
||||
radius = radius,
|
||||
color = c,
|
||||
thickness = thickness,
|
||||
segments = segments,
|
||||
})
|
||||
end,
|
||||
add_filled_circle = function(center, radius, c, segments)
|
||||
table.insert(commands, { kind = "filled_circle", center = center, radius = radius, color = c, segments = segments })
|
||||
end,
|
||||
add_arc = function(center, radius, a_min, a_max, c, thickness, segments)
|
||||
table.insert(commands, {
|
||||
kind = "arc",
|
||||
center = center,
|
||||
radius = radius,
|
||||
a_min = a_min,
|
||||
a_max = a_max,
|
||||
color = c,
|
||||
thickness = thickness,
|
||||
segments = segments,
|
||||
})
|
||||
end,
|
||||
add_image = function(texture, min, max, tint)
|
||||
table.insert(commands, { kind = "image", texture = texture, min = min, max = max, tint = tint })
|
||||
end,
|
||||
add_text = function(pos, c, text)
|
||||
table.insert(commands, { kind = "text", pos = pos, color = c, text = text })
|
||||
end,
|
||||
calc_text_size = function(text)
|
||||
return omath.Vec2.new(#text * 6, 10)
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
local function overlay(commands)
|
||||
return omath.hud.EntityOverlay.new(omath.Vec2.new(100, 10), omath.Vec2.new(100, 50), renderer_with(commands))
|
||||
end
|
||||
|
||||
function Hud_CanvasBox_default_ratio()
|
||||
local box = omath.hud.CanvasBox.new(omath.Vec2.new(100, 10), omath.Vec2.new(100, 50))
|
||||
assert(approx(box.top_left_corner.x, 90) and approx(box.top_left_corner.y, 10))
|
||||
assert(approx(box.top_right_corner.x, 110) and approx(box.bottom_right_corner.y, 50))
|
||||
end
|
||||
|
||||
function Hud_CanvasBox_custom_ratio()
|
||||
local box = omath.hud.CanvasBox.new(omath.Vec2.new(100, 10), omath.Vec2.new(100, 50), 2)
|
||||
assert(approx(box.top_left_corner.x, 80) and approx(box.bottom_right_corner.x, 120))
|
||||
end
|
||||
|
||||
function Hud_CanvasBox_as_table()
|
||||
local points = omath.hud.CanvasBox.new(omath.Vec2.new(100, 10), omath.Vec2.new(100, 50)):as_table()
|
||||
assert(#points == 4)
|
||||
assert(approx(points[1].x, 90) and approx(points[3].x, 110))
|
||||
end
|
||||
|
||||
function Hud_Renderer_callbacks()
|
||||
local commands = {}
|
||||
local renderer = renderer_with(commands)
|
||||
renderer:add_line(omath.Vec2.new(1, 2), omath.Vec2.new(3, 4), color(), 2)
|
||||
renderer:add_text(omath.Vec2.new(5, 6), color(), "hp")
|
||||
local size = renderer:calc_text_size("abcd")
|
||||
assert(#commands == 2)
|
||||
assert(commands[1].kind == "line" and commands[2].text == "hp")
|
||||
assert(size.x == 24 and size.y == 10)
|
||||
end
|
||||
|
||||
function Hud_Renderer_polyline_table()
|
||||
local commands = {}
|
||||
local renderer = renderer_with(commands)
|
||||
renderer:add_polyline({ omath.Vec2.new(1, 1), omath.Vec2.new(2, 2) }, color(), 3)
|
||||
assert(commands[1].kind == "polyline")
|
||||
assert(#commands[1].points == 2 and commands[1].points[2].x == 2)
|
||||
end
|
||||
|
||||
function Hud_Renderer_circle_defaults()
|
||||
local commands = {}
|
||||
local renderer = renderer_with(commands)
|
||||
renderer:add_circle(omath.Vec2.new(1, 2), 5, color(), 2)
|
||||
renderer:add_filled_circle(omath.Vec2.new(3, 4), 6, color())
|
||||
assert(commands[1].kind == "circle" and commands[1].segments == 0)
|
||||
assert(commands[2].kind == "filled_circle" and commands[2].segments == 0)
|
||||
end
|
||||
|
||||
function Hud_EntityOverlay_add_2d_box()
|
||||
local commands = {}
|
||||
overlay(commands):add_2d_box(color(), fill(), 2)
|
||||
assert(#commands == 2)
|
||||
assert(commands[1].kind == "polyline" and #commands[1].points == 4)
|
||||
assert(commands[2].kind == "filled_polyline")
|
||||
end
|
||||
|
||||
function Hud_EntityOverlay_add_cornered_2d_box()
|
||||
local commands = {}
|
||||
overlay(commands):add_cornered_2d_box(color(), fill(), 0.25, 2)
|
||||
assert(#commands == 10)
|
||||
assert(commands[1].kind == "polyline" and commands[10].kind == "line")
|
||||
end
|
||||
|
||||
function Hud_EntityOverlay_add_dashed_box()
|
||||
local commands = {}
|
||||
overlay(commands):add_dashed_box(color(), 8, 5, 1)
|
||||
assert(#commands > 4)
|
||||
assert(commands[1].kind == "line")
|
||||
end
|
||||
|
||||
function Hud_EntityOverlay_add_bar()
|
||||
local commands = {}
|
||||
overlay(commands):add_right_bar(color(), outline(), bg(), 4, 0.5)
|
||||
assert(#commands == 3)
|
||||
assert(commands[1].kind == "filled_rectangle" and commands[2].kind == "filled_rectangle")
|
||||
assert(commands[3].kind == "rectangle")
|
||||
end
|
||||
|
||||
function Hud_EntityOverlay_add_dashed_bar()
|
||||
local commands = {}
|
||||
overlay(commands):add_bottom_dashed_bar(color(), outline(), bg(), 4, 0.5, 8, 5)
|
||||
assert(#commands >= 3)
|
||||
assert(commands[1].kind == "filled_rectangle")
|
||||
end
|
||||
|
||||
function Hud_EntityOverlay_add_label()
|
||||
local commands = {}
|
||||
overlay(commands):add_right_label(color(), 5, false, "HP")
|
||||
assert(#commands == 1)
|
||||
assert(commands[1].kind == "text" and commands[1].text == "HP")
|
||||
assert(approx(commands[1].pos.x, 115))
|
||||
end
|
||||
|
||||
function Hud_EntityOverlay_add_outlined_label()
|
||||
local commands = {}
|
||||
overlay(commands):add_left_label(color(), 5, true, "HP")
|
||||
assert(#commands == 9)
|
||||
assert(commands[9].kind == "text" and commands[9].text == "HP")
|
||||
end
|
||||
|
||||
function Hud_EntityOverlay_add_centered_label()
|
||||
local commands = {}
|
||||
overlay(commands):add_centered_top_label(color(), 5, false, "HP")
|
||||
assert(#commands == 1)
|
||||
assert(commands[1].kind == "text")
|
||||
assert(approx(commands[1].pos.x, 94))
|
||||
end
|
||||
|
||||
function Hud_EntityOverlay_add_spaces()
|
||||
local commands = {}
|
||||
overlay(commands):add_right_space_vertical(10):add_right_label(color(), 5, false, "HP")
|
||||
assert(#commands == 1)
|
||||
assert(approx(commands[1].pos.y, 20))
|
||||
end
|
||||
|
||||
function Hud_EntityOverlay_add_progress_ring()
|
||||
local commands = {}
|
||||
overlay(commands):add_right_progress_ring(color(), bg(), 6, 0.25, 2, 5, 16)
|
||||
assert(#commands == 2)
|
||||
assert(commands[1].kind == "circle" and commands[2].kind == "arc")
|
||||
assert(commands[2].segments == 16)
|
||||
end
|
||||
|
||||
function Hud_EntityOverlay_add_progress_ring_defaults()
|
||||
local commands = {}
|
||||
overlay(commands):add_right_progress_ring(color(), bg(), 6, 0.25)
|
||||
assert(#commands == 2)
|
||||
assert(commands[1].thickness == 2 and commands[1].segments == 0)
|
||||
end
|
||||
|
||||
function Hud_EntityOverlay_add_icon()
|
||||
local commands = {}
|
||||
overlay(commands):add_right_icon("texture-id", 8, 6, color(), 5)
|
||||
assert(#commands == 1)
|
||||
assert(commands[1].kind == "image" and commands[1].texture == "texture-id")
|
||||
end
|
||||
|
||||
function Hud_EntityOverlay_add_snap_line()
|
||||
local commands = {}
|
||||
overlay(commands):add_snap_line(omath.Vec2.new(0, 0), color(), 2)
|
||||
assert(#commands == 1)
|
||||
assert(commands[1].kind == "line")
|
||||
assert(approx(commands[1].b.x, 100) and approx(commands[1].b.y, 50))
|
||||
end
|
||||
|
||||
function Hud_EntityOverlay_add_skeleton()
|
||||
local commands = {}
|
||||
overlay(commands):add_skeleton(color())
|
||||
assert(#commands == 15)
|
||||
assert(commands[1].kind == "line")
|
||||
end
|
||||
|
||||
function Hud_EntityOverlay_add_scan_marker()
|
||||
local commands = {}
|
||||
overlay(commands):add_scan_marker(color(), outline(), 2)
|
||||
assert(#commands == 2)
|
||||
assert(commands[1].kind == "filled_polyline" and #commands[1].points == 3)
|
||||
assert(commands[2].kind == "polyline" and commands[2].thickness == 2)
|
||||
end
|
||||
|
||||
function Hud_EntityOverlay_add_aim_dot()
|
||||
local commands = {}
|
||||
overlay(commands):add_aim_dot(omath.Vec2.new(5, 6), color())
|
||||
assert(#commands == 1)
|
||||
assert(commands[1].kind == "filled_circle" and commands[1].radius == 3)
|
||||
end
|
||||
|
||||
function Hud_EntityOverlay_add_projectile_aim_circle()
|
||||
local commands = {}
|
||||
overlay(commands):add_projectile_aim(omath.Vec2.new(5, 6), color(), 4, 2, omath.hud.ProjectileAimFigure.CIRCLE)
|
||||
assert(#commands == 2)
|
||||
assert(commands[1].kind == "line")
|
||||
assert(commands[2].kind == "filled_circle" and commands[2].radius == 4)
|
||||
end
|
||||
|
||||
function Hud_EntityOverlay_add_projectile_aim_square_default()
|
||||
local commands = {}
|
||||
overlay(commands):add_projectile_aim(omath.Vec2.new(5, 6), color())
|
||||
assert(#commands == 2)
|
||||
assert(commands[1].kind == "line")
|
||||
assert(commands[2].kind == "filled_rectangle")
|
||||
end
|
||||
@@ -0,0 +1,195 @@
|
||||
local function approx(a, b, eps) return math.abs(a - b) < (eps or 1e-5) end
|
||||
|
||||
function Mat4_Constructor_default()
|
||||
local m = omath.Mat4.new()
|
||||
assert(m:row_count() == 4 and m:columns_count() == 4)
|
||||
assert(m:at(0, 0) == 0 and m:at(3, 3) == 0)
|
||||
end
|
||||
|
||||
function Mat4_FromRows()
|
||||
local m = omath.Mat4.from_rows({
|
||||
{1, 2, 3, 4},
|
||||
{5, 6, 7, 8},
|
||||
{9, 10, 11, 12},
|
||||
{13, 14, 15, 16},
|
||||
})
|
||||
assert(m:at(0, 0) == 1 and m:at(3, 3) == 16)
|
||||
end
|
||||
|
||||
function Mat4_SetAt()
|
||||
local m = omath.Mat4.new()
|
||||
m:set_at(2, 3, 42)
|
||||
assert(m:at(2, 3) == 42)
|
||||
end
|
||||
|
||||
function Mat4_SetAndClear()
|
||||
local m = omath.Mat4.new()
|
||||
m:set(2)
|
||||
assert(m:sum() == 32)
|
||||
m:clear()
|
||||
assert(m:sum() == 0)
|
||||
end
|
||||
|
||||
function Mat4_Multiplication_matrix()
|
||||
local identity = omath.Mat4.from_rows({
|
||||
{1, 0, 0, 0},
|
||||
{0, 1, 0, 0},
|
||||
{0, 0, 1, 0},
|
||||
{0, 0, 0, 1},
|
||||
})
|
||||
local m = omath.Mat4.from_rows({
|
||||
{1, 2, 3, 4},
|
||||
{5, 6, 7, 8},
|
||||
{9, 10, 11, 12},
|
||||
{13, 14, 15, 16},
|
||||
})
|
||||
local result = m * identity
|
||||
assert(result == m)
|
||||
end
|
||||
|
||||
function Mat4_Multiplication_scalar()
|
||||
local m = omath.Mat4.from_rows({
|
||||
{1, 0, 0, 0},
|
||||
{0, 1, 0, 0},
|
||||
{0, 0, 1, 0},
|
||||
{0, 0, 0, 1},
|
||||
}) * 2
|
||||
assert(m:at(0, 0) == 2 and m:at(3, 3) == 2)
|
||||
end
|
||||
|
||||
function Mat4_Multiplication_scalar_reversed()
|
||||
local m = 2 * omath.Mat4.from_rows({
|
||||
{1, 0, 0, 0},
|
||||
{0, 1, 0, 0},
|
||||
{0, 0, 1, 0},
|
||||
{0, 0, 0, 1},
|
||||
})
|
||||
assert(m:at(0, 0) == 2 and m:at(3, 3) == 2)
|
||||
end
|
||||
|
||||
function Mat4_Division_scalar()
|
||||
local m = omath.Mat4.from_rows({
|
||||
{2, 0, 0, 0},
|
||||
{0, 2, 0, 0},
|
||||
{0, 0, 2, 0},
|
||||
{0, 0, 0, 2},
|
||||
}) / 2
|
||||
assert(m:at(0, 0) == 1 and m:at(3, 3) == 1)
|
||||
end
|
||||
|
||||
function Mat4_Transposed()
|
||||
local m = omath.Mat4.from_rows({
|
||||
{1, 2, 0, 0},
|
||||
{3, 4, 0, 0},
|
||||
{0, 0, 1, 0},
|
||||
{0, 0, 0, 1},
|
||||
}):transposed()
|
||||
assert(m:at(0, 1) == 3 and m:at(1, 0) == 2)
|
||||
end
|
||||
|
||||
function Mat4_Determinant()
|
||||
local m = omath.Mat4.from_rows({
|
||||
{1, 0, 0, 0},
|
||||
{0, 2, 0, 0},
|
||||
{0, 0, 3, 0},
|
||||
{0, 0, 0, 4},
|
||||
})
|
||||
assert(m:determinant() == 24)
|
||||
end
|
||||
|
||||
function Mat4_Inverted_success()
|
||||
local m = omath.Mat4.from_rows({
|
||||
{2, 0, 0, 0},
|
||||
{0, 2, 0, 0},
|
||||
{0, 0, 2, 0},
|
||||
{0, 0, 0, 2},
|
||||
})
|
||||
local inv = m:inverted()
|
||||
assert(inv ~= nil)
|
||||
assert(approx(inv:at(0, 0), 0.5) and approx(inv:at(3, 3), 0.5))
|
||||
end
|
||||
|
||||
function Mat4_Inverted_singular()
|
||||
local m = omath.Mat4.new()
|
||||
assert(m:inverted() == nil)
|
||||
end
|
||||
|
||||
function Mat4_ToScreenMat()
|
||||
local m = omath.Mat4.to_screen_mat(800, 600)
|
||||
assert(m:at(0, 0) == 400 and m:at(1, 1) == -300 and m:at(3, 1) == 300)
|
||||
end
|
||||
|
||||
function Mat4_Translation()
|
||||
local m = omath.Mat4.translation(omath.Vec3.new(1, 2, 3))
|
||||
assert(m:at(0, 3) == 1 and m:at(1, 3) == 2 and m:at(2, 3) == 3)
|
||||
end
|
||||
|
||||
function Mat4_Scale()
|
||||
local m = omath.Mat4.scale(omath.Vec3.new(2, 3, 4))
|
||||
assert(m:at(0, 0) == 2 and m:at(1, 1) == 3 and m:at(2, 2) == 4)
|
||||
end
|
||||
|
||||
function Mat4_Perspective()
|
||||
local m = omath.Mat4.perspective_left_handed_vertical_fov(90, 16 / 9, 0.1, 1000)
|
||||
assert(m:at(0, 0) > 0 and m:at(1, 1) > 0)
|
||||
end
|
||||
|
||||
function Mat4_AsTable()
|
||||
local m = omath.Mat4.from_rows({
|
||||
{1, 2, 3, 4},
|
||||
{5, 6, 7, 8},
|
||||
{9, 10, 11, 12},
|
||||
{13, 14, 15, 16},
|
||||
})
|
||||
local t = m:as_table()
|
||||
assert(t[1][1] == 1 and t[4][4] == 16)
|
||||
end
|
||||
|
||||
function Mat4_ToString()
|
||||
local m = omath.Mat4.from_rows({
|
||||
{1, 2, 0, 0},
|
||||
{0, 1, 0, 0},
|
||||
{0, 0, 1, 0},
|
||||
{0, 0, 0, 1},
|
||||
})
|
||||
assert(string.find(tostring(m), "%[%[") ~= nil)
|
||||
end
|
||||
|
||||
function Mat3_FromRows()
|
||||
local m = omath.Mat3.from_rows({
|
||||
{1, 2, 3},
|
||||
{0, 1, 4},
|
||||
{5, 6, 0},
|
||||
})
|
||||
assert(m:determinant() == 1)
|
||||
end
|
||||
|
||||
function Mat4ColumnMajor_FromRows()
|
||||
local m = omath.Mat4ColumnMajor.from_rows({
|
||||
{1, 2, 3, 4},
|
||||
{5, 6, 7, 8},
|
||||
{9, 10, 11, 12},
|
||||
{13, 14, 15, 16},
|
||||
})
|
||||
assert(m:at(0, 1) == 2 and m:at(3, 2) == 15)
|
||||
end
|
||||
|
||||
function Mat4ColumnMajor_ToScreenMat()
|
||||
local m = omath.Mat4ColumnMajor.to_screen_mat(800, 600)
|
||||
assert(m:at(0, 0) == 400 and m:at(1, 1) == -300 and m:at(3, 1) == 300)
|
||||
end
|
||||
|
||||
function Mat4ColumnMajor_Translation()
|
||||
local m = omath.Mat4ColumnMajor.translation(omath.Vec3.new(1, 2, 3))
|
||||
assert(m:at(0, 3) == 1 and m:at(1, 3) == 2 and m:at(2, 3) == 3)
|
||||
end
|
||||
|
||||
function Mat4d_FromRows()
|
||||
local m = omath.Mat4d.from_rows({
|
||||
{1, 0, 0, 0},
|
||||
{0, 2, 0, 0},
|
||||
{0, 0, 3, 0},
|
||||
{0, 0, 0, 4},
|
||||
})
|
||||
assert(m:determinant() == 24)
|
||||
end
|
||||
@@ -0,0 +1,111 @@
|
||||
local function approx(a, b, eps) return math.abs(a - b) < (eps or 1e-5) end
|
||||
|
||||
function Quaternion_Constructor_default()
|
||||
local q = omath.Quaternion.new()
|
||||
assert(q.x == 0 and q.y == 0 and q.z == 0 and q.w == 1)
|
||||
end
|
||||
|
||||
function Quaternion_Constructor_xyzw()
|
||||
local q = omath.Quaternion.new(1, 2, 3, 4)
|
||||
assert(q.x == 1 and q.y == 2 and q.z == 3 and q.w == 4)
|
||||
end
|
||||
|
||||
function Quaternion_Field_mutation()
|
||||
local q = omath.Quaternion.new()
|
||||
q.x = 1; q.y = 2; q.z = 3; q.w = 4
|
||||
assert(q.x == 1 and q.y == 2 and q.z == 3 and q.w == 4)
|
||||
end
|
||||
|
||||
function Quaternion_FromAxisAngle_zero_angle()
|
||||
local q = omath.Quaternion.from_axis_angle(omath.Vec3.new(0, 0, 1), 0)
|
||||
assert(approx(q.x, 0) and approx(q.y, 0) and approx(q.z, 0) and approx(q.w, 1))
|
||||
end
|
||||
|
||||
function Quaternion_Addition()
|
||||
local q = omath.Quaternion.new(1, 2, 3, 4) + omath.Quaternion.new(4, 3, 2, 1)
|
||||
assert(q.x == 5 and q.y == 5 and q.z == 5 and q.w == 5)
|
||||
end
|
||||
|
||||
function Quaternion_Multiplication_scalar()
|
||||
local q = omath.Quaternion.new(1, 2, 3, 4) * 2
|
||||
assert(q.x == 2 and q.y == 4 and q.z == 6 and q.w == 8)
|
||||
end
|
||||
|
||||
function Quaternion_Multiplication_scalar_reversed()
|
||||
local q = 2 * omath.Quaternion.new(1, 2, 3, 4)
|
||||
assert(q.x == 2 and q.y == 4 and q.z == 6 and q.w == 8)
|
||||
end
|
||||
|
||||
function Quaternion_Multiplication_quaternion()
|
||||
local i = omath.Quaternion.new(1, 0, 0, 0)
|
||||
local j = omath.Quaternion.new(0, 1, 0, 0)
|
||||
local k = i * j
|
||||
assert(k.x == 0 and k.y == 0 and k.z == 1 and k.w == 0)
|
||||
end
|
||||
|
||||
function Quaternion_UnaryMinus()
|
||||
local q = -omath.Quaternion.new(1, -2, 3, -4)
|
||||
assert(q.x == -1 and q.y == 2 and q.z == -3 and q.w == 4)
|
||||
end
|
||||
|
||||
function Quaternion_EqualTo_true()
|
||||
assert(omath.Quaternion.new(1, 2, 3, 4) == omath.Quaternion.new(1, 2, 3, 4))
|
||||
end
|
||||
|
||||
function Quaternion_EqualTo_false()
|
||||
assert(not (omath.Quaternion.new(1, 2, 3, 4) == omath.Quaternion.new(1, 2, 3, 5)))
|
||||
end
|
||||
|
||||
function Quaternion_ToString()
|
||||
assert(tostring(omath.Quaternion.new(1, 2, 3, 4)) == "Quaternion(1, 2, 3, 4)")
|
||||
end
|
||||
|
||||
function Quaternion_Conjugate()
|
||||
local q = omath.Quaternion.new(1, 2, 3, 4):conjugate()
|
||||
assert(q.x == -1 and q.y == -2 and q.z == -3 and q.w == 4)
|
||||
end
|
||||
|
||||
function Quaternion_Dot()
|
||||
assert(omath.Quaternion.new(1, 0, 0, 0):dot(omath.Quaternion.new(0, 1, 0, 0)) == 0)
|
||||
end
|
||||
|
||||
function Quaternion_Length()
|
||||
assert(approx(omath.Quaternion.new(1, 1, 1, 1):length(), 2))
|
||||
end
|
||||
|
||||
function Quaternion_LengthSqr()
|
||||
assert(omath.Quaternion.new(1, 2, 3, 4):length_sqr() == 30)
|
||||
end
|
||||
|
||||
function Quaternion_Normalized()
|
||||
local q = omath.Quaternion.new(1, 1, 1, 1):normalized()
|
||||
assert(approx(q:length(), 1))
|
||||
assert(approx(q.x, 0.5) and approx(q.y, 0.5) and approx(q.z, 0.5) and approx(q.w, 0.5))
|
||||
end
|
||||
|
||||
function Quaternion_Inverse()
|
||||
local q = omath.Quaternion.from_axis_angle(omath.Vec3.new(0, 0, 1), math.pi / 3)
|
||||
local identity = q * q:inverse()
|
||||
assert(approx(identity.x, 0) and approx(identity.y, 0) and approx(identity.z, 0) and approx(identity.w, 1))
|
||||
end
|
||||
|
||||
function Quaternion_Rotate()
|
||||
local q = omath.Quaternion.from_axis_angle(omath.Vec3.new(0, 0, 1), math.pi / 2)
|
||||
local v = q:rotate(omath.Vec3.new(1, 0, 0))
|
||||
assert(approx(v.x, 0, 1e-4) and approx(v.y, 1, 1e-4) and approx(v.z, 0, 1e-4))
|
||||
end
|
||||
|
||||
function Quaternion_ToRotationMatrix3()
|
||||
local m = omath.Quaternion.new():to_rotation_matrix3()
|
||||
assert(m:at(0, 0) == 1 and m:at(1, 1) == 1 and m:at(2, 2) == 1)
|
||||
end
|
||||
|
||||
function Quaternion_ToRotationMatrix4()
|
||||
local m = omath.Quaternion.new():to_rotation_matrix4()
|
||||
assert(m:at(0, 0) == 1 and m:at(1, 1) == 1 and m:at(2, 2) == 1 and m:at(3, 3) == 1)
|
||||
end
|
||||
|
||||
function Quaternion_AsTable()
|
||||
local t = omath.Quaternion.new(1, 2, 3, 4):as_table()
|
||||
assert(t.x == 1 and t.y == 2 and t.z == 3 and t.w == 4)
|
||||
end
|
||||
@@ -175,6 +175,46 @@ function Source_Camera_get_up()
|
||||
assert(approx(make_camera():get_up():length(), 1.0))
|
||||
end
|
||||
|
||||
function Source_Camera_get_view_matrix()
|
||||
local view = make_camera():get_view_matrix()
|
||||
assert(view ~= nil)
|
||||
assert(view:row_count() == 4 and view:columns_count() == 4)
|
||||
end
|
||||
|
||||
function Source_Camera_get_projection_matrix()
|
||||
local projection = make_camera():get_projection_matrix()
|
||||
assert(projection ~= nil)
|
||||
assert(projection:at(0, 0) > 0 and projection:at(1, 1) > 0)
|
||||
end
|
||||
|
||||
function Source_Camera_get_view_projection_matrix()
|
||||
local view_projection = make_camera():get_view_projection_matrix()
|
||||
assert(view_projection ~= nil)
|
||||
assert(view_projection:row_count() == 4 and view_projection:columns_count() == 4)
|
||||
end
|
||||
|
||||
function Source_Camera_extract_projection_params()
|
||||
local projection = make_camera():get_projection_matrix()
|
||||
local fov, aspect_ratio = omath.source.Camera.extract_projection_params(projection)
|
||||
assert(fov ~= nil)
|
||||
assert(approx(aspect_ratio, 1920 / 1080))
|
||||
end
|
||||
|
||||
function Source_Camera_calc_view_angles_from_view_matrix()
|
||||
local cam = make_camera()
|
||||
local view = cam:get_view_matrix()
|
||||
local angles = omath.source.Camera.calc_view_angles_from_view_matrix(view)
|
||||
assert(approx(angles.pitch:as_degrees(), 0))
|
||||
assert(approx(angles.yaw:as_degrees(), 0))
|
||||
end
|
||||
|
||||
function Source_Camera_calc_origin_from_view_matrix()
|
||||
local cam = make_camera()
|
||||
cam:set_origin(omath.Vec3.new(1, 2, 3))
|
||||
local origin = omath.source.Camera.calc_origin_from_view_matrix(cam:get_view_matrix())
|
||||
assert(approx(origin.x, 1) and approx(origin.y, 2) and approx(origin.z, 3))
|
||||
end
|
||||
|
||||
function Source_Camera_world_to_screen_success()
|
||||
local cam = make_camera()
|
||||
cam:look_at(omath.Vec3.new(1, 0, 0))
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
//
|
||||
// Created by orange on 07.03.2026.
|
||||
//
|
||||
#include <gtest/gtest.h>
|
||||
#include <lua.hpp>
|
||||
#include <omath/lua/lua.hpp>
|
||||
|
||||
class LuaCollision : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
lua_State* L = nullptr;
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
L = luaL_newstate();
|
||||
luaL_openlibs(L);
|
||||
omath::lua::LuaInterpreter::register_lib(L);
|
||||
if (luaL_dofile(L, LUA_SCRIPTS_DIR "/collision_tests.lua") != LUA_OK)
|
||||
FAIL() << lua_tostring(L, -1);
|
||||
}
|
||||
|
||||
void TearDown() override { lua_close(L); }
|
||||
|
||||
void check(const char* func_name)
|
||||
{
|
||||
lua_getglobal(L, func_name);
|
||||
if (lua_pcall(L, 0, 0, 0) != LUA_OK)
|
||||
{
|
||||
FAIL() << lua_tostring(L, -1);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(LuaCollision, Aabb_constructor_and_fields) { check("Collision_Aabb_constructor_and_fields"); }
|
||||
TEST_F(LuaCollision, Aabb_center_extents) { check("Collision_Aabb_center_extents"); }
|
||||
TEST_F(LuaCollision, Aabb_top_bottom_default_axis) { check("Collision_Aabb_top_bottom_default_axis"); }
|
||||
TEST_F(LuaCollision, Aabb_top_bottom_explicit_axis) { check("Collision_Aabb_top_bottom_explicit_axis"); }
|
||||
TEST_F(LuaCollision, Aabb_is_collide) { check("Collision_Aabb_is_collide"); }
|
||||
TEST_F(LuaCollision, Aabb_as_table) { check("Collision_Aabb_as_table"); }
|
||||
TEST_F(LuaCollision, Obb_constructor_and_vertices) { check("Collision_Obb_constructor_and_vertices"); }
|
||||
TEST_F(LuaCollision, Ray_constructor_and_direction) { check("Collision_Ray_constructor_and_direction"); }
|
||||
TEST_F(LuaCollision, LineTracer_triangle_hit) { check("Collision_LineTracer_triangle_hit"); }
|
||||
TEST_F(LuaCollision, LineTracer_triangle_miss) { check("Collision_LineTracer_triangle_miss"); }
|
||||
TEST_F(LuaCollision, LineTracer_aabb_hit) { check("Collision_LineTracer_aabb_hit"); }
|
||||
TEST_F(LuaCollision, LineTracer_aabb_miss) { check("Collision_LineTracer_aabb_miss"); }
|
||||
TEST_F(LuaCollision, LineTracer_obb_hit) { check("Collision_LineTracer_obb_hit"); }
|
||||
TEST_F(LuaCollision, ConvexCollider_vertices_support) { check("Collision_ConvexCollider_vertices_and_support"); }
|
||||
TEST_F(LuaCollision, Gjk_support_vertex) { check("Collision_Gjk_support_vertex"); }
|
||||
TEST_F(LuaCollision, Gjk_collide_true) { check("Collision_Gjk_collide_true"); }
|
||||
TEST_F(LuaCollision, Gjk_collide_false) { check("Collision_Gjk_collide_false"); }
|
||||
TEST_F(LuaCollision, Epa_solve_hit) { check("Collision_Epa_solve_hit"); }
|
||||
TEST_F(LuaCollision, Epa_solve_miss) { check("Collision_Epa_solve_miss"); }
|
||||
@@ -0,0 +1,58 @@
|
||||
//
|
||||
// Created by orange on 07.03.2026.
|
||||
//
|
||||
#include <gtest/gtest.h>
|
||||
#include <lua.hpp>
|
||||
#include <omath/lua/lua.hpp>
|
||||
|
||||
class LuaHud : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
lua_State* L = nullptr;
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
L = luaL_newstate();
|
||||
luaL_openlibs(L);
|
||||
omath::lua::LuaInterpreter::register_lib(L);
|
||||
if (luaL_dofile(L, LUA_SCRIPTS_DIR "/hud_tests.lua") != LUA_OK)
|
||||
FAIL() << lua_tostring(L, -1);
|
||||
}
|
||||
|
||||
void TearDown() override { lua_close(L); }
|
||||
|
||||
void check(const char* func_name)
|
||||
{
|
||||
lua_getglobal(L, func_name);
|
||||
if (lua_pcall(L, 0, 0, 0) != LUA_OK)
|
||||
{
|
||||
FAIL() << lua_tostring(L, -1);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(LuaHud, CanvasBox_default_ratio) { check("Hud_CanvasBox_default_ratio"); }
|
||||
TEST_F(LuaHud, CanvasBox_custom_ratio) { check("Hud_CanvasBox_custom_ratio"); }
|
||||
TEST_F(LuaHud, CanvasBox_as_table) { check("Hud_CanvasBox_as_table"); }
|
||||
TEST_F(LuaHud, Renderer_callbacks) { check("Hud_Renderer_callbacks"); }
|
||||
TEST_F(LuaHud, Renderer_polyline_table) { check("Hud_Renderer_polyline_table"); }
|
||||
TEST_F(LuaHud, Renderer_circle_defaults) { check("Hud_Renderer_circle_defaults"); }
|
||||
TEST_F(LuaHud, EntityOverlay_add_2d_box) { check("Hud_EntityOverlay_add_2d_box"); }
|
||||
TEST_F(LuaHud, EntityOverlay_add_cornered_2d_box) { check("Hud_EntityOverlay_add_cornered_2d_box"); }
|
||||
TEST_F(LuaHud, EntityOverlay_add_dashed_box) { check("Hud_EntityOverlay_add_dashed_box"); }
|
||||
TEST_F(LuaHud, EntityOverlay_add_bar) { check("Hud_EntityOverlay_add_bar"); }
|
||||
TEST_F(LuaHud, EntityOverlay_add_dashed_bar) { check("Hud_EntityOverlay_add_dashed_bar"); }
|
||||
TEST_F(LuaHud, EntityOverlay_add_label) { check("Hud_EntityOverlay_add_label"); }
|
||||
TEST_F(LuaHud, EntityOverlay_add_outlined_label) { check("Hud_EntityOverlay_add_outlined_label"); }
|
||||
TEST_F(LuaHud, EntityOverlay_add_centered_label) { check("Hud_EntityOverlay_add_centered_label"); }
|
||||
TEST_F(LuaHud, EntityOverlay_add_spaces) { check("Hud_EntityOverlay_add_spaces"); }
|
||||
TEST_F(LuaHud, EntityOverlay_add_progress_ring) { check("Hud_EntityOverlay_add_progress_ring"); }
|
||||
TEST_F(LuaHud, EntityOverlay_add_progress_ring_defaults) { check("Hud_EntityOverlay_add_progress_ring_defaults"); }
|
||||
TEST_F(LuaHud, EntityOverlay_add_icon) { check("Hud_EntityOverlay_add_icon"); }
|
||||
TEST_F(LuaHud, EntityOverlay_add_snap_line) { check("Hud_EntityOverlay_add_snap_line"); }
|
||||
TEST_F(LuaHud, EntityOverlay_add_skeleton) { check("Hud_EntityOverlay_add_skeleton"); }
|
||||
TEST_F(LuaHud, EntityOverlay_add_scan_marker) { check("Hud_EntityOverlay_add_scan_marker"); }
|
||||
TEST_F(LuaHud, EntityOverlay_add_aim_dot) { check("Hud_EntityOverlay_add_aim_dot"); }
|
||||
TEST_F(LuaHud, EntityOverlay_add_projectile_aim_circle) { check("Hud_EntityOverlay_add_projectile_aim_circle"); }
|
||||
TEST_F(LuaHud, EntityOverlay_add_projectile_aim_square_default) { check("Hud_EntityOverlay_add_projectile_aim_square_default"); }
|
||||
@@ -0,0 +1,129 @@
|
||||
//
|
||||
// Created by orange on 07.03.2026.
|
||||
//
|
||||
#include <gtest/gtest.h>
|
||||
#include <lua.hpp>
|
||||
#include <omath/lua/lua.hpp>
|
||||
|
||||
class LuaMat : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
lua_State* L = nullptr;
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
L = luaL_newstate();
|
||||
luaL_openlibs(L);
|
||||
omath::lua::LuaInterpreter::register_lib(L);
|
||||
if (luaL_dofile(L, LUA_SCRIPTS_DIR "/mat_tests.lua") != LUA_OK)
|
||||
FAIL() << lua_tostring(L, -1);
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
lua_close(L);
|
||||
}
|
||||
|
||||
void check(const char* func_name)
|
||||
{
|
||||
lua_getglobal(L, func_name);
|
||||
if (lua_pcall(L, 0, 0, 0) != LUA_OK)
|
||||
{
|
||||
FAIL() << lua_tostring(L, -1);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(LuaMat, Constructor_default)
|
||||
{
|
||||
check("Mat4_Constructor_default");
|
||||
}
|
||||
TEST_F(LuaMat, FromRows)
|
||||
{
|
||||
check("Mat4_FromRows");
|
||||
}
|
||||
TEST_F(LuaMat, SetAt)
|
||||
{
|
||||
check("Mat4_SetAt");
|
||||
}
|
||||
TEST_F(LuaMat, SetAndClear)
|
||||
{
|
||||
check("Mat4_SetAndClear");
|
||||
}
|
||||
TEST_F(LuaMat, Multiplication_matrix)
|
||||
{
|
||||
check("Mat4_Multiplication_matrix");
|
||||
}
|
||||
TEST_F(LuaMat, Multiplication_scalar)
|
||||
{
|
||||
check("Mat4_Multiplication_scalar");
|
||||
}
|
||||
TEST_F(LuaMat, Multiplication_scalar_reversed)
|
||||
{
|
||||
check("Mat4_Multiplication_scalar_reversed");
|
||||
}
|
||||
TEST_F(LuaMat, Division_scalar)
|
||||
{
|
||||
check("Mat4_Division_scalar");
|
||||
}
|
||||
TEST_F(LuaMat, Transposed)
|
||||
{
|
||||
check("Mat4_Transposed");
|
||||
}
|
||||
TEST_F(LuaMat, Determinant)
|
||||
{
|
||||
check("Mat4_Determinant");
|
||||
}
|
||||
TEST_F(LuaMat, Inverted_success)
|
||||
{
|
||||
check("Mat4_Inverted_success");
|
||||
}
|
||||
TEST_F(LuaMat, Inverted_singular)
|
||||
{
|
||||
check("Mat4_Inverted_singular");
|
||||
}
|
||||
TEST_F(LuaMat, ToScreenMat)
|
||||
{
|
||||
check("Mat4_ToScreenMat");
|
||||
}
|
||||
TEST_F(LuaMat, Translation)
|
||||
{
|
||||
check("Mat4_Translation");
|
||||
}
|
||||
TEST_F(LuaMat, Scale)
|
||||
{
|
||||
check("Mat4_Scale");
|
||||
}
|
||||
TEST_F(LuaMat, Perspective)
|
||||
{
|
||||
check("Mat4_Perspective");
|
||||
}
|
||||
TEST_F(LuaMat, AsTable)
|
||||
{
|
||||
check("Mat4_AsTable");
|
||||
}
|
||||
TEST_F(LuaMat, ToString)
|
||||
{
|
||||
check("Mat4_ToString");
|
||||
}
|
||||
TEST_F(LuaMat, Mat3_FromRows)
|
||||
{
|
||||
check("Mat3_FromRows");
|
||||
}
|
||||
TEST_F(LuaMat, Mat4ColumnMajor_FromRows)
|
||||
{
|
||||
check("Mat4ColumnMajor_FromRows");
|
||||
}
|
||||
TEST_F(LuaMat, Mat4ColumnMajor_ToScreenMat)
|
||||
{
|
||||
check("Mat4ColumnMajor_ToScreenMat");
|
||||
}
|
||||
TEST_F(LuaMat, Mat4ColumnMajor_Translation)
|
||||
{
|
||||
check("Mat4ColumnMajor_Translation");
|
||||
}
|
||||
TEST_F(LuaMat, Mat4d_FromRows)
|
||||
{
|
||||
check("Mat4d_FromRows");
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
//
|
||||
// Created by orange on 07.03.2026.
|
||||
//
|
||||
#include <gtest/gtest.h>
|
||||
#include <lua.hpp>
|
||||
#include <omath/lua/lua.hpp>
|
||||
|
||||
class LuaQuaternion : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
lua_State* L = nullptr;
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
L = luaL_newstate();
|
||||
luaL_openlibs(L);
|
||||
omath::lua::LuaInterpreter::register_lib(L);
|
||||
if (luaL_dofile(L, LUA_SCRIPTS_DIR "/quaternion_tests.lua") != LUA_OK)
|
||||
FAIL() << lua_tostring(L, -1);
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
lua_close(L);
|
||||
}
|
||||
|
||||
void check(const char* func_name)
|
||||
{
|
||||
lua_getglobal(L, func_name);
|
||||
if (lua_pcall(L, 0, 0, 0) != LUA_OK)
|
||||
{
|
||||
FAIL() << lua_tostring(L, -1);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(LuaQuaternion, Constructor_default)
|
||||
{
|
||||
check("Quaternion_Constructor_default");
|
||||
}
|
||||
TEST_F(LuaQuaternion, Constructor_xyzw)
|
||||
{
|
||||
check("Quaternion_Constructor_xyzw");
|
||||
}
|
||||
TEST_F(LuaQuaternion, Field_mutation)
|
||||
{
|
||||
check("Quaternion_Field_mutation");
|
||||
}
|
||||
TEST_F(LuaQuaternion, FromAxisAngle_zero_angle)
|
||||
{
|
||||
check("Quaternion_FromAxisAngle_zero_angle");
|
||||
}
|
||||
TEST_F(LuaQuaternion, Addition)
|
||||
{
|
||||
check("Quaternion_Addition");
|
||||
}
|
||||
TEST_F(LuaQuaternion, Multiplication_scalar)
|
||||
{
|
||||
check("Quaternion_Multiplication_scalar");
|
||||
}
|
||||
TEST_F(LuaQuaternion, Multiplication_scalar_reversed)
|
||||
{
|
||||
check("Quaternion_Multiplication_scalar_reversed");
|
||||
}
|
||||
TEST_F(LuaQuaternion, Multiplication_quaternion)
|
||||
{
|
||||
check("Quaternion_Multiplication_quaternion");
|
||||
}
|
||||
TEST_F(LuaQuaternion, UnaryMinus)
|
||||
{
|
||||
check("Quaternion_UnaryMinus");
|
||||
}
|
||||
TEST_F(LuaQuaternion, EqualTo_true)
|
||||
{
|
||||
check("Quaternion_EqualTo_true");
|
||||
}
|
||||
TEST_F(LuaQuaternion, EqualTo_false)
|
||||
{
|
||||
check("Quaternion_EqualTo_false");
|
||||
}
|
||||
TEST_F(LuaQuaternion, ToString)
|
||||
{
|
||||
check("Quaternion_ToString");
|
||||
}
|
||||
TEST_F(LuaQuaternion, Conjugate)
|
||||
{
|
||||
check("Quaternion_Conjugate");
|
||||
}
|
||||
TEST_F(LuaQuaternion, Dot)
|
||||
{
|
||||
check("Quaternion_Dot");
|
||||
}
|
||||
TEST_F(LuaQuaternion, Length)
|
||||
{
|
||||
check("Quaternion_Length");
|
||||
}
|
||||
TEST_F(LuaQuaternion, LengthSqr)
|
||||
{
|
||||
check("Quaternion_LengthSqr");
|
||||
}
|
||||
TEST_F(LuaQuaternion, Normalized)
|
||||
{
|
||||
check("Quaternion_Normalized");
|
||||
}
|
||||
TEST_F(LuaQuaternion, Inverse)
|
||||
{
|
||||
check("Quaternion_Inverse");
|
||||
}
|
||||
TEST_F(LuaQuaternion, Rotate)
|
||||
{
|
||||
check("Quaternion_Rotate");
|
||||
}
|
||||
TEST_F(LuaQuaternion, ToRotationMatrix3)
|
||||
{
|
||||
check("Quaternion_ToRotationMatrix3");
|
||||
}
|
||||
TEST_F(LuaQuaternion, ToRotationMatrix4)
|
||||
{
|
||||
check("Quaternion_ToRotationMatrix4");
|
||||
}
|
||||
TEST_F(LuaQuaternion, AsTable)
|
||||
{
|
||||
check("Quaternion_AsTable");
|
||||
}
|
||||
@@ -74,6 +74,12 @@ TEST_F(LuaSourceEngine, Camera_look_at) { check("Source_Camera_l
|
||||
TEST_F(LuaSourceEngine, Camera_get_forward) { check("Source_Camera_get_forward"); }
|
||||
TEST_F(LuaSourceEngine, Camera_get_right) { check("Source_Camera_get_right"); }
|
||||
TEST_F(LuaSourceEngine, Camera_get_up) { check("Source_Camera_get_up"); }
|
||||
TEST_F(LuaSourceEngine, Camera_get_view_matrix) { check("Source_Camera_get_view_matrix"); }
|
||||
TEST_F(LuaSourceEngine, Camera_get_projection_matrix) { check("Source_Camera_get_projection_matrix"); }
|
||||
TEST_F(LuaSourceEngine, Camera_get_view_projection_matrix) { check("Source_Camera_get_view_projection_matrix"); }
|
||||
TEST_F(LuaSourceEngine, Camera_extract_projection_params) { check("Source_Camera_extract_projection_params"); }
|
||||
TEST_F(LuaSourceEngine, Camera_calc_view_angles_from_view_matrix) { check("Source_Camera_calc_view_angles_from_view_matrix"); }
|
||||
TEST_F(LuaSourceEngine, Camera_calc_origin_from_view_matrix) { check("Source_Camera_calc_origin_from_view_matrix"); }
|
||||
TEST_F(LuaSourceEngine, Camera_world_to_screen_success) { check("Source_Camera_world_to_screen_success"); }
|
||||
TEST_F(LuaSourceEngine, Camera_world_to_screen_error) { check("Source_Camera_world_to_screen_error"); }
|
||||
TEST_F(LuaSourceEngine, Camera_screen_to_world) { check("Source_Camera_screen_to_world"); }
|
||||
|
||||
Reference in New Issue
Block a user