1/**
2 * Test texturing with GL shading language.
3 *
4 * Copyright (C) 2007  Brian Paul   All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included
14 * in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
20 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24
25
26#include <assert.h>
27#include <math.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <GL/glew.h>
32#include "glut_wrap.h"
33#include "readtex.h"
34#include "shaderutil.h"
35
36static const char *Demo = "texdemo1";
37
38static const char *ReflectVertFile = "reflect.vert";
39static const char *CubeFragFile = "cubemap.frag";
40
41static const char *SimpleVertFile = "simple.vert";
42static const char *SimpleTexFragFile = "shadowtex.frag";
43
44static const char *GroundImage = DEMOS_DATA_DIR "tile.rgb";
45
46static GLuint Program1, Program2;
47
48static GLfloat TexXrot = 0, TexYrot = 0;
49static GLfloat Xrot = 20.0, Yrot = 20.0, Zrot = 0.0;
50static GLfloat EyeDist = 10;
51static GLboolean Anim = GL_TRUE;
52static int win = 0;
53
54
55static struct uniform_info ReflectUniforms[] = {
56   { "cubeTex",  1, GL_SAMPLER_CUBE, { 0, 0, 0, 0 }, -1 },
57   { "lightPos", 1, GL_FLOAT_VEC3, { 10, 10, 20, 0 }, -1 },
58   END_OF_UNIFORMS
59};
60
61static struct uniform_info SimpleUniforms[] = {
62   { "tex2d",    1, GL_SAMPLER_2D, { 1, 0, 0, 0 }, -1 },
63   { "lightPos", 1, GL_FLOAT_VEC3, { 10, 10, 20, 0 }, -1 },
64   END_OF_UNIFORMS
65};
66
67
68static void
69DrawGround(GLfloat size)
70{
71   glPushMatrix();
72   glRotatef(90, 1, 0, 0);
73   glNormal3f(0, 0, 1);
74   glBegin(GL_POLYGON);
75   glTexCoord2f(-2, -2);  glVertex2f(-size, -size);
76   glTexCoord2f( 2, -2);  glVertex2f( size, -size);
77   glTexCoord2f( 2,  2);  glVertex2f( size,  size);
78   glTexCoord2f(-2,  2);  glVertex2f(-size,  size);
79   glEnd();
80   glPopMatrix();
81}
82
83
84static void
85draw(void)
86{
87   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
88
89   glEnable(GL_TEXTURE_2D);
90
91   glPushMatrix(); /* modelview matrix */
92      glTranslatef(0.0, 0.0, -EyeDist);
93      glRotatef(Xrot, 1, 0, 0);
94      glRotatef(Yrot, 0, 1, 0);
95      glRotatef(Zrot, 0, 0, 1);
96
97      /* sphere w/ reflection map */
98      glPushMatrix();
99         glTranslatef(0, 1, 0);
100         glUseProgram(Program1);
101
102         /* setup texture matrix */
103         glActiveTexture(GL_TEXTURE0);
104         glMatrixMode(GL_TEXTURE);
105         glLoadIdentity();
106         glRotatef(-TexYrot, 0, 1, 0);
107         glRotatef(-TexXrot, 1, 0, 0);
108
109         glEnable(GL_TEXTURE_GEN_S);
110         glEnable(GL_TEXTURE_GEN_T);
111         glEnable(GL_TEXTURE_GEN_R);
112         glutSolidSphere(2.0, 20, 20);
113
114         glLoadIdentity(); /* texture matrix */
115         glMatrixMode(GL_MODELVIEW);
116      glPopMatrix();
117
118      /* ground */
119      glUseProgram(Program2);
120      glTranslatef(0, -1.0, 0);
121      DrawGround(5);
122
123   glPopMatrix();
124
125   glutSwapBuffers();
126}
127
128
129static void
130idle(void)
131{
132   GLfloat t = 0.05 * glutGet(GLUT_ELAPSED_TIME);
133   TexYrot = t;
134   glutPostRedisplay();
135}
136
137
138static void
139key(unsigned char k, int x, int y)
140{
141   (void) x;
142   (void) y;
143   switch (k) {
144   case ' ':
145   case 'a':
146      Anim = !Anim;
147      if (Anim)
148         glutIdleFunc(idle);
149      else
150         glutIdleFunc(NULL);
151      break;
152   case 'z':
153      EyeDist -= 0.5;
154      if (EyeDist < 6.0)
155         EyeDist = 6.0;
156      break;
157   case 'Z':
158      EyeDist += 0.5;
159      if (EyeDist > 90.0)
160         EyeDist = 90;
161      break;
162   case 27:
163      glutDestroyWindow(win);
164      exit(0);
165   }
166   glutPostRedisplay();
167}
168
169
170static void
171specialkey(int key, int x, int y)
172{
173   GLfloat step = 2.0;
174   (void) x;
175   (void) y;
176   switch (key) {
177   case GLUT_KEY_UP:
178      Xrot += step;
179      break;
180   case GLUT_KEY_DOWN:
181      Xrot -= step;
182      break;
183   case GLUT_KEY_LEFT:
184      Yrot -= step;
185      break;
186   case GLUT_KEY_RIGHT:
187      Yrot += step;
188      break;
189   }
190   glutPostRedisplay();
191}
192
193
194/* new window size or exposure */
195static void
196Reshape(int width, int height)
197{
198   GLfloat ar = (float) width / (float) height;
199   glViewport(0, 0, (GLint)width, (GLint)height);
200   glMatrixMode(GL_PROJECTION);
201   glLoadIdentity();
202   glFrustum(-2.0*ar, 2.0*ar, -2.0, 2.0, 4.0, 100.0);
203   glMatrixMode(GL_MODELVIEW);
204   glLoadIdentity();
205}
206
207
208static void
209InitCheckers(void)
210{
211#define CUBE_TEX_SIZE 64
212   GLubyte image[CUBE_TEX_SIZE][CUBE_TEX_SIZE][3];
213   static const GLubyte colors[6][3] = {
214      { 255,   0,   0 },	/* face 0 - red */
215      {   0, 255, 255 },	/* face 1 - cyan */
216      {   0, 255,   0 },	/* face 2 - green */
217      { 255,   0, 255 },	/* face 3 - purple */
218      {   0,   0, 255 },	/* face 4 - blue */
219      { 255, 255,   0 }		/* face 5 - yellow */
220   };
221   static const GLenum targets[6] = {
222      GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
223      GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
224      GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
225      GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
226      GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
227      GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB
228   };
229
230   GLint i, j, f;
231
232   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
233
234   /* make colored checkerboard cube faces */
235   for (f = 0; f < 6; f++) {
236      for (i = 0; i < CUBE_TEX_SIZE; i++) {
237         for (j = 0; j < CUBE_TEX_SIZE; j++) {
238            if ((i/4 + j/4) & 1) {
239               image[i][j][0] = colors[f][0];
240               image[i][j][1] = colors[f][1];
241               image[i][j][2] = colors[f][2];
242            }
243            else {
244               image[i][j][0] = 255;
245               image[i][j][1] = 255;
246               image[i][j][2] = 255;
247            }
248         }
249      }
250
251      glTexImage2D(targets[f], 0, GL_RGB, CUBE_TEX_SIZE, CUBE_TEX_SIZE, 0,
252                   GL_RGB, GL_UNSIGNED_BYTE, image);
253   }
254}
255
256
257static void
258LoadFace(GLenum target, const char *filename,
259         GLboolean flipTB, GLboolean flipLR)
260{
261   GLint w, h;
262   GLenum format;
263   GLubyte *img = LoadRGBImage(filename, &w, &h, &format);
264   if (!img) {
265      printf("Error: couldn't load texture image %s\n", filename);
266      exit(1);
267   }
268   assert(format == GL_RGB);
269
270   /* <sigh> the way the texture cube mapping works, we have to flip
271    * images to make things look right.
272    */
273   if (flipTB) {
274      const int stride = 3 * w;
275      GLubyte temp[3*1024];
276      int i;
277      for (i = 0; i < h / 2; i++) {
278         memcpy(temp, img + i * stride, stride);
279         memcpy(img + i * stride, img + (h - i - 1) * stride, stride);
280         memcpy(img + (h - i - 1) * stride, temp, stride);
281      }
282   }
283   if (flipLR) {
284      const int stride = 3 * w;
285      GLubyte temp[3];
286      GLubyte *row;
287      int i, j;
288      for (i = 0; i < h; i++) {
289         row = img + i * stride;
290         for (j = 0; j < w / 2; j++) {
291            int k = w - j - 1;
292            temp[0] = row[j*3+0];
293            temp[1] = row[j*3+1];
294            temp[2] = row[j*3+2];
295            row[j*3+0] = row[k*3+0];
296            row[j*3+1] = row[k*3+1];
297            row[j*3+2] = row[k*3+2];
298            row[k*3+0] = temp[0];
299            row[k*3+1] = temp[1];
300            row[k*3+2] = temp[2];
301         }
302      }
303   }
304
305   gluBuild2DMipmaps(target, GL_RGB, w, h, format, GL_UNSIGNED_BYTE, img);
306   free(img);
307}
308
309
310static void
311LoadEnvmaps(void)
312{
313   LoadFace(GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB, "right.rgb", GL_TRUE, GL_FALSE);
314   LoadFace(GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB, "left.rgb", GL_TRUE, GL_FALSE);
315   LoadFace(GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB, "top.rgb", GL_FALSE, GL_TRUE);
316   LoadFace(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB, "bottom.rgb", GL_FALSE, GL_TRUE);
317   LoadFace(GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB, "front.rgb", GL_TRUE, GL_FALSE);
318   LoadFace(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB, "back.rgb", GL_TRUE, GL_FALSE);
319}
320
321
322static void
323InitTextures(GLboolean useImageFiles)
324{
325   GLenum filter;
326
327   /*
328    * Env map
329    */
330   glActiveTexture(GL_TEXTURE0);
331   glBindTexture(GL_TEXTURE_CUBE_MAP, 1);
332   if (useImageFiles) {
333      LoadEnvmaps();
334      filter = GL_LINEAR;
335   }
336   else {
337      InitCheckers();
338      filter = GL_NEAREST;
339   }
340   glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, filter);
341   glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, filter);
342   glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
343   glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
344
345   /*
346    * Ground texture
347    */
348   {
349      GLint imgWidth, imgHeight;
350      GLenum imgFormat;
351      GLubyte *image = NULL;
352
353      image = LoadRGBImage(GroundImage, &imgWidth, &imgHeight, &imgFormat);
354      if (!image) {
355         printf("Couldn't read %s\n", GroundImage);
356         exit(0);
357      }
358
359      glActiveTexture(GL_TEXTURE1);
360      glBindTexture(GL_TEXTURE_2D, 2);
361      gluBuild2DMipmaps(GL_TEXTURE_2D, 3, imgWidth, imgHeight,
362                        imgFormat, GL_UNSIGNED_BYTE, image);
363      free(image);
364
365      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
366      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
367      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
368      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
369   }
370}
371
372
373static GLuint
374CreateAProgram(const char *vertProgFile, const char *fragProgFile,
375              struct uniform_info *uniforms)
376{
377   GLuint fragShader, vertShader, program;
378
379   vertShader = CompileShaderFile(GL_VERTEX_SHADER, vertProgFile);
380   fragShader = CompileShaderFile(GL_FRAGMENT_SHADER, fragProgFile);
381   program = LinkShaders(vertShader, fragShader);
382
383   glUseProgram(program);
384
385   SetUniformValues(program, uniforms);
386   PrintUniforms(uniforms);
387
388   return program;
389}
390
391
392static void
393InitPrograms(void)
394{
395   Program1 = CreateAProgram(ReflectVertFile, CubeFragFile, ReflectUniforms);
396   Program2 = CreateAProgram(SimpleVertFile, SimpleTexFragFile, SimpleUniforms);
397}
398
399
400static void
401Init(GLboolean useImageFiles)
402{
403   if (!ShadersSupported()) {
404      exit(1);
405   }
406   printf("GL_RENDERER = %s\n",(const char *) glGetString(GL_RENDERER));
407
408   InitTextures(useImageFiles);
409   InitPrograms();
410
411   glEnable(GL_DEPTH_TEST);
412
413   glClearColor(.6, .6, .9, 0);
414   glColor3f(1.0, 1.0, 1.0);
415}
416
417
418int
419main(int argc, char *argv[])
420{
421   glutInit(&argc, argv);
422   glutInitWindowSize(500, 400);
423   glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
424   win = glutCreateWindow(Demo);
425   glewInit();
426   glutReshapeFunc(Reshape);
427   glutKeyboardFunc(key);
428   glutSpecialFunc(specialkey);
429   glutDisplayFunc(draw);
430   if (Anim)
431      glutIdleFunc(idle);
432   if (argc > 1 && strcmp(argv[1] , "-i") == 0)
433      Init(1);
434   else
435      Init(0);
436   glutMainLoop();
437   return 0;
438}
439