Changeset View
Changeset View
Standalone View
Standalone View
source/blender/blenkernel/intern/mesh_validate.cc
| Show First 20 Lines • Show All 92 Lines • ▼ Show 20 Lines | |||||
| /* NOLINTNEXTLINE: readability-function-size */ | /* NOLINTNEXTLINE: readability-function-size */ | ||||
| bool BKE_mesh_validate_arrays(Mesh *mesh, | bool BKE_mesh_validate_arrays(Mesh *mesh, | ||||
| float (*vert_positions)[3], | float (*vert_positions)[3], | ||||
| uint totvert, | uint totvert, | ||||
| MEdge *medges, | MEdge *medges, | ||||
| uint totedge, | uint totedge, | ||||
| MFace *mfaces, | MFace *mfaces, | ||||
| uint totface, | uint totface, | ||||
| MLoop *mloops, | int *corner_verts, | ||||
| int *corner_edges, | |||||
| uint totloop, | uint totloop, | ||||
| MPoly *mpolys, | MPoly *mpolys, | ||||
| uint totpoly, | uint totpoly, | ||||
| MDeformVert *dverts, /* assume totvert length */ | MDeformVert *dverts, /* assume totvert length */ | ||||
| const bool do_verbose, | const bool do_verbose, | ||||
| const bool do_fixes, | const bool do_fixes, | ||||
| bool *r_changed) | bool *r_changed) | ||||
| { | { | ||||
| #define REMOVE_EDGE_TAG(_me) \ | #define REMOVE_EDGE_TAG(_me) \ | ||||
| { \ | { \ | ||||
| _me->v2 = _me->v1; \ | _me->v2 = _me->v1; \ | ||||
| free_flag.edges = do_fixes; \ | free_flag.edges = do_fixes; \ | ||||
| } \ | } \ | ||||
| (void)0 | (void)0 | ||||
| #define IS_REMOVED_EDGE(_me) (_me->v2 == _me->v1) | #define IS_REMOVED_EDGE(_me) (_me->v2 == _me->v1) | ||||
| #define REMOVE_LOOP_TAG(_ml) \ | #define REMOVE_LOOP_TAG(corner) \ | ||||
| { \ | { \ | ||||
| _ml->e = INVALID_LOOP_EDGE_MARKER; \ | corner_edges[corner] = INVALID_LOOP_EDGE_MARKER; \ | ||||
| free_flag.polyloops = do_fixes; \ | free_flag.polyloops = do_fixes; \ | ||||
| } \ | } \ | ||||
| (void)0 | (void)0 | ||||
| #define REMOVE_POLY_TAG(_mp) \ | #define REMOVE_POLY_TAG(_mp) \ | ||||
| { \ | { \ | ||||
| _mp->totloop *= -1; \ | _mp->totloop *= -1; \ | ||||
| free_flag.polyloops = do_fixes; \ | free_flag.polyloops = do_fixes; \ | ||||
| } \ | } \ | ||||
| (void)0 | (void)0 | ||||
| blender::bke::AttributeWriter<int> material_indices = | blender::bke::AttributeWriter<int> material_indices = | ||||
| mesh->attributes_for_write().lookup_for_write<int>("material_index"); | mesh->attributes_for_write().lookup_for_write<int>("material_index"); | ||||
| blender::MutableVArraySpan<int> material_indices_span(material_indices.varray); | blender::MutableVArraySpan<int> material_indices_span(material_indices.varray); | ||||
| MEdge *me; | MEdge *me; | ||||
| MLoop *ml; | |||||
| MPoly *mp; | MPoly *mp; | ||||
| uint i, j; | uint i, j; | ||||
| int *v; | int *v; | ||||
| bool is_valid = true; | bool is_valid = true; | ||||
| union { | union { | ||||
| struct { | struct { | ||||
| ▲ Show 20 Lines • Show All 184 Lines • ▼ Show 20 Lines | |||||
| sp->invalid = false; | sp->invalid = false; | ||||
| sp->verts = v = (int *)MEM_mallocN(sizeof(int) * mp->totloop, "Vert idx of SortPoly"); | sp->verts = v = (int *)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 | /* 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, | * 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. */ | * 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; j < mp->totloop; j++) { | ||||
| if (ml->v < totvert) { | const int vert = corner_verts[sp->loopstart + j]; | ||||
| BLI_BITMAP_DISABLE(vert_tag, ml->v); | if (vert < totvert) { | ||||
| BLI_BITMAP_DISABLE(vert_tag, vert); | |||||
| } | } | ||||
| } | } | ||||
| /* 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; j < mp->totloop; j++, v++) { | ||||
| if (ml->v >= totvert) { | const int vert = corner_verts[sp->loopstart + j]; | ||||
| if (vert >= totvert) { | |||||
| /* Invalid vert idx. */ | /* Invalid vert idx. */ | ||||
| PRINT_ERR("\tLoop %u has invalid vert reference (%u)", sp->loopstart + j, ml->v); | PRINT_ERR("\tLoop %u has invalid vert reference (%d)", sp->loopstart + j, vert); | ||||
| sp->invalid = true; | sp->invalid = true; | ||||
| } | } | ||||
| else if (BLI_BITMAP_TEST(vert_tag, ml->v)) { | else if (BLI_BITMAP_TEST(vert_tag, vert)) { | ||||
| PRINT_ERR("\tPoly %u has duplicated vert reference at corner (%u)", i, j); | PRINT_ERR("\tPoly %u has duplicated vert reference at corner (%u)", i, j); | ||||
| sp->invalid = true; | sp->invalid = true; | ||||
| } | } | ||||
| else { | else { | ||||
| BLI_BITMAP_ENABLE(vert_tag, ml->v); | BLI_BITMAP_ENABLE(vert_tag, vert); | ||||
| } | } | ||||
| *v = ml->v; | *v = vert; | ||||
| } | } | ||||
| 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; j < mp->totloop; j++) { | ||||
| v1 = ml->v; | const int corner = sp->loopstart + j; | ||||
| v2 = mloops[sp->loopstart + (j + 1) % mp->totloop].v; | const int vert = corner_verts[corner]; | ||||
| const int edge = corner_edges[corner]; | |||||
| v1 = vert; | |||||
| v2 = corner_verts[sp->loopstart + (j + 1) % mp->totloop]; | |||||
| 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)", 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 (edge >= 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 = edge; | ||||
| ml->e = POINTER_AS_INT(BLI_edgehash_lookup(edge_hash, v1, v2)); | corner_edges[corner] = 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", | PRINT_ERR("\tLoop %d has invalid edge reference (%d), fixed using edge %d", | ||||
| sp->loopstart + j, | corner, | ||||
| prev_e, | prev_e, | ||||
| ml->e); | corner_edges[corner]); | ||||
| } | } | ||||
| else { | else { | ||||
| PRINT_ERR("\tLoop %u has invalid edge reference (%u)", sp->loopstart + j, ml->e); | PRINT_ERR("\tLoop %d has invalid edge reference (%d)", corner, edge); | ||||
| sp->invalid = true; | sp->invalid = true; | ||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| me = &medges[ml->e]; | me = &medges[edge]; | ||||
| if (IS_REMOVED_EDGE(me) || | if (IS_REMOVED_EDGE(me) || | ||||
| !((me->v1 == v1 && me->v2 == v2) || (me->v1 == v2 && me->v2 == v1))) { | !((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, | * and we already know from previous test that a valid one exists, | ||||
| * use it (if allowed)! */ | * use it (if allowed)! */ | ||||
| if (do_fixes) { | if (do_fixes) { | ||||
| int prev_e = ml->e; | int prev_e = edge; | ||||
| ml->e = POINTER_AS_INT(BLI_edgehash_lookup(edge_hash, v1, v2)); | corner_edges[corner] = POINTER_AS_INT(BLI_edgehash_lookup(edge_hash, v1, v2)); | ||||
| fix_flag.loops_edge = true; | fix_flag.loops_edge = true; | ||||
| PRINT_ERR( | PRINT_ERR( | ||||
| "\tPoly %u has invalid edge reference (%d, is_removed: %d), fixed using edge " | "\tPoly %u has invalid edge reference (%d, is_removed: %d), fixed using edge " | ||||
| "%u", | "%d", | ||||
| sp->index, | sp->index, | ||||
| prev_e, | prev_e, | ||||
| IS_REMOVED_EDGE(me), | IS_REMOVED_EDGE(me), | ||||
| ml->e); | corner_edges[corner]); | ||||
| } | } | ||||
| else { | else { | ||||
| PRINT_ERR("\tPoly %u has invalid edge reference (%u)", sp->index, ml->e); | PRINT_ERR("\tPoly %u has invalid edge reference (%d)", sp->index, edge); | ||||
| 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 20 Lines • Show All 61 Lines • ▼ Show 20 Lines | |||||
| * iterations, and we don't want to remove some loops that may be used by | * iterations, and we don't want to remove some loops that may be used by | ||||
| * 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++) { | int corner; | ||||
| for (j = prev_end, corner = prev_end; j < sp->loopstart; j++, corner++) { | |||||
| PRINT_ERR("\tLoop %u is unused.", j); | PRINT_ERR("\tLoop %u is unused.", j); | ||||
| if (do_fixes) { | if (do_fixes) { | ||||
| REMOVE_LOOP_TAG(ml); | REMOVE_LOOP_TAG(corner); | ||||
| } | } | ||||
| } | } | ||||
| 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.", | PRINT_ERR("\tPolys %u and %u share loops from %d to %d, considering poly %u as invalid.", | ||||
| Show All 13 Lines | |||||
| 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++) { | int corner; | ||||
| for (j = prev_end, corner = prev_end; j < totloop; j++, corner++) { | |||||
| PRINT_ERR("\tLoop %u is unused.", j); | PRINT_ERR("\tLoop %u is unused.", j); | ||||
| if (do_fixes) { | if (do_fixes) { | ||||
| REMOVE_LOOP_TAG(ml); | REMOVE_LOOP_TAG(corner); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| MEM_freeN(sort_polys); | MEM_freeN(sort_polys); | ||||
| } | } | ||||
| BLI_edgehash_free(edge_hash, nullptr); | BLI_edgehash_free(edge_hash, nullptr); | ||||
| ▲ Show 20 Lines • Show All 184 Lines • ▼ Show 20 Lines | |||||
| me->totpoly, | me->totpoly, | ||||
| cddata_check_mask, | cddata_check_mask, | ||||
| do_verbose, | do_verbose, | ||||
| true, | true, | ||||
| &changed); | &changed); | ||||
| MutableSpan<float3> positions = me->vert_positions_for_write(); | MutableSpan<float3> positions = me->vert_positions_for_write(); | ||||
| MutableSpan<MEdge> edges = me->edges_for_write(); | MutableSpan<MEdge> edges = me->edges_for_write(); | ||||
| MutableSpan<MPoly> polys = me->polys_for_write(); | MutableSpan<MPoly> polys = me->polys_for_write(); | ||||
| MutableSpan<MLoop> loops = me->loops_for_write(); | |||||
| BKE_mesh_validate_arrays( | BKE_mesh_validate_arrays( | ||||
| me, | me, | ||||
| reinterpret_cast<float(*)[3]>(positions.data()), | reinterpret_cast<float(*)[3]>(positions.data()), | ||||
| positions.size(), | positions.size(), | ||||
| edges.data(), | edges.data(), | ||||
| edges.size(), | edges.size(), | ||||
| (MFace *)CustomData_get_layer_for_write(&me->fdata, CD_MFACE, me->totface), | (MFace *)CustomData_get_layer_for_write(&me->fdata, CD_MFACE, me->totface), | ||||
| me->totface, | me->totface, | ||||
| loops.data(), | me->corner_verts_for_write().data(), | ||||
| loops.size(), | me->corner_edges_for_write().data(), | ||||
| me->totloop, | |||||
| polys.data(), | polys.data(), | ||||
| polys.size(), | polys.size(), | ||||
| me->deform_verts_for_write().data(), | me->deform_verts_for_write().data(), | ||||
| do_verbose, | do_verbose, | ||||
| true, | true, | ||||
| &changed); | &changed); | ||||
| if (changed) { | if (changed) { | ||||
| Show All 24 Lines | |||||
| false, /* setting mask here isn't useful, gives false positives */ | false, /* setting mask here isn't useful, gives false positives */ | ||||
| do_verbose, | do_verbose, | ||||
| do_fixes, | do_fixes, | ||||
| &changed); | &changed); | ||||
| MutableSpan<float3> positions = me->vert_positions_for_write(); | MutableSpan<float3> positions = me->vert_positions_for_write(); | ||||
| MutableSpan<MEdge> edges = me->edges_for_write(); | MutableSpan<MEdge> edges = me->edges_for_write(); | ||||
| MutableSpan<MPoly> polys = me->polys_for_write(); | MutableSpan<MPoly> polys = me->polys_for_write(); | ||||
| MutableSpan<MLoop> loops = me->loops_for_write(); | |||||
| is_valid &= BKE_mesh_validate_arrays( | is_valid &= BKE_mesh_validate_arrays( | ||||
| me, | me, | ||||
| reinterpret_cast<float(*)[3]>(positions.data()), | reinterpret_cast<float(*)[3]>(positions.data()), | ||||
| positions.size(), | positions.size(), | ||||
| edges.data(), | edges.data(), | ||||
| edges.size(), | edges.size(), | ||||
| (MFace *)CustomData_get_layer_for_write(&me->fdata, CD_MFACE, me->totface), | (MFace *)CustomData_get_layer_for_write(&me->fdata, CD_MFACE, me->totface), | ||||
| me->totface, | me->totface, | ||||
| loops.data(), | me->corner_verts_for_write().data(), | ||||
| loops.size(), | me->corner_edges_for_write().data(), | ||||
| me->totloop, | |||||
| polys.data(), | polys.data(), | ||||
| polys.size(), | polys.size(), | ||||
| me->deform_verts_for_write().data(), | me->deform_verts_for_write().data(), | ||||
| do_verbose, | do_verbose, | ||||
| do_fixes, | do_fixes, | ||||
| &changed); | &changed); | ||||
| if (!me->runtime->vert_normals_dirty) { | if (!me->runtime->vert_normals_dirty) { | ||||
| ▲ Show 20 Lines • Show All 59 Lines • ▼ Show 20 Lines | |||||
| CustomData_free_elem(&me->fdata, b, a - b); | CustomData_free_elem(&me->fdata, b, a - b); | ||||
| me->totface = b; | me->totface = b; | ||||
| } | } | ||||
| } | } | ||||
| void BKE_mesh_strip_loose_polysloops(Mesh *me) | void BKE_mesh_strip_loose_polysloops(Mesh *me) | ||||
| { | { | ||||
| MutableSpan<MPoly> polys = me->polys_for_write(); | MutableSpan<MPoly> polys = me->polys_for_write(); | ||||
| MutableSpan<MLoop> loops = me->loops_for_write(); | MutableSpan<int> corner_edges = me->corner_edges_for_write(); | ||||
| MPoly *p; | MPoly *p; | ||||
| MLoop *l; | |||||
| int a, b; | int a, b; | ||||
| /* New loops idx! */ | /* New loops idx! */ | ||||
| int *new_idx = (int *)MEM_mallocN(sizeof(int) * me->totloop, __func__); | int *new_idx = (int *)MEM_mallocN(sizeof(int) * me->totloop, __func__); | ||||
| for (a = b = 0, p = polys.data(); a < me->totpoly; a++, p++) { | for (a = b = 0, p = polys.data(); a < me->totpoly; a++, p++) { | ||||
| bool invalid = false; | bool invalid = false; | ||||
| int i = p->loopstart; | int i = p->loopstart; | ||||
| int stop = i + p->totloop; | int stop = i + p->totloop; | ||||
| if (stop > me->totloop || stop < i || p->loopstart < 0) { | if (stop > me->totloop || stop < i || p->loopstart < 0) { | ||||
| invalid = true; | invalid = true; | ||||
| } | } | ||||
| else { | else { | ||||
| l = &loops[i]; | |||||
| i = stop - i; | |||||
| /* If one of the poly's loops is invalid, the whole poly is invalid! */ | /* If one of the poly's loops is invalid, the whole poly is invalid! */ | ||||
| for (; i--; l++) { | if (corner_edges.slice(p->loopstart, p->totloop) | ||||
| if (l->e == INVALID_LOOP_EDGE_MARKER) { | .as_span() | ||||
| invalid = true; | .contains(INVALID_LOOP_EDGE_MARKER)) { | ||||
| break; | invalid = true; | ||||
| } | |||||
| } | } | ||||
| } | } | ||||
| if (p->totloop >= 3 && !invalid) { | if (p->totloop >= 3 && !invalid) { | ||||
| if (a != b) { | if (a != b) { | ||||
| memcpy(&polys[b], p, sizeof(polys[b])); | memcpy(&polys[b], p, sizeof(polys[b])); | ||||
| CustomData_copy_data(&me->pdata, &me->pdata, a, b, 1); | CustomData_copy_data(&me->pdata, &me->pdata, a, b, 1); | ||||
| } | } | ||||
| b++; | b++; | ||||
| } | } | ||||
| } | } | ||||
| if (a != b) { | if (a != b) { | ||||
| CustomData_free_elem(&me->pdata, b, a - b); | CustomData_free_elem(&me->pdata, b, a - b); | ||||
| me->totpoly = b; | me->totpoly = b; | ||||
| } | } | ||||
| /* And now, get rid of invalid loops. */ | /* And now, get rid of invalid loops. */ | ||||
| for (a = b = 0, l = loops.data(); a < me->totloop; a++, l++) { | int corner = 0; | ||||
| if (l->e != INVALID_LOOP_EDGE_MARKER) { | for (a = b = 0; a < me->totloop; a++, corner++) { | ||||
| if (corner_edges[corner] != INVALID_LOOP_EDGE_MARKER) { | |||||
| if (a != b) { | if (a != b) { | ||||
| memcpy(&loops[b], l, sizeof(loops[b])); | |||||
| CustomData_copy_data(&me->ldata, &me->ldata, a, b, 1); | CustomData_copy_data(&me->ldata, &me->ldata, a, b, 1); | ||||
| } | } | ||||
| new_idx[a] = b; | new_idx[a] = b; | ||||
| b++; | b++; | ||||
| } | } | ||||
| else { | else { | ||||
| /* XXX Theoretically, we should be able to not do this, as no remaining poly | /* XXX Theoretically, we should be able to not do this, as no remaining poly | ||||
| * should use any stripped loop. But for security's sake... */ | * should use any stripped loop. But for security's sake... */ | ||||
| Show All 37 Lines | |||||
| if (a != b) { | if (a != b) { | ||||
| CustomData_free_elem(&me->edata, b, a - b); | CustomData_free_elem(&me->edata, b, a - b); | ||||
| me->totedge = b; | me->totedge = b; | ||||
| } | } | ||||
| /* And now, update loops' edge indices. */ | /* And now, update loops' edge indices. */ | ||||
| /* XXX We hope no loop was pointing to a striped edge! | /* XXX We hope no loop was pointing to a striped edge! | ||||
| * Else, its e will be set to INVALID_LOOP_EDGE_MARKER :/ */ | * Else, its e will be set to INVALID_LOOP_EDGE_MARKER :/ */ | ||||
| MutableSpan<MLoop> loops = me->loops_for_write(); | MutableSpan<int> corner_edges = me->corner_edges_for_write(); | ||||
| for (MLoop &loop : loops) { | for (const int i : corner_edges.index_range()) { | ||||
| loop.e = new_idx[loop.e]; | corner_edges[i] = new_idx[corner_edges[i]]; | ||||
| } | } | ||||
| MEM_freeN(new_idx); | MEM_freeN(new_idx); | ||||
| } | } | ||||
| /** \} */ | /** \} */ | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| ▲ Show 20 Lines • Show All 53 Lines • Show Last 20 Lines | |||||