OpenVDB 12.0.0
 
Loading...
Searching...
No Matches
ValueAccessor.h
Go to the documentation of this file.
1// Copyright Contributors to the OpenVDB Project
2// SPDX-License-Identifier: Apache-2.0
3
4/// @file tree/ValueAccessor.h
5///
6/// @brief ValueAccessors are designed to help accelerate accesses into the
7/// OpenVDB Tree structures by storing caches to Tree branches. When
8/// traversing a grid in a spatially coherent pattern (e.g., iterating over
9/// neighboring voxels), the same branches and nodes of the underlying tree
10/// can be hit. If you do this using the Tree/RootNode methods directly,
11/// traversal will occur at O(log(n)) (or O(n) depending on the hash map
12/// implementation) for every access. However, using a ValueAccessor allows
13/// for the Accessor to cache previously visited Nodes, providing possible
14/// subsequent access speeds of O(1) if the next access is close to a
15/// previously cached Node. Accessors are lightweight and can be configured
16/// to cache any number of arbitrary Tree levels.
17///
18/// The ValueAccessor interfaces matches that of compatible OpenVDB Tree
19/// nodes. You can request an Accessor from a Grid (with Grid::getAccessor())
20/// or construct one directly from a Tree. You can use, for example, the
21/// accessor's @c getValue() and @c setValue() methods in place of those on
22/// OpenVDB Nodes/Trees.
23///
24/// @par Example:
25/// @code
26/// FloatGrid grid;
27/// FloatGrid::Accessor acc = grid.getAccessor();
28/// // First access is slow:
29/// acc.setValue(Coord(0, 0, 0), 100);
30///
31/// // Subsequent nearby accesses are fast, since the accessor now holds pointers
32/// // to nodes that contain (0, 0, 0) along the path from the root of the grid's
33/// // tree to the leaf:
34/// acc.setValue(Coord(0, 0, 1), 100);
35/// acc.getValue(Coord(0, 2, 0), 100);
36///
37/// // Slow, because the accessor must be repopulated:
38/// acc.getValue(Coord(-1, -1, -1));
39///
40/// // Fast:
41/// acc.getValue(Coord(-1, -1, -2));
42/// acc.setValue(Coord(-1, -2, 0), -100);
43/// @endcode
44
45#ifndef OPENVDB_TREE_VALUEACCESSOR_HAS_BEEN_INCLUDED
46#define OPENVDB_TREE_VALUEACCESSOR_HAS_BEEN_INCLUDED
47
48#include <openvdb/version.h>
49#include <openvdb/Types.h>
50#include <openvdb/util/Assert.h>
51
52#include <tbb/spin_mutex.h>
53
54#include <limits>
55#include <type_traits>
56#include <mutex>
57
58namespace openvdb {
60namespace OPENVDB_VERSION_NAME {
61namespace tree {
62
63// Forward declaration of the generic ValueAccessor API.
64template<typename TreeType,
65 bool IsSafe = true,
66 typename MutexT = void,
67 typename IndexSequence = openvdb::make_index_sequence<std::max(size_t(1),TreeType::DEPTH)-1>>
68class ValueAccessorImpl;
69
70/// @brief Default alias for a ValueAccessor. This is simply a helper alias
71/// for the generic definition but takes a single Index specifying the number
72/// of nodes to cache. This is expanded into an index sequence (required for
73/// backward compatibility).
74/// @tparam TreeType The tree type
75/// @tparam IsSafe Whether this accessor registers itself to the tree. See
76/// the base class definition for more information on this parameter.
77/// @tparam CacheLevels The number of node levels to cache _excluding_ the
78/// Root node. The Root node is implicitly always included, even if this
79/// value is zero.
80/// @tparam MutexType An optional std compatible mutex to use which ensures
81/// every call to the ValueAccessor API is thread safe. If void (the default)
82/// no locking takes place. In general it's not advised to mutex lock
83/// ValueAccessor methods (instead consider creating a accessor per thread).
84template<typename TreeType, bool IsSafe = true,
85 size_t CacheLevels = std::max(Index(1),TreeType::DEPTH)-1, typename MutexType = void>
87 ValueAccessorImpl<TreeType, IsSafe, MutexType,
89
90/// @brief Helper alias for a ValueAccessor which doesn't cache any Internal
91/// or Leaf nodes.
92template <typename TreeType, bool IsSafe>
95/// @brief Helper alias for a ValueAccessor which caches a single node level.
96/// By default, the node level is 0, which corresponds to the lowest node
97/// level, typically LeafNodes.
98template <typename TreeType, bool IsSafe, size_t L0 = 0>
101/// @brief Helper alias for a ValueAccessor which caches two node levels. By
102/// default the two lowest node levels are selected (0, 1) which typically
103/// correspond to an InternalNode and its child LeafNodes. This instantiation
104/// will only be valid for TreeTypes which have at least two levels of nodes
105/// (excluding the Root node).
106template <typename TreeType, bool IsSafe, size_t L0 = 0, size_t L1 = 1>
109/// @brief Helper alias for a ValueAccessor which caches three node levels. By
110/// default the three lowest node levels are selected (0, 1, 2) which
111/// typically correspond to two InternalNodes followed by the bottom
112/// LeafNodes. This instantiation will only be valid for TreeTypes which have
113/// at least three levels of nodes (excluding the Root node).
114template <typename TreeType, bool IsSafe, size_t L0 = 0, size_t L1 = 1, size_t L2 = 2>
117
118/// @brief Helper alias for a ValueAccesor which spin locks every API call.
119template<typename TreeType, bool IsSafe = true,
120 size_t CacheLevels = std::max(Index(1),TreeType::DEPTH)-1>
122 ValueAccessorImpl<TreeType, IsSafe, tbb::spin_mutex,
124
125
126/// @brief This base class for ValueAccessors manages registration of an
127/// accessor with a tree so that the tree can automatically clear the
128/// accessor whenever one of its nodes is deleted.
129///
130/// @internal A base class is needed because ValueAccessor is templated on both
131/// a Tree type and a mutex type. The various instantiations of the template
132/// are distinct, unrelated types, so they can't easily be stored in a
133/// container (mainly the Tree's CacheRegistry). This base class, in contrast,
134/// is templated only on the Tree type, so for any given Tree, only two
135/// distinct instantiations are possible, ValueAccessorBase<Tree> and
136/// ValueAccessorBase<const Tree>.
137///
138/// @warning If IsSafe = false then the ValueAccessor will not register itself
139/// with the tree from which it is constructed. While in some rare cases this
140/// can lead to better performance (since it avoids the small overhead of
141/// insertion on creation and deletion on destruction) it is also unsafe if
142/// the tree is modified. So unless you're an expert it is highly recommended
143/// to set IsSafe = true, which is the default in all derived ValueAccessors
144/// defined below. However if you know that the tree is no being modifed for
145/// the lifespan of the ValueAccessor AND the work performed per
146/// ValueAccessor is small relative to overhead of registering it you should
147/// consider setting IsSafe = false. If this turns out to improve performance
148/// you should really rewrite your code so as to better amortize the
149/// construction of the ValueAccessor, i.e. reuse it as much as possible!
150template<typename TreeType, bool IsSafe>
152{
153public:
154 /// @brief Returns true if this accessor is operating on a const tree type.
155 static constexpr bool IsConstTree = std::is_const<TreeType>::value;
156
157 /// @brief Return true if this accessor is safe, i.e. registered by the
158 /// tree from which it is constructed. Un-registered accessors can in
159 /// rare cases be faster because it avoids the (small) overhead of
160 /// registration, but they are unsafe if the tree is modified. So unless
161 /// you're an expert it is highly recommended to set IsSafe = true (which
162 /// is the default).
163 static constexpr bool isSafe() { return IsSafe; }
164
165 /// @brief Construct from a tree. Should rarely be invoked directly, the
166 /// drived implementation class calls this. Remains public for backwards
167 /// compatibility.
169 : mTree(&tree)
170 {
171 if (IsSafe) tree.attachAccessor(*this);
172 }
173
174 virtual ~ValueAccessorBase() { if (IsSafe && mTree) mTree->releaseAccessor(*this); }
175
176 /// @brief Copy constructor - if IsSafe, then the copy also registers
177 /// itself against the tree it is accessing.
179 : mTree(other.mTree)
180 {
181 if (IsSafe && mTree) mTree->attachAccessor(*this);
182 }
183
185 {
186 if (&other != this) {
187 if (IsSafe && mTree) mTree->releaseAccessor(*this);
188 mTree = other.mTree;
189 if (IsSafe && mTree) mTree->attachAccessor(*this);
190 }
191 return *this;
192 }
193
194 /// @brief Return a pointer to the tree associated with this accessor.
195 /// @details The pointer will be null only if the tree from which this
196 /// accessor was constructed was subsequently deleted (which generally
197 /// leaves the accessor in an unsafe state).
198 TreeType* getTree() const { return mTree; }
199
200 /// @brief Return a reference to the tree associated with this accessor.
201 TreeType& tree() const { OPENVDB_ASSERT(mTree); return *mTree; }
202
203 /// @brief Pure virtual method, clears the derived accessor
204 virtual void clear() = 0;
205
206protected:
207 // Allow trees to deregister themselves.
208 template<typename> friend class Tree;
209 virtual void release() { mTree = nullptr; }
210 TreeType* mTree;
211}; // class ValueAccessorBase
212
213///////////////////////////////////////////////////////////////////////////////
214
215/// @cond OPENVDB_DOCS_INTERNAL
216
217namespace value_accessor_internal
218{
219
220template<typename ListT, size_t... Ts> struct NodeListBuilderImpl;
221
222template <typename NodeChainT>
223struct NodeListBuilderImpl<NodeChainT>
224{
225 using ListT = TypeList<>;
226};
227
228template <typename NodeChainT, size_t Idx>
229struct NodeListBuilderImpl<NodeChainT, Idx>
230{
231 using NodeT = typename NodeChainT::template Get<Idx>;
232 using ListT = TypeList<NodeT>;
233};
234
235template <typename NodeChainT, size_t ThisIdx, size_t NextIdx, size_t... Idxs>
236struct NodeListBuilderImpl<NodeChainT, ThisIdx, NextIdx, Idxs...>
237{
238 static_assert(ThisIdx < NextIdx,
239 "Invalid cache level - Cache levels must be in increasing ascending order");
240 static_assert(ThisIdx < NodeChainT::Size,
241 "Invalid cache level - Cache level is larger than the number of tree nodes");
242 static_assert(ThisIdx < NodeChainT::Back::LEVEL,
243 "Invalid cache level - Cache level is larger than the number of tree nodes");
244
245 using NodeT = typename NodeChainT::template Get<ThisIdx>;
246 using ListT = typename TypeList<NodeT>::template Append<
247 typename NodeListBuilderImpl<NodeChainT, NextIdx, Idxs...>::ListT>;
248};
249
250template<typename NodeChainT, size_t RootLevel, typename IntegerSequence>
251struct NodeListBuilder;
252
253template<typename NodeChainT, size_t RootLevel, size_t... Is>
254struct NodeListBuilder<NodeChainT, RootLevel, std::integer_sequence<size_t, Is...>>
255{
256 using ListT = typename NodeListBuilderImpl<NodeChainT, Is..., RootLevel>::ListT;
257};
258
259template<typename NodeChainT, size_t RootLevel, size_t... Is>
260struct NodeListBuilder<NodeChainT, RootLevel, openvdb::index_sequence<Is...>>
261{
262 using ListT = typename NodeListBuilderImpl<NodeChainT, Is..., RootLevel>::ListT;
263};
264
265
266template<typename TreeTypeT, typename NodeT>
267struct EnableLeafBuffer
268{
269 using LeafNodeT = typename TreeTypeT::LeafNodeType;
270 static constexpr bool value =
271 std::is_same<NodeT, LeafNodeT>::value &&
272 std::is_same<typename LeafNodeT::Buffer::StorageType,
273 typename LeafNodeT::ValueType>::value;
274};
275
276template<typename TreeTypeT, size_t... Is>
277struct EnableLeafBuffer<TreeTypeT, openvdb::index_sequence<Is...>>
278{
279 // Empty integer seq, no nodes being caches
280 static constexpr bool value = false;
281};
282
283template<typename TreeTypeT, size_t First, size_t... Is>
284struct EnableLeafBuffer<TreeTypeT, openvdb::index_sequence<First, Is...>>
285{
286private:
287 using NodeChainT = typename TreeTypeT::RootNodeType::NodeChainType;
288 using FirstNodeT = typename NodeChainT::template Get<First>;
289public:
290 static constexpr bool value = EnableLeafBuffer<TreeTypeT, FirstNodeT>::value;
291};
292
293} // namespace value_accessor_internal
294
295/// @endcond
296
297///////////////////////////////////////////////////////////////////////////////
298
299/// The following classes exist to perform empty base class optimizations
300/// with the final ValueAccessor implementation. Depending on the template
301/// types provided to the derived implementation, some member variables may not
302/// be necessary (mutex, leaf buffer cache, etc). These classes allow for these
303/// variables to be compiled out. Note that from C++20 we can switch to
304/// [[no_unique_address]] member annotations instead.
305
306/// @brief A small class that contains a Mutex which is derived from by the
307/// internal Value Accessor Implementation. This allows for the empty base
308/// class optimization to be performed in the case where a Mutex/Lock is not
309/// in use. From C++20 we can instead switch to [[no_unique_address]].
310template <typename MutexT>
312{
313 inline auto lock() const { return std::scoped_lock(m); }
314private:
315 mutable MutexT m;
316};
317
318/// @brief Specialization for the case where no Mutex is in use. See above.
319template <>
321{
322 inline constexpr auto lock() const { return 0; }
323};
324
325/// @brief A small class that contains a cached pointer to a LeafNode data
326/// buffer which is derived from by the internal Value Accessor
327/// Implementation. This allows for the empty base class optimization to be
328/// performed in the case where a LeafNode does not store a contiguous
329/// index-able buffer. From C++20 we can instead switch to
330/// [[no_unique_address]].
331template<typename TreeTypeT, typename IntegerSequence, typename Enable = void>
333{
334 template <typename NodeT>
335 static constexpr bool BypassLeafAPI =
336 std::is_same<NodeT, typename TreeTypeT::LeafNodeType>::value;
337 inline const typename TreeTypeT::ValueType* buffer() { OPENVDB_ASSERT(mBuffer); return mBuffer; }
338 inline const typename TreeTypeT::ValueType* buffer() const { OPENVDB_ASSERT(mBuffer); return mBuffer; }
339 inline void setBuffer(const typename TreeTypeT::ValueType* b) const { mBuffer = b; }
340private:
341 mutable const typename TreeTypeT::ValueType* mBuffer;
342};
343
344/// @brief Specialization for the case where a Leaf Buffer cannot be cached.
345// These methods should never be invoked. See above.
346template<typename TreeTypeT, typename IntegerSequence>
347struct ValueAccessorLeafBuffer<TreeTypeT, IntegerSequence,
348 typename std::enable_if<
349 !value_accessor_internal::EnableLeafBuffer<TreeTypeT, IntegerSequence>::value
350 >::type>
351{
352 template <typename> static constexpr bool BypassLeafAPI = false;
353 inline constexpr typename TreeTypeT::ValueType* buffer() { OPENVDB_ASSERT(false); return nullptr; }
354 inline constexpr typename TreeTypeT::ValueType* buffer() const { OPENVDB_ASSERT(false); return nullptr; }
355 inline constexpr void setBuffer(const typename TreeTypeT::ValueType*) const { OPENVDB_ASSERT(false); }
356};
357
358///////////////////////////////////////////////////////////////////////////////
359
360/// @brief The Value Accessor Implementation and API methods. The majoirty of
361/// the API matches the API of a compatible OpenVDB Tree Node.
362template<typename _TreeType, bool IsSafe, typename MutexT, typename IntegerSequence>
363class ValueAccessorImpl final :
364 public ValueAccessorBase<_TreeType, IsSafe>,
365 public ValueAccessorLeafBuffer<_TreeType, IntegerSequence>,
366 public ValueAccessorLock<MutexT>
367{
368public:
369 /// @note Not strictly the only Base Type but provided for backwards
370 /// compatibility.
374
375 using TreeType = _TreeType;
376 using ValueType = typename TreeType::ValueType;
377 using RootNodeT = typename TreeType::RootNodeType;
378 using LeafNodeT = typename TreeType::LeafNodeType;
379 using NodeChainT = typename RootNodeT::NodeChainType;
380
381 /// @brief A resolved, flattened TypeList of node types which this
382 /// accessor is caching. The nodes index in this list does not
383 /// necessarily correspond to the nodes level in the tree.
385 typename value_accessor_internal::NodeListBuilder
386 <NodeChainT, RootNodeT::LEVEL, IntegerSequence>::ListT;
387 using NodePtrList = typename NodeLevelList::template Transform<std::add_pointer_t>;
388
389 /// @brief Return a node type at a particular cache level in the Value
390 /// accessor. The node type at a given cache level does not necessarily
391 /// equal the same node type in the TreeType as this depends entirely on
392 /// which tree levels this Accessor is caching. For example:
393 /// @par Example:
394 /// @code
395 /// // Cache tree levels 0 and 2
396 /// using Impl = ValueAccessorImpl<FloatTree, true, void, 0, 2>
397 /// using CacheLevel1 = Impl::template NodeTypeAtLevel<1>
398 /// using TreeLevel2 = TreeType::RootNodeType::NodeChainType::Get<2>;
399 /// static_assert(std::is_same<CacheLevel1, TreeLevel2>::value);
400 /// @endcode
401 template <size_t Level>
402 using NodeTypeAtLevel = typename NodeLevelList::template Get<Level>;
403
404 /// @brief Given a node type, return whether this Accessor can perform
405 /// optimized value buffer accesses. This is only possible for LeafNodes
406 /// so will always return false for any non LeafNode type. It also
407 /// depends on the value type - if the value buffer is a contiguous
408 /// index-able array of values then this returns true.
409 template <typename NodeT>
410 static constexpr bool IsLeafAndBypassLeafAPI =
411 LeafCacheT::template BypassLeafAPI<NodeT>;
412
413 /// @brief Helper alias which is true if the lowest cached node level is
414 /// a LeafNode type and has a compatible value type for optimized access.
415 static constexpr bool BypassLeafAPI =
417
418 /// @brief The number of node levels that this accessor can cache,
419 /// excluding the RootNode.
420 static constexpr size_t NumCacheLevels = NodeLevelList::Size-1;
421 static_assert(TreeType::DEPTH >= NodeLevelList::Size-1, "cache size exceeds tree depth");
422 static_assert(NodeLevelList::Size > 0, "unexpected cache size");
423
424 /// @brief Constructor from a tree
426 : BaseT(tree)
427 , LeafCacheT()
428 , LockT()
429 , mKeys()
430 , mNodes() {
431 this->clear();
432 }
433
434 ~ValueAccessorImpl() override final = default;
436 ValueAccessorImpl& operator=(const ValueAccessorImpl&) = default;
437
438 /// @brief Return @c true if any of the nodes along the path to the given
439 /// coordinate have been cached.
440 /// @param xyz The index space coordinate to query
441 bool isCached(const Coord& xyz) const
442 {
443 return this->evalFirstIndex([&](const auto Idx) -> bool
444 {
445 using NodeType = typename NodeLevelList::template Get<Idx>;
446 // @warning Putting this exp in the if statement crashes GCC9
447 constexpr bool IsRoot = std::is_same<RootNodeT, NodeType>::value;
448 if constexpr(IsRoot) return false;
449 else return (this->isHashed<NodeType>(xyz));
450 });
451 }
452
453 /// @brief Return the value of the voxel at the given coordinates.
454 /// @param xyz The index space coordinate to query
455 const ValueType& getValue(const Coord& xyz) const
456 {
457 // Don't use evalFirstCached as we don't access the node when
458 // IsLeafAndBypassLeafAPI<NodeType> is true.
459 return *this->evalFirstIndex([&](const auto Idx) -> const ValueType*
460 {
461 using NodeType = typename NodeLevelList::template Get<Idx>;
462 // If not cached return a nullptr. Note that this operator is
463 // guaranteed to return a value as the last node in the chain
464 // is a RootNode and isHashed always returns true for this case
465 if (!this->isHashed<NodeType>(xyz)) return nullptr;
466
468 return &(LeafCacheT::buffer()[LeafNodeT::coordToOffset(xyz)]);
469 }
470 else {
471 auto node = mNodes.template get<Idx>();
472 OPENVDB_ASSERT(node);
473 return &(node->getValueAndCache(xyz, *this));
474 }
475 });
476 }
477
478 /// @brief Return the active state of the voxel at the given coordinates.
479 /// @param xyz The index space coordinate to query
480 bool isValueOn(const Coord& xyz) const
481 {
482 return this->evalFirstCached(xyz, [&](const auto node) -> bool {
483 OPENVDB_ASSERT(node);
484 return node->isValueOnAndCache(xyz, *this);
485 });
486 }
487
488 /// @brief Return the active state of the value at a given coordinate as
489 /// well as its value.
490 /// @param xyz The index space coordinate to query
491 /// @param value The value to get
492 bool probeValue(const Coord& xyz, ValueType& value) const
493 {
494 return this->evalFirstCached(xyz, [&](const auto node) -> bool
495 {
496 using NodeType = std::remove_pointer_t<decltype(node)>;
497 OPENVDB_ASSERT(node);
498
500 const auto offset = LeafNodeT::coordToOffset(xyz);
501 value = LeafCacheT::buffer()[offset];
502 return node->isValueOn(offset);
503 }
504 else {
505 return node->probeValueAndCache(xyz, value, *this);
506 }
507 });
508 }
509
510 /// @brief Return the tree depth (0 = root) at which the value of voxel
511 /// (x, y, z) resides, or -1 if (x, y, z) isn't explicitly represented in
512 /// the tree (i.e., if it is implicitly a background voxel).
513 /// @note This is the inverse of the node LEVEL (where the RootNode level
514 /// is the highest in the tree).
515 /// @param xyz The index space coordinate to query
516 int getValueDepth(const Coord& xyz) const
517 {
518 return this->evalFirstCached(xyz, [&](const auto node) -> int
519 {
520 using NodeType = std::remove_pointer_t<decltype(node)>;
521 OPENVDB_ASSERT(node);
522
523 if constexpr(std::is_same<RootNodeT, NodeType>::value) {
524 return node->getValueDepthAndCache(xyz, *this);
525 }
526 else {
527 return int(RootNodeT::LEVEL - node->getValueLevelAndCache(xyz, *this));
528 }
529 });
530 }
531
532 /// @brief Return @c true if the value of voxel (x, y, z) resides at the
533 /// leaf level of the tree, i.e., if it is not a tile value.
534 /// @param xyz The index space coordinate to query
535 bool isVoxel(const Coord& xyz) const
536 {
538 return this->getValueDepth(xyz) ==
539 static_cast<int>(RootNodeT::LEVEL);
540 }
541
542 //@{
543 /// @brief Set a particular value at the given coordinate and mark the
544 /// coordinate as active
545 /// @note This method will densify branches of the tree if the coordinate
546 /// points to a tile and if the provided value or active state is
547 /// different to the tiles
548 /// @param xyz The index space coordinate to set
549 /// @param value The value to set
550 void setValue(const Coord& xyz, const ValueType& value)
551 {
552 static_assert(!BaseT::IsConstTree, "can't modify a const tree's values");
553 this->evalFirstCached(xyz, [&](const auto node) -> void
554 {
555 using NodeType = std::remove_pointer_t<decltype(node)>;
556 OPENVDB_ASSERT(node);
557
559 const auto offset = LeafNodeT::coordToOffset(xyz);
560 const_cast<ValueType&>(LeafCacheT::buffer()[offset]) = value;
561 const_cast<NodeType*>(node)->setValueOn(offset);
562 }
563 else {
564 const_cast<NodeType*>(node)->setValueAndCache(xyz, value, *this);
565 }
566 });
567 }
568
569 void setValueOn(const Coord& xyz, const ValueType& value) { this->setValue(xyz, value); }
570 //@}
571
572 /// @brief Set a particular value at the given coordinate but preserve its
573 /// active state
574 /// @note This method will densify branches of the tree if the coordinate
575 /// points to a tile and if the provided value is different to the tiles
576 /// @param xyz The index space coordinate to set
577 /// @param value The value to set
578 void setValueOnly(const Coord& xyz, const ValueType& value)
579 {
580 static_assert(!BaseT::IsConstTree, "can't modify a const tree's values");
581 // Don't use evalFirstCached as we don't access the node when
582 // IsLeafAndBypassLeafAPI<NodeType> is true.
583 this->evalFirstIndex([&](const auto Idx) -> bool
584 {
585 using NodeType = typename NodeLevelList::template Get<Idx>;
586 if (!this->isHashed<NodeType>(xyz)) return false;
587
589 const_cast<ValueType&>(LeafCacheT::buffer()[LeafNodeT::coordToOffset(xyz)]) = value;
590 }
591 else {
592 auto node = mNodes.template get<Idx>();
593 OPENVDB_ASSERT(node);
594 const_cast<NodeType*>(node)->setValueOnlyAndCache(xyz, value, *this);
595 }
596 return true;
597 });
598 }
599
600 /// @brief Set a particular value at the given coordinate and mark the
601 /// coordinate as inactive.
602 /// @note This method will densify branches of the tree if the coordinate
603 /// points to a tile and if the provided value or active state is
604 /// different to the tiles
605 /// @param xyz The index space coordinate to set
606 /// @param value The value to set
607 void setValueOff(const Coord& xyz, const ValueType& value)
608 {
609 static_assert(!BaseT::IsConstTree, "can't modify a const tree's values");
610 this->evalFirstCached(xyz, [&](const auto node) -> void
611 {
612 using NodeType = std::remove_pointer_t<decltype(node)>;
613 OPENVDB_ASSERT(node);
614
616 const auto offset = LeafNodeT::coordToOffset(xyz);
617 const_cast<ValueType&>(LeafCacheT::buffer()[offset]) = value;
618 const_cast<NodeType*>(node)->setValueOff(offset);
619 }
620 else {
621 const_cast<NodeType*>(node)->setValueOffAndCache(xyz, value, *this);
622 }
623 });
624 }
625
626 /// @brief Apply a functor to the value at the given coordinate and mark
627 /// mark the coordinate as active
628 /// @details See Tree::modifyValue() for details.
629 /// @param xyz The index space coordinate to modify
630 /// @param op The modify operation
631 template<typename ModifyOp>
632 void modifyValue(const Coord& xyz, const ModifyOp& op)
633 {
634 static_assert(!BaseT::IsConstTree, "can't modify a const tree's values");
635 this->evalFirstCached(xyz, [&](const auto node) -> void
636 {
637 using NodeType = std::remove_pointer_t<decltype(node)>;
638 OPENVDB_ASSERT(node);
639
641 const auto offset = LeafNodeT::coordToOffset(xyz);
642 op(const_cast<ValueType&>(LeafCacheT::buffer()[offset]));
643 const_cast<NodeType*>(node)->setActiveState(offset, true);
644 }
645 else {
646 const_cast<NodeType*>(node)->modifyValueAndCache(xyz, op, *this);
647 }
648 });
649 }
650
651 /// @brief Apply a functor to the voxel at the given coordinates.
652 /// @details See Tree::modifyValueAndActiveState() for details.
653 /// @param xyz The index space coordinate to modify
654 /// @param op The modify operation
655 template<typename ModifyOp>
656 void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op)
657 {
658 static_assert(!BaseT::IsConstTree, "can't modify a const tree's values");
659 this->evalFirstCached(xyz, [&](const auto node) -> void
660 {
661 using NodeType = std::remove_pointer_t<decltype(node)>;
662 OPENVDB_ASSERT(node);
663
665 const auto offset = LeafNodeT::coordToOffset(xyz);
666 bool state = node->isValueOn(offset);
667 op(const_cast<ValueType&>(LeafCacheT::buffer()[offset]), state);
668 const_cast<NodeType*>(node)->setActiveState(offset, state);
669 }
670 else {
671 const_cast<NodeType*>(node)->modifyValueAndActiveStateAndCache(xyz, op, *this);
672 }
673 });
674 }
675
676 /// @brief Set the active state of the voxel at the given coordinates
677 /// without changing its value.
678 /// @note This method will densify branches of the tree if the coordinate
679 /// points to a tile and if the provided activate state flag is different
680 /// to the tiles
681 /// @param xyz The index space coordinate to modify
682 /// @param on Whether to set the active state to on (true) or off (false)
683 void setActiveState(const Coord& xyz, bool on = true)
684 {
685 static_assert(!BaseT::IsConstTree, "can't modify a const tree's values");
686 this->evalFirstCached(xyz, [&](const auto node) -> void
687 {
688 using NodeType = std::remove_pointer_t<decltype(node)>;
689 OPENVDB_ASSERT(node);
690 const_cast<NodeType*>(node)->setActiveStateAndCache(xyz, on, *this);
691 });
692 }
693 /// @brief Mark the voxel at the given coordinates as active without
694 /// changing its value.
695 /// @note This method will densify branches of the tree if the coordinate
696 /// points to a tile and if the tiles active state is off.
697 /// @param xyz The index space coordinate to modify
698 void setValueOn(const Coord& xyz) { this->setActiveState(xyz, true); }
699 /// @brief Mark the voxel at the given coordinates as inactive without
700 /// changing its value.
701 /// @note This method will densify branches of the tree if the coordinate
702 /// points to a tile and if the tiles active state is on.
703 /// @param xyz The index space coordinate to modify
704 void setValueOff(const Coord& xyz) { this->setActiveState(xyz, false); }
705
706 /// @brief Returns the leaf node that contains voxel (x, y, z) and if it
707 /// doesn't exist, create it, but preserve the values and active states
708 /// of the pre-existing branch.
709 /// @note You can use this method to preallocate a static tree topology
710 /// over which to safely perform multithreaded processing.
711 /// @param xyz The index space coordinate at which to create a LeafNode.
712 /// Note that if this coordinate is not a LeafNode origin then the
713 /// LeafNode that would otherwise contain this coordinate is created and
714 /// returned.
716 {
717 static_assert(!BaseT::IsConstTree, "can't get a non-const node from a const tree");
718 return this->evalFirstCached(xyz, [&](const auto node) -> LeafNodeT*
719 {
720 using NodeType = std::remove_pointer_t<decltype(node)>;
721 OPENVDB_ASSERT(node);
722 return const_cast<NodeType*>(node)->touchLeafAndCache(xyz, *this);
723 });
724 }
725
726 /// @brief Add the specified leaf to this tree, possibly creating a child
727 /// branch in the process. If the leaf node already exists, replace it.
728 /// @param leaf The LeafNode to insert into the tree. Must not be a nullptr.
729 void addLeaf(LeafNodeT* leaf)
730 {
731 constexpr int64_t Start = NodeLevelList::template Index<LeafNodeT> + 1;
732 static_assert(!BaseT::IsConstTree, "can't add a node to a const tree");
733 static_assert(Start >= 0);
734 OPENVDB_ASSERT(leaf);
735 this->evalFirstCached<Start>(leaf->origin(), [&](const auto node) -> void
736 {
737 using NodeType = std::remove_pointer_t<decltype(node)>;
738 OPENVDB_ASSERT(node);
739 const_cast<NodeType*>(node)->addLeafAndCache(leaf, *this);
740 });
741 }
742
743 /// @brief Add a tile at the specified tree level that contains the
744 /// coordinate xyz, possibly deleting existing nodes or creating new
745 /// nodes in the process.
746 /// @note Calling this with a level of 0 will modify voxel values. This
747 /// function will always densify a tree branch up to the requested level
748 /// (regardless if the value and active state match).
749 /// @param level The level of the tree to add a tile. Level 0 refers to
750 /// voxels (and is similar to ::setValue, except will always density).
751 /// @param xyz The index space coordinate to add a tile
752 /// @param value The value of the tile
753 /// @param state The active state to set on the new tile
754 void addTile(Index level, const Coord& xyz, const ValueType& value, bool state)
755 {
756 constexpr int64_t Start = NodeLevelList::template Index<LeafNodeT> + 1;
757 static_assert(!BaseT::IsConstTree, "can't add a tile to a const tree");
758 static_assert(Start >= 0);
759 this->evalFirstCached<Start>(xyz, [&](const auto node) -> void
760 {
761 using NodeType = std::remove_pointer_t<decltype(node)>;
762 OPENVDB_ASSERT(node);
763 const_cast<NodeType*>(node)->addTileAndCache(level, xyz, value, state, *this);
764 });
765 }
766
767 ///@{
768 /// @brief Return a pointer to the node of the specified type that contains
769 /// the value located at xyz. If no node of the given NodeT exists which
770 /// contains the value, a nullptr is returned.
771 /// @brief This function may return a nullptr even if the coordinate xyz is
772 /// represented in tree, as it depends on the type NodeT provided. For
773 /// example, the value may exist as a tile in an InternalNode but note as
774 /// a LeafNode.
775 /// @param xyz The index space coordinate to query
776 template<typename NodeT>
777 NodeT* probeNode(const Coord& xyz)
778 {
779 static_assert(!BaseT::IsConstTree, "can't get a non-const node from a const tree");
780 return this->evalFirstPred([&](const auto Idx) -> bool
781 {
782 using NodeType = typename NodeLevelList::template Get<Idx>;
783 // @warning Putting this exp in the if statement crashes GCC9
784 constexpr bool NodeMayBeCached =
785 std::is_same<NodeT, NodeType>::value || NodeT::LEVEL < NodeType::LEVEL;
786
787 if constexpr(NodeMayBeCached) return this->isHashed<NodeType>(xyz);
788 else return false;
789 },
790 [&](const auto node) -> NodeT*
791 {
792 using NodeType = std::remove_pointer_t<decltype(node)>;
793 OPENVDB_ASSERT(node);
794 if constexpr(std::is_same<NodeT, NodeType>::value) {
795 return const_cast<NodeT*>(node);
796 }
797 else {
798 OPENVDB_ASSERT(NodeT::LEVEL < NodeType::LEVEL);
799 return const_cast<NodeType*>(node)->template probeNodeAndCache<NodeT>(xyz, *this);
800 }
801 });
802 }
803
804 template<typename NodeT>
805 const NodeT* probeConstNode(const Coord& xyz) const
806 {
807 return this->evalFirstPred([&](const auto Idx) -> bool
808 {
809 using NodeType = typename NodeLevelList::template Get<Idx>;
810 // @warning Putting this exp in the if statement crashes GCC9
811 constexpr bool NodeMayBeCached =
812 std::is_same<NodeT, NodeType>::value || NodeT::LEVEL < NodeType::LEVEL;
813
814 if constexpr(NodeMayBeCached) return this->isHashed<NodeType>(xyz);
815 else return false;
816 },
817 [&](const auto node) -> const NodeT*
818 {
819 using NodeType = std::remove_pointer_t<decltype(node)>;
820 OPENVDB_ASSERT(node);
821 if constexpr(std::is_same<NodeT, NodeType>::value) {
822 return node;
823 }
824 else {
825 OPENVDB_ASSERT(NodeT::LEVEL < NodeType::LEVEL);
826 return const_cast<NodeType*>(node)->template probeConstNodeAndCache<NodeT>(xyz, *this);
827 }
828 });
829 }
830 /// @}
831
832 ///@{
833 /// @brief Return a pointer to the leaf node that contains the voxel
834 /// coordinate xyz. If no LeafNode exists, returns a nullptr.
835 /// @param xyz The index space coordinate to query
836 LeafNodeT* probeLeaf(const Coord& xyz) { return this->template probeNode<LeafNodeT>(xyz); }
837 const LeafNodeT* probeLeaf(const Coord& xyz) const { return this->probeConstLeaf(xyz); }
838 const LeafNodeT* probeConstLeaf(const Coord& xyz) const
839 {
840 return this->template probeConstNode<LeafNodeT>(xyz);
841 }
842 /// @}
843
844 /// @brief Return the node of type @a NodeT that has been cached on this
845 /// accessor. If this accessor does not cache this NodeT, or if no
846 /// node of this type has been cached, returns a nullptr.
847 template<typename NodeT>
848 NodeT* getNode()
849 {
850 using NodeType = typename std::decay<NodeT>::type;
851 static constexpr int64_t Idx = NodeLevelList::template Index<NodeType>;
852 if constexpr (Idx >= 0) return mNodes.template get<Idx>();
853 else return nullptr;
854 }
855
856 /// @brief Explicitly insert a node of the type @a NodeT into this Value
857 /// Accessors cache.
858 /// @todo deprecate?
859 template<typename NodeT>
860 void insertNode(const Coord& xyz, NodeT& node)
861 {
862 this->insert(xyz, &node);
863 }
864
865 /// @brief Explicitly remove this Value Accessors cached node of the given
866 /// NodeT. If this Value Accessor does not support the caching of the
867 /// provided NodeT, this method does nothing.
868 template<typename NodeT>
870 {
871 static constexpr int64_t Idx = NodeLevelList::template Index<NodeT>;
872 if constexpr (Idx >= 0) {
873 mKeys[Idx] = Coord::max();
874 mNodes.template get<Idx>() = nullptr;
875 }
876 }
877
878 /// @brief Remove all the cached nodes and invalidate the corresponding
879 /// hash-keys.
880 void clear() override final
881 {
882 mKeys.fill(Coord::max());
883 mNodes.foreach([](auto& node) { node = nullptr; });
884 if constexpr (BypassLeafAPI) {
885 LeafCacheT::setBuffer(nullptr);
886 }
887 if (BaseT::mTree) {
888 static constexpr int64_t Idx = NodeLevelList::template Index<RootNodeT>;
889 mNodes.template get<Idx>() = const_cast<RootNodeT*>(&(BaseT::mTree->root()));
890 }
891 }
892
893public:
894 // Backwards compatible support. Use NodeTypeAtLevel<> instead
895 using NodeT0 OPENVDB_DEPRECATED_MESSAGE("Use NodeTypeAtLevel<0>") =
896 typename std::conditional<(NumCacheLevels > 0), NodeTypeAtLevel<0>, void>::type;
897 using NodeT1 OPENVDB_DEPRECATED_MESSAGE("Use NodeTypeAtLevel<1>") =
898 typename std::conditional<(NumCacheLevels > 1), NodeTypeAtLevel<1>, void>::type;
899 using NodeT2 OPENVDB_DEPRECATED_MESSAGE("Use NodeTypeAtLevel<2>") =
900 typename std::conditional<(NumCacheLevels > 2), NodeTypeAtLevel<2>, void>::type;
901 /// @brief Return the number of cache levels employed by this ValueAccessor
902 OPENVDB_DEPRECATED_MESSAGE("Use the static NumCacheLevels constant")
903 static constexpr Index numCacheLevels() { return NumCacheLevels; }
904
905protected:
906 // Allow nodes to insert themselves into the cache.
907 template<typename> friend class RootNode;
908 template<typename, Index> friend class InternalNode;
909 template<typename, Index> friend class LeafNode;
910 // Allow trees to deregister themselves.
911 template<typename> friend class Tree;
912
913 /// @brief Release this accessor from the tree, set the tree to null and
914 /// clear the accessor cache. After calling this method the accessor
915 /// will be completely invalid.
916 void release() override final
917 {
918 this->BaseT::release();
919 this->clear();
920 }
921
922 /// ******************************* WARNING *******************************
923 /// Methods here must be force inline otherwise compilers do not optimize
924 /// out the function call due to recursive templates and performance
925 /// degradation is significant.
926 /// ***********************************************************************
927
928 /// @brief Insert a node into this ValueAccessor's cache
929 template<typename NodeT>
931 [[maybe_unused]] const Coord& xyz,
932 [[maybe_unused]] const NodeT* node) const
933 {
934 // Early exit if NodeT isn't part of this ValueAccessors cache
935 if constexpr(!NodeLevelList::template Contains<NodeT>) return;
936 else {
937 constexpr uint64_t Idx = uint64_t(NodeLevelList::template Index<NodeT>);
938 static_assert(NodeLevelList::template Contains<NodeT>);
939 static_assert(Idx < NumCacheLevels);
940 mKeys[Idx] = xyz & ~(NodeT::DIM-1);
941 mNodes.template get<Idx>() = const_cast<NodeT*>(node);
942 if constexpr(IsLeafAndBypassLeafAPI<NodeT>) {
943 LeafCacheT::setBuffer(node->buffer().data());
944 }
945 }
946 }
947
948 template<typename NodeT>
949 OPENVDB_FORCE_INLINE bool isHashed([[maybe_unused]] const Coord& xyz) const
950 {
951 if constexpr(!NodeLevelList::template Contains<NodeT>) return false;
952 if constexpr(std::is_same<NodeT, RootNodeT>::value) {
953 return true;
954 }
955 else {
956 constexpr uint64_t Idx = uint64_t(NodeLevelList::template Index<NodeT>);
957 static_assert(NodeLevelList::template Contains<NodeT>);
958 static_assert(Idx < NumCacheLevels + 1);
959 return (xyz[0] & ~Coord::ValueType(NodeT::DIM-1)) == mKeys[Idx][0]
960 && (xyz[1] & ~Coord::ValueType(NodeT::DIM-1)) == mKeys[Idx][1]
961 && (xyz[2] & ~Coord::ValueType(NodeT::DIM-1)) == mKeys[Idx][2];
962 }
963 }
964
965private:
966 /// @brief Evaluate a function on each node until its returns value is not
967 /// null or false.
968 /// @param op The function to run
969 template <typename OpT>
970 OPENVDB_FORCE_INLINE auto evalFirstIndex(OpT&& op) const
971 {
972 OPENVDB_ASSERT(BaseT::mTree);
973 // Mutex lock the accessor. Does nothing if no mutex if in place
974 [[maybe_unused]] const auto lock = this->lock();
975 // Get the return type of the provided operation OpT
976 using IndexT = std::integral_constant<std::size_t, 0>;
977 using RetT = typename std::invoke_result<OpT, IndexT>::type;
979 }
980
981 /// @brief Evaluate a predicate on each index I from [0,Size] until it
982 /// returns true, then executes the provided op function on the resolved
983 /// node type. Helps in cases where std::get may be unecessarily invoked.
984 /// @param pred The predicate to run on the node index
985 /// @param op The function to run on the node where the pred returns true
986 template <typename PredT, typename OpT>
987 OPENVDB_FORCE_INLINE auto evalFirstPred(PredT&& pred, OpT&& op) const
988 {
989 OPENVDB_ASSERT(BaseT::mTree);
990 // Mutex lock the accessor. Does nothing if no mutex if in place
991 [[maybe_unused]] const auto lock = this->lock();
992 using RetT = typename std::invoke_result<OpT, RootNodeT*>::type;
993 if constexpr(!std::is_same<RetT, void>::value) {
994 return mNodes.evalFirstPred(pred, op, RetT(false));
995 }
996 else {
997 return mNodes.evalFirstPred(pred, op);
998 }
999 }
1000
1001 /// @brief Helper alias to call this->evalFirstPred(), but with a default
1002 /// predicate set to return true when the node at the given index is
1003 /// cached
1004 /// @param xyz The coord to hash
1005 /// @param op The function to run on the node where the pred returns true
1006 template <size_t Start = 0, typename OpT = void>
1007 OPENVDB_FORCE_INLINE auto evalFirstCached([[maybe_unused]] const Coord& xyz, OpT&& op) const
1008 {
1009 return this->evalFirstPred([&](const auto Idx) -> bool
1010 {
1011 if constexpr(Idx < Start) return false;
1012 if constexpr(Idx > NumCacheLevels+1) return false;
1013 using NodeType = typename NodeLevelList::template Get<Idx>;
1014 return this->isHashed<NodeType>(xyz);
1015 }, op);
1016 }
1017
1018private:
1019 mutable std::array<Coord, NumCacheLevels> mKeys;
1020 mutable typename NodePtrList::AsTupleList mNodes;
1021}; // ValueAccessorImpl
1022
1023} // namespace tree
1024} // namespace OPENVDB_VERSION_NAME
1025} // namespace openvdb
1026
1027#endif // OPENVDB_TREE_VALUEACCESSOR_HAS_BEEN_INCLUDED
#define OPENVDB_ASSERT(X)
Definition Assert.h:41
#define OPENVDB_FORCE_INLINE
Definition Platform.h:103
#define OPENVDB_DEPRECATED_MESSAGE(msg)
Definition Platform.h:148
Int32 ValueType
Definition Coord.h:33
static Coord max()
Return the largest possible coordinate.
Definition Coord.h:47
Signed (x, y, z) 32-bit integer coordinates.
Definition Coord.h:26
static constexpr bool isSafe()
Return true if this accessor is safe, i.e. registered by the tree from which it is constructed....
Definition ValueAccessor.h:163
TreeType * getTree() const
Return a pointer to the tree associated with this accessor.
Definition ValueAccessor.h:198
ValueAccessorBase(const ValueAccessorBase &other)
Copy constructor - if IsSafe, then the copy also registers itself against the tree it is accessing.
Definition ValueAccessor.h:178
ValueAccessorBase & operator=(const ValueAccessorBase &other)
Definition ValueAccessor.h:184
_TreeType * mTree
Definition ValueAccessor.h:210
virtual ~ValueAccessorBase()
Definition ValueAccessor.h:174
virtual void clear()=0
Pure virtual method, clears the derived accessor.
static constexpr bool IsConstTree
Definition ValueAccessor.h:155
ValueAccessorBase(TreeType &tree)
Construct from a tree. Should rarely be invoked directly, the drived implementation class calls this....
Definition ValueAccessor.h:168
friend class Tree
Definition ValueAccessor.h:208
_TreeType & tree() const
Definition ValueAccessor.h:201
virtual void release()
Definition ValueAccessor.h:209
The Value Accessor Implementation and API methods. The majoirty of the API matches the API of a compa...
Definition ValueAccessor.h:367
int getValueDepth(const Coord &xyz) const
Return the tree depth (0 = root) at which the value of voxel (x, y, z) resides, or -1 if (x,...
Definition ValueAccessor.h:516
typename value_accessor_internal::NodeListBuilder< NodeChainT, RootNodeT::LEVEL, openvdb::make_index_sequence< CacheLevels > >::ListT NodeLevelList
Definition ValueAccessor.h:384
bool isValueOn(const Coord &xyz) const
Return the active state of the voxel at the given coordinates.
Definition ValueAccessor.h:480
bool isVoxel(const Coord &xyz) const
Return true if the value of voxel (x, y, z) resides at the leaf level of the tree,...
Definition ValueAccessor.h:535
LeafNodeT * touchLeaf(const Coord &xyz)
Returns the leaf node that contains voxel (x, y, z) and if it doesn't exist, create it,...
Definition ValueAccessor.h:715
OPENVDB_FORCE_INLINE void insert(const Coord &xyz, const NodeT *node) const
Definition ValueAccessor.h:930
void addLeaf(LeafNodeT *leaf)
Add the specified leaf to this tree, possibly creating a child branch in the process....
Definition ValueAccessor.h:729
typename NodeLevelList::template Get< Level > NodeTypeAtLevel
Definition ValueAccessor.h:402
OPENVDB_FORCE_INLINE bool isHashed(const Coord &xyz) const
Definition ValueAccessor.h:949
LeafNodeT * probeLeaf(const Coord &xyz)
Return a pointer to the leaf node that contains the voxel coordinate xyz. If no LeafNode exists,...
Definition ValueAccessor.h:836
void addTile(Index level, const Coord &xyz, const ValueType &value, bool state)
Add a tile at the specified tree level that contains the coordinate xyz, possibly deleting existing n...
Definition ValueAccessor.h:754
void modifyValueAndActiveState(const Coord &xyz, const ModifyOp &op)
Apply a functor to the voxel at the given coordinates.
Definition ValueAccessor.h:656
void setValueOnly(const Coord &xyz, const ValueType &value)
Set a particular value at the given coordinate but preserve its active state.
Definition ValueAccessor.h:578
void setValueOff(const Coord &xyz, const ValueType &value)
Set a particular value at the given coordinate and mark the coordinate as inactive.
Definition ValueAccessor.h:607
bool probeValue(const Coord &xyz, ValueType &value) const
Return the active state of the value at a given coordinate as well as its value.
Definition ValueAccessor.h:492
void setValue(const Coord &xyz, const ValueType &value)
Set a particular value at the given coordinate and mark the coordinate as active.
Definition ValueAccessor.h:550
typename NodeLevelList::template Transform< std::add_pointer_t > NodePtrList
Definition ValueAccessor.h:387
void eraseNode()
Explicitly remove this Value Accessors cached node of the given NodeT. If this Value Accessor does no...
Definition ValueAccessor.h:869
void setValueOff(const Coord &xyz)
Mark the voxel at the given coordinates as inactive without changing its value.
Definition ValueAccessor.h:704
NodeT * getNode()
Return the node of type NodeT that has been cached on this accessor. If this accessor does not cache ...
Definition ValueAccessor.h:848
void modifyValue(const Coord &xyz, const ModifyOp &op)
Apply a functor to the value at the given coordinate and mark mark the coordinate as active.
Definition ValueAccessor.h:632
ValueAccessorImpl(TreeType &tree)
Constructor from a tree.
Definition ValueAccessor.h:425
ValueAccessorLeafBuffer< TreeType, openvdb::make_index_sequence< CacheLevels > > LeafCacheT
Definition ValueAccessor.h:373
const LeafNodeT * probeLeaf(const Coord &xyz) const
Return a pointer to the leaf node that contains the voxel coordinate xyz. If no LeafNode exists,...
Definition ValueAccessor.h:837
void insertNode(const Coord &xyz, NodeT &node)
Explicitly insert a node of the type NodeT into this Value Accessors cache.
Definition ValueAccessor.h:860
const NodeT * probeConstNode(const Coord &xyz) const
Return a pointer to the node of the specified type that contains the value located at xyz....
Definition ValueAccessor.h:805
~ValueAccessorImpl() override final=default
void setValueOn(const Coord &xyz)
Mark the voxel at the given coordinates as active without changing its value.
Definition ValueAccessor.h:698
void setValueOn(const Coord &xyz, const ValueType &value)
Definition ValueAccessor.h:569
NodeT * probeNode(const Coord &xyz)
Return a pointer to the node of the specified type that contains the value located at xyz....
Definition ValueAccessor.h:777
const ValueType & getValue(const Coord &xyz) const
Return the value of the voxel at the given coordinates.
Definition ValueAccessor.h:455
void release() override final
Release this accessor from the tree, set the tree to null and clear the accessor cache....
Definition ValueAccessor.h:916
OutGridT XformOp & op
Definition ValueTransformer.h:140
ValueAccessorImpl< TreeType, IsSafe, MutexType, openvdb::make_index_sequence< CacheLevels > > ValueAccessor
Default alias for a ValueAccessor. This is simply a helper alias for the generic definition but takes...
Definition ValueAccessor.h:86
ValueAccessorImpl< TreeType, IsSafe, tbb::spin_mutex, openvdb::make_index_sequence< CacheLevels > > ValueAccessorRW
Helper alias for a ValueAccesor which spin locks every API call.
Definition ValueAccessor.h:121
ValueAccessorImpl< TreeType, IsSafe, void, openvdb::index_sequence< L0, L1 > > ValueAccessor2
Helper alias for a ValueAccessor which caches two node levels. By default the two lowest node levels ...
Definition ValueAccessor.h:107
ValueAccessorImpl< TreeType, IsSafe, void, openvdb::index_sequence< L0, L1, L2 > > ValueAccessor3
Helper alias for a ValueAccessor which caches three node levels. By default the three lowest node lev...
Definition ValueAccessor.h:115
ValueAccessorImpl< TreeType, IsSafe, void, openvdb::index_sequence<> > ValueAccessor0
Helper alias for a ValueAccessor which doesn't cache any Internal or Leaf nodes.
Definition ValueAccessor.h:93
ValueAccessorImpl< TreeType, IsSafe, void, openvdb::index_sequence< L0 > > ValueAccessor1
Helper alias for a ValueAccessor which caches a single node level. By default, the node level is 0,...
Definition ValueAccessor.h:99
Index32 Index
Definition Types.h:54
std::decay_t< decltype(make_index_sequence_impl< N >())> make_index_sequence
Definition Types.h:233
OPENVDB_FORCE_INLINE RetT evalFirstIndex(OpT op, const RetT def=RetT())
Definition TypeList.h:566
Definition Exceptions.h:13
Definition Coord.h:590
A small class that contains a cached pointer to a LeafNode data buffer which is derived from by the i...
Definition ValueAccessor.h:333
const TreeTypeT::ValueType * buffer()
Definition ValueAccessor.h:337
const TreeTypeT::ValueType * buffer() const
Definition ValueAccessor.h:338
static constexpr bool BypassLeafAPI
Definition ValueAccessor.h:335
void setBuffer(const typename TreeTypeT::ValueType *b) const
Definition ValueAccessor.h:339
constexpr auto lock() const
Definition ValueAccessor.h:322
A small class that contains a Mutex which is derived from by the internal Value Accessor Implementati...
Definition ValueAccessor.h:312
auto lock() const
Definition ValueAccessor.h:313
#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