Changeset View
Changeset View
Standalone View
Standalone View
source/blender/blenkernel/intern/volume.cc
| Show All 22 Lines | |||||
| #include "DNA_defaults.h" | #include "DNA_defaults.h" | ||||
| #include "DNA_material_types.h" | #include "DNA_material_types.h" | ||||
| #include "DNA_object_types.h" | #include "DNA_object_types.h" | ||||
| #include "DNA_scene_types.h" | #include "DNA_scene_types.h" | ||||
| #include "DNA_volume_types.h" | #include "DNA_volume_types.h" | ||||
| #include "BLI_compiler_compat.h" | #include "BLI_compiler_compat.h" | ||||
| #include "BLI_fileops.h" | #include "BLI_fileops.h" | ||||
| #include "BLI_float3.hh" | |||||
| #include "BLI_float4x4.hh" | |||||
| #include "BLI_ghash.h" | #include "BLI_ghash.h" | ||||
| #include "BLI_index_range.hh" | |||||
| #include "BLI_map.hh" | #include "BLI_map.hh" | ||||
| #include "BLI_math.h" | #include "BLI_math.h" | ||||
| #include "BLI_path_util.h" | #include "BLI_path_util.h" | ||||
| #include "BLI_string.h" | #include "BLI_string.h" | ||||
| #include "BLI_utildefines.h" | #include "BLI_utildefines.h" | ||||
| #include "BKE_anim_data.h" | #include "BKE_anim_data.h" | ||||
| #include "BKE_geometry_set.hh" | |||||
| #include "BKE_global.h" | #include "BKE_global.h" | ||||
| #include "BKE_idtype.h" | #include "BKE_idtype.h" | ||||
| #include "BKE_lib_id.h" | #include "BKE_lib_id.h" | ||||
| #include "BKE_lib_query.h" | #include "BKE_lib_query.h" | ||||
| #include "BKE_lib_remap.h" | #include "BKE_lib_remap.h" | ||||
| #include "BKE_main.h" | #include "BKE_main.h" | ||||
| #include "BKE_modifier.h" | #include "BKE_modifier.h" | ||||
| #include "BKE_object.h" | #include "BKE_object.h" | ||||
| Show All 11 Lines | |||||
| #include "CLG_log.h" | #include "CLG_log.h" | ||||
| #ifdef WITH_OPENVDB | #ifdef WITH_OPENVDB | ||||
| static CLG_LogRef LOG = {"bke.volume"}; | static CLG_LogRef LOG = {"bke.volume"}; | ||||
| #endif | #endif | ||||
| #define VOLUME_FRAME_NONE INT_MAX | #define VOLUME_FRAME_NONE INT_MAX | ||||
| using blender::float3; | |||||
| using blender::float4x4; | |||||
| using blender::IndexRange; | |||||
| #ifdef WITH_OPENVDB | #ifdef WITH_OPENVDB | ||||
| # include <atomic> | # include <atomic> | ||||
| # include <list> | # include <list> | ||||
| # include <mutex> | # include <mutex> | ||||
| # include <unordered_set> | # include <unordered_set> | ||||
| # include <openvdb/openvdb.h> | # include <openvdb/openvdb.h> | ||||
| # include <openvdb/points/PointDataGrid.h> | # include <openvdb/points/PointDataGrid.h> | ||||
| ▲ Show 20 Lines • Show All 65 Lines • ▼ Show 20 Lines | struct Entry { | ||||
| /* OpenVDB grid. */ | /* OpenVDB grid. */ | ||||
| openvdb::GridBase::Ptr grid; | openvdb::GridBase::Ptr grid; | ||||
| /* Simplified versions of #grid. The integer key is the simplification level. */ | /* Simplified versions of #grid. The integer key is the simplification level. */ | ||||
| blender::Map<int, openvdb::GridBase::Ptr> simplified_grids; | blender::Map<int, openvdb::GridBase::Ptr> simplified_grids; | ||||
| /* Has the grid tree been loaded? */ | /* Has the grid tree been loaded? */ | ||||
| bool is_loaded; | mutable bool is_loaded; | ||||
| /* Error message if an error occurred while loading. */ | /* Error message if an error occurred while loading. */ | ||||
| std::string error_msg; | std::string error_msg; | ||||
| /* User counting. */ | /* User counting. */ | ||||
| int num_metadata_users; | int num_metadata_users; | ||||
| int num_tree_users; | int num_tree_users; | ||||
| /* Mutex for on-demand reading of tree. */ | /* Mutex for on-demand reading of tree. */ | ||||
| std::mutex mutex; | mutable std::mutex mutex; | ||||
| }; | }; | ||||
| struct EntryHasher { | struct EntryHasher { | ||||
| std::size_t operator()(const Entry &entry) const | std::size_t operator()(const Entry &entry) const | ||||
| { | { | ||||
| std::hash<std::string> string_hasher; | std::hash<std::string> string_hasher; | ||||
| return BLI_ghashutil_combine_hash(string_hasher(entry.filepath), | return BLI_ghashutil_combine_hash(string_hasher(entry.filepath), | ||||
| string_hasher(entry.grid_name)); | string_hasher(entry.grid_name)); | ||||
| } | } | ||||
| }; | }; | ||||
| struct EntryEqual { | struct EntryEqual { | ||||
| bool operator()(const Entry &a, const Entry &b) const | bool operator()(const Entry &a, const Entry &b) const | ||||
| { | { | ||||
| return a.filepath == b.filepath && a.grid_name == b.grid_name; | return a.filepath == b.filepath && a.grid_name == b.grid_name; | ||||
| } | } | ||||
| }; | }; | ||||
| /* Cache */ | /* Cache */ | ||||
| VolumeFileCache() | |||||
| { | |||||
| } | |||||
| ~VolumeFileCache() | ~VolumeFileCache() | ||||
| { | { | ||||
| BLI_assert(cache.empty()); | BLI_assert(cache.empty()); | ||||
| } | } | ||||
| Entry *add_metadata_user(const Entry &template_entry) | Entry *add_metadata_user(const Entry &template_entry) | ||||
| { | { | ||||
| std::lock_guard<std::mutex> lock(mutex); | std::lock_guard<std::mutex> lock(mutex); | ||||
| ▲ Show 20 Lines • Show All 102 Lines • ▼ Show 20 Lines | struct VolumeGrid { | ||||
| ~VolumeGrid() | ~VolumeGrid() | ||||
| { | { | ||||
| if (entry) { | if (entry) { | ||||
| GLOBAL_CACHE.remove_user(*entry, is_loaded); | GLOBAL_CACHE.remove_user(*entry, is_loaded); | ||||
| } | } | ||||
| } | } | ||||
| void load(const char *volume_name, const char *filepath) | void load(const char *volume_name, const char *filepath) const | ||||
| { | { | ||||
| /* If already loaded or not file-backed, nothing to do. */ | /* If already loaded or not file-backed, nothing to do. */ | ||||
| if (is_loaded || entry == nullptr) { | if (is_loaded || entry == nullptr) { | ||||
| return; | return; | ||||
| } | } | ||||
| /* Double-checked lock. */ | /* Double-checked lock. */ | ||||
| std::lock_guard<std::mutex> lock(entry->mutex); | std::lock_guard<std::mutex> lock(entry->mutex); | ||||
| Show All 25 Lines | catch (const openvdb::IoError &e) { | ||||
| entry->error_msg = e.what(); | entry->error_msg = e.what(); | ||||
| } | } | ||||
| std::atomic_thread_fence(std::memory_order_release); | std::atomic_thread_fence(std::memory_order_release); | ||||
| entry->is_loaded = true; | entry->is_loaded = true; | ||||
| is_loaded = true; | is_loaded = true; | ||||
| } | } | ||||
| void unload(const char *volume_name) | void unload(const char *volume_name) const | ||||
| { | { | ||||
| /* Not loaded or not file-backed, nothing to do. */ | /* Not loaded or not file-backed, nothing to do. */ | ||||
| if (!is_loaded || entry == nullptr) { | if (!is_loaded || entry == nullptr) { | ||||
| return; | return; | ||||
| } | } | ||||
| /* Double-checked lock. */ | /* Double-checked lock. */ | ||||
| std::lock_guard<std::mutex> lock(entry->mutex); | std::lock_guard<std::mutex> lock(entry->mutex); | ||||
| ▲ Show 20 Lines • Show All 84 Lines • ▼ Show 20 Lines | protected: | ||||
| /* File cache entry when grid comes directly from a file and may be shared | /* File cache entry when grid comes directly from a file and may be shared | ||||
| * with other volume datablocks. */ | * with other volume datablocks. */ | ||||
| VolumeFileCache::Entry *entry; | VolumeFileCache::Entry *entry; | ||||
| /* If this volume grid is in the global file cache, we can reference a simplified version of it, | /* If this volume grid is in the global file cache, we can reference a simplified version of it, | ||||
| * instead of the original high resolution grid. */ | * instead of the original high resolution grid. */ | ||||
| int simplify_level = 0; | int simplify_level = 0; | ||||
| /* OpenVDB grid if it's not shared through the file cache. */ | /* OpenVDB grid if it's not shared through the file cache. */ | ||||
| openvdb::GridBase::Ptr local_grid; | openvdb::GridBase::Ptr local_grid; | ||||
| /* Indicates if the tree has been loaded for this grid. Note that vdb.tree() | /** | ||||
| * Indicates if the tree has been loaded for this grid. Note that vdb.tree() | |||||
| * may actually be loaded by another user while this is false. But only after | * may actually be loaded by another user while this is false. But only after | ||||
| * calling load() and is_loaded changes to true is it safe to access. */ | * calling load() and is_loaded changes to true is it safe to access. | ||||
| bool is_loaded; | * | ||||
| * Const write access to this must be protected by `entry->mutex`. | |||||
| */ | |||||
| mutable bool is_loaded; | |||||
| }; | }; | ||||
| /* Volume Grid Vector | /* Volume Grid Vector | ||||
| * | * | ||||
| * List of grids contained in a volume datablock. This is runtime-only data, | * List of grids contained in a volume datablock. This is runtime-only data, | ||||
| * the actual grids are always saved in a VDB file. */ | * the actual grids are always saved in a VDB file. */ | ||||
| struct VolumeGridVector : public std::list<VolumeGrid> { | struct VolumeGridVector : public std::list<VolumeGrid> { | ||||
| Show All 16 Lines | struct VolumeGridVector : public std::list<VolumeGrid> { | ||||
| void clear_all() | void clear_all() | ||||
| { | { | ||||
| std::list<VolumeGrid>::clear(); | std::list<VolumeGrid>::clear(); | ||||
| filepath[0] = '\0'; | filepath[0] = '\0'; | ||||
| error_msg.clear(); | error_msg.clear(); | ||||
| metadata.reset(); | metadata.reset(); | ||||
| } | } | ||||
| /* Mutex for file loading of grids list. Const write access to the fields after this must be | |||||
| * protected by locking with this mutex. */ | |||||
| mutable std::mutex mutex; | |||||
| /* Absolute file path that grids have been loaded from. */ | /* Absolute file path that grids have been loaded from. */ | ||||
| char filepath[FILE_MAX]; | char filepath[FILE_MAX]; | ||||
| /* File loading error message. */ | /* File loading error message. */ | ||||
| std::string error_msg; | std::string error_msg; | ||||
| /* File Metadata. */ | /* File Metadata. */ | ||||
| openvdb::MetaMap::Ptr metadata; | openvdb::MetaMap::Ptr metadata; | ||||
| /* Mutex for file loading of grids list. */ | |||||
| std::mutex mutex; | |||||
| }; | }; | ||||
| #endif | #endif | ||||
| /* Module */ | /* Module */ | ||||
| void BKE_volumes_init() | void BKE_volumes_init() | ||||
| { | { | ||||
| #ifdef WITH_OPENVDB | #ifdef WITH_OPENVDB | ||||
| ▲ Show 20 Lines • Show All 270 Lines • ▼ Show 20 Lines | #ifdef WITH_OPENVDB | ||||
| /* Test if there is a file to load, or if already loaded. */ | /* Test if there is a file to load, or if already loaded. */ | ||||
| return (volume->filepath[0] == '\0' || volume->runtime.grids->is_loaded()); | return (volume->filepath[0] == '\0' || volume->runtime.grids->is_loaded()); | ||||
| #else | #else | ||||
| UNUSED_VARS(volume); | UNUSED_VARS(volume); | ||||
| return true; | return true; | ||||
| #endif | #endif | ||||
| } | } | ||||
| bool BKE_volume_load(Volume *volume, Main *bmain) | bool BKE_volume_load(const Volume *volume, const Main *bmain) | ||||
| { | { | ||||
| #ifdef WITH_OPENVDB | #ifdef WITH_OPENVDB | ||||
| VolumeGridVector &grids = *volume->runtime.grids; | const VolumeGridVector &const_grids = *volume->runtime.grids; | ||||
| if (volume->runtime.frame == VOLUME_FRAME_NONE) { | if (volume->runtime.frame == VOLUME_FRAME_NONE) { | ||||
| /* Skip loading this frame, outside of sequence range. */ | /* Skip loading this frame, outside of sequence range. */ | ||||
| return true; | return true; | ||||
| } | } | ||||
| if (BKE_volume_is_loaded(volume)) { | if (BKE_volume_is_loaded(volume)) { | ||||
| return grids.error_msg.empty(); | return const_grids.error_msg.empty(); | ||||
| } | } | ||||
| /* Double-checked lock. */ | /* Double-checked lock. */ | ||||
| std::lock_guard<std::mutex> lock(grids.mutex); | std::lock_guard<std::mutex> lock(const_grids.mutex); | ||||
| if (BKE_volume_is_loaded(volume)) { | if (BKE_volume_is_loaded(volume)) { | ||||
| return grids.error_msg.empty(); | return const_grids.error_msg.empty(); | ||||
| } | } | ||||
| /* Guarded by the lock, we can continue to access the grid vector, | |||||
| * adding error messages or a new grid, etc. */ | |||||
| VolumeGridVector &grids = const_cast<VolumeGridVector &>(const_grids); | |||||
| /* Get absolute file path at current frame. */ | /* Get absolute file path at current frame. */ | ||||
| const char *volume_name = volume->id.name + 2; | const char *volume_name = volume->id.name + 2; | ||||
| char filepath[FILE_MAX]; | char filepath[FILE_MAX]; | ||||
| volume_filepath_get(bmain, volume, filepath); | volume_filepath_get(bmain, volume, filepath); | ||||
| CLOG_INFO(&LOG, 1, "Volume %s: load %s", volume_name, filepath); | CLOG_INFO(&LOG, 1, "Volume %s: load %s", volume_name, filepath); | ||||
| /* Test if file exists. */ | /* Test if file exists. */ | ||||
| ▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | #ifdef WITH_OPENVDB | ||||
| } | } | ||||
| #else | #else | ||||
| UNUSED_VARS(volume); | UNUSED_VARS(volume); | ||||
| #endif | #endif | ||||
| } | } | ||||
| /* File Save */ | /* File Save */ | ||||
| bool BKE_volume_save(Volume *volume, Main *bmain, ReportList *reports, const char *filepath) | bool BKE_volume_save(const Volume *volume, | ||||
| const Main *bmain, | |||||
| ReportList *reports, | |||||
| const char *filepath) | |||||
| { | { | ||||
| #ifdef WITH_OPENVDB | #ifdef WITH_OPENVDB | ||||
| if (!BKE_volume_load(volume, bmain)) { | if (!BKE_volume_load(volume, bmain)) { | ||||
| BKE_reportf(reports, RPT_ERROR, "Could not load volume for writing"); | BKE_reportf(reports, RPT_ERROR, "Could not load volume for writing"); | ||||
| return false; | return false; | ||||
| } | } | ||||
| VolumeGridVector &grids = *volume->runtime.grids; | VolumeGridVector &grids = *volume->runtime.grids; | ||||
| Show All 15 Lines | #ifdef WITH_OPENVDB | ||||
| return true; | return true; | ||||
| #else | #else | ||||
| UNUSED_VARS(volume, bmain, reports, filepath); | UNUSED_VARS(volume, bmain, reports, filepath); | ||||
| return false; | return false; | ||||
| #endif | #endif | ||||
| } | } | ||||
| bool BKE_volume_min_max(const Volume *volume, float3 &r_min, float3 &r_max) | |||||
| { | |||||
| bool have_minmax = false; | |||||
| #ifdef WITH_OPENVDB | |||||
| /* TODO: if we know the volume is going to be displayed, it may be good to | |||||
| * load it as part of dependency graph evaluation for better threading. We | |||||
| * could also share the bounding box computation in the global volume cache. */ | |||||
| if (BKE_volume_load(const_cast<Volume *>(volume), G.main)) { | |||||
| for (const int i : IndexRange(BKE_volume_num_grids(volume))) { | |||||
| const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, i); | |||||
| openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid); | |||||
| float3 grid_min; | |||||
| float3 grid_max; | |||||
| if (BKE_volume_grid_bounds(grid, grid_min, grid_max)) { | |||||
| DO_MIN(grid_min, r_min); | |||||
| DO_MAX(grid_max, r_max); | |||||
| have_minmax = true; | |||||
| } | |||||
| } | |||||
| } | |||||
| #else | |||||
| UNUSED_VARS(volume, r_min, r_max); | |||||
| #endif | |||||
| return have_minmax; | |||||
| } | |||||
| BoundBox *BKE_volume_boundbox_get(Object *ob) | BoundBox *BKE_volume_boundbox_get(Object *ob) | ||||
| { | { | ||||
| BLI_assert(ob->type == OB_VOLUME); | BLI_assert(ob->type == OB_VOLUME); | ||||
| if (ob->runtime.bb != nullptr && (ob->runtime.bb->flag & BOUNDBOX_DIRTY) == 0) { | if (ob->runtime.bb != nullptr && (ob->runtime.bb->flag & BOUNDBOX_DIRTY) == 0) { | ||||
| return ob->runtime.bb; | return ob->runtime.bb; | ||||
| } | } | ||||
| if (ob->runtime.bb == nullptr) { | if (ob->runtime.bb == nullptr) { | ||||
| Volume *volume = (Volume *)ob->data; | ob->runtime.bb = (BoundBox *)MEM_callocN(sizeof(BoundBox), __func__); | ||||
| } | |||||
| ob->runtime.bb = (BoundBox *)MEM_callocN(sizeof(BoundBox), "volume boundbox"); | const Volume *volume = (Volume *)ob->data; | ||||
| float min[3], max[3]; | float3 min, max; | ||||
| bool have_minmax = false; | |||||
| INIT_MINMAX(min, max); | INIT_MINMAX(min, max); | ||||
| if (!BKE_volume_min_max(volume, min, max)) { | |||||
| /* TODO: if we know the volume is going to be displayed, it may be good to | min = float3(-1); | ||||
| * load it as part of dependency graph evaluation for better threading. We | max = float3(1); | ||||
| * could also share the bounding box computation in the global volume cache. */ | |||||
| if (BKE_volume_load(volume, G.main)) { | |||||
| const int num_grids = BKE_volume_num_grids(volume); | |||||
| for (int i = 0; i < num_grids; i++) { | |||||
| VolumeGrid *grid = BKE_volume_grid_get(volume, i); | |||||
| float grid_min[3], grid_max[3]; | |||||
| BKE_volume_grid_load(volume, grid); | |||||
| if (BKE_volume_grid_bounds(grid, grid_min, grid_max)) { | |||||
| DO_MIN(grid_min, min); | |||||
| DO_MAX(grid_max, max); | |||||
| have_minmax = true; | |||||
| } | |||||
| } | |||||
| } | |||||
| if (!have_minmax) { | |||||
| min[0] = min[1] = min[2] = -1.0f; | |||||
| max[0] = max[1] = max[2] = 1.0f; | |||||
| } | } | ||||
| BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max); | BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max); | ||||
| } | |||||
| return ob->runtime.bb; | return ob->runtime.bb; | ||||
| } | } | ||||
| bool BKE_volume_is_y_up(const Volume *volume) | bool BKE_volume_is_y_up(const Volume *volume) | ||||
| { | { | ||||
| /* Simple heuristic for common files to open the right way up. */ | /* Simple heuristic for common files to open the right way up. */ | ||||
| #ifdef WITH_OPENVDB | #ifdef WITH_OPENVDB | ||||
| Show All 16 Lines | |||||
| bool BKE_volume_is_points_only(const Volume *volume) | bool BKE_volume_is_points_only(const Volume *volume) | ||||
| { | { | ||||
| int num_grids = BKE_volume_num_grids(volume); | int num_grids = BKE_volume_num_grids(volume); | ||||
| if (num_grids == 0) { | if (num_grids == 0) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| for (int i = 0; i < num_grids; i++) { | for (int i = 0; i < num_grids; i++) { | ||||
| VolumeGrid *grid = BKE_volume_grid_get(volume, i); | const VolumeGrid *grid = BKE_volume_grid_get_for_read(volume, i); | ||||
| if (BKE_volume_grid_type(grid) != VOLUME_GRID_POINTS) { | if (BKE_volume_grid_type(grid) != VOLUME_GRID_POINTS) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| } | } | ||||
| return true; | return true; | ||||
| } | } | ||||
| Show All 9 Lines | if (volume->runtime.grids) { | ||||
| } | } | ||||
| } | } | ||||
| volume->runtime.default_simplify_level = simplify_level; | volume->runtime.default_simplify_level = simplify_level; | ||||
| #else | #else | ||||
| UNUSED_VARS(volume, depsgraph); | UNUSED_VARS(volume, depsgraph); | ||||
| #endif | #endif | ||||
| } | } | ||||
| static Volume *volume_evaluate_modifiers(struct Depsgraph *depsgraph, | static void volume_evaluate_modifiers(struct Depsgraph *depsgraph, | ||||
| struct Scene *scene, | struct Scene *scene, | ||||
| Object *object, | Object *object, | ||||
| Volume *volume_input) | Volume *volume_input, | ||||
| GeometrySet &geometry_set) | |||||
| { | { | ||||
| Volume *volume = volume_input; | |||||
| /* Modifier evaluation modes. */ | /* Modifier evaluation modes. */ | ||||
| const bool use_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER); | const bool use_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER); | ||||
| const int required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime; | const int required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime; | ||||
| ModifierApplyFlag apply_flag = use_render ? MOD_APPLY_RENDER : MOD_APPLY_USECACHE; | ModifierApplyFlag apply_flag = use_render ? MOD_APPLY_RENDER : MOD_APPLY_USECACHE; | ||||
| const ModifierEvalContext mectx = {depsgraph, object, apply_flag}; | const ModifierEvalContext mectx = {depsgraph, object, apply_flag}; | ||||
| /* Get effective list of modifiers to execute. Some effects like shape keys | /* Get effective list of modifiers to execute. Some effects like shape keys | ||||
| * are added as virtual modifiers before the user created modifiers. */ | * are added as virtual modifiers before the user created modifiers. */ | ||||
| VirtualModifierData virtualModifierData; | VirtualModifierData virtualModifierData; | ||||
| ModifierData *md = BKE_modifiers_get_virtual_modifierlist(object, &virtualModifierData); | ModifierData *md = BKE_modifiers_get_virtual_modifierlist(object, &virtualModifierData); | ||||
| /* Evaluate modifiers. */ | /* Evaluate modifiers. */ | ||||
| for (; md; md = md->next) { | for (; md; md = md->next) { | ||||
| const ModifierTypeInfo *mti = (const ModifierTypeInfo *)BKE_modifier_get_info( | const ModifierTypeInfo *mti = (const ModifierTypeInfo *)BKE_modifier_get_info( | ||||
| (ModifierType)md->type); | (ModifierType)md->type); | ||||
| if (!BKE_modifier_is_enabled(scene, md, required_mode)) { | if (!BKE_modifier_is_enabled(scene, md, required_mode)) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| if (mti->modifyVolume) { | if (mti->modifyGeometrySet) { | ||||
| /* Ensure we are not modifying the input. */ | mti->modifyGeometrySet(md, &mectx, &geometry_set); | ||||
| if (volume == volume_input) { | |||||
| volume = BKE_volume_copy_for_eval(volume, true); | |||||
| } | } | ||||
| else if (mti->modifyVolume) { | |||||
| Volume *volume_next = mti->modifyVolume(md, &mectx, volume); | VolumeComponent &volume_component = geometry_set.get_component_for_write<VolumeComponent>(); | ||||
| Volume *volume_old = volume_component.get_for_write(); | |||||
| if (volume_next && volume_next != volume) { | if (volume_old == nullptr) { | ||||
| /* If the modifier returned a new volume, release the old one. */ | volume_old = BKE_volume_new_for_eval(volume_input); | ||||
| if (volume != volume_input) { | volume_component.replace(volume_old); | ||||
| BKE_id_free(nullptr, volume); | } | ||||
| } | Volume *volume_new = mti->modifyVolume(md, &mectx, volume_old); | ||||
| volume = volume_next; | if (volume_new != volume_old) { | ||||
| volume_component.replace(volume_new); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| return volume; | |||||
| } | } | ||||
| void BKE_volume_eval_geometry(struct Depsgraph *depsgraph, Volume *volume) | void BKE_volume_eval_geometry(struct Depsgraph *depsgraph, Volume *volume) | ||||
| { | { | ||||
| volume_update_simplify_level(volume, depsgraph); | volume_update_simplify_level(volume, depsgraph); | ||||
| /* TODO: can we avoid modifier re-evaluation when frame did not change? */ | /* TODO: can we avoid modifier re-evaluation when frame did not change? */ | ||||
| int frame = volume_sequence_frame(depsgraph, volume); | int frame = volume_sequence_frame(depsgraph, volume); | ||||
| if (frame != volume->runtime.frame) { | if (frame != volume->runtime.frame) { | ||||
| BKE_volume_unload(volume); | BKE_volume_unload(volume); | ||||
| volume->runtime.frame = frame; | volume->runtime.frame = frame; | ||||
| } | } | ||||
| /* Flush back to original. */ | /* Flush back to original. */ | ||||
| if (DEG_is_active(depsgraph)) { | if (DEG_is_active(depsgraph)) { | ||||
| Volume *volume_orig = (Volume *)DEG_get_original_id(&volume->id); | Volume *volume_orig = (Volume *)DEG_get_original_id(&volume->id); | ||||
| if (volume_orig->runtime.frame != volume->runtime.frame) { | if (volume_orig->runtime.frame != volume->runtime.frame) { | ||||
| BKE_volume_unload(volume_orig); | BKE_volume_unload(volume_orig); | ||||
| volume_orig->runtime.frame = volume->runtime.frame; | volume_orig->runtime.frame = volume->runtime.frame; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| static Volume *take_volume_ownership_from_geometry_set(GeometrySet &geometry_set) | |||||
| { | |||||
| if (!geometry_set.has<VolumeComponent>()) { | |||||
| return nullptr; | |||||
| } | |||||
| VolumeComponent &volume_component = geometry_set.get_component_for_write<VolumeComponent>(); | |||||
| Volume *volume = volume_component.release(); | |||||
| if (volume != nullptr) { | |||||
| /* Add back, but only as read-only non-owning component. */ | |||||
| volume_component.replace(volume, GeometryOwnershipType::ReadOnly); | |||||
| } | |||||
| else { | |||||
| /* The component was empty, we can remove it. */ | |||||
| geometry_set.remove<VolumeComponent>(); | |||||
| } | |||||
| return volume; | |||||
| } | |||||
| void BKE_volume_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Object *object) | void BKE_volume_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Object *object) | ||||
| { | { | ||||
| /* Free any evaluated data and restore original data. */ | /* Free any evaluated data and restore original data. */ | ||||
| BKE_object_free_derived_caches(object); | BKE_object_free_derived_caches(object); | ||||
| /* Evaluate modifiers. */ | /* Evaluate modifiers. */ | ||||
| Volume *volume = (Volume *)object->data; | Volume *volume = (Volume *)object->data; | ||||
| Volume *volume_eval = volume_evaluate_modifiers(depsgraph, scene, object, volume); | GeometrySet geometry_set; | ||||
| VolumeComponent &volume_component = geometry_set.get_component_for_write<VolumeComponent>(); | |||||
| volume_component.replace(volume, GeometryOwnershipType::ReadOnly); | |||||
| volume_evaluate_modifiers(depsgraph, scene, object, volume, geometry_set); | |||||
| Volume *volume_eval = take_volume_ownership_from_geometry_set(geometry_set); | |||||
| /* If the geometry set did not contain a volume, we still create an empty one. */ | |||||
| if (volume_eval == nullptr) { | |||||
| volume_eval = BKE_volume_new_for_eval(volume); | |||||
| } | |||||
| /* Assign evaluated object. */ | /* Assign evaluated object. */ | ||||
| const bool is_owned = (volume != volume_eval); | const bool eval_is_owned = (volume != volume_eval); | ||||
| BKE_object_eval_assign_data(object, &volume_eval->id, is_owned); | BKE_object_eval_assign_data(object, &volume_eval->id, eval_is_owned); | ||||
| object->runtime.geometry_set_eval = new GeometrySet(std::move(geometry_set)); | |||||
| } | } | ||||
| void BKE_volume_grids_backup_restore(Volume *volume, VolumeGridVector *grids, const char *filepath) | void BKE_volume_grids_backup_restore(Volume *volume, VolumeGridVector *grids, const char *filepath) | ||||
| { | { | ||||
| #ifdef WITH_OPENVDB | #ifdef WITH_OPENVDB | ||||
| /* Restore grids after datablock was re-copied from original by depsgraph, | /* Restore grids after datablock was re-copied from original by depsgraph, | ||||
| * we don't want to load them again if possible. */ | * we don't want to load them again if possible. */ | ||||
| BLI_assert(volume->id.tag & LIB_TAG_COPIED_ON_WRITE); | BLI_assert(volume->id.tag & LIB_TAG_COPIED_ON_WRITE); | ||||
| ▲ Show 20 Lines • Show All 64 Lines • ▼ Show 20 Lines | |||||
| #ifdef WITH_OPENVDB | #ifdef WITH_OPENVDB | ||||
| return volume->runtime.grids->filepath; | return volume->runtime.grids->filepath; | ||||
| #else | #else | ||||
| UNUSED_VARS(volume); | UNUSED_VARS(volume); | ||||
| return ""; | return ""; | ||||
| #endif | #endif | ||||
| } | } | ||||
| VolumeGrid *BKE_volume_grid_get(const Volume *volume, int grid_index) | const VolumeGrid *BKE_volume_grid_get_for_read(const Volume *volume, int grid_index) | ||||
| { | |||||
| #ifdef WITH_OPENVDB | |||||
| const VolumeGridVector &grids = *volume->runtime.grids; | |||||
| for (const VolumeGrid &grid : grids) { | |||||
| if (grid_index-- == 0) { | |||||
| return &grid; | |||||
| } | |||||
| } | |||||
| return nullptr; | |||||
| #else | |||||
| UNUSED_VARS(volume, grid_index); | |||||
| return nullptr; | |||||
| #endif | |||||
| } | |||||
| VolumeGrid *BKE_volume_grid_get_for_write(Volume *volume, int grid_index) | |||||
| { | { | ||||
| #ifdef WITH_OPENVDB | #ifdef WITH_OPENVDB | ||||
| VolumeGridVector &grids = *volume->runtime.grids; | VolumeGridVector &grids = *volume->runtime.grids; | ||||
| for (VolumeGrid &grid : grids) { | for (VolumeGrid &grid : grids) { | ||||
| if (grid_index-- == 0) { | if (grid_index-- == 0) { | ||||
| return &grid; | return &grid; | ||||
| } | } | ||||
| } | } | ||||
| return nullptr; | return nullptr; | ||||
| #else | #else | ||||
| UNUSED_VARS(volume, grid_index); | UNUSED_VARS(volume, grid_index); | ||||
| return nullptr; | return nullptr; | ||||
| #endif | #endif | ||||
| } | } | ||||
| VolumeGrid *BKE_volume_grid_active_get(const Volume *volume) | const VolumeGrid *BKE_volume_grid_active_get_for_read(const Volume *volume) | ||||
| { | { | ||||
| const int num_grids = BKE_volume_num_grids(volume); | const int num_grids = BKE_volume_num_grids(volume); | ||||
| if (num_grids == 0) { | if (num_grids == 0) { | ||||
| return nullptr; | return nullptr; | ||||
| } | } | ||||
| const int index = clamp_i(volume->active_grid, 0, num_grids - 1); | const int index = clamp_i(volume->active_grid, 0, num_grids - 1); | ||||
| return BKE_volume_grid_get(volume, index); | return BKE_volume_grid_get_for_read(volume, index); | ||||
| } | } | ||||
| /* Tries to find a grid with the given name. Make sure that that the volume has been loaded. */ | /* Tries to find a grid with the given name. Make sure that that the volume has been loaded. */ | ||||
| VolumeGrid *BKE_volume_grid_find(const Volume *volume, const char *name) | const VolumeGrid *BKE_volume_grid_find_for_read(const Volume *volume, const char *name) | ||||
| { | { | ||||
| int num_grids = BKE_volume_num_grids(volume); | int num_grids = BKE_volume_num_grids(volume); | ||||
| for (int i = 0; i < num_grids; i++) { | for (int i = 0; i < num_grids; i++) { | ||||
| VolumeGrid *grid = BKE_volume_grid_get(volume, i); | const VolumeGrid *grid = BKE_volume_grid_get_for_read(volume, i); | ||||
| if (STREQ(BKE_volume_grid_name(grid), name)) { | if (STREQ(BKE_volume_grid_name(grid), name)) { | ||||
| return grid; | return grid; | ||||
| } | } | ||||
| } | } | ||||
| return nullptr; | return nullptr; | ||||
| } | } | ||||
| /* Grid Loading */ | /* Grid Loading */ | ||||
| bool BKE_volume_grid_load(const Volume *volume, VolumeGrid *grid) | bool BKE_volume_grid_load(const Volume *volume, const VolumeGrid *grid) | ||||
| { | { | ||||
| #ifdef WITH_OPENVDB | #ifdef WITH_OPENVDB | ||||
| VolumeGridVector &grids = *volume->runtime.grids; | VolumeGridVector &grids = *volume->runtime.grids; | ||||
| const char *volume_name = volume->id.name + 2; | const char *volume_name = volume->id.name + 2; | ||||
| grid->load(volume_name, grids.filepath); | grid->load(volume_name, grids.filepath); | ||||
| const char *error_msg = grid->error_message(); | const char *error_msg = grid->error_message(); | ||||
| if (error_msg) { | if (error_msg) { | ||||
| grids.error_msg = error_msg; | grids.error_msg = error_msg; | ||||
| return false; | return false; | ||||
| } | } | ||||
| return true; | return true; | ||||
| #else | #else | ||||
| UNUSED_VARS(volume, grid); | UNUSED_VARS(volume, grid); | ||||
| return true; | return true; | ||||
| #endif | #endif | ||||
| } | } | ||||
| void BKE_volume_grid_unload(const Volume *volume, VolumeGrid *grid) | void BKE_volume_grid_unload(const Volume *volume, const VolumeGrid *grid) | ||||
| { | { | ||||
| #ifdef WITH_OPENVDB | #ifdef WITH_OPENVDB | ||||
| const char *volume_name = volume->id.name + 2; | const char *volume_name = volume->id.name + 2; | ||||
| grid->unload(volume_name); | grid->unload(volume_name); | ||||
| #else | #else | ||||
| UNUSED_VARS(volume, grid); | UNUSED_VARS(volume, grid); | ||||
| #endif | #endif | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 113 Lines • ▼ Show 20 Lines | |||||
| #else | #else | ||||
| unit_m4(mat); | unit_m4(mat); | ||||
| UNUSED_VARS(volume_grid); | UNUSED_VARS(volume_grid); | ||||
| #endif | #endif | ||||
| } | } | ||||
| /* Grid Tree and Voxels */ | /* Grid Tree and Voxels */ | ||||
| bool BKE_volume_grid_bounds(const VolumeGrid *volume_grid, float min[3], float max[3]) | |||||
| { | |||||
| #ifdef WITH_OPENVDB | |||||
| /* TODO: we can get this from grid metadata in some cases? */ | |||||
| const openvdb::GridBase::Ptr grid = volume_grid->grid(); | |||||
| BLI_assert(BKE_volume_grid_is_loaded(volume_grid)); | |||||
| openvdb::CoordBBox coordbbox; | |||||
| if (!grid->baseTree().evalLeafBoundingBox(coordbbox)) { | |||||
| INIT_MINMAX(min, max); | |||||
| return false; | |||||
| } | |||||
| openvdb::BBoxd bbox = grid->transform().indexToWorld(coordbbox); | |||||
| min[0] = (float)bbox.min().x(); | |||||
| min[1] = (float)bbox.min().y(); | |||||
| min[2] = (float)bbox.min().z(); | |||||
| max[0] = (float)bbox.max().x(); | |||||
| max[1] = (float)bbox.max().y(); | |||||
| max[2] = (float)bbox.max().z(); | |||||
| return true; | |||||
| #else | |||||
| UNUSED_VARS(volume_grid); | |||||
| INIT_MINMAX(min, max); | |||||
| return false; | |||||
| #endif | |||||
| } | |||||
| /* Volume Editing */ | /* Volume Editing */ | ||||
| Volume *BKE_volume_new_for_eval(const Volume *volume_src) | Volume *BKE_volume_new_for_eval(const Volume *volume_src) | ||||
| { | { | ||||
| Volume *volume_dst = (Volume *)BKE_id_new_nomain(ID_VO, nullptr); | Volume *volume_dst = (Volume *)BKE_id_new_nomain(ID_VO, nullptr); | ||||
| STRNCPY(volume_dst->id.name, volume_src->id.name); | STRNCPY(volume_dst->id.name, volume_src->id.name); | ||||
| volume_dst->mat = (Material **)MEM_dupallocN(volume_src->mat); | volume_dst->mat = (Material **)MEM_dupallocN(volume_src->mat); | ||||
| Show All 31 Lines | struct CreateGridOp { | ||||
| } | } | ||||
| }; | }; | ||||
| #endif | #endif | ||||
| VolumeGrid *BKE_volume_grid_add(Volume *volume, const char *name, VolumeGridType type) | VolumeGrid *BKE_volume_grid_add(Volume *volume, const char *name, VolumeGridType type) | ||||
| { | { | ||||
| #ifdef WITH_OPENVDB | #ifdef WITH_OPENVDB | ||||
| VolumeGridVector &grids = *volume->runtime.grids; | VolumeGridVector &grids = *volume->runtime.grids; | ||||
| BLI_assert(BKE_volume_grid_find(volume, name) == nullptr); | BLI_assert(BKE_volume_grid_find_for_read(volume, name) == nullptr); | ||||
| BLI_assert(type != VOLUME_GRID_UNKNOWN); | BLI_assert(type != VOLUME_GRID_UNKNOWN); | ||||
| openvdb::GridBase::Ptr vdb_grid = BKE_volume_grid_type_operation(type, CreateGridOp{}); | openvdb::GridBase::Ptr vdb_grid = BKE_volume_grid_type_operation(type, CreateGridOp{}); | ||||
| if (!vdb_grid) { | if (!vdb_grid) { | ||||
| return nullptr; | return nullptr; | ||||
| } | } | ||||
| vdb_grid->setName(name); | vdb_grid->setName(name); | ||||
| ▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | if (DEG_get_mode(depsgraph) != DAG_EVAL_RENDER) { | ||||
| } | } | ||||
| } | } | ||||
| return 1.0f; | return 1.0f; | ||||
| } | } | ||||
| /* OpenVDB Grid Access */ | /* OpenVDB Grid Access */ | ||||
| #ifdef WITH_OPENVDB | #ifdef WITH_OPENVDB | ||||
| bool BKE_volume_grid_bounds(openvdb::GridBase::ConstPtr grid, float3 &r_min, float3 &r_max) | |||||
| { | |||||
| /* TODO: we can get this from grid metadata in some cases? */ | |||||
| openvdb::CoordBBox coordbbox; | |||||
| if (!grid->baseTree().evalLeafBoundingBox(coordbbox)) { | |||||
| return false; | |||||
| } | |||||
| openvdb::BBoxd bbox = grid->transform().indexToWorld(coordbbox); | |||||
| r_min = float3((float)bbox.min().x(), (float)bbox.min().y(), (float)bbox.min().z()); | |||||
| r_max = float3((float)bbox.max().x(), (float)bbox.max().y(), (float)bbox.max().z()); | |||||
| return true; | |||||
| } | |||||
| /** | |||||
| * Return a new grid pointer with only the metadata and transform changed. | |||||
| * This is useful for instances, where there is a separate transform on top of the original | |||||
| * grid transform that must be applied for some operations that only take a grid argument. | |||||
| */ | |||||
| openvdb::GridBase::ConstPtr BKE_volume_grid_shallow_transform(openvdb::GridBase::ConstPtr grid, | |||||
| const blender::float4x4 &transform) | |||||
| { | |||||
| openvdb::math::Transform::Ptr grid_transform = grid->transform().copy(); | |||||
| grid_transform->postMult(openvdb::Mat4d(((float *)transform.values))); | |||||
| /* Create a transformed grid. The underlying tree is shared. */ | |||||
| return grid->copyGridReplacingTransform(grid_transform); | |||||
| } | |||||
| openvdb::GridBase::ConstPtr BKE_volume_grid_openvdb_for_metadata(const VolumeGrid *grid) | openvdb::GridBase::ConstPtr BKE_volume_grid_openvdb_for_metadata(const VolumeGrid *grid) | ||||
| { | { | ||||
| return grid->grid(); | return grid->grid(); | ||||
| } | } | ||||
| openvdb::GridBase::ConstPtr BKE_volume_grid_openvdb_for_read(const Volume *volume, | openvdb::GridBase::ConstPtr BKE_volume_grid_openvdb_for_read(const Volume *volume, | ||||
| VolumeGrid *grid) | const VolumeGrid *grid) | ||||
| { | { | ||||
| BKE_volume_grid_load(volume, grid); | BKE_volume_grid_load(volume, grid); | ||||
| return grid->grid(); | return grid->grid(); | ||||
| } | } | ||||
| openvdb::GridBase::Ptr BKE_volume_grid_openvdb_for_write(const Volume *volume, | openvdb::GridBase::Ptr BKE_volume_grid_openvdb_for_write(const Volume *volume, | ||||
| VolumeGrid *grid, | VolumeGrid *grid, | ||||
| const bool clear) | const bool clear) | ||||
| ▲ Show 20 Lines • Show All 63 Lines • Show Last 20 Lines | |||||