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.
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.
@ -45,10 +46,11 @@ func (b *Batch) Draw(t Target) {
}
// 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{
Triangles: t.Copy(),
trans: t.Copy(),
data: MakeTrianglesData(t.Len()),
batch: b,
}
}
@ -101,5 +103,9 @@ func (bt *batchTriangles) Draw() {
}
}
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.
func (c *Canvas) MakeTriangles(t Triangles) Triangles {
tpcs := NewGLTriangles(c.s, t).(trianglesPositionColorTexture)
func (c *Canvas) MakeTriangles(t Triangles) TargetTriangles {
gt := NewGLTriangles(c.s, t).(*glTriangles)
return &canvasTriangles{
c: c,
trianglesPositionColorTexture: tpcs,
c: c,
glTriangles: gt,
}
}
@ -170,16 +170,9 @@ func (c *Canvas) SetMaskColor(col color.Color) {
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 {
c *Canvas
trianglesPositionColorTexture
*glTriangles
}
func (ct *canvasTriangles) Draw() {
@ -199,10 +192,10 @@ func (ct *canvasTriangles) Draw() {
if pic != nil {
pic.Texture().Begin()
ct.trianglesPositionColorTexture.Draw()
ct.glTriangles.Draw()
pic.Texture().End()
} else {
ct.trianglesPositionColorTexture.Draw()
ct.glTriangles.Draw()
}
ct.c.s.End()

View File

@ -1,6 +1,8 @@
package pixel
import (
"fmt"
"github.com/faiface/mainthread"
"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
// thread manually. Also, you need to take care of additional Target initialization or setting of
// uniform attributes.
func NewGLTriangles(shader *pixelgl.Shader, t Triangles) Triangles {
func NewGLTriangles(shader *pixelgl.Shader, t Triangles) TargetTriangles {
var gt *glTriangles
mainthread.Call(func() {
gt = &glTriangles{
@ -21,6 +23,7 @@ func NewGLTriangles(shader *pixelgl.Shader, t Triangles) Triangles {
shader: shader,
}
})
gt.SetLen(t.Len())
gt.Update(t)
return gt
}
@ -35,13 +38,7 @@ func (gt *glTriangles) Len() int {
return len(gt.data) / gt.vs.Stride()
}
func (gt *glTriangles) Draw() {
gt.vs.Begin()
gt.vs.Draw()
gt.vs.End()
}
func (gt *glTriangles) resize(len int) {
func (gt *glTriangles) SetLen(len int) {
if len > gt.Len() {
needAppend := len - gt.Len()
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
if t, ok := t.(*TrianglesData); ok {
for i := offset; i < offset+t.Len(); i++ {
for i := 0; i < gt.Len(); i++ {
var (
px, py = (*t)[i].Position.XY()
col = (*t)[i].Color
@ -79,14 +90,14 @@ func (gt *glTriangles) updateData(offset int, t Triangles) {
}
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()
gt.data[i*gt.vs.Stride()+0] = float32(px)
gt.data[i*gt.vs.Stride()+1] = float32(py)
}
}
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)
gt.data[i*gt.vs.Stride()+2] = float32(col.R)
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 {
for i := offset; i < offset+t.Len(); i++ {
for i := 0; i < gt.Len(); i++ {
tx, ty := t.Texture(i).XY()
gt.data[i*gt.vs.Stride()+6] = float32(tx)
gt.data[i*gt.vs.Stride()+7] = float32(ty)
@ -120,14 +131,10 @@ func (gt *glTriangles) submitData() {
}
func (gt *glTriangles) Update(t Triangles) {
gt.resize(t.Len())
gt.updateData(0, t)
gt.submitData()
}
func (gt *glTriangles) Append(t Triangles) {
gt.resize(gt.Len() + t.Len())
gt.updateData(gt.Len()-t.Len(), t)
if gt.Len() != t.Len() {
panic(fmt.Errorf("%T.Update: invalid triangles len", gt))
}
gt.updateData(t)
gt.submitData()
}
@ -135,6 +142,12 @@ func (gt *glTriangles) Copy() Triangles {
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 {
px := gt.data[i*gt.vs.Stride()+0]
py := gt.data[i*gt.vs.Stride()+1]

View File

@ -13,6 +13,21 @@ type TrianglesData []struct {
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.
//
// 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.
func (td *TrianglesData) Len() int {
return len(*td)
// Slice returns a sub-Triangles of this TrianglesData.
func (td *TrianglesData) Slice(i, j int) Triangles {
s := TrianglesData((*td)[i:j])
return &s
}
// Draw is unimplemented for TrianglesData and panics.
func (td *TrianglesData) Draw() {
panic(fmt.Errorf("%T.Draw: invalid operation", td))
}
func (td *TrianglesData) updateData(offset int, t Triangles) {
func (td *TrianglesData) updateData(t Triangles) {
// fast path optimization
if t, ok := t.(*TrianglesData); ok {
copy((*td)[offset:], *t)
copy(*td, *t)
return
}
// slow path manual copy
if t, ok := t.(TrianglesPosition); ok {
for i := offset; i < len(*td); i++ {
for i := range *td {
(*td)[i].Position = t.Position(i)
}
}
if t, ok := t.(TrianglesColor); ok {
for i := offset; i < len(*td); i++ {
for i := range *td {
(*td)[i].Color = t.Color(i)
}
}
if t, ok := t.(TrianglesTexture); ok {
for i := offset; i < len(*td); i++ {
for i := range *td {
(*td)[i].Texture = t.Texture(i)
}
}
@ -72,19 +83,16 @@ func (td *TrianglesData) updateData(offset int, t Triangles) {
//
// TrianglesPosition, TrianglesColor and TrianglesTexture are supported.
func (td *TrianglesData) Update(t Triangles) {
td.SetLen(t.Len())
td.updateData(0, 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)
if td.Len() != t.Len() {
panic(fmt.Errorf("%T.Update: invalid triangles length", td))
}
td.updateData(t)
}
// Copy returns an exact independent copy of this TrianglesData.
func (td *TrianglesData) Copy() Triangles {
copyTd := make(TrianglesData, td.Len())
copyTd := TrianglesData{}
copyTd.SetLen(td.Len())
copyTd.Update(td)
return &copyTd
}
@ -111,7 +119,7 @@ func (td *TrianglesData) Texture(i int) Vec {
type TrianglesDrawer struct {
Triangles
tris map[Target]Triangles
tris map[Target]TargetTriangles
dirty bool
}
@ -129,7 +137,7 @@ func (td *TrianglesDrawer) flush() {
// Draw draws the wrapped Triangles onto the provided Target.
func (td *TrianglesDrawer) Draw(target Target) {
if td.tris == nil {
td.tris = make(map[Target]Triangles)
td.tris = make(map[Target]TargetTriangles)
}
td.flush()
@ -142,27 +150,8 @@ func (td *TrianglesDrawer) Draw(target Target) {
tri.Draw()
}
// Update updates the wrapped Triangles with the supplied Triangles.
//
// 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 :)
// 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
}
@ -232,7 +221,7 @@ type Polygon struct {
// counter-clock-wise order, it doesn't matter. They should however form a convex polygon.
func NewPolygon(c color.Color, points ...Vec) *Polygon {
p := &Polygon{
data: make(TrianglesData, len(points)),
data: TrianglesData{},
}
p.td = TrianglesDrawer{Triangles: &p.data}
p.SetColor(c)

View File

@ -11,13 +11,13 @@ import "image/color"
type Target interface {
// MakeTriangles generates a specialized copy of the provided Triangles.
//
// When calling Draw method on the returned Triangles, the Triangles will be drawn onto the
// target that generated them.
// When calling Draw method on the returned TargetTriangles, the TargetTriangles will be
// 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
// present) when making new Triangles. This varies from Target to Target.
MakeTriangles(Triangles) Triangles
// present) when making new TargetTriangles. This varies from Target to Target.
MakeTriangles(Triangles) TargetTriangles
// These are the most basic Target "adjustment" methods.
SetPicture(*Picture)
@ -32,33 +32,39 @@ type Triangles interface {
// divided by 3.
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
// for Triangles generated by a Target.
Draw()
// If Triangles B were obtained by calling Slice(4, 9) on Triangles A, then A and B must
// share the same underlying data. Modifying B must change the contents of A in range
// [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.
//
// Properies not supported by these Triangles should be ignored. Properties not supported by
// the supplied Triangles should be left untouched.
//
// If these Triangles and supplied Triangles have different lengths, these Triangles should
// be resized.
// The two Triangles need to have the same Len.
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() Triangles
}
// Drawer is something that can be drawn onto any Target.
type Drawer interface {
Draw(Target)
// TargetTriangles are Triangles generated by a Target with MakeTriangles method. They can be drawn
// onto that Target.
type TargetTriangles interface {
Triangles
// Draw draws Triangles onto an associated Target.
Draw()
}
// TrianglesPosition specifies Triangles with Position property.
@ -86,3 +92,8 @@ type TrianglesTexture interface {
Triangles
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 supports TrianglesPosition, TrianglesColor and TrianglesTexture.
func (w *Window) MakeTriangles(t Triangles) Triangles {
func (w *Window) MakeTriangles(t Triangles) TargetTriangles {
return w.canvas.MakeTriangles(t)
}