diff options
Diffstat (limited to 'source/blender/editors/transform/transform_convert_mesh.c')
-rw-r--r-- | source/blender/editors/transform/transform_convert_mesh.c | 1413 |
1 files changed, 793 insertions, 620 deletions
diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c index 598604eac0d..2e0f7a3a113 100644 --- a/source/blender/editors/transform/transform_convert_mesh.c +++ b/source/blender/editors/transform/transform_convert_mesh.c @@ -28,6 +28,7 @@ #include "BLI_alloca.h" #include "BLI_bitmap.h" +#include "BLI_ghash.h" #include "BLI_linklist_stack.h" #include "BLI_math.h" #include "BLI_memarena.h" @@ -50,10 +51,13 @@ /* Own include. */ #include "transform_convert.h" -/* Used for both mirror epsilon and TD_MIRROR_EDGE_ */ -#define TRANSFORM_MAXDIST_MIRROR 0.00002f +#define USE_FACE_SUBSTITUTE + +/* -------------------------------------------------------------------- */ +/** \name Island Creation + * + * \{ */ -/* when transforming islands */ struct TransIslandData { float (*center)[3]; float (*axismtx)[3][3]; @@ -61,8 +65,186 @@ struct TransIslandData { int *island_vert_map; }; +static void editmesh_islands_info_calc(BMEditMesh *em, + const bool calc_single_islands, + const bool calc_island_center, + const bool calc_island_axismtx, + struct TransIslandData *r_island_data) +{ + BMesh *bm = em->bm; + char htype; + char itype; + int i; + + /* group vars */ + float(*center)[3] = NULL; + float(*axismtx)[3][3] = NULL; + int *groups_array; + int(*group_index)[2]; + int group_tot; + void **ele_array; + + int *vert_map; + + if (em->selectmode & (SCE_SELECT_VERTEX | SCE_SELECT_EDGE)) { + groups_array = MEM_mallocN(sizeof(*groups_array) * bm->totedgesel, __func__); + group_tot = BM_mesh_calc_edge_groups( + bm, groups_array, &group_index, NULL, NULL, BM_ELEM_SELECT); + + htype = BM_EDGE; + itype = BM_VERTS_OF_EDGE; + } + else { /* (bm->selectmode & SCE_SELECT_FACE) */ + groups_array = MEM_mallocN(sizeof(*groups_array) * bm->totfacesel, __func__); + group_tot = BM_mesh_calc_face_groups( + bm, groups_array, &group_index, NULL, NULL, BM_ELEM_SELECT, BM_VERT); + + htype = BM_FACE; + itype = BM_VERTS_OF_FACE; + } + + if (calc_island_center) { + center = MEM_mallocN(sizeof(*center) * group_tot, __func__); + } + + if (calc_island_axismtx) { + axismtx = MEM_mallocN(sizeof(*axismtx) * group_tot, __func__); + } + + vert_map = MEM_mallocN(sizeof(*vert_map) * bm->totvert, __func__); + /* we shouldn't need this, but with incorrect selection flushing + * its possible we have a selected vertex that's not in a face, + * for now best not crash in that case. */ + copy_vn_i(vert_map, bm->totvert, -1); + + BM_mesh_elem_table_ensure(bm, htype); + ele_array = (htype == BM_FACE) ? (void **)bm->ftable : (void **)bm->etable; + + BM_mesh_elem_index_ensure(bm, BM_VERT); + + /* may be an edge OR a face array */ + for (i = 0; i < group_tot; i++) { + BMEditSelection ese = {NULL}; + + const int fg_sta = group_index[i][0]; + const int fg_len = group_index[i][1]; + float co[3], no[3], tangent[3]; + int j; + + zero_v3(co); + zero_v3(no); + zero_v3(tangent); + + ese.htype = htype; + + /* loop on each face or edge in this group: + * - assign r_vert_map + * - calculate (co, no) + */ + for (j = 0; j < fg_len; j++) { + ese.ele = ele_array[groups_array[fg_sta + j]]; + + if (center) { + float tmp_co[3]; + BM_editselection_center(&ese, tmp_co); + add_v3_v3(co, tmp_co); + } + + if (axismtx) { + float tmp_no[3], tmp_tangent[3]; + BM_editselection_normal(&ese, tmp_no); + BM_editselection_plane(&ese, tmp_tangent); + add_v3_v3(no, tmp_no); + add_v3_v3(tangent, tmp_tangent); + } + + { + /* setup vertex map */ + BMIter iter; + BMVert *v; + + /* connected edge-verts */ + BM_ITER_ELEM (v, &iter, ese.ele, itype) { + vert_map[BM_elem_index_get(v)] = i; + } + } + } + + if (center) { + mul_v3_v3fl(center[i], co, 1.0f / (float)fg_len); + } + + if (axismtx) { + if (createSpaceNormalTangent(axismtx[i], no, tangent)) { + /* pass */ + } + else { + if (normalize_v3(no) != 0.0f) { + axis_dominant_v3_to_m3(axismtx[i], no); + invert_m3(axismtx[i]); + } + else { + unit_m3(axismtx[i]); + } + } + } + } + + MEM_freeN(groups_array); + MEM_freeN(group_index); + + /* for PET we need islands of 1 so connected vertices can use it with V3D_AROUND_LOCAL_ORIGINS */ + if (calc_single_islands) { + BMIter viter; + BMVert *v; + int group_tot_single = 0; + + BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, i) { + if (BM_elem_flag_test(v, BM_ELEM_SELECT) && (vert_map[i] == -1)) { + group_tot_single += 1; + } + } + + if (group_tot_single != 0) { + if (center) { + center = MEM_reallocN(center, sizeof(*center) * (group_tot + group_tot_single)); + } + if (axismtx) { + axismtx = MEM_reallocN(axismtx, sizeof(*axismtx) * (group_tot + group_tot_single)); + } + + BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, i) { + if (BM_elem_flag_test(v, BM_ELEM_SELECT) && (vert_map[i] == -1)) { + vert_map[i] = group_tot; + if (center) { + copy_v3_v3(center[group_tot], v->co); + } + if (axismtx) { + if (is_zero_v3(v->no) != 0.0f) { + axis_dominant_v3_to_m3(axismtx[group_tot], v->no); + invert_m3(axismtx[group_tot]); + } + else { + unit_m3(axismtx[group_tot]); + } + } + + group_tot += 1; + } + } + } + } + + r_island_data->axismtx = axismtx; + r_island_data->center = center; + r_island_data->island_tot = group_tot; + r_island_data->island_vert_map = vert_map; +} + +/** \} */ + /* -------------------------------------------------------------------- */ -/** \name Edit Mesh Verts Transform Creation +/** \name Connectivity Distance for Proportional Editing * * \{ */ @@ -243,171 +425,25 @@ static void editmesh_set_connectivity_distance(BMesh *bm, } } -static void editmesh_islands_info_calc(BMEditMesh *em, - const bool calc_single_islands, - const bool calc_island_axismtx, - struct TransIslandData *r_island_data) -{ - BMesh *bm = em->bm; - char htype; - char itype; - int i; - - /* group vars */ - float(*center)[3]; - float(*axismtx)[3][3] = NULL; - int *groups_array; - int(*group_index)[2]; - int group_tot; - void **ele_array; - - int *vert_map; - - if (em->selectmode & (SCE_SELECT_VERTEX | SCE_SELECT_EDGE)) { - groups_array = MEM_mallocN(sizeof(*groups_array) * bm->totedgesel, __func__); - group_tot = BM_mesh_calc_edge_groups( - bm, groups_array, &group_index, NULL, NULL, BM_ELEM_SELECT); - - htype = BM_EDGE; - itype = BM_VERTS_OF_EDGE; - } - else { /* (bm->selectmode & SCE_SELECT_FACE) */ - groups_array = MEM_mallocN(sizeof(*groups_array) * bm->totfacesel, __func__); - group_tot = BM_mesh_calc_face_groups( - bm, groups_array, &group_index, NULL, NULL, BM_ELEM_SELECT, BM_VERT); - - htype = BM_FACE; - itype = BM_VERTS_OF_FACE; - } - - center = MEM_mallocN(sizeof(*center) * group_tot, __func__); - - if (calc_island_axismtx) { - axismtx = MEM_mallocN(sizeof(*axismtx) * group_tot, __func__); - } - - vert_map = MEM_mallocN(sizeof(*vert_map) * bm->totvert, __func__); - /* we shouldn't need this, but with incorrect selection flushing - * its possible we have a selected vertex that's not in a face, - * for now best not crash in that case. */ - copy_vn_i(vert_map, bm->totvert, -1); - - BM_mesh_elem_table_ensure(bm, htype); - ele_array = (htype == BM_FACE) ? (void **)bm->ftable : (void **)bm->etable; - - BM_mesh_elem_index_ensure(bm, BM_VERT); - - /* may be an edge OR a face array */ - for (i = 0; i < group_tot; i++) { - BMEditSelection ese = {NULL}; - - const int fg_sta = group_index[i][0]; - const int fg_len = group_index[i][1]; - float co[3], no[3], tangent[3]; - int j; - - zero_v3(co); - zero_v3(no); - zero_v3(tangent); - - ese.htype = htype; - - /* loop on each face or edge in this group: - * - assign r_vert_map - * - calculate (co, no) - */ - for (j = 0; j < fg_len; j++) { - float tmp_co[3], tmp_no[3], tmp_tangent[3]; - - ese.ele = ele_array[groups_array[fg_sta + j]]; - - BM_editselection_center(&ese, tmp_co); - add_v3_v3(co, tmp_co); - - if (axismtx) { - BM_editselection_normal(&ese, tmp_no); - BM_editselection_plane(&ese, tmp_tangent); - add_v3_v3(no, tmp_no); - add_v3_v3(tangent, tmp_tangent); - } - - { - /* setup vertex map */ - BMIter iter; - BMVert *v; - - /* connected edge-verts */ - BM_ITER_ELEM (v, &iter, ese.ele, itype) { - vert_map[BM_elem_index_get(v)] = i; - } - } - } - - mul_v3_v3fl(center[i], co, 1.0f / (float)fg_len); - - if (axismtx) { - if (createSpaceNormalTangent(axismtx[i], no, tangent)) { - /* pass */ - } - else { - if (normalize_v3(no) != 0.0f) { - axis_dominant_v3_to_m3(axismtx[i], no); - invert_m3(axismtx[i]); - } - else { - unit_m3(axismtx[i]); - } - } - } - } - - MEM_freeN(groups_array); - MEM_freeN(group_index); - - /* for PET we need islands of 1 so connected vertices can use it with V3D_AROUND_LOCAL_ORIGINS */ - if (calc_single_islands) { - BMIter viter; - BMVert *v; - int group_tot_single = 0; - - BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, i) { - if (BM_elem_flag_test(v, BM_ELEM_SELECT) && (vert_map[i] == -1)) { - group_tot_single += 1; - } - } - - if (group_tot_single != 0) { - center = MEM_reallocN(center, sizeof(*center) * (group_tot + group_tot_single)); - if (axismtx) { - axismtx = MEM_reallocN(axismtx, sizeof(*axismtx) * (group_tot + group_tot_single)); - } +/** \} */ - BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, i) { - if (BM_elem_flag_test(v, BM_ELEM_SELECT) && (vert_map[i] == -1)) { - vert_map[i] = group_tot; - copy_v3_v3(center[group_tot], v->co); +/* -------------------------------------------------------------------- */ +/** \name TransDataMirror Creation + * + * \{ */ - if (axismtx) { - if (is_zero_v3(v->no) != 0.0f) { - axis_dominant_v3_to_m3(axismtx[group_tot], v->no); - invert_m3(axismtx[group_tot]); - } - else { - unit_m3(axismtx[group_tot]); - } - } +/* Used for both mirror epsilon and TD_MIRROR_EDGE_ */ +#define TRANSFORM_MAXDIST_MIRROR 0.00002f - group_tot += 1; - } - } - } - } +struct MirrorDataVert { + int index; + int flag; +}; - r_island_data->axismtx = axismtx; - r_island_data->center = center; - r_island_data->island_tot = group_tot; - r_island_data->island_vert_map = vert_map; -} +struct TransMirrorData { + struct MirrorDataVert *vert_map; + int mirror_elem_len; +}; static bool is_in_quadrant_v3(const float co[3], const int quadrant[3], const float epsilon) { @@ -423,172 +459,124 @@ static bool is_in_quadrant_v3(const float co[3], const int quadrant[3], const fl return true; } -static TransDataMirror *editmesh_mirror_data_calc(BMEditMesh *em, - bool use_select, - const bool use_topology, - const bool mirror_axis[3], - int *r_mirror_data_len, - BLI_bitmap **r_mirror_bitmap) +static void editmesh_mirror_data_calc(BMEditMesh *em, + const bool use_select, + const bool use_topology, + const bool mirror_axis[3], + struct TransMirrorData *r_mirror_data) { - BMesh *bm = em->bm; - int *index[3] = {NULL}; - int i; - - bool test_selected_only = use_select && (mirror_axis[0] + mirror_axis[1] + mirror_axis[2]) == 1; - for (i = 0; i < 3; i++) { - if (mirror_axis[i]) { - index[i] = MEM_mallocN(bm->totvert * sizeof(int), __func__); - EDBM_verts_mirror_cache_begin_ex( - em, i, false, test_selected_only, use_topology, TRANSFORM_MAXDIST_MIRROR, index[i]); - } - } + struct MirrorDataVert *vert_map; + BMesh *bm = em->bm; BMVert *eve; BMIter iter; + int i, flag, totvert = bm->totvert; - int quadrant[3]; - { - float select_sum[3] = {0}; - BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { - if (BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { - continue; - } - if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { - add_v3_v3(select_sum, eve->co); - } - } - - for (i = 0; i < 3; i++) { - if (mirror_axis[i]) { - quadrant[i] = select_sum[i] >= 0.0f ? 1 : -1; - } - else { - quadrant[i] = 0; - } - } - } + vert_map = MEM_mallocN(totvert * sizeof(*vert_map), __func__); - /* Tag only elements that will be transformed within the quadrant. */ + float select_sum[3] = {0}; BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { + vert_map[i] = (struct MirrorDataVert){-1, 0}; if (BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { continue; } - if ((!use_select || BM_elem_flag_test(eve, BM_ELEM_SELECT)) && - is_in_quadrant_v3(eve->co, quadrant, TRANSFORM_MAXDIST_MIRROR)) { - BM_elem_flag_enable(eve, BM_ELEM_TAG); - BM_elem_index_set(eve, i); + if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + add_v3_v3(select_sum, eve->co); + } + } + + /* Tag only elements that will be transformed within the quadrant. */ + int quadrant[3]; + for (int a = 0; a < 3; a++) { + if (mirror_axis[a]) { + quadrant[a] = select_sum[a] >= 0.0f ? 1 : -1; } else { - BM_elem_flag_disable(eve, BM_ELEM_TAG); - BM_elem_index_set(eve, -1); + quadrant[a] = 0; } } + uint mirror_elem_len = 0; + int *index[3] = {NULL, NULL, NULL}; + bool test_selected_only = use_select && (mirror_axis[0] + mirror_axis[1] + mirror_axis[2]) == 1; for (int a = 0; a < 3; a++) { - int *index_iter = index[a]; - if (index_iter == NULL) { + if (!mirror_axis[a]) { continue; } + + index[a] = MEM_mallocN(totvert * sizeof(*index[a]), __func__); + EDBM_verts_mirror_cache_begin_ex( + em, a, false, test_selected_only, use_topology, TRANSFORM_MAXDIST_MIRROR, index[a]); + + flag = TD_MIRROR_X << a; BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { + int i_mirr = index[a][i]; + if (i_mirr < 0) { + continue; + } if (BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { continue; } - if (test_selected_only && !BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + if (use_select && !BM_elem_flag_test(eve, BM_ELEM_SELECT)) { continue; } - int elem_index = BM_elem_index_get(eve); - if (elem_index != -1) { - int i_mirr = index_iter[i]; - if (i_mirr >= 0) { - BMVert *vmir = BM_vert_at_index(bm, i_mirr); - BM_elem_index_set(vmir, elem_index); - - /* The slot of this element in the index array no longer needs to be read. - * Use to set the mirror sign. */ - if (index[0] && a > 0) { - index[0][i_mirr] = index[0][i]; - } - if (index[1] && a > 1) { - index[1][i_mirr] = index[1][i]; - } - /* Use -2 to differ from -1, but both can work. */ - index_iter[i_mirr] = -2; - } + if (!is_in_quadrant_v3(eve->co, quadrant, TRANSFORM_MAXDIST_MIRROR)) { + continue; } - } - } - /* Count mirror elements. */ - uint mirror_elem_len = 0; - BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { - if (BM_elem_flag_test(eve, BM_ELEM_HIDDEN | BM_ELEM_TAG)) { - /* Not a mirror element. */ - BM_elem_index_set(eve, -1); - continue; - } - int elem_index = BM_elem_index_get(eve); - if (elem_index != -1) { + vert_map[i_mirr] = (struct MirrorDataVert){i, flag}; mirror_elem_len++; } } - TransDataMirror *td_mirror_iter, *td_mirror = NULL; - if (mirror_elem_len != 0) { - td_mirror = MEM_mallocN(mirror_elem_len * sizeof(*td_mirror), __func__); - td_mirror_iter = &td_mirror[0]; - - *r_mirror_bitmap = BLI_BITMAP_NEW(bm->totvert, __func__); - - BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { - int elem_index = BM_elem_index_get(eve); - if (elem_index != -1) { - BMVert *v_src = BM_vert_at_index(bm, elem_index); + if (mirror_elem_len) { + for (int a = 0; a < 3; a++) { + if (!mirror_axis[a]) { + continue; + } - int flag = 0; - if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { - flag |= TD_SELECTED; - } - if (index[0] && index[0][i] == -2) { - flag |= TD_MIRROR_X; - } - if (index[1] && index[1][i] == -2) { - flag |= TD_MIRROR_Y; + flag = TD_MIRROR_X << a; + for (i = 0; i < totvert; i++) { + int i_mirr = index[a][i]; + if (i_mirr < 0) { + continue; } - if (index[2] && index[2][i] == -2) { - flag |= TD_MIRROR_Z; + if (vert_map[i].index != -1 && !(vert_map[i].flag & flag)) { + if (vert_map[i_mirr].index == -1) { + mirror_elem_len++; + } + vert_map[i_mirr].index = vert_map[i].index; + vert_map[i_mirr].flag |= vert_map[i].flag | flag; } - - td_mirror_iter->extra = eve; - td_mirror_iter->loc = eve->co; - copy_v3_v3(td_mirror_iter->iloc, eve->co); - td_mirror_iter->flag = flag; - td_mirror_iter->loc_src = v_src->co; - /** `center` will be set in the main createTransEditVerts loop. - * copy_v3_v3(td_mirror_iter->center, eve->co); */ - - td_mirror_iter++; - - BLI_BITMAP_ENABLE(*r_mirror_bitmap, i); } } } + else { + MEM_freeN(vert_map); + vert_map = NULL; + } MEM_SAFE_FREE(index[0]); MEM_SAFE_FREE(index[1]); MEM_SAFE_FREE(index[2]); - bm->elem_index_dirty |= BM_VERT; - *r_mirror_data_len = mirror_elem_len; - return td_mirror; + r_mirror_data->vert_map = vert_map; + r_mirror_data->mirror_elem_len = mirror_elem_len; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Edit Mesh Verts Transform Creation + * + * \{ */ + static void transdata_center_get(const struct TransIslandData *island_data, const int island_index, - const bool no_island_center, const float iloc[3], float r_center[3]) { - if (island_index != -1 && !no_island_center) { + if (island_data->center && island_index != -1) { copy_v3_v3(r_center, island_data->center[island_index]); } else { @@ -604,8 +592,7 @@ static void VertsToTransData(TransInfo *t, BMVert *eve, float *bweight, const struct TransIslandData *island_data, - const int island_index, - const bool no_island_center) + const int island_index) { float *no, _no[3]; BLI_assert(BM_elem_flag_test(eve, BM_ELEM_HIDDEN) == 0); @@ -626,7 +613,7 @@ static void VertsToTransData(TransInfo *t, no = eve->no; } - transdata_center_get(island_data, island_index, no_island_center, td->iloc, td->center); + transdata_center_get(island_data, island_index, td->iloc, td->center); if ((island_index != -1) && island_data->axismtx) { copy_m3_m3(td->axismtx, island_data->axismtx[island_index]); @@ -676,54 +663,23 @@ void createTransEditVerts(TransInfo *t) BMesh *bm = em->bm; BMVert *eve; BMIter iter; - float(*mappedcos)[3] = NULL, (*quats)[4] = NULL; - float mtx[3][3], smtx[3][3], (*defmats)[3][3] = NULL, (*defcos)[3] = NULL; - float *dists = NULL; + float mtx[3][3], smtx[3][3]; int a; const int prop_mode = (t->flag & T_PROP_EDIT) ? (t->flag & T_PROP_EDIT_ALL) : 0; - int cd_vert_bweight_offset = -1; struct TransIslandData island_data = {NULL}; - - /* Snap rotation along normal needs a common axis for whole islands, - * otherwise one get random crazy results, see T59104. - * However, we do not want to use the island center for the pivot/translation reference. */ - const bool is_snap_rotate = ((t->mode == TFM_TRANSLATION) && - /* There is not guarantee that snapping - * is initialized yet at this point... */ - (usingSnappingNormal(t) || - (t->settings->snap_flag & SCE_SNAP_ROTATE) != 0) && - (t->around != V3D_AROUND_LOCAL_ORIGINS)); - /* Even for translation this is needed because of island-orientation, see: T51651. */ - const bool is_island_center = (t->around == V3D_AROUND_LOCAL_ORIGINS) || is_snap_rotate; - /* Original index of our connected vertex when connected distances are calculated. - * Optional, allocate if needed. */ - int *dists_index = NULL; - - BLI_bitmap *mirror_bitmap = NULL; + struct TransMirrorData mirror_data = {NULL}; /** * Quick check if we can transform. * * \note ignore modes here, even in edge/face modes, * transform data is created by selected vertices. - * \note in prop mode we need at least 1 selected. */ - if (bm->totvertsel == 0) { - goto cleanup; - } - if (t->mode == TFM_BWEIGHT) { - BM_mesh_cd_flag_ensure(bm, BKE_mesh_from_object(tc->obedit), ME_CDFLAG_VERT_BWEIGHT); - cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT); - } - - if (tc->use_mirror_axis_any) { - bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; - bool use_select = (t->flag & T_PROP_EDIT) == 0; - bool mirror_axis[3] = {tc->use_mirror_axis_x, tc->use_mirror_axis_y, tc->use_mirror_axis_z}; - tc->data_mirror = editmesh_mirror_data_calc( - em, use_select, use_topology, mirror_axis, &tc->data_mirror_len, &mirror_bitmap); + /* Support other objects using PET to adjust these, unless connected is enabled. */ + if ((!prop_mode || (prop_mode & T_PROP_CONNECTED)) && (bm->totvertsel == 0)) { + continue; } int data_len = 0; @@ -733,40 +689,41 @@ void createTransEditVerts(TransInfo *t) data_len++; } } - - /* allocating scratch arrays */ - if (prop_mode & T_PROP_CONNECTED) { - dists = MEM_mallocN(em->bm->totvert * sizeof(float), __func__); - if (is_island_center) { - dists_index = MEM_mallocN(em->bm->totvert * sizeof(int), __func__); - } - } } else { data_len = bm->totvertsel; } - if (mirror_bitmap) { - BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, a) { - if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) { - if (BLI_BITMAP_TEST(mirror_bitmap, a)) { - data_len--; - } - } - } + if (data_len == 0) { + continue; } - BLI_assert(data_len != 0); + /* Snap rotation along normal needs a common axis for whole islands, + * otherwise one get random crazy results, see T59104. + * However, we do not want to use the island center for the pivot/translation reference. */ + const bool is_snap_rotate = ((t->mode == TFM_TRANSLATION) && + /* There is not guarantee that snapping + * is initialized yet at this point... */ + (usingSnappingNormal(t) || + (t->settings->snap_flag & SCE_SNAP_ROTATE) != 0) && + (t->around != V3D_AROUND_LOCAL_ORIGINS)); - tc->data_len = data_len; - tc->data = MEM_callocN(data_len * sizeof(TransData), "TransObData(Mesh EditMode)"); - if (ELEM(t->mode, TFM_SKIN_RESIZE, TFM_SHRINKFATTEN)) { - /* warning, this is overkill, we only need 2 extra floats, - * but this stores loads of extra stuff, for TFM_SHRINKFATTEN its even more overkill - * since we may not use the 'alt' transform mode to maintain shell thickness, - * but with generic transform code its hard to lazy init vars */ - tx = tc->data_ext = MEM_callocN(tc->data_len * sizeof(TransDataExtension), - "TransObData ext"); + /* Even for translation this is needed because of island-orientation, see: T51651. */ + const bool is_island_center = (t->around == V3D_AROUND_LOCAL_ORIGINS) || is_snap_rotate; + if (is_island_center) { + /* In this specific case, near-by vertices will need to know + * the island of the nearest connected vertex. */ + const bool calc_single_islands = ((prop_mode & T_PROP_CONNECTED) && + (t->around == V3D_AROUND_LOCAL_ORIGINS) && + (em->selectmode & SCE_SELECT_VERTEX)); + + const bool calc_island_center = !is_snap_rotate; + /* The island axismtx is only necessary in some modes. + * TODO(Germano): Extend the list to exclude other modes. */ + const bool calc_island_axismtx = !ELEM(t->mode, TFM_SHRINKFATTEN); + + editmesh_islands_info_calc( + em, calc_single_islands, calc_island_center, calc_island_axismtx, &island_data); } copy_m3_m4(mtx, tc->obedit->obmat); @@ -774,26 +731,58 @@ void createTransEditVerts(TransInfo *t) * matrix inversion still works and we can still moving along the other */ pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON); + /* Original index of our connected vertex when connected distances are calculated. + * Optional, allocate if needed. */ + int *dists_index = NULL; + float *dists = NULL; if (prop_mode & T_PROP_CONNECTED) { + dists = MEM_mallocN(bm->totvert * sizeof(float), __func__); + if (is_island_center) { + dists_index = MEM_mallocN(bm->totvert * sizeof(int), __func__); + } editmesh_set_connectivity_distance(em->bm, mtx, dists, dists_index); } - if (is_island_center) { - /* In this specific case, near-by vertices will need to know - * the island of the nearest connected vertex. */ - const bool calc_single_islands = ((prop_mode & T_PROP_CONNECTED) && - (t->around == V3D_AROUND_LOCAL_ORIGINS) && - (em->selectmode & SCE_SELECT_VERTEX)); + /* Create TransDataMirror. */ + if (tc->use_mirror_axis_any) { + bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; + bool use_select = (t->flag & T_PROP_EDIT) == 0; + bool mirror_axis[3] = {tc->use_mirror_axis_x, tc->use_mirror_axis_y, tc->use_mirror_axis_z}; + editmesh_mirror_data_calc(em, use_select, use_topology, mirror_axis, &mirror_data); - /* The island axismtx is only necessary in some modes. - * TODO(Germano): Extend the list to exclude other modes. */ - const bool calc_island_axismtx = !ELEM(t->mode, TFM_SHRINKFATTEN); + if (mirror_data.vert_map) { + tc->data_mirror_len = mirror_data.mirror_elem_len; + tc->data_mirror = MEM_mallocN(mirror_data.mirror_elem_len * sizeof(*tc->data_mirror), + __func__); + + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, a) { + if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + if (mirror_data.vert_map[a].index != -1) { + data_len--; + } + } + } + } + } - editmesh_islands_info_calc(em, calc_single_islands, calc_island_axismtx, &island_data); + /* Create TransData. */ + BLI_assert(data_len >= 1); + tc->data_len = data_len; + tc->data = MEM_callocN(data_len * sizeof(TransData), "TransObData(Mesh EditMode)"); + if (ELEM(t->mode, TFM_SKIN_RESIZE, TFM_SHRINKFATTEN)) { + /* warning, this is overkill, we only need 2 extra floats, + * but this stores loads of extra stuff, for TFM_SHRINKFATTEN its even more overkill + * since we may not use the 'alt' transform mode to maintain shell thickness, + * but with generic transform code its hard to lazy init vars */ + tx = tc->data_ext = MEM_callocN(tc->data_len * sizeof(TransDataExtension), + "TransObData ext"); } /* detect CrazySpace [tm] */ + float(*quats)[4] = NULL; + float(*defmats)[3][3] = NULL; if (BKE_modifiers_get_cage_index(t->scene, tc->obedit, NULL, 1) != -1) { + float(*defcos)[3] = NULL; int totleft = -1; if (BKE_modifiers_is_correctable_deformed(t->scene, tc->obedit)) { BKE_scene_graph_evaluated_ensure(t->depsgraph, CTX_data_main(t->context)); @@ -808,16 +797,17 @@ void createTransEditVerts(TransInfo *t) t->depsgraph, scene_eval, obedit_eval, em_eval, &defmats, &defcos); } - /* if we still have more modifiers, also do crazyspace - * correction with quats, relative to the coordinates after - * the modifiers that support deform matrices (defcos) */ + /* If we still have more modifiers, also do crazy-space + * correction with \a quats, relative to the coordinates after + * the modifiers that support deform matrices \a defcos. */ -#if 0 /* TODO, fix crazyspace+extrude so it can be enabled for general use - campbell */ +#if 0 /* TODO, fix crazy-space & extrude so it can be enabled for general use - campbell */ if ((totleft > 0) || (totleft == -1)) #else if (totleft > 0) #endif { + float(*mappedcos)[3] = NULL; mappedcos = BKE_crazyspace_get_mapped_editverts(t->depsgraph, tc->obedit); quats = MEM_mallocN(em->bm->totvert * sizeof(*quats), "crazy quats"); BKE_crazyspace_set_quats_editmesh(em, defcos, mappedcos, quats, !prop_mode); @@ -831,114 +821,127 @@ void createTransEditVerts(TransInfo *t) } } + int cd_vert_bweight_offset = -1; + if (t->mode == TFM_BWEIGHT) { + BM_mesh_cd_flag_ensure(bm, BKE_mesh_from_object(tc->obedit), ME_CDFLAG_VERT_BWEIGHT); + cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT); + } + TransData *tob = tc->data; TransDataMirror *td_mirror = tc->data_mirror; BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, a) { if (BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { continue; } - else { - int island_index = -1; - if (island_data.island_vert_map) { - const int connected_index = (dists_index && dists_index[a] != -1) ? dists_index[a] : a; - island_index = island_data.island_vert_map[connected_index]; + + int island_index = -1; + if (island_data.island_vert_map) { + const int connected_index = (dists_index && dists_index[a] != -1) ? dists_index[a] : a; + island_index = island_data.island_vert_map[connected_index]; + } + + if (mirror_data.vert_map && mirror_data.vert_map[a].index != -1) { + int elem_index = mirror_data.vert_map[a].index; + BMVert *v_src = BM_vert_at_index(bm, elem_index); + + if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + mirror_data.vert_map[a].flag |= TD_SELECTED; } - if (mirror_bitmap && BLI_BITMAP_TEST(mirror_bitmap, a)) { - transdata_center_get( - &island_data, island_index, is_snap_rotate, td_mirror->iloc, td_mirror->center); + td_mirror->extra = eve; + td_mirror->loc = eve->co; + copy_v3_v3(td_mirror->iloc, eve->co); + td_mirror->flag = mirror_data.vert_map[a].flag; + td_mirror->loc_src = v_src->co; + transdata_center_get(&island_data, island_index, td_mirror->iloc, td_mirror->center); + + td_mirror++; + } + else if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + float *bweight = (cd_vert_bweight_offset != -1) ? + BM_ELEM_CD_GET_VOID_P(eve, cd_vert_bweight_offset) : + NULL; + + /* Do not use the island center in case we are using islands + * only to get axis for snap/rotate to normal... */ + VertsToTransData(t, tob, tx, em, eve, bweight, &island_data, island_index); + if (tx) { + tx++; + } - td_mirror++; + /* selected */ + if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + tob->flag |= TD_SELECTED; } - else if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) { - float *bweight = (cd_vert_bweight_offset != -1) ? - BM_ELEM_CD_GET_VOID_P(eve, cd_vert_bweight_offset) : - NULL; - - /* Do not use the island center in case we are using islands - * only to get axis for snap/rotate to normal... */ - VertsToTransData( - t, tob, tx, em, eve, bweight, &island_data, island_index, is_snap_rotate); - if (tx) { - tx++; - } - /* selected */ - if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { - tob->flag |= TD_SELECTED; + if (prop_mode) { + if (prop_mode & T_PROP_CONNECTED) { + tob->dist = dists[a]; } - - if (prop_mode) { - if (prop_mode & T_PROP_CONNECTED) { - tob->dist = dists[a]; - } - else { - tob->flag |= TD_NOTCONNECTED; - tob->dist = FLT_MAX; - } + else { + tob->flag |= TD_NOTCONNECTED; + tob->dist = FLT_MAX; } + } - /* CrazySpace */ - const bool use_quats = quats && BM_elem_flag_test(eve, BM_ELEM_TAG); - if (use_quats || defmats) { - float mat[3][3], qmat[3][3], imat[3][3]; + /* CrazySpace */ + const bool use_quats = quats && BM_elem_flag_test(eve, BM_ELEM_TAG); + if (use_quats || defmats) { + float mat[3][3], qmat[3][3], imat[3][3]; - /* Use both or either quat and defmat correction. */ - if (use_quats) { - quat_to_mat3(qmat, quats[BM_elem_index_get(eve)]); + /* Use both or either quat and defmat correction. */ + if (use_quats) { + quat_to_mat3(qmat, quats[BM_elem_index_get(eve)]); - if (defmats) { - mul_m3_series(mat, defmats[a], qmat, mtx); - } - else { - mul_m3_m3m3(mat, mtx, qmat); - } + if (defmats) { + mul_m3_series(mat, defmats[a], qmat, mtx); } else { - mul_m3_m3m3(mat, mtx, defmats[a]); + mul_m3_m3m3(mat, mtx, qmat); } - - invert_m3_m3(imat, mat); - - copy_m3_m3(tob->smtx, imat); - copy_m3_m3(tob->mtx, mat); } else { - copy_m3_m3(tob->smtx, smtx); - copy_m3_m3(tob->mtx, mtx); + mul_m3_m3m3(mat, mtx, defmats[a]); } - if (tc->use_mirror_axis_any) { - if (tc->use_mirror_axis_x && fabsf(tob->loc[0]) < TRANSFORM_MAXDIST_MIRROR) { - tob->flag |= TD_MIRROR_EDGE_X; - } - if (tc->use_mirror_axis_y && fabsf(tob->loc[1]) < TRANSFORM_MAXDIST_MIRROR) { - tob->flag |= TD_MIRROR_EDGE_Y; - } - if (tc->use_mirror_axis_z && fabsf(tob->loc[2]) < TRANSFORM_MAXDIST_MIRROR) { - tob->flag |= TD_MIRROR_EDGE_Z; - } - } + invert_m3_m3(imat, mat); - tob++; + copy_m3_m3(tob->smtx, imat); + copy_m3_m3(tob->mtx, mat); + } + else { + copy_m3_m3(tob->smtx, smtx); + copy_m3_m3(tob->mtx, mtx); } + + if (tc->use_mirror_axis_any) { + if (tc->use_mirror_axis_x && fabsf(tob->loc[0]) < TRANSFORM_MAXDIST_MIRROR) { + tob->flag |= TD_MIRROR_EDGE_X; + } + if (tc->use_mirror_axis_y && fabsf(tob->loc[1]) < TRANSFORM_MAXDIST_MIRROR) { + tob->flag |= TD_MIRROR_EDGE_Y; + } + if (tc->use_mirror_axis_z && fabsf(tob->loc[2]) < TRANSFORM_MAXDIST_MIRROR) { + tob->flag |= TD_MIRROR_EDGE_Z; + } + } + + tob++; } } if (island_data.center) { MEM_freeN(island_data.center); } - if (island_data.axismtx) { MEM_freeN(island_data.axismtx); } - if (island_data.island_vert_map) { MEM_freeN(island_data.island_vert_map); } - - cleanup: - /* crazy space free */ + if (mirror_data.vert_map) { + MEM_freeN(mirror_data.vert_map); + } if (quats) { MEM_freeN(quats); } @@ -951,50 +954,50 @@ void createTransEditVerts(TransInfo *t) if (dists_index) { MEM_freeN(dists_index); } - if (mirror_bitmap) { - MEM_freeN(mirror_bitmap); - } } } /** \} */ /* -------------------------------------------------------------------- */ -/** \name CustomData Layer Correction (for meshes) +/** \name CustomData Layer Correction * * \{ */ -struct TransCustomDataLayerVert { - BMVert *v; - float co_orig_3d[3]; +struct TransCustomDataMergeGroup { + /** map {BMVert: TransCustomDataLayerVert} */ struct LinkNode **cd_loop_groups; }; struct TransCustomDataLayer { BMesh *bm; + struct MemArena *arena; - int cd_loop_mdisp_offset; - - /** map {BMVert: TransCustomDataLayerVert} */ - struct GHash *origverts; struct GHash *origfaces; struct BMesh *bm_origfaces; - struct MemArena *arena; - /** Number of math BMLoop layers. */ - int layer_math_map_num; - /** Array size of 'layer_math_map_num' - * maps TransCustomDataLayerVert.cd_group index to absolute CustomData layer index */ - int *layer_math_map; - - /* Array with all elements transformed. */ - struct TransCustomDataLayerVert *data; - int data_len; + /* Special handle for multi-resolution. */ + int cd_loop_mdisp_offset; + + /* Optionally merge custom-data groups (this keeps UVs connected for example). */ + struct { + /** map {BMVert: TransDataBasic} */ + struct GHash *origverts; + struct TransCustomDataMergeGroup *data; + int data_len; + /** Array size of 'layer_math_map_len' + * maps #TransCustomDataLayerVert.cd_group index to absolute #CustomData layer index */ + int *customdatalayer_map; + /** Number of math BMLoop layers. */ + int customdatalayer_map_len; + } merge_group; + + bool use_merge_group; }; -static void trans_mesh_customdata_free_cb(struct TransInfo *UNUSED(t), - struct TransDataContainer *UNUSED(tc), - struct TransCustomData *custom_data) +static void mesh_customdatacorrect_free_cb(struct TransInfo *UNUSED(t), + struct TransDataContainer *UNUSED(tc), + struct TransCustomData *custom_data) { struct TransCustomDataLayer *tcld = custom_data->data; bmesh_edit_end(tcld->bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); @@ -1005,171 +1008,290 @@ static void trans_mesh_customdata_free_cb(struct TransInfo *UNUSED(t), if (tcld->origfaces) { BLI_ghash_free(tcld->origfaces, NULL, NULL); } - if (tcld->origverts) { - BLI_ghash_free(tcld->origverts, NULL, NULL); + if (tcld->merge_group.origverts) { + BLI_ghash_free(tcld->merge_group.origverts, NULL, NULL); } if (tcld->arena) { BLI_memarena_free(tcld->arena); } - if (tcld->layer_math_map) { - MEM_freeN(tcld->layer_math_map); + if (tcld->merge_group.customdatalayer_map) { + MEM_freeN(tcld->merge_group.customdatalayer_map); } MEM_freeN(tcld); custom_data->data = NULL; } -static void create_trans_vert_customdata_layer(BMVert *v, - struct TransCustomDataLayer *tcld, - struct TransCustomDataLayerVert *r_tcld_vert) +#ifdef USE_FACE_SUBSTITUTE + +# define FACE_SUBSTITUTE_INDEX INT_MIN + +/** + * Search for a neighboring face with area and preferably without selected vertex. + * Used to replace area-less faces in custom-data correction. + */ +static BMFace *mesh_customdatacorrect_find_best_face_substitute(BMFace *f) +{ + BMFace *best_face = NULL; + BMLoop *l; + BMIter liter; + BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { + BMLoop *l_radial_next = l->radial_next; + BMFace *f_test = l_radial_next->f; + if (f_test == f) { + continue; + } + if (is_zero_v3(f_test->no)) { + continue; + } + + /* Check the loops edge isn't selected. */ + if (!BM_elem_flag_test(l_radial_next->v, BM_ELEM_SELECT) && + !BM_elem_flag_test(l_radial_next->next->v, BM_ELEM_SELECT)) { + /* Prefer edges with unselected vertices. + * Useful for extrude. */ + best_face = f_test; + break; + } + if (best_face == NULL) { + best_face = f_test; + } + } + return best_face; +} + +static void mesh_customdatacorrect_face_substitute_set(struct TransCustomDataLayer *tcld, + BMFace *f, + BMFace *f_copy) +{ + BLI_assert(is_zero_v3(f->no)); + BMesh *bm = tcld->bm; + /* It is impossible to calculate the loops weights of a face without area. + * Find a substitute. */ + BMFace *f_substitute = mesh_customdatacorrect_find_best_face_substitute(f); + if (f_substitute) { + /* Copy the custom-data from the substitute face. */ + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + BM_loop_interp_from_face(bm, l_iter, f_substitute, false, false); + } while ((l_iter = l_iter->next) != l_first); + + /* Use the substitute face as the reference during the transformation. */ + BMFace *f_substitute_copy = BM_face_copy(tcld->bm_origfaces, bm, f_substitute, true, true); + + /* Hack: reference substitute face in `f_copy->no`. + * `tcld->origfaces` is already used to restore the initial value. */ + BM_elem_index_set(f_copy, FACE_SUBSTITUTE_INDEX); + *((BMFace **)&f_copy->no[0]) = f_substitute_copy; + } +} + +static BMFace *mesh_customdatacorrect_face_substitute_get(BMFace *f_copy) +{ + BLI_assert(BM_elem_index_get(f_copy) == FACE_SUBSTITUTE_INDEX); + return *((BMFace **)&f_copy->no[0]); +} + +#endif /* USE_FACE_SUBSTITUTE */ + +static void mesh_customdatacorrect_init_vert(struct TransCustomDataLayer *tcld, + struct TransDataBasic *td, + const int index) { BMesh *bm = tcld->bm; + BMVert *v = td->extra; BMIter liter; int j, l_num; float *loop_weights; - /* copy face data */ // BM_ITER_ELEM (l, &liter, sv->v, BM_LOOPS_OF_VERT) { BM_iter_init(&liter, bm, BM_LOOPS_OF_VERT, v); l_num = liter.count; - loop_weights = BLI_array_alloca(loop_weights, l_num); + loop_weights = tcld->use_merge_group ? BLI_array_alloca(loop_weights, l_num) : NULL; for (j = 0; j < l_num; j++) { BMLoop *l = BM_iter_step(&liter); BMLoop *l_prev, *l_next; + + /* Generic custom-data correction. Copy face data. */ void **val_p; if (!BLI_ghash_ensure_p(tcld->origfaces, l->f, &val_p)) { BMFace *f_copy = BM_face_copy(tcld->bm_origfaces, bm, l->f, true, true); *val_p = f_copy; +#ifdef USE_FACE_SUBSTITUTE + if (is_zero_v3(l->f->no)) { + mesh_customdatacorrect_face_substitute_set(tcld, l->f, f_copy); + } +#endif } - if ((l_prev = BM_loop_find_prev_nodouble(l, l->next, FLT_EPSILON)) && - (l_next = BM_loop_find_next_nodouble(l, l_prev, FLT_EPSILON))) { - loop_weights[j] = angle_v3v3v3(l_prev->v->co, l->v->co, l_next->v->co); - } - else { - loop_weights[j] = 0.0f; + if (tcld->use_merge_group) { + if ((l_prev = BM_loop_find_prev_nodouble(l, l->next, FLT_EPSILON)) && + (l_next = BM_loop_find_next_nodouble(l, l_prev, FLT_EPSILON))) { + loop_weights[j] = angle_v3v3v3(l_prev->v->co, l->v->co, l_next->v->co); + } + else { + loop_weights[j] = 0.0f; + } } } - /* store cd_loop_groups */ - if (tcld->layer_math_map_num && (l_num != 0)) { - r_tcld_vert->cd_loop_groups = BLI_memarena_alloc(tcld->arena, - tcld->layer_math_map_num * sizeof(void *)); - for (j = 0; j < tcld->layer_math_map_num; j++) { - const int layer_nr = tcld->layer_math_map[j]; - r_tcld_vert->cd_loop_groups[j] = BM_vert_loop_groups_data_layer_create( - bm, v, layer_nr, loop_weights, tcld->arena); + if (tcld->use_merge_group) { + /* Store cd_loop_groups. */ + struct TransCustomDataMergeGroup *merge_data = &tcld->merge_group.data[index]; + if (l_num != 0) { + merge_data->cd_loop_groups = BLI_memarena_alloc( + tcld->arena, tcld->merge_group.customdatalayer_map_len * sizeof(void *)); + for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) { + const int layer_nr = tcld->merge_group.customdatalayer_map[j]; + merge_data->cd_loop_groups[j] = BM_vert_loop_groups_data_layer_create( + bm, v, layer_nr, loop_weights, tcld->arena); + } } + else { + merge_data->cd_loop_groups = NULL; + } + + BLI_ghash_insert(tcld->merge_group.origverts, v, td); } - else { - r_tcld_vert->cd_loop_groups = NULL; - } +} + +static void mesh_customdatacorrect_init_container_generic(TransDataContainer *UNUSED(tc), + struct TransCustomDataLayer *tcld) +{ + BMesh *bm = tcld->bm; + + struct GHash *origfaces = BLI_ghash_ptr_new(__func__); + struct BMesh *bm_origfaces = BM_mesh_create(&bm_mesh_allocsize_default, + &((struct BMeshCreateParams){ + .use_toolflags = false, + })); + + /* We need to have matching custom-data. */ + BM_mesh_copy_init_customdata(bm_origfaces, bm, NULL); + tcld->origfaces = origfaces; + tcld->bm_origfaces = bm_origfaces; - r_tcld_vert->v = v; - copy_v3_v3(r_tcld_vert->co_orig_3d, v->co); - BLI_ghash_insert(tcld->origverts, v, r_tcld_vert); + bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); + tcld->cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS); } -void trans_mesh_customdata_correction_init(TransInfo *t) +static void mesh_customdatacorrect_init_container_merge_group(TransDataContainer *tc, + struct TransCustomDataLayer *tcld) { - FOREACH_TRANS_DATA_CONTAINER (t, tc) { - if (tc->custom.type.data) { - if (tc->custom.type.free_cb == trans_mesh_customdata_free_cb) { - /* Custom data correction has initiated before. */ - continue; - } - else { - BLI_assert(false); - } - } - int i; + BMesh *bm = tcld->bm; + BLI_assert(CustomData_has_math(&bm->ldata)); - BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); - BMesh *bm = em->bm; + /* TODO: We don't need `layer_math_map` when there are no loops linked + * to one of the sliding vertices. */ - bool use_origfaces; - int cd_loop_mdisp_offset; - { - const bool has_layer_math = CustomData_has_math(&bm->ldata); - cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS); - if ((t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT) && - /* don't do this at all for non-basis shape keys, too easy to - * accidentally break uv maps or vertex colors then */ - (bm->shapenr <= 1) && (has_layer_math || (cd_loop_mdisp_offset != -1))) { - use_origfaces = true; - } - else { - use_origfaces = false; - cd_loop_mdisp_offset = -1; - } + /* Over allocate, only 'math' layers are indexed. */ + int *customdatalayer_map = MEM_mallocN(sizeof(int) * bm->ldata.totlayer, __func__); + int layer_math_map_len = 0; + for (int i = 0; i < bm->ldata.totlayer; i++) { + if (CustomData_layer_has_math(&bm->ldata, i)) { + customdatalayer_map[layer_math_map_len++] = i; } + } + BLI_assert(layer_math_map_len != 0); + + tcld->merge_group.data_len = tc->data_len + tc->data_mirror_len; + tcld->merge_group.customdatalayer_map = customdatalayer_map; + tcld->merge_group.customdatalayer_map_len = layer_math_map_len; + tcld->merge_group.origverts = BLI_ghash_ptr_new_ex(__func__, tcld->merge_group.data_len); + tcld->merge_group.data = BLI_memarena_alloc( + tcld->arena, tcld->merge_group.data_len * sizeof(*tcld->merge_group.data)); +} - if (use_origfaces) { - /* create copies of faces for customdata projection */ - bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); +static void mesh_customdatacorrect_init_container(TransDataContainer *tc, + const bool use_merge_group) +{ + if (tc->custom.type.data) { + /* The custom-data correction has been initiated before. + * Free since some modes have different settings. */ + mesh_customdatacorrect_free_cb(NULL, tc, &tc->custom.type); + } - struct GHash *origfaces = BLI_ghash_ptr_new(__func__); - struct BMesh *bm_origfaces = BM_mesh_create(&bm_mesh_allocsize_default, - &((struct BMeshCreateParams){ - .use_toolflags = false, - })); + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); + BMesh *bm = em->bm; - /* we need to have matching customdata */ - BM_mesh_copy_init_customdata(bm_origfaces, bm, NULL); + if (bm->shapenr > 1) { + /* Don't do this at all for non-basis shape keys, too easy to + * accidentally break uv maps or vertex colors then */ + /* create copies of faces for custom-data projection. */ + return; + } + if (!CustomData_has_math(&bm->ldata) && !CustomData_has_layer(&bm->ldata, CD_MDISPS)) { + /* There is no custom-data to correct. */ + return; + } - int *layer_math_map = NULL; - int layer_index_dst = 0; - { - /* TODO: We don't need `sod->layer_math_map` when there are no loops linked - * to one of the sliding vertices. */ - if (CustomData_has_math(&bm->ldata)) { - /* over alloc, only 'math' layers are indexed */ - layer_math_map = MEM_mallocN(bm->ldata.totlayer * sizeof(int), __func__); - for (i = 0; i < bm->ldata.totlayer; i++) { - if (CustomData_layer_has_math(&bm->ldata, i)) { - layer_math_map[layer_index_dst++] = i; - } - } - BLI_assert(layer_index_dst != 0); - } - } + struct TransCustomDataLayer *tcld = MEM_callocN(sizeof(*tcld), __func__); + tcld->bm = bm; + tcld->arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); - struct TransCustomDataLayer *tcld; - tc->custom.type.data = tcld = MEM_mallocN(sizeof(*tcld), __func__); - tc->custom.type.free_cb = trans_mesh_customdata_free_cb; - - tcld->bm = bm; - tcld->origfaces = origfaces; - tcld->bm_origfaces = bm_origfaces; - tcld->cd_loop_mdisp_offset = cd_loop_mdisp_offset; - tcld->layer_math_map = layer_math_map; - tcld->layer_math_map_num = layer_index_dst; - tcld->arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); - - int data_len = tc->data_len + tc->data_mirror_len; - struct GHash *origverts = BLI_ghash_ptr_new_ex(__func__, data_len); - tcld->origverts = origverts; - - struct TransCustomDataLayerVert *tcld_vert, *tcld_vert_iter; - tcld_vert = BLI_memarena_alloc(tcld->arena, data_len * sizeof(*tcld_vert)); - tcld_vert_iter = &tcld_vert[0]; - - TransData *tob; - for (i = tc->data_len, tob = tc->data; i--; tob++, tcld_vert_iter++) { - BMVert *v = tob->extra; - create_trans_vert_customdata_layer(v, tcld, tcld_vert_iter); - } + /* Init `cd_loop_mdisp_offset` to -1 to avoid problems with a valid index. */ + tcld->cd_loop_mdisp_offset = -1; + tcld->use_merge_group = use_merge_group; - TransDataMirror *td_mirror = tc->data_mirror; - for (i = tc->data_mirror_len; i--; td_mirror++, tcld_vert_iter++) { - BMVert *v = td_mirror->extra; - create_trans_vert_customdata_layer(v, tcld, tcld_vert_iter); - } + mesh_customdatacorrect_init_container_generic(tc, tcld); + + if (tcld->use_merge_group) { + mesh_customdatacorrect_init_container_merge_group(tc, tcld); + } - tcld->data = tcld_vert; - tcld->data_len = data_len; + { + /* Setup Verts. */ + int i = 0; + + TransData *tob = tc->data; + for (int j = tc->data_len; j--; tob++, i++) { + mesh_customdatacorrect_init_vert(tcld, (TransDataBasic *)tob, i); + } + + TransDataMirror *td_mirror = tc->data_mirror; + for (int j = tc->data_mirror_len; j--; td_mirror++, i++) { + mesh_customdatacorrect_init_vert(tcld, (TransDataBasic *)td_mirror, i); } } + + tc->custom.type.data = tcld; + tc->custom.type.free_cb = mesh_customdatacorrect_free_cb; +} + +void mesh_customdatacorrect_init(TransInfo *t) +{ + bool use_merge_group = false; + if (ELEM(t->mode, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) { + if (!(t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT_SLIDE)) { + /* No custom-data correction. */ + return; + } + use_merge_group = true; + } + else if (ELEM(t->mode, + TFM_TRANSLATION, + TFM_ROTATION, + TFM_RESIZE, + TFM_TOSPHERE, + TFM_SHEAR, + TFM_BEND, + TFM_SHRINKFATTEN, + TFM_TRACKBALL, + TFM_PUSHPULL, + TFM_ALIGN)) { + { + if (!(t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT)) { + /* No custom-data correction. */ + return; + } + use_merge_group = (t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT_KEEP_CONNECTED) != 0; + } + } + + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + mesh_customdatacorrect_init_container(tc, use_merge_group); + } } /** @@ -1177,30 +1299,29 @@ void trans_mesh_customdata_correction_init(TransInfo *t) */ static const float *trans_vert_orig_co_get(struct TransCustomDataLayer *tcld, BMVert *v) { - struct TransCustomDataLayerVert *tcld_vert = BLI_ghash_lookup(tcld->origverts, v); - return tcld_vert ? tcld_vert->co_orig_3d : v->co; + TransDataBasic *td = BLI_ghash_lookup(tcld->merge_group.origverts, v); + return td ? td->iloc : v->co; } -static void trans_mesh_customdata_correction_apply_vert(struct TransCustomDataLayer *tcld, - struct TransCustomDataLayerVert *tcld_vert, - bool is_final) +static void mesh_customdatacorrect_apply_vert(struct TransCustomDataLayer *tcld, + struct TransDataBasic *td, + struct TransCustomDataMergeGroup *merge_data, + bool do_loop_mdisps) { BMesh *bm = tcld->bm; - BMVert *v = tcld_vert->v; - const float *co_orig_3d = tcld_vert->co_orig_3d; - struct LinkNode **cd_loop_groups = tcld_vert->cd_loop_groups; + BMVert *v = td->extra; + const float *co_orig_3d = td->iloc; BMIter liter; int j, l_num; float *loop_weights; const bool is_moved = (len_squared_v3v3(v->co, co_orig_3d) > FLT_EPSILON); - const bool do_loop_weight = tcld->layer_math_map_num && is_moved; - const bool do_loop_mdisps = is_final && is_moved && (tcld->cd_loop_mdisp_offset != -1); + const bool do_loop_weight = is_moved && tcld->merge_group.customdatalayer_map_len; const float *v_proj_axis = v->no; /* original (l->prev, l, l->next) projections for each loop ('l' remains unchanged) */ float v_proj[3][3]; - if (do_loop_weight || do_loop_mdisps) { + if (do_loop_weight) { project_plane_normalized_v3_v3v3(v_proj[1], co_orig_3d, v_proj_axis); } @@ -1214,6 +1335,14 @@ static void trans_mesh_customdata_correction_apply_vert(struct TransCustomDataLa f_copy = BLI_ghash_lookup(tcld->origfaces, l->f); +#ifdef USE_FACE_SUBSTITUTE + /* In some faces it is not possible to calculate interpolation, + * so we use a substitute. */ + if (BM_elem_index_get(f_copy) == FACE_SUBSTITUTE_INDEX) { + f_copy = mesh_customdatacorrect_face_substitute_get(f_copy); + } +#endif + /* only loop data, no vertex data since that contains shape keys, * and we do not want to mess up other shape keys */ BM_loop_interp_from_face(bm, l, f_copy, false, false); @@ -1265,16 +1394,20 @@ static void trans_mesh_customdata_correction_apply_vert(struct TransCustomDataLa } } - if (tcld->layer_math_map_num && cd_loop_groups) { - if (do_loop_weight) { - for (j = 0; j < tcld->layer_math_map_num; j++) { - BM_vert_loop_groups_data_layer_merge_weights( - bm, cd_loop_groups[j], tcld->layer_math_map[j], loop_weights); + if (tcld->use_merge_group) { + struct LinkNode **cd_loop_groups = merge_data->cd_loop_groups; + if (tcld->merge_group.customdatalayer_map_len && cd_loop_groups) { + if (do_loop_weight) { + for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) { + BM_vert_loop_groups_data_layer_merge_weights( + bm, cd_loop_groups[j], tcld->merge_group.customdatalayer_map[j], loop_weights); + } } - } - else { - for (j = 0; j < tcld->layer_math_map_num; j++) { - BM_vert_loop_groups_data_layer_merge(bm, cd_loop_groups[j], tcld->layer_math_map[j]); + else { + for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) { + BM_vert_loop_groups_data_layer_merge( + bm, cd_loop_groups[j], tcld->merge_group.customdatalayer_map[j]); + } } } } @@ -1284,7 +1417,8 @@ static void trans_mesh_customdata_correction_apply_vert(struct TransCustomDataLa * Interpolate from every other loop (not ideal) * However values will only be taken from loops which overlap other mdisps. * */ - if (do_loop_mdisps) { + const bool update_loop_mdisps = is_moved && do_loop_mdisps && (tcld->cd_loop_mdisp_offset != -1); + if (update_loop_mdisps) { float(*faces_center)[3] = BLI_array_alloca(faces_center, l_num); BMLoop *l; @@ -1313,19 +1447,63 @@ static void trans_mesh_customdata_correction_apply_vert(struct TransCustomDataLa } } -void trans_mesh_customdata_correction_apply(struct TransDataContainer *tc, bool is_final) +static void mesh_customdatacorrect_apply(TransInfo *t, bool is_final) { - struct TransCustomDataLayer *tcld = tc->custom.type.data; - if (!tcld) { - return; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + if (!tc->custom.type.data) { + continue; + } + struct TransCustomDataLayer *tcld = tc->custom.type.data; + const bool use_merge_group = tcld->use_merge_group; + + struct TransCustomDataMergeGroup *merge_data = tcld->merge_group.data; + TransData *tob = tc->data; + for (int i = tc->data_len; i--; tob++) { + mesh_customdatacorrect_apply_vert(tcld, (TransDataBasic *)tob, merge_data, is_final); + + if (use_merge_group) { + merge_data++; + } + } + + TransDataMirror *td_mirror = tc->data_mirror; + for (int i = tc->data_mirror_len; i--; td_mirror++) { + mesh_customdatacorrect_apply_vert(tcld, (TransDataBasic *)td_mirror, merge_data, is_final); + + if (use_merge_group) { + merge_data++; + } + } } +} - const bool has_mdisps = (tcld->cd_loop_mdisp_offset != -1); - struct TransCustomDataLayerVert *tcld_vert_iter = &tcld->data[0]; +static void mesh_customdatacorrect_restore(struct TransInfo *t) +{ + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + struct TransCustomDataLayer *tcld = tc->custom.type.data; + if (!tcld) { + continue; + } - for (int i = tcld->data_len; i--; tcld_vert_iter++) { - if (tcld_vert_iter->cd_loop_groups || has_mdisps) { - trans_mesh_customdata_correction_apply_vert(tcld, tcld_vert_iter, is_final); + BMesh *bm = tcld->bm; + BMesh *bm_copy = tcld->bm_origfaces; + + GHashIterator gh_iter; + GHASH_ITER (gh_iter, tcld->origfaces) { + BMFace *f = BLI_ghashIterator_getKey(&gh_iter); + BMFace *f_copy = BLI_ghashIterator_getValue(&gh_iter); + BLI_assert(f->len == f_copy->len); + + BMLoop *l_iter, *l_first, *l_copy; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + l_copy = BM_FACE_FIRST_LOOP(f_copy); + do { + /* TODO: Restore only the elements that transform. */ + BM_elem_attrs_copy(bm_copy, bm, l_copy, l_iter); + l_copy = l_copy->next; + } while ((l_iter = l_iter->next) != l_first); + + BM_elem_attrs_copy_ex(bm_copy, bm, f_copy, f, BM_ELEM_SELECT, CD_MASK_NORMAL); } } } @@ -1376,8 +1554,9 @@ static void transform_apply_to_mirror(TransInfo *t) void recalcData_mesh(TransInfo *t) { + bool is_cancelling = t->state == TRANS_CANCEL; /* mirror modifier clipping? */ - if (t->state != TRANS_CANCEL) { + if (!is_cancelling) { /* apply clipping after so we never project past the clip plane [#25423] */ applyProject(t); clipMirrorModifier(t); @@ -1385,13 +1564,11 @@ void recalcData_mesh(TransInfo *t) if ((t->flag & T_NO_MIRROR) == 0 && (t->options & CTX_NO_MIRROR) == 0) { transform_apply_to_mirror(t); } - } - if (t->mode == TFM_EDGE_SLIDE) { - projectEdgeSlideData(t, false); + mesh_customdatacorrect_apply(t, false); } - else if (t->mode == TFM_VERT_SLIDE) { - projectVertSlideData(t, false); + else { + mesh_customdatacorrect_restore(t); } FOREACH_TRANS_DATA_CONTAINER (t, tc) { @@ -1409,22 +1586,18 @@ void recalcData_mesh(TransInfo *t) void special_aftertrans_update__mesh(bContext *UNUSED(C), TransInfo *t) { - const bool canceled = (t->state == TRANS_CANCEL); - if (t->mode == TFM_EDGE_SLIDE) { - /* handle multires re-projection, done + const bool is_cancelling = (t->state == TRANS_CANCEL); + const bool use_automerge = !is_cancelling && (t->flag & (T_AUTOMERGE | T_AUTOSPLIT)) != 0; + + if (!is_cancelling && ELEM(t->mode, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) { + /* Handle multires re-projection, done * on transform completion since it's - * really slow -joeedh */ - projectEdgeSlideData(t, !canceled); - } - else if (t->mode == TFM_VERT_SLIDE) { - /* as above */ - projectVertSlideData(t, !canceled); + * really slow -joeedh. */ + mesh_customdatacorrect_apply(t, true); } - bool use_automerge = !canceled && (t->flag & (T_AUTOMERGE | T_AUTOSPLIT)) != 0; if (use_automerge) { FOREACH_TRANS_DATA_CONTAINER (t, tc) { - BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); BMesh *bm = em->bm; char hflag; |