10 Commits

Author SHA1 Message Date
f4f06c54b3 Make TrMat funcs chainable+more 32 scalar funcs 2022-01-13 17:51:04 +04:00
80d1c12e2d Update readme 2022-01-13 16:30:17 +04:00
8bb31393b4 Quat axis and angle tests 2022-01-13 16:22:38 +04:00
e4edb7dcec Angle and axis methods for quat 2022-01-13 16:06:13 +04:00
d832e19dab Rotate and Col functions 2022-01-13 15:49:30 +04:00
b39e8e0b80 Actually correct ortho 2021-11-15 09:53:43 +04:00
585d17aa29 Fix ortho func 2021-11-13 23:27:21 +04:00
d4c7755ca8 More benchmarks 2021-11-09 10:28:23 +04:00
2071b351d5 Move benchmarks into relevant test files 2021-11-09 10:19:42 +04:00
300c699e65 Add Ortho func 2021-11-09 10:13:58 +04:00
14 changed files with 305 additions and 95 deletions

View File

@ -1,6 +1,6 @@
# gglm
Fast Go OpenGL Mathematics library inspired by the c++ library [glm](https://github.com/g-truc/glm).
Fast Go OpenGL/Graphics focused Mathematics library inspired by the c++ library [glm](https://github.com/g-truc/glm).
## Notes

View File

@ -1,7 +1,11 @@
package gglm
const (
Pi float32 = 3.14159265359
Deg2Rad float32 = Pi / 180
Rad2Deg float32 = 180 / Pi
Pi float32 = 3.14159265359
Deg2Rad float32 = Pi / 180
Rad2Deg float32 = 180 / Pi
F32Epsilon float32 = 1e-6
//CosHalf is Cos32(0.5)
CosHalf float32 = 0.87758256189
)

View File

@ -6,6 +6,13 @@ import (
"github.com/bloeys/gglm/gglm"
)
var (
dotVec2Result, distVec2Result float32
dotVec3Result, distVec3Result float32
reflectVec2Result *gglm.Vec2
crossResult, reflectVec3Result *gglm.Vec3
)
func TestDotVec2(t *testing.T) {
v1 := gglm.Vec2{Data: [2]float32{1, 2}}
@ -137,3 +144,73 @@ func TestReflectVec3(t *testing.T) {
t.Errorf("Got: %v; Expected: %v", res, ans)
}
}
func BenchmarkDotVec2(b *testing.B) {
v1 := &gglm.Vec2{}
v2 := &gglm.Vec2{}
for i := 0; i < b.N; i++ {
dotVec2Result = gglm.DotVec2(v1, v2)
}
}
func BenchmarkDotVec3(b *testing.B) {
v1 := &gglm.Vec3{}
v2 := &gglm.Vec3{}
for i := 0; i < b.N; i++ {
dotVec3Result = gglm.DotVec3(v1, v2)
}
}
func BenchmarkCross(b *testing.B) {
v1 := &gglm.Vec3{}
v2 := &gglm.Vec3{}
for i := 0; i < b.N; i++ {
crossResult = gglm.Cross(v1, v2)
}
}
func BenchmarkDistVec2(b *testing.B) {
v1 := &gglm.Vec2{}
v2 := &gglm.Vec2{}
for i := 0; i < b.N; i++ {
distVec2Result = gglm.DistVec2(v1, v2)
}
}
func BenchmarkDistVec3(b *testing.B) {
v1 := &gglm.Vec3{}
v2 := &gglm.Vec3{}
for i := 0; i < b.N; i++ {
distVec3Result = gglm.DistVec3(v1, v2)
}
}
func BenchmarkReflectVec2(b *testing.B) {
v1 := &gglm.Vec2{}
v2 := &gglm.Vec2{}
for i := 0; i < b.N; i++ {
reflectVec2Result = gglm.ReflectVec2(v1, v2)
}
}
func BenchmarkReflectVec3(b *testing.B) {
v1 := &gglm.Vec3{}
v2 := &gglm.Vec3{}
for i := 0; i < b.N; i++ {
reflectVec3Result = gglm.ReflectVec3(v1, v2)
}
}

View File

@ -28,6 +28,10 @@ func (m *Mat2) String() string {
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]}
}
//Add m += m2
func (m *Mat2) Add(m2 *Mat2) *Mat2 {
m.Data[0][0] += m2.Data[0][0]

View File

@ -146,3 +146,13 @@ func TestMulMat2Vec2(t *testing.T) {
t.Errorf("Got: %v; Expected: %v", result.String(), correctAns.String())
}
}
func BenchmarkMulMat2(b *testing.B) {
m1 := gglm.NewMat2Id()
m2 := gglm.NewMat2Id()
for i := 0; i < b.N; i++ {
m1.Mul(m2)
}
}

View File

@ -31,6 +31,10 @@ func (m *Mat3) String() string {
)
}
func (m *Mat3) Col(c int) *Vec3 {
return &Vec3{Data: m.Data[c]}
}
//Add m += m2
func (m *Mat3) Add(m2 *Mat3) *Mat3 {

View File

@ -161,3 +161,13 @@ func TestMulMat3Vec3(t *testing.T) {
t.Errorf("Got: %v; Expected: %v", result.String(), correctAns.String())
}
}
func BenchmarkMulMat3(b *testing.B) {
m1 := gglm.NewMat3Id()
m2 := gglm.NewMat3Id()
for i := 0; i < b.N; i++ {
m1.Mul(m2)
}
}

View File

@ -32,6 +32,10 @@ func (m *Mat4) String() string {
)
}
func (m *Mat4) Col(c int) *Vec4 {
return &Vec4{Data: m.Data[c]}
}
//Add m += m2
func (m *Mat4) Add(m2 *Mat4) *Mat4 {

View File

@ -6,6 +6,10 @@ import (
"github.com/bloeys/gglm/gglm"
)
var (
mulMat4Vec4Res *gglm.Vec4
)
func TestMat4GetSet(t *testing.T) {
m1 := gglm.Mat4{}
@ -177,3 +181,23 @@ func TestMulMat4Vec4(t *testing.T) {
t.Errorf("Got: %v; Expected: %v", result.String(), correctAns.String())
}
}
func BenchmarkMulMat4(b *testing.B) {
m1 := gglm.NewMat4Id()
m2 := gglm.NewMat4Id()
for i := 0; i < b.N; i++ {
m1.Mul(m2)
}
}
func BenchmarkMulMat4Vec4(b *testing.B) {
m1 := gglm.NewMat4Id()
v1 := gglm.Vec4{}
for i := 0; i < b.N; i++ {
mulMat4Vec4Res = gglm.MulMat4Vec4(m1, &v1)
}
}

View File

@ -16,6 +16,37 @@ func (q *Quat) Eq(q2 *Quat) bool {
return q.Data == q2.Data
}
//Angle returns the angle represented by this quaternion
func (q *Quat) Angle() float32 {
if Abs32(q.Data[3]) > CosHalf {
a := Asin32(Sqrt32(q.Data[0]*q.Data[0]+q.Data[1]*q.Data[1]+q.Data[2]*q.Data[2])) * 2
if q.Data[3] < 0 {
return Pi*2 - a
}
return a
}
return Acos32(q.Data[3]) * 2
}
//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}}
}
t = 1 / Sqrt32(t)
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 {

View File

@ -25,3 +25,51 @@ func TestNewQuatAngleAxis(t *testing.T) {
t.Errorf("Got: %v; Expected: %v", q.String(), ans.String())
}
}
func TestQuatAngle(t *testing.T) {
a := gglm.NewQuatAngleAxis(180*gglm.Deg2Rad, gglm.NewVec3(0, 1, 0)).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()
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()
ans = 125 * gglm.Deg2Rad
if !gglm.EqF32(a, ans) {
t.Errorf("Got: %v; Expected: %v", a, ans)
}
}
func TestQuatAxis(t *testing.T) {
a := gglm.NewQuatAngleAxis(1, gglm.NewVec3(0, 1, 0)).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()
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()
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

@ -2,9 +2,6 @@ package gglm
import "math"
//F32Epsilon = 0.0000005
const F32Epsilon float32 = 1e-6
//EqF32 true if abs(f1-f2) <= F32Epsilon
func EqF32(f1, f2 float32) bool {
return math.Abs(float64(f1-f2)) <= float64(F32Epsilon)
@ -31,7 +28,27 @@ func Acos32(x float32) float32 {
return float32(math.Acos(float64(x)))
}
func Tan32(x float32) float32 {
return float32(math.Tan(float64(x)))
}
func Atan32(x float32) float32 {
return float32(math.Atan(float64(x)))
}
func Atan232(x, y float32) float32 {
return float32(math.Atan2(float64(y), float64(x)))
}
func Sincos32(x float32) (sinx, cosx float32) {
a, b := math.Sincos(float64(x))
return float32(a), float32(b)
}
func Abs32(x float32) float32 {
return float32(math.Abs(float64(x)))
}
func Sqrt32(x float32) float32 {
return float32(math.Sqrt(float64(x)))
}

View File

@ -13,18 +13,64 @@ type TrMat struct {
Mat4
}
//Translate adds the vector to the translation components of the transformation matrix
func (t *TrMat) Translate(v *Vec3) {
//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]
return t
}
//Scale multiplies the vector by the scale components of the transformation matrix
func (t *TrMat) Scale(v *Vec3) {
//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]
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 {
s := Sin32(rads)
c := Cos32(rads)
axis = 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{}
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])).
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])).
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])).
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 {
@ -117,6 +163,7 @@ func LookAt(pos, targetPos, worldUp *Vec3) *TrMat {
}
}
//Perspective creates a perspective projection matrix
func Perspective(fov, aspectRatio, nearClip, farClip float32) *Mat4 {
halfFovTan := float32(math.Tan(float64(fov * 0.5)))
return &Mat4{
@ -129,6 +176,20 @@ 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{
Mat4: Mat4{
Data: [4][4]float32{
{2 / (right - left), 0, 0, 0},
{0, 2 / (top - bottom), 0, 0},
{0, 0, -2 / (farClip - nearClip), 0},
{-(right + left) / (right - left), -(top + bottom) / (top - bottom), -(farClip + nearClip) / (farClip - nearClip), 1},
},
},
}
}
func NewTrMatId() *TrMat {
return &TrMat{
Mat4: *NewMat4Id(),

View File

@ -1,84 +0,0 @@
package main
import (
"testing"
"github.com/bloeys/gglm/gglm"
)
var (
dotVec2Result float32
dotVec3Result float32
crossResult *gglm.Vec3
mulMat4Vec4Res *gglm.Vec4
)
func BenchmarkDotVec2(b *testing.B) {
v1 := &gglm.Vec2{}
v2 := &gglm.Vec2{}
for i := 0; i < b.N; i++ {
dotVec2Result = gglm.DotVec2(v1, v2)
}
}
func BenchmarkDotVec3(b *testing.B) {
v1 := &gglm.Vec3{}
v2 := &gglm.Vec3{}
for i := 0; i < b.N; i++ {
dotVec3Result = gglm.DotVec3(v1, v2)
}
}
func BenchmarkCross(b *testing.B) {
v1 := &gglm.Vec3{}
v2 := &gglm.Vec3{}
for i := 0; i < b.N; i++ {
crossResult = gglm.Cross(v1, v2)
}
}
func BenchmarkMulMat2(b *testing.B) {
m1 := gglm.NewMat2Id()
m2 := gglm.NewMat2Id()
for i := 0; i < b.N; i++ {
m1.Mul(m2)
}
}
func BenchmarkMulMat3(b *testing.B) {
m1 := gglm.NewMat3Id()
m2 := gglm.NewMat3Id()
for i := 0; i < b.N; i++ {
m1.Mul(m2)
}
}
func BenchmarkMulMat4(b *testing.B) {
m1 := gglm.NewMat4Id()
m2 := gglm.NewMat4Id()
for i := 0; i < b.N; i++ {
m1.Mul(m2)
}
}
func BenchmarkMulMat4Vec4(b *testing.B) {
m1 := gglm.NewMat4Id()
v1 := gglm.Vec4{}
for i := 0; i < b.N; i++ {
mulMat4Vec4Res = gglm.MulMat4Vec4(m1, &v1)
}
}