void SVG::renderToTexture(const QRectF& textureRect, QGLFramebufferObjectPtr targetFbo) { saveGLState(); // generate and set view box in logical coordinates QRectF viewbox(svgExtents_.x() + textureRect.x() * svgExtents_.width(), svgExtents_.y() + textureRect.y() * svgExtents_.height(), textureRect.width() * svgExtents_.width(), textureRect.height() * svgExtents_.height()); svgRenderer_.setViewBox(viewbox); // Multisampled FBO for anti-aliased rendering QGLFramebufferObjectFormat format; format.setAttachment(QGLFramebufferObject::CombinedDepthStencil); format.setSamples(MULTI_SAMPLE_ANTI_ALIASING_SAMPLES); QGLFramebufferObjectPtr renderFbo( new QGLFramebufferObject( targetFbo->size(), format )); // Render to multisampled FBO QPainter painter(renderFbo.get()); painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); svgRenderer_.render(&painter); painter.end(); // Copy to texture FBO QGLFramebufferObject::blitFramebuffer( targetFbo.get(), QRect(0, 0, renderFbo->width(), renderFbo->height()), renderFbo.get(), QRect(0, 0, renderFbo->width(), renderFbo->height())); glBindTexture(GL_TEXTURE_2D, targetFbo->texture()); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); restoreGLState(); }
int setSavedGLState(int target) { int error; DMARK /* restore original gl state */ ORIG_GL(glPopAttrib)(); error = glError(); if (error) { return error; } /* save original gl state */ error = saveGLState(); if (error) { return error; } /* FIXME CHECK AGAIN!!!! */ #if 0 /* disable everything that could interfere when writting the debug result to * the debug buffer and setup draw and read buffer. */ error = setDbgRenderState(target); if (error) { return error; } #endif return DBG_NO_ERROR; }
void GLWidget::draw() { QPainter p(this); // used for text overlay // save the GL state set for QPainter saveGLState(); // render the 'bubbles.svg' file into our framebuffer object QPainter fbo_painter(fbo); svg_renderer->render(&fbo_painter); fbo_painter.end(); // draw into the GL widget glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustum(-1, 1, -1, 1, 10, 100); glTranslatef(0.0f, 0.0f, -15.0f); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glViewport(0, 0, width(), height()); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBindTexture(GL_TEXTURE_2D, fbo->texture()); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glEnable(GL_TEXTURE_2D); glEnable(GL_MULTISAMPLE); glEnable(GL_CULL_FACE); // draw background glPushMatrix(); glScalef(1.7f, 1.7f, 1.7f); glColor4f(1.0f, 1.0f, 1.0f, 1.0f); glCallList(tile_list); glPopMatrix(); const int w = logo.width(); const int h = logo.height(); glRotatef(rot_x, 1.0f, 0.0f, 0.0f); glRotatef(rot_y, 0.0f, 1.0f, 0.0f); glRotatef(rot_z, 0.0f, 0.0f, 1.0f); glScalef(scale/w, scale/w, scale/w); glDepthFunc(GL_LESS); glEnable(GL_DEPTH_TEST); // draw the Qt icon glTranslatef(-w+1, -h+1, 0.0f); for (int y=h-1; y>=0; --y) { uint *p = (uint*) logo.scanLine(y); uint *end = p + w; int x = 0; while (p < end) { glColor4ub(qRed(*p), qGreen(*p), qBlue(*p), uchar(qAlpha(*p)*.9)); glTranslatef(0.0f, 0.0f, wave[y*w+x]); if (qAlpha(*p) > 128) glCallList(tile_list); glTranslatef(0.0f, 0.0f, -wave[y*w+x]); glTranslatef(2.0f, 0.0f, 0.0f); ++x; ++p; } glTranslatef(-w*2.0f, 2.0f, 0.0f); } // restore the GL state that QPainter expects restoreGLState(); // draw the overlayed text using QPainter p.setPen(QColor(197, 197, 197, 157)); p.setBrush(QColor(197, 197, 197, 127)); p.drawRect(QRect(0, 0, width(), 50)); p.setPen(Qt::black); p.setBrush(Qt::NoBrush); const QString str1(tr("A simple OpenGL framebuffer object example.")); const QString str2(tr("Use the mouse wheel to zoom, press buttons and move mouse to rotate, double-click to flip.")); QFontMetrics fm(p.font()); p.drawText(width()/2 - fm.width(str1)/2, 20, str1); p.drawText(width()/2 - fm.width(str2)/2, 20 + fm.lineSpacing(), str2); }
// Call on UI thread to copy from the shared Surface Texture to the Tile's texture. void TransferQueue::updateDirtyTiles() { android::Mutex::Autolock lock(m_transferQueueItemLocks); cleanupPendingDiscard(); if (!getHasGLContext()) setHasGLContext(true); // Check the pure color tile first, since it is simpler. updatePureColorTiles(); // Start from the oldest item, we call the updateTexImage to retrive // the texture and blit that into each Tile's texture. const int nextItemIndex = getNextTransferQueueIndex(); int index = nextItemIndex; bool usedFboForUpload = false; for (int k = 0; k < m_transferQueueSize ; k++) { if (m_transferQueue[index].status == pendingBlit) { bool obsoleteTile = checkObsolete(&m_transferQueue[index]); // Save the needed info, update the Surf Tex, clean up the item in // the queue. Then either move on to next item or copy the content. TileTexture* destTexture = 0; if (!obsoleteTile) destTexture = m_transferQueue[index].savedTilePtr->backTexture(); if (m_transferQueue[index].uploadType == GpuUpload) { status_t result = m_sharedSurfaceTexture->updateTexImage(); if (result != OK) ALOGE("unexpected error: updateTexImage return %d", result); } if (obsoleteTile) { ALOGV("Warning: the texture is obsolete for this baseTile"); clearItemInTranferQueue(index); index = (index + 1) % m_transferQueueSize; continue; } // guarantee that we have a texture to blit into destTexture->requireGLTexture(); GLUtils::checkGlError("before blitTileFromQueue"); if (m_transferQueue[index].uploadType == CpuUpload) { // Here we just need to upload the bitmap content to the GL Texture GLUtils::updateTextureWithBitmap(destTexture->m_ownTextureId, *m_transferQueue[index].bitmap); } else { if (!usedFboForUpload) { saveGLState(); usedFboForUpload = true; } blitTileFromQueue(m_fboID, destTexture, m_sharedSurfaceTextureId, m_sharedSurfaceTexture->getCurrentTextureTarget(), index); } destTexture->setPure(false); destTexture->transferComplete(); clearItemInTranferQueue(index); ALOGV("Blit tile x, y %d %d with dest texture %p to destTexture->m_ownTextureId %d", m_transferQueue[index].savedTilePtr, destTexture, destTexture->m_ownTextureId); } index = (index + 1) % m_transferQueueSize; } // Clean up FBO setup. Doing this for both CPU/GPU upload can make the // dynamic switch possible. Moving this out from the loop can save some // milli-seconds. if (usedFboForUpload) { restoreGLState(); GLUtils::checkGlError("updateDirtyTiles"); } m_emptyItemCount = m_transferQueueSize; m_transferQueueItemCond.signal(); }
// Call on UI thread to copy from the shared Surface Texture to the BaseTile's texture. void TransferQueue::updateDirtyBaseTiles() { android::Mutex::Autolock lock(m_transferQueueItemLocks); cleanupTransportQueue(); if (!getHasGLContext()) setHasGLContext(true); // Start from the oldest item, we call the updateTexImage to retrive // the texture and blit that into each BaseTile's texture. const int nextItemIndex = getNextTransferQueueIndex(); int index = nextItemIndex; bool usedFboForUpload = false; for (int k = 0; k < ST_BUFFER_NUMBER ; k++) { if (m_transferQueue[index].status == pendingBlit) { bool obsoleteBaseTile = checkObsolete(index); // Save the needed info, update the Surf Tex, clean up the item in // the queue. Then either move on to next item or copy the content. BaseTileTexture* destTexture = 0; if (!obsoleteBaseTile) destTexture = m_transferQueue[index].savedBaseTilePtr->backTexture(); if (m_transferQueue[index].uploadType == GpuUpload) { status_t result = m_sharedSurfaceTexture->updateTexImage(); if (result != OK) XLOGC("unexpected error: updateTexImage return %d", result); } m_transferQueue[index].savedBaseTilePtr = 0; m_transferQueue[index].status = emptyItem; if (obsoleteBaseTile) { XLOG("Warning: the texture is obsolete for this baseTile"); index = (index + 1) % ST_BUFFER_NUMBER; continue; } // guarantee that we have a texture to blit into destTexture->requireGLTexture(); if (m_transferQueue[index].uploadType == CpuUpload) { // Here we just need to upload the bitmap content to the GL Texture GLUtils::updateTextureWithBitmap(destTexture->m_ownTextureId, 0, 0, *m_transferQueue[index].bitmap); } else { if (!usedFboForUpload) { saveGLState(); usedFboForUpload = true; } blitTileFromQueue(m_fboID, destTexture, m_sharedSurfaceTextureId, m_sharedSurfaceTexture->getCurrentTextureTarget(), index); } // After the base tile copied into the GL texture, we need to // update the texture's info such that at draw time, readyFor // will find the latest texture's info // We don't need a map any more, each texture contains its own // texturesTileInfo. destTexture->setOwnTextureTileInfoFromQueue(&m_transferQueue[index].tileInfo); XLOG("Blit tile x, y %d %d with dest texture %p to destTexture->m_ownTextureId %d", m_transferQueue[index].tileInfo.m_x, m_transferQueue[index].tileInfo.m_y, destTexture, destTexture->m_ownTextureId); } index = (index + 1) % ST_BUFFER_NUMBER; } // Clean up FBO setup. Doing this for both CPU/GPU upload can make the // dynamic switch possible. Moving this out from the loop can save some // milli-seconds. if (usedFboForUpload) { glBindFramebuffer(GL_FRAMEBUFFER, 0); // rebind the standard FBO restoreGLState(); GLUtils::checkGlError("updateDirtyBaseTiles"); } m_emptyItemCount = ST_BUFFER_NUMBER; m_transferQueueItemCond.signal(); }
void Visualizer::paintGL() { QPainter painter; painter.begin(this); // qApp->processEvents(); //cout << "paintGL called" << endl; model->gui_ready = true; model->drawing = true; if (model->paths_mutex) { cout << "Visualizer: paths_mutex is set" << endl; model->paths_mutex_seen = true; return; } if (model->targets_mutex) { cout << "Visualizer: targets_mutex is set" << endl; model->targets_mutex_seen = true; return; } if (model->searchers_mutex) { cout << "Visualizer: searchers_mutex is set" << endl; model->searchers_mutex_seen = true; return; } x_min_bound = getModel()->getSearchSpace()->getXMinBound(); y_min_bound = getModel()->getSearchSpace()->getYMinBound(); z_min_bound = getModel()->getSearchSpace()->getZMinBound(); x_max_bound = getModel()->getSearchSpace()->getXMaxBound(); y_max_bound = getModel()->getSearchSpace()->getYMaxBound(); z_max_bound = getModel()->getSearchSpace()->getZMaxBound(); saveGLState(); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); //glTranslatef(0.0, 0.0, -10.0); glFrustum(-1, 1, -1, 1, 0, 100); glViewport(0, 0, 2*width(), 2*height()); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); //glTranslatef(0.0f, 0.0f, -15.0f); glRotatef(xRot / 16.0, 1.0, 0.0, 0.0); glRotatef(yRot / 16.0, 0.0, 1.0, 0.0); glRotatef(zRot / 16.0, 0.0, 0.0, 1.0); glTranslatef(horz_pan, vert_pan, 0.0f); //glScalef (1.0, 1.0, 1.0); /* modeling transformation */ glScalef(scale, scale, scale); glColor4f(1.0, 1.0, 1.0, 0.3); drawRectangularPrism(x_min_bound, y_min_bound, z_min_bound, x_max_bound, y_max_bound, z_max_bound); QPointF pos; QPointF center_pos; SearchSpace* space = model->getSearchSpace(); int n_searchers = space->getNumSearchers(); Searcher* searchers = space->getSearchers(); //cout << "Number of searchers: " << n_searchers << endl; // draw boundary QRect frame_rect = this->contentsRect(); int frame_height = frame_rect.height(); int frame_width = frame_rect.width(); center_pos = frame_rect.center(); for ( int i = 0; i < n_searchers; i++ ) { float scaled_x = searchers[i].getXPos();//*frame_width/2+center_pos.x(); float scaled_y = searchers[i].getYPos();//*frame_height/2+center_pos.y(); float scaled_z = searchers[i].getZPos();//*frame_height/2+center_pos.y(); glColor3f(searchers[i].red/255.0, searchers[i].blue/255.0, searchers[i].green/255.0); GLUquadricObj *quadric=gluNewQuadric(); gluQuadricNormals(quadric, GLU_SMOOTH); glPushMatrix(); glTranslatef( scaled_x, scaled_y, scaled_z ); glPushMatrix(); gluSphere(quadric, target_display_radius, 10,10); glPopMatrix(); gluDeleteQuadric(quadric); //cout << "Raw (" << searchers[i].getXPos() << ", " << searchers[i].getYPos() << ", " << searchers[i].getZPos() <<")" << endl; //cout << "Scaled (" << scaled_x << ", " << scaled_y << ", " << scaled_z << ")" << endl; // Display path vector<Coordinate*> path = searchers[i].getPath(); for(vector<Coordinate*>::iterator it = path.begin(); it != path.end(); ++it) { float scaled_path_x = (*it)->getX();//*frame_width/2;//+center_pos.x(); float scaled_path_y = (*it)->getY();//*frame_height/2;//+center_pos.y(); float scaled_path_z = (*it)->getZ();//*frame_height/2;//+center_pos.y(); // <---- FIX FOR Z Coordinate start, end; start.setX(scaled_path_x); start.setY(scaled_path_y); start.setZ(scaled_path_z); if (it+1 != path.end()) { scaled_path_x = (*(it+1))->getX();//*frame_width/2;//+center_pos.x(); scaled_path_y = (*(it+1))->getY();//*frame_height/2;//+center_pos.y(); scaled_path_z = (*(it+1))->getZ();//*frame_height/2;//+center_pos.y(); } else { break; } end.setX(scaled_path_x); end.setY(scaled_path_y); end.setZ(scaled_path_z); //painter.drawLine(path_segment); glBegin(GL_LINES); glVertex3d(start.getX(), start.getY(), start.getZ()); glVertex3d(end.getX(), end.getY(), end.getZ()); glEnd(); } } // display targets int n_targets = space->getNumTargets(); Target* targets = space->getTargets(); for ( int i = 0; i < n_targets; i++ ) { if ( targets[i].isFound() ) glColor4f( 0.0, 1.0, 1.0, 1.0 ); else glColor4f( 0.0, 1.0, 0.0, 0.2 ); GLUquadricObj *quadric=gluNewQuadric(); gluQuadricNormals(quadric, GLU_SMOOTH); glPushMatrix(); glTranslatef( targets[i].getXPos(),targets[i].getYPos() ,targets[i].getZPos() ); glPushMatrix(); gluSphere(quadric, target_display_radius, 10,10); glPopMatrix(); gluDeleteQuadric(quadric); } restoreGLState(); swapBuffers(); //emit VisFinishedPaintGL(); QString framesPerSecond; framesPerSecond.setNum(frames /(time.elapsed() / 1000.0), 'f', 2); painter.setPen(Qt::white); painter.drawText(20, 40, framesPerSecond + " fps"); painter.end(); swapBuffers(); frames++; if (!(frames % 100)) { time.start(); frames = 0; } model->drawing = false; //glFinish(); //qApp->processEvents(); }
void Visualizer::paintGL() { // Wait for lock on model. Need this to avoid concurrency issues when reading searcher and target data structures. model->lock(); QPainter painter; painter.begin(this); // qApp->processEvents(); //cout << "paintGL called" << endl; model->gui_ready = true; model->drawing = true; SearchSpace* space = model->getSearchSpace(); int n_targets = 0; Target* targets = space->getTargetsCopy(n_targets); int n_searchers = 0; Searcher* searchers = space->getSearchersCopy(n_searchers); x_min_bound = getModel()->getSearchSpace()->getXMinBound(); y_min_bound = getModel()->getSearchSpace()->getYMinBound(); z_min_bound = getModel()->getSearchSpace()->getZMinBound(); x_max_bound = getModel()->getSearchSpace()->getXMaxBound(); y_max_bound = getModel()->getSearchSpace()->getYMaxBound(); z_max_bound = getModel()->getSearchSpace()->getZMaxBound(); float x_center = (x_max_bound-x_min_bound)/2; float y_center = (y_max_bound-y_min_bound)/2; float z_center = (z_max_bound-z_min_bound)/2; saveGLState(); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); //glTranslatef(0.0, 0.0, -10.0); glFrustum(-1, 1, -1, 1, 0, 0); // glViewport(0, 0, 2*width(), 2*height()); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); //glTranslatef(0.0f, 0.0f, -15.0f); glRotatef(xRot / 16.0, 1.0, 0.0, 0.0); glRotatef(yRot / 16.0, 0.0, 1.0, 0.0); glRotatef(zRot / 16.0, 0.0, 0.0, 1.0); glTranslatef(horz_pan, vert_pan, 0.0f); //glScalef (1.0, 1.0, 1.0); /* modeling transformation */ glScalef(scale, scale, scale); glColor4f(1.0, 1.0, 1.0, 0.3); drawRectangularPrism(x_min_bound-x_center, y_min_bound-y_center, z_min_bound-z_center, x_max_bound-x_center, y_max_bound-y_center, z_max_bound-z_center); QPointF pos; QPointF center_pos; //int n_searchers = space->getNumSearchers(); //Searcher* searchers = space->getSearchers(); //cout << "Number of searchers: " << n_searchers << endl; // draw boundary QRect frame_rect = this->contentsRect(); int frame_height = frame_rect.height(); int frame_width = frame_rect.width(); center_pos = frame_rect.center(); for ( int i = 0; i < n_searchers; i++ ) { float scaled_x = searchers[i].getXPos()-x_center; float scaled_y = searchers[i].getYPos()-y_center; float scaled_z = searchers[i].getZPos()-z_center; glColor3f(searchers[i].red/255.0, searchers[i].blue/255.0, searchers[i].green/255.0); GLUquadricObj *quadric=gluNewQuadric(); gluQuadricNormals(quadric, GLU_SMOOTH); glPushMatrix(); glTranslatef( scaled_x, scaled_y, scaled_z ); glPushMatrix(); gluSphere(quadric, target_display_radius, 10,10); glPopMatrix(); gluDeleteQuadric(quadric); //cout << "Raw (" << searchers[i].getXPos() << ", " << searchers[i].getYPos() << ", " << searchers[i].getZPos() <<")" << endl; //cout << "Scaled (" << scaled_x << ", " << scaled_y << ", " << scaled_z << ")" << endl; // Display path vector<Coordinate*> path = searchers[i].getPath(); for(vector<Coordinate*>::iterator it = path.begin(); it != path.end(); ++it) { if (*(it+1) == NULL || *it == NULL) { // cout << "Tried to display path but it seems to have been invalidated" << endl; continue; // Trying to avoid occasional concurrent change errors } float scaled_path_x = (*it)->getX()-x_center;//*frame_width/2;//+center_pos.x(); float scaled_path_y = (*it)->getY()-y_center;//*frame_height/2;//+center_pos.y(); float scaled_path_z = (*it)->getZ()-z_center;//*frame_height/2;//+center_pos.y(); // <---- FIX FOR Z Coordinate start, end; start.setX(scaled_path_x); start.setY(scaled_path_y); start.setZ(scaled_path_z); if (it+1 != path.end()) { scaled_path_x = (*(it+1))->getX()-x_center;//*frame_width/2;//+center_pos.x(); scaled_path_y = (*(it+1))->getY()-y_center;//*frame_height/2;//+center_pos.y(); scaled_path_z = (*(it+1))->getZ()-z_center;//*frame_height/2;//+center_pos.y(); } else { break; } end.setX(scaled_path_x); end.setY(scaled_path_y); end.setZ(scaled_path_z); //painter.drawLine(path_segment); glBegin(GL_LINES); glVertex3d(start.getX(), start.getY(), start.getZ()); glVertex3d(end.getX(), end.getY(), end.getZ()); glEnd(); } } // display targets for ( int i = 0; i < n_targets; i++ ) { if ( targets[i].getXPos() < x_min_bound || targets[i].getXPos() > x_max_bound || targets[i].getYPos() < y_min_bound || targets[i].getYPos() > y_max_bound || targets[i].getZPos() < z_min_bound || targets[i].getZPos() > z_max_bound ) continue; // This can happen when targets are being repositioned at the start of a new target placement - looks ugly if ( targets[i].isFound() ) glColor4f( 0.0, 1.0, 1.0, 1.0 ); else glColor4f( 0.0, 1.0, 0.0, 0.2 ); GLUquadricObj *quadric=gluNewQuadric(); gluQuadricNormals(quadric, GLU_SMOOTH); glPushMatrix(); glTranslatef( targets[i].getXPos()-x_center,targets[i].getYPos()-y_center ,targets[i].getZPos()-z_center ); glPushMatrix(); gluSphere(quadric, target_display_radius, 10,10); glPopMatrix(); gluDeleteQuadric(quadric); } restoreGLState(); //swapBuffers(); //emit VisFinishedPaintGL(); QString framesPerSecond; framesPerSecond.setNum(frames /(time.elapsed() / 1000.0), 'f', 2); painter.setPen(Qt::white); painter.drawText(20, 40, framesPerSecond + " fps"); painter.end(); swapBuffers(); frames++; if (!(frames % 100)) { time.start(); frames = 0; } model->drawing = false; delete [] targets; delete [] searchers; // Release the model lock so the model can continue processessing model->unlock(); //glFinish(); //qApp->processEvents(); }
void RenderViewWidget::draw(){ //Setup shaders Assert(hdrShaderIdx >= 0 && ((size_t)hdrShaderIdx) < hdrFragShaders.size()); // QGLFormat format; // QGLContext context(format); // QGLShaderProgram program(&context); QGLShaderProgram program(QGLContext::currentContext()); program.addShader( hdrFragShaders[hdrShaderIdx] ); program.link(); program.bind(); //Draw QPainter p(this); saveGLState(); glDisable(GL_LIGHTING); glDisable(GL_DEPTH_TEST); //Texture setup glBindTexture(GL_TEXTURE_2D, texture_fbo->texture() ); glEnable(GL_TEXTURE_2D); glEnable(GL_MULTISAMPLE); //TODO: HERE IS WHERE WE SHOULD GET THE IMAGE BUFFER int wim = 128; int him = 128; HDRImage* im = new HDRImage(wim,him); makeIm(im); float* buf = im->getPixelBuf(); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, //Internal format wim, him, 0, GL_RGB, GL_FLOAT, (void*) buf ); delete im; //Full screen quad code from here: //http://www.michimichbeck.de/wordpress/tag/full-screen-quad/ const float ww = 1; const float hh = 1; const float z = -1; glRasterPos2i(0,0); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-1,1,-1,1, 1.0, 40.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glViewport(0,0,w,h); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glBegin (GL_QUADS); glTexCoord2f(0.0f, 0.0f); glVertex3f(-ww, -hh, z); glTexCoord2f(0.0f, 1.0f); glVertex3f(ww, -hh, z); glTexCoord2f(1.0f, 1.0f); glVertex3f(ww, hh , z); glTexCoord2f(0.0f, 1.0f); glVertex3f(-ww, hh, z ); glEnd(); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); restoreGLState(); }