//======================================================================== // 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 #include #include #include #include #include "getopt.h" #define GLFW_NO_GLU 1 #include #include /* OpenGL function pointers */ static PFNGLGENBUFFERSPROC pglGenBuffers = NULL; static PFNGLGENVERTEXARRAYSPROC pglGenVertexArrays = NULL; static PFNGLDELETEVERTEXARRAYSPROC pglDeleteVertexArrays = NULL; static PFNGLCREATESHADERPROC pglCreateShader = NULL; static PFNGLSHADERSOURCEPROC pglShaderSource = NULL; static PFNGLCOMPILESHADERPROC pglCompileShader = NULL; static PFNGLGETSHADERIVPROC pglGetShaderiv = NULL; static PFNGLGETSHADERINFOLOGPROC pglGetShaderInfoLog = NULL; static PFNGLDELETESHADERPROC pglDeleteShader = NULL; static PFNGLCREATEPROGRAMPROC pglCreateProgram = NULL; static PFNGLATTACHSHADERPROC pglAttachShader = NULL; static PFNGLLINKPROGRAMPROC pglLinkProgram = NULL; static PFNGLUSEPROGRAMPROC pglUseProgram = NULL; static PFNGLGETPROGRAMIVPROC pglGetProgramiv = NULL; static PFNGLGETPROGRAMINFOLOGPROC pglGetProgramInfoLog = NULL; static PFNGLDELETEPROGRAMPROC pglDeleteProgram = NULL; static PFNGLGETUNIFORMLOCATIONPROC pglGetUniformLocation = NULL; static PFNGLUNIFORMMATRIX4FVPROC pglUniformMatrix4fv = NULL; static PFNGLGETATTRIBLOCATIONPROC pglGetAttribLocation = NULL; static PFNGLBINDVERTEXARRAYPROC pglBindVertexArray = NULL; static PFNGLBUFFERDATAPROC pglBufferData = NULL; static PFNGLBINDBUFFERPROC pglBindBuffer = NULL; static PFNGLBUFFERSUBDATAPROC pglBufferSubData = NULL; static PFNGLENABLEVERTEXATTRIBARRAYPROC pglEnableVertexAttribArray = NULL; static PFNGLVERTEXATTRIBPOINTERPROC pglVertexAttribPointer = NULL; /* 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)) /* OpenGL function pointers */ #define RESOLVE_GL_FCN(type, var, name) \ if (status == GL_TRUE) \ {\ var = glfwGetProcAddress((name));\ if ((var) == NULL)\ {\ status = GL_FALSE;\ }\ } static GLboolean init_opengl(void) { GLboolean status = GL_TRUE; RESOLVE_GL_FCN(PFN_glCreateShader, pglCreateShader, "glCreateShader"); RESOLVE_GL_FCN(PFN_glShaderSource, pglShaderSource, "glShaderSource"); RESOLVE_GL_FCN(PFN_glCompileShader, pglCompileShader, "glCompileShader"); RESOLVE_GL_FCN(PFN_glGetShaderiv, pglGetShaderiv, "glGetShaderiv"); RESOLVE_GL_FCN(PFN_glGetShaderInfoLog, pglGetShaderInfoLog, "glGetShaderInfoLog"); RESOLVE_GL_FCN(PFN_glDeleteShader, pglDeleteShader, "glDeleteShader"); RESOLVE_GL_FCN(PFN_glCreateProgram, pglCreateProgram, "glCreateProgram"); RESOLVE_GL_FCN(PFN_glAttachShader, pglAttachShader, "glAttachShader"); RESOLVE_GL_FCN(PFN_glLinkProgram, pglLinkProgram, "glLinkProgram"); RESOLVE_GL_FCN(PFN_glUseProgram, pglUseProgram, "glUseProgram"); RESOLVE_GL_FCN(PFN_glGetProgramiv, pglGetProgramiv, "glGetProgramiv"); RESOLVE_GL_FCN(PFN_glGetProgramInfoLog, pglGetProgramInfoLog, "glGetProgramInfoLog"); RESOLVE_GL_FCN(PFN_glDeleteProgram, pglDeleteProgram, "glDeleteProgram"); RESOLVE_GL_FCN(PFN_glGetUniformLocation, pglGetUniformLocation, "glGetUniformLocation"); RESOLVE_GL_FCN(PFN_glUniformMatrix4fv, pglUniformMatrix4fv, "glUniformMatrix4fv"); RESOLVE_GL_FCN(PFN_glGetAttribLocation, pglGetAttribLocation, "glGetAttribLocation"); RESOLVE_GL_FCN(PFN_glGenVertexArrays, pglGenVertexArrays, "glGenVertexArrays"); RESOLVE_GL_FCN(PFN_glDeleteVertexArrays, pglDeleteVertexArrays, "glDeleteVertexArrays"); RESOLVE_GL_FCN(PFN_glBindVertexArray, pglBindVertexArray, "glBindVertexArray"); RESOLVE_GL_FCN(PFN_glGenBuffers, pglGenBuffers, "glGenBuffers"); RESOLVE_GL_FCN(PFN_glBindBuffer, pglBindBuffer, "glBindBuffer"); RESOLVE_GL_FCN(PFN_glBufferData, pglBufferData, "glBufferData"); RESOLVE_GL_FCN(PFN_glBufferSubData, pglBufferSubData, "glBufferSubData"); RESOLVE_GL_FCN(PFN_glEnableVertexAttribArray, pglEnableVertexAttribArray, "glEnableVertexAttribArray"); RESOLVE_GL_FCN(PFN_glVertexAttribPointer, pglVertexAttribPointer, "glVertexAttribPointer"); return status; } /********************************************************************** * Default shader programs *********************************************************************/ static const char* default_vertex_shader = "#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* default_fragment_shader = "#version 150\n" "out vec4 gl_FragColor;\n" "void main()\n" "{\n" " gl_FragColor = 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 *********************************************************************/ /* Load a (text) file into memory and return its contents */ static char* read_file_content(const char* filename) { FILE* fd; size_t size = 0; char* result = NULL; fd = fopen(filename, "r"); if (fd != NULL) { size = fseek(fd, 0, SEEK_END); (void) fseek(fd, 0, SEEK_SET); result = malloc(size + 1); result[size] = '\0'; if (fread(result, size, 1, fd) != 1) { free(result); result = NULL; } (void) fclose(fd); } return result; } /* Creates a shader object of the specified type using the specified text */ static GLuint make_shader(GLenum type, const char* shader_src) { GLuint shader; GLint shader_ok; GLsizei log_length; char info_log[8192]; shader = pglCreateShader(type); if (shader != 0) { pglShaderSource(shader, 1, (const GLchar**)&shader_src, NULL); pglCompileShader(shader); pglGetShaderiv(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" ); pglGetShaderInfoLog(shader, 8192, &log_length,info_log); fprintf(stderr, "ERROR: \n%s\n\n", info_log); pglDeleteShader(shader); shader = 0; } } return shader; } /* Creates a program object using the specified vertex and fragment text */ static GLuint make_shader_program(const char* vertex_shader_src, const char* fragment_shader_src) { 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, (vertex_shader_src == NULL) ? default_vertex_shader : vertex_shader_src); if (vertex_shader != 0u) { fragment_shader = make_shader(GL_FRAGMENT_SHADER, (fragment_shader_src == NULL) ? default_fragment_shader : fragment_shader_src); if (fragment_shader != 0u) { /* make the program that connect the two shader and link it */ program = pglCreateProgram(); if (program != 0u) { /* attach both shader and link */ pglAttachShader(program, vertex_shader); pglAttachShader(program, fragment_shader); pglLinkProgram(program); pglGetProgramiv(program, GL_LINK_STATUS, &program_ok); if (program_ok != GL_TRUE) { fprintf(stderr, "ERROR, failed to link shader program\n"); pglGetProgramInfoLog(program, 8192, &log_length, info_log); fprintf(stderr, "ERROR: \n%s\n\n", info_log); pglDeleteProgram(program); pglDeleteShader(fragment_shader); pglDeleteShader(vertex_shader); program = 0u; } } } else { fprintf(stderr, "ERROR: Unable to load fragment shader\n"); pglDeleteShader(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 * sqrtf((dx * dx) + (dz * dz))) / circle_size; if (fabs(pd) <= 1.0f) { /* tx,tz is within the circle */ GLfloat new_height = disp + (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; pglGenVertexArrays(1, &mesh); pglGenBuffers(4, mesh_vbo); pglBindVertexArray(mesh); /* Prepare the data for drawing through a buffer inidices */ pglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh_vbo[3]); pglBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint)* MAP_NUM_LINES * 2, map_line_indices, GL_STATIC_DRAW); /* Prepare the attributes for rendering */ attrloc = pglGetAttribLocation(program, "x"); pglBindBuffer(GL_ARRAY_BUFFER, mesh_vbo[0]); pglBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * MAP_NUM_TOTAL_VERTICES, &map_vertices[0][0], GL_STATIC_DRAW); pglEnableVertexAttribArray(attrloc); pglVertexAttribPointer(attrloc, 1, GL_FLOAT, GL_FALSE, 0, 0); attrloc = pglGetAttribLocation(program, "z"); pglBindBuffer(GL_ARRAY_BUFFER, mesh_vbo[2]); pglBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * MAP_NUM_TOTAL_VERTICES, &map_vertices[2][0], GL_STATIC_DRAW); pglEnableVertexAttribArray(attrloc); pglVertexAttribPointer(attrloc, 1, GL_FLOAT, GL_FALSE, 0, 0); attrloc = pglGetAttribLocation(program, "y"); pglBindBuffer(GL_ARRAY_BUFFER, mesh_vbo[1]); pglBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * MAP_NUM_TOTAL_VERTICES, &map_vertices[1][0], GL_DYNAMIC_DRAW); pglEnableVertexAttribArray(attrloc); pglVertexAttribPointer(attrloc, 1, GL_FLOAT, GL_FALSE, 0, 0); } /* Update VBO vertices from source data */ static void update_mesh(void) { pglBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GLfloat) * MAP_NUM_TOTAL_VERTICES, &map_vertices[1][0]); } /********************************************************************** * GLFW callback functions *********************************************************************/ /* The program runs as long as this is GL_TRUE */ static GLboolean running = GL_TRUE; /* GLFW Window management functions */ static int window_close_callback(GLFWwindow window) { running = GL_FALSE; /* Disallow window closing * The window will be closed when the main loop terminates */ return 0; } static void key_callback(GLFWwindow window, int key, int action) { switch(key) { case GLFW_KEY_ESC: /* Exit program on Escape */ running = GL_FALSE; break; } } /* Print usage information */ static void usage(void) { printf("Usage: heightmap [-v ] [-f ]\n"); printf(" heightmap [-h]\n"); } int main(int argc, char** argv) { GLFWwindow window; int ch, iter; double dt; double last_update_time; int frame; float f; GLint uloc_modelview; GLint uloc_project; char* vertex_shader_path = NULL; char* fragment_shader_path = NULL; char* vertex_shader_src = NULL; char* fragment_shader_src = NULL; GLuint shader_program; while ((ch = getopt(argc, argv, "f:v:h")) != -1) { switch (ch) { case 'f': fragment_shader_path = optarg; break; case 'v': vertex_shader_path = optarg; break; case 'h': usage(); exit(EXIT_SUCCESS); default: usage(); exit(EXIT_FAILURE); } } if (fragment_shader_path) { vertex_shader_src = read_file_content(fragment_shader_path); if (!fragment_shader_src) { fprintf(stderr, "ERROR: unable to load fragment shader from '%s'\n", fragment_shader_path); exit(EXIT_FAILURE); } } if (vertex_shader_path) { vertex_shader_src = read_file_content(vertex_shader_path); if (!vertex_shader_src) { fprintf(stderr, "ERROR: unable to load vertex shader from '%s'\n", fragment_shader_path); exit(EXIT_FAILURE); } } if (GL_TRUE != glfwInit()) { fprintf(stderr, "ERROR: Unable to initialize GLFW\n"); usage(); free(vertex_shader_src); free(fragment_shader_src); exit(EXIT_FAILURE); } glfwOpenWindowHint(GLFW_WINDOW_NO_RESIZE, GL_TRUE); glfwOpenWindowHint(GLFW_OPENGL_VERSION_MAJOR, 3); glfwOpenWindowHint(GLFW_OPENGL_VERSION_MINOR, 2); glfwOpenWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwOpenWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_FALSE); window = glfwOpenWindow(800, 600, GLFW_WINDOWED, "GLFW OpenGL3 Heightmap demo"); if (! window ) { fprintf(stderr, "ERROR: Unable to create the OpenGL context and associated window\n"); usage(); free(vertex_shader_src); free(fragment_shader_src); exit(EXIT_FAILURE); } glfwSetWindowCloseCallback(window, window_close_callback); glfwSetKeyCallback(window, key_callback); /* Register events callback */ if (GL_TRUE != init_opengl()) { fprintf(stderr, "ERROR: unable to resolve OpenGL function pointers\n"); free(vertex_shader_src); free(fragment_shader_src); exit(EXIT_FAILURE); } /* Prepare opengl resources for rendering */ shader_program = make_shader_program(vertex_shader_src , fragment_shader_src); free(vertex_shader_src); free(fragment_shader_src); if (shader_program == 0u) { fprintf(stderr, "ERROR: during creation of the shader program\n"); usage(); exit(EXIT_FAILURE); } pglUseProgram(shader_program); uloc_project = pglGetUniformLocation(shader_program, "project"); uloc_modelview = pglGetUniformLocation(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); pglUniformMatrix4fv(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; pglUniformMatrix4fv(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 */ glViewport(0, 0, 800, 600); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); /* main loop */ frame = 0; iter = 0; dt = last_update_time = glfwGetTime(); while (running) { ++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(); 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; } } exit(EXIT_SUCCESS); }