130 lines
5.6 KiB
Lua
130 lines
5.6 KiB
Lua
|
|
-- written by groverbuger for g3d
|
||
|
|
-- september 2021
|
||
|
|
-- MIT license
|
||
|
|
|
||
|
|
local vectors = require(g3d.path .. "/vectors")
|
||
|
|
local vectorCrossProduct = vectors.crossProduct
|
||
|
|
local vectorDotProduct = vectors.dotProduct
|
||
|
|
local vectorNormalize = vectors.normalize
|
||
|
|
|
||
|
|
----------------------------------------------------------------------------------------------------
|
||
|
|
-- matrix class
|
||
|
|
----------------------------------------------------------------------------------------------------
|
||
|
|
-- matrices are 16 numbers in table, representing a 4x4 matrix like so:
|
||
|
|
--
|
||
|
|
-- | 1 2 3 4 |
|
||
|
|
-- | |
|
||
|
|
-- | 5 6 7 8 |
|
||
|
|
-- | |
|
||
|
|
-- | 9 10 11 12 |
|
||
|
|
-- | |
|
||
|
|
-- | 13 14 15 16 |
|
||
|
|
|
||
|
|
local matrix = {}
|
||
|
|
matrix.__index = matrix
|
||
|
|
|
||
|
|
local function newMatrix()
|
||
|
|
local self = setmetatable({}, matrix)
|
||
|
|
|
||
|
|
-- initialize a matrix as the identity matrix
|
||
|
|
self[1], self[2], self[3], self[4] = 1, 0, 0, 0
|
||
|
|
self[5], self[6], self[7], self[8] = 0, 1, 0, 0
|
||
|
|
self[9], self[10], self[11], self[12] = 0, 0, 1, 0
|
||
|
|
self[13], self[14], self[15], self[16] = 0, 0, 0, 1
|
||
|
|
|
||
|
|
return self
|
||
|
|
end
|
||
|
|
|
||
|
|
-- automatically converts a matrix to a string
|
||
|
|
-- for printing to console and debugging
|
||
|
|
function matrix:__tostring()
|
||
|
|
return ("%f\t%f\t%f\t%f\n%f\t%f\t%f\t%f\n%f\t%f\t%f\t%f\n%f\t%f\t%f\t%f"):format(unpack(self))
|
||
|
|
end
|
||
|
|
|
||
|
|
----------------------------------------------------------------------------------------------------
|
||
|
|
-- transformation, projection, and rotation matrices
|
||
|
|
----------------------------------------------------------------------------------------------------
|
||
|
|
-- the three most important matrices for 3d graphics
|
||
|
|
-- these three matrices are all you need to write a simple 3d shader
|
||
|
|
|
||
|
|
-- returns a transformation matrix
|
||
|
|
-- translation, rotation, and scale are all 3d vectors
|
||
|
|
function matrix:setTransformationMatrix(translation, rotation, scale)
|
||
|
|
-- translations
|
||
|
|
self[4] = translation[1]
|
||
|
|
self[8] = translation[2]
|
||
|
|
self[12] = translation[3]
|
||
|
|
|
||
|
|
-- rotations
|
||
|
|
if #rotation == 3 then
|
||
|
|
-- use 3D rotation vector as euler angles
|
||
|
|
-- source: https://en.wikipedia.org/wiki/Rotation_matrix
|
||
|
|
local ca, cb, cc = math.cos(rotation[3]), math.cos(rotation[2]), math.cos(rotation[1])
|
||
|
|
local sa, sb, sc = math.sin(rotation[3]), math.sin(rotation[2]), math.sin(rotation[1])
|
||
|
|
self[1], self[2], self[3] = ca*cb, ca*sb*sc - sa*cc, ca*sb*cc + sa*sc
|
||
|
|
self[5], self[6], self[7] = sa*cb, sa*sb*sc + ca*cc, sa*sb*cc - ca*sc
|
||
|
|
self[9], self[10], self[11] = -sb, cb*sc, cb*cc
|
||
|
|
else
|
||
|
|
-- use 4D rotation vector as a quaternion
|
||
|
|
local qx, qy, qz, qw = rotation[1], rotation[2], rotation[3], rotation[4]
|
||
|
|
self[1], self[2], self[3] = 1 - 2*qy^2 - 2*qz^2, 2*qx*qy - 2*qz*qw, 2*qx*qz + 2*qy*qw
|
||
|
|
self[5], self[6], self[7] = 2*qx*qy + 2*qz*qw, 1 - 2*qx^2 - 2*qz^2, 2*qy*qz - 2*qx*qw
|
||
|
|
self[9], self[10], self[11] = 2*qx*qz - 2*qy*qw, 2*qy*qz + 2*qx*qw, 1 - 2*qx^2 - 2*qy^2
|
||
|
|
end
|
||
|
|
|
||
|
|
-- scale
|
||
|
|
local sx, sy, sz = scale[1], scale[2], scale[3]
|
||
|
|
self[1], self[2], self[3] = self[1] * sx, self[2] * sy, self[3] * sz
|
||
|
|
self[5], self[6], self[7] = self[5] * sx, self[6] * sy, self[7] * sz
|
||
|
|
self[9], self[10], self[11] = self[9] * sx, self[10] * sy, self[11] * sz
|
||
|
|
|
||
|
|
-- fourth row is not used, just set it to the fourth row of the identity matrix
|
||
|
|
self[13], self[14], self[15], self[16] = 0, 0, 0, 1
|
||
|
|
end
|
||
|
|
|
||
|
|
-- returns a perspective projection matrix
|
||
|
|
-- (things farther away appear smaller)
|
||
|
|
-- all arguments are scalars aka normal numbers
|
||
|
|
-- aspectRatio is defined as window width divided by window height
|
||
|
|
function matrix:setProjectionMatrix(fov, near, far, aspectRatio)
|
||
|
|
local top = near * math.tan(fov/2)
|
||
|
|
local bottom = -1*top
|
||
|
|
local right = top * aspectRatio
|
||
|
|
local left = -1*right
|
||
|
|
|
||
|
|
self[1], self[2], self[3], self[4] = 2*near/(right-left), 0, (right+left)/(right-left), 0
|
||
|
|
self[5], self[6], self[7], self[8] = 0, 2*near/(top-bottom), (top+bottom)/(top-bottom), 0
|
||
|
|
self[9], self[10], self[11], self[12] = 0, 0, -1*(far+near)/(far-near), -2*far*near/(far-near)
|
||
|
|
self[13], self[14], self[15], self[16] = 0, 0, -1, 0
|
||
|
|
end
|
||
|
|
|
||
|
|
-- returns an orthographic projection matrix
|
||
|
|
-- (things farther away are the same size as things closer)
|
||
|
|
-- all arguments are scalars aka normal numbers
|
||
|
|
-- aspectRatio is defined as window width divided by window height
|
||
|
|
function matrix:setOrthographicMatrix(fov, size, near, far, aspectRatio)
|
||
|
|
local top = size * math.tan(fov/2)
|
||
|
|
local bottom = -1*top
|
||
|
|
local right = top * aspectRatio
|
||
|
|
local left = -1*right
|
||
|
|
|
||
|
|
self[1], self[2], self[3], self[4] = 2/(right-left), 0, 0, -1*(right+left)/(right-left)
|
||
|
|
self[5], self[6], self[7], self[8] = 0, 2/(top-bottom), 0, -1*(top+bottom)/(top-bottom)
|
||
|
|
self[9], self[10], self[11], self[12] = 0, 0, -2/(far-near), -(far+near)/(far-near)
|
||
|
|
self[13], self[14], self[15], self[16] = 0, 0, 0, 1
|
||
|
|
end
|
||
|
|
|
||
|
|
-- returns a view matrix
|
||
|
|
-- eye, target, and up are all 3d vectors
|
||
|
|
function matrix:setViewMatrix(eye, target, up)
|
||
|
|
local z1, z2, z3 = vectorNormalize(eye[1] - target[1], eye[2] - target[2], eye[3] - target[3])
|
||
|
|
local x1, x2, x3 = vectorNormalize(vectorCrossProduct(up[1], up[2], up[3], z1, z2, z3))
|
||
|
|
local y1, y2, y3 = vectorCrossProduct(z1, z2, z3, x1, x2, x3)
|
||
|
|
|
||
|
|
self[1], self[2], self[3], self[4] = x1, x2, x3, -1*vectorDotProduct(x1, x2, x3, eye[1], eye[2], eye[3])
|
||
|
|
self[5], self[6], self[7], self[8] = y1, y2, y3, -1*vectorDotProduct(y1, y2, y3, eye[1], eye[2], eye[3])
|
||
|
|
self[9], self[10], self[11], self[12] = z1, z2, z3, -1*vectorDotProduct(z1, z2, z3, eye[1], eye[2], eye[3])
|
||
|
|
self[13], self[14], self[15], self[16] = 0, 0, 0, 1
|
||
|
|
end
|
||
|
|
|
||
|
|
return newMatrix
|