Salome HOME
4d5e6bbbd568f65bbdb854ae4434496acdd40032
[modules/gui.git] / src / VTKViewer / VTKViewer_OpenGLRenderer.cxx
1 // Copyright (C) 2007-2022  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 // to compile, otherwise we get:
24 // #error gl.h included before glew.h
25 #include <vtkglew/include/GL/glew.h>
26
27 #include "VTKViewer_OpenGLRenderer.h"
28 #include "VTKViewer_Texture.h"
29
30 #include <vtkCuller.h>
31 #include <vtkLightCollection.h>
32 #include <vtkObjectFactory.h>
33 #include <vtkOpenGLCamera.h>
34 #include <vtkOpenGLLight.h>
35 #include <vtkOpenGLProperty.h>
36 #include <vtkRenderWindow.h>
37 #ifndef VTK_OPENGL2
38 #include <vtkOpenGLExtensionManager.h>
39 #include <vtkgl.h> // vtkgl namespace
40 #endif
41 #include <vtkImageImport.h>
42 #include <vtkPNGWriter.h>
43 #include <vtkOpenGLTexture.h>
44 #include <vtkTimerLog.h>
45 #include <vtkOpenGL.h>
46 #include <vtkObjectFactory.h>
47
48 #include <Basics_Utils.hxx>
49
50 vtkStandardNewMacro(VTKViewer_OpenGLRenderer)
51
52 VTKViewer_OpenGLRenderer::VTKViewer_OpenGLRenderer()
53 {
54   this->GradientType = HorizontalGradient;
55
56 #ifdef VTK_OPENGL2
57   this->BackgroundProgram        = 0;
58   this->BackgroundVertexShader   = 0;
59   this->BackgroundFragmentShader = 0;
60   this->VertexArrayObject        = 0;
61
62   this->OpenGLHelper.Init();
63 #endif
64 }
65
66 VTKViewer_OpenGLRenderer::~VTKViewer_OpenGLRenderer()
67 {
68 }
69
70 void VTKViewer_OpenGLRenderer::SetGradientType( const int theGradientType )
71 {
72   this->GradientType = theGradientType;
73 }
74
75 void VTKViewer_OpenGLRenderer::GetCornersColor(Vec2D& cornersColor)
76 {
77   if( this->GradientBackground )
78   {
79     double* corner1 = 0;
80     double* corner2 = 0;
81     double* corner3 = 0;
82     double* corner4 = 0;
83     double dcorner1[3];
84     double dcorner2[3];
85
86     switch( this->GradientType )
87     {
88     case HorizontalGradient:
89       corner1 = this->Background;
90       corner2 = this->Background;
91       corner3 = this->Background2;
92       corner4 = this->Background2;
93       break;
94     case VerticalGradient:
95       corner1 = this->Background;
96       corner2 = this->Background2;
97       corner3 = this->Background2;
98       corner4 = this->Background;
99       break;
100     case FirstDiagonalGradient:
101       corner1 = this->Background;
102       corner3 = this->Background2;
103       dcorner1[0] = dcorner2[0] = 0.5F * ( corner1[0] + corner3[0] );
104       dcorner1[1] = dcorner2[1] = 0.5F * ( corner1[1] + corner3[1] );
105       dcorner1[2] = dcorner2[2] = 0.5F * ( corner1[2] + corner3[2] );
106       corner2 = dcorner1;
107       corner4 = dcorner2;
108       break;
109     case SecondDiagonalGradient:
110       corner2 = this->Background2;
111       corner4 = this->Background;
112       dcorner1[0] = dcorner2[0] = 0.5F * ( corner2[0] + corner4[0] );
113       dcorner1[1] = dcorner2[1] = 0.5F * ( corner2[1] + corner4[1] );
114       dcorner1[2] = dcorner2[2] = 0.5F * ( corner2[2] + corner4[2] );
115       corner1 = dcorner1;
116       corner3 = dcorner2;
117       break;
118     case FirstCornerGradient:
119       corner1 = this->Background2;
120       corner2 = this->Background2;
121       corner3 = this->Background;
122       corner4 = this->Background2;
123       break;
124     case SecondCornerGradient:
125       corner1 = this->Background2;
126       corner2 = this->Background2;
127       corner3 = this->Background2;
128       corner4 = this->Background;
129       break;
130     case ThirdCornerGradient:
131       corner1 = this->Background;
132       corner2 = this->Background2;
133       corner3 = this->Background2;
134       corner4 = this->Background2;
135       break;
136     case FourthCornerGradient:
137       corner1 = this->Background2;
138       corner2 = this->Background;
139       corner3 = this->Background2;
140       corner4 = this->Background2;
141       break;
142     default: // just in case
143       corner1 = this->Background;
144       corner2 = this->Background;
145       corner3 = this->Background;
146       corner4 = this->Background;
147       break;
148     }
149     cornersColor[0][0] = corner1[0]; cornersColor[0][1] = corner1[1]; cornersColor[0][2] = corner1[2];
150     cornersColor[1][0] = corner2[0]; cornersColor[1][1] = corner2[1]; cornersColor[1][2] = corner2[2];
151     cornersColor[2][0] = corner3[0]; cornersColor[2][1] = corner3[1]; cornersColor[2][2] = corner3[2];
152     cornersColor[3][0] = corner4[0]; cornersColor[3][1] = corner4[1]; cornersColor[3][2] = corner4[2];
153   }
154   else
155   {
156     for (int i = 0; i < 4; i++)
157       for(int j = 0; j < 3; j++)
158         cornersColor[i][j] = this->Background[j];
159   }
160 }
161
162 void VTKViewer_OpenGLRenderer::Clear(void)
163 {
164   vtkOpenGLRenderer::Clear();
165 #ifdef VTK_OPENGL2
166   if (this->OpenGLHelper.IsInitialized())
167   {
168     if (this->BackgroundProgram == 0)
169     {
170 #if defined(WIN32) && defined(UNICODE)
171       std::wstring wFilePath = std::wstring( _wgetenv(L"GUI_ROOT_DIR") ) + L"/share/salome/resources/gui/Background";
172       std::string filePath = Kernel_Utils::utf8_encode_s( wFilePath );
173 #else
174       std::string filePath = std::string(getenv("GUI_ROOT_DIR")) + "/share/salome/resources/gui/Background";
175 #endif
176
177       if (!this->OpenGLHelper.CreateShaderProgram (filePath,
178                                                    this->BackgroundProgram,
179                                                    this->BackgroundVertexShader,
180                                                    this->BackgroundFragmentShader))
181       {
182         return;
183       }
184       // Get uniform locations.
185       this->OpenGLHelper.vglUseProgramObjectARB (this->BackgroundProgram);
186
187       this->myLocations.UseTexture        = this->OpenGLHelper.vglGetUniformLocationARB (this->BackgroundProgram, "uUseTexture");
188       this->myLocations.BackgroundTexture = this->OpenGLHelper.vglGetUniformLocationARB (this->BackgroundProgram, "uBackgroundTexture");
189
190       this->OpenGLHelper.vglUseProgramObjectARB (0);
191     }
192   }
193 #endif
194
195   GLbitfield clear_mask = 0;
196
197   if( !this->Transparent() )
198   {
199     glClearColor( static_cast<GLclampf>(this->Background[0]),
200                   static_cast<GLclampf>(this->Background[1]),
201                   static_cast<GLclampf>(this->Background[2]),
202                   static_cast<GLclampf>(this->BackgroundAlpha));
203     clear_mask |= GL_COLOR_BUFFER_BIT;
204   }
205
206   if( !this->GetPreserveDepthBuffer() )
207   {
208     glClearDepth(static_cast<GLclampf>(1.0));
209     clear_mask |= GL_DEPTH_BUFFER_BIT;
210   }
211
212   vtkDebugMacro(<< "glClear\n");
213   glClear(clear_mask);
214
215   // If gradient background is turned on, draw it now.
216   if( !this->Transparent() &&
217       ( this->GradientBackground || this->TexturedBackground ) )
218   {
219     double aTileViewport[4];
220     this->GetRenderWindow()->GetTileViewport( aTileViewport );
221     glPushAttrib( GL_ENABLE_BIT | GL_TRANSFORM_BIT | GL_TEXTURE_BIT );
222     glDisable( GL_ALPHA_TEST );
223     glDisable( GL_DEPTH_TEST );
224     glDisable( GL_LIGHTING );
225     glDisable( GL_TEXTURE_1D );
226     glDisable( GL_TEXTURE_2D );
227     glDisable( GL_BLEND );
228
229     GLint oldShadeModel;
230     glGetIntegerv(GL_SHADE_MODEL, &oldShadeModel);
231     glShadeModel( GL_SMOOTH ); // color interpolation
232
233     glMatrixMode( GL_PROJECTION );
234     glPushMatrix();
235     glLoadIdentity();
236     glMatrixMode( GL_MODELVIEW );
237     glPushMatrix();
238     glLoadIdentity();
239
240     glOrtho( aTileViewport[0], aTileViewport[2], aTileViewport[1], aTileViewport[3], -1.0, 1.0 );
241
242     if( this->GradientBackground )
243     {
244       Vec2D corners = {{0.0f,0.0f,0.0f},
245                      {0.0f,0.0f,0.0f},
246                      {0.0f,0.0f,0.0f},
247                      {0.0f,0.0f,0.0f}};
248       GetCornersColor(corners); 
249
250 #ifdef VTK_OPENGL2
251       if (this->OpenGLHelper.IsInitialized())
252       {
253         if (this->VertexArrayObject == 0)
254         {
255           this->OpenGLHelper.vglGenVertexArraysARB (1, &this->VertexArrayObject);
256         }
257
258         this->OpenGLHelper.vglUseProgramObjectARB (this->BackgroundProgram);
259         this->OpenGLHelper.vglBindVertexArrayARB  (this->VertexArrayObject);
260
261         GLfloat data[10 * 4];
262         if( this->GradientType != FirstCornerGradient && this->GradientType != ThirdCornerGradient )
263         {
264           const float tmpData[] = { (float)corners[0][0], (float)corners[0][1], (float)corners[0][2], 1.0f,       -1.0f,  1.0f, 0.0f,    0.0f, 0.0f, 0.0f,
265                                     (float)corners[1][0], (float)corners[1][1], (float)corners[1][2], 1.0f,       -1.0f, -1.0f, 0.0f,    0.0f, 0.0f, 0.0f,
266                                     (float)corners[2][0], (float)corners[2][1], (float)corners[2][2], 1.0f,        1.0f, -1.0f, 0.0f,    0.0f, 0.0f, 0.0f,
267                                     (float)corners[3][0], (float)corners[3][1], (float)corners[3][2], 1.0f,        1.0f,  1.0f, 0.0f,    0.0f, 0.0f, 0.0f};
268           memcpy (data, tmpData, sizeof(float) * 10 * 4);
269         }
270         else //if( this->GradientType == FirstCornerGradient || this->GradientType == ThirdCornerGradient )
271         {
272           const float tmpData[] = { (float)corners[1][0], (float)corners[1][1], (float)corners[1][2], 1.0f,       -1.0f, -1.0f, 0.0f,    0.0f, 0.0f, 0.0f,
273                                     (float)corners[2][0], (float)corners[2][1], (float)corners[2][2], 1.0f,       -1.0f,  1.0f, 0.0f,    0.0f, 0.0f, 0.0f,
274                                     (float)corners[3][0], (float)corners[3][1], (float)corners[3][2], 1.0f,        1.0f,  1.0f, 0.0f,    0.0f, 0.0f, 0.0f,
275                                     (float)corners[0][0], (float)corners[0][1], (float)corners[0][2], 1.0f,        1.0f, -1.0f, 0.0f,    0.0f, 0.0f, 0.0f};
276           memcpy (data, tmpData, sizeof(float) * 10 * 4);
277         }
278
279         GLuint vertexBuffer;
280         this->OpenGLHelper.vglGenBuffersARB (1, &vertexBuffer);
281         this->OpenGLHelper.vglBindBufferARB (GL_ARRAY_BUFFER_ARB, vertexBuffer);
282         this->OpenGLHelper.vglBufferDataARB (GL_ARRAY_BUFFER_ARB, sizeof(data), data, GL_STATIC_DRAW_ARB);
283
284         GLint colorAttrib  = this->OpenGLHelper.vglGetAttribLocationARB (this->BackgroundProgram, "Color");
285         GLint vertexAttrib = this->OpenGLHelper.vglGetAttribLocationARB (this->BackgroundProgram, "Vertex");
286         GLint bgColorAttrib = this->OpenGLHelper.vglGetAttribLocationARB (this->BackgroundProgram, "ColorBG");
287         GLsizei vertexSize = sizeof(GLfloat) * 10;
288
289         this->OpenGLHelper.vglVertexAttribPointerARB (colorAttrib, 4, GL_FLOAT, GL_FALSE, vertexSize, (const GLvoid*)0);
290         this->OpenGLHelper.vglEnableVertexAttribArrayARB (colorAttrib);
291
292         this->OpenGLHelper.vglVertexAttribPointerARB (vertexAttrib, 3, GL_FLOAT, GL_FALSE, vertexSize, (const GLvoid*)(sizeof(GLfloat) * 4));
293         this->OpenGLHelper.vglEnableVertexAttribArrayARB (vertexAttrib);
294
295         this->OpenGLHelper.vglVertexAttribPointerARB (bgColorAttrib, 3, GL_FLOAT, GL_FALSE, vertexSize, (const GLvoid*)(sizeof(GLfloat) * 7));
296         this->OpenGLHelper.vglEnableVertexAttribArrayARB (bgColorAttrib);
297
298         this->OpenGLHelper.vglUniform1iARB (this->myLocations.UseTexture, 0);
299
300         glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
301
302         this->OpenGLHelper.vglDisableVertexAttribArrayARB (0);
303         this->OpenGLHelper.vglBindBufferARB (GL_ARRAY_BUFFER_ARB, 0);
304         this->OpenGLHelper.vglDeleteBuffersARB (1, &vertexBuffer);
305         this->OpenGLHelper.vglBindVertexArrayARB (0);
306         this->OpenGLHelper.vglUseProgramObjectARB (0);
307       }
308 #else
309
310       glBegin( GL_TRIANGLE_FAN );
311       if( this->GradientType != FirstCornerGradient && this->GradientType != ThirdCornerGradient )
312       {
313         glColor3f( corners[0][0], corners[0][1], corners[0][2] ); glVertex2f( 0.F, 0.F );
314         glColor3f( corners[1][0], corners[1][1], corners[1][2] ); glVertex2f( 1.F, 0.F );
315         glColor3f( corners[2][0], corners[2][1], corners[2][2] ); glVertex2f( 1.F, 1.F );
316         glColor3f( corners[3][0], corners[3][1], corners[3][2] ); glVertex2f( 0.F, 1.F );
317       }
318       else //if( this->GradientType == FirstCornerGradient || this->GradientType == ThirdCornerGradient )
319       {
320         glColor3f( corners[1][0], corners[1][1], corners[1][2] ); glVertex2f( 1.F, 0.F );
321         glColor3f( corners[2][0], corners[2][1], corners[2][2] ); glVertex2f( 1.F, 1.F );
322         glColor3f( corners[3][0], corners[3][1], corners[3][2] ); glVertex2f( 0.F, 1.F );
323         glColor3f( corners[0][0], corners[0][1], corners[0][2] ); glVertex2f( 0.F, 0.F );
324       }
325       glEnd();
326 #endif
327     }
328
329     if( this->TexturedBackground && this->BackgroundTexture )
330     {
331       if( VTKViewer_Texture* aTexture = VTKViewer_Texture::SafeDownCast( this->BackgroundTexture ) )
332       {
333         glEnable( GL_TEXTURE_2D );
334
335         aTexture->Render( this );
336
337         // NOTE: By default the mode is GL_MODULATE. Since the user
338         // cannot set the mode, the default is set to replace.
339         glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
340         glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
341         glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
342
343         // NOTE: vtkTexture Render enables the alpha test
344         // so that no buffer is affected if alpha of incoming fragment is
345         // below the threshold. Here we have to enable it so that it won't
346         // rejects the fragments of the quad as the alpha is set to 0 on it.
347         glDisable( GL_ALPHA_TEST );
348
349         GLfloat texX = 1.F; // texture <s> coordinate
350         GLfloat texY = 1.F; // texture <t> coordinate
351
352         int aPosition = aTexture->GetPosition();
353         int aWidth = aTexture->GetWidth();
354         int aHeight = aTexture->GetHeight();
355         int aViewWidth = this->RenderWindow->GetSize()[0];
356         int aViewHeight = this->RenderWindow->GetSize()[1];
357
358         if( aPosition == VTKViewer_Texture::Tiled )
359         {
360           texX = (GLfloat)aViewWidth / (GLfloat)aWidth;
361           texY = (GLfloat)aViewHeight / (GLfloat)aHeight; texY *= -1;
362         }
363 #ifdef VTK_OPENGL2
364         if (this->OpenGLHelper.IsInitialized())
365         {
366           if (this->VertexArrayObject == 0)
367           {
368             this->OpenGLHelper.vglGenVertexArraysARB (1, &this->VertexArrayObject);
369           }
370
371           this->OpenGLHelper.vglUseProgramObjectARB (this->BackgroundProgram);
372           this->OpenGLHelper.vglBindVertexArrayARB  (this->VertexArrayObject);
373
374           GLfloat dx = (aPosition == VTKViewer_Texture::Centered) ? (( (GLfloat)aWidth / (GLfloat)aViewWidth )) : 1.0f;
375           GLfloat dy = (aPosition == VTKViewer_Texture::Centered) ? (( (GLfloat)aHeight / (GLfloat)aViewHeight )) : (aPosition == VTKViewer_Texture::Stretched) ? 1.0f : -1.0f;
376
377           // call to get backcolor
378           Vec2D bgColor = {{0.0f,0.0f,0.0f},
379                            {0.0f,0.0f,0.0f},
380                            {0.0f,0.0f,0.0f},
381                            {0.0f,0.0f,0.0f}};
382           GetCornersColor(bgColor);            
383           // First 4 components of Vertex is TexCoords now.
384           GLfloat data[10 * 4];
385           if( this->GradientType != FirstCornerGradient && this->GradientType != ThirdCornerGradient )
386           {
387             const float tmpData[] = { 0.0f, texY, 0.0f, 1.0f,       -dx,  dy, 0.0f, (float)bgColor[0][0], (float)bgColor[0][1], (float)bgColor[0][2],
388                                       0.0f, 0.0f, 0.0f, 1.0f,       -dx, -dy, 0.0f, (float)bgColor[1][0], (float)bgColor[1][1], (float)bgColor[1][2],
389                                       texX, 0.0f, 0.0f, 1.0f,        dx, -dy, 0.0f, (float)bgColor[2][0], (float)bgColor[2][1], (float)bgColor[2][2],
390                                       texX, texY, 0.0f, 1.0f,        dx,  dy, 0.0f, (float)bgColor[3][0], (float)bgColor[3][1], (float)bgColor[3][2] };
391             memcpy (data, tmpData, sizeof(float) * 10 * 4);
392           }
393           else //if( this->GradientType == FirstCornerGradient || this->GradientType == ThirdCornerGradient )
394           {
395             const float tmpData[] = { 0.0f, texY, 0.0f, 1.0f,       -dx,  dy, 0.0f, (float)bgColor[2][0], (float)bgColor[2][1], (float)bgColor[2][2],
396                                       0.0f, 0.0f, 0.0f, 1.0f,       -dx, -dy, 0.0f, (float)bgColor[1][0], (float)bgColor[1][1], (float)bgColor[1][2],
397                                       texX, 0.0f, 0.0f, 1.0f,        dx, -dy, 0.0f, (float)bgColor[0][0], (float)bgColor[0][1], (float)bgColor[0][2],
398                                       texX, texY, 0.0f, 1.0f,        dx,  dy, 0.0f, (float)bgColor[3][0], (float)bgColor[3][1], (float)bgColor[3][2] };
399             memcpy (data, tmpData, sizeof(float) * 10 * 4);
400           }
401
402           GLuint vertexBuffer;
403           this->OpenGLHelper.vglGenBuffersARB (1, &vertexBuffer);
404           this->OpenGLHelper.vglBindBufferARB (GL_ARRAY_BUFFER_ARB, vertexBuffer);
405           this->OpenGLHelper.vglBufferDataARB (GL_ARRAY_BUFFER_ARB, sizeof(data), data, GL_STATIC_DRAW_ARB);
406
407           GLint colorAttrib  = this->OpenGLHelper.vglGetAttribLocationARB (this->BackgroundProgram, "Color");
408           GLint vertexAttrib = this->OpenGLHelper.vglGetAttribLocationARB (this->BackgroundProgram, "Vertex");
409           GLint bgColorAttrib = this->OpenGLHelper.vglGetAttribLocationARB (this->BackgroundProgram, "ColorBG");
410           GLsizei vertexSize = sizeof(GLfloat) * 10;
411
412           this->OpenGLHelper.vglVertexAttribPointerARB (colorAttrib, 4, GL_FLOAT, GL_FALSE, vertexSize, (const GLvoid*)0);
413           this->OpenGLHelper.vglEnableVertexAttribArrayARB (colorAttrib);
414
415           this->OpenGLHelper.vglVertexAttribPointerARB (vertexAttrib, 3, GL_FLOAT, GL_FALSE, vertexSize, (const GLvoid*)(sizeof(GLfloat) * 4));
416           this->OpenGLHelper.vglEnableVertexAttribArrayARB (vertexAttrib);
417
418           this->OpenGLHelper.vglVertexAttribPointerARB (bgColorAttrib, 3, GL_FLOAT, GL_FALSE, vertexSize, (const GLvoid*)(sizeof(GLfloat) * 7));
419           this->OpenGLHelper.vglEnableVertexAttribArrayARB (bgColorAttrib);
420
421           this->OpenGLHelper.vglUniform1iARB (this->myLocations.UseTexture, 1);
422           this->OpenGLHelper.vglUniform1iARB (this->myLocations.BackgroundTexture, GL_TEXTURE0);
423
424           glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
425
426           this->OpenGLHelper.vglDisableVertexAttribArrayARB (0);
427           this->OpenGLHelper.vglBindBufferARB (GL_ARRAY_BUFFER_ARB, 0);
428           this->OpenGLHelper.vglDeleteBuffersARB (1, &vertexBuffer);
429           this->OpenGLHelper.vglBindVertexArrayARB (0);
430           this->OpenGLHelper.vglUseProgramObjectARB (0);
431         }
432 #else
433         GLfloat x_offset = 0.5f, y_offset = 0.5f;
434         GLfloat coeff = 0.5f;
435         if( aPosition == VTKViewer_Texture::Centered )
436         {
437           x_offset = ( (GLfloat)aWidth / (GLfloat)aViewWidth ) / 2.;
438           y_offset = ( (GLfloat)aHeight / (GLfloat)aViewHeight ) / 2.;
439         }
440
441         // OCCT issue 0023102: Change the algorithm of rendering the
442         // 3d viewer background using tiled texture
443         // Setting this coefficient to -1.F allows to tile textures relatively
444         // to the top-left corner of the view (value 1.F corresponds to the
445         // initial behaviour - tiling from the bottom-left corner)
446         GLfloat aCoef = -1.F;
447
448         // Note that texture is mapped using GL_REPEAT wrapping mode so integer part
449         // is simply ignored, and negative multiplier is here for convenience only
450         // and does not result e.g. in texture mirroring
451         glBegin( GL_QUADS );
452         glTexCoord2f(  0.F,          0.F ); glVertex2f( -x_offset + coeff, -aCoef * y_offset + coeff );
453         glTexCoord2f( texX,          0.F ); glVertex2f(  x_offset + coeff, -aCoef * y_offset + coeff );
454         glTexCoord2f( texX, aCoef * texY ); glVertex2f(  x_offset + coeff,  aCoef * y_offset + coeff );
455         glTexCoord2f(  0.F, aCoef * texY ); glVertex2f( -x_offset + coeff,  aCoef * y_offset + coeff );
456         glEnd();
457 #endif
458       }
459     }
460
461     // Restore settings.
462     {
463       glEnable( GL_ALPHA_TEST );
464       glEnable( GL_DEPTH_TEST );
465       glEnable( GL_LIGHTING );
466       glEnable( GL_TEXTURE_1D );
467       glEnable( GL_TEXTURE_2D );
468       glEnable( GL_BLEND );
469
470       glShadeModel( oldShadeModel ); // color interpolation
471     }
472
473     glPopMatrix();
474     glMatrixMode( GL_PROJECTION );
475     glPopMatrix();
476     glMatrixMode( GL_MODELVIEW );
477
478     glPopAttrib();
479   }
480 }