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 }