2016-11-23 07:03:23 -06:00
|
|
|
package pixelgl
|
|
|
|
|
2016-11-23 11:06:55 -06:00
|
|
|
import (
|
2016-11-29 16:11:53 -06:00
|
|
|
"unsafe"
|
|
|
|
|
2016-11-23 11:06:55 -06:00
|
|
|
"github.com/go-gl/gl/v3.3-core/gl"
|
|
|
|
"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:
|
|
|
|
//
|
2016-11-29 16:11:53 -06:00
|
|
|
// VertexFormat{{Position, Vec2}, {Color, Vec4}, {TexCoord, Vec2}, {Visible, Bool}}
|
|
|
|
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
|
|
|
)
|
|
|
|
|
|
|
|
// VertexDrawMode specifies how should the vertices be drawn.
|
|
|
|
type VertexDrawMode int
|
|
|
|
|
|
|
|
const (
|
2016-11-23 09:14:40 -06:00
|
|
|
// PointsDrawMode just draws individual points
|
|
|
|
PointsDrawMode VertexDrawMode = gl.POINTS
|
|
|
|
|
|
|
|
// LinesDrawMode takes pairs of vertices and draws a line from each pair
|
|
|
|
LinesDrawMode VertexDrawMode = gl.LINES
|
|
|
|
|
|
|
|
// LineStripDrawMode takes each two subsequent vertices and draws a line from each two
|
|
|
|
LineStripDrawMode VertexDrawMode = gl.LINE_STRIP
|
|
|
|
|
|
|
|
// LineLoopDrawMode is same as line strip, but also draws a line between the first and the last vertex
|
|
|
|
LineLoopDrawMode VertexDrawMode = gl.LINE_LOOP
|
|
|
|
|
|
|
|
// TrianglesDrawMode takes triples of vertices and draws a triangle from each triple
|
|
|
|
TrianglesDrawMode VertexDrawMode = gl.TRIANGLES
|
|
|
|
|
|
|
|
// TriangleStripDrawMode takes each three subsequent vertices and draws a triangle from each three
|
|
|
|
TriangleStripDrawMode VertexDrawMode = gl.TRIANGLE_STRIP
|
|
|
|
|
|
|
|
// TriangleFanDrawMode draws triangles from the first vertex and each two subsequent: {0, 1, 2, 3} -> {0, 1, 2}, {0, 2, 3}.
|
|
|
|
TriangleFanDrawMode VertexDrawMode = gl.TRIANGLE_FAN
|
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-11-25 15:49:56 -06:00
|
|
|
parent Doer
|
2016-11-23 08:59:21 -06:00
|
|
|
format VertexFormat
|
2016-11-26 15:42:26 -06:00
|
|
|
stride int
|
|
|
|
count int
|
2016-11-29 16:11:53 -06:00
|
|
|
attrs map[Attr]int
|
2016-11-23 08:59:21 -06:00
|
|
|
vao uint32
|
|
|
|
vbo uint32
|
|
|
|
mode VertexDrawMode
|
|
|
|
}
|
|
|
|
|
2016-11-29 16:11:53 -06:00
|
|
|
// NewVertexArray creates a new empty vertex array and wraps another Doer around it.
|
|
|
|
func NewVertexArray(parent Doer, format VertexFormat, mode VertexDrawMode, usage VertexUsage, count int) (*VertexArray, error) {
|
2016-11-23 08:59:21 -06:00
|
|
|
va := &VertexArray{
|
|
|
|
parent: parent,
|
|
|
|
format: format,
|
2016-11-29 16:11:53 -06:00
|
|
|
count: count,
|
2016-11-26 15:42:26 -06:00
|
|
|
stride: format.Size(),
|
2016-11-29 16:11:53 -06:00
|
|
|
attrs: make(map[Attr]int),
|
2016-11-23 08:59:21 -06:00
|
|
|
mode: mode,
|
|
|
|
}
|
2016-11-24 07:37:11 -06:00
|
|
|
|
2016-11-26 15:42:26 -06:00
|
|
|
offset := 0
|
|
|
|
for _, attr := range format {
|
2016-11-29 16:11:53 -06:00
|
|
|
switch attr.Type {
|
|
|
|
case Bool, Int, Float, Vec2, Vec3, Vec4:
|
|
|
|
default:
|
|
|
|
return nil, errors.New("failed to create vertex array: invalid vertex format: invalid attribute type")
|
|
|
|
}
|
2016-11-26 15:42:26 -06:00
|
|
|
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-26 15:42:26 -06:00
|
|
|
}
|
|
|
|
|
2016-11-25 15:56:17 -06:00
|
|
|
var err error
|
2016-11-28 16:26:56 -06:00
|
|
|
parent.Do(func(ctx Context) {
|
2016-11-25 15:56:17 -06:00
|
|
|
err = DoGLErr(func() {
|
2016-11-25 15:49:56 -06:00
|
|
|
gl.GenVertexArrays(1, &va.vao)
|
|
|
|
gl.BindVertexArray(va.vao)
|
|
|
|
|
|
|
|
gl.GenBuffers(1, &va.vbo)
|
|
|
|
gl.BindBuffer(gl.ARRAY_BUFFER, va.vbo)
|
2016-11-29 16:11:53 -06:00
|
|
|
|
|
|
|
emptyData := make([]byte, count*va.stride)
|
|
|
|
gl.BufferData(gl.ARRAY_BUFFER, len(emptyData), gl.Ptr(emptyData), uint32(usage))
|
2016-11-25 15:49:56 -06:00
|
|
|
|
|
|
|
offset := 0
|
|
|
|
for i, attr := range format {
|
2016-11-29 16:11:53 -06:00
|
|
|
//XXX: ugly but OpenGL is so inconsistent
|
|
|
|
|
|
|
|
var size int32
|
|
|
|
switch attr.Type {
|
|
|
|
case Bool, Int, Float:
|
|
|
|
size = 1
|
|
|
|
case Vec2:
|
|
|
|
size = 2
|
|
|
|
case Vec3:
|
|
|
|
size = 3
|
|
|
|
case Vec4:
|
|
|
|
size = 4
|
|
|
|
}
|
|
|
|
|
|
|
|
var xtype uint32
|
|
|
|
switch attr.Type {
|
|
|
|
case Bool:
|
|
|
|
xtype = gl.BOOL
|
|
|
|
case Int:
|
|
|
|
xtype = gl.INT
|
|
|
|
case Float, Vec2, Vec3, Vec4:
|
|
|
|
xtype = gl.DOUBLE
|
|
|
|
}
|
|
|
|
|
2016-11-25 15:49:56 -06:00
|
|
|
gl.VertexAttribPointer(
|
|
|
|
uint32(i),
|
2016-11-29 16:11:53 -06:00
|
|
|
size,
|
|
|
|
xtype,
|
2016-11-25 15:49:56 -06:00
|
|
|
false,
|
2016-11-29 16:11:53 -06:00
|
|
|
int32(va.stride),
|
|
|
|
gl.PtrOffset(offset),
|
2016-11-25 15:49:56 -06:00
|
|
|
)
|
|
|
|
gl.EnableVertexAttribArray(uint32(i))
|
2016-11-29 16:11:53 -06:00
|
|
|
offset += attr.Type.Size()
|
2016-11-25 15:49:56 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
gl.BindBuffer(gl.ARRAY_BUFFER, 0)
|
|
|
|
gl.BindVertexArray(0)
|
|
|
|
})
|
2016-11-23 08:59:21 -06:00
|
|
|
})
|
2016-11-23 11:06:55 -06:00
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "failed to create a vertex array")
|
|
|
|
}
|
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)
|
|
|
|
gl.DeleteBuffers(1, &va.vbo)
|
|
|
|
})
|
2016-11-23 13:06:34 -06:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2016-11-29 16:11:53 -06:00
|
|
|
// Count returns the number of vertices in a vertex array.
|
|
|
|
func (va *VertexArray) Count() int {
|
|
|
|
return va.count
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetDrawMode sets the draw mode of a vertex array. Subsequent calls to Draw will use this draw mode.
|
|
|
|
func (va *VertexArray) SetDrawMode(mode VertexDrawMode) {
|
2016-11-24 07:10:33 -06:00
|
|
|
DoNoBlock(func() {
|
2016-11-23 08:59:21 -06:00
|
|
|
va.mode = mode
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// DrawMode returns the most recently set draw mode of a vertex array.
|
|
|
|
func (va *VertexArray) DrawMode() VertexDrawMode {
|
|
|
|
mode := DoVal(func() interface{} {
|
|
|
|
return va.mode
|
|
|
|
})
|
|
|
|
return mode.(VertexDrawMode)
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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-11-29 16:11:53 -06:00
|
|
|
// SetVertex sets the value of all attributes of a vertex.
|
|
|
|
// Argument data must point to a slice/array containing the new vertex data.
|
|
|
|
func (va *VertexArray) SetVertex(vertex int, data unsafe.Pointer) {
|
|
|
|
if vertex < 0 || vertex >= va.count {
|
|
|
|
panic("set vertex error: invalid vertex index")
|
|
|
|
}
|
|
|
|
DoNoBlock(func() {
|
2016-11-24 15:20:31 -06:00
|
|
|
gl.BindBuffer(gl.ARRAY_BUFFER, va.vbo)
|
2016-11-29 16:11:53 -06:00
|
|
|
|
|
|
|
offset := va.stride * vertex
|
|
|
|
gl.BufferSubData(gl.ARRAY_BUFFER, offset, va.format.Size(), data)
|
|
|
|
|
2016-11-24 15:20:31 -06:00
|
|
|
gl.BindBuffer(gl.ARRAY_BUFFER, 0)
|
2016-11-29 16:11:53 -06:00
|
|
|
|
|
|
|
if err := getLastGLErr(); err != nil {
|
|
|
|
panic(errors.Wrap(err, "set vertex error"))
|
|
|
|
}
|
2016-11-24 15:20:31 -06:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2016-11-26 15:42:26 -06:00
|
|
|
// SetVertexAttribute sets the value of the specified vertex attribute of the specified vertex.
|
2016-11-29 16:11:53 -06:00
|
|
|
// Argument data must point to a slice/array containing the new attribute data.
|
|
|
|
func (va *VertexArray) SetVertexAttribute(vertex int, attr Attr, data unsafe.Pointer) {
|
2016-11-26 15:42:26 -06:00
|
|
|
if vertex < 0 || vertex >= va.count {
|
|
|
|
panic("set vertex attribute error: invalid vertex index")
|
|
|
|
}
|
2016-11-26 15:51:20 -06:00
|
|
|
if _, ok := va.attrs[attr]; !ok {
|
2016-11-29 16:11:53 -06:00
|
|
|
return // ignore non-existing attributes
|
2016-11-26 15:51:20 -06:00
|
|
|
}
|
2016-11-24 07:10:33 -06:00
|
|
|
DoNoBlock(func() {
|
2016-11-23 08:59:21 -06:00
|
|
|
gl.BindBuffer(gl.ARRAY_BUFFER, va.vbo)
|
2016-11-26 15:42:26 -06:00
|
|
|
|
2016-11-29 16:11:53 -06:00
|
|
|
offset := va.stride*vertex + va.attrs[attr]
|
|
|
|
gl.BufferSubData(gl.ARRAY_BUFFER, offset, attr.Type.Size(), data)
|
2016-11-26 15:42:26 -06:00
|
|
|
|
2016-11-23 08:59:21 -06:00
|
|
|
gl.BindBuffer(gl.ARRAY_BUFFER, 0)
|
2016-11-26 15:42:26 -06:00
|
|
|
|
2016-11-24 07:10:33 -06:00
|
|
|
if err := getLastGLErr(); err != nil {
|
2016-11-26 15:42:26 -06:00
|
|
|
panic(errors.Wrap(err, "set attribute vertex error"))
|
2016-11-24 07:10:33 -06:00
|
|
|
}
|
2016-11-23 08:59:21 -06:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2016-11-25 15:49:56 -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) {
|
2016-11-25 15:49:56 -06:00
|
|
|
DoNoBlock(func() {
|
|
|
|
gl.BindVertexArray(va.vao)
|
|
|
|
gl.BindBuffer(gl.ARRAY_BUFFER, va.vbo)
|
|
|
|
})
|
2016-11-28 16:26:56 -06:00
|
|
|
sub(ctx)
|
2016-11-25 15:49:56 -06:00
|
|
|
DoNoBlock(func() {
|
|
|
|
gl.DrawArrays(uint32(va.mode), 0, int32(va.count))
|
|
|
|
gl.BindBuffer(gl.ARRAY_BUFFER, 0)
|
|
|
|
gl.BindVertexArray(0)
|
|
|
|
})
|
2016-11-23 08:59:21 -06:00
|
|
|
})
|
|
|
|
}
|