Blender V4.3
mesh_validate.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011-2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <algorithm>
10#include <climits>
11#include <cstdio>
12#include <cstring>
13
14#include "CLG_log.h"
15
16#include "DNA_mesh_types.h"
17#include "DNA_meshdata_types.h"
18#include "DNA_object_types.h"
19
20#include "BLI_map.hh"
21#include "BLI_math_base.h"
22#include "BLI_math_vector.h"
23#include "BLI_ordered_edge.hh"
24#include "BLI_sort.hh"
25#include "BLI_sys_types.h"
26#include "BLI_utildefines.h"
27#include "BLI_vector_set.hh"
28
29#include "BKE_attribute.hh"
30#include "BKE_customdata.hh"
31#include "BKE_deform.hh"
32#include "BKE_mesh.hh"
33#include "BKE_mesh_runtime.hh"
34
35#include "DEG_depsgraph.hh"
36
37#include "MEM_guardedalloc.h"
38
39using blender::float3;
41using blender::Span;
42
43/* corner v/e are unsigned, so using max uint_32 value as invalid marker... */
44#define INVALID_CORNER_EDGE_MARKER 4294967295u
45
46static CLG_LogRef LOG = {"bke.mesh"};
47
48void strip_loose_faces_corners(Mesh *mesh, blender::BitSpan faces_to_remove);
49void mesh_strip_edges(Mesh *mesh);
50
51/* -------------------------------------------------------------------- */
54
59
64
65/* Used to detect faces using exactly the same vertices. */
66/* Used to detect corners used by no (disjoint) or more than one (intersect) faces. */
67struct SortFace {
68 int *verts = nullptr;
69 int numverts = 0;
70 int corner_start = 0;
72 bool invalid = false;
73};
74
75static void edge_store_assign(uint32_t verts[2], const uint32_t v1, const uint32_t v2)
76{
77 if (v1 < v2) {
78 verts[0] = v1;
79 verts[1] = v2;
80 }
81 else {
82 verts[0] = v2;
83 verts[1] = v1;
84 }
85}
86
87static void edge_store_from_mface_quad(EdgeUUID es[4], const MFace *mf)
88{
89 edge_store_assign(es[0].verts, mf->v1, mf->v2);
90 edge_store_assign(es[1].verts, mf->v2, mf->v3);
91 edge_store_assign(es[2].verts, mf->v3, mf->v4);
92 edge_store_assign(es[3].verts, mf->v4, mf->v1);
93}
94
95static void edge_store_from_mface_tri(EdgeUUID es[4], const MFace *mf)
96{
97 edge_store_assign(es[0].verts, mf->v1, mf->v2);
98 edge_store_assign(es[1].verts, mf->v2, mf->v3);
99 edge_store_assign(es[2].verts, mf->v3, mf->v1);
100 es[3].verts[0] = es[3].verts[1] = UINT_MAX;
101}
102
103static bool search_legacy_face_cmp(const SortFaceLegacy &sfa, const SortFaceLegacy &sfb)
104{
105 if (sfa.es[0].edval != sfb.es[0].edval) {
106 return sfa.es[0].edval < sfb.es[0].edval;
107 }
108 if (sfa.es[1].edval != sfb.es[1].edval) {
109 return sfa.es[1].edval < sfb.es[1].edval;
110 }
111 if (sfa.es[2].edval != sfb.es[2].edval) {
112 return sfa.es[2].edval < sfb.es[2].edval;
113 }
114 return sfa.es[3].edval < sfb.es[3].edval;
115}
116
117static bool search_face_cmp(const SortFace &sp1, const SortFace &sp2)
118{
119 /* Reject all invalid faces at end of list! */
120 if (sp1.invalid || sp2.invalid) {
121 return sp1.invalid < sp2.invalid;
122 }
123 /* Else, sort on first non-equal verts (remember verts of valid faces are sorted). */
124 const int max_idx = std::min(sp1.numverts, sp2.numverts);
125 for (int idx = 0; idx < max_idx; idx++) {
126 const int v1_i = sp1.verts[idx];
127 const int v2_i = sp2.verts[idx];
128 if (v1_i != v2_i) {
129 return v1_i < v2_i;
130 }
131 }
132 return sp1.numverts < sp2.numverts;
133}
134
135static bool search_face_corner_cmp(const SortFace &sp1, const SortFace &sp2)
136{
137 /* Reject all invalid faces at end of list! */
138 if (sp1.invalid || sp2.invalid) {
139 return sp1.invalid < sp2.invalid;
140 }
141 /* Else, sort on corner start. */
142 return sp1.corner_start < sp2.corner_start;
143}
144
146
147/* -------------------------------------------------------------------- */
150
151#define PRINT_MSG(...) \
152 if (do_verbose) { \
153 CLOG_INFO(&LOG, 1, __VA_ARGS__); \
154 } \
155 ((void)0)
156
157#define PRINT_ERR(...) \
158 do { \
159 is_valid = false; \
160 if (do_verbose) { \
161 CLOG_ERROR(&LOG, __VA_ARGS__); \
162 } \
163 } while (0)
164
165/* NOLINTNEXTLINE: readability-function-size */
167 float (*vert_positions)[3],
168 uint verts_num,
169 blender::int2 *edges,
170 uint edges_num,
171 MFace *legacy_faces,
172 uint legacy_faces_num,
173 const int *corner_verts,
174 int *corner_edges,
175 uint corners_num,
176 const int *face_offsets,
177 uint faces_num,
178 MDeformVert *dverts, /* assume verts_num length */
179 const bool do_verbose,
180 const bool do_fixes,
181 bool *r_changed)
182{
183 using namespace blender;
184 using namespace blender::bke;
185#define REMOVE_EDGE_TAG(_me) \
186 { \
187 _me[0] = _me[1]; \
188 free_flag.edges = do_fixes; \
189 } \
190 (void)0
191#define IS_REMOVED_EDGE(_me) (_me[0] == _me[1])
192
193#define REMOVE_CORNER_TAG(corner) \
194 { \
195 corner_edges[corner] = INVALID_CORNER_EDGE_MARKER; \
196 free_flag.face_corners = do_fixes; \
197 } \
198 (void)0
199 blender::BitVector<> faces_to_remove(faces_num);
200
201 blender::bke::AttributeWriter<int> material_indices =
202 mesh->attributes_for_write().lookup_for_write<int>("material_index");
203 blender::MutableVArraySpan<int> material_indices_span(material_indices.varray);
204
205 uint i, j;
206 int *v;
207
208 bool is_valid = true;
209
210 union {
211 struct {
212 int verts : 1;
213 int verts_weight : 1;
214 int corners_edge : 1;
215 };
216 int as_flag;
217 } fix_flag;
218
219 union {
220 struct {
221 int edges : 1;
222 int faces : 1;
223 /* This regroups corners and faces! */
224 int face_corners : 1;
225 int mselect : 1;
226 };
227 int as_flag;
228 } free_flag;
229
230 union {
231 struct {
232 int edges : 1;
233 };
234 int as_flag;
235 } recalc_flag;
236
237 Map<OrderedEdge, int> edge_hash;
238 edge_hash.reserve(edges_num);
239
240 BLI_assert(!(do_fixes && mesh == nullptr));
241
242 fix_flag.as_flag = 0;
243 free_flag.as_flag = 0;
244 recalc_flag.as_flag = 0;
245
246 PRINT_MSG("verts(%u), edges(%u), corners(%u), faces(%u)",
247 verts_num,
248 edges_num,
249 corners_num,
250 faces_num);
251
252 if (edges_num == 0 && faces_num != 0) {
253 PRINT_ERR("\tLogical error, %u faces and 0 edges", faces_num);
254 recalc_flag.edges = do_fixes;
255 }
256
257 for (i = 0; i < verts_num; i++) {
258 for (j = 0; j < 3; j++) {
259 if (!isfinite(vert_positions[i][j])) {
260 PRINT_ERR("\tVertex %u: has invalid coordinate", i);
261
262 if (do_fixes) {
263 zero_v3(vert_positions[i]);
264
265 fix_flag.verts = true;
266 }
267 }
268 }
269 }
270
271 for (i = 0; i < edges_num; i++) {
272 blender::int2 &edge = edges[i];
273 bool remove = false;
274
275 if (edge[0] == edge[1]) {
276 PRINT_ERR("\tEdge %u: has matching verts, both %d", i, edge[0]);
277 remove = do_fixes;
278 }
279 if (edge[0] >= verts_num) {
280 PRINT_ERR("\tEdge %u: v1 index out of range, %d", i, edge[0]);
281 remove = do_fixes;
282 }
283 if (edge[1] >= verts_num) {
284 PRINT_ERR("\tEdge %u: v2 index out of range, %d", i, edge[1]);
285 remove = do_fixes;
286 }
287
288 if ((edge[0] != edge[1]) && edge_hash.contains(edge)) {
289 PRINT_ERR("\tEdge %u: is a duplicate of %d", i, edge_hash.lookup(edge));
290 remove = do_fixes;
291 }
292
293 if (remove == false) {
294 if (edge[0] != edge[1]) {
295 edge_hash.add(edge, i);
296 }
297 }
298 else {
299 REMOVE_EDGE_TAG(edge);
300 }
301 }
302
303 if (legacy_faces && !face_offsets) {
304#define REMOVE_FACE_TAG(_mf) \
305 { \
306 _mf->v3 = 0; \
307 free_flag.faces = do_fixes; \
308 } \
309 (void)0
310#define CHECK_FACE_VERT_INDEX(a, b) \
311 if (mf->a == mf->b) { \
312 PRINT_ERR(" face %u: verts invalid, " STRINGIFY(a) "/" STRINGIFY(b) " both %u", i, mf->a); \
313 remove = do_fixes; \
314 } \
315 (void)0
316#define CHECK_FACE_EDGE(a, b) \
317 if (!edge_hash.contains({mf->a, mf->b})) { \
318 PRINT_ERR(" face %u: edge " STRINGIFY(a) "/" STRINGIFY(b) " (%u,%u) is missing edge data", \
319 i, \
320 mf->a, \
321 mf->b); \
322 recalc_flag.edges = do_fixes; \
323 } \
324 (void)0
325
326 MFace *mf;
327 const MFace *mf_prev;
328
329 Array<SortFaceLegacy> sort_faces(legacy_faces_num);
330 SortFaceLegacy *sf;
331 SortFaceLegacy *sf_prev;
332 uint totsortface = 0;
333
334 PRINT_ERR("No faces, only tessellated Faces");
335
336 for (i = 0, mf = legacy_faces, sf = sort_faces.data(); i < legacy_faces_num; i++, mf++) {
337 bool remove = false;
338 int fidx;
339 uint fv[4];
340
341 fidx = mf->v4 ? 3 : 2;
342 do {
343 fv[fidx] = *(&(mf->v1) + fidx);
344 if (fv[fidx] >= verts_num) {
345 PRINT_ERR("\tFace %u: 'v%d' index out of range, %u", i, fidx + 1, fv[fidx]);
346 remove = do_fixes;
347 }
348 } while (fidx--);
349
350 if (remove == false) {
351 if (mf->v4) {
353 CHECK_FACE_VERT_INDEX(v1, v3);
354 CHECK_FACE_VERT_INDEX(v1, v4);
355
358
359 CHECK_FACE_VERT_INDEX(v3, v4);
360 }
361 else {
363 CHECK_FACE_VERT_INDEX(v1, v3);
364
366 }
367
368 if (remove == false) {
369 if (edges_num) {
370 if (mf->v4) {
371 CHECK_FACE_EDGE(v1, v2);
372 CHECK_FACE_EDGE(v2, v3);
373 CHECK_FACE_EDGE(v3, v4);
374 CHECK_FACE_EDGE(v4, v1);
375 }
376 else {
377 CHECK_FACE_EDGE(v1, v2);
378 CHECK_FACE_EDGE(v2, v3);
379 CHECK_FACE_EDGE(v3, v1);
380 }
381 }
382
383 sf->index = i;
384
385 if (mf->v4) {
387 std::sort(sf->es, sf->es + 4, [](const EdgeUUID &a, const EdgeUUID &b) {
388 return a.edval < b.edval;
389 });
390 }
391 else {
393 std::sort(sf->es, sf->es + 3, [](const EdgeUUID &a, const EdgeUUID &b) {
394 return a.edval < b.edval;
395 });
396 }
397
398 totsortface++;
399 sf++;
400 }
401 }
402
403 if (remove) {
404 REMOVE_FACE_TAG(mf);
405 }
406 }
407
408 blender::parallel_sort(sort_faces.begin(), sort_faces.end(), search_legacy_face_cmp);
409
410 sf = sort_faces.data();
411 sf_prev = sf;
412 sf++;
413
414 for (i = 1; i < totsortface; i++, sf++) {
415 bool remove = false;
416
417 /* on a valid mesh, code below will never run */
418 if (memcmp(sf->es, sf_prev->es, sizeof(sf_prev->es)) == 0) {
419 mf = legacy_faces + sf->index;
420
421 if (do_verbose) {
422 mf_prev = legacy_faces + sf_prev->index;
423
424 if (mf->v4) {
425 PRINT_ERR("\tFace %u & %u: are duplicates (%u,%u,%u,%u) (%u,%u,%u,%u)",
426 sf->index,
427 sf_prev->index,
428 mf->v1,
429 mf->v2,
430 mf->v3,
431 mf->v4,
432 mf_prev->v1,
433 mf_prev->v2,
434 mf_prev->v3,
435 mf_prev->v4);
436 }
437 else {
438 PRINT_ERR("\tFace %u & %u: are duplicates (%u,%u,%u) (%u,%u,%u)",
439 sf->index,
440 sf_prev->index,
441 mf->v1,
442 mf->v2,
443 mf->v3,
444 mf_prev->v1,
445 mf_prev->v2,
446 mf_prev->v3);
447 }
448 }
449
450 remove = do_fixes;
451 }
452 else {
453 sf_prev = sf;
454 }
455
456 if (remove) {
457 REMOVE_FACE_TAG(mf);
458 }
459 }
460
461#undef REMOVE_FACE_TAG
462#undef CHECK_FACE_VERT_INDEX
463#undef CHECK_FACE_EDGE
464 }
465
466 /* Checking corners and faces is a bit tricky, as they are quite intricate...
467 *
468 * Faces must have:
469 * - a valid corner_start value.
470 * - a valid corners_num value (>= 3 and corner_start+corners_num < mesh.corners_num).
471 *
472 * corners must have:
473 * - a valid v value.
474 * - a valid e value (corresponding to the edge it defines with the next corner in face).
475 *
476 * Also, corners not used by faces can be discarded.
477 * And "intersecting" corners (i.e. corners used by more than one face) are invalid,
478 * so be sure to leave at most one face per corner!
479 */
480 {
481 BitVector<> vert_tag(mesh->verts_num);
482 Array<SortFace> sort_faces(faces_num);
483 Array<int> sort_face_verts(faces_num == 0 ? 0 : face_offsets[faces_num]);
484 int64_t sort_face_verts_offset = 0;
485
486 for (const int64_t i : blender::IndexRange(faces_num)) {
487 SortFace *sp = &sort_faces[i];
488 const int face_start = face_offsets[i];
489 const int face_size = face_offsets[i + 1] - face_start;
490 sp->index = i;
491
492 /* Material index, isolated from other tests here. While large indices are clamped,
493 * negative indices aren't supported by drawing, exporters etc.
494 * To check the indices are in range, use #BKE_mesh_validate_material_indices */
495 if (material_indices && material_indices_span[i] < 0) {
496 PRINT_ERR("\tFace %u has invalid material (%d)", sp->index, material_indices_span[i]);
497 if (do_fixes) {
498 material_indices_span[i] = 0;
499 }
500 }
501
502 if (face_start < 0 || face_size < 3) {
503 /* Invalid corner data. */
504 PRINT_ERR("\tFace %u is invalid (corner_start: %d, corners_num: %d)",
505 sp->index,
506 face_start,
507 face_size);
508 sp->invalid = true;
509 }
510 else if (face_start + face_size > corners_num) {
511 /* Invalid corner data. */
512 PRINT_ERR(
513 "\tFace %u uses corners out of range "
514 "(corner_start: %d, corner_end: %d, max number of corners: %u)",
515 sp->index,
516 face_start,
517 face_start + face_size - 1,
518 corners_num - 1);
519 sp->invalid = true;
520 }
521 else {
522 /* Face itself is valid, for now. */
523 int v1, v2; /* v1 is prev corner vert idx, v2 is current corner one. */
524 sp->invalid = false;
525 sp->verts = v = sort_face_verts.data() + sort_face_verts_offset;
526 sort_face_verts_offset += face_size;
527 sp->numverts = face_size;
528 sp->corner_start = face_start;
529
530 /* Ideally we would only have to do that once on all vertices
531 * before we start checking each face, but several faces can use same vert,
532 * so we have to ensure here all verts of current face are cleared. */
533 for (j = 0; j < face_size; j++) {
534 const int vert = corner_verts[sp->corner_start + j];
535 if (vert < verts_num) {
536 vert_tag[vert].reset();
537 }
538 }
539
540 /* Test all face's corners' vert idx. */
541 for (j = 0; j < face_size; j++, v++) {
542 const int vert = corner_verts[sp->corner_start + j];
543 if (vert >= verts_num) {
544 /* Invalid vert idx. */
545 PRINT_ERR("\tCorner %u has invalid vert reference (%d)", sp->corner_start + j, vert);
546 sp->invalid = true;
547 }
548 else if (vert_tag[vert].test()) {
549 PRINT_ERR("\tFace %u has duplicated vert reference at corner (%u)", uint(i), j);
550 sp->invalid = true;
551 }
552 else {
553 vert_tag[vert].set();
554 }
555 *v = vert;
556 }
557
558 if (sp->invalid) {
559 sp++;
560 continue;
561 }
562
563 /* Test all face's corners. */
564 for (j = 0; j < face_size; j++) {
565 const int corner = sp->corner_start + j;
566 const int vert = corner_verts[corner];
567 const int edge_i = corner_edges[corner];
568 v1 = vert;
569 v2 = corner_verts[sp->corner_start + (j + 1) % face_size];
570 if (!edge_hash.contains({v1, v2})) {
571 /* Edge not existing. */
572 PRINT_ERR("\tFace %u needs missing edge (%d, %d)", sp->index, v1, v2);
573 if (do_fixes) {
574 recalc_flag.edges = true;
575 }
576 else {
577 sp->invalid = true;
578 }
579 }
580 else if (edge_i >= edges_num) {
581 /* Invalid edge idx.
582 * We already know from previous text that a valid edge exists, use it (if allowed)! */
583 if (do_fixes) {
584 int prev_e = edge_i;
585 corner_edges[corner] = edge_hash.lookup({v1, v2});
586 fix_flag.corners_edge = true;
587 PRINT_ERR("\tCorner %d has invalid edge reference (%d), fixed using edge %d",
588 corner,
589 prev_e,
590 corner_edges[corner]);
591 }
592 else {
593 PRINT_ERR("\tCorner %d has invalid edge reference (%d)", corner, edge_i);
594 sp->invalid = true;
595 }
596 }
597 else {
598 const blender::int2 &edge = edges[edge_i];
599 if (IS_REMOVED_EDGE(edge) ||
600 !((edge[0] == v1 && edge[1] == v2) || (edge[0] == v2 && edge[1] == v1)))
601 {
602 /* The pointed edge is invalid (tagged as removed, or vert idx mismatch),
603 * and we already know from previous test that a valid one exists,
604 * use it (if allowed)! */
605 if (do_fixes) {
606 int prev_e = edge_i;
607 corner_edges[corner] = edge_hash.lookup({v1, v2});
608 fix_flag.corners_edge = true;
609 PRINT_ERR(
610 "\tFace %u has invalid edge reference (%d, is_removed: %d), fixed using edge "
611 "%d",
612 sp->index,
613 prev_e,
614 IS_REMOVED_EDGE(edge),
615 corner_edges[corner]);
616 }
617 else {
618 PRINT_ERR("\tFace %u has invalid edge reference (%d)", sp->index, edge_i);
619 sp->invalid = true;
620 }
621 }
622 }
623 }
624
625 if (!sp->invalid) {
626 /* Needed for checking faces using same verts below. */
627 std::sort(sp->verts, sp->verts + sp->numverts);
628 }
629 }
630 sp++;
631 }
632 BLI_assert(sort_face_verts_offset <= sort_face_verts.size());
633
634 vert_tag.clear_and_shrink();
635
636 /* Second check pass, testing faces using the same verts. */
637 blender::parallel_sort(sort_faces.begin(), sort_faces.end(), search_face_cmp);
638 SortFace *sp, *prev_sp;
639 sp = prev_sp = sort_faces.data();
640 sp++;
641
642 for (i = 1; i < faces_num; i++, sp++) {
643 int p1_nv = sp->numverts, p2_nv = prev_sp->numverts;
644 const int *p1_v = sp->verts, *p2_v = prev_sp->verts;
645
646 if (sp->invalid) {
647 /* Break, because all known invalid faces have been put at the end by the sort above. */
648 break;
649 }
650
651 /* Test same faces. */
652 if ((p1_nv == p2_nv) && (memcmp(p1_v, p2_v, p1_nv * sizeof(*p1_v)) == 0)) {
653 if (do_verbose) {
654 /* TODO: convert list to string */
655 PRINT_ERR("\tFaces %u and %u use same vertices (%d", prev_sp->index, sp->index, *p1_v);
656 for (j = 1; j < p1_nv; j++) {
657 PRINT_ERR(", %d", p1_v[j]);
658 }
659 PRINT_ERR("), considering face %u as invalid.", sp->index);
660 }
661 else {
662 is_valid = false;
663 }
664 sp->invalid = true;
665 }
666 else {
667 prev_sp = sp;
668 }
669 }
670
671 /* Third check pass, testing corners used by none or more than one face. */
672 blender::parallel_sort(sort_faces.begin(), sort_faces.end(), search_face_corner_cmp);
673 sp = sort_faces.data();
674 prev_sp = nullptr;
675 int prev_end = 0;
676 for (i = 0; i < faces_num; i++, sp++) {
677 /* We don't need the verts anymore, and avoid us another corner! */
678 sp->verts = nullptr;
679
680 /* Note above prev_sp: in following code, we make sure it is always valid face (or nullptr).
681 */
682 if (sp->invalid) {
683 if (do_fixes) {
684 faces_to_remove[sp->index].set();
685 free_flag.face_corners = do_fixes;
686 /* DO NOT REMOVE ITS corners!!!
687 * As already invalid faces are at the end of the SortFace list, the corners they
688 * were the only users have already been tagged as "to remove" during previous
689 * iterations, and we don't want to remove some corners that may be used by
690 * another valid face! */
691 }
692 }
693 /* Test corners users. */
694 else {
695 /* Unused corners. */
696 if (prev_end < sp->corner_start) {
697 int corner;
698 for (j = prev_end, corner = prev_end; j < sp->corner_start; j++, corner++) {
699 PRINT_ERR("\tCorner %u is unused.", j);
700 if (do_fixes) {
701 REMOVE_CORNER_TAG(corner);
702 }
703 }
704 prev_end = sp->corner_start + sp->numverts;
705 prev_sp = sp;
706 }
707 /* Multi-used corners. */
708 else if (prev_end > sp->corner_start) {
709 PRINT_ERR(
710 "\tFaces %u and %u share corners from %d to %d, considering face %u as invalid.",
711 prev_sp->index,
712 sp->index,
713 sp->corner_start,
714 prev_end,
715 sp->index);
716 if (do_fixes) {
717 faces_to_remove[sp->index].set();
718 free_flag.face_corners = do_fixes;
719 /* DO NOT REMOVE ITS corners!!!
720 * They might be used by some next, valid face!
721 * Just not updating prev_end/prev_sp vars is enough to ensure the corners
722 * effectively no more needed will be marked as "to be removed"! */
723 }
724 }
725 else {
726 prev_end = sp->corner_start + sp->numverts;
727 prev_sp = sp;
728 }
729 }
730 }
731 /* We may have some remaining unused corners to get rid of! */
732 if (prev_end < corners_num) {
733 int corner;
734 for (j = prev_end, corner = prev_end; j < corners_num; j++, corner++) {
735 PRINT_ERR("\tCorner %u is unused.", j);
736 if (do_fixes) {
737 REMOVE_CORNER_TAG(corner);
738 }
739 }
740 }
741 }
742
743 /* fix deform verts */
744 if (dverts) {
745 MDeformVert *dv;
746 for (i = 0, dv = dverts; i < verts_num; i++, dv++) {
747 MDeformWeight *dw;
748
749 for (j = 0, dw = dv->dw; j < dv->totweight; j++, dw++) {
750 /* NOTE: greater than max defgroups is accounted for in our code, but not < 0. */
751 if (!isfinite(dw->weight)) {
752 PRINT_ERR("\tVertex deform %u, group %u has weight: %f", i, dw->def_nr, dw->weight);
753 if (do_fixes) {
754 dw->weight = 0.0f;
755 fix_flag.verts_weight = true;
756 }
757 }
758 else if (dw->weight < 0.0f || dw->weight > 1.0f) {
759 PRINT_ERR("\tVertex deform %u, group %u has weight: %f", i, dw->def_nr, dw->weight);
760 if (do_fixes) {
761 CLAMP(dw->weight, 0.0f, 1.0f);
762 fix_flag.verts_weight = true;
763 }
764 }
765
766 /* Not technically incorrect since this is unsigned, however,
767 * a value over INT_MAX is almost certainly caused by wrapping an uint. */
768 if (dw->def_nr >= INT_MAX) {
769 PRINT_ERR("\tVertex deform %u, has invalid group %u", i, dw->def_nr);
770 if (do_fixes) {
772 fix_flag.verts_weight = true;
773
774 if (dv->dw) {
775 /* re-allocated, the new values compensate for stepping
776 * within the for corner and may not be valid */
777 j--;
778 dw = dv->dw + j;
779 }
780 else { /* all freed */
781 break;
782 }
783 }
784 }
785 }
786 }
787 }
788
789#undef REMOVE_EDGE_TAG
790#undef IS_REMOVED_EDGE
791#undef REMOVE_CORNER_TAG
792#undef REMOVE_FACE_TAG
793
794 if (mesh) {
795 if (free_flag.faces) {
797 }
798
799 if (free_flag.face_corners) {
800 strip_loose_faces_corners(mesh, faces_to_remove);
801 }
802
803 if (free_flag.edges) {
805 }
806
807 if (recalc_flag.edges) {
808 mesh_calc_edges(*mesh, true, false);
809 }
810 }
811
812 if (mesh && mesh->mselect) {
813 MSelect *msel;
814
815 for (i = 0, msel = mesh->mselect; i < mesh->totselect; i++, msel++) {
816 int tot_elem = 0;
817
818 if (msel->index < 0) {
819 PRINT_ERR(
820 "\tMesh select element %u type %d index is negative, "
821 "resetting selection stack.\n",
822 i,
823 msel->type);
824 free_flag.mselect = do_fixes;
825 break;
826 }
827
828 switch (msel->type) {
829 case ME_VSEL:
830 tot_elem = mesh->verts_num;
831 break;
832 case ME_ESEL:
833 tot_elem = mesh->edges_num;
834 break;
835 case ME_FSEL:
836 tot_elem = mesh->faces_num;
837 break;
838 }
839
840 if (msel->index > tot_elem) {
841 PRINT_ERR(
842 "\tMesh select element %u type %d index %d is larger than data array size %d, "
843 "resetting selection stack.\n",
844 i,
845 msel->type,
846 msel->index,
847 tot_elem);
848
849 free_flag.mselect = do_fixes;
850 break;
851 }
852 }
853
854 if (free_flag.mselect) {
855 MEM_freeN(mesh->mselect);
856 mesh->mselect = nullptr;
857 mesh->totselect = 0;
858 }
859 }
860
861 material_indices_span.save();
862 material_indices.finish();
863
864 PRINT_MSG("%s: finished\n\n", __func__);
865
866 *r_changed = (fix_flag.as_flag || free_flag.as_flag || recalc_flag.as_flag);
867
868 BLI_assert((*r_changed == false) || (do_fixes == true));
869
870 return is_valid;
871}
872
874 eCustomDataMask mask,
875 const uint totitems,
876 const bool do_verbose,
877 const bool do_fixes,
878 bool *r_change)
879{
880 bool is_valid = true;
881 bool has_fixes = false;
882 int i = 0;
883
884 PRINT_MSG("%s: Checking %d CD layers...\n", __func__, data->totlayer);
885
886 /* Set dummy values so the layer-type is always initialized on first access. */
887 int layer_num = -1;
888 int layer_num_type = -1;
889
890 while (i < data->totlayer) {
891 CustomDataLayer *layer = &data->layers[i];
892 const eCustomDataType type = eCustomDataType(layer->type);
893 bool ok = true;
894
895 /* Count layers when the type changes. */
896 if (layer_num_type != type) {
897 layer_num = CustomData_number_of_layers(data, type);
898 layer_num_type = type;
899 }
900
901 /* Validate active index, for a time this could be set to a negative value, see: #105860. */
902 int *active_index_array[] = {
903 &layer->active,
904 &layer->active_rnd,
905 &layer->active_clone,
906 &layer->active_mask,
907 };
908 for (int *active_index : Span(active_index_array, ARRAY_SIZE(active_index_array))) {
909 if (*active_index < 0) {
910 PRINT_ERR("\tCustomDataLayer type %d has a negative active index (%d)\n",
911 layer->type,
912 *active_index);
913 if (do_fixes) {
914 *active_index = 0;
915 has_fixes = true;
916 }
917 }
918 else {
919 if (*active_index >= layer_num) {
920 PRINT_ERR("\tCustomDataLayer type %d has an out of bounds active index (%d >= %d)\n",
921 layer->type,
922 *active_index,
923 layer_num);
924 if (do_fixes) {
925 BLI_assert(layer_num > 0);
926 *active_index = layer_num - 1;
927 has_fixes = true;
928 }
929 }
930 }
931 }
932
934 if (layer_num > 1) {
935 PRINT_ERR("\tCustomDataLayer type %d is a singleton, found %d in Mesh structure\n",
936 type,
937 layer_num);
938 ok = false;
939 }
940 }
941
942 if (mask != 0) {
943 eCustomDataMask layer_typemask = CD_TYPE_AS_MASK(type);
944 if ((layer_typemask & mask) == 0) {
945 PRINT_ERR("\tCustomDataLayer type %d which isn't in the mask\n", type);
946 ok = false;
947 }
948 }
949
950 if (ok == false) {
951 if (do_fixes) {
952 CustomData_free_layer(data, type, 0, i);
953 has_fixes = true;
954 }
955 }
956
957 if (ok) {
958 if (CustomData_layer_validate(layer, totitems, do_fixes)) {
959 PRINT_ERR("\tCustomDataLayer type %d has some invalid data\n", type);
960 has_fixes = do_fixes;
961 }
962 i++;
963 }
964 }
965
966 PRINT_MSG("%s: Finished (is_valid=%d)\n\n", __func__, int(!has_fixes));
967
968 *r_change = has_fixes;
969
970 return is_valid;
971}
972
974 const uint verts_num,
975 CustomData *edge_data,
976 const uint edges_num,
977 CustomData *corner_data,
978 const uint corners_num,
979 CustomData *face_data,
980 const uint faces_num,
981 const bool check_meshmask,
982 const bool do_verbose,
983 const bool do_fixes,
984 bool *r_change)
985{
986 bool is_valid = true;
987 bool is_change_v, is_change_e, is_change_l, is_change_p;
989 if (check_meshmask) {
991 }
992
993 is_valid &= mesh_validate_customdata(
994 vert_data, mask.vmask, verts_num, do_verbose, do_fixes, &is_change_v);
995 is_valid &= mesh_validate_customdata(
996 edge_data, mask.emask, edges_num, do_verbose, do_fixes, &is_change_e);
997 is_valid &= mesh_validate_customdata(
998 corner_data, mask.lmask, corners_num, do_verbose, do_fixes, &is_change_l);
999 is_valid &= mesh_validate_customdata(
1000 face_data, mask.pmask, faces_num, do_verbose, do_fixes, &is_change_p);
1001
1002 const int uv_maps_num = CustomData_number_of_layers(corner_data, CD_PROP_FLOAT2);
1003 if (uv_maps_num > MAX_MTFACE) {
1004 PRINT_ERR(
1005 "\tMore UV layers than %d allowed, %d last ones won't be available for render, shaders, "
1006 "etc.\n",
1007 MAX_MTFACE,
1008 uv_maps_num - MAX_MTFACE);
1009 }
1010
1011 /* check indices of clone/stencil */
1012 if (do_fixes && CustomData_get_clone_layer(corner_data, CD_PROP_FLOAT2) >= uv_maps_num) {
1014 is_change_l = true;
1015 }
1016 if (do_fixes && CustomData_get_stencil_layer(corner_data, CD_PROP_FLOAT2) >= uv_maps_num) {
1018 is_change_l = true;
1019 }
1020
1021 *r_change = (is_change_v || is_change_e || is_change_l || is_change_p);
1022
1023 return is_valid;
1024}
1025
1026bool BKE_mesh_validate(Mesh *mesh, const bool do_verbose, const bool cddata_check_mask)
1027{
1028 bool changed;
1029
1030 if (do_verbose) {
1031 CLOG_INFO(&LOG, 0, "MESH: %s", mesh->id.name + 2);
1032 }
1033
1035 mesh->verts_num,
1036 &mesh->edge_data,
1037 mesh->edges_num,
1038 &mesh->corner_data,
1039 mesh->corners_num,
1040 &mesh->face_data,
1041 mesh->faces_num,
1042 cddata_check_mask,
1043 do_verbose,
1044 true,
1045 &changed);
1046 MutableSpan<float3> positions = mesh->vert_positions_for_write();
1047 MutableSpan<blender::int2> edges = mesh->edges_for_write();
1048 Span<int> face_offsets = mesh->face_offsets();
1049 Span<int> corner_verts = mesh->corner_verts();
1050 MutableSpan<int> corner_edges = mesh->corner_edges_for_write();
1051
1052 MDeformVert *dverts = static_cast<MDeformVert *>(
1053 CustomData_get_layer_for_write(&mesh->vert_data, CD_MDEFORMVERT, mesh->verts_num));
1055 mesh,
1056 reinterpret_cast<float(*)[3]>(positions.data()),
1057 positions.size(),
1058 edges.data(),
1059 edges.size(),
1060 (MFace *)CustomData_get_layer_for_write(&mesh->fdata_legacy, CD_MFACE, mesh->totface_legacy),
1061 mesh->totface_legacy,
1062 corner_verts.data(),
1063 corner_edges.data(),
1064 corner_verts.size(),
1065 face_offsets.data(),
1066 mesh->faces_num,
1067 dverts,
1068 do_verbose,
1069 true,
1070 &changed);
1071
1072 if (changed) {
1075 return true;
1076 }
1077
1078 return false;
1079}
1080
1082{
1083 const bool do_verbose = true;
1084 const bool do_fixes = false;
1085
1086 bool is_valid = true;
1087 bool changed = true;
1088
1090 &mesh->vert_data,
1091 mesh->verts_num,
1092 &mesh->edge_data,
1093 mesh->edges_num,
1094 &mesh->corner_data,
1095 mesh->corners_num,
1096 &mesh->face_data,
1097 mesh->faces_num,
1098 false, /* setting mask here isn't useful, gives false positives */
1099 do_verbose,
1100 do_fixes,
1101 &changed);
1102
1103 MutableSpan<float3> positions = mesh->vert_positions_for_write();
1104 MutableSpan<blender::int2> edges = mesh->edges_for_write();
1105 Span<int> face_offsets = mesh->face_offsets();
1106 Span<int> corner_verts = mesh->corner_verts();
1107 MutableSpan<int> corner_edges = mesh->corner_edges_for_write();
1108
1109 MDeformVert *dverts = static_cast<MDeformVert *>(
1110 CustomData_get_layer_for_write(&mesh->vert_data, CD_MDEFORMVERT, mesh->verts_num));
1111 is_valid &= BKE_mesh_validate_arrays(
1112 mesh,
1113 reinterpret_cast<float(*)[3]>(positions.data()),
1114 positions.size(),
1115 edges.data(),
1116 edges.size(),
1117 (MFace *)CustomData_get_layer_for_write(&mesh->fdata_legacy, CD_MFACE, mesh->totface_legacy),
1118 mesh->totface_legacy,
1119 corner_verts.data(),
1120 corner_edges.data(),
1121 corner_verts.size(),
1122 face_offsets.data(),
1123 mesh->faces_num,
1124 dverts,
1125 do_verbose,
1126 do_fixes,
1127 &changed);
1128
1129 BLI_assert(changed == false);
1130
1131 return is_valid;
1132}
1133
1135{
1136 const int mat_nr_max = max_ii(0, mesh->totcol - 1);
1137 bool is_valid = true;
1138
1139 blender::bke::AttributeWriter<int> material_indices =
1140 mesh->attributes_for_write().lookup_for_write<int>("material_index");
1141 blender::MutableVArraySpan<int> material_indices_span(material_indices.varray);
1142 for (const int i : material_indices_span.index_range()) {
1143 if (material_indices_span[i] < 0 || material_indices_span[i] > mat_nr_max) {
1144 material_indices_span[i] = 0;
1145 is_valid = false;
1146 }
1147 }
1148 material_indices_span.save();
1149 material_indices.finish();
1150
1151 if (!is_valid) {
1153 return true;
1154 }
1155
1156 return false;
1157}
1158
1160
1161/* -------------------------------------------------------------------- */
1164
1166{
1167 MutableSpan<int> face_offsets = mesh->face_offsets_for_write();
1168 MutableSpan<int> corner_edges = mesh->corner_edges_for_write();
1169
1170 int a, b;
1171 /* New corners idx! */
1172 int *new_idx = (int *)MEM_mallocN(sizeof(int) * mesh->corners_num, __func__);
1173
1174 for (a = b = 0; a < mesh->faces_num; a++) {
1175 bool invalid = false;
1176 int start = face_offsets[a];
1177 int size = face_offsets[a + 1] - start;
1178 int stop = start + size;
1179
1180 if (faces_to_remove[a]) {
1181 invalid = true;
1182 }
1183 else if (stop > mesh->corners_num || stop < start || size < 0) {
1184 invalid = true;
1185 }
1186 else {
1187 /* If one of the face's corners is invalid, the whole face is invalid! */
1188 if (corner_edges.slice(start, size).as_span().contains(INVALID_CORNER_EDGE_MARKER)) {
1189 invalid = true;
1190 }
1191 }
1192
1193 if (size >= 3 && !invalid) {
1194 if (a != b) {
1195 face_offsets[b] = face_offsets[a];
1196 CustomData_copy_data(&mesh->face_data, &mesh->face_data, a, b, 1);
1197 }
1198 b++;
1199 }
1200 }
1201 if (a != b) {
1202 CustomData_free_elem(&mesh->face_data, b, a - b);
1203 mesh->faces_num = b;
1204 }
1205
1206 /* And now, get rid of invalid corners. */
1207 int corner = 0;
1208 for (a = b = 0; a < mesh->corners_num; a++, corner++) {
1209 if (corner_edges[corner] != INVALID_CORNER_EDGE_MARKER) {
1210 if (a != b) {
1211 CustomData_copy_data(&mesh->corner_data, &mesh->corner_data, a, b, 1);
1212 }
1213 new_idx[a] = b;
1214 b++;
1215 }
1216 else {
1217 /* XXX Theoretically, we should be able to not do this, as no remaining face
1218 * should use any stripped corner. But for security's sake... */
1219 new_idx[a] = -a;
1220 }
1221 }
1222 if (a != b) {
1223 CustomData_free_elem(&mesh->corner_data, b, a - b);
1224 mesh->corners_num = b;
1225 }
1226
1227 face_offsets[mesh->faces_num] = mesh->corners_num;
1228
1229 /* And now, update faces' start corner index. */
1230 /* NOTE: At this point, there should never be any face using a stripped corner! */
1231 for (const int i : blender::IndexRange(mesh->faces_num)) {
1232 face_offsets[i] = new_idx[face_offsets[i]];
1233 BLI_assert(face_offsets[i] >= 0);
1234 }
1235
1236 MEM_freeN(new_idx);
1237}
1238
1240{
1242 int a, b;
1243 uint *new_idx = (uint *)MEM_mallocN(sizeof(int) * mesh->edges_num, __func__);
1244 MutableSpan<blender::int2> edges = mesh->edges_for_write();
1245
1246 for (a = b = 0, e = edges.data(); a < mesh->edges_num; a++, e++) {
1247 if ((*e)[0] != (*e)[1]) {
1248 if (a != b) {
1249 memcpy(&edges[b], e, sizeof(edges[b]));
1250 CustomData_copy_data(&mesh->edge_data, &mesh->edge_data, a, b, 1);
1251 }
1252 new_idx[a] = b;
1253 b++;
1254 }
1255 else {
1256 new_idx[a] = INVALID_CORNER_EDGE_MARKER;
1257 }
1258 }
1259 if (a != b) {
1260 CustomData_free_elem(&mesh->edge_data, b, a - b);
1261 mesh->edges_num = b;
1262 }
1263
1264 /* And now, update corners' edge indices. */
1265 /* XXX We hope no corner was pointing to a stripped edge!
1266 * Else, its e will be set to INVALID_CORNER_EDGE_MARKER :/ */
1267 MutableSpan<int> corner_edges = mesh->corner_edges_for_write();
1268 for (const int i : corner_edges.index_range()) {
1269 corner_edges[i] = new_idx[corner_edges[i]];
1270 }
1271
1272 MEM_freeN(new_idx);
1273}
1274
CustomData interface, see also DNA_customdata_types.h.
int CustomData_get_clone_layer(const CustomData *data, eCustomDataType type)
int CustomData_get_stencil_layer(const CustomData *data, eCustomDataType type)
bool CustomData_free_layer(CustomData *data, eCustomDataType type, int totelem, int index)
void CustomData_set_layer_clone(CustomData *data, eCustomDataType type, int n)
bool CustomData_layer_validate(CustomDataLayer *layer, uint totitems, bool do_fixes)
void * CustomData_get_layer_for_write(CustomData *data, eCustomDataType type, int totelem)
void CustomData_free_elem(CustomData *data, int index, int count)
void CustomData_copy_data(const CustomData *source, CustomData *dest, int source_index, int dest_index, int count)
bool CustomData_layertype_is_singleton(eCustomDataType type)
int CustomData_number_of_layers(const CustomData *data, eCustomDataType type)
#define CD_TYPE_AS_MASK(_type)
const CustomData_MeshMasks CD_MASK_MESH
void CustomData_set_layer_stencil(CustomData *data, eCustomDataType type, int n)
support for deformation groups and hooks.
void BKE_defvert_remove_group(MDeformVert *dvert, MDeformWeight *dw)
Definition deform.cc:871
void BKE_mesh_strip_loose_faces(Mesh *mesh)
void BKE_mesh_runtime_clear_cache(Mesh *mesh)
#define BLI_assert(a)
Definition BLI_assert.h:50
MINLINE int max_ii(int a, int b)
MINLINE void zero_v3(float r[3])
unsigned int uint
#define CLAMP(a, b, c)
#define ARRAY_SIZE(arr)
#define CLOG_INFO(clg_ref, level,...)
Definition CLG_log.h:179
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_GEOMETRY_ALL_MODES
Definition DNA_ID.h:1148
@ CD_MDEFORMVERT
@ CD_PROP_FLOAT2
#define MAX_MTFACE
@ ME_VSEL
@ ME_FSEL
@ ME_ESEL
Object is a sort of wrapper for general info.
Read Guarded memory(de)allocation.
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Map
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
int64_t size() const
Definition BLI_array.hh:245
const T * end() const
Definition BLI_array.hh:314
const T * data() const
Definition BLI_array.hh:301
const T * begin() const
Definition BLI_array.hh:310
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:271
const Value & lookup(const Key &key) const
Definition BLI_map.hh:506
void reserve(int64_t n)
Definition BLI_map.hh:979
bool contains(const Key &key) const
Definition BLI_map.hh:329
constexpr int64_t size() const
Definition BLI_span.hh:494
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:574
constexpr T * data() const
Definition BLI_span.hh:540
constexpr Span< T > as_span() const
Definition BLI_span.hh:662
constexpr IndexRange index_range() const
Definition BLI_span.hh:671
constexpr const T * data() const
Definition BLI_span.hh:216
constexpr int64_t size() const
Definition BLI_span.hh:253
local_group_size(16, 16) .push_constant(Type b
static float verts[][3]
#define UINT_MAX
Definition hash_md5.cc:44
#define LOG(severity)
Definition log.h:33
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
ccl_device_inline float4 mask(const int4 mask, const float4 a)
static char faces[256]
#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 bool search_face_cmp(const SortFace &sp1, const SortFace &sp2)
void mesh_strip_edges(Mesh *mesh)
#define PRINT_ERR(...)
#define IS_REMOVED_EDGE(_me)
bool BKE_mesh_validate_all_customdata(CustomData *vert_data, const uint verts_num, CustomData *edge_data, const uint edges_num, CustomData *corner_data, const uint corners_num, CustomData *face_data, const uint faces_num, const bool check_meshmask, const bool do_verbose, const bool do_fixes, bool *r_change)
bool BKE_mesh_validate_material_indices(Mesh *mesh)
static void edge_store_from_mface_tri(EdgeUUID es[4], const MFace *mf)
static void edge_store_from_mface_quad(EdgeUUID es[4], const MFace *mf)
static bool search_face_corner_cmp(const SortFace &sp1, const SortFace &sp2)
void strip_loose_faces_corners(Mesh *mesh, blender::BitSpan faces_to_remove)
static bool search_legacy_face_cmp(const SortFaceLegacy &sfa, const SortFaceLegacy &sfb)
static void edge_store_assign(uint32_t verts[2], const uint32_t v1, const uint32_t v2)
#define CHECK_FACE_VERT_INDEX(a, b)
bool BKE_mesh_validate_arrays(Mesh *mesh, float(*vert_positions)[3], uint verts_num, blender::int2 *edges, uint edges_num, MFace *legacy_faces, uint legacy_faces_num, const int *corner_verts, int *corner_edges, uint corners_num, const int *face_offsets, uint faces_num, MDeformVert *dverts, const bool do_verbose, const bool do_fixes, bool *r_changed)
#define REMOVE_FACE_TAG(_mf)
#define REMOVE_CORNER_TAG(corner)
#define REMOVE_EDGE_TAG(_me)
bool BKE_mesh_is_valid(Mesh *mesh)
#define INVALID_CORNER_EDGE_MARKER
#define PRINT_MSG(...)
bool BKE_mesh_validate(Mesh *mesh, const bool do_verbose, const bool cddata_check_mask)
void mesh_calc_edges(Mesh &mesh, bool keep_existing_edges, bool select_new_edges)
void parallel_sort(RandomAccessIterator begin, RandomAccessIterator end)
Definition BLI_sort.hh:23
VecBase< int32_t, 2 > int2
VecBase< float, 3 > float3
unsigned int uint32_t
Definition stdint.h:80
__int64 int64_t
Definition stdint.h:89
struct MDeformWeight * dw
unsigned int def_nr
unsigned int v2
unsigned int v1
unsigned int v4
unsigned int v3
EdgeUUID es[4]
int64_t edval
uint32_t verts[2]