1/*-----------------------------
2 * stex3d.c GL example of the mesa 3d-texture extention to simulate procedural
3 *            texturing, it uses a perlin noise and turbulence functions.
4 *
5 * Author:   Daniel Barrero
6 *           barrero@irit.fr
7 *           dbarrero@pegasus.uniandes.edu.co
8 *
9 * Converted to GLUT by brianp on 1/1/98
10 * Massive clean-up on 2002/10/23 by brianp
11 *
12 *
13 * cc stex3d.c -o stex3d -lglut -lMesaGLU -lMesaGL -lX11 -lXext -lm
14 *
15 *---------------------------- */
16
17#include <string.h>
18#include <stdio.h>
19#include <stdlib.h>
20#include <math.h>
21#include <GL/glew.h>
22#include "glut_wrap.h"
23
24
25#ifndef M_PI
26#define M_PI            3.14159265358979323846
27#endif
28
29#define NOISE_TEXTURE 1
30#define GRADIENT_TEXTURE 2
31
32#define TORUS 1
33#define SPHERE 2
34
35static int tex_width=64, tex_height=64, tex_depth=64;
36static float angx=0, angy=0, angz=0;
37static int texgen = 2, animate = 1, smooth = 1, wireframe = 0;
38static int CurTexture = NOISE_TEXTURE, CurObject = TORUS;
39static GLenum Filter = GL_LINEAR;
40
41
42static void
43BuildTorus(void)
44{
45   GLint i, j;
46   float theta1, phi1, theta2, phi2, rings, sides;
47   float v0[03], v1[3], v2[3], v3[3];
48   float t0[03], t1[3], t2[3], t3[3];
49   float n0[3], n1[3], n2[3], n3[3];
50   float innerRadius = 0.25;
51   float outerRadius = 0.5;
52   float scalFac;
53
54   rings = 16;
55   sides = 12;
56   scalFac = 1 / (outerRadius * 2);
57
58   glNewList(TORUS, GL_COMPILE);
59   for (i = 0; i < rings; i++) {
60      theta1 = (float) i *2.0 * M_PI / rings;
61      theta2 = (float) (i + 1) * 2.0 * M_PI / rings;
62      for (j = 0; j < sides; j++) {
63	 phi1 = (float) j *2.0 * M_PI / sides;
64	 phi2 = (float) (j + 1) * 2.0 * M_PI / sides;
65
66	 v0[0] = cos(theta1) * (outerRadius + innerRadius * cos(phi1));
67	 v0[1] = -sin(theta1) * (outerRadius + innerRadius * cos(phi1));
68	 v0[2] = innerRadius * sin(phi1);
69
70	 v1[0] = cos(theta2) * (outerRadius + innerRadius * cos(phi1));
71	 v1[1] = -sin(theta2) * (outerRadius + innerRadius * cos(phi1));
72	 v1[2] = innerRadius * sin(phi1);
73	 v2[0] = cos(theta2) * (outerRadius + innerRadius * cos(phi2));
74	 v2[1] = -sin(theta2) * (outerRadius + innerRadius * cos(phi2));
75	 v2[2] = innerRadius * sin(phi2);
76
77	 v3[0] = cos(theta1) * (outerRadius + innerRadius * cos(phi2));
78	 v3[1] = -sin(theta1) * (outerRadius + innerRadius * cos(phi2));
79	 v3[2] = innerRadius * sin(phi2);
80
81	 n0[0] = cos(theta1) * (cos(phi1));
82	 n0[1] = -sin(theta1) * (cos(phi1));
83	 n0[2] = sin(phi1);
84
85	 n1[0] = cos(theta2) * (cos(phi1));
86	 n1[1] = -sin(theta2) * (cos(phi1));
87	 n1[2] = sin(phi1);
88
89	 n2[0] = cos(theta2) * (cos(phi2));
90	 n2[1] = -sin(theta2) * (cos(phi2));
91	 n2[2] = sin(phi2);
92
93	 n3[0] = cos(theta1) * (cos(phi2));
94	 n3[1] = -sin(theta1) * (cos(phi2));
95	 n3[2] = sin(phi2);
96
97	 t0[0] = v0[0] * scalFac + 0.5;
98	 t0[1] = v0[1] * scalFac + 0.5;
99	 t0[2] = v0[2] * scalFac + 0.5;
100
101	 t1[0] = v1[0] * scalFac + 0.5;
102	 t1[1] = v1[1] * scalFac + 0.5;
103	 t1[2] = v1[2] * scalFac + 0.5;
104
105	 t2[0] = v2[0] * scalFac + 0.5;
106	 t2[1] = v2[1] * scalFac + 0.5;
107	 t2[2] = v2[2] * scalFac + 0.5;
108
109	 t3[0] = v3[0] * scalFac + 0.5;
110	 t3[1] = v3[1] * scalFac + 0.5;
111	 t3[2] = v3[2] * scalFac + 0.5;
112
113	 glBegin(GL_POLYGON);
114	 glNormal3fv(n3);
115	 glTexCoord3fv(t3);
116	 glVertex3fv(v3);
117	 glNormal3fv(n2);
118	 glTexCoord3fv(t2);
119	 glVertex3fv(v2);
120	 glNormal3fv(n1);
121	 glTexCoord3fv(t1);
122	 glVertex3fv(v1);
123	 glNormal3fv(n0);
124	 glTexCoord3fv(t0);
125	 glVertex3fv(v0);
126	 glEnd();
127      }
128   }
129   glEndList();
130}
131
132
133/*--------------------------------------------------------------------
134 noise function over R3 - implemented by a pseudorandom tricubic spline
135              EXCERPTED FROM SIGGRAPH 92, COURSE 23
136                        PROCEDURAL MODELING
137                             Ken Perlin
138                           New York University
139----------------------------------------------------------------------*/
140
141
142#define DOT(a,b) (a[0] * b[0] + a[1] * b[1] + a[2] * b[2])
143#define B 128
144static int p[B + B + 2];
145static float g[B + B + 2][3];
146#define setup(i,b0,b1,r0,r1) \
147        t = vec[i] + 10000.; \
148        b0 = ((int)t) & (B-1); \
149        b1 = (b0+1) & (B-1); \
150        r0 = t - (int)t; \
151        r1 = r0 - 1.;
152
153static float
154noise3(float vec[3])
155{
156   int bx0, bx1, by0, by1, bz0, bz1, b00, b10, b01, b11;
157   float rx0, rx1, ry0, ry1, rz0, rz1, *q, sx, sy, sz, a, b, c, d, t, u, v;
158   register int i, j;
159
160   setup(0, bx0, bx1, rx0, rx1);
161   setup(1, by0, by1, ry0, ry1);
162   setup(2, bz0, bz1, rz0, rz1);
163
164   i = p[bx0];
165   j = p[bx1];
166
167   b00 = p[i + by0];
168   b10 = p[j + by0];
169   b01 = p[i + by1];
170   b11 = p[j + by1];
171
172#define at(rx,ry,rz) ( rx * q[0] + ry * q[1] + rz * q[2] )
173#define surve(t) ( t * t * (3. - 2. * t) )
174#define lerp(t, a, b) ( a + t * (b - a) )
175
176   sx = surve(rx0);
177   sy = surve(ry0);
178   sz = surve(rz0);
179
180   q = g[b00 + bz0];
181   u = at(rx0, ry0, rz0);
182   q = g[b10 + bz0];
183   v = at(rx1, ry0, rz0);
184   a = lerp(sx, u, v);
185
186   q = g[b01 + bz0];
187   u = at(rx0, ry1, rz0);
188   q = g[b11 + bz0];
189   v = at(rx1, ry1, rz0);
190   b = lerp(sx, u, v);
191
192   c = lerp(sy, a, b);		/* interpolate in y at lo x */
193
194   q = g[b00 + bz1];
195   u = at(rx0, ry0, rz1);
196   q = g[b10 + bz1];
197   v = at(rx1, ry0, rz1);
198   a = lerp(sx, u, v);
199
200   q = g[b01 + bz1];
201   u = at(rx0, ry1, rz1);
202   q = g[b11 + bz1];
203   v = at(rx1, ry1, rz1);
204   b = lerp(sx, u, v);
205
206   d = lerp(sy, a, b);		/* interpolate in y at hi x */
207
208   return 1.5 * lerp(sz, c, d);	/* interpolate in z */
209}
210
211static void
212initNoise(void)
213{
214   /*long random(); */
215   int i, j, k;
216   float v[3], s;
217
218   /* Create an array of random gradient vectors uniformly on the unit sphere */
219   /*srandom(1); */
220   srand(1);
221   for (i = 0; i < B; i++) {
222      do {			/* Choose uniformly in a cube */
223	 for (j = 0; j < 3; j++)
224	    v[j] = (float) ((rand() % (B + B)) - B) / B;
225	 s = DOT(v, v);
226      } while (s > 1.0);	/* If not in sphere try again */
227      s = sqrt(s);
228      for (j = 0; j < 3; j++)	/* Else normalize */
229	 g[i][j] = v[j] / s;
230   }
231
232   /* Create a pseudorandom permutation of [1..B] */
233   for (i = 0; i < B; i++)
234      p[i] = i;
235   for (i = B; i > 0; i -= 2) {
236      k = p[i];
237      p[i] = p[j = rand() % B];
238      p[j] = k;
239   }
240
241   /* Extend g and p arrays to allow for faster indexing */
242   for (i = 0; i < B + 2; i++) {
243      p[B + i] = p[i];
244      for (j = 0; j < 3; j++)
245	 g[B + i][j] = g[i][j];
246   }
247}
248
249
250static float
251turbulence(float point[3], float lofreq, float hifreq)
252{
253   float freq, t, p[3];
254
255   p[0] = point[0] + 123.456;
256   p[1] = point[1];
257   p[2] = point[2];
258
259   t = 0;
260   for (freq = lofreq; freq < hifreq; freq *= 2.) {
261      t += fabs(noise3(p)) / freq;
262      p[0] *= 2.;
263      p[1] *= 2.;
264      p[2] *= 2.;
265   }
266   return t - 0.3;		/* readjust to make mean value = 0.0 */
267}
268
269
270static void
271create3Dtexture(void)
272{
273   unsigned char *voxels = NULL;
274   int i, j, k;
275   unsigned char *vp;
276   float vec[3];
277   int tmp;
278
279   printf("creating 3d textures...\n");
280   voxels =
281      (unsigned char *)
282      malloc((size_t) (4 * tex_width * tex_height * tex_depth));
283   vp = voxels;
284   for (i = 0; i < tex_width; i++) {
285      vec[0] = i;
286      for (j = 0; j < tex_height; j++) {
287	 vec[1] = j;
288	 for (k = 0; k < tex_depth; k++) {
289	    vec[2] = k;
290	    tmp = (sin(k * i * j + turbulence(vec, 0.01, 1)) + 1) * 127.5;
291	    *vp++ = 0;
292	    *vp++ = 0;
293	    *vp++ = tmp;
294	    *vp++ = tmp + 128;
295	 }
296      }
297   }
298
299   printf("setting up 3d texture...\n");
300
301   glBindTexture(GL_TEXTURE_3D, NOISE_TEXTURE);
302   glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_REPEAT);
303   glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_REPEAT);
304   glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_REPEAT);
305   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
306
307   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
308   glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA,
309		tex_width, tex_height, tex_depth,
310		0, GL_RGBA, GL_UNSIGNED_BYTE, voxels);
311   if (glGetError() == GL_OUT_OF_MEMORY)
312      printf("stex3d: Out of memory allocating %d x %d x %d RGBA texture",
313             tex_width, tex_height, tex_depth);
314
315   free(voxels);
316
317   printf("finished setting up 3d texture image.\n");
318}
319
320
321static void
322printHelp(void)
323{
324   printf("\nUsage: stex3d  <cmd line options>\n");
325   printf(" cmd line options:\n");
326   printf("      -wxxx   Width of the texture (Default=64)\n");
327   printf("      -hxxx   Height of the texture (Default=64)\n");
328   printf("      -dxxx   Depth of the texture (Default=64)\n");
329   printf(" Keyboard Options:\n");
330   printf("    up/down   rotate around X\n");
331   printf("  left/right  rotate around Y\n");
332   printf("      z/Z     rotate around Z\n");
333   printf("       a      toggle animation\n");
334   printf("       s      toggle smooth shading\n");
335   printf("       t      toggle texgen mode\n");
336   printf("       o      toggle object: torus/sphere\n");
337   printf("       i      toggle texture image: noise/gradient\n");
338   printf("       f      toggle linear/nearest filtering\n");
339}
340
341
342static GLenum
343parseCmdLine(int argc, char **argv)
344{
345   GLint i;
346
347   for (i = 1; i < argc; i++) {
348      if (strcmp(argv[i], "-help") == 0) {
349	 printHelp();
350	 return GL_FALSE;
351      }
352      else if (strstr(argv[i], "-w") != NULL) {
353	 tex_width = atoi((argv[i]) + 2);
354      }
355      else if (strstr(argv[i], "-h") != NULL) {
356	 tex_height = atoi((argv[i]) + 2);
357      }
358      else if (strstr(argv[i], "-d") != NULL) {
359	 tex_depth = atoi((argv[i]) + 2);
360      }
361      else {
362	 printf("%s (Bad option).\n", argv[i]);
363	 printHelp();
364	 return GL_FALSE;
365      }
366   }
367   if (tex_width == 0 || tex_height == 0 || tex_depth == 0) {
368      printf("%s (Bad option).\n", "size parameters can't be 0");
369      printHelp();
370      return GL_FALSE;
371   }
372   return GL_TRUE;
373}
374
375
376static void
377drawScene(void)
378{
379   static const GLfloat sPlane[4] = { 0.5, 0, 0, -.5 };
380   static const GLfloat tPlane[4] = { 0, 0.5, 0, -.5 };
381   static const GLfloat rPlane[4] = { 0, 0, 0.5, -.5 };
382
383   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
384   glPushMatrix();
385   if (texgen == 2) {
386      glTexGenfv(GL_S, GL_EYE_PLANE, sPlane);
387      glTexGenfv(GL_T, GL_EYE_PLANE, tPlane);
388      glTexGenfv(GL_R, GL_EYE_PLANE, rPlane);
389   }
390
391   glRotatef(angx, 1.0, 0.0, 0.0);
392   glRotatef(angy, 0.0, 1.0, 0.0);
393   glRotatef(angz, 0.0, 0.0, 1.0);
394
395   if (texgen == 1) {
396      glTexGenfv(GL_S, GL_EYE_PLANE, sPlane);
397      glTexGenfv(GL_T, GL_EYE_PLANE, tPlane);
398      glTexGenfv(GL_R, GL_EYE_PLANE, rPlane);
399   }
400
401   if (texgen) {
402      glEnable(GL_TEXTURE_GEN_S);
403      glEnable(GL_TEXTURE_GEN_T);
404      glEnable(GL_TEXTURE_GEN_R);
405   }
406   else {
407      glDisable(GL_TEXTURE_GEN_S);
408      glDisable(GL_TEXTURE_GEN_T);
409      glDisable(GL_TEXTURE_GEN_R);
410   }
411
412   glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, Filter);
413   glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, Filter);
414
415   glCallList(CurObject);
416   glPopMatrix();
417
418   glutSwapBuffers();
419}
420
421
422static void
423resize(int w, int h)
424{
425   float ar = (float) w / (float) h;
426   float ax = 0.6 * ar;
427   float ay = 0.6;
428   glViewport(0, 0, (GLint) w, (GLint) h);
429   glMatrixMode(GL_PROJECTION);
430   glLoadIdentity();
431   glFrustum(-ax, ax, -ay, ay, 2, 20);
432   /*glOrtho(-2, 2, -2, 2, -10, 10);*/
433   glMatrixMode(GL_MODELVIEW);
434   glLoadIdentity();
435   glTranslatef(0, 0, -4);
436}
437
438
439static void
440Idle(void)
441{
442   float t = glutGet(GLUT_ELAPSED_TIME);
443   angx = 0.01 * t;
444   angy = 0.03 * t;
445   angz += 0;
446   glutPostRedisplay();
447}
448
449
450static void
451SpecialKey(int k, int x, int y)
452{
453  switch (k) {
454  case GLUT_KEY_UP:
455    angx += 5.0;
456    break;
457  case GLUT_KEY_DOWN:
458    angx -= 5.0;
459    break;
460  case GLUT_KEY_LEFT:
461    angy += 5.0;
462    break;
463  case GLUT_KEY_RIGHT:
464    angy -= 5.0;
465    break;
466  default:
467    return;
468  }
469  glutPostRedisplay();
470}
471
472
473static void
474KeyHandler(unsigned char key, int x, int y)
475{
476   static const char *mode[] = {
477      "glTexCoord3f (no texgen)",
478      "texgen fixed to object coords",
479      "texgen fixed to eye coords"
480   };
481   (void) x;
482   (void) y;
483   switch (key) {
484   case 27:
485   case 'q':
486   case 'Q':			/* quit game. */
487      exit(0);
488      break;
489   case 'z':
490      angz += 10;
491      break;
492   case 'Z':
493      angz -= 10;
494      break;
495   case 's':
496      smooth = !smooth;
497      if (smooth)
498         glShadeModel(GL_SMOOTH);
499      else
500         glShadeModel(GL_FLAT);
501      break;
502   case 't':
503      texgen++;
504      if (texgen > 2)
505         texgen = 0;
506      printf("Texgen: %s\n", mode[texgen]);
507      break;
508   case 'o':
509      if (CurObject == TORUS)
510         CurObject = SPHERE;
511      else
512         CurObject = TORUS;
513      break;
514   case 'f':
515      if (Filter == GL_LINEAR)
516         Filter = GL_NEAREST;
517      else
518         Filter = GL_LINEAR;
519      break;
520   case 'i':
521      if (CurTexture == NOISE_TEXTURE)
522         CurTexture = GRADIENT_TEXTURE;
523      else
524         CurTexture = NOISE_TEXTURE;
525      glBindTexture(GL_TEXTURE_3D, CurTexture);
526      break;
527   case 'a':
528   case ' ':
529      animate = !animate;
530      if (animate)
531         glutIdleFunc(Idle);
532      else
533         glutIdleFunc(NULL);
534      break;
535   case 'w':
536      wireframe = !wireframe;
537      if (wireframe)
538         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
539      else
540         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
541      break;
542   default:
543      break;
544   }
545   glutPostRedisplay();
546}
547
548
549static void
550create3Dgradient(void)
551{
552   unsigned char *v;
553   int i, j, k;
554   unsigned char *voxels = NULL;
555
556   voxels = (unsigned char *) malloc(4 * tex_width * tex_height * tex_depth);
557   v = voxels;
558
559   for (i = 0; i < tex_depth; i++) {
560      for (j = 0; j < tex_height; j++) {
561	 for (k = 0; k < tex_width; k++) {
562	    GLint r = (255 * i) / (tex_depth - 1);
563	    GLint g = (255 * j) / (tex_height - 1);
564	    GLint b = (255 * k) / (tex_width - 1);
565	    *v++ = r;
566	    *v++ = g;
567	    *v++ = b;
568	    *v++ = 255;
569	 }
570      }
571   }
572
573
574   glBindTexture(GL_TEXTURE_3D, GRADIENT_TEXTURE);
575   glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_REPEAT);
576   glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_REPEAT);
577   glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_REPEAT);
578   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
579
580   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
581   glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA,
582		tex_width, tex_height, tex_depth,
583		0, GL_RGBA, GL_UNSIGNED_BYTE, voxels);
584   if (glGetError() == GL_OUT_OF_MEMORY)
585      printf("stex3d: Out of memory allocating %d x %d x %d RGBA texture",
586             tex_width, tex_height, tex_depth);
587
588   free(voxels);
589}
590
591
592
593static void
594init(void)
595{
596   static const GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };
597   static const GLfloat mat_shininess[] = { 25.0 };
598   static const GLfloat gray[] = { 0.6, 0.6, 0.6, 0.0 };
599   static const GLfloat white[] = { 1.0, 1.0, 1.0, 0.0 };
600   static const GLfloat light_position[] = { 0.0, 1.0, 1.0, 0.0 };
601
602   int max;
603
604   /* see if we have OpenGL 1.2 or later, for 3D texturing */
605   {
606      const char *version = (const char *) glGetString(GL_VERSION);
607      if (strncmp(version, "1.0", 3) == 0 || strncmp(version, "1.1", 3) == 0) {
608	 printf("Sorry, OpenGL 1.2 or later is required\n");
609	 exit(1);
610      }
611   }
612   printf("GL_RENDERER: %s\n", (char *) glGetString(GL_RENDERER));
613   glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &max);
614   printf("GL_MAX_3D_TEXTURE_SIZE: %d\n", max);
615   printf("Current 3D texture size: %d x %d x %d\n",
616          tex_width, tex_height, tex_depth);
617
618   /* init light */
619   glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
620   glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
621   glLightfv(GL_LIGHT1, GL_POSITION, light_position);
622   glLightfv(GL_LIGHT1, GL_AMBIENT, gray);
623   glLightfv(GL_LIGHT1, GL_DIFFUSE, white);
624   glLightfv(GL_LIGHT1, GL_SPECULAR, white);
625   glColorMaterial(GL_FRONT, GL_DIFFUSE);
626   glEnable(GL_COLOR_MATERIAL);
627   glEnable(GL_LIGHTING);
628   glEnable(GL_LIGHT1);
629
630   glClearColor(.5, .5, .5, 0);
631
632   {
633      GLUquadricObj *q;
634      q = gluNewQuadric();
635      gluQuadricTexture( q, GL_TRUE );
636      glNewList(SPHERE, GL_COMPILE);
637      gluSphere( q, 0.95, 30, 15 );
638      glEndList();
639      gluDeleteQuadric(q);
640   }
641
642   BuildTorus();
643
644
645   create3Dgradient();
646
647   initNoise();
648   create3Dtexture();
649
650   glEnable(GL_TEXTURE_3D);
651
652   /*
653      glBlendFunc(GL_SRC_COLOR, GL_SRC_ALPHA);
654      glEnable(GL_BLEND);
655    */
656   glEnable(GL_DEPTH_TEST);
657
658   glColor3f(0.6, 0.7, 0.8);
659}
660
661
662int
663main(int argc, char **argv)
664{
665   glutInit(&argc, argv);
666
667   if (parseCmdLine(argc, argv) == GL_FALSE) {
668      exit(0);
669   }
670
671   glutInitWindowPosition(0, 0);
672   glutInitWindowSize(400, 400);
673   glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
674
675   if (glutCreateWindow("stex3d") <= 0) {
676      exit(0);
677   }
678
679   glewInit();
680
681   init();
682
683   printHelp();
684
685   glutReshapeFunc(resize);
686   glutKeyboardFunc(KeyHandler);
687   glutSpecialFunc(SpecialKey);
688   glutDisplayFunc(drawScene);
689   if (animate)
690      glutIdleFunc(Idle);
691   glutMainLoop();
692   return 0;
693}
694
695