1
2/* projtex.c - by David Yu and David Blythe, SGI */
3
4/**
5 ** Demonstrates simple projective texture mapping.
6 **
7 ** Button1 changes view, Button2 moves texture.
8 **
9 ** (See: Segal, Korobkin, van Widenfelt, Foran, and Haeberli
10 **  "Fast Shadows and Lighting Effects Using Texture Mapping", SIGGRAPH '92)
11 **
12 ** 1994,1995 -- David G Yu
13 **
14 ** cc -o projtex projtex.c texture.c -lglut -lGLU -lGL -lX11 -lm
15 **/
16
17#include <assert.h>
18#include <stdio.h>
19#include <stdlib.h>
20#include <math.h>
21#include <GL/glew.h>
22#include "glut_wrap.h"
23#include "readtex.h"
24
25
26/* Some <math.h> files do not define M_PI... */
27#ifndef M_PI
28#define M_PI 3.14159265358979323846
29#endif
30
31#define MAX_TEX 4
32int NumTextures = 1;
33
34int winWidth, winHeight;
35
36GLboolean redrawContinuously = GL_FALSE;
37
38float angle, axis[3];
39enum MoveModes {
40  MoveNone, MoveView, MoveObject, MoveTexture
41};
42enum MoveModes mode = MoveNone;
43
44GLfloat objectXform[4][4];
45GLfloat textureXform[MAX_TEX][4][4];
46
47void (*drawObject) (void);
48void (*loadTexture) (void);
49GLboolean textureEnabled = GL_TRUE;
50GLboolean showProjection = GL_TRUE;
51GLboolean linearFilter = GL_TRUE;
52
53char *texFilename[MAX_TEX] = {
54   DEMOS_DATA_DIR "girl.rgb",
55   DEMOS_DATA_DIR "tile.rgb",
56   DEMOS_DATA_DIR "bw.rgb",
57   DEMOS_DATA_DIR "reflect.rgb"
58};
59
60
61GLfloat zoomFactor = 1.0;
62
63/*****************************************************************/
64
65
66static void
67ActiveTexture(int i)
68{
69   glActiveTextureARB(i);
70}
71
72
73/* matrix = identity */
74static void
75matrixIdentity(GLfloat matrix[16])
76{
77  matrix[0] = 1.0;
78  matrix[1] = 0.0;
79  matrix[2] = 0.0;
80  matrix[3] = 0.0;
81  matrix[4] = 0.0;
82  matrix[5] = 1.0;
83  matrix[6] = 0.0;
84  matrix[7] = 0.0;
85  matrix[8] = 0.0;
86  matrix[9] = 0.0;
87  matrix[10] = 1.0;
88  matrix[11] = 0.0;
89  matrix[12] = 0.0;
90  matrix[13] = 0.0;
91  matrix[14] = 0.0;
92  matrix[15] = 1.0;
93}
94
95/* matrix2 = transpose(matrix1) */
96static void
97matrixTranspose(GLfloat matrix2[16], GLfloat matrix1[16])
98{
99  matrix2[0] = matrix1[0];
100  matrix2[1] = matrix1[4];
101  matrix2[2] = matrix1[8];
102  matrix2[3] = matrix1[12];
103
104  matrix2[4] = matrix1[1];
105  matrix2[5] = matrix1[5];
106  matrix2[6] = matrix1[9];
107  matrix2[7] = matrix1[13];
108
109  matrix2[8] = matrix1[2];
110  matrix2[9] = matrix1[6];
111  matrix2[10] = matrix1[10];
112  matrix2[11] = matrix1[14];
113
114  matrix2[12] = matrix1[3];
115  matrix2[13] = matrix1[7];
116  matrix2[14] = matrix1[14];
117  matrix2[15] = matrix1[15];
118}
119
120/*****************************************************************/
121
122/* load SGI .rgb image (pad with a border of the specified width and color) */
123#if 0
124static void
125imgLoad(char *filenameIn, int borderIn, GLfloat borderColorIn[4],
126  int *wOut, int *hOut, GLubyte ** imgOut)
127{
128  int border = borderIn;
129  int width, height;
130  int w, h;
131  GLubyte *image, *img, *p;
132  int i, j, components;
133
134  image = (GLubyte *) read_texture(filenameIn, &width, &height, &components);
135  w = width + 2 * border;
136  h = height + 2 * border;
137  img = (GLubyte *) calloc(w * h, 4 * sizeof(unsigned char));
138
139  p = img;
140  for (j = -border; j < height + border; ++j) {
141    for (i = -border; i < width + border; ++i) {
142      if (0 <= j && j <= height - 1 && 0 <= i && i <= width - 1) {
143        p[0] = image[4 * (j * width + i) + 0];
144        p[1] = image[4 * (j * width + i) + 1];
145        p[2] = image[4 * (j * width + i) + 2];
146        p[3] = 0xff;
147      } else {
148        p[0] = borderColorIn[0] * 0xff;
149        p[1] = borderColorIn[1] * 0xff;
150        p[2] = borderColorIn[2] * 0xff;
151        p[3] = borderColorIn[3] * 0xff;
152      }
153      p += 4;
154    }
155  }
156  free(image);
157  *wOut = w;
158  *hOut = h;
159  *imgOut = img;
160}
161#endif
162
163
164/*****************************************************************/
165
166/* Load the image file specified on the command line as the current texture */
167static void
168loadImageTextures(void)
169{
170  GLfloat borderColor[4] =
171  {1.0, 1.0, 1.0, 1.0};
172  int tex;
173
174  for (tex = 0; tex < NumTextures; tex++) {
175     GLubyte *image, *texData3, *texData4;
176     GLint imgWidth, imgHeight;
177     GLenum imgFormat;
178     int i, j;
179
180     printf("loading %s\n", texFilename[tex]);
181     image = LoadRGBImage(texFilename[tex], &imgWidth, &imgHeight, &imgFormat);
182     if (!image) {
183        printf("can't find %s\n", texFilename[tex]);
184        exit(1);
185     }
186     assert(imgFormat == GL_RGB);
187
188     /* scale to 256x256 */
189     texData3 = malloc(256 * 256 * 4);
190     texData4 = malloc(256 * 256 * 4);
191     assert(texData3);
192     assert(texData4);
193     gluScaleImage(imgFormat, imgWidth, imgHeight, GL_UNSIGNED_BYTE, image,
194                   256, 256, GL_UNSIGNED_BYTE, texData3);
195
196     /* convert to rgba */
197     for (i = 0; i < 256 * 256; i++) {
198        texData4[i*4+0] = texData3[i*3+0];
199        texData4[i*4+1] = texData3[i*3+1];
200        texData4[i*4+2] = texData3[i*3+2];
201        texData4[i*4+3] = 128;
202     }
203
204     /* put transparent border around image */
205     for (i = 0; i < 256; i++) {
206        texData4[i*4+0] = 255;
207        texData4[i*4+1] = 255;
208        texData4[i*4+2] = 255;
209        texData4[i*4+3] = 0;
210     }
211     j = 256 * 255 * 4;
212     for (i = 0; i < 256; i++) {
213        texData4[j + i*4+0] = 255;
214        texData4[j + i*4+1] = 255;
215        texData4[j + i*4+2] = 255;
216        texData4[j + i*4+3] = 0;
217     }
218     for (i = 0; i < 256; i++) {
219        j = i * 256 * 4;
220        texData4[j+0] = 255;
221        texData4[j+1] = 255;
222        texData4[j+2] = 255;
223        texData4[j+3] = 0;
224     }
225     for (i = 0; i < 256; i++) {
226        j = i * 256 * 4 + 255 * 4;
227        texData4[j+0] = 255;
228        texData4[j+1] = 255;
229        texData4[j+2] = 255;
230        texData4[j+3] = 0;
231     }
232
233     ActiveTexture(GL_TEXTURE0_ARB + tex);
234     glBindTexture(GL_TEXTURE_2D, tex + 1);
235
236     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
237     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0,
238                  GL_RGBA, GL_UNSIGNED_BYTE, texData4);
239
240     if (linearFilter) {
241       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
242       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
243     } else {
244       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
245       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
246     }
247     glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
248
249     free(texData3);
250     free(texData4);
251     free(image);
252  }
253}
254
255/* Create a simple spotlight pattern and make it the current texture */
256static void
257loadSpotlightTexture(void)
258{
259  static int texWidth = 64, texHeight = 64;
260  static GLubyte *texData;
261  GLfloat borderColor[4] =
262  {0.1, 0.1, 0.1, 1.0};
263
264  if (!texData) {
265    GLubyte *p;
266    int i, j;
267
268    texData = (GLubyte *) malloc(texWidth * texHeight * 4 * sizeof(GLubyte));
269
270    p = texData;
271    for (j = 0; j < texHeight; ++j) {
272      float dy = (texHeight * 0.5 - j + 0.5) / (texHeight * 0.5);
273
274      for (i = 0; i < texWidth; ++i) {
275        float dx = (texWidth * 0.5 - i + 0.5) / (texWidth * 0.5);
276        float r = cos(M_PI / 2.0 * sqrt(dx * dx + dy * dy));
277        float c;
278
279        r = (r < 0) ? 0 : r * r;
280        c = 0xff * (r + borderColor[0]);
281        p[0] = (c <= 0xff) ? c : 0xff;
282        c = 0xff * (r + borderColor[1]);
283        p[1] = (c <= 0xff) ? c : 0xff;
284        c = 0xff * (r + borderColor[2]);
285        p[2] = (c <= 0xff) ? c : 0xff;
286        c = 0xff * (r + borderColor[3]);
287        p[3] = (c <= 0xff) ? c : 0xff;
288        p += 4;
289      }
290    }
291  }
292  if (linearFilter) {
293    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
294    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
295  } else {
296    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
297    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
298  }
299  glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
300  gluBuild2DMipmaps(GL_TEXTURE_2D, 4, texWidth, texHeight,
301    GL_RGBA, GL_UNSIGNED_BYTE, texData);
302}
303
304/*****************************************************************/
305
306static void
307checkErrors(void)
308{
309  GLenum error;
310  while ((error = glGetError()) != GL_NO_ERROR) {
311    fprintf(stderr, "Error: %s\n", (char *) gluErrorString(error));
312  }
313}
314
315static void
316drawCube(void)
317{
318  glBegin(GL_QUADS);
319
320  glNormal3f(-1.0, 0.0, 0.0);
321  glColor3f(0.80, 0.50, 0.50);
322  glVertex3f(-0.5, -0.5, -0.5);
323  glVertex3f(-0.5, -0.5, 0.5);
324  glVertex3f(-0.5, 0.5, 0.5);
325  glVertex3f(-0.5, 0.5, -0.5);
326
327  glNormal3f(1.0, 0.0, 0.0);
328  glColor3f(0.50, 0.80, 0.50);
329  glVertex3f(0.5, 0.5, 0.5);
330  glVertex3f(0.5, -0.5, 0.5);
331  glVertex3f(0.5, -0.5, -0.5);
332  glVertex3f(0.5, 0.5, -0.5);
333
334  glNormal3f(0.0, -1.0, 0.0);
335  glColor3f(0.50, 0.50, 0.80);
336  glVertex3f(-0.5, -0.5, -0.5);
337  glVertex3f(0.5, -0.5, -0.5);
338  glVertex3f(0.5, -0.5, 0.5);
339  glVertex3f(-0.5, -0.5, 0.5);
340
341  glNormal3f(0.0, 1.0, 0.0);
342  glColor3f(0.50, 0.80, 0.80);
343  glVertex3f(0.5, 0.5, 0.5);
344  glVertex3f(0.5, 0.5, -0.5);
345  glVertex3f(-0.5, 0.5, -0.5);
346  glVertex3f(-0.5, 0.5, 0.5);
347
348  glNormal3f(0.0, 0.0, -1.0);
349  glColor3f(0.80, 0.50, 0.80);
350  glVertex3f(-0.5, -0.5, -0.5);
351  glVertex3f(-0.5, 0.5, -0.5);
352  glVertex3f(0.5, 0.5, -0.5);
353  glVertex3f(0.5, -0.5, -0.5);
354
355  glNormal3f(0.0, 0.0, 1.0);
356  glColor3f(1.00, 0.80, 0.50);
357  glVertex3f(0.5, 0.5, 0.5);
358  glVertex3f(-0.5, 0.5, 0.5);
359  glVertex3f(-0.5, -0.5, 0.5);
360  glVertex3f(0.5, -0.5, 0.5);
361  glEnd();
362}
363
364static void
365drawDodecahedron(void)
366{
367#define A (0.5 * 1.61803)  /* (sqrt(5) + 1) / 2 */
368#define B (0.5 * 0.61803)  /* (sqrt(5) - 1) / 2 */
369#define C (0.5 * 1.0)
370  GLfloat vertexes[20][3] =
371  {
372    {-A, 0.0, B},
373    {-A, 0.0, -B},
374    {A, 0.0, -B},
375    {A, 0.0, B},
376    {B, -A, 0.0},
377    {-B, -A, 0.0},
378    {-B, A, 0.0},
379    {B, A, 0.0},
380    {0.0, B, -A},
381    {0.0, -B, -A},
382    {0.0, -B, A},
383    {0.0, B, A},
384    {-C, -C, C},
385    {-C, -C, -C},
386    {C, -C, -C},
387    {C, -C, C},
388    {-C, C, C},
389    {-C, C, -C},
390    {C, C, -C},
391    {C, C, C},
392  };
393#undef A
394#undef B
395#undef C
396  GLint polygons[12][5] =
397  {
398    {0, 12, 10, 11, 16},
399    {1, 17, 8, 9, 13},
400    {2, 14, 9, 8, 18},
401    {3, 19, 11, 10, 15},
402    {4, 14, 2, 3, 15},
403    {5, 12, 0, 1, 13},
404    {6, 17, 1, 0, 16},
405    {7, 19, 3, 2, 18},
406    {8, 17, 6, 7, 18},
407    {9, 14, 4, 5, 13},
408    {10, 12, 5, 4, 15},
409    {11, 19, 7, 6, 16},
410  };
411  int i;
412
413  glColor3f(0.75, 0.75, 0.75);
414  for (i = 0; i < 12; ++i) {
415    GLfloat *p0, *p1, *p2, d;
416    GLfloat u[3], v[3], n[3];
417
418    p0 = &vertexes[polygons[i][0]][0];
419    p1 = &vertexes[polygons[i][1]][0];
420    p2 = &vertexes[polygons[i][2]][0];
421
422    u[0] = p2[0] - p1[0];
423    u[1] = p2[1] - p1[1];
424    u[2] = p2[2] - p1[2];
425
426    v[0] = p0[0] - p1[0];
427    v[1] = p0[1] - p1[1];
428    v[2] = p0[2] - p1[2];
429
430    n[0] = u[1] * v[2] - u[2] * v[1];
431    n[1] = u[2] * v[0] - u[0] * v[2];
432    n[2] = u[0] * v[1] - u[1] * v[0];
433
434    d = 1.0 / sqrt(n[0] * n[0] + n[1] * n[1] + n[2] * n[2]);
435    n[0] *= d;
436    n[1] *= d;
437    n[2] *= d;
438
439    glBegin(GL_POLYGON);
440    glNormal3fv(n);
441    glVertex3fv(p0);
442    glVertex3fv(p1);
443    glVertex3fv(p2);
444    glVertex3fv(vertexes[polygons[i][3]]);
445    glVertex3fv(vertexes[polygons[i][4]]);
446    glEnd();
447  }
448}
449
450static void
451drawSphere(void)
452{
453  int numMajor = 24;
454  int numMinor = 32;
455  float radius = 0.8;
456  double majorStep = (M_PI / numMajor);
457  double minorStep = (2.0 * M_PI / numMinor);
458  int i, j;
459
460  glColor3f(0.50, 0.50, 0.50);
461  for (i = 0; i < numMajor; ++i) {
462    double a = i * majorStep;
463    double b = a + majorStep;
464    double r0 = radius * sin(a);
465    double r1 = radius * sin(b);
466    GLfloat z0 = radius * cos(a);
467    GLfloat z1 = radius * cos(b);
468
469    glBegin(GL_TRIANGLE_STRIP);
470    for (j = 0; j <= numMinor; ++j) {
471      double c = j * minorStep;
472      GLfloat x = cos(c);
473      GLfloat y = sin(c);
474
475      glNormal3f((x * r0) / radius, (y * r0) / radius, z0 / radius);
476      glTexCoord2f(j / (GLfloat) numMinor, i / (GLfloat) numMajor);
477      glVertex3f(x * r0, y * r0, z0);
478
479      glNormal3f((x * r1) / radius, (y * r1) / radius, z1 / radius);
480      glTexCoord2f(j / (GLfloat) numMinor, (i + 1) / (GLfloat) numMajor);
481      glVertex3f(x * r1, y * r1, z1);
482    }
483    glEnd();
484  }
485}
486
487/*****************************************************************/
488
489float xmin = -0.035, xmax = 0.035;
490float ymin = -0.035, ymax = 0.035;
491float nnear = 0.1;
492float ffar = 1.9;
493float distance = -1.0;
494
495static void
496loadTextureProjection(int texUnit, GLfloat m[16])
497{
498  GLfloat mInverse[4][4];
499
500  /* Should use true inverse, but since m consists only of rotations, we can
501     just use the transpose. */
502  matrixTranspose((GLfloat *) mInverse, m);
503
504  ActiveTexture(GL_TEXTURE0_ARB + texUnit);
505  glMatrixMode(GL_TEXTURE);
506  glLoadIdentity();
507  glTranslatef(0.5, 0.5, 0.0);
508  glScalef(0.5, 0.5, 1.0);
509  glFrustum(xmin, xmax, ymin, ymax, nnear, ffar);
510  glTranslatef(0.0, 0.0, distance);
511  glMultMatrixf((GLfloat *) mInverse);
512  glMatrixMode(GL_MODELVIEW);
513}
514
515static void
516drawTextureProjection(void)
517{
518  float t = ffar / nnear;
519  GLfloat n[4][3];
520  GLfloat f[4][3];
521
522  n[0][0] = xmin;
523  n[0][1] = ymin;
524  n[0][2] = -(nnear + distance);
525
526  n[1][0] = xmax;
527  n[1][1] = ymin;
528  n[1][2] = -(nnear + distance);
529
530  n[2][0] = xmax;
531  n[2][1] = ymax;
532  n[2][2] = -(nnear + distance);
533
534  n[3][0] = xmin;
535  n[3][1] = ymax;
536  n[3][2] = -(nnear + distance);
537
538  f[0][0] = xmin * t;
539  f[0][1] = ymin * t;
540  f[0][2] = -(ffar + distance);
541
542  f[1][0] = xmax * t;
543  f[1][1] = ymin * t;
544  f[1][2] = -(ffar + distance);
545
546  f[2][0] = xmax * t;
547  f[2][1] = ymax * t;
548  f[2][2] = -(ffar + distance);
549
550  f[3][0] = xmin * t;
551  f[3][1] = ymax * t;
552  f[3][2] = -(ffar + distance);
553
554  glColor3f(1.0, 1.0, 0.0);
555  glBegin(GL_LINE_LOOP);
556  glVertex3fv(n[0]);
557  glVertex3fv(n[1]);
558  glVertex3fv(n[2]);
559  glVertex3fv(n[3]);
560  glVertex3fv(f[3]);
561  glVertex3fv(f[2]);
562  glVertex3fv(f[1]);
563  glVertex3fv(f[0]);
564  glVertex3fv(n[0]);
565  glVertex3fv(n[1]);
566  glVertex3fv(f[1]);
567  glVertex3fv(f[0]);
568  glVertex3fv(f[3]);
569  glVertex3fv(f[2]);
570  glVertex3fv(n[2]);
571  glVertex3fv(n[3]);
572  glEnd();
573}
574
575/*****************************************************************/
576
577static void
578initialize(void)
579{
580  GLfloat light0Pos[4] =
581  {0.3, 0.3, 0.0, 1.0};
582  GLfloat matAmb[4] =
583  {0.01, 0.01, 0.01, 1.00};
584  GLfloat matDiff[4] =
585  {0.65, 0.65, 0.65, 1.00};
586  GLfloat matSpec[4] =
587  {0.30, 0.30, 0.30, 1.00};
588  GLfloat matShine = 10.0;
589  GLfloat eyePlaneS[] =
590  {1.0, 0.0, 0.0, 0.0};
591  GLfloat eyePlaneT[] =
592  {0.0, 1.0, 0.0, 0.0};
593  GLfloat eyePlaneR[] =
594  {0.0, 0.0, 1.0, 0.0};
595  GLfloat eyePlaneQ[] =
596  {0.0, 0.0, 0.0, 1.0};
597  int i;
598
599  /* Setup Misc.  */
600  glClearColor(0.41, 0.41, 0.31, 0.0);
601
602  glEnable(GL_DEPTH_TEST);
603
604  /*  glLineWidth(2.0);*/
605
606  glCullFace(GL_FRONT);
607  glEnable(GL_CULL_FACE);
608
609  glMatrixMode(GL_PROJECTION);
610  glFrustum(-0.5, 0.5, -0.5, 0.5, 1, 3);
611  glMatrixMode(GL_MODELVIEW);
612  glTranslatef(0, 0, -2);
613
614  matrixIdentity((GLfloat *) objectXform);
615  for (i = 0; i < NumTextures; i++) {
616     matrixIdentity((GLfloat *) textureXform[i]);
617  }
618
619  glMatrixMode(GL_PROJECTION);
620  glPushMatrix();
621  glLoadIdentity();
622  glOrtho(0, 1, 0, 1, -1, 1);
623  glMatrixMode(GL_MODELVIEW);
624  glPushMatrix();
625  glLoadIdentity();
626
627  glRasterPos2i(0, 0);
628
629  glPopMatrix();
630  glMatrixMode(GL_PROJECTION);
631  glPopMatrix();
632  glMatrixMode(GL_MODELVIEW);
633
634  /* Setup Lighting */
635  glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, matAmb);
636  glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, matDiff);
637  glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, matSpec);
638  glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, matShine);
639
640  glEnable(GL_COLOR_MATERIAL);
641
642  glLightfv(GL_LIGHT0, GL_POSITION, light0Pos);
643  glEnable(GL_LIGHT0);
644
645  glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
646  glEnable(GL_LIGHTING);
647
648  /* Setup Texture */
649
650  (*loadTexture) ();
651
652
653  for (i = 0; i < NumTextures; i++) {
654     ActiveTexture(GL_TEXTURE0_ARB + i);
655
656     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
657     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
658     glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
659
660     glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
661     glTexGenfv(GL_S, GL_EYE_PLANE, eyePlaneS);
662
663     glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
664     glTexGenfv(GL_T, GL_EYE_PLANE, eyePlaneT);
665
666     glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
667     glTexGenfv(GL_R, GL_EYE_PLANE, eyePlaneR);
668
669     glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
670     glTexGenfv(GL_Q, GL_EYE_PLANE, eyePlaneQ);
671  }
672}
673
674static void
675display(void)
676{
677  int i;
678
679  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
680
681  if (textureEnabled) {
682    if (mode == MoveTexture || mode == MoveView) {
683      /* Have OpenGL compute the new transformation (simple but slow). */
684      for (i = 0; i < NumTextures; i++) {
685        glPushMatrix();
686        glLoadIdentity();
687#if 0
688        if (i & 1)
689           glRotatef(angle, axis[0], axis[1], axis[2]);
690        else
691#endif
692           glRotatef(angle*(i+1), axis[0], axis[1], axis[2]);
693
694        glMultMatrixf((GLfloat *) textureXform[i]);
695        glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat *) textureXform[i]);
696        glPopMatrix();
697      }
698    }
699    for (i = 0; i < NumTextures; i++) {
700       loadTextureProjection(i, (GLfloat *) textureXform[i]);
701    }
702
703    if (showProjection) {
704      for (i = 0; i < NumTextures; i++) {
705        ActiveTexture(GL_TEXTURE0_ARB + i);
706        glPushMatrix();
707        glMultMatrixf((GLfloat *) textureXform[i]);
708        glDisable(GL_LIGHTING);
709        drawTextureProjection();
710        glEnable(GL_LIGHTING);
711        glPopMatrix();
712      }
713    }
714    for (i = 0; i < NumTextures; i++) {
715      ActiveTexture(GL_TEXTURE0_ARB + i);
716      glEnable(GL_TEXTURE_2D);
717      glEnable(GL_TEXTURE_GEN_S);
718      glEnable(GL_TEXTURE_GEN_T);
719      glEnable(GL_TEXTURE_GEN_R);
720      glEnable(GL_TEXTURE_GEN_Q);
721    }
722  }
723  if (mode == MoveObject || mode == MoveView) {
724    /* Have OpenGL compute the new transformation (simple but slow). */
725    glPushMatrix();
726    glLoadIdentity();
727    glRotatef(angle, axis[0], axis[1], axis[2]);
728    glMultMatrixf((GLfloat *) objectXform);
729    glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat *) objectXform);
730    glPopMatrix();
731  }
732  glPushMatrix();
733  glMultMatrixf((GLfloat *) objectXform);
734  (*drawObject) ();
735  glPopMatrix();
736
737  for (i = 0; i < NumTextures; i++) {
738    ActiveTexture(GL_TEXTURE0_ARB + i);
739    glDisable(GL_TEXTURE_2D);
740    glDisable(GL_TEXTURE_GEN_S);
741    glDisable(GL_TEXTURE_GEN_T);
742    glDisable(GL_TEXTURE_GEN_R);
743    glDisable(GL_TEXTURE_GEN_Q);
744  }
745
746  if (zoomFactor > 1.0) {
747    glDisable(GL_DEPTH_TEST);
748    glCopyPixels(0, 0, winWidth / zoomFactor, winHeight / zoomFactor, GL_COLOR);
749    glEnable(GL_DEPTH_TEST);
750  }
751  glFlush();
752  glutSwapBuffers();
753  checkErrors();
754}
755
756/*****************************************************************/
757
758/* simple trackball-like motion control */
759static float lastPos[3];
760static int lastTime;
761
762static void
763ptov(int x, int y, int width, int height, float v[3])
764{
765  float d, a;
766
767  /* project x,y onto a hemi-sphere centered within width, height */
768  v[0] = (2.0 * x - width) / width;
769  v[1] = (height - 2.0 * y) / height;
770  d = sqrt(v[0] * v[0] + v[1] * v[1]);
771  v[2] = cos((M_PI / 2.0) * ((d < 1.0) ? d : 1.0));
772  a = 1.0 / sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
773  v[0] *= a;
774  v[1] *= a;
775  v[2] *= a;
776}
777
778static void
779startMotion(int x, int y, int but, int time)
780{
781  if (but == GLUT_LEFT_BUTTON) {
782    mode = MoveView;
783  } else if (but == GLUT_MIDDLE_BUTTON) {
784    mode = MoveTexture;
785  } else {
786    return;
787  }
788
789  lastTime = time;
790  ptov(x, y, winWidth, winHeight, lastPos);
791}
792
793static void
794animate(void)
795{
796  glutPostRedisplay();
797}
798
799static void
800vis(int visible)
801{
802  if (visible == GLUT_VISIBLE) {
803    if (redrawContinuously)
804      glutIdleFunc(animate);
805  } else {
806    if (redrawContinuously)
807      glutIdleFunc(NULL);
808  }
809}
810
811static void
812stopMotion(int but, int time)
813{
814  if ((but == GLUT_LEFT_BUTTON && mode == MoveView) ||
815    (but == GLUT_MIDDLE_BUTTON && mode == MoveTexture)) {
816  } else {
817    return;
818  }
819
820  if (time == lastTime) {
821     /*    redrawContinuously = GL_TRUE;*/
822    glutIdleFunc(animate);
823  } else {
824    angle = 0.0;
825    redrawContinuously = GL_FALSE;
826    glutIdleFunc(0);
827  }
828  if (!redrawContinuously) {
829    mode = MoveNone;
830  }
831}
832
833static void
834trackMotion(int x, int y)
835{
836  float curPos[3], dx, dy, dz;
837
838  ptov(x, y, winWidth, winHeight, curPos);
839
840  dx = curPos[0] - lastPos[0];
841  dy = curPos[1] - lastPos[1];
842  dz = curPos[2] - lastPos[2];
843  angle = 90.0 * sqrt(dx * dx + dy * dy + dz * dz);
844
845  axis[0] = lastPos[1] * curPos[2] - lastPos[2] * curPos[1];
846  axis[1] = lastPos[2] * curPos[0] - lastPos[0] * curPos[2];
847  axis[2] = lastPos[0] * curPos[1] - lastPos[1] * curPos[0];
848
849  lastTime = glutGet(GLUT_ELAPSED_TIME);
850  lastPos[0] = curPos[0];
851  lastPos[1] = curPos[1];
852  lastPos[2] = curPos[2];
853  glutPostRedisplay();
854}
855
856/*****************************************************************/
857
858static void
859object(void)
860{
861  static int object;
862
863  object++;
864  object %= 3;
865  switch (object) {
866  case 0:
867    drawObject = drawCube;
868    break;
869  case 1:
870    drawObject = drawDodecahedron;
871    break;
872  case 2:
873    drawObject = drawSphere;
874    break;
875  default:
876    break;
877  }
878}
879
880static void
881nop(void)
882{
883}
884
885static void
886texture(void)
887{
888  static int texture = 0;
889
890  texture++;
891  texture %= 3;
892  if (texture == 1 && texFilename == NULL) {
893    /* Skip file texture if not loaded. */
894    texture++;
895  }
896  switch (texture) {
897  case 0:
898    loadTexture = nop;
899    textureEnabled = GL_FALSE;
900    break;
901  case 1:
902    loadTexture = loadImageTextures;
903    (*loadTexture) ();
904    textureEnabled = GL_TRUE;
905    break;
906  case 2:
907    loadTexture = loadSpotlightTexture;
908    (*loadTexture) ();
909    textureEnabled = GL_TRUE;
910    break;
911  default:
912    break;
913  }
914}
915
916static void
917help(void)
918{
919  printf("'h'   - help\n");
920  printf("'l'   - toggle linear/nearest filter\n");
921  printf("'s'   - toggle projection frustum\n");
922  printf("'t'   - toggle projected texture\n");
923  printf("'o'   - toggle object\n");
924  printf("'z'   - increase zoom factor\n");
925  printf("'Z'   - decrease zoom factor\n");
926  printf("left mouse     - move view\n");
927  printf("middle mouse   - move projection\n");
928}
929
930/* ARGSUSED1 */
931static void
932key(unsigned char key, int x, int y)
933{
934  switch (key) {
935  case '\033':
936    exit(0);
937    break;
938  case 'l':
939    linearFilter = !linearFilter;
940    (*loadTexture) ();
941    break;
942  case 's':
943    showProjection = !showProjection;
944    break;
945  case 't':
946    texture();
947    break;
948  case 'o':
949    object();
950    break;
951  case 'z':
952    zoomFactor += 1.0;
953    glPixelZoom(zoomFactor, zoomFactor);
954    glViewport(0, 0, winWidth / zoomFactor, winHeight / zoomFactor);
955    break;
956  case 'Z':
957    zoomFactor -= 1.0;
958    if (zoomFactor < 1.0)
959      zoomFactor = 1.0;
960    glPixelZoom(zoomFactor, zoomFactor);
961    glViewport(0, 0, winWidth / zoomFactor, winHeight / zoomFactor);
962    break;
963  case 'h':
964    help();
965    break;
966  }
967  glutPostRedisplay();
968}
969
970static void
971mouse(int button, int state, int x, int y)
972{
973  if (state == GLUT_DOWN)
974    startMotion(x, y, button, glutGet(GLUT_ELAPSED_TIME));
975  else if (state == GLUT_UP)
976    stopMotion(button, glutGet(GLUT_ELAPSED_TIME));
977  glutPostRedisplay();
978}
979
980static void
981reshape(int w, int h)
982{
983  winWidth = w;
984  winHeight = h;
985  glViewport(0, 0, w / zoomFactor, h / zoomFactor);
986}
987
988
989static void
990menu(int selection)
991{
992  if (selection == 666) {
993    exit(0);
994  }
995  key((unsigned char) selection, 0, 0);
996}
997
998int
999main(int argc, char **argv)
1000{
1001  glutInitWindowSize(500,500);
1002  glutInit(&argc, argv);
1003  glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE);
1004  (void) glutCreateWindow("projtex");
1005  glewInit();
1006
1007  if (argc > 1) {
1008     NumTextures = atoi(argv[1]);
1009  }
1010  assert(NumTextures <= MAX_TEX);
1011
1012  loadTexture = loadImageTextures;
1013  drawObject = drawCube;
1014  initialize();
1015  glutDisplayFunc(display);
1016  glutKeyboardFunc(key);
1017  glutReshapeFunc(reshape);
1018  glutMouseFunc(mouse);
1019  glutMotionFunc(trackMotion);
1020  glutVisibilityFunc(vis);
1021  glutCreateMenu(menu);
1022  glutAddMenuEntry("Toggle showing projection", 's');
1023  glutAddMenuEntry("Switch texture", 't');
1024  glutAddMenuEntry("Switch object", 'o');
1025  glutAddMenuEntry("Toggle filtering", 'l');
1026  glutAddMenuEntry("Quit", 666);
1027  glutAttachMenu(GLUT_RIGHT_BUTTON);
1028  texture();
1029  glutMainLoop();
1030  return 0;             /* ANSI C requires main to return int. */
1031}
1032