Skip to content
gltriangles.go 4.1 KiB
Newer Older
faiface's avatar
faiface committed

import (
faiface's avatar
faiface committed
	"github.com/faiface/glhf"
faiface's avatar
faiface committed
	"github.com/faiface/mainthread"
	"github.com/faiface/pixel"
faiface's avatar
faiface committed
// NewGLTriangles returns OpenGL triangles implemented using glhf.VertexSlice. A few notes.
faiface's avatar
faiface committed
//
// Triangles returned from this function support TrianglesPosition, TrianglesColor and
// TrianglesTexture. If you need to support more, you can "override" SetLen and Update method.
faiface's avatar
faiface committed
//
faiface's avatar
faiface committed
// Draw method simply draws the underlying glhf.VertexSlice. It needs to be called in the main
faiface's avatar
faiface committed
// thread manually. Also, you need to take care of additional Target initialization or setting of
// uniform attributes.
func NewGLTriangles(shader *glhf.Shader, t pixel.Triangles) pixel.TargetTriangles {
faiface's avatar
faiface committed
	var gt *glTriangles
	mainthread.Call(func() {
		gt = &glTriangles{
faiface's avatar
faiface committed
			vs:     glhf.MakeVertexSlice(shader, 0, t.Len()),
faiface's avatar
faiface committed
			shader: shader,
		}
	})
	gt.SetLen(t.Len())
faiface's avatar
faiface committed
	gt.Update(t)
	return gt
}

type glTriangles struct {
faiface's avatar
faiface committed
	vs     *glhf.VertexSlice
faiface's avatar
faiface committed
	data   []float32
faiface's avatar
faiface committed
	shader *glhf.Shader
faiface's avatar
faiface committed
}

func (gt *glTriangles) Len() int {
	return len(gt.data) / gt.vs.Stride()
}

func (gt *glTriangles) SetLen(len int) {
faiface's avatar
faiface committed
	if len > gt.Len() {
		needAppend := len - gt.Len()
		for i := 0; i < needAppend; i++ {
			gt.data = append(gt.data,
				0, 0,
				1, 1, 1, 1,
				-1, -1,
			)
		}
	}
	if len < gt.Len() {
		gt.data = gt.data[:len]
	}
}

func (gt *glTriangles) Slice(i, j int) pixel.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 pixel.Triangles) {
	// glTriangles short path
	if t, ok := t.(*glTriangles); ok {
		copy(gt.data, t.data)
		return
	}

faiface's avatar
faiface committed
	// TrianglesData short path
	if t, ok := t.(*pixel.TrianglesData); ok {
		for i := 0; i < gt.Len(); i++ {
faiface's avatar
faiface committed
			var (
				px, py = (*t)[i].Position.XY()
				col    = (*t)[i].Color
faiface's avatar
faiface committed
				tx, ty = (*t)[i].Picture.XY()
faiface's avatar
faiface committed
			)
			gt.data[i*gt.vs.Stride()+0] = float32(px)
			gt.data[i*gt.vs.Stride()+1] = float32(py)
			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()+4] = float32(col.B)
			gt.data[i*gt.vs.Stride()+5] = float32(col.A)
			gt.data[i*gt.vs.Stride()+6] = float32(tx)
			gt.data[i*gt.vs.Stride()+7] = float32(ty)
		}
		return
	}

	if t, ok := t.(pixel.TrianglesPosition); ok {
		for i := 0; i < gt.Len(); i++ {
faiface's avatar
faiface committed
			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.(pixel.TrianglesColor); ok {
		for i := 0; i < gt.Len(); i++ {
faiface's avatar
faiface committed
			col := t.Color(i)
			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()+4] = float32(col.B)
			gt.data[i*gt.vs.Stride()+5] = float32(col.A)
		}
	}
faiface's avatar
faiface committed
	if t, ok := t.(pixel.TrianglesPicture); ok {
		for i := 0; i < gt.Len(); i++ {
faiface's avatar
faiface committed
			tx, ty := t.Picture(i).XY()
faiface's avatar
faiface committed
			gt.data[i*gt.vs.Stride()+6] = float32(tx)
			gt.data[i*gt.vs.Stride()+7] = float32(ty)
		}
	}
}

func (gt *glTriangles) submitData() {
	data := gt.data // avoid race condition
	mainthread.CallNonBlock(func() {
		gt.vs.Begin()
		dataLen := len(data) / gt.vs.Stride()
		gt.vs.SetLen(dataLen)
faiface's avatar
faiface committed
		gt.vs.SetVertexData(gt.data)
		gt.vs.End()
	})
}

func (gt *glTriangles) Update(t pixel.Triangles) {
	if gt.Len() != t.Len() {
		panic(fmt.Errorf("%T.Update: invalid triangles len", gt))
	}
	gt.updateData(t)
faiface's avatar
faiface committed
	gt.submitData()
}

func (gt *glTriangles) Copy() pixel.Triangles {
faiface's avatar
faiface committed
	return NewGLTriangles(gt.shader, gt)
}

func (gt *glTriangles) Draw() {
	gt.vs.Begin()
	gt.vs.Draw()
	gt.vs.End()
}

func (gt *glTriangles) Position(i int) pixel.Vec {
faiface's avatar
faiface committed
	px := gt.data[i*gt.vs.Stride()+0]
	py := gt.data[i*gt.vs.Stride()+1]
	return pixel.V(float64(px), float64(py))
func (gt *glTriangles) Color(i int) pixel.NRGBA {
faiface's avatar
faiface committed
	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]
faiface's avatar
faiface committed
		R: float64(r),
		G: float64(g),
		B: float64(b),
		A: float64(a),
	}
}

func (gt *glTriangles) Texture(i int) pixel.Vec {
faiface's avatar
faiface committed
	tx := gt.data[i*gt.vs.Stride()+6]
	ty := gt.data[i*gt.vs.Stride()+7]
	return pixel.V(float64(tx), float64(ty))
faiface's avatar
faiface committed
}