lyra-engine/lyra-scripting/scripts/lua/math/quat.lua

182 lines
5.0 KiB
Lua

---@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