14 Commits

Author SHA1 Message Date
95005baf22 Mat4.InvertAndTranspose()+ ToMat3 and ToMat2 functions 2024-05-04 21:37:24 +04:00
c08e9d8610 NewXYZ funcs for ease of use and new ScaleXYZ funcs for vectors 2024-05-04 21:10:57 +04:00
cc5e7dcbce Mat4 inverse and determinant 2024-05-04 20:45:40 +04:00
ca55a67100 Mat2 and Mat3 determinant and inverse matrices 2024-05-04 04:49:27 +04:00
41307a8c5b Matrix transpose 2024-05-01 02:25:52 +04:00
da81ee79d9 Formatting 2024-05-01 01:25:10 +04:00
4eb59e3386 Implement RotByQuat and AngleVec3 and their tests 2022-12-06 04:26:10 +04:00
ed6806f23b Update readme 2022-10-01 00:15:51 +04:00
69f724922d right/left handed LookAt functions 2022-10-01 00:12:13 +04:00
051f91288d remove comment 2022-07-02 20:59:59 +04:00
d0ac00b388 Set swizzles and tests 2022-05-23 22:26:52 +04:00
971afed401 Reorder interface 2022-05-23 22:12:29 +04:00
547d3ad234 Inlining checks+update readme 2022-05-22 20:24:07 +04:00
e45e4d3304 Add NewQuatEulerXYZ+better comments 2022-05-22 20:14:01 +04:00
20 changed files with 1315 additions and 135 deletions

View File

@ -1,9 +1,10 @@
# gglm
Fast OpenGL/Graphics focused Mathematics library in Go inspired by the c++ library [glm](https://github.com/g-truc/glm).
Fast OpenGL/Graphics focused Mathematics library in Go inspired by the c++ library [glm](https://github.com/g-truc/glm).
gglm currently has the following:
- Matrices are stored column major
- `Vec2`, `Vec3` and `Vec4` structs that implement vector (x,y,z,w) operations
- `Mat2`, `Mat3`, `Mat4` structs that implement square matrix operations
- `Quat` struct that implements quaternion operations
@ -11,9 +12,9 @@ gglm currently has the following:
- Many useful geometric functions (e.g. dot product, cross product, vector reflection etc)
- 32-bit scalar operations (e.g. sin32, cos32, equality using epsilon, sqrt32 etc)
- Useful 32-bit constants (e.g. pi, Deg2Rad, Rad2Deg, float32 epsilon etc)
- Simple 'siwzzle' interfaces that allow you to do things like `.X()` or `.R()` etc.
- Simple 'swizzle' interfaces that allow you to do things like `.X()` or `.R()` etc.
- Very easy to use with graphics/native APIs as everything is implemented using arrays
- `.String()` functions on all types for pretty pritning
- `.String()` functions on all types for pretty printing
## Installation
@ -22,20 +23,11 @@ gglm currently has the following:
## Usage
```go
import "github.com/bloeys/gglm/gglm"
func main() {
//LookAt
camPos := gglm.NewVec3(0, 0, 3)
worldUp := gglm.NewVec3(0, 1, 0)
targetPos := gglm.NewVec3(0, 0, 0)
viewMat := gglm.LookAt(camPos, targetPos, worldUp)
println(viewMat.String())
//Vec2
// Vec2
v1 := &gglm.Vec2{Data: [2]float32{1, 2}}
v2 := &gglm.Vec2{Data: [2]float32{3, 4}}
println(gglm.DistVec2(v1, v2))
@ -43,10 +35,22 @@ func main() {
println(v1.Eq(v2))
v2.Set(1, 2)
println(v1.Eq(v2))
// This performs: v1 += v2
// v1 is returned from the function, so we can chain calls that operate on v1
newX := v1.Add(v2).X()
println("newX:", newX)
// LookAt for a right-handed coordinate system
camPos := gglm.NewVec3(0, 0, 3)
worldUp := gglm.NewVec3(0, 1, 0)
targetPos := gglm.NewVec3(0, 0, 0)
viewMat := gglm.LookAtRH(camPos, targetPos, worldUp)
println(viewMat.String())
}
```
## Notes
You can check compiler inlining decisions using `go run -gcflags "-m" .`. Some functions look a bit weird compared to similar ones
because we are trying to reduce function complexity so the compiler inlines.

View File

@ -6,6 +6,6 @@ const (
Rad2Deg float32 = 180 / Pi
F32Epsilon float32 = 1e-6
//CosHalf is Cos32(0.5)
// CosHalf is Cos32(0.5)
CosHalf float32 = 0.87758256189
)

View File

@ -28,14 +28,14 @@ func Cross(v1, v2 *Vec3) *Vec3 {
}
}
//DistVec2 returns euclidean distance between v1 and v2
// DistVec2 returns euclidean distance between v1 and v2
func DistVec2(v1, v2 *Vec2) float32 {
x := v1.X() - v2.X()
y := v1.Y() - v2.Y()
return float32(math.Sqrt(float64(x*x + y*y)))
}
//DistVec3 returns euclidean distance between v1 and v2
// DistVec3 returns euclidean distance between v1 and v2
func DistVec3(v1, v2 *Vec3) float32 {
x := v1.X() - v2.X()
y := v1.Y() - v2.Y()
@ -43,7 +43,7 @@ func DistVec3(v1, v2 *Vec3) float32 {
return float32(math.Sqrt(float64(x*x + y*y + z*z)))
}
//DistVec4 returns euclidean distance between v1 and v2
// DistVec4 returns euclidean distance between v1 and v2
func DistVec4(v1, v2 *Vec4) float32 {
//Using X() etc won't let the function inline
@ -54,14 +54,14 @@ func DistVec4(v1, v2 *Vec4) float32 {
return float32(math.Sqrt(float64(x*x + y*y + z*z + w*w)))
}
//DistVec2 returns the squared euclidean distance between v1 and v2 (avoids a sqrt)
// DistVec2 returns the squared euclidean distance between v1 and v2 (avoids a sqrt)
func SqrDistVec2(v1, v2 *Vec2) float32 {
x := v1.X() - v2.X()
y := v1.Y() - v2.Y()
return x*x + y*y
}
//DistVec3 returns the squared euclidean distance between v1 and v2 (avoids a sqrt)
// DistVec3 returns the squared euclidean distance between v1 and v2 (avoids a sqrt)
func SqrDistVec3(v1, v2 *Vec3) float32 {
x := v1.X() - v2.X()
y := v1.Y() - v2.Y()
@ -69,7 +69,7 @@ func SqrDistVec3(v1, v2 *Vec3) float32 {
return x*x + y*y + z*z
}
//DistVec4 returns the squared euclidean distance between v1 and v2 (avoids a sqrt)
// DistVec4 returns the squared euclidean distance between v1 and v2 (avoids a sqrt)
func SqrDistVec4(v1, v2 *Vec4) float32 {
x := v1.Data[0] - v2.Data[0]
y := v1.Data[1] - v2.Data[1]
@ -78,9 +78,9 @@ func SqrDistVec4(v1, v2 *Vec4) float32 {
return x*x + y*y + z*z + w*w
}
//ReflectVec2 returns the reflected vector of the incoming vector 'v', and the surface normal 'n'.
// ReflectVec2 returns the reflected vector of the incoming vector 'v', and the surface normal 'n'.
//
//Note: n must be normalized or you will get wrong results
// Note: n must be normalized or you will get wrong results
func ReflectVec2(v, n *Vec2) *Vec2 {
//reflectedVec = v 2*dot(v, norm)*norm
@ -94,9 +94,9 @@ func ReflectVec2(v, n *Vec2) *Vec2 {
}
}
//ReflectVec3 returns the reflected vector of the incoming vector 'v', and the surface normal 'n'.
// ReflectVec3 returns the reflected vector of the incoming vector 'v', and the surface normal 'n'.
//
//Note: n must be normalized or you will get wrong results
// Note: n must be normalized or you will get wrong results
func ReflectVec3(v, n *Vec3) *Vec3 {
//reflectedVec = v 2*dot(v, norm)*norm
@ -111,7 +111,12 @@ func ReflectVec3(v, n *Vec3) *Vec3 {
}
}
//AngleQuat returns the angle between the two quaternions in radians
// AngleVec3 returns the angle between the two vectors in radians
func AngleVec3(v1, v2 *Vec3) float32 {
return Acos32(DotVec3(v1, v2) / (v1.Mag() * v2.Mag()))
}
// AngleQuat returns the angle between the two quaternions in radians
func AngleQuat(q1, q2 *Quat) float32 {
return Acos32(DotQuat(q1, q2))
}

View File

@ -13,7 +13,7 @@ const (
MatSize4x4
)
//String panics if the MatSize is not known
// String panics if the MatSize is not known
func (ms MatSize) String() string {
switch ms {

View File

@ -24,7 +24,7 @@ func (m *Mat2) Size() MatSize {
}
func (m *Mat2) String() string {
//+ always shows +/- sign; - means pad to the right; 9 means total of 9 digits (or padding if less); .3 means 3 decimals
// + always shows +/- sign; - means pad to the right; 9 means total of 9 digits (or padding if less); .3 means 3 decimals
return fmt.Sprintf("\n| %+-9.3f %+-9.3f |\n| %+-9.3f %+-9.3f |\n", m.Data[0][0], m.Data[0][1], m.Data[1][0], m.Data[1][1])
}
@ -32,7 +32,7 @@ func (m *Mat2) Col(c int) *Vec2 {
return &Vec2{Data: m.Data[c]}
}
//Add m += m2
// Add m += m2
func (m *Mat2) Add(m2 *Mat2) *Mat2 {
m.Data[0][0] += m2.Data[0][0]
m.Data[0][1] += m2.Data[0][1]
@ -41,7 +41,7 @@ func (m *Mat2) Add(m2 *Mat2) *Mat2 {
return m
}
//Add m -= m2
// Add m -= m2
func (m *Mat2) Sub(m2 *Mat2) *Mat2 {
m.Data[0][0] -= m2.Data[0][0]
m.Data[0][1] -= m2.Data[0][1]
@ -50,7 +50,7 @@ func (m *Mat2) Sub(m2 *Mat2) *Mat2 {
return m
}
//Mul m *= m2
// Mul m *= m2
func (m1 *Mat2) Mul(m2 *Mat2) *Mat2 {
m1.Data = [2][2]float32{
{
@ -66,7 +66,7 @@ func (m1 *Mat2) Mul(m2 *Mat2) *Mat2 {
return m1
}
//Scale m *= x (element wise multiplication)
// Scale m *= x (element wise multiplication)
func (m *Mat2) Scale(x float32) *Mat2 {
m.Data[0][0] *= x
m.Data[0][1] *= x
@ -75,15 +75,45 @@ func (m *Mat2) Scale(x float32) *Mat2 {
return m
}
func (v *Mat2) Clone() *Mat2 {
return &Mat2{Data: v.Data}
func (m *Mat2) Clone() *Mat2 {
return &Mat2{Data: m.Data}
}
func (m *Mat2) Eq(m2 *Mat2) bool {
return m.Data == m2.Data
}
//AddMat2 m3 = m1 + m2
func (m *Mat2) Transpose() *Mat2 {
m.Data = [2][2]float32{
{m.Data[0][0], m.Data[1][0]},
{m.Data[0][1], m.Data[1][1]},
}
return m
}
func (m *Mat2) Determinant() float32 {
return (m.Data[0][0] * m.Data[1][1]) - (m.Data[0][1] * m.Data[1][0])
}
// Invert inverts this matrix.
//
// Note that the inverse is not defined if the determinant is zero or extremely small.
// In the case the determinant is zero the matrix will (usually) get filled with infinities
func (m *Mat2) Invert() *Mat2 {
// https://www.cuemath.com/algebra/inverse-of-2x2-matrix/
inverseDet := 1 / m.Determinant()
m.Data = [2][2]float32{
{m.Data[1][1] * inverseDet, -m.Data[0][1] * inverseDet}, // Col0
{-m.Data[1][0] * inverseDet, m.Data[0][0] * inverseDet}, // Col1
}
return m
}
// AddMat2 m3 = m1 + m2
func AddMat2(m1, m2 *Mat2) *Mat2 {
return &Mat2{
Data: [2][2]float32{
@ -99,7 +129,7 @@ func AddMat2(m1, m2 *Mat2) *Mat2 {
}
}
//SubMat2 m3 = m1 - m2
// SubMat2 m3 = m1 - m2
func SubMat2(m1, m2 *Mat2) *Mat2 {
return &Mat2{
Data: [2][2]float32{
@ -115,7 +145,7 @@ func SubMat2(m1, m2 *Mat2) *Mat2 {
}
}
//MulMat2 m3 = m1 * m2
// MulMat2 m3 = m1 * m2
func MulMat2(m1, m2 *Mat2) *Mat2 {
return &Mat2{
Data: [2][2]float32{
@ -131,7 +161,7 @@ func MulMat2(m1, m2 *Mat2) *Mat2 {
}
}
//MulMat2Vec2 v2 = m1 * v1
// MulMat2Vec2 v2 = m1 * v1
func MulMat2Vec2(m1 *Mat2, v1 *Vec2) *Vec2 {
return &Vec2{
Data: [2]float32{
@ -141,7 +171,7 @@ func MulMat2Vec2(m1 *Mat2, v1 *Vec2) *Vec2 {
}
}
//NewMat2Id returns the 2x2 identity matrix
// NewMat2Id returns the 2x2 identity matrix
func NewMat2Id() *Mat2 {
return &Mat2{
Data: [2][2]float32{
@ -150,3 +180,39 @@ func NewMat2Id() *Mat2 {
},
}
}
func NewMat2Diag(diagVal float32) *Mat2 {
return &Mat2{
Data: [2][2]float32{
{diagVal, 0},
{0, diagVal},
},
}
}
func NewMat2DiagArr(diag [2]float32) *Mat2 {
return &Mat2{
Data: [2][2]float32{
{diag[0], 0},
{0, diag[1]},
},
}
}
func NewMat2Vec2(col0, col1 *Vec2) *Mat2 {
return &Mat2{
Data: [2][2]float32{
col0.Data,
col1.Data,
},
}
}
func NewMat2Arr(col0, col1 [2]float32) *Mat2 {
return &Mat2{
Data: [2][2]float32{
col0,
col1,
},
}
}

View File

@ -147,6 +147,88 @@ func TestMulMat2Vec2(t *testing.T) {
}
}
func TestTransposeMat2(t *testing.T) {
m := gglm.NewMat2Id()
ans := gglm.NewMat2Id()
if !m.Transpose().Transpose().Eq(ans) {
t.Errorf("Got: %v; Expected: %v", m.String(), ans.String())
}
if !m.Transpose().Eq(ans) {
t.Errorf("Got: %v; Expected: %v", m.String(), ans.String())
}
m = &gglm.Mat2{
Data: [2][2]float32{
{00, 01},
{10, 11},
},
}
ans = &gglm.Mat2{
Data: [2][2]float32{
{00, 10},
{01, 11},
},
}
if !m.Transpose().Eq(ans) {
t.Errorf("Got: %v; Expected: %v", m.String(), ans.String())
}
}
func TestDeterminantMat2(t *testing.T) {
m := gglm.NewMat2Id()
ans := float32(1)
if m.Determinant() != ans {
t.Errorf("Got: %f; Expected: %f", m.Determinant(), ans)
}
m = &gglm.Mat2{
Data: [2][2]float32{
{1, 8},
{5, 31},
},
}
ans = -9
if m.Determinant() != ans {
t.Errorf("Got: %f; Expected: %f", m.Determinant(), ans)
}
}
func TestInvertMat2(t *testing.T) {
m := gglm.NewMat2Id()
ans := gglm.NewMat2Id()
if !m.Invert().Eq(ans) {
t.Errorf("Got: %v; Expected: %v", m.String(), ans.String())
}
m = &gglm.Mat2{
Data: [2][2]float32{
{1, 8},
{5, 31},
},
}
ans = &gglm.Mat2{
Data: [2][2]float32{
{-31 / 9.0, 8 / 9.0},
{5 / 9.0, -1 / 9.0},
},
}
if !m.Invert().Eq(ans) {
t.Errorf("Got: %v; Expected: %v", m.String(), ans.String())
}
}
func BenchmarkMulMat2(b *testing.B) {
m1 := gglm.NewMat2Id()

View File

@ -35,7 +35,7 @@ func (m *Mat3) Col(c int) *Vec3 {
return &Vec3{Data: m.Data[c]}
}
//Add m += m2
// Add m += m2
func (m *Mat3) Add(m2 *Mat3) *Mat3 {
m.Data[0][0] += m2.Data[0][0]
@ -52,7 +52,7 @@ func (m *Mat3) Add(m2 *Mat3) *Mat3 {
return m
}
//Add m -= m2
// Add m -= m2
func (m *Mat3) Sub(m2 *Mat3) *Mat3 {
m.Data[0][0] -= m2.Data[0][0]
@ -69,7 +69,7 @@ func (m *Mat3) Sub(m2 *Mat3) *Mat3 {
return m
}
//Mul m *= m2
// Mul m *= m2
func (m *Mat3) Mul(m2 *Mat3) *Mat3 {
//Array indices:
@ -109,7 +109,7 @@ func (m *Mat3) Mul(m2 *Mat3) *Mat3 {
return m
}
//Scale m *= x (element wise multiplication)
// Scale m *= x (element wise multiplication)
func (m *Mat3) Scale(x float32) *Mat3 {
m.Data[0][0] *= x
@ -126,15 +126,85 @@ func (m *Mat3) Scale(x float32) *Mat3 {
return m
}
func (v *Mat3) Clone() *Mat3 {
return &Mat3{Data: v.Data}
func (m *Mat3) Clone() *Mat3 {
return &Mat3{Data: m.Data}
}
func (m *Mat3) Eq(m2 *Mat3) bool {
return m.Data == m2.Data
}
//AddMat3 m3 = m1 + m2
func (m *Mat3) Transpose() *Mat3 {
m.Data = [3][3]float32{
{m.Data[0][0], m.Data[1][0], m.Data[2][0]},
{m.Data[0][1], m.Data[1][1], m.Data[2][1]},
{m.Data[0][2], m.Data[1][2], m.Data[2][2]},
}
return m
}
func (m *Mat3) Determinant() float32 {
x := m.Data[0][0] * (m.Data[1][1]*m.Data[2][2] - m.Data[2][1]*m.Data[1][2])
y := m.Data[1][0] * (m.Data[2][1]*m.Data[0][2] - m.Data[0][1]*m.Data[2][2])
z := m.Data[2][0] * (m.Data[0][1]*m.Data[1][2] - m.Data[1][1]*m.Data[0][2])
return x + y + z
}
// Invert inverts this matrix.
//
// Note that the inverse is not defined if the determinant is zero or extremely small.
// In the case the determinant is zero the matrix will (usually) get filled with infinities
func (m *Mat3) Invert() *Mat3 {
// https://www.cuemath.com/algebra/inverse-of-3x3-matrix/
// Manually inline the determinant function because go doesn't
x := m.Data[0][0] * (m.Data[1][1]*m.Data[2][2] - m.Data[2][1]*m.Data[1][2])
y := m.Data[1][0] * (m.Data[2][1]*m.Data[0][2] - m.Data[0][1]*m.Data[2][2])
z := m.Data[2][0] * (m.Data[0][1]*m.Data[1][2] - m.Data[1][1]*m.Data[0][2])
inverseDet := 1 / (x + y + z)
m.Data = [3][3]float32{
// Col0
{
(m.Data[1][1]*m.Data[2][2] - m.Data[2][1]*m.Data[1][2]) * inverseDet,
-(m.Data[0][1]*m.Data[2][2] - m.Data[2][1]*m.Data[0][2]) * inverseDet,
(m.Data[0][1]*m.Data[1][2] - m.Data[1][1]*m.Data[0][2]) * inverseDet,
},
// Col1
{
-(m.Data[1][0]*m.Data[2][2] - m.Data[2][0]*m.Data[1][2]) * inverseDet,
(m.Data[0][0]*m.Data[2][2] - m.Data[2][0]*m.Data[0][2]) * inverseDet,
-(m.Data[0][0]*m.Data[1][2] - m.Data[1][0]*m.Data[0][2]) * inverseDet,
},
// Col2
{
(m.Data[1][0]*m.Data[2][1] - m.Data[2][0]*m.Data[1][1]) * inverseDet,
-(m.Data[0][0]*m.Data[2][1] - m.Data[2][0]*m.Data[0][1]) * inverseDet,
(m.Data[0][0]*m.Data[1][1] - m.Data[1][0]*m.Data[0][1]) * inverseDet,
},
}
return m
}
// ToMat2 returns a Mat2 that contains the top-left 2x2 section of the Mat3.
// That is, column 2 and row 2 are dropped.
func (m *Mat3) ToMat2() Mat2 {
return Mat2{
Data: [2][2]float32{
{m.Data[0][0], m.Data[0][1]},
{m.Data[1][0], m.Data[1][1]},
},
}
}
// AddMat3 m3 = m1 + m2
func AddMat3(m1, m2 *Mat3) *Mat3 {
return &Mat3{
Data: [3][3]float32{
@ -157,7 +227,7 @@ func AddMat3(m1, m2 *Mat3) *Mat3 {
}
}
//SubMat3 m3 = m1 - m2
// SubMat3 m3 = m1 - m2
func SubMat3(m1, m2 *Mat3) *Mat3 {
return &Mat3{
Data: [3][3]float32{
@ -180,7 +250,7 @@ func SubMat3(m1, m2 *Mat3) *Mat3 {
}
}
//MulMat3 m3 = m1 * m2
// MulMat3 m3 = m1 * m2
func MulMat3(m1, m2 *Mat3) *Mat3 {
m00 := m1.Data[0][0]
@ -216,7 +286,7 @@ func MulMat3(m1, m2 *Mat3) *Mat3 {
}
}
//MulMat3Vec3 v2 = m1 * v1
// MulMat3Vec3 v2 = m1 * v1
func MulMat3Vec3(m1 *Mat3, v1 *Vec3) *Vec3 {
return &Vec3{
Data: [3]float32{
@ -227,7 +297,7 @@ func MulMat3Vec3(m1 *Mat3, v1 *Vec3) *Vec3 {
}
}
//NewMat3Id returns the 3x3 identity matrix
// NewMat3Id returns the 3x3 identity matrix
func NewMat3Id() *Mat3 {
return &Mat3{
Data: [3][3]float32{
@ -237,3 +307,43 @@ func NewMat3Id() *Mat3 {
},
}
}
func NewMat3Diag(diagVal float32) *Mat3 {
return &Mat3{
Data: [3][3]float32{
{diagVal, 0, 0},
{0, diagVal, 0},
{0, 0, diagVal},
},
}
}
func NewMat3DiagArr(diag [3]float32) *Mat3 {
return &Mat3{
Data: [3][3]float32{
{diag[0], 0, 0},
{0, diag[1], 0},
{0, 0, diag[2]},
},
}
}
func NewMat3Vec3(col0, col1, col2 *Vec3) *Mat3 {
return &Mat3{
Data: [3][3]float32{
col0.Data,
col1.Data,
col2.Data,
},
}
}
func NewMat3Arr(col0, col1, col2 [3]float32) *Mat3 {
return &Mat3{
Data: [3][3]float32{
col0,
col1,
col2,
},
}
}

View File

@ -162,6 +162,93 @@ func TestMulMat3Vec3(t *testing.T) {
}
}
func TestTransposeMat3(t *testing.T) {
m := gglm.NewMat3Id()
ans := gglm.NewMat3Id()
if !m.Transpose().Transpose().Eq(ans) {
t.Errorf("Got: %v; Expected: %v", m.String(), ans.String())
}
if !m.Transpose().Eq(ans) {
t.Errorf("Got: %v; Expected: %v", m.String(), ans.String())
}
m = &gglm.Mat3{
Data: [3][3]float32{
{00, 01, 02},
{10, 11, 12},
{20, 21, 22},
},
}
ans = &gglm.Mat3{
Data: [3][3]float32{
{00, 10, 20},
{01, 11, 21},
{02, 12, 22},
},
}
if !m.Transpose().Eq(ans) {
t.Errorf("Got: %v; Expected: %v", m.String(), ans.String())
}
}
func TestDeterminantMat3(t *testing.T) {
m := gglm.NewMat3Id()
ans := float32(1)
if m.Determinant() != ans {
t.Errorf("Got: %f; Expected: %f", m.Determinant(), ans)
}
m = &gglm.Mat3{
Data: [3][3]float32{
{1, 8, 2},
{5, 3, 5},
{9, 6, 7},
},
}
ans = 77
if m.Determinant() != ans {
t.Errorf("Got: %f; Expected: %f", m.Determinant(), ans)
}
}
func TestInvertMat3(t *testing.T) {
m := gglm.NewMat3Id()
ans := gglm.NewMat3Id()
if !m.Invert().Eq(ans) {
t.Errorf("Got: %v; Expected: %v", m.String(), ans.String())
}
m = &gglm.Mat3{
Data: [3][3]float32{
{1, 8, 2},
{5, 3, 5},
{9, 6, 7},
},
}
ans = &gglm.Mat3{
Data: [3][3]float32{
{-9 / 77.0, -4 / 7.0, 34 / 77.0},
{10 / 77.0, -1 / 7.0, 5 / 77.0},
{3 / 77.0, 6 / 7.0, -37 / 77.0},
},
}
if !m.Invert().Eq(ans) {
t.Errorf("Got: %v; Expected: %v", m.String(), ans.String())
}
}
func BenchmarkMulMat3(b *testing.B) {
m1 := gglm.NewMat3Id()

View File

@ -36,7 +36,7 @@ func (m *Mat4) Col(c int) *Vec4 {
return &Vec4{Data: m.Data[c]}
}
//Add m += m2
// Add m += m2
func (m *Mat4) Add(m2 *Mat4) *Mat4 {
m.Data[0][0] += m2.Data[0][0]
@ -62,7 +62,7 @@ func (m *Mat4) Add(m2 *Mat4) *Mat4 {
return m
}
//Add m -= m2
// Add m -= m2
func (m *Mat4) Sub(m2 *Mat4) *Mat4 {
m.Data[0][0] -= m2.Data[0][0]
@ -87,7 +87,7 @@ func (m *Mat4) Sub(m2 *Mat4) *Mat4 {
return m
}
//Mul m *= m2
// Mul m *= m2
func (m *Mat4) Mul(m2 *Mat4) *Mat4 {
//Array indices:
@ -147,7 +147,7 @@ func (m *Mat4) Mul(m2 *Mat4) *Mat4 {
return m
}
//Scale m *= x (element wise multiplication)
// Scale m *= x (element wise multiplication)
func (m *Mat4) Scale(x float32) *Mat4 {
m.Data[0][0] *= x
@ -180,7 +180,321 @@ func (m *Mat4) Eq(m2 *Mat4) bool {
return m.Data == m2.Data
}
//AddMat4 m3 = m1 + m2
func (m *Mat4) Transpose() *Mat4 {
m.Data = [4][4]float32{
{m.Data[0][0], m.Data[1][0], m.Data[2][0], m.Data[3][0]},
{m.Data[0][1], m.Data[1][1], m.Data[2][1], m.Data[3][1]},
{m.Data[0][2], m.Data[1][2], m.Data[2][2], m.Data[3][2]},
{m.Data[0][3], m.Data[1][3], m.Data[2][3], m.Data[3][3]},
}
return m
}
func (m *Mat4) Determinant() float32 {
// Many thanks to the C++ GLM project here :)
coef00 := m.Data[2][2]*m.Data[3][3] - m.Data[3][2]*m.Data[2][3]
coef02 := m.Data[1][2]*m.Data[3][3] - m.Data[3][2]*m.Data[1][3]
coef03 := m.Data[1][2]*m.Data[2][3] - m.Data[2][2]*m.Data[1][3]
coef04 := m.Data[2][1]*m.Data[3][3] - m.Data[3][1]*m.Data[2][3]
coef06 := m.Data[1][1]*m.Data[3][3] - m.Data[3][1]*m.Data[1][3]
coef07 := m.Data[1][1]*m.Data[2][3] - m.Data[2][1]*m.Data[1][3]
coef08 := m.Data[2][1]*m.Data[3][2] - m.Data[3][1]*m.Data[2][2]
coef10 := m.Data[1][1]*m.Data[3][2] - m.Data[3][1]*m.Data[1][2]
coef11 := m.Data[1][1]*m.Data[2][2] - m.Data[2][1]*m.Data[1][2]
coef12 := m.Data[2][0]*m.Data[3][3] - m.Data[3][0]*m.Data[2][3]
coef14 := m.Data[1][0]*m.Data[3][3] - m.Data[3][0]*m.Data[1][3]
coef15 := m.Data[1][0]*m.Data[2][3] - m.Data[2][0]*m.Data[1][3]
coef16 := m.Data[2][0]*m.Data[3][2] - m.Data[3][0]*m.Data[2][2]
coef18 := m.Data[1][0]*m.Data[3][2] - m.Data[3][0]*m.Data[1][2]
coef19 := m.Data[1][0]*m.Data[2][2] - m.Data[2][0]*m.Data[1][2]
coef20 := m.Data[2][0]*m.Data[3][1] - m.Data[3][0]*m.Data[2][1]
coef22 := m.Data[1][0]*m.Data[3][1] - m.Data[3][0]*m.Data[1][1]
coef23 := m.Data[1][0]*m.Data[2][1] - m.Data[2][0]*m.Data[1][1]
fac0 := NewVec4(coef00, coef00, coef02, coef03)
fac1 := NewVec4(coef04, coef04, coef06, coef07)
fac2 := NewVec4(coef08, coef08, coef10, coef11)
fac3 := NewVec4(coef12, coef12, coef14, coef15)
fac4 := NewVec4(coef16, coef16, coef18, coef19)
fac5 := NewVec4(coef20, coef20, coef22, coef23)
vec0 := NewVec4(m.Data[1][0], m.Data[0][0], m.Data[0][0], m.Data[0][0])
vec1 := NewVec4(m.Data[1][1], m.Data[0][1], m.Data[0][1], m.Data[0][1])
vec2 := NewVec4(m.Data[1][2], m.Data[0][2], m.Data[0][2], m.Data[0][2])
vec3 := NewVec4(m.Data[1][3], m.Data[0][3], m.Data[0][3], m.Data[0][3])
inv0 := NewVec4(
vec1.X()*fac0.X()-vec2.X()*fac1.X()+vec3.X()*fac2.X(),
vec1.Y()*fac0.Y()-vec2.Y()*fac1.Y()+vec3.Y()*fac2.Y(),
vec1.Z()*fac0.Z()-vec2.Z()*fac1.Z()+vec3.Z()*fac2.Z(),
vec1.W()*fac0.W()-vec2.W()*fac1.W()+vec3.W()*fac2.W(),
)
inv1 := NewVec4(
vec0.X()*fac0.X()-vec2.X()*fac3.X()+vec3.X()*fac4.X(),
vec0.Y()*fac0.Y()-vec2.Y()*fac3.Y()+vec3.Y()*fac4.Y(),
vec0.Z()*fac0.Z()-vec2.Z()*fac3.Z()+vec3.Z()*fac4.Z(),
vec0.W()*fac0.W()-vec2.W()*fac3.W()+vec3.W()*fac4.W(),
)
inv2 := NewVec4(
vec0.X()*fac1.X()-vec1.X()*fac3.X()+vec3.X()*fac5.X(),
vec0.Y()*fac1.Y()-vec1.Y()*fac3.Y()+vec3.Y()*fac5.Y(),
vec0.Z()*fac1.Z()-vec1.Z()*fac3.Z()+vec3.Z()*fac5.Z(),
vec0.W()*fac1.W()-vec1.W()*fac3.W()+vec3.W()*fac5.W(),
)
inv3 := NewVec4(
vec0.X()*fac2.X()-vec1.X()*fac4.X()+vec2.X()*fac5.X(),
vec0.Y()*fac2.Y()-vec1.Y()*fac4.Y()+vec2.Y()*fac5.Y(),
vec0.Z()*fac2.Z()-vec1.Z()*fac4.Z()+vec2.Z()*fac5.Z(),
vec0.W()*fac2.W()-vec1.W()*fac4.W()+vec2.W()*fac5.W(),
)
signA := NewVec4(+1, -1, +1, -1)
signB := NewVec4(-1, +1, -1, +1)
inverse := NewMat4Arr(
inv0.ScaleVec(signA).Data,
inv1.ScaleVec(signB).Data,
inv2.ScaleVec(signA).Data,
inv3.ScaleVec(signB).Data,
)
row0 := NewVec4(inverse.Data[0][0], inverse.Data[1][0], inverse.Data[2][0], inverse.Data[3][0])
dot0 := NewVec4Arr(row0.ScaleArr(m.Data[0]).Data)
det := (dot0.X() + dot0.Y()) + (dot0.Z() + dot0.W())
return det
}
// Invert inverts this matrix.
//
// Note that the inverse is not defined if the determinant is zero or extremely small.
// In the case the determinant is zero the matrix will (usually) get filled with infinities
func (m *Mat4) Invert() *Mat4 {
// Many thanks to the C++ GLM project here :)
coef00 := m.Data[2][2]*m.Data[3][3] - m.Data[3][2]*m.Data[2][3]
coef02 := m.Data[1][2]*m.Data[3][3] - m.Data[3][2]*m.Data[1][3]
coef03 := m.Data[1][2]*m.Data[2][3] - m.Data[2][2]*m.Data[1][3]
coef04 := m.Data[2][1]*m.Data[3][3] - m.Data[3][1]*m.Data[2][3]
coef06 := m.Data[1][1]*m.Data[3][3] - m.Data[3][1]*m.Data[1][3]
coef07 := m.Data[1][1]*m.Data[2][3] - m.Data[2][1]*m.Data[1][3]
coef08 := m.Data[2][1]*m.Data[3][2] - m.Data[3][1]*m.Data[2][2]
coef10 := m.Data[1][1]*m.Data[3][2] - m.Data[3][1]*m.Data[1][2]
coef11 := m.Data[1][1]*m.Data[2][2] - m.Data[2][1]*m.Data[1][2]
coef12 := m.Data[2][0]*m.Data[3][3] - m.Data[3][0]*m.Data[2][3]
coef14 := m.Data[1][0]*m.Data[3][3] - m.Data[3][0]*m.Data[1][3]
coef15 := m.Data[1][0]*m.Data[2][3] - m.Data[2][0]*m.Data[1][3]
coef16 := m.Data[2][0]*m.Data[3][2] - m.Data[3][0]*m.Data[2][2]
coef18 := m.Data[1][0]*m.Data[3][2] - m.Data[3][0]*m.Data[1][2]
coef19 := m.Data[1][0]*m.Data[2][2] - m.Data[2][0]*m.Data[1][2]
coef20 := m.Data[2][0]*m.Data[3][1] - m.Data[3][0]*m.Data[2][1]
coef22 := m.Data[1][0]*m.Data[3][1] - m.Data[3][0]*m.Data[1][1]
coef23 := m.Data[1][0]*m.Data[2][1] - m.Data[2][0]*m.Data[1][1]
fac0 := NewVec4(coef00, coef00, coef02, coef03)
fac1 := NewVec4(coef04, coef04, coef06, coef07)
fac2 := NewVec4(coef08, coef08, coef10, coef11)
fac3 := NewVec4(coef12, coef12, coef14, coef15)
fac4 := NewVec4(coef16, coef16, coef18, coef19)
fac5 := NewVec4(coef20, coef20, coef22, coef23)
vec0 := NewVec4(m.Data[1][0], m.Data[0][0], m.Data[0][0], m.Data[0][0])
vec1 := NewVec4(m.Data[1][1], m.Data[0][1], m.Data[0][1], m.Data[0][1])
vec2 := NewVec4(m.Data[1][2], m.Data[0][2], m.Data[0][2], m.Data[0][2])
vec3 := NewVec4(m.Data[1][3], m.Data[0][3], m.Data[0][3], m.Data[0][3])
inv0 := NewVec4(
vec1.X()*fac0.X()-vec2.X()*fac1.X()+vec3.X()*fac2.X(),
vec1.Y()*fac0.Y()-vec2.Y()*fac1.Y()+vec3.Y()*fac2.Y(),
vec1.Z()*fac0.Z()-vec2.Z()*fac1.Z()+vec3.Z()*fac2.Z(),
vec1.W()*fac0.W()-vec2.W()*fac1.W()+vec3.W()*fac2.W(),
)
inv1 := NewVec4(
vec0.X()*fac0.X()-vec2.X()*fac3.X()+vec3.X()*fac4.X(),
vec0.Y()*fac0.Y()-vec2.Y()*fac3.Y()+vec3.Y()*fac4.Y(),
vec0.Z()*fac0.Z()-vec2.Z()*fac3.Z()+vec3.Z()*fac4.Z(),
vec0.W()*fac0.W()-vec2.W()*fac3.W()+vec3.W()*fac4.W(),
)
inv2 := NewVec4(
vec0.X()*fac1.X()-vec1.X()*fac3.X()+vec3.X()*fac5.X(),
vec0.Y()*fac1.Y()-vec1.Y()*fac3.Y()+vec3.Y()*fac5.Y(),
vec0.Z()*fac1.Z()-vec1.Z()*fac3.Z()+vec3.Z()*fac5.Z(),
vec0.W()*fac1.W()-vec1.W()*fac3.W()+vec3.W()*fac5.W(),
)
inv3 := NewVec4(
vec0.X()*fac2.X()-vec1.X()*fac4.X()+vec2.X()*fac5.X(),
vec0.Y()*fac2.Y()-vec1.Y()*fac4.Y()+vec2.Y()*fac5.Y(),
vec0.Z()*fac2.Z()-vec1.Z()*fac4.Z()+vec2.Z()*fac5.Z(),
vec0.W()*fac2.W()-vec1.W()*fac4.W()+vec2.W()*fac5.W(),
)
signA := NewVec4(+1, -1, +1, -1)
signB := NewVec4(-1, +1, -1, +1)
inverse := NewMat4Arr(
inv0.ScaleVec(signA).Data,
inv1.ScaleVec(signB).Data,
inv2.ScaleVec(signA).Data,
inv3.ScaleVec(signB).Data,
)
row0 := NewVec4(inverse.Data[0][0], inverse.Data[1][0], inverse.Data[2][0], inverse.Data[3][0])
dot0 := NewVec4Arr(row0.ScaleArr(m.Data[0]).Data)
det := (dot0.X() + dot0.Y()) + (dot0.Z() + dot0.W())
inverseDet := 1.0 / det
m.Data = inverse.Scale(inverseDet).Data
return m
}
// InvertAndTranspose is equivalent to m.Invert().Transpose(), that is invert first, then transpose the inverted matrix.
//
// This function is provided as a convenience and as a small optimization, as it inlines the invert and transpose functions which means we only
// have 1 function call.
//
// Additionally, the inverse of the matrix is written to the matrix immediately transposed instead of writing the inverse and then transposing it in a second operation.
func (m *Mat4) InvertAndTranspose() *Mat4 {
// Many thanks to the C++ GLM project here :)
coef00 := m.Data[2][2]*m.Data[3][3] - m.Data[3][2]*m.Data[2][3]
coef02 := m.Data[1][2]*m.Data[3][3] - m.Data[3][2]*m.Data[1][3]
coef03 := m.Data[1][2]*m.Data[2][3] - m.Data[2][2]*m.Data[1][3]
coef04 := m.Data[2][1]*m.Data[3][3] - m.Data[3][1]*m.Data[2][3]
coef06 := m.Data[1][1]*m.Data[3][3] - m.Data[3][1]*m.Data[1][3]
coef07 := m.Data[1][1]*m.Data[2][3] - m.Data[2][1]*m.Data[1][3]
coef08 := m.Data[2][1]*m.Data[3][2] - m.Data[3][1]*m.Data[2][2]
coef10 := m.Data[1][1]*m.Data[3][2] - m.Data[3][1]*m.Data[1][2]
coef11 := m.Data[1][1]*m.Data[2][2] - m.Data[2][1]*m.Data[1][2]
coef12 := m.Data[2][0]*m.Data[3][3] - m.Data[3][0]*m.Data[2][3]
coef14 := m.Data[1][0]*m.Data[3][3] - m.Data[3][0]*m.Data[1][3]
coef15 := m.Data[1][0]*m.Data[2][3] - m.Data[2][0]*m.Data[1][3]
coef16 := m.Data[2][0]*m.Data[3][2] - m.Data[3][0]*m.Data[2][2]
coef18 := m.Data[1][0]*m.Data[3][2] - m.Data[3][0]*m.Data[1][2]
coef19 := m.Data[1][0]*m.Data[2][2] - m.Data[2][0]*m.Data[1][2]
coef20 := m.Data[2][0]*m.Data[3][1] - m.Data[3][0]*m.Data[2][1]
coef22 := m.Data[1][0]*m.Data[3][1] - m.Data[3][0]*m.Data[1][1]
coef23 := m.Data[1][0]*m.Data[2][1] - m.Data[2][0]*m.Data[1][1]
fac0 := NewVec4(coef00, coef00, coef02, coef03)
fac1 := NewVec4(coef04, coef04, coef06, coef07)
fac2 := NewVec4(coef08, coef08, coef10, coef11)
fac3 := NewVec4(coef12, coef12, coef14, coef15)
fac4 := NewVec4(coef16, coef16, coef18, coef19)
fac5 := NewVec4(coef20, coef20, coef22, coef23)
vec0 := NewVec4(m.Data[1][0], m.Data[0][0], m.Data[0][0], m.Data[0][0])
vec1 := NewVec4(m.Data[1][1], m.Data[0][1], m.Data[0][1], m.Data[0][1])
vec2 := NewVec4(m.Data[1][2], m.Data[0][2], m.Data[0][2], m.Data[0][2])
vec3 := NewVec4(m.Data[1][3], m.Data[0][3], m.Data[0][3], m.Data[0][3])
inv0 := NewVec4(
vec1.X()*fac0.X()-vec2.X()*fac1.X()+vec3.X()*fac2.X(),
vec1.Y()*fac0.Y()-vec2.Y()*fac1.Y()+vec3.Y()*fac2.Y(),
vec1.Z()*fac0.Z()-vec2.Z()*fac1.Z()+vec3.Z()*fac2.Z(),
vec1.W()*fac0.W()-vec2.W()*fac1.W()+vec3.W()*fac2.W(),
)
inv1 := NewVec4(
vec0.X()*fac0.X()-vec2.X()*fac3.X()+vec3.X()*fac4.X(),
vec0.Y()*fac0.Y()-vec2.Y()*fac3.Y()+vec3.Y()*fac4.Y(),
vec0.Z()*fac0.Z()-vec2.Z()*fac3.Z()+vec3.Z()*fac4.Z(),
vec0.W()*fac0.W()-vec2.W()*fac3.W()+vec3.W()*fac4.W(),
)
inv2 := NewVec4(
vec0.X()*fac1.X()-vec1.X()*fac3.X()+vec3.X()*fac5.X(),
vec0.Y()*fac1.Y()-vec1.Y()*fac3.Y()+vec3.Y()*fac5.Y(),
vec0.Z()*fac1.Z()-vec1.Z()*fac3.Z()+vec3.Z()*fac5.Z(),
vec0.W()*fac1.W()-vec1.W()*fac3.W()+vec3.W()*fac5.W(),
)
inv3 := NewVec4(
vec0.X()*fac2.X()-vec1.X()*fac4.X()+vec2.X()*fac5.X(),
vec0.Y()*fac2.Y()-vec1.Y()*fac4.Y()+vec2.Y()*fac5.Y(),
vec0.Z()*fac2.Z()-vec1.Z()*fac4.Z()+vec2.Z()*fac5.Z(),
vec0.W()*fac2.W()-vec1.W()*fac4.W()+vec2.W()*fac5.W(),
)
signA := NewVec4(+1, -1, +1, -1)
signB := NewVec4(-1, +1, -1, +1)
inverse := NewMat4Arr(
inv0.ScaleVec(signA).Data,
inv1.ScaleVec(signB).Data,
inv2.ScaleVec(signA).Data,
inv3.ScaleVec(signB).Data,
)
row0 := NewVec4(inverse.Data[0][0], inverse.Data[1][0], inverse.Data[2][0], inverse.Data[3][0])
dot0 := NewVec4Arr(row0.ScaleArr(m.Data[0]).Data)
det := (dot0.X() + dot0.Y()) + (dot0.Z() + dot0.W())
inverseDet := 1.0 / det
inverse.Scale(inverseDet)
// Manually inline transpose
m.Data = [4][4]float32{
{inverse.Data[0][0], inverse.Data[1][0], inverse.Data[2][0], inverse.Data[3][0]},
{inverse.Data[0][1], inverse.Data[1][1], inverse.Data[2][1], inverse.Data[3][1]},
{inverse.Data[0][2], inverse.Data[1][2], inverse.Data[2][2], inverse.Data[3][2]},
{inverse.Data[0][3], inverse.Data[1][3], inverse.Data[2][3], inverse.Data[3][3]},
}
return m
}
// ToMat2 returns a Mat2 that contains the top-left 2x2 section of the Mat4.
// That is, columns 2 and 3, and rows 2 and 3, are dropped.
func (m *Mat4) ToMat2() Mat2 {
return Mat2{
Data: [2][2]float32{
{m.Data[0][0], m.Data[0][1]},
{m.Data[1][0], m.Data[1][1]},
},
}
}
// ToMat3 returns a Mat3 that contains the top-left 3x3 section of the Mat4.
// That is, column 3 and row 3 are dropped.
func (m *Mat4) ToMat3() Mat3 {
return Mat3{
Data: [3][3]float32{
{m.Data[0][0], m.Data[0][1], m.Data[0][2]},
{m.Data[1][0], m.Data[1][1], m.Data[1][2]},
{m.Data[2][0], m.Data[2][1], m.Data[2][2]},
},
}
}
// AddMat4 m3 = m1 + m2
func AddMat4(m1, m2 *Mat4) *Mat4 {
return &Mat4{
Data: [4][4]float32{
@ -212,7 +526,7 @@ func AddMat4(m1, m2 *Mat4) *Mat4 {
}
}
//SubMat4 m3 = m1 - m2
// SubMat4 m3 = m1 - m2
func SubMat4(m1, m2 *Mat4) *Mat4 {
return &Mat4{
Data: [4][4]float32{
@ -244,7 +558,7 @@ func SubMat4(m1, m2 *Mat4) *Mat4 {
}
}
//MulMat4 m3 = m1 * m2
// MulMat4 m3 = m1 * m2
func MulMat4(m1, m2 *Mat4) *Mat4 {
m00 := m1.Data[0][0]
@ -297,7 +611,7 @@ func MulMat4(m1, m2 *Mat4) *Mat4 {
}
}
//MulMat4Vec4 v2 = m1 * v1
// MulMat4Vec4 v2 = m1 * v1
func MulMat4Vec4(m1 *Mat4, v1 *Vec4) *Vec4 {
return &Vec4{
Data: [4]float32{
@ -309,7 +623,7 @@ func MulMat4Vec4(m1 *Mat4, v1 *Vec4) *Vec4 {
}
}
//NewMat4Id returns the 4x4 identity matrix
// NewMat4Id returns the 4x4 identity matrix
func NewMat4Id() *Mat4 {
return &Mat4{
Data: [4][4]float32{
@ -320,3 +634,47 @@ func NewMat4Id() *Mat4 {
},
}
}
func NewMat4Diag(diagVal float32) *Mat4 {
return &Mat4{
Data: [4][4]float32{
{diagVal, 0, 0, 0},
{0, diagVal, 0, 0},
{0, 0, diagVal, 0},
{0, 0, 0, diagVal},
},
}
}
func NewMat4DiagArr(diag [4]float32) *Mat4 {
return &Mat4{
Data: [4][4]float32{
{diag[0], 0, 0, 0},
{0, diag[1], 0, 0},
{0, 0, diag[2], 0},
{0, 0, 0, diag[3]},
},
}
}
func NewMat4Vec4(col0, col1, col2, col3 *Vec4) *Mat4 {
return &Mat4{
Data: [4][4]float32{
col0.Data,
col1.Data,
col2.Data,
col3.Data,
},
}
}
func NewMat4Arr(col0, col1, col2, col3 [4]float32) *Mat4 {
return &Mat4{
Data: [4][4]float32{
col0,
col1,
col2,
col3,
},
}
}

View File

@ -182,6 +182,130 @@ func TestMulMat4Vec4(t *testing.T) {
}
}
func TestTransposeMat4(t *testing.T) {
m := gglm.NewMat4Id()
ans := gglm.NewMat4Id()
if !m.Transpose().Transpose().Eq(ans) {
t.Errorf("Got: %v; Expected: %v", m.String(), ans.String())
}
if !m.Transpose().Eq(ans) {
t.Errorf("Got: %v; Expected: %v", m.String(), ans.String())
}
m = &gglm.Mat4{
Data: [4][4]float32{
{00, 01, 02, 03},
{10, 11, 12, 13},
{20, 21, 22, 23},
{30, 31, 32, 33},
},
}
ans = &gglm.Mat4{
Data: [4][4]float32{
{00, 10, 20, 30},
{01, 11, 21, 31},
{02, 12, 22, 32},
{03, 13, 23, 33},
},
}
if !m.Transpose().Eq(ans) {
t.Errorf("Got: %v; Expected: %v", m.String(), ans.String())
}
}
func TestDeterminantMat4(t *testing.T) {
m := gglm.NewMat4Id()
ans := float32(1)
if m.Determinant() != ans {
t.Errorf("Got: %f; Expected: %f", m.Determinant(), ans)
}
m = &gglm.Mat4{
Data: [4][4]float32{
{1, 0, 2, 1},
{2, 1, 3, 0},
{3, 0, 4, 1},
{4, 1, 5, 1},
},
}
ans = -2
if m.Determinant() != ans {
t.Errorf("Got: %f; Expected: %f", m.Determinant(), ans)
}
}
func TestInvertMat4(t *testing.T) {
m := gglm.NewMat4Id()
ans := gglm.NewMat4Id()
if !m.Invert().Eq(ans) {
t.Errorf("Got: %v; Expected: %v", m.String(), ans.String())
}
m = &gglm.Mat4{
Data: [4][4]float32{
{1, 0, 2, 1},
{2, 1, 3, 0},
{3, 0, 4, 1},
{4, 1, 5, 1},
},
}
ans = &gglm.Mat4{
Data: [4][4]float32{
{-1, -1, 0, 1},
{0.5, 0, -3 / 2.0, 1},
{0.5, 1, 0.5, -1},
{1, -1, -1, 1},
},
}
if !m.Invert().Eq(ans) {
t.Errorf("Got: %v\nExpected: %v;\n", m.String(), ans.String())
}
}
func TestInvertAndTransposeMat4(t *testing.T) {
m := gglm.NewMat4Id()
ans := gglm.NewMat4Id().Transpose()
if !m.InvertAndTranspose().Eq(ans) {
t.Errorf("Got: %v; Expected: %v", m.String(), ans.String())
}
m = &gglm.Mat4{
Data: [4][4]float32{
{1, 0, 2, 1},
{2, 1, 3, 0},
{3, 0, 4, 1},
{4, 1, 5, 1},
},
}
ans = &gglm.Mat4{
Data: [4][4]float32{
{-1, -1, 0, 1},
{0.5, 0, -3 / 2.0, 1},
{0.5, 1, 0.5, -1},
{1, -1, -1, 1},
},
}
if !m.InvertAndTranspose().Eq(ans.Transpose()) {
t.Errorf("Got: %v\nExpected: %v;\n", m.String(), ans.String())
}
}
func BenchmarkMulMat4(b *testing.B) {
m1 := gglm.NewMat4Id()

View File

@ -11,12 +11,12 @@ type Quat struct {
Vec4
}
//Eq checks for exact equality
// Eq checks for exact equality
func (q *Quat) Eq(q2 *Quat) bool {
return q.Data == q2.Data
}
//Angle returns the angle represented by this quaternion
// Angle returns the angle represented by this quaternion in radians
func (q *Quat) Angle() float32 {
if Abs32(q.Data[3]) > CosHalf {
@ -31,7 +31,7 @@ func (q *Quat) Angle() float32 {
return Acos32(q.Data[3]) * 2
}
//Axis returns the rotation axis represented by this quaternion
// Axis returns the rotation axis represented by this quaternion
func (q *Quat) Axis() *Vec3 {
var t float32 = 1 - q.Data[3]*q.Data[3]
@ -47,8 +47,8 @@ func (q *Quat) Axis() *Vec3 {
}}
}
//Euler takes rotations in radians and produces a rotation that
//rotates around the z-axis, y-axis and lastly x-axis.
// Euler takes rotations in radians and produces a rotation that
// rotates around the z-axis, y-axis and lastly x-axis.
func NewQuatEuler(v *Vec3) *Quat {
//Some other common terminology: x=roll, y=pitch, z=yaw
@ -74,7 +74,34 @@ func NewQuatEuler(v *Vec3) *Quat {
}
}
//AngleAxis rotates rotRad radians around the *normalized* vector rotAxisNorm
// Euler takes rotations in radians and produces a rotation that
// rotates around the z-axis, y-axis and lastly x-axis.
func NewQuatEulerXYZ(x, y, z float32) *Quat {
//Some other common terminology: x=roll, y=pitch, z=yaw
sinX, cosX := Sincos32(x * 0.5)
sinY, cosY := Sincos32(y * 0.5)
sinZ, cosZ := Sincos32(z * 0.5)
//This produces a z->y->x multiply order, but its written as XYZ.
//This is due to XYZ meaning independent rotation matrices, so Z is applied
//first, then Y matrix and lastly X.
//See this for more info: https://github.com/godotengine/godot/issues/6816#issuecomment-254592170
//
//Note: On most conversion tools putting the multiply order (e.g. ZYX for us) is required.
return &Quat{
Vec4: Vec4{
Data: [4]float32{
sinX*cosY*cosZ - cosX*sinY*sinZ,
cosX*sinY*cosZ + sinX*cosY*sinZ,
cosX*cosY*sinZ - sinX*sinY*cosZ,
cosX*cosY*cosZ + sinX*sinY*sinZ,
},
},
}
}
// NewQuatAngleAxis produces a quaternion thats rotates rotRad radians around the *normalized* vector rotAxisNorm
func NewQuatAngleAxis(rotRad float32, rotAxisNorm *Vec3) *Quat {
s, c := Sincos32(rotRad * 0.5)

View File

@ -14,6 +14,12 @@ func TestNewQuatEuler(t *testing.T) {
if !gglm.EqF32(q.X(), ans.X()) || !gglm.EqF32(q.Y(), ans.Y()) || !gglm.EqF32(q.Z(), ans.Z()) || !gglm.EqF32(q.W(), ans.W()) {
t.Errorf("Got: %v; Expected: %v", q.String(), ans.String())
}
q = gglm.NewQuatEulerXYZ(180*gglm.Deg2Rad, 180*gglm.Deg2Rad, 180*gglm.Deg2Rad)
if !gglm.EqF32(q.X(), ans.X()) || !gglm.EqF32(q.Y(), ans.Y()) || !gglm.EqF32(q.Z(), ans.Z()) || !gglm.EqF32(q.W(), ans.W()) {
t.Errorf("Got: %v; Expected: %v", q.String(), ans.String())
}
}
func TestNewQuatAngleAxis(t *testing.T) {

View File

@ -2,12 +2,12 @@ package gglm
import "math"
//EqF32 true if abs(f1-f2) <= F32Epsilon
// EqF32 true if abs(f1-f2) <= F32Epsilon
func EqF32(f1, f2 float32) bool {
return math.Abs(float64(f1-f2)) <= float64(F32Epsilon)
}
//EqF32Epsilon true if abs(f1-f2) <= eps
// EqF32Epsilon true if abs(f1-f2) <= eps
func EqF32Epsilon(f1, f2, eps float32) bool {
return math.Abs(float64(f1-f2)) <= float64(eps)
}

View File

@ -2,56 +2,64 @@ package gglm
type Swizzle1 interface {
X() float32
R() float32
SetX(float32)
SetR(float32)
AddX(float32)
R() float32
SetR(float32)
AddR(float32)
}
type Swizzle2 interface {
Swizzle1
Y() float32
G() float32
SetY(float32)
SetG(float32)
AddY(float32)
SetXY(float32, float32)
AddXY(float32, float32)
G() float32
SetG(float32)
AddG(float32)
AddXY(float32, float32)
SetRG(float32, float32)
AddRG(float32, float32)
}
type Swizzle3 interface {
Swizzle2
Z() float32
B() float32
SetZ(float32)
SetB(float32)
AddZ(float32)
SetXYZ(float32, float32, float32)
AddXYZ(float32, float32, float32)
B() float32
SetB(float32)
AddB(float32)
AddXYZ(float32, float32, float32)
SetRGB(float32, float32, float32)
AddRGB(float32, float32, float32)
}
type Swizzle4 interface {
Swizzle3
W() float32
A() float32
SetW(float32)
SetA(float32)
AddW(float32)
SetXYZW(float32, float32, float32, float32)
AddXYZW(float32, float32, float32, float32)
A() float32
SetA(float32)
AddA(float32)
AddXYZW(float32, float32, float32, float32)
SetRGBA(float32, float32, float32, float32)
AddRGBA(float32, float32, float32, float32)
}

View File

@ -8,12 +8,12 @@ import (
var _ Mat = &TrMat{}
var _ fmt.Stringer = &TrMat{}
//TrMat represents a transformation matrix
// TrMat represents a transformation matrix
type TrMat struct {
Mat4
}
//Translate adds v to the translation components of the transformation matrix
// Translate adds v to the translation components of the transformation matrix
func (t *TrMat) Translate(v *Vec3) *TrMat {
t.Data[3][0] += v.Data[0]
t.Data[3][1] += v.Data[1]
@ -21,7 +21,7 @@ func (t *TrMat) Translate(v *Vec3) *TrMat {
return t
}
//Scale multiplies the scale components of the transformation matrix by v
// Scale multiplies the scale components of the transformation matrix by v
func (t *TrMat) Scale(v *Vec3) *TrMat {
t.Data[0][0] *= v.Data[0]
t.Data[1][1] *= v.Data[1]
@ -29,7 +29,7 @@ func (t *TrMat) Scale(v *Vec3) *TrMat {
return t
}
//Rotate takes a *normalized* axis and angles in radians to rotate around the given axis
// Rotate takes a *normalized* axis and angles in radians to rotate around the given axis
func (t *TrMat) Rotate(rads float32, axis *Vec3) *TrMat {
s := Sin32(rads)
@ -145,7 +145,29 @@ func NewRotMat(q *Quat) *TrMat {
}
}
func LookAt(pos, targetPos, worldUp *Vec3) *TrMat {
// LookAtRH does a right-handed coordinate system lookAt (RH is the default for OpenGL).
// Can be used to create the view matrix
func LookAtRH(pos, targetPos, worldUp *Vec3) *TrMat {
forward := SubVec3(targetPos, pos).Normalize()
right := Cross(forward, worldUp).Normalize()
up := Cross(right, forward)
return &TrMat{
Mat4: Mat4{
Data: [4][4]float32{
{right.Data[0], up.Data[0], -forward.Data[0], 0},
{right.Data[1], up.Data[1], -forward.Data[1], 0},
{right.Data[2], up.Data[2], -forward.Data[2], 0},
{-DotVec3(pos, right), -DotVec3(pos, up), DotVec3(pos, forward), 1},
},
},
}
}
// LookAtLH does a left-handed coordinate system lookAt.
// Can be used to create the view matrix
func LookAtLH(pos, targetPos, worldUp *Vec3) *TrMat {
forward := SubVec3(targetPos, pos).Normalize()
right := Cross(worldUp, forward).Normalize()
@ -157,13 +179,13 @@ func LookAt(pos, targetPos, worldUp *Vec3) *TrMat {
{right.Data[0], up.Data[0], forward.Data[0], 0},
{right.Data[1], up.Data[1], forward.Data[1], 0},
{right.Data[2], up.Data[2], forward.Data[2], 0},
{-DotVec3(pos, right), -DotVec3(pos, up), DotVec3(pos, forward), 1},
{-DotVec3(pos, right), -DotVec3(pos, up), -DotVec3(pos, forward), 1},
},
},
}
}
//Perspective creates a perspective projection matrix
// Perspective creates a perspective projection matrix
func Perspective(fov, aspectRatio, nearClip, farClip float32) *Mat4 {
halfFovTan := float32(math.Tan(float64(fov * 0.5)))
return &Mat4{
@ -176,7 +198,7 @@ func Perspective(fov, aspectRatio, nearClip, farClip float32) *Mat4 {
}
}
//Perspective creates an orthographic projection matrix
// Perspective creates an orthographic projection matrix
func Ortho(left, right, top, bottom, nearClip, farClip float32) *TrMat {
return &TrMat{
Mat4: Mat4{

View File

@ -62,11 +62,21 @@ func (v *Vec2) AddG(g float32) {
v.Data[1] += g
}
func (v *Vec2) SetXY(x, y float32) {
v.Data[0] = x
v.Data[1] = y
}
func (v *Vec2) AddXY(x, y float32) {
v.Data[0] += x
v.Data[1] += y
}
func (v *Vec2) SetRG(r, g float32) {
v.Data[0] = r
v.Data[1] = g
}
func (v *Vec2) AddRG(r, g float32) {
v.Data[0] += r
v.Data[1] += g
@ -76,33 +86,47 @@ func (v *Vec2) String() string {
return fmt.Sprintf("(%f, %f)", v.X(), v.Y())
}
//Scale v *= x (element wise multiplication)
// Scale v *= x (element wise multiplication)
func (v *Vec2) Scale(x float32) *Vec2 {
v.Data[0] *= x
v.Data[1] *= x
return v
}
//Add v += v2
// ScaleVec v *= v2 (element wise multiplication)
func (v *Vec2) ScaleVec(v2 *Vec2) *Vec2 {
v.Data[0] *= v2.X()
v.Data[1] *= v2.Y()
return v
}
// ScaleArr v *= arr (element wise multiplication)
func (v *Vec2) ScaleArr(arr [2]float32) *Vec2 {
v.Data[0] *= arr[0]
v.Data[1] *= arr[1]
return v
}
// Add v += v2
func (v *Vec2) Add(v2 *Vec2) *Vec2 {
v.Data[0] += v2.X()
v.Data[1] += v2.Y()
return v
}
//SubVec2 v -= v2
// SubVec2 v -= v2
func (v *Vec2) Sub(v2 *Vec2) *Vec2 {
v.Data[0] -= v2.X()
v.Data[1] -= v2.Y()
return v
}
//Mag returns the magnitude of the vector
// Mag returns the magnitude of the vector
func (v *Vec2) Mag() float32 {
return float32(math.Sqrt(float64(v.X()*v.X() + v.Y()*v.Y())))
}
//Mag returns the squared magnitude of the vector
// Mag returns the squared magnitude of the vector
func (v *Vec2) SqrMag() float32 {
return v.X()*v.X() + v.Y()*v.Y()
}
@ -126,7 +150,7 @@ func (v *Vec2) Clone() *Vec2 {
return &Vec2{Data: v.Data}
}
//AddVec2 v3 = v1 + v2
// AddVec2 v3 = v1 + v2
func AddVec2(v1, v2 *Vec2) *Vec2 {
return &Vec2{
Data: [2]float32{
@ -136,7 +160,7 @@ func AddVec2(v1, v2 *Vec2) *Vec2 {
}
}
//SubVec2 v3 = v1 - v2
// SubVec2 v3 = v1 - v2
func SubVec2(v1, v2 *Vec2) *Vec2 {
return &Vec2{
Data: [2]float32{
@ -154,3 +178,9 @@ func NewVec2(x, y float32) *Vec2 {
},
}
}
func NewVec2Arr(arr [2]float32) *Vec2 {
return &Vec2{
Data: arr,
}
}

View File

@ -84,22 +84,44 @@ func (v *Vec3) AddB(b float32) {
v.Data[2] += b
}
func (v *Vec3) SetXY(x, y float32) {
v.Data[0] = x
v.Data[1] = y
}
func (v *Vec3) AddXY(x, y float32) {
v.Data[0] += x
v.Data[1] += y
}
func (v *Vec3) SetRG(r, g float32) {
v.Data[0] = r
v.Data[1] = g
}
func (v *Vec3) AddRG(r, g float32) {
v.Data[0] += r
v.Data[1] += g
}
func (v *Vec3) SetXYZ(x, y, z float32) {
v.Data[0] = x
v.Data[1] = y
v.Data[2] = z
}
func (v *Vec3) AddXYZ(x, y, z float32) {
v.Data[0] += x
v.Data[1] += y
v.Data[2] += z
}
func (v *Vec3) SetRGB(r, g, b float32) {
v.Data[0] = r
v.Data[1] = g
v.Data[2] = b
}
func (v *Vec3) AddRGB(r, g, b float32) {
v.Data[0] += r
v.Data[1] += g
@ -110,7 +132,7 @@ func (v *Vec3) String() string {
return fmt.Sprintf("(%f, %f, %f)", v.X(), v.Y(), v.Z())
}
//Scale v *= x (element wise multiplication)
// Scale v *= x (element wise multiplication)
func (v *Vec3) Scale(x float32) *Vec3 {
v.Data[0] *= x
v.Data[1] *= x
@ -118,6 +140,22 @@ func (v *Vec3) Scale(x float32) *Vec3 {
return v
}
// ScaleVec v *= v2 (element wise multiplication)
func (v *Vec3) ScaleVec(v2 *Vec3) *Vec3 {
v.Data[0] *= v2.X()
v.Data[1] *= v2.Y()
v.Data[2] *= v2.Z()
return v
}
// ScaleArr v *= arr (element wise multiplication)
func (v *Vec3) ScaleArr(arr [3]float32) *Vec3 {
v.Data[0] *= arr[0]
v.Data[1] *= arr[1]
v.Data[2] *= arr[2]
return v
}
func (v *Vec3) Add(v2 *Vec3) *Vec3 {
v.Data[0] += v2.X()
@ -126,7 +164,7 @@ func (v *Vec3) Add(v2 *Vec3) *Vec3 {
return v
}
//SubVec3 v -= v2
// SubVec3 v -= v2
func (v *Vec3) Sub(v2 *Vec3) *Vec3 {
v.Data[0] -= v2.X()
v.Data[1] -= v2.Y()
@ -134,12 +172,12 @@ func (v *Vec3) Sub(v2 *Vec3) *Vec3 {
return v
}
//Mag returns the magnitude of the vector
// Mag returns the magnitude of the vector
func (v *Vec3) Mag() float32 {
return float32(math.Sqrt(float64(v.X()*v.X() + v.Y()*v.Y() + v.Z()*v.Z())))
}
//Mag returns the squared magnitude of the vector
// Mag returns the squared magnitude of the vector
func (v *Vec3) SqrMag() float32 {
return v.X()*v.X() + v.Y()*v.Y() + v.Z()*v.Z()
}
@ -154,7 +192,7 @@ func (v *Vec3) Set(x, y, z float32) {
v.Data[2] = z
}
//Normalize normalizes this vector and returns it (doesn't copy)
// Normalize normalizes this vector and returns it (doesn't copy)
func (v *Vec3) Normalize() *Vec3 {
mag := float32(math.Sqrt(float64(v.X()*v.X() + v.Y()*v.Y() + v.Z()*v.Z())))
v.Data[0] /= mag
@ -164,11 +202,30 @@ func (v *Vec3) Normalize() *Vec3 {
return v
}
// RotByQuat rotates this vector by the given quaternion
func (v *Vec3) RotByQuat(q *Quat) *Vec3 {
// Reference: https://gamedev.stackexchange.com/questions/28395/rotating-vector3-by-a-quaternion
// u := NewVec3(q.X(), q.Y(), q.Z())
// t1 := 2.0f * dot(u, v) * u
// t2 := (s*s - dot(u, u)) * v
// t3 := 2.0f * s * cross(u, v);
// vprime = t1 + t2 + t3
u := NewVec3(q.X(), q.Y(), q.Z())
t1 := u.Clone().Scale(2 * DotVec3(u, v))
t2 := v.Clone().Scale(q.W()*q.W() - DotVec3(u, u))
t3 := Cross(u, v).Scale(2 * q.W())
v.Data = t1.Add(t2).Add(t3).Data
return v
}
func (v *Vec3) Clone() *Vec3 {
return &Vec3{Data: v.Data}
}
//AsRad returns a new vector with all values converted to Radians (i.e. multiplied by gglm.Deg2Rad)
// AsRad returns a new vector with all values converted to Radians (i.e. multiplied by gglm.Deg2Rad)
func (v *Vec3) AsRad() *Vec3 {
return &Vec3{
Data: [3]float32{
@ -179,7 +236,7 @@ func (v *Vec3) AsRad() *Vec3 {
}
}
//AddVec3 v3 = v1 + v2
// AddVec3 v3 = v1 + v2
func AddVec3(v1, v2 *Vec3) *Vec3 {
return &Vec3{
Data: [3]float32{
@ -190,7 +247,7 @@ func AddVec3(v1, v2 *Vec3) *Vec3 {
}
}
//SubVec3 v3 = v1 - v2
// SubVec3 v3 = v1 - v2
func SubVec3(v1, v2 *Vec3) *Vec3 {
return &Vec3{
Data: [3]float32{
@ -210,3 +267,9 @@ func NewVec3(x, y, z float32) *Vec3 {
},
}
}
func NewVec3Arr(arr [3]float32) *Vec3 {
return &Vec3{
Data: arr,
}
}

View File

@ -108,28 +108,57 @@ func (v *Vec4) AddA(a float32) {
v.Data[3] += a
}
func (v *Vec4) SetXY(x, y float32) {
v.Data[0] = x
v.Data[1] = y
}
func (v *Vec4) AddXY(x, y float32) {
v.Data[0] += x
v.Data[1] += y
}
func (v *Vec4) SetRG(r, g float32) {
v.Data[0] = r
v.Data[1] = g
}
func (v *Vec4) AddRG(r, g float32) {
v.Data[0] += r
v.Data[1] += g
}
func (v *Vec4) SetXYZ(x, y, z float32) {
v.Data[0] = x
v.Data[1] = y
v.Data[2] = z
}
func (v *Vec4) AddXYZ(x, y, z float32) {
v.Data[0] += x
v.Data[1] += y
v.Data[2] += z
}
func (v *Vec4) SetRGB(r, g, b float32) {
v.Data[0] = r
v.Data[1] = g
v.Data[2] = b
}
func (v *Vec4) AddRGB(r, g, b float32) {
v.Data[0] += r
v.Data[1] += g
v.Data[2] += b
}
func (v *Vec4) SetXYZW(x, y, z, w float32) {
v.Data[0] = x
v.Data[1] = y
v.Data[2] = z
v.Data[3] = w
}
func (v *Vec4) AddXYZW(x, y, z, w float32) {
v.Data[0] += x
v.Data[1] += y
@ -137,6 +166,13 @@ func (v *Vec4) AddXYZW(x, y, z, w float32) {
v.Data[3] += w
}
func (v *Vec4) SetRGBA(r, g, b, a float32) {
v.Data[0] = r
v.Data[1] = g
v.Data[2] = b
v.Data[3] = a
}
func (v *Vec4) AddRGBA(r, g, b, a float32) {
v.Data[0] += r
v.Data[1] += g
@ -148,7 +184,7 @@ func (v *Vec4) String() string {
return fmt.Sprintf("(%f, %f, %f, %f)", v.X(), v.Y(), v.Z(), v.W())
}
//Scale v *= x (element wise multiplication)
// Scale v *= x (element wise multiplication)
func (v *Vec4) Scale(x float32) *Vec4 {
v.Data[0] *= x
v.Data[1] *= x
@ -157,6 +193,24 @@ func (v *Vec4) Scale(x float32) *Vec4 {
return v
}
// ScaleVec v *= v2 (element wise multiplication)
func (v *Vec4) ScaleVec(v2 *Vec4) *Vec4 {
v.Data[0] *= v2.X()
v.Data[1] *= v2.Y()
v.Data[2] *= v2.Z()
v.Data[3] *= v2.W()
return v
}
// ScaleArr v *= arr (element wise multiplication)
func (v *Vec4) ScaleArr(arr [4]float32) *Vec4 {
v.Data[0] *= arr[0]
v.Data[1] *= arr[1]
v.Data[2] *= arr[2]
v.Data[3] *= arr[3]
return v
}
func (v *Vec4) Add(v2 *Vec4) *Vec4 {
v.Data[0] += v2.X()
v.Data[1] += v2.Y()
@ -165,7 +219,7 @@ func (v *Vec4) Add(v2 *Vec4) *Vec4 {
return v
}
//SubVec4 v -= v2
// SubVec4 v -= v2
func (v *Vec4) Sub(v2 *Vec4) *Vec4 {
v.Data[0] -= v2.X()
v.Data[1] -= v2.Y()
@ -174,12 +228,12 @@ func (v *Vec4) Sub(v2 *Vec4) *Vec4 {
return v
}
//Mag returns the magnitude of the vector
// Mag returns the magnitude of the vector
func (v *Vec4) Mag() float32 {
return float32(math.Sqrt(float64(v.X()*v.X() + v.Y()*v.Y() + v.Z()*v.Z() + v.W()*v.W())))
}
//Mag returns the squared magnitude of the vector
// Mag returns the squared magnitude of the vector
func (v *Vec4) SqrMag() float32 {
return v.X()*v.X() + v.Y()*v.Y() + v.Z()*v.Z() + v.Z()*v.Z()
}
@ -207,7 +261,7 @@ func (v *Vec4) Clone() *Vec4 {
return &Vec4{Data: v.Data}
}
//AddVec4 v3 = v1 + v2
// AddVec4 v3 = v1 + v2
func AddVec4(v1, v2 *Vec4) *Vec4 {
return &Vec4{
Data: [4]float32{
@ -219,7 +273,7 @@ func AddVec4(v1, v2 *Vec4) *Vec4 {
}
}
//SubVec4 v3 = v1 - v2
// SubVec4 v3 = v1 - v2
func SubVec4(v1, v2 *Vec4) *Vec4 {
return &Vec4{
Data: [4]float32{
@ -241,3 +295,9 @@ func NewVec4(x, y, z, w float32) *Vec4 {
},
}
}
func NewVec4Arr(arr [4]float32) *Vec4 {
return &Vec4{
Data: arr,
}
}

View File

@ -102,26 +102,42 @@ func TestVecSwizzleGet(t *testing.T) {
func TestVecSwizzleSet(t *testing.T) {
//Vec2
v2 := gglm.NewVec2(0, 0)
v2 := gglm.NewVec2(1, 1)
ans2 := gglm.NewVec2(1, 2)
v2.SetX(1)
v2.SetY(2)
if !v2.Eq(ans2) {
t.Errorf("Got: %v; Expected: %v", v2.String(), ans2.String())
}
v2 = gglm.NewVec2(1, 1)
ans2 = gglm.NewVec2(11, 22)
v2.SetR(11)
v2.SetG(22)
if !v2.Eq(ans2) {
t.Errorf("Got: %v; Expected: %v", v2.String(), ans2.String())
}
v2 = gglm.NewVec2(1, 1)
ans2 = gglm.NewVec2(1, 2)
v2.SetXY(1, 2)
if !v2.Eq(ans2) {
t.Errorf("Got: %v; Expected: %v", v2.String(), ans2.String())
}
v2 = gglm.NewVec2(1, 1)
ans2 = gglm.NewVec2(11, 22)
v2.SetRG(11, 22)
if !v2.Eq(ans2) {
t.Errorf("Got: %v; Expected: %v", v2.String(), ans2.String())
}
//Vec3
v3 := gglm.NewVec3(0, 0, 0)
v3 := gglm.NewVec3(1, 1, 1)
ans3 := gglm.NewVec3(1, 2, 3)
v3.SetX(1)
@ -132,6 +148,7 @@ func TestVecSwizzleSet(t *testing.T) {
t.Errorf("Got: %v; Expected: %v", v3.String(), ans3.String())
}
v3 = gglm.NewVec3(1, 1, 1)
ans3 = gglm.NewVec3(11, 22, 33)
v3.SetR(11)
@ -142,8 +159,63 @@ func TestVecSwizzleSet(t *testing.T) {
t.Errorf("Got: %v; Expected: %v", v3.String(), ans3.String())
}
v3 = gglm.NewVec3(1, 1, 1)
ans3 = gglm.NewVec3(1, 2, 1)
v3.SetXY(1, 2)
if !v3.Eq(ans3) {
t.Errorf("Got: %v; Expected: %v", v3.String(), ans3.String())
}
v3 = gglm.NewVec3(1, 1, 1)
ans3 = gglm.NewVec3(1, 2, 1)
v3.SetRG(1, 2)
if !v3.Eq(ans3) {
t.Errorf("Got: %v; Expected: %v", v3.String(), ans3.String())
}
v3 = gglm.NewVec3(1, 1, 1)
ans3 = gglm.NewVec3(1, 2, 3)
v3.SetXYZ(1, 2, 3)
if !v3.Eq(ans3) {
t.Errorf("Got: %v; Expected: %v", v3.String(), ans3.String())
}
v3 = gglm.NewVec3(1, 1, 1)
ans3 = gglm.NewVec3(1, 2, 3)
v3.SetRGB(1, 2, 3)
if !v3.Eq(ans3) {
t.Errorf("Got: %v; Expected: %v", v3.String(), ans3.String())
}
// Test AngleVec3
v3 = gglm.NewVec3(1, 0, 0)
v32 := gglm.NewVec3(1, 0, 0)
angleV3 := gglm.AngleVec3(v3, v32) * gglm.Rad2Deg
if angleV3 != 0 {
t.Errorf("Got: %v; Expected: %v", v3.String(), 0)
}
v32.SetXY(0, 1)
angleV3 = gglm.AngleVec3(v3, v32) * gglm.Rad2Deg
if angleV3 != 90 {
t.Errorf("Got: %v; Expected: %v", v3.String(), 0)
}
// Test rot by quat
v32.SetXY(1, 0)
v32.RotByQuat(gglm.NewQuatAngleAxis(90*gglm.Deg2Rad, gglm.NewVec3(0, 1, 0)))
angleV3 = gglm.AngleVec3(v3, v32) * gglm.Rad2Deg
if angleV3 != 90 {
t.Errorf("Got: %v; Expected: %v", v3.String(), 0)
}
//Vec4
v4 := gglm.NewVec4(0, 0, 0, 0)
v4 := gglm.NewVec4(1, 1, 1, 1)
ans4 := gglm.NewVec4(1, 2, 3, 4)
v4.SetX(1)
@ -155,6 +227,7 @@ func TestVecSwizzleSet(t *testing.T) {
t.Errorf("Got: %v; Expected: %v", v4.String(), ans4.String())
}
v4 = gglm.NewVec4(1, 1, 1, 1)
ans4 = gglm.NewVec4(11, 22, 33, 44)
v4.SetR(11)
@ -165,6 +238,54 @@ func TestVecSwizzleSet(t *testing.T) {
if !v4.Eq(ans4) {
t.Errorf("Got: %v; Expected: %v", v4.String(), ans4.String())
}
v4 = gglm.NewVec4(1, 1, 1, 1)
ans4 = gglm.NewVec4(1, 2, 1, 1)
v4.SetXY(1, 2)
if !v4.Eq(ans4) {
t.Errorf("Got: %v; Expected: %v", v4.String(), ans4.String())
}
v4 = gglm.NewVec4(1, 1, 1, 1)
ans4 = gglm.NewVec4(1, 2, 1, 1)
v4.SetRG(1, 2)
if !v4.Eq(ans4) {
t.Errorf("Got: %v; Expected: %v", v4.String(), ans4.String())
}
v4 = gglm.NewVec4(1, 1, 1, 1)
ans4 = gglm.NewVec4(1, 2, 3, 1)
v4.SetXYZ(1, 2, 3)
if !v4.Eq(ans4) {
t.Errorf("Got: %v; Expected: %v", v4.String(), ans4.String())
}
v4 = gglm.NewVec4(1, 1, 1, 1)
ans4 = gglm.NewVec4(1, 2, 3, 1)
v4.SetRGB(1, 2, 3)
if !v4.Eq(ans4) {
t.Errorf("Got: %v; Expected: %v", v4.String(), ans4.String())
}
v4 = gglm.NewVec4(1, 1, 1, 1)
ans4 = gglm.NewVec4(1, 2, 3, 4)
v4.SetXYZW(1, 2, 3, 4)
if !v4.Eq(ans4) {
t.Errorf("Got: %v; Expected: %v", v4.String(), ans4.String())
}
v4 = gglm.NewVec4(1, 1, 1, 1)
ans4 = gglm.NewVec4(1, 2, 3, 4)
v4.SetRGBA(1, 2, 3, 4)
if !v4.Eq(ans4) {
t.Errorf("Got: %v; Expected: %v", v4.String(), ans4.String())
}
}
func TestVecSwizzleAdd(t *testing.T) {

39
main.go
View File

@ -8,7 +8,7 @@ import (
func main() {
//Mat3
// Mat3
m1 := &gglm.Mat3{
Data: [3][3]float32{
{1, 4, 7},
@ -30,7 +30,7 @@ func main() {
println(m1.String())
println(m3.String())
//Mat4
// Mat4
m4 := &gglm.Mat4{
Data: [4][4]float32{
{1, 5, 9, 13},
@ -55,7 +55,7 @@ func main() {
println(m6.String())
println(m4.Eq(m6))
//Vec2
// Vec2
v1 := &gglm.Vec2{Data: [2]float32{1, 2}}
v2 := &gglm.Vec2{Data: [2]float32{3, 4}}
println(gglm.DistVec2(v1, v2))
@ -64,11 +64,14 @@ func main() {
v2.Set(1, 2)
println(v1.Eq(v2))
v1.AddXY(v2.X(), v2.Y())
println(v1.String())
println("V1: " + v1.String())
v1.Normalize()
println("V1 Normal: " + v1.String())
//Vec3
// Vec3
v3 := &gglm.Vec3{Data: [3]float32{1, 2, 3}}
v4 := &gglm.Vec3{Data: [3]float32{4, 5, 6}}
println(gglm.DistVec3(v3, v4))
@ -85,7 +88,7 @@ func main() {
v3.Normalize()
println("V3 Normal: " + v3.String())
//Vec4
// Vec4
v5 := &gglm.Vec4{Data: [4]float32{1, 2, 3, 4}}
v6 := &gglm.Vec4{Data: [4]float32{5, 6, 7, 8}}
println(gglm.DistVec4(v5, v6))
@ -97,11 +100,15 @@ func main() {
println(gglm.DotVec4(v5, v6))
v5.Add(v6)
v5.AddXYZW(v6.X(), v6.Y(), v6.Z(), v6.W())
println(v6.String())
println("V6: " + v6.String())
v6.Normalize()
println("V6 Normal: " + v6.String())
//Mat2Vec2
// Mat2Vec2
mat2A := gglm.Mat2{
Data: [2][2]float32{
{1, 3},
@ -112,7 +119,7 @@ func main() {
vec2A := gglm.Vec2{Data: [2]float32{1, 2}}
println(gglm.MulMat2Vec2(&mat2A, &vec2A).String())
//Mat3Vec3
// Mat3Vec3
mat3A := gglm.Mat3{
Data: [3][3]float32{
{1, 4, 7},
@ -125,13 +132,13 @@ func main() {
mm3v3 := gglm.MulMat3Vec3(&mat3A, &vec3A)
println(mm3v3.String())
//ReflectVec2
// ReflectVec2
vec2B := &gglm.Vec2{Data: [2]float32{4, 5}}
normA := &gglm.Vec2{Data: [2]float32{0, 1}}
rVec2A := gglm.ReflectVec2(vec2B, normA)
println(rVec2A.String())
//Quaternion
// Quaternion
vRot := &gglm.Vec3{Data: [3]float32{60, 30, 20}}
q := gglm.NewQuatEuler(vRot.AsRad())
println("\n" + vRot.AsRad().String())
@ -141,7 +148,7 @@ func main() {
println("\n" + vRot.Normalize().String())
println(q.String())
//Transform
// Transform
translationMat := gglm.NewTranslationMat(&gglm.Vec3{Data: [3]float32{1, 2, 3}})
rotMat := gglm.NewRotMat(gglm.NewQuatEuler(gglm.NewVec3(60, 30, 20).AsRad()))
scaleMat := gglm.NewScaleMat(gglm.NewVec3(1, 1, 1))
@ -151,32 +158,32 @@ func main() {
println("\n\n\n", modelMat.String())
//Clone Vec2
// Clone Vec2
v2Orig := gglm.Vec2{Data: [2]float32{1, 2}}
v2Clone := v2Orig.Clone()
v2Clone.SetX(99)
println("\n\n", v2Orig.String(), "; ", v2Clone.String())
//Clone TrMat
// Clone TrMat
trMatOrig := gglm.NewTranslationMat(gglm.NewVec3(1, 2, 3))
trMatClone := trMatOrig.Clone()
trMatClone.Scale(gglm.NewVec3(2, 2, 2))
trMatClone.Translate(gglm.NewVec3(9, 0, 0))
println("\n\n", trMatOrig.String(), "; ", trMatClone.String())
//Quat geo
// Quat geo
q1 := gglm.NewQuatEuler(gglm.NewVec3(180, 0, 0).AsRad())
q2 := gglm.NewQuatEuler(gglm.NewVec3(0, 180, 0).AsRad())
println(gglm.AngleQuat(q1, q2) * gglm.Rad2Deg)
//LookAt
// LookAt
camPos := gglm.NewVec3(0, 0, 3)
worldUp := gglm.NewVec3(0, 1, 0)
targetPos := gglm.NewVec3(0, 0, 0)
viewMat := gglm.LookAt(camPos, targetPos, worldUp)
viewMat := gglm.LookAtRH(camPos, targetPos, worldUp)
println(viewMat.String())
//Mat2Col
// Mat2Col
mc := gglm.NewMat2Id()
println("===============================")
println(mc.String())