diff options
-rw-r--r-- | release/scripts/startup/bl_ui/space_view3d_toolbar.py | 4 | ||||
-rw-r--r-- | source/blender/blenlib/BLI_math_vector.h | 3 | ||||
-rw-r--r-- | source/blender/blenlib/intern/math_geom.c | 45 | ||||
-rw-r--r-- | source/blender/blenlib/intern/math_vector_inline.c | 16 | ||||
-rw-r--r-- | source/blender/editors/transform/transform_convert.h | 2 | ||||
-rw-r--r-- | source/blender/editors/transform/transform_convert_mesh.c | 511 | ||||
-rw-r--r-- | source/blender/editors/transform/transform_mode.c | 2 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_scene_types.h | 2 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_scene.c | 9 |
9 files changed, 384 insertions, 210 deletions
diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index 39e4ee2beac..6e31808d27d 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -208,6 +208,10 @@ class VIEW3D_PT_tools_meshedit_options(View3DPanel, Panel): row = layout.row(align=True, heading="Transform") row.prop(tool_settings, "use_transform_correct_face_attributes") + row = layout.row(align=True) + row.active = tool_settings.use_transform_correct_face_attributes + row.prop(tool_settings, "use_transform_correct_keep_connected") + row = layout.row(heading="Mirror") sub = row.row(align=True) sub.prop(mesh, "use_mirror_x", text="X", toggle=True) diff --git a/source/blender/blenlib/BLI_math_vector.h b/source/blender/blenlib/BLI_math_vector.h index d46c02a961c..362ab3769a0 100644 --- a/source/blender/blenlib/BLI_math_vector.h +++ b/source/blender/blenlib/BLI_math_vector.h @@ -133,6 +133,7 @@ MINLINE void sub_v3_v3v3_db(double r[3], const double a[3], const double b[3]); MINLINE void sub_v4_v4(float r[4], const float a[4]); MINLINE void sub_v4_v4v4(float r[4], const float a[4], const float b[4]); +MINLINE void sub_v2db_v2fl_v2fl(double r[2], const float a[2], const float b[2]); MINLINE void sub_v3db_v3fl_v3fl(double r[3], const float a[3], const float b[3]); MINLINE void mul_v2_fl(float r[2], float f); @@ -205,6 +206,7 @@ MINLINE double dot_v3db_v3fl(const double a[3], const float b[3]) ATTR_WARN_UNUS MINLINE double dot_v3v3_db(const double a[3], const double b[3]) ATTR_WARN_UNUSED_RESULT; MINLINE float cross_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT; +MINLINE double cross_v2v2_db(const double a[2], const double b[2]) ATTR_WARN_UNUSED_RESULT; MINLINE void cross_v3_v3v3(float r[3], const float a[3], const float b[3]); MINLINE void cross_v3_v3v3_hi_prec(float r[3], const float a[3], const float b[3]); MINLINE void cross_v3_v3v3_db(double r[3], const double a[3], const double b[3]); @@ -221,6 +223,7 @@ MINLINE float len_manhattan_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT; MINLINE int len_manhattan_v2_int(const int v[2]) ATTR_WARN_UNUSED_RESULT; MINLINE float len_manhattan_v3(const float v[3]) ATTR_WARN_UNUSED_RESULT; MINLINE float len_v2(const float a[2]) ATTR_WARN_UNUSED_RESULT; +MINLINE double len_v2_db(const double v[2]) ATTR_WARN_UNUSED_RESULT; MINLINE float len_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT; MINLINE double len_v2v2_db(const double a[2], const double b[2]) ATTR_WARN_UNUSED_RESULT; MINLINE float len_v2v2_int(const int v1[2], const int v2[2]); diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c index ecdf108d00e..937bf8b1ae6 100644 --- a/source/blender/blenlib/intern/math_geom.c +++ b/source/blender/blenlib/intern/math_geom.c @@ -4176,8 +4176,8 @@ int interp_sparse_array(float *array, const int list_size, const float skipval) #define DIR_V2_SET(d_len, va, vb) \ { \ - sub_v2_v2v2((d_len)->dir, va, vb); \ - (d_len)->len = len_v2((d_len)->dir); \ + sub_v2db_v2fl_v2fl((d_len)->dir, va, vb); \ + (d_len)->len = len_v2_db((d_len)->dir); \ } \ (void)0 @@ -4185,8 +4185,8 @@ struct Float3_Len { float dir[3], len; }; -struct Float2_Len { - float dir[2], len; +struct Double2_Len { + double dir[2], len; }; /* Mean value weights - smooth interpolation weights for polygons with @@ -4209,21 +4209,30 @@ static float mean_value_half_tan_v3(const struct Float3_Len *d_curr, return 0.0f; } -static float mean_value_half_tan_v2(const struct Float2_Len *d_curr, - const struct Float2_Len *d_next) +/** + * Mean value weights - same as #mean_value_half_tan_v3 but for 2D vectors. + * + * \note When interpolating a 2D polygon, a point can be considered "outside" + * the polygon's bounds. Thus, when the point is very distant and the vectors + * have relatively close values, the precision problems are evident since they + * do not indicate a point "inside" the polygon. + * To resolve this, doubles are used. + */ +static double mean_value_half_tan_v2_db(const struct Double2_Len *d_curr, + const struct Double2_Len *d_next) { - /* different from the 3d version but still correct */ - const float area = cross_v2v2(d_curr->dir, d_next->dir); + /* Different from the 3d version but still correct. */ + const double area = cross_v2v2_db(d_curr->dir, d_next->dir); /* Compare against zero since 'FLT_EPSILON' can be too large, see: T73348. */ - if (LIKELY(area != 0.0f)) { - const float dot = dot_v2v2(d_curr->dir, d_next->dir); - const float len = d_curr->len * d_next->len; - const float result = (len - dot) / area; + if (LIKELY(area != 0.0)) { + const double dot = dot_v2v2_db(d_curr->dir, d_next->dir); + const double len = d_curr->len * d_next->len; + const double result = (len - dot) / area; if (isfinite(result)) { return result; } } - return 0.0f; + return 0.0; } void interp_weights_poly_v3(float *w, float v[][3], const int n, const float co[3]) @@ -4328,11 +4337,11 @@ void interp_weights_poly_v2(float *w, float v[][2], const int n, const float co[ const float eps_sq = eps * eps; const float *v_curr, *v_next; - float ht_prev, ht; /* half tangents */ + double ht_prev, ht; /* half tangents */ float totweight = 0.0f; int i_curr, i_next; char ix_flag = 0; - struct Float2_Len d_curr, d_next; + struct Double2_Len d_curr, d_next; /* loop over 'i_next' */ i_curr = n - 1; @@ -4343,7 +4352,7 @@ void interp_weights_poly_v2(float *w, float v[][2], const int n, const float co[ DIR_V2_SET(&d_curr, v_curr - 2 /* v[n - 2] */, co); DIR_V2_SET(&d_next, v_curr /* v[n - 1] */, co); - ht_prev = mean_value_half_tan_v2(&d_curr, &d_next); + ht_prev = mean_value_half_tan_v2_db(&d_curr, &d_next); while (i_next < n) { /* Mark Mayer et al algorithm that is used here does not operate well if vertex is close @@ -4362,8 +4371,8 @@ void interp_weights_poly_v2(float *w, float v[][2], const int n, const float co[ d_curr = d_next; DIR_V2_SET(&d_next, v_next, co); - ht = mean_value_half_tan_v2(&d_curr, &d_next); - w[i_curr] = (ht_prev + ht) / d_curr.len; + ht = mean_value_half_tan_v2_db(&d_curr, &d_next); + w[i_curr] = (float)((ht_prev + ht) / d_curr.len); totweight += w[i_curr]; /* step */ diff --git a/source/blender/blenlib/intern/math_vector_inline.c b/source/blender/blenlib/intern/math_vector_inline.c index ca405907bdd..1b47832589e 100644 --- a/source/blender/blenlib/intern/math_vector_inline.c +++ b/source/blender/blenlib/intern/math_vector_inline.c @@ -509,6 +509,12 @@ MINLINE void sub_v3_v3v3_db(double r[3], const double a[3], const double b[3]) r[2] = a[2] - b[2]; } +MINLINE void sub_v2db_v2fl_v2fl(double r[2], const float a[2], const float b[2]) +{ + r[0] = (double)a[0] - (double)b[0]; + r[1] = (double)a[1] - (double)b[1]; +} + MINLINE void sub_v3db_v3fl_v3fl(double r[3], const float a[3], const float b[3]) { r[0] = (double)a[0] - (double)b[0]; @@ -917,6 +923,11 @@ MINLINE float cross_v2v2(const float a[2], const float b[2]) return a[0] * b[1] - a[1] * b[0]; } +MINLINE double cross_v2v2_db(const double a[2], const double b[2]) +{ + return a[0] * b[1] - a[1] * b[0]; +} + MINLINE void cross_v3_v3v3(float r[3], const float a[3], const float b[3]) { BLI_assert(r != a && r != b); @@ -997,6 +1008,11 @@ MINLINE float len_v2(const float v[2]) return sqrtf(v[0] * v[0] + v[1] * v[1]); } +MINLINE double len_v2_db(const double v[2]) +{ + return sqrt(v[0] * v[0] + v[1] * v[1]); +} + MINLINE float len_v2v2(const float v1[2], const float v2[2]) { float x, y; diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h index ee478ad8567..70017d5560b 100644 --- a/source/blender/editors/transform/transform_convert.h +++ b/source/blender/editors/transform/transform_convert.h @@ -47,7 +47,7 @@ bool clipUVTransform(TransInfo *t, float vec[2], const bool resize); void clipUVData(TransInfo *t); /* transform_convert_mesh.c */ -void trans_mesh_customdata_correction_init(TransInfo *t); +void mesh_customdatacorrect_init(TransInfo *t); /* transform_convert_sequencer.c */ int transform_convert_sequencer_get_snap_bound(TransInfo *t); diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c index 31b9b771047..451903f1857 100644 --- a/source/blender/editors/transform/transform_convert_mesh.c +++ b/source/blender/editors/transform/transform_convert_mesh.c @@ -51,6 +51,8 @@ /* Own include. */ #include "transform_convert.h" +#define USE_FACE_SUBSTITUTE + /* -------------------------------------------------------------------- */ /** \name Island Creation * @@ -958,41 +960,44 @@ void createTransEditVerts(TransInfo *t) /** \} */ /* -------------------------------------------------------------------- */ -/** \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); @@ -1003,77 +1008,205 @@ 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 arealess faces in customdata 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, + BMVert *v, + const int index) { 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); + 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 (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; + } } + } - 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); + 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 { - loop_weights[j] = 0.0f; + merge_data->cd_loop_groups = NULL; } + + BLI_ghash_insert(tcld->merge_group.origverts, v, merge_data); } +} + +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; - /* 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); + bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); + tcld->cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS); +} + +static void mesh_customdatacorrect_init_container_merge_group(TransDataContainer *tc, + struct TransCustomDataLayer *tcld) +{ + BMesh *bm = tcld->bm; + BLI_assert(CustomData_has_math(&bm->ldata)); + + /* TODO: We don't need `layer_math_map` when there are no loops linked + * to one of the sliding vertices. */ + + /* 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; } } - 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); + 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)); } -static void trans_mesh_customdata_correction_init_container(TransDataContainer *tc) +static void mesh_customdatacorrect_init_container(TransDataContainer *tc, + const bool use_merge_group) { if (tc->custom.type.data) { - /* Custom data correction has initiated before. */ - BLI_assert(tc->custom.type.free_cb == trans_mesh_customdata_free_cb); - return; + /* The custom-data correction has been initiated before. + * Free since some modes have different settings. */ + mesh_customdatacorrect_free_cb(NULL, tc, &tc->custom.type); } BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); @@ -1082,135 +1215,81 @@ static void trans_mesh_customdata_correction_init_container(TransDataContainer * 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 customdata projection. */ + /* create copies of faces for custom-data projection. */ return; } - - const bool has_layer_math = CustomData_has_math(&bm->ldata); - const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS); - if (!has_layer_math && (cd_loop_mdisp_offset == -1)) { + if (!CustomData_has_math(&bm->ldata) && !CustomData_has_layer(&bm->ldata, CD_MDISPS)) { + /* There is no custom-data to correct. */ return; } - bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); + struct TransCustomDataLayer *tcld = MEM_callocN(sizeof(*tcld), __func__); + tcld->bm = bm; + tcld->arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); - struct GHash *origfaces = BLI_ghash_ptr_new(__func__); - struct BMesh *bm_origfaces = BM_mesh_create(&bm_mesh_allocsize_default, - &((struct BMeshCreateParams){ - .use_toolflags = false, - })); + /* 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; - /* we need to have matching customdata */ - BM_mesh_copy_init_customdata(bm_origfaces, bm, NULL); + mesh_customdatacorrect_init_container_generic(tc, tcld); - int *layer_math_map = NULL; - int layer_math_map_len = 0; - { - /* TODO: We don't need `sod->layer_math_map` when there are no loops linked - * to one of the sliding vertices. */ - if (has_layer_math) { - /* over alloc, only 'math' layers are indexed */ - layer_math_map = MEM_mallocN(bm->ldata.totlayer * sizeof(int), __func__); - for (int i = 0; i < bm->ldata.totlayer; i++) { - if (CustomData_layer_has_math(&bm->ldata, i)) { - layer_math_map[layer_math_map_len++] = i; - } - } - BLI_assert(layer_math_map_len != 0); - } + if (tcld->use_merge_group) { + mesh_customdatacorrect_init_container_merge_group(tc, tcld); } - struct TransCustomDataLayer *tcld = MEM_mallocN(sizeof(*tcld), __func__); - int data_len = tc->data_len + tc->data_mirror_len; - - 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_math_map_len; - tcld->arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); - tcld->origverts = BLI_ghash_ptr_new_ex(__func__, data_len); - tcld->data = BLI_memarena_alloc(tcld->arena, data_len * sizeof(*tcld->data)); - tcld->data_len = data_len; - { /* Setup Verts. */ - struct TransCustomDataLayerVert *tcld_vert_iter = &tcld->data[0]; + int i = 0; TransData *tob = tc->data; - for (int i = tc->data_len; i--; tob++, tcld_vert_iter++) { + for (int j = tc->data_len; j--; tob++, i++) { BMVert *v = tob->extra; - create_trans_vert_customdata_layer(v, tcld, tcld_vert_iter); + mesh_customdatacorrect_init_vert(tcld, v, i); } TransDataMirror *td_mirror = tc->data_mirror; - for (int i = tc->data_mirror_len; i--; td_mirror++, tcld_vert_iter++) { + for (int j = tc->data_mirror_len; j--; td_mirror++, i++) { BMVert *v = td_mirror->extra; - create_trans_vert_customdata_layer(v, tcld, tcld_vert_iter); + mesh_customdatacorrect_init_vert(tcld, v, i); } } tc->custom.type.data = tcld; - tc->custom.type.free_cb = trans_mesh_customdata_free_cb; + tc->custom.type.free_cb = mesh_customdatacorrect_free_cb; } -void trans_mesh_customdata_correction_init(TransInfo *t) +void mesh_customdatacorrect_init(TransInfo *t) { - if (!ELEM(t->mode, - TFM_TRANSLATION, - TFM_ROTATION, - TFM_RESIZE, - TFM_TOSPHERE, - TFM_SHEAR, - TFM_BEND, - TFM_SHRINKFATTEN, - TFM_TRACKBALL, - TFM_PUSHPULL, - TFM_ALIGN, - TFM_EDGE_SLIDE, - TFM_VERT_SLIDE)) { - /* Currently only modes that change the position of vertices are supported. */ - return; - } - - const char uvcalc_correct_flag = ELEM(t->mode, TFM_VERT_SLIDE, TFM_EDGE_SLIDE) ? - UVCALC_TRANSFORM_CORRECT_SLIDE : - UVCALC_TRANSFORM_CORRECT; - - if (t->settings->uvcalc_flag & uvcalc_correct_flag) { - FOREACH_TRANS_DATA_CONTAINER (t, tc) { - trans_mesh_customdata_correction_init_container(tc); + 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; } -} - -static void trans_mesh_customdata_correction_restore(struct TransDataContainer *tc) -{ - struct TransCustomDataLayer *tcld = tc->custom.type.data; - if (!tcld) { - return; + 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; + } } - 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); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + mesh_customdatacorrect_init_container(tc, use_merge_group); } } @@ -1219,23 +1298,24 @@ static void trans_mesh_customdata_correction_restore(struct TransDataContainer * */ 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 do_loop_mdisps) +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; + 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 = is_moved && tcld->layer_math_map_num; + 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]; @@ -1254,6 +1334,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); @@ -1305,17 +1393,20 @@ static void trans_mesh_customdata_correction_apply_vert(struct TransCustomDataLa } } - struct LinkNode **cd_loop_groups = tcld_vert->cd_loop_groups; - 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]); + } } } } @@ -1355,19 +1446,63 @@ static void trans_mesh_customdata_correction_apply_vert(struct TransCustomDataLa } } -static 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 do_loop_mdisps = is_final && (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 || do_loop_mdisps) { - trans_mesh_customdata_correction_apply_vert(tcld, tcld_vert_iter, do_loop_mdisps); + 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); } } } @@ -1428,16 +1563,14 @@ void recalcData_mesh(TransInfo *t) if ((t->flag & T_NO_MIRROR) == 0 && (t->options & CTX_NO_MIRROR) == 0) { transform_apply_to_mirror(t); } + + mesh_customdatacorrect_apply(t, false); + } + else { + mesh_customdatacorrect_restore(t); } FOREACH_TRANS_DATA_CONTAINER (t, tc) { - if (is_cancelling) { - trans_mesh_customdata_correction_restore(tc); - } - else { - trans_mesh_customdata_correction_apply(tc, false); - } - DEG_id_tag_update(tc->obedit->data, 0); /* sets recalc flags */ BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); EDBM_mesh_normals_update(em); @@ -1455,13 +1588,11 @@ void special_aftertrans_update__mesh(bContext *UNUSED(C), TransInfo *t) const bool is_cancelling = (t->state == TRANS_CANCEL); const bool use_automerge = !is_cancelling && (t->flag & (T_AUTOMERGE | T_AUTOSPLIT)) != 0; - if (!is_cancelling && TRANS_DATA_CONTAINER_FIRST_OK(t)->custom.type.data != NULL) { + 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. */ - FOREACH_TRANS_DATA_CONTAINER (t, tc) { - trans_mesh_customdata_correction_apply(tc, true); - } + mesh_customdatacorrect_apply(t, true); } if (use_automerge) { diff --git a/source/blender/editors/transform/transform_mode.c b/source/blender/editors/transform/transform_mode.c index 5988f5a0f83..38d49ab5efd 100644 --- a/source/blender/editors/transform/transform_mode.c +++ b/source/blender/editors/transform/transform_mode.c @@ -1272,7 +1272,7 @@ void transform_mode_init(TransInfo *t, wmOperator *op, const int mode) if (t->data_type == TC_MESH_VERTS) { /* Init Custom Data correction. * Ideally this should be called when creating the TransData. */ - trans_mesh_customdata_correction_init(t); + mesh_customdatacorrect_init(t); } /* TODO(germano): Some of these operations change the `t->mode`. diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index dd62ddb640f..a3f5a95f231 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -2241,6 +2241,8 @@ enum { #define UVCALC_USESUBSURF (1 << 3) /** adjust UV's while transforming to avoid distortion */ #define UVCALC_TRANSFORM_CORRECT (1 << 4) +/** Keep equal values merged while correcting custom-data. */ +#define UVCALC_TRANSFORM_CORRECT_KEEP_CONNECTED (1 << 5) /* ToolSettings.uv_flag */ #define UV_SYNC_SELECTION 1 diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index a6247474c69..66698d60423 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -3090,6 +3090,15 @@ static void rna_def_tool_settings(BlenderRNA *brna) "Correct data such as UV's and vertex colors when transforming"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + prop = RNA_def_property(srna, "use_transform_correct_keep_connected", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, NULL, "uvcalc_flag", UVCALC_TRANSFORM_CORRECT_KEEP_CONNECTED); + RNA_def_property_ui_text( + prop, + "Keep Connected", + "During the Face Attributes correction, merge attributes connected to the same vertex"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + prop = RNA_def_property(srna, "use_mesh_automerge", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "automerge", AUTO_MERGE); RNA_def_property_ui_text( |