Merge pull request #192 from orange-cpp/feature/lua

added more lua stuff
This commit is contained in:
2026-05-18 11:11:13 +03:00
committed by GitHub
19 changed files with 2272 additions and 30 deletions
+169
View File
@@ -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
+244
View File
@@ -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
+195
View File
@@ -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
+111
View File
@@ -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
+40
View File
@@ -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))
+53
View File
@@ -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"); }
+58
View File
@@ -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"); }
+129
View File
@@ -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");
}
+125
View File
@@ -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"); }