Transform: Correct Face Attributes: Option to merge attributes

Keeping face attributes connected is now optional.

Keeping UV's connected is useful for organic modeling, but bad for
architectural.

Differential Revision: https://developer.blender.org/D8360
This commit is contained in:
Germano Cavalcante 2020-07-28 09:35:58 -03:00
parent 47b82fc02f
commit 6a689b5047
Notes: blender-bot 2023-11-07 23:31:57 +01:00
Referenced by issue #114594, Unwanted behaviour - Correct UVs causes merge by distance on uv shells vertices
6 changed files with 257 additions and 197 deletions

View File

@ -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)

View File

@ -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);

View File

@ -958,41 +958,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;
/* Special handle for multi-resolution. */
int cd_loop_mdisp_offset;
/* Array with all elements transformed. */
struct TransCustomDataLayerVert *data;
int data_len;
/* 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 +1006,129 @@ 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)
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;
}
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) {
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) {
/* 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;
}
}
/* 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);
}
BLI_ghash_insert(tcld->merge_group.origverts, v, merge_data);
}
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);
}
static void trans_mesh_customdata_correction_init_container(TransDataContainer *tc)
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;
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;
}
}
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 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 +1137,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;
}
if (!CustomData_has_math(&bm->ldata) && !CustomData_has_layer(&bm->ldata, CD_MDISPS)) {
/* There is no custom-data to correct. */
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)) {
return;
}
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_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);
}
}
struct TransCustomDataLayer *tcld = MEM_mallocN(sizeof(*tcld), __func__);
int data_len = tc->data_len + tc->data_mirror_len;
struct TransCustomDataLayer *tcld = MEM_callocN(sizeof(*tcld), __func__);
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;
/* 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;
mesh_customdatacorrect_init_container_generic(tc, tcld);
if (tcld->use_merge_group) {
mesh_customdatacorrect_init_container_merge_group(tc, tcld);
}
{
/* 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;
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;
}
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);
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;
}
}
}
static void trans_mesh_customdata_correction_restore(struct TransDataContainer *tc)
{
struct TransCustomDataLayer *tcld = tc->custom.type.data;
if (!tcld) {
return;
}
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 +1220,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];
@ -1305,17 +1307,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 +1360,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 +1477,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 +1502,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) {

View File

@ -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`.

View File

@ -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

View File

@ -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(