summaryrefslogtreecommitdiff
path: root/rings.c
diff options
context:
space:
mode:
Diffstat (limited to 'rings.c')
-rw-r--r--rings.c524
1 files changed, 524 insertions, 0 deletions
diff --git a/rings.c b/rings.c
new file mode 100644
index 0000000..275f4ef
--- /dev/null
+++ b/rings.c
@@ -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");
+}