0027836: Visualization, TKOpenGl - gradient background is lost at some camera positions
[occt.git] / src / Graphic3d / Graphic3d_TransformPers.hxx
1 // Created on: 2015-06-18
2 // Created by: Anton POLETAEV
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 #ifndef _Graphic3d_TransformPers_HeaderFile
17 #define _Graphic3d_TransformPers_HeaderFile
18
19 #include <Bnd_Box.hxx>
20 #include <BVH_Box.hxx>
21 #include <Graphic3d_Camera.hxx>
22 #include <Graphic3d_TransformUtils.hxx>
23 #include <Graphic3d_TransModeFlags.hxx>
24 #include <NCollection_Mat4.hxx>
25 #include <NCollection_Vec4.hxx>
26
27 //! Class for keeping and computing transformation persistence.
28 class Graphic3d_TransformPers
29 {
30 public:
31
32   DEFINE_STANDARD_ALLOC
33
34   //! Default constructor.
35   Graphic3d_TransformPers()
36   : Flags (Graphic3d_TMF_None),
37     Point (0.0, 0.0, 0.0) {}
38
39   //! Transformation persistence mode flags.
40   Graphic3d_TransModeFlags Flags;
41
42   //! Reference point for transformation.
43   Graphic3d_Vec3d Point;
44
45 public:
46
47   //! Apply transformation to bounding box of presentation.
48   //! @param theCamera [in] camera definition
49   //! @param theProjection [in] the projection transformation matrix.
50   //! @param theWorldView [in] the world view transformation matrix.
51   //! @param theViewportWidth [in] the width of viewport (for 2d persistence).
52   //! @param theViewportHeight [in] the height of viewport (for 2d persistence).
53   //! @param theBoundingBox [in/out] the bounding box to transform.
54   template<class T>
55   void Apply (const Handle(Graphic3d_Camera)& theCamera,
56               const NCollection_Mat4<T>& theProjection,
57               const NCollection_Mat4<T>& theWorldView,
58               const Standard_Integer theViewportWidth,
59               const Standard_Integer theViewportHeight,
60               Bnd_Box& theBoundingBox) const;
61
62   //! Apply transformation to bounding box of presentation
63   //! @param theCamera [in] camera definition
64   //! @param theProjection [in] the projection transformation matrix.
65   //! @param theWorldView [in] the world view transformation matrix.
66   //! @param theViewportWidth [in] the width of viewport (for 2d persistence).
67   //! @param theViewportHeight [in] the height of viewport (for 2d persistence).
68   //! @param theBoundingBox [in/out] the bounding box to transform.
69   template<class T>
70   void Apply (const Handle(Graphic3d_Camera)& theCamera,
71               const NCollection_Mat4<T>& theProjection,
72               const NCollection_Mat4<T>& theWorldView,
73               const Standard_Integer theViewportWidth,
74               const Standard_Integer theViewportHeight,
75               BVH_Box<T, 4>& theBoundingBox) const;
76
77   //! Compute transformation.
78   //! Computed matrix can be applied to model world transformation
79   //! of an object to implement effect of transformation persistence.
80   //! @param theCamera [in] camera definition
81   //! @param theProjection [in] the projection transformation matrix.
82   //! @param theWorldView [in] the world view transformation matrix.
83   //! @param theViewportWidth [in] the width of viewport (for 2d persistence).
84   //! @param theViewportHeight [in] the height of viewport (for 2d persistence).
85   //! @return transformation matrix to be applied to model world transformation of an object.
86   template<class T>
87   NCollection_Mat4<T> Compute (const Handle(Graphic3d_Camera)& theCamera,
88                                const NCollection_Mat4<T>& theProjection,
89                                const NCollection_Mat4<T>& theWorldView,
90                                const Standard_Integer theViewportWidth,
91                                const Standard_Integer theViewportHeight) const;
92
93   //! Apply transformation persistence on specified matrices.
94   //! @param theCamera camera definition
95   //! @param theProjection projection matrix to modify
96   //! @param theWorldView  world-view matrix to modify
97   //! @param theViewportWidth  viewport width
98   //! @param theViewportHeight viewport height
99   template<class T>
100   void Apply (const Handle(Graphic3d_Camera)& theCamera,
101               NCollection_Mat4<T>& theProjection,
102               NCollection_Mat4<T>& theWorldView,
103               const Standard_Integer theViewportWidth,
104               const Standard_Integer theViewportHeight) const;
105
106   //! Return true if transformation persistence alters projection matrix.
107   bool AltersProjectionMatrix() const
108   {
109     return (Flags & Graphic3d_TMF_PanPers) != 0;
110   }
111
112 };
113
114 // =======================================================================
115 // function : Apply
116 // purpose  : Apply transformation to world view and projection matrices.
117 // =======================================================================
118 template<class T>
119 void Graphic3d_TransformPers::Apply (const Handle(Graphic3d_Camera)& theCamera,
120                                      NCollection_Mat4<T>& theProjection,
121                                      NCollection_Mat4<T>& theWorldView,
122                                      const Standard_Integer theViewportWidth,
123                                      const Standard_Integer theViewportHeight) const
124 {
125   (void )theViewportWidth;
126   if (Flags == Graphic3d_TMF_None
127    || theViewportHeight == 0)
128   {
129     return;
130   }
131
132   const Standard_Integer aVPSizeY = theCamera->Tile().IsValid() ? theCamera->Tile().TotalSize.y() : theViewportHeight;
133   if (Flags == Graphic3d_TMF_TriedronPers)
134   {
135     // reset Z focus for trihedron persistence
136     const Standard_Real aFocus = theCamera->IsOrthographic()
137                                ? theCamera->Distance()
138                                : (theCamera->ZFocusType() == Graphic3d_Camera::FocusType_Relative
139                                 ? Standard_Real(theCamera->ZFocus() * theCamera->Distance())
140                                 : Standard_Real(theCamera->ZFocus()));
141
142     // scale factor to pixels
143     const gp_XYZ aViewDim = theCamera->ViewDimensions (aFocus);
144     const Standard_Real aScale = Abs(aViewDim.Y()) / Standard_Real(aVPSizeY);
145
146     // offset from the corner
147     const Standard_Real anOffset = Point.z() * aScale;
148
149     const gp_Dir aForward (theCamera->Center().XYZ() - theCamera->Eye().XYZ());
150     gp_XYZ aCenter = theCamera->Center().XYZ() + aForward.XYZ() * (aFocus - theCamera->Distance());
151     if (Point.x() != 0.0)
152     {
153       const gp_Dir aSide = aForward.Crossed (theCamera->Up());
154       if (Point.x() > 0.0)
155       {
156         aCenter += aSide.XYZ() * (Abs(aViewDim.X()) * 0.5 - anOffset);
157       }
158       else
159       {
160         aCenter -= aSide.XYZ() * (Abs(aViewDim.X()) * 0.5 - anOffset);
161       }
162     }
163     if (Point.y() != 0.0)
164     {
165       if (Point.y() > 0.0)
166       {
167         aCenter += theCamera->Up().XYZ() * (Abs(aViewDim.Y()) * 0.5 - anOffset);
168       }
169       else
170       {
171         aCenter -= theCamera->Up().XYZ() * (Abs(aViewDim.Y()) * 0.5 - anOffset);
172       }
173     }
174
175     Graphic3d_TransformUtils::Translate (theWorldView, T(aCenter.X()), T(aCenter.Y()), T(aCenter.Z()));
176     Graphic3d_TransformUtils::Scale     (theWorldView, T(aScale),      T(aScale),      T(aScale));
177     return;
178   }
179   else if (Flags == Graphic3d_TMF_2d)
180   {
181     const Standard_Real aFocus = theCamera->IsOrthographic()
182                                ? theCamera->Distance()
183                                : (theCamera->ZFocusType() == Graphic3d_Camera::FocusType_Relative
184                                 ? Standard_Real(theCamera->ZFocus() * theCamera->Distance())
185                                 : Standard_Real(theCamera->ZFocus()));
186
187     // scale factor to pixels
188     const gp_XYZ        aViewDim = theCamera->ViewDimensions (aFocus);
189     const Standard_Real aScale   = Abs(aViewDim.Y()) / Standard_Real(aVPSizeY);
190     gp_XYZ aCenter (0.0, 0.0, -aFocus);
191     if (Point.x() != 0.0)
192     {
193       aCenter.SetX (-aViewDim.X() * 0.5 + Point.z() * aScale);
194       if (Point.x() > 0.0)
195       {
196         aCenter.SetX (-aCenter.X());
197       }
198     }
199     if (Point.y() != 0.0)
200     {
201       aCenter.SetY (-aViewDim.Y() * 0.5 + Point.z() * aScale);
202       if (Point.y() > 0.0)
203       {
204         aCenter.SetY (-aCenter.Y());
205       }
206     }
207
208     theWorldView.InitIdentity();
209     Graphic3d_TransformUtils::Translate (theWorldView, T(aCenter.X()), T(aCenter.Y()), T(aCenter.Z()));
210     Graphic3d_TransformUtils::Scale     (theWorldView, T(aScale),      T(aScale),      T(aScale));
211     return;
212   }
213
214   {
215     // Compute reference point for transformation in untransformed projection space.
216     NCollection_Vec4<T> aRefPoint (static_cast<T> (Point.x()),
217                                    static_cast<T> (Point.y()),
218                                    static_cast<T> (Point.z()),
219                                    static_cast<T> (1.0));
220     NCollection_Vec4<T> aRefPointProj;
221     if ((Flags & Graphic3d_TMF_PanPers) != Graphic3d_TMF_PanPers)
222     {
223       aRefPointProj  = theProjection * (theWorldView * aRefPoint);
224       aRefPointProj /= aRefPointProj.w();
225     }
226
227     // Prevent zooming.
228     if ((Flags & Graphic3d_TMF_ZoomPers) != 0)
229     {
230       const T aSize = static_cast<T> (1.0);
231       const Standard_Integer aViewport[4] = { 0, 0, theViewportHeight, theViewportHeight };
232       NCollection_Mat4<T> aWorldView;
233       aWorldView.InitIdentity();
234
235       NCollection_Vec3<T> aWinCoordsRefPoint;
236       Graphic3d_TransformUtils::Project (static_cast<T> (Point.x()),
237                                          static_cast<T> (Point.y()),
238                                          static_cast<T> (Point.z()),
239                                          theWorldView, theProjection, aViewport,
240                                          aWinCoordsRefPoint.x(), aWinCoordsRefPoint.y(), aWinCoordsRefPoint.z());
241
242       NCollection_Vec3<T> anUnProj1;
243       Graphic3d_TransformUtils::UnProject (aWinCoordsRefPoint.x(), aWinCoordsRefPoint.y(), aWinCoordsRefPoint.z(),
244                                            aWorldView, theProjection, aViewport,
245                                            anUnProj1.x(), anUnProj1.y(), anUnProj1.z());
246
247       NCollection_Vec3<T> anUnProj2;
248       Graphic3d_TransformUtils::UnProject (aWinCoordsRefPoint.x(), aWinCoordsRefPoint.y() + aSize, aWinCoordsRefPoint.z(),
249                                            aWorldView, theProjection, aViewport,
250                                            anUnProj2.x(), anUnProj2.y(), anUnProj2.z());
251
252       const T aScale = (anUnProj2.y() - anUnProj1.y()) / aSize;
253
254       Graphic3d_TransformUtils::Scale (theWorldView, aScale, aScale, aScale);
255     }
256
257     // Prevent translation by nullifying translation component.
258     if ((Flags & Graphic3d_TMF_PanPers) != 0)
259     {
260       theWorldView .SetValue (0, 3, static_cast<T> (0.0));
261       theWorldView .SetValue (1, 3, static_cast<T> (0.0));
262       theWorldView .SetValue (2, 3, static_cast<T> (0.0));
263       theProjection.SetValue (0, 3, static_cast<T> (0.0));
264       theProjection.SetValue (1, 3, static_cast<T> (0.0));
265       theProjection.SetValue (2, 3, static_cast<T> (0.0));
266     }
267
268     // Prevent rotation by nullifying rotation component.
269     if (Flags & Graphic3d_TMF_RotatePers)
270     {
271       theWorldView.SetValue (0, 0, static_cast<T> (1.0));
272       theWorldView.SetValue (1, 0, static_cast<T> (0.0));
273       theWorldView.SetValue (2, 0, static_cast<T> (0.0));
274
275       theWorldView.SetValue (0, 1, static_cast<T> (0.0));
276       theWorldView.SetValue (1, 1, static_cast<T> (1.0));
277       theWorldView.SetValue (2, 1, static_cast<T> (0.0));
278
279       theWorldView.SetValue (0, 2, static_cast<T> (0.0));
280       theWorldView.SetValue (1, 2, static_cast<T> (0.0));
281       theWorldView.SetValue (2, 2, static_cast<T> (1.0));
282     }
283
284     if ((Flags & Graphic3d_TMF_PanPers) != Graphic3d_TMF_PanPers)
285     {
286       NCollection_Mat4<T> anUnviewMat;
287
288       if (!(theProjection * theWorldView).Inverted (anUnviewMat))
289       {
290         Standard_ProgramError::Raise ("Graphic3d_TransformPers::Apply, can not inverse world view projection matrix.");
291       }
292
293       // Move to reference point location in transformed view projection space.
294       aRefPoint  = anUnviewMat * aRefPointProj;
295       aRefPoint /= aRefPoint.w();
296
297       Graphic3d_TransformUtils::Translate<T> (theWorldView, aRefPoint.x(), aRefPoint.y(), aRefPoint.z());
298     }
299   }
300 }
301
302 // =======================================================================
303 // function : Apply
304 // purpose  : Apply transformation to bounding box of presentation.
305 // =======================================================================
306 template<class T>
307 void Graphic3d_TransformPers::Apply (const Handle(Graphic3d_Camera)& theCamera,
308                                      const NCollection_Mat4<T>& theProjection,
309                                      const NCollection_Mat4<T>& theWorldView,
310                                      const Standard_Integer theViewportWidth,
311                                      const Standard_Integer theViewportHeight,
312                                      Bnd_Box& theBoundingBox) const
313 {
314   if (theBoundingBox.IsVoid())
315   {
316     return;
317   }
318
319   T aXmin, aYmin, aZmin, aXmax, aYmax, aZmax;
320
321   theBoundingBox.Get (aXmin, aYmin, aZmin, aXmax, aYmax, aZmax);
322
323   typename BVH_Box<T, 4>::BVH_VecNt aMin (aXmin, aYmin, aZmin, static_cast<T> (1.0));
324   typename BVH_Box<T, 4>::BVH_VecNt aMax (aXmax, aYmax, aZmax, static_cast<T> (1.0));
325   BVH_Box<T, 4> aBBox (aMin, aMax);
326
327   Apply (theCamera, theProjection, theWorldView, theViewportWidth, theViewportHeight, aBBox);
328
329   theBoundingBox = Bnd_Box();
330   theBoundingBox.Update (aBBox.CornerMin().x(), aBBox.CornerMin().y(), aBBox.CornerMin().z(),
331                          aBBox.CornerMax().x(), aBBox.CornerMax().y(), aBBox.CornerMax().z());
332 }
333
334 // =======================================================================
335 // function : Apply
336 // purpose  : Apply transformation to bounding box of presentation.
337 // =======================================================================
338 template<class T>
339 void Graphic3d_TransformPers::Apply (const Handle(Graphic3d_Camera)& theCamera,
340                                      const NCollection_Mat4<T>& theProjection,
341                                      const NCollection_Mat4<T>& theWorldView,
342                                      const Standard_Integer theViewportWidth,
343                                      const Standard_Integer theViewportHeight,
344                                      BVH_Box<T, 4>& theBoundingBox) const
345 {
346   NCollection_Mat4<T> aTPers = Compute (theCamera, theProjection, theWorldView, theViewportWidth, theViewportHeight);
347   if (aTPers.IsIdentity()
348   || !theBoundingBox.IsValid())
349   {
350     return;
351   }
352
353   const typename BVH_Box<T, 4>::BVH_VecNt& aMin = theBoundingBox.CornerMin();
354   const typename BVH_Box<T, 4>::BVH_VecNt& aMax = theBoundingBox.CornerMax();
355
356   typename BVH_Box<T, 4>::BVH_VecNt anArrayOfCorners[8];
357   anArrayOfCorners[0] = typename BVH_Box<T, 4>::BVH_VecNt (aMin.x(), aMin.y(), aMin.z(), static_cast<T> (1.0));
358   anArrayOfCorners[1] = typename BVH_Box<T, 4>::BVH_VecNt (aMin.x(), aMin.y(), aMax.z(), static_cast<T> (1.0));
359   anArrayOfCorners[2] = typename BVH_Box<T, 4>::BVH_VecNt (aMin.x(), aMax.y(), aMin.z(), static_cast<T> (1.0));
360   anArrayOfCorners[3] = typename BVH_Box<T, 4>::BVH_VecNt (aMin.x(), aMax.y(), aMax.z(), static_cast<T> (1.0));
361   anArrayOfCorners[4] = typename BVH_Box<T, 4>::BVH_VecNt (aMax.x(), aMin.y(), aMin.z(), static_cast<T> (1.0));
362   anArrayOfCorners[5] = typename BVH_Box<T, 4>::BVH_VecNt (aMax.x(), aMin.y(), aMax.z(), static_cast<T> (1.0));
363   anArrayOfCorners[6] = typename BVH_Box<T, 4>::BVH_VecNt (aMax.x(), aMax.y(), aMin.z(), static_cast<T> (1.0));
364   anArrayOfCorners[7] = typename BVH_Box<T, 4>::BVH_VecNt (aMax.x(), aMax.y(), aMax.z(), static_cast<T> (1.0));
365
366   theBoundingBox.Clear();
367   for (Standard_Integer anIt = 0; anIt < 8; ++anIt)
368   {
369     typename BVH_Box<T, 4>::BVH_VecNt& aCorner = anArrayOfCorners[anIt];
370     aCorner  = aTPers * aCorner;
371     aCorner /= aCorner.w();
372     theBoundingBox.Add (aCorner);
373   }
374 }
375
376 // =======================================================================
377 // function : Compute
378 // purpose  : Compute transformation.
379 // =======================================================================
380 template<class T>
381 NCollection_Mat4<T> Graphic3d_TransformPers::Compute (const Handle(Graphic3d_Camera)& theCamera,
382                                                       const NCollection_Mat4<T>& theProjection,
383                                                       const NCollection_Mat4<T>& theWorldView,
384                                                       const Standard_Integer theViewportWidth,
385                                                       const Standard_Integer theViewportHeight) const
386 {
387   if (Flags == Graphic3d_TMF_None)
388   {
389     return NCollection_Mat4<T>();
390   }
391
392   NCollection_Mat4<T> aProjection (theProjection);
393   NCollection_Mat4<T> aWorldView  (theWorldView);
394   NCollection_Mat4<T> anUnviewMat;
395   if (AltersProjectionMatrix())
396   {
397     // destructive transformation persistence which directly modifies projection matrix
398     if (!(theProjection * theWorldView).Inverted (anUnviewMat))
399     {
400       return NCollection_Mat4<T>();
401     }
402
403     Apply (theCamera, aProjection, aWorldView, theViewportWidth, theViewportHeight);
404     return anUnviewMat * (aProjection * aWorldView);
405   }
406
407   if (!theWorldView.Inverted (anUnviewMat))
408   {
409     return NCollection_Mat4<T>();
410   }
411
412   // compute only world-view matrix difference to avoid floating point instability
413   // caused by projection matrix modifications outside of this algorithm (e.g. by Z-fit)
414   Apply (theCamera, aProjection, aWorldView, theViewportWidth, theViewportHeight);
415   return anUnviewMat * aWorldView;
416 }
417
418 #endif // _Graphic3d_TransformPers_HeaderFile