Blender V5.0
nanovdb.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: Contributors to the OpenVDB Project
2 * SPDX-FileCopyrightText: 2025 Blender Foundation
3 *
4 * SPDX-License-Identifier: Apache-2.0 */
5
6#ifdef WITH_NANOVDB
7
8# include "util/nanovdb.h"
9# include "util/log.h"
10# include "util/openvdb.h"
11
12# include <openvdb/tools/Activate.h>
13
14# include <nanovdb/util/ForEach.h>
15
16# if NANOVDB_MAJOR_VERSION_NUMBER > 32 || \
17 (NANOVDB_MAJOR_VERSION_NUMBER == 32 && NANOVDB_MINOR_VERSION_NUMBER >= 7)
18# include <nanovdb/tools/CreateNanoGrid.h>
19# else
20# include <nanovdb/util/OpenToNanoVDB.h>
21# endif
22
24
25/* Convert NanoVDB to OpenVDB mask grid.
26 *
27 * Implementation adapted from nanoToOpenVDB in nanovdb, this will create
28 * a MaskGrid from any type of grid using the active voxels. */
29
30template<typename NanoBuildT> class NanoToOpenVDBMask {
31 using NanoNode0 = nanovdb::LeafNode<NanoBuildT, openvdb::Coord, openvdb::util::NodeMask>;
32 using NanoNode1 = nanovdb::InternalNode<NanoNode0>;
33 using NanoNode2 = nanovdb::InternalNode<NanoNode1>;
34 using NanoRootT = nanovdb::RootNode<NanoNode2>;
35 using NanoTreeT = nanovdb::Tree<NanoRootT>;
36 using NanoGridT = nanovdb::Grid<NanoTreeT>;
37 using NanoValueT = typename NanoGridT::ValueType;
38
39 using OpenBuildT = openvdb::ValueMask;
40 using OpenNode0 = openvdb::tree::LeafNode<OpenBuildT, NanoNode0::LOG2DIM>;
41 using OpenNode1 = openvdb::tree::InternalNode<OpenNode0, NanoNode1::LOG2DIM>;
42 using OpenNode2 = openvdb::tree::InternalNode<OpenNode1, NanoNode2::LOG2DIM>;
43 using OpenRootT = openvdb::tree::RootNode<OpenNode2>;
44 using OpenTreeT = openvdb::tree::Tree<OpenRootT>;
45 using OpenGridT = openvdb::Grid<OpenTreeT>;
46 using OpenValueT = typename OpenGridT::ValueType;
47
48 public:
49 NanoToOpenVDBMask() = default;
50 typename OpenGridT::Ptr operator()(const nanovdb::NanoGrid<NanoBuildT> &grid = 0);
51
52 private:
53 template<typename NanoNodeT, typename OpenNodeT>
54 OpenNodeT *processNode(const NanoNodeT * /*node*/);
55
56 OpenNode2 *process(const NanoNode2 *node)
57 {
58 return this->template processNode<NanoNode2, OpenNode2>(node);
59 }
60 OpenNode1 *process(const NanoNode1 *node)
61 {
62 return this->template processNode<NanoNode1, OpenNode1>(node);
63 }
64
65 template<typename NanoLeafT> OpenNode0 *process(const NanoLeafT *node);
66};
67
68template<typename NanoBuildT>
69typename NanoToOpenVDBMask<NanoBuildT>::OpenGridT::Ptr NanoToOpenVDBMask<NanoBuildT>::operator()(
71{
72 /* Since the input nanovdb grid might use nanovdb types (Coord, Mask, Vec3)
73 * we cast to use openvdb types. */
74 const NanoGridT *srcGrid = reinterpret_cast<const NanoGridT *>(&grid);
75 auto dstGrid = openvdb::createGrid<OpenGridT>(OpenValueT());
76
77 /* Set transform. */
78 const nanovdb::Map &nanoMap = reinterpret_cast<const nanovdb::GridData *>(srcGrid)->mMap;
79 auto mat = openvdb::math::Mat4<double>::identity();
80 mat.setMat3(openvdb::math::Mat3<double>(nanoMap.mMatD));
81 mat = mat.transpose(); /* The 3x3 in nanovdb is transposed relative to openvdb's 3x3. */
82 mat.setTranslation(openvdb::math::Vec3<double>(nanoMap.mVecD));
83 dstGrid->setTransform(openvdb::math::Transform::createLinearTransform(mat));
84
85 /* Process root node. */
86 auto &root = dstGrid->tree().root();
87 auto *data = srcGrid->tree().root().data();
88 for (uint32_t i = 0; i < data->mTableSize; ++i) {
89 auto *tile = data->tile(i);
90 if (tile->isChild()) {
91 root.addChild(this->process(data->getChild(tile)));
92 }
93 else {
94 root.addTile(tile->origin(), OpenValueT(), tile->state);
95 }
96 }
97
98 return dstGrid;
99}
100
101template<typename T>
102template<typename SrcNodeT, typename DstNodeT>
103DstNodeT *NanoToOpenVDBMask<T>::processNode(const SrcNodeT *srcNode)
104{
105 DstNodeT *dstNode = new DstNodeT(); /* Un-initialized for fast construction. */
106 dstNode->setOrigin(srcNode->origin());
107 const auto &childMask = srcNode->childMask();
108 const_cast<typename DstNodeT::NodeMaskType &>(dstNode->getValueMask()) = srcNode->valueMask();
109 const_cast<typename DstNodeT::NodeMaskType &>(dstNode->getChildMask()) = childMask;
110 auto *dstTable = const_cast<typename DstNodeT::UnionType *>(dstNode->getTable());
111 auto *srcData = srcNode->data();
112 std::vector<std::pair<uint32_t, const typename SrcNodeT::ChildNodeType *>> childNodes;
113 const auto childCount = childMask.countOn();
114 childNodes.reserve(childCount);
115 for (uint32_t n = 0; n < DstNodeT::NUM_VALUES; ++n) {
116 if (childMask.isOn(n)) {
117 childNodes.emplace_back(n, srcData->getChild(n));
118 }
119 }
120 auto kernel = [&](const auto &r) {
121 for (auto i = r.begin(); i != r.end(); ++i) {
122 auto &p = childNodes[i];
123 dstTable[p.first].setChild(this->process(p.second));
124 }
125 };
126
127# if NANOVDB_MAJOR_VERSION_NUMBER > 32 || \
128 (NANOVDB_MAJOR_VERSION_NUMBER == 32 && NANOVDB_MINOR_VERSION_NUMBER >= 7)
129 nanovdb::util::forEach(0, childCount, 1, kernel);
130# else
131 nanovdb::forEach(0, childCount, 1, kernel);
132# endif
133
134 return dstNode;
135}
136
137template<typename T>
138template<typename NanoLeafT>
139inline typename NanoToOpenVDBMask<T>::OpenNode0 *NanoToOpenVDBMask<T>::process(
140 const NanoLeafT *srcNode)
141{
142 static_assert(std::is_same_v<NanoLeafT, NanoNode0>, "NanoToOpenVDBMask wrong leaf type");
143 OpenNode0 *dstNode = new OpenNode0(); /* Un-initialized for fast construction. */
144 dstNode->setOrigin(srcNode->origin());
145 dstNode->setValueMask(srcNode->valueMask());
146
147 return dstNode;
148}
149
150struct NanoToOpenVDBMaskOp {
151 openvdb::MaskGrid::Ptr mask_grid;
152
153 template<typename NanoBuildT> bool operator()(const nanovdb::NanoGrid<NanoBuildT> &grid)
154 {
155 NanoToOpenVDBMask<NanoBuildT> tmp;
156 mask_grid = tmp(grid);
157 return true;
158 }
159};
160
161template<typename OpType>
162bool nanovdb_grid_type_operation(const nanovdb::GridHandle<> &handle, OpType &&op)
163{
164 const int n = 0;
165
166 if (const auto *grid = handle.template grid<float>(n)) {
167 return op(*grid);
168 }
169 if (const auto *grid = handle.template grid<nanovdb::Fp16>(n)) {
170 return op(*grid);
171 }
172 if (const auto *grid = handle.template grid<nanovdb::FpN>(n)) {
173 return op(*grid);
174 }
175 if (const auto *grid = handle.template grid<nanovdb::Vec3f>(n)) {
176 return op(*grid);
177 }
178 if (const auto *grid = handle.template grid<nanovdb::Vec4f>(n)) {
179 return op(*grid);
180 }
181
182 assert(!"Unknown NanoVDB grid type");
183 return true;
184}
185
186openvdb::MaskGrid::Ptr nanovdb_to_openvdb_mask(const nanovdb::GridHandle<> &handle)
187{
188 NanoToOpenVDBMaskOp op;
189 nanovdb_grid_type_operation(handle, op);
190 return op.mask_grid;
191}
192
193/* Convert OpenVDB to NanoVDB grid. */
194
195struct ToNanoOp {
196 nanovdb::GridHandle<> nanogrid;
197 int precision = 16;
198 float clipping = 0.0f;
199
200 template<typename GridType, typename FloatDataType, const int channels>
201 bool operator()(const typename GridType::ConstPtr &grid)
202 {
203 if constexpr (std::is_same_v<GridType, openvdb::MaskGrid>) {
204 return false;
205 }
206
207 try {
208# if NANOVDB_MAJOR_VERSION_NUMBER > 32 || \
209 (NANOVDB_MAJOR_VERSION_NUMBER == 32 && NANOVDB_MINOR_VERSION_NUMBER >= 6)
210# if NANOVDB_MAJOR_VERSION_NUMBER > 32 || \
211 (NANOVDB_MAJOR_VERSION_NUMBER == 32 && NANOVDB_MINOR_VERSION_NUMBER >= 7)
212 /* OpenVDB 12. */
213 using nanovdb::tools::createNanoGrid;
214 using nanovdb::tools::StatsMode;
215# else
216 /* OpenVDB 11. */
217 using nanovdb::createNanoGrid;
218 using nanovdb::StatsMode;
219# endif
220
221 if constexpr (std::is_same_v<GridType, openvdb::FloatGrid>) {
222 typename GridType::ConstPtr floatgrid = apply_clipping<GridType>(grid);
223 if (precision == 0) {
224 nanogrid = createNanoGrid<openvdb::FloatGrid, nanovdb::FpN>(*floatgrid,
225 StatsMode::Disable);
226 }
227 else if (precision == 16) {
228 nanogrid = createNanoGrid<openvdb::FloatGrid, nanovdb::Fp16>(*floatgrid,
229 StatsMode::Disable);
230 }
231 else {
232 nanogrid = createNanoGrid<openvdb::FloatGrid, float>(*floatgrid, StatsMode::Disable);
233 }
234 }
235 else if constexpr (std::is_same_v<GridType, openvdb::Vec3fGrid>) {
236 /* Enable stats for velocity grid. Weak, but there seems to be no simple iterator over all
237 * values in the grid? */
238 typename GridType::ConstPtr floatgrid = apply_clipping<GridType>(grid);
239 nanogrid = createNanoGrid<openvdb::Vec3fGrid, nanovdb::Vec3f>(*floatgrid,
240 StatsMode::MinMax);
241 }
242 else if constexpr (std::is_same_v<GridType, openvdb::Vec4fGrid>) {
243 typename GridType::ConstPtr floatgrid = apply_clipping<GridType>(grid);
244 nanogrid = createNanoGrid<openvdb::Vec4fGrid, nanovdb::Vec4f>(*floatgrid,
245 StatsMode::Disable);
246 }
247# else
248 /* OpenVDB 10. */
249 if constexpr (std::is_same_v<GridType, openvdb::FloatGrid>) {
250 typename GridType::ConstPtr floatgrid = apply_clipping<GridType>(grid);
251 if (precision == 0) {
252 nanogrid = nanovdb::openToNanoVDB<nanovdb::HostBuffer, openvdb::FloatTree, nanovdb::FpN>(
253 *floatgrid);
254 }
255 else if (precision == 16) {
256 nanogrid =
257 nanovdb::openToNanoVDB<nanovdb::HostBuffer, openvdb::FloatTree, nanovdb::Fp16>(
258 *floatgrid);
259 }
260 else {
261 nanogrid = nanovdb::openToNanoVDB(*floatgrid);
262 }
263 }
264 else if constexpr (std::is_same_v<FloatGridType, openvdb::Vec3fGrid>) {
265 typename GridType::ConstPtr floatgrid = apply_clipping<GridType>(grid);
266 nanogrid = nanovdb::openToNanoVDB(*floatgrid);
267 }
268 else if constexpr (std::is_same_v<FloatGridType, openvdb::Vec4fGrid>) {
269 typename GridType::ConstPtr floatgrid = apply_clipping<GridType>(grid);
270 nanogrid = nanovdb::openToNanoVDB(*floatgrid);
271 }
272# endif
273 }
274 catch (const std::exception &e) {
275 LOG_ERROR << "Error converting OpenVDB to NanoVDB grid: " << e.what();
276 }
277 catch (...) {
278 LOG_ERROR << "Error converting OpenVDB to NanoVDB grid: Unknown error";
279 }
280 return true;
281 }
282
283 template<typename GridT>
284 typename GridT::ConstPtr apply_clipping(const typename GridT::ConstPtr &grid) const
285 {
286 if (clipping == 0.0f) {
287 return grid;
288 }
289
290 /* TODO: Ideally would apply clipping during createNanoGrid, this seems slow. */
291 typename GridT::Ptr newgrid = grid->deepCopy();
292 openvdb::tools::deactivate(
293 *newgrid, typename GridT::ValueType(0.0f), typename GridT::ValueType(clipping));
294 return newgrid;
295 }
296};
297
298nanovdb::GridHandle<> openvdb_to_nanovdb(const openvdb::GridBase::ConstPtr &grid,
299 const int precision,
300 const float clipping)
301{
302 ToNanoOp op;
303 op.precision = precision;
304 op.clipping = clipping;
305 openvdb_grid_type_operation(grid, op);
306 return std::move(op.nanogrid);
307}
308
310
311#endif
BMesh const char void * data
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
void process(btMatrix3x3 &B, btMatrix3x3 &U, btVector3 &sigma, btMatrix3x3 &V)
Helper function of 3X3 SVD for processing 2X2 SVD.
SIMD_FORCE_INLINE btVector3 operator()(const btVector3 &x) const
Return the transform of the vector.
Definition btTransform.h:90
#define CCL_NAMESPACE_END
#define assert(assertion)
const ccl_global KernelWorkTile * tile
#define LOG_ERROR
Definition log.h:101
Grid< NanoTree< BuildT > > NanoGrid
i
Definition text_draw.cc:230