OpenVDB 12.1.0
Loading...
Searching...
No Matches
PointRasterizeEllipsoidsSDFImpl.h
Go to the documentation of this file.
1// Copyright Contributors to the OpenVDB Project
2// SPDX-License-Identifier: MPL-2.0
3
4/// @author Richard Jones, Nick Avramoussis
5///
6/// @file PointRasterizeEllipsoidsSDFImpl.h
7///
8
9#ifndef OPENVDB_POINTS_RASTERIZE_ELLIPSOIDS_SDF_IMPL_HAS_BEEN_INCLUDED
10#define OPENVDB_POINTS_RASTERIZE_ELLIPSOIDS_SDF_IMPL_HAS_BEEN_INCLUDED
11
12/// @brief Dev option to experiment with the ellipsoid kernel
13/// - 0 Distance to unit sphere
14/// - 1 Project unit distance on elipsoid normal
15/// - 2 Distance to axis-aligned ellipse
16#define OPENVDB_ELLIPSOID_KERNEL_MODE 2
17
18namespace openvdb {
20namespace OPENVDB_VERSION_NAME {
21namespace points {
22
24{
25
26inline math::Vec3d
27calcUnitEllipsoidBoundMaxSq(const math::Mat3s& ellipsoidTransform)
28{
29 math::Vec3d boundMax;
30 for (int i = 0; i < 3; i++) {
31 boundMax[i] =
32 ellipsoidTransform(i,0) * ellipsoidTransform(i,0) +
33 ellipsoidTransform(i,1) * ellipsoidTransform(i,1) +
34 ellipsoidTransform(i,2) * ellipsoidTransform(i,2);
35 }
36 return boundMax;
37}
38
39// compute a tight axis-aligned ellipsoid bounding box
40// based on https://tavianator.com/exact-bounding-boxes-for-spheres-ellipsoids/
41inline math::Vec3d
42calcEllipsoidBoundMax(const math::Mat3s& ellipsoidTransform)
43{
44 math::Vec3d boundMax = calcUnitEllipsoidBoundMaxSq(ellipsoidTransform);
45 boundMax[0] = std::sqrt(boundMax[0]);
46 boundMax[1] = std::sqrt(boundMax[1]);
47 boundMax[2] = std::sqrt(boundMax[2]);
48 return boundMax;
49}
50
52{
54 const std::string& rotation,
55 const std::string& pws)
56 : rotation(EllipseIndicies::getAttributeIndex<Mat3s>(desc, rotation, false))
57 , positionws(EllipseIndicies::getAttributeIndex<Vec3d>(desc, pws, true)) {}
58
59 bool hasWorldSpacePosition() const { return positionws != std::numeric_limits<size_t>::max(); }
60
61 const size_t rotation, positionws;
62
63private:
64 template<typename ValueT>
65 static inline size_t
66 getAttributeIndex(const points::AttributeSet::Descriptor& desc,
67 const std::string& name,
68 const bool allowMissing)
69 {
70 const size_t idx = desc.find(name);
72 if (allowMissing) return std::numeric_limits<size_t>::max();
73 throw std::runtime_error("Missing attribute - " + name);
74 }
75 if (typeNameAsString<ValueT>() != desc.valueType(idx)) {
76 throw std::runtime_error("Wrong attribute type for attribute " + name
77 + ", expected " + typeNameAsString<ValueT>());
78 }
79 return idx;
80 }
81};
82
83template <typename SdfT,
84 typename PositionCodecT,
85 typename RadiusType,
86 typename FilterT,
87 bool CPG>
89 public rasterize_sdf_internal::SphericalTransfer<SdfT, PositionCodecT, RadiusType, FilterT, CPG>
90{
91 using BaseT = rasterize_sdf_internal::SphericalTransfer<SdfT, PositionCodecT, RadiusType, FilterT, CPG>;
92 using typename BaseT::TreeT;
93 using typename BaseT::ValueT;
94
95 static const Index DIM = TreeT::LeafNodeType::DIM;
96 static const Index LOG2DIM = TreeT::LeafNodeType::LOG2DIM;
97 // The precision of the kernel arithmetic
98 using RealT = double;
99
103
104 EllipsoidTransfer(const size_t pidx,
105 const Vec3i width,
106 const RadiusType& rt,
107 const math::Transform& source,
108 const FilterT& filter,
109 util::NullInterrupter* interrupt,
110 SdfT& surface,
111 const EllipseIndicies& indices,
112 Int64Tree* cpg = nullptr,
113 const std::unordered_map<const PointDataTree::LeafNodeType*, Index>* ids = nullptr)
114 : BaseT(pidx, width, rt, source, filter, interrupt, surface, cpg, ids)
115 , mIndices(indices)
116 , mRotationHandle()
117 , mPositionWSHandle() {}
118
120 : BaseT(other)
121 , mIndices(other.mIndices)
122 , mRotationHandle()
123 , mPositionWSHandle() {}
124
126 {
127 const bool ret = this->BaseT::startPointLeaf(leaf);
128 mRotationHandle = std::make_unique<RotationHandleT>(leaf.constAttributeArray(mIndices.rotation));
129 if (mIndices.hasWorldSpacePosition()) {
130 mPositionWSHandle = std::make_unique<PwsHandleT>(leaf.constAttributeArray(mIndices.positionws));
131 }
132 return ret;
133 }
134
135 inline void rasterizePoint(const Coord& ijk,
136 const Index id,
137 const CoordBBox& bounds)
138 {
139 if (!BaseT::filter(id)) return;
140
141 Vec3d P;
142 if (this->mPositionWSHandle) {
143 // Position may have been smoothed so we need to use the ws handle
144 P = this->targetTransform().worldToIndex(this->mPositionWSHandle->get(id));
145 }
146 else {
147 const Vec3d PWS = this->sourceTransform().indexToWorld(ijk.asVec3d() + Vec3d(this->mPosition->get(id)));
148 P = this->targetTransform().worldToIndex(PWS);
149 }
150
151 const auto& r = this->mRadius.eval(id);
152 Vec3f radius = r.get(); // index space radius
153
154 // If we have a uniform radius, treat as a sphere
155 // @todo worth using a tolerance here in relation to the voxel size?
156 if ((radius.x() == radius.y()) && (radius.x() == radius.z())) {
157 const FixedBandRadius<float> fixed(radius.x(), r.halfband());
158 this->BaseT::rasterizePoint(P, id, bounds, fixed);
159 return;
160 }
161
162#if OPENVDB_ELLIPSOID_KERNEL_MODE == 0
163 const FixedBandRadius<RealT> fbr(1.0, r.halfband());
164 // If min2 == 0.0, then the index space radius is equal to or less than
165 // the desired half band. In this case each sphere interior always needs
166 // to be filled with distance values as we won't ever reach the negative
167 // background value. If, however, a point overlaps a voxel coord exactly,
168 // x2y2z2 will be 0.0. Forcing min2 to be less than zero here avoids
169 // incorrectly setting these voxels to inactive -background values as
170 // x2y2z2 will never be < 0.0. We still want the lteq logic in the
171 // (x2y2z2 <= min2) check as this is valid when min2 > 0.0.
172 const RealT min2 = fbr.minSq() == 0.0 ? -1.0 : fbr.minSq();
173 const RealT max2 = fbr.maxSq();
174#endif
175
176 const math::Mat3s rotation = mRotationHandle->get(id);
177 const math::Mat3s ellipsoidTransform = rotation.timesDiagonal(radius);
178 // @note Extending the search by the halfband in this way will produce
179 // the desired halfband width, but will not necessarily mean that
180 // the ON values will be levelset up to positive (exterior) background
181 // value due to elliptical coordinates not being a constant distance
182 // apart
183 const Vec3d max = calcEllipsoidBoundMax(ellipsoidTransform) + r.halfband();
184 CoordBBox intersectBox(Coord::round(P - max), Coord::round(P + max));
185 intersectBox.intersect(bounds);
186 if (intersectBox.empty()) return;
187
188 auto* const data = this->template buffer<0>();
189 [[maybe_unused]] auto* const cpg = CPG ? this->template buffer<CPG ? 1 : 0>() : nullptr;
190 auto& mask = *(this->template mask<0>());
191
192 // construct inverse transformation to create sphere out of an ellipsoid
193 const Vec3d radInv = 1.0f / radius;
194
195#if OPENVDB_ELLIPSOID_KERNEL_MODE != 2
196 // Instead of trying to compute the distance from a point to an ellips,
197 // stamp the ellipsoid by deforming the distance to the iterated voxel
198 // by the inverse ellipsoid transform, then modifying it by projecting
199 // it back to our normal coordinate system.
200 math::Mat3s invDiag;
201 invDiag.setSymmetric(radInv, Vec3f(0));
202 const math::Mat3s ellipsoidInverse = invDiag * rotation.transpose();
203#else
204 // Instead of trying to compute the distance from a point to a rotated
205 // ellipse, stamp the ellipsoid by deforming the distance to the
206 // iterated voxel by the inverse rotation. Then calculate the distance
207 // to the axis-aligned ellipse.
208 const Vec3d radInv2 = 1.0f / math::Pow2(radius);
209 const math::Mat3s ellipsoidInverse = rotation.transpose();
210#endif
211
212 // We cache the multiples matrix for each axis component but essentially
213 // this resolves to the invMat multiplied by the x,y,z position
214 // difference (i.e. c-p):
215 // pointOnUnitSphere = ellipsoidInverse * Vec3d(x,y,z);
216 const Coord& a(intersectBox.min());
217 const Coord& b(intersectBox.max());
218 const float* inv = ellipsoidInverse.asPointer(); // @todo do at RealT precision?
219 for (Coord c = a; c.x() <= b.x(); ++c.x()) {
220 const RealT x = static_cast<RealT>(c.x() - P[0]);
221 const Vec3d xradial(x*inv[0], x*inv[3], x*inv[6]);
222 const Index i = ((c.x() & (DIM-1u)) << 2*LOG2DIM); // unsigned bit shift mult
223 for (c.y() = a.y(); c.y() <= b.y(); ++c.y()) {
224 const RealT y = static_cast<RealT>(c.y() - P[1]);
225 const Vec3d xyradial = xradial + Vec3d(y*inv[1], y*inv[4], y*inv[7]);
226 const Index ij = i + ((c.y() & (DIM-1u)) << LOG2DIM);
227 for (c.z() = a.z(); c.z() <= b.z(); ++c.z()) {
228 const Index offset = ij + /*k*/(c.z() & (DIM-1u));
229 if (!mask.isOn(offset)) continue; // inside existing level set or not in range
230 const RealT z = static_cast<RealT>(c.z() - P[2]);
231 // Transform by inverse of ellipsoidal transform to
232 // calculate distance to deformed surface
233 const Vec3d pointOnUnitSphere = (xyradial + Vec3d(z*inv[2], z*inv[5], z*inv[8]));
234
235 ValueT d;
236
237#if OPENVDB_ELLIPSOID_KERNEL_MODE == 0
238 RealT len = pointOnUnitSphere.lengthSqr();
239 // Note that the transformation of the ellipse in this way
240 // also deforms the narrow band width - we may want to do
241 // something about this.
242 if (len >= max2) continue; //outside narrow band of particle in positive direction
243 if (len <= min2) { //outside narrow band of the particle in negative direction. can disable this to fill interior
244 data[offset] = -(this->mBackground);
245 mask.setOff(offset);
246 continue;
247 }
248 d = ValueT(this->mDx * (math::Sqrt(len) - 1.0)); // back to world space
249#elif OPENVDB_ELLIPSOID_KERNEL_MODE == 1
250 RealT len = pointOnUnitSphere.lengthSqr();
251 // @todo There may be a way to map the min2/max2 checks
252 // (or at least the min2 check) onto this length to avoid
253 // the sqrts and projections when outside the half band
254 len = math::Sqrt(len);
255 if (OPENVDB_UNLIKELY(len == 0)) {
256 // The minimum radius of this ellipse in world space. Used only to store
257 // a distance when a given voxel's ijk coordinates overlaps exactly with
258 // the center of an ellipse
259 d = -ValueT(std::min(radius.x(), std::min(radius.y(), radius.z()))) * ValueT(this->mDx);
260 }
261 else {
262 Vec3d ellipseNormal = (ellipsoidInverse.transpose() * pointOnUnitSphere);
263 ellipseNormal.normalize();
264 // Project xyz onto the ellipse normal, scale length by
265 // the offset correction based on the distance from the
266 // unit sphere surface and finally convert back to
267 // world space
268 //
269 // Invert the length to represent a proportional offset to
270 // the final distance when the above sphere point is
271 // projected back onto the ellipse. If the length iz zero,
272 // then this voxel's ijk is the center of the ellipse.
273 d = static_cast<ValueT>(
274 ((x * ellipseNormal.x()) +
275 (y * ellipseNormal.y()) +
276 (z * ellipseNormal.z())) // dot product
277 * (1.0 - (RealT(1.0)/len)) // scale
278 * this->mDx); // world space
279 }
280
281 if (d >= this->mBackground) continue; //outside narrow band of particle in positive direction
282 if (d <= -this->mBackground) { //outside narrow band of the particle in negative direction. can disable this to fill interior
283 data[offset] = -(this->mBackground);
284 mask.setOff(offset);
285 continue;
286 }
287#elif OPENVDB_ELLIPSOID_KERNEL_MODE == 2
288 const RealT k2 = (pointOnUnitSphere * radInv2).length();
289 if (OPENVDB_UNLIKELY(k2 == 0)) {
290 // The minimum radius of this ellipse in world space. Used only to store
291 // a distance when a given voxel's ijk coordinates overlaps exactly with
292 // the center of an ellipse
293 d = -ValueT(std::min(radius.x(), std::min(radius.y(), radius.z()))) * ValueT(this->mDx);
294 }
295 else {
296 const RealT k1 = (pointOnUnitSphere * radInv).length();
297 OPENVDB_ASSERT(k1 > 0);
298 // calc distance and then scale by voxelsize to convert to ws
299 d = static_cast<ValueT>((k1 * (k1 - RealT(1.0)) / k2) * this->mDx);
300 }
301 if (d >= this->mBackground) continue; //outside narrow band of particle in positive direction
302 if (d <= -this->mBackground) { //outside narrow band of the particle in negative direction. can disable this to fill interior
303 data[offset] = -(this->mBackground);
304 mask.setOff(offset);
305 continue;
306 }
307#endif
308 OPENVDB_ASSERT(std::isfinite(d));
309 ValueT& v = data[offset];
310 if (d < v) {
311 v = d;
312 if constexpr(CPG) cpg[offset] = Int64(this->mPLeafMask | Index64(id));
313 }
314 }
315 }
316 }
317 }
318
319private:
320 const EllipseIndicies& mIndices;
321 std::unique_ptr<RotationHandleT> mRotationHandle;
322 std::unique_ptr<PwsHandleT> mPositionWSHandle;
323};
324
325
326template<typename RadiusType, typename MaskTreeT>
328 : public rasterize_sdf_internal::SurfaceMaskOp<MaskTreeT>
329{
330 using BaseT = rasterize_sdf_internal::SurfaceMaskOp<MaskTreeT>;
333 using RadiusT = typename RadiusType::ValueType;
334 static const Index DIM = points::PointDataTree::LeafNodeType::DIM;
335
337 const math::Transform& src,
338 const math::Transform& trg,
339 const RadiusType& rad,
340 const Real halfband,
341 const EllipseIndicies& indices)
342 : BaseT(src, trg, nullptr)
343 , mRadius(rad)
344 , mHalfband(halfband)
345 , mIndices(indices)
346 , mMaxDist(0) {}
347
349 : BaseT(other)
350 , mRadius(other.mRadius)
351 , mHalfband(other.mHalfband)
352 , mIndices(other.mIndices)
353 , mMaxDist(0) {}
354
355 Vec3i getMaxDist() const { return mMaxDist; }
356
358 {
359 mMaxDist = math::maxComponent(mMaxDist, other.mMaxDist);
360 this->BaseT::join(other);
361 }
362
363 /// @brief Fill activity by analyzing the radius values on points in this
364 /// leaf. Ignored ellipsoid rotations which results in faster but over
365 /// zealous activation region.
367 {
368 Vec3f maxr(0);
369 RadiusType rad(mRadius);
370 rad.reset(leaf);
371 if constexpr(RadiusType::Fixed) {
372 maxr = rad.eval(0).get();
373 }
374 else {
375 for (Index i = 0; i < Index(rad.size()); ++i) {
376 maxr = math::maxComponent(maxr, rad.eval(i).get());
377 }
378 }
379
380 // The max stretch coefficient. We can't analyze each xyz component
381 // individually as we don't take into account the ellipse rotation, so
382 // have to expand the worst case uniformly
383 const Real maxRadius = std::max(maxr.x(), std::max(maxr.y(), maxr.z()));
384
385 // @note This addition of the halfband here doesn't take into account
386 // the squash on the halfband itself. The subsequent rasterizer
387 // squashes the halfband but probably shouldn't, so although this
388 // expansion is more then necessary, I'm leaving the logic here for
389 // now. We ignore stretches as these are capped to the half band
390 // length anyway
391 const Vec3i dist = Vec3i(static_cast<int32_t>(math::Round(maxRadius + mHalfband)));
392 if (!mIndices.hasWorldSpacePosition()) {
393 if (!this->activate(leaf, dist)) return; // empty node
394 /// @todo deactivate min
395 mMaxDist = math::maxComponent(mMaxDist, dist);
396 }
397 else {
398 // Positions may have been smoothed, so we need to account for that
399 points::AttributeHandle<Vec3d> pwsHandle(leaf.constAttributeArray(mIndices.positionws));
400 if (pwsHandle.size() == 0) return; // no positions?
401 Vec3d maxPos(std::numeric_limits<Real>::lowest()),
402 minPos(std::numeric_limits<Real>::max());
403 for (Index i = 0; i < Index(pwsHandle.size()); ++i)
404 {
405 const Vec3d Pws = pwsHandle.get(i);
406 minPos = math::minComponent(minPos, Pws);
407 maxPos = math::maxComponent(maxPos, Pws);
408 }
409
410 // Convert point bounds to surface transform, expand and fill
411 CoordBBox surfaceBounds(
412 Coord::round(this->mSurfaceTransform.worldToIndex(minPos)),
413 Coord::round(this->mSurfaceTransform.worldToIndex(maxPos)));
414 surfaceBounds.min() -= Coord(dist);
415 surfaceBounds.max() += Coord(dist);
416 this->activate(surfaceBounds);
417 /// @todo deactivate min
418 this->updateMaxLookup(minPos, maxPos, dist, leaf);
419 }
420 }
421
422 /// @brief Fill activity by analyzing the axis aligned ellipse bounding
423 /// boxes on points in this leaf. Slightly slower than just looking at
424 /// ellipse stretches but produces a more accurate/tighter activation
425 /// result
427 {
428 RadiusType rad(mRadius);
429 rad.reset(leaf);
430 const Vec3f radius0 = rad.eval(0).get();
431
432 if constexpr(RadiusType::Fixed) {
433 // If the radius is fixed and uniform, don't bother evaluating the
434 // rotations (we could just fall back to the spherical transfer...)
435 const bool isSphere = (radius0.x() == radius0.y()) && (radius0.x() == radius0.z());
436 if (isSphere) {
437 this->fillFromStretch(leaf);
438 return;
439 }
440 }
441
442 Vec3d maxPos(std::numeric_limits<Real>::lowest()),
443 minPos(std::numeric_limits<Real>::max());
444
445 // Compute min/max point leaf positions
446 if (!mIndices.hasWorldSpacePosition())
447 {
448 const CoordBBox box = this->getActiveBoundingBox(leaf);
449 if (box.empty()) return;
450 minPos = this->mPointsTransform.indexToWorld(box.min().asVec3d() - 0.5);
451 maxPos = this->mPointsTransform.indexToWorld(box.max().asVec3d() + 0.5);
452 }
453 else
454 {
455 // positions may have been smoothed, so we need to account for that too
456 points::AttributeHandle<Vec3d> pwsHandle(leaf.constAttributeArray(mIndices.positionws));
457 if (pwsHandle.size() == 0) return;
458 for (Index i = 0; i < pwsHandle.size(); ++i)
459 {
460 const Vec3d Pws = pwsHandle.get(i);
461 minPos = math::minComponent(minPos, Pws);
462 maxPos = math::maxComponent(maxPos, Pws);
463 }
464 }
465
466 // Compute max ellipse bounds
467 points::AttributeHandle<math::Mat3s> rotHandle(leaf.constAttributeArray(mIndices.rotation));
468 float maxUniformRadius(0);
469 Vec3f r(radius0);
470 Vec3d maxBounds(0);
471
472 for (Index i = 0; i < rotHandle.size(); ++i)
473 {
474 // If the radius is Fixed, we know we have non-uniform components
475 // If the radius isn't fixed, check uniformity
476 if constexpr(!RadiusType::Fixed)
477 {
478 r = rad.eval(i).get();
479 const bool isSphere = (r.x() == r.y()) && (r.x() == r.z());
480 if (isSphere) {
481 // If this point is a sphere, we don't need to look at the rotations
482 maxUniformRadius = std::max(maxUniformRadius, float(r.x()));
483 continue;
484 }
485 }
486
487 // compute AABB of ellipse
488 const math::Mat3s rotation = rotHandle.get(i);
489 const math::Mat3s ellipsoidTransform = rotation.timesDiagonal(r);
490 const Vec3d bounds = calcUnitEllipsoidBoundMaxSq(ellipsoidTransform);
491 maxBounds = math::maxComponent(maxBounds, bounds);
492 }
493
494 for (size_t i = 0; i < 3; ++i) {
495 // We don't do the sqrt per point so resolve the actual maxBounds now
496 maxBounds[i] = std::sqrt(maxBounds[i]);
497 // Account for uniform stretch values - compare the ellipse to isolated
498 // points and choose the largest radius of the two
499 maxBounds[i] = std::max(double(maxUniformRadius), maxBounds[i]);
500 }
501
502 // @note This addition of the halfband here doesn't take into account
503 // the squash on the halfband itself. The subsequent rasterizer
504 // squashes the halfband but probably shouldn't, so although this
505 // expansion is more then necessary, I'm leaving the logic here for
506 // now. We ignore stretches as these are capped to the half band
507 // length anyway
508 const Coord dist = Coord::round(maxBounds + mHalfband);
509 // Convert point bounds to surface transform, expand and fill
510 CoordBBox surfaceBounds(
511 Coord::round(this->mSurfaceTransform.worldToIndex(minPos)),
512 Coord::round(this->mSurfaceTransform.worldToIndex(maxPos)));
513 surfaceBounds.min() -= dist;
514 surfaceBounds.max() += dist;
515 this->activate(surfaceBounds);
516 /// @todo deactivate min
517 this->updateMaxLookup(minPos, maxPos, dist.asVec3i(), leaf);
518 }
519
520 void operator()(const typename LeafManagerT::LeafRange& range)
521 {
522 for (auto leaf = range.begin(); leaf; ++leaf) {
523 this->fillFromStretchAndRotation(*leaf);
524 }
525 }
526
527private:
528 void updateMaxLookup(const Vec3d& minWs,
529 const Vec3d& maxWs,
530 const Vec3i dist,
531 const typename LeafManagerT::LeafNodeType& leaf)
532 {
533 // Compute the maximum lookup required if points have moved outside of
534 // this node by finding the voxel furthest away from our node and using
535 // it's maximum index coordinate as the distance we need to search
536 Coord minIdx = this->mPointsTransform.worldToIndexCellCentered(minWs);
537 Coord maxIdx = this->mPointsTransform.worldToIndexCellCentered(maxWs);
538 const auto bounds = leaf.getNodeBoundingBox();
539
540 // If any of the ijk coords are > 0 then we need to subtract
541 // the dimension of the current leaf node from the offset distance.
542 // Note that min and max can both be in the negative or positive
543 // direction
544 if (!bounds.isInside(maxIdx)) {
545 maxIdx -= leaf.origin();
546 if (maxIdx.x() > 0) maxIdx.x() -= DIM;
547 if (maxIdx.y() > 0) maxIdx.y() -= DIM;
548 if (maxIdx.z() > 0) maxIdx.z() -= DIM;
549 maxIdx = Abs(maxIdx);
550 }
551 else {
552 maxIdx.reset(0);
553 }
554 if (!bounds.isInside(minIdx))
555 {
556 minIdx -= leaf.origin();
557 if (minIdx.x() > 0) minIdx.x() -= DIM;
558 if (minIdx.y() > 0) minIdx.y() -= DIM;
559 if (minIdx.z() > 0) minIdx.z() -= DIM;
560 minIdx = Abs(minIdx);
561 }
562 else {
563 minIdx.reset(0);
564 }
565
566 // Now compute the max offset
567 maxIdx.maxComponent(minIdx);
568 mMaxDist = math::maxComponent(mMaxDist, dist + maxIdx.asVec3i());
569 }
570
571private:
572 const RadiusType& mRadius;
573 const Real mHalfband;
574 const EllipseIndicies& mIndices;
575 Vec3i mMaxDist;
576};
577
578template <typename PointDataGridT,
579 typename SdfT,
580 typename SettingsT>
582rasterizeEllipsoids(const PointDataGridT& points,
583 const SettingsT& settings,
584 const typename SettingsT::FilterType& filter)
585{
588
589 using namespace rasterize_sdf_internal;
590
591 using PointDataTreeT = typename PointDataGridT::TreeType;
592 using MaskTreeT = typename SdfT::TreeType::template ValueConverter<ValueMask>::Type;
593 using AttributeTypes = typename SettingsT::AttributeTypes;
594 using FilterT = typename SettingsT::FilterType;
595
596 const std::vector<std::string>& attributes = settings.attributes;
597 const Real halfband = settings.halfband;
598 const Vec3d radiusScale = settings.radiusScale;
599 auto* interrupter = settings.interrupter;
600
601 math::Transform::Ptr transform = settings.transform;
602 if (!transform) transform = points.transform().copy();
603 const Real vs = transform->voxelSize()[0];
604 const typename SdfT::ValueType background =
605 static_cast<typename SdfT::ValueType>(vs * halfband);
606
607 // early exit here if no points
608 const auto leaf = points.constTree().cbeginLeaf();
609 if (!leaf) {
610 typename SdfT::Ptr surface = SdfT::create(background);
611 surface->setTransform(transform);
612 surface->setGridClass(GRID_LEVEL_SET);
613 return GridPtrVec(1, surface);
614 }
615
616 // Get attributes
617 const EllipseIndicies indices(leaf->attributeSet().descriptor(),
618 settings.rotation,
619 settings.pws); // pws is optional
620
621 typename SdfT::Ptr surface;
622 GridPtrVec grids;
623
624 if (settings.radius.empty()) {
625 // Initial Index Space radius
626 FixedBandRadius<Vec3f> rad(Vec3f(radiusScale / vs), float(halfband));
627
628 // pre-compute ellipsoidal transform bounds and surface mask. Points that
629 // are not in the ellipses group are treated as spheres and follow
630 // the same logic as that of the Fixed/VaryingSurfaceMaskOps. Ellipsoids
631 // instead compute the max axis-aligned bounding boxes. The maximum extents
632 // of the spheres/ellipses in a leaf is used for the maximum mask/lookup.
633 // The minimum extent is always the smallest spherical radius.
634 // @todo Is the min extent correct?
635 Vec3i width;
636 {
637 if (interrupter) interrupter->start("Generating ellipsoidal surface topology");
638
640 // pass radius scale as index space
641
643 op(points.transform(), *transform, rad, halfband, indices);
644 tbb::parallel_reduce(manager.leafRange(), op);
645
646 surface = rasterize_sdf_internal::initSdfFromMasks<SdfT, MaskTreeT>
647 (transform, background, op.mask(), op.maskoff());
648 // max possible index space radius
649 width = op.getMaxDist();
650
651 if (interrupter) interrupter->end();
652 }
653
654 if (interrupter) interrupter->start("Rasterizing particles to level set using ellipses and fixed spheres");
655
656 grids = doRasterizeSurface<SdfT, EllipsoidTransfer, AttributeTypes, FilterT>
657 (points, attributes, *surface,
658 width, rad, points.transform(), filter, interrupter, *surface, indices); // args
659 }
660 else {
661 using RadiusT = typename SettingsT::RadiusAttributeType;
662
663 const size_t ridx = leaf->attributeSet().find(settings.radius);
664 if (ridx == AttributeSet::INVALID_POS) {
665 OPENVDB_THROW(RuntimeError, "Failed to find radius attribute \"" + settings.radius + "\"");
666 }
667
668 // Initial varying Index Space radius
669 VaryingBandRadius<RadiusT, Vec3f> rad(ridx, float(halfband), Vec3f(radiusScale / vs));
670
671 // pre-compute ellipsoidal transform bounds and surface mask. Points that
672 // are not in the ellipse group are treated as spheres and follow
673 // the same logic as that of the Fixed/VaryingSurfaceMaskOps. Ellipsoids
674 // instead compute the max axis-aligned bounding boxes. The maximum extents
675 // of the spheres/ellipses in a leaf is used for the maximum mask/lookup.
676 // The minimum extent is always the smallest spherical radius.
677 // @todo Is the min extent correct?
678 Vec3i width;
679 {
680 if (interrupter) interrupter->start("Generating variable ellipsoidal surface topology");
681
683
684 // pass radius scale as index space
686 op(points.transform(), *transform, rad, halfband, indices);
687 tbb::parallel_reduce(manager.leafRange(), op);
688
689 surface = rasterize_sdf_internal::initSdfFromMasks<SdfT, MaskTreeT>
690 (transform, background, op.mask(), op.maskoff());
691 // max possible index space radius
692 width = op.getMaxDist();
693
694 if (interrupter) interrupter->end();
695 }
696
697 if (interrupter) interrupter->start("Rasterizing particles to level set using variable ellipses");
698
699 grids = doRasterizeSurface<SdfT, EllipsoidTransfer, AttributeTypes, FilterT>
700 (points, attributes, *surface,
701 width, rad, points.transform(), filter, interrupter, *surface, indices); // args
702 }
703
704 if (interrupter) interrupter->end();
705 tools::pruneLevelSet(surface->tree());
706 grids.insert(grids.begin(), surface);
707 return grids;
708}
709
710} // namespace rasterize_sdf_internal
711
712}
713}
714}
715
716#endif // OPENVDB_POINTS_RASTERIZE_ELLIPSOIDS_SDF_IMPL_HAS_BEEN_INCLUDED
#define OPENVDB_ASSERT(X)
Definition Assert.h:41
#define OPENVDB_UNLIKELY(x)
Definition Platform.h:92
static Coord round(const Vec3< T > &xyz)
Return xyz rounded to the closest integer coordinates (cell centered conversion).
Definition Coord.h:51
Definition Exceptions.h:63
Axis-aligned bounding box of signed integer coordinates.
Definition Coord.h:252
bool empty() const
Return true if this bounding box is empty (i.e., encloses no coordinates).
Definition Coord.h:359
const Coord & min() const
Definition Coord.h:324
const Coord & max() const
Definition Coord.h:325
void intersect(const CoordBBox &bbox)
Intersect this bounding box with the given bounding box.
Definition Coord.h:447
Signed (x, y, z) 32-bit integer coordinates.
Definition Coord.h:26
Vec3d asVec3d() const
Definition Coord.h:144
Int32 y() const
Definition Coord.h:132
void maxComponent(const Coord &other)
Perform a component-wise maximum with the other Coord.
Definition Coord.h:184
Coord & reset(Int32 x, Int32 y, Int32 z)
Reset all three coordinates with the specified arguments.
Definition Coord.h:70
Int32 x() const
Definition Coord.h:131
Vec3i asVec3i() const
Definition Coord.h:146
Int32 z() const
Definition Coord.h:133
Mat3 transpose() const
returns transpose of this
Definition Mat3.h:454
void setSymmetric(const Vec3< T > &vdiag, const Vec3< T > &vtri)
Set diagonal and symmetric triangular components.
Definition Mat3.h:223
T * asPointer()
Definition Mat.h:101
Definition Transform.h:40
SharedPtr< Transform > Ptr
Definition Transform.h:42
T & x()
Reference to the component, e.g. v.x() = 4.5f;.
Definition Vec3.h:86
T & y()
Definition Vec3.h:87
T & z()
Definition Vec3.h:88
T lengthSqr() const
Definition Vec3.h:212
bool normalize(T eps=T(1.0e-7))
this = normalized this
Definition Vec3.h:363
Definition AttributeArray.h:764
Index size() const
Definition AttributeArray.h:786
ValueType get(Index n, Index m=0) const
Definition AttributeArray.h:2058
An immutable object that stores name, type and AttributeSet position for a constant collection of att...
Definition AttributeSet.h:311
size_t find(const std::string &name) const
Return the position of the attribute array whose name is name, or INVALID_POS if no match is found.
const Name & valueType(size_t pos) const
Return the name of the attribute array's type.
@ INVALID_POS
Definition AttributeSet.h:42
This class manages a linear array of pointers to a given tree's leaf nodes, as well as optional auxil...
Definition LeafManager.h:86
LeafRange leafRange(size_t grainsize=1) const
Return a TBB-compatible LeafRange.
Definition LeafManager.h:346
float Sqrt(float x)
Return the square root of a floating-point value.
Definition Math.h:761
Mat3< float > Mat3s
Definition Mat3.h:832
Vec3< double > Vec3d
Definition Vec3.h:665
float Round(float x)
Return x rounded to the nearest integer.
Definition Math.h:819
Vec2< T > minComponent(const Vec2< T > &v1, const Vec2< T > &v2)
Return component-wise minimum of the two vectors.
Definition Vec2.h:504
Vec3< int32_t > Vec3i
Definition Vec3.h:662
Vec2< T > maxComponent(const Vec2< T > &v1, const Vec2< T > &v2)
Return component-wise maximum of the two vectors.
Definition Vec2.h:513
Type Pow2(Type x)
Return x2.
Definition Math.h:548
Definition PointRasterizeEllipsoidsSDFImpl.h:24
GridPtrVec rasterizeEllipsoids(const PointDataGridT &points, const SettingsT &settings, const typename SettingsT::FilterType &filter)
Definition PointRasterizeEllipsoidsSDFImpl.h:582
math::Vec3d calcUnitEllipsoidBoundMaxSq(const math::Mat3s &ellipsoidTransform)
Definition PointRasterizeEllipsoidsSDFImpl.h:27
math::Vec3d calcEllipsoidBoundMax(const math::Mat3s &ellipsoidTransform)
Definition PointRasterizeEllipsoidsSDFImpl.h:42
Definition AttributeArray.h:42
void pruneLevelSet(TreeT &tree, bool threaded=true, size_t grainSize=1)
Reduce the memory footprint of a tree by replacing nodes whose values are all inactive with inactive ...
Definition Prune.h:390
std::vector< GridBase::Ptr > GridPtrVec
Definition Grid.h:508
Index32 Index
Definition Types.h:54
math::Vec3< float > Vec3f
Definition Types.h:74
double Real
Definition Types.h:60
@ GRID_LEVEL_SET
Definition Types.h:526
int64_t Int64
Definition Types.h:57
tree::Tree4< int64_t, 5, 4, 3 >::Type Int64Tree
Definition openvdb.h:57
NumericAttributeTypes:: Append< Vec3AttributeTypes >:: Append< Mat3AttributeTypes >:: Append< Mat4AttributeTypes >:: Append< QuatAttributeTypes >:: Append< points::GroupAttributeArray >:: Append< points::StringAttributeArray >:: Append< points::TypedAttributeArray< bool > > AttributeTypes
The attribute array types which OpenVDB will register by default.
Definition openvdb.h:178
uint64_t Index64
Definition Types.h:53
const char * typeNameAsString()
Definition Types.h:587
Definition Exceptions.h:13
#define OPENVDB_THROW(exception, message)
Definition Exceptions.h:74
Helper metafunction used to determine if the first template parameter is a specialization of the clas...
Definition Types.h:263
Definition PointRasterizeEllipsoidsSDFImpl.h:52
bool hasWorldSpacePosition() const
Definition PointRasterizeEllipsoidsSDFImpl.h:59
const size_t positionws
Definition PointRasterizeEllipsoidsSDFImpl.h:61
const size_t rotation
Definition PointRasterizeEllipsoidsSDFImpl.h:61
EllipseIndicies(const points::AttributeSet::Descriptor &desc, const std::string &rotation, const std::string &pws)
Definition PointRasterizeEllipsoidsSDFImpl.h:53
Definition PointRasterizeEllipsoidsSDFImpl.h:329
typename RadiusType::ValueType RadiusT
Definition PointRasterizeEllipsoidsSDFImpl.h:333
void operator()(const typename LeafManagerT::LeafRange &range)
Definition PointRasterizeEllipsoidsSDFImpl.h:520
static const Index DIM
Definition PointRasterizeEllipsoidsSDFImpl.h:334
void join(EllipseSurfaceMaskOp &other)
Definition PointRasterizeEllipsoidsSDFImpl.h:357
void fillFromStretch(const typename LeafManagerT::LeafNodeType &leaf)
Fill activity by analyzing the radius values on points in this leaf. Ignored ellipsoid rotations whic...
Definition PointRasterizeEllipsoidsSDFImpl.h:366
void fillFromStretchAndRotation(const typename LeafManagerT::LeafNodeType &leaf)
Fill activity by analyzing the axis aligned ellipse bounding boxes on points in this leaf....
Definition PointRasterizeEllipsoidsSDFImpl.h:426
Vec3i getMaxDist() const
Definition PointRasterizeEllipsoidsSDFImpl.h:355
rasterize_sdf_internal::SurfaceMaskOp< MaskTreeT > BaseT
Definition PointRasterizeEllipsoidsSDFImpl.h:330
EllipseSurfaceMaskOp(const EllipseSurfaceMaskOp &other, tbb::split)
Definition PointRasterizeEllipsoidsSDFImpl.h:348
EllipseSurfaceMaskOp(const math::Transform &src, const math::Transform &trg, const RadiusType &rad, const Real halfband, const EllipseIndicies &indices)
Definition PointRasterizeEllipsoidsSDFImpl.h:336
points::PointDataTree::LeafNodeType PointDataLeaf
Definition PointRasterizeEllipsoidsSDFImpl.h:331
tree::LeafManager< const points::PointDataTree > LeafManagerT
Definition PointRasterizeEllipsoidsSDFImpl.h:332
rasterize_sdf_internal::SphericalTransfer< SdfT, PositionCodecT, RadiusType, FilterT, CPG > BaseT
Definition PointRasterizeEllipsoidsSDFImpl.h:91
points::AttributeHandle< math::Mat3s > RotationHandleT
Definition PointRasterizeEllipsoidsSDFImpl.h:100
EllipsoidTransfer(const size_t pidx, const Vec3i width, const RadiusType &rt, const math::Transform &source, const FilterT &filter, util::NullInterrupter *interrupt, SdfT &surface, const EllipseIndicies &indices, Int64Tree *cpg=nullptr, const std::unordered_map< const PointDataTree::LeafNodeType *, Index > *ids=nullptr)
Definition PointRasterizeEllipsoidsSDFImpl.h:104
static const Index DIM
Definition PointRasterizeEllipsoidsSDFImpl.h:95
double RealT
Definition PointRasterizeEllipsoidsSDFImpl.h:98
void rasterizePoint(const Coord &ijk, const Index id, const CoordBBox &bounds)
Definition PointRasterizeEllipsoidsSDFImpl.h:135
points::AttributeHandle< Vec3d > PwsHandleT
Definition PointRasterizeEllipsoidsSDFImpl.h:102
bool startPointLeaf(const PointDataTree::LeafNodeType &leaf)
Definition PointRasterizeEllipsoidsSDFImpl.h:125
static const Index LOG2DIM
Definition PointRasterizeEllipsoidsSDFImpl.h:96
EllipsoidTransfer(const EllipsoidTransfer &other)
Definition PointRasterizeEllipsoidsSDFImpl.h:119
points::AttributeHandle< Vec3f > PHandleT
Definition PointRasterizeEllipsoidsSDFImpl.h:101
Base class for interrupters.
Definition NullInterrupter.h:26
#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