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