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) {
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,

View File

@ -1,29 +1,41 @@
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.
type Attr struct {
Purpose AttrPurpose
Type AttrType
Name string
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.
type AttrType int

View File

@ -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)
}

View File

@ -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")
}

View File

@ -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:

View File

@ -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,
}
)