github.com/acrespo/mobile@v0.0.0-20190107162257-dc0771356504/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 windows
     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 "golang.org/x/mobile/exp/sprite/glsprite"
    12  
    13  import (
    14  	"image"
    15  	"image/draw"
    16  
    17  	"golang.org/x/mobile/event/size"
    18  	"golang.org/x/mobile/exp/f32"
    19  	"golang.org/x/mobile/exp/gl/glutil"
    20  	"golang.org/x/mobile/exp/sprite"
    21  	"golang.org/x/mobile/exp/sprite/clock"
    22  	"golang.org/x/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  	e       *engine
    32  	glImage *glutil.Image
    33  	b       image.Rectangle
    34  }
    35  
    36  func (t *texture) Bounds() (w, h int) { return t.b.Dx(), t.b.Dy() }
    37  
    38  func (t *texture) Download(r image.Rectangle, dst draw.Image) {
    39  	panic("TODO")
    40  }
    41  
    42  func (t *texture) Upload(r image.Rectangle, src image.Image) {
    43  	draw.Draw(t.glImage.RGBA, r, src, src.Bounds().Min, draw.Src)
    44  	t.glImage.Upload()
    45  }
    46  
    47  func (t *texture) Release() {
    48  	t.glImage.Release()
    49  	delete(t.e.textures, t)
    50  }
    51  
    52  // Engine creates an OpenGL-based sprite.Engine.
    53  func Engine(images *glutil.Images) sprite.Engine {
    54  	return &engine{
    55  		nodes:    []*node{nil},
    56  		images:   images,
    57  		textures: make(map[*texture]struct{}),
    58  	}
    59  }
    60  
    61  type engine struct {
    62  	images   *glutil.Images
    63  	textures map[*texture]struct{}
    64  	nodes    []*node
    65  
    66  	absTransforms []f32.Affine
    67  }
    68  
    69  func (e *engine) Register(n *sprite.Node) {
    70  	if n.EngineFields.Index != 0 {
    71  		panic("glsprite: sprite.Node already registered")
    72  	}
    73  	o := &node{}
    74  	o.relTransform.Identity()
    75  
    76  	e.nodes = append(e.nodes, o)
    77  	n.EngineFields.Index = int32(len(e.nodes) - 1)
    78  }
    79  
    80  func (e *engine) Unregister(n *sprite.Node) {
    81  	panic("todo")
    82  }
    83  
    84  func (e *engine) LoadTexture(src image.Image) (sprite.Texture, error) {
    85  	b := src.Bounds()
    86  	t := &texture{
    87  		e:       e,
    88  		glImage: e.images.NewImage(b.Dx(), b.Dy()),
    89  		b:       b,
    90  	}
    91  	e.textures[t] = struct{}{}
    92  	t.Upload(b, src)
    93  	// TODO: set "glImage.Pix = nil"?? We don't need the CPU-side image any more.
    94  	return t, nil
    95  }
    96  
    97  func (e *engine) SetSubTex(n *sprite.Node, x sprite.SubTex) {
    98  	n.EngineFields.Dirty = true // TODO: do we need to propagate dirtiness up/down the tree?
    99  	n.EngineFields.SubTex = x
   100  }
   101  
   102  func (e *engine) SetTransform(n *sprite.Node, m f32.Affine) {
   103  	n.EngineFields.Dirty = true // TODO: do we need to propagate dirtiness up/down the tree?
   104  	e.nodes[n.EngineFields.Index].relTransform = m
   105  }
   106  
   107  func (e *engine) Render(scene *sprite.Node, t clock.Time, sz size.Event) {
   108  	e.absTransforms = append(e.absTransforms[:0], f32.Affine{
   109  		{1, 0, 0},
   110  		{0, 1, 0},
   111  	})
   112  	e.render(scene, t, sz)
   113  }
   114  
   115  func (e *engine) render(n *sprite.Node, t clock.Time, sz size.Event) {
   116  	if n.EngineFields.Index == 0 {
   117  		panic("glsprite: sprite.Node not registered")
   118  	}
   119  	if n.Arranger != nil {
   120  		n.Arranger.Arrange(e, n, t)
   121  	}
   122  
   123  	// Push absTransforms.
   124  	// TODO: cache absolute transforms and use EngineFields.Dirty?
   125  	rel := &e.nodes[n.EngineFields.Index].relTransform
   126  	m := f32.Affine{}
   127  	m.Mul(&e.absTransforms[len(e.absTransforms)-1], rel)
   128  	e.absTransforms = append(e.absTransforms, m)
   129  
   130  	if x := n.EngineFields.SubTex; x.T != nil {
   131  		x.T.(*texture).glImage.Draw(
   132  			sz,
   133  			geom.Point{
   134  				geom.Pt(m[0][2]),
   135  				geom.Pt(m[1][2]),
   136  			},
   137  			geom.Point{
   138  				geom.Pt(m[0][2] + m[0][0]),
   139  				geom.Pt(m[1][2] + m[1][0]),
   140  			},
   141  			geom.Point{
   142  				geom.Pt(m[0][2] + m[0][1]),
   143  				geom.Pt(m[1][2] + m[1][1]),
   144  			},
   145  			x.R,
   146  		)
   147  	}
   148  
   149  	for c := n.FirstChild; c != nil; c = c.NextSibling {
   150  		e.render(c, t, sz)
   151  	}
   152  
   153  	// Pop absTransforms.
   154  	e.absTransforms = e.absTransforms[:len(e.absTransforms)-1]
   155  }
   156  
   157  func (e *engine) Release() {
   158  	for img := range e.textures {
   159  		img.Release()
   160  	}
   161  }