Platformer in OpenGL
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

511 lines
16 KiB

5 years ago
  1. //========================================================================
  2. // Heightmap example program using OpenGL 3 core profile
  3. // Copyright (c) 2010 Olivier Delannoy
  4. //
  5. // This software is provided 'as-is', without any express or implied
  6. // warranty. In no event will the authors be held liable for any damages
  7. // arising from the use of this software.
  8. //
  9. // Permission is granted to anyone to use this software for any purpose,
  10. // including commercial applications, and to alter it and redistribute it
  11. // freely, subject to the following restrictions:
  12. //
  13. // 1. The origin of this software must not be misrepresented; you must not
  14. // claim that you wrote the original software. If you use this software
  15. // in a product, an acknowledgment in the product documentation would
  16. // be appreciated but is not required.
  17. //
  18. // 2. Altered source versions must be plainly marked as such, and must not
  19. // be misrepresented as being the original software.
  20. //
  21. // 3. This notice may not be removed or altered from any source
  22. // distribution.
  23. //
  24. //========================================================================
  25. #include <stdlib.h>
  26. #include <stdio.h>
  27. #include <math.h>
  28. #include <assert.h>
  29. #include <stddef.h>
  30. #include <glad/glad.h>
  31. #include <GLFW/glfw3.h>
  32. /* Map height updates */
  33. #define MAX_CIRCLE_SIZE (5.0f)
  34. #define MAX_DISPLACEMENT (1.0f)
  35. #define DISPLACEMENT_SIGN_LIMIT (0.3f)
  36. #define MAX_ITER (200)
  37. #define NUM_ITER_AT_A_TIME (1)
  38. /* Map general information */
  39. #define MAP_SIZE (10.0f)
  40. #define MAP_NUM_VERTICES (80)
  41. #define MAP_NUM_TOTAL_VERTICES (MAP_NUM_VERTICES*MAP_NUM_VERTICES)
  42. #define MAP_NUM_LINES (3* (MAP_NUM_VERTICES - 1) * (MAP_NUM_VERTICES - 1) + \
  43. 2 * (MAP_NUM_VERTICES - 1))
  44. /**********************************************************************
  45. * Default shader programs
  46. *********************************************************************/
  47. static const char* vertex_shader_text =
  48. "#version 150\n"
  49. "uniform mat4 project;\n"
  50. "uniform mat4 modelview;\n"
  51. "in float x;\n"
  52. "in float y;\n"
  53. "in float z;\n"
  54. "\n"
  55. "void main()\n"
  56. "{\n"
  57. " gl_Position = project * modelview * vec4(x, y, z, 1.0);\n"
  58. "}\n";
  59. static const char* fragment_shader_text =
  60. "#version 150\n"
  61. "out vec4 color;\n"
  62. "void main()\n"
  63. "{\n"
  64. " color = vec4(0.2, 1.0, 0.2, 1.0); \n"
  65. "}\n";
  66. /**********************************************************************
  67. * Values for shader uniforms
  68. *********************************************************************/
  69. /* Frustum configuration */
  70. static GLfloat view_angle = 45.0f;
  71. static GLfloat aspect_ratio = 4.0f/3.0f;
  72. static GLfloat z_near = 1.0f;
  73. static GLfloat z_far = 100.f;
  74. /* Projection matrix */
  75. static GLfloat projection_matrix[16] = {
  76. 1.0f, 0.0f, 0.0f, 0.0f,
  77. 0.0f, 1.0f, 0.0f, 0.0f,
  78. 0.0f, 0.0f, 1.0f, 0.0f,
  79. 0.0f, 0.0f, 0.0f, 1.0f
  80. };
  81. /* Model view matrix */
  82. static GLfloat modelview_matrix[16] = {
  83. 1.0f, 0.0f, 0.0f, 0.0f,
  84. 0.0f, 1.0f, 0.0f, 0.0f,
  85. 0.0f, 0.0f, 1.0f, 0.0f,
  86. 0.0f, 0.0f, 0.0f, 1.0f
  87. };
  88. /**********************************************************************
  89. * Heightmap vertex and index data
  90. *********************************************************************/
  91. static GLfloat map_vertices[3][MAP_NUM_TOTAL_VERTICES];
  92. static GLuint map_line_indices[2*MAP_NUM_LINES];
  93. /* Store uniform location for the shaders
  94. * Those values are setup as part of the process of creating
  95. * the shader program. They should not be used before creating
  96. * the program.
  97. */
  98. static GLuint mesh;
  99. static GLuint mesh_vbo[4];
  100. /**********************************************************************
  101. * OpenGL helper functions
  102. *********************************************************************/
  103. /* Creates a shader object of the specified type using the specified text
  104. */
  105. static GLuint make_shader(GLenum type, const char* text)
  106. {
  107. GLuint shader;
  108. GLint shader_ok;
  109. GLsizei log_length;
  110. char info_log[8192];
  111. shader = glCreateShader(type);
  112. if (shader != 0)
  113. {
  114. glShaderSource(shader, 1, (const GLchar**)&text, NULL);
  115. glCompileShader(shader);
  116. glGetShaderiv(shader, GL_COMPILE_STATUS, &shader_ok);
  117. if (shader_ok != GL_TRUE)
  118. {
  119. fprintf(stderr, "ERROR: Failed to compile %s shader\n", (type == GL_FRAGMENT_SHADER) ? "fragment" : "vertex" );
  120. glGetShaderInfoLog(shader, 8192, &log_length,info_log);
  121. fprintf(stderr, "ERROR: \n%s\n\n", info_log);
  122. glDeleteShader(shader);
  123. shader = 0;
  124. }
  125. }
  126. return shader;
  127. }
  128. /* Creates a program object using the specified vertex and fragment text
  129. */
  130. static GLuint make_shader_program(const char* vs_text, const char* fs_text)
  131. {
  132. GLuint program = 0u;
  133. GLint program_ok;
  134. GLuint vertex_shader = 0u;
  135. GLuint fragment_shader = 0u;
  136. GLsizei log_length;
  137. char info_log[8192];
  138. vertex_shader = make_shader(GL_VERTEX_SHADER, vs_text);
  139. if (vertex_shader != 0u)
  140. {
  141. fragment_shader = make_shader(GL_FRAGMENT_SHADER, fs_text);
  142. if (fragment_shader != 0u)
  143. {
  144. /* make the program that connect the two shader and link it */
  145. program = glCreateProgram();
  146. if (program != 0u)
  147. {
  148. /* attach both shader and link */
  149. glAttachShader(program, vertex_shader);
  150. glAttachShader(program, fragment_shader);
  151. glLinkProgram(program);
  152. glGetProgramiv(program, GL_LINK_STATUS, &program_ok);
  153. if (program_ok != GL_TRUE)
  154. {
  155. fprintf(stderr, "ERROR, failed to link shader program\n");
  156. glGetProgramInfoLog(program, 8192, &log_length, info_log);
  157. fprintf(stderr, "ERROR: \n%s\n\n", info_log);
  158. glDeleteProgram(program);
  159. glDeleteShader(fragment_shader);
  160. glDeleteShader(vertex_shader);
  161. program = 0u;
  162. }
  163. }
  164. }
  165. else
  166. {
  167. fprintf(stderr, "ERROR: Unable to load fragment shader\n");
  168. glDeleteShader(vertex_shader);
  169. }
  170. }
  171. else
  172. {
  173. fprintf(stderr, "ERROR: Unable to load vertex shader\n");
  174. }
  175. return program;
  176. }
  177. /**********************************************************************
  178. * Geometry creation functions
  179. *********************************************************************/
  180. /* Generate vertices and indices for the heightmap
  181. */
  182. static void init_map(void)
  183. {
  184. int i;
  185. int j;
  186. int k;
  187. GLfloat step = MAP_SIZE / (MAP_NUM_VERTICES - 1);
  188. GLfloat x = 0.0f;
  189. GLfloat z = 0.0f;
  190. /* Create a flat grid */
  191. k = 0;
  192. for (i = 0 ; i < MAP_NUM_VERTICES ; ++i)
  193. {
  194. for (j = 0 ; j < MAP_NUM_VERTICES ; ++j)
  195. {
  196. map_vertices[0][k] = x;
  197. map_vertices[1][k] = 0.0f;
  198. map_vertices[2][k] = z;
  199. z += step;
  200. ++k;
  201. }
  202. x += step;
  203. z = 0.0f;
  204. }
  205. #if DEBUG_ENABLED
  206. for (i = 0 ; i < MAP_NUM_TOTAL_VERTICES ; ++i)
  207. {
  208. printf ("Vertice %d (%f, %f, %f)\n",
  209. i, map_vertices[0][i], map_vertices[1][i], map_vertices[2][i]);
  210. }
  211. #endif
  212. /* create indices */
  213. /* line fan based on i
  214. * i+1
  215. * | / i + n + 1
  216. * | /
  217. * |/
  218. * i --- i + n
  219. */
  220. /* close the top of the square */
  221. k = 0;
  222. for (i = 0 ; i < MAP_NUM_VERTICES -1 ; ++i)
  223. {
  224. map_line_indices[k++] = (i + 1) * MAP_NUM_VERTICES -1;
  225. map_line_indices[k++] = (i + 2) * MAP_NUM_VERTICES -1;
  226. }
  227. /* close the right of the square */
  228. for (i = 0 ; i < MAP_NUM_VERTICES -1 ; ++i)
  229. {
  230. map_line_indices[k++] = (MAP_NUM_VERTICES - 1) * MAP_NUM_VERTICES + i;
  231. map_line_indices[k++] = (MAP_NUM_VERTICES - 1) * MAP_NUM_VERTICES + i + 1;
  232. }
  233. for (i = 0 ; i < (MAP_NUM_VERTICES - 1) ; ++i)
  234. {
  235. for (j = 0 ; j < (MAP_NUM_VERTICES - 1) ; ++j)
  236. {
  237. int ref = i * (MAP_NUM_VERTICES) + j;
  238. map_line_indices[k++] = ref;
  239. map_line_indices[k++] = ref + 1;
  240. map_line_indices[k++] = ref;
  241. map_line_indices[k++] = ref + MAP_NUM_VERTICES;
  242. map_line_indices[k++] = ref;
  243. map_line_indices[k++] = ref + MAP_NUM_VERTICES + 1;
  244. }
  245. }
  246. #ifdef DEBUG_ENABLED
  247. for (k = 0 ; k < 2 * MAP_NUM_LINES ; k += 2)
  248. {
  249. int beg, end;
  250. beg = map_line_indices[k];
  251. end = map_line_indices[k+1];
  252. printf ("Line %d: %d -> %d (%f, %f, %f) -> (%f, %f, %f)\n",
  253. k / 2, beg, end,
  254. map_vertices[0][beg], map_vertices[1][beg], map_vertices[2][beg],
  255. map_vertices[0][end], map_vertices[1][end], map_vertices[2][end]);
  256. }
  257. #endif
  258. }
  259. static void generate_heightmap__circle(float* center_x, float* center_y,
  260. float* size, float* displacement)
  261. {
  262. float sign;
  263. /* random value for element in between [0-1.0] */
  264. *center_x = (MAP_SIZE * rand()) / (1.0f * RAND_MAX);
  265. *center_y = (MAP_SIZE * rand()) / (1.0f * RAND_MAX);
  266. *size = (MAX_CIRCLE_SIZE * rand()) / (1.0f * RAND_MAX);
  267. sign = (1.0f * rand()) / (1.0f * RAND_MAX);
  268. sign = (sign < DISPLACEMENT_SIGN_LIMIT) ? -1.0f : 1.0f;
  269. *displacement = (sign * (MAX_DISPLACEMENT * rand())) / (1.0f * RAND_MAX);
  270. }
  271. /* Run the specified number of iterations of the generation process for the
  272. * heightmap
  273. */
  274. static void update_map(int num_iter)
  275. {
  276. assert(num_iter > 0);
  277. while(num_iter)
  278. {
  279. /* center of the circle */
  280. float center_x;
  281. float center_z;
  282. float circle_size;
  283. float disp;
  284. size_t ii;
  285. generate_heightmap__circle(&center_x, &center_z, &circle_size, &disp);
  286. disp = disp / 2.0f;
  287. for (ii = 0u ; ii < MAP_NUM_TOTAL_VERTICES ; ++ii)
  288. {
  289. GLfloat dx = center_x - map_vertices[0][ii];
  290. GLfloat dz = center_z - map_vertices[2][ii];
  291. GLfloat pd = (2.0f * (float) sqrt((dx * dx) + (dz * dz))) / circle_size;
  292. if (fabs(pd) <= 1.0f)
  293. {
  294. /* tx,tz is within the circle */
  295. GLfloat new_height = disp + (float) (cos(pd*3.14f)*disp);
  296. map_vertices[1][ii] += new_height;
  297. }
  298. }
  299. --num_iter;
  300. }
  301. }
  302. /**********************************************************************
  303. * OpenGL helper functions
  304. *********************************************************************/
  305. /* Create VBO, IBO and VAO objects for the heightmap geometry and bind them to
  306. * the specified program object
  307. */
  308. static void make_mesh(GLuint program)
  309. {
  310. GLuint attrloc;
  311. glGenVertexArrays(1, &mesh);
  312. glGenBuffers(4, mesh_vbo);
  313. glBindVertexArray(mesh);
  314. /* Prepare the data for drawing through a buffer inidices */
  315. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh_vbo[3]);
  316. glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint)* MAP_NUM_LINES * 2, map_line_indices, GL_STATIC_DRAW);
  317. /* Prepare the attributes for rendering */
  318. attrloc = glGetAttribLocation(program, "x");
  319. glBindBuffer(GL_ARRAY_BUFFER, mesh_vbo[0]);
  320. glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * MAP_NUM_TOTAL_VERTICES, &map_vertices[0][0], GL_STATIC_DRAW);
  321. glEnableVertexAttribArray(attrloc);
  322. glVertexAttribPointer(attrloc, 1, GL_FLOAT, GL_FALSE, 0, 0);
  323. attrloc = glGetAttribLocation(program, "z");
  324. glBindBuffer(GL_ARRAY_BUFFER, mesh_vbo[2]);
  325. glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * MAP_NUM_TOTAL_VERTICES, &map_vertices[2][0], GL_STATIC_DRAW);
  326. glEnableVertexAttribArray(attrloc);
  327. glVertexAttribPointer(attrloc, 1, GL_FLOAT, GL_FALSE, 0, 0);
  328. attrloc = glGetAttribLocation(program, "y");
  329. glBindBuffer(GL_ARRAY_BUFFER, mesh_vbo[1]);
  330. glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * MAP_NUM_TOTAL_VERTICES, &map_vertices[1][0], GL_DYNAMIC_DRAW);
  331. glEnableVertexAttribArray(attrloc);
  332. glVertexAttribPointer(attrloc, 1, GL_FLOAT, GL_FALSE, 0, 0);
  333. }
  334. /* Update VBO vertices from source data
  335. */
  336. static void update_mesh(void)
  337. {
  338. glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GLfloat) * MAP_NUM_TOTAL_VERTICES, &map_vertices[1][0]);
  339. }
  340. /**********************************************************************
  341. * GLFW callback functions
  342. *********************************************************************/
  343. static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
  344. {
  345. switch(key)
  346. {
  347. case GLFW_KEY_ESCAPE:
  348. /* Exit program on Escape */
  349. glfwSetWindowShouldClose(window, GLFW_TRUE);
  350. break;
  351. }
  352. }
  353. static void error_callback(int error, const char* description)
  354. {
  355. fprintf(stderr, "Error: %s\n", description);
  356. }
  357. int main(int argc, char** argv)
  358. {
  359. GLFWwindow* window;
  360. int iter;
  361. double dt;
  362. double last_update_time;
  363. int frame;
  364. float f;
  365. GLint uloc_modelview;
  366. GLint uloc_project;
  367. int width, height;
  368. GLuint shader_program;
  369. glfwSetErrorCallback(error_callback);
  370. if (!glfwInit())
  371. exit(EXIT_FAILURE);
  372. glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
  373. glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
  374. glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
  375. glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
  376. glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
  377. window = glfwCreateWindow(800, 600, "GLFW OpenGL3 Heightmap demo", NULL, NULL);
  378. if (! window )
  379. {
  380. glfwTerminate();
  381. exit(EXIT_FAILURE);
  382. }
  383. /* Register events callback */
  384. glfwSetKeyCallback(window, key_callback);
  385. glfwMakeContextCurrent(window);
  386. gladLoadGLLoader((GLADloadproc) glfwGetProcAddress);
  387. /* Prepare opengl resources for rendering */
  388. shader_program = make_shader_program(vertex_shader_text, fragment_shader_text);
  389. if (shader_program == 0u)
  390. {
  391. glfwTerminate();
  392. exit(EXIT_FAILURE);
  393. }
  394. glUseProgram(shader_program);
  395. uloc_project = glGetUniformLocation(shader_program, "project");
  396. uloc_modelview = glGetUniformLocation(shader_program, "modelview");
  397. /* Compute the projection matrix */
  398. f = 1.0f / tanf(view_angle / 2.0f);
  399. projection_matrix[0] = f / aspect_ratio;
  400. projection_matrix[5] = f;
  401. projection_matrix[10] = (z_far + z_near)/ (z_near - z_far);
  402. projection_matrix[11] = -1.0f;
  403. projection_matrix[14] = 2.0f * (z_far * z_near) / (z_near - z_far);
  404. glUniformMatrix4fv(uloc_project, 1, GL_FALSE, projection_matrix);
  405. /* Set the camera position */
  406. modelview_matrix[12] = -5.0f;
  407. modelview_matrix[13] = -5.0f;
  408. modelview_matrix[14] = -20.0f;
  409. glUniformMatrix4fv(uloc_modelview, 1, GL_FALSE, modelview_matrix);
  410. /* Create mesh data */
  411. init_map();
  412. make_mesh(shader_program);
  413. /* Create vao + vbo to store the mesh */
  414. /* Create the vbo to store all the information for the grid and the height */
  415. /* setup the scene ready for rendering */
  416. glfwGetFramebufferSize(window, &width, &height);
  417. glViewport(0, 0, width, height);
  418. glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  419. /* main loop */
  420. frame = 0;
  421. iter = 0;
  422. last_update_time = glfwGetTime();
  423. while (!glfwWindowShouldClose(window))
  424. {
  425. ++frame;
  426. /* render the next frame */
  427. glClear(GL_COLOR_BUFFER_BIT);
  428. glDrawElements(GL_LINES, 2* MAP_NUM_LINES , GL_UNSIGNED_INT, 0);
  429. /* display and process events through callbacks */
  430. glfwSwapBuffers(window);
  431. glfwPollEvents();
  432. /* Check the frame rate and update the heightmap if needed */
  433. dt = glfwGetTime();
  434. if ((dt - last_update_time) > 0.2)
  435. {
  436. /* generate the next iteration of the heightmap */
  437. if (iter < MAX_ITER)
  438. {
  439. update_map(NUM_ITER_AT_A_TIME);
  440. update_mesh();
  441. iter += NUM_ITER_AT_A_TIME;
  442. }
  443. last_update_time = dt;
  444. frame = 0;
  445. }
  446. }
  447. glfwTerminate();
  448. exit(EXIT_SUCCESS);
  449. }