diff options
Diffstat (limited to 'rings.c')
-rw-r--r-- | rings.c | 524 |
1 files changed, 524 insertions, 0 deletions
@@ -0,0 +1,524 @@ +/* + * 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 <eric@anholt.net> + * + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <math.h> +#include <err.h> +#include <errno.h> +#include <assert.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <getopt.h> + +#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) +{ + GLvoid *indices[ring.num_verts]; + GLsizei count[ring.num_verts]; + int v; + + for (v = 0; v < ring.num_verts; v++) { + indices[v] = (void *)(uintptr_t)(ring.elements_offset + + ring.elements_vert_stride * v); + count[v] = ring.num_steps * 2 + 2; + } + + if (GLEW_EXT_multi_draw_arrays && !no_multi_draw_arrays) { + glMultiDrawElements(GL_TRIANGLE_STRIP, + count, + GL_UNSIGNED_INT, + (const GLvoid **)indices, + ring.num_verts); + } else { + for (v = 0; v < ring.num_verts; v++) { + glDrawElements(GL_TRIANGLE_STRIP, + ring.num_steps * 2 + 2, + GL_UNSIGNED_INT, + indices[v]); + } + } +} + +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)); + + glUniform1f(uniforms[UNIFORM_NI].location, ni); + glUniform1f(uniforms[UNIFORM_F0].location, F0); +} + +/* 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'll emit num_verts strips + * going around the revolved object. + */ + i = 0; + for (v = 0; v < num_verts; v++) { + int v1 = (v + 1) % num_verts; + + for (s = 0; s <= steps; s++) { + elements[i++] = num_verts * s + v1; + elements[i++] = num_verts * s + v; + } + } + + 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 * 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"); +} |