From 16abe9343a5e01426e85e4bb9d0e95c3217e35ea Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 5 Feb 2021 16:20:14 +0100 Subject: Geometry Nodes: add Volume to Mesh node This node takes a volume and generates a mesh on it's "surface". The surface is defined by a threshold value. Currently, the node only works on volumes generated by the Points to Volume node. This limitation will be resolved soonish. Ref T84605. Differential Revision: https://developer.blender.org/D10243 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_node.h | 1 + source/blender/blenkernel/BKE_volume_to_mesh.hh | 43 +++++ source/blender/blenkernel/CMakeLists.txt | 2 + source/blender/blenkernel/intern/node.cc | 1 + source/blender/blenkernel/intern/volume_to_mesh.cc | 183 +++++++++++++++++++++ source/blender/editors/space_node/drawnode.c | 12 ++ source/blender/makesdna/DNA_node_types.h | 7 + source/blender/makesrna/intern/rna_nodetree.c | 32 ++++ .../blender/modifiers/intern/MOD_volume_to_mesh.cc | 165 +++---------------- source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_geometry.h | 1 + source/blender/nodes/NOD_static_types.h | 2 +- .../geometry/nodes/node_geo_volume_to_mesh.cc | 160 ++++++++++++++++++ 14 files changed, 471 insertions(+), 140 deletions(-) create mode 100644 source/blender/blenkernel/BKE_volume_to_mesh.hh create mode 100644 source/blender/blenkernel/intern/volume_to_mesh.cc create mode 100644 source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index dac027d649e..40b57fd10de 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -526,6 +526,7 @@ geometry_node_categories = [ ]), GeometryNodeCategory("GEO_VOLUME", "Volume", items=[ NodeItem("GeometryNodePointsToVolume"), + NodeItem("GeometryNodeVolumeToMesh"), ]), GeometryNodeCategory("GEO_UTILITIES", "Utilities", items=[ NodeItem("ShaderNodeMapRange"), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 0b8ef70de2a..f4f753c4084 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1369,6 +1369,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_COLLECTION_INFO 1023 #define GEO_NODE_IS_VIEWPORT 1024 #define GEO_NODE_ATTRIBUTE_PROXIMITY 1025 +#define GEO_NODE_VOLUME_TO_MESH 1026 /** \} */ diff --git a/source/blender/blenkernel/BKE_volume_to_mesh.hh b/source/blender/blenkernel/BKE_volume_to_mesh.hh new file mode 100644 index 00000000000..1ec8a8e84cd --- /dev/null +++ b/source/blender/blenkernel/BKE_volume_to_mesh.hh @@ -0,0 +1,43 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "DNA_modifier_types.h" + +#ifdef WITH_OPENVDB +# include +#endif + +struct Mesh; +struct VolumeGrid; + +namespace blender::bke { + +struct VolumeToMeshResolution { + VolumeToMeshResolutionMode mode; + union { + float voxel_size; + float voxel_amount; + } settings; +}; + +#ifdef WITH_OPENVDB +struct Mesh *volume_to_mesh(const openvdb::GridBase &grid, + const VolumeToMeshResolution &resolution, + const float threshold, + const float adaptivity); +#endif + +} // namespace blender::bke diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 0f0ef926a52..0da9598f0ad 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -259,6 +259,7 @@ set(SRC intern/unit.c intern/volume.cc intern/volume_render.cc + intern/volume_to_mesh.cc intern/workspace.c intern/world.c intern/writeavi.c @@ -413,6 +414,7 @@ set(SRC BKE_unit.h BKE_volume.h BKE_volume_render.h + BKE_volume_to_mesh.hh BKE_workspace.h BKE_world.h BKE_writeavi.h diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 44efbe64202..434875820c3 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -4777,6 +4777,7 @@ static void registerGeometryNodes() register_node_type_geo_points_to_volume(); register_node_type_geo_collection_info(); register_node_type_geo_is_viewport(); + register_node_type_geo_volume_to_mesh(); } static void registerFunctionNodes() diff --git a/source/blender/blenkernel/intern/volume_to_mesh.cc b/source/blender/blenkernel/intern/volume_to_mesh.cc new file mode 100644 index 00000000000..7ab67516242 --- /dev/null +++ b/source/blender/blenkernel/intern/volume_to_mesh.cc @@ -0,0 +1,183 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include + +#include "BLI_float3.hh" +#include "BLI_span.hh" +#include "BLI_utildefines.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_volume_types.h" + +#include "BKE_mesh.h" +#include "BKE_volume.h" + +#ifdef WITH_OPENVDB +# include +# include +#endif + +#include "BKE_volume_to_mesh.hh" + +namespace blender::bke { + +#ifdef WITH_OPENVDB + +struct VolumeToMeshOp { + const openvdb::GridBase &base_grid; + const VolumeToMeshResolution resolution; + const float threshold; + const float adaptivity; + std::vector verts; + std::vector tris; + std::vector quads; + + template bool operator()() + { + if constexpr (std::is_scalar_v) { + this->generate_mesh_data(); + return true; + } + return false; + } + + template void generate_mesh_data() + { + const GridType &grid = static_cast(base_grid); + + if (this->resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_GRID) { + this->grid_to_mesh(grid); + return; + } + + const float resolution_factor = this->compute_resolution_factor(base_grid); + typename GridType::Ptr temp_grid = this->create_grid_with_changed_resolution( + grid, resolution_factor); + this->grid_to_mesh(*temp_grid); + } + + template + 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(old_grid, *new_grid); + new_grid->transform() = old_grid.transform(); + new_grid->transform().preScale(1.0f / resolution_factor); + return new_grid; + } + + float compute_resolution_factor(const openvdb::GridBase &grid) const + { + const openvdb::Vec3s voxel_size{grid.voxelSize()}; + const float current_voxel_size = std::max({voxel_size[0], voxel_size[1], voxel_size[2]}); + const float desired_voxel_size = this->compute_desired_voxel_size(grid); + return current_voxel_size / desired_voxel_size; + } + + float compute_desired_voxel_size(const openvdb::GridBase &grid) const + { + if (this->resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE) { + return this->resolution.settings.voxel_size; + } + const openvdb::CoordBBox coord_bbox = base_grid.evalActiveVoxelBoundingBox(); + const openvdb::BBoxd bbox = grid.transform().indexToWorld(coord_bbox); + const float max_extent = bbox.extents()[bbox.maxExtent()]; + const float voxel_size = max_extent / this->resolution.settings.voxel_amount; + return voxel_size; + } + + template void grid_to_mesh(const GridType &grid) + { + openvdb::tools::volumeToMesh( + grid, this->verts, this->tris, this->quads, this->threshold, this->adaptivity); + + /* Better align generated mesh with volume (see T85312). */ + openvdb::Vec3s offset = grid.voxelSize() / 2.0f; + for (openvdb::Vec3s &position : this->verts) { + position += offset; + } + } +}; + +static Mesh *new_mesh_from_openvdb_data(Span verts, + Span tris, + Span quads) +{ + const int tot_loops = 3 * tris.size() + 4 * quads.size(); + const int tot_polys = tris.size() + quads.size(); + + Mesh *mesh = BKE_mesh_new_nomain(verts.size(), 0, 0, tot_loops, tot_polys); + + /* Write vertices. */ + for (const int i : verts.index_range()) { + const blender::float3 co = blender::float3(verts[i].asV()); + copy_v3_v3(mesh->mvert[i].co, co); + } + + /* Write triangles. */ + for (const int i : tris.index_range()) { + mesh->mpoly[i].loopstart = 3 * i; + mesh->mpoly[i].totloop = 3; + for (int j = 0; j < 3; j++) { + /* Reverse vertex order to get correct normals. */ + mesh->mloop[3 * i + j].v = tris[i][2 - j]; + } + } + + /* Write quads. */ + const int poly_offset = tris.size(); + const int loop_offset = tris.size() * 3; + for (const int i : quads.index_range()) { + mesh->mpoly[poly_offset + i].loopstart = loop_offset + 4 * i; + mesh->mpoly[poly_offset + i].totloop = 4; + for (int j = 0; j < 4; j++) { + /* Reverse vertex order to get correct normals. */ + mesh->mloop[loop_offset + 4 * i + j].v = quads[i][3 - j]; + } + } + + BKE_mesh_calc_edges(mesh, false, false); + BKE_mesh_calc_normals(mesh); + return mesh; +} + +Mesh *volume_to_mesh(const openvdb::GridBase &grid, + const VolumeToMeshResolution &resolution, + const float threshold, + const float adaptivity) +{ + const VolumeGridType grid_type = BKE_volume_grid_type_openvdb(grid); + + VolumeToMeshOp to_mesh_op{grid, resolution, threshold, adaptivity}; + if (!BKE_volume_grid_type_operation(grid_type, to_mesh_op)) { + return nullptr; + } + + return new_mesh_from_openvdb_data(to_mesh_op.verts, to_mesh_op.tris, to_mesh_op.quads); +} + +#endif /* WITH_OPENVDB */ + +} // namespace blender::bke diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index 7d0ff2331d0..132dcd8a9fb 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -3389,6 +3389,15 @@ static void node_geometry_buts_attribute_proximity(uiLayout *layout, uiItemR(layout, ptr, "target_geometry_element", DEFAULT_FLAGS, "", ICON_NONE); } +static void node_geometry_buts_volume_to_mesh(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "resolution_mode", DEFAULT_FLAGS, IFACE_("Resolution"), ICON_NONE); +} + static void node_geometry_set_butfunc(bNodeType *ntype) { switch (ntype->type) { @@ -3455,6 +3464,9 @@ static void node_geometry_set_butfunc(bNodeType *ntype) case GEO_NODE_ATTRIBUTE_PROXIMITY: ntype->draw_buttons = node_geometry_buts_attribute_proximity; break; + case GEO_NODE_VOLUME_TO_MESH: + ntype->draw_buttons = node_geometry_buts_volume_to_mesh; + break; } } diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 73b6a1b66c6..e8748e28776 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1209,6 +1209,13 @@ typedef struct NodeGeometryAttributeProximity { char _pad[7]; } NodeGeometryAttributeProximity; +typedef struct NodeGeometryVolumeToMesh { + /* VolumeToMeshResolutionMode */ + uint8_t resolution_mode; + + char _pad[7]; +} NodeGeometryVolumeToMesh; + /* script node mode */ #define NODE_SCRIPT_INTERNAL 0 #define NODE_SCRIPT_EXTERNAL 1 diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 77334f10cd0..5dbc574d073 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -29,6 +29,7 @@ #include "DNA_material_types.h" #include "DNA_mesh_types.h" +#include "DNA_modifier_types.h" #include "DNA_node_types.h" #include "DNA_object_types.h" #include "DNA_particle_types.h" @@ -9000,6 +9001,37 @@ static void def_geo_attribute_proximity(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_volume_to_mesh(StructRNA *srna) +{ + PropertyRNA *prop; + + static EnumPropertyItem resolution_mode_items[] = { + {VOLUME_TO_MESH_RESOLUTION_MODE_GRID, + "GRID", + 0, + "Grid", + "Use resolution of the volume grid"}, + {VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT, + "VOXEL_AMOUNT", + 0, + "Voxel Amount", + "Desired number of voxels along one axis"}, + {VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE, + "VOXEL_SIZE", + 0, + "Voxel Size", + "Desired voxel side length"}, + {0, NULL, 0, NULL, NULL}, + }; + + RNA_def_struct_sdna_from(srna, "NodeGeometryVolumeToMesh", "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 rna_def_shader_node(BlenderRNA *brna) diff --git a/source/blender/modifiers/intern/MOD_volume_to_mesh.cc b/source/blender/modifiers/intern/MOD_volume_to_mesh.cc index 941bc8409f7..41ed7ae983a 100644 --- a/source/blender/modifiers/intern/MOD_volume_to_mesh.cc +++ b/source/blender/modifiers/intern/MOD_volume_to_mesh.cc @@ -24,6 +24,7 @@ #include "BKE_mesh.h" #include "BKE_modifier.h" #include "BKE_volume.h" +#include "BKE_volume_to_mesh.hh" #include "MOD_modifiertypes.h" #include "MOD_ui_common.h" @@ -128,139 +129,6 @@ static void panelRegister(ARegionType *region_type) modifier_panel_register(region_type, eModifierType_VolumeToMesh, panel_draw); } -#ifdef WITH_OPENVDB - -struct VolumeToMeshOp { - const openvdb::GridBase &base_grid; - VolumeToMeshModifierData &vmmd; - const ModifierEvalContext &ctx; - std::vector verts; - std::vector tris; - std::vector quads; - - template bool operator()() - { - if constexpr (std::is_scalar_v) { - this->generate_mesh_data(); - return true; - } - else { - return false; - } - } - - template void generate_mesh_data() - { - /* Make a new transform from the index space into the mesh object space. */ - openvdb::math::Transform::Ptr transform = this->base_grid.transform().copy(); - transform->postMult(openvdb::Mat4d((float *)vmmd.object->obmat)); - openvdb::Mat4d imat = openvdb::Mat4d((float *)ctx.object->imat); - /* `imat` had floating point issues and wasn't affine. */ - imat.setCol(3, openvdb::Vec4d(0, 0, 0, 1)); - transform->postMult(imat); - - /* Create a new grid with a different transform. The underlying tree is shared. */ - typename GridType::ConstPtr grid = openvdb::gridConstPtrCast( - this->base_grid.copyGridReplacingTransform(transform)); - - if (this->vmmd.resolution_mode == VOLUME_TO_MESH_RESOLUTION_MODE_GRID) { - this->grid_to_mesh(*grid); - return; - } - - const float resolution_factor = this->compute_resolution_factor(*grid); - typename GridType::Ptr temp_grid = this->create_grid_with_changed_resolution( - *grid, resolution_factor); - this->grid_to_mesh(*temp_grid); - } - - template - 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(old_grid, *new_grid); - new_grid->transform() = old_grid.transform(); - new_grid->transform().preScale(1.0f / resolution_factor); - return new_grid; - } - - float compute_resolution_factor(const openvdb::GridBase &grid) const - { - const openvdb::Vec3s voxel_size{grid.voxelSize()}; - const float current_voxel_size = std::max({voxel_size[0], voxel_size[1], voxel_size[2]}); - const float desired_voxel_size = this->compute_desired_voxel_size(grid); - return current_voxel_size / desired_voxel_size; - } - - float compute_desired_voxel_size(const openvdb::GridBase &grid) const - { - if (this->vmmd.resolution_mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE) { - return this->vmmd.voxel_size; - } - const openvdb::CoordBBox coord_bbox = base_grid.evalActiveVoxelBoundingBox(); - const openvdb::BBoxd bbox = grid.transform().indexToWorld(coord_bbox); - const float max_extent = bbox.extents()[bbox.maxExtent()]; - const float voxel_size = max_extent / this->vmmd.voxel_amount; - return voxel_size; - } - - template void grid_to_mesh(const GridType &grid) - { - openvdb::tools::volumeToMesh( - grid, this->verts, this->tris, this->quads, this->vmmd.threshold, this->vmmd.adaptivity); - } -}; - -static Mesh *new_mesh_from_openvdb_data(Span verts, - Span tris, - Span quads) -{ - const int tot_loops = 3 * tris.size() + 4 * quads.size(); - const int tot_polys = tris.size() + quads.size(); - - Mesh *mesh = BKE_mesh_new_nomain(verts.size(), 0, 0, tot_loops, tot_polys); - - /* Write vertices. */ - for (const int i : verts.index_range()) { - const blender::float3 co = blender::float3(verts[i].asV()); - copy_v3_v3(mesh->mvert[i].co, co); - } - - /* Write triangles. */ - for (const int i : tris.index_range()) { - mesh->mpoly[i].loopstart = 3 * i; - mesh->mpoly[i].totloop = 3; - for (int j = 0; j < 3; j++) { - /* Reverse vertex order to get correct normals. */ - mesh->mloop[3 * i + j].v = tris[i][2 - j]; - } - } - - /* Write quads. */ - const int poly_offset = tris.size(); - const int loop_offset = tris.size() * 3; - for (const int i : quads.index_range()) { - mesh->mpoly[poly_offset + i].loopstart = loop_offset + 4 * i; - mesh->mpoly[poly_offset + i].totloop = 4; - for (int j = 0; j < 4; j++) { - /* Reverse vertex order to get correct normals. */ - mesh->mloop[loop_offset + 4 * i + j].v = quads[i][3 - j]; - } - } - - BKE_mesh_calc_edges(mesh, false, false); - BKE_mesh_calc_normals(mesh); - return mesh; -} -#endif - static Mesh *create_empty_mesh(const Mesh *input_mesh) { Mesh *new_mesh = BKE_mesh_new_nomain(0, 0, 0, 0, 0); @@ -296,16 +164,35 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * return create_empty_mesh(input_mesh); } - const openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid); + const openvdb::GridBase::ConstPtr local_grid = BKE_volume_grid_openvdb_for_read(volume, + volume_grid); + + openvdb::math::Transform::Ptr transform = local_grid->transform().copy(); + transform->postMult(openvdb::Mat4d(((float *)vmmd->object->obmat))); + openvdb::Mat4d imat = openvdb::Mat4d((float *)ctx->object->imat); + /* `imat` had floating point issues and wasn't affine. */ + imat.setCol(3, openvdb::Vec4d(0, 0, 0, 1)); + transform->postMult(imat); + + /* Create a temporary transformed grid. The underlying tree is shared. */ + openvdb::GridBase::ConstPtr transformed_grid = local_grid->copyGridReplacingTransform(transform); + + blender::bke::VolumeToMeshResolution resolution; + resolution.mode = (VolumeToMeshResolutionMode)vmmd->resolution_mode; + if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT) { + resolution.settings.voxel_amount = vmmd->voxel_amount; + } + if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE) { + resolution.settings.voxel_size = vmmd->voxel_size; + } - const VolumeGridType grid_type = BKE_volume_grid_type(volume_grid); - VolumeToMeshOp to_mesh_op{*grid, *vmmd, *ctx}; - if (!BKE_volume_grid_type_operation(grid_type, to_mesh_op)) { - BKE_modifier_set_error(ctx->object, md, "Expected a scalar grid"); + Mesh *mesh = blender::bke::volume_to_mesh( + *transformed_grid, resolution, vmmd->threshold, vmmd->adaptivity); + if (mesh == nullptr) { + BKE_modifier_set_error(ctx->object, md, "Could not generate mesh from grid"); return create_empty_mesh(input_mesh); } - Mesh *mesh = new_mesh_from_openvdb_data(to_mesh_op.verts, to_mesh_op.tris, to_mesh_op.quads); BKE_mesh_copy_settings(mesh, input_mesh); if (vmmd->flag & VOLUME_TO_MESH_USE_SMOOTH_SHADE) { BKE_mesh_smooth_flag_set(mesh, true); diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 76b19685456..a209faa5d17 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -167,6 +167,7 @@ set(SRC geometry/nodes/node_geo_subdivision_surface.cc geometry/nodes/node_geo_transform.cc geometry/nodes/node_geo_triangulate.cc + geometry/nodes/node_geo_volume_to_mesh.cc geometry/node_geometry_exec.cc geometry/node_geometry_tree.cc geometry/node_geometry_util.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 54a728f5bfe..d4d137c8fc5 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -52,6 +52,7 @@ void register_node_type_geo_sample_texture(void); void register_node_type_geo_points_to_volume(void); void register_node_type_geo_collection_info(void); void register_node_type_geo_is_viewport(void); +void register_node_type_geo_volume_to_mesh(void); #ifdef __cplusplus } diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 1cf08a419c9..980790af766 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -294,7 +294,7 @@ DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POIN DefNode(GeometryNode, GEO_NODE_COLLECTION_INFO, def_geo_collection_info, "COLLECTION_INFO", CollectionInfo, "Collection Info", "") DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_PROXIMITY, def_geo_attribute_proximity, "ATTRIBUTE_PROXIMITY", AttributeProximity, "Attribute Proximity", "") - +DefNode(GeometryNode, GEO_NODE_VOLUME_TO_MESH, def_geo_volume_to_mesh, "VOLUME_TO_MESH", VolumeToMesh, "Volume to Mesh", "") /* undefine macros */ #undef DefNode diff --git a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc new file mode 100644 index 00000000000..b1d1430bccd --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc @@ -0,0 +1,160 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "DEG_depsgraph_query.h" +#ifdef WITH_OPENVDB +# include +# include +#endif + +#include "node_geometry_util.hh" + +#include "BKE_lib_id.h" +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" +#include "BKE_volume.h" +#include "BKE_volume_to_mesh.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +static bNodeSocketTemplate geo_node_volume_to_mesh_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_STRING, N_("Grid")}, + {SOCK_FLOAT, N_("Voxel Size"), 0.3f, 0.0f, 0.0f, 0.0f, 0.01f, FLT_MAX}, + {SOCK_FLOAT, N_("Voxel Amount"), 64.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX}, + {SOCK_FLOAT, N_("Threshold"), 0.1f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX}, + {SOCK_FLOAT, N_("Adaptivity"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_volume_to_mesh_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +namespace blender::nodes { + +static void geo_node_volume_to_mesh_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryVolumeToMesh *data = (NodeGeometryVolumeToMesh *)MEM_callocN( + sizeof(NodeGeometryVolumeToMesh), __func__); + data->resolution_mode = VOLUME_TO_MESH_RESOLUTION_MODE_GRID; + + bNodeSocket *grid_socket = nodeFindSocket(node, SOCK_IN, "Grid"); + bNodeSocketValueString *grid_socket_value = (bNodeSocketValueString *)grid_socket->default_value; + STRNCPY(grid_socket_value->value, "density"); + + node->storage = data; +} + +static void geo_node_volume_to_mesh_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryVolumeToMesh *data = (NodeGeometryVolumeToMesh *)node->storage; + + bNodeSocket *voxel_size_socket = nodeFindSocket(node, SOCK_IN, "Voxel Size"); + bNodeSocket *voxel_amount_socket = nodeFindSocket(node, SOCK_IN, "Voxel Amount"); + nodeSetSocketAvailability(voxel_amount_socket, + data->resolution_mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT); + nodeSetSocketAvailability(voxel_size_socket, + data->resolution_mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE); +} + +#ifdef WITH_OPENVDB + +static void create_mesh_from_volume(GeometrySet &geometry_set_in, + GeometrySet &geometry_set_out, + GeoNodeExecParams ¶ms) +{ + if (!geometry_set_in.has()) { + return; + } + + const NodeGeometryVolumeToMesh &storage = + *(const NodeGeometryVolumeToMesh *)params.node().storage; + + bke::VolumeToMeshResolution resolution; + resolution.mode = (VolumeToMeshResolutionMode)storage.resolution_mode; + if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT) { + resolution.settings.voxel_amount = params.get_input("Voxel Amount"); + if (resolution.settings.voxel_amount <= 0.0f) { + return; + } + } + else if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE) { + resolution.settings.voxel_size = params.get_input("Voxel Size"); + if (resolution.settings.voxel_size <= 0.0f) { + return; + } + } + + const VolumeComponent *component = geometry_set_in.get_component_for_read(); + const Volume *volume = component->get_for_read(); + if (volume == nullptr) { + return; + } + + Main *bmain = DEG_get_bmain(params.depsgraph()); + BKE_volume_load(const_cast(volume), bmain); + + const std::string grid_name = params.get_input("Grid"); + VolumeGrid *volume_grid = BKE_volume_grid_find(volume, grid_name.c_str()); + if (volume_grid == nullptr) { + return; + } + + float threshold = params.get_input("Threshold"); + float adaptivity = params.get_input("Adaptivity"); + + const openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid); + Mesh *mesh = bke::volume_to_mesh(*grid, resolution, threshold, adaptivity); + if (mesh == nullptr) { + return; + } + MeshComponent &dst_component = geometry_set_out.get_component_for_write(); + dst_component.replace(mesh); +} + +#endif /* WITH_OPENVDB */ + +static void geo_node_volume_to_mesh_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set_in = params.extract_input("Geometry"); + GeometrySet geometry_set_out; + +#ifdef WITH_OPENVDB + create_mesh_from_volume(geometry_set_in, geometry_set_out, params); +#endif + + params.set_output("Geometry", geometry_set_out); +} + +} // namespace blender::nodes + +void register_node_type_geo_volume_to_mesh() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_VOLUME_TO_MESH, "Volume to Mesh", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_volume_to_mesh_in, geo_node_volume_to_mesh_out); + node_type_storage( + &ntype, "NodeGeometryVolumeToMesh", node_free_standard_storage, node_copy_standard_storage); + node_type_size(&ntype, 200, 120, 700); + node_type_init(&ntype, blender::nodes::geo_node_volume_to_mesh_init); + node_type_update(&ntype, blender::nodes::geo_node_volume_to_mesh_update); + ntype.geometry_node_execute = blender::nodes::geo_node_volume_to_mesh_exec; + nodeRegisterType(&ntype); +} -- cgit v1.2.3