1/**
2 * Display trilinear mipmap filtering quality.
3 * We look down a long tunnel shape which has a mipmapped texture
4 * applied to it.  Ideally, the transition from one mipmap level to
5 * another should be nice and regular/circular.
6 * This sort of test is frequently seen in online articles about GPU
7 * texture filtering.
8 *
9 * Brian Paul
10 * 13 Oct 2010
11 */
12
13
14#include <stdlib.h>
15#include <stdio.h>
16#include <GL/glew.h>
17#include "glut_wrap.h"
18
19
20static GLfloat LodBias = 0.0;
21static GLboolean NearestFilter = GL_FALSE;
22static GLfloat Zpos = -10.0, Zrot = 0.0;
23static GLuint TexObj;
24static GLboolean HaveAniso;
25static GLfloat AnisoMax = 1.0, MaxAnisoMax = 8.0;
26
27#define TEX_SIZE 1024
28
29
30/** Make a solid-colored texture image */
31static void
32MakeImage(int level, int width, int height, const GLubyte color[4])
33{
34   const int makeStripes = 0;
35   GLubyte img[TEX_SIZE * TEX_SIZE * 3];
36   int i, j;
37   for (i = 0; i < height; i++) {
38      for (j = 0; j < width; j++) {
39         int k = (i * width + j) * 3;
40         int p = (i / 8) & makeStripes;
41         if (p == 0) {
42            img[k + 0] = color[0];
43            img[k + 1] = color[1];
44            img[k + 2] = color[2];
45         }
46         else {
47            img[k + 0] = 0;
48            img[k + 1] = 0;
49            img[k + 2] = 0;
50         }
51      }
52   }
53
54   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
55   glTexImage2D(GL_TEXTURE_2D, level, GL_RGB, width, height, 0,
56                GL_RGB, GL_UNSIGNED_BYTE, img);
57}
58
59
60/** Make a mipmap in which each level is a different, solid color */
61static void
62MakeMipmap(void)
63{
64   static const GLubyte colors[12][3] = {
65      {255, 0, 0},
66      {0, 255, 0},
67      {0, 0, 255},
68      {0, 255, 255},
69      {255, 0, 255},
70      {255, 255, 0},
71      {255, 0, 0},
72      {0, 255, 0},
73      {0, 0, 255},
74      {0, 255, 255},
75      {255, 0, 255},
76      {255, 255, 0},
77   };
78   int i, sz = TEX_SIZE;
79
80   for (i = 0; sz > 0; i++) {
81      MakeImage(i, sz, sz, colors[i]);
82      printf("Level %d size: %d x %d\n", i, sz, sz);
83      sz /= 2;
84   }
85}
86
87
88static void
89Init(void)
90{
91   glClearColor(.5, .5, .5, .5);
92
93   glGenTextures(1, &TexObj);
94   glBindTexture(GL_TEXTURE_2D, TexObj);
95   MakeMipmap();
96
97   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
98   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
99   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
100
101   printf("GL_RENDERER = %s\n", (char *) glGetString(GL_RENDERER));
102   printf("GL_VERSION = %s\n", (char *) glGetString(GL_VERSION));
103
104   HaveAniso = glutExtensionSupported("GL_EXT_texture_filter_anisotropic");
105   if (HaveAniso) {
106      glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &MaxAnisoMax);
107      printf("GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT = %f\n", MaxAnisoMax);
108   }
109}
110
111
112static void
113DrawTunnel(void)
114{
115   const float radius = 10.0, height = 500.0;
116   const int slices = 24, stacks = 52;
117   const float bias = 0.995;
118   GLUquadric *q = gluNewQuadric();
119
120   glPushMatrix();
121      glRotatef(180, 1, 0, 0);
122      glEnable(GL_TEXTURE_2D);
123      gluQuadricTexture(q, GL_TRUE);
124      gluCylinder(q, radius, radius, height, slices, stacks);
125
126      glDisable(GL_TEXTURE_2D);
127      glColor3f(0, 0, 0);
128      gluQuadricDrawStyle(q, GLU_LINE);
129      gluCylinder(q, bias*radius, bias*radius, height/4, slices, stacks/4);
130   glPopMatrix();
131
132   gluDeleteQuadric(q);
133}
134
135
136static void
137PrintString(const char *s)
138{
139   while (*s) {
140      glutBitmapCharacter(GLUT_BITMAP_8_BY_13, (int) *s);
141      s++;
142   }
143}
144
145
146static void
147Display(void)
148{
149   char str[100];
150
151   glBindTexture(GL_TEXTURE_2D, TexObj);
152
153   if (NearestFilter) {
154      glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
155      glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
156                      GL_NEAREST_MIPMAP_NEAREST);
157   }
158   else {
159      glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
160      glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
161                      GL_LINEAR_MIPMAP_LINEAR);
162   }
163
164   if (HaveAniso) {
165      glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, AnisoMax);
166   }
167
168   glTexEnvf(GL_TEXTURE_FILTER_CONTROL_EXT, GL_TEXTURE_LOD_BIAS_EXT, LodBias);
169
170   glClear(GL_COLOR_BUFFER_BIT);
171
172   glPushMatrix();
173      glTranslatef(0.0, 0.0, Zpos);
174      glRotatef(Zrot, 0, 0, 1);
175      DrawTunnel();
176   glPopMatrix();
177
178   glColor3f(1, 1, 1);
179   glWindowPos2i(10, 10);
180   if (HaveAniso)
181      sprintf(str, "LOD bias (b/B): %.3f  MaxAnisotropy: %.3f", LodBias, AnisoMax);
182   else
183      sprintf(str, "LOD bias (b/B): %.3f", LodBias);
184   PrintString(str);
185
186   glutSwapBuffers();
187}
188
189
190static void
191Reshape(int w, int h)
192{
193   glViewport(0, 0, w, h);
194   glMatrixMode(GL_PROJECTION);
195   glLoadIdentity();
196   gluPerspective(80.0, 1.0 * (GLfloat) w / (GLfloat) h, 1.0, 3000.0);
197   glMatrixMode(GL_MODELVIEW);
198   glLoadIdentity();
199}
200
201
202static void
203Key(unsigned char k, int x, int y)
204{
205   (void) x;
206   (void) y;
207   switch (k) {
208   case 'a':
209      AnisoMax -= 0.25;
210      if (AnisoMax <= 1.0)
211         AnisoMax = 1.0;
212      break;
213   case 'A':
214      AnisoMax += 0.25;
215      if (AnisoMax > MaxAnisoMax)
216         AnisoMax = MaxAnisoMax;
217      break;
218   case 'b':
219      LodBias -= 0.125;
220      break;
221   case 'B':
222      LodBias += 0.125;
223      break;
224   case 'f':
225      NearestFilter = !NearestFilter;
226      break;
227   case 'r':
228      Zrot--;
229      break;
230   case 'R':
231      Zrot++;
232      break;
233   case 'z':
234      Zpos--;
235      break;
236   case 'Z':
237      Zpos++;
238      break;
239   case 27:
240      exit(0);
241      break;
242   default:
243      return;
244   }
245   glutPostRedisplay();
246}
247
248
249static void
250Usage(void)
251{
252   printf("Keys:\n");
253   printf("  b/B    decrease/increase GL_TEXTURE_LOD_BIAS\n");
254   printf("  f      toggle nearest/linear filtering\n");
255   printf("  r/R    rotate tunnel\n");
256}
257
258
259int
260main(int argc, char **argv)
261{
262   glutInitWindowSize(600, 600);
263   glutInit(&argc, argv);
264   glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
265   glutCreateWindow(argv[0]);
266   glewInit();
267   glutReshapeFunc(Reshape);
268   glutDisplayFunc(Display);
269   glutKeyboardFunc(Key);
270   Init();
271   Usage();
272   glutMainLoop();
273   return 0;
274}
275