adoprt universal AttrFormat

This commit is contained in:
faiface 2016-12-16 00:28:52 +01:00
parent 4ae2d2c7ad
commit 2aaab88e07
6 changed files with 109 additions and 132 deletions

View File

@ -191,7 +191,7 @@ func NewSprite(parent pixelgl.Doer, picture Picture) *Sprite {
parent.Do(func(ctx pixelgl.Context) { parent.Do(func(ctx pixelgl.Context) {
var err error var err error
va, err = pixelgl.NewVertexArray( va, err = pixelgl.NewVertexArray(
picture.Texture(), pixelgl.ContextHolder{Context: ctx},
ctx.Shader().VertexFormat(), ctx.Shader().VertexFormat(),
pixelgl.DynamicUsage, pixelgl.DynamicUsage,
4, 4,
@ -232,7 +232,7 @@ func NewLineColor(parent pixelgl.Doer, c color.Color, a, b Vec, width float64) *
parent.Do(func(ctx pixelgl.Context) { parent.Do(func(ctx pixelgl.Context) {
var err error var err error
va, err = pixelgl.NewVertexArray( va, err = pixelgl.NewVertexArray(
parent, pixelgl.ContextHolder{Context: ctx},
ctx.Shader().VertexFormat(), ctx.Shader().VertexFormat(),
pixelgl.DynamicUsage, pixelgl.DynamicUsage,
4, 4,
@ -313,7 +313,7 @@ func NewPolygonColor(parent pixelgl.Doer, c color.Color, points ...Vec) *Polygon
parent.Do(func(ctx pixelgl.Context) { parent.Do(func(ctx pixelgl.Context) {
var err error var err error
va, err = pixelgl.NewVertexArray( va, err = pixelgl.NewVertexArray(
parent, pixelgl.ContextHolder{Context: ctx},
ctx.Shader().VertexFormat(), ctx.Shader().VertexFormat(),
pixelgl.DynamicUsage, pixelgl.DynamicUsage,
len(points), len(points),
@ -377,7 +377,7 @@ func NewEllipseColor(parent pixelgl.Doer, c color.Color, radius Vec, fill float6
parent.Do(func(ctx pixelgl.Context) { parent.Do(func(ctx pixelgl.Context) {
var err error var err error
va, err = pixelgl.NewVertexArray( va, err = pixelgl.NewVertexArray(
parent, pixelgl.ContextHolder{Context: ctx},
ctx.Shader().VertexFormat(), ctx.Shader().VertexFormat(),
pixelgl.DynamicUsage, pixelgl.DynamicUsage,
(n+1)*2, (n+1)*2,

View File

@ -1,29 +1,41 @@
package pixelgl package pixelgl
// 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
}
// 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
}
// Attr represents an arbitrary OpenGL attribute, such as a vertex attribute or a shader uniform attribute. // Attr represents an arbitrary OpenGL attribute, such as a vertex attribute or a shader uniform attribute.
type Attr struct { type Attr struct {
Purpose AttrPurpose Name string
Type AttrType Type AttrType
} }
// AttrPurpose specified a purpose of an attribute. Feel free to create your own purposes for your own needs.
type AttrPurpose int
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
)
// AttrType represents the type of an OpenGL attribute. // AttrType represents the type of an OpenGL attribute.
type AttrType int type AttrType int

View File

@ -59,7 +59,7 @@ type ContextHolder struct {
} }
// Do calls sub and passes it the held context. // 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) sub(ch.Context)
} }

View File

@ -7,26 +7,19 @@ import (
"github.com/go-gl/mathgl/mgl32" "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. // Shader is an OpenGL shader program.
type Shader struct { type Shader struct {
parent Doer parent Doer
program binder program binder
vertexFormat VertexFormat vertexFmt AttrFormat
uniformFormat UniformFormat uniformFmt AttrFormat
uniforms map[Attr]int32 uniforms map[string]int32
} }
// NewShader creates a new shader program from the specified vertex shader and fragment shader sources. // 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. // 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{ shader := &Shader{
parent: parent, parent: parent,
program: binder{ program: binder{
@ -35,9 +28,9 @@ func NewShader(parent Doer, vertexFormat VertexFormat, uniformFormat UniformForm
gl.UseProgram(obj) gl.UseProgram(obj)
}, },
}, },
vertexFormat: vertexFormat, vertexFmt: vertexFmt,
uniformFormat: uniformFormat, uniformFmt: uniformFmt,
uniforms: make(map[Attr]int32), uniforms: make(map[string]int32),
} }
var err error var err error
@ -108,17 +101,9 @@ func NewShader(parent Doer, vertexFormat VertexFormat, uniformFormat UniformForm
} }
// uniforms // uniforms
for uname, utype := range uniformFormat { for name := range uniformFmt {
ulocation := gl.GetUniformLocation(shader.program.obj, gl.Str(uname+"\x00")) loc := gl.GetUniformLocation(shader.program.obj, gl.Str(name+"\x00"))
if ulocation == -1 { shader.uniforms[name] = loc
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
} }
return nil return nil
@ -146,13 +131,13 @@ func (s *Shader) ID() uint32 {
} }
// VertexFormat returns the vertex attribute format of this shader. Do not change it. // VertexFormat returns the vertex attribute format of this shader. Do not change it.
func (s *Shader) VertexFormat() VertexFormat { func (s *Shader) VertexFormat() AttrFormat {
return s.vertexFormat return s.vertexFmt
} }
// UniformFormat returns the uniform attribute format of this shader. Do not change it. // UniformFormat returns the uniform attribute format of this shader. Do not change it.
func (s *Shader) UniformFormat() UniformFormat { func (s *Shader) UniformFormat() AttrFormat {
return s.uniformFormat return s.uniformFmt
} }
// SetUniformAttr sets the value of a uniform attribute of a shader. // 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 // Attr{Type: Mat43}: mgl32.Mat4x3
// No other types are supported. // No other types are supported.
func (s *Shader) SetUniformAttr(attr Attr, value interface{}) (ok bool) { func (s *Shader) SetUniformAttr(attr Attr, value interface{}) (ok bool) {
if _, ok := s.uniforms[attr]; !ok { if !s.uniformFmt.Contains(attr) {
return false return false
} }
@ -186,46 +171,46 @@ func (s *Shader) SetUniformAttr(attr Attr, value interface{}) (ok bool) {
switch attr.Type { switch attr.Type {
case Int: case Int:
value := value.(int32) value := value.(int32)
gl.Uniform1iv(s.uniforms[attr], 1, &value) gl.Uniform1iv(s.uniforms[attr.Name], 1, &value)
case Float: case Float:
value := value.(float32) value := value.(float32)
gl.Uniform1fv(s.uniforms[attr], 1, &value) gl.Uniform1fv(s.uniforms[attr.Name], 1, &value)
case Vec2: case Vec2:
value := value.(mgl32.Vec2) value := value.(mgl32.Vec2)
gl.Uniform2fv(s.uniforms[attr], 1, &value[0]) gl.Uniform2fv(s.uniforms[attr.Name], 1, &value[0])
case Vec3: case Vec3:
value := value.(mgl32.Vec3) value := value.(mgl32.Vec3)
gl.Uniform3fv(s.uniforms[attr], 1, &value[0]) gl.Uniform3fv(s.uniforms[attr.Name], 1, &value[0])
case Vec4: case Vec4:
value := value.(mgl32.Vec4) value := value.(mgl32.Vec4)
gl.Uniform4fv(s.uniforms[attr], 1, &value[0]) gl.Uniform4fv(s.uniforms[attr.Name], 1, &value[0])
case Mat2: case Mat2:
value := value.(mgl32.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: case Mat23:
value := value.(mgl32.Mat2x3) 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: case Mat24:
value := value.(mgl32.Mat2x4) 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: case Mat3:
value := value.(mgl32.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: case Mat32:
value := value.(mgl32.Mat3x2) 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: case Mat34:
value := value.(mgl32.Mat3x4) 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: case Mat4:
value := value.(mgl32.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: case Mat42:
value := value.(mgl32.Mat4x2) 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: case Mat43:
value := value.(mgl32.Mat4x3) value := value.(mgl32.Mat4x3)
gl.UniformMatrix4x3fv(s.uniforms[attr], 1, false, &value[0]) gl.UniformMatrix4x3fv(s.uniforms[attr.Name], 1, false, &value[0])
default: default:
panic("set uniform attr: invalid attribute type") panic("set uniform attr: invalid attribute type")
} }

View File

@ -8,24 +8,6 @@ import (
"github.com/pkg/errors" "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. // VertexUsage specifies how often the vertex array data will be updated.
type VertexUsage int type VertexUsage int
@ -46,17 +28,17 @@ type VertexArray struct {
parent Doer parent Doer
vao, vbo, ebo binder vao, vbo, ebo binder
vertexNum, indexNum int vertexNum, indexNum int
format VertexFormat format AttrFormat
usage VertexUsage usage VertexUsage
stride int stride int
attrs map[Attr]int offset map[string]int
} }
// NewVertexArray creates a new empty vertex array and wraps another Doer around it. // 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 // 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. // 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{ va := &VertexArray{
parent: parent, parent: parent,
vao: binder{ vao: binder{
@ -81,21 +63,18 @@ func NewVertexArray(parent Doer, format VertexFormat, usage VertexUsage, vertexN
format: format, format: format,
usage: usage, usage: usage,
stride: format.Size(), stride: format.Size(),
attrs: make(map[Attr]int), offset: make(map[string]int),
} }
offset := 0 offset := 0
for _, attr := range format { for name, typ := range format {
switch attr.Type { switch typ {
case Float, Vec2, Vec3, Vec4: case Float, Vec2, Vec3, Vec4:
default: default:
return nil, errors.New("failed to create vertex array: invalid vertex format: invalid attribute type") return nil, errors.New("failed to create vertex array: invalid vertex format: invalid attribute type")
} }
if _, ok := va.attrs[attr]; ok { va.offset[name] = offset
return nil, errors.New("failed to create vertex array: invalid vertex format: duplicate vertex attribute") offset += typ.Size()
}
va.attrs[attr] = offset
offset += attr.Type.Size()
} }
parent.Do(func(ctx Context) { parent.Do(func(ctx Context) {
@ -112,10 +91,11 @@ func NewVertexArray(parent Doer, format VertexFormat, usage VertexUsage, vertexN
gl.GenBuffers(1, &va.ebo.obj) gl.GenBuffers(1, &va.ebo.obj)
defer va.ebo.bind().restore() defer va.ebo.bind().restore()
offset := 0 for name, typ := range format {
for i, attr := range format { loc := gl.GetAttribLocation(ctx.Shader().ID(), gl.Str(name+"\x00"))
var size int32 var size int32
switch attr.Type { switch typ {
case Float: case Float:
size = 1 size = 1
case Vec2: case Vec2:
@ -127,15 +107,14 @@ func NewVertexArray(parent Doer, format VertexFormat, usage VertexUsage, vertexN
} }
gl.VertexAttribPointer( gl.VertexAttribPointer(
uint32(i), uint32(loc),
size, size,
gl.FLOAT, gl.FLOAT,
false, false,
int32(va.stride), int32(va.stride),
gl.PtrOffset(offset), gl.PtrOffset(va.offset[name]),
) )
gl.EnableVertexAttribArray(uint32(i)) gl.EnableVertexAttribArray(uint32(loc))
offset += attr.Type.Size()
} }
va.vao.restore() va.vao.restore()
@ -153,6 +132,7 @@ func (va *VertexArray) Delete() {
DoNoBlock(func() { DoNoBlock(func() {
gl.DeleteVertexArrays(1, &va.vao.obj) gl.DeleteVertexArrays(1, &va.vao.obj)
gl.DeleteBuffers(1, &va.vbo.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. // VertexFormat returns the format of the vertices inside a vertex array.
// //
// Do not change this format! // Do not change this format!
func (va *VertexArray) VertexFormat() VertexFormat { func (va *VertexArray) VertexFormat() AttrFormat {
return va.format return va.format
} }
@ -219,14 +199,14 @@ func (va *VertexArray) SetVertexAttr(vertex int, attr Attr, value interface{}) (
panic("set vertex attr: invalid vertex index") panic("set vertex attr: invalid vertex index")
} }
if _, ok := va.attrs[attr]; !ok { if !va.format.Contains(attr) {
return false return false
} }
DoNoBlock(func() { DoNoBlock(func() {
va.vbo.bind() va.vbo.bind()
offset := va.stride*vertex + va.attrs[attr] offset := va.stride*vertex + va.offset[attr.Name]
switch attr.Type { switch attr.Type {
case Float: case Float:
@ -262,14 +242,14 @@ func (va *VertexArray) VertexAttr(vertex int, attr Attr) (value interface{}, ok
panic("vertex attr: invalid vertex index") panic("vertex attr: invalid vertex index")
} }
if _, ok := va.attrs[attr]; !ok { if !va.format.Contains(attr) {
return nil, false return nil, false
} }
Do(func() { Do(func() {
va.vbo.bind() va.vbo.bind()
offset := va.stride*vertex + va.attrs[attr] offset := va.stride*vertex + va.offset[attr.Name]
switch attr.Type { switch attr.Type {
case Float: case Float:

View File

@ -337,23 +337,23 @@ func (w *Window) Do(sub func(pixelgl.Context)) {
w.enabled = false w.enabled = false
} }
var defaultVertexFormat = pixelgl.VertexFormat{ var defaultVertexFormat = pixelgl.AttrFormat{
{Purpose: pixelgl.Position, Type: pixelgl.Vec2}, "position": pixelgl.Vec2,
{Purpose: pixelgl.Color, Type: pixelgl.Vec4}, "color": pixelgl.Vec4,
{Purpose: pixelgl.TexCoord, Type: pixelgl.Vec2}, "texCoord": pixelgl.Vec2,
} }
var defaultUniformFormat = pixelgl.UniformFormat{ var defaultUniformFormat = pixelgl.AttrFormat{
"maskColor": {Purpose: pixelgl.MaskColor, Type: pixelgl.Vec4}, "maskColor": pixelgl.Vec4,
"transform": {Purpose: pixelgl.Transform, Type: pixelgl.Mat3}, "transform": pixelgl.Mat3,
} }
var defaultVertexShader = ` var defaultVertexShader = `
#version 330 core #version 330 core
layout (location = 0) in vec2 position; in vec2 position;
layout (location = 1) in vec4 color; in vec4 color;
layout (location = 2) in vec2 texCoord; in vec2 texCoord;
out vec4 Color; out vec4 Color;
out vec2 TexCoord; out vec2 TexCoord;
@ -389,23 +389,23 @@ void main() {
var ( var (
positionVec2 = pixelgl.Attr{ positionVec2 = pixelgl.Attr{
Purpose: pixelgl.Position, Name: "position",
Type: pixelgl.Vec2, Type: pixelgl.Vec2,
} }
colorVec4 = pixelgl.Attr{ colorVec4 = pixelgl.Attr{
Purpose: pixelgl.Color, Name: "color",
Type: pixelgl.Vec4, Type: pixelgl.Vec4,
} }
texCoordVec2 = pixelgl.Attr{ texCoordVec2 = pixelgl.Attr{
Purpose: pixelgl.TexCoord, Name: "texCoord",
Type: pixelgl.Vec2, Type: pixelgl.Vec2,
} }
maskColorVec4 = pixelgl.Attr{ maskColorVec4 = pixelgl.Attr{
Purpose: pixelgl.MaskColor, Name: "maskColor",
Type: pixelgl.Vec4, Type: pixelgl.Vec4,
} }
transformMat3 = pixelgl.Attr{ transformMat3 = pixelgl.Attr{
Purpose: pixelgl.Transform, Name: "transform",
Type: pixelgl.Mat3, Type: pixelgl.Mat3,
} }
) )