0032591: Visualization, V3d_View - improve corner gradient
[occt.git] / src / OpenGl / OpenGl_BackgroundArray.cxx
1 // Created on: 2015-01-16
2 // Created by: Anastasia BORISOVA
3 // Copyright (c) 2015 OPEN CASCADE SAS
4 //
5 // This file is part of Open CASCADE Technology software library.
6 //
7 // This library is free software; you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License version 2.1 as published
9 // by the Free Software Foundation, with special exception defined in the file
10 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11 // distribution for complete text of the license and disclaimer of any warranty.
12 //
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
15
16 #include <OpenGl_BackgroundArray.hxx>
17
18 #include <Aspect_FillMethod.hxx>
19 #include <OpenGl_Texture.hxx>
20 #include <OpenGl_View.hxx>
21 #include <Graphic3d_TextureParams.hxx>
22
23 // =======================================================================
24 // method  : Constructor
25 // purpose :
26 // =======================================================================
27 OpenGl_BackgroundArray::OpenGl_BackgroundArray (const Graphic3d_TypeOfBackground theType)
28 : OpenGl_PrimitiveArray (NULL, Graphic3d_TOPA_TRIANGLES, NULL, NULL, NULL),
29   myType (theType),
30   myFillMethod (Aspect_FM_NONE),
31   myViewWidth (0),
32   myViewHeight (0),
33   myToUpdate (Standard_False)
34 {
35   myDrawMode = GL_TRIANGLES;
36   myIsFillType = true;
37
38   myGradientParams.color1 = OpenGl_Vec4 (0.0f, 0.0f, 0.0f, 1.0f);
39   myGradientParams.color2 = OpenGl_Vec4 (0.0f, 0.0f, 0.0f, 1.0f);
40   myGradientParams.type   = Aspect_GradientFillMethod_None;
41 }
42
43 // =======================================================================
44 // method  : SetTextureParameters
45 // purpose :
46 // =======================================================================
47 void OpenGl_BackgroundArray::SetTextureParameters (const Aspect_FillMethod theFillMethod)
48 {
49   if (myType != Graphic3d_TOB_TEXTURE)
50   {
51     return;
52   }
53
54   myFillMethod = theFillMethod;
55   invalidateData();
56 }
57
58 // =======================================================================
59 // method  : SetTextureFillMethod
60 // purpose :
61 // =======================================================================
62 void OpenGl_BackgroundArray::SetTextureFillMethod (const Aspect_FillMethod theFillMethod)
63 {
64   myFillMethod = theFillMethod;
65   invalidateData();
66 }
67
68 // =======================================================================
69 // method  : SetGradientParameters
70 // purpose :
71 // =======================================================================
72 void OpenGl_BackgroundArray::SetGradientParameters (const Quantity_Color&           theColor1,
73                                                     const Quantity_Color&           theColor2,
74                                                     const Aspect_GradientFillMethod theType)
75 {
76   if (myType != Graphic3d_TOB_GRADIENT)
77   {
78     return;
79   }
80
81   Standard_Real anR, aG, aB;
82   theColor1.Values (anR, aG, aB, Quantity_TOC_RGB);
83   myGradientParams.color1 = OpenGl_Vec4 ((float)anR, (float)aG, (float)aB, 0.0f);
84
85   theColor2.Values (anR, aG, aB, Quantity_TOC_RGB);
86   myGradientParams.color2 = OpenGl_Vec4 ((float)anR, (float)aG, (float)aB, 0.0f);
87
88   myGradientParams.type = theType;
89   invalidateData();
90 }
91
92 // =======================================================================
93 // method  : SetGradientFillMethod
94 // purpose :
95 // =======================================================================
96 void OpenGl_BackgroundArray::SetGradientFillMethod (const Aspect_GradientFillMethod theType)
97 {
98   if (myType != Graphic3d_TOB_GRADIENT)
99   {
100     return;
101   }
102
103   myGradientParams.type = theType;
104   invalidateData();
105 }
106
107 // =======================================================================
108 // method  : IsDefined
109 // purpose :
110 // =======================================================================
111 bool OpenGl_BackgroundArray::IsDefined() const
112 {
113   switch (myType)
114   {
115     case Graphic3d_TOB_GRADIENT: return myGradientParams.type != Aspect_GradientFillMethod_None;
116     case Graphic3d_TOB_TEXTURE:  return myFillMethod          != Aspect_FM_NONE;
117     case Graphic3d_TOB_CUBEMAP:  return Standard_True;
118     case Graphic3d_TOB_NONE:     return Standard_False;
119   }
120   return Standard_False;
121 }
122
123 // =======================================================================
124 // method  : invalidateData
125 // purpose :
126 // =======================================================================
127 void OpenGl_BackgroundArray::invalidateData()
128 {
129   myToUpdate = Standard_True;
130 }
131
132 // =======================================================================
133 // method  : init
134 // purpose :
135 // =======================================================================
136 Standard_Boolean OpenGl_BackgroundArray::init (const Handle(OpenGl_Workspace)& theWorkspace) const
137 {
138   const Handle(OpenGl_Context)& aCtx = theWorkspace->GetGlContext();
139
140   if (myIndices.IsNull())
141   {
142     myIndices = new Graphic3d_IndexBuffer (Graphic3d_Buffer::DefaultAllocator());
143   }
144   if (myAttribs.IsNull())
145   {
146     myAttribs = new Graphic3d_Buffer (Graphic3d_Buffer::DefaultAllocator());
147   }
148
149   switch (myType)
150   {
151     case Graphic3d_TOB_GRADIENT:
152     {
153       if (!createGradientArray (aCtx))
154       {
155         return Standard_False;
156       }
157       break;
158     }
159     case Graphic3d_TOB_TEXTURE:
160     {
161       if (!createTextureArray (theWorkspace))
162       {
163         return Standard_False;
164       }
165       break;
166     }
167     case Graphic3d_TOB_CUBEMAP:
168     {
169       if (!createCubeMapArray())
170       {
171         return Standard_False;
172       }
173       break;
174     }
175     case Graphic3d_TOB_NONE:
176     default:
177     {
178       return Standard_False;
179     }
180   }
181
182   // Init VBO
183   if (myIsVboInit)
184   {
185     clearMemoryGL (aCtx);
186   }
187   buildVBO (aCtx, Standard_True);
188   myIsVboInit = Standard_True;
189
190   // Data is up-to-date
191   myToUpdate = Standard_False;
192   return Standard_True;
193 }
194
195 // =======================================================================
196 // method  : createGradientArray
197 // purpose :
198 // =======================================================================
199 Standard_Boolean OpenGl_BackgroundArray::createGradientArray (const Handle(OpenGl_Context)& theCtx) const
200 {
201   // Initialize data for primitive array
202   Graphic3d_Attribute aGragientAttribInfo[] =
203   {
204     { Graphic3d_TOA_POS,   Graphic3d_TOD_VEC2 },
205     { Graphic3d_TOA_COLOR, Graphic3d_TOD_VEC3 }
206   };
207
208   if (!myAttribs->Init (4, aGragientAttribInfo, 2))
209   {
210     return Standard_False;
211   }
212   if (!myIndices->Init<unsigned short>(6))
213   {
214     return Standard_False;
215   }
216   const unsigned short THE_FS_QUAD_TRIS[6] = {0, 1, 3, 1, 2, 3};
217   for (unsigned int aVertIter = 0; aVertIter < 6; ++aVertIter)
218   {
219     myIndices->SetIndex (aVertIter, THE_FS_QUAD_TRIS[aVertIter]);
220   }
221
222   OpenGl_Vec2 aVertices[4] =
223   {
224     OpenGl_Vec2(float(myViewWidth), 0.0f),
225     OpenGl_Vec2(float(myViewWidth), float(myViewHeight)),
226     OpenGl_Vec2(0.0f,               float(myViewHeight)),
227     OpenGl_Vec2(0.0f,               0.0f)
228   };
229
230   float* aCorners[4]     = {};
231   float  aDiagCorner1[3] = {};
232   float  aDiagCorner2[3] = {};
233
234   switch (myGradientParams.type)
235   {
236     case Aspect_GradientFillMethod_Horizontal:
237     {
238       aCorners[0] = myGradientParams.color2.ChangeData();
239       aCorners[1] = myGradientParams.color2.ChangeData();
240       aCorners[2] = myGradientParams.color1.ChangeData();
241       aCorners[3] = myGradientParams.color1.ChangeData();
242       break;
243     }
244     case Aspect_GradientFillMethod_Vertical:
245     {
246       aCorners[0] = myGradientParams.color2.ChangeData();
247       aCorners[1] = myGradientParams.color1.ChangeData();
248       aCorners[2] = myGradientParams.color1.ChangeData();
249       aCorners[3] = myGradientParams.color2.ChangeData();
250       break;
251     }
252     case Aspect_GradientFillMethod_Diagonal1:
253     {
254       aCorners[0] = myGradientParams.color2.ChangeData();
255       aCorners[2] = myGradientParams.color1.ChangeData();
256       aDiagCorner1[0] = aDiagCorner2[0] = 0.5f * (aCorners[0][0] + aCorners[2][0]);
257       aDiagCorner1[1] = aDiagCorner2[1] = 0.5f * (aCorners[0][1] + aCorners[2][1]);
258       aDiagCorner1[2] = aDiagCorner2[2] = 0.5f * (aCorners[0][2] + aCorners[2][2]);
259       aCorners[1] = aDiagCorner1;
260       aCorners[3] = aDiagCorner2;
261       break;
262     }
263     case Aspect_GradientFillMethod_Diagonal2:
264     {
265       aCorners[1] = myGradientParams.color1.ChangeData();
266       aCorners[3] = myGradientParams.color2.ChangeData();
267       aDiagCorner1[0] = aDiagCorner2[0] = 0.5f * (aCorners[1][0] + aCorners[3][0]);
268       aDiagCorner1[1] = aDiagCorner2[1] = 0.5f * (aCorners[1][1] + aCorners[3][1]);
269       aDiagCorner1[2] = aDiagCorner2[2] = 0.5f * (aCorners[1][2] + aCorners[3][2]);
270       aCorners[0] = aDiagCorner1;
271       aCorners[2] = aDiagCorner2;
272       break;
273     }
274     case Aspect_GradientFillMethod_Corner1:
275     case Aspect_GradientFillMethod_Corner2:
276     case Aspect_GradientFillMethod_Corner3:
277     case Aspect_GradientFillMethod_Corner4:
278     {
279       Graphic3d_Attribute aCornerAttribInfo[] =
280       {
281         { Graphic3d_TOA_POS,   Graphic3d_TOD_VEC2 },
282         { Graphic3d_TOA_UV,    Graphic3d_TOD_VEC2 }
283       };
284
285       OpenGl_Vec2 anUVs[4] =
286       {
287         OpenGl_Vec2 (1.0f, 0.0f),
288         OpenGl_Vec2 (1.0f, 1.0f),
289         OpenGl_Vec2 (0.0f, 1.0f),
290         OpenGl_Vec2 (0.0f, 0.0f)
291       };
292
293       if (!myAttribs->Init (4, aCornerAttribInfo, 2))
294       {
295         return Standard_False;
296       }
297       for (Standard_Integer anIt = 0; anIt < 4; ++anIt)
298       {
299         OpenGl_Vec2* aVertData = reinterpret_cast<OpenGl_Vec2*>(myAttribs->changeValue (anIt));
300         *aVertData = aVertices[anIt];
301
302         OpenGl_Vec2* anUvData = reinterpret_cast<OpenGl_Vec2*>(myAttribs->changeValue (anIt) + myAttribs->AttributeOffset (1));
303         // cyclically move highlighted corner depending on myGradientParams.type
304         *anUvData = anUVs[(anIt + myGradientParams.type - Aspect_GradientFillMethod_Corner1) % 4];
305       }
306       return Standard_True;
307     }
308     case Aspect_GradientFillMethod_Elliptical:
309     {
310       // construction of a circle circumscribed about a view rectangle
311       // using parametric equation (scaled by aspect ratio and centered)
312       const Standard_Integer aSubdiv = 64;
313       if (!myAttribs->Init (aSubdiv + 2, aGragientAttribInfo, 2))
314       {
315         return Standard_False;
316       }
317
318       OpenGl_Vec2 anEllipVerts[aSubdiv + 2];
319       anEllipVerts[0] = OpenGl_Vec2 (float (myViewWidth) / 2.0f, float (myViewHeight) / 2.0f);
320       Standard_Real aTetta = (M_PI * 2.0) / aSubdiv;
321       Standard_Real aParam = 0.0;
322       for (Standard_Integer anIt = 1; anIt < aSubdiv + 2; ++anIt)
323       {
324         anEllipVerts[anIt] = OpenGl_Vec2 (float (Cos (aParam) * Sqrt (2.0) * myViewWidth  / 2.0 + myViewWidth  / 2.0f),
325                                           float (Sin (aParam) * Sqrt (2.0) * myViewHeight / 2.0 + myViewHeight / 2.0f));
326
327         aParam += aTetta;
328       }
329       for (Standard_Integer anIt = 0; anIt < aSubdiv + 2; ++anIt)
330       {
331         OpenGl_Vec2* aVertData = reinterpret_cast<OpenGl_Vec2*>(myAttribs->changeValue (anIt));
332         *aVertData = anEllipVerts[anIt];
333
334         OpenGl_Vec3* aColorData = reinterpret_cast<OpenGl_Vec3*>(myAttribs->changeValue (anIt) + myAttribs->AttributeOffset (1));
335         *aColorData = myGradientParams.color2.rgb();
336       }
337       // the central vertex is colored in different way
338       OpenGl_Vec3* aColorData = reinterpret_cast<OpenGl_Vec3*>(myAttribs->changeValue (0) + myAttribs->AttributeOffset (1));
339       *aColorData = myGradientParams.color1.rgb();
340
341       if (!myIndices->Init<unsigned short> (3 * aSubdiv))
342       {
343         return Standard_False;
344       }
345       for (Standard_Integer aCurTri = 0; aCurTri < aSubdiv; aCurTri++)
346       {
347         myIndices->SetIndex (aCurTri * 3 + 0, 0);
348         myIndices->SetIndex (aCurTri * 3 + 1, aCurTri + 1);
349         myIndices->SetIndex (aCurTri * 3 + 2, aCurTri + 2);
350       }
351
352       return Standard_True;
353     }
354     case Aspect_GradientFillMethod_None:
355     {
356       break;
357     }
358   }
359
360   for (Standard_Integer anIt = 0; anIt < 4; ++anIt)
361   {
362     OpenGl_Vec2* aVertData  = reinterpret_cast<OpenGl_Vec2* >(myAttribs->changeValue (anIt));
363     *aVertData = aVertices[anIt];
364
365     OpenGl_Vec3* aColorData = reinterpret_cast<OpenGl_Vec3* >(myAttribs->changeValue (anIt) + myAttribs->AttributeOffset (1));
366     *aColorData = theCtx->Vec4FromQuantityColor (OpenGl_Vec4(aCorners[anIt][0], aCorners[anIt][1], aCorners[anIt][2], 1.0f)).rgb();
367   }
368
369   return Standard_True;
370 }
371
372 // =======================================================================
373 // method  : createTextureArray
374 // purpose :
375 // =======================================================================
376 Standard_Boolean OpenGl_BackgroundArray::createTextureArray (const Handle(OpenGl_Workspace)& theWorkspace) const
377 {
378   Graphic3d_Attribute aTextureAttribInfo[] =
379   {
380     { Graphic3d_TOA_POS, Graphic3d_TOD_VEC2 },
381     { Graphic3d_TOA_UV,  Graphic3d_TOD_VEC2 }
382   };
383
384   if (!myAttribs->Init (4, aTextureAttribInfo, 2))
385   {
386     return Standard_False;
387   }
388
389   GLfloat aTexRangeX = 1.0f; // texture <s> coordinate
390   GLfloat aTexRangeY = 1.0f; // texture <t> coordinate
391
392   // Set up for stretching or tiling
393   GLfloat anOffsetX = 0.5f * (float )myViewWidth;
394   GLfloat anOffsetY = 0.5f * (float )myViewHeight;
395
396   // Setting this coefficient to -1.0f allows to tile textures relatively to the top-left corner of the view
397   // (value 1.0f corresponds to the initial behavior - tiling from the bottom-left corner)
398   GLfloat aCoef = -1.0f;
399
400   // Get texture parameters
401   const Handle(OpenGl_Context)& aCtx = theWorkspace->GetGlContext();
402   const OpenGl_Aspects* anAspectFace = theWorkspace->Aspects();
403   GLfloat aTextureWidth  = (GLfloat )anAspectFace->TextureSet (aCtx)->First()->SizeX();
404   GLfloat aTextureHeight = (GLfloat )anAspectFace->TextureSet (aCtx)->First()->SizeY();
405
406   if (myFillMethod == Aspect_FM_CENTERED)
407   {
408     anOffsetX = 0.5f * aTextureWidth;
409     anOffsetY = 0.5f * aTextureHeight;
410   }
411   else if (myFillMethod == Aspect_FM_TILED)
412   {
413     aTexRangeX = (GLfloat )myViewWidth  / aTextureWidth;
414     aTexRangeY = (GLfloat )myViewHeight / aTextureHeight;
415   }
416
417   // NOTE: texture is mapped using GL_REPEAT wrapping mode so integer part
418   // is simply ignored, and negative multiplier is here for convenience only
419   // and does not result e.g. in texture mirroring
420
421
422   OpenGl_Vec2* aData = reinterpret_cast<OpenGl_Vec2* >(myAttribs->changeValue (0));
423   aData[0] = OpenGl_Vec2 (anOffsetX, -aCoef * anOffsetY);
424   aData[1] = OpenGl_Vec2 (aTexRangeX, 0.0f);
425
426   aData = reinterpret_cast<OpenGl_Vec2* >(myAttribs->changeValue (1));
427   aData[0] = OpenGl_Vec2 (anOffsetX,  aCoef * anOffsetY);
428   aData[1] = OpenGl_Vec2 (aTexRangeX, aCoef * aTexRangeY);
429
430   aData = reinterpret_cast<OpenGl_Vec2* >(myAttribs->changeValue (2));
431   aData[0] = OpenGl_Vec2 (-anOffsetX, -aCoef * anOffsetY);
432   aData[1] = OpenGl_Vec2 (0.0f, 0.0f);
433
434   aData = reinterpret_cast<OpenGl_Vec2* >(myAttribs->changeValue (3));
435   aData[0] = OpenGl_Vec2 (-anOffsetX, aCoef * anOffsetY);
436   aData[1] = OpenGl_Vec2 (0.0f, aCoef * aTexRangeY);
437
438   if (!myIndices->Init<unsigned short>(6))
439   {
440     return Standard_False;
441   }
442   const unsigned short THE_FS_QUAD_TRIS[6] = {0, 1, 2, 1, 3, 2};
443   for (unsigned int aVertIter = 0; aVertIter < 6; ++aVertIter)
444   {
445     myIndices->SetIndex (aVertIter, THE_FS_QUAD_TRIS[aVertIter]);
446   }
447
448   return Standard_True;
449 }
450
451 // =======================================================================
452 // method  : createCubeMapArray
453 // purpose :
454 // =======================================================================
455 Standard_Boolean OpenGl_BackgroundArray::createCubeMapArray() const
456 {
457   const Graphic3d_Attribute aCubeMapAttribInfo[] =
458   {
459     { Graphic3d_TOA_POS, Graphic3d_TOD_VEC3 }
460   };
461
462   if (myAttribs.IsNull())
463   {
464     myAttribs = new Graphic3d_Buffer (Graphic3d_Buffer::DefaultAllocator());
465     myIndices = new Graphic3d_IndexBuffer (Graphic3d_Buffer::DefaultAllocator());
466   }
467   if (!myAttribs->Init (8, aCubeMapAttribInfo, 1)
468    || !myIndices->Init<unsigned short> (6 * 3 * 2))
469   {
470     return Standard_False;
471   }
472
473   {
474     OpenGl_Vec3* aData = reinterpret_cast<OpenGl_Vec3*>(myAttribs->changeValue(0));
475     aData[0].SetValues (-1.0, -1.0,  1.0);
476     aData[1].SetValues ( 1.0, -1.0,  1.0);
477     aData[2].SetValues (-1.0,  1.0,  1.0);
478     aData[3].SetValues ( 1.0,  1.0,  1.0);
479     aData[4].SetValues (-1.0, -1.0, -1.0);
480     aData[5].SetValues ( 1.0, -1.0, -1.0);
481     aData[6].SetValues (-1.0,  1.0, -1.0);
482     aData[7].SetValues ( 1.0,  1.0, -1.0);
483   }
484   {
485     const unsigned short THE_BOX_TRIS[] =
486     {
487       0, 1, 2, 2, 1, 3, // top face
488       1, 5, 7, 1, 7, 3, // right face
489       0, 6, 4, 0, 2, 6, // left face
490       4, 6, 5, 6, 7, 5, // bottom face
491       0, 5, 1, 0, 4, 5, // front face
492       2, 7, 6, 2, 3, 7  // back face
493     };
494     for (unsigned int aVertIter = 0; aVertIter < 6 * 3 * 2; ++aVertIter)
495     {
496       myIndices->SetIndex (aVertIter, THE_BOX_TRIS[aVertIter]);
497     }
498   }
499
500   return Standard_True;
501 }
502
503 // =======================================================================
504 // method  : Render
505 // purpose :
506 // =======================================================================
507 void OpenGl_BackgroundArray::Render (const Handle(OpenGl_Workspace)& theWorkspace,
508                                      Graphic3d_Camera::Projection theProjection) const
509 {
510   const Handle(OpenGl_Context)& aCtx = theWorkspace->GetGlContext();
511   Standard_Integer aViewSizeX = aCtx->Viewport()[2];
512   Standard_Integer aViewSizeY = aCtx->Viewport()[3];
513   Graphic3d_Vec2i aTileOffset, aTileSize;
514
515   if (aCtx->Camera()->Tile().IsValid())
516   {
517     aViewSizeX = aCtx->Camera()->Tile().TotalSize.x();
518     aViewSizeY = aCtx->Camera()->Tile().TotalSize.y();
519
520     aTileOffset = aCtx->Camera()->Tile().OffsetLowerLeft();
521     aTileSize   = aCtx->Camera()->Tile().TileSize;
522   }
523   if (myToUpdate
524    || myViewWidth  != aViewSizeX
525    || myViewHeight != aViewSizeY
526    || myAttribs.IsNull()
527    || myVboAttribs.IsNull())
528   {
529     myViewWidth  = aViewSizeX;
530     myViewHeight = aViewSizeY;
531     init (theWorkspace);
532   }
533
534   OpenGl_Mat4 aProjection = aCtx->ProjectionState.Current();
535   OpenGl_Mat4 aWorldView  = aCtx->WorldViewState.Current();
536
537   if (myType == Graphic3d_TOB_CUBEMAP)
538   {
539     Graphic3d_Camera aCamera (aCtx->Camera());
540     aCamera.SetZRange (0.01, 1.0); // is needed to avoid perspective camera exception
541
542     // cancel translation
543     aCamera.MoveEyeTo (gp_Pnt (0.0, 0.0, 0.0));
544
545     // Handle projection matrix:
546     // - Cancel any head-to-eye translation for HMD display;
547     // - Ignore stereoscopic projection in case of non-HMD 3D display
548     //   (ideally, we would need a stereoscopic cubemap image; adding a parallax makes no sense);
549     // - Force perspective projection when orthographic camera is active
550     //   (orthographic projection makes no sense for cubemap).
551     const bool isCustomProj = aCamera.IsCustomStereoFrustum()
552                            || aCamera.IsCustomStereoProjection();
553     aCamera.SetProjectionType (theProjection == Graphic3d_Camera::Projection_Orthographic || !isCustomProj
554                              ? Graphic3d_Camera::Projection_Perspective
555                              : theProjection);
556
557     aProjection = aCamera.ProjectionMatrixF();
558     aWorldView = aCamera.OrientationMatrixF();
559     if (isCustomProj)
560     {
561       // get projection matrix without pre-multiplied stereoscopic head-to-eye translation
562       if (theProjection == Graphic3d_Camera::Projection_MonoLeftEye)
563       {
564         Graphic3d_Mat4 aMatProjL, aMatHeadToEyeL, aMatProjR, aMatHeadToEyeR;
565         aCamera.StereoProjectionF (aMatProjL, aMatHeadToEyeL, aMatProjR, aMatHeadToEyeR);
566         aProjection = aMatProjL;
567       }
568       else if (theProjection == Graphic3d_Camera::Projection_MonoRightEye)
569       {
570         Graphic3d_Mat4 aMatProjL, aMatHeadToEyeL, aMatProjR, aMatHeadToEyeR;
571         aCamera.StereoProjectionF (aMatProjL, aMatHeadToEyeL, aMatProjR, aMatHeadToEyeR);
572         aProjection = aMatProjR;
573       }
574     }
575   }
576   else
577   {
578     aProjection.InitIdentity();
579     aWorldView.InitIdentity();
580     if (aCtx->Camera()->Tile().IsValid())
581     {
582       aWorldView.SetDiagonal (OpenGl_Vec4 (2.0f / aTileSize.x(), 2.0f / aTileSize.y(), 1.0f, 1.0f));
583       if (myType == Graphic3d_TOB_GRADIENT)
584       {
585         aWorldView.SetColumn (3, OpenGl_Vec4 (-1.0f - 2.0f * aTileOffset.x() / aTileSize.x(),
586                                               -1.0f - 2.0f * aTileOffset.y() / aTileSize.y(), 0.0f, 1.0f));
587       }
588       else
589       {
590         aWorldView.SetColumn (3, OpenGl_Vec4 (-1.0f + (float) aViewSizeX / aTileSize.x() - 2.0f * aTileOffset.x() / aTileSize.x(),
591                                               -1.0f + (float) aViewSizeY / aTileSize.y() - 2.0f * aTileOffset.y() / aTileSize.y(), 0.0f, 1.0f));
592       }
593     }
594     else
595     {
596       aWorldView.SetDiagonal (OpenGl_Vec4 (2.0f / myViewWidth, 2.0f / myViewHeight, 1.0f, 1.0f));
597       if (myType == Graphic3d_TOB_GRADIENT)
598       {
599         aWorldView.SetColumn (3, OpenGl_Vec4 (-1.0f, -1.0f, 0.0f, 1.0f));
600       }
601     }
602   }
603
604   aCtx->ProjectionState.Push();
605   aCtx->WorldViewState.Push();
606   aCtx->ProjectionState.SetCurrent (aProjection);
607   aCtx->WorldViewState.SetCurrent (aWorldView);
608   aCtx->ApplyProjectionMatrix();
609   aCtx->ApplyModelViewMatrix();
610
611   OpenGl_PrimitiveArray::Render (theWorkspace);
612
613   aCtx->ProjectionState.Pop();
614   aCtx->WorldViewState.Pop();
615   aCtx->ApplyProjectionMatrix();
616 }