lua: create type defs for Vec2, Vec3, Vec4, Quat, Transform, and DeltaTime

This commit is contained in:
SeanOMik 2024-10-04 15:07:42 -04:00
parent de14b6211b
commit 06a4301c23
Signed by: SeanOMik
GPG Key ID: FEC9E2FC15235964
13 changed files with 866 additions and 514 deletions

View File

@ -1,5 +1,3 @@
--local win = require "scripts.window"
local is_window_setup = false local is_window_setup = false
---Return the userdata's name from its metatable. ---Return the userdata's name from its metatable.
@ -35,17 +33,18 @@ end
function on_first() function on_first()
if not is_window_setup then if not is_window_setup then
world:view( world:view(
---@param w Window ---@param w Window
function (w) function (w)
if w.cursor_grab == CursorGrabMode.NONE then if w.cursor_grab == CursorGrabMode.NONE then
w.cursor_grab = CursorGrabMode.LOCKED w.cursor_grab = CursorGrabMode.LOCKED
w.cursor_visible = false w.cursor_visible = false
return w return w
else else
is_window_setup = true is_window_setup = true
print("Window setup") print("Window setup")
end end
end, Window) end, Window
)
end end
end end
@ -70,10 +69,13 @@ function on_update()
---@type number ---@type number
local dt = world:resource(DeltaTime) local dt = world:resource(DeltaTime)
world:view(function (t) world:view(
t:translate(0, 0.15 * dt, 0) ---@param t Transform
return t function (t)
end, Transform) t:translate(0, 0.15 * dt, 0)
return t
end, Transform
)
end end
--[[ function on_post_update() --[[ function on_post_update()

View File

@ -28,10 +28,6 @@ impl Default for Transform {
// TODO: https://www.brainvoyager.com/bv/doc/UsersGuide/CoordsAndTransforms/SpatialTransformationMatrices.html // TODO: https://www.brainvoyager.com/bv/doc/UsersGuide/CoordsAndTransforms/SpatialTransformationMatrices.html
#[allow(dead_code)]
const ZERO_V3: Vec3 = Vec3::new(0.0, 0.0, 0.0);
const ONE_V3: Vec3 = Vec3::new(1.0, 1.0, 1.0);
#[allow(dead_code)] #[allow(dead_code)]
impl Transform { impl Transform {
pub fn new(translation: Vec3, rotation: Quat, scale: Vec3) -> Self { pub fn new(translation: Vec3, rotation: Quat, scale: Vec3) -> Self {
@ -43,33 +39,42 @@ impl Transform {
} }
pub fn from_translation(translation: Vec3) -> Self { pub fn from_translation(translation: Vec3) -> Self {
Self::new(translation, Quat::IDENTITY, ONE_V3) Self::new(translation, Quat::IDENTITY, Vec3::ONE)
} }
pub fn from_xyz(x: f32, y: f32, z: f32) -> Self { pub fn from_xyz(x: f32, y: f32, z: f32) -> Self {
Self::new(Vec3::new(x, y, z), Quat::IDENTITY, ONE_V3) Self::new(Vec3::new(x, y, z), Quat::IDENTITY, Vec3::ONE)
} }
pub fn calculate_mat4(&self) -> Mat4 { pub fn calculate_mat4(&self) -> Mat4 {
Mat4::from_scale_rotation_translation(self.scale, self.rotation, self.translation) Mat4::from_scale_rotation_translation(self.scale, self.rotation, self.translation)
} }
/// Get the forward vector of the Transform. /// Returns a normalized vector pointing in the direction the Transform is facing.
///
/// This represents the front of the object can be used for movement, camera orientation, and
/// other directional calculations.
pub fn forward(&self) -> Vec3 { pub fn forward(&self) -> Vec3 {
(self.rotation * -Vec3::Z).normalize() (self.rotation * -Vec3::Z).normalize()
} }
/// Get the left vector of the Transform. /// Returns a normalized vector pointing to the left side of the Transform.
///
/// The vector is in local space. This represents the direction that is
/// perpendicular to the object's forward direction.
pub fn left(&self) -> Vec3 { pub fn left(&self) -> Vec3 {
(self.rotation * Vec3::X).normalize() (self.rotation * Vec3::X).normalize()
} }
/// Get the up vector of the Transform. /// Returns a normalized vector that indicates the upward direction of the Transform.
///
/// This vector is commonly used to define an object's orientation and is essential for maintaining
/// consistent vertical alignment in 3D environments, such as for camera positioning and object alignment.
pub fn up(&self) -> Vec3 { pub fn up(&self) -> Vec3 {
(self.rotation * Vec3::Y).normalize() (self.rotation * Vec3::Y).normalize()
} }
/// Rotate this transform using a Quaternion /// Rotate this transform using a Quaternion.
pub fn rotate(&mut self, rotation: Quat) { pub fn rotate(&mut self, rotation: Quat) {
self.rotation = (rotation * self.rotation).normalize(); self.rotation = (rotation * self.rotation).normalize();
} }
@ -110,7 +115,7 @@ impl Transform {
let mut res = *self; let mut res = *self;
res.translation = self.translation.lerp(rhs.translation, alpha); res.translation = self.translation.lerp(rhs.translation, alpha);
// normalize rotation here to avoid panics // normalize rotation here to avoid panics
res.rotation = self.rotation.lerp(rhs.rotation.normalize(), alpha); res.rotation = self.rotation.normalize().lerp(rhs.rotation.normalize(), alpha);
res.scale = self.scale.lerp(rhs.scale, alpha); res.scale = self.scale.lerp(rhs.scale, alpha);
res res
} else { } else {

View File

@ -1,182 +0,0 @@
---@class Quat
---@field x number
---@field y number
---@field z number
---@field w number
Quat = { x = 0.0, y = 0.0, z = 0.0, w = 0.0 }
Quat.__index = Quat
Quat.__name = "Quat"
--- Constructs a new Quaternion from x, y, z, and w.
---@param x number
---@param y number
---@param z number
---@param w number
---@return Quat
function Quat:new(x, y, z, w)
local q = {}
setmetatable(q, Quat)
q.x = x
q.y = y
q.z = z
q.w = w
return q
end
Quat.IDENTITY = Quat:new(0, 0, 0, 1)
function Quat:clone()
return Quat:new(self.x, self.y, self.z, self.w)
end
--- Creates a quaternion from the angle, in radians, around the x axis.
--- @param rad number
--- @return Quat
function Quat:from_rotation_x(rad)
local sin = math.sin(rad * 0.5)
local cos = math.cos(rad * 0.5)
return Quat:new(sin, 0, 0, cos)
end
--- Creates a quaternion from the angle, in radians, around the y axis.
--- @param rad number
--- @return Quat
function Quat:from_rotation_y(rad)
local sin = math.sin(rad * 0.5)
local cos = math.cos(rad * 0.5)
return Quat:new(0, sin, 0, cos)
end
--- Creates a quaternion from the angle, in radians, around the z axis.
--- @param rad number
--- @return Quat
function Quat:from_rotation_z(rad)
local sin = math.sin(rad * 0.5)
local cos = math.cos(rad * 0.5)
return Quat:new(0, 0, sin, cos)
end
--- Computes the dot product of `self`.
---@param rhs Quat
---@return number
function Quat:dot(rhs)
return (self.x * rhs.x) + (self.y * rhs.y) + (self.z * rhs.z) + (self.w * rhs.w)
end
--- Computes the length of `self`.
---@return number
function Quat:length()
return math.sqrt(self:dot(self))
end
--- Compute the length of `self` squared.
---@return number
function Quat:length_squared()
return self:length() ^ 2
end
--- Normalizes `self` and returns the new Quat
---@return unknown
function Quat:normalize()
local length = self:length()
return self / length
end
--- Multiplies two Quaternions together. Keep in mind that Quaternion multiplication is NOT
--- commutative so the order in which you multiply the quaternions matters.
---@param rhs Quat
---@return Quat
function Quat:mult_quat(rhs)
local x1, y1, z1, w1 = self.x, self.y, self.z, self.w
local x2, y2, z2, w2 = rhs.x, rhs.y, rhs.z, rhs.w
local x = w1 * x2 + x1 * w2 + y1 * z2 - z1 * y2
local y = w1 * y2 - x1 * z2 + y1 * w2 + z1 * x2
local z = w1 * z2 + x1 * y2 - y1 * x2 + z1 * w2
local w = w1 * w2 - x1 * x2 - y1 * y2 - z1 * x2
return Quat:new(x, y, z, w)
end
--- Multiplies `self` by a Vec3, returning the rotated Vec3
---@param vec Vec3
---@return Vec3
function Quat:mult_vec3(vec)
local vec_quat = Quat:new(vec.x, vec.y, vec.z, 0)
local quat = self:mult_quat(vec_quat)
return Vec3:new(quat.x, quat.y, quat.z)
end
--- Calculates the linear iterpolation between `self` and `rhs` based on the `alpha`.
--- When `alpha` is `0`, the result will be equal to `self`. When `s` is `1`, the result
--- will be equal to `rhs`
--- @param rhs Quat
--- @param alpha number
--- @return Quat
function Quat:lerp(rhs, alpha)
-- ensure alpha is [0, 1]
local alpha = math.max(0, math.min(1, alpha))
local x1, y1, z1, w1 = self.x, self.y, self.z, self.w
local x2, y2, z2, w2 = rhs.x, rhs.y, rhs.z, rhs.w
local x = (1 - alpha) * x1 + alpha * x2
local y = (1 - alpha) * y1 + alpha * y2
local z = (1 - alpha) * z1 + alpha * z2
local w = (1 - alpha) * w1 + alpha * w2
return Quat:new(x, y, z, w):normalize()
end
function Quat:__add(rhs)
return Quat:new(self.x + rhs.x, self.y + rhs.y, self.z + rhs.z, self.w + rhs.w)
end
function Quat:__sub(rhs)
return Quat:new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z, self.w - rhs.w)
end
function Quat:__mul(rhs)
if type(rhs) == "number" then
return Quat:new(self.x * rhs, self.y * rhs, self.z * rhs, self.w * rhs)
elseif type(rhs) == "table" then
local name = rhs.__name
if name == "Vec3" then
return self:mult_vec3(rhs)
elseif name == "Quat" then
return self:mult_quat(rhs)
else
assert(false, "Unknown usertype of rhs" .. name)
end
else
assert(false, "Unknown type of rhs" .. type(rhs))
end
end
function Quat:__div(rhs)
if type(rhs) == "number" then
return Quat:new(self.x / rhs, self.y / rhs, self.z / rhs, self.w / rhs)
else
assert(rhs.__name == "Quat", "Attempted to divide Quat by unknown type " .. rhs.__name)
return Quat:new(self.x / rhs.x, self.y / rhs.y, self.z / rhs.z, self.w / rhs.w)
end
end
function Quat:__eq(rhs)
return self.x == rhs.x and self.y == rhs.y and self.z == rhs.z and self.w == rhs.w
end
function Quat:__lt(rhs)
return self.x < rhs.x and self.y < rhs.y and self.z < rhs.z and self.w < rhs.w
end
function Quat:__le(rhs)
return self.x <= rhs.x and self.y <= rhs.y and self.z <= rhs.z and self.w <= rhs.w
end
function Quat:__tostring()
return "Quat(" .. self.x .. ", " .. self.y .. ", " .. self.z .. ", " .. self.w .. ")"
end

View File

@ -1,95 +0,0 @@
---@class Transform
---@field translation Vec3
---@field rotation Quat
---@field Scale Vec3
Transform = { translation = Vec3.ZERO, rotation = Quat.IDENTITY, scale = Vec3.ONE }
Transform.__index = Transform
Transform.__name = "Transform"
function Transform:new(translation, rotation, scale)
local t = {}
setmetatable(t, Transform)
t.translation = translation
t.rotation = rotation
t.scale = scale
return t
end
function Transform:clone()
return Transform:new(self.translation:clone(), self.rotation:clone(), self.scale:clone())
end
--- Creates a new Transform with the translation at the vec3
--- @param pos Vec3
function Transform:from_vec3(pos)
local t = Transform:clone() -- copy of default transform
t.translation = pos
return t
end
function Transform:from_xyz(x, y, z)
Transform:from_vec3(Vec3:new(x, y, z))
end
--- Calculates the forward vector of the Transform.
--- @return Vec3
function Transform:forward()
return (self.rotation * Vec3.NEG_Z):normalize()
end
--- Calculates the left vector of the Transform.
--- @return Vec3
function Transform:left()
return (self.rotation * Vec3.X):normalize()
end
--- Calculates the up vector of the Transform.
--- @return Vec3
function Transform:up()
return (self.rotation * Vec3.Y):normalize()
end
--- Rotates `self` using a Quaternion
--- @param quat Quat
function Transform:rotate(quat)
self.rotation = (quat * self.rotation):normalize()
end
--- Rotates `self` around the x-axis
--- @param rad number
function Transform:rotate_x(rad)
self:rotate(Quat:from_rotation_x(rad))
end
--- Rotates `self` around the y-axis
--- @param rad number
function Transform:rotate_y(rad)
self:rotate(Quat:from_rotation_y(rad))
end
--- Rotates `self` around the z-axis
--- @param rad number
function Transform:rotate_z(rad)
self:rotate(Quat:from_rotation_z(rad))
end
--- Calculates the linear iterpolation between `self` and `rhs` based on the `alpha`.
--- When `alpha` is `0`, the result will be equal to `self`. When `s` is `1`, the result
--- will be equal to `rhs`
--- @param rhs Transform
--- @param alpha number
--- @return Transform
function Transform:lerp(rhs, alpha)
local res = self:clone()
res.translation = self.translation:lerp(rhs.translation, alpha)
res.rotation = self.rotation:lerp(rhs.rotation, alpha)
res.scale = self.scale:lerp(rhs.scale, alpha)
return res
end
function Transform:__tostring()
return "Transform(pos=" .. tostring(self.translation) .. ", rot="
.. tostring(self.rotation) .. ", scale=" .. tostring(self.scale) .. ")"
end

View File

@ -1,187 +0,0 @@
---@class Vec3
---@field x number
---@field y number
---@field z number
Vec3 = { x = 0.0, y = 0.0, z = 0.0 }
Vec3.__index = Vec3
Vec3.__name = "Vec3"
--- Constructs a new vector
---@param x number
---@param y number
---@param z number
---@return Vec3
function Vec3:new(x, y, z)
local v = {}
setmetatable(v, Vec3)
v.x = x
v.y = y
v.z = z
return v
end
---Creates a copy of self
---@return Vec3
function Vec3:clone()
return Vec3:new(self.x, self.y, self.z)
end
--- Constructs a vector with all elements as parameter `x`.
---@param x number
---@return Vec3
function Vec3:all(x)
return Vec3:new(x, x, x)
end
--- A unit-length vector pointing alongside the positive X axis.
Vec3.X = Vec3:new(1, 0, 0)
--- A unit-length vector pointing alongside the positive Y axis.
Vec3.Y = Vec3:new(0, 1, 0)
--- A unit-length vector pointing alongside the positive Z axis.
Vec3.Z = Vec3:new(0, 0, 1)
--- A unit-length vector pointing alongside the negative X axis.
Vec3.NEG_X = Vec3:new(-1, 0, 0)
--- A unit-length vector pointing alongside the negative Y axis.
Vec3.NEG_Y = Vec3:new(0, -1, 0)
--- A unit-length vector pointing alongside the negative Z axis.
Vec3.NEG_Z = Vec3:new(0, 0, -1)
--- A vector of all zeros
Vec3.ZERO = Vec3:new(0, 0, 0)
--- A vector of all ones
Vec3.ONE = Vec3:new(1, 1, 1)
--- Computes the absolute value of `self`.
function Vec3:abs()
self.x = math.abs(self.x)
self.y = math.abs(self.y)
self.z = math.abs(self.z)
end
--- Computes the length of `self`.
---@return number
function Vec3:length()
return math.sqrt(self:dot(self))
end
---Moves `self` by the provided coordinates
---@param x number
---@param y number
---@param z number
function Vec3:move_by(x, y, z)
self.x = self.x + x
self.y = self.y + y
self.z = self.z + z
end
--- Computes the dot product of `self` and `rhs`.
---@param rhs Vec3
---@return number
function Vec3:dot(rhs)
assert(rhs.__name == "Vec3")
return (self.x * rhs.x) + (self.y * rhs.y) + (self.z * rhs.z)
end
--- Returns a vector that has the minimum value of each element of `self` and `rhs`
---@param rhs Vec3
---@return Vec3
function Vec3:min(rhs)
local x = math.min(self.x, rhs.x)
local y = math.min(self.y, rhs.y)
local z = math.min(self.z, rhs.z)
return Vec3:new(x, y, z)
end
--- Modifies `self` to be normalized to a length 1.
function Vec3:normalize()
local len_recip = 1.0 / self:length()
self.x = self.x * len_recip
self.y = self.y * len_recip
self.z = self.z * len_recip
end
--- Calculates the linear iterpolation between `self` and `rhs` based on the `alpha`.
--- When `alpha` is `0`, the result will be equal to `self`. When `s` is `1`, the result
--- will be equal to `rhs`
--- @param rhs Vec3
--- @param alpha number
--- @return Vec3
function Vec3:lerp(rhs, alpha)
-- ensure alpha is [0, 1]
local alpha = math.max(0, math.min(1, alpha))
local res = self:clone()
res = res + ((rhs - res) * alpha)
return res
end
function Vec3:__add(rhs)
if type(rhs) == "Vec3" then
return Vec3:new(self.x + rhs.x, self.y + rhs.y, self.z + rhs.z)
else
return Vec3:new(self.x + rhs, self.y + rhs, self.z + rhs)
end
end
function Vec3:__sub(rhs)
if type(rhs) == "Vec3" then
return Vec3:new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z)
else
return Vec3:new(self.x - rhs, self.y - rhs, self.z - rhs)
end
end
function Vec3:__mul(rhs)
if type(rhs) == "Vec3" then
return Vec3:new(self.x * rhs.x, self.y * rhs.y, self.z * rhs.z)
else
return Vec3:new(self.x * rhs, self.y * rhs, self.z * rhs)
end
end
function Vec3:__div(rhs)
if type(rhs) == "Vec3" then
return Vec3:new(self.x / rhs.x, self.y / rhs.y, self.z / rhs.z)
else
return Vec3:new(self.x / rhs, self.y / rhs, self.z / rhs)
end
end
function Vec3:__idiv(rhs)
if type(rhs) == "Vec3" then
return Vec3:new(self.x // rhs.x, self.y // rhs.y, self.z // rhs.z)
else
return Vec3:new(self.x // rhs, self.y // rhs, self.z // rhs)
end
end
function Vec3:__unm()
return Vec3:new(-self.x, -self.y, -self.z)
end
function Vec3:__pow(rhs)
if type(rhs) == "number" then
return Vec3:new(self.x ^ rhs, self.y ^ rhs, self.z ^ rhs)
end
end
function Vec3:__eq(rhs)
return self.x == rhs.x and self.y == rhs.y and self.z == rhs.z
end
function Vec3:__lt(rhs)
return self.x < rhs.x and self.y < rhs.y and self.z < rhs.z
end
function Vec3:__le(rhs)
return self.x <= rhs.x and self.y <= rhs.y and self.z <= rhs.z
end
function Vec3:__tostring()
return "Vec3(" .. self.x .. ", " .. self.y .. ", " .. self.z .. ")"
end

View File

@ -0,0 +1,15 @@
---@meta
---@class DeltaTime
---
---DeltaTime is an ECS world resource. When its requested from the world, a `number`
---is returned.
---
---Example:
---```lua
------@type number
---local dt = world:resource(DeltaTime)
---
---print(type(dt)) --> number
---```
DeltaTime = {}

View File

@ -1,3 +1,8 @@
require "math.vec2" require "math.vec2"
require "math.vec3"
require "math.vec4"
require "math.quat"
require "math.transform"
require "ecs.window" require "ecs.window"
require "ecs.delat_time"

View File

@ -0,0 +1,188 @@
---@meta
---@class Quat
---This is a Lua export of [`glam::Quat`](https://docs.rs/glam/latest/glam/f32/struct.Quat.html)
---
---@operator add(self): self
---@operator sub(self): self
---@operator div(number): self
---@operator mul(self|Vec3|number): self
---@diagnostic disable-next-line: unknown-operator
---@operator eq: self
Quat = {
---The x coordinate
---@type number
x = nil,
---The y coordinate
---@type number
y = nil,
---The z coordinate
---@type number
z = nil,
---The w coordinate
---@type number
w = nil,
}
---Create a new `Quat`
---@param x number
---@param y number
---@param z number
---@param w number
---@return self
function Quat.new(x, y, z, w) end
---Creates a quaternion from the angle (in radians) around the x axis.
---@param rad number
---@return self
function Quat.from_rotation_x(rad) end
---Creates a quaternion from the angle (in radians) around the y axis.
---@param rad number
---@return self
function Quat.from_rotation_y(rad) end
---Creates a quaternion from the angle (in radians) around the z axis.
---@param rad number
---@return self
function Quat.from_rotation_z(rad) end
---Creates a quaternion from a `Vec4`.
---@param vec4 Vec4
---@return self
function Quat.from_vec4(vec4) end
---Create a quaternion for a normalized rotation axis and angle (in radians).
---
---The axis must be a unit vector.
---
---@param axis Vec3
---@param rad number
---@return self
function Quat.from_axis_angle(axis, rad) end
---Computes the dot product of self and rhs.
---
---The dot product is equal to the cosine of the angle between two
---quaternion rotations.
---
---@param rhs Quat
---@return number
function Quat:dot(rhs) end
---Computes the length of self.
---
---@return number
function Quat:length() end
---Computes the squared length of self.
---
---This is generally faster than length() as it avoids a square root operation.
---
---@return number
function Quat:length_squared() end
---Computes 1.0 / length().
---
---For valid results, self must not be of length zero.
---@return number
function length_recip() end
---Returns `self` normalized to length `1.0`.
---
---For valid results, `self` must not be of length zero.
---
---@return self
function Quat:normalize() end
---Multipies `self` with a `Quat`
---@param rhs Quat
function Quat:mult_quat(rhs) end
---Multiplies `self` with a `Vec3`
---@param rhs Vec3
function Quat:mult_vec3(rhs) end
---Performs a linear interpolation between `self` and `rhs` based on `alpha`.
---
---Both `Quat`s must be normalized.
---
---When `alpha` is `0.0`, the result will be equal to `self`. When `alpha` is `1.0`,
---the result will be equal to `rhs`.
---
---@param rhs Quat
---@param alpha number
function Quat:lerp(rhs, alpha) end
---Performs a spherical linear interpolation between `self` and `rhs` based on `alpha`.
---
---Both `Quat`s must be normalized.
---
---When `alpha` is `0.0`, the result will be equal to `self`. When `alpha` is `1.0`,
---the result will be equal to `rhs`.
---
---@param rhs Quat
---@param alpha number
function Quat:slerp(rhs, alpha) end
---Returns the inverse of a normalized quaternion.
---
---Typically quaternion inverse returns the conjugate of a normalized quaternion.
---Because `self` is assumed to already be unit length this method does not
---normalize before returning the conjugate.
---@return self
function Quat:inverse() end
---Returns `true` if, and only if, all elements are finite. If any element is either
---`NaN`, positive or negative infinity, this will return `false`.
---
---@return boolean
function Quat:is_finite() end
---@return boolean
function Quat:is_nan() end
---Returns whether `self` is of length `1.0` or not.
---
---Uses a precision threshold of `1e-6`.
---@return boolean
function Quat:is_normalized() end
---@return boolean
function Quat:is_near_identity() end
---Returns the angle (in radians) for the minimal rotation for transforming
---this quaternion into another.
---
---Both quaternions must be normalized.
---@return number
function Quat:angle_between(rhs) end
---Rotates towards `rhs` up to `max_angle` (in radians).
---
---When `max_angle` is `0.0`, the result will be equal to `self`. When `max_angle`
---is equal to `self.angle_between(rhs)`, the result will be equal to `rhs`.
---If `max_angle` is negative, rotates towards the exact opposite of `rhs`.
---Will not go past the target.
---
---Both quaternions must be normalized.
---@return self
function Quat:rotate_towards(rhs, max_angle) end
---Returns true if the absolute difference of all elements between `self` and `rhs` is less
---than or equal to `max_abs_diff`.
---
---This can be used to compare if two quaternions contain similar elements. It works best when
---comparing with a known value. The `max_abs_diff` that should be used used depends on the
---values being compared against.
---
---For more see [comparing floating point numbers](https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/).
---
---@param rhs Quat
---@param max_abs_diff number
---@return boolean
function Quat:abs_diff_eq(rhs, max_abs_diff) end

View File

@ -0,0 +1,134 @@
---@meta
---@class Transform
---
---A Transform represents a transformation of an object. A transform includes the position
---(called translation here), rotation, and scale. Rotation is represented using a Quaternion
---(or Quat for short).
---
---Although Quats can be scary, they are much more robust than euler angles for games
---since they do not suffer from things like
---[gimbal-lock](https://en.wikipedia.org/wiki/Gimbal_lock).
---
---This is a Lua export of [`lyra_math::Transform`].
---
---@operator add(Quat): self
---@operator mul(Vec3): self
---@diagnostic disable-next-line: unknown-operator
---@operator eq: self
Transform = {
---The translation/position of the transform.
---@type Vec3
translation = nil,
---The rotation of the transform.
---@type Quat
rotation = nil,
---The scale of the transform.
---@type Vec3
scale = nil,
}
function Transform:__tostring() end
---@return self
function Transform.default() end
---Create a new transform with its components.
---
---@param translation Vec3
---@param rotation Quat
---@param scale Vec3
---@return self
function Transform.new(translation, rotation, scale) end
---Create a new transform with a `Vec3` translation.
---@param translation Vec3
---@return self
function Transform.from_translation(translation) end
---Create a new transform with a translation of `x`, `y`, and `z`.
---
---@param x number
---@param y number
---@param z number
---@return self
function Transform.from_translation(x, y, z) end
---Create a clone of `self`
---@return self
function Transform:clone() end
---Returns a normalized vector pointing in the direction the Transform is facing.
---
---This represents the front of the object can be used for movement, camera orientation, and
---other directional calculations.
---
---@return Vec3
function Transform:forward() end
---Returns a normalized vector pointing to the left side of the Transform.
---
---The vector is in local space. This represents the direction that is
---perpendicular to the object's forward direction.
---
---@return Vec3
function Transform:left() end
---Returns a normalized vector that indicates the upward direction of the Transform.
---
---This vector is commonly used to define an object's orientation and is essential for maintaining
---consistent vertical alignment in 3D environments, such as for camera positioning and object alignment.
---@return Vec3
function Transform:up() end
---Rotate `self` using a quaternion
---@param quat Quat
function Transform:rotate(quat) end
---Rotate `self` around the x axis by **degrees**.
---
---@param deg number The amount of **degrees** to rotate by.
function Transform:rotate_x(deg) end
---Rotate `self` around the y axis by **degrees**.
---
---@param deg number The amount of **degrees** to rotate by.
function Transform:rotate_y(deg) end
---Rotate `self` around the z axis by **degrees**.
---
---@param deg number The amount of **degrees** to rotate by.
function Transform:rotate_z(deg) end
---Rotate `self` around the x axis by **radians** .
---
---@param rad number The amount of **radians** to rotate by.
function Transform:rotate_x_rad(rad) end
---Rotate `self` around the y axis by **radians** .
---
---@param rad number The amount of **radians** to rotate by.
function Transform:rotate_y_rad(rad) end
---Rotate `self` around the z axis by **radians** .
---
---@param rad number The amount of **radians** to rotate by.
function Transform:rotate_z_rad(rad) end
---Move `self` by `x`, `y`, and `z`.
---
---@param x number
---@param y number
---@param z number
function Transform:translate(x, y, z) end
---Performs a linear interpolation between `self` and `rhs` based on `alpha`.
---
---This will normalize the rotation `Quat`.
---
---When `alpha` is `0.0`, the result will be equal to `self`. When `alpha` is `1.0`,
---the result will be equal to `rhs`.
---
---@param rhs Transform
---@param alpha number
function Transform:lerp(rhs, alpha) end

View File

@ -1,14 +1,125 @@
---@meta
---@class Vec2 ---@class Vec2
---This is a Lua export of [`glam::Vec2`](https://docs.rs/glam/latest/glam/f32/struct.Vec2.html)
---
---@operator add(self|number): self
---@operator sub(self|number): self
---@operator div(self|number): self
---@operator mul(self|number): self
---@operator mod(self|number): self
---@operator unm: self
---@diagnostic disable-next-line: unknown-operator
---@operator eq: self
Vec2 = { Vec2 = {
---The x coordinate ---The x coordinate
---@type number ---@type number
x = nil, x = nil,
---The y coordinate ---The y coordinate
---@type number ---@type number
y = nil, y = nil,
---Create a new `Vec2` ---A constant `Vec2` with coordinates as `f32::NAN`.
---@param x number ---@type Vec2
---@param y number NAN = nil,
new = function (x, y) end
} ---A constant `Vec2` with `x` as `-1.0`.
---@type Vec2
NEG_X = nil,
---A constant `Vec2` with `y` as `-1.0`.
---@type Vec2
NEG_Y = nil,
---A constant `Vec2` with both components as `-1.0`.
---@type Vec2
NEG_ONE = nil,
---A constant `Vec2` with `x` as `1.0`.
---@type Vec2
POS_X = nil,
---A constant `Vec2` with `y` as `1.0`.
---@type Vec2
POS_Y = nil,
---A constant `Vec2` with both components as `1.0`.
---@type Vec2
ONE = nil,
---A constant `Vec2` with both components as `0.0`.
---@type Vec2
ZERO = nil,
}
function Vec2:__tostring() end
---Create a new `Vec2`
---@param x number
---@param y number
---@return self
function Vec2.new(x, y) end
---Returns a vector with a length no less than min and no more than max.
---@param min number the minimum value to clamp the length to
---@param max number the maximum value to clamp the length to
---@return self
function Vec2:clamp_length(min, max) end
---Returns true if the absolute difference of all elements between `self` and `rhs` is less
---than or equal to `max_abs_diff`.
---
---This can be used to compare if two vectors contain similar elements. It works best when
---comparing with a known value. The `max_abs_diff` that should be used used depends on the
---values being compared against.
---
---For more see [comparing floating point numbers](https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/).
---
---@param rhs Vec2 The other `Vec2` to compare to.
---@param max_abs_diff number Maximum absolute difference between `self` and `rhs`.
---@return boolean
function Vec2:abs_diff_eq(rhs, max_abs_diff) end
---Returns a vector containing the smallest integer greater than or equal to a number for each
---element of self.
---@return self
function Vec2:ceil() end
---Returns the angle of rotation (in radians) from `self` to `rhs` in the range [-π, +π].
---
---The inputs do not need to be unit vectors however they must be non-zero.
---
---@param rhs Vec2 The other `Vec2` to get the angle to.
---@return number
function Vec2:angle_to(rhs) end
---Returns a vector containing the absolute value of each element of `self`.
---
---@return self
function Vec2:abs() end
---Component-wise clamping of values.
---
---Each element in `min` must be less-or-equal to the corresponding element in `max`.
---
---@param min self The minimum `Vec2` components to clamp the components of `self` to.
---@param max self The maximum `Vec2` components to clamp the components of `self` to.
---@return self
function Vec2:clamp(min, max) end
---Converts `self` to an array `[x, y]`
---
---@return number[]
function Vec2:to_array() end
---Move `self` by `x` and `y` values.
---
---@param x number
---@param y number
function Vec2:move_by(x, y) end
---Move `self` by a `Vec2`.
---
---@param rhs Vec2
function Vec2:move_by(rhs) end

View File

@ -0,0 +1,139 @@
---@meta
---@class Vec3
---This is a Lua export of [`glam::Vec3`](https://docs.rs/glam/latest/glam/f32/struct.Vec3.html)
---
---@operator add(self|number): self
---@operator sub(self|number): self
---@operator div(self|number): self
---@operator mul(self|number): self
---@operator mod(self|number): self
---@operator unm: self
---@diagnostic disable-next-line: unknown-operator
---@operator eq: self
Vec3 = {
---The x coordinate
---@type number
x = nil,
---The y coordinate
---@type number
y = nil,
---The z coordinate
---@type number
z = nil,
---A constant `Vec3` with coordinates as `f32::NAN`.
---@type Vec3
NAN = nil,
---A constant `Vec3` with `x` as `-1.0`.
---@type Vec3
NEG_X = nil,
---A constant `Vec3` with `y` as `-1.0`.
---@type Vec3
NEG_Y = nil,
---A constant `Vec3` with `z` as `-1.0`.
---@type Vec3
NEG_Z = nil,
---A constant `Vec3` with all components as `-1.0`.
---@type Vec3
NEG_ONE = nil,
---A constant `Vec3` with `x` as `1.0`.
---@type Vec3
POS_X = nil,
---A constant `Vec3` with `y` as `1.0`.
---@type Vec3
POS_Y = nil,
---A constant `Vec3` with `z` as `1.0`.
---@type Vec3
POS_Z = nil,
---A constant `Vec3` with all components as `1.0`.
---@type Vec3
ONE = nil,
---A constant `Vec3` with all components as `0.0`.
---@type Vec3
ZERO = nil,
}
function Vec3:__tostring() end
---Create a new `Vec3`
---@param x number
---@param y number
---@param z number
---@return self
function Vec3.new(x, y, z) end
---Returns a vector with a length no less than min and no more than max.
---@param min number the minimum value to clamp the length to
---@param max number the maximum value to clamp the length to
---@return self
function Vec3:clamp_length(min, max) end
---Returns true if the absolute difference of all elements between `self` and `rhs` is less
---than or equal to `max_abs_diff`.
---
---This can be used to compare if two vectors contain similar elements. It works best when
---comparing with a known value. The `max_abs_diff` that should be used used depends on the
---values being compared against.
---
---For more see [comparing floating point numbers](https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/).
---
---@param rhs Vec3 The other `Vec3` to compare to.
---@param max_abs_diff number Maximum absolute difference between `self` and `rhs`.
---@return boolean
function Vec3:abs_diff_eq(rhs, max_abs_diff) end
---Returns a vector containing the smallest integer greater than or equal to a number for each
---element of self.
---@return self
function Vec3:ceil() end
---Returns the angle (in radians) between two vectors in the range [-π, +π].
---
---The inputs do not need to be unit vectors however they must be non-zero.
---
---@param rhs Vec3 The other `Vec3` to get the angle to.
---@return number
function Vec3:angle_between(rhs) end
---Returns a vector containing the absolute value of each element of `self`.
---
---@return self
function Vec3:abs() end
---Component-wise clamping of values.
---
---Each element in `min` must be less-or-equal to the corresponding element in `max`.
---
---@param min self The minimum `Vec3` components to clamp the components of `self` to.
---@param max self The maximum `Vec3` components to clamp the components of `self` to.
---@return self
function Vec3:clamp(min, max) end
---Converts `self` to an array `[x, y, z]`
---
---@return number[]
function Vec3:to_array() end
---Move `self` by `x`, `y`, and `z` values.
---
---@param x number
---@param y number
---@param z number
function Vec3:move_by(x, y, z) end
---Move `self` by a `Vec3`.
---
---@param rhs Vec3
function Vec3:move_by(rhs) end

View File

@ -0,0 +1,132 @@
---@meta
---@class Vec4
---This is a Lua export of [`glam::Vec4`](https://docs.rs/glam/latest/glam/f32/struct.Vec4.html)
---
---@operator add(self|number): self
---@operator sub(self|number): self
---@operator div(self|number): self
---@operator mul(self|number): self
---@operator mod(self|number): self
---@operator unm: self
---@diagnostic disable-next-line: unknown-operator
---@operator eq: self
Vec4 = {
---The x coordinate
---@type number
x = nil,
---The y coordinate
---@type number
y = nil,
---The z coordinate
---@type number
z = nil,
---The w coordinate
---@type number
w = nil,
---A constant `Vec4` with coordinates as `f32::NAN`.
---@type Vec4
NAN = nil,
---A constant `Vec4` with `x` as `-1.0`.
---@type Vec4
NEG_X = nil,
---A constant `Vec4` with `y` as `-1.0`.
---@type Vec4
NEG_Y = nil,
---A constant `Vec4` with `z` as `-1.0`.
---@type Vec4
NEG_Z = nil,
---A constant `Vec4` with `w` as `-1.0`.
---@type Vec4
NEG_W = nil,
---A constant `Vec4` with all components as `-1.0`.
---@type Vec4
NEG_ONE = nil,
---A constant `Vec4` with `x` as `1.0`.
---@type Vec4
POS_X = nil,
---A constant `Vec4` with `y` as `1.0`.
---@type Vec4
POS_Y = nil,
---A constant `Vec4` with `z` as `1.0`.
---@type Vec4
POS_Z = nil,
---A constant `Vec4` with `w` as `1.0`.
---@type Vec4
POS_W = nil,
---A constant `Vec4` with all components as `1.0`.
---@type Vec4
ONE = nil,
---A constant `Vec4` with all components as `0.0`.
---@type Vec4
ZERO = nil,
}
function Vec4:__tostring() end
---Create a new `Vec4`
---@param x number
---@param y number
---@param z number
---@param w number
---@return self
function Vec4.new(x, y, z, w) end
---Returns a vector with a length no less than min and no more than max.
---@param min number the minimum value to clamp the length to
---@param max number the maximum value to clamp the length to
---@return self
function Vec4:clamp_length(min, max) end
---Returns true if the absolute difference of all elements between `self` and `rhs` is less
---than or equal to `max_abs_diff`.
---
---This can be used to compare if two vectors contain similar elements. It works best when
---comparing with a known value. The `max_abs_diff` that should be used used depends on the
---values being compared against.
---
---For more see [comparing floating point numbers](https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/).
---
---@param rhs Vec4 The other `Vec4` to compare to.
---@param max_abs_diff number Maximum absolute difference between `self` and `rhs`.
---@return boolean
function Vec4:abs_diff_eq(rhs, max_abs_diff) end
---Returns a vector containing the smallest integer greater than or equal to a number for each
---element of self.
---@return self
function Vec4:ceil() end
---Returns a vector containing the absolute value of each element of `self`.
---
---@return self
function Vec4:abs() end
---Component-wise clamping of values.
---
---Each element in `min` must be less-or-equal to the corresponding element in `max`.
---
---@param min self The minimum `Vec4` components to clamp the components of `self` to.
---@param max self The maximum `Vec4` components to clamp the components of `self` to.
---@return self
function Vec4:clamp(min, max) end
---Converts `self` to an array `[x, y, z]`
---
---@return number[]
function Vec4:to_array() end

View File

@ -1,4 +1,6 @@
use crate::lyra_engine; use std::sync::Arc;
use crate::{lua::Error, lyra_engine};
use lyra_game::math; use lyra_game::math;
use lyra_scripting_derive::{lua_vec_wrap_extension, wrap_lua_struct}; use lyra_scripting_derive::{lua_vec_wrap_extension, wrap_lua_struct};
use mlua::FromLuaMulti; use mlua::FromLuaMulti;
@ -29,7 +31,14 @@ wrap_lua_struct!(
} else if let Ok(v) = Self::from_lua_multi(vals_clone, lua) { } else if let Ok(v) = Self::from_lua_multi(vals_clone, lua) {
this.0 += v.0; this.0 += v.0;
} else { } else {
todo!("handle invalid argument error"); return Err(mlua::Error::BadArgument {
to: Some("Vec2:move_by".into()),
pos: 2,
name: None,
cause: Arc::new(mlua::Error::runtime(
"expected (number, number, number) or (Vec2), received neither"
)),
});
} }
Ok(()) Ok(())
@ -62,7 +71,14 @@ wrap_lua_struct!(
} else if let Ok(v) = Self::from_lua_multi(vals_clone, lua) { } else if let Ok(v) = Self::from_lua_multi(vals_clone, lua) {
this.0 += v.0; this.0 += v.0;
} else { } else {
todo!("handle invalid argument error"); return Err(mlua::Error::BadArgument {
to: Some("Vec3:move_by".into()),
pos: 2,
name: None,
cause: Arc::new(mlua::Error::runtime(
"expected (number, number, number) or (Vec3), received neither"
)),
});
} }
Ok(()) Ok(())
@ -122,10 +138,27 @@ wrap_lua_struct!(
Ok(Self(q)) Ok(Self(q))
}); });
methods.add_function("from_vec4", |_, v: LuaVec4| {
Ok(Self(math::Quat::from_vec4(*v)))
});
methods.add_function("from_axis_angle", |_, (axis, angle): (LuaVec3, f32)| {
let q = math::Quat::from_axis_angle(*axis, angle);
Ok(Self(q))
});
methods.add_method("dot", |_, this, (rhs,): (Self,)| { methods.add_method("dot", |_, this, (rhs,): (Self,)| {
Ok(this.dot(rhs.0)) Ok(this.dot(rhs.0))
}); });
methods.add_method("conjugate", |_, this, ()| {
Ok(Self(this.conjugate()))
});
methods.add_method("inverse", |_, this, ()| {
Ok(Self(this.inverse()))
});
methods.add_method("length", |_, this, ()| { methods.add_method("length", |_, this, ()| {
Ok(this.length()) Ok(this.length())
}); });
@ -134,20 +167,50 @@ wrap_lua_struct!(
Ok(this.length_squared()) Ok(this.length_squared())
}); });
methods.add_method_mut("normalize", |_, this, ()| { methods.add_method("length_recip", |_, this, ()| {
this.0 = this.normalize(); Ok(this.length_recip())
Ok(())
}); });
methods.add_method_mut("mult_quat", |_, this, (rhs,): (Self,)| { methods.add_method("normalize", |_, this, ()| {
this.0 *= rhs.0; Ok(Self(this.normalize()))
Ok(()) });
methods.add_method("mult_quat", |_, this, (rhs,): (Self,)| {
Ok(Self(this.0 * rhs.0))
}); });
methods.add_method("mult_vec3", |_, this, (rhs,): (LuaVec3,)| { methods.add_method("mult_vec3", |_, this, (rhs,): (LuaVec3,)| {
Ok(LuaVec3(this.0 * rhs.0)) Ok(LuaVec3(this.0 * rhs.0))
}); });
methods.add_method("is_finite", |_, this, ()| {
Ok(this.is_finite())
});
methods.add_method("is_nan", |_, this, ()| {
Ok(this.is_nan())
});
methods.add_method("is_normalized", |_, this, ()| {
Ok(this.is_normalized())
});
methods.add_method("is_near_identity", |_, this, ()| {
Ok(this.is_near_identity())
});
methods.add_method("angle_between", |_, this, rhs: LuaQuat| {
Ok(this.angle_between(*rhs))
});
methods.add_method("rotate_towards", |_, this, (rhs, max_angle): (LuaQuat, f32)| {
Ok(Self(this.rotate_towards(*rhs, max_angle)))
});
methods.add_method("abs_diff_eq", |_, this, (rhs, max_abs_diff): (LuaQuat, f32)| {
Ok(this.abs_diff_eq(*rhs, max_abs_diff))
});
// manually implemented here since multiplying may not return `Self`. // manually implemented here since multiplying may not return `Self`.
methods.add_meta_method(mlua::MetaMethod::Mul, |lua, this, (val,): (mlua::Value,)| { methods.add_meta_method(mlua::MetaMethod::Mul, |lua, this, (val,): (mlua::Value,)| {
use mlua::IntoLua; use mlua::IntoLua;
@ -169,7 +232,15 @@ wrap_lua_struct!(
.into_lua(lua) .into_lua(lua)
}, },
_ => { _ => {
todo!() let t = val.type_name();
Err(mlua::Error::BadArgument {
to: Some("Quat:__mul".into()),
pos: 2,
name: None,
cause: Arc::new(mlua::Error::external(
Error::type_mismatch("Vec3, Quat, or Number", t)
)),
})
} }
} }
}); });
@ -177,6 +248,10 @@ wrap_lua_struct!(
methods.add_method("lerp", |_, this, (rhs, alpha): (Self, f32)| { methods.add_method("lerp", |_, this, (rhs, alpha): (Self, f32)| {
Ok(Self(this.lerp(*rhs, alpha))) Ok(Self(this.lerp(*rhs, alpha)))
}); });
methods.add_method("slerp", |_, this, (rhs, alpha): (Self, f32)| {
Ok(Self(this.slerp(*rhs, alpha)))
});
} }
); );
@ -193,12 +268,22 @@ wrap_lua_struct!(
Ok(Self(math::Transform::new(*pos, *rot, *scale))) Ok(Self(math::Transform::new(*pos, *rot, *scale)))
}); });
methods.add_function("from_translation", |_, (pos,): (LuaVec3,)| { methods.add_function("from_translation", |lua, vals: mlua::MultiValue| {
Ok(Self(math::Transform::from_translation(*pos))) let vals_clone = vals.clone();
}); if let Ok((x, y, z)) = <(f32, f32, f32) as FromLuaMulti>::from_lua_multi(vals, lua) {
Ok(Self(math::Transform::from_xyz(x, y, z)))
methods.add_function("from_xyz", |_, (x, y, z)| { } else if let Ok(v) = LuaVec3::from_lua_multi(vals_clone, lua) {
Ok(Self(math::Transform::from_xyz(x, y, z))) Ok(Self(math::Transform::from_translation(*v)))
} else {
Err(mlua::Error::BadArgument {
to: Some("Transform:from_translation".into()),
pos: 2,
name: None,
cause: Arc::new(mlua::Error::runtime(
"expected (number, number, number) or (Vec3), received neither"
)),
})
}
}); });
methods.add_method("clone", |_, this, ()| { methods.add_method("clone", |_, this, ()| {