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