123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779 |
- --- double 4x4, 1-based, column major matrices
- -- @module mat4
- local modules = (...): gsub('%.[^%.]+$', '') .. "."
- local constants = require(modules .. "constants")
- local vec2 = require(modules .. "vec2")
- local vec3 = require(modules .. "vec3")
- local quat = require(modules .. "quat")
- local utils = require(modules .. "utils")
- local DBL_EPSILON = constants.DBL_EPSILON
- local sqrt = math.sqrt
- local cos = math.cos
- local sin = math.sin
- local tan = math.tan
- local rad = math.rad
- local mat4 = {}
- local mat4_mt = {}
- -- Private constructor.
- local function new(m)
- m = m or {
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0
- }
- m._m = m
- return setmetatable(m, mat4_mt)
- end
- -- Convert matrix into identity
- local function identity(m)
- m[1], m[2], m[3], m[4] = 1, 0, 0, 0
- m[5], m[6], m[7], m[8] = 0, 1, 0, 0
- m[9], m[10], m[11], m[12] = 0, 0, 1, 0
- m[13], m[14], m[15], m[16] = 0, 0, 0, 1
- return m
- end
- -- 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
- status, ffi = pcall(require, "ffi")
- if status then
- ffi.cdef "typedef struct { double _m[16]; } cpml_mat4;"
- new = ffi.typeof("cpml_mat4")
- end
- end
- -- Statically allocate a temporary variable used in some of our functions.
- local tmp = new()
- --- The public constructor.
- -- @param a Can be of four types: </br>
- -- table Length 16 (4x4 matrix)
- -- table Length 9 (3x3 matrix)
- -- table Length 4 (4 vec4s)
- -- nil
- -- @treturn mat4 out
- function mat4.new(a)
- local out = new()
- -- 4x4 matrix
- if type(a) == "table" and #a == 16 then
- for i = 1, 16 do
- out[i] = tonumber(a[i])
- end
- -- 3x3 matrix
- elseif type(a) == "table" and #a == 9 then
- out[1], out[2], out[3] = a[1], a[2], a[3]
- out[5], out[6], out[7] = a[4], a[5], a[6]
- out[9], out[10], out[11] = a[7], a[8], a[9]
- out[16] = 1
- -- 4 vec4s
- elseif type(a) == "table" and type(a[1]) == "table" then
- local idx = 1
- for i = 1, 4 do
- for j = 1, 4 do
- out[idx] = a[i][j]
- idx = idx + 1
- end
- end
- -- nil
- else
- out[1] = 1
- out[6] = 1
- out[11] = 1
- out[16] = 1
- end
- return out
- end
- --- Create an identity matrix.
- -- @tparam mat4 a Matrix to overwrite
- -- @treturn mat4 out
- function mat4.identity(a)
- return identity(a or new())
- end
- --- Create a matrix from an angle/axis pair.
- -- @tparam number angle Angle of rotation
- -- @tparam vec3 axis Axis of rotation
- -- @treturn mat4 out
- function mat4.from_angle_axis(angle, axis)
- local l = axis:len()
- 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,
- 0, 0, 0, 1
- }
- end
- --- Create a matrix from a quaternion.
- -- @tparam quat q Rotation quaternion
- -- @treturn mat4 out
- function mat4.from_quaternion(q)
- return mat4.from_angle_axis(q:to_angle_axis())
- end
- --- Create a matrix from a direction/up pair.
- -- @tparam vec3 direction Vector direction
- -- @tparam vec3 up Up direction
- -- @treturn mat4 out
- function mat4.from_direction(direction, up)
- local forward = direction:normalize()
- local side = forward:cross(up):normalize()
- local new_up = side:cross(forward):normalize()
- local out = new()
- out[1] = side.x
- out[5] = side.y
- out[9] = side.z
- out[2] = new_up.x
- out[6] = new_up.y
- out[10] = new_up.z
- out[3] = forward.x
- out[7] = forward.y
- out[11] = forward.z
- out[16] = 1
- return out
- end
- --- Create matrix from orthogonal.
- -- @tparam number left
- -- @tparam number right
- -- @tparam number top
- -- @tparam number bottom
- -- @tparam number near
- -- @tparam number far
- -- @treturn mat4 out
- 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
- --- Create matrix from perspective.
- -- @tparam number fovy Field of view
- -- @tparam number aspect Aspect ratio
- -- @tparam number near Near plane
- -- @tparam number far Far plane
- -- @treturn mat4 out
- function mat4.from_perspective(fovy, aspect, near, far)
- assert(aspect ~= 0)
- assert(near ~= far)
- local t = tan(rad(fovy) / 2)
- local out = new()
- out[1] = 1 / (t * aspect)
- out[6] = 1 / t
- out[11] = -(far + near) / (far - near)
- out[12] = -1
- out[15] = -(2 * far * near) / (far - near)
- out[16] = 0
- return out
- end
- -- Adapted from the Oculus SDK.
- --- Create matrix from HMD perspective.
- -- @tparam number tanHalfFov Tangent of half of the field of view
- -- @tparam number zNear Near plane
- -- @tparam number zFar Far plane
- -- @tparam boolean flipZ Z axis is flipped or not
- -- @tparam boolean farAtInfinity Far plane is infinite or not
- -- @treturn mat4 out
- 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.
- local rightHanded = true
- local isOpenGL = true
- local function CreateNDCScaleAndOffsetFromFov()
- local x_scale = 2 / (tanHalfFov.LeftTan + tanHalfFov.RightTan)
- local x_offset = (tanHalfFov.LeftTan - tanHalfFov.RightTan) * x_scale * 0.5
- local y_scale = 2 / (tanHalfFov.UpTan + tanHalfFov.DownTan )
- local y_offset = (tanHalfFov.UpTan - tanHalfFov.DownTan ) * y_scale * 0.5
- local result = {
- Scale = vec2(x_scale, y_scale),
- Offset = vec2(x_offset, y_offset)
- }
- -- Hey - why is that Y.Offset negated?
- -- It's because a projection matrix transforms from world coords with Y=up,
- -- whereas this is from NDC which is Y=down.
- return result
- end
- if not flipZ and farAtInfinity then
- print("Error: Cannot push Far Clip to Infinity when Z-order is not flipped")
- farAtInfinity = false
- end
- -- A projection matrix is very like a scaling from NDC, so we can start with that.
- local scaleAndOffset = CreateNDCScaleAndOffsetFromFov(tanHalfFov)
- local handednessScale = rightHanded and -1.0 or 1.0
- local projection = new()
- -- Produces X result, mapping clip edges to [-w,+w]
- projection[1] = scaleAndOffset.Scale.x
- projection[2] = 0
- projection[3] = handednessScale * scaleAndOffset.Offset.x
- projection[4] = 0
- -- Produces Y result, mapping clip edges to [-w,+w]
- -- Hey - why is that YOffset negated?
- -- It's because a projection matrix transforms from world coords with Y=up,
- -- whereas this is derived from an NDC scaling, which is Y=down.
- projection[5] = 0
- projection[6] = scaleAndOffset.Scale.y
- projection[7] = handednessScale * -scaleAndOffset.Offset.y
- projection[8] = 0
- -- Produces Z-buffer result - app needs to fill this in with whatever Z range it wants.
- -- We'll just use some defaults for now.
- projection[9] = 0
- projection[10] = 0
- if farAtInfinity then
- if isOpenGL then
- -- It's not clear this makes sense for OpenGL - you don't get the same precision benefits you do in D3D.
- projection[11] = -handednessScale
- projection[12] = 2.0 * zNear
- else
- projection[11] = 0
- projection[12] = zNear
- end
- else
- if isOpenGL then
- -- Clip range is [-w,+w], so 0 is at the middle of the range.
- projection[11] = -handednessScale * (flipZ and -1.0 or 1.0) * (zNear + zFar) / (zNear - zFar)
- projection[12] = 2.0 * ((flipZ and -zFar or zFar) * zNear) / (zNear - zFar)
- else
- -- Clip range is [0,+w], so 0 is at the start of the range.
- projection[11] = -handednessScale * (flipZ and -zNear or zFar) / (zNear - zFar)
- projection[12] = ((flipZ and -zFar or zFar) * zNear) / (zNear - zFar)
- end
- end
- -- Produces W result (= Z in)
- projection[13] = 0
- projection[14] = 0
- projection[15] = handednessScale
- projection[16] = 0
- return projection:transpose(projection)
- end
- --- Transform matrix to look at a point.
- -- @tparam vec3 eye Location of viewer's view plane
- -- @tparam vec3 center Location of object to view
- -- @tparam vec3 up Up direction
- -- @treturn mat4 out
- function mat4.look_at(eye, center, up)
- local out = new()
- local forward = (center - eye):normalize()
- local side = forward:cross(up):normalize()
- local new_up = side:cross(forward)
- out[1] = side.x
- out[5] = side.y
- out[9] = side.z
- out[2] = new_up.x
- out[6] = new_up.y
- out[10] = new_up.z
- out[3] = -forward.x
- out[7] = -forward.y
- out[11] = -forward.z
- out[13] = -side:dot(eye)
- out[14] = -new_up:dot(eye)
- out[15] = forward:dot(eye)
- out[16] = 1
- return out
- end
- --- Clone a matrix.
- -- @tparam mat4 a Matrix to clone
- -- @treturn mat4 out
- function mat4.clone(a)
- return new(a)
- end
- --- Multiply two matrices.
- -- @tparam mat4 a Left hand operant
- -- @tparam mat4 b Right hand operant
- -- @treturn mat4 out
- function mat4.mul(a, b)
- local out = new()
- 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]
- out[12] = a[9] * b[4] + a[10] * b[8] + a[11] * b[12] + a[12] * b[16]
- out[13] = a[13] * b[1] + a[14] * b[5] + a[15] * b[9] + a[16] * b[13]
- out[14] = a[13] * b[2] + a[14] * b[6] + a[15] * b[10] + a[16] * b[14]
- out[15] = a[13] * b[3] + a[14] * b[7] + a[15] * b[11] + a[16] * b[15]
- out[16] = a[13] * b[4] + a[14] * b[8] + a[15] * b[12] + a[16] * b[16]
- return out
- end
- --- Multiply a matrix and a vec4.
- -- @tparam mat4 a Left hand operant
- -- @tparam table b Right hand operant
- -- @treturn table out
- function mat4.mul_vec4(a, b)
- return {
- b[1] * a[1] + b[2] * a[5] + b [3] * a[9] + b[4] * a[13],
- b[1] * a[2] + b[2] * a[6] + b [3] * a[10] + b[4] * a[14],
- b[1] * a[3] + b[2] * a[7] + b [3] * a[11] + b[4] * a[15],
- b[1] * a[4] + b[2] * a[8] + b [3] * a[12] + b[4] * a[16]
- }
- end
- --- Invert a matrix.
- -- @tparam mat4 a Matrix to invert
- -- @treturn mat4 out
- function mat4.invert(a)
- local out = new()
- out[1] = a[6] * a[11] * a[16] - a[6] * a[12] * a[15] - a[10] * a[7] * a[16] + a[10] * a[8] * a[15] + a[14] * a[7] * a[12] - a[14] * a[8] * a[11]
- out[2] = -a[2] * a[11] * a[16] + a[2] * a[12] * a[15] + a[10] * a[3] * a[16] - a[10] * a[4] * a[15] - a[14] * a[3] * a[12] + a[14] * a[4] * a[11]
- out[3] = a[2] * a[7] * a[16] - a[2] * a[8] * a[15] - a[6] * a[3] * a[16] + a[6] * a[4] * a[15] + a[14] * a[3] * a[8] - a[14] * a[4] * a[7]
- out[4] = -a[2] * a[7] * a[12] + a[2] * a[8] * a[11] + a[6] * a[3] * a[12] - a[6] * a[4] * a[11] - a[10] * a[3] * a[8] + a[10] * a[4] * a[7]
- out[5] = -a[5] * a[11] * a[16] + a[5] * a[12] * a[15] + a[9] * a[7] * a[16] - a[9] * a[8] * a[15] - a[13] * a[7] * a[12] + a[13] * a[8] * a[11]
- out[6] = a[1] * a[11] * a[16] - a[1] * a[12] * a[15] - a[9] * a[3] * a[16] + a[9] * a[4] * a[15] + a[13] * a[3] * a[12] - a[13] * a[4] * a[11]
- out[7] = -a[1] * a[7] * a[16] + a[1] * a[8] * a[15] + a[5] * a[3] * a[16] - a[5] * a[4] * a[15] - a[13] * a[3] * a[8] + a[13] * a[4] * a[7]
- out[8] = a[1] * a[7] * a[12] - a[1] * a[8] * a[11] - a[5] * a[3] * a[12] + a[5] * a[4] * a[11] + a[9] * a[3] * a[8] - a[9] * a[4] * a[7]
- out[9] = a[5] * a[10] * a[16] - a[5] * a[12] * a[14] - a[9] * a[6] * a[16] + a[9] * a[8] * a[14] + a[13] * a[6] * a[12] - a[13] * a[8] * a[10]
- out[10] = -a[1] * a[10] * a[16] + a[1] * a[12] * a[14] + a[9] * a[2] * a[16] - a[9] * a[4] * a[14] - a[13] * a[2] * a[12] + a[13] * a[4] * a[10]
- out[11] = a[1] * a[6] * a[16] - a[1] * a[8] * a[14] - a[5] * a[2] * a[16] + a[5] * a[4] * a[14] + a[13] * a[2] * a[8] - a[13] * a[4] * a[6]
- out[12] = -a[1] * a[6] * a[12] + a[1] * a[8] * a[10] + a[5] * a[2] * a[12] - a[5] * a[4] * a[10] - a[9] * a[2] * a[8] + a[9] * a[4] * a[6]
- out[13] = -a[5] * a[10] * a[15] + a[5] * a[11] * a[14] + a[9] * a[6] * a[15] - a[9] * a[7] * a[14] - a[13] * a[6] * a[11] + a[13] * a[7] * a[10]
- out[14] = a[1] * a[10] * a[15] - a[1] * a[11] * a[14] - a[9] * a[2] * a[15] + a[9] * a[3] * a[14] + a[13] * a[2] * a[11] - a[13] * a[3] * a[10]
- out[15] = -a[1] * a[6] * a[15] + a[1] * a[7] * a[14] + a[5] * a[2] * a[15] - a[5] * a[3] * a[14] - a[13] * a[2] * a[7] + a[13] * a[3] * a[6]
- out[16] = a[1] * a[6] * a[11] - a[1] * a[7] * a[10] - a[5] * a[2] * a[11] + a[5] * a[3] * a[10] + a[9] * a[2] * a[7] - a[9] * a[3] * a[6]
- local det = a[1] * out[1] + a[2] * out[5] + a[3] * out[9] + a[4] * out[13]
- if det == 0 then return a end
- det = 1 / det
- for i = 1, 16 do
- out[i] = out[i] * det
- end
- return out
- end
- --- Scale a matrix.
- -- @tparam mat4 a Matrix to scale
- -- @tparam vec3 s Scalar
- -- @treturn mat4 out
- function mat4.scale(a, s)
- identity(tmp)
- tmp[1] = s.x
- tmp[6] = s.y
- tmp[11] = s.z
- return tmp:mul(a)
- end
- --- Rotate a matrix.
- -- @tparam mat4 a Matrix to rotate
- -- @tparam number angle Angle to rotate by (in radians)
- -- @tparam vec3 axis Axis to rotate on
- -- @treturn mat4 out
- function mat4.rotate(a, angle, axis)
- if type(angle) == "table" or type(angle) == "cdata" then
- angle, axis = angle:to_angle_axis()
- end
- local l = axis:len()
- if l == 0 then
- return a
- end
- local x, y, z = axis.x / l, axis.y / l, axis.z / l
- local c = cos(angle)
- local s = sin(angle)
- identity(tmp)
- tmp[1] = x * x * (1 - c) + c
- tmp[2] = y * x * (1 - c) + z * s
- tmp[3] = x * z * (1 - c) - y * s
- tmp[5] = x * y * (1 - c) - z * s
- tmp[6] = y * y * (1 - c) + c
- tmp[7] = y * z * (1 - c) + x * s
- tmp[9] = x * z * (1 - c) + y * s
- tmp[10] = y * z * (1 - c) - x * s
- tmp[11] = z * z * (1 - c) + c
- return tmp:mul(a)
- end
- --- Translate a matrix.
- -- @tparam mat4 a Matrix to translate
- -- @tparam vec3 t Translation vector
- -- @treturn mat4 out
- function mat4.translate(a, t)
- identity(tmp)
- tmp[13] = t.x
- tmp[14] = t.y
- tmp[15] = t.z
- return tmp:mul(a)
- end
- --- Shear a matrix.
- -- @tparam mat4 a Matrix to translate
- -- @tparam number yx
- -- @tparam number zx
- -- @tparam number xy
- -- @tparam number zy
- -- @tparam number xz
- -- @tparam number yz
- -- @treturn mat4 out
- function mat4.shear(a, yx, zx, xy, zy, xz, yz)
- identity(tmp)
- tmp[2] = yx or 0
- tmp[3] = zx or 0
- tmp[5] = xy or 0
- tmp[7] = zy or 0
- tmp[9] = xz or 0
- tmp[10] = yz or 0
- return tmp:mul(a)
- end
- --- Transpose a matrix.
- -- @tparam mat4 a Matrix to transpose
- -- @treturn mat4 out
- function mat4.transpose(a)
- local out = new()
- out[1] = a[1]
- out[2] = a[5]
- out[3] = a[9]
- out[4] = a[13]
- out[5] = a[2]
- out[6] = a[6]
- out[7] = a[10]
- out[8] = a[14]
- out[9] = a[3]
- out[10] = a[7]
- out[11] = a[11]
- out[12] = a[15]
- out[13] = a[4]
- out[14] = a[8]
- out[15] = a[12]
- out[16] = a[16]
- return out
- end
- -- https://github.com/g-truc/glm/blob/master/glm/gtc/matrix_transform.inl#L518
- --- Project a matrix from world space to screen space.
- -- @tparam vec3 obj Object position in world space
- -- @tparam mat4 view View matrix
- -- @tparam mat4 projection Projection matrix
- -- @tparam table viewport XYWH of viewport
- -- @treturn vec3 win
- function mat4.project(obj, view, projection, viewport)
- local position = { obj.x, obj.y, obj.z, 1 }
- position = projection * (view * position)
- 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[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#L544
- --- Unproject a matrix from screen space to world space.
- -- @tparam vec3 win Object position in screen space
- -- @tparam mat4 view View matrix
- -- @tparam mat4 projection Projection matrix
- -- @tparam table viewport XYWH of viewport
- -- @treturn vec3 obj
- function mat4.unproject(win, view, projection, viewport)
- 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 = (projection * view):invert() * position
- position = (view * projection):invert() * 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
- --- Return a boolean showing if a table is or is not a mat4.
- -- @tparam mat4 a Matrix to be tested
- -- @treturn boolean is_mat4
- function mat4.is_mat4(a)
- if type(a) == "cdata" then
- return ffi.istype("cpml_mat4", a)
- end
- if type(a) ~= "table" then
- return false
- end
- for i = 1, 16 do
- if type(a[i]) ~= "number" then
- return false
- end
- end
- return true
- end
- --- Return a formatted string.
- -- @tparam mat4 a Matrix to be turned into a string
- -- @treturn string formatted
- function mat4.to_string(a)
- local str = "[ "
- for i = 1, 16 do
- str = str .. string.format("%+0.3f", a[i])
- if i < 16 then
- str = str .. ", "
- end
- end
- str = str .. " ]"
- return str
- end
- --- Convert a matrix to vec4s.
- -- @tparam mat4 a Matrix to be converted
- -- @treturn table vec4s
- 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
- -- http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/
- --- Convert a matrix to a quaternion.
- -- @tparam mat4 a Matrix to be converted
- -- @treturn quat out
- function mat4.to_quat(a)
- tmp = a:transpose()
- local w = sqrt(1 + tmp[1] + tmp[6] + tmp[11]) / 2
- local scale = w * 4
- local q = quat.new(
- tmp[10] - tmp[7] / scale,
- tmp[3] - tmp[9] / scale,
- tmp[5] - tmp[2] / scale,
- w
- )
- return q:normalize()
- end
- -- http://www.crownandcutlass.com/features/technicaldetails/frustum.html
- --- Convert a matrix to a frustum.
- -- @tparam mat4 a Matrix to be converted (projection * view)
- -- @tparam boolean infinite Infinite removes the far plane
- -- @treturn frustum out
- function mat4.to_frustum(a, infinite)
- local t
- local frustum = {}
- -- Extract the LEFT plane
- frustum.left = {}
- frustum.left.a = a[4] + a[1]
- frustum.left.b = a[8] + a[5]
- frustum.left.c = a[12] + a[9]
- frustum.left.d = a[16] + a[13]
- -- Normalize the result
- t = sqrt(frustum.left.a * frustum.left.a + frustum.left.b * frustum.left.b + frustum.left.c * frustum.left.c)
- frustum.left.a = frustum.left.a / t
- frustum.left.b = frustum.left.b / t
- frustum.left.c = frustum.left.c / t
- frustum.left.d = frustum.left.d / t
- -- Extract the RIGHT plane
- frustum.right = {}
- frustum.right.a = a[4] - a[1]
- frustum.right.b = a[8] - a[5]
- frustum.right.c = a[12] - a[9]
- frustum.right.d = a[16] - a[13]
- -- Normalize the result
- t = sqrt(frustum.right.a * frustum.right.a + frustum.right.b * frustum.right.b + frustum.right.c * frustum.right.c)
- frustum.right.a = frustum.right.a / t
- frustum.right.b = frustum.right.b / t
- frustum.right.c = frustum.right.c / t
- frustum.right.d = frustum.right.d / t
- -- Extract the BOTTOM plane
- frustum.bottom = {}
- frustum.bottom.a = a[4] + a[2]
- frustum.bottom.b = a[8] + a[6]
- frustum.bottom.c = a[12] + a[10]
- frustum.bottom.d = a[16] + a[14]
- -- Normalize the result
- t = sqrt(frustum.bottom.a * frustum.bottom.a + frustum.bottom.b * frustum.bottom.b + frustum.bottom.c * frustum.bottom.c)
- frustum.bottom.a = frustum.bottom.a / t
- frustum.bottom.b = frustum.bottom.b / t
- frustum.bottom.c = frustum.bottom.c / t
- frustum.bottom.d = frustum.bottom.d / t
- -- Extract the TOP plane
- frustum.top = {}
- frustum.top.a = a[4] - a[2]
- frustum.top.b = a[8] - a[6]
- frustum.top.c = a[12] - a[10]
- frustum.top.d = a[16] - a[14]
- -- Normalize the result
- t = sqrt(frustum.top.a * frustum.top.a + frustum.top.b * frustum.top.b + frustum.top.c * frustum.top.c)
- frustum.top.a = frustum.top.a / t
- frustum.top.b = frustum.top.b / t
- frustum.top.c = frustum.top.c / t
- frustum.top.d = frustum.top.d / t
- -- Extract the NEAR plane
- frustum.near = {}
- frustum.near.a = a[4] + a[3]
- frustum.near.b = a[8] + a[7]
- frustum.near.c = a[12] + a[11]
- frustum.near.d = a[16] + a[15]
- -- Normalize the result
- t = sqrt(frustum.near.a * frustum.near.a + frustum.near.b * frustum.near.b + frustum.near.c * frustum.near.c)
- frustum.near.a = frustum.near.a / t
- frustum.near.b = frustum.near.b / t
- frustum.near.c = frustum.near.c / t
- frustum.near.d = frustum.near.d / t
- if not infinite then
- -- Extract the FAR plane
- frustum.far = {}
- frustum.far.a = a[4] - a[3]
- frustum.far.b = a[8] - a[7]
- frustum.far.c = a[12] - a[11]
- frustum.far.d = a[16] - a[15]
- -- Normalize the result
- t = sqrt(frustum.far.a * frustum.far.a + frustum.far.b * frustum.far.b + frustum.far.c * frustum.far.c)
- frustum.far.a = frustum.far.a / t
- frustum.far.b = frustum.far.b / t
- frustum.far.c = frustum.far.c / t
- frustum.far.d = frustum.far.d / t
- end
- return frustum
- end
- function mat4_mt.__index(t, k)
- if type(t) == "cdata" then
- if type(k) == "number" then
- return t._m[k-1]
- end
- end
- return rawget(mat4, k)
- end
- function mat4_mt.__newindex(t, k, v)
- if type(t) == "cdata" then
- if type(k) == "number" then
- t._m[k-1] = v
- end
- end
- end
- mat4_mt.__tostring = mat4.to_string
- function mat4_mt.__call(_, a)
- return mat4.new(a)
- end
- function mat4_mt.__unm(a)
- return a:invert()
- end
- function mat4_mt.__eq(a, b)
- if not mat4.is_mat4(a) or not mat4.is_mat4(b) then
- return false
- end
- for i = 1, 16 do
- if not utils.tolerance(b[i]-a[i], DBL_EPSILON) then
- return false
- end
- end
- return true
- end
- function mat4_mt.__mul(a, b)
- assert(mat4.is_mat4(a), "__mul: Wrong argument type for left hand operant. (<cpml.mat4> expected)")
- if vec3.is_vec3(b) then
- return vec3(a:mul_vec4({ b.x, b.y, b.z, 1 }))
- end
- assert(mat4.is_mat4(b) or #b == 4, "__mul: Wrong argument type for right hand operant. (<cpml.mat4> or table #4 expected)")
- if mat4.is_mat4(b) then
- return a:mul(b)
- end
- return a:mul_vec4(b)
- end
- if status then
- ffi.metatype(new, mat4_mt)
- end
- return setmetatable({}, mat4_mt)
|