app-browser.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. // Copyright 2016 The G3N 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 wasm
  5. // +build wasm
  6. package app
  7. import (
  8. "fmt"
  9. "github.com/g3n/engine/renderer"
  10. "github.com/g3n/engine/window"
  11. "syscall/js"
  12. "time"
  13. )
  14. // Default canvas id
  15. const canvasId = "g3n-canvas"
  16. // Application
  17. type Application struct {
  18. window.IWindow // Embedded WebGLCanvas
  19. keyState *window.KeyState // Keep track of keyboard state
  20. renderer *renderer.Renderer // Renderer object
  21. startTime time.Time // Application start time
  22. frameStart time.Time // Frame start time
  23. frameDelta time.Duration // Duration of last frame
  24. exit bool
  25. cbid js.Value
  26. }
  27. // App returns the Application singleton, creating it the first time.
  28. func App(width, height, title) *Application {
  29. // Return singleton if already created
  30. if a != nil {
  31. return a
  32. }
  33. a = new(Application)
  34. // Initialize window
  35. err := window.Init(canvasId)
  36. if err != nil {
  37. panic(err)
  38. }
  39. a.IWindow = window.Get()
  40. // TODO audio setup here
  41. a.keyState = window.NewKeyState(a) // Create KeyState
  42. // Create renderer and add default shaders
  43. a.renderer = renderer.NewRenderer(a.Gls())
  44. err = a.renderer.AddDefaultShaders()
  45. if err != nil {
  46. panic(fmt.Errorf("AddDefaultShaders:%v", err))
  47. }
  48. return a
  49. }
  50. // Run starts the update loop.
  51. // It calls the user-provided update function every frame.
  52. func (a *Application) Run(update func(rend *renderer.Renderer, deltaTime time.Duration)) {
  53. // Create channel so later we can prevent application from finishing while we wait for callbacks
  54. done := make(chan bool)
  55. // Initialize start and frame time
  56. a.startTime = time.Now()
  57. a.frameStart = time.Now()
  58. // Set up recurring calls to user's update function
  59. var tick js.Func
  60. tick = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
  61. // Update frame start and frame delta
  62. now := time.Now()
  63. a.frameDelta = now.Sub(a.frameStart)
  64. a.frameStart = now
  65. // Call user's update function
  66. update(a.renderer, a.frameDelta)
  67. // Set up new callback if not exiting
  68. if !a.exit {
  69. a.cbid = js.Global().Call("requestAnimationFrame", tick)
  70. } else {
  71. a.Dispatch(OnExit, nil)
  72. done <- true // Write to done channel to exit the app
  73. }
  74. return nil
  75. })
  76. defer tick.Release()
  77. a.cbid = js.Global().Call("requestAnimationFrame", tick)
  78. // Read from done channel
  79. // This channel will be empty (except when we want to exit the app)
  80. // It keeps the app from finishing while we wait for the next call to tick()
  81. <-done
  82. // Destroy the window
  83. a.IWindow.Destroy()
  84. }
  85. // Exit exits the app.
  86. func (a *Application) Exit() {
  87. a.exit = true
  88. }
  89. // Renderer returns the application's renderer.
  90. func (a *Application) Renderer() *renderer.Renderer {
  91. return a.renderer
  92. }
  93. // KeyState returns the application's KeyState.
  94. func (a *Application) KeyState() *window.KeyState {
  95. return a.keyState
  96. }
  97. // RunTime returns the elapsed duration since the call to Run().
  98. func (a *Application) RunTime() time.Duration {
  99. return time.Now().Sub(a.startTime)
  100. }