14#ifndef OPENVDB_TOOLS_CONVEXVOXELIZER_HAS_BEEN_INCLUDED
15#define OPENVDB_TOOLS_CONVEXVOXELIZER_HAS_BEEN_INCLUDED
18#include <openvdb/thread/Threading.h>
24#include <tbb/enumerable_thread_specific.h>
25#include <tbb/parallel_for.h>
26#include <tbb/parallel_reduce.h>
27#include <tbb/blocked_range.h>
40template<
typename VectorType>
54 inline size_t size()
const {
return mVec.size(); };
56 inline void getPos(
size_t n, VectorType& xyz)
const { xyz = mVec[n]; }
60 const std::vector<VectorType>& mVec;
169template <
typename Gr
idType,
typename Derived,
typename InterruptType = util::NullInterrupter>
172 using GridPtr =
typename GridType::Ptr;
174 using TreeT =
typename GridType::TreeType;
175 using RootT =
typename TreeT::RootNodeType;
176 using LeafT =
typename TreeT::LeafNodeType;
178 using NodeChainT =
typename RootT::NodeChainType;
180 using AccessorT =
typename GridType::Accessor;
184 using ValueT =
typename GridType::ValueType;
188 static_assert(std::is_floating_point<ValueT>::value);
205 , mBg(grid->background())
206 , mNegBg(-(grid->background()))
208 , mInterrupter(interrupter)
222 class CacheLastLeafAccessor;
238 static const Index LEAFDIM = LeafT::DIM;
241 if (!invokeTileCanFit(LEAFDIM)) {
247 using ChainT =
typename NodeChainT::PopBack;
252 iterateTile<ChainT>();
256 if (!checkInterrupter())
333 : step *
static_cast<ValueT>(
math::Ceil(perturbDown((x - offset)/step))) + offset;
341 template <
typename T>
345 static_assert(std::is_integral<T>::value,
"Index must be an integral type");
363 : step *
static_cast<ValueT>(
math::Floor(perturbUp((x - offset)/step))) + offset;
371 template <
typename T>
375 static_assert(std::is_integral<T>::value,
"Index must be an integral type");
483 const Index i = worldToIndex(x);
485 if (std::isfinite(ymin) && ymin < mYMins[i])
496 const Index i = worldToIndex(x);
498 if (std::isfinite(ymax) && ymax > mYMaxs[i])
509 if (std::isfinite(y)) {
510 const Index i = worldToIndex(x);
527 const Index i = worldToIndex(x);
539 const Index i = worldToIndex(x);
549 const Index i = worldToIndex(x);
551 mYMins[i] = MAXVALUE;
552 mYMaxs[i] = MINVALUE;
567 mYMins.assign(mSize, MAXVALUE);
568 mYMaxs.assign(mSize, MINVALUE);
587 mSize = 1 + indexDistance(mXEnd, mXStart);
589 mYMins.assign(mSize, MAXVALUE);
590 mYMaxs.assign(mSize, MINVALUE);
655 for (
Index i = 0; i < mSize; ++i) {
656 if (mYMins[i] <= mYMaxs[i])
669 assert(mStep == xydata.
step());
673 const std::vector<ValueT>& ymins = xydata.mYMins;
674 const std::vector<ValueT>& ymaxs = xydata.mYMaxs;
676 if (
start < mXStart) {
677 const Index n = indexDistance(mXStart,
start);
678 mYMins.insert(mYMins.begin(), n, MAXVALUE);
679 mYMaxs.insert(mYMaxs.begin(), n, MINVALUE);
684 const Index m = indexDistance(
end, mXEnd);
685 mYMins.insert(mYMins.end(), m, MAXVALUE);
686 mYMaxs.insert(mYMaxs.end(), m, MINVALUE);
690 mSize = 1 + indexDistance(mXEnd, mXStart);
692 const Index offset =
start < mXStart ? 0 : indexDistance(
start, mXStart);
693 for (
Index i = 0, j = offset; i < ymins.size(); ++i, ++j) {
694 if (mYMins[j] > ymins[i]) { mYMins[j] = ymins[i]; }
695 if (mYMaxs[j] < ymaxs[i]) { mYMaxs[j] = ymaxs[i]; }
706 if (mYMins[i] > mYMaxs[i]) i++;
712 mYMins.clear(); mYMaxs.clear();
718 const Index k = mSize - (j + 1);
719 if (mYMins[k] > mYMaxs[k]) j++;
723 if (i == 0 && j == 0)
727 mXStart +=
ValueT(i * mStep);
728 mXEnd -=
ValueT(j * mStep);
731 mYMins.erase(mYMins.begin(), mYMins.begin() + i);
732 mYMaxs.erase(mYMaxs.begin(), mYMaxs.begin() + i);
736 mYMins.erase(mYMins.end() - j, mYMins.end());
737 mYMaxs.erase(mYMaxs.end() - j, mYMaxs.end());
743 inline static const ValueT
744 MINVALUE = std::numeric_limits<ValueT>::lowest(),
745 MAXVALUE = std::numeric_limits<ValueT>::max();
754 worldToIndex(
const ValueT& x)
const
763 indexToWorld(
const Index i)
const
767 return mXStart + ValueT(i * mStep);
772 ValueT mStepInv, mXStart, mXEnd;
774 std::vector<ValueT> mYMins, mYMaxs;
785 inline static ValueT perturbDown(
const ValueT& x) {
return x - ValueT(
EPS); }
786 inline static ValueT perturbUp(
const ValueT& x) {
return x + ValueT(
EPS); }
790 voxelCeil(
const ValueT& x)
792 return static_cast<ValueT
>(math::Ceil(perturbDown(x)));
796 voxelFloor(
const ValueT& x)
798 return static_cast<ValueT
>(math::Floor(perturbUp(x)));
802 inline void thinIterate()
804 invokeSetXYRangeData();
810 template <
typename ChainT>
811 inline void iterateTile()
813 using NodeT =
typename ChainT::Back;
814 using PoppedChainT =
typename ChainT::PopBack;
816 static const Index DIM = NodeT::DIM;
819 if (invokeTileCanFit(DIM)) {
820 invokeSetXYRangeData(DIM);
822 tileIterateXYZ<NodeT>();
825 if constexpr (!std::is_same_v<PoppedChainT, openvdb::TypeList<>>) {
826 iterateTile<PoppedChainT>();
833 invokeSetXYRangeData();
839 template <
bool LeapFrog = false>
843 if (mXYData.isEmpty())
848 const Index n = mXYData.size();
851 CacheLastLeafAccessor acc(getTree());
852 for (Index i = 0; i < n; ++i) {
853 if (mInterrupter && !(i & ((1 << 7) - 1)) && !checkInterrupter())
856 iterateYZ<LeapFrog>(i, acc);
859 tbb::enumerable_thread_specific<TreeT> pool(getTree());
861 auto kernel = [&](
const tbb::blocked_range<Index>& rng) {
862 TreeT &tree = pool.local();
863 CacheLastLeafAccessor acc(tree);
865 if (!checkInterrupter())
868 for (Index i = rng.begin(); i != rng.end(); ++i) {
869 if constexpr (LeapFrog)
870 iterateNoTilesYZ(i, acc);
872 iterateYZ<false>(i, acc);
876 tbb::parallel_for(tbb::blocked_range<Index>(
Index(0), n,
Index(128)), kernel);
877 using RangeT = tbb::blocked_range<typename tbb::enumerable_thread_specific<TreeT>::iterator>;
881 Op(TreeT &tree) : mDelete(false), mTree(&tree) {}
882 Op(
const Op& other, tbb::split) : mDelete(true), mTree(new TreeT(other.mTree->background())) {}
883 ~Op() {
if (mDelete)
delete mTree; }
884 void operator()(
const RangeT &r) {
for (
auto i=r.begin(); i!=r.end(); ++i) this->
merge(*i);}
885 void join(Op &other) { this->
merge(*(other.mTree)); }
886 void merge(TreeT &tree) { mTree->merge(tree, MERGE_ACTIVE_STATES); }
888 tbb::parallel_reduce(RangeT(pool.begin(), pool.end(), 4), op);
898 template <
bool LeapFrog = false>
900 iterateYZ(
const Index& i, CacheLastLeafAccessor& acc)
904 mXYData.XYData(x, yb, yt, i);
906 if (!std::isfinite(yb) || !std::isfinite(yt))
911 for (ValueT y = voxelCeil(yb); y <= perturbUp(yt); ++y) {
912 if (!bottomTop(zb, zt, x, y))
916 Vec3T p(x, y, ValueT(0));
918 ijk[2] =
Int32(voxelCeil(zb))-1;
921 for (ValueT z = voxelCeil(zb); z <= perturbUp(zt); ++z) {
923 const ValueT val = acc.template getValue<1>(ijk);
926 if constexpr (LeapFrog) acc.template leapUp<false>(ijk, z);
931 const ValueT dist = mVox * invokeSignedDistance(p);
933 if (dist <= mNegBg) {
934 acc.template setValueOff<1,false>(ijk, mNegBg);
935 }
else if (dist < val) {
936 acc.template setValueOn<1,false>(ijk, dist);
938 acc.template checkReset<1>(ijk);
948 iterateNoTilesYZ(
const Index& i, CacheLastLeafAccessor& acc)
952 mXYData.XYData(x, yb, yt, i);
954 if (!std::isfinite(yb) || !std::isfinite(yt))
959 for (ValueT y = voxelCeil(yb); y <= perturbUp(yt); ++y) {
960 if (!bottomTop(zb, zt, x, y))
964 Vec3T p(x, y, ValueT(0));
966 bool early_break =
false;
969 ijk[2] =
Int32(voxelCeil(zb))-1;
971 for (ValueT z = voxelCeil(zb); z <= perturbUp(zt); ++z) {
974 const ValueT dist = mVox * invokeSignedDistance(p);
976 if (dist <= mNegBg) {
980 }
else if (dist < mBg) {
981 acc.template setValueOn<1>(ijk, dist);
983 acc.template checkReset<1>(ijk);
987 ijk[2] =
Int32(voxelFloor(zt))+1;
989 for (ValueT z = voxelFloor(zt); z > z_stop; --z) {
992 const ValueT dist = mVox * invokeSignedDistance(p);
994 if (dist <= mNegBg) {
996 }
else if (dist < mBg) {
997 acc.template setValueOn<-1>(ijk, dist);
999 acc.template checkReset<-1>(ijk);
1006 template <
typename NodeT>
1010 if (mXYData.isEmpty())
1013 AccessorT acc(getTree());
1014 for (Index i = 0; i < mXYData.size(); ++i) {
1015 if (mInterrupter && !(i & ((1 << 7) - 1)) && !checkInterrupter())
1018 tileIterateYZ<NodeT>(i, acc);
1022 template <
typename NodeT>
1024 tileIterateYZ(
const Index& i, AccessorT& acc)
1028 mXYData.XYData(x, yb, yt, i);
1030 if (!std::isfinite(yb) || !std::isfinite(yt))
1033 static const Index TILESIZE = NodeT::DIM;
1037 for (ValueT y = tileCeil(yb, TILESIZE); y <= perturbUp(yt); y += TILESIZE) {
1038 if (!bottomTop(zb, zt, x, y))
1042 Vec3T p(x, y, ValueT(0));
1044 bool tiles_added =
false;
1045 ValueT z = tileCeil(zb, TILESIZE) - 2*TILESIZE;
1046 while (z <= tileFloor(zt, TILESIZE) + TILESIZE) {
1050 if (leapFrogToNextTile<NodeT, 1>(ijk, z, acc))
1053 if (addTile<NodeT>(p, ijk, acc)) tiles_added =
true;
1054 else if (tiles_added)
break;
1059 template <
typename NodeT,
int dir>
1061 leapFrogToNextTile(
const Coord& ijk, ValueT& z, AccessorT& acc)
const
1063 static const int offset = NodeT::DIM;
1064 static const int nodeDepth = int(TreeT::DEPTH - NodeT::LEVEL - 1);
1067 if (acc.getValue(ijk) != mNegBg) {
1072 const int depth = acc.getValueDepth(ijk);
1075 if (depth >= nodeDepth) {
1080 const ValueT sz = ValueT(mTileSizes[depth]);
1083 ? sz * ValueT(math::Ceil(z/sz)) + ValueT(0.5)*(offset-1)
1084 : sz * ValueT(math::
Floor(z/sz)) - ValueT(0.5)*(offset+1);
1090 template<
typename NodeT>
1092 addTile(
const Vec3T& p,
const Coord& ijk, AccessorT& acc)
1094 static const Index LEVEL = NodeT::LEVEL + 1;
1096 if (tileFits<NodeT>(p)) {
1097 acc.addTile(LEVEL, ijk, mNegBg,
false);
1104 template <
typename NodeT>
1106 tileFits(
const Vec3T& p)
const
1108 static const Index TILESIZE = NodeT::DIM;
1110 static const ValueT R1 = ValueT(0.500)*(TILESIZE-1),
1111 R2 = ValueT(0.866)*(TILESIZE-1);
1113 const ValueT dist = invokeTilePointSignedDistance(p);
1116 if (dist <= -R2-mHw)
1120 else if (dist >= -R1-mHw)
1124 return invokeTilePointSignedDistance(p + Vec3T(-R1, -R1, -R1)) < -mHw
1125 && invokeTilePointSignedDistance(p + Vec3T(-R1, -R1, R1)) < -mHw
1126 && invokeTilePointSignedDistance(p + Vec3T(-R1, R1, -R1)) < -mHw
1127 && invokeTilePointSignedDistance(p + Vec3T(-R1, R1, R1)) < -mHw
1128 && invokeTilePointSignedDistance(p + Vec3T(R1, -R1, -R1)) < -mHw
1129 && invokeTilePointSignedDistance(p + Vec3T(R1, -R1, R1)) < -mHw
1130 && invokeTilePointSignedDistance(p + Vec3T(R1, R1, -R1)) < -mHw
1131 && invokeTilePointSignedDistance(p + Vec3T(R1, R1, R1)) < -mHw;
1135 invokeSetXYRangeData(
const Index& step = 1)
1138 static_cast<Derived*
>(
this)->setXYRangeData(step);
1142 invokeTileCanFit(
const Index& dim)
const
1144 return static_cast<const Derived*
>(
this)->tileCanFit(dim);
1148 invokeSignedDistance(
const Vec3T& p)
const
1150 return static_cast<const Derived*
>(
this)->signedDistance(p);
1154 invokeTilePointSignedDistance(
const Vec3T& p)
const
1156 return static_cast<const Derived*
>(
this)->tilePointSignedDistance(p);
1161 static inline std::vector<int>
1165 using ChainT =
typename NodeChainT::PopBack;
1167 std::vector<int> sizes;
1168 doTreeTileSizes<ChainT>(sizes);
1173 template <
typename ChainT>
1175 doTreeTileSizes(std::vector<int>& sizes)
1177 using NodeT =
typename ChainT::Back;
1178 using PoppedChainT =
typename ChainT::PopBack;
1180 sizes.push_back(NodeT::DIM);
1182 if constexpr (!std::is_same_v<PoppedChainT, openvdb::TypeList<>>) {
1183 doTreeTileSizes<PoppedChainT>(sizes);
1190 if (util::wasInterrupted(mInterrupter)) {
1191 openvdb::thread::cancelGroupExecution();
1197 inline TreeT& getTree() {
return mGrid->tree(); }
1205 class CacheLastLeafAccessor
1207 using NodeMaskType = util::NodeMask<LeafT::LOG2DIM>;
1214 CacheLastLeafAccessor(TreeT& tree)
1215 : mAcc(tree), mTileSizes(treeTileSizes())
1221 inline void reset(
const Coord& ijk) { cacheNewLeafData(ijk); }
1227 template<
int ZDir = 0>
1228 inline void checkReset(
const Coord& ijk)
1230 if (atNewLeafPos<ZDir>(ijk))
1231 cacheNewLeafData(ijk);
1240 template<
int ZDir = 0,
bool Check = true>
1241 inline ValueT getValue(
const Coord& ijk)
1243 if (Check && atNewLeafPos<ZDir>(ijk))
1244 cacheNewLeafData(ijk);
1247 ? mLeafData[coordToOffset(ijk)]
1248 : mAcc.getValue(ijk);
1257 template<
int ZDir = 0,
bool Check = true>
1258 inline void setValueOn(
const Coord& ijk,
const ValueT& val)
1260 if (Check && atNewLeafPos<ZDir>(ijk))
1261 cacheNewLeafData(ijk);
1264 const Index n = coordToOffset(ijk);
1266 mValueMask->setOn(n);
1268 mAcc.setValueOn(ijk, val);
1269 cacheNewLeafData(ijk);
1279 template<
int ZDir = 0,
bool Check = true>
1280 inline void setValueOff(
const Coord& ijk,
const ValueT& val)
1282 if (Check && atNewLeafPos<ZDir>(ijk))
1283 cacheNewLeafData(ijk);
1286 const Index n = coordToOffset(ijk);
1288 mValueMask->setOff(n);
1290 mAcc.setValueOff(ijk, val);
1291 cacheNewLeafData(ijk);
1301 template<
int ZDir = 0,
bool Check = true>
1302 inline bool isVoxel(
const Coord& ijk)
1304 return Check && atNewLeafPos<ZDir>(ijk) ? mLeaf != nullptr : mAcc.isVoxel(ijk);
1312 template<
bool Check = true>
1313 inline void leapUp(
const Coord& ijk, ValueT& z)
1315 if (isVoxel<1,Check>(ijk))
1318 const int depth = mAcc.getValueDepth(ijk);
1319 const ValueT sz = ValueT(mTileSizes[depth]);
1321 z = sz * ValueT(math::Ceil((z+ValueT(1))/sz)) - ValueT(1);
1328 LOG2DIM = LeafT::LOG2DIM;
1331 bool atNewLeafPos(
const Coord& ijk)
const
1333 if constexpr (ZDir == -1) {
1335 return (ijk[2] & (DIM-1u)) == DIM-1u;
1336 }
else if constexpr (ZDir == 1) {
1338 return (ijk[2] & (DIM-1u)) == 0;
1340 return Coord::lessThan(ijk, mOrigin)
1341 || Coord::lessThan(mOrigin.offsetBy(DIM-1u), ijk);
1345 inline void cacheNewLeafData(
const Coord& ijk)
1347 mLeaf = mAcc.probeLeaf(ijk);
1348 mOrigin = ijk & (~(DIM - 1));
1351 mLeafData = mLeaf->buffer().data();
1352 mValueMask = &(mLeaf->getValueMask());
1354 mLeafData =
nullptr;
1358 inline static Index coordToOffset(
const Coord& ijk)
1360 return ((ijk[0] & (DIM-1u)) << 2*LOG2DIM)
1361 + ((ijk[1] & (DIM-1u)) << LOG2DIM)
1362 + (ijk[2] & (DIM-1u));
1369 NodeMaskType* mValueMask;
1372 const std::vector<int> mTileSizes;
1380 const std::vector<int> mTileSizes = treeTileSizes();
1382 const ValueT mVox, mHw, mBg, mNegBg;
1388 InterruptType* mInterrupter;
#define EPS
Definition ConvexVoxelizer.h:784
General-purpose arithmetic and comparison routines, most of which accept arbitrary value types (or at...
Functions to efficiently merge grids.
Propagate the signs of distance values from the active voxels in the narrow band to the inactive valu...
int Ceil(float x)
Return the ceiling of x.
Definition Math.h:856
float Sqrt(float x)
Return the square root of a floating-point value.
Definition Math.h:761
Coord Abs(const Coord &xyz)
Definition Coord.h:518
float Round(float x)
Return x rounded to the nearest integer.
Definition Math.h:819
Type Pow2(Type x)
Return x2.
Definition Math.h:548
int Floor(float x)
Return the floor of x.
Definition Math.h:848
Index32 Index
Definition Types.h:54
int32_t Int32
Definition Types.h:56
openvdb::GridBase::Ptr GridPtr
Definition Utils.h:44
Definition Exceptions.h:13
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition version.h.in:121
#define OPENVDB_USE_VERSION_NAMESPACE
Definition version.h.in:218