From 700d168a5c7f44daa6235c8d2b8a0275935b93ae Mon Sep 17 00:00:00 2001 From: Erik Abrahamsson Date: Tue, 25 Apr 2023 21:56:14 +0200 Subject: [PATCH] Volumes: Generate proper fog volume in Mesh to Volume node Currently `Mesh to Volume` creates a volume using `openvdb::tools::meshToVolume` which is then filled with the specified density and the class set to Fog Volume. This is wrong because `meshToVolume` creates a signed distance field by default that needs to be converted to a Fog Volume with `openvdb::tools::sdfToFogVolume` to get a proper Fog volume. Here is the description of what that function does (from OpenVDB): "The active and negative-valued interior half of the narrow band becomes a linear ramp from 0 to 1; the inactive interior becomes active with a constant value of 1; and the exterior, including the background and the active exterior half of the narrow band, becomes inactive with a constant value of 0. The interior, though active, remains sparse." This means with this commit old files will not look the same. There is no way to version this as the options for external band width and not filling the volume is removed (they don't make any sense for a proper fog volume). Pull Request: https://projects.blender.org/blender/blender/pulls/107279 --- source/blender/geometry/GEO_mesh_to_volume.hh | 2 - .../blender/geometry/intern/mesh_to_volume.cc | 70 +++++++------------ source/blender/makesdna/DNA_modifier_types.h | 7 -- source/blender/makesrna/intern/rna_modifier.c | 14 +--- .../modifiers/intern/MOD_mesh_to_volume.cc | 24 ++----- .../geometry/nodes/node_geo_mesh_to_volume.cc | 26 ++----- 6 files changed, 40 insertions(+), 103 deletions(-) diff --git a/source/blender/geometry/GEO_mesh_to_volume.hh b/source/blender/geometry/GEO_mesh_to_volume.hh index 393a9cc68fb..98eff7a8999 100644 --- a/source/blender/geometry/GEO_mesh_to_volume.hh +++ b/source/blender/geometry/GEO_mesh_to_volume.hh @@ -47,8 +47,6 @@ VolumeGrid *fog_volume_grid_add_from_mesh(Volume *volume, const Mesh *mesh, const float4x4 &mesh_to_volume_space_transform, float voxel_size, - bool fill_volume, - float exterior_band_width, float interior_band_width, float density); /** diff --git a/source/blender/geometry/intern/mesh_to_volume.cc b/source/blender/geometry/intern/mesh_to_volume.cc index dd6fd27d6c5..2f41c9ba29c 100644 --- a/source/blender/geometry/intern/mesh_to_volume.cc +++ b/source/blender/geometry/intern/mesh_to_volume.cc @@ -10,8 +10,10 @@ #include "GEO_mesh_to_volume.hh" #ifdef WITH_OPENVDB +# include # include # include +# include # include namespace blender::geometry { @@ -88,26 +90,28 @@ float volume_compute_voxel_size(const Depsgraph *depsgraph, float3 bb_max; bounds_fn(bb_min, bb_max); - /* Compute the voxel size based on the desired number of voxels and the approximated bounding - * box of the volume. */ + /* Compute the diagonal of the bounding box. This is used because + * it will always be bigger than the widest side of the mesh. */ const float diagonal = math::distance(math::transform_point(transform, bb_max), math::transform_point(transform, bb_min)); - const float approximate_volume_side_length = diagonal + exterior_band_width * 2.0f; - const float voxel_size = approximate_volume_side_length / res.settings.voxel_amount / - volume_simplify; - return voxel_size; + + /* To get the approximate size per voxel, first subtract the exterior band from the requested + * voxel amount, then divide the diagonal with this value if it's bigger than 1. */ + const float voxel_size = + (diagonal / std::max(1.0f, float(res.settings.voxel_amount) - 2.0f * exterior_band_width)); + + /* Return the simplified voxel size. */ + return voxel_size / volume_simplify; } static openvdb::FloatGrid::Ptr mesh_to_fog_volume_grid( const Mesh *mesh, const float4x4 &mesh_to_volume_space_transform, const float voxel_size, - const bool fill_volume, - const float exterior_band_width, const float interior_band_width, const float density) { - if (voxel_size == 0.0f) { + if (voxel_size < 1e-5f || interior_band_width <= 0.0f) { return nullptr; } @@ -117,22 +121,21 @@ static openvdb::FloatGrid::Ptr mesh_to_fog_volume_grid( mesh_to_index_space_transform.location() -= 0.5f; OpenVDBMeshAdapter mesh_adapter{*mesh, mesh_to_index_space_transform}; + const float interior = std::max(1.0f, interior_band_width / voxel_size); - /* Convert the bandwidths from object in index space. */ - const float exterior = MAX2(0.001f, exterior_band_width / voxel_size); - const float interior = MAX2(0.001f, interior_band_width / voxel_size); - - /* Setting the interior bandwidth to FLT_MAX, will make it fill the entire volume. */ + openvdb::math::Transform::Ptr transform = openvdb::math::Transform::createLinearTransform( + voxel_size); openvdb::FloatGrid::Ptr new_grid = openvdb::tools::meshToVolume( - mesh_adapter, {}, exterior, fill_volume ? FLT_MAX : interior); + mesh_adapter, *transform, 1.0f, interior); - /* Give each grid cell a fixed density for now. */ - openvdb::tools::foreach ( - new_grid->beginValueOn(), - [density](const openvdb::FloatGrid::ValueOnIter &iter) { iter.setValue(density); }); - - new_grid->setGridClass(openvdb::GRID_FOG_VOLUME); + openvdb::tools::sdfToFogVolume(*new_grid); + if (density != 1.0f) { + openvdb::tools::foreach (new_grid->beginValueOn(), + [&](const openvdb::FloatGrid::ValueOnIter &iter) { + iter.modifyValue([&](float &value) { value *= density; }); + }); + } return new_grid; } @@ -180,31 +183,12 @@ VolumeGrid *fog_volume_grid_add_from_mesh(Volume *volume, const Mesh *mesh, const float4x4 &mesh_to_volume_space_transform, const float voxel_size, - const bool fill_volume, - const float exterior_band_width, const float interior_band_width, const float density) { - VolumeGrid *c_grid = BKE_volume_grid_add(volume, name.c_str(), VOLUME_GRID_FLOAT); - openvdb::FloatGrid::Ptr grid = openvdb::gridPtrCast( - BKE_volume_grid_openvdb_for_write(volume, c_grid, false)); - - /* Generate grid from mesh */ - openvdb::FloatGrid::Ptr mesh_grid = mesh_to_fog_volume_grid(mesh, - mesh_to_volume_space_transform, - voxel_size, - fill_volume, - exterior_band_width, - interior_band_width, - density); - - if (mesh_grid != nullptr) { - /* Merge the generated grid. Should be cheap because grid has just been created. */ - grid->merge(*mesh_grid); - /* Change transform so that the index space is correctly transformed to object space. */ - grid->transform().postScale(voxel_size); - } - return c_grid; + openvdb::FloatGrid::Ptr mesh_grid = mesh_to_fog_volume_grid( + mesh, mesh_to_volume_space_transform, voxel_size, interior_band_width, density); + return mesh_grid ? BKE_volume_grid_add_vdb(*volume, name, std::move(mesh_grid)) : nullptr; } VolumeGrid *sdf_volume_grid_add_from_mesh(Volume *volume, diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index 7af83f8f888..da65898f551 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -2333,14 +2333,7 @@ typedef struct MeshToVolumeModifierData { * different. */ int voxel_amount; - /** If true, every cell in the enclosed volume gets a density. Otherwise, the interior_band_width - * is used. */ - char fill_volume; - char _pad1[3]; - - /** Band widths are in object space. */ float interior_band_width; - float exterior_band_width; float density; char _pad2[4]; diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index cb75eb501a8..dae7d8f3b06 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -7085,19 +7085,9 @@ static void rna_def_modifier_mesh_to_volume(BlenderRNA *brna) RNA_def_property_range(prop, 0, INT_MAX); RNA_def_property_update(prop, 0, "rna_Modifier_update"); - prop = RNA_def_property(srna, "use_fill_volume", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "fill_volume", 1); - RNA_def_property_ui_text( - prop, "Fill Volume", "Initialize the density grid in every cell inside the enclosed volume"); - RNA_def_property_update(prop, 0, "rna_Modifier_update"); - prop = RNA_def_property(srna, "interior_band_width", PROP_FLOAT, PROP_NONE); - RNA_def_property_ui_text(prop, "Interior Band Width", "Width of the volume inside of the mesh"); - RNA_def_property_range(prop, 0.0, FLT_MAX); - RNA_def_property_update(prop, 0, "rna_Modifier_update"); - - prop = RNA_def_property(srna, "exterior_band_width", PROP_FLOAT, PROP_NONE); - RNA_def_property_ui_text(prop, "Exterior Band Width", "Width of the volume outside of the mesh"); + RNA_def_property_ui_text( + prop, "Interior Band Width", "Width of the gradient inside of the mesh"); RNA_def_property_range(prop, 0.0, FLT_MAX); RNA_def_property_update(prop, 0, "rna_Modifier_update"); diff --git a/source/blender/modifiers/intern/MOD_mesh_to_volume.cc b/source/blender/modifiers/intern/MOD_mesh_to_volume.cc index cb13f032969..ca4c9b84858 100644 --- a/source/blender/modifiers/intern/MOD_mesh_to_volume.cc +++ b/source/blender/modifiers/intern/MOD_mesh_to_volume.cc @@ -51,9 +51,7 @@ static void initData(ModifierData *md) mvmd->resolution_mode = MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_AMOUNT; mvmd->voxel_size = 0.1f; mvmd->voxel_amount = 32; - mvmd->fill_volume = true; - mvmd->interior_band_width = 0.1f; - mvmd->exterior_band_width = 0.1f; + mvmd->interior_band_width = 0.2f; mvmd->density = 1.0f; } @@ -89,12 +87,7 @@ static void panel_draw(const bContext * /*C*/, Panel *panel) { uiLayout *col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "use_fill_volume", 0, nullptr, ICON_NONE); - uiItemR(col, ptr, "exterior_band_width", 0, nullptr, ICON_NONE); - - uiLayout *subcol = uiLayoutColumn(col, false); - uiLayoutSetActive(subcol, !mvmd->fill_volume); - uiItemR(subcol, ptr, "interior_band_width", 0, nullptr, ICON_NONE); + uiItemR(col, ptr, "interior_band_width", 0, nullptr, ICON_NONE); } { uiLayout *col = uiLayoutColumn(layout, false); @@ -140,13 +133,13 @@ static Volume *mesh_to_volume(ModifierData *md, resolution.mode = (MeshToVolumeModifierResolutionMode)mvmd->resolution_mode; if (resolution.mode == MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_AMOUNT) { resolution.settings.voxel_amount = mvmd->voxel_amount; - if (resolution.settings.voxel_amount <= 0.0f) { + if (resolution.settings.voxel_amount < 1.0f) { return input_volume; } } else if (resolution.mode == MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_SIZE) { resolution.settings.voxel_size = mvmd->voxel_size; - if (resolution.settings.voxel_size <= 0.0f) { + if (resolution.settings.voxel_size < 1e-5f) { return input_volume; } } @@ -157,11 +150,8 @@ static Volume *mesh_to_volume(ModifierData *md, r_max = bb->vec[6]; }; - const float voxel_size = geometry::volume_compute_voxel_size(ctx->depsgraph, - bounds_fn, - resolution, - mvmd->exterior_band_width, - mesh_to_own_object_space_transform); + const float voxel_size = geometry::volume_compute_voxel_size( + ctx->depsgraph, bounds_fn, resolution, 0.0f, mesh_to_own_object_space_transform); /* Create a new volume. */ Volume *volume; @@ -178,8 +168,6 @@ static Volume *mesh_to_volume(ModifierData *md, mesh, mesh_to_own_object_space_transform, voxel_size, - mvmd->fill_volume, - mvmd->exterior_band_width, mvmd->interior_band_width, mvmd->density); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_volume.cc index e3f391563f9..5c30986fca2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_volume.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_volume.cc @@ -32,21 +32,12 @@ static void node_declare(NodeDeclarationBuilder &b) .max(FLT_MAX) .subtype(PROP_DISTANCE); b.add_input(N_("Voxel Amount")).default_value(64.0f).min(0.0f).max(FLT_MAX); - b.add_input(N_("Exterior Band Width")) - .default_value(0.1f) - .min(0.0f) - .max(FLT_MAX) - .subtype(PROP_DISTANCE) - .description(N_("Width of the volume outside of the mesh")); b.add_input(N_("Interior Band Width")) - .default_value(0.0f) - .min(0.0f) + .default_value(0.2f) + .min(0.0001f) .max(FLT_MAX) .subtype(PROP_DISTANCE) - .description(N_("Width of the volume inside of the mesh")); - b.add_input(N_("Fill Volume")) - .default_value(true) - .description(N_("Initialize the density grid in every cell inside the enclosed volume")); + .description(N_("Width of the gradient inside of the mesh")); b.add_output(CTX_N_(BLT_I18NCONTEXT_ID_ID, "Volume")) .translation_context(BLT_I18NCONTEXT_ID_ID); } @@ -86,9 +77,7 @@ static Volume *create_volume_from_mesh(const Mesh &mesh, GeoNodeExecParams ¶ *(const NodeGeometryMeshToVolume *)params.node().storage; const float density = params.get_input("Density"); - const float exterior_band_width = params.get_input("Exterior Band Width"); const float interior_band_width = params.get_input("Interior Band Width"); - const bool fill_volume = params.get_input("Fill Volume"); geometry::MeshToVolumeResolution resolution; resolution.mode = (MeshToVolumeModifierResolutionMode)storage.resolution_mode; @@ -119,11 +108,8 @@ static Volume *create_volume_from_mesh(const Mesh &mesh, GeoNodeExecParams ¶ r_max = max; }; - const float voxel_size = geometry::volume_compute_voxel_size(params.depsgraph(), - bounds_fn, - resolution, - exterior_band_width, - mesh_to_volume_space_transform); + const float voxel_size = geometry::volume_compute_voxel_size( + params.depsgraph(), bounds_fn, resolution, 0.0f, mesh_to_volume_space_transform); Volume *volume = reinterpret_cast(BKE_id_new_nomain(ID_VO, nullptr)); @@ -133,8 +119,6 @@ static Volume *create_volume_from_mesh(const Mesh &mesh, GeoNodeExecParams ¶ &mesh, mesh_to_volume_space_transform, voxel_size, - fill_volume, - exterior_band_width, interior_band_width, density);