--- /dev/null
+
+/* projtex.c - by David Yu and David Blythe, SGI */
+
+/**
+ ** Demonstrates simple projective texture mapping.
+ **
+ ** Button1 changes view, Button2 moves texture.
+ **
+ ** (See: Segal, Korobkin, van Widenfelt, Foran, and Haeberli
+ ** "Fast Shadows and Lighting Effects Using Texture Mapping", SIGGRAPH '92)
+ **
+ ** 1994,1995 -- David G Yu
+ **
+ ** cc -o projtex projtex.c texture.c -lglut -lGLU -lGL -lX11 -lm
+ **/
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <GL/glut.h>
+#if 0
+#include "texture.h"
+#else
+#include "../util/readtex.c"
+#endif
+
+
+/* Some <math.h> files do not define M_PI... */
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+#define MAX_TEX 2
+int NumTextures = 1;
+
+int winWidth, winHeight;
+
+GLboolean redrawContinuously = GL_FALSE;
+
+float angle, axis[3];
+enum MoveModes {
+ MoveNone, MoveView, MoveObject, MoveTexture
+};
+enum MoveModes mode = MoveNone;
+
+GLfloat objectXform[4][4];
+GLfloat textureXform[MAX_TEX][4][4];
+
+void (*drawObject) (void);
+void (*loadTexture) (void);
+GLboolean textureEnabled = GL_TRUE;
+GLboolean showProjection = GL_TRUE;
+GLboolean linearFilter = GL_TRUE;
+
+char *texFilename[MAX_TEX] = {
+ "../images/girl.rgb",
+ "../images/tile.rgb"
+};
+
+
+GLfloat zoomFactor = 1.0;
+
+/*****************************************************************/
+
+
+void ActiveTexture(int i)
+{
+ glActiveTextureARB(i);
+}
+
+
+/* matrix = identity */
+void
+matrixIdentity(GLfloat matrix[16])
+{
+ matrix[0] = 1.0;
+ matrix[1] = 0.0;
+ matrix[2] = 0.0;
+ matrix[3] = 0.0;
+ matrix[4] = 0.0;
+ matrix[5] = 1.0;
+ matrix[6] = 0.0;
+ matrix[7] = 0.0;
+ matrix[8] = 0.0;
+ matrix[9] = 0.0;
+ matrix[10] = 1.0;
+ matrix[11] = 0.0;
+ matrix[12] = 0.0;
+ matrix[13] = 0.0;
+ matrix[14] = 0.0;
+ matrix[15] = 1.0;
+}
+
+/* matrix2 = transpose(matrix1) */
+void
+matrixTranspose(GLfloat matrix2[16], GLfloat matrix1[16])
+{
+ matrix2[0] = matrix1[0];
+ matrix2[1] = matrix1[4];
+ matrix2[2] = matrix1[8];
+ matrix2[3] = matrix1[12];
+
+ matrix2[4] = matrix1[1];
+ matrix2[5] = matrix1[5];
+ matrix2[6] = matrix1[9];
+ matrix2[7] = matrix1[13];
+
+ matrix2[8] = matrix1[2];
+ matrix2[9] = matrix1[6];
+ matrix2[10] = matrix1[10];
+ matrix2[11] = matrix1[14];
+
+ matrix2[12] = matrix1[3];
+ matrix2[13] = matrix1[7];
+ matrix2[14] = matrix1[14];
+ matrix2[15] = matrix1[15];
+}
+
+/*****************************************************************/
+
+/* load SGI .rgb image (pad with a border of the specified width and color) */
+#if 0
+static void
+imgLoad(char *filenameIn, int borderIn, GLfloat borderColorIn[4],
+ int *wOut, int *hOut, GLubyte ** imgOut)
+{
+ int border = borderIn;
+ int width, height;
+ int w, h;
+ GLubyte *image, *img, *p;
+ int i, j, components;
+
+ image = (GLubyte *) read_texture(filenameIn, &width, &height, &components);
+ w = width + 2 * border;
+ h = height + 2 * border;
+ img = (GLubyte *) calloc(w * h, 4 * sizeof(unsigned char));
+
+ p = img;
+ for (j = -border; j < height + border; ++j) {
+ for (i = -border; i < width + border; ++i) {
+ if (0 <= j && j <= height - 1 && 0 <= i && i <= width - 1) {
+ p[0] = image[4 * (j * width + i) + 0];
+ p[1] = image[4 * (j * width + i) + 1];
+ p[2] = image[4 * (j * width + i) + 2];
+ p[3] = 0xff;
+ } else {
+ p[0] = borderColorIn[0] * 0xff;
+ p[1] = borderColorIn[1] * 0xff;
+ p[2] = borderColorIn[2] * 0xff;
+ p[3] = borderColorIn[3] * 0xff;
+ }
+ p += 4;
+ }
+ }
+ free(image);
+ *wOut = w;
+ *hOut = h;
+ *imgOut = img;
+}
+#endif
+
+
+/*****************************************************************/
+
+/* Load the image file specified on the command line as the current texture */
+void
+loadImageTextures(void)
+{
+ GLfloat borderColor[4] =
+ {1.0, 1.0, 1.0, 1.0};
+ int tex;
+
+ for (tex = 0; tex < NumTextures; tex++) {
+ GLubyte *image, *texData3, *texData4;
+ GLint imgWidth, imgHeight;
+ GLenum imgFormat;
+ int i, j;
+
+ printf("loading %s\n", texFilename[tex]);
+ image = LoadRGBImage(texFilename[tex], &imgWidth, &imgHeight, &imgFormat);
+ if (!image) {
+ printf("can't find %s\n", texFilename[tex]);
+ exit(1);
+ }
+ assert(imgFormat == GL_RGB);
+
+ /* scale to 256x256 */
+ texData3 = malloc(256 * 256 * 4);
+ texData4 = malloc(256 * 256 * 4);
+ assert(texData3);
+ assert(texData4);
+ gluScaleImage(imgFormat, imgWidth, imgHeight, GL_UNSIGNED_BYTE, image,
+ 256, 256, GL_UNSIGNED_BYTE, texData3);
+
+ /* convert to rgba */
+ for (i = 0; i < 256 * 256; i++) {
+ texData4[i*4+0] = texData3[i*3+0];
+ texData4[i*4+1] = texData3[i*3+1];
+ texData4[i*4+2] = texData3[i*3+2];
+ texData4[i*4+3] = 128;
+ }
+
+ /* put transparent border around image */
+ for (i = 0; i < 256; i++) {
+ texData4[i*4+0] = 255;
+ texData4[i*4+1] = 255;
+ texData4[i*4+2] = 255;
+ texData4[i*4+3] = 0;
+ }
+ j = 256 * 255 * 4;
+ for (i = 0; i < 256; i++) {
+ texData4[j + i*4+0] = 255;
+ texData4[j + i*4+1] = 255;
+ texData4[j + i*4+2] = 255;
+ texData4[j + i*4+3] = 0;
+ }
+ for (i = 0; i < 256; i++) {
+ j = i * 256 * 4;
+ texData4[j+0] = 255;
+ texData4[j+1] = 255;
+ texData4[j+2] = 255;
+ texData4[j+3] = 0;
+ }
+ for (i = 0; i < 256; i++) {
+ j = i * 256 * 4 + 255 * 4;
+ texData4[j+0] = 255;
+ texData4[j+1] = 255;
+ texData4[j+2] = 255;
+ texData4[j+3] = 0;
+ }
+
+ ActiveTexture(GL_TEXTURE0_ARB + tex);
+ glBindTexture(GL_TEXTURE_2D, tex + 1);
+
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, texData4);
+
+ if (linearFilter) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ } else {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ }
+ glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
+ }
+}
+
+/* Create a simple spotlight pattern and make it the current texture */
+void
+loadSpotlightTexture(void)
+{
+ static int texWidth = 64, texHeight = 64;
+ static GLubyte *texData;
+ GLfloat borderColor[4] =
+ {0.1, 0.1, 0.1, 1.0};
+
+ if (!texData) {
+ GLubyte *p;
+ int i, j;
+
+ texData = (GLubyte *) malloc(texWidth * texHeight * 4 * sizeof(GLubyte));
+
+ p = texData;
+ for (j = 0; j < texHeight; ++j) {
+ float dy = (texHeight * 0.5 - j + 0.5) / (texHeight * 0.5);
+
+ for (i = 0; i < texWidth; ++i) {
+ float dx = (texWidth * 0.5 - i + 0.5) / (texWidth * 0.5);
+ float r = cos(M_PI / 2.0 * sqrt(dx * dx + dy * dy));
+ float c;
+
+ r = (r < 0) ? 0 : r * r;
+ c = 0xff * (r + borderColor[0]);
+ p[0] = (c <= 0xff) ? c : 0xff;
+ c = 0xff * (r + borderColor[1]);
+ p[1] = (c <= 0xff) ? c : 0xff;
+ c = 0xff * (r + borderColor[2]);
+ p[2] = (c <= 0xff) ? c : 0xff;
+ c = 0xff * (r + borderColor[3]);
+ p[3] = (c <= 0xff) ? c : 0xff;
+ p += 4;
+ }
+ }
+ }
+ if (linearFilter) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ } else {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ }
+ glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
+ gluBuild2DMipmaps(GL_TEXTURE_2D, 4, texWidth, texHeight,
+ GL_RGBA, GL_UNSIGNED_BYTE, texData);
+}
+
+/*****************************************************************/
+
+void
+checkErrors(void)
+{
+ GLenum error;
+ while ((error = glGetError()) != GL_NO_ERROR) {
+ fprintf(stderr, "Error: %s\n", (char *) gluErrorString(error));
+ }
+}
+
+void
+drawCube(void)
+{
+ glBegin(GL_QUADS);
+
+ glNormal3f(-1.0, 0.0, 0.0);
+ glColor3f(0.80, 0.50, 0.50);
+ glVertex3f(-0.5, -0.5, -0.5);
+ glVertex3f(-0.5, -0.5, 0.5);
+ glVertex3f(-0.5, 0.5, 0.5);
+ glVertex3f(-0.5, 0.5, -0.5);
+
+ glNormal3f(1.0, 0.0, 0.0);
+ glColor3f(0.50, 0.80, 0.50);
+ glVertex3f(0.5, 0.5, 0.5);
+ glVertex3f(0.5, -0.5, 0.5);
+ glVertex3f(0.5, -0.5, -0.5);
+ glVertex3f(0.5, 0.5, -0.5);
+
+ glNormal3f(0.0, -1.0, 0.0);
+ glColor3f(0.50, 0.50, 0.80);
+ glVertex3f(-0.5, -0.5, -0.5);
+ glVertex3f(0.5, -0.5, -0.5);
+ glVertex3f(0.5, -0.5, 0.5);
+ glVertex3f(-0.5, -0.5, 0.5);
+
+ glNormal3f(0.0, 1.0, 0.0);
+ glColor3f(0.50, 0.80, 0.80);
+ glVertex3f(0.5, 0.5, 0.5);
+ glVertex3f(0.5, 0.5, -0.5);
+ glVertex3f(-0.5, 0.5, -0.5);
+ glVertex3f(-0.5, 0.5, 0.5);
+
+ glNormal3f(0.0, 0.0, -1.0);
+ glColor3f(0.80, 0.50, 0.80);
+ glVertex3f(-0.5, -0.5, -0.5);
+ glVertex3f(-0.5, 0.5, -0.5);
+ glVertex3f(0.5, 0.5, -0.5);
+ glVertex3f(0.5, -0.5, -0.5);
+
+ glNormal3f(0.0, 0.0, 1.0);
+ glColor3f(1.00, 0.80, 0.50);
+ glVertex3f(0.5, 0.5, 0.5);
+ glVertex3f(-0.5, 0.5, 0.5);
+ glVertex3f(-0.5, -0.5, 0.5);
+ glVertex3f(0.5, -0.5, 0.5);
+ glEnd();
+}
+
+void
+drawDodecahedron(void)
+{
+#define A (0.5 * 1.61803) /* (sqrt(5) + 1) / 2 */
+#define B (0.5 * 0.61803) /* (sqrt(5) - 1) / 2 */
+#define C (0.5 * 1.0)
+ GLfloat vertexes[20][3] =
+ {
+ {-A, 0.0, B},
+ {-A, 0.0, -B},
+ {A, 0.0, -B},
+ {A, 0.0, B},
+ {B, -A, 0.0},
+ {-B, -A, 0.0},
+ {-B, A, 0.0},
+ {B, A, 0.0},
+ {0.0, B, -A},
+ {0.0, -B, -A},
+ {0.0, -B, A},
+ {0.0, B, A},
+ {-C, -C, C},
+ {-C, -C, -C},
+ {C, -C, -C},
+ {C, -C, C},
+ {-C, C, C},
+ {-C, C, -C},
+ {C, C, -C},
+ {C, C, C},
+ };
+#undef A
+#undef B
+#undef C
+ GLint polygons[12][5] =
+ {
+ {0, 12, 10, 11, 16},
+ {1, 17, 8, 9, 13},
+ {2, 14, 9, 8, 18},
+ {3, 19, 11, 10, 15},
+ {4, 14, 2, 3, 15},
+ {5, 12, 0, 1, 13},
+ {6, 17, 1, 0, 16},
+ {7, 19, 3, 2, 18},
+ {8, 17, 6, 7, 18},
+ {9, 14, 4, 5, 13},
+ {10, 12, 5, 4, 15},
+ {11, 19, 7, 6, 16},
+ };
+ int i;
+
+ glColor3f(0.75, 0.75, 0.75);
+ for (i = 0; i < 12; ++i) {
+ GLfloat *p0, *p1, *p2, d;
+ GLfloat u[3], v[3], n[3];
+
+ p0 = &vertexes[polygons[i][0]][0];
+ p1 = &vertexes[polygons[i][1]][0];
+ p2 = &vertexes[polygons[i][2]][0];
+
+ u[0] = p2[0] - p1[0];
+ u[1] = p2[1] - p1[1];
+ u[2] = p2[2] - p1[2];
+
+ v[0] = p0[0] - p1[0];
+ v[1] = p0[1] - p1[1];
+ v[2] = p0[2] - p1[2];
+
+ n[0] = u[1] * v[2] - u[2] * v[1];
+ n[1] = u[2] * v[0] - u[0] * v[2];
+ n[2] = u[0] * v[1] - u[1] * v[0];
+
+ d = 1.0 / sqrt(n[0] * n[0] + n[1] * n[1] + n[2] * n[2]);
+ n[0] *= d;
+ n[1] *= d;
+ n[2] *= d;
+
+ glBegin(GL_POLYGON);
+ glNormal3fv(n);
+ glVertex3fv(p0);
+ glVertex3fv(p1);
+ glVertex3fv(p2);
+ glVertex3fv(vertexes[polygons[i][3]]);
+ glVertex3fv(vertexes[polygons[i][4]]);
+ glEnd();
+ }
+}
+
+void
+drawSphere(void)
+{
+ int numMajor = 24;
+ int numMinor = 32;
+ float radius = 0.8;
+ double majorStep = (M_PI / numMajor);
+ double minorStep = (2.0 * M_PI / numMinor);
+ int i, j;
+
+ glColor3f(0.50, 0.50, 0.50);
+ for (i = 0; i < numMajor; ++i) {
+ double a = i * majorStep;
+ double b = a + majorStep;
+ double r0 = radius * sin(a);
+ double r1 = radius * sin(b);
+ GLfloat z0 = radius * cos(a);
+ GLfloat z1 = radius * cos(b);
+
+ glBegin(GL_TRIANGLE_STRIP);
+ for (j = 0; j <= numMinor; ++j) {
+ double c = j * minorStep;
+ GLfloat x = cos(c);
+ GLfloat y = sin(c);
+
+ glNormal3f((x * r0) / radius, (y * r0) / radius, z0 / radius);
+ glTexCoord2f(j / (GLfloat) numMinor, i / (GLfloat) numMajor);
+ glVertex3f(x * r0, y * r0, z0);
+
+ glNormal3f((x * r1) / radius, (y * r1) / radius, z1 / radius);
+ glTexCoord2f(j / (GLfloat) numMinor, (i + 1) / (GLfloat) numMajor);
+ glVertex3f(x * r1, y * r1, z1);
+ }
+ glEnd();
+ }
+}
+
+/*****************************************************************/
+
+float xmin = -0.035, xmax = 0.035;
+float ymin = -0.035, ymax = 0.035;
+float nnear = 0.1;
+float ffar = 1.9;
+float distance = -1.0;
+
+static void
+loadTextureProjection(int texUnit, GLfloat m[16])
+{
+ GLfloat mInverse[4][4];
+
+ /* Should use true inverse, but since m consists only of rotations, we can
+ just use the transpose. */
+ matrixTranspose((GLfloat *) mInverse, m);
+
+ ActiveTexture(GL_TEXTURE0_ARB + texUnit);
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glTranslatef(0.5, 0.5, 0.0);
+ glScalef(0.5, 0.5, 1.0);
+ glFrustum(xmin, xmax, ymin, ymax, nnear, ffar);
+ glTranslatef(0.0, 0.0, distance);
+ glMultMatrixf((GLfloat *) mInverse);
+ glMatrixMode(GL_MODELVIEW);
+}
+
+static void
+drawTextureProjection(void)
+{
+ float t = ffar / nnear;
+ GLfloat n[4][3];
+ GLfloat f[4][3];
+
+ n[0][0] = xmin;
+ n[0][1] = ymin;
+ n[0][2] = -(nnear + distance);
+
+ n[1][0] = xmax;
+ n[1][1] = ymin;
+ n[1][2] = -(nnear + distance);
+
+ n[2][0] = xmax;
+ n[2][1] = ymax;
+ n[2][2] = -(nnear + distance);
+
+ n[3][0] = xmin;
+ n[3][1] = ymax;
+ n[3][2] = -(nnear + distance);
+
+ f[0][0] = xmin * t;
+ f[0][1] = ymin * t;
+ f[0][2] = -(ffar + distance);
+
+ f[1][0] = xmax * t;
+ f[1][1] = ymin * t;
+ f[1][2] = -(ffar + distance);
+
+ f[2][0] = xmax * t;
+ f[2][1] = ymax * t;
+ f[2][2] = -(ffar + distance);
+
+ f[3][0] = xmin * t;
+ f[3][1] = ymax * t;
+ f[3][2] = -(ffar + distance);
+
+ glColor3f(1.0, 1.0, 0.0);
+ glBegin(GL_LINE_LOOP);
+ glVertex3fv(n[0]);
+ glVertex3fv(n[1]);
+ glVertex3fv(n[2]);
+ glVertex3fv(n[3]);
+ glVertex3fv(f[3]);
+ glVertex3fv(f[2]);
+ glVertex3fv(f[1]);
+ glVertex3fv(f[0]);
+ glVertex3fv(n[0]);
+ glVertex3fv(n[1]);
+ glVertex3fv(f[1]);
+ glVertex3fv(f[0]);
+ glVertex3fv(f[3]);
+ glVertex3fv(f[2]);
+ glVertex3fv(n[2]);
+ glVertex3fv(n[3]);
+ glEnd();
+}
+
+/*****************************************************************/
+
+void
+initialize(void)
+{
+ GLfloat light0Pos[4] =
+ {0.3, 0.3, 0.0, 1.0};
+ GLfloat matAmb[4] =
+ {0.01, 0.01, 0.01, 1.00};
+ GLfloat matDiff[4] =
+ {0.65, 0.65, 0.65, 1.00};
+ GLfloat matSpec[4] =
+ {0.30, 0.30, 0.30, 1.00};
+ GLfloat matShine = 10.0;
+ GLfloat eyePlaneS[] =
+ {1.0, 0.0, 0.0, 0.0};
+ GLfloat eyePlaneT[] =
+ {0.0, 1.0, 0.0, 0.0};
+ GLfloat eyePlaneR[] =
+ {0.0, 0.0, 1.0, 0.0};
+ GLfloat eyePlaneQ[] =
+ {0.0, 0.0, 0.0, 1.0};
+ int i;
+
+ /* Setup Misc. */
+ glClearColor(0.41, 0.41, 0.31, 0.0);
+
+ glEnable(GL_DEPTH_TEST);
+
+ /* glLineWidth(2.0);*/
+
+ glCullFace(GL_FRONT);
+ glEnable(GL_CULL_FACE);
+
+ glMatrixMode(GL_PROJECTION);
+ glFrustum(-0.5, 0.5, -0.5, 0.5, 1, 3);
+ glMatrixMode(GL_MODELVIEW);
+ glTranslatef(0, 0, -2);
+
+ matrixIdentity((GLfloat *) objectXform);
+ for (i = 0; i < NumTextures; i++) {
+ matrixIdentity((GLfloat *) textureXform[i]);
+ }
+
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+ glOrtho(0, 1, 0, 1, -1, 1);
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+
+ glRasterPos2i(0, 0);
+
+ glPopMatrix();
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+
+ /* Setup Lighting */
+ glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, matAmb);
+ glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, matDiff);
+ glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, matSpec);
+ glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, matShine);
+
+ glEnable(GL_COLOR_MATERIAL);
+
+ glLightfv(GL_LIGHT0, GL_POSITION, light0Pos);
+ glEnable(GL_LIGHT0);
+
+ glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
+ glEnable(GL_LIGHTING);
+
+ /* Setup Texture */
+
+ (*loadTexture) ();
+
+
+ for (i = 0; i < NumTextures; i++) {
+ ActiveTexture(GL_TEXTURE0_ARB + i);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
+ glTexGenfv(GL_S, GL_EYE_PLANE, eyePlaneS);
+
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
+ glTexGenfv(GL_T, GL_EYE_PLANE, eyePlaneT);
+
+ glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
+ glTexGenfv(GL_R, GL_EYE_PLANE, eyePlaneR);
+
+ glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
+ glTexGenfv(GL_Q, GL_EYE_PLANE, eyePlaneQ);
+ }
+}
+
+void
+display(void)
+{
+ int i;
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ if (textureEnabled) {
+ if (mode == MoveTexture || mode == MoveView) {
+ /* Have OpenGL compute the new transformation (simple but slow). */
+ for (i = 0; i < NumTextures; i++) {
+ glPushMatrix();
+ glLoadIdentity();
+ if (i & 1)
+ glRotatef(angle, axis[0], axis[1], axis[2]);
+ else
+ glRotatef(angle*2, axis[0], axis[1], axis[2]);
+
+ glMultMatrixf((GLfloat *) textureXform[i]);
+ glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat *) textureXform[i]);
+ glPopMatrix();
+ }
+ }
+ for (i = 0; i < NumTextures; i++) {
+ loadTextureProjection(i, (GLfloat *) textureXform[i]);
+ }
+
+ if (showProjection) {
+ for (i = 0; i < NumTextures; i++) {
+ ActiveTexture(GL_TEXTURE0_ARB + i);
+ glPushMatrix();
+ glMultMatrixf((GLfloat *) textureXform[i]);
+ glDisable(GL_LIGHTING);
+ drawTextureProjection();
+ glEnable(GL_LIGHTING);
+ glPopMatrix();
+ }
+ }
+ for (i = 0; i < NumTextures; i++) {
+ ActiveTexture(GL_TEXTURE0_ARB + i);
+ glEnable(GL_TEXTURE_2D);
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_GEN_T);
+ glEnable(GL_TEXTURE_GEN_R);
+ glEnable(GL_TEXTURE_GEN_Q);
+ }
+ }
+ if (mode == MoveObject || mode == MoveView) {
+ /* Have OpenGL compute the new transformation (simple but slow). */
+ glPushMatrix();
+ glLoadIdentity();
+ glRotatef(angle, axis[0], axis[1], axis[2]);
+ glMultMatrixf((GLfloat *) objectXform);
+ glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat *) objectXform);
+ glPopMatrix();
+ }
+ glPushMatrix();
+ glMultMatrixf((GLfloat *) objectXform);
+ (*drawObject) ();
+ glPopMatrix();
+
+ for (i = 0; i < NumTextures; i++) {
+ ActiveTexture(GL_TEXTURE0_ARB + i);
+ glDisable(GL_TEXTURE_2D);
+ glDisable(GL_TEXTURE_GEN_S);
+ glDisable(GL_TEXTURE_GEN_T);
+ glDisable(GL_TEXTURE_GEN_R);
+ glDisable(GL_TEXTURE_GEN_Q);
+ }
+
+ if (zoomFactor > 1.0) {
+ glDisable(GL_DEPTH_TEST);
+ glCopyPixels(0, 0, winWidth / zoomFactor, winHeight / zoomFactor, GL_COLOR);
+ glEnable(GL_DEPTH_TEST);
+ }
+ glFlush();
+ glutSwapBuffers();
+ checkErrors();
+}
+
+/*****************************************************************/
+
+/* simple trackball-like motion control */
+float lastPos[3];
+int lastTime;
+
+void
+ptov(int x, int y, int width, int height, float v[3])
+{
+ float d, a;
+
+ /* project x,y onto a hemi-sphere centered within width, height */
+ v[0] = (2.0 * x - width) / width;
+ v[1] = (height - 2.0 * y) / height;
+ d = sqrt(v[0] * v[0] + v[1] * v[1]);
+ v[2] = cos((M_PI / 2.0) * ((d < 1.0) ? d : 1.0));
+ a = 1.0 / sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
+ v[0] *= a;
+ v[1] *= a;
+ v[2] *= a;
+}
+
+void
+startMotion(int x, int y, int but, int time)
+{
+ if (but == GLUT_LEFT_BUTTON) {
+ mode = MoveView;
+ } else if (but == GLUT_MIDDLE_BUTTON) {
+ mode = MoveTexture;
+ } else {
+ return;
+ }
+
+ lastTime = time;
+ ptov(x, y, winWidth, winHeight, lastPos);
+}
+
+void
+animate(void)
+{
+ glutPostRedisplay();
+}
+
+void
+vis(int visible)
+{
+ if (visible == GLUT_VISIBLE) {
+ if (redrawContinuously)
+ glutIdleFunc(animate);
+ } else {
+ if (redrawContinuously)
+ glutIdleFunc(NULL);
+ }
+}
+
+void
+stopMotion(int but, int time)
+{
+ if ((but == GLUT_LEFT_BUTTON && mode == MoveView) ||
+ (but == GLUT_MIDDLE_BUTTON && mode == MoveTexture)) {
+ } else {
+ return;
+ }
+
+ if (time == lastTime) {
+ /* redrawContinuously = GL_TRUE;*/
+ glutIdleFunc(animate);
+ } else {
+ angle = 0.0;
+ redrawContinuously = GL_FALSE;
+ glutIdleFunc(0);
+ }
+ if (!redrawContinuously) {
+ mode = MoveNone;
+ }
+}
+
+void
+trackMotion(int x, int y)
+{
+ float curPos[3], dx, dy, dz;
+
+ ptov(x, y, winWidth, winHeight, curPos);
+
+ dx = curPos[0] - lastPos[0];
+ dy = curPos[1] - lastPos[1];
+ dz = curPos[2] - lastPos[2];
+ angle = 90.0 * sqrt(dx * dx + dy * dy + dz * dz);
+
+ axis[0] = lastPos[1] * curPos[2] - lastPos[2] * curPos[1];
+ axis[1] = lastPos[2] * curPos[0] - lastPos[0] * curPos[2];
+ axis[2] = lastPos[0] * curPos[1] - lastPos[1] * curPos[0];
+
+ lastTime = glutGet(GLUT_ELAPSED_TIME);
+ lastPos[0] = curPos[0];
+ lastPos[1] = curPos[1];
+ lastPos[2] = curPos[2];
+ glutPostRedisplay();
+}
+
+/*****************************************************************/
+
+void
+object(void)
+{
+ static int object;
+
+ object++;
+ object %= 3;
+ switch (object) {
+ case 0:
+ drawObject = drawCube;
+ break;
+ case 1:
+ drawObject = drawDodecahedron;
+ break;
+ case 2:
+ drawObject = drawSphere;
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+nop(void)
+{
+}
+
+void
+texture(void)
+{
+ static int texture = 0;
+
+ texture++;
+ texture %= 3;
+ if (texture == 1 && texFilename == NULL) {
+ /* Skip file texture if not loaded. */
+ texture++;
+ }
+ switch (texture) {
+ case 0:
+ loadTexture = nop;
+ textureEnabled = GL_FALSE;
+ break;
+ case 1:
+ loadTexture = loadImageTextures;
+ (*loadTexture) ();
+ textureEnabled = GL_TRUE;
+ break;
+ case 2:
+ loadTexture = loadSpotlightTexture;
+ (*loadTexture) ();
+ textureEnabled = GL_TRUE;
+ break;
+ default:
+ break;
+ }
+}
+
+void
+help(void)
+{
+ printf("'h' - help\n");
+ printf("'l' - toggle linear/nearest filter\n");
+ printf("'s' - toggle projection frustum\n");
+ printf("'t' - toggle projected texture\n");
+ printf("'o' - toggle object\n");
+ printf("'z' - increase zoom factor\n");
+ printf("'Z' - decrease zoom factor\n");
+ printf("left mouse - move view\n");
+ printf("middle mouse - move projection\n");
+}
+
+/* ARGSUSED1 */
+void
+key(unsigned char key, int x, int y)
+{
+ switch (key) {
+ case '\033':
+ exit(0);
+ break;
+ case 'l':
+ linearFilter = !linearFilter;
+ (*loadTexture) ();
+ break;
+ case 's':
+ showProjection = !showProjection;
+ break;
+ case 't':
+ texture();
+ break;
+ case 'o':
+ object();
+ break;
+ case 'z':
+ zoomFactor += 1.0;
+ glPixelZoom(zoomFactor, zoomFactor);
+ glViewport(0, 0, winWidth / zoomFactor, winHeight / zoomFactor);
+ break;
+ case 'Z':
+ zoomFactor -= 1.0;
+ if (zoomFactor < 1.0)
+ zoomFactor = 1.0;
+ glPixelZoom(zoomFactor, zoomFactor);
+ glViewport(0, 0, winWidth / zoomFactor, winHeight / zoomFactor);
+ break;
+ case 'h':
+ help();
+ break;
+ }
+ glutPostRedisplay();
+}
+
+void
+mouse(int button, int state, int x, int y)
+{
+ if (state == GLUT_DOWN)
+ startMotion(x, y, button, glutGet(GLUT_ELAPSED_TIME));
+ else if (state == GLUT_UP)
+ stopMotion(button, glutGet(GLUT_ELAPSED_TIME));
+ glutPostRedisplay();
+}
+
+void
+reshape(int w, int h)
+{
+ winWidth = w;
+ winHeight = h;
+ glViewport(0, 0, w / zoomFactor, h / zoomFactor);
+}
+
+
+void
+menu(int selection)
+{
+ if (selection == 666) {
+ exit(0);
+ }
+ key((unsigned char) selection, 0, 0);
+}
+
+int
+main(int argc, char **argv)
+{
+ glutInit(&argc, argv);
+
+ if (argc > 1) {
+ NumTextures = atoi(argv[1]);
+ }
+
+ glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE);
+ (void) glutCreateWindow("projtex");
+
+ loadTexture = loadImageTextures;
+ drawObject = drawCube;
+ initialize();
+ glutDisplayFunc(display);
+ glutKeyboardFunc(key);
+ glutReshapeFunc(reshape);
+ glutMouseFunc(mouse);
+ glutMotionFunc(trackMotion);
+ glutVisibilityFunc(vis);
+ glutCreateMenu(menu);
+ glutAddMenuEntry("Toggle showing projection", 's');
+ glutAddMenuEntry("Switch texture", 't');
+ glutAddMenuEntry("Switch object", 'o');
+ glutAddMenuEntry("Toggle filtering", 'l');
+ glutAddMenuEntry("Quit", 666);
+ glutAttachMenu(GLUT_RIGHT_BUTTON);
+ texture();
+ glutMainLoop();
+ return 0; /* ANSI C requires main to return int. */
+}