24 Commits

Author SHA1 Message Date
6adefa81f7 Add Clamp 2024-05-14 06:06:00 +04:00
b9faa2e59e New NewTrMatXYZ 2024-05-05 00:27:05 +04:00
3eb372dec3 Remove more pointers+better NewXYZ funcs 2024-05-05 00:24:44 +04:00
e6bf7aee10 Remove pointers from .Col() 2024-05-04 23:30:40 +04:00
fa25c1f551 Update workflow name 2024-05-04 22:59:30 +04:00
9efe1a98b1 Update readme 2024-05-04 22:54:40 +04:00
81c90ca4e9 Fix workflow 2024-05-04 22:51:38 +04:00
90d3e8e870 Fix workflow 2024-05-04 22:49:58 +04:00
5d2cfa0329 NewXYZ funcs for Quat+github workflows 2024-05-04 22:44:48 +04:00
afb3bbfe75 Get rid of pointer returns when creating new objects 2024-05-04 22:21:41 +04:00
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
26 changed files with 1754 additions and 314 deletions

21
.github/workflows/test-gglm.yml vendored Executable file
View File

@ -0,0 +1,21 @@
name: "Build and Tests"
on:
create:
workflow_dispatch:
jobs:
test-gglm-windows:
runs-on: [windows-latest]
steps:
- name: Install golang
uses: actions/setup-go@v3
with:
go-version: ">=1.17"
- name: Clone gglm
run: git clone https://github.com/bloeys/gglm
- name: Test gglm
working-directory: gglm
run: go test ./... -v

View File

@ -1,9 +1,12 @@
# gglm
Fast OpenGL/Graphics focused Mathematics library in Go inspired by the c++ library [glm](https://github.com/g-truc/glm).
[![Tests](https://github.com/bloeys/gglm/actions/workflows/test-gglm.yml/badge.svg)](https://github.com/bloeys/gglm/actions/workflows/test-gglm.yml)
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,31 +14,24 @@ 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
Note: gglm requires Go 1.18 or higher.
`go get github.com/bloeys/gglm`
## 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 +39,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.
You can check compiler inlining decisions using `go run -gcflags "-m" .`. Some functions look a bit weird
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

@ -18,8 +18,8 @@ func DotQuat(q1, q2 *Quat) float32 {
return q1.X()*q2.X() + q1.Y()*q2.Y() + q1.Z()*q2.Z() + q1.W()*q2.W()
}
func Cross(v1, v2 *Vec3) *Vec3 {
return &Vec3{
func Cross(v1, v2 *Vec3) Vec3 {
return Vec3{
Data: [3]float32{
v1.Data[1]*v2.Data[2] - v1.Data[2]*v2.Data[1],
v1.Data[2]*v2.Data[0] - v1.Data[0]*v2.Data[2],
@ -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,15 +78,15 @@ 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
func ReflectVec2(v, n *Vec2) *Vec2 {
// Note: n must be normalized or you will get wrong results
func ReflectVec2(v, n *Vec2) Vec2 {
//reflectedVec = v 2*dot(v, norm)*norm
d := 2 * (v.Data[0]*n.Data[0] + v.Data[1]*n.Data[1])
return &Vec2{
return Vec2{
Data: [2]float32{
v.Data[0] - d*n.Data[0],
v.Data[1] - d*n.Data[1],
@ -94,15 +94,15 @@ 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
func ReflectVec3(v, n *Vec3) *Vec3 {
// Note: n must be normalized or you will get wrong results
func ReflectVec3(v, n *Vec3) Vec3 {
//reflectedVec = v 2*dot(v, norm)*norm
d := 2 * (v.Data[0]*n.Data[0] + v.Data[1]*n.Data[1] + v.Data[2]*n.Data[2])
return &Vec3{
return Vec3{
Data: [3]float32{
v.Data[0] - d*n.Data[0],
v.Data[1] - d*n.Data[1],
@ -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

@ -9,8 +9,8 @@ import (
var (
dotVec2Result, distVec2Result float32
dotVec3Result, distVec3Result float32
reflectVec2Result *gglm.Vec2
crossResult, reflectVec3Result *gglm.Vec3
reflectVec2Result gglm.Vec2
crossResult, reflectVec3Result gglm.Vec3
)
func TestDotVec2(t *testing.T) {

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,15 +24,15 @@ 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])
}
func (m *Mat2) Col(c int) *Vec2 {
return &Vec2{Data: m.Data[c]}
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,17 +75,47 @@ 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 AddMat2(m1, m2 *Mat2) *Mat2 {
return &Mat2{
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{
{
m1.Data[0][0] + m2.Data[0][0],
@ -99,9 +129,9 @@ func AddMat2(m1, m2 *Mat2) *Mat2 {
}
}
//SubMat2 m3 = m1 - m2
func SubMat2(m1, m2 *Mat2) *Mat2 {
return &Mat2{
// SubMat2 m3 = m1 - m2
func SubMat2(m1, m2 *Mat2) Mat2 {
return Mat2{
Data: [2][2]float32{
{
m1.Data[0][0] - m2.Data[0][0],
@ -115,9 +145,9 @@ func SubMat2(m1, m2 *Mat2) *Mat2 {
}
}
//MulMat2 m3 = m1 * m2
func MulMat2(m1, m2 *Mat2) *Mat2 {
return &Mat2{
// MulMat2 m3 = m1 * m2
func MulMat2(m1, m2 *Mat2) Mat2 {
return Mat2{
Data: [2][2]float32{
{
m1.Data[0][0]*m2.Data[0][0] + m1.Data[1][0]*m2.Data[0][1],
@ -131,9 +161,9 @@ func MulMat2(m1, m2 *Mat2) *Mat2 {
}
}
//MulMat2Vec2 v2 = m1 * v1
func MulMat2Vec2(m1 *Mat2, v1 *Vec2) *Vec2 {
return &Vec2{
// MulMat2Vec2 v2 = m1 * v1
func MulMat2Vec2(m1 *Mat2, v1 *Vec2) Vec2 {
return Vec2{
Data: [2]float32{
m1.Data[0][0]*v1.Data[0] + m1.Data[1][0]*v1.Data[1],
m1.Data[0][1]*v1.Data[0] + m1.Data[1][1]*v1.Data[1],
@ -141,12 +171,48 @@ func MulMat2Vec2(m1 *Mat2, v1 *Vec2) *Vec2 {
}
}
//NewMat2Id returns the 2x2 identity matrix
func NewMat2Id() *Mat2 {
return &Mat2{
// NewMat2Id returns the 2x2 identity matrix
func NewMat2Id() Mat2 {
return Mat2{
Data: [2][2]float32{
{1, 0},
{0, 1},
},
}
}
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,12 +147,84 @@ 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.Data = [2][2]float32{
{00, 01},
{10, 11},
}
ans.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.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.Data = [2][2]float32{
{1, 8},
{5, 31},
}
ans.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()
m2 := gglm.NewMat2Id()
for i := 0; i < b.N; i++ {
m1.Mul(m2)
m1.Mul(&m2)
}
}

View File

@ -31,11 +31,11 @@ func (m *Mat3) String() string {
)
}
func (m *Mat3) Col(c int) *Vec3 {
return &Vec3{Data: m.Data[c]}
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,17 +126,87 @@ 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 AddMat3(m1, m2 *Mat3) *Mat3 {
return &Mat3{
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{
{
m1.Data[0][0] + m2.Data[0][0],
@ -157,9 +227,9 @@ func AddMat3(m1, m2 *Mat3) *Mat3 {
}
}
//SubMat3 m3 = m1 - m2
func SubMat3(m1, m2 *Mat3) *Mat3 {
return &Mat3{
// SubMat3 m3 = m1 - m2
func SubMat3(m1, m2 *Mat3) Mat3 {
return Mat3{
Data: [3][3]float32{
{
m1.Data[0][0] - m2.Data[0][0],
@ -180,8 +250,8 @@ func SubMat3(m1, m2 *Mat3) *Mat3 {
}
}
//MulMat3 m3 = m1 * m2
func MulMat3(m1, m2 *Mat3) *Mat3 {
// MulMat3 m3 = m1 * m2
func MulMat3(m1, m2 *Mat3) Mat3 {
m00 := m1.Data[0][0]
m01 := m1.Data[0][1]
@ -195,7 +265,7 @@ func MulMat3(m1, m2 *Mat3) *Mat3 {
m21 := m1.Data[2][1]
m22 := m1.Data[2][2]
return &Mat3{
return Mat3{
Data: [3][3]float32{
{
m00*m2.Data[0][0] + m10*m2.Data[0][1] + m20*m2.Data[0][2],
@ -216,9 +286,9 @@ func MulMat3(m1, m2 *Mat3) *Mat3 {
}
}
//MulMat3Vec3 v2 = m1 * v1
func MulMat3Vec3(m1 *Mat3, v1 *Vec3) *Vec3 {
return &Vec3{
// MulMat3Vec3 v2 = m1 * v1
func MulMat3Vec3(m1 *Mat3, v1 *Vec3) Vec3 {
return Vec3{
Data: [3]float32{
m1.Data[0][0]*v1.Data[0] + m1.Data[1][0]*v1.Data[1] + m1.Data[2][0]*v1.Data[2],
m1.Data[0][1]*v1.Data[0] + m1.Data[1][1]*v1.Data[1] + m1.Data[2][1]*v1.Data[2],
@ -227,9 +297,9 @@ func MulMat3Vec3(m1 *Mat3, v1 *Vec3) *Vec3 {
}
}
//NewMat3Id returns the 3x3 identity matrix
func NewMat3Id() *Mat3 {
return &Mat3{
// NewMat3Id returns the 3x3 identity matrix
func NewMat3Id() Mat3 {
return Mat3{
Data: [3][3]float32{
{1, 0, 0},
{0, 1, 0},
@ -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,12 +162,89 @@ 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.Data = [3][3]float32{
{00, 01, 02},
{10, 11, 12},
{20, 21, 22},
}
ans.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.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.Data = [3][3]float32{
{1, 8, 2},
{5, 3, 5},
{9, 6, 7},
}
ans.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()
m2 := gglm.NewMat3Id()
for i := 0; i < b.N; i++ {
m1.Mul(m2)
m1.Mul(&m2)
}
}

View File

@ -32,11 +32,11 @@ func (m *Mat4) String() string {
)
}
func (m *Mat4) Col(c int) *Vec4 {
return &Vec4{Data: m.Data[c]}
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,9 +180,323 @@ func (m *Mat4) Eq(m2 *Mat4) bool {
return m.Data == m2.Data
}
//AddMat4 m3 = m1 + m2
func AddMat4(m1, m2 *Mat4) *Mat4 {
return &Mat4{
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{
{
m1.Data[0][0] + m2.Data[0][0],
@ -212,9 +526,9 @@ func AddMat4(m1, m2 *Mat4) *Mat4 {
}
}
//SubMat4 m3 = m1 - m2
func SubMat4(m1, m2 *Mat4) *Mat4 {
return &Mat4{
// SubMat4 m3 = m1 - m2
func SubMat4(m1, m2 *Mat4) Mat4 {
return Mat4{
Data: [4][4]float32{
{
m1.Data[0][0] - m2.Data[0][0],
@ -244,8 +558,8 @@ func SubMat4(m1, m2 *Mat4) *Mat4 {
}
}
//MulMat4 m3 = m1 * m2
func MulMat4(m1, m2 *Mat4) *Mat4 {
// MulMat4 m3 = m1 * m2
func MulMat4(m1, m2 *Mat4) Mat4 {
m00 := m1.Data[0][0]
m01 := m1.Data[0][1]
@ -267,7 +581,7 @@ func MulMat4(m1, m2 *Mat4) *Mat4 {
m32 := m1.Data[3][2]
m33 := m1.Data[3][3]
return &Mat4{
return Mat4{
Data: [4][4]float32{
{
m00*m2.Data[0][0] + m10*m2.Data[0][1] + m20*m2.Data[0][2] + m30*m2.Data[0][3],
@ -297,9 +611,9 @@ func MulMat4(m1, m2 *Mat4) *Mat4 {
}
}
//MulMat4Vec4 v2 = m1 * v1
func MulMat4Vec4(m1 *Mat4, v1 *Vec4) *Vec4 {
return &Vec4{
// MulMat4Vec4 v2 = m1 * v1
func MulMat4Vec4(m1 *Mat4, v1 *Vec4) Vec4 {
return Vec4{
Data: [4]float32{
m1.Data[0][0]*v1.Data[0] + m1.Data[1][0]*v1.Data[1] + m1.Data[2][0]*v1.Data[2] + m1.Data[3][0]*v1.Data[3],
m1.Data[0][1]*v1.Data[0] + m1.Data[1][1]*v1.Data[1] + m1.Data[2][1]*v1.Data[2] + m1.Data[3][1]*v1.Data[3],
@ -309,9 +623,9 @@ func MulMat4Vec4(m1 *Mat4, v1 *Vec4) *Vec4 {
}
}
//NewMat4Id returns the 4x4 identity matrix
func NewMat4Id() *Mat4 {
return &Mat4{
// NewMat4Id returns the 4x4 identity matrix
func NewMat4Id() Mat4 {
return Mat4{
Data: [4][4]float32{
{1, 0, 0, 0},
{0, 1, 0, 0},
@ -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

@ -7,7 +7,7 @@ import (
)
var (
mulMat4Vec4Res *gglm.Vec4
mulMat4Vec4Res gglm.Vec4
)
func TestMat4GetSet(t *testing.T) {
@ -182,13 +182,124 @@ 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.Data = [4][4]float32{
{00, 01, 02, 03},
{10, 11, 12, 13},
{20, 21, 22, 23},
{30, 31, 32, 33},
}
ans.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.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.Data = [4][4]float32{
{1, 0, 2, 1},
{2, 1, 3, 0},
{3, 0, 4, 1},
{4, 1, 5, 1},
}
ans.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()
ans.Transpose()
if !m.InvertAndTranspose().Eq(&ans) {
t.Errorf("Got: %v; Expected: %v", m.String(), ans.String())
}
m.Data = [4][4]float32{
{1, 0, 2, 1},
{2, 1, 3, 0},
{3, 0, 4, 1},
{4, 1, 5, 1},
}
ans.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()
m2 := gglm.NewMat4Id()
for i := 0; i < b.N; i++ {
m1.Mul(m2)
m1.Mul(&m2)
}
}
@ -198,6 +309,17 @@ func BenchmarkMulMat4Vec4(b *testing.B) {
v1 := gglm.Vec4{}
for i := 0; i < b.N; i++ {
mulMat4Vec4Res = gglm.MulMat4Vec4(m1, &v1)
mulMat4Vec4Res = gglm.MulMat4Vec4(&m1, &v1)
}
}
var mat4InvertRes *gglm.Mat4
func BenchmarkMat4Invert(b *testing.B) {
m1 := gglm.NewMat4Id()
for i := 0; i < b.N; i++ {
mat4InvertRes = m1.Invert()
}
}

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,30 +31,36 @@ func (q *Quat) Angle() float32 {
return Acos32(q.Data[3]) * 2
}
//Axis returns the rotation axis represented by this quaternion
func (q *Quat) Axis() *Vec3 {
// Axis returns the rotation axis represented by this quaternion
func (q *Quat) Axis() Vec3 {
var t float32 = 1 - q.Data[3]*q.Data[3]
if t <= 0 {
return &Vec3{Data: [3]float32{0, 0, 1}}
return Vec3{Data: [3]float32{0, 0, 1}}
}
t = 1 / Sqrt32(t)
return &Vec3{Data: [3]float32{
return Vec3{Data: [3]float32{
q.Data[0] * t,
q.Data[1] * t,
q.Data[2] * t,
}}
}
//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 {
// NewQuatEulerVec takes rotations in radians and produces a rotation that
// rotates around the z-axis, y-axis and lastly x-axis.
func NewQuatEulerVec(v *Vec3) Quat {
return NewQuatEuler(v.X(), v.Y(), v.Z())
}
// NewQuatEuler takes rotations in radians and produces a rotation that
// rotates around the z-axis, y-axis and lastly x-axis.
func NewQuatEuler(x, y, z float32) Quat {
//Some other common terminology: x=roll, y=pitch, z=yaw
sinX, cosX := Sincos32(v.Data[0] * 0.5)
sinY, cosY := Sincos32(v.Data[1] * 0.5)
sinZ, cosZ := Sincos32(v.Data[2] * 0.5)
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
@ -62,7 +68,7 @@ func NewQuatEuler(v *Vec3) *Quat {
//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{
return Quat{
Vec4: Vec4{
Data: [4]float32{
sinX*cosY*cosZ - cosX*sinY*sinZ,
@ -74,26 +80,55 @@ func NewQuatEuler(v *Vec3) *Quat {
}
}
//AngleAxis rotates rotRad radians around the *normalized* vector rotAxisNorm
func NewQuatAngleAxis(rotRad float32, rotAxisNorm *Vec3) *Quat {
// NewQuatAngleAxisVec produces a quaternion thats rotates rotRad radians around the *normalized* vector rotAxisNorm
func NewQuatAngleAxisVec(rotRad float32, rotAxisNorm *Vec3) Quat {
return NewQuatAngleAxis(rotRad, rotAxisNorm.X(), rotAxisNorm.Y(), rotAxisNorm.Z())
}
// NewQuatAngleAxis produces a quaternion thats rotates rotRad radians around the *normalized* vector rotAxisNorm
func NewQuatAngleAxis(rotRad float32, rotAxisNormX, rotAxisNormY, rotAxisNormZ float32) Quat {
s, c := Sincos32(rotRad * 0.5)
return &Quat{
return Quat{
Vec4: Vec4{
Data: [4]float32{
rotAxisNorm.Data[0] * s,
rotAxisNorm.Data[1] * s,
rotAxisNorm.Data[2] * s,
rotAxisNormX * s,
rotAxisNormY * s,
rotAxisNormZ * s,
c,
},
},
}
}
func NewQuatId() *Quat {
return &Quat{
func NewQuatId() Quat {
return Quat{
Vec4: Vec4{
Data: [4]float32{0, 0, 0, 1},
},
}
}
func NewQuat(x, y, z, w float32) Quat {
return Quat{
Vec4: Vec4{
Data: [4]float32{x, y, z, w},
},
}
}
func NewQuatArr(arr [4]float32) Quat {
return Quat{
Vec4: Vec4{
Data: arr,
},
}
}
func NewQuatVec(v *Vec4) Quat {
return Quat{
Vec4: Vec4{
Data: v.Data,
},
}
}

View File

@ -8,8 +8,16 @@ import (
func TestNewQuatEuler(t *testing.T) {
q := gglm.NewQuatEuler(gglm.NewVec3(180, 180, 180).AsRad())
ans := &gglm.Quat{Vec4: *gglm.NewVec4(0, 0, 0, 1)}
degs := gglm.NewVec3(180, 180, 180)
degs.Data = degs.AsRad().Data
q := gglm.NewQuatEulerVec(&degs)
ans := &gglm.Quat{Vec4: gglm.NewVec4(0, 0, 0, 1)}
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.NewQuatEuler(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())
@ -18,8 +26,9 @@ func TestNewQuatEuler(t *testing.T) {
func TestNewQuatAngleAxis(t *testing.T) {
q := gglm.NewQuatAngleAxis(180*gglm.Deg2Rad, gglm.NewVec3(0, 1, 0))
ans := &gglm.Quat{Vec4: *gglm.NewVec4(0, 1, 0, 0)}
rotAxis := gglm.NewVec3(0, 1, 0)
q := gglm.NewQuatAngleAxisVec(180*gglm.Deg2Rad, &rotAxis)
ans := &gglm.Quat{Vec4: gglm.NewVec4(0, 1, 0, 0)}
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())
@ -28,21 +37,27 @@ func TestNewQuatAngleAxis(t *testing.T) {
func TestQuatAngle(t *testing.T) {
a := gglm.NewQuatAngleAxis(180*gglm.Deg2Rad, gglm.NewVec3(0, 1, 0)).Angle()
rotAxis := gglm.NewVec3(0, 1, 0)
quat := gglm.NewQuatAngleAxisVec(180*gglm.Deg2Rad, &rotAxis)
a := quat.Angle()
var ans float32 = 180.0 * gglm.Deg2Rad
if !gglm.EqF32(a, ans) {
t.Errorf("Got: %v; Expected: %v", a, ans)
}
a = gglm.NewQuatAngleAxis(90*gglm.Deg2Rad, gglm.NewVec3(1, 1, 0).Normalize()).Angle()
rotAxis = gglm.NewVec3(1, 1, 0)
quat = gglm.NewQuatAngleAxisVec(90*gglm.Deg2Rad, rotAxis.Normalize())
a = quat.Angle()
ans = 90 * gglm.Deg2Rad
if !gglm.EqF32(a, ans) {
t.Errorf("Got: %v; Expected: %v", a, ans)
}
a = gglm.NewQuatAngleAxis(125*gglm.Deg2Rad, gglm.NewVec3(1, 1, 0).Normalize()).Angle()
rotAxis = gglm.NewVec3(1, 1, 0)
quat = gglm.NewQuatAngleAxisVec(125*gglm.Deg2Rad, rotAxis.Normalize())
a = quat.Angle()
ans = 125 * gglm.Deg2Rad
if !gglm.EqF32(a, ans) {
@ -52,22 +67,30 @@ func TestQuatAngle(t *testing.T) {
func TestQuatAxis(t *testing.T) {
a := gglm.NewQuatAngleAxis(1, gglm.NewVec3(0, 1, 0)).Axis()
rotAxis := gglm.NewVec3(0, 1, 0)
quat := gglm.NewQuatAngleAxisVec(1, &rotAxis)
a := quat.Axis()
ans := gglm.NewVec3(0, 1, 0)
if !gglm.EqF32(a.X(), ans.X()) || !gglm.EqF32(a.Y(), ans.Y()) || !gglm.EqF32(a.Z(), ans.Z()) {
t.Errorf("Got: %v; Expected: %v", a.String(), ans.String())
}
a = gglm.NewQuatAngleAxis(1, gglm.NewVec3(1, 1, 0).Normalize()).Axis()
ans = gglm.NewVec3(1, 1, 0).Normalize()
rotAxis = gglm.NewVec3(1, 1, 0)
quat = gglm.NewQuatAngleAxisVec(1, rotAxis.Normalize())
a = quat.Axis()
ans = gglm.NewVec3(1, 1, 0)
ans.Normalize()
if !gglm.EqF32(a.X(), ans.X()) || !gglm.EqF32(a.Y(), ans.Y()) || !gglm.EqF32(a.Z(), ans.Z()) {
t.Errorf("Got: %v; Expected: %v", a.String(), ans.String())
}
a = gglm.NewQuatAngleAxis(1, gglm.NewVec3(67, 46, 32).Normalize()).Axis()
ans = gglm.NewVec3(67, 46, 32).Normalize()
rotAxis = gglm.NewVec3(67, 46, 32)
quat = gglm.NewQuatAngleAxisVec(1, rotAxis.Normalize())
a = quat.Axis()
ans = gglm.NewVec3(67, 46, 32)
ans.Normalize()
if !gglm.EqF32(a.X(), ans.X()) || !gglm.EqF32(a.Y(), ans.Y()) || !gglm.EqF32(a.Z(), ans.Z()) {
t.Errorf("Got: %v; Expected: %v", a.String(), ans.String())

View File

@ -1,13 +1,17 @@
package gglm
import "math"
import (
"math"
//EqF32 true if abs(f1-f2) <= F32Epsilon
"golang.org/x/exp/constraints"
)
// 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)
}
@ -52,3 +56,21 @@ func Abs32(x float32) float32 {
func Sqrt32(x float32) float32 {
return float32(math.Sqrt(float64(x)))
}
// Clamp returns:
//
// min if x<min
// max if x>max
// x if x>=min && x<=max
func Clamp[T constraints.Ordered](x, min, max T) T {
if x < min {
return min
}
if x > max {
return max
}
return x
}

64
gglm/scalar_test.go Executable file
View File

@ -0,0 +1,64 @@
package gglm_test
import (
"testing"
"github.com/bloeys/gglm/gglm"
)
func TestClamp(t *testing.T) {
x := 5
ans := 5
if gglm.Clamp(x, 0, 10) != ans {
t.Errorf("Got: %v; Expected: %v", x, ans)
}
x = 10
ans = 10
if gglm.Clamp(x, 0, 10) != ans {
t.Errorf("Got: %v; Expected: %v", x, ans)
}
x = 20
ans = 10
if gglm.Clamp(x, 0, 10) != ans {
t.Errorf("Got: %v; Expected: %v", x, ans)
}
x = -10
ans = 0
if gglm.Clamp(x, 0, 10) != ans {
t.Errorf("Got: %v; Expected: %v", x, ans)
}
xf := 1.5
ansf := 1.5
if gglm.Clamp(xf, 0, 10) != ansf {
t.Errorf("Got: %v; Expected: %v", xf, ansf)
}
xf = 15
ansf = 10
if gglm.Clamp(xf, 0, 10) != ansf {
t.Errorf("Got: %v; Expected: %v", xf, ansf)
}
xf = -1.5
ansf = 0
if gglm.Clamp(xf, 0, 10) != ansf {
t.Errorf("Got: %v; Expected: %v", xf, ansf)
}
xf = 2
ansf = 1.5
if gglm.Clamp(xf, 0.5, 1.5) != ansf {
t.Errorf("Got: %v; Expected: %v", xf, ansf)
}
xf = 1.2
ansf = 1.2
if gglm.Clamp(xf, 0.5, 1.5) != ansf {
t.Errorf("Got: %v; Expected: %v", xf, ansf)
}
}

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,29 +8,41 @@ 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
func (t *TrMat) Translate(v *Vec3) *TrMat {
t.Data[3][0] += v.Data[0]
t.Data[3][1] += v.Data[1]
t.Data[3][2] += v.Data[2]
// TranslateVec adds v to the translation components of the transformation matrix
func (t *TrMat) TranslateVec(v *Vec3) *TrMat {
return t.Translate(v.X(), v.Y(), v.Z())
}
// Translate adds v to the translation components of the transformation matrix
func (t *TrMat) Translate(x, y, z float32) *TrMat {
t.Data[3][0] += x
t.Data[3][1] += y
t.Data[3][2] += z
return t
}
//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]
t.Data[2][2] *= v.Data[2]
// ScaleVec multiplies the scale components of the transformation matrix by v
func (t *TrMat) ScaleVec(v *Vec3) *TrMat {
return t.Scale(v.X(), v.Y(), v.Z())
}
// Scale multiplies the scale components of the transformation matrix by v
func (t *TrMat) Scale(x, y, z float32) *TrMat {
t.Data[0][0] *= z
t.Data[1][1] *= y
t.Data[2][2] *= z
return t
}
//Rotate takes a *normalized* axis and angles in radians to rotate around the given axis
func (t *TrMat) Rotate(rads float32, axis *Vec3) *TrMat {
// RotateVec takes a *normalized* axis and angles in radians to rotate around the given axis
func (t *TrMat) RotateVec(rads float32, axis *Vec3) *TrMat {
// Manually inlined
s := Sin32(rads)
c := Cos32(rads)
@ -51,20 +63,85 @@ func (t *TrMat) Rotate(rads float32, axis *Vec3) *TrMat {
rotate.Data[2][1] = temp.Data[2]*axis.Data[1] - s*axis.Data[0]
rotate.Data[2][2] = c + temp.Data[2]*axis.Data[2]
result := &Mat4{}
result.Data[0] = t.Col(0).Scale(rotate.Data[0][0]).
Add(t.Col(1).Scale(rotate.Data[0][1])).
Add(t.Col(2).Scale(rotate.Data[0][2])).
result := Mat4{}
col0 := t.Col(0)
col1 := t.Col(1)
col2 := t.Col(2)
result.Data[0] = col0.Scale(rotate.Data[0][0]).
Add(col1.Scale(rotate.Data[0][1])).
Add(col2.Scale(rotate.Data[0][2])).
Data
result.Data[1] = t.Col(0).Scale(rotate.Data[1][0]).
Add(t.Col(1).Scale(rotate.Data[1][1])).
Add(t.Col(2).Scale(rotate.Data[1][2])).
col0 = t.Col(0)
col1 = t.Col(1)
col2 = t.Col(2)
result.Data[1] = col0.Scale(rotate.Data[1][0]).
Add(col1.Scale(rotate.Data[1][1])).
Add(col2.Scale(rotate.Data[1][2])).
Data
result.Data[2] = t.Col(0).Scale(rotate.Data[2][0]).
Add(t.Col(1).Scale(rotate.Data[2][1])).
Add(t.Col(2).Scale(rotate.Data[2][2])).
col0 = t.Col(0)
col1 = t.Col(1)
col2 = t.Col(2)
result.Data[2] = col0.Scale(rotate.Data[2][0]).
Add(col1.Scale(rotate.Data[2][1])).
Add(col2.Scale(rotate.Data[2][2])).
Data
t.Data[0] = result.Data[0]
t.Data[1] = result.Data[1]
t.Data[2] = result.Data[2]
return t
}
// Rotate takes a *normalized* axis and angles in radians to rotate around the given axis
func (t *TrMat) Rotate(rads float32, axisX, axisY, axisZ float32) *TrMat {
s := Sin32(rads)
c := Cos32(rads)
axis := NewVec3(axisX, axisY, axisZ)
axis.Normalize()
temp := axis.Clone().Scale(1 - c)
rotate := TrMat{}
rotate.Data[0][0] = c + temp.Data[0]*axis.Data[0]
rotate.Data[0][1] = temp.Data[0]*axis.Data[1] + s*axis.Data[2]
rotate.Data[0][2] = temp.Data[0]*axis.Data[2] - s*axis.Data[1]
rotate.Data[1][0] = temp.Data[1]*axis.Data[0] - s*axis.Data[2]
rotate.Data[1][1] = c + temp.Data[1]*axis.Data[1]
rotate.Data[1][2] = temp.Data[1]*axis.Data[2] + s*axis.Data[0]
rotate.Data[2][0] = temp.Data[2]*axis.Data[0] + s*axis.Data[1]
rotate.Data[2][1] = temp.Data[2]*axis.Data[1] - s*axis.Data[0]
rotate.Data[2][2] = c + temp.Data[2]*axis.Data[2]
result := Mat4{}
col0 := t.Col(0)
col1 := t.Col(1)
col2 := t.Col(2)
result.Data[0] = col0.Scale(rotate.Data[0][0]).
Add(col1.Scale(rotate.Data[0][1])).
Add(col2.Scale(rotate.Data[0][2])).
Data
col0 = t.Col(0)
col1 = t.Col(1)
col2 = t.Col(2)
result.Data[1] = col0.Scale(rotate.Data[1][0]).
Add(col1.Scale(rotate.Data[1][1])).
Add(col2.Scale(rotate.Data[1][2])).
Data
col0 = t.Col(0)
col1 = t.Col(1)
col2 = t.Col(2)
result.Data[2] = col0.Scale(rotate.Data[2][0]).
Add(col1.Scale(rotate.Data[2][1])).
Add(col2.Scale(rotate.Data[2][2])).
Data
t.Data[0] = result.Data[0]
@ -88,8 +165,8 @@ func (t *TrMat) Clone() *TrMat {
}
}
func NewTranslationMat(v *Vec3) *TrMat {
return &TrMat{
func NewTranslationMatVec(v *Vec3) TrMat {
return TrMat{
Mat4: Mat4{
Data: [4][4]float32{
{1, 0, 0, 0},
@ -101,8 +178,21 @@ func NewTranslationMat(v *Vec3) *TrMat {
}
}
func NewScaleMat(v *Vec3) *TrMat {
return &TrMat{
func NewTranslationMat(posX, posY, posZ float32) TrMat {
return TrMat{
Mat4: Mat4{
Data: [4][4]float32{
{1, 0, 0, 0},
{0, 1, 0, 0},
{0, 0, 1, 0},
{posX, posY, posZ, 1},
},
},
}
}
func NewScaleMatVec(v *Vec3) TrMat {
return TrMat{
Mat4: Mat4{
Data: [4][4]float32{
{v.Data[0], 0, 0, 0},
@ -114,7 +204,20 @@ func NewScaleMat(v *Vec3) *TrMat {
}
}
func NewRotMat(q *Quat) *TrMat {
func NewScaleMat(scaleX, scaleY, scaleZ float32) TrMat {
return TrMat{
Mat4: Mat4{
Data: [4][4]float32{
{scaleX, 0, 0, 0},
{0, scaleY, 0, 0},
{0, 0, scaleZ, 0},
{0, 0, 0, 1},
},
},
}
}
func NewRotMatQuat(q *Quat) TrMat {
//Based on: https://www.weizmann.ac.il/sci-tea/benari/sites/sci-tea.benari/files/uploads/softwareAndLearningMaterials/quaternion-tutorial-2-0-1.pdf
//Note: in the reference p0,p1,p2,p3 == w,x,y,z
@ -133,7 +236,7 @@ func NewRotMat(q *Quat) *TrMat {
zw := q.Data[2] * q.Data[3]
return &TrMat{
return TrMat{
Mat4: Mat4{
Data: [4][4]float32{
{2*(ww+xx) - 1, 2 * (zw + xy), 2 * (xz - yw), 0},
@ -145,28 +248,54 @@ 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(worldUp, forward).Normalize()
up := Cross(forward, right)
forward := SubVec3(targetPos, pos)
forward.Normalize()
right := Cross(&forward, worldUp)
right.Normalize()
up := Cross(&right, &forward)
return &TrMat{
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},
{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},
},
},
}
}
//Perspective creates a perspective projection matrix
func Perspective(fov, aspectRatio, nearClip, farClip float32) *Mat4 {
// 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)
forward.Normalize()
right := Cross(worldUp, &forward)
right.Normalize()
up := Cross(&forward, &right)
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},
},
},
}
}
// Perspective creates a perspective projection matrix
func Perspective(fov, aspectRatio, nearClip, farClip float32) Mat4 {
halfFovTan := float32(math.Tan(float64(fov * 0.5)))
return &Mat4{
return Mat4{
Data: [4][4]float32{
{1 / (aspectRatio * halfFovTan), 0, 0, 0},
{0, 1 / halfFovTan, 0, 0},
@ -176,9 +305,9 @@ func Perspective(fov, aspectRatio, nearClip, farClip float32) *Mat4 {
}
}
//Perspective creates an orthographic projection matrix
func Ortho(left, right, top, bottom, nearClip, farClip float32) *TrMat {
return &TrMat{
// Perspective creates an orthographic projection matrix
func Ortho(left, right, top, bottom, nearClip, farClip float32) TrMat {
return TrMat{
Mat4: Mat4{
Data: [4][4]float32{
{2 / (right - left), 0, 0, 0},
@ -190,8 +319,26 @@ func Ortho(left, right, top, bottom, nearClip, farClip float32) *TrMat {
}
}
func NewTrMatId() *TrMat {
return &TrMat{
Mat4: *NewMat4Id(),
func NewTrMatId() TrMat {
return TrMat{
Mat4: NewMat4Id(),
}
}
func NewTrMatWithPos(x, y, z float32) TrMat {
tr := TrMat{
Mat4: NewMat4Id(),
}
tr.Translate(x, y, z)
return tr
}
func NewTrMatWithPosVec(pos *Vec3) TrMat {
tr := TrMat{
Mat4: NewMat4Id(),
}
tr.TranslateVec(pos)
return tr
}

View File

@ -27,7 +27,7 @@ func TestNewTrMatId(t *testing.T) {
func TestNewTranslationMat(t *testing.T) {
m := gglm.NewTranslationMat(gglm.NewVec3(1, 2, 3))
m := gglm.NewTranslationMat(1, 2, 3)
ans := &gglm.TrMat{
Mat4: gglm.Mat4{
Data: [4][4]float32{
@ -46,7 +46,7 @@ func TestNewTranslationMat(t *testing.T) {
func TestNewScaleMat(t *testing.T) {
m := gglm.NewScaleMat(gglm.NewVec3(1, 2, 3))
m := gglm.NewScaleMat(1, 2, 3)
ans := &gglm.TrMat{
Mat4: gglm.Mat4{
Data: [4][4]float32{
@ -65,7 +65,8 @@ func TestNewScaleMat(t *testing.T) {
func TestNewRotMat(t *testing.T) {
m := gglm.NewRotMat(gglm.NewQuatId())
quat := gglm.NewQuatId()
m := gglm.NewRotMatQuat(&quat)
ans := &gglm.TrMat{
Mat4: gglm.Mat4{
Data: [4][4]float32{

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,9 +150,9 @@ func (v *Vec2) Clone() *Vec2 {
return &Vec2{Data: v.Data}
}
//AddVec2 v3 = v1 + v2
func AddVec2(v1, v2 *Vec2) *Vec2 {
return &Vec2{
// AddVec2 v3 = v1 + v2
func AddVec2(v1, v2 *Vec2) Vec2 {
return Vec2{
Data: [2]float32{
v1.X() + v2.X(),
v1.Y() + v2.Y(),
@ -136,9 +160,9 @@ func AddVec2(v1, v2 *Vec2) *Vec2 {
}
}
//SubVec2 v3 = v1 - v2
func SubVec2(v1, v2 *Vec2) *Vec2 {
return &Vec2{
// SubVec2 v3 = v1 - v2
func SubVec2(v1, v2 *Vec2) Vec2 {
return Vec2{
Data: [2]float32{
v1.X() - v2.X(),
v1.Y() - v2.Y(),
@ -146,11 +170,17 @@ func SubVec2(v1, v2 *Vec2) *Vec2 {
}
}
func NewVec2(x, y float32) *Vec2 {
return &Vec2{
func NewVec2(x, y float32) Vec2 {
return Vec2{
[2]float32{
x,
y,
},
}
}
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,31 @@ 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)
t3.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,9 +237,9 @@ func (v *Vec3) AsRad() *Vec3 {
}
}
//AddVec3 v3 = v1 + v2
func AddVec3(v1, v2 *Vec3) *Vec3 {
return &Vec3{
// AddVec3 v3 = v1 + v2
func AddVec3(v1, v2 *Vec3) Vec3 {
return Vec3{
Data: [3]float32{
v1.X() + v2.X(),
v1.Y() + v2.Y(),
@ -190,9 +248,9 @@ func AddVec3(v1, v2 *Vec3) *Vec3 {
}
}
//SubVec3 v3 = v1 - v2
func SubVec3(v1, v2 *Vec3) *Vec3 {
return &Vec3{
// SubVec3 v3 = v1 - v2
func SubVec3(v1, v2 *Vec3) Vec3 {
return Vec3{
Data: [3]float32{
v1.X() - v2.X(),
v1.Y() - v2.Y(),
@ -201,8 +259,8 @@ func SubVec3(v1, v2 *Vec3) *Vec3 {
}
}
func NewVec3(x, y, z float32) *Vec3 {
return &Vec3{
func NewVec3(x, y, z float32) Vec3 {
return Vec3{
[3]float32{
x,
y,
@ -210,3 +268,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,9 +261,9 @@ func (v *Vec4) Clone() *Vec4 {
return &Vec4{Data: v.Data}
}
//AddVec4 v3 = v1 + v2
func AddVec4(v1, v2 *Vec4) *Vec4 {
return &Vec4{
// AddVec4 v3 = v1 + v2
func AddVec4(v1, v2 *Vec4) Vec4 {
return Vec4{
Data: [4]float32{
v1.X() + v2.X(),
v1.Y() + v2.Y(),
@ -219,9 +273,9 @@ func AddVec4(v1, v2 *Vec4) *Vec4 {
}
}
//SubVec4 v3 = v1 - v2
func SubVec4(v1, v2 *Vec4) *Vec4 {
return &Vec4{
// SubVec4 v3 = v1 - v2
func SubVec4(v1, v2 *Vec4) Vec4 {
return Vec4{
Data: [4]float32{
v1.X() - v2.X(),
v1.Y() - v2.Y(),
@ -231,8 +285,8 @@ func SubVec4(v1, v2 *Vec4) *Vec4 {
}
}
func NewVec4(x, y, z, w float32) *Vec4 {
return &Vec4{
func NewVec4(x, y, z, w float32) Vec4 {
return Vec4{
[4]float32{
x,
y,
@ -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,48 +102,122 @@ 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) {
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())
}
if !v2.Eq(ans2) {
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)
v3.SetY(2)
v3.SetZ(3)
if !v3.Eq(ans3) {
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) {
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)
rotAxis := gglm.NewVec3(0, 1, 0)
quat := gglm.NewQuatAngleAxisVec(90*gglm.Deg2Rad, &rotAxis)
v32.RotByQuat(&quat)
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)
@ -151,10 +225,11 @@ func TestVecSwizzleSet(t *testing.T) {
v4.SetZ(3)
v4.SetW(4)
if !v4.Eq(ans4) {
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)
@ -162,7 +237,55 @@ func TestVecSwizzleSet(t *testing.T) {
v4.SetB(33)
v4.SetA(44)
if !v4.Eq(ans4) {
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())
}
}
@ -174,26 +297,26 @@ func TestVecSwizzleAdd(t *testing.T) {
ans2 := gglm.NewVec2(2, 3)
v2.AddX(1)
v2.AddY(2)
if !v2.Eq(ans2) {
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) {
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) {
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) {
if !v2.Eq(&ans2) {
t.Errorf("Got: %v; Expected: %v", v2.String(), ans2.String())
}
@ -203,7 +326,7 @@ func TestVecSwizzleAdd(t *testing.T) {
v3.AddX(1)
v3.AddY(2)
v3.AddZ(3)
if !v3.Eq(ans3) {
if !v3.Eq(&ans3) {
t.Errorf("Got: %v; Expected: %v", v3.String(), ans3.String())
}
@ -211,33 +334,33 @@ func TestVecSwizzleAdd(t *testing.T) {
v3.AddR(1)
v3.AddG(2)
v3.AddB(3)
if !v3.Eq(ans3) {
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) {
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) {
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) {
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) {
if !v3.Eq(&ans3) {
t.Errorf("Got: %v; Expected: %v", v3.String(), ans3.String())
}
@ -248,7 +371,7 @@ func TestVecSwizzleAdd(t *testing.T) {
v4.AddY(2)
v4.AddZ(3)
v4.AddW(4)
if !v4.Eq(ans4) {
if !v4.Eq(&ans4) {
t.Errorf("Got: %v; Expected: %v", v4.String(), ans4.String())
}
@ -257,46 +380,46 @@ func TestVecSwizzleAdd(t *testing.T) {
v4.AddG(2)
v4.AddB(3)
v4.AddA(4)
if !v4.Eq(ans4) {
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) {
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) {
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) {
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) {
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) {
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) {
if !v4.Eq(&ans4) {
t.Errorf("Got: %v; Expected: %v", v4.String(), ans4.String())
}
}

4
go.mod
View File

@ -1,3 +1,5 @@
module github.com/bloeys/gglm
go 1.17
go 1.18
require golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842

2
go.sum
View File

@ -0,0 +1,2 @@
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=

84
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},
@ -53,9 +53,9 @@ func main() {
m4.Mul(m5)
println(m4.String())
println(m6.String())
println(m4.Eq(m6))
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))
@ -79,13 +82,15 @@ func main() {
println(v3.Eq(v4))
println(gglm.DotVec3(v3, v4))
println(gglm.Cross(v3, v4).String())
v3v4Cross := gglm.Cross(v3, v4)
println(v3v4Cross.String())
println("V3: " + v3.String())
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 +102,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},
@ -110,9 +119,10 @@ func main() {
}
vec2A := gglm.Vec2{Data: [2]float32{1, 2}}
println(gglm.MulMat2Vec2(&mat2A, &vec2A).String())
mat2Vec2Mul := gglm.MulMat2Vec2(&mat2A, &vec2A)
println(mat2Vec2Mul.String())
//Mat3Vec3
// Mat3Vec3
mat3A := gglm.Mat3{
Data: [3][3]float32{
{1, 4, 7},
@ -125,58 +135,68 @@ 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())
q := gglm.NewQuatEulerVec(vRot.AsRad())
println("\n" + vRot.AsRad().String())
println(q.String(), "\n", q.Mag())
q = gglm.NewQuatAngleAxis(60*gglm.Deg2Rad, vRot.Normalize())
q = gglm.NewQuatAngleAxisVec(60*gglm.Deg2Rad, vRot.Normalize())
println("\n" + vRot.Normalize().String())
println(q.String())
//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))
// Transform
translationMat := gglm.NewTranslationMatVec(&gglm.Vec3{Data: [3]float32{1, 2, 3}})
rotDegs := gglm.NewVec3(60, 30, 20)
quat := gglm.NewQuatEulerVec(rotDegs.AsRad())
rotMat := gglm.NewRotMatQuat(&quat)
scaleMat := gglm.NewScaleMat(1, 1, 1)
modelMat := gglm.NewTrMatId()
modelMat.Mul(translationMat.Mul(rotMat.Mul(scaleMat)))
modelMat.Mul(translationMat.Mul(rotMat.Mul(&scaleMat)))
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
trMatOrig := gglm.NewTranslationMat(gglm.NewVec3(1, 2, 3))
// Clone TrMat
trMatOrig := gglm.NewTranslationMat(1, 2, 3)
trMatClone := trMatOrig.Clone()
trMatClone.Scale(gglm.NewVec3(2, 2, 2))
trMatClone.Translate(gglm.NewVec3(9, 0, 0))
trMatCloneScale := gglm.NewVec3(2, 2, 2)
trMatClone.ScaleVec(&trMatCloneScale)
trMatClone.Translate(9, 0, 0)
println("\n\n", trMatOrig.String(), "; ", trMatClone.String())
//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)
// Quat geo
q1Degs := gglm.NewVec3(180*gglm.Deg2Rad, 0, 0)
q1 := gglm.NewQuatEulerVec(&q1Degs)
//LookAt
q2Degs := gglm.NewVec3(0, 180*gglm.Deg2Rad, 0)
q2 := gglm.NewQuatEulerVec(&q2Degs)
println(gglm.AngleQuat(&q1, &q2) * gglm.Rad2Deg)
// 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())
@ -193,5 +213,5 @@ func main() {
{2, 4},
}}
println(mc2.Mul(mc).String())
println(mc2.Mul(&mc).String())
}