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