Blender  V2.93
mesh_merge.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
17  * All rights reserved.
18  */
19 
23 #include <string.h> /* for memcpy */
24 
25 #include "MEM_guardedalloc.h"
26 
27 #include "DNA_mesh_types.h"
28 #include "DNA_meshdata_types.h"
29 
30 #include "BLI_edgehash.h"
31 #include "BLI_ghash.h"
32 #include "BLI_utildefines.h"
33 #include "BLI_utildefines_stack.h"
34 
35 #include "BKE_customdata.h"
36 #include "BKE_lib_id.h"
37 #include "BKE_mesh.h"
38 #include "BKE_mesh_mapping.h"
39 
50 static int cddm_poly_compare(MLoop *mloop_array,
51  MPoly *mpoly_source,
52  MPoly *mpoly_target,
53  const int *vtargetmap,
54  const int direct_reverse)
55 {
56  int vert_source, first_vert_source, vert_target;
57  int i_loop_source;
58  int i_loop_target, i_loop_target_start, i_loop_target_offset, i_loop_target_adjusted;
59  bool compare_completed = false;
60  bool same_loops = false;
61 
62  MLoop *mloop_source, *mloop_target;
63 
64  BLI_assert(ELEM(direct_reverse, 1, -1));
65 
66  i_loop_source = 0;
67  mloop_source = mloop_array + mpoly_source->loopstart;
68  vert_source = mloop_source->v;
69 
70  if (vtargetmap[vert_source] != -1) {
71  vert_source = vtargetmap[vert_source];
72  }
73  else {
74  /* All source loop vertices should be mapped */
75  BLI_assert(false);
76  }
77 
78  /* Find same vertex within mpoly_target's loops */
79  mloop_target = mloop_array + mpoly_target->loopstart;
80  for (i_loop_target = 0; i_loop_target < mpoly_target->totloop; i_loop_target++, mloop_target++) {
81  if (mloop_target->v == vert_source) {
82  break;
83  }
84  }
85 
86  /* If same vertex not found, then polys cannot be equal */
87  if (i_loop_target >= mpoly_target->totloop) {
88  return false;
89  }
90 
91  /* Now mloop_source and m_loop_target have one identical vertex */
92  /* mloop_source is at position 0, while m_loop_target has advanced to find identical vertex */
93  /* Go around the loop and check that all vertices match in same order */
94  /* Skipping source loops when consecutive source vertices are mapped to same target vertex */
95 
96  i_loop_target_start = i_loop_target;
97  i_loop_target_offset = 0;
98  first_vert_source = vert_source;
99 
100  compare_completed = false;
101  same_loops = false;
102 
103  while (!compare_completed) {
104 
105  vert_target = mloop_target->v;
106 
107  /* First advance i_loop_source, until it points to different vertex, after mapping applied */
108  do {
109  i_loop_source++;
110 
111  if (i_loop_source == mpoly_source->totloop) {
112  /* End of loops for source, must match end of loop for target. */
113  if (i_loop_target_offset == mpoly_target->totloop - 1) {
114  compare_completed = true;
115  same_loops = true;
116  break; /* Polys are identical */
117  }
118 
119  compare_completed = true;
120  same_loops = false;
121  break; /* Polys are different */
122  }
123 
124  mloop_source++;
125  vert_source = mloop_source->v;
126 
127  if (vtargetmap[vert_source] != -1) {
128  vert_source = vtargetmap[vert_source];
129  }
130  else {
131  /* All source loop vertices should be mapped */
132  BLI_assert(false);
133  }
134 
135  } while (vert_source == vert_target);
136 
137  if (compare_completed) {
138  break;
139  }
140 
141  /* Now advance i_loop_target as well */
142  i_loop_target_offset++;
143 
144  if (i_loop_target_offset == mpoly_target->totloop) {
145  /* End of loops for target only, that means no match */
146  /* except if all remaining source vertices are mapped to first target */
147  for (; i_loop_source < mpoly_source->totloop; i_loop_source++, mloop_source++) {
148  vert_source = vtargetmap[mloop_source->v];
149  if (vert_source != first_vert_source) {
150  compare_completed = true;
151  same_loops = false;
152  break;
153  }
154  }
155  if (!compare_completed) {
156  same_loops = true;
157  }
158  break;
159  }
160 
161  /* Adjust i_loop_target for cycling around and for direct/reverse order
162  * defined by delta = +1 or -1 */
163  i_loop_target_adjusted = (i_loop_target_start + direct_reverse * i_loop_target_offset) %
164  mpoly_target->totloop;
165  if (i_loop_target_adjusted < 0) {
166  i_loop_target_adjusted += mpoly_target->totloop;
167  }
168  mloop_target = mloop_array + mpoly_target->loopstart + i_loop_target_adjusted;
169  vert_target = mloop_target->v;
170 
171  if (vert_target != vert_source) {
172  same_loops = false; /* Polys are different */
173  break;
174  }
175  }
176  return same_loops;
177 }
178 
179 /* Utility stuff for using GHash with polys, used by vertex merging. */
180 
181 typedef struct PolyKey {
182  int poly_index; /* index of the MPoly within the derived mesh */
183  int totloops; /* number of loops in the poly */
184  unsigned int hash_sum; /* Sum of all vertices indices */
185  unsigned int hash_xor; /* Xor of all vertices indices */
187 
188 static unsigned int poly_gset_hash_fn(const void *key)
189 {
190  const PolyKey *pk = key;
191  return pk->hash_sum;
192 }
193 
194 static bool poly_gset_compare_fn(const void *k1, const void *k2)
195 {
196  const PolyKey *pk1 = k1;
197  const PolyKey *pk2 = k2;
198  if ((pk1->hash_sum == pk2->hash_sum) && (pk1->hash_xor == pk2->hash_xor) &&
199  (pk1->totloops == pk2->totloops)) {
200  /* Equality - note that this does not mean equality of polys */
201  return false;
202  }
203 
204  return true;
205 }
206 
240  const int *vtargetmap,
241  const int tot_vtargetmap,
242  const int merge_mode)
243 {
244  /* This was commented out back in 2013, see commit f45d8827bafe6b9eaf9de42f4054e9d84a21955d. */
245  // #define USE_LOOPS
246 
247  Mesh *result = NULL;
248 
249  const int totvert = mesh->totvert;
250  const int totedge = mesh->totedge;
251  const int totloop = mesh->totloop;
252  const int totpoly = mesh->totpoly;
253 
254  const int totvert_final = totvert - tot_vtargetmap;
255 
256  MVert *mv, *mvert = MEM_malloc_arrayN(totvert_final, sizeof(*mvert), __func__);
257  int *oldv = MEM_malloc_arrayN(totvert_final, sizeof(*oldv), __func__);
258  int *newv = MEM_malloc_arrayN(totvert, sizeof(*newv), __func__);
259  STACK_DECLARE(mvert);
260  STACK_DECLARE(oldv);
261 
262  /* Note: create (totedge + totloop) elements because partially invalid polys due to merge may
263  * require generating new edges, and while in 99% cases we'll still end with less final edges
264  * than totedge, cases can be forged that would end requiring more. */
265  MEdge *med, *medge = MEM_malloc_arrayN((totedge + totloop), sizeof(*medge), __func__);
266  int *olde = MEM_malloc_arrayN((totedge + totloop), sizeof(*olde), __func__);
267  int *newe = MEM_malloc_arrayN((totedge + totloop), sizeof(*newe), __func__);
268  STACK_DECLARE(medge);
269  STACK_DECLARE(olde);
270 
271  MLoop *ml, *mloop = MEM_malloc_arrayN(totloop, sizeof(*mloop), __func__);
272  int *oldl = MEM_malloc_arrayN(totloop, sizeof(*oldl), __func__);
273 #ifdef USE_LOOPS
274  int *newl = MEM_malloc_arrayN(totloop, sizeof(*newl), __func__);
275 #endif
276  STACK_DECLARE(mloop);
277  STACK_DECLARE(oldl);
278 
279  MPoly *mp, *mpoly = MEM_malloc_arrayN(totpoly, sizeof(*medge), __func__);
280  int *oldp = MEM_malloc_arrayN(totpoly, sizeof(*oldp), __func__);
281  STACK_DECLARE(mpoly);
282  STACK_DECLARE(oldp);
283 
284  EdgeHash *ehash = BLI_edgehash_new_ex(__func__, totedge);
285 
286  int i, j, c;
287 
288  PolyKey *poly_keys;
289  GSet *poly_gset = NULL;
290  MeshElemMap *poly_map = NULL;
291  int *poly_map_mem = NULL;
292 
293  STACK_INIT(oldv, totvert_final);
294  STACK_INIT(olde, totedge);
295  STACK_INIT(oldl, totloop);
296  STACK_INIT(oldp, totpoly);
297 
298  STACK_INIT(mvert, totvert_final);
299  STACK_INIT(medge, totedge);
300  STACK_INIT(mloop, totloop);
301  STACK_INIT(mpoly, totpoly);
302 
303  /* fill newv with destination vertex indices */
304  mv = mesh->mvert;
305  c = 0;
306  for (i = 0; i < totvert; i++, mv++) {
307  if (vtargetmap[i] == -1) {
308  STACK_PUSH(oldv, i);
309  STACK_PUSH(mvert, *mv);
310  newv[i] = c++;
311  }
312  else {
313  /* dummy value */
314  newv[i] = 0;
315  }
316  }
317 
318  /* now link target vertices to destination indices */
319  for (i = 0; i < totvert; i++) {
320  if (vtargetmap[i] != -1) {
321  newv[i] = newv[vtargetmap[i]];
322  }
323  }
324 
325  /* Don't remap vertices in cddm->mloop, because we need to know the original
326  * indices in order to skip faces with all vertices merged.
327  * The "update loop indices..." section further down remaps vertices in mloop.
328  */
329 
330  /* now go through and fix edges and faces */
331  med = mesh->medge;
332  c = 0;
333  for (i = 0; i < totedge; i++, med++) {
334  const unsigned int v1 = (vtargetmap[med->v1] != -1) ? vtargetmap[med->v1] : med->v1;
335  const unsigned int v2 = (vtargetmap[med->v2] != -1) ? vtargetmap[med->v2] : med->v2;
336  if (LIKELY(v1 != v2)) {
337  void **val_p;
338 
339  if (BLI_edgehash_ensure_p(ehash, v1, v2, &val_p)) {
340  newe[i] = POINTER_AS_INT(*val_p);
341  }
342  else {
343  STACK_PUSH(olde, i);
344  STACK_PUSH(medge, *med);
345  newe[i] = c;
346  *val_p = POINTER_FROM_INT(c);
347  c++;
348  }
349  }
350  else {
351  newe[i] = -1;
352  }
353  }
354 
355  if (merge_mode == MESH_MERGE_VERTS_DUMP_IF_EQUAL) {
356  /* In this mode, we need to determine, whenever a poly' vertices are all mapped */
357  /* if the targets already make up a poly, in which case the new poly is dropped */
358  /* This poly equality check is rather complex.
359  * We use a BLI_ghash to speed it up with a first level check */
360  PolyKey *mpgh;
361  poly_keys = MEM_malloc_arrayN(totpoly, sizeof(PolyKey), __func__);
362  poly_gset = BLI_gset_new_ex(poly_gset_hash_fn, poly_gset_compare_fn, __func__, totpoly);
363  /* Duplicates allowed because our compare function is not pure equality */
365 
366  mp = mesh->mpoly;
367  mpgh = poly_keys;
368  for (i = 0; i < totpoly; i++, mp++, mpgh++) {
369  mpgh->poly_index = i;
370  mpgh->totloops = mp->totloop;
371  ml = mesh->mloop + mp->loopstart;
372  mpgh->hash_sum = mpgh->hash_xor = 0;
373  for (j = 0; j < mp->totloop; j++, ml++) {
374  mpgh->hash_sum += ml->v;
375  mpgh->hash_xor ^= ml->v;
376  }
377  BLI_gset_insert(poly_gset, mpgh);
378  }
379 
380  /* Can we optimize by reusing an old `pmap`? How do we know an old `pmap` is stale? */
381  /* When called by `MOD_array.c` the `cddm` has just been created, so it has no valid `pmap`. */
383  &poly_map, &poly_map_mem, mesh->mpoly, mesh->mloop, totvert, totpoly, totloop);
384  } /* done preparing for fast poly compare */
385 
386  mp = mesh->mpoly;
387  mv = mesh->mvert;
388  for (i = 0; i < totpoly; i++, mp++) {
389  MPoly *mp_new;
390 
391  ml = mesh->mloop + mp->loopstart;
392 
393  /* check faces with all vertices merged */
394  bool all_vertices_merged = true;
395 
396  for (j = 0; j < mp->totloop; j++, ml++) {
397  if (vtargetmap[ml->v] == -1) {
398  all_vertices_merged = false;
399  /* This will be used to check for poly using several time the same vert. */
400  mv[ml->v].flag &= ~ME_VERT_TMP_TAG;
401  }
402  else {
403  /* This will be used to check for poly using several time the same vert. */
404  mv[vtargetmap[ml->v]].flag &= ~ME_VERT_TMP_TAG;
405  }
406  }
407 
408  if (UNLIKELY(all_vertices_merged)) {
409  if (merge_mode == MESH_MERGE_VERTS_DUMP_IF_MAPPED) {
410  /* In this mode, all vertices merged is enough to dump face */
411  continue;
412  }
413  if (merge_mode == MESH_MERGE_VERTS_DUMP_IF_EQUAL) {
414  /* Additional condition for face dump: target vertices must make up an identical face.
415  * The test has 2 steps:
416  * 1) first step is fast `ghash` lookup, but not fail-proof.
417  * 2) second step is thorough but more costly poly compare. */
418  int i_poly, v_target;
419  bool found = false;
420  PolyKey pkey;
421 
422  /* Use poly_gset for fast (although not 100% certain) identification of same poly */
423  /* First, make up a poly_summary structure */
424  ml = mesh->mloop + mp->loopstart;
425  pkey.hash_sum = pkey.hash_xor = 0;
426  pkey.totloops = 0;
427  for (j = 0; j < mp->totloop; j++, ml++) {
428  v_target = vtargetmap[ml->v]; /* Cannot be -1, they are all mapped */
429  pkey.hash_sum += v_target;
430  pkey.hash_xor ^= v_target;
431  pkey.totloops++;
432  }
433  if (BLI_gset_haskey(poly_gset, &pkey)) {
434 
435  /* There might be a poly that matches this one.
436  * We could just leave it there and say there is, and do a "continue".
437  * ... but we are checking whether there is an exact poly match.
438  * It's not so costly in terms of CPU since it's very rare, just a lot of complex code.
439  */
440 
441  /* Consider current loop again */
442  ml = mesh->mloop + mp->loopstart;
443  /* Consider the target of the loop's first vert */
444  v_target = vtargetmap[ml->v];
445  /* Now see if v_target belongs to a poly that shares all vertices with source poly,
446  * in same order, or reverse order */
447 
448  for (i_poly = 0; i_poly < poly_map[v_target].count; i_poly++) {
449  MPoly *target_poly = mesh->mpoly + *(poly_map[v_target].indices + i_poly);
450 
451  if (cddm_poly_compare(mesh->mloop, mp, target_poly, vtargetmap, +1) ||
452  cddm_poly_compare(mesh->mloop, mp, target_poly, vtargetmap, -1)) {
453  found = true;
454  break;
455  }
456  }
457  if (found) {
458  /* Current poly's vertices are mapped to a poly that is strictly identical */
459  /* Current poly is dumped */
460  continue;
461  }
462  }
463  }
464  }
465 
466  /* Here either the poly's vertices were not all merged
467  * or they were all merged, but targets do not make up an identical poly,
468  * the poly is retained.
469  */
470  ml = mesh->mloop + mp->loopstart;
471 
472  c = 0;
473  MLoop *last_valid_ml = NULL;
474  MLoop *first_valid_ml = NULL;
475  bool need_edge_from_last_valid_ml = false;
476  bool need_edge_to_first_valid_ml = false;
477  int created_edges = 0;
478  for (j = 0; j < mp->totloop; j++, ml++) {
479  const uint mlv = (vtargetmap[ml->v] != -1) ? vtargetmap[ml->v] : ml->v;
480 #ifndef NDEBUG
481  {
482  MLoop *next_ml = mesh->mloop + mp->loopstart + ((j + 1) % mp->totloop);
483  uint next_mlv = (vtargetmap[next_ml->v] != -1) ? vtargetmap[next_ml->v] : next_ml->v;
484  med = mesh->medge + ml->e;
485  uint v1 = (vtargetmap[med->v1] != -1) ? vtargetmap[med->v1] : med->v1;
486  uint v2 = (vtargetmap[med->v2] != -1) ? vtargetmap[med->v2] : med->v2;
487  BLI_assert((mlv == v1 && next_mlv == v2) || (mlv == v2 && next_mlv == v1));
488  }
489 #endif
490  /* A loop is only valid if its matching edge is,
491  * and it's not reusing a vertex already used by this poly. */
492  if (LIKELY((newe[ml->e] != -1) && ((mv[mlv].flag & ME_VERT_TMP_TAG) == 0))) {
493  mv[mlv].flag |= ME_VERT_TMP_TAG;
494 
495  if (UNLIKELY(last_valid_ml != NULL && need_edge_from_last_valid_ml)) {
496  /* We need to create a new edge between last valid loop and this one! */
497  void **val_p;
498 
499  uint v1 = (vtargetmap[last_valid_ml->v] != -1) ? vtargetmap[last_valid_ml->v] :
500  last_valid_ml->v;
501  uint v2 = mlv;
502  BLI_assert(v1 != v2);
503  if (BLI_edgehash_ensure_p(ehash, v1, v2, &val_p)) {
504  last_valid_ml->e = POINTER_AS_INT(*val_p);
505  }
506  else {
507  const int new_eidx = STACK_SIZE(medge);
508  STACK_PUSH(olde, olde[last_valid_ml->e]);
509  STACK_PUSH(medge, mesh->medge[last_valid_ml->e]);
510  medge[new_eidx].v1 = last_valid_ml->v;
511  medge[new_eidx].v2 = ml->v;
512  /* DO NOT change newe mapping,
513  * could break actual values due to some deleted original edges. */
514  *val_p = POINTER_FROM_INT(new_eidx);
515  created_edges++;
516 
517  last_valid_ml->e = new_eidx;
518  }
519  need_edge_from_last_valid_ml = false;
520  }
521 
522 #ifdef USE_LOOPS
523  newl[j + mp->loopstart] = STACK_SIZE(mloop);
524 #endif
525  STACK_PUSH(oldl, j + mp->loopstart);
526  last_valid_ml = STACK_PUSH_RET_PTR(mloop);
527  *last_valid_ml = *ml;
528  if (first_valid_ml == NULL) {
529  first_valid_ml = last_valid_ml;
530  }
531  c++;
532 
533  /* We absolutely HAVE to handle edge index remapping here, otherwise potential newly
534  * created edges in that part of code make remapping later totally unreliable. */
535  BLI_assert(newe[ml->e] != -1);
536  last_valid_ml->e = newe[ml->e];
537  }
538  else {
539  if (last_valid_ml != NULL) {
540  need_edge_from_last_valid_ml = true;
541  }
542  else {
543  need_edge_to_first_valid_ml = true;
544  }
545  }
546  }
547  if (UNLIKELY(last_valid_ml != NULL && !ELEM(first_valid_ml, NULL, last_valid_ml) &&
548  (need_edge_to_first_valid_ml || need_edge_from_last_valid_ml))) {
549  /* We need to create a new edge between last valid loop and first valid one! */
550  void **val_p;
551 
552  uint v1 = (vtargetmap[last_valid_ml->v] != -1) ? vtargetmap[last_valid_ml->v] :
553  last_valid_ml->v;
554  uint v2 = (vtargetmap[first_valid_ml->v] != -1) ? vtargetmap[first_valid_ml->v] :
555  first_valid_ml->v;
556  BLI_assert(v1 != v2);
557  if (BLI_edgehash_ensure_p(ehash, v1, v2, &val_p)) {
558  last_valid_ml->e = POINTER_AS_INT(*val_p);
559  }
560  else {
561  const int new_eidx = STACK_SIZE(medge);
562  STACK_PUSH(olde, olde[last_valid_ml->e]);
563  STACK_PUSH(medge, mesh->medge[last_valid_ml->e]);
564  medge[new_eidx].v1 = last_valid_ml->v;
565  medge[new_eidx].v2 = first_valid_ml->v;
566  /* DO NOT change newe mapping,
567  * could break actual values due to some deleted original edges. */
568  *val_p = POINTER_FROM_INT(new_eidx);
569  created_edges++;
570 
571  last_valid_ml->e = new_eidx;
572  }
573  need_edge_to_first_valid_ml = need_edge_from_last_valid_ml = false;
574  }
575 
576  if (UNLIKELY(c == 0)) {
577  BLI_assert(created_edges == 0);
578  continue;
579  }
580  if (UNLIKELY(c < 3)) {
581  STACK_DISCARD(oldl, c);
582  STACK_DISCARD(mloop, c);
583  if (created_edges > 0) {
584  for (j = STACK_SIZE(medge) - created_edges; j < STACK_SIZE(medge); j++) {
585  BLI_edgehash_remove(ehash, medge[j].v1, medge[j].v2, NULL);
586  }
587  STACK_DISCARD(olde, created_edges);
588  STACK_DISCARD(medge, created_edges);
589  }
590  continue;
591  }
592 
593  mp_new = STACK_PUSH_RET_PTR(mpoly);
594  *mp_new = *mp;
595  mp_new->totloop = c;
596  BLI_assert(mp_new->totloop >= 3);
597  mp_new->loopstart = STACK_SIZE(mloop) - c;
598 
599  STACK_PUSH(oldp, i);
600  } /* end of the loop that tests polys */
601 
602  if (poly_gset) {
603  // printf("hash quality %.6f\n", BLI_gset_calc_quality(poly_gset));
604 
605  BLI_gset_free(poly_gset, NULL);
606  MEM_freeN(poly_keys);
607  }
608 
609  /*create new cddm*/
611  mesh, STACK_SIZE(mvert), STACK_SIZE(medge), 0, STACK_SIZE(mloop), STACK_SIZE(mpoly));
612 
613  /*update edge indices and copy customdata*/
614  med = medge;
615  for (i = 0; i < result->totedge; i++, med++) {
616  BLI_assert(newv[med->v1] != -1);
617  med->v1 = newv[med->v1];
618  BLI_assert(newv[med->v2] != -1);
619  med->v2 = newv[med->v2];
620 
621  /* Can happen in case vtargetmap contains some double chains, we do not support that. */
622  BLI_assert(med->v1 != med->v2);
623 
624  CustomData_copy_data(&mesh->edata, &result->edata, olde[i], i, 1);
625  }
626 
627  /*update loop indices and copy customdata*/
628  ml = mloop;
629  for (i = 0; i < result->totloop; i++, ml++) {
630  /* Edge remapping has already be done in main loop handling part above. */
631  BLI_assert(newv[ml->v] != -1);
632  ml->v = newv[ml->v];
633 
634  CustomData_copy_data(&mesh->ldata, &result->ldata, oldl[i], i, 1);
635  }
636 
637  /*copy vertex customdata*/
638  mv = mvert;
639  for (i = 0; i < result->totvert; i++, mv++) {
640  CustomData_copy_data(&mesh->vdata, &result->vdata, oldv[i], i, 1);
641  }
642 
643  /*copy poly customdata*/
644  mp = mpoly;
645  for (i = 0; i < result->totpoly; i++, mp++) {
646  CustomData_copy_data(&mesh->pdata, &result->pdata, oldp[i], i, 1);
647  }
648 
649  /*copy over data. CustomData_add_layer can do this, need to look it up.*/
650  memcpy(result->mvert, mvert, sizeof(MVert) * STACK_SIZE(mvert));
651  memcpy(result->medge, medge, sizeof(MEdge) * STACK_SIZE(medge));
652  memcpy(result->mloop, mloop, sizeof(MLoop) * STACK_SIZE(mloop));
653  memcpy(result->mpoly, mpoly, sizeof(MPoly) * STACK_SIZE(mpoly));
654 
655  MEM_freeN(mvert);
656  MEM_freeN(medge);
657  MEM_freeN(mloop);
658  MEM_freeN(mpoly);
659 
660  MEM_freeN(newv);
661  MEM_freeN(newe);
662 #ifdef USE_LOOPS
663  MEM_freeN(newl);
664 #endif
665 
666  MEM_freeN(oldv);
667  MEM_freeN(olde);
668  MEM_freeN(oldl);
669  MEM_freeN(oldp);
670 
671  BLI_edgehash_free(ehash, NULL);
672 
673  if (poly_map != NULL) {
674  MEM_freeN(poly_map);
675  }
676  if (poly_map_mem != NULL) {
677  MEM_freeN(poly_map_mem);
678  }
679 
681 
682  return result;
683 }
CustomData interface, see also DNA_customdata_types.h.
void CustomData_copy_data(const struct CustomData *source, struct CustomData *dest, int source_index, int dest_index, int count)
void BKE_id_free(struct Main *bmain, void *idv)
struct Mesh * BKE_mesh_new_nomain_from_template(const struct Mesh *me_src, int verts_len, int edges_len, int tessface_len, int loops_len, int polys_len)
@ MESH_MERGE_VERTS_DUMP_IF_EQUAL
Definition: BKE_mesh.h:586
@ MESH_MERGE_VERTS_DUMP_IF_MAPPED
Definition: BKE_mesh.h:585
void BKE_mesh_vert_poly_map_create(MeshElemMap **r_map, int **r_mem, const struct MPoly *mpoly, const struct MLoop *mloop, int totvert, int totpoly, int totloop)
#define BLI_assert(a)
Definition: BLI_assert.h:58
void BLI_edgehash_free(EdgeHash *eh, EdgeHashFreeFP free_value)
Definition: edgehash.c:244
EdgeHash * BLI_edgehash_new_ex(const char *info, const unsigned int nentries_reserve)
Definition: edgehash.c:226
bool BLI_edgehash_ensure_p(EdgeHash *eh, unsigned int v0, unsigned int v1, void ***r_val) ATTR_WARN_UNUSED_RESULT
Definition: edgehash.c:355
bool BLI_edgehash_remove(EdgeHash *eh, unsigned int v0, unsigned int v1, EdgeHashFreeFP free_value)
Definition: edgehash.c:383
struct GSet GSet
Definition: BLI_ghash.h:189
@ GHASH_FLAG_ALLOW_DUPES
Definition: BLI_ghash.h:67
void BLI_gset_flag_set(GSet *gs, unsigned int flag)
Definition: BLI_ghash.c:1258
void BLI_gset_insert(GSet *gs, void *key)
Definition: BLI_ghash.c:1147
GSet * BLI_gset_new_ex(GSetHashFP hashfp, GSetCmpFP cmpfp, const char *info, const unsigned int nentries_reserve) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.c:1117
bool BLI_gset_haskey(GSet *gs, const void *key) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.c:1216
void BLI_gset_free(GSet *gs, GSetKeyFreeFP keyfreefp)
Definition: BLI_ghash.c:1253
unsigned int uint
Definition: BLI_sys_types.h:83
#define POINTER_FROM_INT(i)
#define POINTER_AS_INT(i)
#define UNLIKELY(x)
#define ELEM(...)
#define LIKELY(x)
#define STACK_DISCARD(stack, n)
#define STACK_PUSH(stack, val)
#define STACK_DECLARE(stack)
#define STACK_INIT(stack, tot)
#define STACK_SIZE(stack)
#define STACK_PUSH_RET_PTR(stack)
@ ME_VERT_TMP_TAG
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble v1
Read Guarded memory(de)allocation.
ATTR_WARN_UNUSED_RESULT const BMVert * v2
void *(* MEM_malloc_arrayN)(size_t len, size_t size, const char *str)
Definition: mallocn.c:48
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
Mesh * BKE_mesh_merge_verts(Mesh *mesh, const int *vtargetmap, const int tot_vtargetmap, const int merge_mode)
Definition: mesh_merge.c:239
struct PolyKey PolyKey
static bool poly_gset_compare_fn(const void *k1, const void *k2)
Definition: mesh_merge.c:194
static int cddm_poly_compare(MLoop *mloop_array, MPoly *mpoly_source, MPoly *mpoly_target, const int *vtargetmap, const int direct_reverse)
Definition: mesh_merge.c:50
static unsigned int poly_gset_hash_fn(const void *key)
Definition: mesh_merge.c:188
static unsigned c
Definition: RandGen.cpp:97
unsigned int v1
unsigned int v2
unsigned int e
unsigned int v
struct MEdge * medge
struct CustomData pdata ldata
struct MVert * mvert
int totedge
int totvert
struct MLoop * mloop
int totpoly
int totloop
struct MPoly * mpoly
int poly_index
Definition: mesh_merge.c:182
unsigned int hash_xor
Definition: mesh_merge.c:185
unsigned int hash_sum
Definition: mesh_merge.c:184
int totloops
Definition: mesh_merge.c:183