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 }