You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and dots ('.'), can be up to 35 characters long. Letters must be lowercase.
		
		
		
		
		
			
		
			
				
					
					
						
							511 lines
						
					
					
						
							16 KiB
						
					
					
				
			
		
		
	
	
							511 lines
						
					
					
						
							16 KiB
						
					
					
				| //======================================================================== | |
| // Heightmap example program using OpenGL 3 core profile | |
| // Copyright (c) 2010 Olivier Delannoy | |
| // | |
| // This software is provided 'as-is', without any express or implied | |
| // warranty. In no event will the authors be held liable for any damages | |
| // arising from the use of this software. | |
| // | |
| // Permission is granted to anyone to use this software for any purpose, | |
| // including commercial applications, and to alter it and redistribute it | |
| // freely, subject to the following restrictions: | |
| // | |
| // 1. The origin of this software must not be misrepresented; you must not | |
| //    claim that you wrote the original software. If you use this software | |
| //    in a product, an acknowledgment in the product documentation would | |
| //    be appreciated but is not required. | |
| // | |
| // 2. Altered source versions must be plainly marked as such, and must not | |
| //    be misrepresented as being the original software. | |
| // | |
| // 3. This notice may not be removed or altered from any source | |
| //    distribution. | |
| // | |
| //======================================================================== | |
|  | |
| #include <stdlib.h> | |
| #include <stdio.h> | |
| #include <math.h> | |
| #include <assert.h> | |
| #include <stddef.h> | |
|  | |
| #include <glad/glad.h> | |
| #include <GLFW/glfw3.h> | |
|  | |
| /* Map height updates */ | |
| #define MAX_CIRCLE_SIZE (5.0f) | |
| #define MAX_DISPLACEMENT (1.0f) | |
| #define DISPLACEMENT_SIGN_LIMIT (0.3f) | |
| #define MAX_ITER (200) | |
| #define NUM_ITER_AT_A_TIME (1) | |
|  | |
| /* Map general information */ | |
| #define MAP_SIZE (10.0f) | |
| #define MAP_NUM_VERTICES (80) | |
| #define MAP_NUM_TOTAL_VERTICES (MAP_NUM_VERTICES*MAP_NUM_VERTICES) | |
| #define MAP_NUM_LINES (3* (MAP_NUM_VERTICES - 1) * (MAP_NUM_VERTICES - 1) + \ | |
|                2 * (MAP_NUM_VERTICES - 1)) | |
|  | |
| 
 | |
| /********************************************************************** | |
|  * Default shader programs | |
|  *********************************************************************/ | |
| 
 | |
| static const char* vertex_shader_text = | |
| "#version 150\n" | |
| "uniform mat4 project;\n" | |
| "uniform mat4 modelview;\n" | |
| "in float x;\n" | |
| "in float y;\n" | |
| "in float z;\n" | |
| "\n" | |
| "void main()\n" | |
| "{\n" | |
| "   gl_Position = project * modelview * vec4(x, y, z, 1.0);\n" | |
| "}\n"; | |
| 
 | |
| static const char* fragment_shader_text = | |
| "#version 150\n" | |
| "out vec4 color;\n" | |
| "void main()\n" | |
| "{\n" | |
| "    color = vec4(0.2, 1.0, 0.2, 1.0); \n" | |
| "}\n"; | |
| 
 | |
| /********************************************************************** | |
|  * Values for shader uniforms | |
|  *********************************************************************/ | |
| 
 | |
| /* Frustum configuration */ | |
| static GLfloat view_angle = 45.0f; | |
| static GLfloat aspect_ratio = 4.0f/3.0f; | |
| static GLfloat z_near = 1.0f; | |
| static GLfloat z_far = 100.f; | |
| 
 | |
| /* Projection matrix */ | |
| static GLfloat projection_matrix[16] = { | |
|     1.0f, 0.0f, 0.0f, 0.0f, | |
|     0.0f, 1.0f, 0.0f, 0.0f, | |
|     0.0f, 0.0f, 1.0f, 0.0f, | |
|     0.0f, 0.0f, 0.0f, 1.0f | |
| }; | |
| 
 | |
| /* Model view matrix */ | |
| static GLfloat modelview_matrix[16] = { | |
|     1.0f, 0.0f, 0.0f, 0.0f, | |
|     0.0f, 1.0f, 0.0f, 0.0f, | |
|     0.0f, 0.0f, 1.0f, 0.0f, | |
|     0.0f, 0.0f, 0.0f, 1.0f | |
| }; | |
| 
 | |
| /********************************************************************** | |
|  * Heightmap vertex and index data | |
|  *********************************************************************/ | |
| 
 | |
| static GLfloat map_vertices[3][MAP_NUM_TOTAL_VERTICES]; | |
| static GLuint  map_line_indices[2*MAP_NUM_LINES]; | |
| 
 | |
| /* Store uniform location for the shaders | |
|  * Those values are setup as part of the process of creating | |
|  * the shader program. They should not be used before creating | |
|  * the program. | |
|  */ | |
| static GLuint mesh; | |
| static GLuint mesh_vbo[4]; | |
| 
 | |
| /********************************************************************** | |
|  * OpenGL helper functions | |
|  *********************************************************************/ | |
| 
 | |
| /* Creates a shader object of the specified type using the specified text | |
|  */ | |
| static GLuint make_shader(GLenum type, const char* text) | |
| { | |
|     GLuint shader; | |
|     GLint shader_ok; | |
|     GLsizei log_length; | |
|     char info_log[8192]; | |
| 
 | |
|     shader = glCreateShader(type); | |
|     if (shader != 0) | |
|     { | |
|         glShaderSource(shader, 1, (const GLchar**)&text, NULL); | |
|         glCompileShader(shader); | |
|         glGetShaderiv(shader, GL_COMPILE_STATUS, &shader_ok); | |
|         if (shader_ok != GL_TRUE) | |
|         { | |
|             fprintf(stderr, "ERROR: Failed to compile %s shader\n", (type == GL_FRAGMENT_SHADER) ? "fragment" : "vertex" ); | |
|             glGetShaderInfoLog(shader, 8192, &log_length,info_log); | |
|             fprintf(stderr, "ERROR: \n%s\n\n", info_log); | |
|             glDeleteShader(shader); | |
|             shader = 0; | |
|         } | |
|     } | |
|     return shader; | |
| } | |
| 
 | |
| /* Creates a program object using the specified vertex and fragment text | |
|  */ | |
| static GLuint make_shader_program(const char* vs_text, const char* fs_text) | |
| { | |
|     GLuint program = 0u; | |
|     GLint program_ok; | |
|     GLuint vertex_shader = 0u; | |
|     GLuint fragment_shader = 0u; | |
|     GLsizei log_length; | |
|     char info_log[8192]; | |
| 
 | |
|     vertex_shader = make_shader(GL_VERTEX_SHADER, vs_text); | |
|     if (vertex_shader != 0u) | |
|     { | |
|         fragment_shader = make_shader(GL_FRAGMENT_SHADER, fs_text); | |
|         if (fragment_shader != 0u) | |
|         { | |
|             /* make the program that connect the two shader and link it */ | |
|             program = glCreateProgram(); | |
|             if (program != 0u) | |
|             { | |
|                 /* attach both shader and link */ | |
|                 glAttachShader(program, vertex_shader); | |
|                 glAttachShader(program, fragment_shader); | |
|                 glLinkProgram(program); | |
|                 glGetProgramiv(program, GL_LINK_STATUS, &program_ok); | |
| 
 | |
|                 if (program_ok != GL_TRUE) | |
|                 { | |
|                     fprintf(stderr, "ERROR, failed to link shader program\n"); | |
|                     glGetProgramInfoLog(program, 8192, &log_length, info_log); | |
|                     fprintf(stderr, "ERROR: \n%s\n\n", info_log); | |
|                     glDeleteProgram(program); | |
|                     glDeleteShader(fragment_shader); | |
|                     glDeleteShader(vertex_shader); | |
|                     program = 0u; | |
|                 } | |
|             } | |
|         } | |
|         else | |
|         { | |
|             fprintf(stderr, "ERROR: Unable to load fragment shader\n"); | |
|             glDeleteShader(vertex_shader); | |
|         } | |
|     } | |
|     else | |
|     { | |
|         fprintf(stderr, "ERROR: Unable to load vertex shader\n"); | |
|     } | |
|     return program; | |
| } | |
| 
 | |
| /********************************************************************** | |
|  * Geometry creation functions | |
|  *********************************************************************/ | |
| 
 | |
| /* Generate vertices and indices for the heightmap | |
|  */ | |
| static void init_map(void) | |
| { | |
|     int i; | |
|     int j; | |
|     int k; | |
|     GLfloat step = MAP_SIZE / (MAP_NUM_VERTICES - 1); | |
|     GLfloat x = 0.0f; | |
|     GLfloat z = 0.0f; | |
|     /* Create a flat grid */ | |
|     k = 0; | |
|     for (i = 0 ; i < MAP_NUM_VERTICES ; ++i) | |
|     { | |
|         for (j = 0 ; j < MAP_NUM_VERTICES ; ++j) | |
|         { | |
|             map_vertices[0][k] = x; | |
|             map_vertices[1][k] = 0.0f; | |
|             map_vertices[2][k] = z; | |
|             z += step; | |
|             ++k; | |
|         } | |
|         x += step; | |
|         z = 0.0f; | |
|     } | |
| #if DEBUG_ENABLED | |
|     for (i = 0 ; i < MAP_NUM_TOTAL_VERTICES ; ++i) | |
|     { | |
|         printf ("Vertice %d (%f, %f, %f)\n", | |
|                 i, map_vertices[0][i], map_vertices[1][i], map_vertices[2][i]); | |
| 
 | |
|     } | |
| #endif | |
|     /* create indices */ | |
|     /* line fan based on i | |
|      * i+1 | |
|      * |  / i + n + 1 | |
|      * | / | |
|      * |/ | |
|      * i --- i + n | |
|      */ | |
| 
 | |
|     /* close the top of the square */ | |
|     k = 0; | |
|     for (i = 0 ; i < MAP_NUM_VERTICES  -1 ; ++i) | |
|     { | |
|         map_line_indices[k++] = (i + 1) * MAP_NUM_VERTICES -1; | |
|         map_line_indices[k++] = (i + 2) * MAP_NUM_VERTICES -1; | |
|     } | |
|     /* close the right of the square */ | |
|     for (i = 0 ; i < MAP_NUM_VERTICES -1 ; ++i) | |
|     { | |
|         map_line_indices[k++] = (MAP_NUM_VERTICES - 1) * MAP_NUM_VERTICES + i; | |
|         map_line_indices[k++] = (MAP_NUM_VERTICES - 1) * MAP_NUM_VERTICES + i + 1; | |
|     } | |
| 
 | |
|     for (i = 0 ; i < (MAP_NUM_VERTICES - 1) ; ++i) | |
|     { | |
|         for (j = 0 ; j < (MAP_NUM_VERTICES - 1) ; ++j) | |
|         { | |
|             int ref = i * (MAP_NUM_VERTICES) + j; | |
|             map_line_indices[k++] = ref; | |
|             map_line_indices[k++] = ref + 1; | |
| 
 | |
|             map_line_indices[k++] = ref; | |
|             map_line_indices[k++] = ref + MAP_NUM_VERTICES; | |
| 
 | |
|             map_line_indices[k++] = ref; | |
|             map_line_indices[k++] = ref + MAP_NUM_VERTICES + 1; | |
|         } | |
|     } | |
| 
 | |
| #ifdef DEBUG_ENABLED | |
|     for (k = 0 ; k < 2 * MAP_NUM_LINES ; k += 2) | |
|     { | |
|         int beg, end; | |
|         beg = map_line_indices[k]; | |
|         end = map_line_indices[k+1]; | |
|         printf ("Line %d: %d -> %d (%f, %f, %f) -> (%f, %f, %f)\n", | |
|                 k / 2, beg, end, | |
|                 map_vertices[0][beg], map_vertices[1][beg], map_vertices[2][beg], | |
|                 map_vertices[0][end], map_vertices[1][end], map_vertices[2][end]); | |
|     } | |
| #endif | |
| } | |
| 
 | |
| static void generate_heightmap__circle(float* center_x, float* center_y, | |
|         float* size, float* displacement) | |
| { | |
|     float sign; | |
|     /* random value for element in between [0-1.0] */ | |
|     *center_x = (MAP_SIZE * rand()) / (1.0f * RAND_MAX); | |
|     *center_y = (MAP_SIZE * rand()) / (1.0f * RAND_MAX); | |
|     *size = (MAX_CIRCLE_SIZE * rand()) / (1.0f * RAND_MAX); | |
|     sign = (1.0f * rand()) / (1.0f * RAND_MAX); | |
|     sign = (sign < DISPLACEMENT_SIGN_LIMIT) ? -1.0f : 1.0f; | |
|     *displacement = (sign * (MAX_DISPLACEMENT * rand())) / (1.0f * RAND_MAX); | |
| } | |
| 
 | |
| /* Run the specified number of iterations of the generation process for the | |
|  * heightmap | |
|  */ | |
| static void update_map(int num_iter) | |
| { | |
|     assert(num_iter > 0); | |
|     while(num_iter) | |
|     { | |
|         /* center of the circle */ | |
|         float center_x; | |
|         float center_z; | |
|         float circle_size; | |
|         float disp; | |
|         size_t ii; | |
|         generate_heightmap__circle(¢er_x, ¢er_z, &circle_size, &disp); | |
|         disp = disp / 2.0f; | |
|         for (ii = 0u ; ii < MAP_NUM_TOTAL_VERTICES ; ++ii) | |
|         { | |
|             GLfloat dx = center_x - map_vertices[0][ii]; | |
|             GLfloat dz = center_z - map_vertices[2][ii]; | |
|             GLfloat pd = (2.0f * (float) sqrt((dx * dx) + (dz * dz))) / circle_size; | |
|             if (fabs(pd) <= 1.0f) | |
|             { | |
|                 /* tx,tz is within the circle */ | |
|                 GLfloat new_height = disp + (float) (cos(pd*3.14f)*disp); | |
|                 map_vertices[1][ii] += new_height; | |
|             } | |
|         } | |
|         --num_iter; | |
|     } | |
| } | |
| 
 | |
| /********************************************************************** | |
|  * OpenGL helper functions | |
|  *********************************************************************/ | |
| 
 | |
| /* Create VBO, IBO and VAO objects for the heightmap geometry and bind them to | |
|  * the specified program object | |
|  */ | |
| static void make_mesh(GLuint program) | |
| { | |
|     GLuint attrloc; | |
| 
 | |
|     glGenVertexArrays(1, &mesh); | |
|     glGenBuffers(4, mesh_vbo); | |
|     glBindVertexArray(mesh); | |
|     /* Prepare the data for drawing through a buffer inidices */ | |
|     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh_vbo[3]); | |
|     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint)* MAP_NUM_LINES * 2, map_line_indices, GL_STATIC_DRAW); | |
| 
 | |
|     /* Prepare the attributes for rendering */ | |
|     attrloc = glGetAttribLocation(program, "x"); | |
|     glBindBuffer(GL_ARRAY_BUFFER, mesh_vbo[0]); | |
|     glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * MAP_NUM_TOTAL_VERTICES, &map_vertices[0][0], GL_STATIC_DRAW); | |
|     glEnableVertexAttribArray(attrloc); | |
|     glVertexAttribPointer(attrloc, 1, GL_FLOAT, GL_FALSE, 0, 0); | |
| 
 | |
|     attrloc = glGetAttribLocation(program, "z"); | |
|     glBindBuffer(GL_ARRAY_BUFFER, mesh_vbo[2]); | |
|     glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * MAP_NUM_TOTAL_VERTICES, &map_vertices[2][0], GL_STATIC_DRAW); | |
|     glEnableVertexAttribArray(attrloc); | |
|     glVertexAttribPointer(attrloc, 1, GL_FLOAT, GL_FALSE, 0, 0); | |
| 
 | |
|     attrloc = glGetAttribLocation(program, "y"); | |
|     glBindBuffer(GL_ARRAY_BUFFER, mesh_vbo[1]); | |
|     glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * MAP_NUM_TOTAL_VERTICES, &map_vertices[1][0], GL_DYNAMIC_DRAW); | |
|     glEnableVertexAttribArray(attrloc); | |
|     glVertexAttribPointer(attrloc, 1, GL_FLOAT, GL_FALSE, 0, 0); | |
| } | |
| 
 | |
| /* Update VBO vertices from source data | |
|  */ | |
| static void update_mesh(void) | |
| { | |
|     glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GLfloat) * MAP_NUM_TOTAL_VERTICES, &map_vertices[1][0]); | |
| } | |
| 
 | |
| /********************************************************************** | |
|  * GLFW callback functions | |
|  *********************************************************************/ | |
| 
 | |
| static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) | |
| { | |
|     switch(key) | |
|     { | |
|         case GLFW_KEY_ESCAPE: | |
|             /* Exit program on Escape */ | |
|             glfwSetWindowShouldClose(window, GLFW_TRUE); | |
|             break; | |
|     } | |
| } | |
| 
 | |
| static void error_callback(int error, const char* description) | |
| { | |
|     fprintf(stderr, "Error: %s\n", description); | |
| } | |
| 
 | |
| int main(int argc, char** argv) | |
| { | |
|     GLFWwindow* window; | |
|     int iter; | |
|     double dt; | |
|     double last_update_time; | |
|     int frame; | |
|     float f; | |
|     GLint uloc_modelview; | |
|     GLint uloc_project; | |
|     int width, height; | |
| 
 | |
|     GLuint shader_program; | |
| 
 | |
|     glfwSetErrorCallback(error_callback); | |
| 
 | |
|     if (!glfwInit()) | |
|         exit(EXIT_FAILURE); | |
| 
 | |
|     glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); | |
|     glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); | |
|     glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); | |
|     glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); | |
|     glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); | |
| 
 | |
|     window = glfwCreateWindow(800, 600, "GLFW OpenGL3 Heightmap demo", NULL, NULL); | |
|     if (! window ) | |
|     { | |
|         glfwTerminate(); | |
|         exit(EXIT_FAILURE); | |
|     } | |
| 
 | |
|     /* Register events callback */ | |
|     glfwSetKeyCallback(window, key_callback); | |
| 
 | |
|     glfwMakeContextCurrent(window); | |
|     gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); | |
| 
 | |
|     /* Prepare opengl resources for rendering */ | |
|     shader_program = make_shader_program(vertex_shader_text, fragment_shader_text); | |
| 
 | |
|     if (shader_program == 0u) | |
|     { | |
|         glfwTerminate(); | |
|         exit(EXIT_FAILURE); | |
|     } | |
| 
 | |
|     glUseProgram(shader_program); | |
|     uloc_project   = glGetUniformLocation(shader_program, "project"); | |
|     uloc_modelview = glGetUniformLocation(shader_program, "modelview"); | |
| 
 | |
|     /* Compute the projection matrix */ | |
|     f = 1.0f / tanf(view_angle / 2.0f); | |
|     projection_matrix[0]  = f / aspect_ratio; | |
|     projection_matrix[5]  = f; | |
|     projection_matrix[10] = (z_far + z_near)/ (z_near - z_far); | |
|     projection_matrix[11] = -1.0f; | |
|     projection_matrix[14] = 2.0f * (z_far * z_near) / (z_near - z_far); | |
|     glUniformMatrix4fv(uloc_project, 1, GL_FALSE, projection_matrix); | |
| 
 | |
|     /* Set the camera position */ | |
|     modelview_matrix[12]  = -5.0f; | |
|     modelview_matrix[13]  = -5.0f; | |
|     modelview_matrix[14]  = -20.0f; | |
|     glUniformMatrix4fv(uloc_modelview, 1, GL_FALSE, modelview_matrix); | |
| 
 | |
|     /* Create mesh data */ | |
|     init_map(); | |
|     make_mesh(shader_program); | |
| 
 | |
|     /* Create vao + vbo to store the mesh */ | |
|     /* Create the vbo to store all the information for the grid and the height */ | |
| 
 | |
|     /* setup the scene ready for rendering */ | |
|     glfwGetFramebufferSize(window, &width, &height); | |
|     glViewport(0, 0, width, height); | |
|     glClearColor(0.0f, 0.0f, 0.0f, 0.0f); | |
| 
 | |
|     /* main loop */ | |
|     frame = 0; | |
|     iter = 0; | |
|     last_update_time = glfwGetTime(); | |
| 
 | |
|     while (!glfwWindowShouldClose(window)) | |
|     { | |
|         ++frame; | |
|         /* render the next frame */ | |
|         glClear(GL_COLOR_BUFFER_BIT); | |
|         glDrawElements(GL_LINES, 2* MAP_NUM_LINES , GL_UNSIGNED_INT, 0); | |
| 
 | |
|         /* display and process events through callbacks */ | |
|         glfwSwapBuffers(window); | |
|         glfwPollEvents(); | |
|         /* Check the frame rate and update the heightmap if needed */ | |
|         dt = glfwGetTime(); | |
|         if ((dt - last_update_time) > 0.2) | |
|         { | |
|             /* generate the next iteration of the heightmap */ | |
|             if (iter < MAX_ITER) | |
|             { | |
|                 update_map(NUM_ITER_AT_A_TIME); | |
|                 update_mesh(); | |
|                 iter += NUM_ITER_AT_A_TIME; | |
|             } | |
|             last_update_time = dt; | |
|             frame = 0; | |
|         } | |
|     } | |
| 
 | |
|     glfwTerminate(); | |
|     exit(EXIT_SUCCESS); | |
| } | |
| 
 | |
| 
 |