GameClient.cpp 14 KB

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