f2f620b12f43a0f5645d996e656082a9b4655b73
[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   template<class T>
94   void Apply (const Handle(Graphic3d_Camera)& theCamera,
95               NCollection_Mat4<T>& theProjection,
96               NCollection_Mat4<T>& theWorldView,
97               const Standard_Integer theViewportWidth,
98               const Standard_Integer theViewportHeight) const;
99 };
100
101 // =======================================================================
102 // function : Apply
103 // purpose  : Apply transformation to world view and projection matrices.
104 // =======================================================================
105 template<class T>
106 void Graphic3d_TransformPers::Apply (const Handle(Graphic3d_Camera)& theCamera,
107                                      NCollection_Mat4<T>& theProjection,
108                                      NCollection_Mat4<T>& theWorldView,
109                                      const Standard_Integer theViewportWidth,
110                                      const Standard_Integer theViewportHeight) const
111 {
112   if (!Flags)
113   {
114     return;
115   }
116
117   if (Flags == Graphic3d_TMF_TriedronPers)
118   {
119     // reset Z focus for trihedron persistence
120     const Standard_Real aFocus = theCamera->IsOrthographic()
121                                ? theCamera->Distance()
122                                : (theCamera->ZFocusType() == Graphic3d_Camera::FocusType_Relative
123                                 ? Standard_Real(theCamera->ZFocus() * theCamera->Distance())
124                                 : Standard_Real(theCamera->ZFocus()));
125
126     // scale factor to pixels
127     const gp_XYZ aViewDim = theCamera->ViewDimensions (aFocus);
128     const Standard_Real aScale = Abs(aViewDim.Y()) / Standard_Real(theViewportHeight);
129
130     // offset from the corner
131     const Standard_Real anOffset = Point.z() * aScale;
132
133     const gp_Dir aForward (theCamera->Center().XYZ() - theCamera->Eye().XYZ());
134     gp_XYZ aCenter = theCamera->Center().XYZ() + aForward.XYZ() * (aFocus - theCamera->Distance());
135     if (Point.x() != 0.0)
136     {
137       const gp_Dir aSide = aForward.Crossed (theCamera->Up());
138       if (Point.x() > 0.0)
139       {
140         aCenter += aSide.XYZ() * (Abs(aViewDim.X()) * 0.5 - anOffset);
141       }
142       else
143       {
144         aCenter -= aSide.XYZ() * (Abs(aViewDim.X()) * 0.5 - anOffset);
145       }
146     }
147     if (Point.y() != 0.0)
148     {
149       if (Point.y() > 0.0)
150       {
151         aCenter += theCamera->Up().XYZ() * (Abs(aViewDim.Y()) * 0.5 - anOffset);
152       }
153       else
154       {
155         aCenter -= theCamera->Up().XYZ() * (Abs(aViewDim.Y()) * 0.5 - anOffset);
156       }
157     }
158
159     Graphic3d_TransformUtils::Translate (theWorldView, T(aCenter.X()), T(aCenter.Y()), T(aCenter.Z()));
160     Graphic3d_TransformUtils::Scale     (theWorldView, T(aScale),      T(aScale),      T(aScale));
161     return;
162   }
163
164   if (Flags & Graphic3d_TMF_2d)
165   {
166     T aLeft   = -static_cast<T> (theViewportWidth  / 2);
167     T aRight  =  static_cast<T> (theViewportWidth  / 2);
168     T aBottom = -static_cast<T> (theViewportHeight / 2);
169     T aTop    =  static_cast<T> (theViewportHeight / 2);
170     T aGap    =  static_cast<T> (Point.z());
171     if (Point.x() > 0)
172     {
173       aLeft  -= static_cast<T> (theViewportWidth / 2) - aGap;
174       aRight -= static_cast<T> (theViewportWidth / 2) - aGap;
175     }
176     else if (Point.x() < 0)
177     {
178       aLeft  += static_cast<T> (theViewportWidth / 2) - aGap;
179       aRight += static_cast<T> (theViewportWidth / 2) - aGap;
180     }
181     if (Point.y() > 0)
182     {
183       aBottom -= static_cast<T> (theViewportHeight / 2) - aGap;
184       aTop    -= static_cast<T> (theViewportHeight / 2) - aGap;
185     }
186     else if (Point.y() < 0)
187     {
188       aBottom += static_cast<T> (theViewportHeight / 2) - aGap;
189       aTop    += static_cast<T> (theViewportHeight / 2) - aGap;
190     }
191     if (Flags == Graphic3d_TMF_2d_IsTopDown)
192     {
193       const T aTemp = aTop;
194       aTop    = aBottom;
195       aBottom = aTemp;
196     }
197
198     Graphic3d_TransformUtils::Ortho2D<T> (theProjection, aLeft, aRight, aBottom, aTop);
199
200     theWorldView.InitIdentity();
201   }
202   else
203   {
204     // Compute reference point for transformation in untransformed projection space.
205     NCollection_Vec4<T> aRefPoint (static_cast<T> (Point.x()),
206                                    static_cast<T> (Point.y()),
207                                    static_cast<T> (Point.z()),
208                                    static_cast<T> (1.0));
209     NCollection_Vec4<T> aRefPointProj;
210     if ((Flags & Graphic3d_TMF_PanPers) != Graphic3d_TMF_PanPers)
211     {
212       aRefPointProj  = theProjection * (theWorldView * aRefPoint);
213       aRefPointProj /= aRefPointProj.w();
214     }
215
216     // Prevent zooming.
217     if ((Flags & Graphic3d_TMF_ZoomPers) != 0)
218     {
219       const T aSize = static_cast<T> (1.0);
220       const Standard_Integer aViewport[4] = { 0, 0, theViewportHeight, theViewportHeight };
221       NCollection_Mat4<T> aWorldView;
222       aWorldView.InitIdentity();
223
224       NCollection_Vec3<T> aWinCoordsRefPoint;
225       Graphic3d_TransformUtils::Project (static_cast<T> (Point.x()),
226                                          static_cast<T> (Point.y()),
227                                          static_cast<T> (Point.z()),
228                                          theWorldView, theProjection, aViewport,
229                                          aWinCoordsRefPoint.x(), aWinCoordsRefPoint.y(), aWinCoordsRefPoint.z());
230
231       NCollection_Vec3<T> anUnProj1;
232       Graphic3d_TransformUtils::UnProject (aWinCoordsRefPoint.x(), aWinCoordsRefPoint.y(), aWinCoordsRefPoint.z(),
233                                            aWorldView, theProjection, aViewport,
234                                            anUnProj1.x(), anUnProj1.y(), anUnProj1.z());
235
236       NCollection_Vec3<T> anUnProj2;
237       Graphic3d_TransformUtils::UnProject (aWinCoordsRefPoint.x(), aWinCoordsRefPoint.y() + aSize, aWinCoordsRefPoint.z(),
238                                            aWorldView, theProjection, aViewport,
239                                            anUnProj2.x(), anUnProj2.y(), anUnProj2.z());
240
241       const T aScale = (anUnProj2.y() - anUnProj1.y()) / aSize;
242
243       Graphic3d_TransformUtils::Scale (theWorldView, aScale, aScale, aScale);
244     }
245
246     // Prevent translation by nullifying translation component.
247     if ((Flags & Graphic3d_TMF_PanPers) != 0)
248     {
249       theWorldView .SetValue (0, 3, static_cast<T> (0.0));
250       theWorldView .SetValue (1, 3, static_cast<T> (0.0));
251       theWorldView .SetValue (2, 3, static_cast<T> (0.0));
252       theProjection.SetValue (0, 3, static_cast<T> (0.0));
253       theProjection.SetValue (1, 3, static_cast<T> (0.0));
254       theProjection.SetValue (2, 3, static_cast<T> (0.0));
255     }
256
257     // Prevent rotation by nullifying rotation component.
258     if (Flags & Graphic3d_TMF_RotatePers)
259     {
260       theWorldView.SetValue (0, 0, static_cast<T> (1.0));
261       theWorldView.SetValue (1, 0, static_cast<T> (0.0));
262       theWorldView.SetValue (2, 0, static_cast<T> (0.0));
263
264       theWorldView.SetValue (0, 1, static_cast<T> (0.0));
265       theWorldView.SetValue (1, 1, static_cast<T> (1.0));
266       theWorldView.SetValue (2, 1, static_cast<T> (0.0));
267
268       theWorldView.SetValue (0, 2, static_cast<T> (0.0));
269       theWorldView.SetValue (1, 2, static_cast<T> (0.0));
270       theWorldView.SetValue (2, 2, static_cast<T> (1.0));
271     }
272
273     if ((Flags & Graphic3d_TMF_PanPers) != Graphic3d_TMF_PanPers)
274     {
275       NCollection_Mat4<T> anUnviewMat;
276
277       if (!(theProjection * theWorldView).Inverted (anUnviewMat))
278       {
279         Standard_ProgramError::Raise ("Graphic3d_TransformPers::Apply, can not inverse world view projection matrix.");
280       }
281
282       // Move to reference point location in transformed view projection space.
283       aRefPoint  = anUnviewMat * aRefPointProj;
284       aRefPoint /= aRefPoint.w();
285
286       Graphic3d_TransformUtils::Translate<T> (theWorldView, aRefPoint.x(), aRefPoint.y(), aRefPoint.z());
287     }
288   }
289 }
290
291 // =======================================================================
292 // function : Apply
293 // purpose  : Apply transformation to bounding box of presentation.
294 // =======================================================================
295 template<class T>
296 void Graphic3d_TransformPers::Apply (const Handle(Graphic3d_Camera)& theCamera,
297                                      const NCollection_Mat4<T>& theProjection,
298                                      const NCollection_Mat4<T>& theWorldView,
299                                      const Standard_Integer theViewportWidth,
300                                      const Standard_Integer theViewportHeight,
301                                      Bnd_Box& theBoundingBox) const
302 {
303   if (theBoundingBox.IsVoid())
304   {
305     return;
306   }
307
308   T aXmin, aYmin, aZmin, aXmax, aYmax, aZmax;
309
310   theBoundingBox.Get (aXmin, aYmin, aZmin, aXmax, aYmax, aZmax);
311
312   typename BVH_Box<T, 4>::BVH_VecNt aMin (aXmin, aYmin, aZmin, static_cast<T> (1.0));
313   typename BVH_Box<T, 4>::BVH_VecNt aMax (aXmax, aYmax, aZmax, static_cast<T> (1.0));
314   BVH_Box<T, 4> aBBox (aMin, aMax);
315
316   Apply (theCamera, theProjection, theWorldView, theViewportWidth, theViewportHeight, aBBox);
317
318   theBoundingBox = Bnd_Box();
319   theBoundingBox.Update (aBBox.CornerMin().x(), aBBox.CornerMin().y(), aBBox.CornerMin().z(),
320                          aBBox.CornerMax().x(), aBBox.CornerMax().y(), aBBox.CornerMax().z());
321 }
322
323 // =======================================================================
324 // function : Apply
325 // purpose  : Apply transformation to bounding box of presentation.
326 // =======================================================================
327 template<class T>
328 void Graphic3d_TransformPers::Apply (const Handle(Graphic3d_Camera)& theCamera,
329                                      const NCollection_Mat4<T>& theProjection,
330                                      const NCollection_Mat4<T>& theWorldView,
331                                      const Standard_Integer theViewportWidth,
332                                      const Standard_Integer theViewportHeight,
333                                      BVH_Box<T, 4>& theBoundingBox) const
334 {
335   NCollection_Mat4<T> aTPers = Compute (theCamera, theProjection, theWorldView, theViewportWidth, theViewportHeight);
336   if (aTPers.IsIdentity()
337   || !theBoundingBox.IsValid())
338   {
339     return;
340   }
341
342   const typename BVH_Box<T, 4>::BVH_VecNt& aMin = theBoundingBox.CornerMin();
343   const typename BVH_Box<T, 4>::BVH_VecNt& aMax = theBoundingBox.CornerMax();
344
345   typename BVH_Box<T, 4>::BVH_VecNt anArrayOfCorners[8];
346   anArrayOfCorners[0] = typename BVH_Box<T, 4>::BVH_VecNt (aMin.x(), aMin.y(), aMin.z(), static_cast<T> (1.0));
347   anArrayOfCorners[1] = typename BVH_Box<T, 4>::BVH_VecNt (aMin.x(), aMin.y(), aMax.z(), static_cast<T> (1.0));
348   anArrayOfCorners[2] = typename BVH_Box<T, 4>::BVH_VecNt (aMin.x(), aMax.y(), aMin.z(), static_cast<T> (1.0));
349   anArrayOfCorners[3] = typename BVH_Box<T, 4>::BVH_VecNt (aMin.x(), aMax.y(), aMax.z(), static_cast<T> (1.0));
350   anArrayOfCorners[4] = typename BVH_Box<T, 4>::BVH_VecNt (aMax.x(), aMin.y(), aMin.z(), static_cast<T> (1.0));
351   anArrayOfCorners[5] = typename BVH_Box<T, 4>::BVH_VecNt (aMax.x(), aMin.y(), aMax.z(), static_cast<T> (1.0));
352   anArrayOfCorners[6] = typename BVH_Box<T, 4>::BVH_VecNt (aMax.x(), aMax.y(), aMin.z(), static_cast<T> (1.0));
353   anArrayOfCorners[7] = typename BVH_Box<T, 4>::BVH_VecNt (aMax.x(), aMax.y(), aMax.z(), static_cast<T> (1.0));
354
355   theBoundingBox.Clear();
356   for (Standard_Integer anIt = 0; anIt < 8; ++anIt)
357   {
358     typename BVH_Box<T, 4>::BVH_VecNt& aCorner = anArrayOfCorners[anIt];
359     aCorner  = aTPers * aCorner;
360     aCorner /= aCorner.w();
361     theBoundingBox.Add (aCorner);
362   }
363 }
364
365 // =======================================================================
366 // function : Compute
367 // purpose  : Compute transformation.
368 // =======================================================================
369 template<class T>
370 NCollection_Mat4<T> Graphic3d_TransformPers::Compute (const Handle(Graphic3d_Camera)& theCamera,
371                                                       const NCollection_Mat4<T>& theProjection,
372                                                       const NCollection_Mat4<T>& theWorldView,
373                                                       const Standard_Integer theViewportWidth,
374                                                       const Standard_Integer theViewportHeight) const
375 {
376   if (Flags == Graphic3d_TMF_None)
377   {
378     return NCollection_Mat4<T>();
379   }
380
381   NCollection_Mat4<T> anUnviewMat;
382
383   if (!(theProjection * theWorldView).Inverted (anUnviewMat))
384   {
385     return NCollection_Mat4<T>();
386   }
387
388   NCollection_Mat4<T> aProjection (theProjection);
389   NCollection_Mat4<T> aWorldView (theWorldView);
390
391   Apply (theCamera, aProjection, aWorldView, theViewportWidth, theViewportHeight);
392
393   return anUnviewMat * (aProjection * aWorldView);
394 }
395
396 #endif // _Graphic3d_TransformPers_HeaderFile