From 798c6c2a487d80b32128b858cdcb0dea4255bf2a Mon Sep 17 00:00:00 2001 From: Allen Ray Date: Wed, 1 Jul 2020 17:15:11 -0400 Subject: [PATCH] Adding new triangles type: TrianglesClipped --- data.go | 14 ++++ data_test.go | 32 +++++++++ interface.go | 11 ++- pixelgl/canvas.go | 6 -- pixelgl/glshader.go | 12 ++++ pixelgl/gltriangles.go | 147 +++++++++++++++++++++++++++++++---------- 6 files changed, 179 insertions(+), 43 deletions(-) diff --git a/data.go b/data.go index a195969..0f9d19f 100644 --- a/data.go +++ b/data.go @@ -15,6 +15,8 @@ var ( Color RGBA Picture Vec Intensity float64 + ClipRect Rect + IsClipped bool }{Color: RGBA{1, 1, 1, 1}} ) @@ -25,6 +27,8 @@ type TrianglesData []struct { Color RGBA Picture Vec Intensity float64 + ClipRect Rect + IsClipped bool } // MakeTrianglesData creates TrianglesData of length len initialized with default property values. @@ -89,6 +93,11 @@ func (td *TrianglesData) updateData(t Triangles) { (*td)[i].Picture, (*td)[i].Intensity = t.Picture(i) } } + if t, ok := t.(TrianglesClipped); ok { + for i := range *td { + (*td)[i].ClipRect, (*td)[i].IsClipped = t.ClipRect(i) + } + } } // Update copies vertex properties from the supplied Triangles into this TrianglesData. @@ -123,6 +132,11 @@ func (td *TrianglesData) Picture(i int) (pic Vec, intensity float64) { return (*td)[i].Picture, (*td)[i].Intensity } +// ClipRect returns the clipping rectangle property of the i-th vertex. +func (td *TrianglesData) ClipRect(i int) (rect Rect, has bool) { + return (*td)[i].ClipRect, (*td)[i].IsClipped +} + // PictureData specifies an in-memory rectangular area of pixels and implements Picture and // PictureColor. // diff --git a/data_test.go b/data_test.go index e27bd94..42084c8 100644 --- a/data_test.go +++ b/data_test.go @@ -266,3 +266,35 @@ func BenchmarkTrianglesData_Picture(b *testing.B) { }) } } + +func BenchmarkTrianglesData_ClipRect(b *testing.B) { + tests := []struct { + name string + tData *pixel.TrianglesData + position int + }{ + { + name: "Getting beginning position", + tData: pixel.MakeTrianglesData(1000), + position: 2, + }, + { + name: "Getting middle position", + tData: pixel.MakeTrianglesData(1000), + position: 500, + }, + { + name: "Getting end position", + tData: pixel.MakeTrianglesData(1000), + position: 999, + }, + } + + for _, tt := range tests { + b.Run(tt.name, func(b *testing.B) { + for i := 0; i < b.N; i++ { + _, _ = tt.tData.ClipRect(tt.position) + } + }) + } +} diff --git a/interface.go b/interface.go index 9aa5f2e..5945d96 100644 --- a/interface.go +++ b/interface.go @@ -92,7 +92,7 @@ type TrianglesColor interface { Color(i int) RGBA } -// TrianglesPicture specifies Triangles with Picture propery. +// TrianglesPicture specifies Triangles with Picture property. // // The first value returned from Picture method is Picture coordinates. The second one specifies the // weight of the Picture. Value of 0 means, that Picture should be completely ignored, 1 means that @@ -102,6 +102,15 @@ type TrianglesPicture interface { Picture(i int) (pic Vec, intensity float64) } +// TrianglesClipped specifies Triangles with Clipping Rectangle property. +// +// The first value returned from ClipRect method is the clipping rectangle. The second one specifies +// if the triangle is clipped. +type TrianglesClipped interface { + Triangles + ClipRect(i int) (rect Rect, is bool) +} + // Picture represents a rectangular area of raster data, such as a color. It has Bounds which // specify the rectangle where data is located. type Picture interface { diff --git a/pixelgl/canvas.go b/pixelgl/canvas.go index 2cb310b..6cb06e3 100644 --- a/pixelgl/canvas.go +++ b/pixelgl/canvas.go @@ -4,8 +4,6 @@ import ( "fmt" "image/color" - "github.com/go-gl/gl/v3.3-core/gl" - "github.com/faiface/glhf" "github.com/faiface/mainthread" "github.com/faiface/pixel" @@ -322,10 +320,6 @@ func (ct *canvasTriangles) draw(tex *glhf.Texture, bounds pixel.Rect) { ct.shader.s.SetUniformAttr(loc, u.Value()) } - if clip, has := ct.ClipRect(); has { - gl.Scissor(int32(clip.Min.X), int32(clip.Min.Y), int32(clip.W()), int32(clip.H())) - } - if tex == nil { ct.vs.Begin() ct.vs.Draw() diff --git a/pixelgl/glshader.go b/pixelgl/glshader.go index d472cca..7caa507 100644 --- a/pixelgl/glshader.go +++ b/pixelgl/glshader.go @@ -22,6 +22,7 @@ type GLShader struct { colormask mgl32.Vec4 bounds mgl32.Vec4 texbounds mgl32.Vec4 + cliprect mgl32.Vec4 } } @@ -37,6 +38,7 @@ const ( canvasColor canvasTexCoords canvasIntensity + canvasClip ) var defaultCanvasVertexFormat = glhf.AttrFormat{ @@ -44,6 +46,7 @@ var defaultCanvasVertexFormat = glhf.AttrFormat{ canvasColor: {Name: "aColor", Type: glhf.Vec4}, canvasTexCoords: {Name: "aTexCoords", Type: glhf.Vec2}, canvasIntensity: {Name: "aIntensity", Type: glhf.Float}, + canvasClip: {Name: "aClipRect", Type: glhf.Vec4}, } // Sets up a base shader with everything needed for a Pixel @@ -238,11 +241,14 @@ in vec2 aPosition; in vec4 aColor; in vec2 aTexCoords; in float aIntensity; +in vec4 aClipRect; +in float aIsClipped; out vec4 vColor; out vec2 vTexCoords; out float vIntensity; out vec2 vPosition; +out vec4 vClipRect; uniform mat3 uTransform; uniform vec4 uBounds; @@ -251,10 +257,12 @@ void main() { vec2 transPos = (uTransform * vec3(aPosition, 1.0)).xy; vec2 normPos = (transPos - uBounds.xy) / uBounds.zw * 2 - vec2(1, 1); gl_Position = vec4(normPos, 0.0, 1.0); + vColor = aColor; vPosition = aPosition; vTexCoords = aTexCoords; vIntensity = aIntensity; + vClipRect = aClipRect; } ` @@ -264,6 +272,7 @@ var baseCanvasFragmentShader = ` in vec4 vColor; in vec2 vTexCoords; in float vIntensity; +in vec4 vClipRect; out vec4 fragColor; @@ -272,6 +281,9 @@ uniform vec4 uTexBounds; uniform sampler2D uTexture; void main() { + if ((vClipRect != vec4(0,0,0,0)) && (gl_FragCoord.x < vClipRect.x || gl_FragCoord.y < vClipRect.y || gl_FragCoord.x > vClipRect.z || gl_FragCoord.y > vClipRect.w)) + discard; + if (vIntensity == 0) { fragColor = uColorMask * vColor; } else { diff --git a/pixelgl/gltriangles.go b/pixelgl/gltriangles.go index 1cbcbe0..de6fe80 100644 --- a/pixelgl/gltriangles.go +++ b/pixelgl/gltriangles.go @@ -16,13 +16,32 @@ type GLTriangles struct { vs *glhf.VertexSlice data []float32 shader *GLShader - clip pixel.Rect } var ( _ pixel.TrianglesPosition = (*GLTriangles)(nil) _ pixel.TrianglesColor = (*GLTriangles)(nil) _ pixel.TrianglesPicture = (*GLTriangles)(nil) + _ pixel.TrianglesClipped = (*GLTriangles)(nil) +) + +// The following is a helper so that the indices of +// each of these items is easier to see/debug. +const ( + triPosX = iota + triPosY + triColorR + triColorG + triColorB + triColorA + triPicX + triPicY + triIntensity + triClipMinX + triClipMinY + triClipMaxX + triClipMaxY + trisAttrLen ) // NewGLTriangles returns GLTriangles initialized with the data from the supplied Triangles. @@ -71,6 +90,7 @@ func (gt *GLTriangles) SetLen(length int) { 1, 1, 1, 1, 0, 0, 0, + 0, 0, 0, 0, ) } case length < gt.Len(): @@ -111,17 +131,22 @@ func (gt *GLTriangles) updateData(t pixel.Triangles) { col = (*t)[i].Color tx, ty = (*t)[i].Picture.XY() in = (*t)[i].Intensity + rec = (*t)[i].ClipRect ) - d := gt.data[i*stride : i*stride+9] - d[0] = float32(px) - d[1] = float32(py) - d[2] = float32(col.R) - d[3] = float32(col.G) - d[4] = float32(col.B) - d[5] = float32(col.A) - d[6] = float32(tx) - d[7] = float32(ty) - d[8] = float32(in) + d := gt.data[i*stride : i*stride+trisAttrLen] + d[triPosX] = float32(px) + d[triPosY] = float32(py) + d[triColorR] = float32(col.R) + d[triColorG] = float32(col.G) + d[triColorB] = float32(col.B) + d[triColorA] = float32(col.A) + d[triPicX] = float32(tx) + d[triPicY] = float32(ty) + d[triIntensity] = float32(in) + d[triClipMinX] = float32(rec.Min.X) + d[triClipMinY] = float32(rec.Min.Y) + d[triClipMaxX] = float32(rec.Max.X) + d[triClipMaxY] = float32(rec.Max.Y) } return } @@ -129,25 +154,34 @@ func (gt *GLTriangles) updateData(t pixel.Triangles) { if t, ok := t.(pixel.TrianglesPosition); ok { for i := 0; i < length; i++ { px, py := t.Position(i).XY() - gt.data[i*stride+0] = float32(px) - gt.data[i*stride+1] = float32(py) + gt.data[i*stride+triPosX] = float32(px) + gt.data[i*stride+triPosY] = float32(py) } } if t, ok := t.(pixel.TrianglesColor); ok { for i := 0; i < length; i++ { col := t.Color(i) - gt.data[i*stride+2] = float32(col.R) - gt.data[i*stride+3] = float32(col.G) - gt.data[i*stride+4] = float32(col.B) - gt.data[i*stride+5] = float32(col.A) + gt.data[i*stride+triColorR] = float32(col.R) + gt.data[i*stride+triColorG] = float32(col.G) + gt.data[i*stride+triColorB] = float32(col.B) + gt.data[i*stride+triColorA] = float32(col.A) } } if t, ok := t.(pixel.TrianglesPicture); ok { for i := 0; i < length; i++ { pic, intensity := t.Picture(i) - gt.data[i*stride+6] = float32(pic.X) - gt.data[i*stride+7] = float32(pic.Y) - gt.data[i*stride+8] = float32(intensity) + gt.data[i*stride+triPicX] = float32(pic.X) + gt.data[i*stride+triPicY] = float32(pic.Y) + gt.data[i*stride+triIntensity] = float32(intensity) + } + } + if t, ok := t.(pixel.TrianglesClipped); ok { + for i := 0; i < length; i++ { + rect, _ := t.ClipRect(i) + gt.data[i*stride+triClipMinX] = float32(rect.Min.X) + gt.data[i*stride+triClipMinY] = float32(rect.Min.Y) + gt.data[i*stride+triClipMaxX] = float32(rect.Max.X) + gt.data[i*stride+triClipMaxY] = float32(rect.Max.Y) } } } @@ -161,6 +195,12 @@ func (gt *GLTriangles) Update(t pixel.Triangles) { } gt.updateData(t) + // Copy the verteces down to the glhf.VertexData + gt.CopyVertices() +} + +// CopyVertices copies the GLTriangle data down to the vertex data. +func (gt *GLTriangles) CopyVertices() { // this code is supposed to copy the vertex data and CallNonBlock the update if // the data is small enough, otherwise it'll block and not copy the data if len(gt.data) < 256 { // arbitrary heurestic constant @@ -186,19 +226,31 @@ func (gt *GLTriangles) Copy() pixel.Triangles { return NewGLTriangles(gt.shader, gt) } +// index is a helper function that returns the index in the data +// slice given the i-th vertex and the item index. +func (gt *GLTriangles) index(i, idx int) int { + return i*gt.vs.Stride() + idx +} + // Position returns the Position property of the i-th vertex. func (gt *GLTriangles) Position(i int) pixel.Vec { - px := gt.data[i*gt.vs.Stride()+0] - py := gt.data[i*gt.vs.Stride()+1] + px := gt.data[gt.index(i, triPosX)] + py := gt.data[gt.index(i, triPosY)] return pixel.V(float64(px), float64(py)) } +// SetPosition sets the position property of the i-th vertex. +func (gt *GLTriangles) SetPosition(i int, p pixel.Vec) { + gt.data[gt.index(i, triPosX)] = float32(p.X) + gt.data[gt.index(i, triPosY)] = float32(p.Y) +} + // Color returns the Color property of the i-th vertex. func (gt *GLTriangles) Color(i int) pixel.RGBA { - r := gt.data[i*gt.vs.Stride()+2] - g := gt.data[i*gt.vs.Stride()+3] - b := gt.data[i*gt.vs.Stride()+4] - a := gt.data[i*gt.vs.Stride()+5] + r := gt.data[gt.index(i, triColorR)] + g := gt.data[gt.index(i, triColorG)] + b := gt.data[gt.index(i, triColorB)] + a := gt.data[gt.index(i, triColorA)] return pixel.RGBA{ R: float64(r), G: float64(g), @@ -207,21 +259,44 @@ func (gt *GLTriangles) Color(i int) pixel.RGBA { } } +// SetColor sets the color property of the i-th vertex. +func (gt *GLTriangles) SetColor(i int, c pixel.RGBA) { + gt.data[gt.index(i, triColorR)] = float32(c.R) + gt.data[gt.index(i, triColorG)] = float32(c.G) + gt.data[gt.index(i, triColorB)] = float32(c.B) + gt.data[gt.index(i, triColorA)] = float32(c.A) +} + // Picture returns the Picture property of the i-th vertex. func (gt *GLTriangles) Picture(i int) (pic pixel.Vec, intensity float64) { - tx := gt.data[i*gt.vs.Stride()+6] - ty := gt.data[i*gt.vs.Stride()+7] - intensity = float64(gt.data[i*gt.vs.Stride()+8]) + tx := gt.data[gt.index(i, triPicX)] + ty := gt.data[gt.index(i, triPicY)] + intensity = float64(gt.data[gt.index(i, triIntensity)]) return pixel.V(float64(tx), float64(ty)), intensity } -// SetClipRect sets the rectangle to scissor the triangles by -func (gt *GLTriangles) SetClipRect(r pixel.Rect) { - gt.clip = r.Norm() +// SetPicture sets the picture property of the i-th vertex. +func (gt *GLTriangles) SetPicture(i int, pic pixel.Vec, intensity float64) { + gt.data[gt.index(i, triPicX)] = float32(pic.X) + gt.data[gt.index(i, triPicY)] = float32(pic.Y) + gt.data[gt.index(i, triIntensity)] = float32(intensity) } -// ClipRect gets the clipping rectangle and returns true if that -// rectangle is not the Zero Rectangle -func (gt *GLTriangles) ClipRect() (pixel.Rect, bool) { - return gt.clip, gt.clip.Area() != 0 +// ClipRect returns the Clipping rectangle property of the i-th vertex. +func (gt *GLTriangles) ClipRect(i int) (rect pixel.Rect, is bool) { + mx := gt.data[gt.index(i, triClipMinX)] + my := gt.data[gt.index(i, triClipMinY)] + ax := gt.data[gt.index(i, triClipMaxX)] + ay := gt.data[gt.index(i, triClipMaxY)] + rect = pixel.R(float64(mx), float64(my), float64(ax), float64(ay)) + is = rect.Area() != 0.0 + return +} + +// SetClipRect sets the Clipping rectangle property of the i-th vertex. +func (gt *GLTriangles) SetClipRect(i int, rect pixel.Rect) { + gt.data[gt.index(i, triClipMinX)] = float32(rect.Min.X) + gt.data[gt.index(i, triClipMinY)] = float32(rect.Min.Y) + gt.data[gt.index(i, triClipMaxX)] = float32(rect.Max.X) + gt.data[gt.index(i, triClipMaxY)] = float32(rect.Max.Y) }