1 // BEGIN_COPYRIGHT -*- glean -*-
3 // Copyright (C) 1999 Allen Akin All Rights Reserved.
5 // Permission is hereby granted, free of charge, to any person
6 // obtaining a copy of this software and associated documentation
7 // files (the "Software"), to deal in the Software without
8 // restriction, including without limitation the rights to use,
9 // copy, modify, merge, publish, distribute, sublicense, and/or
10 // sell copies of the Software, and to permit persons to whom the
11 // Software is furnished to do so, subject to the following
14 // The above copyright notice and this permission notice shall be
15 // included in all copies or substantial portions of the
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
19 // KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
20 // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
21 // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ALLEN AKIN BE
22 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
23 // AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
24 // OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 // DEALINGS IN THE SOFTWARE.
30 // ttexgen.cpp: Basic test of GL texture coordinate generation.
31 // This test does a basic test of the glTexGen functions, including
32 // object_linear, eye_linear, and sphere_map. We use the Sphere3D with
33 // a GeomRenderer to draw a sphere, and map a check texture onto it. We
34 // use an ortho projection to keep it simple. The result should be a 1:1
35 // mapping of the check texture for all three modes (sphere map maps 1:1
36 // because mapping it onto a sphere inverts the spheremap math).
38 // Note that accuracy issues might cause this test to fail if the
39 // texcoords near the center are a little warped; I've specifically tried
40 // to keep the matrices as "pure" as possible (no rotations) to
41 // keep the numerical precision high. So far it seems to work fine.
42 // Introducing a rotation by 90 degrees about the x axis resulted,
43 // on one driver, in a warping at the center of the sphere which caused
46 // For the second test of the three, we offset the texture by 0.5,
47 // so that each test's rendering is visually distinct from the
50 // To test for pass/fail we examine the color buffer for red and blue,
51 // (the check colors) in the appropriate places.
53 // Author: Brian Sharp (brian@maniacal.org) December 2000
60 const GLuint viewSize
=50;
66 TexgenTest::FailMessage(BasicResult
&r
, const std::string
& texgenMode
,
67 GeomRenderer::DrawMethod method
, bool arraysCompiled
,
69 const std::string
& colorMismatch
) const {
70 env
->log
<< name
<< ": FAIL "
71 << r
.config
->conciseDescription() << '\n';
72 env
->log
<< "\t" << "during mode " << texgenMode
<< ", ";
75 case GeomRenderer::GLVERTEX_MODE
: env
->log
<< "glVertex-style rendering, "; break;
76 case GeomRenderer::GLARRAYELEMENT_MODE
: env
->log
<< "glArrayElement-style rendering, "; break;
77 case GeomRenderer::GLDRAWELEMENTS_MODE
: env
->log
<< "glDrawElements-style rendering, "; break;
78 case GeomRenderer::GLDRAWARRAYS_MODE
: env
->log
<< "glDrawArrays-style rendering, "; break;
80 if (arraysCompiled
) env
->log
<< "arrays locked, ";
81 else env
->log
<< "arrays not locked, ";
83 if (retainedMode
) env
->log
<< "built into a display list, ";
84 else env
->log
<< "called immediately (not display listed), ";
86 env
->log
<< colorMismatch
<< std::endl
;
90 TexgenTest::compareColors(GLfloat
* color0
, GLfloat
* color1
, std::string
& failureInfo
) const {
92 // Compare the colors; fail and report why if they don't match.
93 if (color0
[0] != color1
[0] || color0
[1] != color1
[1] || color0
[2] != color1
[2])
95 // Assemble the error message into a C-string, then hand it back in the string.
96 char failureOut
[1024];
97 sprintf(failureOut
, "expected [%f,%f,%f], read back [%f,%f,%f]",
98 color0
[0], color0
[1], color0
[2],
99 color1
[0], color1
[1], color1
[2]);
101 failureInfo
= std::string(failureOut
);
109 TexgenTest::verifyCheckers(GLfloat
* pixels
, GLfloat
* upperLeftColor
, GLfloat
* upperRightColor
, std::string
& failureInfo
) const {
111 // My loop control variable, since gcc and MSVC do things differently.
114 // It's a viewSize x viewSize pixel block; since we drew a sphere that doesn't quite touch the
115 // edges, we need to be careful not to sample from what should be background.
116 // These pairs are hand-picked coordinates on the image that fall on the bottom-left quadrant
118 // XXX FIX ME: these sample coordinates assume that viewSize == 50.
119 GLuint samples
[6][2] = {{13,13}, {4,22}, {22,4}, {20,20}, {20,10}, {10,20}};
121 // Run through those sample points in the bottom-left corner and make sure they're all the right color.
122 for (samp
=0; samp
<6; samp
++)
124 GLuint sampleOffset
= (samples
[samp
][0] + (viewSize
*samples
[samp
][1]))*3;
125 if (!compareColors(upperRightColor
, pixels
+ sampleOffset
, failureInfo
))
131 // Run through those sample points in the bottom-right corner and make sure they're all the right color.
132 // Note the "viewSize - samples[samp][0]" to flip it to the bottom-right quadrant.
133 for (samp
=0; samp
<6; samp
++)
135 GLuint sampleOffset
= ((viewSize
- samples
[samp
][0]) + (viewSize
*samples
[samp
][1]))*3;
136 if (!compareColors(upperLeftColor
, pixels
+ sampleOffset
, failureInfo
))
142 // Run through those sample points in the upper-right corner and make sure they're all the right color.
143 for (samp
=0; samp
<6; samp
++)
145 GLuint sampleOffset
= ((viewSize
- samples
[samp
][0]) + (viewSize
*(viewSize
- samples
[samp
][1])))*3;
146 if (!compareColors(upperRightColor
, pixels
+ sampleOffset
, failureInfo
))
152 // Run through those sample points in the upper-left corner and make sure they're all the right color.
153 for (samp
=0; samp
<6; samp
++)
155 GLuint sampleOffset
= (samples
[samp
][0] + (viewSize
*(viewSize
- samples
[samp
][1])))*3;
156 if (!compareColors(upperLeftColor
, pixels
+ sampleOffset
, failureInfo
))
166 ///////////////////////////////////////////////////////////////////////////////
167 // runOne: Run a single test case
168 ///////////////////////////////////////////////////////////////////////////////
170 TexgenTest::runOne(BasicResult
& r
, Window
&) {
172 // Temporary buffer to store pixels we've read back for verification.
173 GLfloat pixels
[50*50*3];
175 // Colors for matching against when we readback pixels.
176 GLfloat matchBlue
[3] = {0,0,1};
177 GLfloat matchRed
[3] = {1,0,0};
180 Sphere3D
theSphere(9.9, 32, 16);
182 // A GeomRenderer to draw it with.
183 GeomRenderer sphereRenderer
;
184 sphereRenderer
.setDrawMethod(GeomRenderer::GLVERTEX_MODE
);
185 sphereRenderer
.setParameterBits(GeomRenderer::NORMAL_BIT
);
186 sphereRenderer
.setVArrayIndices(theSphere
.getNumIndices(),GL_UNSIGNED_INT
,theSphere
.getIndices());
187 sphereRenderer
.setVertexPointer(theSphere
.getNumVertices(), 3, GL_FLOAT
, 0, theSphere
.getVertices());
188 sphereRenderer
.setNormalPointer(GL_FLOAT
, 0, theSphere
.getNormals());
190 // draw the sphere in a 50x50 pixel window for some precision.
191 glViewport(0, 0, 50, 50);
194 glDisable(GL_DITHER
);
195 glEnable(GL_CULL_FACE
);
196 glPolygonMode(GL_FRONT_AND_BACK
, GL_FILL
);
199 // Setup the projection.
200 glMatrixMode(GL_PROJECTION
);
202 glOrtho(-10,10,-10,10,-10,10);
203 glMatrixMode(GL_MODELVIEW
);
206 // Set up our texture.
207 glEnable(GL_TEXTURE_2D
);
208 GLuint checkerTextureHandle
;
209 glGenTextures(1, &checkerTextureHandle
);
210 glBindTexture(GL_TEXTURE_2D
, checkerTextureHandle
);
212 glTexEnvi(GL_TEXTURE_ENV
, GL_TEXTURE_ENV_MODE
, GL_REPLACE
);
213 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
214 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
215 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, GL_REPEAT
);
216 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, GL_REPEAT
);
217 glEnable(GL_TEXTURE_GEN_S
);
218 glEnable(GL_TEXTURE_GEN_T
);
220 // Make a little checker texture.
221 unsigned char redBlueCheck
[256*256*3];
222 for (int x
=0; x
<256; x
++)
224 for (int y
=0; y
<256; y
++)
226 bool xPastHalf
= x
>= 128;
227 bool yPastHalf
= y
>= 128;
229 redBlueCheck
[(x
+(256*y
))*3 + 0] = ((xPastHalf
&& yPastHalf
) || (!xPastHalf
&& !yPastHalf
)) ? 255 : 0;
230 redBlueCheck
[(x
+(256*y
))*3 + 1] = 0;
231 redBlueCheck
[(x
+(256*y
))*3 + 2] = ((xPastHalf
&& !yPastHalf
) || (!xPastHalf
&& yPastHalf
)) ? 255 : 0;
234 gluBuild2DMipmaps(GL_TEXTURE_2D
, GL_RGB
, 256, 256, GL_RGB
, GL_UNSIGNED_BYTE
, redBlueCheck
);
236 // Setup our arrays of configuration info; we loop over the rendering pass a number of times,
237 // using a different GL primitive path each time.
238 GeomRenderer::DrawMethod drawMethods
[] = {GeomRenderer::GLVERTEX_MODE
, GeomRenderer::GLARRAYELEMENT_MODE
,
239 GeomRenderer::GLDRAWELEMENTS_MODE
, GeomRenderer::GLARRAYELEMENT_MODE
,
240 GeomRenderer::GLDRAWELEMENTS_MODE
};
242 bool arraysCompiled
[] = {false, false, false, true, true};
244 // Iterate once for all immediate mode styles, then once for retained mode styles.
245 for (int retainedMode
=0; retainedMode
<2; retainedMode
++)
247 for (int testIteration
=0; testIteration
<5; testIteration
++)
249 sphereRenderer
.setDrawMethod(drawMethods
[testIteration
]);
250 if (!sphereRenderer
.setArraysCompiled(arraysCompiled
[testIteration
]))
252 // We don't have the extension... not sure what we should do.
253 // May as well just keep going, it's no big deal (it should still
254 // yield correct results, of course, it's just redundant).
257 // GL_SPHERE_MAP: with spheremap, the UL corner is blue
258 glTexGeni(GL_S
, GL_TEXTURE_GEN_MODE
, GL_SPHERE_MAP
);
259 glTexGeni(GL_T
, GL_TEXTURE_GEN_MODE
, GL_SPHERE_MAP
);
261 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
| GL_STENCIL_BUFFER_BIT
);
262 renderSphere(retainedMode
, sphereRenderer
);
263 glReadPixels(0,0,50,50, GL_RGB
, GL_FLOAT
, pixels
);
266 std::string sphereMapResult
;
267 if (!verifyCheckers(pixels
, matchBlue
, matchRed
, sphereMapResult
))
269 FailMessage(r
, std::string("GL_SPHERE_MAP"), drawMethods
[testIteration
],
270 arraysCompiled
[testIteration
], retainedMode
, sphereMapResult
);
272 glDeleteTextures(1, &checkerTextureHandle
);
276 // GL_OBJECT_LINEAR: with object linear and the below planes, the UL corner is red.
277 glTexGeni(GL_S
, GL_TEXTURE_GEN_MODE
, GL_OBJECT_LINEAR
);
278 glTexGeni(GL_T
, GL_TEXTURE_GEN_MODE
, GL_OBJECT_LINEAR
);
279 float sObjPlane
[4] = {0,0.05,0,1.5}; // We flip the checker by setting W to 1.5 (phases by half a period)
280 float tObjPlane
[4] = {0.05,0,0,1};
281 glTexGenfv(GL_S
, GL_OBJECT_PLANE
, sObjPlane
);
282 glTexGenfv(GL_T
, GL_OBJECT_PLANE
, tObjPlane
);
284 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
| GL_STENCIL_BUFFER_BIT
);
285 renderSphere(retainedMode
, sphereRenderer
);
286 glReadPixels(0,0,50,50, GL_RGB
, GL_FLOAT
, pixels
);
289 std::string objectLinearResult
;
290 if (!verifyCheckers(pixels
, matchRed
, matchBlue
, objectLinearResult
))
292 FailMessage(r
, std::string("GL_OBJECT_LINEAR"), drawMethods
[testIteration
],
293 arraysCompiled
[testIteration
], retainedMode
, objectLinearResult
);
295 glDeleteTextures(1, &checkerTextureHandle
);
299 // GL_EYE_LINEAR: with eye linear and the below planes, the UL corner is blue.
300 glTexGeni(GL_S
, GL_TEXTURE_GEN_MODE
, GL_EYE_LINEAR
);
301 glTexGeni(GL_T
, GL_TEXTURE_GEN_MODE
, GL_EYE_LINEAR
);
302 float sEyePlane
[4] = {0,0.05,0,1};
303 float tEyePlane
[4] = {0.05,0,0,1};
304 glTexGenfv(GL_S
, GL_EYE_PLANE
, sEyePlane
);
305 glTexGenfv(GL_T
, GL_EYE_PLANE
, tEyePlane
);
307 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
| GL_STENCIL_BUFFER_BIT
);
308 renderSphere(retainedMode
, sphereRenderer
);
309 glReadPixels(0,0,50,50, GL_RGB
, GL_FLOAT
, pixels
);
312 std::string eyeLinearResult
;
313 if (!verifyCheckers(pixels
, matchBlue
, matchRed
, eyeLinearResult
))
315 FailMessage(r
, std::string("GL_EYE_LINEAR"), drawMethods
[testIteration
],
316 arraysCompiled
[testIteration
], retainedMode
, eyeLinearResult
);
318 glDeleteTextures(1, &checkerTextureHandle
);
326 } // TexgenTest::runOne
328 ///////////////////////////////////////////////////////////////////////////////
329 // logOne: Log a single test case
330 ///////////////////////////////////////////////////////////////////////////////
332 TexgenTest::logOne(BasicResult
& r
) {
337 } // TexgenTest::logOne
340 TexgenTest::renderSphere(int retainedMode
, GeomRenderer
& sphereRenderer
)
346 result
= sphereRenderer
.generateDisplayList(GL_TRIANGLES
, displayList
);
348 glCallList(displayList
);
349 glDeleteLists(displayList
, 1);
353 result
= sphereRenderer
.renderPrimitives(GL_TRIANGLES
);
356 } // TexgenTest::renderSphere
359 ///////////////////////////////////////////////////////////////////////////////
360 // The test object itself:
361 ///////////////////////////////////////////////////////////////////////////////
362 TexgenTest
texgenTest("texgen", "window, rgb",
364 "This test verifies that the three basic OpenGL texture coordinate\n"
365 "modes: object_linear, eye_linear, and sphere_map, work for a simple\n"