2011-02-23 10:52:22 +00:00
|
|
|
/*
|
2008-12-30 13:16:14 +00:00
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software Foundation,
|
2010-02-12 13:34:04 +00:00
|
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
2008-12-30 13:16:14 +00:00
|
|
|
*
|
|
|
|
* The Original Code is Copyright (C) 2004 by Blender Foundation
|
|
|
|
* All rights reserved.
|
|
|
|
*/
|
|
|
|
|
2019-02-18 08:08:12 +11:00
|
|
|
/** \file
|
|
|
|
* \ingroup edmesh
|
2012-12-19 04:49:32 +00:00
|
|
|
*
|
|
|
|
* meshtools.c: no editmode (violated already :), mirror & join),
|
|
|
|
* tools operating on meshes
|
2011-02-27 20:29:51 +00:00
|
|
|
*/
|
|
|
|
|
2008-12-30 13:16:14 +00:00
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
|
2009-07-13 00:40:20 +00:00
|
|
|
#include "DNA_key_types.h"
|
|
|
|
#include "DNA_material_types.h"
|
2018-06-21 19:05:10 +02:00
|
|
|
#include "DNA_mesh_types.h"
|
|
|
|
#include "DNA_meshdata_types.h"
|
2012-06-24 20:18:32 +00:00
|
|
|
#include "DNA_modifier_types.h"
|
2008-12-30 13:16:14 +00:00
|
|
|
#include "DNA_object_types.h"
|
|
|
|
#include "DNA_scene_types.h"
|
2012-12-23 02:04:38 +00:00
|
|
|
#include "DNA_screen_types.h"
|
|
|
|
#include "DNA_view3d_types.h"
|
2018-02-13 20:35:29 +11:00
|
|
|
#include "DNA_workspace_types.h"
|
2008-12-30 13:16:14 +00:00
|
|
|
|
|
|
|
#include "BLI_blenlib.h"
|
2020-03-19 09:33:03 +01:00
|
|
|
#include "BLI_math.h"
|
2008-12-30 13:16:14 +00:00
|
|
|
|
2009-07-13 00:40:20 +00:00
|
|
|
#include "BKE_context.h"
|
2010-08-22 14:15:28 +00:00
|
|
|
#include "BKE_deform.h"
|
2018-11-07 18:00:24 +01:00
|
|
|
#include "BKE_editmesh.h"
|
2009-07-13 00:40:20 +00:00
|
|
|
#include "BKE_key.h"
|
2018-11-07 18:00:24 +01:00
|
|
|
#include "BKE_layer.h"
|
2020-02-10 12:58:59 +01:00
|
|
|
#include "BKE_lib_id.h"
|
2008-12-30 13:16:14 +00:00
|
|
|
#include "BKE_main.h"
|
2018-11-07 18:00:24 +01:00
|
|
|
#include "BKE_material.h"
|
2008-12-30 13:16:14 +00:00
|
|
|
#include "BKE_mesh.h"
|
2018-06-21 18:38:37 +02:00
|
|
|
#include "BKE_mesh_iterators.h"
|
2018-06-05 16:58:08 +02:00
|
|
|
#include "BKE_mesh_runtime.h"
|
2018-11-07 18:00:24 +01:00
|
|
|
#include "BKE_multires.h"
|
2017-04-12 16:12:28 +02:00
|
|
|
#include "BKE_object.h"
|
2018-03-06 09:57:41 +11:00
|
|
|
#include "BKE_object_deform.h"
|
2019-09-20 12:05:29 +10:00
|
|
|
#include "BKE_object_facemap.h"
|
2009-02-14 21:31:34 +00:00
|
|
|
#include "BKE_report.h"
|
2008-12-30 13:16:14 +00:00
|
|
|
|
2017-06-08 10:14:53 +02:00
|
|
|
#include "DEG_depsgraph.h"
|
|
|
|
#include "DEG_depsgraph_build.h"
|
2018-12-01 14:46:55 +03:00
|
|
|
#include "DEG_depsgraph_query.h"
|
2017-06-08 10:14:53 +02:00
|
|
|
|
2019-08-07 12:43:04 -03:00
|
|
|
#include "DRW_select_buffer.h"
|
|
|
|
|
2008-12-30 13:16:14 +00:00
|
|
|
#include "ED_mesh.h"
|
|
|
|
#include "ED_object.h"
|
2009-01-05 15:19:31 +00:00
|
|
|
#include "ED_view3d.h"
|
2008-12-30 13:16:14 +00:00
|
|
|
|
2009-07-13 00:40:20 +00:00
|
|
|
#include "WM_api.h"
|
|
|
|
#include "WM_types.h"
|
|
|
|
|
2008-12-30 13:16:14 +00:00
|
|
|
/* * ********************** no editmode!!! *********** */
|
|
|
|
|
2009-07-13 00:40:20 +00:00
|
|
|
/*********************** JOIN ***************************/
|
|
|
|
|
2008-12-30 13:16:14 +00:00
|
|
|
/* join selected meshes into the active mesh, context sensitive
|
2012-03-03 16:31:46 +00:00
|
|
|
* return 0 if no join is made (error) and 1 if the join is done */
|
2009-07-11 10:20:48 +00:00
|
|
|
|
2017-02-02 21:37:53 +01:00
|
|
|
static void join_mesh_single(Depsgraph *depsgraph,
|
2018-04-06 12:07:27 +02:00
|
|
|
Main *bmain,
|
|
|
|
Scene *scene,
|
2017-10-04 14:59:44 +05:00
|
|
|
Object *ob_dst,
|
|
|
|
Object *ob_src,
|
2019-09-14 08:10:50 +10:00
|
|
|
const float imat[4][4],
|
2017-02-02 21:37:53 +01:00
|
|
|
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;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-10-04 14:59:44 +05:00
|
|
|
Mesh *me = ob_src->data;
|
2017-02-02 21:37:53 +01:00
|
|
|
MVert *mvert = *mvert_pp;
|
|
|
|
MEdge *medge = *medge_pp;
|
|
|
|
MLoop *mloop = *mloop_pp;
|
|
|
|
MPoly *mpoly = *mpoly_pp;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-02-02 21:37:53 +01:00
|
|
|
if (me->totvert) {
|
|
|
|
/* merge customdata flag */
|
|
|
|
((Mesh *)ob_dst->data)->cd_flag |= me->cd_flag;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-02-02 21:37:53 +01:00
|
|
|
/* standard data */
|
Refactor CDData masks, to have one mask per mesh elem type.
We already have different storages for cddata of verts, edges etc.,
'simply' do the same for the mask flags we use all around Blender code
to request some data, or limit some operation to some layers, etc.
Reason we need this is that some cddata types (like Normals) are
actually shared between verts/polys/loops, and we don’t want to generate
clnors everytime we request vnors!
As a side note, this also does final fix to T59338, which was the
trigger for this patch (need to request computed loop normals for
another mesh than evaluated one).
Reviewers: brecht, campbellbarton, sergey
Differential Revision: https://developer.blender.org/D4407
2019-03-07 11:13:40 +01:00
|
|
|
CustomData_merge(&me->vdata, vdata, CD_MASK_MESH.vmask, CD_DEFAULT, totvert);
|
2017-02-02 21:37:53 +01:00
|
|
|
CustomData_copy_data_named(&me->vdata, vdata, 0, *vertofs, me->totvert);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-02-02 21:37:53 +01:00
|
|
|
/* vertex groups */
|
|
|
|
MDeformVert *dvert = CustomData_get(vdata, *vertofs, CD_MDEFORMVERT);
|
2017-05-22 16:17:37 +02:00
|
|
|
MDeformVert *dvert_src = CustomData_get(&me->vdata, 0, CD_MDEFORMVERT);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-05-22 16:17:37 +02:00
|
|
|
/* Remap to correct new vgroup indices, if needed. */
|
|
|
|
if (dvert_src) {
|
|
|
|
BLI_assert(dvert != NULL);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-05-22 16:17:37 +02:00
|
|
|
/* Build src to merged mapping of vgroup indices. */
|
2018-03-06 09:57:41 +11:00
|
|
|
int *vgroup_index_map;
|
|
|
|
int vgroup_index_map_len;
|
|
|
|
vgroup_index_map = BKE_object_defgroup_index_map_create(
|
|
|
|
ob_src, ob_dst, &vgroup_index_map_len);
|
|
|
|
BKE_object_defgroup_index_map_apply(
|
|
|
|
dvert, me->totvert, vgroup_index_map, vgroup_index_map_len);
|
|
|
|
if (vgroup_index_map != NULL) {
|
|
|
|
MEM_freeN(vgroup_index_map);
|
2017-02-02 21:37:53 +01:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
2017-02-02 21:37:53 +01:00
|
|
|
/* if this is the object we're merging into, no need to do anything */
|
2017-10-04 14:59:44 +05:00
|
|
|
if (ob_src != ob_dst) {
|
2017-02-02 21:37:53 +01:00
|
|
|
float cmat[4][4];
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-02-05 16:23:34 +11:00
|
|
|
/* Watch this: switch matrix multiplication order really goes wrong. */
|
2017-10-04 14:59:44 +05:00
|
|
|
mul_m4_m4m4(cmat, imat, ob_src->obmat);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-02-02 21:37:53 +01:00
|
|
|
/* transform vertex coordinates into new space */
|
2021-06-13 14:47:22 +10:00
|
|
|
for (a = 0; a < me->totvert; a++, mvert++) {
|
2017-02-02 21:37:53 +01:00
|
|
|
mul_m4_v3(cmat, mvert->co);
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-04-22 00:18:34 +10:00
|
|
|
/* 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).
|
2017-02-02 21:37:53 +01:00
|
|
|
*/
|
|
|
|
if (key) {
|
|
|
|
/* if this mesh has any shapekeys, check first, otherwise just copy coordinates */
|
2020-04-03 19:15:01 +02:00
|
|
|
LISTBASE_FOREACH (KeyBlock *, kb, &key->block) {
|
2017-02-02 21:37:53 +01:00
|
|
|
/* get pointer to where to write data for this mesh in shapekey's data array */
|
|
|
|
float(*cos)[3] = ((float(*)[3])kb->data) + *vertofs;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-02-02 21:37:53 +01:00
|
|
|
/* check if this mesh has such a shapekey */
|
|
|
|
KeyBlock *okb = me->key ? BKE_keyblock_find_name(me->key, kb->name) : NULL;
|
|
|
|
if (okb) {
|
2019-01-15 23:24:20 +11:00
|
|
|
/* copy this mesh's shapekey to the destination shapekey
|
|
|
|
* (need to transform first) */
|
2017-02-02 21:37:53 +01:00
|
|
|
float(*ocos)[3] = okb->data;
|
|
|
|
for (a = 0; a < me->totvert; a++, cos++, ocos++) {
|
|
|
|
copy_v3_v3(*cos, *ocos);
|
|
|
|
mul_m4_v3(cmat, *cos);
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2017-02-02 21:37:53 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
}
|
2017-02-02 21:37:53 +01:00
|
|
|
else {
|
|
|
|
/* for each shapekey in destination mesh:
|
2018-11-14 12:53:15 +11:00
|
|
|
* - if it was an 'original', copy the appropriate data from nkey
|
|
|
|
* - otherwise, copy across plain coordinates (no need to transform coordinates)
|
2017-02-02 21:37:53 +01:00
|
|
|
*/
|
|
|
|
if (key) {
|
2020-04-03 19:15:01 +02:00
|
|
|
LISTBASE_FOREACH (KeyBlock *, kb, &key->block) {
|
2017-02-02 21:37:53 +01:00
|
|
|
/* get pointer to where to write data for this mesh in shapekey's data array */
|
|
|
|
float(*cos)[3] = ((float(*)[3])kb->data) + *vertofs;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-02-02 21:37:53 +01:00
|
|
|
/* 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);
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2017-02-02 21:37:53 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
2017-02-02 21:37:53 +01:00
|
|
|
if (me->totedge) {
|
Refactor CDData masks, to have one mask per mesh elem type.
We already have different storages for cddata of verts, edges etc.,
'simply' do the same for the mask flags we use all around Blender code
to request some data, or limit some operation to some layers, etc.
Reason we need this is that some cddata types (like Normals) are
actually shared between verts/polys/loops, and we don’t want to generate
clnors everytime we request vnors!
As a side note, this also does final fix to T59338, which was the
trigger for this patch (need to request computed loop normals for
another mesh than evaluated one).
Reviewers: brecht, campbellbarton, sergey
Differential Revision: https://developer.blender.org/D4407
2019-03-07 11:13:40 +01:00
|
|
|
CustomData_merge(&me->edata, edata, CD_MASK_MESH.emask, CD_DEFAULT, totedge);
|
2017-02-02 21:37:53 +01:00
|
|
|
CustomData_copy_data_named(&me->edata, edata, 0, *edgeofs, me->totedge);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-02-02 21:37:53 +01:00
|
|
|
for (a = 0; a < me->totedge; a++, medge++) {
|
|
|
|
medge->v1 += *vertofs;
|
|
|
|
medge->v2 += *vertofs;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
2017-02-02 21:37:53 +01:00
|
|
|
if (me->totloop) {
|
2017-10-04 14:59:44 +05:00
|
|
|
if (ob_src != ob_dst) {
|
2017-02-02 21:37:53 +01:00
|
|
|
MultiresModifierData *mmd;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-04-06 12:07:27 +02:00
|
|
|
multiresModifier_prepare_join(depsgraph, scene, ob_src, ob_dst);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-10-04 14:59:44 +05:00
|
|
|
if ((mmd = get_multires_modifier(scene, ob_src, true))) {
|
2018-04-05 18:20:27 +02:00
|
|
|
ED_object_iter_other(
|
|
|
|
bmain, ob_src, true, ED_object_multires_update_totlevels_cb, &mmd->totlvl);
|
2017-02-02 21:37:53 +01:00
|
|
|
}
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
Refactor CDData masks, to have one mask per mesh elem type.
We already have different storages for cddata of verts, edges etc.,
'simply' do the same for the mask flags we use all around Blender code
to request some data, or limit some operation to some layers, etc.
Reason we need this is that some cddata types (like Normals) are
actually shared between verts/polys/loops, and we don’t want to generate
clnors everytime we request vnors!
As a side note, this also does final fix to T59338, which was the
trigger for this patch (need to request computed loop normals for
another mesh than evaluated one).
Reviewers: brecht, campbellbarton, sergey
Differential Revision: https://developer.blender.org/D4407
2019-03-07 11:13:40 +01:00
|
|
|
CustomData_merge(&me->ldata, ldata, CD_MASK_MESH.lmask, CD_DEFAULT, totloop);
|
2017-02-02 21:37:53 +01:00
|
|
|
CustomData_copy_data_named(&me->ldata, ldata, 0, *loopofs, me->totloop);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-02-02 21:37:53 +01:00
|
|
|
for (a = 0; a < me->totloop; a++, mloop++) {
|
|
|
|
mloop->v += *vertofs;
|
|
|
|
mloop->e += *edgeofs;
|
|
|
|
}
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-02-02 21:37:53 +01:00
|
|
|
if (me->totpoly) {
|
|
|
|
if (matmap) {
|
|
|
|
/* make mapping for materials */
|
2017-10-04 14:59:44 +05:00
|
|
|
for (a = 1; a <= ob_src->totcol; a++) {
|
2020-02-05 11:23:58 +01:00
|
|
|
Material *ma = BKE_object_material_get(ob_src, a);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-02-02 21:37:53 +01:00
|
|
|
for (b = 0; b < totcol; b++) {
|
|
|
|
if (ma == matar[b]) {
|
|
|
|
matmap[a - 1] = b;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
Refactor CDData masks, to have one mask per mesh elem type.
We already have different storages for cddata of verts, edges etc.,
'simply' do the same for the mask flags we use all around Blender code
to request some data, or limit some operation to some layers, etc.
Reason we need this is that some cddata types (like Normals) are
actually shared between verts/polys/loops, and we don’t want to generate
clnors everytime we request vnors!
As a side note, this also does final fix to T59338, which was the
trigger for this patch (need to request computed loop normals for
another mesh than evaluated one).
Reviewers: brecht, campbellbarton, sergey
Differential Revision: https://developer.blender.org/D4407
2019-03-07 11:13:40 +01:00
|
|
|
CustomData_merge(&me->pdata, pdata, CD_MASK_MESH.pmask, CD_DEFAULT, totpoly);
|
2017-02-02 21:37:53 +01:00
|
|
|
CustomData_copy_data_named(&me->pdata, pdata, 0, *polyofs, me->totpoly);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-02-02 21:37:53 +01:00
|
|
|
for (a = 0; a < me->totpoly; a++, mpoly++) {
|
|
|
|
mpoly->loopstart += *loopofs;
|
|
|
|
mpoly->mat_nr = matmap ? matmap[mpoly->mat_nr] : 0;
|
|
|
|
}
|
2019-09-20 12:05:29 +10:00
|
|
|
|
|
|
|
/* Face maps. */
|
|
|
|
int *fmap = CustomData_get(pdata, *polyofs, CD_FACEMAP);
|
|
|
|
int *fmap_src = CustomData_get(&me->pdata, 0, CD_FACEMAP);
|
|
|
|
|
|
|
|
/* Remap to correct new face-map indices, if needed. */
|
|
|
|
if (fmap_src) {
|
|
|
|
BLI_assert(fmap != NULL);
|
|
|
|
int *fmap_index_map;
|
|
|
|
int fmap_index_map_len;
|
|
|
|
fmap_index_map = BKE_object_facemap_index_map_create(ob_src, ob_dst, &fmap_index_map_len);
|
|
|
|
BKE_object_facemap_index_map_apply(fmap, me->totpoly, fmap_index_map, fmap_index_map_len);
|
|
|
|
if (fmap_index_map != NULL) {
|
|
|
|
MEM_freeN(fmap_index_map);
|
|
|
|
}
|
|
|
|
}
|
2017-02-02 21:37:53 +01:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-02-02 21:37:53 +01:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2020-07-09 17:56:00 +02:00
|
|
|
/* Face Sets IDs are a sparse sequence, so this function offsets all the IDs by face_set_offset and
|
|
|
|
* updates face_set_offset with the maximum ID value. This way, when used in multiple meshes, all
|
|
|
|
* of them will have different IDs for their Face Sets. */
|
|
|
|
static void mesh_join_offset_face_sets_ID(const Mesh *mesh, int *face_set_offset)
|
|
|
|
{
|
|
|
|
if (!mesh->totpoly) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS);
|
|
|
|
if (!face_sets) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int max_face_set = 0;
|
|
|
|
for (int f = 0; f < mesh->totpoly; f++) {
|
2020-07-10 11:41:14 +10:00
|
|
|
/* As face sets encode the visibility in the integer sign, the offset needs to be added or
|
|
|
|
* subtracted depending on the initial sign of the integer to get the new ID. */
|
2020-07-09 17:56:00 +02:00
|
|
|
if (abs(face_sets[f]) <= *face_set_offset) {
|
|
|
|
if (face_sets[f] > 0) {
|
|
|
|
face_sets[f] += *face_set_offset;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
face_sets[f] -= *face_set_offset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
max_face_set = max_ii(max_face_set, abs(face_sets[f]));
|
|
|
|
}
|
|
|
|
*face_set_offset = max_face_set;
|
|
|
|
}
|
|
|
|
|
2020-06-17 17:07:11 +10:00
|
|
|
int ED_mesh_join_objects_exec(bContext *C, wmOperator *op)
|
2008-12-30 13:16:14 +00:00
|
|
|
{
|
2012-03-26 02:56:48 +00:00
|
|
|
Main *bmain = CTX_data_main(C);
|
|
|
|
Scene *scene = CTX_data_scene(C);
|
2017-10-04 14:59:44 +05:00
|
|
|
Object *ob = CTX_data_active_object(C);
|
2017-02-02 21:37:53 +01:00
|
|
|
Material **matar = NULL, *ma;
|
2008-12-30 13:16:14 +00:00
|
|
|
Mesh *me;
|
2017-02-02 21:37:53 +01:00
|
|
|
MVert *mvert = NULL;
|
2011-01-13 04:53:55 +00:00
|
|
|
MEdge *medge = NULL;
|
2011-02-27 06:19:40 +00:00
|
|
|
MPoly *mpoly = NULL;
|
|
|
|
MLoop *mloop = NULL;
|
2012-03-26 02:56:48 +00:00
|
|
|
Key *key, *nkey = NULL;
|
2017-02-02 21:37:53 +01:00
|
|
|
KeyBlock *kb, *kbn;
|
|
|
|
float imat[4][4];
|
2013-07-10 09:55:10 +00:00
|
|
|
int a, b, totcol, totmat = 0, totedge = 0, totvert = 0;
|
2012-03-26 02:56:48 +00:00
|
|
|
int totloop = 0, totpoly = 0, vertofs, *matmap = NULL;
|
2017-02-02 21:37:53 +01:00
|
|
|
int i, haskey = 0, edgeofs, loopofs, polyofs;
|
2020-05-13 15:21:30 -04:00
|
|
|
bool ok = false, join_parent = false;
|
2008-12-30 13:16:14 +00:00
|
|
|
bDeformGroup *dg, *odg;
|
2009-07-16 06:27:37 +00:00
|
|
|
CustomData vdata, edata, fdata, ldata, pdata;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-04-05 18:20:27 +02:00
|
|
|
if (ob->mode & OB_MODE_EDIT) {
|
2012-10-26 17:32:50 +00:00
|
|
|
BKE_report(op->reports, RPT_WARNING, "Cannot join while in edit mode");
|
2009-07-13 00:40:20 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
2010-11-10 01:40:24 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2009-07-13 00:40:20 +00:00
|
|
|
/* ob is the object we are adding geometry to */
|
2012-03-26 02:56:48 +00:00
|
|
|
if (!ob || ob->type != OB_MESH) {
|
2010-12-21 15:10:09 +00:00
|
|
|
BKE_report(op->reports, RPT_WARNING, "Active object is not a mesh");
|
2009-07-13 00:40:20 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
2010-11-10 01:40:24 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-07-25 16:36:22 +02:00
|
|
|
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2008-12-30 13:16:14 +00:00
|
|
|
/* count & check */
|
2018-10-01 16:43:49 +10:00
|
|
|
CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) {
|
|
|
|
if (ob_iter->type == OB_MESH) {
|
|
|
|
me = ob_iter->data;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-26 02:56:48 +00:00
|
|
|
totvert += me->totvert;
|
|
|
|
totedge += me->totedge;
|
|
|
|
totloop += me->totloop;
|
|
|
|
totpoly += me->totpoly;
|
2018-10-01 16:43:49 +10:00
|
|
|
totmat += ob_iter->totcol;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-04-22 09:19:45 +10:00
|
|
|
if (ob_iter == ob) {
|
2013-07-10 09:55:10 +00:00
|
|
|
ok = true;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-05-13 15:21:30 -04:00
|
|
|
if ((ob->parent != NULL) && (ob_iter == ob->parent)) {
|
|
|
|
join_parent = true;
|
|
|
|
}
|
|
|
|
|
2009-07-13 00:40:20 +00:00
|
|
|
/* check for shapekeys */
|
2019-04-22 09:19:45 +10:00
|
|
|
if (me->key) {
|
2009-07-13 00:40:20 +00:00
|
|
|
haskey++;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2008-12-30 13:16:14 +00:00
|
|
|
}
|
2009-07-13 00:40:20 +00:00
|
|
|
CTX_DATA_END;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-05-13 15:21:30 -04:00
|
|
|
/* Apply parent transform if the active object's parent was joined to it.
|
|
|
|
* Note: This doesn't apply recursive parenting. */
|
|
|
|
if (join_parent) {
|
|
|
|
ob->parent = NULL;
|
|
|
|
BKE_object_apply_mat4_ex(ob, ob->obmat, ob->parent, ob->parentinv, false);
|
|
|
|
}
|
|
|
|
|
2018-06-04 09:31:30 +02:00
|
|
|
/* that way the active object is always selected */
|
2013-07-10 09:55:10 +00:00
|
|
|
if (ok == false) {
|
2010-12-21 15:10:09 +00:00
|
|
|
BKE_report(op->reports, RPT_WARNING, "Active object is not a selected mesh");
|
2009-07-13 00:40:20 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
2010-11-10 01:40:24 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-04-22 00:18:34 +10:00
|
|
|
/* Only join meshes if there are verts to join,
|
|
|
|
* there aren't too many, and we only had one mesh selected. */
|
2012-03-26 02:56:48 +00:00
|
|
|
me = (Mesh *)ob->data;
|
|
|
|
key = me->key;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-11-06 12:30:59 +11:00
|
|
|
if (ELEM(totvert, 0, me->totvert)) {
|
2010-12-21 15:10:09 +00:00
|
|
|
BKE_report(op->reports, RPT_WARNING, "No mesh data to join");
|
2009-07-13 00:40:20 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
2010-11-10 01:40:24 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-24 06:38:07 +00:00
|
|
|
if (totvert > MESH_MAX_VERTS) {
|
2012-10-18 16:25:58 +00:00
|
|
|
BKE_reportf(op->reports,
|
|
|
|
RPT_WARNING,
|
|
|
|
"Joining results in %d vertices, limit is %ld",
|
|
|
|
totvert,
|
|
|
|
MESH_MAX_VERTS);
|
|
|
|
return OPERATOR_CANCELLED;
|
2010-11-10 01:40:24 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-02-27 05:38:48 +00:00
|
|
|
/* remove tessface to ensure we don't hold references to invalid faces */
|
2012-10-25 08:48:05 +00:00
|
|
|
BKE_mesh_tessface_clear(me);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2008-12-30 13:16:14 +00:00
|
|
|
/* new material indices and material array */
|
2017-02-02 21:37:53 +01:00
|
|
|
if (totmat) {
|
|
|
|
matar = MEM_callocN(sizeof(*matar) * totmat, "join_mesh matar");
|
|
|
|
matmap = MEM_callocN(sizeof(*matmap) * totmat, "join_mesh matmap");
|
|
|
|
}
|
2012-03-26 02:56:48 +00:00
|
|
|
totcol = ob->totcol;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2008-12-30 13:16:14 +00:00
|
|
|
/* obact materials in new main array, is nicer start! */
|
2012-03-26 02:56:48 +00:00
|
|
|
for (a = 0; a < ob->totcol; a++) {
|
2020-02-05 11:23:58 +01:00
|
|
|
matar[a] = BKE_object_material_get(ob, a + 1);
|
2009-07-13 00:40:20 +00:00
|
|
|
id_us_plus((ID *)matar[a]);
|
2008-12-30 13:16:14 +00:00
|
|
|
/* increase id->us : will be lowered later */
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2009-07-13 00:40:20 +00:00
|
|
|
/* - if destination mesh had shapekeys, move them somewhere safe, and set up placeholders
|
2018-09-02 18:28:27 +10:00
|
|
|
* with arrays that are large enough to hold shapekey data for all meshes
|
|
|
|
* - if destination mesh didn't have shapekeys, but we encountered some in the meshes we're
|
|
|
|
* joining, set up a new keyblock and assign to the mesh
|
2009-07-13 00:40:20 +00:00
|
|
|
*/
|
2012-03-24 06:38:07 +00:00
|
|
|
if (key) {
|
2009-07-13 00:40:20 +00:00
|
|
|
/* make a duplicate copy that will only be used here... (must remember to free it!) */
|
2020-10-07 18:01:25 +02:00
|
|
|
nkey = (Key *)BKE_id_copy(bmain, &key->id);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2009-07-13 00:40:20 +00:00
|
|
|
/* for all keys in old block, clear data-arrays */
|
2012-03-26 02:56:48 +00:00
|
|
|
for (kb = key->block.first; kb; kb = kb->next) {
|
2019-04-22 09:19:45 +10:00
|
|
|
if (kb->data) {
|
2012-03-24 06:38:07 +00:00
|
|
|
MEM_freeN(kb->data);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2020-08-08 13:29:21 +10:00
|
|
|
kb->data = MEM_callocN(sizeof(float[3]) * totvert, "join_shapekey");
|
2012-03-26 02:56:48 +00:00
|
|
|
kb->totelem = totvert;
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2009-07-13 00:40:20 +00:00
|
|
|
}
|
2012-03-24 06:38:07 +00:00
|
|
|
else if (haskey) {
|
2009-07-13 00:40:20 +00:00
|
|
|
/* add a new key-block and add to the mesh */
|
2018-06-12 12:53:27 +02:00
|
|
|
key = me->key = BKE_key_add(bmain, (ID *)me);
|
2009-07-13 00:40:20 +00:00
|
|
|
key->type = KEY_RELATIVE;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-07-09 17:56:00 +02:00
|
|
|
/* Update face_set_id_offset with the face set data in the active object first. This way the Face
|
|
|
|
* Sets IDs in the active object are not the ones that are modified. */
|
|
|
|
Mesh *mesh_active = BKE_mesh_from_object(ob);
|
|
|
|
int face_set_id_offset = 0;
|
|
|
|
mesh_join_offset_face_sets_ID(mesh_active, &face_set_id_offset);
|
|
|
|
|
|
|
|
/* Copy materials, vertex-groups, face sets & face-maps across objects. */
|
2018-10-01 16:43:49 +10:00
|
|
|
CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) {
|
2009-07-13 00:40:20 +00:00
|
|
|
/* only act if a mesh, and not the one we're joining to */
|
2018-10-01 16:43:49 +10:00
|
|
|
if ((ob != ob_iter) && (ob_iter->type == OB_MESH)) {
|
|
|
|
me = ob_iter->data;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2009-07-13 00:40:20 +00:00
|
|
|
/* Join this object's vertex groups to the base one's */
|
2018-10-01 16:43:49 +10:00
|
|
|
for (dg = ob_iter->defbase.first; dg; dg = dg->next) {
|
2009-07-13 00:40:20 +00:00
|
|
|
/* See if this group exists in the object (if it doesn't, add it to the end) */
|
2020-03-06 12:50:56 +11:00
|
|
|
if (!BKE_object_defgroup_find_name(ob, dg->name)) {
|
2020-03-05 07:54:52 +11:00
|
|
|
odg = MEM_mallocN(sizeof(bDeformGroup), "join deformGroup");
|
2009-07-13 00:40:20 +00:00
|
|
|
memcpy(odg, dg, sizeof(bDeformGroup));
|
|
|
|
BLI_addtail(&ob->defbase, odg);
|
|
|
|
}
|
|
|
|
}
|
2019-04-22 09:19:45 +10:00
|
|
|
if (ob->defbase.first && ob->actdef == 0) {
|
2012-03-26 02:56:48 +00:00
|
|
|
ob->actdef = 1;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-09-20 12:05:29 +10:00
|
|
|
/* Join this object's face maps to the base one's. */
|
2020-04-03 19:15:01 +02:00
|
|
|
LISTBASE_FOREACH (bFaceMap *, fmap, &ob_iter->fmaps) {
|
2019-09-20 12:05:29 +10:00
|
|
|
/* See if this group exists in the object (if it doesn't, add it to the end) */
|
|
|
|
if (BKE_object_facemap_find_name(ob, fmap->name) == NULL) {
|
2020-03-05 07:54:52 +11:00
|
|
|
bFaceMap *fmap_new = MEM_mallocN(sizeof(bFaceMap), "join faceMap");
|
2019-09-20 12:05:29 +10:00
|
|
|
memcpy(fmap_new, fmap, sizeof(bFaceMap));
|
|
|
|
BLI_addtail(&ob->fmaps, fmap_new);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ob->fmaps.first && ob->actfmap == 0) {
|
|
|
|
ob->actfmap = 1;
|
|
|
|
}
|
|
|
|
|
2020-07-09 17:56:00 +02:00
|
|
|
mesh_join_offset_face_sets_ID(me, &face_set_id_offset);
|
|
|
|
|
2012-03-26 02:56:48 +00:00
|
|
|
if (me->totvert) {
|
2012-03-24 06:38:07 +00:00
|
|
|
/* Add this object's materials to the base one's if they don't exist already
|
2019-01-15 23:24:20 +11:00
|
|
|
* (but only if limits not exceeded yet) */
|
2012-03-24 06:38:07 +00:00
|
|
|
if (totcol < MAXMAT) {
|
2019-01-15 23:24:20 +11:00
|
|
|
for (a = 1; a <= ob_iter->totcol; a++) {
|
2020-02-05 11:23:58 +01:00
|
|
|
ma = BKE_object_material_get(ob_iter, a);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-10-01 16:43:49 +10:00
|
|
|
for (b = 0; b < totcol; b++) {
|
|
|
|
if (ma == matar[b]) {
|
|
|
|
break;
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
}
|
2012-03-26 02:56:48 +00:00
|
|
|
if (b == totcol) {
|
2017-02-02 21:37:53 +01:00
|
|
|
matar[b] = ma;
|
|
|
|
if (ma) {
|
2011-04-26 07:17:21 +00:00
|
|
|
id_us_plus(&ma->id);
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2017-02-02 21:37:53 +01:00
|
|
|
totcol++;
|
|
|
|
}
|
2012-03-26 02:56:48 +00:00
|
|
|
if (totcol >= MAXMAT) {
|
|
|
|
break;
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-24 06:38:07 +00:00
|
|
|
/* if this mesh has shapekeys,
|
2017-02-02 21:37:53 +01:00
|
|
|
* check if destination mesh already has matching entries too */
|
|
|
|
if (me->key && key) {
|
|
|
|
/* for remapping KeyBlock.relative */
|
2019-01-15 23:24:20 +11:00
|
|
|
int *index_map = MEM_mallocN(sizeof(int) * me->key->totkey, __func__);
|
|
|
|
KeyBlock **kb_map = MEM_mallocN(sizeof(KeyBlock *) * me->key->totkey, __func__);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-09-19 12:11:28 +00:00
|
|
|
for (kb = me->key->block.first, i = 0; kb; kb = kb->next, i++) {
|
2019-01-15 23:24:20 +11:00
|
|
|
BLI_assert(i < me->key->totkey);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-09-19 12:11:28 +00:00
|
|
|
kbn = BKE_keyblock_find_name(key, kb->name);
|
2019-01-15 23:24:20 +11:00
|
|
|
/* if key doesn't exist in destination mesh, add it */
|
2012-03-24 06:38:07 +00:00
|
|
|
if (kbn) {
|
2012-09-19 12:11:28 +00:00
|
|
|
index_map[i] = BLI_findindex(&key->block, kbn);
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2012-09-19 12:11:28 +00:00
|
|
|
else {
|
|
|
|
index_map[i] = key->totkey;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-09-19 12:11:28 +00:00
|
|
|
kbn = BKE_keyblock_add(key, kb->name);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-09-19 12:11:28 +00:00
|
|
|
BKE_keyblock_copy_settings(kbn, kb);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-09-19 12:11:28 +00:00
|
|
|
/* adjust settings to fit (allocate a new data-array) */
|
2020-08-08 13:29:21 +10:00
|
|
|
kbn->data = MEM_callocN(sizeof(float[3]) * totvert, "joined_shapekey");
|
2012-09-19 12:11:28 +00:00
|
|
|
kbn->totelem = totvert;
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
2012-09-19 12:11:28 +00:00
|
|
|
kb_map[i] = kbn;
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
2012-09-19 12:11:28 +00:00
|
|
|
/* remap relative index values */
|
|
|
|
for (kb = me->key->block.first, i = 0; kb; kb = kb->next, i++) {
|
|
|
|
/* sanity check, should always be true */
|
|
|
|
if (LIKELY(kb->relative < me->key->totkey)) {
|
|
|
|
kb_map[i]->relative = index_map[kb->relative];
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
2012-09-19 12:11:28 +00:00
|
|
|
MEM_freeN(index_map);
|
2012-09-19 10:12:07 +00:00
|
|
|
MEM_freeN(kb_map);
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-09-19 12:11:28 +00:00
|
|
|
CTX_DATA_END;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-04-12 11:50:43 +00:00
|
|
|
/* setup new data for destination mesh */
|
|
|
|
CustomData_reset(&vdata);
|
2012-03-26 02:56:48 +00:00
|
|
|
CustomData_reset(&edata);
|
2012-10-31 09:50:24 +00:00
|
|
|
CustomData_reset(&fdata);
|
|
|
|
CustomData_reset(&ldata);
|
|
|
|
CustomData_reset(&pdata);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-10-21 05:46:41 +00:00
|
|
|
mvert = CustomData_add_layer(&vdata, CD_MVERT, CD_CALLOC, NULL, totvert);
|
2012-09-19 12:11:28 +00:00
|
|
|
medge = CustomData_add_layer(&edata, CD_MEDGE, CD_CALLOC, NULL, totedge);
|
|
|
|
mloop = CustomData_add_layer(&ldata, CD_MLOOP, CD_CALLOC, NULL, totloop);
|
2009-07-13 00:40:20 +00:00
|
|
|
mpoly = CustomData_add_layer(&pdata, CD_MPOLY, CD_CALLOC, NULL, totpoly);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-26 02:56:48 +00:00
|
|
|
vertofs = 0;
|
|
|
|
edgeofs = 0;
|
|
|
|
loopofs = 0;
|
|
|
|
polyofs = 0;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-08-25 14:32:10 +10:00
|
|
|
/* Inverse transform for all selected meshes in this object,
|
|
|
|
* See #object_join_exec for detailed comment on why the safe version is used. */
|
|
|
|
invert_m4_m4_safe_ortho(imat, ob->obmat);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-26 02:56:48 +00:00
|
|
|
/* 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.
|
2019-04-17 06:17:24 +02:00
|
|
|
*/
|
2012-03-26 02:56:48 +00:00
|
|
|
join_mesh_single(depsgraph,
|
2019-04-17 06:17:24 +02:00
|
|
|
bmain,
|
|
|
|
scene,
|
|
|
|
ob,
|
|
|
|
ob,
|
|
|
|
imat,
|
|
|
|
&mvert,
|
2012-03-26 02:56:48 +00:00
|
|
|
&medge,
|
|
|
|
&mloop,
|
2019-04-17 06:17:24 +02:00
|
|
|
&mpoly,
|
|
|
|
&vdata,
|
2012-03-26 02:56:48 +00:00
|
|
|
&edata,
|
2019-04-17 06:17:24 +02:00
|
|
|
&ldata,
|
|
|
|
&pdata,
|
2012-03-26 02:56:48 +00:00
|
|
|
totvert,
|
|
|
|
totedge,
|
|
|
|
totloop,
|
|
|
|
totpoly,
|
2019-01-15 23:24:20 +11:00
|
|
|
key,
|
2017-02-02 21:37:53 +01:00
|
|
|
nkey,
|
|
|
|
matar,
|
|
|
|
matmap,
|
|
|
|
totcol,
|
|
|
|
&vertofs,
|
|
|
|
&edgeofs,
|
|
|
|
&loopofs,
|
|
|
|
&polyofs);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-10-01 16:43:49 +10:00
|
|
|
CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) {
|
|
|
|
if (ob_iter == ob) {
|
2017-02-02 21:37:53 +01:00
|
|
|
continue;
|
|
|
|
}
|
2009-07-13 00:40:20 +00:00
|
|
|
/* only join if this is a mesh */
|
2018-10-01 16:43:49 +10:00
|
|
|
if (ob_iter->type == OB_MESH) {
|
2017-02-02 21:37:53 +01:00
|
|
|
join_mesh_single(depsgraph,
|
2018-04-06 12:07:27 +02:00
|
|
|
bmain,
|
|
|
|
scene,
|
2018-10-01 16:43:49 +10:00
|
|
|
ob,
|
|
|
|
ob_iter,
|
|
|
|
imat,
|
2017-02-02 21:37:53 +01:00
|
|
|
&mvert,
|
|
|
|
&medge,
|
|
|
|
&mloop,
|
|
|
|
&mpoly,
|
|
|
|
&vdata,
|
|
|
|
&edata,
|
|
|
|
&ldata,
|
|
|
|
&pdata,
|
|
|
|
totvert,
|
|
|
|
totedge,
|
|
|
|
totloop,
|
|
|
|
totpoly,
|
|
|
|
key,
|
|
|
|
nkey,
|
|
|
|
matar,
|
|
|
|
matmap,
|
|
|
|
totcol,
|
|
|
|
&vertofs,
|
|
|
|
&edgeofs,
|
|
|
|
&loopofs,
|
|
|
|
&polyofs);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2009-07-13 00:40:20 +00:00
|
|
|
/* free base, now that data is merged */
|
2018-10-01 16:43:49 +10:00
|
|
|
if (ob_iter != ob) {
|
|
|
|
ED_object_base_free_and_unlink(bmain, scene, ob_iter);
|
2008-12-30 13:16:14 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
}
|
2009-07-13 00:40:20 +00:00
|
|
|
CTX_DATA_END;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2009-07-13 00:40:20 +00:00
|
|
|
/* return to mesh we're merging to */
|
2012-03-26 02:56:48 +00:00
|
|
|
me = ob->data;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2008-12-30 13:16:14 +00:00
|
|
|
CustomData_free(&me->vdata, me->totvert);
|
|
|
|
CustomData_free(&me->edata, me->totedge);
|
2009-07-16 06:27:37 +00:00
|
|
|
CustomData_free(&me->ldata, me->totloop);
|
|
|
|
CustomData_free(&me->pdata, me->totpoly);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-26 02:56:48 +00:00
|
|
|
me->totvert = totvert;
|
|
|
|
me->totedge = totedge;
|
|
|
|
me->totloop = totloop;
|
|
|
|
me->totpoly = totpoly;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-26 02:56:48 +00:00
|
|
|
me->vdata = vdata;
|
|
|
|
me->edata = edata;
|
|
|
|
me->ldata = ldata;
|
|
|
|
me->pdata = pdata;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-10-25 08:48:05 +00:00
|
|
|
/* tessface data removed above, no need to update */
|
2013-03-17 19:55:10 +00:00
|
|
|
BKE_mesh_update_customdata_pointers(me, false);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-02-15 12:08:01 +00:00
|
|
|
/* update normals in case objects with non-uniform scale are joined */
|
2013-05-28 14:23:07 +00:00
|
|
|
BKE_mesh_calc_normals(me);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2008-12-30 13:16:14 +00:00
|
|
|
/* old material array */
|
2012-03-26 02:56:48 +00:00
|
|
|
for (a = 1; a <= ob->totcol; a++) {
|
|
|
|
ma = ob->mat[a - 1];
|
2019-04-22 09:19:45 +10:00
|
|
|
if (ma) {
|
2015-11-09 19:47:10 +01:00
|
|
|
id_us_min(&ma->id);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2008-12-30 13:16:14 +00:00
|
|
|
}
|
2012-03-26 02:56:48 +00:00
|
|
|
for (a = 1; a <= me->totcol; a++) {
|
|
|
|
ma = me->mat[a - 1];
|
2019-04-22 09:19:45 +10:00
|
|
|
if (ma) {
|
2015-11-09 19:47:10 +01:00
|
|
|
id_us_min(&ma->id);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2008-12-30 13:16:14 +00:00
|
|
|
}
|
2018-03-18 05:03:42 +01:00
|
|
|
MEM_SAFE_FREE(ob->mat);
|
|
|
|
MEM_SAFE_FREE(ob->matbits);
|
|
|
|
MEM_SAFE_FREE(me->mat);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-24 06:38:07 +00:00
|
|
|
if (totcol) {
|
2012-03-26 02:56:48 +00:00
|
|
|
me->mat = matar;
|
2017-02-02 21:37:53 +01:00
|
|
|
ob->mat = MEM_callocN(sizeof(*ob->mat) * totcol, "join obmatar");
|
|
|
|
ob->matbits = MEM_callocN(sizeof(*ob->matbits) * totcol, "join obmatbits");
|
|
|
|
MEM_freeN(matmap);
|
2008-12-30 13:16:14 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-26 02:56:48 +00:00
|
|
|
ob->totcol = me->totcol = totcol;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2008-12-30 13:16:14 +00:00
|
|
|
/* other mesh users */
|
2020-02-05 11:23:58 +01:00
|
|
|
BKE_objects_materials_test_all(bmain, (ID *)me);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2009-07-13 00:40:20 +00:00
|
|
|
/* free temp copy of destination shapekeys (if applicable) */
|
2012-03-24 06:38:07 +00:00
|
|
|
if (nkey) {
|
2017-02-02 21:37:53 +01:00
|
|
|
/* We can assume nobody is using that ID currently. */
|
2019-01-24 14:31:47 +01:00
|
|
|
BKE_id_free_ex(bmain, nkey, LIB_ID_FREE_NO_UI_USER, false);
|
2009-07-13 00:40:20 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-09-19 12:11:28 +00:00
|
|
|
/* ensure newly inserted keys are time sorted */
|
|
|
|
if (key && (key->type != KEY_RELATIVE)) {
|
|
|
|
BKE_key_sort(key);
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-02-05 16:23:34 +11:00
|
|
|
/* Due to dependency cycle some other object might access old derived data. */
|
2017-04-12 16:12:28 +02:00
|
|
|
BKE_object_free_derived_caches(ob);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-06-08 10:14:53 +02:00
|
|
|
DEG_relations_tag_update(bmain); /* removed objects, need to rebuild dag */
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-12-06 17:52:37 +01:00
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-12-06 17:52:37 +01:00
|
|
|
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
|
2012-03-26 02:56:48 +00:00
|
|
|
WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
|
2020-08-21 19:06:20 +02:00
|
|
|
WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2009-07-13 00:40:20 +00:00
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
}
|
2008-12-30 13:16:14 +00:00
|
|
|
|
2020-06-17 17:07:11 +10:00
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/** \name Join as Shapes
|
|
|
|
* \{ */
|
2009-11-28 04:04:01 +00:00
|
|
|
|
2018-06-04 09:31:30 +02:00
|
|
|
/* Append selected meshes vertex locations as shapes of the active mesh,
|
2012-03-03 16:31:46 +00:00
|
|
|
* return 0 if no join is made (error) and 1 of the join is done */
|
2009-11-28 04:04:01 +00:00
|
|
|
|
2020-06-17 17:07:11 +10:00
|
|
|
int ED_mesh_shapes_join_objects_exec(bContext *C, wmOperator *op)
|
2009-11-28 04:04:01 +00:00
|
|
|
{
|
2018-06-12 12:53:27 +02:00
|
|
|
Main *bmain = CTX_data_main(C);
|
2012-03-26 02:56:48 +00:00
|
|
|
Scene *scene = CTX_data_scene(C);
|
2018-10-01 16:43:49 +10:00
|
|
|
Object *ob_active = CTX_data_active_object(C);
|
2019-07-25 16:36:22 +02:00
|
|
|
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
2018-10-01 16:43:49 +10:00
|
|
|
Mesh *me = (Mesh *)ob_active->data;
|
2012-03-26 02:56:48 +00:00
|
|
|
Mesh *selme = NULL;
|
2018-06-21 18:24:32 +02:00
|
|
|
Mesh *me_deformed = NULL;
|
2012-03-26 02:56:48 +00:00
|
|
|
Key *key = me->key;
|
2009-11-28 04:04:01 +00:00
|
|
|
KeyBlock *kb;
|
2014-04-11 11:25:41 +10:00
|
|
|
bool ok = false, nonequal_verts = false;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-10-01 16:43:49 +10:00
|
|
|
CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) {
|
|
|
|
if (ob_iter == ob_active) {
|
|
|
|
continue;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-10-01 16:43:49 +10:00
|
|
|
if (ob_iter->type == OB_MESH) {
|
|
|
|
selme = (Mesh *)ob_iter->data;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-04-22 09:19:45 +10:00
|
|
|
if (selme->totvert == me->totvert) {
|
2014-04-11 11:25:41 +10:00
|
|
|
ok = true;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
|
|
|
else {
|
2012-03-26 02:56:48 +00:00
|
|
|
nonequal_verts = 1;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2009-11-28 04:04:01 +00:00
|
|
|
}
|
|
|
|
CTX_DATA_END;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2009-11-28 04:04:01 +00:00
|
|
|
if (!ok) {
|
2019-04-22 09:19:45 +10:00
|
|
|
if (nonequal_verts) {
|
2011-09-19 12:26:20 +00:00
|
|
|
BKE_report(op->reports, RPT_WARNING, "Selected meshes must have equal numbers of vertices");
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
|
|
|
else {
|
2011-09-19 12:26:20 +00:00
|
|
|
BKE_report(op->reports,
|
|
|
|
RPT_WARNING,
|
|
|
|
"No additional selected meshes with equal vertex count to join");
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2009-11-28 04:04:01 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-24 06:38:07 +00:00
|
|
|
if (key == NULL) {
|
2018-06-12 12:53:27 +02:00
|
|
|
key = me->key = BKE_key_add(bmain, (ID *)me);
|
2012-03-26 02:56:48 +00:00
|
|
|
key->type = KEY_RELATIVE;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-02 16:05:54 +00:00
|
|
|
/* first key added, so it was the basis. initialize it with the existing mesh */
|
2012-09-19 10:12:07 +00:00
|
|
|
kb = BKE_keyblock_add(key, NULL);
|
2018-06-21 18:24:32 +02:00
|
|
|
BKE_keyblock_convert_from_mesh(me, key, kb);
|
2009-11-28 04:04:01 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2009-11-28 04:04:01 +00:00
|
|
|
/* now ready to add new keys from selected meshes */
|
2018-10-01 16:43:49 +10:00
|
|
|
CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) {
|
|
|
|
if (ob_iter == ob_active) {
|
|
|
|
continue;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-10-01 16:43:49 +10:00
|
|
|
if (ob_iter->type == OB_MESH) {
|
|
|
|
selme = (Mesh *)ob_iter->data;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-26 02:56:48 +00:00
|
|
|
if (selme->totvert == me->totvert) {
|
2018-12-01 19:06:44 +03:00
|
|
|
Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
|
|
|
|
Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob_iter);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
Refactor CDData masks, to have one mask per mesh elem type.
We already have different storages for cddata of verts, edges etc.,
'simply' do the same for the mask flags we use all around Blender code
to request some data, or limit some operation to some layers, etc.
Reason we need this is that some cddata types (like Normals) are
actually shared between verts/polys/loops, and we don’t want to generate
clnors everytime we request vnors!
As a side note, this also does final fix to T59338, which was the
trigger for this patch (need to request computed loop normals for
another mesh than evaluated one).
Reviewers: brecht, campbellbarton, sergey
Differential Revision: https://developer.blender.org/D4407
2019-03-07 11:13:40 +01:00
|
|
|
me_deformed = mesh_get_eval_deform(depsgraph, scene_eval, ob_eval, &CD_MASK_BAREMESH);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-06-21 18:24:32 +02:00
|
|
|
if (!me_deformed) {
|
|
|
|
continue;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-10-01 16:43:49 +10:00
|
|
|
kb = BKE_keyblock_add(key, ob_iter->id.name + 2);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-06-21 18:24:32 +02:00
|
|
|
BKE_mesh_runtime_eval_to_meshkey(me_deformed, me, kb);
|
2009-11-28 04:04:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CTX_DATA_END;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-12-06 17:52:37 +01:00
|
|
|
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
|
2012-03-26 02:56:48 +00:00
|
|
|
WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2009-11-28 04:04:01 +00:00
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
}
|
|
|
|
|
2020-06-17 17:07:11 +10:00
|
|
|
/** \} */
|
|
|
|
|
2014-03-13 01:36:24 +11:00
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/** \name Mesh Topology Mirror API
|
|
|
|
* \{ */
|
|
|
|
|
2012-09-07 00:22:10 +00:00
|
|
|
static MirrTopoStore_t mesh_topo_store = {NULL, -1. - 1, -1};
|
2010-02-17 19:50:42 +00:00
|
|
|
|
2020-04-03 21:47:56 +11:00
|
|
|
BLI_INLINE void mesh_mirror_topo_table_get_meshes(Object *ob,
|
|
|
|
Mesh *me_eval,
|
|
|
|
Mesh **r_me_mirror,
|
|
|
|
BMEditMesh **r_em_mirror)
|
|
|
|
{
|
|
|
|
Mesh *me_mirror = NULL;
|
|
|
|
BMEditMesh *em_mirror = NULL;
|
|
|
|
|
|
|
|
Mesh *me = ob->data;
|
|
|
|
if (me_eval != NULL) {
|
|
|
|
me_mirror = me_eval;
|
|
|
|
}
|
|
|
|
else if (me->edit_mesh != NULL) {
|
|
|
|
em_mirror = me->edit_mesh;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
me_mirror = me;
|
|
|
|
}
|
|
|
|
|
|
|
|
*r_me_mirror = me_mirror;
|
|
|
|
*r_em_mirror = em_mirror;
|
|
|
|
}
|
|
|
|
|
2019-11-25 01:14:39 +11:00
|
|
|
/**
|
|
|
|
* Mode is 's' start, or 'e' end, or 'u' use
|
2019-04-22 00:18:34 +10:00
|
|
|
* if end, ob can be NULL.
|
2019-11-25 01:14:39 +11:00
|
|
|
* \note This is supposed return -1 on error,
|
|
|
|
* which callers are currently checking for, but is not used so far.
|
|
|
|
*/
|
2020-04-03 21:47:56 +11:00
|
|
|
void ED_mesh_mirror_topo_table_begin(Object *ob, Mesh *me_eval)
|
2018-05-15 13:26:40 +02:00
|
|
|
{
|
2020-04-03 21:47:56 +11:00
|
|
|
Mesh *me_mirror;
|
|
|
|
BMEditMesh *em_mirror;
|
|
|
|
mesh_mirror_topo_table_get_meshes(ob, me_eval, &me_mirror, &em_mirror);
|
2020-01-07 15:39:08 +11:00
|
|
|
|
2020-04-03 21:47:56 +11:00
|
|
|
ED_mesh_mirrtopo_init(em_mirror, me_mirror, &mesh_topo_store, false);
|
|
|
|
}
|
2020-01-07 15:39:08 +11:00
|
|
|
|
2020-04-03 21:47:56 +11:00
|
|
|
void ED_mesh_mirror_topo_table_end(Object *UNUSED(ob))
|
|
|
|
{
|
|
|
|
/* TODO: store this in object/object-data (keep unused argument for now). */
|
|
|
|
ED_mesh_mirrtopo_free(&mesh_topo_store);
|
|
|
|
}
|
2020-01-07 15:39:08 +11:00
|
|
|
|
2020-09-09 11:10:38 +02:00
|
|
|
/* Returns true on success. */
|
|
|
|
static bool ed_mesh_mirror_topo_table_update(Object *ob, Mesh *me_eval)
|
2020-04-03 21:47:56 +11:00
|
|
|
{
|
|
|
|
Mesh *me_mirror;
|
|
|
|
BMEditMesh *em_mirror;
|
|
|
|
mesh_mirror_topo_table_get_meshes(ob, me_eval, &me_mirror, &em_mirror);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-04-03 21:47:56 +11:00
|
|
|
if (ED_mesh_mirrtopo_recalc_check(em_mirror, me_mirror, &mesh_topo_store)) {
|
|
|
|
ED_mesh_mirror_topo_table_begin(ob, me_eval);
|
|
|
|
}
|
2020-09-09 11:10:38 +02:00
|
|
|
return true;
|
2018-05-15 13:26:40 +02:00
|
|
|
}
|
|
|
|
|
2014-03-13 01:36:24 +11:00
|
|
|
/** \} */
|
|
|
|
|
2020-04-03 21:47:56 +11:00
|
|
|
static int mesh_get_x_mirror_vert_spatial(Object *ob, Mesh *me_eval, int index)
|
2018-05-15 13:26:40 +02:00
|
|
|
{
|
|
|
|
Mesh *me = ob->data;
|
2020-04-03 21:47:56 +11:00
|
|
|
MVert *mvert = me_eval ? me_eval->mvert : me->mvert;
|
2018-05-15 13:26:40 +02:00
|
|
|
float vec[3];
|
|
|
|
|
|
|
|
mvert = &mvert[index];
|
|
|
|
vec[0] = -mvert->co[0];
|
|
|
|
vec[1] = mvert->co[1];
|
|
|
|
vec[2] = mvert->co[2];
|
|
|
|
|
2020-04-03 21:47:56 +11:00
|
|
|
return ED_mesh_mirror_spatial_table_lookup(ob, NULL, me_eval, vec);
|
2018-05-15 13:26:40 +02:00
|
|
|
}
|
|
|
|
|
2018-06-21 19:05:10 +02:00
|
|
|
static int mesh_get_x_mirror_vert_topo(Object *ob, Mesh *mesh, int index)
|
2018-05-15 13:26:40 +02:00
|
|
|
{
|
2020-09-09 11:10:38 +02:00
|
|
|
if (!ed_mesh_mirror_topo_table_update(ob, mesh)) {
|
2018-05-15 13:26:40 +02:00
|
|
|
return -1;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2018-05-15 13:26:40 +02:00
|
|
|
|
|
|
|
return mesh_topo_store.index_lookup[index];
|
|
|
|
}
|
|
|
|
|
2018-06-21 19:05:10 +02:00
|
|
|
int mesh_get_x_mirror_vert(Object *ob, Mesh *me_eval, int index, const bool use_topology)
|
2018-05-15 13:26:40 +02:00
|
|
|
{
|
|
|
|
if (use_topology) {
|
2018-06-21 19:05:10 +02:00
|
|
|
return mesh_get_x_mirror_vert_topo(ob, me_eval, index);
|
2018-05-15 13:26:40 +02:00
|
|
|
}
|
2020-07-03 15:19:52 +02:00
|
|
|
return mesh_get_x_mirror_vert_spatial(ob, me_eval, index);
|
2018-05-15 13:26:40 +02:00
|
|
|
}
|
|
|
|
|
2012-06-27 05:59:41 +00:00
|
|
|
static BMVert *editbmesh_get_x_mirror_vert_spatial(Object *ob, BMEditMesh *em, const float co[3])
|
2008-12-30 13:16:14 +00:00
|
|
|
{
|
|
|
|
float vec[3];
|
2014-03-13 01:36:24 +11:00
|
|
|
int i;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2008-12-30 13:16:14 +00:00
|
|
|
/* ignore nan verts */
|
2016-05-16 00:48:02 +02:00
|
|
|
if ((isfinite(co[0]) == false) || (isfinite(co[1]) == false) || (isfinite(co[2]) == false)) {
|
2008-12-30 13:16:14 +00:00
|
|
|
return NULL;
|
2012-04-28 06:31:57 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-26 02:56:48 +00:00
|
|
|
vec[0] = -co[0];
|
|
|
|
vec[1] = co[1];
|
|
|
|
vec[2] = co[2];
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-04-03 21:47:56 +11:00
|
|
|
i = ED_mesh_mirror_spatial_table_lookup(ob, em, NULL, vec);
|
2014-03-13 01:36:24 +11:00
|
|
|
if (i != -1) {
|
|
|
|
return BM_vert_at_index(em->bm, i);
|
|
|
|
}
|
2008-12-30 13:16:14 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2011-02-27 06:19:40 +00:00
|
|
|
static BMVert *editbmesh_get_x_mirror_vert_topo(Object *ob,
|
|
|
|
struct BMEditMesh *em,
|
|
|
|
BMVert *eve,
|
|
|
|
int index)
|
2010-02-17 19:50:42 +00:00
|
|
|
{
|
2011-12-16 07:27:56 +00:00
|
|
|
intptr_t poinval;
|
2020-09-09 11:10:38 +02:00
|
|
|
if (!ed_mesh_mirror_topo_table_update(ob, NULL)) {
|
2010-02-17 19:50:42 +00:00
|
|
|
return NULL;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2010-09-28 19:53:45 +00:00
|
|
|
if (index == -1) {
|
2011-02-27 06:19:40 +00:00
|
|
|
BMIter iter;
|
|
|
|
BMVert *v;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2011-02-27 06:19:40 +00:00
|
|
|
index = 0;
|
2012-04-19 13:47:58 +00:00
|
|
|
BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
|
2019-04-22 09:19:45 +10:00
|
|
|
if (v == eve) {
|
2011-02-27 06:19:40 +00:00
|
|
|
break;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2011-02-27 06:19:40 +00:00
|
|
|
index++;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2011-02-27 06:19:40 +00:00
|
|
|
if (index == em->bm->totvert) {
|
2010-09-28 19:53:45 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2010-02-17 19:50:42 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-26 02:56:48 +00:00
|
|
|
poinval = mesh_topo_store.index_lookup[index];
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-04-22 09:19:45 +10:00
|
|
|
if (poinval != -1) {
|
2011-02-27 06:19:40 +00:00
|
|
|
return (BMVert *)(poinval);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2010-02-17 19:50:42 +00:00
|
|
|
return NULL;
|
2018-06-04 09:31:30 +02:00
|
|
|
}
|
2010-02-17 19:50:42 +00:00
|
|
|
|
2013-06-28 17:13:09 +00:00
|
|
|
BMVert *editbmesh_get_x_mirror_vert(Object *ob,
|
|
|
|
struct BMEditMesh *em,
|
|
|
|
BMVert *eve,
|
|
|
|
const float co[3],
|
|
|
|
int index,
|
|
|
|
const bool use_topology)
|
2010-02-17 19:50:42 +00:00
|
|
|
{
|
2013-06-28 17:13:09 +00:00
|
|
|
if (use_topology) {
|
2011-02-27 06:19:40 +00:00
|
|
|
return editbmesh_get_x_mirror_vert_topo(ob, em, eve, index);
|
2012-02-06 09:39:47 +00:00
|
|
|
}
|
2020-07-03 15:19:52 +02:00
|
|
|
return editbmesh_get_x_mirror_vert_spatial(ob, em, co);
|
2010-02-17 19:50:42 +00:00
|
|
|
}
|
|
|
|
|
2013-09-17 07:03:13 +00:00
|
|
|
/**
|
2020-03-11 21:39:56 +11:00
|
|
|
* Wrapper for object-mode/edit-mode.
|
2013-09-17 07:03:13 +00:00
|
|
|
*
|
2013-10-28 02:05:33 +00:00
|
|
|
* call #BM_mesh_elem_table_ensure first for editmesh.
|
2013-09-17 07:03:13 +00:00
|
|
|
*/
|
|
|
|
int ED_mesh_mirror_get_vert(Object *ob, int index)
|
|
|
|
{
|
|
|
|
Mesh *me = ob->data;
|
2019-02-17 18:05:18 +11:00
|
|
|
BMEditMesh *em = me->edit_mesh;
|
2013-09-17 07:03:13 +00:00
|
|
|
bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0;
|
|
|
|
int index_mirr;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-09-17 07:03:13 +00:00
|
|
|
if (em) {
|
|
|
|
BMVert *eve, *eve_mirr;
|
2013-10-28 02:05:33 +00:00
|
|
|
eve = BM_vert_at_index(em->bm, index);
|
2013-09-17 07:03:13 +00:00
|
|
|
eve_mirr = editbmesh_get_x_mirror_vert(ob, em, eve, eve->co, index, use_topology);
|
|
|
|
index_mirr = eve_mirr ? BM_elem_index_get(eve_mirr) : -1;
|
|
|
|
}
|
|
|
|
else {
|
Fix T47038: Particles in Particle Edit Mode get added in completely wrong location.
It also fixes another issue (crash) related to symmetric editing.
Quite involved, we (try to!) fix complete broken logic of parts of particle code, which would use poly index
as tessface one (or vice-versa). Issue most probably goes back to BMesh integration time...
This patch mostly fixes particle editing mode:
- Adding/removing particles when using generative modifiers (like subsurf) should now work.
- Adding/removing particles with a non-tessellated mesh (i.e. one having ngons) should also mostly work.
- X-axis-mirror-editing particles over ngons does not really work, not sure why currently.
- All this in both 'modes' (with or without using modifier stack for particles).
Tech side:
- Store a deformed-only DM in particle modifier data.
- Rename existing DM to make it clear it's a final one.
- Use deformed-only DM's tessface2poly mapping to 'solve' poly/tessface mismatches.
- Make (part of) mirror-editing code able to use a DM instead of raw mesh, so that we can mirror based on final DM
when editing particles using modifier stack (mandatory, since there is no way currently to find orig tessface
from an final DM tessface index).
Note that this patch is not really nice and clean (current particles are beyond hope on this side anyway),
it's more like some urgency bandage. Whole crap needs complete rewrite anyway,
BMesh's polygons make it really hard to work with current system (and looptri would not help much here).
Also, did not test everything possibly affected by those changes, so it needs some users' testing & validation too.
Reviewers: psy-fi
Subscribers: dfelinto, eyecandy
Maniphest Tasks: T47038
Differential Revision: https://developer.blender.org/D1685
2016-01-04 12:19:45 +01:00
|
|
|
index_mirr = mesh_get_x_mirror_vert(ob, NULL, index, use_topology);
|
2013-09-17 07:03:13 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-09-17 07:03:13 +00:00
|
|
|
return index_mirr;
|
|
|
|
}
|
|
|
|
|
2012-02-06 09:39:47 +00:00
|
|
|
#if 0
|
|
|
|
|
2019-04-17 08:24:14 +02:00
|
|
|
static float *editmesh_get_mirror_uv(
|
|
|
|
BMEditMesh *em, int axis, float *uv, float *mirrCent, float *face_cent)
|
2010-02-17 19:50:42 +00:00
|
|
|
{
|
|
|
|
float vec[2];
|
|
|
|
float cent_vec[2];
|
|
|
|
float cent[2];
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2010-02-17 19:50:42 +00:00
|
|
|
/* ignore nan verts */
|
2019-04-17 08:24:14 +02:00
|
|
|
if (isnan(uv[0]) || !isfinite(uv[0]) || isnan(uv[1]) || !isfinite(uv[1])) {
|
2010-02-17 19:50:42 +00:00
|
|
|
return NULL;
|
2016-01-21 09:05:52 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2010-02-17 19:50:42 +00:00
|
|
|
if (axis) {
|
2012-03-26 02:56:48 +00:00
|
|
|
vec[0] = uv[0];
|
|
|
|
vec[1] = -((uv[1]) - mirrCent[1]) + mirrCent[1];
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2010-02-17 19:50:42 +00:00
|
|
|
cent_vec[0] = face_cent[0];
|
2012-03-26 02:56:48 +00:00
|
|
|
cent_vec[1] = -((face_cent[1]) - mirrCent[1]) + mirrCent[1];
|
2012-03-24 06:38:07 +00:00
|
|
|
}
|
|
|
|
else {
|
2012-03-26 02:56:48 +00:00
|
|
|
vec[0] = -((uv[0]) - mirrCent[0]) + mirrCent[0];
|
|
|
|
vec[1] = uv[1];
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-26 02:56:48 +00:00
|
|
|
cent_vec[0] = -((face_cent[0]) - mirrCent[0]) + mirrCent[0];
|
2010-02-17 19:50:42 +00:00
|
|
|
cent_vec[1] = face_cent[1];
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2010-02-17 19:50:42 +00:00
|
|
|
/* TODO - Optimize */
|
|
|
|
{
|
2011-02-27 06:19:40 +00:00
|
|
|
BMIter iter;
|
|
|
|
BMFace *efa;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-04-19 13:47:58 +00:00
|
|
|
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
|
2020-08-11 15:11:31 +10:00
|
|
|
BM_face_uv_calc_center_median(efa, cd_loop_uv_offset, cent);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-04-17 08:24:14 +02:00
|
|
|
if ((fabsf(cent[0] - cent_vec[0]) < 0.001f) && (fabsf(cent[1] - cent_vec[1]) < 0.001f)) {
|
2011-02-27 06:19:40 +00:00
|
|
|
BMIter liter;
|
|
|
|
BMLoop *l;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-04-19 13:47:58 +00:00
|
|
|
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
|
2011-02-27 06:19:40 +00:00
|
|
|
MLoopUV *luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV);
|
2019-04-17 08:24:14 +02:00
|
|
|
if ((fabsf(luv->uv[0] - vec[0]) < 0.001f) && (fabsf(luv->uv[1] - vec[1]) < 0.001f)) {
|
2011-02-27 06:19:40 +00:00
|
|
|
return luv->uv;
|
2012-03-26 02:56:48 +00:00
|
|
|
}
|
2010-02-17 19:50:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2010-02-17 19:50:42 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-02-06 09:39:47 +00:00
|
|
|
#endif
|
|
|
|
|
2020-04-03 16:21:24 +11:00
|
|
|
static uint mirror_facehash(const void *ptr)
|
2008-12-30 13:16:14 +00:00
|
|
|
{
|
2012-03-26 02:56:48 +00:00
|
|
|
const MFace *mf = ptr;
|
2020-04-03 16:21:24 +11:00
|
|
|
uint v0, v1;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-24 06:38:07 +00:00
|
|
|
if (mf->v4) {
|
2012-03-26 02:56:48 +00:00
|
|
|
v0 = MIN4(mf->v1, mf->v2, mf->v3, mf->v4);
|
|
|
|
v1 = MAX4(mf->v1, mf->v2, mf->v3, mf->v4);
|
2008-12-30 13:16:14 +00:00
|
|
|
}
|
|
|
|
else {
|
2012-03-26 02:56:48 +00:00
|
|
|
v0 = MIN3(mf->v1, mf->v2, mf->v3);
|
|
|
|
v1 = MAX3(mf->v1, mf->v2, mf->v3);
|
2008-12-30 13:16:14 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-26 02:56:48 +00:00
|
|
|
return ((v0 * 39) ^ (v1 * 31));
|
2008-12-30 13:16:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int mirror_facerotation(MFace *a, MFace *b)
|
|
|
|
{
|
2012-03-24 06:38:07 +00:00
|
|
|
if (b->v4) {
|
2019-04-22 09:19:45 +10:00
|
|
|
if (a->v1 == b->v1 && a->v2 == b->v2 && a->v3 == b->v3 && a->v4 == b->v4) {
|
2008-12-30 13:16:14 +00:00
|
|
|
return 0;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2020-07-03 15:19:52 +02:00
|
|
|
if (a->v4 == b->v1 && a->v1 == b->v2 && a->v2 == b->v3 && a->v3 == b->v4) {
|
2008-12-30 13:16:14 +00:00
|
|
|
return 1;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2020-07-03 15:19:52 +02:00
|
|
|
if (a->v3 == b->v1 && a->v4 == b->v2 && a->v1 == b->v3 && a->v2 == b->v4) {
|
2008-12-30 13:16:14 +00:00
|
|
|
return 2;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2020-07-03 15:19:52 +02:00
|
|
|
if (a->v2 == b->v1 && a->v3 == b->v2 && a->v4 == b->v3 && a->v1 == b->v4) {
|
2008-12-30 13:16:14 +00:00
|
|
|
return 3;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2008-12-30 13:16:14 +00:00
|
|
|
}
|
|
|
|
else {
|
2019-04-22 09:19:45 +10:00
|
|
|
if (a->v1 == b->v1 && a->v2 == b->v2 && a->v3 == b->v3) {
|
2008-12-30 13:16:14 +00:00
|
|
|
return 0;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2020-07-03 15:19:52 +02:00
|
|
|
if (a->v3 == b->v1 && a->v1 == b->v2 && a->v2 == b->v3) {
|
2008-12-30 13:16:14 +00:00
|
|
|
return 1;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2020-07-03 15:19:52 +02:00
|
|
|
if (a->v2 == b->v1 && a->v3 == b->v2 && a->v1 == b->v3) {
|
2008-12-30 13:16:14 +00:00
|
|
|
return 2;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2008-12-30 13:16:14 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2008-12-30 13:16:14 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-09-25 06:15:52 +10:00
|
|
|
static bool mirror_facecmp(const void *a, const void *b)
|
2008-12-30 13:16:14 +00:00
|
|
|
{
|
2012-03-26 02:56:48 +00:00
|
|
|
return (mirror_facerotation((MFace *)a, (MFace *)b) == -1);
|
2008-12-30 13:16:14 +00:00
|
|
|
}
|
|
|
|
|
2018-05-15 13:26:40 +02:00
|
|
|
/* This is a Mesh-based copy of mesh_get_x_mirror_faces() */
|
2018-06-21 19:05:10 +02:00
|
|
|
int *mesh_get_x_mirror_faces(Object *ob, BMEditMesh *em, Mesh *me_eval)
|
2018-05-15 13:26:40 +02:00
|
|
|
{
|
|
|
|
Mesh *me = ob->data;
|
|
|
|
MVert *mv, *mvert;
|
|
|
|
MFace mirrormf, *mf, *hashmf, *mface;
|
|
|
|
GHash *fhash;
|
|
|
|
int *mirrorverts, *mirrorfaces;
|
|
|
|
|
|
|
|
BLI_assert(em == NULL); /* Does not work otherwise, currently... */
|
|
|
|
|
|
|
|
const bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0;
|
2018-06-21 19:05:10 +02:00
|
|
|
const int totvert = me_eval ? me_eval->totvert : me->totvert;
|
|
|
|
const int totface = me_eval ? me_eval->totface : me->totface;
|
2018-05-15 13:26:40 +02:00
|
|
|
int a;
|
|
|
|
|
|
|
|
mirrorverts = MEM_callocN(sizeof(int) * totvert, "MirrorVerts");
|
2020-08-08 13:29:21 +10:00
|
|
|
mirrorfaces = MEM_callocN(sizeof(int[2]) * totface, "MirrorFaces");
|
2018-05-15 13:26:40 +02:00
|
|
|
|
2018-06-21 19:05:10 +02:00
|
|
|
mvert = me_eval ? me_eval->mvert : me->mvert;
|
|
|
|
mface = me_eval ? me_eval->mface : me->mface;
|
2018-05-15 13:26:40 +02:00
|
|
|
|
2020-04-03 21:47:56 +11:00
|
|
|
ED_mesh_mirror_spatial_table_begin(ob, em, me_eval);
|
2018-05-15 13:26:40 +02:00
|
|
|
|
2019-04-22 09:19:45 +10:00
|
|
|
for (a = 0, mv = mvert; a < totvert; a++, mv++) {
|
2018-06-21 19:05:10 +02:00
|
|
|
mirrorverts[a] = mesh_get_x_mirror_vert(ob, me_eval, a, use_topology);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2018-05-15 13:26:40 +02:00
|
|
|
|
2020-04-03 21:47:56 +11:00
|
|
|
ED_mesh_mirror_spatial_table_end(ob);
|
2018-05-15 13:26:40 +02:00
|
|
|
|
|
|
|
fhash = BLI_ghash_new_ex(mirror_facehash, mirror_facecmp, "mirror_facehash gh", me->totface);
|
2019-04-22 09:19:45 +10:00
|
|
|
for (a = 0, mf = mface; a < totface; a++, mf++) {
|
2018-05-15 13:26:40 +02:00
|
|
|
BLI_ghash_insert(fhash, mf, mf);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2018-05-15 13:26:40 +02:00
|
|
|
|
|
|
|
for (a = 0, mf = mface; a < totface; a++, mf++) {
|
|
|
|
mirrormf.v1 = mirrorverts[mf->v3];
|
|
|
|
mirrormf.v2 = mirrorverts[mf->v2];
|
|
|
|
mirrormf.v3 = mirrorverts[mf->v1];
|
|
|
|
mirrormf.v4 = (mf->v4) ? mirrorverts[mf->v4] : 0;
|
|
|
|
|
|
|
|
/* make sure v4 is not 0 if a quad */
|
|
|
|
if (mf->v4 && mirrormf.v4 == 0) {
|
2020-04-03 16:21:24 +11:00
|
|
|
SWAP(uint, mirrormf.v1, mirrormf.v3);
|
|
|
|
SWAP(uint, mirrormf.v2, mirrormf.v4);
|
2018-05-15 13:26:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
hashmf = BLI_ghash_lookup(fhash, &mirrormf);
|
|
|
|
if (hashmf) {
|
|
|
|
mirrorfaces[a * 2] = hashmf - mface;
|
|
|
|
mirrorfaces[a * 2 + 1] = mirror_facerotation(&mirrormf, hashmf);
|
|
|
|
}
|
2019-04-22 09:19:45 +10:00
|
|
|
else {
|
2018-05-15 13:26:40 +02:00
|
|
|
mirrorfaces[a * 2] = -1;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2018-05-15 13:26:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
BLI_ghash_free(fhash, NULL, NULL);
|
|
|
|
MEM_freeN(mirrorverts);
|
|
|
|
|
2008-12-30 13:16:14 +00:00
|
|
|
return mirrorfaces;
|
|
|
|
}
|
2012-09-06 23:50:28 +00:00
|
|
|
|
|
|
|
/* selection, vertex and face */
|
|
|
|
/* returns 0 if not found, otherwise 1 */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Face selection in object mode,
|
|
|
|
* currently only weight-paint and vertex-paint use this.
|
|
|
|
*
|
2013-03-19 23:17:44 +00:00
|
|
|
* \return boolean true == Found
|
2012-09-06 23:50:28 +00:00
|
|
|
*/
|
2019-03-16 11:38:55 +11:00
|
|
|
bool ED_mesh_pick_face(bContext *C, Object *ob, const int mval[2], uint dist_px, uint *r_index)
|
2012-09-06 23:50:28 +00:00
|
|
|
{
|
|
|
|
ViewContext vc;
|
2012-12-23 01:54:11 +00:00
|
|
|
Mesh *me = ob->data;
|
|
|
|
|
|
|
|
BLI_assert(me && GS(me->id.name) == ID_ME);
|
2012-09-06 23:50:28 +00:00
|
|
|
|
2019-04-22 09:19:45 +10:00
|
|
|
if (!me || me->totpoly == 0) {
|
2013-03-19 23:17:44 +00:00
|
|
|
return false;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2012-09-06 23:50:28 +00:00
|
|
|
|
2019-09-18 17:19:07 +02:00
|
|
|
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
|
|
|
ED_view3d_viewcontext_init(C, &vc, depsgraph);
|
2019-05-16 09:26:33 +10:00
|
|
|
ED_view3d_select_id_validate(&vc);
|
2012-09-06 23:50:28 +00:00
|
|
|
|
2019-03-15 16:02:55 -03:00
|
|
|
if (dist_px) {
|
2012-09-07 00:58:00 +00:00
|
|
|
/* sample rect to increase chances of selecting, so that when clicking
|
2012-09-06 23:50:28 +00:00
|
|
|
* on an edge in the backbuf, we can still select a face */
|
2019-08-15 10:31:54 -03:00
|
|
|
*r_index = DRW_select_buffer_find_nearest_to_point(
|
2020-03-06 16:56:42 +01:00
|
|
|
vc.depsgraph, vc.region, vc.v3d, mval, 1, me->totpoly + 1, &dist_px);
|
2012-09-06 23:50:28 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* sample only on the exact position */
|
2020-03-06 16:56:42 +01:00
|
|
|
*r_index = DRW_select_buffer_sample_point(vc.depsgraph, vc.region, vc.v3d, mval);
|
2012-09-06 23:50:28 +00:00
|
|
|
}
|
|
|
|
|
2020-04-03 16:21:24 +11:00
|
|
|
if ((*r_index) == 0 || (*r_index) > (uint)me->totpoly) {
|
2013-03-19 23:17:44 +00:00
|
|
|
return false;
|
2019-03-16 11:38:55 +11:00
|
|
|
}
|
2012-09-06 23:50:28 +00:00
|
|
|
|
2019-03-16 11:38:55 +11:00
|
|
|
(*r_index)--;
|
2012-09-06 23:50:28 +00:00
|
|
|
|
2013-03-19 23:17:44 +00:00
|
|
|
return true;
|
2012-09-06 23:50:28 +00:00
|
|
|
}
|
2019-03-15 16:02:55 -03:00
|
|
|
|
2013-11-01 01:14:36 +00:00
|
|
|
static void ed_mesh_pick_face_vert__mpoly_find(
|
|
|
|
/* context */
|
2020-03-06 16:56:42 +01:00
|
|
|
struct ARegion *region,
|
2013-11-01 01:14:36 +00:00
|
|
|
const float mval[2],
|
2018-06-01 14:29:22 +02:00
|
|
|
/* mesh data (evaluated) */
|
|
|
|
const MPoly *mp,
|
|
|
|
const MVert *mvert,
|
|
|
|
const MLoop *mloop,
|
2013-11-01 01:14:36 +00:00
|
|
|
/* return values */
|
|
|
|
float *r_len_best,
|
|
|
|
int *r_v_idx_best)
|
|
|
|
{
|
|
|
|
const MLoop *ml;
|
|
|
|
int j = mp->totloop;
|
|
|
|
for (ml = &mloop[mp->loopstart]; j--; ml++) {
|
2018-06-01 14:29:22 +02:00
|
|
|
float sco[2];
|
2013-11-01 01:14:36 +00:00
|
|
|
const int v_idx = ml->v;
|
2018-06-01 14:29:22 +02:00
|
|
|
const float *co = mvert[v_idx].co;
|
2020-03-06 16:56:42 +01:00
|
|
|
if (ED_view3d_project_float_object(region, co, sco, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) {
|
2018-06-01 14:29:22 +02:00
|
|
|
const float len_test = len_manhattan_v2v2(mval, sco);
|
|
|
|
if (len_test < *r_len_best) {
|
|
|
|
*r_len_best = len_test;
|
2013-11-01 01:14:36 +00:00
|
|
|
*r_v_idx_best = v_idx;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-09-07 05:54:54 +00:00
|
|
|
/**
|
|
|
|
* Use when the back buffer stores face index values. but we want a vert.
|
|
|
|
* This gets the face then finds the closest vertex to mval.
|
|
|
|
*/
|
2019-03-16 11:38:55 +11:00
|
|
|
bool ED_mesh_pick_face_vert(
|
|
|
|
bContext *C, Object *ob, const int mval[2], uint dist_px, uint *r_index)
|
2012-09-07 05:54:54 +00:00
|
|
|
{
|
2019-07-25 16:36:22 +02:00
|
|
|
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
2020-04-03 16:21:24 +11:00
|
|
|
uint poly_index;
|
2012-12-23 01:54:11 +00:00
|
|
|
Mesh *me = ob->data;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-12-23 01:54:11 +00:00
|
|
|
BLI_assert(me && GS(me->id.name) == ID_ME);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-03-16 11:38:55 +11:00
|
|
|
if (ED_mesh_pick_face(C, ob, mval, dist_px, &poly_index)) {
|
2018-12-01 14:46:55 +03:00
|
|
|
Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
|
|
|
|
Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob);
|
2020-03-06 16:56:42 +01:00
|
|
|
struct ARegion *region = CTX_wm_region(C);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-09-07 05:54:54 +00:00
|
|
|
/* derived mesh to find deformed locations */
|
Refactor CDData masks, to have one mask per mesh elem type.
We already have different storages for cddata of verts, edges etc.,
'simply' do the same for the mask flags we use all around Blender code
to request some data, or limit some operation to some layers, etc.
Reason we need this is that some cddata types (like Normals) are
actually shared between verts/polys/loops, and we don’t want to generate
clnors everytime we request vnors!
As a side note, this also does final fix to T59338, which was the
trigger for this patch (need to request computed loop normals for
another mesh than evaluated one).
Reviewers: brecht, campbellbarton, sergey
Differential Revision: https://developer.blender.org/D4407
2019-03-07 11:13:40 +01:00
|
|
|
Mesh *me_eval = mesh_get_eval_final(
|
|
|
|
depsgraph, scene_eval, ob_eval, &CD_MASK_BAREMESH_ORIGINDEX);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-11-01 01:14:36 +00:00
|
|
|
int v_idx_best = ORIGINDEX_NONE;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-11-01 01:14:36 +00:00
|
|
|
/* find the vert closest to 'mval' */
|
|
|
|
const float mval_f[2] = {UNPACK2(mval)};
|
|
|
|
float len_best = FLT_MAX;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-06-01 14:29:22 +02:00
|
|
|
MPoly *me_eval_mpoly;
|
|
|
|
MLoop *me_eval_mloop;
|
|
|
|
MVert *me_eval_mvert;
|
2020-04-03 16:21:24 +11:00
|
|
|
uint me_eval_mpoly_len;
|
2013-11-01 01:14:36 +00:00
|
|
|
const int *index_mp_to_orig;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-06-01 14:29:22 +02:00
|
|
|
me_eval_mpoly = me_eval->mpoly;
|
|
|
|
me_eval_mloop = me_eval->mloop;
|
|
|
|
me_eval_mvert = me_eval->mvert;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-06-01 14:29:22 +02:00
|
|
|
me_eval_mpoly_len = me_eval->totpoly;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-06-01 14:29:22 +02:00
|
|
|
index_mp_to_orig = CustomData_get_layer(&me_eval->pdata, CD_ORIGINDEX);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-11-01 01:14:36 +00:00
|
|
|
/* tag all verts using this face */
|
|
|
|
if (index_mp_to_orig) {
|
2020-04-03 16:21:24 +11:00
|
|
|
uint i;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-06-01 14:29:22 +02:00
|
|
|
for (i = 0; i < me_eval_mpoly_len; i++) {
|
2013-11-01 01:14:36 +00:00
|
|
|
if (index_mp_to_orig[i] == poly_index) {
|
2020-03-06 16:56:42 +01:00
|
|
|
ed_mesh_pick_face_vert__mpoly_find(region,
|
|
|
|
mval_f,
|
|
|
|
&me_eval_mpoly[i],
|
|
|
|
me_eval_mvert,
|
|
|
|
me_eval_mloop,
|
|
|
|
&len_best,
|
|
|
|
&v_idx_best);
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
}
|
2012-09-07 05:54:54 +00:00
|
|
|
}
|
2013-11-01 01:14:36 +00:00
|
|
|
else {
|
2018-06-01 14:29:22 +02:00
|
|
|
if (poly_index < me_eval_mpoly_len) {
|
2020-03-06 16:56:42 +01:00
|
|
|
ed_mesh_pick_face_vert__mpoly_find(region,
|
2013-11-01 01:14:36 +00:00
|
|
|
mval_f,
|
2018-06-01 14:29:22 +02:00
|
|
|
&me_eval_mpoly[poly_index],
|
|
|
|
me_eval_mvert,
|
|
|
|
me_eval_mloop,
|
2013-11-01 01:14:36 +00:00
|
|
|
&len_best,
|
|
|
|
&v_idx_best);
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
2019-03-16 10:12:47 +11:00
|
|
|
/* map 'dm -> me' r_index if possible */
|
2013-11-01 01:14:36 +00:00
|
|
|
if (v_idx_best != ORIGINDEX_NONE) {
|
|
|
|
const int *index_mv_to_orig;
|
2018-06-01 14:29:22 +02:00
|
|
|
index_mv_to_orig = CustomData_get_layer(&me_eval->vdata, CD_ORIGINDEX);
|
2013-11-01 01:14:36 +00:00
|
|
|
if (index_mv_to_orig) {
|
|
|
|
v_idx_best = index_mv_to_orig[v_idx_best];
|
|
|
|
}
|
2012-09-07 05:54:54 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-11-01 01:14:36 +00:00
|
|
|
if ((v_idx_best != ORIGINDEX_NONE) && (v_idx_best < me->totvert)) {
|
2019-03-16 10:12:47 +11:00
|
|
|
*r_index = v_idx_best;
|
2013-03-19 23:17:44 +00:00
|
|
|
return true;
|
2012-09-07 05:54:54 +00:00
|
|
|
}
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-03-19 23:17:44 +00:00
|
|
|
return false;
|
2012-09-07 05:54:54 +00:00
|
|
|
}
|
2012-09-06 23:50:28 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Vertex selection in object mode,
|
|
|
|
* currently only weight paint uses this.
|
|
|
|
*
|
2013-03-19 23:17:44 +00:00
|
|
|
* \return boolean true == Found
|
2012-09-06 23:50:28 +00:00
|
|
|
*/
|
2012-12-23 02:32:03 +00:00
|
|
|
typedef struct VertPickData {
|
|
|
|
const MVert *mvert;
|
|
|
|
const float *mval_f; /* [2] */
|
2020-03-06 16:56:42 +01:00
|
|
|
ARegion *region;
|
2012-12-23 02:32:03 +00:00
|
|
|
|
|
|
|
/* runtime */
|
|
|
|
float len_best;
|
|
|
|
int v_idx_best;
|
|
|
|
} VertPickData;
|
|
|
|
|
|
|
|
static void ed_mesh_pick_vert__mapFunc(void *userData,
|
|
|
|
int index,
|
|
|
|
const float co[3],
|
|
|
|
const float UNUSED(no_f[3]),
|
|
|
|
const short UNUSED(no_s[3]))
|
|
|
|
{
|
|
|
|
VertPickData *data = userData;
|
|
|
|
if ((data->mvert[index].flag & ME_HIDE) == 0) {
|
|
|
|
float sco[2];
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-03-06 16:56:42 +01:00
|
|
|
if (ED_view3d_project_float_object(data->region, co, sco, V3D_PROJ_TEST_CLIP_DEFAULT) ==
|
2012-12-23 02:32:03 +00:00
|
|
|
V3D_PROJ_RET_OK) {
|
|
|
|
const float len = len_manhattan_v2v2(data->mval_f, sco);
|
|
|
|
if (len < data->len_best) {
|
|
|
|
data->len_best = len;
|
|
|
|
data->v_idx_best = index;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-03-16 11:38:55 +11:00
|
|
|
bool ED_mesh_pick_vert(
|
|
|
|
bContext *C, Object *ob, const int mval[2], uint dist_px, bool use_zbuf, uint *r_index)
|
2012-09-06 23:50:28 +00:00
|
|
|
{
|
|
|
|
ViewContext vc;
|
2012-12-23 01:54:11 +00:00
|
|
|
Mesh *me = ob->data;
|
|
|
|
|
|
|
|
BLI_assert(me && GS(me->id.name) == ID_ME);
|
2012-09-06 23:50:28 +00:00
|
|
|
|
2019-04-22 09:19:45 +10:00
|
|
|
if (!me || me->totvert == 0) {
|
2013-03-19 23:17:44 +00:00
|
|
|
return false;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2012-09-06 23:50:28 +00:00
|
|
|
|
2019-09-18 17:19:07 +02:00
|
|
|
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
|
|
|
ED_view3d_viewcontext_init(C, &vc, depsgraph);
|
2019-05-16 09:26:33 +10:00
|
|
|
ED_view3d_select_id_validate(&vc);
|
2012-09-07 00:58:00 +00:00
|
|
|
|
2012-12-23 01:54:11 +00:00
|
|
|
if (use_zbuf) {
|
2019-03-15 16:02:55 -03:00
|
|
|
if (dist_px > 0) {
|
2012-12-23 01:54:11 +00:00
|
|
|
/* sample rect to increase chances of selecting, so that when clicking
|
|
|
|
* on an face in the backbuf, we can still select a vert */
|
2019-08-15 10:31:54 -03:00
|
|
|
*r_index = DRW_select_buffer_find_nearest_to_point(
|
2020-03-06 16:56:42 +01:00
|
|
|
vc.depsgraph, vc.region, vc.v3d, mval, 1, me->totvert + 1, &dist_px);
|
2012-12-23 01:54:11 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* sample only on the exact position */
|
2020-03-06 16:56:42 +01:00
|
|
|
*r_index = DRW_select_buffer_sample_point(vc.depsgraph, vc.region, vc.v3d, mval);
|
2012-12-23 01:54:11 +00:00
|
|
|
}
|
|
|
|
|
2019-03-16 10:12:47 +11:00
|
|
|
if ((*r_index) == 0 || (*r_index) > (uint)me->totvert) {
|
2013-03-19 23:17:44 +00:00
|
|
|
return false;
|
2019-03-16 10:12:47 +11:00
|
|
|
}
|
2012-12-23 01:54:11 +00:00
|
|
|
|
2019-03-16 10:12:47 +11:00
|
|
|
(*r_index)--;
|
2012-09-06 23:50:28 +00:00
|
|
|
}
|
|
|
|
else {
|
2018-12-01 14:46:55 +03:00
|
|
|
Scene *scene_eval = DEG_get_evaluated_scene(vc.depsgraph);
|
|
|
|
Object *ob_eval = DEG_get_evaluated_object(vc.depsgraph, ob);
|
|
|
|
|
2012-12-23 01:54:11 +00:00
|
|
|
/* derived mesh to find deformed locations */
|
Refactor CDData masks, to have one mask per mesh elem type.
We already have different storages for cddata of verts, edges etc.,
'simply' do the same for the mask flags we use all around Blender code
to request some data, or limit some operation to some layers, etc.
Reason we need this is that some cddata types (like Normals) are
actually shared between verts/polys/loops, and we don’t want to generate
clnors everytime we request vnors!
As a side note, this also does final fix to T59338, which was the
trigger for this patch (need to request computed loop normals for
another mesh than evaluated one).
Reviewers: brecht, campbellbarton, sergey
Differential Revision: https://developer.blender.org/D4407
2019-03-07 11:13:40 +01:00
|
|
|
Mesh *me_eval = mesh_get_eval_final(vc.depsgraph, scene_eval, ob_eval, &CD_MASK_BAREMESH);
|
2020-03-06 16:56:42 +01:00
|
|
|
ARegion *region = vc.region;
|
|
|
|
RegionView3D *rv3d = region->regiondata;
|
2012-09-06 23:50:28 +00:00
|
|
|
|
2012-12-23 02:32:03 +00:00
|
|
|
/* find the vert closest to 'mval' */
|
|
|
|
const float mval_f[2] = {(float)mval[0], (float)mval[1]};
|
2012-09-06 23:50:28 +00:00
|
|
|
|
2013-08-07 03:55:21 +00:00
|
|
|
VertPickData data = {NULL};
|
2012-12-23 02:32:03 +00:00
|
|
|
|
|
|
|
ED_view3d_init_mats_rv3d(ob, rv3d);
|
2012-12-23 01:54:11 +00:00
|
|
|
|
2018-06-21 18:38:37 +02:00
|
|
|
if (me_eval == NULL) {
|
2013-03-19 23:17:44 +00:00
|
|
|
return false;
|
2012-12-23 01:54:11 +00:00
|
|
|
}
|
|
|
|
|
2012-12-23 02:32:03 +00:00
|
|
|
/* setup data */
|
|
|
|
data.mvert = me->mvert;
|
2020-03-06 16:56:42 +01:00
|
|
|
data.region = region;
|
2012-12-23 02:32:03 +00:00
|
|
|
data.mval_f = mval_f;
|
|
|
|
data.len_best = FLT_MAX;
|
|
|
|
data.v_idx_best = -1;
|
2012-12-23 02:04:38 +00:00
|
|
|
|
2018-06-21 18:38:37 +02:00
|
|
|
BKE_mesh_foreach_mapped_vert(me_eval, ed_mesh_pick_vert__mapFunc, &data, MESH_FOREACH_NOP);
|
2012-12-23 01:54:11 +00:00
|
|
|
|
2012-12-23 02:32:03 +00:00
|
|
|
if (data.v_idx_best == -1) {
|
2013-03-19 23:17:44 +00:00
|
|
|
return false;
|
2012-12-23 01:54:11 +00:00
|
|
|
}
|
2012-12-23 02:32:03 +00:00
|
|
|
|
2019-03-16 10:12:47 +11:00
|
|
|
*r_index = data.v_idx_best;
|
2012-12-23 01:54:11 +00:00
|
|
|
}
|
2012-09-06 23:50:28 +00:00
|
|
|
|
2013-03-19 23:17:44 +00:00
|
|
|
return true;
|
2012-09-06 23:50:28 +00:00
|
|
|
}
|
2013-06-24 04:41:03 +00:00
|
|
|
|
|
|
|
MDeformVert *ED_mesh_active_dvert_get_em(Object *ob, BMVert **r_eve)
|
|
|
|
{
|
2018-04-05 18:20:27 +02:00
|
|
|
if (ob->mode & OB_MODE_EDIT && ob->type == OB_MESH && ob->defbase.first) {
|
2013-06-24 04:41:03 +00:00
|
|
|
Mesh *me = ob->data;
|
2019-02-17 18:05:18 +11:00
|
|
|
BMesh *bm = me->edit_mesh->bm;
|
2018-04-05 18:20:27 +02:00
|
|
|
const int cd_dvert_offset = CustomData_get_offset(&bm->vdata, CD_MDEFORMVERT);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-04-05 18:20:27 +02:00
|
|
|
if (cd_dvert_offset != -1) {
|
|
|
|
BMVert *eve = BM_mesh_active_vert_get(bm);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-04-05 18:20:27 +02:00
|
|
|
if (eve) {
|
2019-04-22 09:19:45 +10:00
|
|
|
if (r_eve) {
|
2018-04-05 18:20:27 +02:00
|
|
|
*r_eve = eve;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2018-04-05 18:20:27 +02:00
|
|
|
return BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset);
|
2013-06-24 04:41:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-04-22 09:19:45 +10:00
|
|
|
if (r_eve) {
|
2013-06-24 04:41:03 +00:00
|
|
|
*r_eve = NULL;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2013-06-24 04:41:03 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
MDeformVert *ED_mesh_active_dvert_get_ob(Object *ob, int *r_index)
|
|
|
|
{
|
|
|
|
Mesh *me = ob->data;
|
|
|
|
int index = BKE_mesh_mselect_active_get(me, ME_VSEL);
|
2019-04-22 09:19:45 +10:00
|
|
|
if (r_index) {
|
2013-06-24 04:41:03 +00:00
|
|
|
*r_index = index;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2013-06-24 04:41:03 +00:00
|
|
|
if (index == -1 || me->dvert == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2020-07-03 15:19:52 +02:00
|
|
|
return me->dvert + index;
|
2013-06-24 04:41:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
MDeformVert *ED_mesh_active_dvert_get_only(Object *ob)
|
|
|
|
{
|
|
|
|
if (ob->type == OB_MESH) {
|
2018-04-05 18:20:27 +02:00
|
|
|
if (ob->mode & OB_MODE_EDIT) {
|
2013-06-24 04:41:03 +00:00
|
|
|
return ED_mesh_active_dvert_get_em(ob, NULL);
|
|
|
|
}
|
2020-07-03 15:19:52 +02:00
|
|
|
return ED_mesh_active_dvert_get_ob(ob, NULL);
|
2013-06-24 04:41:03 +00:00
|
|
|
}
|
2020-07-03 15:19:52 +02:00
|
|
|
return NULL;
|
2013-06-24 04:41:03 +00:00
|
|
|
}
|
2018-04-16 16:27:55 +02:00
|
|
|
|
|
|
|
void EDBM_mesh_stats_multi(struct Object **objects,
|
|
|
|
const uint objects_len,
|
|
|
|
int totelem[3],
|
|
|
|
int totelem_sel[3])
|
|
|
|
{
|
|
|
|
if (totelem) {
|
|
|
|
totelem[0] = 0;
|
|
|
|
totelem[1] = 0;
|
|
|
|
totelem[2] = 0;
|
|
|
|
}
|
|
|
|
if (totelem_sel) {
|
|
|
|
totelem_sel[0] = 0;
|
|
|
|
totelem_sel[1] = 0;
|
|
|
|
totelem_sel[2] = 0;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-04-16 16:27:55 +02:00
|
|
|
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
|
|
|
|
Object *obedit = objects[ob_index];
|
|
|
|
BMEditMesh *em = BKE_editmesh_from_object(obedit);
|
|
|
|
BMesh *bm = em->bm;
|
|
|
|
if (totelem) {
|
|
|
|
totelem[0] += bm->totvert;
|
|
|
|
totelem[1] += bm->totedge;
|
|
|
|
totelem[2] += bm->totface;
|
|
|
|
}
|
|
|
|
if (totelem_sel) {
|
|
|
|
totelem_sel[0] += bm->totvertsel;
|
|
|
|
totelem_sel[1] += bm->totedgesel;
|
|
|
|
totelem_sel[2] += bm->totfacesel;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void EDBM_mesh_elem_index_ensure_multi(Object **objects, const uint objects_len, const char htype)
|
|
|
|
{
|
|
|
|
int elem_offset[4] = {0, 0, 0, 0};
|
|
|
|
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
|
|
|
|
Object *obedit = objects[ob_index];
|
|
|
|
BMEditMesh *em = BKE_editmesh_from_object(obedit);
|
|
|
|
BMesh *bm = em->bm;
|
|
|
|
BM_mesh_elem_index_ensure_ex(bm, htype, elem_offset);
|
|
|
|
}
|
|
|
|
}
|