From 19731dc0e568b01cc13f21a4bb4741d922d46f97 Mon Sep 17 00:00:00 2001 From: Marvin Blum Date: Tue, 3 May 2016 21:23:50 +0200 Subject: [PATCH] Added geo and gl packages from gogeline and improved documentation. --- game.go | 7 ++ geo/mat3.go | 171 ++++++++++++++++++++++++++++ geo/mat4.go | 255 +++++++++++++++++++++++++++++++++++++++++ geo/util.go | 20 ++++ geo/vec2.go | 62 ++++++++++ geo/vec3.go | 87 ++++++++++++++ geo/vec4.go | 72 ++++++++++++ gl/drop.go | 15 +++ gl/fbo.go | 123 ++++++++++++++++++++ gl/shader.go | 313 +++++++++++++++++++++++++++++++++++++++++++++++++++ gl/tex.go | 132 ++++++++++++++++++++++ gl/types.go | 3 + gl/vao.go | 39 +++++++ gl/vbo.go | 77 +++++++++++++ system.go | 15 ++- 15 files changed, 1388 insertions(+), 3 deletions(-) create mode 100644 geo/mat3.go create mode 100644 geo/mat4.go create mode 100644 geo/util.go create mode 100644 geo/vec2.go create mode 100644 geo/vec3.go create mode 100644 geo/vec4.go create mode 100644 gl/drop.go create mode 100644 gl/fbo.go create mode 100644 gl/shader.go create mode 100644 gl/tex.go create mode 100644 gl/types.go create mode 100644 gl/vao.go create mode 100644 gl/vbo.go diff --git a/game.go b/game.go index ff87417..51f7102 100644 --- a/game.go +++ b/game.go @@ -67,6 +67,7 @@ func Run(game Game, options *RunOptions) { for running { if exitOnClose && wnd.ShouldClose() { + cleanup() return } @@ -87,3 +88,9 @@ func Run(game Game, options *RunOptions) { func Stop() { running = false } + +func cleanup() { + for _, system := range systems { + system.Cleanup() + } +} diff --git a/geo/mat3.go b/geo/mat3.go new file mode 100644 index 0000000..0e7080c --- /dev/null +++ b/geo/mat3.go @@ -0,0 +1,171 @@ +package geo + +import ( + "math" +) + +// 3x3 column major matrix. +type Mat3 struct { + Values [9]float64 +} + +// Creates a new 3x3 matrix with initial values. +func NewMat3(m00, m10, m20, m01, m11, m21, m02, m12, m22 float64) *Mat3 { + return &Mat3{[9]float64{m00, m10, m20, m01, m11, m21, m02, m12, m22}} +} + +// Creates a copy of actual matrix. +func (m *Mat3) Copy() *Mat3 { + return &Mat3{m.Values} +} + +// Sets the matrix to zeros. +func (m *Mat3) Clear() { + for i := range m.Values { + m.Values[i] = 0 + } +} + +// Sets the matrix to identity matrix. +func (m *Mat3) Identity() { + m.Clear() + m.Values[0] = 1 + m.Values[4] = 1 + m.Values[8] = 1 +} + +// Multiplies actual matrix with given matrix and saves result. +func (m *Mat3) Mult(mat *Mat3) { + this := m.Copy() + + m.Values[0] = mat.Values[0]*this.Values[0] + mat.Values[1]*this.Values[3] + mat.Values[2]*this.Values[6] + m.Values[1] = mat.Values[0]*this.Values[1] + mat.Values[1]*this.Values[4] + mat.Values[2]*this.Values[7] + m.Values[2] = mat.Values[0]*this.Values[2] + mat.Values[1]*this.Values[5] + mat.Values[2]*this.Values[8] + + m.Values[3] = mat.Values[3]*this.Values[0] + mat.Values[4]*this.Values[3] + mat.Values[5]*this.Values[6] + m.Values[4] = mat.Values[3]*this.Values[1] + mat.Values[4]*this.Values[4] + mat.Values[5]*this.Values[7] + m.Values[5] = mat.Values[3]*this.Values[2] + mat.Values[4]*this.Values[5] + mat.Values[5]*this.Values[8] + + m.Values[6] = mat.Values[6]*this.Values[0] + mat.Values[7]*this.Values[3] + mat.Values[8]*this.Values[6] + m.Values[7] = mat.Values[6]*this.Values[1] + mat.Values[7]*this.Values[4] + mat.Values[8]*this.Values[7] + m.Values[8] = mat.Values[6]*this.Values[2] + mat.Values[7]*this.Values[5] + mat.Values[8]*this.Values[8] +} + +// Multiplies given vector with actual matrix and returns result. +func (m *Mat3) MultVec(v Vec3) Vec3 { + vec := Vec3{} + + vec.X = m.Values[0]*v.X + m.Values[3]*v.X + m.Values[6]*v.X + vec.Y = m.Values[1]*v.Y + m.Values[4]*v.Y + m.Values[7]*v.Y + vec.Z = m.Values[2]*v.Z + m.Values[5]*v.Z + m.Values[8]*v.Z + + return vec +} + +// Returns the determinate of actual matrix. +func (m *Mat3) Determinate() float64 { + var d float64 + + d = m.Values[0]*m.Values[4]*m.Values[8] + m.Values[3]*m.Values[7]*m.Values[2] + m.Values[6]*m.Values[1]*m.Values[5] + d -= m.Values[2]*m.Values[4]*m.Values[6] - m.Values[5]*m.Values[7]*m.Values[0] - m.Values[8]*m.Values[1]*m.Values[3] + + return d +} + +// Sets the inverse of actual matrix. +func (m *Mat3) Inverse() { + d := 1 / m.Determinate() + mat := m.Copy() + + m.Values[0] = (mat.Values[4]*mat.Values[8] - mat.Values[7]*mat.Values[5]) * d + m.Values[1] = (mat.Values[7]*mat.Values[2] - mat.Values[1]*mat.Values[8]) * d + m.Values[2] = (mat.Values[1]*mat.Values[5] - mat.Values[4]*mat.Values[2]) * d + + m.Values[3] = (mat.Values[6]*mat.Values[5] - mat.Values[3]*mat.Values[8]) * d + m.Values[4] = (mat.Values[0]*mat.Values[8] - mat.Values[6]*mat.Values[2]) * d + m.Values[5] = (mat.Values[3]*mat.Values[2] - mat.Values[0]*mat.Values[5]) * d + + m.Values[6] = (mat.Values[3]*mat.Values[7] - mat.Values[6]*mat.Values[4]) * d + m.Values[7] = (mat.Values[6]*mat.Values[1] - mat.Values[0]*mat.Values[7]) * d + m.Values[8] = (mat.Values[0]*mat.Values[4] - mat.Values[3]*mat.Values[1]) * d +} + +// Calculates and saves the transpose of actual matrix. +func (m *Mat3) Transpose() { + mat := m.Copy() + + m.Values[1] = mat.Values[3] + m.Values[2] = mat.Values[6] + m.Values[3] = mat.Values[1] + + m.Values[5] = mat.Values[7] + m.Values[6] = mat.Values[2] + m.Values[7] = mat.Values[5] +} + +// Translates and saves actual matrix by given vector. +func (m *Mat3) Translate(v Vec2) { + mat := &Mat3{} + mat.Identity() + + mat.Values[6] = v.X + mat.Values[7] = v.Y + + m.Mult(mat) +} + +// Scales and saves actual matrix by given vector. +func (m *Mat3) Scale(v Vec2) { + mat := &Mat3{} + mat.Identity() + + mat.Values[0] = v.X + mat.Values[4] = v.Y + + m.Mult(mat) +} + +// Rotates and saves actual matrix by given vector. +func (m *Mat3) Rotate(angle float64) { + mat := &Mat3{} + var co, si float64 + + angle = angle * (math.Pi / 180) + si = math.Sin(angle) + co = math.Cos(angle) + + mat.Values[0] = co + mat.Values[1] = si + mat.Values[2] = 0 + + mat.Values[3] = -si + mat.Values[4] = co + mat.Values[5] = 0 + + mat.Values[6] = 0 + mat.Values[7] = 0 + mat.Values[8] = 1 + + m.Mult(mat) +} + +// Sets actual matrix to orthogonal projection with given viewport. +func (m *Mat3) Ortho(viewport Vec4) { + if viewport.X != viewport.Z && viewport.Y != viewport.W { + m.Identity() + + m.Values[0] = 2 / (viewport.Z - viewport.X) + m.Values[4] = 2 / (viewport.W - viewport.Y) + m.Values[6] = -(viewport.Z + viewport.X) / (viewport.Z - viewport.X) + m.Values[7] = -(viewport.W + viewport.Y) / (viewport.W - viewport.Y) + m.Values[8] = 1 + } +} + +// Multiplies both matrices and returns a new Mat3. +func MultMat3(a, b *Mat3) *Mat3 { + c := a.Copy() + c.Mult(b) + + return c +} diff --git a/geo/mat4.go b/geo/mat4.go new file mode 100644 index 0000000..ab66255 --- /dev/null +++ b/geo/mat4.go @@ -0,0 +1,255 @@ +package geo + +import ( + "math" +) + +// 4x4 column major matrix. +type Mat4 struct { + Values [16]float64 +} + +// Creates a new 4x4 matrix with initial values. +func NewMat4(m00, m10, m20, m30, m01, m11, m21, m31, m02, m12, m22, m32, m03, m13, m23, m33 float64) *Mat4 { + return &Mat4{[16]float64{m00, m10, m20, m30, m01, m11, m21, m31, m02, m12, m22, m32, m03, m13, m23, m33}} +} + +// Creates a copy of actual matrix. +func (m *Mat4) Copy() *Mat4 { + return &Mat4{m.Values} +} + +// Sets the matrix to zeros. +func (m *Mat4) Clear() { + for i := range m.Values { + m.Values[i] = 0 + } +} + +// Sets the matrix to identity matrix. +func (m *Mat4) Identity() { + m.Clear() + m.Values[0] = 1 + m.Values[5] = 1 + m.Values[10] = 1 + m.Values[15] = 1 +} + +// Multiplies actual matrix with given matrix and saves result. +func (m *Mat4) Mult(mat *Mat4) { + this := m.Copy() + + m.Values[0] = mat.Values[0]*this.Values[0] + mat.Values[1]*this.Values[4] + mat.Values[2]*this.Values[8] + mat.Values[3]*this.Values[12] + m.Values[1] = mat.Values[0]*this.Values[1] + mat.Values[1]*this.Values[5] + mat.Values[2]*this.Values[9] + mat.Values[3]*this.Values[13] + m.Values[2] = mat.Values[0]*this.Values[2] + mat.Values[1]*this.Values[6] + mat.Values[2]*this.Values[10] + mat.Values[3]*this.Values[14] + m.Values[3] = mat.Values[0]*this.Values[3] + mat.Values[1]*this.Values[7] + mat.Values[2]*this.Values[11] + mat.Values[3]*this.Values[15] + + m.Values[4] = mat.Values[4]*this.Values[0] + mat.Values[5]*this.Values[4] + mat.Values[6]*this.Values[8] + mat.Values[7]*this.Values[12] + m.Values[5] = mat.Values[4]*this.Values[1] + mat.Values[5]*this.Values[5] + mat.Values[6]*this.Values[9] + mat.Values[7]*this.Values[13] + m.Values[6] = mat.Values[4]*this.Values[2] + mat.Values[5]*this.Values[6] + mat.Values[6]*this.Values[10] + mat.Values[7]*this.Values[14] + m.Values[7] = mat.Values[4]*this.Values[3] + mat.Values[5]*this.Values[7] + mat.Values[6]*this.Values[11] + mat.Values[7]*this.Values[15] + + m.Values[8] = mat.Values[8]*this.Values[0] + mat.Values[9]*this.Values[4] + mat.Values[10]*this.Values[8] + mat.Values[11]*this.Values[12] + m.Values[9] = mat.Values[8]*this.Values[1] + mat.Values[9]*this.Values[5] + mat.Values[10]*this.Values[9] + mat.Values[11]*this.Values[13] + m.Values[10] = mat.Values[8]*this.Values[2] + mat.Values[9]*this.Values[6] + mat.Values[10]*this.Values[10] + mat.Values[11]*this.Values[14] + m.Values[11] = mat.Values[8]*this.Values[3] + mat.Values[9]*this.Values[7] + mat.Values[10]*this.Values[11] + mat.Values[11]*this.Values[15] + + m.Values[12] = mat.Values[12]*this.Values[0] + mat.Values[13]*this.Values[4] + mat.Values[14]*this.Values[8] + mat.Values[15]*this.Values[12] + m.Values[13] = mat.Values[12]*this.Values[1] + mat.Values[13]*this.Values[5] + mat.Values[14]*this.Values[9] + mat.Values[15]*this.Values[13] + m.Values[14] = mat.Values[12]*this.Values[2] + mat.Values[13]*this.Values[6] + mat.Values[14]*this.Values[10] + mat.Values[15]*this.Values[14] + m.Values[15] = mat.Values[12]*this.Values[3] + mat.Values[13]*this.Values[7] + mat.Values[14]*this.Values[11] + mat.Values[15]*this.Values[15] +} + +// Multiplies given vector with actual matrix and returns result. +func (m *Mat4) MultVec(v Vec3) Vec3 { + vec := Vec3{} + + vec.X = m.Values[0]*v.X + m.Values[4]*v.X + m.Values[8]*v.X + m.Values[12]*v.X + vec.Y = m.Values[1]*v.Y + m.Values[5]*v.Y + m.Values[9]*v.Y + m.Values[13]*v.Y + vec.Z = m.Values[2]*v.Z + m.Values[6]*v.Z + m.Values[10]*v.Z + m.Values[14]*v.Z + + return vec +} + +// Returns the determinate of actual matrix. +func (m *Mat4) Determinate() float64 { + var d float64 + + d = m.Values[0]*m.Values[4]*m.Values[8] + m.Values[1]*m.Values[5]*m.Values[6] + m.Values[2]*m.Values[3]*m.Values[7] + d -= m.Values[2]*m.Values[4]*m.Values[6] + m.Values[0]*m.Values[5]*m.Values[7] + m.Values[1]*m.Values[3]*m.Values[8] + + return d +} + +// Sets the inverse of actual matrix. +func (m *Mat4) Inverse() { + mat := m.Copy() + + m.Values[0] = mat.Values[0] + m.Values[1] = mat.Values[4] + m.Values[2] = mat.Values[8] + m.Values[4] = mat.Values[1] + m.Values[6] = mat.Values[9] + m.Values[8] = mat.Values[2] + m.Values[9] = mat.Values[6] + + m.Values[12] = m.Values[0]*-mat.Values[12] + m.Values[4]*-mat.Values[13] + m.Values[8]*-mat.Values[14] + m.Values[13] = m.Values[1]*-mat.Values[12] + m.Values[5]*-mat.Values[13] + m.Values[9]*-mat.Values[14] + m.Values[14] = m.Values[2]*-mat.Values[12] + m.Values[6]*-mat.Values[13] + m.Values[10]*-mat.Values[14] + + m.Values[3] = 0 + m.Values[7] = 0 + m.Values[11] = 0 + m.Values[15] = 1 +} + +// Calculates and saves the transpose of actual matrix. +func (m *Mat4) Transpose() { + mat := m.Copy() + + m.Values[1] = mat.Values[4] + m.Values[2] = mat.Values[8] + m.Values[3] = mat.Values[12] + + m.Values[4] = mat.Values[1] + m.Values[6] = mat.Values[9] + m.Values[7] = mat.Values[13] + + m.Values[8] = mat.Values[2] + m.Values[9] = mat.Values[6] + m.Values[11] = mat.Values[14] + + m.Values[12] = mat.Values[3] + m.Values[13] = mat.Values[2] + m.Values[14] = mat.Values[11] +} + +// Translates and saves actual matrix by given vector. +func (m *Mat4) Translate(v Vec3) { + mat := Mat4{} + mat.Identity() + + mat.Values[12] = v.X + mat.Values[13] = v.Y + mat.Values[14] = v.Z + + m.Mult(&mat) +} + +// Scales and saves actual matrix by given vector. +func (m *Mat4) Scale(v Vec3) { + mat := Mat4{} + mat.Identity() + + mat.Values[0] = v.X + mat.Values[5] = v.Y + mat.Values[10] = v.Z + + m.Mult(&mat) +} + +// Rotates and saves actual matrix by given vector. +func (m *Mat4) Rotate(angle float64, axis Vec3) { + mat := Mat4{} + var co, si float64 + + axis.Normalize() + angle = angle * (math.Pi / 180) + si = float64(math.Sin(float64(angle))) + co = float64(math.Cos(float64(angle))) + + mat.Values[0] = axis.X*axis.X*(1-co) + co + mat.Values[1] = axis.Y*axis.X*(1-co) + axis.Z*si + mat.Values[2] = axis.X*axis.Z*(1-co) - axis.Y*si + mat.Values[3] = 0 + + mat.Values[4] = axis.X*axis.Y*(1-co) - axis.Z*si + mat.Values[5] = axis.Y*axis.Y*(1-co) + co + mat.Values[6] = axis.Y*axis.Z*(1-co) + axis.X*si + mat.Values[7] = 0 + + mat.Values[8] = axis.X*axis.Z*(1-co) + axis.Y*si + mat.Values[9] = axis.Y*axis.Z*(1-co) - axis.X*si + mat.Values[10] = axis.Z*axis.Z*(1-co) + co + mat.Values[11] = 0 + + mat.Values[12] = 0 + mat.Values[13] = 0 + mat.Values[14] = 0 + mat.Values[15] = 1 + + m.Mult(&mat) +} + +// Sets actual matrix to orthogonal projection with given viewport. +func (m *Mat4) Ortho(viewport Vec4, znear, zfar float64) { + if viewport.X != viewport.Z && viewport.Y != viewport.W && znear != zfar { + m.Identity() + + m.Values[0] = 2 / (viewport.Z - viewport.X) + m.Values[5] = 2 / (viewport.W - viewport.Y) + m.Values[10] = -2 / (zfar - znear) + m.Values[12] = -(viewport.Z + viewport.X) / (viewport.Z - viewport.X) + m.Values[13] = -(viewport.W + viewport.Y) / (viewport.W - viewport.Y) + m.Values[14] = -(zfar + znear) / (zfar - znear) + m.Values[15] = 1 + } +} + +// Sets actual matrix to project to specified locations with given upper axis. +func (m *Mat4) LookAt(pos, lookAt, up Vec3) { + dir := lookAt.Copy() + dir.Sub(pos) + dir.Normalize() + + right := CrossVec3(dir, up) + right.Normalize() + + up = CrossVec3(right, dir) + up.Normalize() + + mat := Mat4{} + + mat.Values[0] = right.X + mat.Values[4] = right.Y + mat.Values[8] = right.Z + mat.Values[12] = -right.DotVec(pos) + + mat.Values[1] = up.X + mat.Values[5] = up.Y + mat.Values[9] = up.Z + mat.Values[13] = -up.DotVec(pos) + + mat.Values[2] = -dir.X + mat.Values[6] = -dir.Y + mat.Values[10] = -dir.Z + mat.Values[14] = dir.DotVec(pos) + + mat.Values[3] = 0 + mat.Values[7] = 0 + mat.Values[11] = 0 + mat.Values[15] = 1 + + m.Mult(&mat) +} + +// Sets actual matrix to perspective projection with given viewport. +func (m *Mat4) Perspective(fov, ratio, znear, zfar float64) { + f := 1 / math.Tan(float64(fov*(math.Pi/360))) + m.Identity() + + m.Values[0] = f / ratio + m.Values[5] = f + m.Values[10] = (zfar + znear) / (znear - zfar) + m.Values[11] = -1 + m.Values[14] = (2 * zfar * znear) / (znear - zfar) + m.Values[15] = 0 +} + +// Multiplies both matrices and returns a new Mat4. +func MultMat4(a, b *Mat4) *Mat4 { + c := a.Copy() + c.Mult(b) + + return c +} diff --git a/geo/util.go b/geo/util.go new file mode 100644 index 0000000..826eae9 --- /dev/null +++ b/geo/util.go @@ -0,0 +1,20 @@ +package geo + +import ( + "math" +) + +// Returns the distance between two 2D vectors. +func DistanceVec2(a, b Vec2) float64 { + return math.Sqrt(float64((a.X-b.X)*(a.X-b.X) + (a.Y-b.Y)*(a.Y-b.Y))) +} + +// Returns the distance between two 3D vectors. +func DistanceVec3(a, b Vec3) float64 { + return math.Sqrt(float64((a.X-b.X)*(a.X-b.X) + (a.Y-b.Y)*(a.Y-b.Y) + (a.Z-b.Z)*(a.Z-b.Z))) +} + +// Returns the distance between two 4D vectors. +func DistanceVec4(a, b Vec4) float64 { + return math.Sqrt(float64((a.X-b.X)*(a.X-b.X) + (a.Y-b.Y)*(a.Y-b.Y) + (a.Z-b.Z)*(a.Z-b.Z) + (a.W-b.W)*(a.W-b.W))) +} diff --git a/geo/vec2.go b/geo/vec2.go new file mode 100644 index 0000000..13fa064 --- /dev/null +++ b/geo/vec2.go @@ -0,0 +1,62 @@ +package geo + +import ( + "math" +) + +// A 2D vector. +type Vec2 struct { + X, Y float64 +} + +// Creates a copy of actual vector. +func (v *Vec2) Copy() Vec2 { + return Vec2{v.X, v.Y} +} + +// Adds and saves the given vector to actual vector. +func (v *Vec2) Add(vec Vec2) { + v.X += vec.X + v.Y += vec.Y +} + +// Subtracts and saves the given vector to actual vector. +func (v *Vec2) Sub(vec Vec2) { + v.X -= vec.X + v.Y -= vec.Y +} + +// Multiplies and saves the given vector to actual vector. +func (v *Vec2) Mult(vec Vec2) { + v.X *= vec.X + v.Y *= vec.Y +} + +// Divides and saves the given vector to actual vector. +func (v *Vec2) Div(vec Vec2) { + v.X /= vec.X + v.Y /= vec.Y +} + +// Calculates and returns the dot product of actual vector. +func (v *Vec2) Dot() float64 { + return v.X*v.X + v.Y*v.Y +} + +// Calculates and returns the dot product of combination of given vector and actual vector. +func (v *Vec2) DotVec(vec Vec2) float64 { + return v.X*vec.X + v.Y*vec.Y +} + +// Returns the length of actual vector. +func (v *Vec2) Length() float64 { + return float64(math.Sqrt(float64(v.Dot()))) +} + +// Normalizes actual vector to length 1. +func (v *Vec2) Normalize() { + l := v.Length() + + v.X /= l + v.Y /= l +} diff --git a/geo/vec3.go b/geo/vec3.go new file mode 100644 index 0000000..f925da6 --- /dev/null +++ b/geo/vec3.go @@ -0,0 +1,87 @@ +package geo + +import ( + "math" +) + +// A 3D vector. +type Vec3 struct { + X, Y, Z float64 +} + +// Creates a copy of actual vector. +func (v *Vec3) Copy() Vec3 { + return Vec3{v.X, v.Y, v.Z} +} + +// Adds and saves the given vector to actual vector. +func (v *Vec3) Add(vec Vec3) { + v.X += vec.X + v.Y += vec.Y + v.Z += vec.Z +} + +// Subtracts and saves the given vector to actual vector. +func (v *Vec3) Sub(vec Vec3) { + v.X -= vec.X + v.Y -= vec.Y + v.Z -= vec.Z +} + +// Multiplies and saves the given vector to actual vector. +func (v *Vec3) Mult(vec Vec3) { + v.X *= vec.X + v.Y *= vec.Y + v.Z *= vec.Z +} + +// Divides and saves the given vector to actual vector. +func (v *Vec3) Div(vec Vec3) { + v.X /= vec.X + v.Y /= vec.Y + v.Z /= vec.Z +} + +// Calculates and returns the dot product of actual vector. +func (v *Vec3) Dot() float64 { + return v.X*v.X + v.Y*v.Y + v.Z*v.Z +} + +// Calculates and returns the dot product of combination of given vector and actual vector. +func (v *Vec3) DotVec(vec Vec3) float64 { + return v.X*vec.X + v.Y*vec.Y + v.Z*vec.Z +} + +// Returns the length of actual vector. +func (v *Vec3) Length() float64 { + return math.Sqrt(v.Dot()) +} + +// Normalizes actual vector to length 1. +func (v *Vec3) Normalize() { + l := v.Length() + + v.X /= l + v.Y /= l + v.Z /= l +} + +// Calculates and saves cross product of given and actual vector. +func (v *Vec3) Cross(vec Vec3) { + this := Vec3{v.X, v.Y, v.Z} + + v.X = this.Y*vec.Z - this.Z*vec.Y + v.Y = this.Z*vec.X - this.X*vec.Z + v.Z = this.X*vec.Y - this.Y*vec.X +} + +// Calulates the cross product of given vectors and returns result as a new vector. +func CrossVec3(a, b Vec3) Vec3 { + vec := Vec3{} + + vec.X = (a.Y * b.Z) - (a.Z * b.Y) + vec.Y = (a.Z * b.X) - (a.X * b.Z) + vec.Z = (a.X * b.Y) - (a.Y * b.X) + + return vec +} diff --git a/geo/vec4.go b/geo/vec4.go new file mode 100644 index 0000000..c5a027d --- /dev/null +++ b/geo/vec4.go @@ -0,0 +1,72 @@ +package geo + +import ( + "math" +) + +// A 4D vector. +type Vec4 struct { + X, Y, Z, W float64 +} + +// Creates a copy of actual vector. +func (v *Vec4) Copy() Vec4 { + return Vec4{v.X, v.Y, v.Z, v.W} +} + +// Adds and saves the given vector to actual vector. +func (v *Vec4) Add(vec Vec4) { + v.X += vec.X + v.Y += vec.Y + v.Z += vec.Z + v.W += vec.W +} + +// Subracts and saves the given vector to actual vector. +func (v *Vec4) Sub(vec Vec4) { + v.X -= vec.X + v.Y -= vec.Y + v.Z -= vec.Z + v.W -= vec.W +} + +// Multiplies and saves the given vector to actual vector. +func (v *Vec4) Mult(vec Vec4) { + v.X *= vec.X + v.Y *= vec.Y + v.Z *= vec.Z + v.W *= vec.W +} + +// Divides and saves the given vector to actual vector. +func (v *Vec4) Div(vec Vec4) { + v.X /= vec.X + v.Y /= vec.Y + v.Z /= vec.Z + v.W /= vec.W +} + +// Calculates and returns the dot product of actual vector. +func (v *Vec4) Dot() float64 { + return v.X*v.X + v.Y*v.Y + v.Z*v.Z + v.W*v.W +} + +// Calculates and returns the dot product of combination of given vector and actual vector. +func (v *Vec4) DotVec(vec Vec4) float64 { + return v.X*vec.X + v.Y*vec.Y + v.Z*vec.Z + v.W*vec.W +} + +// Returns the length of actual vector. +func (v *Vec4) Length() float64 { + return math.Sqrt(v.Dot()) +} + +// Normalizes actual vector to length 1. +func (v *Vec4) Normalize() { + l := v.Length() + + v.X /= l + v.Y /= l + v.Z /= l + v.W /= l +} diff --git a/gl/drop.go b/gl/drop.go new file mode 100644 index 0000000..6e9d6a3 --- /dev/null +++ b/gl/drop.go @@ -0,0 +1,15 @@ +package gl + +// The dropable interface is used to clean up GL objects. +// Use the Drop() function to drop a range of objects. +type Dropable interface { + Drop() +} + +// Drops given GL objects. +// Objects must implement the Dropable inteface. +func Drop(objects []Dropable) { + for _, obj := range objects { + obj.Drop() + } +} diff --git a/gl/fbo.go b/gl/fbo.go new file mode 100644 index 0000000..527e183 --- /dev/null +++ b/gl/fbo.go @@ -0,0 +1,123 @@ +package gl + +import ( + "github.com/go-gl/gl/v4.5-core/gl" +) + +// Frame Buffer Object. +type FBO struct { + id uint32 + target uint32 + attachments []uint32 +} + +// Creates a new FBO with given target. +func NewFBO(target uint32) *FBO { + fbo := &FBO{} + gl.GenFramebuffers(1, &fbo.id) + fbo.target = target + fbo.attachments = make([]uint32, 0) + + return fbo +} + +// Creates a new FBO with given target and 2D texture. +// Used to render to texture. +func NewFBOWithTex2D(width, height, filter int32) (*FBO, *Tex) { + tex := NewTex(gl.TEXTURE_2D) + tex.Bind() + tex.SetDefaultParams(filter) + tex.Texture2D(0, + gl.RGBA, + width, + height, + gl.RGBA, + gl.UNSIGNED_BYTE, + nil) + tex.Unbind() + + fbo := NewFBO(gl.FRAMEBUFFER) + fbo.Bind() + fbo.Texture2D(gl.COLOR_ATTACHMENT0, tex.GetId(), 0) + fbo.Unbind() + + return fbo, tex +} + +// Drops the FBO. +func (f *FBO) Drop() { + gl.DeleteFramebuffers(1, &f.id) +} + +// Binds the FBO for usage (rendered on). +func (f *FBO) Bind() { + gl.BindFramebuffer(f.target, f.id) +} + +// Unbinds. +func (f *FBO) Unbind() { + gl.BindFramebuffer(f.target, 0) +} + +// Sets draw buffer. +func (f *FBO) DrawBuffer(mode uint32) { + gl.DrawBuffer(mode) +} + +// Sets read buffer. +func (f *FBO) ReadBuffer(mode uint32) { + gl.ReadBuffer(mode) +} + +func (f *FBO) DrawBuffers(mode uint32) { + gl.DrawBuffers(int32(len(f.attachments)), &f.attachments[0]) +} + +// Removes all attached textures from FBO. +func (f *FBO) ClearAttachments() { + f.attachments = make([]uint32, 0) +} + +// Attaches a texture. +func (f *FBO) Texture(attachment, texId uint32, level int32) { + f.attachments = append(f.attachments, attachment) + gl.FramebufferTexture(f.target, attachment, texId, level) +} + +// Attaches a 1D texture. +func (f *FBO) Texture1D(attachment, texId uint32, level int32) { + f.attachments = append(f.attachments, attachment) + gl.FramebufferTexture1D(f.target, attachment, gl.TEXTURE_1D, texId, level) +} + +// Attaches a 2D texture. +func (f *FBO) Texture2D(attachment, texId uint32, level int32) { + f.attachments = append(f.attachments, attachment) + gl.FramebufferTexture2D(f.target, attachment, gl.TEXTURE_2D, texId, level) +} + +// Attaches a 3D texture. +func (f *FBO) Texture3D(attachment, texId uint32, level, layer int32) { + f.attachments = append(f.attachments, attachment) + gl.FramebufferTexture3D(f.target, attachment, gl.TEXTURE_3D, texId, level, layer) +} + +// Returns the status of the FBO. +func (f *FBO) GetStatus() uint32 { + return gl.CheckFramebufferStatus(f.target) +} + +// Returns true if FBO is complete, else false. +func (f *FBO) Complete() bool { + return f.GetStatus() == gl.FRAMEBUFFER_COMPLETE +} + +// Returns the GL ID. +func (f *FBO) GetId() uint32 { + return f.id +} + +// Returns the target. +func (f *FBO) GetTarget() uint32 { + return f.target +} diff --git a/gl/shader.go b/gl/shader.go new file mode 100644 index 0000000..beefae2 --- /dev/null +++ b/gl/shader.go @@ -0,0 +1,313 @@ +package gl + +import ( + "errors" + "geo" + "github.com/go-gl/gl/v4.5-core/gl" + "strings" +) + +// Combination of shaders and shader program. +type Shader struct { + program, vertex, fragment uint32 + attrIndex uint32 + attributes []uint32 + uniformMap map[string]int32 + attrMap map[string]int32 +} + +// Creates a new shader program by given vertex and fragment shader source code. +// The shaders itself will be deleted when compiled, only the program ID will be kept. +func NewShader(vertexShader, fragmentShader string) (*Shader, error) { + shader := &Shader{} + shader.attributes = make([]uint32, 0) + shader.uniformMap = make(map[string]int32) + shader.attrMap = make(map[string]int32) + + shader.program = gl.CreateProgram() + shader.vertex = gl.CreateShader(gl.VERTEX_SHADER) + shader.fragment = gl.CreateShader(gl.FRAGMENT_SHADER) + + if err := compileShader(&shader.vertex, vertexShader+NullTerminator); err != nil { + gl.DeleteShader(shader.vertex) + gl.DeleteShader(shader.fragment) + shader.Drop() + + return nil, err + } + + if err := compileShader(&shader.fragment, fragmentShader+NullTerminator); err != nil { + gl.DeleteShader(shader.vertex) + gl.DeleteShader(shader.fragment) + shader.Drop() + + return nil, err + } + + gl.AttachShader(shader.program, shader.vertex) + gl.AttachShader(shader.program, shader.fragment) + + if err := linkProgram(shader.program); err != nil { + gl.DeleteShader(shader.vertex) + gl.DeleteShader(shader.fragment) + shader.Drop() + + return nil, err + } + + // we don't need to keep them in memory + gl.DetachShader(shader.program, shader.vertex) + gl.DetachShader(shader.program, shader.fragment) + gl.DeleteShader(shader.vertex) + gl.DeleteShader(shader.fragment) + + return shader, nil +} + +func compileShader(shader *uint32, source string) error { + csrc := gl.Str(source) + gl.ShaderSource(*shader, 1, &csrc, nil) + gl.CompileShader(*shader) + + if err := shaderCheckError(*shader); err != nil { + return err + } + + return nil +} + +func shaderCheckError(shader uint32) error { + var status int32 + gl.GetShaderiv(shader, gl.COMPILE_STATUS, &status) + + if status == gl.FALSE { + var logLength int32 + gl.GetShaderiv(shader, gl.INFO_LOG_LENGTH, &logLength) + + log := strings.Repeat("\x00", int(logLength+1)) + gl.GetShaderInfoLog(shader, logLength, nil, gl.Str(log)) + + return errors.New("compiler error:\r\n" + log) + } + + return nil +} + +func linkProgram(program uint32) error { + gl.LinkProgram(program) + + var status int32 + gl.GetProgramiv(program, gl.LINK_STATUS, &status) + + if status == gl.FALSE { + var logLength int32 + gl.GetProgramiv(program, gl.INFO_LOG_LENGTH, &logLength) + + log := strings.Repeat("\x00", int(logLength+1)) + gl.GetProgramInfoLog(program, logLength, nil, gl.Str(log)) + + return errors.New("linker error:\r\n" + log) + } + + return nil +} + +// Deletes the GL program object. +func (s *Shader) Drop() { + gl.DeleteProgram(s.program) +} + +// Binds the shader for usage. +func (s *Shader) Bind() { + gl.UseProgram(s.program) +} + +// Unbinds shader. +func (s *Shader) Unbind() { + gl.UseProgram(0) +} + +// Binds an attribute to this shader, name must be present in shader source. +func (s *Shader) BindAttribIndex(name string, index uint32) { + gl.BindAttribLocation(s.program, index, gl.Str(name+NullTerminator)) + s.attributes = append(s.attributes, index) +} + +// Binds an attribute to this shader (with default index), name must be present in shader source. +func (s *Shader) BindAttrib(name string) { + s.BindAttribIndex(name, s.attrIndex) + s.attrIndex++ +} + +// Enables all bound attributes. +func (s *Shader) EnableVertexAttribArrays() { + for i := range s.attributes { + gl.EnableVertexAttribArray(s.attributes[i]) + } +} + +// Disables all bound attributes. +func (s *Shader) DisableVertexAttribArrays() { + for i := range s.attributes { + gl.DisableVertexAttribArray(s.attributes[i]) + } +} + +// Returns the uniform location of a variable. +// The name must be present in the shader source. +func (s *Shader) GetUniformLocation(name string) int32 { + _, exists := s.uniformMap[name] + + if !exists { + s.uniformMap[name] = gl.GetUniformLocation(s.program, gl.Str(name+NullTerminator)) + } + + return s.uniformMap[name] +} + +// Returns the location of an attribute. +// The name must be present in the shader source. +func (s *Shader) GetAttribLocation(name string) int32 { + _, exists := s.attrMap[name] + + if !exists { + s.attrMap[name] = gl.GetAttribLocation(s.program, gl.Str(name+NullTerminator)) + } + + return s.attrMap[name] +} + +func (s *Shader) SendUniform1i(name string, v int32) { + gl.Uniform1i(s.GetUniformLocation(name), v) +} + +func (s *Shader) SendUniform2i(name string, v0, v1 int32) { + gl.Uniform2i(s.GetUniformLocation(name), v0, v1) +} + +func (s *Shader) SendUniform3i(name string, v0, v1, v2 int32) { + gl.Uniform3i(s.GetUniformLocation(name), v0, v1, v2) +} + +func (s *Shader) SendUniform4i(name string, v0, v1, v2, v3 int32) { + gl.Uniform4i(s.GetUniformLocation(name), v0, v1, v2, v3) +} + +func (s *Shader) SendUniform1f(name string, v float32) { + gl.Uniform1f(s.GetUniformLocation(name), v) +} + +func (s *Shader) SendUniform2f(name string, v0, v1 float32) { + gl.Uniform2f(s.GetUniformLocation(name), v0, v1) +} + +func (s *Shader) SendUniform3f(name string, v0, v1, v2 float32) { + gl.Uniform3f(s.GetUniformLocation(name), v0, v1, v2) +} + +func (s *Shader) SendUniform4f(name string, v0, v1, v2, v3 float32) { + gl.Uniform4f(s.GetUniformLocation(name), v0, v1, v2, v3) +} + +func (s *Shader) SendUniform1iv(name string, count int32, data *int32) { + gl.Uniform1iv(s.GetUniformLocation(name), count, data) +} + +func (s *Shader) SendUniform2iv(name string, count int32, data *int32) { + gl.Uniform2iv(s.GetUniformLocation(name), count, data) +} + +func (s *Shader) SendUniform3iv(name string, count int32, data *int32) { + gl.Uniform3iv(s.GetUniformLocation(name), count, data) +} + +func (s *Shader) SendUniform4iv(name string, count int32, data *int32) { + gl.Uniform4iv(s.GetUniformLocation(name), count, data) +} + +func (s *Shader) SendUniform1fv(name string, count int32, data *float32) { + gl.Uniform1fv(s.GetUniformLocation(name), count, data) +} + +func (s *Shader) SendUniform2fv(name string, count int32, data *float32) { + gl.Uniform2fv(s.GetUniformLocation(name), count, data) +} + +func (s *Shader) SendUniform3fv(name string, count int32, data *float32) { + gl.Uniform3fv(s.GetUniformLocation(name), count, data) +} + +func (s *Shader) SendUniform4fv(name string, count int32, data *float32) { + gl.Uniform4fv(s.GetUniformLocation(name), count, data) +} + +func (s *Shader) SendUniform2x2(name string, data *float32, count int32, transpose bool) { + gl.UniformMatrix2fv(s.GetUniformLocation(name), count, transpose, data) +} + +func (s *Shader) SendUniform3x3(name string, data *float32, count int32, transpose bool) { + gl.UniformMatrix3fv(s.GetUniformLocation(name), count, transpose, data) +} + +func (s *Shader) SendUniform4x4(name string, data *float32, count int32, transpose bool) { + gl.UniformMatrix4fv(s.GetUniformLocation(name), count, transpose, data) +} + +func (s *Shader) SendUniform2x3(name string, data *float32, count int32, transpose bool) { + gl.UniformMatrix2x3fv(s.GetUniformLocation(name), count, transpose, data) +} + +func (s *Shader) SendUniform3x2(name string, data *float32, count int32, transpose bool) { + gl.UniformMatrix3x2fv(s.GetUniformLocation(name), count, transpose, data) +} + +func (s *Shader) SendUniform2x4(name string, data *float32, count int32, transpose bool) { + gl.UniformMatrix2x4fv(s.GetUniformLocation(name), count, transpose, data) +} + +func (s *Shader) SendUniform4x2(name string, data *float32, count int32, transpose bool) { + gl.UniformMatrix4x2fv(s.GetUniformLocation(name), count, transpose, data) +} + +func (s *Shader) SendUniform3x4(name string, data *float32, count int32, transpose bool) { + gl.UniformMatrix3x4fv(s.GetUniformLocation(name), count, transpose, data) +} + +func (s *Shader) SendUniform4x3(name string, data *float32, count int32, transpose bool) { + gl.UniformMatrix4x3fv(s.GetUniformLocation(name), count, transpose, data) +} + +func (s *Shader) SendMat3(name string, m geo.Mat3) { + var data [9]float32 + + for i := 0; i < 9; i++ { + data[i] = float32(m.Values[i]) + } + + gl.UniformMatrix3fv(s.GetUniformLocation(name), 1, false, &data[0]) +} + +func (s *Shader) SendMat4(name string, m geo.Mat4) { + var data [16]float32 + + for i := 0; i < 16; i++ { + data[i] = float32(m.Values[i]) + } + + gl.UniformMatrix4fv(s.GetUniformLocation(name), 1, false, &data[0]) +} + +// Retuns the program GL ID. +func (s *Shader) GetProgramId() uint32 { + return s.program +} + +// Returns the vertex shader GL ID. +func (s *Shader) GetVertexId() uint32 { + return s.vertex +} + +// Returns the fragment shader GL ID. +func (s *Shader) GetFragmentId() uint32 { + return s.fragment +} diff --git a/gl/tex.go b/gl/tex.go new file mode 100644 index 0000000..30ff61d --- /dev/null +++ b/gl/tex.go @@ -0,0 +1,132 @@ +package gl + +import ( + "geo" + "github.com/go-gl/gl/v4.5-core/gl" + "image" +) + +// Texture object. +type Tex struct { + id uint32 + target uint32 + activeTexture uint32 + size geo.Vec3 + rgba *image.RGBA // optional, most of the time nil +} + +// Creates a new texture for given target (e.g. GL_TEXTURE_2D). +func NewTex(target uint32) *Tex { + tex := &Tex{} + tex.target = target + tex.activeTexture = gl.TEXTURE0 + gl.GenTextures(1, &tex.id) + + return tex +} + +// Drops the texture. +func (t *Tex) Drop() { + gl.DeleteBuffers(1, &t.id) +} + +// Binds the texture for rendering. +func (t *Tex) Bind() { + gl.ActiveTexture(t.activeTexture) + gl.BindTexture(t.target, t.id) +} + +// Unbinds. +func (t *Tex) Unbind() { + gl.BindTexture(t.target, 0) +} + +// Sets the default parameters, which are passed filter and CLAMP_TO_EDGE. +func (t *Tex) SetDefaultParams(filter int32) { + t.Parameteri(gl.TEXTURE_MIN_FILTER, filter) + t.Parameteri(gl.TEXTURE_MAG_FILTER, filter) + t.Parameteri(gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) + t.Parameteri(gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) +} + +// Creates a new 1D texture. +func (t *Tex) Texture1D(level, internalFormat, width int32, format, ttype uint32, data []uint8) { + t.size = geo.Vec3{float64(width), 0, 0} + t.Bind() + + if data != nil { + gl.TexImage1D(t.target, level, internalFormat, width, 0, format, ttype, gl.Ptr(data)) + } else { + gl.TexImage1D(t.target, level, internalFormat, width, 0, format, ttype, nil) + } +} + +// Creates a new 2D texture. +func (t *Tex) Texture2D(level, internalFormat, width, height int32, format, ttype uint32, data []uint8) { + t.size = geo.Vec3{float64(width), float64(height), 0} + t.Bind() + + if data != nil { + gl.TexImage2D(t.target, level, internalFormat, width, height, 0, format, ttype, gl.Ptr(data)) + } else { + gl.TexImage2D(t.target, level, internalFormat, width, height, 0, format, ttype, nil) + } +} + +// Creates a new 3D texture. +func (t *Tex) Texture3D(level, internalFormat, width, height, depth int32, format, ttype uint32, data []uint8) { + t.size = geo.Vec3{float64(width), float64(height), float64(depth)} + t.Bind() + + if data != nil { + gl.TexImage3D(t.target, level, internalFormat, width, height, depth, 0, format, ttype, gl.Ptr(data)) + } else { + gl.TexImage3D(t.target, level, internalFormat, width, height, depth, 0, format, ttype, nil) + } +} + +// Sets integer parameter. +func (t *Tex) Parameteri(name uint32, param int32) { + gl.TexParameteri(t.target, name, param) +} + +// Sets float parameter. +func (t *Tex) Parameterf(name uint32, param float32) { + gl.TexParameterf(t.target, name, param) +} + +// Sets which texture boundary is used when bound for rendering. +// Can be GL_TEXTURE0, GL_TEXTURE1, ... GL_TEXTUREn. +func (t *Tex) SetActiveTexture(activeTexture uint32) { + t.activeTexture = activeTexture +} + +// Sets pixel data. +func (t *Tex) SetRGBA(rgba *image.RGBA) { + t.rgba = rgba +} + +// Returns the GL ID. +func (t *Tex) GetId() uint32 { + return t.id +} + +// Returns the texture target +func (t *Tex) GetTarget() uint32 { + return t.target +} + +// Returns the active texture used when bound for rendering. +func (t *Tex) getActiveTexture() uint32 { + return t.activeTexture +} + +// Returns the size of this texture. +func (t *Tex) GetSize() geo.Vec3 { + return t.size +} + +// Returns the pixel data. +func (t *Tex) GetRGBA() *image.RGBA { + return t.rgba +} diff --git a/gl/types.go b/gl/types.go new file mode 100644 index 0000000..4452c0a --- /dev/null +++ b/gl/types.go @@ -0,0 +1,3 @@ +package gl + +const NullTerminator = "\x00" diff --git a/gl/vao.go b/gl/vao.go new file mode 100644 index 0000000..49fde63 --- /dev/null +++ b/gl/vao.go @@ -0,0 +1,39 @@ +package gl + +import ( + "github.com/go-gl/gl/v4.5-core/gl" +) + +// Vertex Array Object. +type VAO struct { + id uint32 +} + +// Creates a new VAO. +// Bind and use VBOs for rendering later. +func NewVAO() *VAO { + vao := &VAO{} + gl.GenVertexArrays(1, &vao.id) + + return vao +} + +// Drops this VAO. +func (v *VAO) Drop() { + gl.DeleteVertexArrays(1, &v.id) +} + +// Binds VAO for rendering. +func (v *VAO) Bind() { + gl.BindVertexArray(v.id) +} + +// Unbinds. +func (v *VAO) Unbind() { + gl.BindVertexArray(0) +} + +// Returns the GL ID. +func (v *VAO) GetId() uint32 { + return v.id +} diff --git a/gl/vbo.go b/gl/vbo.go new file mode 100644 index 0000000..6bb4c92 --- /dev/null +++ b/gl/vbo.go @@ -0,0 +1,77 @@ +package gl + +import ( + "github.com/go-gl/gl/v4.5-core/gl" + "unsafe" +) + +// Vertex Buffer Object. +type VBO struct { + id uint32 + target uint32 + size int32 +} + +// Creates a new VBO with given target. +func NewVBO(target uint32) *VBO { + vbo := &VBO{target: target} + gl.GenBuffers(1, &vbo.id) + + return vbo +} + +// Drops this VBO. +func (v *VBO) Drop() { + gl.DeleteBuffers(1, &v.id) +} + +// Binds VBO for rendering. +func (v *VBO) Bind() { + gl.BindBuffer(v.target, v.id) +} + +// Unbinds. +func (v *VBO) Unbind() { + gl.BindBuffer(v.target, 0) +} + +// Fills VBO with data. +// An unsafe pointer must be used out of Gos unsafe package. +func (v *VBO) Fill(data unsafe.Pointer, elements, size int, use uint32) { + v.size = int32(size) + + v.Bind() + gl.BufferData(v.target, elements*size, data, use) + v.Unbind() +} + +// Updates data or part of data. +// An unsafe pointer must be used out of Gos unsafe package. +func (v *VBO) Update(data unsafe.Pointer, elements, offset, size int) { + v.size = int32(size) + + v.Bind() + gl.BufferSubData(v.target, offset, elements*size, data) + v.Unbind() +} + +// Sets the attribute pointer for rendering. +// Used together with shader. +func (v *VBO) AttribPointer(attribLocation int32, size int32, btype uint32, normalized bool, stride int32) { + gl.VertexAttribPointer(uint32(attribLocation), size, btype, normalized, stride, nil) +} + +// Returns the GL ID. +func (v *VBO) GetId() uint32 { + return v.id +} + +// Returns the target. +func (v *VBO) GetTarget() uint32 { + return v.target +} + +// Returns the number of elements within this VBO. +func (v *VBO) Size() int32 { + return v.size +} diff --git a/system.go b/system.go index 6bb8728..2bd0544 100644 --- a/system.go +++ b/system.go @@ -4,11 +4,15 @@ import () // A system provides logic for actors satisfying required components. // They are automatically updated on each frame. +// When a system is removed from systems, the Cleanup() method will be called. +// This will also happen on program stop. This can be used to cleanup open resources +// (like GL objects). type System interface { Update(float64) - Add(actor *Actor) bool - Remove(actor *Actor) bool - RemoveById(id ActorId) bool + Cleanup() + Add(*Actor) bool + Remove(*Actor) bool + RemoveById(ActorId) bool RemoveAll() Len() int GetName() string @@ -37,6 +41,7 @@ func AddSystem(system System) bool { func RemoveSystem(system System) bool { for i, sys := range systems { if sys == system { + sys.Cleanup() systems = append(systems[:i], systems[i+1:]...) return true } @@ -47,6 +52,10 @@ func RemoveSystem(system System) bool { // Removes all systems. func RemoveAllSystems() { + for _, system := range systems { + system.Cleanup() + } + systems = make([]System, 0) }