Blender  V3.3
volume.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
7 #include "MEM_guardedalloc.h"
8 
9 #include "DNA_defaults.h"
10 #include "DNA_material_types.h"
11 #include "DNA_object_types.h"
12 #include "DNA_scene_types.h"
13 #include "DNA_volume_types.h"
14 
15 #include "BLI_compiler_compat.h"
16 #include "BLI_fileops.h"
17 #include "BLI_float4x4.hh"
18 #include "BLI_ghash.h"
19 #include "BLI_index_range.hh"
20 #include "BLI_map.hh"
21 #include "BLI_math.h"
22 #include "BLI_math_vec_types.hh"
23 #include "BLI_path_util.h"
24 #include "BLI_string.h"
25 #include "BLI_string_ref.hh"
26 #include "BLI_task.hh"
27 #include "BLI_utildefines.h"
28 
29 #include "BKE_anim_data.h"
30 #include "BKE_bpath.h"
31 #include "BKE_geometry_set.hh"
32 #include "BKE_global.h"
33 #include "BKE_idtype.h"
34 #include "BKE_lib_id.h"
35 #include "BKE_lib_query.h"
36 #include "BKE_lib_remap.h"
37 #include "BKE_main.h"
38 #include "BKE_modifier.h"
39 #include "BKE_object.h"
40 #include "BKE_packedFile.h"
41 #include "BKE_report.h"
42 #include "BKE_scene.h"
43 #include "BKE_volume.h"
44 
45 #include "BLT_translation.h"
46 
47 #include "DEG_depsgraph_query.h"
48 
49 #include "BLO_read_write.h"
50 
51 #include "CLG_log.h"
52 
53 #ifdef WITH_OPENVDB
54 static CLG_LogRef LOG = {"bke.volume"};
55 #endif
56 
57 #define VOLUME_FRAME_NONE INT_MAX
58 
59 using blender::float3;
60 using blender::float4x4;
62 using blender::StringRef;
64 
65 #ifdef WITH_OPENVDB
66 # include <atomic>
67 # include <list>
68 # include <mutex>
69 # include <unordered_set>
70 
71 # include <openvdb/openvdb.h>
72 # include <openvdb/points/PointDataGrid.h>
73 # include <openvdb/tools/GridTransformer.h>
74 
75 /* Global Volume File Cache
76  *
77  * Global cache of grids read from VDB files. This is used for sharing grids
78  * between multiple volume datablocks with the same filepath, and sharing grids
79  * between original and copy-on-write datablocks created by the depsgraph.
80  *
81  * There are two types of users. Some datablocks only need the grid metadata,
82  * example an original datablock volume showing the list of grids in the
83  * properties editor. Other datablocks also need the tree and voxel data, for
84  * rendering for example. So, depending on the users the grid in the cache may
85  * have a tree or not.
86  *
87  * When the number of users drops to zero, the grid data is immediately deleted.
88  *
89  * TODO: also add a cache for OpenVDB files rather than individual grids,
90  * so getting the list of grids is also cached.
91  * TODO: Further, we could cache openvdb::io::File so that loading a grid
92  * does not re-open it every time. But then we have to take care not to run
93  * out of file descriptors or prevent other applications from writing to it.
94  */
95 
96 static struct VolumeFileCache {
97  /* Cache Entry */
98  struct Entry {
99  Entry(const std::string &filepath, const openvdb::GridBase::Ptr &grid)
100  : filepath(filepath), grid_name(grid->getName()), grid(grid)
101  {
102  }
103 
104  Entry(const Entry &other)
105  : filepath(other.filepath),
106  grid_name(other.grid_name),
107  grid(other.grid),
108  is_loaded(other.is_loaded)
109  {
110  }
111 
112  /* Returns the original grid or a simplified version depending on the given #simplify_level. */
113  openvdb::GridBase::Ptr simplified_grid(const int simplify_level)
114  {
115  BLI_assert(simplify_level >= 0);
116  if (simplify_level == 0 || !is_loaded) {
117  return grid;
118  }
119 
120  std::lock_guard<std::mutex> lock(mutex);
121  openvdb::GridBase::Ptr simple_grid;
122 
123  /* Isolate creating grid since that's multithreaded and we are
124  * holding a mutex lock. */
126  simple_grid = simplified_grids.lookup_or_add_cb(simplify_level, [&]() {
127  const float resolution_factor = 1.0f / (1 << simplify_level);
128  const VolumeGridType grid_type = BKE_volume_grid_type_openvdb(*grid);
129  return BKE_volume_grid_create_with_changed_resolution(
130  grid_type, *grid, resolution_factor);
131  });
132  });
133  return simple_grid;
134  }
135 
136  /* Unique key: filename + grid name. */
137  std::string filepath;
138  std::string grid_name;
139 
140  /* OpenVDB grid. */
142 
143  /* Simplified versions of #grid. The integer key is the simplification level. */
145 
146  /* Has the grid tree been loaded? */
147  mutable bool is_loaded = false;
148  /* Error message if an error occurred while loading. */
149  std::string error_msg;
150  /* User counting. */
151  int num_metadata_users = 0;
152  int num_tree_users = 0;
153  /* Mutex for on-demand reading of tree. */
154  mutable std::mutex mutex;
155  };
156 
157  struct EntryHasher {
158  std::size_t operator()(const Entry &entry) const
159  {
160  std::hash<std::string> string_hasher;
161  return BLI_ghashutil_combine_hash(string_hasher(entry.filepath),
162  string_hasher(entry.grid_name));
163  }
164  };
165 
166  struct EntryEqual {
167  bool operator()(const Entry &a, const Entry &b) const
168  {
169  return a.filepath == b.filepath && a.grid_name == b.grid_name;
170  }
171  };
172 
173  /* Cache */
174  ~VolumeFileCache()
175  {
176  BLI_assert(cache.empty());
177  }
178 
179  Entry *add_metadata_user(const Entry &template_entry)
180  {
181  std::lock_guard<std::mutex> lock(mutex);
182  EntrySet::iterator it = cache.find(template_entry);
183  if (it == cache.end()) {
184  it = cache.emplace(template_entry).first;
185  }
186 
187  /* Casting const away is weak, but it's convenient having key and value in one. */
188  Entry &entry = (Entry &)*it;
189  entry.num_metadata_users++;
190 
191  /* NOTE: pointers to unordered_set values are not invalidated when adding
192  * or removing other values. */
193  return &entry;
194  }
195 
196  void copy_user(Entry &entry, const bool tree_user)
197  {
198  std::lock_guard<std::mutex> lock(mutex);
199  if (tree_user) {
200  entry.num_tree_users++;
201  }
202  else {
203  entry.num_metadata_users++;
204  }
205  }
206 
207  void remove_user(Entry &entry, const bool tree_user)
208  {
209  std::lock_guard<std::mutex> lock(mutex);
210  if (tree_user) {
211  entry.num_tree_users--;
212  }
213  else {
214  entry.num_metadata_users--;
215  }
216  update_for_remove_user(entry);
217  }
218 
219  void change_to_tree_user(Entry &entry)
220  {
221  std::lock_guard<std::mutex> lock(mutex);
222  entry.num_tree_users++;
223  entry.num_metadata_users--;
224  update_for_remove_user(entry);
225  }
226 
227  void change_to_metadata_user(Entry &entry)
228  {
229  std::lock_guard<std::mutex> lock(mutex);
230  entry.num_metadata_users++;
231  entry.num_tree_users--;
232  update_for_remove_user(entry);
233  }
234 
235  protected:
236  void update_for_remove_user(Entry &entry)
237  {
238  /* Isolate file unloading since that's multithreaded and we are
239  * holding a mutex lock. */
241  if (entry.num_metadata_users + entry.num_tree_users == 0) {
242  cache.erase(entry);
243  }
244  else if (entry.num_tree_users == 0) {
245  /* Note we replace the grid rather than clearing, so that if there is
246  * any other shared pointer to the grid it will keep the tree. */
247  entry.grid = entry.grid->copyGridWithNewTree();
248  entry.simplified_grids.clear();
249  entry.is_loaded = false;
250  }
251  });
252  }
253 
254  /* Cache contents */
255  using EntrySet = std::unordered_set<Entry, EntryHasher, EntryEqual>;
256  EntrySet cache;
257  /* Mutex for multithreaded access. */
259 } GLOBAL_CACHE;
260 
261 /* VolumeGrid
262  *
263  * Wrapper around OpenVDB grid. Grids loaded from OpenVDB files are always
264  * stored in the global cache. Procedurally generated grids are not. */
265 
266 struct VolumeGrid {
267  VolumeGrid(const VolumeFileCache::Entry &template_entry, const int simplify_level)
268  : entry(nullptr), simplify_level(simplify_level), is_loaded(false)
269  {
270  entry = GLOBAL_CACHE.add_metadata_user(template_entry);
271  }
272 
274  : entry(nullptr), local_grid(grid), is_loaded(true)
275  {
276  }
277 
278  VolumeGrid(const VolumeGrid &other)
279  : entry(other.entry),
280  simplify_level(other.simplify_level),
281  local_grid(other.local_grid),
282  is_loaded(other.is_loaded)
283  {
284  if (entry) {
285  GLOBAL_CACHE.copy_user(*entry, is_loaded);
286  }
287  }
288 
289  ~VolumeGrid()
290  {
291  if (entry) {
292  GLOBAL_CACHE.remove_user(*entry, is_loaded);
293  }
294  }
295 
296  void load(const char *volume_name, const char *filepath) const
297  {
298  /* If already loaded or not file-backed, nothing to do. */
299  if (is_loaded || entry == nullptr) {
300  return;
301  }
302 
303  /* Double-checked lock. */
304  std::lock_guard<std::mutex> lock(entry->mutex);
305  if (is_loaded) {
306  return;
307  }
308 
309  /* Change metadata user to tree user. */
310  GLOBAL_CACHE.change_to_tree_user(*entry);
311 
312  /* If already loaded by another user, nothing further to do. */
313  if (entry->is_loaded) {
314  is_loaded = true;
315  return;
316  }
317 
318  /* Load grid from file. */
319  CLOG_INFO(&LOG, 1, "Volume %s: load grid '%s'", volume_name, name());
320 
321  openvdb::io::File file(filepath);
322 
323  /* Isolate file loading since that's potentially multithreaded and we are
324  * holding a mutex lock. */
326  try {
327  /* Disably delay loading and file copying, this has poor performance
328  * on network drivers. */
329  const bool delay_load = false;
330  file.setCopyMaxBytes(0);
331  file.open(delay_load);
332  openvdb::GridBase::Ptr vdb_grid = file.readGrid(name());
333  entry->grid->setTree(vdb_grid->baseTreePtr());
334  }
335  catch (const openvdb::IoError &e) {
336  entry->error_msg = e.what();
337  }
338  });
339 
340  std::atomic_thread_fence(std::memory_order_release);
341  entry->is_loaded = true;
342  is_loaded = true;
343  }
344 
345  void unload(const char *volume_name) const
346  {
347  /* Not loaded or not file-backed, nothing to do. */
348  if (!is_loaded || entry == nullptr) {
349  return;
350  }
351 
352  /* Double-checked lock. */
353  std::lock_guard<std::mutex> lock(entry->mutex);
354  if (!is_loaded) {
355  return;
356  }
357 
358  CLOG_INFO(&LOG, 1, "Volume %s: unload grid '%s'", volume_name, name());
359 
360  /* Change tree user to metadata user. */
361  GLOBAL_CACHE.change_to_metadata_user(*entry);
362 
363  /* Indicate we no longer have a tree. The actual grid may still
364  * have it due to another user. */
365  std::atomic_thread_fence(std::memory_order_release);
366  is_loaded = false;
367  }
368 
369  void clear_reference(const char *UNUSED(volume_name))
370  {
371  /* Clear any reference to a grid in the file cache. */
372  local_grid = grid()->copyGridWithNewTree();
373  if (entry) {
374  GLOBAL_CACHE.remove_user(*entry, is_loaded);
375  entry = nullptr;
376  }
377  is_loaded = true;
378  }
379 
380  void duplicate_reference(const char *volume_name, const char *filepath)
381  {
382  /* Make a deep copy of the grid and remove any reference to a grid in the
383  * file cache. Load file grid into memory first if needed. */
384  load(volume_name, filepath);
385  /* TODO: avoid deep copy if we are the only user. */
386  local_grid = grid()->deepCopyGrid();
387  if (entry) {
388  GLOBAL_CACHE.remove_user(*entry, is_loaded);
389  entry = nullptr;
390  }
391  is_loaded = true;
392  }
393 
394  const char *name() const
395  {
396  /* Don't use vdb.getName() since it copies the string, we want a pointer to the
397  * original so it doesn't get freed out of scope. */
398  openvdb::StringMetadata::ConstPtr name_meta =
399  main_grid()->getMetadata<openvdb::StringMetadata>(openvdb::GridBase::META_GRID_NAME);
400  return (name_meta) ? name_meta->value().c_str() : "";
401  }
402 
403  const char *error_message() const
404  {
405  if (is_loaded && entry && !entry->error_msg.empty()) {
406  return entry->error_msg.c_str();
407  }
408 
409  return nullptr;
410  }
411 
412  bool grid_is_loaded() const
413  {
414  return is_loaded;
415  }
416 
417  openvdb::GridBase::Ptr grid() const
418  {
419  if (entry) {
420  return entry->simplified_grid(simplify_level);
421  }
422  return local_grid;
423  }
424 
425  void set_simplify_level(const int simplify_level)
426  {
427  BLI_assert(simplify_level >= 0);
428  this->simplify_level = simplify_level;
429  }
430 
431  private:
432  const openvdb::GridBase::Ptr &main_grid() const
433  {
434  return (entry) ? entry->grid : local_grid;
435  }
436 
437  protected:
438  /* File cache entry when grid comes directly from a file and may be shared
439  * with other volume datablocks. */
440  VolumeFileCache::Entry *entry;
441  /* If this volume grid is in the global file cache, we can reference a simplified version of it,
442  * instead of the original high resolution grid. */
443  int simplify_level = 0;
444  /* OpenVDB grid if it's not shared through the file cache. */
445  openvdb::GridBase::Ptr local_grid;
453  mutable bool is_loaded;
454 };
455 
456 /* Volume Grid Vector
457  *
458  * List of grids contained in a volume datablock. This is runtime-only data,
459  * the actual grids are always saved in a VDB file. */
460 
461 struct VolumeGridVector : public std::list<VolumeGrid> {
462  VolumeGridVector() : metadata(new openvdb::MetaMap())
463  {
464  filepath[0] = '\0';
465  }
466 
467  VolumeGridVector(const VolumeGridVector &other)
468  : std::list<VolumeGrid>(other), error_msg(other.error_msg), metadata(other.metadata)
469  {
470  memcpy(filepath, other.filepath, sizeof(filepath));
471  }
472 
473  bool is_loaded() const
474  {
475  return filepath[0] != '\0';
476  }
477 
478  void clear_all()
479  {
481  filepath[0] = '\0';
482  error_msg.clear();
483  metadata.reset();
484  }
485 
486  /* Mutex for file loading of grids list. Const write access to the fields after this must be
487  * protected by locking with this mutex. */
488  mutable std::mutex mutex;
489  /* Absolute file path that grids have been loaded from. */
490  char filepath[FILE_MAX];
491  /* File loading error message. */
492  std::string error_msg;
493  /* File Metadata. */
494  openvdb::MetaMap::Ptr metadata;
495 };
496 #endif
497 
498 /* Module */
499 
501 {
502 #ifdef WITH_OPENVDB
504 #endif
505 }
506 
507 /* Volume datablock */
508 
509 static void volume_init_data(ID *id)
510 {
511  Volume *volume = (Volume *)id;
513 
515 
516  BKE_volume_init_grids(volume);
517 
518  BLI_strncpy(volume->velocity_grid, "velocity", sizeof(volume->velocity_grid));
519 }
520 
521 static void volume_copy_data(Main *UNUSED(bmain),
522  ID *id_dst,
523  const ID *id_src,
524  const int UNUSED(flag))
525 {
526  Volume *volume_dst = (Volume *)id_dst;
527  const Volume *volume_src = (const Volume *)id_src;
528 
529  if (volume_src->packedfile) {
530  volume_dst->packedfile = BKE_packedfile_duplicate(volume_src->packedfile);
531  }
532 
533  volume_dst->mat = (Material **)MEM_dupallocN(volume_src->mat);
534 #ifdef WITH_OPENVDB
535  if (volume_src->runtime.grids) {
536  const VolumeGridVector &grids_src = *(volume_src->runtime.grids);
537  volume_dst->runtime.grids = MEM_new<VolumeGridVector>(__func__, grids_src);
538  }
539 #endif
540 
541  volume_dst->batch_cache = nullptr;
542 }
543 
544 static void volume_free_data(ID *id)
545 {
546  Volume *volume = (Volume *)id;
547  BKE_animdata_free(&volume->id, false);
549  MEM_SAFE_FREE(volume->mat);
550 #ifdef WITH_OPENVDB
551  MEM_delete(volume->runtime.grids);
552  volume->runtime.grids = nullptr;
553 #endif
554 }
555 
557 {
558  Volume *volume = (Volume *)id;
559  for (int i = 0; i < volume->totcol; i++) {
561  }
562 }
563 
564 static void volume_foreach_cache(ID *id,
565  IDTypeForeachCacheFunctionCallback function_callback,
566  void *user_data)
567 {
568  Volume *volume = (Volume *)id;
569  IDCacheKey key = {
570  /* id_session_uuid */ id->session_uuid,
571  /* offset_in_ID */ offsetof(Volume, runtime.grids),
572  };
573 
574  function_callback(id, &key, (void **)&volume->runtime.grids, 0, user_data);
575 }
576 
577 static void volume_foreach_path(ID *id, BPathForeachPathData *bpath_data)
578 {
579  Volume *volume = reinterpret_cast<Volume *>(id);
580 
581  if (volume->packedfile != nullptr &&
582  (bpath_data->flag & BKE_BPATH_FOREACH_PATH_SKIP_PACKED) != 0) {
583  return;
584  }
585 
586  BKE_bpath_foreach_path_fixed_process(bpath_data, volume->filepath);
587 }
588 
589 static void volume_blend_write(BlendWriter *writer, ID *id, const void *id_address)
590 {
591  Volume *volume = (Volume *)id;
592  const bool is_undo = BLO_write_is_undo(writer);
593 
594  /* Clean up, important in undo case to reduce false detection of changed datablocks. */
595  volume->runtime.grids = nullptr;
596 
597  /* Do not store packed files in case this is a library override ID. */
598  if (ID_IS_OVERRIDE_LIBRARY(volume) && !is_undo) {
599  volume->packedfile = nullptr;
600  }
601 
602  /* write LibData */
603  BLO_write_id_struct(writer, Volume, id_address, &volume->id);
604  BKE_id_blend_write(writer, &volume->id);
605 
606  /* direct data */
607  BLO_write_pointer_array(writer, volume->totcol, volume->mat);
608  if (volume->adt) {
609  BKE_animdata_blend_write(writer, volume->adt);
610  }
611 
612  BKE_packedfile_blend_write(writer, volume->packedfile);
613 }
614 
615 static void volume_blend_read_data(BlendDataReader *reader, ID *id)
616 {
617  Volume *volume = (Volume *)id;
618  BLO_read_data_address(reader, &volume->adt);
619  BKE_animdata_blend_read_data(reader, volume->adt);
620 
621  BKE_packedfile_blend_read(reader, &volume->packedfile);
622  volume->runtime.frame = 0;
623 
624  /* materials */
625  BLO_read_pointer_array(reader, (void **)&volume->mat);
626 }
627 
628 static void volume_blend_read_lib(BlendLibReader *reader, ID *id)
629 {
630  Volume *volume = (Volume *)id;
631  /* Needs to be done *after* cache pointers are restored (call to
632  * `foreach_cache`/`blo_cache_storage_entry_restore_in_new`), easier for now to do it in
633  * lib_link... */
634  BKE_volume_init_grids(volume);
635 
636  for (int a = 0; a < volume->totcol; a++) {
637  BLO_read_id_address(reader, volume->id.lib, &volume->mat[a]);
638  }
639 }
640 
641 static void volume_blend_read_expand(BlendExpander *expander, ID *id)
642 {
643  Volume *volume = (Volume *)id;
644  for (int a = 0; a < volume->totcol; a++) {
645  BLO_expand(expander, volume->mat[a]);
646  }
647 }
648 
650  /* id_code */ ID_VO,
651  /* id_filter */ FILTER_ID_VO,
652  /* main_listbase_index */ INDEX_ID_VO,
653  /* struct_size */ sizeof(Volume),
654  /* name */ "Volume",
655  /* name_plural */ "volumes",
656  /* translation_context */ BLT_I18NCONTEXT_ID_VOLUME,
658  /* asset_type_info */ nullptr,
659 
660  /* init_data */ volume_init_data,
661  /* copy_data */ volume_copy_data,
662  /* free_data */ volume_free_data,
663  /* make_local */ nullptr,
664  /* foreach_id */ volume_foreach_id,
665  /* foreach_cache */ volume_foreach_cache,
666  /* foreach_path */ volume_foreach_path,
667  /* owner_get */ nullptr,
668 
669  /* blend_write */ volume_blend_write,
670  /* blend_read_data */ volume_blend_read_data,
671  /* blend_read_lib */ volume_blend_read_lib,
672  /* blend_read_expand */ volume_blend_read_expand,
673 
674  /* blend_read_undo_preserve */ nullptr,
675 
676  /* lib_override_apply_post */ nullptr,
677 };
678 
680 {
681 #ifdef WITH_OPENVDB
682  if (volume->runtime.grids == nullptr) {
683  volume->runtime.grids = MEM_new<VolumeGridVector>(__func__);
684  }
685 #else
686  UNUSED_VARS(volume);
687 #endif
688 }
689 
690 void *BKE_volume_add(Main *bmain, const char *name)
691 {
692  Volume *volume = (Volume *)BKE_id_new(bmain, ID_VO, name);
693 
694  return volume;
695 }
696 
697 /* Sequence */
698 
699 static int volume_sequence_frame(const Depsgraph *depsgraph, const Volume *volume)
700 {
701  if (!volume->is_sequence) {
702  return 0;
703  }
704 
705  char filepath[FILE_MAX];
706  STRNCPY(filepath, volume->filepath);
707  int path_frame, path_digits;
708  if (!(volume->is_sequence && BLI_path_frame_get(filepath, &path_frame, &path_digits))) {
709  return 0;
710  }
711 
712  const int scene_frame = DEG_get_ctime(depsgraph);
714  const int frame_duration = volume->frame_duration;
715  const int frame_start = volume->frame_start;
716  const int frame_offset = volume->frame_offset;
717 
718  if (frame_duration == 0) {
719  return VOLUME_FRAME_NONE;
720  }
721 
722  int frame = scene_frame - frame_start + 1;
723 
724  switch (mode) {
725  case VOLUME_SEQUENCE_CLIP: {
726  if (frame < 1 || frame > frame_duration) {
727  return VOLUME_FRAME_NONE;
728  }
729  break;
730  }
731  case VOLUME_SEQUENCE_EXTEND: {
732  frame = clamp_i(frame, 1, frame_duration);
733  break;
734  }
735  case VOLUME_SEQUENCE_REPEAT: {
736  frame = frame % frame_duration;
737  if (frame < 0) {
738  frame += frame_duration;
739  }
740  if (frame == 0) {
741  frame = frame_duration;
742  }
743  break;
744  }
746  const int pingpong_duration = frame_duration * 2 - 2;
747  frame = frame % pingpong_duration;
748  if (frame < 0) {
749  frame += pingpong_duration;
750  }
751  if (frame == 0) {
752  frame = pingpong_duration;
753  }
754  if (frame > frame_duration) {
755  frame = frame_duration * 2 - frame;
756  }
757  break;
758  }
759  }
760 
761  /* Important to apply after, else we can't loop on e.g. frames 100 - 110. */
762  frame += frame_offset;
763 
764  return frame;
765 }
766 
767 #ifdef WITH_OPENVDB
768 static void volume_filepath_get(const Main *bmain, const Volume *volume, char r_filepath[FILE_MAX])
769 {
770  BLI_strncpy(r_filepath, volume->filepath, FILE_MAX);
771  BLI_path_abs(r_filepath, ID_BLEND_PATH(bmain, &volume->id));
772 
773  int path_frame, path_digits;
774  if (volume->is_sequence && BLI_path_frame_get(r_filepath, &path_frame, &path_digits)) {
775  char ext[32];
776  BLI_path_frame_strip(r_filepath, ext, sizeof(ext));
777  BLI_path_frame(r_filepath, volume->runtime.frame, path_digits);
778  BLI_path_extension_ensure(r_filepath, FILE_MAX, ext);
779  }
780 }
781 #endif
782 
783 /* File Load */
784 
785 bool BKE_volume_is_loaded(const Volume *volume)
786 {
787 #ifdef WITH_OPENVDB
788  /* Test if there is a file to load, or if already loaded. */
789  return (volume->filepath[0] == '\0' || volume->runtime.grids->is_loaded());
790 #else
791  UNUSED_VARS(volume);
792  return true;
793 #endif
794 }
795 
796 bool BKE_volume_set_velocity_grid_by_name(Volume *volume, const char *base_name)
797 {
798  const StringRefNull ref_base_name = base_name;
799 
800  if (BKE_volume_grid_find_for_read(volume, base_name)) {
801  BLI_strncpy(volume->velocity_grid, base_name, sizeof(volume->velocity_grid));
802  volume->runtime.velocity_x_grid[0] = '\0';
803  volume->runtime.velocity_y_grid[0] = '\0';
804  volume->runtime.velocity_z_grid[0] = '\0';
805  return true;
806  }
807 
808  /* It could be that the velocity grid is split in multiple grids, try with known postfixes. */
809  const StringRefNull postfixes[][3] = {{"x", "y", "z"}, {".x", ".y", ".z"}, {"_x", "_y", "_z"}};
810 
811  for (const StringRefNull *postfix : postfixes) {
812  bool found = true;
813  for (int i = 0; i < 3; i++) {
814  std::string post_fixed_name = ref_base_name + postfix[i];
815  if (!BKE_volume_grid_find_for_read(volume, post_fixed_name.c_str())) {
816  found = false;
817  break;
818  }
819  }
820 
821  if (!found) {
822  continue;
823  }
824 
825  /* Save the base name as well. */
826  BLI_strncpy(volume->velocity_grid, base_name, sizeof(volume->velocity_grid));
828  (ref_base_name + postfix[0]).c_str(),
829  sizeof(volume->runtime.velocity_x_grid));
831  (ref_base_name + postfix[1]).c_str(),
832  sizeof(volume->runtime.velocity_y_grid));
834  (ref_base_name + postfix[2]).c_str(),
835  sizeof(volume->runtime.velocity_z_grid));
836  return true;
837  }
838 
839  /* Reset to avoid potential issues. */
840  volume->velocity_grid[0] = '\0';
841  volume->runtime.velocity_x_grid[0] = '\0';
842  volume->runtime.velocity_y_grid[0] = '\0';
843  volume->runtime.velocity_z_grid[0] = '\0';
844  return false;
845 }
846 
847 bool BKE_volume_load(const Volume *volume, const Main *bmain)
848 {
849 #ifdef WITH_OPENVDB
850  const VolumeGridVector &const_grids = *volume->runtime.grids;
851 
852  if (volume->runtime.frame == VOLUME_FRAME_NONE) {
853  /* Skip loading this frame, outside of sequence range. */
854  return true;
855  }
856 
857  if (BKE_volume_is_loaded(volume)) {
858  return const_grids.error_msg.empty();
859  }
860 
861  /* Double-checked lock. */
862  std::lock_guard<std::mutex> lock(const_grids.mutex);
863  if (BKE_volume_is_loaded(volume)) {
864  return const_grids.error_msg.empty();
865  }
866 
867  /* Guarded by the lock, we can continue to access the grid vector,
868  * adding error messages or a new grid, etc. */
869  VolumeGridVector &grids = const_cast<VolumeGridVector &>(const_grids);
870 
871  /* Get absolute file path at current frame. */
872  const char *volume_name = volume->id.name + 2;
873  char filepath[FILE_MAX];
874  volume_filepath_get(bmain, volume, filepath);
875 
876  CLOG_INFO(&LOG, 1, "Volume %s: load %s", volume_name, filepath);
877 
878  /* Test if file exists. */
879  if (!BLI_exists(filepath)) {
880  char filename[FILE_MAX];
881  BLI_split_file_part(filepath, filename, sizeof(filename));
882  grids.error_msg = filename + std::string(" not found");
883  CLOG_INFO(&LOG, 1, "Volume %s: %s", volume_name, grids.error_msg.c_str());
884  return false;
885  }
886 
887  /* Open OpenVDB file. */
888  openvdb::io::File file(filepath);
889  openvdb::GridPtrVec vdb_grids;
890 
891  try {
892  /* Disably delay loading and file copying, this has poor performance
893  * on network drivers. */
894  const bool delay_load = false;
895  file.setCopyMaxBytes(0);
896  file.open(delay_load);
897  vdb_grids = *(file.readAllGridMetadata());
898  grids.metadata = file.getMetadata();
899  }
900  catch (const openvdb::IoError &e) {
901  grids.error_msg = e.what();
902  CLOG_INFO(&LOG, 1, "Volume %s: %s", volume_name, grids.error_msg.c_str());
903  }
904 
905  /* Add grids read from file to own vector, filtering out any NULL pointers. */
906  for (const openvdb::GridBase::Ptr &vdb_grid : vdb_grids) {
907  if (vdb_grid) {
908  VolumeFileCache::Entry template_entry(filepath, vdb_grid);
909  grids.emplace_back(template_entry, volume->runtime.default_simplify_level);
910  }
911  }
912 
913  /* Try to detect the velocity grid. */
914  const char *common_velocity_names[] = {"velocity", "vel", "v"};
915  for (const char *common_velocity_name : common_velocity_names) {
916  if (BKE_volume_set_velocity_grid_by_name(const_cast<Volume *>(volume), common_velocity_name)) {
917  break;
918  }
919  }
920 
921  BLI_strncpy(grids.filepath, filepath, FILE_MAX);
922 
923  return grids.error_msg.empty();
924 #else
925  UNUSED_VARS(bmain, volume);
926  return true;
927 #endif
928 }
929 
931 {
932 #ifdef WITH_OPENVDB
933  VolumeGridVector &grids = *volume->runtime.grids;
934  if (grids.filepath[0] != '\0') {
935  const char *volume_name = volume->id.name + 2;
936  CLOG_INFO(&LOG, 1, "Volume %s: unload", volume_name);
937  grids.clear_all();
938  }
939 #else
940  UNUSED_VARS(volume);
941 #endif
942 }
943 
944 /* File Save */
945 
946 bool BKE_volume_save(const Volume *volume,
947  const Main *bmain,
948  ReportList *reports,
949  const char *filepath)
950 {
951 #ifdef WITH_OPENVDB
952  if (!BKE_volume_load(volume, bmain)) {
953  BKE_reportf(reports, RPT_ERROR, "Could not load volume for writing");
954  return false;
955  }
956 
957  VolumeGridVector &grids = *volume->runtime.grids;
958  openvdb::GridCPtrVec vdb_grids;
959 
960  for (VolumeGrid &grid : grids) {
961  vdb_grids.push_back(BKE_volume_grid_openvdb_for_read(volume, &grid));
962  }
963 
964  try {
965  openvdb::io::File file(filepath);
966  file.write(vdb_grids, *grids.metadata);
967  file.close();
968  }
969  catch (const openvdb::IoError &e) {
970  BKE_reportf(reports, RPT_ERROR, "Could not write volume: %s", e.what());
971  return false;
972  }
973 
974  return true;
975 #else
976  UNUSED_VARS(volume, bmain, reports, filepath);
977  return false;
978 #endif
979 }
980 
981 bool BKE_volume_min_max(const Volume *volume, float3 &r_min, float3 &r_max)
982 {
983  bool have_minmax = false;
984 #ifdef WITH_OPENVDB
985  /* TODO: if we know the volume is going to be displayed, it may be good to
986  * load it as part of dependency graph evaluation for better threading. We
987  * could also share the bounding box computation in the global volume cache. */
988  if (BKE_volume_load(const_cast<Volume *>(volume), G.main)) {
989  for (const int i : IndexRange(BKE_volume_num_grids(volume))) {
990  const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, i);
991  openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid);
992  float3 grid_min;
993  float3 grid_max;
994  if (BKE_volume_grid_bounds(grid, grid_min, grid_max)) {
995  DO_MIN(grid_min, r_min);
996  DO_MAX(grid_max, r_max);
997  have_minmax = true;
998  }
999  }
1000  }
1001 #else
1002  UNUSED_VARS(volume, r_min, r_max);
1003 #endif
1004  return have_minmax;
1005 }
1006 
1008 {
1009  BLI_assert(ob->type == OB_VOLUME);
1010 
1011  if (ob->runtime.bb != nullptr && (ob->runtime.bb->flag & BOUNDBOX_DIRTY) == 0) {
1012  return ob->runtime.bb;
1013  }
1014 
1015  if (ob->runtime.bb == nullptr) {
1016  ob->runtime.bb = MEM_cnew<BoundBox>(__func__);
1017  }
1018 
1019  const Volume *volume = (Volume *)ob->data;
1020 
1021  float3 min, max;
1022  INIT_MINMAX(min, max);
1023  if (!BKE_volume_min_max(volume, min, max)) {
1024  min = float3(-1);
1025  max = float3(1);
1026  }
1027 
1029 
1030  return ob->runtime.bb;
1031 }
1032 
1033 bool BKE_volume_is_y_up(const Volume *volume)
1034 {
1035  /* Simple heuristic for common files to open the right way up. */
1036 #ifdef WITH_OPENVDB
1037  VolumeGridVector &grids = *volume->runtime.grids;
1038  if (grids.metadata) {
1039  openvdb::StringMetadata::ConstPtr creator =
1040  grids.metadata->getMetadata<openvdb::StringMetadata>("creator");
1041  if (!creator) {
1042  creator = grids.metadata->getMetadata<openvdb::StringMetadata>("Creator");
1043  }
1044  return (creator && creator->str().rfind("Houdini", 0) == 0);
1045  }
1046 #else
1047  UNUSED_VARS(volume);
1048 #endif
1049 
1050  return false;
1051 }
1052 
1054 {
1055  int num_grids = BKE_volume_num_grids(volume);
1056  if (num_grids == 0) {
1057  return false;
1058  }
1059 
1060  for (int i = 0; i < num_grids; i++) {
1061  const VolumeGrid *grid = BKE_volume_grid_get_for_read(volume, i);
1063  return false;
1064  }
1065  }
1066 
1067  return true;
1068 }
1069 
1070 /* Dependency Graph */
1071 
1073 {
1074 #ifdef WITH_OPENVDB
1075  const int simplify_level = BKE_volume_simplify_level(depsgraph);
1076  if (volume->runtime.grids) {
1077  for (VolumeGrid &grid : *volume->runtime.grids) {
1078  grid.set_simplify_level(simplify_level);
1079  }
1080  }
1081  volume->runtime.default_simplify_level = simplify_level;
1082 #else
1083  UNUSED_VARS(volume, depsgraph);
1084 #endif
1085 }
1086 
1088  struct Scene *scene,
1089  Object *object,
1090  GeometrySet &geometry_set)
1091 {
1092  /* Modifier evaluation modes. */
1093  const bool use_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
1094  const int required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime;
1095  ModifierApplyFlag apply_flag = use_render ? MOD_APPLY_RENDER : MOD_APPLY_USECACHE;
1096  const ModifierEvalContext mectx = {depsgraph, object, apply_flag};
1097 
1099 
1100  /* Get effective list of modifiers to execute. Some effects like shape keys
1101  * are added as virtual modifiers before the user created modifiers. */
1102  VirtualModifierData virtualModifierData;
1103  ModifierData *md = BKE_modifiers_get_virtual_modifierlist(object, &virtualModifierData);
1104 
1105  /* Evaluate modifiers. */
1106  for (; md; md = md->next) {
1108  (ModifierType)md->type);
1109 
1110  if (!BKE_modifier_is_enabled(scene, md, required_mode)) {
1111  continue;
1112  }
1113 
1114  if (mti->modifyGeometrySet) {
1115  mti->modifyGeometrySet(md, &mectx, &geometry_set);
1116  }
1117  }
1118 }
1119 
1121 {
1123 
1124  /* TODO: can we avoid modifier re-evaluation when frame did not change? */
1125  int frame = volume_sequence_frame(depsgraph, volume);
1126  if (frame != volume->runtime.frame) {
1127  BKE_volume_unload(volume);
1128  volume->runtime.frame = frame;
1129  }
1130 
1131  /* Flush back to original. */
1132  if (DEG_is_active(depsgraph)) {
1133  Volume *volume_orig = (Volume *)DEG_get_original_id(&volume->id);
1134  if (volume_orig->runtime.frame != volume->runtime.frame) {
1135  BKE_volume_unload(volume_orig);
1136  volume_orig->runtime.frame = volume->runtime.frame;
1137  }
1138  }
1139 }
1140 
1142 {
1143  if (!geometry_set.has<VolumeComponent>()) {
1144  return nullptr;
1145  }
1146  VolumeComponent &volume_component = geometry_set.get_component_for_write<VolumeComponent>();
1147  Volume *volume = volume_component.release();
1148  if (volume != nullptr) {
1149  /* Add back, but only as read-only non-owning component. */
1150  volume_component.replace(volume, GeometryOwnershipType::ReadOnly);
1151  }
1152  else {
1153  /* The component was empty, we can remove it. */
1154  geometry_set.remove<VolumeComponent>();
1155  }
1156  return volume;
1157 }
1158 
1160 {
1161  /* Free any evaluated data and restore original data. */
1163 
1164  /* Evaluate modifiers. */
1165  Volume *volume = (Volume *)object->data;
1166  GeometrySet geometry_set;
1167  geometry_set.replace_volume(volume, GeometryOwnershipType::ReadOnly);
1168  volume_evaluate_modifiers(depsgraph, scene, object, geometry_set);
1169 
1170  Volume *volume_eval = take_volume_ownership_from_geometry_set(geometry_set);
1171 
1172  /* If the geometry set did not contain a volume, we still create an empty one. */
1173  if (volume_eval == nullptr) {
1174  volume_eval = BKE_volume_new_for_eval(volume);
1175  }
1176 
1177  /* Assign evaluated object. */
1178  const bool eval_is_owned = (volume != volume_eval);
1179  BKE_object_eval_assign_data(object, &volume_eval->id, eval_is_owned);
1180  object->runtime.geometry_set_eval = new GeometrySet(std::move(geometry_set));
1181 }
1182 
1183 void BKE_volume_grids_backup_restore(Volume *volume, VolumeGridVector *grids, const char *filepath)
1184 {
1185 #ifdef WITH_OPENVDB
1186  /* Restore grids after datablock was re-copied from original by depsgraph,
1187  * we don't want to load them again if possible. */
1189  BLI_assert(volume->runtime.grids != nullptr && grids != nullptr);
1190 
1191  if (!grids->is_loaded()) {
1192  /* No grids loaded in CoW datablock, nothing lost by discarding. */
1193  MEM_delete(grids);
1194  }
1195  else if (!STREQ(volume->filepath, filepath)) {
1196  /* Filepath changed, discard grids from CoW datablock. */
1197  MEM_delete(grids);
1198  }
1199  else {
1200  /* Keep grids from CoW datablock. We might still unload them a little
1201  * later in BKE_volume_eval_geometry if the frame changes. */
1202  MEM_delete(volume->runtime.grids);
1203  volume->runtime.grids = grids;
1204  }
1205 #else
1206  UNUSED_VARS(volume, grids, filepath);
1207 #endif
1208 }
1209 
1210 /* Draw Cache */
1211 
1212 void (*BKE_volume_batch_cache_dirty_tag_cb)(Volume *volume, int mode) = nullptr;
1214 
1216 {
1217  if (volume->batch_cache) {
1219  }
1220 }
1221 
1223 {
1224  if (volume->batch_cache) {
1226  }
1227 }
1228 
1229 /* Grids */
1230 
1231 int BKE_volume_num_grids(const Volume *volume)
1232 {
1233 #ifdef WITH_OPENVDB
1234  return volume->runtime.grids->size();
1235 #else
1236  UNUSED_VARS(volume);
1237  return 0;
1238 #endif
1239 }
1240 
1241 const char *BKE_volume_grids_error_msg(const Volume *volume)
1242 {
1243 #ifdef WITH_OPENVDB
1244  return volume->runtime.grids->error_msg.c_str();
1245 #else
1246  UNUSED_VARS(volume);
1247  return "";
1248 #endif
1249 }
1250 
1251 const char *BKE_volume_grids_frame_filepath(const Volume *volume)
1252 {
1253 #ifdef WITH_OPENVDB
1254  return volume->runtime.grids->filepath;
1255 #else
1256  UNUSED_VARS(volume);
1257  return "";
1258 #endif
1259 }
1260 
1261 const VolumeGrid *BKE_volume_grid_get_for_read(const Volume *volume, int grid_index)
1262 {
1263 #ifdef WITH_OPENVDB
1264  const VolumeGridVector &grids = *volume->runtime.grids;
1265  for (const VolumeGrid &grid : grids) {
1266  if (grid_index-- == 0) {
1267  return &grid;
1268  }
1269  }
1270  return nullptr;
1271 #else
1272  UNUSED_VARS(volume, grid_index);
1273  return nullptr;
1274 #endif
1275 }
1276 
1278 {
1279 #ifdef WITH_OPENVDB
1280  VolumeGridVector &grids = *volume->runtime.grids;
1281  for (VolumeGrid &grid : grids) {
1282  if (grid_index-- == 0) {
1283  return &grid;
1284  }
1285  }
1286  return nullptr;
1287 #else
1288  UNUSED_VARS(volume, grid_index);
1289  return nullptr;
1290 #endif
1291 }
1292 
1294 {
1295  const int num_grids = BKE_volume_num_grids(volume);
1296  if (num_grids == 0) {
1297  return nullptr;
1298  }
1299 
1300  const int index = clamp_i(volume->active_grid, 0, num_grids - 1);
1301  return BKE_volume_grid_get_for_read(volume, index);
1302 }
1303 
1304 const VolumeGrid *BKE_volume_grid_find_for_read(const Volume *volume, const char *name)
1305 {
1306  int num_grids = BKE_volume_num_grids(volume);
1307  for (int i = 0; i < num_grids; i++) {
1308  const VolumeGrid *grid = BKE_volume_grid_get_for_read(volume, i);
1309  if (STREQ(BKE_volume_grid_name(grid), name)) {
1310  return grid;
1311  }
1312  }
1313 
1314  return nullptr;
1315 }
1316 
1317 /* Grid Loading */
1318 
1319 bool BKE_volume_grid_load(const Volume *volume, const VolumeGrid *grid)
1320 {
1321 #ifdef WITH_OPENVDB
1322  VolumeGridVector &grids = *volume->runtime.grids;
1323  const char *volume_name = volume->id.name + 2;
1324  grid->load(volume_name, grids.filepath);
1325  const char *error_msg = grid->error_message();
1326  if (error_msg) {
1327  grids.error_msg = error_msg;
1328  return false;
1329  }
1330  return true;
1331 #else
1332  UNUSED_VARS(volume, grid);
1333  return true;
1334 #endif
1335 }
1336 
1337 void BKE_volume_grid_unload(const Volume *volume, const VolumeGrid *grid)
1338 {
1339 #ifdef WITH_OPENVDB
1340  const char *volume_name = volume->id.name + 2;
1341  grid->unload(volume_name);
1342 #else
1343  UNUSED_VARS(volume, grid);
1344 #endif
1345 }
1346 
1348 {
1349 #ifdef WITH_OPENVDB
1350  return grid->grid_is_loaded();
1351 #else
1352  UNUSED_VARS(grid);
1353  return true;
1354 #endif
1355 }
1356 
1357 /* Grid Metadata */
1358 
1359 const char *BKE_volume_grid_name(const VolumeGrid *volume_grid)
1360 {
1361 #ifdef WITH_OPENVDB
1362  return volume_grid->name();
1363 #else
1364  UNUSED_VARS(volume_grid);
1365  return "density";
1366 #endif
1367 }
1368 
1369 #ifdef WITH_OPENVDB
1370 struct ClearGridOp {
1371  openvdb::GridBase &grid;
1372 
1373  template<typename Grid> void operator()()
1374  {
1375  static_cast<Grid &>(grid).clear();
1376  }
1377 };
1378 void BKE_volume_grid_clear_tree(openvdb::GridBase &grid)
1379 {
1380  const VolumeGridType grid_type = BKE_volume_grid_type_openvdb(grid);
1381  ClearGridOp op{grid};
1382  BKE_volume_grid_type_operation(grid_type, op);
1383 }
1384 void BKE_volume_grid_clear_tree(Volume &volume, VolumeGrid &volume_grid)
1385 {
1386  openvdb::GridBase::Ptr grid = BKE_volume_grid_openvdb_for_write(&volume, &volume_grid, false);
1387  BKE_volume_grid_clear_tree(*grid);
1388 }
1389 
1390 VolumeGridType BKE_volume_grid_type_openvdb(const openvdb::GridBase &grid)
1391 {
1392  if (grid.isType<openvdb::FloatGrid>()) {
1393  return VOLUME_GRID_FLOAT;
1394  }
1395  if (grid.isType<openvdb::Vec3fGrid>()) {
1396  return VOLUME_GRID_VECTOR_FLOAT;
1397  }
1398  if (grid.isType<openvdb::BoolGrid>()) {
1399  return VOLUME_GRID_BOOLEAN;
1400  }
1401  if (grid.isType<openvdb::DoubleGrid>()) {
1402  return VOLUME_GRID_DOUBLE;
1403  }
1404  if (grid.isType<openvdb::Int32Grid>()) {
1405  return VOLUME_GRID_INT;
1406  }
1407  if (grid.isType<openvdb::Int64Grid>()) {
1408  return VOLUME_GRID_INT64;
1409  }
1410  if (grid.isType<openvdb::Vec3IGrid>()) {
1411  return VOLUME_GRID_VECTOR_INT;
1412  }
1413  if (grid.isType<openvdb::Vec3dGrid>()) {
1415  }
1416  if (grid.isType<openvdb::MaskGrid>()) {
1417  return VOLUME_GRID_MASK;
1418  }
1419  if (grid.isType<openvdb::points::PointDataGrid>()) {
1420  return VOLUME_GRID_POINTS;
1421  }
1422  return VOLUME_GRID_UNKNOWN;
1423 }
1424 #endif
1425 
1427 {
1428 #ifdef WITH_OPENVDB
1429  const openvdb::GridBase::Ptr grid = volume_grid->grid();
1430  return BKE_volume_grid_type_openvdb(*grid);
1431 #else
1432  UNUSED_VARS(volume_grid);
1433 #endif
1434  return VOLUME_GRID_UNKNOWN;
1435 }
1436 
1438 {
1439  switch (BKE_volume_grid_type(grid)) {
1440  case VOLUME_GRID_BOOLEAN:
1441  case VOLUME_GRID_FLOAT:
1442  case VOLUME_GRID_DOUBLE:
1443  case VOLUME_GRID_INT:
1444  case VOLUME_GRID_INT64:
1445  case VOLUME_GRID_MASK:
1446  return 1;
1450  return 3;
1451  case VOLUME_GRID_POINTS:
1452  case VOLUME_GRID_UNKNOWN:
1453  return 0;
1454  }
1455 
1456  return 0;
1457 }
1458 
1459 void BKE_volume_grid_transform_matrix(const VolumeGrid *volume_grid, float mat[4][4])
1460 {
1461 #ifdef WITH_OPENVDB
1462  const openvdb::GridBase::Ptr grid = volume_grid->grid();
1463  const openvdb::math::Transform &transform = grid->transform();
1464 
1465  /* Perspective not supported for now, getAffineMap() will leave out the
1466  * perspective part of the transform. */
1467  openvdb::math::Mat4f matrix = transform.baseMap()->getAffineMap()->getMat4();
1468  /* Blender column-major and OpenVDB right-multiplication conventions match. */
1469  for (int col = 0; col < 4; col++) {
1470  for (int row = 0; row < 4; row++) {
1471  mat[col][row] = matrix(col, row);
1472  }
1473  }
1474 #else
1475  unit_m4(mat);
1476  UNUSED_VARS(volume_grid);
1477 #endif
1478 }
1479 
1480 void BKE_volume_grid_transform_matrix_set(struct VolumeGrid *volume_grid, const float mat[4][4])
1481 {
1482 #ifdef WITH_OPENVDB
1483  openvdb::math::Mat4f mat_openvdb;
1484  for (int col = 0; col < 4; col++) {
1485  for (int row = 0; row < 4; row++) {
1486  mat_openvdb(col, row) = mat[col][row];
1487  }
1488  }
1489  openvdb::GridBase::Ptr grid = volume_grid->grid();
1490  grid->setTransform(std::make_shared<openvdb::math::Transform>(
1491  std::make_shared<openvdb::math::AffineMap>(mat_openvdb)));
1492 #else
1493  UNUSED_VARS(volume_grid, mat);
1494 #endif
1495 }
1496 
1497 /* Grid Tree and Voxels */
1498 
1499 /* Volume Editing */
1500 
1502 {
1503  Volume *volume_dst = (Volume *)BKE_id_new_nomain(ID_VO, nullptr);
1504 
1505  STRNCPY(volume_dst->id.name, volume_src->id.name);
1506  volume_dst->mat = (Material **)MEM_dupallocN(volume_src->mat);
1507  volume_dst->totcol = volume_src->totcol;
1508  volume_dst->render = volume_src->render;
1509  volume_dst->display = volume_src->display;
1510  BKE_volume_init_grids(volume_dst);
1511 
1512  return volume_dst;
1513 }
1514 
1515 Volume *BKE_volume_copy_for_eval(Volume *volume_src, bool reference)
1516 {
1517  int flags = LIB_ID_COPY_LOCALIZE;
1518 
1519  if (reference) {
1520  flags |= LIB_ID_COPY_CD_REFERENCE;
1521  }
1522 
1523  Volume *result = (Volume *)BKE_id_copy_ex(nullptr, &volume_src->id, nullptr, flags);
1524 
1525  return result;
1526 }
1527 
1528 #ifdef WITH_OPENVDB
1529 struct CreateGridOp {
1530  template<typename GridType> typename openvdb::GridBase::Ptr operator()()
1531  {
1532  if constexpr (std::is_same_v<GridType, openvdb::points::PointDataGrid>) {
1533  return {};
1534  }
1535  else {
1536  return GridType::create();
1537  }
1538  }
1539 };
1540 #endif
1541 
1543 {
1544 #ifdef WITH_OPENVDB
1545  VolumeGridVector &grids = *volume->runtime.grids;
1546  BLI_assert(BKE_volume_grid_find_for_read(volume, name) == nullptr);
1548 
1549  openvdb::GridBase::Ptr vdb_grid = BKE_volume_grid_type_operation(type, CreateGridOp{});
1550  if (!vdb_grid) {
1551  return nullptr;
1552  }
1553 
1554  vdb_grid->setName(name);
1555  grids.emplace_back(vdb_grid);
1556  return &grids.back();
1557 #else
1558  UNUSED_VARS(volume, name, type);
1559  return nullptr;
1560 #endif
1561 }
1562 
1563 #ifdef WITH_OPENVDB
1564 VolumeGrid *BKE_volume_grid_add_vdb(Volume &volume,
1565  const StringRef name,
1566  openvdb::GridBase::Ptr vdb_grid)
1567 {
1568  VolumeGridVector &grids = *volume.runtime.grids;
1569  BLI_assert(BKE_volume_grid_find_for_read(&volume, name.data()) == nullptr);
1570  BLI_assert(BKE_volume_grid_type_openvdb(*vdb_grid) != VOLUME_GRID_UNKNOWN);
1571 
1572  vdb_grid->setName(name);
1573  grids.emplace_back(vdb_grid);
1574  return &grids.back();
1575 }
1576 #endif
1577 
1579 {
1580 #ifdef WITH_OPENVDB
1581  VolumeGridVector &grids = *volume->runtime.grids;
1582  for (VolumeGridVector::iterator it = grids.begin(); it != grids.end(); it++) {
1583  if (&*it == grid) {
1584  grids.erase(it);
1585  break;
1586  }
1587  }
1588 #else
1589  UNUSED_VARS(volume, grid);
1590 #endif
1591 }
1592 
1594 {
1595 #ifdef WITH_OPENVDB
1596  /* Limit taken from openvdb/math/Maps.h. */
1597  return std::abs(determinant) >= 3.0 * openvdb::math::Tolerance<double>::value();
1598 #else
1600  return true;
1601 #endif
1602 }
1603 
1605 {
1608  if (scene->r.mode & R_SIMPLIFY) {
1609  const float simplify = scene->r.simplify_volumes;
1610  if (simplify == 0.0f) {
1611  /* log2 is not defined at 0.0f, so just use some high simplify level. */
1612  return 16;
1613  }
1614  return ceilf(-log2(simplify));
1615  }
1616  }
1617  return 0;
1618 }
1619 
1621 {
1624  if (scene->r.mode & R_SIMPLIFY) {
1625  return scene->r.simplify_volumes;
1626  }
1627  }
1628  return 1.0f;
1629 }
1630 
1631 /* OpenVDB Grid Access */
1632 
1633 #ifdef WITH_OPENVDB
1634 
1635 bool BKE_volume_grid_bounds(openvdb::GridBase::ConstPtr grid, float3 &r_min, float3 &r_max)
1636 {
1637  /* TODO: we can get this from grid metadata in some cases? */
1638  openvdb::CoordBBox coordbbox;
1639  if (!grid->baseTree().evalLeafBoundingBox(coordbbox)) {
1640  return false;
1641  }
1642 
1643  openvdb::BBoxd bbox = grid->transform().indexToWorld(coordbbox);
1644 
1645  r_min = float3((float)bbox.min().x(), (float)bbox.min().y(), (float)bbox.min().z());
1646  r_max = float3((float)bbox.max().x(), (float)bbox.max().y(), (float)bbox.max().z());
1647 
1648  return true;
1649 }
1650 
1651 openvdb::GridBase::ConstPtr BKE_volume_grid_shallow_transform(openvdb::GridBase::ConstPtr grid,
1653 {
1654  openvdb::math::Transform::Ptr grid_transform = grid->transform().copy();
1655  grid_transform->postMult(openvdb::Mat4d(((float *)transform.values)));
1656 
1657  /* Create a transformed grid. The underlying tree is shared. */
1658  return grid->copyGridReplacingTransform(grid_transform);
1659 }
1660 
1661 openvdb::GridBase::ConstPtr BKE_volume_grid_openvdb_for_metadata(const VolumeGrid *grid)
1662 {
1663  return grid->grid();
1664 }
1665 
1666 openvdb::GridBase::ConstPtr BKE_volume_grid_openvdb_for_read(const Volume *volume,
1667  const VolumeGrid *grid)
1668 {
1669  BKE_volume_grid_load(volume, grid);
1670  return grid->grid();
1671 }
1672 
1673 openvdb::GridBase::Ptr BKE_volume_grid_openvdb_for_write(const Volume *volume,
1674  VolumeGrid *grid,
1675  const bool clear)
1676 {
1677  const char *volume_name = volume->id.name + 2;
1678  if (clear) {
1679  grid->clear_reference(volume_name);
1680  }
1681  else {
1682  VolumeGridVector &grids = *volume->runtime.grids;
1683  grid->duplicate_reference(volume_name, grids.filepath);
1684  }
1685 
1686  return grid->grid();
1687 }
1688 
1689 /* Changing the resolution of a grid. */
1690 
1695 template<typename GridType>
1696 static typename GridType::Ptr create_grid_with_changed_resolution(const GridType &old_grid,
1697  const float resolution_factor)
1698 {
1699  BLI_assert(resolution_factor > 0.0f);
1700 
1701  openvdb::Mat4R xform;
1702  xform.setToScale(openvdb::Vec3d(resolution_factor));
1703  openvdb::tools::GridTransformer transformer{xform};
1704 
1705  typename GridType::Ptr new_grid = old_grid.copyWithNewTree();
1706  transformer.transformGrid<openvdb::tools::BoxSampler>(old_grid, *new_grid);
1707  new_grid->transform() = old_grid.transform();
1708  new_grid->transform().preScale(1.0f / resolution_factor);
1709  new_grid->transform().postTranslate(-new_grid->voxelSize() / 2.0f);
1710  return new_grid;
1711 }
1712 
1713 struct CreateGridWithChangedResolutionOp {
1714  const openvdb::GridBase &grid;
1715  const float resolution_factor;
1716 
1717  template<typename GridType> typename openvdb::GridBase::Ptr operator()()
1718  {
1719  return create_grid_with_changed_resolution(static_cast<const GridType &>(grid),
1720  resolution_factor);
1721  }
1722 };
1723 
1724 openvdb::GridBase::Ptr BKE_volume_grid_create_with_changed_resolution(
1725  const VolumeGridType grid_type,
1726  const openvdb::GridBase &old_grid,
1727  const float resolution_factor)
1728 {
1729  CreateGridWithChangedResolutionOp op{old_grid, resolution_factor};
1730  return BKE_volume_grid_type_operation(grid_type, op);
1731 }
1732 
1733 #endif
void BKE_animdata_free(struct ID *id, bool do_id_user)
Definition: anim_data.c:197
void BKE_animdata_blend_read_data(struct BlendDataReader *reader, struct AnimData *adt)
Definition: anim_data.c:1443
void BKE_animdata_blend_write(struct BlendWriter *writer, struct AnimData *adt)
Definition: anim_data.c:1421
bool BKE_bpath_foreach_path_fixed_process(struct BPathForeachPathData *bpath_data, char *path)
Definition: bpath.c:121
@ BKE_BPATH_FOREACH_PATH_SKIP_PACKED
Definition: BKE_bpath.h:37
@ IDTYPE_FLAGS_APPEND_IS_REUSABLE
Definition: BKE_idtype.h:39
void(* IDTypeForeachCacheFunctionCallback)(struct ID *id, const struct IDCacheKey *cache_key, void **cache_p, uint flags, void *user_data)
Definition: BKE_idtype.h:77
@ LIB_ID_COPY_CD_REFERENCE
Definition: BKE_lib_id.h:156
@ LIB_ID_COPY_LOCALIZE
Definition: BKE_lib_id.h:187
struct ID * BKE_id_copy_ex(struct Main *bmain, const struct ID *id, struct ID **r_newid, int flag)
void * BKE_id_new_nomain(short type, const char *name)
Definition: lib_id.c:1173
void BKE_id_blend_write(struct BlendWriter *writer, struct ID *id)
Definition: lib_id.c:2008
void * BKE_id_new(struct Main *bmain, short type, const char *name)
Definition: lib_id.c:1159
#define BKE_LIB_FOREACHID_PROCESS_IDSUPER(_data, _id_super, _cb_flag)
@ IDWALK_CB_USER
Definition: BKE_lib_query.h:73
const ModifierTypeInfo * BKE_modifier_get_info(ModifierType type)
bool BKE_modifier_is_enabled(const struct Scene *scene, struct ModifierData *md, int required_mode)
struct ModifierData * BKE_modifiers_get_virtual_modifierlist(const struct Object *ob, struct VirtualModifierData *data)
void BKE_modifiers_clear_errors(struct Object *ob)
ModifierApplyFlag
Definition: BKE_modifier.h:113
@ MOD_APPLY_USECACHE
Definition: BKE_modifier.h:118
@ MOD_APPLY_RENDER
Definition: BKE_modifier.h:115
General operations, lookup, etc. for blender objects.
void BKE_boundbox_init_from_minmax(struct BoundBox *bb, const float min[3], const float max[3])
Definition: object.cc:3645
void BKE_object_free_derived_caches(struct Object *ob)
Definition: object.cc:1774
void BKE_object_eval_assign_data(struct Object *object, struct ID *data, bool is_owned)
Definition: object.cc:1745
struct PackedFile * BKE_packedfile_duplicate(const struct PackedFile *pf_src)
void BKE_packedfile_blend_write(struct BlendWriter *writer, struct PackedFile *pf)
Definition: packedFile.c:855
void BKE_packedfile_blend_read(struct BlendDataReader *reader, struct PackedFile **pf_p)
Definition: packedFile.c:864
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
Volume data-block.
VolumeGridType
Definition: BKE_volume.h:90
@ VOLUME_GRID_VECTOR_FLOAT
Definition: BKE_volume.h:98
@ VOLUME_GRID_MASK
Definition: BKE_volume.h:97
@ VOLUME_GRID_VECTOR_DOUBLE
Definition: BKE_volume.h:99
@ VOLUME_GRID_VECTOR_INT
Definition: BKE_volume.h:100
@ VOLUME_GRID_UNKNOWN
Definition: BKE_volume.h:91
@ VOLUME_GRID_DOUBLE
Definition: BKE_volume.h:94
@ VOLUME_GRID_BOOLEAN
Definition: BKE_volume.h:92
@ VOLUME_GRID_INT
Definition: BKE_volume.h:95
@ VOLUME_GRID_INT64
Definition: BKE_volume.h:96
@ VOLUME_GRID_POINTS
Definition: BKE_volume.h:101
@ VOLUME_GRID_FLOAT
Definition: BKE_volume.h:93
struct VolumeGrid VolumeGrid
Definition: BKE_volume.h:65
#define BLI_assert(a)
Definition: BLI_assert.h:46
File and directory operations.
int BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: storage.c:314
struct Entry Entry
size_t BLI_ghashutil_combine_hash(size_t hash_a, size_t hash_b)
KDTree *BLI_kdtree_nd_() new(unsigned int maxsize)
Definition: kdtree_impl.h:85
MINLINE int clamp_i(int value, int min, int max)
void unit_m4(float m[4][4])
Definition: rct.c:1090
bool BLI_path_frame(char *path, int frame, int digits) ATTR_NONNULL()
Definition: path_util.c:709
#define FILE_MAX
bool BLI_path_extension_ensure(char *path, size_t maxlen, const char *ext) ATTR_NONNULL()
Definition: path_util.c:1420
bool BLI_path_frame_get(char *path, int *r_frame, int *r_digits_len) ATTR_NONNULL()
Definition: path_util.c:753
void BLI_path_frame_strip(char *path, char *r_ext, size_t ext_maxlen) ATTR_NONNULL()
Definition: path_util.c:805
void BLI_split_file_part(const char *string, char *file, size_t filelen)
Definition: path_util.c:1495
bool BLI_path_abs(char *path, const char *basepath) ATTR_NONNULL()
Definition: path_util.c:897
#define STRNCPY(dst, src)
Definition: BLI_string.h:483
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t maxncpy) ATTR_NONNULL()
Definition: string.c:64
#define INIT_MINMAX(min, max)
#define UNUSED_VARS(...)
#define UNUSED(x)
#define DO_MAX(vec, max)
#define MEMCMP_STRUCT_AFTER_IS_ZERO(struct_var, member)
#define DO_MIN(vec, min)
#define MEMCPY_STRUCT_AFTER(struct_dst, struct_src, member)
#define STREQ(a, b)
#define BLO_read_data_address(reader, ptr_p)
#define BLO_write_id_struct(writer, struct_name, id_address, id)
#define BLO_read_id_address(reader, lib, id_ptr_p)
#define BLO_expand(expander, id)
void BLO_read_pointer_array(BlendDataReader *reader, void **ptr_p)
Definition: readfile.c:5245
bool BLO_write_is_undo(BlendWriter *writer)
Definition: writefile.c:1608
void BLO_write_pointer_array(BlendWriter *writer, uint num, const void *data_ptr)
Definition: writefile.c:1591
#define BLT_I18NCONTEXT_ID_VOLUME
#define CLOG_INFO(clg_ref, level,...)
Definition: CLG_log.h:187
ThreadMutex mutex
struct Depsgraph Depsgraph
Definition: DEG_depsgraph.h:35
bool DEG_is_active(const struct Depsgraph *depsgraph)
Definition: depsgraph.cc:312
@ DAG_EVAL_RENDER
Definition: DEG_depsgraph.h:46
float DEG_get_ctime(const Depsgraph *graph)
struct Scene * DEG_get_input_scene(const Depsgraph *graph)
eEvaluationMode DEG_get_mode(const Depsgraph *graph)
struct ID * DEG_get_original_id(struct ID *id)
@ INDEX_ID_VO
Definition: DNA_ID.h:1031
#define ID_BLEND_PATH(_bmain, _id)
Definition: DNA_ID.h:559
@ LIB_TAG_COPIED_ON_WRITE
Definition: DNA_ID.h:720
#define ID_IS_OVERRIDE_LIBRARY(_id)
Definition: DNA_ID.h:588
#define FILTER_ID_VO
Definition: DNA_ID.h:932
@ ID_VO
Definition: DNA_ID_enums.h:83
#define DNA_struct_default_get(struct_name)
Definition: DNA_defaults.h:29
@ eModifierMode_Render
@ eModifierMode_Realtime
ModifierType
Object is a sort of wrapper for general info.
@ OB_VOLUME
@ BOUNDBOX_DIRTY
#define R_SIMPLIFY
VolumeSequenceMode
@ VOLUME_SEQUENCE_REPEAT
@ VOLUME_SEQUENCE_CLIP
@ VOLUME_SEQUENCE_EXTEND
@ VOLUME_SEQUENCE_PING_PONG
struct Volume Volume
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
float float4x4[4][4]
float float3[3]
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
volatile int lock
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
SIMD_FORCE_INLINE btVector3 transform(const btVector3 &point) const
btScalar determinant() const
Return the determinant of the matrix.
Definition: btMatrix3x3.h:1022
SIMD_FORCE_INLINE btVector3 operator()(const btVector3 &x) const
Return the transform of the vector.
Definition: btTransform.h:90
static ImGlobalTileCache GLOBAL_CACHE
Definition: cache.c:76
void replace(Volume *volume, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
constexpr const char * data() const
FILE * file
Scene scene
const Depsgraph * depsgraph
void * user_data
SyclQueue void void size_t num_bytes void
uint col
CCL_NAMESPACE_BEGIN struct Transform Transform
#define LOG(severity)
Definition: log.h:36
void *(* MEM_dupallocN)(const void *vmemh)
Definition: mallocn.c:28
#define G(x, y, z)
#define ceilf(x)
Definition: metal/compat.h:225
static void clear(Message *msg)
Definition: msgfmt.c:278
VecMat::Vec3< double > Vec3d
Definition: Geom.h:27
static unsigned a[3]
Definition: RandGen.cpp:78
std::unique_ptr< IDProperty, IDPropertyDeleter > create(StringRefNull prop_name, int32_t value)
Allocate a new IDProperty of type IDP_INT, set its name and value.
std::unique_ptr< T > Ptr
Definition: BLI_any.hh:58
T abs(const T &a)
void isolate_task(const Function &function)
Definition: BLI_task.hh:125
vec_base< float, 3 > float3
Eigen::Matrix< float, 4, 4 > Mat4f
Definition: numeric.h:85
static const pxr::TfToken b("b", pxr::TfToken::Immortal)
#define min(a, b)
Definition: sort.c:35
eBPathForeachFlag flag
Definition: BKE_bpath.h:78
void remove(const GeometryComponentType component_type)
GeometryComponent & get_component_for_write(GeometryComponentType component_type)
bool has(const GeometryComponentType component_type) const
void replace_volume(Volume *volume, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
Definition: DNA_ID.h:368
int tag
Definition: DNA_ID.h:387
struct Library * lib
Definition: DNA_ID.h:372
char name[66]
Definition: DNA_ID.h:378
Definition: BKE_main.h:121
struct ModifierData * next
void(* modifyGeometrySet)(struct ModifierData *md, const struct ModifierEvalContext *ctx, struct GeometrySet *geometry_set)
Definition: BKE_modifier.h:240
struct BoundBox * bb
Object_Runtime runtime
void * data
float simplify_volumes
struct RenderData r
struct VolumeGridVector * grids
char velocity_x_grid[64]
char velocity_z_grid[64]
char velocity_y_grid[64]
int frame_duration
char filepath[1024]
int frame_start
char is_sequence
int frame_offset
void * batch_cache
struct PackedFile * packedfile
char velocity_grid[64]
short totcol
struct Material ** mat
VolumeRender render
VolumeDisplay display
char sequence_mode
Volume_Runtime runtime
int active_grid
struct AnimData * adt
static void initialize(SubdivDisplacement *displacement)
float max
const VolumeGrid * BKE_volume_grid_active_get_for_read(const Volume *volume)
Definition: volume.cc:1293
const char * BKE_volume_grids_error_msg(const Volume *volume)
Definition: volume.cc:1241
#define VOLUME_FRAME_NONE
Definition: volume.cc:57
int BKE_volume_grid_channels(const VolumeGrid *grid)
Definition: volume.cc:1437
bool BKE_volume_grid_is_loaded(const VolumeGrid *grid)
Definition: volume.cc:1347
bool BKE_volume_is_loaded(const Volume *volume)
Definition: volume.cc:785
void BKE_volume_batch_cache_free(Volume *volume)
Definition: volume.cc:1222
bool BKE_volume_is_y_up(const Volume *volume)
Definition: volume.cc:1033
static void volume_evaluate_modifiers(struct Depsgraph *depsgraph, struct Scene *scene, Object *object, GeometrySet &geometry_set)
Definition: volume.cc:1087
int BKE_volume_num_grids(const Volume *volume)
Definition: volume.cc:1231
IDTypeInfo IDType_ID_VO
Definition: volume.cc:649
const VolumeGrid * BKE_volume_grid_get_for_read(const Volume *volume, int grid_index)
Definition: volume.cc:1261
const char * BKE_volume_grid_name(const VolumeGrid *volume_grid)
Definition: volume.cc:1359
bool BKE_volume_save(const Volume *volume, const Main *bmain, ReportList *reports, const char *filepath)
Definition: volume.cc:946
const char * BKE_volume_grids_frame_filepath(const Volume *volume)
Definition: volume.cc:1251
void BKE_volume_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Object *object)
Definition: volume.cc:1159
void BKE_volume_batch_cache_dirty_tag(Volume *volume, int mode)
Definition: volume.cc:1215
bool BKE_volume_load(const Volume *volume, const Main *bmain)
Definition: volume.cc:847
void BKE_volume_grid_remove(Volume *volume, VolumeGrid *grid)
Definition: volume.cc:1578
static void volume_update_simplify_level(Volume *volume, const Depsgraph *depsgraph)
Definition: volume.cc:1072
bool BKE_volume_is_points_only(const Volume *volume)
Definition: volume.cc:1053
static void volume_init_data(ID *id)
Definition: volume.cc:509
bool BKE_volume_min_max(const Volume *volume, float3 &r_min, float3 &r_max)
Definition: volume.cc:981
static int volume_sequence_frame(const Depsgraph *depsgraph, const Volume *volume)
Definition: volume.cc:699
void(* BKE_volume_batch_cache_dirty_tag_cb)(Volume *volume, int mode)
Definition: volume.cc:1212
void * BKE_volume_add(Main *bmain, const char *name)
Definition: volume.cc:690
VolumeGridType BKE_volume_grid_type(const VolumeGrid *volume_grid)
Definition: volume.cc:1426
VolumeGrid * BKE_volume_grid_get_for_write(Volume *volume, int grid_index)
Definition: volume.cc:1277
bool BKE_volume_grid_determinant_valid(const double determinant)
Definition: volume.cc:1593
static void volume_blend_read_expand(BlendExpander *expander, ID *id)
Definition: volume.cc:641
static void volume_foreach_path(ID *id, BPathForeachPathData *bpath_data)
Definition: volume.cc:577
void BKE_volume_grid_transform_matrix_set(struct VolumeGrid *volume_grid, const float mat[4][4])
Definition: volume.cc:1480
Volume * BKE_volume_copy_for_eval(Volume *volume_src, bool reference)
Definition: volume.cc:1515
void BKE_volumes_init()
Definition: volume.cc:500
static void volume_foreach_cache(ID *id, IDTypeForeachCacheFunctionCallback function_callback, void *user_data)
Definition: volume.cc:564
int BKE_volume_simplify_level(const Depsgraph *depsgraph)
Definition: volume.cc:1604
static void volume_free_data(ID *id)
Definition: volume.cc:544
void BKE_volume_grid_transform_matrix(const VolumeGrid *volume_grid, float mat[4][4])
Definition: volume.cc:1459
float BKE_volume_simplify_factor(const Depsgraph *depsgraph)
Definition: volume.cc:1620
void BKE_volume_unload(Volume *volume)
Definition: volume.cc:930
void(* BKE_volume_batch_cache_free_cb)(Volume *volume)
Definition: volume.cc:1213
static void volume_blend_write(BlendWriter *writer, ID *id, const void *id_address)
Definition: volume.cc:589
void BKE_volume_eval_geometry(struct Depsgraph *depsgraph, Volume *volume)
Definition: volume.cc:1120
static void volume_blend_read_data(BlendDataReader *reader, ID *id)
Definition: volume.cc:615
bool BKE_volume_set_velocity_grid_by_name(Volume *volume, const char *base_name)
Definition: volume.cc:796
static Volume * take_volume_ownership_from_geometry_set(GeometrySet &geometry_set)
Definition: volume.cc:1141
bool BKE_volume_grid_load(const Volume *volume, const VolumeGrid *grid)
Definition: volume.cc:1319
const VolumeGrid * BKE_volume_grid_find_for_read(const Volume *volume, const char *name)
Definition: volume.cc:1304
static void volume_foreach_id(ID *id, LibraryForeachIDData *data)
Definition: volume.cc:556
Volume * BKE_volume_new_for_eval(const Volume *volume_src)
Definition: volume.cc:1501
void BKE_volume_grid_unload(const Volume *volume, const VolumeGrid *grid)
Definition: volume.cc:1337
static void volume_blend_read_lib(BlendLibReader *reader, ID *id)
Definition: volume.cc:628
BoundBox * BKE_volume_boundbox_get(Object *ob)
Definition: volume.cc:1007
static void volume_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, const int UNUSED(flag))
Definition: volume.cc:521
void BKE_volume_grids_backup_restore(Volume *volume, VolumeGridVector *grids, const char *filepath)
Definition: volume.cc:1183
void BKE_volume_init_grids(Volume *volume)
Definition: volume.cc:679
VolumeGrid * BKE_volume_grid_add(Volume *volume, const char *name, VolumeGridType type)
Definition: volume.cc:1542