summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Anholt <eric@anholt.net>2009-08-10 09:06:18 -0700
committerEric Anholt <eric@anholt.net>2009-08-10 09:06:18 -0700
commit2f27b36a9a648d8f881f12e36594981eb5f17f81 (patch)
tree5e03237e0ed317fe672d75cfc31e951839ea332e
parentb41a892e194534d850861c934e9f257709ad2b6f (diff)
Move the rings drawing code to a separate file.
-rw-r--r--Makefile.am1
-rw-r--r--glass.c502
-rw-r--r--glass.h22
-rw-r--r--rings.c524
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
diff --git a/glass.c b/glass.c
index 7d48241..a06cbca 100644
--- a/glass.c
+++ b/glass.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");
}
/**
diff --git a/glass.h b/glass.h
index d3950ff..33cfff5 100644
--- a/glass.h
+++ b/glass.h
@@ -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);
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");
+}