diff --git a/openvdb/openvdb/tree/InternalNode.h b/openvdb/openvdb/tree/InternalNode.h index 7cf653f901..4cbafff1d0 100644 --- a/openvdb/openvdb/tree/InternalNode.h +++ b/openvdb/openvdb/tree/InternalNode.h @@ -620,6 +620,43 @@ class InternalNode /// If no such node exists, return nullptr. template NodeType* probeNode(const Coord& xyz); template const NodeType* probeConstNode(const Coord& xyz) const; + template const NodeType* probeNode(const Coord& xyz) const { return this->probeConstNode(xyz); } + //@} + + //@{ + /// @brief Return a pointer to the child node that contains voxel (x, y, z). + /// If no such node exists, return nullptr. + ChildNodeType* probeChild(const Coord& xyz); + const ChildNodeType* probeConstChild(const Coord& xyz) const; + const ChildNodeType* probeChild(const Coord& xyz) const { return this->probeConstChild(xyz); } + //@} + + //@{ + /// @brief Return a pointer to the child node that contains voxel (x, y, z). + /// If no such node exists, return nullptr. + ChildNodeType* probeChild(const Coord& xyz, ValueType& value, bool& active); + const ChildNodeType* probeConstChild(const Coord& xyz, ValueType& value, bool& active) const; + const ChildNodeType* probeChild(const Coord& xyz, ValueType& value, bool& active) const { return this->probeConstChild(xyz, value, active); } + //@} + + //@{ + /// @brief Return a pointer to the child node for a specific offset. + /// If no such node exists, return nullptr. + /// @warning This method should only be used by experts seeking low-level optimizations. + /// @note Out-of-bounds memory access attempts will wrap around using modulo indexing. + ChildNodeType* probeChildUnsafe(Index offset); + const ChildNodeType* probeConstChildUnsafe(Index offset) const; + const ChildNodeType* probeChildUnsafe(Index offset) const { return this->probeConstChildUnsafe(offset); } + //@} + + //@{ + /// @brief Return a pointer to the child node for a specific offset. + /// If no such node exists, return nullptr. + /// @warning This method should only be used by experts seeking low-level optimizations. + /// @note Out-of-bounds memory access attempts will wrap around using modulo indexing. + ChildNodeType* probeChildUnsafe(Index offset, ValueType& value, bool& active); + const ChildNodeType* probeConstChildUnsafe(Index offset, ValueType& value, bool& active) const; + const ChildNodeType* probeChildUnsafe(Index offset, ValueType& value, bool& active) const { return this->probeConstChildUnsafe(offset, value, active); } //@} //@{ @@ -1248,6 +1285,82 @@ InternalNode::probeConstNodeAndCache(const Coord& xyz, Accessor //////////////////////////////////////// +template +inline ChildT* +InternalNode::probeChild(const Coord& xyz) +{ + const Index n = this->coordToOffset(xyz); + return this->probeChildUnsafe(n); +} + +template +inline const ChildT* +InternalNode::probeConstChild(const Coord& xyz) const +{ + const Index n = this->coordToOffset(xyz); + return this->probeConstChildUnsafe(n); +} + +template +inline ChildT* +InternalNode::probeChild(const Coord& xyz, ValueType& value, bool& active) +{ + const Index n = this->coordToOffset(xyz); + return this->probeChildUnsafe(n, value, active); +} + +template +inline const ChildT* +InternalNode::probeConstChild(const Coord& xyz, ValueType& value, bool& active) const +{ + const Index n = this->coordToOffset(xyz); + return this->probeConstChildUnsafe(n, value, active); +} + +template +inline ChildT* +InternalNode::probeChildUnsafe(Index offset) +{ + OPENVDB_ASSERT(offset < NUM_VALUES); + if (mChildMask.isOn(offset)) return mNodes[offset].getChild(); + return nullptr; +} + +template +inline const ChildT* +InternalNode::probeConstChildUnsafe(Index offset) const +{ + OPENVDB_ASSERT(offset < NUM_VALUES); + if (mChildMask.isOn(offset)) return mNodes[offset].getChild(); + return nullptr; +} + +template +inline ChildT* +InternalNode::probeChildUnsafe(Index offset, ValueType& value, bool& active) +{ + OPENVDB_ASSERT(offset < NUM_VALUES); + if (mChildMask.isOn(offset)) return mNodes[offset].getChild(); + value = mNodes[offset].getValue(); + active = mValueMask.isOn(offset); + return nullptr; +} + +template +inline const ChildT* +InternalNode::probeConstChildUnsafe(Index offset, ValueType& value, bool& active) const +{ + OPENVDB_ASSERT(offset < NUM_VALUES); + if (mChildMask.isOn(offset)) return mNodes[offset].getChild(); + value = mNodes[offset].getValue(); + active = mValueMask.isOn(offset); + return nullptr; +} + + +//////////////////////////////////////// + + template inline typename ChildT::LeafNodeType* InternalNode::probeLeaf(const Coord& xyz) diff --git a/openvdb/openvdb/tree/RootNode.h b/openvdb/openvdb/tree/RootNode.h index 5eb7794115..63231a777b 100644 --- a/openvdb/openvdb/tree/RootNode.h +++ b/openvdb/openvdb/tree/RootNode.h @@ -724,9 +724,20 @@ class RootNode template NodeT* probeNode(const Coord& xyz); template + const NodeT* probeNode(const Coord& xyz) const; + template const NodeT* probeConstNode(const Coord& xyz) const; //@} + //@{ + /// @brief Return a pointer to the root child node that contains voxel (x, y, z). + /// If no such node exists, query and set the tile value and active status and + /// return @c nullptr. + bool probe(const Coord& xyz, ChildNodeType*& child, ValueType& value, bool& active); + bool probeConst(const Coord& xyz, const ChildNodeType*& child, ValueType& value, bool& active) const; + bool probe(const Coord& xyz, const ChildNodeType*& child, ValueType& value, bool& active) const { return this->probeConst(xyz, child, value, active); } + //} + //@{ /// @brief Same as probeNode() but, if necessary, update the given accessor with pointers /// to the nodes along the path from the root node to the node containing the coordinate. @@ -736,12 +747,20 @@ class RootNode const NodeT* probeConstNodeAndCache(const Coord& xyz, AccessorT& acc) const; //@} + //@{ + /// @brief Return a pointer to the root child node that contains voxel (x, y, z). + /// If no such node exists, return @c nullptr. + ChildNodeType* probeChild(const Coord& xyz); + const ChildNodeType* probeConstChild(const Coord& xyz) const; + const ChildNodeType* probeChild(const Coord& xyz) const { return this->probeConstChild(xyz); } + //@} + //@{ /// @brief Return a pointer to the leaf node that contains voxel (x, y, z). /// If no such node exists, return @c nullptr. LeafNodeType* probeLeaf(const Coord& xyz); const LeafNodeType* probeConstLeaf(const Coord& xyz) const; - const LeafNodeType* probeLeaf(const Coord& xyz) const; + const LeafNodeType* probeLeaf(const Coord& xyz) const { return this->probeConstLeaf(xyz); } //@} //@{ @@ -2786,6 +2805,15 @@ RootNode::probeNode(const Coord& xyz) } +template +template +inline const NodeT* +RootNode::probeNode(const Coord& xyz) const +{ + return this->template probeConstNode(xyz); +} + + template template inline const NodeT* @@ -2804,6 +2832,62 @@ RootNode::probeConstNode(const Coord& xyz) const } +template +inline bool +RootNode::probe(const Coord& xyz, ChildNodeType*& child, ValueType& value, bool& active) +{ + MapIter iter = this->findCoord(xyz); + if (iter == mTable.end()) { + child = nullptr; + return false; + } else if (isChild(iter)) { + child = &getChild(iter); + return true; + } + const Tile& tile = getTile(iter); + child = nullptr; + value = tile.value; + active = tile.active; + return true; +} + + +template +inline bool +RootNode::probeConst(const Coord& xyz, const ChildNodeType*& child, ValueType& value, bool& active) const +{ + MapCIter iter = this->findCoord(xyz); + if (iter == mTable.end()) { + child = nullptr; + return false; + } else if (isChild(iter)) { + child = &getChild(iter); + return true; + } + const Tile& tile = getTile(iter); + child = nullptr; + value = tile.value; + active = tile.active; + return true; +} + + +template +inline ChildT* +RootNode::probeChild(const Coord& xyz) +{ + return this->template probeNode(xyz); +} + + +template +inline const ChildT* +RootNode::probeConstChild(const Coord& xyz) const +{ + return this->template probeConstNode(xyz); +} + + template inline typename ChildT::LeafNodeType* RootNode::probeLeaf(const Coord& xyz) diff --git a/openvdb/openvdb/unittest/TestInternalNode.cc b/openvdb/openvdb/unittest/TestInternalNode.cc index 52e3a98258..a9396cbeb0 100644 --- a/openvdb/openvdb/unittest/TestInternalNode.cc +++ b/openvdb/openvdb/unittest/TestInternalNode.cc @@ -120,3 +120,125 @@ TEST_F(TestInternalNode, test) EXPECT_EQ(Index32(5), internalNode3.transientData()); } } + +TEST_F(TestInternalNode, testProbe) +{ + using RootNode = FloatTree::RootNodeType; + using InternalNode = RootNode::ChildNodeType; + + const Coord ijk(0, 0, 4096); + InternalNode internalNode(ijk, 1.0f); + + internalNode.addTile(32, 3.0f, true); // (0, 128, 4096) + internalNode.addTile(33, 4.0f, true); // (0, 128, 4224) + + auto* child = new InternalNode::ChildNodeType(Coord(0, 256, 4096), 5.0f, true); + EXPECT_TRUE(internalNode.addChild(child)); // always returns true + + { // probeNode, probeConstNode + auto* node1 = internalNode.probeNode(Coord(0, 256, 4096)); + EXPECT_TRUE(bool(node1)); + auto* node2 = internalNode.probeNode(Coord(0, 128, 4096)); + EXPECT_FALSE(bool(node2)); + const InternalNode& constInternalNode = internalNode; + auto* node3 = constInternalNode.probeNode(Coord(0, 256, 4096)); + EXPECT_TRUE(bool(node3)); + auto* node4 = constInternalNode.probeNode(Coord(0, 128, 4096)); + EXPECT_FALSE(bool(node4)); + auto* node5 = internalNode.probeConstNode(Coord(0, 256, 4096)); + EXPECT_TRUE(bool(node5)); + auto* node6 = internalNode.probeConstNode(Coord(0, 128, 4096)); + EXPECT_FALSE(bool(node6)); + } + + { // probeChild, probeConstChild + auto* node1 = internalNode.probeChild(Coord(0, 256, 4096)); + EXPECT_TRUE(bool(node1)); + auto* node2 = internalNode.probeChild(Coord(0, 128, 4096)); + EXPECT_FALSE(bool(node2)); + const InternalNode& constInternalNode = internalNode; + auto* node3 = constInternalNode.probeChild(Coord(0, 256, 4096)); + EXPECT_TRUE(bool(node3)); + auto* node4 = constInternalNode.probeChild(Coord(0, 128, 4096)); + EXPECT_FALSE(bool(node4)); + auto* node5 = internalNode.probeConstChild(Coord(0, 256, 4096)); + EXPECT_TRUE(bool(node5)); + auto* node6 = internalNode.probeConstChild(Coord(0, 128, 4096)); + EXPECT_FALSE(bool(node6)); + } + + { // probeChildUnsafe, probeConstChildUnsafe + auto* node1 = internalNode.probeChildUnsafe(64); + EXPECT_TRUE(bool(node1)); + auto* node2 = internalNode.probeChildUnsafe(33); + EXPECT_FALSE(bool(node2)); + const InternalNode& constInternalNode = internalNode; + auto* node3 = constInternalNode.probeChildUnsafe(64); + EXPECT_TRUE(bool(node3)); + auto* node4 = constInternalNode.probeChildUnsafe(33); + EXPECT_FALSE(bool(node4)); + auto* node5 = internalNode.probeConstChildUnsafe(64); + EXPECT_TRUE(bool(node5)); + auto* node6 = internalNode.probeConstChildUnsafe(33); + EXPECT_FALSE(bool(node6)); + } + + float value = -1.0f; + bool active = false; + + { // probeChild, probeConstChild with value and active status + auto* node1 = internalNode.probeChild(Coord(0, 256, 4096), value, active); + EXPECT_TRUE(bool(node1)); + EXPECT_EQ(value, -1.0f); + EXPECT_FALSE(active); + auto* node2 = internalNode.probeChild(Coord(0, 128, 4096), value, active); + EXPECT_FALSE(bool(node2)); + EXPECT_EQ(value, 3.0f); value = -1.0f; + EXPECT_TRUE(active); active = false; + const InternalNode& constInternalNode = internalNode; + auto* node3 = constInternalNode.probeChild(Coord(0, 256, 4096), value, active); + EXPECT_TRUE(bool(node3)); + EXPECT_EQ(value, -1.0f); + EXPECT_FALSE(active); + auto* node4 = constInternalNode.probeChild(Coord(0, 128, 4096), value, active); + EXPECT_FALSE(bool(node4)); + EXPECT_EQ(value, 3.0f); value = -1.0f; + EXPECT_TRUE(active); active = false; + auto* node5 = internalNode.probeConstChild(Coord(0, 256, 4096), value, active); + EXPECT_TRUE(bool(node5)); + EXPECT_EQ(value, -1.0f); + EXPECT_FALSE(active); + auto* node6 = internalNode.probeConstChild(Coord(0, 128, 4096), value, active); + EXPECT_FALSE(bool(node6)); + EXPECT_EQ(value, 3.0f); value = -1.0f; + EXPECT_TRUE(active); active = false; + } + + { // probeChildUnsafe, probeConstChildUnsafe with value and active status + auto* node1 = internalNode.probeChildUnsafe(64, value, active); + EXPECT_TRUE(bool(node1)); + EXPECT_EQ(value, -1.0f); + EXPECT_FALSE(active); + auto* node2 = internalNode.probeChildUnsafe(33, value, active); + EXPECT_FALSE(bool(node2)); + EXPECT_EQ(value, 4.0f); value = -1.0f; + EXPECT_TRUE(active); active = false; + const InternalNode& constInternalNode = internalNode; + auto* node3 = constInternalNode.probeChildUnsafe(64, value, active); + EXPECT_TRUE(bool(node3)); + EXPECT_EQ(value, -1.0f); + EXPECT_FALSE(active); + auto* node4 = constInternalNode.probeChildUnsafe(33, value, active); + EXPECT_FALSE(bool(node4)); + EXPECT_EQ(value, 4.0f); value = -1.0f; + EXPECT_TRUE(active); active = false; + auto* node5 = internalNode.probeConstChildUnsafe(64, value, active); + EXPECT_TRUE(bool(node5)); + EXPECT_EQ(value, -1.0f); + EXPECT_FALSE(active); + auto* node6 = internalNode.probeConstChildUnsafe(33, value, active); + EXPECT_FALSE(bool(node6)); + EXPECT_EQ(value, 4.0f); value = -1.0f; + EXPECT_TRUE(active); active = false; + } +} diff --git a/openvdb/openvdb/unittest/TestRootNode.cc b/openvdb/openvdb/unittest/TestRootNode.cc index 16be294068..4af727e084 100644 --- a/openvdb/openvdb/unittest/TestRootNode.cc +++ b/openvdb/openvdb/unittest/TestRootNode.cc @@ -110,3 +110,120 @@ TEST_F(TestRoot, test) EXPECT_EQ(Index32(5), rootNode3.transientData()); } } + +TEST_F(TestRoot, testProbe) +{ + using RootNode = FloatTree::RootNodeType; + + RootNode root(1.0f); + + root.addTile(Coord(1, 2, 3), 2.0f, true); + root.addTile(Coord(4096, 2, 3), 3.0f, false); + + auto* child = new RootNode::ChildNodeType(Coord(0, 0, 4096), 5.0f, true); + EXPECT_TRUE(root.addChild(child)); // always returns true + + { // probeNode, probeConstNode + auto* node1 = root.probeNode(Coord(0, 0, 4096)); + EXPECT_TRUE(bool(node1)); + auto* node2 = root.probeNode(Coord(4096, 0, 0)); + EXPECT_FALSE(bool(node2)); + const RootNode& constRoot = root; + auto* node3 = constRoot.probeNode(Coord(0, 0, 4096)); + EXPECT_TRUE(bool(node3)); + auto* node4 = constRoot.probeNode(Coord(4096, 0, 0)); + EXPECT_FALSE(bool(node4)); + auto* node5 = root.probeConstNode(Coord(0, 0, 4096)); + EXPECT_TRUE(bool(node5)); + auto* node6 = root.probeConstNode(Coord(4096, 0, 0)); + EXPECT_FALSE(bool(node6)); + } + + { // probeChild, probeConstChild + auto* node1 = root.probeChild(Coord(0, 0, 4096)); + EXPECT_TRUE(bool(node1)); + auto* node2 = root.probeChild(Coord(4096, 0, 0)); + EXPECT_FALSE(bool(node2)); + const RootNode& constRoot = root; + auto* node3 = constRoot.probeChild(Coord(0, 0, 4096)); + EXPECT_TRUE(bool(node3)); + auto* node4 = constRoot.probeChild(Coord(4096, 0, 0)); + EXPECT_FALSE(bool(node4)); + auto* node5 = root.probeConstChild(Coord(0, 0, 4096)); + EXPECT_TRUE(bool(node5)); + auto* node6 = root.probeConstChild(Coord(4096, 0, 0)); + EXPECT_FALSE(bool(node6)); + } + + RootNode::ChildNodeType* childPtr = nullptr; + const RootNode::ChildNodeType* constChildPtr = nullptr; + float value = -1.0f; + bool active = false; + + { // probe, probeConst - child + bool keyExists = root.probe(Coord(0, 0, 4096), childPtr, value, active); + EXPECT_TRUE(keyExists); + EXPECT_TRUE(bool(childPtr)); + childPtr = nullptr; + keyExists = root.probe(Coord(0, 10, 4096), childPtr, value, active); + EXPECT_TRUE(keyExists); + EXPECT_TRUE(bool(childPtr)); + childPtr = nullptr; + EXPECT_FALSE(root.probe(Coord(4096, 4096, 4096), childPtr, value, active)); + EXPECT_FALSE(bool(childPtr)); + + const RootNode& constRoot = root; + keyExists = constRoot.probe(Coord(0, 0, 4096), constChildPtr, value, active); + EXPECT_TRUE(keyExists); + EXPECT_TRUE(bool(constChildPtr)); + constChildPtr = nullptr; + EXPECT_FALSE(root.probe(Coord(4096, 4096, 4096), constChildPtr, value, active)); + EXPECT_FALSE(bool(childPtr)); + + keyExists = root.probeConst(Coord(0, 0, 4096), constChildPtr, value, active); + EXPECT_TRUE(keyExists); + EXPECT_TRUE(bool(constChildPtr)); + constChildPtr = nullptr; + EXPECT_FALSE(root.probeConst(Coord(4096, 4096, 4096), constChildPtr, value, active)); + EXPECT_FALSE(bool(constChildPtr)); + } + + { // probe, probeConst - tile + EXPECT_TRUE(root.probe(Coord(0, 0, 0), childPtr, value, active)); + EXPECT_FALSE(bool(childPtr)); + EXPECT_EQ(value, 2.0f); + EXPECT_EQ(active, true); + value = -1.0f; + EXPECT_TRUE(root.probe(Coord(4096, 0, 0), childPtr, value, active)); + EXPECT_FALSE(bool(childPtr)); + EXPECT_EQ(value, 3.0f); + EXPECT_EQ(active, false); + EXPECT_FALSE(root.probe(Coord(4096, 4096, 4096), childPtr, value, active)); + EXPECT_FALSE(bool(childPtr)); + + const RootNode& constRoot = root; + EXPECT_TRUE(constRoot.probe(Coord(0, 0, 0), constChildPtr, value, active)); + EXPECT_FALSE(bool(constChildPtr)); + EXPECT_EQ(value, 2.0f); + EXPECT_EQ(active, true); + value = -1.0f; + EXPECT_TRUE(root.probe(Coord(4096, 0, 0), constChildPtr, value, active)); + EXPECT_FALSE(bool(constChildPtr)); + EXPECT_EQ(value, 3.0f); + EXPECT_EQ(active, false); + EXPECT_FALSE(root.probe(Coord(4096, 4096, 4096), constChildPtr, value, active)); + EXPECT_FALSE(bool(constChildPtr)); + + EXPECT_TRUE(root.probeConst(Coord(0, 0, 0), constChildPtr, value, active)); + EXPECT_FALSE(bool(constChildPtr)); + EXPECT_EQ(value, 2.0f); + EXPECT_EQ(active, true); + value = -1.0f; + EXPECT_TRUE(root.probeConst(Coord(4096, 0, 0), constChildPtr, value, active)); + EXPECT_FALSE(bool(constChildPtr)); + EXPECT_EQ(value, 3.0f); + EXPECT_EQ(active, false); + EXPECT_FALSE(root.probeConst(Coord(4096, 4096, 4096), constChildPtr, value, active)); + EXPECT_FALSE(bool(constChildPtr)); + } +} diff --git a/pendingchanges/probe.txt b/pendingchanges/probe.txt new file mode 100644 index 0000000000..0786474ffa --- /dev/null +++ b/pendingchanges/probe.txt @@ -0,0 +1,10 @@ +Improvements: + - Added RootNode::probeChild() const. + - Added RootNode::probeChild() and RootNode::probeConstChild(). + - Added RootNode::probe() and RootNode::probeConst() to query key presence, + child node, value and active state. + - Added InternalNode::probeChild() const. + - Added InternalNode::probeChild() and probeChildConst() with coord access + and optionally value and active state. + - Added InternalNode::probeChild() and probeChildConst() with index access + and optionally value and active state.