GameClient.cpp 18 KB

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