rework Triangles API and adapt it

This commit is contained in:
faiface 2017-01-28 20:01:59 +01:00
parent e7a2af61fb
commit 63339235b7
6 changed files with 116 additions and 104 deletions

View File

@ -35,7 +35,8 @@ func NewBatch(pic *Picture, container Triangles) *Batch {
// Clear removes all objects from the Batch. // Clear removes all objects from the Batch.
func (b *Batch) Clear() { func (b *Batch) Clear() {
b.cont.Update(&TrianglesData{}) b.cont.SetLen(0)
b.cont.Dirty()
} }
// Draw draws all objects that are currently in the Batch onto another Target. // Draw draws all objects that are currently in the Batch onto another Target.
@ -45,10 +46,11 @@ func (b *Batch) Draw(t Target) {
} }
// MakeTriangles returns a specialized copy of the provided Triangles, that draws onto this Batch. // MakeTriangles returns a specialized copy of the provided Triangles, that draws onto this Batch.
func (b *Batch) MakeTriangles(t Triangles) Triangles { func (b *Batch) MakeTriangles(t Triangles) TargetTriangles {
return &batchTriangles{ return &batchTriangles{
Triangles: t.Copy(), Triangles: t.Copy(),
trans: t.Copy(), trans: t.Copy(),
data: MakeTrianglesData(t.Len()),
batch: b, batch: b,
} }
} }
@ -101,5 +103,9 @@ func (bt *batchTriangles) Draw() {
} }
} }
bt.trans.Update(&bt.data) bt.trans.Update(&bt.data)
bt.batch.cont.Append(bt.trans)
cont := bt.batch.cont
cont.SetLen(cont.Len() + bt.trans.Len())
cont.Slice(cont.Len()-bt.trans.Len(), cont.Len()).Update(bt.trans)
cont.Dirty()
} }

View File

@ -129,11 +129,11 @@ func (c *Canvas) Draw(t Target) {
} }
// MakeTriangles returns Triangles that draw onto this Canvas. // MakeTriangles returns Triangles that draw onto this Canvas.
func (c *Canvas) MakeTriangles(t Triangles) Triangles { func (c *Canvas) MakeTriangles(t Triangles) TargetTriangles {
tpcs := NewGLTriangles(c.s, t).(trianglesPositionColorTexture) gt := NewGLTriangles(c.s, t).(*glTriangles)
return &canvasTriangles{ return &canvasTriangles{
c: c, c: c,
trianglesPositionColorTexture: tpcs, glTriangles: gt,
} }
} }
@ -170,16 +170,9 @@ func (c *Canvas) SetMaskColor(col color.Color) {
c.col = mgl32.Vec4{r, g, b, a} c.col = mgl32.Vec4{r, g, b, a}
} }
type trianglesPositionColorTexture interface {
Triangles
Position(i int) Vec
Color(i int) NRGBA
Texture(i int) Vec
}
type canvasTriangles struct { type canvasTriangles struct {
c *Canvas c *Canvas
trianglesPositionColorTexture *glTriangles
} }
func (ct *canvasTriangles) Draw() { func (ct *canvasTriangles) Draw() {
@ -199,10 +192,10 @@ func (ct *canvasTriangles) Draw() {
if pic != nil { if pic != nil {
pic.Texture().Begin() pic.Texture().Begin()
ct.trianglesPositionColorTexture.Draw() ct.glTriangles.Draw()
pic.Texture().End() pic.Texture().End()
} else { } else {
ct.trianglesPositionColorTexture.Draw() ct.glTriangles.Draw()
} }
ct.c.s.End() ct.c.s.End()

View File

@ -1,6 +1,8 @@
package pixel package pixel
import ( import (
"fmt"
"github.com/faiface/mainthread" "github.com/faiface/mainthread"
"github.com/faiface/pixel/pixelgl" "github.com/faiface/pixel/pixelgl"
) )
@ -13,7 +15,7 @@ import (
// Draw method simply draws the underlying pixelgl.VertexSlice. It needs to be called in the main // Draw method simply draws the underlying pixelgl.VertexSlice. It needs to be called in the main
// thread manually. Also, you need to take care of additional Target initialization or setting of // thread manually. Also, you need to take care of additional Target initialization or setting of
// uniform attributes. // uniform attributes.
func NewGLTriangles(shader *pixelgl.Shader, t Triangles) Triangles { func NewGLTriangles(shader *pixelgl.Shader, t Triangles) TargetTriangles {
var gt *glTriangles var gt *glTriangles
mainthread.Call(func() { mainthread.Call(func() {
gt = &glTriangles{ gt = &glTriangles{
@ -21,6 +23,7 @@ func NewGLTriangles(shader *pixelgl.Shader, t Triangles) Triangles {
shader: shader, shader: shader,
} }
}) })
gt.SetLen(t.Len())
gt.Update(t) gt.Update(t)
return gt return gt
} }
@ -35,13 +38,7 @@ func (gt *glTriangles) Len() int {
return len(gt.data) / gt.vs.Stride() return len(gt.data) / gt.vs.Stride()
} }
func (gt *glTriangles) Draw() { func (gt *glTriangles) SetLen(len int) {
gt.vs.Begin()
gt.vs.Draw()
gt.vs.End()
}
func (gt *glTriangles) resize(len int) {
if len > gt.Len() { if len > gt.Len() {
needAppend := len - gt.Len() needAppend := len - gt.Len()
for i := 0; i < needAppend; i++ { for i := 0; i < needAppend; i++ {
@ -57,10 +54,24 @@ func (gt *glTriangles) resize(len int) {
} }
} }
func (gt *glTriangles) updateData(offset int, t Triangles) { func (gt *glTriangles) Slice(i, j int) Triangles {
return &glTriangles{
vs: gt.vs.Slice(i, j),
data: gt.data[i*gt.vs.Stride() : j*gt.vs.Stride()],
shader: gt.shader,
}
}
func (gt *glTriangles) updateData(t Triangles) {
// glTriangles short path
if t, ok := t.(*glTriangles); ok {
copy(gt.data, t.data)
return
}
// TrianglesData short path // TrianglesData short path
if t, ok := t.(*TrianglesData); ok { if t, ok := t.(*TrianglesData); ok {
for i := offset; i < offset+t.Len(); i++ { for i := 0; i < gt.Len(); i++ {
var ( var (
px, py = (*t)[i].Position.XY() px, py = (*t)[i].Position.XY()
col = (*t)[i].Color col = (*t)[i].Color
@ -79,14 +90,14 @@ func (gt *glTriangles) updateData(offset int, t Triangles) {
} }
if t, ok := t.(TrianglesPosition); ok { if t, ok := t.(TrianglesPosition); ok {
for i := offset; i < offset+t.Len(); i++ { for i := 0; i < gt.Len(); i++ {
px, py := t.Position(i).XY() px, py := t.Position(i).XY()
gt.data[i*gt.vs.Stride()+0] = float32(px) gt.data[i*gt.vs.Stride()+0] = float32(px)
gt.data[i*gt.vs.Stride()+1] = float32(py) gt.data[i*gt.vs.Stride()+1] = float32(py)
} }
} }
if t, ok := t.(TrianglesColor); ok { if t, ok := t.(TrianglesColor); ok {
for i := offset; i < offset+t.Len(); i++ { for i := 0; i < gt.Len(); i++ {
col := t.Color(i) col := t.Color(i)
gt.data[i*gt.vs.Stride()+2] = float32(col.R) gt.data[i*gt.vs.Stride()+2] = float32(col.R)
gt.data[i*gt.vs.Stride()+3] = float32(col.G) gt.data[i*gt.vs.Stride()+3] = float32(col.G)
@ -95,7 +106,7 @@ func (gt *glTriangles) updateData(offset int, t Triangles) {
} }
} }
if t, ok := t.(TrianglesTexture); ok { if t, ok := t.(TrianglesTexture); ok {
for i := offset; i < offset+t.Len(); i++ { for i := 0; i < gt.Len(); i++ {
tx, ty := t.Texture(i).XY() tx, ty := t.Texture(i).XY()
gt.data[i*gt.vs.Stride()+6] = float32(tx) gt.data[i*gt.vs.Stride()+6] = float32(tx)
gt.data[i*gt.vs.Stride()+7] = float32(ty) gt.data[i*gt.vs.Stride()+7] = float32(ty)
@ -120,14 +131,10 @@ func (gt *glTriangles) submitData() {
} }
func (gt *glTriangles) Update(t Triangles) { func (gt *glTriangles) Update(t Triangles) {
gt.resize(t.Len()) if gt.Len() != t.Len() {
gt.updateData(0, t) panic(fmt.Errorf("%T.Update: invalid triangles len", gt))
gt.submitData() }
} gt.updateData(t)
func (gt *glTriangles) Append(t Triangles) {
gt.resize(gt.Len() + t.Len())
gt.updateData(gt.Len()-t.Len(), t)
gt.submitData() gt.submitData()
} }
@ -135,6 +142,12 @@ func (gt *glTriangles) Copy() Triangles {
return NewGLTriangles(gt.shader, gt) return NewGLTriangles(gt.shader, gt)
} }
func (gt *glTriangles) Draw() {
gt.vs.Begin()
gt.vs.Draw()
gt.vs.End()
}
func (gt *glTriangles) Position(i int) Vec { func (gt *glTriangles) Position(i int) Vec {
px := gt.data[i*gt.vs.Stride()+0] px := gt.data[i*gt.vs.Stride()+0]
py := gt.data[i*gt.vs.Stride()+1] py := gt.data[i*gt.vs.Stride()+1]

View File

@ -13,6 +13,21 @@ type TrianglesData []struct {
Texture Vec Texture Vec
} }
// MakeTrianglesData creates TrianglesData of length len initialized with default property values.
//
// Prefer this function to make(TrianglesData, len), because make zeros them, while this function
// does a correct intialization.
func MakeTrianglesData(len int) TrianglesData {
td := TrianglesData{}
td.SetLen(len)
return td
}
// Len returns the number of vertices in TrianglesData.
func (td *TrianglesData) Len() int {
return len(*td)
}
// SetLen resizes TrianglesData to len, while keeping the original content. // SetLen resizes TrianglesData to len, while keeping the original content.
// //
// If len is greater than TrianglesData's current length, the new data is filled with default // If len is greater than TrianglesData's current length, the new data is filled with default
@ -33,36 +48,32 @@ func (td *TrianglesData) SetLen(len int) {
} }
} }
// Len returns the number of vertices in TrianglesData. // Slice returns a sub-Triangles of this TrianglesData.
func (td *TrianglesData) Len() int { func (td *TrianglesData) Slice(i, j int) Triangles {
return len(*td) s := TrianglesData((*td)[i:j])
return &s
} }
// Draw is unimplemented for TrianglesData and panics. func (td *TrianglesData) updateData(t Triangles) {
func (td *TrianglesData) Draw() {
panic(fmt.Errorf("%T.Draw: invalid operation", td))
}
func (td *TrianglesData) updateData(offset int, t Triangles) {
// fast path optimization // fast path optimization
if t, ok := t.(*TrianglesData); ok { if t, ok := t.(*TrianglesData); ok {
copy((*td)[offset:], *t) copy(*td, *t)
return return
} }
// slow path manual copy // slow path manual copy
if t, ok := t.(TrianglesPosition); ok { if t, ok := t.(TrianglesPosition); ok {
for i := offset; i < len(*td); i++ { for i := range *td {
(*td)[i].Position = t.Position(i) (*td)[i].Position = t.Position(i)
} }
} }
if t, ok := t.(TrianglesColor); ok { if t, ok := t.(TrianglesColor); ok {
for i := offset; i < len(*td); i++ { for i := range *td {
(*td)[i].Color = t.Color(i) (*td)[i].Color = t.Color(i)
} }
} }
if t, ok := t.(TrianglesTexture); ok { if t, ok := t.(TrianglesTexture); ok {
for i := offset; i < len(*td); i++ { for i := range *td {
(*td)[i].Texture = t.Texture(i) (*td)[i].Texture = t.Texture(i)
} }
} }
@ -72,19 +83,16 @@ func (td *TrianglesData) updateData(offset int, t Triangles) {
// //
// TrianglesPosition, TrianglesColor and TrianglesTexture are supported. // TrianglesPosition, TrianglesColor and TrianglesTexture are supported.
func (td *TrianglesData) Update(t Triangles) { func (td *TrianglesData) Update(t Triangles) {
td.SetLen(t.Len()) if td.Len() != t.Len() {
td.updateData(0, t) panic(fmt.Errorf("%T.Update: invalid triangles length", td))
} }
td.updateData(t)
// Append adds supplied Triangles to the end of the TrianglesData.
func (td *TrianglesData) Append(t Triangles) {
td.SetLen(td.Len() + t.Len())
td.updateData(td.Len()-t.Len(), t)
} }
// Copy returns an exact independent copy of this TrianglesData. // Copy returns an exact independent copy of this TrianglesData.
func (td *TrianglesData) Copy() Triangles { func (td *TrianglesData) Copy() Triangles {
copyTd := make(TrianglesData, td.Len()) copyTd := TrianglesData{}
copyTd.SetLen(td.Len())
copyTd.Update(td) copyTd.Update(td)
return &copyTd return &copyTd
} }
@ -111,7 +119,7 @@ func (td *TrianglesData) Texture(i int) Vec {
type TrianglesDrawer struct { type TrianglesDrawer struct {
Triangles Triangles
tris map[Target]Triangles tris map[Target]TargetTriangles
dirty bool dirty bool
} }
@ -129,7 +137,7 @@ func (td *TrianglesDrawer) flush() {
// Draw draws the wrapped Triangles onto the provided Target. // Draw draws the wrapped Triangles onto the provided Target.
func (td *TrianglesDrawer) Draw(target Target) { func (td *TrianglesDrawer) Draw(target Target) {
if td.tris == nil { if td.tris == nil {
td.tris = make(map[Target]Triangles) td.tris = make(map[Target]TargetTriangles)
} }
td.flush() td.flush()
@ -142,27 +150,8 @@ func (td *TrianglesDrawer) Draw(target Target) {
tri.Draw() tri.Draw()
} }
// Update updates the wrapped Triangles with the supplied Triangles. // Dirty marks the underlying container as changed (dirty). Always call this when you change the
// // underlying Triangles (by Update, SetLen, etc.).
// Call only this method to update the wrapped Triangles, otherwise the TrianglesDrawer will not
// work correctly.
func (td *TrianglesDrawer) Update(t Triangles) {
td.dirty = true
td.Triangles.Update(t)
}
// Append appends the supplied Triangles to the wrapped Triangles.
//
// Call only this method to append to the wrapped Triangles, otherwise the TrianglesDrawer will not
// work correctly.
func (td *TrianglesDrawer) Append(t Triangles) {
td.dirty = true
td.Triangles.Append(t)
}
// Dirty marks the underlying container as changed (dirty). If you, despite all warnings, updated
// the underlying container in a way different from td.Update or td.Append, call Dirty and
// everything will be fine :)
func (td *TrianglesDrawer) Dirty() { func (td *TrianglesDrawer) Dirty() {
td.dirty = true td.dirty = true
} }
@ -232,7 +221,7 @@ type Polygon struct {
// counter-clock-wise order, it doesn't matter. They should however form a convex polygon. // counter-clock-wise order, it doesn't matter. They should however form a convex polygon.
func NewPolygon(c color.Color, points ...Vec) *Polygon { func NewPolygon(c color.Color, points ...Vec) *Polygon {
p := &Polygon{ p := &Polygon{
data: make(TrianglesData, len(points)), data: TrianglesData{},
} }
p.td = TrianglesDrawer{Triangles: &p.data} p.td = TrianglesDrawer{Triangles: &p.data}
p.SetColor(c) p.SetColor(c)

View File

@ -11,13 +11,13 @@ import "image/color"
type Target interface { type Target interface {
// MakeTriangles generates a specialized copy of the provided Triangles. // MakeTriangles generates a specialized copy of the provided Triangles.
// //
// When calling Draw method on the returned Triangles, the Triangles will be drawn onto the // When calling Draw method on the returned TargetTriangles, the TargetTriangles will be
// target that generated them. // drawn onto the Target that generated them.
// //
// Note, that not every Target has to recognize all possible types of triangles. Some may // Note, that not every Target has to recognize all possible types of Triangles. Some may
// only recognize TrianglesPosition and TrianglesColor and ignore all other properties (if // only recognize TrianglesPosition and TrianglesColor and ignore all other properties (if
// present) when making new Triangles. This varies from Target to Target. // present) when making new TargetTriangles. This varies from Target to Target.
MakeTriangles(Triangles) Triangles MakeTriangles(Triangles) TargetTriangles
// These are the most basic Target "adjustment" methods. // These are the most basic Target "adjustment" methods.
SetPicture(*Picture) SetPicture(*Picture)
@ -32,33 +32,39 @@ type Triangles interface {
// divided by 3. // divided by 3.
Len() int Len() int
// Draw draws Triangles onto an associated Target (if any). // SetLen resizes Triangles to len vertices. If Triangles B were obtained by calling Slice
// method on Triangles A, the relationship between A and B is undefined after calling SetLen
// on either one of them.
SetLen(len int)
// Slice returns a sub-Triangles of this Triangles, covering vertices in range [i, j).
// //
// Note, that this method does not have to be implemented, however it is always implemented // If Triangles B were obtained by calling Slice(4, 9) on Triangles A, then A and B must
// for Triangles generated by a Target. // share the same underlying data. Modifying B must change the contents of A in range
Draw() // [4, 9). The vertex with index 0 at B is the vertex with index 4 in A, and so on.
//
// Returned Triangles must have the same underlying type.
Slice(i, j int) Triangles
// Update copies vertex properties from the supplied Triangles into this Triangles. // Update copies vertex properties from the supplied Triangles into this Triangles.
// //
// Properies not supported by these Triangles should be ignored. Properties not supported by // Properies not supported by these Triangles should be ignored. Properties not supported by
// the supplied Triangles should be left untouched. // the supplied Triangles should be left untouched.
// //
// If these Triangles and supplied Triangles have different lengths, these Triangles should // The two Triangles need to have the same Len.
// be resized.
Update(Triangles) Update(Triangles)
// Append adds supplied Triangles to the end of these Triangles.
//
// Behavior regarding unsupported properties should be same as with Update.
Append(Triangles)
// Copy creates an exact independent copy of this Triangles (with the same underlying type). // Copy creates an exact independent copy of this Triangles (with the same underlying type).
Copy() Triangles Copy() Triangles
} }
// Drawer is something that can be drawn onto any Target. // TargetTriangles are Triangles generated by a Target with MakeTriangles method. They can be drawn
type Drawer interface { // onto that Target.
Draw(Target) type TargetTriangles interface {
Triangles
// Draw draws Triangles onto an associated Target.
Draw()
} }
// TrianglesPosition specifies Triangles with Position property. // TrianglesPosition specifies Triangles with Position property.
@ -86,3 +92,8 @@ type TrianglesTexture interface {
Triangles Triangles
Texture(i int) Vec Texture(i int) Vec
} }
// Drawer is something that can be drawn onto any Target.
type Drawer interface {
Draw(Target)
}

View File

@ -369,7 +369,7 @@ func (w *Window) end() {
// Window. // Window.
// //
// Window supports TrianglesPosition, TrianglesColor and TrianglesTexture. // Window supports TrianglesPosition, TrianglesColor and TrianglesTexture.
func (w *Window) MakeTriangles(t Triangles) Triangles { func (w *Window) MakeTriangles(t Triangles) TargetTriangles {
return w.canvas.MakeTriangles(t) return w.canvas.MakeTriangles(t)
} }