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