0025675: Visualization - Fix problems and inefficiencies with frustum culling
[occt.git] / src / OpenGl / OpenGl_Utils.hxx
1 // Created on: 2014-09-30
2 // Created by: Denis BOGOLEPOV
3 // Copyright (c) 2014 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 #ifndef _OpenGl_Utils_H__
17 #define _OpenGl_Utils_H__
18
19 #include <OpenGl_Vec.hxx>
20 #include <NCollection_Vector.hxx>
21
22 //! Helper class that implements some functionality of GLU library.
23 namespace OpenGl_Utils
24 {
25
26   //! Matrix type selector.
27   template<class T>
28   struct MatrixType {
29     //
30   };
31
32   template<>
33   struct MatrixType<Standard_Real> {
34     typedef OpenGl_Mat4d Mat4;
35   };
36
37   template<>
38   struct MatrixType<Standard_ShortReal> {
39     typedef OpenGl_Mat4 Mat4;
40   };
41
42   //! Vector type selector.
43   template<class T>
44   struct VectorType {
45     //
46   };
47
48   template<>
49   struct VectorType<Standard_Real> {
50     typedef OpenGl_Vec2d Vec2;
51     typedef OpenGl_Vec3d Vec3;
52     typedef OpenGl_Vec4d Vec4;
53   };
54
55   template<>
56   struct VectorType<Standard_ShortReal> {
57     typedef OpenGl_Vec2 Vec2;
58     typedef OpenGl_Vec3 Vec3;
59     typedef OpenGl_Vec4 Vec4;
60   };
61
62   //! Software implementation for OpenGL matrix stack.
63   template<class T>
64   class MatrixState
65   {
66   public:
67
68     //! Constructs matrix state object.
69     MatrixState()
70     : myStack (8),
71       myStackHead (-1)
72     {
73       //
74     }
75
76     //! Pushes current matrix into stack.
77     void Push()
78     {
79       if (++myStackHead >= myStack.Size())
80       {
81         myStack.Append (myCurrent);
82       }
83       else
84       {
85         myStack.SetValue (myStackHead, myCurrent);
86       }
87     }
88
89     //! Pops matrix from stack to current.
90     void Pop()
91     {
92       Standard_ASSERT_RETURN (myStackHead != -1, "Matrix stack already empty when MatrixState.Pop() called.", );
93       myCurrent = myStack.Value (myStackHead--);
94     }
95
96     //! @return current matrix.
97     const typename MatrixType<T>::Mat4& Current()
98     {
99       return myCurrent;
100     }
101
102     //! Sets given matrix as current.
103     void SetCurrent (const typename MatrixType<T>::Mat4& theNewCurrent)
104     {
105       myCurrent = theNewCurrent;
106     }
107
108     //! Sets given matrix as current.
109     template <typename Other_t>
110     void SetCurrent (const typename MatrixType<Other_t>::Mat4& theNewCurrent)
111     {
112       myCurrent.Convert (theNewCurrent);
113     }
114
115     //! Sets current matrix to identity.
116     void SetIdentity()
117     {
118       myCurrent = typename MatrixType<T>::Mat4();
119     }
120
121   private:
122
123     NCollection_Vector<typename MatrixType<T>::Mat4> myStack;     //!< Collection used to maintenance matrix stack
124     typename MatrixType<T>::Mat4                     myCurrent;   //!< Current matrix
125     Standard_Integer                                 myStackHead; //!< Index of stack head
126
127   };
128
129   //! Constructs a 3D orthographic projection matrix.
130   template<class T>
131   static void Ortho (typename MatrixType<T>::Mat4& theOut,
132     const T theLeft, const T theRight, const T theBottom, const T theTop, const T theZNear, const T theZFar);
133
134   //! Constructs a 2D orthographic projection matrix.
135   template<class T>
136   static void Ortho2D (typename MatrixType<T>::Mat4& theOut,
137     const T theLeft, const T theRight, const T theBottom, const T theTop);
138
139   //! Maps object coordinates to window coordinates.
140   template<class T>
141   static Standard_Boolean Project (const T                             theObjX,
142                                    const T                             theObjY,
143                                    const T                             theObjZ,
144                                    const typename MatrixType<T>::Mat4& theModViewMat,
145                                    const typename MatrixType<T>::Mat4& theProjectMat,
146                                    const Standard_Integer              theViewport[4],
147                                    T&                                  theWinX,
148                                    T&                                  theWinY,
149                                    T&                                  theWinZ);
150
151   //! Maps window coordinates to object coordinates.
152   template<class T>
153   static Standard_Boolean UnProject (const T                             theWinX,
154                                      const T                             theWinY,
155                                      const T                             theWinZ,
156                                      const typename MatrixType<T>::Mat4& theModViewMat,
157                                      const typename MatrixType<T>::Mat4& theProjectMat,
158                                      const Standard_Integer              theViewport[4],
159                                      T&                                  theObjX,
160                                      T&                                  theObjY,
161                                      T&                                  theObjZ);
162
163   //! Constructs a 4x4 rotation matrix.
164   template<class T>
165   static void ConstructRotate (typename MatrixType<T>::Mat4& theOut,
166                                T                             theA,
167                                T                             theX,
168                                T                             theY,
169                                T                             theZ);
170
171   //! Constructs a 4x4 rotation matrix.
172   template<class T>
173   static void Rotate (typename MatrixType<T>::Mat4& theOut,
174                       T                             theA,
175                       T                             theX,
176                       T                             theY,
177                       T                             theZ);
178
179   //! Constructs a 4x4 scaling matrix.
180   template<class T>
181   static void Scale (typename MatrixType<T>::Mat4& theOut,
182                      T                             theX,
183                      T                             theY,
184                      T                             theZ);
185
186   //! Constructs a 4x4 translation matrix.
187   template<class T>
188   static void Translate (typename MatrixType<T>::Mat4& theOut,
189                          T                             theX,
190                          T                             theY,
191                          T                             theZ);
192
193 }
194
195 // =======================================================================
196 // function : Rotate
197 // purpose  : Constructs a 4x4 rotation matrix
198 // =======================================================================
199 template<class T>
200 void OpenGl_Utils::Rotate (typename MatrixType<T>::Mat4& theOut,
201                            T                             theA,
202                            T                             theX,
203                            T                             theY,
204                            T                             theZ)
205 {
206   typename MatrixType<T>::Mat4 aMat;
207   ConstructRotate (aMat, theA, theX, theY, theZ);
208   theOut = theOut * aMat;
209 }
210
211 // =======================================================================
212 // function : Translate
213 // purpose  : Constructs a 4x4 translation matrix
214 // =======================================================================
215 template<class T>
216 void OpenGl_Utils::Translate (typename MatrixType<T>::Mat4& theOut,
217                               T                             theX,
218                               T                             theY,
219                               T                             theZ)
220 {
221   theOut.ChangeValue (0, 3) = theOut.GetValue (0, 0) * theX +
222                               theOut.GetValue (0, 1) * theY +
223                               theOut.GetValue (0, 2) * theZ +
224                               theOut.GetValue (0, 3);
225
226   theOut.ChangeValue (1, 3) = theOut.GetValue (1, 0) * theX +
227                               theOut.GetValue (1, 1) * theY +
228                               theOut.GetValue (1, 2) * theZ +
229                               theOut.GetValue (1, 3);
230
231   theOut.ChangeValue (2, 3) = theOut.GetValue (2, 0) * theX +
232                               theOut.GetValue (2, 1) * theY +
233                               theOut.GetValue (2, 2) * theZ +
234                               theOut.GetValue (2, 3);
235
236   theOut.ChangeValue (3, 3) = theOut.GetValue (3, 0) * theX +
237                               theOut.GetValue (3, 1) * theY +
238                               theOut.GetValue (3, 2) * theZ +
239                               theOut.GetValue (3, 3);
240 }
241
242 // =======================================================================
243 // function : Scale
244 // purpose  : Constructs a 4x4 scaling matrix
245 // =======================================================================
246 template<class T>
247 void OpenGl_Utils::Scale (typename MatrixType<T>::Mat4& theOut,
248                           T                             theX,
249                           T                             theY,
250                           T                             theZ)
251 {
252   theOut.ChangeValue (0, 0) *= theX;
253   theOut.ChangeValue (1, 0) *= theX;
254   theOut.ChangeValue (2, 0) *= theX;
255   theOut.ChangeValue (3, 0) *= theX;
256
257   theOut.ChangeValue (0, 1) *= theY;
258   theOut.ChangeValue (1, 1) *= theY;
259   theOut.ChangeValue (2, 1) *= theY;
260   theOut.ChangeValue (3, 1) *= theY;
261
262   theOut.ChangeValue (0, 2) *= theZ;
263   theOut.ChangeValue (1, 2) *= theZ;
264   theOut.ChangeValue (2, 2) *= theZ;
265   theOut.ChangeValue (3, 2) *= theZ;
266 }
267
268 // =======================================================================
269 // function : ConstructRotate
270 // purpose  : Constructs a 4x4 rotation matrix
271 // =======================================================================
272 template<class T>
273 void OpenGl_Utils::ConstructRotate (typename MatrixType<T>::Mat4& theOut, T theA, T theX, T theY, T theZ)
274 {
275   const T aSin = std::sin (theA * static_cast<T> (M_PI / 180.0));
276   const T aCos = std::cos (theA * static_cast<T> (M_PI / 180.0));
277
278   const Standard_Boolean isOnlyX = (theX != static_cast<T> (0.0))
279                                 && (theY == static_cast<T> (0.0))
280                                 && (theZ == static_cast<T> (0.0));
281
282   const Standard_Boolean isOnlyY = (theX == static_cast<T> (0.0))
283                                 && (theY != static_cast<T> (0.0))
284                                 && (theZ == static_cast<T> (0.0));
285
286   const Standard_Boolean isOnlyZ = (theX == static_cast<T> (0.0))
287                                 && (theY == static_cast<T> (0.0))
288                                 && (theZ != static_cast<T> (0.0));
289
290   if (isOnlyX) // Rotation only around X
291   {
292     theOut.SetValue (1, 1, aCos);
293     theOut.SetValue (2, 2, aCos);
294
295     if (theX < static_cast<T> (0.0))
296     {
297       theOut.SetValue (1, 2,  aSin);
298       theOut.SetValue (2, 1, -aSin);
299     }
300     else
301     {
302       theOut.SetValue (1, 2, -aSin);
303       theOut.SetValue (2, 1,  aSin);
304     }
305
306     return;
307   }
308   else if (isOnlyY) // Rotation only around Y
309   {
310     theOut.SetValue (0, 0, aCos);
311     theOut.SetValue (2, 2, aCos);
312
313     if (theY < static_cast<T> (0.0))
314     {
315       theOut.SetValue (0, 2, -aSin);
316       theOut.SetValue (2, 0,  aSin);
317     }
318     else
319     {
320       theOut.SetValue (0, 2,  aSin);
321       theOut.SetValue (2, 0, -aSin);
322     }
323
324     return;
325   }
326   else if (isOnlyZ) // Rotation only around Z
327   {
328     theOut.SetValue (0, 0, aCos);
329     theOut.SetValue (1, 1, aCos);
330
331     if (theZ < static_cast<T> (0.0))
332     {
333       theOut.SetValue (0, 1,  aSin);
334       theOut.SetValue (1, 0, -aSin);
335     }
336     else
337     {
338       theOut.SetValue (0, 1, -aSin);
339       theOut.SetValue (1, 0,  aSin);
340     }
341
342     return;
343   }
344
345   T aNorm = std::sqrt (theX * theX + theY * theY + theZ * theZ);
346
347   if (aNorm <= static_cast<T> (1.0e-4))
348   {
349     return; // negligible rotation
350   }
351
352   aNorm = static_cast<T> (1.0) / aNorm;
353
354   theX *= aNorm;
355   theY *= aNorm;
356   theZ *= aNorm;
357
358   const T aXX = theX * theX;
359   const T aYY = theY * theY;
360   const T aZZ = theZ * theZ;
361   const T aXY = theX * theY;
362   const T aYZ = theY * theZ;
363   const T aZX = theZ * theX;
364   const T aSinX = theX * aSin;
365   const T aSinY = theY * aSin;
366   const T aSinZ = theZ * aSin;
367
368   const T aOneMinusCos = static_cast<T> (1.0) - aCos;
369
370   theOut.SetValue (0, 0, aOneMinusCos * aXX + aCos);
371   theOut.SetValue (0, 1, aOneMinusCos * aXY - aSinZ);
372   theOut.SetValue (0, 2, aOneMinusCos * aZX + aSinY);
373
374   theOut.SetValue (1, 0, aOneMinusCos * aXY + aSinZ);
375   theOut.SetValue (1, 1, aOneMinusCos * aYY + aCos);
376   theOut.SetValue (1, 2, aOneMinusCos * aYZ - aSinX);
377
378   theOut.SetValue (2, 0, aOneMinusCos * aZX - aSinY);
379   theOut.SetValue (2, 1, aOneMinusCos * aYZ + aSinX);
380   theOut.SetValue (2, 2, aOneMinusCos * aZZ + aCos);
381 }
382
383 // =======================================================================
384 // function : Ortho
385 // purpose  : Constructs a 3D orthographic projection matrix
386 // =======================================================================
387 template<class T>
388 void OpenGl_Utils::Ortho (typename MatrixType<T>::Mat4& theOut,
389   const T theLeft, const T theRight, const T theBottom, const T theTop, const T theZNear, const T theZFar)
390 {
391   theOut.InitIdentity();
392
393   T* aData = theOut.ChangeData();
394
395   const T anInvDx = static_cast<T> (1.0) / (theRight - theLeft);
396   const T anInvDy = static_cast<T> (1.0) / (theTop - theBottom);
397   const T anInvDz = static_cast<T> (1.0) / (theZFar - theZNear);
398
399   aData[0]  = static_cast<T> ( 2.0) * anInvDx;
400   aData[5]  = static_cast<T> ( 2.0) * anInvDy;
401   aData[10] = static_cast<T> (-2.0) * anInvDz;
402
403   aData[12] = -(theRight + theLeft) * anInvDx;
404   aData[13] = -(theTop + theBottom) * anInvDy;
405   aData[14] = -(theZFar + theZNear) * anInvDz;
406 }
407
408 // =======================================================================
409 // function : Ortho2D
410 // purpose  : Constructs a 2D orthographic projection matrix
411 // =======================================================================
412 template<class T>
413 void OpenGl_Utils::Ortho2D (typename MatrixType<T>::Mat4& theOut,
414   const T theLeft, const T theRight, const T theBottom, const T theTop)
415 {
416   Ortho (theOut, theLeft, theRight, theBottom, theTop, static_cast<T> (-1.0), static_cast<T> (1.0));
417 }
418
419 // =======================================================================
420 // function : Project
421 // purpose  : Maps object coordinates to window coordinates
422 // =======================================================================
423 template<class T>
424 static Standard_Boolean OpenGl_Utils::Project (const T                             theObjX,
425                                                const T                             theObjY,
426                                                const T                             theObjZ,
427                                                const typename MatrixType<T>::Mat4& theModViewMat,
428                                                const typename MatrixType<T>::Mat4& theProjectMat,
429                                                const Standard_Integer              theViewport[4],
430                                                T&                                  theWinX,
431                                                T&                                  theWinY,
432                                                T&                                  theWinZ)
433 {
434   typename VectorType<T>::Vec4 anIn (theObjX, theObjY, theObjZ, static_cast<T> (1.0));
435
436   typename VectorType<T>::Vec4 anOut = theProjectMat * (theModViewMat * anIn);
437
438   if (anOut.w() == static_cast<T> (0.0))
439   {
440     return Standard_False;
441   }
442
443   anOut.w() = static_cast<T> (1.0) / anOut.w();
444
445   anOut.x() *= anOut.w();
446   anOut.y() *= anOut.w();
447   anOut.z() *= anOut.w();
448
449   // Map x, y and z to range 0-1
450   anOut.x() = anOut.x() * 0.5 + 0.5;
451   anOut.y() = anOut.y() * 0.5 + 0.5;
452   anOut.z() = anOut.z() * 0.5 + 0.5;
453
454   // Map x,y to viewport
455   anOut.x() = anOut.x() * theViewport[2] + theViewport[0];
456   anOut.y() = anOut.y() * theViewport[3] + theViewport[1];
457
458   theWinX = anOut.x();
459   theWinY = anOut.y();
460   theWinZ = anOut.z();
461
462   return Standard_True;
463 }
464
465 // =======================================================================
466 // function : UnProject
467 // purpose  : Maps window coordinates to object coordinates
468 // =======================================================================
469 template<class T>
470 static Standard_Boolean OpenGl_Utils::UnProject (const T                             theWinX,
471                                                  const T                             theWinY,
472                                                  const T                             theWinZ,
473                                                  const typename MatrixType<T>::Mat4& theModViewMat,
474                                                  const typename MatrixType<T>::Mat4& theProjectMat,
475                                                  const Standard_Integer              theViewport[4],
476                                                  T&                                  theObjX,
477                                                  T&                                  theObjY,
478                                                  T&                                  theObjZ)
479 {
480   typename MatrixType<T>::Mat4 anUnviewMat;
481
482   if (!(theProjectMat * theModViewMat).Inverted (anUnviewMat))
483   {
484     return Standard_False;
485   }
486
487   typename VectorType<T>::Vec4 anIn (theWinX, theWinY, theWinZ, static_cast<T> (1.0));
488
489   // Map x and y from window coordinates
490   anIn.x() = (anIn.x() - theViewport[0]) / theViewport[2];
491   anIn.y() = (anIn.y() - theViewport[1]) / theViewport[3];
492
493   // Map to range -1 to 1
494   anIn.x() = anIn.x() * static_cast<T> (2.0) - static_cast<T> (1.0);
495   anIn.y() = anIn.y() * static_cast<T> (2.0) - static_cast<T> (1.0);
496   anIn.z() = anIn.z() * static_cast<T> (2.0) - static_cast<T> (1.0);
497
498   typename VectorType<T>::Vec4 anOut = anUnviewMat * anIn;
499
500   if (anOut.w() == static_cast<T> (0.0))
501   {
502     return Standard_False;
503   }
504
505   anOut.w() = static_cast<T> (1.0) / anOut.w();
506
507   anOut.x() *= anOut.w();
508   anOut.y() *= anOut.w();
509   anOut.z() *= anOut.w();
510
511   theObjX = anOut.x();
512   theObjY = anOut.y();
513   theObjZ = anOut.z();
514
515   return Standard_True;
516 }
517
518 #endif // _OpenGl_Utils_H__