add Drawer struct

This commit is contained in:
faiface 2017-02-23 13:07:40 +01:00
parent 6dc55b8746
commit c1843b8608
5 changed files with 111 additions and 73 deletions

View File

@ -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)

87
drawer.go Normal file
View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}