diff --git a/batch.go b/batch.go index 32ff464..2676891 100644 --- a/batch.go +++ b/batch.go @@ -41,7 +41,6 @@ func (b *Batch) Clear() { // Draw draws all objects that are currently in the Batch onto another Target. func (b *Batch) Draw(t Target) { - t.SetPicture(b.fixpic) b.cont.Draw(t) } @@ -98,8 +97,8 @@ func (bt *batchTriangles) Draw() { }) bt.data[i].Position = V(float64(transPos.X()), float64(transPos.Y())) bt.data[i].Color = bt.data[i].Color.Mul(bt.batch.col) - if bt.batch.pic != nil && bt.data[i].Texture != V(-1, -1) { - bt.data[i].Texture = pictureBounds(bt.batch.pic, bt.data[i].Texture) + if bt.batch.pic != nil && bt.data[i].Picture != V(-1, -1) { + bt.data[i].Picture = pictureBounds(bt.batch.pic, bt.data[i].Picture) } } bt.trans.Update(&bt.data) diff --git a/drawer.go b/drawer.go new file mode 100644 index 0000000..d5ec2fe --- /dev/null +++ b/drawer.go @@ -0,0 +1,87 @@ +package pixel + +// Drawer glues all the fundamental interfaces (Target, Triangles, Picture) into a coherent and the +// only intended usage pattern. +// +// Drawer makes it possible to draw any combination of Triangles and Picture onto any Target +// efficiently. +// +// To create a Drawer, just assign it's Triangles and Picture fields: +// d := pixel.Drawer{Triangles: t, Picture: p} +// +// If Triangles is nil, nothing will be drawn. If Picture is nil, Triangles will be drawn without a +// Picture. +// +// Whenever you change the Triangles, call Dirty to notify Drawer that Triangles changed. You don't +// need to notify Drawer about a change of Picture. +type Drawer struct { + Triangles Triangles + Picture Picture + + tris map[Target]TargetTriangles + clean map[Target]bool + pics map[targetPicturePair]TargetPicture + inited bool +} + +type targetPicturePair struct { + Target Target + Picture Picture +} + +func (d *Drawer) lazyInit() { + if !d.inited { + d.tris = make(map[Target]TargetTriangles) + d.clean = make(map[Target]bool) + d.pics = make(map[targetPicturePair]TargetPicture) + d.inited = true + } +} + +// Dirty marks the Triangles of this Drawer as changed. If not called, changes will not be visible +// when drawing. +func (d *Drawer) Dirty() { + d.lazyInit() + + for t := range d.clean { + d.clean[t] = false + } +} + +// Draw efficiently draws Triangles with Picture onto the provided Target. +// +// If Triangles is nil, nothing will be drawn. If Picture is nil, Triangles will be drawn without a +// Picture. +func (d *Drawer) Draw(t Target) { + d.lazyInit() + + if d.Triangles == nil { + return + } + + tri := d.tris[t] + if tri == nil { + tri = t.MakeTriangles(d.Triangles) + d.tris[t] = tri + d.clean[t] = true + } + + if !d.clean[t] { + tri.SetLen(d.Triangles.Len()) + tri.Update(d.Triangles) + d.clean[t] = true + } + + if d.Picture == nil { + tri.Draw() + return + } + + pic := d.pics[targetPicturePair{t, d.Picture}] + if pic == nil { + pic = t.MakePicture(d.Picture) + d.pics[targetPicturePair{t, d.Picture}] = pic + } + + pic.Draw(tri) +} diff --git a/graphics.go b/graphics.go index 1ac0cf0..2e81cd8 100644 --- a/graphics.go +++ b/graphics.go @@ -10,7 +10,7 @@ import ( type TrianglesData []struct { Position Vec Color NRGBA - Texture Vec + Picture Vec } // MakeTrianglesData creates TrianglesData of length len initialized with default property values. @@ -39,7 +39,7 @@ func (td *TrianglesData) SetLen(len int) { *td = append(*td, struct { Position Vec Color NRGBA - Texture Vec + Picture Vec }{V(0, 0), NRGBA{1, 1, 1, 1}, V(-1, -1)}) } } @@ -72,9 +72,9 @@ func (td *TrianglesData) updateData(t Triangles) { (*td)[i].Color = t.Color(i) } } - if t, ok := t.(TrianglesTexture); ok { + if t, ok := t.(TrianglesPicture); ok { for i := range *td { - (*td)[i].Texture = t.Texture(i) + (*td)[i].Picture = t.Picture(i) } } } @@ -107,54 +107,9 @@ func (td *TrianglesData) Color(i int) NRGBA { return (*td)[i].Color } -// Texture returns the texture property of i-th vertex. -func (td *TrianglesData) Texture(i int) Vec { - return (*td)[i].Texture -} - -// TrianglesDrawer is a helper type that wraps Triangles and turns them into a Drawer. -// -// It does so by creating a separate Triangles instance for each Target. The instances are -// correctly updated alongside the wrapped Triangles. -type TrianglesDrawer struct { - Triangles - - tris map[Target]TargetTriangles - dirty bool -} - -func (td *TrianglesDrawer) flush() { - if !td.dirty { - return - } - td.dirty = false - - for _, t := range td.tris { - t.SetLen(td.Len()) - t.Update(td.Triangles) - } -} - -// Draw draws the wrapped Triangles onto the provided Target. -func (td *TrianglesDrawer) Draw(target Target) { - if td.tris == nil { - td.tris = make(map[Target]TargetTriangles) - } - - td.flush() - - tri := td.tris[target] - if tri == nil { - tri = target.MakeTriangles(td.Triangles) - td.tris[target] = tri - } - tri.Draw() -} - -// 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 +// Picture returns the picture property of i-th vertex. +func (td *TrianglesData) Picture(i int) Vec { + return (*td)[i].Picture } // Sprite is a picture that can be drawn onto a Target. To change the position/rotation/scale of @@ -170,12 +125,12 @@ type Sprite struct { func NewSprite(pic *GLPicture) *Sprite { s := &Sprite{ data: TrianglesData{ - {Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Texture: V(0, 0)}, - {Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Texture: V(1, 0)}, - {Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Texture: V(1, 1)}, - {Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Texture: V(0, 0)}, - {Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Texture: V(1, 1)}, - {Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Texture: V(0, 1)}, + {Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Picture: V(0, 0)}, + {Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Picture: V(1, 0)}, + {Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Picture: V(1, 1)}, + {Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Picture: V(0, 0)}, + {Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Picture: V(1, 1)}, + {Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Picture: V(0, 1)}, }, } s.td = TrianglesDrawer{Triangles: &s.data} @@ -207,7 +162,6 @@ func (s *Sprite) Picture() *GLPicture { // Draw draws the Sprite onto the provided Target. func (s *Sprite) Draw(t Target) { - t.SetPicture(s.pic) s.td.Draw(t) } @@ -277,6 +231,5 @@ func (p *Polygon) Points() []Vec { // Draw draws the Polygon onto the Target. func (p *Polygon) Draw(t Target) { - t.SetPicture(nil) p.td.Draw(t) } diff --git a/pixelgl/canvas.go b/pixelgl/canvas.go index 00b4802..a75d6f7 100644 --- a/pixelgl/canvas.go +++ b/pixelgl/canvas.go @@ -59,12 +59,12 @@ func NewCanvas(width, height float64, smooth bool) *Canvas { white := pixel.NRGBA{R: 1, G: 1, B: 1, A: 1} c.drawTd = pixel.TrianglesDrawer{Triangles: &pixel.TrianglesData{ - {Position: pixel.V(-1, -1), Color: white, Texture: pixel.V(0, 0)}, - {Position: pixel.V(1, -1), Color: white, Texture: pixel.V(1, 0)}, - {Position: pixel.V(1, 1), Color: white, Texture: pixel.V(1, 1)}, - {Position: pixel.V(-1, -1), Color: white, Texture: pixel.V(0, 0)}, - {Position: pixel.V(1, 1), Color: white, Texture: pixel.V(1, 1)}, - {Position: pixel.V(-1, 1), Color: white, Texture: pixel.V(0, 1)}, + {Position: pixel.V(-1, -1), Color: white, Picture: pixel.V(0, 0)}, + {Position: pixel.V(1, -1), Color: white, Picture: pixel.V(1, 0)}, + {Position: pixel.V(1, 1), Color: white, Picture: pixel.V(1, 1)}, + {Position: pixel.V(-1, -1), Color: white, Picture: pixel.V(0, 0)}, + {Position: pixel.V(1, 1), Color: white, Picture: pixel.V(1, 1)}, + {Position: pixel.V(-1, 1), Color: white, Picture: pixel.V(0, 1)}, }} c.pic = nil @@ -126,7 +126,6 @@ func (c *Canvas) Clear(col color.Color) { // Draw draws the content of the Canvas onto another Target. If no transform is applied, the content // is fully stretched to fit the Target. func (c *Canvas) Draw(t pixel.Target) { - t.SetPicture(c.Content()) c.drawTd.Draw(t) } diff --git a/pixelgl/gltriangles.go b/pixelgl/gltriangles.go index a0b3939..8a7a46b 100644 --- a/pixelgl/gltriangles.go +++ b/pixelgl/gltriangles.go @@ -76,7 +76,7 @@ func (gt *glTriangles) updateData(t pixel.Triangles) { var ( px, py = (*t)[i].Position.XY() col = (*t)[i].Color - tx, ty = (*t)[i].Texture.XY() + tx, ty = (*t)[i].Picture.XY() ) gt.data[i*gt.vs.Stride()+0] = float32(px) gt.data[i*gt.vs.Stride()+1] = float32(py) @@ -106,9 +106,9 @@ func (gt *glTriangles) updateData(t pixel.Triangles) { gt.data[i*gt.vs.Stride()+5] = float32(col.A) } } - if t, ok := t.(pixel.TrianglesTexture); ok { + if t, ok := t.(pixel.TrianglesPicture); ok { for i := 0; i < gt.Len(); i++ { - tx, ty := t.Texture(i).XY() + tx, ty := t.Picture(i).XY() gt.data[i*gt.vs.Stride()+6] = float32(tx) gt.data[i*gt.vs.Stride()+7] = float32(ty) }