GameClient.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. #include <iostream>
  2. #include <GL/glew.h>
  3. #include <GLFW/glfw3.h>
  4. #include <unordered_map>
  5. #include <cmath>
  6. #include <cstring>
  7. #include <array>
  8. #include "common/utils/Types.h"
  9. #include "client/GameClient.h"
  10. #include "client/input/Keys.h"
  11. #include "client/input/MouseButtons.h"
  12. #include "client/rendering/Shader.h"
  13. #include "client/rendering/FontRenderer.h"
  14. #include "client/rendering/Mesh.h"
  15. #include "client/rendering/Framebuffer.h"
  16. #include "client/Utils.h"
  17. #include "client/math/Camera.h"
  18. #include "client/math/Matrix.h"
  19. #include "client/math/MatrixStack.h"
  20. #include "client/Game.h"
  21. #include "rendering/NoiseTexture.h"
  22. struct InternGameClient
  23. {
  24. ~InternGameClient()
  25. {
  26. if(window != nullptr)
  27. {
  28. glfwDestroyWindow(window);
  29. }
  30. if(glfwInitDone)
  31. {
  32. glfwTerminate();
  33. }
  34. }
  35. bool glfwInitDone = false;
  36. GLFWwindow* window = nullptr;
  37. };
  38. static const u64 NANOS_PER_TICK = 50000000;
  39. static const float lagFactor = 1.0f / NANOS_PER_TICK;
  40. static u64 timeFactor = 1;
  41. static int width = 0;
  42. static int height = 0;
  43. static bool resize = false;
  44. static float fovY = 60.0f;
  45. static float nearClip = 0.1f;
  46. static float farClip = 1000.0f;
  47. static InternGameClient client;
  48. static Keys keys;
  49. static MouseButtons mButtons;
  50. static bool useSSAO = false;
  51. struct Shaders
  52. {
  53. Shaders() :
  54. world("resources/shader/worldVertex.vs", "resources/shader/worldFragment.fs"),
  55. ssao("resources/shader/ssaoVertex.vs", "resources/shader/ssaoFragment.fs"),
  56. ssaoBlur("resources/shader/ssaoBlurVertex.vs", "resources/shader/ssaoBlurFragment.fs"),
  57. shadow("resources/shader/worldShadowVertex.vs", "resources/shader/worldShadowFragment.fs"),
  58. postWorld("resources/shader/worldPostVertex.vs", "resources/shader/worldPostFragment.fs"),
  59. text("resources/shader/textVertex.vs", "resources/shader/textFragment.fs")
  60. {
  61. worldProj.set(11, -1.0f);
  62. worldProj.set(14, 1.0f);
  63. worldProj.set(15, 0.0f);
  64. worldShadowProj.set(11, -1.0f);
  65. worldShadowProj.set(14, 1.0f);
  66. worldShadowProj.set(15, 0.0f);
  67. }
  68. Shader world;
  69. Shader ssao;
  70. Shader ssaoBlur;
  71. Shader shadow;
  72. Shader postWorld;
  73. Shader text;
  74. bool once = true;
  75. Matrix worldProj;
  76. Matrix worldView;
  77. Matrix worldShadowProj;
  78. Matrix worldShadowView;
  79. Matrix worldShadowProjView;
  80. bool isValid() const
  81. {
  82. return world.isValid() && ssao.isValid() && ssaoBlur.isValid() &&
  83. shadow.isValid() && postWorld.isValid() && text.isValid();
  84. }
  85. void updateWorldProjection()
  86. {
  87. float tan = tanf((0.5f * fovY) * M_PI / 180.0f);
  88. float q = 1.0f / tan;
  89. float aspect = (float) width / height;
  90. worldProj.set(0, q / aspect);
  91. worldProj.set(5, q);
  92. worldProj.set(10,(nearClip + farClip) / (nearClip - farClip));
  93. worldProj.set(14, (2.0f * nearClip * farClip) / (nearClip - farClip));
  94. if(once)
  95. {
  96. worldShadowProj.set(0, q / aspect);
  97. worldShadowProj.set(5, q);
  98. worldShadowProj.set(10,(nearClip + farClip) / (nearClip - farClip));
  99. worldShadowProj.set(14, (2.0f * nearClip * farClip) / (nearClip - farClip));
  100. }
  101. }
  102. void updateWorldView(float lag, Camera& cam)
  103. {
  104. cam.update(lag);
  105. const Vector right = cam.getRight();
  106. const Vector up = cam.getUp();
  107. const Vector back = cam.getBack();
  108. const Vector pos = cam.getPosition();
  109. worldView.set(0, right.getX());
  110. worldView.set(1, up.getX());
  111. worldView.set(2, back.getX());
  112. worldView.set(4, right.getY());
  113. worldView.set(5, up.getY());
  114. worldView.set(6, back.getY());
  115. worldView.set(8, right.getZ());
  116. worldView.set(9, up.getZ());
  117. worldView.set(10, back.getZ());
  118. worldView.set(12, right.dotInverse(pos));
  119. worldView.set(13, up.dotInverse(pos));
  120. worldView.set(14, back.dotInverse(pos));
  121. if(once)
  122. {
  123. once = false;
  124. worldShadowView.set(0, right.getX());
  125. worldShadowView.set(1, up.getX());
  126. worldShadowView.set(2, back.getX());
  127. worldShadowView.set(4, right.getY());
  128. worldShadowView.set(5, up.getY());
  129. worldShadowView.set(6, back.getY());
  130. worldShadowView.set(8, right.getZ());
  131. worldShadowView.set(9, up.getZ());
  132. worldShadowView.set(10, back.getZ());
  133. worldShadowView.set(12, right.dotInverse(pos));
  134. worldShadowView.set(13, up.dotInverse(pos));
  135. worldShadowView.set(14, back.dotInverse(pos));
  136. }
  137. }
  138. };
  139. struct Framebuffers
  140. {
  141. Framebuffers(u32 w, u32 h) :
  142. world(w, h, Framebuffer::POSITION | Framebuffer::NORMAL | Framebuffer::COLOR | Framebuffer::RED | Framebuffer::DEPTH24_STENCIL8, 0),
  143. ssao(w, h, Framebuffer::RED, 0),
  144. ssaoBlur(w, h, Framebuffer::RED, 0),
  145. shadow(w, h, Framebuffer::DEPTH24_STENCIL8, GL_LEQUAL)
  146. {
  147. }
  148. void resize(u32 w, u32 h) const
  149. {
  150. world.resize(w, h);
  151. ssao.resize(w, h);
  152. ssaoBlur.resize(w, h);
  153. shadow.resize(w, h);
  154. }
  155. bool isValid() const
  156. {
  157. return world.isValid() && ssao.isValid() && ssaoBlur.isValid();
  158. }
  159. Framebuffer world;
  160. Framebuffer ssao;
  161. Framebuffer ssaoBlur;
  162. Framebuffer shadow;
  163. };
  164. struct InternGame
  165. {
  166. InternGame() : ssaoNoise(4, 4)
  167. {
  168. rectangle.add({-1, -1, 0, 0, 0, 0, 0, 0});
  169. rectangle.add({ 1, 1, 0, 1, 1, 0, 0, 0});
  170. rectangle.add({-1, 1, 0, 0, 1, 0, 0, 0});
  171. rectangle.add({-1, -1, 0, 0, 0, 0, 0, 0});
  172. rectangle.add({ 1, -1, 0, 1, 0, 0, 0, 0});
  173. rectangle.add({ 1, 1, 0, 1, 1, 0, 0, 0});
  174. rectangle.build();
  175. }
  176. Game game;
  177. Camera cam;
  178. MatrixStack model;
  179. FontRenderer fontRenderer;
  180. NoiseTexture ssaoNoise;
  181. Mesh rectangle;
  182. };
  183. static u64 getTimeNanos()
  184. {
  185. return glfwGetTimerValue() * timeFactor;
  186. }
  187. static bool initGLFW()
  188. {
  189. client.glfwInitDone = glfwInit();
  190. if(!client.glfwInitDone)
  191. {
  192. std::cout << "could not initialize GLFW\n";
  193. return true;
  194. }
  195. timeFactor = 1000000000 / glfwGetTimerFrequency();
  196. return false;
  197. }
  198. static bool initWindow(int w, int h, const char* windowName)
  199. {
  200. width = w;
  201. height = h;
  202. glfwDefaultWindowHints();
  203. glfwWindowHint(GLFW_VISIBLE, 0);
  204. glfwWindowHint(GLFW_RESIZABLE, 1);
  205. glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
  206. glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
  207. glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
  208. client.window = glfwCreateWindow(width, height, windowName, nullptr, nullptr);
  209. if(client.window == nullptr)
  210. {
  211. std::cout << "could not create window\n";
  212. return true;
  213. }
  214. glfwMakeContextCurrent(client.window);
  215. glfwSwapInterval(1);
  216. return false;
  217. }
  218. static bool initGLEW()
  219. {
  220. GLenum err = glewInit();
  221. if(err != GLEW_OK)
  222. {
  223. std::cout << "could not initialize GLEW: " << glewGetErrorString(err) << "\n";
  224. return true;
  225. }
  226. std::cout << "using GLEW " << glewGetString(GLEW_VERSION) << "\n";
  227. return false;
  228. }
  229. static void initCallbacks()
  230. {
  231. // GLFWwindow* w, int key, int scancode, int action, int mod
  232. glfwSetKeyCallback(client.window, [](GLFWwindow*, int key, int, int action, int)
  233. {
  234. if(action == GLFW_PRESS)
  235. {
  236. keys.press(key);
  237. }
  238. else if(action == GLFW_RELEASE)
  239. {
  240. keys.release(key);
  241. }
  242. });
  243. // GLFWwindow* w, int button, int action, int mods
  244. glfwSetMouseButtonCallback(client.window, [](GLFWwindow*, int button, int action, int)
  245. {
  246. if(action == GLFW_PRESS)
  247. {
  248. mButtons.press(button);
  249. }
  250. else if(action == GLFW_RELEASE)
  251. {
  252. mButtons.release(button);
  253. }
  254. });
  255. // GLFWwindow* w, double xpos, double ypos
  256. glfwSetCursorPosCallback(client.window, [](GLFWwindow*, double x, double y)
  257. {
  258. mButtons.move(x, y);
  259. });
  260. // GLFWwindow* w, int width, int height
  261. glfwSetFramebufferSizeCallback(client.window, [](GLFWwindow*, int w, int h)
  262. {
  263. glViewport(0, 0, w, h);
  264. width = w;
  265. height = h;
  266. resize = true;
  267. });
  268. }
  269. static void tick(InternGame& game)
  270. {
  271. keys.tick();
  272. mButtons.tick();
  273. game.game.tick(keys, mButtons, game.cam);
  274. if(keys.test.getDownTime() == 1)
  275. {
  276. useSSAO = !useSSAO;
  277. }
  278. mButtons.postTick();
  279. }
  280. static void renderShadow(float lag, Shaders& shaders, InternGame& game, Framebuffers& fb)
  281. {
  282. fb.shadow.bind();
  283. glEnable(GL_DEPTH_TEST);
  284. shaders.shadow.use();
  285. shaders.worldShadowProjView = shaders.worldShadowProj;
  286. shaders.worldShadowProjView.mul(shaders.worldShadowView);
  287. shaders.shadow.setMatrix("projView", shaders.worldShadowProjView.getValues());
  288. glEnable(GL_POLYGON_OFFSET_FILL);
  289. glPolygonOffset(2.0f, 4.0f);
  290. game.game.renderWorld(lag, game.model, shaders.shadow);
  291. glDisable(GL_POLYGON_OFFSET_FILL);
  292. }
  293. static void renderWorld(float lag, Shaders& shaders, InternGame& game, Framebuffers& fb)
  294. {
  295. fb.world.bind();
  296. glEnable(GL_DEPTH_TEST);
  297. shaders.world.use();
  298. Matrix rWorldShadowProjView;
  299. rWorldShadowProjView.translate(0.5f, 0.5f, 0.5f);
  300. rWorldShadowProjView.scale(0.5f, 0.5f, 0.5f);
  301. rWorldShadowProjView.mul(shaders.worldShadowProjView);
  302. shaders.world.setMatrix("projViewShadow", rWorldShadowProjView.getValues());
  303. shaders.world.setMatrix("proj", shaders.worldProj.getValues());
  304. shaders.world.setMatrix("view", shaders.worldView.getValues());
  305. game.model.clear();
  306. shaders.world.setMatrix("model", game.model.get().getValues());
  307. fb.shadow.bindDepthTexture(1);
  308. game.game.renderWorld(lag, game.model, shaders.world);
  309. }
  310. static void renderSSAO(Shaders& shaders, InternGame& game, Framebuffers& fb)
  311. {
  312. // ssao
  313. shaders.ssao.use();
  314. Matrix rProj;
  315. rProj.translate(0.5f, 0.5f, 0.5f);
  316. rProj.scale(0.5f, 0.5f, 0.5f);
  317. rProj.mul(shaders.worldProj);
  318. shaders.ssao.setMatrix("proj", rProj.getValues());
  319. shaders.ssao.setInt("width", width);
  320. shaders.ssao.setInt("height", height);
  321. fb.world.bindPositionTexture(0);
  322. fb.world.bindNormalTexture(1);
  323. fb.world.bindColorTexture(2);
  324. fb.world.bindDepthTexture(3);
  325. game.ssaoNoise.bind(4);
  326. fb.ssao.bind();
  327. game.rectangle.draw();
  328. // ssao blur
  329. shaders.ssaoBlur.use();
  330. fb.ssao.bindRedTexture(0);
  331. fb.ssaoBlur.bind();
  332. game.rectangle.draw();
  333. }
  334. static void renderPostWorld(Shaders& shaders, InternGame& game, Framebuffers& fb)
  335. {
  336. glBindFramebuffer(GL_FRAMEBUFFER, 0);
  337. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
  338. shaders.postWorld.use();
  339. fb.world.bindColorTexture(0);
  340. fb.ssaoBlur.bindRedTexture(1);
  341. fb.world.bindRedTexture(2);
  342. shaders.postWorld.setInt("useSSAO", useSSAO);
  343. game.rectangle.draw();
  344. }
  345. static u64 last = 0;
  346. static u64 sum = 0;
  347. static u64 tindex = 0;
  348. static std::array<u64, 128> values;
  349. static void renderTextOverlay(float lag, Shaders& shaders, InternGame& game)
  350. {
  351. glDisable(GL_DEPTH_TEST);
  352. shaders.text.use();
  353. Matrix m;
  354. shaders.text.setMatrix("proj", m.getValues());
  355. m.translate(-1.0f, 1.0f, 0.0f);
  356. m.scale(2.0f / width, -2.0f / height, 1.0f);
  357. shaders.text.setMatrix("view", m.getValues());
  358. game.model.clear();
  359. game.model.get().scale(2.0f, 2.0f, 2.0f);
  360. shaders.text.setMatrix("model", game.model.get().getValues());
  361. glEnable(GL_BLEND);
  362. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  363. glBlendEquation(GL_FUNC_ADD);
  364. char buffer[50];
  365. snprintf(buffer, 50, "FPS: %f", 128000000000.0f / sum);
  366. game.fontRenderer.drawString(20, 20, buffer);
  367. game.game.renderTextOverlay(lag, game.model, shaders.text, game.fontRenderer);
  368. glDisable(GL_BLEND);
  369. }
  370. static void renderTick(float lag, Shaders& shaders, InternGame& game, Framebuffers& fb)
  371. {
  372. u64 current = getTimeNanos();
  373. sum -= values[tindex];
  374. values[tindex] = current - last;
  375. sum += values[tindex];
  376. last = current;
  377. tindex = (tindex + 1) & (127);
  378. if(resize)
  379. {
  380. fb.resize(width, height);
  381. resize = false;
  382. }
  383. shaders.updateWorldProjection();
  384. shaders.updateWorldView(lag, game.cam);
  385. renderShadow(lag, shaders, game, fb);
  386. renderWorld(lag, shaders, game, fb);
  387. if(useSSAO)
  388. {
  389. renderSSAO(shaders, game, fb);
  390. }
  391. renderPostWorld(shaders, game, fb);
  392. renderTextOverlay(lag, shaders, game);
  393. }
  394. static void loop()
  395. {
  396. Shaders shaders;
  397. if(!shaders.isValid())
  398. {
  399. return;
  400. }
  401. Framebuffers fb(width, height);
  402. if(!fb.isValid())
  403. {
  404. return;
  405. }
  406. InternGame game;
  407. last = getTimeNanos();
  408. u64 lastTime = getTimeNanos();
  409. u64 lag = 0;
  410. while(!glfwWindowShouldClose(client.window))
  411. {
  412. renderTick(lag * lagFactor, shaders, game, fb);
  413. glfwSwapBuffers(client.window);
  414. u64 newTime = getTimeNanos();
  415. lag += newTime - lastTime;
  416. lastTime = newTime;
  417. while(lag >= NANOS_PER_TICK)
  418. {
  419. lag -= NANOS_PER_TICK;
  420. tick(game);
  421. }
  422. glfwPollEvents();
  423. }
  424. }
  425. void GameClient::start(int w, int h, const char* windowName)
  426. {
  427. if(initGLFW() || initWindow(w, h, windowName) || initGLEW())
  428. {
  429. return;
  430. }
  431. initCallbacks();
  432. glfwShowWindow(client.window);
  433. loop();
  434. }