diff options
Diffstat (limited to 'source')
-rw-r--r-- | source/blender/blenkernel/BKE_geometry_set.hh | 5 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_node.h | 1 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/geometry_set.cc | 10 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/node.cc | 1 | ||||
-rw-r--r-- | source/blender/geometry/CMakeLists.txt | 16 | ||||
-rw-r--r-- | source/blender/geometry/GEO_mesh_to_volume.hh | 50 | ||||
-rw-r--r-- | source/blender/geometry/intern/mesh_to_volume.cc | 168 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_node_types.h | 5 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_nodetree.c | 26 | ||||
-rw-r--r-- | source/blender/modifiers/intern/MOD_mesh_to_volume.cc | 155 | ||||
-rw-r--r-- | source/blender/nodes/NOD_geometry.h | 1 | ||||
-rw-r--r-- | source/blender/nodes/NOD_static_types.h | 1 | ||||
-rw-r--r-- | source/blender/nodes/geometry/CMakeLists.txt | 1 | ||||
-rw-r--r-- | source/blender/nodes/geometry/nodes/node_geo_mesh_to_volume.cc | 183 |
14 files changed, 501 insertions, 122 deletions
diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index 8d658c9be15..1d4291473bb 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -412,6 +412,11 @@ struct GeometrySet { static GeometrySet create_with_mesh( Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); /** + * Create a new geometry set that only contains the given volume. + */ + static GeometrySet create_with_volume( + Volume *volume, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); + /** * Create a new geometry set that only contains the given point cloud. */ static GeometrySet create_with_pointcloud( diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 4a84f8fc3c4..bad3e0005b8 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1498,6 +1498,7 @@ struct TexResult; #define GEO_NODE_VOLUME_CUBE 1161 #define GEO_NODE_POINTS 1162 #define GEO_NODE_FIELD_ON_DOMAIN 1163 +#define GEO_NODE_MESH_TO_VOLUME 1164 /** \} */ diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc index 1a43c4d01b0..70a39acf620 100644 --- a/source/blender/blenkernel/intern/geometry_set.cc +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -322,6 +322,16 @@ GeometrySet GeometrySet::create_with_mesh(Mesh *mesh, GeometryOwnershipType owne return geometry_set; } +GeometrySet GeometrySet::create_with_volume(Volume *volume, GeometryOwnershipType ownership) +{ + GeometrySet geometry_set; + if (volume != nullptr) { + VolumeComponent &component = geometry_set.get_component_for_write<VolumeComponent>(); + component.replace(volume, ownership); + } + return geometry_set; +} + GeometrySet GeometrySet::create_with_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership) { diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index b6ab1e0174b..244c9ccd048 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -4804,6 +4804,7 @@ static void registerGeometryNodes() register_node_type_geo_mesh_subdivide(); register_node_type_geo_mesh_to_curve(); register_node_type_geo_mesh_to_points(); + register_node_type_geo_mesh_to_volume(); register_node_type_geo_object_info(); register_node_type_geo_points(); register_node_type_geo_points_to_vertices(); diff --git a/source/blender/geometry/CMakeLists.txt b/source/blender/geometry/CMakeLists.txt index 531487a45e2..f0fb5c5c9af 100644 --- a/source/blender/geometry/CMakeLists.txt +++ b/source/blender/geometry/CMakeLists.txt @@ -19,6 +19,7 @@ set(SRC intern/mesh_merge_by_distance.cc intern/mesh_primitive_cuboid.cc intern/mesh_to_curve_convert.cc + intern/mesh_to_volume.cc intern/point_merge_by_distance.cc intern/realize_instances.cc intern/resample_curves.cc @@ -29,6 +30,7 @@ set(SRC GEO_mesh_merge_by_distance.hh GEO_mesh_primitive_cuboid.hh GEO_mesh_to_curve.hh + GEO_mesh_to_volume.hh GEO_point_merge_by_distance.hh GEO_realize_instances.hh GEO_resample_curves.hh @@ -41,6 +43,20 @@ set(LIB bf_blenlib ) +if(WITH_OPENVDB) + list(APPEND INC + ../../../intern/openvdb + ) + list(APPEND INC_SYS + ${OPENVDB_INCLUDE_DIRS} + ) + list(APPEND LIB + bf_intern_openvdb + ${OPENVDB_LIBRARIES} + ) + add_definitions(-DWITH_OPENVDB ${OPENVDB_DEFINITIONS}) +endif() + if(WITH_TBB) add_definitions(-DWITH_TBB) diff --git a/source/blender/geometry/GEO_mesh_to_volume.hh b/source/blender/geometry/GEO_mesh_to_volume.hh new file mode 100644 index 00000000000..384293df336 --- /dev/null +++ b/source/blender/geometry/GEO_mesh_to_volume.hh @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_float4x4.hh" +#include "BLI_string_ref.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" + +#pragma once + +struct Volume; +struct VolumeGrid; +struct Depsgraph; + +/** \file + * \ingroup geo + */ + +namespace blender::geometry { + +struct MeshToVolumeResolution { + MeshToVolumeModifierResolutionMode mode; + union { + float voxel_size; + float voxel_amount; + } settings; +}; + +#ifdef WITH_OPENVDB +float volume_compute_voxel_size(const Depsgraph *depsgraph, + const float3 &bb_min, + const float3 &bb_max, + const MeshToVolumeResolution resolution, + float exterior_band_width, + const float4x4 &transform); +/** + * Add a new VolumeGrid to the Volume by converting the supplied mesh + */ +VolumeGrid *volume_grid_add_from_mesh(Volume *volume, + const StringRefNull name, + 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); +#endif +} // namespace blender::geometry diff --git a/source/blender/geometry/intern/mesh_to_volume.cc b/source/blender/geometry/intern/mesh_to_volume.cc new file mode 100644 index 00000000000..93a424f9a94 --- /dev/null +++ b/source/blender/geometry/intern/mesh_to_volume.cc @@ -0,0 +1,168 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BKE_mesh_runtime.h" +#include "BKE_volume.h" + +#include "GEO_mesh_to_volume.hh" + +#ifdef WITH_OPENVDB +# include <openvdb/openvdb.h> +# include <openvdb/tools/GridTransformer.h> +# include <openvdb/tools/VolumeToMesh.h> + +namespace blender::geometry { + +/* This class follows the MeshDataAdapter interface from openvdb. */ +class OpenVDBMeshAdapter { + private: + Span<MVert> vertices_; + Span<MLoop> loops_; + Span<MLoopTri> looptris_; + float4x4 transform_; + + public: + OpenVDBMeshAdapter(const Mesh &mesh, float4x4 transform); + size_t polygonCount() const; + size_t pointCount() const; + size_t vertexCount(size_t UNUSED(polygon_index)) const; + void getIndexSpacePoint(size_t polygon_index, size_t vertex_index, openvdb::Vec3d &pos) const; +}; + +OpenVDBMeshAdapter::OpenVDBMeshAdapter(const Mesh &mesh, float4x4 transform) + : vertices_(mesh.mvert, mesh.totvert), loops_(mesh.mloop, mesh.totloop), transform_(transform) +{ + /* This only updates a cache and can be considered to be logically const. */ + const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(&mesh); + const int looptris_len = BKE_mesh_runtime_looptri_len(&mesh); + looptris_ = Span(looptris, looptris_len); +} + +size_t OpenVDBMeshAdapter::polygonCount() const +{ + return static_cast<size_t>(looptris_.size()); +} + +size_t OpenVDBMeshAdapter::pointCount() const +{ + return static_cast<size_t>(vertices_.size()); +} + +size_t OpenVDBMeshAdapter::vertexCount(size_t UNUSED(polygon_index)) const +{ + /* All polygons are triangles. */ + return 3; +} + +void OpenVDBMeshAdapter::getIndexSpacePoint(size_t polygon_index, + size_t vertex_index, + openvdb::Vec3d &pos) const +{ + const MLoopTri &looptri = looptris_[polygon_index]; + const MVert &vertex = vertices_[loops_[looptri.tri[vertex_index]].v]; + const float3 transformed_co = transform_ * float3(vertex.co); + pos = &transformed_co.x; +} + +float volume_compute_voxel_size(const Depsgraph *depsgraph, + const float3 &bb_min, + const float3 &bb_max, + const MeshToVolumeResolution res, + const float exterior_band_width, + const float4x4 &transform) +{ + const float volume_simplify = BKE_volume_simplify_factor(depsgraph); + if (volume_simplify == 0.0f) { + return 0.0f; + } + + if (res.mode == MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_SIZE) { + return res.settings.voxel_size / volume_simplify; + } + if (res.settings.voxel_amount <= 0) { + return 0; + } + /* Compute the voxel size based on the desired number of voxels and the approximated bounding + * box of the volume. */ + const float diagonal = math::distance(transform * bb_max, 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; +} + +static openvdb::FloatGrid::Ptr mesh_to_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) { + return nullptr; + } + + float4x4 mesh_to_index_space_transform; + scale_m4_fl(mesh_to_index_space_transform.values, 1.0f / voxel_size); + mul_m4_m4_post(mesh_to_index_space_transform.values, mesh_to_volume_space_transform.values); + /* Better align generated grid with the source mesh. */ + add_v3_fl(mesh_to_index_space_transform.values[3], -0.5f); + + OpenVDBMeshAdapter mesh_adapter{*mesh, mesh_to_index_space_transform}; + + /* 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); + + openvdb::FloatGrid::Ptr new_grid; + if (fill_volume) { + /* Setting the interior bandwidth to FLT_MAX, will make it fill the entire volume. */ + new_grid = openvdb::tools::meshToVolume<openvdb::FloatGrid>( + mesh_adapter, {}, exterior, FLT_MAX); + } + else { + new_grid = openvdb::tools::meshToVolume<openvdb::FloatGrid>( + mesh_adapter, {}, exterior, 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); }); + + return new_grid; +} + +VolumeGrid *volume_grid_add_from_mesh(Volume *volume, + const StringRefNull name, + 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<openvdb::FloatGrid>( + BKE_volume_grid_openvdb_for_write(volume, c_grid, false)); + + /* Generate grid from mesh */ + openvdb::FloatGrid::Ptr mesh_grid = mesh_to_volume_grid(mesh, + mesh_to_volume_space_transform, + voxel_size, + fill_volume, + exterior_band_width, + interior_band_width, + density); + + /* Merge the generated grid. Should be cheap because grid has just been created. */ + grid->merge(*mesh_grid); + /* Set class to "Fog Volume". */ + grid->setGridClass(openvdb::GRID_FOG_VOLUME); + /* Change transform so that the index space is correctly transformed to object space. */ + grid->transform().postScale(voxel_size); + return c_grid; +} +} // namespace blender::geometry +#endif diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 25c8a1f1514..3f3eb6e0571 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1248,6 +1248,11 @@ typedef struct NodeGeometryVolumeToMesh { uint8_t resolution_mode; } NodeGeometryVolumeToMesh; +typedef struct NodeGeometryMeshToVolume { + /* MeshToVolumeModifierResolutionMode */ + uint8_t resolution_mode; +} NodeGeometryMeshToVolume; + typedef struct NodeGeometrySubdivisionSurface { /* eSubsurfUVSmooth. */ uint8_t uv_smooth; diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 1a8f47605a3..0ad109e711f 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -9970,6 +9970,32 @@ static void def_geo_volume_to_mesh(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_mesh_to_volume(StructRNA *srna) +{ + PropertyRNA *prop; + + static EnumPropertyItem resolution_mode_items[] = { + {MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_AMOUNT, + "VOXEL_AMOUNT", + 0, + "Amount", + "Desired number of voxels along one axis"}, + {MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_SIZE, + "VOXEL_SIZE", + 0, + "Size", + "Desired voxel side length"}, + {0, NULL, 0, NULL, NULL}, + }; + + RNA_def_struct_sdna_from(srna, "NodeGeometryMeshToVolume", "storage"); + + prop = RNA_def_property(srna, "resolution_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, resolution_mode_items); + RNA_def_property_ui_text(prop, "Resolution Mode", "How the voxel size is specified"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + static void def_geo_mesh_circle(StructRNA *srna) { PropertyRNA *prop; diff --git a/source/blender/modifiers/intern/MOD_mesh_to_volume.cc b/source/blender/modifiers/intern/MOD_mesh_to_volume.cc index 11af907adc8..a0ebc9cfdcb 100644 --- a/source/blender/modifiers/intern/MOD_mesh_to_volume.cc +++ b/source/blender/modifiers/intern/MOD_mesh_to_volume.cc @@ -22,6 +22,8 @@ #include "DEG_depsgraph.h" +#include "GEO_mesh_to_volume.hh" + #include "UI_interface.h" #include "UI_resources.h" @@ -39,59 +41,6 @@ #include "RNA_access.h" #include "RNA_prototypes.h" -#ifdef WITH_OPENVDB -# include <openvdb/openvdb.h> -# include <openvdb/tools/MeshToVolume.h> -#endif - -#ifdef WITH_OPENVDB -namespace blender { -/* This class follows the MeshDataAdapter interface from openvdb. */ -class OpenVDBMeshAdapter { - private: - Span<MVert> vertices_; - Span<MLoop> loops_; - Span<MLoopTri> looptris_; - float4x4 transform_; - - public: - OpenVDBMeshAdapter(Mesh &mesh, float4x4 transform) - : vertices_(mesh.mvert, mesh.totvert), - loops_(mesh.mloop, mesh.totloop), - transform_(transform) - { - const MLoopTri *looptries = BKE_mesh_runtime_looptri_ensure(&mesh); - const int looptries_len = BKE_mesh_runtime_looptri_len(&mesh); - looptris_ = Span(looptries, looptries_len); - } - - size_t polygonCount() const - { - return static_cast<size_t>(looptris_.size()); - } - - size_t pointCount() const - { - return static_cast<size_t>(vertices_.size()); - } - - size_t vertexCount(size_t UNUSED(polygon_index)) const - { - /* All polygons are triangles. */ - return 3; - } - - void getIndexSpacePoint(size_t polygon_index, size_t vertex_index, openvdb::Vec3d &pos) const - { - const MLoopTri &looptri = looptris_[polygon_index]; - const MVert &vertex = vertices_[loops_[looptri.tri[vertex_index]].v]; - const float3 transformed_co = transform_ * float3(vertex.co); - pos = &transformed_co.x; - } -}; -} // namespace blender -#endif - static void initData(ModifierData *md) { MeshToVolumeModifierData *mvmd = reinterpret_cast<MeshToVolumeModifierData *>(md); @@ -163,35 +112,6 @@ static void panelRegister(ARegionType *region_type) modifier_panel_register(region_type, eModifierType_MeshToVolume, panel_draw); } -#ifdef WITH_OPENVDB -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 / volume_simplify; - } - if (mvmd->voxel_amount <= 0) { - return 0; - } - /* Compute the voxel size based on the desired number of voxels and the approximated bounding box - * of the volume. */ - const BoundBox *bb = BKE_object_boundbox_get(mvmd->object); - const float diagonal = math::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 / volume_simplify; - return voxel_size; -} -#endif - static Volume *mesh_to_volume(ModifierData *md, const ModifierEvalContext *ctx, Volume *input_volume) @@ -213,51 +133,42 @@ static Volume *mesh_to_volume(ModifierData *md, const float4x4 mesh_to_own_object_space_transform = float4x4(ctx->object->imat) * float4x4(object_to_convert->obmat); - const float voxel_size = compute_voxel_size(ctx, mvmd, mesh_to_own_object_space_transform); - if (voxel_size == 0.0f) { - return input_volume; - } - - float4x4 mesh_to_index_space_transform; - scale_m4_fl(mesh_to_index_space_transform.values, 1.0f / voxel_size); - mul_m4_m4_post(mesh_to_index_space_transform.values, mesh_to_own_object_space_transform.values); - /* Better align generated grid with the source mesh. */ - add_v3_fl(mesh_to_index_space_transform.values[3], -0.5f); - - OpenVDBMeshAdapter mesh_adapter{*mesh, mesh_to_index_space_transform}; - - /* Convert the bandwidths from object in index space. */ - const float exterior_band_width = MAX2(0.001f, mvmd->exterior_band_width / voxel_size); - const float interior_band_width = MAX2(0.001f, mvmd->interior_band_width / voxel_size); - - openvdb::FloatGrid::Ptr new_grid; - if (mvmd->fill_volume) { - /* Setting the interior bandwidth to FLT_MAX, will make it fill the entire volume. */ - new_grid = openvdb::tools::meshToVolume<openvdb::FloatGrid>( - mesh_adapter, {}, exterior_band_width, FLT_MAX); + const BoundBox *bb = BKE_object_boundbox_get(mvmd->object); + geometry::MeshToVolumeResolution resolution; + 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) { + return input_volume; + } } - else { - new_grid = openvdb::tools::meshToVolume<openvdb::FloatGrid>( - mesh_adapter, {}, exterior_band_width, interior_band_width); + 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) { + return input_volume; + } } - /* 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 = openvdb::gridPtrCast<openvdb::FloatGrid>( - BKE_volume_grid_openvdb_for_write(volume, c_density_grid, false)); + const float voxel_size = geometry::volume_compute_voxel_size(ctx->depsgraph, + bb->vec[0], + bb->vec[6], + resolution, + mvmd->exterior_band_width, + mesh_to_own_object_space_transform); - /* Merge the generated grid into the density grid. Should be cheap because density_grid has just - * been created as well. */ - density_grid->merge(*new_grid); - - /* Change transform so that the index space is correctly transformed to object space. */ - density_grid->transform().postScale(voxel_size); + /* Create a new volume. */ + Volume *volume = BKE_volume_new_for_eval(input_volume); - /* Give each grid cell a fixed density for now. */ - openvdb::tools::foreach ( - density_grid->beginValueOn(), - [&](const openvdb::FloatGrid::ValueOnIter &iter) { iter.setValue(mvmd->density); }); + /* Convert mesh to grid and add to volume. */ + geometry::volume_grid_add_from_mesh(volume, + "density", + mesh, + mesh_to_own_object_space_transform, + voxel_size, + mvmd->fill_volume, + mvmd->exterior_band_width, + mvmd->interior_band_width, + mvmd->density); return volume; diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 56d1472e840..889ab2a81e4 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -102,6 +102,7 @@ void register_node_type_geo_mesh_primitive_uv_sphere(void); void register_node_type_geo_mesh_subdivide(void); void register_node_type_geo_mesh_to_curve(void); void register_node_type_geo_mesh_to_points(void); +void register_node_type_geo_mesh_to_volume(void); void register_node_type_geo_object_info(void); void register_node_type_geo_points(void); void register_node_type_geo_points_to_vertices(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 62a6f85a04c..f9925924260 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -357,6 +357,7 @@ DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRI DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "") DefNode(GeometryNode, GEO_NODE_MESH_TO_CURVE, 0, "MESH_TO_CURVE", MeshToCurve, "Mesh to Curve", "") DefNode(GeometryNode, GEO_NODE_MESH_TO_POINTS, def_geo_mesh_to_points, "MESH_TO_POINTS", MeshToPoints, "Mesh to Points", "") +DefNode(GeometryNode, GEO_NODE_MESH_TO_VOLUME, def_geo_mesh_to_volume, "MESH_TO_VOLUME", MeshToVolume, "Mesh To Volume", "") DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "") DefNode(GeometryNode, GEO_NODE_POINTS, 0, "POINTS", Points, "Points", "") DefNode(GeometryNode, GEO_NODE_POINTS_TO_VERTICES, 0, "POINTS_TO_VERTICES", PointsToVertices, "Points to Vertices", "") diff --git a/source/blender/nodes/geometry/CMakeLists.txt b/source/blender/nodes/geometry/CMakeLists.txt index 1fad6ce5b34..9ef3151ddab 100644 --- a/source/blender/nodes/geometry/CMakeLists.txt +++ b/source/blender/nodes/geometry/CMakeLists.txt @@ -112,6 +112,7 @@ set(SRC nodes/node_geo_mesh_subdivide.cc nodes/node_geo_mesh_to_curve.cc nodes/node_geo_mesh_to_points.cc + nodes/node_geo_mesh_to_volume.cc nodes/node_geo_object_info.cc nodes/node_geo_points.cc nodes/node_geo_points_to_vertices.cc 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 new file mode 100644 index 00000000000..9d8a77c3947 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_volume.cc @@ -0,0 +1,183 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "DEG_depsgraph_query.h" +#include "node_geometry_util.hh" + +#include "BKE_lib_id.h" +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" +#include "BKE_mesh_wrapper.h" +#include "BKE_object.h" +#include "BKE_volume.h" + +#include "GEO_mesh_to_volume.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes::node_geo_mesh_to_volume_cc { + +NODE_STORAGE_FUNCS(NodeGeometryMeshToVolume) + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input<decl::Float>(N_("Density")).default_value(1.0f).min(0.01f).max(FLT_MAX); + b.add_input<decl::Float>(N_("Voxel Size")) + .default_value(0.3f) + .min(0.01f) + .max(FLT_MAX) + .subtype(PROP_DISTANCE); + b.add_input<decl::Float>(N_("Voxel Amount")).default_value(64.0f).min(0.0f).max(FLT_MAX); + b.add_input<decl::Float>(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<decl::Float>(N_("Interior Band Width")) + .default_value(0.0f) + .min(0.0f) + .max(FLT_MAX) + .subtype(PROP_DISTANCE) + .description(N_("Width of the volume inside of the mesh")); + b.add_input<decl::Bool>(N_("Fill Volume")) + .default_value(true) + .description(N_("Initialize the density grid in every cell inside the enclosed volume")); + b.add_output<decl::Geometry>(N_("Volume")); +} + +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "resolution_mode", 0, IFACE_("Resolution"), ICON_NONE); +} + +static void node_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryMeshToVolume *data = (NodeGeometryMeshToVolume *)MEM_callocN( + sizeof(NodeGeometryMeshToVolume), __func__); + data->resolution_mode = MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_AMOUNT; + node->storage = data; +} + +static void node_update(bNodeTree *ntree, bNode *node) +{ + NodeGeometryMeshToVolume *data = (NodeGeometryMeshToVolume *)node->storage; + + bNodeSocket *voxel_size_socket = nodeFindSocket(node, SOCK_IN, "Voxel Size"); + bNodeSocket *voxel_amount_socket = nodeFindSocket(node, SOCK_IN, "Voxel Amount"); + nodeSetSocketAvailability(ntree, + voxel_amount_socket, + data->resolution_mode == MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_AMOUNT); + nodeSetSocketAvailability(ntree, + voxel_size_socket, + data->resolution_mode == MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_SIZE); +} + +#ifdef WITH_OPENVDB + +static Volume *create_volume_from_mesh(const Mesh &mesh, GeoNodeExecParams ¶ms) +{ + const NodeGeometryMeshToVolume &storage = + *(const NodeGeometryMeshToVolume *)params.node().storage; + + const float density = params.get_input<float>("Density"); + const float exterior_band_width = params.get_input<float>("Exterior Band Width"); + const float interior_band_width = params.get_input<float>("Interior Band Width"); + const bool fill_volume = params.get_input<bool>("Fill Volume"); + + geometry::MeshToVolumeResolution resolution; + resolution.mode = (MeshToVolumeModifierResolutionMode)storage.resolution_mode; + if (resolution.mode == MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_AMOUNT) { + resolution.settings.voxel_amount = params.get_input<float>("Voxel Amount"); + if (resolution.settings.voxel_amount <= 0.0f) { + return nullptr; + } + } + else if (resolution.mode == MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_SIZE) { + resolution.settings.voxel_size = params.get_input<float>("Voxel Size"); + if (resolution.settings.voxel_size <= 0.0f) { + return nullptr; + } + } + + float3 min, max; + INIT_MINMAX(min, max); + if (!BKE_mesh_wrapper_minmax(&mesh, min, max)) { + min = float3(-1.0f); + max = float3(1.0f); + } + + const float4x4 mesh_to_volume_space_transform = float4x4::identity(); + + const float voxel_size = geometry::volume_compute_voxel_size(params.depsgraph(), + min, + max, + resolution, + exterior_band_width, + mesh_to_volume_space_transform); + + Volume *volume = (Volume *)BKE_id_new_nomain(ID_VO, nullptr); + BKE_volume_init_grids(volume); + + /* Convert mesh to grid and add to volume. */ + geometry::volume_grid_add_from_mesh(volume, + "density", + &mesh, + mesh_to_volume_space_transform, + voxel_size, + fill_volume, + exterior_band_width, + interior_band_width, + density); + + return volume; +} + +#endif /* WITH_OPENVDB */ + +static void node_geo_exec(GeoNodeExecParams params) +{ +#ifdef WITH_OPENVDB + GeometrySet geometry_set(params.extract_input<GeometrySet>("Mesh")); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (geometry_set.has_mesh()) { + Volume *volume = create_volume_from_mesh(*geometry_set.get_mesh_for_read(), params); + geometry_set.replace_volume(volume); + geometry_set.keep_only({GEO_COMPONENT_TYPE_VOLUME, GEO_COMPONENT_TYPE_INSTANCES}); + } + }); + params.set_output("Volume", std::move(geometry_set)); +#else + params.error_message_add(NodeWarningType::Error, + TIP_("Disabled, Blender was compiled without OpenVDB")); + params.set_default_remaining_outputs(); + return; +#endif +} + +} // namespace blender::nodes::node_geo_mesh_to_volume_cc + +void register_node_type_geo_mesh_to_volume() +{ + namespace file_ns = blender::nodes::node_geo_mesh_to_volume_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_MESH_TO_VOLUME, "Mesh to Volume", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + node_type_size(&ntype, 200, 120, 700); + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; + node_type_storage( + &ntype, "NodeGeometryMeshToVolume", node_free_standard_storage, node_copy_standard_storage); + nodeRegisterType(&ntype); +} |