Equalizer
1.2.1
|
00001 00002 /* Copyright (c) 2007-2010, Stefan Eilemann <eile@equalizergraphics.com> 00003 * 00004 * Redistribution and use in source and binary forms, with or without 00005 * modification, are permitted provided that the following conditions are met: 00006 * 00007 * - Redistributions of source code must retain the above copyright notice, this 00008 * list of conditions and the following disclaimer. 00009 * - Redistributions in binary form must reproduce the above copyright notice, 00010 * this list of conditions and the following disclaimer in the documentation 00011 * and/or other materials provided with the distribution. 00012 * - Neither the name of Eyescale Software GmbH nor the names of its 00013 * contributors may be used to endorse or promote products derived from this 00014 * software without specific prior written permission. 00015 * 00016 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 00017 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00018 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 00019 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 00020 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 00021 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 00022 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 00023 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 00024 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 00025 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00026 * POSSIBILITY OF SUCH DAMAGE. 00027 */ 00028 00029 #include "channel.h" 00030 00031 #include "config.h" 00032 #include "configEvent.h" 00033 00034 #include <co/plugins/compressor.h> 00035 00036 #ifdef WIN32_API 00037 # define snprintf _snprintf 00038 #endif 00039 00040 namespace eqPixelBench 00041 { 00042 namespace 00043 { 00044 #pragma warning(disable: 411) // class defines no constructor to initialize ... 00045 struct EnumMap 00046 { 00047 const char* internalFormatString; 00048 const uint32_t internalFormat; 00049 const size_t pixelSize; 00050 }; 00051 00052 #pragma warning(default: 411) 00053 00054 #define ENUM_MAP_ITEM( internalFormat, pixelSize ) \ 00055 { #internalFormat, EQ_COMPRESSOR_DATATYPE_ ## internalFormat, pixelSize } 00056 00057 static EnumMap _enums[] = { 00058 ENUM_MAP_ITEM( RGBA32F, 16 ), // initial buffer resize 00059 ENUM_MAP_ITEM( RGBA, 4 ), 00060 ENUM_MAP_ITEM( RGB10_A2, 4 ), 00061 ENUM_MAP_ITEM( RGBA16F, 8 ), 00062 ENUM_MAP_ITEM( RGBA32F, 16 ), 00063 ENUM_MAP_ITEM( DEPTH, 4 ), 00064 { 0, 0, 0 }}; 00065 #define NUM_IMAGES 8 00066 } 00067 00068 Channel::Channel( eq::Window* parent ) 00069 : eq::Channel( parent ) 00070 { 00071 eq::FrameData* frameData = new eq::FrameData; 00072 _frame.setData( frameData ); 00073 00074 for( unsigned i = 0; i < NUM_IMAGES; ++i ) 00075 frameData->newImage( eq::Frame::TYPE_MEMORY, getDrawableConfig( )); 00076 } 00077 00078 void Channel::frameStart( const eq::uint128_t& frameID, const uint32_t frameNumber ) 00079 { 00080 Config* config = static_cast< Config* >( getConfig( )); 00081 const co::base::Clock* clock = config->getClock(); 00082 00083 if( clock ) 00084 { 00085 ConfigEvent event; 00086 event.msec = clock->getTimef(); 00087 00088 const std::string& name = getName(); 00089 if( name.empty( )) 00090 snprintf( event.data.user.data, 32, "%p", this); 00091 else 00092 snprintf( event.data.user.data, 32, "%s", name.c_str( )); 00093 00094 event.data.user.data[31] = 0; 00095 event.area.x() = 0; 00096 event.area.y() = 0; 00097 00098 snprintf( event.formatType, 32, "app->pipe thread latency"); 00099 event.data.type = ConfigEvent::START_LATENCY; 00100 00101 config->sendEvent( event ); 00102 } 00103 00104 eq::Channel::frameStart( frameID, frameNumber ); 00105 } 00106 00107 void Channel::frameDraw( const eq::uint128_t& frameID ) 00108 { 00109 //----- setup GL state 00110 applyBuffer(); 00111 applyViewport(); 00112 00113 glMatrixMode( GL_PROJECTION ); 00114 glLoadIdentity(); 00115 00116 applyFrustum(); 00117 00118 glMatrixMode( GL_MODELVIEW ); 00119 glLoadIdentity(); 00120 applyHeadTransform(); 00121 00122 setupAssemblyState(); 00123 00124 _testFormats( 1.0f ); 00125 _testFormats( 0.5f ); 00126 _testFormats( 2.0f ); 00127 _testTiledOperations(); 00128 _testDepthAssemble(); 00129 00130 resetAssemblyState(); 00131 } 00132 00133 ConfigEvent Channel::_createConfigEvent() 00134 { 00135 ConfigEvent event; 00136 const std::string& name = getName(); 00137 00138 if( name.empty( )) 00139 snprintf( event.data.user.data, 32, "%p", this ); 00140 else 00141 snprintf( event.data.user.data, 32, "%s", name.c_str( )); 00142 00143 event.data.user.data[31] = 0; 00144 return event; 00145 } 00146 00147 void Channel::_testFormats( float applyZoom ) 00148 { 00149 //----- setup constant data 00150 const eq::Images& images = _frame.getImages(); 00151 eq::Image* image = images[ 0 ]; 00152 EQASSERT( image ); 00153 00154 Config* config = static_cast< Config* >( getConfig( )); 00155 const eq::PixelViewport& pvp = getPixelViewport(); 00156 const eq::Vector2i offset( pvp.x, pvp.y ); 00157 const eq::Zoom zoom( applyZoom, applyZoom ); 00158 00159 co::base::Clock clock; 00160 eq::Window::ObjectManager* glObjects = getObjectManager(); 00161 00162 //----- test all default format/type combinations 00163 ConfigEvent event = _createConfigEvent(); 00164 glGetError(); 00165 for( uint32_t i=0; _enums[i].internalFormatString; ++i ) 00166 { 00167 const uint32_t internalFormat = _enums[i].internalFormat; 00168 image->flush(); 00169 image->setInternalFormat( eq::Frame::BUFFER_COLOR, internalFormat ); 00170 image->setQuality( eq::Frame::BUFFER_COLOR, 0.f ); 00171 image->setAlphaUsage( false ); 00172 00173 const GLEWContext* glewContext = glewGetContext(); 00174 std::vector< uint32_t > names; 00175 image->findTransferers( eq::Frame::BUFFER_COLOR, glewContext, names ); 00176 00177 for( std::vector< uint32_t >::const_iterator j = names.begin(); 00178 j != names.end(); ++j ) 00179 { 00180 _draw( 0 ); 00181 00182 // setup 00183 event.formatType[31] = '\0'; 00184 event.data.type = ConfigEvent::READBACK; 00185 00186 image->allocDownloader( eq::Frame::BUFFER_COLOR, *j, glewContext ); 00187 image->setPixelViewport( pvp ); 00188 00189 const uint32_t outputToken = 00190 image->getExternalFormat( eq::Frame::BUFFER_COLOR ); 00191 snprintf( event.formatType, 32, "%s/%x/%x", 00192 _enums[i].internalFormatString, outputToken, *j ); 00193 00194 00195 // read 00196 glFinish(); 00197 size_t nLoops = 0; 00198 clock.reset(); 00199 while( clock.getTime64() < 100 /*ms*/ ) 00200 { 00201 image->readback( eq::Frame::BUFFER_COLOR, pvp, zoom, glObjects); 00202 ++nLoops; 00203 } 00204 glFinish(); 00205 event.msec = clock.getTimef() / float( nLoops ); 00206 00207 const eq::PixelData& pixels = 00208 image->getPixelData( eq::Frame::BUFFER_COLOR ); 00209 event.area.x() = pixels.pvp.w; 00210 event.area.y() = pixels.pvp.h; 00211 event.dataSizeGPU = pixels.pvp.getArea() * _enums[i].pixelSize; 00212 event.dataSizeCPU = 00213 image->getPixelDataSize( eq::Frame::BUFFER_COLOR ); 00214 00215 GLenum error = glGetError(); 00216 if( error != GL_NO_ERROR ) 00217 event.msec = -static_cast<float>( error ); 00218 config->sendEvent( event ); 00219 00220 eq::Compositor::ImageOp op; 00221 op.channel = this; 00222 op.buffers = eq::Frame::BUFFER_COLOR; 00223 op.offset = offset; 00224 op.zoom = zoom; 00225 00226 event.data.type = ConfigEvent::ASSEMBLE; 00227 event.dataSizeCPU = 00228 image->getPixelDataSize( eq::Frame::BUFFER_COLOR ); 00229 00230 clock.reset(); 00231 eq::Compositor::assembleImage( image, op ); 00232 event.msec = clock.getTimef(); 00233 00234 const eq::PixelData& pixelA = 00235 image->getPixelData( eq::Frame::BUFFER_COLOR ); 00236 event.area.x() = pixelA.pvp.w; 00237 event.area.y() = pixelA.pvp.h; 00238 event.dataSizeGPU = 00239 image->getPixelDataSize( eq::Frame::BUFFER_COLOR ); 00240 00241 error = glGetError(); 00242 00243 if( error != GL_NO_ERROR ) 00244 event.msec = -static_cast<float>( error ); 00245 config->sendEvent( event ); 00246 } 00247 } 00248 } 00249 00250 void Channel::_testTiledOperations() 00251 { 00252 //----- setup constant data 00253 const eq::Images& images = _frame.getImages(); 00254 EQASSERT( images[0] ); 00255 00256 eq::Config* config = getConfig(); 00257 const eq::PixelViewport& pvp = getPixelViewport(); 00258 const eq::Vector2i offset( pvp.x, pvp.y ); 00259 00260 ConfigEvent event = _createConfigEvent(); 00261 event.area.x() = pvp.w; 00262 00263 co::base::Clock clock; 00264 eq::Window::ObjectManager* glObjects = getObjectManager(); 00265 const GLEWContext* glewContext = glewGetContext(); 00266 00267 //----- test tiled assembly algorithms 00268 eq::PixelViewport subPVP = pvp; 00269 subPVP.h /= NUM_IMAGES; 00270 00271 for( unsigned i = 0; i < NUM_IMAGES; ++i ) 00272 { 00273 EQASSERT( images[ i ] ); 00274 images[ i ]->setPixelViewport( subPVP ); 00275 } 00276 00277 for( unsigned tiles = 0; tiles < NUM_IMAGES; ++tiles ) 00278 { 00279 _draw( 0 ); 00280 00281 event.area.y() = subPVP.h * (tiles+1); 00282 00283 //---- readback of 'tiles' depth images 00284 event.data.type = ConfigEvent::READBACK; 00285 snprintf( event.formatType, 32, "%d depth tiles", tiles+1 ); 00286 00287 event.msec = 0; 00288 for( unsigned j = 0; j <= tiles; ++j ) 00289 { 00290 subPVP.y = pvp.y + j * subPVP.h; 00291 eq::Image* image = images[ j ]; 00292 EQCHECK( image->allocDownloader( eq::Frame::BUFFER_DEPTH, 00293 EQ_COMPRESSOR_TRANSFER_DEPTH_TO_DEPTH_UNSIGNED_INT, 00294 glewContext )); 00295 image->clearPixelData( eq::Frame::BUFFER_DEPTH ); 00296 00297 clock.reset(); 00298 image->readback( eq::Frame::BUFFER_DEPTH, subPVP, eq::Zoom(), 00299 glObjects ); 00300 event.msec += clock.getTimef(); 00301 00302 } 00303 00304 config->sendEvent( event ); 00305 00306 if( tiles == NUM_IMAGES-1 ) 00307 for( unsigned j = 0; j <= tiles; ++j ) 00308 _saveImage( images[j], 00309 "EQ_COMPRESSOR_DATATYPE_DEPTH_UNSIGNED_INT", 00310 "tiles" ); 00311 00312 //---- readback of 'tiles' color images 00313 event.data.type = ConfigEvent::READBACK; 00314 snprintf( event.formatType, 32, "%d color tiles", tiles+1 ); 00315 00316 event.msec = 0; 00317 for( unsigned j = 0; j <= tiles; ++j ) 00318 { 00319 subPVP.y = pvp.y + j * subPVP.h; 00320 eq::Image* image = images[ j ]; 00321 00322 EQCHECK( image->allocDownloader( eq::Frame::BUFFER_COLOR, 00323 EQ_COMPRESSOR_TRANSFER_RGBA_TO_BGRA, 00324 glewContext )); 00325 image->clearPixelData( eq::Frame::BUFFER_COLOR ); 00326 00327 clock.reset(); 00328 image->readback( eq::Frame::BUFFER_COLOR, subPVP, eq::Zoom(), 00329 glObjects ); 00330 event.msec += clock.getTimef(); 00331 } 00332 config->sendEvent( event ); 00333 00334 if( tiles == NUM_IMAGES-1 ) 00335 for( unsigned j = 0; j <= tiles; ++j ) 00336 _saveImage( images[j],"EQ_COMPRESSOR_DATATYPE_BGRA","tiles" ); 00337 00338 //---- benchmark assembly operations 00339 subPVP.y = pvp.y + tiles * subPVP.h; 00340 00341 eq::Compositor::ImageOp op; 00342 op.channel = this; 00343 op.buffers = eq::Frame::BUFFER_COLOR | eq::Frame::BUFFER_DEPTH; 00344 op.offset = offset; 00345 00346 // fixed-function 00347 event.data.type = ConfigEvent::ASSEMBLE; 00348 snprintf( event.formatType, 32, "tiles, GL1.1, %d images", tiles+1 ); 00349 00350 clock.reset(); 00351 for( unsigned j = 0; j <= tiles; ++j ) 00352 eq::Compositor::assembleImage( images[j], op ); 00353 00354 event.msec = clock.getTimef(); 00355 config->sendEvent( event ); 00356 00357 // CPU 00358 snprintf( event.formatType, 32, "tiles, CPU, %d images", tiles+1 ); 00359 00360 std::vector< eq::Frame* > frames; 00361 frames.push_back( &_frame ); 00362 00363 clock.reset(); 00364 eq::Compositor::assembleFramesCPU( frames, this ); 00365 event.msec = clock.getTimef(); 00366 config->sendEvent( event ); 00367 } 00368 } 00369 00370 void Channel::_testDepthAssemble() 00371 { 00372 //----- setup constant data 00373 const eq::Images& images = _frame.getImages(); 00374 eq::Image* image = images[ 0 ]; 00375 EQASSERT( image ); 00376 00377 eq::Config* config = getConfig(); 00378 const eq::PixelViewport& pvp = getPixelViewport(); 00379 const eq::Vector2i offset( pvp.x, pvp.y ); 00380 00381 ConfigEvent event = _createConfigEvent(); 00382 event.area.x() = pvp.w; 00383 00384 co::base::Clock clock; 00385 eq::Window::ObjectManager* glObjects = getObjectManager(); 00386 const GLEWContext* glewContext = glewGetContext(); 00387 00388 //----- test depth-based assembly algorithms 00389 for( unsigned i = 0; i < NUM_IMAGES; ++i ) 00390 { 00391 image = images[ i ]; 00392 EQASSERT( image ); 00393 image->setPixelViewport( pvp ); 00394 } 00395 00396 event.area.y() = pvp.h; 00397 00398 for( unsigned i = 0; i < NUM_IMAGES; ++i ) 00399 { 00400 _draw( i ); 00401 00402 // fill depth & color image 00403 image = images[ i ]; 00404 00405 EQCHECK( image->allocDownloader( eq::Frame::BUFFER_COLOR, 00406 EQ_COMPRESSOR_TRANSFER_RGBA_TO_BGRA, 00407 glewContext )); 00408 00409 EQCHECK( image->allocDownloader( eq::Frame::BUFFER_DEPTH, 00410 EQ_COMPRESSOR_TRANSFER_DEPTH_TO_DEPTH_UNSIGNED_INT, 00411 glewContext )); 00412 00413 image->clearPixelData( eq::Frame::BUFFER_COLOR ); 00414 image->clearPixelData( eq::Frame::BUFFER_DEPTH ); 00415 00416 image->readback( eq::Frame::BUFFER_COLOR | eq::Frame::BUFFER_DEPTH, 00417 pvp, eq::Zoom(), glObjects ); 00418 00419 if( i == NUM_IMAGES-1 ) 00420 _saveImage( image,"EQ_COMPRESSOR_DATATYPE_DEPTH_UNSIGNED_INT", 00421 "depthAssemble" ); 00422 00423 // benchmark 00424 eq::Compositor::ImageOp op; 00425 op.channel = this; 00426 op.buffers = eq::Frame::BUFFER_COLOR | eq::Frame::BUFFER_DEPTH; 00427 op.offset = offset; 00428 00429 // fixed-function 00430 event.data.type = ConfigEvent::ASSEMBLE; 00431 snprintf( event.formatType, 32, "depth, GL1.1, %d images", i+1 ); 00432 00433 clock.reset(); 00434 for( unsigned j = 0; j <= i; ++j ) 00435 eq::Compositor::assembleImageDB_FF( images[j], op ); 00436 00437 event.msec = clock.getTimef(); 00438 config->sendEvent( event ); 00439 00440 // GLSL 00441 if( GLEW_VERSION_2_0 ) 00442 { 00443 snprintf( event.formatType, 32, "depth, GLSL, %d images", i+1 ); 00444 00445 clock.reset(); 00446 for( unsigned j = 0; j <= i; ++j ) 00447 eq::Compositor::assembleImageDB_GLSL( images[j], op ); 00448 event.msec = clock.getTimef(); 00449 config->sendEvent( event ); 00450 } 00451 00452 // CPU 00453 snprintf( event.formatType, 32, "depth, CPU, %d images", i+1 ); 00454 00455 std::vector< eq::Frame* > frames; 00456 frames.push_back( &_frame ); 00457 00458 clock.reset(); 00459 eq::Compositor::assembleFramesCPU( frames, this ); 00460 event.msec = clock.getTimef(); 00461 config->sendEvent( event ); 00462 } 00463 } 00464 00465 void Channel::_saveImage( const eq::Image* image, 00466 const char* externalformat, 00467 const char* info ) 00468 { 00469 return; 00470 00471 static uint32_t counter = 0; 00472 std::ostringstream stringstream; 00473 stringstream << "Image_" << ++counter << "_" << externalformat << "_" 00474 << info; 00475 image->writeImages( stringstream.str( )); 00476 } 00477 00478 void Channel::_draw( const eq::uint128_t& spin ) 00479 { 00480 glPushAttrib( GL_ALL_ATTRIB_BITS ); 00481 00482 eq::Channel::frameDraw( spin ); 00483 00484 glClear( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT ); 00485 glEnable( GL_DEPTH_TEST ); 00486 00487 #if 0 00488 setNearFar( 0.5f, 5.0f ); 00489 const GLfloat lightPosition[] = {5.0f, 0.0f, 5.0f, 0.0f}; 00490 const GLfloat lightDiffuse[] = {0.8f, 0.8f, 0.8f, 1.0f}; 00491 00492 const GLfloat materialDiffuse[] = {0.8f, 0.8f, 0.8f, 1.0f}; 00493 00494 glLightfv( GL_LIGHT0, GL_POSITION, lightPosition ); 00495 glLightfv( GL_LIGHT0, GL_DIFFUSE, lightDiffuse ); 00496 00497 glMaterialfv( GL_FRONT, GL_DIFFUSE, materialDiffuse ); 00498 00499 eq::Matrix4f rotation; 00500 eq::Vector3f translation; 00501 00502 translation = eq::Vector3f::ZERO; 00503 translation.z = -2.f; 00504 rotation = eq::Matrix4f::IDENTITY; 00505 rotation.rotate_x( static_cast<float>( -M_PI_2 )); 00506 rotation.rotate_y( static_cast<float>( -M_PI_2 )); 00507 00508 glTranslatef( translation.x, translation.y, translation.z ); 00509 glMultMatrixf( rotation.ml ); 00510 00511 glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); 00512 glColor3f( 1.f, 1.f, 0.f ); 00513 glNormal3f( 1.f, -1.f, 0.f ); 00514 glBegin( GL_TRIANGLE_STRIP ); 00515 glVertex3f( 1.f, 10.f, 2.5f ); 00516 glVertex3f( -1.f, 10.f, 2.5f ); 00517 glVertex3f( 1.f,-10.f, -2.5f ); 00518 glVertex3f( -1.f,-10.f, -2.5f ); 00519 glEnd(); 00520 00521 #else 00522 00523 const float lightPos[] = { 0.0f, 0.0f, 1.0f, 0.0f }; 00524 glLightfv( GL_LIGHT0, GL_POSITION, lightPos ); 00525 00526 const float lightAmbient[] = { 0.2f, 0.2f, 0.2f, 1.0f }; 00527 glLightfv( GL_LIGHT0, GL_AMBIENT, lightAmbient ); 00528 00529 // rotate scene around the origin 00530 glRotatef( static_cast< float >( spin.low() + 3 ) * 10, 1.0f, 0.5f, 0.25f ); 00531 00532 // render six axis-aligned colored quads around the origin 00533 // front 00534 glColor3f( 1.0f, 0.5f, 0.5f ); 00535 glNormal3f( 0.0f, 0.0f, 1.0f ); 00536 glBegin( GL_TRIANGLE_STRIP ); 00537 glVertex3f( .7f, .7f, -1.0f ); 00538 glVertex3f( -.7f, .7f, -1.0f ); 00539 glVertex3f( .7f, -.7f, -1.0f ); 00540 glVertex3f( -.7f, -.7f, -1.0f ); 00541 glEnd(); 00542 00543 // bottom 00544 glColor3f( 0.5f, 1.0f, 0.5f ); 00545 glNormal3f( 0.0f, 1.0f, 0.0f ); 00546 glBegin( GL_TRIANGLE_STRIP ); 00547 glVertex3f( .7f, -1.0f, .7f ); 00548 glVertex3f( -.7f, -1.0f, .7f ); 00549 glVertex3f( .7f, -1.0f, -.7f ); 00550 glVertex3f( -.7f, -1.0f, -.7f ); 00551 glEnd(); 00552 00553 // back 00554 glColor3f( 0.5f, 0.5f, 1.0f ); 00555 glNormal3f( 0.0f, 0.0f, -1.0f ); 00556 glBegin( GL_TRIANGLE_STRIP ); 00557 glVertex3f( .7f, .7f, 1.0f ); 00558 glVertex3f( -.7f, .7f, 1.0f ); 00559 glVertex3f( .7f, -.7f, 1.0f ); 00560 glVertex3f( -.7f, -.7f, 1.0f ); 00561 glEnd(); 00562 00563 // top 00564 glColor3f( 1.0f, 1.0f, 0.5f ); 00565 glNormal3f( 0.f, -1.f, 0.f ); 00566 glBegin( GL_TRIANGLE_STRIP ); 00567 glVertex3f( .7f, 1.0f, .7f ); 00568 glVertex3f( -.7f, 1.0f, .7f ); 00569 glVertex3f( .7f, 1.0f, -.7f ); 00570 glVertex3f( -.7f, 1.0f, -.7f ); 00571 glEnd(); 00572 00573 // right 00574 glColor3f( 1.0f, 0.5f, 1.0f ); 00575 glNormal3f( -1.f, 0.f, 0.f ); 00576 glBegin( GL_TRIANGLE_STRIP ); 00577 glVertex3f( 1.0f, .7f, .7f ); 00578 glVertex3f( 1.0f, -.7f, .7f ); 00579 glVertex3f( 1.0f, .7f, -.7f ); 00580 glVertex3f( 1.0f, -.7f, -.7f ); 00581 glEnd(); 00582 00583 // left 00584 glColor3f( 0.5f, 1.0f, 1.0f ); 00585 glNormal3f( 1.f, 0.f, 0.f ); 00586 glBegin( GL_TRIANGLE_STRIP ); 00587 glVertex3f( -1.0f, .7f, .7f ); 00588 glVertex3f( -1.0f, -.7f, .7f ); 00589 glVertex3f( -1.0f, .7f, -.7f ); 00590 glVertex3f( -1.0f, -.7f, -.7f ); 00591 glEnd(); 00592 00593 #endif 00594 00595 glPopAttrib( ); 00596 } 00597 00598 00599 }