From 1f3c875d877dcf5853492b39292b7ce2158fc7f5 Mon Sep 17 00:00:00 2001 From: faiface <faiface@ksp.sk> Date: Tue, 10 Jan 2017 23:54:35 +0100 Subject: [PATCH] remove ID's from OpenGL objects + replace VertexArray with dynamically sized VertexSlice + add new Sprite --- graphics.go | 79 +++++++- pixelgl/shader.go | 5 - pixelgl/texture.go | 5 - pixelgl/vertex.go | 444 ++++++++++++++++++++------------------------- window.go | 143 ++++++++------- 5 files changed, 346 insertions(+), 330 deletions(-) diff --git a/graphics.go b/graphics.go index d8b4c5d..bfcc5aa 100644 --- a/graphics.go +++ b/graphics.go @@ -9,7 +9,7 @@ import ( // Color and Texture. type TrianglesData []struct { Position Vec - Color color.Color + Color NRGBA Texture Vec } @@ -40,11 +40,17 @@ func (td *TrianglesData) Update(t Triangles) { return } 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 } 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 } @@ -56,7 +62,7 @@ func (td *TrianglesData) Update(t Triangles) { } if t, ok := t.(TrianglesColor); ok { 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 { @@ -181,3 +187,68 @@ func (td *TrianglesDrawer) Update(t Triangles) { td.dirty = true 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) +} diff --git a/pixelgl/shader.go b/pixelgl/shader.go index 35f2820..61652ac 100644 --- a/pixelgl/shader.go +++ b/pixelgl/shader.go @@ -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. func (s *Shader) VertexFormat() AttrFormat { return s.vertexFmt diff --git a/pixelgl/texture.go b/pixelgl/texture.go index 24ac3e1..4cb7690 100644 --- a/pixelgl/texture.go +++ b/pixelgl/texture.go @@ -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. func (t *Texture) Width() int { return t.width diff --git a/pixelgl/vertex.go b/pixelgl/vertex.go index 5f2633f..b2a94f8 100644 --- a/pixelgl/vertex.go +++ b/pixelgl/vertex.go @@ -2,29 +2,176 @@ package pixelgl import ( "runtime" - "unsafe" "github.com/go-gl/gl/v3.3-core/gl" "github.com/go-gl/mathgl/mgl32" "github.com/pkg/errors" ) -// 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 { - vao, vbo binder - numVertices int - format AttrFormat - stride int - offset map[string]int +// 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 } -// NewVertexArray creates a new empty vertex array. +// 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). // -// You cannot specify vertex attributes in this constructor, only their count. Use -// SetVertexAttribute* methods to set the vertex attributes. -func NewVertexArray(shader *Shader, numVertices int) (*VertexArray, error) { - va := &VertexArray{ +// 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{ vao: binder{ restoreLoc: gl.VERTEX_ARRAY_BINDING, bindFunc: func(obj uint32) { @@ -37,10 +184,11 @@ func NewVertexArray(shader *Shader, numVertices int) (*VertexArray, error) { gl.BindBuffer(gl.ARRAY_BUFFER, obj) }, }, - numVertices: numVertices, - format: shader.VertexFormat(), - stride: shader.VertexFormat().Size(), - offset: make(map[string]int), + cap: cap, + format: shader.VertexFormat(), + stride: shader.VertexFormat().Size(), + offset: make(map[string]int), + shader: shader, } offset := 0 @@ -48,7 +196,7 @@ func NewVertexArray(shader *Shader, numVertices int) (*VertexArray, error) { switch typ { case Float, Vec2, Vec3, Vec4: 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 offset += typ.Size() @@ -61,11 +209,11 @@ func NewVertexArray(shader *Shader, numVertices int) (*VertexArray, error) { gl.GenBuffers(1, &va.vbo.obj) 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) 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 switch typ { @@ -92,226 +240,41 @@ func NewVertexArray(shader *Shader, numVertices int) (*VertexArray, error) { 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() { gl.DeleteVertexArrays(1, &va.vao.obj) gl.DeleteBuffers(1, &va.vbo.obj) }) } -// ID returns an OpenGL identifier of a vertex array. -func (va *VertexArray) ID() uint32 { - return va.vao.obj -} - -// NumVertices returns the number of vertices in a vertex array. -func (va *VertexArray) NumVertices() int { - return va.numVertices -} - -// VertexFormat returns the format of the vertices inside a vertex array. -// -// Do not change this format! -func (va *VertexArray) VertexFormat() AttrFormat { - return va.format -} - -// Draw draws a vertex array. -// -// The vertex array must be bound before calling this method. -func (va *VertexArray) Draw() { - gl.DrawArrays(gl.TRIANGLES, 0, int32(va.numVertices)) -} - -// 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. -// -// 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) { - 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 +func (va *vertexArray) begin() { + va.vao.bind() + va.vbo.bind() } -// 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)) +func (va *vertexArray) end() { + va.vbo.restore() + va.vao.restore() } -// 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 +func (va *vertexArray) draw(i, j int) { + gl.DrawArrays(gl.TRIANGLES, int32(i), int32(i+j)) } -// 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") +func (va *vertexArray) setVertexData(i, j int, vertices []VertexData) { + if j-i == 0 { + // avoid setting 0 bytes of buffer data + return } - data := make([]float32, va.numVertices*va.format.Size()/4) + data := make([]float32, (j-i)*va.stride/4) - for vertex := range vertices { + for vertex := i; vertex < j; vertex++ { for attr, value := range vertices[vertex] { if !va.format.Contains(attr) { 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 -// 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) +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) - gl.GetBufferSubData(gl.ARRAY_BUFFER, 0, len(data)*4, gl.Ptr(data)) + gl.GetBufferSubData(gl.ARRAY_BUFFER, i*va.stride, len(data)*4, gl.Ptr(data)) - vertices = make([]map[Attr]interface{}, va.numVertices) + vertices := make([]VertexData, 0, (j - i)) - for vertex := range vertices { + for vertex := i; vertex < j; vertex++ { values := make(map[Attr]interface{}) 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 } - -// 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() -} diff --git a/window.go b/window.go index a2bf8bb..6ba4e89 100644 --- a/window.go +++ b/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, that this method must be called inside the main OpenGL thread (pixelgl.Do/DoNoBlock/DoErr/DoVal). +// Note: must be called inside the main thread. func (w *Window) begin() { if currentWindow != w { w.window.MakeContextCurrent() @@ -355,9 +353,7 @@ func (w *Window) begin() { } } -// end unbinds the shader of a window. -// -// Note, that this method must be called inside the main OpenGL thread (pixelgl.Do/DoNoBlock/DoErr/DoVal). +// Note: must be called inside the main thread. func (w *Window) end() { if w.shader != nil { w.shader.End() @@ -365,70 +361,24 @@ func (w *Window) end() { } type windowTriangles struct { - w *Window - va *pixelgl.VertexArray - data TrianglesData - 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() - }) + w *Window + vs *pixelgl.VertexSlice + data []pixelgl.VertexData } func (wt *windowTriangles) Len() int { - return len(wt.data) + return wt.vs.Len() } func (wt *windowTriangles) Draw() { - wt.flush() pixelgl.DoNoBlock(func() { wt.w.begin() if wt.w.pic != nil { wt.w.pic.Texture().Begin() } - wt.va.Begin() - wt.va.Draw() - wt.va.End() + wt.vs.Begin() + wt.vs.Draw() + wt.vs.End() if wt.w.pic != nil { wt.w.pic.Texture().End() } @@ -437,31 +387,83 @@ func (wt *windowTriangles) Draw() { } func (wt *windowTriangles) Update(t Triangles) { - wt.dirty = true - if t.Len() > wt.Len() { - newData := make(TrianglesData, t.Len()) - // default attribute values + newData := make([]pixelgl.VertexData, t.Len()-wt.Len()) + // default values for i := range newData { - newData[i].Color = NRGBA{R: 1, G: 1, B: 1, A: 1} - newData[i].Texture = V(-1, -1) + newData[i] = make(pixelgl.VertexData) + newData[i][colorVec4] = mgl32.Vec4{1, 1, 1, 1} + newData[i][textureVec2] = mgl32.Vec2{-1, -1} } wt.data = append(wt.data, newData...) } + if t.Len() < wt.Len() { + wt.data = wt.data[:t.Len()] + } + + 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()), + } + } + } - wt.data.Update(t) + // 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 { - 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 { - 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 { - 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 @@ -470,7 +472,8 @@ func (wt *windowTriangles) Texture(i int) Vec { // Window supports TrianglesPosition, TrianglesColor and TrianglesTexture. func (w *Window) MakeTriangles(t Triangles) Triangles { wt := &windowTriangles{ - w: w, + w: w, + vs: pixelgl.MakeVertexSlice(w.shader, 0, 0), } wt.Update(t) return wt -- GitLab