Geometry Nodes: Add "Connected" mode to Merge by Distance node

Expose the "Connected" mode from the weld modifier in the
"Merge by Distance" geometry node. This method only merges
vertices along existing edges, but it can be much faster
because it doesn't have to build a KD Tree of all selected
points.

Differential Revision: https://developer.blender.org/D14321
This commit is contained in:
Aleksi Juvani 2022-04-06 17:30:56 -05:00 committed by Hans Goudey
parent 181d577d7d
commit 81ec3dce65
5 changed files with 112 additions and 7 deletions

View File

@ -2648,5 +2648,22 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
*/
{
/* Keep this block, even when empty. */
/* Add node storage for the merge by distance node. */
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
if (ntree->type == NTREE_GEOMETRY) {
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type == GEO_NODE_MERGE_BY_DISTANCE) {
if (node->storage == NULL) {
NodeGeometryMergeByDistance *data = MEM_callocN(sizeof(NodeGeometryMergeByDistance),
__func__);
data->mode = GEO_NODE_MERGE_BY_DISTANCE_MODE_ALL;
node->storage = data;
}
}
}
}
}
FOREACH_NODETREE_END;
}
}

View File

@ -1296,6 +1296,11 @@ typedef struct NodeGeometryMeshCone {
uint8_t fill_type;
} NodeGeometryMeshCone;
typedef struct NodeGeometryMergeByDistance {
/* GeometryNodeMergeByDistanceMode. */
uint8_t mode;
} NodeGeometryMergeByDistance;
typedef struct NodeGeometryMeshLine {
/* GeometryNodeMeshLineMode. */
uint8_t mode;
@ -2007,6 +2012,11 @@ typedef enum GeometryNodeMeshCircleFillType {
GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN = 2,
} GeometryNodeMeshCircleFillType;
typedef enum GeometryNodeMergeByDistanceMode {
GEO_NODE_MERGE_BY_DISTANCE_MODE_ALL = 0,
GEO_NODE_MERGE_BY_DISTANCE_MODE_CONNECTED = 1,
} GeometryNodeMergeByDistanceMode;
typedef enum GeometryNodeMeshLineMode {
GEO_NODE_MESH_LINE_MODE_END_POINTS = 0,
GEO_NODE_MESH_LINE_MODE_OFFSET = 1,

View File

@ -9846,6 +9846,32 @@ static void def_geo_mesh_cone(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_geo_merge_by_distance(StructRNA *srna)
{
PropertyRNA *prop;
static EnumPropertyItem mode_items[] = {
{GEO_NODE_MERGE_BY_DISTANCE_MODE_ALL,
"ALL",
0,
"All",
"Merge all close selected points, whether or not they are connected"},
{GEO_NODE_MERGE_BY_DISTANCE_MODE_CONNECTED,
"CONNECTED",
0,
"Connected",
"Only merge mesh vertices along existing edges. This method can be much faster"},
{0, NULL, 0, NULL, NULL},
};
RNA_def_struct_sdna_from(srna, "NodeGeometryMergeByDistance", "storage");
prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, mode_items);
RNA_def_property_ui_text(prop, "Mode", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_geo_mesh_line(StructRNA *srna)
{
PropertyRNA *prop;

View File

@ -334,7 +334,7 @@ DefNode(GeometryNode, GEO_NODE_INSTANCES_TO_POINTS, 0, "INSTANCES_TO_POINTS", In
DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "")
DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "")
DefNode(GeometryNode, GEO_NODE_MATERIAL_SELECTION, 0, "MATERIAL_SELECTION", MaterialSelection, "Material Selection", "")
DefNode(GeometryNode, GEO_NODE_MERGE_BY_DISTANCE, 0, "MERGE_BY_DISTANCE", MergeByDistance, "Merge by Distance", "")
DefNode(GeometryNode, GEO_NODE_MERGE_BY_DISTANCE, def_geo_merge_by_distance, "MERGE_BY_DISTANCE", MergeByDistance, "Merge by Distance", "")
DefNode(GeometryNode, GEO_NODE_MESH_BOOLEAN, def_geo_boolean, "MESH_BOOLEAN", MeshBoolean, "Mesh Boolean", "")
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CIRCLE, def_geo_mesh_circle, "MESH_PRIMITIVE_CIRCLE", MeshCircle, "Mesh Circle", "")
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CONE, def_geo_mesh_cone, "MESH_PRIMITIVE_CONE", MeshCone, "Cone", "")

View File

@ -3,10 +3,15 @@
#include "GEO_mesh_merge_by_distance.hh"
#include "GEO_point_merge_by_distance.hh"
#include "UI_interface.h"
#include "UI_resources.h"
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_merge_by_distance_cc {
NODE_STORAGE_FUNCS(NodeGeometryMergeByDistance)
static void node_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>(N_("Geometry"))
@ -16,6 +21,20 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>(N_("Geometry"));
}
static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
uiItemR(layout, ptr, "mode", 0, "", ICON_NONE);
}
static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
NodeGeometryMergeByDistance *data = MEM_cnew<NodeGeometryMergeByDistance>(__func__);
data->mode = GEO_NODE_MERGE_BY_DISTANCE_MODE_ALL;
node->storage = data;
}
static PointCloud *pointcloud_merge_by_distance(const PointCloudComponent &src_points,
const float merge_distance,
const Field<bool> &selection_field)
@ -34,9 +53,24 @@ static PointCloud *pointcloud_merge_by_distance(const PointCloudComponent &src_p
return geometry::point_merge_by_distance(src_points, merge_distance, selection);
}
static std::optional<Mesh *> mesh_merge_by_distance(const MeshComponent &mesh_component,
const float merge_distance,
const Field<bool> &selection_field)
static std::optional<Mesh *> mesh_merge_by_distance_connected(const MeshComponent &mesh_component,
const float merge_distance,
const Field<bool> &selection_field)
{
const int src_size = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT);
Array<bool> selection(src_size);
GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_POINT};
FieldEvaluator evaluator{context, src_size};
evaluator.add_with_destination(selection_field, selection.as_mutable_span());
evaluator.evaluate();
const Mesh &mesh = *mesh_component.get_for_read();
return geometry::mesh_merge_by_distance_connected(mesh, selection, merge_distance, false);
}
static std::optional<Mesh *> mesh_merge_by_distance_all(const MeshComponent &mesh_component,
const float merge_distance,
const Field<bool> &selection_field)
{
const int src_size = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT);
GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_POINT};
@ -55,6 +89,9 @@ static std::optional<Mesh *> mesh_merge_by_distance(const MeshComponent &mesh_co
static void node_geo_exec(GeoNodeExecParams params)
{
const NodeGeometryMergeByDistance &storage = node_storage(params.node());
const GeometryNodeMergeByDistanceMode mode = (GeometryNodeMergeByDistanceMode)storage.mode;
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
const Field<bool> selection = params.extract_input<Field<bool>>("Selection");
@ -69,8 +106,18 @@ static void node_geo_exec(GeoNodeExecParams params)
}
}
if (geometry_set.has_mesh()) {
std::optional<Mesh *> result = mesh_merge_by_distance(
*geometry_set.get_component_for_read<MeshComponent>(), merge_distance, selection);
const MeshComponent &component = *geometry_set.get_component_for_read<MeshComponent>();
std::optional<Mesh *> result;
switch (mode) {
case GEO_NODE_MERGE_BY_DISTANCE_MODE_ALL:
result = mesh_merge_by_distance_all(component, merge_distance, selection);
break;
case GEO_NODE_MERGE_BY_DISTANCE_MODE_CONNECTED:
result = mesh_merge_by_distance_connected(component, merge_distance, selection);
break;
default:
BLI_assert_unreachable();
}
if (result) {
geometry_set.replace_mesh(*result);
}
@ -89,8 +136,13 @@ void register_node_type_geo_merge_by_distance()
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_MERGE_BY_DISTANCE, "Merge by Distance", NODE_CLASS_GEOMETRY);
node_type_init(&ntype, file_ns::node_init);
node_type_storage(&ntype,
"NodeGeometryMergeByDistance",
node_free_standard_storage,
node_copy_standard_storage);
ntype.declare = file_ns::node_declare;
ntype.geometry_node_execute = file_ns::node_geo_exec;
ntype.draw_buttons = file_ns::node_layout;
nodeRegisterType(&ntype);
}