From 9d4111282e90fa483109f1b5ede4382af59458d4 Mon Sep 17 00:00:00 2001 From: faiface Date: Mon, 28 Nov 2016 23:26:56 +0100 Subject: [PATCH] add context --- graphics.go | 58 -------------------------------------------- pixelgl/interface.go | 47 ++++++++++++++++++++++++++++++++--- pixelgl/shader.go | 10 ++++---- pixelgl/texture.go | 10 ++++---- pixelgl/vertex.go | 12 ++++----- window.go | 34 +++++++++++++------------- 6 files changed, 76 insertions(+), 95 deletions(-) delete mode 100644 graphics.go diff --git a/graphics.go b/graphics.go deleted file mode 100644 index 4381671..0000000 --- a/graphics.go +++ /dev/null @@ -1,58 +0,0 @@ -package pixel - -import "github.com/faiface/pixel/pixelgl" - -// Warning: technical stuff below. - -// VertexFormat is an internal format of the OpenGL vertex data. -// -// You can actually change this and all of the Pixel's functions will use the new format. -// Only change when you're implementing an OpenGL effect or something similar. -var VertexFormat = DefaultVertexFormat() - -// DefaultVertexFormat returns the default vertex format used by Pixel. -func DefaultVertexFormat() pixelgl.VertexFormat { - return pixelgl.VertexFormat{ - {Purpose: pixelgl.Position, Size: 2}, - {Purpose: pixelgl.Color, Size: 4}, - {Purpose: pixelgl.TexCoord, Size: 2}, - } -} - -// ConvertVertexData converts data in the oldFormat to the newFormat. Vertex attributes in the new format -// will be copied from the corresponding vertex attributes in the old format. If a vertex attribute in the new format -// has no corresponding attribute in the old format, it will be filled with zeros. -func ConvertVertexData(oldFormat, newFormat pixelgl.VertexFormat, data []float64) []float64 { - // calculate the mapping between old and new format - // if i is a start of a vertex attribute in the new format, then mapping[i] returns - // the index where the same attribute starts in the old format - mapping := make(map[int]int) - i := 0 - for _, newAttr := range newFormat { - j := 0 - for _, oldAttr := range oldFormat { - if newAttr == oldAttr { - mapping[i] = j - break - } - j += oldAttr.Size - } - i += newAttr.Size - } - - oldData, newData := data, []float64{} - - for i := 0; i < len(oldData); i += oldFormat.Size() { - j := 0 - for _, attr := range newFormat { - if oldIndex, ok := mapping[j]; ok { // the attribute was found in the old format - newData = append(newData, oldData[i+oldIndex:i+oldIndex+attr.Size]...) - } else { // the attribute wasn't found in the old format, so fill with zeros - newData = append(newData, make([]float64, attr.Size)...) - } - j += attr.Size - } - } - - return newData -} diff --git a/pixelgl/interface.go b/pixelgl/interface.go index 86ab87f..2b7fbd6 100644 --- a/pixelgl/interface.go +++ b/pixelgl/interface.go @@ -12,10 +12,10 @@ package pixelgl // The recommended way to implement a Doer is to wrap another Doer (vertex array wraps texture and so on), let's call // it parent. Then the Do method will look like this: // -// func (o *MyObject) Do(sub func()) { -// o.parent.Do(func() { +// func (o *MyObject) Do(sub func(Context)) { +// o.parent.Do(func(ctx Context) { // // enter the object's state -// sub() +// sub(ctx) // // leave the object's state // }) // } @@ -23,6 +23,45 @@ package pixelgl // It might seem difficult to grasp this kind of recursion at first, but it's really simple. What it's basically saying // is: "Hey parent, enter your state, then let me enter mine, then I'll do whatever I'm supposed to do in the middle. // After that I'll leave my state and please leave your state too parent." +// +// Also notice, that the functions are passing a Context around. This context contains the most important state variables. +// Usually, you just pass it as you received it. If you want to pass a changed context to your child (e.g. your a shader), +// use ctx.With* methods. type Doer interface { - Do(sub func()) + Do(sub func(Context)) +} + +// Context takes state from one object to another. OpenGL is a state machine, so we have to approach it like that. +// However, global variables are evil, so we have Context, that transfers important OpenGL state from one object to another. +// +// This type does *not* represent an OpenGL context in the OpenGL terminology. +type Context struct { + vertexFormat VertexFormat + shader *Shader +} + +// VertexFormat returns the current vertex format. +func (c Context) VertexFormat() VertexFormat { + return c.vertexFormat +} + +// Shader returns the current shader. +func (c Context) Shader() *Shader { + return c.shader +} + +// WithVertexFormat returns a copy of this context with the specified vertex format. +func (c Context) WithVertexFormat(vf VertexFormat) Context { + return Context{ + vertexFormat: vf, + shader: c.shader, + } +} + +// WithShader returns a copy of this context with the specified shader. +func (c Context) WithShader(s *Shader) Context { + return Context{ + vertexFormat: c.vertexFormat, + shader: s, + } } diff --git a/pixelgl/shader.go b/pixelgl/shader.go index 7c28f78..134f4c4 100644 --- a/pixelgl/shader.go +++ b/pixelgl/shader.go @@ -22,7 +22,7 @@ func NewShader(parent Doer, vertexShader, fragmentShader string) (*Shader, error } var err, glerr error - parent.Do(func() { + parent.Do(func(ctx Context) { err, glerr = DoErrGLErr(func() error { var vshader, fshader uint32 @@ -105,7 +105,7 @@ func NewShader(parent Doer, vertexShader, fragmentShader string) (*Shader, error // Delete deletes a shader program. Don't use a shader after deletion. func (s *Shader) Delete() { - s.parent.Do(func() { + s.parent.Do(func(ctx Context) { DoNoBlock(func() { gl.DeleteProgram(s.program) }) @@ -113,12 +113,12 @@ func (s *Shader) Delete() { } // Do stars using a shader, executes sub, and stops using it. -func (s *Shader) Do(sub func()) { - s.parent.Do(func() { +func (s *Shader) Do(sub func(Context)) { + s.parent.Do(func(ctx Context) { DoNoBlock(func() { gl.UseProgram(s.program) }) - sub() + sub(ctx.WithShader(s)) DoNoBlock(func() { gl.UseProgram(0) }) diff --git a/pixelgl/texture.go b/pixelgl/texture.go index 29ff28e..3f9f8c9 100644 --- a/pixelgl/texture.go +++ b/pixelgl/texture.go @@ -17,7 +17,7 @@ func NewTexture(parent Doer, width, height int, pixels []uint8) (*Texture, error texture := &Texture{parent: parent} var err error - parent.Do(func() { + parent.Do(func(ctx Context) { err = DoGLErr(func() { gl.GenTextures(1, &texture.tex) gl.BindTexture(gl.TEXTURE_2D, texture.tex) @@ -48,7 +48,7 @@ func NewTexture(parent Doer, width, height int, pixels []uint8) (*Texture, error // Delete deletes a texture. Don't use a texture after deletion. func (t *Texture) Delete() { - t.parent.Do(func() { + t.parent.Do(func(ctx Context) { DoNoBlock(func() { gl.DeleteTextures(1, &t.tex) }) @@ -56,12 +56,12 @@ func (t *Texture) Delete() { } // Do bind a texture, executes sub, and unbinds the texture. -func (t *Texture) Do(sub func()) { - t.parent.Do(func() { +func (t *Texture) Do(sub func(Context)) { + t.parent.Do(func(ctx Context) { DoNoBlock(func() { gl.BindTexture(gl.TEXTURE_2D, t.tex) }) - sub() + sub(ctx) DoNoBlock(func() { gl.BindTexture(gl.TEXTURE_2D, 0) }) diff --git a/pixelgl/vertex.go b/pixelgl/vertex.go index a0ed2f4..a9c8420 100644 --- a/pixelgl/vertex.go +++ b/pixelgl/vertex.go @@ -125,7 +125,7 @@ func NewVertexArray(parent Doer, format VertexFormat, mode VertexDrawMode, usage } var err error - parent.Do(func() { + parent.Do(func(ctx Context) { err = DoGLErr(func() { gl.GenVertexArrays(1, &va.vao) gl.BindVertexArray(va.vao) @@ -161,7 +161,7 @@ func NewVertexArray(parent Doer, format VertexFormat, mode VertexDrawMode, usage // Delete deletes a vertex array and it's associated vertex buffer. Don't use a vertex array after deletion. func (va *VertexArray) Delete() { - va.parent.Do(func() { + va.parent.Do(func(ctx Context) { DoNoBlock(func() { gl.DeleteVertexArrays(1, &va.vao) gl.DeleteBuffers(1, &va.vbo) @@ -193,7 +193,7 @@ func (va *VertexArray) DrawMode() VertexDrawMode { // Draw draws a vertex array. func (va *VertexArray) Draw() { - va.Do(func() {}) + va.Do(func(Context) {}) } // Data returns a copy of data inside a vertex array (actually it's vertex buffer). @@ -233,13 +233,13 @@ func (va *VertexArray) SetVertexAttribute(vertex int, attr VertexAttribute, data } // Do binds a vertex arrray and it's associated vertex buffer, executes sub, and unbinds the vertex array and it's vertex buffer. -func (va *VertexArray) Do(sub func()) { - va.parent.Do(func() { +func (va *VertexArray) Do(sub func(Context)) { + va.parent.Do(func(ctx Context) { DoNoBlock(func() { gl.BindVertexArray(va.vao) gl.BindBuffer(gl.ARRAY_BUFFER, va.vbo) }) - sub() + sub(ctx) DoNoBlock(func() { gl.DrawArrays(uint32(va.mode), 0, int32(va.count)) gl.BindBuffer(gl.ARRAY_BUFFER, 0) diff --git a/window.go b/window.go index d06c9b2..6b5c85a 100644 --- a/window.go +++ b/window.go @@ -108,7 +108,7 @@ func NewWindow(config WindowConfig) (*Window, error) { // Delete destroys a window. The window can't be used any further. func (w *Window) Delete() { - w.Do(func() { + w.Do(func(pixelgl.Context) { pixelgl.Do(func() { w.window.Destroy() }) @@ -117,14 +117,14 @@ func (w *Window) Delete() { // Clear clears the window with a color. func (w *Window) Clear(c color.Color) { - w.Do(func() { + w.Do(func(pixelgl.Context) { pixelgl.Clear(colorToRGBA(c)) }) } // Update swaps buffers and polls events. func (w *Window) Update() { - w.Do(func() { + w.Do(func(pixelgl.Context) { pixelgl.Do(func() { if w.config.VSync { glfw.SwapInterval(1) @@ -137,7 +137,7 @@ func (w *Window) Update() { // SetTitle changes the title of a window. func (w *Window) SetTitle(title string) { - w.Do(func() { + w.Do(func(pixelgl.Context) { pixelgl.Do(func() { w.window.SetTitle(title) }) @@ -147,7 +147,7 @@ func (w *Window) SetTitle(title string) { // SetSize resizes a window to the specified size in pixels. // In case of a fullscreen window, it changes the resolution of that window. func (w *Window) SetSize(width, height float64) { - w.Do(func() { + w.Do(func(pixelgl.Context) { pixelgl.Do(func() { w.window.SetSize(int(width), int(height)) }) @@ -156,7 +156,7 @@ func (w *Window) SetSize(width, height float64) { // Size returns the size of the client area of a window (the part you can draw on). func (w *Window) Size() (width, height float64) { - w.Do(func() { + w.Do(func(pixelgl.Context) { pixelgl.Do(func() { wi, hi := w.window.GetSize() width = float64(wi) @@ -168,7 +168,7 @@ func (w *Window) Size() (width, height float64) { // Show makes a window visible if it was hidden. func (w *Window) Show() { - w.Do(func() { + w.Do(func(pixelgl.Context) { pixelgl.Do(func() { w.window.Show() }) @@ -177,7 +177,7 @@ func (w *Window) Show() { // Hide hides a window if it was visible. func (w *Window) Hide() { - w.Do(func() { + w.Do(func(pixelgl.Context) { pixelgl.Do(func() { w.window.Hide() }) @@ -191,7 +191,7 @@ func (w *Window) Hide() { func (w *Window) SetFullscreen(monitor *Monitor) { if w.Monitor() != monitor { if monitor == nil { - w.Do(func() { + w.Do(func(pixelgl.Context) { pixelgl.Do(func() { w.window.SetMonitor( nil, @@ -204,7 +204,7 @@ func (w *Window) SetFullscreen(monitor *Monitor) { }) }) } else { - w.Do(func() { + w.Do(func(pixelgl.Context) { pixelgl.Do(func() { w.restore.xpos, w.restore.ypos = w.window.GetPos() w.restore.width, w.restore.height = w.window.GetSize() @@ -233,7 +233,7 @@ func (w *Window) IsFullscreen() bool { // Monitor returns a monitor a fullscreen window is on. If the window is not fullscreen, this function returns nil. func (w *Window) Monitor() *Monitor { var monitor *glfw.Monitor - w.Do(func() { + w.Do(func(pixelgl.Context) { monitor = pixelgl.DoVal(func() interface{} { return w.window.GetMonitor() }).(*glfw.Monitor) @@ -248,7 +248,7 @@ func (w *Window) Monitor() *Monitor { // Focus brings a window to the front and sets input focus. func (w *Window) Focus() { - w.Do(func() { + w.Do(func(pixelgl.Context) { pixelgl.Do(func() { w.window.Focus() }) @@ -258,7 +258,7 @@ func (w *Window) Focus() { // Focused returns true if a window has input focus. func (w *Window) Focused() bool { var focused bool - w.Do(func() { + w.Do(func(pixelgl.Context) { focused = pixelgl.DoVal(func() interface{} { return w.window.GetAttrib(glfw.Focused) == glfw.True }).(bool) @@ -268,7 +268,7 @@ func (w *Window) Focused() bool { // Maximize puts a windowed window to a maximized state. func (w *Window) Maximize() { - w.Do(func() { + w.Do(func(pixelgl.Context) { pixelgl.Do(func() { w.window.Maximize() }) @@ -277,7 +277,7 @@ func (w *Window) Maximize() { // Restore restores a windowed window from a maximized state. func (w *Window) Restore() { - w.Do(func() { + w.Do(func(pixelgl.Context) { pixelgl.Do(func() { w.window.Restore() }) @@ -290,7 +290,7 @@ 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()) { +func (w *Window) Do(sub func(pixelgl.Context)) { currentWindow.Lock() defer currentWindow.Unlock() @@ -302,5 +302,5 @@ func (w *Window) Do(sub func()) { currentWindow.handler = w } - sub() + sub(pixelgl.Context{}) }