19 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
f3db256007 Greatly shorten vec_test 2022-05-22 20:06:06 +04:00
d7f5cbb136 Improve vec tests 2022-05-22 19:43:12 +04:00
2648dc910c 'Add' functions to swizzle interfaces+vec tests 2022-05-22 19:41:43 +04:00
386fa0b641 Ignore emacs files 2022-02-08 18:04:47 +04:00
7d8dce922f Readme update 2022-02-08 17:00:04 +04:00
21 changed files with 1803 additions and 115 deletions

1
.gitignore vendored
View File

@ -14,3 +14,4 @@
# Dependency directories (remove the comment below to include it)
vendor/
.vscode
*~

View File

@ -1,6 +1,54 @@
# gglm
Fast Go OpenGL/Graphics focused Mathematics library 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
- `TrMat` struct that implements 3D transformation matrix operations
- 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 '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 printing
## Installation
`go get github.com/bloeys/gglm`
## Usage
```go
import "github.com/bloeys/gglm/gglm"
func main() {
// Vec2
v1 := &gglm.Vec2{Data: [2]float32{1, 2}}
v2 := &gglm.Vec2{Data: [2]float32{3, 4}}
println(gglm.DistVec2(v1, v2))
println(gglm.SqrDistVec2(v2, v1))
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

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

@ -30,53 +30,103 @@ func (v *Vec2) G() float32 {
return v.Data[1]
}
func (v *Vec2) SetX(f float32) {
v.Data[0] = f
func (v *Vec2) SetX(x float32) {
v.Data[0] = x
}
func (v *Vec2) SetR(f float32) {
v.Data[0] = f
func (v *Vec2) SetR(r float32) {
v.Data[0] = r
}
func (v *Vec2) SetY(f float32) {
v.Data[1] = f
func (v *Vec2) SetY(y float32) {
v.Data[1] = y
}
func (v *Vec2) SetG(f float32) {
v.Data[1] = f
func (v *Vec2) SetG(g float32) {
v.Data[1] = g
}
func (v *Vec2) AddX(x float32) {
v.Data[0] += x
}
func (v *Vec2) AddY(y float32) {
v.Data[1] += y
}
func (v *Vec2) AddR(r float32) {
v.Data[0] += r
}
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
}
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()
}
@ -100,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{
@ -110,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{
@ -128,3 +178,9 @@ func NewVec2(x, y float32) *Vec2 {
},
}
}
func NewVec2Arr(arr [2]float32) *Vec2 {
return &Vec2{
Data: arr,
}
}

View File

@ -60,11 +60,79 @@ func (v *Vec3) SetB(f float32) {
v.Data[2] = f
}
func (v *Vec3) AddX(x float32) {
v.Data[0] += x
}
func (v *Vec3) AddY(y float32) {
v.Data[1] += y
}
func (v *Vec3) AddZ(z float32) {
v.Data[2] += z
}
func (v *Vec3) AddR(r float32) {
v.Data[0] += r
}
func (v *Vec3) AddG(g float32) {
v.Data[1] += g
}
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
v.Data[2] += b
}
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
@ -72,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()
@ -80,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()
@ -88,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()
}
@ -108,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
@ -118,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{
@ -133,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{
@ -144,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{
@ -164,3 +267,9 @@ func NewVec3(x, y, z float32) *Vec3 {
},
}
}
func NewVec3Arr(arr [3]float32) *Vec3 {
return &Vec3{
Data: arr,
}
}

View File

@ -76,11 +76,115 @@ func (v *Vec4) SetA(f float32) {
v.Data[3] = f
}
func (v *Vec4) AddX(x float32) {
v.Data[0] += x
}
func (v *Vec4) AddY(y float32) {
v.Data[1] += y
}
func (v *Vec4) AddZ(z float32) {
v.Data[2] += z
}
func (v *Vec4) AddW(w float32) {
v.Data[3] += w
}
func (v *Vec4) AddR(r float32) {
v.Data[0] += r
}
func (v *Vec4) AddG(g float32) {
v.Data[1] += g
}
func (v *Vec4) AddB(b float32) {
v.Data[2] += b
}
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
v.Data[2] += z
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
v.Data[2] += b
v.Data[3] += a
}
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
@ -89,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()
@ -97,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()
@ -106,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()
}
@ -139,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{
@ -151,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{
@ -173,3 +295,9 @@ func NewVec4(x, y, z, w float32) *Vec4 {
},
}
}
func NewVec4Arr(arr [4]float32) *Vec4 {
return &Vec4{
Data: arr,
}
}

423
gglm/vec_test.go Executable file
View File

@ -0,0 +1,423 @@
package gglm_test
import (
"testing"
"github.com/bloeys/gglm/gglm"
)
func TestVecSwizzleGet(t *testing.T) {
//Vec2
v2 := gglm.NewVec2(1, 2)
var ans2X float32 = 1
var ans2Y float32 = 2
if v2.X() != ans2X {
t.Errorf("Got: %v; Expected: %v", v2.X(), ans2X)
}
if v2.Y() != ans2Y {
t.Errorf("Got: %v; Expected: %v", v2.Y(), ans2Y)
}
if v2.R() != ans2X {
t.Errorf("Got: %v; Expected: %v", v2.R(), ans2X)
}
if v2.G() != ans2Y {
t.Errorf("Got: %v; Expected: %v", v2.G(), ans2Y)
}
//Vec3
v3 := gglm.NewVec3(1, 2, 3)
var ans3X float32 = 1
var ans3Y float32 = 2
var ans3Z float32 = 3
if v3.X() != ans3X {
t.Errorf("Got: %v; Expected: %v", v3.X(), ans3X)
}
if v3.Y() != ans3Y {
t.Errorf("Got: %v; Expected: %v", v3.Y(), ans3Y)
}
if v3.Z() != ans3Z {
t.Errorf("Got: %v; Expected: %v", v3.Z(), ans3Z)
}
if v3.R() != ans3X {
t.Errorf("Got: %v; Expected: %v", v3.R(), ans3X)
}
if v3.G() != ans3Y {
t.Errorf("Got: %v; Expected: %v", v3.G(), ans3Y)
}
if v3.B() != ans3Z {
t.Errorf("Got: %v; Expected: %v", v3.B(), ans3Z)
}
//Vec4
v4 := gglm.NewVec4(1, 2, 3, 4)
var ans4X float32 = 1
var ans4Y float32 = 2
var ans4Z float32 = 3
var ans4W float32 = 4
if v4.X() != ans4X {
t.Errorf("Got: %v; Expected: %v", v4.X(), ans4X)
}
if v4.Y() != ans4Y {
t.Errorf("Got: %v; Expected: %v", v4.Y(), ans4Y)
}
if v4.Z() != ans4Z {
t.Errorf("Got: %v; Expected: %v", v4.Z(), ans4Z)
}
if v4.W() != ans4W {
t.Errorf("Got: %v; Expected: %v", v4.W(), ans4W)
}
if v4.R() != ans4X {
t.Errorf("Got: %v; Expected: %v", v4.R(), ans4X)
}
if v4.G() != ans4Y {
t.Errorf("Got: %v; Expected: %v", v4.G(), ans4Y)
}
if v4.B() != ans4Z {
t.Errorf("Got: %v; Expected: %v", v4.B(), ans4Z)
}
if v4.A() != ans4W {
t.Errorf("Got: %v; Expected: %v", v4.A(), ans4W)
}
}
func TestVecSwizzleSet(t *testing.T) {
//Vec2
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(1, 1, 1)
ans3 := gglm.NewVec3(1, 2, 3)
v3.SetX(1)
v3.SetY(2)
v3.SetZ(3)
if !v3.Eq(ans3) {
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)
v3.SetG(22)
v3.SetB(33)
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.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(1, 1, 1, 1)
ans4 := gglm.NewVec4(1, 2, 3, 4)
v4.SetX(1)
v4.SetY(2)
v4.SetZ(3)
v4.SetW(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(11, 22, 33, 44)
v4.SetR(11)
v4.SetG(22)
v4.SetB(33)
v4.SetA(44)
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) {
//Vec2
v2 := gglm.NewVec2(1, 1)
ans2 := gglm.NewVec2(2, 3)
v2.AddX(1)
v2.AddY(2)
if !v2.Eq(ans2) {
t.Errorf("Got: %v; Expected: %v", v2.String(), ans2.String())
}
v2 = gglm.NewVec2(1, 1)
v2.AddR(1)
v2.AddG(2)
if !v2.Eq(ans2) {
t.Errorf("Got: %v; Expected: %v", v2.String(), ans2.String())
}
v2 = gglm.NewVec2(1, 1)
v2.AddXY(1, 2)
if !v2.Eq(ans2) {
t.Errorf("Got: %v; Expected: %v", v2.String(), ans2.String())
}
v2 = gglm.NewVec2(1, 1)
v2.AddRG(1, 2)
if !v2.Eq(ans2) {
t.Errorf("Got: %v; Expected: %v", v2.String(), ans2.String())
}
//Vec3
v3 := gglm.NewVec3(1, 1, 1)
ans3 := gglm.NewVec3(2, 3, 4)
v3.AddX(1)
v3.AddY(2)
v3.AddZ(3)
if !v3.Eq(ans3) {
t.Errorf("Got: %v; Expected: %v", v3.String(), ans3.String())
}
v3 = gglm.NewVec3(1, 1, 1)
v3.AddR(1)
v3.AddG(2)
v3.AddB(3)
if !v3.Eq(ans3) {
t.Errorf("Got: %v; Expected: %v", v3.String(), ans3.String())
}
v3 = gglm.NewVec3(1, 1, 1)
ans3 = gglm.NewVec3(2, 3, 1)
v3.AddXY(1, 2)
if !v3.Eq(ans3) {
t.Errorf("Got: %v; Expected: %v", v3.String(), ans3.String())
}
v3 = gglm.NewVec3(1, 1, 1)
v3.AddRG(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(2, 3, 4)
v3.AddXYZ(1, 2, 3)
if !v3.Eq(ans3) {
t.Errorf("Got: %v; Expected: %v", v3.String(), ans3.String())
}
v3 = gglm.NewVec3(1, 1, 1)
v3.AddRGB(1, 2, 3)
if !v3.Eq(ans3) {
t.Errorf("Got: %v; Expected: %v", v3.String(), ans3.String())
}
//Vec4
v4 := gglm.NewVec4(1, 1, 1, 1)
ans4 := gglm.NewVec4(2, 3, 4, 5)
v4.AddX(1)
v4.AddY(2)
v4.AddZ(3)
v4.AddW(4)
if !v4.Eq(ans4) {
t.Errorf("Got: %v; Expected: %v", v4.String(), ans4.String())
}
v4 = gglm.NewVec4(1, 1, 1, 1)
v4.AddR(1)
v4.AddG(2)
v4.AddB(3)
v4.AddA(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(2, 3, 1, 1)
v4.AddXY(1, 2)
if !v4.Eq(ans4) {
t.Errorf("Got: %v; Expected: %v", v4.String(), ans4.String())
}
v4 = gglm.NewVec4(1, 1, 1, 1)
v4.AddRG(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(2, 3, 4, 1)
v4.AddXYZ(1, 2, 3)
if !v4.Eq(ans4) {
t.Errorf("Got: %v; Expected: %v", v4.String(), ans4.String())
}
v4 = gglm.NewVec4(1, 1, 1, 1)
v4.AddRGB(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(2, 3, 4, 5)
v4.AddXYZW(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)
v4.AddRGBA(1, 2, 3, 4)
if !v4.Eq(ans4) {
t.Errorf("Got: %v; Expected: %v", v4.String(), ans4.String())
}
}

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())