OpenVDB 11.0.0
Loading...
Searching...
No Matches
PointReplicateImpl.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 Nick Avramoussis
5///
6/// @file PointReplicateImpl.h
7///
8
9#ifndef OPENVDB_POINTS_POINT_REPLICATE_IMPL_HAS_BEEN_INCLUDED
10#define OPENVDB_POINTS_POINT_REPLICATE_IMPL_HAS_BEEN_INCLUDED
11
12namespace openvdb {
14namespace OPENVDB_VERSION_NAME {
15namespace points {
16
17template <typename PointDataGridT>
18typename PointDataGridT::Ptr
19replicate(const PointDataGridT& source,
20 const Index multiplier,
21 const std::vector<std::string>& attributes,
22 const std::string& scaleAttribute,
23 const std::string& replicationIndex)
24{
25 // The copy iterator, used to transfer array values from the source grid
26 // to the target (replicated grid).
27 struct CopyIter
28 {
29#ifdef __clang__
30 // Silence incorrect clang warning
31 _Pragma("clang diagnostic push")
32 _Pragma("clang diagnostic ignored \"-Wunused-local-typedef\"")
33 using GetIncrementCB = std::function<Index(const Index)>;
34 _Pragma("clang diagnostic pop")
35#else
36 using GetIncrementCB = std::function<Index(const Index)>;
37#endif
38
39 CopyIter(const Index end, const GetIncrementCB& cb)
40 : mIt(0), mEnd(0), mSource(0), mSourceEnd(end), mCallback(cb) {
41 mEnd = mCallback(mSource);
42 }
43
44 operator bool() const { return mSource < mSourceEnd; }
45
46 CopyIter& operator++()
47 {
48 ++mIt;
49 // If we have hit the end for current source idx, increment the source idx
50 // moving end to the new position for the next source with a non zero
51 // number of new values
52 while (mIt >= mEnd) {
53 ++mSource;
54 if (*this) mEnd += mCallback(mSource);
55 else return *this;
56 }
57
58 return *this;
59 }
60
61 Index sourceIndex() const { assert(*this); return mSource; }
62 Index targetIndex() const { assert(*this); return mIt; }
63
64 private:
65 Index mIt, mEnd, mSource;
66 const Index mSourceEnd;
67 const GetIncrementCB mCallback;
68 }; // struct CopyIter
69
70
71 // We want the topology and index values of the leaf nodes, but we
72 // DON'T want to copy the arrays. This should only shallow copy the
73 // descriptor and arrays
74 PointDataGrid::Ptr points = source.deepCopy();
75
76 auto iter = source.tree().cbeginLeaf();
77 if (!iter) return points;
78
79 const AttributeSet::Descriptor& sourceDescriptor =
80 iter->attributeSet().descriptor();
81
82 // verify position
83
84 const size_t ppos = sourceDescriptor.find("P");
85 assert(ppos != AttributeSet::INVALID_POS);
86
87 // build new dummy attribute set
88
90 std::vector<size_t> attribsToDrop;
91 if (!attributes.empty()) {
92 for (const auto& nameIdxPair : sourceDescriptor.map()) {
93 if (std::find(attributes.begin(), attributes.end(), nameIdxPair.first) != attributes.end()) continue;
94 if (nameIdxPair.first == "P") continue;
95 attribsToDrop.emplace_back(nameIdxPair.second);
96 }
97 set.reset(new AttributeSet(iter->attributeSet(), 1));
98 }
99 else {
100 set.reset(new AttributeSet(AttributeSet::Descriptor::create(sourceDescriptor.type(ppos))));
101 }
102
103 if (!replicationIndex.empty()) {
104 // don't copy replicationIndex attribute if it exists
105 // as it'll be overwritten and we create it after
106 const size_t replIdxIdx = sourceDescriptor.find(replicationIndex);
107 if (replIdxIdx != AttributeSet::INVALID_POS) attribsToDrop.emplace_back(replIdxIdx);
108 }
109 set->dropAttributes(attribsToDrop);
110
111 // validate replication attributes
112
113 size_t replicationIdx = AttributeSet::INVALID_POS;
114 if (!replicationIndex.empty()) {
115 set->appendAttribute(replicationIndex, TypedAttributeArray<int32_t>::attributeType());
116 replicationIdx = set->descriptor().find(replicationIndex);
117 }
118
119 AttributeSet::DescriptorPtr descriptor = set->descriptorPtr();
120
121 const size_t scaleIdx = !scaleAttribute.empty() ?
122 sourceDescriptor.find(scaleAttribute) : AttributeSet::INVALID_POS;
123
124 openvdb::tree::LeafManager<const PointDataTree> sourceManager(source.tree());
125 openvdb::tree::LeafManager<openvdb::points::PointDataTree> manager(points->tree());
126
127 manager.foreach(
128 [&](PointDataTree::LeafNodeType& leaf, size_t pos)
129 {
130 using ValueType = PointDataTree::ValueType;
131
132 const auto& sourceLeaf = sourceManager.leaf(pos);
133 // @note This really shoudn't return uint64_t as AttributeArray's size is
134 // limited to the max of a uint32_t...
135 assert(sourceLeaf.pointCount() < Index64(std::numeric_limits<Index>::max()));
136 const Index sourceCount = static_cast<Index>(sourceLeaf.pointCount());
137
138 Index uniformMultiplier = multiplier;
139 AttributeHandle<float>::UniquePtr scaleHandle(nullptr);
140 bool useScale = scaleIdx != AttributeSet::INVALID_POS;
141 if (useScale) {
142 scaleHandle = std::make_unique<AttributeHandle<float>>
143 (sourceLeaf.constAttributeArray(scaleIdx));
144 }
145 // small lambda that returns the amount of points to generate
146 // based on a scale. Should only be called if useScale is true,
147 // otherwise the scaleHandle will be reset or null
148 auto getPointsToGenerate = [&](const Index index) -> Index {
149 const float scale = std::max(0.0f, scaleHandle->get(index));
150 return static_cast<Index>
151 (math::Round(static_cast<float>(multiplier) * scale));
152 };
153
154 // if uniform, update the multiplier and don't bother using the scale attribute
155
156 if (useScale && scaleHandle->isUniform()) {
157 uniformMultiplier = getPointsToGenerate(0);
158 scaleHandle.reset();
159 useScale = false;
160 }
161
162 // get the new count and build the new offsets - do this in this loop so we
163 // don't have to cache the offset vector. Note that the leaf offsets become
164 // invalid until leaf.replaceAttributeSet is called and should not be used
165
166 Index total = 0;
167
168 if (useScale) {
169 for (auto iter = sourceLeaf.cbeginValueAll(); iter; ++iter) {
170 for (auto piter = sourceLeaf.beginIndexVoxel(iter.getCoord());
171 piter; ++piter) { total += getPointsToGenerate(*piter); }
172 leaf.setOffsetOnly(iter.pos(), total);
173 }
174 }
175 else {
176 total = uniformMultiplier * sourceCount;
177
178 // with a uniform multiplier, just multiply each voxel value
179 auto* data = leaf.buffer().data();
180 for (size_t i = 0; i < leaf.buffer().size(); ++i) {
181 const ValueType::IntType value = data[i];
182 data[i] = value * uniformMultiplier;
183 }
184 }
185
186 // turn voxels off if no points
187 leaf.updateValueMask();
188 const AttributeSet& sourceSet = sourceLeaf.attributeSet();
189
190 std::unique_ptr<openvdb::points::AttributeSet> newSet
191 (new AttributeSet(*set, total));
192
193 auto copy = [&](const std::string& name)
194 {
195 const auto* sourceArray = sourceSet.getConst(name);
196 assert(sourceArray);
197
198 // manually expand so that copyValues() doesn't expand and fill the array -
199 // we don't want to unnecessarily zero initialize the target values as we know
200 // we're going to write to all of them.
201 auto* array = newSet->get(name);
202 assert(array);
203 array->expand(/*fill*/false);
204
205 if (useScale) {
206 const CopyIter iter(sourceCount, [&](const Index i) { return getPointsToGenerate(i); });
207 array->copyValues(*sourceArray, iter);
208 }
209 else {
210 const CopyIter iter(sourceCount, [&](const Index) { return uniformMultiplier; });
211 array->copyValues(*sourceArray, iter);
212 }
213 };
214
215 copy("P");
216 for (const auto& iter : descriptor->map()) {
217 if (iter.first == "P") continue;
218 if (iter.first == replicationIndex) continue;
219 copy(iter.first);
220 }
221
222 // assign the replication idx if requested
223
224 if (replicationIdx != AttributeSet::INVALID_POS && total > 0) {
226 idxHandle(*newSet->get(replicationIdx), /*expand*/false);
227 idxHandle.expand(/*fill*/false);
228 assert(idxHandle.size() == total);
229
230
231 Index offset = 0;
232
233 if (useScale) {
234 for (Index i = 0; i < sourceCount; ++i) {
235 const Index pointRepCount = getPointsToGenerate(i);
236 for (Index j = 0; j < pointRepCount; ++j) {
237 idxHandle.set(offset++, j);
238 }
239 }
240 }
241 else {
242 while (offset < total) {
243 for (Index j = 0; j < uniformMultiplier; ++j) {
244 idxHandle.set(offset++, j);
245 }
246 }
247 }
248 }
249
250 leaf.replaceAttributeSet(newSet.release(), /*mismatch*/true);
251 });
252
253 if (!scaleAttribute.empty()) {
254 tools::pruneInactive(points->tree());
255 }
256
257 return points;
258}
259
260template <typename PointDataGridT>
261typename PointDataGridT::Ptr
262replicate(const PointDataGridT& source,
263 const Index multiplier,
264 const std::string& scaleAttribute,
265 const std::string& replicationIndex)
266{
267 auto iter = source.tree().cbeginLeaf();
268 if (!iter) return source.deepCopy();
269
270 const openvdb::points::AttributeSet::Descriptor& sourceDescriptor =
271 iter->attributeSet().descriptor();
272
273 std::vector<std::string> attribs;
274 attribs.reserve(sourceDescriptor.map().size());
275 for (const auto& namepos : sourceDescriptor.map()) {
276 attribs.emplace_back(namepos.first);
277 }
278
279 return replicate(source, multiplier, attribs, scaleAttribute, replicationIndex);
280}
281
282
283} // namespace points
284} // namespace OPENVDB_VERSION_NAME
285} // namespace openvdb
286
287#endif // OPENVDB_POINTS_POINT_REPLICATE_IMPL_HAS_BEEN_INCLUDED
SharedPtr< Grid > Ptr
Definition Grid.h:573
Index size() const
Definition AttributeArray.h:855
ValueType get(Index n, Index m=0) const
Definition AttributeArray.h:2185
bool isUniform() const
Definition AttributeArray.h:2211
std::unique_ptr< Handle > UniquePtr
Definition AttributeArray.h:837
An immutable object that stores name, type and AttributeSet position for a constant collection of att...
Definition AttributeSet.h:312
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 NamePair & type(size_t pos) const
Return the name of the attribute array's type.
const NameToPosMap & map() const
Return a reference to the name-to-position map.
Definition AttributeSet.h:416
Ordered collection of uniquely-named attribute arrays.
Definition AttributeSet.h:39
const AttributeArray * getConst(const std::string &name) const
Return a pointer to the attribute array whose name is name or a null pointer if no match is found.
std::shared_ptr< AttributeSet > Ptr
Definition AttributeSet.h:43
std::shared_ptr< Descriptor > DescriptorPtr
Definition AttributeSet.h:49
Write-able version of AttributeHandle.
Definition AttributeArray.h:904
void set(Index n, const ValueType &value)
Definition AttributeArray.h:2242
void expand(bool fill=true)
If this array is uniform, replace it with an array of length size().
Definition AttributeArray.h:2254
Typed class for storing attribute data.
Definition AttributeArray.h:545
typename RootNodeType::ValueType ValueType
Definition Tree.h:184
typename RootNodeType::LeafNodeType LeafNodeType
Definition Tree.h:186
PointDataGridT::Ptr replicate(const PointDataGridT &source, const Index multiplier, const std::vector< std::string > &attributes, const std::string &scaleAttribute="", const std::string &replicationIndex="")
Replicates points provided in a source grid into a new grid, transfering and creating attributes foun...
Definition PointReplicateImpl.h:19
Index32 Index
Definition Types.h:54
uint64_t Index64
Definition Types.h:53
Definition Exceptions.h:13
#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:212