diff --git a/openvdb/openvdb/tools/MeshToVolume.h b/openvdb/openvdb/tools/MeshToVolume.h index 2aac956e75..669ed621d7 100644 --- a/openvdb/openvdb/tools/MeshToVolume.h +++ b/openvdb/openvdb/tools/MeshToVolume.h @@ -1645,7 +1645,7 @@ checkNeighbours(const Index pos, const typename LeafNodeType::ValueType * data, // i + 1, j, k if (mask[0] && Compare::check(data[pos + NodeT::DIM * NodeT::DIM])) return true; // i+1, j, k-1 - if (mask[6] && Compare::check(data[pos + NodeT::DIM * NodeT::DIM])) return true; + if (mask[6] && Compare::check(data[pos + NodeT::DIM * NodeT::DIM - 1])) return true; // i-1, j, k-1 if (mask[7] && Compare::check(data[pos - NodeT::DIM * NodeT::DIM - 1])) return true; // i+1, j, k+1 diff --git a/openvdb/openvdb/unittest/TestMeshToVolume.cc b/openvdb/openvdb/unittest/TestMeshToVolume.cc index 5c4ff2ffdf..2042e1ad24 100644 --- a/openvdb/openvdb/unittest/TestMeshToVolume.cc +++ b/openvdb/openvdb/unittest/TestMeshToVolume.cc @@ -60,6 +60,39 @@ TEST_F(TestMeshToVolume, testUtils) EXPECT_TRUE( mZ == 9); } +// Regression test for the checkNeighbours(pos, data, mask) overload in +// mesh_to_volume_internal: each of its 26 branches hand-computes a flat-array +// offset for one entry of util::COORD_OFFSETS from the node's i/j/k strides, so a +// copy-paste slip in any single branch (as happened for i+1,j,k-1, which reused +// the i+1,j,k offset and so silently ignored a negative neighbour on read) goes +// undetected by callers that never place a negative value at that exact offset. +// This checks all 26 branches against the offset table directly. +TEST_F(TestMeshToVolume, testCheckNeighboursOffsets) +{ + using namespace openvdb; + using namespace openvdb::tools::mesh_to_volume_internal; + + using LeafT = FloatTree::LeafNodeType; + struct IsNegative { static bool check(float v) { return v < 0.0f; } }; + + // interior voxel, far enough from every face that all 26 neighbours are in-node + const Coord center(LeafT::DIM / 2, LeafT::DIM / 2, LeafT::DIM / 2); + const Index pos = LeafT::coordToOffset(center); + + for (int m = 0; m < 26; ++m) { + std::vector data(LeafT::NUM_VALUES, /*background=*/1.0f); + const Coord neighbourIjk = center + util::COORD_OFFSETS[m]; + data[LeafT::coordToOffset(neighbourIjk)] = -1.0f; + + bool mask[26] = {false}; + mask[m] = true; + + EXPECT_TRUE((checkNeighbours(pos, data.data(), mask))) + << "checkNeighbours missed neighbour offset " << m + << " = " << util::COORD_OFFSETS[m]; + } +} + TEST_F(TestMeshToVolume, testConversion) { using namespace openvdb; diff --git a/pendingchanges/openvdb_tools.txt b/pendingchanges/openvdb_tools.txt index 7a6aea662f..df7befe90a 100644 --- a/pendingchanges/openvdb_tools.txt +++ b/pendingchanges/openvdb_tools.txt @@ -18,3 +18,4 @@ tools: maximum allowed) instead of crashing. The check is confined to process() so that offset() -- used by the offset-only path -- continues to accept coarser voxel sizes unaffected. - Fixed a floating-point false positive in "tools::CheckLevelSet::checkBackground()" in Diagnostics.h where the ratio "background / voxelSize" could compute to just below the expected half-width due to non-representable IEEE 754 values, causing a valid level set created with exactly LEVEL_SET_HALF_WIDTH voxels of narrow band to incorrectly fail check #4. The strict "<" comparison is now relaxed by a relative tolerance of 1e-6. A regression test covering this exact case was added to TestDiagnostics.cc. + - Fixed an index error in "mesh_to_volume_internal::checkNeighbours()" in MeshToVolume.h where the i+1,j,k-1 neighbour reused the i+1,j,k offset, silently missing that neighbour's value during self-intersection checks in mesh-to-volume conversion (bugfix contributed by hexuejun). A regression test covering all 26 neighbour offsets was added to TestMeshToVolume.cc.