From c9f445787e018b1c638a2ff797f3346914a43259 Mon Sep 17 00:00:00 2001
From: faiface <faiface@ksp.sk>
Date: Sat, 14 Jan 2017 16:07:51 +0100
Subject: [PATCH] add support for Picture slicing (Batch and Window)

---
 batch.go  | 22 +++++++++++++---------
 util.go   | 19 +++++++++++++++++++
 window.go | 22 +++++++++++++++++++++-
 3 files changed, 53 insertions(+), 10 deletions(-)

diff --git a/batch.go b/batch.go
index c69d617..b0d5e0a 100644
--- a/batch.go
+++ b/batch.go
@@ -12,7 +12,8 @@ import (
 // To put an object into a Batch, just draw it onto it:
 //   object.Draw(batch)
 type Batch struct {
-	cont TrianglesDrawer
+	cont   TrianglesDrawer
+	fixpic *Picture
 
 	pic *Picture
 	mat mgl32.Mat3
@@ -27,8 +28,8 @@ type Batch struct {
 // Note, that if the container does not support TrianglesColor, color masking will not work.
 func NewBatch(pic *Picture, container Triangles) *Batch {
 	return &Batch{
-		cont: TrianglesDrawer{Triangles: container},
-		pic:  pic,
+		cont:   TrianglesDrawer{Triangles: container},
+		fixpic: pic,
 	}
 }
 
@@ -39,7 +40,7 @@ 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.pic)
+	t.SetPicture(b.fixpic)
 	b.cont.Draw(t)
 }
 
@@ -52,15 +53,16 @@ func (b *Batch) MakeTriangles(t Triangles) Triangles {
 	}
 }
 
-// SetPicture only checks, whether the supplied Picture has the same underlying Picture as the fixed
-// Picture of this Batch. If that is not true, this method panics.
+// SetPicture sets the current Picture that will be used with the following draws. The underlying
+// Texture of this Picture must be same as the underlying Texture of the Batch's Picture.
 func (b *Batch) SetPicture(p *Picture) {
 	if p == nil {
 		return
 	}
-	if p.Texture() != b.pic.Texture() {
-		panic("batch: attempted to draw with a different Picture")
+	if p.Texture() != b.fixpic.Texture() {
+		panic("batch: attempted to draw with a different underlying Picture")
 	}
+	b.pic = p
 }
 
 // SetTransform sets transforms used in the following draws onto the Batch.
@@ -96,7 +98,9 @@ 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)
-		//TODO: texture
+		if bt.batch.pic != nil && bt.data[i].Texture != V(-1, -1) {
+			bt.data[i].Texture = pictureBounds(bt.batch.pic, bt.data[i].Texture)
+		}
 	}
 	bt.trans.Update(&bt.data)
 	bt.batch.cont.Append(bt.trans)
diff --git a/util.go b/util.go
index ce46294..6aafdb8 100644
--- a/util.go
+++ b/util.go
@@ -12,6 +12,17 @@ func clamp(x, low, high float64) float64 {
 	return x
 }
 
+func lerp(x float64, a, b Vec) Vec {
+	return a.Scaled(1-x) + b.Scaled(x)
+}
+
+func lerp2d(x, a, b Vec) Vec {
+	return V(
+		lerp(x.X(), a, b).X(),
+		lerp(x.Y(), a, b).Y(),
+	)
+}
+
 func transformToMat(t ...Transform) mgl32.Mat3 {
 	mat := mgl32.Ident3()
 	for i := range t {
@@ -19,3 +30,11 @@ func transformToMat(t ...Transform) mgl32.Mat3 {
 	}
 	return mat
 }
+
+func pictureBounds(p *Picture, v Vec) Vec {
+	w, h := float64(p.Texture().Width()), float64(p.Texture().Height())
+	a := p.Bounds().Pos
+	b := p.Bounds().Pos + p.Bounds().Size
+	u := lerp2d(v, a, b)
+	return V(u.X()/w, u.Y()/h)
+}
diff --git a/window.go b/window.go
index 8d70cd9..bc953b5 100644
--- a/window.go
+++ b/window.go
@@ -66,6 +66,7 @@ type Window struct {
 	pic *Picture
 	mat mgl32.Mat3
 	col mgl32.Vec4
+	bnd mgl32.Vec4
 
 	// need to save these to correctly restore a fullscreen window
 	restore struct {
@@ -374,12 +375,14 @@ func (wt *windowTriangles) Draw() {
 	pic := wt.w.pic // avoid
 	mat := wt.w.mat // race
 	col := wt.w.col // condition
+	bnd := wt.w.bnd
 
 	pixelgl.DoNoBlock(func() {
 		wt.w.begin()
 
 		wt.w.shader.SetUniformAttr(transformMat3, mat)
 		wt.w.shader.SetUniformAttr(maskColorVec4, col)
+		wt.w.shader.SetUniformAttr(boundsVec4, bnd)
 
 		if pic != nil {
 			pic.Texture().Begin()
@@ -514,6 +517,14 @@ func (w *Window) MakeTriangles(t Triangles) Triangles {
 
 // SetPicture sets a Picture that will be used in subsequent drawings onto the window.
 func (w *Window) SetPicture(p *Picture) {
+	if p != nil {
+		min := pictureBounds(p, V(0, 0))
+		max := pictureBounds(p, V(1, 1))
+		w.bnd = mgl32.Vec4{
+			float32(min.X()), float32(min.Y()),
+			float32(max.X()), float32(max.Y()),
+		}
+	}
 	w.pic = p
 }
 
@@ -552,11 +563,13 @@ var defaultVertexFormat = pixelgl.AttrFormat{
 const (
 	maskColorVec4 int = iota
 	transformMat3
+	boundsVec4
 )
 
 var defaultUniformFormat = pixelgl.AttrFormat{
 	{Name: "maskColor", Type: pixelgl.Vec4},
 	{Name: "transform", Type: pixelgl.Mat3},
+	{Name: "bounds", Type: pixelgl.Vec4},
 }
 
 var defaultVertexShader = `
@@ -587,13 +600,20 @@ in vec2 Texture;
 out vec4 color;
 
 uniform vec4 maskColor;
+uniform vec4 bounds;
 uniform sampler2D tex;
 
 void main() {
+	vec2 boundsMin = bounds.xy;
+	vec2 boundsMax = bounds.zw;
+
+	float tx = boundsMin.x * (1 - Texture.x) + boundsMax.x * Texture.x;
+	float ty = boundsMin.y * (1 - Texture.y) + boundsMax.y * Texture.y;
+
 	if (Texture == vec2(-1, -1)) {
 		color = maskColor * Color;
 	} else {
-		color = maskColor * Color * texture(tex, vec2(Texture.x, 1 - Texture.y));
+		color = maskColor * Color * texture(tex, vec2(tx, 1 - ty));
 	}
 }
 `
-- 
GitLab