Files
test2/source/blender/bmesh/intern/bmesh_construct.c
Campbell Barton 7fec23ae0a fix for problem with edge slide where it would stop shapekey modifier from being applied (because of added vertices),
now, instead of making hidden copies of faces, the faces are copied into a temp bmesh.

also remove a hash that was being created and not used (old code).
2013-07-11 04:24:36 +00:00

1071 lines
29 KiB
C

/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* 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,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2007 Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Geoffrey Bantle.
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/bmesh/intern/bmesh_construct.c
* \ingroup bmesh
*
* BM construction functions.
*/
#include "MEM_guardedalloc.h"
#include "BLI_array.h"
#include "BLI_math.h"
#include "BKE_customdata.h"
#include "DNA_meshdata_types.h"
#include "bmesh.h"
#include "intern/bmesh_private.h"
#define SELECT 1
/* prototypes */
static void bm_loop_attrs_copy(BMesh *source_mesh, BMesh *target_mesh,
const BMLoop *source_loop, BMLoop *target_loop);
/**
* \brief Make Quad/Triangle
*
* Creates a new quad or triangle from a list of 3 or 4 vertices.
* If \a no_double is true, then a check is done to see if a face
* with these vertices already exists and returns it instead.
*
* If a pointer to an example face is provided, it's custom data
* and properties will be copied to the new face.
*
* \note The winding of the face is determined by the order
* of the vertices in the vertex array.
*/
BMFace *BM_face_create_quad_tri(BMesh *bm,
BMVert *v1, BMVert *v2, BMVert *v3, BMVert *v4,
const BMFace *example, const bool no_double)
{
BMVert *vtar[4] = {v1, v2, v3, v4};
return BM_face_create_quad_tri_v(bm, vtar, v4 ? 4 : 3, example, no_double);
}
BMFace *BM_face_create_quad_tri_v(BMesh *bm, BMVert **verts, int len, const BMFace *example, const bool no_double)
{
BMFace *f = NULL;
bool is_overlap = false;
/* sanity check - debug mode only */
if (len == 3) {
BLI_assert(verts[0] != verts[1]);
BLI_assert(verts[0] != verts[2]);
BLI_assert(verts[1] != verts[2]);
}
else if (len == 4) {
BLI_assert(verts[0] != verts[1]);
BLI_assert(verts[0] != verts[2]);
BLI_assert(verts[0] != verts[3]);
BLI_assert(verts[1] != verts[2]);
BLI_assert(verts[1] != verts[3]);
BLI_assert(verts[2] != verts[3]);
}
else {
BLI_assert(0);
}
if (no_double) {
/* check if face exists or overlaps */
is_overlap = BM_face_exists(verts, len, &f);
}
/* make new face */
if ((f == NULL) && (!is_overlap)) {
BMEdge *edar[4] = {NULL};
edar[0] = BM_edge_create(bm, verts[0], verts[1], NULL, BM_CREATE_NO_DOUBLE);
edar[1] = BM_edge_create(bm, verts[1], verts[2], NULL, BM_CREATE_NO_DOUBLE);
if (len == 4) {
edar[2] = BM_edge_create(bm, verts[2], verts[3], NULL, BM_CREATE_NO_DOUBLE);
edar[3] = BM_edge_create(bm, verts[3], verts[0], NULL, BM_CREATE_NO_DOUBLE);
}
else {
edar[2] = BM_edge_create(bm, verts[2], verts[0], NULL, BM_CREATE_NO_DOUBLE);
}
f = BM_face_create(bm, verts, edar, len, 0);
if (example && f) {
BM_elem_attrs_copy(bm, bm, example, f);
}
}
return f;
}
/**
* \brief copies face loop data from shared adjacent faces.
* \note when a matching edge is found, both loops of that edge are copied
* this is done since the face may not be completely surrounded by faces,
* this way: a quad with 2 connected quads on either side will still get all 4 loops updated */
void BM_face_copy_shared(BMesh *bm, BMFace *f)
{
BMLoop *l_first;
BMLoop *l_iter;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
BMLoop *l_other = l_iter->radial_next;
if (l_other && l_other != l_iter) {
if (l_other->v == l_iter->v) {
bm_loop_attrs_copy(bm, bm, l_other, l_iter);
bm_loop_attrs_copy(bm, bm, l_other->next, l_iter->next);
}
else {
bm_loop_attrs_copy(bm, bm, l_other->next, l_iter);
bm_loop_attrs_copy(bm, bm, l_other, l_iter->next);
}
/* since we copy both loops of the shared edge, step over the next loop here */
if ((l_iter = l_iter->next) == l_first) {
break;
}
}
} while ((l_iter = l_iter->next) != l_first);
}
/**
* \brief Make NGon
*
* Makes an ngon from an unordered list of edges. \a v1 and \a v2
* must be the verts defining edges[0],
* and define the winding of the new face.
*
* \a edges are not required to be ordered, simply to to form
* a single closed loop as a whole.
*
* \note While this function will work fine when the edges
* are already sorted, if the edges are always going to be sorted,
* #BM_face_create should be considered over this function as it
* avoids some unnecessary work.
*/
BMFace *BM_face_create_ngon(BMesh *bm, BMVert *v1, BMVert *v2, BMEdge **edges, const int len, const int create_flag)
{
BMEdge **edges_sort = BLI_array_alloca(edges_sort, len);
BMVert **verts_sort = BLI_array_alloca(verts_sort, len + 1);
int esort_index = 0;
int vsort_index = 0;
BMFace *f = NULL;
BMEdge *e;
BMVert *v, *ev1, *ev2;
int i;
bool is_v1_found, is_reverse;
/* this code is hideous, yeek. I'll have to think about ways of
* cleaning it up. basically, it now combines the old BM_face_create_ngon
* _and_ the old bmesh_mf functions, so its kindof smashed together
* - joeedh */
BLI_assert(len && v1 && v2 && edges && bm);
/* put edges in correct order */
for (i = 0; i < len; i++) {
BM_ELEM_API_FLAG_ENABLE(edges[i], _FLAG_MF);
}
ev1 = edges[0]->v1;
ev2 = edges[0]->v2;
if (v1 == ev2) {
/* Swapping here improves performance and consistency of face
* structure in the special case that the edges are already in
* the correct order and winding */
SWAP(BMVert *, ev1, ev2);
}
verts_sort[vsort_index++] = ev1;
v = ev2;
e = edges[0];
do {
BMEdge *e2 = e;
/* vertex array is (len + 1) */
if (UNLIKELY(vsort_index > len)) {
goto err; /* vertex in loop twice */
}
verts_sort[vsort_index++] = v;
edges_sort[esort_index++] = e;
/* we only flag the verts to check if they are in the face more than once */
BM_ELEM_API_FLAG_ENABLE(v, _FLAG_MV);
do {
e2 = bmesh_disk_edge_next(e2, v);
if (e2 != e && BM_ELEM_API_FLAG_TEST(e2, _FLAG_MF)) {
v = BM_edge_other_vert(e2, v);
break;
}
} while (e2 != e);
if (UNLIKELY(e2 == e)) {
goto err; /* the edges do not form a closed loop */
}
e = e2;
} while (e != edges[0]);
if (UNLIKELY(esort_index != len)) {
goto err; /* we didn't use all edges in forming the boundary loop */
}
/* ok, edges are in correct order, now ensure they are going
* in the correct direction */
is_v1_found = is_reverse = false;
for (i = 0; i < len; i++) {
if (BM_vert_in_edge(edges_sort[i], v1)) {
/* see if v1 and v2 are in the same edge */
if (BM_vert_in_edge(edges_sort[i], v2)) {
/* if v1 is shared by the *next* edge, then the winding
* is incorrect */
if (BM_vert_in_edge(edges_sort[(i + 1) % len], v1)) {
is_reverse = true;
break;
}
}
is_v1_found = true;
}
if ((is_v1_found == false) && BM_vert_in_edge(edges_sort[i], v2)) {
is_reverse = true;
break;
}
}
if (is_reverse) {
for (i = 0; i < len / 2; i++) {
v = verts_sort[i];
verts_sort[i] = verts_sort[len - i - 1];
verts_sort[len - i - 1] = v;
}
}
for (i = 0; i < len; i++) {
edges_sort[i] = BM_edge_exists(verts_sort[i], verts_sort[(i + 1) % len]);
if (UNLIKELY(edges_sort[i] == NULL)) {
goto err;
}
/* check if vert is in face more than once. if the flag is disabled. we've already visited */
if (UNLIKELY(!BM_ELEM_API_FLAG_TEST(verts_sort[i], _FLAG_MV))) {
goto err;
}
BM_ELEM_API_FLAG_DISABLE(verts_sort[i], _FLAG_MV);
}
f = BM_face_create(bm, verts_sort, edges_sort, len, create_flag);
/* clean up flags */
for (i = 0; i < len; i++) {
BM_ELEM_API_FLAG_DISABLE(edges_sort[i], _FLAG_MF);
}
return f;
err:
for (i = 0; i < len; i++) {
BM_ELEM_API_FLAG_DISABLE(edges[i], _FLAG_MF);
}
for (i = 0; i < vsort_index; i++) {
BM_ELEM_API_FLAG_DISABLE(verts_sort[i], _FLAG_MV);
}
return NULL;
}
/**
* Create an ngon from an array of sorted verts
*
* Special features this has over other functions.
* - Optionally calculate winding based on surrounding edges.
* - Optionally create edges between vertices.
* - Uses verts so no need to find edges (handy when you only have verts)
*/
BMFace *BM_face_create_ngon_verts(BMesh *bm, BMVert **vert_arr, const int len, const int create_flag,
const bool calc_winding, const bool create_edges)
{
BMEdge **edge_arr = BLI_array_alloca(edge_arr, len);
unsigned int winding[2] = {0, 0};
int i, i_prev = len - 1;
BLI_assert(len > 2);
for (i = 0; i < len; i++) {
if (create_edges) {
edge_arr[i] = BM_edge_create(bm, vert_arr[i_prev], vert_arr[i], NULL, BM_CREATE_NO_DOUBLE);
}
else {
edge_arr[i] = BM_edge_exists(vert_arr[i_prev], vert_arr[i]);
if (edge_arr[i] == NULL) {
return NULL;
}
}
if (calc_winding) {
/* the edge may exist already and be attached to a face
* in this case we can find the best winding to use for the new face */
if (edge_arr[i]->l) {
BMVert *test_v1, *test_v2;
/* we want to use the reverse winding to the existing order */
BM_edge_ordered_verts(edge_arr[i], &test_v2, &test_v1);
winding[(vert_arr[i_prev] == test_v2)]++;
}
}
i_prev = i;
}
/* --- */
if (calc_winding) {
if (winding[0] < winding[1]) {
winding[0] = 1;
winding[1] = 0;
}
else {
winding[0] = 0;
winding[1] = 1;
}
}
else {
winding[0] = 0;
winding[1] = 1;
}
/* --- */
/* create the face */
return BM_face_create_ngon(bm, vert_arr[winding[0]], vert_arr[winding[1]], edge_arr, len, create_flag);
}
typedef struct AngleIndexPair {
float angle;
int index;
} AngleIndexPair;
static int angle_index_pair_cmp(const void *e1, const void *e2)
{
const AngleIndexPair *p1 = e1, *p2 = e2;
if (p1->angle > p2->angle) return 1;
else if (p1->angle < p2->angle) return -1;
else return 0;
}
/**
* Makes an NGon from an un-ordered set of verts
*
* assumes...
* - that verts are only once in the list.
* - that the verts have roughly planer bounds
* - that the verts are roughly circular
* there can be concave areas but overlapping folds from the center point will fail.
*
* a brief explanation of the method used
* - find the center point
* - find the normal of the vcloud
* - order the verts around the face based on their angle to the normal vector at the center point.
*
* \note Since this is a vcloud there is no direction.
*/
BMFace *BM_face_create_ngon_vcloud(BMesh *bm, BMVert **vert_arr, int len, const int create_flag)
{
BMFace *f;
float totv_inv = 1.0f / (float)len;
int i = 0;
float cent[3], nor[3];
float *far = NULL, *far_cross = NULL;
float far_vec[3];
float far_cross_vec[3];
float sign_vec[3]; /* work out if we are pos/neg angle */
float far_dist, far_best;
float far_cross_dist, far_cross_best = 0.0f;
AngleIndexPair *vang;
BMVert **vert_arr_map;
/* get the center point and collect vector array since we loop over these a lot */
zero_v3(cent);
for (i = 0; i < len; i++) {
madd_v3_v3fl(cent, vert_arr[i]->co, totv_inv);
}
/* find the far point from cent */
far_best = 0.0f;
for (i = 0; i < len; i++) {
far_dist = len_squared_v3v3(vert_arr[i]->co, cent);
if (far_dist > far_best || far == NULL) {
far = vert_arr[i]->co;
far_best = far_dist;
}
}
sub_v3_v3v3(far_vec, far, cent);
// far_dist = len_v3(far_vec); /* real dist */ /* UNUSED */
/* --- */
/* find a point 90deg about to compare with */
far_cross_best = 0.0f;
for (i = 0; i < len; i++) {
if (far == vert_arr[i]->co) {
continue;
}
sub_v3_v3v3(far_cross_vec, vert_arr[i]->co, cent);
far_cross_dist = normalize_v3(far_cross_vec);
/* more of a weight then a distance */
far_cross_dist = (/* first we want to have a value close to zero mapped to 1 */
1.0f - fabsf(dot_v3v3(far_vec, far_cross_vec)) *
/* second we multiply by the distance
* so points close to the center are not preferred */
far_cross_dist);
if (far_cross_dist > far_cross_best || far_cross == NULL) {
far_cross = vert_arr[i]->co;
far_cross_best = far_cross_dist;
}
}
sub_v3_v3v3(far_cross_vec, far_cross, cent);
/* --- */
/* now we have 2 vectors we can have a cross product */
cross_v3_v3v3(nor, far_vec, far_cross_vec);
normalize_v3(nor);
cross_v3_v3v3(sign_vec, far_vec, nor); /* this vector should match 'far_cross_vec' closely */
/* --- */
/* now calculate every points angle around the normal (signed) */
vang = MEM_mallocN(sizeof(AngleIndexPair) * len, __func__);
for (i = 0; i < len; i++) {
float co[3];
float proj_vec[3];
float angle;
/* center relative vec */
sub_v3_v3v3(co, vert_arr[i]->co, cent);
/* align to plane */
project_v3_v3v3(proj_vec, co, nor);
sub_v3_v3(co, proj_vec);
/* now 'co' is valid - we can compare its angle against the far vec */
angle = angle_v3v3(far_vec, co);
if (dot_v3v3(co, sign_vec) < 0.0f) {
angle = -angle;
}
vang[i].angle = angle;
vang[i].index = i;
}
/* sort by angle and magic! - we have our ngon */
qsort(vang, len, sizeof(AngleIndexPair), angle_index_pair_cmp);
/* --- */
/* create edges and find the winding (if faces are attached to any existing edges) */
vert_arr_map = MEM_mallocN(sizeof(BMVert **) * len, __func__);
for (i = 0; i < len; i++) {
vert_arr_map[i] = vert_arr[vang[i].index];
}
MEM_freeN(vang);
f = BM_face_create_ngon_verts(bm, vert_arr_map, len, create_flag, true, true);
MEM_freeN(vert_arr_map);
return f;
}
/**
* Called by operators to remove elements that they have marked for
* removal.
*/
void BMO_remove_tagged_faces(BMesh *bm, const short oflag)
{
BMFace *f;
BMIter iter;
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
if (BMO_elem_flag_test(bm, f, oflag)) {
BM_face_kill(bm, f);
}
}
}
void BMO_remove_tagged_edges(BMesh *bm, const short oflag)
{
BMEdge *e;
BMIter iter;
BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
if (BMO_elem_flag_test(bm, e, oflag)) {
BM_edge_kill(bm, e);
}
}
}
void BMO_remove_tagged_verts(BMesh *bm, const short oflag)
{
BMVert *v;
BMIter iter;
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
if (BMO_elem_flag_test(bm, v, oflag)) {
BM_vert_kill(bm, v);
}
}
}
/**
* you need to make remove tagged verts/edges/faces
* api functions that take a filter callback.....
* and this new filter type will be for opstack flags.
* This is because the BM_remove_taggedXXX functions bypass iterator API.
* - Ops don't care about 'UI' considerations like selection state, hide state, etc.
* If you want to work on unhidden selections for instance,
* copy output from a 'select context' operator to another operator....
*/
static void bmo_remove_tagged_context_verts(BMesh *bm, const short oflag)
{
BMVert *v;
BMEdge *e;
BMFace *f;
BMIter iter;
BMIter itersub;
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
if (BMO_elem_flag_test(bm, v, oflag)) {
/* Visit edge */
BM_ITER_ELEM (e, &itersub, v, BM_EDGES_OF_VERT) {
BMO_elem_flag_enable(bm, e, oflag);
}
/* Visit face */
BM_ITER_ELEM (f, &itersub, v, BM_FACES_OF_VERT) {
BMO_elem_flag_enable(bm, f, oflag);
}
}
}
BMO_remove_tagged_faces(bm, oflag);
BMO_remove_tagged_edges(bm, oflag);
BMO_remove_tagged_verts(bm, oflag);
}
static void bmo_remove_tagged_context_edges(BMesh *bm, const short oflag)
{
BMEdge *e;
BMFace *f;
BMIter iter;
BMIter itersub;
BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
if (BMO_elem_flag_test(bm, e, oflag)) {
BM_ITER_ELEM (f, &itersub, e, BM_FACES_OF_EDGE) {
BMO_elem_flag_enable(bm, f, oflag);
}
}
}
BMO_remove_tagged_faces(bm, oflag);
BMO_remove_tagged_edges(bm, oflag);
}
#define DEL_WIREVERT (1 << 10)
/**
* \warning oflag applies to different types in some contexts,
* not just the type being removed.
*
* \warning take care, uses operator flag DEL_WIREVERT
*/
void BMO_remove_tagged_context(BMesh *bm, const short oflag, const int type)
{
BMVert *v;
BMEdge *e;
BMFace *f;
BMIter viter;
BMIter eiter;
BMIter fiter;
switch (type) {
case DEL_VERTS:
{
bmo_remove_tagged_context_verts(bm, oflag);
break;
}
case DEL_EDGES:
{
/* flush down to vert */
BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
if (BMO_elem_flag_test(bm, e, oflag)) {
BMO_elem_flag_enable(bm, e->v1, oflag);
BMO_elem_flag_enable(bm, e->v2, oflag);
}
}
bmo_remove_tagged_context_edges(bm, oflag);
/* remove loose vertice */
BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
if (BMO_elem_flag_test(bm, v, oflag) && (!(v->e)))
BMO_elem_flag_enable(bm, v, DEL_WIREVERT);
}
BMO_remove_tagged_verts(bm, DEL_WIREVERT);
break;
}
case DEL_EDGESFACES:
{
bmo_remove_tagged_context_edges(bm, oflag);
break;
}
case DEL_ONLYFACES:
{
BMO_remove_tagged_faces(bm, oflag);
break;
}
case DEL_ONLYTAGGED:
{
BMO_remove_tagged_faces(bm, oflag);
BMO_remove_tagged_edges(bm, oflag);
BMO_remove_tagged_verts(bm, oflag);
break;
}
case DEL_FACES:
{
/* go through and mark all edges and all verts of all faces for delete */
BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
if (BMO_elem_flag_test(bm, f, oflag)) {
for (e = BM_iter_new(&eiter, bm, BM_EDGES_OF_FACE, f); e; e = BM_iter_step(&eiter))
BMO_elem_flag_enable(bm, e, oflag);
for (v = BM_iter_new(&viter, bm, BM_VERTS_OF_FACE, f); v; v = BM_iter_step(&viter))
BMO_elem_flag_enable(bm, v, oflag);
}
}
/* now go through and mark all remaining faces all edges for keeping */
BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
if (!BMO_elem_flag_test(bm, f, oflag)) {
for (e = BM_iter_new(&eiter, bm, BM_EDGES_OF_FACE, f); e; e = BM_iter_step(&eiter)) {
BMO_elem_flag_disable(bm, e, oflag);
}
for (v = BM_iter_new(&viter, bm, BM_VERTS_OF_FACE, f); v; v = BM_iter_step(&viter)) {
BMO_elem_flag_disable(bm, v, oflag);
}
}
}
/* also mark all the vertices of remaining edges for keeping */
BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
if (!BMO_elem_flag_test(bm, e, oflag)) {
BMO_elem_flag_disable(bm, e->v1, oflag);
BMO_elem_flag_disable(bm, e->v2, oflag);
}
}
/* now delete marked face */
BMO_remove_tagged_faces(bm, oflag);
/* delete marked edge */
BMO_remove_tagged_edges(bm, oflag);
/* remove loose vertice */
BMO_remove_tagged_verts(bm, oflag);
break;
}
case DEL_ALL:
{
/* does this option even belong in here? */
BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
BMO_elem_flag_enable(bm, f, oflag);
}
BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
BMO_elem_flag_enable(bm, e, oflag);
}
BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
BMO_elem_flag_enable(bm, v, oflag);
}
BMO_remove_tagged_faces(bm, oflag);
BMO_remove_tagged_edges(bm, oflag);
BMO_remove_tagged_verts(bm, oflag);
break;
}
}
}
/*************************************************************/
static void bm_vert_attrs_copy(BMesh *source_mesh, BMesh *target_mesh,
const BMVert *source_vertex, BMVert *target_vertex)
{
if ((source_mesh == target_mesh) && (source_vertex == target_vertex)) {
BLI_assert(!"BMVert: source and targer match");
return;
}
copy_v3_v3(target_vertex->no, source_vertex->no);
CustomData_bmesh_free_block_data(&target_mesh->vdata, &target_vertex->head.data);
CustomData_bmesh_copy_data(&source_mesh->vdata, &target_mesh->vdata,
source_vertex->head.data, &target_vertex->head.data);
}
static void bm_edge_attrs_copy(BMesh *source_mesh, BMesh *target_mesh,
const BMEdge *source_edge, BMEdge *target_edge)
{
if ((source_mesh == target_mesh) && (source_edge == target_edge)) {
BLI_assert(!"BMEdge: source and targer match");
return;
}
CustomData_bmesh_free_block_data(&target_mesh->edata, &target_edge->head.data);
CustomData_bmesh_copy_data(&source_mesh->edata, &target_mesh->edata,
source_edge->head.data, &target_edge->head.data);
}
static void bm_loop_attrs_copy(BMesh *source_mesh, BMesh *target_mesh,
const BMLoop *source_loop, BMLoop *target_loop)
{
if ((source_mesh == target_mesh) && (source_loop == target_loop)) {
BLI_assert(!"BMLoop: source and targer match");
return;
}
CustomData_bmesh_free_block_data(&target_mesh->ldata, &target_loop->head.data);
CustomData_bmesh_copy_data(&source_mesh->ldata, &target_mesh->ldata,
source_loop->head.data, &target_loop->head.data);
}
static void bm_face_attrs_copy(BMesh *source_mesh, BMesh *target_mesh,
const BMFace *source_face, BMFace *target_face)
{
if ((source_mesh == target_mesh) && (source_face == target_face)) {
BLI_assert(!"BMFace: source and targer match");
return;
}
copy_v3_v3(target_face->no, source_face->no);
CustomData_bmesh_free_block_data(&target_mesh->pdata, &target_face->head.data);
CustomData_bmesh_copy_data(&source_mesh->pdata, &target_mesh->pdata,
source_face->head.data, &target_face->head.data);
target_face->mat_nr = source_face->mat_nr;
}
/* BMESH_TODO: Special handling for hide flags? */
/**
* Copies attributes, e.g. customdata, header flags, etc, from one element
* to another of the same type.
*/
void BM_elem_attrs_copy(BMesh *source_mesh, BMesh *target_mesh, const void *source, void *target)
{
const BMHeader *sheader = source;
BMHeader *theader = target;
BLI_assert(sheader->htype == theader->htype);
if (sheader->htype != theader->htype) {
BLI_assert(!"type mismatch");
return;
}
/* First we copy select */
if (BM_elem_flag_test((BMElem *)sheader, BM_ELEM_SELECT)) {
BM_elem_select_set(target_mesh, (BMElem *)target, true);
}
/* Now we copy flags */
theader->hflag = sheader->hflag;
/* Copy specific attributes */
switch (theader->htype) {
case BM_VERT:
bm_vert_attrs_copy(source_mesh, target_mesh, (const BMVert *)source, (BMVert *)target);
break;
case BM_EDGE:
bm_edge_attrs_copy(source_mesh, target_mesh, (const BMEdge *)source, (BMEdge *)target);
break;
case BM_LOOP:
bm_loop_attrs_copy(source_mesh, target_mesh, (const BMLoop *)source, (BMLoop *)target);
break;
case BM_FACE:
bm_face_attrs_copy(source_mesh, target_mesh, (const BMFace *)source, (BMFace *)target);
break;
default:
BLI_assert(0);
}
}
/* helper function for 'BM_mesh_copy' */
static BMFace *bm_mesh_copy_new_face(BMesh *bm_new, BMesh *bm_old,
BMVert **vtable, BMEdge **etable,
BMFace *f)
{
BMLoop **loops = BLI_array_alloca(loops, f->len);
BMVert **verts = BLI_array_alloca(verts, f->len);
BMEdge **edges = BLI_array_alloca(edges, f->len);
BMFace *f_new;
BMLoop *l_iter, *l_first;
int j;
j = 0;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
loops[j] = l_iter;
verts[j] = vtable[BM_elem_index_get(l_iter->v)];
edges[j] = etable[BM_elem_index_get(l_iter->e)];
j++;
} while ((l_iter = l_iter->next) != l_first);
f_new = BM_face_create(bm_new, verts, edges, f->len, BM_CREATE_SKIP_CD);
if (UNLIKELY(f_new == NULL)) {
return NULL;
}
/* use totface in case adding some faces fails */
BM_elem_index_set(f_new, (bm_new->totface - 1)); /* set_inline */
BM_elem_attrs_copy(bm_old, bm_new, f, f_new);
j = 0;
l_iter = l_first = BM_FACE_FIRST_LOOP(f_new);
do {
BM_elem_attrs_copy(bm_old, bm_new, loops[j], l_iter);
j++;
} while ((l_iter = l_iter->next) != l_first);
return f_new;
}
void BM_mesh_copy_init_customdata(BMesh *bm_dst, BMesh *bm_src, const BMAllocTemplate *allocsize)
{
if (allocsize == NULL) {
allocsize = &bm_mesh_allocsize_default;
}
CustomData_copy(&bm_src->vdata, &bm_dst->vdata, CD_MASK_BMESH, CD_CALLOC, 0);
CustomData_copy(&bm_src->edata, &bm_dst->edata, CD_MASK_BMESH, CD_CALLOC, 0);
CustomData_copy(&bm_src->ldata, &bm_dst->ldata, CD_MASK_BMESH, CD_CALLOC, 0);
CustomData_copy(&bm_src->pdata, &bm_dst->pdata, CD_MASK_BMESH, CD_CALLOC, 0);
CustomData_bmesh_init_pool(&bm_dst->vdata, allocsize->totvert, BM_VERT);
CustomData_bmesh_init_pool(&bm_dst->edata, allocsize->totedge, BM_EDGE);
CustomData_bmesh_init_pool(&bm_dst->ldata, allocsize->totloop, BM_LOOP);
CustomData_bmesh_init_pool(&bm_dst->pdata, allocsize->totface, BM_FACE);
}
BMesh *BM_mesh_copy(BMesh *bm_old)
{
BMesh *bm_new;
BMVert *v, *v_new, **vtable = NULL;
BMEdge *e, *e_new, **etable = NULL;
BMFace *f, *f_new, **ftable = NULL;
BMElem **eletable;
BMEditSelection *ese;
BMIter iter;
int i;
const BMAllocTemplate allocsize = {bm_old->totvert,
bm_old->totedge,
bm_old->totloop,
bm_old->totface};
/* allocate a bmesh */
bm_new = BM_mesh_create(&allocsize);
BM_mesh_copy_init_customdata(bm_new, bm_old, &allocsize);
vtable = MEM_mallocN(sizeof(BMVert *) * bm_old->totvert, "BM_mesh_copy vtable");
etable = MEM_mallocN(sizeof(BMEdge *) * bm_old->totedge, "BM_mesh_copy etable");
ftable = MEM_mallocN(sizeof(BMFace *) * bm_old->totface, "BM_mesh_copy ftable");
BM_ITER_MESH_INDEX (v, &iter, bm_old, BM_VERTS_OF_MESH, i) {
/* copy between meshes so cant use 'example' argument */
v_new = BM_vert_create(bm_new, v->co, NULL, BM_CREATE_SKIP_CD);
BM_elem_attrs_copy(bm_old, bm_new, v, v_new);
vtable[i] = v_new;
BM_elem_index_set(v, i); /* set_inline */
BM_elem_index_set(v_new, i); /* set_inline */
}
bm_old->elem_index_dirty &= ~BM_VERT;
bm_new->elem_index_dirty &= ~BM_VERT;
/* safety check */
BLI_assert(i == bm_old->totvert);
BM_ITER_MESH_INDEX (e, &iter, bm_old, BM_EDGES_OF_MESH, i) {
e_new = BM_edge_create(bm_new,
vtable[BM_elem_index_get(e->v1)],
vtable[BM_elem_index_get(e->v2)],
e, BM_CREATE_SKIP_CD);
BM_elem_attrs_copy(bm_old, bm_new, e, e_new);
etable[i] = e_new;
BM_elem_index_set(e, i); /* set_inline */
BM_elem_index_set(e_new, i); /* set_inline */
}
bm_old->elem_index_dirty &= ~BM_EDGE;
bm_new->elem_index_dirty &= ~BM_EDGE;
/* safety check */
BLI_assert(i == bm_old->totedge);
BM_ITER_MESH_INDEX (f, &iter, bm_old, BM_FACES_OF_MESH, i) {
BM_elem_index_set(f, i); /* set_inline */
f_new = bm_mesh_copy_new_face(bm_new, bm_old, vtable, etable, f);
ftable[i] = f_new;
if (f == bm_old->act_face) bm_new->act_face = f_new;
}
bm_old->elem_index_dirty &= ~BM_FACE;
bm_new->elem_index_dirty &= ~BM_FACE;
/* safety check */
BLI_assert(i == bm_old->totface);
/* copy over edit selection history */
for (ese = bm_old->selected.first; ese; ese = ese->next) {
BMElem *ele = NULL;
switch (ese->htype) {
case BM_VERT:
eletable = (BMElem **)vtable;
break;
case BM_EDGE:
eletable = (BMElem **)etable;
break;
case BM_FACE:
eletable = (BMElem **)ftable;
break;
default:
eletable = NULL;
break;
}
if (eletable) {
ele = eletable[BM_elem_index_get(ese->ele)];
if (ele) {
BM_select_history_store(bm_new, ele);
}
}
}
MEM_freeN(etable);
MEM_freeN(vtable);
MEM_freeN(ftable);
return bm_new;
}
/* ME -> BM */
char BM_vert_flag_from_mflag(const char meflag)
{
return ( ((meflag & SELECT) ? BM_ELEM_SELECT : 0) |
((meflag & ME_HIDE) ? BM_ELEM_HIDDEN : 0)
);
}
char BM_edge_flag_from_mflag(const short meflag)
{
return ( ((meflag & SELECT) ? BM_ELEM_SELECT : 0) |
((meflag & ME_SEAM) ? BM_ELEM_SEAM : 0) |
((meflag & ME_EDGEDRAW) ? BM_ELEM_DRAW : 0) |
((meflag & ME_SHARP) == 0 ? BM_ELEM_SMOOTH : 0) | /* invert */
((meflag & ME_HIDE) ? BM_ELEM_HIDDEN : 0)
);
}
char BM_face_flag_from_mflag(const char meflag)
{
return ( ((meflag & ME_FACE_SEL) ? BM_ELEM_SELECT : 0) |
((meflag & ME_SMOOTH) ? BM_ELEM_SMOOTH : 0) |
((meflag & ME_HIDE) ? BM_ELEM_HIDDEN : 0)
);
}
/* BM -> ME */
char BM_vert_flag_to_mflag(BMVert *eve)
{
const char hflag = eve->head.hflag;
return ( ((hflag & BM_ELEM_SELECT) ? SELECT : 0) |
((hflag & BM_ELEM_HIDDEN) ? ME_HIDE : 0)
);
}
short BM_edge_flag_to_mflag(BMEdge *eed)
{
const char hflag = eed->head.hflag;
return ( ((hflag & BM_ELEM_SELECT) ? SELECT : 0) |
((hflag & BM_ELEM_SEAM) ? ME_SEAM : 0) |
((hflag & BM_ELEM_DRAW) ? ME_EDGEDRAW : 0) |
((hflag & BM_ELEM_SMOOTH) == 0 ? ME_SHARP : 0) |
((hflag & BM_ELEM_HIDDEN) ? ME_HIDE : 0) |
((BM_edge_is_wire(eed)) ? ME_LOOSEEDGE : 0) | /* not typical */
ME_EDGERENDER
);
}
char BM_face_flag_to_mflag(BMFace *efa)
{
const char hflag = efa->head.hflag;
return ( ((hflag & BM_ELEM_SELECT) ? ME_FACE_SEL : 0) |
((hflag & BM_ELEM_SMOOTH) ? ME_SMOOTH : 0) |
((hflag & BM_ELEM_HIDDEN) ? ME_HIDE : 0)
);
}