This repository was archived by the owner on Aug 14, 2026. It is now read-only.
forked from TeJota1337/DramaticShapeVoxelMod
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMat4.lua
More file actions
153 lines (138 loc) · 5.19 KB
/
Copy pathMat4.lua
File metadata and controls
153 lines (138 loc) · 5.19 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
-- Minimal 4x4 matrix math for voxel world mode.
--
-- Row-major, and sent to the shader with shader:send("mvp", "row", m).
-- LOVE 11.5's matrix uniform defaults to column-major, so the "row" layout
-- argument is what lets these tables read the same way they are written
-- here -- translation in the fourth column, m[4]/m[8]/m[12].
--
-- Only what the renderer actually needs: a perspective projection (the
-- camera), an orthographic one (the sun's shadow pass), an asymmetric one
-- (a headset's per-eye frustum), a look-based view, a quaternion rotation
-- (a headset's pose), and the translate/rotateY/scale a model matrix is
-- built from. No general inverse -- the VR view inverts its rigid pieces
-- one at a time.
local Mat4 = {}
function Mat4.identity()
return { 1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1 }
end
-- a * b, both row-major
function Mat4.mul(a, b)
local o = {}
for r = 0, 3 do
local a0, a1 = a[r * 4 + 1], a[r * 4 + 2]
local a2, a3 = a[r * 4 + 3], a[r * 4 + 4]
for c = 1, 4 do
o[r * 4 + c] = a0 * b[c] + a1 * b[4 + c] + a2 * b[8 + c] + a3 * b[12 + c]
end
end
return o
end
function Mat4.translate(x, y, z)
return { 1, 0, 0, x,
0, 1, 0, y,
0, 0, 1, z,
0, 0, 0, 1 }
end
function Mat4.scale(x, y, z)
return { x, 0, 0, 0,
0, y, 0, 0,
0, 0, z, 0,
0, 0, 0, 1 }
end
function Mat4.rotateY(a)
local c, s = math.cos(a), math.sin(a)
return { c, 0, s, 0,
0, 1, 0, 0,
-s, 0, c, 0,
0, 0, 0, 1 }
end
function Mat4.rotateX(a)
local c, s = math.cos(a), math.sin(a)
return { 1, 0, 0, 0,
0, c, -s, 0,
0, s, c, 0,
0, 0, 0, 1 }
end
-- The rotation a unit quaternion describes, row-major. The VR rig is what
-- needs it: an OpenXR eye pose arrives as position + orientation
-- quaternion, and both the eye's transform and its inverse (the view) are
-- built from this.
function Mat4.fromQuat(x, y, z, w)
local xx, yy, zz = x * x, y * y, z * z
local xy, xz, yz = x * y, x * z, y * z
local wx, wy, wz = w * x, w * y, w * z
return { 1 - 2 * (yy + zz), 2 * (xy - wz), 2 * (xz + wy), 0,
2 * (xy + wz), 1 - 2 * (xx + zz), 2 * (yz - wx), 0,
2 * (xz - wy), 2 * (yz + wx), 1 - 2 * (xx + yy), 0,
0, 0, 0, 1 }
end
-- Transpose. For a pure rotation this IS the inverse, which is how the VR
-- view matrix is assembled without a general 4x4 inverse.
function Mat4.transpose(m)
return { m[1], m[5], m[9], m[13],
m[2], m[6], m[10], m[14],
m[3], m[7], m[11], m[15],
m[4], m[8], m[12], m[16] }
end
-- Right-handed perspective from an OpenXR-style asymmetric field of view:
-- four signed HALF-ANGLES off the view axis (left and down negative), onto
-- GL clip space (z in [-1, 1]). A headset's per-eye frustum is off-centre
-- -- the nose side is narrower than the temple side -- so the symmetric
-- perspective() above cannot express it.
function Mat4.fovProjection(angleLeft, angleRight, angleUp, angleDown,
near, far)
local l, r = math.tan(angleLeft), math.tan(angleRight)
local u, d = math.tan(angleUp), math.tan(angleDown)
local w, h, dz = r - l, u - d, near - far
return { 2 / w, 0, (r + l) / w, 0,
0, 2 / h, (u + d) / h, 0,
0, 0, (far + near) / dz, (2 * far * near) / dz,
0, 0, -1, 0 }
end
-- Right-handed perspective onto GL clip space (z in [-1, 1]).
function Mat4.perspective(fovY, aspect, near, far)
local f = 1 / math.tan(fovY / 2)
local d = near - far
return { f / aspect, 0, 0, 0,
0, f, 0, 0,
0, 0, (far + near) / d, (2 * far * near) / d,
0, 0, -1, 0 }
end
-- Right-handed orthographic projection onto GL clip space (z in [-1, 1]).
-- The view-space box is x in [l, r], y in [b, t], z in [-f, -n] -- near and
-- far are DISTANCES down the view's -z, exactly as in perspective() above.
-- Parallel, so a sun is a direction and nothing else: no eye point, no
-- foreshortening, and clip z stays linear in world units, which is what
-- lets the shadow pass store depth as a plain number.
function Mat4.ortho(l, r, b, t, n, f)
return { 2 / (r - l), 0, 0, -(r + l) / (r - l),
0, 2 / (t - b), 0, -(t + b) / (t - b),
0, 0, -2 / (f - n), -(f + n) / (f - n),
0, 0, 0, 1 }
end
-- Right-handed look-at. eye/target/up are {x, y, z}.
function Mat4.lookAt(eye, target, up)
local function sub(a, b) return { a[1] - b[1], a[2] - b[2], a[3] - b[3] } end
local function norm(v)
local l = math.sqrt(v[1] * v[1] + v[2] * v[2] + v[3] * v[3])
if l == 0 then return { 0, 0, 0 } end
return { v[1] / l, v[2] / l, v[3] / l }
end
local function cross(a, b)
return { a[2] * b[3] - a[3] * b[2],
a[3] * b[1] - a[1] * b[3],
a[1] * b[2] - a[2] * b[1] }
end
local function dot(a, b) return a[1] * b[1] + a[2] * b[2] + a[3] * b[3] end
local f = norm(sub(target, eye)) -- forward
local s = norm(cross(f, up)) -- right
local u = cross(s, f) -- true up
return { s[1], s[2], s[3], -dot(s, eye),
u[1], u[2], u[3], -dot(u, eye),
-f[1], -f[2], -f[3], dot(f, eye),
0, 0, 0, 1 }
end
return Mat4