github.com/c-darwin/mobile@v0.0.0-20160313183840-ff625c46f7c9/exp/sprite/glsprite/glsprite.go (about)

     1  // Copyright 2014 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // +build darwin linux
     6  
     7  // Package glsprite implements a sprite Engine using OpenGL ES 2.
     8  //
     9  // Each sprite.Texture is loaded as a GL texture object and drawn
    10  // to the screen via an affine transform done in a simple shader.
    11  package glsprite // import "github.com/c-darwin/mobile/exp/sprite/glsprite"
    12  
    13  import (
    14  	"image"
    15  	"image/draw"
    16  
    17  	"github.com/c-darwin/mobile/event/size"
    18  	"github.com/c-darwin/mobile/exp/f32"
    19  	"github.com/c-darwin/mobile/exp/gl/glutil"
    20  	"github.com/c-darwin/mobile/exp/sprite"
    21  	"github.com/c-darwin/mobile/exp/sprite/clock"
    22  	"github.com/c-darwin/mobile/geom"
    23  )
    24  
    25  type node struct {
    26  	// TODO: move this into package sprite as Node.EngineFields.RelTransform??
    27  	relTransform f32.Affine
    28  }
    29  
    30  type texture struct {
    31  	glImage *glutil.Image
    32  	b       image.Rectangle
    33  }
    34  
    35  func (t *texture) Bounds() (w, h int) { return t.b.Dx(), t.b.Dy() }
    36  
    37  func (t *texture) Download(r image.Rectangle, dst draw.Image) {
    38  	panic("TODO")
    39  }
    40  
    41  func (t *texture) Upload(r image.Rectangle, src image.Image) {
    42  	draw.Draw(t.glImage.RGBA, r, src, src.Bounds().Min, draw.Src)
    43  	t.glImage.Upload()
    44  }
    45  
    46  func (t *texture) Unload() {
    47  	panic("TODO")
    48  }
    49  
    50  func Engine() sprite.Engine {
    51  	return &engine{
    52  		nodes: []*node{nil},
    53  	}
    54  }
    55  
    56  type engine struct {
    57  	glImages map[sprite.Texture]*glutil.Image
    58  	nodes    []*node
    59  
    60  	absTransforms []f32.Affine
    61  }
    62  
    63  func (e *engine) Register(n *sprite.Node) {
    64  	if n.EngineFields.Index != 0 {
    65  		panic("glsprite: sprite.Node already registered")
    66  	}
    67  	o := &node{}
    68  	o.relTransform.Identity()
    69  
    70  	e.nodes = append(e.nodes, o)
    71  	n.EngineFields.Index = int32(len(e.nodes) - 1)
    72  }
    73  
    74  func (e *engine) Unregister(n *sprite.Node) {
    75  	panic("todo")
    76  }
    77  
    78  func (e *engine) LoadTexture(src image.Image) (sprite.Texture, error) {
    79  	b := src.Bounds()
    80  	t := &texture{glutil.NewImage(b.Dx(), b.Dy()), b}
    81  	t.Upload(b, src)
    82  	// TODO: set "glImage.Pix = nil"?? We don't need the CPU-side image any more.
    83  	return t, nil
    84  }
    85  
    86  func (e *engine) SetSubTex(n *sprite.Node, x sprite.SubTex) {
    87  	n.EngineFields.Dirty = true // TODO: do we need to propagate dirtiness up/down the tree?
    88  	n.EngineFields.SubTex = x
    89  }
    90  
    91  func (e *engine) SetTransform(n *sprite.Node, m f32.Affine) {
    92  	n.EngineFields.Dirty = true // TODO: do we need to propagate dirtiness up/down the tree?
    93  	e.nodes[n.EngineFields.Index].relTransform = m
    94  }
    95  
    96  func (e *engine) Render(scene *sprite.Node, t clock.Time, sz size.Event) {
    97  	e.absTransforms = append(e.absTransforms[:0], f32.Affine{
    98  		{1, 0, 0},
    99  		{0, 1, 0},
   100  	})
   101  	e.render(scene, t, sz)
   102  }
   103  
   104  func (e *engine) render(n *sprite.Node, t clock.Time, sz size.Event) {
   105  	if n.EngineFields.Index == 0 {
   106  		panic("glsprite: sprite.Node not registered")
   107  	}
   108  	if n.Arranger != nil {
   109  		n.Arranger.Arrange(e, n, t)
   110  	}
   111  
   112  	// Push absTransforms.
   113  	// TODO: cache absolute transforms and use EngineFields.Dirty?
   114  	rel := &e.nodes[n.EngineFields.Index].relTransform
   115  	m := f32.Affine{}
   116  	m.Mul(&e.absTransforms[len(e.absTransforms)-1], rel)
   117  	e.absTransforms = append(e.absTransforms, m)
   118  
   119  	if x := n.EngineFields.SubTex; x.T != nil {
   120  		x.T.(*texture).glImage.Draw(
   121  			sz,
   122  			geom.Point{
   123  				geom.Pt(m[0][2]),
   124  				geom.Pt(m[1][2]),
   125  			},
   126  			geom.Point{
   127  				geom.Pt(m[0][2] + m[0][0]),
   128  				geom.Pt(m[1][2] + m[1][0]),
   129  			},
   130  			geom.Point{
   131  				geom.Pt(m[0][2] + m[0][1]),
   132  				geom.Pt(m[1][2] + m[1][1]),
   133  			},
   134  			x.R,
   135  		)
   136  	}
   137  
   138  	for c := n.FirstChild; c != nil; c = c.NextSibling {
   139  		e.render(c, t, sz)
   140  	}
   141  
   142  	// Pop absTransforms.
   143  	e.absTransforms = e.absTransforms[:len(e.absTransforms)-1]
   144  }