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> 29 #include <type_traits> 40 template<
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;
169 template <
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);
203 , mVox(ValueT((grid->voxelSize())[0]))
204 , mHw(ValueT(grid->background()/(grid->voxelSize())[0]))
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())
305 return static_cast<const Derived*
>(
this)->signedDistance(p);
317 std::function<bool(ValueT&, ValueT&, const ValueT&, const ValueT&)> bottomTop =
318 [](ValueT&, ValueT&,
const ValueT&,
const ValueT&) {
return false; };
329 const ValueT offset = ValueT(0.5) * (step - ValueT(1));
331 return step == ValueT(1)
332 ?
static_cast<ValueT
>(
math::Ceil(perturbDown(x)))
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");
347 const ValueT s =
static_cast<ValueT
>(step);
349 return tileCeil(x, s);
359 const ValueT offset = ValueT(0.5) * (step - ValueT(1));
361 return step == ValueT(1)
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");
377 const ValueT s =
static_cast<ValueT
>(step);
379 return tileFloor(x, s);
390 const ValueT& r,
const ValueT& x)
403 const ValueT& r,
const ValueT& x)
418 const ValueT& r,
const ValueT& x,
const ValueT& y)
432 sphereTop(
const ValueT& x0,
const ValueT& y0,
const ValueT& z0,
433 const ValueT& r,
const ValueT& x,
const ValueT& y)
459 reset(xmin, xmax, step);
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;
560 mStepInv = ValueT(1);
567 mYMins.assign(mSize, MAXVALUE);
568 mYMaxs.assign(mSize, MINVALUE);
577 reset(
const ValueT& xmin,
const ValueT& xmax,
const Index& step = 1)
582 mStepInv = ValueT(1)/
static_cast<ValueT
>(mStep);
584 mXStart = tileCeil(xmin, mStep);
585 mXEnd = tileFloor(xmax, mStep);
587 mSize = 1 + indexDistance(mXEnd, mXStart);
589 mYMins.assign(mSize, MAXVALUE);
590 mYMaxs.assign(mSize, MINVALUE);
603 inline ValueT
start()
const {
return mXStart; }
607 inline ValueT
end()
const {
return mXEnd; }
612 inline ValueT
getX(
const Index& i)
const {
return indexToWorld(i); }
617 inline ValueT
getYMin(
const Index& i)
const { assert(i < mSize);
return mYMins[i]; }
622 inline ValueT
getYMax(
const Index& i)
const { assert(i < mSize);
return mYMaxs[i]; }
628 inline ValueT
getYMin(
const ValueT& x)
const {
return mYMins[worldToIndex(x)]; }
634 inline ValueT
getYMax(
const ValueT& x)
const {
return mYMaxs[worldToIndex(x)]; }
642 XYData(ValueT& x, ValueT& ymin, ValueT& ymax,
const Index& i)
const 655 for (
Index i = 0; i < mSize; ++i) {
656 if (mYMins[i] <= mYMaxs[i])
669 assert(mStep == xydata.
step());
671 const ValueT start = xydata.
start(), end = xydata.
end();
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++;
711 mSize = 0; mXStart = ValueT(0); mXEnd = ValueT(0);
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(),
748 indexDistance(
const ValueT& a,
const ValueT& b)
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>();
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)); }
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))
915 Coord ijk(Int32(x), Int32(y), Int32(0));
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))
963 Coord ijk(Int32(x), Int32(y), Int32(0));
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))
1041 Coord ijk(Int32(x), Int32(y), Int32(0));
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);
1183 doTreeTileSizes<PoppedChainT>(sizes);
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;
1397 #endif // OPENVDB_TOOLS_CONVEXVOXELIZER_HAS_BEEN_INCLUDED
int Floor(float x)
Return the floor of x.
Definition: Math.h:848
Coord Abs(const Coord &xyz)
Definition: Coord.h:518
General-purpose arithmetic and comparison routines, most of which accept arbitrary value types (or at...
float Sqrt(float x)
Return the square root of a floating-point value.
Definition: Math.h:761
Index32 Index
Definition: Types.h:54
A list of types (not necessarily unique)
Definition: TypeList.h:577
openvdb::GridBase::Ptr GridPtr
Definition: Utils.h:44
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
Type Pow2(Type x)
Return x2.
Definition: Math.h:548
Definition: Exceptions.h:13
float Round(float x)
Return x rounded to the nearest integer.
Definition: Math.h:819
bool wasInterrupted(T *i, int percent=-1)
Definition: NullInterrupter.h:49
Functions to efficiently merge grids.
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h.in:121
void split(ContainerT &out, const std::string &in, const char delim)
Definition: Name.h:43
#define EPS
Definition: ConvexVoxelizer.h:784
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h.in:218