app-desktop.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  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. "time"
  10. "github.com/g3n/engine/audio/al"
  11. "github.com/g3n/engine/audio/vorbis"
  12. "github.com/g3n/engine/renderer"
  13. "github.com/g3n/engine/window"
  14. )
  15. // Application
  16. type Application struct {
  17. window.IWindow // Embedded GlfwWindow
  18. keyState *window.KeyState // Keep track of keyboard state
  19. renderer *renderer.Renderer // Renderer object
  20. audioDev *al.Device // Default audio device
  21. startTime time.Time // Application start time
  22. frameStart time.Time // Frame start time
  23. frameDelta time.Duration // Duration of last frame
  24. }
  25. // App returns the Application singleton, creating it the first time.
  26. func App(width, height int, title string) *Application {
  27. // Return singleton if already created
  28. if a != nil {
  29. return a
  30. }
  31. a = new(Application)
  32. // Initialize window
  33. err := window.Init(width, height, title)
  34. if err != nil {
  35. panic(err)
  36. }
  37. a.IWindow = window.Get()
  38. a.openDefaultAudioDevice() // Set up audio
  39. a.keyState = window.NewKeyState(a) // Create KeyState
  40. // Create renderer and add default shaders
  41. a.renderer = renderer.NewRenderer(a.Gls())
  42. err = a.renderer.AddDefaultShaders()
  43. if err != nil {
  44. panic(fmt.Errorf("AddDefaultShaders:%v", err))
  45. }
  46. return a
  47. }
  48. // Run starts the update loop.
  49. // It calls the user-provided update function every frame.
  50. func (a *Application) Run(update func(rend *renderer.Renderer, deltaTime time.Duration)) {
  51. // Initialize start and frame time
  52. a.startTime = time.Now()
  53. a.frameStart = time.Now()
  54. // Set up recurring calls to user's update function
  55. for {
  56. // If Exit() was called or there was an attempt to close the window dispatch OnExit event for subscribers.
  57. // If no subscriber cancelled the event, terminate the application.
  58. if a.IWindow.(*window.GlfwWindow).ShouldClose() {
  59. a.Dispatch(OnExit, nil)
  60. // TODO allow for cancelling exit e.g. showing dialog asking the user if he/she wants to save changes
  61. // if exit was cancelled {
  62. // a.IWindow.(*window.GlfwWindow).SetShouldClose(false)
  63. // } else {
  64. break
  65. // }
  66. }
  67. // Update frame start and frame delta
  68. now := time.Now()
  69. a.frameDelta = now.Sub(a.frameStart)
  70. a.frameStart = now
  71. // Call user's update function
  72. update(a.renderer, a.frameDelta)
  73. // Swap buffers and poll events
  74. a.IWindow.(*window.GlfwWindow).SwapBuffers()
  75. a.IWindow.(*window.GlfwWindow).PollEvents()
  76. }
  77. // Close default audio device
  78. if a.audioDev != nil {
  79. al.CloseDevice(a.audioDev)
  80. }
  81. // Destroy window
  82. a.Destroy()
  83. }
  84. // Exit requests to terminate the application
  85. // Application will dispatch OnQuit events to registered subscribers which
  86. // can cancel the process by calling CancelDispatch().
  87. func (a *Application) Exit() {
  88. a.IWindow.(*window.GlfwWindow).SetShouldClose(true)
  89. }
  90. // Renderer returns the application's renderer.
  91. func (a *Application) Renderer() *renderer.Renderer {
  92. return a.renderer
  93. }
  94. // KeyState returns the application's KeyState.
  95. func (a *Application) KeyState() *window.KeyState {
  96. return a.keyState
  97. }
  98. // RunTime returns the elapsed duration since the call to Run().
  99. func (a *Application) RunTime() time.Duration {
  100. return time.Since(a.startTime)
  101. }
  102. // openDefaultAudioDevice opens the default audio device setting it to the current context
  103. func (a *Application) openDefaultAudioDevice() error {
  104. // Opens default audio device
  105. var err error
  106. a.audioDev, err = al.OpenDevice("")
  107. if err != nil {
  108. return fmt.Errorf("opening OpenAL default device: %s", err)
  109. }
  110. // Check for OpenAL effects extension support
  111. var attribs []int
  112. if al.IsExtensionPresent("ALC_EXT_EFX") {
  113. attribs = []int{al.MAX_AUXILIARY_SENDS, 4}
  114. }
  115. // Create audio context
  116. acx, err := al.CreateContext(a.audioDev, attribs)
  117. if err != nil {
  118. return fmt.Errorf("creating OpenAL context: %s", err)
  119. }
  120. // Makes the context the current one
  121. err = al.MakeContextCurrent(acx)
  122. if err != nil {
  123. return fmt.Errorf("setting OpenAL context current: %s", err)
  124. }
  125. // Logs audio library versions
  126. log.Info("%s version: %s", al.GetString(al.Vendor), al.GetString(al.Version))
  127. log.Info("%s", vorbis.VersionString())
  128. return nil
  129. }