OpenVDB 11.0.0

Loading...
Searching...
No Matches
PointSampleImpl.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, Francisco Gochez, Dan Bailey
5///
6/// @file PointSampleImpl.h
7///
8
9#ifndef OPENVDB_POINTS_POINT_SAMPLE_IMPL_HAS_BEEN_INCLUDED
10#define OPENVDB_POINTS_POINT_SAMPLE_IMPL_HAS_BEEN_INCLUDED
11
12 namespace openvdb {
13 OPENVDB_USE_VERSION_NAMESPACE
14 namespace OPENVDB_VERSION_NAME {
15 namespace points {
16
17/// @cond OPENVDB_DOCS_INTERNAL
18
19 namespace point_sample_internal {
20
21
22 template<typename FromType, typename ToType>
23 struct CompatibleTypes { enum { value = std::is_constructible<ToType, FromType>::value }; };
24
25// Specializations for types that can be converted from source grid to target attribute
26 template<typename T> struct CompatibleTypes<
27 T, T> { enum { value = true }; };
28 template<typename T> struct CompatibleTypes<
29 T, math::Vec2<T>> { enum { value = true }; };
30 template<typename T> struct CompatibleTypes<
31 T, math::Vec3<T>> { enum { value = true }; };
32 template<typename T> struct CompatibleTypes<
33 T, math::Vec4<T>> { enum { value = true }; };
34 template<typename T> struct CompatibleTypes<
35 math::Vec2<T>, math::Vec2<T>> { enum { value = true }; };
36 template<typename T> struct CompatibleTypes<
37 math::Vec3<T>, math::Vec3<T>> { enum { value = true }; };
38 template<typename T> struct CompatibleTypes<
39 math::Vec4<T>, math::Vec4<T>> { enum { value = true }; };
40 template<typename T0, typename T1> struct CompatibleTypes<
41 math::Vec2<T0>, math::Vec2<T1>> { enum { value = CompatibleTypes<T0, T1>::value }; };
42 template<typename T0, typename T1> struct CompatibleTypes<
43 math::Vec3<T0>, math::Vec3<T1>> { enum { value = CompatibleTypes<T0, T1>::value }; };
44 template<typename T0, typename T1> struct CompatibleTypes<
45 math::Vec4<T0>, math::Vec4<T1>> { enum { value = CompatibleTypes<T0, T1>::value }; };
46 template<typename T> struct CompatibleTypes<
47 ValueMask, T> { enum { value = CompatibleTypes<bool, T>::value }; };
48
49
50// Ability to access the Order and Staggered template parameter from tools::Sampler<Order, Staggered>
51 template <typename T> struct SamplerTraits {
52 static const size_t Order = 0;
53 static const bool Staggered = false;
54};
55 template <size_t T0, bool T1> struct SamplerTraits<tools::Sampler<T0, T1>> {
56 static const size_t Order = T0;
57 static const bool Staggered = T1;
58};
59
60
61// default sampling is incompatible, so throw an error
62 template <typename ValueT, typename SamplerT, typename AccessorT, bool Round, bool Compatible = false>
63 struct SampleWithRoundingOp
64{
65 static inline void sample(ValueT&, const AccessorT&, const Vec3d&)
66 {
67 std::ostringstream ostr;
68 ostr << "Cannot sample a " << typeNameAsString<typename AccessorT::ValueType>()
69 << " grid on to a " << typeNameAsString<ValueT>() << " attribute";
70 OPENVDB_THROW(TypeError, ostr.str());
71 }
72};
73// partial specialization to handle sampling and rounding of compatible conversion
74 template <typename ValueT, typename SamplerT, typename AccessorT>
75 struct SampleWithRoundingOp<ValueT, SamplerT, AccessorT, /*Round=*/true, /*Compatible=*/true>
76{
77 static inline void sample(ValueT& value, const AccessorT& accessor, const Vec3d& position)
78 {
79 value = ValueT(math::Round(SamplerT::sample(accessor, position)));
80 }
81};
82// partial specialization to handle sampling and simple casting of compatible conversion
83 template <typename ValueT, typename SamplerT, typename AccessorT>
84 struct SampleWithRoundingOp<ValueT, SamplerT, AccessorT, /*Round=*/false, /*Compatible=*/true>
85{
86 static inline void sample(ValueT& value, const AccessorT& accessor, const Vec3d& position)
87 {
88 value = ValueT(SamplerT::sample(accessor, position));
89 }
90};
91
92
93 template <typename PointDataGridT, typename SamplerT, typename FilterT, typename InterrupterT>
94 class PointDataSampler
95{
96 public:
97 PointDataSampler(size_t order,
98 PointDataGridT& points,
99 const SamplerT& sampler,
100 const FilterT& filter,
101 InterrupterT* const interrupter,
102 const bool threaded)
103 : mOrder(order)
104 , mPoints(points)
105 , mSampler(sampler)
106 , mFilter(filter)
107 , mInterrupter(interrupter)
108 , mThreaded(threaded) { }
109
110 private:
111 // No-op transformation
112 struct AlignedTransform
113 {
114 inline Vec3d transform(const Vec3d& position) const { return position; }
115 }; // struct AlignedTransform
116
117 // Re-sample world-space position from source to target transforms
118 struct NonAlignedTransform
119 {
120 NonAlignedTransform(const math::Transform& source, const math::Transform& target)
121 : mSource(source)
122 , mTarget(target) { }
123
124 inline Vec3d transform(const Vec3d& position) const
125 {
126 return mSource.worldToIndex(mTarget.indexToWorld(position));
127 }
128
129 private:
130 const math::Transform& mSource;
131 const math::Transform& mTarget;
132 }; // struct NonAlignedTransform
133
134 // A simple convenience wrapper that contains the source grid accessor and the sampler
135 template <typename ValueT, typename SourceGridT, typename GridSamplerT>
136 struct SamplerWrapper
137 {
138 using ValueType = ValueT;
139 using SourceValueType = typename SourceGridT::ValueType;
140 using SourceAccessorT = typename SourceGridT::ConstAccessor;
141
142 // can only sample from a bool or mask grid using a PointSampler
143 static const bool SourceIsBool = std::is_same<SourceValueType, bool>::value ||
144 std::is_same<SourceValueType, ValueMask>::value;
145 static const bool OrderIsZero = SamplerTraits<GridSamplerT>::Order == 0;
146 static const bool IsValid = !SourceIsBool || OrderIsZero;
147
148 SamplerWrapper(const SourceGridT& sourceGrid, const SamplerT& sampler)
149 : mAccessor(sourceGrid.getConstAccessor())
150 , mSampler(sampler) { }
151
152 // note that creating a new accessor from the underlying tree is faster than
153 // copying an existing accessor
154 SamplerWrapper(const SamplerWrapper& other)
155 : mAccessor(other.mAccessor.tree())
156 , mSampler(other.mSampler) { }
157
158 template <bool IsValidT = IsValid>
159 inline typename std::enable_if<IsValidT, ValueT>::type
160 sample(const Vec3d& position) const {
161 return mSampler.template sample<ValueT, GridSamplerT, SourceAccessorT>(
162 mAccessor, position);
163 }
164
165 template <bool IsValidT = IsValid>
166 inline typename std::enable_if<!IsValidT, ValueT>::type
167 sample(const Vec3d& /*position*/) const {
168 OPENVDB_THROW(RuntimeError, "Cannot sample bool grid with BoxSampler or QuadraticSampler.");
169 }
170
171 private:
172 SourceAccessorT mAccessor;
173 const SamplerT& mSampler;
174 }; // struct SamplerWrapper
175
176 template <typename SamplerWrapperT, typename TransformerT>
177 inline void doSample(const SamplerWrapperT& sampleWrapper, const Index targetIndex,
178 const TransformerT& transformer)
179 {
180 using PointDataTreeT = typename PointDataGridT::TreeType;
181 using LeafT = typename PointDataTreeT::LeafNodeType;
182 using LeafManagerT = typename tree::LeafManager<PointDataTreeT>;
183
184 const auto& filter(mFilter);
185 const auto& interrupter(mInterrupter);
186
187 auto sampleLambda = [targetIndex, &sampleWrapper, &transformer, &filter, &interrupter](
188 LeafT& leaf, size_t /*idx*/)
189 {
190 using TargetHandleT = AttributeWriteHandle<typename SamplerWrapperT::ValueType>;
191
192 if (util::wasInterrupted(interrupter)) {
193 thread::cancelGroupExecution();
194 return;
195 }
196
197 SamplerWrapperT newSampleWrapper(sampleWrapper);
198 auto positionHandle = AttributeHandle<Vec3f>::create(leaf.constAttributeArray("P"));
199 auto targetHandle = TargetHandleT::create(leaf.attributeArray(targetIndex));
200 for (auto iter = leaf.beginIndexOn(filter); iter; ++iter) {
201 const Vec3d position = transformer.transform(
202 positionHandle->get(*iter) + iter.getCoord().asVec3d());
203 targetHandle->set(*iter, newSampleWrapper.sample(position));
204 }
205 };
206
207 LeafManagerT leafManager(mPoints.tree());
208
209 if (mInterrupter) mInterrupter->start();
210
211 leafManager.foreach(sampleLambda, mThreaded);
212
213 if (mInterrupter) mInterrupter->end();
214 }
215
216 template <typename SourceGridT, typename SamplerWrapperT>
217 inline void resolveTransform(const SourceGridT& sourceGrid, const SamplerWrapperT& sampleWrapper,
218 const Index targetIndex)
219 {
220 const auto& sourceTransform = sourceGrid.constTransform();
221 const auto& pointsTransform = mPoints.constTransform();
222
223 if (sourceTransform == pointsTransform) {
224 AlignedTransform transformer;
225 doSample(sampleWrapper, targetIndex, transformer);
226 } else {
227 NonAlignedTransform transformer(sourceTransform, pointsTransform);
228 doSample(sampleWrapper, targetIndex, transformer);
229 }
230 }
231
232 template <typename SourceGridT, typename TargetValueT, size_t Order>
233 inline void resolveStaggered(const SourceGridT& sourceGrid, const Index targetIndex)
234 {
235 using SamplerWrapperT = SamplerWrapper<TargetValueT, SourceGridT, tools::Sampler<Order, false>>;
236 using StaggeredSamplerWrapperT = SamplerWrapper<TargetValueT, SourceGridT, tools::Sampler<Order, true>>;
237
238 using SourceValueType = typename SourceGridT::ValueType;
239 if (VecTraits<SourceValueType>::Size == 3 && sourceGrid.getGridClass() == GRID_STAGGERED) {
240 StaggeredSamplerWrapperT sampleWrapper(sourceGrid, mSampler);
241 resolveTransform(sourceGrid, sampleWrapper, targetIndex);
242 } else {
243 SamplerWrapperT sampleWrapper(sourceGrid, mSampler);
244 resolveTransform(sourceGrid, sampleWrapper, targetIndex);
245 }
246 }
247
248 public:
249 template <typename SourceGridT, typename TargetValueT = typename SourceGridT::ValueType>
250 inline void sample(const SourceGridT& sourceGrid, Index targetIndex)
251 {
252 using SourceValueType = typename SourceGridT::ValueType;
253 static const bool SourceIsMask = std::is_same<SourceValueType, bool>::value ||
254 std::is_same<SourceValueType, ValueMask>::value;
255
256 if (SourceIsMask || mOrder == 0) {
257 resolveStaggered<SourceGridT, TargetValueT, 0>(sourceGrid, targetIndex);
258 } else if (mOrder == 1) {
259 resolveStaggered<SourceGridT, TargetValueT, 1>(sourceGrid, targetIndex);
260 } else if (mOrder == 2) {
261 resolveStaggered<SourceGridT, TargetValueT, 2>(sourceGrid, targetIndex);
262 }
263 }
264
265 private:
266 size_t mOrder;
267 PointDataGridT& mPoints;
268 const SamplerT& mSampler;
269 const FilterT& mFilter;
270 InterrupterT* const mInterrupter;
271 const bool mThreaded;
272}; // class PointDataSampler
273
274
275 template <typename PointDataGridT, typename ValueT>
276 struct AppendAttributeOp
277{
278 static void append(PointDataGridT& points, const Name& attribute)
279 {
280 appendAttribute<ValueT>(points.tree(), attribute);
281 }
282};
283// partial specialization to disable attempts to append attribute type of DummySampleType
284 template <typename PointDataGridT>
285 struct AppendAttributeOp<PointDataGridT, DummySampleType>
286{
287 static void append(PointDataGridT&, const Name&) { }
288};
289
290} // namespace point_sample_internal
291
292/// @endcond
293
294////////////////////////////////////////
295
296
297 template<typename ValueT, typename SamplerT, typename AccessorT>
298ValueT SampleWithRounding::sample(const AccessorT& accessor, const Vec3d& position) const
299{
300 using namespace point_sample_internal;
301 using SourceValueT = typename AccessorT::ValueType;
302 static const bool staggered = SamplerTraits<SamplerT>::Staggered;
303 static const bool compatible = CompatibleTypes</*from=*/SourceValueT, /*to=*/ValueT>::value &&
304 (!staggered || (staggered && VecTraits<SourceValueT>::Size == 3));
305 static const bool round = std::is_floating_point<SourceValueT>::value &&
306 std::is_integral<ValueT>::value;
307 ValueT value;
308 SampleWithRoundingOp<ValueT, SamplerT, AccessorT, round, compatible>::sample(
309 value, accessor, position);
310 return value;
311}
312
313
314////////////////////////////////////////
315
316
317 template<typename PointDataGridT, typename SourceGridT, typename TargetValueT,
318 typename SamplerT, typename FilterT, typename InterrupterT>
319 inline void sampleGrid( size_t order,
320 PointDataGridT& points,
321 const SourceGridT& sourceGrid,
322 const Name& targetAttribute,
323 const FilterT& filter,
324 const SamplerT& sampler,
325 InterrupterT* const interrupter,
326 const bool threaded)
327{
328 using point_sample_internal::AppendAttributeOp;
329 using point_sample_internal::PointDataSampler;
330
331 // use the name of the grid if no target attribute name supplied
332 Name attribute(targetAttribute);
333 if (targetAttribute.empty()) {
334 attribute = sourceGrid.getName();
335 }
336
337 // we do not allow sampling onto the "P" attribute
338 if (attribute == "P") {
339 OPENVDB_THROW(RuntimeError, "Cannot sample onto the \"P\" attribute");
340 }
341
342 auto leaf = points.tree().cbeginLeaf();
343 if (!leaf) return;
344
345 PointDataSampler<PointDataGridT, SamplerT, FilterT, InterrupterT> pointDataSampler(
346 order, points, sampler, filter, interrupter, threaded);
347
348 const auto& descriptor = leaf->attributeSet().descriptor();
349 size_t targetIndex = descriptor.find(attribute);
350 const bool attributeExists = targetIndex != AttributeSet::INVALID_POS;
351
352 if (std::is_same<TargetValueT, DummySampleType>::value) {
353 if (!attributeExists) {
354 // append attribute of source grid value type
355 appendAttribute<typename SourceGridT::ValueType>(points.tree(), attribute);
356 targetIndex = leaf->attributeSet().descriptor().find(attribute);
357 assert(targetIndex != AttributeSet::INVALID_POS);
358
359 // sample using same type as source grid
360 pointDataSampler.template sample<SourceGridT>(sourceGrid, Index(targetIndex));
361 } else {
362 auto targetIdx = static_cast<Index>(targetIndex);
363 // attempt to explicitly sample using type of existing attribute
364 const Name& targetType = descriptor.valueType(targetIndex);
365 if (targetType == typeNameAsString<Vec3f>()) {
366 pointDataSampler.template sample<SourceGridT, Vec3f>(sourceGrid, targetIdx);
367 } else if (targetType == typeNameAsString<Vec3d>()) {
368 pointDataSampler.template sample<SourceGridT, Vec3d>(sourceGrid, targetIdx);
369 } else if (targetType == typeNameAsString<Vec3i>()) {
370 pointDataSampler.template sample<SourceGridT, Vec3i>(sourceGrid, targetIdx);
371 } else if (targetType == typeNameAsString<int8_t>()) {
372 pointDataSampler.template sample<SourceGridT, int8_t>(sourceGrid, targetIdx);
373 } else if (targetType == typeNameAsString<int16_t>()) {
374 pointDataSampler.template sample<SourceGridT, int16_t>(sourceGrid, targetIdx);
375 } else if (targetType == typeNameAsString<int32_t>()) {
376 pointDataSampler.template sample<SourceGridT, int32_t>(sourceGrid, targetIdx);
377 } else if (targetType == typeNameAsString<int64_t>()) {
378 pointDataSampler.template sample<SourceGridT, int64_t>(sourceGrid, targetIdx);
379 } else if (targetType == typeNameAsString<float>()) {
380 pointDataSampler.template sample<SourceGridT, float>(sourceGrid, targetIdx);
381 } else if (targetType == typeNameAsString<double>()) {
382 pointDataSampler.template sample<SourceGridT, double>(sourceGrid, targetIdx);
383 } else if (targetType == typeNameAsString<bool>()) {
384 pointDataSampler.template sample<SourceGridT, bool>(sourceGrid, targetIdx);
385 } else {
386 std::ostringstream ostr;
387 ostr << "Cannot sample attribute of type - " << targetType;
388 OPENVDB_THROW(TypeError, ostr.str());
389 }
390 }
391 } else {
392 if (!attributeExists) {
393 // append attribute of target value type
394 // (point_sample_internal wrapper disables the ability to use DummySampleType)
395 AppendAttributeOp<PointDataGridT, TargetValueT>::append(points, attribute);
396 targetIndex = leaf->attributeSet().descriptor().find(attribute);
397 assert(targetIndex != AttributeSet::INVALID_POS);
398 }
399 else {
400 const Name targetType = typeNameAsString<TargetValueT>();
401 const Name attributeType = descriptor.valueType(targetIndex);
402 if (targetType != attributeType) {
403 std::ostringstream ostr;
404 ostr << "Requested attribute type " << targetType << " for sampling "
405 << " does not match existing attribute type " << attributeType;
406 OPENVDB_THROW(TypeError, ostr.str());
407 }
408 }
409
410 // sample using target value type
411 pointDataSampler.template sample<SourceGridT, TargetValueT>(
412 sourceGrid, static_cast<Index>(targetIndex));
413 }
414}
415
416 template<typename PointDataGridT, typename SourceGridT, typename FilterT, typename InterrupterT>
417 inline void pointSample(PointDataGridT& points,
418 const SourceGridT& sourceGrid,
419 const Name& targetAttribute,
420 const FilterT& filter,
421 InterrupterT* const interrupter)
422{
423 SampleWithRounding sampler;
424 sampleGrid(/*order=*/0, points, sourceGrid, targetAttribute, filter, sampler, interrupter);
425}
426
427 template<typename PointDataGridT, typename SourceGridT, typename FilterT, typename InterrupterT>
428 inline void boxSample( PointDataGridT& points,
429 const SourceGridT& sourceGrid,
430 const Name& targetAttribute,
431 const FilterT& filter,
432 InterrupterT* const interrupter)
433{
434 SampleWithRounding sampler;
435 sampleGrid(/*order=*/1, points, sourceGrid, targetAttribute, filter, sampler, interrupter);
436}
437
438 template<typename PointDataGridT, typename SourceGridT, typename FilterT, typename InterrupterT>
439 inline void quadraticSample(PointDataGridT& points,
440 const SourceGridT& sourceGrid,
441 const Name& targetAttribute,
442 const FilterT& filter,
443 InterrupterT* const interrupter)
444{
445 SampleWithRounding sampler;
446 sampleGrid(/*order=*/2, points, sourceGrid, targetAttribute, filter, sampler, interrupter);
447}
448
449
450////////////////////////////////////////
451
452
453} // namespace points
454} // namespace OPENVDB_VERSION_NAME
455} // namespace openvdb
456
457#endif // OPENVDB_POINTS_POINT_SAMPLE_IMPL_HAS_BEEN_INCLUDED
OPENVDB_THROW
#define OPENVDB_THROW(exception, message)
Definition Exceptions.h:74
openvdb::v11_0::RuntimeError
Definition Exceptions.h:63
openvdb::v11_0::TypeError
Definition Exceptions.h:64
nanovdb::GridClass::Staggered
@ Staggered
openvdb::v11_0::math::Vec3d
Vec3< double > Vec3d
Definition Vec3.h:664
openvdb::v11_0::points::sampleGrid
void sampleGrid(size_t order, PointDataGridT &points, const SourceGridT &sourceGrid, const Name &targetAttribute, const FilterT &filter=NullFilter(), const SamplerT &sampler=SampleWithRounding(), InterrupterT *const interrupter=nullptr, const bool threaded=true)
Performs sampling and conversion from a VDB grid onto a VDB Points attribute.
Definition PointSampleImpl.h:319
openvdb::v11_0::points::boxSample
void boxSample(PointDataGridT &points, const SourceGridT &sourceGrid, const Name &targetAttribute="", const FilterT &filter=NullFilter(), InterrupterT *const interrupter=nullptr)
Performs tri-linear sampling from a VDB grid onto a VDB Points attribute.
Definition PointSampleImpl.h:428
openvdb::v11_0::points::quadraticSample
void quadraticSample(PointDataGridT &points, const SourceGridT &sourceGrid, const Name &targetAttribute="", const FilterT &filter=NullFilter(), InterrupterT *const interrupter=nullptr)
Performs tri-quadratic sampling from a VDB grid onto a VDB Points attribute.
Definition PointSampleImpl.h:439
openvdb::v11_0::points::pointSample
void pointSample(PointDataGridT &points, const SourceGridT &sourceGrid, const Name &targetAttribute="", const FilterT &filter=NullFilter(), InterrupterT *const interrupter=nullptr)
Performs closest point sampling from a VDB grid onto a VDB Points attribute.
Definition PointSampleImpl.h:417
openvdb::v11_0::Name
std::string Name
Definition Name.h:19
openvdb::v11_0::Index
Index32 Index
Definition Types.h:54
openvdb
Definition Exceptions.h:13
openvdb::v11_0::VecTraits
Definition Types.h:244
openvdb::v11_0::points::SampleWithRounding
Definition PointSample.h:85
OPENVDB_VERSION_NAME
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition version.h.in:121
OPENVDB_USE_VERSION_NAMESPACE
#define OPENVDB_USE_VERSION_NAMESPACE
Definition version.h.in:212