b5ac8292 |
1 | // Created on: 2013-05-29 |
2 | // Created by: Anton POLETAEV |
3 | // Copyright (c) 1999-2014 OPEN CASCADE SAS |
4 | // |
5 | // This file is part of Open CASCADE Technology software library. |
6 | // |
d5f74e42 |
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 |
b5ac8292 |
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 | #include <gp_Pln.hxx> |
17 | #include <Standard_ShortReal.hxx> |
18 | |
b5ac8292 |
19 | #include <Graphic3d_Camera.hxx> |
825aa485 |
20 | #include <Graphic3d_Vec4.hxx> |
21 | #include <Graphic3d_WorldViewProjState.hxx> |
b5ac8292 |
22 | |
23 | #include <Standard_Atomic.hxx> |
197ac94e |
24 | #include <Standard_Assert.hxx> |
b5ac8292 |
25 | |
ed063270 |
26 | #include <NCollection_Sequence.hxx> |
27 | |
b5ac8292 |
28 | |
29 | namespace |
30 | { |
31 | // (degrees -> radians) * 0.5 |
197ac94e |
32 | static const Standard_Real DTR_HALF = 0.5 * 0.0174532925; |
33 | |
34 | // default property values |
35 | static const Standard_Real DEFAULT_ZNEAR = 0.001; |
36 | static const Standard_Real DEFAULT_ZFAR = 3000.0; |
b5ac8292 |
37 | |
38 | // atomic state counter |
39 | static volatile Standard_Integer THE_STATE_COUNTER = 0; |
3c648527 |
40 | |
41 | // minimum camera distance |
42 | static const Standard_Real MIN_DISTANCE = Pow (0.1, ShortRealDigits() - 2); |
ea764884 |
43 | |
44 | // z-range tolerance compatible with for floating point. |
45 | static Standard_Real zEpsilon() |
46 | { |
47 | return FLT_EPSILON; |
48 | } |
49 | |
50 | // relative z-range tolerance compatible with for floating point. |
51 | static Standard_Real zEpsilon (const Standard_Real theValue) |
52 | { |
5c8908e0 |
53 | Standard_Real anAbsValue = Abs (theValue); |
54 | if (anAbsValue <= (double)FLT_MIN) |
825aa485 |
55 | { |
5c8908e0 |
56 | return FLT_MIN; |
825aa485 |
57 | } |
5c8908e0 |
58 | Standard_Real aLogRadix = Log10 (anAbsValue) / Log10 (FLT_RADIX); |
ea764884 |
59 | Standard_Real aExp = Floor (aLogRadix); |
60 | return FLT_EPSILON * Pow (FLT_RADIX, aExp); |
61 | }; |
b5ac8292 |
62 | }; |
63 | |
64 | // ======================================================================= |
65 | // function : Graphic3d_Camera |
66 | // purpose : |
67 | // ======================================================================= |
68 | Graphic3d_Camera::Graphic3d_Camera() |
69 | : myUp (0.0, 1.0, 0.0), |
70 | myEye (0.0, 0.0, -1500.0), |
71 | myCenter (0.0, 0.0, 0.0), |
b5ac8292 |
72 | myAxialScale (1.0, 1.0, 1.0), |
73 | myProjType (Projection_Orthographic), |
74 | myFOVy (45.0), |
197ac94e |
75 | myZNear (DEFAULT_ZNEAR), |
76 | myZFar (DEFAULT_ZFAR), |
b5ac8292 |
77 | myAspect (1.0), |
78 | myScale (1000.0), |
79 | myZFocus (1.0), |
80 | myZFocusType (FocusType_Relative), |
81 | myIOD (0.05), |
197ac94e |
82 | myIODType (IODType_Relative) |
b5ac8292 |
83 | { |
825aa485 |
84 | myWorldViewProjState.Initialize ((Standard_Size)Standard_Atomic_Increment (&THE_STATE_COUNTER), |
85 | (Standard_Size)Standard_Atomic_Increment (&THE_STATE_COUNTER), |
86 | this); |
b5ac8292 |
87 | } |
88 | |
89 | // ======================================================================= |
90 | // function : Graphic3d_Camera |
91 | // purpose : |
92 | // ======================================================================= |
93 | Graphic3d_Camera::Graphic3d_Camera (const Handle(Graphic3d_Camera)& theOther) |
b5ac8292 |
94 | { |
825aa485 |
95 | myWorldViewProjState.Initialize (this); |
96 | |
b5ac8292 |
97 | Copy (theOther); |
98 | } |
99 | |
100 | // ======================================================================= |
101 | // function : CopyMappingData |
102 | // purpose : |
103 | // ======================================================================= |
104 | void Graphic3d_Camera::CopyMappingData (const Handle(Graphic3d_Camera)& theOtherCamera) |
105 | { |
825aa485 |
106 | myFOVy = theOtherCamera->myFOVy; |
107 | myZNear = theOtherCamera->myZNear; |
108 | myZFar = theOtherCamera->myZFar; |
109 | myAspect = theOtherCamera->myAspect; |
110 | myScale = theOtherCamera->myScale; |
111 | myZFocus = theOtherCamera->myZFocus; |
112 | myZFocusType = theOtherCamera->myZFocusType; |
113 | myIOD = theOtherCamera->myIOD; |
114 | myIODType = theOtherCamera->myIODType; |
115 | myProjType = theOtherCamera->myProjType; |
116 | |
117 | myWorldViewProjState.ProjectionState() = theOtherCamera->ProjectionState(); |
b5ac8292 |
118 | |
197ac94e |
119 | InvalidateProjection(); |
b5ac8292 |
120 | } |
121 | |
122 | // ======================================================================= |
123 | // function : CopyOrientationData |
124 | // purpose : |
125 | // ======================================================================= |
126 | void Graphic3d_Camera::CopyOrientationData (const Handle(Graphic3d_Camera)& theOtherCamera) |
127 | { |
825aa485 |
128 | myUp = theOtherCamera->myUp; |
129 | myEye = theOtherCamera->myEye; |
130 | myCenter = theOtherCamera->myCenter; |
131 | myAxialScale = theOtherCamera->myAxialScale; |
132 | |
133 | myWorldViewProjState.WorldViewState() = theOtherCamera->WorldViewState(); |
b5ac8292 |
134 | |
197ac94e |
135 | InvalidateOrientation(); |
b5ac8292 |
136 | } |
137 | |
138 | // ======================================================================= |
139 | // function : Copy |
140 | // purpose : |
141 | // ======================================================================= |
142 | void Graphic3d_Camera::Copy (const Handle(Graphic3d_Camera)& theOther) |
143 | { |
b5ac8292 |
144 | CopyMappingData (theOther); |
145 | CopyOrientationData (theOther); |
b5ac8292 |
146 | } |
147 | |
148 | // ======================================================================= |
149 | // function : SetEye |
150 | // purpose : |
151 | // ======================================================================= |
152 | void Graphic3d_Camera::SetEye (const gp_Pnt& theEye) |
153 | { |
154 | myEye = theEye; |
197ac94e |
155 | InvalidateOrientation(); |
b5ac8292 |
156 | } |
157 | |
158 | // ======================================================================= |
159 | // function : SetCenter |
160 | // purpose : |
161 | // ======================================================================= |
162 | void Graphic3d_Camera::SetCenter (const gp_Pnt& theCenter) |
163 | { |
164 | myCenter = theCenter; |
197ac94e |
165 | InvalidateOrientation(); |
b5ac8292 |
166 | } |
167 | |
168 | // ======================================================================= |
169 | // function : SetUp |
170 | // purpose : |
171 | // ======================================================================= |
172 | void Graphic3d_Camera::SetUp (const gp_Dir& theUp) |
173 | { |
174 | myUp = theUp; |
197ac94e |
175 | InvalidateOrientation(); |
b5ac8292 |
176 | } |
177 | |
178 | // ======================================================================= |
179 | // function : SetAxialScale |
180 | // purpose : |
181 | // ======================================================================= |
197ac94e |
182 | void Graphic3d_Camera::SetAxialScale (const gp_XYZ& theAxialScale) |
b5ac8292 |
183 | { |
184 | myAxialScale = theAxialScale; |
197ac94e |
185 | InvalidateOrientation(); |
b5ac8292 |
186 | } |
187 | |
188 | // ======================================================================= |
189 | // function : SetDistance |
190 | // purpose : |
191 | // ======================================================================= |
192 | void Graphic3d_Camera::SetDistance (const Standard_Real theDistance) |
193 | { |
194 | gp_Vec aCenter2Eye (Direction()); |
195 | aCenter2Eye.Reverse(); |
3c648527 |
196 | |
197 | // Camera should have non-zero distance. |
198 | aCenter2Eye.Scale (Max (theDistance, MIN_DISTANCE)); |
b5ac8292 |
199 | SetEye (Center().Translated (aCenter2Eye)); |
200 | } |
201 | |
202 | // ======================================================================= |
203 | // function : Distance |
204 | // purpose : |
205 | // ======================================================================= |
206 | Standard_Real Graphic3d_Camera::Distance() const |
207 | { |
208 | return myEye.Distance (myCenter); |
209 | } |
210 | |
211 | // ======================================================================= |
212 | // function : SetDirection |
213 | // purpose : |
214 | // ======================================================================= |
215 | void Graphic3d_Camera::SetDirection (const gp_Dir& theDir) |
216 | { |
217 | gp_Vec aScaledDir (theDir); |
218 | aScaledDir.Scale (Distance()); |
219 | aScaledDir.Reverse(); |
220 | SetEye (Center().Translated (aScaledDir)); |
221 | } |
222 | |
223 | // ======================================================================= |
224 | // function : Direction |
225 | // purpose : |
226 | // ======================================================================= |
227 | gp_Dir Graphic3d_Camera::Direction() const |
228 | { |
229 | return gp_Dir (gp_Vec (myEye, myCenter)); |
230 | } |
231 | |
232 | // ======================================================================= |
233 | // function : SetScale |
234 | // purpose : |
235 | // ======================================================================= |
236 | void Graphic3d_Camera::SetScale (const Standard_Real theScale) |
237 | { |
c357e426 |
238 | if (Scale() == theScale) |
239 | { |
240 | return; |
241 | } |
242 | |
b5ac8292 |
243 | myScale = theScale; |
244 | |
245 | switch (myProjType) |
246 | { |
247 | case Projection_Perspective : |
248 | case Projection_Stereo : |
249 | case Projection_MonoLeftEye : |
250 | case Projection_MonoRightEye : |
251 | { |
252 | Standard_Real aDistance = theScale * 0.5 / Tan(myFOVy * M_PI / 360.0); |
253 | SetDistance (aDistance); |
254 | } |
255 | |
256 | default : |
257 | break; |
258 | } |
259 | |
197ac94e |
260 | InvalidateProjection(); |
b5ac8292 |
261 | } |
262 | |
263 | // ======================================================================= |
264 | // function : Scale |
265 | // purpose : |
266 | // ======================================================================= |
267 | Standard_Real Graphic3d_Camera::Scale() const |
268 | { |
269 | switch (myProjType) |
270 | { |
271 | case Projection_Orthographic : |
272 | return myScale; |
273 | |
274 | // case Projection_Perspective : |
275 | // case Projection_Stereo : |
276 | // case Projection_MonoLeftEye : |
277 | // case Projection_MonoRightEye : |
278 | default : |
197ac94e |
279 | return Distance() * 2.0 * Tan (myFOVy * M_PI / 360.0); |
b5ac8292 |
280 | } |
281 | } |
282 | |
283 | // ======================================================================= |
284 | // function : SetProjectionType |
285 | // purpose : |
286 | // ======================================================================= |
287 | void Graphic3d_Camera::SetProjectionType (const Projection theProjectionType) |
288 | { |
c357e426 |
289 | Projection anOldType = ProjectionType(); |
b5ac8292 |
290 | |
291 | if (anOldType == theProjectionType) |
292 | { |
293 | return; |
294 | } |
295 | |
197ac94e |
296 | if (anOldType == Projection_Orthographic) |
b5ac8292 |
297 | { |
197ac94e |
298 | if (myZNear <= RealEpsilon()) |
299 | { |
300 | myZNear = DEFAULT_ZNEAR; |
301 | } |
302 | if (myZFar <= RealEpsilon()) |
303 | { |
304 | myZFar = DEFAULT_ZFAR; |
305 | } |
b5ac8292 |
306 | } |
307 | |
197ac94e |
308 | myProjType = theProjectionType; |
309 | |
310 | InvalidateProjection(); |
b5ac8292 |
311 | } |
312 | |
313 | // ======================================================================= |
314 | // function : SetFOVy |
315 | // purpose : |
316 | // ======================================================================= |
317 | void Graphic3d_Camera::SetFOVy (const Standard_Real theFOVy) |
318 | { |
c357e426 |
319 | if (FOVy() == theFOVy) |
320 | { |
321 | return; |
322 | } |
323 | |
b5ac8292 |
324 | myFOVy = theFOVy; |
c357e426 |
325 | |
197ac94e |
326 | InvalidateProjection(); |
b5ac8292 |
327 | } |
328 | |
329 | // ======================================================================= |
197ac94e |
330 | // function : SetZRange |
b5ac8292 |
331 | // purpose : |
332 | // ======================================================================= |
197ac94e |
333 | void Graphic3d_Camera::SetZRange (const Standard_Real theZNear, |
334 | const Standard_Real theZFar) |
b5ac8292 |
335 | { |
197ac94e |
336 | Standard_ASSERT_RAISE (theZFar > theZNear, "ZFar should be greater than ZNear"); |
337 | if (!IsOrthographic()) |
b5ac8292 |
338 | { |
197ac94e |
339 | Standard_ASSERT_RAISE (theZNear > 0.0, "Only positive Z-Near is allowed for perspective camera"); |
340 | Standard_ASSERT_RAISE (theZFar > 0.0, "Only positive Z-Far is allowed for perspective camera"); |
b5ac8292 |
341 | } |
342 | |
c357e426 |
343 | if (ZNear() == theZNear |
344 | && ZFar () == theZFar) |
345 | { |
346 | return; |
347 | } |
348 | |
197ac94e |
349 | myZNear = theZNear; |
350 | myZFar = theZFar; |
b5ac8292 |
351 | |
197ac94e |
352 | InvalidateProjection(); |
b5ac8292 |
353 | } |
354 | |
355 | // ======================================================================= |
356 | // function : SetAspect |
357 | // purpose : |
358 | // ======================================================================= |
359 | void Graphic3d_Camera::SetAspect (const Standard_Real theAspect) |
360 | { |
c357e426 |
361 | if (Aspect() == theAspect) |
362 | { |
363 | return; |
364 | } |
365 | |
b5ac8292 |
366 | myAspect = theAspect; |
c357e426 |
367 | |
197ac94e |
368 | InvalidateProjection(); |
b5ac8292 |
369 | } |
370 | |
371 | // ======================================================================= |
372 | // function : SetZFocus |
373 | // purpose : |
374 | // ======================================================================= |
375 | void Graphic3d_Camera::SetZFocus(const FocusType theType, const Standard_Real theZFocus) |
376 | { |
c357e426 |
377 | if (ZFocusType() == theType |
378 | && ZFocus () == theZFocus) |
379 | { |
380 | return; |
381 | } |
382 | |
b5ac8292 |
383 | myZFocusType = theType; |
c357e426 |
384 | myZFocus = theZFocus; |
385 | |
197ac94e |
386 | InvalidateProjection(); |
b5ac8292 |
387 | } |
388 | |
389 | // ======================================================================= |
390 | // function : SetIOD |
391 | // purpose : |
392 | // ======================================================================= |
393 | void Graphic3d_Camera::SetIOD (const IODType theType, const Standard_Real theIOD) |
394 | { |
c357e426 |
395 | if (IODType() == theType |
396 | && IOD () == theIOD) |
397 | { |
398 | return; |
399 | } |
400 | |
b5ac8292 |
401 | myIODType = theType; |
c357e426 |
402 | myIOD = theIOD; |
403 | |
197ac94e |
404 | InvalidateProjection(); |
b5ac8292 |
405 | } |
406 | |
407 | // ======================================================================= |
408 | // function : OrthogonalizeUp |
409 | // purpose : |
410 | // ======================================================================= |
411 | void Graphic3d_Camera::OrthogonalizeUp() |
412 | { |
197ac94e |
413 | SetUp (OrthogonalizedUp()); |
b5ac8292 |
414 | } |
415 | |
416 | // ======================================================================= |
197ac94e |
417 | // function : OrthogonalizedUp |
b5ac8292 |
418 | // purpose : |
419 | // ======================================================================= |
197ac94e |
420 | gp_Dir Graphic3d_Camera::OrthogonalizedUp() const |
b5ac8292 |
421 | { |
197ac94e |
422 | gp_Dir aDir = Direction(); |
423 | gp_Dir aLeft = aDir.Crossed (Up()); |
b5ac8292 |
424 | |
197ac94e |
425 | // recompute up as: up = left x direction |
426 | return aLeft.Crossed (aDir); |
b5ac8292 |
427 | } |
428 | |
429 | // ======================================================================= |
430 | // function : Transform |
431 | // purpose : |
432 | // ======================================================================= |
433 | void Graphic3d_Camera::Transform (const gp_Trsf& theTrsf) |
434 | { |
435 | myUp.Transform (theTrsf); |
436 | myEye.Transform (theTrsf); |
437 | myCenter.Transform (theTrsf); |
197ac94e |
438 | InvalidateOrientation(); |
b5ac8292 |
439 | } |
440 | |
441 | // ======================================================================= |
442 | // function : safePointCast |
443 | // purpose : |
444 | // ======================================================================= |
197ac94e |
445 | static Graphic3d_Vec4d safePointCast (const gp_Pnt& thePnt) |
b5ac8292 |
446 | { |
447 | Standard_Real aLim = 1e15f; |
197ac94e |
448 | |
b5ac8292 |
449 | // have to deal with values greater then max float |
450 | gp_Pnt aSafePoint = thePnt; |
451 | const Standard_Real aBigFloat = aLim * 0.1f; |
452 | if (Abs (aSafePoint.X()) > aLim) |
453 | aSafePoint.SetX (aSafePoint.X() >= 0 ? aBigFloat : -aBigFloat); |
454 | if (Abs (aSafePoint.Y()) > aLim) |
455 | aSafePoint.SetY (aSafePoint.Y() >= 0 ? aBigFloat : -aBigFloat); |
456 | if (Abs (aSafePoint.Z()) > aLim) |
457 | aSafePoint.SetZ (aSafePoint.Z() >= 0 ? aBigFloat : -aBigFloat); |
458 | |
459 | // convert point |
197ac94e |
460 | Graphic3d_Vec4d aPnt (aSafePoint.X(), aSafePoint.Y(), aSafePoint.Z(), 1.0); |
b5ac8292 |
461 | |
462 | return aPnt; |
463 | } |
464 | |
465 | // ======================================================================= |
466 | // function : Project |
467 | // purpose : |
468 | // ======================================================================= |
469 | gp_Pnt Graphic3d_Camera::Project (const gp_Pnt& thePnt) const |
470 | { |
197ac94e |
471 | const Graphic3d_Mat4d& aViewMx = OrientationMatrix(); |
472 | const Graphic3d_Mat4d& aProjMx = ProjectionMatrix(); |
b5ac8292 |
473 | |
474 | // use compatible type of point |
197ac94e |
475 | Graphic3d_Vec4d aPnt = safePointCast (thePnt); |
b5ac8292 |
476 | |
477 | aPnt = aViewMx * aPnt; // convert to view coordinate space |
478 | aPnt = aProjMx * aPnt; // convert to projection coordinate space |
479 | |
480 | const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w()); |
481 | |
482 | return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW); |
483 | } |
484 | |
485 | // ======================================================================= |
486 | // function : UnProject |
487 | // purpose : |
488 | // ======================================================================= |
489 | gp_Pnt Graphic3d_Camera::UnProject (const gp_Pnt& thePnt) const |
490 | { |
197ac94e |
491 | const Graphic3d_Mat4d& aViewMx = OrientationMatrix(); |
492 | const Graphic3d_Mat4d& aProjMx = ProjectionMatrix(); |
b5ac8292 |
493 | |
197ac94e |
494 | Graphic3d_Mat4d aInvView; |
495 | Graphic3d_Mat4d aInvProj; |
b5ac8292 |
496 | |
497 | // this case should never happen |
498 | if (!aViewMx.Inverted (aInvView) || !aProjMx.Inverted (aInvProj)) |
499 | { |
500 | return gp_Pnt (0.0, 0.0, 0.0); |
501 | } |
502 | |
503 | // use compatible type of point |
197ac94e |
504 | Graphic3d_Vec4d aPnt = safePointCast (thePnt); |
b5ac8292 |
505 | |
506 | aPnt = aInvProj * aPnt; // convert to view coordinate space |
507 | aPnt = aInvView * aPnt; // convert to world coordinate space |
508 | |
509 | const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w()); |
510 | |
511 | return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW); |
512 | } |
513 | |
514 | // ======================================================================= |
515 | // function : ConvertView2Proj |
516 | // purpose : |
517 | // ======================================================================= |
518 | gp_Pnt Graphic3d_Camera::ConvertView2Proj (const gp_Pnt& thePnt) const |
519 | { |
197ac94e |
520 | const Graphic3d_Mat4d& aProjMx = ProjectionMatrix(); |
b5ac8292 |
521 | |
522 | // use compatible type of point |
197ac94e |
523 | Graphic3d_Vec4d aPnt = safePointCast (thePnt); |
b5ac8292 |
524 | |
525 | aPnt = aProjMx * aPnt; // convert to projection coordinate space |
526 | |
527 | const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w()); |
528 | |
529 | return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW); |
530 | } |
531 | |
532 | // ======================================================================= |
533 | // function : ConvertProj2View |
534 | // purpose : |
535 | // ======================================================================= |
536 | gp_Pnt Graphic3d_Camera::ConvertProj2View (const gp_Pnt& thePnt) const |
537 | { |
197ac94e |
538 | const Graphic3d_Mat4d& aProjMx = ProjectionMatrix(); |
b5ac8292 |
539 | |
197ac94e |
540 | Graphic3d_Mat4d aInvProj; |
b5ac8292 |
541 | |
542 | // this case should never happen, but... |
543 | if (!aProjMx.Inverted (aInvProj)) |
544 | { |
197ac94e |
545 | return gp_Pnt (0, 0, 0); |
b5ac8292 |
546 | } |
547 | |
548 | // use compatible type of point |
197ac94e |
549 | Graphic3d_Vec4d aPnt = safePointCast (thePnt); |
b5ac8292 |
550 | |
551 | aPnt = aInvProj * aPnt; // convert to view coordinate space |
552 | |
553 | const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w()); |
554 | |
555 | return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW); |
556 | } |
557 | |
558 | // ======================================================================= |
559 | // function : ConvertWorld2View |
560 | // purpose : |
561 | // ======================================================================= |
562 | gp_Pnt Graphic3d_Camera::ConvertWorld2View (const gp_Pnt& thePnt) const |
563 | { |
197ac94e |
564 | const Graphic3d_Mat4d& aViewMx = OrientationMatrix(); |
b5ac8292 |
565 | |
566 | // use compatible type of point |
197ac94e |
567 | Graphic3d_Vec4d aPnt = safePointCast (thePnt); |
b5ac8292 |
568 | |
569 | aPnt = aViewMx * aPnt; // convert to view coordinate space |
570 | |
571 | const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w()); |
572 | |
573 | return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW); |
574 | } |
575 | |
576 | // ======================================================================= |
577 | // function : ConvertView2World |
578 | // purpose : |
579 | // ======================================================================= |
580 | gp_Pnt Graphic3d_Camera::ConvertView2World (const gp_Pnt& thePnt) const |
581 | { |
197ac94e |
582 | const Graphic3d_Mat4d& aViewMx = OrientationMatrix(); |
b5ac8292 |
583 | |
197ac94e |
584 | Graphic3d_Mat4d aInvView; |
b5ac8292 |
585 | |
586 | if (!aViewMx.Inverted (aInvView)) |
587 | { |
588 | return gp_Pnt(0, 0, 0); |
589 | } |
590 | |
591 | // use compatible type of point |
197ac94e |
592 | Graphic3d_Vec4d aPnt = safePointCast (thePnt); |
b5ac8292 |
593 | |
594 | aPnt = aInvView * aPnt; // convert to world coordinate space |
595 | |
596 | const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w()); |
597 | |
598 | return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW); |
599 | } |
600 | |
601 | // ======================================================================= |
602 | // function : ViewDimensions |
603 | // purpose : |
604 | // ======================================================================= |
197ac94e |
605 | gp_XYZ Graphic3d_Camera::ViewDimensions() const |
b5ac8292 |
606 | { |
607 | // view plane dimensions |
539d3a1b |
608 | Standard_Real aSize = IsOrthographic() ? myScale : (2.0 * Distance() * Tan (DTR_HALF * myFOVy)); |
609 | Standard_Real aSizeX, aSizeY; |
610 | if (myAspect > 1.0) |
611 | { |
612 | aSizeX = aSize * myAspect; |
613 | aSizeY = aSize; |
614 | } |
615 | else |
616 | { |
617 | aSizeX = aSize; |
618 | aSizeY = aSize / myAspect; |
619 | } |
b5ac8292 |
620 | |
621 | // and frustum depth |
197ac94e |
622 | return gp_XYZ (aSizeX, aSizeY, myZFar - myZNear); |
b5ac8292 |
623 | } |
624 | |
625 | // ======================================================================= |
197ac94e |
626 | // function : Frustum |
b5ac8292 |
627 | // purpose : |
628 | // ======================================================================= |
197ac94e |
629 | void Graphic3d_Camera::Frustum (gp_Pln& theLeft, |
630 | gp_Pln& theRight, |
631 | gp_Pln& theBottom, |
632 | gp_Pln& theTop, |
633 | gp_Pln& theNear, |
634 | gp_Pln& theFar) const |
b5ac8292 |
635 | { |
197ac94e |
636 | gp_Vec aProjection = gp_Vec (Direction()); |
637 | gp_Vec anUp = OrthogonalizedUp(); |
638 | gp_Vec aSide = aProjection ^ anUp; |
639 | |
640 | Standard_ASSERT_RAISE ( |
641 | !aProjection.IsParallel (anUp, Precision::Angular()), |
642 | "Can not derive SIDE = PROJ x UP - directions are parallel"); |
643 | |
644 | theNear = gp_Pln (Eye().Translated (aProjection * ZNear()), aProjection); |
645 | theFar = gp_Pln (Eye().Translated (aProjection * ZFar()), -aProjection); |
646 | |
647 | Standard_Real aHScaleHor = Scale() * 0.5 * Aspect(); |
648 | Standard_Real aHScaleVer = Scale() * 0.5; |
649 | |
650 | gp_Pnt aPntLeft = Center().Translated (aHScaleHor * -aSide); |
651 | gp_Pnt aPntRight = Center().Translated (aHScaleHor * aSide); |
652 | gp_Pnt aPntBottom = Center().Translated (aHScaleVer * -anUp); |
653 | gp_Pnt aPntTop = Center().Translated (aHScaleVer * anUp); |
654 | |
655 | gp_Vec aDirLeft = aSide; |
656 | gp_Vec aDirRight = -aSide; |
657 | gp_Vec aDirBottom = anUp; |
658 | gp_Vec aDirTop = -anUp; |
659 | if (!IsOrthographic()) |
660 | { |
661 | Standard_Real aHFOVHor = ATan (Tan (DTR_HALF * FOVy()) * Aspect()); |
662 | Standard_Real aHFOVVer = DTR_HALF * FOVy(); |
663 | aDirLeft.Rotate (gp_Ax1 (gp::Origin(), anUp), aHFOVHor); |
664 | aDirRight.Rotate (gp_Ax1 (gp::Origin(), anUp), -aHFOVHor); |
665 | aDirBottom.Rotate (gp_Ax1 (gp::Origin(), aSide), -aHFOVVer); |
666 | aDirTop.Rotate (gp_Ax1 (gp::Origin(), aSide), aHFOVVer); |
667 | } |
668 | |
669 | theLeft = gp_Pln (aPntLeft, aDirLeft); |
670 | theRight = gp_Pln (aPntRight, aDirRight); |
671 | theBottom = gp_Pln (aPntBottom, aDirBottom); |
672 | theTop = gp_Pln (aPntTop, aDirTop); |
673 | } |
674 | |
675 | // ======================================================================= |
676 | // function : OrientationMatrix |
677 | // purpose : |
678 | // ======================================================================= |
679 | const Graphic3d_Mat4d& Graphic3d_Camera::OrientationMatrix() const |
680 | { |
681 | return *UpdateOrientation (myMatricesD).Orientation; |
682 | } |
683 | |
684 | // ======================================================================= |
685 | // function : OrientationMatrixF |
686 | // purpose : |
687 | // ======================================================================= |
688 | const Graphic3d_Mat4& Graphic3d_Camera::OrientationMatrixF() const |
689 | { |
690 | return *UpdateOrientation (myMatricesF).Orientation; |
691 | } |
692 | |
693 | // ======================================================================= |
694 | // function : ProjectionMatrix |
695 | // purpose : |
696 | // ======================================================================= |
697 | const Graphic3d_Mat4d& Graphic3d_Camera::ProjectionMatrix() const |
698 | { |
699 | return *UpdateProjection (myMatricesD).MProjection; |
700 | } |
701 | |
702 | // ======================================================================= |
703 | // function : ProjectionMatrixF |
704 | // purpose : |
705 | // ======================================================================= |
706 | const Graphic3d_Mat4& Graphic3d_Camera::ProjectionMatrixF() const |
707 | { |
708 | return *UpdateProjection (myMatricesF).MProjection; |
709 | } |
710 | |
711 | // ======================================================================= |
712 | // function : ProjectionStereoLeft |
713 | // purpose : |
714 | // ======================================================================= |
715 | const Graphic3d_Mat4d& Graphic3d_Camera::ProjectionStereoLeft() const |
716 | { |
717 | return *UpdateProjection (myMatricesD).LProjection; |
718 | } |
719 | |
720 | // ======================================================================= |
721 | // function : ProjectionStereoLeftF |
722 | // purpose : |
723 | // ======================================================================= |
724 | const Graphic3d_Mat4& Graphic3d_Camera::ProjectionStereoLeftF() const |
725 | { |
726 | return *UpdateProjection (myMatricesF).LProjection; |
727 | } |
728 | |
729 | // ======================================================================= |
730 | // function : ProjectionStereoRight |
731 | // purpose : |
732 | // ======================================================================= |
733 | const Graphic3d_Mat4d& Graphic3d_Camera::ProjectionStereoRight() const |
734 | { |
735 | return *UpdateProjection (myMatricesD).RProjection; |
736 | } |
737 | |
738 | // ======================================================================= |
739 | // function : ProjectionStereoRightF |
740 | // purpose : |
741 | // ======================================================================= |
742 | const Graphic3d_Mat4& Graphic3d_Camera::ProjectionStereoRightF() const |
743 | { |
744 | return *UpdateProjection (myMatricesF).RProjection; |
b5ac8292 |
745 | } |
746 | |
747 | // ======================================================================= |
748 | // function : UpdateProjection |
749 | // purpose : |
750 | // ======================================================================= |
197ac94e |
751 | template <typename Elem_t> |
752 | Graphic3d_Camera::TransformMatrices<Elem_t>& |
753 | Graphic3d_Camera::UpdateProjection (TransformMatrices<Elem_t>& theMatrices) const |
b5ac8292 |
754 | { |
197ac94e |
755 | if (theMatrices.IsProjectionValid()) |
b5ac8292 |
756 | { |
197ac94e |
757 | return theMatrices; // for inline accessors |
b5ac8292 |
758 | } |
759 | |
197ac94e |
760 | theMatrices.InitProjection(); |
b5ac8292 |
761 | |
762 | // sets top of frustum based on FOVy and near clipping plane |
197ac94e |
763 | Elem_t aScale = static_cast<Elem_t> (myScale); |
764 | Elem_t aZNear = static_cast<Elem_t> (myZNear); |
765 | Elem_t aZFar = static_cast<Elem_t> (myZFar); |
766 | Elem_t anAspect = static_cast<Elem_t> (myAspect); |
ab1c121b |
767 | Elem_t aDXHalf = 0.0, aDYHalf = 0.0; |
b5ac8292 |
768 | if (IsOrthographic()) |
769 | { |
ab1c121b |
770 | aDXHalf = aScale * Elem_t (0.5); |
197ac94e |
771 | aDYHalf = aScale * Elem_t (0.5); |
b5ac8292 |
772 | } |
773 | else |
774 | { |
ab1c121b |
775 | aDXHalf = aZNear * Elem_t (Tan (DTR_HALF * myFOVy)); |
197ac94e |
776 | aDYHalf = aZNear * Elem_t (Tan (DTR_HALF * myFOVy)); |
b5ac8292 |
777 | } |
778 | |
ab1c121b |
779 | if (anAspect > 1.0) |
780 | { |
781 | aDXHalf *= anAspect; |
782 | } |
783 | else |
784 | { |
785 | aDYHalf /= anAspect; |
786 | } |
787 | |
b5ac8292 |
788 | // sets right of frustum based on aspect ratio |
197ac94e |
789 | Elem_t aLeft = -aDXHalf; |
790 | Elem_t aRight = aDXHalf; |
791 | Elem_t aBot = -aDYHalf; |
792 | Elem_t aTop = aDYHalf; |
b5ac8292 |
793 | |
197ac94e |
794 | Elem_t aIOD = myIODType == IODType_Relative |
795 | ? static_cast<Elem_t> (myIOD * Distance()) |
796 | : static_cast<Elem_t> (myIOD); |
b5ac8292 |
797 | |
197ac94e |
798 | Elem_t aFocus = myZFocusType == FocusType_Relative |
799 | ? static_cast<Elem_t> (myZFocus * Distance()) |
800 | : static_cast<Elem_t> (myZFocus); |
b5ac8292 |
801 | |
802 | switch (myProjType) |
803 | { |
804 | case Projection_Orthographic : |
197ac94e |
805 | OrthoProj (aLeft, aRight, aBot, aTop, aZNear, aZFar, *theMatrices.MProjection); |
b5ac8292 |
806 | break; |
807 | |
808 | case Projection_Perspective : |
197ac94e |
809 | PerspectiveProj (aLeft, aRight, aBot, aTop, aZNear, aZFar, *theMatrices.MProjection); |
b5ac8292 |
810 | break; |
811 | |
812 | case Projection_MonoLeftEye : |
813 | { |
197ac94e |
814 | StereoEyeProj (aLeft, aRight, aBot, aTop, |
815 | aZNear, aZFar, aIOD, aFocus, |
816 | Standard_True, *theMatrices.MProjection); |
38a0206f |
817 | *theMatrices.LProjection = *theMatrices.MProjection; |
b5ac8292 |
818 | break; |
819 | } |
820 | |
821 | case Projection_MonoRightEye : |
822 | { |
197ac94e |
823 | StereoEyeProj (aLeft, aRight, aBot, aTop, |
824 | aZNear, aZFar, aIOD, aFocus, |
825 | Standard_False, *theMatrices.MProjection); |
38a0206f |
826 | *theMatrices.RProjection = *theMatrices.MProjection; |
b5ac8292 |
827 | break; |
828 | } |
829 | |
830 | case Projection_Stereo : |
831 | { |
197ac94e |
832 | PerspectiveProj (aLeft, aRight, aBot, aTop, aZNear, aZFar, *theMatrices.MProjection); |
b5ac8292 |
833 | |
197ac94e |
834 | StereoEyeProj (aLeft, aRight, aBot, aTop, |
835 | aZNear, aZFar, aIOD, aFocus, |
836 | Standard_True, |
837 | *theMatrices.LProjection); |
b5ac8292 |
838 | |
197ac94e |
839 | StereoEyeProj (aLeft, aRight, aBot, aTop, |
840 | aZNear, aZFar, aIOD, aFocus, |
841 | Standard_False, |
842 | *theMatrices.RProjection); |
b5ac8292 |
843 | break; |
844 | } |
845 | } |
197ac94e |
846 | |
847 | return theMatrices; // for inline accessors |
b5ac8292 |
848 | } |
849 | |
850 | // ======================================================================= |
851 | // function : UpdateOrientation |
852 | // purpose : |
853 | // ======================================================================= |
197ac94e |
854 | template <typename Elem_t> |
855 | Graphic3d_Camera::TransformMatrices<Elem_t>& |
856 | Graphic3d_Camera::UpdateOrientation (TransformMatrices<Elem_t>& theMatrices) const |
b5ac8292 |
857 | { |
197ac94e |
858 | if (theMatrices.IsOrientationValid()) |
b5ac8292 |
859 | { |
197ac94e |
860 | return theMatrices; // for inline accessors |
b5ac8292 |
861 | } |
862 | |
197ac94e |
863 | theMatrices.InitOrientation(); |
864 | |
865 | NCollection_Vec3<Elem_t> anEye (static_cast<Elem_t> (myEye.X()), |
866 | static_cast<Elem_t> (myEye.Y()), |
867 | static_cast<Elem_t> (myEye.Z())); |
868 | |
869 | NCollection_Vec3<Elem_t> aCenter (static_cast<Elem_t> (myCenter.X()), |
870 | static_cast<Elem_t> (myCenter.Y()), |
871 | static_cast<Elem_t> (myCenter.Z())); |
b5ac8292 |
872 | |
197ac94e |
873 | NCollection_Vec3<Elem_t> anUp (static_cast<Elem_t> (myUp.X()), |
874 | static_cast<Elem_t> (myUp.Y()), |
875 | static_cast<Elem_t> (myUp.Z())); |
b5ac8292 |
876 | |
197ac94e |
877 | NCollection_Vec3<Elem_t> anAxialScale (static_cast<Elem_t> (myAxialScale.X()), |
878 | static_cast<Elem_t> (myAxialScale.Y()), |
879 | static_cast<Elem_t> (myAxialScale.Z())); |
b5ac8292 |
880 | |
197ac94e |
881 | LookOrientation (anEye, aCenter, anUp, anAxialScale, *theMatrices.Orientation); |
b5ac8292 |
882 | |
197ac94e |
883 | return theMatrices; // for inline accessors |
884 | } |
b5ac8292 |
885 | |
197ac94e |
886 | // ======================================================================= |
887 | // function : InvalidateProjection |
888 | // purpose : |
889 | // ======================================================================= |
890 | void Graphic3d_Camera::InvalidateProjection() |
891 | { |
892 | myMatricesD.ResetProjection(); |
893 | myMatricesF.ResetProjection(); |
825aa485 |
894 | myWorldViewProjState.ProjectionState() = (Standard_Size)Standard_Atomic_Increment (&THE_STATE_COUNTER); |
197ac94e |
895 | } |
b5ac8292 |
896 | |
197ac94e |
897 | // ======================================================================= |
898 | // function : InvalidateOrientation |
899 | // purpose : |
900 | // ======================================================================= |
901 | void Graphic3d_Camera::InvalidateOrientation() |
902 | { |
903 | myMatricesD.ResetOrientation(); |
904 | myMatricesF.ResetOrientation(); |
825aa485 |
905 | myWorldViewProjState.WorldViewState() = (Standard_Size)Standard_Atomic_Increment (&THE_STATE_COUNTER); |
b5ac8292 |
906 | } |
907 | |
908 | // ======================================================================= |
909 | // function : OrthoProj |
910 | // purpose : |
911 | // ======================================================================= |
197ac94e |
912 | template <typename Elem_t> |
913 | void Graphic3d_Camera::OrthoProj (const Elem_t theLeft, |
914 | const Elem_t theRight, |
915 | const Elem_t theBottom, |
916 | const Elem_t theTop, |
917 | const Elem_t theNear, |
918 | const Elem_t theFar, |
919 | NCollection_Mat4<Elem_t>& theOutMx) |
b5ac8292 |
920 | { |
921 | // row 0 |
197ac94e |
922 | theOutMx.ChangeValue (0, 0) = Elem_t (2.0) / (theRight - theLeft); |
923 | theOutMx.ChangeValue (0, 1) = Elem_t (0.0); |
924 | theOutMx.ChangeValue (0, 2) = Elem_t (0.0); |
b5ac8292 |
925 | theOutMx.ChangeValue (0, 3) = - (theRight + theLeft) / (theRight - theLeft); |
926 | |
927 | // row 1 |
197ac94e |
928 | theOutMx.ChangeValue (1, 0) = Elem_t (0.0); |
929 | theOutMx.ChangeValue (1, 1) = Elem_t (2.0) / (theTop - theBottom); |
930 | theOutMx.ChangeValue (1, 2) = Elem_t (0.0); |
b5ac8292 |
931 | theOutMx.ChangeValue (1, 3) = - (theTop + theBottom) / (theTop - theBottom); |
932 | |
933 | // row 2 |
197ac94e |
934 | theOutMx.ChangeValue (2, 0) = Elem_t (0.0); |
935 | theOutMx.ChangeValue (2, 1) = Elem_t (0.0); |
936 | theOutMx.ChangeValue (2, 2) = Elem_t (-2.0) / (theFar - theNear); |
b5ac8292 |
937 | theOutMx.ChangeValue (2, 3) = - (theFar + theNear) / (theFar - theNear); |
938 | |
939 | // row 3 |
197ac94e |
940 | theOutMx.ChangeValue (3, 0) = Elem_t (0.0); |
941 | theOutMx.ChangeValue (3, 1) = Elem_t (0.0); |
942 | theOutMx.ChangeValue (3, 2) = Elem_t (0.0); |
943 | theOutMx.ChangeValue (3, 3) = Elem_t (1.0); |
b5ac8292 |
944 | } |
945 | |
946 | // ======================================================================= |
947 | // function : PerspectiveProj |
948 | // purpose : |
949 | // ======================================================================= |
197ac94e |
950 | template <typename Elem_t> |
951 | void Graphic3d_Camera::PerspectiveProj (const Elem_t theLeft, |
952 | const Elem_t theRight, |
953 | const Elem_t theBottom, |
954 | const Elem_t theTop, |
955 | const Elem_t theNear, |
956 | const Elem_t theFar, |
957 | NCollection_Mat4<Elem_t>& theOutMx) |
b5ac8292 |
958 | { |
959 | // column 0 |
197ac94e |
960 | theOutMx.ChangeValue (0, 0) = (Elem_t (2.0) * theNear) / (theRight - theLeft); |
961 | theOutMx.ChangeValue (1, 0) = Elem_t (0.0); |
962 | theOutMx.ChangeValue (2, 0) = Elem_t (0.0); |
963 | theOutMx.ChangeValue (3, 0) = Elem_t (0.0); |
b5ac8292 |
964 | |
965 | // column 1 |
197ac94e |
966 | theOutMx.ChangeValue (0, 1) = Elem_t (0.0); |
967 | theOutMx.ChangeValue (1, 1) = (Elem_t (2.0) * theNear) / (theTop - theBottom); |
968 | theOutMx.ChangeValue (2, 1) = Elem_t (0.0); |
969 | theOutMx.ChangeValue (3, 1) = Elem_t (0.0); |
b5ac8292 |
970 | |
971 | // column 2 |
972 | theOutMx.ChangeValue (0, 2) = (theRight + theLeft) / (theRight - theLeft); |
973 | theOutMx.ChangeValue (1, 2) = (theTop + theBottom) / (theTop - theBottom); |
974 | theOutMx.ChangeValue (2, 2) = -(theFar + theNear) / (theFar - theNear); |
197ac94e |
975 | theOutMx.ChangeValue (3, 2) = Elem_t (-1.0); |
b5ac8292 |
976 | |
977 | // column 3 |
197ac94e |
978 | theOutMx.ChangeValue (0, 3) = Elem_t (0.0); |
979 | theOutMx.ChangeValue (1, 3) = Elem_t (0.0); |
980 | theOutMx.ChangeValue (2, 3) = -(Elem_t (2.0) * theFar * theNear) / (theFar - theNear); |
981 | theOutMx.ChangeValue (3, 3) = Elem_t (0.0); |
b5ac8292 |
982 | } |
983 | |
984 | // ======================================================================= |
985 | // function : StereoEyeProj |
986 | // purpose : |
987 | // ======================================================================= |
197ac94e |
988 | template <typename Elem_t> |
989 | void Graphic3d_Camera::StereoEyeProj (const Elem_t theLeft, |
990 | const Elem_t theRight, |
991 | const Elem_t theBottom, |
992 | const Elem_t theTop, |
993 | const Elem_t theNear, |
994 | const Elem_t theFar, |
995 | const Elem_t theIOD, |
996 | const Elem_t theZFocus, |
997 | const Standard_Boolean theIsLeft, |
998 | NCollection_Mat4<Elem_t>& theOutMx) |
b5ac8292 |
999 | { |
197ac94e |
1000 | Elem_t aDx = theIsLeft ? Elem_t (0.5) * theIOD : Elem_t (-0.5) * theIOD; |
1001 | Elem_t aDXStereoShift = aDx * theNear / theZFocus; |
b5ac8292 |
1002 | |
1003 | // construct eye projection matrix |
1004 | PerspectiveProj (theLeft + aDXStereoShift, |
1005 | theRight + aDXStereoShift, |
1006 | theBottom, theTop, theNear, theFar, |
b5ac8292 |
1007 | theOutMx); |
1008 | |
197ac94e |
1009 | if (theIOD != Elem_t (0.0)) |
b5ac8292 |
1010 | { |
1011 | // X translation to cancel parallax |
197ac94e |
1012 | theOutMx.Translate (NCollection_Vec3<Elem_t> (aDx, Elem_t (0.0), Elem_t (0.0))); |
b5ac8292 |
1013 | } |
1014 | } |
1015 | |
1016 | // ======================================================================= |
1017 | // function : LookOrientation |
1018 | // purpose : |
1019 | // ======================================================================= |
197ac94e |
1020 | template <typename Elem_t> |
1021 | void Graphic3d_Camera::LookOrientation (const NCollection_Vec3<Elem_t>& theEye, |
1022 | const NCollection_Vec3<Elem_t>& theLookAt, |
1023 | const NCollection_Vec3<Elem_t>& theUpDir, |
1024 | const NCollection_Vec3<Elem_t>& theAxialScale, |
1025 | NCollection_Mat4<Elem_t>& theOutMx) |
b5ac8292 |
1026 | { |
197ac94e |
1027 | NCollection_Vec3<Elem_t> aForward = theLookAt - theEye; |
b5ac8292 |
1028 | aForward.Normalize(); |
1029 | |
1030 | // side = forward x up |
197ac94e |
1031 | NCollection_Vec3<Elem_t> aSide = NCollection_Vec3<Elem_t>::Cross (aForward, theUpDir); |
b5ac8292 |
1032 | aSide.Normalize(); |
1033 | |
1034 | // recompute up as: up = side x forward |
197ac94e |
1035 | NCollection_Vec3<Elem_t> anUp = NCollection_Vec3<Elem_t>::Cross (aSide, aForward); |
b5ac8292 |
1036 | |
197ac94e |
1037 | NCollection_Mat4<Elem_t> aLookMx; |
b5ac8292 |
1038 | aLookMx.SetRow (0, aSide); |
1039 | aLookMx.SetRow (1, anUp); |
1040 | aLookMx.SetRow (2, -aForward); |
1041 | |
197ac94e |
1042 | theOutMx.InitIdentity(); |
1043 | theOutMx.Multiply (aLookMx); |
b5ac8292 |
1044 | theOutMx.Translate (-theEye); |
1045 | |
197ac94e |
1046 | NCollection_Mat4<Elem_t> anAxialScaleMx; |
b5ac8292 |
1047 | anAxialScaleMx.ChangeValue (0, 0) = theAxialScale.x(); |
1048 | anAxialScaleMx.ChangeValue (1, 1) = theAxialScale.y(); |
1049 | anAxialScaleMx.ChangeValue (2, 2) = theAxialScale.z(); |
1050 | |
1051 | theOutMx.Multiply (anAxialScaleMx); |
1052 | } |
6bc6a6fc |
1053 | |
1054 | //============================================================================= |
1055 | //function : ZFitAll |
1056 | //purpose : |
1057 | //============================================================================= |
10dbdf34 |
1058 | bool Graphic3d_Camera::ZFitAll (const Standard_Real theScaleFactor, |
1059 | const Bnd_Box& theMinMax, |
1060 | const Bnd_Box& theGraphicBB, |
1061 | Standard_Real& theZNear, |
1062 | Standard_Real& theZFar) const |
6bc6a6fc |
1063 | { |
1064 | Standard_ASSERT_RAISE (theScaleFactor > 0.0, "Zero or negative scale factor is not allowed."); |
1065 | |
ea764884 |
1066 | // Method changes zNear and zFar parameters of camera so as to fit graphical structures |
1067 | // by their graphical boundaries. It precisely fits min max boundaries of primary application |
1068 | // objects (second argument), while it can sacrifice the real graphical boundaries of the |
1069 | // scene with infinite or helper objects (third argument) for the sake of perspective projection. |
ed063270 |
1070 | if (theGraphicBB.IsVoid()) |
6bc6a6fc |
1071 | { |
10dbdf34 |
1072 | theZNear = DEFAULT_ZNEAR; |
1073 | theZFar = DEFAULT_ZFAR; |
1074 | return false; |
6bc6a6fc |
1075 | } |
1076 | |
ea764884 |
1077 | // Measure depth of boundary points from camera eye. |
ed063270 |
1078 | NCollection_Sequence<gp_Pnt> aPntsToMeasure; |
1079 | |
ea764884 |
1080 | Standard_Real aGraphicBB[6]; |
ed063270 |
1081 | theGraphicBB.Get (aGraphicBB[0], aGraphicBB[1], aGraphicBB[2], aGraphicBB[3], aGraphicBB[4], aGraphicBB[5]); |
1082 | |
1083 | aPntsToMeasure.Append (gp_Pnt (aGraphicBB[0], aGraphicBB[1], aGraphicBB[2])); |
1084 | aPntsToMeasure.Append (gp_Pnt (aGraphicBB[0], aGraphicBB[1], aGraphicBB[5])); |
1085 | aPntsToMeasure.Append (gp_Pnt (aGraphicBB[0], aGraphicBB[4], aGraphicBB[2])); |
1086 | aPntsToMeasure.Append (gp_Pnt (aGraphicBB[0], aGraphicBB[4], aGraphicBB[5])); |
1087 | aPntsToMeasure.Append (gp_Pnt (aGraphicBB[3], aGraphicBB[1], aGraphicBB[2])); |
1088 | aPntsToMeasure.Append (gp_Pnt (aGraphicBB[3], aGraphicBB[1], aGraphicBB[5])); |
1089 | aPntsToMeasure.Append (gp_Pnt (aGraphicBB[3], aGraphicBB[4], aGraphicBB[2])); |
1090 | aPntsToMeasure.Append (gp_Pnt (aGraphicBB[3], aGraphicBB[4], aGraphicBB[5])); |
1091 | |
5a19c303 |
1092 | Standard_Boolean isFiniteMinMax = !theMinMax.IsVoid() && !theMinMax.IsWhole(); |
1093 | |
1094 | if (isFiniteMinMax) |
6bc6a6fc |
1095 | { |
ea764884 |
1096 | Standard_Real aMinMax[6]; |
ed063270 |
1097 | theMinMax.Get (aMinMax[0], aMinMax[1], aMinMax[2], aMinMax[3], aMinMax[4], aMinMax[5]); |
1098 | |
1099 | aPntsToMeasure.Append (gp_Pnt (aMinMax[0], aMinMax[1], aMinMax[2])); |
1100 | aPntsToMeasure.Append (gp_Pnt (aMinMax[0], aMinMax[1], aMinMax[5])); |
1101 | aPntsToMeasure.Append (gp_Pnt (aMinMax[0], aMinMax[4], aMinMax[2])); |
1102 | aPntsToMeasure.Append (gp_Pnt (aMinMax[0], aMinMax[4], aMinMax[5])); |
1103 | aPntsToMeasure.Append (gp_Pnt (aMinMax[3], aMinMax[1], aMinMax[2])); |
1104 | aPntsToMeasure.Append (gp_Pnt (aMinMax[3], aMinMax[1], aMinMax[5])); |
1105 | aPntsToMeasure.Append (gp_Pnt (aMinMax[3], aMinMax[4], aMinMax[2])); |
1106 | aPntsToMeasure.Append (gp_Pnt (aMinMax[3], aMinMax[4], aMinMax[5])); |
1107 | } |
6bc6a6fc |
1108 | |
ea764884 |
1109 | // Camera eye plane. |
6bc6a6fc |
1110 | gp_Dir aCamDir = Direction(); |
1111 | gp_Pnt aCamEye = myEye; |
1112 | gp_Pln aCamPln (aCamEye, aCamDir); |
1113 | |
5a19c303 |
1114 | Standard_Real aModelMinDist = RealLast(); |
1115 | Standard_Real aModelMaxDist = RealFirst(); |
1116 | Standard_Real aGraphMinDist = RealLast(); |
1117 | Standard_Real aGraphMaxDist = RealFirst(); |
6bc6a6fc |
1118 | |
1119 | const gp_XYZ& anAxialScale = myAxialScale; |
1120 | |
ea764884 |
1121 | // Get minimum and maximum distances to the eye plane. |
ed063270 |
1122 | Standard_Integer aCounter = 0; |
1123 | NCollection_Sequence<gp_Pnt>::Iterator aPntIt(aPntsToMeasure); |
1124 | for (; aPntIt.More(); aPntIt.Next()) |
6bc6a6fc |
1125 | { |
ed063270 |
1126 | gp_Pnt aMeasurePnt = aPntIt.Value(); |
6bc6a6fc |
1127 | |
1128 | aMeasurePnt = gp_Pnt (aMeasurePnt.X() * anAxialScale.X(), |
ed063270 |
1129 | aMeasurePnt.Y() * anAxialScale.Y(), |
1130 | aMeasurePnt.Z() * anAxialScale.Z()); |
6bc6a6fc |
1131 | |
1132 | Standard_Real aDistance = aCamPln.Distance (aMeasurePnt); |
1133 | |
ea764884 |
1134 | // Check if the camera is intruded into the scene. |
6bc6a6fc |
1135 | if (aCamDir.IsOpposite (gp_Vec (aCamEye, aMeasurePnt), M_PI * 0.5)) |
1136 | { |
1137 | aDistance *= -1; |
1138 | } |
1139 | |
ea764884 |
1140 | // The first eight points are from theGraphicBB, the last eight points are from theMinMax (can be absent). |
5a19c303 |
1141 | Standard_Real& aChangeMinDist = aCounter >= 8 ? aModelMinDist : aGraphMinDist; |
1142 | Standard_Real& aChangeMaxDist = aCounter >= 8 ? aModelMaxDist : aGraphMaxDist; |
6bc6a6fc |
1143 | aChangeMinDist = Min (aDistance, aChangeMinDist); |
1144 | aChangeMaxDist = Max (aDistance, aChangeMaxDist); |
ed063270 |
1145 | aCounter++; |
6bc6a6fc |
1146 | } |
1147 | |
ea764884 |
1148 | // Compute depth of bounding box center. |
5a19c303 |
1149 | Standard_Real aMidDepth = (aGraphMinDist + aGraphMaxDist) * 0.5; |
1150 | Standard_Real aHalfDepth = (aGraphMaxDist - aGraphMinDist) * 0.5; |
6bc6a6fc |
1151 | |
ea764884 |
1152 | // Compute enlarged or shrank near and far z ranges. |
35c4a17c |
1153 | Standard_Real aZNear = aMidDepth - aHalfDepth * theScaleFactor; |
1154 | Standard_Real aZFar = aMidDepth + aHalfDepth * theScaleFactor; |
6bc6a6fc |
1155 | |
1156 | if (!IsOrthographic()) |
1157 | { |
ea764884 |
1158 | // Everything is behind the perspective camera. |
1159 | if (aZFar < zEpsilon()) |
6bc6a6fc |
1160 | { |
10dbdf34 |
1161 | theZNear = DEFAULT_ZNEAR; |
1162 | theZFar = DEFAULT_ZFAR; |
1163 | return false; |
6bc6a6fc |
1164 | } |
6bc6a6fc |
1165 | } |
1166 | |
ea764884 |
1167 | // |
1168 | // Consider clipping errors due to double to single precision floating-point conversion. |
1169 | // |
1170 | |
1171 | // Model to view transformation performs translation of points against eye position |
1172 | // in three dimensions. Both point coordinate and eye position values are converted from |
1173 | // double to single precision floating point numbers producing conversion errors. |
1174 | // Epsilon (Mod) * 3.0 should safely compensate precision error for z coordinate after |
1175 | // translation assuming that the: |
1176 | // Epsilon (Eye.Mod()) * 3.0 > Epsilon (Eye.X()) + Epsilon (Eye.Y()) + Epsilon (Eye.Z()). |
1177 | Standard_Real aEyeConf = 3.0 * zEpsilon (myEye.XYZ().Modulus()); |
1178 | |
1179 | // Model to view transformation performs rotation of points according to view direction. |
1180 | // New z coordinate is computed as a multiplication of point's x, y, z coordinates by the |
1181 | // "forward" direction vector's x, y, z coordinates. Both point's and "z" direction vector's |
1182 | // values are converted from double to single precision floating point numbers producing |
1183 | // conversion errors. |
1184 | // Epsilon (Mod) * 6.0 should safely compensate the precision errors for the multiplication |
1185 | // of point coordinates by direction vector. |
1186 | gp_Pnt aGraphicMin = theGraphicBB.CornerMin(); |
1187 | gp_Pnt aGraphicMax = theGraphicBB.CornerMax(); |
1188 | |
1189 | Standard_Real aModelConf = 6.0 * zEpsilon (aGraphicMin.XYZ().Modulus()) + |
1190 | 6.0 * zEpsilon (aGraphicMax.XYZ().Modulus()); |
1191 | |
1192 | // Compensate floating point conversion errors by increasing zNear, zFar to avoid clipping. |
1193 | aZNear -= zEpsilon (aZNear) + aEyeConf + aModelConf; |
1194 | aZFar += zEpsilon (aZFar) + aEyeConf + aModelConf; |
1195 | |
1196 | if (!IsOrthographic()) |
6bc6a6fc |
1197 | { |
5a19c303 |
1198 | // For perspective projection, the value of z in normalized device coordinates is non-linear |
1199 | // function of eye z coordinate. For fixed-point depth representation resolution of z in |
1200 | // model-view space will grow towards zFar plane and its scale depends mostly on how far is zNear |
1201 | // against camera's eye. The purpose of the code below is to select most appropriate zNear distance |
1202 | // to balance between clipping (less zNear, more chances to observe closely small models without clipping) |
1203 | // and resolution of depth. A well applicable criteria to this is a ratio between resolution of z at center |
1204 | // of model boundaries and the distance to that center point. The ratio is chosen empirically and validated |
1205 | // by tests database. It is considered to be ~0.001 (0.1%) for 24 bit depth buffer, for less depth bitness |
1206 | // the zNear will be placed similarly giving lower resolution. |
1207 | // Approximation of the formula for respectively large z range is: |
1208 | // zNear = [z * (1 + k) / (k * c)], |
1209 | // where: |
1210 | // z - distance to center of model boundaries; |
1211 | // k - chosen ratio, c - capacity of depth buffer; |
1212 | // k = 0.001, k * c = 1677.216, (1 + k) / (k * c) ~ 5.97E-4 |
1213 | // |
1214 | // The function uses center of model boundaries computed from "theMinMax" boundaries (instead of using real |
1215 | // graphical boundaries of all displayed objects). That means that it can sacrifice resolution of presentation |
1216 | // of non primary ("infinite") application graphical objects in favor of better perspective projection of the |
1217 | // small applicative objects measured with "theMinMax" values. |
1218 | Standard_Real aZRange = isFiniteMinMax ? aModelMaxDist - aModelMinDist : aGraphMaxDist - aGraphMinDist; |
1219 | Standard_Real aZMin = isFiniteMinMax ? aModelMinDist : aGraphMinDist; |
1220 | Standard_Real aZ = aZMin < 0 ? aZRange / 2.0 : aZRange / 2.0 + aZMin; |
1221 | Standard_Real aZNearMin = aZ * 5.97E-4; |
1222 | if (aZNear < aZNearMin) |
1223 | { |
1224 | // Clip zNear according to the minimum value matching the quality. |
1225 | aZNear = aZNearMin; |
1226 | } |
1227 | else |
1228 | { |
1229 | // Compensate zNear conversion errors for perspective projection. |
1230 | aZNear -= aZFar * zEpsilon (aZNear) / (aZFar - zEpsilon (aZNear)); |
1231 | } |
1232 | |
1233 | // Compensate zFar conversion errors for perspective projection. |
1234 | aZFar += zEpsilon (aZFar); |
ea764884 |
1235 | |
1236 | // Ensure that after all the zNear is not a negative value. |
1237 | if (aZNear < zEpsilon()) |
1238 | { |
1239 | aZNear = zEpsilon(); |
1240 | } |
6bc6a6fc |
1241 | } |
1242 | |
10dbdf34 |
1243 | theZNear = aZNear; |
1244 | theZFar = aZFar; |
1245 | return true; |
6bc6a6fc |
1246 | } |