1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3 //
4 /// @file GU_VDBPointTools.h
5 /// @author FX R&D OpenVDB team
6 ///
7 /// @brief Collection of PointIndexGrid helpers for Houdini
8 
9 #ifndef GU_VDBPOINTTOOLS_H_HAS_BEEN_INCLUDED
10 #define GU_VDBPOINTTOOLS_H_HAS_BEEN_INCLUDED
11 
12 #if defined(SESI_OPENVDB)
13     #include "GU_Detail.h"
14     #include "GU_DetailHandle.h"
15     #include "GU_PackedContext.h"
16     #include "GU_PackedFragment.h"
17     #include "GU_PackedGeometry.h"
18     #include "GU_PrimPacked.h"
19 #else
20     #include <GU/GU_Detail.h>
21     #include <GU/GU_DetailHandle.h>
22     #include <GU/GU_PackedContext.h>
23     #include <GU/GU_PackedFragment.h>
24     #include <GU/GU_PackedGeometry.h>
25     #include <GU/GU_PrimPacked.h>
26 #endif
27 #include <GA/GA_ElementGroup.h>
28 #include <UT/UT_SharedPtr.h>
29 #include <UT/UT_VectorTypes.h>
30 
31 #include <openvdb/Platform.h>
32 #include <openvdb/tools/PointIndexGrid.h>
33 #include <openvdb/tools/ParticleAtlas.h>
34 #include <openvdb/tools/PointsToMask.h>
35 
36 #include <vector>
37 
38 
39 /// @brief Houdini point attribute wrapper
40 template<typename VectorType>
41 struct GU_VDBPointList
42 {
43     using Ptr = UT_SharedPtr<GU_VDBPointList>;
44     using ConstPtr = UT_SharedPtr<const GU_VDBPointList>;
45 
46     using PosType = VectorType;
47     using ScalarType = typename PosType::value_type;
48 
49     GU_VDBPointList(const GU_Detail& detail, const GA_PointGroup* group = nullptr)
50         : mPositionHandle(detail.getP())
51         , mVelocityHandle()
52         , mRadiusHandle()
53         , mIndexMap(&detail.getP()->getIndexMap())
54         , mOffsets()
55         , mSize(mIndexMap->indexSize())
56     {
57         if (group) {
58             mSize = group->entries();
59             mOffsets.reserve(mSize);
60 
61             GA_Offset start, end;
62             GA_Range range(*group);
63             for (GA_Iterator it = range.begin(); it.blockAdvance(start, end); ) {
64                 for (GA_Offset off = start; off < end; ++off) {
65                     mOffsets.push_back(off);
66                 }
67             }
68 
69             getOffset = &GU_VDBPointList::offsetFromGroupMap;
70         } else if (mIndexMap->isTrivialMap()) {
71             getOffset = &GU_VDBPointList::offsetFromIndexCast;
72         } else {
73             getOffset = &GU_VDBPointList::offsetFromGeoMap;
74         }
75 
76         // Bind optional attributes
77 
78         GA_ROAttributeRef velRef = detail.findFloatTuple(GA_ATTRIB_POINT, GEO_STD_ATTRIB_VELOCITY, 3);
79         if (velRef.isValid()) {
80             mVelocityHandle.bind(velRef.getAttribute());
81         }
82 
83         GA_ROAttributeRef radRef = detail.findFloatTuple(GA_ATTRIB_POINT, GEO_STD_ATTRIB_PSCALE);
84         if (radRef.isValid()) {
85             mRadiusHandle.bind(radRef.getAttribute());
86         }
87     }
88 
89     static Ptr create(const GU_Detail& detail, const GA_PointGroup* group = nullptr)
90     {
91         return Ptr(new GU_VDBPointList(detail, group));
92     }
93 
sizeGU_VDBPointList94     size_t size() const { return mSize; }
95 
hasVelocityGU_VDBPointList96     bool hasVelocity() const { return mVelocityHandle.isValid(); }
hasRadiusGU_VDBPointList97     bool hasRadius() const { return mRadiusHandle.isValid(); }
98 
99     // Index access methods
100 
getPosGU_VDBPointList101     void getPos(size_t n, PosType& xyz) const {
102         getPosFromOffset((this->*getOffset)(n), xyz);
103     }
104 
getVelocityGU_VDBPointList105     void getVelocity(size_t n, PosType& v) const {
106         getVelocityFromOffset((this->*getOffset)(n), v);
107     }
108 
getRadiusGU_VDBPointList109     void getRadius(size_t n, ScalarType& r) const {
110         getRadiusFromOffset((this->*getOffset)(n), r);
111     }
112 
113     // Offset access methods
114 
offsetFromIndexGU_VDBPointList115     GA_Offset offsetFromIndex(size_t n) const {
116         return (this->*getOffset)(n);
117     }
118 
getPosFromOffsetGU_VDBPointList119     void getPosFromOffset(const GA_Offset offset, PosType& xyz) const {
120         const UT_Vector3 data = mPositionHandle.get(offset);
121         xyz[0] = ScalarType(data[0]);
122         xyz[1] = ScalarType(data[1]);
123         xyz[2] = ScalarType(data[2]);
124     }
125 
getVelocityFromOffsetGU_VDBPointList126     void getVelocityFromOffset(const GA_Offset offset, PosType& v) const {
127         const UT_Vector3 data = mVelocityHandle.get(offset);
128         v[0] = ScalarType(data[0]);
129         v[1] = ScalarType(data[1]);
130         v[2] = ScalarType(data[2]);
131     }
132 
getRadiusFromOffsetGU_VDBPointList133     void getRadiusFromOffset(const GA_Offset offset, ScalarType& r) const {
134         r = ScalarType(mRadiusHandle.get(offset));
135     }
136 
137 private:
138     // Disallow copying
139     GU_VDBPointList(const GU_VDBPointList&);
140     GU_VDBPointList& operator=(const GU_VDBPointList&);
141 
142     GA_Offset (GU_VDBPointList::* getOffset)(const size_t) const;
143 
offsetFromGeoMapGU_VDBPointList144     GA_Offset offsetFromGeoMap(const size_t n) const {
145         return mIndexMap->offsetFromIndex(GA_Index(n));
146     }
147 
offsetFromGroupMapGU_VDBPointList148     GA_Offset offsetFromGroupMap(const size_t n) const {
149         return mOffsets[n];
150     }
151 
offsetFromIndexCastGU_VDBPointList152     GA_Offset offsetFromIndexCast(const size_t n) const {
153         return GA_Offset(n);
154     }
155 
156     GA_ROHandleV3 mPositionHandle, mVelocityHandle;
157     GA_ROHandleF mRadiusHandle;
158     GA_IndexMap const * const mIndexMap;
159     std::vector<GA_Offset> mOffsets;
160     size_t mSize;
161 }; // GU_VDBPointList
162 
163 
164 ////////////////////////////////////////
165 
166 
167 // PointIndexGrid utility methods
168 
169 
170 namespace GU_VDBPointToolsInternal {
171 
172 template<typename PointArrayType>
173 struct IndexToOffsetOp {
IndexToOffsetOpIndexToOffsetOp174     IndexToOffsetOp(const PointArrayType& points): mPointList(&points) {}
175 
176     template <typename LeafT>
operatorIndexToOffsetOp177     void operator()(LeafT &leaf, size_t /*leafIndex*/) const {
178         typename LeafT::IndexArray& indices = leaf.indices();
179         for (size_t n = 0, N = indices.size(); n < N; ++n) {
180              indices[n] = static_cast<typename LeafT::ValueType::IntType>(
181                  mPointList->offsetFromIndex(GA_Index{indices[n]}));
182         }
183     }
184     PointArrayType const * const mPointList;
185 };
186 
187 
188 struct PackedMaskConstructor
189 {
PackedMaskConstructorPackedMaskConstructor190     PackedMaskConstructor(const std::vector<const GA_Primitive*>& prims,
191         const openvdb::math::Transform& xform)
192         : mPrims(prims.empty() ? nullptr : &prims.front())
193         , mXForm(xform)
194         , mMaskGrid(new openvdb::MaskGrid(false))
195     {
196         mMaskGrid->setTransform(mXForm.copy());
197     }
198 
PackedMaskConstructorPackedMaskConstructor199     PackedMaskConstructor(PackedMaskConstructor& rhs, tbb::split)
200         : mPrims(rhs.mPrims)
201         , mXForm(rhs.mXForm)
202         , mMaskGrid(new openvdb::MaskGrid(false))
203     {
204         mMaskGrid->setTransform(mXForm.copy());
205     }
206 
getMaskGridPackedMaskConstructor207     openvdb::MaskGrid::Ptr getMaskGrid() { return mMaskGrid; }
208 
joinPackedMaskConstructor209     void join(PackedMaskConstructor& rhs) { mMaskGrid->tree().topologyUnion(rhs.mMaskGrid->tree()); }
210 
operatorPackedMaskConstructor211     void operator()(const tbb::blocked_range<size_t>& range)
212     {
213         GU_PackedContext packedcontext;
214 
215         for (size_t n = range.begin(), N = range.end(); n < N; ++n) {
216             const GA_Primitive *prim = mPrims[n];
217             if (!prim || !GU_PrimPacked::isPackedPrimitive(*prim)) continue;
218 
219             const GU_PrimPacked * pprim = static_cast<const GU_PrimPacked*>(prim);
220 
221             GU_Detail tmpdetail;
222             const GU_Detail *detailtouse;
223 
224             GU_DetailHandleAutoReadLock readlock(pprim->getPackedDetail(packedcontext));
225 
226             UT_Matrix4D mat;
227             pprim->getFullTransform4(mat);
228             if (mat.isIdentity() && readlock.isValid() && readlock.getGdp()) {
229                 detailtouse = readlock.getGdp();
230             } else {
231                 pprim->unpackWithContext(tmpdetail, packedcontext);
232                 detailtouse = &tmpdetail;
233             }
234 
235             GU_VDBPointList<openvdb::Vec3R>  points(*detailtouse);
236             openvdb::MaskGrid::Ptr grid = openvdb::tools::createPointMask(points, mXForm);
237             mMaskGrid->tree().topologyUnion(grid->tree());
238         }
239     }
240 
241 private:
242     GA_Primitive const * const * const  mPrims;
243     openvdb::math::Transform            mXForm;
244     openvdb::MaskGrid::Ptr              mMaskGrid;
245 }; // struct PackedMaskConstructor
246 
247 
248 inline void
getPackedPrimitiveOffsets(const GU_Detail & detail,std::vector<const GA_Primitive * > & primitives)249 getPackedPrimitiveOffsets(const GU_Detail& detail, std::vector<const GA_Primitive*>& primitives)
250 {
251     const GA_Size numPacked = GU_PrimPacked::countPackedPrimitives(detail);
252 
253     primitives.clear();
254     primitives.reserve(size_t(numPacked));
255 
256     if (numPacked != GA_Size(0)) {
257         GA_Offset start, end;
258         GA_Range range = detail.getPrimitiveRange();
259         const GA_PrimitiveList& primList = detail.getPrimitiveList();
260 
261         for (GA_Iterator it = range.begin(); it.blockAdvance(start, end); ) {
262             for (GA_Offset off = start; off < end; ++off) {
263 
264                 const GA_Primitive *prim = primList.get(off);
265 
266                 if (prim && GU_PrimPacked::isPackedPrimitive(*prim)) {
267                     primitives.push_back(prim);
268                 }
269             }
270         }
271     }
272 }
273 
274 } // namespace GU_VDBPointToolsInternal
275 
276 
277 ////////////////////////////////////////
278 
279 
280 /// @brief    Utility method to construct a GU_VDBPointList.
281 /// @details  The GU_VDBPointList is compatible with the PointIndexGrid and ParticleAtals structures.
282 inline GU_VDBPointList<openvdb::Vec3s>::Ptr
283 GUvdbCreatePointList(const GU_Detail& detail, const GA_PointGroup* pointGroup = nullptr)
284 {
285     return GU_VDBPointList<openvdb::Vec3s>::create(detail, pointGroup);
286 }
287 
288 
289 /// @brief  Utility method to change point indices into Houdini geometry offsets.
290 /// @note   PointIndexGrid's that store Houdini geometry offsets are not
291 ///         safe to write to disk, offsets are not guaranteed to be immutable
292 ///         under defragmentation operations or I/O.
293 template<typename PointIndexTreeType, typename PointArrayType>
294 inline void
GUvdbConvertIndexToOffset(PointIndexTreeType & tree,const PointArrayType & points)295 GUvdbConvertIndexToOffset(PointIndexTreeType& tree, const PointArrayType& points)
296 {
297     openvdb::tree::LeafManager<PointIndexTreeType> leafnodes(tree);
298     leafnodes.foreach(GU_VDBPointToolsInternal::IndexToOffsetOp<PointArrayType>(points));
299 }
300 
301 
302 /// @brief    Utility method to construct a PointIndexGrid.
303 /// @details  The PointIndexGrid supports fast spatial queries for points.
304 inline openvdb::tools::PointIndexGrid::Ptr
305 GUvdbCreatePointIndexGrid(
306     const openvdb::math::Transform& xform,
307     const GU_Detail& detail,
308     const GA_PointGroup* pointGroup = nullptr)
309 {
310     GU_VDBPointList<openvdb::Vec3s> points(detail, pointGroup);
311     return openvdb::tools::createPointIndexGrid<openvdb::tools::PointIndexGrid>(points, xform);
312 }
313 
314 
315 /// @brief    Utility method to construct a PointIndexGrid.
316 /// @details  The PointIndexGrid supports fast spatial queries for points.
317 template<typename PointArrayType>
318 inline openvdb::tools::PointIndexGrid::Ptr
GUvdbCreatePointIndexGrid(const openvdb::math::Transform & xform,const PointArrayType & points)319 GUvdbCreatePointIndexGrid(const openvdb::math::Transform& xform, const PointArrayType& points)
320 {
321     return openvdb::tools::createPointIndexGrid<openvdb::tools::PointIndexGrid>(points, xform);
322 }
323 
324 
325 /// @brief    Utility method to construct a ParticleAtals.
326 /// @details  The ParticleAtals supports fast spatial queries for particles.
327 template<typename ParticleArrayType>
328 inline openvdb::tools::ParticleIndexAtlas::Ptr
GUvdbCreateParticleAtlas(const double minVoxelSize,const ParticleArrayType & particles)329 GUvdbCreateParticleAtlas(const double minVoxelSize, const ParticleArrayType& particles)
330 {
331     using ParticleIndexAtlas = openvdb::tools::ParticleIndexAtlas;
332     ParticleIndexAtlas::Ptr atlas(new ParticleIndexAtlas());
333 
334     if (particles.hasRadius()) {
335         atlas->construct(particles, minVoxelSize);
336     }
337 
338     return atlas;
339 }
340 
341 
342 /// @brief    Utility method to construct a boolean PointMaskGrid
343 /// @details  This method supports packed points.
344 inline openvdb::MaskGrid::Ptr
345 GUvdbCreatePointMaskGrid(
346     const openvdb::math::Transform& xform,
347     const GU_Detail& detail,
348     const GA_PointGroup* pointGroup = nullptr)
349 {
350     std::vector<const GA_Primitive*> packed;
351     GU_VDBPointToolsInternal::getPackedPrimitiveOffsets(detail, packed);
352 
353     if (!packed.empty()) {
354         GU_VDBPointToolsInternal::PackedMaskConstructor op(packed, xform);
355         tbb::parallel_reduce(tbb::blocked_range<size_t>(0, packed.size()), op);
356         return op.getMaskGrid();
357     }
358 
359     GU_VDBPointList<openvdb::Vec3R> points(detail, pointGroup);
360     return openvdb::tools::createPointMask(points, xform);
361 }
362 
363 
364 /// @brief  Utility method to construct a PointIndexGrid that stores
365 ///         Houdini geometry offsets.
366 ///
367 /// @note  PointIndexGrid's that store Houdini geometry offsets are not
368 ///        safe to write to disk, offsets are not guaranteed to be immutable
369 ///        under defragmentation operations or I/O.
370 inline openvdb::tools::PointIndexGrid::Ptr
371 GUvdbCreatePointOffsetGrid(
372     const openvdb::math::Transform& xform,
373     const GU_Detail& detail,
374     const GA_PointGroup* pointGroup = nullptr)
375 {
376     GU_VDBPointList<openvdb::Vec3s> points(detail, pointGroup);
377 
378     openvdb::tools::PointIndexGrid::Ptr grid =
379         openvdb::tools::createPointIndexGrid<openvdb::tools::PointIndexGrid>(points, xform);
380 
381     GUvdbConvertIndexToOffset(grid->tree(), points);
382 
383     return grid;
384 }
385 
386 
387 #endif // GU_VDBPOINTTOOLS_H_HAS_BEEN_INCLUDED
388