From 2624ccd8ba673ba79a99d24357458d763ac1e4c3 Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Tue, 14 Jul 2020 17:55:25 +0100 Subject: Gracefully handle rounding issues --- src/BoundingBox.cpp | 16 ++++++++++------ src/Entities/Entity.cpp | 25 +++++++++++++++++-------- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/BoundingBox.cpp b/src/BoundingBox.cpp index 1356e165c..e60be3476 100644 --- a/src/BoundingBox.cpp +++ b/src/BoundingBox.cpp @@ -213,41 +213,45 @@ bool cBoundingBox::CalcLineIntersection(Vector3d a_Min, Vector3d a_Max, Vector3d return true; } + // Correction for floating point imprecision when calculating + // the coefficient to intersect a plane by slightly increasing it + static const auto Correction = 1.000001; + eBlockFace Face = BLOCK_FACE_NONE; double Coeff = Vector3d::NO_INTERSECTION; // Check each individual bbox face for intersection with the line, remember the one with the lowest coeff - double c = a_Line1.LineCoeffToXYPlane(a_Line2, a_Min.z); + double c = a_Line1.LineCoeffToXYPlane(a_Line2, a_Min.z) * Correction; if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c)) { Face = (a_Line1.z > a_Line2.z) ? BLOCK_FACE_ZP : BLOCK_FACE_ZM; Coeff = c; } - c = a_Line1.LineCoeffToXYPlane(a_Line2, a_Max.z); + c = a_Line1.LineCoeffToXYPlane(a_Line2, a_Max.z) * Correction; if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c)) { Face = (a_Line1.z > a_Line2.z) ? BLOCK_FACE_ZP : BLOCK_FACE_ZM; Coeff = c; } - c = a_Line1.LineCoeffToXZPlane(a_Line2, a_Min.y); + c = a_Line1.LineCoeffToXZPlane(a_Line2, a_Min.y) * Correction; if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c)) { Face = (a_Line1.y > a_Line2.y) ? BLOCK_FACE_YP : BLOCK_FACE_YM; Coeff = c; } - c = a_Line1.LineCoeffToXZPlane(a_Line2, a_Max.y); + c = a_Line1.LineCoeffToXZPlane(a_Line2, a_Max.y) * Correction; if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c)) { Face = (a_Line1.y > a_Line2.y) ? BLOCK_FACE_YP : BLOCK_FACE_YM; Coeff = c; } - c = a_Line1.LineCoeffToYZPlane(a_Line2, a_Min.x); + c = a_Line1.LineCoeffToYZPlane(a_Line2, a_Min.x) * Correction; if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c)) { Face = (a_Line1.x > a_Line2.x) ? BLOCK_FACE_XP : BLOCK_FACE_XM; Coeff = c; } - c = a_Line1.LineCoeffToYZPlane(a_Line2, a_Max.x); + c = a_Line1.LineCoeffToYZPlane(a_Line2, a_Max.x) * Correction; if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c)) { Face = (a_Line1.x > a_Line2.x) ? BLOCK_FACE_XP : BLOCK_FACE_XM; diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index 37ef925cf..1f270cff0 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -1067,9 +1067,15 @@ void cEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) // TODO: checks too much, very inefficient for (int i = bstart.x; i <= bend.x; i++) - for (int j = bstart.y; j <= bend.y; j++) - for (int k = bstart.z; k <= bend.z; k++) - Checks.emplace(Vector3i(i, j, k)); + { + for (int j = bstart.y; j <= bend.y; j++) + { + for (int k = bstart.z; k <= bend.z; k++) + { + Checks.emplace(Vector3i(i, j, k)); + } + } + } return Checks; } @@ -1146,15 +1152,18 @@ void cEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) eBlockFace Face; // Face hit cBoundingBox bb(a_BlockPos, a_BlockPos + Vector3i(1, 1, 1)); // Bounding box of the block hit + // First calculate line intersection if (!bb.CalcLineIntersection(m_Start, m_End, LineCoeff, Face)) { - // Math rounding errors have caused the calculation to miss the block completely, assume immediate hit - LineCoeff = 0; + // Maths rounding errors have caused the calculation to miss the block completely + m_HitCoords = m_End; + m_HitBlockFace = BLOCK_FACE_NONE; + return true; } - m_HitCoords = m_Start + (m_End - m_Start) * LineCoeff; // Point where projectile goes into the hit block - - TestBoundingBoxCollisionWithEnvironment(m_HitCoords); + // Check bbox collision, setting final values + // Line tracer hit position is used as a starting point: + TestBoundingBoxCollisionWithEnvironment(m_Start + (m_End - m_Start) * LineCoeff); return m_HitBlockFace != BLOCK_FACE_NONE; } -- cgit v1.2.3