Skip to content
batch.go 3.11 KiB
Newer Older
faiface's avatar
faiface committed
package pixel

import (
faiface's avatar
faiface committed
	"fmt"
faiface's avatar
faiface committed
	"image/color"
faiface's avatar
faiface committed
	"math"
faiface's avatar
faiface committed

	"github.com/go-gl/mathgl/mgl32"
)

// Batch is a Target that allows for efficient drawing of many objects with the same Picture (but
// different slices of the same Picture are allowed).
//
// To put an object into a Batch, just draw it onto it:
//   object.Draw(batch)
type Batch struct {
faiface's avatar
faiface committed
	cont Drawer
faiface's avatar
faiface committed

	mat mgl32.Mat3
	col NRGBA
}

faiface's avatar
faiface committed
var _ BasicTarget = (*Batch)(nil)

faiface's avatar
faiface committed
// NewBatch creates an empty Batch with the specified Picture and container.
//
// The container is where objects get accumulated. Batch will support precisely those vertex
// properties, that the supplied container supports.
//
// Note, that if the container does not support TrianglesColor, color masking will not work.
faiface's avatar
faiface committed
func NewBatch(container Triangles, pic Picture) *Batch {
faiface's avatar
faiface committed
	return &Batch{
faiface's avatar
faiface committed
		cont: Drawer{Triangles: container, Picture: pic},
faiface's avatar
faiface committed
	}
}

// Clear removes all objects from the Batch.
func (b *Batch) Clear() {
faiface's avatar
faiface committed
	b.cont.Triangles.SetLen(0)
	b.cont.Dirty()
faiface's avatar
faiface committed
}

// Draw draws all objects that are currently in the Batch onto another Target.
func (b *Batch) Draw(t Target) {
	b.cont.Draw(t)
}

// SetTransform sets transforms used in the following draws onto the Batch.
func (b *Batch) SetTransform(t ...Transform) {
	b.mat = transformToMat(t...)
}

faiface's avatar
faiface committed
// SetColorMask sets a mask color used in the following draws onto the Batch.
func (b *Batch) SetColorMask(c color.Color) {
faiface's avatar
faiface committed
	if c == nil {
		b.col = NRGBA{1, 1, 1, 1}
		return
	}
	b.col = NRGBAModel.Convert(c).(NRGBA)
}

faiface's avatar
faiface committed
// MakeTriangles returns a specialized copy of the provided Triangles that draws onto this Batch.
func (b *Batch) MakeTriangles(t Triangles) TargetTriangles {
	bt := &batchTriangles{
		Triangles: t.Copy(),
		orig:      MakeTrianglesData(t.Len()),
		trans:     MakeTrianglesData(t.Len()),
		b:         b,
	}
	bt.orig.Update(t)
	bt.trans.Update(&bt.orig)
	return bt
}

// MakePicture returns a specialized copy of the provided Picture that draws onto this Batch.
func (b *Batch) MakePicture(p Picture) TargetPicture {
	return &batchPicture{
		Picture: p,
faiface's avatar
faiface committed
		b:       b,
faiface's avatar
faiface committed
type batchTriangles struct {
	Triangles
faiface's avatar
faiface committed
	orig, trans TrianglesData
faiface's avatar
faiface committed

faiface's avatar
faiface committed
	b *Batch
faiface's avatar
faiface committed
}

faiface's avatar
faiface committed
func (bt *batchTriangles) draw(bp *batchPicture) {
	for i := range bt.trans {
		transPos := bt.b.mat.Mul3x1(mgl32.Vec3{
			float32(bt.orig[i].Position.X()),
			float32(bt.orig[i].Position.Y()),
faiface's avatar
faiface committed
			1,
		})
faiface's avatar
faiface committed
		bt.trans[i].Position = V(float64(transPos.X()), float64(transPos.Y()))
		bt.trans[i].Color = bt.orig[i].Color.Mul(bt.b.col)
		if bp == nil {
			bt.trans[i].Picture = V(math.Inf(+1), math.Inf(+1))
faiface's avatar
faiface committed
	}
faiface's avatar
faiface committed
	bt.Triangles.Update(&bt.trans)

	cont := bt.b.cont.Triangles
	cont.SetLen(cont.Len() + bt.Triangles.Len())
	cont.Slice(cont.Len()-bt.Triangles.Len(), cont.Len()).Update(bt.Triangles)
	bt.b.cont.Dirty()
}

func (bt *batchTriangles) Draw() {
	bt.draw(nil)
}

type batchPicture struct {
	Picture
faiface's avatar
faiface committed

	b *Batch
faiface's avatar
faiface committed
}

func (bp *batchPicture) Slice(r Rect) Picture {
	return &batchPicture{
		Picture: bp.Picture.Slice(r),
	}
}

func (bp *batchPicture) Draw(t TargetTriangles) {
faiface's avatar
faiface committed
	bt := t.(*batchTriangles)
	if bp.b != bt.b {
faiface's avatar
faiface committed
		panic(fmt.Errorf("%T.Draw: TargetTriangles generated by different Batch", bp))
faiface's avatar
faiface committed
	}
	bt.draw(bp)
faiface's avatar
faiface committed
}