diff options
author | Eric Anholt <eric@anholt.net> | 2009-08-10 09:06:18 -0700 |
---|---|---|
committer | Eric Anholt <eric@anholt.net> | 2009-08-10 09:06:18 -0700 |
commit | 2f27b36a9a648d8f881f12e36594981eb5f17f81 (patch) | |
tree | 5e03237e0ed317fe672d75cfc31e951839ea332e | |
parent | b41a892e194534d850861c934e9f257709ad2b6f (diff) |
Move the rings drawing code to a separate file.
-rw-r--r-- | Makefile.am | 1 | ||||
-rw-r--r-- | glass.c | 502 | ||||
-rw-r--r-- | glass.h | 22 | ||||
-rw-r--r-- | rings.c | 524 |
4 files changed, 552 insertions, 497 deletions
diff --git a/Makefile.am b/Makefile.am index af1a63e..1fb3911 100644 --- a/Makefile.am +++ b/Makefile.am @@ -12,5 +12,6 @@ glass_SOURCES = \ ground.c \ load_texture.c \ matrix.c \ + rings.c \ shadow_map.c @@ -48,24 +48,12 @@ static int win_width, win_height; -enum uniform_list { - UNIFORM_MV, - UNIFORM_MVP, - UNIFORM_LIGHT_MVP, - UNIFORM_LIGHT_EYE, - UNIFORM_NORMAL_SAMPLER, - UNIFORM_HEIGHTMAP_SAMPLER, - UNIFORM_SHADOW_SAMPLER, - UNIFORM_NI, - UNIFORM_F0, -}; - enum shadowmap_display_uniform_list { SHADOWMAP_DISPLAY_UNIFORM_SHADOW_SAMPLER, SHADOWMAP_DISPLAY_UNIFORM_MAX }; -struct uniform_desc uniforms[] = { +struct uniform_desc uniforms[UNIFORM_MAX] = { [UNIFORM_MV] = { "mv" }, [UNIFORM_MVP] = { "mvp" }, [UNIFORM_LIGHT_MVP] = { "light_mvp" }, @@ -93,10 +81,8 @@ struct uniform_desc shadowmap_display_uniforms[SHADOWMAP_DISPLAY_UNIFORM_MAX] = [SHADOWMAP_DISPLAY_UNIFORM_SHADOW_SAMPLER] = { "shadow_sampler" }, }; -static GLuint glass_prog = 0, shadowmap_display_prog = 0; -GLuint ground_prog = 0, shadow_prog = 0; -struct revolved_object ring; -GLuint normalmap_tex, heightmap_tex; +static GLuint shadowmap_display_prog = 0; +GLuint glass_prog = 0, ground_prog = 0, shadow_prog = 0; static GLboolean display_shadow_map = GL_FALSE; static const GLUvec4 light_start_world = {{0.0, 10.0, 25.0, 1.0}}; @@ -111,8 +97,6 @@ GLUvec4 light_world; GLUmat4 projection; GLUmat4 world_to_eye; GLUmat4 ring_obj_to_world[NUM_RINGS]; -GLUvec4 ring_bounding_sphere_center_world; -float ring_bounding_sphere_radius; static time_t start_tv_sec = 0; static float start_time, cur_time, last_fps_time = 0; @@ -139,17 +123,6 @@ get_time_in_secs(void) } static 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); -} - -static void update_light_position(void) { GLUmat4 light_eye_matrix, light_world_matrix, temp; @@ -182,35 +155,6 @@ update_light_position(void) light_eye.values[3] = 1.0; } -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]); - } - } -} - static void calc_new_ring_transforms(int instance) { @@ -265,60 +209,8 @@ calc_new_ring_transforms(int instance) * So we're just making a bounding box and doing a bounding sphere * of it, really. */ - ring_bounding_sphere_radius = sqrt((5 + ring.radius) * (5 + ring.radius) * 3); -} - -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); -} - -static 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); + ring_bounding_sphere_radius = sqrt((5 + ring.radius) * + (5 + ring.radius) * 3); } static void @@ -397,201 +289,6 @@ draw(void) } 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); -} - -static void reshape(int width, int height) { win_width = width; @@ -660,135 +357,6 @@ compile_program(GLenum type, const char *filename) return prog; } -/* 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 GLuint load_program(char *base_filename, struct uniform_desc *uniforms, int num_uniforms) @@ -891,16 +459,6 @@ load_programs(void) static void init(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; - reshape(DEFAULT_WIDTH, DEFAULT_HEIGHT); glewInit(); @@ -916,57 +474,9 @@ init(void) gluLookAt4v(&world_to_eye, &eye_world, &eye_center_world, &z_axis); setup_ground(); - - 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); + setup_rings(); load_programs(); - - glActiveTexture(GL_TEXTURE0); - normalmap_tex = load_texture_rgb("normalmap.png"); - glActiveTexture(GL_TEXTURE1); - heightmap_tex = load_texture_rgb("heightmap.png"); } /** @@ -38,6 +38,19 @@ struct revolved_object { float radius; }; +enum uniform_list { + UNIFORM_MV, + UNIFORM_MVP, + UNIFORM_LIGHT_MVP, + UNIFORM_LIGHT_EYE, + UNIFORM_NORMAL_SAMPLER, + UNIFORM_HEIGHTMAP_SAMPLER, + UNIFORM_SHADOW_SAMPLER, + UNIFORM_NI, + UNIFORM_F0, + UNIFORM_MAX +}; + enum ground_uniform_list { GROUND_UNIFORM_MV, GROUND_UNIFORM_MVP, @@ -59,9 +72,10 @@ struct uniform_desc { #define NUM_RINGS 27 +extern struct uniform_desc uniforms[UNIFORM_MAX]; extern struct uniform_desc ground_uniforms[GROUND_UNIFORM_MAX]; extern struct uniform_desc shadow_uniforms[SHADOW_UNIFORM_MAX]; -extern GLuint ground_prog, shadow_prog; +extern GLuint glass_prog, ground_prog, shadow_prog; extern GLUmat4 projection; extern GLUmat4 world_to_eye, world_to_shadow_texcoords; extern GLUvec4 light_world, light_eye; @@ -71,6 +85,7 @@ extern GLUvec4 ring_bounding_sphere_center_world; extern float ring_bounding_sphere_radius; extern GLuint shadow_tex; extern GLUmat4 world_to_light_ndc; +extern int no_multi_draw_arrays; void do_ring_drawelements(void); @@ -97,5 +112,10 @@ GLuint load_texture_rgb(const char *filename); void draw_ground(void); void setup_ground(void); +/* rings.c */ +void draw_rings(void); +void update_brdf_constants(void); +void setup_rings(void); + /* shadow_map.c */ void generate_rings_shadowmap(void); @@ -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"); +} |