Volumes: simplify volumes in modifiers or on load

This changes how the simplify volumes setting works. Before, it only
affeted viewport rendering. This was an issue, because all internal
computations would still have to happen on the high resolution volumes.
With this patch, the simplify setting already affects file loading and
procedural generation of volumes.

Rendering does not have to care about the simplify option anymore,
it just gets the correct simplified version from the depsgraph.

Reviewers: brecht

Differential Revision: https://developer.blender.org/D9176
This commit is contained in:
Jacques Lucke 2020-10-20 11:00:16 +02:00
parent f3ecb4c91e
commit 63a9f24b55
8 changed files with 186 additions and 102 deletions

View File

@ -140,6 +140,10 @@ struct VolumeGrid *BKE_volume_grid_add(struct Volume *volume,
VolumeGridType type);
void BKE_volume_grid_remove(struct Volume *volume, struct VolumeGrid *grid);
/* Simplify */
int BKE_volume_simplify_level(const struct Depsgraph *depsgraph);
float BKE_volume_simplify_factor(const struct Depsgraph *depsgraph);
/* File Save */
bool BKE_volume_save(struct Volume *volume,
struct Main *bmain,
@ -166,16 +170,7 @@ openvdb::GridBase::Ptr BKE_volume_grid_openvdb_for_write(const struct Volume *vo
struct VolumeGrid *grid,
const bool clear);
template<typename GridType>
typename GridType::Ptr BKE_volume_grid_openvdb_for_write(const struct Volume *volume,
struct VolumeGrid *grid,
const bool clear)
{
openvdb::GridBase::Ptr openvdb_grid = BKE_volume_grid_openvdb_for_write(volume, grid, clear);
BLI_assert(openvdb_grid->isType<GridType>());
typename GridType::Ptr typed_openvdb_grid = openvdb::gridPtrCast<GridType>(openvdb_grid);
return typed_openvdb_grid;
}
VolumeGridType BKE_volume_grid_type_openvdb(const openvdb::GridBase::Ptr &grid);
template<typename OpType>
auto BKE_volume_grid_type_operation(const VolumeGridType grid_type, OpType &&op)
@ -212,4 +207,9 @@ auto BKE_volume_grid_type_operation(const VolumeGridType grid_type, OpType &&op)
return op.template operator()<openvdb::FloatGrid>();
}
openvdb::GridBase::Ptr BKE_volume_grid_create_with_changed_resolution(
const VolumeGridType grid_type,
const openvdb::GridBase &old_grid,
const float resolution_factor);
#endif

View File

@ -44,7 +44,6 @@ typedef struct DenseFloatVolumeGrid {
bool BKE_volume_grid_dense_floats(const struct Volume *volume,
struct VolumeGrid *volume_grid,
const float resolution_factor,
DenseFloatVolumeGrid *r_dense_grid);
void BKE_volume_dense_float_grid_clear(DenseFloatVolumeGrid *dense_grid);

View File

@ -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"
@ -70,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
*
@ -115,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. */
@ -224,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;
}
}
@ -241,7 +264,8 @@ 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(NULL), simplify_level(simplify_level), is_loaded(false)
{
entry = GLOBAL_CACHE.add_metadata_user(template_entry);
}
@ -251,7 +275,10 @@ struct VolumeGrid {
}
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);
@ -360,8 +387,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() : "";
}
@ -379,7 +406,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;
}
@ -388,6 +430,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()
@ -761,7 +806,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);
}
}
@ -906,6 +951,17 @@ bool BKE_volume_is_points_only(const Volume *volume)
/* Dependency Graph */
static void volume_update_simplify_level(Volume *volume, const Depsgraph *depsgraph)
{
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;
}
static Volume *volume_evaluate_modifiers(struct Depsgraph *depsgraph,
struct Scene *scene,
Object *object,
@ -956,6 +1012,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) {
@ -1157,11 +1215,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;
}
@ -1195,10 +1251,16 @@ VolumeGridType BKE_volume_grid_type(const VolumeGrid *volume_grid)
if (grid->isType<openvdb::points::PointDataGrid>()) {
return VOLUME_GRID_POINTS;
}
#else
UNUSED_VARS(volume_grid);
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);
#endif
return VOLUME_GRID_UNKNOWN;
}
@ -1229,7 +1291,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
@ -1253,7 +1315,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;
@ -1302,7 +1364,6 @@ Volume *BKE_volume_copy_for_eval(Volume *volume_src, bool reference)
}
Volume *result = (Volume *)BKE_id_copy_ex(NULL, &volume_src->id, NULL, flags);
result->filepath[0] = '\0';
return result;
}
@ -1357,6 +1418,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
@ -1387,4 +1475,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

View File

@ -34,59 +34,12 @@
#ifdef WITH_OPENVDB
# include <openvdb/openvdb.h>
# include <openvdb/tools/Dense.h>
# include <openvdb/tools/GridTransformer.h>
#endif
/* Dense Voxels */
#ifdef WITH_OPENVDB
/**
* 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 = GridType::create();
transformer.transformGrid<openvdb::tools::BoxSampler>(old_grid, *new_grid);
new_grid->transform() = old_grid.transform();
new_grid->transform().preScale(1.0f / resolution_factor);
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);
}
}
};
static openvdb::GridBase::Ptr create_grid_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);
}
template<typename GridType, typename VoxelType>
static void extract_dense_voxels(const openvdb::GridBase &grid,
const openvdb::CoordBBox bbox,
@ -152,16 +105,11 @@ static void create_texture_to_object_matrix(const openvdb::Mat4d &grid_transform
bool BKE_volume_grid_dense_floats(const Volume *volume,
VolumeGrid *volume_grid,
const float resolution_factor,
DenseFloatVolumeGrid *r_dense_grid)
{
#ifdef WITH_OPENVDB
const VolumeGridType grid_type = BKE_volume_grid_type(volume_grid);
openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid);
if (resolution_factor != 1.0f) {
grid = create_grid_with_changed_resolution(grid_type, *grid, resolution_factor);
}
const openvdb::CoordBBox bbox = grid->evalActiveVoxelBoundingBox();
if (bbox.empty()) {
@ -189,7 +137,7 @@ bool BKE_volume_grid_dense_floats(const Volume *volume,
copy_v3_v3_int(r_dense_grid->resolution, resolution.asV());
return true;
#endif
UNUSED_VARS(volume, volume_grid, resolution_factor, r_dense_grid);
UNUSED_VARS(volume, volume_grid, r_dense_grid);
return false;
}

View File

@ -262,8 +262,6 @@ static DRWVolumeGrid *volume_grid_cache_get(Volume *volume,
VolumeGrid *grid,
VolumeBatchCache *cache)
{
const DRWContextState *draw_ctx = DRW_context_state_get();
const char *name = BKE_volume_grid_name(grid);
/* Return cached grid. */
@ -289,22 +287,12 @@ static DRWVolumeGrid *volume_grid_cache_get(Volume *volume,
return cache_grid;
}
/* Load grid tree into memory, if not loaded already. */
/* Remember if grid was loaded. If it was not, we want to unload it after the GPUTexture has been
* created. */
const bool was_loaded = BKE_volume_grid_is_loaded(grid);
BKE_volume_grid_load(volume, grid);
float resolution_factor = 1.0f;
if (DEG_get_mode(draw_ctx->depsgraph) != DAG_EVAL_RENDER) {
if (draw_ctx->scene->r.mode & R_SIMPLIFY) {
resolution_factor = draw_ctx->scene->r.simplify_volumes;
}
}
if (resolution_factor == 0.0f) {
return cache_grid;
}
DenseFloatVolumeGrid dense_grid;
if (BKE_volume_grid_dense_floats(volume, grid, resolution_factor, &dense_grid)) {
if (BKE_volume_grid_dense_floats(volume, grid, &dense_grid)) {
copy_m4_m4(cache_grid->texture_to_object, dense_grid.texture_to_object);
invert_m4_m4(cache_grid->object_to_texture, dense_grid.texture_to_object);

View File

@ -31,7 +31,9 @@ typedef struct Volume_Runtime {
/* Current frame in sequence for evaluated volume */
int frame;
int _pad;
/* Default simplify level for volume grids loaded from files. */
int default_simplify_level;
} Volume_Runtime;
typedef struct VolumeDisplay {

View File

@ -33,7 +33,7 @@
#include "DNA_screen_types.h"
#include "DNA_volume_types.h"
#include "DEG_depsgraph_build.h"
#include "DEG_depsgraph.h"
#include "UI_interface.h"
#include "UI_resources.h"
@ -176,12 +176,19 @@ static void panelRegister(ARegionType *region_type)
modifier_panel_register(region_type, eModifierType_MeshToVolume, panel_draw);
}
static float compute_voxel_size(const MeshToVolumeModifierData *mvmd,
static float compute_voxel_size(const ModifierEvalContext *ctx,
const MeshToVolumeModifierData *mvmd,
const blender::float4x4 &transform)
{
using namespace blender;
float volume_simplify = BKE_volume_simplify_factor(ctx->depsgraph);
if (volume_simplify == 0.0f) {
return 0.0f;
}
if (mvmd->resolution_mode == MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_SIZE) {
return mvmd->voxel_size;
return mvmd->voxel_size / volume_simplify;
}
if (mvmd->voxel_amount <= 0) {
return 0;
@ -192,7 +199,7 @@ static float compute_voxel_size(const MeshToVolumeModifierData *mvmd,
const float diagonal = float3::distance(transform * float3(bb->vec[6]),
transform * float3(bb->vec[0]));
const float approximate_volume_side_length = diagonal + mvmd->exterior_band_width * 2.0f;
const float voxel_size = approximate_volume_side_length / mvmd->voxel_amount;
const float voxel_size = approximate_volume_side_length / mvmd->voxel_amount / volume_simplify;
return voxel_size;
}
@ -215,7 +222,7 @@ static Volume *modifyVolume(ModifierData *md, const ModifierEvalContext *ctx, Vo
const float4x4 mesh_to_own_object_space_transform = float4x4(ctx->object->imat) *
float4x4(object_to_convert->obmat);
const float voxel_size = compute_voxel_size(mvmd, mesh_to_own_object_space_transform);
const float voxel_size = compute_voxel_size(ctx, mvmd, mesh_to_own_object_space_transform);
if (voxel_size == 0.0f) {
return input_volume;
}
@ -246,8 +253,8 @@ static Volume *modifyVolume(ModifierData *md, const ModifierEvalContext *ctx, Vo
/* Create a new volume object and add the density grid. */
Volume *volume = BKE_volume_new_for_eval(input_volume);
VolumeGrid *c_density_grid = BKE_volume_grid_add(volume, "density", VOLUME_GRID_FLOAT);
openvdb::FloatGrid::Ptr density_grid = BKE_volume_grid_openvdb_for_write<openvdb::FloatGrid>(
volume, c_density_grid, false);
openvdb::FloatGrid::Ptr density_grid = openvdb::gridPtrCast<openvdb::FloatGrid>(
BKE_volume_grid_openvdb_for_write(volume, c_density_grid, false));
/* Merge the generated grid into the density grid. Should be cheap because density_grid has just
* been created as well. */

View File

@ -33,6 +33,7 @@
#include "DNA_volume_types.h"
#include "DEG_depsgraph_build.h"
#include "DEG_depsgraph_query.h"
#include "UI_interface.h"
#include "UI_resources.h"
@ -290,6 +291,7 @@ static Volume *modifyVolume(ModifierData *md, const ModifierEvalContext *ctx, Vo
VolumeDisplaceModifierData *vdmd = reinterpret_cast<VolumeDisplaceModifierData *>(md);
/* Iterate over all grids and displace them one by one. */
BKE_volume_load(volume, DEG_get_bmain(ctx->depsgraph));
const int grid_amount = BKE_volume_num_grids(volume);
for (int grid_index = 0; grid_index < grid_amount; grid_index++) {
VolumeGrid *volume_grid = BKE_volume_grid_get(volume, grid_index);