Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/modifiers/intern/MOD_weld.cc')
-rw-r--r--source/blender/modifiers/intern/MOD_weld.cc1966
1 files changed, 1966 insertions, 0 deletions
diff --git a/source/blender/modifiers/intern/MOD_weld.cc b/source/blender/modifiers/intern/MOD_weld.cc
new file mode 100644
index 00000000000..92207e1fbe6
--- /dev/null
+++ b/source/blender/modifiers/intern/MOD_weld.cc
@@ -0,0 +1,1966 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2005 by the Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup modifiers
+ *
+ * Weld modifier: Remove doubles.
+ */
+
+/* TODOs:
+ * - Review weight and vertex color interpolation.;
+ */
+
+//#define USE_WELD_DEBUG
+//#define USE_WELD_NORMALS
+//#define USE_BVHTREEKDOP
+
+#include <algorithm>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_utildefines.h"
+
+#include "BLI_array.hh"
+#include "BLI_bitmap.h"
+#include "BLI_index_range.hh"
+#include "BLI_kdtree.h"
+#include "BLI_math.h"
+#include "BLI_span.hh"
+#include "BLI_vector.hh"
+
+#include "BLT_translation.h"
+
+#include "DNA_defaults.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_types.h"
+#include "DNA_screen_types.h"
+
+#ifdef USE_BVHTREEKDOP
+# include "BKE_bvhutils.h"
+#endif
+
+#include "BKE_context.h"
+#include "BKE_deform.h"
+#include "BKE_mesh.h"
+#include "BKE_modifier.h"
+#include "BKE_screen.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+
+#include "DEG_depsgraph.h"
+
+#include "MOD_modifiertypes.h"
+#include "MOD_ui_common.h"
+
+using blender::Array;
+using blender::IndexRange;
+using blender::MutableSpan;
+using blender::Span;
+using blender::Vector;
+
+/* Indicates when the element was not computed. */
+#define OUT_OF_CONTEXT (int)(-1)
+/* Indicates if the edge or face will be collapsed. */
+#define ELEM_COLLAPSED (int)(-2)
+/* indicates whether an edge or vertex in groups_map will be merged. */
+#define ELEM_MERGED (int)(-2)
+
+/* Used to indicate a range in an array specifying a group. */
+struct WeldGroup {
+ int len;
+ int ofs;
+};
+
+/* Edge groups that will be merged. Final vertices are also indicated. */
+struct WeldGroupEdge {
+ struct WeldGroup group;
+ int v1;
+ int v2;
+};
+
+struct WeldVert {
+ /* Indexes relative to the original Mesh. */
+ int vert_dest;
+ int vert_orig;
+};
+
+struct WeldEdge {
+ union {
+ int flag;
+ struct {
+ /* Indexes relative to the original Mesh. */
+ int edge_dest;
+ int edge_orig;
+ int vert_a;
+ int vert_b;
+ };
+ };
+};
+
+struct WeldLoop {
+ union {
+ int flag;
+ struct {
+ /* Indexes relative to the original Mesh. */
+ int vert;
+ int edge;
+ int loop_orig;
+ int loop_skip_to;
+ };
+ };
+};
+
+struct WeldPoly {
+ union {
+ int flag;
+ struct {
+ /* Indexes relative to the original Mesh. */
+ int poly_dst;
+ int poly_orig;
+ int loop_start;
+ int loop_end;
+ /* Final Polygon Size. */
+ int len;
+ /* Group of loops that will be affected. */
+ struct WeldGroup loops;
+ };
+ };
+};
+
+struct WeldMesh {
+ /* Group of vertices to be merged. */
+ Array<WeldGroup> vert_groups;
+ Array<int> vert_groups_buffer;
+
+ /* Group of edges to be merged. */
+ Array<WeldGroupEdge> edge_groups;
+ Array<int> edge_groups_buffer;
+ /* From the original index of the vertex, this indicates which group it is or is going to be
+ * merged. */
+ Array<int> edge_groups_map;
+
+ /* References all polygons and loops that will be affected. */
+ Vector<WeldLoop> wloop;
+ Vector<WeldPoly> wpoly;
+ WeldPoly *wpoly_new;
+ int wloop_len;
+ int wpoly_len;
+ int wpoly_new_len;
+
+ /* 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;
+
+ int vert_kill_len;
+ int edge_kill_len;
+ int loop_kill_len;
+ int poly_kill_len; /* Including the new polygons. */
+
+ /* Size of the affected polygon with more sides. */
+ int max_poly_len;
+};
+
+struct WeldLoopOfPolyIter {
+ int loop_start;
+ int loop_end;
+ Span<WeldLoop> wloop;
+ Span<MLoop> mloop;
+ Span<int> loop_map;
+ /* Weld group. */
+ int *group;
+
+ int l_curr;
+ int l_next;
+
+ /* Return */
+ int group_len;
+ int v;
+ int e;
+ char type;
+};
+
+/* -------------------------------------------------------------------- */
+/** \name Debug Utils
+ * \{ */
+
+#ifdef USE_WELD_DEBUG
+static bool weld_iter_loop_of_poly_begin(WeldLoopOfPolyIter *iter,
+ const WeldPoly &wp,
+ Span<WeldLoop> wloop,
+ Span<MLoop> mloop,
+ Span<int> loop_map,
+ int *group_buffer);
+
+static bool weld_iter_loop_of_poly_next(WeldLoopOfPolyIter *iter);
+
+static void weld_assert_edge_kill_len(Span<WeldEdge> wedge, const int supposed_kill_len)
+{
+ int kills = 0;
+ const WeldEdge *we = &wedge[0];
+ for (int i = wedge.size(); i--; we++) {
+ int edge_dest = we->edge_dest;
+ /* Magically includes collapsed edges. */
+ if (edge_dest != OUT_OF_CONTEXT) {
+ kills++;
+ }
+ }
+ BLI_assert(kills == supposed_kill_len);
+}
+
+static void weld_assert_poly_and_loop_kill_len(Span<WeldPoly> wpoly,
+ Span<WeldPoly> wpoly_new,
+ Span<WeldLoop> wloop,
+ Span<MLoop> mloop,
+ Span<int> loop_map,
+ Span<int> poly_map,
+ Span<MPoly> mpoly,
+ const int supposed_poly_kill_len,
+ const int supposed_loop_kill_len)
+{
+ int poly_kills = 0;
+ int loop_kills = mloop.size();
+ const MPoly *mp = &mpoly[0];
+ for (int i = 0; i < mpoly.size(); i++, mp++) {
+ int poly_ctx = poly_map[i];
+ if (poly_ctx != OUT_OF_CONTEXT) {
+ const WeldPoly *wp = &wpoly[poly_ctx];
+ WeldLoopOfPolyIter iter;
+ if (!weld_iter_loop_of_poly_begin(&iter, *wp, wloop, mloop, loop_map, nullptr)) {
+ poly_kills++;
+ continue;
+ }
+ else {
+ if (wp->poly_dst != OUT_OF_CONTEXT) {
+ poly_kills++;
+ continue;
+ }
+ int remain = wp->len;
+ int l = wp->loop_start;
+ while (remain) {
+ int l_next = l + 1;
+ int loop_ctx = loop_map[l];
+ if (loop_ctx != OUT_OF_CONTEXT) {
+ const WeldLoop *wl = &wloop[loop_ctx];
+ if (wl->loop_skip_to != OUT_OF_CONTEXT) {
+ l_next = wl->loop_skip_to;
+ }
+ if (wl->flag != ELEM_COLLAPSED) {
+ loop_kills--;
+ remain--;
+ }
+ }
+ else {
+ loop_kills--;
+ remain--;
+ }
+ l = l_next;
+ }
+ }
+ }
+ else {
+ loop_kills -= mp->totloop;
+ }
+ }
+
+ const WeldPoly *wp = wpoly_new.data();
+ for (int i = wpoly_new.size(); i--; wp++) {
+ if (wp->poly_dst != OUT_OF_CONTEXT) {
+ poly_kills++;
+ continue;
+ }
+ int remain = wp->len;
+ int l = wp->loop_start;
+ while (remain) {
+ int l_next = l + 1;
+ int loop_ctx = loop_map[l];
+ if (loop_ctx != OUT_OF_CONTEXT) {
+ const WeldLoop *wl = &wloop[loop_ctx];
+ if (wl->loop_skip_to != OUT_OF_CONTEXT) {
+ l_next = wl->loop_skip_to;
+ }
+ if (wl->flag != ELEM_COLLAPSED) {
+ loop_kills--;
+ remain--;
+ }
+ }
+ else {
+ loop_kills--;
+ remain--;
+ }
+ l = l_next;
+ }
+ }
+
+ BLI_assert(poly_kills == supposed_poly_kill_len);
+ BLI_assert(loop_kills == supposed_loop_kill_len);
+}
+
+static void weld_assert_poly_no_vert_repetition(const WeldPoly &wp,
+ Span<WeldLoop> wloop,
+ Span<MLoop> mloop,
+ Span<int> loop_map)
+{
+ const int len = wp.len;
+ Array<int, 64> verts(len);
+ WeldLoopOfPolyIter iter;
+ if (!weld_iter_loop_of_poly_begin(&iter, wp, wloop, mloop, loop_map, nullptr)) {
+ return;
+ }
+ else {
+ int i = 0;
+ while (weld_iter_loop_of_poly_next(&iter)) {
+ verts[i++] = iter.v;
+ }
+ }
+ for (int i = 0; i < len; i++) {
+ int va = verts[i];
+ for (int j = i + 1; j < len; j++) {
+ int vb = verts[j];
+ BLI_assert(va != vb);
+ }
+ }
+}
+
+static void weld_assert_poly_len(const WeldPoly *wp, const Span<WeldLoop> wloop)
+{
+ if (wp->flag == ELEM_COLLAPSED) {
+ return;
+ }
+
+ int len = wp->len;
+ const WeldLoop *wl = &wloop[wp->loops.ofs];
+ BLI_assert(wp->loop_start <= wl->loop_orig);
+
+ int end_wloop = wp->loops.ofs + wp->loops.len;
+ const WeldLoop *wl_end = &wloop[end_wloop - 1];
+
+ int min_len = 0;
+ for (; wl <= wl_end; wl++) {
+ BLI_assert(wl->loop_skip_to == OUT_OF_CONTEXT); /* Not for this case. */
+ if (wl->flag != ELEM_COLLAPSED) {
+ min_len++;
+ }
+ }
+ BLI_assert(len >= min_len);
+
+ int max_len = wp->loop_end - wp->loop_start + 1;
+ BLI_assert(len <= max_len);
+}
+
+#endif /* USE_WELD_DEBUG */
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Weld Vert API
+ * \{ */
+
+static Vector<WeldVert> weld_vert_ctx_alloc_and_setup(Span<int> vert_dest_map,
+ const int vert_kill_len)
+{
+ Vector<WeldVert> wvert;
+ wvert.reserve(std::min<int>(2 * vert_kill_len, vert_dest_map.size()));
+
+ for (const int i : vert_dest_map.index_range()) {
+ if (vert_dest_map[i] != OUT_OF_CONTEXT) {
+ wvert.append({vert_dest_map[i], i});
+ }
+ }
+ return wvert;
+}
+
+static void weld_vert_groups_setup(Span<WeldVert> wvert,
+ Span<int> vert_dest_map,
+ MutableSpan<int> r_vert_groups_map,
+ Array<int> &r_vert_groups_buffer,
+ Array<WeldGroup> &r_vert_groups)
+{
+ /* Get weld vert groups. */
+
+ int wgroups_len = 0;
+ for (const int i : vert_dest_map.index_range()) {
+ const int vert_dest = vert_dest_map[i];
+ if (vert_dest != OUT_OF_CONTEXT) {
+ if (vert_dest != i) {
+ r_vert_groups_map[i] = ELEM_MERGED;
+ }
+ else {
+ r_vert_groups_map[i] = wgroups_len;
+ wgroups_len++;
+ }
+ }
+ else {
+ r_vert_groups_map[i] = OUT_OF_CONTEXT;
+ }
+ }
+
+ r_vert_groups.reinitialize(wgroups_len);
+ r_vert_groups.fill({0, 0});
+ MutableSpan<WeldGroup> wgroups = r_vert_groups;
+
+ for (const WeldVert &wv : wvert) {
+ int group_index = r_vert_groups_map[wv.vert_dest];
+ wgroups[group_index].len++;
+ }
+
+ int ofs = 0;
+ for (WeldGroup &wg : wgroups) {
+ wg.ofs = ofs;
+ ofs += wg.len;
+ }
+
+ BLI_assert(ofs == wvert.size());
+
+ r_vert_groups_buffer.reinitialize(ofs);
+ for (const WeldVert &wv : wvert) {
+ int group_index = r_vert_groups_map[wv.vert_dest];
+ r_vert_groups_buffer[wgroups[group_index].ofs++] = wv.vert_orig;
+ }
+
+ for (WeldGroup &wg : wgroups) {
+ wg.ofs -= wg.len;
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Weld Edge API
+ * \{ */
+
+static void weld_edge_ctx_setup(MutableSpan<WeldGroup> r_vlinks,
+ MutableSpan<int> r_edge_dest_map,
+ MutableSpan<WeldEdge> r_wedge,
+ int *r_edge_kiil_len)
+{
+ /* Setup Edge Overlap. */
+ int edge_kill_len = 0;
+
+ MutableSpan<WeldGroup> v_links = r_vlinks;
+
+ for (WeldEdge &we : r_wedge) {
+ int dst_vert_a = we.vert_a;
+ int dst_vert_b = we.vert_b;
+
+ if (dst_vert_a == dst_vert_b) {
+ BLI_assert(we.edge_dest == OUT_OF_CONTEXT);
+ r_edge_dest_map[we.edge_orig] = ELEM_COLLAPSED;
+ we.flag = ELEM_COLLAPSED;
+ edge_kill_len++;
+ continue;
+ }
+
+ v_links[dst_vert_a].len++;
+ v_links[dst_vert_b].len++;
+ }
+
+ int link_len = 0;
+ for (WeldGroup &vl : r_vlinks) {
+ vl.ofs = link_len;
+ link_len += vl.len;
+ }
+
+ if (link_len > 0) {
+ Array<int> link_edge_buffer(link_len);
+
+ for (const int i : r_wedge.index_range()) {
+ const WeldEdge &we = r_wedge[i];
+ if (we.flag == ELEM_COLLAPSED) {
+ continue;
+ }
+
+ int dst_vert_a = we.vert_a;
+ int dst_vert_b = we.vert_b;
+
+ link_edge_buffer[v_links[dst_vert_a].ofs++] = i;
+ link_edge_buffer[v_links[dst_vert_b].ofs++] = i;
+ }
+
+ for (WeldGroup &vl : r_vlinks) {
+ /* Fix offset */
+ vl.ofs -= vl.len;
+ }
+
+ for (const int i : r_wedge.index_range()) {
+ const WeldEdge &we = r_wedge[i];
+ if (we.edge_dest != OUT_OF_CONTEXT) {
+ /* No need to retest edges.
+ * (Already includes collapsed edges). */
+ continue;
+ }
+
+ int dst_vert_a = we.vert_a;
+ int dst_vert_b = we.vert_b;
+
+ struct WeldGroup *link_a = &v_links[dst_vert_a];
+ struct WeldGroup *link_b = &v_links[dst_vert_b];
+
+ int edges_len_a = link_a->len;
+ int edges_len_b = link_b->len;
+
+ if (edges_len_a <= 1 || edges_len_b <= 1) {
+ continue;
+ }
+
+ int *edges_ctx_a = &link_edge_buffer[link_a->ofs];
+ int *edges_ctx_b = &link_edge_buffer[link_b->ofs];
+ int edge_orig = we.edge_orig;
+
+ for (; edges_len_a--; edges_ctx_a++) {
+ int e_ctx_a = *edges_ctx_a;
+ if (e_ctx_a == i) {
+ continue;
+ }
+ while (edges_len_b && *edges_ctx_b < e_ctx_a) {
+ edges_ctx_b++;
+ edges_len_b--;
+ }
+ if (edges_len_b == 0) {
+ break;
+ }
+ int e_ctx_b = *edges_ctx_b;
+ if (e_ctx_a == e_ctx_b) {
+ WeldEdge *we_b = &r_wedge[e_ctx_b];
+ BLI_assert(ELEM(we_b->vert_a, dst_vert_a, dst_vert_b));
+ BLI_assert(ELEM(we_b->vert_b, dst_vert_a, dst_vert_b));
+ BLI_assert(we_b->edge_dest == OUT_OF_CONTEXT);
+ BLI_assert(we_b->edge_orig != edge_orig);
+ r_edge_dest_map[we_b->edge_orig] = edge_orig;
+ we_b->edge_dest = edge_orig;
+ edge_kill_len++;
+ }
+ }
+ }
+
+#ifdef USE_WELD_DEBUG
+ weld_assert_edge_kill_len(r_wedge, edge_kill_len);
+#endif
+ }
+
+ *r_edge_kiil_len = edge_kill_len;
+}
+
+static Vector<WeldEdge> weld_edge_ctx_alloc(Span<MEdge> medge,
+ Span<int> vert_dest_map,
+ MutableSpan<int> r_edge_dest_map,
+ MutableSpan<int> r_edge_ctx_map)
+{
+ /* Edge Context. */
+ int wedge_len = 0;
+
+ Vector<WeldEdge> wedge;
+ wedge.reserve(medge.size());
+
+ for (const int i : medge.index_range()) {
+ int v1 = medge[i].v1;
+ int v2 = medge[i].v2;
+ int v_dest_1 = vert_dest_map[v1];
+ int v_dest_2 = vert_dest_map[v2];
+ if ((v_dest_1 != OUT_OF_CONTEXT) || (v_dest_2 != OUT_OF_CONTEXT)) {
+ WeldEdge we{};
+ we.vert_a = (v_dest_1 != OUT_OF_CONTEXT) ? v_dest_1 : v1;
+ we.vert_b = (v_dest_2 != OUT_OF_CONTEXT) ? v_dest_2 : v2;
+ we.edge_dest = OUT_OF_CONTEXT;
+ we.edge_orig = i;
+ wedge.append(we);
+ r_edge_dest_map[i] = i;
+ r_edge_ctx_map[i] = wedge_len++;
+ }
+ else {
+ r_edge_dest_map[i] = OUT_OF_CONTEXT;
+ r_edge_ctx_map[i] = OUT_OF_CONTEXT;
+ }
+ }
+
+ return wedge;
+}
+
+static void weld_edge_groups_setup(const int medge_len,
+ const int edge_kill_len,
+ MutableSpan<WeldEdge> wedge,
+ Span<int> wedge_map,
+ MutableSpan<int> r_edge_groups_map,
+ Array<int> &r_edge_groups_buffer,
+ Array<WeldGroupEdge> &r_edge_groups)
+{
+ /* Get weld edge groups. */
+
+ struct WeldGroupEdge *wegrp_iter;
+
+ int wgroups_len = wedge.size() - edge_kill_len;
+ r_edge_groups.reinitialize(wgroups_len);
+ r_edge_groups.fill({0});
+ MutableSpan<WeldGroupEdge> wegroups = r_edge_groups;
+ wegrp_iter = &r_edge_groups[0];
+
+ wgroups_len = 0;
+ for (const int i : IndexRange(medge_len)) {
+ int edge_ctx = wedge_map[i];
+ if (edge_ctx != OUT_OF_CONTEXT) {
+ WeldEdge *we = &wedge[edge_ctx];
+ int edge_dest = we->edge_dest;
+ if (edge_dest != OUT_OF_CONTEXT) {
+ BLI_assert(edge_dest != we->edge_orig);
+ r_edge_groups_map[i] = ELEM_MERGED;
+ }
+ else {
+ we->edge_dest = we->edge_orig;
+ wegrp_iter->v1 = we->vert_a;
+ wegrp_iter->v2 = we->vert_b;
+ r_edge_groups_map[i] = wgroups_len;
+ wgroups_len++;
+ wegrp_iter++;
+ }
+ }
+ else {
+ r_edge_groups_map[i] = OUT_OF_CONTEXT;
+ }
+ }
+
+ BLI_assert(wgroups_len == wedge.size() - edge_kill_len);
+
+ for (const WeldEdge &we : wedge) {
+ if (we.flag == ELEM_COLLAPSED) {
+ continue;
+ }
+ int group_index = r_edge_groups_map[we.edge_dest];
+ wegroups[group_index].group.len++;
+ }
+
+ int ofs = 0;
+ for (WeldGroupEdge &wegrp : wegroups) {
+ wegrp.group.ofs = ofs;
+ ofs += wegrp.group.len;
+ }
+
+ r_edge_groups_buffer.reinitialize(ofs);
+ for (const WeldEdge &we : wedge) {
+ if (we.flag == ELEM_COLLAPSED) {
+ continue;
+ }
+ int group_index = r_edge_groups_map[we.edge_dest];
+ r_edge_groups_buffer[wegroups[group_index].group.ofs++] = we.edge_orig;
+ }
+
+ for (WeldGroupEdge &wegrp : wegroups) {
+ wegrp.group.ofs -= wegrp.group.len;
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Weld Poly and Loop API
+ * \{ */
+
+static bool weld_iter_loop_of_poly_begin(WeldLoopOfPolyIter *iter,
+ const WeldPoly &wp,
+ Span<WeldLoop> wloop,
+ Span<MLoop> mloop,
+ Span<int> loop_map,
+ int *group_buffer)
+{
+ if (wp.flag == ELEM_COLLAPSED) {
+ return false;
+ }
+
+ iter->loop_start = wp.loop_start;
+ iter->loop_end = wp.loop_end;
+ iter->wloop = wloop;
+ iter->mloop = mloop;
+ iter->loop_map = loop_map;
+ iter->group = group_buffer;
+
+ int group_len = 0;
+ if (group_buffer) {
+ /* First loop group needs more attention. */
+ int loop_start, loop_end, l;
+ loop_start = iter->loop_start;
+ loop_end = l = iter->loop_end;
+ while (l >= loop_start) {
+ const 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;
+ }
+ }
+ break;
+ }
+ if (l != loop_end) {
+ group_len = loop_end - l;
+ int i = 0;
+ while (l < loop_end) {
+ iter->group[i++] = ++l;
+ }
+ }
+ }
+ iter->group_len = group_len;
+
+ iter->l_next = iter->loop_start;
+#ifdef USE_WELD_DEBUG
+ iter->v = OUT_OF_CONTEXT;
+#endif
+ return true;
+}
+
+static bool weld_iter_loop_of_poly_next(WeldLoopOfPolyIter *iter)
+{
+ int loop_end = iter->loop_end;
+ Span<WeldLoop> wloop = iter->wloop;
+ Span<int> loop_map = iter->loop_map;
+ int l = iter->l_curr = iter->l_next;
+ if (l == iter->loop_start) {
+ /* `grupo_len` is already calculated in the first loop */
+ }
+ else {
+ iter->group_len = 0;
+ }
+ while (l <= loop_end) {
+ int l_next = l + 1;
+ const int loop_ctx = loop_map[l];
+ if (loop_ctx != OUT_OF_CONTEXT) {
+ const WeldLoop *wl = &wloop[loop_ctx];
+ if (wl->loop_skip_to != OUT_OF_CONTEXT) {
+ l_next = wl->loop_skip_to;
+ }
+ if (wl->flag == ELEM_COLLAPSED) {
+ if (iter->group) {
+ iter->group[iter->group_len++] = l;
+ }
+ l = l_next;
+ continue;
+ }
+#ifdef USE_WELD_DEBUG
+ BLI_assert(iter->v != wl->vert);
+#endif
+ iter->v = wl->vert;
+ iter->e = wl->edge;
+ iter->type = 1;
+ }
+ else {
+ const MLoop *ml = &iter->mloop[l];
+#ifdef USE_WELD_DEBUG
+ BLI_assert((uint)iter->v != ml->v);
+#endif
+ iter->v = ml->v;
+ iter->e = ml->e;
+ iter->type = 0;
+ }
+ if (iter->group) {
+ iter->group[iter->group_len++] = l;
+ }
+ iter->l_next = l_next;
+ return true;
+ }
+
+ return false;
+}
+
+static void weld_poly_loop_ctx_alloc(Span<MPoly> mpoly,
+ Span<MLoop> mloop,
+ Span<int> vert_dest_map,
+ Span<int> edge_dest_map,
+ WeldMesh *r_weld_mesh)
+{
+ /* Loop/Poly Context. */
+ Array<int> loop_map(mloop.size());
+ Array<int> poly_map(mpoly.size());
+ int wloop_len = 0;
+ int wpoly_len = 0;
+ int max_ctx_poly_len = 4;
+
+ Vector<WeldLoop> wloop;
+ wloop.reserve(mloop.size());
+
+ Vector<WeldPoly> wpoly;
+ wpoly.reserve(mpoly.size());
+
+ int maybe_new_poly = 0;
+
+ for (const int i : mpoly.index_range()) {
+ const MPoly &mp = mpoly[i];
+ const int loopstart = mp.loopstart;
+ const int totloop = mp.totloop;
+
+ int vert_ctx_len = 0;
+
+ int prev_wloop_len = wloop_len;
+ for (const int i_loop : mloop.index_range().slice(loopstart, totloop)) {
+ int v = mloop[i_loop].v;
+ int e = mloop[i_loop].e;
+ 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) {
+ vert_ctx_len++;
+ }
+ if (is_vert_ctx || is_edge_ctx) {
+ WeldLoop wl{};
+ wl.vert = is_vert_ctx ? v_dest : v;
+ wl.edge = is_edge_ctx ? e_dest : e;
+ wl.loop_orig = i_loop;
+ wl.loop_skip_to = OUT_OF_CONTEXT;
+ wloop.append(wl);
+
+ loop_map[i_loop] = wloop_len++;
+ }
+ else {
+ loop_map[i_loop] = OUT_OF_CONTEXT;
+ }
+ }
+ if (wloop_len != prev_wloop_len) {
+ int loops_len = wloop_len - prev_wloop_len;
+ WeldPoly wp{};
+ wp.poly_dst = OUT_OF_CONTEXT;
+ wp.poly_orig = i;
+ wp.loops.len = loops_len;
+ wp.loops.ofs = prev_wloop_len;
+ wp.loop_start = loopstart;
+ wp.loop_end = loopstart + totloop - 1;
+ wp.len = totloop;
+ wpoly.append(wp);
+
+ poly_map[i] = wpoly_len++;
+ if (totloop > 5 && vert_ctx_len > 1) {
+ int max_new = (totloop / 3) - 1;
+ vert_ctx_len /= 2;
+ maybe_new_poly += MIN2(max_new, vert_ctx_len);
+ CLAMP_MIN(max_ctx_poly_len, totloop);
+ }
+ }
+ else {
+ poly_map[i] = OUT_OF_CONTEXT;
+ }
+ }
+
+ if (mpoly.size() < (wpoly_len + maybe_new_poly)) {
+ wpoly.resize(wpoly_len + maybe_new_poly);
+ }
+
+ WeldPoly *poly_new = wpoly.data() + wpoly_len;
+
+ r_weld_mesh->wloop = std::move(wloop);
+ r_weld_mesh->wpoly = std::move(wpoly);
+ r_weld_mesh->wpoly_new = poly_new;
+ r_weld_mesh->wloop_len = wloop_len;
+ r_weld_mesh->wpoly_len = wpoly_len;
+ 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;
+}
+
+static void weld_poly_split_recursive(Span<int> vert_dest_map,
+#ifdef USE_WELD_DEBUG
+ const Span<MLoop> mloop,
+#endif
+ int ctx_verts_len,
+ WeldPoly *r_wp,
+ WeldMesh *r_weld_mesh,
+ int *r_poly_kill,
+ int *r_loop_kill)
+{
+ int poly_len = r_wp->len;
+ if (poly_len > 3 && ctx_verts_len > 1) {
+ const int ctx_loops_len = r_wp->loops.len;
+ const int ctx_loops_ofs = r_wp->loops.ofs;
+ MutableSpan<WeldLoop> wloop = r_weld_mesh->wloop;
+ WeldPoly *wpoly_new = r_weld_mesh->wpoly_new;
+
+ int loop_kill = 0;
+
+ WeldLoop *poly_loops = &wloop[ctx_loops_ofs];
+ WeldLoop *wla = &poly_loops[0];
+ WeldLoop *wla_prev = &poly_loops[ctx_loops_len - 1];
+ while (wla_prev->flag == ELEM_COLLAPSED) {
+ wla_prev--;
+ }
+ const int la_len = ctx_loops_len - 1;
+ for (int la = 0; la < la_len; la++, wla++) {
+ wa_continue:
+ if (wla->flag == ELEM_COLLAPSED) {
+ continue;
+ }
+ int vert_a = wla->vert;
+ /* Only test vertices that will be merged. */
+ if (vert_dest_map[vert_a] != OUT_OF_CONTEXT) {
+ int lb = la + 1;
+ WeldLoop *wlb = wla + 1;
+ WeldLoop *wlb_prev = wla;
+ int killed_ab = 0;
+ ctx_verts_len = 1;
+ for (; lb < ctx_loops_len; lb++, wlb++) {
+ BLI_assert(wlb->loop_skip_to == OUT_OF_CONTEXT);
+ if (wlb->flag == ELEM_COLLAPSED) {
+ killed_ab++;
+ continue;
+ }
+ int vert_b = wlb->vert;
+ if (vert_dest_map[vert_b] != OUT_OF_CONTEXT) {
+ ctx_verts_len++;
+ }
+ if (vert_a == vert_b) {
+ const int dist_a = wlb->loop_orig - wla->loop_orig - killed_ab;
+ const int dist_b = poly_len - dist_a;
+
+ BLI_assert(dist_a != 0 && dist_b != 0);
+ if (dist_a == 1 || dist_b == 1) {
+ BLI_assert(dist_a != dist_b);
+ BLI_assert((wla->flag == ELEM_COLLAPSED) || (wlb->flag == ELEM_COLLAPSED));
+ }
+ else {
+ WeldLoop *wl_tmp = nullptr;
+ if (dist_a == 2) {
+ wl_tmp = wlb_prev;
+ BLI_assert(wla->flag != ELEM_COLLAPSED);
+ BLI_assert(wl_tmp->flag != ELEM_COLLAPSED);
+ wla->flag = ELEM_COLLAPSED;
+ wl_tmp->flag = ELEM_COLLAPSED;
+ loop_kill += 2;
+ poly_len -= 2;
+ }
+ if (dist_b == 2) {
+ if (wl_tmp != nullptr) {
+ r_wp->flag = ELEM_COLLAPSED;
+ *r_poly_kill += 1;
+ }
+ else {
+ wl_tmp = wla_prev;
+ BLI_assert(wlb->flag != ELEM_COLLAPSED);
+ BLI_assert(wl_tmp->flag != ELEM_COLLAPSED);
+ wlb->flag = ELEM_COLLAPSED;
+ wl_tmp->flag = ELEM_COLLAPSED;
+ }
+ loop_kill += 2;
+ poly_len -= 2;
+ }
+ if (wl_tmp == nullptr) {
+ const int new_loops_len = lb - la;
+ const int new_loops_ofs = ctx_loops_ofs + la;
+
+ WeldPoly *new_wp = &wpoly_new[r_weld_mesh->wpoly_new_len++];
+ new_wp->poly_dst = OUT_OF_CONTEXT;
+ new_wp->poly_orig = r_wp->poly_orig;
+ new_wp->loops.len = new_loops_len;
+ new_wp->loops.ofs = new_loops_ofs;
+ new_wp->loop_start = wla->loop_orig;
+ new_wp->loop_end = wlb_prev->loop_orig;
+ new_wp->len = dist_a;
+ weld_poly_split_recursive(vert_dest_map,
+#ifdef USE_WELD_DEBUG
+ mloop,
+#endif
+ ctx_verts_len,
+ new_wp,
+ r_weld_mesh,
+ r_poly_kill,
+ r_loop_kill);
+ BLI_assert(dist_b == poly_len - dist_a);
+ poly_len = dist_b;
+ if (wla_prev->loop_orig > wla->loop_orig) {
+ /* New start. */
+ r_wp->loop_start = wlb->loop_orig;
+ }
+ else {
+ /* The `loop_start` doesn't change but some loops must be skipped. */
+ wla_prev->loop_skip_to = wlb->loop_orig;
+ }
+ wla = wlb;
+ la = lb;
+ goto wa_continue;
+ }
+ break;
+ }
+ }
+ if (wlb->flag != ELEM_COLLAPSED) {
+ wlb_prev = wlb;
+ }
+ }
+ }
+ if (wla->flag != ELEM_COLLAPSED) {
+ wla_prev = wla;
+ }
+ }
+ r_wp->len = poly_len;
+ *r_loop_kill += loop_kill;
+
+#ifdef USE_WELD_DEBUG
+ weld_assert_poly_no_vert_repetition(*r_wp, wloop, mloop, r_weld_mesh->loop_map);
+#endif
+ }
+}
+
+static void weld_poly_loop_ctx_setup(Span<MLoop> mloop,
+#ifdef USE_WELD_DEBUG
+ Span<MPoly> mpoly,
+#endif
+ const int mvert_len,
+ Span<int> vert_dest_map,
+ const int remain_edge_ctx_len,
+ MutableSpan<WeldGroup> r_vlinks,
+ WeldMesh *r_weld_mesh)
+{
+ int poly_kill_len, loop_kill_len, wpoly_len, wpoly_new_len;
+
+ WeldPoly *wpoly_new;
+ WeldLoop *wl;
+
+ MutableSpan<WeldPoly> wpoly = r_weld_mesh->wpoly;
+ MutableSpan<WeldLoop> wloop = r_weld_mesh->wloop;
+ wpoly_new = r_weld_mesh->wpoly_new;
+ wpoly_len = r_weld_mesh->wpoly_len;
+ wpoly_new_len = 0;
+ poly_kill_len = 0;
+ loop_kill_len = 0;
+
+ Span<int> loop_map = r_weld_mesh->loop_map;
+
+ if (remain_edge_ctx_len) {
+
+ /* Setup Poly/Loop. Note that `wpoly_len` may be different than `wpoly.size()` here. */
+ for (const int i : IndexRange(wpoly_len)) {
+ WeldPoly &wp = wpoly[i];
+ const int ctx_loops_len = wp.loops.len;
+ const int ctx_loops_ofs = wp.loops.ofs;
+
+ int poly_len = wp.len;
+ int ctx_verts_len = 0;
+ wl = &wloop[ctx_loops_ofs];
+ for (int l = ctx_loops_len; l--; wl++) {
+ const int edge_dest = wl->edge;
+ if (edge_dest == ELEM_COLLAPSED) {
+ wl->flag = ELEM_COLLAPSED;
+ if (poly_len == 3) {
+ wp.flag = ELEM_COLLAPSED;
+ poly_kill_len++;
+ loop_kill_len += 3;
+ poly_len = 0;
+ break;
+ }
+ loop_kill_len++;
+ poly_len--;
+ }
+ else {
+ const int vert_dst = wl->vert;
+ if (vert_dest_map[vert_dst] != OUT_OF_CONTEXT) {
+ ctx_verts_len++;
+ }
+ }
+ }
+
+ if (poly_len) {
+ wp.len = poly_len;
+#ifdef USE_WELD_DEBUG
+ weld_assert_poly_len(wp, wloop);
+#endif
+
+ weld_poly_split_recursive(vert_dest_map,
+#ifdef USE_WELD_DEBUG
+ mloop,
+#endif
+ ctx_verts_len,
+ &wp,
+ r_weld_mesh,
+ &poly_kill_len,
+ &loop_kill_len);
+
+ wpoly_new_len = r_weld_mesh->wpoly_new_len;
+ }
+ }
+
+#ifdef USE_WELD_DEBUG
+ weld_assert_poly_and_loop_kill_len(wpoly,
+ {wpoly_new, wpoly_new_len},
+ wloop,
+ mloop,
+ loop_map,
+ r_weld_mesh->poly_map,
+ mpoly,
+ poly_kill_len,
+ loop_kill_len);
+#endif
+
+ /* Setup Polygon Overlap. */
+
+ int wpoly_and_new_len = wpoly_len + wpoly_new_len;
+
+ r_vlinks.fill({0, 0});
+ MutableSpan<WeldGroup> v_links = r_vlinks;
+
+ for (const int i : IndexRange(wpoly_and_new_len)) {
+ const WeldPoly &wp = wpoly[i];
+ WeldLoopOfPolyIter iter;
+ if (weld_iter_loop_of_poly_begin(&iter, wp, wloop, mloop, loop_map, nullptr)) {
+ while (weld_iter_loop_of_poly_next(&iter)) {
+ v_links[iter.v].len++;
+ }
+ }
+ }
+
+ int link_len = 0;
+ for (const int i : IndexRange(mvert_len)) {
+ v_links[i].ofs = link_len;
+ link_len += v_links[i].len;
+ }
+
+ if (link_len) {
+ Array<int> link_poly_buffer(link_len);
+
+ for (const int i : IndexRange(wpoly_and_new_len)) {
+ const WeldPoly &wp = wpoly[i];
+ WeldLoopOfPolyIter iter;
+ if (weld_iter_loop_of_poly_begin(&iter, wp, wloop, mloop, loop_map, nullptr)) {
+ while (weld_iter_loop_of_poly_next(&iter)) {
+ link_poly_buffer[v_links[iter.v].ofs++] = i;
+ }
+ }
+ }
+
+ for (WeldGroup &vl : r_vlinks) {
+ /* Fix offset */
+ vl.ofs -= vl.len;
+ }
+
+ int polys_len_a, polys_len_b, *polys_ctx_a, *polys_ctx_b, p_ctx_a, p_ctx_b;
+ polys_len_b = p_ctx_b = 0; /* silence warnings */
+
+ for (const int i : IndexRange(wpoly_and_new_len)) {
+ const WeldPoly &wp = wpoly[i];
+ if (wp.poly_dst != OUT_OF_CONTEXT) {
+ /* No need to retest poly.
+ * (Already includes collapsed polygons). */
+ continue;
+ }
+
+ WeldLoopOfPolyIter iter;
+ weld_iter_loop_of_poly_begin(&iter, wp, wloop, mloop, loop_map, nullptr);
+ weld_iter_loop_of_poly_next(&iter);
+ struct WeldGroup *link_a = &v_links[iter.v];
+ polys_len_a = link_a->len;
+ if (polys_len_a == 1) {
+ BLI_assert(link_poly_buffer[link_a->ofs] == i);
+ continue;
+ }
+ int wp_len = wp.len;
+ polys_ctx_a = &link_poly_buffer[link_a->ofs];
+ for (; polys_len_a--; polys_ctx_a++) {
+ p_ctx_a = *polys_ctx_a;
+ if (p_ctx_a == i) {
+ continue;
+ }
+
+ WeldPoly *wp_tmp = &wpoly[p_ctx_a];
+ if (wp_tmp->len != wp_len) {
+ continue;
+ }
+
+ WeldLoopOfPolyIter iter_b = iter;
+ while (weld_iter_loop_of_poly_next(&iter_b)) {
+ struct WeldGroup *link_b = &v_links[iter_b.v];
+ polys_len_b = link_b->len;
+ if (polys_len_b == 1) {
+ BLI_assert(link_poly_buffer[link_b->ofs] == i);
+ polys_len_b = 0;
+ break;
+ }
+
+ polys_ctx_b = &link_poly_buffer[link_b->ofs];
+ for (; polys_len_b; polys_len_b--, polys_ctx_b++) {
+ p_ctx_b = *polys_ctx_b;
+ if (p_ctx_b < p_ctx_a) {
+ continue;
+ }
+ if (p_ctx_b >= p_ctx_a) {
+ if (p_ctx_b > p_ctx_a) {
+ polys_len_b = 0;
+ }
+ break;
+ }
+ }
+ if (polys_len_b == 0) {
+ break;
+ }
+ }
+ if (polys_len_b == 0) {
+ continue;
+ }
+ BLI_assert(p_ctx_a > i);
+ BLI_assert(p_ctx_a == p_ctx_b);
+ BLI_assert(wp_tmp->poly_dst == OUT_OF_CONTEXT);
+ BLI_assert(wp_tmp != &wp);
+ wp_tmp->poly_dst = wp.poly_orig;
+ loop_kill_len += wp_tmp->len;
+ poly_kill_len++;
+ }
+ }
+ }
+ }
+ else {
+ poly_kill_len = r_weld_mesh->wpoly_len;
+ loop_kill_len = r_weld_mesh->wloop_len;
+
+ for (WeldPoly &wp : wpoly) {
+ wp.flag = ELEM_COLLAPSED;
+ }
+ }
+
+#ifdef USE_WELD_DEBUG
+ weld_assert_poly_and_loop_kill_len(wpoly,
+ {wpoly_new, wpoly_new_len},
+ wloop,
+ mloop,
+ loop_map,
+ r_weld_mesh->poly_map,
+ mpoly,
+ poly_kill_len,
+ loop_kill_len);
+#endif
+
+ r_weld_mesh->wpoly_new = wpoly_new;
+ r_weld_mesh->poly_kill_len = poly_kill_len;
+ r_weld_mesh->loop_kill_len = loop_kill_len;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Weld Mesh API
+ * \{ */
+
+static void weld_mesh_context_create(const Mesh *mesh,
+ MutableSpan<int> vert_dest_map,
+ const int vert_kill_len,
+ WeldMesh *r_weld_mesh)
+{
+ Span<MEdge> medge{mesh->medge, mesh->totedge};
+ Span<MPoly> mpoly{mesh->mpoly, mesh->totpoly};
+ Span<MLoop> mloop{mesh->mloop, mesh->totloop};
+ const int mvert_len = mesh->totvert;
+
+ Vector<WeldVert> wvert = weld_vert_ctx_alloc_and_setup(vert_dest_map, vert_kill_len);
+ r_weld_mesh->vert_kill_len = vert_kill_len;
+
+ Array<int> edge_dest_map(medge.size());
+ Array<int> edge_ctx_map(medge.size());
+ Vector<WeldEdge> wedge = weld_edge_ctx_alloc(medge, vert_dest_map, edge_dest_map, edge_ctx_map);
+
+ Array<WeldGroup> v_links(mvert_len, {0, 0});
+ weld_edge_ctx_setup(v_links, edge_dest_map, wedge, &r_weld_mesh->edge_kill_len);
+
+ weld_poly_loop_ctx_alloc(mpoly, mloop, vert_dest_map, edge_dest_map, r_weld_mesh);
+
+ weld_poly_loop_ctx_setup(mloop,
+#ifdef USE_WELD_DEBUG
+ mpoly,
+
+#endif
+ mvert_len,
+ vert_dest_map,
+ wedge.size() - r_weld_mesh->edge_kill_len,
+ v_links,
+ r_weld_mesh);
+
+ weld_vert_groups_setup(wvert,
+ vert_dest_map,
+ vert_dest_map,
+ r_weld_mesh->vert_groups_buffer,
+ r_weld_mesh->vert_groups);
+
+ weld_edge_groups_setup(medge.size(),
+ r_weld_mesh->edge_kill_len,
+ wedge,
+ edge_ctx_map,
+ edge_dest_map,
+ r_weld_mesh->edge_groups_buffer,
+ r_weld_mesh->edge_groups);
+
+ r_weld_mesh->edge_groups_map = std::move(edge_dest_map);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Weld CustomData
+ * \{ */
+
+static void customdata_weld(
+ const CustomData *source, CustomData *dest, const int *src_indices, int count, int dest_index)
+{
+ if (count == 1) {
+ CustomData_copy_data(source, dest, src_indices[0], dest_index, 1);
+ return;
+ }
+
+ CustomData_interp(source, dest, (const int *)src_indices, nullptr, nullptr, count, dest_index);
+
+ int src_i, dest_i;
+ int j;
+
+ float co[3] = {0.0f, 0.0f, 0.0f};
+#ifdef USE_WELD_NORMALS
+ float no[3] = {0.0f, 0.0f, 0.0f};
+#endif
+ int crease = 0;
+ int bweight = 0;
+ short flag = 0;
+
+ /* interpolates a layer at a time */
+ dest_i = 0;
+ for (src_i = 0; src_i < source->totlayer; src_i++) {
+ const int type = source->layers[src_i].type;
+
+ /* find the first dest layer with type >= the source type
+ * (this should work because layers are ordered by type)
+ */
+ while (dest_i < dest->totlayer && dest->layers[dest_i].type < type) {
+ dest_i++;
+ }
+
+ /* if there are no more dest layers, we're done */
+ if (dest_i == dest->totlayer) {
+ break;
+ }
+
+ /* if we found a matching layer, add the data */
+ if (dest->layers[dest_i].type == type) {
+ void *src_data = source->layers[src_i].data;
+
+ if (type == CD_MVERT) {
+ for (j = 0; j < count; j++) {
+ MVert *mv_src = &((MVert *)src_data)[src_indices[j]];
+ add_v3_v3(co, mv_src->co);
+#ifdef USE_WELD_NORMALS
+ short *mv_src_no = mv_src->no;
+ no[0] += mv_src_no[0];
+ no[1] += mv_src_no[1];
+ no[2] += mv_src_no[2];
+#endif
+ bweight += mv_src->bweight;
+ flag |= mv_src->flag;
+ }
+ }
+ else if (type == CD_MEDGE) {
+ for (j = 0; j < count; j++) {
+ MEdge *me_src = &((MEdge *)src_data)[src_indices[j]];
+ crease += me_src->crease;
+ bweight += me_src->bweight;
+ flag |= me_src->flag;
+ }
+ }
+ else if (CustomData_layer_has_interp(dest, dest_i)) {
+ /* Already calculated.
+ * TODO: Optimize by exposing `typeInfo->interp`. */
+ }
+ else if (CustomData_layer_has_math(dest, dest_i)) {
+ const int size = CustomData_sizeof(type);
+ void *dst_data = dest->layers[dest_i].data;
+ void *v_dst = POINTER_OFFSET(dst_data, (size_t)dest_index * size);
+ for (j = 0; j < count; j++) {
+ CustomData_data_add(
+ type, v_dst, POINTER_OFFSET(src_data, (size_t)src_indices[j] * size));
+ }
+ }
+ else {
+ CustomData_copy_layer_type_data(source, dest, type, src_indices[0], dest_index, 1);
+ }
+
+ /* if there are multiple source & dest layers of the same type,
+ * we don't want to copy all source layers to the same dest, so
+ * increment dest_i
+ */
+ dest_i++;
+ }
+ }
+
+ float fac = 1.0f / count;
+
+ for (dest_i = 0; dest_i < dest->totlayer; dest_i++) {
+ CustomDataLayer *layer_dst = &dest->layers[dest_i];
+ const int type = layer_dst->type;
+ if (type == CD_MVERT) {
+ MVert *mv = &((MVert *)layer_dst->data)[dest_index];
+ mul_v3_fl(co, fac);
+ bweight *= fac;
+ CLAMP_MAX(bweight, 255);
+
+ copy_v3_v3(mv->co, co);
+#ifdef USE_WELD_NORMALS
+ mul_v3_fl(no, fac);
+ short *mv_no = mv->no;
+ mv_no[0] = (short)no[0];
+ mv_no[1] = (short)no[1];
+ mv_no[2] = (short)no[2];
+#endif
+
+ mv->flag = (char)flag;
+ mv->bweight = (char)bweight;
+ }
+ else if (type == CD_MEDGE) {
+ MEdge *me = &((MEdge *)layer_dst->data)[dest_index];
+ crease *= fac;
+ bweight *= fac;
+ CLAMP_MAX(crease, 255);
+ CLAMP_MAX(bweight, 255);
+
+ me->crease = (char)crease;
+ me->bweight = (char)bweight;
+ me->flag = flag;
+ }
+ else if (CustomData_layer_has_interp(dest, dest_i)) {
+ /* Already calculated. */
+ }
+ else if (CustomData_layer_has_math(dest, dest_i)) {
+ const int size = CustomData_sizeof(type);
+ void *dst_data = layer_dst->data;
+ void *v_dst = POINTER_OFFSET(dst_data, (size_t)dest_index * size);
+ CustomData_data_multiply(type, v_dst, fac);
+ }
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Weld Modifier Main
+ * \{ */
+
+#ifdef USE_BVHTREEKDOP
+struct WeldOverlapData {
+ const MVert *mvert;
+ float merge_dist_sq;
+};
+static bool bvhtree_weld_overlap_cb(void *userdata, int index_a, int index_b, int UNUSED(thread))
+{
+ if (index_a < index_b) {
+ struct WeldOverlapData *data = userdata;
+ const MVert *mvert = data->mvert;
+ const float dist_sq = len_squared_v3v3(mvert[index_a].co, mvert[index_b].co);
+ BLI_assert(dist_sq <= ((data->merge_dist_sq + FLT_EPSILON) * 3));
+ return dist_sq <= data->merge_dist_sq;
+ }
+ return false;
+}
+#endif
+
+/** Use for #MOD_WELD_MODE_CONNECTED calculation. */
+struct WeldVertexCluster {
+ float co[3];
+ int merged_verts;
+};
+
+static Mesh *weldModifier_doWeld(WeldModifierData *wmd,
+ const ModifierEvalContext *UNUSED(ctx),
+ Mesh *mesh)
+{
+ BLI_bitmap *v_mask = nullptr;
+ int v_mask_act = 0;
+
+ Span<MVert> mvert{mesh->mvert, mesh->totvert};
+ Span<MEdge> medge{mesh->medge, mesh->totedge};
+ Span<MPoly> mpoly{mesh->mpoly, mesh->totpoly};
+ Span<MLoop> mloop{mesh->mloop, mesh->totloop};
+ const int totvert = mesh->totvert;
+ const int totedge = mesh->totedge;
+ const int totloop = mesh->totloop;
+ const int totpoly = mesh->totpoly;
+
+ /* Vertex Group. */
+ const int defgrp_index = BKE_id_defgroup_name_index(&mesh->id, wmd->defgrp_name);
+ if (defgrp_index != -1) {
+ MDeformVert *dvert;
+ dvert = static_cast<MDeformVert *>(CustomData_get_layer(&mesh->vdata, CD_MDEFORMVERT));
+ if (dvert) {
+ const bool invert_vgroup = (wmd->flag & MOD_WELD_INVERT_VGROUP) != 0;
+ v_mask = BLI_BITMAP_NEW(totvert, __func__);
+ for (const int i : IndexRange(totvert)) {
+ const bool found = BKE_defvert_find_weight(&dvert[i], defgrp_index) > 0.0f;
+ if (found != invert_vgroup) {
+ BLI_BITMAP_ENABLE(v_mask, i);
+ v_mask_act++;
+ }
+ }
+ }
+ }
+
+ /* From the original index of the vertex.
+ * This indicates which vert it is or is going to be merged. */
+ Array<int> vert_dest_map(totvert);
+ int vert_kill_len = 0;
+ if (wmd->mode == MOD_WELD_MODE_ALL)
+#ifdef USE_BVHTREEKDOP
+ {
+ /* Get overlap map. */
+ struct BVHTreeFromMesh treedata;
+ BVHTree *bvhtree = bvhtree_from_mesh_verts_ex(&treedata,
+ mvert.data(),
+ totvert,
+ false,
+ v_mask,
+ v_mask_act,
+ wmd->merge_dist / 2,
+ 2,
+ 6,
+ 0,
+ nullptr,
+ nullptr);
+
+ if (bvhtree) {
+ struct WeldOverlapData data;
+ data.mvert = mvert;
+ data.merge_dist_sq = square_f(wmd->merge_dist);
+
+ uint overlap_len;
+ BVHTreeOverlap *overlap = BLI_bvhtree_overlap_ex(bvhtree,
+ bvhtree,
+ &overlap_len,
+ bvhtree_weld_overlap_cb,
+ &data,
+ 1,
+ BVH_OVERLAP_RETURN_PAIRS);
+
+ free_bvhtree_from_mesh(&treedata);
+ if (overlap) {
+ range_vn_i(vert_dest_map.data(), totvert, 0);
+
+ const BVHTreeOverlap *overlap_iter = &overlap[0];
+ for (int i = 0; i < overlap_len; i++, overlap_iter++) {
+ int indexA = overlap_iter->indexA;
+ int indexB = overlap_iter->indexB;
+
+ BLI_assert(indexA < indexB);
+
+ int va_dst = vert_dest_map[indexA];
+ while (va_dst != vert_dest_map[va_dst]) {
+ va_dst = vert_dest_map[va_dst];
+ }
+ int vb_dst = vert_dest_map[indexB];
+ while (vb_dst != vert_dest_map[vb_dst]) {
+ vb_dst = vert_dest_map[vb_dst];
+ }
+ if (va_dst == vb_dst) {
+ continue;
+ }
+ if (va_dst > vb_dst) {
+ SWAP(int, va_dst, vb_dst);
+ }
+ vert_kill_len++;
+ vert_dest_map[vb_dst] = va_dst;
+ }
+
+ /* Fix #r_vert_dest_map for next step. */
+ for (int i = 0; i < totvert; i++) {
+ if (i == vert_dest_map[i]) {
+ vert_dest_map[i] = OUT_OF_CONTEXT;
+ }
+ else {
+ int v = i;
+ while (v != vert_dest_map[v] && vert_dest_map[v] != OUT_OF_CONTEXT) {
+ v = vert_dest_map[v];
+ }
+ vert_dest_map[v] = v;
+ vert_dest_map[i] = v;
+ }
+ }
+
+ MEM_freeN(overlap);
+ }
+ }
+ }
+#else
+ {
+ KDTree_3d *tree = BLI_kdtree_3d_new(v_mask ? v_mask_act : totvert);
+ for (const int i : IndexRange(totvert)) {
+ if (!v_mask || BLI_BITMAP_TEST(v_mask, i)) {
+ BLI_kdtree_3d_insert(tree, i, mvert[i].co);
+ }
+ vert_dest_map[i] = OUT_OF_CONTEXT;
+ }
+
+ BLI_kdtree_3d_balance(tree);
+ vert_kill_len = BLI_kdtree_3d_calc_duplicates_fast(
+ tree, wmd->merge_dist, false, vert_dest_map.data());
+ BLI_kdtree_3d_free(tree);
+ }
+#endif
+ else {
+ BLI_assert(wmd->mode == MOD_WELD_MODE_CONNECTED);
+
+ Array<WeldVertexCluster> vert_clusters(totvert);
+
+ for (const int i : mvert.index_range()) {
+ WeldVertexCluster &vc = vert_clusters[i];
+ copy_v3_v3(vc.co, mvert[i].co);
+ vc.merged_verts = 0;
+ }
+ const float merge_dist_sq = square_f(wmd->merge_dist);
+
+ range_vn_i(vert_dest_map.data(), totvert, 0);
+
+ /* Collapse Edges that are shorter than the threshold. */
+ for (const int i : medge.index_range()) {
+ int v1 = medge[i].v1;
+ int v2 = medge[i].v2;
+
+ if (wmd->flag & MOD_WELD_LOOSE_EDGES && (medge[i].flag & ME_LOOSEEDGE) == 0) {
+ continue;
+ }
+ while (v1 != vert_dest_map[v1]) {
+ v1 = vert_dest_map[v1];
+ }
+ while (v2 != vert_dest_map[v2]) {
+ v2 = vert_dest_map[v2];
+ }
+ if (v1 == v2) {
+ continue;
+ }
+ if (v_mask && (!BLI_BITMAP_TEST(v_mask, v1) || !BLI_BITMAP_TEST(v_mask, v2))) {
+ continue;
+ }
+ if (v1 > v2) {
+ SWAP(int, v1, v2);
+ }
+ WeldVertexCluster *v1_cluster = &vert_clusters[v1];
+ WeldVertexCluster *v2_cluster = &vert_clusters[v2];
+
+ float edgedir[3];
+ sub_v3_v3v3(edgedir, v2_cluster->co, v1_cluster->co);
+ const float dist_sq = len_squared_v3(edgedir);
+ if (dist_sq <= merge_dist_sq) {
+ float influence = (v2_cluster->merged_verts + 1) /
+ (float)(v1_cluster->merged_verts + v2_cluster->merged_verts + 2);
+ madd_v3_v3fl(v1_cluster->co, edgedir, influence);
+
+ v1_cluster->merged_verts += v2_cluster->merged_verts + 1;
+ vert_dest_map[v2] = v1;
+ vert_kill_len++;
+ }
+ }
+
+ for (const int i : IndexRange(totvert)) {
+ if (i == vert_dest_map[i]) {
+ vert_dest_map[i] = OUT_OF_CONTEXT;
+ }
+ else {
+ int v = i;
+ while ((v != vert_dest_map[v]) && (vert_dest_map[v] != OUT_OF_CONTEXT)) {
+ v = vert_dest_map[v];
+ }
+ vert_dest_map[v] = v;
+ vert_dest_map[i] = v;
+ }
+ }
+ }
+
+ if (v_mask) {
+ MEM_freeN(v_mask);
+ }
+
+ if (vert_kill_len == 0) {
+ return mesh;
+ }
+
+ WeldMesh weld_mesh;
+ weld_mesh_context_create(mesh, vert_dest_map, vert_kill_len, &weld_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 = totloop - weld_mesh.loop_kill_len;
+ const int result_npolys = totpoly - weld_mesh.poly_kill_len + weld_mesh.wpoly_new_len;
+
+ Mesh *result = BKE_mesh_new_nomain_from_template(
+ mesh, result_nverts, result_nedges, 0, result_nloops, result_npolys);
+
+ /* Vertices. */
+
+ /* Be careful when editing this array, to avoid new allocations it uses the same buffer as
+ * #vert_dest_map. This map will be used to adjust the edges, polys and loops. */
+ MutableSpan<int> vert_final = vert_dest_map;
+
+ int dest_index = 0;
+ for (int i = 0; i < totvert; i++) {
+ int source_index = i;
+ int count = 0;
+ while (i < totvert && vert_dest_map[i] == OUT_OF_CONTEXT) {
+ vert_final[i] = dest_index + count;
+ count++;
+ i++;
+ }
+ if (count) {
+ CustomData_copy_data(&mesh->vdata, &result->vdata, source_index, dest_index, count);
+ dest_index += count;
+ }
+ if (i == totvert) {
+ break;
+ }
+ if (vert_dest_map[i] != ELEM_MERGED) {
+ struct WeldGroup *wgroup = &weld_mesh.vert_groups[vert_dest_map[i]];
+ customdata_weld(&mesh->vdata,
+ &result->vdata,
+ &weld_mesh.vert_groups_buffer[wgroup->ofs],
+ wgroup->len,
+ dest_index);
+ vert_final[i] = dest_index;
+ dest_index++;
+ }
+ }
+
+ BLI_assert(dest_index == result_nverts);
+
+ /* Edges. */
+
+ /* Be careful when editing this array, to avoid new allocations it uses the same buffer as
+ * #edge_groups_map. This map will be used to adjust the polys and loops. */
+ MutableSpan<int> edge_final = weld_mesh.edge_groups_map;
+
+ dest_index = 0;
+ for (int i = 0; i < totedge; i++) {
+ int source_index = i;
+ int count = 0;
+ while (i < totedge && weld_mesh.edge_groups_map[i] == OUT_OF_CONTEXT) {
+ edge_final[i] = dest_index + count;
+ count++;
+ i++;
+ }
+ if (count) {
+ CustomData_copy_data(&mesh->edata, &result->edata, source_index, dest_index, count);
+ MEdge *me = &result->medge[dest_index];
+ dest_index += count;
+ for (; count--; me++) {
+ me->v1 = vert_final[me->v1];
+ me->v2 = vert_final[me->v2];
+ }
+ }
+ if (i == totedge) {
+ break;
+ }
+ if (weld_mesh.edge_groups_map[i] != ELEM_MERGED) {
+ struct WeldGroupEdge *wegrp = &weld_mesh.edge_groups[weld_mesh.edge_groups_map[i]];
+ customdata_weld(&mesh->edata,
+ &result->edata,
+ &weld_mesh.edge_groups_buffer[wegrp->group.ofs],
+ wegrp->group.len,
+ dest_index);
+ MEdge *me = &result->medge[dest_index];
+ me->v1 = vert_final[wegrp->v1];
+ me->v2 = vert_final[wegrp->v2];
+ me->flag |= ME_LOOSEEDGE;
+
+ edge_final[i] = dest_index;
+ dest_index++;
+ }
+ }
+
+ BLI_assert(dest_index == result_nedges);
+
+ /* Polys/Loops. */
+
+ MPoly *r_mp = &result->mpoly[0];
+ MLoop *r_ml = &result->mloop[0];
+ int r_i = 0;
+ int loop_cur = 0;
+ Array<int, 64> group_buffer(weld_mesh.max_poly_len);
+ for (const int i : mpoly.index_range()) {
+ const MPoly &mp = mpoly[i];
+ int loop_start = loop_cur;
+ int poly_ctx = weld_mesh.poly_map[i];
+ if (poly_ctx == OUT_OF_CONTEXT) {
+ int mp_loop_len = mp.totloop;
+ CustomData_copy_data(&mesh->ldata, &result->ldata, mp.loopstart, loop_cur, mp_loop_len);
+ loop_cur += mp_loop_len;
+ for (; mp_loop_len--; r_ml++) {
+ r_ml->v = vert_final[r_ml->v];
+ r_ml->e = edge_final[r_ml->e];
+ }
+ }
+ else {
+ const WeldPoly &wp = weld_mesh.wpoly[poly_ctx];
+ WeldLoopOfPolyIter iter;
+ if (!weld_iter_loop_of_poly_begin(
+ &iter, wp, weld_mesh.wloop, mloop, weld_mesh.loop_map, group_buffer.data())) {
+ continue;
+ }
+
+ 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);
+ int v = vert_final[iter.v];
+ int e = edge_final[iter.e];
+ r_ml->v = v;
+ r_ml->e = e;
+ r_ml++;
+ loop_cur++;
+ if (iter.type) {
+ result->medge[e].flag &= ~ME_LOOSEEDGE;
+ }
+ BLI_assert((result->medge[e].flag & ME_LOOSEEDGE) == 0);
+ }
+ }
+
+ CustomData_copy_data(&mesh->pdata, &result->pdata, i, r_i, 1);
+ r_mp->loopstart = loop_start;
+ r_mp->totloop = loop_cur - loop_start;
+ r_mp++;
+ r_i++;
+ }
+
+ for (const int i : IndexRange(weld_mesh.wpoly_new_len)) {
+ const WeldPoly &wp = weld_mesh.wpoly_new[i];
+ int loop_start = loop_cur;
+ WeldLoopOfPolyIter iter;
+ if (!weld_iter_loop_of_poly_begin(
+ &iter, wp, weld_mesh.wloop, mloop, weld_mesh.loop_map, group_buffer.data())) {
+ continue;
+ }
+
+ 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);
+ int v = vert_final[iter.v];
+ int e = edge_final[iter.e];
+ r_ml->v = v;
+ r_ml->e = e;
+ r_ml++;
+ loop_cur++;
+ if (iter.type) {
+ result->medge[e].flag &= ~ME_LOOSEEDGE;
+ }
+ BLI_assert((result->medge[e].flag & ME_LOOSEEDGE) == 0);
+ }
+
+ r_mp->loopstart = loop_start;
+ r_mp->totloop = loop_cur - loop_start;
+ r_mp++;
+ r_i++;
+ }
+
+ BLI_assert((int)r_i == result_npolys);
+ BLI_assert(loop_cur == result_nloops);
+
+ /* We could only update the normals of the elements in context, but the next modifier can make it
+ * dirty anyway which would make the work useless. */
+ BKE_mesh_normals_tag_dirty(result);
+
+ return result;
+}
+
+static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
+{
+ WeldModifierData *wmd = (WeldModifierData *)md;
+ return weldModifier_doWeld(wmd, ctx, mesh);
+}
+
+static void initData(ModifierData *md)
+{
+ WeldModifierData *wmd = (WeldModifierData *)md;
+
+ BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(wmd, modifier));
+
+ MEMCPY_STRUCT_AFTER(wmd, DNA_struct_default_get(WeldModifierData), modifier);
+}
+
+static void requiredDataMask(Object *UNUSED(ob),
+ ModifierData *md,
+ CustomData_MeshMasks *r_cddata_masks)
+{
+ WeldModifierData *wmd = (WeldModifierData *)md;
+
+ /* Ask for vertexgroups if we need them. */
+ if (wmd->defgrp_name[0] != '\0') {
+ r_cddata_masks->vmask |= CD_MASK_MDEFORMVERT;
+ }
+}
+
+static void panel_draw(const bContext *UNUSED(C), Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ob_ptr;
+ PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr);
+ int weld_mode = RNA_enum_get(ptr, "mode");
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiItemR(layout, ptr, "mode", 0, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "merge_threshold", 0, IFACE_("Distance"), ICON_NONE);
+ if (weld_mode == MOD_WELD_MODE_CONNECTED) {
+ uiItemR(layout, ptr, "loose_edges", 0, nullptr, ICON_NONE);
+ }
+ modifier_vgroup_ui(layout, ptr, &ob_ptr, "vertex_group", "invert_vertex_group", nullptr);
+
+ modifier_panel_end(layout, ptr);
+}
+
+static void panelRegister(ARegionType *region_type)
+{
+ modifier_panel_register(region_type, eModifierType_Weld, panel_draw);
+}
+
+ModifierTypeInfo modifierType_Weld = {
+ /* name */ "Weld",
+ /* structName */ "WeldModifierData",
+ /* structSize */ sizeof(WeldModifierData),
+ /* srna */ &RNA_WeldModifier,
+ /* type */ eModifierTypeType_Constructive,
+ /* flags */
+ (ModifierTypeFlag)(eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsMapping |
+ eModifierTypeFlag_SupportsEditmode | eModifierTypeFlag_EnableInEditmode |
+ eModifierTypeFlag_AcceptsCVs),
+ /* icon */ ICON_AUTOMERGE_OFF, /* TODO: Use correct icon. */
+
+ /* copyData */ BKE_modifier_copydata_generic,
+
+ /* deformVerts */ nullptr,
+ /* deformMatrices */ nullptr,
+ /* deformVertsEM */ nullptr,
+ /* deformMatricesEM */ nullptr,
+ /* modifyMesh */ modifyMesh,
+ /* modifyHair */ nullptr,
+ /* modifyGeometrySet */ nullptr,
+
+ /* initData */ initData,
+ /* requiredDataMask */ requiredDataMask,
+ /* freeData */ nullptr,
+ /* isDisabled */ nullptr,
+ /* updateDepsgraph */ nullptr,
+ /* dependsOnTime */ nullptr,
+ /* dependsOnNormals */ nullptr,
+ /* foreachIDLink */ nullptr,
+ /* foreachTexLink */ nullptr,
+ /* freeRuntimeData */ nullptr,
+ /* panelRegister */ panelRegister,
+ /* blendWrite */ nullptr,
+ /* blendRead */ nullptr,
+};
+
+/** \} */