go-opengl-pixel/pixelgl/vertex.go

307 lines
8.4 KiB
Go
Raw Normal View History

2016-11-23 07:03:23 -06:00
package pixelgl
2016-11-23 11:06:55 -06:00
import (
"unsafe"
2016-11-23 11:06:55 -06:00
"github.com/go-gl/gl/v3.3-core/gl"
2016-12-01 09:44:54 -06:00
"github.com/go-gl/mathgl/mgl32"
2016-11-23 11:06:55 -06:00
"github.com/pkg/errors"
)
2016-11-23 08:59:21 -06:00
2016-11-23 07:03:23 -06:00
// VertexFormat defines a data format in a vertex buffer.
//
// Example:
//
// VertexFormat{"position": {Position, Vec2}, "colr": {Color, Vec4}, "texCoord": {TexCoord, Vec2}}
2016-11-30 10:41:48 -06:00
//
// Note: vertex array currently doesn't support matrices in vertex format.
2016-12-02 12:29:39 -06:00
type VertexFormat []Attr
2016-11-23 07:03:23 -06:00
2016-11-29 16:11:53 -06:00
// Size calculates the total size of a single vertex in this vertex format (sum of the sizes of all vertex attributes).
2016-11-23 08:59:21 -06:00
func (vf VertexFormat) Size() int {
2016-11-29 16:11:53 -06:00
total := 0
for _, attr := range vf {
total += attr.Type.Size()
2016-11-23 08:59:21 -06:00
}
2016-11-29 16:11:53 -06:00
return total
2016-11-23 07:03:23 -06:00
}
2016-11-23 08:59:21 -06:00
// VertexUsage specifies how often the vertex array data will be updated.
type VertexUsage int
const (
2016-11-23 09:14:40 -06:00
// StaticUsage means the data never or rarely gets updated.
StaticUsage VertexUsage = gl.STATIC_DRAW
// DynamicUsage means the data gets updated often.
DynamicUsage VertexUsage = gl.DYNAMIC_DRAW
// StreamUsage means the data gets updated every frame.
StreamUsage VertexUsage = gl.STREAM_DRAW
2016-11-23 08:59:21 -06:00
)
// 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 {
2016-12-08 08:25:00 -06:00
parent Doer
vao, vbo, ebo binder
2016-12-08 08:25:00 -06:00
vertexNum, indexNum int
format VertexFormat
usage VertexUsage
stride int
attrs map[Attr]int
2016-11-23 08:59:21 -06:00
}
2016-11-29 16:11:53 -06:00
// NewVertexArray creates a new empty vertex array and wraps another Doer around it.
2016-12-08 08:25:00 -06:00
//
// 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) {
2016-11-23 08:59:21 -06:00
va := &VertexArray{
parent: parent,
vao: binder{
restoreLoc: gl.VERTEX_ARRAY_BINDING,
bindFunc: func(obj uint32) {
gl.BindVertexArray(obj)
},
},
vbo: binder{
restoreLoc: gl.ARRAY_BUFFER_BINDING,
bindFunc: func(obj uint32) {
gl.BindBuffer(gl.ARRAY_BUFFER, obj)
},
},
ebo: binder{
restoreLoc: gl.ELEMENT_ARRAY_BUFFER_BINDING,
bindFunc: func(obj uint32) {
gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, obj)
},
},
vertexNum: vertexNum,
2016-12-08 08:25:00 -06:00
format: format,
usage: usage,
stride: format.Size(),
attrs: make(map[Attr]int),
2016-11-23 08:59:21 -06:00
}
2016-11-24 07:37:11 -06:00
offset := 0
for _, attr := range format {
2016-11-29 16:11:53 -06:00
switch attr.Type {
2016-12-02 11:21:28 -06:00
case Float, Vec2, Vec3, Vec4:
2016-11-29 16:11:53 -06:00
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
2016-11-29 16:11:53 -06:00
offset += attr.Type.Size()
}
2016-11-28 16:26:56 -06:00
parent.Do(func(ctx Context) {
2016-12-08 08:25:00 -06:00
Do(func() {
gl.GenVertexArrays(1, &va.vao.obj)
va.vao.bind()
gl.GenBuffers(1, &va.vbo.obj)
defer va.vbo.bind().restore()
2016-11-29 16:11:53 -06:00
2016-12-08 08:25:00 -06:00
emptyData := make([]byte, vertexNum*va.stride)
2016-11-29 16:11:53 -06:00
gl.BufferData(gl.ARRAY_BUFFER, len(emptyData), gl.Ptr(emptyData), uint32(usage))
gl.GenBuffers(1, &va.ebo.obj)
defer va.ebo.bind().restore()
2016-12-08 08:25:00 -06:00
offset := 0
2016-12-02 12:29:39 -06:00
for i, attr := range format {
2016-11-29 16:11:53 -06:00
var size int32
switch attr.Type {
2016-12-02 11:21:28 -06:00
case Float:
2016-11-29 16:11:53 -06:00
size = 1
case Vec2:
size = 2
case Vec3:
size = 3
case Vec4:
size = 4
}
gl.VertexAttribPointer(
2016-12-02 12:29:39 -06:00
uint32(i),
2016-11-29 16:11:53 -06:00
size,
2016-12-02 11:55:52 -06:00
gl.FLOAT,
false,
2016-11-29 16:11:53 -06:00
int32(va.stride),
gl.PtrOffset(offset),
)
2016-12-02 12:29:39 -06:00
gl.EnableVertexAttribArray(uint32(i))
2016-11-29 16:11:53 -06:00
offset += attr.Type.Size()
}
va.vao.restore()
})
2016-11-23 08:59:21 -06:00
})
2016-12-08 08:25:00 -06:00
va.SetIndices(indices)
2016-11-24 07:37:11 -06:00
2016-11-23 11:06:55 -06:00
return va, nil
2016-11-23 08:59:21 -06:00
}
2016-11-23 13:06:34 -06:00
// Delete deletes a vertex array and it's associated vertex buffer. Don't use a vertex array after deletion.
func (va *VertexArray) Delete() {
2016-11-28 16:26:56 -06:00
va.parent.Do(func(ctx Context) {
2016-11-25 15:56:17 -06:00
DoNoBlock(func() {
gl.DeleteVertexArrays(1, &va.vao.obj)
gl.DeleteBuffers(1, &va.vbo.obj)
2016-11-25 15:56:17 -06:00
})
2016-11-23 13:06:34 -06:00
})
}
// ID returns an OpenGL identifier of a vertex array.
func (va *VertexArray) ID() uint32 {
return va.vao.obj
}
2016-12-08 14:02:21 -06:00
// VertexNum returns the number of vertices in a vertex array.
func (va *VertexArray) VertexNum() int {
2016-12-08 08:25:00 -06:00
return va.vertexNum
2016-11-29 16:11:53 -06:00
}
2016-11-23 08:59:21 -06:00
// VertexFormat returns the format of the vertices inside a vertex array.
//
// Do not change this format!
func (va *VertexArray) VertexFormat() VertexFormat {
return va.format
}
2016-12-08 08:25:00 -06:00
// VertexUsage returns the usage of the verteices inside a vertex array.
func (va *VertexArray) VertexUsage() VertexUsage {
return va.usage
2016-11-23 08:59:21 -06:00
}
// Draw draws a vertex array.
func (va *VertexArray) Draw() {
2016-11-28 16:26:56 -06:00
va.Do(func(Context) {})
2016-11-23 08:59:21 -06:00
}
2016-12-08 08:25:00 -06:00
// 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.
func (va *VertexArray) SetIndices(indices []int) {
if len(indices)%3 != 0 {
panic("vertex array set indices: number of indices not divisible by 3")
}
indices32 := make([]uint32, len(indices))
for i := range indices32 {
indices32[i] = uint32(indices[i])
}
va.indexNum = len(indices32)
DoNoBlock(func() {
defer va.ebo.bind().restore()
2016-12-08 08:25:00 -06:00
gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, 4*len(indices32), gl.Ptr(indices32), uint32(va.usage))
})
}
// SetVertexAttr sets the value of the specified vertex attribute of the specified vertex.
//
// If the vertex attribute does not exist, this method returns false. If the vertex is out of range,
// this method panics.
//
// Supplied value must correspond to the type of the attribute. Correct types are these (righ-hand is the type of the value):
// Attr{Type: Float}: float32
// Attr{Type: Vec2}: mgl32.Vec2
// Attr{Type: Vec3}: mgl32.Vec3
// Attr{Type: Vec4}: mgl32.Vec4
// No other types are supported.
func (va *VertexArray) SetVertexAttr(vertex int, attr Attr, value interface{}) (ok bool) {
2016-12-08 08:25:00 -06:00
if vertex < 0 || vertex >= va.vertexNum {
panic("set vertex attr: invalid vertex index")
}
if _, ok := va.attrs[attr]; !ok {
return false
}
DoNoBlock(func() {
defer va.vbo.bind().restore()
offset := va.stride*vertex + va.attrs[attr]
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
}
// VertexAttr returns the current value of the specified vertex attribute of the specified vertex.
//
// If the vertex attribute does not exist, this method returns nil and false. If the vertex is out of range,
// this method panics.
//
// The type of the returned value follows the same rules as with SetVertexAttr.
func (va *VertexArray) VertexAttr(vertex int, attr Attr) (value interface{}, ok bool) {
if vertex < 0 || vertex >= va.vertexNum {
panic("vertex attr: invalid vertex index")
}
2016-11-26 15:51:20 -06:00
if _, ok := va.attrs[attr]; !ok {
return nil, false
2016-11-26 15:51:20 -06:00
}
Do(func() {
defer va.vbo.bind().restore()
2016-11-29 16:11:53 -06:00
offset := va.stride*vertex + va.attrs[attr]
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
}
2016-11-23 08:59:21 -06:00
})
return value, true
2016-11-23 08:59:21 -06:00
}
// Do binds a vertex arrray and it's associated vertex buffer, executes sub, and unbinds the vertex array and it's vertex buffer.
2016-11-28 16:26:56 -06:00
func (va *VertexArray) Do(sub func(Context)) {
va.parent.Do(func(ctx Context) {
DoNoBlock(func() {
va.vao.bind()
})
2016-11-28 16:26:56 -06:00
sub(ctx)
DoNoBlock(func() {
2016-12-08 08:25:00 -06:00
gl.DrawElements(gl.TRIANGLES, int32(va.indexNum), gl.UNSIGNED_INT, gl.PtrOffset(0))
va.vao.restore()
})
2016-11-23 08:59:21 -06:00
})
}