3 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
8 changed files with 240 additions and 42 deletions

View File

@ -20,6 +20,8 @@ gglm currently has the following:
## Installation
Note: gglm requires Go 1.18 or higher.
`go get github.com/bloeys/gglm`
## Usage
@ -54,5 +56,5 @@ func main() {
## Notes
You can check compiler inlining decisions using `go run -gcflags "-m" .`. Some functions look a bit weird compared to similar ones
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

@ -1,6 +1,10 @@
package gglm
import "math"
import (
"math"
"golang.org/x/exp/constraints"
)
// EqF32 true if abs(f1-f2) <= F32Epsilon
func EqF32(f1, f2 float32) bool {
@ -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

@ -13,24 +13,36 @@ type TrMat struct {
Mat4
}
// 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(v *Vec3) *TrMat {
t.Data[3][0] += v.Data[0]
t.Data[3][1] += v.Data[1]
t.Data[3][2] += v.Data[2]
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
}
// 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(v *Vec3) *TrMat {
t.Data[0][0] *= v.Data[0]
t.Data[1][1] *= v.Data[1]
t.Data[2][2] *= v.Data[2]
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)
@ -83,6 +95,61 @@ func (t *TrMat) Rotate(rads float32, axis *Vec3) *TrMat {
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]
t.Data[1] = result.Data[1]
t.Data[2] = result.Data[2]
return t
}
func (t *TrMat) Mul(m *TrMat) *TrMat {
t.Mat4.Mul(&m.Mat4)
return t
@ -98,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},
@ -111,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},
@ -124,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
@ -143,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},
@ -157,7 +250,7 @@ func NewRotMat(q *Quat) *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 {
func LookAtRH(pos, targetPos, worldUp *Vec3) TrMat {
forward := SubVec3(targetPos, pos)
forward.Normalize()
@ -165,7 +258,7 @@ func LookAtRH(pos, targetPos, worldUp *Vec3) *TrMat {
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},
@ -179,7 +272,7 @@ func LookAtRH(pos, targetPos, worldUp *Vec3) *TrMat {
// LookAtLH does a left-handed coordinate system lookAt.
// Can be used to create the view matrix
func LookAtLH(pos, targetPos, worldUp *Vec3) *TrMat {
func LookAtLH(pos, targetPos, worldUp *Vec3) TrMat {
forward := SubVec3(targetPos, pos)
forward.Normalize()
@ -187,7 +280,7 @@ func LookAtLH(pos, targetPos, worldUp *Vec3) *TrMat {
right.Normalize()
up := Cross(&forward, &right)
return &TrMat{
return TrMat{
Mat4: Mat4{
Data: [4][4]float32{
{right.Data[0], up.Data[0], forward.Data[0], 0},
@ -200,9 +293,9 @@ func LookAtLH(pos, targetPos, worldUp *Vec3) *TrMat {
}
// Perspective creates a perspective projection matrix
func Perspective(fov, aspectRatio, nearClip, farClip float32) *Mat4 {
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},
@ -213,8 +306,8 @@ 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{
func Ortho(left, right, top, bottom, nearClip, farClip float32) TrMat {
return TrMat{
Mat4: Mat4{
Data: [4][4]float32{
{2 / (right - left), 0, 0, 0},
@ -231,3 +324,21 @@ func NewTrMatId() 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,8 +27,7 @@ func TestNewTrMatId(t *testing.T) {
func TestNewTranslationMat(t *testing.T) {
pos := gglm.NewVec3(1, 2, 3)
m := gglm.NewTranslationMat(&pos)
m := gglm.NewTranslationMat(1, 2, 3)
ans := &gglm.TrMat{
Mat4: gglm.Mat4{
Data: [4][4]float32{
@ -47,8 +46,7 @@ func TestNewTranslationMat(t *testing.T) {
func TestNewScaleMat(t *testing.T) {
pos := gglm.NewVec3(1, 2, 3)
m := gglm.NewScaleMat(&pos)
m := gglm.NewScaleMat(1, 2, 3)
ans := &gglm.TrMat{
Mat4: gglm.Mat4{
Data: [4][4]float32{
@ -68,7 +66,7 @@ func TestNewScaleMat(t *testing.T) {
func TestNewRotMat(t *testing.T) {
quat := gglm.NewQuatId()
m := gglm.NewRotMat(&quat)
m := gglm.NewRotMatQuat(&quat)
ans := &gglm.TrMat{
Mat4: gglm.Mat4{
Data: [4][4]float32{

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=

17
main.go
View File

@ -152,17 +152,16 @@ func main() {
println(q.String())
// Transform
translationMat := gglm.NewTranslationMat(&gglm.Vec3{Data: [3]float32{1, 2, 3}})
translationMat := gglm.NewTranslationMatVec(&gglm.Vec3{Data: [3]float32{1, 2, 3}})
rotDegs := gglm.NewVec3(60, 30, 20)
quat := gglm.NewQuatEulerVec(rotDegs.AsRad())
rotMat := gglm.NewRotMat(&quat)
rotMat := gglm.NewRotMatQuat(&quat)
scale := gglm.NewVec3(1, 1, 1)
scaleMat := gglm.NewScaleMat(&scale)
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())
@ -173,15 +172,13 @@ func main() {
println("\n\n", v2Orig.String(), "; ", v2Clone.String())
// Clone TrMat
pos := gglm.NewVec3(1, 2, 3)
trMatOrig := gglm.NewTranslationMat(&pos)
trMatOrig := gglm.NewTranslationMat(1, 2, 3)
trMatClone := trMatOrig.Clone()
trMatCloneScale := gglm.NewVec3(2, 2, 2)
trMatClone.Scale(&trMatCloneScale)
trMatClone.ScaleVec(&trMatCloneScale)
pos = gglm.NewVec3(9, 0, 0)
trMatClone.Translate(&pos)
trMatClone.Translate(9, 0, 0)
println("\n\n", trMatOrig.String(), "; ", trMatClone.String())
// Quat geo