diff options
Diffstat (limited to 'source/blender/blenkernel/intern/volume.cc')
-rw-r--r-- | source/blender/blenkernel/intern/volume.cc | 335 |
1 files changed, 242 insertions, 93 deletions
diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc index eb871e41f8e..11aa9597740 100644 --- a/source/blender/blenkernel/intern/volume.cc +++ b/source/blender/blenkernel/intern/volume.cc @@ -29,6 +29,7 @@ #include "BLI_compiler_compat.h" #include "BLI_fileops.h" #include "BLI_ghash.h" +#include "BLI_map.hh" #include "BLI_math.h" #include "BLI_path_util.h" #include "BLI_string.h" @@ -44,6 +45,7 @@ #include "BKE_modifier.h" #include "BKE_object.h" #include "BKE_packedFile.h" +#include "BKE_report.h" #include "BKE_scene.h" #include "BKE_volume.h" @@ -69,6 +71,7 @@ static CLG_LogRef LOG = {"bke.volume"}; # include <openvdb/openvdb.h> # include <openvdb/points/PointDataGrid.h> +# include <openvdb/tools/GridTransformer.h> /* Global Volume File Cache * @@ -114,12 +117,32 @@ static struct VolumeFileCache { { } + /* Returns the original grid or a simplified version depending on the given #simplify_level. */ + openvdb::GridBase::Ptr simplified_grid(const int simplify_level) + { + BLI_assert(simplify_level >= 0); + if (simplify_level == 0 || !is_loaded) { + return grid; + } + + std::lock_guard<std::mutex> lock(mutex); + return simplified_grids.lookup_or_add_cb(simplify_level, [&]() { + const float resolution_factor = 1.0f / (1 << simplify_level); + const VolumeGridType grid_type = BKE_volume_grid_type_openvdb(grid); + return BKE_volume_grid_create_with_changed_resolution(grid_type, *grid, resolution_factor); + }); + } + /* Unique key: filename + grid name. */ std::string filepath; std::string grid_name; /* OpenVDB grid. */ openvdb::GridBase::Ptr grid; + + /* Simplified versions of #grid. The integer key is the simplification level. */ + blender::Map<int, openvdb::GridBase::Ptr> simplified_grids; + /* Has the grid tree been loaded? */ bool is_loaded; /* Error message if an error occured during loading. */ @@ -154,7 +177,7 @@ static struct VolumeFileCache { ~VolumeFileCache() { - assert(cache.empty()); + BLI_assert(cache.empty()); } Entry *add_metadata_user(const Entry &template_entry) @@ -223,6 +246,7 @@ static struct VolumeFileCache { /* Note we replace the grid rather than clearing, so that if there is * any other shared pointer to the grid it will keep the tree. */ entry.grid = entry.grid->copyGridWithNewTree(); + entry.simplified_grids.clear(); entry.is_loaded = false; } } @@ -240,17 +264,22 @@ static struct VolumeFileCache { * stored in the global cache. Procedurally generated grids are not. */ struct VolumeGrid { - VolumeGrid(const VolumeFileCache::Entry &template_entry) : entry(NULL), is_loaded(false) + VolumeGrid(const VolumeFileCache::Entry &template_entry, const int simplify_level) + : entry(nullptr), simplify_level(simplify_level), is_loaded(false) { entry = GLOBAL_CACHE.add_metadata_user(template_entry); } - VolumeGrid(const openvdb::GridBase::Ptr &grid) : entry(NULL), local_grid(grid), is_loaded(true) + VolumeGrid(const openvdb::GridBase::Ptr &grid) + : entry(nullptr), local_grid(grid), is_loaded(true) { } VolumeGrid(const VolumeGrid &other) - : entry(other.entry), local_grid(other.local_grid), is_loaded(other.is_loaded) + : entry(other.entry), + simplify_level(other.simplify_level), + local_grid(other.local_grid), + is_loaded(other.is_loaded) { if (entry) { GLOBAL_CACHE.copy_user(*entry, is_loaded); @@ -267,7 +296,7 @@ struct VolumeGrid { void load(const char *volume_name, const char *filepath) { /* If already loaded or not file-backed, nothing to do. */ - if (is_loaded || entry == NULL) { + if (is_loaded || entry == nullptr) { return; } @@ -309,7 +338,7 @@ struct VolumeGrid { void unload(const char *volume_name) { /* Not loaded or not file-backed, nothing to do. */ - if (!is_loaded || entry == NULL) { + if (!is_loaded || entry == nullptr) { return; } @@ -336,7 +365,7 @@ struct VolumeGrid { local_grid = grid()->copyGridWithNewTree(); if (entry) { GLOBAL_CACHE.remove_user(*entry, is_loaded); - entry = NULL; + entry = nullptr; } is_loaded = true; } @@ -350,7 +379,7 @@ struct VolumeGrid { local_grid = grid()->deepCopyGrid(); if (entry) { GLOBAL_CACHE.remove_user(*entry, is_loaded); - entry = NULL; + entry = nullptr; } is_loaded = true; } @@ -359,8 +388,8 @@ struct VolumeGrid { { /* Don't use vdb.getName() since it copies the string, we want a pointer to the * original so it doesn't get freed out of scope. */ - openvdb::StringMetadata::ConstPtr name_meta = grid()->getMetadata<openvdb::StringMetadata>( - openvdb::GridBase::META_GRID_NAME); + openvdb::StringMetadata::ConstPtr name_meta = + main_grid()->getMetadata<openvdb::StringMetadata>(openvdb::GridBase::META_GRID_NAME); return (name_meta) ? name_meta->value().c_str() : ""; } @@ -370,7 +399,7 @@ struct VolumeGrid { return entry->error_msg.c_str(); } - return NULL; + return nullptr; } bool grid_is_loaded() const @@ -378,7 +407,22 @@ struct VolumeGrid { return is_loaded; } - const openvdb::GridBase::Ptr &grid() const + openvdb::GridBase::Ptr grid() const + { + if (entry) { + return entry->simplified_grid(simplify_level); + } + return local_grid; + } + + void set_simplify_level(const int simplify_level) + { + BLI_assert(simplify_level >= 0); + this->simplify_level = simplify_level; + } + + private: + const openvdb::GridBase::Ptr &main_grid() const { return (entry) ? entry->grid : local_grid; } @@ -387,6 +431,9 @@ struct VolumeGrid { /* File cache entry when grid comes directly from a file and may be shared * with other volume datablocks. */ VolumeFileCache::Entry *entry; + /* 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. */ + int simplify_level = 0; /* OpenVDB grid if it's not shared through the file cache. */ openvdb::GridBase::Ptr local_grid; /* Indicates if the tree has been loaded for this grid. Note that vdb.tree() @@ -401,7 +448,7 @@ struct VolumeGrid { * the actual grids are always saved in a VDB file. */ struct VolumeGridVector : public std::list<VolumeGrid> { - VolumeGridVector() + VolumeGridVector() : metadata(new openvdb::MetaMap()) { filepath[0] = '\0'; } @@ -516,7 +563,7 @@ static void volume_blend_write(BlendWriter *writer, ID *id, const void *id_addre Volume *volume = (Volume *)id; if (volume->id.us > 0 || BLO_write_is_undo(writer)) { /* Clean up, important in undo case to reduce false detection of changed datablocks. */ - volume->runtime.grids = 0; + volume->runtime.grids = nullptr; /* write LibData */ BLO_write_id_struct(writer, Volume, id_address, &volume->id); @@ -587,12 +634,14 @@ IDTypeInfo IDType_ID_VO = { /* blend_read_data */ volume_blend_read_data, /* blend_read_lib */ volume_blend_read_lib, /* blend_read_expand */ volume_blend_read_expand, + + /* blend_read_undo_preserve */ nullptr, }; void BKE_volume_init_grids(Volume *volume) { #ifdef WITH_OPENVDB - if (volume->runtime.grids == NULL) { + if (volume->runtime.grids == nullptr) { volume->runtime.grids = OBJECT_GUARDED_NEW(VolumeGridVector); } #else @@ -602,20 +651,11 @@ void BKE_volume_init_grids(Volume *volume) void *BKE_volume_add(Main *bmain, const char *name) { - Volume *volume = (Volume *)BKE_libblock_alloc(bmain, ID_VO, name, 0); - - volume_init_data(&volume->id); + Volume *volume = (Volume *)BKE_id_new(bmain, ID_VO, name); return volume; } -Volume *BKE_volume_copy(Main *bmain, const Volume *volume) -{ - Volume *volume_copy; - BKE_id_copy(bmain, &volume->id, (ID **)&volume_copy); - return volume_copy; -} - /* Sequence */ static int volume_sequence_frame(const Depsgraph *depsgraph, const Volume *volume) @@ -750,15 +790,6 @@ bool BKE_volume_load(Volume *volume, Main *bmain) return false; } - /* Test if file exists. */ - if (!BLI_exists(grids.filepath)) { - char filename[FILE_MAX]; - BLI_split_file_part(grids.filepath, filename, sizeof(filename)); - grids.error_msg = filename + std::string(" not found"); - CLOG_INFO(&LOG, 1, "Volume %s: %s", volume_name, grids.error_msg.c_str()); - return false; - } - /* Open OpenVDB file. */ openvdb::io::File file(grids.filepath); openvdb::GridPtrVec vdb_grids; @@ -778,7 +809,7 @@ bool BKE_volume_load(Volume *volume, Main *bmain) for (const openvdb::GridBase::Ptr &vdb_grid : vdb_grids) { if (vdb_grid) { VolumeFileCache::Entry template_entry(grids.filepath, vdb_grid); - grids.emplace_back(template_entry); + grids.emplace_back(template_entry, volume->runtime.default_simplify_level); } } @@ -803,15 +834,49 @@ void BKE_volume_unload(Volume *volume) #endif } +/* File Save */ + +bool BKE_volume_save(Volume *volume, Main *bmain, ReportList *reports, const char *filepath) +{ +#ifdef WITH_OPENVDB + if (!BKE_volume_load(volume, bmain)) { + BKE_reportf(reports, RPT_ERROR, "Could not load volume for writing"); + return false; + } + + VolumeGridVector &grids = *volume->runtime.grids; + openvdb::GridCPtrVec vdb_grids; + + for (VolumeGrid &grid : grids) { + vdb_grids.push_back(BKE_volume_grid_openvdb_for_read(volume, &grid)); + } + + try { + openvdb::io::File file(filepath); + file.write(vdb_grids, *grids.metadata); + file.close(); + } + catch (const openvdb::IoError &e) { + BKE_reportf(reports, RPT_ERROR, "Could not write volume: %s", e.what()); + return false; + } + + return true; +#else + UNUSED_VARS(volume, bmain, reports, filepath); + return false; +#endif +} + BoundBox *BKE_volume_boundbox_get(Object *ob) { BLI_assert(ob->type == OB_VOLUME); - if (ob->runtime.bb != NULL && (ob->runtime.bb->flag & BOUNDBOX_DIRTY) == 0) { + if (ob->runtime.bb != nullptr && (ob->runtime.bb->flag & BOUNDBOX_DIRTY) == 0) { return ob->runtime.bb; } - if (ob->runtime.bb == NULL) { + if (ob->runtime.bb == nullptr) { Volume *volume = (Volume *)ob->data; ob->runtime.bb = (BoundBox *)MEM_callocN(sizeof(BoundBox), "volume boundbox"); @@ -889,6 +954,21 @@ bool BKE_volume_is_points_only(const Volume *volume) /* Dependency Graph */ +static void volume_update_simplify_level(Volume *volume, const Depsgraph *depsgraph) +{ +#ifdef WITH_OPENVDB + const int simplify_level = BKE_volume_simplify_level(depsgraph); + if (volume->runtime.grids) { + for (VolumeGrid &grid : *volume->runtime.grids) { + grid.set_simplify_level(simplify_level); + } + } + volume->runtime.default_simplify_level = simplify_level; +#else + UNUSED_VARS(volume, depsgraph); +#endif +} + static Volume *volume_evaluate_modifiers(struct Depsgraph *depsgraph, struct Scene *scene, Object *object, @@ -927,7 +1007,7 @@ static Volume *volume_evaluate_modifiers(struct Depsgraph *depsgraph, if (volume_next && volume_next != volume) { /* If the modifier returned a new volume, release the old one. */ if (volume != volume_input) { - BKE_id_free(NULL, volume); + BKE_id_free(nullptr, volume); } volume = volume_next; } @@ -939,6 +1019,8 @@ static Volume *volume_evaluate_modifiers(struct Depsgraph *depsgraph, void BKE_volume_eval_geometry(struct Depsgraph *depsgraph, Volume *volume) { + volume_update_simplify_level(volume, depsgraph); + /* TODO: can we avoid modifier re-evaluation when frame did not change? */ int frame = volume_sequence_frame(depsgraph, volume); if (frame != volume->runtime.frame) { @@ -976,7 +1058,7 @@ void BKE_volume_grids_backup_restore(Volume *volume, VolumeGridVector *grids, co /* Restore grids after datablock was re-copied from original by depsgraph, * we don't want to load them again if possible. */ BLI_assert(volume->id.tag & LIB_TAG_COPIED_ON_WRITE); - BLI_assert(volume->runtime.grids != NULL && grids != NULL); + BLI_assert(volume->runtime.grids != nullptr && grids != nullptr); if (!grids->is_loaded()) { /* No grids loaded in CoW datablock, nothing lost by discarding. */ @@ -999,8 +1081,8 @@ void BKE_volume_grids_backup_restore(Volume *volume, VolumeGridVector *grids, co /* Draw Cache */ -void (*BKE_volume_batch_cache_dirty_tag_cb)(Volume *volume, int mode) = NULL; -void (*BKE_volume_batch_cache_free_cb)(Volume *volume) = NULL; +void (*BKE_volume_batch_cache_dirty_tag_cb)(Volume *volume, int mode) = nullptr; +void (*BKE_volume_batch_cache_free_cb)(Volume *volume) = nullptr; void BKE_volume_batch_cache_dirty_tag(Volume *volume, int mode) { @@ -1057,10 +1139,10 @@ VolumeGrid *BKE_volume_grid_get(const Volume *volume, int grid_index) return &grid; } } - return NULL; + return nullptr; #else UNUSED_VARS(volume, grid_index); - return NULL; + return nullptr; #endif } @@ -1068,13 +1150,14 @@ VolumeGrid *BKE_volume_grid_active_get(const Volume *volume) { const int num_grids = BKE_volume_num_grids(volume); if (num_grids == 0) { - return NULL; + return nullptr; } const int index = clamp_i(volume->active_grid, 0, num_grids - 1); return BKE_volume_grid_get(volume, index); } +/* 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) { int num_grids = BKE_volume_num_grids(volume); @@ -1085,7 +1168,7 @@ VolumeGrid *BKE_volume_grid_find(const Volume *volume, const char *name) } } - return NULL; + return nullptr; } /* Grid Loading */ @@ -1140,11 +1223,9 @@ const char *BKE_volume_grid_name(const VolumeGrid *volume_grid) #endif } -VolumeGridType BKE_volume_grid_type(const VolumeGrid *volume_grid) -{ #ifdef WITH_OPENVDB - const openvdb::GridBase::Ptr &grid = volume_grid->grid(); - +VolumeGridType BKE_volume_grid_type_openvdb(const openvdb::GridBase::Ptr &grid) +{ if (grid->isType<openvdb::FloatGrid>()) { return VOLUME_GRID_FLOAT; } @@ -1178,10 +1259,18 @@ VolumeGridType BKE_volume_grid_type(const VolumeGrid *volume_grid) if (grid->isType<openvdb::points::PointDataGrid>()) { return VOLUME_GRID_POINTS; } + return VOLUME_GRID_UNKNOWN; +} +#endif + +VolumeGridType BKE_volume_grid_type(const VolumeGrid *volume_grid) +{ +#ifdef WITH_OPENVDB + const openvdb::GridBase::Ptr grid = volume_grid->grid(); + return BKE_volume_grid_type_openvdb(grid); #else UNUSED_VARS(volume_grid); #endif - return VOLUME_GRID_UNKNOWN; } @@ -1212,7 +1301,7 @@ int BKE_volume_grid_channels(const VolumeGrid *grid) void BKE_volume_grid_transform_matrix(const VolumeGrid *volume_grid, float mat[4][4]) { #ifdef WITH_OPENVDB - const openvdb::GridBase::Ptr &grid = volume_grid->grid(); + const openvdb::GridBase::Ptr grid = volume_grid->grid(); const openvdb::math::Transform &transform = grid->transform(); /* Perspective not supported for now, getAffineMap() will leave out the @@ -1236,7 +1325,7 @@ bool BKE_volume_grid_bounds(const VolumeGrid *volume_grid, float min[3], float m { #ifdef WITH_OPENVDB /* TODO: we can get this from grid metadata in some cases? */ - const openvdb::GridBase::Ptr &grid = volume_grid->grid(); + const openvdb::GridBase::Ptr grid = volume_grid->grid(); BLI_assert(BKE_volume_grid_is_loaded(volume_grid)); openvdb::CoordBBox coordbbox; @@ -1264,11 +1353,13 @@ bool BKE_volume_grid_bounds(const VolumeGrid *volume_grid, float min[3], float m Volume *BKE_volume_new_for_eval(const Volume *volume_src) { - Volume *volume_dst = (Volume *)BKE_id_new_nomain(ID_VO, NULL); + Volume *volume_dst = (Volume *)BKE_id_new_nomain(ID_VO, nullptr); STRNCPY(volume_dst->id.name, volume_src->id.name); volume_dst->mat = (Material **)MEM_dupallocN(volume_src->mat); volume_dst->totcol = volume_src->totcol; + volume_dst->render = volume_src->render; + volume_dst->display = volume_src->display; BKE_volume_init_grids(volume_dst); return volume_dst; @@ -1282,54 +1373,35 @@ Volume *BKE_volume_copy_for_eval(Volume *volume_src, bool reference) flags |= LIB_ID_COPY_CD_REFERENCE; } - Volume *result; - BKE_id_copy_ex(NULL, &volume_src->id, (ID **)&result, flags); - result->filepath[0] = '\0'; + Volume *result = (Volume *)BKE_id_copy_ex(nullptr, &volume_src->id, nullptr, flags); return result; } +#ifdef WITH_OPENVDB +struct CreateGridOp { + template<typename GridType> typename openvdb::GridBase::Ptr operator()() + { + if constexpr (std::is_same_v<GridType, openvdb::points::PointDataGrid>) { + return {}; + } + else { + return GridType::create(); + } + } +}; +#endif + VolumeGrid *BKE_volume_grid_add(Volume *volume, const char *name, VolumeGridType type) { #ifdef WITH_OPENVDB VolumeGridVector &grids = *volume->runtime.grids; - BLI_assert(BKE_volume_grid_find(volume, name) == NULL); + BLI_assert(BKE_volume_grid_find(volume, name) == nullptr); + BLI_assert(type != VOLUME_GRID_UNKNOWN); - openvdb::GridBase::Ptr vdb_grid; - switch (type) { - case VOLUME_GRID_FLOAT: - vdb_grid = openvdb::FloatGrid::create(); - break; - case VOLUME_GRID_VECTOR_FLOAT: - vdb_grid = openvdb::Vec3fGrid::create(); - break; - case VOLUME_GRID_BOOLEAN: - vdb_grid = openvdb::BoolGrid::create(); - break; - case VOLUME_GRID_DOUBLE: - vdb_grid = openvdb::DoubleGrid::create(); - break; - case VOLUME_GRID_INT: - vdb_grid = openvdb::Int32Grid::create(); - break; - case VOLUME_GRID_INT64: - vdb_grid = openvdb::Int64Grid::create(); - break; - case VOLUME_GRID_VECTOR_INT: - vdb_grid = openvdb::Vec3IGrid::create(); - break; - case VOLUME_GRID_VECTOR_DOUBLE: - vdb_grid = openvdb::Vec3dGrid::create(); - break; - case VOLUME_GRID_STRING: - vdb_grid = openvdb::StringGrid::create(); - break; - case VOLUME_GRID_MASK: - vdb_grid = openvdb::MaskGrid::create(); - break; - case VOLUME_GRID_POINTS: - case VOLUME_GRID_UNKNOWN: - return NULL; + openvdb::GridBase::Ptr vdb_grid = BKE_volume_grid_type_operation(type, CreateGridOp{}); + if (!vdb_grid) { + return nullptr; } vdb_grid->setName(name); @@ -1337,7 +1409,7 @@ VolumeGrid *BKE_volume_grid_add(Volume *volume, const char *name, VolumeGridType return &grids.back(); #else UNUSED_VARS(volume, name, type); - return NULL; + return nullptr; #endif } @@ -1356,6 +1428,33 @@ void BKE_volume_grid_remove(Volume *volume, VolumeGrid *grid) #endif } +int BKE_volume_simplify_level(const Depsgraph *depsgraph) +{ + if (DEG_get_mode(depsgraph) != DAG_EVAL_RENDER) { + const Scene *scene = DEG_get_input_scene(depsgraph); + if (scene->r.mode & R_SIMPLIFY) { + const float simplify = scene->r.simplify_volumes; + if (simplify == 0.0f) { + /* log2 is not defined at 0.0f, so just use some high simplify level. */ + return 16; + } + return ceilf(-log2(simplify)); + } + } + return 0; +} + +float BKE_volume_simplify_factor(const Depsgraph *depsgraph) +{ + if (DEG_get_mode(depsgraph) != DAG_EVAL_RENDER) { + const Scene *scene = DEG_get_input_scene(depsgraph); + if (scene->r.mode & R_SIMPLIFY) { + return scene->r.simplify_volumes; + } + } + return 1.0f; +} + /* OpenVDB Grid Access */ #ifdef WITH_OPENVDB @@ -1386,4 +1485,54 @@ openvdb::GridBase::Ptr BKE_volume_grid_openvdb_for_write(const Volume *volume, return grid->grid(); } + +/* Changing the resolution of a grid. */ + +/** + * Returns a grid of the same type as the input, but with more/less resolution. If + * resolution_factor is 1/2, the resolution on each axis is halved. The transform of the returned + * grid is adjusted to match the original grid. */ +template<typename GridType> +static typename GridType::Ptr create_grid_with_changed_resolution(const GridType &old_grid, + const float resolution_factor) +{ + BLI_assert(resolution_factor > 0.0f); + + openvdb::Mat4R xform; + xform.setToScale(openvdb::Vec3d(resolution_factor)); + openvdb::tools::GridTransformer transformer{xform}; + + typename GridType::Ptr new_grid = old_grid.copyWithNewTree(); + transformer.transformGrid<openvdb::tools::BoxSampler>(old_grid, *new_grid); + new_grid->transform() = old_grid.transform(); + new_grid->transform().preScale(1.0f / resolution_factor); + new_grid->transform().postTranslate(-new_grid->voxelSize() / 2.0f); + return new_grid; +} + +struct CreateGridWithChangedResolutionOp { + const openvdb::GridBase &grid; + const float resolution_factor; + + template<typename GridType> typename openvdb::GridBase::Ptr operator()() + { + if constexpr (std::is_same_v<GridType, openvdb::StringGrid>) { + return {}; + } + else { + return create_grid_with_changed_resolution(static_cast<const GridType &>(grid), + resolution_factor); + } + } +}; + +openvdb::GridBase::Ptr BKE_volume_grid_create_with_changed_resolution( + const VolumeGridType grid_type, + const openvdb::GridBase &old_grid, + const float resolution_factor) +{ + CreateGridWithChangedResolutionOp op{old_grid, resolution_factor}; + return BKE_volume_grid_type_operation(grid_type, op); +} + #endif |