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