0027536: Visualization - incorrect behavior of zoom persisted objects
[occt.git] / src / Graphic3d / Graphic3d_TransformPers.hxx
CommitLineData
825aa485 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_TransformUtils.hxx>
22#include <Graphic3d_TransModeFlags.hxx>
23#include <NCollection_Mat4.hxx>
24#include <NCollection_Vec4.hxx>
25
26//! Class for keeping and computing transformation persistence.
27class Graphic3d_TransformPers
28{
29public:
30
31 DEFINE_STANDARD_ALLOC
32
33 //! Default constructor.
34 Graphic3d_TransformPers()
35 : Flags (Graphic3d_TMF_None),
36 Point (0.0, 0.0, 0.0) {}
37
38 //! Transformation persistence mode flags.
39 Graphic3d_TransModeFlags Flags;
40
41 //! Reference point for transformation.
42 Graphic3d_Vec3d Point;
43
44public:
45
46 //! Apply transformation to bounding box of presentation.
47 //! @param theProjection [in] the projection transformation matrix.
48 //! @param theWorldView [in] the world view transformation matrix.
49 //! @param theViewportWidth [in] the width of viewport (for 2d persistence).
50 //! @param theViewportHeight [in] the height of viewport (for 2d persistence).
51 //! @param theBoundingBox [in/out] the bounding box to transform.
52 template<class T>
53 void Apply (const NCollection_Mat4<T>& theProjection,
54 const NCollection_Mat4<T>& theWorldView,
55 const Standard_Integer theViewportWidth,
56 const Standard_Integer theViewportHeight,
57 Bnd_Box& theBoundingBox) const;
58
59 //! Apply transformation to bounding box of presentation
60 //! @param theProjection [in] the projection transformation matrix.
61 //! @param theWorldView [in] the world view transformation matrix.
62 //! @param theViewportWidth [in] the width of viewport (for 2d persistence).
63 //! @param theViewportHeight [in] the height of viewport (for 2d persistence).
64 //! @param theBoundingBox [in/out] the bounding box to transform.
65 template<class T>
66 void Apply (const NCollection_Mat4<T>& theProjection,
67 const NCollection_Mat4<T>& theWorldView,
68 const Standard_Integer theViewportWidth,
69 const Standard_Integer theViewportHeight,
70 BVH_Box<T, 4>& theBoundingBox) const;
71
72 //! Compute transformation.
73 //! Computed matrix can be applied to model world transformation
74 //! of an object to implement effect of transformation persistence.
75 //! @param theProjection [in] the projection transformation matrix.
76 //! @param theWorldView [in] the world view transformation matrix.
77 //! @param theViewportWidth [in] the width of viewport (for 2d persistence).
78 //! @param theViewportHeight [in] the height of viewport (for 2d persistence).
79 //! @return transformation matrix to be applied to model world transformation of an object.
80 template<class T>
81 NCollection_Mat4<T> Compute (const NCollection_Mat4<T>& theProjection,
82 const NCollection_Mat4<T>& theWorldView,
83 const Standard_Integer theViewportWidth,
84 const Standard_Integer theViewportHeight) const;
85
86 template<class T>
87 void Apply (NCollection_Mat4<T>& theProjection,
88 NCollection_Mat4<T>& theWorldView,
89 const Standard_Integer theViewportWidth,
90 const Standard_Integer theViewportHeight) const;
91};
92
93// =======================================================================
94// function : Apply
95// purpose : Apply transformation to world view and projection matrices.
96// =======================================================================
97template<class T>
98void Graphic3d_TransformPers::Apply (NCollection_Mat4<T>& theProjection,
99 NCollection_Mat4<T>& theWorldView,
100 const Standard_Integer theViewportWidth,
101 const Standard_Integer theViewportHeight) const
102{
103 if (!Flags)
104 {
105 return;
106 }
107
108 if (Flags & Graphic3d_TMF_2d)
109 {
110 T aLeft = -static_cast<T> (theViewportWidth / 2);
111 T aRight = static_cast<T> (theViewportWidth / 2);
112 T aBottom = -static_cast<T> (theViewportHeight / 2);
113 T aTop = static_cast<T> (theViewportHeight / 2);
114 T aGap = static_cast<T> (Point.z());
115 if (Point.x() > 0)
116 {
117 aLeft -= static_cast<T> (theViewportWidth / 2) - aGap;
118 aRight -= static_cast<T> (theViewportWidth / 2) - aGap;
119 }
120 else if (Point.x() < 0)
121 {
122 aLeft += static_cast<T> (theViewportWidth / 2) - aGap;
123 aRight += static_cast<T> (theViewportWidth / 2) - aGap;
124 }
125 if (Point.y() > 0)
126 {
127 aBottom -= static_cast<T> (theViewportHeight / 2) - aGap;
128 aTop -= static_cast<T> (theViewportHeight / 2) - aGap;
129 }
130 else if (Point.y() < 0)
131 {
132 aBottom += static_cast<T> (theViewportHeight / 2) - aGap;
133 aTop += static_cast<T> (theViewportHeight / 2) - aGap;
134 }
135 if (Flags == Graphic3d_TMF_2d_IsTopDown)
136 {
137 const T aTemp = aTop;
138 aTop = aBottom;
139 aBottom = aTemp;
140 }
141
142 Graphic3d_TransformUtils::Ortho2D<T> (theProjection, aLeft, aRight, aBottom, aTop);
143
144 theWorldView.InitIdentity();
145 }
146 else
147 {
148 // Compute reference point for transformation in untransformed projection space.
149 NCollection_Vec4<T> aRefPoint (static_cast<T> (Point.x()),
150 static_cast<T> (Point.y()),
151 static_cast<T> (Point.z()),
152 static_cast<T> (1.0));
153 NCollection_Vec4<T> aRefPointProj;
154 if ((Flags & Graphic3d_TMF_PanPers) != Graphic3d_TMF_PanPers)
155 {
156 aRefPointProj = theProjection * (theWorldView * aRefPoint);
157 aRefPointProj /= aRefPointProj.w();
158 }
159
160 // Prevent zooming.
91d96372 161 if (Flags & Graphic3d_TMF_ZoomPers)
162 {
163 const T aDet00 = (2.0f / theViewportWidth) / theProjection.GetValue(0, 0);
164 const T aDet11 = (2.0f / theViewportHeight) / theProjection.GetValue(1, 1);
165 const T aDet2 = Max (aDet00, aDet11);
166
167 theProjection.ChangeValue(0, 0) *= aDet00;
168 theProjection.ChangeValue(1, 1) *= aDet11;
169 theProjection.ChangeValue(2, 2) *= aDet2;
170 }
171
172 if (Flags == Graphic3d_TMF_TriedronPers)
825aa485 173 {
174 // Compute fixed-zoom multiplier. Actually function works ugly with TelPerspective!
175 const T aDet2 = static_cast<T> (0.002) / Max (theProjection.GetValue (1, 1), theProjection.GetValue (0, 0));
91d96372 176
825aa485 177 theProjection.ChangeValue (0, 0) *= aDet2;
178 theProjection.ChangeValue (1, 1) *= aDet2;
179 theProjection.ChangeValue (2, 2) *= aDet2;
180 }
181
182 // Prevent translation by nullifying translation component.
183 if ((Flags & Graphic3d_TMF_PanPers) || Flags == Graphic3d_TMF_TriedronPers)
184 {
185 theWorldView .SetValue (0, 3, static_cast<T> (0.0));
186 theWorldView .SetValue (1, 3, static_cast<T> (0.0));
187 theWorldView .SetValue (2, 3, static_cast<T> (0.0));
188 theProjection.SetValue (0, 3, static_cast<T> (0.0));
189 theProjection.SetValue (1, 3, static_cast<T> (0.0));
190 theProjection.SetValue (2, 3, static_cast<T> (0.0));
191 }
192
193 // Prevent scaling-on-axis.
194 if (Flags & Graphic3d_TMF_ZoomPers)
195 {
196 NCollection_Vec3<T> aVecX = theWorldView.GetColumn (0).xyz();
197 NCollection_Vec3<T> aVecY = theWorldView.GetColumn (1).xyz();
198 NCollection_Vec3<T> aVecZ = theWorldView.GetColumn (2).xyz();
199 T aScaleX = aVecX.Modulus();
200 T aScaleY = aVecY.Modulus();
201 T aScaleZ = aVecZ.Modulus();
202 for (Standard_Integer anI = 0; anI < 3; ++anI)
203 {
204 theWorldView.ChangeValue (0, anI) /= aScaleX;
205 theWorldView.ChangeValue (1, anI) /= aScaleY;
206 theWorldView.ChangeValue (2, anI) /= aScaleZ;
207 }
208 }
209
210 // Prevent rotation by nullifying rotation component.
211 if (Flags & Graphic3d_TMF_RotatePers)
212 {
213 theWorldView.SetValue (0, 0, static_cast<T> (1.0));
214 theWorldView.SetValue (1, 0, static_cast<T> (0.0));
215 theWorldView.SetValue (2, 0, static_cast<T> (0.0));
216
217 theWorldView.SetValue (0, 1, static_cast<T> (0.0));
218 theWorldView.SetValue (1, 1, static_cast<T> (1.0));
219 theWorldView.SetValue (2, 1, static_cast<T> (0.0));
220
221 theWorldView.SetValue (0, 2, static_cast<T> (0.0));
222 theWorldView.SetValue (1, 2, static_cast<T> (0.0));
223 theWorldView.SetValue (2, 2, static_cast<T> (1.0));
224 }
225
226 if (Flags == Graphic3d_TMF_TriedronPers)
227 {
228 if (Point.x() != 0.0 && Point.y() != 0.0)
229 {
230 NCollection_Mat4<T> anUnviewMat;
231
232 if (!(theProjection).Inverted (anUnviewMat))
233 {
234 Standard_ProgramError::Raise ("Graphic3d_TransformPers::Apply, can not inverse projection matrix.");
235 }
236
237 NCollection_Vec4<T> aProjMax (static_cast<T> ( 1.0), static_cast<T> ( 1.0), static_cast<T> (0.0), static_cast<T> (1.0));
238 NCollection_Vec4<T> aProjMin (static_cast<T> (-1.0), static_cast<T> (-1.0), static_cast<T> (0.0), static_cast<T> (1.0));
239 NCollection_Vec4<T> aViewMax = anUnviewMat * aProjMax;
240 NCollection_Vec4<T> aViewMin = anUnviewMat * aProjMin;
241
242 aViewMax /= aViewMax.w();
243 aViewMin /= aViewMin.w();
244
245 T aMoveX = static_cast<T> (0.5) * (aViewMax.x() - aViewMin.x() - static_cast<T> (Point.z()));
246 T aMoveY = static_cast<T> (0.5) * (aViewMax.y() - aViewMin.y() - static_cast<T> (Point.z()));
247
248 aMoveX = (Point.x() > 0.0) ? aMoveX : -aMoveX;
249 aMoveY = (Point.y() > 0.0) ? aMoveY : -aMoveY;
250
251 Graphic3d_TransformUtils::Translate<T> (theProjection, aMoveX, aMoveY, static_cast<T> (0.0));
252 }
253 }
254 else if ((Flags & Graphic3d_TMF_PanPers) != Graphic3d_TMF_PanPers)
255 {
256 NCollection_Mat4<T> anUnviewMat;
257
258 if (!(theProjection * theWorldView).Inverted (anUnviewMat))
259 {
260 Standard_ProgramError::Raise ("Graphic3d_TransformPers::Apply, can not inverse world view projection matrix.");
261 }
262
263 // Move to reference point location in transformed view projection space.
264 aRefPoint = anUnviewMat * aRefPointProj;
265 aRefPoint /= aRefPoint.w();
266
267 Graphic3d_TransformUtils::Translate<T> (theWorldView, aRefPoint.x(), aRefPoint.y(), aRefPoint.z());
268 }
269 }
270}
271
272// =======================================================================
273// function : Apply
274// purpose : Apply transformation to bounding box of presentation.
275// =======================================================================
276template<class T>
277void Graphic3d_TransformPers::Apply (const NCollection_Mat4<T>& theProjection,
278 const NCollection_Mat4<T>& theWorldView,
279 const Standard_Integer theViewportWidth,
280 const Standard_Integer theViewportHeight,
281 Bnd_Box& theBoundingBox) const
282{
283 T aXmin, aYmin, aZmin, aXmax, aYmax, aZmax;
284
285 theBoundingBox.Get (aXmin, aYmin, aZmin, aXmax, aYmax, aZmax);
286
287 typename BVH_Box<T, 4>::BVH_VecNt aMin (aXmin, aYmin, aZmin, static_cast<T> (1.0));
288 typename BVH_Box<T, 4>::BVH_VecNt aMax (aXmax, aYmax, aZmax, static_cast<T> (1.0));
289 BVH_Box<T, 4> aBBox (aMin, aMax);
290
291 Apply (theProjection, theWorldView, theViewportWidth, theViewportHeight, aBBox);
292
293 theBoundingBox = Bnd_Box();
294 theBoundingBox.Update (aBBox.CornerMin().x(), aBBox.CornerMin().y(), aBBox.CornerMin().z(),
295 aBBox.CornerMax().x(), aBBox.CornerMax().y(), aBBox.CornerMax().z());
296}
297
298// =======================================================================
299// function : Apply
300// purpose : Apply transformation to bounding box of presentation.
301// =======================================================================
302template<class T>
303void Graphic3d_TransformPers::Apply (const NCollection_Mat4<T>& theProjection,
304 const NCollection_Mat4<T>& theWorldView,
305 const Standard_Integer theViewportWidth,
306 const Standard_Integer theViewportHeight,
307 BVH_Box<T, 4>& theBoundingBox) const
308{
309 NCollection_Mat4<T> aTPers = Compute (theProjection, theWorldView, theViewportWidth, theViewportHeight);
310
311 if (aTPers.IsIdentity())
312 {
313 return;
314 }
315
316 const typename BVH_Box<T, 4>::BVH_VecNt& aMin = theBoundingBox.CornerMin();
317 const typename BVH_Box<T, 4>::BVH_VecNt& aMax = theBoundingBox.CornerMax();
318
319 typename BVH_Box<T, 4>::BVH_VecNt anArrayOfCorners[8];
320 anArrayOfCorners[0] = typename BVH_Box<T, 4>::BVH_VecNt (aMin.x(), aMin.y(), aMin.z(), static_cast<T> (1.0));
321 anArrayOfCorners[1] = typename BVH_Box<T, 4>::BVH_VecNt (aMin.x(), aMin.y(), aMax.z(), static_cast<T> (1.0));
322 anArrayOfCorners[2] = typename BVH_Box<T, 4>::BVH_VecNt (aMin.x(), aMax.y(), aMin.z(), static_cast<T> (1.0));
323 anArrayOfCorners[3] = typename BVH_Box<T, 4>::BVH_VecNt (aMin.x(), aMax.y(), aMax.z(), static_cast<T> (1.0));
324 anArrayOfCorners[4] = typename BVH_Box<T, 4>::BVH_VecNt (aMax.x(), aMin.y(), aMin.z(), static_cast<T> (1.0));
325 anArrayOfCorners[5] = typename BVH_Box<T, 4>::BVH_VecNt (aMax.x(), aMin.y(), aMax.z(), static_cast<T> (1.0));
326 anArrayOfCorners[6] = typename BVH_Box<T, 4>::BVH_VecNt (aMax.x(), aMax.y(), aMin.z(), static_cast<T> (1.0));
327 anArrayOfCorners[7] = typename BVH_Box<T, 4>::BVH_VecNt (aMax.x(), aMax.y(), aMax.z(), static_cast<T> (1.0));
328
329 theBoundingBox.Clear();
330 for (Standard_Integer anIt = 0; anIt < 8; ++anIt)
331 {
332 typename BVH_Box<T, 4>::BVH_VecNt& aCorner = anArrayOfCorners[anIt];
333 aCorner = aTPers * aCorner;
334 aCorner /= aCorner.w();
335 theBoundingBox.Add (aCorner);
336 }
337}
338
339// =======================================================================
340// function : Compute
341// purpose : Compute transformation.
342// =======================================================================
343template<class T>
344NCollection_Mat4<T> Graphic3d_TransformPers::Compute (const NCollection_Mat4<T>& theProjection,
345 const NCollection_Mat4<T>& theWorldView,
346 const Standard_Integer theViewportWidth,
347 const Standard_Integer theViewportHeight) const
348{
349 if (Flags == Graphic3d_TMF_None)
350 {
351 return NCollection_Mat4<T>();
352 }
353
354 NCollection_Mat4<T> anUnviewMat;
355
356 if (!(theProjection * theWorldView).Inverted (anUnviewMat))
357 {
358 return NCollection_Mat4<T>();
359 }
360
361 NCollection_Mat4<T> aProjection (theProjection);
362 NCollection_Mat4<T> aWorldView (theWorldView);
363
364 Apply (aProjection, aWorldView, theViewportWidth, theViewportHeight);
365
366 return anUnviewMat * (aProjection * aWorldView);
367}
368
369#endif // _Graphic3d_TransformPers_HeaderFile