window.nim 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. # author: Ethosa
  2. import
  3. thirdparty/opengl,
  4. thirdparty/opengl/glut,
  5. core/color,
  6. core/exceptions,
  7. core/input,
  8. nodes/node,
  9. nodes/scene,
  10. environment,
  11. os
  12. var
  13. cmdLine {.importc: "cmdLine".}: array[0..255, cstring]
  14. cmdCount {.importc: "cmdCount".}: cint
  15. loadExtensions() # Load OpenGL extensions.
  16. glutInit(addr cmdCount, addr cmdLine) # Initializ glut lib.
  17. glutInitDisplayMode(GLUT_DOUBLE)
  18. var
  19. env*: EnvironmentRef = newEnvironment()
  20. width, height: cint
  21. main_scene*: ScenePtr = nil
  22. current_scene*: ScenePtr = nil
  23. scenes*: seq[ScenePtr] = @[]
  24. paused*: bool = false
  25. # --- Callbacks --- #
  26. var
  27. mouse_on: NodePtr = nil
  28. window_created: bool = false
  29. proc display {.cdecl.} =
  30. ## Displays window.
  31. let (r, g, b, a) = env.color.toFloatTuple()
  32. glClearColor(r*env.brightness, g*env.brightness, b*env.brightness, a*env.brightness)
  33. glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT)
  34. glEnable(GL_BLEND)
  35. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
  36. # Draw current scene.
  37. current_scene.drawScene(width.GLfloat, height.GLfloat, paused)
  38. press_state = -1
  39. mouse_on = nil
  40. # Update window.
  41. glFlush()
  42. glutSwapBuffers()
  43. os.sleep(env.delay)
  44. proc reshape(w, h: cint) {.cdecl.} =
  45. ## This called when window resized.
  46. if w > 0 and h > 0:
  47. glViewport(0, 0, w, h)
  48. glMatrixMode(GL_PROJECTION)
  49. glLoadIdentity()
  50. glOrtho(-w.GLdouble/2, w.GLdouble/2, -h.GLdouble/2, h.GLdouble/2, -1, 1)
  51. glMatrixMode(GL_MODELVIEW)
  52. glLoadIdentity()
  53. width = w
  54. height = h
  55. if current_scene != nil:
  56. current_scene.reAnchorScene(w.GLfloat, h.GLfloat, paused)
  57. template check(event, condition, conditionelif: untyped): untyped =
  58. if last_event is `event` and `condition`:
  59. press_state = 2
  60. elif `conditionelif`:
  61. press_state = 1
  62. else:
  63. press_state = 0
  64. proc mouse(button, state, x, y: cint) {.cdecl.} =
  65. ## Handle mouse input.
  66. check(InputEventMouseButton, last_event.pressed and state == GLUT_DOWN, state == GLUT_DOWN)
  67. last_event.button_index = button
  68. last_event.x = x.float
  69. last_event.y = y.float
  70. last_event.kind = MOUSE
  71. mouse_pressed = state == GLUT_DOWN
  72. last_event.pressed = state == GLUT_DOWN
  73. current_scene.handleScene(last_event, mouse_on, paused)
  74. proc keyboardpress(c: int8, x, y: cint) {.cdecl.} =
  75. ## Called when press any key on keyboard.
  76. if c < 0:
  77. return
  78. let key = $c.char
  79. check(InputEventKeyboard, last_event.pressed, true)
  80. last_event.key = key
  81. last_event.key_int = c
  82. last_event.x = x.float
  83. last_event.y = y.float
  84. if key notin pressed_keys:
  85. pressed_keys.add(key)
  86. pressed_keys_ints.add(c)
  87. last_event.kind = KEYBOARD
  88. last_key_state = key_state
  89. key_state = true
  90. current_scene.handleScene(last_event, mouse_on, paused)
  91. proc keyboardup(c: int8, x, y: cint) {.cdecl.} =
  92. ## Called when any key no more pressed.
  93. if c < 0:
  94. return
  95. let key = $c.char
  96. check(InputEventKeyboard, false, false)
  97. last_event.key = key
  98. last_event.key_int = c
  99. last_event.x = x.float
  100. last_event.y = y.float
  101. last_event.kind = KEYBOARD
  102. last_key_state = key_state
  103. key_state = false
  104. var i = 0
  105. for k in pressed_keys:
  106. if k == key:
  107. pressed_keys.delete(i)
  108. pressed_keys_ints.delete(i)
  109. break
  110. inc i
  111. current_scene.handleScene(last_event, mouse_on, paused)
  112. proc keyboardspecialpress(c: cint, x, y: cint) {.cdecl.} =
  113. ## Called when press any key on keyboard.
  114. if c < 0:
  115. return
  116. check(InputEventKeyboard, last_event.pressed, true)
  117. last_event.key_cint = c
  118. last_event.x = x.float
  119. last_event.y = y.float
  120. if c notin pressed_keys_cints:
  121. pressed_keys_cints.add(c)
  122. last_event.kind = KEYBOARD
  123. last_key_state = key_state
  124. key_state = true
  125. current_scene.handleScene(last_event, mouse_on, paused)
  126. proc keyboardspecialup(c: cint, x, y: cint) {.cdecl.} =
  127. ## Called when any key no more pressed.
  128. if c < 0:
  129. return
  130. check(InputEventKeyboard, false, false)
  131. last_event.key_cint = c
  132. last_event.x = x.float
  133. last_event.y = y.float
  134. last_event.kind = KEYBOARD
  135. last_key_state = key_state
  136. key_state = false
  137. var i = 0
  138. for k in pressed_keys_cints:
  139. if k == c:
  140. pressed_keys_cints.delete(i)
  141. break
  142. inc i
  143. current_scene.handleScene(last_event, mouse_on, paused)
  144. proc motion(x, y: cint) {.cdecl.} =
  145. ## Called on any mouse motion.
  146. last_event.kind = MOTION
  147. last_event.xrel = last_event.x - x.float
  148. last_event.yrel = last_event.y - y.float
  149. last_event.x = x.float
  150. last_event.y = y.float
  151. current_scene.handleScene(last_event, mouse_on, paused)
  152. # ---- Public ---- #
  153. proc addScene*(scene: ScenePtr) =
  154. ## Adds a new scenes in app.
  155. ##
  156. ## Arguments:
  157. ## - `scene` - pointer to the Scene object.
  158. if scene notin scenes:
  159. scenes.add(scene)
  160. proc changeScene*(name: string): bool {.discardable.} =
  161. ## Changes current scene.
  162. ##
  163. ## Arguments:
  164. ## - `name` - name of the added scene.
  165. result = false
  166. for scene in scenes:
  167. if scene.name == name:
  168. if current_scene != nil:
  169. current_scene.exit()
  170. current_scene = nil
  171. current_scene = scene
  172. current_scene.enter()
  173. current_scene.reAnchorScene(width.GLfloat, height.GLfloat, paused)
  174. result = true
  175. break
  176. proc setMainScene*(name: string) =
  177. ## Set up main scene.
  178. ##
  179. ## Arguments:
  180. ## - `name` - name of the added scene.
  181. for scene in scenes:
  182. if scene.name == name:
  183. main_scene = scene
  184. break
  185. proc setTitle*(title: cstring) =
  186. ## Changes window title.
  187. if not window_created:
  188. raise newException(WindowNotCreatedError, "Window not created!")
  189. glutSetWindowTitle(title)
  190. proc Window*(title: cstring, w: cint = 640, h: cint = 360) {.cdecl.} =
  191. ## Creates a new window pointer
  192. ##
  193. ## Arguments:
  194. ## - `title` - window title.
  195. # Set up window.
  196. glutInitWindowSize(w, h)
  197. glutInitWindowPosition(100, 100)
  198. discard glutCreateWindow(title)
  199. # Set up OpenGL
  200. let (r, g, b, a) = env.color.toFloatTuple()
  201. glClearColor(r, g, b, a)
  202. glShadeModel(GL_FLAT)
  203. glClear(GL_COLOR_BUFFER_BIT)
  204. reshape(w, h)
  205. window_created = true
  206. proc windowLaunch* =
  207. ## Start main window loop.
  208. glutDisplayFunc(display)
  209. glutIdleFunc(display)
  210. glutReshapeFunc(reshape)
  211. glutMouseFunc(mouse)
  212. glutKeyboardFunc(keyboardpress)
  213. glutKeyboardUpFunc(keyboardup)
  214. glutSpecialFunc(keyboardspecialpress)
  215. glutSpecialUpFunc(keyboardspecialup)
  216. glutMotionFunc(motion)
  217. glutPassiveMotionFunc(motion)
  218. if main_scene == nil:
  219. raise newException(MainSceneNotLoadedError, "Main scene is not indicated!")
  220. changeScene(main_scene.name)
  221. glutMainLoop()
  222. current_scene.exit()