Fix issue in VoronoiSimplex

This commit is contained in:
Daniel Chappuis 2016-06-27 18:50:12 +02:00
parent fd224ebaba
commit ccd33c2502
2 changed files with 83 additions and 62 deletions

View File

@ -63,7 +63,7 @@ void VoronoiSimplex::removePoint(int index) {
mNbPoints--; mNbPoints--;
mPoints[index] = mPoints[mNbPoints]; mPoints[index] = mPoints[mNbPoints];
mSuppPointsA[index] = mSuppPointsA[mNbPoints]; mSuppPointsA[index] = mSuppPointsA[mNbPoints];
mSuppPointsB[index] = mSuppPointsA[mNbPoints]; mSuppPointsB[index] = mSuppPointsB[mNbPoints];
} }
// Reduce the simplex (only keep vertices that participate to the point closest to the origin) // Reduce the simplex (only keep vertices that participate to the point closest to the origin)
@ -138,6 +138,7 @@ bool VoronoiSimplex::isAffinelyDependent() const {
case 3: return (mPoints[1] - mPoints[0]).cross(mPoints[2] - mPoints[0]).lengthSquare() <= epsilon; case 3: return (mPoints[1] - mPoints[0]).cross(mPoints[2] - mPoints[0]).lengthSquare() <= epsilon;
// Four points are independent if the tetrahedron volume is larger than zero // Four points are independent if the tetrahedron volume is larger than zero
// Test in three different ways (for more robustness)
case 4: return std::abs((mPoints[1] - mPoints[0]).dot((mPoints[2] - mPoints[0]).cross(mPoints[3] - mPoints[0]))) <= epsilon; case 4: return std::abs((mPoints[1] - mPoints[0]).dot((mPoints[2] - mPoints[0]).cross(mPoints[3] - mPoints[0]))) <= epsilon;
} }
@ -152,10 +153,10 @@ bool VoronoiSimplex::isAffinelyDependent() const {
void VoronoiSimplex::computeClosestPointsOfAandB(Vector3& pA, Vector3& pB) { void VoronoiSimplex::computeClosestPointsOfAandB(Vector3& pA, Vector3& pB) {
// Recompute the closest point (if necessary) // Recompute the closest point (if necessary)
recomputeClosestPoint(); //recomputeClosestPoint();
pA = mSuppPointsA; pA = mClosestSuppPointA;
pB = mSuppPointsB; pB = mClosestSuppPointB;
} }
// Recompute the closest point if the simplex has been modified // Recompute the closest point if the simplex has been modified
@ -195,38 +196,11 @@ bool VoronoiSimplex::recomputeClosestPoint() {
{ {
int bitsUsedPoints = 0; int bitsUsedPoints = 0;
float t;
// The simplex is a line AB (where A=mPoints[0] and B=mPoints[1]. // The simplex is a line AB (where A=mPoints[0] and B=mPoints[1].
// We need to find the point of that line closest to the origin // We need to find the point of that line closest to the origin
Vector3 AP = -mPoints[0]; computeClosestPointOnSegment(mPoints[0], mPoints[1], bitsUsedPoints, t);
Vector3 AB = mPoints[1] - mPoints[0];
decimal APDotAB = AP.dot(AB);
decimal t;
// If the closest point is on the side of A in the direction of B
if (APDotAB > decimal(0.0)) {
decimal lengthABSquare = AB.lengthSquare();
// If the closest point is on the segment AB
if (APDotAB < lengthABSquare) {
t = APDotAB / lengthABSquare;
bitsUsedPoints = 3; // 0011 (both A and B are used)
}
else { // If the origin is on the side of B that is not in the direction of A
// Therefore, the closest point is B
t = decimal(1.0);
bitsUsedPoints = 2; // 0010 (only B is used)
}
}
else { // If the origin is on the side of A that is not in the direction of B
// Therefore, the closest point of the line is A
t = decimal(0.0);
bitsUsedPoints = 1; // 0001 (only A is used)
}
// Compute the closest point // Compute the closest point
mClosestSuppPointA = mSuppPointsA[0] + t * (mSuppPointsA[1] - mSuppPointsA[0]); mClosestSuppPointA = mSuppPointsA[0] + t * (mSuppPointsA[1] - mSuppPointsA[0]);
@ -275,10 +249,11 @@ bool VoronoiSimplex::recomputeClosestPoint() {
int bitsUsedVertices = 0; int bitsUsedVertices = 0;
Vector2 baryCoordsAB; Vector2 baryCoordsAB;
Vector2 baryCoordsCD; Vector2 baryCoordsCD;
bool isDegenerate;
// Compute the point closest to the origin on the tetrahedron // Compute the point closest to the origin on the tetrahedron
bool isOutside = computeClosestPointOnTetrahedron(mPoints[0], mPoints[1], mPoints[2], mPoints[3], bool isOutside = computeClosestPointOnTetrahedron(mPoints[0], mPoints[1], mPoints[2], mPoints[3],
bitsUsedVertices, baryCoordsAB, baryCoordsCD); bitsUsedVertices, baryCoordsAB, baryCoordsCD, isDegenerate);
// If the origin is outside the tetrahedron // If the origin is outside the tetrahedron
if (isOutside) { if (isOutside) {
@ -298,22 +273,27 @@ bool VoronoiSimplex::recomputeClosestPoint() {
} }
else { else {
// The origin is inside the tetrahedron, therefore, the closest point // If it is a degenerate case
// is the origin if (isDegenerate) {
mIsClosestPointValid = false;
}
else {
setBarycentricCoords(0.0, 0.0, 0.0, 0.0); // The origin is inside the tetrahedron, therefore, the closest point
// is the origin
mClosestSuppPointA.setToZero(); setBarycentricCoords(0.0, 0.0, 0.0, 0.0);
mClosestSuppPointB.setToZero();
mClosestPoint.setToZero();
mIsClosestPointValid = true; mClosestSuppPointA.setToZero();
mClosestSuppPointB.setToZero();
mClosestPoint.setToZero();
mIsClosestPointValid = true;
}
break; break;
} }
mIsClosestPointValid = checkClosestPointValid(); mIsClosestPointValid = checkClosestPointValid();
} }
break; break;
} }
@ -322,11 +302,45 @@ bool VoronoiSimplex::recomputeClosestPoint() {
return mIsClosestPointValid; return mIsClosestPointValid;
} }
// Compute point of a line segment that is closest to the origin
void VoronoiSimplex::computeClosestPointOnSegment(const Vector3& a, const Vector3& b, int& bitUsedVertices,
float& t) const {
Vector3 AP = -a;
Vector3 AB = b - a;
decimal APDotAB = AP.dot(AB);
// If the closest point is on the side of A in the direction of B
if (APDotAB > decimal(0.0)) {
decimal lengthABSquare = AB.lengthSquare();
// If the closest point is on the segment AB
if (APDotAB < lengthABSquare) {
t = APDotAB / lengthABSquare;
bitUsedVertices = 3; // 0011 (both A and B are used)
}
else { // If the origin is on the side of B that is not in the direction of A
// Therefore, the closest point is B
t = decimal(1.0);
bitUsedVertices = 2; // 0010 (only B is used)
}
}
else { // If the origin is on the side of A that is not in the direction of B
// Therefore, the closest point of the line is A
t = decimal(0.0);
bitUsedVertices = 1; // 0001 (only A is used)
}
}
// Compute point on a triangle that is closest to the origin // Compute point on a triangle that is closest to the origin
/// This implementation is based on the one in the book /// This implementation is based on the one in the book
/// "Real-Time Collision Detection" by Christer Ericson. /// "Real-Time Collision Detection" by Christer Ericson.
void VoronoiSimplex::computeClosestPointOnTriangle(const Vector3& a, const Vector3& b, void VoronoiSimplex::computeClosestPointOnTriangle(const Vector3& a, const Vector3& b, const Vector3& c,
const Vector3& c, int& bitsUsedVertices, Vector3& baryCoordsABC) const { int& bitsUsedVertices, Vector3& baryCoordsABC) const {
// Check if the origin is in the Voronoi region of vertex A // Check if the origin is in the Voronoi region of vertex A
Vector3 ab = b - a; Vector3 ab = b - a;
@ -438,7 +452,9 @@ void VoronoiSimplex::computeClosestPointOnTriangle(const Vector3& a, const Vecto
/// This method returns true if the origin is outside the tetrahedron. /// This method returns true if the origin is outside the tetrahedron.
bool VoronoiSimplex::computeClosestPointOnTetrahedron(const Vector3& a, const Vector3& b, const Vector3& c, bool VoronoiSimplex::computeClosestPointOnTetrahedron(const Vector3& a, const Vector3& b, const Vector3& c,
const Vector3& d, int& bitsUsedPoints, Vector2& baryCoordsAB, const Vector3& d, int& bitsUsedPoints, Vector2& baryCoordsAB,
Vector2& baryCoordsCD) const { Vector2& baryCoordsCD, bool& isDegenerate) const {
isDegenerate = false;
// Start as if the origin was inside the tetrahedron // Start as if the origin was inside the tetrahedron
bitsUsedPoints = 15; // 1111 (A, B, C and D are used) bitsUsedPoints = 15; // 1111 (A, B, C and D are used)
@ -451,6 +467,15 @@ bool VoronoiSimplex::computeClosestPointOnTetrahedron(const Vector3& a, const Ve
int isOriginOutsideFaceADB = testOriginOutsideOfPlane(a, d, b, c); int isOriginOutsideFaceADB = testOriginOutsideOfPlane(a, d, b, c);
int isOriginOutsideFaceBDC = testOriginOutsideOfPlane(b, d, c, a); int isOriginOutsideFaceBDC = testOriginOutsideOfPlane(b, d, c, a);
// If we have a degenerate tetrahedron
if (isOriginOutsideFaceABC < 0 || isOriginOutsideFaceACD < 0 ||
isOriginOutsideFaceADB < 0 || isOriginOutsideFaceBDC < 0) {
// The tetrahedron is degenerate
isDegenerate = true;
return false;
}
if (isOriginOutsideFaceABC == 0 && isOriginOutsideFaceACD == 0 && if (isOriginOutsideFaceABC == 0 && isOriginOutsideFaceACD == 0 &&
isOriginOutsideFaceADB == 0 && isOriginOutsideFaceBDC == 0) { isOriginOutsideFaceADB == 0 && isOriginOutsideFaceBDC == 0) {
@ -574,31 +599,20 @@ int VoronoiSimplex::testOriginOutsideOfPlane(const Vector3& a, const Vector3& b,
decimal signp = (-a).dot(n); decimal signp = (-a).dot(n);
decimal signd = (d-a).dot(n); decimal signd = (d-a).dot(n);
// This assert is raised if the tetrahedron is degenerate (all points on the same plane) // If the tetrahedron is degenerate (all points on the same plane)
// This should never happen because after a point is added into the simplex, the user // This should never happen because after a point is added into the simplex, the user
// of this class must check that the simplex is not affinely dependent using the // of this class must check that the simplex is not affinely dependent using the
// isAffinelyDependent() method // isAffinelyDependent() method
assert(signd * signd > epsilon * epsilon); if(signd * signd < epsilon * epsilon) {
return -1;
}
return signp * signd < decimal(0.0); return signp * signd < decimal(0.0);
} }
// Backup the closest point // Backup the closest point
void VoronoiSimplex::backupClosestPointInSimplex(Vector3& v) { void VoronoiSimplex::backupClosestPointInSimplex(Vector3& v) {
decimal minDistSquare = DECIMAL_LARGEST; v = mClosestPoint;
Bits bit;
for (bit = mAllBits; bit != 0x0; bit--) {
if (isSubset(bit, mAllBits) && isProperSubset(bit)) {
Vector3 u = computeClosestPointForSubset(bit);
decimal distSquare = u.dot(u);
if (distSquare < minDistSquare) {
minDistSquare = distSquare;
mBitsCurrentSimplex = bit;
v = u;
}
}
}
} }
// Return the maximum squared length of a point // Return the maximum squared length of a point

View File

@ -104,6 +104,10 @@ class VoronoiSimplex {
/// Return true if the /// Return true if the
bool checkClosestPointValid() const; bool checkClosestPointValid() const;
/// Compute point of a line segment that is closest to the origin
void computeClosestPointOnSegment(const Vector3& a, const Vector3& b, int& bitUsedVertices,
float& t) const;
/// Compute point of a triangle that is closest to the origin /// Compute point of a triangle that is closest to the origin
void computeClosestPointOnTriangle(const Vector3& a, const Vector3& b, void computeClosestPointOnTriangle(const Vector3& a, const Vector3& b,
const Vector3& c, int& bitsUsedPoints, Vector3& baryCoordsABC) const; const Vector3& c, int& bitsUsedPoints, Vector3& baryCoordsABC) const;
@ -111,7 +115,8 @@ class VoronoiSimplex {
/// Compute point of a tetrahedron that is closest to the origin /// Compute point of a tetrahedron that is closest to the origin
bool computeClosestPointOnTetrahedron(const Vector3& a, const Vector3& b, bool computeClosestPointOnTetrahedron(const Vector3& a, const Vector3& b,
const Vector3& c, const Vector3& d, const Vector3& c, const Vector3& d,
int& bitsUsedPoints, Vector2& baryCoordsAB, Vector2& baryCoordsCD) const; int& bitsUsedPoints, Vector2& baryCoordsAB, Vector2& baryCoordsCD,
bool& isDegenerate) const;
/// Test if a point is outside of the plane given by the points of the triangle (a, b, c) /// Test if a point is outside of the plane given by the points of the triangle (a, b, c)
int testOriginOutsideOfPlane(const Vector3& a, const Vector3& b, const Vector3& c, const Vector3& d) const; int testOriginOutsideOfPlane(const Vector3& a, const Vector3& b, const Vector3& c, const Vector3& d) const;
@ -198,4 +203,6 @@ inline bool VoronoiSimplex::checkClosestPointValid() const {
mBarycentricCoords[2] >= decimal(0.0) && mBarycentricCoords[3] >= decimal(0.0); mBarycentricCoords[2] >= decimal(0.0) && mBarycentricCoords[3] >= decimal(0.0);
} }
}
#endif #endif