Skip to content
gltriangles.go 5.38 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"
// GLTriangles are OpenGL triangles implemented using glhf.VertexSlice.
faiface's avatar
faiface committed
//
// Triangles returned from this function support TrianglesPosition, TrianglesColor and
faiface's avatar
faiface committed
// TrianglesPicture. If you need to support more, you can "override" SetLen and Update methods.
type GLTriangles struct {
	vs     *glhf.VertexSlice
	data   []float32
	shader *glhf.Shader
}

var (
	_ pixel.TrianglesPosition = (*GLTriangles)(nil)
	_ pixel.TrianglesColor    = (*GLTriangles)(nil)
	_ pixel.TrianglesPicture  = (*GLTriangles)(nil)
)

// NewGLTriangles returns GLTriangles initialized with the data from the supplied Triangles.
faiface's avatar
faiface committed
//
// Only draw the Triangles using the provided Shader.
func NewGLTriangles(shader *glhf.Shader, t pixel.Triangles) *GLTriangles {
	var gt *GLTriangles
faiface's avatar
faiface committed
	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
}

// VertexSlice returns the VertexSlice of this GLTriangles.
//
// You can use it to draw them.
func (gt *GLTriangles) VertexSlice() *glhf.VertexSlice {
	return gt.vs
}

// Shader returns the GLTriangles's associated shader.
func (gt *GLTriangles) Shader() *glhf.Shader {
	return gt.shader
// Len returns the number of vertices.
func (gt *GLTriangles) Len() int {
faiface's avatar
faiface committed
	return len(gt.data) / gt.vs.Stride()
}

// SetLen efficiently resizes GLTriangles to len.
faiface's avatar
faiface committed
//
// Time complexity is amortized O(1).
func (gt *GLTriangles) SetLen(length int) {
	switch {
	case length > gt.Len():
		needAppend := length - gt.Len()
faiface's avatar
faiface committed
		for i := 0; i < needAppend; i++ {
			gt.data = append(gt.data,
				0, 0,
				1, 1, 1, 1,
	case length < gt.Len():
		gt.data = gt.data[:length*gt.vs.Stride()]
	default:
		return
faiface's avatar
faiface committed
	}
	mainthread.CallNonBlock(func() {
		gt.vs.Begin()
		gt.vs.SetLen(length)
		gt.vs.End()
	})
// Slice returns a sub-Triangles of this GLTriangles in range [i, j).
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
	stride := gt.vs.Stride()
	length := gt.Len()
	if t, ok := t.(*pixel.TrianglesData); ok {
		for i := 0; i < length; 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()
				in     = (*t)[i].Intensity
faiface's avatar
faiface committed
			)
			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)
	if t, ok := t.(pixel.TrianglesPosition); ok {
		for i := 0; i < length; i++ {
faiface's avatar
faiface committed
			px, py := t.Position(i).XY()
			gt.data[i*stride+0] = float32(px)
			gt.data[i*stride+1] = float32(py)
	if t, ok := t.(pixel.TrianglesColor); ok {
		for i := 0; i < length; i++ {
faiface's avatar
faiface committed
			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)
faiface's avatar
faiface committed
	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)
// Update copies vertex properties from the supplied Triangles into this GLTriangles.
//
// The two Triangles (gt and t) must be of the same len.
func (gt *GLTriangles) Update(t pixel.Triangles) {
	if gt.Len() != t.Len() {
		panic(fmt.Errorf("(%T).Update: invalid triangles len", gt))
	}
	gt.updateData(t)

	// 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
		data := append([]float32{}, gt.data...)
		mainthread.CallNonBlock(func() {
			gt.vs.Begin()
			gt.vs.SetVertexData(data)
			gt.vs.End()
		})
	} else {
		mainthread.Call(func() {
			gt.vs.Begin()
			gt.vs.SetVertexData(gt.data)
			gt.vs.End()
		})
	}
// Copy returns an independent copy of this GLTriangles.
//
faiface's avatar
faiface committed
// The returned Triangles are *GLTriangles as the underlying type.
func (gt *GLTriangles) Copy() pixel.Triangles {
faiface's avatar
faiface committed
	return NewGLTriangles(gt.shader, gt)
}

// Position returns the Position property of the i-th vertex.
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))
// Color returns the Color property of the i-th vertex.
func (gt *GLTriangles) Color(i int) pixel.RGBA {
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]
	return pixel.RGBA{
faiface's avatar
faiface committed
		R: float64(r),
		G: float64(g),
		B: float64(b),
		A: float64(a),
	}
}

// Picture returns the Picture property of the i-th vertex.
func (gt *GLTriangles) Picture(i int) (pic pixel.Vec, intensity float64) {
faiface's avatar
faiface committed
	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])
	return pixel.V(float64(tx), float64(ty)), intensity
faiface's avatar
faiface committed
}