0028572: Modeling Algorithms - Wrong result of the mkface command
[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 <NCollection_AlignedAllocator.hxx>
20 #include <OpenGl_Texture.hxx>
21 #include <OpenGl_View.hxx>
22 #include <Graphic3d_TextureParams.hxx>
23
24 // =======================================================================
25 // method  : Constructor
26 // purpose :
27 // =======================================================================
28 OpenGl_BackgroundArray::OpenGl_BackgroundArray (const Graphic3d_TypeOfBackground theType)
29 : OpenGl_PrimitiveArray (NULL, Graphic3d_TOPA_TRIANGLESTRIPS, NULL, NULL, NULL),
30   myType (theType),
31   myFillMethod (Aspect_FM_NONE),
32   myViewWidth (0),
33   myViewHeight (0),
34   myToUpdate (Standard_False)
35 {
36   myDrawMode = GL_TRIANGLE_STRIP;
37   myIsFillType = true;
38
39   myGradientParams.color1 = OpenGl_Vec4 (0.0f, 0.0f, 0.0f, 1.0f);
40   myGradientParams.color2 = OpenGl_Vec4 (0.0f, 0.0f, 0.0f, 1.0f);
41   myGradientParams.type   = Aspect_GFM_NONE;
42 }
43
44 // =======================================================================
45 // method  : SetTextureParameters
46 // purpose :
47 // =======================================================================
48 void OpenGl_BackgroundArray::SetTextureParameters (const Aspect_FillMethod theFillMethod)
49 {
50   if (myType != Graphic3d_TOB_TEXTURE)
51   {
52     return;
53   }
54
55   myFillMethod = theFillMethod;
56   invalidateData();
57 }
58
59 // =======================================================================
60 // method  : SetTextureFillMethod
61 // purpose :
62 // =======================================================================
63 void OpenGl_BackgroundArray::SetTextureFillMethod (const Aspect_FillMethod theFillMethod)
64 {
65   myFillMethod = theFillMethod;
66   invalidateData();
67 }
68
69 // =======================================================================
70 // method  : SetGradientParameters
71 // purpose :
72 // =======================================================================
73 void OpenGl_BackgroundArray::SetGradientParameters (const Quantity_Color&           theColor1,
74                                                     const Quantity_Color&           theColor2,
75                                                     const Aspect_GradientFillMethod theType)
76 {
77   if (myType != Graphic3d_TOB_GRADIENT)
78   {
79     return;
80   }
81
82   Standard_Real anR, aG, aB;
83   theColor1.Values (anR, aG, aB, Quantity_TOC_RGB);
84   myGradientParams.color1 = OpenGl_Vec4 ((float)anR, (float)aG, (float)aB, 0.0f);
85
86   theColor2.Values (anR, aG, aB, Quantity_TOC_RGB);
87   myGradientParams.color2 = OpenGl_Vec4 ((float)anR, (float)aG, (float)aB, 0.0f);
88
89   myGradientParams.type = theType;
90   invalidateData();
91 }
92
93 // =======================================================================
94 // method  : SetGradientFillMethod
95 // purpose :
96 // =======================================================================
97 void OpenGl_BackgroundArray::SetGradientFillMethod (const Aspect_GradientFillMethod theType)
98 {
99   if (myType != Graphic3d_TOB_GRADIENT)
100   {
101     return;
102   }
103
104   myGradientParams.type = theType;
105   invalidateData();
106 }
107
108 // =======================================================================
109 // method  : IsDefined
110 // purpose :
111 // =======================================================================
112 bool OpenGl_BackgroundArray::IsDefined() const
113 {
114   switch (myType)
115   {
116     case Graphic3d_TOB_GRADIENT: return myGradientParams.type != Aspect_GFM_NONE;
117     case Graphic3d_TOB_TEXTURE:  return myFillMethod          != Aspect_FM_NONE;
118     case Graphic3d_TOB_CUBEMAP:  return Standard_True;
119     case Graphic3d_TOB_NONE:     return Standard_False;
120   }
121   return Standard_False;
122 }
123
124 // =======================================================================
125 // method  : invalidateData
126 // purpose :
127 // =======================================================================
128 void OpenGl_BackgroundArray::invalidateData()
129 {
130   myToUpdate = Standard_True;
131 }
132
133 // =======================================================================
134 // method  : init
135 // purpose :
136 // =======================================================================
137 Standard_Boolean OpenGl_BackgroundArray::init (const Handle(OpenGl_Workspace)& theWorkspace) const
138 {
139   const Handle(OpenGl_Context)& aCtx = theWorkspace->GetGlContext();
140   switch (myType)
141   {
142     case Graphic3d_TOB_GRADIENT:
143     {
144       if (!createGradientArray (aCtx))
145       {
146         return Standard_False;
147       }
148       break;
149     }
150     case Graphic3d_TOB_TEXTURE:
151     {
152       if (!createTextureArray (theWorkspace))
153       {
154         return Standard_False;
155       }
156       break;
157     }
158     case Graphic3d_TOB_CUBEMAP:
159     {
160       if (!createCubeMapArray())
161       {
162         return Standard_False;
163       }
164       break;
165     }
166     case Graphic3d_TOB_NONE:
167     default:
168     {
169       return Standard_False;
170     }
171   }
172
173   // Init VBO
174   if (myIsVboInit)
175   {
176     clearMemoryGL (aCtx);
177   }
178   buildVBO (aCtx, Standard_True);
179   myIsVboInit = Standard_True;
180
181   // Data is up-to-date
182   myToUpdate = Standard_False;
183   return Standard_True;
184 }
185
186 // =======================================================================
187 // method  : createGradientArray
188 // purpose :
189 // =======================================================================
190 Standard_Boolean OpenGl_BackgroundArray::createGradientArray (const Handle(OpenGl_Context)& theCtx) const
191 {
192   // Initialize data for primitive array
193   Graphic3d_Attribute aGragientAttribInfo[] =
194   {
195     { Graphic3d_TOA_POS,   Graphic3d_TOD_VEC2 },
196     { Graphic3d_TOA_COLOR, Graphic3d_TOD_VEC3 }
197   };
198
199   if (myAttribs.IsNull())
200   {
201     Handle(NCollection_AlignedAllocator) anAlloc = new NCollection_AlignedAllocator (16);
202     myAttribs = new Graphic3d_Buffer (anAlloc);
203   }
204   if (!myAttribs->Init (4, aGragientAttribInfo, 2))
205   {
206     return Standard_False;
207   }
208
209   OpenGl_Vec2 aVertices[4] =
210   {
211     OpenGl_Vec2(float(myViewWidth), 0.0f),
212     OpenGl_Vec2(float(myViewWidth), float(myViewHeight)),
213     OpenGl_Vec2(0.0f,               0.0f),
214     OpenGl_Vec2(0.0f,               float(myViewHeight))
215   };
216
217   float* aCorners[4]     = {};
218   float  aDiagCorner1[3] = {};
219   float  aDiagCorner2[3] = {};
220
221   switch (myGradientParams.type)
222   {
223     case Aspect_GFM_HOR:
224     {
225       aCorners[0] = myGradientParams.color2.ChangeData();
226       aCorners[1] = myGradientParams.color2.ChangeData();
227       aCorners[2] = myGradientParams.color1.ChangeData();
228       aCorners[3] = myGradientParams.color1.ChangeData();
229       break;
230     }
231     case Aspect_GFM_VER:
232     {
233       aCorners[0] = myGradientParams.color2.ChangeData();
234       aCorners[1] = myGradientParams.color1.ChangeData();
235       aCorners[2] = myGradientParams.color2.ChangeData();
236       aCorners[3] = myGradientParams.color1.ChangeData();
237       break;
238     }
239     case Aspect_GFM_DIAG1:
240     {
241       aCorners[0] = myGradientParams.color2.ChangeData();
242       aCorners[3] = myGradientParams.color1.ChangeData();
243       aDiagCorner1[0] = aDiagCorner2[0] = 0.5f * (aCorners[0][0] + aCorners[3][0]);
244       aDiagCorner1[1] = aDiagCorner2[1] = 0.5f * (aCorners[0][1] + aCorners[3][1]);
245       aDiagCorner1[2] = aDiagCorner2[2] = 0.5f * (aCorners[0][2] + aCorners[3][2]);
246       aCorners[1] = aDiagCorner1;
247       aCorners[2] = aDiagCorner2;
248       break;
249     }
250     case Aspect_GFM_DIAG2:
251     {
252       aCorners[1] = myGradientParams.color1.ChangeData();
253       aCorners[2] = myGradientParams.color2.ChangeData();
254       aDiagCorner1[0] = aDiagCorner2[0] = 0.5f * (aCorners[1][0] + aCorners[2][0]);
255       aDiagCorner1[1] = aDiagCorner2[1] = 0.5f * (aCorners[1][1] + aCorners[2][1]);
256       aDiagCorner1[2] = aDiagCorner2[2] = 0.5f * (aCorners[1][2] + aCorners[2][2]);
257       aCorners[0] = aDiagCorner1;
258       aCorners[3] = aDiagCorner2;
259       break;
260     }
261     case Aspect_GFM_CORNER1:
262     {
263       aVertices[0] = OpenGl_Vec2(float(myViewWidth), float(myViewHeight));
264       aVertices[1] = OpenGl_Vec2(0.0f,               float(myViewHeight));
265       aVertices[2] = OpenGl_Vec2(float(myViewWidth), 0.0f);
266       aVertices[3] = OpenGl_Vec2(0.0f,               0.0f);
267
268       aCorners[0] = myGradientParams.color2.ChangeData();
269       aCorners[1] = myGradientParams.color1.ChangeData();
270       aCorners[2] = myGradientParams.color2.ChangeData();
271       aCorners[3] = myGradientParams.color2.ChangeData();
272       break;
273     }
274     case Aspect_GFM_CORNER2:
275     {
276       aCorners[0] = myGradientParams.color2.ChangeData();
277       aCorners[1] = myGradientParams.color1.ChangeData();
278       aCorners[2] = myGradientParams.color2.ChangeData();
279       aCorners[3] = myGradientParams.color2.ChangeData();
280       break;
281     }
282     case Aspect_GFM_CORNER3:
283     {
284       aVertices[0] = OpenGl_Vec2(float(myViewWidth), float(myViewHeight));
285       aVertices[1] = OpenGl_Vec2(0.0f,               float(myViewHeight));
286       aVertices[2] = OpenGl_Vec2(float(myViewWidth), 0.0f);
287       aVertices[3] = OpenGl_Vec2(0.0f,               0.0f);
288
289       aCorners[0] = myGradientParams.color2.ChangeData();
290       aCorners[1] = myGradientParams.color2.ChangeData();
291       aCorners[2] = myGradientParams.color1.ChangeData();
292       aCorners[3] = myGradientParams.color2.ChangeData();
293       break;
294     }
295     case Aspect_GFM_CORNER4:
296     {
297       aCorners[0] = myGradientParams.color2.ChangeData();
298       aCorners[1] = myGradientParams.color2.ChangeData();
299       aCorners[2] = myGradientParams.color1.ChangeData();
300       aCorners[3] = myGradientParams.color2.ChangeData();
301       break;
302     }
303     case Aspect_GFM_NONE:
304     {
305       break;
306     }
307   }
308
309   for (Standard_Integer anIt = 0; anIt < 4; ++anIt)
310   {
311     OpenGl_Vec2* aVertData  = reinterpret_cast<OpenGl_Vec2* >(myAttribs->changeValue (anIt));
312     *aVertData = aVertices[anIt];
313
314     OpenGl_Vec3* aColorData = reinterpret_cast<OpenGl_Vec3* >(myAttribs->changeValue (anIt) + myAttribs->AttributeOffset (1));
315     *aColorData = theCtx->Vec4FromQuantityColor (OpenGl_Vec4(aCorners[anIt][0], aCorners[anIt][1], aCorners[anIt][2], 1.0f)).rgb();
316   }
317
318   return Standard_True;
319 }
320
321 // =======================================================================
322 // method  : createTextureArray
323 // purpose :
324 // =======================================================================
325 Standard_Boolean OpenGl_BackgroundArray::createTextureArray (const Handle(OpenGl_Workspace)& theWorkspace) const
326 {
327   Graphic3d_Attribute aTextureAttribInfo[] =
328   {
329     { Graphic3d_TOA_POS, Graphic3d_TOD_VEC2 },
330     { Graphic3d_TOA_UV,  Graphic3d_TOD_VEC2 }
331   };
332
333   if (myAttribs.IsNull())
334   {
335     Handle(NCollection_AlignedAllocator) anAlloc = new NCollection_AlignedAllocator (16);
336     myAttribs = new Graphic3d_Buffer (anAlloc);
337   }
338   if (!myAttribs->Init (4, aTextureAttribInfo, 2))
339   {
340     return Standard_False;
341   }
342
343   GLfloat aTexRangeX = 1.0f; // texture <s> coordinate
344   GLfloat aTexRangeY = 1.0f; // texture <t> coordinate
345
346   // Set up for stretching or tiling
347   GLfloat anOffsetX = 0.5f * (float )myViewWidth;
348   GLfloat anOffsetY = 0.5f * (float )myViewHeight;
349
350   // Setting this coefficient to -1.0f allows to tile textures relatively to the top-left corner of the view
351   // (value 1.0f corresponds to the initial behavior - tiling from the bottom-left corner)
352   GLfloat aCoef = -1.0f;
353
354   // Get texture parameters
355   const Handle(OpenGl_Context)& aCtx = theWorkspace->GetGlContext();
356   const OpenGl_Aspects* anAspectFace = theWorkspace->Aspects();
357   GLfloat aTextureWidth  = (GLfloat )anAspectFace->TextureSet (aCtx)->First()->SizeX();
358   GLfloat aTextureHeight = (GLfloat )anAspectFace->TextureSet (aCtx)->First()->SizeY();
359
360   if (myFillMethod == Aspect_FM_CENTERED)
361   {
362     anOffsetX = 0.5f * aTextureWidth;
363     anOffsetY = 0.5f * aTextureHeight;
364   }
365   else if (myFillMethod == Aspect_FM_TILED)
366   {
367     aTexRangeX = (GLfloat )myViewWidth  / aTextureWidth;
368     aTexRangeY = (GLfloat )myViewHeight / aTextureHeight;
369   }
370
371   // NOTE: texture is mapped using GL_REPEAT wrapping mode so integer part
372   // is simply ignored, and negative multiplier is here for convenience only
373   // and does not result e.g. in texture mirroring
374
375
376   OpenGl_Vec2* aData = reinterpret_cast<OpenGl_Vec2* >(myAttribs->changeValue (0));
377   aData[0] = OpenGl_Vec2 (anOffsetX, -aCoef * anOffsetY);
378   aData[1] = OpenGl_Vec2 (aTexRangeX, 0.0f);
379
380   aData = reinterpret_cast<OpenGl_Vec2* >(myAttribs->changeValue (1));
381   aData[0] = OpenGl_Vec2 (anOffsetX,  aCoef * anOffsetY);
382   aData[1] = OpenGl_Vec2 (aTexRangeX, aCoef * aTexRangeY);
383
384   aData = reinterpret_cast<OpenGl_Vec2* >(myAttribs->changeValue (2));
385   aData[0] = OpenGl_Vec2 (-anOffsetX, -aCoef * anOffsetY);
386   aData[1] = OpenGl_Vec2 (0.0f, 0.0f);
387
388   aData = reinterpret_cast<OpenGl_Vec2* >(myAttribs->changeValue (3));
389   aData[0] = OpenGl_Vec2 (-anOffsetX, aCoef * anOffsetY);
390   aData[1] = OpenGl_Vec2 (0.0f, aCoef * aTexRangeY);
391
392   return Standard_True;
393 }
394
395 // =======================================================================
396 // method  : createCubeMapArray
397 // purpose :
398 // =======================================================================
399 Standard_Boolean OpenGl_BackgroundArray::createCubeMapArray() const
400 {
401   const Graphic3d_Attribute aCubeMapAttribInfo[] =
402   {
403     { Graphic3d_TOA_POS, Graphic3d_TOD_VEC3 }
404   };
405
406   if (myAttribs.IsNull())
407   {
408     Handle(NCollection_AlignedAllocator) anAlloc = new NCollection_AlignedAllocator (16);
409     myAttribs = new Graphic3d_Buffer (anAlloc);
410     myIndices = new Graphic3d_IndexBuffer (anAlloc);
411   }
412   if (!myAttribs->Init (8, aCubeMapAttribInfo, 1)
413    || !myIndices->Init<unsigned short> (14))
414   {
415     return Standard_False;
416   }
417
418   {
419     OpenGl_Vec3* aData = reinterpret_cast<OpenGl_Vec3*>(myAttribs->changeValue(0));
420     aData[0].SetValues (-1.0, -1.0,  1.0);
421     aData[1].SetValues ( 1.0, -1.0,  1.0);
422     aData[2].SetValues (-1.0,  1.0,  1.0);
423     aData[3].SetValues ( 1.0,  1.0,  1.0);
424     aData[4].SetValues (-1.0, -1.0, -1.0);
425     aData[5].SetValues ( 1.0, -1.0, -1.0);
426     aData[6].SetValues (-1.0,  1.0, -1.0);
427     aData[7].SetValues ( 1.0,  1.0, -1.0);
428   }
429   {
430     const unsigned short THE_BOX_TRISTRIP[14] = { 0, 1, 2, 3, 7, 1, 5, 4, 7, 6, 2, 4, 0, 1 };
431     for (unsigned int aVertIter = 0; aVertIter < 14; ++aVertIter)
432     {
433       myIndices->SetIndex (aVertIter, THE_BOX_TRISTRIP[aVertIter]);
434     }
435   }
436
437   return Standard_True;
438 }
439
440 // =======================================================================
441 // method  : Render
442 // purpose :
443 // =======================================================================
444 void OpenGl_BackgroundArray::Render (const Handle(OpenGl_Workspace)& theWorkspace,
445                                      Graphic3d_Camera::Projection theProjection) const
446 {
447   const Handle(OpenGl_Context)& aCtx = theWorkspace->GetGlContext();
448   Standard_Integer aViewSizeX = aCtx->Viewport()[2];
449   Standard_Integer aViewSizeY = aCtx->Viewport()[3];
450   Graphic3d_Vec2i aTileOffset, aTileSize;
451
452   if (theWorkspace->View()->Camera()->Tile().IsValid())
453   {
454     aViewSizeX = theWorkspace->View()->Camera()->Tile().TotalSize.x();
455     aViewSizeY = theWorkspace->View()->Camera()->Tile().TotalSize.y();
456
457     aTileOffset = theWorkspace->View()->Camera()->Tile().OffsetLowerLeft();
458     aTileSize   = theWorkspace->View()->Camera()->Tile().TileSize;
459   }
460   if (myToUpdate
461    || myViewWidth  != aViewSizeX
462    || myViewHeight != aViewSizeY
463    || myAttribs.IsNull()
464    || myVboAttribs.IsNull())
465   {
466     myViewWidth  = aViewSizeX;
467     myViewHeight = aViewSizeY;
468     init (theWorkspace);
469   }
470
471   OpenGl_Mat4 aProjection = aCtx->ProjectionState.Current();
472   OpenGl_Mat4 aWorldView  = aCtx->WorldViewState.Current();
473
474   if (myType == Graphic3d_TOB_CUBEMAP)
475   {
476     Graphic3d_Camera aCamera (theWorkspace->View()->Camera());
477     aCamera.SetZRange (0.01, 1.0); // is needed to avoid perspective camera exception
478
479     // cancel translation
480     aCamera.MoveEyeTo (gp_Pnt (0.0, 0.0, 0.0));
481
482     // Handle projection matrix:
483     // - Cancel any head-to-eye translation for HMD display;
484     // - Ignore stereoscopic projection in case of non-HMD 3D display
485     //   (ideally, we would need a stereoscopic cubemap image; adding a parallax makes no sense);
486     // - Force perspective projection when orthographic camera is active
487     //   (orthographic projection makes no sense for cubemap).
488     const bool isCustomProj = aCamera.IsCustomStereoFrustum()
489                            || aCamera.IsCustomStereoProjection();
490     aCamera.SetProjectionType (theProjection == Graphic3d_Camera::Projection_Orthographic || !isCustomProj
491                              ? Graphic3d_Camera::Projection_Perspective
492                              : theProjection);
493
494     aProjection = aCamera.ProjectionMatrixF();
495     aWorldView = aCamera.OrientationMatrixF();
496     if (isCustomProj)
497     {
498       // get projection matrix without pre-multiplied stereoscopic head-to-eye translation
499       if (theProjection == Graphic3d_Camera::Projection_MonoLeftEye)
500       {
501         Graphic3d_Mat4 aMatProjL, aMatHeadToEyeL, aMatProjR, aMatHeadToEyeR;
502         aCamera.StereoProjectionF (aMatProjL, aMatHeadToEyeL, aMatProjR, aMatHeadToEyeR);
503         aProjection = aMatProjL;
504       }
505       else if (theProjection == Graphic3d_Camera::Projection_MonoRightEye)
506       {
507         Graphic3d_Mat4 aMatProjL, aMatHeadToEyeL, aMatProjR, aMatHeadToEyeR;
508         aCamera.StereoProjectionF (aMatProjL, aMatHeadToEyeL, aMatProjR, aMatHeadToEyeR);
509         aProjection = aMatProjR;
510       }
511     }
512   }
513   else
514   {
515     aProjection.InitIdentity();
516     aWorldView.InitIdentity();
517     if (theWorkspace->View()->Camera()->Tile().IsValid())
518     {
519       aWorldView.SetDiagonal (OpenGl_Vec4 (2.0f / aTileSize.x(), 2.0f / aTileSize.y(), 1.0f, 1.0f));
520       if (myType == Graphic3d_TOB_GRADIENT)
521       {
522         aWorldView.SetColumn (3, OpenGl_Vec4 (-1.0f - 2.0f * aTileOffset.x() / aTileSize.x(),
523                                               -1.0f - 2.0f * aTileOffset.y() / aTileSize.y(), 0.0f, 1.0f));
524       }
525       else
526       {
527         aWorldView.SetColumn (3, OpenGl_Vec4 (-1.0f + (float) aViewSizeX / aTileSize.x() - 2.0f * aTileOffset.x() / aTileSize.x(),
528                                               -1.0f + (float) aViewSizeY / aTileSize.y() - 2.0f * aTileOffset.y() / aTileSize.y(), 0.0f, 1.0f));
529       }
530     }
531     else
532     {
533       aWorldView.SetDiagonal (OpenGl_Vec4 (2.0f / myViewWidth, 2.0f / myViewHeight, 1.0f, 1.0f));
534       if (myType == Graphic3d_TOB_GRADIENT)
535       {
536         aWorldView.SetColumn (3, OpenGl_Vec4 (-1.0f, -1.0f, 0.0f, 1.0f));
537       }
538     }
539   }
540
541   aCtx->ProjectionState.Push();
542   aCtx->WorldViewState.Push();
543   aCtx->ProjectionState.SetCurrent (aProjection);
544   aCtx->WorldViewState.SetCurrent (aWorldView);
545   aCtx->ApplyProjectionMatrix();
546   aCtx->ApplyModelViewMatrix();
547
548   OpenGl_PrimitiveArray::Render (theWorkspace);
549
550   aCtx->ProjectionState.Pop();
551   aCtx->WorldViewState.Pop();
552   aCtx->ApplyProjectionMatrix();
553 }