Files
aego/core/mathx/mat4.go

248 lines
5.4 KiB
Go

package mathx
type Mat4 [16]float32
func Mat4Identity() Mat4 {
return Mat4{1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}
}
func Mat4Ortho(left, right, bottom, top, near, far float32) Mat4 {
var m Mat4
m[0] = 2 / (right - left)
m[5] = 2 / (top - bottom)
m[10] = -2 / (far - near)
m[12] = -(right + left) / (right - left)
m[13] = -(top + bottom) / (top - bottom)
m[14] = -(far + near) / (far - near)
m[15] = 1
return m
}
func Mat4FromMat3(m Mat3) Mat4 {
return Mat4{
m[0], m[1], 0, 0,
m[3], m[4], 0, 0,
0, 0, 1, 0,
m[6], m[7], 0, 1,
}
}
func (m Mat4) Mul(o Mat4) Mat4 {
var r Mat4
for c := 0; c < 4; c++ {
for row := 0; row < 4; row++ {
var sum float32
for k := 0; k < 4; k++ {
sum += m[k*4+row] * o[c*4+k]
}
r[c*4+row] = sum
}
}
return r
}
func (m Mat4) Apply(p Vec2) Vec2 {
x := m[0]*p.X + m[4]*p.Y + m[12]
y := m[1]*p.X + m[5]*p.Y + m[13]
w := m[3]*p.X + m[7]*p.Y + m[15]
if w != 0 {
return Vec2{X: x / w, Y: y / w}
}
return Vec2{X: x, Y: y}
}
func Mat4Translate(p Vec3) Mat4 {
m := Mat4Identity()
m[12], m[13], m[14] = p.X, p.Y, p.Z
return m
}
func Mat4Scale(s Vec3) Mat4 {
return Mat4{s.X, 0, 0, 0, 0, s.Y, 0, 0, 0, 0, s.Z, 0, 0, 0, 0, 1}
}
func Mat4TRS(pos Vec3, rot Quat, scale Vec3) Mat4 {
x, y, z := rot.Normalized().axes()
x, y, z = x.Scale(scale.X), y.Scale(scale.Y), z.Scale(scale.Z)
return Mat4{
x.X, x.Y, x.Z, 0,
y.X, y.Y, y.Z, 0,
z.X, z.Y, z.Z, 0,
pos.X, pos.Y, pos.Z, 1,
}
}
func Mat4Perspective(fovY, aspect, near, far float32) Mat4 {
if aspect == 0 {
aspect = 1
}
if near == far || fovY <= 0 || fovY >= Pi {
return Mat4Identity()
}
f := 1 / Tan(fovY/2)
var m Mat4
m[0] = f / aspect
m[5] = f
m[10] = (far + near) / (near - far)
m[11] = -1
m[14] = 2 * far * near / (near - far)
return m
}
func Mat4LookAt(eye, target, up Vec3) Mat4 {
f := target.Sub(eye).Normalized()
s := f.Cross(up)
if s.Len() < 1e-6 {
s = f.Cross(Vec3{X: 1})
if s.Len() < 1e-6 {
s = f.Cross(Vec3{Z: 1})
}
}
s = s.Normalized()
u := s.Cross(f)
return Mat4{
s.X, u.X, -f.X, 0,
s.Y, u.Y, -f.Y, 0,
s.Z, u.Z, -f.Z, 0,
-s.Dot(eye), -u.Dot(eye), f.Dot(eye), 1,
}
}
func Mat4Lift(m Mat3) Mat4 {
return Mat4{
m[0], -m[1], 0, 0,
-m[3], m[4], 0, 0,
0, 0, 1, 0,
m[6], -m[7], 0, 1,
}
}
func (m Mat4) Col(i int) Vec3 {
return Vec3{X: m[i*4], Y: m[i*4+1], Z: m[i*4+2]}
}
func (m Mat4) Translation() Vec3 {
return Vec3{X: m[12], Y: m[13], Z: m[14]}
}
func (m Mat4) MulVec4(v Vec4) Vec4 {
return Vec4{
X: m[0]*v.X + m[4]*v.Y + m[8]*v.Z + m[12]*v.W,
Y: m[1]*v.X + m[5]*v.Y + m[9]*v.Z + m[13]*v.W,
Z: m[2]*v.X + m[6]*v.Y + m[10]*v.Z + m[14]*v.W,
W: m[3]*v.X + m[7]*v.Y + m[11]*v.Z + m[15]*v.W,
}
}
func (m Mat4) TransformPoint(p Vec3) Vec3 {
v := m.MulVec4(Vec4{X: p.X, Y: p.Y, Z: p.Z, W: 1})
if v.W != 0 {
return Vec3{X: v.X / v.W, Y: v.Y / v.W, Z: v.Z / v.W}
}
return v.XYZ()
}
func (m Mat4) TransformDir(d Vec3) Vec3 {
return Vec3{
X: m[0]*d.X + m[4]*d.Y + m[8]*d.Z,
Y: m[1]*d.X + m[5]*d.Y + m[9]*d.Z,
Z: m[2]*d.X + m[6]*d.Y + m[10]*d.Z,
}
}
func (m Mat4) Transpose() Mat4 {
var r Mat4
for c := 0; c < 4; c++ {
for row := 0; row < 4; row++ {
r[row*4+c] = m[c*4+row]
}
}
return r
}
func (m Mat4) Inverse() (Mat4, bool) {
a00, a01, a02, a03 := m[0], m[1], m[2], m[3]
a10, a11, a12, a13 := m[4], m[5], m[6], m[7]
a20, a21, a22, a23 := m[8], m[9], m[10], m[11]
a30, a31, a32, a33 := m[12], m[13], m[14], m[15]
b00 := a00*a11 - a01*a10
b01 := a00*a12 - a02*a10
b02 := a00*a13 - a03*a10
b03 := a01*a12 - a02*a11
b04 := a01*a13 - a03*a11
b05 := a02*a13 - a03*a12
b06 := a20*a31 - a21*a30
b07 := a20*a32 - a22*a30
b08 := a20*a33 - a23*a30
b09 := a21*a32 - a22*a31
b10 := a21*a33 - a23*a31
b11 := a22*a33 - a23*a32
det := b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06
if Abs(det) < 1e-20 {
return Mat4Identity(), false
}
d := 1 / det
return Mat4{
(a11*b11 - a12*b10 + a13*b09) * d,
(a02*b10 - a01*b11 - a03*b09) * d,
(a31*b05 - a32*b04 + a33*b03) * d,
(a22*b04 - a21*b05 - a23*b03) * d,
(a12*b08 - a10*b11 - a13*b07) * d,
(a00*b11 - a02*b08 + a03*b07) * d,
(a32*b02 - a30*b05 - a33*b01) * d,
(a20*b05 - a22*b02 + a23*b01) * d,
(a10*b10 - a11*b08 + a13*b06) * d,
(a01*b08 - a00*b10 - a03*b06) * d,
(a30*b04 - a31*b02 + a33*b00) * d,
(a21*b02 - a20*b04 - a23*b00) * d,
(a11*b07 - a10*b09 - a12*b06) * d,
(a00*b09 - a01*b07 + a02*b06) * d,
(a31*b01 - a30*b03 - a32*b00) * d,
(a20*b03 - a21*b01 + a22*b00) * d,
}, true
}
func (m Mat4) InverseAffine() (Mat4, bool) {
x, y, z := m.Col(0), m.Col(1), m.Col(2)
det := x.Cross(y).Dot(z)
if Abs(det) < 1e-12 {
return Mat4Identity(), false
}
d := 1 / det
rx := y.Cross(z).Scale(d)
ry := z.Cross(x).Scale(d)
rz := x.Cross(y).Scale(d)
t := m.Translation()
return Mat4{
rx.X, ry.X, rz.X, 0,
rx.Y, ry.Y, rz.Y, 0,
rx.Z, ry.Z, rz.Z, 0,
-rx.Dot(t), -ry.Dot(t), -rz.Dot(t), 1,
}, true
}
func (m Mat4) Decompose() (Vec3, Quat, Vec3, bool) {
x, y, z := m.Col(0), m.Col(1), m.Col(2)
s := Vec3{X: x.Len(), Y: y.Len(), Z: z.Len()}
if s.X < 1e-8 || s.Y < 1e-8 || s.Z < 1e-8 {
return m.Translation(), QuatIdentity(), s, false
}
if x.Cross(y).Dot(z) < 0 {
s.X = -s.X
}
q := quatFromAxes(x.Scale(1/s.X), y.Scale(1/s.Y), z.Scale(1/s.Z))
return m.Translation(), q, s, true
}
func (m Mat4) NearlyEqual(o Mat4, eps float32) bool {
for i := range m {
if !NearlyEqual(m[i], o[i], eps) {
return false
}
}
return true
}