Changeset View
Changeset View
Standalone View
Standalone View
source/blender/blenkernel/intern/mesh_validate.c
| Show All 25 Lines | |||||
| */ | */ | ||||
| #include <stdio.h> | #include <stdio.h> | ||||
| #include <stdlib.h> | #include <stdlib.h> | ||||
| #include <string.h> | #include <string.h> | ||||
| #include <limits.h> | #include <limits.h> | ||||
| #include "CLG_log.h" | |||||
| #include "DNA_mesh_types.h" | #include "DNA_mesh_types.h" | ||||
| #include "DNA_meshdata_types.h" | #include "DNA_meshdata_types.h" | ||||
| #include "DNA_object_types.h" | #include "DNA_object_types.h" | ||||
| #include "BLI_sys_types.h" | #include "BLI_sys_types.h" | ||||
| #include "BLI_utildefines.h" | #include "BLI_utildefines.h" | ||||
| #include "BLI_edgehash.h" | #include "BLI_edgehash.h" | ||||
| #include "BLI_math_base.h" | #include "BLI_math_base.h" | ||||
| #include "BLI_math_vector.h" | #include "BLI_math_vector.h" | ||||
| #include "BKE_deform.h" | #include "BKE_deform.h" | ||||
| #include "BKE_mesh.h" | #include "BKE_mesh.h" | ||||
| #include "DEG_depsgraph.h" | #include "DEG_depsgraph.h" | ||||
| #include "MEM_guardedalloc.h" | #include "MEM_guardedalloc.h" | ||||
| /* loop v/e are unsigned, so using max uint_32 value as invalid marker... */ | /* loop v/e are unsigned, so using max uint_32 value as invalid marker... */ | ||||
| #define INVALID_LOOP_EDGE_MARKER 4294967295u | #define INVALID_LOOP_EDGE_MARKER 4294967295u | ||||
| static CLG_LogRef LOG = {"bke.mesh"}; | |||||
| /** \name Internal functions | /** \name Internal functions | ||||
| * \{ */ | * \{ */ | ||||
| typedef union { | typedef union { | ||||
| uint32_t verts[2]; | uint32_t verts[2]; | ||||
| int64_t edval; | int64_t edval; | ||||
| } EdgeUUID; | } EdgeUUID; | ||||
| ▲ Show 20 Lines • Show All 130 Lines • ▼ Show 20 Lines | |||||
| /** \} */ | /** \} */ | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name Mesh Validation | /** \name Mesh Validation | ||||
| * \{ */ | * \{ */ | ||||
| #define PRINT_MSG(...) (void) \ | #define PRINT_MSG(...) if(do_verbose) CLOG_INFO(&LOG, 1, __VA_ARGS__) | ||||
| ( \ | |||||
| ((do_verbose) ? printf(__VA_ARGS__) : 0)) | #define PRINT_ERR(...) do { \ | ||||
| is_valid = false; \ | |||||
| #define PRINT_ERR(...) (void) \ | if (do_verbose) { CLOG_ERROR(&LOG, __VA_ARGS__); } \ | ||||
| (is_valid = false, \ | } while(0) | ||||
| ((do_verbose) ? printf(__VA_ARGS__) : 0)) | |||||
| /** | /** | ||||
| * Validate the mesh, \a do_fixes requires \a mesh to be non-null. | * Validate the mesh, \a do_fixes requires \a mesh to be non-null. | ||||
| * | * | ||||
| * \return false if no changes needed to be made. | * \return false if no changes needed to be made. | ||||
| */ | */ | ||||
| bool BKE_mesh_validate_arrays( | bool BKE_mesh_validate_arrays( | ||||
| Mesh *mesh, | Mesh *mesh, | ||||
| ▲ Show 20 Lines • Show All 51 Lines • ▼ Show 20 Lines | #define REMOVE_POLY_TAG(_mp) { _mp->totloop *= -1; free_flag.polyloops = do_fixes; } (void)0 | ||||
| EdgeHash *edge_hash = BLI_edgehash_new_ex(__func__, totedge); | EdgeHash *edge_hash = BLI_edgehash_new_ex(__func__, totedge); | ||||
| BLI_assert(!(do_fixes && mesh == NULL)); | BLI_assert(!(do_fixes && mesh == NULL)); | ||||
| fix_flag.as_flag = 0; | fix_flag.as_flag = 0; | ||||
| free_flag.as_flag = 0; | free_flag.as_flag = 0; | ||||
| recalc_flag.as_flag = 0; | recalc_flag.as_flag = 0; | ||||
| PRINT_MSG("%s: verts(%u), edges(%u), loops(%u), polygons(%u)\n", | PRINT_MSG("verts(%u), edges(%u), loops(%u), polygons(%u)", | ||||
| __func__, totvert, totedge, totloop, totpoly); | totvert, totedge, totloop, totpoly); | ||||
| if (totedge == 0 && totpoly != 0) { | if (totedge == 0 && totpoly != 0) { | ||||
| PRINT_ERR("\tLogical error, %u polygons and 0 edges\n", totpoly); | PRINT_ERR("Logical error, %u polygons and 0 edges", totpoly); | ||||
| recalc_flag.edges = do_fixes; | recalc_flag.edges = do_fixes; | ||||
| } | } | ||||
| for (i = 0; i < totvert; i++, mv++) { | for (i = 0; i < totvert; i++, mv++) { | ||||
| bool fix_normal = true; | bool fix_normal = true; | ||||
| for (j = 0; j < 3; j++) { | for (j = 0; j < 3; j++) { | ||||
| if (!isfinite(mv->co[j])) { | if (!isfinite(mv->co[j])) { | ||||
| PRINT_ERR("\tVertex %u: has invalid coordinate\n", i); | PRINT_ERR("Vertex %u: has invalid coordinate", i); | ||||
| if (do_fixes) { | if (do_fixes) { | ||||
| zero_v3(mv->co); | zero_v3(mv->co); | ||||
| fix_flag.verts = true; | fix_flag.verts = true; | ||||
| } | } | ||||
| } | } | ||||
| if (mv->no[j] != 0) | if (mv->no[j] != 0) | ||||
| fix_normal = false; | fix_normal = false; | ||||
| } | } | ||||
| if (fix_normal) { | if (fix_normal) { | ||||
| PRINT_ERR("\tVertex %u: has zero normal, assuming Z-up normal\n", i); | PRINT_ERR("\tVertex %u: has zero normal, assuming Z-up normal", i); | ||||
| if (do_fixes) { | if (do_fixes) { | ||||
| mv->no[2] = SHRT_MAX; | mv->no[2] = SHRT_MAX; | ||||
| fix_flag.verts = true; | fix_flag.verts = true; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| for (i = 0, me = medges; i < totedge; i++, me++) { | for (i = 0, me = medges; i < totedge; i++, me++) { | ||||
| bool remove = false; | bool remove = false; | ||||
| if (me->v1 == me->v2) { | if (me->v1 == me->v2) { | ||||
| PRINT_ERR("\tEdge %u: has matching verts, both %u\n", i, me->v1); | PRINT_ERR("\tEdge %u: has matching verts, both %u", i, me->v1); | ||||
| remove = do_fixes; | remove = do_fixes; | ||||
| } | } | ||||
| if (me->v1 >= totvert) { | if (me->v1 >= totvert) { | ||||
| PRINT_ERR("\tEdge %u: v1 index out of range, %u\n", i, me->v1); | PRINT_ERR("\tEdge %u: v1 index out of range, %u", i, me->v1); | ||||
| remove = do_fixes; | remove = do_fixes; | ||||
| } | } | ||||
| if (me->v2 >= totvert) { | if (me->v2 >= totvert) { | ||||
| PRINT_ERR("\tEdge %u: v2 index out of range, %u\n", i, me->v2); | PRINT_ERR("\tEdge %u: v2 index out of range, %u", i, me->v2); | ||||
| remove = do_fixes; | remove = do_fixes; | ||||
| } | } | ||||
| if ((me->v1 != me->v2) && BLI_edgehash_haskey(edge_hash, me->v1, me->v2)) { | if ((me->v1 != me->v2) && BLI_edgehash_haskey(edge_hash, me->v1, me->v2)) { | ||||
| PRINT_ERR("\tEdge %u: is a duplicate of %d\n", i, | PRINT_ERR("\tEdge %u: is a duplicate of %d", i, | ||||
| POINTER_AS_INT(BLI_edgehash_lookup(edge_hash, me->v1, me->v2))); | POINTER_AS_INT(BLI_edgehash_lookup(edge_hash, me->v1, me->v2))); | ||||
| remove = do_fixes; | remove = do_fixes; | ||||
| } | } | ||||
| if (remove == false) { | if (remove == false) { | ||||
| if (me->v1 != me->v2) { | if (me->v1 != me->v2) { | ||||
| BLI_edgehash_insert(edge_hash, me->v1, me->v2, POINTER_FROM_INT(i)); | BLI_edgehash_insert(edge_hash, me->v1, me->v2, POINTER_FROM_INT(i)); | ||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| REMOVE_EDGE_TAG(me); | REMOVE_EDGE_TAG(me); | ||||
| } | } | ||||
| } | } | ||||
| if (mfaces && !mpolys) { | if (mfaces && !mpolys) { | ||||
| # define REMOVE_FACE_TAG(_mf) { _mf->v3 = 0; free_flag.faces = do_fixes; } (void)0 | # define REMOVE_FACE_TAG(_mf) { _mf->v3 = 0; free_flag.faces = do_fixes; } (void)0 | ||||
| # define CHECK_FACE_VERT_INDEX(a, b) \ | # define CHECK_FACE_VERT_INDEX(a, b) \ | ||||
| if (mf->a == mf->b) { \ | if (mf->a == mf->b) { \ | ||||
| PRINT_ERR(" face %u: verts invalid, " STRINGIFY(a) "/" STRINGIFY(b) " both %u\n", i, mf->a); \ | PRINT_ERR(" face %u: verts invalid, " STRINGIFY(a) "/" STRINGIFY(b) " both %u", i, mf->a); \ | ||||
| remove = do_fixes; \ | remove = do_fixes; \ | ||||
| } (void)0 | } (void)0 | ||||
| # define CHECK_FACE_EDGE(a, b) \ | # define CHECK_FACE_EDGE(a, b) \ | ||||
| if (!BLI_edgehash_haskey(edge_hash, mf->a, mf->b)) { \ | if (!BLI_edgehash_haskey(edge_hash, mf->a, mf->b)) { \ | ||||
| PRINT_ERR(" face %u: edge " STRINGIFY(a) "/" STRINGIFY(b) \ | PRINT_ERR(" face %u: edge " STRINGIFY(a) "/" STRINGIFY(b) \ | ||||
| " (%u,%u) is missing edge data\n", i, mf->a, mf->b); \ | " (%u,%u) is missing edge data", i, mf->a, mf->b); \ | ||||
| recalc_flag.edges = do_fixes; \ | recalc_flag.edges = do_fixes; \ | ||||
| } (void)0 | } (void)0 | ||||
| MFace *mf; | MFace *mf; | ||||
| MFace *mf_prev; | MFace *mf_prev; | ||||
| SortFace *sort_faces = MEM_callocN(sizeof(SortFace) * totface, "search faces"); | SortFace *sort_faces = MEM_callocN(sizeof(SortFace) * totface, "search faces"); | ||||
| SortFace *sf; | SortFace *sf; | ||||
| SortFace *sf_prev; | SortFace *sf_prev; | ||||
| unsigned int totsortface = 0; | unsigned int totsortface = 0; | ||||
| PRINT_ERR("No Polys, only tessellated Faces\n"); | PRINT_ERR("No Polys, only tessellated Faces"); | ||||
| for (i = 0, mf = mfaces, sf = sort_faces; i < totface; i++, mf++) { | for (i = 0, mf = mfaces, sf = sort_faces; i < totface; i++, mf++) { | ||||
| bool remove = false; | bool remove = false; | ||||
| int fidx; | int fidx; | ||||
| unsigned int fv[4]; | unsigned int fv[4]; | ||||
| fidx = mf->v4 ? 3 : 2; | fidx = mf->v4 ? 3 : 2; | ||||
| do { | do { | ||||
| fv[fidx] = *(&(mf->v1) + fidx); | fv[fidx] = *(&(mf->v1) + fidx); | ||||
| if (fv[fidx] >= totvert) { | if (fv[fidx] >= totvert) { | ||||
| PRINT_ERR("\tFace %u: 'v%d' index out of range, %u\n", i, fidx + 1, fv[fidx]); | PRINT_ERR("\tFace %u: 'v%d' index out of range, %u", i, fidx + 1, fv[fidx]); | ||||
| remove = do_fixes; | remove = do_fixes; | ||||
| } | } | ||||
| } while (fidx--); | } while (fidx--); | ||||
| if (remove == false) { | if (remove == false) { | ||||
| if (mf->v4) { | if (mf->v4) { | ||||
| CHECK_FACE_VERT_INDEX(v1, v2); | CHECK_FACE_VERT_INDEX(v1, v2); | ||||
| CHECK_FACE_VERT_INDEX(v1, v3); | CHECK_FACE_VERT_INDEX(v1, v3); | ||||
| ▲ Show 20 Lines • Show All 60 Lines • ▼ Show 20 Lines | for (i = 1; i < totsortface; i++, sf++) { | ||||
| /* on a valid mesh, code below will never run */ | /* on a valid mesh, code below will never run */ | ||||
| if (memcmp(sf->es, sf_prev->es, sizeof(sf_prev->es)) == 0) { | if (memcmp(sf->es, sf_prev->es, sizeof(sf_prev->es)) == 0) { | ||||
| mf = mfaces + sf->index; | mf = mfaces + sf->index; | ||||
| if (do_verbose) { | if (do_verbose) { | ||||
| mf_prev = mfaces + sf_prev->index; | mf_prev = mfaces + sf_prev->index; | ||||
| if (mf->v4) { | if (mf->v4) { | ||||
| PRINT_ERR("\tFace %u & %u: are duplicates (%u,%u,%u,%u) (%u,%u,%u,%u)\n", | PRINT_ERR("\tFace %u & %u: are duplicates (%u,%u,%u,%u) (%u,%u,%u,%u)", | ||||
| sf->index, sf_prev->index, mf->v1, mf->v2, mf->v3, mf->v4, | sf->index, sf_prev->index, mf->v1, mf->v2, mf->v3, mf->v4, | ||||
| mf_prev->v1, mf_prev->v2, mf_prev->v3, mf_prev->v4); | mf_prev->v1, mf_prev->v2, mf_prev->v3, mf_prev->v4); | ||||
| } | } | ||||
| else { | else { | ||||
| PRINT_ERR("\tFace %u & %u: are duplicates (%u,%u,%u) (%u,%u,%u)\n", | PRINT_ERR("\tFace %u & %u: are duplicates (%u,%u,%u) (%u,%u,%u)", | ||||
| sf->index, sf_prev->index, mf->v1, mf->v2, mf->v3, | sf->index, sf_prev->index, mf->v1, mf->v2, mf->v3, | ||||
| mf_prev->v1, mf_prev->v2, mf_prev->v3); | mf_prev->v1, mf_prev->v2, mf_prev->v3); | ||||
| } | } | ||||
| } | } | ||||
| remove = do_fixes; | remove = do_fixes; | ||||
| } | } | ||||
| else { | else { | ||||
| Show All 31 Lines | */ | ||||
| SortPoly *prev_sp, *sp = sort_polys; | SortPoly *prev_sp, *sp = sort_polys; | ||||
| int prev_end; | int prev_end; | ||||
| for (i = 0, mp = mpolys; i < totpoly; i++, mp++, sp++) { | for (i = 0, mp = mpolys; i < totpoly; i++, mp++, sp++) { | ||||
| sp->index = i; | sp->index = i; | ||||
| if (mp->loopstart < 0 || mp->totloop < 3) { | if (mp->loopstart < 0 || mp->totloop < 3) { | ||||
| /* Invalid loop data. */ | /* Invalid loop data. */ | ||||
| PRINT_ERR("\tPoly %u is invalid (loopstart: %d, totloop: %d)\n", | PRINT_ERR("\tPoly %u is invalid (loopstart: %d, totloop: %d)", | ||||
| sp->index, mp->loopstart, mp->totloop); | sp->index, mp->loopstart, mp->totloop); | ||||
| sp->invalid = true; | sp->invalid = true; | ||||
| } | } | ||||
| else if (mp->loopstart + mp->totloop > totloop) { | else if (mp->loopstart + mp->totloop > totloop) { | ||||
| /* Invalid loop data. */ | /* Invalid loop data. */ | ||||
| PRINT_ERR("\tPoly %u uses loops out of range (loopstart: %d, loopend: %d, max nbr of loops: %u)\n", | PRINT_ERR("\tPoly %u uses loops out of range (loopstart: %d, loopend: %d, max nbr of loops: %u)", | ||||
| sp->index, mp->loopstart, mp->loopstart + mp->totloop - 1, totloop - 1); | sp->index, mp->loopstart, mp->loopstart + mp->totloop - 1, totloop - 1); | ||||
| sp->invalid = true; | sp->invalid = true; | ||||
| } | } | ||||
| else { | else { | ||||
| /* Poly itself is valid, for now. */ | /* Poly itself is valid, for now. */ | ||||
| int v1, v2; /* v1 is prev loop vert idx, v2 is current loop one. */ | int v1, v2; /* v1 is prev loop vert idx, v2 is current loop one. */ | ||||
| sp->invalid = false; | sp->invalid = false; | ||||
| sp->verts = v = MEM_mallocN(sizeof(int) * mp->totloop, "Vert idx of SortPoly"); | sp->verts = v = MEM_mallocN(sizeof(int) * mp->totloop, "Vert idx of SortPoly"); | ||||
| sp->numverts = mp->totloop; | sp->numverts = mp->totloop; | ||||
| sp->loopstart = mp->loopstart; | sp->loopstart = mp->loopstart; | ||||
| /* Ideally we would only have to do that once on all vertices before we start checking each poly, but | /* Ideally we would only have to do that once on all vertices before we start checking each poly, but | ||||
| * several polys can use same vert, so we have to ensure here all verts of current poly are cleared. */ | * several polys can use same vert, so we have to ensure here all verts of current poly are cleared. */ | ||||
| for (j = 0, ml = &mloops[sp->loopstart]; j < mp->totloop; j++, ml++) { | for (j = 0, ml = &mloops[sp->loopstart]; j < mp->totloop; j++, ml++) { | ||||
| if (ml->v < totvert) { | if (ml->v < totvert) { | ||||
| mverts[ml->v].flag &= ~ME_VERT_TMP_TAG; | mverts[ml->v].flag &= ~ME_VERT_TMP_TAG; | ||||
| } | } | ||||
| } | } | ||||
| /* Test all poly's loops' vert idx. */ | /* Test all poly's loops' vert idx. */ | ||||
| for (j = 0, ml = &mloops[sp->loopstart]; j < mp->totloop; j++, ml++, v++) { | for (j = 0, ml = &mloops[sp->loopstart]; j < mp->totloop; j++, ml++, v++) { | ||||
| if (ml->v >= totvert) { | if (ml->v >= totvert) { | ||||
| /* Invalid vert idx. */ | /* Invalid vert idx. */ | ||||
| PRINT_ERR("\tLoop %u has invalid vert reference (%u)\n", sp->loopstart + j, ml->v); | PRINT_ERR("\tLoop %u has invalid vert reference (%u)", sp->loopstart + j, ml->v); | ||||
| sp->invalid = true; | sp->invalid = true; | ||||
| } | } | ||||
| else if (mverts[ml->v].flag & ME_VERT_TMP_TAG) { | else if (mverts[ml->v].flag & ME_VERT_TMP_TAG) { | ||||
| PRINT_ERR("\tPoly %u has duplicated vert reference at corner (%u)\n", i, j); | PRINT_ERR("\tPoly %u has duplicated vert reference at corner (%u)", i, j); | ||||
| sp->invalid = true; | sp->invalid = true; | ||||
| } | } | ||||
| else { | else { | ||||
| mverts[ml->v].flag |= ME_VERT_TMP_TAG; | mverts[ml->v].flag |= ME_VERT_TMP_TAG; | ||||
| } | } | ||||
| *v = ml->v; | *v = ml->v; | ||||
| } | } | ||||
| if (sp->invalid) | if (sp->invalid) | ||||
| continue; | continue; | ||||
| /* Test all poly's loops. */ | /* Test all poly's loops. */ | ||||
| for (j = 0, ml = &mloops[sp->loopstart]; j < mp->totloop; j++, ml++) { | for (j = 0, ml = &mloops[sp->loopstart]; j < mp->totloop; j++, ml++) { | ||||
| v1 = ml->v; | v1 = ml->v; | ||||
| v2 = mloops[sp->loopstart + (j + 1) % mp->totloop].v; | v2 = mloops[sp->loopstart + (j + 1) % mp->totloop].v; | ||||
| if (!BLI_edgehash_haskey(edge_hash, v1, v2)) { | if (!BLI_edgehash_haskey(edge_hash, v1, v2)) { | ||||
| /* Edge not existing. */ | /* Edge not existing. */ | ||||
| PRINT_ERR("\tPoly %u needs missing edge (%d, %d)\n", sp->index, v1, v2); | PRINT_ERR("\tPoly %u needs missing edge (%d, %d)", sp->index, v1, v2); | ||||
| if (do_fixes) | if (do_fixes) | ||||
| recalc_flag.edges = true; | recalc_flag.edges = true; | ||||
| else | else | ||||
| sp->invalid = true; | sp->invalid = true; | ||||
| } | } | ||||
| else if (ml->e >= totedge) { | else if (ml->e >= totedge) { | ||||
| /* Invalid edge idx. | /* Invalid edge idx. | ||||
| * We already know from previous text that a valid edge exists, use it (if allowed)! */ | * We already know from previous text that a valid edge exists, use it (if allowed)! */ | ||||
| if (do_fixes) { | if (do_fixes) { | ||||
| int prev_e = ml->e; | int prev_e = ml->e; | ||||
| ml->e = POINTER_AS_INT(BLI_edgehash_lookup(edge_hash, v1, v2)); | ml->e = POINTER_AS_INT(BLI_edgehash_lookup(edge_hash, v1, v2)); | ||||
| fix_flag.loops_edge = true; | fix_flag.loops_edge = true; | ||||
| PRINT_ERR("\tLoop %u has invalid edge reference (%d), fixed using edge %u\n", | PRINT_ERR("\tLoop %u has invalid edge reference (%d), fixed using edge %u", | ||||
| sp->loopstart + j, prev_e, ml->e); | sp->loopstart + j, prev_e, ml->e); | ||||
| } | } | ||||
| else { | else { | ||||
| PRINT_ERR("\tLoop %u has invalid edge reference (%u)\n", sp->loopstart + j, ml->e); | PRINT_ERR("\tLoop %u has invalid edge reference (%u)", sp->loopstart + j, ml->e); | ||||
| sp->invalid = true; | sp->invalid = true; | ||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| me = &medges[ml->e]; | me = &medges[ml->e]; | ||||
| if (IS_REMOVED_EDGE(me) || !((me->v1 == v1 && me->v2 == v2) || (me->v1 == v2 && me->v2 == v1))) { | if (IS_REMOVED_EDGE(me) || !((me->v1 == v1 && me->v2 == v2) || (me->v1 == v2 && me->v2 == v1))) { | ||||
| /* The pointed edge is invalid (tagged as removed, or vert idx mismatch), | /* The pointed edge is invalid (tagged as removed, or vert idx mismatch), | ||||
| * and we already know from previous test that a valid one exists, use it (if allowed)! */ | * and we already know from previous test that a valid one exists, use it (if allowed)! */ | ||||
| if (do_fixes) { | if (do_fixes) { | ||||
| int prev_e = ml->e; | int prev_e = ml->e; | ||||
| ml->e = POINTER_AS_INT(BLI_edgehash_lookup(edge_hash, v1, v2)); | ml->e = POINTER_AS_INT(BLI_edgehash_lookup(edge_hash, v1, v2)); | ||||
| fix_flag.loops_edge = true; | fix_flag.loops_edge = true; | ||||
| PRINT_ERR("\tPoly %u has invalid edge reference (%d, is_removed: %d), fixed using edge %u\n", | PRINT_ERR("\tPoly %u has invalid edge reference (%d, is_removed: %d), fixed using edge %u", | ||||
| sp->index, prev_e, IS_REMOVED_EDGE(me), ml->e); | sp->index, prev_e, IS_REMOVED_EDGE(me), ml->e); | ||||
| } | } | ||||
| else { | else { | ||||
| PRINT_ERR("\tPoly %u has invalid edge reference (%u)\n", sp->index, ml->e); | PRINT_ERR("\tPoly %u has invalid edge reference (%u)", sp->index, ml->e); | ||||
| sp->invalid = true; | sp->invalid = true; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| if (!sp->invalid) { | if (!sp->invalid) { | ||||
| /* Needed for checking polys using same verts below. */ | /* Needed for checking polys using same verts below. */ | ||||
| Show All 14 Lines | for (i = 1; i < totpoly; i++, sp++) { | ||||
| if (sp->invalid) { | if (sp->invalid) { | ||||
| /* break, because all known invalid polys have been put at the end by qsort with search_poly_cmp. */ | /* break, because all known invalid polys have been put at the end by qsort with search_poly_cmp. */ | ||||
| break; | break; | ||||
| } | } | ||||
| /* Test same polys. */ | /* Test same polys. */ | ||||
| if ((p1_nv == p2_nv) && (memcmp(p1_v, p2_v, p1_nv * sizeof(*p1_v)) == 0)) { | if ((p1_nv == p2_nv) && (memcmp(p1_v, p2_v, p1_nv * sizeof(*p1_v)) == 0)) { | ||||
| if (do_verbose) { | if (do_verbose) { | ||||
| // TODO: convert list to string | |||||
| PRINT_ERR("\tPolys %u and %u use same vertices (%d", | PRINT_ERR("\tPolys %u and %u use same vertices (%d", | ||||
| prev_sp->index, sp->index, *p1_v); | prev_sp->index, sp->index, *p1_v); | ||||
| for (j = 1; j < p1_nv; j++) | for (j = 1; j < p1_nv; j++) | ||||
| PRINT_ERR(", %d", p1_v[j]); | PRINT_ERR(", %d", p1_v[j]); | ||||
| PRINT_ERR("), considering poly %u as invalid.\n", sp->index); | PRINT_ERR("), considering poly %u as invalid.", sp->index); | ||||
| } | } | ||||
| else { | else { | ||||
| is_valid = false; | is_valid = false; | ||||
| } | } | ||||
| sp->invalid = true; | sp->invalid = true; | ||||
| } | } | ||||
| else { | else { | ||||
| prev_sp = sp; | prev_sp = sp; | ||||
| Show All 21 Lines | for (i = 0; i < totpoly; i++, sp++) { | ||||
| * another valid poly! */ | * another valid poly! */ | ||||
| } | } | ||||
| } | } | ||||
| /* Test loops users. */ | /* Test loops users. */ | ||||
| else { | else { | ||||
| /* Unused loops. */ | /* Unused loops. */ | ||||
| if (prev_end < sp->loopstart) { | if (prev_end < sp->loopstart) { | ||||
| for (j = prev_end, ml = &mloops[prev_end]; j < sp->loopstart; j++, ml++) { | for (j = prev_end, ml = &mloops[prev_end]; j < sp->loopstart; j++, ml++) { | ||||
| PRINT_ERR("\tLoop %u is unused.\n", j); | PRINT_ERR("\tLoop %u is unused.", j); | ||||
| if (do_fixes) | if (do_fixes) | ||||
| REMOVE_LOOP_TAG(ml); | REMOVE_LOOP_TAG(ml); | ||||
| } | } | ||||
| prev_end = sp->loopstart + sp->numverts; | prev_end = sp->loopstart + sp->numverts; | ||||
| prev_sp = sp; | prev_sp = sp; | ||||
| } | } | ||||
| /* Multi-used loops. */ | /* Multi-used loops. */ | ||||
| else if (prev_end > sp->loopstart) { | else if (prev_end > sp->loopstart) { | ||||
| PRINT_ERR("\tPolys %u and %u share loops from %d to %d, considering poly %u as invalid.\n", | PRINT_ERR("\tPolys %u and %u share loops from %d to %d, considering poly %u as invalid.", | ||||
| prev_sp->index, sp->index, sp->loopstart, prev_end, sp->index); | prev_sp->index, sp->index, sp->loopstart, prev_end, sp->index); | ||||
| if (do_fixes) { | if (do_fixes) { | ||||
| REMOVE_POLY_TAG((&mpolys[sp->index])); | REMOVE_POLY_TAG((&mpolys[sp->index])); | ||||
| /* DO NOT REMOVE ITS LOOPS!!! | /* DO NOT REMOVE ITS LOOPS!!! | ||||
| * They might be used by some next, valid poly! | * They might be used by some next, valid poly! | ||||
| * Just not updating prev_end/prev_sp vars is enough to ensure the loops | * Just not updating prev_end/prev_sp vars is enough to ensure the loops | ||||
| * effectively no more needed will be marked as "to be removed"! */ | * effectively no more needed will be marked as "to be removed"! */ | ||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| prev_end = sp->loopstart + sp->numverts; | prev_end = sp->loopstart + sp->numverts; | ||||
| prev_sp = sp; | prev_sp = sp; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* We may have some remaining unused loops to get rid of! */ | /* We may have some remaining unused loops to get rid of! */ | ||||
| if (prev_end < totloop) { | if (prev_end < totloop) { | ||||
| for (j = prev_end, ml = &mloops[prev_end]; j < totloop; j++, ml++) { | for (j = prev_end, ml = &mloops[prev_end]; j < totloop; j++, ml++) { | ||||
| PRINT_ERR("\tLoop %u is unused.\n", j); | PRINT_ERR("\tLoop %u is unused.", j); | ||||
| if (do_fixes) | if (do_fixes) | ||||
| REMOVE_LOOP_TAG(ml); | REMOVE_LOOP_TAG(ml); | ||||
| } | } | ||||
| } | } | ||||
| MEM_freeN(sort_polys); | MEM_freeN(sort_polys); | ||||
| } | } | ||||
| BLI_edgehash_free(edge_hash, NULL); | BLI_edgehash_free(edge_hash, NULL); | ||||
| /* fix deform verts */ | /* fix deform verts */ | ||||
| if (dverts) { | if (dverts) { | ||||
| MDeformVert *dv; | MDeformVert *dv; | ||||
| for (i = 0, dv = dverts; i < totvert; i++, dv++) { | for (i = 0, dv = dverts; i < totvert; i++, dv++) { | ||||
| MDeformWeight *dw; | MDeformWeight *dw; | ||||
| for (j = 0, dw = dv->dw; j < dv->totweight; j++, dw++) { | for (j = 0, dw = dv->dw; j < dv->totweight; j++, dw++) { | ||||
| /* note, greater than max defgroups is accounted for in our code, but not < 0 */ | /* note, greater than max defgroups is accounted for in our code, but not < 0 */ | ||||
| if (!isfinite(dw->weight)) { | if (!isfinite(dw->weight)) { | ||||
| PRINT_ERR("\tVertex deform %u, group %d has weight: %f\n", i, dw->def_nr, dw->weight); | PRINT_ERR("\tVertex deform %u, group %d has weight: %f", i, dw->def_nr, dw->weight); | ||||
| if (do_fixes) { | if (do_fixes) { | ||||
| dw->weight = 0.0f; | dw->weight = 0.0f; | ||||
| fix_flag.verts_weight = true; | fix_flag.verts_weight = true; | ||||
| } | } | ||||
| } | } | ||||
| else if (dw->weight < 0.0f || dw->weight > 1.0f) { | else if (dw->weight < 0.0f || dw->weight > 1.0f) { | ||||
| PRINT_ERR("\tVertex deform %u, group %d has weight: %f\n", i, dw->def_nr, dw->weight); | PRINT_ERR("\tVertex deform %u, group %d has weight: %f", i, dw->def_nr, dw->weight); | ||||
| if (do_fixes) { | if (do_fixes) { | ||||
| CLAMP(dw->weight, 0.0f, 1.0f); | CLAMP(dw->weight, 0.0f, 1.0f); | ||||
| fix_flag.verts_weight = true; | fix_flag.verts_weight = true; | ||||
| } | } | ||||
| } | } | ||||
| if (dw->def_nr < 0) { | if (dw->def_nr < 0) { | ||||
| PRINT_ERR("\tVertex deform %u, has invalid group %d\n", i, dw->def_nr); | PRINT_ERR("\tVertex deform %u, has invalid group %d", i, dw->def_nr); | ||||
| if (do_fixes) { | if (do_fixes) { | ||||
| defvert_remove_group(dv, dw); | defvert_remove_group(dv, dw); | ||||
| fix_flag.verts_weight = true; | fix_flag.verts_weight = true; | ||||
| if (dv->dw) { | if (dv->dw) { | ||||
| /* re-allocated, the new values compensate for stepping | /* re-allocated, the new values compensate for stepping | ||||
| * within the for loop and may not be valid */ | * within the for loop and may not be valid */ | ||||
| j--; | j--; | ||||
| ▲ Show 20 Lines • Show All 192 Lines • ▼ Show 20 Lines | |||||
| * \returns true if a change is made. | * \returns true if a change is made. | ||||
| */ | */ | ||||
| bool BKE_mesh_validate(Mesh *me, const bool do_verbose, const bool cddata_check_mask) | bool BKE_mesh_validate(Mesh *me, const bool do_verbose, const bool cddata_check_mask) | ||||
| { | { | ||||
| bool is_valid = true; | bool is_valid = true; | ||||
| bool changed; | bool changed; | ||||
| if (do_verbose) { | if (do_verbose) { | ||||
| printf("MESH: %s\n", me->id.name + 2); | CLOG_INFO(&LOG, 2, "MESH: %s", me->id.name + 2); | ||||
| } | } | ||||
| is_valid &= BKE_mesh_validate_all_customdata( | is_valid &= BKE_mesh_validate_all_customdata( | ||||
| &me->vdata, me->totvert, | &me->vdata, me->totvert, | ||||
| &me->edata, me->totedge, | &me->edata, me->totedge, | ||||
| &me->ldata, me->totloop, | &me->ldata, me->totloop, | ||||
| &me->pdata, me->totpoly, | &me->pdata, me->totpoly, | ||||
| cddata_check_mask, | cddata_check_mask, | ||||
| ▲ Show 20 Lines • Show All 587 Lines • Show Last 20 Lines | |||||