OpenVDB 13.0.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: Apache-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 const bool xformIsQuat)
57 : xform(xformIsQuat ?
58 EllipseIndicies::getAttributeIndex<Quats>(desc, rotation, false) :
59 EllipseIndicies::getAttributeIndex<Mat3s>(desc, rotation, false))
60 , positionws(EllipseIndicies::getAttributeIndex<Vec3d>(desc, pws, true)) {}
61
62 bool hasWorldSpacePosition() const { return positionws != std::numeric_limits<size_t>::max(); }
63
64 const size_t xform, positionws;
65
66private:
67 template<typename ValueT>
68 static inline size_t
69 getAttributeIndex(const points::AttributeSet::Descriptor& desc,
70 const std::string& name,
71 const bool optional)
72 {
73 const size_t idx = desc.find(name);
75 if (optional) return std::numeric_limits<size_t>::max();
76 throw std::runtime_error("Missing attribute - " + name);
77 }
78 if (typeNameAsString<ValueT>() != desc.valueType(idx)) {
79 throw std::runtime_error("Wrong attribute type for attribute " + name
80 + ", expected " + typeNameAsString<ValueT>());
81 }
82 return idx;
83 }
84};
85
86template <typename SdfT,
87 typename PositionCodecT,
88 typename RadiusType,
89 typename FilterT,
90 bool CPG>
92 public rasterize_sdf_internal::SphericalTransfer<SdfT, PositionCodecT, RadiusType, FilterT, CPG>
93{
94 using BaseT = rasterize_sdf_internal::SphericalTransfer<SdfT, PositionCodecT, RadiusType, FilterT, CPG>;
95 using typename BaseT::TreeT;
96 using typename BaseT::ValueT;
97
98 static const Index DIM = TreeT::LeafNodeType::DIM;
99 static const Index LOG2DIM = TreeT::LeafNodeType::LOG2DIM;
100 // The precision of the kernel arithmetic
101 using RealT = double;
102
105
106 EllipsoidTransfer(const size_t pidx,
107 const Vec3i width,
108 const RadiusType& rt,
109 const math::Transform& source,
110 const FilterT& filter,
111 util::NullInterrupter* interrupt,
112 SdfT& surface,
113 const EllipseIndicies& indices,
114 Int64Tree* cpg = nullptr,
115 const std::unordered_map<const PointDataTree::LeafNodeType*, Index>* ids = nullptr)
116 : BaseT(pidx, width, rt, source, filter, interrupt, surface, cpg, ids)
117 , mIndices(indices)
118 , mPositionWSHandle() {}
119
121 : BaseT(other)
122 , mIndices(other.mIndices)
123 , mPositionWSHandle() {}
124
126 {
127 const bool ret = this->BaseT::startPointLeaf(leaf);
128 if (mIndices.hasWorldSpacePosition()) {
129 mPositionWSHandle = std::make_unique<PwsHandleT>(leaf.constAttributeArray(mIndices.positionws));
130 }
131 return ret;
132 }
133
134 inline void rasterizePoint(const Coord& ijk,
135 const Index id,
136 const CoordBBox& bounds,
137 const Mat3s& xform)
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 // Evaluate any provided radius and apply it as a scale (in world space)
152 // to the current xform (which will also be in world space). We can
153 // skip this step if the xform is unitary, but we have to check every
154 // xform anyway.
155 const auto& r = this->mRadius.eval(id);
156 math::Mat3s xformws = xform.timesDiagonal(r.get() * this->mDx);
157
158 // Extract the final world space radius from the scale xform which
159 // takes into account any scale on the xform
160 Vec3f radius {
161 xformws.col(0).length(),
162 xformws.col(1).length(),
163 xformws.col(2).length()
164 };
165
166 // Normalize out the scale from out transform (i.e. make it unitary)
167 for (int i = 0; i < 3; ++i) {
168 for (int j = 0; j < 3; ++j) {
169 xformws(i,j) /= radius[j];
170 }
171 }
172
173 // finally put the combined radius back into index space
174 radius /= float(this->mDx);
175 OPENVDB_ASSERT(std::isfinite(radius.x()));
176 OPENVDB_ASSERT(std::isfinite(radius.y()));
177 OPENVDB_ASSERT(std::isfinite(radius.z()));
178
179 // If we have a uniform radius, treat as a sphere
180 // @todo worth using a tolerance here in relation to the voxel size?
181 if ((radius.x() == radius.y()) && (radius.x() == radius.z())) {
182 const FixedBandRadius<float> fixed(radius.x(), r.halfband());
183 this->BaseT::rasterizePoint(P, id, bounds, fixed);
184 return;
185 }
186
187#if OPENVDB_ELLIPSOID_KERNEL_MODE == 0
188 const FixedBandRadius<RealT> fbr(1.0, r.halfband());
189 // If min2 == 0.0, then the index space radius is equal to or less than
190 // the desired half band. In this case each sphere interior always needs
191 // to be filled with distance values as we won't ever reach the negative
192 // background value. If, however, a point overlaps a voxel coord exactly,
193 // x2y2z2 will be 0.0. Forcing min2 to be less than zero here avoids
194 // incorrectly setting these voxels to inactive -background values as
195 // x2y2z2 will never be < 0.0. We still want the lteq logic in the
196 // (x2y2z2 <= min2) check as this is valid when min2 > 0.0.
197 const RealT min2 = fbr.minSq() == 0.0 ? -1.0 : fbr.minSq();
198 const RealT max2 = fbr.maxSq();
199#endif
200 // @note Extending the search by the halfband in this way will produce
201 // the desired halfband width, but will not necessarily mean that
202 // the ON values will be levelset up to positive (exterior) background
203 // value due to elliptical coordinates not being a constant distance
204 // apart
205 const math::Mat3s xformis = xform.timesDiagonal(r.get());
206 const Vec3d max = calcEllipsoidBoundMax(xformis) + r.halfband();
207 CoordBBox intersectBox(Coord::round(P - max), Coord::round(P + max));
208 intersectBox.intersect(bounds);
209 if (intersectBox.empty()) return;
210
211 auto* const data = this->template buffer<0>();
212 [[maybe_unused]] auto* const cpg = CPG ? this->template buffer<CPG ? 1 : 0>() : nullptr;
213 auto& mask = *(this->template mask<0>());
214
215 // construct inverse transformation to create sphere out of an ellipsoid
216 const Vec3d radInv = 1.0f / radius;
217
218#if OPENVDB_ELLIPSOID_KERNEL_MODE != 2
219 // Instead of trying to compute the distance from a point to an ellips,
220 // stamp the ellipsoid by deforming the distance to the iterated voxel
221 // by the inverse ellipsoid transform, then modifying it by projecting
222 // it back to our normal coordinate system.
223 math::Mat3s invDiag;
224 invDiag.setSymmetric(radInv, Vec3f(0));
225 const math::Mat3s ellipsoidInverse = invDiag * xformws.transpose();
226#else
227 // Instead of trying to compute the distance from a point to a rotated
228 // ellipse, stamp the ellipsoid by deforming the distance to the
229 // iterated voxel by the inverse xform. Then calculate the distance
230 // to the axis-aligned ellipse.
231 const Vec3d radInv2 = 1.0f / math::Pow2(radius);
232 const math::Mat3s ellipsoidInverse = xformws.transpose();
233#endif
234
235 // We cache the multiples matrix for each axis component but essentially
236 // this resolves to the invMat multiplied by the x,y,z position
237 // difference (i.e. c-p):
238 // pointOnUnitSphere = ellipsoidInverse * Vec3d(x,y,z);
239 const Coord& a(intersectBox.min());
240 const Coord& b(intersectBox.max());
241 const float* inv = ellipsoidInverse.asPointer(); // @todo do at RealT precision?
242 for (Coord c = a; c.x() <= b.x(); ++c.x()) {
243 const RealT x = static_cast<RealT>(c.x() - P[0]);
244 const Vec3d xradial(x*inv[0], x*inv[3], x*inv[6]);
245 const Index i = ((c.x() & (DIM-1u)) << 2*LOG2DIM); // unsigned bit shift mult
246 for (c.y() = a.y(); c.y() <= b.y(); ++c.y()) {
247 const RealT y = static_cast<RealT>(c.y() - P[1]);
248 const Vec3d xyradial = xradial + Vec3d(y*inv[1], y*inv[4], y*inv[7]);
249 const Index ij = i + ((c.y() & (DIM-1u)) << LOG2DIM);
250 for (c.z() = a.z(); c.z() <= b.z(); ++c.z()) {
251 const Index offset = ij + /*k*/(c.z() & (DIM-1u));
252 if (!mask.isOn(offset)) continue; // inside existing level set or not in range
253 const RealT z = static_cast<RealT>(c.z() - P[2]);
254 // Transform by inverse of ellipsoidal transform to
255 // calculate distance to deformed surface
256 const Vec3d pointOnUnitSphere = (xyradial + Vec3d(z*inv[2], z*inv[5], z*inv[8]));
257
258 ValueT d;
259
260#if OPENVDB_ELLIPSOID_KERNEL_MODE == 0
261 RealT len = pointOnUnitSphere.lengthSqr();
262 // Note that the transformation of the ellipse in this way
263 // also deforms the narrow band width - we may want to do
264 // something about this.
265 if (len >= max2) continue; //outside narrow band of particle in positive direction
266 if (len <= min2) { //outside narrow band of the particle in negative direction. can disable this to fill interior
267 data[offset] = -(this->mBackground);
268 mask.setOff(offset);
269 continue;
270 }
271 d = ValueT(this->mDx * (math::Sqrt(len) - 1.0)); // back to world space
272#elif OPENVDB_ELLIPSOID_KERNEL_MODE == 1
273 RealT len = pointOnUnitSphere.lengthSqr();
274 // @todo There may be a way to map the min2/max2 checks
275 // (or at least the min2 check) onto this length to avoid
276 // the sqrts and projections when outside the half band
277 len = math::Sqrt(len);
278 if (OPENVDB_UNLIKELY(len == 0)) {
279 // The minimum radius of this ellipse in world space. Used only to store
280 // a distance when a given voxel's ijk coordinates overlaps exactly with
281 // the center of an ellipse
282 d = -ValueT(std::min(radius.x(), std::min(radius.y(), radius.z()))) * ValueT(this->mDx);
283 }
284 else {
285 Vec3d ellipseNormal = (ellipsoidInverse.transpose() * pointOnUnitSphere);
286 ellipseNormal.normalize();
287 // Project xyz onto the ellipse normal, scale length by
288 // the offset correction based on the distance from the
289 // unit sphere surface and finally convert back to
290 // world space
291 //
292 // Invert the length to represent a proportional offset to
293 // the final distance when the above sphere point is
294 // projected back onto the ellipse. If the length iz zero,
295 // then this voxel's ijk is the center of the ellipse.
296 d = static_cast<ValueT>(
297 ((x * ellipseNormal.x()) +
298 (y * ellipseNormal.y()) +
299 (z * ellipseNormal.z())) // dot product
300 * (1.0 - (RealT(1.0)/len)) // scale
301 * this->mDx); // world space
302 }
303
304 if (d >= this->mBackground) continue; //outside narrow band of particle in positive direction
305 if (d <= -this->mBackground) { //outside narrow band of the particle in negative direction. can disable this to fill interior
306 data[offset] = -(this->mBackground);
307 mask.setOff(offset);
308 continue;
309 }
310#elif OPENVDB_ELLIPSOID_KERNEL_MODE == 2
311 const RealT k2 = (pointOnUnitSphere * radInv2).length();
312 if (OPENVDB_UNLIKELY(k2 == 0)) {
313 // The minimum radius of this ellipse in world space. Used only to store
314 // a distance when a given voxel's ijk coordinates overlaps exactly with
315 // the center of an ellipse
316 d = -ValueT(std::min(radius.x(), std::min(radius.y(), radius.z()))) * ValueT(this->mDx);
317 }
318 else {
319 const RealT k1 = (pointOnUnitSphere * radInv).length();
320 OPENVDB_ASSERT(k1 > 0);
321 // calc distance and then scale by voxelsize to convert to ws
322 d = static_cast<ValueT>((k1 * (k1 - RealT(1.0)) / k2) * this->mDx);
323 }
324 if (d >= this->mBackground) continue; //outside narrow band of particle in positive direction
325 if (d <= -this->mBackground) { //outside narrow band of the particle in negative direction. can disable this to fill interior
326 data[offset] = -(this->mBackground);
327 mask.setOff(offset);
328 continue;
329 }
330#endif
331 OPENVDB_ASSERT(std::isfinite(d));
332 ValueT& v = data[offset];
333 if (d < v) {
334 v = d;
335 if constexpr(CPG) cpg[offset] = Int64(this->mPLeafMask | Index64(id));
336 }
337 }
338 }
339 }
340 }
341
342protected:
344private:
345 std::unique_ptr<PwsHandleT> mPositionWSHandle;
346};
347
348/// @brief Specializations of the EllipsoidTransfer for different Xform
349/// attribute types.
350/// @todo This is a bit silly and is only required because doRasterizeSurface
351/// was designed for a specific templated transfer interface. This can easily
352/// be extended; instead of taking a specific set of template parameters on
353/// the TransferInterfaceT, it could append a variable <typename ...TArgsT>
354/// or simply take a typename ResolverT which resolved to the final transfer
355/// scheme type.
356template <typename SdfT,
357 typename PositionCodecT,
358 typename RadiusType,
359 typename FilterType,
360 bool CPG>
362 public EllipsoidTransfer<SdfT, PositionCodecT, RadiusType, FilterType, CPG>
363{
366
367 EllipsoidTransferQuat(const size_t pidx,
368 const Vec3i width,
369 const RadiusType& rt,
370 const math::Transform& source,
371 const FilterType& filter,
372 util::NullInterrupter* interrupt,
373 SdfT& surface,
374 const EllipseIndicies& indices,
375 Int64Tree* cpg = nullptr,
376 const std::unordered_map<const PointDataTree::LeafNodeType*, Index>* ids = nullptr)
377 : BaseT(pidx, width, rt, source, filter, interrupt, surface, indices, cpg, ids)
378 , mRotationHandle() {}
379
381 : BaseT(other)
382 , mRotationHandle() {}
383
385 {
386 const bool ret = this->BaseT::startPointLeaf(leaf);
387 mRotationHandle = std::make_unique<QuatHandleT>(leaf.constAttributeArray(this->BaseT::mIndices.xform));
388 return ret;
389 }
390
391 inline void rasterizePoint(const Coord& ijk,
392 const Index id,
393 const CoordBBox& bounds)
394 {
395 Mat3s R;
396 R.setToRotation(mRotationHandle->get(id));
397 this->BaseT::rasterizePoint(ijk, id, bounds, R);
398 }
399
400private:
401 std::unique_ptr<QuatHandleT> mRotationHandle;
402};
403
404/// @brief Specialization for mat3 types (see above comment)
405template <typename SdfT,
406 typename PositionCodecT,
407 typename RadiusType,
408 typename FilterType,
409 bool CPG>
411 public EllipsoidTransfer<SdfT, PositionCodecT, RadiusType, FilterType, CPG>
412{
415
416 EllipsoidTransferMat3(const size_t pidx,
417 const Vec3i width,
418 const RadiusType& rt,
419 const math::Transform& source,
420 const FilterType& filter,
421 util::NullInterrupter* interrupt,
422 SdfT& surface,
423 const EllipseIndicies& indices,
424 Int64Tree* cpg = nullptr,
425 const std::unordered_map<const PointDataTree::LeafNodeType*, Index>* ids = nullptr)
426 : BaseT(pidx, width, rt, source, filter, interrupt, surface, indices, cpg, ids)
427 , mXformHandle() {}
428
430 : BaseT(other)
431 , mXformHandle() {}
432
434 {
435 const bool ret = this->BaseT::startPointLeaf(leaf);
436 mXformHandle.reset(new XformHandleT(leaf.constAttributeArray(this->BaseT::mIndices.xform)));
437 return ret;
438 }
439
440 inline void rasterizePoint(const Coord& ijk,
441 const Index id,
442 const CoordBBox& bounds)
443 {
444 this->BaseT::rasterizePoint(ijk, id, bounds, mXformHandle->get(id));
445 }
446
447private:
448 std::unique_ptr<XformHandleT> mXformHandle;
449};
450
451template<typename RadiusType,
452 typename XformT,
453 typename MaskTreeT>
455 : public rasterize_sdf_internal::SurfaceMaskOp<MaskTreeT>
456{
457 using BaseT = rasterize_sdf_internal::SurfaceMaskOp<MaskTreeT>;
460 using RadiusT = typename RadiusType::ValueType;
462 static const Index DIM = points::PointDataTree::LeafNodeType::DIM;
463
465 const math::Transform& src,
466 const math::Transform& trg,
467 const RadiusType& rad,
468 const Real halfband,
469 const EllipseIndicies& indices)
470 : BaseT(src, trg, nullptr)
471 , mRadius(rad)
472 , mHalfband(halfband)
473 , mIndices(indices)
474 , mMaxDist(0) {}
475
477 : BaseT(other)
478 , mRadius(other.mRadius)
479 , mHalfband(other.mHalfband)
480 , mIndices(other.mIndices)
481 , mMaxDist(0) {}
482
483 Vec3i getMaxDist() const { return mMaxDist; }
484
486 {
487 mMaxDist = math::maxComponent(mMaxDist, other.mMaxDist);
488 this->BaseT::join(other);
489 }
490
491 /// @brief Fill activity by analyzing the axis aligned ellipse bounding
492 /// boxes on points in this leaf. Slightly slower than just looking at
493 /// ellipse stretches but produces a more accurate/tighter activation
494 /// result
496 {
497 RadiusType rad(mRadius);
498 rad.reset(leaf);
499
500 Vec3d maxPos(std::numeric_limits<Real>::lowest()),
501 minPos(std::numeric_limits<Real>::max());
502
503 // Compute min/max point leaf positions
504 if (mIndices.positionws == std::numeric_limits<size_t>::max())
505 {
506 const CoordBBox box = this->getActiveBoundingBox(leaf);
507 if (box.empty()) return;
508 minPos = this->mPointsTransform.indexToWorld(box.min().asVec3d() - 0.5);
509 maxPos = this->mPointsTransform.indexToWorld(box.max().asVec3d() + 0.5);
510 }
511 else
512 {
513 // positions may have been smoothed, so we need to account for that too
514 points::AttributeHandle<Vec3d> pwsHandle(leaf.constAttributeArray(mIndices.positionws));
515 if (pwsHandle.size() == 0) return;
516 for (Index i = 0; i < pwsHandle.size(); ++i)
517 {
518 const Vec3d Pws = pwsHandle.get(i);
519 minPos = math::minComponent(minPos, Pws);
520 maxPos = math::maxComponent(maxPos, Pws);
521 }
522 }
523
524 // Compute max ellips bounds
525 Vec3d maxBounds(0);
526 RotationHandleT xformHandle(leaf.constAttributeArray(mIndices.xform));
527
528 for (Index i = 0; i < xformHandle.size(); ++i)
529 {
530 math::Mat3s xform;
531 if constexpr (std::is_same_v<XformT, math::Mat3s>) {
532 xform = xformHandle.get(i);
533 }
534 else {
535 // expected quaternion
536 xform.setToRotation(xformHandle.get(i));
537 }
538 // We used to optimize for uniform radius here, but since the xform
539 // is now allowed to contain scale (i.e. not necessarily unitary),
540 // its simpler to just compute the AABB of the ellipse (in index
541 // space) than to figure out if it contains scale and normalize it
542 const math::Mat3s xformis = xform.timesDiagonal(rad.eval(i).get());
543 const Vec3d bounds = calcUnitEllipsoidBoundMaxSq(xformis);
544 maxBounds = math::maxComponent(maxBounds, bounds);
545 }
546
547 for (size_t i = 0; i < 3; ++i) {
548 // We don't do the sqrt per point so resolve the actual maxBounds now
549 maxBounds[i] = std::sqrt(maxBounds[i]);
550 }
551
552 // @note This addition of the halfband here doesn't take into account
553 // the squash on the halfband itself. The subsequent rasterizer
554 // squashes the halfband but probably shouldn't, so although this
555 // expansion is more then necessary, I'm leaving the logic here for
556 // now. We ignore stretches as these are capped to the half band
557 // length anyway
558 const Coord dist = Coord::round(maxBounds + mHalfband);
559 // Convert point bounds to surface transform, expand and fill
560 CoordBBox surfaceBounds(
561 Coord::round(this->mSurfaceTransform.worldToIndex(minPos)),
562 Coord::round(this->mSurfaceTransform.worldToIndex(maxPos)));
563 surfaceBounds.min() -= dist;
564 surfaceBounds.max() += dist;
565 this->activate(surfaceBounds);
566 /// @todo deactivate min
567 this->updateMaxLookup(minPos, maxPos, dist.asVec3i(), leaf);
568 }
569
570 void operator()(const typename LeafManagerT::LeafRange& range)
571 {
572 for (auto leaf = range.begin(); leaf; ++leaf) {
573 this->fillFromStretchAndRotation(*leaf);
574 }
575 }
576
577private:
578 void updateMaxLookup(const Vec3d& minWs,
579 const Vec3d& maxWs,
580 const Vec3i dist,
581 const typename LeafManagerT::LeafNodeType& leaf)
582 {
583 // Compute the maximum lookup required if points have moved outside of
584 // this node by finding the voxel furthest away from our node and using
585 // it's maximum index coordinate as the distance we need to search
586 Coord minIdx = this->mPointsTransform.worldToIndexCellCentered(minWs);
587 Coord maxIdx = this->mPointsTransform.worldToIndexCellCentered(maxWs);
588 const auto bounds = leaf.getNodeBoundingBox();
589
590 // If any of the ijk coords are > 0 then we need to subtract
591 // the dimension of the current leaf node from the offset distance.
592 // Note that min and max can both be in the negative or positive
593 // direction
594 if (!bounds.isInside(maxIdx)) {
595 maxIdx -= leaf.origin();
596 if (maxIdx.x() > 0) maxIdx.x() -= DIM;
597 if (maxIdx.y() > 0) maxIdx.y() -= DIM;
598 if (maxIdx.z() > 0) maxIdx.z() -= DIM;
599 maxIdx = Abs(maxIdx);
600 }
601 else {
602 maxIdx.reset(0);
603 }
604 if (!bounds.isInside(minIdx))
605 {
606 minIdx -= leaf.origin();
607 if (minIdx.x() > 0) minIdx.x() -= DIM;
608 if (minIdx.y() > 0) minIdx.y() -= DIM;
609 if (minIdx.z() > 0) minIdx.z() -= DIM;
610 minIdx = Abs(minIdx);
611 }
612 else {
613 minIdx.reset(0);
614 }
615
616 // Now compute the max offset
617 maxIdx.maxComponent(minIdx);
618 mMaxDist = math::maxComponent(mMaxDist, dist + maxIdx.asVec3i());
619 }
620
621private:
622 const RadiusType& mRadius;
623 const Real mHalfband;
624 const EllipseIndicies& mIndices;
625 Vec3i mMaxDist;
626};
627
628template <typename PointDataGridT,
629 typename SdfT,
630 typename SettingsT,
631 typename XformT>
633rasterizeEllipsoids(const PointDataGridT& points,
634 const SettingsT& settings,
635 const typename SettingsT::FilterType& filter)
636{
638 static_assert(std::is_same_v<XformT, Quats> || std::is_same_v<XformT, Mat3s>);
640
641 using namespace rasterize_sdf_internal;
642
643 using PointDataTreeT = typename PointDataGridT::TreeType;
644 using MaskTreeT = typename SdfT::TreeType::template ValueConverter<ValueMask>::Type;
645 using AttributeTypes = typename SettingsT::AttributeTypes;
646 using FilterT = typename SettingsT::FilterType;
647
648 constexpr bool XformIsQuat = std::is_same_v<XformT, math::Quats>;
649
650 const std::vector<std::string>& attributes = settings.attributes;
651 const Real halfband = settings.halfband;
652 const Vec3d radiusScale = settings.radiusScale;
653 auto* interrupter = settings.interrupter;
654
655 math::Transform::Ptr transform = settings.transform;
656 if (!transform) transform = points.transform().copy();
657 const Real vs = transform->voxelSize()[0];
658 const typename SdfT::ValueType background =
659 static_cast<typename SdfT::ValueType>(vs * halfband);
660
661 // early exit here if no points
662 const auto leaf = points.constTree().cbeginLeaf();
663 if (!leaf) {
664 typename SdfT::Ptr surface = SdfT::create(background);
665 surface->setTransform(transform);
666 surface->setGridClass(GRID_LEVEL_SET);
667 return GridPtrVec(1, surface);
668 }
669
670 // deprecated, to remove, should always use xform
672 const auto& attr = settings.rotation.empty() ? settings.xform : settings.rotation;
674
675 // Get attributes
676 const EllipseIndicies indices(leaf->attributeSet().descriptor(),
677 attr,
678 settings.pws, // pws is optional
679 XformIsQuat);
680
681 typename SdfT::Ptr surface;
682 GridPtrVec grids;
683
684 if (settings.radius.empty()) {
685 // Initial Index Space radius
686 FixedBandRadius<Vec3f> rad(Vec3f(radiusScale / vs), float(halfband));
687
688 // pre-compute ellipsoidal transform bounds and surface mask. Points that
689 // are not in the ellipses group are treated as spheres and follow
690 // the same logic as that of the Fixed/VaryingSurfaceMaskOps. Ellipsoids
691 // instead compute the max axis-aligned bounding boxes. The maximum extents
692 // of the spheres/ellipses in a leaf is used for the maximum mask/lookup.
693 // The minimum extent is always the smallest spherical radius.
694 // @todo Is the min extent correct?
695 Vec3i width;
696 {
697 if (interrupter) interrupter->start("Generating ellipsoidal surface topology");
698
700 // pass radius scale as index space
701
703 op(points.transform(), *transform, rad, halfband, indices);
704 tbb::parallel_reduce(manager.leafRange(), op);
705
706 surface = rasterize_sdf_internal::initSdfFromMasks<SdfT, MaskTreeT>
707 (transform, background, op.mask(), op.maskoff());
708 // max possible index space radius
709 width = op.getMaxDist();
710
711 if (interrupter) interrupter->end();
712 }
713
714 if (interrupter) interrupter->start("Rasterizing particles to level set using ellipses and fixed spheres");
715
716 if constexpr (std::is_same_v<XformT, math::Mat3s>) {
717 grids = doRasterizeSurface<SdfT, EllipsoidTransferMat3, AttributeTypes, FilterT>
718 (points, attributes, *surface,
719 width, rad, points.transform(), filter, interrupter, *surface, indices); // args
720 }
721 else {
722 grids = doRasterizeSurface<SdfT, EllipsoidTransferQuat, AttributeTypes, FilterT>
723 (points, attributes, *surface,
724 width, rad, points.transform(), filter, interrupter, *surface, indices); // args
725 }
726 }
727 else {
728 using RadiusT = typename SettingsT::RadiusAttributeType;
729
730 const size_t ridx = leaf->attributeSet().find(settings.radius);
731 if (ridx == AttributeSet::INVALID_POS) {
732 OPENVDB_THROW(RuntimeError, "Failed to find radius attribute \"" + settings.radius + "\"");
733 }
734
735 // Initial varying Index Space radius
736 VaryingBandRadius<RadiusT, Vec3f> rad(ridx, float(halfband), Vec3f(radiusScale / vs));
737
738 // pre-compute ellipsoidal transform bounds and surface mask. Points that
739 // are not in the ellipse group are treated as spheres and follow
740 // the same logic as that of the Fixed/VaryingSurfaceMaskOps. Ellipsoids
741 // instead compute the max axis-aligned bounding boxes. The maximum extents
742 // of the spheres/ellipses in a leaf is used for the maximum mask/lookup.
743 // The minimum extent is always the smallest spherical radius.
744 // @todo Is the min extent correct?
745 Vec3i width;
746 {
747 if (interrupter) interrupter->start("Generating variable ellipsoidal surface topology");
748
750
751 // pass radius scale as index space
753 op(points.transform(), *transform, rad, halfband, indices);
754 tbb::parallel_reduce(manager.leafRange(), op);
755
756 surface = rasterize_sdf_internal::initSdfFromMasks<SdfT, MaskTreeT>
757 (transform, background, op.mask(), op.maskoff());
758 // max possible index space radius
759 width = op.getMaxDist();
760
761 if (interrupter) interrupter->end();
762 }
763
764 if (interrupter) interrupter->start("Rasterizing particles to level set using variable ellipses");
765
766 if constexpr (std::is_same_v<XformT, math::Mat3s>) {
767 grids = doRasterizeSurface<SdfT, EllipsoidTransferMat3, AttributeTypes, FilterT>
768 (points, attributes, *surface,
769 width, rad, points.transform(), filter, interrupter, *surface, indices); // args
770 }
771 else {
772 grids = doRasterizeSurface<SdfT, EllipsoidTransferQuat, AttributeTypes, FilterT>
773 (points, attributes, *surface,
774 width, rad, points.transform(), filter, interrupter, *surface, indices); // args
775 }
776 }
777
778 if (interrupter) interrupter->end();
779 tools::pruneLevelSet(surface->tree());
780 grids.insert(grids.begin(), surface);
781 return grids;
782}
783
784template <typename PointDataGridT,
785 typename SdfT,
786 typename SettingsT>
788rasterizeEllipsoids(const PointDataGridT& points,
789 const SettingsT& settings,
790 const typename SettingsT::FilterType& filter)
791{
792 if (const auto leaf = points.constTree().cbeginLeaf()) {
793 const auto& desc = leaf->attributeSet().descriptor();
794 // deprecated, to remove, should always use xform
796 const auto& attr = settings.rotation.empty() ? settings.xform : settings.rotation;
798 const size_t idx = desc.find(attr);
800 if (typeNameAsString<math::Mat3s>() == desc.valueType(idx)) {
802 }
803 if (typeNameAsString<math::Quats>() == desc.valueType(idx)) {
805 }
806 throw std::runtime_error("Wrong attribute type for attribute " +
807 attr + ", expected " +
810 }
811 }
812
813 // If we're here, either the point grid is empty or the xform attribute didn't exist.
814 // Run a default instantiation which will elevate the correct runtime error.
816}
817
818} // namespace rasterize_sdf_internal
819
820}
821}
822}
823
824#endif // OPENVDB_POINTS_RASTERIZE_ELLIPSOIDS_SDF_IMPL_HAS_BEEN_INCLUDED
#define OPENVDB_ASSERT(X)
Definition Assert.h:41
#define OPENVDB_NO_DEPRECATION_WARNING_END
Definition Platform.h:218
#define OPENVDB_NO_DEPRECATION_WARNING_BEGIN
Bracket code with OPENVDB_NO_DEPRECATION_WARNING_BEGIN/_END, to inhibit warnings about deprecated cod...
Definition Platform.h:217
#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
void setToRotation(const Quat< T > &q)
Set this matrix to the rotation matrix specified by the quaternion.
Definition Mat3.h:253
Mat3 transpose() const
returns transpose of this
Definition Mat3.h:454
Mat3 timesDiagonal(const Vec3< T > &diag) const
Treat diag as a diagonal matrix and return the product of this matrix with diag (from the right).
Definition Mat3.h:521
Vec3< T > col(int j) const
Get jth column, e.g. Vec3d v = m.col(0);.
Definition Mat3.h:168
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 length() const
Length of the vector.
Definition Vec3.h:201
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
Selectively extract and filter point data using a custom filter operator.
float Sqrt(float x)
Return the square root of a floating-point value.
Definition Math.h:832
Mat3< float > Mat3s
Definition Mat3.h:832
Vec3< double > Vec3d
Definition Vec3.h:665
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
Quat< float > Quats
Definition Quat.h:611
Type Pow2(Type x)
Return x2.
Definition Math.h:573
Definition PointRasterizeEllipsoidsSDFImpl.h:24
GridPtrVec rasterizeEllipsoids(const PointDataGridT &points, const SettingsT &settings, const typename SettingsT::FilterType &filter)
Definition PointRasterizeEllipsoidsSDFImpl.h:633
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:34
math::Vec3< float > Vec3f
Definition Types.h:55
double Real
Definition Types.h:40
@ GRID_LEVEL_SET
Definition Types.h:522
int64_t Int64
Definition Types.h:37
tree::Tree4< int64_t, 5, 4, 3 >::Type Int64Tree
Definition openvdb.h:58
uint64_t Index64
Definition Types.h:33
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:180
const char * typeNameAsString()
Definition Types.h:583
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:244
Definition PointRasterizeEllipsoidsSDFImpl.h:52
const size_t xform
Definition PointRasterizeEllipsoidsSDFImpl.h:64
bool hasWorldSpacePosition() const
Definition PointRasterizeEllipsoidsSDFImpl.h:62
const size_t positionws
Definition PointRasterizeEllipsoidsSDFImpl.h:64
EllipseIndicies(const points::AttributeSet::Descriptor &desc, const std::string &rotation, const std::string &pws, const bool xformIsQuat)
Definition PointRasterizeEllipsoidsSDFImpl.h:53
Definition PointRasterizeEllipsoidsSDFImpl.h:456
typename RadiusType::ValueType RadiusT
Definition PointRasterizeEllipsoidsSDFImpl.h:460
void operator()(const typename LeafManagerT::LeafRange &range)
Definition PointRasterizeEllipsoidsSDFImpl.h:570
static const Index DIM
Definition PointRasterizeEllipsoidsSDFImpl.h:462
void join(EllipseSurfaceMaskOp &other)
Definition PointRasterizeEllipsoidsSDFImpl.h:485
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:495
Vec3i getMaxDist() const
Definition PointRasterizeEllipsoidsSDFImpl.h:483
rasterize_sdf_internal::SurfaceMaskOp< MaskTreeT > BaseT
Definition PointRasterizeEllipsoidsSDFImpl.h:457
EllipseSurfaceMaskOp(const EllipseSurfaceMaskOp &other, tbb::split)
Definition PointRasterizeEllipsoidsSDFImpl.h:476
EllipseSurfaceMaskOp(const math::Transform &src, const math::Transform &trg, const RadiusType &rad, const Real halfband, const EllipseIndicies &indices)
Definition PointRasterizeEllipsoidsSDFImpl.h:464
points::PointDataTree::LeafNodeType PointDataLeaf
Definition PointRasterizeEllipsoidsSDFImpl.h:458
tree::LeafManager< const points::PointDataTree > LeafManagerT
Definition PointRasterizeEllipsoidsSDFImpl.h:459
points::AttributeHandle< XformT > RotationHandleT
Definition PointRasterizeEllipsoidsSDFImpl.h:461
EllipsoidTransfer< SdfT, PositionCodecT, RadiusType, FilterType, CPG > BaseT
Definition PointRasterizeEllipsoidsSDFImpl.h:413
void rasterizePoint(const Coord &ijk, const Index id, const CoordBBox &bounds)
Definition PointRasterizeEllipsoidsSDFImpl.h:440
bool startPointLeaf(const PointDataTree::LeafNodeType &leaf)
Definition PointRasterizeEllipsoidsSDFImpl.h:433
points::AttributeHandle< math::Mat3s > XformHandleT
Definition PointRasterizeEllipsoidsSDFImpl.h:414
EllipsoidTransferMat3(const size_t pidx, const Vec3i width, const RadiusType &rt, const math::Transform &source, const FilterType &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:416
EllipsoidTransferMat3(const EllipsoidTransferMat3 &other)
Definition PointRasterizeEllipsoidsSDFImpl.h:429
EllipsoidTransfer< SdfT, PositionCodecT, RadiusType, FilterType, CPG > BaseT
Definition PointRasterizeEllipsoidsSDFImpl.h:364
void rasterizePoint(const Coord &ijk, const Index id, const CoordBBox &bounds)
Definition PointRasterizeEllipsoidsSDFImpl.h:391
points::AttributeHandle< math::Quats > QuatHandleT
Definition PointRasterizeEllipsoidsSDFImpl.h:365
bool startPointLeaf(const PointDataTree::LeafNodeType &leaf)
Definition PointRasterizeEllipsoidsSDFImpl.h:384
EllipsoidTransferQuat(const size_t pidx, const Vec3i width, const RadiusType &rt, const math::Transform &source, const FilterType &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:367
EllipsoidTransferQuat(const EllipsoidTransferQuat &other)
Definition PointRasterizeEllipsoidsSDFImpl.h:380
rasterize_sdf_internal::SphericalTransfer< SdfT, PositionCodecT, RadiusType, FilterT, CPG > BaseT
Definition PointRasterizeEllipsoidsSDFImpl.h:94
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:106
double RealT
Definition PointRasterizeEllipsoidsSDFImpl.h:101
points::AttributeHandle< Vec3d > PwsHandleT
Definition PointRasterizeEllipsoidsSDFImpl.h:104
bool startPointLeaf(const PointDataTree::LeafNodeType &leaf)
Definition PointRasterizeEllipsoidsSDFImpl.h:125
void rasterizePoint(const Coord &ijk, const Index id, const CoordBBox &bounds, const Mat3s &xform)
Definition PointRasterizeEllipsoidsSDFImpl.h:134
EllipsoidTransfer(const EllipsoidTransfer &other)
Definition PointRasterizeEllipsoidsSDFImpl.h:120
points::AttributeHandle< Vec3f > PHandleT
Definition PointRasterizeEllipsoidsSDFImpl.h:103
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