Blender V4.3
volume_grid.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Foundation
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BKE_volume_grid.hh"
7
9#include "BLI_task.hh"
10
11#ifdef WITH_OPENVDB
12# include <openvdb/Grid.h>
13#endif
14
16
17#ifdef WITH_OPENVDB
18
19VolumeGridData::VolumeGridData()
20{
21 tree_access_token_ = std::make_shared<AccessToken>(*this);
22}
23
24struct CreateGridOp {
25 template<typename GridT> openvdb::GridBase::Ptr operator()() const
26 {
27 return GridT::create();
28 }
29};
30
31static openvdb::GridBase::Ptr create_grid_for_type(const VolumeGridType grid_type)
32{
33 return BKE_volume_grid_type_operation(grid_type, CreateGridOp{});
34}
35
36VolumeGridData::VolumeGridData(const VolumeGridType grid_type)
37 : VolumeGridData(create_grid_for_type(grid_type))
38{
39}
40
41VolumeGridData::VolumeGridData(std::shared_ptr<openvdb::GridBase> grid)
42 : grid_(std::move(grid)), tree_loaded_(true), transform_loaded_(true), meta_data_loaded_(true)
43{
44 BLI_assert(grid_);
45 BLI_assert(grid_.use_count() == 1);
46 BLI_assert(grid_->isTreeUnique());
47
48 tree_sharing_info_ = OpenvdbTreeSharingInfo::make(grid_->baseTreePtr());
49 tree_access_token_ = std::make_shared<AccessToken>(*this);
50}
51
52VolumeGridData::VolumeGridData(std::function<LazyLoadedGrid()> lazy_load_grid,
53 std::shared_ptr<openvdb::GridBase> meta_data_and_transform_grid)
54 : grid_(std::move(meta_data_and_transform_grid)), lazy_load_grid_(std::move(lazy_load_grid))
55{
56 if (grid_) {
57 transform_loaded_ = true;
58 meta_data_loaded_ = true;
59 }
60 tree_access_token_ = std::make_shared<AccessToken>(*this);
61}
62
63VolumeGridData::~VolumeGridData() = default;
64
65void VolumeGridData::delete_self()
66{
67 MEM_delete(this);
68}
69
70const openvdb::GridBase &VolumeGridData::grid(VolumeTreeAccessToken &r_token) const
71{
72 return *this->grid_ptr(r_token);
73}
74
75openvdb::GridBase &VolumeGridData::grid_for_write(VolumeTreeAccessToken &r_token)
76{
77 return *this->grid_ptr_for_write(r_token);
78}
79
80std::shared_ptr<const openvdb::GridBase> VolumeGridData::grid_ptr(
81 VolumeTreeAccessToken &r_token) const
82{
83 std::lock_guard lock{mutex_};
84 this->ensure_grid_loaded();
85 r_token.token_ = tree_access_token_;
86 return grid_;
87}
88
89std::shared_ptr<openvdb::GridBase> VolumeGridData::grid_ptr_for_write(
90 VolumeTreeAccessToken &r_token)
91{
92 BLI_assert(this->is_mutable());
93 std::lock_guard lock{mutex_};
94 this->ensure_grid_loaded();
95 r_token.token_ = tree_access_token_;
96 if (tree_sharing_info_->is_mutable()) {
97 tree_sharing_info_->tag_ensured_mutable();
98 }
99 else {
100 auto tree_copy = grid_->baseTree().copy();
101 grid_->setTree(tree_copy);
102 tree_sharing_info_ = OpenvdbTreeSharingInfo::make(std::move(tree_copy));
103 }
104 /* Can't reload the grid anymore if it has been changed. */
105 lazy_load_grid_ = {};
106 return grid_;
107}
108
109const openvdb::math::Transform &VolumeGridData::transform() const
110{
111 std::lock_guard lock{mutex_};
112 if (!transform_loaded_) {
113 this->ensure_grid_loaded();
114 }
115 return grid_->transform();
116}
117
118openvdb::math::Transform &VolumeGridData::transform_for_write()
119{
120 BLI_assert(this->is_mutable());
121 std::lock_guard lock{mutex_};
122 if (!transform_loaded_) {
123 this->ensure_grid_loaded();
124 }
125 return grid_->transform();
126}
127
128std::string VolumeGridData::name() const
129{
130 std::lock_guard lock{mutex_};
131 if (!meta_data_loaded_) {
132 this->ensure_grid_loaded();
133 }
134 return grid_->getName();
135}
136
137void VolumeGridData::set_name(const StringRef name)
138{
139 BLI_assert(this->is_mutable());
140 std::lock_guard lock{mutex_};
141 if (!meta_data_loaded_) {
142 this->ensure_grid_loaded();
143 }
144 grid_->setName(name);
145}
146
147VolumeGridType VolumeGridData::grid_type() const
148{
149 std::lock_guard lock{mutex_};
150 if (!meta_data_loaded_) {
151 this->ensure_grid_loaded();
152 }
153 return get_type(*grid_);
154}
155
156std::optional<VolumeGridType> VolumeGridData::grid_type_without_load() const
157{
158 std::lock_guard lock{mutex_};
159 if (!meta_data_loaded_) {
160 return std::nullopt;
161 }
162 return get_type(*grid_);
163}
164
165openvdb::GridClass VolumeGridData::grid_class() const
166{
167 std::lock_guard lock{mutex_};
168 if (!meta_data_loaded_) {
169 this->ensure_grid_loaded();
170 }
171 return grid_->getGridClass();
172}
173
174bool VolumeGridData::is_reloadable() const
175{
176 return bool(lazy_load_grid_);
177}
178
179bool VolumeGridData::is_loaded() const
180{
181 std::lock_guard lock{mutex_};
182 return tree_loaded_ && transform_loaded_ && meta_data_loaded_;
183}
184
185void VolumeGridData::count_memory(MemoryCounter &memory) const
186{
187 std::lock_guard lock{mutex_};
188 if (!tree_loaded_) {
189 return;
190 }
191 const openvdb::TreeBase &tree = grid_->baseTree();
192 memory.add_shared(tree_sharing_info_.get(),
193 [&](MemoryCounter &shared_memory) { shared_memory.add(tree.memUsage()); });
194}
195
196std::string VolumeGridData::error_message() const
197{
198 std::lock_guard lock{mutex_};
199 return error_message_;
200}
201
202void VolumeGridData::unload_tree_if_possible() const
203{
204 std::lock_guard lock{mutex_};
205 if (!grid_) {
206 return;
207 }
208 if (!tree_loaded_) {
209 return;
210 }
211 if (!this->is_reloadable()) {
212 return;
213 }
214 if (tree_access_token_.use_count() != 1) {
215 /* Some code is using the tree currently, so it can't be freed. */
216 return;
217 }
218 grid_->newTree();
219 tree_loaded_ = false;
220 tree_sharing_info_.reset();
221}
222
223GVolumeGrid VolumeGridData::copy() const
224{
225 std::lock_guard lock{mutex_};
226 this->ensure_grid_loaded();
227 /* Can't use #MEM_new because the default constructor is private. */
228 VolumeGridData *new_copy = new (MEM_mallocN(sizeof(VolumeGridData), __func__)) VolumeGridData();
229 /* Makes a deep copy of the meta-data but shares the tree. */
230 new_copy->grid_ = grid_->copyGrid();
231 new_copy->tree_sharing_info_ = tree_sharing_info_;
232 new_copy->tree_loaded_ = tree_loaded_;
233 new_copy->transform_loaded_ = transform_loaded_;
234 new_copy->meta_data_loaded_ = meta_data_loaded_;
235 return GVolumeGrid(new_copy);
236}
237
238void VolumeGridData::ensure_grid_loaded() const
239{
240 /* Assert that the mutex is locked. */
241 BLI_assert(!mutex_.try_lock());
242
243 if (tree_loaded_ && transform_loaded_ && meta_data_loaded_) {
244 return;
245 }
246 BLI_assert(lazy_load_grid_);
247 LazyLoadedGrid loaded_grid;
248 /* Isolate because the a mutex is locked. */
249 threading::isolate_task([&]() {
250 error_message_.clear();
251 try {
252 loaded_grid = lazy_load_grid_();
253 }
254 catch (const openvdb::IoError &e) {
255 error_message_ = e.what();
256 }
257 catch (...) {
258 error_message_ = "Unknown error reading VDB file";
259 }
260 });
261 if (!loaded_grid.grid) {
262 BLI_assert(!loaded_grid.tree_sharing_info);
263 if (grid_) {
264 const openvdb::Name &grid_type = grid_->type();
265 if (openvdb::GridBase::isRegistered(grid_type)) {
266 /* Create a dummy grid of the expected type. */
267 loaded_grid.grid = openvdb::GridBase::createGrid(grid_type);
268 }
269 }
270 }
271 if (!loaded_grid.grid) {
272 /* Create a dummy grid. We can't really know the expected data type here. */
273 loaded_grid.grid = openvdb::FloatGrid::create();
274 }
275 BLI_assert(loaded_grid.grid);
276 BLI_assert(loaded_grid.grid.unique());
277
278 if (!loaded_grid.tree_sharing_info) {
279 BLI_assert(loaded_grid.grid->isTreeUnique());
280 loaded_grid.tree_sharing_info = OpenvdbTreeSharingInfo::make(loaded_grid.grid->baseTreePtr());
281 }
282
283 if (grid_) {
284 /* Keep the existing grid pointer and just insert the newly loaded data. */
285 BLI_assert(!tree_loaded_);
286 BLI_assert(meta_data_loaded_);
287 grid_->setTree(loaded_grid.grid->baseTreePtr());
288 if (!transform_loaded_) {
289 grid_->setTransform(loaded_grid.grid->transformPtr());
290 }
291 }
292 else {
293 grid_ = std::move(loaded_grid.grid);
294 }
295
296 BLI_assert(!tree_sharing_info_);
297 BLI_assert(loaded_grid.tree_sharing_info);
298 tree_sharing_info_ = std::move(loaded_grid.tree_sharing_info);
299
300 tree_loaded_ = true;
301 transform_loaded_ = true;
302 meta_data_loaded_ = true;
303}
304
305GVolumeGrid::GVolumeGrid(std::shared_ptr<openvdb::GridBase> grid)
306{
307 data_ = ImplicitSharingPtr(MEM_new<VolumeGridData>(__func__, std::move(grid)));
308}
309
310GVolumeGrid::GVolumeGrid(const VolumeGridType grid_type)
311 : GVolumeGrid(create_grid_for_type(grid_type))
312{
313}
314
315VolumeGridData &GVolumeGrid::get_for_write()
316{
317 BLI_assert(*this);
318 if (data_->is_mutable()) {
319 data_->tag_ensured_mutable();
320 }
321 else {
322 *this = data_->copy();
323 }
324 return const_cast<VolumeGridData &>(*data_);
325}
326
327VolumeGridType get_type(const openvdb::GridBase &grid)
328{
329 if (grid.isType<openvdb::FloatGrid>()) {
330 return VOLUME_GRID_FLOAT;
331 }
332 if (grid.isType<openvdb::Vec3fGrid>()) {
334 }
335 if (grid.isType<openvdb::BoolGrid>()) {
336 return VOLUME_GRID_BOOLEAN;
337 }
338 if (grid.isType<openvdb::DoubleGrid>()) {
339 return VOLUME_GRID_DOUBLE;
340 }
341 if (grid.isType<openvdb::Int32Grid>()) {
342 return VOLUME_GRID_INT;
343 }
344 if (grid.isType<openvdb::Int64Grid>()) {
345 return VOLUME_GRID_INT64;
346 }
347 if (grid.isType<openvdb::Vec3IGrid>()) {
349 }
350 if (grid.isType<openvdb::Vec3dGrid>()) {
352 }
353 if (grid.isType<openvdb::MaskGrid>()) {
354 return VOLUME_GRID_MASK;
355 }
356 if (grid.isType<openvdb::points::PointDataGrid>()) {
357 return VOLUME_GRID_POINTS;
358 }
359 return VOLUME_GRID_UNKNOWN;
360}
361
362ImplicitSharingPtr<> OpenvdbTreeSharingInfo::make(std::shared_ptr<openvdb::tree::TreeBase> tree)
363{
364 return ImplicitSharingPtr<>{MEM_new<OpenvdbTreeSharingInfo>(__func__, std::move(tree))};
365}
366
367OpenvdbTreeSharingInfo::OpenvdbTreeSharingInfo(std::shared_ptr<openvdb::tree::TreeBase> tree)
368 : tree_(std::move(tree))
369{
370}
371
372void OpenvdbTreeSharingInfo::delete_self_with_data()
373{
374 MEM_delete(this);
375}
376
377void OpenvdbTreeSharingInfo::delete_data_only()
378{
379 tree_.reset();
380}
381
382VolumeTreeAccessToken::~VolumeTreeAccessToken()
383{
384 const VolumeGridData *grid = token_ ? &token_->grid : nullptr;
385 token_.reset();
386 if (grid) {
387 /* Unload immediately when the value is not used anymore. However, the tree may still be cached
388 * at a deeper level and thus usually does not have to be loaded from disk again.*/
389 grid->unload_tree_if_possible();
390 }
391}
392
393#endif /* WITH_OPENVDB */
394
395std::string get_name(const VolumeGridData &volume_grid)
396{
397#ifdef WITH_OPENVDB
398 return volume_grid.name();
399#else
400 UNUSED_VARS(volume_grid);
401 return "density";
402#endif
403}
404
405VolumeGridType get_type(const VolumeGridData &volume_grid)
406{
407#ifdef WITH_OPENVDB
408 return volume_grid.grid_type();
409#else
410 UNUSED_VARS(volume_grid);
411 return VOLUME_GRID_UNKNOWN;
412#endif
413}
414
416{
417 switch (type) {
421 case VOLUME_GRID_INT:
423 case VOLUME_GRID_MASK:
424 return 1;
428 return 3;
431 return 0;
432 }
433 return 0;
434}
435
436float4x4 get_transform_matrix(const VolumeGridData &grid)
437{
438#ifdef WITH_OPENVDB
439 const openvdb::math::Transform &transform = grid.transform();
440
441 /* Perspective not supported for now, getAffineMap() will leave out the
442 * perspective part of the transform. */
443 openvdb::math::Mat4f matrix = transform.baseMap()->getAffineMap()->getMat4();
444 /* Blender column-major and OpenVDB right-multiplication conventions match. */
446 for (int col = 0; col < 4; col++) {
447 for (int row = 0; row < 4; row++) {
448 result[col][row] = matrix(col, row);
449 }
450 }
451 return result;
452#else
453 UNUSED_VARS(grid);
454 return float4x4::identity();
455#endif
456}
457
458void set_transform_matrix(VolumeGridData &grid, const float4x4 &matrix)
459{
460#ifdef WITH_OPENVDB
461 openvdb::math::Mat4f matrix_openvdb;
462 for (int col = 0; col < 4; col++) {
463 for (int row = 0; row < 4; row++) {
464 matrix_openvdb(col, row) = matrix[col][row];
465 }
466 }
467
468 grid.transform_for_write() = openvdb::math::Transform(
469 std::make_shared<openvdb::math::AffineMap>(matrix_openvdb));
470#else
471 UNUSED_VARS(grid, matrix);
472#endif
473}
474
475void clear_tree(VolumeGridData &grid)
476{
477#ifdef WITH_OPENVDB
478 VolumeTreeAccessToken tree_token;
479 grid.grid_for_write(tree_token).clear();
480#else
481 UNUSED_VARS(grid);
482#endif
483}
484
485bool is_loaded(const VolumeGridData &grid)
486{
487#ifdef WITH_OPENVDB
488 return grid.is_loaded();
489#else
490 UNUSED_VARS(grid);
491 return false;
492#endif
493}
494
495void count_memory(const VolumeGridData &grid, MemoryCounter &memory)
496{
497#ifdef WITH_OPENVDB
498 grid.count_memory(memory);
499#else
500 UNUSED_VARS(grid, memory);
501#endif
502}
503
504void load(const VolumeGridData &grid)
505{
506#ifdef WITH_OPENVDB
507 VolumeTreeAccessToken tree_token;
508 /* Just "touch" the grid, so that it is loaded. */
509 grid.grid(tree_token);
510#else
511 UNUSED_VARS(grid);
512#endif
513}
514
515std::string error_message_from_load(const VolumeGridData &grid)
516{
517#ifdef WITH_OPENVDB
518 return grid.error_message();
519#else
520 UNUSED_VARS(grid);
521 return "";
522#endif
523}
524
525} // namespace blender::bke::volume_grid
VolumeGridType
@ VOLUME_GRID_VECTOR_FLOAT
@ VOLUME_GRID_MASK
@ VOLUME_GRID_VECTOR_DOUBLE
@ VOLUME_GRID_VECTOR_INT
@ VOLUME_GRID_UNKNOWN
@ VOLUME_GRID_DOUBLE
@ VOLUME_GRID_BOOLEAN
@ VOLUME_GRID_INT
@ VOLUME_GRID_INT64
@ VOLUME_GRID_POINTS
@ VOLUME_GRID_FLOAT
#define BLI_assert(a)
Definition BLI_assert.h:50
#define UNUSED_VARS(...)
volatile int lock
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
SIMD_FORCE_INLINE btVector3 operator()(const btVector3 &x) const
Return the transform of the vector.
Definition btTransform.h:90
KDTree_3d * tree
uint col
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
float4x4 get_transform_matrix(const VolumeGridData &grid)
std::string get_name(const VolumeGridData &grid)
void count_memory(const VolumeGridData &grid, MemoryCounter &memory)
int get_channels_num(VolumeGridType type)
std::string error_message_from_load(const VolumeGridData &grid)
void load(const VolumeGridData &grid)
void clear_tree(VolumeGridData &grid)
VolumeGridType get_type(const VolumeGridData &grid)
void set_transform_matrix(VolumeGridData &grid, const float4x4 &matrix)
bool is_loaded(const VolumeGridData &grid)