From 2aaab88e070855b7b82102af1a8b631da53e3d46 Mon Sep 17 00:00:00 2001
From: faiface <faiface@ksp.sk>
Date: Fri, 16 Dec 2016 00:28:52 +0100
Subject: [PATCH] adoprt universal AttrFormat

---
 graphics.go          |  8 ++---
 pixelgl/attr.go      | 52 ++++++++++++++++++------------
 pixelgl/interface.go |  2 +-
 pixelgl/shader.go    | 77 ++++++++++++++++++--------------------------
 pixelgl/vertex.go    | 62 ++++++++++++-----------------------
 window.go            | 40 +++++++++++------------
 6 files changed, 109 insertions(+), 132 deletions(-)

diff --git a/graphics.go b/graphics.go
index b0485bb..1eaf02c 100644
--- a/graphics.go
+++ b/graphics.go
@@ -191,7 +191,7 @@ func NewSprite(parent pixelgl.Doer, picture Picture) *Sprite {
 	parent.Do(func(ctx pixelgl.Context) {
 		var err error
 		va, err = pixelgl.NewVertexArray(
-			picture.Texture(),
+			pixelgl.ContextHolder{Context: ctx},
 			ctx.Shader().VertexFormat(),
 			pixelgl.DynamicUsage,
 			4,
@@ -232,7 +232,7 @@ func NewLineColor(parent pixelgl.Doer, c color.Color, a, b Vec, width float64) *
 	parent.Do(func(ctx pixelgl.Context) {
 		var err error
 		va, err = pixelgl.NewVertexArray(
-			parent,
+			pixelgl.ContextHolder{Context: ctx},
 			ctx.Shader().VertexFormat(),
 			pixelgl.DynamicUsage,
 			4,
@@ -313,7 +313,7 @@ func NewPolygonColor(parent pixelgl.Doer, c color.Color, points ...Vec) *Polygon
 	parent.Do(func(ctx pixelgl.Context) {
 		var err error
 		va, err = pixelgl.NewVertexArray(
-			parent,
+			pixelgl.ContextHolder{Context: ctx},
 			ctx.Shader().VertexFormat(),
 			pixelgl.DynamicUsage,
 			len(points),
@@ -377,7 +377,7 @@ func NewEllipseColor(parent pixelgl.Doer, c color.Color, radius Vec, fill float6
 	parent.Do(func(ctx pixelgl.Context) {
 		var err error
 		va, err = pixelgl.NewVertexArray(
-			parent,
+			pixelgl.ContextHolder{Context: ctx},
 			ctx.Shader().VertexFormat(),
 			pixelgl.DynamicUsage,
 			(n+1)*2,
diff --git a/pixelgl/attr.go b/pixelgl/attr.go
index b7f6b8c..7da5e4a 100644
--- a/pixelgl/attr.go
+++ b/pixelgl/attr.go
@@ -1,28 +1,40 @@
 package pixelgl
 
-// Attr represents an arbitrary OpenGL attribute, such as a vertex attribute or a shader uniform attribute.
-type Attr struct {
-	Purpose AttrPurpose
-	Type    AttrType
+// AttrFormat defines names and types of OpenGL attributes (vertex format, uniform format, etc.).
+//
+// Example:
+//   AttrFormat{"position": Vec2, "color": Vec4, "texCoord": Vec2}
+type AttrFormat map[string]AttrType
+
+// Contains checks whether a format contains a specific attribute.
+//
+// It does a little more than a hard check: e.g. if you query a Vec2 attribute, but the format contains Vec3,
+// Contains returns true, because Vec2 is assignable to Vec3. Specifically, Float -> Vec2 -> Vec3 -> Vec4 (transitively).
+// This however does not work for matrices or ints.
+func (af AttrFormat) Contains(attr Attr) bool {
+	if typ, ok := af[attr.Name]; ok {
+		if (Float <= typ && typ <= Vec4) && (Float <= attr.Type && attr.Type <= typ) {
+			return true
+		}
+		return attr.Type == typ
+	}
+	return false
 }
 
-// AttrPurpose specified a purpose of an attribute. Feel free to create your own purposes for your own needs.
-type AttrPurpose int
+// Size returns the total size of all attributes of an attribute format.
+func (af AttrFormat) Size() int {
+	total := 0
+	for _, typ := range af {
+		total += typ.Size()
+	}
+	return total
+}
 
-const (
-	// Position of a vertex
-	Position AttrPurpose = iota
-	// Color of a vertex
-	Color
-	// TexCoord are texture coordinates
-	TexCoord
-	// Transform is an object transformation matrix
-	Transform
-	// MaskColor is a masking color. When drawing, each color gets multiplied by this color.
-	MaskColor
-	// NumStandardAttrPurposes is the number of standard attribute purposes
-	NumStandardAttrPurposes
-)
+// Attr represents an arbitrary OpenGL attribute, such as a vertex attribute or a shader uniform attribute.
+type Attr struct {
+	Name string
+	Type AttrType
+}
 
 // AttrType represents the type of an OpenGL attribute.
 type AttrType int
diff --git a/pixelgl/interface.go b/pixelgl/interface.go
index 7041322..874d070 100644
--- a/pixelgl/interface.go
+++ b/pixelgl/interface.go
@@ -59,7 +59,7 @@ type ContextHolder struct {
 }
 
 // Do calls sub and passes it the held context.
-func (ch *ContextHolder) Do(sub func(ctx Context)) {
+func (ch ContextHolder) Do(sub func(ctx Context)) {
 	sub(ch.Context)
 }
 
diff --git a/pixelgl/shader.go b/pixelgl/shader.go
index ce638a3..666eed6 100644
--- a/pixelgl/shader.go
+++ b/pixelgl/shader.go
@@ -7,26 +7,19 @@ import (
 	"github.com/go-gl/mathgl/mgl32"
 )
 
-// UniformFormat defines names, purposes and types of uniform variables inside a shader.
-//
-// Example:
-//
-//   UniformFormat{"transform": {Transform, Mat3}, "camera": {Camera, Mat3}}
-type UniformFormat map[string]Attr
-
 // Shader is an OpenGL shader program.
 type Shader struct {
-	parent        Doer
-	program       binder
-	vertexFormat  VertexFormat
-	uniformFormat UniformFormat
-	uniforms      map[Attr]int32
+	parent     Doer
+	program    binder
+	vertexFmt  AttrFormat
+	uniformFmt AttrFormat
+	uniforms   map[string]int32
 }
 
 // NewShader creates a new shader program from the specified vertex shader and fragment shader sources.
 //
 // Note that vertexShader and fragmentShader parameters must contain the source code, they're not filenames.
-func NewShader(parent Doer, vertexFormat VertexFormat, uniformFormat UniformFormat, vertexShader, fragmentShader string) (*Shader, error) {
+func NewShader(parent Doer, vertexFmt, uniformFmt AttrFormat, vertexShader, fragmentShader string) (*Shader, error) {
 	shader := &Shader{
 		parent: parent,
 		program: binder{
@@ -35,9 +28,9 @@ func NewShader(parent Doer, vertexFormat VertexFormat, uniformFormat UniformForm
 				gl.UseProgram(obj)
 			},
 		},
-		vertexFormat:  vertexFormat,
-		uniformFormat: uniformFormat,
-		uniforms:      make(map[Attr]int32),
+		vertexFmt:  vertexFmt,
+		uniformFmt: uniformFmt,
+		uniforms:   make(map[string]int32),
 	}
 
 	var err error
@@ -108,17 +101,9 @@ func NewShader(parent Doer, vertexFormat VertexFormat, uniformFormat UniformForm
 			}
 
 			// uniforms
-			for uname, utype := range uniformFormat {
-				ulocation := gl.GetUniformLocation(shader.program.obj, gl.Str(uname+"\x00"))
-				if ulocation == -1 {
-					gl.DeleteProgram(shader.program.obj)
-					return fmt.Errorf("shader does not contain uniform '%s'", uname)
-				}
-				if _, ok := shader.uniforms[utype]; ok {
-					gl.DeleteProgram(shader.program.obj)
-					return fmt.Errorf("failed to create shader: invalid uniform format: duplicate uniform attribute")
-				}
-				shader.uniforms[utype] = ulocation
+			for name := range uniformFmt {
+				loc := gl.GetUniformLocation(shader.program.obj, gl.Str(name+"\x00"))
+				shader.uniforms[name] = loc
 			}
 
 			return nil
@@ -146,13 +131,13 @@ func (s *Shader) ID() uint32 {
 }
 
 // VertexFormat returns the vertex attribute format of this shader. Do not change it.
-func (s *Shader) VertexFormat() VertexFormat {
-	return s.vertexFormat
+func (s *Shader) VertexFormat() AttrFormat {
+	return s.vertexFmt
 }
 
 // UniformFormat returns the uniform attribute format of this shader. Do not change it.
-func (s *Shader) UniformFormat() UniformFormat {
-	return s.uniformFormat
+func (s *Shader) UniformFormat() AttrFormat {
+	return s.uniformFmt
 }
 
 // SetUniformAttr sets the value of a uniform attribute of a shader.
@@ -176,7 +161,7 @@ func (s *Shader) UniformFormat() UniformFormat {
 //   Attr{Type: Mat43}: mgl32.Mat4x3
 // No other types are supported.
 func (s *Shader) SetUniformAttr(attr Attr, value interface{}) (ok bool) {
-	if _, ok := s.uniforms[attr]; !ok {
+	if !s.uniformFmt.Contains(attr) {
 		return false
 	}
 
@@ -186,46 +171,46 @@ func (s *Shader) SetUniformAttr(attr Attr, value interface{}) (ok bool) {
 		switch attr.Type {
 		case Int:
 			value := value.(int32)
-			gl.Uniform1iv(s.uniforms[attr], 1, &value)
+			gl.Uniform1iv(s.uniforms[attr.Name], 1, &value)
 		case Float:
 			value := value.(float32)
-			gl.Uniform1fv(s.uniforms[attr], 1, &value)
+			gl.Uniform1fv(s.uniforms[attr.Name], 1, &value)
 		case Vec2:
 			value := value.(mgl32.Vec2)
-			gl.Uniform2fv(s.uniforms[attr], 1, &value[0])
+			gl.Uniform2fv(s.uniforms[attr.Name], 1, &value[0])
 		case Vec3:
 			value := value.(mgl32.Vec3)
-			gl.Uniform3fv(s.uniforms[attr], 1, &value[0])
+			gl.Uniform3fv(s.uniforms[attr.Name], 1, &value[0])
 		case Vec4:
 			value := value.(mgl32.Vec4)
-			gl.Uniform4fv(s.uniforms[attr], 1, &value[0])
+			gl.Uniform4fv(s.uniforms[attr.Name], 1, &value[0])
 		case Mat2:
 			value := value.(mgl32.Mat2)
-			gl.UniformMatrix2fv(s.uniforms[attr], 1, false, &value[0])
+			gl.UniformMatrix2fv(s.uniforms[attr.Name], 1, false, &value[0])
 		case Mat23:
 			value := value.(mgl32.Mat2x3)
-			gl.UniformMatrix2x3fv(s.uniforms[attr], 1, false, &value[0])
+			gl.UniformMatrix2x3fv(s.uniforms[attr.Name], 1, false, &value[0])
 		case Mat24:
 			value := value.(mgl32.Mat2x4)
-			gl.UniformMatrix2x4fv(s.uniforms[attr], 1, false, &value[0])
+			gl.UniformMatrix2x4fv(s.uniforms[attr.Name], 1, false, &value[0])
 		case Mat3:
 			value := value.(mgl32.Mat3)
-			gl.UniformMatrix3fv(s.uniforms[attr], 1, false, &value[0])
+			gl.UniformMatrix3fv(s.uniforms[attr.Name], 1, false, &value[0])
 		case Mat32:
 			value := value.(mgl32.Mat3x2)
-			gl.UniformMatrix3x2fv(s.uniforms[attr], 1, false, &value[0])
+			gl.UniformMatrix3x2fv(s.uniforms[attr.Name], 1, false, &value[0])
 		case Mat34:
 			value := value.(mgl32.Mat3x4)
-			gl.UniformMatrix3x4fv(s.uniforms[attr], 1, false, &value[0])
+			gl.UniformMatrix3x4fv(s.uniforms[attr.Name], 1, false, &value[0])
 		case Mat4:
 			value := value.(mgl32.Mat4)
-			gl.UniformMatrix4fv(s.uniforms[attr], 1, false, &value[0])
+			gl.UniformMatrix4fv(s.uniforms[attr.Name], 1, false, &value[0])
 		case Mat42:
 			value := value.(mgl32.Mat4x2)
-			gl.UniformMatrix4x2fv(s.uniforms[attr], 1, false, &value[0])
+			gl.UniformMatrix4x2fv(s.uniforms[attr.Name], 1, false, &value[0])
 		case Mat43:
 			value := value.(mgl32.Mat4x3)
-			gl.UniformMatrix4x3fv(s.uniforms[attr], 1, false, &value[0])
+			gl.UniformMatrix4x3fv(s.uniforms[attr.Name], 1, false, &value[0])
 		default:
 			panic("set uniform attr: invalid attribute type")
 		}
diff --git a/pixelgl/vertex.go b/pixelgl/vertex.go
index da0843d..7ff2ebf 100644
--- a/pixelgl/vertex.go
+++ b/pixelgl/vertex.go
@@ -8,24 +8,6 @@ import (
 	"github.com/pkg/errors"
 )
 
-// VertexFormat defines a data format in a vertex buffer.
-//
-// Example:
-//
-//   VertexFormat{"position": {Position, Vec2}, "colr": {Color, Vec4}, "texCoord": {TexCoord, Vec2}}
-//
-// Note: vertex array currently doesn't support matrices in vertex format.
-type VertexFormat []Attr
-
-// Size calculates the total size of a single vertex in this vertex format (sum of the sizes of all vertex attributes).
-func (vf VertexFormat) Size() int {
-	total := 0
-	for _, attr := range vf {
-		total += attr.Type.Size()
-	}
-	return total
-}
-
 // VertexUsage specifies how often the vertex array data will be updated.
 type VertexUsage int
 
@@ -46,17 +28,17 @@ type VertexArray struct {
 	parent              Doer
 	vao, vbo, ebo       binder
 	vertexNum, indexNum int
-	format              VertexFormat
+	format              AttrFormat
 	usage               VertexUsage
 	stride              int
-	attrs               map[Attr]int
+	offset              map[string]int
 }
 
 // NewVertexArray creates a new empty vertex array and wraps another Doer around it.
 //
 // You cannot specify vertex attributes in this constructor, only their count. Use SetVertexAttribute* methods to
 // set the vertex attributes. Use indices to specify how you want to combine vertices into triangles.
-func NewVertexArray(parent Doer, format VertexFormat, usage VertexUsage, vertexNum int, indices []int) (*VertexArray, error) {
+func NewVertexArray(parent Doer, format AttrFormat, usage VertexUsage, vertexNum int, indices []int) (*VertexArray, error) {
 	va := &VertexArray{
 		parent: parent,
 		vao: binder{
@@ -81,21 +63,18 @@ func NewVertexArray(parent Doer, format VertexFormat, usage VertexUsage, vertexN
 		format:    format,
 		usage:     usage,
 		stride:    format.Size(),
-		attrs:     make(map[Attr]int),
+		offset:    make(map[string]int),
 	}
 
 	offset := 0
-	for _, attr := range format {
-		switch attr.Type {
+	for name, typ := range format {
+		switch typ {
 		case Float, Vec2, Vec3, Vec4:
 		default:
 			return nil, errors.New("failed to create vertex array: invalid vertex format: invalid attribute type")
 		}
-		if _, ok := va.attrs[attr]; ok {
-			return nil, errors.New("failed to create vertex array: invalid vertex format: duplicate vertex attribute")
-		}
-		va.attrs[attr] = offset
-		offset += attr.Type.Size()
+		va.offset[name] = offset
+		offset += typ.Size()
 	}
 
 	parent.Do(func(ctx Context) {
@@ -112,10 +91,11 @@ func NewVertexArray(parent Doer, format VertexFormat, usage VertexUsage, vertexN
 			gl.GenBuffers(1, &va.ebo.obj)
 			defer va.ebo.bind().restore()
 
-			offset := 0
-			for i, attr := range format {
+			for name, typ := range format {
+				loc := gl.GetAttribLocation(ctx.Shader().ID(), gl.Str(name+"\x00"))
+
 				var size int32
-				switch attr.Type {
+				switch typ {
 				case Float:
 					size = 1
 				case Vec2:
@@ -127,15 +107,14 @@ func NewVertexArray(parent Doer, format VertexFormat, usage VertexUsage, vertexN
 				}
 
 				gl.VertexAttribPointer(
-					uint32(i),
+					uint32(loc),
 					size,
 					gl.FLOAT,
 					false,
 					int32(va.stride),
-					gl.PtrOffset(offset),
+					gl.PtrOffset(va.offset[name]),
 				)
-				gl.EnableVertexAttribArray(uint32(i))
-				offset += attr.Type.Size()
+				gl.EnableVertexAttribArray(uint32(loc))
 			}
 
 			va.vao.restore()
@@ -153,6 +132,7 @@ func (va *VertexArray) Delete() {
 		DoNoBlock(func() {
 			gl.DeleteVertexArrays(1, &va.vao.obj)
 			gl.DeleteBuffers(1, &va.vbo.obj)
+			gl.DeleteBuffers(1, &va.ebo.obj)
 		})
 	})
 }
@@ -170,7 +150,7 @@ func (va *VertexArray) VertexNum() int {
 // VertexFormat returns the format of the vertices inside a vertex array.
 //
 // Do not change this format!
-func (va *VertexArray) VertexFormat() VertexFormat {
+func (va *VertexArray) VertexFormat() AttrFormat {
 	return va.format
 }
 
@@ -219,14 +199,14 @@ func (va *VertexArray) SetVertexAttr(vertex int, attr Attr, value interface{}) (
 		panic("set vertex attr: invalid vertex index")
 	}
 
-	if _, ok := va.attrs[attr]; !ok {
+	if !va.format.Contains(attr) {
 		return false
 	}
 
 	DoNoBlock(func() {
 		va.vbo.bind()
 
-		offset := va.stride*vertex + va.attrs[attr]
+		offset := va.stride*vertex + va.offset[attr.Name]
 
 		switch attr.Type {
 		case Float:
@@ -262,14 +242,14 @@ func (va *VertexArray) VertexAttr(vertex int, attr Attr) (value interface{}, ok
 		panic("vertex attr: invalid vertex index")
 	}
 
-	if _, ok := va.attrs[attr]; !ok {
+	if !va.format.Contains(attr) {
 		return nil, false
 	}
 
 	Do(func() {
 		va.vbo.bind()
 
-		offset := va.stride*vertex + va.attrs[attr]
+		offset := va.stride*vertex + va.offset[attr.Name]
 
 		switch attr.Type {
 		case Float:
diff --git a/window.go b/window.go
index 35a556b..a6dd197 100644
--- a/window.go
+++ b/window.go
@@ -337,23 +337,23 @@ func (w *Window) Do(sub func(pixelgl.Context)) {
 	w.enabled = false
 }
 
-var defaultVertexFormat = pixelgl.VertexFormat{
-	{Purpose: pixelgl.Position, Type: pixelgl.Vec2},
-	{Purpose: pixelgl.Color, Type: pixelgl.Vec4},
-	{Purpose: pixelgl.TexCoord, Type: pixelgl.Vec2},
+var defaultVertexFormat = pixelgl.AttrFormat{
+	"position": pixelgl.Vec2,
+	"color":    pixelgl.Vec4,
+	"texCoord": pixelgl.Vec2,
 }
 
-var defaultUniformFormat = pixelgl.UniformFormat{
-	"maskColor": {Purpose: pixelgl.MaskColor, Type: pixelgl.Vec4},
-	"transform": {Purpose: pixelgl.Transform, Type: pixelgl.Mat3},
+var defaultUniformFormat = pixelgl.AttrFormat{
+	"maskColor": pixelgl.Vec4,
+	"transform": pixelgl.Mat3,
 }
 
 var defaultVertexShader = `
 #version 330 core
 
-layout (location = 0) in vec2 position;
-layout (location = 1) in vec4 color;
-layout (location = 2) in vec2 texCoord;
+in vec2 position;
+in vec4 color;
+in vec2 texCoord;
 
 out vec4 Color;
 out vec2 TexCoord;
@@ -389,23 +389,23 @@ void main() {
 
 var (
 	positionVec2 = pixelgl.Attr{
-		Purpose: pixelgl.Position,
-		Type:    pixelgl.Vec2,
+		Name: "position",
+		Type: pixelgl.Vec2,
 	}
 	colorVec4 = pixelgl.Attr{
-		Purpose: pixelgl.Color,
-		Type:    pixelgl.Vec4,
+		Name: "color",
+		Type: pixelgl.Vec4,
 	}
 	texCoordVec2 = pixelgl.Attr{
-		Purpose: pixelgl.TexCoord,
-		Type:    pixelgl.Vec2,
+		Name: "texCoord",
+		Type: pixelgl.Vec2,
 	}
 	maskColorVec4 = pixelgl.Attr{
-		Purpose: pixelgl.MaskColor,
-		Type:    pixelgl.Vec4,
+		Name: "maskColor",
+		Type: pixelgl.Vec4,
 	}
 	transformMat3 = pixelgl.Attr{
-		Purpose: pixelgl.Transform,
-		Type:    pixelgl.Mat3,
+		Name: "transform",
+		Type: pixelgl.Mat3,
 	}
 )
-- 
GitLab