41 #define VHACD_VERSION_MAJOR 4
42 #define VHACD_VERSION_MINOR 1
125 #include <functional>
168 template <
typename T>
180 const T&
GetX()
const;
181 const T&
GetY()
const;
182 const T&
GetZ()
const;
252 template <
typename U>
269 BoundsAABB(
const std::vector<VHACD::Vertex>& points);
336 virtual void Update(
const double overallProgress,
337 const double stageProgress,
338 const char*
const stage,
339 const char* operation) = 0;
355 virtual void Log(
const char*
const msg) = 0;
367 virtual void*
StartTask(std::function<
void()> func) = 0;
368 virtual void JoinTask(
void* Task) = 0;
416 virtual void Cancel() = 0;
428 virtual bool Compute(
const float*
const points,
429 const uint32_t countPoints,
430 const uint32_t*
const triangles,
431 const uint32_t countTriangles,
432 const Parameters& params) = 0;
444 virtual bool Compute(
const double*
const points,
445 const uint32_t countPoints,
446 const uint32_t*
const triangles,
447 const uint32_t countTriangles,
448 const Parameters& params) = 0;
464 virtual bool GetConvexHull(
const uint32_t index, ConvexHull& ch)
const = 0;
469 virtual void Clean() = 0;
505 template <
typename T>
506 T
clamp(
const T& v,
const T& lo,
const T& hi)
522 template <
typename T>
528 template <
typename T>
534 template <
typename T>
540 template <
typename T>
546 template <
typename T>
552 template <
typename T>
558 template <
typename T>
564 template <
typename T>
573 template <
typename T>
582 template <
typename T>
592 template <
typename T>
595 return std::sqrt(GetNormSquared());
598 template <
typename T>
601 return this->Dot(*
this);
604 template <
typename T>
607 auto it = std::max_element(m_data.begin(), m_data.end());
608 return int(std::distance(m_data.begin(), it));
614 template <
typename T>
623 template <
typename T>
626 GetX() += rhs.
GetX();
627 GetY() += rhs.
GetY();
628 GetZ() += rhs.
GetZ();
632 template <
typename T>
635 GetX() -= rhs.
GetX();
636 GetY() -= rhs.
GetY();
637 GetZ() -= rhs.
GetZ();
641 template <
typename T>
647 template <
typename T>
651 GetZ() * rhs.
GetX() - GetX() * rhs.
GetZ(),
652 GetX() * rhs.
GetY() - GetY() * rhs.
GetX());
655 template <
typename T>
658 return GetX() * rhs.
GetX() + GetY() * rhs.
GetY() + GetZ() * rhs.
GetZ();
661 template <
typename T>
667 template <
typename T>
673 template <
typename T>
682 template <
typename T>
691 template <
typename T>
700 template <
typename T>
709 template <
typename T>
718 template <
typename T>
721 return Vector3<T>(GetX() * rhs, GetY() * rhs, GetZ() * rhs);
724 template <
typename T>
727 return Vector3<T>(GetX() / rhs, GetY() / rhs, GetZ() / rhs);
733 template <
typename T>
742 template <
typename T>
745 if (GetX() == rhs.
GetX())
747 if (GetY() == rhs.
GetY())
749 return (GetZ() < rhs.
GetZ());
751 return (GetY() < rhs.
GetY());
753 return (GetX() < rhs.
GetX());
756 template <
typename T>
759 if (GetX() == rhs.
GetX())
761 if (GetY() == rhs.
GetY())
763 return (GetZ() > rhs.
GetZ());
765 return (GetY() > rhs.
GetY());
767 return (GetX() > rhs.
GetZ());
770 template <
typename T>
773 return GetX() >= rhs.
GetX() && GetY() >= rhs.
GetY() && GetZ() >= rhs.
GetZ();
776 template <
typename T>
779 return GetX() <= rhs.
GetX() && GetY() <= rhs.
GetY() && GetZ() <= rhs.
GetZ();
782 template <
typename T>
785 return Vector3<T>(std::min(GetX(), rhs.
GetX()), std::min(GetY(), rhs.
GetY()), std::min(GetZ(), rhs.
GetZ()));
788 template <
typename T>
791 return Vector3<T>(std::max(GetX(), rhs.
GetX()), std::max(GetY(), rhs.
GetY()), std::max(GetZ(), rhs.
GetZ()));
794 template <
typename T>
797 return *std::min_element(m_data.begin(), m_data.end());
800 template <
typename T>
803 return *std::max_element(m_data.begin(), m_data.end());
806 template <
typename T>
809 auto it = std::min_element(m_data.begin(), m_data.end());
810 idx = uint32_t(std::distance(m_data.begin(), it));
814 template <
typename T>
817 auto it = std::max_element(m_data.begin(), m_data.end());
818 idx = uint32_t(std::distance(m_data.begin(), it));
825 template <
typename T>
830 template <
typename T>
835 template <
typename T>
840 template <
typename T>
841 template <
typename U>
846 template <
typename T>
849 static_assert(std::is_same<T, double>::value,
"Vertex to Vector3 constructor only enabled for double");
852 template <
typename T>
855 static_assert(std::is_same<T, uint32_t>::value,
"Triangle to Vector3 constructor only enabled for uint32_t");
858 template <
typename T>
861 static_assert(std::is_same<T, double>::value,
"Vector3 to Vertex conversion only enable for double");
862 return ::VHACD::Vertex(GetX(), GetY(), GetZ());
870 #if ENABLE_VHACD_IMPLEMENTATION
881 #include <condition_variable>
890 #include <unordered_map>
891 #include <unordered_set>
896 #pragma warning(push)
897 #pragma warning(disable : 4100 4127 4189 4244 4456 4701 4702 4996)
901 #pragma GCC diagnostic push
917 Timer() : m_startTime(std::chrono::high_resolution_clock::
now()) {}
919 void Reset() { m_startTime = std::chrono::high_resolution_clock::now(); }
921 double GetElapsedSeconds()
923 auto s = PeekElapsedSeconds();
928 double PeekElapsedSeconds()
930 auto now = std::chrono::high_resolution_clock::now();
931 std::chrono::duration<double> diff =
now - m_startTime;
936 std::chrono::time_point<std::chrono::high_resolution_clock> m_startTime;
949 double dtime = m_timer.GetElapsedSeconds();
953 snprintf(scratch,
sizeof(scratch),
"%s took %0.5f seconds", m_action, dtime);
954 m_logger->Log(scratch);
958 const char* m_action{
nullptr };
964 for (uint32_t i = 1; i < points.size(); ++i)
967 m_min = m_min.CWiseMin(p);
968 m_max = m_max.CWiseMax(p);
974 BoundsAABB BoundsAABB::Union(
const BoundsAABB& b)
976 return BoundsAABB(GetMin().CWiseMin(b.GetMin()), GetMax().CWiseMax(b.GetMax()));
1004 double inflate = (GetMin() - GetMax()).GetNorm() * double(0.5) * ratio;
1005 return BoundsAABB(GetMin() - inflate, GetMax() + inflate);
1028 template <
class T,
class dCompareKey>
1029 void Sort(T*
const array,
int elements)
1031 const int batchSize = 8;
1035 stack[0][1] = elements - 1;
1037 const dCompareKey comparator;
1041 int lo = stack[stackIndex][0];
1042 int hi = stack[stackIndex][1];
1043 if ((hi - lo) > batchSize)
1045 int mid = (
lo +
hi) >> 1;
1046 if (comparator.Compare(array[lo], array[mid]) > 0)
1048 std::swap(array[lo], array[mid]);
1050 if (comparator.Compare(array[mid], array[hi]) > 0)
1052 std::swap(array[mid], array[hi]);
1054 if (comparator.Compare(array[lo], array[mid]) > 0)
1056 std::swap(array[lo], array[mid]);
1060 const T pivot(array[mid]);
1063 while (comparator.Compare(array[i], pivot) < 0)
1067 while (comparator.Compare(array[j], pivot) > 0)
1074 std::swap(array[i], array[j]);
1082 stack[stackIndex][0] = i;
1083 stack[stackIndex][1] =
hi;
1088 stack[stackIndex][0] =
lo;
1089 stack[stackIndex][1] = j;
1092 assert(stackIndex <
int(
sizeof(stack) / (2 *
sizeof(stack[0][0]))));
1096 int stride = batchSize + 1;
1097 if (elements < stride)
1101 for (
int i = 1; i < stride; ++i)
1103 if (comparator.Compare(array[0], array[i]) > 0)
1105 std::swap(array[0], array[i]);
1109 for (
int i = 1; i < elements; ++i)
1112 const T tmp(array[i]);
1113 for (; comparator.Compare(array[j - 1], tmp) > 0; --j)
1116 array[j] = array[j - 1];
1148 double base = (p2 - p1).GetNorm();
1154 if (base ==
double(0.0))
1156 height = double(0.0);
1160 double dot = (p3 - p1).Dot(p2 - p1);
1161 double alpha = dot / (base * base);
1167 return double(0.5) * base * height;
1170 bool ComputeCentroid(
const std::vector<VHACD::Vertex>& points,
1171 const std::vector<VHACD::Triangle>& indices,
1181 double denominator = 0;
1183 for (uint32_t i = 0; i < indices.size(); i++)
1185 uint32_t i1 = indices[i].mI0;
1186 uint32_t i2 = indices[i].mI1;
1187 uint32_t i3 = indices[i].mI2;
1197 double area = ComputeArea(p1, p2, p3);
1199 numerator += (sum * area);
1201 denominator += area;
1203 double recip = 1 / denominator;
1204 center = numerator * recip;
1210 double Determinant3x3(
const std::array<VHACD::Vect3, 3>& matrix,
double& error)
1212 double det = double(0.0);
1213 error = double(0.0);
1215 double a01xa12 = matrix[0].GetY() * matrix[1].GetZ();
1216 double a02xa11 = matrix[0].GetZ() * matrix[1].GetY();
1217 error += (std::abs(a01xa12) + std::abs(a02xa11)) * std::abs(matrix[2].GetX());
1218 det += (a01xa12 - a02xa11) * matrix[2].GetX();
1220 double a00xa12 = matrix[0].GetX() * matrix[1].GetZ();
1221 double a02xa10 = matrix[0].GetZ() * matrix[1].GetX();
1222 error += (std::abs(a00xa12) + std::abs(a02xa10)) * std::abs(matrix[2].GetY());
1223 det -= (a00xa12 - a02xa10) * matrix[2].GetY();
1225 double a00xa11 = matrix[0].GetX() * matrix[1].GetY();
1226 double a01xa10 = matrix[0].GetY() * matrix[1].GetX();
1227 error += (std::abs(a00xa11) + std::abs(a01xa10)) * std::abs(matrix[2].GetZ());
1228 det += (a00xa11 - a01xa10) * matrix[2].GetZ();
1233 double ComputeMeshVolume(
const std::vector<VHACD::Vertex>& vertices,
const std::vector<VHACD::Triangle>& indices)
1236 for (uint32_t i = 0; i < indices.size(); i++)
1238 const std::array<VHACD::Vect3, 3> m = { vertices[indices[i].mI0],
1239 vertices[indices[i].mI1],
1240 vertices[indices[i].mI2] };
1242 volume += Determinant3x3(m, placeholder);
1245 volume *= (double(1.0) / double(6.0));
1258 template <
typename T, std::
size_t MaxBundleSize = 1024>
1263 bool IsFull()
const;
1267 std::size_t m_index;
1268 std::array<T, MaxBundleSize> m_nodes;
1271 std::list<NodeStorage> m_list;
1272 typename std::list<NodeStorage>::iterator m_head{ m_list.end() };
1282 template <
typename T, std::
size_t MaxBundleSize>
1283 bool NodeBundle<T, MaxBundleSize>::NodeStorage::IsFull()
const
1285 return m_index == MaxBundleSize;
1288 template <
typename T, std::
size_t MaxBundleSize>
1289 T& NodeBundle<T, MaxBundleSize>::NodeStorage::GetNextNode()
1291 assert(m_index < MaxBundleSize);
1292 T& ret = m_nodes[m_index];
1297 template <
typename T, std::
size_t MaxBundleSize>
1298 T& NodeBundle<T, MaxBundleSize>::GetNextNode()
1303 if (m_head == m_list.end() || m_head->IsFull())
1305 m_head = m_list.emplace(m_list.end());
1308 return m_head->GetNextNode();
1311 template <
typename T, std::
size_t MaxBundleSize>
1312 T& NodeBundle<T, MaxBundleSize>::GetFirstNode()
1314 assert(m_head != m_list.end());
1315 return m_list.front().m_nodes[0];
1318 template <
typename T, std::
size_t MaxBundleSize>
1319 void NodeBundle<T, MaxBundleSize>::Clear()
1327 inline int dExp2(
int x)
1330 for (exp = -1; x; x >>= 1)
1342 inline int dBitReversal(
int v,
int base)
1345 int power = dExp2(base) - 1;
1348 x += (v & 1) << power;
1357 #define VHACD_GOOGOL_SIZE 4
1360 Googol(
double value);
1362 operator double()
const;
1363 Googol
operator+(
const Googol& A)
const;
1364 Googol
operator-(
const Googol& A)
const;
1365 Googol
operator*(
const Googol& A)
const;
1366 Googol operator/(
const Googol& A)
const;
1368 Googol& operator+=(
const Googol& A);
1369 Googol& operator-=(
const Googol& A);
1371 bool operator>(
const Googol& A)
const;
1372 bool operator>=(
const Googol& A)
const;
1373 bool operator<(
const Googol& A)
const;
1374 bool operator<=(
const Googol& A)
const;
1379 Googol Floor()
const;
1380 Googol InvSqrt()
const;
1381 Googol Sqrt()
const;
1383 void ToString(
char*
const string)
const;
1386 void NegateMantissa(std::array<uint64_t, VHACD_GOOGOL_SIZE>& mantissa)
const;
1387 void CopySignedMantissa(std::array<uint64_t, VHACD_GOOGOL_SIZE>& mantissa)
const;
1388 int NormalizeMantissa(std::array<uint64_t, VHACD_GOOGOL_SIZE>& mantissa)
const;
1389 void ShiftRightMantissa(std::array<uint64_t, VHACD_GOOGOL_SIZE>& mantissa,
int bits)
const;
1390 uint64_t CheckCarrier(uint64_t a, uint64_t b)
const;
1392 int LeadingZeros(uint64_t a)
const;
1393 void ExtendedMultiply(uint64_t a, uint64_t b, uint64_t& high, uint64_t& low)
const;
1394 void ScaleMantissa(uint64_t* out, uint64_t scale)
const;
1397 int m_exponent{ 0 };
1398 std::array<uint64_t, VHACD_GOOGOL_SIZE> m_mantissa{ 0 };
1401 static Googol m_zero;
1402 static Googol m_one;
1403 static Googol m_two;
1404 static Googol m_three;
1405 static Googol m_half;
1408 Googol Googol::m_zero(
double(0.0));
1409 Googol Googol::m_one(
double(1.0));
1410 Googol Googol::m_two(
double(2.0));
1411 Googol Googol::m_three(
double(3.0));
1412 Googol Googol::m_half(
double(0.5));
1414 Googol::Googol(
double value)
1417 double mantissa = fabs(frexp(value, &exp));
1420 m_sign = (value >= 0) ? 0 : 1;
1422 m_mantissa[0] = uint64_t(
double(uint64_t(1) << 62) * mantissa);
1425 Googol::operator double()
const
1427 double mantissa = (double(1.0) / double(uint64_t(1) << 62)) *
double(m_mantissa[0]);
1428 mantissa = ldexp(mantissa, m_exponent) * (m_sign ? double(-1.0) : double(1.0));
1432 Googol Googol::operator+(
const Googol& A)
const
1435 if (m_mantissa[0] && A.m_mantissa[0])
1437 std::array<uint64_t, VHACD_GOOGOL_SIZE> mantissa0;
1438 std::array<uint64_t, VHACD_GOOGOL_SIZE> mantissa1;
1439 std::array<uint64_t, VHACD_GOOGOL_SIZE> mantissa;
1441 CopySignedMantissa(mantissa0);
1442 A.CopySignedMantissa(mantissa1);
1444 int exponentDiff = m_exponent - A.m_exponent;
1445 int exponent = m_exponent;
1446 if (exponentDiff > 0)
1448 ShiftRightMantissa(mantissa1, exponentDiff);
1450 else if (exponentDiff < 0)
1452 exponent = A.m_exponent;
1453 ShiftRightMantissa(mantissa0, -exponentDiff);
1456 uint64_t carrier = 0;
1457 for (
int i = VHACD_GOOGOL_SIZE - 1; i >= 0; i--)
1459 uint64_t m0 = mantissa0[i];
1460 uint64_t m1 = mantissa1[i];
1461 mantissa[i] = m0 + m1 + carrier;
1462 carrier = CheckCarrier(m0, m1) | CheckCarrier(m0 + m1, carrier);
1466 if (int64_t(mantissa[0]) < 0)
1469 NegateMantissa(mantissa);
1472 int bits = NormalizeMantissa(mantissa);
1473 if (bits <= (-64 * VHACD_GOOGOL_SIZE))
1481 tmp.m_exponent = int(exponent + bits);
1484 tmp.m_mantissa = mantissa;
1486 else if (A.m_mantissa[0])
1498 Googol Googol::operator-(
const Googol& A)
const
1501 tmp.m_sign = !tmp.m_sign;
1507 if (m_mantissa[0] && A.m_mantissa[0])
1509 std::array<uint64_t, VHACD_GOOGOL_SIZE * 2> mantissaAcc{ 0 };
1510 for (
int i = VHACD_GOOGOL_SIZE - 1; i >= 0; i--)
1512 uint64_t a = m_mantissa[i];
1515 uint64_t mantissaScale[2 * VHACD_GOOGOL_SIZE] = { 0 };
1516 A.ScaleMantissa(&mantissaScale[i], a);
1518 uint64_t carrier = 0;
1519 for (
int j = 0; j < 2 * VHACD_GOOGOL_SIZE; j++)
1521 const int k = 2 * VHACD_GOOGOL_SIZE - 1 - j;
1522 uint64_t m0 = mantissaAcc[k];
1523 uint64_t m1 = mantissaScale[k];
1524 mantissaAcc[k] = m0 + m1 + carrier;
1525 carrier = CheckCarrier(m0, m1) | CheckCarrier(m0 + m1, carrier);
1530 uint64_t carrier = 0;
1531 int bits = LeadingZeros(mantissaAcc[0]) - 2;
1532 for (
int i = 0; i < 2 * VHACD_GOOGOL_SIZE; i++)
1534 const int k = 2 * VHACD_GOOGOL_SIZE - 1 - i;
1535 uint64_t a = mantissaAcc[k];
1536 mantissaAcc[k] = (a << uint64_t(bits)) | carrier;
1537 carrier = a >> uint64_t(64 - bits);
1540 int exp = m_exponent + A.m_exponent - (bits - 2);
1543 tmp.m_sign = m_sign ^ A.m_sign;
1544 tmp.m_exponent = exp;
1545 for (std::size_t i = 0; i < tmp.m_mantissa.size(); ++i)
1547 tmp.m_mantissa[i] = mantissaAcc[i];
1552 return Googol(
double(0.0));
1555 Googol Googol::operator/(
const Googol& A)
const
1557 Googol tmp(
double(1.0) / A);
1558 tmp = tmp * (m_two - A * tmp);
1559 tmp = tmp * (m_two - A * tmp);
1566 tmp = tmp * (m_two - A * tmp);
1568 }
while (test && (passes < (2 * VHACD_GOOGOL_SIZE)));
1569 return (*
this) * tmp;
1572 Googol& Googol::operator+=(
const Googol& A)
1578 Googol& Googol::operator-=(
const Googol& A)
1584 bool Googol::operator>(
const Googol& A)
const
1586 Googol tmp(*
this - A);
1587 return double(tmp) > double(0.0);
1590 bool Googol::operator>=(
const Googol& A)
const
1592 Googol tmp(*
this - A);
1593 return double(tmp) >= double(0.0);
1596 bool Googol::operator<(
const Googol& A)
const
1598 Googol tmp(*
this - A);
1599 return double(tmp) < double(0.0);
1602 bool Googol::operator<=(
const Googol& A)
const
1604 Googol tmp(*
this - A);
1605 return double(tmp) <= double(0.0);
1608 bool Googol::operator==(
const Googol& A)
const
1610 return m_sign == A.m_sign && m_exponent == A.m_exponent && m_mantissa == A.m_mantissa;
1613 bool Googol::operator!=(
const Googol& A)
const {
return !(*
this == A); }
1615 Googol Googol::Abs()
const
1622 Googol Googol::Floor()
const
1626 return Googol(
double(0.0));
1628 int bits = m_exponent + 2;
1637 for (
int i = VHACD_GOOGOL_SIZE - 1; i > start; i--)
1639 tmp.m_mantissa[i] = 0;
1643 uint64_t mask(~0ULL);
1644 mask <<= (64 - bits);
1645 tmp.m_mantissa[start] &= mask;
1649 Googol Googol::InvSqrt()
const
1651 const Googol& me = *
this;
1652 Googol x(
double(1.0) / sqrt(me));
1660 x = m_half * x * (m_three - me * x * x);
1662 }
while (test && (passes < (2 * VHACD_GOOGOL_SIZE)));
1666 Googol Googol::Sqrt()
const {
return *
this * InvSqrt(); }
1668 void Googol::ToString(
char*
const string)
const
1671 Googol base(
double(10.0));
1672 while (
double(tmp) >
double(1.0))
1678 while (tmp.m_mantissa[0])
1681 Googol digit(tmp.Floor());
1684 string[index] = char(val) +
'0';
1690 void Googol::NegateMantissa(std::array<uint64_t, VHACD_GOOGOL_SIZE>& mantissa)
const
1692 uint64_t carrier = 1;
1693 for (
size_t i = mantissa.size() - 1; i < mantissa.size(); i--)
1695 uint64_t a = ~mantissa[i] + carrier;
1704 void Googol::CopySignedMantissa(std::array<uint64_t, VHACD_GOOGOL_SIZE>& mantissa)
const
1706 mantissa = m_mantissa;
1709 NegateMantissa(mantissa);
1713 int Googol::NormalizeMantissa(std::array<uint64_t, VHACD_GOOGOL_SIZE>& mantissa)
const
1716 if (int64_t(mantissa[0] * 2) < 0)
1719 ShiftRightMantissa(mantissa, 1);
1723 while (!mantissa[0] && bits > (-64 * VHACD_GOOGOL_SIZE))
1726 for (
int i = 1; i < VHACD_GOOGOL_SIZE; i++)
1728 mantissa[i - 1] = mantissa[i];
1730 mantissa[VHACD_GOOGOL_SIZE - 1] = 0;
1733 if (bits > (-64 * VHACD_GOOGOL_SIZE))
1735 int n = LeadingZeros(mantissa[0]) - 2;
1738 uint64_t carrier = 0;
1739 for (
int i = VHACD_GOOGOL_SIZE - 1; i >= 0; i--)
1741 uint64_t a = mantissa[i];
1742 mantissa[i] = (a << n) | carrier;
1743 carrier = a >> (64 - n);
1750 uint64_t carrier = 0;
1752 for (
int i = 0; i < VHACD_GOOGOL_SIZE; i++)
1754 uint64_t a = mantissa[i];
1755 mantissa[i] = (a >> shift) | carrier;
1756 carrier = a << (64 - shift);
1765 void Googol::ShiftRightMantissa(std::array<uint64_t, VHACD_GOOGOL_SIZE>& mantissa,
int bits)
const
1767 uint64_t carrier = 0;
1768 if (int64_t(mantissa[0]) < int64_t(0))
1770 carrier = uint64_t(-1);
1775 for (
int i = VHACD_GOOGOL_SIZE - 2; i >= 0; i--)
1777 mantissa[i + 1] = mantissa[i];
1779 mantissa[0] = carrier;
1785 carrier <<= (64 - bits);
1786 for (
int i = 0; i < VHACD_GOOGOL_SIZE; i++)
1788 uint64_t a = mantissa[i];
1789 mantissa[i] = (a >> bits) | carrier;
1790 carrier = a << (64 - bits);
1795 uint64_t Googol::CheckCarrier(uint64_t a, uint64_t b)
const {
return ((uint64_t(-1) - b) < a) ? uint64_t(1) : 0; }
1797 int Googol::LeadingZeros(uint64_t a)
const
1799 #define VHACD_COUNTBIT(mask, add) \
1802 uint64_t test = a & mask; \
1803 n += test ? 0 : add; \
1804 a = test ? test : (a & ~mask); \
1808 VHACD_COUNTBIT(0xffffffff00000000LL, 32);
1809 VHACD_COUNTBIT(0xffff0000ffff0000LL, 16);
1810 VHACD_COUNTBIT(0xff00ff00ff00ff00LL, 8);
1811 VHACD_COUNTBIT(0xf0f0f0f0f0f0f0f0LL, 4);
1812 VHACD_COUNTBIT(0xccccccccccccccccLL, 2);
1813 VHACD_COUNTBIT(0xaaaaaaaaaaaaaaaaLL, 1);
1818 void Googol::ExtendedMultiply(uint64_t a, uint64_t b, uint64_t& high, uint64_t& low)
const
1820 uint64_t bLow = b & 0xffffffff;
1821 uint64_t bHigh = b >> 32;
1822 uint64_t aLow = a & 0xffffffff;
1823 uint64_t aHigh = a >> 32;
1825 uint64_t l = bLow * aLow;
1827 uint64_t c1 = bHigh * aLow;
1828 uint64_t c2 = bLow * aHigh;
1829 uint64_t m = c1 + c2;
1830 uint64_t carrier = CheckCarrier(c1, c2) << 32;
1832 uint64_t h = bHigh * aHigh + carrier;
1834 uint64_t ml = m << 32;
1835 uint64_t ll = l + ml;
1836 uint64_t mh = (m >> 32) + CheckCarrier(l, ml);
1837 uint64_t hh = h + mh;
1843 void Googol::ScaleMantissa(uint64_t* dst, uint64_t scale)
const
1845 uint64_t carrier = 0;
1846 for (
int i = VHACD_GOOGOL_SIZE - 1; i >= 0; i--)
1852 ExtendedMultiply(scale, m_mantissa[i], high, low);
1853 uint64_t acc = low + carrier;
1854 carrier = CheckCarrier(low, carrier);
1860 dst[i + 1] = carrier;
1869 Googol det = double(0.0);
1871 Googol a01xa12 = matrix[0].GetY() * matrix[1].GetZ();
1872 Googol a02xa11 = matrix[0].GetZ() * matrix[1].GetY();
1873 det += (a01xa12 - a02xa11) * matrix[2].GetX();
1875 Googol a00xa12 = matrix[0].GetX() * matrix[1].GetZ();
1876 Googol a02xa10 = matrix[0].GetZ() * matrix[1].GetX();
1877 det -= (a00xa12 - a02xa10) * matrix[2].GetY();
1879 Googol a00xa11 = matrix[0].GetX() * matrix[1].GetY();
1880 Googol a01xa10 = matrix[0].GetY() * matrix[1].GetX();
1881 det += (a00xa11 - a01xa10) * matrix[2].GetZ();
1888 HullPlane(
const HullPlane&) =
default;
1889 HullPlane(
double x,
double y,
double z,
double w);
1895 HullPlane Scale(
double s)
const;
1897 HullPlane& operator=(
const HullPlane& rhs);
1902 const double& GetW()
const;
1908 HullPlane::HullPlane(
double x,
double y,
double z,
double w) :
VHACD::
Vect3(x, y, z), m_w(w) {}
1913 :
VHACD::
Vect3((p1 - p0).Cross(p2 - p0)), m_w(-Dot(p0))
1917 HullPlane HullPlane::Scale(
double s)
const {
return HullPlane(*
this * s, m_w * s); }
1919 HullPlane& HullPlane::operator=(
const HullPlane& rhs)
1921 GetX() = rhs.GetX();
1922 GetY() = rhs.GetY();
1923 GetZ() = rhs.GetZ();
1930 double& HullPlane::GetW() {
return m_w; }
1932 const double& HullPlane::GetW()
const {
return m_w; }
1934 class ConvexHullFace
1937 ConvexHullFace() =
default;
1938 double Evalue(
const std::vector<VHACD::Vect3>& pointArray,
const VHACD::Vect3&
point)
const;
1939 HullPlane GetPlaneEquation(
const std::vector<VHACD::Vect3>& pointArray,
bool& isValid)
const;
1941 std::array<int, 3> m_index;
1945 std::array<std::list<ConvexHullFace>::iterator, 3> m_twin;
1947 friend class ConvexHull;
1950 double ConvexHullFace::Evalue(
const std::vector<VHACD::Vect3>& pointArray,
const VHACD::Vect3&
point)
const
1956 std::array<VHACD::Vect3, 3> matrix = { p2 - p0, p1 - p0,
point - p0 };
1958 double det = Determinant3x3(matrix, error);
1966 double precision = double(1.0) / double(1 << 24);
1967 double errbound = error * precision;
1968 if (fabs(det) > errbound)
1977 std::array<VHACD::Vector3<Googol>, 3> exactMatrix = { p2g - p0g, p1g - p0g, pointg - p0g };
1978 return Determinant3x3(exactMatrix);
1981 HullPlane ConvexHullFace::GetPlaneEquation(
const std::vector<VHACD::Vect3>& pointArray,
bool& isvalid)
const
1986 HullPlane plane(p0, p1, p2);
1989 double mag2 = plane.Dot(plane);
1990 if (mag2 >
double(1.0e-16))
1993 plane = plane.Scale(
double(1.0) / sqrt(mag2));
2001 ConvexHullVertex() =
default;
2002 ConvexHullVertex(
const ConvexHullVertex&) =
default;
2003 ConvexHullVertex&
operator=(
const ConvexHullVertex& rhs) =
default;
2004 using VHACD::Vect3::operator=;
2009 class ConvexHullAABBTreeNode
2011 #define VHACD_CONVEXHULL_3D_VERTEX_CLUSTER_SIZE 8
2013 ConvexHullAABBTreeNode() =
default;
2014 ConvexHullAABBTreeNode(ConvexHullAABBTreeNode* parent);
2017 ConvexHullAABBTreeNode* m_left{
nullptr };
2018 ConvexHullAABBTreeNode* m_right{
nullptr };
2019 ConvexHullAABBTreeNode* m_parent{
nullptr };
2022 std::array<size_t, VHACD_CONVEXHULL_3D_VERTEX_CLUSTER_SIZE> m_indices;
2025 ConvexHullAABBTreeNode::ConvexHullAABBTreeNode(ConvexHullAABBTreeNode* parent) : m_parent(parent) {}
2032 ConvexHull(
const ConvexHull& source);
2033 ConvexHull(
const std::vector<::VHACD::Vertex>& vertexCloud,
double distTol,
int maxVertexCount = 0x7fffffff);
2034 ~ConvexHull() =
default;
2036 const std::vector<VHACD::Vect3>& GetVertexPool()
const;
2038 const std::list<ConvexHullFace>& GetList()
const {
return m_list; }
2041 void BuildHull(
const std::vector<::VHACD::Vertex>& vertexCloud,
double distTol,
int maxVertexCount);
2043 void GetUniquePoints(std::vector<ConvexHullVertex>& points);
2044 int InitVertexArray(std::vector<ConvexHullVertex>& points, NodeBundle<ConvexHullAABBTreeNode>& memoryPool);
2046 ConvexHullAABBTreeNode* BuildTreeNew(std::vector<ConvexHullVertex>& points,
2047 std::vector<ConvexHullAABBTreeNode>& memoryPool)
const;
2048 ConvexHullAABBTreeNode* BuildTreeOld(std::vector<ConvexHullVertex>& points,
2049 NodeBundle<ConvexHullAABBTreeNode>& memoryPool);
2050 ConvexHullAABBTreeNode* BuildTreeRecurse(ConvexHullAABBTreeNode*
const parent,
2051 ConvexHullVertex*
const points,
2054 NodeBundle<ConvexHullAABBTreeNode>& memoryPool)
const;
2056 std::list<ConvexHullFace>::iterator AddFace(
int i0,
int i1,
int i2);
2058 void CalculateConvexHull3D(ConvexHullAABBTreeNode* vertexTree,
2059 std::vector<ConvexHullVertex>& points,
2062 int maxVertexCount);
2064 int SupportVertex(ConvexHullAABBTreeNode**
const tree,
2065 const std::vector<ConvexHullVertex>& points,
2067 const bool removeEntry =
true)
const;
2073 std::list<ConvexHullFace> m_list;
2076 double m_diag{ 0.0 };
2077 std::vector<VHACD::Vect3> m_points;
2080 class ConvexHull::ndNormalMap
2085 static const ndNormalMap& GetNormalMap();
2090 std::array<VHACD::Vect3, 128> m_normal;
2094 const ConvexHull::ndNormalMap& ConvexHull::ndNormalMap::GetNormalMap()
2096 static ndNormalMap normalMap;
2100 void ConvexHull::ndNormalMap::TessellateTriangle(
int level,
2108 assert(fabs(p0.
Dot(p0) -
double(1.0)) <
double(1.0e-4));
2109 assert(fabs(p1.
Dot(p1) -
double(1.0)) <
double(1.0e-4));
2110 assert(fabs(p2.
Dot(p2) -
double(1.0)) <
double(1.0e-4));
2115 p01 = p01 * (double(1.0) / p01.GetNorm());
2116 p12 = p12 * (double(1.0) / p12.GetNorm());
2117 p20 = p20 * (double(1.0) / p20.GetNorm());
2119 assert(fabs(p01.GetNormSquared() -
double(1.0)) <
double(1.0e-4));
2120 assert(fabs(p12.GetNormSquared() -
double(1.0)) <
double(1.0e-4));
2121 assert(fabs(p20.GetNormSquared() -
double(1.0)) <
double(1.0e-4));
2123 TessellateTriangle(level - 1, p0, p01, p20, count);
2124 TessellateTriangle(level - 1, p1, p12, p01, count);
2125 TessellateTriangle(level - 1, p2, p20, p12, count);
2126 TessellateTriangle(level - 1, p01, p12, p20, count);
2134 HullPlane n(p0, p1, p2);
2135 n = n.Scale(
double(1.0) / n.GetNorm());
2136 n.GetW() = double(0.0);
2137 int index = dBitReversal(count,
int(m_normal.size()));
2138 m_normal[index] = n;
2140 assert(count <=
int(m_normal.size()));
2144 ConvexHull::ndNormalMap::ndNormalMap()
2146 VHACD::Vect3 p0(
double(1.0),
double(0.0),
double(0.0));
2147 VHACD::Vect3 p1(
double(-1.0),
double(0.0),
double(0.0));
2148 VHACD::Vect3 p2(
double(0.0),
double(1.0),
double(0.0));
2149 VHACD::Vect3 p3(
double(0.0),
double(-1.0),
double(0.0));
2150 VHACD::Vect3 p4(
double(0.0),
double(0.0),
double(1.0));
2151 VHACD::Vect3 p5(
double(0.0),
double(0.0),
double(-1.0));
2154 int subdivisions = 2;
2155 TessellateTriangle(subdivisions, p4, p0, p2, count);
2156 TessellateTriangle(subdivisions, p0, p5, p2, count);
2157 TessellateTriangle(subdivisions, p5, p1, p2, count);
2158 TessellateTriangle(subdivisions, p1, p4, p2, count);
2159 TessellateTriangle(subdivisions, p0, p4, p3, count);
2160 TessellateTriangle(subdivisions, p5, p0, p3, count);
2161 TessellateTriangle(subdivisions, p1, p5, p3, count);
2162 TessellateTriangle(subdivisions, p4, p1, p3, count);
2165 ConvexHull::ConvexHull(
const std::vector<::VHACD::Vertex>& vertexCloud,
double distTol,
int maxVertexCount)
2167 if (vertexCloud.size() >= 4)
2169 BuildHull(vertexCloud, distTol, maxVertexCount);
2173 const std::vector<VHACD::Vect3>& ConvexHull::GetVertexPool()
const {
return m_points; }
2175 void ConvexHull::BuildHull(
const std::vector<::VHACD::Vertex>& vertexCloud,
double distTol,
int maxVertexCount)
2177 size_t treeCount = vertexCloud.size() / (VHACD_CONVEXHULL_3D_VERTEX_CLUSTER_SIZE >> 1);
2178 treeCount = std::max(treeCount,
size_t(4)) * 2;
2180 std::vector<ConvexHullVertex> points(vertexCloud.size());
2193 NodeBundle<ConvexHullAABBTreeNode> treePool;
2194 for (
size_t i = 0; i < vertexCloud.size(); ++i)
2198 int count = InitVertexArray(points, treePool);
2200 if (m_points.size() >= 4)
2202 CalculateConvexHull3D(&treePool.GetFirstNode(), points, count, distTol, maxVertexCount);
2206 void ConvexHull::GetUniquePoints(std::vector<ConvexHullVertex>& points)
2211 int Compare(
const ConvexHullVertex& elementA,
const ConvexHullVertex& elementB)
const
2213 for (
int i = 0; i < 3; i++)
2215 if (elementA[i] < elementB[i])
2219 else if (elementA[i] > elementB[i])
2228 int count = int(points.size());
2229 Sort<ConvexHullVertex, CompareVertex>(points.data(), count);
2232 CompareVertex compareVertex;
2233 for (
int i = 1; i < count; ++i)
2235 for (; i < count; ++i)
2237 if (compareVertex.Compare(points[indexCount], points[i]))
2240 points[indexCount] = points[i];
2245 points.resize(indexCount + 1);
2248 ConvexHullAABBTreeNode* ConvexHull::BuildTreeRecurse(ConvexHullAABBTreeNode*
const parent,
2249 ConvexHullVertex*
const points,
2252 NodeBundle<ConvexHullAABBTreeNode>& memoryPool)
const
2254 ConvexHullAABBTreeNode* tree =
nullptr;
2259 if (count <= VHACD_CONVEXHULL_3D_VERTEX_CLUSTER_SIZE)
2261 ConvexHullAABBTreeNode& clump = memoryPool.GetNextNode();
2263 clump.m_count = count;
2264 for (
int i = 0; i < count; ++i)
2266 clump.m_indices[i] = i + baseIndex;
2269 minP = minP.CWiseMin(p);
2270 maxP = maxP.CWiseMax(p);
2273 clump.m_left =
nullptr;
2274 clump.m_right =
nullptr;
2281 for (
int i = 0; i < count; ++i)
2284 minP = minP.CWiseMin(p);
2285 maxP = maxP.CWiseMax(p);
2290 varian = varian * double(count) - median.
CWiseMul(median);
2292 double maxVarian = double(-1.0e10);
2293 for (
int i = 0; i < 3; ++i)
2295 if (varian[i] > maxVarian)
2298 maxVarian = varian[i];
2301 VHACD::Vect3 center(median * (
double(1.0) /
double(count)));
2303 double test = center[index];
2309 for (; i0 <= i1; i0++)
2311 double val = points[i0][index];
2318 for (; i1 >= i0; i1--)
2320 double val = points[i1][index];
2329 std::swap(points[i0], points[i1]);
2339 if (i0 >= (count - 1))
2344 tree = &memoryPool.GetNextNode();
2349 tree->m_left = BuildTreeRecurse(tree, points, i0, baseIndex, memoryPool);
2350 tree->m_right = BuildTreeRecurse(tree, &points[i0], count - i0, i0 + baseIndex, memoryPool);
2354 tree->m_parent = parent;
2364 ConvexHullAABBTreeNode* ConvexHull::BuildTreeOld(std::vector<ConvexHullVertex>& points,
2365 NodeBundle<ConvexHullAABBTreeNode>& memoryPool)
2367 GetUniquePoints(points);
2368 int count = int(points.size());
2373 return BuildTreeRecurse(
nullptr, points.data(), count, 0, memoryPool);
2376 ConvexHullAABBTreeNode* ConvexHull::BuildTreeNew(std::vector<ConvexHullVertex>& points,
2377 std::vector<ConvexHullAABBTreeNode>& memoryPool)
const
2388 dCluster firstCluster;
2389 firstCluster.m_count = int(points.size());
2391 for (
int i = 0; i < firstCluster.m_count; ++i)
2394 firstCluster.m_sum += p;
2395 firstCluster.m_sum2 += p.
CWiseMul(p);
2399 const int clusterSize = 16;
2401 if (firstCluster.m_count > clusterSize)
2403 dCluster spliteStack[128];
2404 spliteStack[0] = firstCluster;
2410 dCluster cluster(spliteStack[stack]);
2412 const VHACD::Vect3 origin(cluster.m_sum * (
double(1.0) / cluster.m_count));
2413 const VHACD::Vect3 variance2(cluster.m_sum2 * (
double(1.0) / cluster.m_count) - origin.CWiseMul(origin));
2414 double maxVariance2 = variance2.MaxCoeff();
2416 if ((cluster.m_count <= clusterSize) || (stack > (
sizeof(spliteStack) /
sizeof(spliteStack[0]) - 4)) ||
2417 (maxVariance2 < 1.e-4f))
2445 int count = cluster.m_count;
2446 for (
int i = cluster.m_count - 1; i > 0; --i)
2448 for (
int j = i - 1; j >= 0; --j)
2450 VHACD::Vect3 error(points[cluster.m_start + j] - points[cluster.m_start + i]);
2451 double mag2 = error.Dot(error);
2452 if (mag2 <
double(1.0e-6))
2454 points[cluster.m_start + j] = points[cluster.m_start + i];
2461 assert(baseCount <= cluster.m_start);
2462 for (
int i = 0; i < count; ++i)
2464 points[baseCount] = points[cluster.m_start + i];
2470 const int firstSortAxis = variance2.LongestAxis();
2471 double axisVal = origin[firstSortAxis];
2474 int i1 = cluster.m_count - 1;
2476 const int start = cluster.m_start;
2479 while ((points[start + i0][firstSortAxis] <= axisVal) && (i0 < i1))
2484 while ((points[start + i1][firstSortAxis] > axisVal) && (i0 < i1))
2492 std::swap(points[start + i0], points[start + i1]);
2498 while ((points[start + i0][firstSortAxis] <= axisVal) && (i0 < cluster.m_count))
2504 for (
int i = 0; i < i0; ++i)
2506 assert(points[start + i][firstSortAxis] <= axisVal);
2509 for (
int i = i0; i < cluster.m_count; ++i)
2511 assert(points[start + i][firstSortAxis] > axisVal);
2517 for (
int i = 0; i < i0; ++i)
2524 dCluster cluster_i1(cluster);
2525 cluster_i1.m_start = start + i0;
2526 cluster_i1.m_count = cluster.m_count - i0;
2527 cluster_i1.m_sum -= xc;
2528 cluster_i1.m_sum2 -= x2c;
2529 spliteStack[stack] = cluster_i1;
2530 assert(cluster_i1.m_count > 0);
2533 dCluster cluster_i0(cluster);
2534 cluster_i0.m_start = start;
2535 cluster_i0.m_count = i0;
2536 cluster_i0.m_sum = xc;
2537 cluster_i0.m_sum2 = x2c;
2538 assert(cluster_i0.m_count > 0);
2539 spliteStack[stack] = cluster_i0;
2545 points.resize(baseCount);
2562 ConvexHullAABBTreeNode* m_parent;
2563 ConvexHullAABBTreeNode** m_child;
2568 for (
int i = 0; i < baseCount; ++i)
2577 dTreeBox treeBoxStack[128];
2578 treeBoxStack[0].m_start = 0;
2579 treeBoxStack[0].m_count = baseCount;
2580 treeBoxStack[0].m_sum = sum;
2581 treeBoxStack[0].m_sum2 = sum2;
2582 treeBoxStack[0].m_min = minP;
2583 treeBoxStack[0].m_max = maxP;
2584 treeBoxStack[0].m_child =
nullptr;
2585 treeBoxStack[0].m_parent =
nullptr;
2588 ConvexHullAABBTreeNode* root =
nullptr;
2592 dTreeBox box(treeBoxStack[stack]);
2593 if (box.m_count <= VHACD_CONVEXHULL_3D_VERTEX_CLUSTER_SIZE)
2595 assert(memoryPool.size() != memoryPool.capacity() &&
"memoryPool is going to be reallocated, pointers will be "
2597 memoryPool.emplace_back();
2598 ConvexHullAABBTreeNode& clump = memoryPool.back();
2600 clump.m_count = box.m_count;
2601 for (
int i = 0; i < box.m_count; ++i)
2603 clump.m_indices[i] = i + box.m_start;
2605 clump.m_box[0] = box.m_min;
2606 clump.m_box[1] = box.m_max;
2610 *box.m_child = &clump;
2620 const VHACD::Vect3 origin(box.m_sum * (
double(1.0) / box.m_count));
2621 const VHACD::Vect3 variance2(box.m_sum2 * (
double(1.0) / box.m_count) - origin.CWiseMul(origin));
2623 int firstSortAxis = 0;
2624 if ((variance2.GetY() >= variance2.GetX()) && (variance2.GetY() >= variance2.GetZ()))
2628 else if ((variance2.GetZ() >= variance2.GetX()) && (variance2.GetZ() >= variance2.GetY()))
2632 double axisVal = origin[firstSortAxis];
2635 int i1 = box.m_count - 1;
2637 const int start = box.m_start;
2640 while ((points[start + i0][firstSortAxis] <= axisVal) && (i0 < i1))
2645 while ((points[start + i1][firstSortAxis] > axisVal) && (i0 < i1))
2653 std::swap(points[start + i0], points[start + i1]);
2659 while ((points[start + i0][firstSortAxis] <= axisVal) && (i0 < box.m_count))
2665 for (
int i = 0; i < i0; ++i)
2667 assert(points[start + i][firstSortAxis] <= axisVal);
2670 for (
int i = i0; i < box.m_count; ++i)
2672 assert(points[start + i][firstSortAxis] > axisVal);
2676 assert(memoryPool.size() != memoryPool.capacity() &&
"memoryPool is going to be reallocated, pointers will be "
2678 memoryPool.emplace_back();
2679 ConvexHullAABBTreeNode& node = memoryPool.back();
2681 node.m_box[0] = box.m_min;
2682 node.m_box[1] = box.m_max;
2685 *box.m_child = &node;
2698 for (
int i = i0; i < box.m_count; ++i)
2707 dTreeBox cluster_i1(box);
2708 cluster_i1.m_start = start + i0;
2709 cluster_i1.m_count = box.m_count - i0;
2710 cluster_i1.m_sum = xc;
2711 cluster_i1.m_sum2 = x2c;
2712 cluster_i1.m_min = p0;
2713 cluster_i1.m_max = p1;
2714 cluster_i1.m_parent = &node;
2715 cluster_i1.m_child = &node.m_right;
2716 treeBoxStack[stack] = cluster_i1;
2717 assert(cluster_i1.m_count > 0);
2726 for (
int i = 0; i < i0; ++i)
2735 dTreeBox cluster_i0(box);
2736 cluster_i0.m_start = start;
2737 cluster_i0.m_count = i0;
2738 cluster_i0.m_min = p0;
2739 cluster_i0.m_max = p1;
2740 cluster_i0.m_sum = xc;
2741 cluster_i0.m_sum2 = x2c;
2742 cluster_i0.m_parent = &node;
2743 cluster_i0.m_child = &node.m_left;
2744 assert(cluster_i0.m_count > 0);
2745 treeBoxStack[stack] = cluster_i0;
2754 int ConvexHull::SupportVertex(ConvexHullAABBTreeNode**
const treePointer,
2755 const std::vector<ConvexHullVertex>& points,
2757 const bool removeEntry)
const
2759 #define VHACD_STACK_DEPTH_3D 64
2760 double aabbProjection[VHACD_STACK_DEPTH_3D];
2761 ConvexHullAABBTreeNode* stackPool[VHACD_STACK_DEPTH_3D];
2767 stackPool[0] = *treePointer;
2768 aabbProjection[0] = double(1.0e20);
2769 double maxProj = double(-1.0e20);
2770 int ix = (dir[0] > double(0.0)) ? 1 : 0;
2771 int iy = (dir[1] > double(0.0)) ? 1 : 0;
2772 int iz = (dir[2] > double(0.0)) ? 1 : 0;
2776 double boxSupportValue = aabbProjection[stack];
2777 if (boxSupportValue > maxProj)
2779 ConvexHullAABBTreeNode* me = stackPool[stack];
2784 if (me->m_left && me->m_right)
2787 me->m_left->m_box[ix].GetX(), me->m_left->m_box[iy].GetY(), me->m_left->m_box[iz].GetZ());
2788 double leftSupportDist = leftSupportPoint.Dot(dir);
2791 me->m_right->m_box[ix].GetX(), me->m_right->m_box[iy].GetY(), me->m_right->m_box[iz].GetZ());
2792 double rightSupportDist = rightSupportPoint.Dot(dir);
2798 if (rightSupportDist >= leftSupportDist)
2800 aabbProjection[stack] = leftSupportDist;
2801 stackPool[stack] = me->m_left;
2803 assert(stack < VHACD_STACK_DEPTH_3D);
2804 aabbProjection[stack] = rightSupportDist;
2805 stackPool[stack] = me->m_right;
2807 assert(stack < VHACD_STACK_DEPTH_3D);
2811 aabbProjection[stack] = rightSupportDist;
2812 stackPool[stack] = me->m_right;
2814 assert(stack < VHACD_STACK_DEPTH_3D);
2815 aabbProjection[stack] = leftSupportDist;
2816 stackPool[stack] = me->m_left;
2818 assert(stack < VHACD_STACK_DEPTH_3D);
2826 ConvexHullAABBTreeNode* cluster = me;
2827 for (
size_t i = 0; i < cluster->m_count; ++i)
2829 const ConvexHullVertex& p = points[cluster->m_indices[i]];
2830 assert(p.GetX() >= cluster->m_box[0].GetX());
2831 assert(p.GetX() <= cluster->m_box[1].GetX());
2832 assert(p.GetY() >= cluster->m_box[0].GetY());
2833 assert(p.GetY() <= cluster->m_box[1].GetY());
2834 assert(p.GetZ() >= cluster->m_box[0].GetZ());
2835 assert(p.GetZ() <= cluster->m_box[1].GetZ());
2839 double dist = p.Dot(dir);
2843 index = cluster->m_indices[i];
2846 else if (removeEntry)
2848 cluster->m_indices[i] = cluster->m_indices[cluster->m_count - 1];
2849 cluster->m_count = cluster->m_count - 1;
2854 if (cluster->m_count == 0)
2856 ConvexHullAABBTreeNode*
const parent = cluster->m_parent;
2859 ConvexHullAABBTreeNode*
const sibling = (parent->m_left != cluster) ? parent->m_left : parent->m_right;
2860 assert(sibling != cluster);
2861 ConvexHullAABBTreeNode*
const grandParent = parent->m_parent;
2864 sibling->m_parent = grandParent;
2865 if (grandParent->m_right == parent)
2867 grandParent->m_right = sibling;
2871 grandParent->m_left = sibling;
2876 sibling->m_parent =
nullptr;
2877 *treePointer = sibling;
2885 assert(index != -1);
2889 double ConvexHull::TetrahedrumVolume(
const VHACD::Vect3& p0,
2897 return p3p0.Dot(p1p0.Cross(p2p0));
2900 int ConvexHull::InitVertexArray(std::vector<ConvexHullVertex>& points, NodeBundle<ConvexHullAABBTreeNode>& memoryPool)
2904 ConvexHullAABBTreeNode* tree = BuildTreeOld(points, memoryPool);
2906 ConvexHullAABBTreeNode* tree = BuildTreeNew(points, (
char**)&memoryPool, maxMemSize);
2908 int count = int(points.size());
2915 m_points.resize(count);
2916 m_aabbP0 = tree->m_box[0];
2917 m_aabbP1 = tree->m_box[1];
2920 m_diag = boxSize.GetNorm();
2921 const ndNormalMap& normalMap = ndNormalMap::GetNormalMap();
2923 int index0 = SupportVertex(&tree, points, normalMap.m_normal[0]);
2924 m_points[0] = points[index0];
2925 points[index0].m_mark = 1;
2927 bool validTetrahedrum =
false;
2929 for (
int i = 1; i < normalMap.m_count; ++i)
2931 int index = SupportVertex(&tree, points, normalMap.m_normal[i]);
2934 e1 = points[index] - m_points[0];
2935 double error2 = e1.GetNormSquared();
2936 if (error2 > (
double(1.0e-4) * m_diag * m_diag))
2938 m_points[1] = points[index];
2939 points[index].m_mark = 1;
2940 validTetrahedrum =
true;
2944 if (!validTetrahedrum)
2951 validTetrahedrum =
false;
2954 for (
int i = 2; i < normalMap.m_count; ++i)
2956 int index = SupportVertex(&tree, points, normalMap.m_normal[i]);
2958 e2 = points[index] - m_points[0];
2959 normal = e1.Cross(e2);
2960 double error2 = normal.GetNorm();
2961 if (error2 > (
double(1.0e-4) * m_diag * m_diag))
2963 m_points[2] = points[index];
2964 points[index].m_mark = 1;
2965 validTetrahedrum =
true;
2970 if (!validTetrahedrum)
2978 validTetrahedrum =
false;
2981 index0 = SupportVertex(&tree, points, normal);
2982 e3 = points[index0] - m_points[0];
2983 double err2 = normal.Dot(e3);
2984 if (fabs(err2) > (
double(1.0e-6) * m_diag * m_diag))
2987 m_points[3] = points[index0];
2988 points[index0].m_mark = 1;
2989 validTetrahedrum =
true;
2991 if (!validTetrahedrum)
2994 int index = SupportVertex(&tree, points, n);
2995 e3 = points[index] - m_points[0];
2996 double error2 = normal.Dot(e3);
2997 if (fabs(error2) > (
double(1.0e-6) * m_diag * m_diag))
3000 m_points[3] = points[index];
3001 points[index].m_mark = 1;
3002 validTetrahedrum =
true;
3005 if (!validTetrahedrum)
3007 for (
int i = 3; i < normalMap.m_count; ++i)
3009 int index = SupportVertex(&tree, points, normalMap.m_normal[i]);
3013 e3 = points[index] - m_points[0];
3014 double error2 = normal.Dot(e3);
3015 if (fabs(error2) > (
double(1.0e-6) * m_diag * m_diag))
3018 m_points[3] = points[index];
3019 points[index].m_mark = 1;
3020 validTetrahedrum =
true;
3025 if (!validTetrahedrum)
3033 double volume = TetrahedrumVolume(m_points[0], m_points[1], m_points[2], m_points[3]);
3034 if (volume >
double(0.0))
3036 std::swap(m_points[2], m_points[3]);
3038 assert(TetrahedrumVolume(m_points[0], m_points[1], m_points[2], m_points[3]) <
double(0.0));
3042 std::list<ConvexHullFace>::iterator ConvexHull::AddFace(
int i0,
int i1,
int i2)
3044 ConvexHullFace face;
3045 face.m_index[0] = i0;
3046 face.m_index[1] = i1;
3047 face.m_index[2] = i2;
3049 std::list<ConvexHullFace>::iterator node = m_list.emplace(m_list.end(), face);
3053 void ConvexHull::CalculateConvexHull3D(ConvexHullAABBTreeNode* vertexTree,
3054 std::vector<ConvexHullVertex>& points,
3059 distTol = fabs(distTol) * m_diag;
3060 std::list<ConvexHullFace>::iterator f0Node = AddFace(0, 1, 2);
3061 std::list<ConvexHullFace>::iterator f1Node = AddFace(0, 2, 3);
3062 std::list<ConvexHullFace>::iterator f2Node = AddFace(2, 1, 3);
3063 std::list<ConvexHullFace>::iterator f3Node = AddFace(1, 0, 3);
3065 ConvexHullFace& f0 = *f0Node;
3066 ConvexHullFace& f1 = *f1Node;
3067 ConvexHullFace& f2 = *f2Node;
3068 ConvexHullFace& f3 = *f3Node;
3070 f0.m_twin[0] = f3Node;
3071 f0.m_twin[1] = f2Node;
3072 f0.m_twin[2] = f1Node;
3074 f1.m_twin[0] = f0Node;
3075 f1.m_twin[1] = f2Node;
3076 f1.m_twin[2] = f3Node;
3078 f2.m_twin[0] = f0Node;
3079 f2.m_twin[1] = f3Node;
3080 f2.m_twin[2] = f1Node;
3082 f3.m_twin[0] = f0Node;
3083 f3.m_twin[1] = f1Node;
3084 f3.m_twin[2] = f2Node;
3086 std::list<std::list<ConvexHullFace>::iterator> boundaryFaces;
3087 boundaryFaces.push_back(f0Node);
3088 boundaryFaces.push_back(f1Node);
3089 boundaryFaces.push_back(f2Node);
3090 boundaryFaces.push_back(f3Node);
3092 m_points.resize(count);
3095 maxVertexCount -= 4;
3096 int currentIndex = 4;
3101 std::vector<std::list<ConvexHullFace>::iterator> stack;
3102 std::vector<std::list<ConvexHullFace>::iterator> coneList;
3103 std::vector<std::list<ConvexHullFace>::iterator> deleteList;
3105 stack.reserve(1024 + count);
3106 coneList.reserve(1024 + count);
3107 deleteList.reserve(1024 + count);
3109 while (boundaryFaces.size() && count && (maxVertexCount > 0))
3126 std::list<ConvexHullFace>::iterator faceNode = boundaryFaces.back();
3127 ConvexHullFace& face = *faceNode;
3128 HullPlane planeEquation(face.GetPlaneEquation(m_points, isvalid));
3135 index = SupportVertex(&vertexTree, points, planeEquation);
3137 dist = planeEquation.Evalue(p);
3140 if (isvalid && (dist >= distTol) && (face.Evalue(m_points, p) <
double(0.0)))
3142 stack.push_back(faceNode);
3145 while (stack.size())
3147 std::list<ConvexHullFace>::iterator node1 = stack.back();
3148 ConvexHullFace& face1 = *node1;
3152 if (!face1.m_mark && (face1.Evalue(m_points, p) <
double(0.0)))
3155 for (
const auto node : deleteList)
3157 assert(node != node1);
3161 deleteList.push_back(node1);
3163 for (std::list<ConvexHullFace>::iterator& twinNode : face1.m_twin)
3165 ConvexHullFace& twinFace = *twinNode;
3166 if (!twinFace.m_mark)
3168 stack.push_back(twinNode);
3174 m_points[currentIndex] = points[index];
3175 points[index].m_mark = 1;
3178 for (std::list<ConvexHullFace>::iterator node1 : deleteList)
3180 ConvexHullFace& face1 = *node1;
3181 assert(face1.m_mark == 1);
3182 for (std::size_t j0 = 0; j0 < face1.m_twin.size(); ++j0)
3184 std::list<ConvexHullFace>::iterator twinNode = face1.m_twin[j0];
3185 ConvexHullFace& twinFace = *twinNode;
3186 if (!twinFace.m_mark)
3188 std::size_t j1 = (j0 == 2) ? 0 : j0 + 1;
3189 std::list<ConvexHullFace>::iterator newNode = AddFace(currentIndex, face1.m_index[j0], face1.m_index[j1]);
3190 boundaryFaces.push_front(newNode);
3191 ConvexHullFace& newFace = *newNode;
3193 newFace.m_twin[1] = twinNode;
3194 for (std::size_t k = 0; k < twinFace.m_twin.size(); ++k)
3196 if (twinFace.m_twin[k] == node1)
3198 twinFace.m_twin[k] = newNode;
3201 coneList.push_back(newNode);
3206 for (std::size_t i = 0; i < coneList.size() - 1; ++i)
3208 std::list<ConvexHullFace>::iterator nodeA = coneList[i];
3209 ConvexHullFace& faceA = *nodeA;
3210 assert(faceA.m_mark == 0);
3211 for (std::size_t j = i + 1; j < coneList.size(); j++)
3213 std::list<ConvexHullFace>::iterator nodeB = coneList[j];
3214 ConvexHullFace& faceB = *nodeB;
3215 assert(faceB.m_mark == 0);
3216 if (faceA.m_index[2] == faceB.m_index[1])
3218 faceA.m_twin[2] = nodeB;
3219 faceB.m_twin[0] = nodeA;
3224 for (std::size_t j = i + 1; j < coneList.size(); j++)
3226 std::list<ConvexHullFace>::iterator nodeB = coneList[j];
3227 ConvexHullFace& faceB = *nodeB;
3228 assert(faceB.m_mark == 0);
3229 if (faceA.m_index[1] == faceB.m_index[2])
3231 faceA.m_twin[0] = nodeB;
3232 faceB.m_twin[2] = nodeA;
3238 for (std::list<ConvexHullFace>::iterator node : deleteList)
3240 auto it = std::find(boundaryFaces.begin(), boundaryFaces.end(), node);
3241 if (it != boundaryFaces.end())
3243 boundaryFaces.erase(it);
3254 auto it = std::find(boundaryFaces.begin(), boundaryFaces.end(), faceNode);
3255 if (it != boundaryFaces.end())
3257 boundaryFaces.erase(it);
3261 m_points.resize(currentIndex);
3277 class KdTreeFindNode
3280 KdTreeFindNode() =
default;
3282 KdTreeNode* m_node{
nullptr };
3283 double m_distance{ 0.0 };
3293 uint32_t Search(
const VHACD::Vect3& pos,
double radius, uint32_t maxObjects, KdTreeFindNode* found)
const;
3297 KdTreeNode& GetNewNode(uint32_t index);
3301 bool& _found)
const;
3303 const std::vector<VHACD::Vertex>& GetVertices()
const;
3304 std::vector<VHACD::Vertex>&& TakeVertices();
3306 uint32_t GetVCount()
const;
3309 KdTreeNode* m_root{
nullptr };
3310 NodeBundle<KdTreeNode> m_bundle;
3312 std::vector<VHACD::Vertex> m_vertices;
3318 KdTreeNode() =
default;
3319 KdTreeNode(uint32_t index);
3321 void Add(KdTreeNode& node, Axes dim,
const KdTree& iface);
3323 uint32_t GetIndex()
const;
3325 void Search(Axes axis,
3329 uint32_t maxObjects,
3330 KdTreeFindNode* found,
3331 const KdTree& iface);
3334 uint32_t m_index = 0;
3335 KdTreeNode* m_left =
nullptr;
3336 KdTreeNode* m_right =
nullptr;
3339 const VHACD::Vertex& KdTree::GetPosition(uint32_t index)
const
3341 assert(index < m_vertices.size());
3342 return m_vertices[index];
3345 uint32_t KdTree::Search(
const VHACD::Vect3& pos,
double radius, uint32_t maxObjects, KdTreeFindNode* found)
const
3350 m_root->Search(X_AXIS, pos, radius, count, maxObjects, found, *
this);
3356 uint32_t ret = uint32_t(m_vertices.size());
3357 m_vertices.emplace_back(v);
3358 KdTreeNode& node = GetNewNode(ret);
3361 m_root->Add(node, X_AXIS, *
this);
3370 KdTreeNode& KdTree::GetNewNode(uint32_t index)
3372 KdTreeNode& node = m_bundle.GetNextNode();
3373 node = KdTreeNode(index);
3384 KdTreeFindNode found;
3385 uint32_t count = Search(pos, radius, 1, &found);
3388 KdTreeNode* node = found.m_node;
3389 ret = node->GetIndex();
3395 const std::vector<VHACD::Vertex>& KdTree::GetVertices()
const {
return m_vertices; }
3397 std::vector<VHACD::Vertex>&& KdTree::TakeVertices() {
return std::move(m_vertices); }
3399 uint32_t KdTree::GetVCount()
const {
return uint32_t(m_vertices.size()); }
3401 KdTreeNode::KdTreeNode(uint32_t index) : m_index(index) {}
3403 void KdTreeNode::Add(KdTreeNode& node, Axes dim,
const KdTree& tree)
3423 const VHACD::Vertex& nodePosition = tree.GetPosition(node.m_index);
3425 if (nodePosition[idx] <= position[idx])
3428 m_left->Add(node, axis, tree);
3435 m_right->Add(node, axis, tree);
3441 uint32_t KdTreeNode::GetIndex()
const {
return m_index; }
3443 void KdTreeNode::Search(Axes axis,
3447 uint32_t maxObjects,
3448 KdTreeFindNode* found,
3449 const KdTree& iface)
3451 const VHACD::Vect3 position = iface.GetPosition(m_index);
3455 KdTreeNode* search1 = 0;
3456 KdTreeNode* search2 = 0;
3478 if (-d[idx] < radius)
3485 if (d[idx] < radius)
3489 double r2 = radius * radius;
3498 found[count].m_node =
this;
3499 found[count].m_distance = m;
3504 if (m < found[0].m_distance)
3506 if (maxObjects == 1)
3508 found[0].m_node =
this;
3509 found[0].m_distance = m;
3513 found[1] = found[0];
3514 found[0].m_node =
this;
3515 found[0].m_distance = m;
3518 else if (maxObjects > 1)
3520 found[1].m_node =
this;
3521 found[1].m_distance = m;
3527 bool inserted =
false;
3529 for (uint32_t i = 0; i < count; i++)
3531 if (m < found[i].m_distance)
3534 uint32_t scan = count;
3535 if (scan >= maxObjects)
3536 scan = maxObjects - 1;
3537 for (uint32_t j = scan; j > i; j--)
3539 found[j] = found[j - 1];
3541 found[i].m_node =
this;
3542 found[i].m_distance = m;
3548 if (!inserted && count < maxObjects)
3550 found[count].m_node =
this;
3551 found[count].m_distance = m;
3559 if (count > maxObjects)
3566 search1->Search(axis, pos, radius, count, maxObjects, found, iface);
3569 search2->Search(axis, pos, radius, count, maxObjects, found, iface);
3575 VertexIndex(
double granularity,
bool snapToGrid);
3581 const std::vector<VHACD::Vertex>& GetVertices()
const;
3583 std::vector<VHACD::Vertex>&& TakeVertices();
3585 uint32_t GetVCount()
const;
3587 bool SaveAsObj(
const char* fname, uint32_t tcount, uint32_t* indices)
3591 FILE* fph = fopen(fname,
"wb");
3596 const std::vector<VHACD::Vertex>& v = GetVertices();
3597 for (uint32_t i = 0; i < v.size(); ++i)
3599 fprintf(fph,
"v %0.9f %0.9f %0.9f\r\n", v[i].mX, v[i].mY, v[i].mZ);
3602 for (uint32_t i = 0; i < tcount; i++)
3604 uint32_t i1 = *indices++;
3605 uint32_t i2 = *indices++;
3606 uint32_t i3 = *indices++;
3607 fprintf(fph,
"f %d %d %d\r\n", i1 + 1, i2 + 1, i3 + 1);
3616 bool m_snapToGrid : 1;
3617 double m_granularity;
3621 VertexIndex::VertexIndex(
double granularity,
bool snapToGrid) : m_snapToGrid(snapToGrid), m_granularity(granularity) {}
3625 for (
int i = 0; i < 3; ++i)
3627 double m = fmod(p[i], m_granularity);
3633 uint32_t VertexIndex::GetIndex(
VHACD::Vect3 p,
bool& newPos)
3645 ret = m_KdTree.GetNearest(p, m_granularity, found);
3655 const std::vector<VHACD::Vertex>& VertexIndex::GetVertices()
const {
return m_KdTree.GetVertices(); }
3657 std::vector<VHACD::Vertex>&& VertexIndex::TakeVertices() {
return std::move(m_KdTree.TakeVertices()); }
3659 uint32_t VertexIndex::GetVCount()
const {
return m_KdTree.GetVCount(); }
3671 static constexpr
int VoxelBitsZStart = 0;
3672 static constexpr
int VoxelBitsYStart = 10;
3673 static constexpr
int VoxelBitsXStart = 20;
3674 static constexpr
int VoxelBitMask = 0x03FF;
3678 Voxel(uint32_t index);
3680 Voxel(uint32_t x, uint32_t y, uint32_t z);
3686 uint32_t GetX()
const;
3687 uint32_t GetY()
const;
3688 uint32_t GetZ()
const;
3690 uint32_t GetVoxelAddress()
const;
3693 uint32_t m_voxel{ 0 };
3696 Voxel::Voxel(uint32_t index) : m_voxel(index) {}
3698 Voxel::Voxel(uint32_t x, uint32_t y, uint32_t z)
3699 : m_voxel((x << VoxelBitsXStart) | (y << VoxelBitsYStart) | (z << VoxelBitsZStart))
3701 assert(x < 1024 &&
"Voxel constructed with X outside of range");
3702 assert(y < 1024 &&
"Voxel constructed with Y outside of range");
3703 assert(z < 1024 &&
"Voxel constructed with Z outside of range");
3706 bool Voxel::operator==(
const Voxel& v)
const {
return m_voxel == v.m_voxel; }
3710 uint32_t Voxel::GetX()
const {
return (m_voxel >> VoxelBitsXStart) & VoxelBitMask; }
3712 uint32_t Voxel::GetY()
const {
return (m_voxel >> VoxelBitsYStart) & VoxelBitMask; }
3714 uint32_t Voxel::GetZ()
const {
return (m_voxel >> VoxelBitsZStart) & VoxelBitMask; }
3716 uint32_t Voxel::GetVoxelAddress()
const {
return m_voxel; }
3720 std::vector<VHACD::Vertex> m_vertices;
3721 std::vector<VHACD::Triangle> m_indices;
3733 for (uint32_t i = 0; i < 3; ++i)
3735 if (start[i] < bounds.
GetMin()[i])
3737 if (dir[i] !=
double(0.0))
3738 ta[i] = (bounds.
GetMin()[i] - start[i]) / dir[i];
3741 else if (start[i] > bounds.
GetMax()[i])
3743 if (dir[i] !=
double(0.0))
3744 ta[i] = (bounds.
GetMax()[i] - start[i]) / dir[i];
3759 double tmax = ta.MaxCoeff(taxis);
3761 if (tmax <
double(0.0))
3768 double eps = double(0.0);
3786 inline bool IntersectRayTriTwoSided(
const VHACD::Vect3& p,
3802 double d = -dir.
Dot(n);
3803 double ood = double(1.0) / d;
3806 t = ap.
Dot(n) * ood;
3807 if (t <
double(0.0))
3813 v = ac.
Dot(e) * ood;
3814 if (v <
double(0.0) || v >
double(1.0))
3818 w = -ab.
Dot(e) * ood;
3819 if (w <
double(0.0) || v + w >
double(1.0))
3824 u = double(1.0) - v - w;
3847 double d1 = ab.
Dot(ap);
3848 double d2 = ac.
Dot(ap);
3849 if (d1 <=
double(0.0) && d2 <=
double(0.0))
3857 double d3 = ab.
Dot(bp);
3858 double d4 = ac.
Dot(bp);
3859 if (d3 >=
double(0.0) && d4 <= d3)
3866 double vc = d1 * d4 - d3 * d2;
3867 if (vc <=
double(0.0) && d1 >=
double(0.0) && d3 <=
double(0.0))
3875 double d5 = ab.
Dot(cp);
3876 double d6 = ac.
Dot(cp);
3877 if (d6 >=
double(0.0) && d5 <= d6)
3884 double vb = d5 * d2 - d1 * d6;
3885 if (vb <=
double(0.0) && d2 >=
double(0.0) && d6 <=
double(0.0))
3892 double va = d3 * d6 - d5 * d4;
3893 if (va <=
double(0.0) && (d4 - d3) >=
double(0.0) && (d5 - d6) >=
double(0.0))
3895 w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
3896 v = double(1.0) - w;
3897 return b + w * (c - b);
3900 double denom = double(1.0) / (va + vb + vc);
3903 return a + ab * v + ac * w;
3909 AABBTree() =
default;
3910 AABBTree(AABBTree&&) =
default;
3911 AABBTree& operator=(AABBTree&&) =
default;
3913 AABBTree(
const std::vector<VHACD::Vertex>& vertices,
const std::vector<VHACD::Triangle>& indices);
3931 uint32_t& faceIndex)
const;
3944 uint32_t m_children;
3945 uint32_t m_numFaces{ 0 };
3948 uint32_t* m_faces{
nullptr };
3954 FaceSorter(
const std::vector<VHACD::Vertex>& positions,
const std::vector<VHACD::Triangle>& indices, uint32_t axis);
3956 bool operator()(uint32_t lhs, uint32_t rhs)
const;
3958 double GetCentroid(uint32_t face)
const;
3960 const std::vector<VHACD::Vertex>& m_vertices;
3961 const std::vector<VHACD::Triangle>& m_indices;
3966 uint32_t PartitionMedian(Node& n, uint32_t* faces, uint32_t numFaces);
3967 uint32_t PartitionSAH(Node& n, uint32_t* faces, uint32_t numFaces);
3971 void BuildRecursive(uint32_t nodeIndex, uint32_t* faces, uint32_t numFaces);
3973 void TraceRecursive(uint32_t nodeIndex,
3981 uint32_t& faceIndex)
const;
3984 const double maxDis,
3988 uint32_t& faceIndex,
3991 void GetClosestPointWithinDistanceSqRecursive(uint32_t nodeIndex,
3996 uint32_t& outFaceIndex,
4002 uint32_t m_freeNode;
4004 const std::vector<VHACD::Vertex>* m_vertices{
nullptr };
4005 const std::vector<VHACD::Triangle>* m_indices{
nullptr };
4007 std::vector<uint32_t> m_faces;
4008 std::vector<Node> m_nodes;
4009 std::vector<VHACD::BoundsAABB> m_faceBounds;
4012 uint32_t m_treeDepth{ 0 };
4013 uint32_t m_innerNodes{ 0 };
4014 uint32_t m_leafNodes{ 0 };
4016 uint32_t s_depth{ 0 };
4019 AABBTree::FaceSorter::FaceSorter(
const std::vector<VHACD::Vertex>& positions,
4020 const std::vector<VHACD::Triangle>& indices,
4022 : m_vertices(positions), m_indices(indices), m_axis(axis)
4026 inline bool AABBTree::FaceSorter::operator()(uint32_t lhs, uint32_t rhs)
const
4028 double a = GetCentroid(lhs);
4029 double b = GetCentroid(rhs);
4041 inline double AABBTree::FaceSorter::GetCentroid(uint32_t face)
const
4043 const VHACD::Vect3& a = m_vertices[m_indices[face].mI0];
4044 const VHACD::Vect3& b = m_vertices[m_indices[face].mI1];
4045 const VHACD::Vect3& c = m_vertices[m_indices[face].mI2];
4047 return (a[m_axis] + b[m_axis] + c[m_axis]) / double(3.0);
4050 AABBTree::AABBTree(
const std::vector<VHACD::Vertex>& vertices,
const std::vector<VHACD::Triangle>& indices)
4051 : m_vertices(&vertices), m_indices(&indices)
4066 bool hit = TraceRay(start, dir, outT, u, v, w, faceSign, faceIndex);
4069 hitLocation = start + dir * outT;
4072 if (hit && outT > distance)
4081 uint32_t& insideCount,
4082 uint32_t& outsideCount)
const
4084 double outT, u, v, w, faceSign;
4086 bool hit = TraceRay(start, dir, outT, u, v, w, faceSign, faceIndex);
4108 uint32_t& faceIndex)
const
4111 TraceRecursive(0, start, dir, outT, u, v, w, faceSign, faceIndex);
4112 return (outT != FLT_MAX);
4115 VHACD::Vect3 AABBTree::GetCenter()
const {
return m_nodes[0].m_extents.GetCenter(); }
4117 VHACD::Vect3 AABBTree::GetMinExtents()
const {
return m_nodes[0].m_extents.GetMin(); }
4119 VHACD::Vect3 AABBTree::GetMaxExtents()
const {
return m_nodes[0].m_extents.GetMax(); }
4127 bool hit = GetClosestPointWithinDistance(
point, maxDistance, dis, v, w, faceIndex, closestPoint);
4132 uint32_t AABBTree::PartitionMedian(Node& n, uint32_t* faces, uint32_t numFaces)
4134 FaceSorter predicate(*m_vertices, *m_indices, n.m_extents.GetSize().LongestAxis());
4135 std::nth_element(faces, faces + numFaces / 2, faces + numFaces, predicate);
4137 return numFaces / 2;
4141 uint32_t AABBTree::PartitionSAH(Node&, uint32_t* faces, uint32_t numFaces)
4143 uint32_t bestAxis = 0;
4144 uint32_t bestIndex = 0;
4145 double bestCost = FLT_MAX;
4147 for (uint32_t a = 0; a < 3; ++a)
4150 FaceSorter predicate(*m_vertices, *m_indices, a);
4151 std::sort(faces, faces + numFaces, predicate);
4154 std::vector<double> cumulativeLower(numFaces);
4155 std::vector<double> cumulativeUpper(numFaces);
4160 for (uint32_t i = 0; i < numFaces; ++i)
4162 lower.
Union(m_faceBounds[faces[i]]);
4163 upper.
Union(m_faceBounds[faces[numFaces - i - 1]]);
4166 cumulativeUpper[numFaces - i - 1] = upper.
SurfaceArea();
4169 double invTotalSA = double(1.0) / cumulativeUpper[0];
4172 for (uint32_t i = 0; i < numFaces - 1; ++i)
4174 double pBelow = cumulativeLower[i] * invTotalSA;
4175 double pAbove = cumulativeUpper[i] * invTotalSA;
4177 double cost = double(0.125) + (pBelow * i + pAbove * (numFaces - i));
4178 if (cost <= bestCost)
4188 FaceSorter predicate(*m_vertices, *m_indices, bestAxis);
4189 std::sort(faces, faces + numFaces, predicate);
4191 return bestIndex + 1;
4194 void AABBTree::Build()
4196 const uint32_t numFaces = uint32_t(m_indices->size());
4199 m_faces.reserve(numFaces);
4202 m_faceBounds.reserve(numFaces);
4204 std::vector<VHACD::BoundsAABB> stack;
4205 for (uint32_t i = 0; i < numFaces; ++i)
4209 m_faces.push_back(i);
4210 m_faceBounds.push_back(top);
4213 m_nodes.reserve(uint32_t(numFaces *
double(1.5)));
4219 BuildRecursive(0, m_faces.data(), numFaces);
4221 assert(s_depth == 0);
4224 void AABBTree::BuildRecursive(uint32_t nodeIndex, uint32_t* faces, uint32_t numFaces)
4226 const uint32_t kMaxFacesPerLeaf = 6;
4229 if (nodeIndex >= m_nodes.size())
4231 uint32_t s = std::max(uint32_t(
double(1.5) * m_nodes.size()), 512U);
4236 Node& n = m_nodes[nodeIndex];
4240 m_treeDepth = std::max(m_treeDepth, s_depth);
4242 n.m_extents = CalculateFaceBounds(faces, numFaces);
4245 if (numFaces <= kMaxFacesPerLeaf)
4248 n.m_numFaces = numFaces;
4257 const uint32_t leftCount = PartitionMedian(n, faces, numFaces);
4259 const uint32_t rightCount = numFaces - leftCount;
4262 m_nodes[nodeIndex].m_children = m_freeNode;
4268 BuildRecursive(m_nodes[nodeIndex].m_children + 0, faces, leftCount);
4269 BuildRecursive(m_nodes[nodeIndex].m_children + 1, faces + leftCount, rightCount);
4275 void AABBTree::TraceRecursive(uint32_t nodeIndex,
4283 uint32_t& faceIndex)
const
4285 const Node& node = m_nodes[nodeIndex];
4287 if (node.m_faces == NULL)
4290 const Node& leftChild = m_nodes[node.m_children + 0];
4291 const Node& rightChild = m_nodes[node.m_children + 1];
4293 double dist[2] = { FLT_MAX, FLT_MAX };
4295 IntersectRayAABB(start, dir, leftChild.m_extents, dist[0]);
4296 IntersectRayAABB(start, dir, rightChild.m_extents, dist[1]);
4298 uint32_t closest = 0;
4299 uint32_t furthest = 1;
4301 if (dist[1] < dist[0])
4307 if (dist[closest] < outT)
4309 TraceRecursive(node.m_children + closest, start, dir, outT, outU, outV, outW, faceSign, faceIndex);
4312 if (dist[furthest] < outT)
4314 TraceRecursive(node.m_children + furthest, start, dir, outT, outU, outV, outW, faceSign, faceIndex);
4319 double t, u, v, w, s;
4321 for (uint32_t i = 0; i < node.m_numFaces; ++i)
4323 uint32_t indexStart = node.m_faces[i];
4325 const VHACD::Vect3& a = (*m_vertices)[(*m_indices)[indexStart].mI0];
4326 const VHACD::Vect3& b = (*m_vertices)[(*m_indices)[indexStart].mI1];
4327 const VHACD::Vect3& c = (*m_vertices)[(*m_indices)[indexStart].mI2];
4328 if (IntersectRayTriTwoSided(start, dir, a, b, c, t, u, v, w, s, NULL))
4337 faceIndex = node.m_faces[i];
4345 const double maxDis,
4349 uint32_t& faceIndex,
4353 faceIndex = uint32_t(~0);
4354 double disSq = dis * dis;
4356 GetClosestPointWithinDistanceSqRecursive(0,
point, disSq, v, w, faceIndex, closest);
4359 return (faceIndex < (~(
static_cast<unsigned int>(0))));
4362 void AABBTree::GetClosestPointWithinDistanceSqRecursive(uint32_t nodeIndex,
4367 uint32_t& outFaceIndex,
4370 const Node& node = m_nodes[nodeIndex];
4372 if (node.m_faces ==
nullptr)
4375 const Node& leftChild = m_nodes[node.m_children + 0];
4376 const Node& rightChild = m_nodes[node.m_children + 1];
4382 uint32_t closest = 0;
4383 uint32_t furthest = 1;
4384 double dcSq = (
point - lp).GetNormSquared();
4385 double dfSq = (
point - rp).GetNormSquared();
4390 std::swap(dfSq, dcSq);
4393 if (dcSq < outDisSq)
4395 GetClosestPointWithinDistanceSqRecursive(
4396 node.m_children + closest,
point, outDisSq, outV, outW, outFaceIndex, closestPoint);
4399 if (dfSq < outDisSq)
4401 GetClosestPointWithinDistanceSqRecursive(
4402 node.m_children + furthest,
point, outDisSq, outV, outW, outFaceIndex, closestPoint);
4408 for (uint32_t i = 0; i < node.m_numFaces; ++i)
4410 uint32_t indexStart = node.m_faces[i];
4412 const VHACD::Vect3& a = (*m_vertices)[(*m_indices)[indexStart].mI0];
4413 const VHACD::Vect3& b = (*m_vertices)[(*m_indices)[indexStart].mI1];
4414 const VHACD::Vect3& c = (*m_vertices)[(*m_indices)[indexStart].mI2];
4417 double disSq = (cp -
point).GetNormSquared();
4419 if (disSq < outDisSq)
4425 outFaceIndex = node.m_faces[i];
4431 VHACD::BoundsAABB AABBTree::CalculateFaceBounds(uint32_t* faces, uint32_t numFaces)
4437 for (uint32_t i = 0; i < numFaces; ++i)
4439 VHACD::Vect3 a = (*m_vertices)[(*m_indices)[faces[i]].mI0];
4440 VHACD::Vect3 b = (*m_vertices)[(*m_indices)[faces[i]].mI1];
4441 VHACD::Vect3 c = (*m_vertices)[(*m_indices)[faces[i]].mI2];
4443 minExtents = a.
CWiseMin(minExtents);
4444 maxExtents = a.
CWiseMax(maxExtents);
4446 minExtents = b.
CWiseMin(minExtents);
4447 maxExtents = b.
CWiseMax(maxExtents);
4449 minExtents = c.
CWiseMin(minExtents);
4450 maxExtents = c.
CWiseMax(maxExtents);
4456 enum class VoxelValue : uint8_t
4458 PRIMITIVE_UNDEFINED = 0,
4459 PRIMITIVE_OUTSIDE_SURFACE_TOWALK = 1,
4460 PRIMITIVE_OUTSIDE_SURFACE = 2,
4461 PRIMITIVE_INSIDE_SURFACE = 3,
4462 PRIMITIVE_ON_SURFACE = 4
4468 void Voxelize(
const std::vector<VHACD::Vertex>& points,
4469 const std::vector<VHACD::Triangle>& triangles,
4472 const AABBTree& aabbTree);
4474 void RaycastFill(
const AABBTree& aabbTree);
4476 void SetVoxel(
const size_t i,
const size_t j,
const size_t k, VoxelValue value);
4478 VoxelValue& GetVoxel(
const size_t i,
const size_t j,
const size_t k);
4480 const VoxelValue& GetVoxel(
const size_t i,
const size_t j,
const size_t k)
const;
4482 const std::vector<Voxel>& GetSurfaceVoxels()
const;
4483 const std::vector<Voxel>& GetInteriorVoxels()
const;
4485 double GetScale()
const;
4490 double m_scale{ 1.0 };
4492 size_t m_numVoxelsOnSurface{ 0 };
4493 size_t m_numVoxelsInsideSurface{ 0 };
4494 size_t m_numVoxelsOutsideSurface{ 0 };
4495 std::vector<VoxelValue> m_data;
4498 void MarkOutsideSurface(
const size_t i0,
4504 void FillOutsideSurface();
4506 void FillInsideSurface();
4508 std::vector<VHACD::Voxel> m_surfaceVoxels;
4509 std::vector<VHACD::Voxel> m_interiorVoxels;
4518 for (q = 0; q < 3; q++)
4521 if (normal[q] >
double(0.0))
4523 vmin[q] = -maxbox[q] - v;
4524 vmax[q] = maxbox[q] - v;
4528 vmin[q] = maxbox[q] - v;
4529 vmax[q] = -maxbox[q] - v;
4532 if (normal.
Dot(vmin) >
double(0.0))
4534 if (normal.
Dot(vmax) >=
double(0.0))
4539 bool AxisTest(
double a,
4547 double boxHalfSize1,
4548 double boxHalfSize2)
4550 double p0 = a * v0 + b * v1;
4551 double p1 = a * v2 + b * v3;
4553 double min = std::min(p0, p1);
4554 double max = std::max(p0, p1);
4556 double rad = fa * boxHalfSize1 + fb * boxHalfSize2;
4557 if (min > rad || max < -rad)
4591 double fex = fabs(e0[0]);
4592 double fey = fabs(e0[1]);
4593 double fez = fabs(e0[2]);
4598 if (!AxisTest(e0[2], -e0[1], fez, fey, v0[1], v0[2], v2[1], v2[2], boxHalfSize[1], boxHalfSize[2]))
4600 if (!AxisTest(-e0[2], e0[0], fez, fex, v0[0], v0[2], v2[0], v2[2], boxHalfSize[0], boxHalfSize[2]))
4602 if (!AxisTest(e0[1], -e0[0], fey, fex, v1[0], v1[1], v2[0], v2[1], boxHalfSize[0], boxHalfSize[1]))
4609 if (!AxisTest(e1[2], -e1[1], fez, fey, v0[1], v0[2], v2[1], v2[2], boxHalfSize[1], boxHalfSize[2]))
4611 if (!AxisTest(-e1[2], e1[0], fez, fex, v0[0], v0[2], v2[0], v2[2], boxHalfSize[0], boxHalfSize[2]))
4613 if (!AxisTest(e1[1], -e1[0], fey, fex, v0[0], v0[1], v1[0], v1[1], boxHalfSize[0], boxHalfSize[2]))
4620 if (!AxisTest(e2[2], -e2[1], fez, fey, v0[1], v0[2], v1[1], v1[2], boxHalfSize[1], boxHalfSize[2]))
4622 if (!AxisTest(-e2[2], e2[0], fez, fex, v0[0], v0[2], v1[0], v1[2], boxHalfSize[0], boxHalfSize[2]))
4624 if (!AxisTest(e2[1], -e2[0], fey, fex, v1[0], v1[1], v2[0], v2[1], boxHalfSize[0], boxHalfSize[1]))
4634 double min = std::min({ v0.GetX(), v1.GetX(), v2.GetX() });
4635 double max = std::max({ v0.GetX(), v1.GetX(), v2.GetX() });
4636 if (min > boxHalfSize[0] || max < -boxHalfSize[0])
4640 min = std::min({ v0.GetY(), v1.GetY(), v2.GetY() });
4641 max = std::max({ v0.GetY(), v1.GetY(), v2.GetY() });
4642 if (min > boxHalfSize[1] || max < -boxHalfSize[1])
4646 min = std::min({ v0.GetZ(), v1.GetZ(), v2.GetZ() });
4647 max = std::max({ v0.GetZ(), v1.GetZ(), v2.GetZ() });
4648 if (min > boxHalfSize[2] || max < -boxHalfSize[2])
4656 if (!PlaneBoxOverlap(normal, v0, boxHalfSize))
4661 void Volume::Voxelize(
const std::vector<VHACD::Vertex>& points,
4662 const std::vector<VHACD::Triangle>& indices,
4663 const size_t dimensions,
4665 const AABBTree& aabbTree)
4667 double a = std::pow(dimensions, 0.33);
4668 size_t dim = a * double(1.5);
4669 dim = std::max(dim,
size_t(32));
4671 if (points.size() == 0)
4676 m_bounds = BoundsAABB(points);
4683 if (d[0] >= d[1] && d[0] >= d[2])
4686 m_dim[0] = uint32_t(dim);
4687 m_dim[1] = uint32_t(2 +
static_cast<size_t>(dim * d[1] / d[0]));
4688 m_dim[2] = uint32_t(2 +
static_cast<size_t>(dim * d[2] / d[0]));
4690 else if (d[1] >= d[0] && d[1] >= d[2])
4693 m_dim[1] = uint32_t(dim);
4694 m_dim[0] = uint32_t(2 +
static_cast<size_t>(dim * d[0] / d[1]));
4695 m_dim[2] = uint32_t(2 +
static_cast<size_t>(dim * d[2] / d[1]));
4700 m_dim[2] = uint32_t(dim);
4701 m_dim[0] = uint32_t(2 +
static_cast<size_t>(dim * d[0] / d[2]));
4702 m_dim[1] = uint32_t(2 +
static_cast<size_t>(dim * d[1] / d[2]));
4705 m_scale =
r / (dim - 1);
4706 double invScale = (dim - 1) / r;
4708 m_data = std::vector<VoxelValue>(m_dim[0] * m_dim[1] * m_dim[2], VoxelValue::PRIMITIVE_UNDEFINED);
4709 m_numVoxelsOnSurface = 0;
4710 m_numVoxelsInsideSurface = 0;
4711 m_numVoxelsOutsideSurface = 0;
4717 for (
size_t t = 0; t < indices.size(); ++t)
4722 for (int32_t c = 0; c < 3; ++c)
4724 pt = points[tri[c]];
4726 p[c] = (pt - m_bounds.GetMin()) * invScale;
4728 size_t i =
static_cast<size_t>(p[c][0] + double(0.5));
4729 size_t j =
static_cast<size_t>(p[c][1] + double(0.5));
4730 size_t k =
static_cast<size_t>(p[c][2] + double(0.5));
4732 assert(i < m_dim[0] && j < m_dim[1] && k < m_dim[2]);
4742 i0 = std::min(i0, i);
4743 j0 = std::min(j0, j);
4744 k0 = std::min(k0, k);
4746 i1 = std::max(i1, i);
4747 j1 = std::max(j1, j);
4748 k1 = std::max(k1, k);
4763 for (
size_t i_id = i0; i_id < i1; ++i_id)
4765 boxcenter[0] = uint32_t(i_id);
4766 for (
size_t j_id = j0; j_id < j1; ++j_id)
4768 boxcenter[1] = uint32_t(j_id);
4769 for (
size_t k_id = k0; k_id < k1; ++k_id)
4771 boxcenter[2] = uint32_t(k_id);
4772 bool res = TriBoxOverlap(boxcenter, boxhalfsize, p[0], p[1], p[2]);
4773 VoxelValue& value = GetVoxel(i_id, j_id, k_id);
4774 if (res && value == VoxelValue::PRIMITIVE_UNDEFINED)
4776 value = VoxelValue::PRIMITIVE_ON_SURFACE;
4777 ++m_numVoxelsOnSurface;
4778 m_surfaceVoxels.emplace_back(uint32_t(i_id), uint32_t(j_id), uint32_t(k_id));
4785 if (fillMode == FillMode::SURFACE_ONLY)
4787 const size_t i0_local = m_dim[0];
4788 const size_t j0_local = m_dim[1];
4789 const size_t k0_local = m_dim[2];
4790 for (
size_t i_id = 0; i_id < i0_local; ++i_id)
4792 for (
size_t j_id = 0; j_id < j0_local; ++j_id)
4794 for (
size_t k_id = 0; k_id < k0_local; ++k_id)
4796 const VoxelValue& voxel = GetVoxel(i_id, j_id, k_id);
4797 if (voxel != VoxelValue::PRIMITIVE_ON_SURFACE)
4799 SetVoxel(i_id, j_id, k_id, VoxelValue::PRIMITIVE_OUTSIDE_SURFACE);
4805 else if (fillMode == FillMode::FLOOD_FILL)
4810 MarkOutsideSurface(0, 0, 0, m_dim[0], m_dim[1], 1);
4811 MarkOutsideSurface(0, 0, m_dim[2] - 1, m_dim[0], m_dim[1], m_dim[2]);
4812 MarkOutsideSurface(0, 0, 0, m_dim[0], 1, m_dim[2]);
4813 MarkOutsideSurface(0, m_dim[1] - 1, 0, m_dim[0], m_dim[1], m_dim[2]);
4814 MarkOutsideSurface(0, 0, 0, 1, m_dim[1], m_dim[2]);
4815 MarkOutsideSurface(m_dim[0] - 1, 0, 0, m_dim[0], m_dim[1], m_dim[2]);
4816 FillOutsideSurface();
4817 FillInsideSurface();
4819 else if (fillMode == FillMode::RAYCAST_FILL)
4821 RaycastFill(aabbTree);
4825 void Volume::RaycastFill(
const AABBTree& aabbTree)
4827 const uint32_t i0 = m_dim[0];
4828 const uint32_t j0 = m_dim[1];
4829 const uint32_t k0 = m_dim[2];
4831 size_t maxSize = i0 * j0 * k0;
4833 std::vector<Voxel> temp;
4834 temp.reserve(maxSize);
4835 uint32_t count{ 0 };
4836 m_numVoxelsInsideSurface = 0;
4837 for (uint32_t i = 0; i < i0; ++i)
4839 for (uint32_t j = 0; j < j0; ++j)
4841 for (uint32_t k = 0; k < k0; ++k)
4843 VoxelValue& voxel = GetVoxel(i, j, k);
4844 if (voxel != VoxelValue::PRIMITIVE_ON_SURFACE)
4848 uint32_t insideCount = 0;
4849 uint32_t outsideCount = 0;
4853 VHACD::Vect3(0, 1, 0),
VHACD::Vect3(0, -1, 0),
VHACD::Vect3(0, 0, 1),
VHACD::Vect3(0, 0, -1)
4856 for (uint32_t r = 0;
r < 6;
r++)
4858 aabbTree.TraceRay(start, directions[r], insideCount, outsideCount);
4865 if (insideCount >= 3)
4871 if (outsideCount == 0 && insideCount >= 3)
4873 voxel = VoxelValue::PRIMITIVE_INSIDE_SURFACE;
4874 temp.emplace_back(i, j, k);
4876 m_numVoxelsInsideSurface++;
4880 voxel = VoxelValue::PRIMITIVE_OUTSIDE_SURFACE;
4889 m_interiorVoxels = std::move(temp);
4893 void Volume::SetVoxel(
const size_t i,
const size_t j,
const size_t k, VoxelValue value)
4895 assert(i < m_dim[0]);
4896 assert(j < m_dim[1]);
4897 assert(k < m_dim[2]);
4899 m_data[k + j * m_dim[2] + i * m_dim[1] * m_dim[2]] = value;
4902 VoxelValue& Volume::GetVoxel(
const size_t i,
const size_t j,
const size_t k)
4904 assert(i < m_dim[0]);
4905 assert(j < m_dim[1]);
4906 assert(k < m_dim[2]);
4907 return m_data[k + j * m_dim[2] + i * m_dim[1] * m_dim[2]];
4910 const VoxelValue& Volume::GetVoxel(
const size_t i,
const size_t j,
const size_t k)
const
4912 assert(i < m_dim[0]);
4913 assert(j < m_dim[1]);
4914 assert(k < m_dim[2]);
4915 return m_data[k + j * m_dim[2] + i * m_dim[1] * m_dim[2]];
4918 const std::vector<Voxel>& Volume::GetSurfaceVoxels()
const {
return m_surfaceVoxels; }
4920 const std::vector<Voxel>& Volume::GetInteriorVoxels()
const {
return m_interiorVoxels; }
4922 double Volume::GetScale()
const {
return m_scale; }
4928 void Volume::MarkOutsideSurface(
const size_t i0,
4935 for (
size_t i = i0; i < i1; ++i)
4937 for (
size_t j = j0; j < j1; ++j)
4939 for (
size_t k = k0; k < k1; ++k)
4941 VoxelValue& v = GetVoxel(i, j, k);
4942 if (v == VoxelValue::PRIMITIVE_UNDEFINED)
4944 v = VoxelValue::PRIMITIVE_OUTSIDE_SURFACE_TOWALK;
4951 inline void WalkForward(int64_t start, int64_t end, VoxelValue* ptr, int64_t stride, int64_t maxDistance)
4953 for (int64_t i = start, count = 0; count < maxDistance && i < end && *ptr == VoxelValue::PRIMITIVE_UNDEFINED;
4954 ++i, ptr += stride, ++count)
4956 *ptr = VoxelValue::PRIMITIVE_OUTSIDE_SURFACE_TOWALK;
4960 inline void WalkBackward(int64_t start, int64_t end, VoxelValue* ptr, int64_t stride, int64_t maxDistance)
4962 for (int64_t i = start, count = 0; count < maxDistance && i >= end && *ptr == VoxelValue::PRIMITIVE_UNDEFINED;
4963 --i, ptr -= stride, ++count)
4965 *ptr = VoxelValue::PRIMITIVE_OUTSIDE_SURFACE_TOWALK;
4969 void Volume::FillOutsideSurface()
4971 size_t voxelsWalked = 0;
4972 const int64_t i0 = m_dim[0];
4973 const int64_t j0 = m_dim[1];
4974 const int64_t k0 = m_dim[2];
4980 const size_t walkDistance = 64;
4984 const size_t istride = &GetVoxel(1, 0, 0) - &GetVoxel(0, 0, 0);
4985 const size_t jstride = &GetVoxel(0, 1, 0) - &GetVoxel(0, 0, 0);
4986 const size_t kstride = &GetVoxel(0, 0, 1) - &GetVoxel(0, 0, 0);
4997 for (int64_t i = 0; i < i0; ++i)
4999 for (int64_t j = 0; j < j0; ++j)
5001 for (int64_t k = 0; k < k0; ++k)
5003 VoxelValue& voxel = GetVoxel(i, j, k);
5004 if (voxel == VoxelValue::PRIMITIVE_OUTSIDE_SURFACE_TOWALK)
5007 voxel = VoxelValue::PRIMITIVE_OUTSIDE_SURFACE;
5012 WalkForward(k + 1, k0, &voxel + kstride, kstride, walkDistance);
5013 WalkBackward(k - 1, 0, &voxel - kstride, kstride, walkDistance);
5015 WalkForward(j + 1, j0, &voxel + jstride, jstride, walkDistance);
5016 WalkBackward(j - 1, 0, &voxel - jstride, jstride, walkDistance);
5018 WalkForward(i + 1, i0, &voxel + istride, istride, walkDistance);
5019 WalkBackward(i - 1, 0, &voxel - istride, istride, walkDistance);
5025 m_numVoxelsOutsideSurface += voxelsWalked;
5026 }
while (voxelsWalked != 0);
5029 void Volume::FillInsideSurface()
5031 const uint32_t i0 = uint32_t(m_dim[0]);
5032 const uint32_t j0 = uint32_t(m_dim[1]);
5033 const uint32_t k0 = uint32_t(m_dim[2]);
5035 size_t maxSize = i0 * j0 * k0;
5037 std::vector<Voxel> temp;
5038 temp.reserve(maxSize);
5039 uint32_t count{ 0 };
5041 for (uint32_t i = 0; i < i0; ++i)
5043 for (uint32_t j = 0; j < j0; ++j)
5045 for (uint32_t k = 0; k < k0; ++k)
5047 VoxelValue& v = GetVoxel(i, j, k);
5048 if (v == VoxelValue::PRIMITIVE_UNDEFINED)
5050 v = VoxelValue::PRIMITIVE_INSIDE_SURFACE;
5051 temp.emplace_back(i, j, k);
5053 ++m_numVoxelsInsideSurface;
5061 m_interiorVoxels = std::move(temp);
5098 uint32_t ComputeConvexHull(
const std::vector<VHACD::Vertex>& vertices, uint32_t maxHullVertices);
5100 const std::vector<VHACD::Vertex>& GetVertices()
const;
5101 const std::vector<VHACD::Triangle>& GetIndices()
const;
5104 std::vector<VHACD::Vertex> m_vertices;
5105 std::vector<VHACD::Triangle> m_indices;
5108 uint32_t QuickHull::ComputeConvexHull(
const std::vector<VHACD::Vertex>& vertices, uint32_t maxHullVertices)
5112 VHACD::ConvexHull ch(vertices,
double(0.0001), maxHullVertices);
5114 auto& vlist = ch.GetVertexPool();
5117 size_t vcount = vlist.size();
5118 m_vertices.resize(vcount);
5119 std::copy(vlist.begin(), vlist.end(), m_vertices.begin());
5122 for (std::list<ConvexHullFace>::const_iterator node = ch.GetList().begin(); node != ch.GetList().end(); ++node)
5124 const VHACD::ConvexHullFace& face = *node;
5125 m_indices.emplace_back(face.m_index[0], face.m_index[1], face.m_index[2]);
5128 return uint32_t(m_indices.size());
5131 const std::vector<VHACD::Vertex>& QuickHull::GetVertices()
const {
return m_vertices; }
5133 const std::vector<VHACD::Triangle>& QuickHull::GetIndices()
const {
return m_indices; }
5139 void ShrinkWrap(SimpleMesh& sourceConvexHull,
5140 const AABBTree& aabbTree,
5141 uint32_t maxHullVertexCount,
5142 double distanceThreshold,
5145 std::vector<VHACD::Vertex> verts;
5146 verts.reserve(sourceConvexHull.m_vertices.size());
5149 for (uint32_t j = 0; j < sourceConvexHull.m_vertices.size(); j++)
5155 if (aabbTree.GetClosestPointWithinDistance(p, distanceThreshold, closest))
5160 verts.emplace_back(p);
5163 VHACD::QuickHull qh;
5164 uint32_t tcount = qh.ComputeConvexHull(verts, maxHullVertexCount);
5167 sourceConvexHull.m_vertices = qh.GetVertices();
5168 sourceConvexHull.m_indices = qh.GetIndices();
5174 #if !VHACD_DISABLE_THREADING
5184 ThreadPool(
int worker);
5186 template <
typename F,
typename... Args>
5187 auto enqueue(F&& f, Args&&... args)
5188 #ifndef __cpp_lib_is_invocable
5189 -> std::future<
typename std::result_of<F(Args...)>::type>;
5191 -> std::future<
typename std::invoke_result_t<F, Args...>>;
5194 std::vector<std::thread> workers;
5195 std::deque<std::function<void()>> tasks;
5196 std::mutex task_mutex;
5197 std::condition_variable cv;
5202 ThreadPool::ThreadPool() : ThreadPool(1) {}
5204 ThreadPool::ThreadPool(
int worker) : closed(false), count(0)
5206 workers.reserve(worker);
5207 for (
int i = 0; i < worker; i++)
5209 workers.emplace_back([
this] {
5210 std::unique_lock<std::mutex> lock(this->task_mutex);
5213 while (this->tasks.empty())
5219 this->cv.wait(lock);
5221 auto task = this->tasks.front();
5222 this->tasks.pop_front();
5231 template <
typename F,
typename... Args>
5232 auto ThreadPool::enqueue(F&& f, Args&&... args)
5233 #ifndef __cpp_lib_is_invocable
5234 -> std::future<
typename std::result_of<F(Args...)>::type>
5236 -> std::future<
typename std::invoke_result_t<F, Args...>>
5239 #ifndef __cpp_lib_is_invocable
5240 using return_type =
typename std::result_of<F(Args...)>::type;
5242 using return_type =
typename std::invoke_result_t<F, Args...>;
5245 std::make_shared<std::packaged_task<return_type()>>(std::bind(std::forward<F>(f), std::forward<Args>(args)...));
5246 auto result = task->get_future();
5249 std::unique_lock<std::mutex> lock(task_mutex);
5252 tasks.emplace_back([task] { (*task)(); });
5260 ThreadPool::~ThreadPool()
5263 std::unique_lock<std::mutex> lock(task_mutex);
5267 for (
auto&& worker : workers)
5276 COMPUTE_BOUNDS_OF_INPUT_MESH,
5277 REINDEXING_INPUT_MESH,
5278 CREATE_RAYCAST_MESH,
5279 VOXELIZING_INPUT_MESH,
5280 BUILD_INITIAL_CONVEX_HULL,
5281 PERFORMING_DECOMPOSITION,
5282 INITIALIZING_CONVEX_HULLS_FOR_MERGING,
5283 COMPUTING_COST_MATRIX,
5284 MERGING_CONVEX_HULLS,
5289 class VHACDCallbacks
5292 virtual void ProgressUpdate(Stages stage,
double stageProgress,
const char* operation) = 0;
5293 virtual bool IsCanceled()
const = 0;
5295 virtual ~VHACDCallbacks() =
default;
5298 enum class SplitAxis
5315 VoxelHull(
const VoxelHull& parent, SplitAxis axis, uint32_t splitLoc);
5319 VoxelHull(Volume& voxels,
const IVHACD::Parameters& params, VHACDCallbacks* callbacks);
5321 ~VoxelHull() =
default;
5324 void MinMaxVoxelRegion(
const Voxel& v);
5326 void BuildRaycastMesh();
5330 void ComputeConvexHull();
5337 GetPoint(
const int32_t x,
const int32_t y,
const int32_t z,
const double scale,
const VHACD::Vect3& bmin)
const;
5354 void BuildVoxelMesh();
5358 void AddVoxelBox(
const Voxel& v);
5377 SplitAxis ComputeSplitPlane(uint32_t& location);
5383 bool FindConcavity(uint32_t idx, uint32_t& splitLoc);
5386 bool FindConcavityX(uint32_t& splitLoc);
5389 bool FindConcavityY(uint32_t& splitLoc);
5392 bool FindConcavityZ(uint32_t& splitLoc);
5396 void PerformPlaneSplit();
5400 void SaveVoxelMesh(
const SimpleMesh& inputMesh,
bool saveVoxelMesh,
bool saveSourceMesh);
5402 void SaveOBJ(
const char* fname,
const VoxelHull* h);
5404 void SaveOBJ(
const char* fname);
5407 void WriteOBJ(FILE* fph,
5408 const std::vector<VHACD::Vertex>& vertices,
5409 const std::vector<VHACD::Triangle>& indices,
5410 uint32_t baseIndex);
5413 SplitAxis m_axis{ SplitAxis::X_AXIS_NEGATIVE };
5414 Volume* m_voxels{
nullptr };
5415 double m_voxelScale{ 0 };
5416 double m_voxelScaleHalf{ 0 };
5419 uint32_t m_depth{ 0 };
5420 uint32_t m_index{ 0 };
5421 double m_volumeError{ 0 };
5422 double m_voxelVolume{ 0 };
5423 double m_hullVolume{ 0 };
5425 std::unique_ptr<IVHACD::ConvexHull> m_convexHull{
nullptr };
5426 std::vector<Voxel> m_surfaceVoxels;
5427 std::vector<Voxel> m_newSurfaceVoxels;
5428 std::vector<Voxel> m_interiorVoxels;
5430 std::unique_ptr<VoxelHull> m_hullA{
nullptr };
5431 std::unique_ptr<VoxelHull> m_hullB{
nullptr };
5437 AABBTree m_AABBTree;
5438 std::unordered_map<uint32_t, uint32_t> m_voxelIndexMap;
5440 std::vector<VHACD::Vertex> m_vertices;
5441 std::vector<VHACD::Triangle> m_indices;
5442 static uint32_t m_voxelHullCount;
5443 IVHACD::Parameters m_params;
5444 VHACDCallbacks* m_callbacks{
nullptr };
5447 uint32_t VoxelHull::m_voxelHullCount = 0;
5449 VoxelHull::VoxelHull(
const VoxelHull& parent, SplitAxis axis, uint32_t splitLoc)
5451 , m_voxels(parent.m_voxels)
5452 , m_voxelScale(m_voxels->GetScale())
5453 , m_voxelScaleHalf(m_voxelScale * double(0.5))
5454 , m_voxelBounds(m_voxels->GetBounds())
5455 , m_voxelAdjust(m_voxelBounds.GetMin() - m_voxelScaleHalf)
5456 , m_depth(parent.m_depth + 1)
5457 , m_index(++m_voxelHullCount)
5460 , m_params(parent.m_params)
5466 case SplitAxis::X_AXIS_NEGATIVE:
5467 m_2.GetX() = splitLoc;
5469 case SplitAxis::X_AXIS_POSITIVE:
5470 m_1.GetX() = splitLoc + 1;
5472 case SplitAxis::Y_AXIS_NEGATIVE:
5473 m_2.GetY() = splitLoc;
5475 case SplitAxis::Y_AXIS_POSITIVE:
5476 m_1.GetY() = splitLoc + 1;
5478 case SplitAxis::Z_AXIS_NEGATIVE:
5479 m_2.GetZ() = splitLoc;
5481 case SplitAxis::Z_AXIS_POSITIVE:
5482 m_1.GetZ() = splitLoc + 1;
5488 for (
auto& i : parent.m_interiorVoxels)
5493 bool newSurface =
false;
5496 case SplitAxis::X_AXIS_NEGATIVE:
5497 if (v.
GetX() == splitLoc)
5502 case SplitAxis::X_AXIS_POSITIVE:
5503 if (v.
GetX() == m_1.GetX())
5508 case SplitAxis::Y_AXIS_NEGATIVE:
5509 if (v.
GetY() == splitLoc)
5514 case SplitAxis::Y_AXIS_POSITIVE:
5515 if (v.
GetY() == m_1.GetY())
5520 case SplitAxis::Z_AXIS_NEGATIVE:
5521 if (v.
GetZ() == splitLoc)
5526 case SplitAxis::Z_AXIS_POSITIVE:
5527 if (v.
GetZ() == m_1.GetZ())
5537 m_newSurfaceVoxels.push_back(i);
5541 m_interiorVoxels.push_back(i);
5546 for (
auto& i : parent.m_surfaceVoxels)
5551 m_surfaceVoxels.push_back(i);
5555 for (
auto& i : parent.m_newSurfaceVoxels)
5560 m_newSurfaceVoxels.push_back(i);
5567 for (
auto& i : m_surfaceVoxels)
5569 MinMaxVoxelRegion(i);
5571 for (
auto& i : m_newSurfaceVoxels)
5573 MinMaxVoxelRegion(i);
5575 for (
auto& i : m_interiorVoxels)
5577 MinMaxVoxelRegion(i);
5582 ComputeConvexHull();
5585 VoxelHull::VoxelHull(Volume& voxels,
const IVHACD::Parameters& params, VHACDCallbacks* callbacks)
5587 , m_voxelScale(m_voxels->GetScale())
5588 , m_voxelScaleHalf(m_voxelScale * double(0.5))
5589 , m_voxelBounds(m_voxels->GetBounds())
5590 , m_voxelAdjust(m_voxelBounds.GetMin() - m_voxelScaleHalf)
5591 , m_index(++m_voxelHullCount)
5593 , m_surfaceVoxels(m_voxels->GetSurfaceVoxels())
5595 , m_interiorVoxels(m_voxels->GetInteriorVoxels())
5596 , m_2(m_voxels->GetDimensions() - 1)
5598 , m_callbacks(callbacks)
5602 ComputeConvexHull();
5605 void VoxelHull::MinMaxVoxelRegion(
const Voxel& v)
5608 m_1 = m_1.CWiseMin(x);
5609 m_2 = m_2.CWiseMax(x);
5612 void VoxelHull::BuildRaycastMesh()
5615 if (!m_indices.empty())
5617 m_AABBTree = AABBTree(m_vertices, m_indices);
5621 void VoxelHull::ComputeConvexHull()
5623 if (!m_vertices.empty())
5626 VHACD::QuickHull qh;
5627 uint32_t tcount = qh.ComputeConvexHull(m_vertices, uint32_t(m_vertices.size()));
5630 m_convexHull = std::unique_ptr<IVHACD::ConvexHull>(
new IVHACD::ConvexHull);
5632 m_convexHull->m_points = qh.GetVertices();
5633 m_convexHull->m_triangles = qh.GetIndices();
5635 VHACD::ComputeCentroid(m_convexHull->m_points, m_convexHull->m_triangles, m_convexHull->m_center);
5636 m_convexHull->m_volume = VHACD::ComputeMeshVolume(m_convexHull->m_points, m_convexHull->m_triangles);
5641 m_hullVolume = m_convexHull->m_volume;
5644 double singleVoxelVolume = m_voxelScale * m_voxelScale * m_voxelScale;
5645 size_t voxelCount = m_interiorVoxels.size() + m_newSurfaceVoxels.size() + m_surfaceVoxels.size();
5646 m_voxelVolume = singleVoxelVolume * double(voxelCount);
5647 double diff = fabs(m_hullVolume - m_voxelVolume);
5648 m_volumeError = (diff * 100) / m_voxelVolume;
5651 bool VoxelHull::IsComplete()
5654 if (m_convexHull ==
nullptr)
5658 else if (m_volumeError < m_params.m_minimumVolumePercentErrorAllowed)
5662 else if (m_depth > m_params.m_maxRecursionDepth)
5670 if (d.
GetX() <= m_params.m_minEdgeLength && d.
GetY() <= m_params.m_minEdgeLength &&
5671 d.
GetZ() <= m_params.m_minEdgeLength)
5691 uint32_t address = (p.
GetX() << 20) | (p.
GetY() << 10) | p.
GetZ();
5692 auto found = m_voxelIndexMap.find(address);
5693 if (found != m_voxelIndexMap.end())
5695 ret = found->second;
5700 ret = uint32_t(m_voxelIndexMap.size());
5701 m_voxelIndexMap[address] = ret;
5702 m_vertices.emplace_back(vertex);
5707 void VoxelHull::BuildVoxelMesh()
5714 for (
auto& i : m_surfaceVoxels)
5718 for (
auto& i : m_newSurfaceVoxels)
5724 void VoxelHull::AddVoxelBox(
const Voxel& v)
5733 std::array<VHACD::Vector3<uint32_t>, 8> box{ { { bmin.GetX(), bmin.GetY(), bmin.GetZ() },
5734 { bmax.GetX(), bmin.GetY(), bmin.GetZ() },
5735 { bmax.GetX(), bmax.GetY(), bmin.GetZ() },
5736 { bmin.GetX(), bmax.GetY(), bmin.GetZ() },
5737 { bmin.GetX(), bmin.GetY(), bmax.GetZ() },
5738 { bmax.GetX(), bmin.GetY(), bmax.GetZ() },
5739 { bmax.GetX(), bmax.GetY(), bmax.GetZ() },
5740 { bmin.GetX(), bmax.GetY(), bmax.GetZ() } } };
5743 AddTri(box, 2, 1, 0);
5744 AddTri(box, 3, 2, 0);
5746 AddTri(box, 7, 2, 3);
5747 AddTri(box, 7, 6, 2);
5749 AddTri(box, 5, 1, 2);
5750 AddTri(box, 5, 2, 6);
5752 AddTri(box, 5, 4, 1);
5753 AddTri(box, 4, 0, 1);
5755 AddTri(box, 4, 6, 7);
5756 AddTri(box, 4, 5, 6);
5758 AddTri(box, 4, 7, 0);
5759 AddTri(box, 7, 3, 0);
5764 AddTriangle(box[i1], box[i2], box[i3]);
5771 uint32_t i1 = GetVertexIndex(p1);
5772 uint32_t i2 = GetVertexIndex(p2);
5773 uint32_t i3 = GetVertexIndex(p3);
5775 m_indices.emplace_back(i1, i2, i3);
5778 SplitAxis VoxelHull::ComputeSplitPlane(uint32_t& location)
5780 SplitAxis ret = SplitAxis::X_AXIS_NEGATIVE;
5786 ret = SplitAxis::X_AXIS_NEGATIVE;
5787 location = (m_2.GetX() + 1 + m_1.GetX()) / 2;
5789 if (m_params.m_findBestPlane && FindConcavityX(edgeLoc))
5796 ret = SplitAxis::Y_AXIS_NEGATIVE;
5797 location = (m_2.GetY() + 1 + m_1.GetY()) / 2;
5799 if (m_params.m_findBestPlane && FindConcavityY(edgeLoc))
5806 ret = SplitAxis::Z_AXIS_NEGATIVE;
5807 location = (m_2.GetZ() + 1 + m_1.GetZ()) / 2;
5809 if (m_params.m_findBestPlane && FindConcavityZ(edgeLoc))
5820 return GetPoint(ip.
GetX(), ip.
GetY(), ip.
GetZ(), m_voxelScale, m_voxelAdjust);
5832 if (m_AABBTree.TraceRay(from, to, outT, faceSign, hitLocation))
5834 ret = (from - hitLocation).GetNorm();
5844 bool VoxelHull::FindConcavity(uint32_t idx, uint32_t& splitLoc)
5848 int32_t d = (m_2[idx] - m_1[idx]) + 1;
5877 assert(0 &&
"findConcavity::idx must be 0, 1, or 2");
5883 std::vector<double> edgeError1 = std::vector<double>(d);
5884 std::vector<double> edgeError2 = std::vector<double>(d);
5887 uint32_t index1 = 0;
5890 for (uint32_t i0 = m_1[idx1]; i0 <= m_2[idx1]; i0++)
5892 double errorTotal = 0;
5896 for (uint32_t i1 = m_1[idx2]; i1 <= m_2[idx2]; i1++)
5922 double e1 = Raycast(p1, p2);
5923 double e2 = Raycast(p2, p1);
5925 errorTotal = errorTotal + e1 + e2;
5928 edgeError1[index1] = errorTotal;
5933 uint32_t index2 = 0;
5935 for (uint32_t i0 = m_1[idx1]; i0 <= m_2[idx1]; i0++)
5937 double errorTotal = 0;
5939 for (uint32_t i1 = m_1[idx3]; i1 <= m_2[idx3]; i1++)
5965 double e1 = Raycast(p1, p2);
5966 double e2 = Raycast(p2, p1);
5968 errorTotal = errorTotal + e1 + e2;
5970 edgeError2[index2] = errorTotal;
5977 for (uint32_t x = 1; x < index1; x++)
5979 if (edgeError1[x] > 0 && edgeError1[x - 1] > 0)
5981 double diff = abs(edgeError1[x] - edgeError1[x - 1]);
5991 for (uint32_t x = 1; x < index2; x++)
5993 if (edgeError2[x] > 0 && edgeError2[x - 1] > 0)
5995 double diff = abs(edgeError2[x] - edgeError2[x - 1]);
6004 splitLoc = maxC + m_1[idx1];
6007 if (splitLoc > (m_1[idx1] + 4) && splitLoc < (m_2[idx1] - 4))
6016 bool VoxelHull::FindConcavityX(uint32_t& splitLoc) {
return FindConcavity(0, splitLoc); }
6019 bool VoxelHull::FindConcavityY(uint32_t& splitLoc) {
return FindConcavity(1, splitLoc); }
6022 bool VoxelHull::FindConcavityZ(uint32_t& splitLoc) {
return FindConcavity(2, splitLoc); }
6024 void VoxelHull::PerformPlaneSplit()
6032 SplitAxis axis = ComputeSplitPlane(splitLoc);
6035 case SplitAxis::X_AXIS_NEGATIVE:
6036 case SplitAxis::X_AXIS_POSITIVE:
6038 m_hullA = std::unique_ptr<VoxelHull>(
new VoxelHull(*
this, SplitAxis::X_AXIS_NEGATIVE, splitLoc));
6039 m_hullB = std::unique_ptr<VoxelHull>(
new VoxelHull(*
this, SplitAxis::X_AXIS_POSITIVE, splitLoc));
6041 case SplitAxis::Y_AXIS_NEGATIVE:
6042 case SplitAxis::Y_AXIS_POSITIVE:
6044 m_hullA = std::unique_ptr<VoxelHull>(
new VoxelHull(*
this, SplitAxis::Y_AXIS_NEGATIVE, splitLoc));
6045 m_hullB = std::unique_ptr<VoxelHull>(
new VoxelHull(*
this, SplitAxis::Y_AXIS_POSITIVE, splitLoc));
6047 case SplitAxis::Z_AXIS_NEGATIVE:
6048 case SplitAxis::Z_AXIS_POSITIVE:
6050 m_hullA = std::unique_ptr<VoxelHull>(
new VoxelHull(*
this, SplitAxis::Z_AXIS_NEGATIVE, splitLoc));
6051 m_hullB = std::unique_ptr<VoxelHull>(
new VoxelHull(*
this, SplitAxis::Z_AXIS_POSITIVE, splitLoc));
6057 void VoxelHull::SaveVoxelMesh(
const SimpleMesh& inputMesh,
bool saveVoxelMesh,
bool saveSourceMesh)
6060 snprintf(scratch,
sizeof(scratch),
"voxel-mesh-%03d.obj", m_index);
6061 FILE* fph = fopen(scratch,
"wb");
6064 uint32_t baseIndex = 1;
6067 WriteOBJ(fph, m_vertices, m_indices, baseIndex);
6068 baseIndex += uint32_t(m_vertices.size());
6072 WriteOBJ(fph, inputMesh.m_vertices, inputMesh.m_indices, baseIndex);
6078 void VoxelHull::SaveOBJ(
const char* fname,
const VoxelHull* h)
6080 FILE* fph = fopen(fname,
"wb");
6083 uint32_t baseIndex = 1;
6084 WriteOBJ(fph, m_vertices, m_indices, baseIndex);
6086 baseIndex += uint32_t(m_vertices.size());
6088 WriteOBJ(fph, h->m_vertices, h->m_indices, baseIndex);
6093 void VoxelHull::SaveOBJ(
const char* fname)
6095 FILE* fph = fopen(fname,
"wb");
6098 printf(
"Saving '%s' with %d vertices and %d triangles\n",
6100 uint32_t(m_vertices.size()),
6101 uint32_t(m_indices.size()));
6102 WriteOBJ(fph, m_vertices, m_indices, 1);
6107 void VoxelHull::WriteOBJ(FILE* fph,
6108 const std::vector<VHACD::Vertex>& vertices,
6109 const std::vector<VHACD::Triangle>& indices,
6117 for (
size_t i = 0; i < vertices.size(); ++i)
6120 fprintf(fph,
"v %0.9f %0.9f %0.9f\n", v.
mX, v.
mY, v.
mZ);
6123 for (
size_t i = 0; i < indices.size(); ++i)
6126 fprintf(fph,
"f %d %d %d\n", t.
mI0 + baseIndex, t.
mI1 + baseIndex, t.
mI2 + baseIndex);
6137 VHACDImpl* m_this{
nullptr };
6138 IVHACD::ConvexHull* m_hullA{
nullptr };
6139 IVHACD::ConvexHull* m_hullB{
nullptr };
6140 double m_concavity{ 0 };
6141 std::future<void> m_future;
6147 HullPair() =
default;
6148 HullPair(uint32_t hullA, uint32_t hullB,
double concavity);
6150 bool operator<(
const HullPair& h)
const;
6152 uint32_t m_hullA{ 0 };
6153 uint32_t m_hullB{ 0 };
6154 double m_concavity{ 0 };
6157 HullPair::HullPair(uint32_t hullA, uint32_t hullB,
double concavity)
6158 : m_hullA(hullA), m_hullB(hullB), m_concavity(concavity)
6162 bool HullPair::operator<(
const HullPair& h)
const {
return m_concavity > h.m_concavity ? true :
false; }
6166 class VHACDImpl :
public IVHACD,
public VHACDCallbacks
6169 static constexpr uint32_t MaxConvexHullFragments{ 100000 };
6172 VHACDImpl() =
default;
6177 ~VHACDImpl()
override { Clean(); }
6179 void Cancel() override final;
6181 bool Compute(const
float* const points,
6182 const uint32_t countPoints,
6183 const uint32_t* const triangles,
6184 const uint32_t countTriangles,
6185 const Parameters& params) override final;
6187 bool Compute(const
double* const points,
6188 const uint32_t countPoints,
6189 const uint32_t* const triangles,
6190 const uint32_t countTriangles,
6191 const Parameters& params) override final;
6193 uint32_t GetNConvexHulls() const override final;
6195 bool GetConvexHull(const uint32_t index, ConvexHull& ch) const override final;
6197 void Clean() override final;
6199 void Release() override final;
6203 bool ComputeCenterOfMass(
double centerOfMass[3]) const override final;
6209 bool IsReady(
void) const override final;
6221 uint32_t findNearestConvexHull(const
double pos[3],
double& distanceToHull) override final;
6224 bool Compute(const std::vector<
VHACD::Vertex>& points,
6225 const std::vector<
VHACD::Triangle>& triangles,
6226 const Parameters& params);
6229 uint32_t GetIndex(
VHACD::VertexIndex& vi, const
VHACD::Vertex& p);
6234 void CopyInputMesh(const std::vector<
VHACD::Vertex>& points, const std::vector<
VHACD::Triangle>& triangles);
6236 void ScaleOutputConvexHull(ConvexHull& ch);
6238 void AddCostToPriorityQueue(CostTask& task);
6240 void ReleaseConvexHull(ConvexHull* ch);
6242 void PerformConvexDecomposition();
6244 double ComputeConvexHullVolume(const ConvexHull& sm);
6248 double ComputeConcavity(
double volumeSeparate,
double volumeCombined,
double volumeMesh);
6254 bool DoFastCost(CostTask& mt);
6256 void PerformMergeCostTask(CostTask& mt);
6258 ConvexHull* ComputeReducedConvexHull(const ConvexHull& ch, uint32_t maxVerts,
bool projectHullVertices);
6264 ConvexHull* ComputeCombinedConvexHull(const ConvexHull& sm1, const ConvexHull& sm2);
6266 ConvexHull* GetHull(uint32_t index);
6268 bool RemoveHull(uint32_t index);
6270 ConvexHull* CopyConvexHull(const ConvexHull& source);
6272 const
char* GetStageName(Stages stage) const;
6277 void ProgressUpdate(Stages stage,
double stageProgress, const
char* operation) override final;
6279 bool IsCanceled() const override final;
6281 std::atomic<
bool> m_canceled{
false };
6282 Parameters m_params;
6284 std::vector<IVHACD::ConvexHull*> m_convexHulls;
6285 std::vector<std::unique_ptr<VoxelHull>> m_voxelHulls;
6286 std::vector<std::unique_ptr<VoxelHull>> m_pendingHulls;
6288 std::vector<std::unique_ptr<AABBTree>> m_trees;
6289 VHACD::AABBTree m_AABBTree;
6290 VHACD::Volume m_voxelize;
6292 double m_scale{ double(1.0) };
6293 double m_recipScale{ double(1.0) };
6294 SimpleMesh m_inputMesh;
6295 std::vector<VHACD::Vertex> m_vertices;
6296 std::vector<VHACD::Triangle> m_indices;
6298 double m_overallHullVolume{ double(0.0) };
6299 double m_voxelScale{ double(0.0) };
6300 double m_voxelHalfScale{ double(0.0) };
6303 uint32_t m_meshId{ 0 };
6304 std::priority_queue<HullPair> m_hullPairQueue;
6305 #if !VHACD_DISABLE_THREADING
6306 std::unique_ptr<ThreadPool> m_threadPool{
nullptr };
6308 std::unordered_map<uint32_t, IVHACD::ConvexHull*> m_hulls;
6310 double m_overallProgress{ double(0.0) };
6311 double m_stageProgress{ double(0.0) };
6312 double m_operationProgress{ double(0.0) };
6315 void VHACDImpl::Cancel() { m_canceled =
true; }
6317 bool VHACDImpl::Compute(
const float*
const points,
6318 const uint32_t countPoints,
6319 const uint32_t*
const triangles,
6320 const uint32_t countTriangles,
6321 const Parameters& params)
6323 std::vector<VHACD::Vertex> v;
6324 v.reserve(countPoints);
6325 for (uint32_t i = 0; i < countPoints; ++i)
6327 v.emplace_back(points[i * 3 + 0], points[i * 3 + 1], points[i * 3 + 2]);
6330 std::vector<VHACD::Triangle> t;
6331 t.reserve(countTriangles);
6332 for (uint32_t i = 0; i < countTriangles; ++i)
6334 t.emplace_back(triangles[i * 3 + 0], triangles[i * 3 + 1], triangles[i * 3 + 2]);
6337 return Compute(v, t, params);
6340 bool VHACDImpl::Compute(
const double*
const points,
6341 const uint32_t countPoints,
6342 const uint32_t*
const triangles,
6343 const uint32_t countTriangles,
6344 const Parameters& params)
6346 std::vector<VHACD::Vertex> v;
6347 v.reserve(countPoints);
6348 for (uint32_t i = 0; i < countPoints; ++i)
6350 v.emplace_back(points[i * 3 + 0], points[i * 3 + 1], points[i * 3 + 2]);
6353 std::vector<VHACD::Triangle> t;
6354 t.reserve(countTriangles);
6355 for (uint32_t i = 0; i < countTriangles; ++i)
6357 t.emplace_back(triangles[i * 3 + 0], triangles[i * 3 + 1], triangles[i * 3 + 2]);
6360 return Compute(v, t, params);
6363 uint32_t VHACDImpl::GetNConvexHulls()
const {
return uint32_t(m_convexHulls.size()); }
6365 bool VHACDImpl::GetConvexHull(
const uint32_t index, ConvexHull& ch)
const
6369 if (index < uint32_t(m_convexHulls.size()))
6371 ch = *m_convexHulls[index];
6378 void VHACDImpl::Clean()
6380 #if !VHACD_DISABLE_THREADING
6381 m_threadPool =
nullptr;
6386 for (
auto& ch : m_convexHulls)
6388 ReleaseConvexHull(ch);
6390 m_convexHulls.clear();
6392 for (
auto& ch : m_hulls)
6394 ReleaseConvexHull(ch.second);
6398 m_voxelHulls.clear();
6400 m_pendingHulls.clear();
6406 void VHACDImpl::Release() {
delete this; }
6408 bool VHACDImpl::ComputeCenterOfMass(
double centerOfMass[3])
const
6415 bool VHACDImpl::IsReady()
const {
return true; }
6417 uint32_t VHACDImpl::findNearestConvexHull(
const double pos[3],
double& distanceToHull)
6421 uint32_t hullCount = GetNConvexHulls();
6427 if (m_trees.empty())
6430 for (uint32_t i = 0; i < hullCount; i++)
6433 GetConvexHull(i, ch);
6439 double closest = 1e99;
6440 for (uint32_t i = 0; i < hullCount; i++)
6442 std::unique_ptr<AABBTree>& t = m_trees[i];
6447 if (t->GetClosestPointWithinDistance(position, 1e99, closestPoint))
6451 if (distanceSquared < closest)
6453 closest = distanceSquared;
6459 distanceToHull = sqrt(closest);
6465 bool VHACDImpl::Compute(
const std::vector<VHACD::Vertex>& points,
6466 const std::vector<VHACD::Triangle>& triangles,
6467 const Parameters& params)
6475 #if !VHACD_DISABLE_THREADING
6476 if (m_params.m_asyncACD)
6478 m_threadPool = std::unique_ptr<ThreadPool>(
new ThreadPool(8));
6481 CopyInputMesh(points, triangles);
6485 PerformConvexDecomposition();
6492 if (m_params.m_logger)
6494 m_params.m_logger->Log(
"VHACD operation canceled before it was complete.");
6501 #if !VHACD_DISABLE_THREADING
6502 m_threadPool =
nullptr;
6507 uint32_t VHACDImpl::GetIndex(VHACD::VertexIndex& vi,
const VHACD::Vertex& p)
6511 uint32_t ret = vi.GetIndex(pos, newPos);
6515 void VHACDImpl::CopyInputMesh(
const std::vector<VHACD::Vertex>& points,
const std::vector<VHACD::Triangle>& triangles)
6519 m_indices.reserve(triangles.size());
6524 ProgressUpdate(Stages::COMPUTE_BOUNDS_OF_INPUT_MESH, 0,
"ComputingBounds");
6525 for (uint32_t i = 0; i < points.size(); i++)
6529 bmin = bmin.CWiseMin(p);
6530 bmax = bmax.CWiseMax(p);
6532 ProgressUpdate(Stages::COMPUTE_BOUNDS_OF_INPUT_MESH, 100,
"ComputingBounds");
6534 m_center = (bmax + bmin) *
double(0.5);
6539 m_recipScale = m_scale > double(0.0) ? double(1.0) / m_scale : double(0.0);
6542 VHACD::VertexIndex vi = VHACD::VertexIndex(
double(0.001),
false);
6544 uint32_t dcount = 0;
6546 for (uint32_t i = 0; i < triangles.size() && !m_canceled; ++i)
6553 uint32_t i1 = GetIndex(vi, p1);
6554 uint32_t i2 = GetIndex(vi, p2);
6555 uint32_t i3 = GetIndex(vi, p3);
6557 if (i1 == i2 || i1 == i3 || i2 == i3)
6563 m_indices.emplace_back(i1, i2, i3);
6569 if (m_params.m_logger)
6572 snprintf(scratch,
sizeof(scratch),
"Skipped %d degenerate triangles", dcount);
6573 m_params.m_logger->Log(scratch);
6577 m_vertices = vi.TakeVertices();
6583 ProgressUpdate(Stages::CREATE_RAYCAST_MESH, 0,
"Building RaycastMesh");
6584 m_AABBTree = VHACD::AABBTree(m_vertices, m_indices);
6585 ProgressUpdate(Stages::CREATE_RAYCAST_MESH, 100,
"RaycastMesh completed");
6589 ProgressUpdate(Stages::VOXELIZING_INPUT_MESH, 0,
"Voxelizing Input Mesh");
6590 m_voxelize = VHACD::Volume();
6591 m_voxelize.Voxelize(m_vertices, m_indices, m_params.m_resolution, m_params.m_fillMode, m_AABBTree);
6592 m_voxelScale = m_voxelize.GetScale();
6593 m_voxelHalfScale = m_voxelScale * double(0.5);
6594 m_voxelBmin = m_voxelize.GetBounds().GetMin();
6595 m_voxelBmax = m_voxelize.GetBounds().GetMax();
6596 ProgressUpdate(Stages::VOXELIZING_INPUT_MESH, 100,
"Voxelization complete");
6599 m_inputMesh.m_vertices = m_vertices;
6600 m_inputMesh.m_indices = m_indices;
6603 ProgressUpdate(Stages::BUILD_INITIAL_CONVEX_HULL, 0,
"Build initial ConvexHull");
6604 std::unique_ptr<VoxelHull> vh = std::unique_ptr<VoxelHull>(
new VoxelHull(m_voxelize, m_params,
this));
6605 if (vh->m_convexHull)
6607 m_overallHullVolume = vh->m_convexHull->m_volume;
6609 m_pendingHulls.push_back(std::move(vh));
6610 ProgressUpdate(Stages::BUILD_INITIAL_CONVEX_HULL, 100,
"Initial ConvexHull complete");
6614 void VHACDImpl::ScaleOutputConvexHull(ConvexHull& ch)
6616 for (uint32_t i = 0; i < ch.m_points.size(); i++)
6619 p = (p * m_scale) + m_center;
6622 ch.m_volume = ComputeConvexHullVolume(ch);
6624 ch.mBmin = b.GetMin();
6625 ch.mBmax = b.GetMax();
6626 ComputeCentroid(ch.m_points, ch.m_triangles, ch.m_center);
6629 void VHACDImpl::AddCostToPriorityQueue(CostTask& task)
6631 HullPair hp(task.m_hullA->m_meshId, task.m_hullB->m_meshId, task.m_concavity);
6632 m_hullPairQueue.push(hp);
6635 void VHACDImpl::ReleaseConvexHull(ConvexHull* ch)
6643 void jobCallback(std::unique_ptr<VoxelHull>& userPtr) { userPtr->PerformPlaneSplit(); }
6645 void computeMergeCostTask(CostTask& ptr) { ptr.m_this->PerformMergeCostTask(ptr); }
6647 void VHACDImpl::PerformConvexDecomposition()
6650 ScopedTime st(
"Convex Decomposition", m_params.m_logger);
6651 double maxHulls = pow(2, m_params.m_maxRecursionDepth);
6656 while (!m_pendingHulls.empty() && !m_canceled)
6658 size_t count = m_pendingHulls.size() + m_voxelHulls.size();
6659 double e = t.PeekElapsedSeconds();
6660 if (e >=
double(0.1))
6663 double stageProgress = (double(count) * double(100.0)) / maxHulls;
6665 Stages::PERFORMING_DECOMPOSITION, stageProgress,
"Performing recursive decomposition of convex hulls");
6668 std::vector<std::unique_ptr<VoxelHull>> oldList = std::move(m_pendingHulls);
6672 std::vector<std::future<void>> futures(oldList.size());
6673 uint32_t futureCount = 0;
6674 for (
auto& i : oldList)
6676 if (i->IsComplete() || count > MaxConvexHullFragments)
6681 #if !VHACD_DISABLE_THREADING
6684 futures[futureCount] = m_threadPool->enqueue([&i] { jobCallback(i); });
6690 i->PerformPlaneSplit();
6697 for (uint32_t i = 0; i < futureCount; i++)
6705 for (
auto& vh : oldList)
6707 if (vh->IsComplete() || count > MaxConvexHullFragments)
6709 if (vh->m_convexHull)
6711 m_voxelHulls.push_back(std::move(vh));
6718 m_pendingHulls.push_back(std::move(vh->m_hullA));
6722 m_pendingHulls.push_back(std::move(vh->m_hullB));
6736 std::vector<ConvexHull*> hulls;
6738 ProgressUpdate(Stages::INITIALIZING_CONVEX_HULLS_FOR_MERGING, 0,
"Initializing ConvexHulls");
6739 for (
auto& vh : m_voxelHulls)
6745 ConvexHull* ch = CopyConvexHull(*vh->m_convexHull);
6747 ch->m_meshId = m_meshId;
6748 m_hulls[m_meshId] = ch;
6750 ch->m_volume = ComputeConvexHullVolume(*ch);
6756 ComputeCentroid(ch->m_points, ch->m_triangles, ch->m_center);
6758 hulls.push_back(ch);
6760 ProgressUpdate(Stages::INITIALIZING_CONVEX_HULLS_FOR_MERGING, 100,
"ConvexHull initialization complete");
6762 m_voxelHulls.clear();
6766 size_t hullCount = hulls.size();
6768 if (hullCount > m_params.m_maxConvexHulls && !m_canceled)
6770 size_t costMatrixSize = ((hullCount * hullCount) - hullCount) >> 1;
6771 std::vector<CostTask> tasks;
6772 tasks.reserve(costMatrixSize);
6774 ScopedTime st(
"Computing the Cost Matrix", m_params.m_logger);
6778 ProgressUpdate(Stages::COMPUTING_COST_MATRIX, 0,
"Computing Hull Merge Cost Matrix");
6779 for (
size_t i = 1; i < hullCount && !m_canceled; i++)
6781 ConvexHull* chA = hulls[i];
6783 for (
size_t j = 0; j < i && !m_canceled; j++)
6785 ConvexHull* chB = hulls[j];
6797 tasks.push_back(std::move(t));
6798 CostTask* task = &tasks.back();
6799 #if !VHACD_DISABLE_THREADING
6802 task->m_future = m_threadPool->enqueue([task] { computeMergeCostTask(*task); });
6811 #if !VHACD_DISABLE_THREADING
6814 for (CostTask& task : tasks)
6816 task.m_future.get();
6819 for (CostTask& task : tasks)
6821 AddCostToPriorityQueue(task);
6827 for (CostTask& task : tasks)
6829 PerformMergeCostTask(task);
6830 AddCostToPriorityQueue(task);
6833 ProgressUpdate(Stages::COMPUTING_COST_MATRIX, 100,
"Finished cost matrix");
6838 ScopedTime stMerging(
"Merging Convex Hulls", m_params.m_logger);
6841 bool cancel =
false;
6843 uint32_t maxMergeCount = uint32_t(m_hulls.size()) - m_params.m_maxConvexHulls;
6844 uint32_t startCount = uint32_t(m_hulls.size());
6846 while (!cancel && m_hulls.size() > m_params.m_maxConvexHulls && !m_hullPairQueue.empty() && !m_canceled)
6848 double e = t.PeekElapsedSeconds();
6849 if (e >=
double(0.1))
6852 uint32_t hullsProcessed = startCount - uint32_t(m_hulls.size());
6853 double stageProgress = double(hullsProcessed * 100) / double(maxMergeCount);
6854 ProgressUpdate(Stages::MERGING_CONVEX_HULLS, stageProgress,
"Merging Convex Hulls");
6857 HullPair hp = m_hullPairQueue.top();
6858 m_hullPairQueue.pop();
6867 ConvexHull* ch1 = GetHull(hp.m_hullA);
6868 ConvexHull* ch2 = GetHull(hp.m_hullB);
6877 ConvexHull* combinedHull = ComputeCombinedConvexHull(*ch1, *ch2);
6879 RemoveHull(hp.m_hullA);
6880 RemoveHull(hp.m_hullB);
6883 combinedHull->m_meshId = m_meshId;
6885 tasks.reserve(m_hulls.size());
6890 for (
auto& i : m_hulls)
6896 ConvexHull* secondHull = i.second;
6898 ct.m_hullA = combinedHull;
6899 ct.m_hullB = secondHull;
6906 tasks.push_back(std::move(ct));
6909 m_hulls[combinedHull->m_meshId] = combinedHull;
6912 #if !VHACD_DISABLE_THREADING
6913 if (m_threadPool && tasks.size() >= 2)
6915 for (CostTask& task : tasks)
6917 task.m_future = m_threadPool->enqueue([&task] { computeMergeCostTask(task); });
6920 for (CostTask& task : tasks)
6922 task.m_future.get();
6928 for (CostTask& task : tasks)
6930 PerformMergeCostTask(task);
6934 for (CostTask& task : tasks)
6936 AddCostToPriorityQueue(task);
6942 ProgressUpdate(Stages::FINALIZING_RESULTS, 0,
"Finalizing results");
6943 for (
auto& i : m_hulls)
6949 ConvexHull* ch = i.second;
6951 if (ch->m_points.size() > m_params.m_maxNumVerticesPerCH || m_params.m_shrinkWrap)
6953 ConvexHull* reduce = ComputeReducedConvexHull(*ch, m_params.m_maxNumVerticesPerCH, m_params.m_shrinkWrap);
6954 ReleaseConvexHull(ch);
6957 ScaleOutputConvexHull(*ch);
6958 ch->m_meshId = m_meshId;
6960 m_convexHulls.push_back(ch);
6964 ProgressUpdate(Stages::FINALIZING_RESULTS, 100,
"Finalized results complete");
6969 ProgressUpdate(Stages::FINALIZING_RESULTS, 0,
"Finalizing results");
6971 for (
auto& ch : hulls)
6974 if (ch->m_points.size() > m_params.m_maxNumVerticesPerCH || m_params.m_shrinkWrap)
6976 ConvexHull* reduce = ComputeReducedConvexHull(*ch, m_params.m_maxNumVerticesPerCH, m_params.m_shrinkWrap);
6977 ReleaseConvexHull(ch);
6980 ScaleOutputConvexHull(*ch);
6981 ch->m_meshId = m_meshId;
6983 m_convexHulls.push_back(ch);
6986 ProgressUpdate(Stages::FINALIZING_RESULTS, 100,
"Finalized results");
6991 double VHACDImpl::ComputeConvexHullVolume(
const ConvexHull& sm)
6993 double totalVolume = 0;
6995 for (uint32_t i = 0; i < sm.m_points.size(); i++)
7000 bary /= double(sm.m_points.size());
7002 for (uint32_t i = 0; i < sm.m_triangles.size(); i++)
7004 uint32_t i1 = sm.m_triangles[i].mI0;
7005 uint32_t i2 = sm.m_triangles[i].mI1;
7006 uint32_t i3 = sm.m_triangles[i].mI2;
7012 totalVolume += ComputeVolume4(ver0, ver1, ver2, bary);
7014 totalVolume = totalVolume / double(6.0);
7025 double dot = ad.
Dot(bcd);
7029 double VHACDImpl::ComputeConcavity(
double volumeSeparate,
double volumeCombined,
double volumeMesh)
7031 return fabs(volumeSeparate - volumeCombined) / volumeMesh;
7034 bool VHACDImpl::DoFastCost(CostTask& mt)
7038 ConvexHull* ch1 = mt.m_hullA;
7039 ConvexHull* ch2 = mt.m_hullB;
7043 if (!ch1b.Intersects(ch2b))
7047 double combinedVolume = b.
Volume();
7048 double concavity = ComputeConcavity(ch1->m_volume + ch2->m_volume, combinedVolume, m_overallHullVolume);
7049 HullPair hp(ch1->m_meshId, ch2->m_meshId, concavity);
7050 m_hullPairQueue.push(hp);
7056 void VHACDImpl::PerformMergeCostTask(CostTask& mt)
7058 ConvexHull* ch1 = mt.m_hullA;
7059 ConvexHull* ch2 = mt.m_hullB;
7061 double volume1 = ch1->m_volume;
7062 double volume2 = ch2->m_volume;
7064 ConvexHull* combined = ComputeCombinedConvexHull(*ch1,
7066 double combinedVolume = ComputeConvexHullVolume(*combined);
7067 mt.m_concavity = ComputeConcavity(volume1 + volume2, combinedVolume, m_overallHullVolume);
7068 ReleaseConvexHull(combined);
7071 IVHACD::ConvexHull* VHACDImpl::ComputeReducedConvexHull(
const ConvexHull& ch,
7073 bool projectHullVertices)
7075 SimpleMesh sourceConvexHull;
7077 sourceConvexHull.m_vertices = ch.m_points;
7078 sourceConvexHull.m_indices = ch.m_triangles;
7080 ShrinkWrap(sourceConvexHull, m_AABBTree, maxVerts, m_voxelScale * 4, projectHullVertices);
7082 ConvexHull* ret =
new ConvexHull;
7084 ret->m_points = sourceConvexHull.m_vertices;
7085 ret->m_triangles = sourceConvexHull.m_indices;
7090 ComputeCentroid(ret->m_points, ret->m_triangles, ret->m_center);
7092 ret->m_volume = ComputeConvexHullVolume(*ret);
7098 IVHACD::ConvexHull* VHACDImpl::ComputeCombinedConvexHull(
const ConvexHull& sm1,
const ConvexHull& sm2)
7100 uint32_t vcount = uint32_t(sm1.m_points.size() + sm2.m_points.size());
7101 std::vector<VHACD::Vertex> vertices(vcount);
7102 auto it = std::copy(sm1.m_points.begin(), sm1.m_points.end(), vertices.begin());
7103 std::copy(sm2.m_points.begin(), sm2.m_points.end(), it);
7105 VHACD::QuickHull qh;
7106 qh.ComputeConvexHull(vertices, vcount);
7108 ConvexHull* ret =
new ConvexHull;
7109 ret->m_points = qh.GetVertices();
7110 ret->m_triangles = qh.GetIndices();
7112 ret->m_volume = ComputeConvexHullVolume(*ret);
7117 ComputeCentroid(ret->m_points, ret->m_triangles, ret->m_center);
7123 IVHACD::ConvexHull* VHACDImpl::GetHull(uint32_t index)
7125 ConvexHull* ret =
nullptr;
7127 auto found = m_hulls.find(index);
7128 if (found != m_hulls.end())
7130 ret = found->second;
7136 bool VHACDImpl::RemoveHull(uint32_t index)
7139 auto found = m_hulls.find(index);
7140 if (found != m_hulls.end())
7143 ReleaseConvexHull(found->second);
7144 m_hulls.erase(found);
7149 IVHACD::ConvexHull* VHACDImpl::CopyConvexHull(
const ConvexHull& source)
7151 ConvexHull* ch =
new ConvexHull;
7157 const char* VHACDImpl::GetStageName(Stages stage)
const
7159 const char* ret =
"unknown";
7162 case Stages::COMPUTE_BOUNDS_OF_INPUT_MESH:
7163 ret =
"COMPUTE_BOUNDS_OF_INPUT_MESH";
7165 case Stages::REINDEXING_INPUT_MESH:
7166 ret =
"REINDEXING_INPUT_MESH";
7168 case Stages::CREATE_RAYCAST_MESH:
7169 ret =
"CREATE_RAYCAST_MESH";
7171 case Stages::VOXELIZING_INPUT_MESH:
7172 ret =
"VOXELIZING_INPUT_MESH";
7174 case Stages::BUILD_INITIAL_CONVEX_HULL:
7175 ret =
"BUILD_INITIAL_CONVEX_HULL";
7177 case Stages::PERFORMING_DECOMPOSITION:
7178 ret =
"PERFORMING_DECOMPOSITION";
7180 case Stages::INITIALIZING_CONVEX_HULLS_FOR_MERGING:
7181 ret =
"INITIALIZING_CONVEX_HULLS_FOR_MERGING";
7183 case Stages::COMPUTING_COST_MATRIX:
7184 ret =
"COMPUTING_COST_MATRIX";
7186 case Stages::MERGING_CONVEX_HULLS:
7187 ret =
"MERGING_CONVEX_HULLS";
7189 case Stages::FINALIZING_RESULTS:
7190 ret =
"FINALIZING_RESULTS";
7192 case Stages::NUM_STAGES:
7200 void VHACDImpl::ProgressUpdate(Stages stage,
double stageProgress,
const char* operation)
7202 if (m_params.m_callback)
7204 double overallProgress = (double(stage) * 100) /
double(Stages::NUM_STAGES);
7205 const char* s = GetStageName(stage);
7206 m_params.m_callback->Update(overallProgress, stageProgress, s, operation);
7210 bool VHACDImpl::IsCanceled()
const {
return m_canceled; }
7214 VHACDImpl* ret =
new VHACDImpl;
7215 return static_cast<IVHACD*
>(ret);
7220 #if !VHACD_DISABLE_THREADING
7225 double m_overallProgress{ double(-1.0) };
7226 double m_stageProgress{ double(-1.0) };
7227 std::string m_stage;
7228 std::string m_operation;
7237 VHACDAsyncImpl() =
default;
7239 ~VHACDAsyncImpl()
override;
7241 void Cancel() override final;
7243 bool Compute(const
float* const points,
7244 const uint32_t countPoints,
7245 const uint32_t* const triangles,
7246 const uint32_t countTriangles,
7247 const Parameters& params) override final;
7249 bool Compute(const
double* const points,
7250 const uint32_t countPoints,
7251 const uint32_t* const triangles,
7252 const uint32_t countTriangles,
7253 const Parameters& params) override final;
7255 bool GetConvexHull(const uint32_t index,
VHACD::IVHACD::ConvexHull& ch) const override final;
7257 uint32_t GetNConvexHulls() const override final;
7259 void Clean() override final;
7261 void Release() override final;
7265 bool ComputeCenterOfMass(
double centerOfMass[3]) const override;
7267 bool IsReady() const override final;
7279 uint32_t findNearestConvexHull(const
double pos[3],
double& distanceToHull) override final;
7281 void Update(const
double overallProgress,
7282 const
double stageProgress,
7283 const
char* const stage,
7284 const
char* operation) override final;
7286 void Log(const
char* const msg) override final;
7288 void* StartTask(std::function<
void()> func) override;
7290 void JoinTask(
void* Task) override;
7292 bool Compute(const Parameters params);
7294 bool ComputeNow(const std::vector<
VHACD::Vertex>& points,
7295 const std::vector<
VHACD::Triangle>& triangles,
7296 const Parameters& _desc);
7301 void ProcessPendingMessages() const;
7304 VHACD::VHACDImpl m_VHACD;
7305 std::vector<
VHACD::Vertex> m_vertices;
7306 std::vector<
VHACD::Triangle> m_indices;
7307 VHACD::IVHACD::IUserCallback* m_callback{
nullptr };
7310 void* m_task{
nullptr };
7311 std::atomic<bool> m_running{
false };
7312 std::atomic<bool> m_cancel{
false };
7318 mutable std::mutex m_messageMutex;
7319 mutable std::vector<LogMessage> m_messages;
7320 mutable std::atomic<bool> m_haveMessages{
false };
7323 VHACDAsyncImpl::~VHACDAsyncImpl() { Cancel(); }
7325 void VHACDAsyncImpl::Cancel()
7332 m_taskRunner->JoinTask(m_task);
7338 bool VHACDAsyncImpl::Compute(
const float*
const points,
7339 const uint32_t countPoints,
7340 const uint32_t*
const triangles,
7341 const uint32_t countTriangles,
7342 const Parameters& params)
7344 m_vertices.reserve(countPoints);
7345 for (uint32_t i = 0; i < countPoints; ++i)
7347 m_vertices.emplace_back(points[i * 3 + 0], points[i * 3 + 1], points[i * 3 + 2]);
7350 m_indices.reserve(countTriangles);
7351 for (uint32_t i = 0; i < countTriangles; ++i)
7353 m_indices.emplace_back(triangles[i * 3 + 0], triangles[i * 3 + 1], triangles[i * 3 + 2]);
7356 return Compute(params);
7359 bool VHACDAsyncImpl::Compute(
const double*
const points,
7360 const uint32_t countPoints,
7361 const uint32_t*
const triangles,
7362 const uint32_t countTriangles,
7363 const Parameters& params)
7368 m_vertices.reserve(countPoints);
7369 for (uint32_t i = 0; i < countPoints; ++i)
7371 m_vertices.emplace_back(points[i * 3 + 0], points[i * 3 + 1], points[i * 3 + 2]);
7374 m_indices.reserve(countTriangles);
7375 for (uint32_t i = 0; i < countTriangles; ++i)
7377 m_indices.emplace_back(triangles[i * 3 + 0], triangles[i * 3 + 1], triangles[i * 3 + 2]);
7380 return Compute(params);
7385 return m_VHACD.GetConvexHull(index, ch);
7388 uint32_t VHACDAsyncImpl::GetNConvexHulls()
const
7390 ProcessPendingMessages();
7391 return m_VHACD.GetNConvexHulls();
7394 void VHACDAsyncImpl::Clean()
7400 void VHACDAsyncImpl::Release() {
delete this; }
7402 bool VHACDAsyncImpl::ComputeCenterOfMass(
double centerOfMass[3])
const
7406 centerOfMass[0] = 0;
7407 centerOfMass[1] = 0;
7408 centerOfMass[2] = 0;
7412 ret = m_VHACD.ComputeCenterOfMass(centerOfMass);
7417 bool VHACDAsyncImpl::IsReady()
const
7419 ProcessPendingMessages();
7423 uint32_t VHACDAsyncImpl::findNearestConvexHull(
const double pos[3],
double& distanceToHull)
7431 ret = m_VHACD.findNearestConvexHull(pos, distanceToHull);
7437 void VHACDAsyncImpl::Update(
const double overallProgress,
7438 const double stageProgress,
7439 const char*
const stage,
7440 const char* operation)
7442 m_messageMutex.lock();
7444 m.m_operation = std::string(operation);
7445 m.m_overallProgress = overallProgress;
7446 m.m_stageProgress = stageProgress;
7447 m.m_stage = std::string(stage);
7448 m_messages.push_back(m);
7449 m_haveMessages =
true;
7450 m_messageMutex.unlock();
7453 void VHACDAsyncImpl::Log(
const char*
const msg)
7455 m_messageMutex.lock();
7457 m.m_operation = std::string(msg);
7458 m_haveMessages =
true;
7459 m_messages.push_back(m);
7460 m_messageMutex.unlock();
7463 void* VHACDAsyncImpl::StartTask(std::function<
void()> func) {
return new std::thread(func); }
7465 void VHACDAsyncImpl::JoinTask(
void* Task)
7467 std::thread* t =
static_cast<std::thread*
>(Task);
7472 bool VHACDAsyncImpl::Compute(Parameters params)
7476 m_taskRunner = params.m_taskRunner ? params.m_taskRunner :
this;
7477 params.m_taskRunner = m_taskRunner;
7480 m_task = m_taskRunner->StartTask([
this, params]() {
7481 ComputeNow(m_vertices, m_indices, params);
7484 if (params.m_callback && !m_cancel)
7486 params.m_callback->NotifyVHACDComplete();
7493 bool VHACDAsyncImpl::ComputeNow(
const std::vector<VHACD::Vertex>& points,
7494 const std::vector<VHACD::Triangle>& triangles,
7495 const Parameters& _desc)
7500 m_callback = _desc.m_callback;
7501 m_logger = _desc.m_logger;
7505 desc.m_callback = _desc.m_callback ? this :
nullptr;
7506 desc.m_logger = _desc.m_logger ? this :
nullptr;
7509 if (desc.m_taskRunner ==
nullptr)
7511 desc.m_taskRunner =
this;
7514 bool ok = m_VHACD.Compute(points, triangles, desc);
7517 ret = m_VHACD.GetNConvexHulls();
7520 return ret ? true :
false;
7523 void VHACDAsyncImpl::ProcessPendingMessages()
const
7531 m_messageMutex.lock();
7532 for (
auto& i : m_messages)
7534 if (i.m_overallProgress == -1)
7538 m_logger->Log(i.m_operation.c_str());
7541 else if (m_callback)
7543 m_callback->Update(i.m_overallProgress, i.m_stageProgress, i.m_stage.c_str(), i.m_operation.c_str());
7547 m_haveMessages =
false;
7548 m_messageMutex.unlock();
7554 VHACDAsyncImpl* m =
new VHACDAsyncImpl;
7555 return static_cast<IVHACD*
>(m);
7562 #pragma warning(pop)
7566 #pragma GCC diagnostic pop
7569 #endif // ENABLE_VHACD_IMPLEMENTATION
7572 TESSERACT_COMMON_IGNORE_WARNINGS_PUSH