glsprite.go 3.9 KB

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