GameEngine.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. #include "GameEngine.h"
  2. #include "Key.h"
  3. #include "Mouse.h"
  4. #include <cmath>
  5. Clock GameEngine::tps;
  6. Clock GameEngine::fps;
  7. InitFunction GameEngine::init = nullptr;
  8. TickFunction GameEngine::tick = nullptr;
  9. RenderTickFunction GameEngine::renderTick = nullptr;
  10. GLuint GameEngine::vShader = 0;
  11. GLuint GameEngine::fShader = 0;
  12. GLFWwindow* GameEngine::window = nullptr;
  13. GLuint GameEngine::program = 0;
  14. int GameEngine::scale = 1;
  15. int GameEngine::width = 0;
  16. int GameEngine::height = 0;
  17. float GameEngine::fovY = 60;
  18. float GameEngine::nearClip = 0.1f;
  19. float GameEngine::farClip = 1000.0f;
  20. Matrix3D GameEngine::proj;
  21. Camera GameEngine::camera;
  22. DirectRenderer GameEngine::directRenderer;
  23. GLint GameEngine::unifProjMatrix = 0;
  24. GLint GameEngine::unifViewMatrix = 0;
  25. GLint GameEngine::unifUseTexture = 0;
  26. GLint GameEngine::unifUseColor = 0;
  27. GLint GameEngine::unifUseMixColor = 0;
  28. GLint GameEngine::unifMixColorLoc = 0;
  29. Camera& GameEngine::getCamera()
  30. {
  31. return camera;
  32. }
  33. DirectRenderer& GameEngine::getDirectRenderer()
  34. {
  35. return directRenderer;
  36. }
  37. void GameEngine::onKeyEvent(GLFWwindow* w, int key, int scancode, int action, int mods)
  38. {
  39. if(action == GLFW_RELEASE)
  40. {
  41. if(key == GLFW_KEY_ESCAPE)
  42. {
  43. //activeFocus = 0;
  44. glfwSetInputMode(w, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
  45. }
  46. Key::release(key);
  47. }
  48. else if(action == GLFW_PRESS)
  49. {
  50. Key::press(key);
  51. }
  52. }
  53. void GameEngine::onMouseClick(GLFWwindow* w, int button, int action, int mods)
  54. {
  55. if(action == GLFW_PRESS)
  56. {
  57. /*if(!activeFocus)
  58. {
  59. glfwSetInputMode(w, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
  60. oldMouseX = 0;
  61. oldMouseY = 0;
  62. activeFocus = 1;
  63. }
  64. else
  65. {*/
  66. Mouse::press(button);
  67. //}
  68. }
  69. else if(action == GLFW_RELEASE)
  70. {
  71. Mouse::release(button);
  72. }
  73. }
  74. /*static void onMouseMove(GLFWwindow* w, double x, double y)
  75. {
  76. if(activeFocus)
  77. {
  78. if(oldMouseX == 0 && oldMouseY == 0)
  79. {
  80. oldMouseX = x;
  81. oldMouseY = y;
  82. }
  83. else
  84. {
  85. mouseMove(x - oldMouseX, y - oldMouseY);
  86. oldMouseX = x;
  87. oldMouseY = y;
  88. }
  89. }
  90. }*/
  91. void GameEngine::onWindowResize(GLFWwindow* w, int width, int height)
  92. {
  93. glViewport(0, 0, width, height);
  94. GameEngine::width = width;
  95. GameEngine::height = height;
  96. updateScale();
  97. }
  98. GLchar* GameEngine::readFile(const char* name)
  99. {
  100. ifstream in;
  101. in.open(name);
  102. if(!in.fail())
  103. {
  104. int size = 128;
  105. int index = 0;
  106. GLchar* content = new GLchar[size];
  107. while(true)
  108. {
  109. GLchar c = in.get();
  110. if(in.eof())
  111. {
  112. break;
  113. }
  114. if(index >= size - 1)
  115. {
  116. GLchar* newContent = new GLchar[size * 2];
  117. memcpy(newContent, content, size);
  118. size *= 2;
  119. delete[] content;
  120. content = newContent;
  121. }
  122. content[index] = c;
  123. index++;
  124. }
  125. content[index] = '\0';
  126. index++;
  127. in.close();
  128. return content;
  129. }
  130. return nullptr;
  131. }
  132. bool GameEngine::checkShaderErrors(const char* name, GLuint shader)
  133. {
  134. bool returnValue = false;
  135. cout << "compiling " << name << " shader ..." << endl;
  136. GLenum error = glGetError();
  137. if(error)
  138. {
  139. cout << "error: " << glGetError() << endl;
  140. returnValue = true;
  141. }
  142. else
  143. {
  144. cout << "no error occured ..." << endl;
  145. }
  146. GLint compiled[1];
  147. glGetShaderiv(shader, GL_COMPILE_STATUS, compiled);
  148. if(compiled[0])
  149. {
  150. cout << name << " shader successfully compiled" << endl;
  151. }
  152. else
  153. {
  154. cout << name << "compiling of " << name << " failed:" << endl;
  155. GLchar buffer[512];
  156. GLsizei bufferSize = 512;
  157. GLsizei charsUsed = 0;
  158. glGetShaderInfoLog(shader, bufferSize, &charsUsed, buffer);
  159. // glGetProgramInfoLog should be null terminated ...
  160. buffer[bufferSize - 1] = '\0';
  161. cout << buffer << endl;
  162. returnValue = true;
  163. }
  164. return returnValue;
  165. }
  166. GLuint GameEngine::compileProgram(const GLchar* vertex, const GLchar* fragment)
  167. {
  168. vShader = glCreateShader(GL_VERTEX_SHADER);
  169. glShaderSource(vShader, 1, &vertex, nullptr);
  170. glCompileShader(vShader);
  171. if(checkShaderErrors("vertex", vShader))
  172. {
  173. return 0;
  174. }
  175. fShader = glCreateShader(GL_FRAGMENT_SHADER);
  176. glShaderSource(fShader, 1, &fragment, nullptr);
  177. glCompileShader(fShader);
  178. if(checkShaderErrors("fragment", fShader))
  179. {
  180. return 0;
  181. }
  182. GLuint program = glCreateProgram();
  183. glAttachShader(program, vShader);
  184. glAttachShader(program, fShader);
  185. glLinkProgram(program);
  186. cout << "linking shaders to program ..." << endl;
  187. GLenum error = glGetError();
  188. if(error)
  189. {
  190. cout << "error: " << glGetError() << endl;
  191. return 0;
  192. }
  193. else
  194. {
  195. cout << "no error occured ..." << endl;
  196. }
  197. GLint compiled[1];
  198. glGetProgramiv(program, GL_LINK_STATUS, compiled);
  199. if(compiled[0])
  200. {
  201. cout << "shaders successfully linked" << endl;
  202. }
  203. else
  204. {
  205. cout << "linking of shaders failed:" << endl;
  206. GLchar buffer[512];
  207. GLsizei bufferSize = 512;
  208. GLsizei charsUsed = 0;
  209. glGetProgramInfoLog(program, bufferSize, &charsUsed, buffer);
  210. // glGetProgramInfoLog should be null terminated ...
  211. buffer[bufferSize - 1] = '\0';
  212. cout << buffer << endl;
  213. return 0;
  214. }
  215. glUseProgram(program);
  216. return program;
  217. }
  218. GLuint GameEngine::createProgram()
  219. {
  220. GLchar* vertex = readFile("shader/vertex.vs");
  221. if(vertex == nullptr)
  222. {
  223. cout << "cannot read vertex.vs" << endl;
  224. return 0;
  225. }
  226. GLchar* fragment = readFile("shader/fragment.fs");
  227. if(fragment == nullptr)
  228. {
  229. cout << "cannot read fragment.fs" << endl;
  230. delete[] vertex;
  231. return 0;
  232. }
  233. GLuint program = compileProgram(vertex, fragment);
  234. delete[] vertex;
  235. delete[] fragment;
  236. return program;
  237. }
  238. void GameEngine::updateProjection()
  239. {
  240. float q = 1.0f / tanf((0.5f * fovY) * M_PI / 180.0f);
  241. proj.set(0, 0, (q * height) / width);
  242. proj.set(1, 1, q);
  243. proj.set(2, 2, (nearClip + farClip) / (nearClip - farClip));
  244. proj.set(3, 2, -1.0f);
  245. proj.set(2, 3, (2.0f * nearClip * farClip) / (nearClip - farClip));
  246. proj.set(3, 3, 0);
  247. glUniformMatrix4fv(unifProjMatrix, 1, 0, proj.getValues());
  248. }
  249. void GameEngine::updateView(Matrix3D& m)
  250. {
  251. glUniformMatrix4fv(unifViewMatrix, 1, 0, m.getValues());
  252. }
  253. void GameEngine::onInit()
  254. {
  255. Key::init();
  256. Mouse::init();
  257. init();
  258. unifProjMatrix = glGetUniformLocation(program, "projMatrix");
  259. unifViewMatrix = glGetUniformLocation(program, "viewMatrix");
  260. unifUseTexture = glGetUniformLocation(program, "useTexture");
  261. unifUseColor = glGetUniformLocation(program, "useColor");
  262. unifUseMixColor = glGetUniformLocation(program, "useMixColor");
  263. unifMixColorLoc = glGetUniformLocation(program, "mixColor");
  264. directRenderer.init();
  265. }
  266. void GameEngine::onTick()
  267. {
  268. tick();
  269. Key::tick();
  270. Mouse::tick();
  271. }
  272. void GameEngine::start(int width, int height, const char* name, InitFunction init, TickFunction tick, RenderTickFunction renderTick)
  273. {
  274. GameEngine::width = width;
  275. GameEngine::height = height;
  276. GameEngine::init = init;
  277. GameEngine::tick = tick;
  278. GameEngine::renderTick = renderTick;
  279. updateScale();
  280. if(!glfwInit())
  281. {
  282. cout << "could not initialize GLFW" << endl;
  283. return;
  284. }
  285. glfwDefaultWindowHints();
  286. glfwWindowHint(GLFW_VISIBLE, 0);
  287. glfwWindowHint(GLFW_RESIZABLE, 1);
  288. window = glfwCreateWindow(width, height, name, nullptr, nullptr);
  289. if(!window)
  290. {
  291. cout << "could not create window" << endl;
  292. glfwTerminate();
  293. return;
  294. }
  295. glfwSetKeyCallback(window, onKeyEvent);
  296. glfwSetFramebufferSizeCallback(window, onWindowResize);
  297. glfwSetMouseButtonCallback(window, onMouseClick);
  298. //glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
  299. //glfwSetCursorPosCallback(window, onMouseMove);
  300. glfwMakeContextCurrent(window);
  301. glfwSwapInterval(1);
  302. glfwShowWindow(window);
  303. GLenum err = glewInit();
  304. if(GLEW_OK != err)
  305. {
  306. cout << "could not initialize GLEW: " << glewGetErrorString(err) << endl;
  307. return;
  308. }
  309. cout << "Status: Using GLEW " << glewGetString(GLEW_VERSION) << endl;
  310. program = createProgram();
  311. if(program == 0)
  312. {
  313. onTerm();
  314. return;
  315. }
  316. glEnable(GL_CULL_FACE);
  317. glEnable(GL_DEPTH_TEST);
  318. glDepthFunc(GL_LEQUAL);
  319. onInit();
  320. uint64_t newTime = glfwGetTimerValue();
  321. uint64_t oldTime = newTime;
  322. uint64_t lag = 0;
  323. while(!glfwWindowShouldClose(window))
  324. {
  325. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  326. oldTime = newTime;
  327. newTime = glfwGetTimerValue();
  328. lag += newTime - oldTime;
  329. int ticksPerFrame = 0;
  330. while(lag >= NANOS_PER_TICK)
  331. {
  332. lag -= NANOS_PER_TICK;
  333. tps.update();
  334. onTick();
  335. ticksPerFrame++;
  336. if(ticksPerFrame >= MAX_TICKS_PER_FRAME)
  337. {
  338. long skip = lag / NANOS_PER_TICK;
  339. lag -= skip * NANOS_PER_TICK;
  340. if(skip > 0)
  341. {
  342. cout << "skipped " << skip << " game ticks " << lag << endl;
  343. }
  344. break;
  345. }
  346. }
  347. updateProjection();
  348. fps.update();
  349. renderTick((float) lag / NANOS_PER_TICK);
  350. glfwSwapBuffers(window);
  351. glfwPollEvents();
  352. }
  353. onTerm();
  354. }
  355. void GameEngine::onTerm()
  356. {
  357. if(vShader != 0)
  358. {
  359. glDeleteShader(vShader);
  360. }
  361. if(fShader != 0)
  362. {
  363. glDeleteShader(fShader);
  364. }
  365. if(program != 0)
  366. {
  367. glDeleteProgram(program);
  368. }
  369. glfwDestroyWindow(window);
  370. glfwTerminate();
  371. }
  372. void GameEngine::setTextureEnabled(bool use)
  373. {
  374. glUniform1i(unifUseTexture, use);
  375. }
  376. void GameEngine::setColorEnabled(bool use)
  377. {
  378. glUniform1i(unifUseColor, use);
  379. }
  380. void GameEngine::setMixColorEnabled(bool use)
  381. {
  382. glUniform1i(unifUseMixColor, use);
  383. }
  384. void GameEngine::setMixColor(float r, float g, float b, float a)
  385. {
  386. glUniform4f(unifMixColorLoc, r, g, b, a);
  387. }
  388. void GameEngine::setUseBlending(bool use)
  389. {
  390. if(use)
  391. {
  392. glEnable(GL_BLEND);
  393. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  394. glBlendEquation(GL_FUNC_ADD);
  395. }
  396. else
  397. {
  398. glDisable(GL_BLEND);
  399. }
  400. }
  401. void GameEngine::printError()
  402. {
  403. GLenum error = glGetError();
  404. switch(error)
  405. {
  406. case GL_NO_ERROR:
  407. cout << "> No error has been recorded." << endl;
  408. break;
  409. case GL_INVALID_ENUM:
  410. cout << "> An unacceptable value is specified for an enumerated argument." << endl;
  411. break;
  412. case GL_INVALID_VALUE:
  413. cout << "> A numeric argument is out of range." << endl;
  414. break;
  415. case GL_INVALID_OPERATION:
  416. cout << "> The specified operation is not allowed in the current state." << endl;
  417. break;
  418. case GL_INVALID_FRAMEBUFFER_OPERATION:
  419. cout << "> The framebuffer object is not complete." << endl;
  420. break;
  421. case GL_OUT_OF_MEMORY:
  422. cout << "> There is not enough memory left to execute the command." << endl;
  423. break;
  424. case GL_STACK_UNDERFLOW:
  425. cout << "> An attempt has been made to perform an operation that would cause an internal stack to underflow." << endl;
  426. break;
  427. case GL_STACK_OVERFLOW:
  428. cout << "> An attempt has been made to perform an operation that would cause an internal stack to overflow." << endl;
  429. break;
  430. default:
  431. cout << "> Unknown OpenGL error: " << error << endl;
  432. }
  433. }
  434. void GameEngine::stop()
  435. {
  436. glfwSetWindowShouldClose(window, 1);
  437. }
  438. void GameEngine::updateScale()
  439. {
  440. scale = 1;
  441. while(width / (scale + 1) >= 400 && height / (scale + 1) >= 300)
  442. {
  443. scale++;
  444. }
  445. }
  446. double GameEngine::getTicksPerSecond()
  447. {
  448. return tps.getUpdatesPerSecond();
  449. }
  450. double GameEngine::getFramesPerSecond()
  451. {
  452. return fps.getUpdatesPerSecond();
  453. }