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 //
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 #include <Graphic3d_Camera.hxx>
17
18 #include <gp_Pln.hxx>
19 #include <gp_QuaternionNLerp.hxx>
20 #include <gp_QuaternionSLerp.hxx>
21 #include <Graphic3d_Vec4.hxx>
22 #include <Graphic3d_WorldViewProjState.hxx>
23 #include <NCollection_Sequence.hxx>
24 #include <Standard_ShortReal.hxx>
25 #include <Standard_Atomic.hxx>
26 #include <Standard_Assert.hxx>
27
28 IMPLEMENT_STANDARD_RTTIEXT(Graphic3d_Camera,Standard_Transient)
29
30 namespace
31 {
32 // (degrees -> radians) * 0.5
33 static const Standard_Real DTR_HALF = 0.5 * 0.0174532925;
34
35 // default property values
36 static const Standard_Real DEFAULT_ZNEAR = 0.001;
37 static const Standard_Real DEFAULT_ZFAR = 3000.0;
38
39 // atomic state counter
40 static volatile Standard_Integer THE_STATE_COUNTER = 0;
41
42 // z-range tolerance compatible with for floating point.
zEpsilon()43 static Standard_Real zEpsilon()
44 {
45 return FLT_EPSILON;
46 }
47
48 // relative z-range tolerance compatible with for floating point.
zEpsilon(const Standard_Real theValue)49 static Standard_Real zEpsilon (const Standard_Real theValue)
50 {
51 Standard_Real anAbsValue = Abs (theValue);
52 if (anAbsValue <= (double)FLT_MIN)
53 {
54 return FLT_MIN;
55 }
56 Standard_Real aLogRadix = Log10 (anAbsValue) / Log10 (FLT_RADIX);
57 Standard_Real aExp = Floor (aLogRadix);
58 return FLT_EPSILON * Pow (FLT_RADIX, aExp);
59 }
60
61 //! Convert camera definition to Ax3
cameraToAx3(const Graphic3d_Camera & theCamera)62 gp_Ax3 cameraToAx3 (const Graphic3d_Camera& theCamera)
63 {
64 const gp_Dir aBackDir = -theCamera.Direction();
65 const gp_Dir anXAxis (theCamera.Up().Crossed (aBackDir));
66 const gp_Dir anYAxis (aBackDir .Crossed (anXAxis));
67 const gp_Dir aZAxis (anXAxis .Crossed (anYAxis));
68 return gp_Ax3 (gp_Pnt (0.0, 0.0, 0.0), aZAxis, anXAxis);
69 }
70 }
71
72 // =======================================================================
73 // function : Graphic3d_Camera
74 // purpose :
75 // =======================================================================
Graphic3d_Camera()76 Graphic3d_Camera::Graphic3d_Camera()
77 : myUp (0.0, 1.0, 0.0),
78 myDirection (0.0, 0.0, 1.0),
79 myEye (0.0, 0.0, -1500.0),
80 myDistance (1500.0),
81 myAxialScale (1.0, 1.0, 1.0),
82 myProjType (Projection_Orthographic),
83 myFOVy (45.0),
84 myFOVx (45.0),
85 myFOV2d (180.0),
86 myFOVyTan (Tan (DTR_HALF * 45.0)),
87 myZNear (DEFAULT_ZNEAR),
88 myZFar (DEFAULT_ZFAR),
89 myAspect (1.0),
90 myIsZeroToOneDepth (false),
91 myScale (1000.0),
92 myZFocus (1.0),
93 myZFocusType (FocusType_Relative),
94 myIOD (0.05),
95 myIODType (IODType_Relative),
96 myIsCustomProjMatM (false),
97 myIsCustomProjMatLR(false),
98 myIsCustomFrustomLR(false)
99 {
100 myWorldViewProjState.Initialize ((Standard_Size)Standard_Atomic_Increment (&THE_STATE_COUNTER),
101 (Standard_Size)Standard_Atomic_Increment (&THE_STATE_COUNTER),
102 this);
103 }
104
105 // =======================================================================
106 // function : Graphic3d_Camera
107 // purpose :
108 // =======================================================================
Graphic3d_Camera(const Handle (Graphic3d_Camera)& theOther)109 Graphic3d_Camera::Graphic3d_Camera (const Handle(Graphic3d_Camera)& theOther)
110 : myUp (0.0, 1.0, 0.0),
111 myDirection (0.0, 0.0, 1.0),
112 myEye (0.0, 0.0, -1500.0),
113 myDistance (1500.0),
114 myAxialScale (1.0, 1.0, 1.0),
115 myProjType (Projection_Orthographic),
116 myFOVy (45.0),
117 myFOVx (45.0),
118 myFOV2d (180.0),
119 myFOVyTan (Tan (DTR_HALF * 45.0)),
120 myZNear (DEFAULT_ZNEAR),
121 myZFar (DEFAULT_ZFAR),
122 myAspect (1.0),
123 myIsZeroToOneDepth (false),
124 myScale (1000.0),
125 myZFocus (1.0),
126 myZFocusType (FocusType_Relative),
127 myIOD (0.05),
128 myIODType (IODType_Relative),
129 myIsCustomProjMatM (false),
130 myIsCustomProjMatLR(false),
131 myIsCustomFrustomLR(false)
132 {
133 myWorldViewProjState.Initialize (this);
134
135 Copy (theOther);
136 }
137
138 // =======================================================================
139 // function : CopyMappingData
140 // purpose :
141 // =======================================================================
CopyMappingData(const Handle (Graphic3d_Camera)& theOtherCamera)142 void Graphic3d_Camera::CopyMappingData (const Handle(Graphic3d_Camera)& theOtherCamera)
143 {
144 SetZeroToOneDepth (theOtherCamera->IsZeroToOneDepth());
145 SetProjectionType (theOtherCamera->ProjectionType());
146 SetFOVy (theOtherCamera->FOVy());
147 SetFOV2d (theOtherCamera->FOV2d());
148 SetZRange (theOtherCamera->ZNear(), theOtherCamera->ZFar());
149 SetAspect (theOtherCamera->Aspect());
150 SetScale (theOtherCamera->Scale());
151 SetZFocus (theOtherCamera->ZFocusType(), theOtherCamera->ZFocus());
152 SetIOD (theOtherCamera->GetIODType(), theOtherCamera->IOD());
153 SetTile (theOtherCamera->myTile);
154
155 ResetCustomProjection();
156 if (theOtherCamera->IsCustomStereoProjection())
157 {
158 SetCustomStereoProjection (theOtherCamera->myCustomProjMatL,
159 theOtherCamera->myCustomHeadToEyeMatL,
160 theOtherCamera->myCustomProjMatR,
161 theOtherCamera->myCustomHeadToEyeMatR);
162 }
163 else if (theOtherCamera->IsCustomStereoFrustum())
164 {
165 SetCustomStereoFrustums (theOtherCamera->myCustomFrustumL, theOtherCamera->myCustomFrustumR);
166 }
167 if (theOtherCamera->IsCustomMonoProjection())
168 {
169 SetCustomMonoProjection (theOtherCamera->myCustomProjMatM);
170 }
171 }
172
173 // =======================================================================
174 // function : CopyOrientationData
175 // purpose :
176 // =======================================================================
CopyOrientationData(const Handle (Graphic3d_Camera)& theOtherCamera)177 void Graphic3d_Camera::CopyOrientationData (const Handle(Graphic3d_Camera)& theOtherCamera)
178 {
179 if (!myEye.IsEqual (theOtherCamera->Eye(), 0.0)
180 || !myUp.IsEqual (theOtherCamera->Up(), 0.0)
181 || !myDirection.IsEqual (theOtherCamera->Direction(), 0.0)
182 || myDistance != theOtherCamera->Distance())
183 {
184 myEye = theOtherCamera->Eye();
185 myUp = theOtherCamera->Up();
186 myDirection = theOtherCamera->Direction();
187 myDistance = theOtherCamera->Distance();
188 InvalidateOrientation();
189 }
190 SetAxialScale (theOtherCamera->AxialScale());
191 }
192
193 // =======================================================================
194 // function : Copy
195 // purpose :
196 // =======================================================================
Copy(const Handle (Graphic3d_Camera)& theOther)197 void Graphic3d_Camera::Copy (const Handle(Graphic3d_Camera)& theOther)
198 {
199 CopyMappingData (theOther);
200 CopyOrientationData (theOther);
201 }
202
203 // =======================================================================
204 // function : SetIdentityOrientation
205 // purpose :
206 // =======================================================================
SetIdentityOrientation()207 void Graphic3d_Camera::SetIdentityOrientation()
208 {
209 SetEyeAndCenter (gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(0.0, 0.0, -1.0));
210 SetUp (gp_Dir(0.0, 1.0, 0.0));
211 }
212
213 // =======================================================================
214 // function : MoveEyeTo
215 // purpose :
216 // =======================================================================
MoveEyeTo(const gp_Pnt & theEye)217 void Graphic3d_Camera::MoveEyeTo (const gp_Pnt& theEye)
218 {
219 if (myEye.IsEqual (theEye, 0.0))
220 {
221 return;
222 }
223
224 myEye = theEye;
225 InvalidateOrientation();
226 }
227
228 // =======================================================================
229 // function : SetEyeAndCenter
230 // purpose :
231 // =======================================================================
SetEyeAndCenter(const gp_Pnt & theEye,const gp_Pnt & theCenter)232 void Graphic3d_Camera::SetEyeAndCenter (const gp_Pnt& theEye,
233 const gp_Pnt& theCenter)
234 {
235 if (Eye() .IsEqual (theEye, 0.0)
236 && Center().IsEqual (theCenter, 0.0))
237 {
238 return;
239 }
240
241 myEye = theEye;
242 myDistance = theEye.Distance (theCenter);
243 if (myDistance > gp::Resolution())
244 {
245 myDirection = gp_Dir (theCenter.XYZ() - theEye.XYZ());
246 }
247 InvalidateOrientation();
248 }
249
250 // =======================================================================
251 // function : SetEye
252 // purpose :
253 // =======================================================================
SetEye(const gp_Pnt & theEye)254 void Graphic3d_Camera::SetEye (const gp_Pnt& theEye)
255 {
256 if (Eye().IsEqual (theEye, 0.0))
257 {
258 return;
259 }
260
261 const gp_Pnt aCenter = Center();
262 myEye = theEye;
263 myDistance = myEye.Distance (aCenter);
264 if (myDistance > gp::Resolution())
265 {
266 myDirection = gp_Dir (aCenter.XYZ() - myEye.XYZ());
267 }
268 InvalidateOrientation();
269 }
270
271 // =======================================================================
272 // function : SetCenter
273 // purpose :
274 // =======================================================================
SetCenter(const gp_Pnt & theCenter)275 void Graphic3d_Camera::SetCenter (const gp_Pnt& theCenter)
276 {
277 const Standard_Real aDistance = myEye.Distance (theCenter);
278 if (myDistance == aDistance)
279 {
280 return;
281 }
282
283 myDistance = aDistance;
284 if (myDistance > gp::Resolution())
285 {
286 myDirection = gp_Dir (theCenter.XYZ() - myEye.XYZ());
287 }
288 InvalidateOrientation();
289 }
290
291 // =======================================================================
292 // function : SetUp
293 // purpose :
294 // =======================================================================
SetUp(const gp_Dir & theUp)295 void Graphic3d_Camera::SetUp (const gp_Dir& theUp)
296 {
297 if (Up().IsEqual (theUp, 0.0))
298 {
299 return;
300 }
301
302 myUp = theUp;
303 InvalidateOrientation();
304 }
305
306 // =======================================================================
307 // function : SetAxialScale
308 // purpose :
309 // =======================================================================
SetAxialScale(const gp_XYZ & theAxialScale)310 void Graphic3d_Camera::SetAxialScale (const gp_XYZ& theAxialScale)
311 {
312 if (AxialScale().IsEqual (theAxialScale, 0.0))
313 {
314 return;
315 }
316
317 myAxialScale = theAxialScale;
318 InvalidateOrientation();
319 }
320
321 // =======================================================================
322 // function : SetDistance
323 // purpose :
324 // =======================================================================
SetDistance(const Standard_Real theDistance)325 void Graphic3d_Camera::SetDistance (const Standard_Real theDistance)
326 {
327 if (myDistance == theDistance)
328 {
329 return;
330 }
331
332 const gp_Pnt aCenter = Center();
333 myDistance = theDistance;
334 myEye = aCenter.XYZ() - myDirection.XYZ() * myDistance;
335 InvalidateOrientation();
336 }
337
338 // =======================================================================
339 // function : SetDirectionFromEye
340 // purpose :
341 // =======================================================================
SetDirectionFromEye(const gp_Dir & theDir)342 void Graphic3d_Camera::SetDirectionFromEye (const gp_Dir& theDir)
343 {
344 if (myDirection.IsEqual (theDir, 0.0))
345 {
346 return;
347 }
348
349 myDirection = theDir;
350 InvalidateOrientation();
351 }
352
353 // =======================================================================
354 // function : SetDirection
355 // purpose :
356 // =======================================================================
SetDirection(const gp_Dir & theDir)357 void Graphic3d_Camera::SetDirection (const gp_Dir& theDir)
358 {
359 if (myDirection.IsEqual (theDir, 0.0))
360 {
361 return;
362 }
363
364 const gp_Pnt aCenter = Center();
365 myDirection = theDir;
366 myEye = aCenter.XYZ() - theDir.XYZ() * myDistance;
367 InvalidateOrientation();
368 }
369
370 // =======================================================================
371 // function : SetScale
372 // purpose :
373 // =======================================================================
SetScale(const Standard_Real theScale)374 void Graphic3d_Camera::SetScale (const Standard_Real theScale)
375 {
376 if (Scale() == theScale)
377 {
378 return;
379 }
380
381 myScale = theScale;
382
383 switch (myProjType)
384 {
385 case Projection_Perspective :
386 case Projection_Stereo :
387 case Projection_MonoLeftEye :
388 case Projection_MonoRightEye :
389 {
390 Standard_Real aDistance = theScale * 0.5 / myFOVyTan;
391 SetDistance (aDistance);
392 }
393
394 default :
395 break;
396 }
397
398 InvalidateProjection();
399 }
400
401 // =======================================================================
402 // function : Scale
403 // purpose :
404 // =======================================================================
Scale() const405 Standard_Real Graphic3d_Camera::Scale() const
406 {
407 switch (myProjType)
408 {
409 case Projection_Orthographic :
410 return myScale;
411
412 // case Projection_Perspective :
413 // case Projection_Stereo :
414 // case Projection_MonoLeftEye :
415 // case Projection_MonoRightEye :
416 default :
417 return Distance() * 2.0 * myFOVyTan;
418 }
419 }
420
421 // =======================================================================
422 // function : SetProjectionType
423 // purpose :
424 // =======================================================================
SetProjectionType(const Projection theProjectionType)425 void Graphic3d_Camera::SetProjectionType (const Projection theProjectionType)
426 {
427 Projection anOldType = ProjectionType();
428
429 if (anOldType == theProjectionType)
430 {
431 return;
432 }
433
434 if (anOldType == Projection_Orthographic)
435 {
436 if (myZNear <= RealEpsilon())
437 {
438 myZNear = DEFAULT_ZNEAR;
439 }
440 if (myZFar <= RealEpsilon())
441 {
442 myZFar = DEFAULT_ZFAR;
443 }
444 }
445
446 myProjType = theProjectionType;
447
448 InvalidateProjection();
449 }
450
451 // =======================================================================
452 // function : SetFOVy
453 // purpose :
454 // =======================================================================
SetFOVy(const Standard_Real theFOVy)455 void Graphic3d_Camera::SetFOVy (const Standard_Real theFOVy)
456 {
457 if (FOVy() == theFOVy)
458 {
459 return;
460 }
461
462 myFOVy = theFOVy;
463 myFOVx = theFOVy * myAspect;
464 myFOVyTan = Tan(DTR_HALF * myFOVy);
465
466 InvalidateProjection();
467 }
468
469 // =======================================================================
470 // function : SetFOV2d
471 // purpose :
472 // =======================================================================
SetFOV2d(const Standard_Real theFOV)473 void Graphic3d_Camera::SetFOV2d (const Standard_Real theFOV)
474 {
475 if (FOV2d() == theFOV)
476 {
477 return;
478 }
479
480 myFOV2d = theFOV;
481 InvalidateProjection();
482 }
483
484 // =======================================================================
485 // function : SetZRange
486 // purpose :
487 // =======================================================================
SetZRange(const Standard_Real theZNear,const Standard_Real theZFar)488 void Graphic3d_Camera::SetZRange (const Standard_Real theZNear,
489 const Standard_Real theZFar)
490 {
491 Standard_ASSERT_RAISE (theZFar > theZNear, "ZFar should be greater than ZNear");
492 if (!IsOrthographic())
493 {
494 Standard_ASSERT_RAISE (theZNear > 0.0, "Only positive Z-Near is allowed for perspective camera");
495 Standard_ASSERT_RAISE (theZFar > 0.0, "Only positive Z-Far is allowed for perspective camera");
496 }
497
498 if (ZNear() == theZNear
499 && ZFar () == theZFar)
500 {
501 return;
502 }
503
504 myZNear = theZNear;
505 myZFar = theZFar;
506
507 InvalidateProjection();
508 }
509
510 // =======================================================================
511 // function : SetAspect
512 // purpose :
513 // =======================================================================
SetAspect(const Standard_Real theAspect)514 void Graphic3d_Camera::SetAspect (const Standard_Real theAspect)
515 {
516 if (Aspect() == theAspect)
517 {
518 return;
519 }
520
521 myAspect = theAspect;
522 myFOVx = myFOVy * theAspect;
523
524 InvalidateProjection();
525 }
526
527 // =======================================================================
528 // function : SetZFocus
529 // purpose :
530 // =======================================================================
SetZFocus(const FocusType theType,const Standard_Real theZFocus)531 void Graphic3d_Camera::SetZFocus(const FocusType theType, const Standard_Real theZFocus)
532 {
533 if (ZFocusType() == theType
534 && ZFocus () == theZFocus)
535 {
536 return;
537 }
538
539 myZFocusType = theType;
540 myZFocus = theZFocus;
541
542 InvalidateProjection();
543 }
544
545 // =======================================================================
546 // function : SetIOD
547 // purpose :
548 // =======================================================================
SetIOD(const IODType theType,const Standard_Real theIOD)549 void Graphic3d_Camera::SetIOD (const IODType theType, const Standard_Real theIOD)
550 {
551 if (GetIODType() == theType
552 && IOD () == theIOD)
553 {
554 return;
555 }
556
557 myIODType = theType;
558 myIOD = theIOD;
559
560 InvalidateProjection();
561 }
562
563 // =======================================================================
564 // function : SetTile
565 // purpose :
566 // =======================================================================
SetTile(const Graphic3d_CameraTile & theTile)567 void Graphic3d_Camera::SetTile (const Graphic3d_CameraTile& theTile)
568 {
569 if (myTile == theTile)
570 {
571 return;
572 }
573
574 myTile = theTile;
575 InvalidateProjection();
576 }
577
578 // =======================================================================
579 // function : OrthogonalizeUp
580 // purpose :
581 // =======================================================================
OrthogonalizeUp()582 void Graphic3d_Camera::OrthogonalizeUp()
583 {
584 SetUp (OrthogonalizedUp());
585 }
586
587 // =======================================================================
588 // function : OrthogonalizedUp
589 // purpose :
590 // =======================================================================
OrthogonalizedUp() const591 gp_Dir Graphic3d_Camera::OrthogonalizedUp() const
592 {
593 gp_Dir aDir = Direction();
594 gp_Dir aLeft = aDir.Crossed (Up());
595
596 // recompute up as: up = left x direction
597 return aLeft.Crossed (aDir);
598 }
599
600 // =======================================================================
601 // function : Transform
602 // purpose :
603 // =======================================================================
Transform(const gp_Trsf & theTrsf)604 void Graphic3d_Camera::Transform (const gp_Trsf& theTrsf)
605 {
606 if (theTrsf.Form() == gp_Identity)
607 {
608 return;
609 }
610
611 myUp .Transform (theTrsf);
612 myDirection.Transform (theTrsf);
613 myEye.Transform (theTrsf);
614 InvalidateOrientation();
615 }
616
617 // =======================================================================
618 // function : safePointCast
619 // purpose :
620 // =======================================================================
safePointCast(const gp_Pnt & thePnt)621 static Graphic3d_Vec4d safePointCast (const gp_Pnt& thePnt)
622 {
623 Standard_Real aLim = 1e15f;
624
625 // have to deal with values greater then max float
626 gp_Pnt aSafePoint = thePnt;
627 const Standard_Real aBigFloat = aLim * 0.1f;
628 if (Abs (aSafePoint.X()) > aLim)
629 aSafePoint.SetX (aSafePoint.X() >= 0 ? aBigFloat : -aBigFloat);
630 if (Abs (aSafePoint.Y()) > aLim)
631 aSafePoint.SetY (aSafePoint.Y() >= 0 ? aBigFloat : -aBigFloat);
632 if (Abs (aSafePoint.Z()) > aLim)
633 aSafePoint.SetZ (aSafePoint.Z() >= 0 ? aBigFloat : -aBigFloat);
634
635 // convert point
636 Graphic3d_Vec4d aPnt (aSafePoint.X(), aSafePoint.Y(), aSafePoint.Z(), 1.0);
637
638 return aPnt;
639 }
640
641 // =======================================================================
642 // function : Project
643 // purpose :
644 // =======================================================================
Project(const gp_Pnt & thePnt) const645 gp_Pnt Graphic3d_Camera::Project (const gp_Pnt& thePnt) const
646 {
647 const Graphic3d_Mat4d& aViewMx = OrientationMatrix();
648 const Graphic3d_Mat4d& aProjMx = ProjectionMatrix();
649
650 // use compatible type of point
651 Graphic3d_Vec4d aPnt = safePointCast (thePnt);
652
653 aPnt = aViewMx * aPnt; // convert to view coordinate space
654 aPnt = aProjMx * aPnt; // convert to projection coordinate space
655
656 const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
657
658 return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
659 }
660
661 // =======================================================================
662 // function : UnProject
663 // purpose :
664 // =======================================================================
UnProject(const gp_Pnt & thePnt) const665 gp_Pnt Graphic3d_Camera::UnProject (const gp_Pnt& thePnt) const
666 {
667 const Graphic3d_Mat4d& aViewMx = OrientationMatrix();
668 const Graphic3d_Mat4d& aProjMx = ProjectionMatrix();
669
670 Graphic3d_Mat4d aInvView;
671 Graphic3d_Mat4d aInvProj;
672
673 // this case should never happen
674 if (!aViewMx.Inverted (aInvView) || !aProjMx.Inverted (aInvProj))
675 {
676 return gp_Pnt (0.0, 0.0, 0.0);
677 }
678
679 // use compatible type of point
680 Graphic3d_Vec4d aPnt = safePointCast (thePnt);
681
682 aPnt = aInvProj * aPnt; // convert to view coordinate space
683 aPnt = aInvView * aPnt; // convert to world coordinate space
684
685 const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
686
687 return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
688 }
689
690 // =======================================================================
691 // function : ConvertView2Proj
692 // purpose :
693 // =======================================================================
ConvertView2Proj(const gp_Pnt & thePnt) const694 gp_Pnt Graphic3d_Camera::ConvertView2Proj (const gp_Pnt& thePnt) const
695 {
696 const Graphic3d_Mat4d& aProjMx = ProjectionMatrix();
697
698 // use compatible type of point
699 Graphic3d_Vec4d aPnt = safePointCast (thePnt);
700
701 aPnt = aProjMx * aPnt; // convert to projection coordinate space
702
703 const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
704
705 return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
706 }
707
708 // =======================================================================
709 // function : ConvertProj2View
710 // purpose :
711 // =======================================================================
ConvertProj2View(const gp_Pnt & thePnt) const712 gp_Pnt Graphic3d_Camera::ConvertProj2View (const gp_Pnt& thePnt) const
713 {
714 const Graphic3d_Mat4d& aProjMx = ProjectionMatrix();
715
716 Graphic3d_Mat4d aInvProj;
717
718 // this case should never happen, but...
719 if (!aProjMx.Inverted (aInvProj))
720 {
721 return gp_Pnt (0, 0, 0);
722 }
723
724 // use compatible type of point
725 Graphic3d_Vec4d aPnt = safePointCast (thePnt);
726
727 aPnt = aInvProj * aPnt; // convert to view coordinate space
728
729 const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
730
731 return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
732 }
733
734 // =======================================================================
735 // function : ConvertWorld2View
736 // purpose :
737 // =======================================================================
ConvertWorld2View(const gp_Pnt & thePnt) const738 gp_Pnt Graphic3d_Camera::ConvertWorld2View (const gp_Pnt& thePnt) const
739 {
740 const Graphic3d_Mat4d& aViewMx = OrientationMatrix();
741
742 // use compatible type of point
743 Graphic3d_Vec4d aPnt = safePointCast (thePnt);
744
745 aPnt = aViewMx * aPnt; // convert to view coordinate space
746
747 const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
748
749 return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
750 }
751
752 // =======================================================================
753 // function : ConvertView2World
754 // purpose :
755 // =======================================================================
ConvertView2World(const gp_Pnt & thePnt) const756 gp_Pnt Graphic3d_Camera::ConvertView2World (const gp_Pnt& thePnt) const
757 {
758 const Graphic3d_Mat4d& aViewMx = OrientationMatrix();
759
760 Graphic3d_Mat4d aInvView;
761
762 if (!aViewMx.Inverted (aInvView))
763 {
764 return gp_Pnt(0, 0, 0);
765 }
766
767 // use compatible type of point
768 Graphic3d_Vec4d aPnt = safePointCast (thePnt);
769
770 aPnt = aInvView * aPnt; // convert to world coordinate space
771
772 const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
773
774 return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
775 }
776
777 // =======================================================================
778 // function : ViewDimensions
779 // purpose :
780 // =======================================================================
ViewDimensions(const Standard_Real theZValue) const781 gp_XYZ Graphic3d_Camera::ViewDimensions (const Standard_Real theZValue) const
782 {
783 // view plane dimensions
784 Standard_Real aSize = IsOrthographic() ? myScale : (2.0 * theZValue * myFOVyTan);
785 Standard_Real aSizeX, aSizeY;
786 if (myAspect > 1.0)
787 {
788 aSizeX = aSize * myAspect;
789 aSizeY = aSize;
790 }
791 else
792 {
793 aSizeX = aSize;
794 aSizeY = aSize / myAspect;
795 }
796
797 // and frustum depth
798 return gp_XYZ (aSizeX, aSizeY, myZFar - myZNear);
799 }
800
801 // =======================================================================
802 // function : Frustum
803 // purpose :
804 // =======================================================================
Frustum(gp_Pln & theLeft,gp_Pln & theRight,gp_Pln & theBottom,gp_Pln & theTop,gp_Pln & theNear,gp_Pln & theFar) const805 void Graphic3d_Camera::Frustum (gp_Pln& theLeft,
806 gp_Pln& theRight,
807 gp_Pln& theBottom,
808 gp_Pln& theTop,
809 gp_Pln& theNear,
810 gp_Pln& theFar) const
811 {
812 gp_Vec aProjection = gp_Vec (Direction());
813 gp_Vec anUp = OrthogonalizedUp();
814 gp_Vec aSide = aProjection ^ anUp;
815
816 Standard_ASSERT_RAISE (
817 !aProjection.IsParallel (anUp, Precision::Angular()),
818 "Can not derive SIDE = PROJ x UP - directions are parallel");
819
820 theNear = gp_Pln (Eye().Translated (aProjection * ZNear()), aProjection);
821 theFar = gp_Pln (Eye().Translated (aProjection * ZFar()), -aProjection);
822
823 Standard_Real aHScaleHor = 0.0, aHScaleVer = 0.0;
824 if (Aspect() >= 1.0)
825 {
826 aHScaleHor = Scale() * 0.5 * Aspect();
827 aHScaleVer = Scale() * 0.5;
828 }
829 else
830 {
831 aHScaleHor = Scale() * 0.5;
832 aHScaleVer = Scale() * 0.5 / Aspect();
833 }
834
835 gp_Pnt aPntLeft = Center().Translated (aHScaleHor * -aSide);
836 gp_Pnt aPntRight = Center().Translated (aHScaleHor * aSide);
837 gp_Pnt aPntBottom = Center().Translated (aHScaleVer * -anUp);
838 gp_Pnt aPntTop = Center().Translated (aHScaleVer * anUp);
839
840 gp_Vec aDirLeft = aSide;
841 gp_Vec aDirRight = -aSide;
842 gp_Vec aDirBottom = anUp;
843 gp_Vec aDirTop = -anUp;
844 if (!IsOrthographic())
845 {
846 Standard_Real aHFOVHor = ATan (Tan (DTR_HALF * FOVy()) * Aspect());
847 Standard_Real aHFOVVer = DTR_HALF * FOVy();
848 aDirLeft.Rotate (gp_Ax1 (gp::Origin(), anUp), aHFOVHor);
849 aDirRight.Rotate (gp_Ax1 (gp::Origin(), anUp), -aHFOVHor);
850 aDirBottom.Rotate (gp_Ax1 (gp::Origin(), aSide), -aHFOVVer);
851 aDirTop.Rotate (gp_Ax1 (gp::Origin(), aSide), aHFOVVer);
852 }
853
854 theLeft = gp_Pln (aPntLeft, aDirLeft);
855 theRight = gp_Pln (aPntRight, aDirRight);
856 theBottom = gp_Pln (aPntBottom, aDirBottom);
857 theTop = gp_Pln (aPntTop, aDirTop);
858 }
859
860 // =======================================================================
861 // function : OrientationMatrix
862 // purpose :
863 // =======================================================================
OrientationMatrix() const864 const Graphic3d_Mat4d& Graphic3d_Camera::OrientationMatrix() const
865 {
866 return UpdateOrientation (myMatricesD).Orientation;
867 }
868
869 // =======================================================================
870 // function : OrientationMatrixF
871 // purpose :
872 // =======================================================================
OrientationMatrixF() const873 const Graphic3d_Mat4& Graphic3d_Camera::OrientationMatrixF() const
874 {
875 return UpdateOrientation (myMatricesF).Orientation;
876 }
877
878 // =======================================================================
879 // function : ProjectionMatrix
880 // purpose :
881 // =======================================================================
ProjectionMatrix() const882 const Graphic3d_Mat4d& Graphic3d_Camera::ProjectionMatrix() const
883 {
884 return UpdateProjection (myMatricesD).MProjection;
885 }
886
887 // =======================================================================
888 // function : ProjectionMatrixF
889 // purpose :
890 // =======================================================================
ProjectionMatrixF() const891 const Graphic3d_Mat4& Graphic3d_Camera::ProjectionMatrixF() const
892 {
893 return UpdateProjection (myMatricesF).MProjection;
894 }
895
896 // =======================================================================
897 // function : ProjectionStereoLeft
898 // purpose :
899 // =======================================================================
ProjectionStereoLeft() const900 const Graphic3d_Mat4d& Graphic3d_Camera::ProjectionStereoLeft() const
901 {
902 return UpdateProjection (myMatricesD).LProjection;
903 }
904
905 // =======================================================================
906 // function : ProjectionStereoLeftF
907 // purpose :
908 // =======================================================================
ProjectionStereoLeftF() const909 const Graphic3d_Mat4& Graphic3d_Camera::ProjectionStereoLeftF() const
910 {
911 return UpdateProjection (myMatricesF).LProjection;
912 }
913
914 // =======================================================================
915 // function : ProjectionStereoRight
916 // purpose :
917 // =======================================================================
ProjectionStereoRight() const918 const Graphic3d_Mat4d& Graphic3d_Camera::ProjectionStereoRight() const
919 {
920 return UpdateProjection (myMatricesD).RProjection;
921 }
922
923 // =======================================================================
924 // function : ProjectionStereoRightF
925 // purpose :
926 // =======================================================================
ProjectionStereoRightF() const927 const Graphic3d_Mat4& Graphic3d_Camera::ProjectionStereoRightF() const
928 {
929 return UpdateProjection (myMatricesF).RProjection;
930 }
931
932 // =======================================================================
933 // function : ResetCustomProjection
934 // purpose :
935 // =======================================================================
ResetCustomProjection()936 void Graphic3d_Camera::ResetCustomProjection()
937 {
938 if (myIsCustomFrustomLR
939 || myIsCustomProjMatLR
940 || myIsCustomProjMatM)
941 {
942 myIsCustomFrustomLR = false;
943 myIsCustomProjMatLR = false;
944 myIsCustomProjMatM = false;
945 InvalidateProjection();
946 }
947 }
948
949 // =======================================================================
950 // function : StereoProjection
951 // purpose :
952 // =======================================================================
StereoProjection(Graphic3d_Mat4d & theProjL,Graphic3d_Mat4d & theHeadToEyeL,Graphic3d_Mat4d & theProjR,Graphic3d_Mat4d & theHeadToEyeR) const953 void Graphic3d_Camera::StereoProjection (Graphic3d_Mat4d& theProjL,
954 Graphic3d_Mat4d& theHeadToEyeL,
955 Graphic3d_Mat4d& theProjR,
956 Graphic3d_Mat4d& theHeadToEyeR) const
957 {
958 stereoProjection (theProjL, theHeadToEyeL, theProjR, theHeadToEyeR);
959 }
960
961 // =======================================================================
962 // function : StereoProjectionF
963 // purpose :
964 // =======================================================================
StereoProjectionF(Graphic3d_Mat4 & theProjL,Graphic3d_Mat4 & theHeadToEyeL,Graphic3d_Mat4 & theProjR,Graphic3d_Mat4 & theHeadToEyeR) const965 void Graphic3d_Camera::StereoProjectionF (Graphic3d_Mat4& theProjL,
966 Graphic3d_Mat4& theHeadToEyeL,
967 Graphic3d_Mat4& theProjR,
968 Graphic3d_Mat4& theHeadToEyeR) const
969 {
970 stereoProjection (theProjL, theHeadToEyeL, theProjR, theHeadToEyeR);
971 }
972
973 // =======================================================================
974 // function : stereoProjection
975 // purpose :
976 // =======================================================================
977 template <typename Elem_t>
stereoProjection(NCollection_Mat4<Elem_t> & theProjL,NCollection_Mat4<Elem_t> & theHeadToEyeL,NCollection_Mat4<Elem_t> & theProjR,NCollection_Mat4<Elem_t> & theHeadToEyeR) const978 void Graphic3d_Camera::stereoProjection (NCollection_Mat4<Elem_t>& theProjL,
979 NCollection_Mat4<Elem_t>& theHeadToEyeL,
980 NCollection_Mat4<Elem_t>& theProjR,
981 NCollection_Mat4<Elem_t>& theHeadToEyeR) const
982 {
983 if (myIsCustomProjMatLR)
984 {
985 theProjL .ConvertFrom (myCustomProjMatL);
986 theHeadToEyeL.ConvertFrom (myCustomHeadToEyeMatL);
987 theProjR .ConvertFrom (myCustomProjMatR);
988 theHeadToEyeR.ConvertFrom (myCustomHeadToEyeMatR);
989 return;
990 }
991
992 NCollection_Mat4<Elem_t> aDummy;
993 computeProjection (aDummy, theProjL, theProjR, false);
994
995 const Standard_Real aIOD = myIODType == IODType_Relative
996 ? myIOD * Distance()
997 : myIOD;
998 if (aIOD != 0.0)
999 {
1000 // X translation to cancel parallax
1001 theHeadToEyeL.InitIdentity();
1002 theHeadToEyeL.SetColumn (3, NCollection_Vec3<Elem_t> (Elem_t ( 0.5 * aIOD), Elem_t (0.0), Elem_t (0.0)));
1003 theHeadToEyeR.InitIdentity();
1004 theHeadToEyeR.SetColumn (3, NCollection_Vec3<Elem_t> (Elem_t (-0.5 * aIOD), Elem_t (0.0), Elem_t (0.0)));
1005 }
1006 }
1007
1008 // =======================================================================
1009 // function : SetCustomStereoFrustums
1010 // purpose :
1011 // =======================================================================
SetCustomStereoFrustums(const Aspect_FrustumLRBT<Standard_Real> & theFrustumL,const Aspect_FrustumLRBT<Standard_Real> & theFrustumR)1012 void Graphic3d_Camera::SetCustomStereoFrustums (const Aspect_FrustumLRBT<Standard_Real>& theFrustumL,
1013 const Aspect_FrustumLRBT<Standard_Real>& theFrustumR)
1014 {
1015 myCustomFrustumL = theFrustumL;
1016 myCustomFrustumR = theFrustumR;
1017 myIsCustomFrustomLR = true;
1018 myIsCustomProjMatLR = false;
1019 InvalidateProjection();
1020 }
1021
1022 // =======================================================================
1023 // function : SetCustomStereoProjection
1024 // purpose :
1025 // =======================================================================
SetCustomStereoProjection(const Graphic3d_Mat4d & theProjL,const Graphic3d_Mat4d & theHeadToEyeL,const Graphic3d_Mat4d & theProjR,const Graphic3d_Mat4d & theHeadToEyeR)1026 void Graphic3d_Camera::SetCustomStereoProjection (const Graphic3d_Mat4d& theProjL,
1027 const Graphic3d_Mat4d& theHeadToEyeL,
1028 const Graphic3d_Mat4d& theProjR,
1029 const Graphic3d_Mat4d& theHeadToEyeR)
1030 {
1031 myCustomProjMatL = theProjL;
1032 myCustomProjMatR = theProjR;
1033 myCustomHeadToEyeMatL = theHeadToEyeL;
1034 myCustomHeadToEyeMatR = theHeadToEyeR;
1035 myIsCustomProjMatLR = true;
1036 myIsCustomFrustomLR = false;
1037 InvalidateProjection();
1038 }
1039
1040 // =======================================================================
1041 // function : SetCustomMonoProjection
1042 // purpose :
1043 // =======================================================================
SetCustomMonoProjection(const Graphic3d_Mat4d & theProj)1044 void Graphic3d_Camera::SetCustomMonoProjection (const Graphic3d_Mat4d& theProj)
1045 {
1046 myCustomProjMatM = theProj;
1047 myIsCustomProjMatM = true;
1048 InvalidateProjection();
1049 }
1050
1051 // =======================================================================
1052 // function : computeProjection
1053 // purpose :
1054 // =======================================================================
1055 template <typename Elem_t>
computeProjection(NCollection_Mat4<Elem_t> & theProjM,NCollection_Mat4<Elem_t> & theProjL,NCollection_Mat4<Elem_t> & theProjR,bool theToAddHeadToEye) const1056 void Graphic3d_Camera::computeProjection (NCollection_Mat4<Elem_t>& theProjM,
1057 NCollection_Mat4<Elem_t>& theProjL,
1058 NCollection_Mat4<Elem_t>& theProjR,
1059 bool theToAddHeadToEye) const
1060 {
1061 theProjM.InitIdentity();
1062 theProjL.InitIdentity();
1063 theProjR.InitIdentity();
1064
1065 // sets top of frustum based on FOVy and near clipping plane
1066 Elem_t aScale = static_cast<Elem_t> (myScale);
1067 Elem_t aZNear = static_cast<Elem_t> (myZNear);
1068 Elem_t aZFar = static_cast<Elem_t> (myZFar);
1069 Elem_t anAspect = static_cast<Elem_t> (myAspect);
1070 Elem_t aDXHalf = 0.0, aDYHalf = 0.0;
1071 if (IsOrthographic())
1072 {
1073 aDXHalf = aDYHalf = aScale * Elem_t (0.5);
1074 }
1075 else
1076 {
1077 aDXHalf = aDYHalf = aZNear * Elem_t (myFOVyTan);
1078 }
1079
1080 if (anAspect > 1.0)
1081 {
1082 aDXHalf *= anAspect;
1083 }
1084 else
1085 {
1086 aDYHalf /= anAspect;
1087 }
1088
1089 // sets right of frustum based on aspect ratio
1090 Aspect_FrustumLRBT<Elem_t> anLRBT;
1091 anLRBT.Left = -aDXHalf;
1092 anLRBT.Right = aDXHalf;
1093 anLRBT.Bottom = -aDYHalf;
1094 anLRBT.Top = aDYHalf;
1095
1096 Elem_t aIOD = myIODType == IODType_Relative
1097 ? static_cast<Elem_t> (myIOD * Distance())
1098 : static_cast<Elem_t> (myIOD);
1099
1100 Elem_t aFocus = myZFocusType == FocusType_Relative
1101 ? static_cast<Elem_t> (myZFocus * Distance())
1102 : static_cast<Elem_t> (myZFocus);
1103
1104 if (myTile.IsValid())
1105 {
1106 const Elem_t aDXFull = Elem_t(2) * aDXHalf;
1107 const Elem_t aDYFull = Elem_t(2) * aDYHalf;
1108 const Graphic3d_Vec2i anOffset = myTile.OffsetLowerLeft();
1109 anLRBT.Left = -aDXHalf + aDXFull * static_cast<Elem_t> (anOffset.x()) / static_cast<Elem_t> (myTile.TotalSize.x());
1110 anLRBT.Right = -aDXHalf + aDXFull * static_cast<Elem_t> (anOffset.x() + myTile.TileSize.x()) / static_cast<Elem_t> (myTile.TotalSize.x());
1111 anLRBT.Bottom = -aDYHalf + aDYFull * static_cast<Elem_t> (anOffset.y()) / static_cast<Elem_t> (myTile.TotalSize.y());
1112 anLRBT.Top = -aDYHalf + aDYFull * static_cast<Elem_t> (anOffset.y() + myTile.TileSize.y()) / static_cast<Elem_t> (myTile.TotalSize.y());
1113 }
1114
1115 if (myIsCustomProjMatM)
1116 {
1117 theProjM.ConvertFrom (myCustomProjMatM);
1118 }
1119 switch (myProjType)
1120 {
1121 case Projection_Orthographic:
1122 {
1123 if (!myIsCustomProjMatM)
1124 {
1125 orthoProj (theProjM, anLRBT, aZNear, aZFar);
1126 }
1127 break;
1128 }
1129 case Projection_Perspective:
1130 {
1131 if (!myIsCustomProjMatM)
1132 {
1133 perspectiveProj (theProjM, anLRBT, aZNear, aZFar);
1134 }
1135 break;
1136 }
1137 case Projection_MonoLeftEye:
1138 case Projection_MonoRightEye:
1139 case Projection_Stereo:
1140 {
1141 if (!myIsCustomProjMatM)
1142 {
1143 perspectiveProj (theProjM, anLRBT, aZNear, aZFar);
1144 }
1145 if (myIsCustomProjMatLR)
1146 {
1147 if (theToAddHeadToEye)
1148 {
1149 theProjL.ConvertFrom (myCustomProjMatL * myCustomHeadToEyeMatL);
1150 theProjR.ConvertFrom (myCustomProjMatR * myCustomHeadToEyeMatR);
1151 }
1152 else
1153 {
1154 theProjL.ConvertFrom (myCustomProjMatL);
1155 theProjR.ConvertFrom (myCustomProjMatR);
1156 }
1157 }
1158 else if (myIsCustomFrustomLR)
1159 {
1160 anLRBT = Aspect_FrustumLRBT<Elem_t> (myCustomFrustumL).Multiplied (aZNear);
1161 perspectiveProj (theProjL, anLRBT, aZNear, aZFar);
1162
1163 anLRBT = Aspect_FrustumLRBT<Elem_t> (myCustomFrustumR).Multiplied (aZNear);
1164 perspectiveProj (theProjR, anLRBT, aZNear, aZFar);
1165 }
1166 else
1167 {
1168 stereoEyeProj (theProjL,
1169 anLRBT, aZNear, aZFar, aIOD, aFocus,
1170 Aspect_Eye_Left);
1171 stereoEyeProj (theProjR,
1172 anLRBT, aZNear, aZFar, aIOD, aFocus,
1173 Aspect_Eye_Right);
1174 }
1175
1176 if (theToAddHeadToEye
1177 && !myIsCustomProjMatLR
1178 && aIOD != Elem_t (0.0))
1179 {
1180 // X translation to cancel parallax
1181 theProjL.Translate (NCollection_Vec3<Elem_t> (Elem_t ( 0.5) * aIOD, Elem_t (0.0), Elem_t (0.0)));
1182 theProjR.Translate (NCollection_Vec3<Elem_t> (Elem_t (-0.5) * aIOD, Elem_t (0.0), Elem_t (0.0)));
1183 }
1184 break;
1185 }
1186 }
1187 if (myProjType == Projection_MonoLeftEye)
1188 {
1189 theProjM = theProjL;
1190 }
1191 else if (myProjType == Projection_MonoRightEye)
1192 {
1193 theProjM = theProjR;
1194 }
1195 }
1196
1197 // =======================================================================
1198 // function : UpdateOrientation
1199 // purpose :
1200 // =======================================================================
1201 template <typename Elem_t>
1202 Graphic3d_Camera::TransformMatrices<Elem_t>&
UpdateOrientation(TransformMatrices<Elem_t> & theMatrices) const1203 Graphic3d_Camera::UpdateOrientation (TransformMatrices<Elem_t>& theMatrices) const
1204 {
1205 if (theMatrices.IsOrientationValid())
1206 {
1207 return theMatrices; // for inline accessors
1208 }
1209
1210 theMatrices.InitOrientation();
1211
1212 NCollection_Vec3<Elem_t> anEye (static_cast<Elem_t> (myEye.X()),
1213 static_cast<Elem_t> (myEye.Y()),
1214 static_cast<Elem_t> (myEye.Z()));
1215
1216 NCollection_Vec3<Elem_t> aViewDir (static_cast<Elem_t> (myDirection.X()),
1217 static_cast<Elem_t> (myDirection.Y()),
1218 static_cast<Elem_t> (myDirection.Z()));
1219
1220 NCollection_Vec3<Elem_t> anUp (static_cast<Elem_t> (myUp.X()),
1221 static_cast<Elem_t> (myUp.Y()),
1222 static_cast<Elem_t> (myUp.Z()));
1223
1224 NCollection_Vec3<Elem_t> anAxialScale (static_cast<Elem_t> (myAxialScale.X()),
1225 static_cast<Elem_t> (myAxialScale.Y()),
1226 static_cast<Elem_t> (myAxialScale.Z()));
1227
1228 LookOrientation (anEye, aViewDir, anUp, anAxialScale, theMatrices.Orientation);
1229
1230 return theMatrices; // for inline accessors
1231 }
1232
1233 // =======================================================================
1234 // function : InvalidateProjection
1235 // purpose :
1236 // =======================================================================
InvalidateProjection()1237 void Graphic3d_Camera::InvalidateProjection()
1238 {
1239 myMatricesD.ResetProjection();
1240 myMatricesF.ResetProjection();
1241 myWorldViewProjState.ProjectionState() = (Standard_Size)Standard_Atomic_Increment (&THE_STATE_COUNTER);
1242 }
1243
1244 // =======================================================================
1245 // function : InvalidateOrientation
1246 // purpose :
1247 // =======================================================================
InvalidateOrientation()1248 void Graphic3d_Camera::InvalidateOrientation()
1249 {
1250 myMatricesD.ResetOrientation();
1251 myMatricesF.ResetOrientation();
1252 myWorldViewProjState.WorldViewState() = (Standard_Size)Standard_Atomic_Increment (&THE_STATE_COUNTER);
1253 }
1254
1255 // =======================================================================
1256 // function : orthoProj
1257 // purpose :
1258 // =======================================================================
1259 template <typename Elem_t>
orthoProj(NCollection_Mat4<Elem_t> & theOutMx,const Aspect_FrustumLRBT<Elem_t> & theLRBT,const Elem_t theNear,const Elem_t theFar) const1260 void Graphic3d_Camera::orthoProj (NCollection_Mat4<Elem_t>& theOutMx,
1261 const Aspect_FrustumLRBT<Elem_t>& theLRBT,
1262 const Elem_t theNear,
1263 const Elem_t theFar) const
1264 {
1265 // row 0
1266 theOutMx.ChangeValue (0, 0) = Elem_t (2.0) / (theLRBT.Right - theLRBT.Left);
1267 theOutMx.ChangeValue (0, 1) = Elem_t (0.0);
1268 theOutMx.ChangeValue (0, 2) = Elem_t (0.0);
1269 theOutMx.ChangeValue (0, 3) = - (theLRBT.Right + theLRBT.Left) / (theLRBT.Right - theLRBT.Left);
1270
1271 // row 1
1272 theOutMx.ChangeValue (1, 0) = Elem_t (0.0);
1273 theOutMx.ChangeValue (1, 1) = Elem_t (2.0) / (theLRBT.Top - theLRBT.Bottom);
1274 theOutMx.ChangeValue (1, 2) = Elem_t (0.0);
1275 theOutMx.ChangeValue (1, 3) = - (theLRBT.Top + theLRBT.Bottom) / (theLRBT.Top - theLRBT.Bottom);
1276
1277 // row 2
1278 theOutMx.ChangeValue (2, 0) = Elem_t (0.0);
1279 theOutMx.ChangeValue (2, 1) = Elem_t (0.0);
1280 if (myIsZeroToOneDepth)
1281 {
1282 theOutMx.ChangeValue (2, 2) = Elem_t (-1.0) / (theFar - theNear);
1283 theOutMx.ChangeValue (2, 3) = -theNear / (theFar - theNear);
1284 }
1285 else
1286 {
1287 theOutMx.ChangeValue (2, 2) = Elem_t (-2.0) / (theFar - theNear);
1288 theOutMx.ChangeValue (2, 3) = - (theFar + theNear) / (theFar - theNear);
1289 }
1290
1291 // row 3
1292 theOutMx.ChangeValue (3, 0) = Elem_t (0.0);
1293 theOutMx.ChangeValue (3, 1) = Elem_t (0.0);
1294 theOutMx.ChangeValue (3, 2) = Elem_t (0.0);
1295 theOutMx.ChangeValue (3, 3) = Elem_t (1.0);
1296 }
1297
1298 // =======================================================================
1299 // function : PerspectiveProj
1300 // purpose :
1301 // =======================================================================
1302 template <typename Elem_t>
perspectiveProj(NCollection_Mat4<Elem_t> & theOutMx,const Aspect_FrustumLRBT<Elem_t> & theLRBT,const Elem_t theNear,const Elem_t theFar) const1303 void Graphic3d_Camera::perspectiveProj (NCollection_Mat4<Elem_t>& theOutMx,
1304 const Aspect_FrustumLRBT<Elem_t>& theLRBT,
1305 const Elem_t theNear,
1306 const Elem_t theFar) const
1307 {
1308 // column 0
1309 theOutMx.ChangeValue (0, 0) = (Elem_t (2.0) * theNear) / (theLRBT.Right - theLRBT.Left);
1310 theOutMx.ChangeValue (1, 0) = Elem_t (0.0);
1311 theOutMx.ChangeValue (2, 0) = Elem_t (0.0);
1312 theOutMx.ChangeValue (3, 0) = Elem_t (0.0);
1313
1314 // column 1
1315 theOutMx.ChangeValue (0, 1) = Elem_t (0.0);
1316 theOutMx.ChangeValue (1, 1) = (Elem_t (2.0) * theNear) / (theLRBT.Top - theLRBT.Bottom);
1317 theOutMx.ChangeValue (2, 1) = Elem_t (0.0);
1318 theOutMx.ChangeValue (3, 1) = Elem_t (0.0);
1319
1320 // column 2
1321 theOutMx.ChangeValue (0, 2) = (theLRBT.Right + theLRBT.Left) / (theLRBT.Right - theLRBT.Left);
1322 theOutMx.ChangeValue (1, 2) = (theLRBT.Top + theLRBT.Bottom) / (theLRBT.Top - theLRBT.Bottom);
1323 if (myIsZeroToOneDepth)
1324 {
1325 theOutMx.ChangeValue (2, 2) = theFar / (theNear - theFar);
1326 }
1327 else
1328 {
1329 theOutMx.ChangeValue (2, 2) = -(theFar + theNear) / (theFar - theNear);
1330 }
1331 theOutMx.ChangeValue (3, 2) = Elem_t (-1.0);
1332
1333 // column 3
1334 theOutMx.ChangeValue (0, 3) = Elem_t (0.0);
1335 theOutMx.ChangeValue (1, 3) = Elem_t (0.0);
1336 if (myIsZeroToOneDepth)
1337 {
1338 theOutMx.ChangeValue (2, 3) = -(theFar * theNear) / (theFar - theNear);
1339 }
1340 else
1341 {
1342 theOutMx.ChangeValue (2, 3) = -(Elem_t (2.0) * theFar * theNear) / (theFar - theNear);
1343 }
1344 theOutMx.ChangeValue (3, 3) = Elem_t (0.0);
1345 }
1346
1347 // =======================================================================
1348 // function : StereoEyeProj
1349 // purpose :
1350 // =======================================================================
1351 template <typename Elem_t>
stereoEyeProj(NCollection_Mat4<Elem_t> & theOutMx,const Aspect_FrustumLRBT<Elem_t> & theLRBT,const Elem_t theNear,const Elem_t theFar,const Elem_t theIOD,const Elem_t theZFocus,const Aspect_Eye theEyeIndex) const1352 void Graphic3d_Camera::stereoEyeProj (NCollection_Mat4<Elem_t>& theOutMx,
1353 const Aspect_FrustumLRBT<Elem_t>& theLRBT,
1354 const Elem_t theNear,
1355 const Elem_t theFar,
1356 const Elem_t theIOD,
1357 const Elem_t theZFocus,
1358 const Aspect_Eye theEyeIndex) const
1359 {
1360 Elem_t aDx = theEyeIndex == Aspect_Eye_Left ? Elem_t (0.5) * theIOD : Elem_t (-0.5) * theIOD;
1361 Elem_t aDXStereoShift = aDx * theNear / theZFocus;
1362
1363 // construct eye projection matrix
1364 Aspect_FrustumLRBT<Elem_t> aLRBT = theLRBT;
1365 aLRBT.Left = theLRBT.Left + aDXStereoShift;
1366 aLRBT.Right = theLRBT.Right + aDXStereoShift;
1367 perspectiveProj (theOutMx, aLRBT, theNear, theFar);
1368 }
1369
1370 // =======================================================================
1371 // function : LookOrientation
1372 // purpose :
1373 // =======================================================================
1374 template <typename Elem_t>
LookOrientation(const NCollection_Vec3<Elem_t> & theEye,const NCollection_Vec3<Elem_t> & theFwdDir,const NCollection_Vec3<Elem_t> & theUpDir,const NCollection_Vec3<Elem_t> & theAxialScale,NCollection_Mat4<Elem_t> & theOutMx)1375 void Graphic3d_Camera::LookOrientation (const NCollection_Vec3<Elem_t>& theEye,
1376 const NCollection_Vec3<Elem_t>& theFwdDir,
1377 const NCollection_Vec3<Elem_t>& theUpDir,
1378 const NCollection_Vec3<Elem_t>& theAxialScale,
1379 NCollection_Mat4<Elem_t>& theOutMx)
1380 {
1381 NCollection_Vec3<Elem_t> aForward = theFwdDir;
1382 aForward.Normalize();
1383
1384 // side = forward x up
1385 NCollection_Vec3<Elem_t> aSide = NCollection_Vec3<Elem_t>::Cross (aForward, theUpDir);
1386 aSide.Normalize();
1387
1388 // recompute up as: up = side x forward
1389 NCollection_Vec3<Elem_t> anUp = NCollection_Vec3<Elem_t>::Cross (aSide, aForward);
1390
1391 NCollection_Mat4<Elem_t> aLookMx;
1392 aLookMx.SetRow (0, aSide);
1393 aLookMx.SetRow (1, anUp);
1394 aLookMx.SetRow (2, -aForward);
1395
1396 theOutMx.InitIdentity();
1397 theOutMx.Multiply (aLookMx);
1398 theOutMx.Translate (-theEye);
1399
1400 NCollection_Mat4<Elem_t> anAxialScaleMx;
1401 anAxialScaleMx.ChangeValue (0, 0) = theAxialScale.x();
1402 anAxialScaleMx.ChangeValue (1, 1) = theAxialScale.y();
1403 anAxialScaleMx.ChangeValue (2, 2) = theAxialScale.z();
1404
1405 theOutMx.Multiply (anAxialScaleMx);
1406 }
1407
1408 // =======================================================================
1409 // function : FitMinMax
1410 // purpose :
1411 // =======================================================================
FitMinMax(const Bnd_Box & theBox,const Standard_Real theResolution,const bool theToEnlargeIfLine)1412 bool Graphic3d_Camera::FitMinMax (const Bnd_Box& theBox,
1413 const Standard_Real theResolution,
1414 const bool theToEnlargeIfLine)
1415 {
1416 // Check bounding box for validness
1417 if (theBox.IsVoid())
1418 {
1419 return false; // bounding box is out of bounds...
1420 }
1421
1422 // Apply "axial scaling" to the bounding points.
1423 // It is not the best approach to make this scaling as a part of fit all operation,
1424 // but the axial scale is integrated into camera orientation matrix and the other
1425 // option is to perform frustum plane adjustment algorithm in view camera space,
1426 // which will lead to a number of additional world-view space conversions and
1427 // loosing precision as well.
1428 const gp_Pnt aBndMin = theBox.CornerMin().XYZ().Multiplied (myAxialScale);
1429 const gp_Pnt aBndMax = theBox.CornerMax().XYZ().Multiplied (myAxialScale);
1430 if (aBndMax.IsEqual (aBndMin, RealEpsilon()))
1431 {
1432 return false; // nothing to fit all
1433 }
1434
1435 // Prepare camera frustum planes.
1436 gp_Pln aFrustumPlaneArray[6];
1437 NCollection_Array1<gp_Pln> aFrustumPlane (aFrustumPlaneArray[0], 1, 6);
1438 Frustum (aFrustumPlane[1], aFrustumPlane[2], aFrustumPlane[3],
1439 aFrustumPlane[4], aFrustumPlane[5], aFrustumPlane[6]);
1440
1441 // Prepare camera up, side, direction vectors.
1442 const gp_Dir aCamUp = OrthogonalizedUp();
1443 const gp_Dir aCamDir = Direction();
1444 const gp_Dir aCamSide = aCamDir ^ aCamUp;
1445
1446 // Prepare scene bounding box parameters.
1447 const gp_Pnt aBndCenter = (aBndMin.XYZ() + aBndMax.XYZ()) / 2.0;
1448
1449 gp_Pnt aBndCornerArray[8];
1450 NCollection_Array1<gp_Pnt> aBndCorner (aBndCornerArray[0], 1, 8);
1451 aBndCorner[1].SetCoord (aBndMin.X(), aBndMin.Y(), aBndMin.Z());
1452 aBndCorner[2].SetCoord (aBndMin.X(), aBndMin.Y(), aBndMax.Z());
1453 aBndCorner[3].SetCoord (aBndMin.X(), aBndMax.Y(), aBndMin.Z());
1454 aBndCorner[4].SetCoord (aBndMin.X(), aBndMax.Y(), aBndMax.Z());
1455 aBndCorner[5].SetCoord (aBndMax.X(), aBndMin.Y(), aBndMin.Z());
1456 aBndCorner[6].SetCoord (aBndMax.X(), aBndMin.Y(), aBndMax.Z());
1457 aBndCorner[7].SetCoord (aBndMax.X(), aBndMax.Y(), aBndMin.Z());
1458 aBndCorner[8].SetCoord (aBndMax.X(), aBndMax.Y(), aBndMax.Z());
1459
1460 // Perspective-correct camera projection vector, matching the bounding box is determined geometrically.
1461 // Knowing the initial shape of a frustum it is possible to match it to a bounding box.
1462 // Then, knowing the relation of camera projection vector to the frustum shape it is possible to
1463 // set up perspective-correct camera projection matching the bounding box.
1464 // These steps support non-asymmetric transformations of view-projection space provided by camera.
1465 // The zooming can be done by calculating view plane size matching the bounding box at center of
1466 // the bounding box. The only limitation here is that the scale of camera should define size of
1467 // its view plane passing through the camera center, and the center of camera should be on the
1468 // same line with the center of bounding box.
1469
1470 // The following method is applied:
1471 // 1) Determine normalized asymmetry of camera projection vector by frustum planes.
1472 // 2) Determine new location of frustum planes, "matching" the bounding box.
1473 // 3) Determine new camera projection vector using the normalized asymmetry.
1474 // 4) Determine new zooming in view space.
1475
1476 // 1. Determine normalized projection asymmetry (if any).
1477 Standard_Real anAssymX = Tan (( aCamSide).Angle (aFrustumPlane[1].Axis().Direction()))
1478 - Tan ((-aCamSide).Angle (aFrustumPlane[2].Axis().Direction()));
1479 Standard_Real anAssymY = Tan (( aCamUp) .Angle (aFrustumPlane[3].Axis().Direction()))
1480 - Tan ((-aCamUp) .Angle (aFrustumPlane[4].Axis().Direction()));
1481
1482 // 2. Determine how far should be the frustum planes placed from center
1483 // of bounding box, in order to match the bounding box closely.
1484 Standard_Real aFitDistanceArray[6];
1485 NCollection_Array1<Standard_Real> aFitDistance (aFitDistanceArray[0], 1, 6);
1486 aFitDistance.Init (0.0);
1487 for (Standard_Integer anI = aFrustumPlane.Lower(); anI <= aFrustumPlane.Upper(); ++anI)
1488 {
1489 // Measure distances from center of bounding box to its corners towards the frustum plane.
1490 const gp_Dir& aPlaneN = aFrustumPlane[anI].Axis().Direction();
1491
1492 Standard_Real& aFitDist = aFitDistance[anI];
1493 for (Standard_Integer aJ = aBndCorner.Lower(); aJ <= aBndCorner.Upper(); ++aJ)
1494 {
1495 aFitDist = Max (aFitDist, gp_Vec (aBndCenter, aBndCorner[aJ]).Dot (aPlaneN));
1496 }
1497 }
1498 // The center of camera is placed on the same line with center of bounding box.
1499 // The view plane section crosses the bounding box at its center.
1500 // To compute view plane size, evaluate coefficients converting "point -> plane distance"
1501 // into view section size between the point and the frustum plane.
1502 // proj
1503 // /|\ right half of frame //
1504 // | //
1505 // point o<-- distance * coeff -->//---- (view plane section)
1506 // \ //
1507 // (distance) //
1508 // ~ //
1509 // (distance) //
1510 // \/\//
1511 // \//
1512 // //
1513 // (frustum plane)
1514 aFitDistance[1] *= Sqrt(1 + Pow (Tan ( aCamSide .Angle (aFrustumPlane[1].Axis().Direction())), 2.0));
1515 aFitDistance[2] *= Sqrt(1 + Pow (Tan ((-aCamSide).Angle (aFrustumPlane[2].Axis().Direction())), 2.0));
1516 aFitDistance[3] *= Sqrt(1 + Pow (Tan ( aCamUp .Angle (aFrustumPlane[3].Axis().Direction())), 2.0));
1517 aFitDistance[4] *= Sqrt(1 + Pow (Tan ((-aCamUp) .Angle (aFrustumPlane[4].Axis().Direction())), 2.0));
1518 aFitDistance[5] *= Sqrt(1 + Pow (Tan ( aCamDir .Angle (aFrustumPlane[5].Axis().Direction())), 2.0));
1519 aFitDistance[6] *= Sqrt(1 + Pow (Tan ((-aCamDir) .Angle (aFrustumPlane[6].Axis().Direction())), 2.0));
1520
1521 Standard_Real aViewSizeXv = aFitDistance[1] + aFitDistance[2];
1522 Standard_Real aViewSizeYv = aFitDistance[3] + aFitDistance[4];
1523 Standard_Real aViewSizeZv = aFitDistance[5] + aFitDistance[6];
1524
1525 // 3. Place center of camera on the same line with center of bounding
1526 // box applying corresponding projection asymmetry (if any).
1527 Standard_Real anAssymXv = anAssymX * aViewSizeXv * 0.5;
1528 Standard_Real anAssymYv = anAssymY * aViewSizeYv * 0.5;
1529 Standard_Real anOffsetXv = (aFitDistance[2] - aFitDistance[1]) * 0.5 + anAssymXv;
1530 Standard_Real anOffsetYv = (aFitDistance[4] - aFitDistance[3]) * 0.5 + anAssymYv;
1531 gp_Vec aTranslateSide = gp_Vec (aCamSide) * anOffsetXv;
1532 gp_Vec aTranslateUp = gp_Vec (aCamUp) * anOffsetYv;
1533 gp_Pnt aCamNewCenter = aBndCenter.Translated (aTranslateSide).Translated (aTranslateUp);
1534
1535 gp_Trsf aCenterTrsf;
1536 aCenterTrsf.SetTranslation (Center(), aCamNewCenter);
1537 Transform (aCenterTrsf);
1538 SetDistance (aFitDistance[6] + aFitDistance[5]);
1539
1540 if (aViewSizeXv < theResolution
1541 && aViewSizeYv < theResolution)
1542 {
1543 // Bounding box collapses to a point or thin line going in depth of the screen
1544 if (aViewSizeXv < theResolution || !theToEnlargeIfLine)
1545 {
1546 return false; // This is just one point or line and zooming has no effect.
1547 }
1548
1549 // Looking along line and "theToEnlargeIfLine" is requested.
1550 // Fit view to see whole scene on rotation.
1551 aViewSizeXv = aViewSizeZv;
1552 aViewSizeYv = aViewSizeZv;
1553 }
1554
1555 const Standard_Real anAspect = Aspect();
1556 if (anAspect > 1.0)
1557 {
1558 SetScale (Max (aViewSizeXv / anAspect, aViewSizeYv));
1559 }
1560 else
1561 {
1562 SetScale (Max (aViewSizeXv, aViewSizeYv * anAspect));
1563 }
1564 return true;
1565 }
1566
1567 //=============================================================================
1568 //function : ZFitAll
1569 //purpose :
1570 //=============================================================================
ZFitAll(const Standard_Real theScaleFactor,const Bnd_Box & theMinMax,const Bnd_Box & theGraphicBB,Standard_Real & theZNear,Standard_Real & theZFar) const1571 bool Graphic3d_Camera::ZFitAll (const Standard_Real theScaleFactor,
1572 const Bnd_Box& theMinMax,
1573 const Bnd_Box& theGraphicBB,
1574 Standard_Real& theZNear,
1575 Standard_Real& theZFar) const
1576 {
1577 Standard_ASSERT_RAISE (theScaleFactor > 0.0, "Zero or negative scale factor is not allowed.");
1578
1579 // Method changes zNear and zFar parameters of camera so as to fit graphical structures
1580 // by their graphical boundaries. It precisely fits min max boundaries of primary application
1581 // objects (second argument), while it can sacrifice the real graphical boundaries of the
1582 // scene with infinite or helper objects (third argument) for the sake of perspective projection.
1583 if (theGraphicBB.IsVoid())
1584 {
1585 theZNear = DEFAULT_ZNEAR;
1586 theZFar = DEFAULT_ZFAR;
1587 return false;
1588 }
1589
1590 // Measure depth of boundary points from camera eye.
1591 NCollection_Sequence<gp_Pnt> aPntsToMeasure;
1592
1593 Standard_Real aGraphicBB[6];
1594 theGraphicBB.Get (aGraphicBB[0], aGraphicBB[1], aGraphicBB[2], aGraphicBB[3], aGraphicBB[4], aGraphicBB[5]);
1595
1596 aPntsToMeasure.Append (gp_Pnt (aGraphicBB[0], aGraphicBB[1], aGraphicBB[2]));
1597 aPntsToMeasure.Append (gp_Pnt (aGraphicBB[0], aGraphicBB[1], aGraphicBB[5]));
1598 aPntsToMeasure.Append (gp_Pnt (aGraphicBB[0], aGraphicBB[4], aGraphicBB[2]));
1599 aPntsToMeasure.Append (gp_Pnt (aGraphicBB[0], aGraphicBB[4], aGraphicBB[5]));
1600 aPntsToMeasure.Append (gp_Pnt (aGraphicBB[3], aGraphicBB[1], aGraphicBB[2]));
1601 aPntsToMeasure.Append (gp_Pnt (aGraphicBB[3], aGraphicBB[1], aGraphicBB[5]));
1602 aPntsToMeasure.Append (gp_Pnt (aGraphicBB[3], aGraphicBB[4], aGraphicBB[2]));
1603 aPntsToMeasure.Append (gp_Pnt (aGraphicBB[3], aGraphicBB[4], aGraphicBB[5]));
1604
1605 Standard_Boolean isFiniteMinMax = !theMinMax.IsVoid() && !theMinMax.IsWhole();
1606
1607 if (isFiniteMinMax)
1608 {
1609 Standard_Real aMinMax[6];
1610 theMinMax.Get (aMinMax[0], aMinMax[1], aMinMax[2], aMinMax[3], aMinMax[4], aMinMax[5]);
1611
1612 aPntsToMeasure.Append (gp_Pnt (aMinMax[0], aMinMax[1], aMinMax[2]));
1613 aPntsToMeasure.Append (gp_Pnt (aMinMax[0], aMinMax[1], aMinMax[5]));
1614 aPntsToMeasure.Append (gp_Pnt (aMinMax[0], aMinMax[4], aMinMax[2]));
1615 aPntsToMeasure.Append (gp_Pnt (aMinMax[0], aMinMax[4], aMinMax[5]));
1616 aPntsToMeasure.Append (gp_Pnt (aMinMax[3], aMinMax[1], aMinMax[2]));
1617 aPntsToMeasure.Append (gp_Pnt (aMinMax[3], aMinMax[1], aMinMax[5]));
1618 aPntsToMeasure.Append (gp_Pnt (aMinMax[3], aMinMax[4], aMinMax[2]));
1619 aPntsToMeasure.Append (gp_Pnt (aMinMax[3], aMinMax[4], aMinMax[5]));
1620 }
1621
1622 // Camera eye plane.
1623 gp_Dir aCamDir = Direction();
1624 gp_Pnt aCamEye = myEye;
1625 gp_Pln aCamPln (aCamEye, aCamDir);
1626
1627 Standard_Real aModelMinDist = RealLast();
1628 Standard_Real aModelMaxDist = RealFirst();
1629 Standard_Real aGraphMinDist = RealLast();
1630 Standard_Real aGraphMaxDist = RealFirst();
1631
1632 const gp_XYZ& anAxialScale = myAxialScale;
1633
1634 // Get minimum and maximum distances to the eye plane.
1635 Standard_Integer aCounter = 0;
1636 NCollection_Sequence<gp_Pnt>::Iterator aPntIt(aPntsToMeasure);
1637 for (; aPntIt.More(); aPntIt.Next())
1638 {
1639 gp_Pnt aMeasurePnt = aPntIt.Value();
1640
1641 aMeasurePnt = gp_Pnt (aMeasurePnt.X() * anAxialScale.X(),
1642 aMeasurePnt.Y() * anAxialScale.Y(),
1643 aMeasurePnt.Z() * anAxialScale.Z());
1644
1645 Standard_Real aDistance = aCamPln.Distance (aMeasurePnt);
1646
1647 // Check if the camera is intruded into the scene.
1648 gp_Vec aVecToMeasurePnt (aCamEye, aMeasurePnt);
1649 if (aVecToMeasurePnt.Magnitude() > gp::Resolution()
1650 && aCamDir.IsOpposite (aVecToMeasurePnt, M_PI * 0.5))
1651 {
1652 aDistance *= -1;
1653 }
1654
1655 // The first eight points are from theGraphicBB, the last eight points are from theMinMax (can be absent).
1656 Standard_Real& aChangeMinDist = aCounter >= 8 ? aModelMinDist : aGraphMinDist;
1657 Standard_Real& aChangeMaxDist = aCounter >= 8 ? aModelMaxDist : aGraphMaxDist;
1658 aChangeMinDist = Min (aDistance, aChangeMinDist);
1659 aChangeMaxDist = Max (aDistance, aChangeMaxDist);
1660 aCounter++;
1661 }
1662
1663 // Compute depth of bounding box center.
1664 Standard_Real aMidDepth = (aGraphMinDist + aGraphMaxDist) * 0.5;
1665 Standard_Real aHalfDepth = (aGraphMaxDist - aGraphMinDist) * 0.5;
1666
1667 // Compute enlarged or shrank near and far z ranges.
1668 Standard_Real aZNear = aMidDepth - aHalfDepth * theScaleFactor;
1669 Standard_Real aZFar = aMidDepth + aHalfDepth * theScaleFactor;
1670
1671 if (!IsOrthographic())
1672 {
1673 // Everything is behind the perspective camera.
1674 if (aZFar < zEpsilon())
1675 {
1676 theZNear = DEFAULT_ZNEAR;
1677 theZFar = DEFAULT_ZFAR;
1678 return false;
1679 }
1680 }
1681
1682 //
1683 // Consider clipping errors due to double to single precision floating-point conversion.
1684 //
1685
1686 // Model to view transformation performs translation of points against eye position
1687 // in three dimensions. Both point coordinate and eye position values are converted from
1688 // double to single precision floating point numbers producing conversion errors.
1689 // Epsilon (Mod) * 3.0 should safely compensate precision error for z coordinate after
1690 // translation assuming that the:
1691 // Epsilon (Eye.Mod()) * 3.0 > Epsilon (Eye.X()) + Epsilon (Eye.Y()) + Epsilon (Eye.Z()).
1692 Standard_Real aEyeConf = 3.0 * zEpsilon (myEye.XYZ().Modulus());
1693
1694 // Model to view transformation performs rotation of points according to view direction.
1695 // New z coordinate is computed as a multiplication of point's x, y, z coordinates by the
1696 // "forward" direction vector's x, y, z coordinates. Both point's and "z" direction vector's
1697 // values are converted from double to single precision floating point numbers producing
1698 // conversion errors.
1699 // Epsilon (Mod) * 6.0 should safely compensate the precision errors for the multiplication
1700 // of point coordinates by direction vector.
1701 gp_Pnt aGraphicMin = theGraphicBB.CornerMin();
1702 gp_Pnt aGraphicMax = theGraphicBB.CornerMax();
1703
1704 Standard_Real aModelConf = 6.0 * zEpsilon (aGraphicMin.XYZ().Modulus()) +
1705 6.0 * zEpsilon (aGraphicMax.XYZ().Modulus());
1706
1707 // Compensate floating point conversion errors by increasing zNear, zFar to avoid clipping.
1708 aZNear -= zEpsilon (aZNear) + aEyeConf + aModelConf;
1709 aZFar += zEpsilon (aZFar) + aEyeConf + aModelConf;
1710
1711 if (!IsOrthographic())
1712 {
1713 // For perspective projection, the value of z in normalized device coordinates is non-linear
1714 // function of eye z coordinate. For fixed-point depth representation resolution of z in
1715 // model-view space will grow towards zFar plane and its scale depends mostly on how far is zNear
1716 // against camera's eye. The purpose of the code below is to select most appropriate zNear distance
1717 // to balance between clipping (less zNear, more chances to observe closely small models without clipping)
1718 // and resolution of depth. A well applicable criteria to this is a ratio between resolution of z at center
1719 // of model boundaries and the distance to that center point. The ratio is chosen empirically and validated
1720 // by tests database. It is considered to be ~0.001 (0.1%) for 24 bit depth buffer, for less depth bitness
1721 // the zNear will be placed similarly giving lower resolution.
1722 // Approximation of the formula for respectively large z range is:
1723 // zNear = [z * (1 + k) / (k * c)],
1724 // where:
1725 // z - distance to center of model boundaries;
1726 // k - chosen ratio, c - capacity of depth buffer;
1727 // k = 0.001, k * c = 1677.216, (1 + k) / (k * c) ~ 5.97E-4
1728 //
1729 // The function uses center of model boundaries computed from "theMinMax" boundaries (instead of using real
1730 // graphical boundaries of all displayed objects). That means that it can sacrifice resolution of presentation
1731 // of non primary ("infinite") application graphical objects in favor of better perspective projection of the
1732 // small applicative objects measured with "theMinMax" values.
1733 Standard_Real aZRange = isFiniteMinMax ? aModelMaxDist - aModelMinDist : aGraphMaxDist - aGraphMinDist;
1734 Standard_Real aZMin = isFiniteMinMax ? aModelMinDist : aGraphMinDist;
1735 Standard_Real aZ = aZMin < 0 ? aZRange / 2.0 : aZRange / 2.0 + aZMin;
1736 Standard_Real aZNearMin = aZ * 5.97E-4;
1737 if (aZNear < aZNearMin)
1738 {
1739 // Clip zNear according to the minimum value matching the quality.
1740 aZNear = aZNearMin;
1741 if (aZFar < aZNear)
1742 {
1743 aZFar = aZNear;
1744 }
1745 }
1746 else
1747 {
1748 // Compensate zNear conversion errors for perspective projection.
1749 aZNear -= aZFar * zEpsilon (aZNear) / (aZFar - zEpsilon (aZNear));
1750 }
1751
1752 // Compensate zFar conversion errors for perspective projection.
1753 aZFar += zEpsilon (aZFar);
1754
1755 // Ensure that after all the zNear is not a negative value.
1756 if (aZNear < zEpsilon())
1757 {
1758 aZNear = zEpsilon();
1759 }
1760 Standard_ASSERT_RAISE (aZFar > aZNear, "ZFar should be greater than ZNear");
1761 }
1762
1763 theZNear = aZNear;
1764 theZFar = aZFar;
1765 Standard_ASSERT_RAISE (aZFar > aZNear, "ZFar should be greater than ZNear");
1766 return true;
1767 }
1768
1769 //=============================================================================
1770 //function : Interpolate
1771 //purpose :
1772 //=============================================================================
Interpolate(const Handle (Graphic3d_Camera)& theStart,const Handle (Graphic3d_Camera)& theEnd,const double theT,Handle (Graphic3d_Camera)& theCamera)1773 void Graphic3d_Camera::Interpolate (const Handle(Graphic3d_Camera)& theStart,
1774 const Handle(Graphic3d_Camera)& theEnd,
1775 const double theT,
1776 Handle(Graphic3d_Camera)& theCamera)
1777 {
1778 if (Abs (theT - 1.0) < Precision::Confusion())
1779 {
1780 // just copy end-point transformation
1781 theCamera->Copy (theEnd);
1782 return;
1783 }
1784
1785 theCamera->Copy (theStart);
1786 if (Abs (theT - 0.0) < Precision::Confusion())
1787 {
1788 return;
1789 }
1790
1791 // apply rotation
1792 {
1793 gp_Ax3 aCamStart = cameraToAx3 (*theStart);
1794 gp_Ax3 aCamEnd = cameraToAx3 (*theEnd);
1795 gp_Trsf aTrsfStart, aTrsfEnd;
1796 aTrsfStart.SetTransformation (aCamStart, gp::XOY());
1797 aTrsfEnd .SetTransformation (aCamEnd, gp::XOY());
1798
1799 gp_Quaternion aRotStart = aTrsfStart.GetRotation();
1800 gp_Quaternion aRotEnd = aTrsfEnd .GetRotation();
1801 gp_Quaternion aRotDelta = aRotEnd * aRotStart.Inverted();
1802 gp_Quaternion aRot = gp_QuaternionNLerp::Interpolate (gp_Quaternion(), aRotDelta, theT);
1803 gp_Trsf aTrsfRot;
1804 aTrsfRot.SetRotation (aRot);
1805 theCamera->Transform (aTrsfRot);
1806 }
1807
1808 // apply translation
1809 {
1810 gp_XYZ aCenter = NCollection_Lerp<gp_XYZ>::Interpolate (theStart->Center().XYZ(), theEnd->Center().XYZ(), theT);
1811 gp_XYZ anEye = NCollection_Lerp<gp_XYZ>::Interpolate (theStart->Eye().XYZ(), theEnd->Eye().XYZ(), theT);
1812 gp_XYZ anAnchor = aCenter;
1813 Standard_Real aKc = 0.0;
1814
1815 const Standard_Real aDeltaCenter = theStart->Center().Distance (theEnd->Center());
1816 const Standard_Real aDeltaEye = theStart->Eye() .Distance (theEnd->Eye());
1817 if (aDeltaEye <= gp::Resolution())
1818 {
1819 anAnchor = anEye;
1820 aKc = 1.0;
1821 }
1822 else if (aDeltaCenter > gp::Resolution())
1823 {
1824 aKc = aDeltaCenter / (aDeltaCenter + aDeltaEye);
1825
1826 const gp_XYZ anAnchorStart = NCollection_Lerp<gp_XYZ>::Interpolate (theStart->Center().XYZ(), theStart->Eye().XYZ(), aKc);
1827 const gp_XYZ anAnchorEnd = NCollection_Lerp<gp_XYZ>::Interpolate (theEnd ->Center().XYZ(), theEnd ->Eye().XYZ(), aKc);
1828 anAnchor = NCollection_Lerp<gp_XYZ>::Interpolate (anAnchorStart, anAnchorEnd, theT);
1829 }
1830
1831 const gp_Vec aDirEyeToCenter = theCamera->Direction();
1832 const Standard_Real aDistEyeCenterStart = theStart->Eye().Distance (theStart->Center());
1833 const Standard_Real aDistEyeCenterEnd = theEnd ->Eye().Distance (theEnd ->Center());
1834 const Standard_Real aDistEyeCenter = NCollection_Lerp<Standard_Real>::Interpolate (aDistEyeCenterStart, aDistEyeCenterEnd, theT);
1835 aCenter = anAnchor + aDirEyeToCenter.XYZ() * aDistEyeCenter * aKc;
1836 anEye = anAnchor - aDirEyeToCenter.XYZ() * aDistEyeCenter * (1.0 - aKc);
1837
1838 theCamera->SetEyeAndCenter (anEye, aCenter);
1839 }
1840
1841 // apply scaling
1842 if (Abs(theStart->Scale() - theEnd->Scale()) > Precision::Confusion()
1843 && theStart->IsOrthographic())
1844 {
1845 const Standard_Real aScale = NCollection_Lerp<Standard_Real>::Interpolate (theStart->Scale(), theEnd->Scale(), theT);
1846 theCamera->SetScale (aScale);
1847 }
1848 }
1849
1850 //=======================================================================
1851 //function : FrustumPoints
1852 //purpose :
1853 //=======================================================================
FrustumPoints(NCollection_Array1<Graphic3d_Vec3d> & thePoints,const Graphic3d_Mat4d & theModelWorld) const1854 void Graphic3d_Camera::FrustumPoints (NCollection_Array1<Graphic3d_Vec3d>& thePoints,
1855 const Graphic3d_Mat4d& theModelWorld) const
1856 {
1857 if (thePoints.Length() != FrustumVerticesNB)
1858 {
1859 thePoints.Resize (0, FrustumVerticesNB, Standard_False);
1860 }
1861
1862 const Graphic3d_Mat4d& aProjectionMat = ProjectionMatrix();
1863 const Graphic3d_Mat4d aWorldViewMat = OrientationMatrix() * theModelWorld;
1864
1865 Standard_Real nLeft = 0.0, nRight = 0.0, nTop = 0.0, nBottom = 0.0;
1866 Standard_Real fLeft = 0.0, fRight = 0.0, fTop = 0.0, fBottom = 0.0;
1867 Standard_Real aNear = myZNear, aFar = myZFar;
1868 if (!IsOrthographic())
1869 {
1870 // handle perspective projection
1871 // Near plane
1872 nLeft = aNear * (aProjectionMat.GetValue (0, 2) - 1.0) / aProjectionMat.GetValue (0, 0);
1873 nRight = aNear * (aProjectionMat.GetValue (0, 2) + 1.0) / aProjectionMat.GetValue (0, 0);
1874 nTop = aNear * (aProjectionMat.GetValue (1, 2) + 1.0) / aProjectionMat.GetValue (1, 1);
1875 nBottom = aNear * (aProjectionMat.GetValue (1, 2) - 1.0) / aProjectionMat.GetValue (1, 1);
1876 // Far plane
1877 fLeft = aFar * (aProjectionMat.GetValue (0, 2) - 1.0) / aProjectionMat.GetValue (0, 0);
1878 fRight = aFar * (aProjectionMat.GetValue (0, 2) + 1.0) / aProjectionMat.GetValue (0, 0);
1879 fTop = aFar * (aProjectionMat.GetValue (1, 2) + 1.0) / aProjectionMat.GetValue (1, 1);
1880 fBottom = aFar * (aProjectionMat.GetValue (1, 2) - 1.0) / aProjectionMat.GetValue (1, 1);
1881 }
1882 else
1883 {
1884 // handle orthographic projection
1885 // Near plane
1886 nLeft = ( 1.0 + aProjectionMat.GetValue (0, 3)) / (-aProjectionMat.GetValue (0, 0));
1887 fLeft = nLeft;
1888 nRight = ( 1.0 - aProjectionMat.GetValue (0, 3)) / aProjectionMat.GetValue (0, 0);
1889 fRight = nRight;
1890 nTop = ( 1.0 - aProjectionMat.GetValue (1, 3)) / aProjectionMat.GetValue (1, 1);
1891 fTop = nTop;
1892 nBottom = (-1.0 - aProjectionMat.GetValue (1, 3)) / aProjectionMat.GetValue (1, 1);
1893 fBottom = nBottom;
1894 }
1895
1896 Graphic3d_Vec4d aLeftTopNear (nLeft, nTop, -aNear, 1.0), aRightBottomFar (fRight, fBottom, -aFar, 1.0);
1897 Graphic3d_Vec4d aLeftBottomNear (nLeft, nBottom, -aNear, 1.0), aRightTopFar (fRight, fTop, -aFar, 1.0);
1898 Graphic3d_Vec4d aRightBottomNear (nRight, nBottom, -aNear, 1.0), aLeftTopFar (fLeft, fTop, -aFar, 1.0);
1899 Graphic3d_Vec4d aRightTopNear (nRight, nTop, -aNear, 1.0), aLeftBottomFar (fLeft, fBottom, -aFar, 1.0);
1900
1901 Graphic3d_Mat4d anInvWorldView;
1902 aWorldViewMat.Inverted (anInvWorldView);
1903
1904 Graphic3d_Vec4d aTmpPnt;
1905 aTmpPnt = anInvWorldView * aLeftTopNear;
1906 thePoints.SetValue (FrustumVert_LeftTopNear, aTmpPnt.xyz() / aTmpPnt.w());
1907 aTmpPnt = anInvWorldView * aRightBottomFar;
1908 thePoints.SetValue (FrustumVert_RightBottomFar, aTmpPnt.xyz() / aTmpPnt.w());
1909 aTmpPnt = anInvWorldView * aLeftBottomNear;
1910 thePoints.SetValue (FrustumVert_LeftBottomNear, aTmpPnt.xyz() / aTmpPnt.w());
1911 aTmpPnt = anInvWorldView * aRightTopFar;
1912 thePoints.SetValue (FrustumVert_RightTopFar, aTmpPnt.xyz() / aTmpPnt.w());
1913 aTmpPnt = anInvWorldView * aRightBottomNear;
1914 thePoints.SetValue (FrustumVert_RightBottomNear, aTmpPnt.xyz() / aTmpPnt.w());
1915 aTmpPnt = anInvWorldView * aLeftTopFar;
1916 thePoints.SetValue (FrustumVert_LeftTopFar, aTmpPnt.xyz() / aTmpPnt.w());
1917 aTmpPnt = anInvWorldView * aRightTopNear;
1918 thePoints.SetValue (FrustumVert_RightTopNear, aTmpPnt.xyz() / aTmpPnt.w());
1919 aTmpPnt = anInvWorldView * aLeftBottomFar;
1920 thePoints.SetValue (FrustumVert_LeftBottomFar, aTmpPnt.xyz() / aTmpPnt.w());
1921 }
1922
1923 //=======================================================================
1924 //function : DumpJson
1925 //purpose :
1926 //=======================================================================
DumpJson(Standard_OStream & theOStream,Standard_Integer theDepth) const1927 void Graphic3d_Camera::DumpJson (Standard_OStream& theOStream, Standard_Integer theDepth) const
1928 {
1929 OCCT_DUMP_TRANSIENT_CLASS_BEGIN (theOStream)
1930
1931 OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &myUp)
1932 OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &myDirection)
1933 OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &myEye)
1934
1935 OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myDistance)
1936
1937 OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &myAxialScale)
1938 OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myProjType)
1939 OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myFOVy)
1940 OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myZNear)
1941 OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myZFar)
1942 OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myAspect)
1943
1944 OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myScale)
1945 OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myZFocus)
1946 OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myZFocusType)
1947
1948 OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myIOD)
1949 OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myIODType)
1950
1951 OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &myTile)
1952 OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &myMatricesD)
1953 OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &myMatricesF)
1954 OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &myWorldViewProjState)
1955 }
1956