remove ID's from OpenGL objects + replace VertexArray with dynamically sized VertexSlice + add new Sprite
This commit is contained in:
parent
fb3df06337
commit
1f3c875d87
79
graphics.go
79
graphics.go
|
@ -9,7 +9,7 @@ import (
|
||||||
// Color and Texture.
|
// Color and Texture.
|
||||||
type TrianglesData []struct {
|
type TrianglesData []struct {
|
||||||
Position Vec
|
Position Vec
|
||||||
Color color.Color
|
Color NRGBA
|
||||||
Texture Vec
|
Texture Vec
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,11 +40,17 @@ func (td *TrianglesData) Update(t Triangles) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if t, ok := t.(*TrianglesColorData); ok {
|
if t, ok := t.(*TrianglesColorData); ok {
|
||||||
copy(*td, *(*TrianglesData)(t))
|
for i := range *td {
|
||||||
|
(*td)[i].Position = (*t)[i].Position
|
||||||
|
(*td)[i].Color = (*t)[i].Color
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if t, ok := t.(*TrianglesTextureData); ok {
|
if t, ok := t.(*TrianglesTextureData); ok {
|
||||||
copy(*td, *(*TrianglesData)(t))
|
for i := range *td {
|
||||||
|
(*td)[i].Position = (*t)[i].Position
|
||||||
|
(*td)[i].Texture = (*t)[i].Texture
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +62,7 @@ func (td *TrianglesData) Update(t Triangles) {
|
||||||
}
|
}
|
||||||
if t, ok := t.(TrianglesColor); ok {
|
if t, ok := t.(TrianglesColor); ok {
|
||||||
for i := range *td {
|
for i := range *td {
|
||||||
(*td)[i].Color = t.Color(i)
|
(*td)[i].Color = NRGBAModel.Convert(t.Color(i)).(NRGBA)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if t, ok := t.(TrianglesTexture); ok {
|
if t, ok := t.(TrianglesTexture); ok {
|
||||||
|
@ -181,3 +187,68 @@ func (td *TrianglesDrawer) Update(t Triangles) {
|
||||||
td.dirty = true
|
td.dirty = true
|
||||||
td.Triangles.Update(t)
|
td.Triangles.Update(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sprite is a picture, positioned somewhere, with an optional mask color.
|
||||||
|
type Sprite struct {
|
||||||
|
td TrianglesDrawer
|
||||||
|
pic *Picture
|
||||||
|
transform []Transform
|
||||||
|
maskColor color.Color
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSprite creates a Sprite with the supplied Picture. The dimensions of the returned Sprite match
|
||||||
|
// the dimensions of the Picture.
|
||||||
|
func NewSprite(pic *Picture) *Sprite {
|
||||||
|
s := &Sprite{
|
||||||
|
td: TrianglesDrawer{Triangles: &TrianglesTextureData{}},
|
||||||
|
}
|
||||||
|
s.SetPicture(pic)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPicture changes the Picture of the Sprite and resizes it accordingly.
|
||||||
|
func (s *Sprite) SetPicture(pic *Picture) {
|
||||||
|
w, h := pic.Bounds().Size.XY()
|
||||||
|
s.td.Update(&TrianglesTextureData{
|
||||||
|
{Position: V(0, 0), Texture: V(0, 0)},
|
||||||
|
{Position: V(w, 0), Texture: V(1, 0)},
|
||||||
|
{Position: V(w, h), Texture: V(1, 1)},
|
||||||
|
{Position: V(0, 0), Texture: V(0, 0)},
|
||||||
|
{Position: V(w, h), Texture: V(1, 1)},
|
||||||
|
{Position: V(0, h), Texture: V(0, 1)},
|
||||||
|
})
|
||||||
|
s.pic = pic
|
||||||
|
}
|
||||||
|
|
||||||
|
// Picture returns the current Picture of the Sprite.
|
||||||
|
func (s *Sprite) Picture() *Picture {
|
||||||
|
return s.pic
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTransform sets a chain of Transforms that will be applied to this Sprite in reverse order.
|
||||||
|
func (s *Sprite) SetTransform(t ...Transform) {
|
||||||
|
s.transform = t
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transform returns the current chain of Transforms that this Sprite is transformed by.
|
||||||
|
func (s *Sprite) Transform() []Transform {
|
||||||
|
return s.transform
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMaskColor changes the mask color of the Sprite.
|
||||||
|
func (s *Sprite) SetMaskColor(c color.Color) {
|
||||||
|
s.maskColor = c
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaskColor returns the current mask color of the Sprite.
|
||||||
|
func (s *Sprite) MaskColor() color.Color {
|
||||||
|
return s.maskColor
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw draws the Sprite onto the provided Target.
|
||||||
|
func (s *Sprite) Draw(target Target) {
|
||||||
|
target.SetPicture(s.pic)
|
||||||
|
target.SetTransform(s.transform...)
|
||||||
|
target.SetMaskColor(s.maskColor)
|
||||||
|
s.td.Draw(target)
|
||||||
|
}
|
||||||
|
|
|
@ -115,11 +115,6 @@ func (s *Shader) delete() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ID returns an OpenGL identifier of a shader program.
|
|
||||||
func (s *Shader) ID() uint32 {
|
|
||||||
return s.program.obj
|
|
||||||
}
|
|
||||||
|
|
||||||
// VertexFormat returns the vertex attribute format of this shader. Do not change it.
|
// VertexFormat returns the vertex attribute format of this shader. Do not change it.
|
||||||
func (s *Shader) VertexFormat() AttrFormat {
|
func (s *Shader) VertexFormat() AttrFormat {
|
||||||
return s.vertexFmt
|
return s.vertexFmt
|
||||||
|
|
|
@ -67,11 +67,6 @@ func (t *Texture) delete() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ID returns an OpenGL identifier of a texture.
|
|
||||||
func (t *Texture) ID() uint32 {
|
|
||||||
return t.tex.obj
|
|
||||||
}
|
|
||||||
|
|
||||||
// Width returns the width of a texture in pixels.
|
// Width returns the width of a texture in pixels.
|
||||||
func (t *Texture) Width() int {
|
func (t *Texture) Width() int {
|
||||||
return t.width
|
return t.width
|
||||||
|
|
|
@ -2,29 +2,176 @@ package pixelgl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"runtime"
|
"runtime"
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"github.com/go-gl/gl/v3.3-core/gl"
|
"github.com/go-gl/gl/v3.3-core/gl"
|
||||||
"github.com/go-gl/mathgl/mgl32"
|
"github.com/go-gl/mathgl/mgl32"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// VertexArray is an OpenGL vertex array object that also holds it's own vertex buffer object.
|
// VertexData holds data of one vertex stored in vertex attributes. The values must match attribute
|
||||||
// From the user's points of view, VertexArray is an array of vertices that can be drawn.
|
// types precisely. Here's the table of correct types (no other types are valid):
|
||||||
type VertexArray struct {
|
//
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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).
|
||||||
|
//
|
||||||
|
// 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
|
vao, vbo binder
|
||||||
numVertices int
|
cap int
|
||||||
format AttrFormat
|
format AttrFormat
|
||||||
stride int
|
stride int
|
||||||
offset map[string]int
|
offset map[string]int
|
||||||
|
shader *Shader
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewVertexArray creates a new empty vertex array.
|
const vertexArrayMinCap = 4
|
||||||
//
|
|
||||||
// You cannot specify vertex attributes in this constructor, only their count. Use
|
func newVertexArray(shader *Shader, cap int) *vertexArray {
|
||||||
// SetVertexAttribute* methods to set the vertex attributes.
|
if cap < vertexArrayMinCap {
|
||||||
func NewVertexArray(shader *Shader, numVertices int) (*VertexArray, error) {
|
cap = vertexArrayMinCap
|
||||||
va := &VertexArray{
|
}
|
||||||
|
|
||||||
|
va := &vertexArray{
|
||||||
vao: binder{
|
vao: binder{
|
||||||
restoreLoc: gl.VERTEX_ARRAY_BINDING,
|
restoreLoc: gl.VERTEX_ARRAY_BINDING,
|
||||||
bindFunc: func(obj uint32) {
|
bindFunc: func(obj uint32) {
|
||||||
|
@ -37,10 +184,11 @@ func NewVertexArray(shader *Shader, numVertices int) (*VertexArray, error) {
|
||||||
gl.BindBuffer(gl.ARRAY_BUFFER, obj)
|
gl.BindBuffer(gl.ARRAY_BUFFER, obj)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
numVertices: numVertices,
|
cap: cap,
|
||||||
format: shader.VertexFormat(),
|
format: shader.VertexFormat(),
|
||||||
stride: shader.VertexFormat().Size(),
|
stride: shader.VertexFormat().Size(),
|
||||||
offset: make(map[string]int),
|
offset: make(map[string]int),
|
||||||
|
shader: shader,
|
||||||
}
|
}
|
||||||
|
|
||||||
offset := 0
|
offset := 0
|
||||||
|
@ -48,7 +196,7 @@ func NewVertexArray(shader *Shader, numVertices int) (*VertexArray, error) {
|
||||||
switch typ {
|
switch typ {
|
||||||
case Float, Vec2, Vec3, Vec4:
|
case Float, Vec2, Vec3, Vec4:
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("failed to create vertex array: invalid attribute type")
|
panic(errors.New("failed to create vertex array: invalid attribute type"))
|
||||||
}
|
}
|
||||||
va.offset[name] = offset
|
va.offset[name] = offset
|
||||||
offset += typ.Size()
|
offset += typ.Size()
|
||||||
|
@ -61,11 +209,11 @@ func NewVertexArray(shader *Shader, numVertices int) (*VertexArray, error) {
|
||||||
gl.GenBuffers(1, &va.vbo.obj)
|
gl.GenBuffers(1, &va.vbo.obj)
|
||||||
defer va.vbo.bind().restore()
|
defer va.vbo.bind().restore()
|
||||||
|
|
||||||
emptyData := make([]byte, numVertices*va.stride)
|
emptyData := make([]byte, cap*va.stride)
|
||||||
gl.BufferData(gl.ARRAY_BUFFER, len(emptyData), gl.Ptr(emptyData), gl.DYNAMIC_DRAW)
|
gl.BufferData(gl.ARRAY_BUFFER, len(emptyData), gl.Ptr(emptyData), gl.DYNAMIC_DRAW)
|
||||||
|
|
||||||
for name, typ := range va.format {
|
for name, typ := range va.format {
|
||||||
loc := gl.GetAttribLocation(shader.ID(), gl.Str(name+"\x00"))
|
loc := gl.GetAttribLocation(shader.program.obj, gl.Str(name+"\x00"))
|
||||||
|
|
||||||
var size int32
|
var size int32
|
||||||
switch typ {
|
switch typ {
|
||||||
|
@ -92,226 +240,41 @@ func NewVertexArray(shader *Shader, numVertices int) (*VertexArray, error) {
|
||||||
|
|
||||||
va.vao.restore()
|
va.vao.restore()
|
||||||
|
|
||||||
runtime.SetFinalizer(va, (*VertexArray).delete)
|
runtime.SetFinalizer(va, (*vertexArray).delete)
|
||||||
|
|
||||||
return va, nil
|
return va
|
||||||
}
|
}
|
||||||
|
|
||||||
func (va *VertexArray) delete() {
|
func (va *vertexArray) delete() {
|
||||||
DoNoBlock(func() {
|
DoNoBlock(func() {
|
||||||
gl.DeleteVertexArrays(1, &va.vao.obj)
|
gl.DeleteVertexArrays(1, &va.vao.obj)
|
||||||
gl.DeleteBuffers(1, &va.vbo.obj)
|
gl.DeleteBuffers(1, &va.vbo.obj)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ID returns an OpenGL identifier of a vertex array.
|
func (va *vertexArray) begin() {
|
||||||
func (va *VertexArray) ID() uint32 {
|
va.vao.bind()
|
||||||
return va.vao.obj
|
va.vbo.bind()
|
||||||
}
|
}
|
||||||
|
|
||||||
// NumVertices returns the number of vertices in a vertex array.
|
func (va *vertexArray) end() {
|
||||||
func (va *VertexArray) NumVertices() int {
|
va.vbo.restore()
|
||||||
return va.numVertices
|
va.vao.restore()
|
||||||
}
|
}
|
||||||
|
|
||||||
// VertexFormat returns the format of the vertices inside a vertex array.
|
func (va *vertexArray) draw(i, j int) {
|
||||||
//
|
gl.DrawArrays(gl.TRIANGLES, int32(i), int32(i+j))
|
||||||
// Do not change this format!
|
|
||||||
func (va *VertexArray) VertexFormat() AttrFormat {
|
|
||||||
return va.format
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw draws a vertex array.
|
func (va *vertexArray) setVertexData(i, j int, vertices []VertexData) {
|
||||||
//
|
if j-i == 0 {
|
||||||
// The vertex array must be bound before calling this method.
|
// avoid setting 0 bytes of buffer data
|
||||||
func (va *VertexArray) Draw() {
|
return
|
||||||
gl.DrawArrays(gl.TRIANGLES, 0, int32(va.numVertices))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetVertexAttr sets the value of the specified vertex attribute of the specified vertex.
|
data := make([]float32, (j-i)*va.stride/4)
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
// 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")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !va.format.Contains(attr) {
|
for vertex := i; vertex < j; vertex++ {
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
|
|
||||||
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.
|
|
||||||
//
|
|
||||||
// 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")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !va.format.Contains(attr) {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
|
|
||||||
return value, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetVertex sets values of the attributes specified in the supplied map. All other attributes
|
|
||||||
// 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")
|
|
||||||
}
|
|
||||||
|
|
||||||
data := make([]float32, va.format.Size()/4)
|
|
||||||
|
|
||||||
for attr, value := range values {
|
|
||||||
if !va.format.Contains(attr) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
offset := va.offset[attr.Name]
|
|
||||||
|
|
||||||
switch attr.Type {
|
|
||||||
case Float:
|
|
||||||
data[offset/4] = value.(float32)
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
|
|
||||||
data := make([]float32, va.format.Size()/4)
|
|
||||||
|
|
||||||
offset := va.stride * vertex
|
|
||||||
gl.GetBufferSubData(gl.ARRAY_BUFFER, offset, len(data)*4, gl.Ptr(data))
|
|
||||||
|
|
||||||
values = make(map[Attr]interface{})
|
|
||||||
|
|
||||||
for name, typ := range va.format {
|
|
||||||
attr := Attr{name, typ}
|
|
||||||
offset := 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return values
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetVertices sets values of vertex attributes of all vertices as specified in the supplied
|
|
||||||
// slice of maps. If the length of vertices does not match the number of vertices in the vertex
|
|
||||||
// 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")
|
|
||||||
}
|
|
||||||
|
|
||||||
data := make([]float32, va.numVertices*va.format.Size()/4)
|
|
||||||
|
|
||||||
for vertex := range vertices {
|
|
||||||
for attr, value := range vertices[vertex] {
|
for attr, value := range vertices[vertex] {
|
||||||
if !va.format.Contains(attr) {
|
if !va.format.Contains(attr) {
|
||||||
continue
|
continue
|
||||||
|
@ -337,21 +300,22 @@ func (va *VertexArray) SetVertices(vertices []map[Attr]interface{}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gl.BufferSubData(gl.ARRAY_BUFFER, 0, len(data)*4, gl.Ptr(data))
|
gl.BufferSubData(gl.ARRAY_BUFFER, i*va.stride, len(data)*4, gl.Ptr(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vertices returns values of vertex attributes of all vertices in a vertex array in a slice
|
func (va *vertexArray) vertexData(i, j int) []VertexData {
|
||||||
// of maps.
|
if j-i == 0 {
|
||||||
//
|
// avoid getting 0 bytes of buffer data
|
||||||
// The vertex array must be bound before calling this metod.
|
return nil
|
||||||
func (va *VertexArray) Vertices() (vertices []map[Attr]interface{}) {
|
}
|
||||||
data := make([]float32, va.numVertices*va.format.Size()/4)
|
|
||||||
|
|
||||||
gl.GetBufferSubData(gl.ARRAY_BUFFER, 0, len(data)*4, gl.Ptr(data))
|
data := make([]float32, (j-i)*va.stride/4)
|
||||||
|
|
||||||
vertices = make([]map[Attr]interface{}, va.numVertices)
|
gl.GetBufferSubData(gl.ARRAY_BUFFER, i*va.stride, len(data)*4, gl.Ptr(data))
|
||||||
|
|
||||||
for vertex := range vertices {
|
vertices := make([]VertexData, 0, (j - i))
|
||||||
|
|
||||||
|
for vertex := i; vertex < j; vertex++ {
|
||||||
values := make(map[Attr]interface{})
|
values := make(map[Attr]interface{})
|
||||||
|
|
||||||
for name, typ := range va.format {
|
for name, typ := range va.format {
|
||||||
|
@ -376,20 +340,8 @@ func (va *VertexArray) Vertices() (vertices []map[Attr]interface{}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vertices[vertex] = values
|
vertices = append(vertices, values)
|
||||||
}
|
}
|
||||||
|
|
||||||
return vertices
|
return vertices
|
||||||
}
|
}
|
||||||
|
|
||||||
// Begin binds a vertex array. This is neccessary before using the vertex array.
|
|
||||||
func (va *VertexArray) Begin() {
|
|
||||||
va.vao.bind()
|
|
||||||
va.vbo.bind()
|
|
||||||
}
|
|
||||||
|
|
||||||
// End unbinds a vertex array and restores the previous one.
|
|
||||||
func (va *VertexArray) End() {
|
|
||||||
va.vbo.restore()
|
|
||||||
va.vao.restore()
|
|
||||||
}
|
|
||||||
|
|
139
window.go
139
window.go
|
@ -341,9 +341,7 @@ func (w *Window) Restore() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// begin makes the OpenGL context of a window current and binds it's shader.
|
// Note: must be called inside the main thread.
|
||||||
//
|
|
||||||
// Note, that this method must be called inside the main OpenGL thread (pixelgl.Do/DoNoBlock/DoErr/DoVal).
|
|
||||||
func (w *Window) begin() {
|
func (w *Window) begin() {
|
||||||
if currentWindow != w {
|
if currentWindow != w {
|
||||||
w.window.MakeContextCurrent()
|
w.window.MakeContextCurrent()
|
||||||
|
@ -355,9 +353,7 @@ func (w *Window) begin() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// end unbinds the shader of a window.
|
// Note: must be called inside the main thread.
|
||||||
//
|
|
||||||
// Note, that this method must be called inside the main OpenGL thread (pixelgl.Do/DoNoBlock/DoErr/DoVal).
|
|
||||||
func (w *Window) end() {
|
func (w *Window) end() {
|
||||||
if w.shader != nil {
|
if w.shader != nil {
|
||||||
w.shader.End()
|
w.shader.End()
|
||||||
|
@ -366,69 +362,23 @@ func (w *Window) end() {
|
||||||
|
|
||||||
type windowTriangles struct {
|
type windowTriangles struct {
|
||||||
w *Window
|
w *Window
|
||||||
va *pixelgl.VertexArray
|
vs *pixelgl.VertexSlice
|
||||||
data TrianglesData
|
data []pixelgl.VertexData
|
||||||
attrData []map[pixelgl.Attr]interface{}
|
|
||||||
dirty bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wt *windowTriangles) flush() {
|
|
||||||
if !wt.dirty {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
wt.dirty = false
|
|
||||||
|
|
||||||
if wt.va == nil || wt.va.NumVertices() != wt.Len() {
|
|
||||||
// reallocate vertex array
|
|
||||||
pixelgl.Do(func() {
|
|
||||||
var err error
|
|
||||||
wt.va, err = pixelgl.NewVertexArray(wt.w.shader, wt.Len())
|
|
||||||
if err != nil {
|
|
||||||
panic(errors.Wrap(err, "windowTriangles: failed to create vertex array"))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if wt.Len() > len(wt.attrData) {
|
|
||||||
wt.attrData = append(wt.attrData, make([]map[pixelgl.Attr]interface{}, wt.Len()-len(wt.attrData))...)
|
|
||||||
}
|
|
||||||
if wt.Len() < len(wt.attrData) {
|
|
||||||
wt.attrData = wt.attrData[:wt.Len()]
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, v := range wt.data {
|
|
||||||
if wt.attrData[i] == nil {
|
|
||||||
wt.attrData[i] = make(map[pixelgl.Attr]interface{})
|
|
||||||
}
|
|
||||||
p := v.Position
|
|
||||||
c := v.Color
|
|
||||||
t := v.Texture
|
|
||||||
wt.attrData[i][positionVec2] = mgl32.Vec2{float32(p.X()), float32(p.Y())}
|
|
||||||
wt.attrData[i][colorVec4] = mgl32.Vec4{float32(c.R), float32(c.G), float32(c.B), float32(c.A)}
|
|
||||||
wt.attrData[i][textureVec2] = mgl32.Vec2{float32(t.X()), float32(t.Y())}
|
|
||||||
}
|
|
||||||
|
|
||||||
pixelgl.Do(func() {
|
|
||||||
wt.va.Begin()
|
|
||||||
wt.va.SetVertices(wt.attrData)
|
|
||||||
wt.va.End()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wt *windowTriangles) Len() int {
|
func (wt *windowTriangles) Len() int {
|
||||||
return len(wt.data)
|
return wt.vs.Len()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wt *windowTriangles) Draw() {
|
func (wt *windowTriangles) Draw() {
|
||||||
wt.flush()
|
|
||||||
pixelgl.DoNoBlock(func() {
|
pixelgl.DoNoBlock(func() {
|
||||||
wt.w.begin()
|
wt.w.begin()
|
||||||
if wt.w.pic != nil {
|
if wt.w.pic != nil {
|
||||||
wt.w.pic.Texture().Begin()
|
wt.w.pic.Texture().Begin()
|
||||||
}
|
}
|
||||||
wt.va.Begin()
|
wt.vs.Begin()
|
||||||
wt.va.Draw()
|
wt.vs.Draw()
|
||||||
wt.va.End()
|
wt.vs.End()
|
||||||
if wt.w.pic != nil {
|
if wt.w.pic != nil {
|
||||||
wt.w.pic.Texture().End()
|
wt.w.pic.Texture().End()
|
||||||
}
|
}
|
||||||
|
@ -437,31 +387,83 @@ func (wt *windowTriangles) Draw() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wt *windowTriangles) Update(t Triangles) {
|
func (wt *windowTriangles) Update(t Triangles) {
|
||||||
wt.dirty = true
|
|
||||||
|
|
||||||
if t.Len() > wt.Len() {
|
if t.Len() > wt.Len() {
|
||||||
newData := make(TrianglesData, t.Len())
|
newData := make([]pixelgl.VertexData, t.Len()-wt.Len())
|
||||||
// default attribute values
|
// default values
|
||||||
for i := range newData {
|
for i := range newData {
|
||||||
newData[i].Color = NRGBA{R: 1, G: 1, B: 1, A: 1}
|
newData[i] = make(pixelgl.VertexData)
|
||||||
newData[i].Texture = V(-1, -1)
|
newData[i][colorVec4] = mgl32.Vec4{1, 1, 1, 1}
|
||||||
|
newData[i][textureVec2] = mgl32.Vec2{-1, -1}
|
||||||
}
|
}
|
||||||
wt.data = append(wt.data, newData...)
|
wt.data = append(wt.data, newData...)
|
||||||
}
|
}
|
||||||
|
if t.Len() < wt.Len() {
|
||||||
|
wt.data = wt.data[:t.Len()]
|
||||||
|
}
|
||||||
|
|
||||||
wt.data.Update(t)
|
if t, ok := t.(TrianglesPosition); ok {
|
||||||
|
for i := range wt.data {
|
||||||
|
pos := t.Position(i)
|
||||||
|
wt.data[i][positionVec2] = mgl32.Vec2{
|
||||||
|
float32(pos.X()),
|
||||||
|
float32(pos.Y()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if t, ok := t.(TrianglesColor); ok {
|
||||||
|
for i := range wt.data {
|
||||||
|
col := NRGBAModel.Convert(t.Color(i)).(NRGBA)
|
||||||
|
wt.data[i][colorVec4] = mgl32.Vec4{
|
||||||
|
float32(col.R),
|
||||||
|
float32(col.G),
|
||||||
|
float32(col.B),
|
||||||
|
float32(col.A),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if t, ok := t.(TrianglesTexture); ok {
|
||||||
|
for i := range wt.data {
|
||||||
|
tex := t.Texture(i)
|
||||||
|
wt.data[i][textureVec2] = mgl32.Vec2{
|
||||||
|
float32(tex.X()),
|
||||||
|
float32(tex.Y()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// submit data to vertex slice
|
||||||
|
data := wt.data // avoid race condition
|
||||||
|
pixelgl.DoNoBlock(func() {
|
||||||
|
wt.vs.Begin()
|
||||||
|
if len(wt.data) > wt.vs.Len() {
|
||||||
|
wt.vs.Append(make([]pixelgl.VertexData, len(data)-wt.vs.Len())...)
|
||||||
|
}
|
||||||
|
if len(wt.data) < wt.vs.Len() {
|
||||||
|
wt.vs = wt.vs.Slice(0, len(wt.data))
|
||||||
|
}
|
||||||
|
wt.vs.SetVertexData(wt.data)
|
||||||
|
wt.vs.End()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wt *windowTriangles) Position(i int) Vec {
|
func (wt *windowTriangles) Position(i int) Vec {
|
||||||
return wt.data.Position(i)
|
v := wt.data[i][positionVec2].(mgl32.Vec2)
|
||||||
|
return V(float64(v.X()), float64(v.Y()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wt *windowTriangles) Color(i int) color.Color {
|
func (wt *windowTriangles) Color(i int) color.Color {
|
||||||
return wt.data.Color(i)
|
c := wt.data[i][colorVec4].(mgl32.Vec4)
|
||||||
|
return NRGBA{
|
||||||
|
R: float64(c.X()),
|
||||||
|
G: float64(c.Y()),
|
||||||
|
B: float64(c.Z()),
|
||||||
|
A: float64(c.W()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wt *windowTriangles) Texture(i int) Vec {
|
func (wt *windowTriangles) Texture(i int) Vec {
|
||||||
return wt.data.Texture(i)
|
t := wt.data[i][textureVec2].(mgl32.Vec2)
|
||||||
|
return V(float64(t.X()), float64(t.Y()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeTriangles generates a specialized copy of the supplied triangles that will draw onto this
|
// MakeTriangles generates a specialized copy of the supplied triangles that will draw onto this
|
||||||
|
@ -471,6 +473,7 @@ func (wt *windowTriangles) Texture(i int) Vec {
|
||||||
func (w *Window) MakeTriangles(t Triangles) Triangles {
|
func (w *Window) MakeTriangles(t Triangles) Triangles {
|
||||||
wt := &windowTriangles{
|
wt := &windowTriangles{
|
||||||
w: w,
|
w: w,
|
||||||
|
vs: pixelgl.MakeVertexSlice(w.shader, 0, 0),
|
||||||
}
|
}
|
||||||
wt.Update(t)
|
wt.Update(t)
|
||||||
return wt
|
return wt
|
||||||
|
|
Loading…
Reference in New Issue