Fix T50084: Adding torus re-orders UV layers.

Issue was indeed in join operation, mesh in which we join all others
could be re-added to final data after others, leading to undesired
re-ordering of CD layers, and existing vertices etc. being shifted away
from their original indices, etc.

All kind of more or less bad and undesired changes, fixed by always
re-inserting destination mesh first.

Also cleaned up a bit that code, it was doing some rather
non-recommanded things (like allocating zero-sized mem, doing own
coocking to remove a data-block from main, etc.).
This commit is contained in:
2017-02-02 21:37:53 +01:00
parent 33e456b0ce
commit 9f67367f0a

View File

@@ -75,26 +75,213 @@
/* join selected meshes into the active mesh, context sensitive
* return 0 if no join is made (error) and 1 if the join is done */
static void join_mesh_single(
Main *bmain, Scene *scene,
Object *ob_dst, Base *base_src, float imat[4][4],
MVert **mvert_pp, MEdge **medge_pp, MLoop **mloop_pp, MPoly **mpoly_pp,
CustomData *vdata, CustomData *edata, CustomData *ldata, CustomData *pdata,
int totvert, int totedge, int totloop, int totpoly,
Key *key, Key *nkey,
Material **matar, int *matmap, int totcol,
int *vertofs, int *edgeofs, int *loopofs, int *polyofs)
{
int a, b;
Mesh *me = base_src->object->data;
MVert *mvert = *mvert_pp;
MEdge *medge = *medge_pp;
MLoop *mloop = *mloop_pp;
MPoly *mpoly = *mpoly_pp;
if (me->totvert) {
/* merge customdata flag */
((Mesh *)ob_dst->data)->cd_flag |= me->cd_flag;
/* standard data */
CustomData_merge(&me->vdata, vdata, CD_MASK_MESH, CD_DEFAULT, totvert);
CustomData_copy_data_named(&me->vdata, vdata, 0, *vertofs, me->totvert);
/* vertex groups */
MDeformVert *dvert = CustomData_get(vdata, *vertofs, CD_MDEFORMVERT);
/* NB: vertex groups here are new version */
if (dvert) {
for (a = 0; a < me->totvert; a++) {
for (b = 0; b < dvert[a].totweight; b++) {
/* Find the old vertex group */
bDeformGroup *dg, *odg = BLI_findlink(&base_src->object->defbase, dvert[a].dw[b].def_nr);
int index;
if (odg) {
/* Search for a match in the new object, and set new index */
for (dg = ob_dst->defbase.first, index = 0; dg; dg = dg->next, index++) {
if (STREQ(dg->name, odg->name)) {
dvert[a].dw[b].def_nr = index;
break;
}
}
}
}
}
}
/* if this is the object we're merging into, no need to do anything */
if (base_src->object != ob_dst) {
float cmat[4][4];
/* watch this: switch matmul order really goes wrong */
mul_m4_m4m4(cmat, imat, base_src->object->obmat);
/* transform vertex coordinates into new space */
for (a = 0, mvert = *mvert_pp; a < me->totvert; a++, mvert++) {
mul_m4_v3(cmat, mvert->co);
}
/* for each shapekey in destination mesh:
* - if there's a matching one, copy it across (will need to transform vertices into new space...)
* - otherwise, just copy own coordinates of mesh (no need to transform vertex coordinates into new space)
*/
if (key) {
/* if this mesh has any shapekeys, check first, otherwise just copy coordinates */
for (KeyBlock *kb = key->block.first; kb; kb = kb->next) {
/* get pointer to where to write data for this mesh in shapekey's data array */
float (*cos)[3] = ((float (*)[3])kb->data) + *vertofs;
/* check if this mesh has such a shapekey */
KeyBlock *okb = me->key ? BKE_keyblock_find_name(me->key, kb->name) : NULL;
if (okb) {
/* copy this mesh's shapekey to the destination shapekey (need to transform first) */
float (*ocos)[3] = okb->data;
for (a = 0; a < me->totvert; a++, cos++, ocos++) {
copy_v3_v3(*cos, *ocos);
mul_m4_v3(cmat, *cos);
}
}
else {
/* copy this mesh's vertex coordinates to the destination shapekey */
for (a = 0, mvert = *mvert_pp; a < me->totvert; a++, cos++, mvert++) {
copy_v3_v3(*cos, mvert->co);
}
}
}
}
}
else {
/* for each shapekey in destination mesh:
* - if it was an 'original', copy the appropriate data from nkey
* - otherwise, copy across plain coordinates (no need to transform coordinates)
*/
if (key) {
for (KeyBlock *kb = key->block.first; kb; kb = kb->next) {
/* get pointer to where to write data for this mesh in shapekey's data array */
float (*cos)[3] = ((float (*)[3])kb->data) + *vertofs;
/* check if this was one of the original shapekeys */
KeyBlock *okb = nkey ? BKE_keyblock_find_name(nkey, kb->name) : NULL;
if (okb) {
/* copy this mesh's shapekey to the destination shapekey */
float (*ocos)[3] = okb->data;
for (a = 0; a < me->totvert; a++, cos++, ocos++) {
copy_v3_v3(*cos, *ocos);
}
}
else {
/* copy base-coordinates to the destination shapekey */
for (a = 0, mvert = *mvert_pp; a < me->totvert; a++, cos++, mvert++) {
copy_v3_v3(*cos, mvert->co);
}
}
}
}
}
}
if (me->totedge) {
CustomData_merge(&me->edata, edata, CD_MASK_MESH, CD_DEFAULT, totedge);
CustomData_copy_data_named(&me->edata, edata, 0, *edgeofs, me->totedge);
for (a = 0; a < me->totedge; a++, medge++) {
medge->v1 += *vertofs;
medge->v2 += *vertofs;
}
}
if (me->totloop) {
if (base_src->object != ob_dst) {
MultiresModifierData *mmd;
multiresModifier_prepare_join(scene, base_src->object, ob_dst);
if ((mmd = get_multires_modifier(scene, base_src->object, true))) {
ED_object_iter_other(bmain, base_src->object, true,
ED_object_multires_update_totlevels_cb,
&mmd->totlvl);
}
}
CustomData_merge(&me->ldata, ldata, CD_MASK_MESH, CD_DEFAULT, totloop);
CustomData_copy_data_named(&me->ldata, ldata, 0, *loopofs, me->totloop);
for (a = 0; a < me->totloop; a++, mloop++) {
mloop->v += *vertofs;
mloop->e += *edgeofs;
}
}
if (me->totpoly) {
if (matmap) {
/* make mapping for materials */
for (a = 1; a <= base_src->object->totcol; a++) {
Material *ma = give_current_material(base_src->object, a);
for (b = 0; b < totcol; b++) {
if (ma == matar[b]) {
matmap[a - 1] = b;
break;
}
}
}
}
CustomData_merge(&me->pdata, pdata, CD_MASK_MESH, CD_DEFAULT, totpoly);
CustomData_copy_data_named(&me->pdata, pdata, 0, *polyofs, me->totpoly);
for (a = 0; a < me->totpoly; a++, mpoly++) {
mpoly->loopstart += *loopofs;
mpoly->mat_nr = matmap ? matmap[mpoly->mat_nr] : 0;
}
}
/* these are used for relinking (cannot be set earlier, or else reattaching goes wrong) */
*vertofs += me->totvert;
*mvert_pp += me->totvert;
*edgeofs += me->totedge;
*medge_pp += me->totedge;
*loopofs += me->totloop;
*mloop_pp += me->totloop;
*polyofs += me->totpoly;
*mpoly_pp += me->totpoly;
}
int join_mesh_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
Object *ob = CTX_data_active_object(C);
Material **matar, *ma;
Base *ob_base = CTX_data_active_base(C);
Object *ob = ob_base->object;
Material **matar = NULL, *ma;
Mesh *me;
MVert *mvert, *mv;
MVert *mvert = NULL;
MEdge *medge = NULL;
MPoly *mpoly = NULL;
MLoop *mloop = NULL;
Key *key, *nkey = NULL;
KeyBlock *kb, *okb, *kbn;
float imat[4][4], cmat[4][4], *fp1, *fp2;
KeyBlock *kb, *kbn;
float imat[4][4];
int a, b, totcol, totmat = 0, totedge = 0, totvert = 0;
int totloop = 0, totpoly = 0, vertofs, *matmap = NULL;
int i, j, index, haskey = 0, edgeofs, loopofs, polyofs;
int i, haskey = 0, edgeofs, loopofs, polyofs;
bool ok = false;
bDeformGroup *dg, *odg;
MDeformVert *dvert;
CustomData vdata, edata, fdata, ldata, pdata;
if (scene->obedit) {
@@ -154,8 +341,10 @@ int join_mesh_exec(bContext *C, wmOperator *op)
BKE_mesh_tessface_clear(me);
/* new material indices and material array */
matar = MEM_callocN(sizeof(void *) * totmat, "join_mesh matar");
if (totmat) matmap = MEM_callocN(sizeof(int) * totmat, "join_mesh matmap");
if (totmat) {
matar = MEM_callocN(sizeof(*matar) * totmat, "join_mesh matar");
matmap = MEM_callocN(sizeof(*matmap) * totmat, "join_mesh matmap");
}
totcol = ob->totcol;
/* obact materials in new main array, is nicer start! */
@@ -214,7 +403,9 @@ int join_mesh_exec(bContext *C, wmOperator *op)
ma = give_current_material(base->object, a);
for (b = 0; b < totcol; b++) {
if (ma == matar[b]) break;
if (ma == matar[b]) {
break;
}
}
if (b == totcol) {
matar[b] = ma;
@@ -223,8 +414,9 @@ int join_mesh_exec(bContext *C, wmOperator *op)
}
totcol++;
}
if (totcol >= MAXMAT)
if (totcol >= MAXMAT) {
break;
}
}
}
@@ -301,187 +493,41 @@ int join_mesh_exec(bContext *C, wmOperator *op)
/* inverse transform for all selected meshes in this object */
invert_m4_m4(imat, ob->obmat);
/* Add back active mesh first. This allows to keep things similar as they were, as much as possible (i.e. data from
* active mesh will remain first ones in new result of the merge, in same order for CD layers, etc. See also T50084.
*/
join_mesh_single(
bmain, scene,
ob, ob_base, imat,
&mvert, &medge, &mloop, &mpoly,
&vdata, &edata, &ldata, &pdata,
totvert, totedge, totloop, totpoly,
key, nkey,
matar, matmap, totcol,
&vertofs, &edgeofs, &loopofs, &polyofs);
CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases)
{
if (base->object == ob) {
continue;
}
/* only join if this is a mesh */
if (base->object->type == OB_MESH) {
me = base->object->data;
if (me->totvert) {
join_mesh_single(
bmain, scene,
ob, base, imat,
&mvert, &medge, &mloop, &mpoly,
&vdata, &edata, &ldata, &pdata,
totvert, totedge, totloop, totpoly,
key, nkey,
matar, matmap, totcol,
&vertofs, &edgeofs, &loopofs, &polyofs);
/* merge customdata flag */
((Mesh *)ob->data)->cd_flag |= me->cd_flag;
/* standard data */
CustomData_merge(&me->vdata, &vdata, CD_MASK_MESH, CD_DEFAULT, totvert);
CustomData_copy_data_named(&me->vdata, &vdata, 0, vertofs, me->totvert);
/* vertex groups */
dvert = CustomData_get(&vdata, vertofs, CD_MDEFORMVERT);
/* NB: vertex groups here are new version */
if (dvert) {
for (i = 0; i < me->totvert; i++) {
for (j = 0; j < dvert[i].totweight; j++) {
/* Find the old vertex group */
odg = BLI_findlink(&base->object->defbase, dvert[i].dw[j].def_nr);
if (odg) {
/* Search for a match in the new object, and set new index */
for (dg = ob->defbase.first, index = 0; dg; dg = dg->next, index++) {
if (STREQ(dg->name, odg->name)) {
dvert[i].dw[j].def_nr = index;
break;
}
}
}
}
}
}
/* if this is the object we're merging into, no need to do anything */
if (base->object != ob) {
/* watch this: switch matmul order really goes wrong */
mul_m4_m4m4(cmat, imat, base->object->obmat);
/* transform vertex coordinates into new space */
for (a = 0, mv = mvert; a < me->totvert; a++, mv++) {
mul_m4_v3(cmat, mv->co);
}
/* for each shapekey in destination mesh:
* - if there's a matching one, copy it across (will need to transform vertices into new space...)
* - otherwise, just copy own coordinates of mesh (no need to transform vertex coordinates into new space)
*/
if (key) {
/* if this mesh has any shapekeys, check first, otherwise just copy coordinates */
for (kb = key->block.first; kb; kb = kb->next) {
/* get pointer to where to write data for this mesh in shapekey's data array */
fp1 = ((float *)kb->data) + (vertofs * 3);
/* check if this mesh has such a shapekey */
okb = me->key ? BKE_keyblock_find_name(me->key, kb->name) : NULL;
if (okb) {
/* copy this mesh's shapekey to the destination shapekey (need to transform first) */
fp2 = ((float *)(okb->data));
for (a = 0; a < me->totvert; a++, fp1 += 3, fp2 += 3) {
copy_v3_v3(fp1, fp2);
mul_m4_v3(cmat, fp1);
}
}
else {
/* copy this mesh's vertex coordinates to the destination shapekey */
mv = mvert;
for (a = 0; a < me->totvert; a++, fp1 += 3, mv++) {
copy_v3_v3(fp1, mv->co);
}
}
}
}
}
else {
/* for each shapekey in destination mesh:
* - if it was an 'original', copy the appropriate data from nkey
* - otherwise, copy across plain coordinates (no need to transform coordinates)
*/
if (key) {
for (kb = key->block.first; kb; kb = kb->next) {
/* get pointer to where to write data for this mesh in shapekey's data array */
fp1 = ((float *)kb->data) + (vertofs * 3);
/* check if this was one of the original shapekeys */
okb = nkey ? BKE_keyblock_find_name(nkey, kb->name) : NULL;
if (okb) {
/* copy this mesh's shapekey to the destination shapekey */
fp2 = ((float *)(okb->data));
for (a = 0; a < me->totvert; a++, fp1 += 3, fp2 += 3) {
copy_v3_v3(fp1, fp2);
}
}
else {
/* copy base-coordinates to the destination shapekey */
mv = mvert;
for (a = 0; a < me->totvert; a++, fp1 += 3, mv++) {
copy_v3_v3(fp1, mv->co);
}
}
}
}
}
/* advance mvert pointer to end of base mesh's data */
mvert += me->totvert;
}
if (me->totedge) {
CustomData_merge(&me->edata, &edata, CD_MASK_MESH, CD_DEFAULT, totedge);
CustomData_copy_data_named(&me->edata, &edata, 0, edgeofs, me->totedge);
for (a = 0; a < me->totedge; a++, medge++) {
medge->v1 += vertofs;
medge->v2 += vertofs;
}
}
if (me->totloop) {
if (base->object != ob) {
MultiresModifierData *mmd;
multiresModifier_prepare_join(scene, base->object, ob);
if ((mmd = get_multires_modifier(scene, base->object, true))) {
ED_object_iter_other(bmain, base->object, true,
ED_object_multires_update_totlevels_cb,
&mmd->totlvl);
}
}
CustomData_merge(&me->ldata, &ldata, CD_MASK_MESH, CD_DEFAULT, totloop);
CustomData_copy_data_named(&me->ldata, &ldata, 0, loopofs, me->totloop);
for (a = 0; a < me->totloop; a++, mloop++) {
mloop->v += vertofs;
mloop->e += edgeofs;
}
}
if (me->totpoly) {
if (totmat) {
/* make mapping for materials */
for (a = 1; a <= base->object->totcol; a++) {
ma = give_current_material(base->object, a);
for (b = 0; b < totcol; b++) {
if (ma == matar[b]) {
matmap[a - 1] = b;
break;
}
}
}
}
CustomData_merge(&me->pdata, &pdata, CD_MASK_MESH, CD_DEFAULT, totpoly);
CustomData_copy_data_named(&me->pdata, &pdata, 0, polyofs, me->totpoly);
for (a = 0; a < me->totpoly; a++, mpoly++) {
mpoly->loopstart += loopofs;
mpoly->mat_nr = matmap ? matmap[(int)mpoly->mat_nr] : 0;
}
polyofs += me->totpoly;
}
/* these are used for relinking (cannot be set earlier,
* or else reattaching goes wrong)
*/
vertofs += me->totvert;
edgeofs += me->totedge;
loopofs += me->totloop;
/* free base, now that data is merged */
if (base->object != ob)
if (base->object != ob) {
ED_base_object_free_and_unlink(bmain, scene, base);
}
}
}
CTX_DATA_END;
@@ -529,34 +575,20 @@ int join_mesh_exec(bContext *C, wmOperator *op)
if (totcol) {
me->mat = matar;
ob->mat = MEM_callocN(sizeof(void *) * totcol, "join obmatar");
ob->matbits = MEM_callocN(sizeof(char) * totcol, "join obmatbits");
ob->mat = MEM_callocN(sizeof(*ob->mat) * totcol, "join obmatar");
ob->matbits = MEM_callocN(sizeof(*ob->matbits) * totcol, "join obmatbits");
MEM_freeN(matmap);
}
else
MEM_freeN(matar);
ob->totcol = me->totcol = totcol;
if (matmap) MEM_freeN(matmap);
/* other mesh users */
test_all_objects_materials(bmain, (ID *)me);
/* free temp copy of destination shapekeys (if applicable) */
if (nkey) {
/* XXX 2.5 Animato */
#if 0
/* free it's ipo too - both are not actually freed from memory yet as ID-blocks */
if (nkey->ipo) {
BKE_ipo_free(nkey->ipo);
BLI_remlink(&bmain->ipo, nkey->ipo);
MEM_freeN(nkey->ipo);
}
#endif
BKE_key_free(nkey);
BLI_remlink(&bmain->key, nkey);
MEM_freeN(nkey);
/* We can assume nobody is using that ID currently. */
BKE_libblock_free_ex(bmain, nkey, false, false);
}
/* ensure newly inserted keys are time sorted */
@@ -564,7 +596,7 @@ int join_mesh_exec(bContext *C, wmOperator *op)
BKE_key_sort(key);
}
DAG_relations_tag_update(bmain); // removed objects, need to rebuild dag
DAG_relations_tag_update(bmain); /* removed objects, need to rebuild dag */
DAG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA);