/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "UI_interface.h" #include "UI_resources.h" #include "BLI_array.hh" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "BKE_customdata.h" #include "BKE_mesh.h" #include "BKE_pointcloud.h" #include "BKE_spline.hh" #include "node_geometry_util.hh" namespace blender::nodes::node_geo_delete_geometry_cc { using blender::bke::CustomDataAttributes; template static void copy_data_based_on_mask(Span data, MutableSpan r_data, IndexMask mask) { for (const int i_out : mask.index_range()) { r_data[i_out] = data[mask[i_out]]; } } template static void copy_data_based_on_map(Span src, MutableSpan dst, Span index_map) { for (const int i_src : index_map.index_range()) { const int i_dst = index_map[i_src]; if (i_dst != -1) { dst[i_dst] = src[i_src]; } } } /** Utility function for making an IndexMask from a boolean selection. The indices vector should * live at least as long as the returned IndexMask. */ static IndexMask index_mask_indices(Span mask, const bool invert, Vector &indices) { for (const int i : mask.index_range()) { if (mask[i] != invert) { indices.append(i); } } return IndexMask(indices); } /** * Copies the attributes with a domain in `domains` to `result_component`. */ static void copy_attributes(const Map &attributes, const GeometryComponent &in_component, GeometryComponent &result_component, const Span domains) { for (Map::Item entry : attributes.items()) { const AttributeIDRef attribute_id = entry.key; ReadAttributeLookup attribute = in_component.attribute_try_get_for_read(attribute_id); if (!attribute) { continue; } /* Only copy if it is on a domain we want. */ if (!domains.contains(attribute.domain)) { continue; } const CustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray.type()); OutputAttribute result_attribute = result_component.attribute_try_get_for_output_only( attribute_id, attribute.domain, data_type); if (!result_attribute) { continue; } attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); VArray_Span span{attribute.varray.typed()}; MutableSpan out_span = result_attribute.as_span(); out_span.copy_from(span); }); result_attribute.save(); } } /** * For each attribute with a domain in `domains` it copies the parts of that attribute which lie in * the mask to `result_component`. */ static void copy_attributes_based_on_mask(const Map &attributes, const GeometryComponent &in_component, GeometryComponent &result_component, const AttributeDomain domain, const IndexMask mask) { for (Map::Item entry : attributes.items()) { const AttributeIDRef attribute_id = entry.key; ReadAttributeLookup attribute = in_component.attribute_try_get_for_read(attribute_id); if (!attribute) { continue; } /* Only copy if it is on a domain we want. */ if (domain != attribute.domain) { continue; } const CustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray.type()); OutputAttribute result_attribute = result_component.attribute_try_get_for_output_only( attribute_id, attribute.domain, data_type); if (!result_attribute) { continue; } attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); VArray_Span span{attribute.varray.typed()}; MutableSpan out_span = result_attribute.as_span(); copy_data_based_on_mask(span, out_span, mask); }); result_attribute.save(); } } static void copy_attributes_based_on_map(const Map &attributes, const GeometryComponent &in_component, GeometryComponent &result_component, const AttributeDomain domain, const Span index_map) { for (Map::Item entry : attributes.items()) { const AttributeIDRef attribute_id = entry.key; ReadAttributeLookup attribute = in_component.attribute_try_get_for_read(attribute_id); if (!attribute) { continue; } /* Only copy if it is on a domain we want. */ if (domain != attribute.domain) { continue; } const CustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray.type()); OutputAttribute result_attribute = result_component.attribute_try_get_for_output_only( attribute_id, attribute.domain, data_type); if (!result_attribute) { continue; } attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); VArray_Span span{attribute.varray.typed()}; MutableSpan out_span = result_attribute.as_span(); copy_data_based_on_map(span, out_span, index_map); }); result_attribute.save(); } } static void copy_face_corner_attributes(const Map &attributes, const GeometryComponent &in_component, GeometryComponent &out_component, const int num_selected_loops, const Span selected_poly_indices, const Mesh &mesh_in) { Vector indices; indices.reserve(num_selected_loops); for (const int src_poly_index : selected_poly_indices) { const MPoly &src_poly = mesh_in.mpoly[src_poly_index]; const int src_loop_start = src_poly.loopstart; const int tot_loop = src_poly.totloop; for (const int i : IndexRange(tot_loop)) { indices.append_unchecked(src_loop_start + i); } } copy_attributes_based_on_mask( attributes, in_component, out_component, ATTR_DOMAIN_CORNER, IndexMask(indices)); } static void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh, Mesh &dst_mesh, Span vertex_map) { BLI_assert(src_mesh.totvert == vertex_map.size()); for (const int i_src : vertex_map.index_range()) { const int i_dst = vertex_map[i_src]; if (i_dst == -1) { continue; } const MVert &v_src = src_mesh.mvert[i_src]; MVert &v_dst = dst_mesh.mvert[i_dst]; v_dst = v_src; } } static void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, Mesh &dst_mesh, Span edge_map) { BLI_assert(src_mesh.totedge == edge_map.size()); for (const int i_src : IndexRange(src_mesh.totedge)) { const int i_dst = edge_map[i_src]; if (ELEM(i_dst, -1, -2)) { continue; } const MEdge &e_src = src_mesh.medge[i_src]; MEdge &e_dst = dst_mesh.medge[i_dst]; e_dst = e_src; e_dst.v1 = e_src.v1; e_dst.v2 = e_src.v2; } } static void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, Mesh &dst_mesh, Span vertex_map, Span edge_map) { BLI_assert(src_mesh.totvert == vertex_map.size()); BLI_assert(src_mesh.totedge == edge_map.size()); for (const int i_src : IndexRange(src_mesh.totedge)) { const int i_dst = edge_map[i_src]; if (i_dst == -1) { continue; } const MEdge &e_src = src_mesh.medge[i_src]; MEdge &e_dst = dst_mesh.medge[i_dst]; e_dst = e_src; e_dst.v1 = vertex_map[e_src.v1]; e_dst.v2 = vertex_map[e_src.v2]; } } /* Faces and edges changed but vertices are the same. */ static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, Mesh &dst_mesh, Span edge_map, Span masked_poly_indices, Span new_loop_starts) { for (const int i_dst : masked_poly_indices.index_range()) { const int i_src = masked_poly_indices[i_dst]; const MPoly &mp_src = src_mesh.mpoly[i_src]; MPoly &mp_dst = dst_mesh.mpoly[i_dst]; const int i_ml_src = mp_src.loopstart; const int i_ml_dst = new_loop_starts[i_dst]; const MLoop *ml_src = src_mesh.mloop + i_ml_src; MLoop *ml_dst = dst_mesh.mloop + i_ml_dst; mp_dst = mp_src; mp_dst.loopstart = i_ml_dst; for (int i : IndexRange(mp_src.totloop)) { ml_dst[i].v = ml_src[i].v; ml_dst[i].e = edge_map[ml_src[i].e]; } } } /* Only faces changed. */ static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, Mesh &dst_mesh, Span masked_poly_indices, Span new_loop_starts) { for (const int i_dst : masked_poly_indices.index_range()) { const int i_src = masked_poly_indices[i_dst]; const MPoly &mp_src = src_mesh.mpoly[i_src]; MPoly &mp_dst = dst_mesh.mpoly[i_dst]; const int i_ml_src = mp_src.loopstart; const int i_ml_dst = new_loop_starts[i_dst]; const MLoop *ml_src = src_mesh.mloop + i_ml_src; MLoop *ml_dst = dst_mesh.mloop + i_ml_dst; mp_dst = mp_src; mp_dst.loopstart = i_ml_dst; for (int i : IndexRange(mp_src.totloop)) { ml_dst[i].v = ml_src[i].v; ml_dst[i].e = ml_src[i].e; } } } static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, Mesh &dst_mesh, Span vertex_map, Span edge_map, Span masked_poly_indices, Span new_loop_starts) { for (const int i_dst : masked_poly_indices.index_range()) { const int i_src = masked_poly_indices[i_dst]; const MPoly &mp_src = src_mesh.mpoly[i_src]; MPoly &mp_dst = dst_mesh.mpoly[i_dst]; const int i_ml_src = mp_src.loopstart; const int i_ml_dst = new_loop_starts[i_dst]; const MLoop *ml_src = src_mesh.mloop + i_ml_src; MLoop *ml_dst = dst_mesh.mloop + i_ml_dst; mp_dst = mp_src; mp_dst.loopstart = i_ml_dst; for (int i : IndexRange(mp_src.totloop)) { ml_dst[i].v = vertex_map[ml_src[i].v]; ml_dst[i].e = edge_map[ml_src[i].e]; } } } static void spline_copy_builtin_attributes(const Spline &spline, Spline &r_spline, const IndexMask mask) { copy_data_based_on_mask(spline.positions(), r_spline.positions(), mask); copy_data_based_on_mask(spline.radii(), r_spline.radii(), mask); copy_data_based_on_mask(spline.tilts(), r_spline.tilts(), mask); switch (spline.type()) { case Spline::Type::Poly: break; case Spline::Type::Bezier: { const BezierSpline &src = static_cast(spline); BezierSpline &dst = static_cast(r_spline); copy_data_based_on_mask(src.handle_positions_left(), dst.handle_positions_left(), mask); copy_data_based_on_mask(src.handle_positions_right(), dst.handle_positions_right(), mask); copy_data_based_on_mask(src.handle_types_left(), dst.handle_types_left(), mask); copy_data_based_on_mask(src.handle_types_right(), dst.handle_types_right(), mask); break; } case Spline::Type::NURBS: { const NURBSpline &src = static_cast(spline); NURBSpline &dst = static_cast(r_spline); copy_data_based_on_mask(src.weights(), dst.weights(), mask); break; } } } static void copy_dynamic_attributes(const CustomDataAttributes &src, CustomDataAttributes &dst, const IndexMask mask) { src.foreach_attribute( [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { std::optional src_attribute = src.get_for_read(attribute_id); BLI_assert(src_attribute); if (!dst.create(attribute_id, meta_data.data_type)) { /* Since the source spline of the same type had the attribute, adding it should work. */ BLI_assert_unreachable(); } std::optional new_attribute = dst.get_for_write(attribute_id); BLI_assert(new_attribute); attribute_math::convert_to_static_type(new_attribute->type(), [&](auto dummy) { using T = decltype(dummy); copy_data_based_on_mask(src_attribute->typed(), new_attribute->typed(), mask); }); return true; }, ATTR_DOMAIN_POINT); } /** * Deletes points in the spline. Those not in the mask are deleted. The spline is not split into * multiple newer splines. */ static SplinePtr spline_delete(const Spline &spline, const IndexMask mask) { SplinePtr new_spline = spline.copy_only_settings(); new_spline->resize(mask.size()); spline_copy_builtin_attributes(spline, *new_spline, mask); copy_dynamic_attributes(spline.attributes, new_spline->attributes, mask); return new_spline; } static std::unique_ptr curve_separate(const CurveEval &input_curve, const Span selection, const AttributeDomain selection_domain, const bool invert) { Span input_splines = input_curve.splines(); std::unique_ptr output_curve = std::make_unique(); /* Keep track of which splines were copied to the result to copy spline domain attributes. */ Vector copied_splines; if (selection_domain == ATTR_DOMAIN_CURVE) { /* Operates on each of the splines as a whole, i.e. not on the points in the splines * themselves. */ for (const int i : selection.index_range()) { if (selection[i] != invert) { output_curve->add_spline(input_splines[i]->copy()); copied_splines.append(i); } } } else { /* Operates on the points in the splines themselves. */ /* Reuse index vector for each spline. */ Vector indices_to_copy; int selection_index = 0; for (const int i : input_splines.index_range()) { const Spline &spline = *input_splines[i]; indices_to_copy.clear(); for (const int i_point : IndexRange(spline.size())) { if (selection[selection_index] != invert) { /* Append i_point instead of selection_index because we need indices local to the spline * for copying. */ indices_to_copy.append(i_point); } selection_index++; } /* Avoid creating an empty spline. */ if (indices_to_copy.is_empty()) { continue; } SplinePtr new_spline = spline_delete(spline, IndexMask(indices_to_copy)); output_curve->add_spline(std::move(new_spline)); copied_splines.append(i); } } if (copied_splines.is_empty()) { return {}; } output_curve->attributes.reallocate(output_curve->splines().size()); copy_dynamic_attributes( input_curve.attributes, output_curve->attributes, IndexMask(copied_splines)); return output_curve; } static void separate_curve_selection(GeometrySet &geometry_set, const Field &selection_field, const AttributeDomain selection_domain, const bool invert) { const CurveComponent &src_component = *geometry_set.get_component_for_read(); GeometryComponentFieldContext field_context{src_component, selection_domain}; fn::FieldEvaluator selection_evaluator{field_context, src_component.attribute_domain_size(selection_domain)}; selection_evaluator.add(selection_field); selection_evaluator.evaluate(); const VArray_Span &selection = selection_evaluator.get_evaluated(0); std::unique_ptr r_curve = curve_separate( *src_component.get_for_read(), selection, selection_domain, invert); if (r_curve) { geometry_set.replace_curve(r_curve.release()); } else { geometry_set.replace_curve(nullptr); } } static void separate_point_cloud_selection(GeometrySet &geometry_set, const Field &selection_field, const bool invert) { const PointCloudComponent &src_points = *geometry_set.get_component_for_read(); GeometryComponentFieldContext field_context{src_points, ATTR_DOMAIN_POINT}; fn::FieldEvaluator selection_evaluator{field_context, src_points.attribute_domain_size(ATTR_DOMAIN_POINT)}; selection_evaluator.add(selection_field); selection_evaluator.evaluate(); const VArray_Span &selection = selection_evaluator.get_evaluated(0); Vector indices; const IndexMask mask = index_mask_indices(selection, invert, indices); const int total = mask.size(); PointCloud *pointcloud = BKE_pointcloud_new_nomain(total); if (total == 0) { geometry_set.replace_pointcloud(pointcloud); return; } PointCloudComponent dst_points; dst_points.replace(pointcloud, GeometryOwnershipType::Editable); Map attributes; geometry_set.gather_attributes_for_propagation( {GEO_COMPONENT_TYPE_POINT_CLOUD}, GEO_COMPONENT_TYPE_POINT_CLOUD, false, attributes); copy_attributes_based_on_mask(attributes, src_points, dst_points, ATTR_DOMAIN_POINT, mask); geometry_set.replace_pointcloud(pointcloud); } static void separate_instance_selection(GeometrySet &geometry_set, const Field &selection_field, const bool invert) { InstancesComponent &instances = geometry_set.get_component_for_write(); GeometryComponentFieldContext field_context{instances, ATTR_DOMAIN_INSTANCE}; const int domain_size = instances.attribute_domain_size(ATTR_DOMAIN_INSTANCE); fn::FieldEvaluator evaluator{field_context, domain_size}; evaluator.add(selection_field); evaluator.evaluate(); const VArray_Span &selection = evaluator.get_evaluated(0); Vector indices; const IndexMask mask = index_mask_indices(selection, invert, indices); if (mask.is_empty()) { geometry_set.remove(); return; } instances.remove_instances(mask); } static void compute_selected_vertices_from_vertex_selection(const Span vertex_selection, const bool invert, MutableSpan r_vertex_map, int *r_num_selected_vertices) { BLI_assert(vertex_selection.size() == r_vertex_map.size()); int num_selected_vertices = 0; for (const int i : r_vertex_map.index_range()) { if (vertex_selection[i] != invert) { r_vertex_map[i] = num_selected_vertices; num_selected_vertices++; } else { r_vertex_map[i] = -1; } } *r_num_selected_vertices = num_selected_vertices; } static void compute_selected_edges_from_vertex_selection(const Mesh &mesh, const Span vertex_selection, const bool invert, MutableSpan r_edge_map, int *r_num_selected_edges) { BLI_assert(mesh.totedge == r_edge_map.size()); int num_selected_edges = 0; for (const int i : IndexRange(mesh.totedge)) { const MEdge &edge = mesh.medge[i]; /* Only add the edge if both vertices will be in the new mesh. */ if (vertex_selection[edge.v1] != invert && vertex_selection[edge.v2] != invert) { r_edge_map[i] = num_selected_edges; num_selected_edges++; } else { r_edge_map[i] = -1; } } *r_num_selected_edges = num_selected_edges; } static void compute_selected_polygons_from_vertex_selection(const Mesh &mesh, const Span vertex_selection, const bool invert, Vector &r_selected_poly_indices, Vector &r_loop_starts, int *r_num_selected_polys, int *r_num_selected_loops) { BLI_assert(mesh.totvert == vertex_selection.size()); r_selected_poly_indices.reserve(mesh.totpoly); r_loop_starts.reserve(mesh.totloop); int num_selected_loops = 0; for (const int i : IndexRange(mesh.totpoly)) { const MPoly &poly_src = mesh.mpoly[i]; bool all_verts_in_selection = true; Span loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop); for (const MLoop &loop : loops_src) { if (vertex_selection[loop.v] == invert) { all_verts_in_selection = false; break; } } if (all_verts_in_selection) { r_selected_poly_indices.append_unchecked(i); r_loop_starts.append_unchecked(num_selected_loops); num_selected_loops += poly_src.totloop; } } *r_num_selected_polys = r_selected_poly_indices.size(); *r_num_selected_loops = num_selected_loops; } /** * Checks for every edge if it is in `edge_selection`. If it is, then the two vertices of the * edge are kept along with the edge. */ static void compute_selected_vertices_and_edges_from_edge_selection( const Mesh &mesh, const Span edge_selection, const bool invert, MutableSpan r_vertex_map, MutableSpan r_edge_map, int *r_num_selected_vertices, int *r_num_selected_edges) { BLI_assert(mesh.totedge == edge_selection.size()); int num_selected_edges = 0; int num_selected_vertices = 0; for (const int i : IndexRange(mesh.totedge)) { const MEdge &edge = mesh.medge[i]; if (edge_selection[i] != invert) { r_edge_map[i] = num_selected_edges; num_selected_edges++; if (r_vertex_map[edge.v1] == -1) { r_vertex_map[edge.v1] = num_selected_vertices; num_selected_vertices++; } if (r_vertex_map[edge.v2] == -1) { r_vertex_map[edge.v2] = num_selected_vertices; num_selected_vertices++; } } else { r_edge_map[i] = -1; } } *r_num_selected_vertices = num_selected_vertices; *r_num_selected_edges = num_selected_edges; } /** * Checks for every edge if it is in `edge_selection`. */ static void compute_selected_edges_from_edge_selection(const Mesh &mesh, const Span edge_selection, const bool invert, MutableSpan r_edge_map, int *r_num_selected_edges) { BLI_assert(mesh.totedge == edge_selection.size()); int num_selected_edges = 0; for (const int i : IndexRange(mesh.totedge)) { if (edge_selection[i] != invert) { r_edge_map[i] = num_selected_edges; num_selected_edges++; } else { r_edge_map[i] = -1; } } *r_num_selected_edges = num_selected_edges; } /** * Checks for every polygon if all the edges are in `edge_selection`. If they are, then that * polygon is kept. */ static void compute_selected_polygons_from_edge_selection(const Mesh &mesh, const Span edge_selection, const bool invert, Vector &r_selected_poly_indices, Vector &r_loop_starts, int *r_num_selected_polys, int *r_num_selected_loops) { r_selected_poly_indices.reserve(mesh.totpoly); r_loop_starts.reserve(mesh.totloop); int num_selected_loops = 0; for (const int i : IndexRange(mesh.totpoly)) { const MPoly &poly_src = mesh.mpoly[i]; bool all_edges_in_selection = true; Span loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop); for (const MLoop &loop : loops_src) { if (edge_selection[loop.e] == invert) { all_edges_in_selection = false; break; } } if (all_edges_in_selection) { r_selected_poly_indices.append_unchecked(i); r_loop_starts.append_unchecked(num_selected_loops); num_selected_loops += poly_src.totloop; } } *r_num_selected_polys = r_selected_poly_indices.size(); *r_num_selected_loops = num_selected_loops; } /** * Checks for every edge and polygon if all its vertices are in `vertex_selection`. */ static void compute_selected_mesh_data_from_vertex_selection_edge_face( const Mesh &mesh, const Span vertex_selection, const bool invert, MutableSpan r_edge_map, Vector &r_selected_poly_indices, Vector &r_loop_starts, int *r_num_selected_edges, int *r_num_selected_polys, int *r_num_selected_loops) { compute_selected_edges_from_vertex_selection( mesh, vertex_selection, invert, r_edge_map, r_num_selected_edges); compute_selected_polygons_from_vertex_selection(mesh, vertex_selection, invert, r_selected_poly_indices, r_loop_starts, r_num_selected_polys, r_num_selected_loops); } /** * Checks for every vertex if it is in `vertex_selection`. The polygons and edges are kept if all * vertices of that polygon or edge are in the selection. */ static void compute_selected_mesh_data_from_vertex_selection(const Mesh &mesh, const Span vertex_selection, const bool invert, MutableSpan r_vertex_map, MutableSpan r_edge_map, Vector &r_selected_poly_indices, Vector &r_loop_starts, int *r_num_selected_vertices, int *r_num_selected_edges, int *r_num_selected_polys, int *r_num_selected_loops) { compute_selected_vertices_from_vertex_selection( vertex_selection, invert, r_vertex_map, r_num_selected_vertices); compute_selected_edges_from_vertex_selection( mesh, vertex_selection, invert, r_edge_map, r_num_selected_edges); compute_selected_polygons_from_vertex_selection(mesh, vertex_selection, invert, r_selected_poly_indices, r_loop_starts, r_num_selected_polys, r_num_selected_loops); } /** * Checks for every edge if it is in `edge_selection`. The polygons are kept if all edges are in * the selection. */ static void compute_selected_mesh_data_from_edge_selection_edge_face( const Mesh &mesh, const Span edge_selection, const bool invert, MutableSpan r_edge_map, Vector &r_selected_poly_indices, Vector &r_loop_starts, int *r_num_selected_edges, int *r_num_selected_polys, int *r_num_selected_loops) { compute_selected_edges_from_edge_selection( mesh, edge_selection, invert, r_edge_map, r_num_selected_edges); compute_selected_polygons_from_edge_selection(mesh, edge_selection, invert, r_selected_poly_indices, r_loop_starts, r_num_selected_polys, r_num_selected_loops); } /** * Checks for every edge if it is in `edge_selection`. If it is, the vertices belonging to * that edge are kept as well. The polygons are kept if all edges are in the selection. */ static void compute_selected_mesh_data_from_edge_selection(const Mesh &mesh, const Span edge_selection, const bool invert, MutableSpan r_vertex_map, MutableSpan r_edge_map, Vector &r_selected_poly_indices, Vector &r_loop_starts, int *r_num_selected_vertices, int *r_num_selected_edges, int *r_num_selected_polys, int *r_num_selected_loops) { r_vertex_map.fill(-1); compute_selected_vertices_and_edges_from_edge_selection(mesh, edge_selection, invert, r_vertex_map, r_edge_map, r_num_selected_vertices, r_num_selected_edges); compute_selected_polygons_from_edge_selection(mesh, edge_selection, invert, r_selected_poly_indices, r_loop_starts, r_num_selected_polys, r_num_selected_loops); } /** * Checks for every polygon if it is in `poly_selection`. */ static void compute_selected_polygons_from_poly_selection(const Mesh &mesh, const Span poly_selection, const bool invert, Vector &r_selected_poly_indices, Vector &r_loop_starts, int *r_num_selected_polys, int *r_num_selected_loops) { BLI_assert(mesh.totpoly == poly_selection.size()); r_selected_poly_indices.reserve(mesh.totpoly); r_loop_starts.reserve(mesh.totloop); int num_selected_loops = 0; for (const int i : IndexRange(mesh.totpoly)) { const MPoly &poly_src = mesh.mpoly[i]; /* We keep this one. */ if (poly_selection[i] != invert) { r_selected_poly_indices.append_unchecked(i); r_loop_starts.append_unchecked(num_selected_loops); num_selected_loops += poly_src.totloop; } } *r_num_selected_polys = r_selected_poly_indices.size(); *r_num_selected_loops = num_selected_loops; } /** * Checks for every polygon if it is in `poly_selection`. If it is, the edges * belonging to that polygon are kept as well. */ static void compute_selected_mesh_data_from_poly_selection_edge_face( const Mesh &mesh, const Span poly_selection, const bool invert, MutableSpan r_edge_map, Vector &r_selected_poly_indices, Vector &r_loop_starts, int *r_num_selected_edges, int *r_num_selected_polys, int *r_num_selected_loops) { BLI_assert(mesh.totpoly == poly_selection.size()); BLI_assert(mesh.totedge == r_edge_map.size()); r_edge_map.fill(-1); r_selected_poly_indices.reserve(mesh.totpoly); r_loop_starts.reserve(mesh.totloop); int num_selected_loops = 0; int num_selected_edges = 0; for (const int i : IndexRange(mesh.totpoly)) { const MPoly &poly_src = mesh.mpoly[i]; /* We keep this one. */ if (poly_selection[i] != invert) { r_selected_poly_indices.append_unchecked(i); r_loop_starts.append_unchecked(num_selected_loops); num_selected_loops += poly_src.totloop; /* Add the vertices and the edges. */ Span loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop); for (const MLoop &loop : loops_src) { /* Check first if it has not yet been added. */ if (r_edge_map[loop.e] == -1) { r_edge_map[loop.e] = num_selected_edges; num_selected_edges++; } } } } *r_num_selected_edges = num_selected_edges; *r_num_selected_polys = r_selected_poly_indices.size(); *r_num_selected_loops = num_selected_loops; } /** * Checks for every polygon if it is in `poly_selection`. If it is, the edges and vertices * belonging to that polygon are kept as well. */ static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh, const Span poly_selection, const bool invert, MutableSpan r_vertex_map, MutableSpan r_edge_map, Vector &r_selected_poly_indices, Vector &r_loop_starts, int *r_num_selected_vertices, int *r_num_selected_edges, int *r_num_selected_polys, int *r_num_selected_loops) { BLI_assert(mesh.totpoly == poly_selection.size()); BLI_assert(mesh.totedge == r_edge_map.size()); r_vertex_map.fill(-1); r_edge_map.fill(-1); r_selected_poly_indices.reserve(mesh.totpoly); r_loop_starts.reserve(mesh.totloop); int num_selected_loops = 0; int num_selected_vertices = 0; int num_selected_edges = 0; for (const int i : IndexRange(mesh.totpoly)) { const MPoly &poly_src = mesh.mpoly[i]; /* We keep this one. */ if (poly_selection[i] != invert) { r_selected_poly_indices.append_unchecked(i); r_loop_starts.append_unchecked(num_selected_loops); num_selected_loops += poly_src.totloop; /* Add the vertices and the edges. */ Span loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop); for (const MLoop &loop : loops_src) { /* Check first if it has not yet been added. */ if (r_vertex_map[loop.v] == -1) { r_vertex_map[loop.v] = num_selected_vertices; num_selected_vertices++; } if (r_edge_map[loop.e] == -1) { r_edge_map[loop.e] = num_selected_edges; num_selected_edges++; } } } } *r_num_selected_vertices = num_selected_vertices; *r_num_selected_edges = num_selected_edges; *r_num_selected_polys = r_selected_poly_indices.size(); *r_num_selected_loops = num_selected_loops; } /** * Keep the parts of the mesh that are in the selection. */ static void do_mesh_separation(GeometrySet &geometry_set, const MeshComponent &in_component, const VArray_Span &selection, const bool invert, const AttributeDomain domain, const GeometryNodeDeleteGeometryMode mode) { /* Needed in all cases. */ Vector selected_poly_indices; Vector new_loop_starts; int num_selected_polys = 0; int num_selected_loops = 0; const Mesh &mesh_in = *in_component.get_for_read(); Mesh *mesh_out; MeshComponent out_component; Map attributes; geometry_set.gather_attributes_for_propagation( {GEO_COMPONENT_TYPE_MESH}, GEO_COMPONENT_TYPE_MESH, false, attributes); switch (mode) { case GEO_NODE_DELETE_GEOMETRY_MODE_ALL: { Array vertex_map(mesh_in.totvert); int num_selected_vertices = 0; Array edge_map(mesh_in.totedge); int num_selected_edges = 0; /* Fill all the maps based on the selection. */ switch (domain) { case ATTR_DOMAIN_POINT: compute_selected_mesh_data_from_vertex_selection(mesh_in, selection, invert, vertex_map, edge_map, selected_poly_indices, new_loop_starts, &num_selected_vertices, &num_selected_edges, &num_selected_polys, &num_selected_loops); break; case ATTR_DOMAIN_EDGE: compute_selected_mesh_data_from_edge_selection(mesh_in, selection, invert, vertex_map, edge_map, selected_poly_indices, new_loop_starts, &num_selected_vertices, &num_selected_edges, &num_selected_polys, &num_selected_loops); break; case ATTR_DOMAIN_FACE: compute_selected_mesh_data_from_poly_selection(mesh_in, selection, invert, vertex_map, edge_map, selected_poly_indices, new_loop_starts, &num_selected_vertices, &num_selected_edges, &num_selected_polys, &num_selected_loops); break; default: BLI_assert_unreachable(); break; } mesh_out = BKE_mesh_new_nomain_from_template(&mesh_in, num_selected_vertices, num_selected_edges, 0, num_selected_loops, num_selected_polys); out_component.replace(mesh_out, GeometryOwnershipType::Editable); /* Copy the selected parts of the mesh over to the new mesh. */ copy_masked_vertices_to_new_mesh(mesh_in, *mesh_out, vertex_map); copy_masked_edges_to_new_mesh(mesh_in, *mesh_out, vertex_map, edge_map); copy_masked_polys_to_new_mesh( mesh_in, *mesh_out, vertex_map, edge_map, selected_poly_indices, new_loop_starts); /* Copy attributes. */ copy_attributes_based_on_map( attributes, in_component, out_component, ATTR_DOMAIN_POINT, vertex_map); copy_attributes_based_on_map( attributes, in_component, out_component, ATTR_DOMAIN_EDGE, edge_map); copy_attributes_based_on_mask(attributes, in_component, out_component, ATTR_DOMAIN_FACE, IndexMask(Vector(selected_poly_indices.as_span()))); copy_face_corner_attributes(attributes, in_component, out_component, num_selected_loops, selected_poly_indices, mesh_in); break; } case GEO_NODE_DELETE_GEOMETRY_MODE_EDGE_FACE: { Array edge_map(mesh_in.totedge); int num_selected_edges = 0; /* Fill all the maps based on the selection. */ switch (domain) { case ATTR_DOMAIN_POINT: compute_selected_mesh_data_from_vertex_selection_edge_face(mesh_in, selection, invert, edge_map, selected_poly_indices, new_loop_starts, &num_selected_edges, &num_selected_polys, &num_selected_loops); break; case ATTR_DOMAIN_EDGE: compute_selected_mesh_data_from_edge_selection_edge_face(mesh_in, selection, invert, edge_map, selected_poly_indices, new_loop_starts, &num_selected_edges, &num_selected_polys, &num_selected_loops); break; case ATTR_DOMAIN_FACE: compute_selected_mesh_data_from_poly_selection_edge_face(mesh_in, selection, invert, edge_map, selected_poly_indices, new_loop_starts, &num_selected_edges, &num_selected_polys, &num_selected_loops); break; default: BLI_assert_unreachable(); break; } mesh_out = BKE_mesh_new_nomain_from_template(&mesh_in, mesh_in.totvert, num_selected_edges, 0, num_selected_loops, num_selected_polys); out_component.replace(mesh_out, GeometryOwnershipType::Editable); /* Copy the selected parts of the mesh over to the new mesh. */ memcpy(mesh_out->mvert, mesh_in.mvert, mesh_in.totvert * sizeof(MVert)); copy_masked_edges_to_new_mesh(mesh_in, *mesh_out, edge_map); copy_masked_polys_to_new_mesh( mesh_in, *mesh_out, edge_map, selected_poly_indices, new_loop_starts); /* Copy attributes. */ copy_attributes(attributes, in_component, out_component, {ATTR_DOMAIN_POINT}); copy_attributes_based_on_map( attributes, in_component, out_component, ATTR_DOMAIN_EDGE, edge_map); copy_attributes_based_on_mask(attributes, in_component, out_component, ATTR_DOMAIN_FACE, IndexMask(Vector(selected_poly_indices.as_span()))); copy_face_corner_attributes(attributes, in_component, out_component, num_selected_loops, selected_poly_indices, mesh_in); break; } case GEO_NODE_DELETE_GEOMETRY_MODE_ONLY_FACE: { /* Fill all the maps based on the selection. */ switch (domain) { case ATTR_DOMAIN_POINT: compute_selected_polygons_from_vertex_selection(mesh_in, selection, invert, selected_poly_indices, new_loop_starts, &num_selected_polys, &num_selected_loops); break; case ATTR_DOMAIN_EDGE: compute_selected_polygons_from_edge_selection(mesh_in, selection, invert, selected_poly_indices, new_loop_starts, &num_selected_polys, &num_selected_loops); break; case ATTR_DOMAIN_FACE: compute_selected_polygons_from_poly_selection(mesh_in, selection, invert, selected_poly_indices, new_loop_starts, &num_selected_polys, &num_selected_loops); break; default: BLI_assert_unreachable(); break; } mesh_out = BKE_mesh_new_nomain_from_template( &mesh_in, mesh_in.totvert, mesh_in.totedge, 0, num_selected_loops, num_selected_polys); out_component.replace(mesh_out, GeometryOwnershipType::Editable); /* Copy the selected parts of the mesh over to the new mesh. */ memcpy(mesh_out->mvert, mesh_in.mvert, mesh_in.totvert * sizeof(MVert)); memcpy(mesh_out->medge, mesh_in.medge, mesh_in.totedge * sizeof(MEdge)); copy_masked_polys_to_new_mesh(mesh_in, *mesh_out, selected_poly_indices, new_loop_starts); /* Copy attributes. */ copy_attributes( attributes, in_component, out_component, {ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE}); copy_attributes_based_on_mask(attributes, in_component, out_component, ATTR_DOMAIN_FACE, IndexMask(Vector(selected_poly_indices.as_span()))); copy_face_corner_attributes(attributes, in_component, out_component, num_selected_loops, selected_poly_indices, mesh_in); break; } } BKE_mesh_calc_edges_loose(mesh_out); /* Tag to recalculate normals later. */ BKE_mesh_normals_tag_dirty(mesh_out); geometry_set.replace_mesh(mesh_out); } static void separate_mesh_selection(GeometrySet &geometry_set, const Field &selection_field, const AttributeDomain selection_domain, const GeometryNodeDeleteGeometryMode mode, const bool invert) { const MeshComponent &src_component = *geometry_set.get_component_for_read(); GeometryComponentFieldContext field_context{src_component, selection_domain}; fn::FieldEvaluator selection_evaluator{field_context, src_component.attribute_domain_size(selection_domain)}; selection_evaluator.add(selection_field); selection_evaluator.evaluate(); const VArray_Span &selection = selection_evaluator.get_evaluated(0); /* Check if there is anything to delete. */ bool delete_nothing = true; for (const int i : selection.index_range()) { if (selection[i] == invert) { delete_nothing = false; break; } } if (delete_nothing) { return; } do_mesh_separation(geometry_set, src_component, selection, invert, selection_domain, mode); } } // namespace blender::nodes::node_geo_delete_geometry_cc namespace blender::nodes { void separate_geometry(GeometrySet &geometry_set, const AttributeDomain domain, const GeometryNodeDeleteGeometryMode mode, const Field &selection_field, const bool invert, bool &r_is_error) { namespace file_ns = blender::nodes::node_geo_delete_geometry_cc; bool some_valid_domain = false; if (geometry_set.has_pointcloud()) { if (domain == ATTR_DOMAIN_POINT) { file_ns::separate_point_cloud_selection(geometry_set, selection_field, invert); some_valid_domain = true; } } if (geometry_set.has_mesh()) { if (ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE, ATTR_DOMAIN_FACE, ATTR_DOMAIN_CORNER)) { file_ns::separate_mesh_selection(geometry_set, selection_field, domain, mode, invert); some_valid_domain = true; } } if (geometry_set.has_curve()) { if (ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) { file_ns::separate_curve_selection(geometry_set, selection_field, domain, invert); some_valid_domain = true; } } if (geometry_set.has_instances()) { if (domain == ATTR_DOMAIN_INSTANCE) { file_ns::separate_instance_selection(geometry_set, selection_field, invert); some_valid_domain = true; } } r_is_error = !some_valid_domain && geometry_set.has_realized_data(); } } // namespace blender::nodes namespace blender::nodes::node_geo_delete_geometry_cc { NODE_STORAGE_FUNCS(NodeGeometryDeleteGeometry) static void node_declare(NodeDeclarationBuilder &b) { b.add_input(N_("Geometry")); b.add_input(N_("Selection")) .default_value(true) .hide_value() .supports_field() .description(N_("The parts of the geometry to be deleted")); b.add_output(N_("Geometry")); } static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { const bNode *node = static_cast(ptr->data); const NodeGeometryDeleteGeometry &storage = node_storage(*node); const AttributeDomain domain = static_cast(storage.domain); uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); /* Only show the mode when it is relevant. */ if (ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE, ATTR_DOMAIN_FACE)) { uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); } } static void node_init(bNodeTree *UNUSED(tree), bNode *node) { NodeGeometryDeleteGeometry *data = MEM_cnew(__func__); data->domain = ATTR_DOMAIN_POINT; data->mode = GEO_NODE_DELETE_GEOMETRY_MODE_ALL; node->storage = data; } static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input("Geometry"); const Field selection_field = params.extract_input>("Selection"); const NodeGeometryDeleteGeometry &storage = node_storage(params.node()); const AttributeDomain domain = static_cast(storage.domain); const GeometryNodeDeleteGeometryMode mode = (GeometryNodeDeleteGeometryMode)storage.mode; if (domain == ATTR_DOMAIN_INSTANCE) { bool is_error; separate_geometry(geometry_set, domain, mode, selection_field, true, is_error); } else { geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { bool is_error; /* Invert here because we want to keep the things not in the selection. */ separate_geometry(geometry_set, domain, mode, selection_field, true, is_error); }); } params.set_output("Geometry", std::move(geometry_set)); } } // namespace blender::nodes::node_geo_delete_geometry_cc void register_node_type_geo_delete_geometry() { namespace file_ns = blender::nodes::node_geo_delete_geometry_cc; static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_DELETE_GEOMETRY, "Delete Geometry", NODE_CLASS_GEOMETRY); node_type_storage(&ntype, "NodeGeometryDeleteGeometry", node_free_standard_storage, node_copy_standard_storage); node_type_init(&ntype, file_ns::node_init); ntype.declare = file_ns::node_declare; ntype.geometry_node_execute = file_ns::node_geo_exec; ntype.draw_buttons = file_ns::node_layout; nodeRegisterType(&ntype); }