Refactored mat4

Did some tidying up on quat, vec2, vec3, and utils
This commit is contained in:
karai17 2016-07-19 23:55:21 -03:00
parent f9d31b20fb
commit 4f9bc17bf4
8 changed files with 494 additions and 287 deletions

View File

@ -77,7 +77,7 @@ function mat4.identity()
}
end
function mat4.from_axis_angle(angle, axis)
function mat4.from_angle_axis(angle, axis)
if type(angle) == "table" then
angle, axis = vec3.to_angle_axis(angle)
end
@ -119,6 +119,39 @@ function mat4.from_direction(direction, up)
return out
end
function mat4.from_transform(trans, rot, scale)
local angle, axis = vec3.to_angle_axis(rot)
local l = vec3.len(axis)
if l == 0 then
return new()
end
local x, y, z = axis.x / l, axis.y / l, axis.z / l
local c = cos(angle)
local s = sin(angle)
return new {
x*x*(1-c)+c, y*x*(1-c)+z*s, x*z*(1-c)-y*s, 0,
x*y*(1-c)-z*s, y*y*(1-c)+c, y*z*(1-c)+x*s, 0,
x*z*(1-c)+y*s, y*z*(1-c)-x*s, z*z*(1-c)+c, 0,
trans.x, trans.y, trans.z, 1
}
end
function mat4.from_ortho(left, right, top, bottom, near, far)
local out = new()
out[1] = 2 / (right - left)
out[6] = 2 / (top - bottom)
out[11] = -2 / (far - near)
out[13] = -((right + left) / (right - left))
out[14] = -((top + bottom) / (top - bottom))
out[15] = -((far + near) / (far - near))
out[16] = 1
return out
end
function mat4.from_perspective(fovy, aspect, near, far)
assert(aspect ~= 0)
assert(near ~= far)
@ -135,19 +168,6 @@ function mat4.from_perspective(fovy, aspect, near, far)
return out
end
function mat4.from_ortho(left, right, top, bottom, near, far)
local out = new()
out[1] = 2 / (right - left)
out[6] = 2 / (top - bottom)
out[11] = -2 / (far - near)
out[13] = -((right + left) / (right - left))
out[14] = -((top + bottom) / (top - bottom))
out[15] = -((far + near) / (far - near))
out[16] = 1
return out
end
-- Adapted from the Oculus SDK.
function mat4.from_hmd_perspective(tanHalfFov, zNear, zFar, flipZ, farAtInfinity)
-- CPML is right-handed and intended for GL, so these don't need to be arguments.
@ -236,14 +256,14 @@ function mat4.clone(a)
end
function mat4.mul(out, a, b)
out[1] = a[1] * b[1] + a[2] * b[5] + a [3] * b[9] + a[4] * b[13]
out[2] = a[1] * b[2] + a[2] * b[6] + a [3] * b[10] + a[4] * b[14]
out[3] = a[1] * b[3] + a[2] * b[7] + a [3] * b[11] + a[4] * b[15]
out[4] = a[1] * b[4] + a[2] * b[8] + a [3] * b[12] + a[4] * b[16]
out[5] = a[5] * b[1] + a[6] * b[5] + a [7] * b[9] + a[8] * b[13]
out[6] = a[5] * b[2] + a[6] * b[6] + a [7] * b[10] + a[8] * b[14]
out[7] = a[5] * b[3] + a[6] * b[7] + a [7] * b[11] + a[8] * b[15]
out[8] = a[5] * b[4] + a[6] * b[8] + a [7] * b[12] + a[8] * b[16]
out[1] = a[1] * b[1] + a[2] * b[5] + a[3] * b[9] + a[4] * b[13]
out[2] = a[1] * b[2] + a[2] * b[6] + a[3] * b[10] + a[4] * b[14]
out[3] = a[1] * b[3] + a[2] * b[7] + a[3] * b[11] + a[4] * b[15]
out[4] = a[1] * b[4] + a[2] * b[8] + a[3] * b[12] + a[4] * b[16]
out[5] = a[5] * b[1] + a[6] * b[5] + a[7] * b[9] + a[8] * b[13]
out[6] = a[5] * b[2] + a[6] * b[6] + a[7] * b[10] + a[8] * b[14]
out[7] = a[5] * b[3] + a[6] * b[7] + a[7] * b[11] + a[8] * b[15]
out[8] = a[5] * b[4] + a[6] * b[8] + a[7] * b[12] + a[8] * b[16]
out[9] = a[9] * b[1] + a[10] * b[5] + a[11] * b[9] + a[12] * b[13]
out[10] = a[9] * b[2] + a[10] * b[6] + a[11] * b[10] + a[12] * b[14]
out[11] = a[9] * b[3] + a[10] * b[7] + a[11] * b[11] + a[12] * b[15]
@ -256,6 +276,15 @@ function mat4.mul(out, a, b)
return out
end
function mat4.mul_mat41(out, a, b)
out[1] = b[1] * a[1] + b[2] * a[5] + b [3] * a[9] + b[4] * a[13]
out[2] = b[1] * a[2] + b[2] * a[6] + b [3] * a[10] + b[4] * a[14]
out[3] = b[1] * a[3] + b[2] * a[7] + b [3] * a[11] + b[4] * a[15]
out[4] = b[1] * a[4] + b[2] * a[8] + b [3] * a[12] + b[4] * a[16]
return out
end
function mat4.invert(out, a)
out[1] = a[6] * a[11] * a[16] - a[6] *
a[12] * a[15] - a[10] * a[7] * a[16] +
@ -350,26 +379,6 @@ function mat4.invert(out, a)
return out
end
function mat4.compose_world_matrix(t, rot, scale)
local angle, axis = vec3.to_angle_axis(rot)
local l = vec3.len(axis)
if l == 0 then
return new()
end
local x, y, z = axis.x / l, axis.y / l, axis.z / l
local c = cos(angle)
local s = sin(angle)
return new {
x*x*(1-c)+c, y*x*(1-c)+z*s, x*z*(1-c)-y*s, 0,
x*y*(1-c)-z*s, y*y*(1-c)+c, y*z*(1-c)+x*s, 0,
x*z*(1-c)+y*s, y*z*(1-c)-x*s, z*z*(1-c)+c, 0,
t.x, t.y, t.z, 1
}
end
function mat4.scale(out, a, s)
local m = new {
s.x, 0, 0, 0,
@ -395,7 +404,7 @@ function mat4.rotate(out, a, angle, axis)
local x, y, z = axis.x / l, axis.y / l, axis.z / l
local c = cos(angle)
local s = sin(angle)
local m = {
local m = new {
x*x*(1-c)+c, y*x*(1-c)+z*s, x*z*(1-c)-y*s, 0,
x*y*(1-c)-z*s, y*y*(1-c)+c, y*z*(1-c)+x*s, 0,
x*z*(1-c)+y*s, y*z*(1-c)-x*s, z*z*(1-c)+c, 0,
@ -423,7 +432,7 @@ function mat4.shear(out, a, yx, zx, xy, zy, xz, yz)
local zy = zy or 0
local xz = xz or 0
local yz = yz or 0
local m = {
local m = new {
1, yx, zx, 0,
xy, 1, zy, 0,
xz, yz, 1, 0,
@ -476,10 +485,69 @@ function mat4.transpose(out, a)
return out
end
function mat4.tostring()
-- https://github.com/g-truc/glm/blob/master/glm/gtc/matrix_transform.inl#L317
-- Note: GLM calls the view matrix "model"
function mat4.project(obj, view, projection, viewport)
local position = { obj.x, obj.y, obj.z, 1 }
position = mat4.mul(position, mat4.transpose(mat4(), view))
position = mat4.mul(position, mat4.transpose(mat4(), projection))
position[1] = position[1] / position[4] * 0.5 + 0.5
position[2] = position[2] / position[4] * 0.5 + 0.5
position[3] = position[3] / position[4] * 0.5 + 0.5
position[4] = position[4] / position[4] * 0.5 + 0.5
position[1] = position[1] * viewport[3] + viewport[1]
position[2] = position[2] * viewport[4] + viewport[2]
return vec3(position[1], position[2], position[3])
end
-- https://github.com/g-truc/glm/blob/master/glm/gtc/matrix_transform.inl#L338
-- Note: GLM calls the view matrix "model"
function mat4.unproject(win, view, projection, viewport)
local inverse = mat4()
mat4.mul(inverse, view, projection)
mat4.invert(inverse, inverse)
local position = { win.x, win.y, win.z, 1 }
position[1] = (position[1] - viewport[1]) / viewport[3]
position[2] = (position[2] - viewport[2]) / viewport[4]
position[1] = position[1] * 2 - 1
position[2] = position[2] * 2 - 1
position[3] = position[3] * 2 - 1
position[4] = position[4] * 2 - 1
mat4.mul(position, inverse, position)
position[1] = position[1] / position[4]
position[2] = position[2] / position[4]
position[3] = position[3] / position[4]
return vec3(position[1], position[2], position[3])
end
function mat4.is_mat4(a)
if not type(a) == "table" and not type(a) == "cdata" then
return false
end
for i = 1, 16 do
if type(a[i]) ~= "number" then
return false
end
end
return true
end
function mat4.to_string()
local str = "[ "
for i, v in ipairs(a) do
str = str .. string.format("%2.5f", v)
str = str .. string.format("%+0.3f", v)
if i < #a then
str = str .. ", "
end
@ -488,39 +556,44 @@ function mat4.tostring()
return str
end
function mat4.to_vec4s(a)
return {
{ a[1], a[2], a[3], a[4] },
{ a[5], a[6], a[7], a[8] },
{ a[9], a[10], a[11], a[12] },
{ a[13], a[14], a[15], a[16] }
}
end
function mat4.to_quat(a)
local m = mat4.to_vec4s(mat4.transpose(out, a))
local w = sqrt(1 + m[1][1] + m[2][2] + m[3][3]) / 2
local scale = w * 4
return quat.normalize(quat(), quat.new(
local q = quat.new(
m[3][2] - m[2][3] / scale,
m[1][3] - m[3][1] / scale,
m[2][1] - m[1][2] / scale,
w
))
)
return quat.normalize(q, q)
end
local mat4_mt = {}
mat4_mt.__index = mat4
mat4_mt.__tostring = mat4.tostring
mat4_mt.__tostring = mat4.to_string
function mat4_mt.__call(a)
function mat4_mt.__call(self, a)
return mat4.new(a)
end
function mat4_mt.__tostring(a)
return mat4.tostring(a)
end
function mat4_mt.__unm(a)
return mat4.invert(new(), a)
end
function mat4_mt.__eq(a, b)
assert(mat4.ismat4(a), "__eq: Wrong argument type for left hand operant. (<cpml.mat4> expected)")
assert(mat4.ismat4(b), "__eq: Wrong argument type for right hand operant. (<cpml.mat4> expected)")
assert(mat4.is_mat4(a), "__eq: Wrong argument type for left hand operant. (<cpml.mat4> expected)")
assert(mat4.is_mat4(b), "__eq: Wrong argument type for right hand operant. (<cpml.mat4> expected)")
for i = 1, 16 do
if a[i] ~= b[i] then
@ -532,10 +605,14 @@ function mat4_mt.__eq(a, b)
end
function mat4_mt.__mul(a, b)
assert(mat4.ismat4(a), "__mul: Wrong argument type for left hand operant. (<cpml.mat4> expected)")
assert(mat4.ismat4(b), "__mul: Wrong argument type for right hand operant. (<cpml.mat4> expected)")
assert(mat4.is_mat4(a), "__mul: Wrong argument type for left hand operant. (<cpml.mat4> expected)")
assert(mat4.is_mat4(b) or #b == 4, "__mul: Wrong argument type for right hand operant. (<cpml.mat4> or table #4 expected)")
return mat4.mul(new(), a, b)
if mat4.is_mat4(b) then
return mat4.mul(new(), a, b)
end
return mat4.mul_mat41({}, a, b)
end
if status then

View File

@ -1,21 +1,25 @@
--- Mesh utilities
-- @module mesh
local current_folder = (...):gsub('%.[^%.]+$', '') .. "."
local vec3 = require(current_folder .. "vec3")
local mesh = {}
local modules = (...):gsub('%.[^%.]+$', '') .. "."
local vec3 = require(modules .. "vec3")
local mesh = {}
function mesh.compute_normal(a, b, c)
return (c - a):cross(b - a):normalize()
local out = vec3()
local ca = vec3.sub(vec3(), c, a)
local ba = vec3.sub(vec3(), b, a)
vec3.cross(out, ca, ba)
vec3.normalize(out, out)
return out
end
function mesh.average(vertices)
local avg = vec3(0,0,0)
local out = vec3(0, 0, 0)
for _, v in ipairs(vertices) do
avg = avg + v
vec3.add(out, out, v)
end
return avg / #vertices
return vec3.div(out, out, #vertices)
end
return mesh

View File

@ -1,20 +1,22 @@
--- A quaternion and associated utilities.
-- @module quat
local current_folder = (...):gsub('%.[^%.]+$', '') .. "."
local constants = require(current_folder .. "constants")
local vec3 = require(current_folder .. "vec3")
local ffi = require "ffi"
local DOT_THRESHOLD = constants.DOT_THRESHOLD
local FLT_EPSILON = constants.FLT_EPSILON
local abs, acos, asin, atan2 = math.abs, math.acos, math.asin, math.atan2
local cos, sin, min, max, pi = math.cos, math.sin, math.min, math.max, math.pi
local sqrt = math.sqrt
local quat = {}
local modules = (...):gsub('%.[^%.]+$', '') .. "."
local constants = require(modules .. "constants")
local vec3 = require(modules .. "vec3")
local DOT_THRESHOLD = constants.DOT_THRESHOLD
local DBL_EPSILON = constants.DBL_EPSILON
local abs = math.abs
local acos = math.acos
local asin = math.asin
local atan2 = math.atan2
local cos = math.cos
local sin = math.sin
local min = math.min
local max = math.max
local sqrt = math.sqrt
local pi = math.pi
local quat = {}
-- Private constructor.
local function new(x, y, z, w)
@ -23,6 +25,9 @@ local function new(x, y, z, w)
return setmetatable(q, quat_mt)
end
quat.unit = new(0, 0, 0, 1)
quat.zero = new(0, 0, 0, 0)
-- Do the check to see if JIT is enabled. If so use the optimized FFI structs.
local status, ffi
if type(jit) == "table" and jit.status() then
@ -48,7 +53,7 @@ function quat.new(x, y, z, w)
assert(type(z) == "number", "new: Wrong argument type for z (<number> expected)")
assert(type(w) == "number", "new: Wrong argument type for w (<number> expected)")
return new(x, y. z, w)
return new(x, y, z, w)
-- {x=x, y=y, z=z, w=w} or {x, y, z, w}
elseif type(x) == "table" then
@ -66,16 +71,14 @@ function quat.new(x, y, z, w)
end
--- Create a quaternion from an axis, angle pair.
-- @tparam vec3 axis
-- @tparam number angle
-- @tparam vec3 axis
-- @treturn quat
function quat.from_axis_angle(axis, angle)
function quat.from_angle_axis(angle, axis)
local len = vec3.len(axis)
local s = sin(angle * 0.5)
local c = cos(angle * 0.5)
return quat.new(axis.x*s, axis.y*s, axis.z*s, c)
local s = sin(angle * 0.5)
local c = cos(angle * 0.5)
return new(axis.x * s, axis.y * s, axis.z * s, c)
end
--- Create a quaternion from a normalized, up vector pair.
@ -83,17 +86,16 @@ end
-- @tparam vec3 up
-- @treturn quat
function quat.from_direction(normal, up)
local a = vec3.cross(vec3(), up, normal)
local d = vec3.dot(up, normal)
local a = vec3()
vec3.cross(a, up, normal)
return quat.new(a.x, a.y, a.z, d+1)
return new(a.x, a.y, a.z, d + 1)
end
--- Clone a quaternion.
-- @tparam quat a
-- @treturn quat clone
function quat.clone(a)
new(a.x, a.y, a.z, a.w)
return new(a.x, a.y, a.z, a.w)
end
--- Component-wise add a quaternion.
@ -148,6 +150,7 @@ function quat.mul_vec3(out, a, b)
vec3.add(out, out, uuv)
vec3.mul(out, out, 2)
vec3.add(out, b, out)
return out
end
--- Pow a quaternion by an exponent
@ -178,6 +181,38 @@ function quat.pow(out, a, n)
return out
end
--- Normalize a quaternion.
-- @tparam quat out
-- @tparam quat a
-- @treturn quat out
function quat.normalize(out, a)
local l = 1 / quat.len(a)
quat.scale(out, a, l)
return out
end
--- Return the inner angle between two quaternions.
-- @tparam quat a
-- @tparam quat b
-- @treturn number angle
function quat.dot(a, b)
return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w
end
--- Return the length of a quaternion.
-- @tparam quat a
-- @treturn number len
function quat.len(a)
return sqrt(a.x * a.x + a.y * a.y + a.z * a.z + a.w * a.w)
end
--- Return the squared length of a quaternion.
-- @tparam quat a
-- @treturn number len
function quat.len2(a)
return a.x * a.x + a.y * a.y + a.z * a.z + a.w * a.w
end
--- Component-wise scale a quaternion by a scalar.
-- @tparam quat out
-- @tparam quat a
@ -199,7 +234,7 @@ function quat.conjugate(out, a)
out.x = -a.x
out.y = -a.y
out.z = -a.z
out.w = a.w
out.w = a.w
return out
end
@ -218,6 +253,7 @@ end
-- @tparam quat a
-- @treturn quat out
function quat.reciprocal(out, a)
assert(not quat.is_zer(a), "Cannot reciprocate a zero quaternion")
local l = quat.len2(a)
quat.conjugate(out, a)
quat.scale(out, out, 1 / l)
@ -270,20 +306,10 @@ function quat.slerp(out, a, b, s)
return out
end
--- Normalize a quaternion.
-- @tparam quat out
-- @tparam quat a
-- @treturn quat out
function quat.normalize(out, a)
local l = 1 / quat.len(a)
quat.scale(out, a, l)
return out
end
--- Return the imaginary part of the quaternion as a vec3.
-- @tparam vec3 out
-- @tparam quat a
-- @treturn quat out
-- @treturn vec3 out
function quat.imaginary(out, a)
out.x = a.x
out.y = a.y
@ -298,28 +324,6 @@ function quat.real(a)
return a.w
end
--- Return the inner angle between two quaternions.
-- @tparam quat a
-- @tparam quat b
-- @treturn number angle
function quat.dot(a, b)
return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w
end
--- Return the length of a quaternion.
-- @tparam quat a
-- @treturn number len
function quat.len(a)
return sqrt(a.x * a.x + a.y * a.y + a.z * a.z + a.w * a.w)
end
--- Return the squared length of a quaternion.
-- @tparam quat a
-- @treturn number len
function quat.len2(a)
return a.x * a.x + a.y * a.y + a.z * a.z + a.w * a.w
end
--- Unpack a quaternion into form x,y,z,w.
-- @tparam quat a
-- @treturn number x
@ -330,17 +334,46 @@ function quat.unpack(a)
return a.x, a.y, a.z, a.w
end
function quat.to_angle_axis(a)
if a.w > 1 or a.w < -1 then
a = quat.normalize(a, a)
end
local angle = 2 * acos(a.w)
local s = sqrt(1 - a.w * a.w)
local x, y, z
if s < constants.DBL_EPSILON then
x = a.x
y = a.y
z = a.z
else
x = a.x / s -- normalize axis
y = a.y / s
z = a.z / s
end
return angle, vec3(x, y, z)
end
function quat.to_vec3(out, a)
out.x = a.x
out.y = a.y
out.z = a.z
return out
end
--- Return a string formatted "{x, y, z, w}"
-- @tparam quat a
-- @treturn string
function quat.tostring(a)
function quat.to_string(a)
return string.format("(%+0.3f,%+0.3f,%+0.3f,%+0.3f)", a.x, a.y, a.z, a.w)
end
--- Return a boolean showing if a table is or is not a quat
-- @param q object to be tested
-- @treturn boolean
function quat.isquat(q)
function quat.is_quat(q)
return
(
type(v) == "table" or
@ -352,67 +385,74 @@ function quat.isquat(q)
type(v.w) == "number"
end
local quat_mt = {}
function quat.is_zero(a)
return
a.x == 0 and
a.y == 0 and
a.z == 0 and
a.w == 0
end
quat_mt.__index = quat
quat_mt.__tostring = quat.tostring
function quat.is_real(a)
return
a.x == 0 and
a.y == 0 and
a.z == 0
end
function quat_mt.__call(self, x, y, z)
return quat.new(x, y, z, w)
function quat.is_imaginary(a)
return a.w == 0
end
local quat_mt = {}
quat_mt.__index = quat
quat_mt.__tostring = quat.to_string
function quat_mt.__call(self, x, y, z, w)
return new(x, y, z, w)
end
function quat_mt.__unm(a)
local temp = quat.new()
quat.scale(temp, a, -1)
return temp
return quat.scale(new(), a, -1)
end
function quat_mt.__eq(a,b)
assert(quat.isquat(a), "__eq: Wrong argument type for left hand operant. (<cpml.quat> expected)")
assert(quat.isquat(b), "__eq: Wrong argument type for right hand operant. (<cpml.quat> expected)")
assert(quat.is_quat(a), "__eq: Wrong argument type for left hand operant. (<cpml.quat> expected)")
assert(quat.is_quat(b), "__eq: Wrong argument type for right hand operant. (<cpml.quat> expected)")
return a.x == b.x and a.y == b.y and a.z == b.z and a.w == b.w
end
function quat_mt.__add(a, b)
assert(quat.isquat(a), "__add: Wrong argument type for left hand operant. (<cpml.quat> expected)")
assert(quat.isquat(b), "__add: Wrong argument type for right hand operant. (<cpml.quat> expected)")
local temp = quat.new()
quat.add(temp, a, b)
return temp
assert(quat.is_quat(a), "__add: Wrong argument type for left hand operant. (<cpml.quat> expected)")
assert(quat.is_quat(b), "__add: Wrong argument type for right hand operant. (<cpml.quat> expected)")
return quat.add(new(), a, b)
end
function quat_mt.__sub(a, b)
assert(quat.isquat(a), "__sub: Wrong argument type for left hand operant. (<cpml.quat> expected)")
assert(quat.isquat(b), "__sub: Wrong argument type for right hand operant. (<cpml.quat> expected)")
local temp = quat.new()
quat.sub(temp, a, b)
return temp
assert(quat.is_quat(a), "__sub: Wrong argument type for left hand operant. (<cpml.quat> expected)")
assert(quat.is_quat(b), "__sub: Wrong argument type for right hand operant. (<cpml.quat> expected)")
return quat.sub(new(), a, b)
end
function quat_mt.__mul(a, b)
assert(quat.isquat(a), "__mul: Wrong argument type for left hand operant. (<cpml.quat> expected)")
assert(quat.isquat(b) or vec3.isvec3(b), "__mul: Wrong argument type for right hand operant. (<cpml.quat> or <cpml.vec3> expected)")
assert(quat.is_quat(a), "__mul: Wrong argument type for left hand operant. (<cpml.quat> expected)")
assert(quat.is_quat(b) or vec3.is_vec3(b) or type(b) = "number", "__mul: Wrong argument type for right hand operant. (<cpml.quat> or <cpml.vec3> expected)")
if quat.isquat(b) then
local temp = quat.new()
quat.mul(temp, a, b)
return temp
elseif vec3.isvec3(b) then
local temp = vec3()
quat.mul_vec3(temp, a, b)
return temp
if quat.is_quat(b) then
return quat.mul(new(), a, b)
end
if type(b) == "number" then
return quat.scale(new(), a, b)
end
return quat.mul_vec3(vec3(), a, b)
end
function quat_mt.__pow(a, n)
assert(quat.isquat(a), "__pow: Wrong argument type for left hand operant. (<cpml.quat> expected)")
assert(quat.is_quat(a), "__pow: Wrong argument type for left hand operant. (<cpml.quat> expected)")
assert(type(b) == "number", "__pow: Wrong argument type for right hand operant. (<number> expected)")
local temp = quat.new()
quat.pow(temp, a, n)
return temp
return quat.pow(new(), a, n)
end
if status then

View File

@ -1,15 +1,25 @@
--- Various utility functions
-- @module utils
local utils = {}
local modules = (...): gsub('%.[^%.]+$', '') .. "."
local vec2 = require(modules .. "vec2")
local vec3 = require(modules .. "vec3")
local atan2 = math.atan2
local acos = math.acos
local sqrt = math.sqrt
local abs = math.abs
local ceil = math.ceil
local floor = math.floor
local log = math.log
local utils = {}
-- reimplementation of math.frexp, due to its removal from Lua 5.3 :(
-- courtesy of airstruck
local log2 = math.log(2)
local log2 = log(2)
local frexp = math.frexp or function(x)
if x == 0 then return 0, 0 end
local e = math.floor(math.log(math.abs(x)) / log2 + 1)
local e = floor(log(abs(x)) / log2 + 1)
return x / 2 ^ e, e
end
@ -27,7 +37,7 @@ end
-- @param size
-- @return number
function utils.deadzone(value, size)
return math.abs(value) >= size and value or 0
return abs(value) >= size and value or 0
end
--- Check if value is equal or greater than threshold.
@ -36,7 +46,7 @@ end
-- @return boolean
function utils.threshold(value, threshold)
-- I know, it barely saves any typing at all.
return math.abs(value) >= threshold
return abs(value) >= threshold
end
--- Scales a value from one range to another.
@ -79,7 +89,7 @@ end
-- @return number
function utils.round(value, precision)
if precision then return utils.round(value / precision) * precision end
return value >= 0 and math.floor(value+0.5) or math.ceil(value-0.5)
return value >= 0 and floor(value+0.5) or ceil(value-0.5)
end
--- Wrap `value` around if it exceeds `limit`.
@ -104,4 +114,87 @@ function utils.is_pot(value)
return (frexp(value)) == 0.5
end
-- Originally from vec3
function utils.project_on(out, a, b)
local isvec3 = vec3.is_vec3(out)
local s =
(a.x * b.x + a.y * b.y + isvec3 and a.z * b.z or 0) /
(b.x * b.x + b.y * b.y + isvec3 and b.z * b.z or 0)
out.x = b.x * s
out.y = b.y * s
out.z = isvec3 and b.z * s or nil
return out
end
-- Originally from vec3
function utils.project_from(out, a, b)
local isvec3 = vec3.is_vec3(out)
local s =
(b.x * b.x + b.y * b.y + isvec3 and b.z * b.z or 0) /
(a.x * b.x + a.y * b.y + isvec3 and a.z * b.z or 0)
out.x = b.x * s
out.y = b.y * s
out.z = isvec3 and b.z * s or nil
return out
end
-- Originally from vec3
function utils.mirror_on(out, a, b)
local isvec3 = vec3.is_vec3(out)
local s =
(a.x * b.x + a.y * b.y + isvec3 and a.z * b.z or 0) /
(b.x * b.x + b.y * b.y + isvec3 and b.z * b.z or 0) * 2
out.x = b.x * s - a.x
out.y = b.y * s - a.y
out.z = isvec3 and b.z * s - a.z or nil
return out
end
-- Originally from vec3
function utils.reflect(out, i, n)
vec3.mul(out, n, vec3.dot(n, i) * 2)
vec3.sub(out, i, out)
return out
end
-- Originally from vec3
function utils.refract(out, i, n, ior)
local d = vec3.dot(n, i)
local k = 1 - ior * ior * (1 - d * d)
if k >= 0 then
vec3.mul(out, i, ior)
vec3.mul(tmp, n, ior * d + sqrt(k))
vec3.sub(out, out, tmp)
end
return out
end
-- Originally from vec3
function utils.angle_to(a, b)
if b then
return atan2(a.y - b.y, a.x - b.x)
end
return atan2(a.y, a.x)
end
-- Originally from vec3
function utils.angle_between(a, b)
if b then
if vec2.is_vec2(a) then
return acos(vec2.dot(a, b) / (vec2.len(a) * vec2.len(b)))
end
return acos(vec3.dot(a, b) / (vec3.len(a) * vec3.len(b)))
end
return 0
end
return utils

View File

@ -5,7 +5,6 @@ local atan2 = math.atan2
local sqrt = math.sqrt
local cos = math.cos
local sin = math.sin
local pi = math.pi
local vec2 = {}
-- Private constructor.
@ -15,6 +14,10 @@ local function new(x, y)
return setmetatable(v, vec2_mt)
end
vec2.unit_x = new(1, 0)
vec2.unit_y = new(0, 1)
vec2.zero = new(0, 0)
-- Do the check to see if JIT is enabled. If so use the optimized FFI structs.
local status, ffi
if type(jit) == "table" and jit.status() then
@ -55,6 +58,17 @@ function vec2.new(x, y)
end
end
--- Convert point from polar to cartesian.
-- @tparam vec2 out vector for result to be stored in
-- @tparam number radius radius of the point
-- @tparam number theta angle of the point (in radians)
-- @treturn vec2
function vec2.from_cartesian(out, radius, theta)
out.x = radius * cos(theta)
out.y = radius * sin(theta)
return out
end
--- Clone a vector.
-- @tparam vec2 a vector to be cloned
-- @treturn vec2
@ -173,6 +187,19 @@ function vec2.dist2(a, b)
return dx * dx + dy * dy
end
function vec2.rotate(out, a, phi)
local c, s = cos(phi), sin(phi)
out.x = c * a.x - s * a.y
out.y = s * a.x + c * a.y
return out
end
function vec2.perpendicular(out, a)
out.x = -a.y
out.y = a.x
return out
end
--- Lerp between two vectors.
-- @tparam vec2 out vector for result to be stored in
-- @tparam vec2 a first vector
@ -194,17 +221,10 @@ function vec2.unpack(a)
return a.x, a.y
end
--- Return a string formatted "{x, y}"
-- @tparam vec2 a the vector to be turned into a string
-- @treturn string
function vec2.tostring(a)
return string.format("(%+0.3f,%+0.3f)", a.x, a.y)
end
--- Return a boolean showing if a table is or is not a vec2
-- @param v the object to be tested
-- @treturn boolean
function vec2.isvec2(v)
function vec2.is_vec2(v)
return
(
type(v) == "table" or
@ -214,17 +234,6 @@ function vec2.isvec2(v)
type(v.y) == "number"
end
--- Convert point from polar to cartesian.
-- @tparam vec2 out vector for result to be stored in
-- @tparam number radius radius of the point
-- @tparam number theta angle of the point (in radians)
-- @treturn vec2
function vec2.to_cartesian(out, radius, theta)
out.x = radius * cos(theta)
out.y = radius * sin(theta)
return out
end
--- Convert point from cartesian to polar.
-- @tparam vec2 a vector to convert
-- @treturn number radius
@ -232,71 +241,59 @@ end
function vec2.to_polar(a)
local radius = sqrt(a.x^2 + a.y^2)
local theta = atan2(a.y, a.x)
theta = theta > 0 and theta or theta + 2 * pi
theta = theta > 0 and theta or theta + 2 * math.pi
return radius, theta
end
local vec2_mt = {}
--- Return a string formatted "{x, y}"
-- @tparam vec2 a the vector to be turned into a string
-- @treturn string
function vec2.to_string(a)
return string.format("(%+0.3f,%+0.3f)", a.x, a.y)
end
vec2_mt.__index = vec2
vec2_mt.__tostring = vec2.tostring
local vec2_mt = {}
vec2_mt.__index = vec2
vec2_mt.__tostring = vec2.to_string
function vec2_mt.__call(self, x, y)
return vec2.new(x, y)
end
function vec2_mt.__tostring(a)
return vec2.tostring(a)
end
function vec2_mt.__unm(a)
return vec2.new(-a.x, -a.y)
end
function vec2_mt.__eq(a,b)
assert(vec2.isvec2(a), "__eq: Wrong argument type for left hand operant. (<cpml.vec2> expected)")
assert(vec2.isvec2(b), "__eq: Wrong argument type for right hand operant. (<cpml.vec2> expected)")
assert(vec2.is_vec2(a), "__eq: Wrong argument type for left hand operant. (<cpml.vec2> expected)")
assert(vec2.is_vec2(b), "__eq: Wrong argument type for right hand operant. (<cpml.vec2> expected)")
return a.x == b.x and a.y == b.y
end
function vec2_mt.__add(a, b)
assert(vec2.isvec2(a), "__add: Wrong argument type for left hand operant. (<cpml.vec2> expected)")
assert(vec2.isvec2(b), "__add: Wrong argument type for right hand operant. (<cpml.vec2> expected)")
assert(vec2.is_vec2(a), "__add: Wrong argument type for left hand operant. (<cpml.vec2> expected)")
assert(vec2.is_vec2(b), "__add: Wrong argument type for right hand operant. (<cpml.vec2> expected)")
return vec2.add(new(), a, b)
end
function vec2_mt.__sub(a, b)
assert(vec2.isvec2(a), "__add: Wrong argument type for left hand operant. (<cpml.vec2> expected)")
assert(vec2.isvec2(b), "__add: Wrong argument type for right hand operant. (<cpml.vec2> expected)")
assert(vec2.is_vec2(a), "__add: Wrong argument type for left hand operant. (<cpml.vec2> expected)")
assert(vec2.is_vec2(b), "__add: Wrong argument type for right hand operant. (<cpml.vec2> expected)")
return vec2.sub(new(), a, b)
end
function vec2_mt.__mul(a, b)
local isvecb = vec2.isvec2(b)
a, b = isvecb and b or a, isvecb and a or b
assert(vec2.isvec2(a), "__mul: Wrong argument type for left hand operant. (<cpml.vec2> expected)")
assert(vec2.is_vec2(a), "__mul: Wrong argument type for left hand operant. (<cpml.vec2> expected)")
assert(type(b) == "number", "__mul: Wrong argument type for right hand operant. (<number> expected)")
return vec2.mul(new(), a, b)
end
function vec2_mt.__div(a, b)
local isvecb = vec2.isvec2(b)
a, b = isvecb and b or a, isvecb and a or b
assert(vec2.isvec2(a), "__div: Wrong argument type for left hand operant. (<cpml.vec2> expected)")
assert(vec2.is_vec2(a), "__div: Wrong argument type for left hand operant. (<cpml.vec2> expected)")
assert(type(b) == "number", "__div: Wrong argument type for right hand operant. (<number> expected)")
return vec2.div(new(), a, b)
end
vec2.unit_x = new(1, 0)
vec2.unit_y = new(0, 1)
if status then
ffi.metatype(new, vec2_mt)
end

View File

@ -11,6 +11,11 @@ local function new(x, y, z)
return setmetatable(v, vec3_mt)
end
vec3.unit_x = new(1, 0, 0)
vec3.unit_y = new(0, 1, 0)
vec3.unit_z = new(0, 0, 1)
vec3.zero = new(0, 0, 0)
-- Do the check to see if JIT is enabled. If so use the optimized FFI structs.
local status, ffi
if type(jit) == "table" and jit.status() then
@ -185,6 +190,36 @@ function vec3.dist2(a, b)
return dx * dx + dy * dy + dz * dz
end
--- Rotate vector about an axis.
-- @param phi Amount to rotate, in radians
-- @param axis Axis to rotate by
-- @return vec3
function vec3.rotate(out, a, phi, axis)
if not vec3.is_vec3(axis) then
return a
end
local u = vec3.normalize(vec3(), axis)
local c, s = cos(phi), sin(phi)
-- Calculate generalized rotation matrix
local m1 = new((c + u.x * u.x * (1 - c)), (u.x * u.y * (1 - c) - u.z * s), (u.x * u.z * (1 - c) + u.y * s))
local m2 = new((u.y * u.x * (1 - c) + u.z * s), (c + u.y * u.y * (1 - c)), (u.y * u.z * (1 - c) - u.x * s))
local m3 = new((u.z * u.x * (1 - c) - u.y * s), (u.z * u.y * (1 - c) + u.x * s), (c + u.z * u.z * (1 - c)) )
out.x = vec3.dot(a, m1)
out.y = vec3.dot(a, m2)
out.z = vec3.dot(a, m3)
return out
end
function vec3.perpendicular(out, a)
out.x = -a.y
out.y = a.x
out.z = 0
return out
end
--- Lerp between two vectors.
-- @tparam vec3 out vector for result to be stored in
-- @tparam vec3 a first vector
@ -207,108 +242,69 @@ function vec3.unpack(a)
return a.x, a.y, a.z
end
--- Return a string formatted "{x, y, z}"
-- @tparam vec3 a the vector to be turned into a string
-- @treturn string
function vec3.tostring(a)
return string.format("(%+0.3f,%+0.3f,%+0.3f)", a.x, a.y, a.z)
end
--- Return a boolean showing if a table is or is not a vec3
-- @param v the object to be tested
-- @treturn boolean
function vec3.isvec3(v)
function vec3.is_vec3(a)
return
(
type(v) == "table" or
type(v) == "cdata"
type(a) == "table" or
type(a) == "cdata"
) and
type(v.x) == "number" and
type(v.y) == "number" and
type(v.z) == "number"
type(a.x) == "number" and
type(a.y) == "number" and
type(a.z) == "number"
end
--- Return a string formatted "{x, y, z}"
-- @tparam vec3 a the vector to be turned into a string
-- @treturn string
function vec3.to_string(a)
return string.format("(%+0.3f,%+0.3f,%+0.3f)", a.x, a.y, a.z)
end
function vec3.reflect(out, i, n)
vec3.mul(out, n, 2.0 * vec3.dot(n, i))
vec3.sub(out, i, out)
return out
end
function vec3.refract(out, i, n, ior)
local d = vec3.dot(n, i)
local k = 1.0 - ior * ior * (1.0 - d * d)
if k >= 0.0 then
vec3.mul(out, i, ior)
vec3.mul(tmp, n, ior * d + sqrt(k))
vec3.sub(out, out, tmp)
end
return out
end
local vec3_mt = {}
vec3_mt.__index = vec3
vec3_mt.__tostring = vec3.tostring
local vec3_mt = {}
vec3_mt.__index = vec3
vec3_mt.__tostring = vec3.to_string
function vec3_mt.__call(self, x, y, z)
return vec3.new(x, y, z)
end
function vec3_mt.__tostring(a)
return vec3.tostring(a)
end
function vec3_mt.__unm(a)
return vec3.new(-a.x, -a.y, -a.z)
end
function vec3_mt.__eq(a,b)
assert(vec3.isvec3(a), "__eq: Wrong argument type for left hand operant. (<cpml.vec3> expected)")
assert(vec3.isvec3(b), "__eq: Wrong argument type for right hand operant. (<cpml.vec3> expected)")
assert(vec3.is_vec3(a), "__eq: Wrong argument type for left hand operant. (<cpml.vec3> expected)")
assert(vec3.is_vec3(b), "__eq: Wrong argument type for right hand operant. (<cpml.vec3> expected)")
return a.x == b.x and a.y == b.y and a.z == b.z
end
function vec3_mt.__add(a, b)
assert(vec3.isvec3(a), "__add: Wrong argument type for left hand operant. (<cpml.vec3> expected)")
assert(vec3.isvec3(b), "__add: Wrong argument type for right hand operant. (<cpml.vec3> expected)")
assert(vec3.is_vec3(a), "__add: Wrong argument type for left hand operant. (<cpml.vec3> expected)")
assert(vec3.is_vec3(b), "__add: Wrong argument type for right hand operant. (<cpml.vec3> expected)")
return vec3.add(new(), a, b)
end
function vec3_mt.__sub(a, b)
assert(vec3.isvec3(a), "__sub: Wrong argument type for left hand operant. (<cpml.vec3> expected)")
assert(vec3.isvec3(b), "__sub: Wrong argument type for right hand operant. (<cpml.vec3> expected)")
assert(vec3.is_vec3(a), "__sub: Wrong argument type for left hand operant. (<cpml.vec3> expected)")
assert(vec3.is_vec3(b), "__sub: Wrong argument type for right hand operant. (<cpml.vec3> expected)")
return vec3.sub(new(), a, b)
end
function vec3_mt.__mul(a, b)
local isvecb = vec3.isvec3(b)
a, b = isvecb and b or a, isvecb and a or b
assert(vec3.isvec3(a), "__mul: Wrong argument type for left hand operant. (<cpml.vec3> expected)")
assert(vec3.is_vec3(a), "__mul: Wrong argument type for left hand operant. (<cpml.vec3> expected)")
assert(type(b) == "number", "__mul: Wrong argument type for right hand operant. (<number> expected)")
return vec3.mul(new(), a, b)
end
function vec3_mt.__div(a, b)
local isvecb = vec3.isvec3(b)
a, b = isvecb and b or a, isvecb and a or b
assert(vec3.isvec3(a), "__div: Wrong argument type for left hand operant. (<cpml.vec3> expected)")
assert(vec3.is_vec3(a), "__div: Wrong argument type for left hand operant. (<cpml.vec3> expected)")
assert(type(b) == "number", "__div: Wrong argument type for right hand operant. (<number> expected)")
return vec3.div(new(), a, b)
end
vec3.unit_x = new(1, 0, 0)
vec3.unit_y = new(0, 1, 0)
vec3.unit_z = new(0, 0, 1)
if status then
ffi.metatype(new, vec3_mt)
end

View File

@ -8,7 +8,7 @@ describe("vec2:", function()
local a = vec2()
assert.is.equal(a.x, 0)
assert.is.equal(a.y, 0)
assert.is_true(vec2.isvec2(a))
assert.is_true(vec2.is_vec2(a))
-- new vector from table
local b = vec2 { 0, 0 }

View File

@ -9,7 +9,7 @@ describe("vec3:", function()
assert.is.equal(a.x, 0)
assert.is.equal(a.y, 0)
assert.is.equal(a.z, 0)
assert.is_true(vec3.isvec3(a))
assert.is_true(vec3.is_vec3(a))
-- new vector from table
local b = vec3 { 0, 0, 0 }