From cafda2cfae99b77e0ba8ab1c91dccb7695b3bc55 Mon Sep 17 00:00:00 2001 From: faiface Date: Sun, 1 Jan 2017 22:12:12 +0100 Subject: [PATCH] backup before reinstall --- graphics.go | 209 ++++++++++++++------------------------------------- interface.go | 21 ++++++ picture.go | 23 +++--- window.go | 146 +++++++++++++++++++++-------------- 4 files changed, 180 insertions(+), 219 deletions(-) create mode 100644 interface.go diff --git a/graphics.go b/graphics.go index 03e3893..132e470 100644 --- a/graphics.go +++ b/graphics.go @@ -9,72 +9,6 @@ import ( "github.com/pkg/errors" ) -// Drawer is anything that can be drawn. It's by no means a drawer inside your table. -// -// Drawer consists of a single methods: Draw. Draw methods takes any number of Transform -// arguments. It applies these transforms in the reverse order and finally draws something -// transformed by these transforms. -// -// Example: -// -// // object is a drawer -// object.Draw(pixel.Position(pixel.V(100, 100).Rotate(math.Pi / 2))) -// camera := pixel.Camera(pixel.V(0, 0), pixel.V(500, 500), pixel.V(window.Size())) -// object.Draw(camera, pixel.Position(0).Scale(0.5)) -type Drawer interface { - Draw(t ...Transform) -} - -// Group is used to effeciently handle a collection of objects with a common parent. Usually many -// objects share a parent, using a group can significantly increase performance in these cases. -// -// To use a group, first, create a group and as it's parent use the common parent of the -// collection of objects: -// -// group := pixel.NewGroup(commonParent) -// -// Then, when creating the objects, use the group as their parent, instead of the original -// common parent, but, don't forget to put everything into a With block, like this: -// -// group.With(func() { -// object := newArbitratyObject(group, ...) // group is the parent of the object -// }) -// -// When dealing with objects associated with a group, it's always necessary to wrap that into -// a With block: -// -// group.With(func() { -// for _, obj := range objectsWithCommonParent { -// // do something with obj -// } -// }) -// -// That's all! -type Group struct { - parent pixelgl.Doer - context pixelgl.Context -} - -// NewGroup creates a new group with the specified parent. -func NewGroup(parent pixelgl.Doer) *Group { - return &Group{ - parent: parent, - } -} - -// With enables the parent of a group and executes sub. -func (g *Group) With(sub func()) { - g.parent.Do(func(ctx pixelgl.Context) { - g.context = ctx - sub() - }) -} - -// Do just passes a cached context to sub. -func (g *Group) Do(sub func(pixelgl.Context)) { - sub(g.context) -} - // Shape is a general drawable shape constructed from vertices. // // Vertices are specified in the vertex array of a shape. A shape can have a picture, a color @@ -83,21 +17,21 @@ func (g *Group) Do(sub func(pixelgl.Context)) { // Usually you use this type only indirectly throught other specific shapes (sprites, polygons, // ...) embedding it. type Shape struct { - parent pixelgl.Doer picture *Picture color color.Color transform Transform - va *pixelgl.VertexArray + vertices []map[pixelgl.Attr]interface{} + vas map[Target]*pixelgl.VertexArray } // NewShape creates a new shape with specified parent, picture, color, transform and vertex array. -func NewShape(parent pixelgl.Doer, picture *Picture, c color.Color, transform Transform, va *pixelgl.VertexArray) *Shape { +func NewShape(picture *Picture, c color.Color, transform Transform, vertices []map[pixelgl.Attr]interface{}) *Shape { return &Shape{ - parent: parent, picture: picture, color: c, transform: transform, - va: va, + vertices: vertices, + vas: make(map[Target]*pixelgl.VertexArray), } } @@ -131,30 +65,45 @@ func (s *Shape) Transform() Transform { return s.transform } -// VertexArray changes the underlying vertex array of a shape. -func (s *Shape) VertexArray() *pixelgl.VertexArray { - return s.va +// Vertices returns the vertex attribute values of all vertices in a shape. +// +// Do not change! +func (s *Shape) Vertices() []map[pixelgl.Attr]interface{} { + return s.vertices } // Draw draws a sprite transformed by the supplied transforms applied in the reverse order. -func (s *Shape) Draw(t ...Transform) { +func (s *Shape) Draw(target Target, t ...Transform) { mat := mgl32.Ident3() for i := range t { mat = mat.Mul3(t[i].Mat()) } mat = mat.Mul3(s.transform.Mat()) - s.parent.Do(func(ctx pixelgl.Context) { - c := NRGBAModel.Convert(s.color).(NRGBA) - ctx.Shader().SetUniformAttr(maskColorVec4, mgl32.Vec4{float32(c.R), float32(c.G), float32(c.B), float32(c.A)}) - ctx.Shader().SetUniformAttr(transformMat3, mat) + c := NRGBAModel.Convert(s.color).(NRGBA) + + if s.vas[target] == nil { + s.vas[target] = target.MakeVertexArray(s.vertices) + } + va := s.vas[target] + + pixelgl.Do(func() { + target.Begin() + defer target.End() + + target.Shader().SetUniformAttr(maskColorVec4, mgl32.Vec4{float32(c.R), float32(c.G), float32(c.B), float32(c.A)}) + target.Shader().SetUniformAttr(transformMat3, mat) if s.picture != nil { - s.picture.Texture().Do(func(pixelgl.Context) { - s.va.Draw() - }) + s.picture.Texture().Begin() + va.Begin() + va.Draw() + va.End() + s.picture.Texture().End() } else { - s.va.Draw() + va.Begin() + va.Draw() + va.End() } }) } @@ -173,7 +122,7 @@ type MultiShape struct { // NewMultiShape creates a new multishape from several other shapes. // // If two of the supplied shapes have different pictures, this function panics. -func NewMultiShape(parent pixelgl.Doer, shapes ...*Shape) *MultiShape { +func NewMultiShape(shapes ...*Shape) *MultiShape { var picture *Picture for _, shape := range shapes { if picture != nil && shape.Picture() != nil && shape.Picture().Texture().ID() != picture.Texture().ID() { @@ -184,20 +133,9 @@ func NewMultiShape(parent pixelgl.Doer, shapes ...*Shape) *MultiShape { } } - var va *pixelgl.VertexArray - - var indices []int - offset := 0 - for _, shape := range shapes { - for _, i := range shape.va.Indices() { - indices = append(indices, offset+i) - } - offset += shape.VertexArray().NumVertices() - } - var vertices []map[pixelgl.Attr]interface{} for _, shape := range shapes { - shapeVertices := shape.VertexArray().Vertices() + shapeVertices := shape.Vertices() for vertex := range shapeVertices { if pos, ok := shapeVertices[vertex][positionVec2]; ok { @@ -221,22 +159,7 @@ func NewMultiShape(parent pixelgl.Doer, shapes ...*Shape) *MultiShape { vertices = append(vertices, shapeVertices...) } - parent.Do(func(ctx pixelgl.Context) { - var err error - va, err = pixelgl.NewVertexArray( - pixelgl.ContextHolder{Context: ctx}, - ctx.Shader().VertexFormat(), - len(vertices), - indices, - ) - if err != nil { - panic(errors.Wrap(err, "failed to create multishape")) - } - }) - - va.SetVertices(vertices) - - return &MultiShape{NewShape(parent, picture, color.White, Position(0), va)} + return &MultiShape{NewShape(picture, color.White, Position(0), vertices)} } // Sprite is a picture that can be drawn on the screen. Optionally it can be color masked @@ -252,25 +175,10 @@ type Sprite struct { // NewSprite creates a new sprite with the supplied picture. The sprite's size is the size of // the supplied picture. If you want to change the sprite's size, change it's transform. -func NewSprite(parent pixelgl.Doer, picture *Picture) *Sprite { - var va *pixelgl.VertexArray - - parent.Do(func(ctx pixelgl.Context) { - var err error - va, err = pixelgl.NewVertexArray( - pixelgl.ContextHolder{Context: ctx}, - ctx.Shader().VertexFormat(), - 4, - []int{0, 1, 2, 0, 2, 3}, - ) - if err != nil { - panic(errors.Wrap(err, "failed to create sprite")) - } - }) +func NewSprite(picture *Picture) *Sprite { + w, h := picture.Bounds().Size.XY() vertices := make([]map[pixelgl.Attr]interface{}, 4) - - w, h := picture.Bounds().Size.XY() for i, p := range []Vec{V(0, 0), V(w, 0), V(w, h), V(0, h)} { texCoord := V( (picture.Bounds().X()+p.X())/float64(picture.Texture().Width()), @@ -284,9 +192,16 @@ func NewSprite(parent pixelgl.Doer, picture *Picture) *Sprite { } } - va.SetVertices(vertices) + vertices = []map[pixelgl.Attr]interface{}{ + vertices[0], + vertices[1], + vertices[2], + vertices[0], + vertices[2], + vertices[3], + } - return &Sprite{NewShape(parent, picture, color.White, Position(0), va)} + return &Sprite{NewShape(picture, color.White, Position(0), vertices)} } // LineColor a line shape (with sharp ends) filled with a single color. @@ -296,26 +211,9 @@ type LineColor struct { width float64 } -// NewLineColor creates a new line shape between points A and B filled with a single color. Parent -// is an object that this shape belongs to, such as a window, or a graphics effect. -func NewLineColor(parent pixelgl.Doer, c color.Color, a, b Vec, width float64) *LineColor { - var va *pixelgl.VertexArray - - parent.Do(func(ctx pixelgl.Context) { - var err error - va, err = pixelgl.NewVertexArray( - pixelgl.ContextHolder{Context: ctx}, - ctx.Shader().VertexFormat(), - 4, - []int{0, 1, 2, 1, 2, 3}, - ) - if err != nil { - panic(errors.Wrap(err, "failed to create line")) - } - }) - +// NewLineColor creates a new line shape between points A and B filled with a single color. +func NewLineColor(c color.Color, a, b Vec, width float64) *LineColor { vertices := make([]map[pixelgl.Attr]interface{}, 4) - for i := 0; i < 4; i++ { vertices[i] = map[pixelgl.Attr]interface{}{ colorVec4: mgl32.Vec4{1, 1, 1, 1}, @@ -323,9 +221,16 @@ func NewLineColor(parent pixelgl.Doer, c color.Color, a, b Vec, width float64) * } } - va.SetVertices(vertices) + vertices = []map[pixelgl.Attr]interface{}{ + vertices[0], + vertices[1], + vertices[2], + vertices[1], + vertices[2], + vertices[3], + } - lc := &LineColor{NewShape(parent, nil, c, Position(0), va), a, b, width} + lc := &LineColor{NewShape(nil, c, Position(0), vertices), a, b, width} lc.setPoints() return lc } diff --git a/interface.go b/interface.go new file mode 100644 index 0000000..6bfd693 --- /dev/null +++ b/interface.go @@ -0,0 +1,21 @@ +package pixel + +import "github.com/faiface/pixel/pixelgl" + +// Target is an OpenGL graphics destination such as a window, a canvas, and so on. Something that +// you can draw on. +type Target interface { + pixelgl.BeginEnder + Shader() *pixelgl.Shader + + // MakeVertexArray returns a new vertex array drawable on the Target. + MakeVertexArray(vertices []map[pixelgl.Attr]interface{}) *pixelgl.VertexArray +} + +// Drawer is anything that can be drawn. It's by no means a drawer inside your table. +// +// Drawer consists of a single methods: Draw. Draw takes a target and any number of transform +// arguments. It's up to a Drawer to make sure that it draws correctly onto the provided target. +type Drawer interface { + Draw(target Target, t ...Transform) +} diff --git a/picture.go b/picture.go index 54f4e79..101551d 100644 --- a/picture.go +++ b/picture.go @@ -24,16 +24,19 @@ func NewPicture(img image.Image, smooth bool) *Picture { rgba := image.NewRGBA(image.Rect(0, 0, img.Bounds().Dx(), img.Bounds().Dy())) draw.Draw(rgba, rgba.Bounds(), img, img.Bounds().Min, draw.Src) - texture, err := pixelgl.NewTexture( - pixelgl.NoOpDoer, - img.Bounds().Dx(), - img.Bounds().Dy(), - smooth, - rgba.Pix, - ) - if err != nil { - panic(errors.Wrap(err, "failed to create picture")) - } + var texture *pixelgl.Texture + pixelgl.Do(func() { + var err error + texture, err = pixelgl.NewTexture( + img.Bounds().Dx(), + img.Bounds().Dy(), + smooth, + rgba.Pix, + ) + if err != nil { + panic(errors.Wrap(err, "failed to create picture")) + } + }) return &Picture{ texture: texture, diff --git a/window.go b/window.go index 4939d93..9879eec 100644 --- a/window.go +++ b/window.go @@ -2,7 +2,6 @@ package pixel import ( "image/color" - "sync" "runtime" @@ -58,10 +57,10 @@ 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 - defaultShader *pixelgl.Shader + enabled bool + window *glfw.Window + config WindowConfig + shader *pixelgl.Shader // need to save these to correctly restore a fullscreen window restore struct { @@ -74,6 +73,8 @@ type Window struct { } } +var currentWindow *Window + // NewWindow creates a new window with it's properties specified in the provided config. // // If window creation fails, an error is returned. @@ -103,7 +104,11 @@ func NewWindow(config WindowConfig) (*Window, error) { glfw.WindowHint(glfw.Maximized, bool2int[config.Maximized]) glfw.WindowHint(glfw.Samples, config.MSAASamples) - w.window, err = glfw.CreateWindow(int(config.Width), int(config.Height), config.Title, nil, nil) + var share *glfw.Window + if currentWindow != nil { + share = currentWindow.window + } + w.window, err = glfw.CreateWindow(int(config.Width), int(config.Height), config.Title, nil, share) if err != nil { return err } @@ -114,13 +119,11 @@ func NewWindow(config WindowConfig) (*Window, error) { return nil, errors.Wrap(err, "creating window failed") } - w.initInput() + pixelgl.Do(func() { + w.Begin() + defer w.End() - w.SetFullscreen(config.Fullscreen) - - w.Do(func(pixelgl.Context) { - w.defaultShader, err = pixelgl.NewShader( - pixelgl.NoOpDoer, + w.shader, err = pixelgl.NewShader( defaultVertexFormat, defaultUniformFormat, defaultVertexShader, @@ -128,14 +131,21 @@ func NewWindow(config WindowConfig) (*Window, error) { ) // default uniforms - w.defaultShader.SetUniformAttr(maskColorVec4, mgl32.Vec4{1, 1, 1, 1}) - w.defaultShader.SetUniformAttr(transformMat3, mgl32.Ident3()) + w.shader.Begin() + w.shader.SetUniformAttr(maskColorVec4, mgl32.Vec4{1, 1, 1, 1}) + w.shader.SetUniformAttr(transformMat3, mgl32.Ident3()) + + // this is tricky, w.shader.End() is not needed here, because it will + // actually be ended by a deferred w.End() call }) if err != nil { w.Destroy() return nil, errors.Wrap(err, "creating window failed") } + w.initInput() + w.SetFullscreen(config.Fullscreen) + runtime.SetFinalizer(w, (*Window).Destroy) return w, nil @@ -150,37 +160,42 @@ func (w *Window) Destroy() { // Clear clears the window with a color. func (w *Window) Clear(c color.Color) { - w.Do(func(pixelgl.Context) { - pixelgl.DoNoBlock(func() { - c := NRGBAModel.Convert(c).(NRGBA) - gl.ClearColor(float32(c.R), float32(c.G), float32(c.B), float32(c.A)) - gl.Clear(gl.COLOR_BUFFER_BIT) - }) + pixelgl.DoNoBlock(func() { + w.Begin() + defer w.End() + + c := NRGBAModel.Convert(c).(NRGBA) + gl.ClearColor(float32(c.R), float32(c.G), float32(c.B), float32(c.A)) + gl.Clear(gl.COLOR_BUFFER_BIT) }) } // Update swaps buffers and polls events. func (w *Window) Update() { - w.Do(func(pixelgl.Context) { - pixelgl.Do(func() { - if w.config.VSync { - glfw.SwapInterval(1) - } - w.window.SwapBuffers() - }) + pixelgl.Do(func() { + w.Begin() + defer w.End() - w.updateInput() + if w.config.VSync { + glfw.SwapInterval(1) + } + w.window.SwapBuffers() + }) - pixelgl.Do(func() { - w, h := w.window.GetSize() - gl.Viewport(0, 0, int32(w), int32(h)) - }) + w.updateInput() + + pixelgl.Do(func() { + w.Begin() + defer w.End() + + w, h := w.window.GetSize() + gl.Viewport(0, 0, int32(w), int32(h)) }) } -// DefaultShader returns the default shader used by a window. -func (w *Window) DefaultShader() *pixelgl.Shader { - return w.defaultShader +// Shader returns the default shader used by a window. +func (w *Window) Shader() *pixelgl.Shader { + return w.shader } // SetClosed sets the closed flag of a window. @@ -327,33 +342,50 @@ func (w *Window) Restore() { }) } -var currentWindow struct { - sync.Mutex - handler *Window +// Begin makes the OpenGL context of a window current and binds it's shader. +// +// You usually do not need to use this method, however, you have to use it when you're +// directly using this window's context (such as drawing on it using OpenGL). +// +// Note, that this method must be called inside the main OpenGL thread (pixelgl.Do/DoNoBlock/DoErr/DoVal). +func (w *Window) Begin() { + if currentWindow != w { + w.window.MakeContextCurrent() + pixelgl.Init() + currentWindow = w + } + if w.shader != nil { + w.shader.Begin() + } } -// Do makes the context of this window current, if it's not already, and executes sub. -func (w *Window) Do(sub func(pixelgl.Context)) { - if !w.enabled { - currentWindow.Lock() - defer currentWindow.Unlock() +// End unbinds the shader of a window. +func (w *Window) End() { + if w.shader != nil { + w.shader.End() + } +} - if currentWindow.handler != w { - pixelgl.Do(func() { - w.window.MakeContextCurrent() - pixelgl.Init() - }) - currentWindow.handler = w +// MakeVertexArray implements Target. +func (w *Window) MakeVertexArray(vertices []map[pixelgl.Attr]interface{}) *pixelgl.VertexArray { + var va *pixelgl.VertexArray + + pixelgl.Do(func() { + w.Begin() + defer w.End() + + var err error + va, err = pixelgl.NewVertexArray(w.Shader(), len(vertices)) + if err != nil { + panic(err) } - } - w.enabled = true - if w.defaultShader != nil { - w.defaultShader.Do(sub) - } else { - sub(pixelgl.Context{}) - } - w.enabled = false + va.Begin() + va.SetVertices(vertices) + va.End() + }) + + return va } var defaultVertexFormat = pixelgl.AttrFormat{