Window.java 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. package me.hammerle.snuviengine.api;
  2. import org.lwjgl.glfw.*;
  3. import org.lwjgl.opengl.*;
  4. import static org.lwjgl.glfw.GLFW.*;
  5. import static org.lwjgl.opengl.GL11.*;
  6. import static org.lwjgl.system.MemoryUtil.*;
  7. public final class Window {
  8. private final double secondsPerTick;
  9. private final WindowView windowView;
  10. private long window = NULL;
  11. private Keys keys = null;
  12. private Gamepad gamepad = null;
  13. private Renderer renderer;
  14. private final Stats stats = new Stats();
  15. private int newWidth = -1;
  16. private int newHeight = -1;
  17. public Window(long nanosPerTick, WindowView windowView) {
  18. secondsPerTick = nanosPerTick / 1_000_000_000.0;
  19. this.windowView = windowView;
  20. }
  21. public boolean initialize(String name, int width, int height) {
  22. setErrorCallback();
  23. if(initializeGLFW()) {
  24. return false;
  25. }
  26. setWindowHints();
  27. if(createWindow(name, width, height)) {
  28. return false;
  29. }
  30. setKeyCallback();
  31. setGamepad();
  32. setResizeCallback();
  33. createAndSetContext();
  34. setBufferSwapInterval();
  35. return !initializeRenderer(width, height);
  36. }
  37. private void setErrorCallback() {
  38. GLFWErrorCallback.createPrint(System.out).set();
  39. }
  40. private boolean initializeGLFW() {
  41. return !glfwInit();
  42. }
  43. private void setWindowHints() {
  44. glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
  45. glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
  46. glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
  47. }
  48. private boolean createWindow(String name, int width, int height) {
  49. window = glfwCreateWindow(width, height, name, NULL, NULL);
  50. if(window == NULL) {
  51. terminateGLFW();
  52. return true;
  53. }
  54. return false;
  55. }
  56. private void terminateGLFW() {
  57. glfwTerminate();
  58. }
  59. private void setKeyCallback() {
  60. keys = new Keys();
  61. glfwSetKeyCallback(window, (eventWindow, key, scancode, action, mods) -> {
  62. if(action == GLFW_PRESS) {
  63. keys.press(key);
  64. } else if(action == GLFW_RELEASE) {
  65. keys.release(key);
  66. }
  67. });
  68. }
  69. private void setGamepad() {
  70. gamepad = new Gamepad(GLFW_JOYSTICK_1);
  71. }
  72. private void setResizeCallback() {
  73. glfwSetFramebufferSizeCallback(window, (eventWindow, width, height) -> {
  74. newWidth = width;
  75. newHeight = height;
  76. });
  77. }
  78. private void createAndSetContext() {
  79. glfwMakeContextCurrent(window);
  80. GL.createCapabilities();
  81. }
  82. private void setBufferSwapInterval() {
  83. glfwSwapInterval(1);
  84. }
  85. private boolean initializeRenderer(int width, int height) {
  86. int shaderProgram = Shader.createShaderProgram("vertex.vs", "fragment.fs");
  87. if(shaderProgram == -1) {
  88. return true;
  89. }
  90. renderer = new Renderer(shaderProgram, width, height, windowView);
  91. return false;
  92. }
  93. public void open(Game game) {
  94. game.onCompleteInitialization(stats, keys, gamepad);
  95. runGameLoop(game);
  96. game.onStop();
  97. destroyWindow();
  98. terminateGLFW();
  99. freeErrorCallback();
  100. }
  101. private void runGameLoop(Game game) {
  102. double lastSecondTime = glfwGetTime();
  103. double lag = 0;
  104. while(!glfwWindowShouldClose(window) && game.isRunning()) {
  105. double newSecondTime = glfwGetTime();
  106. lag += newSecondTime - lastSecondTime;
  107. lastSecondTime = newSecondTime;
  108. while(lag >= secondsPerTick) {
  109. lag -= secondsPerTick;
  110. tick(game);
  111. }
  112. renderTick(game, (float) (lag / secondsPerTick));
  113. }
  114. }
  115. private void tick(Game game) {
  116. stats.updateTicksPerSecond();
  117. gamepad.tick();
  118. keys.tick();
  119. game.tick();
  120. }
  121. private void renderTick(Game game, float lag) {
  122. updateWindowSize();
  123. stats.updateFramesPerSecond();
  124. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  125. game.renderTick(renderer, lag);
  126. glfwSwapBuffers(window);
  127. glfwPollEvents();
  128. }
  129. private void updateWindowSize() {
  130. if(newWidth == -1 || newHeight == -1) {
  131. return;
  132. }
  133. int width = windowView.getWidth(newWidth, newHeight);
  134. int height = windowView.getHeight(newWidth, newHeight);
  135. if(width != newWidth || height != newHeight) {
  136. glfwSetWindowSize(window, width, height);
  137. } else {
  138. newWidth = -1;
  139. newHeight = -1;
  140. }
  141. glViewport(0, 0, width, height);
  142. renderer.setSize(width, height, windowView);
  143. }
  144. private void destroyWindow() {
  145. glfwDestroyWindow(window);
  146. }
  147. private void freeErrorCallback() {
  148. GLFWErrorCallback error = glfwSetErrorCallback(null);
  149. if(error != null) {
  150. error.free();
  151. }
  152. }
  153. }