6 Commits

Author SHA1 Message Date
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
9 changed files with 553 additions and 12 deletions

1
.gitignore vendored
View File

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

View File

@ -1,8 +1,52 @@
# 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:
- `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 'siwzzle' 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
## Installation
`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
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))
}
```
## Notes
You can check compiler inlining decisions using `go run -gcflags "-m" .`. Some functions look a bit weird compared to similar ones
because we are trying to reduce function complexity so the compiler inlines.

View File

@ -16,7 +16,7 @@ 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 {
@ -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

@ -6,6 +6,9 @@ type Swizzle1 interface {
SetX(float32)
SetR(float32)
AddX(float32)
AddR(float32)
}
type Swizzle2 interface {
@ -15,6 +18,12 @@ type Swizzle2 interface {
SetY(float32)
SetG(float32)
AddY(float32)
AddG(float32)
AddXY(float32, float32)
AddRG(float32, float32)
}
type Swizzle3 interface {
@ -24,6 +33,12 @@ type Swizzle3 interface {
SetZ(float32)
SetB(float32)
AddZ(float32)
AddB(float32)
AddXYZ(float32, float32, float32)
AddRGB(float32, float32, float32)
}
type Swizzle4 interface {
@ -33,4 +48,10 @@ type Swizzle4 interface {
SetW(float32)
SetA(float32)
AddW(float32)
AddA(float32)
AddXYZW(float32, float32, float32, float32)
AddRGBA(float32, float32, float32, float32)
}

View File

@ -30,20 +30,46 @@ 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) AddXY(x, y float32) {
v.Data[0] += x
v.Data[1] += y
}
func (v *Vec2) AddRG(r, g float32) {
v.Data[0] += r
v.Data[1] += g
}
func (v *Vec2) String() string {

View File

@ -60,6 +60,52 @@ 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) AddXY(x, y float32) {
v.Data[0] += x
v.Data[1] += y
}
func (v *Vec3) AddRG(r, g float32) {
v.Data[0] += r
v.Data[1] += g
}
func (v *Vec3) AddXYZ(x, y, z float32) {
v.Data[0] += x
v.Data[1] += y
v.Data[2] += z
}
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())
}

View File

@ -76,6 +76,74 @@ 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) AddXY(x, y float32) {
v.Data[0] += x
v.Data[1] += y
}
func (v *Vec4) AddRG(r, g float32) {
v.Data[0] += r
v.Data[1] += g
}
func (v *Vec4) AddXYZ(x, y, z float32) {
v.Data[0] += x
v.Data[1] += y
v.Data[2] += z
}
func (v *Vec4) AddRGB(r, g, b float32) {
v.Data[0] += r
v.Data[1] += g
v.Data[2] += b
}
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) 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())
}

302
gglm/vec_test.go Executable file
View File

@ -0,0 +1,302 @@
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(0, 0)
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())
}
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())
}
//Vec3
v3 := gglm.NewVec3(0, 0, 0)
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())
}
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())
}
//Vec4
v4 := gglm.NewVec4(0, 0, 0, 0)
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())
}
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())
}
}
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())
}
}