diff --git a/graphics.go b/graphics.go index 6f9e012..6094159 100644 --- a/graphics.go +++ b/graphics.go @@ -114,9 +114,9 @@ func NewSprite(parent pixelgl.Doer, picture Picture) *Sprite { s.va, err = pixelgl.NewVertexArray( picture.Texture(), ctx.Shader().VertexFormat(), - pixelgl.TriangleFanDrawMode, pixelgl.DynamicUsage, 4, + []int{0, 1, 2, 0, 2, 3}, ) if err != nil { panic(errors.Wrap(err, "failed to create sprite")) @@ -231,9 +231,9 @@ func NewLineColor(parent pixelgl.Doer, c color.Color, a, b Vec, width float64) * lc.va, err = pixelgl.NewVertexArray( parent, ctx.Shader().VertexFormat(), - pixelgl.TriangleStripDrawMode, pixelgl.DynamicUsage, 4, + []int{0, 1, 2, 1, 2, 3}, ) if err != nil { panic(errors.Wrap(err, "failed to create line")) @@ -340,14 +340,19 @@ func NewPolygonColor(parent pixelgl.Doer, c color.Color, points ...Vec) *Polygon points: points, } + var indices []int + for i := 2; i < len(points); i++ { + indices = append(indices, 0, i-1, i) + } + parent.Do(func(ctx pixelgl.Context) { var err error pc.va, err = pixelgl.NewVertexArray( parent, ctx.Shader().VertexFormat(), - pixelgl.TriangleFanDrawMode, pixelgl.DynamicUsage, len(points), + indices, ) if err != nil { panic(errors.Wrap(err, "failed to create polygon")) @@ -448,17 +453,22 @@ func NewEllipseColor(parent pixelgl.Doer, c color.Color, radius Vec, fill float6 fill: fill, } + var indices []int + for i := 2; i < (n+1)*2; i++ { + indices = append(indices, i-2, i-1, i) + } + parent.Do(func(ctx pixelgl.Context) { var err error ec.va, err = pixelgl.NewVertexArray( parent, ctx.Shader().VertexFormat(), - pixelgl.TriangleStripDrawMode, pixelgl.DynamicUsage, (n+1)*2, + indices, ) if err != nil { - panic(errors.Wrap(err, "failed to create circle")) + panic(errors.Wrap(err, "failed to create ellipse")) } }) diff --git a/pixelgl/vertex.go b/pixelgl/vertex.go index 8a8f4ae..658c06d 100644 --- a/pixelgl/vertex.go +++ b/pixelgl/vertex.go @@ -40,55 +40,31 @@ const ( StreamUsage VertexUsage = gl.STREAM_DRAW ) -// VertexDrawMode specifies how should the vertices be drawn. -type VertexDrawMode int - -const ( - // 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 -) - // 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 { - enabled bool - parent Doer - vao uint32 - vbo uint32 - format VertexFormat - stride int - count int - attrs map[Attr]int - mode VertexDrawMode + enabled bool + parent Doer + vao, vbo, ebo uint32 + vertexNum, indexNum int + format VertexFormat + usage VertexUsage + stride int + attrs map[Attr]int } // 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) { +// +// 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) { va := &VertexArray{ - parent: parent, - format: format, - count: count, - stride: format.Size(), - attrs: make(map[Attr]int), - mode: mode, + parent: parent, + format: format, + usage: usage, + vertexNum: vertexNum, + stride: format.Size(), + attrs: make(map[Attr]int), } offset := 0 @@ -105,18 +81,19 @@ func NewVertexArray(parent Doer, format VertexFormat, mode VertexDrawMode, usage offset += attr.Type.Size() } - var err error parent.Do(func(ctx Context) { - err = DoErr(func() error { + Do(func() { gl.GenVertexArrays(1, &va.vao) gl.BindVertexArray(va.vao) gl.GenBuffers(1, &va.vbo) gl.BindBuffer(gl.ARRAY_BUFFER, va.vbo) - emptyData := make([]byte, count*va.stride) + emptyData := make([]byte, vertexNum*va.stride) gl.BufferData(gl.ARRAY_BUFFER, len(emptyData), gl.Ptr(emptyData), uint32(usage)) + gl.GenBuffers(1, &va.ebo) + offset := 0 for i, attr := range format { var size int32 @@ -143,15 +120,16 @@ func NewVertexArray(parent Doer, format VertexFormat, mode VertexDrawMode, usage offset += attr.Type.Size() } + gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, va.ebo) // need to bind EBO, so that VAO registers it + gl.BindBuffer(gl.ARRAY_BUFFER, 0) gl.BindVertexArray(0) - return nil + gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, 0) }) }) - if err != nil { - return nil, errors.Wrap(err, "failed to create vertex array") - } + + va.SetIndices(indices) return va, nil } @@ -173,7 +151,7 @@ func (va *VertexArray) ID() uint32 { // Count returns the number of vertices in a vertex array. func (va *VertexArray) Count() int { - return va.count + return va.vertexNum } // VertexFormat returns the format of the vertices inside a vertex array. @@ -183,19 +161,9 @@ 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) { - DoNoBlock(func() { - 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) +// VertexUsage returns the usage of the verteices inside a vertex array. +func (va *VertexArray) VertexUsage() VertexUsage { + return va.usage } // Draw draws a vertex array. @@ -203,10 +171,29 @@ func (va *VertexArray) Draw() { va.Do(func(Context) {}) } +// 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() { + gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, va.ebo) + gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, 4*len(indices32), gl.Ptr(indices32), uint32(va.usage)) + gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, 0) + }) +} + // SetVertex sets the value of all attributes of a vertex. // Argument data must be a slice/array containing the new vertex data. func (va *VertexArray) SetVertex(vertex int, data interface{}) { - if vertex < 0 || vertex >= va.count { + if vertex < 0 || vertex >= va.vertexNum { panic("set vertex error: invalid vertex index") } DoNoBlock(func() { @@ -220,7 +207,7 @@ func (va *VertexArray) SetVertex(vertex int, data interface{}) { } func (va *VertexArray) checkVertex(vertex int) { - if vertex < 0 || vertex >= va.count { + if vertex < 0 || vertex >= va.vertexNum { panic("invalid vertex index") } } @@ -322,14 +309,12 @@ func (va *VertexArray) Do(sub func(Context)) { } DoNoBlock(func() { gl.BindVertexArray(va.vao) - gl.BindBuffer(gl.ARRAY_BUFFER, va.vbo) }) va.enabled = true sub(ctx) va.enabled = false DoNoBlock(func() { - gl.DrawArrays(uint32(va.mode), 0, int32(va.count)) - gl.BindBuffer(gl.ARRAY_BUFFER, 0) + gl.DrawElements(gl.TRIANGLES, int32(va.indexNum), gl.UNSIGNED_INT, gl.PtrOffset(0)) gl.BindVertexArray(0) }) }) diff --git a/window.go b/window.go index 2aaa01b..5ad5d0e 100644 --- a/window.go +++ b/window.go @@ -53,6 +53,7 @@ type WindowConfig struct { // Window is a window handler. Use this type to manipulate a window (input, drawing, ...). type Window struct { + enabled bool window *glfw.Window config WindowConfig contextHolder pixelgl.ContextHolder @@ -314,22 +315,26 @@ var currentWindow struct { // Do makes the context of this window current, if it's not already, and executes sub. func (w *Window) Do(sub func(pixelgl.Context)) { - currentWindow.Lock() - defer currentWindow.Unlock() + if !w.enabled { + currentWindow.Lock() + defer currentWindow.Unlock() - if currentWindow.handler != w { - pixelgl.Do(func() { - w.window.MakeContextCurrent() - pixelgl.Init() - }) - currentWindow.handler = w + if currentWindow.handler != w { + pixelgl.Do(func() { + w.window.MakeContextCurrent() + pixelgl.Init() + }) + currentWindow.handler = w + } } + w.enabled = true if w.defaultShader != nil { w.defaultShader.Do(sub) } else { w.contextHolder.Do(sub) } + w.enabled = false } var defaultVertexFormat = pixelgl.VertexFormat{