Engine.java 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. package me.hammerle.snuviengine.api;
  2. import org.lwjgl.glfw.*;
  3. import org.lwjgl.opengl.*;
  4. import static org.lwjgl.glfw.Callbacks.*;
  5. import static org.lwjgl.glfw.GLFW.*;
  6. import static org.lwjgl.opengl.GL11.*;
  7. import static org.lwjgl.system.MemoryUtil.*;
  8. public abstract class Engine
  9. {
  10. private static final String VERSION = "0.0.1";
  11. public static final int TILE_SIZE = 16;
  12. public static final float SCALE = 2.0f;
  13. private long window;
  14. private long fpsLimit = -1;
  15. private long sleep = 0;
  16. private long nanosPerTick = 10_000_000;
  17. private final int maxTicksPerFrame = 20;
  18. private final Timer fps = new Timer(60);
  19. private final Timer tps = new Timer(1_000_000_000l / nanosPerTick);
  20. public Engine()
  21. {
  22. }
  23. public final void run()
  24. {
  25. initGLFW();
  26. loop();
  27. glfwFreeCallbacks(window);
  28. glfwDestroyWindow(window);
  29. glfwTerminate();
  30. GLFWErrorCallback error = glfwSetErrorCallback(null);
  31. if(error != null)
  32. {
  33. error.free();
  34. }
  35. }
  36. public final void stop()
  37. {
  38. glfwSetWindowShouldClose(window, true);
  39. }
  40. private void initGLFW()
  41. {
  42. GLFWErrorCallback.createPrint(System.err).set();
  43. if(!glfwInit())
  44. {
  45. throw new IllegalStateException("Unable to initialize GLFW");
  46. }
  47. glfwDefaultWindowHints();
  48. glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
  49. glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
  50. window = glfwCreateWindow(Shader.getViewWidth(), Shader.getViewHeight(), "SnuviEngine " + VERSION, NULL, NULL);
  51. if(window == NULL)
  52. {
  53. throw new RuntimeException("Failed to create the GLFW window");
  54. }
  55. glfwSetKeyCallback(window, (w, key, scancode, action, mods) ->
  56. {
  57. if(action == GLFW_RELEASE)
  58. {
  59. KeyHandler.onKeyUpEvent(key);
  60. }
  61. else if(action == GLFW_PRESS)
  62. {
  63. KeyHandler.onKeyDownEvent(key);
  64. }
  65. });
  66. glfwSetFramebufferSizeCallback(window, (w, fwidth, fheight) ->
  67. {
  68. glViewport(0, 0, fwidth, fheight);
  69. Shader.setViewPort(fwidth, fheight);
  70. });
  71. //glfwSetWindowAspectRatio(window, 16, 9);
  72. glfwMakeContextCurrent(window);
  73. glfwSwapInterval(1);
  74. glfwShowWindow(window);
  75. }
  76. private void sleep(long nanos)
  77. {
  78. if(nanos < 0)
  79. {
  80. sleep += nanos;
  81. return;
  82. }
  83. if(sleep < 0)
  84. {
  85. sleep += nanos / 4;
  86. nanos = (nanos * 3) / 4;
  87. }
  88. long end = System.nanoTime() + nanos - 10000;
  89. try
  90. {
  91. Thread.sleep(nanos / 1_000_000);
  92. }
  93. catch(InterruptedException ex)
  94. {
  95. }
  96. int i = 0;
  97. while(System.nanoTime() < end)
  98. {
  99. i++;
  100. }
  101. }
  102. private void loop()
  103. {
  104. GL.createCapabilities();
  105. Shader.init();
  106. init();
  107. fps.update();
  108. tps.update();
  109. long lag = 0;
  110. while(!glfwWindowShouldClose(window))
  111. {
  112. glfwSwapBuffers(window);
  113. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  114. if(fpsLimit > 0)
  115. {
  116. sleep(fpsLimit - fps.getCurrentTime());
  117. }
  118. fps.update();
  119. lag += fps.getTime();
  120. int ticksPerFrame = 0;
  121. while(lag >= nanosPerTick)
  122. {
  123. lag -= nanosPerTick;
  124. tps.update();
  125. KeyHandler.tick();
  126. tick();
  127. ticksPerFrame++;
  128. if(ticksPerFrame >= maxTicksPerFrame)
  129. {
  130. long skip = lag / nanosPerTick;
  131. lag -= skip * nanosPerTick;
  132. if(skip > 0)
  133. {
  134. System.out.println("skipped " + skip + " game ticks " + lag);
  135. }
  136. break;
  137. }
  138. }
  139. Shader.doTasks();
  140. renderTick((float) lag / nanosPerTick);
  141. tps.draw();
  142. fps.draw();
  143. glfwPollEvents();
  144. }
  145. }
  146. public final void setNanosPerTick(long nanos)
  147. {
  148. nanosPerTick = nanos;
  149. tps.setExpectedValue(1_000_000_000l / nanos);
  150. }
  151. public final long getNanosPerTick()
  152. {
  153. return nanosPerTick;
  154. }
  155. public final double getTps()
  156. {
  157. return tps.getCallsPerSecond();
  158. }
  159. public final void setRenderTps(boolean active)
  160. {
  161. tps.setActive(active);
  162. }
  163. public final double getFps()
  164. {
  165. return fps.getCallsPerSecond();
  166. }
  167. public final void setMaxFps(int max)
  168. {
  169. fpsLimit = 1_000_000_000 / max;
  170. }
  171. public final void setRenderFps(boolean active)
  172. {
  173. fps.setActive(active);
  174. }
  175. public abstract void init();
  176. public abstract void tick();
  177. public abstract void renderTick(float lag);
  178. }