diff --git a/src/rocky/Horizon.cpp b/src/rocky/Horizon.cpp index 211151e8e..d589101af 100644 --- a/src/rocky/Horizon.cpp +++ b/src/rocky/Horizon.cpp @@ -4,6 +4,7 @@ * MIT License */ #include "Horizon.h" +#include using namespace ROCKY_NAMESPACE; @@ -123,9 +124,21 @@ Horizon::isVisible(double x, double y, double z, double radius) const VT = target - _eye; double a = glm::dot(VT, -_eyeUnit); double b = a * _coneTan; - double c = sqrt(glm::dot(VT, VT) - a * a); + double VTdotVT = glm::dot(VT, VT); + double cSquared = VTdotVT - a * a; + + // Guard against numerical precision issues that could produce negative value + if (cSquared < 0.0) + cSquared = 0.0; + + double c = sqrt(cSquared); double d = c - b; double e = d * _coneCos; + + // Check for NaN in the calculation (indicates numerical instability) + if (std::isnan(e)) + return true; // Default to visible if we can't determine + if (e > -radius) return true; diff --git a/src/rocky/vsg/terrain/SurfaceNode.h b/src/rocky/vsg/terrain/SurfaceNode.h index cccd70915..2741fd547 100644 --- a/src/rocky/vsg/terrain/SurfaceNode.h +++ b/src/rocky/vsg/terrain/SurfaceNode.h @@ -113,16 +113,19 @@ namespace ROCKY_NAMESPACE auto viewID = rv.getCommandBuffer()->viewID; auto& horizon = (*_horizon)[viewID]; + // Use the bounding sphere radius as margin for more conservative culling + double cullRadius = worldBoundingSphere.radius; + if (_horizonCullingPoint_valid) { - return horizon.isVisible(_horizonCullingPoint); + return horizon.isVisible(_horizonCullingPoint, cullRadius); } else { for (int p = 0; p < 4; ++p) { auto& wp = _worldPoints[p]; - if (horizon.isVisible(wp.x, wp.y, wp.z)) + if (horizon.isVisible(wp.x, wp.y, wp.z, cullRadius)) return true; } return false;