GameClient.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. #include <iostream>
  2. #include <GL/glew.h>
  3. #include <GLFW/glfw3.h>
  4. #include <unordered_map>
  5. #include <cmath>
  6. #include "common/utils/Types.h"
  7. #include "client/GameClient.h"
  8. #include "client/input/Keys.h"
  9. #include "client/input/MouseButtons.h"
  10. #include "client/rendering/Shader.h"
  11. #include "client/rendering/FontRenderer.h"
  12. #include "client/rendering/Mesh.h"
  13. #include "client/rendering/Framebuffer.h"
  14. #include "client/Utils.h"
  15. #include "client/math/Camera.h"
  16. #include "client/math/Matrix.h"
  17. #include "client/math/MatrixStack.h"
  18. #include "client/Game.h"
  19. struct InternGameClient
  20. {
  21. ~InternGameClient()
  22. {
  23. if(window != nullptr)
  24. {
  25. glfwDestroyWindow(window);
  26. }
  27. if(glfwInitDone)
  28. {
  29. glfwTerminate();
  30. }
  31. }
  32. bool glfwInitDone = false;
  33. GLFWwindow* window = nullptr;
  34. };
  35. static const u64 NANOS_PER_TICK = 50000000;
  36. static const float lagFactor = 1.0f / NANOS_PER_TICK;
  37. static u64 timeFactor = 1;
  38. static int width = 0;
  39. static int height = 0;
  40. static bool resize = false;
  41. static float fovY = 60.0f;
  42. static float nearClip = 0.1f;
  43. static float farClip = 1000.0f;
  44. static InternGameClient client;
  45. static Keys keys;
  46. static MouseButtons mButtons;
  47. struct Shaders
  48. {
  49. Shaders() :
  50. world("resources/shader/testVertex.vs", "resources/shader/testFragment.fs"),
  51. text("resources/shader/textVertex.vs", "resources/shader/textFragment.fs")
  52. {
  53. }
  54. Shader world;
  55. Shader text;
  56. bool isValid() const
  57. {
  58. return world.isValid() && text.isValid();
  59. }
  60. };
  61. struct Framebuffers
  62. {
  63. Framebuffers(u32 w, u32 h) : worldBuffer(w, h, Framebuffer::COLOR)
  64. {
  65. }
  66. void resize(u32 w, u32 h) const
  67. {
  68. worldBuffer.resize(w, h);
  69. }
  70. bool isValid() const
  71. {
  72. return worldBuffer.isValid();
  73. }
  74. Framebuffer worldBuffer;
  75. };
  76. struct InternGame
  77. {
  78. Game game;
  79. Camera cam;
  80. MatrixStack model;
  81. FontRenderer fontRenderer;
  82. };
  83. static u64 getTimeNanos()
  84. {
  85. return glfwGetTimerValue() * timeFactor;
  86. }
  87. static bool initGLFW()
  88. {
  89. client.glfwInitDone = glfwInit();
  90. if(!client.glfwInitDone)
  91. {
  92. std::cout << "could not initialize GLFW\n";
  93. return true;
  94. }
  95. timeFactor = 1000000000 / glfwGetTimerFrequency();
  96. return false;
  97. }
  98. static bool initWindow(int w, int h, const char* windowName)
  99. {
  100. width = w;
  101. height = h;
  102. glfwDefaultWindowHints();
  103. glfwWindowHint(GLFW_VISIBLE, 0);
  104. glfwWindowHint(GLFW_RESIZABLE, 1);
  105. glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
  106. glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
  107. glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
  108. client.window = glfwCreateWindow(width, height, windowName, nullptr, nullptr);
  109. if(client.window == nullptr)
  110. {
  111. std::cout << "could not create window\n";
  112. return true;
  113. }
  114. glfwMakeContextCurrent(client.window);
  115. glfwSwapInterval(1);
  116. return false;
  117. }
  118. static bool initGLEW()
  119. {
  120. GLenum err = glewInit();
  121. if(err != GLEW_OK)
  122. {
  123. std::cout << "could not initialize GLEW: " << glewGetErrorString(err) << "\n";
  124. return true;
  125. }
  126. std::cout << "using GLEW " << glewGetString(GLEW_VERSION) << "\n";
  127. return false;
  128. }
  129. static void initCallbacks()
  130. {
  131. // GLFWwindow* w, int key, int scancode, int action, int mod
  132. glfwSetKeyCallback(client.window, [](GLFWwindow*, int key, int, int action, int)
  133. {
  134. if(action == GLFW_PRESS)
  135. {
  136. keys.press(key);
  137. }
  138. else if(action == GLFW_RELEASE)
  139. {
  140. keys.release(key);
  141. }
  142. });
  143. // GLFWwindow* w, int button, int action, int mods
  144. glfwSetMouseButtonCallback(client.window, [](GLFWwindow*, int button, int action, int)
  145. {
  146. if(action == GLFW_PRESS)
  147. {
  148. mButtons.press(button);
  149. }
  150. else if(action == GLFW_RELEASE)
  151. {
  152. mButtons.release(button);
  153. }
  154. });
  155. // GLFWwindow* w, double xpos, double ypos
  156. glfwSetCursorPosCallback(client.window, [](GLFWwindow*, double x, double y)
  157. {
  158. mButtons.move(x, y);
  159. });
  160. // GLFWwindow* w, int width, int height
  161. glfwSetFramebufferSizeCallback(client.window, [](GLFWwindow*, int w, int h)
  162. {
  163. glViewport(0, 0, w, h);
  164. width = w;
  165. height = h;
  166. resize = true;
  167. });
  168. }
  169. static void tick(InternGame& game)
  170. {
  171. keys.tick();
  172. mButtons.tick();
  173. game.game.tick(keys, mButtons, game.cam);
  174. mButtons.postTick();
  175. }
  176. static void renderWorld(float lag, Shaders& shaders, InternGame& game)
  177. {
  178. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  179. glEnable(GL_DEPTH_TEST);
  180. // use world program
  181. shaders.world.use();
  182. // send projection matrix
  183. float tan = tanf((0.5f * fovY) * M_PI / 180.0f);
  184. float q = 1.0f / tan;
  185. float aspect = (float) width / height;
  186. float proj[] =
  187. {
  188. q / aspect, 0.0f, 0.0f, 0.0f,
  189. 0.0f, q, 0.0f, 0.0f,
  190. 0.0f, 0.0f, (nearClip + farClip) / (nearClip - farClip), -1.0f,
  191. 0.0f, 0.0f, (2.0f * nearClip * farClip) / (nearClip - farClip), 0.0f
  192. };
  193. shaders.world.setMatrix("proj", proj);
  194. // send view matrix
  195. game.cam.update(lag);
  196. const Vector right = game.cam.getRight();
  197. const Vector up = game.cam.getUp();
  198. const Vector back = game.cam.getBack();
  199. const Vector pos = game.cam.getPosition();
  200. float view[] =
  201. {
  202. right.getX(), up.getX(), back.getX(), 0.0f,
  203. right.getY(), up.getY(), back.getY(), 0.0f,
  204. right.getZ(), up.getZ(), back.getZ(), 0.0f,
  205. right.dotInverse(pos), up.dotInverse(pos), back.dotInverse(pos), 1.0f
  206. };
  207. shaders.world.setMatrix("view", view);
  208. // clear and send model matrix
  209. game.model.clear();
  210. shaders.world.setMatrix("model", game.model.get().getValues());
  211. // call high level world renderer
  212. game.game.renderWorld(lag, game.model, shaders.world);
  213. }
  214. static void renderTextOverlay(float lag, Shaders& shaders, InternGame& game)
  215. {
  216. glDisable(GL_DEPTH_TEST);
  217. shaders.text.use();
  218. Matrix m;
  219. shaders.text.setMatrix("proj", m.getValues());
  220. m.translate(-1.0f, 1.0f, 0.0f);
  221. m.scale(2.0f / width, -2.0f / height, 1.0f);
  222. shaders.text.setMatrix("view", m.getValues());
  223. game.model.clear();
  224. game.model.get().scale(2.0f, 2.0f, 2.0f);
  225. shaders.text.setMatrix("model", game.model.get().getValues());
  226. glEnable(GL_BLEND);
  227. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  228. glBlendEquation(GL_FUNC_ADD);
  229. game.game.renderTextOverlay(lag, game.model, shaders.text, game.fontRenderer);
  230. glDisable(GL_BLEND);
  231. }
  232. static void renderTick(float lag, Shaders& shaders, InternGame& game, Framebuffers& fb)
  233. {
  234. if(resize)
  235. {
  236. fb.resize(width, height);
  237. resize = false;
  238. }
  239. fb.worldBuffer.bind();
  240. renderWorld(lag, shaders, game);
  241. glBindFramebuffer(GL_FRAMEBUFFER, 0);
  242. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
  243. renderTextOverlay(lag, shaders, game);
  244. }
  245. static void loop()
  246. {
  247. Shaders shaders;
  248. if(!shaders.isValid())
  249. {
  250. return;
  251. }
  252. Framebuffers framebuffers(width, height);
  253. if(!framebuffers.isValid())
  254. {
  255. return;
  256. }
  257. InternGame game;
  258. Shader sh("resources/shader/test2Vertex.vs", "resources/shader/test2Fragment.fs");
  259. Mesh m;
  260. m.add({-1, -1, 0, 0, 0, 0, 0, 0});
  261. m.add({-1, 1, 0, 0, 1, 0, 0, 0});
  262. m.add({ 1, 1, 0, 1, 1, 0, 0, 0});
  263. m.add({-1, -1, 0, 0, 0, 0, 0, 0});
  264. m.add({ 1, -1, 0, 1, 0, 0, 0, 0});
  265. m.add({ 1, 1, 0, 1, 1, 0, 0, 0});
  266. m.build();
  267. u64 lastTime = getTimeNanos();
  268. u64 lag = 0;
  269. while(!glfwWindowShouldClose(client.window))
  270. {
  271. renderTick(lag * lagFactor, shaders, game, framebuffers);
  272. framebuffers.worldBuffer.bindColorTexture(1);
  273. sh.use();
  274. glEnable(GL_BLEND);
  275. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  276. glBlendEquation(GL_FUNC_ADD);
  277. m.draw();
  278. glDisable(GL_BLEND);
  279. glfwSwapBuffers(client.window);
  280. u64 newTime = getTimeNanos();
  281. lag += newTime - lastTime;
  282. lastTime = newTime;
  283. while(lag >= NANOS_PER_TICK)
  284. {
  285. lag -= NANOS_PER_TICK;
  286. tick(game);
  287. }
  288. glfwPollEvents();
  289. }
  290. }
  291. void GameClient::start(int w, int h, const char* windowName)
  292. {
  293. if(initGLFW() || initWindow(w, h, windowName) || initGLEW())
  294. {
  295. return;
  296. }
  297. initCallbacks();
  298. glfwShowWindow(client.window);
  299. loop();
  300. }