1/*
2 * Test rendering into a cube map texture with FBOs.
3 *
4 * Brian Paul
5 * May 2011
6 */
7
8#include <assert.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <math.h>
13#include <GL/glew.h>
14#include "glut_wrap.h"
15#include "shaderutil.h"
16
17
18#define DEG_TO_RAD (3.14159 / 180.0)
19
20#define ELEMENTS(array) (sizeof(array) / sizeof(array[0]))
21
22static GLint WindowWidth = 750, WindowHeight = 450;
23static GLint Win;
24
25static GLfloat Xrot = 10, Yrot = 0, Zrot = 0;
26
27static GLfloat GroundColor[4] = {0.5, 0.5, 0.75, 1};
28static GLfloat GroundY = -1.0f;
29
30static GLfloat ViewDist = 30.0;
31
32static GLfloat SpherePos[4] = { 0, 2.5, 0, 1 };
33static GLfloat LightDist = 15;
34static GLfloat LightLatitude = 30.0;
35static GLfloat LightLongitude = 45.0;
36static GLfloat LightPos[4];
37
38static GLboolean Anim = GL_TRUE;
39
40static GLboolean NeedNewCubeMap = GL_TRUE;
41static GLuint CubeTexture;
42
43
44struct cylinder
45{
46   float PosX, PosY;
47   float Radius, Height;
48   float Color[4];
49};
50
51#define NUM_CYLINDERS 30
52
53static struct cylinder Cylinders[NUM_CYLINDERS];
54
55
56static float
57RandomFloat(float min, float max)
58{
59   const int k = 10000;
60   float t = (rand() % k) / (float) (k - 1);  /* t in [0,1] */
61   float r = min + t * (max - min);
62   return r;
63}
64
65
66static void
67CheckError(int line)
68{
69   GLenum err = glGetError();
70   if (err) {
71      printf("GL Error 0x%x at line %d\n", (int) err, line);
72   }
73}
74
75
76static void
77GenerateCylinders(void)
78{
79   int i;
80   for (i = 0; i < NUM_CYLINDERS; i++) {
81      float r = RandomFloat(5.0, 9.0);
82      float a = RandomFloat(0, 2 * M_PI);
83
84      Cylinders[i].PosX = r * cos(a);
85      Cylinders[i].PosY = r * sin(a);
86      Cylinders[i].Radius = RandomFloat(0.25, 1.0);
87      Cylinders[i].Height = RandomFloat(1.0, 7.0);
88      Cylinders[i].Color[0] = RandomFloat(0.25, 1.0);
89      Cylinders[i].Color[1] = RandomFloat(0.25, 1.0);
90      Cylinders[i].Color[2] = RandomFloat(0.25, 1.0);
91      Cylinders[i].Color[3] = RandomFloat(0.25, 1.0);
92   }
93}
94
95static void
96DrawCylinder(const struct cylinder *cyl)
97{
98   glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, cyl->Color);
99   glPushMatrix();
100   glTranslatef(cyl->PosX, GroundY, cyl->PosY);
101   glRotatef(-90, 1, 0, 0);
102   glutSolidCone(cyl->Radius, cyl->Height, 12, 2);
103   glPopMatrix();
104}
105
106
107static void
108DrawLightSource(void)
109{
110   glPushMatrix();
111   glTranslatef(LightPos[0], LightPos[1], LightPos[2]);
112   glColor3f(1, 1, 1);
113   glutSolidSphere(0.25, 8, 8);
114   glPopMatrix();
115}
116
117
118static void
119DrawScene(void)
120{
121   int i;
122
123   glEnable(GL_LIGHTING);
124
125   for (i = 0; i < NUM_CYLINDERS; i++) {
126      DrawCylinder(&Cylinders[i]);
127   }
128
129   glDisable(GL_LIGHTING);
130
131   if (1)
132      DrawLightSource();
133
134   /* ground plane */
135   if (1) {
136      GLfloat k = 10.0, g = GroundY;
137      CheckError(__LINE__);
138      glPushMatrix();
139      glColor3fv(GroundColor);
140      glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, GroundColor);
141      glNormal3f(0, 1, 0);
142      glBegin(GL_POLYGON);
143      glVertex3f(-k, g, -k);
144      glVertex3f( k, g, -k);
145      glVertex3f( k, g,  k);
146      glVertex3f(-k, g,  k);
147      glEnd();
148      glPopMatrix();
149   }
150
151   CheckError(__LINE__);
152}
153
154
155/*
156 * Draw sphere with cube map texture that reflects rest of the scene.
157 */
158static void
159DrawShinySphere(void)
160{
161   glEnable(GL_TEXTURE_CUBE_MAP);
162
163#if 0
164   glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB);
165   glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB);
166   glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB);
167#else
168   glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB);
169   glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB);
170   glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB);
171#endif
172   glEnable(GL_TEXTURE_GEN_S);
173   glEnable(GL_TEXTURE_GEN_T);
174   glEnable(GL_TEXTURE_GEN_R);
175
176   glPushMatrix();
177   glTranslatef(SpherePos[0], SpherePos[1], SpherePos[2]);
178   glColor3f(0.75, 0.75, 0.75);
179
180   if (1) {
181      glMatrixMode(GL_TEXTURE);
182      glLoadIdentity();
183      glRotatef(-Yrot, 0, 1, 0);
184      glRotatef(-Xrot, 1, 0, 0);
185   }
186
187   glutSolidSphere(2.5, 16, 16);
188
189   glMatrixMode(GL_MODELVIEW);
190
191   glPopMatrix();
192
193   glDisable(GL_TEXTURE_CUBE_MAP);
194   glDisable(GL_TEXTURE_GEN_S);
195   glDisable(GL_TEXTURE_GEN_T);
196   glDisable(GL_TEXTURE_GEN_R);
197}
198
199
200/**
201 * Setup modelview and projection for drawing a cube face.
202 */
203static void
204SetupCubeFaceView(GLenum face, const GLfloat centerPos[4])
205{
206   GLfloat near_val = 0.5, far_val = 20.0;
207
208   glMatrixMode(GL_PROJECTION);
209   glLoadIdentity();
210   glFrustum(-near_val, near_val, -near_val, near_val, near_val, far_val);
211
212   glMatrixMode(GL_MODELVIEW);
213   glLoadIdentity();
214
215   switch (face) {
216   case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
217      glRotatef(180, 1, 0, 0);
218      glRotatef(-90, 0, 1, 0);
219      break;
220   case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
221      glRotatef(180, 1, 0, 0);
222      glRotatef(90, 0, 1, 0);
223      break;
224   case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
225      glRotatef(-90, 1, 0, 0);
226      break;
227   case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
228      glRotatef(90, 1, 0, 0);
229      break;
230   case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
231      glRotatef(180, 1, 0, 0);
232      break;
233   case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
234      glRotatef(0, 1, 0, 0);
235      glRotatef(180, 0, 0, 1);
236      break;
237   }
238
239   glTranslatef(-centerPos[0], -centerPos[1], -centerPos[2]);
240
241   glLightfv(GL_LIGHT0, GL_POSITION, LightPos);
242}
243
244
245
246static void
247ComputeLightPos(GLfloat dist, GLfloat latitude, GLfloat longitude,
248                GLfloat pos[4])
249{
250   pos[0] = dist * sin(longitude * DEG_TO_RAD);
251   pos[1] = dist * sin(latitude * DEG_TO_RAD);
252   pos[2] = dist * cos(latitude * DEG_TO_RAD) * cos(longitude * DEG_TO_RAD);
253   pos[3] = 1;
254}
255
256
257static GLint
258ChooseCubeSize(void)
259{
260   if (WindowWidth >= 1024 && WindowHeight >= 1024) {
261      return 1024;
262   }
263   else if (WindowWidth >= 512 && WindowHeight >= 512) {
264      return 512;
265   }
266   else if (WindowWidth >= 256 && WindowHeight >= 256) {
267      return 256;
268   }
269   else {
270      return 128;
271   }
272}
273
274
275static GLuint
276CreateCubeTexture(GLint size)
277{
278   GLboolean linearFilter = GL_TRUE;
279   GLuint cube, face;
280
281   glGenTextures(1, &cube);
282   glBindTexture(GL_TEXTURE_CUBE_MAP, cube);
283
284   /* Set the filter mode so that the texture is texture-complete.
285    * Otherwise it will cause the framebuffer to fail the framebuffer
286    * completeness test.
287    */
288   if (linearFilter) {
289      glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
290      glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
291   }
292   else {
293      glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
294      glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
295   }
296
297   glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
298   glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
299
300   for (face = 0; face < 6; face++) {
301      glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, 0, GL_RGBA,
302		   size, size, 0,
303		   GL_RGBA, GL_UNSIGNED_BYTE, NULL);
304   }
305
306   CheckError(__LINE__);
307
308   return cube;
309}
310
311
312/**
313 * Render the six faces of a cube map.
314 */
315static void
316RenderCubeMap(void)
317{
318   GLuint fbo, face, depthStencilRb;
319   GLint cubeSize;
320
321   CheckError(__LINE__);
322
323   cubeSize = ChooseCubeSize();
324   printf("Rendering %d x %d cube texture\n", cubeSize, cubeSize);
325
326   CubeTexture = CreateCubeTexture(cubeSize);
327
328   /*
329    * Make FBO.
330    */
331   glGenFramebuffersEXT(1, &fbo);
332   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
333
334   /*
335    * Make depth/stencil renderbuffer.
336    */
337   glGenRenderbuffersEXT(1, &depthStencilRb);
338   glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthStencilRb);
339   glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_STENCIL,
340                            cubeSize, cubeSize);
341   glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
342                                GL_DEPTH_ATTACHMENT_EXT,
343                                GL_RENDERBUFFER_EXT, depthStencilRb);
344   glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
345                                GL_STENCIL_ATTACHMENT_EXT,
346                                GL_RENDERBUFFER_EXT, depthStencilRb);
347
348   glViewport(0, 0, cubeSize, cubeSize);
349
350   /*
351    * Render into cube faces.
352    */
353   for (face = 0; face < 6; face++) {
354      GLenum status;
355
356      /* Render color into face of cubemap */
357      glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
358                                GL_TEXTURE_CUBE_MAP_POSITIVE_X + face,
359				CubeTexture, 0);
360
361      status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
362      CheckError(__LINE__);
363      if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
364	 fprintf(stderr, "FBO not complete!  status = 0x%04x\n", status);
365	 assert(status == GL_FRAMEBUFFER_COMPLETE_EXT);
366      }
367
368      CheckError(__LINE__);
369
370      /* Render the depth image into cube face */
371      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
372
373      SetupCubeFaceView(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, SpherePos);
374
375      DrawScene();
376   }
377
378   glDeleteRenderbuffersEXT(1, &depthStencilRb);
379   glDeleteFramebuffersEXT(1, &fbo);
380   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
381
382   CheckError(__LINE__);
383}
384
385
386/**
387 * Redraw window image
388 */
389static void
390Display(void)
391{
392   const GLfloat ar = (GLfloat) WindowWidth / (GLfloat) WindowHeight;
393
394   ComputeLightPos(LightDist, LightLatitude, LightLongitude, LightPos);
395
396   if (NeedNewCubeMap) {
397      /* regenerate cube map faces */
398      RenderCubeMap();
399      NeedNewCubeMap = GL_FALSE;
400   }
401
402   glViewport(0, 0, WindowWidth, WindowHeight);
403
404   /* draw scene from camera's view */
405   glMatrixMode(GL_PROJECTION);
406   glLoadIdentity();
407   glFrustum(-ar, ar, -1.0, 1.0, 4.0, 50.0);
408
409   glMatrixMode(GL_MODELVIEW);
410   glLoadIdentity();
411   glTranslatef(0.0, 0.0, -ViewDist);
412   glRotatef(Xrot, 1, 0, 0);
413   glRotatef(Yrot, 0, 1, 0);
414   glRotatef(Zrot, 0, 0, 1);
415
416   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
417
418   glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
419   glLightfv(GL_LIGHT0, GL_POSITION, LightPos);
420
421   DrawScene();
422   DrawShinySphere();
423
424   glutSwapBuffers();
425}
426
427
428static void
429Reshape(int width, int height)
430{
431   WindowWidth = width;
432   WindowHeight = height;
433   NeedNewCubeMap = GL_TRUE;
434}
435
436
437static void
438Idle(void)
439{
440   static double t0 = -1.;
441   double dt, t = glutGet(GLUT_ELAPSED_TIME) / 1000.0;
442   if (t0 < 0.0)
443      t0 = t;
444   dt = t - t0;
445   t0 = t;
446   Yrot += 40.0 * dt;
447   glutPostRedisplay();
448}
449
450
451static void
452Key(unsigned char key, int x, int y)
453{
454   const GLfloat step = 3.0;
455   (void) x;
456   (void) y;
457   switch (key) {
458   case 'a':
459      Anim = !Anim;
460      if (Anim)
461         glutIdleFunc(Idle);
462      else
463         glutIdleFunc(NULL);
464      break;
465   case 'r':
466      GenerateCylinders();
467      NeedNewCubeMap = GL_TRUE;
468      break;
469   case 'z':
470      Zrot -= step;
471         break;
472   case 'Z':
473      Zrot += step;
474      break;
475   case 27:
476      glutDestroyWindow(Win);
477      exit(0);
478      break;
479   }
480   fflush(stdout);
481   glutPostRedisplay();
482}
483
484
485static void
486SpecialKey(int key, int x, int y)
487{
488   const GLfloat step = 3.0;
489   const int mod = glutGetModifiers();
490   (void) x;
491   (void) y;
492   switch (key) {
493      case GLUT_KEY_UP:
494         if (mod)
495            //LightLatitude += step;
496            SpherePos[1] += .1*step;
497         else
498            Xrot += step;
499         break;
500      case GLUT_KEY_DOWN:
501         if (mod)
502            //LightLatitude -= step;
503            SpherePos[1] -= .1*step;
504         else
505            Xrot -= step;
506         break;
507      case GLUT_KEY_LEFT:
508         if (mod)
509            LightLongitude += step;
510         else
511            Yrot += step;
512         break;
513      case GLUT_KEY_RIGHT:
514         if (mod)
515            LightLongitude -= step;
516         else
517            Yrot -= step;
518         break;
519   }
520   if (mod)
521      NeedNewCubeMap = GL_TRUE;
522
523   glutPostRedisplay();
524}
525
526
527static void
528Init(void)
529{
530   const char *extensions[3] = {
531      "GL_ARB_depth_texture",
532      "GL_ARB_texture_cube_map",
533      "GL_EXT_framebuffer_object"
534   };
535   int i;
536
537   for (i = 0; i < ELEMENTS(extensions); i++) {
538      if (!glutExtensionSupported(extensions[i])) {
539         printf("Sorry, this demo requires %s\n", extensions[i]);
540         exit(1);
541      }
542   }
543
544   glClearColor(0.25, 0.25, 0.25, 1.0);
545
546   glEnable(GL_DEPTH_TEST);
547   glEnable(GL_LIGHTING);
548   glEnable(GL_LIGHT0);
549
550   GenerateCylinders();
551
552   printf("GL_RENDERER = %s\n", (char *) glGetString(GL_RENDERER));
553}
554
555
556static void
557PrintHelp(void)
558{
559   printf("Keys:\n");
560   printf("  a = toggle animation\n");
561   printf("  cursor keys = rotate scene\n");
562   printf("  <shift> + up/down = move sphere\n");
563   printf("  <shift> + left/right = move light\n");
564   fflush(stdout);
565}
566
567
568int
569main(int argc, char *argv[])
570{
571   glutInitWindowSize(WindowWidth, WindowHeight);
572   glutInit(&argc, argv);
573   glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH | GLUT_STENCIL);
574   Win = glutCreateWindow(argv[0]);
575   glewInit();
576   glutReshapeFunc(Reshape);
577   glutKeyboardFunc(Key);
578   glutSpecialFunc(SpecialKey);
579   glutDisplayFunc(Display);
580   if (Anim)
581      glutIdleFunc(Idle);
582   Init();
583   PrintHelp();
584   glutMainLoop();
585   return 0;
586}
587