summaryrefslogtreecommitdiff
path: root/thirdparty
diff options
context:
space:
mode:
Diffstat (limited to 'thirdparty')
-rw-r--r--thirdparty/README.md17
-rw-r--r--thirdparty/bullet/BulletCollision/CollisionDispatch/btBox2dBox2dCollisionAlgorithm.cpp1
-rw-r--r--thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionObject.h3
-rw-r--r--thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionWorld.cpp6
-rw-r--r--thirdparty/bullet/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.cpp135
-rw-r--r--thirdparty/bullet/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h47
-rw-r--r--thirdparty/bullet/BulletDynamics/Featherstone/btMultiBody.cpp13
-rw-r--r--thirdparty/bullet/BulletDynamics/Featherstone/btMultiBody.h20
-rw-r--r--thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyConstraint.cpp5
-rw-r--r--thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyConstraint.h2
-rw-r--r--thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.cpp9
-rw-r--r--thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyJointMotor.cpp1
-rw-r--r--thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodySphericalJointMotor.cpp21
-rw-r--r--thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodySphericalJointMotor.h49
-rw-r--r--thirdparty/bullet/BulletDynamics/MLCPSolvers/btMLCPSolver.cpp4
-rw-r--r--thirdparty/bullet/BulletSoftBody/btDeformableBodySolver.cpp8
-rw-r--r--thirdparty/bullet/BulletSoftBody/btDeformableMousePickingForce.h2
-rw-r--r--thirdparty/bullet/BulletSoftBody/btSoftBody.h4
-rw-r--r--thirdparty/bullet/LinearMath/btIDebugDraw.h6
-rw-r--r--thirdparty/bullet/LinearMath/btScalar.h2
-rw-r--r--thirdparty/bullet/LinearMath/btSerializer.h7
-rw-r--r--thirdparty/bullet/VERSION.txt1
-rw-r--r--thirdparty/bullet/patches/bullet-fix-warnings.patch42
-rw-r--r--thirdparty/doctest/LICENSE.txt2
-rw-r--r--thirdparty/doctest/doctest.h586
-rw-r--r--thirdparty/fonts/Tamsyn10x20.pngbin270 -> 0 bytes
-rw-r--r--thirdparty/fonts/Tamsyn5x9.pngbin175 -> 0 bytes
-rw-r--r--thirdparty/meshoptimizer/patches/attribute-aware-simplify-distance-only-metric.patch176
-rw-r--r--thirdparty/meshoptimizer/simplifier.cpp62
29 files changed, 946 insertions, 285 deletions
diff --git a/thirdparty/README.md b/thirdparty/README.md
index 2d24beec15..23380b6413 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -20,13 +20,15 @@ Files extracted from upstream source:
## bullet
- Upstream: https://github.com/bulletphysics/bullet3
-- Version: 3.08 (df09fd9ed37e365ceae884ca7f620b61607dae2e, 2020)
+- Version: 3.17 (ebe1916b90acae8b13cd8c6b637d8327cdc64e94, 2021)
- License: zlib
Files extracted from upstream source:
-- src/* apart from CMakeLists.txt and premake4.lua files
-- LICENSE.txt
+- `src/*` apart from CMakeLists.txt and premake4.lua files
+- `LICENSE.txt`, and `VERSION` as `VERSION.txt`
+
+Includes a warning fix which should be upstreamed soon (see patch in `patches`).
## certs
@@ -136,11 +138,6 @@ Files extracted from upstream source:
* Upstream: https://fonts.google.com/specimen/Open+Sans
* Version: 1.10 (downloaded from Google Fonts in February 2021)
* License: Apache 2.0
-- `Tamsyn*.png`:
- * Upstream: http://www.fial.com/~scott/tamsyn-font/
- * Version: 1.11 (2015)
- * License: Tamsyn
- * Comment: Extracted "0..9,A..F" characters for hex code printing.
## freetype
@@ -371,7 +368,9 @@ Files extracted from upstream repository:
- `LICENSE.md`.
An [experimental upstream feature](https://github.com/zeux/meshoptimizer/tree/simplify-attr),
-has been backported, see patch in `patches` directory.
+has been backported. On top of that, it was modified to report only distance error metrics
+instead of a combination of distance and attribute errors. Patches for both changes can be
+found in the `patches` directory.
## miniupnpc
diff --git a/thirdparty/bullet/BulletCollision/CollisionDispatch/btBox2dBox2dCollisionAlgorithm.cpp b/thirdparty/bullet/BulletCollision/CollisionDispatch/btBox2dBox2dCollisionAlgorithm.cpp
index c79623bd57..6873a95d90 100644
--- a/thirdparty/bullet/BulletCollision/CollisionDispatch/btBox2dBox2dCollisionAlgorithm.cpp
+++ b/thirdparty/bullet/BulletCollision/CollisionDispatch/btBox2dBox2dCollisionAlgorithm.cpp
@@ -80,6 +80,7 @@ struct ClipVertex
btVector3 v;
int id;
//b2ContactID id;
+ //b2ContactID id;
};
#define b2Dot(a, b) (a).dot(b)
diff --git a/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionObject.h b/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionObject.h
index e085c40892..dbe82fd61f 100644
--- a/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionObject.h
+++ b/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionObject.h
@@ -24,6 +24,7 @@ subject to the following restrictions:
#define WANTS_DEACTIVATION 3
#define DISABLE_DEACTIVATION 4
#define DISABLE_SIMULATION 5
+#define FIXED_BASE_MULTI_BODY 6
struct btBroadphaseProxy;
class btCollisionShape;
@@ -304,7 +305,7 @@ public:
SIMD_FORCE_INLINE bool isActive() const
{
- return ((getActivationState() != ISLAND_SLEEPING) && (getActivationState() != DISABLE_SIMULATION));
+ return ((getActivationState() != FIXED_BASE_MULTI_BODY) && (getActivationState() != ISLAND_SLEEPING) && (getActivationState() != DISABLE_SIMULATION));
}
void setRestitution(btScalar rest)
diff --git a/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionWorld.cpp b/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionWorld.cpp
index 71184f36ac..f74dcabc54 100644
--- a/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionWorld.cpp
+++ b/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionWorld.cpp
@@ -1037,7 +1037,7 @@ struct btSingleSweepCallback : public btBroadphaseRayCallback
m_castShape(castShape)
{
btVector3 unnormalizedRayDir = (m_convexToTrans.getOrigin() - m_convexFromTrans.getOrigin());
- btVector3 rayDir = unnormalizedRayDir.normalized();
+ btVector3 rayDir = unnormalizedRayDir.fuzzyZero() ? btVector3(btScalar(0.0), btScalar(0.0), btScalar(0.0)) : unnormalizedRayDir.normalized();
///what about division by zero? --> just set rayDirection[i] to INF/BT_LARGE_FLOAT
m_rayDirectionInverse[0] = rayDir[0] == btScalar(0.0) ? btScalar(BT_LARGE_FLOAT) : btScalar(1.0) / rayDir[0];
m_rayDirectionInverse[1] = rayDir[1] == btScalar(0.0) ? btScalar(BT_LARGE_FLOAT) : btScalar(1.0) / rayDir[1];
@@ -1294,9 +1294,7 @@ public:
btVector3 normalColor(1, 1, 0);
m_debugDrawer->drawLine(center, center + normal, normalColor);
}
- m_debugDrawer->drawLine(wv0, wv1, m_color);
- m_debugDrawer->drawLine(wv1, wv2, m_color);
- m_debugDrawer->drawLine(wv2, wv0, m_color);
+ m_debugDrawer->drawTriangle(wv0, wv1, wv2, m_color, 1.0);
}
};
diff --git a/thirdparty/bullet/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.cpp b/thirdparty/bullet/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.cpp
index cab6980b65..01bf7f67f5 100644
--- a/thirdparty/bullet/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.cpp
+++ b/thirdparty/bullet/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.cpp
@@ -18,12 +18,57 @@ subject to the following restrictions:
#include "LinearMath/btTransformUtil.h"
btHeightfieldTerrainShape::btHeightfieldTerrainShape(
+ int heightStickWidth, int heightStickLength,
+ const float* heightfieldData, btScalar minHeight, btScalar maxHeight,
+ int upAxis, bool flipQuadEdges)
+ : m_userValue3(0), m_triangleInfoMap(0)
+{
+ initialize(heightStickWidth, heightStickLength, heightfieldData,
+ /*heightScale=*/1, minHeight, maxHeight, upAxis, PHY_FLOAT,
+ flipQuadEdges);
+}
+
+btHeightfieldTerrainShape::btHeightfieldTerrainShape(
+ int heightStickWidth, int heightStickLength, const double* heightfieldData,
+ btScalar minHeight, btScalar maxHeight, int upAxis, bool flipQuadEdges)
+ : m_userValue3(0), m_triangleInfoMap(0)
+{
+ initialize(heightStickWidth, heightStickLength, heightfieldData,
+ /*heightScale=*/1, minHeight, maxHeight, upAxis, PHY_DOUBLE,
+ flipQuadEdges);
+}
+
+btHeightfieldTerrainShape::btHeightfieldTerrainShape(
+ int heightStickWidth, int heightStickLength, const short* heightfieldData, btScalar heightScale,
+ btScalar minHeight, btScalar maxHeight, int upAxis, bool flipQuadEdges)
+ : m_userValue3(0), m_triangleInfoMap(0)
+{
+ initialize(heightStickWidth, heightStickLength, heightfieldData,
+ heightScale, minHeight, maxHeight, upAxis, PHY_SHORT,
+ flipQuadEdges);
+}
+
+btHeightfieldTerrainShape::btHeightfieldTerrainShape(
+ int heightStickWidth, int heightStickLength, const unsigned char* heightfieldData, btScalar heightScale,
+ btScalar minHeight, btScalar maxHeight, int upAxis, bool flipQuadEdges)
+ : m_userValue3(0), m_triangleInfoMap(0)
+{
+ initialize(heightStickWidth, heightStickLength, heightfieldData,
+ heightScale, minHeight, maxHeight, upAxis, PHY_UCHAR,
+ flipQuadEdges);
+}
+
+btHeightfieldTerrainShape::btHeightfieldTerrainShape(
int heightStickWidth, int heightStickLength, const void* heightfieldData,
btScalar heightScale, btScalar minHeight, btScalar maxHeight, int upAxis,
PHY_ScalarType hdt, bool flipQuadEdges)
:m_userValue3(0),
m_triangleInfoMap(0)
{
+ // legacy constructor: Assumes PHY_FLOAT means btScalar.
+#ifdef BT_USE_DOUBLE_PRECISION
+ if (hdt == PHY_FLOAT) hdt = PHY_DOUBLE;
+#endif
initialize(heightStickWidth, heightStickLength, heightfieldData,
heightScale, minHeight, maxHeight, upAxis, hdt,
flipQuadEdges);
@@ -33,9 +78,12 @@ btHeightfieldTerrainShape::btHeightfieldTerrainShape(int heightStickWidth, int h
: m_userValue3(0),
m_triangleInfoMap(0)
{
- // legacy constructor: support only float or unsigned char,
- // and min height is zero
+ // legacy constructor: support only btScalar or unsigned char data,
+ // and min height is zero.
PHY_ScalarType hdt = (useFloatData) ? PHY_FLOAT : PHY_UCHAR;
+#ifdef BT_USE_DOUBLE_PRECISION
+ if (hdt == PHY_FLOAT) hdt = PHY_DOUBLE;
+#endif
btScalar minHeight = 0.0f;
// previously, height = uchar * maxHeight / 65535.
@@ -59,7 +107,7 @@ void btHeightfieldTerrainShape::initialize(
// btAssert(heightScale) -- do we care? Trust caller here
btAssert(minHeight <= maxHeight); // && "bad min/max height");
btAssert(upAxis >= 0 && upAxis < 3); // && "bad upAxis--should be in range [0,2]");
- btAssert(hdt != PHY_UCHAR || hdt != PHY_FLOAT || hdt != PHY_SHORT); // && "Bad height data type enum");
+ btAssert(hdt != PHY_UCHAR || hdt != PHY_FLOAT || hdt != PHY_DOUBLE || hdt != PHY_SHORT); // && "Bad height data type enum");
// initialize member variables
m_shapeType = TERRAIN_SHAPE_PROXYTYPE;
@@ -152,6 +200,12 @@ btHeightfieldTerrainShape::getRawHeightFieldValue(int x, int y) const
break;
}
+ case PHY_DOUBLE:
+ {
+ val = m_heightfieldDataDouble[(y * m_heightStickWidth) + x];
+ break;
+ }
+
case PHY_UCHAR:
{
unsigned char heightFieldValue = m_heightfieldDataUnsignedChar[(y * m_heightStickWidth) + x];
@@ -232,6 +286,30 @@ getQuantized(
return (int)(x + 0.5);
}
+// Equivalent to std::minmax({a, b, c}).
+// Performs at most 3 comparisons.
+static btHeightfieldTerrainShape::Range minmaxRange(btScalar a, btScalar b, btScalar c)
+{
+ if (a > b)
+ {
+ if (b > c)
+ return btHeightfieldTerrainShape::Range(c, a);
+ else if (a > c)
+ return btHeightfieldTerrainShape::Range(b, a);
+ else
+ return btHeightfieldTerrainShape::Range(b, c);
+ }
+ else
+ {
+ if (a > c)
+ return btHeightfieldTerrainShape::Range(c, b);
+ else if (b > c)
+ return btHeightfieldTerrainShape::Range(a, b);
+ else
+ return btHeightfieldTerrainShape::Range(a, c);
+ }
+}
+
/// given input vector, return quantized version
/**
This routine is basically determining the gridpoint indices for a given
@@ -334,7 +412,8 @@ void btHeightfieldTerrainShape::processAllTriangles(btTriangleCallback* callback
}
// TODO If m_vboundsGrid is available, use it to determine if we really need to process this area
-
+
+ const Range aabbUpRange(aabbMin[m_upAxis], aabbMax[m_upAxis]);
for (int j = startJ; j < endJ; j++)
{
for (int x = startX; x < endX; x++)
@@ -349,29 +428,51 @@ void btHeightfieldTerrainShape::processAllTriangles(btTriangleCallback* callback
if (m_flipQuadEdges || (m_useDiamondSubdivision && !((j + x) & 1)) || (m_useZigzagSubdivision && !(j & 1)))
{
- //first triangle
getVertex(x, j, vertices[indices[0]]);
getVertex(x, j + 1, vertices[indices[1]]);
getVertex(x + 1, j + 1, vertices[indices[2]]);
- callback->processTriangle(vertices, 2 * x, j);
- //second triangle
- // getVertex(x,j,vertices[0]);//already got this vertex before, thanks to Danny Chapman
- getVertex(x + 1, j + 1, vertices[indices[1]]);
+
+ // Skip triangle processing if the triangle is out-of-AABB.
+ Range upRange = minmaxRange(vertices[0][m_upAxis], vertices[1][m_upAxis], vertices[2][m_upAxis]);
+
+ if (upRange.overlaps(aabbUpRange))
+ callback->processTriangle(vertices, 2 * x, j);
+
+ // already set: getVertex(x, j, vertices[indices[0]])
+
+ // equivalent to: getVertex(x + 1, j + 1, vertices[indices[1]]);
+ vertices[indices[1]] = vertices[indices[2]];
+
getVertex(x + 1, j, vertices[indices[2]]);
- callback->processTriangle(vertices, 2 * x+1, j);
+ upRange.min = btMin(upRange.min, vertices[indices[2]][m_upAxis]);
+ upRange.max = btMax(upRange.max, vertices[indices[2]][m_upAxis]);
+
+ if (upRange.overlaps(aabbUpRange))
+ callback->processTriangle(vertices, 2 * x + 1, j);
}
else
{
- //first triangle
getVertex(x, j, vertices[indices[0]]);
getVertex(x, j + 1, vertices[indices[1]]);
getVertex(x + 1, j, vertices[indices[2]]);
- callback->processTriangle(vertices, 2 * x, j);
- //second triangle
- getVertex(x + 1, j, vertices[indices[0]]);
- //getVertex(x,j+1,vertices[1]);
+
+ // Skip triangle processing if the triangle is out-of-AABB.
+ Range upRange = minmaxRange(vertices[0][m_upAxis], vertices[1][m_upAxis], vertices[2][m_upAxis]);
+
+ if (upRange.overlaps(aabbUpRange))
+ callback->processTriangle(vertices, 2 * x, j);
+
+ // already set: getVertex(x, j + 1, vertices[indices[1]]);
+
+ // equivalent to: getVertex(x + 1, j, vertices[indices[0]]);
+ vertices[indices[0]] = vertices[indices[2]];
+
getVertex(x + 1, j + 1, vertices[indices[2]]);
- callback->processTriangle(vertices, 2 * x+1, j);
+ upRange.min = btMin(upRange.min, vertices[indices[2]][m_upAxis]);
+ upRange.max = btMax(upRange.max, vertices[indices[2]][m_upAxis]);
+
+ if (upRange.overlaps(aabbUpRange))
+ callback->processTriangle(vertices, 2 * x + 1, j);
}
}
}
@@ -846,4 +947,4 @@ void btHeightfieldTerrainShape::buildAccelerator(int chunkSize)
void btHeightfieldTerrainShape::clearAccelerator()
{
m_vboundsGrid.clear();
-} \ No newline at end of file
+}
diff --git a/thirdparty/bullet/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h b/thirdparty/bullet/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h
index 2cf3c00721..7e251fa71e 100644
--- a/thirdparty/bullet/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h
+++ b/thirdparty/bullet/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h
@@ -50,17 +50,15 @@ subject to the following restrictions:
The heightfield heights are determined from the data type used for the
heightfieldData array.
- - PHY_UCHAR: height at a point is the uchar value at the
+ - unsigned char: height at a point is the uchar value at the
grid point, multipled by heightScale. uchar isn't recommended
because of its inability to deal with negative values, and
low resolution (8-bit).
- - PHY_SHORT: height at a point is the short int value at that grid
+ - short: height at a point is the short int value at that grid
point, multipled by heightScale.
- - PHY_FLOAT: height at a point is the float value at that grid
- point. heightScale is ignored when using the float heightfield
- data type.
+ - float or dobule: height at a point is the value at that grid point.
Whatever the caller specifies as minHeight and maxHeight will be honored.
The class will not inspect the heightfield to discover the actual minimum
@@ -75,6 +73,14 @@ btHeightfieldTerrainShape : public btConcaveShape
public:
struct Range
{
+ Range() {}
+ Range(btScalar min, btScalar max) : min(min), max(max) {}
+
+ bool overlaps(const Range& other) const
+ {
+ return !(min > other.max || max < other.min);
+ }
+
btScalar min;
btScalar max;
};
@@ -95,7 +101,8 @@ protected:
union {
const unsigned char* m_heightfieldDataUnsignedChar;
const short* m_heightfieldDataShort;
- const btScalar* m_heightfieldDataFloat;
+ const float* m_heightfieldDataFloat;
+ const double* m_heightfieldDataDouble;
const void* m_heightfieldDataUnknown;
};
@@ -135,11 +142,33 @@ protected:
public:
BT_DECLARE_ALIGNED_ALLOCATOR();
- /// preferred constructor
+ /// preferred constructors
+ btHeightfieldTerrainShape(
+ int heightStickWidth, int heightStickLength,
+ const float* heightfieldData, btScalar minHeight, btScalar maxHeight,
+ int upAxis, bool flipQuadEdges);
+ btHeightfieldTerrainShape(
+ int heightStickWidth, int heightStickLength,
+ const double* heightfieldData, btScalar minHeight, btScalar maxHeight,
+ int upAxis, bool flipQuadEdges);
+ btHeightfieldTerrainShape(
+ int heightStickWidth, int heightStickLength,
+ const short* heightfieldData, btScalar heightScale, btScalar minHeight, btScalar maxHeight,
+ int upAxis, bool flipQuadEdges);
+ btHeightfieldTerrainShape(
+ int heightStickWidth, int heightStickLength,
+ const unsigned char* heightfieldData, btScalar heightScale, btScalar minHeight, btScalar maxHeight,
+ int upAxis, bool flipQuadEdges);
+
+ /// legacy constructor
/**
This constructor supports a range of heightfield
data types, and allows for a non-zero minimum height value.
heightScale is needed for any integer-based heightfield data types.
+
+ This legacy constructor considers `PHY_FLOAT` to mean `btScalar`.
+ With `BT_USE_DOUBLE_PRECISION`, it will expect `heightfieldData`
+ to be double-precision.
*/
btHeightfieldTerrainShape(int heightStickWidth, int heightStickLength,
const void* heightfieldData, btScalar heightScale,
@@ -150,7 +179,7 @@ public:
/// legacy constructor
/**
The legacy constructor assumes the heightfield has a minimum height
- of zero. Only unsigned char or floats are supported. For legacy
+ of zero. Only unsigned char or btScalar data are supported. For legacy
compatibility reasons, heightScale is calculated as maxHeight / 65535
(and is only used when useFloatData = false).
*/
@@ -218,4 +247,4 @@ public:
}
};
-#endif //BT_HEIGHTFIELD_TERRAIN_SHAPE_H \ No newline at end of file
+#endif //BT_HEIGHTFIELD_TERRAIN_SHAPE_H
diff --git a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBody.cpp b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBody.cpp
index 7cb92fa3b4..d7588aedc8 100644
--- a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBody.cpp
+++ b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBody.cpp
@@ -33,8 +33,8 @@
namespace
{
-const btScalar SLEEP_EPSILON = btScalar(0.05); // this is a squared velocity (m^2 s^-2)
-const btScalar SLEEP_TIMEOUT = btScalar(2); // in seconds
+const btScalar INITIAL_SLEEP_EPSILON = btScalar(0.05); // this is a squared velocity (m^2 s^-2)
+const btScalar INITIAL_SLEEP_TIMEOUT = btScalar(2); // in seconds
} // namespace
void btMultiBody::spatialTransform(const btMatrix3x3 &rotation_matrix, // rotates vectors in 'from' frame to vectors in 'to' frame
@@ -110,6 +110,9 @@ btMultiBody::btMultiBody(int n_links,
m_canSleep(canSleep),
m_canWakeup(true),
m_sleepTimer(0),
+ m_sleepEpsilon(INITIAL_SLEEP_EPSILON),
+ m_sleepTimeout(INITIAL_SLEEP_TIMEOUT),
+
m_userObjectPointer(0),
m_userIndex2(-1),
m_userIndex(-1),
@@ -1411,7 +1414,7 @@ void btMultiBody::solveImatrix(const btSpatialForceVector &rhs, btSpatialMotionV
}
}
-void btMultiBody::mulMatrix(btScalar *pA, btScalar *pB, int rowsA, int colsA, int rowsB, int colsB, btScalar *pC) const
+void btMultiBody::mulMatrix(const btScalar *pA, const btScalar *pB, int rowsA, int colsA, int rowsB, int colsB, btScalar *pC) const
{
for (int row = 0; row < rowsA; row++)
{
@@ -2104,10 +2107,10 @@ void btMultiBody::checkMotionAndSleepIfRequired(btScalar timestep)
motion += m_realBuf[i] * m_realBuf[i];
}
- if (motion < SLEEP_EPSILON)
+ if (motion < m_sleepEpsilon)
{
m_sleepTimer += timestep;
- if (m_sleepTimer > SLEEP_TIMEOUT)
+ if (m_sleepTimer > m_sleepTimeout)
{
goToSleep();
}
diff --git a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBody.h b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBody.h
index 5a3efc9414..345970d261 100644
--- a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBody.h
+++ b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBody.h
@@ -545,7 +545,10 @@ public:
{
m_canWakeup = canWakeup;
}
- bool isAwake() const { return m_awake; }
+ bool isAwake() const
+ {
+ return m_awake;
+ }
void wakeUp();
void goToSleep();
void checkMotionAndSleepIfRequired(btScalar timestep);
@@ -726,6 +729,17 @@ public:
bool isLinkAndAllAncestorsKinematic(const int i) const;
+ void setSleepThreshold(btScalar sleepThreshold)
+ {
+ m_sleepEpsilon = sleepThreshold;
+ }
+
+ void setSleepTimeout(btScalar sleepTimeout)
+ {
+ this->m_sleepTimeout = sleepTimeout;
+ }
+
+
private:
btMultiBody(const btMultiBody &); // not implemented
void operator=(const btMultiBody &); // not implemented
@@ -745,7 +759,7 @@ private:
}
}
- void mulMatrix(btScalar * pA, btScalar * pB, int rowsA, int colsA, int rowsB, int colsB, btScalar *pC) const;
+ void mulMatrix(const btScalar *pA, const btScalar *pB, int rowsA, int colsA, int rowsB, int colsB, btScalar *pC) const;
private:
btMultiBodyLinkCollider *m_baseCollider; //can be NULL
@@ -801,6 +815,8 @@ private:
bool m_canSleep;
bool m_canWakeup;
btScalar m_sleepTimer;
+ btScalar m_sleepEpsilon;
+ btScalar m_sleepTimeout;
void *m_userObjectPointer;
int m_userIndex2;
diff --git a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyConstraint.cpp b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyConstraint.cpp
index 1ba5861145..00d5fd5609 100644
--- a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyConstraint.cpp
+++ b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyConstraint.cpp
@@ -61,7 +61,8 @@ btScalar btMultiBodyConstraint::fillMultiBodyConstraint(btMultiBodySolverConstra
btScalar lowerLimit, btScalar upperLimit,
bool angConstraint,
btScalar relaxation,
- bool isFriction, btScalar desiredVelocity, btScalar cfmSlip)
+ bool isFriction, btScalar desiredVelocity, btScalar cfmSlip,
+ btScalar damping)
{
solverConstraint.m_multiBodyA = m_bodyA;
solverConstraint.m_multiBodyB = m_bodyB;
@@ -348,7 +349,7 @@ btScalar btMultiBodyConstraint::fillMultiBodyConstraint(btMultiBodySolverConstra
{
btScalar positionalError = 0.f;
- btScalar velocityError = desiredVelocity - rel_vel; // * damping;
+ btScalar velocityError = (desiredVelocity - rel_vel) * damping;
btScalar erp = infoGlobal.m_erp2;
diff --git a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyConstraint.h b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyConstraint.h
index 4a6007ee3e..1aaa07b69e 100644
--- a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyConstraint.h
+++ b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyConstraint.h
@@ -94,7 +94,7 @@ protected:
bool angConstraint = false,
btScalar relaxation = 1.f,
- bool isFriction = false, btScalar desiredVelocity = 0, btScalar cfmSlip = 0);
+ bool isFriction = false, btScalar desiredVelocity = 0, btScalar cfmSlip = 0, btScalar damping = 1.0);
public:
BT_DECLARE_ALIGNED_ALLOCATOR();
diff --git a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.cpp b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.cpp
index fef95f0c4e..e7af332eb3 100644
--- a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.cpp
+++ b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.cpp
@@ -137,7 +137,14 @@ void btMultiBodyDynamicsWorld::updateActivationState(btScalar timeStep)
btMultiBodyLinkCollider* col = body->getBaseCollider();
if (col && col->getActivationState() == ACTIVE_TAG)
{
- col->setActivationState(WANTS_DEACTIVATION);
+ if (body->hasFixedBase())
+ {
+ col->setActivationState(FIXED_BASE_MULTI_BODY);
+ } else
+ {
+ col->setActivationState(WANTS_DEACTIVATION);
+ }
+
col->setDeactivationTime(0.f);
}
for (int b = 0; b < body->getNumLinks(); b++)
diff --git a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyJointMotor.cpp b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyJointMotor.cpp
index 4372489fa1..fec9b03213 100644
--- a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyJointMotor.cpp
+++ b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyJointMotor.cpp
@@ -43,6 +43,7 @@ void btMultiBodyJointMotor::finalizeMultiDof()
unsigned int offset = 6 + (m_bodyA->getLink(m_linkA).m_dofOffset + linkDoF);
// row 0: the lower bound
+ // row 0: the lower bound
jacobianA(0)[offset] = 1;
m_numDofsFinalized = m_jacSizeBoth;
diff --git a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodySphericalJointMotor.cpp b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodySphericalJointMotor.cpp
index 5c20d2a0d4..00a7ef3579 100644
--- a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodySphericalJointMotor.cpp
+++ b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodySphericalJointMotor.cpp
@@ -26,10 +26,13 @@ btMultiBodySphericalJointMotor::btMultiBodySphericalJointMotor(btMultiBody* body
: btMultiBodyConstraint(body, body, link, body->getLink(link).m_parent, 3, true, MULTIBODY_CONSTRAINT_SPHERICAL_MOTOR),
m_desiredVelocity(0, 0, 0),
m_desiredPosition(0,0,0,1),
- m_kd(1.),
- m_kp(0.2),
+ m_use_multi_dof_params(false),
+ m_kd(1., 1., 1.),
+ m_kp(0.2, 0.2, 0.2),
m_erp(1),
- m_rhsClamp(SIMD_INFINITY)
+ m_rhsClamp(SIMD_INFINITY),
+ m_maxAppliedImpulseMultiDof(maxMotorImpulse, maxMotorImpulse, maxMotorImpulse),
+ m_damping(1.0, 1.0, 1.0)
{
m_maxAppliedImpulse = maxMotorImpulse;
@@ -45,6 +48,7 @@ void btMultiBodySphericalJointMotor::finalizeMultiDof()
unsigned int offset = 6 + (m_bodyA->getLink(m_linkA).m_dofOffset + linkDoF);
// row 0: the lower bound
+ // row 0: the lower bound
jacobianA(0)[offset] = 1;
m_numDofsFinalized = m_jacSizeBoth;
@@ -138,7 +142,8 @@ btQuaternion relRot = currentQuat.inverse() * desiredQuat;
btScalar currentVelocity = m_bodyA->getJointVelMultiDof(m_linkA)[dof];
btScalar desiredVelocity = this->m_desiredVelocity[row];
- btScalar velocityError = desiredVelocity - currentVelocity;
+ double kd = m_use_multi_dof_params ? m_kd[row % 3] : m_kd[0];
+ btScalar velocityError = (desiredVelocity - currentVelocity) * kd;
btMatrix3x3 frameAworld;
frameAworld.setIdentity();
@@ -151,12 +156,16 @@ btQuaternion relRot = currentQuat.inverse() * desiredQuat;
case btMultibodyLink::eSpherical:
{
btVector3 constraintNormalAng = frameAworld.getColumn(row % 3);
- posError = m_kp*angleDiff[row % 3];
+ double kp = m_use_multi_dof_params ? m_kp[row % 3] : m_kp[0];
+ posError = kp*angleDiff[row % 3];
+ double max_applied_impulse = m_use_multi_dof_params ? m_maxAppliedImpulseMultiDof[row % 3] : m_maxAppliedImpulse;
fillMultiBodyConstraint(constraintRow, data, 0, 0, constraintNormalAng,
btVector3(0,0,0), dummy, dummy,
posError,
infoGlobal,
- -m_maxAppliedImpulse, m_maxAppliedImpulse, true);
+ -max_applied_impulse, max_applied_impulse, true,
+ 1.0, false, 0, 0,
+ m_damping[row % 3]);
constraintRow.m_orgConstraint = this;
constraintRow.m_orgDofIndex = row;
break;
diff --git a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodySphericalJointMotor.h b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodySphericalJointMotor.h
index 621beab5a4..bdeccc2e24 100644
--- a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodySphericalJointMotor.h
+++ b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodySphericalJointMotor.h
@@ -26,10 +26,13 @@ class btMultiBodySphericalJointMotor : public btMultiBodyConstraint
protected:
btVector3 m_desiredVelocity;
btQuaternion m_desiredPosition;
- btScalar m_kd;
- btScalar m_kp;
+ bool m_use_multi_dof_params;
+ btVector3 m_kd;
+ btVector3 m_kp;
btScalar m_erp;
btScalar m_rhsClamp; //maximum error
+ btVector3 m_maxAppliedImpulseMultiDof;
+ btVector3 m_damping;
public:
btMultiBodySphericalJointMotor(btMultiBody* body, int link, btScalar maxMotorImpulse);
@@ -44,16 +47,32 @@ public:
btMultiBodyJacobianData& data,
const btContactSolverInfo& infoGlobal);
- virtual void setVelocityTarget(const btVector3& velTarget, btScalar kd = 1.f)
+ virtual void setVelocityTarget(const btVector3& velTarget, btScalar kd = 1.0)
+ {
+ m_desiredVelocity = velTarget;
+ m_kd = btVector3(kd, kd, kd);
+ m_use_multi_dof_params = false;
+ }
+
+ virtual void setVelocityTargetMultiDof(const btVector3& velTarget, const btVector3& kd = btVector3(1.0, 1.0, 1.0))
{
m_desiredVelocity = velTarget;
m_kd = kd;
+ m_use_multi_dof_params = true;
}
- virtual void setPositionTarget(const btQuaternion& posTarget, btScalar kp = 1.f)
+ virtual void setPositionTarget(const btQuaternion& posTarget, btScalar kp =1.f)
+ {
+ m_desiredPosition = posTarget;
+ m_kp = btVector3(kp, kp, kp);
+ m_use_multi_dof_params = false;
+ }
+
+ virtual void setPositionTargetMultiDof(const btQuaternion& posTarget, const btVector3& kp = btVector3(1.f, 1.f, 1.f))
{
m_desiredPosition = posTarget;
m_kp = kp;
+ m_use_multi_dof_params = true;
}
virtual void setErp(btScalar erp)
@@ -68,6 +87,28 @@ public:
{
m_rhsClamp = rhsClamp;
}
+
+ btScalar getMaxAppliedImpulseMultiDof(int i) const
+ {
+ return m_maxAppliedImpulseMultiDof[i];
+ }
+
+ void setMaxAppliedImpulseMultiDof(const btVector3& maxImp)
+ {
+ m_maxAppliedImpulseMultiDof = maxImp;
+ m_use_multi_dof_params = true;
+ }
+
+ btScalar getDamping(int i) const
+ {
+ return m_damping[i];
+ }
+
+ void setDamping(const btVector3& damping)
+ {
+ m_damping = damping;
+ }
+
virtual void debugDraw(class btIDebugDraw* drawer)
{
//todo(erwincoumans)
diff --git a/thirdparty/bullet/BulletDynamics/MLCPSolvers/btMLCPSolver.cpp b/thirdparty/bullet/BulletDynamics/MLCPSolvers/btMLCPSolver.cpp
index 5d864f2757..ed4e0b686d 100644
--- a/thirdparty/bullet/BulletDynamics/MLCPSolvers/btMLCPSolver.cpp
+++ b/thirdparty/bullet/BulletDynamics/MLCPSolvers/btMLCPSolver.cpp
@@ -532,7 +532,7 @@ void btMLCPSolver::createMLCP(const btContactSolverInfo& infoGlobal)
J_transpose = J.transpose();
btMatrixXu& tmp = m_scratchTmp;
-
+ //Minv.printMatrix("Minv=");
{
{
BT_PROFILE("J*Minv");
@@ -543,7 +543,7 @@ void btMLCPSolver::createMLCP(const btContactSolverInfo& infoGlobal)
m_A = tmp * J_transpose;
}
}
-
+ //J.printMatrix("J");
if (1)
{
// add cfm to the diagonal of m_A
diff --git a/thirdparty/bullet/BulletSoftBody/btDeformableBodySolver.cpp b/thirdparty/bullet/BulletSoftBody/btDeformableBodySolver.cpp
index 4b11fccecb..e81680f019 100644
--- a/thirdparty/bullet/BulletSoftBody/btDeformableBodySolver.cpp
+++ b/thirdparty/bullet/BulletSoftBody/btDeformableBodySolver.cpp
@@ -405,6 +405,10 @@ void btDeformableBodySolver::predictMotion(btScalar solverdt)
for (int i = 0; i < m_softBodies.size(); ++i)
{
btSoftBody* psb = m_softBodies[i];
+ /* Clear contacts */
+ psb->m_nodeRigidContacts.resize(0);
+ psb->m_faceRigidContacts.resize(0);
+ psb->m_faceNodeContacts.resize(0);
if (psb->isActive())
{
@@ -472,10 +476,6 @@ void btDeformableBodySolver::predictDeformableMotion(btSoftBody* psb, btScalar d
{
psb->updateFaceTree(true, true);
}
- /* Clear contacts */
- psb->m_nodeRigidContacts.resize(0);
- psb->m_faceRigidContacts.resize(0);
- psb->m_faceNodeContacts.resize(0);
/* Optimize dbvt's */
// psb->m_ndbvt.optimizeIncremental(1);
// psb->m_fdbvt.optimizeIncremental(1);
diff --git a/thirdparty/bullet/BulletSoftBody/btDeformableMousePickingForce.h b/thirdparty/bullet/BulletSoftBody/btDeformableMousePickingForce.h
index d218d96214..697408355c 100644
--- a/thirdparty/bullet/BulletSoftBody/btDeformableMousePickingForce.h
+++ b/thirdparty/bullet/BulletSoftBody/btDeformableMousePickingForce.h
@@ -29,7 +29,7 @@ class btDeformableMousePickingForce : public btDeformableLagrangianForce
public:
typedef btAlignedObjectArray<btVector3> TVStack;
- btDeformableMousePickingForce(btScalar k, btScalar d, const btSoftBody::Face& face, btVector3 mouse_pos, btScalar maxForce = 0.3) : m_elasticStiffness(k), m_dampingStiffness(d), m_face(face), m_mouse_pos(mouse_pos), m_maxForce(maxForce)
+ btDeformableMousePickingForce(btScalar k, btScalar d, const btSoftBody::Face& face, const btVector3& mouse_pos, btScalar maxForce = 0.3) : m_elasticStiffness(k), m_dampingStiffness(d), m_face(face), m_mouse_pos(mouse_pos), m_maxForce(maxForce)
{
}
diff --git a/thirdparty/bullet/BulletSoftBody/btSoftBody.h b/thirdparty/bullet/BulletSoftBody/btSoftBody.h
index f578487b8c..dfde8fd1e4 100644
--- a/thirdparty/bullet/BulletSoftBody/btSoftBody.h
+++ b/thirdparty/bullet/BulletSoftBody/btSoftBody.h
@@ -1317,8 +1317,8 @@ public:
}
for (int k = 0; k < m_faceNodeContacts.size(); ++k)
{
- int i = indices[k];
- btSoftBody::DeformableFaceNodeContact& c = m_faceNodeContacts[i];
+ int idx = indices[k];
+ btSoftBody::DeformableFaceNodeContact& c = m_faceNodeContacts[idx];
btSoftBody::Node* node = c.m_node;
btSoftBody::Face* face = c.m_face;
const btVector3& w = c.m_bary;
diff --git a/thirdparty/bullet/LinearMath/btIDebugDraw.h b/thirdparty/bullet/LinearMath/btIDebugDraw.h
index 82ec19a69b..df4db2ff5a 100644
--- a/thirdparty/bullet/LinearMath/btIDebugDraw.h
+++ b/thirdparty/bullet/LinearMath/btIDebugDraw.h
@@ -4,8 +4,8 @@ Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.org
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use of this software.
-Permission is granted to anyone to use this software for any purpose,
-including commercial applications, and to alter it and redistribute it freely,
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
@@ -21,7 +21,7 @@ subject to the following restrictions:
///The btIDebugDraw interface class allows hooking up a debug renderer to visually debug simulations.
///Typical use case: create a debug drawer object, and assign it to a btCollisionWorld or btDynamicsWorld using setDebugDrawer and call debugDrawWorld.
-///A class that implements the btIDebugDraw interface has to implement the drawLine method at a minimum.
+///A class that implements the btIDebugDraw interface will need to provide non-empty implementations of the the drawLine and getDebugMode methods at a minimum.
///For color arguments the X,Y,Z components refer to Red, Green and Blue each in the range [0..1]
class btIDebugDraw
{
diff --git a/thirdparty/bullet/LinearMath/btScalar.h b/thirdparty/bullet/LinearMath/btScalar.h
index 0402146af1..b239217bb6 100644
--- a/thirdparty/bullet/LinearMath/btScalar.h
+++ b/thirdparty/bullet/LinearMath/btScalar.h
@@ -25,7 +25,7 @@ subject to the following restrictions:
#include <float.h>
/* SVN $Revision$ on $Date$ from http://bullet.googlecode.com*/
-#define BT_BULLET_VERSION 308
+#define BT_BULLET_VERSION 317
inline int btGetVersion()
{
diff --git a/thirdparty/bullet/LinearMath/btSerializer.h b/thirdparty/bullet/LinearMath/btSerializer.h
index 4d1c760e24..f18442f23d 100644
--- a/thirdparty/bullet/LinearMath/btSerializer.h
+++ b/thirdparty/bullet/LinearMath/btSerializer.h
@@ -480,8 +480,8 @@ public:
}
buffer[9] = '3';
- buffer[10] = '0';
- buffer[11] = '8';
+ buffer[10] = '1';
+ buffer[11] = '7';
}
virtual void startSerialization()
@@ -499,7 +499,6 @@ public:
writeDNA();
//if we didn't pre-allocate a buffer, we need to create a contiguous buffer now
- int mysize = 0;
if (!m_totalSize)
{
if (m_buffer)
@@ -511,14 +510,12 @@ public:
unsigned char* currentPtr = m_buffer;
writeHeader(m_buffer);
currentPtr += BT_HEADER_LENGTH;
- mysize += BT_HEADER_LENGTH;
for (int i = 0; i < m_chunkPtrs.size(); i++)
{
int curLength = sizeof(btChunk) + m_chunkPtrs[i]->m_length;
memcpy(currentPtr, m_chunkPtrs[i], curLength);
btAlignedFree(m_chunkPtrs[i]);
currentPtr += curLength;
- mysize += curLength;
}
}
diff --git a/thirdparty/bullet/VERSION.txt b/thirdparty/bullet/VERSION.txt
new file mode 100644
index 0000000000..78c8a7428a
--- /dev/null
+++ b/thirdparty/bullet/VERSION.txt
@@ -0,0 +1 @@
+3.17
diff --git a/thirdparty/bullet/patches/bullet-fix-warnings.patch b/thirdparty/bullet/patches/bullet-fix-warnings.patch
new file mode 100644
index 0000000000..69cde1b16e
--- /dev/null
+++ b/thirdparty/bullet/patches/bullet-fix-warnings.patch
@@ -0,0 +1,42 @@
+diff --git a/thirdparty/bullet/BulletSoftBody/btSoftBody.h b/thirdparty/bullet/BulletSoftBody/btSoftBody.h
+index f578487b8c..dfde8fd1e4 100644
+--- a/thirdparty/bullet/BulletSoftBody/btSoftBody.h
++++ b/thirdparty/bullet/BulletSoftBody/btSoftBody.h
+@@ -1317,8 +1317,8 @@ public:
+ }
+ for (int k = 0; k < m_faceNodeContacts.size(); ++k)
+ {
+- int i = indices[k];
+- btSoftBody::DeformableFaceNodeContact& c = m_faceNodeContacts[i];
++ int idx = indices[k];
++ btSoftBody::DeformableFaceNodeContact& c = m_faceNodeContacts[idx];
+ btSoftBody::Node* node = c.m_node;
+ btSoftBody::Face* face = c.m_face;
+ const btVector3& w = c.m_bary;
+diff --git a/thirdparty/bullet/LinearMath/btSerializer.h b/thirdparty/bullet/LinearMath/btSerializer.h
+index ce4fc34e20..11592d2ccd 100644
+--- a/thirdparty/bullet/LinearMath/btSerializer.h
++++ b/thirdparty/bullet/LinearMath/btSerializer.h
+@@ -499,7 +499,6 @@ public:
+ writeDNA();
+
+ //if we didn't pre-allocate a buffer, we need to create a contiguous buffer now
+- int mysize = 0;
+ if (!m_totalSize)
+ {
+ if (m_buffer)
+@@ -511,14 +510,12 @@ public:
+ unsigned char* currentPtr = m_buffer;
+ writeHeader(m_buffer);
+ currentPtr += BT_HEADER_LENGTH;
+- mysize += BT_HEADER_LENGTH;
+ for (int i = 0; i < m_chunkPtrs.size(); i++)
+ {
+ int curLength = sizeof(btChunk) + m_chunkPtrs[i]->m_length;
+ memcpy(currentPtr, m_chunkPtrs[i], curLength);
+ btAlignedFree(m_chunkPtrs[i]);
+ currentPtr += curLength;
+- mysize += curLength;
+ }
+ }
+
diff --git a/thirdparty/doctest/LICENSE.txt b/thirdparty/doctest/LICENSE.txt
index 50a358cd1b..d67bb64f9d 100644
--- a/thirdparty/doctest/LICENSE.txt
+++ b/thirdparty/doctest/LICENSE.txt
@@ -1,6 +1,6 @@
The MIT License (MIT)
-Copyright (c) 2016-2020 Viktor Kirilov
+Copyright (c) 2016-2021 Viktor Kirilov
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/thirdparty/doctest/doctest.h b/thirdparty/doctest/doctest.h
index 7712dd6b63..42eb039979 100644
--- a/thirdparty/doctest/doctest.h
+++ b/thirdparty/doctest/doctest.h
@@ -4,7 +4,7 @@
//
// doctest.h - the lightest feature-rich C++ single-header testing framework for unit tests and TDD
//
-// Copyright (c) 2016-2020 Viktor Kirilov
+// Copyright (c) 2016-2021 Viktor Kirilov
//
// Distributed under the MIT Software License
// See accompanying file LICENSE.txt or copy at
@@ -48,8 +48,8 @@
#define DOCTEST_VERSION_MAJOR 2
#define DOCTEST_VERSION_MINOR 4
-#define DOCTEST_VERSION_PATCH 4
-#define DOCTEST_VERSION_STR "2.4.4"
+#define DOCTEST_VERSION_PATCH 6
+#define DOCTEST_VERSION_STR "2.4.6"
#define DOCTEST_VERSION \
(DOCTEST_VERSION_MAJOR * 10000 + DOCTEST_VERSION_MINOR * 100 + DOCTEST_VERSION_PATCH)
@@ -354,7 +354,7 @@ DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum'
#define DOCTEST_GLOBAL_NO_WARNINGS(var) \
DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wglobal-constructors") \
DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-variable") \
- static int var DOCTEST_UNUSED // NOLINT(fuchsia-statically-constructed-objects,cert-err58-cpp)
+ static const int var DOCTEST_UNUSED // NOLINT(fuchsia-statically-constructed-objects,cert-err58-cpp)
#define DOCTEST_GLOBAL_NO_WARNINGS_END() DOCTEST_CLANG_SUPPRESS_WARNING_POP
#ifndef DOCTEST_BREAK_INTO_DEBUGGER
@@ -362,16 +362,16 @@ DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum'
#ifdef DOCTEST_PLATFORM_LINUX
#if defined(__GNUC__) && (defined(__i386) || defined(__x86_64))
// Break at the location of the failing check if possible
-#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :)
+#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) // NOLINT (hicpp-no-assembler)
#else
#include <signal.h>
#define DOCTEST_BREAK_INTO_DEBUGGER() raise(SIGTRAP)
#endif
#elif defined(DOCTEST_PLATFORM_MAC)
#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(__i386)
-#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :)
+#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) // NOLINT (hicpp-no-assembler)
#else
-#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("brk #0");
+#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("brk #0"); // NOLINT (hicpp-no-assembler)
#endif
#elif DOCTEST_MSVC
#define DOCTEST_BREAK_INTO_DEBUGGER() __debugbreak()
@@ -656,12 +656,14 @@ DOCTEST_INTERFACE const char* skipPathFromFilename(const char* file);
struct DOCTEST_INTERFACE TestCaseData
{
- String m_file; // the file in which the test was registered
+ String m_file; // the file in which the test was registered (using String - see #350)
unsigned m_line; // the line where the test was registered
const char* m_name; // name of the test case
const char* m_test_suite; // the test suite in which the test was added
const char* m_description;
bool m_skip;
+ bool m_no_breaks;
+ bool m_no_output;
bool m_may_fail;
bool m_should_fail;
int m_expected_failures;
@@ -715,12 +717,18 @@ struct DOCTEST_INTERFACE IContextScope
virtual void stringify(std::ostream*) const = 0;
};
+namespace detail {
+ struct DOCTEST_INTERFACE TestCase;
+} // namespace detail
+
struct ContextOptions //!OCLINT too many fields
{
std::ostream* cout; // stdout stream - std::cout by default
std::ostream* cerr; // stderr stream - std::cerr by default
String binary_name; // the test binary name
+ const detail::TestCase* currentTest = nullptr;
+
// == parameters from the command line
String out; // output filename
String order_by; // how tests should be ordered
@@ -773,6 +781,29 @@ namespace detail {
template<class T> struct remove_reference<T&> { typedef T type; };
template<class T> struct remove_reference<T&&> { typedef T type; };
+ template<typename T, typename U = T&&> U declval(int);
+
+ template<typename T> T declval(long);
+
+ template<typename T> auto declval() DOCTEST_NOEXCEPT -> decltype(declval<T>(0)) ;
+
+ template<class T> struct is_lvalue_reference { const static bool value=false; };
+ template<class T> struct is_lvalue_reference<T&> { const static bool value=true; };
+
+ template <class T>
+ inline T&& forward(typename remove_reference<T>::type& t) DOCTEST_NOEXCEPT
+ {
+ return static_cast<T&&>(t);
+ }
+
+ template <class T>
+ inline T&& forward(typename remove_reference<T>::type&& t) DOCTEST_NOEXCEPT
+ {
+ static_assert(!is_lvalue_reference<T>::value,
+ "Can not forward an rvalue as an lvalue.");
+ return static_cast<T&&>(t);
+ }
+
template<class T> struct remove_const { typedef T type; };
template<class T> struct remove_const<const T> { typedef T type; };
#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
@@ -1040,10 +1071,20 @@ namespace detail {
return toString(lhs) + op + toString(rhs);
}
+#if DOCTEST_CLANG && DOCTEST_CLANG < DOCTEST_COMPILER(3, 6, 0)
+DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wunused-comparison")
+#endif
+
+// This will check if there is any way it could find a operator like member or friend and uses it.
+// If not it doesn't find the operator or if the operator at global scope is defined after
+// this template, the template won't be instantiated due to SFINAE. Once the template is not
+// instantiated it can look for global operator using normal conversions.
+#define SFINAE_OP(ret,op) decltype(doctest::detail::declval<L>() op doctest::detail::declval<R>(),static_cast<ret>(0))
+
#define DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(op, op_str, op_macro) \
template <typename R> \
- DOCTEST_NOINLINE Result operator op(const DOCTEST_REF_WRAP(R) rhs) { \
- bool res = op_macro(lhs, rhs); \
+ DOCTEST_NOINLINE SFINAE_OP(Result,op) operator op(R&& rhs) { \
+ bool res = op_macro(doctest::detail::forward<L>(lhs), doctest::detail::forward<R>(rhs)); \
if(m_at & assertType::is_false) \
res = !res; \
if(!res || doctest::getContextOptions()->success) \
@@ -1171,12 +1212,16 @@ namespace detail {
L lhs;
assertType::Enum m_at;
- explicit Expression_lhs(L in, assertType::Enum at)
- : lhs(in)
+ explicit Expression_lhs(L&& in, assertType::Enum at)
+ : lhs(doctest::detail::forward<L>(in))
, m_at(at) {}
DOCTEST_NOINLINE operator Result() {
- bool res = !!lhs;
+// this is needed only foc MSVC 2015:
+// https://ci.appveyor.com/project/onqtam/doctest/builds/38181202
+DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4800) // 'int': forcing value to bool
+ bool res = static_cast<bool>(lhs);
+DOCTEST_MSVC_SUPPRESS_WARNING_POP
if(m_at & assertType::is_false) //!OCLINT bitwise operator in conditional
res = !res;
@@ -1185,6 +1230,10 @@ namespace detail {
return Result(res);
}
+ /* This is required for user-defined conversions from Expression_lhs to L */
+ //operator L() const { return lhs; }
+ operator L() const { return lhs; }
+
// clang-format off
DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(==, " == ", DOCTEST_CMP_EQ) //!OCLINT bitwise operator in conditional
DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(!=, " != ", DOCTEST_CMP_NE) //!OCLINT bitwise operator in conditional
@@ -1225,6 +1274,10 @@ namespace detail {
#endif // DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION
+#if DOCTEST_CLANG && DOCTEST_CLANG < DOCTEST_COMPILER(3, 6, 0)
+DOCTEST_CLANG_SUPPRESS_WARNING_POP
+#endif
+
struct DOCTEST_INTERFACE ExpressionDecomposer
{
assertType::Enum m_at;
@@ -1236,8 +1289,8 @@ namespace detail {
// https://github.com/catchorg/Catch2/issues/870
// https://github.com/catchorg/Catch2/issues/565
template <typename L>
- Expression_lhs<const DOCTEST_REF_WRAP(L)> operator<<(const DOCTEST_REF_WRAP(L) operand) {
- return Expression_lhs<const DOCTEST_REF_WRAP(L)>(operand, m_at);
+ Expression_lhs<L> operator<<(L &&operand) {
+ return Expression_lhs<L>(doctest::detail::forward<L>(operand), m_at);
}
};
@@ -1246,6 +1299,8 @@ namespace detail {
const char* m_test_suite;
const char* m_description;
bool m_skip;
+ bool m_no_breaks;
+ bool m_no_output;
bool m_may_fail;
bool m_should_fail;
int m_expected_failures;
@@ -1524,7 +1579,7 @@ namespace detail {
template <typename L> class ContextScope : public ContextScopeBase
{
- const L &lambda_;
+ const L lambda_;
public:
explicit ContextScope(const L &lambda) : lambda_(lambda) {}
@@ -1585,6 +1640,8 @@ namespace detail {
DOCTEST_DEFINE_DECORATOR(test_suite, const char*, "");
DOCTEST_DEFINE_DECORATOR(description, const char*, "");
DOCTEST_DEFINE_DECORATOR(skip, bool, true);
+DOCTEST_DEFINE_DECORATOR(no_breaks, bool, true);
+DOCTEST_DEFINE_DECORATOR(no_output, bool, true);
DOCTEST_DEFINE_DECORATOR(timeout, double, 0);
DOCTEST_DEFINE_DECORATOR(may_fail, bool, true);
DOCTEST_DEFINE_DECORATOR(should_fail, bool, true);
@@ -1921,10 +1978,12 @@ int registerReporter(const char* name, int priority, bool isReporter) {
static DOCTEST_NOINLINE doctest::detail::TestSuite& getCurrentTestSuite() { \
DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4640) \
DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wexit-time-destructors") \
- static doctest::detail::TestSuite data; \
+ DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wmissing-field-initializers") \
+ static doctest::detail::TestSuite data{}; \
static bool inited = false; \
DOCTEST_MSVC_SUPPRESS_WARNING_POP \
DOCTEST_CLANG_SUPPRESS_WARNING_POP \
+ DOCTEST_GCC_SUPPRESS_WARNING_POP \
if(!inited) { \
data* decorators; \
inited = true; \
@@ -1979,17 +2038,15 @@ int registerReporter(const char* name, int priority, bool isReporter) {
// for logging
#define DOCTEST_INFO(...) \
DOCTEST_INFO_IMPL(DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_), DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_), \
- DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_), __VA_ARGS__)
+ __VA_ARGS__)
-#define DOCTEST_INFO_IMPL(lambda_name, mb_name, s_name, ...) \
- DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4626) \
- auto lambda_name = [&](std::ostream* s_name) { \
+#define DOCTEST_INFO_IMPL(mb_name, s_name, ...) \
+ auto DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_) = doctest::detail::MakeContextScope( \
+ [&](std::ostream* s_name) { \
doctest::detail::MessageBuilder mb_name(__FILE__, __LINE__, doctest::assertType::is_warn); \
mb_name.m_stream = s_name; \
mb_name * __VA_ARGS__; \
- }; \
- DOCTEST_MSVC_SUPPRESS_WARNING_POP \
- auto DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_) = doctest::detail::MakeContextScope(lambda_name)
+ })
#define DOCTEST_CAPTURE(x) DOCTEST_INFO(#x " := ", x)
@@ -2461,7 +2518,7 @@ int registerReporter(const char* name, int priority, bool isReporter) {
#define DOCTEST_FAST_CHECK_UNARY_FALSE DOCTEST_CHECK_UNARY_FALSE
#define DOCTEST_FAST_REQUIRE_UNARY_FALSE DOCTEST_REQUIRE_UNARY_FALSE
-#define DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE DOCTEST_TEST_CASE_TEMPLATE_INVOKE
+#define DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE(id, ...) DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id,__VA_ARGS__)
// clang-format on
// BDD style macros
@@ -2481,138 +2538,138 @@ int registerReporter(const char* name, int priority, bool isReporter) {
// == SHORT VERSIONS OF THE MACROS
#if !defined(DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES)
-#define TEST_CASE DOCTEST_TEST_CASE
-#define TEST_CASE_CLASS DOCTEST_TEST_CASE_CLASS
-#define TEST_CASE_FIXTURE DOCTEST_TEST_CASE_FIXTURE
-#define TYPE_TO_STRING DOCTEST_TYPE_TO_STRING
-#define TEST_CASE_TEMPLATE DOCTEST_TEST_CASE_TEMPLATE
-#define TEST_CASE_TEMPLATE_DEFINE DOCTEST_TEST_CASE_TEMPLATE_DEFINE
-#define TEST_CASE_TEMPLATE_INVOKE DOCTEST_TEST_CASE_TEMPLATE_INVOKE
-#define TEST_CASE_TEMPLATE_APPLY DOCTEST_TEST_CASE_TEMPLATE_APPLY
-#define SUBCASE DOCTEST_SUBCASE
-#define TEST_SUITE DOCTEST_TEST_SUITE
-#define TEST_SUITE_BEGIN DOCTEST_TEST_SUITE_BEGIN
+#define TEST_CASE(name) DOCTEST_TEST_CASE(name)
+#define TEST_CASE_CLASS(name) DOCTEST_TEST_CASE_CLASS(name)
+#define TEST_CASE_FIXTURE(x, name) DOCTEST_TEST_CASE_FIXTURE(x, name)
+#define TYPE_TO_STRING(...) DOCTEST_TYPE_TO_STRING(__VA_ARGS__)
+#define TEST_CASE_TEMPLATE(name, T, ...) DOCTEST_TEST_CASE_TEMPLATE(name, T, __VA_ARGS__)
+#define TEST_CASE_TEMPLATE_DEFINE(name, T, id) DOCTEST_TEST_CASE_TEMPLATE_DEFINE(name, T, id)
+#define TEST_CASE_TEMPLATE_INVOKE(id, ...) DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id, __VA_ARGS__)
+#define TEST_CASE_TEMPLATE_APPLY(id, ...) DOCTEST_TEST_CASE_TEMPLATE_APPLY(id, __VA_ARGS__)
+#define SUBCASE(name) DOCTEST_SUBCASE(name)
+#define TEST_SUITE(decorators) DOCTEST_TEST_SUITE(decorators)
+#define TEST_SUITE_BEGIN(name) DOCTEST_TEST_SUITE_BEGIN(name)
#define TEST_SUITE_END DOCTEST_TEST_SUITE_END
-#define REGISTER_EXCEPTION_TRANSLATOR DOCTEST_REGISTER_EXCEPTION_TRANSLATOR
-#define REGISTER_REPORTER DOCTEST_REGISTER_REPORTER
-#define REGISTER_LISTENER DOCTEST_REGISTER_LISTENER
-#define INFO DOCTEST_INFO
-#define CAPTURE DOCTEST_CAPTURE
-#define ADD_MESSAGE_AT DOCTEST_ADD_MESSAGE_AT
-#define ADD_FAIL_CHECK_AT DOCTEST_ADD_FAIL_CHECK_AT
-#define ADD_FAIL_AT DOCTEST_ADD_FAIL_AT
-#define MESSAGE DOCTEST_MESSAGE
-#define FAIL_CHECK DOCTEST_FAIL_CHECK
-#define FAIL DOCTEST_FAIL
-#define TO_LVALUE DOCTEST_TO_LVALUE
-
-#define WARN DOCTEST_WARN
-#define WARN_FALSE DOCTEST_WARN_FALSE
-#define WARN_THROWS DOCTEST_WARN_THROWS
-#define WARN_THROWS_AS DOCTEST_WARN_THROWS_AS
-#define WARN_THROWS_WITH DOCTEST_WARN_THROWS_WITH
-#define WARN_THROWS_WITH_AS DOCTEST_WARN_THROWS_WITH_AS
-#define WARN_NOTHROW DOCTEST_WARN_NOTHROW
-#define CHECK DOCTEST_CHECK
-#define CHECK_FALSE DOCTEST_CHECK_FALSE
-#define CHECK_THROWS DOCTEST_CHECK_THROWS
-#define CHECK_THROWS_AS DOCTEST_CHECK_THROWS_AS
-#define CHECK_THROWS_WITH DOCTEST_CHECK_THROWS_WITH
-#define CHECK_THROWS_WITH_AS DOCTEST_CHECK_THROWS_WITH_AS
-#define CHECK_NOTHROW DOCTEST_CHECK_NOTHROW
-#define REQUIRE DOCTEST_REQUIRE
-#define REQUIRE_FALSE DOCTEST_REQUIRE_FALSE
-#define REQUIRE_THROWS DOCTEST_REQUIRE_THROWS
-#define REQUIRE_THROWS_AS DOCTEST_REQUIRE_THROWS_AS
-#define REQUIRE_THROWS_WITH DOCTEST_REQUIRE_THROWS_WITH
-#define REQUIRE_THROWS_WITH_AS DOCTEST_REQUIRE_THROWS_WITH_AS
-#define REQUIRE_NOTHROW DOCTEST_REQUIRE_NOTHROW
-
-#define WARN_MESSAGE DOCTEST_WARN_MESSAGE
-#define WARN_FALSE_MESSAGE DOCTEST_WARN_FALSE_MESSAGE
-#define WARN_THROWS_MESSAGE DOCTEST_WARN_THROWS_MESSAGE
-#define WARN_THROWS_AS_MESSAGE DOCTEST_WARN_THROWS_AS_MESSAGE
-#define WARN_THROWS_WITH_MESSAGE DOCTEST_WARN_THROWS_WITH_MESSAGE
-#define WARN_THROWS_WITH_AS_MESSAGE DOCTEST_WARN_THROWS_WITH_AS_MESSAGE
-#define WARN_NOTHROW_MESSAGE DOCTEST_WARN_NOTHROW_MESSAGE
-#define CHECK_MESSAGE DOCTEST_CHECK_MESSAGE
-#define CHECK_FALSE_MESSAGE DOCTEST_CHECK_FALSE_MESSAGE
-#define CHECK_THROWS_MESSAGE DOCTEST_CHECK_THROWS_MESSAGE
-#define CHECK_THROWS_AS_MESSAGE DOCTEST_CHECK_THROWS_AS_MESSAGE
-#define CHECK_THROWS_WITH_MESSAGE DOCTEST_CHECK_THROWS_WITH_MESSAGE
-#define CHECK_THROWS_WITH_AS_MESSAGE DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE
-#define CHECK_NOTHROW_MESSAGE DOCTEST_CHECK_NOTHROW_MESSAGE
-#define REQUIRE_MESSAGE DOCTEST_REQUIRE_MESSAGE
-#define REQUIRE_FALSE_MESSAGE DOCTEST_REQUIRE_FALSE_MESSAGE
-#define REQUIRE_THROWS_MESSAGE DOCTEST_REQUIRE_THROWS_MESSAGE
-#define REQUIRE_THROWS_AS_MESSAGE DOCTEST_REQUIRE_THROWS_AS_MESSAGE
-#define REQUIRE_THROWS_WITH_MESSAGE DOCTEST_REQUIRE_THROWS_WITH_MESSAGE
-#define REQUIRE_THROWS_WITH_AS_MESSAGE DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE
-#define REQUIRE_NOTHROW_MESSAGE DOCTEST_REQUIRE_NOTHROW_MESSAGE
-
-#define SCENARIO DOCTEST_SCENARIO
-#define SCENARIO_CLASS DOCTEST_SCENARIO_CLASS
-#define SCENARIO_TEMPLATE DOCTEST_SCENARIO_TEMPLATE
-#define SCENARIO_TEMPLATE_DEFINE DOCTEST_SCENARIO_TEMPLATE_DEFINE
-#define GIVEN DOCTEST_GIVEN
-#define WHEN DOCTEST_WHEN
-#define AND_WHEN DOCTEST_AND_WHEN
-#define THEN DOCTEST_THEN
-#define AND_THEN DOCTEST_AND_THEN
-
-#define WARN_EQ DOCTEST_WARN_EQ
-#define CHECK_EQ DOCTEST_CHECK_EQ
-#define REQUIRE_EQ DOCTEST_REQUIRE_EQ
-#define WARN_NE DOCTEST_WARN_NE
-#define CHECK_NE DOCTEST_CHECK_NE
-#define REQUIRE_NE DOCTEST_REQUIRE_NE
-#define WARN_GT DOCTEST_WARN_GT
-#define CHECK_GT DOCTEST_CHECK_GT
-#define REQUIRE_GT DOCTEST_REQUIRE_GT
-#define WARN_LT DOCTEST_WARN_LT
-#define CHECK_LT DOCTEST_CHECK_LT
-#define REQUIRE_LT DOCTEST_REQUIRE_LT
-#define WARN_GE DOCTEST_WARN_GE
-#define CHECK_GE DOCTEST_CHECK_GE
-#define REQUIRE_GE DOCTEST_REQUIRE_GE
-#define WARN_LE DOCTEST_WARN_LE
-#define CHECK_LE DOCTEST_CHECK_LE
-#define REQUIRE_LE DOCTEST_REQUIRE_LE
-#define WARN_UNARY DOCTEST_WARN_UNARY
-#define CHECK_UNARY DOCTEST_CHECK_UNARY
-#define REQUIRE_UNARY DOCTEST_REQUIRE_UNARY
-#define WARN_UNARY_FALSE DOCTEST_WARN_UNARY_FALSE
-#define CHECK_UNARY_FALSE DOCTEST_CHECK_UNARY_FALSE
-#define REQUIRE_UNARY_FALSE DOCTEST_REQUIRE_UNARY_FALSE
+#define REGISTER_EXCEPTION_TRANSLATOR(signature) DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature)
+#define REGISTER_REPORTER(name, priority, reporter) DOCTEST_REGISTER_REPORTER(name, priority, reporter)
+#define REGISTER_LISTENER(name, priority, reporter) DOCTEST_REGISTER_LISTENER(name, priority, reporter)
+#define INFO(...) DOCTEST_INFO(__VA_ARGS__)
+#define CAPTURE(x) DOCTEST_CAPTURE(x)
+#define ADD_MESSAGE_AT(file, line, ...) DOCTEST_ADD_MESSAGE_AT(file, line, __VA_ARGS__)
+#define ADD_FAIL_CHECK_AT(file, line, ...) DOCTEST_ADD_FAIL_CHECK_AT(file, line, __VA_ARGS__)
+#define ADD_FAIL_AT(file, line, ...) DOCTEST_ADD_FAIL_AT(file, line, __VA_ARGS__)
+#define MESSAGE(...) DOCTEST_MESSAGE(__VA_ARGS__)
+#define FAIL_CHECK(...) DOCTEST_FAIL_CHECK(__VA_ARGS__)
+#define FAIL(...) DOCTEST_FAIL(__VA_ARGS__)
+#define TO_LVALUE(...) DOCTEST_TO_LVALUE(__VA_ARGS__)
+
+#define WARN(...) DOCTEST_WARN(__VA_ARGS__)
+#define WARN_FALSE(...) DOCTEST_WARN_FALSE(__VA_ARGS__)
+#define WARN_THROWS(...) DOCTEST_WARN_THROWS(__VA_ARGS__)
+#define WARN_THROWS_AS(expr, ...) DOCTEST_WARN_THROWS_AS(expr, __VA_ARGS__)
+#define WARN_THROWS_WITH(expr, ...) DOCTEST_WARN_THROWS_WITH(expr, __VA_ARGS__)
+#define WARN_THROWS_WITH_AS(expr, with, ...) DOCTEST_WARN_THROWS_WITH_AS(expr, with, __VA_ARGS__)
+#define WARN_NOTHROW(...) DOCTEST_WARN_NOTHROW(__VA_ARGS__)
+#define CHECK(...) DOCTEST_CHECK(__VA_ARGS__)
+#define CHECK_FALSE(...) DOCTEST_CHECK_FALSE(__VA_ARGS__)
+#define CHECK_THROWS(...) DOCTEST_CHECK_THROWS(__VA_ARGS__)
+#define CHECK_THROWS_AS(expr, ...) DOCTEST_CHECK_THROWS_AS(expr, __VA_ARGS__)
+#define CHECK_THROWS_WITH(expr, ...) DOCTEST_CHECK_THROWS_WITH(expr, __VA_ARGS__)
+#define CHECK_THROWS_WITH_AS(expr, with, ...) DOCTEST_CHECK_THROWS_WITH_AS(expr, with, __VA_ARGS__)
+#define CHECK_NOTHROW(...) DOCTEST_CHECK_NOTHROW(__VA_ARGS__)
+#define REQUIRE(...) DOCTEST_REQUIRE(__VA_ARGS__)
+#define REQUIRE_FALSE(...) DOCTEST_REQUIRE_FALSE(__VA_ARGS__)
+#define REQUIRE_THROWS(...) DOCTEST_REQUIRE_THROWS(__VA_ARGS__)
+#define REQUIRE_THROWS_AS(expr, ...) DOCTEST_REQUIRE_THROWS_AS(expr, __VA_ARGS__)
+#define REQUIRE_THROWS_WITH(expr, ...) DOCTEST_REQUIRE_THROWS_WITH(expr, __VA_ARGS__)
+#define REQUIRE_THROWS_WITH_AS(expr, with, ...) DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, __VA_ARGS__)
+#define REQUIRE_NOTHROW(...) DOCTEST_REQUIRE_NOTHROW(__VA_ARGS__)
+
+#define WARN_MESSAGE(cond, ...) DOCTEST_WARN_MESSAGE(cond, __VA_ARGS__)
+#define WARN_FALSE_MESSAGE(cond, ...) DOCTEST_WARN_FALSE_MESSAGE(cond, __VA_ARGS__)
+#define WARN_THROWS_MESSAGE(expr, ...) DOCTEST_WARN_THROWS_MESSAGE(expr, __VA_ARGS__)
+#define WARN_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, __VA_ARGS__)
+#define WARN_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, __VA_ARGS__)
+#define WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, __VA_ARGS__)
+#define WARN_NOTHROW_MESSAGE(expr, ...) DOCTEST_WARN_NOTHROW_MESSAGE(expr, __VA_ARGS__)
+#define CHECK_MESSAGE(cond, ...) DOCTEST_CHECK_MESSAGE(cond, __VA_ARGS__)
+#define CHECK_FALSE_MESSAGE(cond, ...) DOCTEST_CHECK_FALSE_MESSAGE(cond, __VA_ARGS__)
+#define CHECK_THROWS_MESSAGE(expr, ...) DOCTEST_CHECK_THROWS_MESSAGE(expr, __VA_ARGS__)
+#define CHECK_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, __VA_ARGS__)
+#define CHECK_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, __VA_ARGS__)
+#define CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, __VA_ARGS__)
+#define CHECK_NOTHROW_MESSAGE(expr, ...) DOCTEST_CHECK_NOTHROW_MESSAGE(expr, __VA_ARGS__)
+#define REQUIRE_MESSAGE(cond, ...) DOCTEST_REQUIRE_MESSAGE(cond, __VA_ARGS__)
+#define REQUIRE_FALSE_MESSAGE(cond, ...) DOCTEST_REQUIRE_FALSE_MESSAGE(cond, __VA_ARGS__)
+#define REQUIRE_THROWS_MESSAGE(expr, ...) DOCTEST_REQUIRE_THROWS_MESSAGE(expr, __VA_ARGS__)
+#define REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, __VA_ARGS__)
+#define REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, __VA_ARGS__)
+#define REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, __VA_ARGS__)
+#define REQUIRE_NOTHROW_MESSAGE(expr, ...) DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, __VA_ARGS__)
+
+#define SCENARIO(name) DOCTEST_SCENARIO(name)
+#define SCENARIO_CLASS(name) DOCTEST_SCENARIO_CLASS(name)
+#define SCENARIO_TEMPLATE(name, T, ...) DOCTEST_SCENARIO_TEMPLATE(name, T, __VA_ARGS__)
+#define SCENARIO_TEMPLATE_DEFINE(name, T, id) DOCTEST_SCENARIO_TEMPLATE_DEFINE(name, T, id)
+#define GIVEN(name) DOCTEST_GIVEN(name)
+#define WHEN(name) DOCTEST_WHEN(name)
+#define AND_WHEN(name) DOCTEST_AND_WHEN(name)
+#define THEN(name) DOCTEST_THEN(name)
+#define AND_THEN(name) DOCTEST_AND_THEN(name)
+
+#define WARN_EQ(...) DOCTEST_WARN_EQ(__VA_ARGS__)
+#define CHECK_EQ(...) DOCTEST_CHECK_EQ(__VA_ARGS__)
+#define REQUIRE_EQ(...) DOCTEST_REQUIRE_EQ(__VA_ARGS__)
+#define WARN_NE(...) DOCTEST_WARN_NE(__VA_ARGS__)
+#define CHECK_NE(...) DOCTEST_CHECK_NE(__VA_ARGS__)
+#define REQUIRE_NE(...) DOCTEST_REQUIRE_NE(__VA_ARGS__)
+#define WARN_GT(...) DOCTEST_WARN_GT(__VA_ARGS__)
+#define CHECK_GT(...) DOCTEST_CHECK_GT(__VA_ARGS__)
+#define REQUIRE_GT(...) DOCTEST_REQUIRE_GT(__VA_ARGS__)
+#define WARN_LT(...) DOCTEST_WARN_LT(__VA_ARGS__)
+#define CHECK_LT(...) DOCTEST_CHECK_LT(__VA_ARGS__)
+#define REQUIRE_LT(...) DOCTEST_REQUIRE_LT(__VA_ARGS__)
+#define WARN_GE(...) DOCTEST_WARN_GE(__VA_ARGS__)
+#define CHECK_GE(...) DOCTEST_CHECK_GE(__VA_ARGS__)
+#define REQUIRE_GE(...) DOCTEST_REQUIRE_GE(__VA_ARGS__)
+#define WARN_LE(...) DOCTEST_WARN_LE(__VA_ARGS__)
+#define CHECK_LE(...) DOCTEST_CHECK_LE(__VA_ARGS__)
+#define REQUIRE_LE(...) DOCTEST_REQUIRE_LE(__VA_ARGS__)
+#define WARN_UNARY(...) DOCTEST_WARN_UNARY(__VA_ARGS__)
+#define CHECK_UNARY(...) DOCTEST_CHECK_UNARY(__VA_ARGS__)
+#define REQUIRE_UNARY(...) DOCTEST_REQUIRE_UNARY(__VA_ARGS__)
+#define WARN_UNARY_FALSE(...) DOCTEST_WARN_UNARY_FALSE(__VA_ARGS__)
+#define CHECK_UNARY_FALSE(...) DOCTEST_CHECK_UNARY_FALSE(__VA_ARGS__)
+#define REQUIRE_UNARY_FALSE(...) DOCTEST_REQUIRE_UNARY_FALSE(__VA_ARGS__)
// KEPT FOR BACKWARDS COMPATIBILITY
-#define FAST_WARN_EQ DOCTEST_FAST_WARN_EQ
-#define FAST_CHECK_EQ DOCTEST_FAST_CHECK_EQ
-#define FAST_REQUIRE_EQ DOCTEST_FAST_REQUIRE_EQ
-#define FAST_WARN_NE DOCTEST_FAST_WARN_NE
-#define FAST_CHECK_NE DOCTEST_FAST_CHECK_NE
-#define FAST_REQUIRE_NE DOCTEST_FAST_REQUIRE_NE
-#define FAST_WARN_GT DOCTEST_FAST_WARN_GT
-#define FAST_CHECK_GT DOCTEST_FAST_CHECK_GT
-#define FAST_REQUIRE_GT DOCTEST_FAST_REQUIRE_GT
-#define FAST_WARN_LT DOCTEST_FAST_WARN_LT
-#define FAST_CHECK_LT DOCTEST_FAST_CHECK_LT
-#define FAST_REQUIRE_LT DOCTEST_FAST_REQUIRE_LT
-#define FAST_WARN_GE DOCTEST_FAST_WARN_GE
-#define FAST_CHECK_GE DOCTEST_FAST_CHECK_GE
-#define FAST_REQUIRE_GE DOCTEST_FAST_REQUIRE_GE
-#define FAST_WARN_LE DOCTEST_FAST_WARN_LE
-#define FAST_CHECK_LE DOCTEST_FAST_CHECK_LE
-#define FAST_REQUIRE_LE DOCTEST_FAST_REQUIRE_LE
-
-#define FAST_WARN_UNARY DOCTEST_FAST_WARN_UNARY
-#define FAST_CHECK_UNARY DOCTEST_FAST_CHECK_UNARY
-#define FAST_REQUIRE_UNARY DOCTEST_FAST_REQUIRE_UNARY
-#define FAST_WARN_UNARY_FALSE DOCTEST_FAST_WARN_UNARY_FALSE
-#define FAST_CHECK_UNARY_FALSE DOCTEST_FAST_CHECK_UNARY_FALSE
-#define FAST_REQUIRE_UNARY_FALSE DOCTEST_FAST_REQUIRE_UNARY_FALSE
-
-#define TEST_CASE_TEMPLATE_INSTANTIATE DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE
+#define FAST_WARN_EQ(...) DOCTEST_FAST_WARN_EQ(__VA_ARGS__)
+#define FAST_CHECK_EQ(...) DOCTEST_FAST_CHECK_EQ(__VA_ARGS__)
+#define FAST_REQUIRE_EQ(...) DOCTEST_FAST_REQUIRE_EQ(__VA_ARGS__)
+#define FAST_WARN_NE(...) DOCTEST_FAST_WARN_NE(__VA_ARGS__)
+#define FAST_CHECK_NE(...) DOCTEST_FAST_CHECK_NE(__VA_ARGS__)
+#define FAST_REQUIRE_NE(...) DOCTEST_FAST_REQUIRE_NE(__VA_ARGS__)
+#define FAST_WARN_GT(...) DOCTEST_FAST_WARN_GT(__VA_ARGS__)
+#define FAST_CHECK_GT(...) DOCTEST_FAST_CHECK_GT(__VA_ARGS__)
+#define FAST_REQUIRE_GT(...) DOCTEST_FAST_REQUIRE_GT(__VA_ARGS__)
+#define FAST_WARN_LT(...) DOCTEST_FAST_WARN_LT(__VA_ARGS__)
+#define FAST_CHECK_LT(...) DOCTEST_FAST_CHECK_LT(__VA_ARGS__)
+#define FAST_REQUIRE_LT(...) DOCTEST_FAST_REQUIRE_LT(__VA_ARGS__)
+#define FAST_WARN_GE(...) DOCTEST_FAST_WARN_GE(__VA_ARGS__)
+#define FAST_CHECK_GE(...) DOCTEST_FAST_CHECK_GE(__VA_ARGS__)
+#define FAST_REQUIRE_GE(...) DOCTEST_FAST_REQUIRE_GE(__VA_ARGS__)
+#define FAST_WARN_LE(...) DOCTEST_FAST_WARN_LE(__VA_ARGS__)
+#define FAST_CHECK_LE(...) DOCTEST_FAST_CHECK_LE(__VA_ARGS__)
+#define FAST_REQUIRE_LE(...) DOCTEST_FAST_REQUIRE_LE(__VA_ARGS__)
+
+#define FAST_WARN_UNARY(...) DOCTEST_FAST_WARN_UNARY(__VA_ARGS__)
+#define FAST_CHECK_UNARY(...) DOCTEST_FAST_CHECK_UNARY(__VA_ARGS__)
+#define FAST_REQUIRE_UNARY(...) DOCTEST_FAST_REQUIRE_UNARY(__VA_ARGS__)
+#define FAST_WARN_UNARY_FALSE(...) DOCTEST_FAST_WARN_UNARY_FALSE(__VA_ARGS__)
+#define FAST_CHECK_UNARY_FALSE(...) DOCTEST_FAST_CHECK_UNARY_FALSE(__VA_ARGS__)
+#define FAST_REQUIRE_UNARY_FALSE(...) DOCTEST_FAST_REQUIRE_UNARY_FALSE(__VA_ARGS__)
+
+#define TEST_CASE_TEMPLATE_INSTANTIATE(id, ...) DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE(id, __VA_ARGS__)
#endif // DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES
@@ -2689,6 +2746,7 @@ DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-field-initializers")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-member-function")
+DOCTEST_CLANG_SUPPRESS_WARNING("-Wnonportable-system-include-path")
DOCTEST_GCC_SUPPRESS_WARNING_PUSH
DOCTEST_GCC_SUPPRESS_WARNING("-Wunknown-pragmas")
@@ -2794,11 +2852,7 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN
#ifdef __AFXDLL
#include <AfxWin.h>
#else
-#if defined(__MINGW32__) || defined(__MINGW64__)
#include <windows.h>
-#else // MINGW
-#include <Windows.h>
-#endif // MINGW
#endif
#include <io.h>
@@ -2834,12 +2888,24 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END
#define DOCTEST_THREAD_LOCAL thread_local
#endif
+#ifndef DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES
+#define DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES 32
+#endif
+
+#ifndef DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE
+#define DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE 64
+#endif
+
#ifdef DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS
#define DOCTEST_OPTIONS_PREFIX_DISPLAY DOCTEST_CONFIG_OPTIONS_PREFIX
#else
#define DOCTEST_OPTIONS_PREFIX_DISPLAY ""
#endif
+#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
+#define DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS
+#endif
+
namespace doctest {
bool is_running_in_test = false;
@@ -2972,18 +3038,105 @@ typedef timer_large_integer::type ticks_t;
ticks_t m_ticks = 0;
};
+#ifdef DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS
+ template <typename T>
+ using AtomicOrMultiLaneAtomic = std::atomic<T>;
+#else // DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS
+ // Provides a multilane implementation of an atomic variable that supports add, sub, load,
+ // store. Instead of using a single atomic variable, this splits up into multiple ones,
+ // each sitting on a separate cache line. The goal is to provide a speedup when most
+ // operations are modifying. It achieves this with two properties:
+ //
+ // * Multiple atomics are used, so chance of congestion from the same atomic is reduced.
+ // * Each atomic sits on a separate cache line, so false sharing is reduced.
+ //
+ // The disadvantage is that there is a small overhead due to the use of TLS, and load/store
+ // is slower because all atomics have to be accessed.
+ template <typename T>
+ class MultiLaneAtomic
+ {
+ struct CacheLineAlignedAtomic
+ {
+ std::atomic<T> atomic{};
+ char padding[DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE - sizeof(std::atomic<T>)];
+ };
+ CacheLineAlignedAtomic m_atomics[DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES];
+
+ static_assert(sizeof(CacheLineAlignedAtomic) == DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE,
+ "guarantee one atomic takes exactly one cache line");
+
+ public:
+ T operator++() DOCTEST_NOEXCEPT { return fetch_add(1) + 1; }
+
+ T operator++(int) DOCTEST_NOEXCEPT { return fetch_add(1); }
+
+ T fetch_add(T arg, std::memory_order order = std::memory_order_seq_cst) DOCTEST_NOEXCEPT {
+ return myAtomic().fetch_add(arg, order);
+ }
+
+ T fetch_sub(T arg, std::memory_order order = std::memory_order_seq_cst) DOCTEST_NOEXCEPT {
+ return myAtomic().fetch_sub(arg, order);
+ }
+
+ operator T() const DOCTEST_NOEXCEPT { return load(); }
+
+ T load(std::memory_order order = std::memory_order_seq_cst) const DOCTEST_NOEXCEPT {
+ auto result = T();
+ for(auto const& c : m_atomics) {
+ result += c.atomic.load(order);
+ }
+ return result;
+ }
+
+ T operator=(T desired) DOCTEST_NOEXCEPT {
+ store(desired);
+ return desired;
+ }
+
+ void store(T desired, std::memory_order order = std::memory_order_seq_cst) DOCTEST_NOEXCEPT {
+ // first value becomes desired", all others become 0.
+ for(auto& c : m_atomics) {
+ c.atomic.store(desired, order);
+ desired = {};
+ }
+ }
+
+ private:
+ // Each thread has a different atomic that it operates on. If more than NumLanes threads
+ // use this, some will use the same atomic. So performance will degrate a bit, but still
+ // everything will work.
+ //
+ // The logic here is a bit tricky. The call should be as fast as possible, so that there
+ // is minimal to no overhead in determining the correct atomic for the current thread.
+ //
+ // 1. A global static counter laneCounter counts continuously up.
+ // 2. Each successive thread will use modulo operation of that counter so it gets an atomic
+ // assigned in a round-robin fashion.
+ // 3. This tlsLaneIdx is stored in the thread local data, so it is directly available with
+ // little overhead.
+ std::atomic<T>& myAtomic() DOCTEST_NOEXCEPT {
+ static std::atomic<size_t> laneCounter;
+ DOCTEST_THREAD_LOCAL size_t tlsLaneIdx =
+ laneCounter++ % DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES;
+
+ return m_atomics[tlsLaneIdx].atomic;
+ }
+ };
+
+ template <typename T>
+ using AtomicOrMultiLaneAtomic = MultiLaneAtomic<T>;
+#endif // DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS
+
// this holds both parameters from the command line and runtime data for tests
struct ContextState : ContextOptions, TestRunStats, CurrentTestCaseStats
{
- std::atomic<int> numAssertsCurrentTest_atomic;
- std::atomic<int> numAssertsFailedCurrentTest_atomic;
+ AtomicOrMultiLaneAtomic<int> numAssertsCurrentTest_atomic;
+ AtomicOrMultiLaneAtomic<int> numAssertsFailedCurrentTest_atomic;
std::vector<std::vector<String>> filters = decltype(filters)(9); // 9 different filters
std::vector<IReporter*> reporters_currently_used;
- const TestCase* currentTest = nullptr;
-
assert_handler ah = nullptr;
Timer timer;
@@ -3093,14 +3246,16 @@ String::String(const char* in)
String::String(const char* in, unsigned in_size) {
using namespace std;
if(in_size <= last) {
- memcpy(buf, in, in_size + 1);
+ memcpy(buf, in, in_size);
+ buf[in_size] = '\0';
setLast(last - in_size);
} else {
setOnHeap();
data.size = in_size;
data.capacity = data.size + 1;
data.ptr = new char[data.capacity];
- memcpy(data.ptr, in, in_size + 1);
+ memcpy(data.ptr, in, in_size);
+ data.ptr[in_size] = '\0';
}
}
@@ -3471,7 +3626,7 @@ int registerReporter(const char*, int, IReporter*) { return 0; }
namespace doctest_detail_test_suite_ns {
// holds the current test suite
doctest::detail::TestSuite& getCurrentTestSuite() {
- static doctest::detail::TestSuite data;
+ static doctest::detail::TestSuite data{};
return data;
}
} // namespace doctest_detail_test_suite_ns
@@ -3583,7 +3738,7 @@ namespace detail {
Subcase::Subcase(const String& name, const char* file, int line)
: m_signature({name, file, line}) {
- ContextState* s = g_cs;
+ auto* s = g_cs;
// check subcase filters
if(s->subcasesStack.size() < size_t(s->subcase_filter_levels)) {
@@ -3625,7 +3780,7 @@ namespace detail {
g_cs->subcasesPassed.insert(g_cs->subcasesStack);
g_cs->subcasesStack.pop_back();
-#if defined(__cpp_lib_uncaught_exceptions) && __cpp_lib_uncaught_exceptions >= 201411L
+#if defined(__cpp_lib_uncaught_exceptions) && __cpp_lib_uncaught_exceptions >= 201411L && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200)
if(std::uncaught_exceptions() > 0
#else
if(std::uncaught_exception()
@@ -3660,6 +3815,8 @@ namespace detail {
// clear state
m_description = nullptr;
m_skip = false;
+ m_no_breaks = false;
+ m_no_output = false;
m_may_fail = false;
m_should_fail = false;
m_expected_failures = 0;
@@ -3675,6 +3832,8 @@ namespace detail {
m_test_suite = test_suite.m_test_suite;
m_description = test_suite.m_description;
m_skip = test_suite.m_skip;
+ m_no_breaks = test_suite.m_no_breaks;
+ m_no_output = test_suite.m_no_output;
m_may_fail = test_suite.m_may_fail;
m_should_fail = test_suite.m_should_fail;
m_expected_failures = test_suite.m_expected_failures;
@@ -3721,14 +3880,20 @@ namespace detail {
// this will be used only to differentiate between test cases - not relevant for sorting
if(m_line != other.m_line)
return m_line < other.m_line;
- const int file_cmp = m_file.compare(other.m_file);
- if(file_cmp != 0)
- return file_cmp < 0;
const int name_cmp = strcmp(m_name, other.m_name);
if(name_cmp != 0)
return name_cmp < 0;
+ const int file_cmp = m_file.compare(other.m_file);
+ if(file_cmp != 0)
+ return file_cmp < 0;
return m_template_id < other.m_template_id;
}
+
+ // all the registered tests
+ std::set<TestCase>& getRegisteredTests() {
+ static std::set<TestCase> data;
+ return data;
+ }
} // namespace detail
namespace {
using namespace detail;
@@ -3760,12 +3925,6 @@ namespace {
return suiteOrderComparator(lhs, rhs);
}
- // all the registered tests
- std::set<TestCase>& getRegisteredTests() {
- static std::set<TestCase> data;
- return data;
- }
-
#ifdef DOCTEST_CONFIG_COLORS_WINDOWS
HANDLE g_stdoutHandle;
WORD g_origFgAttrs;
@@ -3994,7 +4153,7 @@ namespace detail {
// ContextScope has been destroyed (base class destructors run after derived class destructors).
// Instead, ContextScope calls this method directly from its destructor.
void ContextScopeBase::destroy() {
-#if defined(__cpp_lib_uncaught_exceptions) && __cpp_lib_uncaught_exceptions >= 201411L
+#if defined(__cpp_lib_uncaught_exceptions) && __cpp_lib_uncaught_exceptions >= 201411L && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200)
if(std::uncaught_exceptions() > 0) {
#else
if(std::uncaught_exception()) {
@@ -4016,7 +4175,9 @@ namespace {
#if !defined(DOCTEST_CONFIG_POSIX_SIGNALS) && !defined(DOCTEST_CONFIG_WINDOWS_SEH)
struct FatalConditionHandler
{
- void reset() {}
+ static void reset() {}
+ static void allocateAltStackMem() {}
+ static void freeAltStackMem() {}
};
#else // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH
@@ -4069,6 +4230,9 @@ namespace {
std::exit(EXIT_FAILURE);
}
+ static void allocateAltStackMem() {}
+ static void freeAltStackMem() {}
+
FatalConditionHandler() {
isSet = true;
// 32k seems enough for doctest to handle stack overflow,
@@ -4086,7 +4250,7 @@ namespace {
// - std::terminate is called FROM THE TEST RUNNER THREAD
// - an exception is thrown from a destructor FROM THE TEST RUNNER THREAD
original_terminate_handler = std::get_terminate();
- std::set_terminate([]() noexcept {
+ std::set_terminate([]() DOCTEST_NOEXCEPT {
reportFatal("Terminate handler called");
if(isDebuggerActive() && !g_cs->no_breaks)
DOCTEST_BREAK_INTO_DEBUGGER();
@@ -4097,7 +4261,7 @@ namespace {
// - std::terminate is called FROM A DIFFERENT THREAD
// - an exception is thrown from a destructor FROM A DIFFERENT THREAD
// - an uncaught exception is thrown FROM A DIFFERENT THREAD
- prev_sigabrt_handler = std::signal(SIGABRT, [](int signal) noexcept {
+ prev_sigabrt_handler = std::signal(SIGABRT, [](int signal) DOCTEST_NOEXCEPT {
if(signal == SIGABRT) {
reportFatal("SIGABRT - Abort (abnormal termination) signal");
if(isDebuggerActive() && !g_cs->no_breaks)
@@ -4135,8 +4299,8 @@ namespace {
SetErrorMode(prev_error_mode_1);
_set_error_mode(prev_error_mode_2);
_set_abort_behavior(prev_abort_behavior, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
- _CrtSetReportMode(_CRT_ASSERT, prev_report_mode);
- _CrtSetReportFile(_CRT_ASSERT, prev_report_file);
+ static_cast<void>(_CrtSetReportMode(_CRT_ASSERT, prev_report_mode));
+ static_cast<void>(_CrtSetReportFile(_CRT_ASSERT, prev_report_file));
isSet = false;
}
}
@@ -4186,7 +4350,8 @@ namespace {
static bool isSet;
static struct sigaction oldSigActions[DOCTEST_COUNTOF(signalDefs)];
static stack_t oldSigStack;
- static char altStackMem[4 * SIGSTKSZ];
+ static size_t altStackSize;
+ static char* altStackMem;
static void handleSignal(int sig) {
const char* name = "<unknown signal>";
@@ -4202,11 +4367,19 @@ namespace {
raise(sig);
}
+ static void allocateAltStackMem() {
+ altStackMem = new char[altStackSize];
+ }
+
+ static void freeAltStackMem() {
+ delete[] altStackMem;
+ }
+
FatalConditionHandler() {
isSet = true;
stack_t sigStack;
sigStack.ss_sp = altStackMem;
- sigStack.ss_size = sizeof(altStackMem);
+ sigStack.ss_size = altStackSize;
sigStack.ss_flags = 0;
sigaltstack(&sigStack, &oldSigStack);
struct sigaction sa = {};
@@ -4231,10 +4404,11 @@ namespace {
}
};
- bool FatalConditionHandler::isSet = false;
+ bool FatalConditionHandler::isSet = false;
struct sigaction FatalConditionHandler::oldSigActions[DOCTEST_COUNTOF(signalDefs)] = {};
- stack_t FatalConditionHandler::oldSigStack = {};
- char FatalConditionHandler::altStackMem[] = {};
+ stack_t FatalConditionHandler::oldSigStack = {};
+ size_t FatalConditionHandler::altStackSize = 4 * SIGSTKSZ;
+ char* FatalConditionHandler::altStackMem = nullptr;
#endif // DOCTEST_PLATFORM_WINDOWS
#endif // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH
@@ -4336,8 +4510,8 @@ namespace detail {
failed_out_of_a_testing_context(*this);
}
- return m_failed && isDebuggerActive() &&
- !getContextOptions()->no_breaks; // break into debugger
+ return m_failed && isDebuggerActive() && !getContextOptions()->no_breaks &&
+ (g_cs->currentTest == nullptr || !g_cs->currentTest->m_no_breaks); // break into debugger
}
void ResultBuilder::react() const {
@@ -4387,7 +4561,8 @@ namespace detail {
addFailedAssert(m_severity);
}
- return isDebuggerActive() && !getContextOptions()->no_breaks && !isWarn; // break
+ return isDebuggerActive() && !getContextOptions()->no_breaks && !isWarn &&
+ (g_cs->currentTest == nullptr || !g_cs->currentTest->m_no_breaks); // break into debugger
}
void MessageBuilder::react() {
@@ -5495,7 +5670,7 @@ namespace {
<< Whitespace(sizePrefixDisplay*1) << "output filename\n";
s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ob, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "order-by=<string> "
<< Whitespace(sizePrefixDisplay*1) << "how the tests should be ordered\n";
- s << Whitespace(sizePrefixDisplay*3) << " <string> - by [file/suite/name/rand]\n";
+ s << Whitespace(sizePrefixDisplay*3) << " <string> - [file/suite/name/rand/none]\n";
s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "rs, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "rand-seed=<int> "
<< Whitespace(sizePrefixDisplay*1) << "seed for random ordering\n";
s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "f, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "first=<int> "
@@ -5668,6 +5843,9 @@ namespace {
}
void test_case_end(const CurrentTestCaseStats& st) override {
+ if(tc->m_no_output)
+ return;
+
// log the preamble of the test case only if there is something
// else to print - something other than that an assert has failed
if(opt.duration ||
@@ -5702,6 +5880,9 @@ namespace {
}
void test_case_exception(const TestCaseException& e) override {
+ if(tc->m_no_output)
+ return;
+
logTestStart();
file_line_to_stream(tc->m_file.c_str(), tc->m_line, " ");
@@ -5736,7 +5917,7 @@ namespace {
}
void log_assert(const AssertData& rb) override {
- if(!rb.m_failed && !opt.success)
+ if((!rb.m_failed && !opt.success) || tc->m_no_output)
return;
std::lock_guard<std::mutex> lock(mutex);
@@ -5752,6 +5933,9 @@ namespace {
}
void log_message(const MessageData& mb) override {
+ if(tc->m_no_output)
+ return;
+
std::lock_guard<std::mutex> lock(mutex);
logTestStart();
@@ -5782,8 +5966,10 @@ namespace {
bool with_col = g_no_colors; \
g_no_colors = false; \
ConsoleReporter::func(arg); \
- DOCTEST_OUTPUT_DEBUG_STRING(oss.str().c_str()); \
- oss.str(""); \
+ if(oss.tellp() != std::streampos{}) { \
+ DOCTEST_OUTPUT_DEBUG_STRING(oss.str().c_str()); \
+ oss.str(""); \
+ } \
g_no_colors = with_col; \
}
@@ -5970,7 +6156,7 @@ void Context::parseArgs(int argc, const char* const* argv, bool withDefaults) {
#define DOCTEST_PARSE_AS_BOOL_OR_FLAG(name, sname, var, default) \
if(parseIntOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX name "=", option_bool, intRes) || \
parseIntOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX sname "=", option_bool, intRes)) \
- p->var = !!intRes; \
+ p->var = static_cast<bool>(intRes); \
else if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX name) || \
parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX sname)) \
p->var = true; \
@@ -6115,7 +6301,11 @@ int Context::run() {
p->cout = &fstr;
}
+ FatalConditionHandler::allocateAltStackMem();
+
auto cleanup_and_return = [&]() {
+ FatalConditionHandler::freeAltStackMem();
+
if(fstr.is_open())
fstr.close();
@@ -6187,6 +6377,9 @@ int Context::run() {
first[i] = first[idxToSwap];
first[idxToSwap] = temp;
}
+ } else if(p->order_by.compare("none", true) == 0) {
+ // means no sorting - beneficial for death tests which call into the executable
+ // with a specific test case in mind - we don't want to slow down the startup times
}
}
@@ -6286,10 +6479,13 @@ int Context::run() {
#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
try {
#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
+// MSVC 2015 diagnoses fatalConditionHandler as unused (because reset() is a static method)
+DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4101) // unreferenced local variable
FatalConditionHandler fatalConditionHandler; // Handle signals
// execute the test
tc.m_test();
fatalConditionHandler.reset();
+DOCTEST_MSVC_SUPPRESS_WARNING_POP
#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
} catch(const TestFailureException&) {
p->failure_flags |= TestCaseFailureReason::AssertFailure;
diff --git a/thirdparty/fonts/Tamsyn10x20.png b/thirdparty/fonts/Tamsyn10x20.png
deleted file mode 100644
index b2d3b5cb5c..0000000000
--- a/thirdparty/fonts/Tamsyn10x20.png
+++ /dev/null
Binary files differ
diff --git a/thirdparty/fonts/Tamsyn5x9.png b/thirdparty/fonts/Tamsyn5x9.png
deleted file mode 100644
index ac42b32641..0000000000
--- a/thirdparty/fonts/Tamsyn5x9.png
+++ /dev/null
Binary files differ
diff --git a/thirdparty/meshoptimizer/patches/attribute-aware-simplify-distance-only-metric.patch b/thirdparty/meshoptimizer/patches/attribute-aware-simplify-distance-only-metric.patch
new file mode 100644
index 0000000000..54132a6c86
--- /dev/null
+++ b/thirdparty/meshoptimizer/patches/attribute-aware-simplify-distance-only-metric.patch
@@ -0,0 +1,176 @@
+diff --git a/thirdparty/meshoptimizer/simplifier.cpp b/thirdparty/meshoptimizer/simplifier.cpp
+index 0f10ebef4b..cf5db4e119 100644
+--- a/thirdparty/meshoptimizer/simplifier.cpp
++++ b/thirdparty/meshoptimizer/simplifier.cpp
+@@ -20,7 +20,7 @@
+ #define TRACESTATS(i) (void)0
+ #endif
+
+-#define ATTRIBUTES 8
++#define ATTRIBUTES 3
+
+ // This work is based on:
+ // Michael Garland and Paul S. Heckbert. Surface simplification using quadric error metrics. 1997
+@@ -445,6 +445,7 @@ struct Collapse
+ float error;
+ unsigned int errorui;
+ };
++ float distance_error;
+ };
+
+ static float normalize(Vector3& v)
+@@ -525,6 +526,34 @@ static float quadricError(const Quadric& Q, const Vector3& v)
+ return fabsf(r) * s;
+ }
+
++static float quadricErrorNoAttributes(const Quadric& Q, const Vector3& v)
++{
++ float rx = Q.b0;
++ float ry = Q.b1;
++ float rz = Q.b2;
++
++ rx += Q.a10 * v.y;
++ ry += Q.a21 * v.z;
++ rz += Q.a20 * v.x;
++
++ rx *= 2;
++ ry *= 2;
++ rz *= 2;
++
++ rx += Q.a00 * v.x;
++ ry += Q.a11 * v.y;
++ rz += Q.a22 * v.z;
++
++ float r = Q.c;
++ r += rx * v.x;
++ r += ry * v.y;
++ r += rz * v.z;
++
++ float s = Q.w == 0.f ? 0.f : 1.f / Q.w;
++
++ return fabsf(r) * s;
++}
++
+ static void quadricFromPlane(Quadric& Q, float a, float b, float c, float d, float w)
+ {
+ float aw = a * w;
+@@ -680,7 +709,7 @@ static void quadricUpdateAttributes(Quadric& Q, const Vector3& p0, const Vector3
+ }
+ #endif
+
+-static void fillFaceQuadrics(Quadric* vertex_quadrics, const unsigned int* indices, size_t index_count, const Vector3* vertex_positions, const unsigned int* remap)
++static void fillFaceQuadrics(Quadric* vertex_quadrics, Quadric* vertex_no_attrib_quadrics, const unsigned int* indices, size_t index_count, const Vector3* vertex_positions, const unsigned int* remap)
+ {
+ for (size_t i = 0; i < index_count; i += 3)
+ {
+@@ -690,6 +719,9 @@ static void fillFaceQuadrics(Quadric* vertex_quadrics, const unsigned int* indic
+
+ Quadric Q;
+ quadricFromTriangle(Q, vertex_positions[i0], vertex_positions[i1], vertex_positions[i2], 1.f);
++ quadricAdd(vertex_no_attrib_quadrics[remap[i0]], Q);
++ quadricAdd(vertex_no_attrib_quadrics[remap[i1]], Q);
++ quadricAdd(vertex_no_attrib_quadrics[remap[i2]], Q);
+
+ #if ATTRIBUTES
+ quadricUpdateAttributes(Q, vertex_positions[i0], vertex_positions[i1], vertex_positions[i2], Q.w);
+@@ -700,7 +732,7 @@ static void fillFaceQuadrics(Quadric* vertex_quadrics, const unsigned int* indic
+ }
+ }
+
+-static void fillEdgeQuadrics(Quadric* vertex_quadrics, const unsigned int* indices, size_t index_count, const Vector3* vertex_positions, const unsigned int* remap, const unsigned char* vertex_kind, const unsigned int* loop, const unsigned int* loopback)
++static void fillEdgeQuadrics(Quadric* vertex_quadrics, Quadric* vertex_no_attrib_quadrics, const unsigned int* indices, size_t index_count, const Vector3* vertex_positions, const unsigned int* remap, const unsigned char* vertex_kind, const unsigned int* loop, const unsigned int* loopback)
+ {
+ for (size_t i = 0; i < index_count; i += 3)
+ {
+@@ -744,6 +776,9 @@ static void fillEdgeQuadrics(Quadric* vertex_quadrics, const unsigned int* indic
+
+ quadricAdd(vertex_quadrics[remap[i0]], Q);
+ quadricAdd(vertex_quadrics[remap[i1]], Q);
++
++ quadricAdd(vertex_no_attrib_quadrics[remap[i0]], Q);
++ quadricAdd(vertex_no_attrib_quadrics[remap[i1]], Q);
+ }
+ }
+ }
+@@ -848,7 +883,7 @@ static size_t pickEdgeCollapses(Collapse* collapses, const unsigned int* indices
+ return collapse_count;
+ }
+
+-static void rankEdgeCollapses(Collapse* collapses, size_t collapse_count, const Vector3* vertex_positions, const Quadric* vertex_quadrics, const unsigned int* remap)
++static void rankEdgeCollapses(Collapse* collapses, size_t collapse_count, const Vector3* vertex_positions, const Quadric* vertex_quadrics, const Quadric* vertex_no_attrib_quadrics, const unsigned int* remap)
+ {
+ for (size_t i = 0; i < collapse_count; ++i)
+ {
+@@ -868,10 +903,14 @@ static void rankEdgeCollapses(Collapse* collapses, size_t collapse_count, const
+ float ei = quadricError(qi, vertex_positions[i1]);
+ float ej = quadricError(qj, vertex_positions[j1]);
+
++ const Quadric& naqi = vertex_no_attrib_quadrics[remap[i0]];
++ const Quadric& naqj = vertex_no_attrib_quadrics[remap[j0]];
++
+ // pick edge direction with minimal error
+ c.v0 = ei <= ej ? i0 : j0;
+ c.v1 = ei <= ej ? i1 : j1;
+ c.error = ei <= ej ? ei : ej;
++ c.distance_error = ei <= ej ? quadricErrorNoAttributes(naqi, vertex_positions[i1]) : quadricErrorNoAttributes(naqj, vertex_positions[j1]);
+ }
+ }
+
+@@ -968,7 +1007,7 @@ static void sortEdgeCollapses(unsigned int* sort_order, const Collapse* collapse
+ }
+ }
+
+-static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char* collapse_locked, Quadric* vertex_quadrics, const Collapse* collapses, size_t collapse_count, const unsigned int* collapse_order, const unsigned int* remap, const unsigned int* wedge, const unsigned char* vertex_kind, const Vector3* vertex_positions, const EdgeAdjacency& adjacency, size_t triangle_collapse_goal, float error_limit, float& result_error)
++static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char* collapse_locked, Quadric* vertex_quadrics, Quadric* vertex_no_attrib_quadrics, const Collapse* collapses, size_t collapse_count, const unsigned int* collapse_order, const unsigned int* remap, const unsigned int* wedge, const unsigned char* vertex_kind, const Vector3* vertex_positions, const EdgeAdjacency& adjacency, size_t triangle_collapse_goal, float error_limit, float& result_error)
+ {
+ size_t edge_collapses = 0;
+ size_t triangle_collapses = 0;
+@@ -1030,6 +1069,7 @@ static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char*
+ assert(collapse_remap[r1] == r1);
+
+ quadricAdd(vertex_quadrics[r1], vertex_quadrics[r0]);
++ quadricAdd(vertex_no_attrib_quadrics[r1], vertex_no_attrib_quadrics[r0]);
+
+ if (vertex_kind[i0] == Kind_Complex)
+ {
+@@ -1067,7 +1107,7 @@ static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char*
+ triangle_collapses += (vertex_kind[i0] == Kind_Border) ? 1 : 2;
+ edge_collapses++;
+
+- result_error = result_error < c.error ? c.error : result_error;
++ result_error = result_error < c.distance_error ? c.distance_error : result_error;
+ }
+
+ #if TRACE
+@@ -1455,9 +1495,11 @@ size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned
+
+ Quadric* vertex_quadrics = allocator.allocate<Quadric>(vertex_count);
+ memset(vertex_quadrics, 0, vertex_count * sizeof(Quadric));
++ Quadric* vertex_no_attrib_quadrics = allocator.allocate<Quadric>(vertex_count);
++ memset(vertex_no_attrib_quadrics, 0, vertex_count * sizeof(Quadric));
+
+- fillFaceQuadrics(vertex_quadrics, indices, index_count, vertex_positions, remap);
+- fillEdgeQuadrics(vertex_quadrics, indices, index_count, vertex_positions, remap, vertex_kind, loop, loopback);
++ fillFaceQuadrics(vertex_quadrics, vertex_no_attrib_quadrics, indices, index_count, vertex_positions, remap);
++ fillEdgeQuadrics(vertex_quadrics, vertex_no_attrib_quadrics, indices, index_count, vertex_positions, remap, vertex_kind, loop, loopback);
+
+ if (result != indices)
+ memcpy(result, indices, index_count * sizeof(unsigned int));
+@@ -1488,7 +1530,7 @@ size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned
+ if (edge_collapse_count == 0)
+ break;
+
+- rankEdgeCollapses(edge_collapses, edge_collapse_count, vertex_positions, vertex_quadrics, remap);
++ rankEdgeCollapses(edge_collapses, edge_collapse_count, vertex_positions, vertex_quadrics, vertex_no_attrib_quadrics, remap);
+
+ #if TRACE > 1
+ dumpEdgeCollapses(edge_collapses, edge_collapse_count, vertex_kind);
+@@ -1507,7 +1549,7 @@ size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned
+ printf("pass %d: ", int(pass_count++));
+ #endif
+
+- size_t collapses = performEdgeCollapses(collapse_remap, collapse_locked, vertex_quadrics, edge_collapses, edge_collapse_count, collapse_order, remap, wedge, vertex_kind, vertex_positions, adjacency, triangle_collapse_goal, error_limit, result_error);
++ size_t collapses = performEdgeCollapses(collapse_remap, collapse_locked, vertex_quadrics, vertex_no_attrib_quadrics, edge_collapses, edge_collapse_count, collapse_order, remap, wedge, vertex_kind, vertex_positions, adjacency, triangle_collapse_goal, error_limit, result_error);
+
+ // no edges can be collapsed any more due to hitting the error limit or triangle collapse limit
+ if (collapses == 0)
diff --git a/thirdparty/meshoptimizer/simplifier.cpp b/thirdparty/meshoptimizer/simplifier.cpp
index 0f10ebef4b..cf5db4e119 100644
--- a/thirdparty/meshoptimizer/simplifier.cpp
+++ b/thirdparty/meshoptimizer/simplifier.cpp
@@ -20,7 +20,7 @@
#define TRACESTATS(i) (void)0
#endif
-#define ATTRIBUTES 8
+#define ATTRIBUTES 3
// This work is based on:
// Michael Garland and Paul S. Heckbert. Surface simplification using quadric error metrics. 1997
@@ -445,6 +445,7 @@ struct Collapse
float error;
unsigned int errorui;
};
+ float distance_error;
};
static float normalize(Vector3& v)
@@ -525,6 +526,34 @@ static float quadricError(const Quadric& Q, const Vector3& v)
return fabsf(r) * s;
}
+static float quadricErrorNoAttributes(const Quadric& Q, const Vector3& v)
+{
+ float rx = Q.b0;
+ float ry = Q.b1;
+ float rz = Q.b2;
+
+ rx += Q.a10 * v.y;
+ ry += Q.a21 * v.z;
+ rz += Q.a20 * v.x;
+
+ rx *= 2;
+ ry *= 2;
+ rz *= 2;
+
+ rx += Q.a00 * v.x;
+ ry += Q.a11 * v.y;
+ rz += Q.a22 * v.z;
+
+ float r = Q.c;
+ r += rx * v.x;
+ r += ry * v.y;
+ r += rz * v.z;
+
+ float s = Q.w == 0.f ? 0.f : 1.f / Q.w;
+
+ return fabsf(r) * s;
+}
+
static void quadricFromPlane(Quadric& Q, float a, float b, float c, float d, float w)
{
float aw = a * w;
@@ -680,7 +709,7 @@ static void quadricUpdateAttributes(Quadric& Q, const Vector3& p0, const Vector3
}
#endif
-static void fillFaceQuadrics(Quadric* vertex_quadrics, const unsigned int* indices, size_t index_count, const Vector3* vertex_positions, const unsigned int* remap)
+static void fillFaceQuadrics(Quadric* vertex_quadrics, Quadric* vertex_no_attrib_quadrics, const unsigned int* indices, size_t index_count, const Vector3* vertex_positions, const unsigned int* remap)
{
for (size_t i = 0; i < index_count; i += 3)
{
@@ -690,6 +719,9 @@ static void fillFaceQuadrics(Quadric* vertex_quadrics, const unsigned int* indic
Quadric Q;
quadricFromTriangle(Q, vertex_positions[i0], vertex_positions[i1], vertex_positions[i2], 1.f);
+ quadricAdd(vertex_no_attrib_quadrics[remap[i0]], Q);
+ quadricAdd(vertex_no_attrib_quadrics[remap[i1]], Q);
+ quadricAdd(vertex_no_attrib_quadrics[remap[i2]], Q);
#if ATTRIBUTES
quadricUpdateAttributes(Q, vertex_positions[i0], vertex_positions[i1], vertex_positions[i2], Q.w);
@@ -700,7 +732,7 @@ static void fillFaceQuadrics(Quadric* vertex_quadrics, const unsigned int* indic
}
}
-static void fillEdgeQuadrics(Quadric* vertex_quadrics, const unsigned int* indices, size_t index_count, const Vector3* vertex_positions, const unsigned int* remap, const unsigned char* vertex_kind, const unsigned int* loop, const unsigned int* loopback)
+static void fillEdgeQuadrics(Quadric* vertex_quadrics, Quadric* vertex_no_attrib_quadrics, const unsigned int* indices, size_t index_count, const Vector3* vertex_positions, const unsigned int* remap, const unsigned char* vertex_kind, const unsigned int* loop, const unsigned int* loopback)
{
for (size_t i = 0; i < index_count; i += 3)
{
@@ -744,6 +776,9 @@ static void fillEdgeQuadrics(Quadric* vertex_quadrics, const unsigned int* indic
quadricAdd(vertex_quadrics[remap[i0]], Q);
quadricAdd(vertex_quadrics[remap[i1]], Q);
+
+ quadricAdd(vertex_no_attrib_quadrics[remap[i0]], Q);
+ quadricAdd(vertex_no_attrib_quadrics[remap[i1]], Q);
}
}
}
@@ -848,7 +883,7 @@ static size_t pickEdgeCollapses(Collapse* collapses, const unsigned int* indices
return collapse_count;
}
-static void rankEdgeCollapses(Collapse* collapses, size_t collapse_count, const Vector3* vertex_positions, const Quadric* vertex_quadrics, const unsigned int* remap)
+static void rankEdgeCollapses(Collapse* collapses, size_t collapse_count, const Vector3* vertex_positions, const Quadric* vertex_quadrics, const Quadric* vertex_no_attrib_quadrics, const unsigned int* remap)
{
for (size_t i = 0; i < collapse_count; ++i)
{
@@ -868,10 +903,14 @@ static void rankEdgeCollapses(Collapse* collapses, size_t collapse_count, const
float ei = quadricError(qi, vertex_positions[i1]);
float ej = quadricError(qj, vertex_positions[j1]);
+ const Quadric& naqi = vertex_no_attrib_quadrics[remap[i0]];
+ const Quadric& naqj = vertex_no_attrib_quadrics[remap[j0]];
+
// pick edge direction with minimal error
c.v0 = ei <= ej ? i0 : j0;
c.v1 = ei <= ej ? i1 : j1;
c.error = ei <= ej ? ei : ej;
+ c.distance_error = ei <= ej ? quadricErrorNoAttributes(naqi, vertex_positions[i1]) : quadricErrorNoAttributes(naqj, vertex_positions[j1]);
}
}
@@ -968,7 +1007,7 @@ static void sortEdgeCollapses(unsigned int* sort_order, const Collapse* collapse
}
}
-static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char* collapse_locked, Quadric* vertex_quadrics, const Collapse* collapses, size_t collapse_count, const unsigned int* collapse_order, const unsigned int* remap, const unsigned int* wedge, const unsigned char* vertex_kind, const Vector3* vertex_positions, const EdgeAdjacency& adjacency, size_t triangle_collapse_goal, float error_limit, float& result_error)
+static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char* collapse_locked, Quadric* vertex_quadrics, Quadric* vertex_no_attrib_quadrics, const Collapse* collapses, size_t collapse_count, const unsigned int* collapse_order, const unsigned int* remap, const unsigned int* wedge, const unsigned char* vertex_kind, const Vector3* vertex_positions, const EdgeAdjacency& adjacency, size_t triangle_collapse_goal, float error_limit, float& result_error)
{
size_t edge_collapses = 0;
size_t triangle_collapses = 0;
@@ -1030,6 +1069,7 @@ static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char*
assert(collapse_remap[r1] == r1);
quadricAdd(vertex_quadrics[r1], vertex_quadrics[r0]);
+ quadricAdd(vertex_no_attrib_quadrics[r1], vertex_no_attrib_quadrics[r0]);
if (vertex_kind[i0] == Kind_Complex)
{
@@ -1067,7 +1107,7 @@ static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char*
triangle_collapses += (vertex_kind[i0] == Kind_Border) ? 1 : 2;
edge_collapses++;
- result_error = result_error < c.error ? c.error : result_error;
+ result_error = result_error < c.distance_error ? c.distance_error : result_error;
}
#if TRACE
@@ -1455,9 +1495,11 @@ size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned
Quadric* vertex_quadrics = allocator.allocate<Quadric>(vertex_count);
memset(vertex_quadrics, 0, vertex_count * sizeof(Quadric));
+ Quadric* vertex_no_attrib_quadrics = allocator.allocate<Quadric>(vertex_count);
+ memset(vertex_no_attrib_quadrics, 0, vertex_count * sizeof(Quadric));
- fillFaceQuadrics(vertex_quadrics, indices, index_count, vertex_positions, remap);
- fillEdgeQuadrics(vertex_quadrics, indices, index_count, vertex_positions, remap, vertex_kind, loop, loopback);
+ fillFaceQuadrics(vertex_quadrics, vertex_no_attrib_quadrics, indices, index_count, vertex_positions, remap);
+ fillEdgeQuadrics(vertex_quadrics, vertex_no_attrib_quadrics, indices, index_count, vertex_positions, remap, vertex_kind, loop, loopback);
if (result != indices)
memcpy(result, indices, index_count * sizeof(unsigned int));
@@ -1488,7 +1530,7 @@ size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned
if (edge_collapse_count == 0)
break;
- rankEdgeCollapses(edge_collapses, edge_collapse_count, vertex_positions, vertex_quadrics, remap);
+ rankEdgeCollapses(edge_collapses, edge_collapse_count, vertex_positions, vertex_quadrics, vertex_no_attrib_quadrics, remap);
#if TRACE > 1
dumpEdgeCollapses(edge_collapses, edge_collapse_count, vertex_kind);
@@ -1507,7 +1549,7 @@ size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned
printf("pass %d: ", int(pass_count++));
#endif
- size_t collapses = performEdgeCollapses(collapse_remap, collapse_locked, vertex_quadrics, edge_collapses, edge_collapse_count, collapse_order, remap, wedge, vertex_kind, vertex_positions, adjacency, triangle_collapse_goal, error_limit, result_error);
+ size_t collapses = performEdgeCollapses(collapse_remap, collapse_locked, vertex_quadrics, vertex_no_attrib_quadrics, edge_collapses, edge_collapse_count, collapse_order, remap, wedge, vertex_kind, vertex_positions, adjacency, triangle_collapse_goal, error_limit, result_error);
// no edges can be collapsed any more due to hitting the error limit or triangle collapse limit
if (collapses == 0)