remove Doer and replace with BeginEnder
This commit is contained in:
parent
727c9bd772
commit
2b6205fe45
|
@ -1,76 +1,11 @@
|
|||
package pixelgl
|
||||
|
||||
// Doer is an interface for manipulating OpenGL state.
|
||||
// BeginEnder is an interface for manipulating OpenGL state.
|
||||
//
|
||||
// OpenGL is a state machine. Every object can 'enter' it's state and 'leave' it's state. For
|
||||
// example, you can bind a buffer and unbind a buffer, bind a texture and unbind it, use shader
|
||||
// and unuse it, and so on.
|
||||
//
|
||||
// This interface provides a clever and flexible way to do it. A typical workflow of an OpenGL
|
||||
// object is that you enter (load, bind) that object's state, then do something with it, and
|
||||
// then leave the state. That 'something' in between, let's call it sub (as in subroutine).
|
||||
//
|
||||
// The recommended way to implement a Doer is to wrap another Doer (vertex array wraps texture
|
||||
// and so on), let's call it parent. Then the Do method will look like this:
|
||||
//
|
||||
// func (o *MyObject) Do(sub func(Context)) {
|
||||
// o.parent.Do(func(ctx Context) {
|
||||
// // enter the object's state
|
||||
// sub(ctx)
|
||||
// // leave the object's state
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// It might seem difficult to grasp this kind of recursion at first, but it's really simple. What
|
||||
// it's basically saying is: "Hey parent, enter your state, then let me enter mine, then I'll
|
||||
// do whatever I'm supposed to do in the middle. After that I'll leave my state and please
|
||||
// leave your state too parent."
|
||||
//
|
||||
// Also notice, that the functions are passing a Context around. This context contains the
|
||||
// most important state variables. Usually, you just pass it as you received it. If you want
|
||||
// to pass a changed context to your child (e.g. your a shader), use ctx.With* methods.
|
||||
//
|
||||
// If possible and makes sense, Do method should be reentrant.
|
||||
type Doer interface {
|
||||
Do(sub func(Context))
|
||||
}
|
||||
|
||||
// Context takes state from one object to another. OpenGL is a state machine, so we have
|
||||
// to approach it like that. However, global variables are evil, so we have Context, that
|
||||
// transfers important OpenGL state from one object to another.
|
||||
//
|
||||
// This type does *not* represent an OpenGL context in the OpenGL terminology.
|
||||
type Context struct {
|
||||
shader *Shader
|
||||
}
|
||||
|
||||
// Shader returns the current shader.
|
||||
func (c Context) Shader() *Shader {
|
||||
return c.shader
|
||||
}
|
||||
|
||||
// WithShader returns a copy of this context with the specified shader.
|
||||
func (c Context) WithShader(s *Shader) Context {
|
||||
return Context{
|
||||
shader: s,
|
||||
}
|
||||
}
|
||||
|
||||
// ContextHolder is a root Doer with no parent. It simply forwards a context to a child.
|
||||
type ContextHolder struct {
|
||||
Context Context
|
||||
}
|
||||
|
||||
// Do calls sub and passes it the held context.
|
||||
func (ch ContextHolder) Do(sub func(ctx Context)) {
|
||||
sub(ch.Context)
|
||||
}
|
||||
|
||||
type noOpDoer struct{}
|
||||
|
||||
func (noOpDoer) Do(sub func(ctx Context)) {
|
||||
sub(Context{})
|
||||
}
|
||||
|
||||
// NoOpDoer is a Doer that just passes an empty context to the caller of Do.
|
||||
var NoOpDoer Doer = noOpDoer{}
|
||||
type BeginEnder interface {
|
||||
Begin()
|
||||
End()
|
||||
}
|
|
@ -10,7 +10,6 @@ import (
|
|||
|
||||
// Shader is an OpenGL shader program.
|
||||
type Shader struct {
|
||||
parent Doer
|
||||
program binder
|
||||
vertexFmt AttrFormat
|
||||
uniformFmt AttrFormat
|
||||
|
@ -22,9 +21,8 @@ type Shader struct {
|
|||
//
|
||||
// Note that vertexShader and fragmentShader parameters must contain the source code, they're
|
||||
// not filenames.
|
||||
func NewShader(parent Doer, vertexFmt, uniformFmt AttrFormat, vertexShader, fragmentShader string) (*Shader, error) {
|
||||
func NewShader(vertexFmt, uniformFmt AttrFormat, vertexShader, fragmentShader string) (*Shader, error) {
|
||||
shader := &Shader{
|
||||
parent: parent,
|
||||
program: binder{
|
||||
restoreLoc: gl.CURRENT_PROGRAM,
|
||||
bindFunc: func(obj uint32) {
|
||||
|
@ -36,84 +34,74 @@ func NewShader(parent Doer, vertexFmt, uniformFmt AttrFormat, vertexShader, frag
|
|||
uniforms: make(map[string]int32),
|
||||
}
|
||||
|
||||
var err error
|
||||
parent.Do(func(ctx Context) {
|
||||
err = DoErr(func() error {
|
||||
var vshader, fshader uint32
|
||||
var vshader, fshader uint32
|
||||
|
||||
// vertex shader
|
||||
{
|
||||
vshader = gl.CreateShader(gl.VERTEX_SHADER)
|
||||
src, free := gl.Strs(vertexShader)
|
||||
defer free()
|
||||
length := int32(len(vertexShader))
|
||||
gl.ShaderSource(vshader, 1, src, &length)
|
||||
gl.CompileShader(vshader)
|
||||
// vertex shader
|
||||
{
|
||||
vshader = gl.CreateShader(gl.VERTEX_SHADER)
|
||||
src, free := gl.Strs(vertexShader)
|
||||
defer free()
|
||||
length := int32(len(vertexShader))
|
||||
gl.ShaderSource(vshader, 1, src, &length)
|
||||
gl.CompileShader(vshader)
|
||||
|
||||
var (
|
||||
success int32
|
||||
infoLog = make([]byte, 512)
|
||||
)
|
||||
gl.GetShaderiv(vshader, gl.COMPILE_STATUS, &success)
|
||||
if success == 0 {
|
||||
gl.GetShaderInfoLog(vshader, int32(len(infoLog)), nil, &infoLog[0])
|
||||
return fmt.Errorf("error compiling vertex shader: %s", string(infoLog))
|
||||
}
|
||||
var (
|
||||
success int32
|
||||
infoLog = make([]byte, 512)
|
||||
)
|
||||
gl.GetShaderiv(vshader, gl.COMPILE_STATUS, &success)
|
||||
if success == 0 {
|
||||
gl.GetShaderInfoLog(vshader, int32(len(infoLog)), nil, &infoLog[0])
|
||||
return nil, fmt.Errorf("error compiling vertex shader: %s", string(infoLog))
|
||||
}
|
||||
|
||||
defer gl.DeleteShader(vshader)
|
||||
}
|
||||
defer gl.DeleteShader(vshader)
|
||||
}
|
||||
|
||||
// fragment shader
|
||||
{
|
||||
fshader = gl.CreateShader(gl.FRAGMENT_SHADER)
|
||||
src, free := gl.Strs(fragmentShader)
|
||||
defer free()
|
||||
length := int32(len(fragmentShader))
|
||||
gl.ShaderSource(fshader, 1, src, &length)
|
||||
gl.CompileShader(fshader)
|
||||
// fragment shader
|
||||
{
|
||||
fshader = gl.CreateShader(gl.FRAGMENT_SHADER)
|
||||
src, free := gl.Strs(fragmentShader)
|
||||
defer free()
|
||||
length := int32(len(fragmentShader))
|
||||
gl.ShaderSource(fshader, 1, src, &length)
|
||||
gl.CompileShader(fshader)
|
||||
|
||||
var (
|
||||
success int32
|
||||
infoLog = make([]byte, 512)
|
||||
)
|
||||
gl.GetShaderiv(fshader, gl.COMPILE_STATUS, &success)
|
||||
if success == 0 {
|
||||
gl.GetShaderInfoLog(fshader, int32(len(infoLog)), nil, &infoLog[0])
|
||||
return fmt.Errorf("error compiling fragment shader: %s", string(infoLog))
|
||||
}
|
||||
var (
|
||||
success int32
|
||||
infoLog = make([]byte, 512)
|
||||
)
|
||||
gl.GetShaderiv(fshader, gl.COMPILE_STATUS, &success)
|
||||
if success == 0 {
|
||||
gl.GetShaderInfoLog(fshader, int32(len(infoLog)), nil, &infoLog[0])
|
||||
return nil, fmt.Errorf("error compiling fragment shader: %s", string(infoLog))
|
||||
}
|
||||
|
||||
defer gl.DeleteShader(fshader)
|
||||
}
|
||||
defer gl.DeleteShader(fshader)
|
||||
}
|
||||
|
||||
// shader program
|
||||
{
|
||||
shader.program.obj = gl.CreateProgram()
|
||||
gl.AttachShader(shader.program.obj, vshader)
|
||||
gl.AttachShader(shader.program.obj, fshader)
|
||||
gl.LinkProgram(shader.program.obj)
|
||||
// shader program
|
||||
{
|
||||
shader.program.obj = gl.CreateProgram()
|
||||
gl.AttachShader(shader.program.obj, vshader)
|
||||
gl.AttachShader(shader.program.obj, fshader)
|
||||
gl.LinkProgram(shader.program.obj)
|
||||
|
||||
var (
|
||||
success int32
|
||||
infoLog = make([]byte, 512)
|
||||
)
|
||||
gl.GetProgramiv(shader.program.obj, gl.LINK_STATUS, &success)
|
||||
if success == 0 {
|
||||
gl.GetProgramInfoLog(shader.program.obj, int32(len(infoLog)), nil, &infoLog[0])
|
||||
return fmt.Errorf("error linking shader program: %s", string(infoLog))
|
||||
}
|
||||
}
|
||||
var (
|
||||
success int32
|
||||
infoLog = make([]byte, 512)
|
||||
)
|
||||
gl.GetProgramiv(shader.program.obj, gl.LINK_STATUS, &success)
|
||||
if success == 0 {
|
||||
gl.GetProgramInfoLog(shader.program.obj, int32(len(infoLog)), nil, &infoLog[0])
|
||||
return nil, fmt.Errorf("error linking shader program: %s", string(infoLog))
|
||||
}
|
||||
}
|
||||
|
||||
// uniforms
|
||||
for name := range uniformFmt {
|
||||
loc := gl.GetUniformLocation(shader.program.obj, gl.Str(name+"\x00"))
|
||||
shader.uniforms[name] = loc
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
// uniforms
|
||||
for name := range uniformFmt {
|
||||
loc := gl.GetUniformLocation(shader.program.obj, gl.Str(name+"\x00"))
|
||||
shader.uniforms[name] = loc
|
||||
}
|
||||
|
||||
runtime.SetFinalizer(shader, (*Shader).delete)
|
||||
|
@ -163,76 +151,69 @@ func (s *Shader) UniformFormat() AttrFormat {
|
|||
// Attr{Type: Mat42}: mgl32.Mat4x2
|
||||
// Attr{Type: Mat43}: mgl32.Mat4x3
|
||||
// No other types are supported.
|
||||
//
|
||||
// The shader must be bound before calling this method.
|
||||
func (s *Shader) SetUniformAttr(attr Attr, value interface{}) (ok bool) {
|
||||
if !s.uniformFmt.Contains(attr) {
|
||||
return false
|
||||
}
|
||||
|
||||
DoNoBlock(func() {
|
||||
s.program.bind()
|
||||
|
||||
switch attr.Type {
|
||||
case Int:
|
||||
value := value.(int32)
|
||||
gl.Uniform1iv(s.uniforms[attr.Name], 1, &value)
|
||||
case Float:
|
||||
value := value.(float32)
|
||||
gl.Uniform1fv(s.uniforms[attr.Name], 1, &value)
|
||||
case Vec2:
|
||||
value := value.(mgl32.Vec2)
|
||||
gl.Uniform2fv(s.uniforms[attr.Name], 1, &value[0])
|
||||
case Vec3:
|
||||
value := value.(mgl32.Vec3)
|
||||
gl.Uniform3fv(s.uniforms[attr.Name], 1, &value[0])
|
||||
case Vec4:
|
||||
value := value.(mgl32.Vec4)
|
||||
gl.Uniform4fv(s.uniforms[attr.Name], 1, &value[0])
|
||||
case Mat2:
|
||||
value := value.(mgl32.Mat2)
|
||||
gl.UniformMatrix2fv(s.uniforms[attr.Name], 1, false, &value[0])
|
||||
case Mat23:
|
||||
value := value.(mgl32.Mat2x3)
|
||||
gl.UniformMatrix2x3fv(s.uniforms[attr.Name], 1, false, &value[0])
|
||||
case Mat24:
|
||||
value := value.(mgl32.Mat2x4)
|
||||
gl.UniformMatrix2x4fv(s.uniforms[attr.Name], 1, false, &value[0])
|
||||
case Mat3:
|
||||
value := value.(mgl32.Mat3)
|
||||
gl.UniformMatrix3fv(s.uniforms[attr.Name], 1, false, &value[0])
|
||||
case Mat32:
|
||||
value := value.(mgl32.Mat3x2)
|
||||
gl.UniformMatrix3x2fv(s.uniforms[attr.Name], 1, false, &value[0])
|
||||
case Mat34:
|
||||
value := value.(mgl32.Mat3x4)
|
||||
gl.UniformMatrix3x4fv(s.uniforms[attr.Name], 1, false, &value[0])
|
||||
case Mat4:
|
||||
value := value.(mgl32.Mat4)
|
||||
gl.UniformMatrix4fv(s.uniforms[attr.Name], 1, false, &value[0])
|
||||
case Mat42:
|
||||
value := value.(mgl32.Mat4x2)
|
||||
gl.UniformMatrix4x2fv(s.uniforms[attr.Name], 1, false, &value[0])
|
||||
case Mat43:
|
||||
value := value.(mgl32.Mat4x3)
|
||||
gl.UniformMatrix4x3fv(s.uniforms[attr.Name], 1, false, &value[0])
|
||||
default:
|
||||
panic("set uniform attr: invalid attribute type")
|
||||
}
|
||||
|
||||
s.program.restore()
|
||||
})
|
||||
switch attr.Type {
|
||||
case Int:
|
||||
value := value.(int32)
|
||||
gl.Uniform1iv(s.uniforms[attr.Name], 1, &value)
|
||||
case Float:
|
||||
value := value.(float32)
|
||||
gl.Uniform1fv(s.uniforms[attr.Name], 1, &value)
|
||||
case Vec2:
|
||||
value := value.(mgl32.Vec2)
|
||||
gl.Uniform2fv(s.uniforms[attr.Name], 1, &value[0])
|
||||
case Vec3:
|
||||
value := value.(mgl32.Vec3)
|
||||
gl.Uniform3fv(s.uniforms[attr.Name], 1, &value[0])
|
||||
case Vec4:
|
||||
value := value.(mgl32.Vec4)
|
||||
gl.Uniform4fv(s.uniforms[attr.Name], 1, &value[0])
|
||||
case Mat2:
|
||||
value := value.(mgl32.Mat2)
|
||||
gl.UniformMatrix2fv(s.uniforms[attr.Name], 1, false, &value[0])
|
||||
case Mat23:
|
||||
value := value.(mgl32.Mat2x3)
|
||||
gl.UniformMatrix2x3fv(s.uniforms[attr.Name], 1, false, &value[0])
|
||||
case Mat24:
|
||||
value := value.(mgl32.Mat2x4)
|
||||
gl.UniformMatrix2x4fv(s.uniforms[attr.Name], 1, false, &value[0])
|
||||
case Mat3:
|
||||
value := value.(mgl32.Mat3)
|
||||
gl.UniformMatrix3fv(s.uniforms[attr.Name], 1, false, &value[0])
|
||||
case Mat32:
|
||||
value := value.(mgl32.Mat3x2)
|
||||
gl.UniformMatrix3x2fv(s.uniforms[attr.Name], 1, false, &value[0])
|
||||
case Mat34:
|
||||
value := value.(mgl32.Mat3x4)
|
||||
gl.UniformMatrix3x4fv(s.uniforms[attr.Name], 1, false, &value[0])
|
||||
case Mat4:
|
||||
value := value.(mgl32.Mat4)
|
||||
gl.UniformMatrix4fv(s.uniforms[attr.Name], 1, false, &value[0])
|
||||
case Mat42:
|
||||
value := value.(mgl32.Mat4x2)
|
||||
gl.UniformMatrix4x2fv(s.uniforms[attr.Name], 1, false, &value[0])
|
||||
case Mat43:
|
||||
value := value.(mgl32.Mat4x3)
|
||||
gl.UniformMatrix4x3fv(s.uniforms[attr.Name], 1, false, &value[0])
|
||||
default:
|
||||
panic("set uniform attr: invalid attribute type")
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Do stars using a shader, executes sub, and stops using it.
|
||||
func (s *Shader) Do(sub func(Context)) {
|
||||
s.parent.Do(func(ctx Context) {
|
||||
DoNoBlock(func() {
|
||||
s.program.bind()
|
||||
})
|
||||
sub(ctx.WithShader(s))
|
||||
DoNoBlock(func() {
|
||||
s.program.restore()
|
||||
})
|
||||
})
|
||||
// Begin binds a shader program. This is necessary before using the shader.
|
||||
func (s *Shader) Begin() {
|
||||
s.program.bind()
|
||||
}
|
||||
|
||||
// End unbinds a shader program and restores the previous one.
|
||||
func (s *Shader) End() {
|
||||
s.program.restore()
|
||||
}
|
||||
|
|
|
@ -1,20 +1,21 @@
|
|||
package pixelgl
|
||||
|
||||
import "github.com/go-gl/gl/v3.3-core/gl"
|
||||
import "runtime"
|
||||
import (
|
||||
"runtime"
|
||||
|
||||
"github.com/go-gl/gl/v3.3-core/gl"
|
||||
)
|
||||
|
||||
// Texture is an OpenGL texture.
|
||||
type Texture struct {
|
||||
parent Doer
|
||||
tex binder
|
||||
width, height int
|
||||
}
|
||||
|
||||
// NewTexture creates a new texture with the specified width and height.
|
||||
// The pixels must be a sequence of RGBA values.
|
||||
func NewTexture(parent Doer, width, height int, smooth bool, pixels []uint8) (*Texture, error) {
|
||||
func NewTexture(width, height int, smooth bool, pixels []uint8) (*Texture, error) {
|
||||
texture := &Texture{
|
||||
parent: parent,
|
||||
tex: binder{
|
||||
restoreLoc: gl.TEXTURE_BINDING_2D,
|
||||
bindFunc: func(obj uint32) {
|
||||
|
@ -25,37 +26,35 @@ func NewTexture(parent Doer, width, height int, smooth bool, pixels []uint8) (*T
|
|||
height: height,
|
||||
}
|
||||
|
||||
parent.Do(func(ctx Context) {
|
||||
Do(func() {
|
||||
gl.GenTextures(1, &texture.tex.obj)
|
||||
defer texture.tex.bind().restore()
|
||||
gl.GenTextures(1, &texture.tex.obj)
|
||||
|
||||
gl.TexImage2D(
|
||||
gl.TEXTURE_2D,
|
||||
0,
|
||||
gl.RGBA,
|
||||
int32(width),
|
||||
int32(height),
|
||||
0,
|
||||
gl.RGBA,
|
||||
gl.UNSIGNED_BYTE,
|
||||
gl.Ptr(pixels),
|
||||
)
|
||||
texture.Begin()
|
||||
defer texture.End()
|
||||
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.MIRRORED_REPEAT)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.MIRRORED_REPEAT)
|
||||
gl.TexImage2D(
|
||||
gl.TEXTURE_2D,
|
||||
0,
|
||||
gl.RGBA,
|
||||
int32(width),
|
||||
int32(height),
|
||||
0,
|
||||
gl.RGBA,
|
||||
gl.UNSIGNED_BYTE,
|
||||
gl.Ptr(pixels),
|
||||
)
|
||||
|
||||
if smooth {
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
|
||||
} else {
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_NEAREST)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
|
||||
}
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.MIRRORED_REPEAT)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.MIRRORED_REPEAT)
|
||||
|
||||
gl.GenerateMipmap(gl.TEXTURE_2D)
|
||||
})
|
||||
})
|
||||
if smooth {
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
|
||||
} else {
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_NEAREST)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
|
||||
}
|
||||
|
||||
gl.GenerateMipmap(gl.TEXTURE_2D)
|
||||
|
||||
runtime.SetFinalizer(texture, (*Texture).delete)
|
||||
|
||||
|
@ -83,15 +82,12 @@ func (t *Texture) Height() int {
|
|||
return t.height
|
||||
}
|
||||
|
||||
// Do bind a texture, executes sub, and unbinds the texture.
|
||||
func (t *Texture) Do(sub func(Context)) {
|
||||
t.parent.Do(func(ctx Context) {
|
||||
DoNoBlock(func() {
|
||||
t.tex.bind()
|
||||
})
|
||||
sub(ctx)
|
||||
DoNoBlock(func() {
|
||||
t.tex.restore()
|
||||
})
|
||||
})
|
||||
// Begin binds a texture. This is necessary before using the texture.
|
||||
func (t *Texture) Begin() {
|
||||
t.tex.bind()
|
||||
}
|
||||
|
||||
// End unbinds a texture and restores the previous one.
|
||||
func (t *Texture) End() {
|
||||
t.tex.restore()
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
package pixelgl
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"runtime"
|
||||
"unsafe"
|
||||
|
||||
"github.com/go-gl/gl/v3.3-core/gl"
|
||||
"github.com/go-gl/mathgl/mgl32"
|
||||
|
@ -13,7 +12,6 @@ import (
|
|||
// VertexArray is an OpenGL vertex array object that also holds it's own vertex buffer object.
|
||||
// From the user's points of view, VertexArray is an array of vertices that can be drawn.
|
||||
type VertexArray struct {
|
||||
parent Doer
|
||||
vao, vbo, ebo binder
|
||||
numVertices, numIndices int
|
||||
format AttrFormat
|
||||
|
@ -21,14 +19,13 @@ type VertexArray struct {
|
|||
offset map[string]int
|
||||
}
|
||||
|
||||
// NewVertexArray creates a new empty vertex array and wraps another Doer around it.
|
||||
// NewVertexArray creates a new empty vertex array.
|
||||
//
|
||||
// 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 AttrFormat, numVertices int, indices []int) (*VertexArray, error) {
|
||||
func NewVertexArray(shader *Shader, numVertices int, indices []int) (*VertexArray, error) {
|
||||
va := &VertexArray{
|
||||
parent: parent,
|
||||
vao: binder{
|
||||
restoreLoc: gl.VERTEX_ARRAY_BINDING,
|
||||
bindFunc: func(obj uint32) {
|
||||
|
@ -48,65 +45,62 @@ func NewVertexArray(parent Doer, format AttrFormat, numVertices int, indices []i
|
|||
},
|
||||
},
|
||||
numVertices: numVertices,
|
||||
format: format,
|
||||
stride: format.Size(),
|
||||
format: shader.VertexFormat(),
|
||||
stride: shader.VertexFormat().Size(),
|
||||
offset: make(map[string]int),
|
||||
}
|
||||
|
||||
offset := 0
|
||||
for name, typ := range format {
|
||||
for name, typ := range va.format {
|
||||
switch typ {
|
||||
case Float, Vec2, Vec3, Vec4:
|
||||
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 attribute type")
|
||||
}
|
||||
va.offset[name] = offset
|
||||
offset += typ.Size()
|
||||
}
|
||||
|
||||
parent.Do(func(ctx Context) {
|
||||
Do(func() {
|
||||
gl.GenVertexArrays(1, &va.vao.obj)
|
||||
va.vao.bind()
|
||||
gl.GenVertexArrays(1, &va.vao.obj)
|
||||
|
||||
gl.GenBuffers(1, &va.vbo.obj)
|
||||
defer va.vbo.bind().restore()
|
||||
va.vao.bind()
|
||||
|
||||
emptyData := make([]byte, numVertices*va.stride)
|
||||
gl.BufferData(gl.ARRAY_BUFFER, len(emptyData), gl.Ptr(emptyData), gl.DYNAMIC_DRAW)
|
||||
gl.GenBuffers(1, &va.vbo.obj)
|
||||
defer va.vbo.bind().restore()
|
||||
|
||||
gl.GenBuffers(1, &va.ebo.obj)
|
||||
defer va.ebo.bind().restore()
|
||||
emptyData := make([]byte, numVertices*va.stride)
|
||||
gl.BufferData(gl.ARRAY_BUFFER, len(emptyData), gl.Ptr(emptyData), gl.DYNAMIC_DRAW)
|
||||
|
||||
for name, typ := range format {
|
||||
loc := gl.GetAttribLocation(ctx.Shader().ID(), gl.Str(name+"\x00"))
|
||||
gl.GenBuffers(1, &va.ebo.obj)
|
||||
defer va.ebo.bind().restore()
|
||||
|
||||
var size int32
|
||||
switch typ {
|
||||
case Float:
|
||||
size = 1
|
||||
case Vec2:
|
||||
size = 2
|
||||
case Vec3:
|
||||
size = 3
|
||||
case Vec4:
|
||||
size = 4
|
||||
}
|
||||
for name, typ := range va.format {
|
||||
loc := gl.GetAttribLocation(shader.ID(), gl.Str(name+"\x00"))
|
||||
|
||||
gl.VertexAttribPointer(
|
||||
uint32(loc),
|
||||
size,
|
||||
gl.FLOAT,
|
||||
false,
|
||||
int32(va.stride),
|
||||
gl.PtrOffset(va.offset[name]),
|
||||
)
|
||||
gl.EnableVertexAttribArray(uint32(loc))
|
||||
}
|
||||
var size int32
|
||||
switch typ {
|
||||
case Float:
|
||||
size = 1
|
||||
case Vec2:
|
||||
size = 2
|
||||
case Vec3:
|
||||
size = 3
|
||||
case Vec4:
|
||||
size = 4
|
||||
}
|
||||
|
||||
va.vao.restore()
|
||||
})
|
||||
})
|
||||
gl.VertexAttribPointer(
|
||||
uint32(loc),
|
||||
size,
|
||||
gl.FLOAT,
|
||||
false,
|
||||
int32(va.stride),
|
||||
gl.PtrOffset(va.offset[name]),
|
||||
)
|
||||
gl.EnableVertexAttribArray(uint32(loc))
|
||||
}
|
||||
|
||||
va.vao.restore()
|
||||
|
||||
va.SetIndices(indices)
|
||||
|
||||
|
@ -141,13 +135,17 @@ func (va *VertexArray) VertexFormat() AttrFormat {
|
|||
}
|
||||
|
||||
// Draw draws a vertex array.
|
||||
//
|
||||
// The vertex array must be bound before calling this method.
|
||||
func (va *VertexArray) Draw() {
|
||||
va.Do(func(Context) {})
|
||||
gl.DrawElements(gl.TRIANGLES, int32(va.numIndices), gl.UNSIGNED_INT, gl.PtrOffset(0))
|
||||
}
|
||||
|
||||
// SetIndices sets the indices of triangles to be drawn. Triangles will be formed from the
|
||||
// vertices of the array as defined by these indices. The first drawn triangle is specified by
|
||||
// the first three indices, the second by the fourth through sixth and so on.
|
||||
//
|
||||
// The vertex array must be bound before calling this method.
|
||||
func (va *VertexArray) SetIndices(indices []int) {
|
||||
if len(indices)%3 != 0 {
|
||||
panic("vertex array set indices: number of indices not divisible by 3")
|
||||
|
@ -157,25 +155,23 @@ func (va *VertexArray) SetIndices(indices []int) {
|
|||
indices32[i] = uint32(indices[i])
|
||||
}
|
||||
va.numIndices = len(indices32)
|
||||
DoNoBlock(func() {
|
||||
va.ebo.bind()
|
||||
gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, 4*len(indices32), gl.Ptr(indices32), gl.DYNAMIC_DRAW)
|
||||
va.ebo.restore()
|
||||
})
|
||||
|
||||
gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, 4*len(indices32), gl.Ptr(indices32), gl.DYNAMIC_DRAW)
|
||||
}
|
||||
|
||||
// Indices returns the current indices of triangles to be drawn.
|
||||
//
|
||||
// The vertex array must be bound before calling this method.
|
||||
func (va *VertexArray) Indices() []int {
|
||||
indices32 := make([]uint32, va.numIndices)
|
||||
Do(func() {
|
||||
va.ebo.bind()
|
||||
gl.GetBufferSubData(gl.ELEMENT_ARRAY_BUFFER, 0, 4*len(indices32), gl.Ptr(indices32))
|
||||
va.ebo.restore()
|
||||
})
|
||||
|
||||
gl.GetBufferSubData(gl.ELEMENT_ARRAY_BUFFER, 0, 4*len(indices32), gl.Ptr(indices32))
|
||||
|
||||
indices := make([]int, len(indices32))
|
||||
for i := range indices {
|
||||
indices[i] = int(indices32[i])
|
||||
}
|
||||
|
||||
return indices
|
||||
}
|
||||
|
||||
|
@ -191,6 +187,8 @@ func (va *VertexArray) Indices() []int {
|
|||
// Attr{Type: Vec3}: mgl32.Vec3
|
||||
// Attr{Type: Vec4}: mgl32.Vec4
|
||||
// No other types are supported.
|
||||
//
|
||||
// The vertex array must be bound before calling this method.
|
||||
func (va *VertexArray) SetVertexAttr(vertex int, attr Attr, value interface{}) (ok bool) {
|
||||
if vertex < 0 || vertex >= va.numVertices {
|
||||
panic("set vertex attr: invalid vertex index")
|
||||
|
@ -200,30 +198,24 @@ func (va *VertexArray) SetVertexAttr(vertex int, attr Attr, value interface{}) (
|
|||
return false
|
||||
}
|
||||
|
||||
DoNoBlock(func() {
|
||||
va.vbo.bind()
|
||||
offset := va.stride*vertex + va.offset[attr.Name]
|
||||
|
||||
offset := va.stride*vertex + va.offset[attr.Name]
|
||||
|
||||
switch attr.Type {
|
||||
case Float:
|
||||
value := value.(float32)
|
||||
gl.BufferSubData(gl.ARRAY_BUFFER, offset, attr.Type.Size(), unsafe.Pointer(&value))
|
||||
case Vec2:
|
||||
value := value.(mgl32.Vec2)
|
||||
gl.BufferSubData(gl.ARRAY_BUFFER, offset, attr.Type.Size(), unsafe.Pointer(&value))
|
||||
case Vec3:
|
||||
value := value.(mgl32.Vec3)
|
||||
gl.BufferSubData(gl.ARRAY_BUFFER, offset, attr.Type.Size(), unsafe.Pointer(&value))
|
||||
case Vec4:
|
||||
value := value.(mgl32.Vec4)
|
||||
gl.BufferSubData(gl.ARRAY_BUFFER, offset, attr.Type.Size(), unsafe.Pointer(&value))
|
||||
default:
|
||||
panic("set vertex attr: invalid attribute type")
|
||||
}
|
||||
|
||||
va.vbo.restore()
|
||||
})
|
||||
switch attr.Type {
|
||||
case Float:
|
||||
value := value.(float32)
|
||||
gl.BufferSubData(gl.ARRAY_BUFFER, offset, attr.Type.Size(), unsafe.Pointer(&value))
|
||||
case Vec2:
|
||||
value := value.(mgl32.Vec2)
|
||||
gl.BufferSubData(gl.ARRAY_BUFFER, offset, attr.Type.Size(), unsafe.Pointer(&value))
|
||||
case Vec3:
|
||||
value := value.(mgl32.Vec3)
|
||||
gl.BufferSubData(gl.ARRAY_BUFFER, offset, attr.Type.Size(), unsafe.Pointer(&value))
|
||||
case Vec4:
|
||||
value := value.(mgl32.Vec4)
|
||||
gl.BufferSubData(gl.ARRAY_BUFFER, offset, attr.Type.Size(), unsafe.Pointer(&value))
|
||||
default:
|
||||
panic("set vertex attr: invalid attribute type")
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
@ -234,6 +226,8 @@ func (va *VertexArray) SetVertexAttr(vertex int, attr Attr, value interface{}) (
|
|||
// out of range, this method panics.
|
||||
//
|
||||
// The type of the returned value follows the same rules as with SetVertexAttr.
|
||||
//
|
||||
// The vertex array must be bound before calling this method.
|
||||
func (va *VertexArray) VertexAttr(vertex int, attr Attr) (value interface{}, ok bool) {
|
||||
if vertex < 0 || vertex >= va.numVertices {
|
||||
panic("vertex attr: invalid vertex index")
|
||||
|
@ -243,34 +237,28 @@ func (va *VertexArray) VertexAttr(vertex int, attr Attr) (value interface{}, ok
|
|||
return nil, false
|
||||
}
|
||||
|
||||
Do(func() {
|
||||
va.vbo.bind()
|
||||
offset := va.stride*vertex + va.offset[attr.Name]
|
||||
|
||||
offset := va.stride*vertex + va.offset[attr.Name]
|
||||
|
||||
switch attr.Type {
|
||||
case Float:
|
||||
var data float32
|
||||
gl.GetBufferSubData(gl.ARRAY_BUFFER, offset, attr.Type.Size(), unsafe.Pointer(&data))
|
||||
value = data
|
||||
case Vec2:
|
||||
var data mgl32.Vec2
|
||||
gl.GetBufferSubData(gl.ARRAY_BUFFER, offset, attr.Type.Size(), unsafe.Pointer(&data))
|
||||
value = data
|
||||
case Vec3:
|
||||
var data mgl32.Vec3
|
||||
gl.GetBufferSubData(gl.ARRAY_BUFFER, offset, attr.Type.Size(), unsafe.Pointer(&data))
|
||||
value = data
|
||||
case Vec4:
|
||||
var data mgl32.Vec4
|
||||
gl.GetBufferSubData(gl.ARRAY_BUFFER, offset, attr.Type.Size(), unsafe.Pointer(&data))
|
||||
value = data
|
||||
default:
|
||||
panic("set vertex attr: invalid attribute type")
|
||||
}
|
||||
|
||||
va.vbo.restore()
|
||||
})
|
||||
switch attr.Type {
|
||||
case Float:
|
||||
var data float32
|
||||
gl.GetBufferSubData(gl.ARRAY_BUFFER, offset, attr.Type.Size(), unsafe.Pointer(&data))
|
||||
value = data
|
||||
case Vec2:
|
||||
var data mgl32.Vec2
|
||||
gl.GetBufferSubData(gl.ARRAY_BUFFER, offset, attr.Type.Size(), unsafe.Pointer(&data))
|
||||
value = data
|
||||
case Vec3:
|
||||
var data mgl32.Vec3
|
||||
gl.GetBufferSubData(gl.ARRAY_BUFFER, offset, attr.Type.Size(), unsafe.Pointer(&data))
|
||||
value = data
|
||||
case Vec4:
|
||||
var data mgl32.Vec4
|
||||
gl.GetBufferSubData(gl.ARRAY_BUFFER, offset, attr.Type.Size(), unsafe.Pointer(&data))
|
||||
value = data
|
||||
default:
|
||||
panic("set vertex attr: invalid attribute type")
|
||||
}
|
||||
|
||||
return value, true
|
||||
}
|
||||
|
@ -279,6 +267,8 @@ func (va *VertexArray) VertexAttr(vertex int, attr Attr) (value interface{}, ok
|
|||
// will be set to zero.
|
||||
//
|
||||
// Not existing attributes are silently skipped.
|
||||
//
|
||||
// The vertex array must be bound before calling this method.
|
||||
func (va *VertexArray) SetVertex(vertex int, values map[Attr]interface{}) {
|
||||
if vertex < 0 || vertex >= va.numVertices {
|
||||
panic("set vertex: invalid vertex index")
|
||||
|
@ -310,17 +300,13 @@ func (va *VertexArray) SetVertex(vertex int, values map[Attr]interface{}) {
|
|||
}
|
||||
}
|
||||
|
||||
DoNoBlock(func() {
|
||||
va.vbo.bind()
|
||||
|
||||
offset := va.stride * vertex
|
||||
gl.BufferSubData(gl.ARRAY_BUFFER, offset, len(data)*4, gl.Ptr(data))
|
||||
|
||||
va.vbo.restore()
|
||||
})
|
||||
offset := va.stride * vertex
|
||||
gl.BufferSubData(gl.ARRAY_BUFFER, offset, len(data)*4, gl.Ptr(data))
|
||||
}
|
||||
|
||||
// Vertex returns values of all vertex attributes of the specified vertex in a map.
|
||||
//
|
||||
// The vertex array must be bound before calling this method.
|
||||
func (va *VertexArray) Vertex(vertex int) (values map[Attr]interface{}) {
|
||||
if vertex < 0 || vertex >= va.numVertices {
|
||||
panic("set vertex: invalid vertex index")
|
||||
|
@ -328,14 +314,8 @@ func (va *VertexArray) Vertex(vertex int) (values map[Attr]interface{}) {
|
|||
|
||||
data := make([]float32, va.format.Size()/4)
|
||||
|
||||
Do(func() {
|
||||
va.vbo.bind()
|
||||
|
||||
offset := va.stride * vertex
|
||||
gl.GetBufferSubData(gl.ARRAY_BUFFER, offset, len(data)*4, gl.Ptr(data))
|
||||
|
||||
va.vbo.restore()
|
||||
})
|
||||
offset := va.stride * vertex
|
||||
gl.GetBufferSubData(gl.ARRAY_BUFFER, offset, len(data)*4, gl.Ptr(data))
|
||||
|
||||
values = make(map[Attr]interface{})
|
||||
|
||||
|
@ -369,6 +349,8 @@ func (va *VertexArray) Vertex(vertex int) (values map[Attr]interface{}) {
|
|||
// array, this method panics.
|
||||
//
|
||||
// Not existing attributes are silently skipped.
|
||||
//
|
||||
// The vertex array must be bound before calling this metod.
|
||||
func (va *VertexArray) SetVertices(vertices []map[Attr]interface{}) {
|
||||
if len(vertices) != va.numVertices {
|
||||
panic("set vertex array: wrong number of supplied vertices")
|
||||
|
@ -402,23 +384,17 @@ func (va *VertexArray) SetVertices(vertices []map[Attr]interface{}) {
|
|||
}
|
||||
}
|
||||
|
||||
DoNoBlock(func() {
|
||||
va.vbo.bind()
|
||||
gl.BufferSubData(gl.ARRAY_BUFFER, 0, len(data)*4, gl.Ptr(data))
|
||||
va.vbo.restore()
|
||||
})
|
||||
gl.BufferSubData(gl.ARRAY_BUFFER, 0, len(data)*4, gl.Ptr(data))
|
||||
}
|
||||
|
||||
// Vertices returns values of vertex attributes of all vertices in a vertex array in a slice
|
||||
// of maps.
|
||||
//
|
||||
// The vertex array must be bound before calling this metod.
|
||||
func (va *VertexArray) Vertices() (vertices []map[Attr]interface{}) {
|
||||
data := make([]float32, va.numVertices*va.format.Size()/4)
|
||||
|
||||
Do(func() {
|
||||
va.vbo.bind()
|
||||
gl.GetBufferSubData(gl.ARRAY_BUFFER, 0, len(data)*4, gl.Ptr(data))
|
||||
va.vbo.restore()
|
||||
})
|
||||
gl.GetBufferSubData(gl.ARRAY_BUFFER, 0, len(data)*4, gl.Ptr(data))
|
||||
|
||||
vertices = make([]map[Attr]interface{}, va.numVertices)
|
||||
|
||||
|
@ -453,17 +429,16 @@ func (va *VertexArray) Vertices() (vertices []map[Attr]interface{}) {
|
|||
return vertices
|
||||
}
|
||||
|
||||
// Do binds a vertex arrray and it's associated vertex buffer, executes sub, and unbinds the
|
||||
// vertex array and it's vertex buffer.
|
||||
func (va *VertexArray) Do(sub func(Context)) {
|
||||
va.parent.Do(func(ctx Context) {
|
||||
DoNoBlock(func() {
|
||||
va.vao.bind()
|
||||
})
|
||||
sub(ctx)
|
||||
DoNoBlock(func() {
|
||||
gl.DrawElements(gl.TRIANGLES, int32(va.numIndices), gl.UNSIGNED_INT, gl.PtrOffset(0))
|
||||
va.vao.restore()
|
||||
})
|
||||
})
|
||||
// Begin binds a vertex array. This is neccessary before using the vertex array.
|
||||
func (va *VertexArray) Begin() {
|
||||
va.vao.bind()
|
||||
va.vbo.bind()
|
||||
va.ebo.bind()
|
||||
}
|
||||
|
||||
// End unbinds a vertex array and restores the previous one.
|
||||
func (va *VertexArray) End() {
|
||||
va.ebo.restore()
|
||||
va.vbo.restore()
|
||||
va.vao.restore()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue