diff options
author | Hans Goudey <h.goudey@me.com> | 2021-10-26 19:25:44 +0300 |
---|---|---|
committer | Hans Goudey <h.goudey@me.com> | 2021-10-26 19:25:44 +0300 |
commit | 8ddfdfd2b2a52f746312246aa3099ab0df8544a0 (patch) | |
tree | 5b228cb84c3587661a9c5b59878ebc6f99f07dd5 /source | |
parent | 7979dff9dc7985cebb530c7490dc730d9c1acf1d (diff) |
Geometry Nodes: Handle multiple grids in the volume to mesh node
In future use cases, a volume can contain many grids that represent the
density information. In this case, it's better if the volume to mesh node
creates a mesh based on all of the grids in the volume.
This is also a benefit to share-ability, since one doesn't have to
specify the grid name in the node. Instead, in the future we can have
a way to split particular grids into separate volumes, if only one
grid should be considered.
The code changes are relatively simple:
- Move the old volume to mesh node to the legacy folder.
- Run the volume to mesh node on all instance geometry, like elsewhere.
- Make the blenkernel's volume to mesh API a bit more specific.
Differential Revision: https://developer.blender.org/D12997
Diffstat (limited to 'source')
-rw-r--r-- | source/blender/blenkernel/BKE_node.h | 3 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_volume.h | 5 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_volume_to_mesh.hh | 32 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/node.cc | 1 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/volume.cc | 17 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/volume_to_mesh.cc | 93 | ||||
-rw-r--r-- | source/blender/blenloader/intern/versioning_290.c | 2 | ||||
-rw-r--r-- | source/blender/blenloader/intern/versioning_300.c | 3 | ||||
-rw-r--r-- | source/blender/nodes/CMakeLists.txt | 2 | ||||
-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/nodes/legacy/node_geo_volume_to_mesh.cc | 173 | ||||
-rw-r--r-- | source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc | 127 |
13 files changed, 383 insertions, 77 deletions
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index c868eb414f7..8daa96164ef 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1442,7 +1442,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_COLLECTION_INFO 1023 #define GEO_NODE_IS_VIEWPORT 1024 #define GEO_NODE_LEGACY_ATTRIBUTE_PROXIMITY 1025 -#define GEO_NODE_VOLUME_TO_MESH 1026 +#define GEO_NODE_LEGACY_VOLUME_TO_MESH 1026 #define GEO_NODE_LEGACY_ATTRIBUTE_COMBINE_XYZ 1027 #define GEO_NODE_LEGACY_ATTRIBUTE_SEPARATE_XYZ 1028 #define GEO_NODE_SUBDIVIDE_MESH 1029 @@ -1548,6 +1548,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_CURVE_TO_POINTS 1130 #define GEO_NODE_INSTANCES_TO_POINTS 1131 #define GEO_NODE_IMAGE_TEXTURE 1132 +#define GEO_NODE_VOLUME_TO_MESH 1133 /** \} */ diff --git a/source/blender/blenkernel/BKE_volume.h b/source/blender/blenkernel/BKE_volume.h index 5fe0d54c2cf..601e0cf26a9 100644 --- a/source/blender/blenkernel/BKE_volume.h +++ b/source/blender/blenkernel/BKE_volume.h @@ -160,6 +160,7 @@ bool BKE_volume_save(const struct Volume *volume, #ifdef __cplusplus # include "BLI_float3.hh" # include "BLI_float4x4.hh" +# include "BLI_string_ref.hh" bool BKE_volume_min_max(const Volume *volume, blender::float3 &r_min, blender::float3 &r_max); @@ -167,6 +168,10 @@ bool BKE_volume_min_max(const Volume *volume, blender::float3 &r_min, blender::f # include <openvdb/openvdb.h> # include <openvdb/points/PointDataGrid.h> +VolumeGrid *BKE_volume_grid_add_vdb(Volume &volume, + blender::StringRef name, + openvdb::GridBase::Ptr vdb_grid); + bool BKE_volume_grid_bounds(openvdb::GridBase::ConstPtr grid, blender::float3 &r_min, blender::float3 &r_max); diff --git a/source/blender/blenkernel/BKE_volume_to_mesh.hh b/source/blender/blenkernel/BKE_volume_to_mesh.hh index 1f6e89636c4..9532da8c23c 100644 --- a/source/blender/blenkernel/BKE_volume_to_mesh.hh +++ b/source/blender/blenkernel/BKE_volume_to_mesh.hh @@ -14,6 +14,8 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "BLI_span.hh" + #include "DNA_modifier_types.h" #ifdef WITH_OPENVDB @@ -33,10 +35,40 @@ struct VolumeToMeshResolution { }; #ifdef WITH_OPENVDB + +/** + * The result of converting a volume grid to mesh data, in the format used by the OpenVDB API. + */ +struct OpenVDBMeshData { + std::vector<openvdb::Vec3s> verts; + std::vector<openvdb::Vec3I> tris; + std::vector<openvdb::Vec4I> quads; + bool is_empty() const + { + return verts.empty(); + } +}; + struct Mesh *volume_to_mesh(const openvdb::GridBase &grid, const VolumeToMeshResolution &resolution, const float threshold, const float adaptivity); + +struct OpenVDBMeshData volume_to_mesh_data(const openvdb::GridBase &grid, + const VolumeToMeshResolution &resolution, + const float threshold, + const float adaptivity); + +void fill_mesh_from_openvdb_data(const Span<openvdb::Vec3s> vdb_verts, + const Span<openvdb::Vec3I> vdb_tris, + const Span<openvdb::Vec4I> vdb_quads, + const int vert_offset, + const int poly_offset, + const int loop_offset, + MutableSpan<MVert> verts, + MutableSpan<MPoly> polys, + MutableSpan<MLoop> loops); + #endif } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index fb2110d7e53..8494c51a66e 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5753,6 +5753,7 @@ static void registerGeometryNodes() register_node_type_geo_legacy_select_by_handle_type(); register_node_type_geo_legacy_select_by_material(); register_node_type_geo_legacy_subdivision_surface(); + register_node_type_geo_legacy_volume_to_mesh(); register_node_type_geo_align_rotation_to_vector(); register_node_type_geo_attribute_capture(); diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc index 7e7a40d8e9b..a72b5268e1d 100644 --- a/source/blender/blenkernel/intern/volume.cc +++ b/source/blender/blenkernel/intern/volume.cc @@ -36,6 +36,7 @@ #include "BLI_math.h" #include "BLI_path_util.h" #include "BLI_string.h" +#include "BLI_string_ref.hh" #include "BLI_task.hh" #include "BLI_utildefines.h" @@ -71,6 +72,7 @@ static CLG_LogRef LOG = {"bke.volume"}; using blender::float3; using blender::float4x4; using blender::IndexRange; +using blender::StringRef; #ifdef WITH_OPENVDB # include <atomic> @@ -1451,6 +1453,21 @@ VolumeGrid *BKE_volume_grid_add(Volume *volume, const char *name, VolumeGridType #endif } +#ifdef WITH_OPENVDB +VolumeGrid *BKE_volume_grid_add_vdb(Volume &volume, + const StringRef name, + openvdb::GridBase::Ptr vdb_grid) +{ + VolumeGridVector &grids = *volume.runtime.grids; + BLI_assert(BKE_volume_grid_find_for_read(&volume, name.data()) == nullptr); + BLI_assert(BKE_volume_grid_type_openvdb(*vdb_grid) != VOLUME_GRID_UNKNOWN); + + vdb_grid->setName(name); + grids.emplace_back(vdb_grid); + return &grids.back(); +} +#endif + void BKE_volume_grid_remove(Volume *volume, VolumeGrid *grid) { #ifdef WITH_OPENVDB diff --git a/source/blender/blenkernel/intern/volume_to_mesh.cc b/source/blender/blenkernel/intern/volume_to_mesh.cc index e9d6eea4614..6e465b2fdf0 100644 --- a/source/blender/blenkernel/intern/volume_to_mesh.cc +++ b/source/blender/blenkernel/intern/volume_to_mesh.cc @@ -121,46 +121,66 @@ struct VolumeToMeshOp { } }; -static Mesh *new_mesh_from_openvdb_data(Span<openvdb::Vec3s> verts, - Span<openvdb::Vec3I> tris, - Span<openvdb::Vec4I> quads) +/** + * Convert mesh data from the format provided by OpenVDB into Blender's #Mesh data structure. + * This can be used to add mesh data from a grid into an existing mesh rather than merging multiple + * meshes later on. + */ +void fill_mesh_from_openvdb_data(const Span<openvdb::Vec3s> vdb_verts, + const Span<openvdb::Vec3I> vdb_tris, + const Span<openvdb::Vec4I> vdb_quads, + const int vert_offset, + const int poly_offset, + const int loop_offset, + MutableSpan<MVert> verts, + MutableSpan<MPoly> polys, + MutableSpan<MLoop> loops) { - 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); + for (const int i : vdb_verts.index_range()) { + const blender::float3 co = blender::float3(vdb_verts[i].asV()); + copy_v3_v3(verts[vert_offset + i].co, co); } /* Write triangles. */ - for (const int i : tris.index_range()) { - mesh->mpoly[i].loopstart = 3 * i; - mesh->mpoly[i].totloop = 3; + for (const int i : vdb_tris.index_range()) { + polys[poly_offset + i].loopstart = loop_offset + 3 * i; + polys[poly_offset + 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]; + loops[loop_offset + 3 * i + j].v = vert_offset + vdb_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; + const int quad_offset = poly_offset + vdb_tris.size(); + const int quad_loop_offset = loop_offset + vdb_tris.size() * 3; + for (const int i : vdb_quads.index_range()) { + polys[quad_offset + i].loopstart = quad_loop_offset + 4 * i; + polys[quad_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]; + loops[quad_loop_offset + 4 * i + j].v = vert_offset + vdb_quads[i][3 - j]; } } +} - BKE_mesh_calc_edges(mesh, false, false); - BKE_mesh_normals_tag_dirty(mesh); - return mesh; +/** + * Convert an OpenVDB volume grid to corresponding mesh data: vertex positions and quad and + * triangle indices. + */ +bke::OpenVDBMeshData volume_to_mesh_data(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 {}; + } + return {std::move(to_mesh_op.verts), std::move(to_mesh_op.tris), std::move(to_mesh_op.quads)}; } Mesh *volume_to_mesh(const openvdb::GridBase &grid, @@ -168,14 +188,27 @@ Mesh *volume_to_mesh(const openvdb::GridBase &grid, const float threshold, const float adaptivity) { - const VolumeGridType grid_type = BKE_volume_grid_type_openvdb(grid); + const bke::OpenVDBMeshData mesh_data = volume_to_mesh_data( + grid, resolution, threshold, adaptivity); + + const int tot_loops = 3 * mesh_data.tris.size() + 4 * mesh_data.quads.size(); + const int tot_polys = mesh_data.tris.size() + mesh_data.quads.size(); + Mesh *mesh = BKE_mesh_new_nomain(mesh_data.verts.size(), 0, 0, tot_loops, tot_polys); + + fill_mesh_from_openvdb_data(mesh_data.verts, + mesh_data.tris, + mesh_data.quads, + 0, + 0, + 0, + {mesh->mvert, mesh->totvert}, + {mesh->mpoly, mesh->totpoly}, + {mesh->mloop, mesh->totloop}); - VolumeToMeshOp to_mesh_op{grid, resolution, threshold, adaptivity}; - if (!BKE_volume_grid_type_operation(grid_type, to_mesh_op)) { - return nullptr; - } + BKE_mesh_calc_edges(mesh, false, false); + BKE_mesh_normals_tag_dirty(mesh); - return new_mesh_from_openvdb_data(to_mesh_op.verts, to_mesh_op.tris, to_mesh_op.quads); + return mesh; } #endif /* WITH_OPENVDB */ diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index d2c722f8be7..def14768ec6 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -1985,7 +1985,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) if (!MAIN_VERSION_ATLEAST(bmain, 293, 18)) { FOREACH_NODETREE_BEGIN (bmain, ntree, id) { if (ntree->type == NTREE_GEOMETRY) { - version_node_socket_name(ntree, GEO_NODE_VOLUME_TO_MESH, "Grid", "Density"); + version_node_socket_name(ntree, GEO_NODE_LEGACY_VOLUME_TO_MESH, "Grid", "Density"); } } FOREACH_NODETREE_END; diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 57447db8723..d0d6c1471b7 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -967,7 +967,7 @@ static bool geometry_node_is_293_legacy(const short node_type) /* Maybe legacy: Special case for grid names? Or finish patch from level set branch to * generate a mesh for all grids in the volume. */ - case GEO_NODE_VOLUME_TO_MESH: + case GEO_NODE_LEGACY_VOLUME_TO_MESH: return false; /* Legacy: Transferred *all* attributes before, will not transfer all built-ins now. */ @@ -2098,6 +2098,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } version_node_id(ntree, FN_NODE_SLICE_STRING, "FunctionNodeSliceString"); version_geometry_nodes_set_position_node_offset(ntree); + version_node_id(ntree, GEO_NODE_LEGACY_VOLUME_TO_MESH, "GeometryNodeLegacyVolumeToMesh"); } /* Keep this block, even when empty. */ diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 657d0d11441..acf0ab9b224 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -193,6 +193,8 @@ set(SRC geometry/nodes/legacy/node_geo_raycast.cc geometry/nodes/legacy/node_geo_select_by_material.cc geometry/nodes/legacy/node_geo_subdivision_surface.cc + geometry/nodes/legacy/node_geo_volume_to_mesh.cc + geometry/nodes/node_geo_attribute_capture.cc geometry/nodes/node_geo_attribute_remove.cc geometry/nodes/node_geo_attribute_statistic.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 6702443e20e..d6f0b511861 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -47,6 +47,7 @@ void register_node_type_geo_legacy_curve_subdivide(void); void register_node_type_geo_legacy_edge_split(void); void register_node_type_geo_legacy_subdivision_surface(void); void register_node_type_geo_legacy_raycast(void); +void register_node_type_geo_legacy_volume_to_mesh(void); void register_node_type_geo_align_rotation_to_vector(void); void register_node_type_geo_attribute_capture(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 7fd4840489e..af9685fcf0d 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -321,6 +321,7 @@ DefNode(GeometryNode, GEO_NODE_LEGACY_POINTS_TO_VOLUME, def_geo_legacy_points_to DefNode(GeometryNode, GEO_NODE_LEGACY_RAYCAST, def_geo_legacy_raycast, "LEGACY_RAYCAST", LegacyRaycast, "Raycast", "") DefNode(GeometryNode, GEO_NODE_LEGACY_SELECT_BY_MATERIAL, 0, "LEGACY_SELECT_BY_MATERIAL", LegacySelectByMaterial, "Select by Material", "") DefNode(GeometryNode, GEO_NODE_LEGACY_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "LEGACY_SUBDIVISION_SURFACE", LegacySubdivisionSurface, "Subdivision Surface", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_VOLUME_TO_MESH, def_geo_volume_to_mesh, "LEGACY_VOLUME_TO_MESH", LegacyVolumeToMesh, "Volume to Mesh", "") DefNode(GeometryNode, GEO_NODE_CAPTURE_ATTRIBUTE, def_geo_attribute_capture, "CAPTURE_ATTRIBUTE", CaptureAttribute, "Capture Attribute", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_REMOVE, 0, "ATTRIBUTE_REMOVE", AttributeRemove, "Attribute Remove", "") diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_volume_to_mesh.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_volume_to_mesh.cc new file mode 100644 index 00000000000..45f55dcf92e --- /dev/null +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_volume_to_mesh.cc @@ -0,0 +1,173 @@ +/* + * 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 <openvdb/tools/GridTransformer.h> +# include <openvdb/tools/VolumeToMesh.h> +#endif + +#include "node_geometry_util.hh" + +#include "BKE_lib_id.h" +#include "BKE_material.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" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes { + +static void geo_node_volume_to_mesh_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::String>("Density"); + b.add_input<decl::Float>("Voxel Size").default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); + b.add_input<decl::Float>("Voxel Amount").default_value(64.0f).min(0.0f); + b.add_input<decl::Float>("Threshold").default_value(0.1f).min(0.0f); + b.add_input<decl::Float>("Adaptivity").min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_output<decl::Geometry>("Geometry"); +} + +static void geo_node_volume_to_mesh_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 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, "Density"); + 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<VolumeComponent>()) { + 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<float>("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<float>("Voxel Size"); + if (resolution.settings.voxel_size <= 0.0f) { + return; + } + } + + const VolumeComponent *component = geometry_set_in.get_component_for_read<VolumeComponent>(); + const Volume *volume = component->get_for_read(); + if (volume == nullptr) { + return; + } + + const Main *bmain = DEG_get_bmain(params.depsgraph()); + BKE_volume_load(volume, bmain); + + const std::string grid_name = params.get_input<std::string>("Density"); + const VolumeGrid *volume_grid = BKE_volume_grid_find_for_read(volume, grid_name.c_str()); + if (volume_grid == nullptr) { + return; + } + + float threshold = params.get_input<float>("Threshold"); + float adaptivity = params.get_input<float>("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; + } + BKE_id_material_eval_ensure_default_slot(&mesh->id); + MeshComponent &dst_component = geometry_set_out.get_component_for_write<MeshComponent>(); + dst_component.replace(mesh); +} + +#endif /* WITH_OPENVDB */ + +static void geo_node_volume_to_mesh_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set_in = params.extract_input<GeometrySet>("Geometry"); + GeometrySet geometry_set_out; + +#ifdef WITH_OPENVDB + create_mesh_from_volume(geometry_set_in, geometry_set_out, params); +#else + params.error_message_add(NodeWarningType::Error, + TIP_("Disabled, Blender was compiled without OpenVDB")); +#endif + + params.set_output("Geometry", geometry_set_out); +} + +} // namespace blender::nodes + +void register_node_type_geo_legacy_volume_to_mesh() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_VOLUME_TO_MESH, "Volume to Mesh", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_volume_to_mesh_declare; + node_type_storage( + &ntype, "NodeGeometryVolumeToMesh", node_free_standard_storage, node_copy_standard_storage); + node_type_size(&ntype, 170, 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; + ntype.draw_buttons = blender::nodes::geo_node_volume_to_mesh_layout; + nodeRegisterType(&ntype); +} 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 index d1fb22f4ba2..c0084de367f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc @@ -39,13 +39,12 @@ namespace blender::nodes { static void geo_node_volume_to_mesh_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Density"); + b.add_input<decl::Geometry>("Volume"); b.add_input<decl::Float>("Voxel Size").default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); b.add_input<decl::Float>("Voxel Amount").default_value(64.0f).min(0.0f); b.add_input<decl::Float>("Threshold").default_value(0.1f).min(0.0f); b.add_input<decl::Float>("Adaptivity").min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_output<decl::Geometry>("Geometry"); + b.add_output<decl::Geometry>("Mesh"); } static void geo_node_volume_to_mesh_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -60,11 +59,6 @@ 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, "Density"); - bNodeSocketValueString *grid_socket_value = (bNodeSocketValueString *)grid_socket->default_value; - STRNCPY(grid_socket_value->value, "density"); - node->storage = data; } @@ -82,75 +76,120 @@ static void geo_node_volume_to_mesh_update(bNodeTree *UNUSED(ntree), bNode *node #ifdef WITH_OPENVDB -static void create_mesh_from_volume(GeometrySet &geometry_set_in, - GeometrySet &geometry_set_out, - GeoNodeExecParams ¶ms) +static bke::VolumeToMeshResolution get_resolution_param(const GeoNodeExecParams ¶ms) { - if (!geometry_set_in.has<VolumeComponent>()) { - 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<float>("Voxel Amount"); - if (resolution.settings.voxel_amount <= 0.0f) { - return; - } + resolution.settings.voxel_amount = std::max(params.get_input<float>("Voxel Amount"), 0.0f); } else if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE) { - resolution.settings.voxel_size = params.get_input<float>("Voxel Size"); - if (resolution.settings.voxel_size <= 0.0f) { - return; - } + resolution.settings.voxel_size = std::max(params.get_input<float>("Voxel Size"), 0.0f); + } + + return resolution; +} + +static Mesh *create_mesh_from_volume_grids(Span<openvdb::GridBase::ConstPtr> grids, + const float threshold, + const float adaptivity, + const bke::VolumeToMeshResolution &resolution) +{ + Array<bke::OpenVDBMeshData> mesh_data(grids.size()); + for (const int i : grids.index_range()) { + mesh_data[i] = bke::volume_to_mesh_data(*grids[i], resolution, threshold, adaptivity); + } + + int vert_offset = 0; + int poly_offset = 0; + int loop_offset = 0; + Array<int> vert_offsets(mesh_data.size()); + Array<int> poly_offsets(mesh_data.size()); + Array<int> loop_offsets(mesh_data.size()); + for (const int i : grids.index_range()) { + const bke::OpenVDBMeshData &data = mesh_data[i]; + vert_offsets[i] = vert_offset; + poly_offsets[i] = poly_offset; + loop_offsets[i] = loop_offset; + vert_offset += data.verts.size(); + poly_offset += (data.tris.size() + data.quads.size()); + loop_offset += (3 * data.tris.size() + 4 * data.quads.size()); } - const VolumeComponent *component = geometry_set_in.get_component_for_read<VolumeComponent>(); - const Volume *volume = component->get_for_read(); + Mesh *mesh = BKE_mesh_new_nomain(vert_offset, 0, 0, loop_offset, poly_offset); + BKE_id_material_eval_ensure_default_slot(&mesh->id); + MutableSpan<MVert> verts{mesh->mvert, mesh->totvert}; + MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop}; + MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly}; + + for (const int i : grids.index_range()) { + const bke::OpenVDBMeshData &data = mesh_data[i]; + bke::fill_mesh_from_openvdb_data(data.verts, + data.tris, + data.quads, + vert_offsets[i], + poly_offsets[i], + loop_offsets[i], + verts, + polys, + loops); + } + + BKE_mesh_calc_edges(mesh, false, false); + BKE_mesh_normals_tag_dirty(mesh); + + return mesh; +} + +static Mesh *create_mesh_from_volume(GeometrySet &geometry_set, GeoNodeExecParams ¶ms) +{ + const Volume *volume = geometry_set.get_volume_for_read(); if (volume == nullptr) { - return; + return nullptr; } + const bke::VolumeToMeshResolution resolution = get_resolution_param(params); const Main *bmain = DEG_get_bmain(params.depsgraph()); BKE_volume_load(volume, bmain); - const std::string grid_name = params.get_input<std::string>("Density"); - const VolumeGrid *volume_grid = BKE_volume_grid_find_for_read(volume, grid_name.c_str()); - if (volume_grid == nullptr) { - return; + Vector<openvdb::GridBase::ConstPtr> grids; + for (const int i : IndexRange(BKE_volume_num_grids(volume))) { + const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, i); + openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid); + grids.append(std::move(grid)); } - float threshold = params.get_input<float>("Threshold"); - float adaptivity = params.get_input<float>("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; + if (grids.is_empty()) { + return nullptr; } - BKE_id_material_eval_ensure_default_slot(&mesh->id); - MeshComponent &dst_component = geometry_set_out.get_component_for_write<MeshComponent>(); - dst_component.replace(mesh); + + return create_mesh_from_volume_grids(grids, + params.get_input<float>("Threshold"), + params.get_input<float>("Adaptivity"), + resolution); } #endif /* WITH_OPENVDB */ static void geo_node_volume_to_mesh_exec(GeoNodeExecParams params) { - GeometrySet geometry_set_in = params.extract_input<GeometrySet>("Geometry"); - GeometrySet geometry_set_out; + GeometrySet geometry_set = params.extract_input<GeometrySet>("Volume"); #ifdef WITH_OPENVDB - create_mesh_from_volume(geometry_set_in, geometry_set_out, params); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + Mesh *mesh = create_mesh_from_volume(geometry_set, params); + geometry_set.replace_mesh(mesh); + geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES}); + }); #else params.error_message_add(NodeWarningType::Error, TIP_("Disabled, Blender was compiled without OpenVDB")); #endif - params.set_output("Geometry", geometry_set_out); + params.set_output("Mesh", std::move(geometry_set)); } } // namespace blender::nodes |