Blender  V3.3
mesh_validate.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2011 Blender Foundation. All rights reserved. */
3 
8 #include <climits>
9 #include <cstdio>
10 #include <cstdlib>
11 #include <cstring>
12 
13 #include "CLG_log.h"
14 
15 #include "BLI_bitmap.h"
16 #include "DNA_mesh_types.h"
17 #include "DNA_meshdata_types.h"
18 #include "DNA_object_types.h"
19 
20 #include "BLI_sys_types.h"
21 
22 #include "BLI_edgehash.h"
23 #include "BLI_math_base.h"
24 #include "BLI_math_vector.h"
25 #include "BLI_utildefines.h"
26 
27 #include "BKE_customdata.h"
28 #include "BKE_deform.h"
29 #include "BKE_mesh.h"
30 
31 #include "DEG_depsgraph.h"
32 
33 #include "MEM_guardedalloc.h"
34 
35 /* loop v/e are unsigned, so using max uint_32 value as invalid marker... */
36 #define INVALID_LOOP_EDGE_MARKER 4294967295u
37 
38 static CLG_LogRef LOG = {"bke.mesh"};
39 
40 /* -------------------------------------------------------------------- */
44 union EdgeUUID {
47 };
48 
49 struct SortFace {
52 };
53 
54 /* Used to detect polys (faces) using exactly the same vertices. */
55 /* Used to detect loops used by no (disjoint) or more than one (intersect) polys. */
56 struct SortPoly {
57  int *verts;
58  int numverts;
59  int loopstart;
61  bool invalid; /* Poly index. */
62 };
63 
64 static void edge_store_assign(uint32_t verts[2], const uint32_t v1, const uint32_t v2)
65 {
66  if (v1 < v2) {
67  verts[0] = v1;
68  verts[1] = v2;
69  }
70  else {
71  verts[0] = v2;
72  verts[1] = v1;
73  }
74 }
75 
76 static void edge_store_from_mface_quad(EdgeUUID es[4], MFace *mf)
77 {
78  edge_store_assign(es[0].verts, mf->v1, mf->v2);
79  edge_store_assign(es[1].verts, mf->v2, mf->v3);
80  edge_store_assign(es[2].verts, mf->v3, mf->v4);
81  edge_store_assign(es[3].verts, mf->v4, mf->v1);
82 }
83 
84 static void edge_store_from_mface_tri(EdgeUUID es[4], MFace *mf)
85 {
86  edge_store_assign(es[0].verts, mf->v1, mf->v2);
87  edge_store_assign(es[1].verts, mf->v2, mf->v3);
88  edge_store_assign(es[2].verts, mf->v3, mf->v1);
89  es[3].verts[0] = es[3].verts[1] = UINT_MAX;
90 }
91 
92 static int int64_cmp(const void *v1, const void *v2)
93 {
94  const int64_t x1 = *(const int64_t *)v1;
95  const int64_t x2 = *(const int64_t *)v2;
96 
97  if (x1 > x2) {
98  return 1;
99  }
100  if (x1 < x2) {
101  return -1;
102  }
103 
104  return 0;
105 }
106 
107 static int search_face_cmp(const void *v1, const void *v2)
108 {
109  const SortFace *sfa = static_cast<const SortFace *>(v1);
110  const SortFace *sfb = static_cast<const SortFace *>(v2);
111 
112  if (sfa->es[0].edval > sfb->es[0].edval) {
113  return 1;
114  }
115  if (sfa->es[0].edval < sfb->es[0].edval) {
116  return -1;
117  }
118 
119  if (sfa->es[1].edval > sfb->es[1].edval) {
120  return 1;
121  }
122  if (sfa->es[1].edval < sfb->es[1].edval) {
123  return -1;
124  }
125 
126  if (sfa->es[2].edval > sfb->es[2].edval) {
127  return 1;
128  }
129  if (sfa->es[2].edval < sfb->es[2].edval) {
130  return -1;
131  }
132 
133  if (sfa->es[3].edval > sfb->es[3].edval) {
134  return 1;
135  }
136  if (sfa->es[3].edval < sfb->es[3].edval) {
137  return -1;
138  }
139 
140  return 0;
141 }
142 
143 /* TODO: check there is not some standard define of this somewhere! */
144 static int int_cmp(const void *v1, const void *v2)
145 {
146  return *(int *)v1 > *(int *)v2 ? 1 : *(int *)v1 < *(int *)v2 ? -1 : 0;
147 }
148 
149 static int search_poly_cmp(const void *v1, const void *v2)
150 {
151  const SortPoly *sp1 = static_cast<const SortPoly *>(v1);
152  const SortPoly *sp2 = static_cast<const SortPoly *>(v2);
153 
154  /* Reject all invalid polys at end of list! */
155  if (sp1->invalid || sp2->invalid) {
156  return sp1->invalid ? (sp2->invalid ? 0 : 1) : -1;
157  }
158  /* Else, sort on first non-equal verts (remember verts of valid polys are sorted). */
159  const int max_idx = sp1->numverts > sp2->numverts ? sp2->numverts : sp1->numverts;
160  for (int idx = 0; idx < max_idx; idx++) {
161  const int v1_i = sp1->verts[idx];
162  const int v2_i = sp2->verts[idx];
163  if (v1_i != v2_i) {
164  return (v1_i > v2_i) ? 1 : -1;
165  }
166  }
167  return sp1->numverts > sp2->numverts ? 1 : sp1->numverts < sp2->numverts ? -1 : 0;
168 }
169 
170 static int search_polyloop_cmp(const void *v1, const void *v2)
171 {
172  const SortPoly *sp1 = static_cast<const SortPoly *>(v1);
173  const SortPoly *sp2 = static_cast<const SortPoly *>(v2);
174 
175  /* Reject all invalid polys at end of list! */
176  if (sp1->invalid || sp2->invalid) {
177  return sp1->invalid && sp2->invalid ? 0 : sp1->invalid ? 1 : -1;
178  }
179  /* Else, sort on loopstart. */
180  return sp1->loopstart > sp2->loopstart ? 1 : sp1->loopstart < sp2->loopstart ? -1 : 0;
181 }
182 
185 /* -------------------------------------------------------------------- */
189 #define PRINT_MSG(...) \
190  if (do_verbose) { \
191  CLOG_INFO(&LOG, 1, __VA_ARGS__); \
192  } \
193  ((void)0)
194 
195 #define PRINT_ERR(...) \
196  do { \
197  is_valid = false; \
198  if (do_verbose) { \
199  CLOG_ERROR(&LOG, __VA_ARGS__); \
200  } \
201  } while (0)
202 
203 /* NOLINTNEXTLINE: readability-function-size */
205  MVert *mverts,
206  uint totvert,
207  MEdge *medges,
208  uint totedge,
209  MFace *mfaces,
210  uint totface,
211  MLoop *mloops,
212  uint totloop,
213  MPoly *mpolys,
214  uint totpoly,
215  MDeformVert *dverts, /* assume totvert length */
216  const bool do_verbose,
217  const bool do_fixes,
218  bool *r_changed)
219 {
220 #define REMOVE_EDGE_TAG(_me) \
221  { \
222  _me->v2 = _me->v1; \
223  free_flag.edges = do_fixes; \
224  } \
225  (void)0
226 #define IS_REMOVED_EDGE(_me) (_me->v2 == _me->v1)
227 
228 #define REMOVE_LOOP_TAG(_ml) \
229  { \
230  _ml->e = INVALID_LOOP_EDGE_MARKER; \
231  free_flag.polyloops = do_fixes; \
232  } \
233  (void)0
234 #define REMOVE_POLY_TAG(_mp) \
235  { \
236  _mp->totloop *= -1; \
237  free_flag.polyloops = do_fixes; \
238  } \
239  (void)0
240 
241  MVert *mv = mverts;
242  MEdge *me;
243  MLoop *ml;
244  MPoly *mp;
245  uint i, j;
246  int *v;
247 
248  bool is_valid = true;
249 
250  union {
251  struct {
252  int verts : 1;
253  int verts_weight : 1;
254  int loops_edge : 1;
255  };
256  int as_flag;
257  } fix_flag;
258 
259  union {
260  struct {
261  int edges : 1;
262  int faces : 1;
263  /* This regroups loops and polys! */
264  int polyloops : 1;
265  int mselect : 1;
266  };
267  int as_flag;
268  } free_flag;
269 
270  union {
271  struct {
272  int edges : 1;
273  };
274  int as_flag;
275  } recalc_flag;
276 
277  EdgeHash *edge_hash = BLI_edgehash_new_ex(__func__, totedge);
278 
279  BLI_assert(!(do_fixes && mesh == nullptr));
280 
281  fix_flag.as_flag = 0;
282  free_flag.as_flag = 0;
283  recalc_flag.as_flag = 0;
284 
285  PRINT_MSG("verts(%u), edges(%u), loops(%u), polygons(%u)", totvert, totedge, totloop, totpoly);
286 
287  if (totedge == 0 && totpoly != 0) {
288  PRINT_ERR("\tLogical error, %u polygons and 0 edges", totpoly);
289  recalc_flag.edges = do_fixes;
290  }
291 
292  const float(*vert_normals)[3] = nullptr;
295  vert_normals = BKE_mesh_vertex_normals_ensure(mesh);
296  }
297 
298  for (i = 0; i < totvert; i++, mv++) {
299  bool fix_normal = true;
300 
301  for (j = 0; j < 3; j++) {
302  if (!isfinite(mv->co[j])) {
303  PRINT_ERR("\tVertex %u: has invalid coordinate", i);
304 
305  if (do_fixes) {
306  zero_v3(mv->co);
307 
308  fix_flag.verts = true;
309  }
310  }
311 
312  if (vert_normals && vert_normals[i][j] != 0.0f) {
313  fix_normal = false;
314  break;
315  }
316  }
317 
318  if (vert_normals && fix_normal) {
319  /* If the vertex normal accumulates to zero or isn't part of a face, the location is used.
320  * When the location is also zero, a zero normal warning should not be raised.
321  * since this is the expected behavior of normal calculation.
322  *
323  * This avoids false positives but isn't foolproof as it's possible the vertex
324  * is part of a polygon that has a normal which this vertex should be using,
325  * although it's also possible degenerate/opposite faces accumulate to a zero vector.
326  * To detect this a full normal recalculation would be needed, which is out of scope
327  * for a basic validity check (see "Vertex Normal" in the doc-string). */
328  if (!is_zero_v3(mv->co)) {
329  PRINT_ERR("\tVertex %u: has zero normal, assuming Z-up normal", i);
330  if (do_fixes) {
331  float *normal = (float *)vert_normals[i];
332  normal[2] = 1.0f;
333  fix_flag.verts = true;
334  }
335  }
336  }
337  }
338 
339  for (i = 0, me = medges; i < totedge; i++, me++) {
340  bool remove = false;
341 
342  if (me->v1 == me->v2) {
343  PRINT_ERR("\tEdge %u: has matching verts, both %u", i, me->v1);
344  remove = do_fixes;
345  }
346  if (me->v1 >= totvert) {
347  PRINT_ERR("\tEdge %u: v1 index out of range, %u", i, me->v1);
348  remove = do_fixes;
349  }
350  if (me->v2 >= totvert) {
351  PRINT_ERR("\tEdge %u: v2 index out of range, %u", i, me->v2);
352  remove = do_fixes;
353  }
354 
355  if ((me->v1 != me->v2) && BLI_edgehash_haskey(edge_hash, me->v1, me->v2)) {
356  PRINT_ERR("\tEdge %u: is a duplicate of %d",
357  i,
358  POINTER_AS_INT(BLI_edgehash_lookup(edge_hash, me->v1, me->v2)));
359  remove = do_fixes;
360  }
361 
362  if (remove == false) {
363  if (me->v1 != me->v2) {
364  BLI_edgehash_insert(edge_hash, me->v1, me->v2, POINTER_FROM_INT(i));
365  }
366  }
367  else {
368  REMOVE_EDGE_TAG(me);
369  }
370  }
371 
372  if (mfaces && !mpolys) {
373 #define REMOVE_FACE_TAG(_mf) \
374  { \
375  _mf->v3 = 0; \
376  free_flag.faces = do_fixes; \
377  } \
378  (void)0
379 #define CHECK_FACE_VERT_INDEX(a, b) \
380  if (mf->a == mf->b) { \
381  PRINT_ERR(" face %u: verts invalid, " STRINGIFY(a) "/" STRINGIFY(b) " both %u", i, mf->a); \
382  remove = do_fixes; \
383  } \
384  (void)0
385 #define CHECK_FACE_EDGE(a, b) \
386  if (!BLI_edgehash_haskey(edge_hash, mf->a, mf->b)) { \
387  PRINT_ERR(" face %u: edge " STRINGIFY(a) "/" STRINGIFY(b) " (%u,%u) is missing edge data", \
388  i, \
389  mf->a, \
390  mf->b); \
391  recalc_flag.edges = do_fixes; \
392  } \
393  (void)0
394 
395  MFace *mf;
396  MFace *mf_prev;
397 
398  SortFace *sort_faces = (SortFace *)MEM_callocN(sizeof(SortFace) * totface, "search faces");
399  SortFace *sf;
400  SortFace *sf_prev;
401  uint totsortface = 0;
402 
403  PRINT_ERR("No Polys, only tessellated Faces");
404 
405  for (i = 0, mf = mfaces, sf = sort_faces; i < totface; i++, mf++) {
406  bool remove = false;
407  int fidx;
408  uint fv[4];
409 
410  fidx = mf->v4 ? 3 : 2;
411  do {
412  fv[fidx] = *(&(mf->v1) + fidx);
413  if (fv[fidx] >= totvert) {
414  PRINT_ERR("\tFace %u: 'v%d' index out of range, %u", i, fidx + 1, fv[fidx]);
415  remove = do_fixes;
416  }
417  } while (fidx--);
418 
419  if (remove == false) {
420  if (mf->v4) {
424 
427 
428  CHECK_FACE_VERT_INDEX(v3, v4);
429  }
430  else {
433 
435  }
436 
437  if (remove == false) {
438  if (totedge) {
439  if (mf->v4) {
441  CHECK_FACE_EDGE(v2, v3);
442  CHECK_FACE_EDGE(v3, v4);
443  CHECK_FACE_EDGE(v4, v1);
444  }
445  else {
447  CHECK_FACE_EDGE(v2, v3);
448  CHECK_FACE_EDGE(v3, v1);
449  }
450  }
451 
452  sf->index = i;
453 
454  if (mf->v4) {
456 
457  qsort(sf->es, 4, sizeof(int64_t), int64_cmp);
458  }
459  else {
460  edge_store_from_mface_tri(sf->es, mf);
461  qsort(sf->es, 3, sizeof(int64_t), int64_cmp);
462  }
463 
464  totsortface++;
465  sf++;
466  }
467  }
468 
469  if (remove) {
470  REMOVE_FACE_TAG(mf);
471  }
472  }
473 
474  qsort(sort_faces, totsortface, sizeof(SortFace), search_face_cmp);
475 
476  sf = sort_faces;
477  sf_prev = sf;
478  sf++;
479 
480  for (i = 1; i < totsortface; i++, sf++) {
481  bool remove = false;
482 
483  /* on a valid mesh, code below will never run */
484  if (memcmp(sf->es, sf_prev->es, sizeof(sf_prev->es)) == 0) {
485  mf = mfaces + sf->index;
486 
487  if (do_verbose) {
488  mf_prev = mfaces + sf_prev->index;
489 
490  if (mf->v4) {
491  PRINT_ERR("\tFace %u & %u: are duplicates (%u,%u,%u,%u) (%u,%u,%u,%u)",
492  sf->index,
493  sf_prev->index,
494  mf->v1,
495  mf->v2,
496  mf->v3,
497  mf->v4,
498  mf_prev->v1,
499  mf_prev->v2,
500  mf_prev->v3,
501  mf_prev->v4);
502  }
503  else {
504  PRINT_ERR("\tFace %u & %u: are duplicates (%u,%u,%u) (%u,%u,%u)",
505  sf->index,
506  sf_prev->index,
507  mf->v1,
508  mf->v2,
509  mf->v3,
510  mf_prev->v1,
511  mf_prev->v2,
512  mf_prev->v3);
513  }
514  }
515 
516  remove = do_fixes;
517  }
518  else {
519  sf_prev = sf;
520  }
521 
522  if (remove) {
523  REMOVE_FACE_TAG(mf);
524  }
525  }
526 
527  MEM_freeN(sort_faces);
528 
529 #undef REMOVE_FACE_TAG
530 #undef CHECK_FACE_VERT_INDEX
531 #undef CHECK_FACE_EDGE
532  }
533 
534  /* Checking loops and polys is a bit tricky, as they are quite intricate...
535  *
536  * Polys must have:
537  * - a valid loopstart value.
538  * - a valid totloop value (>= 3 and loopstart+totloop < me.totloop).
539  *
540  * Loops must have:
541  * - a valid v value.
542  * - a valid e value (corresponding to the edge it defines with the next loop in poly).
543  *
544  * Also, loops not used by polys can be discarded.
545  * And "intersecting" loops (i.e. loops used by more than one poly) are invalid,
546  * so be sure to leave at most one poly per loop!
547  */
548  {
549  BLI_bitmap *vert_tag = BLI_BITMAP_NEW(mesh->totvert, __func__);
550 
551  SortPoly *sort_polys = (SortPoly *)MEM_callocN(sizeof(SortPoly) * totpoly,
552  "mesh validate's sort_polys");
553  SortPoly *prev_sp, *sp = sort_polys;
554  int prev_end;
555 
556  for (i = 0, mp = mpolys; i < totpoly; i++, mp++, sp++) {
557  sp->index = i;
558 
559  /* Material index, isolated from other tests here. While large indices are clamped,
560  * negative indices aren't supported by drawing, exporters etc.
561  * To check the indices are in range, use #BKE_mesh_validate_material_indices */
562  if (mp->mat_nr < 0) {
563  PRINT_ERR("\tPoly %u has invalid material (%d)", sp->index, mp->mat_nr);
564  if (do_fixes) {
565  mp->mat_nr = 0;
566  }
567  }
568 
569  if (mp->loopstart < 0 || mp->totloop < 3) {
570  /* Invalid loop data. */
571  PRINT_ERR("\tPoly %u is invalid (loopstart: %d, totloop: %d)",
572  sp->index,
573  mp->loopstart,
574  mp->totloop);
575  sp->invalid = true;
576  }
577  else if (mp->loopstart + mp->totloop > totloop) {
578  /* Invalid loop data. */
579  PRINT_ERR(
580  "\tPoly %u uses loops out of range "
581  "(loopstart: %d, loopend: %d, max number of loops: %u)",
582  sp->index,
583  mp->loopstart,
584  mp->loopstart + mp->totloop - 1,
585  totloop - 1);
586  sp->invalid = true;
587  }
588  else {
589  /* Poly itself is valid, for now. */
590  int v1, v2; /* v1 is prev loop vert idx, v2 is current loop one. */
591  sp->invalid = false;
592  sp->verts = v = (int *)MEM_mallocN(sizeof(int) * mp->totloop, "Vert idx of SortPoly");
593  sp->numverts = mp->totloop;
594  sp->loopstart = mp->loopstart;
595 
596  /* Ideally we would only have to do that once on all vertices
597  * before we start checking each poly, but several polys can use same vert,
598  * so we have to ensure here all verts of current poly are cleared. */
599  for (j = 0, ml = &mloops[sp->loopstart]; j < mp->totloop; j++, ml++) {
600  if (ml->v < totvert) {
601  BLI_BITMAP_DISABLE(vert_tag, ml->v);
602  }
603  }
604 
605  /* Test all poly's loops' vert idx. */
606  for (j = 0, ml = &mloops[sp->loopstart]; j < mp->totloop; j++, ml++, v++) {
607  if (ml->v >= totvert) {
608  /* Invalid vert idx. */
609  PRINT_ERR("\tLoop %u has invalid vert reference (%u)", sp->loopstart + j, ml->v);
610  sp->invalid = true;
611  }
612  else if (BLI_BITMAP_TEST(vert_tag, ml->v)) {
613  PRINT_ERR("\tPoly %u has duplicated vert reference at corner (%u)", i, j);
614  sp->invalid = true;
615  }
616  else {
617  BLI_BITMAP_ENABLE(vert_tag, ml->v);
618  }
619  *v = ml->v;
620  }
621 
622  if (sp->invalid) {
623  continue;
624  }
625 
626  /* Test all poly's loops. */
627  for (j = 0, ml = &mloops[sp->loopstart]; j < mp->totloop; j++, ml++) {
628  v1 = ml->v;
629  v2 = mloops[sp->loopstart + (j + 1) % mp->totloop].v;
630  if (!BLI_edgehash_haskey(edge_hash, v1, v2)) {
631  /* Edge not existing. */
632  PRINT_ERR("\tPoly %u needs missing edge (%d, %d)", sp->index, v1, v2);
633  if (do_fixes) {
634  recalc_flag.edges = true;
635  }
636  else {
637  sp->invalid = true;
638  }
639  }
640  else if (ml->e >= totedge) {
641  /* Invalid edge idx.
642  * We already know from previous text that a valid edge exists, use it (if allowed)! */
643  if (do_fixes) {
644  int prev_e = ml->e;
645  ml->e = POINTER_AS_INT(BLI_edgehash_lookup(edge_hash, v1, v2));
646  fix_flag.loops_edge = true;
647  PRINT_ERR("\tLoop %u has invalid edge reference (%d), fixed using edge %u",
648  sp->loopstart + j,
649  prev_e,
650  ml->e);
651  }
652  else {
653  PRINT_ERR("\tLoop %u has invalid edge reference (%u)", sp->loopstart + j, ml->e);
654  sp->invalid = true;
655  }
656  }
657  else {
658  me = &medges[ml->e];
659  if (IS_REMOVED_EDGE(me) ||
660  !((me->v1 == v1 && me->v2 == v2) || (me->v1 == v2 && me->v2 == v1))) {
661  /* The pointed edge is invalid (tagged as removed, or vert idx mismatch),
662  * and we already know from previous test that a valid one exists,
663  * use it (if allowed)! */
664  if (do_fixes) {
665  int prev_e = ml->e;
666  ml->e = POINTER_AS_INT(BLI_edgehash_lookup(edge_hash, v1, v2));
667  fix_flag.loops_edge = true;
668  PRINT_ERR(
669  "\tPoly %u has invalid edge reference (%d, is_removed: %d), fixed using edge "
670  "%u",
671  sp->index,
672  prev_e,
673  IS_REMOVED_EDGE(me),
674  ml->e);
675  }
676  else {
677  PRINT_ERR("\tPoly %u has invalid edge reference (%u)", sp->index, ml->e);
678  sp->invalid = true;
679  }
680  }
681  }
682  }
683 
684  if (!sp->invalid) {
685  /* Needed for checking polys using same verts below. */
686  qsort(sp->verts, sp->numverts, sizeof(int), int_cmp);
687  }
688  }
689  }
690 
691  MEM_freeN(vert_tag);
692 
693  /* Second check pass, testing polys using the same verts. */
694  qsort(sort_polys, totpoly, sizeof(SortPoly), search_poly_cmp);
695  sp = prev_sp = sort_polys;
696  sp++;
697 
698  for (i = 1; i < totpoly; i++, sp++) {
699  int p1_nv = sp->numverts, p2_nv = prev_sp->numverts;
700  const int *p1_v = sp->verts, *p2_v = prev_sp->verts;
701 
702  if (sp->invalid) {
703  /* Break, because all known invalid polys have been put at the end
704  * by qsort with search_poly_cmp. */
705  break;
706  }
707 
708  /* Test same polys. */
709  if ((p1_nv == p2_nv) && (memcmp(p1_v, p2_v, p1_nv * sizeof(*p1_v)) == 0)) {
710  if (do_verbose) {
711  /* TODO: convert list to string */
712  PRINT_ERR("\tPolys %u and %u use same vertices (%d", prev_sp->index, sp->index, *p1_v);
713  for (j = 1; j < p1_nv; j++) {
714  PRINT_ERR(", %d", p1_v[j]);
715  }
716  PRINT_ERR("), considering poly %u as invalid.", sp->index);
717  }
718  else {
719  is_valid = false;
720  }
721  sp->invalid = true;
722  }
723  else {
724  prev_sp = sp;
725  }
726  }
727 
728  /* Third check pass, testing loops used by none or more than one poly. */
729  qsort(sort_polys, totpoly, sizeof(SortPoly), search_polyloop_cmp);
730  sp = sort_polys;
731  prev_sp = nullptr;
732  prev_end = 0;
733  for (i = 0; i < totpoly; i++, sp++) {
734  /* Free this now, we don't need it anymore, and avoid us another loop! */
735  if (sp->verts) {
736  MEM_freeN(sp->verts);
737  }
738 
739  /* Note above prev_sp: in following code, we make sure it is always valid poly (or nullptr).
740  */
741  if (sp->invalid) {
742  if (do_fixes) {
743  REMOVE_POLY_TAG((&mpolys[sp->index]));
744  /* DO NOT REMOVE ITS LOOPS!!!
745  * As already invalid polys are at the end of the SortPoly list, the loops they
746  * were the only users have already been tagged as "to remove" during previous
747  * iterations, and we don't want to remove some loops that may be used by
748  * another valid poly! */
749  }
750  }
751  /* Test loops users. */
752  else {
753  /* Unused loops. */
754  if (prev_end < sp->loopstart) {
755  for (j = prev_end, ml = &mloops[prev_end]; j < sp->loopstart; j++, ml++) {
756  PRINT_ERR("\tLoop %u is unused.", j);
757  if (do_fixes) {
758  REMOVE_LOOP_TAG(ml);
759  }
760  }
761  prev_end = sp->loopstart + sp->numverts;
762  prev_sp = sp;
763  }
764  /* Multi-used loops. */
765  else if (prev_end > sp->loopstart) {
766  PRINT_ERR("\tPolys %u and %u share loops from %d to %d, considering poly %u as invalid.",
767  prev_sp->index,
768  sp->index,
769  sp->loopstart,
770  prev_end,
771  sp->index);
772  if (do_fixes) {
773  REMOVE_POLY_TAG((&mpolys[sp->index]));
774  /* DO NOT REMOVE ITS LOOPS!!!
775  * They might be used by some next, valid poly!
776  * Just not updating prev_end/prev_sp vars is enough to ensure the loops
777  * effectively no more needed will be marked as "to be removed"! */
778  }
779  }
780  else {
781  prev_end = sp->loopstart + sp->numverts;
782  prev_sp = sp;
783  }
784  }
785  }
786  /* We may have some remaining unused loops to get rid of! */
787  if (prev_end < totloop) {
788  for (j = prev_end, ml = &mloops[prev_end]; j < totloop; j++, ml++) {
789  PRINT_ERR("\tLoop %u is unused.", j);
790  if (do_fixes) {
791  REMOVE_LOOP_TAG(ml);
792  }
793  }
794  }
795 
796  MEM_freeN(sort_polys);
797  }
798 
799  BLI_edgehash_free(edge_hash, nullptr);
800 
801  /* fix deform verts */
802  if (dverts) {
803  MDeformVert *dv;
804  for (i = 0, dv = dverts; i < totvert; i++, dv++) {
805  MDeformWeight *dw;
806 
807  for (j = 0, dw = dv->dw; j < dv->totweight; j++, dw++) {
808  /* NOTE: greater than max defgroups is accounted for in our code, but not < 0. */
809  if (!isfinite(dw->weight)) {
810  PRINT_ERR("\tVertex deform %u, group %u has weight: %f", i, dw->def_nr, dw->weight);
811  if (do_fixes) {
812  dw->weight = 0.0f;
813  fix_flag.verts_weight = true;
814  }
815  }
816  else if (dw->weight < 0.0f || dw->weight > 1.0f) {
817  PRINT_ERR("\tVertex deform %u, group %u has weight: %f", i, dw->def_nr, dw->weight);
818  if (do_fixes) {
819  CLAMP(dw->weight, 0.0f, 1.0f);
820  fix_flag.verts_weight = true;
821  }
822  }
823 
824  /* Not technically incorrect since this is unsigned, however,
825  * a value over INT_MAX is almost certainly caused by wrapping an uint. */
826  if (dw->def_nr >= INT_MAX) {
827  PRINT_ERR("\tVertex deform %u, has invalid group %u", i, dw->def_nr);
828  if (do_fixes) {
829  BKE_defvert_remove_group(dv, dw);
830  fix_flag.verts_weight = true;
831 
832  if (dv->dw) {
833  /* re-allocated, the new values compensate for stepping
834  * within the for loop and may not be valid */
835  j--;
836  dw = dv->dw + j;
837  }
838  else { /* all freed */
839  break;
840  }
841  }
842  }
843  }
844  }
845  }
846 
847 #undef REMOVE_EDGE_TAG
848 #undef IS_REMOVED_EDGE
849 #undef REMOVE_LOOP_TAG
850 #undef REMOVE_POLY_TAG
851 
852  if (mesh) {
853  if (free_flag.faces) {
855  }
856 
857  if (free_flag.polyloops) {
859  }
860 
861  if (free_flag.edges) {
863  }
864 
865  if (recalc_flag.edges) {
866  BKE_mesh_calc_edges(mesh, true, false);
867  }
868  }
869 
870  if (mesh && mesh->mselect) {
871  MSelect *msel;
872 
873  for (i = 0, msel = mesh->mselect; i < mesh->totselect; i++, msel++) {
874  int tot_elem = 0;
875 
876  if (msel->index < 0) {
877  PRINT_ERR(
878  "\tMesh select element %u type %d index is negative, "
879  "resetting selection stack.\n",
880  i,
881  msel->type);
882  free_flag.mselect = do_fixes;
883  break;
884  }
885 
886  switch (msel->type) {
887  case ME_VSEL:
888  tot_elem = mesh->totvert;
889  break;
890  case ME_ESEL:
891  tot_elem = mesh->totedge;
892  break;
893  case ME_FSEL:
894  tot_elem = mesh->totpoly;
895  break;
896  }
897 
898  if (msel->index > tot_elem) {
899  PRINT_ERR(
900  "\tMesh select element %u type %d index %d is larger than data array size %d, "
901  "resetting selection stack.\n",
902  i,
903  msel->type,
904  msel->index,
905  tot_elem);
906 
907  free_flag.mselect = do_fixes;
908  break;
909  }
910  }
911 
912  if (free_flag.mselect) {
914  mesh->mselect = nullptr;
915  mesh->totselect = 0;
916  }
917  }
918 
919  PRINT_MSG("%s: finished\n\n", __func__);
920 
921  *r_changed = (fix_flag.as_flag || free_flag.as_flag || recalc_flag.as_flag);
922 
923  BLI_assert((*r_changed == false) || (do_fixes == true));
924 
925  return is_valid;
926 }
927 
930  const uint totitems,
931  const bool do_verbose,
932  const bool do_fixes,
933  bool *r_change)
934 {
935  bool is_valid = true;
936  bool has_fixes = false;
937  int i = 0;
938 
939  PRINT_MSG("%s: Checking %d CD layers...\n", __func__, data->totlayer);
940 
941  while (i < data->totlayer) {
942  CustomDataLayer *layer = &data->layers[i];
943  bool ok = true;
944 
946  const int layer_tot = CustomData_number_of_layers(data, layer->type);
947  if (layer_tot > 1) {
948  PRINT_ERR("\tCustomDataLayer type %d is a singleton, found %d in Mesh structure\n",
949  layer->type,
950  layer_tot);
951  ok = false;
952  }
953  }
954 
955  if (mask != 0) {
956  eCustomDataMask layer_typemask = CD_TYPE_AS_MASK(layer->type);
957  if ((layer_typemask & mask) == 0) {
958  PRINT_ERR("\tCustomDataLayer type %d which isn't in the mask\n", layer->type);
959  ok = false;
960  }
961  }
962 
963  if (ok == false) {
964  if (do_fixes) {
965  CustomData_free_layer(data, layer->type, 0, i);
966  has_fixes = true;
967  }
968  }
969 
970  if (ok) {
971  if (CustomData_layer_validate(layer, totitems, do_fixes)) {
972  PRINT_ERR("\tCustomDataLayer type %d has some invalid data\n", layer->type);
973  has_fixes = do_fixes;
974  }
975  i++;
976  }
977  }
978 
979  PRINT_MSG("%s: Finished (is_valid=%d)\n\n", __func__, (int)!has_fixes);
980 
981  *r_change = has_fixes;
982 
983  return is_valid;
984 }
985 
987  const uint totvert,
988  CustomData *edata,
989  const uint totedge,
990  CustomData *ldata,
991  const uint totloop,
992  CustomData *pdata,
993  const uint totpoly,
994  const bool check_meshmask,
995  const bool do_verbose,
996  const bool do_fixes,
997  bool *r_change)
998 {
999  bool is_valid = true;
1000  bool is_change_v, is_change_e, is_change_l, is_change_p;
1001  CustomData_MeshMasks mask = {0};
1002  if (check_meshmask) {
1003  mask = CD_MASK_MESH;
1004  /* Normal data isn't in the mask since it is derived data,
1005  * but it is valid and should not be removed. */
1006  mask.vmask |= CD_MASK_NORMAL;
1007  mask.pmask |= CD_MASK_NORMAL;
1008  }
1009 
1011  vdata, mask.vmask, totvert, do_verbose, do_fixes, &is_change_v);
1013  edata, mask.emask, totedge, do_verbose, do_fixes, &is_change_e);
1015  ldata, mask.lmask, totloop, do_verbose, do_fixes, &is_change_l);
1017  pdata, mask.pmask, totpoly, do_verbose, do_fixes, &is_change_p);
1018 
1019  const int tot_uvloop = CustomData_number_of_layers(ldata, CD_MLOOPUV);
1020  if (tot_uvloop > MAX_MTFACE) {
1021  PRINT_ERR(
1022  "\tMore UV layers than %d allowed, %d last ones won't be available for render, shaders, "
1023  "etc.\n",
1024  MAX_MTFACE,
1025  tot_uvloop - MAX_MTFACE);
1026  }
1027 
1028  /* check indices of clone/stencil */
1029  if (do_fixes && CustomData_get_clone_layer(ldata, CD_MLOOPUV) >= tot_uvloop) {
1031  is_change_l = true;
1032  }
1033  if (do_fixes && CustomData_get_stencil_layer(ldata, CD_MLOOPUV) >= tot_uvloop) {
1035  is_change_l = true;
1036  }
1037 
1038  *r_change = (is_change_v || is_change_e || is_change_l || is_change_p);
1039 
1040  return is_valid;
1041 }
1042 
1043 bool BKE_mesh_validate(Mesh *me, const bool do_verbose, const bool cddata_check_mask)
1044 {
1045  bool changed;
1046 
1047  if (do_verbose) {
1048  CLOG_INFO(&LOG, 0, "MESH: %s", me->id.name + 2);
1049  }
1050 
1052  me->totvert,
1053  &me->edata,
1054  me->totedge,
1055  &me->ldata,
1056  me->totloop,
1057  &me->pdata,
1058  me->totpoly,
1059  cddata_check_mask,
1060  do_verbose,
1061  true,
1062  &changed);
1063 
1065  me->mvert,
1066  me->totvert,
1067  me->medge,
1068  me->totedge,
1069  me->mface,
1070  me->totface,
1071  me->mloop,
1072  me->totloop,
1073  me->mpoly,
1074  me->totpoly,
1075  me->dvert,
1076  do_verbose,
1077  true,
1078  &changed);
1079 
1080  if (changed) {
1082  return true;
1083  }
1084 
1085  return false;
1086 }
1087 
1089 {
1090  const bool do_verbose = true;
1091  const bool do_fixes = false;
1092 
1093  bool is_valid = true;
1094  bool changed = true;
1095 
1097 
1099  &me->vdata,
1100  me->totvert,
1101  &me->edata,
1102  me->totedge,
1103  &me->ldata,
1104  me->totloop,
1105  &me->pdata,
1106  me->totpoly,
1107  false, /* setting mask here isn't useful, gives false positives */
1108  do_verbose,
1109  do_fixes,
1110  &changed);
1111 
1113  me->mvert,
1114  me->totvert,
1115  me->medge,
1116  me->totedge,
1117  me->mface,
1118  me->totface,
1119  me->mloop,
1120  me->totloop,
1121  me->mpoly,
1122  me->totpoly,
1123  me->dvert,
1124  do_verbose,
1125  do_fixes,
1126  &changed);
1127 
1128  BLI_assert(changed == false);
1129 
1130  return is_valid;
1131 }
1132 
1134 {
1135  /* Cast to unsigned to catch negative indices too. */
1136  const uint16_t mat_nr_max = max_ii(0, me->totcol - 1);
1137  MPoly *mp;
1138  const int totpoly = me->totpoly;
1139  int i;
1140  bool is_valid = true;
1141 
1142  for (mp = me->mpoly, i = 0; i < totpoly; i++, mp++) {
1143  if ((uint16_t)mp->mat_nr > mat_nr_max) {
1144  mp->mat_nr = 0;
1145  is_valid = false;
1146  }
1147  }
1148 
1149  if (!is_valid) {
1151  return true;
1152  }
1153 
1154  return false;
1155 }
1156 
1159 /* -------------------------------------------------------------------- */
1164 {
1165  /* NOTE: We need to keep this for edge creation (for now?), and some old `readfile.c` code. */
1166  MFace *f;
1167  int a, b;
1168 
1169  for (a = b = 0, f = me->mface; a < me->totface; a++, f++) {
1170  if (f->v3) {
1171  if (a != b) {
1172  memcpy(&me->mface[b], f, sizeof(me->mface[b]));
1173  CustomData_copy_data(&me->fdata, &me->fdata, a, b, 1);
1174  }
1175  b++;
1176  }
1177  }
1178  if (a != b) {
1179  CustomData_free_elem(&me->fdata, b, a - b);
1180  me->totface = b;
1181  }
1182 }
1183 
1185 {
1186  MPoly *p;
1187  MLoop *l;
1188  int a, b;
1189  /* New loops idx! */
1190  int *new_idx = (int *)MEM_mallocN(sizeof(int) * me->totloop, __func__);
1191 
1192  for (a = b = 0, p = me->mpoly; a < me->totpoly; a++, p++) {
1193  bool invalid = false;
1194  int i = p->loopstart;
1195  int stop = i + p->totloop;
1196 
1197  if (stop > me->totloop || stop < i || p->loopstart < 0) {
1198  invalid = true;
1199  }
1200  else {
1201  l = &me->mloop[i];
1202  i = stop - i;
1203  /* If one of the poly's loops is invalid, the whole poly is invalid! */
1204  for (; i--; l++) {
1205  if (l->e == INVALID_LOOP_EDGE_MARKER) {
1206  invalid = true;
1207  break;
1208  }
1209  }
1210  }
1211 
1212  if (p->totloop >= 3 && !invalid) {
1213  if (a != b) {
1214  memcpy(&me->mpoly[b], p, sizeof(me->mpoly[b]));
1215  CustomData_copy_data(&me->pdata, &me->pdata, a, b, 1);
1216  }
1217  b++;
1218  }
1219  }
1220  if (a != b) {
1221  CustomData_free_elem(&me->pdata, b, a - b);
1222  me->totpoly = b;
1223  }
1224 
1225  /* And now, get rid of invalid loops. */
1226  for (a = b = 0, l = me->mloop; a < me->totloop; a++, l++) {
1227  if (l->e != INVALID_LOOP_EDGE_MARKER) {
1228  if (a != b) {
1229  memcpy(&me->mloop[b], l, sizeof(me->mloop[b]));
1230  CustomData_copy_data(&me->ldata, &me->ldata, a, b, 1);
1231  }
1232  new_idx[a] = b;
1233  b++;
1234  }
1235  else {
1236  /* XXX Theoretically, we should be able to not do this, as no remaining poly
1237  * should use any stripped loop. But for security's sake... */
1238  new_idx[a] = -a;
1239  }
1240  }
1241  if (a != b) {
1242  CustomData_free_elem(&me->ldata, b, a - b);
1243  me->totloop = b;
1244  }
1245 
1246  /* And now, update polys' start loop index. */
1247  /* NOTE: At this point, there should never be any poly using a striped loop! */
1248  for (a = 0, p = me->mpoly; a < me->totpoly; a++, p++) {
1249  p->loopstart = new_idx[p->loopstart];
1250  }
1251 
1252  MEM_freeN(new_idx);
1253 }
1254 
1256 {
1257  MEdge *e;
1258  MLoop *l;
1259  int a, b;
1260  uint *new_idx = (uint *)MEM_mallocN(sizeof(int) * me->totedge, __func__);
1261 
1262  for (a = b = 0, e = me->medge; a < me->totedge; a++, e++) {
1263  if (e->v1 != e->v2) {
1264  if (a != b) {
1265  memcpy(&me->medge[b], e, sizeof(me->medge[b]));
1266  CustomData_copy_data(&me->edata, &me->edata, a, b, 1);
1267  }
1268  new_idx[a] = b;
1269  b++;
1270  }
1271  else {
1272  new_idx[a] = INVALID_LOOP_EDGE_MARKER;
1273  }
1274  }
1275  if (a != b) {
1276  CustomData_free_elem(&me->edata, b, a - b);
1277  me->totedge = b;
1278  }
1279 
1280  /* And now, update loops' edge indices. */
1281  /* XXX We hope no loop was pointing to a striped edge!
1282  * Else, its e will be set to INVALID_LOOP_EDGE_MARKER :/ */
1283  for (a = 0, l = me->mloop; a < me->totloop; a++, l++) {
1284  l->e = new_idx[l->e];
1285  }
1286 
1287  MEM_freeN(new_idx);
1288 }
1289 
1292 /* -------------------------------------------------------------------- */
1296 /* make edges in a Mesh, for outside of editmode */
1297 
1298 struct EdgeSort {
1301 };
1302 
1303 /* edges have to be added with lowest index first for sorting */
1304 static void to_edgesort(struct EdgeSort *ed, uint v1, uint v2, char is_loose, short is_draw)
1305 {
1306  if (v1 < v2) {
1307  ed->v1 = v1;
1308  ed->v2 = v2;
1309  }
1310  else {
1311  ed->v1 = v2;
1312  ed->v2 = v1;
1313  }
1314  ed->is_loose = is_loose;
1315  ed->is_draw = is_draw;
1316 }
1317 
1318 static int vergedgesort(const void *v1, const void *v2)
1319 {
1320  const struct EdgeSort *x1 = static_cast<const struct EdgeSort *>(v1);
1321  const struct EdgeSort *x2 = static_cast<const struct EdgeSort *>(v2);
1322 
1323  if (x1->v1 > x2->v1) {
1324  return 1;
1325  }
1326  if (x1->v1 < x2->v1) {
1327  return -1;
1328  }
1329  if (x1->v2 > x2->v2) {
1330  return 1;
1331  }
1332  if (x1->v2 < x2->v2) {
1333  return -1;
1334  }
1335 
1336  return 0;
1337 }
1338 
1339 /* Create edges based on known verts and faces,
1340  * this function is only used when loading very old blend files */
1341 
1342 static void mesh_calc_edges_mdata(MVert *UNUSED(allvert),
1343  MFace *allface,
1344  MLoop *allloop,
1345  MPoly *allpoly,
1346  int UNUSED(totvert),
1347  int totface,
1348  int UNUSED(totloop),
1349  int totpoly,
1350  const bool use_old,
1351  MEdge **r_medge,
1352  int *r_totedge)
1353 {
1354  MPoly *mpoly;
1355  MFace *mface;
1356  MEdge *medge, *med;
1357  EdgeHash *hash;
1358  struct EdgeSort *edsort, *ed;
1359  int a, totedge = 0;
1360  uint totedge_final = 0;
1361  uint edge_index;
1362 
1363  /* we put all edges in array, sort them, and detect doubles that way */
1364 
1365  for (a = totface, mface = allface; a > 0; a--, mface++) {
1366  if (mface->v4) {
1367  totedge += 4;
1368  }
1369  else if (mface->v3) {
1370  totedge += 3;
1371  }
1372  else {
1373  totedge += 1;
1374  }
1375  }
1376 
1377  if (totedge == 0) {
1378  /* flag that mesh has edges */
1379  (*r_medge) = (MEdge *)MEM_callocN(0, __func__);
1380  (*r_totedge) = 0;
1381  return;
1382  }
1383 
1384  ed = edsort = (EdgeSort *)MEM_mallocN(totedge * sizeof(struct EdgeSort), "EdgeSort");
1385 
1386  for (a = totface, mface = allface; a > 0; a--, mface++) {
1387  to_edgesort(ed++, mface->v1, mface->v2, !mface->v3, mface->edcode & ME_V1V2);
1388  if (mface->v4) {
1389  to_edgesort(ed++, mface->v2, mface->v3, 0, mface->edcode & ME_V2V3);
1390  to_edgesort(ed++, mface->v3, mface->v4, 0, mface->edcode & ME_V3V4);
1391  to_edgesort(ed++, mface->v4, mface->v1, 0, mface->edcode & ME_V4V1);
1392  }
1393  else if (mface->v3) {
1394  to_edgesort(ed++, mface->v2, mface->v3, 0, mface->edcode & ME_V2V3);
1395  to_edgesort(ed++, mface->v3, mface->v1, 0, mface->edcode & ME_V3V1);
1396  }
1397  }
1398 
1399  qsort(edsort, totedge, sizeof(struct EdgeSort), vergedgesort);
1400 
1401  /* count final amount */
1402  for (a = totedge, ed = edsort; a > 1; a--, ed++) {
1403  /* edge is unique when it differs from next edge, or is last */
1404  if (ed->v1 != (ed + 1)->v1 || ed->v2 != (ed + 1)->v2) {
1405  totedge_final++;
1406  }
1407  }
1408  totedge_final++;
1409 
1410  medge = (MEdge *)MEM_callocN(sizeof(MEdge) * totedge_final, __func__);
1411 
1412  for (a = totedge, med = medge, ed = edsort; a > 1; a--, ed++) {
1413  /* edge is unique when it differs from next edge, or is last */
1414  if (ed->v1 != (ed + 1)->v1 || ed->v2 != (ed + 1)->v2) {
1415  med->v1 = ed->v1;
1416  med->v2 = ed->v2;
1417  if (use_old == false || ed->is_draw) {
1418  med->flag = ME_EDGEDRAW | ME_EDGERENDER;
1419  }
1420  if (ed->is_loose) {
1421  med->flag |= ME_LOOSEEDGE;
1422  }
1423 
1424  /* order is swapped so extruding this edge as a surface won't flip face normals
1425  * with cyclic curves */
1426  if (ed->v1 + 1 != ed->v2) {
1427  SWAP(uint, med->v1, med->v2);
1428  }
1429  med++;
1430  }
1431  else {
1432  /* Equal edge, merge the draw-flag. */
1433  (ed + 1)->is_draw |= ed->is_draw;
1434  }
1435  }
1436  /* last edge */
1437  med->v1 = ed->v1;
1438  med->v2 = ed->v2;
1439  med->flag = ME_EDGEDRAW;
1440  if (ed->is_loose) {
1441  med->flag |= ME_LOOSEEDGE;
1442  }
1443  med->flag |= ME_EDGERENDER;
1444 
1445  MEM_freeN(edsort);
1446 
1447  /* set edge members of mloops */
1448  hash = BLI_edgehash_new_ex(__func__, totedge_final);
1449  for (edge_index = 0, med = medge; edge_index < totedge_final; edge_index++, med++) {
1450  BLI_edgehash_insert(hash, med->v1, med->v2, POINTER_FROM_UINT(edge_index));
1451  }
1452 
1453  mpoly = allpoly;
1454  for (a = 0; a < totpoly; a++, mpoly++) {
1455  MLoop *ml, *ml_next;
1456  int i = mpoly->totloop;
1457 
1458  ml_next = allloop + mpoly->loopstart; /* first loop */
1459  ml = &ml_next[i - 1]; /* last loop */
1460 
1461  while (i-- != 0) {
1462  ml->e = POINTER_AS_UINT(BLI_edgehash_lookup(hash, ml->v, ml_next->v));
1463  ml = ml_next;
1464  ml_next++;
1465  }
1466  }
1467 
1468  BLI_edgehash_free(hash, nullptr);
1469 
1470  *r_medge = medge;
1471  *r_totedge = totedge_final;
1472 }
1473 
1474 void BKE_mesh_calc_edges_legacy(Mesh *me, const bool use_old)
1475 {
1476  MEdge *medge;
1477  int totedge = 0;
1478 
1480  me->mface,
1481  me->mloop,
1482  me->mpoly,
1483  me->totvert,
1484  me->totface,
1485  me->totloop,
1486  me->totpoly,
1487  use_old,
1488  &medge,
1489  &totedge);
1490 
1491  if (totedge == 0) {
1492  /* flag that mesh has edges */
1493  me->medge = medge;
1494  me->totedge = 0;
1495  return;
1496  }
1497 
1498  medge = (MEdge *)CustomData_add_layer(&me->edata, CD_MEDGE, CD_ASSIGN, medge, totedge);
1499  me->medge = medge;
1500  me->totedge = totedge;
1501 
1503 }
1504 
1506 {
1507  MEdge *med = mesh->medge;
1508  for (int i = 0; i < mesh->totedge; i++, med++) {
1509  med->flag |= ME_LOOSEEDGE;
1510  }
1511  MLoop *ml = mesh->mloop;
1512  for (int i = 0; i < mesh->totloop; i++, ml++) {
1513  mesh->medge[ml->e].flag &= ~ME_LOOSEEDGE;
1514  }
1515  med = mesh->medge;
1516  for (int i = 0; i < mesh->totedge; i++, med++) {
1517  if (med->flag & ME_LOOSEEDGE) {
1518  med->flag |= ME_EDGEDRAW;
1519  }
1520  }
1521 }
1522 
1524 {
1525  const int numFaces = mesh->totface;
1527 
1528  MFace *mf = mesh->mface;
1529  for (int i = 0; i < numFaces; i++, mf++) {
1530  BLI_edgeset_add(eh, mf->v1, mf->v2);
1531  BLI_edgeset_add(eh, mf->v2, mf->v3);
1532 
1533  if (mf->v4) {
1534  BLI_edgeset_add(eh, mf->v3, mf->v4);
1535  BLI_edgeset_add(eh, mf->v4, mf->v1);
1536  }
1537  else {
1538  BLI_edgeset_add(eh, mf->v3, mf->v1);
1539  }
1540  }
1541 
1542  const int numEdges = BLI_edgeset_len(eh);
1543 
1544  /* write new edges into a temporary CustomData */
1545  CustomData edgeData;
1546  CustomData_reset(&edgeData);
1547  CustomData_add_layer(&edgeData, CD_MEDGE, CD_CALLOC, nullptr, numEdges);
1548  CustomData_add_layer(&edgeData, CD_ORIGINDEX, CD_CALLOC, nullptr, numEdges);
1549 
1550  MEdge *med = (MEdge *)CustomData_get_layer(&edgeData, CD_MEDGE);
1551  int *index = (int *)CustomData_get_layer(&edgeData, CD_ORIGINDEX);
1552 
1554  for (int i = 0; BLI_edgesetIterator_isDone(ehi) == false;
1555  BLI_edgesetIterator_step(ehi), i++, med++, index++) {
1556  BLI_edgesetIterator_getKey(ehi, &med->v1, &med->v2);
1557 
1558  med->flag = ME_EDGEDRAW | ME_EDGERENDER;
1559  *index = ORIGINDEX_NONE;
1560  }
1562 
1563  /* free old CustomData and assign new one */
1565  mesh->edata = edgeData;
1566  mesh->totedge = numEdges;
1567 
1569 
1570  BLI_edgeset_free(eh);
1571 }
1572 
typedef float(TangentPoint)[2]
CustomData interface, see also DNA_customdata_types.h.
bool CustomData_layer_validate(struct CustomDataLayer *layer, uint totitems, bool do_fixes)
Definition: customdata.cc:4468
void CustomData_free(struct CustomData *data, int totelem)
Definition: customdata.cc:2373
int CustomData_number_of_layers(const struct CustomData *data, int type)
int CustomData_get_clone_layer(const struct CustomData *data, int type)
bool CustomData_free_layer(struct CustomData *data, int type, int totelem, int index)
Definition: customdata.cc:2831
@ CD_ASSIGN
@ CD_CALLOC
bool CustomData_layertype_is_singleton(int type)
Definition: customdata.cc:4280
void CustomData_set_layer_clone(struct CustomData *data, int type, int n)
Definition: customdata.cc:2560
int CustomData_get_stencil_layer(const struct CustomData *data, int type)
uint64_t eCustomDataMask
#define ORIGINDEX_NONE
void * CustomData_get_layer(const struct CustomData *data, int type)
void * CustomData_add_layer(struct CustomData *data, int type, eCDAllocType alloctype, void *layer, int totelem)
Definition: customdata.cc:2776
void CustomData_free_elem(struct CustomData *data, int index, int count)
Definition: customdata.cc:3206
void CustomData_set_layer_stencil(struct CustomData *data, int type, int n)
Definition: customdata.cc:2569
#define CD_TYPE_AS_MASK(_type)
void CustomData_copy_data(const struct CustomData *source, struct CustomData *dest, int source_index, int dest_index, int count)
const CustomData_MeshMasks CD_MASK_MESH
Definition: customdata.cc:2065
void CustomData_reset(struct CustomData *data)
Definition: customdata.cc:2367
support for deformation groups and hooks.
void BKE_defvert_remove_group(struct MDeformVert *dvert, struct MDeformWeight *dw)
Definition: deform.c:804
const float(* BKE_mesh_vertex_normals_ensure(const struct Mesh *mesh))[3]
void BKE_mesh_assert_normals_dirty_or_calculated(const struct Mesh *mesh)
bool BKE_mesh_vertex_normals_are_dirty(const struct Mesh *mesh)
void BKE_mesh_calc_edges(struct Mesh *mesh, bool keep_existing_edges, bool select_new_edges)
#define BLI_assert(a)
Definition: BLI_assert.h:46
#define BLI_BITMAP_NEW(_num, _alloc_string)
Definition: BLI_bitmap.h:40
#define BLI_BITMAP_TEST(_bitmap, _index)
Definition: BLI_bitmap.h:64
#define BLI_BITMAP_ENABLE(_bitmap, _index)
Definition: BLI_bitmap.h:81
#define BLI_BITMAP_DISABLE(_bitmap, _index)
Definition: BLI_bitmap.h:88
unsigned int BLI_bitmap
Definition: BLI_bitmap.h:16
void BLI_edgehash_free(EdgeHash *eh, EdgeHashFreeFP free_value)
Definition: edgehash.c:230
void BLI_edgehash_insert(EdgeHash *eh, unsigned int v0, unsigned int v1, void *val)
Definition: edgehash.c:261
bool BLI_edgeset_add(EdgeSet *es, unsigned int v0, unsigned int v1)
Definition: edgehash.c:484
BLI_INLINE bool BLI_edgesetIterator_isDone(const EdgeSetIterator *esi)
Definition: BLI_edgehash.h:234
BLI_INLINE void BLI_edgesetIterator_step(EdgeSetIterator *esi)
Definition: BLI_edgehash.h:230
BLI_INLINE void BLI_edgesetIterator_getKey(EdgeSetIterator *esi, unsigned int *r_v0, unsigned int *r_v1)
Definition: BLI_edgehash.h:222
void BLI_edgesetIterator_free(EdgeSetIterator *esi)
Definition: edgehash.c:536
EdgeSet * BLI_edgeset_new_ex(const char *info, unsigned int nentries_reserve) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
Definition: edgehash.c:424
void BLI_edgeset_free(EdgeSet *es)
Definition: edgehash.c:441
#define BLI_EDGEHASH_SIZE_GUESS_FROM_POLYS(totpoly)
Definition: BLI_edgehash.h:183
EdgeSetIterator * BLI_edgesetIterator_new(EdgeSet *es)
Definition: edgehash.c:527
bool BLI_edgehash_haskey(const EdgeHash *eh, unsigned int v0, unsigned int v1) ATTR_WARN_UNUSED_RESULT
Definition: edgehash.c:363
int BLI_edgeset_len(const EdgeSet *es) ATTR_WARN_UNUSED_RESULT
Definition: edgehash.c:448
EdgeHash * BLI_edgehash_new_ex(const char *info, unsigned int nentries_reserve)
Definition: edgehash.c:212
void * BLI_edgehash_lookup(const EdgeHash *eh, unsigned int v0, unsigned int v1) ATTR_WARN_UNUSED_RESULT
Definition: edgehash.c:295
MINLINE int max_ii(int a, int b)
MINLINE bool is_zero_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void zero_v3(float r[3])
unsigned int uint
Definition: BLI_sys_types.h:67
#define SWAP(type, a, b)
#define POINTER_FROM_INT(i)
#define POINTER_AS_UINT(i)
#define UNUSED(x)
#define POINTER_AS_INT(i)
#define POINTER_FROM_UINT(i)
#define CLOG_INFO(clg_ref, level,...)
Definition: CLG_log.h:187
void DEG_id_tag_update(struct ID *id, int flag)
@ ID_RECALC_GEOMETRY_ALL_MODES
Definition: DNA_ID.h:884
#define CD_MASK_NORMAL
@ CD_ORIGINDEX
@ CD_MEDGE
@ CD_MLOOPUV
#define MAX_MTFACE
@ ME_VSEL
@ ME_FSEL
@ ME_ESEL
@ ME_V4V1
@ ME_V2V3
@ ME_V1V2
@ ME_V3V4
@ ME_V3V1
@ ME_EDGEDRAW
@ ME_EDGERENDER
@ ME_LOOSEEDGE
Object is a sort of wrapper for general info.
_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 GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble u2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLdouble GLdouble v2 _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLdouble GLdouble nz _GL_VOID_RET _GL_VOID GLfloat GLfloat nz _GL_VOID_RET _GL_VOID GLint GLint nz _GL_VOID_RET _GL_VOID GLshort GLshort nz _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const GLfloat *values _GL_VOID_RET _GL_VOID GLsizei const GLushort *values _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID const GLuint const GLclampf *priorities _GL_VOID_RET _GL_VOID GLdouble y _GL_VOID_RET _GL_VOID GLfloat y _GL_VOID_RET _GL_VOID GLint y _GL_VOID_RET _GL_VOID GLshort y _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLfloat GLfloat z _GL_VOID_RET _GL_VOID GLint GLint z _GL_VOID_RET _GL_VOID GLshort GLshort z _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble w _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat w _GL_VOID_RET _GL_VOID GLint GLint GLint w _GL_VOID_RET _GL_VOID GLshort GLshort GLshort w _GL_VOID_RET _GL_VOID GLdouble GLdouble x2
_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.
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Bright Control the brightness and contrast of the input color Vector Map an input vectors to used to fine tune the interpolation of the input Camera Retrieve information about the camera and how it relates to the current shading point s position CLAMP
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
bool is_valid
static float verts[][3]
#define UINT_MAX
Definition: hash_md5.c:43
IconTextureDrawCall normal
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:31
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:33
ccl_device_inline float4 mask(const int4 &mask, const float4 &a)
Definition: math_float4.h:513
static char faces[256]
void BKE_mesh_strip_loose_polysloops(Mesh *me)
#define CHECK_FACE_EDGE(a, b)
static bool mesh_validate_customdata(CustomData *data, eCustomDataMask mask, const uint totitems, const bool do_verbose, const bool do_fixes, bool *r_change)
static void edge_store_from_mface_quad(EdgeUUID es[4], MFace *mf)
#define PRINT_ERR(...)
#define IS_REMOVED_EDGE(_me)
static void mesh_calc_edges_mdata(MVert *UNUSED(allvert), MFace *allface, MLoop *allloop, MPoly *allpoly, int UNUSED(totvert), int totface, int UNUSED(totloop), int totpoly, const bool use_old, MEdge **r_medge, int *r_totedge)
static int search_face_cmp(const void *v1, const void *v2)
static int vergedgesort(const void *v1, const void *v2)
static int search_poly_cmp(const void *v1, const void *v2)
static void edge_store_assign(uint32_t verts[2], const uint32_t v1, const uint32_t v2)
void BKE_mesh_calc_edges_legacy(Mesh *me, const bool use_old)
void BKE_mesh_calc_edges_tessface(Mesh *mesh)
#define CHECK_FACE_VERT_INDEX(a, b)
bool BKE_mesh_validate_arrays(Mesh *mesh, MVert *mverts, uint totvert, MEdge *medges, uint totedge, MFace *mfaces, uint totface, MLoop *mloops, uint totloop, MPoly *mpolys, uint totpoly, MDeformVert *dverts, const bool do_verbose, const bool do_fixes, bool *r_changed)
#define REMOVE_FACE_TAG(_mf)
static int search_polyloop_cmp(const void *v1, const void *v2)
#define REMOVE_POLY_TAG(_mp)
#define INVALID_LOOP_EDGE_MARKER
#define REMOVE_EDGE_TAG(_me)
static int int64_cmp(const void *v1, const void *v2)
void BKE_mesh_strip_loose_edges(Mesh *me)
#define REMOVE_LOOP_TAG(_ml)
static int int_cmp(const void *v1, const void *v2)
bool BKE_mesh_validate_material_indices(Mesh *me)
bool BKE_mesh_validate(Mesh *me, const bool do_verbose, const bool cddata_check_mask)
static void to_edgesort(struct EdgeSort *ed, uint v1, uint v2, char is_loose, short is_draw)
static CLG_LogRef LOG
void BKE_mesh_calc_edges_loose(Mesh *mesh)
bool BKE_mesh_is_valid(Mesh *me)
static void edge_store_from_mface_tri(EdgeUUID es[4], MFace *mf)
bool BKE_mesh_validate_all_customdata(CustomData *vdata, const uint totvert, CustomData *edata, const uint totedge, CustomData *ldata, const uint totloop, CustomData *pdata, const uint totpoly, const bool check_meshmask, const bool do_verbose, const bool do_fixes, bool *r_change)
#define PRINT_MSG(...)
void BKE_mesh_strip_loose_faces(Mesh *me)
bool isfinite(uchar)
Definition: scene/image.cpp:31
static unsigned a[3]
Definition: RandGen.cpp:78
bool remove(void *owner, const AttributeIDRef &attribute_id)
static const pxr::TfToken b("b", pxr::TfToken::Immortal)
#define hash
Definition: noise.c:153
unsigned short uint16_t
Definition: stdint.h:79
unsigned int uint32_t
Definition: stdint.h:80
__int64 int64_t
Definition: stdint.h:89
struct BMEdge * e
Definition: bmesh_class.h:164
char name[66]
Definition: DNA_ID.h:378
struct MDeformWeight * dw
unsigned int def_nr
unsigned int v1
unsigned int v2
unsigned int v2
unsigned int v1
unsigned int v4
unsigned int v3
unsigned int e
unsigned int v
short mat_nr
struct MEdge * medge
CustomData vdata
struct MVert * mvert
struct MDeformVert * dvert
int totedge
int totvert
struct MLoop * mloop
int totface
CustomData pdata
CustomData fdata
int totpoly
short totcol
CustomData edata
int totloop
int totselect
struct MFace * mface
struct MPoly * mpoly
CustomData ldata
struct MSelect * mselect
EdgeUUID es[4]
int * verts
bool invalid
int64_t edval
uint32_t verts[2]