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
This commit is contained in:
Philipp Oeser 2022-04-04 12:26:50 +02:00
parent 209bf7780e
commit e2975cb701
4 changed files with 65 additions and 11 deletions

View File

@ -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<const Mesh *> meshes,
Span<const float4x4 *> transforms,
@ -30,6 +32,7 @@ Mesh *direct_mesh_boolean(Span<const Mesh *> meshes,
Span<Array<short>> material_remaps,
bool use_self,
bool hole_tolerant,
int boolean_mode);
int boolean_mode,
Vector<int> *r_intersecting_edges);
} // namespace blender::meshintersect

View File

@ -791,7 +791,8 @@ Mesh *direct_mesh_boolean(Span<const Mesh *> meshes,
Span<Array<short>> material_remaps,
const bool use_self,
const bool hole_tolerant,
const int boolean_mode)
const int boolean_mode,
Vector<int> *r_intersecting_edges)
{
#ifdef WITH_GMP
BLI_assert(meshes.size() == transforms.size());
@ -828,7 +829,23 @@ Mesh *direct_mesh_boolean(Span<const Mesh *> 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,

View File

@ -447,7 +447,8 @@ static Mesh *exact_boolean_mesh(BooleanModifierData *bmd,
material_remaps,
use_self,
hole_tolerant,
bmd->operation);
bmd->operation,
nullptr);
}
#endif

View File

@ -20,6 +20,7 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_input<decl::Bool>(N_("Self Intersection"));
b.add_input<decl::Bool>(N_("Hole Tolerant"));
b.add_output<decl::Geometry>(N_("Mesh"));
b.add_output<decl::Bool>(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<int> 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<bool> attribute = mesh_component.attribute_try_get_for_output_only<bool>(
attribute_outputs.intersecting_edges_id.get(), ATTR_DOMAIN_EDGE);
MutableSpan<bool> 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<bool>(
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,