/* * Copyright © 2009 Eric Anholt * Copyright © 2009 Ian D. Romanick * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. * * Authors: * Eric Anholt * */ #include #include #include #include #include #include #include #include #include #include #include "glass.h" static GLuint normalmap_tex, heightmap_tex; struct revolved_object ring; GLUvec4 ring_bounding_sphere_center_world; float ring_bounding_sphere_radius; static void install_transform(int instance) { GLUmat4 mv, mvp, light_mvp; /* Generate the whole mvp */ gluMult4m_4m(&mv, &world_to_eye, &ring_obj_to_world[instance]); gluMult4m_4m(&mvp, &projection, &mv); gluMult4m_4m(&light_mvp, &world_to_shadow_texcoords, &ring_obj_to_world[instance]); glUniformMatrix4fv(uniforms[UNIFORM_MV].location, 1, 0, (float *)&mv); glUniformMatrix4fv(uniforms[UNIFORM_MVP].location, 1, 0, (float *)&mvp); glUniformMatrix4fv(uniforms[UNIFORM_LIGHT_MVP].location, 1, 0, (float *)&light_mvp); } void do_ring_drawelements(void) { glDrawElements(GL_TRIANGLE_STRIP, ring.num_verts * (ring.num_steps * 2 + 2), GL_UNSIGNED_INT, (void *)(uintptr_t)ring.elements_offset); } void draw_rings(void) { int instance; glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, normalmap_tex); glEnable(GL_TEXTURE_2D); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, heightmap_tex); glEnable(GL_TEXTURE_2D); glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, shadow_tex); glEnable(GL_TEXTURE_2D); if (glass_prog != 0) { glUseProgram(glass_prog); glUniform3fv(uniforms[UNIFORM_LIGHT_EYE].location, 1, light_eye.values); glBindVertexArray(ring.array_obj); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB, ring.vbo); for (instance = 0; instance < NUM_RINGS; instance++) { install_transform(instance); do_ring_drawelements(); } } glActiveTexture(GL_TEXTURE0); glDisable(GL_TEXTURE_2D); glActiveTexture(GL_TEXTURE1); glDisable(GL_TEXTURE_2D); glActiveTexture(GL_TEXTURE2); glDisable(GL_TEXTURE_2D); } void update_brdf_constants(void) { float ni = 1.5; /* index of refraction */ /* Fresnel coefficient at normal (theta = 0) */ float F0 = ((ni - 1) * (ni - 1)) / ((ni + 1) * (ni + 1)); /* brushed metal */ float ward_n = .037; float ward_m = .063; float ward_mm_inv = 1.0 / (ward_m * ward_m); float ward_mn_inv = 1.0 / (ward_m * ward_n); float ward_nn_inv = 1.0 / (ward_n * ward_n); glUniform1f(uniforms[UNIFORM_NI].location, ni); glUniform1f(uniforms[UNIFORM_F0].location, F0); glUniform1f(uniforms[UNIFORM_WARD_MM_INV].location, ward_mm_inv); glUniform1f(uniforms[UNIFORM_WARD_MN_INV].location, ward_mn_inv); glUniform1f(uniforms[UNIFORM_WARD_NN_INV].location, ward_nn_inv); } /* Perform a sinusoidal weighting of v1 vs v2's components over steps, * outputting to verts. */ static void interp_corner_verts(float *verts, float v1x, float v1y, float v2x, float v2y, int num_verts, int y_oriented) { int i; for (i = 0; i < num_verts; i++) { float smooth1 = 1 - cos((float)i / (num_verts - 1) * M_PI / 2); float smooth2 = sin((float)i / (num_verts - 1) * M_PI / 2); if (y_oriented) { verts[i * 2 + 0] = v1x + (v2x - v1x) * smooth1; verts[i * 2 + 1] = v1y + (v2y - v1y) * smooth2; } else { verts[i * 2 + 0] = v1x + (v2x - v1x) * smooth2; verts[i * 2 + 1] = v1y + (v2y - v1y) * smooth1; } /* printf("%7.4f %7.4f\n", verts[i * 2 + 0], verts[i * 2 + 1]); */ } } /* Generate a rounded rectangle: * |top_flat| * _ __________ * | / \ _ * h| | | | side_flat. * | | | _ *0.0 _ \__________/ * |-----w------| * 0.0 * The rounded bits are subdivided steps times. (steps = 0 means straight line * between the vertices). The vertices are emitted in clockwise order. * * An additional vertex is generated along the flats near the corner * transitions so that automatic normal generation produces actual flat sides. */ static void generate_rounded_rect_verts(float w, float h, float top_flat, float side_flat, int steps, float **out_verts, int *out_num_verts) { float *verts; int num_verts; float corner_x = (w - top_flat) / 2; float corner_y = (h - side_flat) / 2; float top_epsilon = top_flat / 100; float side_epsilon = side_flat / 100; int v; assert(w > top_flat); assert(h > side_flat); assert(steps > 0); num_verts = steps * 4 + 4 * 4; verts = malloc(num_verts * 2 * sizeof(float)); if (verts == NULL) errx(1, "out of memory\n"); v = 0; #define ADDVERT(x, y) do { \ verts[v * 2 + 0] = x; \ verts[v * 2 + 1] = y; \ v++; \ } while (0); /* left flat */ ADDVERT(0, corner_y); ADDVERT(0, corner_y + side_epsilon); ADDVERT(0, h - corner_y - side_epsilon); ADDVERT(0, h - corner_y); /* TL corner */ interp_corner_verts(&verts[v * 2], 0, h - corner_y, corner_x, h, steps, 1); v+= steps; /* top flat */ ADDVERT(corner_x, h); ADDVERT(corner_x + top_epsilon, h); ADDVERT(w - corner_x - top_epsilon, h); ADDVERT(w - corner_x, h); /* TR corner */ interp_corner_verts(&verts[v * 2], w - corner_x, h, w, h - corner_y, steps, 0); v+= steps; /* right flat */ ADDVERT(w, h - corner_y); ADDVERT(w, h - corner_y - side_epsilon); ADDVERT(w, corner_y + side_epsilon); ADDVERT(w, corner_y); /* BR corner */ interp_corner_verts(&verts[v * 2], w, corner_y, w - corner_x, 0, steps, 1); v+= steps; /* bottom flat */ ADDVERT(w - corner_x, 0); ADDVERT(w - corner_x - top_epsilon, 0); ADDVERT(corner_x + top_epsilon, 0); ADDVERT(corner_x, 0); /* BL corner */ interp_corner_verts(&verts[v * 2], corner_x, 0, 0, corner_y, steps, 0); v+= steps; *out_verts = verts; *out_num_verts = num_verts; } static void revolve(const float *verts, unsigned int num_verts, unsigned int steps, const float *translation_matrix, float *pos, float *norm, float *tangent, float *tex_coord, unsigned int *elements) { int i, v, s; float verts4[num_verts * 4]; float normals4[num_verts * 4]; float texcoord_y[num_verts]; float unrotated_tangent4[4]; float texcoord_len = 0; /* calculate the radius of the ring. */ for (i = 0; i < num_verts; i++) { } /* Calculate the 4-component vertex and normalized normal data * that will be rotated around. */ ring.radius = 0; for (i = 0; i < num_verts; i++) { int v0; /* previous vertex */ int v2; /* next vertex */ float v0_v2[2], v1_v2[2], normal_len; float position[4], this_radius; /* Expand vertex data out to 4f, so we can use the same * matrix functions. */ verts4[i * 4 + 0] = verts[i * 2 + 0]; verts4[i * 4 + 1] = verts[i * 2 + 1]; verts4[i * 4 + 2] = 0.0; verts4[i * 4 + 3] = 1.0; mult_m4_p4(position, translation_matrix, &verts4[i * 4]); this_radius = sqrt(position[0] * position[0] + position[1] * position[1] + position[2] * position[2]); if (this_radius > ring.radius) ring.radius = this_radius; if (i == 0) v0 = num_verts - 1; else v0 = i - 1; if (i == num_verts - 1) v2 = 0; else v2 = i + 1; v0_v2[0] = verts[v0 * 2 + 0] - verts[v2 * 2 + 0]; v0_v2[1] = verts[v0 * 2 + 1] - verts[v2 * 2 + 1]; v1_v2[0] = verts[i * 2 + 0] - verts[v2 * 2 + 0]; v1_v2[1] = verts[i * 2 + 1] - verts[v2 * 2 + 1]; /* Make up a normal for this vector by taking the perpendicular * of the neighboring two points, and normalizing it. */ normal_len = sqrt(v0_v2[0] * v0_v2[0] + v0_v2[1] * v0_v2[1]); normals4[i * 4 + 0] = v0_v2[1]; normals4[i * 4 + 1] = -v0_v2[0]; normals4[i * 4 + 2] = 0.0; normals4[i * 4 + 3] = normal_len; /* Sum the distance between this vertex and the next and store * in the texcoord.y array. This way we get non-distorted * mapping of texcoords to vertices even if they're unevenly * distributed. */ texcoord_y[i] = texcoord_len; texcoord_len += sqrt(v1_v2[0] * v1_v2[0] + v1_v2[1] * v1_v2[1]); } /* Normalize texcoord.y to [0,1] range. */ for (v = 0; v < num_verts; v++) { texcoord_y[v] /= texcoord_len; } /* Create a tangent vector pointing in the direction of the rotation. */ unrotated_tangent4[0] = 0.0; unrotated_tangent4[1] = 0.0; unrotated_tangent4[2] = 1.0; unrotated_tangent4[3] = 1.0; /* Calculate the positions and normals */ for (i = 0; i <= steps; i++) { int j; float position[4], normal[4]; float rotation_matrix[16]; float translate_rotate_matrix[16]; float rotation_rads = ((float)i / steps) * 2 * M_PI; float tangent4[4]; /* Calculate the rotation matrix for this step: */ init_m4_y_rotate(rotation_matrix, rotation_rads); /* Concatenate the translation matrix for the vertices with our * rotation matrix. */ mult_m4_m4(translate_rotate_matrix, rotation_matrix, translation_matrix); /* print_m4(translate_rotate_matrix, "tr matrix"); */ mult_m4_p4(tangent4, rotation_matrix, unrotated_tangent4); tangent4[0] /= tangent4[3]; tangent4[1] /= tangent4[3]; tangent4[2] /= tangent4[3]; tangent4[3] = 1.0; /* Apply it to each of the vertices to get the verts for this * step. */ for (j = 0; j < num_verts; j++) { int vert_index = i * num_verts + j; mult_m4_p4(position, translate_rotate_matrix, &verts4[j * 4]); pos[vert_index * 3 + 0] = position[0] / position[3]; pos[vert_index * 3 + 1] = position[1] / position[3]; pos[vert_index * 3 + 2] = position[2] / position[3]; /* translated_point[3] should be 1.0. */ /* printf("vert %2d@%2d: (%7.4f, %7.4f, %7.4f) -> " "(%7.4f, %7.4f, %7.4f)\n", j, i, verts4[j * 4], verts4[j * 4 + 1], verts4[j * 4 + 1], result[0], result[1], result[2]); */ mult_m4_p4(normal, rotation_matrix, &normals4[j * 4]); norm[vert_index * 3 + 0] = normal[0] / normal[3]; norm[vert_index * 3 + 1] = normal[1] / normal[3]; norm[vert_index * 3 + 2] = normal[2] / normal[3]; /* printf("norm: %7.4f %7.4f %7.4f %7.4f\n", normal[0], normal[1], normal[2], normal[3]); */ tangent[vert_index * 3 + 0] = tangent4[0]; tangent[vert_index * 3 + 1] = tangent4[1]; tangent[vert_index * 3 + 2] = tangent4[2]; tex_coord[vert_index * 2 + 0] = (float)i / steps * 4; tex_coord[vert_index * 2 + 1] = texcoord_y[j]; } } /* Calculate the element data (indices). We emit strips going * around the ring (steps), where at the end of each strip we * dupe the vert so we can keep the strip going back the other * way without restarting the primitives. */ i = 0; for (v = 0; v < num_verts; v++) { int v0 = v; int v1 = (v + 1) % num_verts; for (s = 0; s <= steps; s++) { if (!(v & 1)) { elements[i++] = num_verts * s + v1; elements[i++] = num_verts * s + v0; } else { elements[i++] = num_verts * (s + 1) - 1 - v1; elements[i++] = num_verts * (s + 1) - 1 - v0; } } elements[i] = elements[i - 1]; i++; } glBindVertexArray(ring.array_obj); glBindBuffer(GL_ARRAY_BUFFER_ARB, ring.vbo); glVertexPointer(3, GL_FLOAT, 0, (void *)(uintptr_t)ring.pos_offset); glEnable(GL_VERTEX_ARRAY); glNormalPointer(GL_FLOAT, 0, (void *)(uintptr_t)ring.norm_offset); glEnable(GL_NORMAL_ARRAY); glClientActiveTexture(GL_TEXTURE0); glTexCoordPointer(2, GL_FLOAT, 0, (void *)(uintptr_t)ring.texcoord_offset); glEnable(GL_TEXTURE_COORD_ARRAY); glClientActiveTexture(GL_TEXTURE1); glTexCoordPointer(3, GL_FLOAT, 0, (void *)(uintptr_t)ring.tangent_offset); glEnable(GL_TEXTURE_COORD_ARRAY); glNormalPointer(GL_FLOAT, 0, (void *)(uintptr_t)ring.norm_offset); glEnable(GL_NORMAL_ARRAY); glBindVertexArray(ring.shadow_array_obj); glBindBuffer(GL_ARRAY_BUFFER_ARB, ring.vbo); glVertexPointer(3, GL_FLOAT, 0, (void *)(uintptr_t)ring.pos_offset); glEnable(GL_VERTEX_ARRAY); } void setup_rings(void) { int size; int num_steps = 100; int corner_steps = 5; float *verts; int num_verts; char *base; float translation_matrix[16]; GLuint vbo; int sv; generate_rounded_rect_verts(0.2, 1.0, .02, 0.9, corner_steps, &verts, &num_verts); memset(translation_matrix, 0, sizeof(translation_matrix)); translation_matrix[0 * 4 + 0] = 1.0; translation_matrix[1 * 4 + 1] = 1.0; translation_matrix[3 * 4 + 0] = 1.9; translation_matrix[3 * 4 + 1] = -0.5; translation_matrix[2 * 4 + 2] = 1.0; translation_matrix[3 * 4 + 3] = 1.0; sv = (num_steps + 1) * num_verts; ring.num_steps = num_steps; ring.num_verts = num_verts; ring.pos_offset = 0; ring.norm_offset = ring.pos_offset + 3 * 4 * sv; ring.tangent_offset = ring.norm_offset + 3 * 4 * sv; ring.texcoord_offset = ring.tangent_offset + 3 * 4 * sv; ring.elements_offset = ring.texcoord_offset + 2 * 4 * sv; ring.elements_vert_stride = ((num_steps + 1) * 2 + 1) * 4; size = ring.elements_offset + ring.elements_vert_stride * ring.num_verts; glGenVertexArrays(1, &ring.array_obj); glGenVertexArrays(1, &ring.shadow_array_obj); glGenBuffers(1, &vbo); ring.vbo = vbo; glBindBuffer(GL_ARRAY_BUFFER_ARB, vbo); glBufferData(GL_ARRAY_BUFFER_ARB, size, NULL, GL_STATIC_DRAW_ARB); base = glMapBuffer(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB); revolve(verts, num_verts, num_steps, translation_matrix, (float *)(base + ring.pos_offset), (float *)(base + ring.norm_offset), (float *)(base + ring.tangent_offset), (float *)(base + ring.texcoord_offset), (unsigned int *)(base + ring.elements_offset)); glUnmapBuffer(GL_ARRAY_BUFFER_ARB); free(verts); normalmap_tex = load_texture_rgb("normalmap.png"); heightmap_tex = load_texture_rgb("heightmap.png"); }