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 }