2016-11-23 07:03:23 -06:00
|
|
|
package pixelgl
|
|
|
|
|
2016-11-23 11:06:55 -06:00
|
|
|
import (
|
2016-12-18 13:16:27 -06:00
|
|
|
"runtime"
|
|
|
|
|
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
|
|
|
|
2017-01-10 16:54:35 -06:00
|
|
|
// VertexData holds data of one vertex stored in vertex attributes. The values must match attribute
|
|
|
|
// types precisely. Here's the table of correct types (no other types are valid):
|
|
|
|
//
|
|
|
|
// Attr{Type: Float}: float32
|
|
|
|
// Attr{Type: Vec2}: mgl32.Vec2
|
|
|
|
// Attr{Type: Vec3}: mgl32.Vec3
|
|
|
|
// Attr{Type: Vec4}: mgl32.Vec4
|
|
|
|
type VertexData map[Attr]interface{}
|
|
|
|
|
|
|
|
// VertexSlice points to a portion of (or possibly whole) vertex array. It is used as a pointer,
|
|
|
|
// contrary to Go's builtin slices. This is, so that append can be 'in-place'. That's for the good,
|
|
|
|
// because Begin/End-ing a VertexSlice would become super confusing, if append returned a new
|
|
|
|
// VertexSlice.
|
|
|
|
//
|
|
|
|
// It also implements all basic slice-like operations: appending, sub-slicing, etc.
|
|
|
|
//
|
|
|
|
// Note that you need to Begin a VertexSlice before getting or updating it's elements or drawing it.
|
|
|
|
// After you're done with it, you need to End it.
|
|
|
|
type VertexSlice struct {
|
|
|
|
va *vertexArray
|
|
|
|
i, j int
|
|
|
|
}
|
|
|
|
|
|
|
|
// MakeVertexSlice allocates a new vertex array with specified capacity and returns a VertexSlice
|
|
|
|
// that points to it's first len elements.
|
|
|
|
//
|
|
|
|
// Note, that a vertex array is specialized for a specific shader and can't be used with another
|
|
|
|
// shader.
|
|
|
|
func MakeVertexSlice(shader *Shader, len, cap int) *VertexSlice {
|
|
|
|
if len > cap {
|
|
|
|
panic("failed to make vertex slice: len > cap")
|
|
|
|
}
|
|
|
|
return &VertexSlice{
|
|
|
|
va: newVertexArray(shader, cap),
|
|
|
|
i: 0,
|
|
|
|
j: len,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// VertexFormat returns the format of vertex attributes inside the underlying vertex array of this
|
|
|
|
// VertexSlice.
|
|
|
|
func (vs *VertexSlice) VertexFormat() AttrFormat {
|
|
|
|
return vs.va.format
|
|
|
|
}
|
|
|
|
|
|
|
|
// Len returns the length of the VertexSlice.
|
|
|
|
func (vs *VertexSlice) Len() int {
|
|
|
|
return vs.j - vs.i
|
2016-11-23 08:59:21 -06:00
|
|
|
}
|
|
|
|
|
2017-01-10 16:54:35 -06:00
|
|
|
// Cap returns the capacity of an underlying vertex array.
|
|
|
|
func (vs *VertexSlice) Cap() int {
|
|
|
|
return vs.va.cap - vs.i
|
|
|
|
}
|
|
|
|
|
|
|
|
// Slice returns a sub-slice of this VertexSlice covering the range [i, j) (relative to this
|
|
|
|
// VertexSlice).
|
2016-12-08 08:25:00 -06:00
|
|
|
//
|
2017-01-10 16:54:35 -06:00
|
|
|
// Note, that the returned VertexSlice shares an underlying vertex array with the original
|
|
|
|
// VertexSlice. Modifying the contents of one modifies corresponding contents of the other.
|
|
|
|
func (vs *VertexSlice) Slice(i, j int) *VertexSlice {
|
|
|
|
if i < 0 || j < i || j > vs.va.cap {
|
|
|
|
panic("failed to slice vertex slice: index out of range")
|
|
|
|
}
|
|
|
|
return &VertexSlice{
|
|
|
|
va: vs.va,
|
|
|
|
i: vs.i + i,
|
|
|
|
j: vs.i + j,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// grow returns supplied vs with length changed to len. Allocates new underlying vertex array if
|
|
|
|
// necessary. The original content is preserved.
|
|
|
|
func (vs VertexSlice) grow(len int) VertexSlice {
|
|
|
|
if len <= vs.Cap() {
|
|
|
|
// capacity sufficient
|
|
|
|
return VertexSlice{
|
|
|
|
va: vs.va,
|
|
|
|
i: vs.i,
|
|
|
|
j: vs.i + len,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// grow the capacity
|
|
|
|
newCap := vs.Cap()
|
|
|
|
if newCap < 1024 {
|
|
|
|
newCap += newCap
|
|
|
|
} else {
|
|
|
|
newCap += newCap / 4
|
|
|
|
}
|
|
|
|
if newCap < len {
|
|
|
|
newCap = len
|
|
|
|
}
|
|
|
|
newVs := VertexSlice{
|
|
|
|
va: newVertexArray(vs.va.shader, newCap),
|
|
|
|
i: 0,
|
|
|
|
j: len,
|
|
|
|
}
|
|
|
|
// preserve the original content
|
|
|
|
newVs.Begin()
|
|
|
|
newVs.Slice(0, vs.Len()).SetVertexData(vs.VertexData())
|
|
|
|
newVs.End()
|
|
|
|
return newVs
|
|
|
|
}
|
|
|
|
|
|
|
|
// Append adds supplied vertices to the end of the VertexSlice. If the capacity of the VertexSlice
|
|
|
|
// is not sufficient, a new, larger underlying vertex array will be allocated. The content of the
|
|
|
|
// original VertexSlice will be copied to the new underlying vertex array.
|
|
|
|
//
|
|
|
|
// The VertexSlice is appended 'in-place', contrary Go's builtin slices.
|
|
|
|
func (vs *VertexSlice) Append(vertices ...VertexData) {
|
|
|
|
vs.End() // vs must have been Begin-ed before calling this method
|
|
|
|
*vs = vs.grow(vs.Len() + len(vertices))
|
|
|
|
vs.Begin()
|
|
|
|
vs.Slice(vs.Len()-len(vertices), vs.Len()).SetVertexData(vertices)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetVertexData sets the contents of the VertexSlice.
|
|
|
|
//
|
|
|
|
// If the length of vertices does not match the length of the VertexSlice, this methdo panics.
|
|
|
|
func (vs *VertexSlice) SetVertexData(vertices []VertexData) {
|
|
|
|
if len(vertices) != vs.Len() {
|
|
|
|
panic("set vertex data: wrong length of vertices")
|
|
|
|
}
|
|
|
|
vs.va.setVertexData(vs.i, vs.j, vertices)
|
|
|
|
}
|
|
|
|
|
|
|
|
// VertexData returns the contents of the VertexSlice.
|
|
|
|
func (vs *VertexSlice) VertexData() []VertexData {
|
|
|
|
return vs.va.vertexData(vs.i, vs.j)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Draw draws the content of the VertexSlice.
|
|
|
|
func (vs *VertexSlice) Draw() {
|
|
|
|
vs.va.draw(vs.i, vs.j)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Begin binds the underlying vertex array. Calling this method is necessary before using the VertexSlice.
|
|
|
|
func (vs *VertexSlice) Begin() {
|
|
|
|
vs.va.begin()
|
|
|
|
}
|
|
|
|
|
|
|
|
// End unbinds the underlying vertex array. Call this method when you're done with VertexSlice.
|
|
|
|
func (vs *VertexSlice) End() {
|
|
|
|
vs.va.end()
|
|
|
|
}
|
|
|
|
|
|
|
|
type vertexArray struct {
|
|
|
|
vao, vbo binder
|
|
|
|
cap int
|
|
|
|
format AttrFormat
|
|
|
|
stride int
|
|
|
|
offset map[string]int
|
|
|
|
shader *Shader
|
|
|
|
}
|
|
|
|
|
|
|
|
const vertexArrayMinCap = 4
|
|
|
|
|
|
|
|
func newVertexArray(shader *Shader, cap int) *vertexArray {
|
|
|
|
if cap < vertexArrayMinCap {
|
|
|
|
cap = vertexArrayMinCap
|
|
|
|
}
|
|
|
|
|
|
|
|
va := &vertexArray{
|
2016-12-14 09:24:31 -06:00
|
|
|
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)
|
|
|
|
},
|
|
|
|
},
|
2017-01-10 16:54:35 -06:00
|
|
|
cap: cap,
|
|
|
|
format: shader.VertexFormat(),
|
|
|
|
stride: shader.VertexFormat().Size(),
|
|
|
|
offset: make(map[string]int),
|
|
|
|
shader: shader,
|
2016-11-23 08:59:21 -06:00
|
|
|
}
|
2016-11-24 07:37:11 -06:00
|
|
|
|
2016-11-26 15:42:26 -06:00
|
|
|
offset := 0
|
2016-12-30 18:33:53 -06:00
|
|
|
for name, typ := range va.format {
|
2016-12-15 17:28:52 -06:00
|
|
|
switch typ {
|
2016-12-02 11:21:28 -06:00
|
|
|
case Float, Vec2, Vec3, Vec4:
|
2016-11-29 16:11:53 -06:00
|
|
|
default:
|
2017-01-10 16:54:35 -06:00
|
|
|
panic(errors.New("failed to create vertex array: invalid attribute type"))
|
2016-11-29 16:11:53 -06:00
|
|
|
}
|
2016-12-15 17:28:52 -06:00
|
|
|
va.offset[name] = offset
|
|
|
|
offset += typ.Size()
|
2016-11-26 15:42:26 -06:00
|
|
|
}
|
|
|
|
|
2016-12-30 18:33:53 -06:00
|
|
|
gl.GenVertexArrays(1, &va.vao.obj)
|
2016-11-25 15:49:56 -06:00
|
|
|
|
2016-12-30 18:33:53 -06:00
|
|
|
va.vao.bind()
|
|
|
|
|
|
|
|
gl.GenBuffers(1, &va.vbo.obj)
|
|
|
|
defer va.vbo.bind().restore()
|
|
|
|
|
2017-01-10 16:54:35 -06:00
|
|
|
emptyData := make([]byte, cap*va.stride)
|
2016-12-30 18:33:53 -06:00
|
|
|
gl.BufferData(gl.ARRAY_BUFFER, len(emptyData), gl.Ptr(emptyData), gl.DYNAMIC_DRAW)
|
|
|
|
|
|
|
|
for name, typ := range va.format {
|
2017-01-10 16:54:35 -06:00
|
|
|
loc := gl.GetAttribLocation(shader.program.obj, gl.Str(name+"\x00"))
|
2016-12-30 18:33:53 -06:00
|
|
|
|
|
|
|
var size int32
|
|
|
|
switch typ {
|
|
|
|
case Float:
|
|
|
|
size = 1
|
|
|
|
case Vec2:
|
|
|
|
size = 2
|
|
|
|
case Vec3:
|
|
|
|
size = 3
|
|
|
|
case Vec4:
|
|
|
|
size = 4
|
|
|
|
}
|
|
|
|
|
|
|
|
gl.VertexAttribPointer(
|
|
|
|
uint32(loc),
|
|
|
|
size,
|
|
|
|
gl.FLOAT,
|
|
|
|
false,
|
|
|
|
int32(va.stride),
|
|
|
|
gl.PtrOffset(va.offset[name]),
|
|
|
|
)
|
|
|
|
gl.EnableVertexAttribArray(uint32(loc))
|
|
|
|
}
|
|
|
|
|
|
|
|
va.vao.restore()
|
2016-12-08 08:25:00 -06:00
|
|
|
|
2017-01-10 16:54:35 -06:00
|
|
|
runtime.SetFinalizer(va, (*vertexArray).delete)
|
2016-12-18 13:16:27 -06:00
|
|
|
|
2017-01-10 16:54:35 -06:00
|
|
|
return va
|
2016-11-23 08:59:21 -06:00
|
|
|
}
|
|
|
|
|
2017-01-10 16:54:35 -06:00
|
|
|
func (va *vertexArray) delete() {
|
2016-12-18 18:11:34 -06:00
|
|
|
DoNoBlock(func() {
|
|
|
|
gl.DeleteVertexArrays(1, &va.vao.obj)
|
|
|
|
gl.DeleteBuffers(1, &va.vbo.obj)
|
2016-11-23 13:06:34 -06:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2017-01-10 16:54:35 -06:00
|
|
|
func (va *vertexArray) begin() {
|
|
|
|
va.vao.bind()
|
|
|
|
va.vbo.bind()
|
2016-11-23 08:59:21 -06:00
|
|
|
}
|
|
|
|
|
2017-01-10 16:54:35 -06:00
|
|
|
func (va *vertexArray) end() {
|
|
|
|
va.vbo.restore()
|
|
|
|
va.vao.restore()
|
2016-12-19 18:23:33 -06:00
|
|
|
}
|
|
|
|
|
2017-01-10 16:54:35 -06:00
|
|
|
func (va *vertexArray) draw(i, j int) {
|
|
|
|
gl.DrawArrays(gl.TRIANGLES, int32(i), int32(i+j))
|
2016-12-20 06:56:25 -06:00
|
|
|
}
|
|
|
|
|
2017-01-10 16:54:35 -06:00
|
|
|
func (va *vertexArray) setVertexData(i, j int, vertices []VertexData) {
|
|
|
|
if j-i == 0 {
|
|
|
|
// avoid setting 0 bytes of buffer data
|
|
|
|
return
|
2016-12-19 18:23:33 -06:00
|
|
|
}
|
|
|
|
|
2017-01-10 16:54:35 -06:00
|
|
|
data := make([]float32, (j-i)*va.stride/4)
|
2016-12-19 18:23:33 -06:00
|
|
|
|
2017-01-10 16:54:35 -06:00
|
|
|
for vertex := i; vertex < j; vertex++ {
|
2016-12-19 18:23:33 -06:00
|
|
|
for attr, value := range vertices[vertex] {
|
|
|
|
if !va.format.Contains(attr) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
offset := va.stride*vertex + va.offset[attr.Name]
|
|
|
|
|
|
|
|
switch attr.Type {
|
|
|
|
case Float:
|
2016-12-20 06:56:25 -06:00
|
|
|
data[offset/4] = value.(float32)
|
2016-12-19 18:23:33 -06:00
|
|
|
case Vec2:
|
|
|
|
value := value.(mgl32.Vec2)
|
|
|
|
copy(data[offset/4:offset/4+attr.Type.Size()/4], value[:])
|
|
|
|
case Vec3:
|
|
|
|
value := value.(mgl32.Vec3)
|
|
|
|
copy(data[offset/4:offset/4+attr.Type.Size()/4], value[:])
|
|
|
|
case Vec4:
|
|
|
|
value := value.(mgl32.Vec4)
|
|
|
|
copy(data[offset/4:offset/4+attr.Type.Size()/4], value[:])
|
|
|
|
default:
|
|
|
|
panic("set vertex: invalid attribute type")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-10 16:54:35 -06:00
|
|
|
gl.BufferSubData(gl.ARRAY_BUFFER, i*va.stride, len(data)*4, gl.Ptr(data))
|
2016-12-20 06:56:25 -06:00
|
|
|
}
|
2016-12-19 18:23:33 -06:00
|
|
|
|
2017-01-10 16:54:35 -06:00
|
|
|
func (va *vertexArray) vertexData(i, j int) []VertexData {
|
|
|
|
if j-i == 0 {
|
|
|
|
// avoid getting 0 bytes of buffer data
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
data := make([]float32, (j-i)*va.stride/4)
|
2016-12-20 06:56:25 -06:00
|
|
|
|
2017-01-10 16:54:35 -06:00
|
|
|
gl.GetBufferSubData(gl.ARRAY_BUFFER, i*va.stride, len(data)*4, gl.Ptr(data))
|
2016-12-20 06:56:25 -06:00
|
|
|
|
2017-01-10 16:54:35 -06:00
|
|
|
vertices := make([]VertexData, 0, (j - i))
|
2016-12-20 06:56:25 -06:00
|
|
|
|
2017-01-10 16:54:35 -06:00
|
|
|
for vertex := i; vertex < j; vertex++ {
|
2016-12-20 06:56:25 -06:00
|
|
|
values := make(map[Attr]interface{})
|
|
|
|
|
|
|
|
for name, typ := range va.format {
|
|
|
|
attr := Attr{name, typ}
|
|
|
|
offset := va.stride*vertex + va.offset[attr.Name]
|
|
|
|
|
|
|
|
switch attr.Type {
|
|
|
|
case Float:
|
|
|
|
values[attr] = data[offset/4]
|
|
|
|
case Vec2:
|
|
|
|
var value mgl32.Vec2
|
|
|
|
copy(value[:], data[offset/4:offset/4+attr.Type.Size()/4])
|
|
|
|
values[attr] = value
|
|
|
|
case Vec3:
|
|
|
|
var value mgl32.Vec3
|
|
|
|
copy(value[:], data[offset/4:offset/4+attr.Type.Size()/4])
|
|
|
|
values[attr] = value
|
|
|
|
case Vec4:
|
|
|
|
var value mgl32.Vec4
|
|
|
|
copy(value[:], data[offset/4:offset/4+attr.Type.Size()/4])
|
|
|
|
values[attr] = value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-10 16:54:35 -06:00
|
|
|
vertices = append(vertices, values)
|
2016-12-20 06:56:25 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
return vertices
|
2016-12-19 18:23:33 -06:00
|
|
|
}
|