From e2975cb701692574504967178699c9ab083dc3f8 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Mon, 4 Apr 2022 12:26:50 +0200 Subject: Geometry Nodes: add 'Intersecting Edges' output for boolean node This patch adds a 'Intersecting Edges' output with a boolean selection that only gives you the new edges on intersections. Will work on a couple of examples next, this should make some interesting effects possible (including getting us closer to the "bevel- after-boolean-usecase") To achieve this, a Vector is passed to `direct_mesh_boolean` when the iMesh is still available (and intersecting edges appended), then from those edge indices a selection will be stored as attribute. Differential Revision: https://developer.blender.org/D15151 --- .../blender/blenkernel/BKE_mesh_boolean_convert.hh | 5 ++- .../blenkernel/intern/mesh_boolean_convert.cc | 21 +++++++++- source/blender/modifiers/intern/MOD_boolean.cc | 3 +- .../nodes/geometry/nodes/node_geo_boolean.cc | 47 ++++++++++++++++++---- 4 files changed, 65 insertions(+), 11 deletions(-) diff --git a/source/blender/blenkernel/BKE_mesh_boolean_convert.hh b/source/blender/blenkernel/BKE_mesh_boolean_convert.hh index abaf2ab0178..441783d46a1 100644 --- a/source/blender/blenkernel/BKE_mesh_boolean_convert.hh +++ b/source/blender/blenkernel/BKE_mesh_boolean_convert.hh @@ -23,6 +23,8 @@ namespace blender::meshintersect { * \param material_remaps: An array of maps from material slot numbers in the corresponding mesh * to the material slot in the first mesh. It is OK for material_remaps or any of its constituent * arrays to be empty. + * \param r_intersecting_edges: Array to store indices of edges on the resulting mesh in. These + * 'new' edges are the result of the intersections. */ Mesh *direct_mesh_boolean(Span meshes, Span transforms, @@ -30,6 +32,7 @@ Mesh *direct_mesh_boolean(Span meshes, Span> material_remaps, bool use_self, bool hole_tolerant, - int boolean_mode); + int boolean_mode, + Vector *r_intersecting_edges); } // namespace blender::meshintersect diff --git a/source/blender/blenkernel/intern/mesh_boolean_convert.cc b/source/blender/blenkernel/intern/mesh_boolean_convert.cc index 14bd6aa5b2f..79bdc08fe8f 100644 --- a/source/blender/blenkernel/intern/mesh_boolean_convert.cc +++ b/source/blender/blenkernel/intern/mesh_boolean_convert.cc @@ -791,7 +791,8 @@ Mesh *direct_mesh_boolean(Span meshes, Span> material_remaps, const bool use_self, const bool hole_tolerant, - const int boolean_mode) + const int boolean_mode, + Vector *r_intersecting_edges) { #ifdef WITH_GMP BLI_assert(meshes.size() == transforms.size()); @@ -828,7 +829,23 @@ Mesh *direct_mesh_boolean(Span meshes, write_obj_mesh(m_out, "m_out"); } - return imesh_to_mesh(&m_out, mim); + Mesh *result = imesh_to_mesh(&m_out, mim); + + /* Store intersecting edge indices. */ + if (r_intersecting_edges != nullptr) { + for (int fi : m_out.face_index_range()) { + const Face &face = *m_out.face(fi); + const MPoly &poly = result->mpoly[fi]; + for (int corner_i : face.index_range()) { + if (face.is_intersect[corner_i]) { + int e_index = result->mloop[poly.loopstart + corner_i].e; + r_intersecting_edges->append(e_index); + } + } + } + } + + return result; #else // WITH_GMP UNUSED_VARS(meshes, transforms, diff --git a/source/blender/modifiers/intern/MOD_boolean.cc b/source/blender/modifiers/intern/MOD_boolean.cc index 07504d91fea..5739de1c65c 100644 --- a/source/blender/modifiers/intern/MOD_boolean.cc +++ b/source/blender/modifiers/intern/MOD_boolean.cc @@ -447,7 +447,8 @@ static Mesh *exact_boolean_mesh(BooleanModifierData *bmd, material_remaps, use_self, hole_tolerant, - bmd->operation); + bmd->operation, + nullptr); } #endif diff --git a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc index e485172d3e1..daeca311e08 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc @@ -20,6 +20,7 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_input(N_("Self Intersection")); b.add_input(N_("Hole Tolerant")); b.add_output(N_("Mesh")); + b.add_output(N_("Intersecting Edges")).field_source(); } static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -27,6 +28,10 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) uiItemR(layout, ptr, "operation", 0, "", ICON_NONE); } +struct AttributeOutputs { + StrongAnonymousAttributeID intersecting_edges_id; +}; + static void node_update(bNodeTree *ntree, bNode *node) { GeometryNodeBooleanOperation operation = (GeometryNodeBooleanOperation)node->custom1; @@ -121,13 +126,21 @@ static void node_geo_exec(GeoNodeExecParams params) } } - Mesh *result = blender::meshintersect::direct_mesh_boolean(meshes, - transforms, - float4x4::identity(), - material_remaps, - use_self, - hole_tolerant, - operation); + AttributeOutputs attribute_outputs; + if (params.output_is_required("Intersecting Edges")) { + attribute_outputs.intersecting_edges_id = StrongAnonymousAttributeID("Intersecting Edges"); + } + + Vector intersecting_edges; + Mesh *result = blender::meshintersect::direct_mesh_boolean( + meshes, + transforms, + float4x4::identity(), + material_remaps, + use_self, + hole_tolerant, + operation, + attribute_outputs.intersecting_edges_id ? &intersecting_edges : nullptr); if (!result) { params.set_default_remaining_outputs(); return; @@ -138,6 +151,26 @@ static void node_geo_exec(GeoNodeExecParams params) result->totcol = materials.size(); MutableSpan(result->mat, result->totcol).copy_from(materials); + /* Store intersecting edges in attribute. */ + if (attribute_outputs.intersecting_edges_id) { + MeshComponent mesh_component; + mesh_component.replace(result, GeometryOwnershipType::Editable); + OutputAttribute_Typed attribute = mesh_component.attribute_try_get_for_output_only( + attribute_outputs.intersecting_edges_id.get(), ATTR_DOMAIN_EDGE); + MutableSpan selection = attribute.as_span(); + selection.fill(false); + for (const int i : intersecting_edges) { + selection[i] = true; + } + + attribute.save(); + + params.set_output( + "Intersecting Edges", + AnonymousAttributeFieldInput::Create( + std::move(attribute_outputs.intersecting_edges_id), params.attribute_producer_name())); + } + params.set_output("Mesh", GeometrySet::create_with_mesh(result)); #else params.error_message_add(NodeWarningType::Error, -- cgit v1.2.3