Fix #112909 and #112922: Weld modifier crashes in some situations #112967

Merged
Philipp Oeser merged 1 commits from mano-wii/blender:blender-v3.6-release into blender-v3.6-release 2023-10-12 14:38:11 +02:00
1 changed files with 302 additions and 264 deletions

View File

@ -51,11 +51,10 @@ struct WeldLoop {
int flag;
struct {
/* Indices relative to the original Mesh. */
int vert;
int edge;
int vert;
int loop_orig;
/* Switches to this loop when iterating. Except when it is the first loop of the polygon. */
int switch_to;
int loop_next;
};
};
};
@ -69,8 +68,11 @@ struct WeldPoly {
int poly_orig;
int loop_start;
int loop_end;
/* To find groups. */
int loop_ctx_start;
int loop_ctx_len;
#ifdef USE_WELD_DEBUG
/* Final Polygon Size. */
int loop_len;
#endif
};
@ -95,15 +97,15 @@ struct WeldMesh {
/* From the actual index of the element in the mesh, it indicates what is the index of the Weld
* element above. */
Array<int> loop_map;
Array<int> poly_map;
Array<int> face_map;
int vert_kill_len;
int edge_kill_len;
int loop_kill_len;
int poly_kill_len; /* Including the new polygons. */
int face_kill_len; /* Including the new polygons. */
/* Size of the affected polygon with more sides. */
int max_poly_len;
/* Size of the affected face with more sides. */
int max_face_len;
#ifdef USE_WELD_DEBUG
Span<int> corner_verts;
@ -114,15 +116,17 @@ struct WeldMesh {
struct WeldLoopOfPolyIter {
int loop_iter;
int loop_orig_start;
int loop_end;
/* Weld group. */
int loop_ctx_start;
int loop_ctx_len;
int *group;
Span<WeldLoop> wloop;
Span<int> corner_verts;
Span<int> corner_edges;
Span<int> loop_map;
/* Weld group. */
int *group;
/* Return */
int group_len;
@ -157,7 +161,7 @@ static void weld_assert_edge_kill_len(Span<int> edge_dest_map, const int expecte
}
static void weld_assert_poly_and_loop_kill_len(WeldMesh *weld_mesh,
const int expected_poly_kill_len,
const int expected_faces_kill_len,
const int expected_loop_kill_len)
{
const Span<int> corner_verts = weld_mesh->corner_verts;
@ -166,8 +170,8 @@ static void weld_assert_poly_and_loop_kill_len(WeldMesh *weld_mesh,
int poly_kills = 0;
int loop_kills = corner_verts.size();
for (const int i : polys.index_range()) {
int poly_ctx = weld_mesh->poly_map[i];
for (const int i : faces.index_range()) {
int poly_ctx = weld_mesh->face_map[i];
if (poly_ctx != OUT_OF_CONTEXT) {
const WeldPoly *wp = &weld_mesh->wpoly[poly_ctx];
WeldLoopOfPolyIter iter;
@ -208,7 +212,7 @@ static void weld_assert_poly_and_loop_kill_len(WeldMesh *weld_mesh,
}
}
else {
loop_kills -= polys[i].size();
loop_kills -= faces[i].size();
}
}
@ -238,7 +242,7 @@ static void weld_assert_poly_and_loop_kill_len(WeldMesh *weld_mesh,
}
}
BLI_assert(poly_kills == expected_poly_kill_len);
BLI_assert(poly_kills == expected_faces_kill_len);
BLI_assert(loop_kills == expected_loop_kill_len);
}
@ -261,9 +265,9 @@ static void weld_assert_poly_no_vert_repetition(const WeldPoly *wp,
return;
}
else {
while (weld_iter_loop_of_poly_next(iter)) {
do {
verts[i++] = iter.v;
}
} while (weld_iter_loop_of_poly_next(iter));
}
BLI_assert(i == wp->loop_len);
@ -277,30 +281,6 @@ static void weld_assert_poly_no_vert_repetition(const WeldPoly *wp,
}
}
static void weld_assert_poly_len(const WeldPoly *wp,
const Span<WeldLoop> wloop,
Span<int> loop_map)
{
if (wp->flag == ELEM_COLLAPSED) {
return;
}
int len = 0;
for (int l = wp->loop_start; l <= wp->loop_end; l++) {
int loop_ctx = loop_map[l];
if (loop_ctx == OUT_OF_CONTEXT) {
len++;
continue;
}
const WeldLoop *wl = &wloop[loop_ctx];
BLI_assert(wp->loop_start <= wl->loop_orig);
if (wl->flag != ELEM_COLLAPSED) {
len++;
}
}
BLI_assert(wp->loop_len >= len);
}
#endif /* USE_WELD_DEBUG */
/** \} */
@ -507,6 +487,57 @@ static void weld_edge_find_doubles(Span<WeldEdge> weld_edges,
/** \name Poly and Loop API
* \{ */
static bool weld_iter_loop_of_poly_next(WeldLoopOfPolyIter &iter)
{
if (iter.loop_iter > iter.loop_end) {
return false;
}
Span<WeldLoop> wloop = iter.wloop;
Span<int> loop_map = iter.loop_map;
int l = iter.loop_iter;
int l_next = l + 1;
int loop_ctx = loop_map[l];
if (loop_ctx != OUT_OF_CONTEXT) {
const WeldLoop *wl = &wloop[loop_ctx];
#ifdef USE_WELD_DEBUG
BLI_assert(wl->flag != ELEM_COLLAPSED);
BLI_assert(iter.v != wl->vert);
#endif
iter.v = wl->vert;
iter.e = wl->edge;
if (wl->loop_next > l) {
/* Allow the loop to break. */
l_next = wl->loop_next;
}
if (iter.group) {
iter.group_len = 0;
int count = iter.loop_ctx_len;
for (wl = &wloop[iter.loop_ctx_start]; count--; wl++) {
if (wl->vert == iter.v) {
iter.group[iter.group_len++] = wl->loop_orig;
}
}
}
}
else {
#ifdef USE_WELD_DEBUG
BLI_assert(iter.v != iter.corner_verts[l]);
#endif
iter.v = iter.corner_verts[l];
iter.e = iter.corner_edges[l];
if (iter.group) {
iter.group[0] = l;
iter.group_len = 1;
}
}
iter.loop_iter = l_next;
return true;
}
static bool weld_iter_loop_of_poly_begin(WeldLoopOfPolyIter &iter,
const WeldPoly &wp,
Span<WeldLoop> wloop,
@ -520,100 +551,29 @@ static bool weld_iter_loop_of_poly_begin(WeldLoopOfPolyIter &iter,
}
iter.loop_iter = wp.loop_start;
iter.loop_orig_start = wp.loop_start;
iter.loop_end = wp.loop_end;
iter.loop_ctx_start = wp.loop_ctx_start;
iter.loop_ctx_len = wp.loop_ctx_len;
iter.wloop = wloop;
iter.corner_verts = corner_verts;
iter.corner_edges = corner_edges;
iter.loop_map = loop_map;
iter.group = group_buffer;
int group_len = 0;
if (group_buffer) {
/* Include any final loops that are collapsed.
* Would it be better to move this to `weld_iter_loop_of_poly_next`? */
int loop_end_ctx;
int loop_end = wp.loop_end;
while (((loop_end_ctx = loop_map[loop_end]) != OUT_OF_CONTEXT) &&
(wloop[loop_end_ctx].flag == ELEM_COLLAPSED))
{
loop_end--;
group_len++;
}
int i = 0;
while (loop_end < wp.loop_end) {
iter.group[i++] = ++loop_end;
}
}
iter.group_len = group_len;
iter.group_len = 0;
#ifdef USE_WELD_DEBUG
iter.v = OUT_OF_CONTEXT;
#endif
return true;
}
static bool weld_iter_loop_of_poly_next(WeldLoopOfPolyIter &iter)
{
Span<WeldLoop> wloop = iter.wloop;
Span<int> loop_map = iter.loop_map;
int l = iter.loop_iter;
if (l == iter.loop_orig_start) {
/* `grupo_len` is already calculated in the first loop */
}
else {
iter.group_len = 0;
}
while (l <= iter.loop_end) {
if (iter.group) {
iter.group[iter.group_len++] = l;
}
int loop_ctx = loop_map[l];
if (loop_ctx != OUT_OF_CONTEXT) {
const WeldLoop *wl = &wloop[loop_ctx];
if (wl->flag == ELEM_COLLAPSED) {
l++;
continue;
}
else if (wl->switch_to != OUT_OF_CONTEXT) {
if (iter.group) {
iter.group[iter.group_len++] = wl->switch_to;
}
if (l != iter.loop_orig_start) {
l = wl->switch_to;
loop_ctx = loop_map[l];
wl = &wloop[loop_ctx];
}
}
#ifdef USE_WELD_DEBUG
BLI_assert(iter.v != wl->vert);
#endif
iter.v = wl->vert;
iter.e = wl->edge;
}
else {
#ifdef USE_WELD_DEBUG
BLI_assert(iter.v != iter.corner_verts[l]);
#endif
iter.v = iter.corner_verts[l];
iter.e = iter.corner_edges[l];
}
iter.loop_iter = l + 1;
return true;
}
return false;
return weld_iter_loop_of_poly_next(iter);
}
/**
* Alloc Weld Polygons and Weld Loops.
*
* \return r_weld_mesh: Loop and poly members will be allocated here.
* \return r_weld_mesh: Loop and face members will be allocated here.
*/
static void weld_poly_loop_ctx_alloc(const OffsetIndices<int> polys,
static void weld_poly_loop_ctx_alloc(const OffsetIndices<int> faces,
const Span<int> corner_verts,
const Span<int> corner_edges,
WeldMesh *r_weld_mesh)
@ -623,7 +583,7 @@ static void weld_poly_loop_ctx_alloc(const OffsetIndices<int> polys,
/* Loop/Poly Context. */
Array<int> loop_map(corner_verts.size());
Array<int> poly_map(polys.size());
Array<int> face_map(faces.size());
int wloop_len = 0;
int wpoly_len = 0;
int max_ctx_poly_len = 4;
@ -632,31 +592,53 @@ static void weld_poly_loop_ctx_alloc(const OffsetIndices<int> polys,
wloop.reserve(corner_verts.size());
Vector<WeldPoly> wpoly;
wpoly.reserve(polys.size());
wpoly.reserve(faces.size());
int maybe_new_poly = 0;
for (const int i : polys.index_range()) {
const int loopstart = polys[i].start();
const int totloop = polys[i].size();
for (const int i : faces.index_range()) {
const int loopstart = faces[i].start();
const int totloop = faces[i].size();
const int loop_end = loopstart + totloop - 1;
int v_first = corner_verts[loopstart];
int v_dest_first = vert_dest_map[v_first];
bool is_vert_first_ctx = v_dest_first != OUT_OF_CONTEXT;
int v_next = v_first;
int v_dest_next = v_dest_first;
bool is_vert_next_ctx = is_vert_first_ctx;
int prev_wloop_len = wloop_len;
for (const int loop_orig : polys[i]) {
int v = corner_verts[loop_orig];
int e = corner_edges[loop_orig];
int v_dest = vert_dest_map[v];
int e_dest = edge_dest_map[e];
bool is_vert_ctx = v_dest != OUT_OF_CONTEXT;
bool is_edge_ctx = e_dest != OUT_OF_CONTEXT;
if (is_vert_ctx || is_edge_ctx) {
wloop.increase_size_by_unchecked(1);
for (const int loop_orig : faces[i]) {
int v = v_next;
int v_dest = v_dest_next;
bool is_vert_ctx = is_vert_next_ctx;
int loop_next;
if (loop_orig != loop_end) {
loop_next = loop_orig + 1;
v_next = corner_verts[loop_next];
v_dest_next = vert_dest_map[v_next];
is_vert_next_ctx = v_dest_next != OUT_OF_CONTEXT;
}
else {
loop_next = loopstart;
v_next = v_first;
v_dest_next = v_dest_first;
is_vert_next_ctx = is_vert_first_ctx;
}
if (is_vert_ctx || is_vert_next_ctx) {
int e = corner_edges[loop_orig];
int e_dest = edge_dest_map[e];
bool is_edge_ctx = e_dest != OUT_OF_CONTEXT;
wloop.increase_size_by_unchecked(1);
WeldLoop &wl = wloop.last();
wl.vert = is_vert_ctx ? v_dest : v;
wl.edge = is_edge_ctx ? e_dest : e;
wl.loop_orig = loop_orig;
wl.switch_to = OUT_OF_CONTEXT;
wl.loop_next = loop_next;
loop_map[loop_orig] = wloop_len++;
}
@ -664,32 +646,36 @@ static void weld_poly_loop_ctx_alloc(const OffsetIndices<int> polys,
loop_map[loop_orig] = OUT_OF_CONTEXT;
}
}
if (wloop_len != prev_wloop_len) {
int loop_ctx_len = wloop_len - prev_wloop_len;
wpoly.increase_size_by_unchecked(1);
WeldPoly &wp = wpoly.last();
int loops_len = wloop_len - prev_wloop_len;
wp.poly_dst = OUT_OF_CONTEXT;
wp.poly_orig = i;
wp.loop_start = loopstart;
wp.loop_end = loop_end;
wp.loop_ctx_start = prev_wloop_len;
wp.loop_ctx_len = loop_ctx_len;
#ifdef USE_WELD_DEBUG
wp.loop_len = totloop;
#endif
poly_map[i] = wpoly_len++;
if (totloop > 5 && loops_len > 1) {
face_map[i] = wpoly_len++;
if (totloop > 5 && loop_ctx_len > 1) {
/* We could be smarter here and actually count how many new polygons will be created.
* But counting this can be inefficient as it depends on the number of non-consecutive
* self polygon merges. For now just estimate a maximum value. */
int max_new = std::min((totloop / 3), loops_len) - 1;
* self face merges. For now just estimate a maximum value. */
int max_new = std::min((totloop / 3), loop_ctx_len) - 1;
maybe_new_poly += max_new;
CLAMP_MIN(max_ctx_poly_len, totloop);
}
}
else {
poly_map[i] = OUT_OF_CONTEXT;
face_map[i] = OUT_OF_CONTEXT;
}
}
@ -699,12 +685,11 @@ static void weld_poly_loop_ctx_alloc(const OffsetIndices<int> polys,
r_weld_mesh->wpoly = std::move(wpoly);
r_weld_mesh->wpoly_new_len = 0;
r_weld_mesh->loop_map = std::move(loop_map);
r_weld_mesh->poly_map = std::move(poly_map);
r_weld_mesh->max_poly_len = max_ctx_poly_len;
r_weld_mesh->face_map = std::move(face_map);
r_weld_mesh->max_face_len = max_ctx_poly_len;
}
static void weld_poly_split_recursive(int poly_loop_len,
const int start_loop_from,
Span<int> vert_dest_map,
WeldPoly *r_wp,
WeldMesh *r_weld_mesh,
@ -718,56 +703,56 @@ static void weld_poly_split_recursive(int poly_loop_len,
Span<int> loop_map = r_weld_mesh->loop_map;
MutableSpan<WeldLoop> wloop = r_weld_mesh->wloop;
int loop_end_ctx;
int loop_end = r_wp->loop_end;
while (((loop_end_ctx = loop_map[loop_end]) == OUT_OF_CONTEXT) ||
(wloop[loop_end_ctx].flag == ELEM_COLLAPSED))
{
loop_end--;
}
int loop_kill = 0;
WeldLoop *wla_prev = &wloop[loop_end_ctx];
int loop_ctx_a, loop_ctx_a_prev = loop_map[loop_end];
for (int la = start_loop_from; la <= loop_end; loop_ctx_a_prev = loop_ctx_a, la++) {
loop_ctx_a = loop_map[la];
int loop_end = r_wp->loop_end;
int loop_ctx_a = loop_map[loop_end];
WeldLoop *wla_prev = (loop_ctx_a != OUT_OF_CONTEXT) ? &wloop[loop_ctx_a] : nullptr;
int la = r_wp->loop_start;
do {
int loop_ctx_a = loop_map[la];
if (loop_ctx_a == OUT_OF_CONTEXT) {
la++;
wla_prev = nullptr;
continue;
}
WeldLoop *wla = &wloop[loop_ctx_a];
if (wla->flag == ELEM_COLLAPSED) {
continue;
}
BLI_assert(wla->flag != ELEM_COLLAPSED);
int vert_a = wla->vert;
if (vert_dest_map[vert_a] == OUT_OF_CONTEXT) {
/* Only test vertices that will be merged. */
la = wla->loop_next;
wla_prev = wla;
continue;
}
wa_continue:
int dist_a = 1;
int lb_prev = la;
WeldLoop *wlb_prev = wla;
int killed_ab = 0;
int loop_ctx_b, loop_ctx_b_prev = loop_ctx_a_prev;
for (int lb = la + 1; lb <= loop_end; loop_ctx_b_prev = loop_ctx_b, lb++) {
loop_ctx_b = loop_map[lb];
int lb = wla->loop_next;
do {
int loop_ctx_b = loop_map[lb];
if (loop_ctx_b == OUT_OF_CONTEXT) {
dist_a++;
lb_prev = lb;
wlb_prev = nullptr;
lb++;
continue;
}
WeldLoop *wlb = &wloop[loop_ctx_b];
if (wlb->flag == ELEM_COLLAPSED) {
killed_ab++;
continue;
}
BLI_assert(wlb->flag != ELEM_COLLAPSED);
int vert_b = wlb->vert;
if (vert_a != vert_b) {
dist_a++;
lb_prev = lb;
wlb_prev = wlb;
lb = wlb->loop_next;
continue;
}
int dist_a = wlb->loop_orig - wla->loop_orig - killed_ab;
int dist_b = poly_loop_len - dist_a;
BLI_assert(dist_a != 0 && dist_b != 0);
@ -793,36 +778,35 @@ static void weld_poly_split_recursive(int poly_loop_len,
*r_poly_kill += 1;
*r_loop_kill += loop_kill;
/* Since all the loops are collapsed, avoid looping through them.
* This may result in wrong poly_kill counts. */
* This may result in wrong poly_kill counts. */
return;
}
else {
wla_prev->loop_next = lb;
wlb_prev->loop_next = la;
if (r_wp->loop_start == la) {
r_wp->loop_start = lb;
}
if (dist_a == 2) {
WeldLoop *wlb_prev = &wloop[loop_ctx_b_prev];
BLI_assert(wlb_prev->flag != ELEM_COLLAPSED);
wla->flag = ELEM_COLLAPSED;
wlb_prev->flag = ELEM_COLLAPSED;
loop_kill += 2;
dist_a = dist_b;
}
else if (dist_b == 2) {
WeldLoop *wla_prev = &wloop[loop_ctx_a_prev];
BLI_assert(wla_prev->flag != ELEM_COLLAPSED);
wlb->flag = ELEM_COLLAPSED;
wla_prev->flag = ELEM_COLLAPSED;
loop_kill += 2;
dist_b = dist_a;
lb = la;
r_wp->loop_start = la;
r_wp->loop_end = loop_end = lb_prev;
poly_loop_len = dist_a;
break;
}
else {
if (r_wp->loop_start == la) {
r_wp->loop_start = lb;
/* Specify a value for the `switch_to` in order to form groups and a more pretty merge.
* But it's not really necessary for Weld to work. */
wlb->switch_to = wla->switch_to != OUT_OF_CONTEXT ? wla->switch_to : la;
}
wla->switch_to = lb;
r_weld_mesh->wpoly.increase_size_by_unchecked(1);
r_weld_mesh->wpoly_new_len++;
@ -830,22 +814,36 @@ static void weld_poly_split_recursive(int poly_loop_len,
new_test->poly_dst = OUT_OF_CONTEXT;
new_test->poly_orig = r_wp->poly_orig;
new_test->loop_start = la;
new_test->loop_end = lb - 1;
new_test->loop_end = lb_prev;
new_test->loop_ctx_start = r_wp->loop_ctx_start;
new_test->loop_ctx_len = r_wp->loop_ctx_len;
#ifdef USE_WELD_DEBUG
new_test->loop_len = dist_a;
#endif
weld_poly_split_recursive(
dist_a, la, vert_dest_map, new_test, r_weld_mesh, r_poly_kill, r_loop_kill);
dist_a, vert_dest_map, new_test, r_weld_mesh, r_poly_kill, r_loop_kill);
}
la = lb;
wla = wlb;
poly_loop_len = dist_b;
goto wa_continue;
dist_a = 1;
}
wlb_prev = wlb;
lb_prev = lb;
lb = wlb->loop_next;
} while (lb_prev != loop_end);
wla_prev = wla;
if (la == loop_end) {
/* No need to start again. */
break;
}
}
la = wla->loop_next;
} while (la != loop_end);
*r_loop_kill += loop_kill;
#ifdef USE_WELD_DEBUG
@ -862,13 +860,13 @@ static void weld_poly_split_recursive(int poly_loop_len,
* \param r_vlinks: An uninitialized buffer used to compute groups of WeldPolys attached to each
* weld target vertex. It doesn't need to be passed as a parameter but this is
* done to reduce allocations.
* \return r_weld_mesh: Loop and poly members will be configured here.
* \return r_weld_mesh: Loop and face members will be configured here.
*/
static void weld_poly_loop_ctx_setup_collapsed_and_split(const int remain_edge_ctx_len,
WeldMesh *r_weld_mesh)
{
if (remain_edge_ctx_len == 0) {
r_weld_mesh->poly_kill_len = r_weld_mesh->wpoly.size();
r_weld_mesh->face_kill_len = r_weld_mesh->wpoly.size();
r_weld_mesh->loop_kill_len = r_weld_mesh->wloop.size();
for (WeldPoly &wp : r_weld_mesh->wpoly) {
@ -883,7 +881,7 @@ static void weld_poly_loop_ctx_setup_collapsed_and_split(const int remain_edge_c
Span<int> loop_map = r_weld_mesh->loop_map;
Span<int> vert_dest_map = r_weld_mesh->vert_dest_map;
int poly_kill_len = 0;
int face_kill_len = 0;
int loop_kill_len = 0;
/* Setup Poly/Loop. */
@ -893,9 +891,13 @@ static void weld_poly_loop_ctx_setup_collapsed_and_split(const int remain_edge_c
for (const int i : wpoly_original_range) {
WeldPoly &wp = wpoly[i];
int poly_loop_len = (wp.loop_end - wp.loop_start) + 1;
for (int l = wp.loop_start; l <= wp.loop_end; l++) {
WeldLoop *wl_prev = nullptr;
bool chang_loop_start = false;
int l = wp.loop_start;
do {
int loop_ctx = loop_map[l];
if (loop_ctx == OUT_OF_CONTEXT) {
wl_prev = nullptr;
continue;
}
@ -905,33 +907,68 @@ static void weld_poly_loop_ctx_setup_collapsed_and_split(const int remain_edge_c
wl->flag = ELEM_COLLAPSED;
if (poly_loop_len == 3) {
wp.flag = ELEM_COLLAPSED;
poly_kill_len++;
face_kill_len++;
loop_kill_len += 3;
poly_loop_len = 0;
break;
}
if (l == wp.loop_start) {
chang_loop_start = true;
}
loop_kill_len++;
poly_loop_len--;
}
}
else {
if (chang_loop_start) {
wp.loop_start = l;
chang_loop_start = false;
}
if (wl_prev) {
wl_prev->loop_next = l;
}
wl_prev = wl;
BLI_assert(wl->loop_next == l + 1 || l == wp.loop_end);
}
} while (l++ != wp.loop_end);
if (poly_loop_len) {
if (wl_prev) {
wl_prev->loop_next = wp.loop_start;
wp.loop_end = wl_prev->loop_orig;
}
#ifdef USE_WELD_DEBUG
wp.loop_len = poly_loop_len;
weld_assert_poly_len(&wp, wloop, loop_map);
for (int loop_orig : IndexRange(wp.loop_start, poly_loop_len)) {
int loop_ctx = loop_map[loop_orig];
if (loop_ctx == OUT_OF_CONTEXT) {
continue;
}
WeldLoop *wl = &wloop[loop_ctx];
if (wl->flag == ELEM_COLLAPSED) {
continue;
}
loop_ctx = loop_map[wl->loop_next];
if (loop_ctx == OUT_OF_CONTEXT) {
continue;
}
wl = &wloop[loop_ctx];
BLI_assert(wl->flag != ELEM_COLLAPSED);
}
#endif
weld_poly_split_recursive(poly_loop_len,
wp.loop_start,
vert_dest_map,
&wp,
r_weld_mesh,
&poly_kill_len,
&loop_kill_len);
weld_poly_split_recursive(
poly_loop_len, vert_dest_map, &wp, r_weld_mesh, &face_kill_len, &loop_kill_len);
}
}
r_weld_mesh->poly_kill_len = poly_kill_len;
r_weld_mesh->face_kill_len = face_kill_len;
r_weld_mesh->loop_kill_len = loop_kill_len;
#ifdef USE_WELD_DEBUG
@ -982,37 +1019,37 @@ static int poly_find_doubles(const OffsetIndices<int> poly_corners_offsets,
};
/* Add +1 to allow calculation of the length of the last group. */
Array<int> linked_polys_offset(corner_index_max + 1, 0);
Array<int> linked_faces_offset(corner_index_max + 1, 0);
for (const int elem_index : corners) {
linked_polys_offset[elem_index]++;
linked_faces_offset[elem_index]++;
}
int link_polys_buffer_len = 0;
int link_faces_buffer_len = 0;
for (const int elem_index : IndexRange(corner_index_max)) {
link_polys_buffer_len += linked_polys_offset[elem_index];
linked_polys_offset[elem_index] = link_polys_buffer_len;
link_faces_buffer_len += linked_faces_offset[elem_index];
linked_faces_offset[elem_index] = link_faces_buffer_len;
}
linked_polys_offset[corner_index_max] = link_polys_buffer_len;
linked_faces_offset[corner_index_max] = link_faces_buffer_len;
if (link_polys_buffer_len == 0) {
if (link_faces_buffer_len == 0) {
return 0;
}
Array<int> linked_polys_buffer(link_polys_buffer_len);
Array<int> linked_faces_buffer(link_faces_buffer_len);
/* Use a reverse for loop to ensure that indexes are assigned in ascending order. */
for (int poly_index = poly_num; poly_index--;) {
if (poly_corners_offsets[poly_index].size() == 0) {
for (int face_index = poly_num; face_index--;) {
if (poly_corners_offsets[face_index].size() == 0) {
continue;
}
for (int corner_index = poly_corners_offsets[poly_index].last();
corner_index >= poly_corners_offsets[poly_index].first();
for (int corner_index = poly_corners_offsets[face_index].last();
corner_index >= poly_corners_offsets[face_index].first();
corner_index--)
{
const int elem_index = corners[corner_index];
linked_polys_buffer[--linked_polys_offset[elem_index]] = poly_index;
linked_faces_buffer[--linked_faces_offset[elem_index]] = face_index;
}
}
@ -1026,56 +1063,56 @@ static int poly_find_doubles(const OffsetIndices<int> poly_corners_offsets,
int doubles_buffer_num = 0;
int doubles_num = 0;
for (const int poly_index : IndexRange(poly_num)) {
if (is_double[poly_index]) {
for (const int face_index : IndexRange(poly_num)) {
if (is_double[face_index]) {
continue;
}
int corner_num = poly_corners_offsets[poly_index].size();
int corner_num = poly_corners_offsets[face_index].size();
if (corner_num == 0) {
continue;
}
/* Set or overwrite the first slot of the possible group. */
doubles_buffer[doubles_buffer_num] = poly_index;
doubles_buffer[doubles_buffer_num] = face_index;
int corner_first = poly_corners_offsets[poly_index].first();
int corner_first = poly_corners_offsets[face_index].first();
int elem_index = corners[corner_first];
int link_offs = linked_polys_offset[elem_index];
int polys_a_num = linked_polys_offset[elem_index + 1] - link_offs;
if (polys_a_num == 1) {
BLI_assert(linked_polys_buffer[linked_polys_offset[elem_index]] == poly_index);
int link_offs = linked_faces_offset[elem_index];
int faces_a_num = linked_faces_offset[elem_index + 1] - link_offs;
if (faces_a_num == 1) {
BLI_assert(linked_faces_buffer[linked_faces_offset[elem_index]] == face_index);
continue;
}
const int *polys_a = &linked_polys_buffer[link_offs];
const int *faces_a = &linked_faces_buffer[link_offs];
int poly_to_test;
/* Skip polygons with lower index as these have already been checked. */
do {
poly_to_test = *polys_a;
polys_a++;
polys_a_num--;
} while (poly_to_test != poly_index);
poly_to_test = *faces_a;
faces_a++;
faces_a_num--;
} while (poly_to_test != face_index);
int *isect_result = doubles_buffer.data() + doubles_buffer_num + 1;
/* `polys_a` are the polygons connected to the first corner. So skip the first corner. */
/* `faces_a` are the polygons connected to the first corner. So skip the first corner. */
for (int corner_index : IndexRange(corner_first + 1, corner_num - 1)) {
elem_index = corners[corner_index];
link_offs = linked_polys_offset[elem_index];
int polys_b_num = linked_polys_offset[elem_index + 1] - link_offs;
const int *polys_b = &linked_polys_buffer[link_offs];
link_offs = linked_faces_offset[elem_index];
int faces_b_num = linked_faces_offset[elem_index + 1] - link_offs;
const int *faces_b = &linked_faces_buffer[link_offs];
/* Skip polygons with lower index as these have already been checked. */
do {
poly_to_test = *polys_b;
polys_b++;
polys_b_num--;
} while (poly_to_test != poly_index);
poly_to_test = *faces_b;
faces_b++;
faces_b_num--;
} while (poly_to_test != face_index);
doubles_num = intersect(Span<int>{polys_a, polys_a_num},
Span<int>{polys_b, polys_b_num},
doubles_num = intersect(Span<int>{faces_a, faces_a_num},
Span<int>{faces_b, faces_b_num},
is_double,
isect_result);
@ -1084,20 +1121,20 @@ static int poly_find_doubles(const OffsetIndices<int> poly_corners_offsets,
}
/* Intersect the last result. */
polys_a = isect_result;
polys_a_num = doubles_num;
faces_a = isect_result;
faces_a_num = doubles_num;
}
if (doubles_num) {
for (const int poly_double : Span<int>{isect_result, doubles_num}) {
BLI_assert(poly_double > poly_index);
BLI_assert(poly_double > face_index);
is_double[poly_double].set();
}
doubles_buffer_num += doubles_num;
doubles_offsets.append(++doubles_buffer_num);
if ((doubles_buffer_num + 1) == poly_num) {
/* The last slot is the remaining unduplicated polygon.
/* The last slot is the remaining unduplicated face.
* Avoid checking intersection as there are no more slots left. */
break;
}
@ -1114,7 +1151,7 @@ static void weld_poly_find_doubles(const Span<int> corner_verts,
const int medge_len,
WeldMesh *r_weld_mesh)
{
if (r_weld_mesh->poly_kill_len == r_weld_mesh->wpoly.size()) {
if (r_weld_mesh->face_kill_len == r_weld_mesh->wpoly.size()) {
return;
}
@ -1141,9 +1178,9 @@ static void weld_poly_find_doubles(const Span<int> corner_verts,
continue;
}
while (weld_iter_loop_of_poly_next(iter)) {
do {
new_corner_edges.append(iter.e);
}
} while (weld_iter_loop_of_poly_next(iter));
}
poly_offs_[face_len] = new_corner_edges.size();
@ -1171,7 +1208,7 @@ static void weld_poly_find_doubles(const Span<int> corner_verts,
}
}
r_weld_mesh->poly_kill_len += doubles_num;
r_weld_mesh->face_kill_len += doubles_num;
r_weld_mesh->loop_kill_len += loop_kill_num;
}
@ -1508,7 +1545,7 @@ static Mesh *create_merged_mesh(const Mesh &mesh,
SCOPED_TIMER(__func__);
#endif
const OffsetIndices src_polys = mesh.polys();
const OffsetIndices src_faces = mesh.polys();
const Span<int> src_corner_verts = mesh.corner_verts();
const Span<int> src_corner_edges = mesh.corner_edges();
const int totvert = mesh.totvert;
@ -1520,12 +1557,12 @@ static Mesh *create_merged_mesh(const Mesh &mesh,
const int result_nverts = totvert - weld_mesh.vert_kill_len;
const int result_nedges = totedge - weld_mesh.edge_kill_len;
const int result_nloops = src_corner_verts.size() - weld_mesh.loop_kill_len;
const int result_npolys = src_polys.size() - weld_mesh.poly_kill_len + weld_mesh.wpoly_new_len;
const int result_nfaces = src_faces.size() - weld_mesh.face_kill_len + weld_mesh.wpoly_new_len;
Mesh *result = BKE_mesh_new_nomain_from_template(
&mesh, result_nverts, result_nedges, result_npolys, result_nloops);
&mesh, result_nverts, result_nedges, result_nfaces, result_nloops);
MutableSpan<int2> dst_edges = result->edges_for_write();
MutableSpan<int> dst_poly_offsets = result->poly_offsets_for_write();
MutableSpan<int> dst_face_offsets = result->poly_offsets_for_write();
MutableSpan<int> dst_corner_verts = result->corner_verts_for_write();
MutableSpan<int> dst_corner_edges = result->corner_edges_for_write();
@ -1561,18 +1598,18 @@ static Mesh *create_merged_mesh(const Mesh &mesh,
BLI_assert(IN_RANGE_INCL(edge[1], 0, result_nverts - 1));
}
/* Polys/Loops. */
/* Faces/Loops. */
int r_i = 0;
int loop_cur = 0;
Array<int, 64> group_buffer(weld_mesh.max_poly_len);
for (const int i : src_polys.index_range()) {
Array<int, 64> group_buffer(weld_mesh.max_face_len);
for (const int i : src_faces.index_range()) {
const int loop_start = loop_cur;
const int poly_ctx = weld_mesh.poly_map[i];
const int poly_ctx = weld_mesh.face_map[i];
if (poly_ctx == OUT_OF_CONTEXT) {
int mp_loop_len = src_polys[i].size();
int mp_loop_len = src_faces[i].size();
CustomData_copy_data(
&mesh.ldata, &result->ldata, src_polys[i].start(), loop_cur, src_polys[i].size());
&mesh.ldata, &result->ldata, src_faces[i].start(), loop_cur, src_faces[i].size());
for (; mp_loop_len--; loop_cur++) {
dst_corner_verts[loop_cur] = vert_final_map[dst_corner_verts[loop_cur]];
dst_corner_edges[loop_cur] = edge_final_map[dst_corner_edges[loop_cur]];
@ -1595,17 +1632,17 @@ static Mesh *create_merged_mesh(const Mesh &mesh,
if (wp.poly_dst != OUT_OF_CONTEXT) {
continue;
}
while (weld_iter_loop_of_poly_next(iter)) {
do {
customdata_weld(
&mesh.ldata, &result->ldata, group_buffer.data(), iter.group_len, loop_cur);
dst_corner_verts[loop_cur] = vert_final_map[iter.v];
dst_corner_edges[loop_cur] = edge_final_map[iter.e];
loop_cur++;
}
} while (weld_iter_loop_of_poly_next(iter));
}
CustomData_copy_data(&mesh.pdata, &result->pdata, i, r_i, 1);
dst_poly_offsets[r_i] = loop_start;
dst_face_offsets[r_i] = loop_start;
r_i++;
}
@ -1628,18 +1665,19 @@ static Mesh *create_merged_mesh(const Mesh &mesh,
if (wp.poly_dst != OUT_OF_CONTEXT) {
continue;
}
while (weld_iter_loop_of_poly_next(iter)) {
customdata_weld(&mesh.ldata, &result->ldata, group_buffer.data(), iter.group_len, loop_cur);
do {
customdata_weld(
&mesh.ldata, &result->ldata, group_buffer.data(), iter.group_len, loop_cur);
dst_corner_verts[loop_cur] = vert_final_map[iter.v];
dst_corner_edges[loop_cur] = edge_final_map[iter.e];
loop_cur++;
}
} while (weld_iter_loop_of_poly_next(iter));
dst_poly_offsets[r_i] = loop_start;
dst_face_offsets[r_i] = loop_start;
r_i++;
}
BLI_assert(int(r_i) == result_npolys);
BLI_assert(int(r_i) == result_nfaces);
BLI_assert(loop_cur == result_nloops);
return result;