/* * CS5600 University of Utah * Charles McGarvey * mcgarvey@eng.utah.edu */ #if OPENGL #define GL_GLEXT_PROTOTYPES #include #include #include "map.h" /* * More scene methods adding support for rendering with OpenGL. */ DEFINE_MAP_TYPE2(int64_t, GLuint, model_tex); DEFINE_ARRAY_TYPE(vec); DEFINE_ARRAY_TYPE(color); static scene_t* _scene = NULL; static GLuint _program = 0; static map_t* _textures = NULL; /* * Load a shader of a specified type. */ static GLuint _gl_load_shader(const char* filename, GLenum type) { GLuint shader = glCreateShader(type); if (shader == 0) { return 0; } FILE* file = fopen(filename, "r"); if (file == NULL) { fprintf(stderr, "Cannot read %s: %s\n", filename, strerror(errno)); return 0; } char* src = NULL; size_t size = 0; for (size_t bytes; !feof(file); size += bytes) { src = mem_realloc(src, size + 4096); bytes = fread(src + size, 1, 4095, file); if (ferror(file)) { fprintf(stderr, "Error reading %s: %s\n", filename, strerror(errno)); fclose(file); mem_free(src); return 0; } } src[size] = '\0'; fclose(file); glShaderSource(shader, 1, (const char**)&src, NULL); glCompileShader(shader); mem_free(src); GLint status; glGetShaderiv(shader, GL_COMPILE_STATUS, &status); if (status == 0) { GLint len = 0; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len); if (1 < len) { char* infoLog = mem_alloc(len); glGetShaderInfoLog(shader, len, NULL, infoLog); fprintf(stderr, "Error compiling %s:\n%s\n", filename, infoLog); mem_free(infoLog); } glDeleteShader(shader); return 0; } return shader; } void _model_gl_draw(const model_t* model) { mat_t modelTransform; model_transformation(model, &modelTransform); GLuint model_matrix = glGetUniformLocation(_program, "model"); glUniformMatrix4fv(model_matrix, 1, GL_FALSE, mat_data(&modelTransform)); GLuint specular = glGetUniformLocation(_program, "mspecular"); color_t mspec = model_specular(model); glUniform4fv(specular, 1, color_data(&mspec)); GLuint shininess = glGetUniformLocation(_program, "shininess"); glUniform1f(shininess, model_shininess(model)); GLuint* tex = map_model_tex_search(_textures, (int64_t)model); if (tex) { GLuint texture = glGetUniformLocation(_program, "sampler"); glUniform1i(texture, 0); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, *tex); GLuint is_texture = glGetUniformLocation(_program, "isTexture"); glUniform1i(is_texture, 1); } else { GLuint is_texture = glGetUniformLocation(_program, "isTexture"); glUniform1i(is_texture, 0); } const array_t* triangles = model_geometry(model); array_t* vertices = array_vec_alloc(); array_t* normals = array_vec_alloc(); array_t* texcoords = array_vec_alloc(); array_t* colors = array_color_alloc(); array_it_t it = array_begin(triangles); tri_t* t; while (t = array_it_tri_next(&it)) { array_vec_push(vertices, t->a.v); array_vec_push(vertices, t->b.v); array_vec_push(vertices, t->c.v); array_vec_push(normals, t->a.n); array_vec_push(normals, t->b.n); array_vec_push(normals, t->c.n); array_vec_push(texcoords, t->a.t); array_vec_push(texcoords, t->b.t); array_vec_push(texcoords, t->c.t); array_color_push(colors, t->a.c); array_color_push(colors, t->b.c); array_color_push(colors, t->c.c); } glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glEnableVertexAttribArray(2); glEnableVertexAttribArray(3); glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(vec_t), array_vec_front(vertices)); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(vec_t), array_vec_front(normals)); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(vec_t), array_vec_front(texcoords)); glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, sizeof(vec_t), array_color_front(colors)); glDrawArrays(GL_TRIANGLES, 0, array_size(vertices)); array_destroy(vertices); array_destroy(normals); array_destroy(texcoords); array_destroy(colors); glBindTexture(GL_TEXTURE_2D, 0); } void _scene_glut_display() { assert(_scene); scene_t* s = _scene; #if VERBOSITY >= 3 timer_start(); #endif glViewport(0, 0, s->w, s->h); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glUseProgram(_program); GLuint view_matrix = glGetUniformLocation(_program, "view"); glUniformMatrix4fv(view_matrix, 1, GL_FALSE, mat_data(&s->view)); GLuint projection_matrix = glGetUniformLocation(_program, "projection"); glUniformMatrix4fv(projection_matrix, 1, GL_FALSE, mat_data(&s->projection)); GLuint eye_position = glGetUniformLocation(_program, "eye"); glUniform3fv(eye_position, 1, vec_data(&s->eye)); GLuint ambient_light = glGetUniformLocation(_program, "ambient"); glUniform4fv(ambient_light, 1, color_data(&s->ambient)); array_t* lightPos = array_vec_alloc(); array_t* lightDiffuse = array_color_alloc(); array_t* lightSpecular = array_color_alloc(); int count = 0; for (list_t* i = s->lights; i; i = i->link) { light_t* light = (light_t*)i->val; ++count; array_vec_push(lightPos, light->v); array_color_push(lightDiffuse, light->d); array_color_push(lightSpecular, light->s); } GLuint num_lights = glGetUniformLocation(_program, "numLights"); glUniform1i(num_lights, count); GLuint light_position = glGetUniformLocation(_program, "light"); glUniform4fv(light_position, count, vec_data(array_front(lightPos))); GLuint light_diffuse = glGetUniformLocation(_program, "diffuse"); glUniform4fv(light_diffuse, count, color_data(array_front(lightDiffuse))); GLuint light_specular = glGetUniformLocation(_program, "specular"); glUniform4fv(light_specular, count, color_data(array_front(lightSpecular))); array_destroy(lightPos); array_destroy(lightDiffuse); array_destroy(lightSpecular); #if VERBOSITY >= 3 printf("rendering scene...\n"); #endif for (list_t* gi = s->models; gi; gi = gi->link) { model_t* m = (model_t*)gi->val; _model_gl_draw(m); } #if VERBOSITY >= 3 long dt = timer_stop(); printf("render complete!\ntime\t%.3fms\n", (float)dt / 1000.0f); #endif glutSwapBuffers(); } void _scene_glut_keyboard(unsigned char key, int x, int y) { if (key == 27) { exit(0); // exit when the escape key is pressed } } void _scene_gl_init() { assert(_scene); scene_t* s = _scene; glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glEnable(GL_DEPTH_TEST); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_TEXTURE_2D); glEnable(GL_CULL_FACE); GLuint vShader = _gl_load_shader("vertex.glsl", GL_VERTEX_SHADER); GLuint fShader = _gl_load_shader("fragment.glsl", GL_FRAGMENT_SHADER); if (vShader == 0 || fShader == 0) { fprintf(stderr, "One or more shaders failed to compile."); exit(1); } GLuint program = glCreateProgram(); if (program == 0) { fprintf(stderr, "Could not create GL program."); exit(1); } glAttachShader(program, vShader); glAttachShader(program, fShader); glBindAttribLocation(program, 0, "vPosition"); glBindAttribLocation(program, 1, "vNormal"); glBindAttribLocation(program, 2, "vTextureCoord"); glBindAttribLocation(program, 3, "vColor"); glLinkProgram(program); GLint status; glGetProgramiv(program, GL_LINK_STATUS, &status); if (status == 0) { GLint len = 0; glGetProgramiv(program, GL_INFO_LOG_LENGTH, &len); if (1 < len) { char* infoLog = mem_alloc(len); glGetProgramInfoLog(program, len, NULL, infoLog); fprintf(stderr, "Program link error:\n%s\n", infoLog); mem_free(infoLog); } glDeleteProgram(program); fprintf(stderr, "Could not link GL program."); exit(1); } _textures = map_model_tex_alloc(); for (list_t* gi = s->models; gi; gi = gi->link) { model_t* m = (model_t*)gi->val; int w, h; void* tdata = model_tdata(m, &w, &h); if (tdata) { GLuint tex; glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glGenTextures(1, &tex); glBindTexture(GL_TEXTURE_2D, tex); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, tdata); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); map_model_tex_insert(_textures, (int64_t)m, tex); } } glBindTexture(GL_TEXTURE_2D, 0); _program = program; } void _scene_glut_idle() { glutPostRedisplay(); } void scene_render_gl(scene_t* s) { _scene = s; // initialize glut int argc = 0; glutInit(&argc, NULL); glutInitWindowSize(s->w, s->h); glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH); glutCreateWindow("Project 4 Scene"); // initialize opengl _scene_gl_init(); glutDisplayFunc(_scene_glut_display); glutKeyboardFunc(_scene_glut_keyboard); /*glutIdleFunc(_scene_glut_idle);*/ glutMainLoop(); } #endif // OPENGL