// minimum camera distance
static const Standard_Real MIN_DISTANCE = Pow (0.1, ShortRealDigits() - 2);
+
+ // z-range tolerance compatible with for floating point.
+ static Standard_Real zEpsilon()
+ {
+ return FLT_EPSILON;
+ }
+
+ // relative z-range tolerance compatible with for floating point.
+ static Standard_Real zEpsilon (const Standard_Real theValue)
+ {
+ Standard_Real aLogRadix = Log10 (Abs (theValue)) / Log10 (FLT_RADIX);
+ Standard_Real aExp = Floor (aLogRadix);
+ return FLT_EPSILON * Pow (FLT_RADIX, aExp);
+ };
};
// =======================================================================
{
Standard_ASSERT_RAISE (theScaleFactor > 0.0, "Zero or negative scale factor is not allowed.");
- // Method changes ZNear and ZFar planes of camera so as to fit the graphical structures
- // by their real boundaries (computed ignoring infinite flag) into the viewing volume.
- // In addition to the graphical boundaries, the usual min max used for fitting perspective
- // camera. To avoid numeric errors for perspective camera the negative ZNear values are
- // fixed using tolerance distance, relative to boundaries size. The tolerance distance
- // should be computed using information on boundaries of primary application actors,
- // (e.g. representing the displayed model) - to ensure that they are not unreasonably clipped.
- const Standard_ShortReal anEpsilon = 1e-4f;
-
+ // Method changes zNear and zFar parameters of camera so as to fit graphical structures
+ // by their graphical boundaries. It precisely fits min max boundaries of primary application
+ // objects (second argument), while it can sacrifice the real graphical boundaries of the
+ // scene with infinite or helper objects (third argument) for the sake of perspective projection.
if (theGraphicBB.IsVoid())
{
- // Precision factor used to add meaningful tolerance to
- // ZNear, ZFar values in order to avoid equality after type conversion
- // to ShortReal matrices type.
-
- Standard_Real aZFar = Distance() * 3.0;
- Standard_Real aZNear = 0.0;
-
- if (!IsOrthographic())
- {
- if (aZFar < anEpsilon)
- {
- aZNear = anEpsilon;
- aZFar = anEpsilon * 2.0;
- }
- else if (aZNear < aZFar * anEpsilon)
- {
- aZNear = aZFar * anEpsilon;
- }
- }
-
- SetZRange (aZNear, aZFar);
+ SetZRange (DEFAULT_ZNEAR, DEFAULT_ZFAR);
return;
}
- // Measure depth of boundary points from camera eye
+ // Measure depth of boundary points from camera eye.
NCollection_Sequence<gp_Pnt> aPntsToMeasure;
- Standard_Real aGraphicBB[6]; // real graphical boundaries (not accounting infinite flag).
+ Standard_Real aGraphicBB[6];
theGraphicBB.Get (aGraphicBB[0], aGraphicBB[1], aGraphicBB[2], aGraphicBB[3], aGraphicBB[4], aGraphicBB[5]);
aPntsToMeasure.Append (gp_Pnt (aGraphicBB[0], aGraphicBB[1], aGraphicBB[2]));
if (!theMinMax.IsVoid() && !theMinMax.IsWhole())
{
- Standard_Real aMinMax[6]; // applicative min max boundaries
+ Standard_Real aMinMax[6];
theMinMax.Get (aMinMax[0], aMinMax[1], aMinMax[2], aMinMax[3], aMinMax[4], aMinMax[5]);
aPntsToMeasure.Append (gp_Pnt (aMinMax[0], aMinMax[1], aMinMax[2]));
aPntsToMeasure.Append (gp_Pnt (aMinMax[3], aMinMax[4], aMinMax[5]));
}
- // Camera eye plane
+ // Camera eye plane.
gp_Dir aCamDir = Direction();
gp_Pnt aCamEye = myEye;
gp_Pln aCamPln (aCamEye, aCamDir);
const gp_XYZ& anAxialScale = myAxialScale;
- // Get minimum and maximum distances to the eye plane
+ // Get minimum and maximum distances to the eye plane.
Standard_Integer aCounter = 0;
NCollection_Sequence<gp_Pnt>::Iterator aPntIt(aPntsToMeasure);
for (; aPntIt.More(); aPntIt.Next())
Standard_Real aDistance = aCamPln.Distance (aMeasurePnt);
- // Check if the camera is intruded into the scene
+ // Check if the camera is intruded into the scene.
if (aCamDir.IsOpposite (gp_Vec (aCamEye, aMeasurePnt), M_PI * 0.5))
{
aDistance *= -1;
}
- // the first eight points are from theGraphicBB, the last eight points are from theMinMax
- // (they can be absent).
+ // The first eight points are from theGraphicBB, the last eight points are from theMinMax (can be absent).
Standard_Real& aChangeMinDist = aCounter >= 8 ? aModelMinDist : aGraphicMinDist;
Standard_Real& aChangeMaxDist = aCounter >= 8 ? aModelMaxDist : aGraphicMaxDist;
aChangeMinDist = Min (aDistance, aChangeMinDist);
aCounter++;
}
- // Compute depth of bounding box center
+ // Compute depth of bounding box center.
Standard_Real aMidDepth = (aGraphicMinDist + aGraphicMaxDist) * 0.5;
Standard_Real aHalfDepth = (aGraphicMaxDist - aGraphicMinDist) * 0.5;
- // Compute enlarged or shrank near and far z ranges
+ // Compute enlarged or shrank near and far z ranges.
Standard_Real aZNear = aMidDepth - aHalfDepth * theScaleFactor;
Standard_Real aZFar = aMidDepth + aHalfDepth * theScaleFactor;
- Standard_Real aZRange = Abs (aZFar - aZNear);
- Standard_Real aZConf = Max (static_cast <Standard_Real> (anEpsilon * aZRange),
- static_cast <Standard_Real> (anEpsilon));
-
- aZNear -= Abs (aZNear) * anEpsilon + aZConf;
- aZFar += Abs (aZFar) * anEpsilon + aZConf;
if (!IsOrthographic())
{
- if (aZFar > anEpsilon)
+ // Everything is behind the perspective camera.
+ if (aZFar < zEpsilon())
{
- // Choose between model distance and graphical distance, as the model boundaries
- // might be infinite if all structures have infinite flag.
- const Standard_Real aGraphicDepth = aGraphicMaxDist >= aGraphicMinDist
- ? aGraphicMaxDist - aGraphicMinDist : RealLast();
-
- const Standard_Real aModelDepth = aModelMaxDist >= aModelMinDist
- ? aModelMaxDist - aModelMinDist : RealLast();
-
- const Standard_Real aMinDepth = Min (aModelDepth, aGraphicDepth);
- const Standard_Real aZTol = Max (static_cast<Standard_Real> (anEpsilon * Abs (aMinDepth)),
- static_cast<Standard_Real> (anEpsilon));
- if (aZNear < aZTol)
- {
- aZNear = aZTol;
- }
+ SetZRange (DEFAULT_ZNEAR, DEFAULT_ZFAR);
+ return;
}
- else
+
+ // For better perspective the zNear value should not be less than zEpsilon (zFar).
+ // If zNear computed by graphical boundaries do not meet the rule (e.g. it is negative
+ // when computing it for grid) it could be increased up to minimum depth computed by
+ // application min max values. This means that z-fit can sacrifice presentation of
+ // non primary application graphical objects in favor of better perspective projection;
+ if (aZNear < zEpsilon (aZFar))
{
- aZNear = anEpsilon;
- aZFar = anEpsilon * 2.0;
+ // Otherwise it should be increased up to zEpsilon (1.0) to avoid clipping of primary
+ // graphical objects.
+ if (aModelMinDist < zEpsilon (aZFar))
+ {
+ aMidDepth = (aModelMinDist + aModelMaxDist) * 0.5;
+ aHalfDepth = (aModelMinDist - aModelMaxDist) * 0.5;
+ aZNear = Max (zEpsilon(), aMidDepth - aHalfDepth * theScaleFactor);
+ }
+ else
+ {
+ aZNear = zEpsilon (aZFar);
+ }
}
}
- if (aZFar < (aZNear + Abs (aZFar) * anEpsilon))
+ //
+ // Consider clipping errors due to double to single precision floating-point conversion.
+ //
+
+ // Model to view transformation performs translation of points against eye position
+ // in three dimensions. Both point coordinate and eye position values are converted from
+ // double to single precision floating point numbers producing conversion errors.
+ // Epsilon (Mod) * 3.0 should safely compensate precision error for z coordinate after
+ // translation assuming that the:
+ // Epsilon (Eye.Mod()) * 3.0 > Epsilon (Eye.X()) + Epsilon (Eye.Y()) + Epsilon (Eye.Z()).
+ Standard_Real aEyeConf = 3.0 * zEpsilon (myEye.XYZ().Modulus());
+
+ // Model to view transformation performs rotation of points according to view direction.
+ // New z coordinate is computed as a multiplication of point's x, y, z coordinates by the
+ // "forward" direction vector's x, y, z coordinates. Both point's and "z" direction vector's
+ // values are converted from double to single precision floating point numbers producing
+ // conversion errors.
+ // Epsilon (Mod) * 6.0 should safely compensate the precision errors for the multiplication
+ // of point coordinates by direction vector.
+ gp_Pnt aGraphicMin = theGraphicBB.CornerMin();
+ gp_Pnt aGraphicMax = theGraphicBB.CornerMax();
+
+ Standard_Real aModelConf = 6.0 * zEpsilon (aGraphicMin.XYZ().Modulus()) +
+ 6.0 * zEpsilon (aGraphicMax.XYZ().Modulus());
+
+ // Compensate floating point conversion errors by increasing zNear, zFar to avoid clipping.
+ aZNear -= zEpsilon (aZNear) + aEyeConf + aModelConf;
+ aZFar += zEpsilon (aZFar) + aEyeConf + aModelConf;
+
+ if (!IsOrthographic())
{
- aZFar = aZNear + Abs (aZFar) * anEpsilon;
+ // Compensate zNear, zFar conversion errors for perspective projection.
+ aZNear -= aZFar * zEpsilon (aZNear) / (aZFar - zEpsilon (aZNear));
+ aZFar += zEpsilon (aZFar);
+
+ // Ensure that after all the zNear is not a negative value.
+ if (aZNear < zEpsilon())
+ {
+ aZNear = zEpsilon();
+ }
}
SetZRange (aZNear, aZFar);
--- /dev/null
+puts "============"
+puts "CR25760"
+puts "============"
+puts ""
+#######################################################################
+# Visualization - precision factor added to ZNear, ZFar in method ZFitAll() of Graphic3d_Camera is not enough
+#######################################################################
+
+vinit View1 w=409 h=409
+vclear
+
+vclear
+vautozfit 0
+
+proc test3d {dstart} {
+
+ set proj1 { 0.47243081629544409 -0.39335870920278265 -0.78871924644244684}
+ set proj2 {-0.31828216872577886 0.17649241059446089 -0.93142197208020105}
+
+ for {set i 1} {$i <= 3} {incr i} {
+ for {set r 1} {$r <= 3} {incr r} {
+
+ set x [expr pow(100, $i)]
+ set y [expr pow( 70, $i)]
+ set z [expr pow( 50, $i)]
+ set dist [expr pow(100, $r)]
+
+ vclear
+ vertex v0 $x $y $z
+ vertex v1 [expr "$x + ($dist * [lindex $proj1 0])"] [expr "$y + ($dist * [lindex $proj1 1])"] [expr "$z + ($dist * [lindex $proj1 2])"]
+ vertex v2 [expr "$x + ($dist * [lindex $proj2 0])"] [expr "$y + ($dist * [lindex $proj2 1])"] [expr "$z + ($dist * [lindex $proj2 2])"]
+
+ for {set d [expr $dstart * {max ($x,$y,$z,$dist)}]} {$d <= 1e7} {set d [expr "abs ($d) * 1.2E5"]} {
+ for {set p 1} {$p <= 2} {incr p} {
+ set proj [set proj$p]
+
+ vremove -all
+ vdisplay v0
+ vdisplay v$p
+ vviewparams -eye [expr "$x - ($d * [lindex $proj 0])"] [expr "$y - ($d * [lindex $proj 1])"] [expr "$z - ($d * [lindex $proj 2])"] -at $x $y $z
+ vzfit
+
+ vremove -all
+ vdisplay v0
+ if { [checkcolor 204 204 1 1 0] != 1 } {
+ puts "Error: 3D projection test failed with the following parameters:"
+ vviewparams
+ vzrange
+ puts ""
+ puts "v1 x: $x"
+ puts "v1 y: $y"
+ puts "v1 z: $z"
+ puts "v2 x: [expr $x + ($dist * [lindex $proj 0])]"
+ puts "v2 y: [expr $y + ($dist * [lindex $proj 1])]"
+ puts "v2 z: [expr $z + ($dist * [lindex $proj 2])]"
+ puts ""
+ return 0
+ }
+
+ vremove -all
+ vdisplay v$p
+ if { [checkcolor 204 204 1 1 0] != 1 } {
+ puts "Error: 3D projection test failed with the following parameters:"
+ vviewparams
+ vzrange
+ puts ""
+ puts "v1 x: $x"
+ puts "v1 y: $y"
+ puts "v1 z: $z"
+ puts "v2 x: [expr $x + ($dist * [lindex $proj 0])]"
+ puts "v2 y: [expr $y + ($dist * [lindex $proj 1])]"
+ puts "v2 z: [expr $z + ($dist * [lindex $proj 2])]"
+ puts ""
+ return 0
+ }
+ }
+ }
+ }
+ }
+ return 1
+}
+
+set tcl_precision 16
+
+####################################################################
+# Test orthographic camera without frustum culling. #
+# Test camera with scale 1E-8 to avoid jittering. #
+####################################################################
+vcamera -ortho
+vviewparams -scale 1e-8
+vfrustumculling 0
+
+if { [test3d 1e-7] != 1 } {
+ puts "Error: 3D projection test failed: camera is orthographic, view frustum culling is OFF"
+}
+
+####################################################################
+# Test orthographic camera with frustum culling. #
+# Test camera with scale 1E-8 to avoid jittering. #
+####################################################################
+vcamera -ortho
+vviewparams -scale 1e-8
+vfrustumculling 1
+
+if { [test3d 1e-7] != 1 } {
+ puts "Error: 3D projection test failed: camera is orthographic, view frustum culling is ON"
+}
+
+####################################################################
+# Test perspective camera without frustum culling. #
+# Test camera with less starting distance 1.0 to avoid jittering. #
+####################################################################
+vcamera -persp
+vfrustumculling 0
+
+if { [test3d 1.0] != 1 } {
+ puts "Error: 3D projection test failed: camera is perspective, view frustum culling is OFF"
+}
+
+####################################################################
+# Test perspective camera with frustum culling. #
+# Test camera with less starting distance 1.0 to avoid jittering. #
+####################################################################
+vcamera -persp
+vfrustumculling 1
+
+if { [test3d 1.0] != 1 } {
+ puts "Error: 3D projection test failed: camera is perspective, view frustum culling is ON"
+}