62 #ifndef OPENVDB_TOOLS_PARTICLES_TO_LEVELSET_HAS_BEEN_INCLUDED
63 #define OPENVDB_TOOLS_PARTICLES_TO_LEVELSET_HAS_BEEN_INCLUDED
65 #include <tbb/parallel_reduce.h>
66 #include <tbb/blocked_range.h>
80 #include <type_traits>
93 template<
typename Gr
idT,
typename ParticleListT,
typename InterrupterT = util::NullInterrupter>
94 inline void particlesToSdf(
const ParticleListT&, GridT&, InterrupterT* =
nullptr);
100 template<
typename Gr
idT,
typename ParticleListT,
typename InterrupterT = util::NullInterrupter>
101 inline void particlesToSdf(
const ParticleListT&, GridT&,
Real radius, InterrupterT* =
nullptr);
110 template<
typename Gr
idT,
typename ParticleListT,
typename InterrupterT = util::NullInterrupter>
117 template<
typename Gr
idT,
typename ParticleListT,
typename InterrupterT = util::NullInterrupter>
118 inline void particlesToMask(
const ParticleListT&, GridT&, InterrupterT* =
nullptr);
124 template<
typename Gr
idT,
typename ParticleListT,
typename InterrupterT = util::NullInterrupter>
125 inline void particlesToMask(
const ParticleListT&, GridT&,
Real radius, InterrupterT* =
nullptr);
134 template<
typename Gr
idT,
typename ParticleListT,
typename InterrupterT = util::NullInterrupter>
141 namespace p2ls_internal {
146 template<
typename VisibleT,
typename BlindT>
class BlindData;
150 template<
typename SdfGridT,
151 typename AttributeT = void,
156 using DisableT =
typename std::is_void<AttributeT>::type;
162 using AttType =
typename std::conditional<DisableT::value, size_t, AttributeT>::type;
163 using AttGridType =
typename SdfGridT::template ValueConverter<AttType>::Type;
165 static const bool OutputIsMask = std::is_same<SdfType, bool>::value;
197 void finalize(
bool prune =
false);
237 template<
typename ParticleListT>
238 void rasterizeSpheres(
const ParticleListT& pa);
246 template<
typename ParticleListT>
247 void rasterizeSpheres(
const ParticleListT& pa,
Real radius);
264 template<
typename ParticleListT>
265 void rasterizeTrails(
const ParticleListT& pa,
Real delta=1.0);
268 using BlindType = p2ls_internal::BlindData<SdfType, AttType>;
269 using BlindGridType =
typename SdfGridT::template ValueConverter<BlindType>::Type;
272 template<
typename ParticleListT,
typename Gr
idT>
struct Raster;
274 SdfGridType* mSdfGrid;
275 typename AttGridType::Ptr mAttGrid;
276 BlindGridType* mBlindGrid;
277 InterrupterT* mInterrupter;
278 Real mDx, mHalfWidth;
280 size_t mMinCount, mMaxCount;
285 template<
typename SdfGr
idT,
typename AttributeT,
typename InterrupterT>
290 mInterrupter(interrupter),
291 mDx(grid.voxelSize()[0]),
292 mHalfWidth(grid.background()/mDx),
299 if (!mSdfGrid->hasUniformVoxels()) {
302 if (!DisableT::value) {
303 mBlindGrid =
new BlindGridType(BlindType(grid.background()));
304 mBlindGrid->setTransform(mSdfGrid->transform().copy());
308 template<
typename SdfGr
idT,
typename AttributeT,
typename InterrupterT>
309 template<
typename ParticleListT>
313 if (DisableT::value) {
314 Raster<ParticleListT, SdfGridT> r(*
this, mSdfGrid, pa);
315 r.rasterizeSpheres();
317 Raster<ParticleListT, BlindGridType> r(*
this, mBlindGrid, pa);
318 r.rasterizeSpheres();
322 template<
typename SdfGr
idT,
typename AttributeT,
typename InterrupterT>
323 template<
typename ParticleListT>
327 if (DisableT::value) {
328 Raster<ParticleListT, SdfGridT> r(*
this, mSdfGrid, pa);
329 r.rasterizeSpheres(radius/mDx);
331 Raster<ParticleListT, BlindGridType> r(*
this, mBlindGrid, pa);
332 r.rasterizeSpheres(radius/mDx);
336 template<
typename SdfGr
idT,
typename AttributeT,
typename InterrupterT>
337 template<
typename ParticleListT>
341 if (DisableT::value) {
342 Raster<ParticleListT, SdfGridT> r(*
this, mSdfGrid, pa);
343 r.rasterizeTrails(delta);
345 Raster<ParticleListT, BlindGridType> r(*
this, mBlindGrid, pa);
346 r.rasterizeTrails(delta);
351 template<
typename SdfGr
idT,
typename AttributeT,
typename InterrupterT>
370 using AttTreeT =
typename AttGridType::TreeType;
371 using AttLeafT =
typename AttTreeT::LeafNodeType;
372 using BlindTreeT =
typename BlindGridType::TreeType;
373 using BlindLeafIterT =
typename BlindTreeT::LeafCIter;
374 using BlindLeafT =
typename BlindTreeT::LeafNodeType;
375 using SdfTreeT =
typename SdfGridType::TreeType;
376 using SdfLeafT =
typename SdfTreeT::LeafNodeType;
379 const BlindTreeT& blindTree = mBlindGrid->tree();
382 typename AttTreeT::Ptr attTree(
new AttTreeT(
385 mAttGrid =
typename AttGridType::Ptr(
new AttGridType(attTree));
386 mAttGrid->setTransform(mBlindGrid->transform().copy());
388 typename SdfTreeT::Ptr sdfTree;
392 sdfTree.reset(
new SdfTreeT(blindTree,
397 leafNodes.
foreach([&](AttLeafT& attLeaf,
size_t ) {
398 if (
const auto* blindLeaf = blindTree.probeConstLeaf(attLeaf.origin())) {
399 for (auto iter = attLeaf.beginValueOn(); iter; ++iter) {
400 const auto pos = iter.pos();
401 attLeaf.setValueOnly(pos, blindLeaf->getValue(pos).blind());
406 const auto blindAcc = mBlindGrid->getConstAccessor();
407 auto iter = attTree->beginValueOn();
408 iter.setMaxDepth(AttTreeT::ValueOnIter::LEAF_DEPTH - 1);
409 for ( ; iter; ++iter) {
410 iter.modifyValue([&](
AttType& v) { v = blindAcc.getValue(iter.getCoord()).blind(); });
415 sdfTree.reset(
new SdfTreeT(blindTree, blindTree.background().visible(),
TopologyCopy()));
416 for (BlindLeafIterT n = blindTree.cbeginLeaf(); n; ++n) {
417 const BlindLeafT& leaf = *n;
420 SdfLeafT* sdfLeaf = sdfTree->probeLeaf(xyz);
421 AttLeafT* attLeaf = attTree->probeLeaf(xyz);
423 typename BlindLeafT::ValueOnCIter m=leaf.cbeginValueOn();
426 const BlindType& v = leaf.getValue(k);
427 sdfLeaf->setValueOnly(k, v.visible());
428 attLeaf->setValueOnly(k, v.blind());
433 const BlindType& v = *m;
434 sdfLeaf->setValueOnly(k, v.visible());
435 attLeaf->setValueOnly(k, v.blind());
442 if (mSdfGrid->empty()) {
443 mSdfGrid->setTree(sdfTree);
446 mSdfGrid->tree().topologyUnion(*sdfTree);
460 template<
typename SdfGr
idT,
typename AttributeT,
typename InterrupterT>
461 template<
typename ParticleListT,
typename Gr
idT>
462 struct ParticlesToLevelSet<SdfGridT, AttributeT, InterrupterT>::Raster
464 using DisableT =
typename std::is_void<AttributeT>::type;
465 using ParticlesToLevelSetT = ParticlesToLevelSet<SdfGridT, AttributeT, InterrupterT>;
466 using SdfT =
typename ParticlesToLevelSetT::SdfType;
467 using AttT =
typename ParticlesToLevelSetT::AttType;
468 using ValueT =
typename GridT::ValueType;
469 using AccessorT =
typename GridT::Accessor;
470 using TreeT =
typename GridT::TreeType;
471 using LeafNodeT =
typename TreeT::LeafNodeType;
472 using PointPartitionerT = PointPartitioner<Index32, LeafNodeT::LOG2DIM>;
475 OutputIsMask = std::is_same<SdfT, bool>::value,
476 DoAttrXfer = !DisableT::value;
479 Raster(ParticlesToLevelSetT& parent, GridT* grid,
const ParticleListT& particles)
481 , mParticles(particles)
483 , mMap(*(mGrid->transform().baseMap()))
488 mPointPartitioner =
new PointPartitionerT;
489 mPointPartitioner->construct(particles, mGrid->transform());
493 Raster(Raster& other, tbb::split)
494 : mParent(other.mParent)
495 , mParticles(other.mParticles)
496 , mGrid(new GridT(*other.mGrid,
openvdb::ShallowCopy()))
502 , mPointPartitioner(other.mPointPartitioner)
514 delete mPointPartitioner;
518 void rasterizeSpheres()
520 mMinCount = mMaxCount = 0;
521 if (mParent.mInterrupter) {
522 mParent.mInterrupter->start(
"Rasterizing particles to level set using spheres");
524 mTask = std::bind(&Raster::rasterSpheres, std::placeholders::_1, std::placeholders::_2);
526 if (mParent.mInterrupter) mParent.mInterrupter->end();
529 void rasterizeSpheres(
Real radius)
531 mMinCount = radius < mParent.mRmin ? mParticles.size() : 0;
532 mMaxCount = radius > mParent.mRmax ? mParticles.size() : 0;
533 if (mMinCount>0 || mMaxCount>0) {
534 mParent.mMinCount = mMinCount;
535 mParent.mMaxCount = mMaxCount;
537 if (mParent.mInterrupter) {
538 mParent.mInterrupter->start(
539 "Rasterizing particles to level set using const spheres");
541 mTask = std::bind(&Raster::rasterFixedSpheres,
542 std::placeholders::_1, std::placeholders::_2, radius);
544 if (mParent.mInterrupter) mParent.mInterrupter->end();
548 void rasterizeTrails(
Real delta=1.0)
550 mMinCount = mMaxCount = 0;
551 if (mParent.mInterrupter) {
552 mParent.mInterrupter->start(
"Rasterizing particles to level set using trails");
554 mTask = std::bind(&Raster::rasterTrails,
555 std::placeholders::_1, std::placeholders::_2, delta);
557 if (mParent.mInterrupter) mParent.mInterrupter->end();
561 void operator()(
const tbb::blocked_range<size_t>& r)
565 mParent.mMinCount = mMinCount;
566 mParent.mMaxCount = mMaxCount;
570 void join(Raster& other)
577 mGrid->topologyUnion(*other.mGrid);
583 mMinCount += other.mMinCount;
584 mMaxCount += other.mMaxCount;
589 Raster& operator=(
const Raster&) {
return *
this; }
592 bool ignoreParticle(
Real R)
594 if (R < mParent.mRmin) {
598 if (R > mParent.mRmax) {
607 void rasterSpheres(
const tbb::blocked_range<size_t>& r)
609 AccessorT acc = mGrid->getAccessor();
611 const Real invDx = 1 / mParent.mDx;
617 for (
size_t n = r.begin(), N = r.end(); n < N; ++n) {
619 typename PointPartitionerT::IndexIterator iter = mPointPartitioner->indices(n);
620 for ( ; run && iter; ++iter) {
622 mParticles.getPosRad(
id, pos, rad);
623 const Real R = invDx * rad;
624 if (this->ignoreParticle(R))
continue;
625 const Vec3R P = mMap.applyInverseMap(pos);
626 this->getAtt<DisableT>(
id, att);
627 run = this->makeSphere(P, R, att, acc);
635 void rasterFixedSpheres(
const tbb::blocked_range<size_t>& r,
Real R)
637 AccessorT acc = mGrid->getAccessor();
642 for (
size_t n = r.begin(), N = r.end(); n < N; ++n) {
644 for (
auto iter = mPointPartitioner->indices(n); iter; ++iter) {
646 this->getAtt<DisableT>(
id, att);
647 mParticles.getPos(
id, pos);
648 const Vec3R P = mMap.applyInverseMap(pos);
649 this->makeSphere(P, R, att, acc);
657 void rasterTrails(
const tbb::blocked_range<size_t>& r,
Real delta)
659 AccessorT acc = mGrid->getAccessor();
664 const Vec3R origin = mMap.applyInverseMap(
Vec3R(0,0,0));
665 const Real Rmin = mParent.mRmin, invDx = 1 / mParent.mDx;
668 for (
size_t n = r.begin(), N = r.end(); n < N; ++n) {
670 typename PointPartitionerT::IndexIterator iter = mPointPartitioner->indices(n);
671 for ( ; run && iter; ++iter) {
673 mParticles.getPosRadVel(
id, pos, rad, vel);
674 const Real R0 = invDx * rad;
675 if (this->ignoreParticle(R0))
continue;
676 this->getAtt<DisableT>(
id, att);
677 const Vec3R P0 = mMap.applyInverseMap(pos);
678 const Vec3R V = mMap.applyInverseMap(vel) - origin;
679 const Real speed = V.length(), invSpeed = 1.0 / speed;
680 const Vec3R Nrml = -V * invSpeed;
683 for (
size_t m = 0; run && d <= speed ; ++m) {
684 run = this->makeSphere(P, R, att, acc);
685 P += 0.5 * delta * R * Nrml;
686 d = (P - P0).length();
687 R = R0 - (R0 - Rmin) * d * invSpeed;
698 if (mParent.mGrainSize>0) {
699 tbb::parallel_reduce(
700 tbb::blocked_range<size_t>(0, bucketCount, mParent.mGrainSize), *
this);
702 (*this)(tbb::blocked_range<size_t>(0, bucketCount));
713 bool makeSphere(
const Vec3R& P,
Real R,
const AttT& att, AccessorT& acc)
717 return makeSphereMask(P, R, att, acc);
719 return makeNarrowBandSphere(P, R, att, acc);
738 bool makeNarrowBandSphere(
const Vec3R& P,
Real R,
const AttT& att, AccessorT& acc)
742 w = mParent.mHalfWidth,
750 const ValueT inside = -mGrid->background();
754 for (Coord c = lo; c.x() <= hi.x(); ++c.x()) {
757 tbb::task::self().cancel_group_execution();
761 for (c.y() = lo.y(); c.y() <= hi.y(); ++c.y()) {
763 for (c.z() = lo.z(); c.z() <= hi.z(); ++c.z()) {
765 #if defined __INTEL_COMPILER
766 _Pragma(
"warning (push)")
767 _Pragma("warning (disable:186)")
769 if (x2y2z2 >= max2 || (!acc.probeValue(c, v) && (v < ValueT(0))))
771 #if defined __INTEL_COMPILER
772 _Pragma(
"warning (pop)")
774 if (x2y2z2 <= min2) {
775 acc.setValueOff(c, inside);
780 const ValueT d = Merge(
static_cast<SdfT
>(dx*(
math::Sqrt(x2y2z2)-R)), att);
781 if (d < v) acc.setValue(c, d);
790 bool makeSphereMask(
const Vec3R& p,
Real r,
const AttT& att, AccessorT& acc)
804 const std::vector<CoordBBox> padding{
805 CoordBBox(outLo.x(), outLo.y(), outLo.z(), inLo.x()-1, outHi.y(), outHi.z()),
806 CoordBBox(inHi.x()+1, outLo.y(), outLo.z(), outHi.x(), outHi.y(), outHi.z()),
807 CoordBBox(outLo.x(), outLo.y(), outLo.z(), outHi.x(), inLo.y()-1, outHi.z()),
808 CoordBBox(outLo.x(), inHi.y()+1, outLo.z(), outHi.x(), outHi.y(), outHi.z()),
809 CoordBBox(outLo.x(), outLo.y(), outLo.z(), outHi.x(), outHi.y(), inLo.z()-1),
810 CoordBBox(outLo.x(), outLo.y(), inHi.z()+1, outHi.x(), outHi.y(), outHi.z()),
812 const ValueT onValue = Merge(SdfT(1), att);
816 acc.tree().sparseFill(CoordBBox(inLo, inHi), onValue);
819 for (
const auto& bbox: padding) {
821 tbb::task::self().cancel_group_execution();
824 const Coord &bmin = bbox.min(), &bmax = bbox.max();
827 for (c = bmin, cx = c.x(); c.x() <= bmax.x(); ++c.x(), cx += 1) {
829 for (c.y() = bmin.y(), cy = c.y(); c.y() <= bmax.y(); ++c.y(), cy += 1) {
831 for (c.z() = bmin.z(), cz = c.z(); c.z() <= bmax.z(); ++c.z(), cz += 1) {
833 if (x2y2z2 < rSquared) {
834 acc.setValue(c, onValue);
843 using FuncType =
typename std::function<void (Raster*,
const tbb::blocked_range<size_t>&)>;
845 template<
typename DisableType>
846 typename std::enable_if<DisableType::value>::type
847 getAtt(
size_t, AttT&)
const {}
849 template<
typename DisableType>
850 typename std::enable_if<!DisableType::value>::type
851 getAtt(
size_t n, AttT& a)
const { mParticles.getAtt(n, a); }
854 typename std::enable_if<std::is_same<T, ValueT>::value, ValueT>::type
855 Merge(T s,
const AttT&)
const {
return s; }
858 typename std::enable_if<!std::is_same<T, ValueT>::value, ValueT>::type
859 Merge(T s,
const AttT& a)
const {
return ValueT(s,a); }
861 ParticlesToLevelSetT& mParent;
862 const ParticleListT& mParticles;
864 const math::MapBase& mMap;
865 size_t mMinCount, mMaxCount;
868 PointPartitionerT* mPointPartitioner;
875 namespace p2ls_internal {
881 template<
typename VisibleT,
typename BlindT>
885 using type = VisibleT;
886 using VisibleType = VisibleT;
887 using BlindType = BlindT;
890 explicit BlindData(VisibleT v) : mVisible(v), mBlind(
zeroVal<BlindType>()) {}
891 BlindData(VisibleT v, BlindT b) : mVisible(v), mBlind(b) {}
892 BlindData(
const BlindData&) =
default;
893 BlindData& operator=(
const BlindData&) =
default;
894 const VisibleT& visible()
const {
return mVisible; }
895 const BlindT& blind()
const {
return mBlind; }
897 bool operator==(
const BlindData& rhs)
const {
return mVisible == rhs.mVisible; }
899 bool operator< (
const BlindData& rhs)
const {
return mVisible < rhs.mVisible; }
900 bool operator> (
const BlindData& rhs)
const {
return mVisible > rhs.mVisible; }
901 BlindData
operator+(
const BlindData& rhs)
const {
return BlindData(mVisible + rhs.mVisible); }
902 BlindData
operator-(
const BlindData& rhs)
const {
return BlindData(mVisible - rhs.mVisible); }
903 BlindData
operator-()
const {
return BlindData(-mVisible, mBlind); }
912 template<
typename VisibleT,
typename BlindT>
913 inline std::ostream&
operator<<(std::ostream& ostr,
const BlindData<VisibleT, BlindT>& rhs)
915 ostr << rhs.visible();
921 template<
typename VisibleT,
typename BlindT>
922 inline BlindData<VisibleT, BlindT>
Abs(
const BlindData<VisibleT, BlindT>& x)
924 return BlindData<VisibleT, BlindT>(
math::Abs(x.visible()), x.blind());
929 template<
typename VisibleT,
typename BlindT,
typename T>
930 inline BlindData<VisibleT, BlindT>
931 operator+(
const BlindData<VisibleT, BlindT>& x,
const T& rhs)
933 return BlindData<VisibleT, BlindT>(x.visible() +
static_cast<VisibleT
>(rhs), x.blind());
944 template<
typename Gr
idT,
typename ParticleListT,
typename InterrupterT>
948 static_assert(std::is_floating_point<typename GridT::ValueType>::value,
949 "particlesToSdf requires an SDF grid with floating-point values");
953 " try Grid::setGridClass(openvdb::GRID_LEVEL_SET)");
957 p2ls.rasterizeSpheres(plist);
961 template<
typename Gr
idT,
typename ParticleListT,
typename InterrupterT>
965 static_assert(std::is_floating_point<typename GridT::ValueType>::value,
966 "particlesToSdf requires an SDF grid with floating-point values");
970 " try Grid::setGridClass(openvdb::GRID_LEVEL_SET)");
974 p2ls.rasterizeSpheres(plist, radius);
978 template<
typename Gr
idT,
typename ParticleListT,
typename InterrupterT>
982 static_assert(std::is_floating_point<typename GridT::ValueType>::value,
983 "particleTrailsToSdf requires an SDF grid with floating-point values");
987 " try Grid::setGridClass(openvdb::GRID_LEVEL_SET)");
991 p2ls.rasterizeTrails(plist, delta);
995 template<
typename Gr
idT,
typename ParticleListT,
typename InterrupterT>
999 static_assert(std::is_same<bool, typename GridT::ValueType>::value,
1000 "particlesToMask requires a boolean-valued grid");
1002 p2ls.rasterizeSpheres(plist);
1006 template<
typename Gr
idT,
typename ParticleListT,
typename InterrupterT>
1010 static_assert(std::is_same<bool, typename GridT::ValueType>::value,
1011 "particlesToMask requires a boolean-valued grid");
1013 p2ls.rasterizeSpheres(plist, radius);
1017 template<
typename Gr
idT,
typename ParticleListT,
typename InterrupterT>
1021 static_assert(std::is_same<bool, typename GridT::ValueType>::value,
1022 "particleTrailsToMask requires a boolean-valued grid");
1024 p2ls.rasterizeTrails(plist, delta);
1032 #endif // OPENVDB_TOOLS_PARTICLES_TO_LEVELSET_HAS_BEEN_INCLUDED