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:
parent
209bf7780e
commit
e2975cb701
@ -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
|
||||
|
@ -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,
|
||||
|
@ -447,7 +447,8 @@ static Mesh *exact_boolean_mesh(BooleanModifierData *bmd,
|
||||
material_remaps,
|
||||
use_self,
|
||||
hole_tolerant,
|
||||
bmd->operation);
|
||||
bmd->operation,
|
||||
nullptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user