diff --git a/.github/workflows/ax.yml b/.github/workflows/ax.yml index 98c5ed5ba6..4e56d3d721 100644 --- a/.github/workflows/ax.yml +++ b/.github/workflows/ax.yml @@ -70,18 +70,14 @@ jobs: - { image: '2023-clang15', cxx: 'clang++', build: 'Release', cmake: '' } - { image: '2023-clang15', cxx: 'g++', build: 'Release', cmake: '' } - { image: '2023-clang15', cxx: 'clang++', build: 'Debug', cmake: '' } - - { image: '2022-clang11', cxx: 'clang++', build: 'Release', cmake: '' } - - { image: '2022-clang11', cxx: 'g++', build: 'Release', cmake: '' } fail-fast: false steps: - - name: Enable Node 16 - if: contains(matrix.config.image, '2022') - run: | - echo "ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true" >> $GITHUB_ENV - uses: actions/checkout@v3 - name: nanobind #if: contains(matrix.config.image, '2023') == false run: ./ci/install_nanobind.sh 2.0.0 + - name: install_gtest + run: ./ci/install_gtest.sh 1.15.2 - name: timestamp id: timestamp run: echo "timestamp=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT @@ -126,9 +122,8 @@ jobs: strategy: matrix: config: - #@note llvm10 never got its own brew formula... - # Last macos runner befor M1 (macos-14) - - { runner: 'macos-13', cxx: 'clang++', build: 'Release', llvm: '12' } + # Last macos runner before M1 (macos-14) + - { runner: 'macos-13', cxx: 'clang++', build: 'Release', llvm: '15' } - { runner: 'macos-13', cxx: 'clang++', build: 'Release', llvm: '13' } fail-fast: false steps: @@ -159,13 +154,13 @@ jobs: github.event.inputs.type == 'grammar' runs-on: ${{ (github.repository_owner == 'AcademySoftwareFoundation' && 'ubuntu-20.04-8c-32g-300h') || 'ubuntu-latest' }} container: - image: aswf/ci-openvdb:2022-clang11 + image: aswf/ci-openvdb:2023-clang15 steps: - uses: actions/checkout@v3 - name: build run: ./ci/build.sh -v --components=axgr --target=openvdb_ax_grammar --cargs=\"-DOPENVDB_AX_GRAMMAR_NO_LINES=ON\" - name: upload grammar - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: ax_grammar path: ./build/openvdb_ax/openvdb_ax/openvdb_ax/grammar diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 394729dcab..6d479e57a5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -76,27 +76,21 @@ jobs: strategy: matrix: config: - - { cxx: clang++, image: '2024', abi: '11', build: 'Release', cmake: '' } - - { cxx: g++, image: '2024', abi: '11', build: 'Release', cmake: '' } - - { cxx: clang++, image: '2024', abi: '11', build: 'Debug', cmake: '' } - - { cxx: clang++, image: '2023', abi: '11', build: 'Release', cmake: '' } - - { cxx: g++, image: '2023', abi: '11', build: 'Release', cmake: '' } - - { cxx: clang++, image: '2022', abi: '10', build: 'Release', cmake: '-DDISABLE_DEPENDENCY_VERSION_CHECKS=ON' } - - { cxx: g++, image: '2022', abi: '10', build: 'Release', cmake: '-DDISABLE_DEPENDENCY_VERSION_CHECKS=ON' } + - { cxx: clang++, image: '2024', abi: '12', build: 'Release', cmake: '' } + - { cxx: g++, image: '2024', abi: '12', build: 'Release', cmake: '' } + - { cxx: clang++, image: '2024', abi: '12', build: 'Debug', cmake: '' } + - { cxx: clang++, image: '2023', abi: '11', build: 'Release', cmake: '-DDISABLE_DEPENDENCY_VERSION_CHECKS=ON' } + - { cxx: g++, image: '2023', abi: '11', build: 'Release', cmake: '-DDISABLE_DEPENDENCY_VERSION_CHECKS=ON' } fail-fast: false steps: - - name: Enable Node 16 - # Solution taken from https://github.blog/changelog/2024-03-07-github-actions-all-actions-will-run-on-node20-instead-of-node16-by-default - if: contains(matrix.config.image, '2022') - run: | - echo "ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true" >> $GITHUB_ENV - uses: actions/checkout@v3 - name: nanobind - #if: contains(matrix.config.image, '2023') == false run: ./ci/install_nanobind.sh 2.0.0 - name: glfw if: contains(matrix.config.image, '2023') == true run: ./ci/install_glfw.sh 3.3.10 + - name: install_gtest + run: ./ci/install_gtest.sh 1.15.2 - name: timestamp id: timestamp run: echo "timestamp=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 2f78f9a787..617ae20a12 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -40,12 +40,9 @@ jobs: container: # @note We can't update this as epydoc doesn't support python3. We'll # need to re-write the python docs to use sphinx - image: aswf/ci-openvdb:2022 + image: aswf/ci-openvdb:2024 steps: - - name: Enable Node 16 - run: | - echo "ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true" >> $GITHUB_ENV - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: install_doxygen run: ./ci/install_doxygen.sh 1_8_11 - name: nanobind @@ -55,10 +52,6 @@ jobs: # run: pip install epydoc - name: install_latex run: | - # Fix error: Cannot prepare internal mirrorlist: No URLs in mirrorlist. CentOS 8 reached EOL means need to replace the official mirror to vault.centos.org - # Comment out mirrorlist and replace #baseurl=...mirror.centos.org with baseurl=...vault.centos.org in files starting with CentOS- in /etc/yum.repos.d folder - sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-* - sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-* yum -y install texlive-latex-bin texlive-dvips texlive-collection-fontsrecommended texlive-collection-latexrecommended - name: build run: > @@ -92,7 +85,7 @@ jobs: github.event.inputs.deploy == 'docs' uses: peaceiris/actions-gh-pages@v3 with: - deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }} + deploy_key: ${{ secrets.PRIVATE_KEY }} publish_dir: /usr/local/share/doc/OpenVDB/html destination_dir: documentation/doxygen external_repository: AcademySoftwareFoundation/openvdb-website diff --git a/.github/workflows/houdini.yml b/.github/workflows/houdini.yml index 2506eb333b..28efc703b8 100644 --- a/.github/workflows/houdini.yml +++ b/.github/workflows/houdini.yml @@ -80,15 +80,8 @@ jobs: - { cxx: g++, image: '2023', hou_hash: '20_5', build: 'Release', components: 'core,hou,bin,view,render,python,test,axcore,axbin,axtest' } - { cxx: clang++, image: '2023', hou_hash: '20_0-newabi', build: 'Release', components: 'core,hou,bin,view,render,python,test,axcore,axbin,axtest' } - { cxx: g++, image: '2023', hou_hash: '20_0-newabi', build: 'Release', components: 'core,hou,bin,view,render,python,test,axcore,axbin,axtest' } - - { cxx: clang++, image: '2022', hou_hash: '20_0-oldabi', build: 'Release', components: 'core,hou' } - - { cxx: g++, image: '2022', hou_hash: '20_0-oldabi', build: 'Release', components: 'core,hou' } fail-fast: false steps: - # See note on this step in the Houdini weekly.yml job - # We can remove this when we no longer use < 2023 images - - name: Enable Node 16 - run: | - echo "ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true" >> $GITHUB_ENV - name: remove zstd run: yum -y remove zstd - uses: actions/checkout@v3 @@ -99,8 +92,9 @@ jobs: if: contains(matrix.config.image, '2023') == true run: ./ci/install_glfw.sh 3.3.10 - name: cppunit - if: contains(matrix.config.image, '2022') == false run: ./ci/install_cppunit.sh 1.15.1 + - name: install_gtest + run: ./ci/install_gtest.sh 1.15.2 - name: timestamp id: timestamp run: echo "timestamp=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT diff --git a/.github/workflows/nanovdb.yml b/.github/workflows/nanovdb.yml index dee840e076..0efc4580a8 100644 --- a/.github/workflows/nanovdb.yml +++ b/.github/workflows/nanovdb.yml @@ -67,10 +67,6 @@ jobs: - { cxx: clang++, image: '2024', build: 'Debug' } fail-fast: false steps: - - name: Enable Node 16 - if: contains(matrix.config.image, '2022') - run: | - echo "ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true" >> $GITHUB_ENV - uses: actions/checkout@v3 - name: setup_cuda_12 run: | @@ -150,8 +146,8 @@ jobs: strategy: matrix: config: - - { runner: 'macos-12', cxx: 'clang++', build: 'Release' } - - { runner: 'macos-12', cxx: 'clang++', build: 'Debug' } + - { runner: 'macos-13', cxx: 'clang++', build: 'Release' } + - { runner: 'macos-13', cxx: 'clang++', build: 'Debug' } fail-fast: false steps: - uses: actions/checkout@v3 @@ -174,12 +170,12 @@ jobs: github.event.inputs.type == 'all' || github.event.inputs.type == 'linux' runs-on: ${{ (github.repository_owner == 'AcademySoftwareFoundation' && 'ubuntu-20.04-8c-32g-300h') || 'ubuntu-latest' }} + container: + image: aswf/ci-openvdb:2024 steps: - uses: actions/checkout@v3 - name: install_gtest - run: | - sudo apt-get update - sudo apt-get -q install -y libgtest-dev + run: ./ci/install_gtest.sh 1.15.2 - name: build_and_test run: | cd nanovdb/nanovdb diff --git a/.github/workflows/weekly.yml b/.github/workflows/weekly.yml index 234393c46c..57dec07ec8 100644 --- a/.github/workflows/weekly.yml +++ b/.github/workflows/weekly.yml @@ -70,17 +70,12 @@ jobs: strategy: matrix: config: - - { houdini_version: '19.5', platform: 'linux_x86_64_gcc9.3', hou_hash: '19_5' } - - { houdini_version: '20.0', platform: 'linux_x86_64_gcc9.3', hou_hash: '20_0-oldabi' } - { houdini_version: '20.0', platform: 'linux_x86_64_gcc11.2', hou_hash: '20_0-newabi' } - { houdini_version: '20.5', platform: 'linux_x86_64_gcc11.2', hou_hash: '20_5' } fail-fast: false container: image: aswf/ci-base:2024 steps: - - name: Enable Node 16 - run: | - echo "ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true" >> $GITHUB_ENV - uses: actions/checkout@v3 # We bumped from the 2021 CI image to 2023 here to fix some OpenSSL issues # with the Houdini download script. In so doing we broke some of the caching @@ -172,9 +167,6 @@ jobs: - { name: 'conf', build: 'Release', components: 'core,python,bin,view,render,test', cmake: '-DCMAKE_FIND_PACKAGE_PREFER_CONFIG=ON' } fail-fast: false steps: - - name: Enable Node 16 - run: | - echo "ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true" >> $GITHUB_ENV - uses: actions/checkout@v3 - name: nanobind #if: contains(container.image, '2023') == false @@ -224,7 +216,7 @@ jobs: ./ci/build.sh -v --build-type=Release --components=\"core,axcore,python,bin,render,test,axbin\" - --cargs=\"-DCMAKE_CXX_STANDARD=20 -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/install ${{ matrix.config.cmake }}\" + --cargs=\"-DCMAKE_CXX_STANDARD=20 -DOPENVDB_USE_DELAYED_LOADING=OFF -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/install ${{ matrix.config.cmake }}\" - name: test run: cd build && ctest -V @@ -303,20 +295,10 @@ jobs: matrix: config: # Unified - - { image: '2023-clang14', cxx: 'clang++', build: 'Release', components: 'core,bin,axcore,axbin,axtest', cmake: '' } - - { image: '2023-clang14', cxx: 'g++', build: 'Release', components: 'core,bin,axcore,axbin,axtest', cmake: '' } - - { image: '2022-clang13', cxx: 'clang++', build: 'Release', components: 'core,bin,axcore,axbin,axtest', cmake: '' } - - { image: '2022-clang13', cxx: 'g++', build: 'Release', components: 'core,bin,axcore,axbin,axtest', cmake: '' } - # Standalone - - { image: '2022-clang11', cxx: 'clang++', build: 'Debug', components: 'core', cmake: '' } - - { image: '2022-clang11', cxx: 'clang++', build: 'Release', components: 'core', cmake: '' } - - { image: '2022-clang11', cxx: 'g++', build: 'Release', components: 'core', cmake: '' } + - { image: '2023-clang15', cxx: 'clang++', build: 'Release', components: 'core,bin,axcore,axbin,axtest', cmake: '' } + - { image: '2023-clang15', cxx: 'g++', build: 'Release', components: 'core,bin,axcore,axbin,axtest', cmake: '' } fail-fast: false steps: - - name: Enable Node 16 - if: contains(matrix.config.image, '2022') - run: | - echo "ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true" >> $GITHUB_ENV - uses: actions/checkout@v3 - name: nanobind #f: contains(matrix.config.image, '2023') == false @@ -364,9 +346,7 @@ jobs: strategy: matrix: config: - - { cxx: 'clang++', build: 'Release', llvm: '14' } - { cxx: 'clang++', build: 'Release', llvm: '15' } - #- { cxx: 'clang++', build: 'Release', llvm: '16' } - not supported yet fail-fast: false steps: - uses: actions/checkout@v3 @@ -522,9 +502,6 @@ jobs: blosc: ['1.18.0','1.19.0','1.20.0','1.21.0'] fail-fast: false steps: - - name: Enable Node 16 - run: | - echo "ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true" >> $GITHUB_ENV - uses: actions/checkout@v3 - name: install_blosc run: sudo ./ci/install_blosc.sh ${{ matrix.blosc }} @@ -612,7 +589,7 @@ jobs: -strict -extended - name: upload_report - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: always() with: name: abi_report diff --git a/CHANGES b/CHANGES index c0d27a1ce9..ca2d030a4e 100644 --- a/CHANGES +++ b/CHANGES @@ -1,7 +1,210 @@ OpenVDB Version History ======================= -Version 11.0.1 - In development +Version 12.0.1 - In development + +Version 12.0.0 - October 31, 2024 + + OpenVDB is now licensed under the Apache 2.0 license, instead of the MPL 2.0 license. + + This version introduces ABI changes relative to older major releases, + so to preserve ABI compatibility it might be necessary to define the + macro OPENVDB_ABI_VERSION_NUMBER=N, where, for example, N is 10 for + Houdini 20.0 and 11 for Houdini 20.5. + + GCC 9 is no longer supported. + + OpenVDB: + New features: + - Added fillet() method in tools::LevelSetFilter to round off concave edges + to create smoother transition between surfaces. + + Improvements: + - Added openvdb::assertAbort to replace cassert and a + OPENVDB_ENABLE_ASSERTS cmake argument/compile define to toggle + assertions in OpenVDB code, independantly of NDEBUG. Asserts are + no longer enabled by default in when NDEBUG is absent (e.g. + Debug builds). + - Removed last traces of Boost when OPENVDB_USE_DELAYED_LOADING is OFF + [Reported by Brian McKinnon] + - RootNode code cleanup to eliminate redundant key conversion and + to create map values in-place. + - Add RootNode::deleteChildOrTile() to delete a child or tile of + the root node. + - ValueAccessors are now defined and created in the Tree class + instead of in the Grid class so that custom Tree implementations + may define and create their own ValueAccessors if desired. + - Added support for PDAL to vdb_tool [Contributed by Tom Matterson] + - LeafManager and NodeManager now use Index64 for leaf counts internally. + - 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. + - Added InternalNode::isValueOff(), LeafNode::isValueOff(), + LeafNodeBool::isValueOff(), LeafNodeMask::isValueOff(). + - Added LeafNodeMask::probeValue(Index,val), LeafNodeBool::probeValue(Index,val). + - Added RootNode::getValueUnsafe(), RootNode::getChildUnsafe(), + RootNode::getConstChildUnsafe(). + - Added InternalNode::getValueUnsafe(), InternalNode::getChildUnsafe(), + InternalNode::getConstChildUnsafe(). + - Added InternalNode::setActiveStateUnsafe(), InternalNode::setValueOnlyUnsafe(), + InternalNode::setValueOnUnsafe(), InternalNode::setValueOffUnsafe(). + - Added InternalNode::setChildUnsafe(), InternalNode::resetChildUnsafe(), + InternalNode::stealChildUnsafe(), InternalNode::deleteChildUnsafe(). + - For LeafNode, LeafNodeBool and LeafNodeMask - added + LeafNode::getValueUnsafe(), LeafNode::setActiveStateunsafe(), + LeafNode::setValueOnlyUnsafe(), LeafNode::setValueOnUnsafe(), + LeafNode::setValueOffUnsafe(). + + ABI changes: + - Tree::leafCount(), Tree::unallocatedLeafCount(), + Tree::nonLeafCount() and Tree::nodeCount() now use Index64 in their + return types instead of Index32. + + API Changes: + - RootNode::tileCount(), RootNode::activeTileCount() and + RootNode::inactiveTileCount() are now public. + - RootNode::hasKey() and RootNode::coordToKey() are now public. + - RootNode::leafCount(), RootNode::nonLeafCount() and RootNode::nodeCount() + now use Index64 instead of Index32. The Index32 variant is deprecated. + - InternalNode::leafCount(), InternalNode::nonLeafCount() and + InternalNode::nodeCount() now use Index64 instead of Index32. The Index32 + variant is deprecated. + - LeafNode::leafCount() and LeafNode::nonLeafCount() now use Index64 instead + of Index32. The Index32 variant is deprecated. + + Bug Fixes: + - Fix potential crash reading corrupt .vdb files with invalid + blosc or zip chunks. + [Fix thanks to Matthias Ueberheide] + - Fix a bug in RootNode::setOrigin() where the origin was updated + before the error was thrown potentially leaving the root in an + invalid state. + - Fixed a thread sanitizer issue which could cause undefined + behaviour in VolumeToSpheres::fillWithSpheres + [Reported by Jérémie Dumas] + - Fixed an occurance of undefined behaviour in tools::activate + (though this would typically not have manifested with any + unintended behaviour) + + NanoVDB: + Bug fix: + - nanovdb::readGrids works with raw grid buffer. + + Improvements: + - Restructure files location and namespace to be more align with + OpenVDB. The namespaces touched by the restructuring are: io, + cuda, util, tools, and math. + - Add two scripts updateFiles.sh and updateFiles.py to update the + files using NanoVDB. The script updateFiles.py works on both + Windows and Linux. For a more complete list of changes, see API + Changes (details). + + - cuda::PointsToGrid supports target density. + - Add support for NanoVDB Grid of type UInt8. + - Add ability to use externally managed CUDA buffer. + - Add create methods for CudaDeviceBuffer and exceptions. + - Improve GridValidator logic, e.g. include check for grid count. + - Add operator > and >= for class Coord according to lexicographical order. + - Add toCodec to convert string to Codec enumeration type. + - Add nanovdb::strlen(). + - Add strncpy util. + - Add NANOVDB_DISABLE_SYNC_CUDA_MALLOC that maps cudaMallocAsync + and cudaFreeAsync to cudaMalloc and cudaFree respectively. + - Add guard to UINT64_C. + - Remove use of cudaMallocAsync in PointsToGrid.cuh. + - Align PNanoVDB blind metadata to NanoVDB. + + API Changes: + - Change mapToGridType to toGridType. + - Change mapToMagic to toMagic. + - Change CpuTimer.h to Timer.h. + + API Changes (details): + - These APIs are now under the math namespace: Ray, DDA, HDDA, + Vec3, Vec4, BBox, ZeroCrossing, TreeMarcher, PointTreeMarcher, + BoxStencil, CurvatureStencil, GradStencil, WenoStencil, AlignUp, + Min, Max, Abs, Clamp, Sqrt, Sign, Maximum, Delta, RoundDown, pi, + isApproxZero, Round, createSampler, SampleFromVoxels. + + - These APIs are now under the tools namespace: createNanoGrid, + StatsMode, createLevelSetSphere, createFogVolumeSphere, + createFogVolumeSphere, createFogVolumeSphere, + createFogVolumeTorus, createLevelSetBox, CreateNanoGrid, + updateGridStats, evalChecksum, validateChecksum, checkGrid, + Extrema. + - These APIs are now under the util namespace: is_floating_point, + findLowestOn, findHighestOn, Range, streq, strcpy, strcat, + empty, Split, invoke, forEach, reduce, prefixSum, is_same, + is_specialization, PtrAdd, PtrDiff. + + - Move nanovdb::build to nanovdb::tools::build. + - Rename nanovdb::BBoxR to nanovdb::Vec3dBBox. + - Rename nanovdb::BBox to nanovdb::Vec3dBbox. + - Move nanovdb::cudaCreateNodeManager to nanovdb::cuda::createNodeManager. + - Move and rename nanovdb::cudaVoxelsToGrid to nanovdb::cuda::voxelsToGrid. + - Move and rename nanovdb::cudaPointsToGrid to nanovdb::cuda::pointsToGrid. + - Move nanovdb::DitherLUT to nanovdb::math::DitherLUT. + - Move and rename nanovdb::PackedRGBA8 to nanovdb::math::Rgba8. + - Move nanovdb::Rgba8 to nanovdb::math::Rgba8. + - Move and rename nanovdb::CpuTimer to nanovdb::util::Timer. + - Move nanovdb::GpuTimer to nanovdb::util::cuda::Timer. + - Move and rename nanovdb::CountOn to nanovdb::util::countOn. + + - Move util/GridHandle.h to GridHandle.h. + - Move util/BuildGrid.h to tools/GridBuilder.h. + - Move util/GridBuilder.h to tools/GridBuilder.h. + - Move util/IO.h to io/IO.h. + - Move util/CSampleFromVoxels.h to math/CSampleFromVoxels.h. + - Move util/DitherLUT.h to math/DitherLUT.h. + - Move util/HDDA.h to math/HDDA.h. + - Move util/Ray.h to math/Ray.h. + - Move util/SampleFromVoxels.h to math/SampleFromVoxels.h. + - Move util/Stencils.h to nanovdb/math/Stencils.h. + - Move util/CreateNanoGrid.h to tools/CreateNanoGrid.h. + - Move and rename util/Primitives.h to tools/CreatePrimitives.h. + - Move util/GridChecksum.h to tools/GridChecksum.h. + - Move util/GridStats.h to tools/GridStats.h. + - Move util/GridChecksum.h to tools/GridChecksum.h. + - Move util/GridValidator.h to tools/GridValidator.h. + - Move util/NanoToOpenVDB.h to tools/NanoToOpenVDB.h. + - Move util/cuda/CudaGridChecksum.cuh to tools/cuda/CudaGridChecksum.cuh. + - Move util/cuda/CudaGridStats.cuh to tools/cuda/CudaGridStats.cuh. + - Move util/cuda/CudaGridValidator.cuh to tools/cuda/CudaGridValidator.cuh. + - Move util/cuda/CudaIndexToGrid.cuh to tools/cuda/CudaIndexToGrid.cuh. + - Move and rename util/cuda/CudaPointsToGrid.cuh to tools/cuda/PointsToGrid.cuh. + - Move util/cuda/CudaSignedFloodFill.cuh to tools/cuda/CudaSignedFloodFill.cuh. + - Move and rename util/cuda/CudaDeviceBuffer.h to cuda/DeviceBuffer.h. + - Move and rename util/cuda/CudaGridHandle.cuh to cuda/GridHandle.cuh. + - Move and rename util/cuda/CudaUtils.h to util/cuda/Util.h. + - Move and consolidate util/cuda/GpuTimer.h to util/cuda/Timer.h. + + Houdini: + - When OPENVDB_ENABLE_RPATH is ON, the location of + libopenvdb_houdini is now added to the rpath of all Houdini + dsos. + + Python: + - OpenVDB Python bindings are now implemented using nanobind + instead of pybind11 + - The OpenVDB Python module has been changed from pyopenvdb to openvdb + - Added Python bindings for NanoVDB + + Build: + - Fixed an issue with OpenVDB AX's CMake on Windows where the + static and shared library targets would have the same name + [Reported by Nicholas Yue] + - USE_EXPLICIT_INSTANTIATION is now disabled on Windows by default + due to OOM linker issues. + - Jemalloc is now the preferred allocator of choice on all + platforms when CONCURRENT_MALLOC is set to Auto. + - Fixed an issue with the Blosc CMake FindPackage for the OpenVDB + Windows static library. Version 11.0.0 - November 1, 2023 @@ -399,7 +602,6 @@ Version 9.1.0 - June 9, 2022 Positional arguments as input files are deprecated. - Added tools::minMax() which supports multithreaded evaluation of active minimum and maximum values. Grid::evalMinMax() has been deprecated. - [Contributed by Greg Hurst] - Significant performance improvements to AX point kernels, primarily due to providing AX access to attribute buffers for superior code generation. - vdb_print now prints both the in-core memory and total memory usage for diff --git a/CMakeLists.txt b/CMakeLists.txt index b3cb513d11..e17ee726b2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,7 +42,7 @@ # note: cmake_minimum_required must be called before project commands to # ensure policy scope is set up correctly -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.20) # CMP0091 allows for MSVC ABI targetting via CMAKE_MSVC_RUNTIME_LIBRARY # from CMake 3.15 and above. Must come before project(). @@ -52,7 +52,7 @@ endif() ###### Version -set(OpenVDB_MAJOR_VERSION 11) +set(OpenVDB_MAJOR_VERSION 12) set(OpenVDB_MINOR_VERSION 0) set(OpenVDB_PATCH_VERSION 1) set(OpenVDB_VERSION "${OpenVDB_MAJOR_VERSION}.${OpenVDB_MINOR_VERSION}.${OpenVDB_PATCH_VERSION}") @@ -285,6 +285,30 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") # Add cmake modules to installation command +if(SKBUILD) + set(OPENVDB_INSTALL_BINDIR openvdb/${CMAKE_INSTALL_BINDIR}) + set(OPENVDB_INSTALL_LIBDIR openvdb/${CMAKE_INSTALL_LIBDIR}) + set(OPENVDB_INSTALL_INCLUDEDIR openvdb/${CMAKE_INSTALL_INCLUDEDIR}) + + if(OPENVDB_BUILD_NANOVDB) + set(NANOVDB_INSTALL_BINDIR nanovdb/${CMAKE_INSTALL_BINDIR}) + set(NANOVDB_INSTALL_LIBDIR nanovdb/${CMAKE_INSTALL_LIBDIR}) + set(NANOVDB_INSTALL_INCLUDEDIR nanovdb/${CMAKE_INSTALL_INCLUDEDIR}) + set(NANOVDB_INSTALL_DOCDIR nanovdb/${CMAKE_INSTALL_DOCDIR}) + endif() +else() + set(OPENVDB_INSTALL_BINDIR ${CMAKE_INSTALL_BINDIR}) + set(OPENVDB_INSTALL_LIBDIR ${CMAKE_INSTALL_LIBDIR}) + set(OPENVDB_INSTALL_INCLUDEDIR ${CMAKE_INSTALL_INCLUDEDIR}) + + if(OPENVDB_BUILD_NANOVDB) + set(NANOVDB_INSTALL_BINDIR ${CMAKE_INSTALL_BINDIR}) + set(NANOVDB_INSTALL_LIBDIR ${CMAKE_INSTALL_LIBDIR}) + set(NANOVDB_INSTALL_INCLUDEDIR ${CMAKE_INSTALL_INCLUDEDIR}) + set(NANOVDB_INSTALL_DOCDIR ${CMAKE_INSTALL_DOCDIR}) + endif() +endif() + if(OPENVDB_INSTALL_CMAKE_MODULES) set(OPENVDB_CMAKE_MODULES cmake/FindBlosc.cmake @@ -298,7 +322,7 @@ if(OPENVDB_INSTALL_CMAKE_MODULES) cmake/OpenVDBMayaSetup.cmake cmake/OpenVDBUtils.cmake ) - install(FILES ${OPENVDB_CMAKE_MODULES} DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/OpenVDB) + install(FILES ${OPENVDB_CMAKE_MODULES} DESTINATION ${OPENVDB_INSTALL_LIBDIR}/cmake/OpenVDB) endif() # Configure component dependencies by loading the Houdini/Maya setup @@ -453,65 +477,24 @@ endif() # Locate Python and nanobind if necessary if(OPENVDB_BUILD_PYTHON_MODULE OR (OPENVDB_BUILD_NANOVDB AND NANOVDB_BUILD_PYTHON_MODULE)) - # Small function which mimics basic output (bar components) of - # FindPackageHandleStandardArgs. This is required as we want to ensure - # the minimum python version is MINIMUM_PYTHON_VERSION - however this cannot - # be provided to find_package(Python) with differing major versions. e.g. - # calls to find_package(Python 2.7) fails if python3 is found on the system. - function(OPENVDB_CHECK_PYTHON_VERSION) - set(PY_TARGET ${ARGV0}) - set(PY_TARGET_VERSION ${ARGV1}) - set(PY_TARGET_INCLUDES ${ARGV2}) - set(MIN_VERSION ${ARGV3}) - set(FUTURE_MIN_VERSION ${ARGV4}) - - if(NOT TARGET ${PY_TARGET}) - message(FATAL_ERROR "Could NOT find ${PY_TARGET} (Required is at least version " - "\"${MIN_VERSION}\")" - ) - endif() - if(PY_TARGET_VERSION AND MIN_VERSION) - if(PY_TARGET_VERSION VERSION_LESS MIN_VERSION) - message(FATAL_ERROR "Could NOT find ${PY_TARGET}: Found unsuitable version " - "\"${PY_TARGET_VERSION}\" but required is at least \"${MIN_VERSION}\" (found ${PY_TARGET_INCLUDES})" - ) - endif() - endif() + # Call find_package(Python ...) + find_package(Python ${MINIMUM_PYTHON_VERSION} REQUIRED COMPONENTS Development Interpreter) + find_package(nanobind ${MINIMUM_NANOBIND_VERSION} REQUIRED) - message(STATUS "Found ${PY_TARGET}: ${PY_TARGET_INCLUDES}) (found suitable " - "version \"${PY_TARGET_VERSION}\", minimum required is \"${MIN_VERSION}\")" - ) - - if(OPENVDB_FUTURE_DEPRECATION AND PY_TARGET_VERSION AND FUTURE_MIN_VERSION) - if(PY_TARGET_VERSION VERSION_LESS FUTURE_MIN_VERSION) - message(DEPRECATION "Support for ${PY_TARGET} versions < ${FUTURE_MIN_VERSION} " - "is deprecated and will be removed.") - endif() + if(OPENVDB_FUTURE_DEPRECATION AND FUTURE_MINIMUM_PYTHON_VERSION) + if(Python_VERSION VERSION_LESS ${FUTURE_MINIMUM_PYTHON_VERSION}) + message(DEPRECATION "Support for Python versions < ${FUTURE_MINIMUM_GLFW_VERSION} " + "is deprecated and will be removed.") endif() - endfunction() - - # Configure Python and Numpy. - # To ensure consistent versions between components Interpreter, Compiler, - # Development and NumPy, specify all components at the same time when using - # FindPython. - - # @note explicitly only search for Development.Module from 3.18 as searching - # Development.Embed can cause issues on linux systems where it doesn't exist - set(OPENVDB_PYTHON_REQUIRED_COMPONENTS Development Interpreter) - - # Make sure find_package(Python) is only ever invoked once with all required components - find_package(Python 3.8 REQUIRED COMPONENTS ${OPENVDB_PYTHON_REQUIRED_COMPONENTS}) - find_package(nanobind REQUIRED) - - openvdb_check_python_version(Python::Module - "${Python_VERSION}" - "${Python_INCLUDE_DIRS}" - "${MINIMUM_PYTHON_VERSION}" - "${FUTURE_MINIMUM_PYTHON_VERSION}") + endif() if(NOT DEFINED VDB_PYTHON_INSTALL_DIRECTORY) - get_filename_component(Python_PACKAGES_DIR ${Python_SITELIB} NAME) + if(DEFINED Python_SITELIB) + get_filename_component(Python_PACKAGES_DIR ${Python_SITELIB} NAME) + else() + set(Python_PACKAGES_DIR "site-packages") + endif() set(VDB_PYTHON_INSTALL_DIRECTORY ${CMAKE_INSTALL_LIBDIR}/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}/${Python_PACKAGES_DIR} CACHE STRING "The directory to install the openvdb and nanovdb Python modules." diff --git a/ci/build.sh b/ci/build.sh index d2e8e96603..b0b3c8dfb1 100755 --- a/ci/build.sh +++ b/ci/build.sh @@ -1,4 +1,6 @@ #!/usr/bin/env bash +# Copyright Contributors to the OpenVDB Project +# SPDX-License-Identifier: Apache-2.0 set -e @@ -184,11 +186,12 @@ set -x # - always enabled the python tests with OPENVDB_BUILD_PYTHON_UNITTESTS if the python module is in use, # regardless of the 'test' component being enabled or not (see the OPENVDB_BUILD_PYTHON_UNITTESTS option). cmake \ - -DOPENVDB_USE_DEPRECATED_ABI_9=ON \ -DOPENVDB_USE_DEPRECATED_ABI_10=ON \ + -DOPENVDB_USE_DEPRECATED_ABI_11=ON \ -DOPENVDB_BUILD_VDB_PRINT=ON \ -DOPENVDB_BUILD_VDB_LOD=ON \ -DOPENVDB_BUILD_VDB_TOOL=ON \ + -DOPENVDB_BUILD_VDB_TOOL_UNITTESTS=ON \ -DOPENVDB_TOOL_USE_NANO=OFF \ -DOPENVDB_BUILD_PYTHON_UNITTESTS=ON \ -DMSVC_MP_THREAD_COUNT=${PARMS[-j]} \ diff --git a/ci/build_sonar.sh b/ci/build_sonar.sh index 10012a5c43..1e720bc626 100755 --- a/ci/build_sonar.sh +++ b/ci/build_sonar.sh @@ -1,4 +1,6 @@ #!/usr/bin/env bash +# Copyright Contributors to the OpenVDB Project +# SPDX-License-Identifier: Apache-2.0 set -ex diff --git a/ci/download_houdini.sh b/ci/download_houdini.sh index 77aa8e3bd9..0f63a4eceb 100755 --- a/ci/download_houdini.sh +++ b/ci/download_houdini.sh @@ -1,4 +1,6 @@ #!/usr/bin/env bash +# Copyright Contributors to the OpenVDB Project +# SPDX-License-Identifier: Apache-2.0 set -ex diff --git a/ci/download_vdb_caches.py b/ci/download_vdb_caches.py index 8fa6dadb74..55edaaca74 100755 --- a/ci/download_vdb_caches.py +++ b/ci/download_vdb_caches.py @@ -1,4 +1,6 @@ #!/usr/bin/env python3 +# Copyright Contributors to the OpenVDB Project +# SPDX-License-Identifier: Apache-2.0 import os import sys diff --git a/ci/extract_test_examples.sh b/ci/extract_test_examples.sh index cc892c12b7..5cdf559863 100755 --- a/ci/extract_test_examples.sh +++ b/ci/extract_test_examples.sh @@ -1,4 +1,6 @@ #!/usr/bin/env bash +# Copyright Contributors to the OpenVDB Project +# SPDX-License-Identifier: Apache-2.0 ################################################################################# # This script extracts all code blocks from AX documentation which are NOT # # marked as cpp/sh/unparsed and attempts to parse or compile them through the # diff --git a/ci/install_blosc.sh b/ci/install_blosc.sh index 0436fa875e..e40d233f5b 100755 --- a/ci/install_blosc.sh +++ b/ci/install_blosc.sh @@ -1,4 +1,6 @@ #!/usr/bin/env bash +# Copyright Contributors to the OpenVDB Project +# SPDX-License-Identifier: Apache-2.0 set -ex diff --git a/ci/install_cppunit.sh b/ci/install_cppunit.sh index 7f64238e8a..da7cc300b3 100755 --- a/ci/install_cppunit.sh +++ b/ci/install_cppunit.sh @@ -1,4 +1,6 @@ #!/usr/bin/env bash +# Copyright Contributors to the OpenVDB Project +# SPDX-License-Identifier: Apache-2.0 set -ex CURL_VERSION="$1" diff --git a/ci/install_doxygen.sh b/ci/install_doxygen.sh index 5c3ab3136d..d89ac38510 100755 --- a/ci/install_doxygen.sh +++ b/ci/install_doxygen.sh @@ -1,4 +1,6 @@ #!/usr/bin/env bash +# Copyright Contributors to the OpenVDB Project +# SPDX-License-Identifier: Apache-2.0 set -ex diff --git a/ci/install_glfw.sh b/ci/install_glfw.sh index 33108657f4..faeb243ae7 100755 --- a/ci/install_glfw.sh +++ b/ci/install_glfw.sh @@ -1,4 +1,6 @@ #!/usr/bin/env bash +# Copyright Contributors to the OpenVDB Project +# SPDX-License-Identifier: Apache-2.0 set -ex GLFW_VERSION="$1" diff --git a/ci/install_gtest.sh b/ci/install_gtest.sh new file mode 100755 index 0000000000..9084b31cf4 --- /dev/null +++ b/ci/install_gtest.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +# Copyright Contributors to the OpenVDB Project +# SPDX-License-Identifier: Apache-2.0 + +set -ex + +GTEST_VERSION="$1" + + +git clone https://github.com/google/googletest.git -b v${GTEST_VERSION} +cd googletest +mkdir build +cd build +cmake .. + +make -j$(nproc) + +sudo make install diff --git a/ci/install_llvm_windows.sh b/ci/install_llvm_windows.sh index 642013d46a..d8866520f0 100644 --- a/ci/install_llvm_windows.sh +++ b/ci/install_llvm_windows.sh @@ -1,10 +1,12 @@ #!/usr/bin/env bash +# Copyright Contributors to the OpenVDB Project +# SPDX-License-Identifier: Apache-2.0 set -ex LLVM_CRT="$1" -git clone -b llvmorg-12.0.0 --depth 1 https://github.com/llvm/llvm-project.git llvm +git clone -b llvmorg-14.0.0 --depth 1 https://github.com/llvm/llvm-project.git llvm cd llvm mkdir .build diff --git a/ci/install_macos.sh b/ci/install_macos.sh index 80b71d4864..a581229035 100755 --- a/ci/install_macos.sh +++ b/ci/install_macos.sh @@ -1,4 +1,6 @@ #!/usr/bin/env bash +# Copyright Contributors to the OpenVDB Project +# SPDX-License-Identifier: Apache-2.0 set -x diff --git a/ci/install_nanobind.sh b/ci/install_nanobind.sh index 8103a4fabe..8a885a6b4a 100755 --- a/ci/install_nanobind.sh +++ b/ci/install_nanobind.sh @@ -1,4 +1,6 @@ #!/usr/bin/env bash +# Copyright Contributors to the OpenVDB Project +# SPDX-License-Identifier: Apache-2.0 set -ex @@ -25,4 +27,4 @@ cmake \ .. make -j8 -make install +sudo make install diff --git a/ci/install_tbb_macos.sh b/ci/install_tbb_macos.sh index 4b71fe9e93..cb8d8d9595 100755 --- a/ci/install_tbb_macos.sh +++ b/ci/install_tbb_macos.sh @@ -1,4 +1,6 @@ #!/usr/bin/env bash +# Copyright Contributors to the OpenVDB Project +# SPDX-License-Identifier: Apache-2.0 set -x diff --git a/ci/test_install.sh b/ci/test_install.sh index be6a477dd8..5ff44aecd4 100755 --- a/ci/test_install.sh +++ b/ci/test_install.sh @@ -1,4 +1,6 @@ #!/usr/bin/env bash +# Copyright Contributors to the OpenVDB Project +# SPDX-License-Identifier: Apache-2.0 set -e # Various tests to test the FindOpenVDB CMake modules and @@ -8,7 +10,7 @@ set -e # the expected VDB installation cmakelists=" -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.20) project(TestInstall LANGUAGES CXX) find_package(OpenVDB REQUIRED COMPONENTS openvdb) add_executable(test_vdb_print \"../openvdb_cmd/vdb_print/main.cc\") diff --git a/ci/test_sonar.sh b/ci/test_sonar.sh index 15cc571d23..c8f72a0c86 100755 --- a/ci/test_sonar.sh +++ b/ci/test_sonar.sh @@ -1,4 +1,6 @@ #!/usr/bin/env bash +# Copyright Contributors to the OpenVDB Project +# SPDX-License-Identifier: Apache-2.0 set -ex diff --git a/cmake/FindBlosc.cmake b/cmake/FindBlosc.cmake index fedf1ad4d3..158050e216 100644 --- a/cmake/FindBlosc.cmake +++ b/cmake/FindBlosc.cmake @@ -88,7 +88,7 @@ may be provided to tell this module where to look. #]=======================================================================] -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.20) include(GNUInstallDirs) mark_as_advanced( diff --git a/cmake/FindCppUnit.cmake b/cmake/FindCppUnit.cmake index 8d9f973ebd..34bdced9ed 100644 --- a/cmake/FindCppUnit.cmake +++ b/cmake/FindCppUnit.cmake @@ -83,7 +83,7 @@ may be provided to tell this module where to look. #]=======================================================================] -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.20) include(GNUInstallDirs) diff --git a/cmake/FindJemalloc.cmake b/cmake/FindJemalloc.cmake index 7f8a483cd0..9b2e14555c 100644 --- a/cmake/FindJemalloc.cmake +++ b/cmake/FindJemalloc.cmake @@ -64,7 +64,7 @@ may be provided to tell this module where to look. #]=======================================================================] -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.20) include(GNUInstallDirs) diff --git a/cmake/FindLog4cplus.cmake b/cmake/FindLog4cplus.cmake index dc00598960..7e3a584806 100644 --- a/cmake/FindLog4cplus.cmake +++ b/cmake/FindLog4cplus.cmake @@ -87,7 +87,7 @@ may be provided to tell this module where to look. #]=======================================================================] -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.20) include(GNUInstallDirs) diff --git a/cmake/FindOpenEXR.cmake b/cmake/FindOpenEXR.cmake index 4601a0fbbb..a8aea21d01 100644 --- a/cmake/FindOpenEXR.cmake +++ b/cmake/FindOpenEXR.cmake @@ -93,7 +93,7 @@ may be provided to tell this module where to look. #]=======================================================================] -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.20) include(GNUInstallDirs) diff --git a/cmake/FindOpenVDB.cmake b/cmake/FindOpenVDB.cmake index e35b5f5a0d..33b08388c0 100644 --- a/cmake/FindOpenVDB.cmake +++ b/cmake/FindOpenVDB.cmake @@ -109,7 +109,7 @@ may be provided to tell this module where to look. #]=======================================================================] -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.20) include(GNUInstallDirs) @@ -334,10 +334,8 @@ set(_OPENVDB_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) set(OPENVDB_PYTHON_PATH_SUFFIXES lib64/python - lib64/python2.7 lib64/python3 lib/python - lib/python2.7 lib/python3 ) diff --git a/cmake/FindTBB.cmake b/cmake/FindTBB.cmake index db5601249c..eda06bb754 100644 --- a/cmake/FindTBB.cmake +++ b/cmake/FindTBB.cmake @@ -93,7 +93,7 @@ may be provided to tell this module where to look. #]=======================================================================] -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.20) include(GNUInstallDirs) diff --git a/cmake/OpenVDBGLFW3Setup.cmake b/cmake/OpenVDBGLFW3Setup.cmake index 78e2f2170f..26980fc6ff 100644 --- a/cmake/OpenVDBGLFW3Setup.cmake +++ b/cmake/OpenVDBGLFW3Setup.cmake @@ -47,7 +47,7 @@ The following variables may be provided to tell this module where to look. # Find the glfw3 installation and use glfw's CMake to initialize # the glfw lib -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.20) set(_FIND_GLFW3_ADDITIONAL_OPTIONS "") if(DISABLE_CMAKE_SEARCH_PATHS) @@ -109,18 +109,6 @@ endif() set(glfw3_FIND_VERSION ${MINIMUM_GLFW_VERSION}) find_package(glfw3 ${MINIMUM_GLFW_VERSION} REQUIRED) -# We only use find_package_handle_standard_args to verify and print -# appropriate messages. This now explicitly errors in 3.19... -# @todo Improve this entire GLFW3 search -# https://gitlab.kitware.com/cmake/cmake/-/issues/21505 -if(${CMAKE_VERSION} VERSION_LESS 3.19) - find_package(PackageHandleStandardArgs) - find_package_handle_standard_args(glfw3 - REQUIRED_VARS glfw3_DIR glfw3_FOUND - VERSION_VAR glfw3_VERSION - ) -endif() - if(OPENVDB_FUTURE_DEPRECATION AND FUTURE_MINIMUM_GLFW_VERSION) if(glfw3_VERSION VERSION_LESS ${FUTURE_MINIMUM_GLFW_VERSION}) message(DEPRECATION "Support for GLFW versions < ${FUTURE_MINIMUM_GLFW_VERSION} " diff --git a/cmake/OpenVDBHoudiniSetup.cmake b/cmake/OpenVDBHoudiniSetup.cmake index b28174f12d..a4ab24f2e0 100644 --- a/cmake/OpenVDBHoudiniSetup.cmake +++ b/cmake/OpenVDBHoudiniSetup.cmake @@ -73,7 +73,7 @@ may be provided to tell this module where to look. # Find the Houdini installation and use Houdini's CMake to initialize # the Houdini lib -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.20) # Include utility functions for version information include(${CMAKE_CURRENT_LIST_DIR}/OpenVDBUtils.cmake) diff --git a/cmake/OpenVDBMayaSetup.cmake b/cmake/OpenVDBMayaSetup.cmake index fb05ae919b..4ff3166a2e 100644 --- a/cmake/OpenVDBMayaSetup.cmake +++ b/cmake/OpenVDBMayaSetup.cmake @@ -51,7 +51,7 @@ variables may be provided to tell this module where to look. # Find the Maya installation and use Maya's CMake to initialize # the Maya lib -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.20) set(_FIND_MAYA_ADDITIONAL_OPTIONS "") diff --git a/cmake/OpenVDBUtils.cmake b/cmake/OpenVDBUtils.cmake index 6e8426c112..b2f07c7549 100644 --- a/cmake/OpenVDBUtils.cmake +++ b/cmake/OpenVDBUtils.cmake @@ -57,7 +57,7 @@ The following functions are provided: #]=======================================================================] -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.20) function(OPENVDB_GET_VERSION_DEFINE HEADER KEY VALUE) diff --git a/cmake/Uninstall.cmake b/cmake/Uninstall.cmake index d2a99e3faf..e95c4c5229 100644 --- a/cmake/Uninstall.cmake +++ b/cmake/Uninstall.cmake @@ -16,7 +16,7 @@ existing from a previous run of cmake. #]=======================================================================] -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.20) set(MANIFEST "${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt") diff --git a/cmake/config/OpenVDBCXX.cmake b/cmake/config/OpenVDBCXX.cmake index 365c5d5cde..3586732bdb 100644 --- a/cmake/config/OpenVDBCXX.cmake +++ b/cmake/config/OpenVDBCXX.cmake @@ -7,7 +7,7 @@ #]=======================================================================] -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.20) ############################################################################### diff --git a/cmake/config/OpenVDBVersions.cmake b/cmake/config/OpenVDBVersions.cmake index fadad1104d..0b8dd5ef6a 100644 --- a/cmake/config/OpenVDBVersions.cmake +++ b/cmake/config/OpenVDBVersions.cmake @@ -8,7 +8,7 @@ #]=======================================================================] -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.20) ############################################################################### @@ -34,24 +34,26 @@ if(NOT DISABLE_DEPENDENCY_VERSION_CHECKS) # What's usually important is that the version of libstdc++ and glibc in use # matches. Compilers other than GCC should provide options to ensure this # targetting e.g. --gcc-toolchain and -fgnuc-version with Clang. - set(MINIMUM_GCC_VERSION 9.3.1) + set(MINIMUM_GCC_VERSION 11.2.1) set(MINIMUM_CLANG_VERSION 5.0) set(MINIMUM_ICC_VERSION 19) - set(MINIMUM_MSVC_VERSION 19.28) # 1928 (Visual Studio 2019 Version 16.8 + 16.9) + set(MINIMUM_MSVC_VERSION 19.30) # 1928 (Visual Studio 2019 Version 16.8 + 16.9) - # Should be 1.76 for VFX 22, but only version in apt is 1.73 - set(MINIMUM_BOOST_VERSION 1.73) - set(MINIMUM_PYBIND_VERSION 2.9.1) + set(MINIMUM_BOOST_VERSION 1.80) + # Nanobind does not store the version in the CMake config file in 2.0.0. + # This issue was fixed in 2.1.0 so next time we bump the minimum version, + # we can define this. + #set(MINIMUM_NANOBIND_VERSION 2.0.0) set(MINIMUM_IMATH_VERSION 3.1) set(MINIMUM_OPENEXR_VERSION 3.1) set(MINIMUM_ZLIB_VERSION 1.2.7) set(MINIMUM_TBB_VERSION 2020.3) - set(MINIMUM_LLVM_VERSION 10.0.0) + set(MINIMUM_LLVM_VERSION 13.0.0) set(MINIMUM_BLOSC_VERSION 1.17.0) set(MINIMUM_GLFW_VERSION 3.1) - set(MINIMUM_PYTHON_VERSION 3.9.1) - set(MINIMUM_NUMPY_VERSION 1.20.0) + set(MINIMUM_PYTHON_VERSION 3.10) + set(MINIMUM_NUMPY_VERSION 1.23.0) set(MINIMUM_GOOGLETEST_VERSION 1.10) set(MINIMUM_LOG4CPLUS_VERSION 1.1.2) @@ -66,20 +68,21 @@ endif() # @note At the time of writing, any variables that are commented out don't # have target transitional versions. -set(FUTURE_MINIMUM_GCC_VERSION 11.2.1) -set(FUTURE_MINIMUM_MSVC_VERSION 19.30) # 1930 (Visual Studio 2022) +# set(FUTURE_MINIMUM_GCC_VERSION 11.2.1) +# set(FUTURE_MINIMUM_MSVC_VERSION 19.30) # set(FUTURE_MINIMUM_ICC_VERSION 19) # set(FUTURE_MINIMUM_CXX_STANDARD 20) -set(FUTURE_MINIMUM_CMAKE_VERSION 3.20) -# set(FUTURE_MINIMUM_OPENEXR_VERSION 3.1) -set(FUTURE_MINIMUM_BOOST_VERSION 1.80) +set(FUTURE_MINIMUM_CMAKE_VERSION 3.24) +set(FUTURE_MINIMUM_OPENEXR_VERSION 3.2) +set(FUTURE_MINIMUM_BOOST_VERSION 1.82) set(FUTURE_MINIMUM_GLFW_VERSION 3.3) set(FUTURE_MINIMUM_LOG4CPLUS_VERSION 2.0) +# set(FUTURE_MINIMUM_NANOBIND_VERSION 2.1.0) # set(FUTURE_MINIMUM_BLOSC_VERSION 1.17.0) # set(FUTURE_MINIMUM_TBB_VERSION 2020.3) -set(FUTURE_MINIMUM_PYTHON_VERSION 3.10) -set(FUTURE_MINIMUM_NUMPY_VERSION 1.23.0) +set(FUTURE_MINIMUM_PYTHON_VERSION 3.11) +set(FUTURE_MINIMUM_NUMPY_VERSION 1.26.0) # set(FUTURE_MINIMUM_HOUDINI_VERSION 20.0) -set(FUTURE_MINIMUM_LLVM_VERSION 13.0.0) +set(FUTURE_MINIMUM_LLVM_VERSION 15.0.0) diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index a6f4f26731..67e5f259e5 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -7,7 +7,7 @@ #]=======================================================================] -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.20) project(OpenVDBDocs LANGUAGES NONE) include(GNUInstallDirs) diff --git a/doc/build.txt b/doc/build.txt index 83c5788c49..c04e870aa3 100644 --- a/doc/build.txt +++ b/doc/build.txt @@ -238,10 +238,8 @@ it's a good idea to read the above section on DCC | Supported Version | OpenVDB ABI | -------- | ----------------- | ----------- | -Houdini | 19.5 | 9 | Houdini | 20.0 | 10 | -Maya | 2018 | Any | -Maya | 2019 | Any | +Houdini | 20.5 | 11 | @subsection buildBuildHou Building Against Houdini @@ -514,7 +512,7 @@ You can add the below CMake snippet to your main `CMakeLists.txt` to bring in OpenVDB as a dependency: @code{.cmake} -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.20) list(APPEND CMAKE_MODULE_PATH "/location/of/openvdb/install/lib/cmake/OpenVDB") find_package(OpenVDB REQUIRED) target_link_libraries(myapp OpenVDB::openvdb) @@ -607,13 +605,6 @@ OpenVDB uses [imported targets](https://cmake.org/cmake/help/latest/command/add_ for all its dependencies. For imported Boost compatibility, the following versions of CMake are required: - - Boost 1.73 requires CMake 3.17.2 or newer. - - Boost 1.74 requires CMake 3.19 or newer. - - Boost 1.75 requires CMake 3.19.5 or newer. - - Boost 1.76 requires CMake 3.20.3 or newer. - - Boost 1.77 requires CMake 3.21.3 or newer. - - Boost 1.78 requires CMake 3.22.2 or newer. - - Boost 1.79 requires CMake 3.23.2 or newer. - Boost 1.80 requires CMake 3.24.2 or newer. - Boost 1.81 requires CMake 3.25.2 or newer. - Boost 1.82 requires CMake 3.27.0 or newer. diff --git a/doc/changes.txt b/doc/changes.txt index b7435d86d2..1ed11d5f11 100644 --- a/doc/changes.txt +++ b/doc/changes.txt @@ -2,6 +2,231 @@ @page changes Release Notes +@htmlonly @endhtmlonly +@par +Version 12.0.1 - In development + +@htmlonly @endhtmlonly +@par +Version 12.0.0 - October 31, 2024 + +@par +
+OpenVDB is now licensed under the Apache 2.0 license, instead of the MPL 2.0 license. +
+ +@par +
+This version introduces ABI changes relative to older major releases, so to +preserve ABI compatibility it might be necessary to define the macro +OPENVDB_ABI_VERSION_NUMBER=N, where, for example, +N is 10 for Houdini 20.0 and 11 for Houdini 20.5. +
+ +@par +
+GCC 9 is no longer supported. +
+ +@par +OpenVDB: +- New features: + - Added tools::LevelSetFilter::fillet() method to round off concave edges + to create smoother transition between surfaces. + +- Improvements: + - Added openvdb::assertAbort to replace cassert and a + @c OPENVDB_ENABLE_ASSERTS cmake argument/compile define to toggle + assertions in OpenVDB code, independantly of NDEBUG. Asserts are + no longer enabled by default in when NDEBUG is absent (e.g. + Debug builds). + - Removed last traces of Boost when @c OPENVDB_USE_DELAYED_LOADING is OFF + [Reported by Brian McKinnon] + - RootNode code cleanup to eliminate redundant key conversion and + to create map values in-place. + - Add RootNode::deleteChildOrTile() to delete a child or tile of + the root node. + - ValueAccessors are now defined and created in the Tree class + instead of in the Grid class so that custom Tree implementations + may define and create their own ValueAccessors if desired. + - Added support for PDAL to vdb_tool [Contributed by Tom Matterson] + - LeafManager and NodeManager now use Index64 for leaf counts internally. + - 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. + - Added InternalNode::isValueOff(), LeafNode::isValueOff(), + LeafNodeBool::isValueOff(), LeafNodeMask::isValueOff(). + - Added LeafNodeMask::probeValue(Index,val), LeafNodeBool::probeValue(Index,val). + - Added RootNode::getValueUnsafe(), RootNode::getChildUnsafe(), + RootNode::getConstChildUnsafe(). + - Added InternalNode::getValueUnsafe(), InternalNode::getChildUnsafe(), + InternalNode::getConstChildUnsafe(). + - Added InternalNode::setActiveStateUnsafe(), InternalNode::setValueOnlyUnsafe(), + InternalNode::setValueOnUnsafe(), InternalNode::setValueOffUnsafe(). + - Added InternalNode::setChildUnsafe(), InternalNode::resetChildUnsafe(), + InternalNode::stealChildUnsafe(), InternalNode::deleteChildUnsafe(). + - For LeafNode, LeafNodeBool and LeafNodeMask - added + LeafNode::getValueUnsafe(), LeafNode::setActiveStateunsafe(), + LeafNode::setValueOnlyUnsafe(), LeafNode::setValueOnUnsafe(), + LeafNode::setValueOffUnsafe(). + +- ABI changes: + - Tree::leafCount(), Tree::unallocatedLeafCount(), + Tree::nonLeafCount() and Tree::nodeCount() now use Index64 in their + return types instead of Index32. + +- API Changes: + - RootNode::tileCount(), RootNode::activeTileCount() and + RootNode::inactiveTileCount() are now public. + - RootNode::hasKey() and RootNode::coordToKey() are now public. + - RootNode::leafCount(), RootNode::nonLeafCount() and RootNode::nodeCount() + now use Index64 instead of Index32. The Index32 variant is deprecated. + - InternalNode::leafCount(), InternalNode::nonLeafCount() and + InternalNode::nodeCount() now use Index64 instead of Index32. The Index32 + variant is deprecated. + - LeafNode::leafCount() and LeafNode::nonLeafCount() now use Index64 instead + of Index32. The Index32 variant is deprecated. + +- Bug Fixes: + - Fix potential crash reading corrupt .vdb files with invalid + blosc or zip chunks. + [Reported by Matthias Ueberheide] + + - Fix a bug in RootNode::setOrigin() where the origin was updated + before the error was thrown potentially leaving the root in an + invalid state. + - Fixed a thread sanitizer issue which could cause undefined + behaviour in VolumeToSpheres::fillWithSpheres + [Reported by Jérémie Dumas] + - Fixed an occurance of undefined behaviour in tools::activate + (though this would typically not have manifested with any + unintended behaviour) + +@par +NanoVDB: +- Bug fix: + - nanovdb::readGrids works with raw grid buffer. + +- Improvements: + - Restructure files location and namespace to be more align with + OpenVDB. The namespaces touched by the restructuring are: io, + cuda, util, tools, and math. + - Add two scripts updateFiles.sh and updateFiles.py to update the + files using NanoVDB. The script updateFiles.py works on both + Windows and Linux. For a more complete list of changes, see API + Changes (details). + + - cuda::PointsToGrid supports target density. + - Add support for NanoVDB Grid of type UInt8. + - Add ability to use externally managed CUDA buffer. + - Add create methods for CudaDeviceBuffer and exceptions. + - Improve GridValidator logic, e.g. include check for grid count. + - Add operator > and >= for class Coord according to lexicographical order. + - Add toCodec to convert string to Codec enumeration type. + - Add nanovdb::strlen(). + - Add strncpy util. + - Add @c NANOVDB_DISABLE_SYNC_CUDA_MALLOC that maps cudaMallocAsync + and cudaFreeAsync to cudaMalloc and cudaFree respectively. + - Add guard to @c UINT64_C. + - Remove use of cudaMallocAsync in PointsToGrid.cuh. + - Align PNanoVDB blind metadata to NanoVDB. + +- API Changes: + - Change mapToGridType to toGridType. + - Change mapToMagic to toMagic. + - Change CpuTimer.h to Timer.h. + +- API Changes (details): + - These APIs are now under the math namespace: Ray, DDA, HDDA, + Vec3, Vec4, BBox, ZeroCrossing, TreeMarcher, PointTreeMarcher, + BoxStencil, CurvatureStencil, GradStencil, WenoStencil, AlignUp, + Min, Max, Abs, Clamp, Sqrt, Sign, Maximum, Delta, RoundDown, pi, + isApproxZero, Round, createSampler, SampleFromVoxels. + + - These APIs are now under the tools namespace: createNanoGrid, + StatsMode, createLevelSetSphere, createFogVolumeSphere, + createFogVolumeSphere, createFogVolumeSphere, + createFogVolumeTorus, createLevelSetBox, CreateNanoGrid, + updateGridStats, evalChecksum, validateChecksum, checkGrid, + Extrema. + - These APIs are now under the util namespace: is_floating_point, + findLowestOn, findHighestOn, Range, streq, strcpy, strcat, + empty, Split, invoke, forEach, reduce, prefixSum, is_same, + is_specialization, PtrAdd, PtrDiff. + + - Move nanovdb::build to nanovdb::tools::build. + - Rename nanovdb::BBoxR to nanovdb::Vec3dBBox. + - Rename nanovdb::BBox to nanovdb::Vec3dBbox. + - Move nanovdb::cudaCreateNodeManager to nanovdb::cuda::createNodeManager. + - Move and rename nanovdb::cudaVoxelsToGrid to nanovdb::cuda::voxelsToGrid. + - Move and rename nanovdb::cudaPointsToGrid to nanovdb::cuda::pointsToGrid. + - Move nanovdb::DitherLUT to nanovdb::math::DitherLUT. + - Move and rename nanovdb::PackedRGBA8 to nanovdb::math::Rgba8. + - Move nanovdb::Rgba8 to nanovdb::math::Rgba8. + - Move and rename nanovdb::CpuTimer to nanovdb::util::Timer. + - Move nanovdb::GpuTimer to nanovdb::util::cuda::Timer. + - Move and rename nanovdb::CountOn to nanovdb::util::countOn. + + - Move util/GridHandle.h to GridHandle.h. + - Move util/BuildGrid.h to tools/GridBuilder.h. + - Move util/GridBuilder.h to tools/GridBuilder.h. + - Move util/IO.h to io/IO.h. + - Move util/CSampleFromVoxels.h to math/CSampleFromVoxels.h. + - Move util/DitherLUT.h to math/DitherLUT.h. + - Move util/HDDA.h to math/HDDA.h. + - Move util/Ray.h to math/Ray.h. + - Move util/SampleFromVoxels.h to math/SampleFromVoxels.h. + - Move util/Stencils.h to nanovdb/math/Stencils.h. + - Move util/CreateNanoGrid.h to tools/CreateNanoGrid.h. + - Move and rename util/Primitives.h to tools/CreatePrimitives.h. + - Move util/GridChecksum.h to tools/GridChecksum.h. + - Move util/GridStats.h to tools/GridStats.h. + - Move util/GridChecksum.h to tools/GridChecksum.h. + - Move util/GridValidator.h to tools/GridValidator.h. + - Move util/NanoToOpenVDB.h to tools/NanoToOpenVDB.h. + - Move util/cuda/CudaGridChecksum.cuh to tools/cuda/CudaGridChecksum.cuh. + - Move util/cuda/CudaGridStats.cuh to tools/cuda/CudaGridStats.cuh. + - Move util/cuda/CudaGridValidator.cuh to tools/cuda/CudaGridValidator.cuh. + - Move util/cuda/CudaIndexToGrid.cuh to tools/cuda/CudaIndexToGrid.cuh. + - Move and rename util/cuda/CudaPointsToGrid.cuh to tools/cuda/PointsToGrid.cuh. + - Move util/cuda/CudaSignedFloodFill.cuh to tools/cuda/CudaSignedFloodFill.cuh. + - Move and rename util/cuda/CudaDeviceBuffer.h to cuda/DeviceBuffer.h. + - Move and rename util/cuda/CudaGridHandle.cuh to cuda/GridHandle.cuh. + - Move and rename util/cuda/CudaUtils.h to util/cuda/Util.h. + - Move and consolidate util/cuda/GpuTimer.h to util/cuda/Timer.h. + +@par +Python: + - OpenVDB Python bindings are now implemented using nanobind + instead of pybind11 + - The OpenVDB Python module has been changed from pyopenvdb to openvdb + - Added Python bindings for NanoVDB + +@par +Houdini: + - When @c OPENVDB_ENABLE_RPATH is @c ON, the location of + libopenvdb_houdini is now added to the rpath of all Houdini + dsos. + +@par +Build: + - Fixed an issue with OpenVDB AX's CMake on Windows where the + static and shared library targets would have the same name + [Reported by Nicholas Yue] + - @c USE_EXPLICIT_INSTANTIATION is now disabled on Windows by default + due to OOM linker issues. + - Jemalloc is now the preferred allocator of choice on all + platforms when @c CONCURRENT_MALLOC is set to Auto. + - Fixed an issue with the Blosc CMake FindPackage for the OpenVDB + Windows static library. + + @htmlonly @endhtmlonly @par Version 11.0.0 - November 1, 2023 diff --git a/doc/codingstyle.txt b/doc/codingstyle.txt index 2a9a60aaf9..6f298e83e7 100644 --- a/doc/codingstyle.txt +++ b/doc/codingstyle.txt @@ -7,7 +7,7 @@ This document details the coding practices that are used in the OpenVDB codebase. Contributed code should conform to these guidelines to maintain consistency and maintainability. If there is a rule that you would like -clarified, changed, or added, please send a note to openvdb@gmail.com. +clarified, changed, or added, please send a note to openvdb-dev@lists.aswf.io. @section sStyleContents Contents diff --git a/doc/dependencies.txt b/doc/dependencies.txt index c20ba77bb1..041fb834c0 100644 --- a/doc/dependencies.txt +++ b/doc/dependencies.txt @@ -56,22 +56,22 @@ OpenVDB Documentation | Doxygen | - Package | Minimum | Recommended | Description | apt-get | Homebrew | Source -------------- | ------- | ----------- | ----------------------------------------------------------------- | ------- | -------- | ------ -CMake | 3.18 | Latest | Cross-platform family of tools designed to help build software | Y | Y | https://cmake.org -GCC | 9.3.1 | 11.2.1 | C++ 17 Compiler: The GNU Compiler Collection | Y | Y | https://www.gnu.org/software/gcc +CMake | 3.20 | Latest | Cross-platform family of tools designed to help build software | Y | Y | https://cmake.org +GCC | 11.2.1 | 11.2.1 | C++ 17 Compiler: The GNU Compiler Collection | Y | Y | https://www.gnu.org/software/gcc Clang | 5.0 | Latest | C++ 17 Compiler: A C language family frontend for LLVM | Y | Y | https://clang.llvm.org Intel ICC | 19 | Latest | C++ 17 Compiler: Intels C++ Compiler | Y | Y | https://software.intel.com/en-us/c-compilers -MSVC | 19.28 | 19.30 | C++ 17 Compiler: Microsoft Visual C++ Compiler | Y | Y | https://visualstudio.microsoft.com/vs +MSVC | 19.30 | 19.30 | C++ 17 Compiler: Microsoft Visual C++ Compiler | Y | Y | https://visualstudio.microsoft.com/vs Imath | 3.1 | Latest | Half precision floating points | Y | Y | http://www.openexr.com OpenEXR | 3.1 | Latest | EXR serialization support | Y | Y | http://www.openexr.com -TBB | 2020.2 | 2020.3 | Threading Building Blocks - template library for task parallelism | Y | Y | https://www.threadingbuildingblocks.org +TBB | 2020.3 | 2020.3 | Threading Building Blocks - template library for task parallelism | Y | Y | https://www.threadingbuildingblocks.org ZLIB | 1.2.7 | Latest | Compression library for disk serialization compression | Y | Y | https://www.zlib.net -Boost | 1.73 | 1.80 | Components: iostreams | Y | Y | https://www.boost.org -LLVM | 10.0.0 | 13.0.0* | Target-independent code generation | Y | Y | https://llvm.org/ -Bison | 3.0.0 | 3.7.0 | General-purpose parser generator | Y | Y | https://www.gnu.org/software/gcc -Flex | 2.6.0 | 2.6.4 | Fast lexical analyzer generator | Y | Y | https://github.com/westes/flex -Python | 3.9.1 | 3.10 | The python interpreter and libraries | Y | Y | https://www.python.org -nanobind | 2.0.0 | Latest | C++/python bindings | Y | Y | https://nanobind.readthedocs.io -NumPy | 1.20.0 | 1.23.0 | Scientific computing with Python | Y | Y | http://www.numpy.org +Boost | 1.80 | 1.82 | Components: iostreams | Y | Y | https://www.boost.org +LLVM | 13.0.0 | 15.0.0* | Target-independent code generation | Y | Y | https://llvm.org/ +Bison | 3.7.0 | 3.7.0 | General-purpose parser generator | Y | Y | https://www.gnu.org/software/gcc +Flex | 2.6.4 | 2.6.4 | Fast lexical analyzer generator | Y | Y | https://github.com/westes/flex +Python | 3.10 | 3.11 | The python interpreter and libraries | Y | Y | https://www.python.org +nanobind | 2.0.0 | 2.1.0 | C++/python bindings | Y | Y | https://nanobind.readthedocs.io +NumPy | 1.23.0 | 1.26.0 | Scientific computing with Python | Y | Y | http://www.numpy.org GoogleTest | 1.10 | Latest | A unit testing framework module for C++ | Y | Y | https://github.com/google/googletest CppUnit | 1.10 | Latest | A unit testing framework module for C++ | N | Y | https://freedesktop.org/wiki/Software/cppunit Blosc | 1.17.0* | 1.17.0 | Recommended dependency for improved disk compression | Y | Y | https://github.com/Blosc/c-blosc/releases @@ -145,7 +145,7 @@ apt-get install zlibc # zlib apt-get install libboost-iostreams-dev # Boost::iostream apt-get install libblosc-dev # Blosc # AX -apt-get install llvm-10-dev # LLVM +apt-get install llvm-15-dev # LLVM # Python apt-get install python-dev # Python apt-get install python-numpy # NumPy diff --git a/nanovdb/nanovdb/CMakeLists.txt b/nanovdb/nanovdb/CMakeLists.txt index 48d327d94e..4e0284ecbf 100644 --- a/nanovdb/nanovdb/CMakeLists.txt +++ b/nanovdb/nanovdb/CMakeLists.txt @@ -7,7 +7,7 @@ #]=======================================================================] -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.20) project(NanoVDB LANGUAGES C CXX) include(GNUInstallDirs) @@ -74,18 +74,16 @@ endif() #if(NANOVDB_BUILD_UNITTESTS OR NANOVDB_BUILD_BENCHMARK) if(NANOVDB_BUILD_UNITTESTS) - find_package(GTest REQUIRED) + find_package(GTest ${MINIMUM_GOOGLETEST_VERSION} REQUIRED) endif() if(NANOVDB_USE_CUDA) set(CMAKE_CUDA_STANDARD 17) set(CMAKE_CUDA_STANDARD_REQUIRED ON) - if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.18) - # Allow the user to provide CMAKE_CUDA_ARCHITECTURES - if(NOT DEFINED CMAKE_CUDA_ARCHITECTURES) - set(CMAKE_CUDA_ARCHITECTURES 75) - endif() + # Allow the user to provide CMAKE_CUDA_ARCHITECTURES + if(NOT DEFINED CMAKE_CUDA_ARCHITECTURES) + set(CMAKE_CUDA_ARCHITECTURES 75) endif() enable_language(CUDA) @@ -124,15 +122,15 @@ if(NANOVDB_USE_OPENVDB) endif() if(NANOVDB_USE_TBB AND NOT TARGET TBB::tbb) - find_package(TBB REQUIRED) + find_package(TBB ${MINIMUM_TBB_VERSION} REQUIRED) endif() if(NANOVDB_USE_BLOSC AND NOT TARGET Blosc::blosc) - find_package(Blosc REQUIRED) + find_package(Blosc ${MINIMUM_BLOSC_VERSION} REQUIRED) endif() if(NANOVDB_USE_ZLIB AND NOT TARGET ZLIB::ZLIB) - find_package(ZLIB REQUIRED) + find_package(ZLIB ${MINIMUM_ZLIB_VERSION} REQUIRED) endif() if(NANOVDB_USE_MAGICAVOXEL) @@ -322,16 +320,16 @@ if(TARGET Threads::Threads) target_link_libraries(nanovdb INTERFACE Threads::Threads) endif() -set(NANOVDB_INSTALL_INCLUDE_DIR ${CMAKE_INSTALL_INCLUDEDIR}/nanovdb) -set(NANOVDB_INSTALL_CUDA_DIR ${NANOVDB_INSTALL_INCLUDE_DIR}/cuda) -set(NANOVDB_INSTALL_IO_DIR ${NANOVDB_INSTALL_INCLUDE_DIR}/io) -set(NANOVDB_INSTALL_MATH_DIR ${NANOVDB_INSTALL_INCLUDE_DIR}/math) -set(NANOVDB_INSTALL_TOOLS_DIR ${NANOVDB_INSTALL_INCLUDE_DIR}/tools) +set(NANOVDB_INSTALL_ROOT_DIR ${NANOVDB_INSTALL_INCLUDEDIR}/nanovdb) +set(NANOVDB_INSTALL_CUDA_DIR ${NANOVDB_INSTALL_ROOT_DIR}/cuda) +set(NANOVDB_INSTALL_IO_DIR ${NANOVDB_INSTALL_ROOT_DIR}/io) +set(NANOVDB_INSTALL_MATH_DIR ${NANOVDB_INSTALL_ROOT_DIR}/math) +set(NANOVDB_INSTALL_TOOLS_DIR ${NANOVDB_INSTALL_ROOT_DIR}/tools) set(NANOVDB_INSTALL_TOOLS_CUDA_DIR ${NANOVDB_INSTALL_TOOLS_DIR}/cuda) -set(NANOVDB_INSTALL_UTIL_DIR ${NANOVDB_INSTALL_INCLUDE_DIR}/util) +set(NANOVDB_INSTALL_UTIL_DIR ${NANOVDB_INSTALL_ROOT_DIR}/util) set(NANOVDB_INSTALL_UTIL_CUDA_DIR ${NANOVDB_INSTALL_UTIL_DIR}/cuda) -install(FILES ${NANOVDB_INCLUDE_FILES} DESTINATION ${NANOVDB_INSTALL_INCLUDE_DIR}) +install(FILES ${NANOVDB_INCLUDE_FILES} DESTINATION ${NANOVDB_INSTALL_ROOT_DIR}) install(FILES ${NANOVDB_INCLUDE_CUDA_FILES} DESTINATION ${NANOVDB_INSTALL_CUDA_DIR}) install(FILES ${NANOVDB_INCLUDE_IO_FILES} DESTINATION ${NANOVDB_INSTALL_IO_DIR}) install(FILES ${NANOVDB_INCLUDE_MATH_FILES} DESTINATION ${NANOVDB_INSTALL_MATH_DIR}) diff --git a/nanovdb/nanovdb/cmd/CMakeLists.txt b/nanovdb/nanovdb/cmd/CMakeLists.txt index f8162d8a7b..985a7d1d70 100644 --- a/nanovdb/nanovdb/cmd/CMakeLists.txt +++ b/nanovdb/nanovdb/cmd/CMakeLists.txt @@ -7,7 +7,7 @@ #]=======================================================================] -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.20) project(NanoVDBExamples LANGUAGES CXX) include(GNUInstallDirs) @@ -32,17 +32,17 @@ if(NOT NANOVDB_USE_OPENVDB) else() add_executable(nanovdb_convert convert/nanovdb_convert.cc) target_link_libraries(nanovdb_convert PRIVATE nanovdb) - install(TARGETS nanovdb_convert RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + install(TARGETS nanovdb_convert RUNTIME DESTINATION ${NANOVDB_INSTALL_BINDIR}) endif() # ----------------------------------------------------------------------------- # nanovdb_print add_executable(nanovdb_print print/nanovdb_print.cc) target_link_libraries(nanovdb_print PRIVATE nanovdb) -install(TARGETS nanovdb_print RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +install(TARGETS nanovdb_print RUNTIME DESTINATION ${NANOVDB_INSTALL_BINDIR}) # ----------------------------------------------------------------------------- # nanovdb_validate add_executable(nanovdb_validate validate/nanovdb_validate.cc) target_link_libraries(nanovdb_validate PRIVATE nanovdb) -install(TARGETS nanovdb_validate RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +install(TARGETS nanovdb_validate RUNTIME DESTINATION ${NANOVDB_INSTALL_BINDIR}) diff --git a/nanovdb/nanovdb/cmd/updateFiles.py b/nanovdb/nanovdb/cmd/updateFiles.py index e4041c91f6..16fe6b8806 100644 --- a/nanovdb/nanovdb/cmd/updateFiles.py +++ b/nanovdb/nanovdb/cmd/updateFiles.py @@ -1,3 +1,5 @@ +# Copyright Contributors to the OpenVDB Project +# SPDX-License-Identifier: Apache-2.0 import argparse import os from pathlib import Path diff --git a/nanovdb/nanovdb/cmd/updateFiles.sh b/nanovdb/nanovdb/cmd/updateFiles.sh index 87613c3a44..b75a65656f 100755 --- a/nanovdb/nanovdb/cmd/updateFiles.sh +++ b/nanovdb/nanovdb/cmd/updateFiles.sh @@ -1,4 +1,6 @@ #!/bin/bash +# Copyright Contributors to the OpenVDB Project +# SPDX-License-Identifier: Apache-2.0 #Usage process all files in this directory or optionally specify a target directory # Define directory in which to find files diff --git a/nanovdb/nanovdb/docs/CMakeLists.txt b/nanovdb/nanovdb/docs/CMakeLists.txt index 6a131e68e7..81910cbe3b 100644 --- a/nanovdb/nanovdb/docs/CMakeLists.txt +++ b/nanovdb/nanovdb/docs/CMakeLists.txt @@ -1,3 +1,5 @@ +# Copyright Contributors to the OpenVDB Project +# SPDX-License-Identifier: Apache-2.0 # find_package(doxygen REQUIRED dot ) if(WIN32) set(DOXYGEN_EXECUTABLE "C:/Program Files/doxygen/bin/doxygen.exe") diff --git a/nanovdb/nanovdb/examples/CMakeLists.txt b/nanovdb/nanovdb/examples/CMakeLists.txt index 4ccececa9e..d06371cd95 100644 --- a/nanovdb/nanovdb/examples/CMakeLists.txt +++ b/nanovdb/nanovdb/examples/CMakeLists.txt @@ -7,7 +7,7 @@ #]=======================================================================] -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.20) project(NanoVDBExamples LANGUAGES CXX) include(GNUInstallDirs) @@ -78,7 +78,7 @@ function(NANOVDB_EXAMPLE) target_include_directories(${EXAMPLE_NAME} PUBLIC ex_util) target_link_libraries(${EXAMPLE_NAME} PRIVATE nanovdb) - install(TARGETS ${EXAMPLE_NAME} DESTINATION ${CMAKE_INSTALL_DOCDIR}/examples) + install(TARGETS ${EXAMPLE_NAME} DESTINATION ${NANOVDB_INSTALL_DOCDIR}/examples) endfunction() # ----------------------------------------------------------------------- diff --git a/nanovdb/nanovdb/examples/ex_read_nanovdb_sphere_accessor/read_nanovdb_sphere_accessor.cc b/nanovdb/nanovdb/examples/ex_read_nanovdb_sphere_accessor/read_nanovdb_sphere_accessor.cc index 91010b6cf7..dc763ddc8a 100644 --- a/nanovdb/nanovdb/examples/ex_read_nanovdb_sphere_accessor/read_nanovdb_sphere_accessor.cc +++ b/nanovdb/nanovdb/examples/ex_read_nanovdb_sphere_accessor/read_nanovdb_sphere_accessor.cc @@ -1,3 +1,5 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 #include // this is required to read (and write) NanoVDB files on the host /// @brief Read a NanoVDB grid from a file and print out multiple values. diff --git a/nanovdb/nanovdb/python/CMakeLists.txt b/nanovdb/nanovdb/python/CMakeLists.txt index 558c58a828..f13cf10116 100644 --- a/nanovdb/nanovdb/python/CMakeLists.txt +++ b/nanovdb/nanovdb/python/CMakeLists.txt @@ -1,3 +1,5 @@ +# Copyright Contributors to the OpenVDB Project +# SPDX-License-Identifier: Apache-2.0 option(NANOVDB_BUILD_PYTHON_UNITTESTS [=[ "Include the NanoVDB Python unit test. Requires a python interpreter]=] ${NANOVDB_BUILD_UNITTESTS}) @@ -27,7 +29,13 @@ target_include_directories(nanovdb_python PRIVATE ${CUDA_INCLUDE_DIRECTORY}) target_link_libraries(nanovdb_python PRIVATE nanovdb ${CUDA_LIBRARIES} ${NANOVDB_BLOSC} ${NANOVDB_ZLIB} ${NANOVDB_OPENVDB} ${NANOVDB_TBB}) target_compile_definitions(nanovdb_python PRIVATE ${NANOVDB_USE_CUDA_FLAG} ${NANOVDB_USE_BLOSC_FLAG} ${NANOVDB_USE_ZLIB_FLAG} ${NANOVDB_USE_OPENVDB_FLAG} ${NANOVDB_USE_TBB_FLAG}) set_target_properties(nanovdb_python PROPERTIES OUTPUT_NAME "nanovdb") -install(TARGETS nanovdb_python DESTINATION ${VDB_PYTHON_INSTALL_DIRECTORY}) +if(SKBUILD) + set_target_properties(nanovdb_python PROPERTIES INSTALL_RPATH "$ORIGIN/../../openvdb/lib") + install(TARGETS nanovdb_python DESTINATION ${NANOVDB_INSTALL_LIBDIR}) + install(FILES __init__.py DESTINATION nanovdb) +else() + install(TARGETS nanovdb_python DESTINATION ${VDB_PYTHON_INSTALL_DIRECTORY}) +endif() # pytest if(NANOVDB_BUILD_PYTHON_UNITTESTS) diff --git a/nanovdb/nanovdb/python/NanoVDBModule.cc b/nanovdb/nanovdb/python/NanoVDBModule.cc index 583b5464f9..89af0ec095 100644 --- a/nanovdb/nanovdb/python/NanoVDBModule.cc +++ b/nanovdb/nanovdb/python/NanoVDBModule.cc @@ -1,3 +1,5 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 #include #include #include diff --git a/nanovdb/nanovdb/python/PyCreateNanoGrid.cc b/nanovdb/nanovdb/python/PyCreateNanoGrid.cc index c776865306..eed931766c 100644 --- a/nanovdb/nanovdb/python/PyCreateNanoGrid.cc +++ b/nanovdb/nanovdb/python/PyCreateNanoGrid.cc @@ -1,3 +1,5 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 #include "PyCreateNanoGrid.h" #include diff --git a/nanovdb/nanovdb/python/PyCreateNanoGrid.h b/nanovdb/nanovdb/python/PyCreateNanoGrid.h index 863ca1b396..3402ca67b8 100644 --- a/nanovdb/nanovdb/python/PyCreateNanoGrid.h +++ b/nanovdb/nanovdb/python/PyCreateNanoGrid.h @@ -1,3 +1,5 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 #ifndef NANOVDB_PYCREATENANOGRID_HAS_BEEN_INCLUDED #define NANOVDB_PYCREATENANOGRID_HAS_BEEN_INCLUDED diff --git a/nanovdb/nanovdb/python/PyGridChecksum.cc b/nanovdb/nanovdb/python/PyGridChecksum.cc index ba6e709b10..281fd05c0b 100644 --- a/nanovdb/nanovdb/python/PyGridChecksum.cc +++ b/nanovdb/nanovdb/python/PyGridChecksum.cc @@ -1,3 +1,5 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 #include "PyGridChecksum.h" #include diff --git a/nanovdb/nanovdb/python/PyGridChecksum.h b/nanovdb/nanovdb/python/PyGridChecksum.h index f8c2048b83..dd988f871c 100644 --- a/nanovdb/nanovdb/python/PyGridChecksum.h +++ b/nanovdb/nanovdb/python/PyGridChecksum.h @@ -1,3 +1,5 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 #ifndef NANOVDB_PYGRIDCHECKSUM_HAS_BEEN_INCLUDED #define NANOVDB_PYGRIDCHECKSUM_HAS_BEEN_INCLUDED diff --git a/nanovdb/nanovdb/python/PyGridHandle.cc b/nanovdb/nanovdb/python/PyGridHandle.cc index 4f4e8ff72d..efd4253337 100644 --- a/nanovdb/nanovdb/python/PyGridHandle.cc +++ b/nanovdb/nanovdb/python/PyGridHandle.cc @@ -1,3 +1,5 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 #include "PyGridHandle.h" #include diff --git a/nanovdb/nanovdb/python/PyGridHandle.h b/nanovdb/nanovdb/python/PyGridHandle.h index a15c588c83..190c6ab15f 100644 --- a/nanovdb/nanovdb/python/PyGridHandle.h +++ b/nanovdb/nanovdb/python/PyGridHandle.h @@ -1,3 +1,5 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 #ifndef NANOVDB_PYGRIDHANDLE_HAS_BEEN_INCLUDED #define NANOVDB_PYGRIDHANDLE_HAS_BEEN_INCLUDED diff --git a/nanovdb/nanovdb/python/PyGridStats.cc b/nanovdb/nanovdb/python/PyGridStats.cc index 7f8598a7ac..fbd8caec15 100644 --- a/nanovdb/nanovdb/python/PyGridStats.cc +++ b/nanovdb/nanovdb/python/PyGridStats.cc @@ -1,3 +1,5 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 #include "PyGridStats.h" #include diff --git a/nanovdb/nanovdb/python/PyGridStats.h b/nanovdb/nanovdb/python/PyGridStats.h index c53af4567d..90254bc23b 100644 --- a/nanovdb/nanovdb/python/PyGridStats.h +++ b/nanovdb/nanovdb/python/PyGridStats.h @@ -1,3 +1,5 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 #ifndef NANOVDB_PYGRIDSTATS_HAS_BEEN_INCLUDED #define NANOVDB_PYGRIDSTATS_HAS_BEEN_INCLUDED diff --git a/nanovdb/nanovdb/python/PyGridValidator.cc b/nanovdb/nanovdb/python/PyGridValidator.cc index 278588b466..8e4f20df64 100644 --- a/nanovdb/nanovdb/python/PyGridValidator.cc +++ b/nanovdb/nanovdb/python/PyGridValidator.cc @@ -1,3 +1,5 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 #include "PyGridValidator.h" #include diff --git a/nanovdb/nanovdb/python/PyGridValidator.h b/nanovdb/nanovdb/python/PyGridValidator.h index 6725f88b4d..659dede241 100644 --- a/nanovdb/nanovdb/python/PyGridValidator.h +++ b/nanovdb/nanovdb/python/PyGridValidator.h @@ -1,3 +1,5 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 #ifndef NANOVDB_PYGRIDVALIDATOR_HAS_BEEN_INCLUDED #define NANOVDB_PYGRIDVALIDATOR_HAS_BEEN_INCLUDED diff --git a/nanovdb/nanovdb/python/PyHostBuffer.cc b/nanovdb/nanovdb/python/PyHostBuffer.cc index 4af48c2040..e8fdfb3946 100644 --- a/nanovdb/nanovdb/python/PyHostBuffer.cc +++ b/nanovdb/nanovdb/python/PyHostBuffer.cc @@ -1,3 +1,5 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 #include "PyHostBuffer.h" #include diff --git a/nanovdb/nanovdb/python/PyHostBuffer.h b/nanovdb/nanovdb/python/PyHostBuffer.h index f1bf704edc..29b5a917ce 100644 --- a/nanovdb/nanovdb/python/PyHostBuffer.h +++ b/nanovdb/nanovdb/python/PyHostBuffer.h @@ -1,3 +1,5 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 #ifndef NANOVDB_PYHOSTBUFFER_HAS_BEEN_INCLUDED #define NANOVDB_PYHOSTBUFFER_HAS_BEEN_INCLUDED diff --git a/nanovdb/nanovdb/python/PyIO.cc b/nanovdb/nanovdb/python/PyIO.cc index d77cb40f86..b46a52ba74 100644 --- a/nanovdb/nanovdb/python/PyIO.cc +++ b/nanovdb/nanovdb/python/PyIO.cc @@ -1,3 +1,5 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 #include "PyIO.h" #include diff --git a/nanovdb/nanovdb/python/PyIO.h b/nanovdb/nanovdb/python/PyIO.h index 907ef0f7f9..de857a5148 100644 --- a/nanovdb/nanovdb/python/PyIO.h +++ b/nanovdb/nanovdb/python/PyIO.h @@ -1,3 +1,5 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 #ifndef NANOVDB_PYIO_HAS_BEEN_INCLUDED #define NANOVDB_PYIO_HAS_BEEN_INCLUDED diff --git a/nanovdb/nanovdb/python/PyMath.cc b/nanovdb/nanovdb/python/PyMath.cc index 06a4ad5606..337865ed8c 100644 --- a/nanovdb/nanovdb/python/PyMath.cc +++ b/nanovdb/nanovdb/python/PyMath.cc @@ -1,3 +1,5 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 #include "PyMath.h" #include diff --git a/nanovdb/nanovdb/python/PyMath.h b/nanovdb/nanovdb/python/PyMath.h index e9a2cde791..13c288e110 100644 --- a/nanovdb/nanovdb/python/PyMath.h +++ b/nanovdb/nanovdb/python/PyMath.h @@ -1,3 +1,5 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 #ifndef NANOVDB_PYMATH_HAS_BEEN_INCLUDED #define NANOVDB_PYMATH_HAS_BEEN_INCLUDED diff --git a/nanovdb/nanovdb/python/PyNanoToOpenVDB.cc b/nanovdb/nanovdb/python/PyNanoToOpenVDB.cc index 8225cd78ca..49dc464a05 100644 --- a/nanovdb/nanovdb/python/PyNanoToOpenVDB.cc +++ b/nanovdb/nanovdb/python/PyNanoToOpenVDB.cc @@ -1,3 +1,5 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 #include "PyNanoToOpenVDB.h" #include diff --git a/nanovdb/nanovdb/python/PyNanoToOpenVDB.h b/nanovdb/nanovdb/python/PyNanoToOpenVDB.h index ac60fdbf96..54c38501e5 100644 --- a/nanovdb/nanovdb/python/PyNanoToOpenVDB.h +++ b/nanovdb/nanovdb/python/PyNanoToOpenVDB.h @@ -1,3 +1,5 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 #ifndef NANOVDB_PYNANOTOOPENVDB_HAS_BEEN_INCLUDED #define NANOVDB_PYNANOTOOPENVDB_HAS_BEEN_INCLUDED diff --git a/nanovdb/nanovdb/python/PyPrimitives.cc b/nanovdb/nanovdb/python/PyPrimitives.cc index 3b7f11c0d9..29053d4e68 100644 --- a/nanovdb/nanovdb/python/PyPrimitives.cc +++ b/nanovdb/nanovdb/python/PyPrimitives.cc @@ -1,3 +1,5 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 #include "PyPrimitives.h" #include diff --git a/nanovdb/nanovdb/python/PyPrimitives.h b/nanovdb/nanovdb/python/PyPrimitives.h index 8be1e0208c..a930ee5d8d 100644 --- a/nanovdb/nanovdb/python/PyPrimitives.h +++ b/nanovdb/nanovdb/python/PyPrimitives.h @@ -1,3 +1,5 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 #ifndef NANOVDB_PYPRIMITIVES_HAS_BEEN_INCLUDED #define NANOVDB_PYPRIMITIVES_HAS_BEEN_INCLUDED diff --git a/nanovdb/nanovdb/python/PySampleFromVoxels.cc b/nanovdb/nanovdb/python/PySampleFromVoxels.cc index 2a3b7e5b6e..82dc9a26d1 100644 --- a/nanovdb/nanovdb/python/PySampleFromVoxels.cc +++ b/nanovdb/nanovdb/python/PySampleFromVoxels.cc @@ -1,3 +1,5 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 #include "PySampleFromVoxels.h" #include diff --git a/nanovdb/nanovdb/python/PySampleFromVoxels.h b/nanovdb/nanovdb/python/PySampleFromVoxels.h index 140db2f3be..6c5733f714 100644 --- a/nanovdb/nanovdb/python/PySampleFromVoxels.h +++ b/nanovdb/nanovdb/python/PySampleFromVoxels.h @@ -1,3 +1,5 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 #ifndef NANOVDB_PYSAMPLEFROMVOXELS_HAS_BEEN_INCLUDED #define NANOVDB_PYSAMPLEFROMVOXELS_HAS_BEEN_INCLUDED diff --git a/nanovdb/nanovdb/python/PyTools.cc b/nanovdb/nanovdb/python/PyTools.cc index de74380987..4df996d557 100644 --- a/nanovdb/nanovdb/python/PyTools.cc +++ b/nanovdb/nanovdb/python/PyTools.cc @@ -1,3 +1,5 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 #include "PyTools.h" #include diff --git a/nanovdb/nanovdb/python/PyTools.h b/nanovdb/nanovdb/python/PyTools.h index be6e49ff53..bfb9d42ef8 100644 --- a/nanovdb/nanovdb/python/PyTools.h +++ b/nanovdb/nanovdb/python/PyTools.h @@ -1,3 +1,5 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 #ifndef NANOVDB_PYTOOLS_HAS_BEEN_INCLUDED #define NANOVDB_PYTOOLS_HAS_BEEN_INCLUDED diff --git a/nanovdb/nanovdb/python/__init__.py b/nanovdb/nanovdb/python/__init__.py new file mode 100644 index 0000000000..4f955e4a02 --- /dev/null +++ b/nanovdb/nanovdb/python/__init__.py @@ -0,0 +1,9 @@ +# Copyright Contributors to the OpenVDB Project +# SPDX-License-Identifier: Apache-2.0 +import sys +if sys.platform == "win32": + import os + openvdb_dll_directory = os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir, 'openvdb', 'lib') + os.add_dll_directory(directory) + +from .lib.nanovdb import * diff --git a/nanovdb/nanovdb/python/cuda/PyDeviceBuffer.cc b/nanovdb/nanovdb/python/cuda/PyDeviceBuffer.cc index 58a6d984f3..523e4b5834 100644 --- a/nanovdb/nanovdb/python/cuda/PyDeviceBuffer.cc +++ b/nanovdb/nanovdb/python/cuda/PyDeviceBuffer.cc @@ -1,3 +1,5 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 #ifdef NANOVDB_USE_CUDA #include "PyDeviceBuffer.h" diff --git a/nanovdb/nanovdb/python/cuda/PyDeviceBuffer.h b/nanovdb/nanovdb/python/cuda/PyDeviceBuffer.h index c65239cb69..87a081f638 100644 --- a/nanovdb/nanovdb/python/cuda/PyDeviceBuffer.h +++ b/nanovdb/nanovdb/python/cuda/PyDeviceBuffer.h @@ -1,3 +1,5 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 #ifndef NANOVDB_CUDA_PYDEVICEBUFFER_HAS_BEEN_INCLUDED #define NANOVDB_CUDA_PYDEVICEBUFFER_HAS_BEEN_INCLUDED diff --git a/nanovdb/nanovdb/python/cuda/PyDeviceGridHandle.cu b/nanovdb/nanovdb/python/cuda/PyDeviceGridHandle.cu index a8dc924d7f..983caebd05 100644 --- a/nanovdb/nanovdb/python/cuda/PyDeviceGridHandle.cu +++ b/nanovdb/nanovdb/python/cuda/PyDeviceGridHandle.cu @@ -1,3 +1,5 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 #ifdef NANOVDB_USE_CUDA #include "../PyGridHandle.h" diff --git a/nanovdb/nanovdb/python/cuda/PyPointsToGrid.cu b/nanovdb/nanovdb/python/cuda/PyPointsToGrid.cu index 82a5826fc4..933f5570b6 100644 --- a/nanovdb/nanovdb/python/cuda/PyPointsToGrid.cu +++ b/nanovdb/nanovdb/python/cuda/PyPointsToGrid.cu @@ -1,3 +1,5 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 #include "PyPointsToGrid.h" #include diff --git a/nanovdb/nanovdb/python/cuda/PyPointsToGrid.h b/nanovdb/nanovdb/python/cuda/PyPointsToGrid.h index 4bb530ac22..4af07f5657 100644 --- a/nanovdb/nanovdb/python/cuda/PyPointsToGrid.h +++ b/nanovdb/nanovdb/python/cuda/PyPointsToGrid.h @@ -1,3 +1,5 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 #ifndef NANOVDB_CUDA_PYPOINTSTOGRID_HAS_BEEN_INCLUDED #define NANOVDB_CUDA_PYPOINTSTOGRID_HAS_BEEN_INCLUDED diff --git a/nanovdb/nanovdb/python/cuda/PySampleFromVoxels.cu b/nanovdb/nanovdb/python/cuda/PySampleFromVoxels.cu index 8bab06e51e..aaebf66772 100644 --- a/nanovdb/nanovdb/python/cuda/PySampleFromVoxels.cu +++ b/nanovdb/nanovdb/python/cuda/PySampleFromVoxels.cu @@ -1,3 +1,5 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 #include "PySampleFromVoxels.h" #include diff --git a/nanovdb/nanovdb/python/cuda/PySampleFromVoxels.h b/nanovdb/nanovdb/python/cuda/PySampleFromVoxels.h index c23a97e849..22cc4aec4d 100644 --- a/nanovdb/nanovdb/python/cuda/PySampleFromVoxels.h +++ b/nanovdb/nanovdb/python/cuda/PySampleFromVoxels.h @@ -1,3 +1,5 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 #ifndef NANOVDB_CUDA_PYSAMPLEFROMVOXELS_HAS_BEEN_INCLUDED #define NANOVDB_CUDA_PYSAMPLEFROMVOXELS_HAS_BEEN_INCLUDED diff --git a/nanovdb/nanovdb/python/cuda/PySignedFloodFill.cu b/nanovdb/nanovdb/python/cuda/PySignedFloodFill.cu index 2b68ca38df..ddbd5c67cb 100644 --- a/nanovdb/nanovdb/python/cuda/PySignedFloodFill.cu +++ b/nanovdb/nanovdb/python/cuda/PySignedFloodFill.cu @@ -1,3 +1,5 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 #include "PySignedFloodFill.h" #include diff --git a/nanovdb/nanovdb/python/cuda/PySignedFloodFill.h b/nanovdb/nanovdb/python/cuda/PySignedFloodFill.h index 7ae16defe7..d5f19bcbe2 100644 --- a/nanovdb/nanovdb/python/cuda/PySignedFloodFill.h +++ b/nanovdb/nanovdb/python/cuda/PySignedFloodFill.h @@ -1,3 +1,5 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 #ifndef NANOVDB_CUDA_PYSIGNEDFLOODFILL_HAS_BEEN_INCLUDED #define NANOVDB_CUDA_PYSIGNEDFLOODFILL_HAS_BEEN_INCLUDED diff --git a/nanovdb/nanovdb/python/test/TestNanoVDB.py b/nanovdb/nanovdb/python/test/TestNanoVDB.py index fca793295e..03660c6342 100644 --- a/nanovdb/nanovdb/python/test/TestNanoVDB.py +++ b/nanovdb/nanovdb/python/test/TestNanoVDB.py @@ -1,4 +1,6 @@ -# /usr/bin/env python +#!/usr/bin/env python +# Copyright Contributors to the OpenVDB Project +# SPDX-License-Identifier: Apache-2.0 import nanovdb import unittest diff --git a/nanovdb/nanovdb/tools/GridBuilder.h b/nanovdb/nanovdb/tools/GridBuilder.h index 30385661d0..428215ba65 100644 --- a/nanovdb/nanovdb/tools/GridBuilder.h +++ b/nanovdb/nanovdb/tools/GridBuilder.h @@ -1158,7 +1158,7 @@ struct LeafNode ValueIterator& operator=(const ValueIterator&) = default; ValueType operator*() const { NANOVDB_ASSERT(*this); return mParent->mValues[mPos];} Coord getCoord() const { NANOVDB_ASSERT(*this); return mParent->offsetToGlobalCoord(mPos);} - bool isActive() const { NANOVDB_ASSERT(*this); return mParent->isActive(mPos);} + bool isActive() const { NANOVDB_ASSERT(*this); return mParent->mValueMask.isOn(mPos);} operator bool() const {return mPos < SIZE;} ValueIterator& operator++() {++mPos; return *this;} ValueIterator operator++(int) { diff --git a/nanovdb/nanovdb/unittest/CMakeLists.txt b/nanovdb/nanovdb/unittest/CMakeLists.txt index de9f355f0b..ac63a610d4 100644 --- a/nanovdb/nanovdb/unittest/CMakeLists.txt +++ b/nanovdb/nanovdb/unittest/CMakeLists.txt @@ -7,7 +7,7 @@ #]=======================================================================] -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.20) project(NanoVDBTests LANGUAGES CXX) include(GNUInstallDirs) diff --git a/nanovdb/nanovdb/unittest/TestNanoVDB.cc b/nanovdb/nanovdb/unittest/TestNanoVDB.cc index 7eacd07915..33e120c063 100644 --- a/nanovdb/nanovdb/unittest/TestNanoVDB.cc +++ b/nanovdb/nanovdb/unittest/TestNanoVDB.cc @@ -4,6 +4,7 @@ // Uncomment to temporarily disable testing of PNanoVDB //#define DISABLE_PNANOVDB +#include #include #include #include // for std::stringstream diff --git a/openvdb/openvdb/CMakeLists.txt b/openvdb/openvdb/CMakeLists.txt index a0d7868192..f45257e413 100644 --- a/openvdb/openvdb/CMakeLists.txt +++ b/openvdb/openvdb/CMakeLists.txt @@ -7,7 +7,7 @@ #]=======================================================================] -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.20) project(OpenVDBCore LANGUAGES CXX) include(GNUInstallDirs) @@ -405,6 +405,7 @@ set(OPENVDB_LIBRARY_MATH_INCLUDE_FILES math/DDA.h math/FiniteDifference.h math/Half.h + math/HalfDecl.h math/LegacyFrustum.h math/Maps.h math/Mat.h @@ -720,27 +721,27 @@ endif() if(OPENVDB_CORE_STATIC) install(TARGETS openvdb_static - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${OPENVDB_INSTALL_BINDIR} + LIBRARY DESTINATION ${OPENVDB_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${OPENVDB_INSTALL_LIBDIR} ) endif() if(OPENVDB_CORE_SHARED) install(TARGETS openvdb_shared - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${OPENVDB_INSTALL_BINDIR} + LIBRARY DESTINATION ${OPENVDB_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${OPENVDB_INSTALL_LIBDIR} ) endif() -install(FILES ${OPENVDB_LIBRARY_INCLUDE_FILES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/openvdb) -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/openvdb/version.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/openvdb) -install(FILES ${OPENVDB_LIBRARY_IO_INCLUDE_FILES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/openvdb/io) -install(FILES ${OPENVDB_LIBRARY_MATH_INCLUDE_FILES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/openvdb/math) -install(FILES ${OPENVDB_LIBRARY_POINTS_INCLUDE_FILES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/openvdb/points) -install(FILES ${OPENVDB_LIBRARY_POINTS_IMPL_INCLUDE_FILES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/openvdb/points/impl) -install(FILES ${OPENVDB_LIBRARY_TOOLS_INCLUDE_FILES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/openvdb/tools) -install(FILES ${OPENVDB_LIBRARY_TREE_INCLUDE_FILES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/openvdb/tree) -install(FILES ${OPENVDB_LIBRARY_UTIL_INCLUDE_FILES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/openvdb/util) -install(FILES ${OPENVDB_LIBRARY_THREAD_INCLUDE_FILES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/openvdb/thread) +install(FILES ${OPENVDB_LIBRARY_INCLUDE_FILES} DESTINATION ${OPENVDB_INSTALL_INCLUDEDIR}/openvdb) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/openvdb/version.h DESTINATION ${OPENVDB_INSTALL_INCLUDEDIR}/openvdb) +install(FILES ${OPENVDB_LIBRARY_IO_INCLUDE_FILES} DESTINATION ${OPENVDB_INSTALL_INCLUDEDIR}/openvdb/io) +install(FILES ${OPENVDB_LIBRARY_MATH_INCLUDE_FILES} DESTINATION ${OPENVDB_INSTALL_INCLUDEDIR}/openvdb/math) +install(FILES ${OPENVDB_LIBRARY_POINTS_INCLUDE_FILES} DESTINATION ${OPENVDB_INSTALL_INCLUDEDIR}/openvdb/points) +install(FILES ${OPENVDB_LIBRARY_POINTS_IMPL_INCLUDE_FILES} DESTINATION ${OPENVDB_INSTALL_INCLUDEDIR}/openvdb/points/impl) +install(FILES ${OPENVDB_LIBRARY_TOOLS_INCLUDE_FILES} DESTINATION ${OPENVDB_INSTALL_INCLUDEDIR}/openvdb/tools) +install(FILES ${OPENVDB_LIBRARY_TREE_INCLUDE_FILES} DESTINATION ${OPENVDB_INSTALL_INCLUDEDIR}/openvdb/tree) +install(FILES ${OPENVDB_LIBRARY_UTIL_INCLUDE_FILES} DESTINATION ${OPENVDB_INSTALL_INCLUDEDIR}/openvdb/util) +install(FILES ${OPENVDB_LIBRARY_THREAD_INCLUDE_FILES} DESTINATION ${OPENVDB_INSTALL_INCLUDEDIR}/openvdb/thread) diff --git a/openvdb/openvdb/Grid.h b/openvdb/openvdb/Grid.h index cb2d2eba74..4bb72719bc 100644 --- a/openvdb/openvdb/Grid.h +++ b/openvdb/openvdb/Grid.h @@ -577,6 +577,7 @@ class Grid: public GridBase using TreePtrType = typename _TreeType::Ptr; using ConstTreePtrType = typename _TreeType::ConstPtr; using ValueType = typename _TreeType::ValueType; + using ComputeType = typename _TreeType::ComputeType; using BuildType = typename _TreeType::BuildType; using ValueOnIter = typename _TreeType::ValueOnIter; @@ -1781,10 +1782,11 @@ template typename GridType::Ptr createLevelSet(Real voxelSize, Real halfWidth) { - using ValueType = typename GridType::ValueType; + using ValueType = typename GridType::ValueType; + using ComputeType = typename GridType::ComputeType; // GridType::ValueType is required to be a floating-point scalar. - static_assert(std::is_floating_point::value, + static_assert(std::is_floating_point::value, "level-set grids must be floating-point-valued"); typename GridType::Ptr grid = GridType::create( diff --git a/openvdb/openvdb/Metadata.h b/openvdb/openvdb/Metadata.h index d17082e150..6106944243 100644 --- a/openvdb/openvdb/Metadata.h +++ b/openvdb/openvdb/Metadata.h @@ -359,6 +359,7 @@ operator<<(std::ostream& ostr, const Metadata& metadata) using BoolMetadata = TypedMetadata; using DoubleMetadata = TypedMetadata; using FloatMetadata = TypedMetadata; +using HalfMetadata = TypedMetadata; using Int32Metadata = TypedMetadata; using Int64Metadata = TypedMetadata; using StringMetadata = TypedMetadata; diff --git a/openvdb/openvdb/Platform.h b/openvdb/openvdb/Platform.h index bb99d9bf10..eebd4620ad 100644 --- a/openvdb/openvdb/Platform.h +++ b/openvdb/openvdb/Platform.h @@ -211,16 +211,10 @@ #define OPENVDB_NO_TYPE_CONVERSION_WARNING_END #elif defined __GNUC__ // -Wfloat-conversion was only introduced in GCC 4.9 - #if OPENVDB_CHECK_GCC(4, 9) - #define OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN \ - _Pragma("GCC diagnostic push") \ - _Pragma("GCC diagnostic ignored \"-Wconversion\"") \ - _Pragma("GCC diagnostic ignored \"-Wfloat-conversion\"") - #else - #define OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN \ - _Pragma("GCC diagnostic push") \ - _Pragma("GCC diagnostic ignored \"-Wconversion\"") - #endif + #define OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wconversion\"") \ + _Pragma("GCC diagnostic ignored \"-Wfloat-conversion\"") #define OPENVDB_NO_TYPE_CONVERSION_WARNING_END \ _Pragma("GCC diagnostic pop") #else diff --git a/openvdb/openvdb/Types.h b/openvdb/openvdb/Types.h index 9d05033c7d..26b6b5d74a 100644 --- a/openvdb/openvdb/Types.h +++ b/openvdb/openvdb/Types.h @@ -8,27 +8,7 @@ #include "Platform.h" #include "TypeList.h" // backwards compat -#ifdef OPENVDB_USE_IMATH_HALF -#ifdef OPENVDB_IMATH_VERSION -#include -#else -#include -#endif -namespace openvdb { -OPENVDB_USE_VERSION_NAMESPACE -namespace OPENVDB_VERSION_NAME { -namespace math { -using half = half; -}}} -#else -#include -namespace openvdb { -OPENVDB_USE_VERSION_NAMESPACE -namespace OPENVDB_VERSION_NAME { -namespace math { -using half = internal::half; -}}} -#endif +#include #include #include @@ -58,12 +38,13 @@ using Int64 = int64_t; using Int = Int32; using Byte = unsigned char; using Real = double; +using Half = math::half; // Two-dimensional vector types using Vec2R = math::Vec2; using Vec2I = math::Vec2; using Vec2f = math::Vec2; -using Vec2H = math::Vec2; +using Vec2H = math::Vec2; using math::Vec2i; using math::Vec2s; using math::Vec2d; @@ -72,7 +53,7 @@ using math::Vec2d; using Vec3R = math::Vec3; using Vec3I = math::Vec3; using Vec3f = math::Vec3; -using Vec3H = math::Vec3; +using Vec3H = math::Vec3; using Vec3U8 = math::Vec3; using Vec3U16 = math::Vec3; using math::Vec3i; @@ -87,7 +68,7 @@ using BBoxd = math::BBox; using Vec4R = math::Vec4; using Vec4I = math::Vec4; using Vec4f = math::Vec4; -using Vec4H = math::Vec4; +using Vec4H = math::Vec4; using math::Vec4i; using math::Vec4s; using math::Vec4d; @@ -446,6 +427,22 @@ template struct CopyConstness +struct ValueToComputeMap +{ + using Type = T; +}; + +template<> +struct ValueToComputeMap +{ + using Type = float; +}; + + //////////////////////////////////////// @@ -691,11 +688,11 @@ class PartialCreate {}; // For half compilation namespace math { template<> -inline auto cwiseAdd(const math::Vec3& v, const float s) +inline auto cwiseAdd(const Vec3H& v, const float s) { - math::Vec3 out; - const math::half* ip = v.asPointer(); - math::half* op = out.asPointer(); + Vec3H out; + const Half* ip = v.asPointer(); + Half* op = out.asPointer(); for (unsigned i = 0; i < 3; ++i, ++op, ++ip) { OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN *op = *ip + s; diff --git a/openvdb/openvdb/io/Archive.cc b/openvdb/openvdb/io/Archive.cc index 39e79c86ca..8f99565f20 100644 --- a/openvdb/openvdb/io/Archive.cc +++ b/openvdb/openvdb/io/Archive.cc @@ -357,10 +357,10 @@ struct PopulateDelayedLoadMetadataOp using MaskT = typename LeafT::NodeMaskType; const TreeT& tree = grid.constTree(); - const Index32 leafCount = tree.leafCount(); + const Index64 leafCount = tree.leafCount(); // early exit if not leaf nodes - if (leafCount == Index32(0)) return; + if (leafCount == Index64(0)) return; metadata.resizeMask(leafCount); diff --git a/openvdb/openvdb/math/HalfDecl.h b/openvdb/openvdb/math/HalfDecl.h new file mode 100644 index 0000000000..f9d654557f --- /dev/null +++ b/openvdb/openvdb/math/HalfDecl.h @@ -0,0 +1,30 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_HALFDECL_HAS_BEEN_INCLUDED +#define OPENVDB_HALFDECL_HAS_BEEN_INCLUDED + +#ifdef OPENVDB_USE_IMATH_HALF +#ifdef OPENVDB_IMATH_VERSION +#include +#else +#include +#endif +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { +using half = half; +}}} +#else +#include +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { +using half = internal::half; +}}} +#endif + + +#endif // OPENVDB_HALFDECL_HAS_BEEN_INCLUDED diff --git a/openvdb/openvdb/math/Math.h b/openvdb/openvdb/math/Math.h index 4d5e3423a9..578ebc2c3b 100644 --- a/openvdb/openvdb/math/Math.h +++ b/openvdb/openvdb/math/Math.h @@ -10,6 +10,7 @@ #include #include +#include #include #include // for std::max() #include @@ -145,16 +146,18 @@ template<> inline std::string negative(const std::string& val) { return val; } //@{ /// Tolerance for floating-point comparison -template struct Tolerance { static T value() { return zeroVal(); } }; -template<> struct Tolerance { static float value() { return 1e-8f; } }; -template<> struct Tolerance { static double value() { return 1e-15; } }; +template struct Tolerance { static T value() { return zeroVal(); } }; +template<> struct Tolerance { static math::half value() { return static_cast(0.00097656f); } }; +template<> struct Tolerance { static float value() { return 1e-8f; } }; +template<> struct Tolerance { static double value() { return 1e-15; } }; //@} //@{ /// Delta for small floating-point offsets -template struct Delta { static T value() { return zeroVal(); } }; -template<> struct Delta { static float value() { return 1e-5f; } }; -template<> struct Delta { static double value() { return 1e-9; } }; +template struct Delta { static T value() { return zeroVal(); } }; +template<> struct Delta { static math::half value() { return static_cast(0.00390625f); } }; +template<> struct Delta { static float value() { return 1e-5f; } }; +template<> struct Delta { static double value() { return 1e-9; } }; //@} @@ -312,6 +315,11 @@ inline int64_t Abs(int64_t i) "std::abs(int64) broken"); return std::abs(i); } + +// isNegative and operator-() are bitwise ops +// all half types in HalfDecl.h _could_ define abs() as half(FromBits, bits() & 0x7fff) +inline math::half Abs(math::half x) { return x.isNegative() ? -x : x; } + inline float Abs(float x) { return std::fabs(x); } inline double Abs(double x) { return std::fabs(x); } inline long double Abs(long double x) { return std::fabs(x); } @@ -361,6 +369,10 @@ isApproxZero(const Type& x, const Type& tolerance) } +/// Return @c true if @a x is less than zero. +inline bool +isNegative(math::half& x) { return x.isNegative(); } + /// Return @c true if @a x is less than zero. template inline bool @@ -374,6 +386,10 @@ template<> inline bool isNegative(const bool&) { return false; } inline bool isFinite(const float x) { return std::isfinite(x); } +/// Return @c true if @a x is finite. +inline bool +isFinite(const math::half x) { return x.isFinite(); } + /// Return @c true if @a x is finite. template::value, int>::type = 0> inline bool @@ -384,6 +400,10 @@ isFinite(const Type& x) { return std::isfinite(static_cast(x)); } inline bool isInfinite(const float x) { return std::isinf(x); } +/// Return @c true if @a x is an infinity value (either positive infinity or negative infinity). +inline bool +isInfinite(const math::half x) { return x.isInfinity(); } + /// Return @c true if @a x is an infinity value (either positive infinity or negative infinity). template::value, int>::type = 0> inline bool @@ -394,6 +414,10 @@ isInfinite(const Type& x) { return std::isinf(static_cast(x)); } inline bool isNan(const float x) { return std::isnan(x); } +/// Return @c true if @a x is a NaN (Not-A-Number) value. +inline bool +isNan(const math::half x) { return x.isNan(); } + /// Return @c true if @a x is a NaN (Not-A-Number) value. template::value, int>::type = 0> inline bool @@ -569,6 +593,15 @@ Pow(Type x, int n) return ans; } +//@{ +/// Return @a be. +inline math::half +Pow(math::half b, math::half e) +{ + OPENVDB_ASSERT( b >= 0.0f && "Pow(half,half): base is negative" ); + return math::half(powf(float(b),float(e))); +} + //@{ /// Return @a be. inline float @@ -589,12 +622,29 @@ Pow(double b, double e) // ==========> Max <================== +namespace internal { + +inline const math::half& +max_impl(const math::half& a, const math::half& b) +{ + return a > b ? a : b; +} + +template +inline const Type& +max_impl(const Type& a, const Type& b) +{ + return std::max(a,b); +} + +} // namespace internal + /// Return the maximum of two values template inline const Type& Max(const Type& a, const Type& b) { - return std::max(a,b); + return internal::max_impl(a,b); } /// Return the maximum of three values @@ -602,7 +652,7 @@ template inline const Type& Max(const Type& a, const Type& b, const Type& c) { - return std::max(std::max(a,b), c); + return internal::max_impl(internal::max_impl(a,b), c); } /// Return the maximum of four values @@ -610,7 +660,7 @@ template inline const Type& Max(const Type& a, const Type& b, const Type& c, const Type& d) { - return std::max(std::max(a,b), std::max(c,d)); + return internal::max_impl(internal::max_impl(a,b), internal::max_impl(c,d)); } /// Return the maximum of five values @@ -618,7 +668,7 @@ template inline const Type& Max(const Type& a, const Type& b, const Type& c, const Type& d, const Type& e) { - return std::max(std::max(a,b), Max(c,d,e)); + return internal::max_impl(internal::max_impl(a,b), Max(c,d,e)); } /// Return the maximum of six values @@ -626,7 +676,7 @@ template inline const Type& Max(const Type& a, const Type& b, const Type& c, const Type& d, const Type& e, const Type& f) { - return std::max(Max(a,b,c), Max(d,e,f)); + return internal::max_impl(Max(a,b,c), Max(d,e,f)); } /// Return the maximum of seven values @@ -635,7 +685,7 @@ inline const Type& Max(const Type& a, const Type& b, const Type& c, const Type& d, const Type& e, const Type& f, const Type& g) { - return std::max(Max(a,b,c,d), Max(e,f,g)); + return internal::max_impl(Max(a,b,c,d), Max(e,f,g)); } /// Return the maximum of eight values @@ -644,28 +694,48 @@ inline const Type& Max(const Type& a, const Type& b, const Type& c, const Type& d, const Type& e, const Type& f, const Type& g, const Type& h) { - return std::max(Max(a,b,c,d), Max(e,f,g,h)); + return internal::max_impl(Max(a,b,c,d), Max(e,f,g,h)); } // ==========> Min <================== +namespace internal { + +inline const math::half& +min_impl(const math::half& a, const math::half& b) +{ + return a < b ? a : b; +} + +template +inline const Type& +min_impl(const Type& a, const Type& b) +{ + return std::min(a,b); +} + +} // namespace internal + /// Return the minimum of two values template inline const Type& -Min(const Type& a, const Type& b) { return std::min(a, b); } +Min(const Type& a, const Type& b) { return internal::min_impl(a, b); } /// Return the minimum of three values template inline const Type& -Min(const Type& a, const Type& b, const Type& c) { return std::min(std::min(a, b), c); } +Min(const Type& a, const Type& b, const Type& c) +{ + return internal::min_impl(internal::min_impl(a, b), c); +} /// Return the minimum of four values template inline const Type& Min(const Type& a, const Type& b, const Type& c, const Type& d) { - return std::min(std::min(a, b), std::min(c, d)); + return internal::min_impl(internal::min_impl(a, b), internal::min_impl(c, d)); } /// Return the minimum of five values @@ -673,7 +743,7 @@ template inline const Type& Min(const Type& a, const Type& b, const Type& c, const Type& d, const Type& e) { - return std::min(std::min(a,b), Min(c,d,e)); + return internal::min_impl(internal::min_impl(a,b), Min(c,d,e)); } /// Return the minimum of six values @@ -681,7 +751,7 @@ template inline const Type& Min(const Type& a, const Type& b, const Type& c, const Type& d, const Type& e, const Type& f) { - return std::min(Min(a,b,c), Min(d,e,f)); + return internal::min_impl(Min(a,b,c), Min(d,e,f)); } /// Return the minimum of seven values @@ -690,7 +760,7 @@ inline const Type& Min(const Type& a, const Type& b, const Type& c, const Type& d, const Type& e, const Type& f, const Type& g) { - return std::min(Min(a,b,c,d), Min(e,f,g)); + return internal::min_impl(Min(a,b,c,d), Min(e,f,g)); } /// Return the minimum of eight values @@ -699,7 +769,7 @@ inline const Type& Min(const Type& a, const Type& b, const Type& c, const Type& d, const Type& e, const Type& f, const Type& g, const Type& h) { - return std::min(Min(a,b,c,d), Min(e,f,g,h)); + return internal::min_impl(Min(a,b,c,d), Min(e,f,g,h)); } diff --git a/openvdb/openvdb/math/Stencils.h b/openvdb/openvdb/math/Stencils.h index 0c4ed2c0c8..285aecb965 100644 --- a/openvdb/openvdb/math/Stencils.h +++ b/openvdb/openvdb/math/Stencils.h @@ -37,7 +37,7 @@ class BaseStencil public: typedef GridT GridType; typedef typename GridT::TreeType TreeType; - typedef typename GridT::ValueType ValueType; + typedef typename GridT::ComputeType ValueType; typedef tree::ValueAccessor AccessorType; typedef std::vector BufferType; @@ -250,7 +250,7 @@ class SevenPointStencil: public BaseStencil, Gr public: typedef GridT GridType; typedef typename GridT::TreeType TreeType; - typedef typename GridT::ValueType ValueType; + typedef typename GridT::ComputeType ValueType; static const int SIZE = 7; @@ -304,7 +304,7 @@ class BoxStencil: public BaseStencil, GridT, IsSafe> public: typedef GridT GridType; typedef typename GridT::TreeType TreeType; - typedef typename GridT::ValueType ValueType; + typedef typename GridT::ComputeType ValueType; static const int SIZE = 8; @@ -475,7 +475,7 @@ class SecondOrderDenseStencil public: typedef GridT GridType; typedef typename GridT::TreeType TreeType; - typedef typename GridType::ValueType ValueType; + typedef typename GridType::ComputeType ValueType; static const int SIZE = 19; @@ -555,7 +555,7 @@ class ThirteenPointStencil public: typedef GridT GridType; typedef typename GridT::TreeType TreeType; - typedef typename GridType::ValueType ValueType; + typedef typename GridType::ComputeType ValueType; static const int SIZE = 13; @@ -686,7 +686,7 @@ class FourthOrderDenseStencil public: typedef GridT GridType; typedef typename GridT::TreeType TreeType; - typedef typename GridType::ValueType ValueType; + typedef typename GridType::ComputeType ValueType; static const int SIZE = 61; @@ -825,7 +825,7 @@ class NineteenPointStencil public: typedef GridT GridType; typedef typename GridT::TreeType TreeType; - typedef typename GridType::ValueType ValueType; + typedef typename GridType::ComputeType ValueType; static const int SIZE = 19; @@ -1041,7 +1041,7 @@ class SixthOrderDenseStencil public: typedef GridT GridType; typedef typename GridT::TreeType TreeType; - typedef typename GridType::ValueType ValueType; + typedef typename GridType::ComputeType ValueType; static const int SIZE = 127; @@ -1235,7 +1235,7 @@ class GradStencil : public BaseStencil, GridT, IsSafe public: typedef GridT GridType; typedef typename GridT::TreeType TreeType; - typedef typename GridType::ValueType ValueType; + typedef typename GridType::ComputeType ValueType; static const int SIZE = 7; @@ -1369,7 +1369,7 @@ class WenoStencil: public BaseStencil, GridT, IsSafe> public: typedef GridT GridType; typedef typename GridT::TreeType TreeType; - typedef typename GridType::ValueType ValueType; + typedef typename GridType::ComputeType ValueType; static const int SIZE = 19; @@ -1519,7 +1519,7 @@ class CurvatureStencil: public BaseStencil, Grid public: typedef GridT GridType; typedef typename GridT::TreeType TreeType; - typedef typename GridT::ValueType ValueType; + typedef typename GridT::ComputeType ValueType; static const int SIZE = 19; @@ -1545,7 +1545,7 @@ class CurvatureStencil: public BaseStencil, Grid { Real alpha, normGrad; return this->meanCurvature(alpha, normGrad) ? - ValueType(alpha*mInv2Dx/math::Pow3(normGrad)) : 0; + ValueType(alpha*mInv2Dx/math::Pow3(normGrad)) : ValueType(0); } /// @brief Return the Gaussian curvature at the previously buffered location. @@ -1556,7 +1556,7 @@ class CurvatureStencil: public BaseStencil, Grid { Real alpha, normGrad; return this->gaussianCurvature(alpha, normGrad) ? - ValueType(alpha*mInvDx2/math::Pow4(normGrad)) : 0; + ValueType(alpha*mInvDx2/math::Pow4(normGrad)) : ValueType(0); } /// @brief Return both the mean and the Gaussian curvature at the @@ -1571,7 +1571,7 @@ class CurvatureStencil: public BaseStencil, Grid mean = ValueType(alphaM*mInv2Dx/math::Pow3(normGrad)); gauss = ValueType(alphaG*mInvDx2/math::Pow4(normGrad)); } else { - mean = gauss = 0; + mean = gauss = ValueType(0); } } @@ -1585,7 +1585,7 @@ class CurvatureStencil: public BaseStencil, Grid { Real alpha, normGrad; return this->meanCurvature(alpha, normGrad) ? - ValueType(alpha*mInvDx2/(2*math::Pow2(normGrad))) : 0; + ValueType(alpha*mInvDx2/(2*math::Pow2(normGrad))) : ValueType(0); } /// Return the mean Gaussian multiplied by the norm of the @@ -1597,7 +1597,7 @@ class CurvatureStencil: public BaseStencil, Grid { Real alpha, normGrad; return this->gaussianCurvature(alpha, normGrad) ? - ValueType(2*alpha*mInv2Dx*mInvDx2/math::Pow3(normGrad)) : 0; + ValueType(2*alpha*mInv2Dx*mInvDx2/math::Pow3(normGrad)) : ValueType(0); } /// @brief Return both the mean and the Gaussian curvature at the @@ -1627,7 +1627,7 @@ class CurvatureStencil: public BaseStencil, Grid Real alphaM, alphaG, normGrad; if (this->curvatures(alphaM, alphaG, normGrad)) { const Real mean = alphaM*mInv2Dx/math::Pow3(normGrad); - const Real tmp = std::sqrt(mean*mean - alphaG*mInvDx2/math::Pow4(normGrad)); + const Real tmp = math::Sqrt(mean*mean - alphaG*mInvDx2/math::Pow4(normGrad)); pair.first = ValueType(mean - tmp); pair.second = ValueType(mean + tmp); } @@ -1768,7 +1768,7 @@ class DenseStencil: public BaseStencil, GridT, IsSaf public: typedef GridT GridType; typedef typename GridT::TreeType TreeType; - typedef typename GridType::ValueType ValueType; + typedef typename GridType::ComputeType ValueType; DenseStencil(const GridType& grid, int halfWidth) : BaseType(grid, /*size=*/math::Pow3(2 * halfWidth + 1)) diff --git a/openvdb/openvdb/openvdb.cc b/openvdb/openvdb/openvdb.cc index 6a89674e5e..ee58883918 100644 --- a/openvdb/openvdb/openvdb.cc +++ b/openvdb/openvdb/openvdb.cc @@ -14,31 +14,32 @@ #include #endif -#if OPENVDB_ABI_VERSION_NUMBER <= 8 - #error ABI <= 8 is no longer supported +#if OPENVDB_ABI_VERSION_NUMBER <= 9 + #error ABI <= 9 is no longer supported #endif // If using an OPENVDB_ABI_VERSION_NUMBER that has been deprecated, issue an // error directive. This can be optionally suppressed by defining: // OPENVDB_USE_DEPRECATED_ABI_=ON. -#ifndef OPENVDB_USE_DEPRECATED_ABI_9 - #if OPENVDB_ABI_VERSION_NUMBER == 9 - #error ABI = 9 is deprecated, CMake option OPENVDB_USE_DEPRECATED_ABI_9 suppresses this error - #endif -#endif #ifndef OPENVDB_USE_DEPRECATED_ABI_10 #if OPENVDB_ABI_VERSION_NUMBER == 10 #error ABI = 10 is deprecated, CMake option OPENVDB_USE_DEPRECATED_ABI_10 suppresses this error #endif #endif +#ifndef OPENVDB_USE_DEPRECATED_ABI_11 + #if OPENVDB_ABI_VERSION_NUMBER == 11 + PRAGMA(message("NOTE: ABI = 11 is deprecated, define OPENVDB_USE_DEPRECATED_ABI_11 " + "to suppress this message")) + #endif +#endif // If using a future OPENVDB_ABI_VERSION_NUMBER, issue an error directive. // This can be optionally suppressed by defining: // OPENVDB_USE_FUTURE_ABI_=ON. -#ifndef OPENVDB_USE_FUTURE_ABI_12 - #if OPENVDB_ABI_VERSION_NUMBER == 12 - #error ABI = 12 is still in active development and has not been finalized, \ -CMake option OPENVDB_USE_FUTURE_ABI_12 suppresses this error +#ifndef OPENVDB_USE_FUTURE_ABI_13 + #if OPENVDB_ABI_VERSION_NUMBER == 13 + #error ABI = 13 is still in active development and has not been finalized, \ +CMake option OPENVDB_USE_FUTURE_ABI_13 suppresses this error #endif #endif diff --git a/openvdb/openvdb/openvdb.h b/openvdb/openvdb/openvdb.h index 1e47cae1a9..eec6aba555 100644 --- a/openvdb/openvdb/openvdb.h +++ b/openvdb/openvdb/openvdb.h @@ -53,6 +53,7 @@ namespace io { class DelayedLoadMetadata; } using BoolTree = tree::Tree4::Type; using DoubleTree = tree::Tree4::Type; using FloatTree = tree::Tree4::Type; +using HalfTree = tree::Tree4::Type; using Int32Tree = tree::Tree4::Type; using Int64Tree = tree::Tree4::Type; using MaskTree = tree::Tree4::Type; @@ -73,6 +74,7 @@ using VectorTree = Vec3fTree; using BoolGrid = Grid; using DoubleGrid = Grid; using FloatGrid = Grid; +using HalfGrid = Grid; using Int32Grid = Grid; using Int64Grid = Grid; using MaskGrid = Grid; @@ -204,6 +206,7 @@ using MetaTypes = TypeList< BoolMetadata, DoubleMetadata, FloatMetadata, + HalfMetadata, Int32Metadata, Int64Metadata, StringMetadata, diff --git a/openvdb/openvdb/points/AttributeArray.h b/openvdb/openvdb/points/AttributeArray.h index 0f08a2ce21..f09b58821f 100644 --- a/openvdb/openvdb/points/AttributeArray.h +++ b/openvdb/openvdb/points/AttributeArray.h @@ -145,14 +145,6 @@ class OPENVDB_API AttributeArray /// Return a copy of this attribute. virtual AttributeArray::Ptr copy() const = 0; -#if OPENVDB_ABI_VERSION_NUMBER < 10 - /// Return a copy of this attribute. -#ifndef _MSC_VER - OPENVDB_DEPRECATED_MESSAGE("In-memory compression no longer supported, use AttributeArray::copy() instead") -#endif - virtual AttributeArray::Ptr copyUncompressed() const = 0; -#endif - /// Return the number of elements in this array. /// @note This does not count each data element in a strided array virtual Index size() const = 0; @@ -197,13 +189,11 @@ class OPENVDB_API AttributeArray /// Return the number of bytes of memory used by this attribute. virtual size_t memUsage() const = 0; -#if OPENVDB_ABI_VERSION_NUMBER >= 10 /// Return the number of bytes of memory used by this attribute array once it /// has been deserialized (this may be different to memUsage() if delay-loading /// is in use). Note that this method does NOT consider the fact that a /// uniform attribute could be expanded and only deals with delay-loading. virtual size_t memUsageIfLoaded() const = 0; -#endif /// Create a new attribute array of the given (registered) type, length and stride. /// @details If @a lock is non-null, the AttributeArray registry mutex @@ -228,15 +218,6 @@ class OPENVDB_API AttributeArray template bool hasValueType() const { return this->type().first == typeNameAsString(); } -#if OPENVDB_ABI_VERSION_NUMBER < 10 - /// @brief Set value at given index @a n from @a sourceIndex of another @a sourceArray. - // Windows does not allow base classes to be easily deprecated. -#ifndef _MSC_VER - OPENVDB_DEPRECATED_MESSAGE("Use copyValues() with source-target index pairs") -#endif - virtual void set(const Index n, const AttributeArray& sourceArray, const Index sourceIndex) = 0; -#endif - /// @brief Copy values into this array from a source array to a target array /// as referenced by an iterator. /// @details Iterators must adhere to the ForwardIterator interface described @@ -278,19 +259,6 @@ class OPENVDB_API AttributeArray /// Compact the existing array to become uniform if all values are identical virtual bool compact() = 0; -#if OPENVDB_ABI_VERSION_NUMBER < 10 - // Windows does not allow base classes to be deprecated -#ifndef _MSC_VER - OPENVDB_DEPRECATED_MESSAGE("Previously this compressed the attribute array, now it does nothing") -#endif - virtual bool compress() = 0; - // Windows does not allow base classes to be deprecated -#ifndef _MSC_VER - OPENVDB_DEPRECATED_MESSAGE("Previously this uncompressed the attribute array, now it does nothing") -#endif - virtual bool decompress() = 0; -#endif - /// @brief Specify whether this attribute should be hidden (e.g., from UI or iterators). /// @details This is useful if the attribute is used for blind data or as scratch space /// for a calculation. @@ -359,10 +327,8 @@ class OPENVDB_API AttributeArray bool operator==(const AttributeArray& other) const; bool operator!=(const AttributeArray& other) const { return !this->operator==(other); } -#if OPENVDB_ABI_VERSION_NUMBER >= 9 /// Indirect virtual function to retrieve the data buffer cast to a char byte array const char* constDataAsByteArray() const { return this->dataAsByteArray(); } -#endif private: friend class ::TestAttributeArray; @@ -565,11 +531,6 @@ class TypedAttributeArray final: public AttributeArray /// while being copied using this copy-constructor in another thread. /// It is not thread-safe for write. TypedAttributeArray(const TypedAttributeArray&); -#if OPENVDB_ABI_VERSION_NUMBER < 10 - /// Deep copy constructor. - OPENVDB_DEPRECATED_MESSAGE("Use copy-constructor without unused bool parameter") - TypedAttributeArray(const TypedAttributeArray&, bool /*unused*/); -#endif /// Deep copy assignment operator. /// @note this operator is thread-safe. @@ -585,13 +546,6 @@ class TypedAttributeArray final: public AttributeArray /// @note This method is thread-safe. AttributeArray::Ptr copy() const override; -#if OPENVDB_ABI_VERSION_NUMBER < 10 - /// Return a copy of this attribute. - /// @note This method is thread-safe. - OPENVDB_DEPRECATED_MESSAGE("In-memory compression no longer supported, use AttributeArray::copy() instead") - AttributeArray::Ptr copyUncompressed() const override; -#endif - /// Return a new attribute array of the given length @a n and @a stride with uniform value zero. static Ptr create(Index n, Index strideOrTotalSize = 1, bool constantStride = true, const Metadata* metadata = nullptr); @@ -657,13 +611,11 @@ class TypedAttributeArray final: public AttributeArray /// Return the number of bytes of memory used by this attribute. size_t memUsage() const override; -#if OPENVDB_ABI_VERSION_NUMBER >= 10 /// Return the number of bytes of memory used by this attribute array once it /// has been deserialized (this may be different to memUsage() if delay-loading /// is in use). Note that this method does NOT consider the fact that a /// uniform attribute could be expanded and only deals with delay-loading. size_t memUsageIfLoaded() const override; -#endif /// Return the value at index @a n (assumes in-core) ValueType getUnsafe(Index n) const; @@ -691,12 +643,6 @@ class TypedAttributeArray final: public AttributeArray /// (assumes in-core) static void setUnsafe(AttributeArray* array, const Index n, const ValueType& value); -#if OPENVDB_ABI_VERSION_NUMBER < 10 - /// Set value at given index @a n from @a sourceIndex of another @a sourceArray - OPENVDB_DEPRECATED_MESSAGE("Use copyValues() with source-target index pairs") - void set(const Index n, const AttributeArray& sourceArray, const Index sourceIndex) override; -#endif - /// Return @c true if this array is stored as a single uniform value. bool isUniform() const override { return mIsUniform; } /// @brief Replace the single value storage with an array of length size(). @@ -719,15 +665,6 @@ class TypedAttributeArray final: public AttributeArray /// Non-member equivalent to fill() that static_casts array to this TypedAttributeArray static void fill(AttributeArray* array, const ValueType& value); -#if OPENVDB_ABI_VERSION_NUMBER < 10 - /// Compress the attribute array. - OPENVDB_DEPRECATED_MESSAGE("Previously this compressed the attribute array, now it does nothing") - bool compress() override; - /// Uncompress the attribute array. - OPENVDB_DEPRECATED_MESSAGE("Previously this uncompressed the attribute array, now it does nothing") - bool decompress() override; -#endif - /// Read attribute data from a stream. void read(std::istream&) override; /// Write attribute data to a stream. @@ -789,14 +726,7 @@ class TypedAttributeArray final: public AttributeArray /// Load data from memory-mapped file. inline void doLoad() const; /// Load data from memory-mapped file (unsafe as this function is not protected by a mutex). -#if OPENVDB_ABI_VERSION_NUMBER >= 10 inline void doLoadUnsafe() const; -#else - /// @param compression parameter no longer used - inline void doLoadUnsafe(const bool compression = true) const; - /// Compress in-core data assuming mutex is locked - inline bool compressUnsafe(); -#endif /// Toggle out-of-core state inline void setOutOfCore(const bool); @@ -1275,15 +1205,6 @@ TypedAttributeArray::copy() const } -#if OPENVDB_ABI_VERSION_NUMBER < 10 -template -AttributeArray::Ptr -TypedAttributeArray::copyUncompressed() const -{ - return this->copy(); -} -#endif - template size_t TypedAttributeArray::arrayMemUsage() const @@ -1385,14 +1306,13 @@ TypedAttributeArray::memUsage() const return sizeof(*this) + (bool(mData) ? this->arrayMemUsage() : 0); } -#if OPENVDB_ABI_VERSION_NUMBER >= 10 + template size_t TypedAttributeArray::memUsageIfLoaded() const { return sizeof(*this) + (mIsUniform ? 1 : this->dataSize()) * sizeof(StorageType); } -#endif template @@ -1497,21 +1417,6 @@ TypedAttributeArray::setUnsafe(AttributeArray* array, const } -#if OPENVDB_ABI_VERSION_NUMBER < 10 -template -void -TypedAttributeArray::set(Index n, const AttributeArray& sourceArray, const Index sourceIndex) -{ - const TypedAttributeArray& sourceTypedArray = static_cast(sourceArray); - - ValueType sourceValue; - sourceTypedArray.get(sourceIndex, sourceValue); - - this->set(n, sourceValue); -} -#endif - - template void TypedAttributeArray::expand(bool fill) @@ -1605,32 +1510,6 @@ TypedAttributeArray::fill(AttributeArray* array, const Value } -#if OPENVDB_ABI_VERSION_NUMBER < 10 -template -inline bool -TypedAttributeArray::compress() -{ - return false; -} - - -template -inline bool -TypedAttributeArray::compressUnsafe() -{ - return false; -} - - -template -inline bool -TypedAttributeArray::decompress() -{ - return false; -} -#endif - - template bool TypedAttributeArray::isOutOfCore() const @@ -1976,11 +1855,7 @@ TypedAttributeArray::writePagedBuffers(compression::PagedOut template void -#if OPENVDB_ABI_VERSION_NUMBER >= 10 TypedAttributeArray::doLoadUnsafe() const -#else -TypedAttributeArray::doLoadUnsafe(const bool /*compression*/) const -#endif { if (!(this->isOutOfCore())) return; diff --git a/openvdb/openvdb/points/AttributeSet.cc b/openvdb/openvdb/points/AttributeSet.cc index 475922aee1..1eebe436ff 100644 --- a/openvdb/openvdb/points/AttributeSet.cc +++ b/openvdb/openvdb/points/AttributeSet.cc @@ -140,7 +140,6 @@ AttributeSet::memUsage() const } -#if OPENVDB_ABI_VERSION_NUMBER >= 10 size_t AttributeSet::memUsageIfLoaded() const { @@ -150,7 +149,6 @@ AttributeSet::memUsageIfLoaded() const } return bytes; } -#endif size_t diff --git a/openvdb/openvdb/points/AttributeSet.h b/openvdb/openvdb/points/AttributeSet.h index 1f3d70710a..3137eb7413 100644 --- a/openvdb/openvdb/points/AttributeSet.h +++ b/openvdb/openvdb/points/AttributeSet.h @@ -114,12 +114,10 @@ class OPENVDB_API AttributeSet /// Return the number of bytes of memory used by this attribute set. size_t memUsage() const; -#if OPENVDB_ABI_VERSION_NUMBER >= 10 /// Return the number of bytes of memory used by this attribute set once it /// has been deserialized (this may be different to memUsage() if delay-loading /// is in use). size_t memUsageIfLoaded() const; -#endif /// @brief Return the position of the attribute array whose name is @a name, /// or @c INVALID_POS if no match is found. diff --git a/openvdb/openvdb/points/IndexFilter.h b/openvdb/openvdb/points/IndexFilter.h index 28e2dc9c47..9f0e57d708 100644 --- a/openvdb/openvdb/points/IndexFilter.h +++ b/openvdb/openvdb/points/IndexFilter.h @@ -245,7 +245,7 @@ class RandomLeafFilter std::mt19937 generator(seed); std::uniform_int_distribution dist(0, std::numeric_limits::max() - 1); - Index32 leafCounter = 0; + Index64 leafCounter = 0; float totalPointsFloat = 0.0f; int totalPoints = 0; for (auto iter = tree.cbeginLeaf(); iter; ++iter) { diff --git a/openvdb/openvdb/points/PointDataGrid.h b/openvdb/openvdb/points/PointDataGrid.h index 3d93a70c70..9852796753 100644 --- a/openvdb/openvdb/points/PointDataGrid.h +++ b/openvdb/openvdb/points/PointDataGrid.h @@ -504,9 +504,7 @@ class PointDataLeafNode : public tree::LeafNode, io::MultiPass { Index64 memUsage() const; -#if OPENVDB_ABI_VERSION_NUMBER >= 10 Index64 memUsageIfLoaded() const; -#endif void evalActiveBoundingBox(CoordBBox& bbox, bool visitVoxels = true) const; @@ -1524,14 +1522,12 @@ PointDataLeafNode::memUsage() const return BaseLeaf::memUsage() + mAttributeSet->memUsage(); } -#if OPENVDB_ABI_VERSION_NUMBER >= 10 template inline Index64 PointDataLeafNode::memUsageIfLoaded() const { return BaseLeaf::memUsageIfLoaded() + mAttributeSet->memUsageIfLoaded(); } -#endif template inline void diff --git a/openvdb/openvdb/python/CMakeLists.txt b/openvdb/openvdb/python/CMakeLists.txt index f54ff3ac55..c67884bce3 100644 --- a/openvdb/openvdb/python/CMakeLists.txt +++ b/openvdb/openvdb/python/CMakeLists.txt @@ -7,7 +7,7 @@ #]=======================================================================] -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.20) project(OpenVDBPython LANGUAGES CXX) include(GNUInstallDirs) @@ -74,8 +74,13 @@ if(USE_AX) target_compile_definitions(openvdb_python PUBLIC "-DPY_OPENVDB_USE_AX") endif() set_target_properties(openvdb_python PROPERTIES OUTPUT_NAME "openvdb") - -install(TARGETS openvdb_python DESTINATION ${VDB_PYTHON_INSTALL_DIRECTORY}) +if(SKBUILD) + set_target_properties(openvdb_python PROPERTIES INSTALL_RPATH "$ORIGIN") + install(TARGETS openvdb_python DESTINATION ${OPENVDB_INSTALL_LIBDIR}) + install(FILES __init__.py DESTINATION openvdb) +else() + install(TARGETS openvdb_python DESTINATION ${VDB_PYTHON_INSTALL_DIRECTORY}) +endif() # pytest if(OPENVDB_BUILD_PYTHON_UNITTESTS) diff --git a/openvdb/openvdb/python/__init__.py b/openvdb/openvdb/python/__init__.py new file mode 100644 index 0000000000..39242a9737 --- /dev/null +++ b/openvdb/openvdb/python/__init__.py @@ -0,0 +1,3 @@ +# Copyright Contributors to the OpenVDB Project +# SPDX-License-Identifier: Apache-2.0 +from .lib.openvdb import * diff --git a/openvdb/openvdb/python/pyGrid.h b/openvdb/openvdb/python/pyGrid.h index c7d9b2d205..a8948064a6 100644 --- a/openvdb/openvdb/python/pyGrid.h +++ b/openvdb/openvdb/python/pyGrid.h @@ -210,7 +210,7 @@ getNodeLog2Dims(const GridType& grid) template -inline Index +inline Index64 treeDepth(const GridType& grid) { return grid.tree().treeDepth(); @@ -218,7 +218,7 @@ treeDepth(const GridType& grid) template -inline Index32 +inline Index64 leafCount(const GridType& grid) { return grid.tree().leafCount(); @@ -226,7 +226,7 @@ leafCount(const GridType& grid) template -inline Index32 +inline Index64 nonLeafCount(const GridType& grid) { return grid.tree().nonLeafCount(); diff --git a/openvdb/openvdb/python/pyTypeCasters.h b/openvdb/openvdb/python/pyTypeCasters.h index c9faf3f0bd..56159666bd 100644 --- a/openvdb/openvdb/python/pyTypeCasters.h +++ b/openvdb/openvdb/python/pyTypeCasters.h @@ -1,3 +1,5 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 #ifndef OPENVDB_PYTYPECASTERS_HAS_BEEN_INCLUDED #define OPENVDB_PYTYPECASTERS_HAS_BEEN_INCLUDED diff --git a/openvdb/openvdb/tools/FastSweeping.h b/openvdb/openvdb/tools/FastSweeping.h index b7d652a217..22646aa196 100644 --- a/openvdb/openvdb/tools/FastSweeping.h +++ b/openvdb/openvdb/tools/FastSweeping.h @@ -460,18 +460,20 @@ maskSdf(const GridT &sdfGrid, template class FastSweeping { - static_assert(std::is_floating_point::value, + static_assert(std::is_floating_point::value, "FastSweeping requires SdfGridT to have floating-point values"); // Defined types related to the signed distance (or fog) grid using SdfValueT = typename SdfGridT::ValueType; + using SdfComputeT = typename SdfGridT::ComputeType; using SdfTreeT = typename SdfGridT::TreeType; using SdfAccT = tree::ValueAccessor;//don't register accessors using SdfConstAccT = typename tree::ValueAccessor;//don't register accessors // define types related to the extension field - using ExtGridT = typename SdfGridT::template ValueConverter::Type; - using ExtTreeT = typename ExtGridT::TreeType; - using ExtAccT = tree::ValueAccessor; + using ExtGridT = typename SdfGridT::template ValueConverter::Type; + using ExtTreeT = typename ExtGridT::TreeType; + using ExtComputeT = typename ExtGridT::ComputeType; + using ExtAccT = tree::ValueAccessor; // define types related to the tree that masks out the active voxels to be solved for using SweepMaskTreeT = typename SdfTreeT::template ValueConverter::Type; @@ -1073,7 +1075,7 @@ struct FastSweeping::DilateKernel } else { switch (mode) { case FastSweepingDomain::SWEEP_ALL : - voxelIter.setValue(value > 0 ? Unknown : -Unknown); + voxelIter.setValue(math::isNegative(value) ? -Unknown : Unknown); break; case FastSweepingDomain::SWEEP_GREATER_THAN_ISOVALUE : if (value > 0) voxelIter.setValue(Unknown); @@ -1085,7 +1087,7 @@ struct FastSweeping::DilateKernel } break; case FastSweepingDomain::SWEEP_LESS_THAN_ISOVALUE : - if (value < 0) voxelIter.setValue(-Unknown); + if (math::isNegative(value)) voxelIter.setValue(-Unknown); else { maskLeaf->setValueOff(voxelIter.pos()); bool isInputOn = sdfInputAcc.probeValue(ijk, inputValue); @@ -1127,8 +1129,8 @@ struct FastSweeping::InitSdf void run(SdfValueT isoValue) { - mIsoValue = isoValue; - mAboveSign = mParent->mIsInputSdf ? SdfValueT(1) : SdfValueT(-1); + mIsoValue = SdfComputeT(isoValue); + mAboveSign = mParent->mIsInputSdf ? SdfComputeT(1) : SdfComputeT(-1); SdfTreeT &tree = mSdfGrid->tree();//sdf const bool hasActiveTiles = tree.hasActiveTiles(); @@ -1166,12 +1168,13 @@ struct FastSweeping::InitSdf { SweepMaskAccT sweepMaskAcc(mParent->mSweepMask); math::GradStencil stencil(*mSdfGrid); - const SdfValueT isoValue = mIsoValue, above = mAboveSign*std::numeric_limits::max();//local copy - const SdfValueT h = mAboveSign*static_cast(mSdfGrid->voxelSize()[0]);//Voxel size + const SdfComputeT isoValue = mIsoValue; + const SdfValueT above = mAboveSign*std::numeric_limits::max();//local copy + const SdfComputeT h = mAboveSign*static_cast(mSdfGrid->voxelSize()[0]);//Voxel size for (auto leafIter = r.begin(); leafIter; ++leafIter) { SdfValueT* sdf = leafIter.buffer(1).data(); for (auto voxelIter = leafIter->beginValueAll(); voxelIter; ++voxelIter) { - const SdfValueT value = *voxelIter; + const SdfComputeT value = SdfComputeT(*voxelIter); const bool isAbove = value > isoValue; if (!voxelIter.isValueOn()) {// inactive voxels sdf[voxelIter.pos()] = isAbove ? above : -above; @@ -1184,21 +1187,21 @@ struct FastSweeping::InitSdf } else {// compute distance to iso-surface // disable boundary voxels from the mask tree sweepMaskAcc.setValueOff(ijk); - const SdfValueT delta = value - isoValue;//offset relative to iso-value + const SdfComputeT delta = value - isoValue;//offset relative to iso-value if (math::isApproxZero(delta)) {//voxel is on the iso-surface sdf[voxelIter.pos()] = 0; } else {//voxel is neighboring the iso-surface - SdfValueT sum = 0; + SdfComputeT sum = 0; for (int i=0; i<6;) { - SdfValueT d = std::numeric_limits::max(), d2; + SdfComputeT d = std::numeric_limits::max(), d2; if (mask.test(i++)) d = math::Abs(delta/(value-stencil.getValue(i))); if (mask.test(i++)) { d2 = math::Abs(delta/(value-stencil.getValue(i))); if (d2 < d) d = d2; } - if (d < std::numeric_limits::max()) sum += 1/(d*d); + if (d < std::numeric_limits::max()) sum += 1/(d*d); } - sdf[voxelIter.pos()] = isAbove ? h / math::Sqrt(sum) : -h / math::Sqrt(sum); + sdf[voxelIter.pos()] = SdfValueT(isAbove ? h / math::Sqrt(sum) : -h / math::Sqrt(sum)); }// voxel is neighboring the iso-surface }// intersecting voxels }// active voxels @@ -1209,18 +1212,19 @@ struct FastSweeping::InitSdf template void operator()(const RootOrInternalNodeT& node) const { - const SdfValueT isoValue = mIsoValue, above = mAboveSign*std::numeric_limits::max(); + const SdfComputeT isoValue = mIsoValue; + const SdfValueT above = mAboveSign*std::numeric_limits::max(); for (auto it = node.cbeginValueAll(); it; ++it) { SdfValueT& v = const_cast(*it); - v = v > isoValue ? above : -above; + v = SdfComputeT(v) > isoValue ? above : -above; }//loop over all tiles }// FastSweeping::InitSdf::operator()(const RootOrInternalNodeT&) // Public member data FastSweeping *mParent; SdfGridT *mSdfGrid;//raw pointer, i.e. lock free - SdfValueT mIsoValue; - SdfValueT mAboveSign;//sign of distance values above the iso-value + SdfComputeT mIsoValue; + SdfComputeT mAboveSign;//sign of distance values above the iso-value };// FastSweeping::InitSdf @@ -1244,7 +1248,7 @@ struct FastSweeping::InitExt } mAboveSign = mParent->mIsInputSdf ? SdfValueT(1) : SdfValueT(-1); - mIsoValue = isoValue; + mIsoValue = SdfComputeT(isoValue); auto &tree1 = mSdfGrid->tree(); auto &tree2 = mExtGrid->tree(); const bool hasActiveTiles = tree1.hasActiveTiles();//very fast @@ -1296,17 +1300,17 @@ struct FastSweeping::InitExt }// FastSweeping::InitExt::run // int implements an update if minD needs to be updated - template::value, int>::type = 0> + template::value, int>::type = 0> void sumHelper(ExtT& sum2, ExtT ext, bool update, const SdfT& /* d2 */) const { if (update) sum2 = ext; }// int implementation // non-int implements a weighted sum - template::value, int>::type = 0> - void sumHelper(ExtT& sum2, ExtT ext, bool /* update */, const SdfT& d2) const { sum2 += static_cast(d2 * ext); }// non-int implementation + template::value, int>::type = 0> + void sumHelper(ExtT& sum2, ExtT ext, bool /* update */, const SdfT& d2) const { sum2 += static_cast(d2 * ext); }// non-int implementation - template::value, int>::type = 0> + template::value, int>::type = 0> ExtT extValHelper(ExtT extSum, const SdfT& /* sdfSum */) const { return extSum; }// int implementation - template::value, int>::type = 0> + template::value, int>::type = 0> ExtT extValHelper(ExtT extSum, const SdfT& sdfSum) const {return ExtT((SdfT(1) / sdfSum) * extSum); }// non-int implementation void operator()(const LeafRange& r) const @@ -1316,13 +1320,14 @@ struct FastSweeping::InitExt math::GradStencil stencil(*mSdfGrid); const math::Transform& xform = mExtGrid->transform(); typename OpPoolT::reference op = mOpPool->local(); - const SdfValueT isoValue = mIsoValue, above = mAboveSign*std::numeric_limits::max();//local copy - const SdfValueT h = mAboveSign*static_cast(mSdfGrid->voxelSize()[0]);//Voxel size + const SdfComputeT isoValue = mIsoValue; + const SdfValueT above = mAboveSign*std::numeric_limits::max();//local copy + const SdfComputeT h = mAboveSign*static_cast(mSdfGrid->voxelSize()[0]);//Voxel size for (auto leafIter = r.begin(); leafIter; ++leafIter) { SdfValueT *sdf = leafIter.buffer(1).data(); ExtValueT *ext = acc.probeLeaf(leafIter->origin())->buffer().data();//should be safe! for (auto voxelIter = leafIter->beginValueAll(); voxelIter; ++voxelIter) { - const SdfValueT value = *voxelIter; + const SdfComputeT value = SdfComputeT(*voxelIter); const bool isAbove = value > isoValue; if (!voxelIter.isValueOn()) {// inactive voxels sdf[voxelIter.pos()] = isAbove ? above : -above; @@ -1336,20 +1341,20 @@ struct FastSweeping::InitExt } else {// compute distance to iso-surface // disable boundary voxels from the mask tree sweepMaskAcc.setValueOff(ijk); - const SdfValueT delta = value - isoValue;//offset relative to iso-value + const SdfComputeT delta = value - isoValue;//offset relative to iso-value if (math::isApproxZero(delta)) {//voxel is on the iso-surface sdf[voxelIter.pos()] = 0; ext[voxelIter.pos()] = ExtValueT(op(xform.indexToWorld(ijk))); } else {//voxel is neighboring the iso-surface - SdfValueT sum1 = 0; - ExtValueT sum2 = zeroVal(); + SdfComputeT sum1 = 0; + ExtComputeT sum2 = zeroVal(); // minD is used to update sum2 in the integer case, // where we choose the value of the extension grid corresponding to // the smallest value of d in the 6 direction neighboring the current // voxel. - SdfValueT minD = std::numeric_limits::max(); + SdfComputeT minD = std::numeric_limits::max(); for (int n=0, i=0; i<6;) { - SdfValueT d = std::numeric_limits::max(), d2; + SdfComputeT d = std::numeric_limits::max(), d2; if (mask.test(i++)) { d = math::Abs(delta/(value-stencil.getValue(i))); n = i - 1; @@ -1361,19 +1366,19 @@ struct FastSweeping::InitExt n = i - 1; } } - if (d < std::numeric_limits::max()) { + if (d < std::numeric_limits::max()) { d2 = 1/(d*d); sum1 += d2; - const Vec3R xyz(static_cast(ijk[0])+d*static_cast(FastSweeping::mOffset[n][0]), - static_cast(ijk[1])+d*static_cast(FastSweeping::mOffset[n][1]), - static_cast(ijk[2])+d*static_cast(FastSweeping::mOffset[n][2])); + const Vec3R xyz(static_cast(ijk[0])+d*static_cast(FastSweeping::mOffset[n][0]), + static_cast(ijk[1])+d*static_cast(FastSweeping::mOffset[n][1]), + static_cast(ijk[2])+d*static_cast(FastSweeping::mOffset[n][2])); // If current d is less than minD, update minD - sumHelper(sum2, ExtValueT(op(xform.indexToWorld(xyz))), d < minD, d2); + sumHelper(sum2, ExtComputeT(op(xform.indexToWorld(xyz))), d < minD, d2); if (d < minD) minD = d; } }//look over six cases - ext[voxelIter.pos()] = extValHelper(sum2, sum1); - sdf[voxelIter.pos()] = isAbove ? h / math::Sqrt(sum1) : -h / math::Sqrt(sum1); + ext[voxelIter.pos()] = ExtValueT(extValHelper(sum2, sum1)); + sdf[voxelIter.pos()] = SdfValueT(isAbove ? h / math::Sqrt(sum1) : -h / math::Sqrt(sum1)); }// voxel is neighboring the iso-surface }// intersecting voxels }// active voxels @@ -1384,10 +1389,11 @@ struct FastSweeping::InitExt template void operator()(const RootOrInternalNodeT& node) const { - const SdfValueT isoValue = mIsoValue, above = mAboveSign*std::numeric_limits::max(); + const SdfComputeT isoValue = mIsoValue; + const SdfValueT above = mAboveSign*std::numeric_limits::max(); for (auto it = node.cbeginValueAll(); it; ++it) { SdfValueT& v = const_cast(*it); - v = v > isoValue ? above : -above; + v = SdfComputeT(v) > isoValue ? above : -above; }//loop over all tiles } // Public member data @@ -1395,8 +1401,8 @@ struct FastSweeping::InitExt OpPoolT *mOpPool; SdfGridT *mSdfGrid; ExtGridT *mExtGrid; - SdfValueT mIsoValue; - SdfValueT mAboveSign;//sign of distance values above the iso-value + SdfComputeT mIsoValue; + SdfValueT mAboveSign;//sign of distance values above the iso-value };// FastSweeping::InitExt /// Private class of FastSweeping to perform multi-threaded initialization @@ -1592,26 +1598,26 @@ struct FastSweeping::SweepingKernel // Private struct for nearest neighbor grid points (very memory light!) struct NN { - SdfValueT v; + SdfComputeT v; int n; inline static Coord ijk(const Coord &p, int i) { return p + FastSweeping::mOffset[i]; } NN() : v(), n() {} - NN(const SdfAccT &a, const Coord &p, int i) : v(math::Abs(a.getValue(ijk(p,i)))), n(i) {} + NN(const SdfAccT &a, const Coord &p, int i) : v(math::Abs(SdfComputeT(a.getValue(ijk(p,i))))), n(i) {} inline Coord operator()(const Coord &p) const { return ijk(p, n); } inline bool operator<(const NN &rhs) const { return v < rhs.v; } - inline operator bool() const { return v < SdfValueT(1000); } + inline operator bool() const { return v < SdfComputeT(1000); } };// NN /// @note Extending an integer field is based on the nearest-neighbor interpolation - template::value, int>::type = 0> + template::value, int>::type = 0> ExtT twoNghbr(const NN& d1, const NN& d2, const SdfT& /* w */, const ExtT& v1, const ExtT& v2) const { return d1.v < d2.v ? v1 : v2; }// int implementation /// @note Extending a non-integer field is based on a weighted interpolation - template::value, int>::type = 0> + template::value, int>::type = 0> ExtT twoNghbr(const NN& d1, const NN& d2, const SdfT& w, const ExtT& v1, const ExtT& v2) const { return ExtT(w*(d1.v*v1 + d2.v*v2)); }// non-int implementation /// @note Extending an integer field is based on the nearest-neighbor interpolation - template::value, int>::type = 0> + template::value, int>::type = 0> ExtT threeNghbr(const NN& d1, const NN& d2, const NN& d3, const SdfT& /* w */, const ExtT& v1, const ExtT& v2, const ExtT& v3) const { math::Vec3 d(d1.v, d2.v, d3.v); math::Vec3 v(v1, v2, v3); @@ -1619,7 +1625,7 @@ struct FastSweeping::SweepingKernel }// int implementation /// @note Extending a non-integer field is based on a weighted interpolation - template::value, int>::type = 0> + template::value, int>::type = 0> ExtT threeNghbr(const NN& d1, const NN& d2, const NN& d3, const SdfT& w, const ExtT& v1, const ExtT& v2, const ExtT& v3) const { return ExtT(w*(d1.v*v1 + d2.v*v2 + d3.v*v3)); }// non-int implementation @@ -1629,8 +1635,8 @@ struct FastSweeping::SweepingKernel typename ExtGridT::TreeType *tree2 = mParent->mExtGrid ? &mParent->mExtGrid->tree() : nullptr; typename ExtGridT::TreeType *tree3 = mParent->mExtGridInput ? &mParent->mExtGridInput->tree() : nullptr; - const SdfValueT h = static_cast(mParent->mSdfGrid->voxelSize()[0]); - const SdfValueT sqrt2h = math::Sqrt(SdfValueT(2))*h; + const SdfComputeT h = static_cast(mParent->mSdfGrid->voxelSize()[0]); + const SdfComputeT sqrt2h = math::Sqrt(SdfComputeT(2))*h; const FastSweepingDomain mode = mParent->mSweepDirection; const bool isInputSdf = mParent->mIsInputSdf; @@ -1649,7 +1655,7 @@ struct FastSweeping::SweepingKernel SdfAccT acc1(mParent->mSdfGrid->tree()); auto acc2 = std::unique_ptr(tree2 ? new ExtAccT(*tree2) : nullptr); auto acc3 = std::unique_ptr(tree3 ? new ExtAccT(*tree3) : nullptr); - SdfValueT absV, sign, update, D; + SdfComputeT absV, sign, update, D; NN d1, d2, d3;//distance values and coordinates of closest neighbor points const LeafSliceArray& leafSliceArray = mVoxelSliceMap[voxelSliceIndex]; @@ -1686,10 +1692,10 @@ struct FastSweeping::SweepingKernel SdfValueT &value = const_cast(acc1.getValue(ijk)); // Extract the sign - sign = value >= SdfValueT(0) ? SdfValueT(1) : SdfValueT(-1); + sign = math::isNegative(value) ? SdfComputeT(-1) : SdfComputeT(1); // Absolute value - absV = math::Abs(value); + absV = SdfComputeT(math::Abs(value)); // sort values so d1 <= d2 <= d3 if (d2 < d1) std::swap(d1, d2); @@ -1707,12 +1713,12 @@ struct FastSweeping::SweepingKernel // There is an assert upstream to check if mExtGridInput exists if mode != SWEEP_ALL ExtValueT updateExt = acc2->getValue(d1(ijk)); if (mode == FastSweepingDomain::SWEEP_GREATER_THAN_ISOVALUE) { - if (isInputSdf) updateExt = (value >= SdfValueT(0)) ? acc2->getValue(d1(ijk)) : acc3->getValue(ijk); - else updateExt = (value <= SdfValueT(0)) ? acc2->getValue(d1(ijk)) : acc3->getValue(ijk); + if (isInputSdf) updateExt = math::isNegative(value) ? acc3->getValue(ijk) : acc2->getValue(d1(ijk)); + else updateExt = math::isNegative(-value) ? acc3->getValue(ijk) : acc2->getValue(d1(ijk)); } // SWEEP_GREATER_THAN_ISOVALUE else if (mode == FastSweepingDomain::SWEEP_LESS_THAN_ISOVALUE) { - if (isInputSdf) updateExt = (value <= SdfValueT(0)) ? acc2->getValue(d1(ijk)) : acc3->getValue(ijk); - else updateExt = (value >= SdfValueT(0)) ? acc2->getValue(d1(ijk)) : acc3->getValue(ijk); + if (isInputSdf) updateExt = math::isNegative(-value) ? acc3->getValue(ijk) : acc2->getValue(d1(ijk)); + else updateExt = math::isNegative(value) ? acc3->getValue(ijk) : acc2->getValue(d1(ijk)); } // SWEEP_LESS_THAN_ISOVALUE acc2->setValue(ijk, updateExt); }//update ext? @@ -1725,8 +1731,8 @@ struct FastSweeping::SweepingKernel //D = SdfValueT(2) * h * h - math::Pow2(d1.v - d2.v);// = 2h^2-(d1-d2)^2 //if (D >= SdfValueT(0)) {// non-negative discriminant if (d2.v <= sqrt2h + d1.v) { - D = SdfValueT(2) * h * h - math::Pow2(d1.v - d2.v);// = 2h^2-(d1-d2)^2 - update = SdfValueT(0.5) * (d1.v + d2.v + std::sqrt(D)); + D = SdfComputeT(2) * h * h - math::Pow2(d1.v - d2.v);// = 2h^2-(d1-d2)^2 + update = SdfComputeT(0.5) * (d1.v + d2.v + math::Sqrt(D)); if (update > d2.v && update <= d3.v) { if (update < absV) { value = sign * update; @@ -1734,19 +1740,19 @@ struct FastSweeping::SweepingKernel d1.v -= update; d2.v -= update; // affine combination of two neighboring extension values - const SdfValueT w = SdfValueT(1)/(d1.v+d2.v); - const ExtValueT v1 = acc2->getValue(d1(ijk)); - const ExtValueT v2 = acc2->getValue(d2(ijk)); - const ExtValueT extVal = twoNghbr(d1, d2, w, v1, v2); + const SdfComputeT w = SdfComputeT(1)/(d1.v+d2.v); + const ExtComputeT v1 = ExtComputeT(acc2->getValue(d1(ijk))); + const ExtComputeT v2 = ExtComputeT(acc2->getValue(d2(ijk))); + const ExtValueT extVal = ExtValueT(twoNghbr(d1, d2, w, v1, v2)); ExtValueT updateExt = extVal; if (mode == FastSweepingDomain::SWEEP_GREATER_THAN_ISOVALUE) { - if (isInputSdf) updateExt = (value >= SdfValueT(0)) ? extVal : acc3->getValue(ijk); - else updateExt = (value <= SdfValueT(0)) ? extVal : acc3->getValue(ijk); + if (isInputSdf) updateExt = math::isNegative(value) ? acc3->getValue(ijk) : extVal; + else updateExt = math::isNegative(-value) ? acc3->getValue(ijk) : extVal; } // SWEEP_GREATER_THAN_ISOVALUE else if (mode == FastSweepingDomain::SWEEP_LESS_THAN_ISOVALUE) { - if (isInputSdf) updateExt = (value <= SdfValueT(0)) ? extVal : acc3->getValue(ijk); - else updateExt = (value >= SdfValueT(0)) ? extVal : acc3->getValue(ijk); + if (isInputSdf) updateExt = math::isNegative(-value) ? acc3->getValue(ijk) : extVal; + else updateExt = math::isNegative(value) ? acc3->getValue(ijk) : extVal; } // SWEEP_LESS_THAN_ISOVALUE acc2->setValue(ijk, updateExt); }//update ext? @@ -1759,10 +1765,10 @@ struct FastSweeping::SweepingKernel // (x-d1)^2 + (x-d2)^2 + (x-d3)^2 = h^2 // 3x^2 - 2(d1 + d2 + d3)x + d1^2 + d2^2 + d3^2 = h^2 // ax^2 + bx + c=0, a=3, b=-2(d1+d2+d3), c=d1^2 + d2^2 + d3^2 - h^2 - const SdfValueT d123 = d1.v + d2.v + d3.v; - D = d123*d123 - SdfValueT(3)*(d1.v*d1.v + d2.v*d2.v + d3.v*d3.v - h * h); - if (D >= SdfValueT(0)) {// non-negative discriminant - update = SdfValueT(1.0/3.0) * (d123 + std::sqrt(D));//always passes test + const SdfComputeT d123 = d1.v + d2.v + d3.v; + D = d123*d123 - SdfComputeT(3)*(d1.v*d1.v + d2.v*d2.v + d3.v*d3.v - h * h); + if (D >= SdfComputeT(0)) {// non-negative discriminant + update = SdfComputeT(1.0/3.0) * (d123 + std::sqrt(D));//always passes test //if (update > d3.v) {//disabled due to round-off errors if (update < absV) { value = sign * update; @@ -1771,20 +1777,20 @@ struct FastSweeping::SweepingKernel d2.v -= update; d3.v -= update; // affine combination of three neighboring extension values - const SdfValueT w = SdfValueT(1)/(d1.v+d2.v+d3.v); - const ExtValueT v1 = acc2->getValue(d1(ijk)); - const ExtValueT v2 = acc2->getValue(d2(ijk)); - const ExtValueT v3 = acc2->getValue(d3(ijk)); - const ExtValueT extVal = threeNghbr(d1, d2, d3, w, v1, v2, v3); + const SdfComputeT w = SdfComputeT(1)/(d1.v+d2.v+d3.v); + const ExtComputeT v1 = ExtComputeT(acc2->getValue(d1(ijk))); + const ExtComputeT v2 = ExtComputeT(acc2->getValue(d2(ijk))); + const ExtComputeT v3 = ExtComputeT(acc2->getValue(d3(ijk))); + const ExtValueT extVal = ExtValueT(threeNghbr(d1, d2, d3, w, v1, v2, v3)); ExtValueT updateExt = extVal; if (mode == FastSweepingDomain::SWEEP_GREATER_THAN_ISOVALUE) { - if (isInputSdf) updateExt = (value >= SdfValueT(0)) ? extVal : acc3->getValue(ijk); - else updateExt = (value <= SdfValueT(0)) ? extVal : acc3->getValue(ijk); + if (isInputSdf) updateExt = math::isNegative(value) ? acc3->getValue(ijk) : extVal; + else updateExt = math::isNegative(-value) ? acc3->getValue(ijk) : extVal; } // SWEEP_GREATER_THAN_ISOVALUE else if (mode == FastSweepingDomain::SWEEP_LESS_THAN_ISOVALUE) { - if (isInputSdf) updateExt = (value <= SdfValueT(0)) ? extVal : acc3->getValue(ijk); - else updateExt = (value >= SdfValueT(0)) ? extVal : acc3->getValue(ijk); + if (isInputSdf) updateExt = math::isNegative(-value) ? acc3->getValue(ijk) : extVal; + else updateExt = math::isNegative(value) ? acc3->getValue(ijk) : extVal; } // SWEEP_LESS_THAN_ISOVALUE acc2->setValue(ijk, updateExt); }//update ext? diff --git a/openvdb/openvdb/tools/Interpolation.h b/openvdb/openvdb/tools/Interpolation.h index b3870c0838..e84dc3a80c 100644 --- a/openvdb/openvdb/tools/Interpolation.h +++ b/openvdb/openvdb/tools/Interpolation.h @@ -41,6 +41,7 @@ #define OPENVDB_TOOLS_INTERPOLATION_HAS_BEEN_INCLUDED #include // for OPENVDB_VERSION_NAME +#include // for ValueToComputeMap #include // for round() #include // for SmoothUnitStep #include // for Transform @@ -314,7 +315,7 @@ class GridSampler /// @brief Sample value in integer index space /// @param i Integer x-coordinate in index space /// @param j Integer y-coordinate in index space - /// @param k Integer x-coordinate in index space + /// @param k Integer z-coordinate in index space ValueType sampleVoxel(typename Coord::ValueType i, typename Coord::ValueType j, typename Coord::ValueType k) const @@ -393,7 +394,7 @@ class GridSampler, SamplerType> /// @brief Sample value in integer index space /// @param i Integer x-coordinate in index space /// @param j Integer y-coordinate in index space - /// @param k Integer x-coordinate in index space + /// @param k Integer z-coordinate in index space ValueType sampleVoxel(typename Coord::ValueType i, typename Coord::ValueType j, typename Coord::ValueType k) const @@ -745,17 +746,31 @@ BoxSampler::sample(const TreeT& inTree, const Vec3R& inCoord, typename TreeT::ValueType& result) { using ValueT = typename TreeT::ValueType; + using ComputeT = typename TreeT::ComputeType; const Vec3i inIdx = local_util::floorVec3(inCoord); const Vec3R uvw = inCoord - inIdx; // Retrieve the values of the eight voxels surrounding the // fractional source coordinates. - ValueT data[2][2][2]; - - const bool hasActiveValues = BoxSampler::probeValues(data, inTree, Coord(inIdx)); + ValueT probeData[2][2][2]; + ComputeT computeData[2][2][2]; + bool hasActiveValues; + + if constexpr(std::is_same_v) { + hasActiveValues = BoxSampler::probeValues(computeData, inTree, Coord(inIdx)); + } else { + hasActiveValues = BoxSampler::probeValues(probeData, inTree, Coord(inIdx)); + for (int dx = 0; dx < 2; ++dx) { + for (int dy = 0; dy < 2; ++dy) { + for (int dz = 0; dz < 2; ++dz) { + computeData[dx][dy][dz] = probeData[dx][dy][dz]; + } + } + } + } - result = BoxSampler::trilinearInterpolation(data, uvw); + result = BoxSampler::trilinearInterpolation(computeData, uvw); return hasActiveValues; } @@ -766,17 +781,18 @@ inline typename TreeT::ValueType BoxSampler::sample(const TreeT& inTree, const Vec3R& inCoord) { using ValueT = typename TreeT::ValueType; + using ComputeT = typename TreeT::ComputeType; const Vec3i inIdx = local_util::floorVec3(inCoord); const Vec3R uvw = inCoord - inIdx; // Retrieve the values of the eight voxels surrounding the // fractional source coordinates. - ValueT data[2][2][2]; + ComputeT data[2][2][2]; BoxSampler::getValues(data, inTree, Coord(inIdx)); - return BoxSampler::trilinearInterpolation(data, uvw); + return ValueT(BoxSampler::trilinearInterpolation(data, uvw)); } @@ -832,6 +848,7 @@ QuadraticSampler::sample(const TreeT& inTree, const Vec3R& inCoord, typename TreeT::ValueType& result) { using ValueT = typename TreeT::ValueType; + using ComputeT = typename TreeT::ComputeType; const Vec3i inIdx = local_util::floorVec3(inCoord), inLoIdx = inIdx - Vec3i(1, 1, 1); const Vec3R uvw = inCoord - inIdx; @@ -840,15 +857,17 @@ QuadraticSampler::sample(const TreeT& inTree, const Vec3R& inCoord, // fractional source coordinates. bool active = false; ValueT data[3][3][3]; + ComputeT computeData[3][3][3]; for (int dx = 0, ix = inLoIdx.x(); dx < 3; ++dx, ++ix) { for (int dy = 0, iy = inLoIdx.y(); dy < 3; ++dy, ++iy) { for (int dz = 0, iz = inLoIdx.z(); dz < 3; ++dz, ++iz) { if (inTree.probeValue(Coord(ix, iy, iz), data[dx][dy][dz])) active = true; + computeData[dx][dy][dz] = data[dx][dy][dz]; } } } - result = QuadraticSampler::triquadraticInterpolation(data, uvw); + result = QuadraticSampler::triquadraticInterpolation(computeData, uvw); return active; } @@ -858,13 +877,14 @@ inline typename TreeT::ValueType QuadraticSampler::sample(const TreeT& inTree, const Vec3R& inCoord) { using ValueT = typename TreeT::ValueType; + using ComputeT = typename TreeT::ComputeType; const Vec3i inIdx = local_util::floorVec3(inCoord), inLoIdx = inIdx - Vec3i(1, 1, 1); const Vec3R uvw = inCoord - inIdx; // Retrieve the values of the 27 voxels surrounding the // fractional source coordinates. - ValueT data[3][3][3]; + ComputeT data[3][3][3]; for (int dx = 0, ix = inLoIdx.x(); dx < 3; ++dx, ++ix) { for (int dy = 0, iy = inLoIdx.y(); dy < 3; ++dy, ++iy) { for (int dz = 0, iz = inLoIdx.z(); dz < 3; ++dz, ++iz) { @@ -873,7 +893,7 @@ QuadraticSampler::sample(const TreeT& inTree, const Vec3R& inCoord) } } - return QuadraticSampler::triquadraticInterpolation(data, uvw); + return ValueT(QuadraticSampler::triquadraticInterpolation(data, uvw)); } diff --git a/openvdb/openvdb/tools/LevelSetAdvect.h b/openvdb/openvdb/tools/LevelSetAdvect.h index 7d4787d09c..fe9105ae12 100644 --- a/openvdb/openvdb/tools/LevelSetAdvect.h +++ b/openvdb/openvdb/tools/LevelSetAdvect.h @@ -80,6 +80,7 @@ class LevelSetAdvection using LeafType = typename TrackerT::LeafType; using BufferType = typename TrackerT::BufferType; using ValueType = typename TrackerT::ValueType; + using ComputeType = typename TrackerT::ComputeType; using VectorType = typename FieldT::VectorType; /// Main constructor @@ -134,7 +135,7 @@ class LevelSetAdvection /// final time, time1. If time0>time1 backward advection is performed. /// /// @return number of CFL iterations used to advect from time0 to time1 - size_t advect(ValueType time0, ValueType time1); + size_t advect(ComputeType time0, ComputeType time1); private: // disallow copy construction and copy by assignment! @@ -154,7 +155,7 @@ class LevelSetAdvection virtual ~Advect() { if (mIsMaster) this->clearField(); } /// Advect the level set from its current time, time0, to its final time, time1. /// @return number of CFL iterations - size_t advect(ValueType time0, ValueType time1); + size_t advect(ComputeType time0, ComputeType time1); /// Used internally by tbb::parallel_for() void operator()(const LeafRange& r) const { @@ -164,13 +165,13 @@ class LevelSetAdvection /// method calling tbb void cook(const char* msg, size_t swapBuffer = 0); /// Sample field and return the CFL time step - typename GridT::ValueType sampleField(ValueType time0, ValueType time1); - template void sample(const LeafRange& r, ValueType t0, ValueType t1); - inline void sampleXformed(const LeafRange& r, ValueType t0, ValueType t1) + typename GridT::ComputeType sampleField(ComputeType time0, ComputeType time1); + template void sample(const LeafRange& r, ComputeType t0, ComputeType t1); + inline void sampleXformed(const LeafRange& r, ComputeType t0, ComputeType t1) { this->sample(r, t0, t1); } - inline void sampleAligned(const LeafRange& r, ValueType t0, ValueType t1) + inline void sampleAligned(const LeafRange& r, ComputeType t0, ComputeType t1) { this->sample(r, t0, t1); } @@ -178,11 +179,11 @@ class LevelSetAdvection // Convex combination of Phi and a forward Euler advection steps: // Phi(result) = alpha * Phi(phi) + (1-alpha) * (Phi(0) - dt * Speed(speed)*|Grad[Phi(0)]|); template - void euler(const LeafRange&, ValueType, Index, Index); - inline void euler01(const LeafRange& r, ValueType t) {this->euler<0,1>(r, t, 0, 1);} - inline void euler12(const LeafRange& r, ValueType t) {this->euler<1,2>(r, t, 1, 1);} - inline void euler34(const LeafRange& r, ValueType t) {this->euler<3,4>(r, t, 1, 2);} - inline void euler13(const LeafRange& r, ValueType t) {this->euler<1,3>(r, t, 1, 2);} + void euler(const LeafRange&, ComputeType, Index, Index); + inline void euler01(const LeafRange& r, ComputeType t) {this->euler<0,1>(r, t, 0, 1);} + inline void euler12(const LeafRange& r, ComputeType t) {this->euler<1,2>(r, t, 1, 1);} + inline void euler34(const LeafRange& r, ComputeType t) {this->euler<3,4>(r, t, 1, 2);} + inline void euler13(const LeafRange& r, ComputeType t) {this->euler<1,3>(r, t, 1, 2);} LevelSetAdvection& mParent; VectorType* mVelocity; @@ -193,16 +194,16 @@ class LevelSetAdvection }; // end of private Advect struct template - size_t advect1(ValueType time0, ValueType time1); + size_t advect1(ComputeType time0, ComputeType time1); template - size_t advect2(ValueType time0, ValueType time1); + size_t advect2(ComputeType time0, ComputeType time1); template - size_t advect3(ValueType time0, ValueType time1); + size_t advect3(ComputeType time0, ComputeType time1); TrackerT mTracker; //each thread needs a deep copy of the field since it might contain a ValueAccessor @@ -215,7 +216,7 @@ class LevelSetAdvection template size_t -LevelSetAdvection::advect(ValueType time0, ValueType time1) +LevelSetAdvection::advect(ComputeType time0, ComputeType time1) { switch (mSpatialScheme) { case math::FIRST_BIAS: @@ -238,7 +239,7 @@ LevelSetAdvection::advect(ValueType time0, ValueType template template size_t -LevelSetAdvection::advect1(ValueType time0, ValueType time1) +LevelSetAdvection::advect1(ComputeType time0, ComputeType time1) { switch (mTemporalScheme) { case math::TVD_RK1: @@ -257,7 +258,7 @@ LevelSetAdvection::advect1(ValueType time0, ValueType template template size_t -LevelSetAdvection::advect2(ValueType time0, ValueType time1) +LevelSetAdvection::advect2(ComputeType time0, ComputeType time1) { const math::Transform& trans = mTracker.grid().transform(); if (trans.mapType() == math::UniformScaleMap::mapType()) { @@ -282,7 +283,7 @@ template< math::TemporalIntegrationScheme TemporalScheme, typename MapT> size_t -LevelSetAdvection::advect3(ValueType time0, ValueType time1) +LevelSetAdvection::advect3(ComputeType time0, ComputeType time1) { Advect tmp(*this); return tmp.advect(time0, time1); @@ -338,7 +339,7 @@ template< inline size_t LevelSetAdvection:: Advect:: -advect(ValueType time0, ValueType time1) +advect(ComputeType time0, ComputeType time1) { namespace ph = std::placeholders; @@ -352,7 +353,7 @@ advect(ValueType time0, ValueType time1) mParent.mTracker.leafs().rebuildAuxBuffers(TemporalScheme == math::TVD_RK3 ? 2 : 1); //timer.stop(); - const ValueType dt = this->sampleField(time0, time1); + const ComputeType dt = this->sampleField(time0, time1); if ( math::isZero(dt) ) break;//V is essentially zero so terminate OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN //switch is resolved at compile-time @@ -423,10 +424,10 @@ template< typename MapT, math::BiasedGradientScheme SpatialScheme, math::TemporalIntegrationScheme TemporalScheme> -inline typename GridT::ValueType +inline typename GridT::ComputeType LevelSetAdvection:: Advect:: -sampleField(ValueType time0, ValueType time1) +sampleField(ComputeType time0, ComputeType time1) { namespace ph = std::placeholders; @@ -448,19 +449,19 @@ sampleField(ValueType time0, ValueType time1) this->cook("Sampling advection field"); // Find the extrema of the magnitude of the velocities - ValueType maxAbsV = 0; + ComputeType maxAbsV = 0; VectorType* v = mVelocity; for (size_t i = 0; i < voxelCount; ++i, ++v) { - maxAbsV = math::Max(maxAbsV, ValueType(v->lengthSqr())); + maxAbsV = math::Max(maxAbsV, ComputeType(v->lengthSqr())); } // Compute the CFL number - if (math::isApproxZero(maxAbsV, math::Delta::value())) return ValueType(0); - static const ValueType CFL = (TemporalScheme == math::TVD_RK1 ? ValueType(0.3) : - TemporalScheme == math::TVD_RK2 ? ValueType(0.9) : - ValueType(1.0))/math::Sqrt(ValueType(3.0)); - const ValueType dt = math::Abs(time1 - time0), dx = mParent.mTracker.voxelSize(); - return math::Min(dt, ValueType(CFL*dx/math::Sqrt(maxAbsV))); + if (math::isApproxZero(maxAbsV, math::Delta::value())) return ComputeType(0); + static const ValueType CFL = (TemporalScheme == math::TVD_RK1 ? ComputeType(0.3) : + TemporalScheme == math::TVD_RK2 ? ComputeType(0.9) : + ComputeType(1.0))/math::Sqrt(ComputeType(3.0)); + const ComputeType dt = math::Abs(time1 - time0), dx = mParent.mTracker.voxelSize(); + return math::Min(dt, ComputeType(CFL*dx/math::Sqrt(maxAbsV))); } @@ -473,7 +474,7 @@ template inline void LevelSetAdvection:: Advect:: -sample(const LeafRange& range, ValueType time0, ValueType time1) +sample(const LeafRange& range, ComputeType time0, ComputeType time1) { const bool isForward = time0 < time1; using VoxelIterT = typename LeafType::ValueOnCIter; @@ -544,15 +545,15 @@ template inline void LevelSetAdvection:: Advect:: -euler(const LeafRange& range, ValueType dt, Index phiBuffer, Index resultBuffer) +euler(const LeafRange& range, ComputeType dt, Index phiBuffer, Index resultBuffer) { using SchemeT = math::BIAS_SCHEME; using StencilT = typename SchemeT::template ISStencil::StencilType; using VoxelIterT = typename LeafType::ValueOnCIter; using GradT = math::GradientBiased; - static const ValueType Alpha = ValueType(Nominator)/ValueType(Denominator); - static const ValueType Beta = ValueType(1) - Alpha; + static const ComputeType Alpha = ComputeType(Nominator)/ComputeType(Denominator); + static const ComputeType Beta = ComputeType(1) - Alpha; mParent.mTracker.checkInterrupter(); const MapT& map = *mMap; @@ -564,9 +565,9 @@ euler(const LeafRange& range, ValueType dt, Index phiBuffer, Index resultBuffer) for (VoxelIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter, ++vel) { const Index i = voxelIter.pos(); stencil.moveTo(voxelIter); - const ValueType a = - stencil.getValue() - dt * vel->dot(GradT::result(map, stencil, *vel)); - result[i] = Nominator ? Alpha * phi[i] + Beta * a : a; + const ComputeType a = + stencil.getValue() - dt * ComputeType(vel->dot(GradT::result(map, stencil, *vel))); + result[i] = Nominator ? ValueType(Alpha * phi[i] + Beta * a) : ValueType(a); }//loop over active voxels in the leaf of the mask }//loop over leafs of the level set } @@ -583,6 +584,7 @@ euler(const LeafRange& range, ValueType dt, Index phiBuffer, Index resultBuffer) #include #endif +OPENVDB_INSTANTIATE_CLASS LevelSetAdvection, util::NullInterrupter>; OPENVDB_INSTANTIATE_CLASS LevelSetAdvection, util::NullInterrupter>; OPENVDB_INSTANTIATE_CLASS LevelSetAdvection, util::NullInterrupter>; diff --git a/openvdb/openvdb/tools/LevelSetFilter.h b/openvdb/openvdb/tools/LevelSetFilter.h index 0ce7dcd6f7..bc1a425815 100644 --- a/openvdb/openvdb/tools/LevelSetFilter.h +++ b/openvdb/openvdb/tools/LevelSetFilter.h @@ -98,11 +98,15 @@ class LevelSetFilter : public LevelSetTracker /// @brief One iteration of filleting on the level set. /// @param mask Optional alpha mask. /// - /// @note This filter rounds off concave edges to create a smoother transition between surfaces - /// Fillets a level set by - /// offsetting at locations with a negative principal curvature, proportional to its magnitude - /// leaves locations with non-negative principal curvatures untouched - /// This filter converges to the convex hull if iterated enough times + /// @note This filter rounds off concave edges to create a smoother + /// transition between surfaces. Fillets a level set by offsetting + /// at locations with a negative principal curvature, proportional + /// to its magnitude leaves locations with non-negative principal + /// curvatures untouched. This filter converges to the convex hull + /// if iterated enough times. + /// + /// @details See also Level Set Methods and Fast Marching Methods + /// by James Sethian, pp. 204. void fillet(const MaskType* mask = nullptr) { Filter f(this, mask); f.fillet(); @@ -409,10 +413,10 @@ LevelSetFilter::Filter::meanCurvatureImpl(const LeafRa } } -/// Fillets a level set by -/// offsetting at locations with a negative principal curvature, proportional to its magnitude -/// leaves locations with non-negative principal curvatures untouched -/// This filter converges to the convex hull if iterated enough times +/// Fillets a level set by offsetting at locations with a negative principal +/// curvature, proportional to its magnitude. Leaves locations with non-negative +/// principal curvatures untouched. This filter converges to the convex hull if +/// iterated enough times. template inline void LevelSetFilter::Filter::filletImpl(const LeafRange& range) diff --git a/openvdb/openvdb/tools/LevelSetMeasure.h b/openvdb/openvdb/tools/LevelSetMeasure.h index 5a31ffe6fe..9e1a50e87a 100644 --- a/openvdb/openvdb/tools/LevelSetMeasure.h +++ b/openvdb/openvdb/tools/LevelSetMeasure.h @@ -105,10 +105,10 @@ class LevelSetMeasure public: using GridType = GridT; using TreeType = typename GridType::TreeType; - using ValueType = typename TreeType::ValueType; + using ComputeType = typename TreeType::ComputeType; using ManagerType = typename tree::LeafManager; - static_assert(std::is_floating_point::value, + static_assert(std::is_floating_point::value, "level set measure is supported only for scalar, floating-point grids"); /// @brief Main constructor from a grid @@ -344,7 +344,7 @@ inline void LevelSetMeasure:: MeasureArea::operator()(const LeafRange& range) const { - using Vec3T = math::Vec3; + using Vec3T = math::Vec3; // computations are performed in index space where dV = 1 mParent->checkInterrupter(); const Real invDx = 1.0/mParent->mDx; @@ -374,12 +374,12 @@ inline void LevelSetMeasure:: MeasureCurvatures::operator()(const LeafRange& range) const { - using Vec3T = math::Vec3; + using Vec3T = math::Vec3; // computations are performed in index space where dV = 1 mParent->checkInterrupter(); const Real dx = mParent->mDx, dx2=dx*dx, invDx = 1.0/dx; const DiracDelta DD(1.5);// dirac delta function is 3 voxel units wide - ValueType mean, gauss; + ComputeType mean, gauss; const size_t leafCount = mParent->mLeafs->leafCount(); for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { Real sumM = 0, sumG = 0;//reduce risk of catastrophic cancellation @@ -408,7 +408,7 @@ MeasureCurvatures::operator()(const LeafRange& range) const template inline -typename std::enable_if::value, Real>::type +typename std::enable_if::value, Real>::type doLevelSetArea(const GridT& grid, bool useWorldUnits) { LevelSetMeasure m(grid); @@ -417,7 +417,7 @@ doLevelSetArea(const GridT& grid, bool useWorldUnits) template inline -typename std::enable_if::value, Real>::type +typename std::enable_if::value, Real>::type doLevelSetArea(const GridT&, bool) { OPENVDB_THROW(TypeError, @@ -441,7 +441,7 @@ levelSetArea(const GridT& grid, bool useWorldUnits) template inline -typename std::enable_if::value, Real>::type +typename std::enable_if::value, Real>::type doLevelSetVolume(const GridT& grid, bool useWorldUnits) { LevelSetMeasure m(grid); @@ -450,7 +450,7 @@ doLevelSetVolume(const GridT& grid, bool useWorldUnits) template inline -typename std::enable_if::value, Real>::type +typename std::enable_if::value, Real>::type doLevelSetVolume(const GridT&, bool) { OPENVDB_THROW(TypeError, @@ -474,7 +474,7 @@ levelSetVolume(const GridT& grid, bool useWorldUnits) template inline -typename std::enable_if::value, int>::type +typename std::enable_if::value, int>::type doLevelSetEulerCharacteristic(const GridT& grid) { LevelSetMeasure m(grid); @@ -483,7 +483,7 @@ doLevelSetEulerCharacteristic(const GridT& grid) template inline -typename std::enable_if::value, int>::type +typename std::enable_if::value, int>::type doLevelSetEulerCharacteristic(const GridT&) { OPENVDB_THROW(TypeError, @@ -508,7 +508,7 @@ levelSetEulerCharacteristic(const GridT& grid) template inline -typename std::enable_if::value, int>::type +typename std::enable_if::value, int>::type doLevelSetEuler(const GridT& grid) { LevelSetMeasure m(grid); @@ -518,7 +518,7 @@ doLevelSetEuler(const GridT& grid) template inline -typename std::enable_if::value, int>::type +typename std::enable_if::value, int>::type doLevelSetGenus(const GridT& grid) { LevelSetMeasure m(grid); @@ -527,7 +527,7 @@ doLevelSetGenus(const GridT& grid) template inline -typename std::enable_if::value, int>::type +typename std::enable_if::value, int>::type doLevelSetGenus(const GridT&) { OPENVDB_THROW(TypeError, @@ -576,6 +576,7 @@ OPENVDB_REAL_TREE_INSTANTIATE(_FUNCTION) OPENVDB_REAL_TREE_INSTANTIATE(_FUNCTION) #undef _FUNCTION +OPENVDB_INSTANTIATE_CLASS LevelSetMeasure; OPENVDB_INSTANTIATE_CLASS LevelSetMeasure; OPENVDB_INSTANTIATE_CLASS LevelSetMeasure; diff --git a/openvdb/openvdb/tools/LevelSetMorph.h b/openvdb/openvdb/tools/LevelSetMorph.h index e455d07d25..1b4006e1e4 100644 --- a/openvdb/openvdb/tools/LevelSetMorph.h +++ b/openvdb/openvdb/tools/LevelSetMorph.h @@ -54,6 +54,7 @@ class LevelSetMorphing using LeafType = typename TrackerT::LeafType; using BufferType = typename TrackerT::BufferType; using ValueType = typename TrackerT::ValueType; + using ComputeType = typename TrackerT::ComputeType; /// Main constructor LevelSetMorphing(GridT& sourceGrid, const GridT& targetGrid, InterruptT* interrupt = nullptr) @@ -119,11 +120,11 @@ class LevelSetMorphing /// @brief Return the minimum value of the mask to be used for the /// derivation of a smooth alpha value. - ValueType minMask() const { return mMinMask; } + ComputeType minMask() const { return mMinMask; } /// @brief Return the maximum value of the mask to be used for the /// derivation of a smooth alpha value. - ValueType maxMask() const { return mDeltaMask + mMinMask; } + ComputeType maxMask() const { return mDeltaMask + mMinMask; } /// @brief Define the range for the (optional) scalar mask. /// @param min Minimum value of the range. @@ -132,7 +133,7 @@ class LevelSetMorphing /// respectfully zero and one, and values inside the range maps /// smoothly to 0->1 (unless of course the mask is inverted). /// @throw ValueError if @a min is not smaller than @a max. - void setMaskRange(ValueType min, ValueType max) + void setMaskRange(ComputeType min, ComputeType max) { if (!(min < max)) OPENVDB_THROW(ValueError, "Invalid mask range (expects min < max)"); mMinMask = min; @@ -150,7 +151,7 @@ class LevelSetMorphing /// final time, @a time1. If @a time0 > @a time1, perform backward advection. /// /// @return the number of CFL iterations used to advect from @a time0 to @a time1 - size_t advect(ValueType time0, ValueType time1); + size_t advect(ComputeType time0, ComputeType time1); private: @@ -159,22 +160,22 @@ class LevelSetMorphing LevelSetMorphing& operator=(const LevelSetMorphing&);// not implemented template - size_t advect1(ValueType time0, ValueType time1); + size_t advect1(ComputeType time0, ComputeType time1); template - size_t advect2(ValueType time0, ValueType time1); + size_t advect2(ComputeType time0, ComputeType time1); template - size_t advect3(ValueType time0, ValueType time1); + size_t advect3(ComputeType time0, ComputeType time1); TrackerT mTracker; const GridT *mTarget, *mMask; math::BiasedGradientScheme mSpatialScheme; math::TemporalIntegrationScheme mTemporalScheme; - ValueType mMinMask, mDeltaMask; + ComputeType mMinMask, mDeltaMask; bool mInvertMask; // This templated private class implements all the level set magic. @@ -192,7 +193,7 @@ class LevelSetMorphing virtual ~Morph() {} /// Advect the level set from its current time, time0, to its final time, time1. /// @return number of CFL iterations - size_t advect(ValueType time0, ValueType time1); + size_t advect(ComputeType time0, ComputeType time1); /// Used internally by tbb::parallel_for() void operator()(const LeafRange& r) const { @@ -214,22 +215,23 @@ class LevelSetMorphing void cook(ThreadingMode mode, size_t swapBuffer = 0); /// Sample field and return the CFT time step - typename GridT::ValueType sampleSpeed(ValueType time0, ValueType time1, Index speedBuffer); + typename GridT::ComputeType sampleSpeed(ComputeType time0, + ComputeType time1, Index speedBuffer); void sampleXformedSpeed(const LeafRange& r, Index speedBuffer); void sampleAlignedSpeed(const LeafRange& r, Index speedBuffer); // Convex combination of Phi and a forward Euler advection steps: // Phi(result) = alpha * Phi(phi) + (1-alpha) * (Phi(0) - dt * Speed(speed)*|Grad[Phi(0)]|); template - void euler(const LeafRange&, ValueType, Index, Index, Index); - inline void euler01(const LeafRange& r, ValueType t, Index s) {this->euler<0,1>(r,t,0,1,s);} - inline void euler12(const LeafRange& r, ValueType t) {this->euler<1,2>(r, t, 1, 1, 2);} - inline void euler34(const LeafRange& r, ValueType t) {this->euler<3,4>(r, t, 1, 2, 3);} - inline void euler13(const LeafRange& r, ValueType t) {this->euler<1,3>(r, t, 1, 2, 3);} + void euler(const LeafRange&, ComputeType, Index, Index, Index); + inline void euler01(const LeafRange& r, ComputeType t, Index s) {this->euler<0,1>(r,t,0,1,s);} + inline void euler12(const LeafRange& r, ComputeType t) {this->euler<1,2>(r, t, 1, 1, 2);} + inline void euler34(const LeafRange& r, ComputeType t) {this->euler<3,4>(r, t, 1, 2, 3);} + inline void euler13(const LeafRange& r, ComputeType t) {this->euler<1,3>(r, t, 1, 2, 3);} using FuncType = typename std::function; LevelSetMorphing* mParent; - ValueType mMinAbsS, mMaxAbsS; + ComputeType mMinAbsS, mMaxAbsS; const MapT* mMap; FuncType mTask; }; // end of private Morph struct @@ -238,7 +240,7 @@ class LevelSetMorphing template inline size_t -LevelSetMorphing::advect(ValueType time0, ValueType time1) +LevelSetMorphing::advect(ComputeType time0, ComputeType time1) { switch (mSpatialScheme) { case math::FIRST_BIAS: @@ -264,7 +266,7 @@ LevelSetMorphing::advect(ValueType time0, ValueType time1) template template inline size_t -LevelSetMorphing::advect1(ValueType time0, ValueType time1) +LevelSetMorphing::advect1(ComputeType time0, ComputeType time1) { switch (mTemporalScheme) { case math::TVD_RK1: @@ -284,7 +286,7 @@ template template inline size_t -LevelSetMorphing::advect2(ValueType time0, ValueType time1) +LevelSetMorphing::advect2(ComputeType time0, ComputeType time1) { const math::Transform& trans = mTracker.grid().transform(); if (trans.mapType() == math::UniformScaleMap::mapType()) { @@ -307,7 +309,7 @@ template inline size_t -LevelSetMorphing::advect3(ValueType time0, ValueType time1) +LevelSetMorphing::advect3(ComputeType time0, ComputeType time1) { Morph tmp(*this); return tmp.advect(time0, time1); @@ -324,7 +326,7 @@ LevelSetMorphing:: Morph:: Morph(LevelSetMorphing& parent) : mParent(&parent) - , mMinAbsS(ValueType(1e-6)) + , mMinAbsS(ComputeType(1e-6)) , mMap(parent.mTracker.grid().transform().template constMap().get()) , mTask(nullptr) { @@ -366,7 +368,7 @@ template :: Morph:: -advect(ValueType time0, ValueType time1) +advect(ComputeType time0, ComputeType time1) { namespace ph = std::placeholders; @@ -377,7 +379,7 @@ advect(ValueType time0, ValueType time1) while (time0 < time1 && mParent->mTracker.checkInterrupter()) { mParent->mTracker.leafs().rebuildAuxBuffers(auxBuffers); - const ValueType dt = this->sampleSpeed(time0, time1, auxBuffers); + const typename GridT::ComputeType dt = this->sampleSpeed(time0, time1, auxBuffers); if ( math::isZero(dt) ) break;//V is essentially zero so terminate OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN //switch is resolved at compile-time @@ -447,16 +449,16 @@ advect(ValueType time0, ValueType time1) template template -inline typename GridT::ValueType +inline typename GridT::ComputeType LevelSetMorphing:: Morph:: -sampleSpeed(ValueType time0, ValueType time1, Index speedBuffer) +sampleSpeed(ComputeType time0, ComputeType time1, Index speedBuffer) { namespace ph = std::placeholders; mMaxAbsS = mMinAbsS; const size_t leafCount = mParent->mTracker.leafs().leafCount(); - if (leafCount==0 || time0 >= time1) return ValueType(0); + if (leafCount==0 || time0 >= time1) return ComputeType(0); const math::Transform& xform = mParent->mTracker.grid().transform(); if (mParent->mTarget->transform() == xform && @@ -466,12 +468,12 @@ sampleSpeed(ValueType time0, ValueType time1, Index speedBuffer) mTask = std::bind(&Morph::sampleXformedSpeed, ph::_1, ph::_2, speedBuffer); } this->cook(PARALLEL_REDUCE); - if (math::isApproxEqual(mMinAbsS, mMaxAbsS)) return ValueType(0);//speed is essentially zero - static const ValueType CFL = (TemporalScheme == math::TVD_RK1 ? ValueType(0.3) : - TemporalScheme == math::TVD_RK2 ? ValueType(0.9) : - ValueType(1.0))/math::Sqrt(ValueType(3.0)); - const ValueType dt = math::Abs(time1 - time0), dx = mParent->mTracker.voxelSize(); - return math::Min(dt, ValueType(CFL*dx/mMaxAbsS)); + if (math::isApproxEqual(mMinAbsS, mMaxAbsS)) return ComputeType(0);//speed is essentially zero + static const ComputeType CFL = (TemporalScheme == math::TVD_RK1 ? ComputeType(0.3) : + TemporalScheme == math::TVD_RK2 ? ComputeType(0.9) : + ComputeType(1.0))/math::Sqrt(ComputeType(3.0)); + const ComputeType dt = math::Abs(time1 - time0), dx = mParent->mTracker.voxelSize(); + return math::Min(dt, ComputeType(CFL*dx/mMaxAbsS)); } template @@ -498,12 +500,12 @@ sampleXformedSpeed(const LeafRange& range, Index speedBuffer) ValueType& s = speed[voxelIter.pos()]; s -= target.wsSample(map.applyMap(voxelIter.getCoord().asVec3d())); if (!math::isApproxZero(s)) isZero = false; - mMaxAbsS = math::Max(mMaxAbsS, math::Abs(s)); + mMaxAbsS = math::Max(mMaxAbsS, ComputeType(math::Abs(s))); } if (isZero) speed[0] = std::numeric_limits::max();//tag first voxel } } else { - const ValueType min = mParent->mMinMask, invNorm = 1.0f/(mParent->mDeltaMask); + const ComputeType min = mParent->mMinMask, invNorm = 1.0f/(mParent->mDeltaMask); const bool invMask = mParent->isMaskInverted(); typename GridT::ConstAccessor maskAcc = mParent->mMask->getAccessor(); SamplerT mask(maskAcc, mParent->mMask->transform()); @@ -512,12 +514,12 @@ sampleXformedSpeed(const LeafRange& range, Index speedBuffer) bool isZero = true; for (VoxelIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) { const Vec3R xyz = map.applyMap(voxelIter.getCoord().asVec3d());//world space - const ValueType a = math::SmoothUnitStep((mask.wsSample(xyz)-min)*invNorm); + const ComputeType a = math::SmoothUnitStep((mask.wsSample(xyz)-min)*invNorm); ValueType& s = speed[voxelIter.pos()]; s -= target.wsSample(xyz); - s *= invMask ? 1 - a : a; + s *= invMask ? ValueType(1 - a) : ValueType(a); if (!math::isApproxZero(s)) isZero = false; - mMaxAbsS = math::Max(mMaxAbsS, math::Abs(s)); + mMaxAbsS = math::Max(mMaxAbsS, ComputeType(math::Abs(s))); } if (isZero) speed[0] = std::numeric_limits::max();//tag first voxel } @@ -546,12 +548,12 @@ sampleAlignedSpeed(const LeafRange& range, Index speedBuffer) ValueType& s = speed[voxelIter.pos()]; s -= target.getValue(voxelIter.getCoord()); if (!math::isApproxZero(s)) isZero = false; - mMaxAbsS = math::Max(mMaxAbsS, math::Abs(s)); + mMaxAbsS = math::Max(mMaxAbsS, ComputeType(math::Abs(s))); } if (isZero) speed[0] = std::numeric_limits::max();//tag first voxel } } else { - const ValueType min = mParent->mMinMask, invNorm = 1.0f/(mParent->mDeltaMask); + const ComputeType min = mParent->mMinMask, invNorm = 1.0f/(mParent->mDeltaMask); const bool invMask = mParent->isMaskInverted(); typename GridT::ConstAccessor mask = mParent->mMask->getAccessor(); for (typename LeafRange::Iterator leafIter = range.begin(); leafIter; ++leafIter) { @@ -559,12 +561,12 @@ sampleAlignedSpeed(const LeafRange& range, Index speedBuffer) bool isZero = true; for (VoxelIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) { const Coord ijk = voxelIter.getCoord();//index space - const ValueType a = math::SmoothUnitStep((mask.getValue(ijk)-min)*invNorm); + const ComputeType a = math::SmoothUnitStep((mask.getValue(ijk)-min)*invNorm); ValueType& s = speed[voxelIter.pos()]; s -= target.getValue(ijk); - s *= invMask ? 1 - a : a; + s *= invMask ? ValueType(1 - a) : ValueType(a); if (!math::isApproxZero(s)) isZero = false; - mMaxAbsS = math::Max(mMaxAbsS, math::Abs(s)); + mMaxAbsS = math::Max(mMaxAbsS, ComputeType(math::Abs(s))); } if (isZero) speed[0] = std::numeric_limits::max();//tag first voxel } @@ -607,7 +609,7 @@ template inline void LevelSetMorphing:: Morph:: -euler(const LeafRange& range, ValueType dt, +euler(const LeafRange& range, ComputeType dt, Index phiBuffer, Index resultBuffer, Index speedBuffer) { using SchemeT = math::BIAS_SCHEME; @@ -615,8 +617,8 @@ euler(const LeafRange& range, ValueType dt, using VoxelIterT = typename LeafType::ValueOnCIter; using NumGrad = math::GradientNormSqrd; - static const ValueType Alpha = ValueType(Nominator)/ValueType(Denominator); - static const ValueType Beta = ValueType(1) - Alpha; + static const ComputeType Alpha = ComputeType(Nominator)/ComputeType(Denominator); + static const ComputeType Beta = ComputeType(1) - Alpha; mParent->mTracker.checkInterrupter(); const MapT& map = *mMap; @@ -631,8 +633,9 @@ euler(const LeafRange& range, ValueType dt, const Index n = voxelIter.pos(); if (math::isApproxZero(speed[n])) continue; stencil.moveTo(voxelIter); - const ValueType v = stencil.getValue() - dt * speed[n] * NumGrad::result(map, stencil); - result[n] = Nominator ? Alpha * phi[n] + Beta * v : v; + const ComputeType v = ComputeType(stencil.getValue()) - + dt * ComputeType(speed[n]) * ComputeType(NumGrad::result(map, stencil)); + result[n] = Nominator ? ValueType(Alpha * phi[n] + Beta * v) : ValueType(v); }//loop over active voxels in the leaf of the mask }//loop over leafs of the level set } @@ -649,6 +652,7 @@ euler(const LeafRange& range, ValueType dt, #include #endif +OPENVDB_INSTANTIATE_CLASS LevelSetMorphing; OPENVDB_INSTANTIATE_CLASS LevelSetMorphing; OPENVDB_INSTANTIATE_CLASS LevelSetMorphing; diff --git a/openvdb/openvdb/tools/LevelSetSphere.h b/openvdb/openvdb/tools/LevelSetSphere.h index 83dac13496..5ff6f496f7 100644 --- a/openvdb/openvdb/tools/LevelSetSphere.h +++ b/openvdb/openvdb/tools/LevelSetSphere.h @@ -90,8 +90,9 @@ class LevelSetSphere public: using TreeT = typename GridT::TreeType; using ValueT = typename GridT::ValueType; - using Vec3T = typename math::Vec3; - static_assert(std::is_floating_point::value, + using ComputeT = typename GridT::ComputeType; + using Vec3T = typename math::Vec3; + static_assert(std::is_floating_point::value, "level set grids must have scalar, floating-point value types"); /// @brief Constructor @@ -104,7 +105,7 @@ class LevelSetSphere /// @note If the radius of the sphere is smaller than /// 1.5*voxelSize, i.e. the sphere is smaller than the Nyquist /// frequency of the grid, it is ignored! - LevelSetSphere(ValueT radius, const Vec3T ¢er, InterruptT* interrupt = nullptr) + LevelSetSphere(ComputeT radius, const Vec3T ¢er, InterruptT* interrupt = nullptr) : mRadius(radius), mCenter(center), mInterrupt(interrupt) { if (mRadius<=0) OPENVDB_THROW(ValueError, "radius must be positive"); @@ -115,7 +116,7 @@ class LevelSetSphere /// @param voxelSize Size of voxels in world units /// @param halfWidth Half-width of narrow-band in voxel units /// @param threaded If true multi-threading is enabled (true by default) - typename GridT::Ptr getLevelSet(ValueT voxelSize, ValueT halfWidth, bool threaded = true) + typename GridT::Ptr getLevelSet(ComputeT voxelSize, ComputeT halfWidth, bool threaded = true) { mGrid = createLevelSet(voxelSize, halfWidth); this->rasterSphere(voxelSize, halfWidth, threaded); @@ -124,7 +125,7 @@ class LevelSetSphere } private: - void rasterSphere(ValueT dx, ValueT w, bool threaded) + void rasterSphere(ComputeT dx, ComputeT w, bool threaded) { if (!(dx>0.0f)) OPENVDB_THROW(ValueError, "voxel size must be positive"); if (!(w>1)) OPENVDB_THROW(ValueError, "half-width must be larger than one"); @@ -158,16 +159,16 @@ class LevelSetSphere // Compute signed distances to sphere using leapfrogging in k for (i = r.begin(); i != r.end(); ++i) { if (util::wasInterrupted(mInterrupt)) return; - const auto x2 = math::Pow2(ValueT(i) - c[0]); + const auto x2 = math::Pow2(ComputeT(i) - c[0]); for (j = jmin; j <= jmax; ++j) { - const auto x2y2 = math::Pow2(ValueT(j) - c[1]) + x2; + const auto x2y2 = math::Pow2(ComputeT(j) - c[1]) + x2; for (k = kmin; k <= kmax; k += m) { m = 1; // Distance in voxel units to sphere - const auto v = math::Sqrt(x2y2 + math::Pow2(ValueT(k)-c[2]))-r0; + const auto v = math::Sqrt(x2y2 + math::Pow2(ComputeT(k)-c[2]))-r0; const auto d = math::Abs(v); if (d < w) { // inside narrow band - acc.setValue(ijk, dx*v);// distance in world units + acc.setValue(ijk, ValueT(dx*v));// distance in world units } else { // outside narrow band m += math::Floor(d-w);// leapfrog } @@ -221,12 +222,12 @@ createLevelSetSphere(float radius, const openvdb::Vec3f& center, float voxelSize float halfWidth, InterruptT* interrupt, bool threaded) { // GridType::ValueType is required to be a floating-point scalar. - static_assert(std::is_floating_point::value, + static_assert(std::is_floating_point::value, "level set grids must have scalar, floating-point value types"); - using ValueT = typename GridType::ValueType; - LevelSetSphere factory(ValueT(radius), center, interrupt); - return factory.getLevelSet(ValueT(voxelSize), ValueT(halfWidth), threaded); + using ComputeT = typename GridType::ComputeType; + LevelSetSphere factory(ComputeT(radius), center, interrupt); + return factory.getLevelSet(ComputeT(voxelSize), ComputeT(halfWidth), threaded); } diff --git a/openvdb/openvdb/tools/LevelSetTracker.h b/openvdb/openvdb/tools/LevelSetTracker.h index d171cabf95..8cdec9373d 100644 --- a/openvdb/openvdb/tools/LevelSetTracker.h +++ b/openvdb/openvdb/tools/LevelSetTracker.h @@ -63,11 +63,12 @@ class LevelSetTracker using TreeType = typename GridT::TreeType; using LeafType = typename TreeType::LeafNodeType; using ValueType = typename TreeType::ValueType; + using ComputeType = typename TreeType::ComputeType; using LeafManagerType = typename tree::LeafManager; // leafs + buffers using LeafRange = typename LeafManagerType::LeafRange; using BufferType = typename LeafManagerType::BufferType; using MaskTreeType = typename TreeType::template ValueConverter::Type; - static_assert(std::is_floating_point::value, + static_assert(std::is_floating_point::value, "LevelSetTracker requires a level set grid with floating-point values"); /// Lightweight struct that stores the state of the LevelSetTracker @@ -132,7 +133,7 @@ class LevelSetTracker bool resize(Index halfWidth = static_cast(LEVEL_SET_HALF_WIDTH)); /// @brief Return the half width of the narrow band in floating-point voxel units. - ValueType getHalfWidth() const { return mGrid->background()/mDx; } + ValueType getHalfWidth() const { return ComputeType(mGrid->background())/mDx; } /// @brief Return the state of the tracker (see struct defined above) State getState() const { return mState; } @@ -232,7 +233,7 @@ class LevelSetTracker void eval(StencilT& stencil, const ValueType* phi, ValueType* result, Index n) const; LevelSetTracker& mTracker; const MaskT* mMask; - const ValueType mDt, mInvDx; + const ComputeType mDt, mInvDx; typename std::function mTask; }; // Normalizer struct @@ -247,12 +248,12 @@ class LevelSetTracker // a list of the current LeafNodes! The auxiliary buffers on the // other hand always have to be allocated locally, since some // methods need them and others don't! - GridType* mGrid; - LeafManagerType* mLeafs; - InterruptT* mInterrupter; - const ValueType mDx; - State mState; - TrimMode mTrimMode = TrimMode::kAll; + GridType* mGrid; + LeafManagerType* mLeafs; + InterruptT* mInterrupter; + const ComputeType mDx; + State mState; + TrimMode mTrimMode = TrimMode::kAll; }; // end of LevelSetTracker class template @@ -261,7 +262,7 @@ LevelSetTracker(GridT& grid, InterruptT* interrupt): mGrid(&grid), mLeafs(new LeafManagerType(grid.tree())), mInterrupter(interrupt), - mDx(static_cast(grid.voxelSize()[0])), + mDx(static_cast(grid.voxelSize()[0])), mState() { if ( !grid.hasUniformVoxels() ) { @@ -328,7 +329,8 @@ dilate(int iterations) for (int i=0; i < iterations; ++i) { MaskTreeType mask0(mGrid->tree(), false, TopologyCopy()); tools::dilateActiveValues( *mLeafs, 1, tools::NN_FACE, tools::IGNORE_TILES); - tools::changeLevelSetBackground(this->leafs(), mDx + mGrid->background()); + tools::changeLevelSetBackground(this->leafs(), + ValueType(mDx + ComputeType(mGrid->background()))); MaskTreeType mask(mGrid->tree(), false, TopologyCopy()); mask.topologyDifference(mask0); this->normalize(&mask); @@ -344,7 +346,7 @@ erode(int iterations) tools::erodeActiveValues(*mLeafs, iterations, tools::NN_FACE, tools::IGNORE_TILES); tools::pruneLevelSet(mLeafs->tree()); mLeafs->rebuildLeafArray(); - const ValueType background = mGrid->background() - ValueType(iterations) * mDx; + const ValueType background = ComputeType(mGrid->background()) - ComputeType(iterations) * mDx; tools::changeLevelSetBackground(this->leafs(), background); } @@ -518,9 +520,10 @@ Normalizer:: Normalizer(LevelSetTracker& tracker, const MaskT* mask) : mTracker(tracker) , mMask(mask) - , mDt(tracker.voxelSize()*(TemporalScheme == math::TVD_RK1 ? 0.3f : + , mDt(ComputeType(tracker.grid().voxelSize()[0]) * + (TemporalScheme == math::TVD_RK1 ? 0.3f : TemporalScheme == math::TVD_RK2 ? 0.9f : 1.0f)) - , mInvDx(1.0f/tracker.voxelSize()) + , mInvDx(1.0f/ComputeType(tracker.grid().voxelSize()[0])) , mTask(nullptr) { } @@ -631,15 +634,15 @@ Normalizer:: eval(StencilT& stencil, const ValueType* phi, ValueType* result, Index n) const { using GradientT = typename math::ISGradientNormSqrd; - static const ValueType alpha = ValueType(Nominator)/ValueType(Denominator); - static const ValueType beta = ValueType(1) - alpha; - - const ValueType normSqGradPhi = GradientT::result(stencil); - const ValueType phi0 = stencil.getValue(); - ValueType v = phi0 / ( math::Sqrt(math::Pow2(phi0) + normSqGradPhi) + - math::Tolerance::value() ); - v = phi0 - mDt * v * (math::Sqrt(normSqGradPhi) * mInvDx - 1.0f); - result[n] = Nominator ? alpha * phi[n] + beta * v : v; + static const ComputeType alpha = ComputeType(Nominator)/ComputeType(Denominator); + static const ComputeType beta = ComputeType(1) - alpha; + + const ComputeType normSqGradPhi = GradientT::result(stencil); + const ComputeType phi0 = stencil.getValue(); + ComputeType v = phi0 / ( math::Sqrt(math::Pow2(phi0) + normSqGradPhi) + + math::Tolerance::value() ); + v = phi0 - mDt * v * (math::Sqrt(normSqGradPhi) * mInvDx - ComputeType(1)); + result[n] = Nominator ? ComputeType(alpha * ComputeType(phi[n]) + beta * v) : v; } template @@ -687,6 +690,7 @@ euler(const LeafRange& range, Index phiBuffer, Index resultBuffer) #include #endif +OPENVDB_INSTANTIATE_CLASS LevelSetTracker; OPENVDB_INSTANTIATE_CLASS LevelSetTracker; OPENVDB_INSTANTIATE_CLASS LevelSetTracker; diff --git a/openvdb/openvdb/tools/LevelSetUtil.h b/openvdb/openvdb/tools/LevelSetUtil.h index 012da732ec..17b5c82283 100644 --- a/openvdb/openvdb/tools/LevelSetUtil.h +++ b/openvdb/openvdb/tools/LevelSetUtil.h @@ -461,7 +461,8 @@ struct SDFVoxelsToFogVolume { ValueType* values = node.buffer().data(); for (Index i = 0; i < LeafNodeType::SIZE; ++i) { - values[i] = values[i] > ValueType(0.0) ? ValueType(0.0) : values[i] * mWeight; + values[i] = values[i] > ValueType(0.0) ? + ValueType(0.0) : ValueType(values[i] * mWeight); if (values[i] > ValueType(0.0)) node.setValueOn(i); } diff --git a/openvdb/openvdb/tools/MeshToVolume.h b/openvdb/openvdb/tools/MeshToVolume.h index 2d4d3fafad..dcd656ec09 100644 --- a/openvdb/openvdb/tools/MeshToVolume.h +++ b/openvdb/openvdb/tools/MeshToVolume.h @@ -16,9 +16,10 @@ #ifndef OPENVDB_TOOLS_MESH_TO_VOLUME_HAS_BEEN_INCLUDED #define OPENVDB_TOOLS_MESH_TO_VOLUME_HAS_BEEN_INCLUDED -#include // for OPENVDB_HAS_CXX11 +#include #include #include // for GodunovsNormSqrd +#include // for isFinite(), isNan() #include // for closestPointOnTriangleToPoint #include #include @@ -39,7 +40,6 @@ #include #include // for std::sort() -#include // for std::isfinite(), std::isnan() #include #include #include @@ -3348,7 +3348,7 @@ meshToVolume( // Note: inf interior width is all right, this value makes the converter fill // interior regions with distance values. - if (!std::isfinite(exteriorWidth) || std::isnan(interiorWidth)) { + if (!math::isFinite(exteriorWidth) || math::isNan(interiorWidth)) { std::stringstream msg; msg << "Illegal narrow band width: exterior = " << exteriorWidth << ", interior = " << interiorWidth; @@ -3358,7 +3358,7 @@ meshToVolume( const ValueType voxelSize = ValueType(transform.voxelSize()[0]); - if (!std::isfinite(voxelSize) || math::isZero(voxelSize)) { + if (!math::isFinite(voxelSize) || math::isZero(voxelSize)) { std::stringstream msg; msg << "Illegal transform, voxel size = " << voxelSize; OPENVDB_LOG_DEBUG(msg.str()); diff --git a/openvdb/openvdb/tools/RayIntersector.h b/openvdb/openvdb/tools/RayIntersector.h index c97942ad82..b60cb323d6 100644 --- a/openvdb/openvdb/tools/RayIntersector.h +++ b/openvdb/openvdb/tools/RayIntersector.h @@ -88,6 +88,7 @@ class LevelSetRayIntersector using RealType = typename RayT::RealType; using Vec3Type = typename RayT::Vec3T; using ValueT = typename GridT::ValueType; + using ComputeT = typename GridT::ComputeType; using TreeT = typename GridT::TreeType; static_assert(NodeLevel >= -1 && NodeLevel < int(TreeT::DEPTH)-1, "NodeLevel out of range"); @@ -97,7 +98,7 @@ class LevelSetRayIntersector /// @brief Constructor /// @param grid level set grid to intersect rays against. /// @param isoValue optional iso-value for the ray-intersection. - LevelSetRayIntersector(const GridT& grid, const ValueT& isoValue = zeroVal()) + LevelSetRayIntersector(const GridT& grid, const ComputeT& isoValue = zeroVal()) : mTester(grid, isoValue) { if (!grid.hasUniformVoxels() ) { @@ -112,7 +113,7 @@ class LevelSetRayIntersector } /// @brief Return the iso-value used for ray-intersections - const ValueT& getIsoValue() const { return mTester.getIsoValue(); } + const ComputeT& getIsoValue() const { return mTester.getIsoValue(); } /// @brief Return @c true if the index-space ray intersects the level set. /// @param iRay ray represented in index space. @@ -516,17 +517,18 @@ class LinearSearchImpl using RayT = math::Ray; using VecT = math::Vec3; using ValueT = typename GridT::ValueType; + using ComputeT = typename GridT::ComputeType; using AccessorT = typename GridT::ConstAccessor; using StencilT = math::BoxStencil; /// @brief Constructor from a grid. /// @throw RunTimeError if the grid is empty. /// @throw ValueError if the isoValue is not inside the narrow-band. - LinearSearchImpl(const GridT& grid, const ValueT& isoValue = zeroVal()) + LinearSearchImpl(const GridT& grid, const ComputeT& isoValue = zeroVal()) : mStencil(grid), mIsoValue(isoValue), - mMinValue(isoValue - ValueT(2 * grid.voxelSize()[0])), - mMaxValue(isoValue + ValueT(2 * grid.voxelSize()[0])) + mMinValue(isoValue - ComputeT(2 * grid.voxelSize()[0])), + mMaxValue(isoValue + ComputeT(2 * grid.voxelSize()[0])) { if ( grid.empty() ) { OPENVDB_THROW(RuntimeError, "LinearSearchImpl does not supports empty grids"); @@ -539,7 +541,7 @@ class LinearSearchImpl } /// @brief Return the iso-value used for ray-intersections - const ValueT& getIsoValue() const { return mIsoValue; } + const ComputeT& getIsoValue() const { return mIsoValue; } /// @brief Return @c false if the ray misses the bbox of the grid. /// @param iRay Ray represented in index space. @@ -595,7 +597,7 @@ class LinearSearchImpl inline void init(RealT t0) { mT[0] = t0; - mV[0] = static_cast(this->interpValue(t0)); + mV[0] = static_cast(this->interpValue(t0)); } inline void setRange(RealT t0, RealT t1) { mRay.setTimes(t0, t1); } @@ -617,26 +619,28 @@ class LinearSearchImpl /// call getIndexPos, getWorldPos and getWorldPosAndNml! inline bool operator()(const Coord& ijk, RealT time) { - ValueT V; - if (mStencil.accessor().probeValue(ijk, V) &&//within narrow band - V>mMinValue && V(this->interpValue(time)); - if (math::ZeroCrossing(mV[0], mV[1])) { - mTime = this->interpTime(); - OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN - for (int n=0; Iterations>0 && n(this->interpValue(mTime)); - const int m = math::ZeroCrossing(mV[0], V) ? 1 : 0; - mV[m] = V; - mT[m] = mTime; + ValueT V_raw; + if (mStencil.accessor().probeValue(ijk, V_raw)) {//within narrow band + ComputeT V = V_raw; + if (V>mMinValue && V(this->interpValue(time)); + if (math::ZeroCrossing(mV[0], mV[1])) { mTime = this->interpTime(); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + for (int n=0; Iterations>0 && n(this->interpValue(mTime)); + const int m = math::ZeroCrossing(mV[0], V) ? 1 : 0; + mV[m] = V; + mT[m] = mTime; + mTime = this->interpTime(); + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + return true; } - OPENVDB_NO_UNREACHABLE_CODE_WARNING_END - return true; + mT[0] = mT[1]; + mV[0] = mV[1]; } - mT[0] = mT[1]; - mV[0] = mV[1]; } return false; } @@ -659,9 +663,9 @@ class LinearSearchImpl RayT mRay; StencilT mStencil; RealT mTime;//time of intersection - ValueT mV[2]; + ComputeT mV[2]; RealT mT[2]; - const ValueT mIsoValue, mMinValue, mMaxValue; + const ComputeT mIsoValue, mMinValue, mMaxValue; math::CoordBBox mBBox; };// LinearSearchImpl diff --git a/openvdb/openvdb/tools/RayTracer.h b/openvdb/openvdb/tools/RayTracer.h index fc11b34161..486e626f3a 100644 --- a/openvdb/openvdb/tools/RayTracer.h +++ b/openvdb/openvdb/tools/RayTracer.h @@ -152,10 +152,10 @@ class VolumeRender using GridType = typename IntersectorT::GridType; using RayType = typename IntersectorT::RayType; - using ValueType = typename GridType::ValueType; + using ComputeType = typename GridType::ComputeType; using AccessorType = typename GridType::ConstAccessor; using SamplerType = tools::GridSampler; - static_assert(std::is_floating_point::value, + static_assert(std::is_floating_point::value, "VolumeRender requires a floating-point-valued grid"); /// @brief Constructor taking an intersector and a base camera. @@ -549,9 +549,11 @@ class MatteShader: public BaseShader ~MatteShader() override = default; Film::RGBA operator()(const Vec3R& xyz, const Vec3R&, const Vec3R&) const override { + using RealT = Film::RGBA::ValueT; + typename GridT::ValueType v = zeroVal(); SamplerType::sample(mAcc, mXform->worldToIndex(xyz), v); - return Film::RGBA(v[0], v[1], v[2]); + return Film::RGBA(RealT(v[0]), RealT(v[1]), RealT(v[2])); } BaseShader* copy() const override { return new MatteShader(*this); } @@ -596,9 +598,13 @@ class NormalShader: public BaseShader ~NormalShader() override = default; Film::RGBA operator()(const Vec3R& xyz, const Vec3R& normal, const Vec3R&) const override { + using RealT = Film::RGBA::ValueT; + typename GridT::ValueType v = zeroVal(); SamplerType::sample(mAcc, mXform->worldToIndex(xyz), v); - return Film::RGBA(v[0]*(normal[0]+1.0), v[1]*(normal[1]+1.0), v[2]*(normal[2]+1.0)); + return Film::RGBA(RealT(v[0])*(normal[0]+1.0), + RealT(v[1])*(normal[1]+1.0), + RealT(v[2])*(normal[2]+1.0)); } BaseShader* copy() const override { return new NormalShader(*this); } @@ -649,10 +655,13 @@ class PositionShader: public BaseShader ~PositionShader() override = default; Film::RGBA operator()(const Vec3R& xyz, const Vec3R&, const Vec3R&) const override { + using RealT = Film::RGBA::ValueT; + typename GridT::ValueType v = zeroVal(); SamplerType::sample(mAcc, mXform->worldToIndex(xyz), v); const Vec3R rgb = (xyz - mMin) * mInvDim; - return Film::RGBA(v[0],v[1],v[2]) * Film::RGBA(rgb[0], rgb[1], rgb[2]); + return Film::RGBA(RealT(v[0]), RealT(v[1]), RealT(v[2])) + * Film::RGBA(rgb[0], rgb[1], rgb[2]); } BaseShader* copy() const override { return new PositionShader(*this); } @@ -703,12 +712,14 @@ class DiffuseShader: public BaseShader ~DiffuseShader() override = default; Film::RGBA operator()(const Vec3R& xyz, const Vec3R& normal, const Vec3R& rayDir) const override { + using RealT = Film::RGBA::ValueT; + typename GridT::ValueType v = zeroVal(); SamplerType::sample(mAcc, mXform->worldToIndex(xyz), v); // We take the abs of the dot product corresponding to having // light sources at +/- rayDir, i.e., two-sided shading. - return Film::RGBA(v[0],v[1],v[2]) - * static_cast(math::Abs(normal.dot(rayDir))); + return Film::RGBA(RealT(v[0]), RealT(v[1]), RealT(v[2])) + * static_cast(math::Abs(normal.dot(rayDir))); } BaseShader* copy() const override { return new DiffuseShader(*this); } diff --git a/openvdb/openvdb/tools/SignedFloodFill.h b/openvdb/openvdb/tools/SignedFloodFill.h index 03ff521dd0..d2bf0f4c70 100644 --- a/openvdb/openvdb/tools/SignedFloodFill.h +++ b/openvdb/openvdb/tools/SignedFloodFill.h @@ -83,9 +83,10 @@ class SignedFloodFillOp { public: using ValueT = typename TreeOrLeafManagerT::ValueType; + using ComputeT = typename TreeOrLeafManagerT::ComputeType; using RootT = typename TreeOrLeafManagerT::RootNodeType; using LeafT = typename TreeOrLeafManagerT::LeafNodeType; - static_assert(std::is_signed::value, + static_assert(std::is_signed::value, "signed flood fill is supported only for signed value grids"); SignedFloodFillOp(const TreeOrLeafManagerT& tree, Index minLevel = 0) @@ -116,19 +117,19 @@ class SignedFloodFillOp const Index first = valueMask.findFirstOn(); if (first < LeafT::SIZE) { - bool xInside = buffer[first]<0, yInside = xInside, zInside = xInside; + bool xInside = math::isNegative(buffer[first]), yInside = xInside, zInside = xInside; for (Index x = 0; x != (1 << LeafT::LOG2DIM); ++x) { const Index x00 = x << (2 * LeafT::LOG2DIM); - if (valueMask.isOn(x00)) xInside = buffer[x00] < 0; // element(x, 0, 0) + if (valueMask.isOn(x00)) xInside = math::isNegative(buffer[x00]); // element(x, 0, 0) yInside = xInside; for (Index y = 0; y != (1 << LeafT::LOG2DIM); ++y) { const Index xy0 = x00 + (y << LeafT::LOG2DIM); - if (valueMask.isOn(xy0)) yInside = buffer[xy0] < 0; // element(x, y, 0) + if (valueMask.isOn(xy0)) yInside = math::isNegative(buffer[xy0]); // element(x, y, 0) zInside = yInside; for (Index z = 0; z != (1 << LeafT::LOG2DIM); ++z) { const Index xyz = xy0 + z; // element(x, y, z) if (valueMask.isOn(xyz)) { - zInside = buffer[xyz] < 0; + zInside = math::isNegative(buffer[xyz]); } else { buffer[xyz] = zInside ? mInside : mOutside; } @@ -136,7 +137,7 @@ class SignedFloodFillOp } } } else {// if no active voxels exist simply use the sign of the first value - leaf.fill(buffer[0] < 0 ? mInside : mOutside); + leaf.fill(math::isNegative(buffer[0]) ? mInside : mOutside); } } @@ -152,20 +153,20 @@ class SignedFloodFillOp const Index first = childMask.findFirstOn(); if (first < NodeT::NUM_VALUES) { - bool xInside = table[first].getChild()->getFirstValue()<0; + bool xInside = math::isNegative(table[first].getChild()->getFirstValue()); bool yInside = xInside, zInside = xInside; for (Index x = 0; x != (1 << NodeT::LOG2DIM); ++x) { const int x00 = x << (2 * NodeT::LOG2DIM); // offset for block(x, 0, 0) - if (childMask.isOn(x00)) xInside = table[x00].getChild()->getLastValue()<0; + if (childMask.isOn(x00)) xInside = math::isNegative(table[x00].getChild()->getLastValue()); yInside = xInside; for (Index y = 0; y != (1 << NodeT::LOG2DIM); ++y) { const Index xy0 = x00 + (y << NodeT::LOG2DIM); // offset for block(x, y, 0) - if (childMask.isOn(xy0)) yInside = table[xy0].getChild()->getLastValue()<0; + if (childMask.isOn(xy0)) yInside = math::isNegative(table[xy0].getChild()->getLastValue()); zInside = yInside; for (Index z = 0; z != (1 << NodeT::LOG2DIM); ++z) { const Index xyz = xy0 + z; // offset for block(x, y, z) if (childMask.isOn(xyz)) { - zInside = table[xyz].getChild()->getLastValue()<0; + zInside = math::isNegative(table[xyz].getChild()->getLastValue()); } else { table[xyz].setValue(zInside ? mInside : mOutside); } @@ -215,7 +216,7 @@ class SignedFloodFillOp template inline -typename std::enable_if::value, void>::type +typename std::enable_if_t::value, void> doSignedFloodFill(TreeOrLeafManagerT& tree, typename TreeOrLeafManagerT::ValueType outsideValue, typename TreeOrLeafManagerT::ValueType insideValue, @@ -231,7 +232,7 @@ doSignedFloodFill(TreeOrLeafManagerT& tree, // Dummy (no-op) implementation for unsigned types template inline -typename std::enable_if::value, void>::type +typename std::enable_if_t::value, void> doSignedFloodFill(TreeOrLeafManagerT&, const typename TreeOrLeafManagerT::ValueType&, const typename TreeOrLeafManagerT::ValueType&, diff --git a/openvdb/openvdb/tools/ValueTransformer.h b/openvdb/openvdb/tools/ValueTransformer.h index 0022e49db5..0d9af45858 100644 --- a/openvdb/openvdb/tools/ValueTransformer.h +++ b/openvdb/openvdb/tools/ValueTransformer.h @@ -135,12 +135,14 @@ inline void foreach(const IterT& iter, const XformOp& op, /// consider using @c tbb::parallel_for() or @c tbb::parallel_reduce() in conjunction /// with a tree::IteratorRange that wraps a grid or tree iterator. template -inline void transformValues(const InIterT& inIter, OutGridT& outGrid, +inline OPENVDB_UBSAN_SUPPRESS("undefined") +void transformValues(const InIterT& inIter, OutGridT& outGrid, XformOp& op, bool threaded = true, bool shareOp = true, MergePolicy merge = MERGE_ACTIVE_STATES); template -inline void transformValues(const InIterT& inIter, OutGridT& outGrid, +inline OPENVDB_UBSAN_SUPPRESS("undefined") +void transformValues(const InIterT& inIter, OutGridT& outGrid, const XformOp& op, bool threaded = true, bool shareOp = true, MergePolicy merge = MERGE_ACTIVE_STATES); @@ -585,8 +587,8 @@ class CopyableOpTransformer template -inline void -transformValues(const InIterT& inIter, OutGridT& outGrid, XformOp& op, +inline OPENVDB_UBSAN_SUPPRESS("undefined") +void transformValues(const InIterT& inIter, OutGridT& outGrid, XformOp& op, bool threaded, bool shared, MergePolicy merge) { using Adapter = TreeAdapter; @@ -603,8 +605,8 @@ transformValues(const InIterT& inIter, OutGridT& outGrid, XformOp& op, } template -inline void -transformValues(const InIterT& inIter, OutGridT& outGrid, const XformOp& op, +inline OPENVDB_UBSAN_SUPPRESS("undefined") +void transformValues(const InIterT& inIter, OutGridT& outGrid, const XformOp& op, bool threaded, bool /*share*/, MergePolicy merge) { using Adapter = TreeAdapter; diff --git a/openvdb/openvdb/tools/VolumeAdvect.h b/openvdb/openvdb/tools/VolumeAdvect.h index aa7b856134..f32c2d1433 100644 --- a/openvdb/openvdb/tools/VolumeAdvect.h +++ b/openvdb/openvdb/tools/VolumeAdvect.h @@ -365,6 +365,7 @@ struct VolumeAdvection::Advec using TreeT = typename VolumeGridT::TreeType; using AccT = typename VolumeGridT::ConstAccessor; using ValueT = typename TreeT::ValueType; + using ComputeT = typename TreeT::ComputeType; using LeafManagerT = typename tree::LeafManager; using LeafNodeT = typename LeafManagerT::LeafNodeType; using LeafRangeT = typename LeafManagerT::LeafRange; @@ -443,12 +444,13 @@ struct VolumeAdvection::Advec const ValueT* in0 = leaf->buffer().data(); for (VoxelIterT voxelIter = leafIter->beginValueOn(); voxelIter; ++voxelIter) { const Index i = voxelIter.pos(); - out0[i] += RealT(0.5) * ( in0[i] - out1[i] ); + out0[i] += RealT(0.5) * ( ComputeT(in0[i]) - ComputeT(out1[i]) ); } } else { for (VoxelIterT voxelIter = leafIter->beginValueOn(); voxelIter; ++voxelIter) { const Index i = voxelIter.pos(); - out0[i] += RealT(0.5) * ( acc.getValue(voxelIter.getCoord()) - out1[i] ); + out0[i] += RealT(0.5) * + (ComputeT(acc.getValue(voxelIter.getCoord())) - ComputeT(out1[i])); }//loop over active voxels } }//loop over leaf nodes @@ -467,12 +469,13 @@ struct VolumeAdvection::Advec const ValueT* in0 = leaf->buffer().data(); for (VoxelIterT voxelIter = leafIter->beginValueOn(); voxelIter; ++voxelIter) { const Index i = voxelIter.pos(); - out0[i] = RealT(0.5)*( RealT(3)*in0[i] - out1[i] ); + out0[i] = RealT(0.5)*( RealT(3)*ComputeT(in0[i]) - ComputeT(out1[i]) ); }//loop over active voxels } else { for (VoxelIterT voxelIter = leafIter->beginValueOn(); voxelIter; ++voxelIter) { const Index i = voxelIter.pos(); - out0[i] = RealT(0.5)*( RealT(3)*acc.getValue(voxelIter.getCoord()) - out1[i] ); + out0[i] = RealT(0.5)*( + RealT(3)*ComputeT(acc.getValue(voxelIter.getCoord())) - ComputeT(out1[i])); }//loop over active voxels } }//loop over leaf nodes @@ -498,7 +501,7 @@ struct VolumeAdvection::Advec if (mParent->interrupt()) return; const bool doLimiter = mParent->isLimiterOn(); const bool doClamp = mParent->mLimiter == Scheme::CLAMP; - ValueT data[2][2][2], vMin, vMax; + ComputeT data[2][2][2], vMin, vMax; const math::Transform& xform = mInGrid->transform(); AccT acc = mInGrid->getAccessor(); const ValueT backg = mInGrid->background(); @@ -516,14 +519,15 @@ struct VolumeAdvection::Advec BoxSampler::getValues(data, acc, ijk); BoxSampler::extrema(data, vMin, vMax); if ( doClamp ) { - value = math::Clamp( value, vMin, vMax); - } else if (value < vMin || value > vMax ) { + value = math::Clamp( ComputeT(value), vMin, vMax); + } else if (ComputeT(value) < vMin || ComputeT(value) > vMax ) { iPos -= Vec3R(ijk[0], ijk[1], ijk[2]);//unit coordinates value = BoxSampler::trilinearInterpolation( data, iPos ); } } - if (math::isApproxEqual(value, backg, math::Delta::value())) { + if (math::isApproxEqual(ComputeT(value), ComputeT(backg), + math::Delta::value())) { value = backg; leafIter->setValueOff( voxelIter.pos() ); } @@ -553,10 +557,12 @@ struct VolumeAdvection::Advec OPENVDB_INSTANTIATE_CLASS VolumeAdvection; OPENVDB_INSTANTIATE_CLASS VolumeAdvection; +OPENVDB_INSTANTIATE HalfGrid::Ptr VolumeAdvection::advect>(const HalfGrid&, double); OPENVDB_INSTANTIATE FloatGrid::Ptr VolumeAdvection::advect>(const FloatGrid&, double); OPENVDB_INSTANTIATE DoubleGrid::Ptr VolumeAdvection::advect>(const DoubleGrid&, double); OPENVDB_INSTANTIATE Vec3SGrid::Ptr VolumeAdvection::advect>(const Vec3SGrid&, double); +OPENVDB_INSTANTIATE HalfGrid::Ptr VolumeAdvection::advect>(const HalfGrid&, double); OPENVDB_INSTANTIATE FloatGrid::Ptr VolumeAdvection::advect>(const FloatGrid&, double); OPENVDB_INSTANTIATE DoubleGrid::Ptr VolumeAdvection::advect>(const DoubleGrid&, double); OPENVDB_INSTANTIATE Vec3SGrid::Ptr VolumeAdvection::advect>(const Vec3SGrid&, double); diff --git a/openvdb/openvdb/tools/VolumeToSpheres.h b/openvdb/openvdb/tools/VolumeToSpheres.h index b5d7b95181..a0466936c0 100644 --- a/openvdb/openvdb/tools/VolumeToSpheres.h +++ b/openvdb/openvdb/tools/VolumeToSpheres.h @@ -1004,6 +1004,7 @@ ClosestSurfacePoint::searchAndReplace(std::vector& points, #include #endif +OPENVDB_INSTANTIATE_CLASS ClosestSurfacePoint; OPENVDB_INSTANTIATE_CLASS ClosestSurfacePoint; OPENVDB_INSTANTIATE_CLASS ClosestSurfacePoint; diff --git a/openvdb/openvdb/tree/InternalNode.h b/openvdb/openvdb/tree/InternalNode.h index 46fef2d96d..5dfb394db8 100644 --- a/openvdb/openvdb/tree/InternalNode.h +++ b/openvdb/openvdb/tree/InternalNode.h @@ -37,6 +37,7 @@ class InternalNode using ChildNodeType = _ChildNodeType; using LeafNodeType = typename ChildNodeType::LeafNodeType; using ValueType = typename ChildNodeType::ValueType; + using ComputeType = typename ChildNodeType::ComputeType; using BuildType = typename ChildNodeType::BuildType; using UnionType = NodeUnion; using NodeMaskType = util::NodeMask; @@ -274,9 +275,11 @@ class InternalNode /// Set the transient data value. void setTransientData(Index32 transientData) { mTransientData = transientData; } - Index32 leafCount() const; + Index64 leafCount() const; + Index64 nonLeafCount() const; + void nodeCount(std::vector &vec) const; + OPENVDB_DEPRECATED_MESSAGE("Use input type std::vector for nodeCount.") void nodeCount(std::vector &vec) const; - Index32 nonLeafCount() const; Index32 childCount() const; Index64 onVoxelCount() const; Index64 offVoxelCount() const; @@ -331,7 +334,11 @@ class InternalNode /// Return @c true if the voxel at the given coordinates is active. bool isValueOn(const Coord& xyz) const; /// Return @c true if the voxel at the given offset is active. - bool isValueOn(Index offset) const { return mValueMask.isOn(offset); } + bool isValueOn(Index offset) const { OPENVDB_ASSERT(offset < NUM_VALUES); return mValueMask.isOn(offset); } + /// Return @c true if the voxel at the given coordinates is inactive. + bool isValueOff(const Coord& xyz) const; + /// Return @c true if the voxel at the given offset is inactive. + bool isValueOff(Index offset) const { OPENVDB_ASSERT(offset < NUM_VALUES); return mValueMask.isOff(offset); } /// Return @c true if this node or any of its child nodes have any active tiles. bool hasActiveTiles() const; @@ -458,6 +465,79 @@ class InternalNode void readBuffers(std::istream&, const CoordBBox&, bool fromHalf = false); + // + // Unsafe methods + // + // WARNING: For improved performance, these unsafe methods do not check the value or + // child masks. If used incorrectly, at best they will leave the InternalNode in an + // invalid state and at worst cause the application to crash. Always use the safer + // alternative method(s) unless you really know what you're doing. + // Enabling OpenVDB asserts will catch where assumptions are incorrectly invalidated. + + /// @brief Return the tile value at offset. + /// @note Use getValue(const Coord&) for a safer alternative. + /// @warning This method should only be used by experts seeking low-level optimizations. + const ValueType& getValueUnsafe(Index offset) const; + /// @brief Return the tile value and active state at offset. + /// @note Use probeValue(const Coord&, ValueType&) for a safer alternative. + /// @warning This method should only be used by experts seeking low-level optimizations. + bool getValueUnsafe(Index offset, ValueType& value) const; + + /// @brief Return the child node at offset. + /// @note Use probeChild(const Coord&) for a safer alternative. + /// @warning This method should only be used by experts seeking low-level optimizations. + ChildNodeType* getChildUnsafe(Index offset); + /// @brief Return the child node at offset. + /// @note Use probeConstChild(const Coord&) for a safer alternative. + /// @warning This method should only be used by experts seeking low-level optimizations. + const ChildNodeType* getConstChildUnsafe(Index offset) const; + /// @brief Return the child node at offset. + /// @note Use probeChild(const Coord&) for a safer alternative. + /// @warning This method should only be used by experts seeking low-level optimizations. + const ChildNodeType* getChildUnsafe(Index offset) const; + + /// @brief Set the tile active state at offset but don't change its value. + /// @note Use setActiveState(const Coord&, bool) for a safer alternative. + /// @warning This method should only be used by experts seeking low-level optimizations. + void setActiveStateUnsafe(Index offset, bool on); + /// @brief Set the tile value at offset but don't change its value. + /// @note Use setValueOnly(const Coord&, const ValueType&) for a safer alternative. + /// @warning This method should only be used by experts seeking low-level optimizations. + void setValueOnlyUnsafe(Index offset, const ValueType& value); + /// @brief Mark the tile active at offset but don't change its value. + /// @note Use setValueOn(const Coord&) for a safer alternative. + /// @warning This method should only be used by experts seeking low-level optimizations. + void setValueOnUnsafe(Index offset); + /// @brief Set the tile value at offset and mark the voxel as active. + /// @note Use setValueOn(const Coord&, const ValueType&) for a safer alternative. + /// @warning This method should only be used by experts seeking low-level optimizations. + void setValueOnUnsafe(Index offset, const ValueType& value); + /// @brief Mark the tile inactive at offset but don't change its value. + /// @note Use setValueOff(const Coord&) for a safer alternative. + /// @warning This method should only be used by experts seeking low-level optimizations. + void setValueOffUnsafe(Index offset); + /// @brief Set the tile value at offset and mark the voxel as inactive. + /// @note Use setValueOff(const Coord&, const ValueType&) for a safer alternative. + /// @warning This method should only be used by experts seeking low-level optimizations. + void setValueOffUnsafe(Index offset, const ValueType& value); + + /// @brief Replace a tile at offset with the given child node. + /// @note Use addChild(ChildNodeType*) for a safer alternative. + /// @warning This method should only be used by experts seeking low-level optimizations. + void setChildUnsafe(Index offset, ChildNodeType* child); + /// @brief Replace a child node at offset with the given child node. + /// @note Use addChild(ChildNodeType*) for a safer alternative. + /// @warning This method should only be used by experts seeking low-level optimizations. + void resetChildUnsafe(Index offset, ChildNodeType* child); + /// @brief Replace a child node at offset with the given value and active state. + /// @note Use addChild(ChildNodeType*) for a safer alternative. + /// @warning This method should only be used by experts seeking low-level optimizations. + ChildNodeType* stealChildUnsafe(Index offset, const ValueType& value, bool active); + /// @brief Delete a child node at offset and replace with the given value and active state. + /// @note Use addTile(Index, const ValueType&, bool) for a safer alternative. + /// @warning This method should only be used by experts seeking low-level optimizations. + void deleteChildUnsafe(Index offset, const ValueType& value, bool active); + // // Aux methods // @@ -620,6 +700,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); } //@} //@{ @@ -988,11 +1105,11 @@ InternalNode::~InternalNode() template -inline Index32 +inline Index64 InternalNode::leafCount() const { if (ChildNodeType::getLevel() == 0) return mChildMask.countOn(); - Index32 sum = 0; + Index64 sum = 0; for (ChildOnCIter iter = this->cbeginChildOn(); iter; ++iter) { sum += iter->leafCount(); } @@ -1001,7 +1118,7 @@ InternalNode::leafCount() const template inline void -InternalNode::nodeCount(std::vector &vec) const +InternalNode::nodeCount(std::vector &vec) const { OPENVDB_ASSERT(vec.size() > ChildNodeType::LEVEL); const auto count = mChildMask.countOn(); @@ -1011,12 +1128,28 @@ InternalNode::nodeCount(std::vector &vec) const vec[ChildNodeType::LEVEL] += count; } +template +inline void +InternalNode::nodeCount(std::vector &vec) const +{ + OPENVDB_ASSERT(vec.size() > ChildNodeType::LEVEL); + const auto count = mChildMask.countOn(); + if (ChildNodeType::LEVEL > 0 && count > 0) { + for (auto iter = this->cbeginChildOn(); iter; ++iter) { + OPENVDB_NO_DEPRECATION_WARNING_BEGIN + iter->nodeCount(vec); + OPENVDB_NO_DEPRECATION_WARNING_END + } + } + vec[ChildNodeType::LEVEL] += count; +} + template -inline Index32 +inline Index64 InternalNode::nonLeafCount() const { - Index32 sum = 1; + Index64 sum = 1; if (ChildNodeType::getLevel() == 0) return sum; for (ChildOnCIter iter = this->cbeginChildOn(); iter; ++iter) { sum += iter->nonLeafCount(); @@ -1248,6 +1381,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) @@ -1550,8 +1759,17 @@ inline bool InternalNode::isValueOn(const Coord& xyz) const { const Index n = this->coordToOffset(xyz); - if (this->isChildMaskOff(n)) return this->isValueMaskOn(n); - return mNodes[n].getChild()->isValueOn(xyz); + return this->isChildMaskOff(n) ? this->isValueMaskOn(n) + : mNodes[n].getChild()->isValueOn(xyz); +} + +template +inline bool +InternalNode::isValueOff(const Coord& xyz) const +{ + const Index n = this->coordToOffset(xyz); + return this->isChildMaskOff(n) ? this->isValueMaskOn(n) + : mNodes[n].getChild()->isValueOff(xyz); } template @@ -2272,6 +2490,156 @@ InternalNode::getLastValue() const //////////////////////////////////////// +template +inline const typename ChildT::ValueType& +InternalNode::getValueUnsafe(Index n) const +{ + OPENVDB_ASSERT(n < NUM_VALUES); + OPENVDB_ASSERT(mChildMask.isOff(n)); + return mNodes[n].getValue(); +} + +template +inline bool +InternalNode::getValueUnsafe(Index n, ValueType& value) const +{ + OPENVDB_ASSERT(n < NUM_VALUES); + OPENVDB_ASSERT(mChildMask.isOff(n)); + value = mNodes[n].getValue(); + return mValueMask.isOn(n); +} + +template +inline ChildT* +InternalNode::getChildUnsafe(Index n) +{ + OPENVDB_ASSERT(n < NUM_VALUES); + OPENVDB_ASSERT(mChildMask.isOn(n)); + return mNodes[n].getChild(); +} + +template +inline const ChildT* +InternalNode::getConstChildUnsafe(Index n) const +{ + OPENVDB_ASSERT(n < NUM_VALUES); + OPENVDB_ASSERT(mChildMask.isOn(n)); + return mNodes[n].getChild(); +} + +template +inline const ChildT* +InternalNode::getChildUnsafe(Index n) const +{ + return this->getConstChildUnsafe(n); +} + +template +inline void +InternalNode::setActiveStateUnsafe(Index n, bool on) +{ + OPENVDB_ASSERT(n < NUM_VALUES); + OPENVDB_ASSERT(mChildMask.isOff(n)); + mValueMask.set(n, on); +} + +template +inline void +InternalNode::setValueOnlyUnsafe(Index n, const ValueType& value) +{ + OPENVDB_ASSERT(n < NUM_VALUES); + OPENVDB_ASSERT(mChildMask.isOff(n)); + mNodes[n].setValue(value); +} + +template +inline void +InternalNode::setValueOnUnsafe(Index n) +{ + OPENVDB_ASSERT(n < NUM_VALUES); + OPENVDB_ASSERT(mChildMask.isOff(n)); + mValueMask.setOn(n); +} + +template +inline void +InternalNode::setValueOnUnsafe(Index n, const ValueType& value) +{ + OPENVDB_ASSERT(n < NUM_VALUES); + OPENVDB_ASSERT(mChildMask.isOff(n)); + mNodes[n].setValue(value); + mValueMask.setOn(n); +} + +template +inline void +InternalNode::setValueOffUnsafe(Index n) +{ + OPENVDB_ASSERT(n < NUM_VALUES); + OPENVDB_ASSERT(mChildMask.isOff(n)); + mValueMask.setOff(n); +} + +template +inline void +InternalNode::setValueOffUnsafe(Index n, const ValueType& value) +{ + OPENVDB_ASSERT(n < NUM_VALUES); + OPENVDB_ASSERT(mChildMask.isOff(n)); + mNodes[n].setValue(value); + mValueMask.setOff(n); +} + +template +inline void +InternalNode::setChildUnsafe(Index n, ChildNodeType* child) +{ + // replace tile with child + OPENVDB_ASSERT(n < NUM_VALUES); + OPENVDB_ASSERT(mChildMask.isOff(n)); + mNodes[n].setChild(child); + mChildMask.setOn(n); + mValueMask.setOff(n); +} + +template +inline void +InternalNode::resetChildUnsafe(Index n, ChildNodeType* child) +{ + // replace child with child + OPENVDB_ASSERT(child); + OPENVDB_ASSERT(n < NUM_VALUES); + OPENVDB_ASSERT(mChildMask.isOn(n)); + delete mNodes[n].getChild(); + mNodes[n].setChild(child); +} + +template +inline ChildT* +InternalNode::stealChildUnsafe(Index n, const ValueType& value, bool active) +{ + // replace child with tile (and return child) + OPENVDB_ASSERT(n < NUM_VALUES); + OPENVDB_ASSERT(mChildMask.isOn(n)); + auto* child = mNodes[n].getChild(); + mChildMask.setOff(n); + mValueMask.set(n, active); + mNodes[n].setValue(value); + return child; +} + +template +inline void +InternalNode::deleteChildUnsafe(Index n, const ValueType& value, bool active) +{ + // replace child with tile (and delete child) + delete this->stealChildUnsafe(n, value, active); +} + + +//////////////////////////////////////// + + template inline void InternalNode::negate() diff --git a/openvdb/openvdb/tree/LeafManager.h b/openvdb/openvdb/tree/LeafManager.h index 05667c6b02..0c456a98cd 100644 --- a/openvdb/openvdb/tree/LeafManager.h +++ b/openvdb/openvdb/tree/LeafManager.h @@ -87,6 +87,7 @@ class LeafManager public: using TreeType = TreeT; using ValueType = typename TreeT::ValueType; + using ComputeType = typename TreeT::ComputeType; using RootNodeType = typename TreeT::RootNodeType; using NonConstLeafType = typename TreeType::LeafNodeType; using LeafType = typename CopyConstness::Type; @@ -596,7 +597,7 @@ class LeafManager // Compute the leaf counts for each node - std::vector leafCounts; + std::vector leafCounts; if (serial) { leafCounts.reserve(leafParents.size()); for (LeafParentT* leafParent : leafParents) { diff --git a/openvdb/openvdb/tree/LeafNode.h b/openvdb/openvdb/tree/LeafNode.h index b1304e8ded..16775fa8b6 100644 --- a/openvdb/openvdb/tree/LeafNode.h +++ b/openvdb/openvdb/tree/LeafNode.h @@ -40,6 +40,7 @@ class LeafNode public: using BuildType = T; using ValueType = T; + using ComputeType = typename ValueToComputeMap::Type; using Buffer = LeafBuffer; using LeafNodeType = LeafNode; using NodeMaskType = util::NodeMask; @@ -129,11 +130,13 @@ class LeafNode /// Return the dimension of child nodes of this LeafNode, which is one for voxels. static Index getChildDim() { return 1; } /// Return the leaf count for this node, which is one. - static Index32 leafCount() { return 1; } + static Index64 leafCount() { return 1; } /// no-op + void nodeCount(std::vector &) const {} + OPENVDB_DEPRECATED_MESSAGE("Use input type std::vector for nodeCount.") void nodeCount(std::vector &) const {} /// Return the non-leaf count for this node, which is zero. - static Index32 nonLeafCount() { return 0; } + static Index64 nonLeafCount() { return 0; } /// Return the child count for this node, which is zero. static Index32 childCount() { return 0; } @@ -473,9 +476,13 @@ class LeafNode void setValuesOff() { mValueMask.setOff(); } /// Return @c true if the voxel at the given coordinates is active. - bool isValueOn(const Coord& xyz) const {return this->isValueOn(LeafNode::coordToOffset(xyz));} + bool isValueOn(const Coord& xyz) const { return this->isValueOn(LeafNode::coordToOffset(xyz)); } /// Return @c true if the voxel at the given offset is active. - bool isValueOn(Index offset) const { return mValueMask.isOn(offset); } + bool isValueOn(Index offset) const { OPENVDB_ASSERT(offset < SIZE); return mValueMask.isOn(offset); } + /// Return @c true if the voxel at the given coordinates is inactive. + bool isValueOff(const Coord& xyz) const { return this->isValueOff(LeafNode::coordToOffset(xyz)); } + /// Return @c true if the voxel at the given offset is inactive. + bool isValueOff(Index offset) const { OPENVDB_ASSERT(offset < SIZE); return mValueMask.isOff(offset); } /// Return @c false since leaf nodes never contain tiles. static bool hasActiveTiles() { return false; } @@ -825,6 +832,29 @@ class LeafNode /// Return @c true if all of this node's values are inactive. bool isInactive() const { return mValueMask.isOff(); } + // + // Unsafe methods + // + // These methods are not in fact unsafe, but are only offered so that + // the same methods can be called on both internal nodes and leaf nodes. + + /// Return the value of the voxel at the given offset. + const ValueType& getValueUnsafe(Index offset) const { return this->getValue(offset); } + /// Return true if the voxel at the given offset is active and set value. + bool getValueUnsafe(Index offset, ValueType& value) const { return this->probeValue(offset, value); } + /// Set the active state of the voxel at the given offset but don't change its value. + void setActiveStateUnsafe(Index offset, bool on) { this->setActiveState(offset, on); } + /// Set the value of the voxel at the given coordinates but don't change its active state. + void setValueOnlyUnsafe(Index offset, const ValueType& value) { return this->setValueOnly(offset, value); } + /// Mark the voxel at the given offset as active but don't change its value. + void setValueOnUnsafe(Index offset) { this->setValueOn(offset); } + /// Set the value of the voxel at the given coordinates and mark the voxel as active. + void setValueOnUnsafe(Index offset, const ValueType& value) { this->setValueOn(offset, value); } + /// Mark the voxel at the given offset as inactive but don't change its value. + void setValueOffUnsafe(Index offset) { this->setValueOff(offset); } + /// Set the value of the voxel at the given coordinates and mark the voxel as active. + void setValueOffUnsafe(Index offset, const ValueType& value) { this->setValueOff(offset, value); } + protected: friend class ::TestLeaf; template friend class ::TestLeafIO; diff --git a/openvdb/openvdb/tree/LeafNodeBool.h b/openvdb/openvdb/tree/LeafNodeBool.h index 63dd477bd0..e3dc41c5ed 100644 --- a/openvdb/openvdb/tree/LeafNodeBool.h +++ b/openvdb/openvdb/tree/LeafNodeBool.h @@ -32,6 +32,7 @@ class LeafNode using LeafNodeType = LeafNode; using BuildType = bool; using ValueType = bool; + using ComputeType = typename ValueToComputeMap::Type; using Buffer = LeafBuffer; using NodeMaskType = util::NodeMask; using Ptr = SharedPtr; @@ -131,10 +132,12 @@ class LeafNode static void getNodeLog2Dims(std::vector& dims) { dims.push_back(Log2Dim); } static Index getChildDim() { return 1; } - static Index32 leafCount() { return 1; } + static Index64 leafCount() { return 1; } /// no-op + void nodeCount(std::vector &) const {} + OPENVDB_DEPRECATED_MESSAGE("Use input type std::vector for nodeCount.") void nodeCount(std::vector &) const {} - static Index32 nonLeafCount() { return 0; } + static Index64 nonLeafCount() { return 0; } /// Return the number of active voxels. Index64 onVoxelCount() const { return mValueMask.countOn(); } @@ -240,6 +243,10 @@ class LeafNode /// @param xyz the coordinates of the voxel to be probed /// @param[out] val the value of the voxel at the given coordinates bool probeValue(const Coord& xyz, bool& val) const; + /// @brief Return @c true if the voxel at the given offset is active. + /// @param offset the linear offset of the voxel to be probed + /// @param[out] val the value of the voxel at the given coordinates + bool probeValue(Index offset, bool& val) const; /// Return the level (0) at which leaf node values reside. static Index getValueLevel(const Coord&) { return LEVEL; } @@ -295,9 +302,13 @@ class LeafNode void setValuesOff() { mValueMask.setOff(); } /// Return @c true if the voxel at the given coordinates is active. - bool isValueOn(const Coord& xyz) const { return mValueMask.isOn(this->coordToOffset(xyz)); } + bool isValueOn(const Coord& xyz) const { return this->isValueOn(this->coordToOffset(xyz)); } /// Return @c true if the voxel at the given offset is active. bool isValueOn(Index offset) const { OPENVDB_ASSERT(offset < SIZE); return mValueMask.isOn(offset); } + /// Return @c true if the voxel at the given coordinates is inactive. + bool isValueOff(const Coord& xyz) const { return this->isValueOff(this->coordToOffset(xyz)); } + /// Return @c true if the voxel at the given offset is inactive. + bool isValueOff(Index offset) const { OPENVDB_ASSERT(offset < SIZE); return mValueMask.isOff(offset); } /// Return @c false since leaf nodes never contain tiles. static bool hasActiveTiles() { return false; } @@ -457,6 +468,29 @@ class LeafNode /// Return @c true if all of this node's values are inactive. bool isInactive() const { return mValueMask.isOff(); } + // + // Unsafe methods + // + // These methods are not in fact unsafe, but are only offered so that + // the same methods can be called on both internal nodes and leaf nodes. + + /// Return the value of the voxel at the given offset. + const bool& getValueUnsafe(Index offset) const { return this->getValue(offset); } + /// Return true if the voxel at the given offset is active and set value. + bool getValueUnsafe(Index offset, bool& value) const { return this->probeValue(offset, value); } + /// Set the active state of the voxel at the given offset but don't change its value. + void setActiveStateUnsafe(Index offset, bool on) { this->setActiveState(offset, on); } + /// Set the value of the voxel at the given coordinates but don't change its active state. + void setValueOnlyUnsafe(Index offset, const bool& value) { return this->setValueOnly(offset, value); } + /// Mark the voxel at the given offset as active but don't change its value. + void setValueOnUnsafe(Index offset) { this->setValueOn(offset); } + /// Set the value of the voxel at the given coordinates and mark the voxel as active. + void setValueOnUnsafe(Index offset, const bool& value) { this->setValueOn(offset, value); } + /// Mark the voxel at the given offset as inactive but don't change its value. + void setValueOffUnsafe(Index offset) { this->setValueOff(offset); } + /// Set the value of the voxel at the given coordinates and mark the voxel as active. + void setValueOffUnsafe(Index offset, const bool& value) { this->setValueOff(offset, value); } + void resetBackground(bool oldBackground, bool newBackground); void negate() { mBuffer.mData.toggle(); } @@ -1188,12 +1222,18 @@ template inline bool LeafNode::probeValue(const Coord& xyz, bool& val) const { - const Index offset = this->coordToOffset(xyz); + return this->probeValue(this->coordToOffset(xyz), val); +} + +template +inline bool +LeafNode::probeValue(Index offset, bool& val) const +{ + OPENVDB_ASSERT(offset < SIZE); val = mBuffer.mData.isOn(offset); return mValueMask.isOn(offset); } - template inline void LeafNode::setValueOn(const Coord& xyz, bool val) diff --git a/openvdb/openvdb/tree/LeafNodeMask.h b/openvdb/openvdb/tree/LeafNodeMask.h index bedf229b35..f428f21591 100644 --- a/openvdb/openvdb/tree/LeafNodeMask.h +++ b/openvdb/openvdb/tree/LeafNodeMask.h @@ -34,6 +34,7 @@ class LeafNode using LeafNodeType = LeafNode; using BuildType = ValueMask;// this is a rare case where using ValueType = bool;// value type != build type + using ComputeType = typename ValueToComputeMap::Type; using Buffer = LeafBuffer;// buffer uses the bool specialization using NodeMaskType = util::NodeMask; using Ptr = SharedPtr; @@ -112,11 +113,13 @@ class LeafNode /// Return the dimension of child nodes of this LeafNode, which is one for voxels. static Index getChildDim() { return 1; } /// Return the leaf count for this node, which is one. - static Index32 leafCount() { return 1; } + static Index64 leafCount() { return 1; } /// no-op + void nodeCount(std::vector &) const {} + OPENVDB_DEPRECATED_MESSAGE("Use input type std::vector for nodeCount.") void nodeCount(std::vector &) const {} /// Return the non-leaf count for this node, which is zero. - static Index32 nonLeafCount() { return 0; } + static Index64 nonLeafCount() { return 0; } /// Return the number of active voxels. Index64 onVoxelCount() const { return mBuffer.mData.countOn(); } @@ -222,6 +225,10 @@ class LeafNode /// @param xyz the coordinates of the voxel to be probed /// @param[out] val the value of the voxel at the given coordinates bool probeValue(const Coord& xyz, bool& val) const; + /// @brief Return @c true if the voxel at the given offset is active. + /// @param offset the linear offset of the voxel to be probed + /// @param[out] val the value of the voxel at the given coordinates + bool probeValue(Index offset, bool& val) const; /// Return the level (0) at which leaf node values reside. static Index getValueLevel(const Coord&) { return LEVEL; } @@ -277,9 +284,13 @@ class LeafNode void setValuesOff() { mBuffer.mData.setOff(); } /// Return @c true if the voxel at the given coordinates is active. - bool isValueOn(const Coord& xyz) const { return mBuffer.mData.isOn(this->coordToOffset(xyz)); } + bool isValueOn(const Coord& xyz) const { return this->isValueOn(this->coordToOffset(xyz)); } /// Return @c true if the voxel at the given offset is active. bool isValueOn(Index offset) const { OPENVDB_ASSERT(offset < SIZE); return mBuffer.mData.isOn(offset); } + /// Return @c true if the voxel at the given coordinates is inactive. + bool isValueOff(const Coord& xyz) const { return this->isValueOff(this->coordToOffset(xyz)); } + /// Return @c true if the voxel at the given offset is inactive. + bool isValueOff(Index offset) const { OPENVDB_ASSERT(offset < SIZE); return mBuffer.mData.isOff(offset); } /// Return @c false since leaf nodes never contain tiles. static bool hasActiveTiles() { return false; } @@ -440,6 +451,29 @@ class LeafNode /// Return @c true if all of this node's values are inactive. bool isInactive() const { return mBuffer.mData.isOff(); } + // + // Unsafe methods + // + // These methods are not in fact unsafe, but are only offered so that + // the same methods can be called on both internal nodes and leaf nodes. + + /// Return the value of the voxel at the given offset. + const bool& getValueUnsafe(Index offset) const { return this->getValue(offset); } + /// Return true if the voxel at the given offset is active and set value. + bool getValueUnsafe(Index offset, bool& value) const { return this->probeValue(offset, value); } + /// Set the active state of the voxel at the given offset but don't change its value. + void setActiveStateUnsafe(Index offset, bool on) { this->setActiveState(offset, on); } + /// Set the value of the voxel at the given coordinates but don't change its active state. + void setValueOnlyUnsafe(Index offset, const bool& value) { return this->setValueOnly(offset, value); } + /// Mark the voxel at the given offset as active but don't change its value. + void setValueOnUnsafe(Index offset) { this->setValueOn(offset); } + /// Set the value of the voxel at the given coordinates and mark the voxel as active. + void setValueOnUnsafe(Index offset, const bool& value) { this->setValueOn(offset, value); } + /// Mark the voxel at the given offset as inactive but don't change its value. + void setValueOffUnsafe(Index offset) { this->setValueOff(offset); } + /// Set the value of the voxel at the given coordinates and mark the voxel as active. + void setValueOffUnsafe(Index offset, const bool& value) { this->setValueOff(offset, value); } + /// @brief no-op since for this template specialization voxel /// values and states are indistinguishable. void resetBackground(bool, bool) {} @@ -1109,7 +1143,14 @@ template inline bool LeafNode::probeValue(const Coord& xyz, bool& val) const { - const Index offset = this->coordToOffset(xyz); + return this->probeValue(this->coordToOffset(xyz), val); +} + + +template +inline bool +LeafNode::probeValue(Index offset, bool& val) const +{ val = mBuffer.mData.isOn(offset); return val; } diff --git a/openvdb/openvdb/tree/NodeManager.h b/openvdb/openvdb/tree/NodeManager.h index 850589e231..1023c00748 100644 --- a/openvdb/openvdb/tree/NodeManager.h +++ b/openvdb/openvdb/tree/NodeManager.h @@ -107,7 +107,7 @@ class NodeList { // Compute the node counts for each node - std::vector nodeCounts; + std::vector nodeCounts; if (serial) { nodeCounts.reserve(parents.nodeCount()); for (size_t i = 0; i < parents.nodeCount(); i++) { @@ -328,7 +328,7 @@ class NodeList void operator()(const NodeRange& range) const { for (typename NodeRange::Iterator it = range.begin(); it; ++it) { - OpT::template eval(mNodeOp, it); + OpT::eval(mNodeOp, it); } } const NodeOp mNodeOp; @@ -348,7 +348,7 @@ class NodeList void operator()(const NodeRange& range) const { for (typename NodeRange::Iterator it = range.begin(); it; ++it) { - OpT::template eval(mNodeOp, it); + OpT::eval(mNodeOp, it); } } const NodeOp& mNodeOp; @@ -373,7 +373,7 @@ class NodeList void operator()(const NodeRange& range) { for (typename NodeRange::Iterator it = range.begin(); it; ++it) { - OpT::template eval(*mNodeOp, it); + OpT::eval(*mNodeOp, it); } } void join(const NodeReducer& other) diff --git a/openvdb/openvdb/tree/RootNode.h b/openvdb/openvdb/tree/RootNode.h index 0aca129889..e97f52898d 100644 --- a/openvdb/openvdb/tree/RootNode.h +++ b/openvdb/openvdb/tree/RootNode.h @@ -42,6 +42,7 @@ class RootNode using ChildNodeType = ChildType; using LeafNodeType = typename ChildType::LeafNodeType; using ValueType = typename ChildType::ValueType; + using ComputeType = typename ChildType::ComputeType; using BuildType = typename ChildType::BuildType; static const Index LEVEL = 1 + ChildType::LEVEL; // level 0 = leaf @@ -140,6 +141,7 @@ class RootNode NodeStruct(ChildType& c): child(&c) {} NodeStruct(const Tile& t): child(nullptr), tile(t) {} NodeStruct(const NodeStruct&) = default; + NodeStruct(NodeStruct&&) noexcept = default; NodeStruct& operator=(const NodeStruct&) = default; ~NodeStruct() {} ///< @note doesn't delete child @@ -483,14 +485,19 @@ class RootNode template static bool hasCompatibleValueType(const RootNode& other); - Index32 leafCount() const; - Index32 nonLeafCount() const; + Index64 leafCount() const; + Index64 nonLeafCount() const; Index32 childCount() const; + Index32 tileCount() const; + Index32 activeTileCount() const; + Index32 inactiveTileCount() const; Index64 onVoxelCount() const; Index64 offVoxelCount() const; Index64 onLeafVoxelCount() const; Index64 offLeafVoxelCount() const; Index64 onTileCount() const; + void nodeCount(std::vector &vec) const; + OPENVDB_DEPRECATED_MESSAGE("Use input type std::vector for nodeCount.") void nodeCount(std::vector &vec) const; bool isValueOn(const Coord& xyz) const; @@ -706,6 +713,12 @@ class RootNode template void addTileAndCache(Index level, const Coord& xyz, const ValueType&, bool state, AccessorT&); + /// @brief Delete any child or tile containing voxel (x, y, z) at the root level. + /// Do nothing if no child or tile was found. + /// @warning This method will invalidate any existing RootNode iterators. + /// @return @c true if child or tile was deleted + bool deleteChildOrTile(const Coord& xyz); + /// @brief Return a pointer to the leaf node that contains voxel (x, y, z). /// If no such node exists, create one that preserves the values and /// active states of all voxels. @@ -724,9 +737,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 +760,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); } //@} //@{ @@ -755,6 +787,35 @@ class RootNode const LeafNodeType* probeLeafAndCache(const Coord& xyz, AccessorT& acc) const; //@} + // + // Unsafe methods + // + // WARNING: For improved performance, these unsafe methods assume that the tile + // or child exists. If used incorrectly, this can cause the application to crash. + // Always use the safer alternative method(s) unless you really know what you're doing. + // Enabling OpenVDB asserts will catch where assumptions are incorrectly invalidated. + + /// @brief Return the tile value at the given coordinate. + /// @note Use cbeginValueAll() for a safer alternative. + /// @warning This method should only be used by experts seeking low-level optimizations. + const ValueType& getTileValueUnsafe(const Coord& xyz) const; + /// @brief Return the tile value and active state at the given coordinate. + /// @note Use cbeginValueAll() for a safer alternative. + /// @warning This method should only be used by experts seeking low-level optimizations. + bool getTileValueUnsafe(const Coord& xyz, ValueType& value) const; + /// @brief Return the child node at the given coordinate. + /// @note Use beginChildAll() for a safer alternative. + /// @warning This method should only be used by experts seeking low-level optimizations. + ChildNodeType* getChildUnsafe(const Coord& xyz); + /// @brief Return the child node at the given coordinate. + /// @note Use cbeginChildAll() for a safer alternative. + /// @warning This method should only be used by experts seeking low-level optimizations. + const ChildNodeType* getConstChildUnsafe(const Coord& xyz) const; + /// @brief Return the child node at the given coordinate. + /// @note Use cbeginChildAll() for a safer alternative. + /// @warning This method should only be used by experts seeking low-level optimizations. + const ChildNodeType* getChildUnsafe(const Coord& xyz) const; + // // Aux methods @@ -881,7 +942,6 @@ class RootNode void combine2(const RootNode& other0, const OtherRootNode& other1, CombineOp& op, bool prune = false); -#if OPENVDB_ABI_VERSION_NUMBER >= 10 /// Return the grid index coordinates of this node's local origin. const Coord& origin() const { return mOrigin; } /// @brief change the origin on this root node @@ -890,7 +950,12 @@ class RootNode /// @warning This method will throw if the origin is non-zero, since /// other tools do not yet support variable offsets. void setOrigin(const Coord &origin); -#endif + + /// Return a MapType key for the given coordinates, offset by the mOrigin. + Coord coordToKey(const Coord& xyz) const { return (xyz - mOrigin) & ~(ChildType::DIM - 1); } + + /// Return @c true if this node's mTable contains the given key. + bool hasKey(const Coord& key) const { return mTable.find(key) != mTable.end(); } private: /// During topology-only construction, access is needed @@ -900,32 +965,9 @@ class RootNode template friend struct RootNodeCopyHelper; template friend struct RootNodeCombineHelper; - /// Currently no-op, but can be used to define empty and delete keys for mTable - void initTable() {} - //@{ - /// @internal Used by doVisit2(). - void resetTable(MapType& table) { mTable.swap(table); table.clear(); } - void resetTable(const MapType&) const {} - //@} - - Index getChildCount() const; - Index getTileCount() const; - Index getActiveTileCount() const; - Index getInactiveTileCount() const; - -#if OPENVDB_ABI_VERSION_NUMBER < 10 - /// Static method that returns a MapType key for the given coordinates. - static Coord coordToKey(const Coord& xyz) {return xyz & ~(ChildType::DIM - 1); } -#else - /// Return a MapType key for the given coordinates, offset by the mOrigin. - Coord coordToKey(const Coord& xyz) const { return (xyz - mOrigin) & ~(ChildType::DIM - 1); } -#endif - /// Insert this node's mTable keys into the given set. void insertKeys(CoordSet&) const; - /// Return @c true if this node's mTable contains the given key. - bool hasKey(const Coord& key) const { return mTable.find(key) != mTable.end(); } //@{ /// @brief Look up the given key in this node's mTable. /// @return an iterator pointing to the matching mTable entry or to mTable.end(). @@ -963,9 +1005,7 @@ class RootNode MapType mTable; ValueType mBackground; -#if OPENVDB_ABI_VERSION_NUMBER >= 10 Coord mOrigin; -#endif /// Transient Data (not serialized) Index32 mTransientData = 0; }; // end of RootNode class @@ -1032,11 +1072,8 @@ template inline RootNode::RootNode() : mBackground(zeroVal()) -#if OPENVDB_ABI_VERSION_NUMBER >= 10 , mOrigin(0, 0, 0) -#endif { - this->initTable(); } @@ -1044,11 +1081,8 @@ template inline RootNode::RootNode(const ValueType& background) : mBackground(background) -#if OPENVDB_ABI_VERSION_NUMBER >= 10 , mOrigin(0, 0, 0) -#endif { - this->initTable(); } @@ -1058,28 +1092,23 @@ inline RootNode::RootNode(const RootNode& other, const ValueType& backgd, const ValueType& foregd, TopologyCopy) : mBackground(backgd) -#if OPENVDB_ABI_VERSION_NUMBER >= 10 , mOrigin(other.mOrigin) -#endif , mTransientData(other.mTransientData) { using OtherRootT = RootNode; -#if OPENVDB_ABI_VERSION_NUMBER >= 10 if (mOrigin != Coord(0,0,0)) { OPENVDB_THROW(ValueError, "RootNode::RootNode: non-zero offsets are currently not supported"); } -#endif enforceSameConfiguration(other); const Tile bgTile(backgd, /*active=*/false), fgTile(foregd, true); - this->initTable(); for (typename OtherRootT::MapCIter i=other.mTable.begin(), e=other.mTable.end(); i != e; ++i) { - mTable[i->first] = OtherRootT::isTile(i) + mTable.emplace(i->first, OtherRootT::isTile(i) ? NodeStruct(OtherRootT::isTileOn(i) ? fgTile : bgTile) - : NodeStruct(*(new ChildT(OtherRootT::getChild(i), backgd, foregd, TopologyCopy()))); + : NodeStruct(*(new ChildT(OtherRootT::getChild(i), backgd, foregd, TopologyCopy())))); } } @@ -1090,27 +1119,24 @@ inline RootNode::RootNode(const RootNode& other, const ValueType& backgd, TopologyCopy) : mBackground(backgd) -#if OPENVDB_ABI_VERSION_NUMBER >= 10 , mOrigin(other.mOrigin) -#endif , mTransientData(other.mTransientData) { using OtherRootT = RootNode; -#if OPENVDB_ABI_VERSION_NUMBER >= 10 if (mOrigin != Coord(0,0,0)) { OPENVDB_THROW(ValueError, "RootNode::RootNode: non-zero offsets are currently not supported"); } -#endif enforceSameConfiguration(other); const Tile bgTile(backgd, /*active=*/false), fgTile(backgd, true); - this->initTable(); + for (typename OtherRootT::MapCIter i=other.mTable.begin(), e=other.mTable.end(); i != e; ++i) { - mTable[i->first] = OtherRootT::isTile(i) + mTable.emplace(i->first, + OtherRootT::isTile(i) ? NodeStruct(OtherRootT::isTileOn(i) ? fgTile : bgTile) - : NodeStruct(*(new ChildT(OtherRootT::getChild(i), backgd, TopologyCopy()))); + : NodeStruct(*(new ChildT(OtherRootT::getChild(i), backgd, TopologyCopy())))); } } @@ -1146,7 +1172,6 @@ struct RootNodeCopyHelper { using ValueT = typename RootT::ValueType; using ChildT = typename RootT::ChildNodeType; - using NodeStruct = typename RootT::NodeStruct; using Tile = typename RootT::Tile; using OtherValueT = typename OtherRootT::ValueType; using OtherMapCIter = typename OtherRootT::MapCIter; @@ -1158,26 +1183,23 @@ struct RootNodeCopyHelper }; self.mBackground = Local::convertValue(other.mBackground); -#if OPENVDB_ABI_VERSION_NUMBER >= 10 if (other.mOrigin != Coord(0,0,0)) { OPENVDB_THROW(ValueError, "RootNodeCopyHelper::copyWithValueConversion: non-zero offsets are currently not supported"); } self.mOrigin = other.mOrigin; -#endif self.mTransientData = other.mTransientData; self.clear(); - self.initTable(); for (OtherMapCIter i = other.mTable.begin(), e = other.mTable.end(); i != e; ++i) { if (other.isTile(i)) { // Copy the other node's tile, but convert its value to this node's ValueType. const OtherTile& otherTile = other.getTile(i); - self.mTable[i->first] = NodeStruct( + self.mTable.emplace(i->first, Tile(Local::convertValue(otherTile.value), otherTile.active)); } else { // Copy the other node's child, but convert its values to this node's ValueType. - self.mTable[i->first] = NodeStruct(*(new ChildT(other.getChild(i)))); + self.mTable.emplace(i->first, *(new ChildT(other.getChild(i)))); } } } @@ -1191,20 +1213,17 @@ RootNode::operator=(const RootNode& other) { if (&other != this) { mBackground = other.mBackground; -#if OPENVDB_ABI_VERSION_NUMBER >= 10 mOrigin = other.mOrigin; if (mOrigin != Coord(0,0,0)) { OPENVDB_THROW(ValueError, "RootNode::operator=: non-zero offsets are currently not supported"); } -#endif mTransientData = other.mTransientData; this->clear(); - this->initTable(); for (MapCIter i = other.mTable.begin(), e = other.mTable.end(); i != e; ++i) { - mTable[i->first] = - isTile(i) ? NodeStruct(getTile(i)) : NodeStruct(*(new ChildT(getChild(i)))); + mTable.emplace(i->first, + isTile(i) ? NodeStruct(getTile(i)) : NodeStruct(*(new ChildT(getChild(i))))); } } return *this; @@ -1321,8 +1340,8 @@ inline typename RootNode::MapIter RootNode::findOrAddCoord(const Coord& xyz) { const Coord key = coordToKey(xyz); - std::pair result = mTable.insert( - typename MapType::value_type(key, NodeStruct(Tile(mBackground, /*active=*/false)))); + std::pair result = mTable.try_emplace(key, + Tile(mBackground, /*active=*/false)); return result.first; } @@ -1332,8 +1351,8 @@ inline bool RootNode::expand(const Coord& xyz) { const Coord key = coordToKey(xyz); - std::pair result = mTable.insert( - typename MapType::value_type(key, NodeStruct(Tile(mBackground, /*active=*/false)))); + std::pair result = mTable.try_emplace(key, + Tile(mBackground, /*active=*/false)); return result.second; // return true if the key did not already exist } @@ -1524,43 +1543,38 @@ RootNode::evalActiveBoundingBox(CoordBBox& bbox, bool visitVoxels) const template -inline Index -RootNode::getChildCount() const { - return this->childCount(); -} - - -template -inline Index -RootNode::getTileCount() const +inline Index64 +RootNode::leafCount() const { - Index sum = 0; + Index64 sum = 0; for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { - if (isTile(i)) ++sum; + if (isChild(i)) sum += getChild(i).leafCount(); } return sum; } template -inline Index -RootNode::getActiveTileCount() const +inline Index64 +RootNode::nonLeafCount() const { - Index sum = 0; - for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { - if (isTileOn(i)) ++sum; + Index64 sum = 1; + if (ChildT::LEVEL != 0) { + for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + if (isChild(i)) sum += getChild(i).nonLeafCount(); + } } return sum; } template -inline Index -RootNode::getInactiveTileCount() const +inline Index32 +RootNode::childCount() const { Index sum = 0; for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { - if (isTileOff(i)) ++sum; + if (isChild(i)) ++sum; } return sum; } @@ -1568,11 +1582,11 @@ RootNode::getInactiveTileCount() const template inline Index32 -RootNode::leafCount() const +RootNode::tileCount() const { Index32 sum = 0; for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { - if (isChild(i)) sum += getChild(i).leafCount(); + if (isTile(i)) ++sum; } return sum; } @@ -1580,13 +1594,11 @@ RootNode::leafCount() const template inline Index32 -RootNode::nonLeafCount() const +RootNode::activeTileCount() const { - Index32 sum = 1; - if (ChildT::LEVEL != 0) { - for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { - if (isChild(i)) sum += getChild(i).nonLeafCount(); - } + Index32 sum = 0; + for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + if (isTileOn(i)) ++sum; } return sum; } @@ -1594,11 +1606,11 @@ RootNode::nonLeafCount() const template inline Index32 -RootNode::childCount() const +RootNode::inactiveTileCount() const { - Index sum = 0; + Index32 sum = 0; for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { - if (isChild(i)) ++sum; + if (isTileOff(i)) ++sum; } return sum; } @@ -1674,6 +1686,22 @@ RootNode::onTileCount() const return sum; } +template +inline void +RootNode::nodeCount(std::vector &vec) const +{ + OPENVDB_ASSERT(vec.size() > LEVEL); + Index64 sum = 0; + for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + if (isChild(i)) { + ++sum; + getChild(i).nodeCount(vec); + } + } + vec[LEVEL] = 1;// one root node + vec[ChildNodeType::LEVEL] = sum; +} + template inline void RootNode::nodeCount(std::vector &vec) const @@ -1683,7 +1711,9 @@ RootNode::nodeCount(std::vector &vec) const for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { if (isChild(i)) { ++sum; + OPENVDB_NO_DEPRECATION_WARNING_BEGIN getChild(i).nodeCount(vec); + OPENVDB_NO_DEPRECATION_WARNING_END } } vec[LEVEL] = 1;// one root node @@ -1790,11 +1820,12 @@ inline void RootNode::setActiveState(const Coord& xyz, bool on) { ChildT* child = nullptr; - MapIter iter = this->findCoord(xyz); + const Coord key = this->coordToKey(xyz); + MapIter iter = this->findKey(key); if (iter == mTable.end()) { if (on) { child = new ChildT(xyz, mBackground); - mTable[this->coordToKey(xyz)] = NodeStruct(*child); + mTable.emplace(key, *child); } else { // Nothing to do; (x, y, z) is background and therefore already inactive. } @@ -1813,11 +1844,12 @@ inline void RootNode::setActiveStateAndCache(const Coord& xyz, bool on, AccessorT& acc) { ChildT* child = nullptr; - MapIter iter = this->findCoord(xyz); + const Coord key = this->coordToKey(xyz); + MapIter iter = this->findKey(key); if (iter == mTable.end()) { if (on) { child = new ChildT(xyz, mBackground); - mTable[this->coordToKey(xyz)] = NodeStruct(*child); + mTable.emplace(key, *child); } else { // Nothing to do; (x, y, z) is background and therefore already inactive. } @@ -1839,11 +1871,12 @@ inline void RootNode::setValueOff(const Coord& xyz, const ValueType& value) { ChildT* child = nullptr; - MapIter iter = this->findCoord(xyz); + const Coord key = this->coordToKey(xyz); + MapIter iter = this->findKey(key); if (iter == mTable.end()) { if (!math::isExactlyEqual(mBackground, value)) { child = new ChildT(xyz, mBackground); - mTable[this->coordToKey(xyz)] = NodeStruct(*child); + mTable.emplace(key, *child); } } else if (isChild(iter)) { child = &getChild(iter); @@ -1860,11 +1893,12 @@ inline void RootNode::setValueOffAndCache(const Coord& xyz, const ValueType& value, AccessorT& acc) { ChildT* child = nullptr; - MapIter iter = this->findCoord(xyz); + const Coord key = this->coordToKey(xyz); + MapIter iter = this->findKey(key); if (iter == mTable.end()) { if (!math::isExactlyEqual(mBackground, value)) { child = new ChildT(xyz, mBackground); - mTable[this->coordToKey(xyz)] = NodeStruct(*child); + mTable.emplace(key, *child); } } else if (isChild(iter)) { child = &getChild(iter); @@ -1884,10 +1918,11 @@ inline void RootNode::setValueOn(const Coord& xyz, const ValueType& value) { ChildT* child = nullptr; - MapIter iter = this->findCoord(xyz); + const Coord key = this->coordToKey(xyz); + MapIter iter = this->findKey(key); if (iter == mTable.end()) { child = new ChildT(xyz, mBackground); - mTable[this->coordToKey(xyz)] = NodeStruct(*child); + mTable.emplace(key, *child); } else if (isChild(iter)) { child = &getChild(iter); } else if (isTileOff(iter) || !math::isExactlyEqual(getTile(iter).value, value)) { @@ -1903,10 +1938,11 @@ inline void RootNode::setValueAndCache(const Coord& xyz, const ValueType& value, AccessorT& acc) { ChildT* child = nullptr; - MapIter iter = this->findCoord(xyz); + const Coord key = this->coordToKey(xyz); + MapIter iter = this->findKey(key); if (iter == mTable.end()) { child = new ChildT(xyz, mBackground); - mTable[this->coordToKey(xyz)] = NodeStruct(*child); + mTable.emplace(key, *child); } else if (isChild(iter)) { child = &getChild(iter); } else if (isTileOff(iter) || !math::isExactlyEqual(getTile(iter).value, value)) { @@ -1925,10 +1961,11 @@ inline void RootNode::setValueOnly(const Coord& xyz, const ValueType& value) { ChildT* child = nullptr; - MapIter iter = this->findCoord(xyz); + const Coord key = this->coordToKey(xyz); + MapIter iter = this->findKey(key); if (iter == mTable.end()) { child = new ChildT(xyz, mBackground); - mTable[this->coordToKey(xyz)] = NodeStruct(*child); + mTable.emplace(key, *child); } else if (isChild(iter)) { child = &getChild(iter); } else if (!math::isExactlyEqual(getTile(iter).value, value)) { @@ -1944,10 +1981,11 @@ inline void RootNode::setValueOnlyAndCache(const Coord& xyz, const ValueType& value, AccessorT& acc) { ChildT* child = nullptr; - MapIter iter = this->findCoord(xyz); + const Coord key = this->coordToKey(xyz); + MapIter iter = this->findKey(key); if (iter == mTable.end()) { child = new ChildT(xyz, mBackground); - mTable[this->coordToKey(xyz)] = NodeStruct(*child); + mTable.emplace(key, *child); } else if (isChild(iter)) { child = &getChild(iter); } else if (!math::isExactlyEqual(getTile(iter).value, value)) { @@ -1967,10 +2005,11 @@ inline void RootNode::modifyValue(const Coord& xyz, const ModifyOp& op) { ChildT* child = nullptr; - MapIter iter = this->findCoord(xyz); + const Coord key = this->coordToKey(xyz); + MapIter iter = this->findKey(key); if (iter == mTable.end()) { child = new ChildT(xyz, mBackground); - mTable[this->coordToKey(xyz)] = NodeStruct(*child); + mTable.emplace(key, *child); } else if (isChild(iter)) { child = &getChild(iter); } else { @@ -1999,10 +2038,11 @@ inline void RootNode::modifyValueAndCache(const Coord& xyz, const ModifyOp& op, AccessorT& acc) { ChildT* child = nullptr; - MapIter iter = this->findCoord(xyz); + const Coord key = this->coordToKey(xyz); + MapIter iter = this->findKey(key); if (iter == mTable.end()) { child = new ChildT(xyz, mBackground); - mTable[this->coordToKey(xyz)] = NodeStruct(*child); + mTable.emplace(key, *child); } else if (isChild(iter)) { child = &getChild(iter); } else { @@ -2035,10 +2075,11 @@ inline void RootNode::modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) { ChildT* child = nullptr; - MapIter iter = this->findCoord(xyz); + const Coord key = this->coordToKey(xyz); + MapIter iter = this->findKey(key); if (iter == mTable.end()) { child = new ChildT(xyz, mBackground); - mTable[this->coordToKey(xyz)] = NodeStruct(*child); + mTable.emplace(key, *child); } else if (isChild(iter)) { child = &getChild(iter); } else { @@ -2063,10 +2104,11 @@ RootNode::modifyValueAndActiveStateAndCache( const Coord& xyz, const ModifyOp& op, AccessorT& acc) { ChildT* child = nullptr; - MapIter iter = this->findCoord(xyz); + const Coord key = this->coordToKey(xyz); + MapIter iter = this->findKey(key); if (iter == mTable.end()) { child = new ChildT(xyz, mBackground); - mTable[this->coordToKey(xyz)] = NodeStruct(*child); + mTable.emplace(key, *child); } else if (isChild(iter)) { child = &getChild(iter); } else { @@ -2154,13 +2196,13 @@ RootNode::fill(const CoordBBox& bbox, const ValueType& value, bool activ // No child or tile exists. Create a child and initialize it // with the background value. child = new ChildT(xyz, mBackground); - mTable[tileMin] = NodeStruct(*child); + mTable.emplace(tileMin, *child); } else if (isTile(iter)) { // Replace the tile with a newly-created child that is filled // with the tile's value and active state. const Tile& tile = getTile(iter); child = new ChildT(xyz, tile.value, tile.active); - mTable[tileMin] = NodeStruct(*child); + setChild(iter, *child); } else if (isChild(iter)) { child = &getChild(iter); } @@ -2314,7 +2356,7 @@ RootNode::writeTopology(std::ostream& os, bool toHalf) const } io::setGridBackgroundValuePtr(os, &mBackground); - const Index numTiles = this->getTileCount(), numChildren = this->childCount(); + const Index numTiles = this->tileCount(), numChildren = this->childCount(); os.write(reinterpret_cast(&numTiles), sizeof(Index)); os.write(reinterpret_cast(&numChildren), sizeof(Index)); @@ -2361,7 +2403,6 @@ RootNode::readTopology(std::istream& is, bool fromHalf) is.read(reinterpret_cast(rangeMin.asPointer()), 3 * sizeof(Int32)); is.read(reinterpret_cast(rangeMax.asPointer()), 3 * sizeof(Int32)); - this->initTable(); Index tableSize = 0, log2Dim[4] = { 0, 0, 0, 0 }; Int32 offset[3]; for (int i = 0; i < 3; ++i) { @@ -2394,14 +2435,14 @@ RootNode::readTopology(std::istream& is, bool fromHalf) // Read in and insert a child node. ChildT* child = new ChildT(PartialCreate(), origin, mBackground); child->readTopology(is); - mTable[origin] = NodeStruct(*child); + mTable.emplace(origin, *child); } else { // Read in a tile value and insert a tile, but only if the value // is either active or non-background. ValueType value; is.read(reinterpret_cast(&value), sizeof(ValueType)); if (valueMask.isOn(i) || (!math::isApproxEqual(value, mBackground))) { - mTable[origin] = NodeStruct(Tile(value, valueMask.isOn(i))); + mTable.emplace(origin, Tile(value, valueMask.isOn(i))); } } } @@ -2428,7 +2469,7 @@ RootNode::readTopology(std::istream& is, bool fromHalf) is.read(reinterpret_cast(vec), 3 * sizeof(Int32)); is.read(reinterpret_cast(&value), sizeof(ValueType)); is.read(reinterpret_cast(&active), sizeof(bool)); - mTable[Coord(vec)] = NodeStruct(Tile(value, active)); + mTable.emplace(Coord(vec), Tile(value, active)); } // Read child nodes. @@ -2437,7 +2478,7 @@ RootNode::readTopology(std::istream& is, bool fromHalf) Coord origin(vec); ChildT* child = new ChildT(PartialCreate(), origin, mBackground); child->readTopology(is, fromHalf); - mTable[Coord(vec)] = NodeStruct(*child); + mTable.emplace(Coord(vec), *child); } return true; // not empty @@ -2575,14 +2616,15 @@ RootNode::addLeaf(LeafNodeType* leaf) if (leaf == nullptr) return; ChildT* child = nullptr; const Coord& xyz = leaf->origin(); - MapIter iter = this->findCoord(xyz); + const Coord key = this->coordToKey(xyz); + MapIter iter = this->findKey(key); if (iter == mTable.end()) { if (ChildT::LEVEL>0) { child = new ChildT(xyz, mBackground, false); } else { child = reinterpret_cast(leaf); } - mTable[this->coordToKey(xyz)] = NodeStruct(*child); + mTable.emplace(key, *child); } else if (isChild(iter)) { if (ChildT::LEVEL>0) { child = &getChild(iter); @@ -2610,14 +2652,15 @@ RootNode::addLeafAndCache(LeafNodeType* leaf, AccessorT& acc) if (leaf == nullptr) return; ChildT* child = nullptr; const Coord& xyz = leaf->origin(); - MapIter iter = this->findCoord(xyz); + const Coord key = this->coordToKey(xyz); + MapIter iter = this->findKey(key); if (iter == mTable.end()) { if (ChildT::LEVEL>0) { child = new ChildT(xyz, mBackground, false); } else { child = reinterpret_cast(leaf); } - mTable[this->coordToKey(xyz)] = NodeStruct(*child); + mTable.emplace(key, *child); } else if (isChild(iter)) { if (ChildT::LEVEL>0) { child = &getChild(iter); @@ -2643,34 +2686,34 @@ RootNode::addChild(ChildT* child) { if (!child) return false; const Coord& xyz = child->origin(); - MapIter iter = this->findCoord(xyz); + const Coord key = this->coordToKey(xyz); + MapIter iter = this->findKey(key); if (iter == mTable.end()) {//background - mTable[this->coordToKey(xyz)] = NodeStruct(*child); + mTable.emplace(key, *child); } else {//child or tile setChild(iter, *child);//this also deletes the existing child node } return true; } -#if OPENVDB_ABI_VERSION_NUMBER >= 10 template inline void RootNode::setOrigin(const Coord &origin) { - mOrigin = origin; - if (mOrigin != Coord(0,0,0)) { + if (origin != Coord(0,0,0)) { OPENVDB_THROW(ValueError, "RootNode::setOrigin: non-zero offsets are currently not supported"); } + mOrigin = origin; } -#endif template inline void RootNode::addTile(const Coord& xyz, const ValueType& value, bool state) { - MapIter iter = this->findCoord(xyz); + const Coord key = this->coordToKey(xyz); + MapIter iter = this->findKey(key); if (iter == mTable.end()) {//background - mTable[this->coordToKey(xyz)] = NodeStruct(Tile(value, state)); + mTable.emplace(key, Tile(value, state)); } else {//child or tile setTile(iter, Tile(value, state));//this also deletes the existing child node } @@ -2682,14 +2725,15 @@ RootNode::addTile(Index level, const Coord& xyz, const ValueType& value, bool state) { if (LEVEL >= level) { - MapIter iter = this->findCoord(xyz); + const Coord key = this->coordToKey(xyz); + MapIter iter = this->findKey(key); if (iter == mTable.end()) {//background if (LEVEL > level) { ChildT* child = new ChildT(xyz, mBackground, false); - mTable[this->coordToKey(xyz)] = NodeStruct(*child); + mTable.emplace(key, *child); child->addTile(level, xyz, value, state); } else { - mTable[this->coordToKey(xyz)] = NodeStruct(Tile(value, state)); + mTable.emplace(key, Tile(value, state)); } } else if (isChild(iter)) {//child if (LEVEL > level) { @@ -2717,15 +2761,16 @@ RootNode::addTileAndCache(Index level, const Coord& xyz, const ValueType bool state, AccessorT& acc) { if (LEVEL >= level) { - MapIter iter = this->findCoord(xyz); + const Coord key = this->coordToKey(xyz); + MapIter iter = this->findKey(key); if (iter == mTable.end()) {//background if (LEVEL > level) { ChildT* child = new ChildT(xyz, mBackground, false); acc.insert(xyz, child); - mTable[this->coordToKey(xyz)] = NodeStruct(*child); + mTable.emplace(key, *child); child->addTileAndCache(level, xyz, value, state, acc); } else { - mTable[this->coordToKey(xyz)] = NodeStruct(Tile(value, state)); + mTable.emplace(key, Tile(value, state)); } } else if (isChild(iter)) {//child if (LEVEL > level) { @@ -2749,6 +2794,22 @@ RootNode::addTileAndCache(Index level, const Coord& xyz, const ValueType } +template +inline bool +RootNode::deleteChildOrTile(const Coord& xyz) +{ + Coord key = this->coordToKey(xyz); + MapIter iter = this->findKey(key); + if (iter != mTable.end()) { + // the child must be deleted to prevent a memory leak + if (isChild(iter)) delete iter->second.child; + mTable.erase(iter); + return true; + } + return false; +} + + //////////////////////////////////////// @@ -2757,10 +2818,11 @@ inline typename ChildT::LeafNodeType* RootNode::touchLeaf(const Coord& xyz) { ChildT* child = nullptr; - MapIter iter = this->findCoord(xyz); + const Coord key = this->coordToKey(xyz); + MapIter iter = this->findKey(key); if (iter == mTable.end()) { child = new ChildT(xyz, mBackground, false); - mTable[this->coordToKey(xyz)] = NodeStruct(*child); + mTable.emplace(key, *child); } else if (isChild(iter)) { child = &getChild(iter); } else { @@ -2777,10 +2839,11 @@ inline typename ChildT::LeafNodeType* RootNode::touchLeafAndCache(const Coord& xyz, AccessorT& acc) { ChildT* child = nullptr; - MapIter iter = this->findCoord(xyz); + const Coord key = this->coordToKey(xyz); + MapIter iter = this->findKey(key); if (iter == mTable.end()) { child = new ChildT(xyz, mBackground, false); - mTable[this->coordToKey(xyz)] = NodeStruct(*child); + mTable.emplace(key, *child); } else if (isChild(iter)) { child = &getChild(iter); } else { @@ -2813,6 +2876,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* @@ -2831,6 +2903,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) @@ -2914,6 +3042,64 @@ RootNode::probeConstNodeAndCache(const Coord& xyz, AccessorT& acc) const //////////////////////////////////////// + +template +inline const typename ChildT::ValueType& +RootNode::getTileValueUnsafe(const Coord& xyz) const +{ + MapCIter iter = this->findCoord(xyz); + OPENVDB_ASSERT(iter != mTable.end()); + OPENVDB_ASSERT(isTile(iter)); + return getTile(iter).value; +} + + +template +inline bool +RootNode::getTileValueUnsafe(const Coord& xyz, ValueType& value) const +{ + MapCIter iter = this->findCoord(xyz); + OPENVDB_ASSERT(iter != mTable.end()); + OPENVDB_ASSERT(isTile(iter)); + const Tile& tile = getTile(iter); + value = tile.value; + return tile.active; +} + + +template +inline ChildT* +RootNode::getChildUnsafe(const Coord& xyz) +{ + MapIter iter = this->findCoord(xyz); + OPENVDB_ASSERT(iter != mTable.end()); + OPENVDB_ASSERT(isChild(iter)); + return &getChild(iter); +} + + +template +inline const ChildT* +RootNode::getConstChildUnsafe(const Coord& xyz) const +{ + MapCIter iter = this->findCoord(xyz); + OPENVDB_ASSERT(iter != mTable.end()); + OPENVDB_ASSERT(isChild(iter)); + return &getChild(iter); +} + + +template +inline const ChildT* +RootNode::getChildUnsafe(const Coord& xyz) const +{ + return this->getConstChildUnsafe(xyz); +} + + +//////////////////////////////////////// + + template template inline void @@ -3021,7 +3207,7 @@ RootNode::merge(RootNode& other) if (j == mTable.end()) { // insert other node's child ChildNodeType& child = stealChild(i, Tile(other.mBackground, /*on=*/false)); child.resetBackground(other.mBackground, mBackground); - mTable[i->first] = NodeStruct(child); + mTable.emplace(i->first, child); } else if (isTile(j)) { if (isTileOff(j)) { // replace inactive tile with other node's child ChildNodeType& child = stealChild(i, Tile(other.mBackground, /*on=*/false)); @@ -3034,7 +3220,7 @@ RootNode::merge(RootNode& other) } } else if (other.isTileOn(i)) { if (j == mTable.end()) { // insert other node's active tile - mTable[i->first] = i->second; + mTable.emplace(i->first, i->second); } else if (!isTileOn(j)) { // Replace anything except an active tile with the other node's active tile. setTile(j, Tile(other.getTile(i).value, true)); @@ -3050,7 +3236,7 @@ RootNode::merge(RootNode& other) if (j == mTable.end()) { // insert other node's child ChildNodeType& child = stealChild(i, Tile(other.mBackground, /*on=*/false)); child.resetBackground(other.mBackground, mBackground); - mTable[i->first] = NodeStruct(child); + mTable.emplace(i->first, child); } else if (isTile(j)) { // replace tile with other node's child ChildNodeType& child = stealChild(i, Tile(other.mBackground, /*on=*/false)); child.resetBackground(other.mBackground, mBackground); @@ -3071,7 +3257,7 @@ RootNode::merge(RootNode& other) // Steal and insert the other node's child. ChildNodeType& child = stealChild(i, Tile(other.mBackground, /*on=*/false)); child.resetBackground(other.mBackground, mBackground); - mTable[i->first] = NodeStruct(child); + mTable.emplace(i->first, child); } else if (isTile(j)) { // Replace this node's tile with the other node's child. ChildNodeType& child = stealChild(i, Tile(other.mBackground, /*on=*/false)); @@ -3091,7 +3277,7 @@ RootNode::merge(RootNode& other) } else if (other.isTileOn(i)) { if (j == mTable.end()) { // Insert a copy of the other node's active tile. - mTable[i->first] = i->second; + mTable.emplace(i->first, i->second); } else if (isTileOff(j)) { // Replace this node's inactive tile with a copy of the other's active tile. setTile(j, Tile(other.getTile(i).value, true)); @@ -3130,7 +3316,7 @@ RootNode::topologyUnion(const RootNode& other, const boo MapIter j = mTable.find(i->first); if (other.isChild(i)) { if (j == mTable.end()) { // create child branch with identical topology - mTable[i->first] = NodeStruct( + mTable.emplace(i->first, *(new ChildT(other.getChild(i), mBackground, TopologyCopy()))); } else if (this->isChild(j)) { // union with child branch this->getChild(j).topologyUnion(other.getChild(i), preserveTiles); @@ -3144,7 +3330,7 @@ RootNode::topologyUnion(const RootNode& other, const boo } } else if (other.isTileOn(i)) { // other is an active tile if (j == mTable.end()) { // insert an active tile - mTable[i->first] = NodeStruct(Tile(mBackground, true)); + mTable.emplace(i->first, Tile(mBackground, true)); } else if (this->isChild(j)) { this->getChild(j).setValuesOn(); } else if (this->isTileOff(j)) { diff --git a/openvdb/openvdb/tree/Tree.h b/openvdb/openvdb/tree/Tree.h index 06f8278827..c2c6ffa9f0 100644 --- a/openvdb/openvdb/tree/Tree.h +++ b/openvdb/openvdb/tree/Tree.h @@ -102,7 +102,11 @@ class OPENVDB_API TreeBase /// @sa readNonresidentBuffers, io::File::open virtual void clipUnallocatedNodes() = 0; /// Return the total number of unallocated leaf nodes residing in this tree. +#if OPENVDB_ABI_VERSION_NUMBER >= 12 + virtual Index64 unallocatedLeafCount() const = 0; +#else virtual Index32 unallocatedLeafCount() const = 0; +#endif // @@ -113,13 +117,25 @@ class OPENVDB_API TreeBase /// A tree with only a root node and leaf nodes has depth 2, for example. virtual Index treeDepth() const = 0; /// Return the number of leaf nodes. +#if OPENVDB_ABI_VERSION_NUMBER >= 12 + virtual Index64 leafCount() const = 0; +#else virtual Index32 leafCount() const = 0; +#endif /// Return a vector with node counts. The number of nodes of type NodeType /// is given as element NodeType::LEVEL in the return vector. Thus, the size /// of this vector corresponds to the height (or depth) of this tree. +#if OPENVDB_ABI_VERSION_NUMBER >= 12 + virtual std::vector nodeCount() const = 0; +#else virtual std::vector nodeCount() const = 0; +#endif /// Return the number of non-leaf nodes. +#if OPENVDB_ABI_VERSION_NUMBER >= 12 + virtual Index64 nonLeafCount() const = 0; +#else virtual Index32 nonLeafCount() const = 0; +#endif /// Return the number of active voxels stored in leaf nodes. virtual Index64 activeLeafVoxelCount() const = 0; /// Return the number of inactive voxels stored in leaf nodes. @@ -183,6 +199,7 @@ class Tree: public TreeBase using RootNodeType = _RootNodeType; using ValueType = typename RootNodeType::ValueType; + using ComputeType = typename RootNodeType::ComputeType; using BuildType = typename RootNodeType::BuildType; using LeafNodeType = typename RootNodeType::LeafNodeType; @@ -343,18 +360,37 @@ class Tree: public TreeBase /// A tree with only a root node and leaf nodes has depth 2, for example. Index treeDepth() const override { return DEPTH; } /// Return the number of leaf nodes. - Index32 leafCount() const override { return mRoot.leafCount(); } +#if OPENVDB_ABI_VERSION_NUMBER >= 12 + Index64 leafCount() const override { return mRoot.leafCount(); } +#else + Index32 leafCount() const override { return static_cast(mRoot.leafCount()); } +#endif /// Return a vector with node counts. The number of nodes of type NodeType /// is given as element NodeType::LEVEL in the return vector. Thus, the size /// of this vector corresponds to the height (or depth) of this tree. +#if OPENVDB_ABI_VERSION_NUMBER >= 12 + std::vector nodeCount() const override + { + std::vector vec(DEPTH, 0); + mRoot.nodeCount( vec ); + return vec;// Named Return Value Optimization + } +#else std::vector nodeCount() const override { std::vector vec(DEPTH, 0); + OPENVDB_NO_DEPRECATION_WARNING_BEGIN mRoot.nodeCount( vec ); + OPENVDB_NO_DEPRECATION_WARNING_END return vec;// Named Return Value Optimization } +#endif /// Return the number of non-leaf nodes. - Index32 nonLeafCount() const override { return mRoot.nonLeafCount(); } +#if OPENVDB_ABI_VERSION_NUMBER >= 12 + Index64 nonLeafCount() const override { return mRoot.nonLeafCount(); } +#else + Index32 nonLeafCount() const override { return static_cast(mRoot.nonLeafCount()); } +#endif /// Return the number of active voxels stored in leaf nodes. Index64 activeLeafVoxelCount() const override { return tools::countActiveLeafVoxels(*this); } /// Return the number of inactive voxels stored in leaf nodes. @@ -469,7 +505,11 @@ class Tree: public TreeBase void clipUnallocatedNodes() override; /// Return the total number of unallocated leaf nodes residing in this tree. +#if OPENVDB_ABI_VERSION_NUMBER >= 12 + Index64 unallocatedLeafCount() const override; +#else Index32 unallocatedLeafCount() const override; +#endif //@{ /// @brief Set all voxels within a given axis-aligned box to a constant value. @@ -1673,6 +1713,16 @@ Tree::clipUnallocatedNodes() } } +#if OPENVDB_ABI_VERSION_NUMBER >= 12 +template +inline Index64 +Tree::unallocatedLeafCount() const +{ + Index64 sum = 0; + for (auto it = this->cbeginLeaf(); it; ++it) if (!it->isAllocated()) ++sum; + return sum; +} +#else template inline Index32 Tree::unallocatedLeafCount() const @@ -1681,6 +1731,7 @@ Tree::unallocatedLeafCount() const for (auto it = this->cbeginLeaf(); it; ++it) if (!it->isAllocated()) ++sum; return sum; } +#endif template @@ -2036,7 +2087,7 @@ Tree::print(std::ostream& os, int verboseLevel) const } const auto nodeCount = this->nodeCount();//fast - const Index32 leafCount = nodeCount.front();// leaf is the first element + const Index64 leafCount = nodeCount.front();// leaf is the first element OPENVDB_ASSERT(dims.size() == nodeCount.size()); Index64 totalNodeCount = 0; diff --git a/openvdb/openvdb/tree/ValueAccessor.h b/openvdb/openvdb/tree/ValueAccessor.h index 542ff0d1b3..8c3cdfd405 100644 --- a/openvdb/openvdb/tree/ValueAccessor.h +++ b/openvdb/openvdb/tree/ValueAccessor.h @@ -374,6 +374,7 @@ class ValueAccessorImpl final : using TreeType = _TreeType; using ValueType = typename TreeType::ValueType; + using ComputeType = typename TreeType::ComputeType; using RootNodeT = typename TreeType::RootNodeType; using LeafNodeT = typename TreeType::LeafNodeType; using NodeChainT = typename RootNodeT::NodeChainType; diff --git a/openvdb/openvdb/unittest/CMakeLists.txt b/openvdb/openvdb/unittest/CMakeLists.txt index a8739faa0a..b1e04ebcfe 100644 --- a/openvdb/openvdb/unittest/CMakeLists.txt +++ b/openvdb/openvdb/unittest/CMakeLists.txt @@ -7,7 +7,7 @@ #]=======================================================================] -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.20) project(OpenVDBUnitTests LANGUAGES CXX) include(GNUInstallDirs) @@ -111,6 +111,7 @@ else() TestInt32Metadata.cc TestInt64Metadata.cc TestInternalOrigin.cc + TestInternalNode.cc TestLaplacian.cc TestLeaf.cc TestLeafBool.cc @@ -118,6 +119,7 @@ else() TestLeafManager.cc TestLeafMask.cc TestLeafOrigin.cc + TestLevelSetFilter.cc TestLevelSetRayIntersector.cc TestLevelSetUtil.cc TestLinearInterp.cc @@ -166,6 +168,7 @@ else() TestQuantizedUnitVec.cc TestQuat.cc TestRay.cc + TestRootNode.cc TestStats.cc TestStencils.cc TestStream.cc diff --git a/openvdb/openvdb/unittest/TestFile.cc b/openvdb/openvdb/unittest/TestFile.cc index c5d4d85d9a..dd344cb515 100644 --- a/openvdb/openvdb/unittest/TestFile.cc +++ b/openvdb/openvdb/unittest/TestFile.cc @@ -1866,7 +1866,7 @@ TEST_F(TestFile, testMultiPassIO) file.open(); const auto newGrid = GridBase::grid( file.readGrid("test", BBoxd(Vec3d(0), Vec3d(1)))); - EXPECT_EQ(Index32(1), newGrid->tree().leafCount()); + EXPECT_EQ(Index64(1), newGrid->tree().leafCount()); auto leafIter = newGrid->tree().beginLeaf(); EXPECT_EQ(3, int(leafIter->mReadPasses.size())); diff --git a/openvdb/openvdb/unittest/TestFilter.cc b/openvdb/openvdb/unittest/TestFilter.cc index 0d3006f56c..12f9cdedd6 100644 --- a/openvdb/openvdb/unittest/TestFilter.cc +++ b/openvdb/openvdb/unittest/TestFilter.cc @@ -245,7 +245,7 @@ TEST_F(TestFilter, testFilterTiles) openvdb::FloatGrid::Ptr ref = openvdb::FloatGrid::create(0.0f); auto& tree = ref->tree(); tree.addTile(test.mLevel, Coord(0), 1.0f, true); - EXPECT_EQ(Index32(0), tree.leafCount()); + EXPECT_EQ(Index64(0), tree.leafCount()); EXPECT_EQ(Index64(1), tree.activeTileCount()); EXPECT_EQ(test.mVoxels, tree.activeVoxelCount()); EXPECT_EQ(1.0f, tree.getValue(Coord(0))); @@ -262,7 +262,7 @@ TEST_F(TestFilter, testFilterTiles) // disable tile processing, do nothing filter.setProcessTiles(false); filter.offset(1.0f); - EXPECT_EQ(Index32(0), tree.leafCount()); + EXPECT_EQ(Index64(0), tree.leafCount()); EXPECT_EQ(Index64(1), tree.activeTileCount()); EXPECT_EQ(test.mVoxels, tree.activeVoxelCount()); EXPECT_EQ(1.0f, tree.getValue(Coord(0))); @@ -271,7 +271,7 @@ TEST_F(TestFilter, testFilterTiles) // enable filter.setProcessTiles(true); filter.offset(1.0f); - EXPECT_EQ(Index32(0), tree.leafCount()); + EXPECT_EQ(Index64(0), tree.leafCount()); EXPECT_EQ(Index64(1), tree.activeTileCount()); EXPECT_EQ(test.mVoxels, tree.activeVoxelCount()); EXPECT_EQ(2.0f, tree.getValue(Coord(0))); @@ -285,7 +285,7 @@ TEST_F(TestFilter, testFilterTiles) // disable tile processing, do nothing filter.setProcessTiles(false); filter.mean(width, iter); - EXPECT_EQ(Index32(0), tree.leafCount()); + EXPECT_EQ(Index64(0), tree.leafCount()); EXPECT_EQ(Index64(1), tree.activeTileCount()); EXPECT_EQ(test.mVoxels, tree.activeVoxelCount()); EXPECT_EQ(1.0f, tree.getValue(Coord(0))); @@ -308,7 +308,7 @@ TEST_F(TestFilter, testFilterTiles) // disable tile processing, do nothing filter.setProcessTiles(false); filter.median(width, iter); - EXPECT_EQ(Index32(0), tree.leafCount()); + EXPECT_EQ(Index64(0), tree.leafCount()); EXPECT_EQ(Index64(1), tree.activeTileCount()); EXPECT_EQ(test.mVoxels, tree.activeVoxelCount()); EXPECT_EQ(1.0f, tree.getValue(Coord(0))); @@ -339,7 +339,7 @@ TEST_F(TestFilter, testFilterTiles) openvdb::FloatGrid::Ptr ref = openvdb::FloatGrid::create(1.0f); auto& tree = ref->tree(); tree.addTile(test.mLevel, Coord(0), 1.0f, true); - EXPECT_EQ(Index32(0), tree.leafCount()); + EXPECT_EQ(Index64(0), tree.leafCount()); EXPECT_EQ(Index64(1), tree.activeTileCount()); EXPECT_EQ(test.mVoxels, tree.activeVoxelCount()); EXPECT_EQ(1.0f, tree.getValue(Coord(0))); @@ -355,7 +355,7 @@ TEST_F(TestFilter, testFilterTiles) openvdb::tools::Filter filter(*grid); filter.setProcessTiles(true); filter.mean(width, iter); - EXPECT_EQ(Index32(0), tree.leafCount()); + EXPECT_EQ(Index64(0), tree.leafCount()); EXPECT_EQ(Index64(1), tree.activeTileCount()); EXPECT_EQ(test.mVoxels, tree.activeVoxelCount()); EXPECT_EQ(1.0f, tree.getValue(Coord(0))); @@ -368,7 +368,7 @@ TEST_F(TestFilter, testFilterTiles) openvdb::tools::Filter filter(*grid); filter.setProcessTiles(true); filter.median(width, iter); - EXPECT_EQ(Index32(0), tree.leafCount()); + EXPECT_EQ(Index64(0), tree.leafCount()); EXPECT_EQ(Index64(1), tree.activeTileCount()); EXPECT_EQ(test.mVoxels, tree.activeVoxelCount()); EXPECT_EQ(1.0f, tree.getValue(Coord(0))); @@ -387,7 +387,7 @@ TEST_F(TestFilter, testFilterTiles) openvdb::FloatGrid::Ptr ref = openvdb::FloatGrid::create(1.0f); auto& tree = ref->tree(); tree.addTile(1, Coord(0), 1.0f, true); - EXPECT_EQ(Index32(0), tree.leafCount()); + EXPECT_EQ(Index64(0), tree.leafCount()); EXPECT_EQ(Index64(1), tree.activeTileCount()); EXPECT_EQ(Index64(LeafT::NUM_VALUES), tree.activeVoxelCount()); EXPECT_EQ(1.0f, tree.getValue(Coord(0))); @@ -402,7 +402,7 @@ TEST_F(TestFilter, testFilterTiles) // filter.setProcessTiles(true); filter.mean(1, 1); - EXPECT_EQ(Index32(0), tree.leafCount()); + EXPECT_EQ(Index64(0), tree.leafCount()); EXPECT_EQ(Index64(1), tree.activeTileCount()); EXPECT_EQ(Index64(LeafT::NUM_VALUES), tree.activeVoxelCount()); EXPECT_EQ(1.0f, tree.getValue(Coord(0))); @@ -410,11 +410,11 @@ TEST_F(TestFilter, testFilterTiles) // create leaf neighbour tree.touchLeaf(Coord(-1,0,0)); - EXPECT_EQ(Index32(1), tree.leafCount()); + EXPECT_EQ(Index64(1), tree.leafCount()); EXPECT_EQ(Index64(1), tree.activeTileCount()); filter.mean(1, 1); - EXPECT_EQ(Index32(2), tree.leafCount()); + EXPECT_EQ(Index64(2), tree.leafCount()); EXPECT_EQ(Index64(0), tree.activeTileCount()); EXPECT_EQ(Index64(LeafT::NUM_VALUES), tree.activeVoxelCount()); } @@ -426,18 +426,18 @@ TEST_F(TestFilter, testFilterTiles) openvdb::FloatGrid::Ptr ref = openvdb::FloatGrid::create(1.0f); auto& tree = ref->tree(); tree.addTile(level, Coord(0), 1.0f, true); - EXPECT_EQ(Index32(0), tree.leafCount()); + EXPECT_EQ(Index64(0), tree.leafCount()); EXPECT_EQ(Index64(1), tree.activeTileCount()); EXPECT_EQ(1.0f, tree.getValue(Coord(0))); EXPECT_TRUE(tree.isValueOn(Coord(0))); // create a leaf and tile neighbour tree.touchLeaf(Coord(-int(LeafT::DIM),0,0)); - EXPECT_EQ(Index32(1), tree.leafCount()); + EXPECT_EQ(Index64(1), tree.leafCount()); EXPECT_EQ(Index64(1), tree.activeTileCount()); // create tile level 1 neighbour with a different value tree.addTile(1, Coord(-int(LeafT::DIM),0,LeafT::DIM*3), 2.0f, true); - EXPECT_EQ(Index32(1), tree.leafCount()); + EXPECT_EQ(Index64(1), tree.leafCount()); EXPECT_EQ(Index64(2), tree.activeTileCount()); return ref; }; @@ -453,7 +453,7 @@ TEST_F(TestFilter, testFilterTiles) // (+ itself becomes a leaf) filter.mean(/*width*/LeafT::DIM+1, /*iter*/1); // 2 leaf nodes from the tile/leaf neighbours + their neighbours - EXPECT_EQ(Index32(2+4+5), tree.leafCount()); + EXPECT_EQ(Index64(2+4+5), tree.leafCount()); EXPECT_EQ((Index64(InternalNode1::NUM_VALUES) - 1) + (Index64(InternalNode2::NUM_VALUES) - (4+5)), tree.activeTileCount()); EXPECT_EQ(Index64(InternalNode1::NUM_VOXELS) + @@ -467,7 +467,7 @@ TEST_F(TestFilter, testFilterTiles) filter.setProcessTiles(true); // with width = 2 and iter = 2, edge/vertex neighbours should also be voxelized filter.mean(/*width*/2, /*iter*/2); - EXPECT_EQ(Index32(2+4+6), tree.leafCount()); + EXPECT_EQ(Index64(2+4+6), tree.leafCount()); EXPECT_EQ((Index64(InternalNode1::NUM_VALUES) - 1) + (Index64(InternalNode2::NUM_VALUES) - (4+6)), tree.activeTileCount()); EXPECT_EQ(Index64(InternalNode1::NUM_VOXELS) + @@ -481,7 +481,7 @@ TEST_F(TestFilter, testFilterTiles) filter.setProcessTiles(true); // with width = 1 and iter = 9 - checks an iter count > LeafT::DIM filter.mean(/*width*/1, /*iter*/LeafT::DIM+1); - EXPECT_EQ(Index32(38), tree.leafCount()); + EXPECT_EQ(Index64(38), tree.leafCount()); EXPECT_EQ((Index64(InternalNode2::NUM_VALUES) - 36), tree.activeTileCount()); EXPECT_EQ(Index64(InternalNode2::NUM_VOXELS) + Index64(LeafT::NUM_VOXELS), tree.activeVoxelCount()); diff --git a/openvdb/openvdb/unittest/TestGrid.cc b/openvdb/openvdb/unittest/TestGrid.cc index d7d6056bb5..f96ff63ea2 100644 --- a/openvdb/openvdb/unittest/TestGrid.cc +++ b/openvdb/openvdb/unittest/TestGrid.cc @@ -29,6 +29,7 @@ class ProxyTree: public openvdb::TreeBase public: using ValueType = int; using BuildType = int; + using ComputeType = int; using LeafNodeType = void; using ValueAllCIter = void; using ValueAllIter = void; @@ -75,7 +76,11 @@ class ProxyTree: public openvdb::TreeBase void prune(const ValueType& = 0) {} void clip(const openvdb::CoordBBox&) {} void clipUnallocatedNodes() override {} +#if OPENVDB_ABI_VERSION_NUMBER >= 12 + openvdb::Index64 unallocatedLeafCount() const override { return 0; } +#else openvdb::Index32 unallocatedLeafCount() const override { return 0; } +#endif void getIndexRange(openvdb::CoordBBox&) const override {} bool evalLeafBoundingBox(openvdb::CoordBBox& bbox) const override @@ -88,10 +93,17 @@ class ProxyTree: public openvdb::TreeBase { dim = openvdb::Coord(0, 0, 0); return false; } openvdb::Index treeDepth() const override { return 0; } - openvdb::Index leafCount() const override { return 0; } +#if OPENVDB_ABI_VERSION_NUMBER >= 12 + openvdb::Index64 leafCount() const override { return 0; } + std::vector nodeCount() const override + { return std::vector(DEPTH, 0); } + openvdb::Index64 nonLeafCount() const override { return 0; } +#else + openvdb::Index32 leafCount() const override { return 0; } std::vector nodeCount() const override { return std::vector(DEPTH, 0); } - openvdb::Index nonLeafCount() const override { return 0; } + openvdb::Index32 nonLeafCount() const override { return 0; } +#endif openvdb::Index64 activeVoxelCount() const override { return 0UL; } openvdb::Index64 inactiveVoxelCount() const override { return 0UL; } openvdb::Index64 activeLeafVoxelCount() const override { return 0UL; } @@ -270,7 +282,7 @@ TEST_F(TestGrid, testCopyGrid) // shallow-copy a const grid but supply a new transform and meta map EXPECT_EQ(1.0, grid1->transform().voxelSize().x()); EXPECT_EQ(size_t(0), grid1->metaCount()); - EXPECT_EQ(Index(2), grid1->tree().leafCount()); + EXPECT_EQ(Index64(2), grid1->tree().leafCount()); math::Transform::Ptr xform(math::Transform::createLinearTransform(/*voxelSize=*/0.25)); MetaMap meta; @@ -283,7 +295,7 @@ TEST_F(TestGrid, testCopyGrid) EXPECT_EQ(0.25, grid3->transform().voxelSize().x()); EXPECT_EQ(size_t(1), grid3->metaCount()); - EXPECT_EQ(Index(2), tree3.leafCount()); + EXPECT_EQ(Index64(2), tree3.leafCount()); EXPECT_EQ(long(3), constGrid1->constTreePtr().use_count()); } diff --git a/openvdb/openvdb/unittest/TestIndexFilter.cc b/openvdb/openvdb/unittest/TestIndexFilter.cc index a9aff3f378..3389511d85 100644 --- a/openvdb/openvdb/unittest/TestIndexFilter.cc +++ b/openvdb/openvdb/unittest/TestIndexFilter.cc @@ -157,7 +157,7 @@ TEST_F(TestIndexFilter, testActiveFilter) // check there are two leafs - EXPECT_EQ(Index32(2), points->tree().leafCount()); + EXPECT_EQ(Index64(2), points->tree().leafCount()); ActiveFilter activeFilter; InactiveFilter inActiveFilter; @@ -551,7 +551,7 @@ TEST_F(TestIndexFilter, testAttributeHashFilter) // four points, two leafs - EXPECT_EQ(tree.leafCount(), Index32(2)); + EXPECT_EQ(tree.leafCount(), Index64(2)); appendAttribute(tree, "id"); @@ -846,7 +846,7 @@ TEST_F(TestIndexFilter, testBBoxFilter) PointDataTree& tree = grid->tree(); // check one leaf per point - EXPECT_EQ(tree.leafCount(), Index32(2)); + EXPECT_EQ(tree.leafCount(), Index64(2)); // build some bounding box filters to test diff --git a/openvdb/openvdb/unittest/TestInternalNode.cc b/openvdb/openvdb/unittest/TestInternalNode.cc new file mode 100644 index 0000000000..14b56d6325 --- /dev/null +++ b/openvdb/openvdb/unittest/TestInternalNode.cc @@ -0,0 +1,333 @@ + +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include + +using namespace openvdb; +using namespace openvdb::tree; + + +class TestInternalNode: public ::testing::Test +{ +}; + + +TEST_F(TestInternalNode, test) +{ + const Coord c0(1000, 1000, 1000); + const Coord c1(896, 896, 896); + + using LeafNodeType = openvdb::tree::LeafNode; + using InternalNodeType = openvdb::tree::InternalNode; + using ChildType = LeafNodeType; + + { // test inserting child nodes directly and indirectly + Coord c2 = c1.offsetBy(8,0,0); + Coord c3 = c1.offsetBy(16,16,16); + + InternalNodeType internalNode(c1, 0.0f); + internalNode.touchLeaf(c2); + internalNode.touchLeaf(c3); + + EXPECT_EQ(Index(2), internalNode.leafCount()); + EXPECT_EQ(Index32(2), internalNode.childCount()); + EXPECT_TRUE(!internalNode.hasActiveTiles()); + + { // verify c0 and c1 are the root node coordinates + auto childIter = internalNode.cbeginChildOn(); + EXPECT_EQ(c2, childIter.getCoord()); + ++childIter; + EXPECT_EQ(c3, childIter.getCoord()); + } + + // copy the internal node + InternalNodeType internalNodeCopy(internalNode); + + // steal the internal node children leaving it empty again + std::vector children; + internalNode.stealNodes(children, 0.0f, false); + EXPECT_EQ(Index(0), internalNode.leafCount()); + EXPECT_EQ(Index32(0), internalNode.childCount()); + + // insert the root node children directly + for (ChildType* child : children) { + internalNode.addChild(child); + } + EXPECT_EQ(Index(2), internalNode.leafCount()); + EXPECT_EQ(Index32(2), internalNode.childCount()); + + { // verify the coordinates of the root node children + auto childIter = internalNode.cbeginChildOn(); + EXPECT_EQ(c2, childIter.getCoord()); + ++childIter; + EXPECT_EQ(c3, childIter.getCoord()); + } + } + + { // test inserting a tile and replacing with a child node + InternalNodeType internalNode(c1, 0.0f); + EXPECT_TRUE(!internalNode.hasActiveTiles()); + EXPECT_EQ(Index(0), internalNode.leafCount()); + EXPECT_EQ(Index32(0), internalNode.childCount()); + + // add a tile + internalNode.addTile(Index(0), /*value=*/1.0f, /*state=*/true); + EXPECT_TRUE(internalNode.hasActiveTiles()); + EXPECT_EQ(Index(0), internalNode.leafCount()); + EXPECT_EQ(Index32(0), internalNode.childCount()); + + // replace the tile with a child node + EXPECT_TRUE(internalNode.addChild(new ChildType(c1, 2.0f))); + EXPECT_TRUE(!internalNode.hasActiveTiles()); + EXPECT_EQ(Index(1), internalNode.leafCount()); + EXPECT_EQ(Index32(1), internalNode.childCount()); + EXPECT_EQ(c1, internalNode.cbeginChildOn().getCoord()); + EXPECT_NEAR(internalNode.cbeginChildOn()->getValue(0), 2.0f, /*tolerance=*/0.0); + + // replace the child node with another child node + EXPECT_TRUE(internalNode.addChild(new ChildType(c1, 3.0f))); + EXPECT_NEAR(internalNode.cbeginChildOn()->getValue(0), 3.0f, /*tolerance=*/0.0); + } + + { // test inserting child nodes that do and do not belong to the internal node + InternalNodeType internalNode(c1, 0.0f); + + // succeed if child belongs to this internal node + EXPECT_TRUE(internalNode.addChild(new ChildType(c0.offsetBy(8,0,0)))); + EXPECT_TRUE(internalNode.probeLeaf(c0.offsetBy(8,0,0))); + Index index1 = internalNode.coordToOffset(c0); + Index index2 = internalNode.coordToOffset(c0.offsetBy(8,0,0)); + EXPECT_TRUE(!internalNode.isChildMaskOn(index1)); + EXPECT_TRUE(internalNode.isChildMaskOn(index2)); + + // fail otherwise + auto* child = new ChildType(c0.offsetBy(8000,0,0)); + EXPECT_TRUE(!internalNode.addChild(child)); + delete child; + } + + { // test transient data + InternalNodeType internalNode(c1, 0.0f); + EXPECT_EQ(Index32(0), internalNode.transientData()); + internalNode.setTransientData(Index32(5)); + EXPECT_EQ(Index32(5), internalNode.transientData()); + InternalNodeType internalNode2(internalNode); + EXPECT_EQ(Index32(5), internalNode2.transientData()); + InternalNodeType internalNode3 = internalNode; + 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; + } +} + +TEST_F(TestInternalNode, testUnsafe) +{ + 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, false); // (0, 128, 4224) + + auto* child = new InternalNode::ChildNodeType(Coord(0, 256, 4096), 5.0f, true); + EXPECT_TRUE(internalNode.addChild(child)); // always returns true + + { // get value + + EXPECT_EQ(internalNode.getValueUnsafe(32), 3.0f); + EXPECT_EQ(internalNode.getValueUnsafe(33), 4.0f); + + float value = -1.0f; + EXPECT_TRUE(internalNode.getValueUnsafe(32, value)); + EXPECT_EQ(value, 3.0f); value = -1.0f; + EXPECT_FALSE(internalNode.getValueUnsafe(33, value)); + EXPECT_EQ(value, 4.0f); value = -1.0f; + } + + { // set value and active state + EXPECT_TRUE(internalNode.isValueOn(32)); + internalNode.setValueOffUnsafe(32); + EXPECT_TRUE(internalNode.isValueOff(32)); + internalNode.setValueOnUnsafe(32); + EXPECT_TRUE(internalNode.isValueOn(32)); + internalNode.setActiveStateUnsafe(32, false); + EXPECT_TRUE(internalNode.isValueOff(32)); + internalNode.setActiveStateUnsafe(32, true); + EXPECT_TRUE(internalNode.isValueOn(32)); + + internalNode.setValueOnlyUnsafe(32, 5.0f); + EXPECT_EQ(internalNode.getValueUnsafe(32), 5.0f); + EXPECT_TRUE(internalNode.isValueOn(32)); + internalNode.setValueOffUnsafe(32); + EXPECT_TRUE(internalNode.isValueOff(32)); + internalNode.setValueOnUnsafe(32); + EXPECT_TRUE(internalNode.isValueOn(32)); + + internalNode.setValueOnUnsafe(33, 7.0f); + EXPECT_TRUE(internalNode.isValueOn(33)); + EXPECT_EQ(internalNode.getValueUnsafe(33), 7.0f); + internalNode.setValueOffUnsafe(33, 6.0f); + EXPECT_TRUE(internalNode.isValueOff(33)); + EXPECT_EQ(internalNode.getValueUnsafe(33), 6.0f); + } + + { // get child + auto* node1 = internalNode.getChildUnsafe(64); + EXPECT_TRUE(bool(node1)); + const InternalNode& constInternalNode = internalNode; + auto* node2 = constInternalNode.getChildUnsafe(64); + EXPECT_TRUE(bool(node2)); + auto* node3 = internalNode.getConstChildUnsafe(64); + EXPECT_TRUE(bool(node3)); + } + + { // set child + auto* child1 = new InternalNode::ChildNodeType(Coord(0, 128, 0), 8.0f, true); + internalNode.setChildUnsafe(32, child1); + auto* node1 = internalNode.getChildUnsafe(32); + EXPECT_TRUE(node1); + EXPECT_EQ(node1->origin(), Coord(0, 128, 0)); + + auto* child2 = new InternalNode::ChildNodeType(Coord(0, 256, 0), 9.0f, true); + internalNode.resetChildUnsafe(64, child2); + auto* node2 = internalNode.getChildUnsafe(64); + EXPECT_TRUE(node2); + EXPECT_EQ(node2->origin(), Coord(0, 256, 0)); + + auto* node3 = internalNode.stealChildUnsafe(64, 12.0f, false); + EXPECT_TRUE(node3); + EXPECT_EQ(node3->origin(), Coord(0, 256, 0)); + delete node3; + EXPECT_EQ(internalNode.getValueUnsafe(64), 12.0f); + EXPECT_TRUE(internalNode.isValueOff(64)); + + internalNode.deleteChildUnsafe(32, 13.0f, true); + EXPECT_EQ(internalNode.getValueUnsafe(32), 13.0f); + EXPECT_TRUE(internalNode.isValueOn(32)); + } +} diff --git a/openvdb/openvdb/unittest/TestLeaf.cc b/openvdb/openvdb/unittest/TestLeaf.cc index 96ac1f04c1..ee6f6324ad 100644 --- a/openvdb/openvdb/unittest/TestLeaf.cc +++ b/openvdb/openvdb/unittest/TestLeaf.cc @@ -508,8 +508,8 @@ TEST_F(TestLeaf, testCount) EXPECT_EQ(Index(512), leaf.numValues()); EXPECT_EQ(Index(0), leaf.getLevel()); EXPECT_EQ(Index(1), leaf.getChildDim()); - EXPECT_EQ(Index(1), leaf.leafCount()); - EXPECT_EQ(Index(0), leaf.nonLeafCount()); + EXPECT_EQ(Index64(1), leaf.leafCount()); + EXPECT_EQ(Index64(0), leaf.nonLeafCount()); EXPECT_EQ(Index(0), leaf.childCount()); std::vector dims; @@ -533,3 +533,41 @@ TEST_F(TestLeaf, testTransientData) LeafT leaf3 = leaf; EXPECT_EQ(Index32(5), leaf3.transientData()); } + +TEST_F(TestLeaf, testUnsafe) +{ + using namespace openvdb; + using LeafT = tree::LeafNode; + const Coord origin(-9, -2, -8); + LeafT leaf(origin, 1.0f, false); + + EXPECT_FALSE(leaf.isValueOn(1)); + EXPECT_TRUE(leaf.isValueOff(1)); + EXPECT_EQ(leaf.getValueUnsafe(1), 1.0f); + float value = -1.0f; + EXPECT_FALSE(leaf.getValueUnsafe(1, value)); + EXPECT_EQ(value, 1.0f); value = -1.0f; + + EXPECT_TRUE(leaf.isValueOff(32)); + leaf.setValueOnUnsafe(32); + EXPECT_TRUE(leaf.isValueOn(32)); + leaf.setValueOffUnsafe(32); + EXPECT_TRUE(leaf.isValueOff(32)); + leaf.setActiveStateUnsafe(32, true); + EXPECT_TRUE(leaf.isValueOn(32)); + leaf.setActiveStateUnsafe(32, false); + EXPECT_TRUE(leaf.isValueOff(32)); + + leaf.setValueOnlyUnsafe(32, 5.0f); + EXPECT_EQ(leaf.getValueUnsafe(32), 5.0f); + EXPECT_TRUE(leaf.isValueOff(32)); + leaf.setValueOnUnsafe(32); + EXPECT_TRUE(leaf.isValueOn(32)); + + leaf.setValueOnUnsafe(33, 7.0f); + EXPECT_TRUE(leaf.isValueOn(33)); + EXPECT_EQ(leaf.getValueUnsafe(33), 7.0f); + leaf.setValueOffUnsafe(33, 6.0f); + EXPECT_TRUE(leaf.isValueOff(33)); + EXPECT_EQ(leaf.getValueUnsafe(33), 6.0f); +} diff --git a/openvdb/openvdb/unittest/TestLeafBool.cc b/openvdb/openvdb/unittest/TestLeafBool.cc index c533feeb0e..fa9c343b09 100644 --- a/openvdb/openvdb/unittest/TestLeafBool.cc +++ b/openvdb/openvdb/unittest/TestLeafBool.cc @@ -653,3 +653,41 @@ TEST_F(TestLeafBool, testTransientData) LeafType leaf3 = leaf; EXPECT_EQ(openvdb::Index32(5), leaf3.transientData()); } + +TEST_F(TestLeafBool, testUnsafe) +{ + using namespace openvdb; + using LeafT = tree::LeafNode; + const Coord origin(-9, -2, -8); + LeafT leaf(origin, true, false); + + EXPECT_FALSE(leaf.isValueOn(1)); + EXPECT_TRUE(leaf.isValueOff(1)); + EXPECT_EQ(leaf.getValueUnsafe(1), true); + bool value = false; + EXPECT_FALSE(leaf.getValueUnsafe(1, value)); + EXPECT_EQ(value, true); value = false; + + EXPECT_TRUE(leaf.isValueOff(32)); + leaf.setValueOnUnsafe(32); + EXPECT_TRUE(leaf.isValueOn(32)); + leaf.setValueOffUnsafe(32); + EXPECT_TRUE(leaf.isValueOff(32)); + leaf.setActiveStateUnsafe(32, true); + EXPECT_TRUE(leaf.isValueOn(32)); + leaf.setActiveStateUnsafe(32, false); + EXPECT_TRUE(leaf.isValueOff(32)); + + leaf.setValueOnlyUnsafe(32, false); + EXPECT_EQ(leaf.getValueUnsafe(32), false); + EXPECT_TRUE(leaf.isValueOff(32)); + leaf.setValueOnUnsafe(32); + EXPECT_TRUE(leaf.isValueOn(32)); + + leaf.setValueOnUnsafe(33, false); + EXPECT_TRUE(leaf.isValueOn(33)); + EXPECT_EQ(leaf.getValueUnsafe(33), false); + leaf.setValueOffUnsafe(33, true); + EXPECT_TRUE(leaf.isValueOff(33)); + EXPECT_EQ(leaf.getValueUnsafe(33), true); +} diff --git a/openvdb/openvdb/unittest/TestLeafMask.cc b/openvdb/openvdb/unittest/TestLeafMask.cc index d4b6c6eae9..7597158f45 100644 --- a/openvdb/openvdb/unittest/TestLeafMask.cc +++ b/openvdb/openvdb/unittest/TestLeafMask.cc @@ -574,3 +574,42 @@ TEST_F(TestLeafMask, testTransientData) LeafType leaf3 = leaf; EXPECT_EQ(openvdb::Index32(5), leaf3.transientData()); } + +TEST_F(TestLeafMask, testUnsafe) +{ + using namespace openvdb; + using LeafT = tree::LeafNode; + const Coord origin(-9, -2, -8); + LeafT leaf(origin, false, /*dummy=*/false); + + EXPECT_FALSE(leaf.isValueOn(1)); + EXPECT_TRUE(leaf.isValueOff(1)); + EXPECT_EQ(leaf.getValueUnsafe(1), false); + bool value = true; + EXPECT_FALSE(leaf.getValueUnsafe(1, value)); + EXPECT_EQ(value, false); + + EXPECT_TRUE(leaf.isValueOff(32)); + leaf.setValueOnUnsafe(32); + EXPECT_TRUE(leaf.isValueOn(32)); + leaf.setValueOffUnsafe(32); + EXPECT_TRUE(leaf.isValueOff(32)); + leaf.setActiveStateUnsafe(32, true); + EXPECT_TRUE(leaf.isValueOn(32)); + leaf.setActiveStateUnsafe(32, false); + EXPECT_TRUE(leaf.isValueOff(32)); + + leaf.setValueOnlyUnsafe(32, true); + EXPECT_EQ(leaf.getValueUnsafe(32), true); + EXPECT_TRUE(leaf.isValueOn(32)); + leaf.setValueOffUnsafe(32); + EXPECT_TRUE(leaf.isValueOff(32)); + + leaf.setValueOnUnsafe(33, true); + EXPECT_TRUE(leaf.isValueOn(33)); + EXPECT_EQ(leaf.getValueUnsafe(33), true); + leaf.setValueOffUnsafe(33, false); + EXPECT_TRUE(leaf.isValueOff(33)); + EXPECT_EQ(leaf.getValueUnsafe(33), false); +} + diff --git a/openvdb/openvdb/unittest/TestLevelSetFilter.cc b/openvdb/openvdb/unittest/TestLevelSetFilter.cc new file mode 100644 index 0000000000..12fc472f01 --- /dev/null +++ b/openvdb/openvdb/unittest/TestLevelSetFilter.cc @@ -0,0 +1,80 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include +#include +#include // for csgUnion() + +#include + +using namespace openvdb; + +class TestLevelSetFilter: public ::testing::Test +{ +public: + void SetUp() override { openvdb::initialize(); } + void TearDown() override { openvdb::uninitialize(); } + + void testLevelSetFillet(); +}; // class TestLevelSetFilter + + +//////////////////////////////////////// + + +TEST_F(TestLevelSetFilter, testLevelSetFillet) +{ + using GridT = FloatGrid; + using FilterT = tools::LevelSetFilter; + + const float radius = 5.0f; + const float voxelSize = 1.0f; + const float halfWidth = 3.0f; + typename GridT::Ptr sdfGrid = tools::createLevelSetSphere(/*radius=*/radius, + /*center=*/Vec3f(-radius, 0.0f, 0.0f), + /*dx=*/voxelSize, /*halfWidth*/ halfWidth); + typename GridT::Ptr sdfGridB = tools::createLevelSetSphere(/*radius=*/radius, + /*center=*/Vec3f(radius, 0.0f, 0.0f), + /*dx=*/voxelSize, /*halfWidth*/ halfWidth); + typename GridT::Accessor acc = sdfGrid->getAccessor(); + + EXPECT_TRUE(sdfGrid); + EXPECT_TRUE(sdfGridB); + + tools::csgUnion(*sdfGrid, *sdfGridB); + + { + EXPECT_TRUE(sdfGrid); + + Coord ijk(0, 3, 0); + + // We expect that the intersection between the two spheres are at (0, 0, 0) + // so the SDF value of the union in these offsets locations should be > 0 + EXPECT_TRUE(acc.getValue(ijk) > 0.f); + EXPECT_TRUE(acc.getValue(ijk.offsetBy(0, 0, 1)) > 0.0f); + EXPECT_TRUE(acc.getValue(ijk.offsetBy(0, 0,-1)) > 0.0f); + EXPECT_TRUE(acc.getValue(ijk.offsetBy(0,-1, 0)) > 0.0f); + EXPECT_TRUE(acc.getValue(ijk.offsetBy(0,-1, 1)) > 0.0f); + EXPECT_TRUE(acc.getValue(ijk.offsetBy(0,-1,-1)) > 0.0f); + } + + FilterT filter(*sdfGrid); + filter.fillet(); + + { + EXPECT_TRUE(sdfGrid); + + Coord ijk(0, 3, 0); + + // After the fillet operation, we expect that the zero-isocontour is + // pushed outward. + EXPECT_TRUE(acc.getValue(ijk) < 0.f); + EXPECT_TRUE(acc.getValue(ijk.offsetBy(0, 0, 1)) < 0.0f); + EXPECT_TRUE(acc.getValue(ijk.offsetBy(0, 0,-1)) < 0.0f); + EXPECT_TRUE(acc.getValue(ijk.offsetBy(0,-1, 0)) < 0.0f); + EXPECT_TRUE(acc.getValue(ijk.offsetBy(0,-1, 1)) < 0.0f); + EXPECT_TRUE(acc.getValue(ijk.offsetBy(0,-1,-1)) < 0.0f); + } +} diff --git a/openvdb/openvdb/unittest/TestLevelSetUtil.cc b/openvdb/openvdb/unittest/TestLevelSetUtil.cc index 165a960e5c..78e14ac9fa 100644 --- a/openvdb/openvdb/unittest/TestLevelSetUtil.cc +++ b/openvdb/openvdb/unittest/TestLevelSetUtil.cc @@ -146,7 +146,7 @@ TEST_F(TestLevelSetUtil, testSegmentationTools) openvdb::tools::segmentSDF(*sdfGrid, segments); EXPECT_EQ(size_t(1), segments.size()); - EXPECT_EQ(openvdb::Index32(0), segments[0]->tree().leafCount()); + EXPECT_EQ(openvdb::Index64(0), segments[0]->tree().leafCount()); EXPECT_EQ(10.2f, segments[0]->background()); } @@ -173,7 +173,7 @@ TEST_F(TestLevelSetUtil, testSegmentationTools) openvdb::tools::segmentSDF(*sdfGrid, segments); EXPECT_EQ(size_t(1), segments.size()); - EXPECT_EQ(openvdb::Index32(0), segments[0]->tree().leafCount()); + EXPECT_EQ(openvdb::Index64(0), segments[0]->tree().leafCount()); EXPECT_EQ(sdfGrid->background(), segments[0]->background()); } @@ -195,14 +195,14 @@ TEST_F(TestLevelSetUtil, testSegmentationTools) openvdb::FloatGrid::Ptr grid = openvdb::FloatGrid::create(/*background=*/3.1f); - EXPECT_EQ(openvdb::Index32(0), grid->tree().leafCount()); + EXPECT_EQ(openvdb::Index64(0), grid->tree().leafCount()); std::vector segments; openvdb::tools::segmentActiveVoxels(*grid, segments); // note that an empty volume should segment into an empty volume EXPECT_EQ(size_t(1), segments.size()); - EXPECT_EQ(openvdb::Index32(0), segments[0]->tree().leafCount()); + EXPECT_EQ(openvdb::Index64(0), segments[0]->tree().leafCount()); EXPECT_EQ(3.1f, segments[0]->background()); } @@ -213,14 +213,14 @@ TEST_F(TestLevelSetUtil, testSegmentationTools) grid->tree().touchLeaf(openvdb::Coord(0,0,0)); grid->tree().touchLeaf(openvdb::Coord(100,100,100)); - EXPECT_EQ(openvdb::Index32(2), grid->tree().leafCount()); + EXPECT_EQ(openvdb::Index64(2), grid->tree().leafCount()); EXPECT_EQ(openvdb::Index64(0), grid->tree().activeVoxelCount()); std::vector segments; openvdb::tools::segmentActiveVoxels(*grid, segments); EXPECT_EQ(size_t(1), segments.size()); - EXPECT_EQ(openvdb::Index32(0), segments[0]->tree().leafCount()); + EXPECT_EQ(openvdb::Index64(0), segments[0]->tree().leafCount()); } } diff --git a/openvdb/openvdb/unittest/TestMerge.cc b/openvdb/openvdb/unittest/TestMerge.cc index 519a75f2a9..3a98d58479 100644 --- a/openvdb/openvdb/unittest/TestMerge.cc +++ b/openvdb/openvdb/unittest/TestMerge.cc @@ -82,7 +82,7 @@ TEST_F(TestMerge, testTreeToMerge) { // non-const tree FloatGrid::Ptr grid = createLevelSet(); grid->tree().touchLeaf(Coord(8)); - EXPECT_EQ(Index(1), grid->tree().leafCount()); + EXPECT_EQ(Index64(1), grid->tree().leafCount()); tools::TreeToMerge treeToMerge{grid->tree(), Steal()}; EXPECT_EQ(&grid->constTree().root(), treeToMerge.rootPtr()); @@ -98,14 +98,14 @@ TEST_F(TestMerge, testTreeToMerge) const LeafNode* leafNode = treeToMerge.probeConstNode(Coord(8)); EXPECT_TRUE(leafNode); EXPECT_EQ(grid->constTree().probeConstLeaf(Coord(8)), leafNode); - EXPECT_EQ(Index(1), grid->tree().leafCount()); + EXPECT_EQ(Index64(1), grid->tree().leafCount()); EXPECT_EQ(Index(1), grid->tree().root().childCount()); // steal leaf node std::unique_ptr leafNodePtr = treeToMerge.stealOrDeepCopyNode(Coord(8)); EXPECT_TRUE(leafNodePtr); - EXPECT_EQ(Index(0), grid->tree().leafCount()); + EXPECT_EQ(Index64(0), grid->tree().leafCount()); EXPECT_EQ(leafNodePtr->origin(), Coord(8)); EXPECT_EQ(Index(1), grid->tree().root().childCount()); @@ -139,7 +139,7 @@ TEST_F(TestMerge, testTreeToMerge) { // const tree FloatGrid::Ptr grid = createLevelSet(); grid->tree().touchLeaf(Coord(8)); - EXPECT_EQ(Index(1), grid->tree().leafCount()); + EXPECT_EQ(Index64(1), grid->tree().leafCount()); tools::TreeToMerge treeToMerge{grid->constTree(), DeepCopy(), /*initialize=*/false}; EXPECT_TRUE(!treeToMerge.hasMask()); @@ -158,14 +158,14 @@ TEST_F(TestMerge, testTreeToMerge) const LeafNode* leafNode = treeToMerge.probeConstNode(Coord(8)); EXPECT_TRUE(leafNode); EXPECT_EQ(grid->constTree().probeConstLeaf(Coord(8)), leafNode); - EXPECT_EQ(Index(1), grid->tree().leafCount()); + EXPECT_EQ(Index64(1), grid->tree().leafCount()); EXPECT_EQ(Index(1), grid->tree().root().childCount()); { // deep copy leaf node tools::TreeToMerge treeToMerge2{grid->constTree(), DeepCopy()}; std::unique_ptr leafNodePtr = treeToMerge2.stealOrDeepCopyNode(Coord(8)); EXPECT_TRUE(leafNodePtr); - EXPECT_EQ(Index(1), grid->tree().leafCount()); // leaf has not been stolen + EXPECT_EQ(Index64(1), grid->tree().leafCount()); // leaf has not been stolen EXPECT_EQ(leafNodePtr->origin(), Coord(8)); EXPECT_EQ(Index(1), grid->tree().root().childCount()); } @@ -221,7 +221,7 @@ TEST_F(TestMerge, testTreeToMerge) FloatGrid::Ptr grid = createLevelSet(); grid->tree().touchLeaf(Coord(8)); - EXPECT_EQ(Index(1), grid->tree().leafCount()); + EXPECT_EQ(Index64(1), grid->tree().leafCount()); treeToMerge.reset(grid->treePtr(), Steal()); } @@ -241,7 +241,7 @@ TEST_F(TestMerge, testTreeToMerge) EXPECT_TRUE(!treeToMerge2.treeToSteal()); EXPECT_TRUE(treeToMerge2.treeToDeepCopy()); - EXPECT_EQ(Index(0), treeToMerge2.treeToDeepCopy()->leafCount()); + EXPECT_EQ(Index64(0), treeToMerge2.treeToDeepCopy()->leafCount()); FloatGrid::Ptr grid = createLevelSet(); grid->tree().touchLeaf(Coord(8)); @@ -249,7 +249,7 @@ TEST_F(TestMerge, testTreeToMerge) EXPECT_TRUE(treeToMerge2.treeToSteal()); EXPECT_TRUE(!treeToMerge2.treeToDeepCopy()); - EXPECT_EQ(Index(1), treeToMerge2.treeToSteal()->leafCount()); + EXPECT_EQ(Index64(1), treeToMerge2.treeToSteal()->leafCount()); } } @@ -908,7 +908,7 @@ TEST_F(TestMerge, testCsgUnion) tree::DynamicNodeManager nodeManager(grid->tree()); nodeManager.foreachTopDown(mergeOp); - EXPECT_EQ(Index32(1), grid->tree().leafCount()); + EXPECT_EQ(Index64(1), grid->tree().leafCount()); } { // merge a leaf node into a grid with an outside tile @@ -955,8 +955,8 @@ TEST_F(TestMerge, testCsgUnion) tree::DynamicNodeManager nodeManager(grid->tree()); nodeManager.foreachTopDown(mergeOp); - EXPECT_EQ(Index32(1), grid->tree().leafCount()); - EXPECT_EQ(Index32(0), grid2->tree().leafCount()); + EXPECT_EQ(Index64(1), grid->tree().leafCount()); + EXPECT_EQ(Index64(0), grid2->tree().leafCount()); // test background values are remapped @@ -1027,8 +1027,8 @@ TEST_F(TestMerge, testCsgUnion) FloatGrid::Ptr grid2 = createLevelSet(); grid2->tree().touchLeaf(Coord(0, 0, 0)); - EXPECT_EQ(Index32(0), grid->tree().leafCount()); - EXPECT_EQ(Index32(1), grid2->tree().leafCount()); + EXPECT_EQ(Index64(0), grid->tree().leafCount()); + EXPECT_EQ(Index64(1), grid2->tree().leafCount()); // merge from a const tree @@ -1038,9 +1038,9 @@ TEST_F(TestMerge, testCsgUnion) tree::DynamicNodeManager nodeManager(grid->tree()); nodeManager.foreachTopDown(mergeOp); - EXPECT_EQ(Index32(1), grid->tree().leafCount()); + EXPECT_EQ(Index64(1), grid->tree().leafCount()); // leaf has been deep copied not stolen - EXPECT_EQ(Index32(1), grid2->tree().leafCount()); + EXPECT_EQ(Index64(1), grid2->tree().leafCount()); } } @@ -1772,7 +1772,7 @@ TEST_F(TestMerge, testCsgIntersection) tree::DynamicNodeManager nodeManager(grid->tree()); nodeManager.foreachTopDown(mergeOp); - EXPECT_EQ(Index32(0), grid->tree().leafCount()); + EXPECT_EQ(Index64(0), grid->tree().leafCount()); } { // merge a leaf node into a grid with a background tile @@ -1785,7 +1785,7 @@ TEST_F(TestMerge, testCsgIntersection) tree::DynamicNodeManager nodeManager(grid->tree()); nodeManager.foreachTopDown(mergeOp); - EXPECT_EQ(Index32(0), grid->tree().leafCount()); + EXPECT_EQ(Index64(0), grid->tree().leafCount()); } { // merge a leaf node into a grid with an outside tile @@ -1828,8 +1828,8 @@ TEST_F(TestMerge, testCsgIntersection) tree::DynamicNodeManager nodeManager(grid->tree()); nodeManager.foreachTopDown(mergeOp); - EXPECT_EQ(Index32(1), grid->tree().leafCount()); - EXPECT_EQ(Index32(0), grid2->tree().leafCount()); + EXPECT_EQ(Index64(1), grid->tree().leafCount()); + EXPECT_EQ(Index64(0), grid2->tree().leafCount()); // test background values are remapped @@ -1901,8 +1901,8 @@ TEST_F(TestMerge, testCsgIntersection) FloatGrid::Ptr grid2 = createLevelSet(); grid2->tree().touchLeaf(Coord(0, 0, 0)); - EXPECT_EQ(Index32(0), grid->tree().leafCount()); - EXPECT_EQ(Index32(1), grid2->tree().leafCount()); + EXPECT_EQ(Index64(0), grid->tree().leafCount()); + EXPECT_EQ(Index64(1), grid2->tree().leafCount()); // merge from a const tree @@ -1912,9 +1912,9 @@ TEST_F(TestMerge, testCsgIntersection) tree::DynamicNodeManager nodeManager(grid->tree()); nodeManager.foreachTopDown(mergeOp); - EXPECT_EQ(Index32(1), grid->tree().leafCount()); + EXPECT_EQ(Index64(1), grid->tree().leafCount()); // leaf has been deep copied not stolen - EXPECT_EQ(Index32(1), grid2->tree().leafCount()); + EXPECT_EQ(Index64(1), grid2->tree().leafCount()); } { // merge three leaf nodes from four grids @@ -2472,15 +2472,15 @@ TEST_F(TestMerge, testCsgDifference) FloatGrid::Ptr grid2 = createLevelSet(); grid2->tree().touchLeaf(Coord(0, 0, 0)); - EXPECT_EQ(Index32(0), grid->tree().leafCount()); - EXPECT_EQ(Index32(1), grid2->tree().leafCount()); + EXPECT_EQ(Index64(0), grid->tree().leafCount()); + EXPECT_EQ(Index64(1), grid2->tree().leafCount()); tools::CsgDifferenceOp mergeOp(grid2->tree(), Steal()); tree::DynamicNodeManager nodeManager(grid->tree()); nodeManager.foreachTopDown(mergeOp); - EXPECT_EQ(Index32(1), grid->tree().leafCount()); - EXPECT_EQ(Index32(0), grid2->tree().leafCount()); + EXPECT_EQ(Index64(1), grid->tree().leafCount()); + EXPECT_EQ(Index64(0), grid2->tree().leafCount()); } { // merge two leaf nodes into a grid @@ -2489,8 +2489,8 @@ TEST_F(TestMerge, testCsgDifference) FloatGrid::Ptr grid2 = createLevelSet(); grid2->tree().touchLeaf(Coord(0, 0, 0)); - EXPECT_EQ(Index32(1), grid->tree().leafCount()); - EXPECT_EQ(Index32(1), grid2->tree().leafCount()); + EXPECT_EQ(Index64(1), grid->tree().leafCount()); + EXPECT_EQ(Index64(1), grid2->tree().leafCount()); tools::CsgDifferenceOp mergeOp(grid2->tree(), Steal()); tree::DynamicNodeManager nodeManager(grid->tree()); @@ -2556,15 +2556,15 @@ TEST_F(TestMerge, testCsgDifference) FloatGrid::Ptr grid2 = createLevelSet(); grid2->tree().touchLeaf(Coord(0, 0, 0)); - EXPECT_EQ(Index32(0), grid->tree().leafCount()); - EXPECT_EQ(Index32(1), grid2->tree().leafCount()); + EXPECT_EQ(Index64(0), grid->tree().leafCount()); + EXPECT_EQ(Index64(1), grid2->tree().leafCount()); tools::CsgDifferenceOp mergeOp(grid2->constTree(), DeepCopy()); tree::DynamicNodeManager nodeManager(grid->tree()); nodeManager.foreachTopDown(mergeOp); - EXPECT_EQ(Index32(1), grid->tree().leafCount()); - EXPECT_EQ(Index32(1), grid2->tree().leafCount()); + EXPECT_EQ(Index64(1), grid->tree().leafCount()); + EXPECT_EQ(Index64(1), grid2->tree().leafCount()); } } @@ -2887,7 +2887,7 @@ TEST_F(TestMerge, testSum) tree::DynamicNodeManager nodeManager(tree); nodeManager.foreachTopDown(mergeOp); - EXPECT_EQ(Index32(1), tree.leafCount()); + EXPECT_EQ(Index64(1), tree.leafCount()); EXPECT_EQ(tree.cbeginLeaf()->getFirstValue(), 0.0f); } @@ -2902,7 +2902,7 @@ TEST_F(TestMerge, testSum) tree::DynamicNodeManager nodeManager(tree); nodeManager.foreachTopDown(mergeOp); - EXPECT_EQ(Index32(1), tree.leafCount()); + EXPECT_EQ(Index64(1), tree.leafCount()); EXPECT_EQ(Index(0), getTileCount(tree.root())); auto iter = tree.cbeginLeaf(); EXPECT_EQ(iter->getValue(0), 10.0f); @@ -2924,7 +2924,7 @@ TEST_F(TestMerge, testSum) tree::DynamicNodeManager nodeManager(tree); nodeManager.foreachTopDown(mergeOp); - EXPECT_EQ(Index32(1), tree.leafCount()); + EXPECT_EQ(Index64(1), tree.leafCount()); EXPECT_EQ(Index(0), getTileCount(tree.root())); auto iter = tree.cbeginLeaf(); EXPECT_EQ(iter->getValue(0), 210.0f); @@ -2949,7 +2949,7 @@ TEST_F(TestMerge, testSum) tree::DynamicNodeManager nodeManager(tree); nodeManager.foreachTopDown(mergeOp); - EXPECT_EQ(Index32(1), tree.leafCount()); + EXPECT_EQ(Index64(1), tree.leafCount()); EXPECT_EQ(Index(0), getTileCount(tree.root())); auto iter = tree.cbeginLeaf(); EXPECT_EQ(iter->getValue(0), 210.0f); @@ -2973,7 +2973,7 @@ TEST_F(TestMerge, testSum) tree::DynamicNodeManager nodeManager(tree); nodeManager.foreachTopDown(mergeOp); - EXPECT_EQ(Index32(1), tree.leafCount()); + EXPECT_EQ(Index64(1), tree.leafCount()); EXPECT_EQ(Index(0), getTileCount(tree.root())); auto iter = tree.cbeginLeaf(); EXPECT_EQ(iter->getValue(0), 15.0f); @@ -2995,7 +2995,7 @@ TEST_F(TestMerge, testSum) tree::DynamicNodeManager nodeManager(tree); nodeManager.foreachTopDown(mergeOp); - EXPECT_EQ(Index32(1), tree.leafCount()); + EXPECT_EQ(Index64(1), tree.leafCount()); EXPECT_EQ(Index(0), getTileCount(tree.root())); auto iter = tree.cbeginLeaf(); EXPECT_EQ(iter->getValue(0), 10.0f); @@ -3017,7 +3017,7 @@ TEST_F(TestMerge, testSum) tree::DynamicNodeManager nodeManager(tree); nodeManager.foreachTopDown(mergeOp); - EXPECT_EQ(Index32(1), tree.leafCount()); + EXPECT_EQ(Index64(1), tree.leafCount()); EXPECT_EQ(Index(0), getTileCount(tree.root())); auto iter = tree.cbeginLeaf(); EXPECT_EQ(iter->getValue(0), 10.0f); @@ -3039,7 +3039,7 @@ TEST_F(TestMerge, testSum) tree::DynamicNodeManager nodeManager(tree); nodeManager.foreachTopDown(mergeOp); - EXPECT_EQ(Index32(1), tree.leafCount()); + EXPECT_EQ(Index64(1), tree.leafCount()); EXPECT_EQ(Index(0), getTileCount(tree.root())); auto iter = tree.cbeginLeaf(); EXPECT_EQ(iter->getValue(0), 10.0f); @@ -3155,8 +3155,8 @@ TEST_F(TestMerge, testSum) FloatGrid::Ptr grid2 = createLevelSet(); tree2.touchLeaf(Coord(0, 0, 0)); - EXPECT_EQ(Index32(0), tree.leafCount()); - EXPECT_EQ(Index32(1), tree2.leafCount()); + EXPECT_EQ(Index64(0), tree.leafCount()); + EXPECT_EQ(Index64(1), tree2.leafCount()); // merge from a const tree @@ -3168,9 +3168,9 @@ TEST_F(TestMerge, testSum) tree::DynamicNodeManager nodeManager(tree); nodeManager.foreachTopDown(mergeOp); - EXPECT_EQ(Index32(1), tree.leafCount()); + EXPECT_EQ(Index64(1), tree.leafCount()); // leaf has been deep copied not stolen - EXPECT_EQ(Index32(1), tree2.leafCount()); + EXPECT_EQ(Index64(1), tree2.leafCount()); } } @@ -3185,7 +3185,7 @@ TEST_F(TestMerge, testSum) tree::DynamicNodeManager nodeManager(tree); nodeManager.foreachTopDown(mergeOp); - EXPECT_EQ(Index32(1), tree.leafCount()); + EXPECT_EQ(Index64(1), tree.leafCount()); EXPECT_EQ(Index(0), getTileCount(tree.root())); auto iter = tree.cbeginLeaf(); EXPECT_EQ(iter->getValue(0), Vec3s(1.0f, 2.0f, 3.0f)); @@ -3206,7 +3206,7 @@ TEST_F(TestMerge, testSum) tree::DynamicNodeManager nodeManager(tree); nodeManager.foreachTopDown(mergeOp); - EXPECT_EQ(Index32(1), tree.leafCount()); + EXPECT_EQ(Index64(1), tree.leafCount()); EXPECT_EQ(Index(0), getTileCount(tree.root())); auto iter = tree.cbeginLeaf(); EXPECT_FALSE(iter->isValueOn(0)); diff --git a/openvdb/openvdb/unittest/TestMorphology.cc b/openvdb/openvdb/unittest/TestMorphology.cc index c7498996b9..a03e1db857 100644 --- a/openvdb/openvdb/unittest/TestMorphology.cc +++ b/openvdb/openvdb/unittest/TestMorphology.cc @@ -113,7 +113,7 @@ TestMorphologyInternal::testMorphActiveLeafValues() EXPECT_EQ(Index64(1), tree.activeVoxelCount()); openvdb::tools::erodeActiveValues(tree, 1, NN, openvdb::tools::IGNORE_TILES); EXPECT_EQ(Index64(0), tree.activeVoxelCount()); - EXPECT_EQ(Index32(1), tree.leafCount()); + EXPECT_EQ(Index64(1), tree.leafCount()); // check values if (!IsMask) { EXPECT_EQ(tree.getValue(xyz), ValueType(1.0)); @@ -124,7 +124,7 @@ TestMorphologyInternal::testMorphActiveLeafValues() { // Create an active, leaf node-sized tile and a single edge/corner voxel tree.clear(); tree.addTile(/*level*/1, Coord(0), ValueType(1.0), true); - EXPECT_EQ(Index32(0), tree.leafCount()); + EXPECT_EQ(Index64(0), tree.leafCount()); EXPECT_EQ(Index64(leafDim * leafDim * leafDim), tree.activeVoxelCount()); EXPECT_EQ(Index64(1), tree.activeTileCount()); @@ -143,7 +143,7 @@ TestMorphologyInternal::testMorphActiveLeafValues() if (NN == openvdb::tools::NN_FACE_EDGE_VERTEX) expected += 22; // 4 overlapping EXPECT_EQ(expected, tree.activeVoxelCount()); EXPECT_EQ(Index64(1), tree.activeTileCount()); - Index32 leafs; + Index64 leafs; if (NN == openvdb::tools::NN_FACE) leafs = 3; if (NN == openvdb::tools::NN_FACE_EDGE) leafs = 6; if (NN == openvdb::tools::NN_FACE_EDGE_VERTEX) leafs = 7; @@ -434,7 +434,7 @@ TestMorphologyInternal::testMorphActiveValues() { // Test behaviour with an existing active tile at (0,0,0) tree.clear(); tree.addTile(/*level*/1, Coord(0), ValueType(1.0), true); - EXPECT_EQ(Index32(0), tree.leafCount()); + EXPECT_EQ(Index64(0), tree.leafCount()); EXPECT_EQ(Index64(leafDim * leafDim * leafDim), tree.activeVoxelCount()); EXPECT_EQ(Index64(1), tree.activeTileCount()); @@ -450,7 +450,7 @@ TestMorphologyInternal::testMorphActiveValues() TreeT erodeexp(tree), erodepres(tree); openvdb::tools::erodeActiveValues(erodeexp, 1, NN, openvdb::tools::EXPAND_TILES); Index64 expected = (leafDim-2) * (leafDim-2) * (leafDim-2); - EXPECT_EQ(Index32(1), erodeexp.leafCount()); + EXPECT_EQ(Index64(1), erodeexp.leafCount()); EXPECT_EQ(expected, erodeexp.activeVoxelCount()); EXPECT_EQ(Index64(0), erodeexp.activeTileCount()); EXPECT_TRUE(erodeexp.probeConstLeaf(Coord(0))); @@ -465,7 +465,7 @@ TestMorphologyInternal::testMorphActiveValues() if (NN == openvdb::tools::NN_FACE) expected += (leafDim * leafDim) * 6; // faces if (NN == openvdb::tools::NN_FACE_EDGE) expected += ((leafDim * leafDim) * 6) + (leafDim) * 12; // edges if (NN == openvdb::tools::NN_FACE_EDGE_VERTEX) expected += ((leafDim * leafDim) * 6) + ((leafDim) * 12) + 8; // edges - EXPECT_EQ(Index32(1+offsets), tree.leafCount()); + EXPECT_EQ(Index64(1+offsets), tree.leafCount()); EXPECT_EQ(expected, tree.activeVoxelCount()); EXPECT_EQ(Index64(0), tree.activeTileCount()); // Check actual values around center node faces @@ -492,7 +492,7 @@ TestMorphologyInternal::testMorphActiveValues() TreeT erode(tree); openvdb::tools::erodeActiveValues(erode, 1, NN, openvdb::tools::IGNORE_TILES); Index64 expected = leafDim * leafDim * leafDim; - EXPECT_EQ(Index32(1+offsets), erode.leafCount()); + EXPECT_EQ(Index64(1+offsets), erode.leafCount()); EXPECT_EQ(expected, erode.activeVoxelCount()); EXPECT_EQ(Index64(0), erode.activeTileCount()); EXPECT_TRUE(erode.probeConstLeaf(Coord(0))); @@ -514,7 +514,7 @@ TestMorphologyInternal::testMorphActiveValues() if (NN == openvdb::tools::NN_FACE_EDGE) expected += ((leafDim * leafDim) * 6) + (leafDim) * 12; // edges if (NN == openvdb::tools::NN_FACE_EDGE_VERTEX) expected += ((leafDim * leafDim) * 6) + ((leafDim) * 12) + 8; // edges - EXPECT_EQ(Index32(offsets), tree.leafCount()); + EXPECT_EQ(Index64(offsets), tree.leafCount()); EXPECT_EQ(expected, tree.activeVoxelCount()); EXPECT_EQ(Index64(1), tree.activeTileCount()); EXPECT_TRUE(copy.hasSameTopology(tree)); @@ -539,7 +539,7 @@ TestMorphologyInternal::testMorphActiveValues() openvdb::tools::erodeActiveValues(erode, 1, NN, openvdb::tools::PRESERVE_TILES); // PRESERVE_TILES will prune the result Index64 expected = leafDim * leafDim * leafDim; - EXPECT_EQ(Index32(0), erode.leafCount()); + EXPECT_EQ(Index64(0), erode.leafCount()); EXPECT_EQ(expected, erode.activeVoxelCount()); EXPECT_EQ(Index64(1), erode.activeTileCount()); EXPECT_TRUE(!erode.probeConstLeaf(Coord(0))); @@ -549,7 +549,7 @@ TestMorphologyInternal::testMorphActiveValues() { // Test tile preservation with voxel topology - create an active, leaf node-sized tile and a single edge voxel tree.clear(); tree.addTile(/*level*/1, Coord(0), ValueType(1.0), true); - EXPECT_EQ(Index32(0), tree.leafCount()); + EXPECT_EQ(Index64(0), tree.leafCount()); EXPECT_EQ(Index64(leafDim * leafDim * leafDim), tree.activeVoxelCount()); EXPECT_EQ(Index64(1), tree.activeTileCount()); @@ -575,7 +575,7 @@ TestMorphologyInternal::testMorphActiveValues() // Check actual values around center node faces EXPECT_EQ(Index64(1), tree.activeTileCount()); - EXPECT_EQ(Index32(offsets), tree.leafCount()); + EXPECT_EQ(Index64(offsets), tree.leafCount()); EXPECT_TRUE(!tree.probeConstLeaf(Coord(0))); EXPECT_TRUE(tree.isValueOn(Coord(0))); for (int i = 0; i < int(leafDim); ++i) { @@ -592,7 +592,7 @@ TestMorphologyInternal::testMorphActiveValues() { // Test tile is preserved with erosions IGNORE_TILES, irrespective of iterations openvdb::tools::erodeActiveValues(tree, 10, NN, openvdb::tools::IGNORE_TILES); EXPECT_EQ(Index64(1), tree.activeTileCount()); - EXPECT_EQ(Index32(offsets), tree.leafCount()); + EXPECT_EQ(Index64(offsets), tree.leafCount()); EXPECT_EQ(Index64(leafDim * leafDim * leafDim), tree.activeVoxelCount()); EXPECT_TRUE(!tree.probeConstLeaf(Coord(0))); EXPECT_TRUE(tree.isValueOn(Coord(0))); @@ -613,7 +613,7 @@ TestMorphologyInternal::testMorphActiveValues() tree.touchLeaf(Coord(leafDim*6, 0, 0))->setValuesOn(); Index64 expected = (leafDim * leafDim * leafDim) + ((leafDim * leafDim * leafDim) - (leafDim * leafDim)) * 2; - EXPECT_EQ(Index32(3), tree.leafCount()); + EXPECT_EQ(Index64(3), tree.leafCount()); EXPECT_EQ(expected, tree.activeVoxelCount()); EXPECT_EQ(Index64(0), tree.activeTileCount()); @@ -631,7 +631,7 @@ TestMorphologyInternal::testMorphActiveValues() if (NN == openvdb::tools::NN_FACE_EDGE) expected = offsets*3 -10; if (NN == openvdb::tools::NN_FACE_EDGE_VERTEX) expected = offsets*3 -18; if (!IsMask) expected += 1; - EXPECT_EQ(Index32(expected), tree.leafCount()); + EXPECT_EQ(Index64(expected), tree.leafCount()); // first if (IsMask) { // should have been pruned @@ -654,7 +654,7 @@ TestMorphologyInternal::testMorphActiveValues() openvdb::tools::PRESERVE_TILES); expected = (leafDim * leafDim * leafDim) + ((leafDim * leafDim * leafDim) - (leafDim * leafDim)) * 2; - EXPECT_EQ(Index32(2), tree.leafCount()); + EXPECT_EQ(Index64(2), tree.leafCount()); EXPECT_EQ(expected, tree.activeVoxelCount()); EXPECT_EQ(Index64(1), tree.activeTileCount()); } diff --git a/openvdb/openvdb/unittest/TestNodeManager.cc b/openvdb/openvdb/unittest/TestNodeManager.cc index d5a45fa587..6bd4759b3e 100644 --- a/openvdb/openvdb/unittest/TestNodeManager.cc +++ b/openvdb/openvdb/unittest/TestNodeManager.cc @@ -294,8 +294,8 @@ TEST_F(TestNodeManager, testDynamic) std::make_unique(Coord(0, 0, 0), /*value=*/1.0f); EXPECT_TRUE(sourceTree.root().addChild(child.release())); - EXPECT_EQ(Index32(0), sourceTree.leafCount()); - EXPECT_EQ(Index32(2), sourceTree.nonLeafCount()); + EXPECT_EQ(Index64(0), sourceTree.leafCount()); + EXPECT_EQ(Index64(2), sourceTree.nonLeafCount()); ExpandOp expandOp; @@ -304,7 +304,7 @@ TEST_F(TestNodeManager, testDynamic) openvdb::tree::NodeManager manager(tree); EXPECT_EQ(Index64(1), manager.nodeCount()); manager.foreachTopDown(expandOp); - EXPECT_EQ(Index32(0), tree.leafCount()); + EXPECT_EQ(Index64(0), tree.leafCount()); // first level has been expanded, but node manager cache does not include the new nodes SumOp sumOp; @@ -324,7 +324,7 @@ TEST_F(TestNodeManager, testDynamic) Int32Tree tree(sourceTree); openvdb::tree::DynamicNodeManager manager(tree); manager.foreachTopDown(expandOp, /*threaded=*/true, /*leafGrainSize=*/32, /*nonLeafGrainSize=*/8); - EXPECT_EQ(Index32(32768), tree.leafCount()); + EXPECT_EQ(Index64(32768), tree.leafCount()); SumOp sumOp; manager.reduceTopDown(sumOp); @@ -340,7 +340,7 @@ TEST_F(TestNodeManager, testDynamic) openvdb::tree::DynamicNodeManager manager(tree); ExpandOp zeroExpandOp(true); manager.foreachTopDown(zeroExpandOp); - EXPECT_EQ(Index32(32768), tree.leafCount()); + EXPECT_EQ(Index64(32768), tree.leafCount()); SumOp sumOp; manager.reduceTopDown(sumOp); diff --git a/openvdb/openvdb/unittest/TestNodeVisitor.cc b/openvdb/openvdb/unittest/TestNodeVisitor.cc index 19732621f3..e24bf72bb2 100644 --- a/openvdb/openvdb/unittest/TestNodeVisitor.cc +++ b/openvdb/openvdb/unittest/TestNodeVisitor.cc @@ -22,7 +22,7 @@ struct NodeCountOp counts[level]++; } - std::vector counts; + std::vector counts; }; // struct NodeCountOp @@ -35,10 +35,14 @@ TEST_F(TestNodeVisitor, testNodeCount) NodeCountOp nodeCountOp; tools::visitNodesDepthFirst(grid->tree(), nodeCountOp); - std::vector nodeCount1 = nodeCountOp.counts; + std::vector nodeCount1 = nodeCountOp.counts; +#if OPENVDB_ABI_VERSION_NUMBER >= 12 + std::vector nodeCount2 = grid->tree().nodeCount(); +#else std::vector nodeCount2 = grid->tree().nodeCount(); +#endif - EXPECT_EQ(nodeCount1.size(), nodeCount2.size()); + EXPECT_EQ(nodeCount1.size(), Index64(nodeCount2.size())); for (size_t i = 0; i < nodeCount1.size(); i++) { EXPECT_EQ(nodeCount1[i], nodeCount2[i]); @@ -55,7 +59,7 @@ struct LeafCountOp void operator()(const NodeT&, size_t) { } void operator()(const LeafT&, size_t) { count++; } - openvdb::Index32 count{0}; + openvdb::Index64 count{0}; }; // struct LeafCountOp @@ -94,7 +98,7 @@ struct DescendOp } openvdb::Index32 previousLevel{0}; - openvdb::Index32 count{0}; + openvdb::Index64 count{0}; }; // struct DescendOp @@ -142,9 +146,14 @@ TEST_F(TestNodeVisitor, testOriginArray) FloatGrid::Ptr grid = tools::createLevelSetCube(/*scale=*/10.0f); + Index64 totalNodeCount(0); +#if OPENVDB_ABI_VERSION_NUMBER >= 12 + std::vector nodeCount = grid->tree().nodeCount(); + for (Index64 count : nodeCount) totalNodeCount += count; +#else std::vector nodeCount = grid->tree().nodeCount(); - Index32 totalNodeCount(0); - for (Index32 count : nodeCount) totalNodeCount += count; + for (Index32 count : nodeCount) totalNodeCount += Index64(count); +#endif // use an offset size_t offset = 10; @@ -203,12 +212,12 @@ TEST_F(TestNodeVisitor, testPartialDeactivate) DeactivateOp deactivateOp; tools::DepthFirstNodeVisitor::visit(*iter, deactivateOp); - EXPECT_EQ(Index32(1413), grid->tree().leafCount()); + EXPECT_EQ(Index64(1413), grid->tree().leafCount()); tools::pruneInactive(grid->tree()); // a subset of the leaf nodes have now been deactivated and removed - EXPECT_EQ(Index32(1195), grid->tree().leafCount()); + EXPECT_EQ(Index64(1195), grid->tree().leafCount()); } diff --git a/openvdb/openvdb/unittest/TestPointAttribute.cc b/openvdb/openvdb/unittest/TestPointAttribute.cc index 19b66213b4..4fff63f23e 100644 --- a/openvdb/openvdb/unittest/TestPointAttribute.cc +++ b/openvdb/openvdb/unittest/TestPointAttribute.cc @@ -36,7 +36,7 @@ TEST_F(TestPointAttribute, testAppendDrop) PointDataTree& tree = grid->tree(); // check one leaf per point - EXPECT_EQ(tree.leafCount(), Index32(4)); + EXPECT_EQ(tree.leafCount(), Index64(4)); // retrieve first and last leaf attribute sets @@ -298,7 +298,7 @@ TEST_F(TestPointAttribute, testRename) PointDataTree& tree = grid->tree(); // check one leaf per point - EXPECT_EQ(tree.leafCount(), Index32(4)); + EXPECT_EQ(tree.leafCount(), Index64(4)); const openvdb::TypedMetadata defaultValue(5.0f); @@ -373,7 +373,7 @@ TEST_F(TestPointAttribute, testBloscCompress) PointDataTree& tree = grid->tree(); // check two leaves - EXPECT_EQ(tree.leafCount(), Index32(2)); + EXPECT_EQ(tree.leafCount(), Index64(2)); // retrieve first and last leaf attribute sets diff --git a/openvdb/openvdb/unittest/TestPointCount.cc b/openvdb/openvdb/unittest/TestPointCount.cc index 350aa888ae..58ea887907 100644 --- a/openvdb/openvdb/unittest/TestPointCount.cc +++ b/openvdb/openvdb/unittest/TestPointCount.cc @@ -186,7 +186,7 @@ TEST_F(TestPointCount, testGroup) PointDataTree& tree = grid->tree(); // check one leaf - EXPECT_EQ(tree.leafCount(), Index32(1)); + EXPECT_EQ(tree.leafCount(), Index64(1)); // retrieve first and last leaf attribute sets @@ -371,7 +371,7 @@ TEST_F(TestPointCount, testGroup) grid = createPointDataGrid(positions, *transform); PointDataTree& tree2 = grid->tree(); - EXPECT_EQ(tree2.leafCount(), Index32(4)); + EXPECT_EQ(tree2.leafCount(), Index64(4)); leafIter = tree2.beginLeaf(); diff --git a/openvdb/openvdb/unittest/TestPointDataLeaf.cc b/openvdb/openvdb/unittest/TestPointDataLeaf.cc index 70a37166c7..b22153e05a 100644 --- a/openvdb/openvdb/unittest/TestPointDataLeaf.cc +++ b/openvdb/openvdb/unittest/TestPointDataLeaf.cc @@ -1523,7 +1523,7 @@ TEST_F(TestPointDataLeaf, testCopyDescriptor) PointDataTree tree2(tree); - EXPECT_EQ(tree2.leafCount(), openvdb::Index32(2)); + EXPECT_EQ(tree2.leafCount(), openvdb::Index64(2)); descrA->setGroup("test", size_t(1)); diff --git a/openvdb/openvdb/unittest/TestPointGroup.cc b/openvdb/openvdb/unittest/TestPointGroup.cc index 04c2df33bb..8133956c2b 100644 --- a/openvdb/openvdb/unittest/TestPointGroup.cc +++ b/openvdb/openvdb/unittest/TestPointGroup.cc @@ -137,7 +137,7 @@ TEST_F(TestPointGroup, testAppendDrop) PointDataTree& tree = grid->tree(); // check one leaf per point - EXPECT_EQ(tree.leafCount(), Index32(4)); + EXPECT_EQ(tree.leafCount(), Index64(4)); // retrieve first and last leaf attribute sets @@ -312,7 +312,7 @@ TEST_F(TestPointGroup, testCompact) PointDataTree& tree = grid->tree(); // check one leaf - EXPECT_EQ(tree.leafCount(), Index32(1)); + EXPECT_EQ(tree.leafCount(), Index64(1)); // retrieve first and last leaf attribute sets diff --git a/openvdb/openvdb/unittest/TestPointMove.cc b/openvdb/openvdb/unittest/TestPointMove.cc index 3331fa2a27..cf165a9998 100644 --- a/openvdb/openvdb/unittest/TestPointMove.cc +++ b/openvdb/openvdb/unittest/TestPointMove.cc @@ -766,7 +766,7 @@ TEST_F(TestPointMove, testCustomDeformer) PointDataGrid::Ptr points = positionsToGrid(positions, voxelSize); PointDataGrid::Ptr cachedPoints = points->deepCopy(); - const int leafCount = points->tree().leafCount(); + const int leafCount = int(points->tree().leafCount()); const int pointCount = int(positions.size()); std::atomic resetCalls, applyCalls; diff --git a/openvdb/openvdb/unittest/TestPointRasterizeSDF.cc b/openvdb/openvdb/unittest/TestPointRasterizeSDF.cc index 9540f25a99..3af09c8288 100644 --- a/openvdb/openvdb/unittest/TestPointRasterizeSDF.cc +++ b/openvdb/openvdb/unittest/TestPointRasterizeSDF.cc @@ -135,7 +135,7 @@ TEST_F(TestPointRasterizeSDF, testRasterizeSpheres) EXPECT_EQ(GRID_LEVEL_SET, sdf->getGridClass()); EXPECT_EQ(float(s.halfband * s.points->voxelSize()[0]), sdf->background()); - EXPECT_EQ(Index32(8), sdf->tree().leafCount()); + EXPECT_EQ(Index64(8), sdf->tree().leafCount()); EXPECT_EQ(Index64(0), sdf->tree().activeTileCount()); EXPECT_EQ(Index64(485), sdf->tree().activeVoxelCount()); @@ -163,7 +163,7 @@ TEST_F(TestPointRasterizeSDF, testRasterizeSpheres) EXPECT_EQ(GRID_LEVEL_SET, sdf->getGridClass()); EXPECT_EQ(float(s.halfband * s.points->voxelSize()[0]), sdf->background()); - EXPECT_EQ(Index32(8), sdf->tree().leafCount()); + EXPECT_EQ(Index64(8), sdf->tree().leafCount()); EXPECT_EQ(Index64(0), sdf->tree().activeTileCount()); EXPECT_EQ(Index64(739), sdf->tree().activeVoxelCount()); @@ -192,7 +192,7 @@ TEST_F(TestPointRasterizeSDF, testRasterizeSpheres) EXPECT_EQ(GRID_LEVEL_SET, sdf->getGridClass()); EXPECT_EQ(float(s.halfband * s.transform->voxelSize()[0]), sdf->background()); - EXPECT_EQ(Index32(27), sdf->tree().leafCount()); + EXPECT_EQ(Index64(27), sdf->tree().leafCount()); EXPECT_EQ(Index64(0), sdf->tree().activeTileCount()); EXPECT_EQ(Index64(5005), sdf->tree().activeVoxelCount()); @@ -236,7 +236,7 @@ TEST_F(TestPointRasterizeSDF, testRasterizeSpheres) EXPECT_EQ(GRID_LEVEL_SET, sdf->getGridClass()); EXPECT_EQ(float(s.halfband * s.points->voxelSize()[0]), sdf->background()); - EXPECT_EQ(Index32(8), sdf->tree().leafCount()); + EXPECT_EQ(Index64(8), sdf->tree().leafCount()); EXPECT_EQ(Index64(0), sdf->tree().activeTileCount()); EXPECT_EQ(Index64(485), sdf->tree().activeVoxelCount()); @@ -265,7 +265,7 @@ TEST_F(TestPointRasterizeSDF, testRasterizeSpheres) EXPECT_EQ(GRID_LEVEL_SET, sdf->getGridClass()); EXPECT_EQ(float(s.halfband * s.points->voxelSize()[0]), sdf->background()); - EXPECT_EQ(Index32(38), sdf->tree().leafCount()); + EXPECT_EQ(Index64(38), sdf->tree().leafCount()); EXPECT_EQ(Index64(0), sdf->tree().activeTileCount()); EXPECT_EQ(Index64(485*8), sdf->tree().activeVoxelCount()); // 485 per sphere @@ -306,7 +306,7 @@ TEST_F(TestPointRasterizeSDF, testRasterizeSpheres) EXPECT_EQ(GRID_LEVEL_SET, sdf->getGridClass()); EXPECT_EQ(float(s.halfband * s.transform->voxelSize()[0]), sdf->background()); - EXPECT_EQ(Index32(46), sdf->tree().leafCount()); + EXPECT_EQ(Index64(46), sdf->tree().leafCount()); EXPECT_EQ(Index64(0), sdf->tree().activeTileCount()); EXPECT_EQ(Index64(7198), sdf->tree().activeVoxelCount()); @@ -359,7 +359,7 @@ TEST_F(TestPointRasterizeSDF, testRasterizeSpheres) EXPECT_EQ(GRID_LEVEL_SET, sdf->getGridClass()); EXPECT_EQ(float(s.halfband * s.points->voxelSize()[0]), sdf->background()); - EXPECT_EQ(Index32(17), sdf->tree().leafCount()); // less leaf nodes, active points are on a single face + EXPECT_EQ(Index64(17), sdf->tree().leafCount()); // less leaf nodes, active points are on a single face EXPECT_EQ(Index64(0), sdf->tree().activeTileCount()); EXPECT_EQ(Index64(485*4), sdf->tree().activeVoxelCount()); // 485 per sphere @@ -568,7 +568,7 @@ TEST_F(TestPointRasterizeSDF, testRasterizeVariableSpheres) EXPECT_EQ(GRID_LEVEL_SET, sdf->getGridClass()); EXPECT_EQ(float(s.halfband * s.points->voxelSize()[0]), sdf->background()); - EXPECT_EQ(Index32(8), sdf->tree().leafCount()); // less leaf nodes, active points are on a single face + EXPECT_EQ(Index64(8), sdf->tree().leafCount()); // less leaf nodes, active points are on a single face EXPECT_EQ(Index64(0), sdf->tree().activeTileCount()); EXPECT_EQ(Index64(1454), sdf->tree().activeVoxelCount()); // 485 per sphere @@ -732,7 +732,7 @@ TEST_F(TestPointRasterizeSDF, testRasterizeSmoothSpheres) EXPECT_EQ(GRID_LEVEL_SET, sdf->getGridClass()); EXPECT_EQ(float(s.halfband * s.points->voxelSize()[0]), sdf->background()); - EXPECT_EQ(Index32(44), sdf->tree().leafCount()); + EXPECT_EQ(Index64(44), sdf->tree().leafCount()); EXPECT_EQ(Index64(0), sdf->tree().activeTileCount()); EXPECT_EQ(Index64(6303), sdf->tree().activeVoxelCount()); const CoordBBox bounds(Coord(-7), Coord(7)); @@ -874,7 +874,7 @@ TEST_F(TestPointRasterizeSDF, testRasterizeVariableSmoothSpheres) EXPECT_TRUE(sdf->transform() == s.points->transform()); EXPECT_EQ(GRID_LEVEL_SET, sdf->getGridClass()); EXPECT_EQ(float(s.halfband * s.points->voxelSize()[0]), sdf->background()); - EXPECT_EQ(Index32(64), sdf->tree().leafCount()); + EXPECT_EQ(Index64(64), sdf->tree().leafCount()); EXPECT_EQ(Index64(0), sdf->tree().activeTileCount()); EXPECT_EQ(Index64(15011), sdf->tree().activeVoxelCount()); for (auto iter = sdf->cbeginValueOn(); iter; ++iter) { diff --git a/openvdb/openvdb/unittest/TestPointScatter.cc b/openvdb/openvdb/unittest/TestPointScatter.cc index ca8d92b070..58bf459ab6 100644 --- a/openvdb/openvdb/unittest/TestPointScatter.cc +++ b/openvdb/openvdb/unittest/TestPointScatter.cc @@ -36,7 +36,7 @@ TEST_F(TestPointScatter, testUniformPointScatter) BoolGrid grid; grid.sparseFill(boxBounds, false, /*active*/true); auto points = points::uniformPointScatter(grid, total); - EXPECT_EQ(Index32(8), points->tree().leafCount()); + EXPECT_EQ(Index64(8), points->tree().leafCount()); EXPECT_EQ(Index64(27), points->activeVoxelCount()); EXPECT_EQ(total, pointCount(points->tree())); } @@ -44,7 +44,7 @@ TEST_F(TestPointScatter, testUniformPointScatter) DoubleGrid grid; grid.sparseFill(boxBounds, 0.0, /*active*/true); auto points = points::uniformPointScatter(grid, total); - EXPECT_EQ(Index32(8), points->tree().leafCount()); + EXPECT_EQ(Index64(8), points->tree().leafCount()); EXPECT_EQ(Index64(27), points->activeVoxelCount()); EXPECT_EQ(total, pointCount(points->tree())); } @@ -52,7 +52,7 @@ TEST_F(TestPointScatter, testUniformPointScatter) FloatGrid grid; grid.sparseFill(boxBounds, 0.0f, /*active*/true); auto points = points::uniformPointScatter(grid, total); - EXPECT_EQ(Index32(8), points->tree().leafCount()); + EXPECT_EQ(Index64(8), points->tree().leafCount()); EXPECT_EQ(Index64(27), points->activeVoxelCount()); EXPECT_EQ(total, pointCount(points->tree())); } @@ -60,7 +60,7 @@ TEST_F(TestPointScatter, testUniformPointScatter) Int32Grid grid; grid.sparseFill(boxBounds, 0, /*active*/true); auto points = points::uniformPointScatter(grid, total); - EXPECT_EQ(Index32(8), points->tree().leafCount()); + EXPECT_EQ(Index64(8), points->tree().leafCount()); EXPECT_EQ(Index64(27), points->activeVoxelCount()); EXPECT_EQ(total, pointCount(points->tree())); } @@ -68,7 +68,7 @@ TEST_F(TestPointScatter, testUniformPointScatter) Int64Grid grid; grid.sparseFill(boxBounds, 0, /*active*/true); auto points = points::uniformPointScatter(grid, total); - EXPECT_EQ(Index32(8), points->tree().leafCount()); + EXPECT_EQ(Index64(8), points->tree().leafCount()); EXPECT_EQ(Index64(27), points->activeVoxelCount()); EXPECT_EQ(total, pointCount(points->tree())); } @@ -76,7 +76,7 @@ TEST_F(TestPointScatter, testUniformPointScatter) MaskGrid grid; grid.sparseFill(boxBounds, /*maskBuffer*/true); auto points = points::uniformPointScatter(grid, total); - EXPECT_EQ(Index32(8), points->tree().leafCount()); + EXPECT_EQ(Index64(8), points->tree().leafCount()); EXPECT_EQ(Index64(27), points->activeVoxelCount()); EXPECT_EQ(total, pointCount(points->tree())); } @@ -84,7 +84,7 @@ TEST_F(TestPointScatter, testUniformPointScatter) Vec3DGrid grid; grid.sparseFill(boxBounds, Vec3d(), /*active*/true); auto points = points::uniformPointScatter(grid, total); - EXPECT_EQ(Index32(8), points->tree().leafCount()); + EXPECT_EQ(Index64(8), points->tree().leafCount()); EXPECT_EQ(Index64(27), points->activeVoxelCount()); EXPECT_EQ(total, pointCount(points->tree())); } @@ -92,7 +92,7 @@ TEST_F(TestPointScatter, testUniformPointScatter) Vec3IGrid grid; grid.sparseFill(boxBounds, Vec3i(), /*active*/true); auto points = points::uniformPointScatter(grid, total); - EXPECT_EQ(Index32(8), points->tree().leafCount()); + EXPECT_EQ(Index64(8), points->tree().leafCount()); EXPECT_EQ(Index64(27), points->activeVoxelCount()); EXPECT_EQ(total, pointCount(points->tree())); } @@ -100,7 +100,7 @@ TEST_F(TestPointScatter, testUniformPointScatter) Vec3SGrid grid; grid.sparseFill(boxBounds, Vec3f(), /*active*/true); auto points = points::uniformPointScatter(grid, total); - EXPECT_EQ(Index32(8), points->tree().leafCount()); + EXPECT_EQ(Index64(8), points->tree().leafCount()); EXPECT_EQ(Index64(27), points->activeVoxelCount()); EXPECT_EQ(total, pointCount(points->tree())); } @@ -108,7 +108,7 @@ TEST_F(TestPointScatter, testUniformPointScatter) PointDataGrid grid; grid.sparseFill(boxBounds, 0, /*active*/true); auto points = points::uniformPointScatter(grid, total); - EXPECT_EQ(Index32(8), points->tree().leafCount()); + EXPECT_EQ(Index64(8), points->tree().leafCount()); EXPECT_EQ(Index64(27), points->activeVoxelCount()); EXPECT_EQ(total, pointCount(points->tree())); } @@ -128,7 +128,7 @@ TEST_F(TestPointScatter, testUniformPointScatter) BoolGrid grid; grid.sparseFill(boxBounds, false, /*active*/true); auto points = points::uniformPointScatter(grid, 1); - EXPECT_EQ(Index32(1), points->tree().leafCount()); + EXPECT_EQ(Index64(1), points->tree().leafCount()); EXPECT_EQ(Index64(1), points->activeVoxelCount()); EXPECT_EQ(Index64(1), pointCount(points->tree())); } @@ -145,7 +145,7 @@ TEST_F(TestPointScatter, testUniformPointScatter) auto points = points::uniformPointScatter(grid, total); EXPECT_EQ(Index64(0), points->tree().activeTileCount()); - EXPECT_EQ(Index32(1), points->tree().leafCount()); + EXPECT_EQ(Index64(1), points->tree().leafCount()); EXPECT_TRUE(Index64(NUM_VALUES) > points->tree().activeVoxelCount()); EXPECT_EQ(total, pointCount(points->tree())); @@ -226,7 +226,7 @@ TEST_F(TestPointScatter, testUniformPointScatter) points = points::uniformPointScatter(grid, total); - EXPECT_EQ(Index32(1), points->tree().leafCount()); + EXPECT_EQ(Index64(1), points->tree().leafCount()); EXPECT_TRUE(Index64(NUM_VALUES) > points->tree().activeVoxelCount()); EXPECT_EQ(total, pointCount(points->tree())); @@ -234,7 +234,7 @@ TEST_F(TestPointScatter, testUniformPointScatter) points = points::uniformPointScatter(grid, Index64(NUM_VALUES)); - EXPECT_EQ(Index32(1), points->tree().leafCount()); + EXPECT_EQ(Index64(1), points->tree().leafCount()); EXPECT_EQ(Index64(NUM_VALUES), points->activeVoxelCount()); EXPECT_EQ(Index64(NUM_VALUES), pointCount(points->tree())); @@ -262,7 +262,7 @@ TEST_F(TestPointScatter, testDenseUniformPointScatter) BoolGrid grid; grid.sparseFill(boxBounds, false, /*active*/true); auto points = points::denseUniformPointScatter(grid, pointsPerVoxel); - EXPECT_EQ(Index32(8), points->tree().leafCount()); + EXPECT_EQ(Index64(8), points->tree().leafCount()); EXPECT_EQ(Index64(27), points->activeVoxelCount()); EXPECT_EQ(Index64(pointsPerVoxel * 27), pointCount(points->tree())); } @@ -270,7 +270,7 @@ TEST_F(TestPointScatter, testDenseUniformPointScatter) DoubleGrid grid; grid.sparseFill(boxBounds, 0.0, /*active*/true); auto points = points::denseUniformPointScatter(grid, pointsPerVoxel); - EXPECT_EQ(Index32(8), points->tree().leafCount()); + EXPECT_EQ(Index64(8), points->tree().leafCount()); EXPECT_EQ(Index64(27), points->activeVoxelCount()); EXPECT_EQ(Index64(pointsPerVoxel * 27), pointCount(points->tree())); } @@ -278,7 +278,7 @@ TEST_F(TestPointScatter, testDenseUniformPointScatter) FloatGrid grid; grid.sparseFill(boxBounds, 0.0f, /*active*/true); auto points = points::denseUniformPointScatter(grid, pointsPerVoxel); - EXPECT_EQ(Index32(8), points->tree().leafCount()); + EXPECT_EQ(Index64(8), points->tree().leafCount()); EXPECT_EQ(Index64(27), points->activeVoxelCount()); EXPECT_EQ(Index64(pointsPerVoxel * 27), pointCount(points->tree())); } @@ -286,7 +286,7 @@ TEST_F(TestPointScatter, testDenseUniformPointScatter) Int32Grid grid; grid.sparseFill(boxBounds, 0, /*active*/true); auto points = points::denseUniformPointScatter(grid, pointsPerVoxel); - EXPECT_EQ(Index32(8), points->tree().leafCount()); + EXPECT_EQ(Index64(8), points->tree().leafCount()); EXPECT_EQ(Index64(27), points->activeVoxelCount()); EXPECT_EQ(Index64(pointsPerVoxel * 27), pointCount(points->tree())); } @@ -294,7 +294,7 @@ TEST_F(TestPointScatter, testDenseUniformPointScatter) Int64Grid grid; grid.sparseFill(boxBounds, 0, /*active*/true); auto points = points::denseUniformPointScatter(grid, pointsPerVoxel); - EXPECT_EQ(Index32(8), points->tree().leafCount()); + EXPECT_EQ(Index64(8), points->tree().leafCount()); EXPECT_EQ(Index64(27), points->activeVoxelCount()); EXPECT_EQ(Index64(pointsPerVoxel * 27), pointCount(points->tree())); } @@ -302,7 +302,7 @@ TEST_F(TestPointScatter, testDenseUniformPointScatter) MaskGrid grid; grid.sparseFill(boxBounds, /*maskBuffer*/true); auto points = points::denseUniformPointScatter(grid, pointsPerVoxel); - EXPECT_EQ(Index32(8), points->tree().leafCount()); + EXPECT_EQ(Index64(8), points->tree().leafCount()); EXPECT_EQ(Index64(27), points->activeVoxelCount()); EXPECT_EQ(Index64(pointsPerVoxel * 27), pointCount(points->tree())); } @@ -310,7 +310,7 @@ TEST_F(TestPointScatter, testDenseUniformPointScatter) Vec3DGrid grid; grid.sparseFill(boxBounds, Vec3d(), /*active*/true); auto points = points::denseUniformPointScatter(grid, pointsPerVoxel); - EXPECT_EQ(Index32(8), points->tree().leafCount()); + EXPECT_EQ(Index64(8), points->tree().leafCount()); EXPECT_EQ(Index64(27), points->activeVoxelCount()); EXPECT_EQ(Index64(pointsPerVoxel * 27), pointCount(points->tree())); } @@ -318,7 +318,7 @@ TEST_F(TestPointScatter, testDenseUniformPointScatter) Vec3IGrid grid; grid.sparseFill(boxBounds, Vec3i(), /*active*/true); auto points = points::denseUniformPointScatter(grid, pointsPerVoxel); - EXPECT_EQ(Index32(8), points->tree().leafCount()); + EXPECT_EQ(Index64(8), points->tree().leafCount()); EXPECT_EQ(Index64(27), points->activeVoxelCount()); EXPECT_EQ(Index64(pointsPerVoxel * 27), pointCount(points->tree())); } @@ -326,7 +326,7 @@ TEST_F(TestPointScatter, testDenseUniformPointScatter) Vec3SGrid grid; grid.sparseFill(boxBounds, Vec3f(), /*active*/true); auto points = points::denseUniformPointScatter(grid, pointsPerVoxel); - EXPECT_EQ(Index32(8), points->tree().leafCount()); + EXPECT_EQ(Index64(8), points->tree().leafCount()); EXPECT_EQ(Index64(27), points->activeVoxelCount()); EXPECT_EQ(Index64(pointsPerVoxel * 27), pointCount(points->tree())); } @@ -334,7 +334,7 @@ TEST_F(TestPointScatter, testDenseUniformPointScatter) PointDataGrid grid; grid.sparseFill(boxBounds, 0, /*active*/true); auto points = points::denseUniformPointScatter(grid, pointsPerVoxel); - EXPECT_EQ(Index32(8), points->tree().leafCount()); + EXPECT_EQ(Index64(8), points->tree().leafCount()); EXPECT_EQ(Index64(27), points->activeVoxelCount()); EXPECT_EQ(Index64(pointsPerVoxel * 27), pointCount(points->tree())); } @@ -354,7 +354,7 @@ TEST_F(TestPointScatter, testDenseUniformPointScatter) BoolGrid grid; grid.sparseFill(boxBounds, false, /*active*/true); auto points = points::denseUniformPointScatter(grid, 0.8f); - EXPECT_EQ(Index32(8), points->tree().leafCount()); + EXPECT_EQ(Index64(8), points->tree().leafCount()); // Note that a value of 22 is precomputed as the number of active // voxels/points produced by a value of 0.8 EXPECT_EQ(Index64(22), points->activeVoxelCount()); @@ -373,7 +373,7 @@ TEST_F(TestPointScatter, testDenseUniformPointScatter) const Index32 NUM_VALUES = BoolGrid::TreeType::LeafNodeType::NUM_VALUES; - EXPECT_EQ(Index32(1), grid.tree().leafCount()); + EXPECT_EQ(Index64(1), grid.tree().leafCount()); EXPECT_EQ(Index64(NUM_VALUES + 1), grid.activeVoxelCount()); auto points = points::denseUniformPointScatter(grid, pointsPerVoxel); @@ -381,7 +381,7 @@ TEST_F(TestPointScatter, testDenseUniformPointScatter) const Index64 expectedCount = Index64(pointsPerVoxel * (NUM_VALUES + 1)); EXPECT_EQ(Index64(0), points->tree().activeTileCount()); - EXPECT_EQ(Index32(2), points->tree().leafCount()); + EXPECT_EQ(Index64(2), points->tree().leafCount()); EXPECT_EQ(Index64(NUM_VALUES + 1), points->activeVoxelCount()); EXPECT_EQ(expectedCount, pointCount(points->tree())); @@ -462,7 +462,7 @@ TEST_F(TestPointScatter, testDenseUniformPointScatter) points = points::denseUniformPointScatter(grid, pointsPerVoxel); - EXPECT_EQ(Index32(2), points->tree().leafCount()); + EXPECT_EQ(Index64(2), points->tree().leafCount()); EXPECT_EQ(Index64(NUM_VALUES + 1), points->activeVoxelCount()); EXPECT_EQ(expectedCount, pointCount(points->tree())); } @@ -480,7 +480,7 @@ TEST_F(TestPointScatter, testNonUniformPointScatter) grid.sparseFill(totalBoxBounds, false, /*active*/true); grid.sparseFill(activeBoxBounds, true, /*active*/true); auto points = points::nonUniformPointScatter(grid, pointsPerVoxel); - EXPECT_EQ(Index32(8), points->tree().leafCount()); + EXPECT_EQ(Index64(8), points->tree().leafCount()); EXPECT_EQ(Index64(27), points->activeVoxelCount()); EXPECT_EQ(Index64(pointsPerVoxel * 27), pointCount(points->tree())); } @@ -489,7 +489,7 @@ TEST_F(TestPointScatter, testNonUniformPointScatter) grid.sparseFill(totalBoxBounds, 0.0, /*active*/true); grid.sparseFill(activeBoxBounds, 1.0, /*active*/true); auto points = points::nonUniformPointScatter(grid, pointsPerVoxel); - EXPECT_EQ(Index32(8), points->tree().leafCount()); + EXPECT_EQ(Index64(8), points->tree().leafCount()); EXPECT_EQ(Index64(27), points->activeVoxelCount()); EXPECT_EQ(Index64(pointsPerVoxel * 27), pointCount(points->tree())); } @@ -498,7 +498,7 @@ TEST_F(TestPointScatter, testNonUniformPointScatter) grid.sparseFill(totalBoxBounds, 0.0f, /*active*/true); grid.sparseFill(activeBoxBounds, 1.0f, /*active*/true); auto points = points::nonUniformPointScatter(grid, pointsPerVoxel); - EXPECT_EQ(Index32(8), points->tree().leafCount()); + EXPECT_EQ(Index64(8), points->tree().leafCount()); EXPECT_EQ(Index64(27), points->activeVoxelCount()); EXPECT_EQ(Index64(pointsPerVoxel * 27), pointCount(points->tree())); } @@ -507,7 +507,7 @@ TEST_F(TestPointScatter, testNonUniformPointScatter) grid.sparseFill(totalBoxBounds, 0, /*active*/true); grid.sparseFill(activeBoxBounds, 1, /*active*/true); auto points = points::nonUniformPointScatter(grid, pointsPerVoxel); - EXPECT_EQ(Index32(8), points->tree().leafCount()); + EXPECT_EQ(Index64(8), points->tree().leafCount()); EXPECT_EQ(Index64(27), points->activeVoxelCount()); EXPECT_EQ(Index64(pointsPerVoxel * 27), pointCount(points->tree())); } @@ -516,7 +516,7 @@ TEST_F(TestPointScatter, testNonUniformPointScatter) grid.sparseFill(totalBoxBounds, 0, /*active*/true); grid.sparseFill(activeBoxBounds, 1, /*active*/true); auto points = points::nonUniformPointScatter(grid, pointsPerVoxel); - EXPECT_EQ(Index32(8), points->tree().leafCount()); + EXPECT_EQ(Index64(8), points->tree().leafCount()); EXPECT_EQ(Index64(27), points->activeVoxelCount()); EXPECT_EQ(Index64(pointsPerVoxel * 27), pointCount(points->tree())); } @@ -525,7 +525,7 @@ TEST_F(TestPointScatter, testNonUniformPointScatter) grid.sparseFill(totalBoxBounds, /*maskBuffer*/0); grid.sparseFill(activeBoxBounds, /*maskBuffer*/1); auto points = points::nonUniformPointScatter(grid, pointsPerVoxel); - EXPECT_EQ(Index32(8), points->tree().leafCount()); + EXPECT_EQ(Index64(8), points->tree().leafCount()); EXPECT_EQ(Index64(27), points->activeVoxelCount()); EXPECT_EQ(Index64(pointsPerVoxel * 27), pointCount(points->tree())); } @@ -543,7 +543,7 @@ TEST_F(TestPointScatter, testNonUniformPointScatter) const Index32 NUM_VALUES = BoolGrid::TreeType::LeafNodeType::NUM_VALUES; - EXPECT_EQ(Index32(1), grid.tree().leafCount()); + EXPECT_EQ(Index64(1), grid.tree().leafCount()); EXPECT_EQ(Index64(NUM_VALUES + 1), grid.activeVoxelCount()); auto points = points::nonUniformPointScatter(grid, pointsPerVoxel); @@ -551,7 +551,7 @@ TEST_F(TestPointScatter, testNonUniformPointScatter) const Index64 expectedCount = Index64(pointsPerVoxel * (NUM_VALUES + 1)); EXPECT_EQ(Index64(0), points->tree().activeTileCount()); - EXPECT_EQ(Index32(2), points->tree().leafCount()); + EXPECT_EQ(Index64(2), points->tree().leafCount()); EXPECT_EQ(Index64(NUM_VALUES + 1), points->activeVoxelCount()); EXPECT_EQ(expectedCount, pointCount(points->tree())); @@ -637,7 +637,7 @@ TEST_F(TestPointScatter, testNonUniformPointScatter) points = points::nonUniformPointScatter(countGrid, pointsPerVoxel); - EXPECT_EQ(Index32(1), points->tree().leafCount()); + EXPECT_EQ(Index64(1), points->tree().leafCount()); EXPECT_EQ(Index64(7), points->activeVoxelCount()); EXPECT_EQ(Index64(pointsPerVoxel * 28), pointCount(points->tree())); @@ -656,7 +656,7 @@ TEST_F(TestPointScatter, testNonUniformPointScatter) points = points::nonUniformPointScatter(grid, pointsPerVoxel); - EXPECT_EQ(Index32(2), points->tree().leafCount()); + EXPECT_EQ(Index64(2), points->tree().leafCount()); EXPECT_EQ(Index64(NUM_VALUES + 1), points->activeVoxelCount()); EXPECT_EQ(expectedCount, pointCount(points->tree())); } diff --git a/openvdb/openvdb/unittest/TestRootNode.cc b/openvdb/openvdb/unittest/TestRootNode.cc new file mode 100644 index 0000000000..e774f3b66b --- /dev/null +++ b/openvdb/openvdb/unittest/TestRootNode.cc @@ -0,0 +1,616 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 + +#include +#include + +#include + +using namespace openvdb; +using namespace tree; + +class TestRoot: public ::testing::Test +{ +}; + + +TEST_F(TestRoot, test) +{ + using RootNodeType = FloatTree::RootNodeType; + using ChildType = RootNodeType::ChildNodeType; + const Coord c0(0,0,0), c1(49152, 16384, 28672); + + { // test inserting child nodes directly and indirectly + RootNodeType root(0.0f); + EXPECT_TRUE(root.empty()); + EXPECT_EQ(Index32(0), root.childCount()); + + // populate the tree by inserting the two leaf nodes containing c0 and c1 + root.touchLeaf(c0); + root.touchLeaf(c1); + EXPECT_EQ(Index(2), root.getTableSize()); + EXPECT_EQ(Index32(2), root.childCount()); + EXPECT_TRUE(!root.hasActiveTiles()); + + { // verify c0 and c1 are the root node coordinates + auto rootIter = root.cbeginChildOn(); + EXPECT_EQ(c0, rootIter.getCoord()); + ++rootIter; + EXPECT_EQ(c1, rootIter.getCoord()); + } + + // copy the root node + RootNodeType rootCopy(root); + + // steal the root node children leaving the root node empty again + std::vector children; + root.stealNodes(children); + EXPECT_TRUE(root.empty()); + + // insert the root node children directly + for (ChildType* child : children) { + root.addChild(child); + } + EXPECT_EQ(Index(2), root.getTableSize()); + EXPECT_EQ(Index32(2), root.childCount()); + + { // verify the coordinates of the root node children + auto rootIter = root.cbeginChildOn(); + EXPECT_EQ(c0, rootIter.getCoord()); + ++rootIter; + EXPECT_EQ(c1, rootIter.getCoord()); + } + } + + { // test inserting tiles and replacing them with child nodes + RootNodeType root(0.0f); + EXPECT_TRUE(root.empty()); + + // no-op + root.addChild(nullptr); + + // populate the root node by inserting tiles + root.addTile(c0, /*value=*/1.0f, /*state=*/true); + root.addTile(c1, /*value=*/2.0f, /*state=*/true); + EXPECT_EQ(Index(2), root.getTableSize()); + EXPECT_EQ(Index32(0), root.childCount()); + EXPECT_TRUE(root.hasActiveTiles()); + EXPECT_NEAR(root.getValue(c0), 1.0f, /*tolerance=*/0.0); + EXPECT_NEAR(root.getValue(c1), 2.0f, /*tolerance=*/0.0); + + // insert child nodes with the same coordinates + root.addChild(new ChildType(c0, 3.0f)); + root.addChild(new ChildType(c1, 4.0f)); + + // insert a new child at c0 + root.addChild(new ChildType(c0, 5.0f)); + + // verify active tiles have been replaced by child nodes + EXPECT_EQ(Index(2), root.getTableSize()); + EXPECT_EQ(Index32(2), root.childCount()); + EXPECT_TRUE(!root.hasActiveTiles()); + + { // verify the coordinates of the root node children + auto rootIter = root.cbeginChildOn(); + EXPECT_EQ(c0, rootIter.getCoord()); + EXPECT_NEAR(root.getValue(c0), 5.0f, /*tolerance=*/0.0); + ++rootIter; + EXPECT_EQ(c1, rootIter.getCoord()); + } + } + + { // test transient data + RootNodeType rootNode(0.0f); + EXPECT_EQ(Index32(0), rootNode.transientData()); + rootNode.setTransientData(Index32(5)); + EXPECT_EQ(Index32(5), rootNode.transientData()); + RootNodeType rootNode2(rootNode); + EXPECT_EQ(Index32(5), rootNode2.transientData()); + RootNodeType rootNode3 = rootNode; + EXPECT_EQ(Index32(5), rootNode3.transientData()); + } +} + +TEST_F(TestRoot, testMap) +{ + using RootNode = FloatTree::RootNodeType; + + { // empty root node + RootNode root(1.0f); + + // background checks + + EXPECT_EQ(root.background(), 1.0f); + root.setBackground(2.0f, false); + EXPECT_EQ(root.background(), 2.0f); + EXPECT_EQ(root.numBackgroundTiles(), 0); + + // count checks + + EXPECT_TRUE(root.empty()); + EXPECT_FALSE(root.hasActiveTiles()); + EXPECT_EQ(root.getTableSize(), 0); + EXPECT_EQ(root.leafCount(), 0); + EXPECT_EQ(root.nonLeafCount(), 1); // root counts as a node + EXPECT_EQ(root.childCount(), 0); + EXPECT_EQ(root.tileCount(), 0); + EXPECT_EQ(root.activeTileCount(), 0); + EXPECT_EQ(root.inactiveTileCount(), 0); + + EXPECT_EQ(root.onVoxelCount(), 0); + EXPECT_EQ(root.offVoxelCount(), 0); + EXPECT_EQ(root.onLeafVoxelCount(), 0); + EXPECT_EQ(root.offLeafVoxelCount(), 0); + EXPECT_EQ(root.onTileCount(), 0); + + // bounding box checks + + EXPECT_EQ(root.getMinIndex(), Coord()); + EXPECT_EQ(root.getMaxIndex(), Coord()); + EXPECT_EQ(root.getWidth(), 0); + EXPECT_EQ(root.getHeight(), 0); + EXPECT_EQ(root.getDepth(), 0); + EXPECT_EQ(root.getNodeBoundingBox(), CoordBBox::inf()); // always infinite + + CoordBBox bbox; + root.evalActiveBoundingBox(bbox); + EXPECT_EQ(bbox, CoordBBox()); // empty bbox + + root.getIndexRange(bbox); + EXPECT_EQ(bbox, CoordBBox(Coord(0), Coord(0))); // zero bbox + + // origin checks + + root.setOrigin(Coord(0, 0, 0)); + EXPECT_THROW(root.setOrigin(Coord(1, 2, 3)), ValueError); // non-zero origins not supported + + // key checks + + EXPECT_EQ(root.getValueDepth(Coord(0, 0, 0)), -1); + + EXPECT_EQ(root.coordToKey(Coord(0, 0, 0)), Coord(0, 0, 0)); + EXPECT_EQ(root.coordToKey(Coord(1, 2, 3)), Coord(0, 0, 0)); + EXPECT_EQ(root.coordToKey(Coord(5000, 6000, 7000)), Coord(4096, 4096, 4096)); + + EXPECT_FALSE(root.hasKey(Coord(0, 0, 0))); + } + + { // one active, non-background root node tile + RootNode root(1.0f); + root.addTile(Coord(1, 2, 3), 2.0f, true); + + // background checks + + EXPECT_EQ(root.background(), 1.0f); + EXPECT_EQ(root.numBackgroundTiles(), 0); + + // count checks + + EXPECT_FALSE(root.empty()); + EXPECT_TRUE(root.hasActiveTiles()); + EXPECT_EQ(root.getTableSize(), 1); + EXPECT_EQ(root.leafCount(), 0); + EXPECT_EQ(root.nonLeafCount(), 1); + EXPECT_EQ(root.childCount(), 0); + EXPECT_EQ(root.tileCount(), 1); + EXPECT_EQ(root.activeTileCount(), 1); + EXPECT_EQ(root.inactiveTileCount(), 0); + + Index64 voxels = Index64(4096) * 4096 * 4096; + EXPECT_EQ(root.onVoxelCount(), voxels); + EXPECT_EQ(root.offVoxelCount(), 0); + EXPECT_EQ(root.onLeafVoxelCount(), 0); + EXPECT_EQ(root.offLeafVoxelCount(), 0); + EXPECT_EQ(root.onTileCount(), 1); + + // bounding box checks + + EXPECT_EQ(root.getMinIndex(), Coord(0)); + EXPECT_EQ(root.getMaxIndex(), Coord(4095)); + EXPECT_EQ(root.getWidth(), 4095); + EXPECT_EQ(root.getHeight(), 4095); + EXPECT_EQ(root.getDepth(), 4095); + EXPECT_EQ(root.getNodeBoundingBox(), CoordBBox::inf()); // always infinite + + CoordBBox bbox; + root.evalActiveBoundingBox(bbox); + EXPECT_EQ(bbox, CoordBBox(Coord(0), Coord(4095))); + + root.getIndexRange(bbox); + EXPECT_EQ(bbox, CoordBBox(Coord(0), Coord(4095))); + + // key checks + + EXPECT_EQ(root.getValueDepth(Coord(0, 0, 0)), 0); + + EXPECT_EQ(root.coordToKey(Coord(0, 0, 0)), Coord(0, 0, 0)); + EXPECT_EQ(root.coordToKey(Coord(1, 2, 3)), Coord(0, 0, 0)); + EXPECT_EQ(root.coordToKey(Coord(5000, 6000, 7000)), Coord(4096, 4096, 4096)); + + EXPECT_TRUE(root.hasKey(Coord(0, 0, 0))); + EXPECT_FALSE(root.hasKey(Coord(1, 2, 3))); + + // erase background tiles + + root.eraseBackgroundTiles(); + EXPECT_EQ(root.getTableSize(), 1); + EXPECT_TRUE(root.hasKey(Coord(0, 0, 0))); + + // clear root + + root.clear(); + EXPECT_EQ(root.getTableSize(), 0); + EXPECT_FALSE(root.hasKey(Coord(0, 0, 0))); + } + + { // one inactive, background root node tile + RootNode root(1.0f); + root.addTile(Coord(1, 2, 3), 1.0f, false); + + // background checks + + EXPECT_EQ(root.background(), 1.0f); + EXPECT_EQ(root.numBackgroundTiles(), 1); + + // count checks + + EXPECT_TRUE(root.empty()); // root is empty if it only has inactive background tiles + EXPECT_FALSE(root.hasActiveTiles()); + EXPECT_EQ(root.getTableSize(), 1); + EXPECT_EQ(root.leafCount(), 0); + EXPECT_EQ(root.nonLeafCount(), 1); + EXPECT_EQ(root.childCount(), 0); + EXPECT_EQ(root.tileCount(), 1); + EXPECT_EQ(root.activeTileCount(), 0); + EXPECT_EQ(root.inactiveTileCount(), 1); + + EXPECT_EQ(root.onVoxelCount(), 0); + EXPECT_EQ(root.offVoxelCount(), 0); + EXPECT_EQ(root.onLeafVoxelCount(), 0); + EXPECT_EQ(root.offLeafVoxelCount(), 0); + EXPECT_EQ(root.onTileCount(), 0); + + // bounding box checks + + EXPECT_EQ(root.getMinIndex(), Coord(0)); + EXPECT_EQ(root.getMaxIndex(), Coord(4095)); + EXPECT_EQ(root.getWidth(), 4095); + EXPECT_EQ(root.getHeight(), 4095); + EXPECT_EQ(root.getDepth(), 4095); + EXPECT_EQ(root.getNodeBoundingBox(), CoordBBox::inf()); // always infinite + + CoordBBox bbox; + root.evalActiveBoundingBox(bbox); + EXPECT_EQ(bbox, CoordBBox()); // empty bbox + + root.getIndexRange(bbox); + EXPECT_EQ(bbox, CoordBBox(Coord(0), Coord(4095))); + + // key checks + + EXPECT_EQ(root.getValueDepth(Coord(0, 0, 0)), 0); + + EXPECT_EQ(root.coordToKey(Coord(0, 0, 0)), Coord(0, 0, 0)); + EXPECT_EQ(root.coordToKey(Coord(1, 2, 3)), Coord(0, 0, 0)); + EXPECT_EQ(root.coordToKey(Coord(5000, 6000, 7000)), Coord(4096, 4096, 4096)); + + EXPECT_TRUE(root.hasKey(Coord(0, 0, 0))); + EXPECT_FALSE(root.hasKey(Coord(1, 2, 3))); + + // erase background tiles + + root.eraseBackgroundTiles(); + EXPECT_EQ(root.getTableSize(), 0); + EXPECT_FALSE(root.hasKey(Coord(0, 0, 0))); + } + + { // one active, background root node tile + RootNode root(1.0f); + root.addTile(Coord(1, 2, 3), 1.0f, true); + + // background checks + + EXPECT_EQ(root.background(), 1.0f); + EXPECT_EQ(root.numBackgroundTiles(), 0); + + // count checks + + EXPECT_FALSE(root.empty()); + EXPECT_TRUE(root.hasActiveTiles()); + EXPECT_EQ(root.getTableSize(), 1); + EXPECT_EQ(root.leafCount(), 0); + EXPECT_EQ(root.nonLeafCount(), 1); + EXPECT_EQ(root.childCount(), 0); + EXPECT_EQ(root.tileCount(), 1); + EXPECT_EQ(root.activeTileCount(), 1); + EXPECT_EQ(root.inactiveTileCount(), 0); + + Index64 voxels = Index64(4096) * 4096 * 4096; + EXPECT_EQ(root.onVoxelCount(), voxels); + EXPECT_EQ(root.offVoxelCount(), 0); + EXPECT_EQ(root.onLeafVoxelCount(), 0); + EXPECT_EQ(root.offLeafVoxelCount(), 0); + EXPECT_EQ(root.onTileCount(), 1); + + // bounding box checks + + EXPECT_EQ(root.getMinIndex(), Coord(0)); + EXPECT_EQ(root.getMaxIndex(), Coord(4095)); + EXPECT_EQ(root.getWidth(), 4095); + EXPECT_EQ(root.getHeight(), 4095); + EXPECT_EQ(root.getDepth(), 4095); + EXPECT_EQ(root.getNodeBoundingBox(), CoordBBox::inf()); // always infinite + + CoordBBox bbox; + root.evalActiveBoundingBox(bbox); + EXPECT_EQ(bbox, CoordBBox(Coord(0), Coord(4095))); + + root.getIndexRange(bbox); + EXPECT_EQ(bbox, CoordBBox(Coord(0), Coord(4095))); + + // key checks + + EXPECT_EQ(root.getValueDepth(Coord(0, 0, 0)), 0); + + EXPECT_EQ(root.coordToKey(Coord(0, 0, 0)), Coord(0, 0, 0)); + EXPECT_EQ(root.coordToKey(Coord(1, 2, 3)), Coord(0, 0, 0)); + EXPECT_EQ(root.coordToKey(Coord(5000, 6000, 7000)), Coord(4096, 4096, 4096)); + + EXPECT_TRUE(root.hasKey(Coord(0, 0, 0))); + EXPECT_FALSE(root.hasKey(Coord(1, 2, 3))); + } + + { // one internal node tile (which implicitly adds a root node child) + RootNode root(1.0f); + root.addTile(2, Coord(1, 2, 3), 2.0f, true); + + // count checks + + EXPECT_FALSE(root.empty()); + EXPECT_TRUE(root.hasActiveTiles()); // this method recurses down the tree + EXPECT_EQ(root.getTableSize(), 1); + EXPECT_EQ(root.leafCount(), 0); + EXPECT_EQ(root.nonLeafCount(), 2); + EXPECT_EQ(root.childCount(), 1); + EXPECT_EQ(root.tileCount(), 0); + EXPECT_EQ(root.activeTileCount(), 0); + EXPECT_EQ(root.inactiveTileCount(), 0); + + Index64 totalVoxels = Index64(4096) * 4096 * 4096; + Index64 onVoxels = Index64(128) * 128 * 128; + EXPECT_EQ(root.onVoxelCount(), onVoxels); + EXPECT_EQ(root.offVoxelCount(), totalVoxels - onVoxels); + EXPECT_EQ(root.onLeafVoxelCount(), 0); + EXPECT_EQ(root.offLeafVoxelCount(), 0); + EXPECT_EQ(root.onTileCount(), 1); + + // bounding box checks + + EXPECT_EQ(root.getMinIndex(), Coord(0)); + EXPECT_EQ(root.getMaxIndex(), Coord(4095)); + EXPECT_EQ(root.getWidth(), 4095); + EXPECT_EQ(root.getHeight(), 4095); + EXPECT_EQ(root.getDepth(), 4095); + EXPECT_EQ(root.getNodeBoundingBox(), CoordBBox::inf()); // always infinite + + CoordBBox bbox; + root.evalActiveBoundingBox(bbox); + EXPECT_EQ(bbox, CoordBBox(Coord(0), Coord(127))); + + root.getIndexRange(bbox); + EXPECT_EQ(bbox, CoordBBox(Coord(0), Coord(4095))); + + // key checks + + EXPECT_EQ(root.getValueDepth(Coord(0, 0, 0)), 1); // InternalNode 1 + + EXPECT_EQ(root.coordToKey(Coord(0, 0, 0)), Coord(0, 0, 0)); + EXPECT_EQ(root.coordToKey(Coord(1, 2, 3)), Coord(0, 0, 0)); + EXPECT_EQ(root.coordToKey(Coord(5000, 6000, 7000)), Coord(4096, 4096, 4096)); + + EXPECT_TRUE(root.hasKey(Coord(0, 0, 0))); + EXPECT_FALSE(root.hasKey(Coord(1, 2, 3))); + } +} + +TEST_F(TestRoot, testDelete) +{ + 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, true); + + auto* child = new RootNode::ChildNodeType(Coord(0, 0, 4096), 5.0f, true); + EXPECT_TRUE(root.addChild(child)); // always returns true + + EXPECT_EQ(root.getTableSize(), 3); + EXPECT_TRUE(root.hasKey(Coord(0, 0, 0))); + EXPECT_TRUE(root.hasKey(Coord(4096, 0, 0))); + EXPECT_TRUE(root.hasKey(Coord(0, 0, 4096))); + EXPECT_FALSE(root.hasKey(Coord(4096, 4096, 0))); + + EXPECT_FALSE(root.deleteChildOrTile(Coord(4096, 4096, 0))); + + EXPECT_EQ(root.getTableSize(), 3); + EXPECT_TRUE(root.hasKey(Coord(0, 0, 0))); + EXPECT_TRUE(root.hasKey(Coord(4096, 0, 0))); + EXPECT_TRUE(root.hasKey(Coord(0, 0, 4096))); + EXPECT_FALSE(root.hasKey(Coord(4096, 4096, 0))); + + EXPECT_TRUE(root.deleteChildOrTile(Coord(4096, 5, 6))); + + EXPECT_EQ(root.getTableSize(), 2); + EXPECT_TRUE(root.hasKey(Coord(0, 0, 0))); + EXPECT_FALSE(root.hasKey(Coord(4096, 0, 0))); + EXPECT_TRUE(root.hasKey(Coord(0, 0, 4096))); + EXPECT_FALSE(root.hasKey(Coord(4096, 4096, 0))); + + EXPECT_TRUE(root.deleteChildOrTile(Coord(1, 5, 4097))); + + EXPECT_EQ(root.getTableSize(), 1); + EXPECT_TRUE(root.hasKey(Coord(0, 0, 0))); + EXPECT_FALSE(root.hasKey(Coord(4096, 0, 0))); + EXPECT_FALSE(root.hasKey(Coord(0, 0, 4096))); + EXPECT_FALSE(root.hasKey(Coord(4096, 4096, 0))); + + EXPECT_TRUE(root.deleteChildOrTile(Coord(1, 5, 7))); + + EXPECT_EQ(root.getTableSize(), 0); + EXPECT_FALSE(root.hasKey(Coord(0, 0, 0))); + EXPECT_FALSE(root.hasKey(Coord(4096, 0, 0))); + EXPECT_FALSE(root.hasKey(Coord(0, 0, 4096))); + EXPECT_FALSE(root.hasKey(Coord(4096, 4096, 0))); +} + + +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)); + } +} + +TEST_F(TestRoot, testUnsafe) +{ + 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 + + { // get value + EXPECT_EQ(root.getTileValueUnsafe(Coord(1, 2, 3)), 2.0f); + EXPECT_EQ(root.getTileValueUnsafe(Coord(4096, 2, 3)), 3.0f); + float value = -1.0f; + EXPECT_TRUE(root.getTileValueUnsafe(Coord(1, 2, 3), value)); + EXPECT_EQ(value, 2.0f); value = -1.0f; + EXPECT_FALSE(root.getTileValueUnsafe(Coord(4096, 2, 3), value)); + EXPECT_EQ(value, 3.0f); value = -1.0f; + } + + { // get child + auto* node1 = root.getChildUnsafe(Coord(0, 0, 4096)); + EXPECT_TRUE(node1); + const RootNode& constRoot = root; + auto* node2 = constRoot.getChildUnsafe(Coord(0, 0, 4096)); + EXPECT_TRUE(node2); + auto* node3 = root.getConstChildUnsafe(Coord(0, 0, 4096)); + EXPECT_TRUE(node3); + } +} diff --git a/openvdb/openvdb/unittest/TestTools.cc b/openvdb/openvdb/unittest/TestTools.cc index 931f879614..df17050a06 100644 --- a/openvdb/openvdb/unittest/TestTools.cc +++ b/openvdb/openvdb/unittest/TestTools.cc @@ -1498,7 +1498,6 @@ struct FloatToVec } - TEST_F(TestTools, testTransformValues) { using openvdb::CoordBBox; @@ -1707,8 +1706,8 @@ TEST_F(TestTools, testPrune) const float value = 5.345f; FloatTree tree(value); - EXPECT_EQ(Index32(0), tree.leafCount()); - EXPECT_EQ(Index32(1), tree.nonLeafCount()); // root node + EXPECT_EQ(Index64(0), tree.leafCount()); + EXPECT_EQ(Index64(1), tree.nonLeafCount()); // root node EXPECT_TRUE(tree.empty()); tree.fill(CoordBBox(Coord(-10), Coord(10)), value, /*active=*/false); @@ -1716,8 +1715,8 @@ TEST_F(TestTools, testPrune) tools::prune(tree); - EXPECT_EQ(Index32(0), tree.leafCount()); - EXPECT_EQ(Index32(1), tree.nonLeafCount()); // root node + EXPECT_EQ(Index64(0), tree.leafCount()); + EXPECT_EQ(Index64(1), tree.nonLeafCount()); // root node EXPECT_TRUE(tree.empty()); } @@ -1739,18 +1738,18 @@ TEST_F(TestTools, testPrune) FloatTree tree(val); tree.addLeaf(leaf); - EXPECT_EQ(Index32(1), tree.leafCount()); - EXPECT_EQ(Index32(3), tree.nonLeafCount()); // root+2*internal + EXPECT_EQ(Index64(1), tree.leafCount()); + EXPECT_EQ(Index64(3), tree.nonLeafCount()); // root+2*internal tools::prune(tree);// tolerance is zero - EXPECT_EQ(Index32(1), tree.leafCount()); - EXPECT_EQ(Index32(3), tree.nonLeafCount()); // root+2*internal + EXPECT_EQ(Index64(1), tree.leafCount()); + EXPECT_EQ(Index64(3), tree.nonLeafCount()); // root+2*internal tools::prune(tree, tol); - EXPECT_EQ(Index32(0), tree.leafCount()); - EXPECT_EQ(Index32(3), tree.nonLeafCount()); // root+2*internal + EXPECT_EQ(Index64(0), tree.leafCount()); + EXPECT_EQ(Index64(3), tree.nonLeafCount()); // root+2*internal std::sort(data.begin(), data.end()); const float median = data[(LeafNodeT::NUM_VALUES-1)>>1]; @@ -1765,7 +1764,7 @@ TEST_F(TestTools, testPrune) io::File sourceFile("/usr/pic1/Data/OpenVDB/LevelSetModels/crawler.vdb"); sourceFile.open(false);//disable delayed loading FloatGrid::Ptr grid = gridPtrCast(sourceFile.getGrids()->at(0)); - const Index32 leafCount = grid->tree().leafCount(); + const Index64 leafCount = grid->tree().leafCount(); timer.start("\nSerial tolerance prune"); grid->tree().prune(); @@ -1778,7 +1777,7 @@ TEST_F(TestTools, testPrune) io::File sourceFile("/usr/pic1/Data/OpenVDB/LevelSetModels/crawler.vdb"); sourceFile.open(false);//disable delayed loading FloatGrid::Ptr grid = gridPtrCast(sourceFile.getGrids()->at(0)); - const Index32 leafCount = grid->tree().leafCount(); + const Index64 leafCount = grid->tree().leafCount(); timer.start("\nParallel tolerance prune"); tools::prune(grid->tree()); diff --git a/openvdb/openvdb/unittest/TestTree.cc b/openvdb/openvdb/unittest/TestTree.cc index 92ab90a5a1..a5ac2afade 100644 --- a/openvdb/openvdb/unittest/TestTree.cc +++ b/openvdb/openvdb/unittest/TestTree.cc @@ -1343,15 +1343,15 @@ TEST_F(TestTree, testTopologyUnion) tree0.addTile(1, xyz, true, true); // leaf level tile tree1.touchLeaf(xyz)->setValueOn(0); // single leaf tree0.topologyUnion(tree1, true); // single tile - EXPECT_EQ(openvdb::Index32(0), tree0.leafCount()); - EXPECT_EQ(openvdb::Index32(3), tree0.nonLeafCount()); + EXPECT_EQ(openvdb::Index64(0), tree0.leafCount()); + EXPECT_EQ(openvdb::Index64(3), tree0.nonLeafCount()); EXPECT_EQ(openvdb::Index64(1), tree0.activeTileCount()); EXPECT_EQ(openvdb::Index64(LeafT::NUM_VOXELS), tree0.activeVoxelCount()); tree1.addTile(1, xyz + openvdb::Coord(8), true, true); // leaf + tile tree0.topologyUnion(tree1, true); // two tiles - EXPECT_EQ(openvdb::Index32(0), tree0.leafCount()); - EXPECT_EQ(openvdb::Index32(3), tree0.nonLeafCount()); + EXPECT_EQ(openvdb::Index64(0), tree0.leafCount()); + EXPECT_EQ(openvdb::Index64(3), tree0.nonLeafCount()); EXPECT_EQ(openvdb::Index64(2), tree0.activeTileCount()); EXPECT_EQ(openvdb::Index64(LeafT::NUM_VOXELS*2), tree0.activeVoxelCount()); @@ -1359,8 +1359,8 @@ TEST_F(TestTree, testTopologyUnion) tree0.clear(); tree0.addTile(2, xyz, true, true); tree0.topologyUnion(tree1, true); // all topology in tree1 is already active. no change - EXPECT_EQ(openvdb::Index32(0), tree0.leafCount()); - EXPECT_EQ(openvdb::Index32(2), tree0.nonLeafCount()); + EXPECT_EQ(openvdb::Index64(0), tree0.leafCount()); + EXPECT_EQ(openvdb::Index64(2), tree0.nonLeafCount()); EXPECT_EQ(openvdb::Index64(1), tree0.activeTileCount()); EXPECT_EQ(openvdb::Index64(InternalT1::NUM_VOXELS), tree0.activeVoxelCount()); @@ -1368,8 +1368,8 @@ TEST_F(TestTree, testTopologyUnion) tree0.clear(); tree0.addTile(3, xyz, true, true); tree0.topologyUnion(tree1, true); - EXPECT_EQ(openvdb::Index32(0), tree0.leafCount()); - EXPECT_EQ(openvdb::Index32(1), tree0.nonLeafCount()); + EXPECT_EQ(openvdb::Index64(0), tree0.leafCount()); + EXPECT_EQ(openvdb::Index64(1), tree0.nonLeafCount()); EXPECT_EQ(openvdb::Index64(1), tree0.activeTileCount()); EXPECT_EQ(openvdb::Index64(InternalT2::NUM_VOXELS), tree0.activeVoxelCount()); @@ -1379,8 +1379,8 @@ TEST_F(TestTree, testTopologyUnion) tree0.addTile(1, xyz, true, true); tree1.addTile(2, xyz, true, true); tree0.topologyUnion(tree1, true); - EXPECT_EQ(openvdb::Index32(0), tree0.leafCount()); - EXPECT_EQ(openvdb::Index32(3), tree0.nonLeafCount()); + EXPECT_EQ(openvdb::Index64(0), tree0.leafCount()); + EXPECT_EQ(openvdb::Index64(3), tree0.nonLeafCount()); openvdb::Index64 tiles = openvdb::Index64(InternalT1::DIM) / InternalT1::getChildDim(); tiles = tiles * tiles * tiles; EXPECT_EQ(tiles, tree0.activeTileCount()); @@ -1429,22 +1429,22 @@ TEST_F(TestTree, testTopologyIntersection) tree0.setValue(openvdb::Coord( 400, 30, 20), 2.0f); tree0.setValue(openvdb::Coord( 8, 11, 11), 3.0f); EXPECT_EQ(openvdb::Index64(3), tree0.activeVoxelCount()); - EXPECT_EQ(openvdb::Index32(3), tree0.leafCount() ); + EXPECT_EQ(openvdb::Index64(3), tree0.leafCount() ); tree1.setValue(openvdb::Coord( 500, 301, 200), 4.0f); tree1.setValue(openvdb::Coord( 400, 30, 20), 5.0f); tree1.setValue(openvdb::Coord( 8, 11, 11), 6.0f); EXPECT_EQ(openvdb::Index64(3), tree1.activeVoxelCount()); - EXPECT_EQ(openvdb::Index32(3), tree1.leafCount() ); + EXPECT_EQ(openvdb::Index64(3), tree1.leafCount() ); tree1.topologyIntersection(tree0); - EXPECT_EQ( openvdb::Index32(3), tree1.leafCount() ); + EXPECT_EQ( openvdb::Index64(3), tree1.leafCount() ); EXPECT_EQ( openvdb::Index64(2), tree1.activeVoxelCount() ); EXPECT_TRUE(!tree1.empty()); openvdb::tools::pruneInactive(tree1); EXPECT_TRUE(!tree1.empty()); - EXPECT_EQ( openvdb::Index32(2), tree1.leafCount() ); + EXPECT_EQ( openvdb::Index64(2), tree1.leafCount() ); EXPECT_EQ( openvdb::Index64(2), tree1.activeVoxelCount() ); } {//passive tile @@ -1453,17 +1453,17 @@ TEST_F(TestTree, testTopologyIntersection) openvdb::FloatTree tree0(background), tree1(background); tree0.fill(openvdb::CoordBBox(openvdb::Coord(0),openvdb::Coord(dim-1)),2.0f, false); EXPECT_EQ(openvdb::Index64(0), tree0.activeVoxelCount()); - EXPECT_EQ(openvdb::Index32(0), tree0.leafCount() ); + EXPECT_EQ(openvdb::Index64(0), tree0.leafCount() ); tree1.setValue(openvdb::Coord( 500, 301, 200), 4.0f); tree1.setValue(openvdb::Coord( 400, 30, 20), 5.0f); tree1.setValue(openvdb::Coord( dim, 11, 11), 6.0f); - EXPECT_EQ(openvdb::Index32(3), tree1.leafCount() ); + EXPECT_EQ(openvdb::Index64(3), tree1.leafCount() ); EXPECT_EQ(openvdb::Index64(3), tree1.activeVoxelCount()); tree1.topologyIntersection(tree0); - EXPECT_EQ( openvdb::Index32(0), tree1.leafCount() ); + EXPECT_EQ( openvdb::Index64(0), tree1.leafCount() ); EXPECT_EQ( openvdb::Index64(0), tree1.activeVoxelCount() ); EXPECT_TRUE(tree1.empty()); } @@ -1473,17 +1473,17 @@ TEST_F(TestTree, testTopologyIntersection) openvdb::FloatTree tree0(background), tree1(background); tree1.fill(openvdb::CoordBBox(openvdb::Coord(0),openvdb::Coord(dim-1)),2.0f, true); EXPECT_EQ(dim*dim*dim, tree1.activeVoxelCount()); - EXPECT_EQ(openvdb::Index32(0), tree1.leafCount() ); + EXPECT_EQ(openvdb::Index64(0), tree1.leafCount() ); tree0.setValue(openvdb::Coord( 500, 301, 200), 4.0f); tree0.setValue(openvdb::Coord( 400, 30, 20), 5.0f); tree0.setValue(openvdb::Coord( dim, 11, 11), 6.0f); EXPECT_EQ(openvdb::Index64(3), tree0.activeVoxelCount()); - EXPECT_EQ(openvdb::Index32(3), tree0.leafCount() ); + EXPECT_EQ(openvdb::Index64(3), tree0.leafCount() ); tree1.topologyIntersection(tree0); - EXPECT_EQ( openvdb::Index32(2), tree1.leafCount() ); + EXPECT_EQ( openvdb::Index64(2), tree1.leafCount() ); EXPECT_EQ( openvdb::Index64(2), tree1.activeVoxelCount() ); EXPECT_TRUE(!tree1.empty()); openvdb::tools::pruneInactive(tree1); @@ -1522,10 +1522,10 @@ TEST_F(TestTree, testTopologyIntersection) tree3.setValue(iter.getCoord(), vec_val); } - EXPECT_EQ(openvdb::Index32(4), tree0.leafCount()); - EXPECT_EQ(openvdb::Index32(4), tree1.leafCount()); - EXPECT_EQ(openvdb::Index32(7), tree2.leafCount()); - EXPECT_EQ(openvdb::Index32(7), tree3.leafCount()); + EXPECT_EQ(openvdb::Index64(4), tree0.leafCount()); + EXPECT_EQ(openvdb::Index64(4), tree1.leafCount()); + EXPECT_EQ(openvdb::Index64(7), tree2.leafCount()); + EXPECT_EQ(openvdb::Index64(7), tree3.leafCount()); //tree1.topologyInterection(tree2);//should make tree1 = tree0 @@ -1673,22 +1673,22 @@ TEST_F(TestTree, testTopologyDifference) tree0.setValue(openvdb::Coord( 400, 30, 20), 2.0f); tree0.setValue(openvdb::Coord( 8, 11, 11), 3.0f); EXPECT_EQ(openvdb::Index64(3), tree0.activeVoxelCount()); - EXPECT_EQ(openvdb::Index32(3), tree0.leafCount() ); + EXPECT_EQ(openvdb::Index64(3), tree0.leafCount() ); tree1.setValue(openvdb::Coord( 500, 301, 200), 4.0f); tree1.setValue(openvdb::Coord( 400, 30, 20), 5.0f); tree1.setValue(openvdb::Coord( 8, 11, 11), 6.0f); EXPECT_EQ(openvdb::Index64(3), tree1.activeVoxelCount()); - EXPECT_EQ(openvdb::Index32(3), tree1.leafCount() ); + EXPECT_EQ(openvdb::Index64(3), tree1.leafCount() ); tree1.topologyDifference(tree0); - EXPECT_EQ( openvdb::Index32(3), tree1.leafCount() ); + EXPECT_EQ( openvdb::Index64(3), tree1.leafCount() ); EXPECT_EQ( openvdb::Index64(1), tree1.activeVoxelCount() ); EXPECT_TRUE(!tree1.empty()); openvdb::tools::pruneInactive(tree1); EXPECT_TRUE(!tree1.empty()); - EXPECT_EQ( openvdb::Index32(1), tree1.leafCount() ); + EXPECT_EQ( openvdb::Index64(1), tree1.leafCount() ); EXPECT_EQ( openvdb::Index64(1), tree1.activeVoxelCount() ); } {//passive tile @@ -1699,22 +1699,22 @@ TEST_F(TestTree, testTopologyDifference) EXPECT_EQ(openvdb::Index64(0), tree0.activeVoxelCount()); EXPECT_TRUE(!tree0.hasActiveTiles()); EXPECT_EQ(openvdb::Index64(0), tree0.root().onTileCount()); - EXPECT_EQ(openvdb::Index32(0), tree0.leafCount() ); + EXPECT_EQ(openvdb::Index64(0), tree0.leafCount() ); tree1.setValue(openvdb::Coord( 500, 301, 200), 4.0f); tree1.setValue(openvdb::Coord( 400, 30, 20), 5.0f); tree1.setValue(openvdb::Coord( dim, 11, 11), 6.0f); EXPECT_EQ(openvdb::Index64(3), tree1.activeVoxelCount()); EXPECT_TRUE(!tree1.hasActiveTiles()); - EXPECT_EQ(openvdb::Index32(3), tree1.leafCount() ); + EXPECT_EQ(openvdb::Index64(3), tree1.leafCount() ); tree1.topologyDifference(tree0); - EXPECT_EQ( openvdb::Index32(3), tree1.leafCount() ); + EXPECT_EQ( openvdb::Index64(3), tree1.leafCount() ); EXPECT_EQ( openvdb::Index64(3), tree1.activeVoxelCount() ); EXPECT_TRUE(!tree1.empty()); openvdb::tools::pruneInactive(tree1); - EXPECT_EQ( openvdb::Index32(3), tree1.leafCount() ); + EXPECT_EQ( openvdb::Index64(3), tree1.leafCount() ); EXPECT_EQ( openvdb::Index64(3), tree1.activeVoxelCount() ); EXPECT_TRUE(!tree1.empty()); } @@ -1726,14 +1726,14 @@ TEST_F(TestTree, testTopologyDifference) EXPECT_EQ(dim*dim*dim, tree1.activeVoxelCount()); EXPECT_TRUE(tree1.hasActiveTiles()); EXPECT_EQ(openvdb::Index64(1), tree1.root().onTileCount()); - EXPECT_EQ(openvdb::Index32(0), tree0.leafCount() ); + EXPECT_EQ(openvdb::Index64(0), tree0.leafCount() ); tree0.setValue(openvdb::Coord( 500, 301, 200), 4.0f); tree0.setValue(openvdb::Coord( 400, 30, 20), 5.0f); tree0.setValue(openvdb::Coord( int(dim), 11, 11), 6.0f); EXPECT_TRUE(!tree0.hasActiveTiles()); EXPECT_EQ(openvdb::Index64(3), tree0.activeVoxelCount()); - EXPECT_EQ(openvdb::Index32(3), tree0.leafCount() ); + EXPECT_EQ(openvdb::Index64(3), tree0.leafCount() ); EXPECT_TRUE( tree0.isValueOn(openvdb::Coord( int(dim), 11, 11))); EXPECT_TRUE(!tree1.isValueOn(openvdb::Coord( int(dim), 11, 11))); @@ -1754,22 +1754,22 @@ TEST_F(TestTree, testTopologyDifference) EXPECT_EQ(dim*dim*dim, tree1.activeVoxelCount()); EXPECT_TRUE(tree1.hasActiveTiles()); EXPECT_EQ(openvdb::Index64(1), tree1.root().onTileCount()); - EXPECT_EQ(openvdb::Index32(0), tree0.leafCount() ); + EXPECT_EQ(openvdb::Index64(0), tree0.leafCount() ); tree0.setValue(openvdb::Coord( 500, 301, 200), 4.0f); tree0.setValue(openvdb::Coord( 400, 30, 20), 5.0f); tree0.setValue(openvdb::Coord( dim, 11, 11), 6.0f); EXPECT_TRUE(!tree0.hasActiveTiles()); EXPECT_EQ(openvdb::Index64(3), tree0.activeVoxelCount()); - EXPECT_EQ(openvdb::Index32(3), tree0.leafCount() ); + EXPECT_EQ(openvdb::Index64(3), tree0.leafCount() ); tree0.topologyDifference(tree1); - EXPECT_EQ( openvdb::Index32(1), tree0.leafCount() ); + EXPECT_EQ( openvdb::Index64(1), tree0.leafCount() ); EXPECT_EQ( openvdb::Index64(1), tree0.activeVoxelCount() ); EXPECT_TRUE(!tree0.empty()); openvdb::tools::pruneInactive(tree0); - EXPECT_EQ( openvdb::Index32(1), tree0.leafCount() ); + EXPECT_EQ( openvdb::Index64(1), tree0.leafCount() ); EXPECT_EQ( openvdb::Index64(1), tree0.activeVoxelCount() ); EXPECT_TRUE(!tree1.empty()); } @@ -1806,10 +1806,10 @@ TEST_F(TestTree, testTopologyDifference) tree3.setValue(iter.getCoord(), vec_val); } - EXPECT_EQ(openvdb::Index32(4), tree0.leafCount()); - EXPECT_EQ(openvdb::Index32(4), tree1.leafCount()); - EXPECT_EQ(openvdb::Index32(7), tree2.leafCount()); - EXPECT_EQ(openvdb::Index32(7), tree3.leafCount()); + EXPECT_EQ(openvdb::Index64(4), tree0.leafCount()); + EXPECT_EQ(openvdb::Index64(4), tree1.leafCount()); + EXPECT_EQ(openvdb::Index64(7), tree2.leafCount()); + EXPECT_EQ(openvdb::Index64(7), tree3.leafCount()); //tree1.topologyInterection(tree2);//should make tree1 = tree0 @@ -2094,16 +2094,16 @@ TEST_F(TestTree, testPruneInactive) tree.setValue(Coord( 5, 10,-20), 0.3f); // Verify that the tree has the expected numbers of active voxels and leaf nodes. EXPECT_EQ(Index64(8), tree.activeVoxelCount()); - EXPECT_EQ(Index32(8), tree.leafCount()); + EXPECT_EQ(Index64(8), tree.leafCount()); // Verify that prune() has no effect, since the values are all different. openvdb::tools::prune(tree); EXPECT_EQ(Index64(8), tree.activeVoxelCount()); - EXPECT_EQ(Index32(8), tree.leafCount()); + EXPECT_EQ(Index64(8), tree.leafCount()); // Verify that pruneInactive() has no effect, since the values are active. openvdb::tools::pruneInactive(tree); EXPECT_EQ(Index64(8), tree.activeVoxelCount()); - EXPECT_EQ(Index32(8), tree.leafCount()); + EXPECT_EQ(Index64(8), tree.leafCount()); // Make some of the active values inactive, without changing their values. tree.setValueOff(Coord(-5, 10, 20)); @@ -2111,15 +2111,15 @@ TEST_F(TestTree, testPruneInactive) tree.setValueOff(Coord(-5, 10,-20)); tree.setValueOff(Coord(-5,-10,-20)); EXPECT_EQ(Index64(4), tree.activeVoxelCount()); - EXPECT_EQ(Index32(8), tree.leafCount()); + EXPECT_EQ(Index64(8), tree.leafCount()); // Verify that prune() has no effect, since the values are still different. openvdb::tools::prune(tree); EXPECT_EQ(Index64(4), tree.activeVoxelCount()); - EXPECT_EQ(Index32(8), tree.leafCount()); + EXPECT_EQ(Index64(8), tree.leafCount()); // Verify that pruneInactive() prunes the nodes containing only inactive voxels. openvdb::tools::pruneInactive(tree); EXPECT_EQ(Index64(4), tree.activeVoxelCount()); - EXPECT_EQ(Index32(4), tree.leafCount()); + EXPECT_EQ(Index64(4), tree.leafCount()); // Make all of the active values inactive, without changing their values. tree.setValueOff(Coord( 5, 10, 20)); @@ -2127,11 +2127,11 @@ TEST_F(TestTree, testPruneInactive) tree.setValueOff(Coord( 5,-10,-20)); tree.setValueOff(Coord( 5, 10,-20)); EXPECT_EQ(Index64(0), tree.activeVoxelCount()); - EXPECT_EQ(Index32(4), tree.leafCount()); + EXPECT_EQ(Index64(4), tree.leafCount()); // Verify that prune() has no effect, since the values are still different. openvdb::tools::prune(tree); EXPECT_EQ(Index64(0), tree.activeVoxelCount()); - EXPECT_EQ(Index32(4), tree.leafCount()); + EXPECT_EQ(Index64(4), tree.leafCount()); // Verify that pruneInactive() prunes all of the remaining leaf nodes. openvdb::tools::pruneInactive(tree); EXPECT_TRUE(tree.empty()); @@ -2158,7 +2158,7 @@ TEST_F(TestTree, testPruneLevelSet) } } - const openvdb::Index32 leafCount = tree.leafCount(); + const openvdb::Index64 leafCount = tree.leafCount(); EXPECT_EQ(tree.activeVoxelCount(), count); EXPECT_EQ(tree.activeLeafVoxelCount(), count); @@ -2646,6 +2646,7 @@ TEST_F(TestTree, testStealNodes) TEST_F(TestTree, testStealNode) { using openvdb::Index; + using openvdb::Index64; using openvdb::FloatTree; const float background=0.0f, value = 5.6f, epsilon=0.000001f; @@ -2656,19 +2657,19 @@ TEST_F(TestTree, testStealNode) EXPECT_EQ(Index(0), NodeT::getLevel()); FloatTree tree(background); - EXPECT_EQ(Index(0), tree.leafCount()); + EXPECT_EQ(Index64(0), tree.leafCount()); EXPECT_TRUE(!tree.isValueOn(xyz)); EXPECT_NEAR(background, tree.getValue(xyz), epsilon); EXPECT_TRUE(tree.root().stealNode(xyz, value, false) == nullptr); tree.setValue(xyz, value); - EXPECT_EQ(Index(1), tree.leafCount()); + EXPECT_EQ(Index64(1), tree.leafCount()); EXPECT_TRUE(tree.isValueOn(xyz)); EXPECT_NEAR(value, tree.getValue(xyz), epsilon); NodeT* node = tree.root().stealNode(xyz, background, false); EXPECT_TRUE(node != nullptr); - EXPECT_EQ(Index(0), tree.leafCount()); + EXPECT_EQ(Index64(0), tree.leafCount()); EXPECT_TRUE(!tree.isValueOn(xyz)); EXPECT_NEAR(background, tree.getValue(xyz), epsilon); EXPECT_TRUE(tree.root().stealNode(xyz, value, false) == nullptr); @@ -2681,19 +2682,19 @@ TEST_F(TestTree, testStealNode) EXPECT_EQ(Index(1), NodeT::getLevel()); FloatTree tree(background); - EXPECT_EQ(Index(0), tree.leafCount()); + EXPECT_EQ(Index64(0), tree.leafCount()); EXPECT_TRUE(!tree.isValueOn(xyz)); EXPECT_NEAR(background, tree.getValue(xyz), epsilon); EXPECT_TRUE(tree.root().stealNode(xyz, value, false) == nullptr); tree.setValue(xyz, value); - EXPECT_EQ(Index(1), tree.leafCount()); + EXPECT_EQ(Index64(1), tree.leafCount()); EXPECT_TRUE(tree.isValueOn(xyz)); EXPECT_NEAR(value, tree.getValue(xyz), epsilon); NodeT* node = tree.root().stealNode(xyz, background, false); EXPECT_TRUE(node != nullptr); - EXPECT_EQ(Index(0), tree.leafCount()); + EXPECT_EQ(Index64(0), tree.leafCount()); EXPECT_TRUE(!tree.isValueOn(xyz)); EXPECT_NEAR(background, tree.getValue(xyz), epsilon); EXPECT_TRUE(tree.root().stealNode(xyz, value, false) == nullptr); @@ -2706,19 +2707,19 @@ TEST_F(TestTree, testStealNode) EXPECT_EQ(Index(2), NodeT::getLevel()); FloatTree tree(background); - EXPECT_EQ(Index(0), tree.leafCount()); + EXPECT_EQ(Index64(0), tree.leafCount()); EXPECT_TRUE(!tree.isValueOn(xyz)); EXPECT_NEAR(background, tree.getValue(xyz), epsilon); EXPECT_TRUE(tree.root().stealNode(xyz, value, false) == nullptr); tree.setValue(xyz, value); - EXPECT_EQ(Index(1), tree.leafCount()); + EXPECT_EQ(Index64(1), tree.leafCount()); EXPECT_TRUE(tree.isValueOn(xyz)); EXPECT_NEAR(value, tree.getValue(xyz), epsilon); NodeT* node = tree.root().stealNode(xyz, background, false); EXPECT_TRUE(node != nullptr); - EXPECT_EQ(Index(0), tree.leafCount()); + EXPECT_EQ(Index64(0), tree.leafCount()); EXPECT_TRUE(!tree.isValueOn(xyz)); EXPECT_NEAR(background, tree.getValue(xyz), epsilon); EXPECT_TRUE(tree.root().stealNode(xyz, value, false) == nullptr); @@ -2743,7 +2744,7 @@ TEST_F(TestTree, testNodeCount) std::vector dims; tree.getNodeLog2Dims(dims); - std::vector nodeCount1(dims.size()); + std::vector nodeCount1(dims.size()); //timer.start("Old technique");// use for benchmark test for (auto it = tree.cbeginNode(); it; ++it) ++(nodeCount1[dims.size()-1-it.getDepth()]); //timer.restart("New technique");// use for benchmark test @@ -2755,204 +2756,3 @@ TEST_F(TestTree, testNodeCount) EXPECT_EQ(tree.leafCount(), nodeCount2.front());// leaf nodes for (size_t i=0; i children; - root.stealNodes(children); - EXPECT_TRUE(root.empty()); - - // insert the root node children directly - for (ChildType* child : children) { - root.addChild(child); - } - EXPECT_EQ(openvdb::Index(2), root.getTableSize()); - EXPECT_EQ(openvdb::Index32(2), root.childCount()); - - { // verify the coordinates of the root node children - auto rootIter = root.cbeginChildOn(); - EXPECT_EQ(c0, rootIter.getCoord()); - ++rootIter; - EXPECT_EQ(c1, rootIter.getCoord()); - } - } - - { // test inserting tiles and replacing them with child nodes - RootNodeType root(0.0f); - EXPECT_TRUE(root.empty()); - - // no-op - root.addChild(nullptr); - - // populate the root node by inserting tiles - root.addTile(c0, /*value=*/1.0f, /*state=*/true); - root.addTile(c1, /*value=*/2.0f, /*state=*/true); - EXPECT_EQ(openvdb::Index(2), root.getTableSize()); - EXPECT_EQ(openvdb::Index32(0), root.childCount()); - EXPECT_TRUE(root.hasActiveTiles()); - ASSERT_DOUBLES_EXACTLY_EQUAL(1.0f, root.getValue(c0)); - ASSERT_DOUBLES_EXACTLY_EQUAL(2.0f, root.getValue(c1)); - - // insert child nodes with the same coordinates - root.addChild(new ChildType(c0, 3.0f)); - root.addChild(new ChildType(c1, 4.0f)); - - // insert a new child at c0 - root.addChild(new ChildType(c0, 5.0f)); - - // verify active tiles have been replaced by child nodes - EXPECT_EQ(openvdb::Index(2), root.getTableSize()); - EXPECT_EQ(openvdb::Index32(2), root.childCount()); - EXPECT_TRUE(!root.hasActiveTiles()); - - { // verify the coordinates of the root node children - auto rootIter = root.cbeginChildOn(); - EXPECT_EQ(c0, rootIter.getCoord()); - ASSERT_DOUBLES_EXACTLY_EQUAL(5.0f, root.getValue(c0)); - ++rootIter; - EXPECT_EQ(c1, rootIter.getCoord()); - } - } - - { // test transient data - RootNodeType rootNode(0.0f); - EXPECT_EQ(openvdb::Index32(0), rootNode.transientData()); - rootNode.setTransientData(openvdb::Index32(5)); - EXPECT_EQ(openvdb::Index32(5), rootNode.transientData()); - RootNodeType rootNode2(rootNode); - EXPECT_EQ(openvdb::Index32(5), rootNode2.transientData()); - RootNodeType rootNode3 = rootNode; - EXPECT_EQ(openvdb::Index32(5), rootNode3.transientData()); - } -} - -TEST_F(TestTree, testInternalNode) -{ - const openvdb::Coord c0(1000, 1000, 1000); - const openvdb::Coord c1(896, 896, 896); - - using InternalNodeType = InternalNodeType1; - using ChildType = LeafNodeType; - - { // test inserting child nodes directly and indirectly - openvdb::Coord c2 = c1.offsetBy(8,0,0); - openvdb::Coord c3 = c1.offsetBy(16,16,16); - - InternalNodeType internalNode(c1, 0.0f); - internalNode.touchLeaf(c2); - internalNode.touchLeaf(c3); - - EXPECT_EQ(openvdb::Index(2), internalNode.leafCount()); - EXPECT_EQ(openvdb::Index32(2), internalNode.childCount()); - EXPECT_TRUE(!internalNode.hasActiveTiles()); - - { // verify c0 and c1 are the root node coordinates - auto childIter = internalNode.cbeginChildOn(); - EXPECT_EQ(c2, childIter.getCoord()); - ++childIter; - EXPECT_EQ(c3, childIter.getCoord()); - } - - // copy the internal node - InternalNodeType internalNodeCopy(internalNode); - - // steal the internal node children leaving it empty again - std::vector children; - internalNode.stealNodes(children, 0.0f, false); - EXPECT_EQ(openvdb::Index(0), internalNode.leafCount()); - EXPECT_EQ(openvdb::Index32(0), internalNode.childCount()); - - // insert the root node children directly - for (ChildType* child : children) { - internalNode.addChild(child); - } - EXPECT_EQ(openvdb::Index(2), internalNode.leafCount()); - EXPECT_EQ(openvdb::Index32(2), internalNode.childCount()); - - { // verify the coordinates of the root node children - auto childIter = internalNode.cbeginChildOn(); - EXPECT_EQ(c2, childIter.getCoord()); - ++childIter; - EXPECT_EQ(c3, childIter.getCoord()); - } - } - - { // test inserting a tile and replacing with a child node - InternalNodeType internalNode(c1, 0.0f); - EXPECT_TRUE(!internalNode.hasActiveTiles()); - EXPECT_EQ(openvdb::Index(0), internalNode.leafCount()); - EXPECT_EQ(openvdb::Index32(0), internalNode.childCount()); - - // add a tile - internalNode.addTile(openvdb::Index(0), /*value=*/1.0f, /*state=*/true); - EXPECT_TRUE(internalNode.hasActiveTiles()); - EXPECT_EQ(openvdb::Index(0), internalNode.leafCount()); - EXPECT_EQ(openvdb::Index32(0), internalNode.childCount()); - - // replace the tile with a child node - EXPECT_TRUE(internalNode.addChild(new ChildType(c1, 2.0f))); - EXPECT_TRUE(!internalNode.hasActiveTiles()); - EXPECT_EQ(openvdb::Index(1), internalNode.leafCount()); - EXPECT_EQ(openvdb::Index32(1), internalNode.childCount()); - EXPECT_EQ(c1, internalNode.cbeginChildOn().getCoord()); - ASSERT_DOUBLES_EXACTLY_EQUAL(2.0f, internalNode.cbeginChildOn()->getValue(0)); - - // replace the child node with another child node - EXPECT_TRUE(internalNode.addChild(new ChildType(c1, 3.0f))); - ASSERT_DOUBLES_EXACTLY_EQUAL(3.0f, internalNode.cbeginChildOn()->getValue(0)); - } - - { // test inserting child nodes that do and do not belong to the internal node - InternalNodeType internalNode(c1, 0.0f); - - // succeed if child belongs to this internal node - EXPECT_TRUE(internalNode.addChild(new ChildType(c0.offsetBy(8,0,0)))); - EXPECT_TRUE(internalNode.probeLeaf(c0.offsetBy(8,0,0))); - openvdb::Index index1 = internalNode.coordToOffset(c0); - openvdb::Index index2 = internalNode.coordToOffset(c0.offsetBy(8,0,0)); - EXPECT_TRUE(!internalNode.isChildMaskOn(index1)); - EXPECT_TRUE(internalNode.isChildMaskOn(index2)); - - // fail otherwise - auto* child = new ChildType(c0.offsetBy(8000,0,0)); - EXPECT_TRUE(!internalNode.addChild(child)); - delete child; - } - - { // test transient data - InternalNodeType internalNode(c1, 0.0f); - EXPECT_EQ(openvdb::Index32(0), internalNode.transientData()); - internalNode.setTransientData(openvdb::Index32(5)); - EXPECT_EQ(openvdb::Index32(5), internalNode.transientData()); - InternalNodeType internalNode2(internalNode); - EXPECT_EQ(openvdb::Index32(5), internalNode2.transientData()); - InternalNodeType internalNode3 = internalNode; - EXPECT_EQ(openvdb::Index32(5), internalNode3.transientData()); - } -} diff --git a/openvdb/openvdb/unittest/TestTreeGetSetValues.cc b/openvdb/openvdb/unittest/TestTreeGetSetValues.cc index 27dcf9e89d..ea5ce6e9f8 100644 --- a/openvdb/openvdb/unittest/TestTreeGetSetValues.cc +++ b/openvdb/openvdb/unittest/TestTreeGetSetValues.cc @@ -224,108 +224,108 @@ TEST_F(TestTreeGetSetValues, testFill) // The following tests assume a [3,2,3] tree configuration. tree.clear(); - EXPECT_EQ(openvdb::Index32(0), tree.leafCount()); - EXPECT_EQ(openvdb::Index32(1), tree.nonLeafCount()); // root node + EXPECT_EQ(openvdb::Index64(0), tree.leafCount()); + EXPECT_EQ(openvdb::Index64(1), tree.nonLeafCount()); // root node // Partially fill a single leaf node. tree.fill(CoordBBox(Coord(8), Coord(14)), 0.0); - EXPECT_EQ(openvdb::Index32(1), tree.leafCount()); - EXPECT_EQ(openvdb::Index32(3), tree.nonLeafCount()); + EXPECT_EQ(openvdb::Index64(1), tree.leafCount()); + EXPECT_EQ(openvdb::Index64(3), tree.nonLeafCount()); // Completely fill the leaf node, replacing it with a tile. tree.fill(CoordBBox(Coord(8), Coord(15)), 0.0); - EXPECT_EQ(openvdb::Index32(0), tree.leafCount()); - EXPECT_EQ(openvdb::Index32(3), tree.nonLeafCount()); + EXPECT_EQ(openvdb::Index64(0), tree.leafCount()); + EXPECT_EQ(openvdb::Index64(3), tree.nonLeafCount()); { const int activeVoxelCount = int(tree.activeVoxelCount()); // Fill a single voxel of the tile with a different (active) value. tree.fill(CoordBBox(Coord(10), Coord(10)), 1.0); - EXPECT_EQ(openvdb::Index32(1), tree.leafCount()); - EXPECT_EQ(openvdb::Index32(3), tree.nonLeafCount()); + EXPECT_EQ(openvdb::Index64(1), tree.leafCount()); + EXPECT_EQ(openvdb::Index64(3), tree.nonLeafCount()); EXPECT_EQ(activeVoxelCount, int(tree.activeVoxelCount())); // Fill the voxel with an inactive value. tree.fill(CoordBBox(Coord(10), Coord(10)), 1.0, /*active=*/false); - EXPECT_EQ(openvdb::Index32(1), tree.leafCount()); - EXPECT_EQ(openvdb::Index32(3), tree.nonLeafCount()); + EXPECT_EQ(openvdb::Index64(1), tree.leafCount()); + EXPECT_EQ(openvdb::Index64(3), tree.nonLeafCount()); EXPECT_EQ(activeVoxelCount - 1, int(tree.activeVoxelCount())); // Completely fill the leaf node, replacing it with a tile again. tree.fill(CoordBBox(Coord(8), Coord(15)), 0.0); - EXPECT_EQ(openvdb::Index32(0), tree.leafCount()); - EXPECT_EQ(openvdb::Index32(3), tree.nonLeafCount()); + EXPECT_EQ(openvdb::Index64(0), tree.leafCount()); + EXPECT_EQ(openvdb::Index64(3), tree.nonLeafCount()); } // Expand by one voxel, creating seven neighboring leaf nodes. tree.fill(CoordBBox(Coord(8), Coord(16)), 0.0); - EXPECT_EQ(openvdb::Index32(7), tree.leafCount()); - EXPECT_EQ(openvdb::Index32(3), tree.nonLeafCount()); + EXPECT_EQ(openvdb::Index64(7), tree.leafCount()); + EXPECT_EQ(openvdb::Index64(3), tree.nonLeafCount()); // Completely fill the internal node containing the tile, replacing it with // a tile at the next level of the tree. tree.fill(CoordBBox(Coord(0), Coord(31)), 0.0); - EXPECT_EQ(openvdb::Index32(0), tree.leafCount()); - EXPECT_EQ(openvdb::Index32(2), tree.nonLeafCount()); + EXPECT_EQ(openvdb::Index64(0), tree.leafCount()); + EXPECT_EQ(openvdb::Index64(2), tree.nonLeafCount()); // Expand by one voxel, creating a layer of leaf nodes on three faces. tree.fill(CoordBBox(Coord(0), Coord(32)), 0.0); - EXPECT_EQ(openvdb::Index32(5*5 + 4*5 + 4*4), tree.leafCount()); - EXPECT_EQ(openvdb::Index32(2 + 7), tree.nonLeafCount()); // +7 internal nodes + EXPECT_EQ(openvdb::Index64(5*5 + 4*5 + 4*4), tree.leafCount()); + EXPECT_EQ(openvdb::Index64(2 + 7), tree.nonLeafCount()); // +7 internal nodes // Completely fill the second-level internal node, replacing it with a root-level tile. tree.fill(CoordBBox(Coord(0), Coord(255)), 0.0); - EXPECT_EQ(openvdb::Index32(0), tree.leafCount()); - EXPECT_EQ(openvdb::Index32(1), tree.nonLeafCount()); + EXPECT_EQ(openvdb::Index64(0), tree.leafCount()); + EXPECT_EQ(openvdb::Index64(1), tree.nonLeafCount()); // Repeat, filling with an inactive value. tree.clear(); - EXPECT_EQ(openvdb::Index32(0), tree.leafCount()); - EXPECT_EQ(openvdb::Index32(1), tree.nonLeafCount()); // root node + EXPECT_EQ(openvdb::Index64(0), tree.leafCount()); + EXPECT_EQ(openvdb::Index64(1), tree.nonLeafCount()); // root node // Partially fill a single leaf node. tree.fill(CoordBBox(Coord(8), Coord(14)), 0.0, /*active=*/false); - EXPECT_EQ(openvdb::Index32(1), tree.leafCount()); - EXPECT_EQ(openvdb::Index32(3), tree.nonLeafCount()); + EXPECT_EQ(openvdb::Index64(1), tree.leafCount()); + EXPECT_EQ(openvdb::Index64(3), tree.nonLeafCount()); // Completely fill the leaf node, replacing it with a tile. tree.fill(CoordBBox(Coord(8), Coord(15)), 0.0, /*active=*/false); - EXPECT_EQ(openvdb::Index32(0), tree.leafCount()); - EXPECT_EQ(openvdb::Index32(3), tree.nonLeafCount()); + EXPECT_EQ(openvdb::Index64(0), tree.leafCount()); + EXPECT_EQ(openvdb::Index64(3), tree.nonLeafCount()); // Expand by one voxel, creating seven neighboring leaf nodes. tree.fill(CoordBBox(Coord(8), Coord(16)), 0.0, /*active=*/false); - EXPECT_EQ(openvdb::Index32(7), tree.leafCount()); - EXPECT_EQ(openvdb::Index32(3), tree.nonLeafCount()); + EXPECT_EQ(openvdb::Index64(7), tree.leafCount()); + EXPECT_EQ(openvdb::Index64(3), tree.nonLeafCount()); // Completely fill the internal node containing the tile, replacing it with // a tile at the next level of the tree. tree.fill(CoordBBox(Coord(0), Coord(31)), 0.0, /*active=*/false); - EXPECT_EQ(openvdb::Index32(0), tree.leafCount()); - EXPECT_EQ(openvdb::Index32(2), tree.nonLeafCount()); + EXPECT_EQ(openvdb::Index64(0), tree.leafCount()); + EXPECT_EQ(openvdb::Index64(2), tree.nonLeafCount()); // Expand by one voxel, creating a layer of leaf nodes on three faces. tree.fill(CoordBBox(Coord(0), Coord(32)), 0.0, /*active=*/false); - EXPECT_EQ(openvdb::Index32(5*5 + 4*5 + 4*4), tree.leafCount()); - EXPECT_EQ(openvdb::Index32(2 + 7), tree.nonLeafCount()); // +7 internal nodes + EXPECT_EQ(openvdb::Index64(5*5 + 4*5 + 4*4), tree.leafCount()); + EXPECT_EQ(openvdb::Index64(2 + 7), tree.nonLeafCount()); // +7 internal nodes // Completely fill the second-level internal node, replacing it with a root-level tile. tree.fill(CoordBBox(Coord(0), Coord(255)), 0.0, /*active=*/false); - EXPECT_EQ(openvdb::Index32(0), tree.leafCount()); - EXPECT_EQ(openvdb::Index32(1), tree.nonLeafCount()); + EXPECT_EQ(openvdb::Index64(0), tree.leafCount()); + EXPECT_EQ(openvdb::Index64(1), tree.nonLeafCount()); tree.clear(); - EXPECT_EQ(openvdb::Index32(0), tree.leafCount()); - EXPECT_EQ(openvdb::Index32(1), tree.nonLeafCount()); // root node + EXPECT_EQ(openvdb::Index64(0), tree.leafCount()); + EXPECT_EQ(openvdb::Index64(1), tree.nonLeafCount()); // root node EXPECT_TRUE(tree.empty()); // Partially fill a region with inactive background values. tree.fill(CoordBBox(Coord(27), Coord(254)), background, /*active=*/false); // Confirm that after pruning, the tree is empty. openvdb::tools::prune(tree); - EXPECT_EQ(openvdb::Index32(0), tree.leafCount()); - EXPECT_EQ(openvdb::Index32(1), tree.nonLeafCount()); // root node + EXPECT_EQ(openvdb::Index64(0), tree.leafCount()); + EXPECT_EQ(openvdb::Index64(1), tree.nonLeafCount()); // root node EXPECT_TRUE(tree.empty()); } diff --git a/openvdb/openvdb/unittest/TestValueAccessor.cc b/openvdb/openvdb/unittest/TestValueAccessor.cc index 24d33afda5..947552e37a 100644 --- a/openvdb/openvdb/unittest/TestValueAccessor.cc +++ b/openvdb/openvdb/unittest/TestValueAccessor.cc @@ -587,6 +587,7 @@ TEST_F(TestValueAccessor, testMultiThreadedRWAccessors) TEST_F(TestValueAccessor, testAccessorRegistration) { using openvdb::Index; + using openvdb::Index64; const float background = 5.0f, value = -9.345f; const openvdb::Coord c0(5, 10, 20); @@ -597,29 +598,29 @@ TEST_F(TestValueAccessor, testAccessorRegistration) // Set a single leaf voxel via the accessor and verify that // the cache is populated. acc.setValue(c0, value); - EXPECT_EQ(Index(1), tree->leafCount()); - EXPECT_EQ(tree->root().getLevel(), tree->nonLeafCount()); + EXPECT_EQ(Index64(1), tree->leafCount()); + EXPECT_EQ(Index64(tree->root().getLevel()), tree->nonLeafCount()); EXPECT_TRUE(acc.getNode() != nullptr); // Reset the voxel to the background value and verify that no nodes // have been deleted and that the cache is still populated. tree->setValueOff(c0, background); - EXPECT_EQ(Index(1), tree->leafCount()); - EXPECT_EQ(tree->root().getLevel(), tree->nonLeafCount()); + EXPECT_EQ(Index64(1), tree->leafCount()); + EXPECT_EQ(Index64(tree->root().getLevel()), tree->nonLeafCount()); EXPECT_TRUE(acc.getNode() != nullptr); // Prune the tree and verify that only the root node remains and that // the cache has been cleared. openvdb::tools::prune(*tree); //tree->prune(); - EXPECT_EQ(Index(0), tree->leafCount()); - EXPECT_EQ(Index(1), tree->nonLeafCount()); // root node only + EXPECT_EQ(Index64(0), tree->leafCount()); + EXPECT_EQ(Index64(1), tree->nonLeafCount()); // root node only EXPECT_TRUE(acc.getNode() == nullptr); // Set the leaf voxel again and verify that the cache is repopulated. acc.setValue(c0, value); - EXPECT_EQ(Index(1), tree->leafCount()); - EXPECT_EQ(tree->root().getLevel(), tree->nonLeafCount()); + EXPECT_EQ(Index64(1), tree->leafCount()); + EXPECT_EQ(Index64(tree->root().getLevel()), tree->nonLeafCount()); EXPECT_TRUE(acc.getNode() != nullptr); // Delete the tree and verify that the cache has been cleared. @@ -665,8 +666,6 @@ TEST_F(TestValueAccessor, testGetNode) } } -#if OPENVDB_ABI_VERSION_NUMBER >= 10 - template struct AssertBypass { inline void operator()() { @@ -703,5 +702,3 @@ TEST_F(TestValueAccessor, testBypassLeafAPI) static_assert(!ValueAccessor2::BypassLeafAPI); static_assert(!ValueAccessor3::BypassLeafAPI); } - -#endif diff --git a/openvdb/openvdb/version.h.in b/openvdb/openvdb/version.h.in index d5ef9fb8e6..1591071955 100644 --- a/openvdb/openvdb/version.h.in +++ b/openvdb/openvdb/version.h.in @@ -175,10 +175,10 @@ // This can be suppressed by defining OPENVDB_USE_FUTURE_ABI_=ON. // Note that, whilst the VDB CMake does not allow this option to be hit, // it exists to propagate this message to downstream targets - #if OPENVDB_ABI_VERSION_NUMBER == 12 - #ifndef OPENVDB_USE_FUTURE_ABI_12 - PRAGMA(message("NOTE: ABI = 12 is still in active development and has not been finalized, " - "define OPENVDB_USE_FUTURE_ABI_11 to suppress this message")) + #if OPENVDB_ABI_VERSION_NUMBER == 13 + #ifndef OPENVDB_USE_FUTURE_ABI_13 + PRAGMA(message("NOTE: ABI = 13 is still in active development and has not been finalized, " + "define OPENVDB_USE_FUTURE_ABI_13 to suppress this message")) #endif #else #error expected OPENVDB_ABI_VERSION_NUMBER <= OPENVDB_LIBRARY_MAJOR_VERSION_NUMBER @@ -189,18 +189,18 @@ // directive. This can be suppressed by defining OPENVDB_USE_DEPRECATED_ABI_. // Note that, whilst the VDB CMake does not allow this option to be hit, // it exists to propagate this message to downstream targets -#ifndef OPENVDB_USE_DEPRECATED_ABI_9 - #if OPENVDB_ABI_VERSION_NUMBER == 9 - PRAGMA(message("NOTE: ABI = 9 is deprecated, define OPENVDB_USE_DEPRECATED_ABI_9 " - "to suppress this message")) - #endif -#endif #ifndef OPENVDB_USE_DEPRECATED_ABI_10 #if OPENVDB_ABI_VERSION_NUMBER == 10 PRAGMA(message("NOTE: ABI = 10 is deprecated, define OPENVDB_USE_DEPRECATED_ABI_10 " "to suppress this message")) #endif #endif +#ifndef OPENVDB_USE_DEPRECATED_ABI_11 + #if OPENVDB_ABI_VERSION_NUMBER == 11 + PRAGMA(message("NOTE: ABI = 11 is deprecated, define OPENVDB_USE_DEPRECATED_ABI_11 " + "to suppress this message")) + #endif +#endif /// By default, the @b OPENVDB_REQUIRE_VERSION_NAME macro is undefined, and /// symbols from the version namespace are promoted to the top-level namespace diff --git a/openvdb_ax/openvdb_ax/CMakeLists.txt b/openvdb_ax/openvdb_ax/CMakeLists.txt index 14c0f21ac0..e0d95caeb7 100644 --- a/openvdb_ax/openvdb_ax/CMakeLists.txt +++ b/openvdb_ax/openvdb_ax/CMakeLists.txt @@ -7,7 +7,7 @@ #]=======================================================================] -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.20) # There is a bug introduced with LLVM 14 config files which causes CMake to # complain if the project is not init-ted with LANGUAGES C @@ -354,23 +354,23 @@ if(OPENVDB_AX_SHARED) endif() endif() -install(FILES ax.h Exceptions.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/openvdb_ax/) -install(FILES ${OPENVDB_AX_AST_INCLUDE_FILES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/openvdb_ax/ast) -install(FILES ${OPENVDB_AX_CODEGEN_INCLUDE_FILES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/openvdb_ax/codegen) -install(FILES ${OPENVDB_AX_COMPILER_INCLUDE_FILES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/openvdb_ax/compiler) +install(FILES ax.h Exceptions.h DESTINATION ${OPENVDB_INSTALL_INCLUDEDIR}/openvdb_ax/) +install(FILES ${OPENVDB_AX_AST_INCLUDE_FILES} DESTINATION ${OPENVDB_INSTALL_INCLUDEDIR}/openvdb_ax/ast) +install(FILES ${OPENVDB_AX_CODEGEN_INCLUDE_FILES} DESTINATION ${OPENVDB_INSTALL_INCLUDEDIR}/openvdb_ax/codegen) +install(FILES ${OPENVDB_AX_COMPILER_INCLUDE_FILES} DESTINATION ${OPENVDB_INSTALL_INCLUDEDIR}/openvdb_ax/compiler) if(OPENVDB_AX_STATIC) install(TARGETS openvdb_ax_static - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${OPENVDB_INSTALL_BINDIR} + LIBRARY DESTINATION ${OPENVDB_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${OPENVDB_INSTALL_LIBDIR} ) endif() if(OPENVDB_AX_SHARED) install(TARGETS openvdb_ax_shared - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${OPENVDB_INSTALL_BINDIR} + LIBRARY DESTINATION ${OPENVDB_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${OPENVDB_INSTALL_LIBDIR} ) endif() diff --git a/openvdb_ax/openvdb_ax/compiler/AttributeBindings.h b/openvdb_ax/openvdb_ax/compiler/AttributeBindings.h index ebd7bba23b..dd295b2501 100644 --- a/openvdb_ax/openvdb_ax/compiler/AttributeBindings.h +++ b/openvdb_ax/openvdb_ax/compiler/AttributeBindings.h @@ -1,5 +1,5 @@ // Copyright Contributors to the OpenVDB Project -// SPDX-License-Identifier: Apache-2.0/ +// SPDX-License-Identifier: Apache-2.0 /// @file compiler/AttributeBindings.h /// diff --git a/openvdb_ax/openvdb_ax/compiler/AttributeRegistry.h b/openvdb_ax/openvdb_ax/compiler/AttributeRegistry.h index 6f40790d2d..1f03655f54 100644 --- a/openvdb_ax/openvdb_ax/compiler/AttributeRegistry.h +++ b/openvdb_ax/openvdb_ax/compiler/AttributeRegistry.h @@ -1,5 +1,5 @@ // Copyright Contributors to the OpenVDB Project -// SPDX-License-Identifier: Apache-2.0/ +// SPDX-License-Identifier: Apache-2.0 /// @file compiler/AttributeRegistry.h /// diff --git a/openvdb_ax/openvdb_ax/test/CMakeLists.txt b/openvdb_ax/openvdb_ax/test/CMakeLists.txt index 5dead7b2da..a5997e2099 100644 --- a/openvdb_ax/openvdb_ax/test/CMakeLists.txt +++ b/openvdb_ax/openvdb_ax/test/CMakeLists.txt @@ -7,7 +7,7 @@ #]=======================================================================] -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.20) project(OpenVDBAXUnitTests LANGUAGES CXX) option(OPENVDB_AX_TEST_PROFILE "Switch on profiling for some of the unit tests." OFF) diff --git a/openvdb_ax/openvdb_ax/test/TestAXCmd.cmake b/openvdb_ax/openvdb_ax/test/TestAXCmd.cmake index 7cccd639c4..9f1743877f 100644 --- a/openvdb_ax/openvdb_ax/test/TestAXCmd.cmake +++ b/openvdb_ax/openvdb_ax/test/TestAXCmd.cmake @@ -12,7 +12,7 @@ #]=======================================================================] -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.20) option(UPDATE_BASELINES "Replace the expected outputs whilst running tests" OFF) option(DOWNLOAD_VDBS "Fetch .vdb files required for some tests" OFF) @@ -33,7 +33,7 @@ set(SPHERE_VDB ${CMAKE_BINARY_DIR}/sphere.vdb) set(TORUS_VDB ${CMAKE_BINARY_DIR}/torus.vdb) if(DOWNLOAD_VDBS) - find_package(Python COMPONENTS Interpreter REQUIRED) + find_package(Python ${MINIMUM_PYTHON_VERSION} COMPONENTS Interpreter REQUIRED) if(NOT EXISTS ${SPHERE_VDB} OR NOT EXISTS ${TORUS_VDB} OR NOT EXISTS ${SPHERE_POINTS_VDB}) diff --git a/openvdb_ax/openvdb_ax/test/backend/TestStringIR.cc b/openvdb_ax/openvdb_ax/test/backend/TestStringIR.cc index 1301262521..fe5dde8726 100644 --- a/openvdb_ax/openvdb_ax/test/backend/TestStringIR.cc +++ b/openvdb_ax/openvdb_ax/test/backend/TestStringIR.cc @@ -289,10 +289,8 @@ TestStringIR::testStringStringIR() { static auto setInvalidString = [](String& S) { #if defined(__GNUC__) && !defined(__clang__) -#if OPENVDB_CHECK_GCC(8, 0) _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wclass-memaccess\"") -#endif #endif // zero out the data held by a String object (expected to not hold heap memory). // This is used to test the IR methods work as expected with the allocated, but @@ -300,9 +298,7 @@ TestStringIR::testStringStringIR() OPENVDB_ASSERT(S.isLocal()); std::memset(&S, 0, sizeof(String)); // uninit string, invalid class memory #if defined(__GNUC__) && !defined(__clang__) -#if OPENVDB_CHECK_GCC(8, 0) _Pragma("GCC diagnostic pop") -#endif #endif }; diff --git a/openvdb_ax/openvdb_ax/test/compiler/TestPointExecutable.cc b/openvdb_ax/openvdb_ax/test/compiler/TestPointExecutable.cc index 2e5ad4fdcc..a619856140 100644 --- a/openvdb_ax/openvdb_ax/test/compiler/TestPointExecutable.cc +++ b/openvdb_ax/openvdb_ax/test/compiler/TestPointExecutable.cc @@ -20,6 +20,12 @@ class TestPointExecutable : public CppUnit::TestCase { public: +#if OPENVDB_ABI_VERSION_NUMBER >= 12 + using LeafIndexType = openvdb::Index64; +#else + using LeafIndexType = openvdb::Index32; +#endif + CPPUNIT_TEST_SUITE(TestPointExecutable); CPPUNIT_TEST(testConstructionDestruction); CPPUNIT_TEST(testCreateMissingAttributes); @@ -545,7 +551,7 @@ TestPointExecutable::testAttributeCodecs() points = points::createPointDataGrid (twoPoints, *defaultTransform); - CPPUNIT_ASSERT_EQUAL(points->tree().leafCount(), Index32(1)); + CPPUNIT_ASSERT_EQUAL(points->tree().leafCount(), LeafIndexType(1)); // collapsed uniform 0 attributes points::appendAttribute(points->tree(), "f"); @@ -641,7 +647,7 @@ TestPointExecutable::testAttributeCodecs() points = points::createPointDataGrid (twoPoints, *defaultTransform); - CPPUNIT_ASSERT_EQUAL(points->tree().leafCount(), Index32(1)); + CPPUNIT_ASSERT_EQUAL(points->tree().leafCount(), LeafIndexType(1)); // collapsed uniform 0 attributes points::appendAttribute>(points->tree(), "fpu8"); @@ -728,7 +734,7 @@ TestPointExecutable::testAttributeCodecs() points = points::createPointDataGrid , points::PointDataGrid> (twoPoints, *defaultTransform); - CPPUNIT_ASSERT_EQUAL(points->tree().leafCount(), Index32(1)); + CPPUNIT_ASSERT_EQUAL(points->tree().leafCount(), LeafIndexType(1)); points::appendAttribute(points->tree(), "t"); points::appendAttribute>(points->tree(), "f"); diff --git a/openvdb_ax/openvdb_ax/test/compiler/TestVolumeExecutable.cc b/openvdb_ax/openvdb_ax/test/compiler/TestVolumeExecutable.cc index 2a00ae6703..f0b9eeef8f 100644 --- a/openvdb_ax/openvdb_ax/test/compiler/TestVolumeExecutable.cc +++ b/openvdb_ax/openvdb_ax/test/compiler/TestVolumeExecutable.cc @@ -13,6 +13,12 @@ class TestVolumeExecutable : public CppUnit::TestCase { public: +#if OPENVDB_ABI_VERSION_NUMBER >= 12 + using LeafIndexType = openvdb::Index64; +#else + using LeafIndexType = openvdb::Index32; +#endif + CPPUNIT_TEST_SUITE(TestVolumeExecutable); CPPUNIT_TEST(testConstructionDestruction); CPPUNIT_TEST(testCreateMissingGrids); @@ -155,7 +161,7 @@ TestVolumeExecutable::testTreeExecutionLevel() const openvdb::FloatTree copy = tree; // check config auto CHECK_CONFIG = [&]() { - CPPUNIT_ASSERT_EQUAL(openvdb::Index32(1), tree.leafCount()); + CPPUNIT_ASSERT_EQUAL(LeafIndexType(1), tree.leafCount()); CPPUNIT_ASSERT_EQUAL(openvdb::Index64(3), tree.activeTileCount()); CPPUNIT_ASSERT_EQUAL(int(openvdb::FloatTree::DEPTH-4), tree.getValueDepth(openvdb::Coord(0))); CPPUNIT_ASSERT_EQUAL(int(openvdb::FloatTree::DEPTH-3), tree.getValueDepth(openvdb::Coord(NodeT2::DIM))); @@ -357,7 +363,7 @@ TestVolumeExecutable::testActiveTileStreaming() CPPUNIT_ASSERT_EQUAL(openvdb::Index(openvdb::FloatTree::DEPTH-1), max); executable->execute(grid); - CPPUNIT_ASSERT_EQUAL(openvdb::Index32(1), tree.leafCount()); + CPPUNIT_ASSERT_EQUAL(LeafIndexType(1), tree.leafCount()); CPPUNIT_ASSERT_EQUAL(openvdb::Index64(3), tree.activeTileCount()); CPPUNIT_ASSERT_EQUAL(int(openvdb::FloatTree::DEPTH-4), tree.getValueDepth(openvdb::Coord(0))); CPPUNIT_ASSERT_EQUAL(int(openvdb::FloatTree::DEPTH-3), tree.getValueDepth(openvdb::Coord(NodeT2::DIM))); @@ -404,7 +410,7 @@ TestVolumeExecutable::testActiveTileStreaming() openvdb::Index64(NodeT1::NUM_VOXELS) + openvdb::Index64(NodeT0::NUM_VOXELS); - CPPUNIT_ASSERT_EQUAL(openvdb::Index32(voxels / openvdb::FloatTree::LeafNodeType::NUM_VOXELS), tree.leafCount()); + CPPUNIT_ASSERT_EQUAL(LeafIndexType(voxels / openvdb::FloatTree::LeafNodeType::NUM_VOXELS), tree.leafCount()); CPPUNIT_ASSERT_EQUAL(openvdb::Index64(0), tree.activeTileCount()); CPPUNIT_ASSERT_EQUAL(int(openvdb::FloatTree::DEPTH-1), tree.getValueDepth(openvdb::Coord(0))); CPPUNIT_ASSERT_EQUAL(int(openvdb::FloatTree::DEPTH-1), tree.getValueDepth(openvdb::Coord(NodeT1::DIM))); @@ -463,7 +469,7 @@ TestVolumeExecutable::testActiveTileStreaming() ((n1ChildCount * (n2ChildAxisCount * n2ChildAxisCount)) - leafs) // NodeT1 face tiles (NodeT0) - leafs + 1 /*NodeT1*/ + 1 /*NodeT0*/; - CPPUNIT_ASSERT_EQUAL(openvdb::Index32(leafs), tree.leafCount()); + CPPUNIT_ASSERT_EQUAL(LeafIndexType(leafs), tree.leafCount()); CPPUNIT_ASSERT_EQUAL(openvdb::Index64(tiles), tree.activeTileCount()); CPPUNIT_ASSERT_EQUAL(int(openvdb::FloatTree::DEPTH-3), tree.getValueDepth(openvdb::Coord(NodeT2::DIM))); CPPUNIT_ASSERT_EQUAL(int(openvdb::FloatTree::DEPTH-2), tree.getValueDepth(openvdb::Coord(NodeT2::DIM+NodeT1::DIM))); @@ -512,7 +518,7 @@ TestVolumeExecutable::testActiveTileStreaming() executable->execute(grid); - CPPUNIT_ASSERT_EQUAL(openvdb::Index32(0), tree.leafCount()); + CPPUNIT_ASSERT_EQUAL(LeafIndexType(0), tree.leafCount()); CPPUNIT_ASSERT_EQUAL(openvdb::Index64(5), tree.activeTileCount()); CPPUNIT_ASSERT_EQUAL(int(openvdb::FloatTree::DEPTH-3), tree.getValueDepth(openvdb::Coord(NodeT1::DIM*0, 0, 0))); CPPUNIT_ASSERT_EQUAL(int(openvdb::FloatTree::DEPTH-3), tree.getValueDepth(openvdb::Coord(NodeT1::DIM*1, 0, 0))); @@ -567,7 +573,7 @@ TestVolumeExecutable::testActiveTileStreaming() (n1ChildCount - leafs) // NodeT1 face tiles (NodeT0) - leafs + 3 /*NodeT1*/ + 1 /*NodeT0*/; - CPPUNIT_ASSERT_EQUAL(openvdb::Index32(leafs), tree.leafCount()); + CPPUNIT_ASSERT_EQUAL(LeafIndexType(leafs), tree.leafCount()); CPPUNIT_ASSERT_EQUAL(openvdb::Index64(tiles), tree.activeTileCount()); CPPUNIT_ASSERT_EQUAL(int(openvdb::BoolTree::DEPTH-3), tree.getValueDepth(openvdb::Coord(NodeT1::DIM*1, 0, 0))); CPPUNIT_ASSERT_EQUAL(int(openvdb::BoolTree::DEPTH-3), tree.getValueDepth(openvdb::Coord(NodeT1::DIM*2, 0, 0))); @@ -624,7 +630,7 @@ TestVolumeExecutable::testActiveTileStreaming() (n1ChildCount - leafs) // NodeT1 face tiles (NodeT0) - leafs + 3 /*NodeT1*/ + 1 /*NodeT0*/; - CPPUNIT_ASSERT_EQUAL(openvdb::Index32(leafs), tree.leafCount()); + CPPUNIT_ASSERT_EQUAL(LeafIndexType(leafs), tree.leafCount()); CPPUNIT_ASSERT_EQUAL(openvdb::Index64(tiles), tree.activeTileCount()); CPPUNIT_ASSERT_EQUAL(int(StringTree::DEPTH-3), tree.getValueDepth(openvdb::Coord(NodeT1::DIM*1, 0, 0))); CPPUNIT_ASSERT_EQUAL(int(StringTree::DEPTH-3), tree.getValueDepth(openvdb::Coord(NodeT1::DIM*2, 0, 0))); @@ -668,7 +674,7 @@ TestVolumeExecutable::testActiveTileStreaming() executable->execute(grid); - CPPUNIT_ASSERT_EQUAL(openvdb::Index32(1), tree.leafCount()); + CPPUNIT_ASSERT_EQUAL(LeafIndexType(1), tree.leafCount()); CPPUNIT_ASSERT_EQUAL(openvdb::Index64(2), tree.activeTileCount()); CPPUNIT_ASSERT(tree.hasSameTopology(copy)); CPPUNIT_ASSERT_EQUAL(int(openvdb::FloatTree::DEPTH-3), tree.getValueDepth(openvdb::Coord(0))); @@ -715,7 +721,7 @@ TestVolumeExecutable::testActiveTileStreaming() openvdb::Index64(NodeT1::NUM_VOXELS) + openvdb::Index64(NodeT0::NUM_VOXELS); - CPPUNIT_ASSERT_EQUAL(openvdb::Index32(voxels / openvdb::FloatTree::LeafNodeType::NUM_VOXELS) + 1, tree.leafCount()); + CPPUNIT_ASSERT_EQUAL(LeafIndexType(voxels / openvdb::FloatTree::LeafNodeType::NUM_VOXELS) + 1, tree.leafCount()); CPPUNIT_ASSERT_EQUAL(openvdb::Index64(0), tree.activeTileCount()); CPPUNIT_ASSERT_EQUAL(voxels, tree.activeVoxelCount()); CPPUNIT_ASSERT_EQUAL(leaf, tree.probeLeaf(openvdb::Coord(NodeT1::DIM + NodeT0::DIM))); diff --git a/openvdb_cmd/CMakeLists.txt b/openvdb_cmd/CMakeLists.txt index 02c02e2641..edbb1a4e59 100644 --- a/openvdb_cmd/CMakeLists.txt +++ b/openvdb_cmd/CMakeLists.txt @@ -7,7 +7,7 @@ #]=======================================================================] -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.20) project(OpenVDBBinaries LANGUAGES CXX) include(GNUInstallDirs) diff --git a/openvdb_cmd/vdb_ax/CMakeLists.txt b/openvdb_cmd/vdb_ax/CMakeLists.txt index ee5875fe90..11562df353 100644 --- a/openvdb_cmd/vdb_ax/CMakeLists.txt +++ b/openvdb_cmd/vdb_ax/CMakeLists.txt @@ -7,7 +7,7 @@ #]=======================================================================] -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.20) project(VDBAX LANGUAGES CXX) include(GNUInstallDirs) @@ -34,4 +34,4 @@ set(SOURCE_FILES main.cc) add_executable(vdb_ax ${SOURCE_FILES}) target_link_libraries(vdb_ax ${OPENVDB_BINARIES_DEPENDENT_LIBS}) -install(TARGETS vdb_ax RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +install(TARGETS vdb_ax RUNTIME DESTINATION ${OPENVDB_INSTALL_BINDIR}) diff --git a/openvdb_cmd/vdb_lod/CMakeLists.txt b/openvdb_cmd/vdb_lod/CMakeLists.txt index 37cede195f..a67bdde2f4 100644 --- a/openvdb_cmd/vdb_lod/CMakeLists.txt +++ b/openvdb_cmd/vdb_lod/CMakeLists.txt @@ -7,7 +7,7 @@ #]=======================================================================] -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.20) project(VDBLOD LANGUAGES CXX) include(GNUInstallDirs) @@ -16,4 +16,4 @@ set(SOURCE_FILES main.cc) add_executable(vdb_lod ${SOURCE_FILES}) target_link_libraries(vdb_lod ${OPENVDB_BINARIES_DEPENDENT_LIBS}) -install(TARGETS vdb_lod RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +install(TARGETS vdb_lod RUNTIME DESTINATION ${OPENVDB_INSTALL_BINDIR}) diff --git a/openvdb_cmd/vdb_print/CMakeLists.txt b/openvdb_cmd/vdb_print/CMakeLists.txt index 3a3c73629f..de58c90fba 100644 --- a/openvdb_cmd/vdb_print/CMakeLists.txt +++ b/openvdb_cmd/vdb_print/CMakeLists.txt @@ -7,7 +7,7 @@ #]=======================================================================] -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.20) project(VDBPrint LANGUAGES CXX) include(GNUInstallDirs) @@ -16,4 +16,4 @@ set(SOURCE_FILES main.cc) add_executable(vdb_print ${SOURCE_FILES}) target_link_libraries(vdb_print ${OPENVDB_BINARIES_DEPENDENT_LIBS}) -install(TARGETS vdb_print RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +install(TARGETS vdb_print RUNTIME DESTINATION ${OPENVDB_INSTALL_BINDIR}) diff --git a/openvdb_cmd/vdb_print/main.cc b/openvdb_cmd/vdb_print/main.cc index 3e4fceb8b9..e093838768 100644 --- a/openvdb_cmd/vdb_print/main.cc +++ b/openvdb_cmd/vdb_print/main.cc @@ -210,31 +210,15 @@ printShortListing(const StringVec& filenames, bool metadata) // Print the grid's size, in bytes // no support for memUsageIfLoaded until ABI >= 10 for points::PointDataGrid types -#if OPENVDB_ABI_VERSION_NUMBER < 10 - using ListT = openvdb::GridTypes::Remove; -#else using ListT = openvdb::GridTypes; -#endif - const bool success = - grid->apply([&](const auto& typed){ - // @todo combine these methods to avoid iterating across the tree twice - const openvdb::Index64 incore = openvdb::tools::memUsage(typed.tree()); - const openvdb::Index64 total = openvdb::tools::memUsageIfLoaded(typed.tree()); - - std::cout << " " << std::right << std::setw(6) << bytesAsString(incore) << " (In Core)"; - std::cout << " " << std::right << std::setw(6) << bytesAsString(total) << " (Total)"; - }); - - (void)success; -#if OPENVDB_ABI_VERSION_NUMBER < 10 - if (!success) { - // could be a points grid, print in-core memory only - grid->apply([&](const auto& typed){ - const openvdb::Index64 incore = openvdb::tools::memUsage(typed.tree()); - std::cout << " " << std::right << std::setw(6) << bytesAsString(incore) << " (In Core)"; - }); - } -#endif + grid->apply([&](const auto& typed){ + // @todo combine these methods to avoid iterating across the tree twice + const openvdb::Index64 incore = openvdb::tools::memUsage(typed.tree()); + const openvdb::Index64 total = openvdb::tools::memUsageIfLoaded(typed.tree()); + + std::cout << " " << std::right << std::setw(6) << bytesAsString(incore) << " (In Core)"; + std::cout << " " << std::right << std::setw(6) << bytesAsString(total) << " (Total)"; + }); std::cout << std::endl; diff --git a/openvdb_cmd/vdb_render/CMakeLists.txt b/openvdb_cmd/vdb_render/CMakeLists.txt index fcb534c250..032dfeeb66 100644 --- a/openvdb_cmd/vdb_render/CMakeLists.txt +++ b/openvdb_cmd/vdb_render/CMakeLists.txt @@ -7,7 +7,7 @@ #]=======================================================================] -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.20) project(VDBRender LANGUAGES CXX) include(GNUInstallDirs) @@ -17,11 +17,11 @@ if(USE_PNG) endif() if(USE_IMATH_HALF) - find_package(Imath CONFIG REQUIRED) + find_package(Imath ${MINIMUM_IMATH_VERSION} CONFIG REQUIRED) endif() if(USE_EXR) - find_package(OpenEXR CONFIG REQUIRED) + find_package(OpenEXR ${MINIMUM_OPENEXR_VERSION} CONFIG REQUIRED) endif() set(SOURCE_FILES main.cc) @@ -51,4 +51,4 @@ target_link_libraries(vdb_render $ ) -install(TARGETS vdb_render RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +install(TARGETS vdb_render RUNTIME DESTINATION ${OPENVDB_INSTALL_BINDIR}) diff --git a/openvdb_cmd/vdb_tool/CMakeLists.txt b/openvdb_cmd/vdb_tool/CMakeLists.txt index f9b846b8aa..8101a78816 100644 --- a/openvdb_cmd/vdb_tool/CMakeLists.txt +++ b/openvdb_cmd/vdb_tool/CMakeLists.txt @@ -1,4 +1,6 @@ -cmake_minimum_required(VERSION 3.18) +# Copyright Contributors to the OpenVDB Project +# SPDX-License-Identifier: Apache-2.0 +cmake_minimum_required(VERSION 3.20) # CMP0091 allows for MSVC ABI targetting via CMAKE_MSVC_RUNTIME_LIBRARY # from CMake 3.15 and above. Must come before project(). @@ -26,7 +28,7 @@ list(APPEND CMAKE_MODULE_PATH ${OPENVDB_CMAKE_PATH}) add_library(vdb_tool_common INTERFACE) # Optional components -option(BUILD_TEST "Build unit tests" OFF) +option(OPENVDB_BUILD_VDB_TOOL_UNITTESTS "Build unit tests" OFF) option(OPENVDB_TOOL_USE_NANO "Compile with NanoVDB support" OFF) option(OPENVDB_TOOL_NANO_USE_ZIP "Compile NanoVDB with zip compression support. Requires OPENVDB_TOOL_USE_NANO=ON to have effect" ON) @@ -34,6 +36,7 @@ option(OPENVDB_TOOL_NANO_USE_BLOSC "Compile NanoVDB with Blosc compression suppo option(OPENVDB_TOOL_USE_PNG "Compile with PNG support" OFF) option(OPENVDB_TOOL_USE_EXR "Compile with EXR support" OFF) option(OPENVDB_TOOL_USE_JPG "Compile with JPG support" OFF) +option(OPENVDB_TOOL_USE_PDAL "Compile with extended points support" OFF) option(OPENVDB_TOOL_USE_ABC "Compile with Alembic support" OFF) option(OPENVDB_TOOL_USE_ALL "Compile with all optional components" OFF) if(OPENVDB_TOOL_USE_ALL) @@ -42,18 +45,20 @@ if(OPENVDB_TOOL_USE_ALL) set(OPENVDB_TOOL_USE_EXR ON) set(OPENVDB_TOOL_USE_JPG ON) set(OPENVDB_TOOL_USE_ABC ON) + set(OPENVDB_TOOL_USE_PDAL ON) + endif() if(OPENVDB_TOOL_USE_NANO) target_compile_definitions(vdb_tool_common INTERFACE "VDB_TOOL_USE_NANO") if(OPENVDB_TOOL_NANO_USE_ZIP) target_compile_definitions(vdb_tool_common INTERFACE "NANOVDB_USE_ZIP") - find_package(ZLIB REQUIRED) + find_package(ZLIB ${MINIMUM_ZLIB_VERSION} REQUIRED) target_link_libraries(vdb_tool_common INTERFACE ZLIB::ZLIB) endif() if(OPENVDB_TOOL_NANO_USE_BLOSC) target_compile_definitions(vdb_tool_common INTERFACE "NANOVDB_USE_BLOSC") - find_package(Blosc REQUIRED) + find_package(Blosc ${MINIMUM_BLOSC_VERSION} REQUIRED) target_link_libraries(vdb_tool_common INTERFACE blosc) endif() #target_include_directories(vdb_tool_common INTERFACE ${PROJECT_SOURCE_DIR}/../nanovdb/) @@ -83,6 +88,18 @@ if(OPENVDB_TOOL_USE_PNG) target_link_libraries(vdb_tool_common INTERFACE png) endif() +if(OPENVDB_TOOL_USE_PDAL) + target_compile_definitions(vdb_tool_common INTERFACE "VDB_TOOL_USE_PDAL") + if(WIN32) + find_package(libdpal CONFIG REQUIRED) + else() + find_package(PDAL REQUIRED) + endif() + message(STATUS "PDAL: ${PDAL_LIBRARIES} ${PDAL_INCLUDE_DIRS}") + target_link_libraries(vdb_tool_common INTERFACE ${PDAL_LIBRARIES}) + target_include_directories(vdb_tool_common INTERFACE ${PDAL_INCLUDE_DIRS}) +endif() + if(OPENVDB_TOOL_USE_JPG) target_compile_definitions(vdb_tool_common INTERFACE "VDB_TOOL_USE_JPG") find_package(JPEG REQUIRED) @@ -92,7 +109,7 @@ endif() if(OPENVDB_TOOL_USE_EXR) target_compile_definitions(vdb_tool_common INTERFACE "VDB_TOOL_USE_EXR") - find_package(OpenEXR REQUIRED) + find_package(OpenEXR ${MINIMUM_OPENEXR_VERSION} REQUIRED) target_link_libraries(vdb_tool_common INTERFACE OpenEXR::IlmImf) endif() @@ -166,14 +183,15 @@ target_include_directories(vdb_tool_common INTERFACE "${Boost_INCLUDE_DIRS}" "${ add_executable(vdb_tool src/main.cpp) target_include_directories(vdb_tool PRIVATE vdb_tool_common) target_link_libraries(vdb_tool PRIVATE vdb_tool_common) -install(TARGETS vdb_tool RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +install(TARGETS vdb_tool RUNTIME DESTINATION ${OPENVDB_INSTALL_BINDIR}) # unit test -if(BUILD_TEST) - find_package(GTest CONFIG REQUIRED) +if(OPENVDB_BUILD_VDB_TOOL_UNITTESTS) + find_package(GTest ${MINIMUM_GOOGLETEST_VERSION} CONFIG REQUIRED) add_executable(vdb_tool_test src/unittest.cpp) target_include_directories(vdb_tool_test PRIVATE vdb_tool_common) target_link_libraries(vdb_tool_test PRIVATE vdb_tool_common GTest::gmock GTest::gtest GTest::gmock_main GTest::gtest_main) + add_test(vdb_tool_unit_test vdb_tool_test) endif() diff --git a/openvdb_cmd/vdb_tool/README.md b/openvdb_cmd/vdb_tool/README.md index f64ebb6bea..23bb29066f 100644 --- a/openvdb_cmd/vdb_tool/README.md +++ b/openvdb_cmd/vdb_tool/README.md @@ -10,8 +10,8 @@ This command-line tool, dubbed vdb_tool, can combine any number of the of high-l | **eval** | Evaluate an expression written in our Reverse Polish Notation (see below) | | **config** | Load a configuration file and add the actions for processing | | **default** | Set default values used by all subsequent actions | -| **read** | Read mesh, points and level sets as obj, ply, abc, stl, pts, vdb or nvdb files | -| **write** | Write a polygon mesh, points or level set as a obj, ply, stl, abc or vdb file | +| **read** | Read mesh, points and level sets as obj, ply, abc, stl, off, pts, vdb or nvdb files | +| **write** | Write a polygon mesh, points or level set as a obj, ply, stl, off, abc or vdb file | | **vdb2points** | Extracts points from a VDB grid | | **mesh2ls** | Convert a polygon mesh to a narrow-band level set | | **points2ls** | Convert points into a narrow-band level set | @@ -59,9 +59,10 @@ For support, bug-reports or ideas for improvements please contact ken.museth@gma | Extension | Actions | Description | |-------|-------|-------| | vdb | read and write | OpenVDB sparse volume files with float, Vec3f and points | -| obj | read and write | ASCII OBJ mesh files with triangle, quad or points | -| ply | read and write | Binary and ASCII PLY mesh files with triangle, quad or points | +| obj | read and write | ASCII OBJ mesh files with triangles, quads or points | +| ply | read and write | Binary and ASCII PLY mesh files with triangles, quads or points | | stl | read and write | Binary STL mesh files with triangles | +| off | read and write | ASCI OFF mesh files with triangles, quads or points | | pts | read | ASCII PTS points files with one or more point clouds | | abc | optional read and write | Alembic binary mesh files | | nvdb| optional read and write | NanoVDB file with voxels or points | @@ -73,7 +74,7 @@ For support, bug-reports or ideas for improvements please contact ken.museth@gma # Terminology -We introduce terms: **actions**, **options**, **expressions**, and **instructions**. Actions are high-level openvdb tools, which each have unique options, e.g. -mesh2ls geo=1 voxel=0.1, where "-mesh2ls" is an action with two options "geo" and "voxel". Expressions are strings of code with one or more low-level instructions in our stack-based programming language (see below). These expressions start with "{" and ends with "}", and ":" is used to separate values and instructions. E.g. {1:2:+} is an expression with two values (1 and 2) and one instruction "+", and it reduces to the string value "3". See section on the "Stack-based string expressions" below for more details. +We introduce the following terms: **actions**, **options**, **expressions**, and **instructions**. Actions are high-level openvdb tools, which each have unique options, e.g. -mesh2ls geo=1 voxel=0.1, where "-mesh2ls" is an action with two options "geo" and "voxel". Expressions are strings of code with one or more low-level instructions in our stack-based programming language (see below). These expressions start with "{" and ends with "}", and ":" is used to separate values and instructions. E.g. {1:2:+} is an expression with two values (1 and 2) and one instruction "+", and it reduces to the string value "3". See section on the "Stack-based string expressions" below for more details. Note that **actions** always start with one or more "-" and (except for file names) its associated **options** always contain a "=" and an optional number of leading characters used for identification, e.g. "-erode r=2" is identical to "-erode radius=2.0", but "-erode rr=2" will produce an error since "rr" does not match the first two characters of any option associated with the action "erode". @@ -88,7 +89,7 @@ This tool supports its own light-weight stack-oriented programming language that This tool is using CMake for build on Linux and Windows. The only mandatory dependency of is [OpenVDB](http://www.openvdb.org). Optional dependencies include NanoVDB, libpng, libjpeg, OpenEXR, and Alembic. To enable them use the `-DUSE_=ON` flags. See the CMakeLists.txt for details. -The included unit test are using Gtest. Add `-DBUILD_TEST=ON` to the cmake command line to build it. +The included unit test are using Gtest. Add `-DOPENVDB_BUILD_VDB_TOOL_UNITTESTS=ON` to the cmake command line to build it. ## Building OpenVDB @@ -102,7 +103,7 @@ To generate the makefile, navigate to the cloned directory of vdb_tool, then fol ```bash mkdir build cd build -cmake -DOPENVDB_CMAKE_PATH=/usr/local/lib/cmake/OpenVDB -DUSE_ALL=ON -DBUILD_TEST=ON .. +cmake -DOPENVDB_CMAKE_PATH=/usr/local/lib/cmake/OpenVDB -DUSE_ALL=ON -DOPENVDB_BUILD_VDB_TOOL_UNITTESTS=ON .. ``` Update the OpenVDB cmake path above as needed. diff --git a/openvdb_cmd/vdb_tool/include/Geometry.h b/openvdb_cmd/vdb_tool/include/Geometry.h index 219a1dd4a7..ebd497b3bb 100644 --- a/openvdb_cmd/vdb_tool/include/Geometry.h +++ b/openvdb_cmd/vdb_tool/include/Geometry.h @@ -33,7 +33,7 @@ #ifdef VDB_TOOL_USE_NANO #include -#include +#include #endif #ifdef VDB_TOOL_USE_ABC @@ -45,6 +45,14 @@ #include #endif +#ifdef VDB_TOOL_USE_PDAL +#include "pdal/pdal.hpp" +#include "pdal/PipelineManager.hpp" +#include "pdal/PipelineReaderJSON.hpp" +#include "pdal/util/FileUtils.hpp" +#include +#endif + #if defined(_WIN32) #include #else @@ -58,6 +66,8 @@ OPENVDB_USE_VERSION_NAMESPACE namespace OPENVDB_VERSION_NAME { namespace vdb_tool { +#define MY_CLEAN_VERSION + /// @brief Class that encapsulates (explicit) geometry, i.e. vertices/points, /// triangles and quads. It is used to represent points and polygon meshes class Geometry @@ -82,9 +92,13 @@ class Geometry const std::vector& vtx() const { return mVtx; } const std::vector& tri() const { return mTri; } const std::vector& quad() const { return mQuad; } + const std::vector& rgb() const { return mRGB; } + std::vector& vtx() { return mVtx; } std::vector& tri() { return mTri; } std::vector& quad() { return mQuad; } + std::vector& rgb() { return mRGB; } + const BBoxT& bbox() const; void clear(); @@ -92,26 +106,31 @@ class Geometry // Reads all the vertices in the file and treats them as Geometry void write(const std::string &fileName) const; void writeOBJ(const std::string &fileName) const; - void writePLY(const std::string &fileName) const; + void writeOFF(const std::string &fileName) const; + void writePLY(const std::string &fileName, bool binary = true) const; void writeSTL(const std::string &fileName) const; void writeGEO(const std::string &fileName) const; void writeABC(const std::string &fileName) const; void writeOBJ(std::ostream &os) const; - void writePLY(std::ostream &os) const; + void writeOFF(std::ostream &os) const; + void writePLY(std::ostream &os, bool binary = true) const; void writeSTL(std::ostream &os) const; void read(const std::string &fileName); void readOBJ(const std::string &fileName); + void readOFF(const std::string &fileName); void readPLY(const std::string &fileName); void readSTL(const std::string &fileName); void readPTS(const std::string &fileName); void readGEO(const std::string &fileName); void readABC(const std::string &fileName); + void readPDAL(const std::string &fileName); void readVDB(const std::string &fileName); void readNVDB(const std::string &fileName); void readOBJ(std::istream &is); + void readOFF(std::istream &is); void readPLY(std::istream &is); size_t vtxCount() const { return mVtx.size(); } @@ -138,6 +157,7 @@ class Geometry std::vector mVtx; std::vector mTri; std::vector mQuad; + std::vector mRGB; mutable BBoxT mBBox; std::string mName; @@ -215,7 +235,7 @@ const math::BBox& Geometry::bbox() const void Geometry::write(const std::string &fileName) const { - switch (findFileExt(fileName, {"geo", "obj", "ply", "stl", "abc"})) { + switch (findFileExt(fileName, {"geo", "obj", "ply", "stl", "abc", "off"})) { case 1: this->writeGEO(fileName); break; @@ -231,30 +251,33 @@ void Geometry::write(const std::string &fileName) const case 5: this->writeABC(fileName); break; + case 6: + this->writeOFF(fileName); + break; default: throw std::invalid_argument("Geometry file \"" + fileName + "\" has an invalid extension"); } }// Geometry::write -void Geometry::writePLY(const std::string &fileName) const +void Geometry::writePLY(const std::string &fileName, bool binary) const { if (fileName == "stdout.ply") { //if (isatty(fileno(stdout))) throw std::invalid_argument("writePLY: stdout is not connected to the terminal!"); - this->writePLY(std::cout); + this->writePLY(std::cout, binary); } else { std::ofstream outfile(fileName, std::ios_base::binary); if (!outfile.is_open()) throw std::invalid_argument("Error writing to ply file \""+fileName+"\""); - this->writePLY(outfile);; + this->writePLY(outfile, binary); } }// Geometry::writePLY -void Geometry::writePLY(std::ostream &os) const +void Geometry::writePLY(std::ostream &os, bool binary) const { - os << "ply\n"; - if (isLittleEndian()) { - os << "format binary_little_endian 1.0\n"; + os << "ply\nformat "; + if (binary) { + os << "binary_" << (isLittleEndian() ? "little" : "big") << "_endian 1.0\n"; } else { - os << "format binary_big_endian 1.0\n"; + os << "ascii 1.0\n"; } os << "comment created by vdb_tool" << std::endl; os << "element vertex " << mVtx.size() << std::endl; @@ -265,32 +288,26 @@ void Geometry::writePLY(std::ostream &os) const os << "property list uchar int vertex_index\n"; os << "end_header\n"; static_assert(sizeof(Vec3s) == 3 * sizeof(float), "Unexpected sizeof(Vec3s)"); - os.write((const char *)mVtx.data(), mVtx.size() * 3 * sizeof(float)); - if (mTri.size()>0) { - const size_t size = sizeof(char) + 3*sizeof(uint32_t); - char *buffer = static_cast(std::malloc(mTri.size()*size)), *p = buffer;// uninitialized - if (buffer==nullptr) throw std::invalid_argument("Geometry::writePLY: failed to allocate buffer"); - static_assert(sizeof(Vec3I) == 3 * sizeof(uint32_t), "Unexpected sizeof(Vec3I)"); - for (const Vec3I *t = mTri.data(), *e = t + mTri.size(); t!=e; ++t) { - *p = 3; - std::memcpy(p + 1, t, 3*sizeof(uint32_t)); - p += size; - } - os.write(buffer, mTri.size()*size); - std::free(buffer); - } - if (mQuad.size()>0) { - const size_t size = sizeof(char) + 4*sizeof(uint32_t); - char *buffer = static_cast(std::malloc(mQuad.size()*size)), *p = buffer;// uninitialized - if (buffer==nullptr) throw std::invalid_argument("Geometry::writePLY: failed to allocate buffer"); - static_assert(sizeof(Vec4I) == 4 * sizeof(uint32_t), "Unexpected sizeof(Vec4I)"); - for (const Vec4I *q = mQuad.data(), *e = q + mQuad.size(); q!=e; ++q) { - *p = 4; - std::memcpy(p + 1, q, 4*sizeof(uint32_t)); - p += size; - } - os.write(buffer, mQuad.size()*size); - std::free(buffer); + if (binary) { + os.write((const char *)mVtx.data(), mVtx.size() * 3 * sizeof(float));// write x,y,z vertex coordinates + auto writeFaces = [](std::ostream &os, const uint32_t *faces, size_t count, uint8_t n) { + if (count==0) return; + const int size = 1 + 4*n; + char *buffer = (char*)std::malloc(count*size), *p = buffer;// uninitialized + if (buffer==nullptr) throw std::invalid_argument("Geometry::writePLY: failed to allocate buffer"); + for (const uint32_t *f = faces, *e = f + n*count; f!=e; f+=n, p += size) { + *p = (char)n; + std::memcpy(p + 1, f, 4*n); + } + os.write(buffer, count*size); + std::free(buffer); + }; + writeFaces(os, (const uint32_t*)mTri.data(), mTri.size(), 3); + writeFaces(os, (const uint32_t*)mQuad.data(), mQuad.size(), 4); + } else {// ascii + for (auto &v : mVtx) os << v[0] << " " << v[1] << " " << v[2] << "\n"; + for (auto &t : mTri) os << "3 " << t[0] << " " << t[1] << " " << t[2] << "\n"; + for (auto &q : mQuad) os << "4 " << q[0] << " " << q[1] << " " << q[2] << " " << q[3] << "\n"; } }// Geometry::writePLY @@ -302,24 +319,38 @@ void Geometry::writeOBJ(const std::string &fileName) const } else { std::ofstream outfile(fileName); if (!outfile.is_open()) throw std::invalid_argument("Error writing to obj file \""+fileName+"\""); - this->writeOBJ(outfile);; + this->writeOBJ(outfile); } }// Geometry::writeOBJ void Geometry::writeOBJ(std::ostream &os) const { - os << "# Created by vdb_tool\n"; - for (auto &v : mVtx) { - os << "v " << v[0] << " " << v[1] << " " << v[2] << "\n"; - } - for (auto &t : mTri) { - os << "f " << t[0]+1 << " " << t[1]+1 << " " << t[2]+1 << "\n";// obj is 1-based - } - for (auto &q : mQuad) { - os << "f " << q[0]+1 << " " << q[1]+1 << " " << q[2]+1 << " " << q[3]+1 << "\n";// obj is 1-based - } + os << "# obj file created by vdb_tool\n"; + for (auto &v : mVtx) os << "v " << v[0] << " " << v[1] << " " << v[2] << "\n"; + for (auto &t : mTri) os << "f " << t[0]+1 << " " << t[1]+1 << " " << t[2]+1 << "\n";// obj is 1-based + for (auto &q : mQuad) os << "f " << q[0]+1 << " " << q[1]+1 << " " << q[2]+1 << " " << q[3]+1 << "\n";// obj is 1-based }// Geometry::writeOBJ +void Geometry::writeOFF(const std::string &fileName) const +{ + if (fileName=="stdout.off") { + this->writeOFF(std::cout); + } else { + std::ofstream outfile(fileName); + if (!outfile.is_open()) throw std::invalid_argument("Error writing to off file \""+fileName+"\""); + this->writeOFF(outfile); + } +}// Geometry::writeOFF + +void Geometry::writeOFF(std::ostream &os) const +{ + os << "OFF\n# Created by vdb_tool\n"; + os << mVtx.size() << " " << (mTri.size() + mQuad.size()) << " " << 0 << "\n"; + for (auto &v : mVtx) os << v[0] << " " << v[1] << " " << v[2] << "\n"; + for (auto &t : mTri) os << "3 " << t[0] << " " << t[1] << " " << t[2] << "\n"; + for (auto &q : mQuad) os << "4 " << q[0] << " " << q[1] << " " << q[2] << " " << q[3] << "\n"; +}// Geometry::writeOFF + void Geometry::writeSTL(const std::string &fileName) const { if (fileName == "stdout.stl") { @@ -328,7 +359,7 @@ void Geometry::writeSTL(const std::string &fileName) const } else { std::ofstream outfile(fileName, std::ios::out | std::ios_base::binary); if (!outfile.is_open()) throw std::invalid_argument("Error writing to stl file \""+fileName+"\""); - this->writeSTL(outfile);; + this->writeSTL(outfile); } }// Geometry::writeSTL @@ -367,7 +398,7 @@ void Geometry::writeGEO(const std::string &fileName) const void Geometry::read(const std::string &fileName) { - switch (findFileExt(fileName, {"obj", "ply", "pts", "stl", "abc", "vdb", "nvdb", "geo"})) { + switch (findFileExt(fileName, {"obj", "ply", "pts", "stl", "abc", "vdb", "nvdb", "geo", "off"})) { case 1: this->readOBJ(fileName); break; @@ -392,9 +423,20 @@ void Geometry::read(const std::string &fileName) case 8: this->readGEO(fileName); break; + case 9: + this->readOFF(fileName); + break; default: - throw std::invalid_argument("Geometry::read: File \""+fileName+"\" has an invalid extension"); - break; +#if VDB_TOOL_USE_PDAL + pdal::StageFactory factory; + const std::string driver = factory.inferReaderDriver(fileName); + if (driver != "") { + this->readPDAL(fileName); + break; + } +#endif + throw std::invalid_argument("Geometry::read: File \""+fileName+"\" has an invalid extension"); + break; } }// Geometry::read @@ -442,6 +484,118 @@ void Geometry::readOBJ(std::istream &is) mBBox = BBoxT();//invalidate BBox }// Geometry::readOBJ +void Geometry::readPDAL(const std::string &fileName) +{ + #if VDB_TOOL_USE_PDAL + if (!pdal::FileUtils::fileExists(fileName)) throw std::invalid_argument("Error opening file \""+fileName+"\" - it doesn't exist!"); + + pdal::StageFactory factory; + std::string type = factory.inferReaderDriver(fileName); + std::string pipelineJson = R"({ + "pipeline" : [ + { + "type" : ")" + type + R"(", + "filename" : ")" + fileName + R"(" + } + ] + })"; + + Vec3f p; + Vec3s rgb; + try { + pdal::PipelineManager manager; + std::stringstream s(pipelineJson); + manager.readPipeline(s); + manager.execute(pdal::ExecMode::Standard); + + for (const std::shared_ptr& view : manager.views()) { + bool hasColor = false; + if (view->hasDim(pdal::Dimension::Id::Red) && view->hasDim(pdal::Dimension::Id::Green) && view->hasDim(pdal::Dimension::Id::Blue)) + hasColor = true; + for (const pdal::PointRef& point : *view) { + p[0] = point.getFieldAs(pdal::Dimension::Id::X); + p[1] = point.getFieldAs(pdal::Dimension::Id::Y); + p[2] = point.getFieldAs(pdal::Dimension::Id::Z); + mVtx.push_back(p); + if (hasColor) { + rgb[0] = point.getFieldAs(pdal::Dimension::Id::Red); + rgb[1] = point.getFieldAs(pdal::Dimension::Id::Green); + rgb[2] = point.getFieldAs(pdal::Dimension::Id::Blue); + mRGB.push_back(rgb); + } + } + } + + } + catch (const pdal::pdal_error& e) { + throw std::runtime_error("PDAL failed: " + std::string(e.what())); + } + catch (const std::exception& e) { + throw std::runtime_error("Reading file failed: " + std::string(e.what())); + } +#else + throw std::runtime_error("Cannot read file \"" + fileName + "\". PDAL support is not enabled in this build, please recompile with PDAL support"); +#endif + mBBox = BBoxT(); //invalidate BBox +}// Geometry::readPDAL + +void Geometry::readOFF(const std::string &fileName) +{ + if (fileName == "stdin.off") { + this->readOFF(std::cin); + } else { + std::ifstream infile(fileName); + if (!infile.is_open()) throw std::invalid_argument("Error opening Geometry file \""+fileName+"\""); + this->readOFF(infile); + } +}// Geometry::readOFF + +void Geometry::readOFF(std::istream &is) +{ + // read header + std::string line; + if (!std::getline(is, line) || line != "OFF") { + throw std::invalid_argument("Geometry::readOFF: expected header \"OFF\" but read \"" + line + "\""); + } + + // read vertex and face counts + size_t vtxCount=0, faceCount=0, edgeCount=0, nGon=0; + while (vtxCount == 0 && std::getline(is, line)) { + if (line.empty() || line[0] == '#') continue; + std::istringstream iss(line); + iss >> vtxCount >> faceCount >> edgeCount; + } + + // read vertices + Vec3f p; + vtxCount += mVtx.size(); + while (mVtx.size() < vtxCount && std::getline(is, line)) { + if (line.empty() || line[0] == '#') continue; + std::istringstream iss(line); + iss >> p[0] >> p[1] >> p[2]; + mVtx.push_back(p); + } + + // read faces + int f[4]; + faceCount += mTri.size() + mQuad.size(); + while (mTri.size() + mQuad.size() < faceCount && std::getline(is, line)) { + if (line.empty() || line[0] == '#') continue; + std::istringstream iss(line); + iss >> nGon; + if (nGon == 3) { + iss >> f[0] >> f[1] >> f[2]; + mTri.emplace_back(f[0],f[1],f[2]); + } else if (nGon == 4) { + iss >> f[0] >> f[1] >> f[2] >> f[3]; + mQuad.emplace_back(f[0],f[1],f[2],f[3]); + } else { + throw std::invalid_argument("Geometry::readOFF: " + std::to_string(nGon) + "-gons are not supported"); + } + } + mBBox = BBoxT();//invalidate BBox +}// Geometry::readOFF + void Geometry::readPLY(const std::string &fileName) { if (fileName == "stdin.ply") { @@ -468,9 +622,7 @@ void Geometry::readPLY(std::istream &is) auto tokens = tokenize_line(); auto test = [&tokens](int i, std::vector str) { if (i >= static_cast(tokens.size())) return false; - for (auto &s : str) { - if (tokens[i] == s) return true; - } + for (auto &s : str) if (tokens[i] == s) return true; return false; }; auto error = [&tokens](const std::string &msg){ @@ -479,8 +631,19 @@ void Geometry::readPLY(std::istream &is) std::cerr << "\"\n"; throw std::invalid_argument(msg); }; + auto sizeOf = [test, error](int i){ + if ( test(i, {"float", "float32", "int", "int32"}) ) return 4; + if ( test(i, {"double", "float64"}) ) return 8; + if ( test(i, {"int16", "uint16"}) ) return 2; + if ( test(i, {"uchar", "int8"}) ) return 1; + error("vdb_tool::readPLY: unsupported type"); + return 0; + }; + // check header if (!test(0, {"ply"})) error("vdb_tool::readPLY: not a ply file"); + + // check file format int format = -1;// 0 is ascii, 1 is little endian and 2 is big endian tokens = tokenize_line(); if (!(test(0, {"format"}) && test(2, {"1.0"})) ) { @@ -496,18 +659,20 @@ void Geometry::readPLY(std::istream &is) } const bool reverseBytes = format && format != (isLittleEndian() ? 1 : 2); // header: https://www.mathworks.com/help/vision/ug/the-ply-format.html - size_t vtxCount = 0, polyCount = 0; - struct Skip {int count, bytes;} vtx_skip[2]={{0,0},{0,0}}, ply_skip[2]={{0,0},{0,0}}; + size_t vtxCount = 0, faceCount = 0; + int vtxStride=0, vtxProps=0;// byte size of all vtx properties, number of vertex properties + struct Triplet {int offset, id, size;} xyz[3];// byte offset, id#, byte size + struct Skip {int count, bytes;} faceSkip[2]={{0,0},{0,0}};// head, {faces}, tail + + // parse header with vertex, face and property information tokens = tokenize_line(); bool run = true; while(run) { if ( test(0, {"element"}) ) { if ( test(1, {"vertex"}) ) { vtxCount = std::stoll(tokens[2]); - int n = 0; const std::string axis[3] = {"x", "y", "z"}; while(true) { - const int m = n>0 ? 1 : 0; tokens = tokenize_line(); if ( test(0, {"end_header"}) ) { run = false; @@ -515,35 +680,16 @@ void Geometry::readPLY(std::istream &is) } else if ( test(0, {"element"}) ) { break; } else if ( test(0, {"property"}) ) { - if ( test(1, {"float", "float32"}) ) { - if ( test(2, {"x", "y", "z"}) ) {// nx,ny.nz - if (n>2 || !test(2, {axis[n++]}) ) error("vdb_tool::readPLY: expected x or y or z"); - } else {// e.g. nx, ny, nz, intensity, s, t etc - if (n!=0 && n!=3) error("vdb_tool::readPLY: vertex float property interlaced with coordinates"); - vtx_skip[m].count += 1; - vtx_skip[m].bytes += static_cast(sizeof(float)); - } - } else if ( test(1, {"int16", "uint16"}) ) {// e.g. material_index etc - if (n!=0 && n!=3) error("vdb_tool::readPLY: vertex int16 property interlaced with coordinates is not supported"); - vtx_skip[m].count += 1; - vtx_skip[m].bytes += static_cast(sizeof(int16_t)); - } else if ( test(1, {"int", "int32"}) ) {// e.g. material_index etc - if (n!=0 && n!=3) error("vdb_tool::readPLY: vertex int32 property interlaced with coordinates is not supported"); - vtx_skip[m].count += 1; - vtx_skip[m].bytes += static_cast(sizeof(int32_t)); - } else if ( test(1, {"uchar", "int8"}) ) {// eg red, green, blue, alpha - if (n!=0 && n!=3) error("vdb_tool::readPLY: vertex int8 property interlaced with coordinates is not supported"); - vtx_skip[m].count += 1; - vtx_skip[m].bytes += static_cast(sizeof(unsigned char)); - } else { - error("vdb_tool::readPLY: invalid vertex property"); - } + Triplet t{vtxStride, vtxProps++, sizeOf(1)}; + for (int i=0; i<3; ++i) if (test(2, {axis[i]})) xyz[i] = t; + vtxStride += t.size; } } - if (n!=3) error("vdb_tool::readPLY: missing vertex coordinates"); + for (int i=0; i<3; ++i) if (xyz[i].size!=4 && xyz[i].size!=8) error("vdb_tool::readPLY: missing "+axis[i]+ + " vertex coordinates or unsupported size "+std::to_string(xyz[i].size)); } else if ( test(1, {"face"}) ) { - polyCount = std::stoll(tokens[2]); - int n = 0; + faceCount = std::stoll(tokens[2]); + int n = 0;// 0 is head and 1 is tail while (true) { tokens = tokenize_line(); if ( test(0, {"end_header"}) ) { @@ -551,15 +697,15 @@ void Geometry::readPLY(std::istream &is) break; } else if (test(0, {"element"}) ) { break; - } else if (test(0, {"property"}) ) { - if (test(1, {"list"}) && - test(2, {"uchar", "uint8"}) && - test(3, {"int", "uint", "int32"}) && + } else if (test(0, {"property"}) ) {// eg: "property list uchar int vertex_indices" + if (test(1, {"list"}) &&// list of vertex ID belonging to a polygon + test(2, {"uchar", "uint8"}) &&// size of polygon, e.g. 3 or 4 + test(3, {"int", "uint", "int32"}) &&// type of vertex id test(4, {"vertex_index", "vertex_indices"}) ) { - n = 1; - } else if ( test(1, {"uchar", "uint8"}) ) { - ply_skip[n].count += 1; - ply_skip[n].bytes += 1; + n = 1;// change from head to tail + } else if ( test(1, {"uchar", "uint8"}) ) {// eg: "property uchar intensity" + faceSkip[n].count += 1; + faceSkip[n].bytes += 1; } else { error("vdb_tool::readPLY: invalid face properties"); } @@ -578,7 +724,7 @@ void Geometry::readPLY(std::istream &is) } else { error("vdb_tool::readPLY: invalid element"); } - } else if ( test(0, {"comment", "obj_info"}) ) { + } else if ( test(0, {"comment", "obj_info"}) ) {// eq: "obj_info 3D colored patch boundaries" and "comment author: Paraform" tokens = tokenize_line(); } else { error("vdb_tool::readPLY: unexpected entry in header"); @@ -588,96 +734,69 @@ void Geometry::readPLY(std::istream &is) // read vertex coordinates mVtx.resize(vtxCount); if (format) {// binary - if (vtx_skip[0].count == 0 && vtx_skip[1].count == 0) {//faster + if (xyz[0].offset==0 && xyz[1].offset==4 && xyz[2].offset==8 && vtxStride==12) {// most common case is.read((char *)(mVtx.data()), vtxCount * 3 * sizeof(float)); + if (reverseBytes) for (Vec3f &v : mVtx) swapBytes(&v[0], 3); } else { - const size_t bSize = vtx_skip[0].bytes + 3*sizeof(float) + vtx_skip[1].bytes; - char *buffer = static_cast(std::malloc(vtxCount*bSize));// uninitialized + char *buffer = static_cast(std::malloc(vtxCount*vtxStride)), *p = buffer;// uninitialized if (buffer==nullptr) throw std::invalid_argument("Geometry::readPLY: failed to allocate buffer"); - is.read(buffer, vtxCount*bSize); - for (size_t i=0; i(buffer + i*bSize + vtx_skip[0].bytes); - mVtx[i] = Vec3f(p); + is.read(buffer, vtxCount*vtxStride); + for (Vec3f &vtx : mVtx) { + for (int i=0; i<3; ++i) { + if (xyz[i].size == 4) { + float v = *(float*)(p + xyz[i].offset); + vtx[i] = reverseBytes ? swapBytes(v) : v; + } else { + double v = *(double*)(p + xyz[i].offset); + vtx[i] = float(reverseBytes ? swapBytes(v) : v); + } + } + p += vtxStride; } std::free(buffer); } - if (reverseBytes) { - auto flipBytes = [](float v)->float{ - float tmp; - char *p = (char*)&v, *q = (char*)&tmp; - q[0] = p[3]; - q[1] = p[2]; - q[2] = p[1]; - q[3] = p[0]; - return tmp; - };// flipBytes in float - for (size_t i = 0; i < mVtx.size(); ++i) { - auto &p = mVtx[i]; - p[0] = flipBytes(p[0]); - p[1] = flipBytes(p[1]); - p[2] = flipBytes(p[2]); - } - } - } else {// ascii + + } else {// ascii vertices for (auto &v : mVtx) { tokens = tokenize_line(); - if (static_cast(tokens.size()) != vtx_skip[0].count + 3 + vtx_skip[1].count) { - error("vdb_tool::readPLY: error reading ascii vertex coordinates"); - } - for (int i = 0; i<3; ++i) { - v[i] = std::stof(tokens[i + vtx_skip[0].count]); - } + if (int(tokens.size()) != vtxProps) error("vdb_tool::readPLY: error reading ascii vertex coordinates"); + for (int i = 0; i<3; ++i) v[i] = std::stof(tokens[xyz[0].id]); }// loop over vertices } // read polygon vertex lists uint32_t vtx[4]; if (format) {// binary - auto flipBytes = [&](int n){ - uint32_t tmp; - char *q = (char*)&tmp; - for (int i=0; i(std::malloc(ply_skip[0].bytes + 1));// uninitialized + char *buffer = static_cast(std::malloc(faceSkip[0].bytes + 1));// uninitialized if (buffer==nullptr) throw std::invalid_argument("Geometry::readPLY: failed to allocate buffer"); - for (size_t i=0; i unsigned int switch (n) { case 3: - is.read((char *)(&vtx), 3*sizeof(uint32_t)); - if (reverseBytes) flipBytes(3); + is.read((char*)vtx, 3*sizeof(uint32_t)); + if (reverseBytes) swapBytes(vtx, 3); mTri.emplace_back(vtx); break; case 4: - is.read((char *)(&vtx), 4*sizeof(uint32_t)); - if (reverseBytes) flipBytes(4); + is.read((char*)vtx, 4*sizeof(uint32_t)); + if (reverseBytes) swapBytes(vtx, 4); mQuad.emplace_back(vtx); break; default: throw std::invalid_argument("Geometry::readPLY: binary " + std::to_string(n) + "-gons are not supported"); break; } - is.ignore(ply_skip[1].bytes); + is.ignore(faceSkip[1].bytes); }// loop over polygons std::free(buffer); - } else {// ascii format - for (size_t i=0; i(std::stoll(tokens[i + 1 + ply_skip[0].count])); - } + const std::string polySize = tokens[faceSkip[0].count]; + const int n = std::stoi(polySize); + if (n!=3 && n!=4) throw std::invalid_argument("Geometry::readPLY: ascii " + polySize + "-gons are not supported"); + for (int i = 0, j=1+faceSkip[0].count; i(std::stoll(tokens[j])); if (n==3) { mTri.emplace_back(vtx); } else { @@ -753,6 +872,8 @@ void Geometry::readPTS(const std::string &fileName) if (!infile.is_open()) throw std::runtime_error("Error opening particle file \""+fileName+"\""); std::string line; std::istringstream iss; + bool readColor = false; + Vec3s rgb; while(std::getline(infile, line)) { const size_t n = mVtx.size(), m = std::stoi(line); mVtx.resize(n + m); @@ -764,6 +885,18 @@ void Geometry::readPTS(const std::string &fileName) if (!(iss >> p[0] >> p[1] >> p[2])) {;//ignore intensity, r, g, b throw std::invalid_argument("Geometry::readPTS: error parsing line: \""+line+"\""); } + if (readColor) { + if (!(iss >> i) ) { // converting intensity to a multiplier on rgb might be appropriate, but i can't find a good spec for it + readColor = false; + continue; + } + if (!(iss >> rgb[0] >> rgb[1] >> rgb[2])) { + readColor = false; + continue; + } + mRGB.push_back(rgb/255.0); + } + }// loop over points }// loop over scans mBBox = BBoxT();//invalidate BBox diff --git a/openvdb_cmd/vdb_tool/include/Tool.h b/openvdb_cmd/vdb_tool/include/Tool.h index 9fc7745f40..b24e71696e 100644 --- a/openvdb_cmd/vdb_tool/include/Tool.h +++ b/openvdb_cmd/vdb_tool/include/Tool.h @@ -47,14 +47,15 @@ #include // for tools::interiorMask() #include #include +#include #include #include #ifdef VDB_TOOL_USE_NANO #include -#include -#include -#include +#include +#include +#include #endif #ifdef VDB_TOOL_USE_EXR @@ -69,6 +70,10 @@ #include #endif +#ifdef VDB_TOOL_USE_PDAL +#include +#endif + #ifdef VDB_TOOL_USE_JPG #include #endif @@ -364,14 +369,14 @@ void Tool::init() mParser.addAction( "read", "i", "Read one or more geometry or VDB files from disk or STDIN.", - {{"files", "", "{file|stdin}.{abc|obj|ply|stl|vdb}", "list of files or the input stream, e.g. file.vdb,stdin.vdb. Note that \"files=\" is optional since any argument without \"=\" is intrepreted as a file and appended to \"files\""}, + {{"files", "", "{file|stdin}.{abc|obj|ply|stl|off|vdb}", "list of files or the input stream, e.g. file.vdb,stdin.vdb. Note that \"files=\" is optional since any argument without \"=\" is intrepreted as a file and appended to \"files\""}, {"grids", "*", "*|grid_name,...", "list of VDB grids name to be imported (defaults to \"*\", i.e. import all available grids)"}, {"delayed", "true", "1|0|true|false", "toggle delayed loading of VDB grids (enabled by default). This option is ignored by other file types"}}, [](){}, [&](){this->read();}, 0);// anonymous options are treated as to the first option,i.e. "files" mParser.addAction( "write", "o", "Write list of geometry, VDB or config files to disk or STDOUT", - {{"files", "", "{file|stdout}.{obj|ply|stl|vdb|nvdb}", "list of files or the output stream, e.g. file.vdb or stdin.vdb. Note that \"files=\" is optional since any argument without the \"=\" character is intrepreted as a file and appended to \"files\"."}, + {{"files", "", "{file|stdout}.{obj|ply|stl|off|vdb|nvdb}", "list of files or the output stream, e.g. file.vdb or stdin.vdb. Note that \"files=\" is optional since any argument without the \"=\" character is intrepreted as a file and appended to \"files\"."}, {"geo", "0", "0|1...", "geometry to write (defaults to \"0\" which is the latest)."}, {"vdb", "*", "0,1,...", "list of VDB grids to write (defaults to \"*\", i.e. all available grids)."}, {"keep", "", "1|0|true|false", "toggle wether to preserved or deleted geometry and grids after they have been written."}, @@ -920,8 +925,8 @@ std::string Tool::examples() const { const int w = 16; std::stringstream ss; - ss << std::left << std::setw(w) << "Surface points:" << mCmdName << " -read points.[obj/ply/stl/pts] -points2ls d=256 r=2.0 w=3 -dilate r=2 -gauss i=1 w=1 -erode r=2 -ls2m a=0.25 -write output.[ply/obj/stl]\n"; - ss << std::left << std::setw(w) << "Convert mesh: " << mCmdName << " -read mesh.[ply/obj] -mesh2ls d=256 -write output.vdb config.txt\n"; + ss << std::left << std::setw(w) << "Surface points:" << mCmdName << " -read points.[obj/ply/stl/off/pts] -points2ls d=256 r=2.0 w=3 -dilate r=2 -gauss i=1 w=1 -erode r=2 -ls2m a=0.25 -write output.[ply/obj/stl]\n"; + ss << std::left << std::setw(w) << "Convert mesh: " << mCmdName << " -read mesh.[ply/obj/off] -mesh2ls d=256 -write output.vdb config.txt\n"; ss << std::left << std::setw(w) << "Config example:" << mCmdName << " -config config.txt\n"; return ss.str(); } @@ -958,7 +963,7 @@ void Tool::read() { OPENVDB_ASSERT(mParser.getAction().name == "read"); for (auto &fileName : mParser.getVec("files")) { - switch (findFileExt(fileName, {"geo,obj,ply,abc,pts,stl", "vdb", "nvdb"})) { + switch (findFileExt(fileName, {"geo,obj,ply,abc,pts,off,stl", "vdb", "nvdb"})) { case 1: this->readGeo(fileName); break; @@ -969,6 +974,14 @@ void Tool::read() this->readNVDB(fileName); break; default: +#if VDB_TOOL_USE_PDAL + pdal::StageFactory factory; + if (factory.inferReaderDriver(fileName) != "") + { + this->readGeo(fileName); + break; + } +#endif throw std::invalid_argument("File \""+fileName+"\" has an invalid extension"); break; } @@ -1049,7 +1062,7 @@ void Tool::readNVDB(const std::string &fileName) const size_t count = mGrid.size(); if (grids.size()) { for (const auto& gridHandle : grids) { - if (gridNames[0]=="*" || findMatch(gridHandle.gridMetaData()->shortGridName(), gridNames)) mGrid.push_back(nanovdb::nanoToOpenVDB(gridHandle)); + if (gridNames[0]=="*" || findMatch(gridHandle.gridMetaData()->shortGridName(), gridNames)) mGrid.push_back(nanovdb::tools::nanoToOpenVDB(gridHandle)); } } else if (mParser.verbose>0) { std::cerr << "readVDB: no vdb grids in \"" << fileName << "\""; @@ -1122,7 +1135,7 @@ void Tool::write() { OPENVDB_ASSERT(mParser.getAction().name == "write"); for (std::string &fileName : mParser.getVec("files")) { - switch (findFileExt(fileName, {"geo,obj,ply,stl,abc", "vdb", "nvdb", "txt"})) { + switch (findFileExt(fileName, {"geo,obj,ply,stl,off,abc", "vdb", "nvdb", "txt"})) { case 1: this->writeGeo(fileName); break; @@ -1249,26 +1262,26 @@ void Tool::writeNVDB(const std::string &fileName) throw std::invalid_argument("writeNVDB: unsupported bits \""+bits+"\""); } - nanovdb::StatsMode sMode = nanovdb::StatsMode::Default; + nanovdb::tools::StatsMode sMode = nanovdb::tools::StatsMode::Default; if (stats == "none") { - sMode = nanovdb::StatsMode::Disable; + sMode = nanovdb::tools::StatsMode::Disable; } else if (stats == "bbox") { - sMode = nanovdb::StatsMode::BBox; + sMode = nanovdb::tools::StatsMode::BBox; } else if (stats == "extrema") { - sMode = nanovdb::StatsMode::MinMax; + sMode = nanovdb::tools::StatsMode::MinMax; } else if (stats == "all") { - sMode = nanovdb::StatsMode::All; + sMode = nanovdb::tools::StatsMode::All; } else if (stats != "") { throw std::invalid_argument("writeNVDB: unsupported stats \""+stats+"\""); } - nanovdb::ChecksumMode cMode = nanovdb::ChecksumMode::Default; + nanovdb::CheckMode cMode = nanovdb::CheckMode::Default; if (checksum == "none") { - cMode = nanovdb::ChecksumMode::Disable; + cMode = nanovdb::CheckMode::Disable; } else if (checksum == "partial") { - cMode = nanovdb::ChecksumMode::Partial; + cMode = nanovdb::CheckMode::Partial; } else if (checksum == "full") { - cMode = nanovdb::ChecksumMode::Full; + cMode = nanovdb::CheckMode::Full; } else if (checksum != "") { throw std::invalid_argument("writeNVDB: unsupported checksum \""+checksum+"\""); } @@ -1289,21 +1302,21 @@ void Tool::writeNVDB(const std::string &fileName) using SrcGridT = openvdb::FloatGrid; switch (qMode){ case nanovdb::GridType::Fp4: - return nanovdb::createNanoGrid(*floatGrid, sMode, cMode, dither, verbose); + return nanovdb::tools::createNanoGrid(*floatGrid, sMode, cMode, dither, verbose); case nanovdb::GridType::Fp8: - return nanovdb::createNanoGrid(*floatGrid, sMode, cMode, dither, verbose); + return nanovdb::tools::createNanoGrid(*floatGrid, sMode, cMode, dither, verbose); case nanovdb::GridType::Fp16: - return nanovdb::createNanoGrid(*floatGrid, sMode, cMode, dither, verbose); + return nanovdb::tools::createNanoGrid(*floatGrid, sMode, cMode, dither, verbose); case nanovdb::GridType::FpN: if (absolute) { - return nanovdb::createNanoGrid(*floatGrid, sMode, cMode, dither, verbose, nanovdb::AbsDiff(tolerance)); + return nanovdb::tools::createNanoGrid(*floatGrid, sMode, cMode, dither, verbose, nanovdb::tools::AbsDiff(tolerance)); } else { - return nanovdb::createNanoGrid(*floatGrid, sMode, cMode, dither, verbose, nanovdb::RelDiff(tolerance)); + return nanovdb::tools::createNanoGrid(*floatGrid, sMode, cMode, dither, verbose, nanovdb::tools::RelDiff(tolerance)); } default: break;// 32 bit float grids are handled below }// end of switch } - return nanovdb::openToNanoVDB(base, sMode, cMode, verbose);// float and other grids + return nanovdb::tools::openToNanoVDB(base, sMode, cMode, verbose);// float and other grids };// openToNano if (fileName=="stdout.nvdb") { @@ -1421,6 +1434,7 @@ void Tool::pointsToVdb() const int bits = mParser.get("bits"); std::string grid_name = mParser.get("name"); using GridT = points::PointDataGrid; + using IdGridT = tools::PointIndexGrid; if (mParser.verbose) mTimer.start("Points to VDB"); auto it = this->getGeom(age); Points points((*it)->vtx()); @@ -1428,19 +1442,48 @@ void Tool::pointsToVdb() auto xform = math::Transform::createLinearTransform(voxelSize); GridT::Ptr grid; + IdGridT::Ptr indexGrid; + + points::PointAttributeVector positionsWrapper((*it)->vtx()); + openvdb::NamePair rgbAttribute ; switch (bits) { case 8: - grid = points::createPointDataGrid, GridT>((*it)->vtx(), *xform); + indexGrid = tools::createPointIndexGrid(positionsWrapper, *xform); + grid = points::createPointDataGrid, GridT>(*indexGrid, positionsWrapper, *xform); + openvdb::points::TypedAttributeArray::registerType(); + rgbAttribute = + openvdb::points::TypedAttributeArray::attributeType(); + openvdb::points::appendAttribute(grid->tree(), "Cd", rgbAttribute); break; case 16: - grid = points::createPointDataGrid, GridT>((*it)->vtx(), *xform); + indexGrid = tools::createPointIndexGrid(positionsWrapper, *xform); + grid = points::createPointDataGrid, GridT>(*indexGrid, positionsWrapper, *xform); + openvdb::points::TypedAttributeArray::registerType(); + rgbAttribute = + openvdb::points::TypedAttributeArray::attributeType(); + openvdb::points::appendAttribute(grid->tree(), "Cd", rgbAttribute); break; case 32: - grid = points::createPointDataGrid((*it)->vtx(), *xform); + indexGrid = tools::createPointIndexGrid(positionsWrapper, *xform); + grid = points::createPointDataGrid(*indexGrid, positionsWrapper, *xform); + + openvdb::points::TypedAttributeArray::registerType(); + rgbAttribute = + openvdb::points::TypedAttributeArray::attributeType(); + openvdb::points::appendAttribute(grid->tree(), "Cd", rgbAttribute); break; default: throw std::invalid_argument("pointsToVdb: unsupported bit-width: "+std::to_string(bits)); } + + if ((*it)->rgb().size() == (*it)->vtx().size()) { + + points::PointAttributeVector rgbWrapper((*it)->rgb()); + points::populateAttribute>( + grid->tree(), indexGrid->tree(), "Cd", rgbWrapper); + + } if (grid_name.empty()) grid_name = "points2vdb_"+(*it)->getName(); grid->setName(grid_name); mGrid.push_back(grid); diff --git a/openvdb_cmd/vdb_tool/include/Util.h b/openvdb_cmd/vdb_tool/include/Util.h index 96c324711c..9d084fb22e 100644 --- a/openvdb_cmd/vdb_tool/include/Util.h +++ b/openvdb_cmd/vdb_tool/include/Util.h @@ -377,6 +377,31 @@ inline bool isLittleEndian() return (*(char *)&tmp == 1); } +/// @brief invert endianess of a type +/// @tparam T Template type to be inverted +/// @param val value to be inverted +/// @return value with reverse bytes +template +inline T swapBytes(T val) +{ + T tmp; + for (char *src=(char*)&val, *dst=(char*)(&tmp)+sizeof(T)-1, *end=src+sizeof(T);src!=end; *dst-- = *src++); + return tmp; +} + +/// @brief invert endianess of an array of values of a specific type +/// @tparam T Template type to be inverted +/// @param val pointer to array with values to be inverted +/// @param n number of elements in the array +template +inline void swapBytes(T *val, int n) +{ + for (T tmp, *last = val + n; val < last; ++val) { + for (char *src=(char*)val, *dst=(char*)(&tmp)+sizeof(T)-1, *end=src+sizeof(T); src!=end; *dst-- = *src++); + *val = tmp; + } +} + /// @brief return a pseudo random uuid string. /// /// @details this function approximates a uuid version 4, variant 1 as detailed diff --git a/openvdb_cmd/vdb_tool/src/unittest.cpp b/openvdb_cmd/vdb_tool/src/unittest.cpp index 16a6ef557c..0ddd6fb2cf 100644 --- a/openvdb_cmd/vdb_tool/src/unittest.cpp +++ b/openvdb_cmd/vdb_tool/src/unittest.cpp @@ -301,6 +301,52 @@ TEST_F(Test_vdb_tool, Util) } EXPECT_EQ(size, tmp.size()); } + + {//swapBytes + const int i = 4, j = openvdb::vdb_tool::swapBytes(i); + EXPECT_NE(i, j); + EXPECT_EQ(i, openvdb::vdb_tool::swapBytes(j)); + + const float a = 4, b = openvdb::vdb_tool::swapBytes(a); + EXPECT_NE(a, b); + EXPECT_EQ(a, openvdb::vdb_tool::swapBytes(b)); + + const double x = 4, y = openvdb::vdb_tool::swapBytes(x); + EXPECT_NE(x, y); + EXPECT_EQ(x, openvdb::vdb_tool::swapBytes(y)); + + int vec_i[3]={3,4,5}, vec_j[3]; + for (int n=0; n<3; ++n) { + vec_j[n] = openvdb::vdb_tool::swapBytes(vec_i[n]); + EXPECT_NE(vec_i[n], vec_j[n]); + } + openvdb::vdb_tool::swapBytes(vec_j, 3); + for (int n=0; n<3; ++n) EXPECT_EQ(vec_i[n], vec_j[n]); + + float vec_a[3]={3,4,5}, vec_b[3]; + for (int n=0; n<3; ++n) { + vec_b[n] = openvdb::vdb_tool::swapBytes(vec_a[n]); + EXPECT_NE(vec_a[n], vec_b[n]); + } + openvdb::vdb_tool::swapBytes(vec_b, 3); + for (int n=0; n<3; ++n) EXPECT_EQ(vec_a[n], vec_b[n]); + + double vec_x[3]={3,4,5}, vec_y[3]; + for (int n=0; n<3; ++n) { + vec_y[n] = openvdb::vdb_tool::swapBytes(vec_x[n]); + EXPECT_NE(vec_x[n], vec_y[n]); + } + openvdb::vdb_tool::swapBytes(vec_y, 3); + for (int n=0; n<3; ++n) EXPECT_EQ(vec_x[n], vec_y[n]); + } + {// weird pointer behaviour + float vec[4], *p = vec; + EXPECT_EQ(vec, p);// of course + EXPECT_EQ((char*)(vec), (char*)p);// sure + EXPECT_EQ((char*)(&vec), (char*)p);// wait, what?! + EXPECT_NE((char*)(vec), (char*)(&p));// yep + EXPECT_NE((char*)(&p), (char*)p);// of course + } }// Util TEST_F(Test_vdb_tool, getArgs) @@ -371,18 +417,18 @@ TEST_F(Test_vdb_tool, Geometry) EXPECT_EQ(4, geo2.vtxCount()); EXPECT_EQ(2, geo2.triCount()); EXPECT_EQ(1, geo2.quadCount()); - EXPECT_EQ(openvdb::Vec3f(1,2,3), geo.bbox().min()); - EXPECT_EQ(openvdb::Vec3f(10,11,12), geo.bbox().max()); + EXPECT_EQ(openvdb::Vec3f(1,2,3), geo2.bbox().min()); + EXPECT_EQ(openvdb::Vec3f(10,11,12), geo2.bbox().max()); - EXPECT_EQ(openvdb::Vec3f(1,2,3), geo.vtx()[0]); - EXPECT_EQ(openvdb::Vec3f(4,5,6), geo.vtx()[1]); - EXPECT_EQ(openvdb::Vec3f(7,8,9), geo.vtx()[2]); - EXPECT_EQ(openvdb::Vec3f(10,11,12), geo.vtx()[3]); + EXPECT_EQ(openvdb::Vec3f(1,2,3), geo2.vtx()[0]); + EXPECT_EQ(openvdb::Vec3f(4,5,6), geo2.vtx()[1]); + EXPECT_EQ(openvdb::Vec3f(7,8,9), geo2.vtx()[2]); + EXPECT_EQ(openvdb::Vec3f(10,11,12), geo2.vtx()[3]); - EXPECT_EQ(openvdb::Vec3I(0,1,2), geo.tri()[0]); - EXPECT_EQ(openvdb::Vec3I(1,2,3), geo.tri()[1]); + EXPECT_EQ(openvdb::Vec3I(0,1,2), geo2.tri()[0]); + EXPECT_EQ(openvdb::Vec3I(1,2,3), geo2.tri()[1]); - EXPECT_EQ(openvdb::Vec4I(0,1,2,3), geo.quad()[0]); + EXPECT_EQ(openvdb::Vec4I(0,1,2,3), geo2.quad()[0]); } {// write to file std::ofstream os("data/test.geo", std::ios_base::binary); @@ -395,19 +441,62 @@ TEST_F(Test_vdb_tool, Geometry) EXPECT_EQ(4, geo2.vtxCount()); EXPECT_EQ(2, geo2.triCount()); EXPECT_EQ(1, geo2.quadCount()); - EXPECT_EQ(openvdb::Vec3f(1,2,3), geo.bbox().min()); - EXPECT_EQ(openvdb::Vec3f(10,11,12), geo.bbox().max()); + EXPECT_EQ(openvdb::Vec3f(1,2,3), geo2.bbox().min()); + EXPECT_EQ(openvdb::Vec3f(10,11,12), geo2.bbox().max()); - EXPECT_EQ(openvdb::Vec3f(1,2,3), geo.vtx()[0]); - EXPECT_EQ(openvdb::Vec3f(4,5,6), geo.vtx()[1]); - EXPECT_EQ(openvdb::Vec3f(7,8,9), geo.vtx()[2]); - EXPECT_EQ(openvdb::Vec3f(10,11,12), geo.vtx()[3]); + EXPECT_EQ(openvdb::Vec3f(1,2,3), geo2.vtx()[0]); + EXPECT_EQ(openvdb::Vec3f(4,5,6), geo2.vtx()[1]); + EXPECT_EQ(openvdb::Vec3f(7,8,9), geo2.vtx()[2]); + EXPECT_EQ(openvdb::Vec3f(10,11,12), geo2.vtx()[3]); - EXPECT_EQ(openvdb::Vec3I(0,1,2), geo.tri()[0]); - EXPECT_EQ(openvdb::Vec3I(1,2,3), geo.tri()[1]); + EXPECT_EQ(openvdb::Vec3I(0,1,2), geo2.tri()[0]); + EXPECT_EQ(openvdb::Vec3I(1,2,3), geo2.tri()[1]); - EXPECT_EQ(openvdb::Vec4I(0,1,2,3), geo.quad()[0]); + EXPECT_EQ(openvdb::Vec4I(0,1,2,3), geo2.quad()[0]); + } + {// test readOFF and writeOFF + geo.write("data/test.off"); + openvdb::vdb_tool::Geometry geo2; + geo2.read("data/test.off"); + EXPECT_EQ(4, geo2.vtxCount()); + EXPECT_EQ(2, geo2.triCount()); + EXPECT_EQ(1, geo2.quadCount()); + EXPECT_EQ(openvdb::Vec3f(1,2,3), geo2.bbox().min()); + EXPECT_EQ(openvdb::Vec3f(10,11,12), geo2.bbox().max()); + + EXPECT_EQ(openvdb::Vec3f(1,2,3), geo2.vtx()[0]); + EXPECT_EQ(openvdb::Vec3f(4,5,6), geo2.vtx()[1]); + EXPECT_EQ(openvdb::Vec3f(7,8,9), geo2.vtx()[2]); + EXPECT_EQ(openvdb::Vec3f(10,11,12), geo2.vtx()[3]); + + EXPECT_EQ(openvdb::Vec3I(0,1,2), geo2.tri()[0]); + EXPECT_EQ(openvdb::Vec3I(1,2,3), geo2.tri()[1]); + + EXPECT_EQ(openvdb::Vec4I(0,1,2,3), geo2.quad()[0]); + } + #ifdef VDB_TOOL_USE_PDAL + {// read from PDAL-supported ASCII format file + // (NOTE: PDAL also supports other formats e.g. LAS, LAZ, E57, Draco, FBX, NumPy, OBJ,…) + + // write a test file + std::ofstream os("data/test.txt"); + os << "X,Y,Z\n"; + for (size_t i=0; i and >= for class Coord according to lexicographical order. -Add toCodec to convert string to Codec enumeration type. -Add nanovdb::strlen(). -Add strncpy util. -Add NANOVDB_DISABLE_SYNC_CUDA_MALLOC that maps cudaMallocAsync and -cudaFreeAsync to cudaMalloc and cudaFree respectively. -Add guard to UINT64_C. -Remove use of cudaMallocAsync in PointsToGrid.cuh. -Align PNanoVDB blind metadata to NanoVDB. - -API Changes: -Change mapToGridType to toGridType. -Change mapToMagic to toMagic. -Change CpuTimer.h to Timer.h. - -API Changes (details): -These APIs are now under the math namespace: Ray, DDA, HDDA, Vec3, Vec4, BBox, -ZeroCrossing, TreeMarcher, PointTreeMarcher, BoxStencil, CurvatureStencil, -GradStencil, WenoStencil, AlignUp, Min, Max, Abs, Clamp, Sqrt, Sign, Maximum, -Delta, RoundDown, pi, isApproxZero, Round, createSampler, SampleFromVoxels. - -These APIs are now under the tools namespace: createNanoGrid, StatsMode, -createLevelSetSphere, createFogVolumeSphere, createFogVolumeSphere, -createFogVolumeSphere, createFogVolumeTorus, createLevelSetBox, CreateNanoGrid, -updateGridStats, evalChecksum, validateChecksum, checkGrid, Extrema. - -These APIs are now under the util namespace: is_floating_point, findLowestOn, -findHighestOn, Range, streq, strcpy, strcat, empty, Split, invoke, forEach, -reduce, prefixSum, is_same, is_specialization, PtrAdd, PtrDiff. - -Move nanovdb::build to nanovdb::tools::build. -Rename nanovdb::BBoxR to nanovdb::Vec3dBBox. -Rename nanovdb::BBox to nanovdb::Vec3dBbox. -Move nanovdb::cudaCreateNodeManager to nanovdb::cuda::createNodeManager. -Move and rename nanovdb::cudaVoxelsToGrid to nanovdb::cuda::voxelsToGrid. -Move and rename nanovdb::cudaPointsToGrid to nanovdb::cuda::pointsToGrid. -Move nanovdb::DitherLUT to nanovdb::math::DitherLUT. -Move and rename nanovdb::PackedRGBA8 to nanovdb::math::Rgba8. -Move nanovdb::Rgba8 to nanovdb::math::Rgba8. -Move and rename nanovdb::CpuTimer to nanovdb::util::Timer. -Move nanovdb::GpuTimer to nanovdb::util::cuda::Timer. -Move and rename nanovdb::CountOn to nanovdb::util::countOn. - -Move util/GridHandle.h to GridHandle.h. -Move util/BuildGrid.h to tools/GridBuilder.h. -Move util/GridBuilder.h to tools/GridBuilder.h. -Move util/IO.h to io/IO.h. -Move util/CSampleFromVoxels.h to math/CSampleFromVoxels.h. -Move util/DitherLUT.h to math/DitherLUT.h. -Move util/HDDA.h to math/HDDA.h. -Move util/Ray.h to math/Ray.h. -Move util/SampleFromVoxels.h to math/SampleFromVoxels.h. -Move util/Stencils.h to nanovdb/math/Stencils.h. -Move util/CreateNanoGrid.h to tools/CreateNanoGrid.h. -Move and rename util/Primitives.h to tools/CreatePrimitives.h. -Move util/GridChecksum.h to tools/GridChecksum.h. -Move util/GridStats.h to tools/GridStats.h. -Move util/GridChecksum.h to tools/GridChecksum.h. -Move util/GridValidator.h to tools/GridValidator.h. -Move util/NanoToOpenVDB.h to tools/NanoToOpenVDB.h. -Move util/cuda/CudaGridChecksum.cuh to tools/cuda/CudaGridChecksum.cuh. -Move util/cuda/CudaGridStats.cuh to tools/cuda/CudaGridStats.cuh. -Move util/cuda/CudaGridValidator.cuh to tools/cuda/CudaGridValidator.cuh. -Move util/cuda/CudaIndexToGrid.cuh to tools/cuda/CudaIndexToGrid.cuh. -Move and rename util/cuda/CudaPointsToGrid.cuh to tools/cuda/PointsToGrid.cuh. -Move util/cuda/CudaSignedFloodFill.cuh to tools/cuda/CudaSignedFloodFill.cuh. -Move and rename util/cuda/CudaDeviceBuffer.h to cuda/DeviceBuffer.h. -Move and rename util/cuda/CudaGridHandle.cuh to cuda/GridHandle.cuh. -Move and rename util/cuda/CudaUtils.h to util/cuda/Util.h. -Move and consolidate util/cuda/GpuTimer.h to util/cuda/Timer.h. diff --git a/pendingchanges/tsan_spheres.txt b/pendingchanges/tsan_spheres.txt deleted file mode 100644 index 150bdb7803..0000000000 --- a/pendingchanges/tsan_spheres.txt +++ /dev/null @@ -1,5 +0,0 @@ -OpenVDB: - Bug Fixes: - - Fixed a thread sanitizer issue which could cause undefined behaviour - in VolumeToSpheres::fillWithSpheres - [Reported by Jérémie Dumas] diff --git a/pendingchanges/ubsan.txt b/pendingchanges/ubsan.txt deleted file mode 100644 index f050fd29e0..0000000000 --- a/pendingchanges/ubsan.txt +++ /dev/null @@ -1,3 +0,0 @@ -OpenVDB: - - Bug Fixes:: - - Fixed an occurance of undefined behaviour in tools::activate (though this would typically not have manifested with any unintended behaviour) diff --git a/pendingchanges/value_accessors_tree.txt b/pendingchanges/value_accessors_tree.txt deleted file mode 100644 index 8076f7cd6c..0000000000 --- a/pendingchanges/value_accessors_tree.txt +++ /dev/null @@ -1,3 +0,0 @@ -OpenVDB: - Improvements: - - ValueAccessors are now defined and created in the Tree class instead of in the Grid class so that custom Tree implementations may define and create their own ValueAccessors if desired. diff --git a/pendingchanges/vdb_tool.txt b/pendingchanges/vdb_tool.txt new file mode 100644 index 0000000000..c68dc25834 --- /dev/null +++ b/pendingchanges/vdb_tool.txt @@ -0,0 +1 @@ +added read and write support for OFF (Object File Format) files to vdb_tool \ No newline at end of file diff --git a/pendingchanges/windows_static_blosc.txt b/pendingchanges/windows_static_blosc.txt deleted file mode 100644 index 882dc1f3fa..0000000000 --- a/pendingchanges/windows_static_blosc.txt +++ /dev/null @@ -1,2 +0,0 @@ -Build: - - Fixed an issue with the Blosc CMake FindPackage for the OpenVDB Windows static library. diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000000..b42bcd5c80 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,44 @@ +[build-system] +requires = ["scikit_build_core", "nanobind"] +build-backend = "scikit_build_core.build" + +[project] +name = "openvdb" +version = "12.0.1" +description= "Python bindings for OpenVDB: sparse volume data structure and tools." +dependencies = [ + "numpy", +] +authors = [ + { name = "OpenVDB Developer Team", email = "openvdb-dev@lists.aswf.io" }, +] +requires-python = ">=3.10" +classifiers = [ + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", +] + +[project.urls] +homepage = "https://www.openvdb.org/" +documentation = "https://www.openvdb.org/documentation" +forum = "https://github.com/AcademySoftwareFoundation/openvdb/discussions" +repository = "https://github.com/AcademySoftwareFoundation/openvdb" +slack = "https://slack.aswf.io/" + +[tool.scikit-build] +wheel.packages = [] + +[tool.scikit-build.cmake.define] +OPENVDB_CORE_STATIC="OFF" +USE_EXPLICIT_INSTANTIATION="OFF" +DISABLE_DEPENDENCY_VERSION_CHECKS="ON" +OPENVDB_USE_DELAYED_LOADING="OFF" +OPENVDB_BUILD_PYTHON_MODULE="ON" +USE_NUMPY="ON" + +USE_NANOVDB="ON" +NANOVDB_USE_CUDA="ON" +NANOVDB_BUILD_PYTHON_MODULE="ON" +NANOVDB_USE_OPENVDB="ON" + diff --git a/tsc/meetings/2024-09-24.md b/tsc/meetings/2024-09-24.md index bbb86694a3..02a4e06d4b 100644 --- a/tsc/meetings/2024-09-24.md +++ b/tsc/meetings/2024-09-24.md @@ -1,4 +1,4 @@ -Minutes from OpenVDB TSC meeting, March 12th, 2024 +Minutes from OpenVDB TSC meeting, September 9th, 2024 Attendees: *Ken* M., *Jeff* L., *Andre* P, *Dan* B., *Greg* H. diff --git a/tsc/meetings/2024-10-08.md b/tsc/meetings/2024-10-08.md new file mode 100644 index 0000000000..19a2582a38 --- /dev/null +++ b/tsc/meetings/2024-10-08.md @@ -0,0 +1,82 @@ +Minutes from OpenVDB TSC meeting, Octtober 8th, 2024 + +Attendees: *Ken* M., *Nick* A., *Jeff* L., *Andre* P, *Dan* B., *Greg* H. + +Regrets: *Rich* J. + +Additional Attendees: +John Mertic (Linux Foundation), Dhruv Govil (Apple), Mathew Cong (NVIDIA), +Jonathan Swartz (NVIDIA), JT Nelson (Blender), Victor Lu + +Agenda: + +1) Confirm quorum +2) Secretary +3) Re-licensing Progress +4) CI +5) Changing cpp test to gtest in AX +6) Deprecation Policy +7) PR-1916 - Nanobind PR +8) ABI and API Changes +9) Tube complex and thicken mesh +10) Next meeting + +------------ + +1) Confirm quorum + +Quorum is present. + +2) Secretary + +Secretary is Andre Pradhana. + +3) Re-licensing Progress + +NVIDIA has signed the CLA, but has not added their name to the list. ILM and +United Therapeutics have signed. + +The next step is to merge PR-1858 (which needs a second approval). Noted that +the PR probably needs to be updated to be more aligned with the master branch. +There was a suggestion to change the target of the PR to a feature branch to +bring everything up to date before merging to master. + +4) CI + +There are a few CI issues, e.g. with Houdini 20.5 and AX unit tests failing due +to LLVM version issues. + +5) Changing cpp test to gtest in AX + +Tim is looking at changing cpp test to unit test in +[PR-1919](https://github.com/AcademySoftwareFoundation/openvdb/pull/1919). + +6) Deprecation Policy + +Currently, we do not change the ABI until after the major release, i.e. we used +to add an extra minor release. + +We took a vote (six people agreed, one abstains) and agreed to drop ABI support +at the same time as a major release. Our deprecation policy is to support the +current and last two versions of the VFX reference platform. + +7) PR-1916 - Nanobind PR + +This PR is to switch from pybind11 to NanoBind. Andre agreed to take a look at +the PR. + +8) ABI and API Changes + +Dan went through a few suggestions: + - Root Node and Internal Nodes API changes: adding public methods to check if a + key exists in map and adding index-based iteration on internal nodes. + - Adding unsafe methods to bypass checks. It was suggested to add new methods + without unsafe labels. + +9) Tube complex and thicken mesh + +Greg will have a PR ready for creating tube complexes and thicken mesh. + +10) Next meeting + +Next meeting is on October 22nd, 2024. 2pm-3pm EDT (GMT-4). diff --git a/tsc/meetings/2024-10-22.md b/tsc/meetings/2024-10-22.md new file mode 100644 index 0000000000..c405ee6982 --- /dev/null +++ b/tsc/meetings/2024-10-22.md @@ -0,0 +1,151 @@ +Minutes from OpenVDB TSC meeting, October 22nd, 2024 + +Attendees: *Jeff* L., *Ken* M., *Andre* P., *Dan* B., *Nick* A., *Greg* H. + +Regrets: *Rich* J. + +Additional Attendees: +John Mertic (Linux Foundation), J. T. Nelson (Blender), Dhruv Govil (Apple), +Jonathan Swartz (NVIDIA), Matthew Cong (NVIDIA), Francis Williams (NVIDIA) + +Agenda: + +1) Confirm quorum +2) Secretary +3) Re-licensing to Apache and CLA/CCLA +4) CI +5) Drop ABI-9 support +6) PR-1916 +7) PR-1931 +8) PR-1936 +9) PR-1938 +10) PR-1939 +11) Half Grid Support PR +12) PR-1793 - Fillet in LevelSetFilter +13) PR-1794 +14) Pip Install OpenVDB +15) PR-1935 - Create Tubes and Thickened Mesh +16) Next Meeting + +------------ + +1) Confirm quorum + +Quorum is present. + +2) Secretary + +Secretary is Andre Pradhana. + +3) Re-licensing to Apache and CLA/CCLA + +The relicensing PR is merged. New contributions need to be licensed under +Apache, not SPDX. + +Today the following companies have signed for the new CLA: United Therapeutics, +Lucas Film, SideFX, and NVIDIA. Weta is in the process. Apple does individual +CLA. + +The process is that the old CLA will be taken out, documented for historic +purposes, and insert the new CLAs. New contributors need to sign the new CLA. +John Mertic needed approval from TSC members. + +We had concern regarding TSC members who cannot contribute code because they are +blocked by CLA approval. We voted if we needed to delay CLA update. Five people +agree to go through with updating the CLA and relicensing for VDB 12. John got +the approval. + +4) CI + +Dan fixed Windows CI. + +Weekly extra build has been upgraded to 2024 images now which uses LLVM 17. AX +was removed from this CI because of incompatibility. + +NanoVDB CI is updated to use 2024 OS Image. Andre looked at it and approved. + +5) Drop ABI-9 support + +We are dropping ABI-9 support for VDB-12. This means we are now dropping C++11 +and Python 3.7. + +Most notably, this also changes the old deprecation policy so that we do not +need to maintain an older version of the ABI for an extra minor release. + +6) [PR-1916](https://github.com/AcademySoftwareFoundation/openvdb/pull/1916) + +Nanobind support for OpenVDB and NanoVDB has the correct approvals. Dan merged +it. + +7) [PR-1931](https://github.com/AcademySoftwareFoundation/openvdb/pull/1931) + +A few methods were made to be public (e.g. haskey()). It’s suggested to make +some of the APIs to be aligned with what’s in the Tree API. + +Gave a motivation on the reason to add an API called deleteChildOrChild, which +gives one the ability to delete a child or coordinate that has a given +coordinate. A potential problem with the implementation detail of deleting an +element of a map is a case where one iterates through a map and deletes an entry +of the map, which invalidates the iterator itself. Needs good documentation, +e.g. calling this can invalidate child iterators. + +There is a bug in RooNode setOrigin. + +Ken will take a look. + +8) [PR-1936](https://github.com/AcademySoftwareFoundation/openvdb/pull/1936) + +Added const version of probeNode (for internal and root nodes) that didn’t exist +before. The API allows one to give the node type you want to probe and probe the +level below. + +The internal node can probe based on a child, but also overloads to probe with a +child, a value, and an activate state. + +The APIs have assert. + +Ken will take a look. + +9) [PR-1938](https://github.com/AcademySoftwareFoundation/openvdb/pull/1938) + +Discussion about adding unsafe methods, e.g. getValueUnsafe, getChildUnsafe. + +There was a concern: if a user deletes the word unsafe from the API call, will +the code still work? The answer is it depends on the implementation. + +Assert are added into these methods, so debug build should help with debugging +with a debug build. + +10) [PR-1939](https://github.com/AcademySoftwareFoundation/openvdb/pull/1939) + +Added a more efficient implementation in RootNode constructor by calling emplace +method in the table. + +Ken will take a look. + +11) Half Grid Support PR + +Andre and Greg will meet. We will create a feature/half_grid_branch and work +from there. + +12) [PR-1793](https://github.com/AcademySoftwareFoundation/openvdb/pull/1793) - Fillet in LevelSetFilter + +This PR is for VDB-12. + +13) [PR-1794](https://github.com/AcademySoftwareFoundation/openvdb/pull/1794) + +This PR is for VDB-12. + +14) Pip Install OpenVDB + +Matthew will try to get pip install for Python for VDB-12. + +15) [PR-1935](https://github.com/AcademySoftwareFoundation/openvdb/pull/1935) - Create Tubes and Thickened Mesh + +Discussed replacing the word thickened to dilate. Another suggestion along the +line of the term “alpha shape”. + +16) Next Meeting + +Next meeting is on October 29th, 2024. 2pm-3pm EDT (GMT-4). + diff --git a/tsc/meetings/2024-10-29.md b/tsc/meetings/2024-10-29.md new file mode 100644 index 0000000000..98a157f6f8 --- /dev/null +++ b/tsc/meetings/2024-10-29.md @@ -0,0 +1,140 @@ +Minutes from OpenVDB TSC meeting, October 29th, 2024 + +Attendees: *Ken* M., *Jeff* L., *Andre* P, *Dan* B., *Greg* H, *Nick* A. + +Additional Attendees: Dhruv Govil (Apple), Mathew Cong (NVIDIA), +Jonathan Swartz (NVIDIA) + +Regrets: *Richard* J. + +Agenda: + +1) Confirm quorum +2) Secretary +3) Meeting Times +4) OpenVDB 12 PRs +5) 12 Release +6) Next Meeting + +------------ + +1) Confirm quorum + +Quorum is present. + +2) Secretary + +Secretary is Jeff Lait. + +3) Meeting Times + +Do we alternate meeting times to get better universal times? + +We've lacked agenda so it is unclear if you need to show up or not. + +We need to be better at uploading meeting notes. + +In the winter, 8am works in NZ. We should figure out a better schedule for the summer. Either a new day or alternate days. + +We are able to sync and update the calendar now! + +We should start the call for agenda items again. + +Next weeks meeting we should each know what days don't work for this +time slot, so we can see if a non-Tuesday will work. + +4) OpenVDB 12 PRs + + +a) PR1936, 1938, 1939 + +Feedback addressed, ready to go. + +b) 1951 + +Awaiting on NVidia CLA. + +c) 1941 + +Awaiting on NVidia CLA. + +SKBuild is a bit of a mess. This is from the featurevdb vs nanovdb branches. Ideally only on SKBuild test? + +d) 1952 + +Need to sudo make install. Cleaned up old code. Now falls back +rather than uses complicated logic. + +Approved and ready. + +e) 1954 + +Newest container complains about undefined behaviour, we should fix +eventually, but turn off so it passes. We used to use a macro for +this, but it is believed __attribute__ is now supported by GCC. + +Nick will investigate, might be failing on Windows. + +f) 1794 + +Prevent integer overflow. + +This is an ABI change. Virtual functions now return different things. +Return type doesn't necessarily affect the mangle, but might affect the layout. + +We add a nodeCount(std::vector) rather than replace, and +deprecate the Index32 in new ABI. + +The version that returns std::vector needs both versions +switched according to the ABI type. + +We should also switch nonLeafCount at the same time rather than +waiting for next 2^32 overflow. + +g) 1935 + +Tube complex PR. + +Needs more unit tests. ConvexVoxelizer uses GridT, but things are +cast to float internally? Is this a float only setup? Should there be +static_asserts to early exit? This was originally float only and +tried to make generic. Passes shared pointer to grid, but holds +reference to a tree - should keep shared pointer to grid keep +lifetime guarantees. + +Should this all be put into one header? But these are separate functions. + +Should ConvexVoxelizer be a private namespace? Similar to Points api? +Ideally the user's entry ponit is a nicely documented header file. +Convex Voxelizer is meant to be a starting point for new types. An +example for a level set sphere test is provided. + +This should have its own unit test rather than adding to test tools. + +This can wait for VDB 12.1. + +h) Half grid feature branch + +Vec3h - Vec3h doesn't work.... Still failing unit tests. + +Not including means that .vdb files can't use this natively, as VDB12 +compliant DCC will not necessarily have this type registered. + +Requires too much for VDB12 release. + +We need a placeholder PR so we remember to finish this. + +i) 1821 + +PDAL support. Missing unit tests. Handles LIDAR style point formats. +Will be delayed until unit tests, can go in VDB12.1. + +5) Release + +Dan will do the release. PRs need to be merged by the 30th. + +6) Next Meeting + +Agenda: Dilation. New TSC meeting day + +Next Meeting 11-05-2024 at the same time. diff --git a/tsc/meetings/2024-11-05.md b/tsc/meetings/2024-11-05.md new file mode 100644 index 0000000000..3ad3b8c5e4 --- /dev/null +++ b/tsc/meetings/2024-11-05.md @@ -0,0 +1,100 @@ +Minutes from OpenVDB TSC meeting, November 5th, 2024 + +Attendees: *Ken* M., *Andre* P, *Dan* B., *Greg* H, *Nick* A. + +Additional Attendees: Jonathan Swartz (NVIDIA), Barry Dempsey + +Regrets: *Jeff* L., *Richard* J. + +Agenda: + +1) Confirm quorum +2) Secretary +3) V12 post mortem +4) 12.1 release +5) File format updates +6) Dilation +7) Next meeting + +------------ + +1) Confirm quorum + +Quorum is present. + +2) Secretary + +Secretary is Greg Hurst. + +3) V12 post mortem + +Release wasn't too bad -- getting CI in order was a bit of work but started a bit early. + +Having a X.1, X.2, etc. release there would be less of a squeeze for new features. + +Ken: perhaps 3 releases per year, including the major release. + +Nick: Would be nice if we could be in a place where patch releases are simple whenever there's a new feature. This means we can't put anything into master unless we're completely happy with it. We also need to make sure that the weekly tests are always in working order and when a failure happens, we need to address ASAP. + +CI could be more complicated once we start adding GPU's into the mix. + +Linux & Mac OS migrations have happenend and switching from bash to shell in Windows had made more stable. + +Chat groups can be much easier to manage the CI compared to emails, which we currently operate like. Perhaps Slack or Google chat? + +Ken: Could Nick add documentation or a presentation about the current CI? It's complex and a bit intimidating right now. + +Porting AX tests to GTest from CPPTest can help a bit with CI. + +4) 12.1 release + +March 1, 2025 release? + +* Tubes + Dilated Meshes +* HalfGrid +* Large Nano updates + +5) File format updates + +Migrate away from Blosc in favor from LZ4? + +Blosc brings a lot of compression codecs with it, so we could precondition ourselves and then just bring in LZ4. + +6) Dilation + +Dan presents on dilation -- noticed serial dilation was faster when making volume advection calls + +Active ideas of how we can speed up the multithreaded code. + +Vague description: + +Current: dilate into multiple grids then merge +Future: create 1 grid and dilate into it, parallelize over internal nodes directly above leaf nodes + +1. Partial Topology Copy, turn dense leaf nodes into active tiles +2. Node dilation, iterate over leafs and touch leaf neighbors +3. Mask dilation, scatter method but make sure you're not writing to the same leaf node at the same time. Split into center, face, edge, and corner passes, so 4 passes instead of 7. Still does scattering over a copy of the data since multiple passes are used. +4. Prune tree + +Tentatively a ~4-5x speedup for 'dense-like' grids and ~2x speedup for higher SA/V grids. + +TODOs +* Topology Copy + * Skip Topology Copy and Re-use Input Topology +* Node Dilation + * Apply Center, Face, Edge, Corner Scheme + * Avoid Naive Root Children Dilation +* Mask Dilation + * Cache Neighboring Internal Nodes + * Thread Corner Cases + * Simplify Edge Cases + * Extend to NN_FACE_EDGE/NN_FACE_EDGE_VERTEX modes +* Testing + * Build Worst Case and Poorly Balanced Trees + * Multiple Iterations + +7) Next meeting + +Tentativley we'd like to move meetings to Wednesdays at 11:00 PST if we get a buy in from Jeff and Rich. + +If this is the case, the next meeting is Wednesday Novermber 20, 2024 at 11:00 PST. diff --git a/tsc/meetings/2024-12-03.md b/tsc/meetings/2024-12-03.md new file mode 100644 index 0000000000..e864d84bdd --- /dev/null +++ b/tsc/meetings/2024-12-03.md @@ -0,0 +1,80 @@ +Minutes from OpenVDB TSC meeting, December 3rd, 2024 + +Attendees: *Ken* M., *Andre* P, *Dan* B. + +Additional Attendees: Jonathan Swartz (NVIDIA), Dhruv Govil (Apple) + +Regrets: *Jeff* L., *Richard* J., *Nick* A., *Greg* H. + +Agenda: + +1) Confirm quorum +2) Secretary +3) PR1971 +4) Binary Distribution +5) PR1977 +6) Fluid Example +7) TBB Threading +8) ASWF Code Build +9) Next meeting + +------------ + +1) Confirm quorum + +No quorum. + +2) Secretary + +Secretary is Dan Bailey. + +3) PR1971 + +A bug was discovered with vdb_tool that wasn't caught with CI. This has now been +addressed and the CI extended to test this case. + +4) Binary Distribution + +Brief discussion about binary distribution. Still want to support package +manager maintainers, but hesitant to take this on ourselves. A bigger ROI might +be to focus our efforts on migrating towards CMake config files to make using +OpenVDB as a dependency easier. The autogenerated version.h is a step in that +direction, but could be improved with CMake configs. Other ASWF projects such +as OpenEXR have successfully gone in this direction. + +Anaconda integration has been attempted, but is challenging as it only supports +a single library per module by default. + +5) PR1977 + +Dhruv has submitted this PR to address issues with compiling OpenVDB using Clang +19. This new version is stricter and is catching a few valid issues. + +6) Fluid Example + +All would like this to go in. Andre says that it is blocked on an issue with the +Poisson solver which needs to be resolved. Also, the OpenVDB Remove Divergence +SOP has a bug in how the matrices are initialized that needs to be addressed. + +There is a new NanoVDB poisson solver which is of interest to the group. All +agreed that it would potentially be a good fit for NanoVDB. Dan would also +like an example of how to call it directly from OpenVDB. + +7) TBB Threading + +Question about our use of TBB (#1973). Previously discussed making TBB optional +by wrapping all the data structures and function calls in +openvdb/util/Threading.h. This could provide a route to running +single-threading or providing another threading implementation like Apple's +Grand Central Dispatch. A lot of work needed and not clear how general this +approach would be. + +8) ASWF Code Build + +Jonathan to reach out to CI working group to try and move this forward to get +GPU runtime testing. Ken can escalate to TSC if need be. + +9) Next meeting + +Next meeting is Tuesday December 17, 2024 at 11:00 PST. This will be the last +meeting of the year. \ No newline at end of file diff --git a/tsc/process/deprecation.md b/tsc/process/deprecation.md index bf54ece78d..3ed9a307ca 100644 --- a/tsc/process/deprecation.md +++ b/tsc/process/deprecation.md @@ -5,7 +5,7 @@ OpenVDB is committed to supporting three years of Houdini and Maya based on those versions of the platform. The latest supported year is that in which the VDB version listed matches the major version. -For example, version 6.1.0 of OpenVDB supports VFX Reference Platform years +For example, all 6.x versions of OpenVDB support VFX Reference Platform years 2019, 2018 and 2017. This infers the following support: @@ -14,9 +14,5 @@ This infers the following support: * C++11 and C++14 * Houdini 16.5, 17.0 and 17.5 -When version 7.0.0 is released, OpenVDB will support VFX Reference Platform -years 2020, 2019 and 2018. Support for Houdini 16.5 and C++11 will be dropped. - -Support for obsolete ABIs will not be dropped until the first minor release -after the introduction of a new ABI. For example, the latest version to retain -support for ABI=4 will be the release prior to 7.1.0. +All 7.x versions of OpenVDB support VFX Reference Platform years 2020, 2019 and +2018. Support for Houdini 16.5, C++11 and ABI=4 is removed.