From b226c115e263afbb1c2ded1e5a73e954690945c2 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 23 Jun 2023 17:07:05 +0200 Subject: [PATCH] Fix #109236: Split Edges node skips loose edges With the previous fix to the node, 8a11f0f3a23ef9b3c11c, the new edge indices are built with the changed corner vertices from a previous step in the algorithm. That doesn't work for loose edges though, since they aren't used by any face corners. The best solution I could come up with was adding a second loop over the split vertices that adjusts the vertex indices of loose edges. This can be skipped when there are none. Pull Request: https://projects.blender.org/blender/blender/pulls/109262 --- .../geometry/intern/mesh_split_edges.cc | 62 +++++++++++++++++-- 1 file changed, 56 insertions(+), 6 deletions(-) diff --git a/source/blender/geometry/intern/mesh_split_edges.cc b/source/blender/geometry/intern/mesh_split_edges.cc index fe27c2df561..77a1df81b7b 100644 --- a/source/blender/geometry/intern/mesh_split_edges.cc +++ b/source/blender/geometry/intern/mesh_split_edges.cc @@ -180,6 +180,33 @@ static void split_vertex_per_fan(const int vertex, } } +/** Assign the newly created vertex duplicates to the loose edges around this vertex. */ +static void reassign_loose_edge_verts(const int vertex, + const int start_offset, + const Span fans, + const Span fan_sizes, + const BoundedBitSpan loose_edges, + MutableSpan edges) +{ + int fan_start = 0; + /* We don't need to create a new vertex for the last fan. That fan can just be connected to the + * original vertex. */ + for (const int i : fan_sizes.index_range().drop_back(1)) { + const int new_vert = start_offset + i; + for (const int edge_i : fans.slice(fan_start, fan_sizes[i])) { + if (loose_edges[edge_i]) { + if (edges[edge_i][0] == vertex) { + edges[edge_i][0] = new_vert; + } + else if (edges[edge_i][1] == vertex) { + edges[edge_i][1] = new_vert; + } + } + } + fan_start += fan_sizes[i]; + } +} + /** * Get the index of the adjacent edge to a loop connected to a vertex. In other words, for the * given polygon return the unique edge connected to the given vertex and not on the given loop. @@ -340,12 +367,14 @@ void split_edges(Mesh &mesh, MutableSpan corner_edges = mesh.corner_edges_for_write(); - /* Step 1: Split the edges. */ + /* Split corner edge indices and update the edge to corner map. This step does not take into + * account future deduplication of the new edges, but is necessary in order to calculate the + * new fans around each vertex. */ mask.foreach_index([&](const int edge_i) { split_edge_per_poly(edge_i, edge_offsets[edge_i], edge_to_loop_map, corner_edges); }); - /* Step 1.5: Update topology information (can't parallelize). */ + /* Update vertex to edge map with new vertices from duplicated edges. */ for (const int edge_i : mask) { const int2 &edge = edges[edge_i]; for (const int duplicate_i : IndexRange(edge_offsets[edge_i], num_edge_duplicates[edge_i])) { @@ -356,7 +385,8 @@ void split_edges(Mesh &mesh, MutableSpan corner_verts = mesh.corner_verts_for_write(); - /* Step 2: Calculate vertex fans. */ + /* Calculate vertex fans by reordering the vertex to edge maps. Fans are the the ordered + * groups of consecutive edges between consecutive faces looping around a vertex. */ Array> vertex_fan_sizes(mesh.totvert); threading::parallel_for(IndexRange(mesh.totvert), 512, [&](IndexRange range) { for (const int vert : range) { @@ -374,7 +404,7 @@ void split_edges(Mesh &mesh, } }); - /* Step 2.5: Calculate offsets for next step. */ + /* Calculate result indices per source vertex as offsets for parallelizing the next step. */ Array vert_offsets(mesh.totvert); int total_verts_num = mesh.totvert; for (const int vert : IndexRange(mesh.totvert)) { @@ -386,7 +416,7 @@ void split_edges(Mesh &mesh, total_verts_num += vertex_fan_sizes[vert].size() - 1; } - /* Step 3: Split the vertices. + /* Split the vertices into their duplicates so that each fan has its own result vertex. * Build a map from each new vertex to an old vertex to use for transferring attributes later. */ const int new_verts_num = total_verts_num - mesh.totvert; Array new_to_old_verts_map(new_verts_num); @@ -406,6 +436,7 @@ void split_edges(Mesh &mesh, } }); + /* Create deduplicated new edges based on the corner vertices at each polygon. */ VectorSet new_edges; new_edges.reserve(new_edges_size + loose_edges.size()); for (const int i : polys.index_range()) { @@ -418,6 +449,7 @@ void split_edges(Mesh &mesh, } loose_edges.foreach_index([&](const int64_t i) { new_edges.add(OrderedEdge(edges[i])); }); + /* Build a map of old to new edges for transferring attributes. */ Array new_to_old_edges_map(new_edges.size()); auto index_mask_to_indices = [&](const IndexMask &mask, MutableSpan indices) { for (const int i : mask.index_range()) { @@ -435,10 +467,28 @@ void split_edges(Mesh &mesh, } } - /* Step 5: Resize the mesh to add the new vertices and rebuild the edges. */ + /* Resize the mesh to add the new vertices and rebuild the edges. */ add_new_vertices(mesh, new_to_old_verts_map); add_new_edges(mesh, new_edges.as_span().cast(), new_to_old_edges_map, propagation_info); + /* Connect loose edges to duplicated vertices. */ + if (loose_edges_cache.count > 0) { + MutableSpan new_edges_span = mesh.edges_for_write(); + threading::parallel_for(should_split_vert.index_range(), 512, [&](IndexRange range) { + for (const int vert : range) { + if (!should_split_vert[vert]) { + continue; + } + reassign_loose_edge_verts(vert, + vert_offsets[vert], + vert_to_edge_map[vert], + vertex_fan_sizes[vert], + loose_edges_cache.is_loose_bits, + new_edges_span); + } + }); + } + BKE_mesh_tag_edges_split(&mesh); }