From 3bd4f229beb1e18802d93c9f5a44b0ec80828a26 Mon Sep 17 00:00:00 2001 From: mano-wii Date: Wed, 11 Sep 2019 13:48:42 -0300 Subject: Transform: Edit Mesh: Support mirror on all axes Part of T68930 Now two other mirror options that can be enabled simultaneously: Mirror Y and Z. Reviewers: campbellbarton Reviewed By: campbellbarton Subscribers: ThatAsherGuy Differential Revision: https://developer.blender.org/D5720 --- .../scripts/startup/bl_ui/space_view3d_toolbar.py | 16 +- source/blender/editors/transform/transform.c | 434 +------------ source/blender/editors/transform/transform.h | 76 +-- .../blender/editors/transform/transform_convert.c | 26 +- .../blender/editors/transform/transform_convert.h | 2 + .../editors/transform/transform_convert_mesh.c | 720 ++++++++++++++++++--- .../blender/editors/transform/transform_generics.c | 63 +- source/blender/makesrna/intern/rna_mesh.c | 2 - 8 files changed, 709 insertions(+), 630 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index f0712e0ae86..ff3cbb08aab 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -177,11 +177,21 @@ class VIEW3D_PT_tools_meshedit_options(View3DPanel, Panel): ob = context.active_object mesh = ob.data - col = layout.column(align=True) - col.prop(mesh, "use_mirror_x") + split = layout.split() + + col = split.column() + col.alignment = 'RIGHT' + col.label(text="Mirror") + + col = split.column() row = col.row(align=True) - row.active = ob.data.use_mirror_x + row.prop(mesh, "use_mirror_x", text="X", toggle=True) + row.prop(mesh, "use_mirror_y", text="Y", toggle=True) + row.prop(mesh, "use_mirror_z", text="Z", toggle=True) + + row = layout.row(align=True) + row.active = ob.data.use_mirror_x or ob.data.use_mirror_y or ob.data.use_mirror_z row.prop(mesh, "use_mirror_topology") diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index d3b331faca9..ff1b9095c81 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -6389,367 +6389,6 @@ static void applyBoneEnvelope(TransInfo *t, const int UNUSED(mval[2])) } /** \} */ -/* -------------------------------------------------------------------- */ -/* Original Data Store */ - -/** \name Orig-Data Store Utility Functions - * \{ */ - -static void slide_origdata_init_flag(TransInfo *t, TransDataContainer *tc, SlideOrigData *sod) -{ - BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); - BMesh *bm = em->bm; - const bool has_layer_math = CustomData_has_math(&bm->ldata); - const int 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))) { - sod->use_origfaces = true; - sod->cd_loop_mdisp_offset = cd_loop_mdisp_offset; - } - else { - sod->use_origfaces = false; - sod->cd_loop_mdisp_offset = -1; - } -} - -static void slide_origdata_init_data(TransDataContainer *tc, SlideOrigData *sod) -{ - if (sod->use_origfaces) { - BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); - BMesh *bm = em->bm; - - sod->origfaces = BLI_ghash_ptr_new(__func__); - sod->bm_origfaces = BM_mesh_create(&bm_mesh_allocsize_default, - &((struct BMeshCreateParams){ - .use_toolflags = false, - })); - /* we need to have matching customdata */ - BM_mesh_copy_init_customdata(sod->bm_origfaces, bm, NULL); - } -} - -static void slide_origdata_create_data_vert(BMesh *bm, - SlideOrigData *sod, - TransDataGenericSlideVert *sv) -{ - 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, sv->v); - l_num = liter.count; - loop_weights = BLI_array_alloca(loop_weights, l_num); - for (j = 0; j < l_num; j++) { - BMLoop *l = BM_iter_step(&liter); - BMLoop *l_prev, *l_next; - void **val_p; - if (!BLI_ghash_ensure_p(sod->origfaces, l->f, &val_p)) { - BMFace *f_copy = BM_face_copy(sod->bm_origfaces, bm, l->f, true, true); - *val_p = f_copy; - } - - 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 (sod->layer_math_map_num && (l_num != 0)) { - sv->cd_loop_groups = BLI_memarena_alloc(sod->arena, sod->layer_math_map_num * sizeof(void *)); - for (j = 0; j < sod->layer_math_map_num; j++) { - const int layer_nr = sod->layer_math_map[j]; - sv->cd_loop_groups[j] = BM_vert_loop_groups_data_layer_create( - bm, sv->v, layer_nr, loop_weights, sod->arena); - } - } - else { - sv->cd_loop_groups = NULL; - } - - BLI_ghash_insert(sod->origverts, sv->v, sv); -} - -static void slide_origdata_create_data(TransDataContainer *tc, - SlideOrigData *sod, - TransDataGenericSlideVert *sv_array, - unsigned int v_stride, - unsigned int v_num) -{ - if (sod->use_origfaces) { - BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); - BMesh *bm = em->bm; - unsigned int i; - TransDataGenericSlideVert *sv; - - int layer_index_dst; - int j; - - 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 */ - sod->layer_math_map = MEM_mallocN(bm->ldata.totlayer * sizeof(int), __func__); - for (j = 0; j < bm->ldata.totlayer; j++) { - if (CustomData_layer_has_math(&bm->ldata, j)) { - sod->layer_math_map[layer_index_dst++] = j; - } - } - BLI_assert(layer_index_dst != 0); - } - - sod->layer_math_map_num = layer_index_dst; - - sod->arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); - - sod->origverts = BLI_ghash_ptr_new_ex(__func__, v_num); - - for (i = 0, sv = sv_array; i < v_num; i++, sv = POINTER_OFFSET(sv, v_stride)) { - slide_origdata_create_data_vert(bm, sod, sv); - } - - if (tc->mirror.axis_flag) { - TransData *td = tc->data; - TransDataGenericSlideVert *sv_mirror; - - sod->sv_mirror = MEM_callocN(sizeof(*sv_mirror) * tc->data_len, __func__); - sod->totsv_mirror = tc->data_len; - - sv_mirror = sod->sv_mirror; - - for (i = 0; i < tc->data_len; i++, td++) { - BMVert *eve = td->extra; - /* Check the vertex has been used since both sides - * of the mirror may be selected & sliding. */ - if (eve && !BLI_ghash_haskey(sod->origverts, eve)) { - sv_mirror->v = eve; - copy_v3_v3(sv_mirror->co_orig_3d, eve->co); - - slide_origdata_create_data_vert(bm, sod, sv_mirror); - sv_mirror++; - } - else { - sod->totsv_mirror--; - } - } - - if (sod->totsv_mirror == 0) { - MEM_freeN(sod->sv_mirror); - sod->sv_mirror = NULL; - } - } - } -} - -/** - * If we're sliding the vert, return its original location, if not, the current location is good. - */ -static const float *slide_origdata_orig_vert_co(SlideOrigData *sod, BMVert *v) -{ - TransDataGenericSlideVert *sv = BLI_ghash_lookup(sod->origverts, v); - return sv ? sv->co_orig_3d : v->co; -} - -static void slide_origdata_interp_data_vert(SlideOrigData *sod, - BMesh *bm, - bool is_final, - TransDataGenericSlideVert *sv) -{ - BMIter liter; - int j, l_num; - float *loop_weights; - const bool is_moved = (len_squared_v3v3(sv->v->co, sv->co_orig_3d) > FLT_EPSILON); - const bool do_loop_weight = sod->layer_math_map_num && is_moved; - const bool do_loop_mdisps = is_final && is_moved && (sod->cd_loop_mdisp_offset != -1); - const float *v_proj_axis = sv->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) { - project_plane_normalized_v3_v3v3(v_proj[1], sv->co_orig_3d, v_proj_axis); - } - - // BM_ITER_ELEM (l, &liter, sv->v, BM_LOOPS_OF_VERT) - BM_iter_init(&liter, bm, BM_LOOPS_OF_VERT, sv->v); - l_num = liter.count; - loop_weights = do_loop_weight ? BLI_array_alloca(loop_weights, l_num) : NULL; - for (j = 0; j < l_num; j++) { - BMFace *f_copy; /* the copy of 'f' */ - BMLoop *l = BM_iter_step(&liter); - - f_copy = BLI_ghash_lookup(sod->origfaces, l->f); - - /* 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); - - /* make sure face-attributes are correct (e.g. #MLoopUV, #MLoopCol) */ - BM_elem_attrs_copy_ex(sod->bm_origfaces, bm, f_copy, l->f, 0x0, CD_MASK_NORMAL); - - /* weight the loop */ - if (do_loop_weight) { - const float eps = 1.0e-8f; - const BMLoop *l_prev = l->prev; - const BMLoop *l_next = l->next; - const float *co_prev = slide_origdata_orig_vert_co(sod, l_prev->v); - const float *co_next = slide_origdata_orig_vert_co(sod, l_next->v); - bool co_prev_ok; - bool co_next_ok; - - /* In the unlikely case that we're next to a zero length edge - - * walk around the to the next. - * - * Since we only need to check if the vertex is in this corner, - * its not important _which_ loop - as long as its not overlapping - * 'sv->co_orig_3d', see: T45096. */ - project_plane_normalized_v3_v3v3(v_proj[0], co_prev, v_proj_axis); - while (UNLIKELY(((co_prev_ok = (len_squared_v3v3(v_proj[1], v_proj[0]) > eps)) == false) && - ((l_prev = l_prev->prev) != l->next))) { - co_prev = slide_origdata_orig_vert_co(sod, l_prev->v); - project_plane_normalized_v3_v3v3(v_proj[0], co_prev, v_proj_axis); - } - project_plane_normalized_v3_v3v3(v_proj[2], co_next, v_proj_axis); - while (UNLIKELY(((co_next_ok = (len_squared_v3v3(v_proj[1], v_proj[2]) > eps)) == false) && - ((l_next = l_next->next) != l->prev))) { - co_next = slide_origdata_orig_vert_co(sod, l_next->v); - project_plane_normalized_v3_v3v3(v_proj[2], co_next, v_proj_axis); - } - - if (co_prev_ok && co_next_ok) { - const float dist = dist_signed_squared_to_corner_v3v3v3( - sv->v->co, UNPACK3(v_proj), v_proj_axis); - - loop_weights[j] = (dist >= 0.0f) ? 1.0f : ((dist <= -eps) ? 0.0f : (1.0f + (dist / eps))); - if (UNLIKELY(!isfinite(loop_weights[j]))) { - loop_weights[j] = 0.0f; - } - } - else { - loop_weights[j] = 0.0f; - } - } - } - - if (sod->layer_math_map_num && sv->cd_loop_groups) { - if (do_loop_weight) { - for (j = 0; j < sod->layer_math_map_num; j++) { - BM_vert_loop_groups_data_layer_merge_weights( - bm, sv->cd_loop_groups[j], sod->layer_math_map[j], loop_weights); - } - } - else { - for (j = 0; j < sod->layer_math_map_num; j++) { - BM_vert_loop_groups_data_layer_merge(bm, sv->cd_loop_groups[j], sod->layer_math_map[j]); - } - } - } - - /* Special handling for multires - * - * Interpolate from every other loop (not ideal) - * However values will only be taken from loops which overlap other mdisps. - * */ - if (do_loop_mdisps) { - float(*faces_center)[3] = BLI_array_alloca(faces_center, l_num); - BMLoop *l; - - BM_ITER_ELEM_INDEX (l, &liter, sv->v, BM_LOOPS_OF_VERT, j) { - BM_face_calc_center_median(l->f, faces_center[j]); - } - - BM_ITER_ELEM_INDEX (l, &liter, sv->v, BM_LOOPS_OF_VERT, j) { - BMFace *f_copy = BLI_ghash_lookup(sod->origfaces, l->f); - float f_copy_center[3]; - BMIter liter_other; - BMLoop *l_other; - int j_other; - - BM_face_calc_center_median(f_copy, f_copy_center); - - BM_ITER_ELEM_INDEX (l_other, &liter_other, sv->v, BM_LOOPS_OF_VERT, j_other) { - BM_face_interp_multires_ex(bm, - l_other->f, - f_copy, - faces_center[j_other], - f_copy_center, - sod->cd_loop_mdisp_offset); - } - } - } -} - -static void slide_origdata_interp_data(Object *obedit, - SlideOrigData *sod, - TransDataGenericSlideVert *sv, - unsigned int v_stride, - unsigned int v_num, - bool is_final) -{ - if (sod->use_origfaces) { - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMesh *bm = em->bm; - unsigned int i; - const bool has_mdisps = (sod->cd_loop_mdisp_offset != -1); - - for (i = 0; i < v_num; i++, sv = POINTER_OFFSET(sv, v_stride)) { - - if (sv->cd_loop_groups || has_mdisps) { - slide_origdata_interp_data_vert(sod, bm, is_final, sv); - } - } - - if (sod->sv_mirror) { - sv = sod->sv_mirror; - for (i = 0; i < v_num; i++, sv++) { - if (sv->cd_loop_groups || has_mdisps) { - slide_origdata_interp_data_vert(sod, bm, is_final, sv); - } - } - } - } -} - -static void slide_origdata_free_date(SlideOrigData *sod) -{ - if (sod->use_origfaces) { - if (sod->bm_origfaces) { - BM_mesh_free(sod->bm_origfaces); - sod->bm_origfaces = NULL; - } - - if (sod->origfaces) { - BLI_ghash_free(sod->origfaces, NULL, NULL); - sod->origfaces = NULL; - } - - if (sod->origverts) { - BLI_ghash_free(sod->origverts, NULL, NULL); - sod->origverts = NULL; - } - - if (sod->arena) { - BLI_memarena_free(sod->arena); - sod->arena = NULL; - } - - MEM_SAFE_FREE(sod->layer_math_map); - - MEM_SAFE_FREE(sod->sv_mirror); - } -} - -/** \} */ - /* -------------------------------------------------------------------- */ /* Transform (Edge Slide) */ @@ -7201,8 +6840,6 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, TransDataContainer *t View3D *v3d = NULL; RegionView3D *rv3d = NULL; - slide_origdata_init_flag(t, tc, &sld->orig_data); - sld->curr_sv_index = 0; /*ensure valid selection*/ @@ -7533,18 +7170,10 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, TransDataContainer *t calcEdgeSlide_mval_range(t, tc, sld, sv_table, loop_nr, mval, use_occlude_geometry, true); - /* create copies of faces for customdata projection */ - bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); - slide_origdata_init_data(tc, &sld->orig_data); - slide_origdata_create_data( - tc, &sld->orig_data, (TransDataGenericSlideVert *)sld->sv, sizeof(*sld->sv), sld->totsv); - if (rv3d) { calcEdgeSlide_even(t, tc, sld, mval); } - sld->em = em; - tc->custom.mode.data = sld; MEM_freeN(sv_table); @@ -7578,8 +7207,6 @@ static bool createEdgeSlideVerts_single_side(TransInfo *t, TransDataContainer *t rv3d = t->ar ? t->ar->regiondata : NULL; } - slide_origdata_init_flag(t, tc, &sld->orig_data); - sld->curr_sv_index = 0; /* ensure valid selection */ { @@ -7728,18 +7355,10 @@ static bool createEdgeSlideVerts_single_side(TransInfo *t, TransDataContainer *t calcEdgeSlide_mval_range(t, tc, sld, sv_table, loop_nr, mval, use_occlude_geometry, false); - /* create copies of faces for customdata projection */ - bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); - slide_origdata_init_data(tc, &sld->orig_data); - slide_origdata_create_data( - tc, &sld->orig_data, (TransDataGenericSlideVert *)sld->sv, sizeof(*sld->sv), sld->totsv); - if (rv3d) { calcEdgeSlide_even(t, tc, sld, mval); } - sld->em = em; - tc->custom.mode.data = sld; MEM_freeN(sv_table); @@ -7756,25 +7375,10 @@ void projectEdgeSlideData(TransInfo *t, bool is_final) continue; } - SlideOrigData *sod = &sld->orig_data; - if (sod->use_origfaces == false) { - continue; - } - - slide_origdata_interp_data(tc->obedit, - sod, - (TransDataGenericSlideVert *)sld->sv, - sizeof(*sld->sv), - sld->totsv, - is_final); + trans_mesh_customdata_correction_apply(tc, is_final); } } -void freeEdgeSlideTempFaces(EdgeSlideData *sld) -{ - slide_origdata_free_date(&sld->orig_data); -} - void freeEdgeSlideVerts(TransInfo *UNUSED(t), TransDataContainer *UNUSED(tc), TransCustomData *custom_data) @@ -7785,10 +7389,6 @@ void freeEdgeSlideVerts(TransInfo *UNUSED(t), return; } - freeEdgeSlideTempFaces(sld); - - bmesh_edit_end(sld->em->bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); - MEM_freeN(sld->sv); MEM_freeN(sld); @@ -7847,6 +7447,8 @@ static void initEdgeSlide_ex( tc->custom.mode.free_cb = freeEdgeSlideVerts; } + trans_mesh_customdata_correction_init(t); + /* set custom point first if you want value to be initialized by init */ calcEdgeSlideCustomPoints(t); initMouseInputMode(t, &t->mouse, INPUT_CUSTOM_RATIO_FLIP); @@ -8322,8 +7924,6 @@ static bool createVertSlideVerts(TransInfo *t, TransDataContainer *tc) VertSlideData *sld = MEM_callocN(sizeof(*sld), "sld"); int j; - slide_origdata_init_flag(t, tc, &sld->orig_data); - sld->curr_sv_index = 0; j = 0; @@ -8387,13 +7987,6 @@ static bool createVertSlideVerts(TransInfo *t, TransDataContainer *tc) sld->sv = sv_array; sld->totsv = j; - bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); - slide_origdata_init_data(tc, &sld->orig_data); - slide_origdata_create_data( - tc, &sld->orig_data, (TransDataGenericSlideVert *)sld->sv, sizeof(*sld->sv), sld->totsv); - - sld->em = em; - tc->custom.mode.data = sld; /* most likely will be set below */ @@ -8423,23 +8016,10 @@ void projectVertSlideData(TransInfo *t, bool is_final) { FOREACH_TRANS_DATA_CONTAINER (t, tc) { VertSlideData *sld = tc->custom.mode.data; - SlideOrigData *sod = &sld->orig_data; - if (sod->use_origfaces == true) { - slide_origdata_interp_data(tc->obedit, - sod, - (TransDataGenericSlideVert *)sld->sv, - sizeof(*sld->sv), - sld->totsv, - is_final); - } + trans_mesh_customdata_correction_apply(tc, is_final); } } -void freeVertSlideTempFaces(VertSlideData *sld) -{ - slide_origdata_free_date(&sld->orig_data); -} - void freeVertSlideVerts(TransInfo *UNUSED(t), TransDataContainer *UNUSED(tc), TransCustomData *custom_data) @@ -8450,10 +8030,6 @@ void freeVertSlideVerts(TransInfo *UNUSED(t), return; } - freeVertSlideTempFaces(sld); - - bmesh_edit_end(sld->em->bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); - if (sld->totsv > 0) { TransDataVertSlideVert *sv = sld->sv; int i = 0; @@ -8503,6 +8079,8 @@ static void initVertSlide_ex(TransInfo *t, bool use_even, bool flipped, bool use return; } + trans_mesh_customdata_correction_init(t); + /* set custom point first if you want value to be initialized by init */ calcVertSlideCustomPoints(t); initMouseInputMode(t, &t->mouse, INPUT_CUSTOM_RATIO); diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index 92db32fa836..b90fff29a84 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -294,38 +294,11 @@ typedef struct TransDataEdgeSlideVert { int loop_nr; } TransDataEdgeSlideVert; -/* store original data so we can correct UV's and similar when sliding */ -typedef struct SlideOrigData { - /** Set when #origfaces is initialized. */ - bool use_origfaces; - int cd_loop_mdisp_offset; - - /** map {BMVert: TransDataGenericSlideVert} */ - 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 TransDataVertSlideVert.cd_group index to absolute CustomData layer index */ - int *layer_math_map; - - /** Array of slide vert data especially for mirror verts. */ - TransDataGenericSlideVert *sv_mirror; - int totsv_mirror; -} SlideOrigData; - typedef struct EdgeSlideData { TransDataEdgeSlideVert *sv; int totsv; int mval_start[2], mval_end[2]; - struct BMEditMesh *em; - - SlideOrigData orig_data; - int curr_sv_index; /** when un-clamped - use this index: #TransDataEdgeSlideVert.dir_side */ @@ -354,11 +327,6 @@ typedef struct TransDataVertSlideVert { typedef struct VertSlideData { TransDataVertSlideVert *sv; int totsv; - - struct BMEditMesh *em; - - SlideOrigData orig_data; - int curr_sv_index; /* result of ED_view3d_ob_project_mat_get */ @@ -449,6 +417,18 @@ typedef struct TransData { short protectflag; } TransData; +typedef struct TransDataMirror { + /** location of mirrored reference data. */ + const float *loc_src; + /** Location of the data to transform. */ + float *loc_dst; + void *extra; + /* `sign` can be -2, -1, 0 or 1. */ + int sign_x : 2; + int sign_y : 2; + int sign_z : 2; +} TransDataMirror; + typedef struct MouseInput { void (*apply)(struct TransInfo *t, struct MouseInput *mi, const double mval[2], float output[3]); void (*post)(struct TransInfo *t, float values[3]); @@ -549,10 +529,18 @@ typedef struct TransDataContainer { * Mirror option */ struct { - /* Currently for mesh X mirror only. */ - int axis_flag; - /** Set to -1.0f or 1.0 when use_mirror is set. */ - float sign; + union { + struct { + uint axis_x : 1; + uint axis_y : 1; + uint axis_z : 1; + }; + /* For easy checking. */ + char use_mirror_any; + }; + /** Mirror data array. */ + TransDataMirror *data; + int data_len; } mirror; TransCustomDataContainer custom; @@ -860,16 +848,18 @@ enum { /** For Graph Editor - curves that can only have int-values * need their keyframes tagged with this. */ TD_INTVALUES = 1 << 15, - /** For editmode mirror, clamp to x = 0 */ - TD_MIRROR_EDGE = 1 << 16, + /** For editmode mirror, clamp axis to 0 */ + TD_MIRROR_EDGE_X = 1 << 16, + TD_MIRROR_EDGE_Y = 1 << 17, + TD_MIRROR_EDGE_Z = 1 << 18, /** For fcurve handles, move them along with their keyframes */ - TD_MOVEHANDLE1 = 1 << 17, - TD_MOVEHANDLE2 = 1 << 18, + TD_MOVEHANDLE1 = 1 << 19, + TD_MOVEHANDLE2 = 1 << 20, /** Exceptional case with pose bone rotating when a parent bone has 'Local Location' * option enabled and rotating also transforms it. */ - TD_PBONE_LOCAL_MTX_P = 1 << 19, + TD_PBONE_LOCAL_MTX_P = 1 << 21, /** Same as above but for a child bone. */ - TD_PBONE_LOCAL_MTX_C = 1 << 20, + TD_PBONE_LOCAL_MTX_C = 1 << 22, }; /** #TransSnap.status */ @@ -1091,11 +1081,9 @@ int getTransformOrientation(const struct bContext *C, float normal[3], float pla void freeCustomNormalArray(TransInfo *t, TransDataContainer *tc, TransCustomData *custom_data); -void freeEdgeSlideTempFaces(EdgeSlideData *sld); void freeEdgeSlideVerts(TransInfo *t, TransDataContainer *tc, TransCustomData *custom_data); void projectEdgeSlideData(TransInfo *t, bool is_final); -void freeVertSlideTempFaces(VertSlideData *sld); void freeVertSlideVerts(TransInfo *t, TransDataContainer *tc, TransCustomData *custom_data); void projectVertSlideData(TransInfo *t, bool is_final); diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index dfdd7b71332..1e783e0e7b8 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -1778,18 +1778,16 @@ static void special_aftertrans_update__mesh(bContext *UNUSED(C), TransInfo *t) char hflag; bool has_face_sel = (bm->totfacesel != 0); - if (tc->mirror.axis_flag) { - TransData *td; + if (tc->mirror.use_mirror_any) { + TransDataMirror *tdm; int i; /* Rather then adjusting the selection (which the user would notice) * tag all mirrored verts, then auto-merge those. */ BM_mesh_elem_hflag_disable_all(bm, BM_VERT, BM_ELEM_TAG, false); - for (i = 0, td = tc->data; i < tc->data_len; i++, td++) { - if (td->extra) { - BM_elem_flag_enable((BMVert *)td->extra, BM_ELEM_TAG); - } + for (i = tc->mirror.data_len, tdm = tc->mirror.data; i--; tdm++) { + BM_elem_flag_enable((BMVert *)tdm->extra, BM_ELEM_TAG); } hflag = BM_ELEM_SELECT | BM_ELEM_TAG; @@ -1850,26 +1848,10 @@ void special_aftertrans_update(bContext *C, TransInfo *t) * on transform completion since it's * really slow -joeedh */ projectEdgeSlideData(t, true); - - FOREACH_TRANS_DATA_CONTAINER (t, tc) { - EdgeSlideData *sld = tc->custom.mode.data; - - if (sld == NULL) { - continue; - } - - /* Free temporary faces to avoid auto-merging and deleting - * during cleanup - psy-fi. */ - freeEdgeSlideTempFaces(sld); - } } else if (t->mode == TFM_VERT_SLIDE) { /* as above */ projectVertSlideData(t, true); - FOREACH_TRANS_DATA_CONTAINER (t, tc) { - VertSlideData *sld = tc->custom.mode.data; - freeVertSlideTempFaces(sld); - } } if (t->obedit_type == OB_MESH) { diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h index 02ad4688ab6..53ff9952d05 100644 --- a/source/blender/editors/transform/transform_convert.h +++ b/source/blender/editors/transform/transform_convert.h @@ -69,6 +69,8 @@ void flushTransGraphData(TransInfo *t); void flushTransMasking(TransInfo *t); /* transform_convert_mesh.c */ void flushTransUVs(TransInfo *t); +void trans_mesh_customdata_correction_init(TransInfo *t); +void trans_mesh_customdata_correction_apply(struct TransDataContainer *tc, bool is_final); /* transform_convert_node.c */ void flushTransNodes(TransInfo *t); /* transform_convert_object.c */ diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c index 98ae15ed2dc..32239dd0bd7 100644 --- a/source/blender/editors/transform/transform_convert_mesh.c +++ b/source/blender/editors/transform/transform_convert_mesh.c @@ -27,8 +27,10 @@ #include "MEM_guardedalloc.h" +#include "BLI_alloca.h" #include "BLI_bitmap.h" #include "BLI_math.h" +#include "BLI_memarena.h" #include "BLI_linklist_stack.h" #include "BKE_context.h" @@ -52,6 +54,9 @@ #include "transform_convert.h" #include "bmesh.h" +/* Used for both mirror epsilon and TD_MIRROR_EDGE_ */ +#define TRANSFORM_MAXDIST_MIRROR 0.00002f + /* when transforming islands */ struct TransIslandData { float co[3]; @@ -395,6 +400,164 @@ static struct TransIslandData *editmesh_islands_info_calc(BMEditMesh *em, return trans_islands; } +static bool is_in_quadrant_v3(const float co[3], const int quadrant[3], const float epsilon) +{ + if (quadrant[0] && ((co[0] * quadrant[0]) < -epsilon)) { + return false; + } + if (quadrant[1] && ((co[1] * quadrant[1]) < -epsilon)) { + return false; + } + if (quadrant[2] && ((co[2] * quadrant[2]) < -epsilon)) { + return false; + } + 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) +{ + 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]); + } + } + + BMVert *eve; + BMIter iter; + + 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; + } + } + } + + /* Tag only elements that will be transformed within the quadrant. */ + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { + 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); + } + else { + BM_elem_flag_disable(eve, BM_ELEM_TAG); + BM_elem_index_set(eve, -1); + } + } + + for (int a = 0; a < 3; a++) { + int *index_iter = index[a]; + if (index_iter == NULL) { + continue; + } + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { + if (BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { + continue; + } + if (test_selected_only && !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; + } + } + } + } + + /* 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) { + mirror_elem_len++; + } + } + + TransDataMirror *mirror_data_iter, *mirror_data = NULL; + if (mirror_elem_len != 0) { + mirror_data = MEM_mallocN(mirror_elem_len * sizeof(*mirror_data), __func__); + mirror_data_iter = &mirror_data[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); + + mirror_data_iter->loc_src = v_src->co; + mirror_data_iter->loc_dst = eve->co; + mirror_data_iter->sign_x = index[0] && index[0][i] == -2 ? -1 : 1; + mirror_data_iter->sign_y = index[1] && index[1][i] == -2 ? -1 : 1; + mirror_data_iter->sign_z = index[2] && index[2][i] == -2 ? -1 : 1; + mirror_data_iter->extra = eve; + + mirror_data_iter++; + + BLI_BITMAP_ENABLE(*r_mirror_bitmap, i); + } + } + } + + 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 mirror_data; +} + /* way to overwrite what data is edited with transform */ static void VertsToTransData(TransInfo *t, TransData *td, @@ -448,7 +611,7 @@ static void VertsToTransData(TransInfo *t, td->ext = NULL; td->val = NULL; - td->extra = NULL; + td->extra = eve; if (t->mode == TFM_BWEIGHT) { td->val = bweight; td->ival = *bweight; @@ -487,9 +650,7 @@ void createTransEditVerts(TransInfo *t) float *dists = NULL; int a; const int prop_mode = (t->flag & T_PROP_EDIT) ? (t->flag & T_PROP_EDIT_ALL) : 0; - int mirror = 0; int cd_vert_bweight_offset = -1; - bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; struct TransIslandData *island_info = NULL; int island_info_tot; @@ -510,11 +671,6 @@ void createTransEditVerts(TransInfo *t) * Optional, allocate if needed. */ int *dists_index = NULL; - if (tc->mirror.axis_flag) { - EDBM_verts_mirror_cache_begin(em, 0, false, (t->flag & T_PROP_EDIT) == 0, use_topology); - mirror = 1; - } - /** * Quick check if we can transform. * @@ -531,16 +687,23 @@ void createTransEditVerts(TransInfo *t) cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT); } + BLI_bitmap *mirror_bitmap = NULL; + if (tc->mirror.use_mirror_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->mirror.axis_x, tc->mirror.axis_y, tc->mirror.axis_z}; + tc->mirror.data = editmesh_mirror_data_calc( + em, use_select, use_topology, mirror_axis, &tc->mirror.data_len, &mirror_bitmap); + } + + int data_len = 0; if (prop_mode) { - unsigned int count = 0; BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { - count++; + data_len++; } } - tc->data_len = count; - /* allocating scratch arrays */ if (prop_mode & T_PROP_CONNECTED) { dists = MEM_mallocN(em->bm->totvert * sizeof(float), __func__); @@ -550,10 +713,23 @@ void createTransEditVerts(TransInfo *t) } } else { - tc->data_len = bm->totvertsel; + 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--; + } + } + } } - tob = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransObData(Mesh EditMode)"); + BLI_assert(data_len != 0); + + tc->data_len = data_len; + tc->data = tob = 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 @@ -622,95 +798,91 @@ void createTransEditVerts(TransInfo *t) } } - /* find out which half we do */ - if (mirror) { - BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { - if (BM_elem_flag_test(eve, BM_ELEM_SELECT) && eve->co[0] != 0.0f) { - if (eve->co[0] < 0.0f) { - tc->mirror.sign = -1.0f; - mirror = -1; - } - break; - } + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, a) { + if (BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { + continue; } - } + if (mirror_bitmap && BLI_BITMAP_TEST(mirror_bitmap, a)) { + continue; + } + if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + struct TransIslandData *v_island = NULL; + float *bweight = (cd_vert_bweight_offset != -1) ? + BM_ELEM_CD_GET_VOID_P(eve, cd_vert_bweight_offset) : + NULL; + + if (island_info) { + const int connected_index = (dists_index && dists_index[a] != -1) ? dists_index[a] : a; + v_island = (island_vert_map[connected_index] != -1) ? + &island_info[island_vert_map[connected_index]] : + NULL; + } - BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, a) { - if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { - if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) { - struct TransIslandData *v_island = NULL; - float *bweight = (cd_vert_bweight_offset != -1) ? - BM_ELEM_CD_GET_VOID_P(eve, cd_vert_bweight_offset) : - NULL; - - if (island_info) { - const int connected_index = (dists_index && dists_index[a] != -1) ? dists_index[a] : a; - v_island = (island_vert_map[connected_index] != -1) ? - &island_info[island_vert_map[connected_index]] : - 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, v_island, is_snap_rotate); + if (tx) { + tx++; + } - /* 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, v_island, is_snap_rotate); - if (tx) { - tx++; - } + /* selected */ + if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + tob->flag |= TD_SELECTED; + } - /* 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]); } - /* Mirror? */ - if ((mirror > 0 && tob->iloc[0] > 0.0f) || (mirror < 0 && tob->iloc[0] < 0.0f)) { - BMVert *vmir = EDBM_verts_mirror_get(em, eve); // t->obedit, em, eve, tob->iloc, a); - if (vmir && vmir != eve) { - tob->extra = vmir; - } + 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); + } + + if (tc->mirror.use_mirror_any) { + if (tc->mirror.axis_x && fabsf(tob->loc[0]) < TRANSFORM_MAXDIST_MIRROR) { + tob->flag |= TD_MIRROR_EDGE_X; + } + if (tc->mirror.axis_y && fabsf(tob->loc[1]) < TRANSFORM_MAXDIST_MIRROR) { + tob->flag |= TD_MIRROR_EDGE_Y; + } + if (tc->mirror.axis_z && fabsf(tob->loc[2]) < TRANSFORM_MAXDIST_MIRROR) { + tob->flag |= TD_MIRROR_EDGE_Z; } - tob++; } + + tob++; } } @@ -719,15 +891,6 @@ void createTransEditVerts(TransInfo *t) MEM_freeN(island_vert_map); } - if (mirror != 0) { - tob = tc->data; - for (a = 0; a < tc->data_len; a++, tob++) { - if (ABS(tob->loc[0]) <= 0.00001f) { - tob->flag |= TD_MIRROR_EDGE; - } - } - } - cleanup: /* crazy space free */ if (quats) { @@ -742,9 +905,374 @@ void createTransEditVerts(TransInfo *t) if (dists_index) { MEM_freeN(dists_index); } + if (mirror_bitmap) { + MEM_freeN(mirror_bitmap); + } + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name CustomData Layer Correction (for meshes) + * + * \{ */ + +struct TransCustomDataLayerVert { + BMVert *v; + float co_orig_3d[3]; + struct LinkNode **cd_loop_groups; +}; + +struct TransCustomDataLayer { + BMesh *bm; + + 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; +}; + +static void trans_mesh_customdata_free_cb(struct TransInfo *t, + struct TransDataContainer *tc, + struct TransCustomData *custom_data) +{ + struct TransCustomDataLayer *tcld = custom_data->data; + bmesh_edit_end(tcld->bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); + + if (tcld->bm_origfaces) { + BM_mesh_free(tcld->bm_origfaces); + } + if (tcld->origfaces) { + BLI_ghash_free(tcld->origfaces, NULL, NULL); + } + if (tcld->origverts) { + BLI_ghash_free(tcld->origverts, NULL, NULL); + } + if (tcld->arena) { + BLI_memarena_free(tcld->arena); + } + if (tcld->layer_math_map) { + MEM_freeN(tcld->layer_math_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) +{ + BMesh *bm = tcld->bm; + 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); + for (j = 0; j < l_num; j++) { + BMLoop *l = BM_iter_step(&liter); + BMLoop *l_prev, *l_next; + 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; + } + + 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); + } + } + else { + r_tcld_vert->cd_loop_groups = NULL; + } + + 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); +} + +void trans_mesh_customdata_correction_init(TransInfo *t) +{ + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + BLI_assert(tc->custom.type.data == NULL); + int i; + + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); + BMesh *bm = em->bm; + + 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; + cd_loop_mdisp_offset = cd_loop_mdisp_offset; + } + else { + use_origfaces = false; + cd_loop_mdisp_offset = -1; + } + } + + if (use_origfaces) { + /* create copies of faces for customdata projection */ + bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); + + 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 customdata */ + BM_mesh_copy_init_customdata(bm_origfaces, bm, NULL); + + 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; + 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->mirror.data_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); + } + + TransDataMirror *tdm; + for (i = tc->mirror.data_len, tdm = tc->mirror.data; i--; tdm++, tcld_vert_iter++) { + BMVert *v = tdm->extra; + create_trans_vert_customdata_layer(v, tcld, tcld_vert_iter); + } + + tcld->data = tcld_vert; + tcld->data_len = data_len; + } + } +} + +/** + * If we're sliding the vert, return its original location, if not, the current location is good. + */ +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; +} + +static void trans_mesh_customdata_correction_apply_vert(struct TransCustomDataLayer *tcld, + struct TransCustomDataLayerVert *tcld_vert, + bool is_final) +{ + 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; + + 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 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) { + project_plane_normalized_v3_v3v3(v_proj[1], co_orig_3d, v_proj_axis); + } + + // 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 = do_loop_weight ? BLI_array_alloca(loop_weights, l_num) : NULL; + for (j = 0; j < l_num; j++) { + BMFace *f_copy; /* the copy of 'f' */ + BMLoop *l = BM_iter_step(&liter); + + f_copy = BLI_ghash_lookup(tcld->origfaces, l->f); + + /* 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); + + /* make sure face-attributes are correct (e.g. #MLoopUV, #MLoopCol) */ + BM_elem_attrs_copy_ex(tcld->bm_origfaces, bm, f_copy, l->f, 0x0, CD_MASK_NORMAL); + + /* weight the loop */ + if (do_loop_weight) { + const float eps = 1.0e-8f; + const BMLoop *l_prev = l->prev; + const BMLoop *l_next = l->next; + const float *co_prev = trans_vert_orig_co_get(tcld, l_prev->v); + const float *co_next = trans_vert_orig_co_get(tcld, l_next->v); + bool co_prev_ok; + bool co_next_ok; + + /* In the unlikely case that we're next to a zero length edge - + * walk around the to the next. + * + * Since we only need to check if the vertex is in this corner, + * its not important _which_ loop - as long as its not overlapping + * 'sv->co_orig_3d', see: T45096. */ + project_plane_normalized_v3_v3v3(v_proj[0], co_prev, v_proj_axis); + while (UNLIKELY(((co_prev_ok = (len_squared_v3v3(v_proj[1], v_proj[0]) > eps)) == false) && + ((l_prev = l_prev->prev) != l->next))) { + co_prev = trans_vert_orig_co_get(tcld, l_prev->v); + project_plane_normalized_v3_v3v3(v_proj[0], co_prev, v_proj_axis); + } + project_plane_normalized_v3_v3v3(v_proj[2], co_next, v_proj_axis); + while (UNLIKELY(((co_next_ok = (len_squared_v3v3(v_proj[1], v_proj[2]) > eps)) == false) && + ((l_next = l_next->next) != l->prev))) { + co_next = trans_vert_orig_co_get(tcld, l_next->v); + project_plane_normalized_v3_v3v3(v_proj[2], co_next, v_proj_axis); + } + + if (co_prev_ok && co_next_ok) { + const float dist = dist_signed_squared_to_corner_v3v3v3( + v->co, UNPACK3(v_proj), v_proj_axis); + + loop_weights[j] = (dist >= 0.0f) ? 1.0f : ((dist <= -eps) ? 0.0f : (1.0f + (dist / eps))); + if (UNLIKELY(!isfinite(loop_weights[j]))) { + loop_weights[j] = 0.0f; + } + } + else { + loop_weights[j] = 0.0f; + } + } + } + + 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); + } + } + 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]); + } + } + } + + /* Special handling for multires + * + * Interpolate from every other loop (not ideal) + * However values will only be taken from loops which overlap other mdisps. + * */ + if (do_loop_mdisps) { + float(*faces_center)[3] = BLI_array_alloca(faces_center, l_num); + BMLoop *l; + + BM_ITER_ELEM_INDEX (l, &liter, v, BM_LOOPS_OF_VERT, j) { + BM_face_calc_center_median(l->f, faces_center[j]); + } + + BM_ITER_ELEM_INDEX (l, &liter, v, BM_LOOPS_OF_VERT, j) { + BMFace *f_copy = BLI_ghash_lookup(tcld->origfaces, l->f); + float f_copy_center[3]; + BMIter liter_other; + BMLoop *l_other; + int j_other; + + BM_face_calc_center_median(f_copy, f_copy_center); + + BM_ITER_ELEM_INDEX (l_other, &liter_other, v, BM_LOOPS_OF_VERT, j_other) { + BM_face_interp_multires_ex(bm, + l_other->f, + f_copy, + faces_center[j_other], + f_copy_center, + tcld->cd_loop_mdisp_offset); + } + } + } +} + +void trans_mesh_customdata_correction_apply(struct TransDataContainer *tc, bool is_final) +{ + struct TransCustomDataLayer *tcld = tc->custom.type.data; + if (!tcld) { + return; + } + + const bool has_mdisps = (tcld->cd_loop_mdisp_offset != -1); + struct TransCustomDataLayerVert *tcld_vert_iter = &tcld->data[0]; - if (tc->mirror.axis_flag) { - EDBM_verts_mirror_cache_end(em); + 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); } } } diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index 32d8c7381e7..e4d3c9abceb 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -224,35 +224,31 @@ static void clipMirrorModifier(TransInfo *t) } /* assumes obedit set to mesh object */ -static void editbmesh_apply_to_mirror(TransInfo *t) +static void transform_apply_to_mirror(TransInfo *t) { FOREACH_TRANS_DATA_CONTAINER (t, tc) { - if (tc->mirror.axis_flag) { - TransData *td = tc->data; - BMVert *eve; + if (tc->mirror.use_mirror_any) { int i; - - for (i = 0; i < tc->data_len; i++, td++) { - if (td->flag & TD_NOACTION) { - break; - } - if (td->loc == NULL) { - break; - } - if (td->flag & TD_SKIP) { - continue; - } - - eve = td->extra; - if (eve) { - eve->co[0] = -td->loc[0]; - eve->co[1] = td->loc[1]; - eve->co[2] = td->loc[2]; + TransData *td; + for (i = 0, td = tc->data; i < tc->data_len; i++, td++) { + if (td->flag & (TD_MIRROR_EDGE_X | TD_MIRROR_EDGE_Y | TD_MIRROR_EDGE_Z)) { + if (td->flag & TD_MIRROR_EDGE_X) { + td->loc[0] = 0.0f; + } + if (td->flag & TD_MIRROR_EDGE_Y) { + td->loc[1] = 0.0f; + } + if (td->flag & TD_MIRROR_EDGE_Z) { + td->loc[2] = 0.0f; + } } + } - if (td->flag & TD_MIRROR_EDGE) { - td->loc[0] = 0; - } + TransDataMirror *tdm; + for (i = 0, tdm = tc->mirror.data; i < tc->mirror.data_len; i++, tdm++) { + tdm->loc_dst[0] = tdm->loc_src[0] * tdm->sign_x; + tdm->loc_dst[1] = tdm->loc_src[1] * tdm->sign_y; + tdm->loc_dst[2] = tdm->loc_src[2] * tdm->sign_z; } } } @@ -889,7 +885,7 @@ static void recalcData_objects(TransInfo *t) clipMirrorModifier(t); } if ((t->flag & T_NO_MIRROR) == 0 && (t->options & CTX_NO_MIRROR) == 0) { - editbmesh_apply_to_mirror(t); + transform_apply_to_mirror(t); } if (t->mode == TFM_EDGE_SLIDE) { @@ -1361,11 +1357,12 @@ void initTransDataContainers_FromObjectData(TransInfo *t, for (int i = 0; i < objects_len; i++) { TransDataContainer *tc = &t->data_container[i]; - /* TODO, multiple axes. */ - tc->mirror.axis_flag = (((t->flag & T_NO_MIRROR) == 0) && - ((t->options & CTX_NO_MIRROR) == 0) && - (objects[i]->type == OB_MESH) && - (((Mesh *)objects[i]->data)->editflag & ME_EDIT_MIRROR_X) != 0); + if (((t->flag & T_NO_MIRROR) == 0) && ((t->options & CTX_NO_MIRROR) == 0) && + (objects[i]->type == OB_MESH)) { + tc->mirror.axis_x = (((Mesh *)objects[i]->data)->editflag & ME_EDIT_MIRROR_X) != 0; + tc->mirror.axis_y = (((Mesh *)objects[i]->data)->editflag & ME_EDIT_MIRROR_Y) != 0; + tc->mirror.axis_z = (((Mesh *)objects[i]->data)->editflag & ME_EDIT_MIRROR_Z) != 0; + } if (object_mode & OB_MODE_EDIT) { tc->obedit = objects[i]; @@ -1902,6 +1899,7 @@ void postTrans(bContext *C, TransInfo *t) MEM_SAFE_FREE(tc->data_ext); MEM_SAFE_FREE(tc->data_2d); + MEM_SAFE_FREE(tc->mirror.data); } } @@ -2349,11 +2347,6 @@ void calculatePropRatio(TransInfo *t) if (td->flag & TD_SELECTED) { td->factor = 1.0f; } - else if (tc->mirror.axis_flag && (td->loc[0] * tc->mirror.sign) < -0.00001f) { - td->flag |= TD_SKIP; - td->factor = 0.0f; - restoreElement(td); - } else if ((connected && (td->flag & TD_NOTCONNECTED || td->dist > t->prop_size)) || (connected == 0 && td->rdist > t->prop_size)) { /* diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index f7e0990aa88..be3fa5679e2 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -3088,7 +3088,6 @@ static void rna_def_mesh(BlenderRNA *brna) RNA_def_property_ui_text(prop, "X Mirror", "X Axis mirror editing"); RNA_def_property_update(prop, 0, "rna_Mesh_update_draw"); -# if 0 prop = RNA_def_property(srna, "use_mirror_y", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "editflag", ME_EDIT_MIRROR_Y); RNA_def_property_ui_text(prop, "Y Mirror", "Y Axis mirror editing"); @@ -3096,7 +3095,6 @@ static void rna_def_mesh(BlenderRNA *brna) prop = RNA_def_property(srna, "use_mirror_z", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "editflag", ME_EDIT_MIRROR_Z); RNA_def_property_ui_text(prop, "Z Mirror", "Z Axis mirror editing"); -# endif prop = RNA_def_property(srna, "use_mirror_topology", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "editflag", ME_EDIT_MIRROR_TOPO); -- cgit v1.2.3