From 63339235b7b8b3d14904b0fa67728e17103215f2 Mon Sep 17 00:00:00 2001 From: faiface Date: Sat, 28 Jan 2017 20:01:59 +0100 Subject: [PATCH] rework Triangles API and adapt it --- batch.go | 12 ++++++-- canvas.go | 21 +++++-------- gltriangles.go | 55 +++++++++++++++++++++------------- graphics.go | 81 ++++++++++++++++++++++---------------------------- interface.go | 49 ++++++++++++++++++------------ window.go | 2 +- 6 files changed, 116 insertions(+), 104 deletions(-) diff --git a/batch.go b/batch.go index b7c2ecc..72aa476 100644 --- a/batch.go +++ b/batch.go @@ -35,7 +35,8 @@ func NewBatch(pic *Picture, container Triangles) *Batch { // Clear removes all objects from the Batch. func (b *Batch) Clear() { - b.cont.Update(&TrianglesData{}) + b.cont.SetLen(0) + b.cont.Dirty() } // Draw draws all objects that are currently in the Batch onto another Target. @@ -45,10 +46,11 @@ func (b *Batch) Draw(t Target) { } // MakeTriangles returns a specialized copy of the provided Triangles, that draws onto this Batch. -func (b *Batch) MakeTriangles(t Triangles) Triangles { +func (b *Batch) MakeTriangles(t Triangles) TargetTriangles { return &batchTriangles{ Triangles: t.Copy(), trans: t.Copy(), + data: MakeTrianglesData(t.Len()), batch: b, } } @@ -101,5 +103,9 @@ func (bt *batchTriangles) Draw() { } } bt.trans.Update(&bt.data) - bt.batch.cont.Append(bt.trans) + + cont := bt.batch.cont + cont.SetLen(cont.Len() + bt.trans.Len()) + cont.Slice(cont.Len()-bt.trans.Len(), cont.Len()).Update(bt.trans) + cont.Dirty() } diff --git a/canvas.go b/canvas.go index e082c18..c472283 100644 --- a/canvas.go +++ b/canvas.go @@ -129,11 +129,11 @@ func (c *Canvas) Draw(t Target) { } // MakeTriangles returns Triangles that draw onto this Canvas. -func (c *Canvas) MakeTriangles(t Triangles) Triangles { - tpcs := NewGLTriangles(c.s, t).(trianglesPositionColorTexture) +func (c *Canvas) MakeTriangles(t Triangles) TargetTriangles { + gt := NewGLTriangles(c.s, t).(*glTriangles) return &canvasTriangles{ - c: c, - trianglesPositionColorTexture: tpcs, + c: c, + glTriangles: gt, } } @@ -170,16 +170,9 @@ func (c *Canvas) SetMaskColor(col color.Color) { c.col = mgl32.Vec4{r, g, b, a} } -type trianglesPositionColorTexture interface { - Triangles - Position(i int) Vec - Color(i int) NRGBA - Texture(i int) Vec -} - type canvasTriangles struct { c *Canvas - trianglesPositionColorTexture + *glTriangles } func (ct *canvasTriangles) Draw() { @@ -199,10 +192,10 @@ func (ct *canvasTriangles) Draw() { if pic != nil { pic.Texture().Begin() - ct.trianglesPositionColorTexture.Draw() + ct.glTriangles.Draw() pic.Texture().End() } else { - ct.trianglesPositionColorTexture.Draw() + ct.glTriangles.Draw() } ct.c.s.End() diff --git a/gltriangles.go b/gltriangles.go index 1e55e4a..ca022fa 100644 --- a/gltriangles.go +++ b/gltriangles.go @@ -1,6 +1,8 @@ package pixel import ( + "fmt" + "github.com/faiface/mainthread" "github.com/faiface/pixel/pixelgl" ) @@ -13,7 +15,7 @@ import ( // Draw method simply draws the underlying pixelgl.VertexSlice. It needs to be called in the main // thread manually. Also, you need to take care of additional Target initialization or setting of // uniform attributes. -func NewGLTriangles(shader *pixelgl.Shader, t Triangles) Triangles { +func NewGLTriangles(shader *pixelgl.Shader, t Triangles) TargetTriangles { var gt *glTriangles mainthread.Call(func() { gt = &glTriangles{ @@ -21,6 +23,7 @@ func NewGLTriangles(shader *pixelgl.Shader, t Triangles) Triangles { shader: shader, } }) + gt.SetLen(t.Len()) gt.Update(t) return gt } @@ -35,13 +38,7 @@ func (gt *glTriangles) Len() int { return len(gt.data) / gt.vs.Stride() } -func (gt *glTriangles) Draw() { - gt.vs.Begin() - gt.vs.Draw() - gt.vs.End() -} - -func (gt *glTriangles) resize(len int) { +func (gt *glTriangles) SetLen(len int) { if len > gt.Len() { needAppend := len - gt.Len() for i := 0; i < needAppend; i++ { @@ -57,10 +54,24 @@ func (gt *glTriangles) resize(len int) { } } -func (gt *glTriangles) updateData(offset int, t Triangles) { +func (gt *glTriangles) Slice(i, j int) Triangles { + return &glTriangles{ + vs: gt.vs.Slice(i, j), + data: gt.data[i*gt.vs.Stride() : j*gt.vs.Stride()], + shader: gt.shader, + } +} + +func (gt *glTriangles) updateData(t Triangles) { + // glTriangles short path + if t, ok := t.(*glTriangles); ok { + copy(gt.data, t.data) + return + } + // TrianglesData short path if t, ok := t.(*TrianglesData); ok { - for i := offset; i < offset+t.Len(); i++ { + for i := 0; i < gt.Len(); i++ { var ( px, py = (*t)[i].Position.XY() col = (*t)[i].Color @@ -79,14 +90,14 @@ func (gt *glTriangles) updateData(offset int, t Triangles) { } if t, ok := t.(TrianglesPosition); ok { - for i := offset; i < offset+t.Len(); i++ { + for i := 0; i < gt.Len(); i++ { px, py := t.Position(i).XY() gt.data[i*gt.vs.Stride()+0] = float32(px) gt.data[i*gt.vs.Stride()+1] = float32(py) } } if t, ok := t.(TrianglesColor); ok { - for i := offset; i < offset+t.Len(); i++ { + for i := 0; i < gt.Len(); i++ { col := t.Color(i) gt.data[i*gt.vs.Stride()+2] = float32(col.R) gt.data[i*gt.vs.Stride()+3] = float32(col.G) @@ -95,7 +106,7 @@ func (gt *glTriangles) updateData(offset int, t Triangles) { } } if t, ok := t.(TrianglesTexture); ok { - for i := offset; i < offset+t.Len(); i++ { + for i := 0; i < gt.Len(); i++ { tx, ty := t.Texture(i).XY() gt.data[i*gt.vs.Stride()+6] = float32(tx) gt.data[i*gt.vs.Stride()+7] = float32(ty) @@ -120,14 +131,10 @@ func (gt *glTriangles) submitData() { } func (gt *glTriangles) Update(t Triangles) { - gt.resize(t.Len()) - gt.updateData(0, t) - gt.submitData() -} - -func (gt *glTriangles) Append(t Triangles) { - gt.resize(gt.Len() + t.Len()) - gt.updateData(gt.Len()-t.Len(), t) + if gt.Len() != t.Len() { + panic(fmt.Errorf("%T.Update: invalid triangles len", gt)) + } + gt.updateData(t) gt.submitData() } @@ -135,6 +142,12 @@ func (gt *glTriangles) Copy() Triangles { return NewGLTriangles(gt.shader, gt) } +func (gt *glTriangles) Draw() { + gt.vs.Begin() + gt.vs.Draw() + gt.vs.End() +} + func (gt *glTriangles) Position(i int) Vec { px := gt.data[i*gt.vs.Stride()+0] py := gt.data[i*gt.vs.Stride()+1] diff --git a/graphics.go b/graphics.go index eceaad8..637ea4e 100644 --- a/graphics.go +++ b/graphics.go @@ -13,6 +13,21 @@ type TrianglesData []struct { Texture Vec } +// MakeTrianglesData creates TrianglesData of length len initialized with default property values. +// +// Prefer this function to make(TrianglesData, len), because make zeros them, while this function +// does a correct intialization. +func MakeTrianglesData(len int) TrianglesData { + td := TrianglesData{} + td.SetLen(len) + return td +} + +// Len returns the number of vertices in TrianglesData. +func (td *TrianglesData) Len() int { + return len(*td) +} + // SetLen resizes TrianglesData to len, while keeping the original content. // // If len is greater than TrianglesData's current length, the new data is filled with default @@ -33,36 +48,32 @@ func (td *TrianglesData) SetLen(len int) { } } -// Len returns the number of vertices in TrianglesData. -func (td *TrianglesData) Len() int { - return len(*td) +// Slice returns a sub-Triangles of this TrianglesData. +func (td *TrianglesData) Slice(i, j int) Triangles { + s := TrianglesData((*td)[i:j]) + return &s } -// Draw is unimplemented for TrianglesData and panics. -func (td *TrianglesData) Draw() { - panic(fmt.Errorf("%T.Draw: invalid operation", td)) -} - -func (td *TrianglesData) updateData(offset int, t Triangles) { +func (td *TrianglesData) updateData(t Triangles) { // fast path optimization if t, ok := t.(*TrianglesData); ok { - copy((*td)[offset:], *t) + copy(*td, *t) return } // slow path manual copy if t, ok := t.(TrianglesPosition); ok { - for i := offset; i < len(*td); i++ { + for i := range *td { (*td)[i].Position = t.Position(i) } } if t, ok := t.(TrianglesColor); ok { - for i := offset; i < len(*td); i++ { + for i := range *td { (*td)[i].Color = t.Color(i) } } if t, ok := t.(TrianglesTexture); ok { - for i := offset; i < len(*td); i++ { + for i := range *td { (*td)[i].Texture = t.Texture(i) } } @@ -72,19 +83,16 @@ func (td *TrianglesData) updateData(offset int, t Triangles) { // // TrianglesPosition, TrianglesColor and TrianglesTexture are supported. func (td *TrianglesData) Update(t Triangles) { - td.SetLen(t.Len()) - td.updateData(0, t) -} - -// Append adds supplied Triangles to the end of the TrianglesData. -func (td *TrianglesData) Append(t Triangles) { - td.SetLen(td.Len() + t.Len()) - td.updateData(td.Len()-t.Len(), t) + if td.Len() != t.Len() { + panic(fmt.Errorf("%T.Update: invalid triangles length", td)) + } + td.updateData(t) } // Copy returns an exact independent copy of this TrianglesData. func (td *TrianglesData) Copy() Triangles { - copyTd := make(TrianglesData, td.Len()) + copyTd := TrianglesData{} + copyTd.SetLen(td.Len()) copyTd.Update(td) return ©Td } @@ -111,7 +119,7 @@ func (td *TrianglesData) Texture(i int) Vec { type TrianglesDrawer struct { Triangles - tris map[Target]Triangles + tris map[Target]TargetTriangles dirty bool } @@ -129,7 +137,7 @@ func (td *TrianglesDrawer) flush() { // Draw draws the wrapped Triangles onto the provided Target. func (td *TrianglesDrawer) Draw(target Target) { if td.tris == nil { - td.tris = make(map[Target]Triangles) + td.tris = make(map[Target]TargetTriangles) } td.flush() @@ -142,27 +150,8 @@ func (td *TrianglesDrawer) Draw(target Target) { tri.Draw() } -// Update updates the wrapped Triangles with the supplied Triangles. -// -// Call only this method to update the wrapped Triangles, otherwise the TrianglesDrawer will not -// work correctly. -func (td *TrianglesDrawer) Update(t Triangles) { - td.dirty = true - td.Triangles.Update(t) -} - -// Append appends the supplied Triangles to the wrapped Triangles. -// -// Call only this method to append to the wrapped Triangles, otherwise the TrianglesDrawer will not -// work correctly. -func (td *TrianglesDrawer) Append(t Triangles) { - td.dirty = true - td.Triangles.Append(t) -} - -// Dirty marks the underlying container as changed (dirty). If you, despite all warnings, updated -// the underlying container in a way different from td.Update or td.Append, call Dirty and -// everything will be fine :) +// Dirty marks the underlying container as changed (dirty). Always call this when you change the +// underlying Triangles (by Update, SetLen, etc.). func (td *TrianglesDrawer) Dirty() { td.dirty = true } @@ -232,7 +221,7 @@ type Polygon struct { // counter-clock-wise order, it doesn't matter. They should however form a convex polygon. func NewPolygon(c color.Color, points ...Vec) *Polygon { p := &Polygon{ - data: make(TrianglesData, len(points)), + data: TrianglesData{}, } p.td = TrianglesDrawer{Triangles: &p.data} p.SetColor(c) diff --git a/interface.go b/interface.go index 27c4f0e..3c3cb09 100644 --- a/interface.go +++ b/interface.go @@ -11,13 +11,13 @@ import "image/color" type Target interface { // MakeTriangles generates a specialized copy of the provided Triangles. // - // When calling Draw method on the returned Triangles, the Triangles will be drawn onto the - // target that generated them. + // When calling Draw method on the returned TargetTriangles, the TargetTriangles will be + // drawn onto the Target that generated them. // - // Note, that not every Target has to recognize all possible types of triangles. Some may + // Note, that not every Target has to recognize all possible types of Triangles. Some may // only recognize TrianglesPosition and TrianglesColor and ignore all other properties (if - // present) when making new Triangles. This varies from Target to Target. - MakeTriangles(Triangles) Triangles + // present) when making new TargetTriangles. This varies from Target to Target. + MakeTriangles(Triangles) TargetTriangles // These are the most basic Target "adjustment" methods. SetPicture(*Picture) @@ -32,33 +32,39 @@ type Triangles interface { // divided by 3. Len() int - // Draw draws Triangles onto an associated Target (if any). + // SetLen resizes Triangles to len vertices. If Triangles B were obtained by calling Slice + // method on Triangles A, the relationship between A and B is undefined after calling SetLen + // on either one of them. + SetLen(len int) + + // Slice returns a sub-Triangles of this Triangles, covering vertices in range [i, j). // - // Note, that this method does not have to be implemented, however it is always implemented - // for Triangles generated by a Target. - Draw() + // If Triangles B were obtained by calling Slice(4, 9) on Triangles A, then A and B must + // share the same underlying data. Modifying B must change the contents of A in range + // [4, 9). The vertex with index 0 at B is the vertex with index 4 in A, and so on. + // + // Returned Triangles must have the same underlying type. + Slice(i, j int) Triangles // Update copies vertex properties from the supplied Triangles into this Triangles. // // Properies not supported by these Triangles should be ignored. Properties not supported by // the supplied Triangles should be left untouched. // - // If these Triangles and supplied Triangles have different lengths, these Triangles should - // be resized. + // The two Triangles need to have the same Len. Update(Triangles) - // Append adds supplied Triangles to the end of these Triangles. - // - // Behavior regarding unsupported properties should be same as with Update. - Append(Triangles) - // Copy creates an exact independent copy of this Triangles (with the same underlying type). Copy() Triangles } -// Drawer is something that can be drawn onto any Target. -type Drawer interface { - Draw(Target) +// TargetTriangles are Triangles generated by a Target with MakeTriangles method. They can be drawn +// onto that Target. +type TargetTriangles interface { + Triangles + + // Draw draws Triangles onto an associated Target. + Draw() } // TrianglesPosition specifies Triangles with Position property. @@ -86,3 +92,8 @@ type TrianglesTexture interface { Triangles Texture(i int) Vec } + +// Drawer is something that can be drawn onto any Target. +type Drawer interface { + Draw(Target) +} diff --git a/window.go b/window.go index fff9c48..24c2aee 100644 --- a/window.go +++ b/window.go @@ -369,7 +369,7 @@ func (w *Window) end() { // Window. // // Window supports TrianglesPosition, TrianglesColor and TrianglesTexture. -func (w *Window) MakeTriangles(t Triangles) Triangles { +func (w *Window) MakeTriangles(t Triangles) TargetTriangles { return w.canvas.MakeTriangles(t) }