-- double 4x4, 1-based, column major -- local matrix = {} local current_folder = (...):gsub('%.[^%.]+$', '') .. "." local constants = require(current_folder .. "constants") local vec3 = require(current_folder .. "vec3") local mat4 = {} mat4.__index = mat4 setmetatable(mat4, mat4) -- from https://github.com/davidm/lua-matrix/blob/master/lua/matrix.lua -- Multiply two matrices; m1 columns must be equal to m2 rows -- e.g. #m1[1] == #m2 local function matrix_mult_nxn(m1, m2) local mtx = {} for i = 1, #m1 do mtx[i] = {} for j = 1, #m2[1] do local num = m1[i][1] * m2[1][j] for n = 2, #m1[1] do num = num + m1[i][n] * m2[n][j] end mtx[i][j] = num end end return mtx end function mat4:__call(v) local m = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 } if type(v) == "table" and #v == 16 then for i=1,16 do m[i] = v[i] end elseif type(v) == "table" and #v == 9 then m[1], m[2], m[3] = v[1], v[2], v[3] m[5], m[6], m[7] = v[4], v[5], v[6] m[9], m[10], m[11] = v[7], v[8], v[9] m[16] = 1 elseif type(v) == "table" and type(v[1]) == "table" then local idx = 1 for i=1, 4 do for j=1, 4 do m[idx] = v[i][j] idx = idx + 1 end end end -- Look in mat4 for metamethods setmetatable(m, mat4) return m end function mat4:__eq(b) local abs = math.abs for i=1, 16 do if abs(self[i] - b[i]) > constants.FLT_EPSILON then return false end end return true end function mat4:__tostring() local str = "[ " for i, v in ipairs(self) do str = str .. string.format("%2.5f", v) if i < #self then str = str .. ", " end end str = str .. " ]" return str end function mat4:ortho(left, right, top, bottom, near, far) local out = mat4() 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:perspective(fovy, aspect, near, far) assert(aspect ~= 0) assert(near ~= far) local t = math.tan(fovy / 2) local result = mat4( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ) result[1] = 1 / (aspect * t) result[6] = 1 / t result[11] = - (far + near) / (far - near) result[12] = - 1 result[15] = - (2 * far * near) / (far - near) return result end function mat4:translate(t) local m = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, t.x, t.y, t.z, 1 } return mat4(m) * mat4(self) end function mat4:scale(s) local m = { s.x, 0, 0, 0, 0, s.y, 0, 0, 0, 0, s.z, 0, 0, 0, 0, 1 } return mat4(m) * mat4(self) end local function len(v) return math.sqrt(v[1] * v[1] + v[2] * v[2] + v[3] * v[3]) end function mat4:rotate(angle, axis) if type(angle) == "table" then angle, axis = angle:to_axis_angle() end local l = len(axis) if l == 0 then return self end local x, y, z = axis[1] / l, axis[2] / l, axis[3] / l local c = math.cos(angle) local s = math.sin(angle) local m = { 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, } return mat4(m) * mat4(self) end -- Set mat4 to identity mat4. Tested OK function mat4:identity() local out = mat4() for i=1, 16, 5 do out[i] = 1 end return out end function mat4:clone() return mat4(self) end -- Inverse of matrix. Tested OK function mat4:invert() local out = mat4() out[1] = self[6] * self[11] * self[16] - self[6] * self[12] * self[15] - self[10] * self[7] * self[16] + self[10] * self[8] * self[15] + self[14] * self[7] * self[12] - self[14] * self[8] * self[11] out[5] = -self[5] * self[11] * self[16] + self[5] * self[12] * self[15] + self[9] * self[7] * self[16] - self[9] * self[8] * self[15] - self[13] * self[7] * self[12] + self[13] * self[8] * self[11] out[9] = self[5] * self[10] * self[16] - self[5] * self[12] * self[14] - self[9] * self[6] * self[16] + self[9] * self[8] * self[14] + self[13] * self[6] * self[12] - self[13] * self[8] * self[10] out[13] = -self[5] * self[10] * self[15] + self[5] * self[11] * self[14] + self[9] * self[6] * self[15] - self[9] * self[7] * self[14] - self[13] * self[6] * self[11] + self[13] * self[7] * self[10] out[2] = -self[2] * self[11] * self[16] + self[2] * self[12] * self[15] + self[10] * self[3] * self[16] - self[10] * self[4] * self[15] - self[14] * self[3] * self[12] + self[14] * self[4] * self[11] out[6] = self[1] * self[11] * self[16] - self[1] * self[12] * self[15] - self[9] * self[3] * self[16] + self[9] * self[4] * self[15] + self[13] * self[3] * self[12] - self[13] * self[4] * self[11] out[10] = -self[1] * self[10] * self[16] + self[1] * self[12] * self[14] + self[9] * self[2] * self[16] - self[9] * self[4] * self[14] - self[13] * self[2] * self[12] + self[13] * self[4] * self[10] out[14] = self[1] * self[10] * self[15] - self[1] * self[11] * self[14] - self[9] * self[2] * self[15] + self[9] * self[3] * self[14] + self[13] * self[2] * self[11] - self[13] * self[3] * self[10] out[3] = self[2] * self[7] * self[16] - self[2] * self[8] * self[15] - self[6] * self[3] * self[16] + self[6] * self[4] * self[15] + self[14] * self[3] * self[8] - self[14] * self[4] * self[7] out[7] = -self[1] * self[7] * self[16] + self[1] * self[8] * self[15] + self[5] * self[3] * self[16] - self[5] * self[4] * self[15] - self[13] * self[3] * self[8] + self[13] * self[4] * self[7] out[11] = self[1] * self[6] * self[16] - self[1] * self[8] * self[14] - self[5] * self[2] * self[16] + self[5] * self[4] * self[14] + self[13] * self[2] * self[8] - self[13] * self[4] * self[6] out[15] = -self[1] * self[6] * self[15] + self[1] * self[7] * self[14] + self[5] * self[2] * self[15] - self[5] * self[3] * self[14] - self[13] * self[2] * self[7] + self[13] * self[3] * self[6] out[4] = -self[2] * self[7] * self[12] + self[2] * self[8] * self[11] + self[6] * self[3] * self[12] - self[6] * self[4] * self[11] - self[10] * self[3] * self[8] + self[10] * self[4] * self[7] out[8] = self[1] * self[7] * self[12] - self[1] * self[8] * self[11] - self[5] * self[3] * self[12] + self[5] * self[4] * self[11] + self[9] * self[3] * self[8] - self[9] * self[4] * self[7] out[12] = -self[1] * self[6] * self[12] + self[1] * self[8] * self[10] + self[5] * self[2] * self[12] - self[5] * self[4] * self[10] - self[9] * self[2] * self[8] + self[9] * self[4] * self[6] out[16] = self[1] * self[6] * self[11] - self[1] * self[7] * self[10] - self[5] * self[2] * self[11] + self[5] * self[3] * self[10] + self[9] * self[2] * self[7] - self[9] * self[3] * self[6] local det = self[1] * out[1] + self[2] * out[5] + self[3] * out[9] + self[4] * out[13] if det == 0 then return self end det = 1.0 / det for i = 1, 16 do out[i] = out[i] * det end return out end -- 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 = view:transpose() * position position = projection:transpose() * 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[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 = (projection:transpose() * view:transpose()):invert() 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 position = inverse * position position[1] = position[1] / position[4] position[2] = position[2] / position[4] position[3] = position[3] / position[4] position[4] = position[4] / position[4] return vec3(position[1], position[2], position[3]) end function mat4:look_at(eye, center, up) local forward = (center - eye):normalize() local side = forward:cross(up):normalize() local new_up = side:cross(forward):normalize() local view = mat4() view[1] = side.x view[5] = side.y view[9] = side.z view[2] = new_up.x view[6] = new_up.y view[10]= new_up.z view[3] = -forward.x view[7] = -forward.y view[11]= -forward.z view[16]= 1 -- Fix 1u offset local new_eye = eye + forward local out = mat4():translate(-new_eye) * view return out * self end function mat4:transpose() local m = { self[1], self[5], self[9], self[13], self[2], self[6], self[10], self[14], self[3], self[7], self[11], self[15], self[4], self[8], self[12], self[16] } return mat4(m) end function mat4:__unm() return self:invert() end -- Multiply mat4 by a mat4. Tested OK function mat4:__mul(m) if #m == 4 then local tmp = matrix_mult_nxn(self:to_vec4s(), { {m[1]}, {m[2]}, {m[3]}, {m[4]} }) local v = {} for i=1, 4 do v[i] = tmp[i][1] end return v end local out = mat4() for i=0, 12, 4 do for j=1, 4 do out[i+j] = m[j] * self[i+1] + m[j+4] * self[i+2] + m[j+8] * self[i+3] + m[j+12] * self[i+4] end end return out end function mat4:to_vec4s() return { { self[1], self[2], self[3], self[4] }, { self[5], self[6], self[7], self[8] }, { self[9], self[10], self[11], self[12] }, { self[13], self[14], self[15], self[16] } } end return mat4