/**
 * \brief copies face loop data from shared adjacent faces.
 *
 * \param filter_fn: A function that filters the source loops before copying (don't always want to copy all)
 *
 * \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,
        BMLoopFilterFunc filter_fn, void *user_data)
{
	BMLoop *l_first;
	BMLoop *l_iter;

#ifdef DEBUG
	l_iter = l_first = BM_FACE_FIRST_LOOP(f);
	do {
		BLI_assert(BM_ELEM_API_FLAG_TEST(l_iter, _FLAG_OVERLAP) == 0);
	} while ((l_iter = l_iter->next) != l_first);
#endif

	l_iter = l_first = BM_FACE_FIRST_LOOP(f);
	do {
		BMLoop *l_other = l_iter->radial_next;

		if (l_other && l_other != l_iter) {
			BMLoop *l_src[2];
			BMLoop *l_dst[2] = {l_iter, l_iter->next};
			uint j;

			if (l_other->v == l_iter->v) {
				l_src[0] = l_other;
				l_src[1] = l_other->next;
			}
			else {
				l_src[0] = l_other->next;
				l_src[1] = l_other;
			}

			for (j = 0; j < 2; j++) {
				BLI_assert(l_dst[j]->v == l_src[j]->v);
				if (BM_ELEM_API_FLAG_TEST(l_dst[j], _FLAG_OVERLAP) == 0) {
					if ((filter_fn == NULL) || filter_fn(l_src[j], user_data)) {
						bm_loop_attrs_copy(bm, bm, l_src[j], l_dst[j], 0x0);
						BM_ELEM_API_FLAG_ENABLE(l_dst[j], _FLAG_OVERLAP);
					}
				}
			}
		}
	} while ((l_iter = l_iter->next) != l_first);


	l_iter = l_first = BM_FACE_FIRST_LOOP(f);
	do {
		BM_ELEM_API_FLAG_DISABLE(l_iter, _FLAG_OVERLAP);
	} while ((l_iter = l_iter->next) != l_first);
}
Exemple #2
0
/**
 * Given an array of faces, recalculate their normals.
 * this functions assumes all faces in the array are connected by edges.
 *
 * \param bm:
 * \param faces: Array of connected faces.
 * \param faces_len: Length of \a faces
 * \param oflag: Flag to check before doing the actual face flipping.
 */
static void bmo_recalc_face_normals_array(BMesh *bm,
                                          BMFace **faces,
                                          const int faces_len,
                                          const short oflag)
{
  int i, f_start_index;
  const short oflag_flip = oflag | FACE_FLIP;
  bool is_flip;

  BMFace *f;

  BLI_LINKSTACK_DECLARE(fstack, BMFace *);

  f_start_index = recalc_face_normals_find_index(bm, faces, faces_len, &is_flip);

  if (is_flip) {
    BMO_face_flag_enable(bm, faces[f_start_index], FACE_FLIP);
  }

  /* now that we've found our starting face, make all connected faces
   * have the same winding.  this is done recursively, using a manual
   * stack (if we use simple function recursion, we'd end up overloading
   * the stack on large meshes). */
  BLI_LINKSTACK_INIT(fstack);

  BLI_LINKSTACK_PUSH(fstack, faces[f_start_index]);
  BMO_face_flag_enable(bm, faces[f_start_index], FACE_TEMP);

  while ((f = BLI_LINKSTACK_POP(fstack))) {
    const bool flip_state = BMO_face_flag_test_bool(bm, f, FACE_FLIP);
    BMLoop *l_iter, *l_first;

    l_iter = l_first = BM_FACE_FIRST_LOOP(f);
    do {
      BMLoop *l_other = l_iter->radial_next;

      if ((l_other != l_iter) && bmo_recalc_normal_loop_filter_cb(l_iter, NULL)) {
        if (!BMO_face_flag_test(bm, l_other->f, FACE_TEMP)) {
          BMO_face_flag_enable(bm, l_other->f, FACE_TEMP);
          BMO_face_flag_set(bm, l_other->f, FACE_FLIP, (l_other->v == l_iter->v) != flip_state);
          BLI_LINKSTACK_PUSH(fstack, l_other->f);
        }
      }
    } while ((l_iter = l_iter->next) != l_first);
  }

  BLI_LINKSTACK_FREE(fstack);

  /* apply flipping to oflag'd faces */
  for (i = 0; i < faces_len; i++) {
    if (BMO_face_flag_test(bm, faces[i], oflag_flip) == oflag_flip) {
      BM_face_normal_flip(bm, faces[i]);
    }
    BMO_face_flag_disable(bm, faces[i], FACE_TEMP);
  }
}
/* 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, NULL, 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_ex(bm_old, bm_new, f, f_new, 0xff, 0x0);
	f_new->head.hflag = f->head.hflag;  /* low level! don't do this for normal api use */

	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;
}
Exemple #4
0
/* Return TRUE if all vertices in the face are visible, FALSE otherwise */
int paint_is_bmesh_face_hidden(BMFace *f)
{
	BMLoop *l_iter;
	BMLoop *l_first;

	l_iter = l_first = BM_FACE_FIRST_LOOP(f);
	do {
		if (BM_elem_flag_test(l_iter->v, BM_ELEM_HIDDEN)) {
			return true;
		}
	} while ((l_iter = l_iter->next) != l_first);

	return false;
}
Exemple #5
0
static bool bm_face_is_snap_target(BMFace *f, void *UNUSED(user_data))
{
	if (BM_elem_flag_test(f, BM_ELEM_SELECT | BM_ELEM_HIDDEN)) {
		return false;
	}

	BMLoop *l_iter, *l_first;
	l_iter = l_first = BM_FACE_FIRST_LOOP(f);
	do {
		if (BM_elem_flag_test(l_iter->v, BM_ELEM_SELECT)) {
			return false;
		}
	} while ((l_iter = l_iter->next) != l_first);

	return true;
}
/*****loop cycle functions, e.g. loops surrounding a face**** */
bool bmesh_loop_validate(BMFace *f)
{
	int i;
	int len = f->len;
	BMLoop *l_iter, *l_first;

	l_first = BM_FACE_FIRST_LOOP(f);

	if (l_first == NULL) {
		return false;
	}

	/* Validate that the face loop cycle is the length specified by f->len */
	for (i = 1, l_iter = l_first->next; i < len; i++, l_iter = l_iter->next) {
		if ((l_iter->f != f) ||
		    (l_iter == l_first))
		{
			return false;
		}
	}
	if (l_iter != l_first) {
		return false;
	}

	/* Validate the loop->prev links also form a cycle of length f->len */
	for (i = 1, l_iter = l_first->prev; i < len; i++, l_iter = l_iter->prev) {
		if (l_iter == l_first) {
			return false;
		}
	}
	if (l_iter != l_first) {
		return false;
	}

	return true;
}
/**
 * \brief Mesh -> BMesh
 * \param bm: The mesh to write into, while this is typically a newly created BMesh,
 * merging into existing data is supported.
 * Note the custom-data layout isn't used.
 * If more comprehensive merging is needed we should move this into a separate function
 * since this should be kept fast for edit-mode switching and storing undo steps.
 *
 * \warning This function doesn't calculate face normals.
 */
void BM_mesh_bm_from_me(
        BMesh *bm, Mesh *me,
        const struct BMeshFromMeshParams *params)
{
	const bool is_new =
	        !(bm->totvert ||
	          (bm->vdata.totlayer || bm->edata.totlayer || bm->pdata.totlayer || bm->ldata.totlayer));
	MVert *mvert;
	MEdge *medge;
	MLoop *mloop;
	MPoly *mp;
	KeyBlock *actkey, *block;
	BMVert *v, **vtable = NULL;
	BMEdge *e, **etable = NULL;
	BMFace *f, **ftable = NULL;
	float (*keyco)[3] = NULL;
	int totuv, totloops, i;

	if (!me || !me->totvert) {
		if (me && is_new) { /*no verts? still copy customdata layout*/
			CustomData_copy(&me->vdata, &bm->vdata, CD_MASK_BMESH, CD_ASSIGN, 0);
			CustomData_copy(&me->edata, &bm->edata, CD_MASK_BMESH, CD_ASSIGN, 0);
			CustomData_copy(&me->ldata, &bm->ldata, CD_MASK_BMESH, CD_ASSIGN, 0);
			CustomData_copy(&me->pdata, &bm->pdata, CD_MASK_BMESH, CD_ASSIGN, 0);

			CustomData_bmesh_init_pool(&bm->vdata, me->totvert, BM_VERT);
			CustomData_bmesh_init_pool(&bm->edata, me->totedge, BM_EDGE);
			CustomData_bmesh_init_pool(&bm->ldata, me->totloop, BM_LOOP);
			CustomData_bmesh_init_pool(&bm->pdata, me->totpoly, BM_FACE);
		}
		return; /* sanity check */
	}

	if (is_new) {
		CustomData_copy(&me->vdata, &bm->vdata, CD_MASK_BMESH, CD_CALLOC, 0);
		CustomData_copy(&me->edata, &bm->edata, CD_MASK_BMESH, CD_CALLOC, 0);
		CustomData_copy(&me->ldata, &bm->ldata, CD_MASK_BMESH, CD_CALLOC, 0);
		CustomData_copy(&me->pdata, &bm->pdata, CD_MASK_BMESH, CD_CALLOC, 0);

		/* make sure uv layer names are consisten */
		totuv = CustomData_number_of_layers(&bm->pdata, CD_MTEXPOLY);
		for (i = 0; i < totuv; i++) {
			int li = CustomData_get_layer_index_n(&bm->pdata, CD_MTEXPOLY, i);
			CustomData_set_layer_name(&bm->ldata, CD_MLOOPUV, i, bm->pdata.layers[li].name);
		}
	}

	/* -------------------------------------------------------------------- */
	/* Shape Key */
	int tot_shape_keys = me->key ? BLI_listbase_count(&me->key->block) : 0;
	if (is_new == false) {
		tot_shape_keys = min_ii(tot_shape_keys, CustomData_number_of_layers(&bm->vdata, CD_SHAPEKEY));
	}
	const float (**shape_key_table)[3] = tot_shape_keys ? BLI_array_alloca(shape_key_table, tot_shape_keys) : NULL;

	if ((params->active_shapekey != 0) && (me->key != NULL)) {
		actkey = BLI_findlink(&me->key->block, params->active_shapekey - 1);
	}
	else {
		actkey = NULL;
	}

	if (is_new) {
		if (tot_shape_keys || params->add_key_index) {
			CustomData_add_layer(&bm->vdata, CD_SHAPE_KEYINDEX, CD_ASSIGN, NULL, 0);
		}
	}

	if (tot_shape_keys) {
		if (is_new) {
			/* check if we need to generate unique ids for the shapekeys.
			 * this also exists in the file reading code, but is here for
			 * a sanity check */
			if (!me->key->uidgen) {
				fprintf(stderr,
				        "%s had to generate shape key uid's in a situation we shouldn't need to! "
				        "(bmesh internal error)\n",
				        __func__);

				me->key->uidgen = 1;
				for (block = me->key->block.first; block; block = block->next) {
					block->uid = me->key->uidgen++;
				}
			}
		}

		if (actkey && actkey->totelem == me->totvert) {
			keyco = params->use_shapekey ? actkey->data : NULL;
			if (is_new) {
				bm->shapenr = params->active_shapekey;
			}
		}

		for (i = 0, block = me->key->block.first; i < tot_shape_keys; block = block->next, i++) {
			if (is_new) {
				CustomData_add_layer_named(&bm->vdata, CD_SHAPEKEY,
				                           CD_ASSIGN, NULL, 0, block->name);
				int j = CustomData_get_layer_index_n(&bm->vdata, CD_SHAPEKEY, i);
				bm->vdata.layers[j].uid = block->uid;
			}
			shape_key_table[i] = (const float (*)[3])block->data;
		}
	}

	if (is_new) {
		CustomData_bmesh_init_pool(&bm->vdata, me->totvert, BM_VERT);
		CustomData_bmesh_init_pool(&bm->edata, me->totedge, BM_EDGE);
		CustomData_bmesh_init_pool(&bm->ldata, me->totloop, BM_LOOP);
		CustomData_bmesh_init_pool(&bm->pdata, me->totpoly, BM_FACE);

		BM_mesh_cd_flag_apply(bm, me->cd_flag);
	}

	const int cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT);
	const int cd_edge_bweight_offset = CustomData_get_offset(&bm->edata, CD_BWEIGHT);
	const int cd_edge_crease_offset  = CustomData_get_offset(&bm->edata, CD_CREASE);
	const int cd_shape_key_offset = me->key ? CustomData_get_offset(&bm->vdata, CD_SHAPEKEY) : -1;
	const int cd_shape_keyindex_offset = is_new && (tot_shape_keys || params->add_key_index) ?
	          CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX) : -1;

	vtable = MEM_mallocN(sizeof(BMVert **) * me->totvert, __func__);

	for (i = 0, mvert = me->mvert; i < me->totvert; i++, mvert++) {
		v = vtable[i] = BM_vert_create(bm, keyco ? keyco[i] : mvert->co, NULL, BM_CREATE_SKIP_CD);
		BM_elem_index_set(v, i); /* set_ok */

		/* transfer flag */
		v->head.hflag = BM_vert_flag_from_mflag(mvert->flag & ~SELECT);

		/* this is necessary for selection counts to work properly */
		if (mvert->flag & SELECT) {
			BM_vert_select_set(bm, v, true);
		}

		normal_short_to_float_v3(v->no, mvert->no);

		/* Copy Custom Data */
		CustomData_to_bmesh_block(&me->vdata, &bm->vdata, i, &v->head.data, true);

		if (cd_vert_bweight_offset != -1) BM_ELEM_CD_SET_FLOAT(v, cd_vert_bweight_offset, (float)mvert->bweight / 255.0f);

		/* set shape key original index */
		if (cd_shape_keyindex_offset != -1) BM_ELEM_CD_SET_INT(v, cd_shape_keyindex_offset, i);

		/* set shapekey data */
		if (tot_shape_keys) {
			float (*co_dst)[3] = BM_ELEM_CD_GET_VOID_P(v, cd_shape_key_offset);
			for (int j = 0; j < tot_shape_keys; j++, co_dst++) {
				copy_v3_v3(*co_dst, shape_key_table[j][i]);
			}
		}
	}
	if (is_new) {
		bm->elem_index_dirty &= ~BM_VERT; /* added in order, clear dirty flag */
	}

	etable = MEM_mallocN(sizeof(BMEdge **) * me->totedge, __func__);

	medge = me->medge;
	for (i = 0; i < me->totedge; i++, medge++) {
		e = etable[i] = BM_edge_create(bm, vtable[medge->v1], vtable[medge->v2], NULL, BM_CREATE_SKIP_CD);
		BM_elem_index_set(e, i); /* set_ok */

		/* transfer flags */
		e->head.hflag = BM_edge_flag_from_mflag(medge->flag & ~SELECT);

		/* this is necessary for selection counts to work properly */
		if (medge->flag & SELECT) {
			BM_edge_select_set(bm, e, true);
		}

		/* Copy Custom Data */
		CustomData_to_bmesh_block(&me->edata, &bm->edata, i, &e->head.data, true);

		if (cd_edge_bweight_offset != -1) BM_ELEM_CD_SET_FLOAT(e, cd_edge_bweight_offset, (float)medge->bweight / 255.0f);
		if (cd_edge_crease_offset  != -1) BM_ELEM_CD_SET_FLOAT(e, cd_edge_crease_offset,  (float)medge->crease  / 255.0f);

	}
	if (is_new) {
		bm->elem_index_dirty &= ~BM_EDGE; /* added in order, clear dirty flag */
	}

	/* only needed for selection. */
	if (me->mselect && me->totselect != 0) {
		ftable = MEM_mallocN(sizeof(BMFace **) * me->totpoly, __func__);
	}

	mloop = me->mloop;
	mp = me->mpoly;
	for (i = 0, totloops = 0; i < me->totpoly; i++, mp++) {
		BMLoop *l_iter;
		BMLoop *l_first;

		f = bm_face_create_from_mpoly(mp, mloop + mp->loopstart,
		                              bm, vtable, etable);
		if (ftable != NULL) {
			ftable[i] = f;
		}

		if (UNLIKELY(f == NULL)) {
			printf("%s: Warning! Bad face in mesh"
			       " \"%s\" at index %d!, skipping\n",
			       __func__, me->id.name + 2, i);
			continue;
		}

		/* don't use 'i' since we may have skipped the face */
		BM_elem_index_set(f, bm->totface - 1); /* set_ok */

		/* transfer flag */
		f->head.hflag = BM_face_flag_from_mflag(mp->flag & ~ME_FACE_SEL);

		/* this is necessary for selection counts to work properly */
		if (mp->flag & ME_FACE_SEL) {
			BM_face_select_set(bm, f, true);
		}

		f->mat_nr = mp->mat_nr;
		if (i == me->act_face) bm->act_face = f;

		int j = mp->loopstart;
		l_iter = l_first = BM_FACE_FIRST_LOOP(f);
		do {
			/* don't use 'j' since we may have skipped some faces, hence some loops. */
			BM_elem_index_set(l_iter, totloops++); /* set_ok */

			/* Save index of correspsonding MLoop */
			CustomData_to_bmesh_block(&me->ldata, &bm->ldata, j++, &l_iter->head.data, true);
		} while ((l_iter = l_iter->next) != l_first);

		/* Copy Custom Data */
		CustomData_to_bmesh_block(&me->pdata, &bm->pdata, i, &f->head.data, true);

		if (params->calc_face_normal) {
			BM_face_normal_update(f);
		}
	}
	if (is_new) {
		bm->elem_index_dirty &= ~(BM_FACE | BM_LOOP); /* added in order, clear dirty flag */
	}

	/* -------------------------------------------------------------------- */
	/* MSelect clears the array elements (avoid adding multiple times).
	 *
	 * Take care to keep this last and not use (v/e/ftable) after this.
	 */

	if (me->mselect && me->totselect != 0) {
		MSelect *msel;
		for (i = 0, msel = me->mselect; i < me->totselect; i++, msel++) {
			BMElem **ele_p;
			switch (msel->type) {
				case ME_VSEL:
					ele_p = (BMElem **)&vtable[msel->index];
					break;
				case ME_ESEL:
					ele_p = (BMElem **)&etable[msel->index];
					break;
				case ME_FSEL:
					ele_p = (BMElem **)&ftable[msel->index];
					break;
				default:
					continue;
			}

			if (*ele_p != NULL) {
				BM_select_history_store_notest(bm, *ele_p);
				*ele_p = NULL;
			}
		}
	}
	else {
		BM_select_history_clear(bm);
	}

	MEM_freeN(vtable);
	MEM_freeN(etable);
	if (ftable) {
		MEM_freeN(ftable);
	}
}
void  bmiter__loop_of_face_begin(BMIter *iter)
{
	init_iterator(iter);
	iter->l_first = iter->l_next = BM_FACE_FIRST_LOOP(iter->pdata);
}
Exemple #9
0
static BVHTree *bvhtree_from_mesh_looptri_create_tree(
        float epsilon, int tree_type, int axis,
        BMEditMesh *em, const MVert *vert, const MLoop *mloop, const MLoopTri *looptri, const int looptri_num,
        BLI_bitmap *mask, int looptri_num_active)
{
	BVHTree *tree = NULL;
	int i;

	if (looptri_num) {
		if (mask && looptri_num_active < 0) {
			looptri_num_active = 0;
			for (i = 0; i < looptri_num; i++) {
				if (BLI_BITMAP_TEST_BOOL(mask, i)) {
					looptri_num_active++;
				}
			}
		}
		else if (!mask) {
			looptri_num_active = looptri_num;
		}

		/* Create a bvh-tree of the given target */
		/* printf("%s: building BVH, total=%d\n", __func__, numFaces); */
		tree = BLI_bvhtree_new(looptri_num_active, epsilon, tree_type, axis);
		if (tree) {
			if (em) {
				const struct BMLoop *(*looptris)[3] = (void *)em->looptris;

				/* avoid double-up on face searches for quads-ngons */
				bool insert_prev = false;
				BMFace *f_prev = NULL;

				/* data->em_evil is only set for snapping, and only for the mesh of the object
				 * which is currently open in edit mode. When set, the bvhtree should not contain
				 * faces that will interfere with snapping (e.g. faces that are hidden/selected
				 * or faces that have selected verts). */

				/* Insert BMesh-tessellation triangles into the bvh tree, unless they are hidden
				 * and/or selected. Even if the faces themselves are not selected for the snapped
				 * transform, having a vertex selected means the face (and thus it's tessellated
				 * triangles) will be moving and will not be a good snap targets. */
				for (i = 0; i < looptri_num; i++) {
					const BMLoop **ltri = looptris[i];
					BMFace *f = ltri[0]->f;
					bool insert = mask ? BLI_BITMAP_TEST_BOOL(mask, i) : true;

					/* Start with the assumption the triangle should be included for snapping. */
					if (f == f_prev) {
						insert = insert_prev;
					}
					else if (insert) {
						if (BM_elem_flag_test(f, BM_ELEM_SELECT) || BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
							/* Don't insert triangles tessellated from faces that are hidden or selected */
							insert = false;
						}
						else {
							BMLoop *l_iter, *l_first;
							l_iter = l_first = BM_FACE_FIRST_LOOP(f);
							do {
								if (BM_elem_flag_test(l_iter->v, BM_ELEM_SELECT)) {
									/* Don't insert triangles tessellated from faces that have any selected verts */
									insert = false;
									break;
								}
							} while ((l_iter = l_iter->next) != l_first);
						}

						/* skip if face doesn't change */
						f_prev = f;
						insert_prev = insert;
					}

					if (insert) {
						/* No reason found to block hit-testing the triangle for snap, so insert it now.*/
						float co[3][3];
						copy_v3_v3(co[0], ltri[0]->v->co);
						copy_v3_v3(co[1], ltri[1]->v->co);
						copy_v3_v3(co[2], ltri[2]->v->co);

						BLI_bvhtree_insert(tree, i, co[0], 3);
					}
				}
			}
			else {
				if (vert && looptri) {
					for (i = 0; i < looptri_num; i++) {
						float co[4][3];
						if (mask && !BLI_BITMAP_TEST_BOOL(mask, i)) {
							continue;
						}

						copy_v3_v3(co[0], vert[mloop[looptri[i].tri[0]].v].co);
						copy_v3_v3(co[1], vert[mloop[looptri[i].tri[1]].v].co);
						copy_v3_v3(co[2], vert[mloop[looptri[i].tri[2]].v].co);

						BLI_bvhtree_insert(tree, i, co[0], 3);
					}
				}
			}
			BLI_bvhtree_balance(tree);
		}
	}

	return tree;
}
Exemple #10
0
void  bmiter__edge_of_face_begin(BMIter *iter)
{
	init_iterator(iter);
	iter->firstloop = iter->nextloop = BM_FACE_FIRST_LOOP(iter->pdata);
}
Exemple #11
0
/**
 * \brief Mesh -> BMesh
 *
 * \warning This function doesn't calculate face normals.
 */
void BM_mesh_bm_from_me(BMesh *bm, Mesh *me,
                        const bool calc_face_normal, const bool set_key, int act_key_nr)
{
	MVert *mvert;
	MEdge *medge;
	MLoop *mloop;
	MPoly *mp;
	KeyBlock *actkey, *block;
	BMVert *v, **vtable = NULL;
	BMEdge *e, **etable = NULL;
	BMFace *f;
	float (*keyco)[3] = NULL;
	int *keyi;
	int totuv, i, j;

	int cd_vert_bweight_offset;
	int cd_edge_bweight_offset;
	int cd_edge_crease_offset;

	/* free custom data */
	/* this isnt needed in most cases but do just incase */
	CustomData_free(&bm->vdata, bm->totvert);
	CustomData_free(&bm->edata, bm->totedge);
	CustomData_free(&bm->ldata, bm->totloop);
	CustomData_free(&bm->pdata, bm->totface);

	if (!me || !me->totvert) {
		if (me) { /*no verts? still copy customdata layout*/
			CustomData_copy(&me->vdata, &bm->vdata, CD_MASK_BMESH, CD_ASSIGN, 0);
			CustomData_copy(&me->edata, &bm->edata, CD_MASK_BMESH, CD_ASSIGN, 0);
			CustomData_copy(&me->ldata, &bm->ldata, CD_MASK_BMESH, CD_ASSIGN, 0);
			CustomData_copy(&me->pdata, &bm->pdata, CD_MASK_BMESH, CD_ASSIGN, 0);

			CustomData_bmesh_init_pool(&bm->vdata, me->totvert, BM_VERT);
			CustomData_bmesh_init_pool(&bm->edata, me->totedge, BM_EDGE);
			CustomData_bmesh_init_pool(&bm->ldata, me->totloop, BM_LOOP);
			CustomData_bmesh_init_pool(&bm->pdata, me->totpoly, BM_FACE);
		}
		return; /* sanity check */
	}

	vtable = MEM_mallocN(sizeof(void **) * me->totvert, "mesh to bmesh vtable");

	CustomData_copy(&me->vdata, &bm->vdata, CD_MASK_BMESH, CD_CALLOC, 0);
	CustomData_copy(&me->edata, &bm->edata, CD_MASK_BMESH, CD_CALLOC, 0);
	CustomData_copy(&me->ldata, &bm->ldata, CD_MASK_BMESH, CD_CALLOC, 0);
	CustomData_copy(&me->pdata, &bm->pdata, CD_MASK_BMESH, CD_CALLOC, 0);

	/* make sure uv layer names are consisten */
	totuv = CustomData_number_of_layers(&bm->pdata, CD_MTEXPOLY);
	for (i = 0; i < totuv; i++) {
		int li = CustomData_get_layer_index_n(&bm->pdata, CD_MTEXPOLY, i);
		CustomData_set_layer_name(&bm->ldata, CD_MLOOPUV, i, bm->pdata.layers[li].name);
	}

	if ((act_key_nr != 0) && (me->key != NULL)) {
		actkey = BLI_findlink(&me->key->block, act_key_nr - 1);
	}
	else {
		actkey = NULL;
	}

	if (me->key) {
		CustomData_add_layer(&bm->vdata, CD_SHAPE_KEYINDEX, CD_ASSIGN, NULL, 0);

		/* check if we need to generate unique ids for the shapekeys.
		 * this also exists in the file reading code, but is here for
		 * a sanity check */
		if (!me->key->uidgen) {
			fprintf(stderr,
			        "%s had to generate shape key uid's in a situation we shouldn't need to! "
			        "(bmesh internal error)\n",
			        __func__);

			me->key->uidgen = 1;
			for (block = me->key->block.first; block; block = block->next) {
				block->uid = me->key->uidgen++;
			}
		}

		if (actkey && actkey->totelem == me->totvert) {
			keyco = actkey->data;
			bm->shapenr = act_key_nr;
		}

		for (i = 0, block = me->key->block.first; block; block = block->next, i++) {
			CustomData_add_layer_named(&bm->vdata, CD_SHAPEKEY,
			                           CD_ASSIGN, NULL, 0, block->name);

			j = CustomData_get_layer_index_n(&bm->vdata, CD_SHAPEKEY, i);
			bm->vdata.layers[j].uid = block->uid;
		}
	}

	CustomData_bmesh_init_pool(&bm->vdata, me->totvert, BM_VERT);
	CustomData_bmesh_init_pool(&bm->edata, me->totedge, BM_EDGE);
	CustomData_bmesh_init_pool(&bm->ldata, me->totloop, BM_LOOP);
	CustomData_bmesh_init_pool(&bm->pdata, me->totpoly, BM_FACE);

	BM_mesh_cd_flag_apply(bm, me->cd_flag);

	cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT);
	cd_edge_bweight_offset = CustomData_get_offset(&bm->edata, CD_BWEIGHT);
	cd_edge_crease_offset  = CustomData_get_offset(&bm->edata, CD_CREASE);

	for (i = 0, mvert = me->mvert; i < me->totvert; i++, mvert++) {
		v = vtable[i] = BM_vert_create(bm, keyco && set_key ? keyco[i] : mvert->co, NULL, BM_CREATE_SKIP_CD);
		BM_elem_index_set(v, i); /* set_ok */

		/* transfer flag */
		v->head.hflag = BM_vert_flag_from_mflag(mvert->flag & ~SELECT);

		/* this is necessary for selection counts to work properly */
		if (mvert->flag & SELECT) {
			BM_vert_select_set(bm, v, true);
		}

		normal_short_to_float_v3(v->no, mvert->no);

		/* Copy Custom Data */
		CustomData_to_bmesh_block(&me->vdata, &bm->vdata, i, &v->head.data, true);

		if (cd_vert_bweight_offset != -1) BM_ELEM_CD_SET_FLOAT(v, cd_vert_bweight_offset, (float)mvert->bweight / 255.0f);

		/* set shapekey data */
		if (me->key) {
			/* set shape key original index */
			keyi = CustomData_bmesh_get(&bm->vdata, v->head.data, CD_SHAPE_KEYINDEX);
			if (keyi) {
				*keyi = i;
			}

			for (block = me->key->block.first, j = 0; block; block = block->next, j++) {
				float *co = CustomData_bmesh_get_n(&bm->vdata, v->head.data, CD_SHAPEKEY, j);

				if (co) {
					copy_v3_v3(co, ((float *)block->data) + 3 * i);
				}
			}
		}
	}

	bm->elem_index_dirty &= ~BM_VERT; /* added in order, clear dirty flag */

	if (!me->totedge) {
		MEM_freeN(vtable);
		return;
	}

	etable = MEM_mallocN(sizeof(void **) * me->totedge, "mesh to bmesh etable");

	medge = me->medge;
	for (i = 0; i < me->totedge; i++, medge++) {
		e = etable[i] = BM_edge_create(bm, vtable[medge->v1], vtable[medge->v2], NULL, BM_CREATE_SKIP_CD);
		BM_elem_index_set(e, i); /* set_ok */

		/* transfer flags */
		e->head.hflag = BM_edge_flag_from_mflag(medge->flag & ~SELECT);

		/* this is necessary for selection counts to work properly */
		if (medge->flag & SELECT) {
			BM_edge_select_set(bm, e, true);
		}

		/* Copy Custom Data */
		CustomData_to_bmesh_block(&me->edata, &bm->edata, i, &e->head.data, true);

		if (cd_edge_bweight_offset != -1) BM_ELEM_CD_SET_FLOAT(e, cd_edge_bweight_offset, (float)medge->bweight / 255.0f);
		if (cd_edge_crease_offset  != -1) BM_ELEM_CD_SET_FLOAT(e, cd_edge_crease_offset,  (float)medge->crease  / 255.0f);

	}

	bm->elem_index_dirty &= ~BM_EDGE; /* added in order, clear dirty flag */

	mloop = me->mloop;
	mp = me->mpoly;
	for (i = 0; i < me->totpoly; i++, mp++) {
		BMLoop *l_iter;
		BMLoop *l_first;

		f = bm_face_create_from_mpoly(mp, mloop + mp->loopstart,
		                              bm, vtable, etable);

		if (UNLIKELY(f == NULL)) {
			printf("%s: Warning! Bad face in mesh"
			       " \"%s\" at index %d!, skipping\n",
			       __func__, me->id.name + 2, i);
			continue;
		}

		/* don't use 'i' since we may have skipped the face */
		BM_elem_index_set(f, bm->totface - 1); /* set_ok */

		/* transfer flag */
		f->head.hflag = BM_face_flag_from_mflag(mp->flag & ~ME_FACE_SEL);

		/* this is necessary for selection counts to work properly */
		if (mp->flag & ME_FACE_SEL) {
			BM_face_select_set(bm, f, true);
		}

		f->mat_nr = mp->mat_nr;
		if (i == me->act_face) bm->act_face = f;

		j = mp->loopstart;
		l_iter = l_first = BM_FACE_FIRST_LOOP(f);
		do {
			/* Save index of correspsonding MLoop */
			CustomData_to_bmesh_block(&me->ldata, &bm->ldata, j++, &l_iter->head.data, true);
		} while ((l_iter = l_iter->next) != l_first);

		/* Copy Custom Data */
		CustomData_to_bmesh_block(&me->pdata, &bm->pdata, i, &f->head.data, true);

		if (calc_face_normal) {
			BM_face_normal_update(f);
		}
	}

	bm->elem_index_dirty &= ~BM_FACE; /* added in order, clear dirty flag */

	if (me->mselect && me->totselect != 0) {

		BMVert **vert_array = MEM_mallocN(sizeof(BMVert *) * bm->totvert, "VSelConv");
		BMEdge **edge_array = MEM_mallocN(sizeof(BMEdge *) * bm->totedge, "ESelConv");
		BMFace **face_array = MEM_mallocN(sizeof(BMFace *) * bm->totface, "FSelConv");
		MSelect *msel;

#pragma omp parallel sections if (bm->totvert + bm->totedge + bm->totface >= BM_OMP_LIMIT)
		{
#pragma omp section
			{ BM_iter_as_array(bm, BM_VERTS_OF_MESH, NULL, (void **)vert_array, bm->totvert); }
#pragma omp section
			{ BM_iter_as_array(bm, BM_EDGES_OF_MESH, NULL, (void **)edge_array, bm->totedge); }
#pragma omp section
			{ BM_iter_as_array(bm, BM_FACES_OF_MESH, NULL, (void **)face_array, bm->totface); }
		}

		for (i = 0, msel = me->mselect; i < me->totselect; i++, msel++) {
			switch (msel->type) {
				case ME_VSEL:
					BM_select_history_store(bm, (BMElem *)vert_array[msel->index]);
					break;
				case ME_ESEL:
					BM_select_history_store(bm, (BMElem *)edge_array[msel->index]);
					break;
				case ME_FSEL:
					BM_select_history_store(bm, (BMElem *)face_array[msel->index]);
					break;
			}
		}

		MEM_freeN(vert_array);
		MEM_freeN(edge_array);
		MEM_freeN(face_array);
	}
	else {
		me->totselect = 0;
		if (me->mselect) {
			MEM_freeN(me->mselect);
			me->mselect = NULL;
		}
	}

	MEM_freeN(vtable);
	MEM_freeN(etable);
}
Exemple #12
0
/**
 * \return a face index in \a faces and set \a r_is_flip
 * if the face is flipped away from the center.
 */
static int recalc_face_normals_find_index(BMesh *bm,
                                          BMFace **faces,
                                          const int faces_len,
                                          bool *r_is_flip)
{
  const float eps = FLT_EPSILON;
  float cent_area_accum = 0.0f;
  float cent[3];
  const float cent_fac = 1.0f / (float)faces_len;

  bool is_flip = false;
  int f_start_index;
  int i;

  /** Search for the best loop. Members are compared in-order defined here. */
  struct {
    /**
     * Squared distance from the center to the loops vertex 'l->v'.
     * The normalized direction between the center and this vertex
     * is also used for the dot-products below.
     */
    float dist_sq;
    /**
     * Signed dot product using the normalized edge vector,
     * (best of 'l->prev->v' or 'l->next->v').
     */
    float edge_dot;
    /**
     * Unsigned dot product using the loop-normal
     * (sign is used to check if we need to flip).
     */
    float loop_dot;
  } best, test;

  UNUSED_VARS_NDEBUG(bm);

  zero_v3(cent);

  /* first calculate the center */
  for (i = 0; i < faces_len; i++) {
    float f_cent[3];
    const float f_area = BM_face_calc_area(faces[i]);
    BM_face_calc_center_median_weighted(faces[i], f_cent);
    madd_v3_v3fl(cent, f_cent, cent_fac * f_area);
    cent_area_accum += f_area;

    BLI_assert(BMO_face_flag_test(bm, faces[i], FACE_TEMP) == 0);
    BLI_assert(BM_face_is_normal_valid(faces[i]));
  }

  if (cent_area_accum != 0.0f) {
    mul_v3_fl(cent, 1.0f / cent_area_accum);
  }

  /* Distances must start above zero,
   * or we can't do meaningful calculations based on the direction to the center */
  best.dist_sq = eps;
  best.edge_dot = best.loop_dot = -FLT_MAX;

  /* used in degenerate cases only */
  f_start_index = 0;

  /**
   * Find the outer-most vertex, comparing distance to the center,
   * then the outer-most loop attached to that vertex.
   *
   * Important this is correctly detected,
   * where casting a ray from the center wont hit any loops past this one.
   * Otherwise the result may be incorrect.
   */
  for (i = 0; i < faces_len; i++) {
    BMLoop *l_iter, *l_first;

    l_iter = l_first = BM_FACE_FIRST_LOOP(faces[i]);
    do {
      bool is_best_dist_sq;
      float dir[3];
      sub_v3_v3v3(dir, l_iter->v->co, cent);
      test.dist_sq = len_squared_v3(dir);
      is_best_dist_sq = (test.dist_sq > best.dist_sq);
      if (is_best_dist_sq || (test.dist_sq == best.dist_sq)) {
        float edge_dir_pair[2][3];
        mul_v3_fl(dir, 1.0f / sqrtf(test.dist_sq));

        sub_v3_v3v3(edge_dir_pair[0], l_iter->next->v->co, l_iter->v->co);
        sub_v3_v3v3(edge_dir_pair[1], l_iter->prev->v->co, l_iter->v->co);

        if ((normalize_v3(edge_dir_pair[0]) > eps) && (normalize_v3(edge_dir_pair[1]) > eps)) {
          bool is_best_edge_dot;
          test.edge_dot = max_ff(dot_v3v3(dir, edge_dir_pair[0]), dot_v3v3(dir, edge_dir_pair[1]));
          is_best_edge_dot = (test.edge_dot > best.edge_dot);
          if (is_best_dist_sq || is_best_edge_dot || (test.edge_dot == best.edge_dot)) {
            float loop_dir[3];
            cross_v3_v3v3(loop_dir, edge_dir_pair[0], edge_dir_pair[1]);
            if (normalize_v3(loop_dir) > eps) {
              float loop_dir_dot;
              /* Highly unlikely the furthest loop is also the concave part of an ngon,
               * but it can be contrived with _very_ non-planar faces - so better check. */
              if (UNLIKELY(dot_v3v3(loop_dir, l_iter->f->no) < 0.0f)) {
                negate_v3(loop_dir);
              }
              loop_dir_dot = dot_v3v3(dir, loop_dir);
              test.loop_dot = fabsf(loop_dir_dot);
              if (is_best_dist_sq || is_best_edge_dot || (test.loop_dot > best.loop_dot)) {
                best = test;
                f_start_index = i;
                is_flip = (loop_dir_dot < 0.0f);
              }
            }
          }
        }
      }
    } while ((l_iter = l_iter->next) != l_first);
  }

  *r_is_flip = is_flip;
  return f_start_index;
}
Exemple #13
0
/* Builds a bvh tree.. where nodes are the faces of the given dm. */
BVHTree *bvhtree_from_mesh_faces(BVHTreeFromMesh *data, DerivedMesh *dm, float epsilon, int tree_type, int axis)
{
	BVHTree *tree = bvhcache_find(&dm->bvhCache, BVHTREE_FROM_FACES);
	BMEditMesh *em = data->em_evil;

	/* Not in cache */
	if (tree == NULL) {
		int i;
		int numFaces;

		/* BMESH specific check that we have tessfaces,
		 * we _could_ tessellate here but rather not - campbell
		 *
		 * this assert checks we have tessfaces,
		 * if not caller should use DM_ensure_tessface() */
		if (em) {
			numFaces = em->tottri;
		}
		else {
			numFaces = dm->getNumTessFaces(dm);
			BLI_assert(!(numFaces == 0 && dm->getNumPolys(dm) != 0));
		}

		if (numFaces != 0) {
			/* Create a bvh-tree of the given target */
			// printf("%s: building BVH, total=%d\n", __func__, numFaces);
			tree = BLI_bvhtree_new(numFaces, epsilon, tree_type, axis);
			if (tree != NULL) {
				if (em) {
					const struct BMLoop *(*looptris)[3] = (void *)em->looptris;

					/* avoid double-up on face searches for quads-ngons */
					bool insert_prev = false;
					BMFace *f_prev = NULL;

					/* data->em_evil is only set for snapping, and only for the mesh of the object
					 * which is currently open in edit mode. When set, the bvhtree should not contain
					 * faces that will interfere with snapping (e.g. faces that are hidden/selected
					 * or faces that have selected verts).*/

					/* Insert BMesh-tessellation triangles into the bvh tree, unless they are hidden
					 * and/or selected. Even if the faces themselves are not selected for the snapped
					 * transform, having a vertex selected means the face (and thus it's tessellated
					 * triangles) will be moving and will not be a good snap targets.*/
					for (i = 0; i < em->tottri; i++) {
						const BMLoop **ltri = looptris[i];
						BMFace *f = ltri[0]->f;
						bool insert;

						/* Start with the assumption the triangle should be included for snapping. */
						if (f == f_prev) {
							insert = insert_prev;
						}
						else {
							if (BM_elem_flag_test(f, BM_ELEM_SELECT) || BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
								/* Don't insert triangles tessellated from faces that are hidden
								 * or selected*/
								insert = false;
							}
							else {
								BMLoop *l_iter, *l_first;
								insert = true;
								l_iter = l_first = BM_FACE_FIRST_LOOP(f);
								do {
									if (BM_elem_flag_test(l_iter->v, BM_ELEM_SELECT)) {
										/* Don't insert triangles tessellated from faces that have
										 * any selected verts.*/
										insert = false;
										break;
									}
								} while ((l_iter = l_iter->next) != l_first);
							}

							/* skip if face doesn't change */
							f_prev = f;
							insert_prev = insert;
						}

						if (insert) {
							/* No reason found to block hit-testing the triangle for snap,
							 * so insert it now.*/
							float co[3][3];
							copy_v3_v3(co[0], ltri[0]->v->co);
							copy_v3_v3(co[1], ltri[1]->v->co);
							copy_v3_v3(co[2], ltri[2]->v->co);
					
							BLI_bvhtree_insert(tree, i, co[0], 3);
						}
					}
				}
				else {
					MVert *vert = dm->getVertDataArray(dm, CD_MVERT);
					MFace *face = dm->getTessFaceDataArray(dm, CD_MFACE);

					if (vert != NULL && face != NULL) {
						for (i = 0; i < numFaces; i++) {
							float co[4][3];
							copy_v3_v3(co[0], vert[face[i].v1].co);
							copy_v3_v3(co[1], vert[face[i].v2].co);
							copy_v3_v3(co[2], vert[face[i].v3].co);
							if (face[i].v4)
								copy_v3_v3(co[3], vert[face[i].v4].co);
					
							BLI_bvhtree_insert(tree, i, co[0], face[i].v4 ? 4 : 3);
						}
					}
				}
				BLI_bvhtree_balance(tree);

				/* Save on cache for later use */
//				printf("BVHTree built and saved on cache\n");
				bvhcache_insert(&dm->bvhCache, tree, BVHTREE_FROM_FACES);
			}
		}
	}
	else {
//		printf("BVHTree is already build, using cached tree\n");
	}


	/* Setup BVHTreeFromMesh */
	memset(data, 0, sizeof(*data));
	data->tree = tree;
	data->em_evil = em;

	if (data->tree) {
		data->cached = true;

		if (em) {
			data->nearest_callback = editmesh_faces_nearest_point;
			data->raycast_callback = editmesh_faces_spherecast;
		}
		else {
			data->nearest_callback = mesh_faces_nearest_point;
			data->raycast_callback = mesh_faces_spherecast;

			data->vert = dm->getVertDataArray(dm, CD_MVERT);
			data->face = dm->getTessFaceDataArray(dm, CD_MFACE);
		}

		data->sphere_radius = epsilon;
	}
	return data->tree;

}
Exemple #14
0
/**
 * The main function for copying DerivedMesh data into BMesh.
 *
 * \note The mesh may already have geometry. see 'is_init'
 */
void DM_to_bmesh_ex(DerivedMesh *dm, BMesh *bm, const bool calc_face_normal)
{
	MVert *mv, *mvert;
	MEdge *me, *medge;
	MPoly /* *mpoly, */ /* UNUSED */ *mp;
	MLoop *mloop;
	BMVert *v, **vtable;
	BMEdge *e, **etable;
	float (*face_normals)[3];
	BMFace *f;
	int i, j, totvert, totedge /* , totface */ /* UNUSED */ ;
	bool is_init = (bm->totvert == 0) && (bm->totedge == 0) && (bm->totface == 0);
	bool is_cddm = (dm->type == DM_TYPE_CDDM);  /* duplicate the arrays for non cddm */
	char has_orig_hflag = 0;

	int cd_vert_bweight_offset;
	int cd_edge_bweight_offset;
	int cd_edge_crease_offset;

	if (is_init == FALSE) {
		/* check if we have an origflag */
		has_orig_hflag |= CustomData_has_layer(&bm->vdata, CD_ORIGINDEX) ? BM_VERT : 0;
		has_orig_hflag |= CustomData_has_layer(&bm->edata, CD_ORIGINDEX) ? BM_EDGE : 0;
		has_orig_hflag |= CustomData_has_layer(&bm->pdata, CD_ORIGINDEX) ? BM_FACE : 0;
	}

	/*merge custom data layout*/
	CustomData_bmesh_merge(&dm->vertData, &bm->vdata, CD_MASK_DERIVEDMESH, CD_CALLOC, bm, BM_VERT);
	CustomData_bmesh_merge(&dm->edgeData, &bm->edata, CD_MASK_DERIVEDMESH, CD_CALLOC, bm, BM_EDGE);
	CustomData_bmesh_merge(&dm->loopData, &bm->ldata, CD_MASK_DERIVEDMESH, CD_CALLOC, bm, BM_LOOP);
	CustomData_bmesh_merge(&dm->polyData, &bm->pdata, CD_MASK_DERIVEDMESH, CD_CALLOC, bm, BM_FACE);

	if (is_init) {
		BM_mesh_cd_flag_apply(bm, dm->cd_flag);
	}

	cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT);
	cd_edge_bweight_offset = CustomData_get_offset(&bm->edata, CD_BWEIGHT);
	cd_edge_crease_offset  = CustomData_get_offset(&bm->edata, CD_CREASE);

	totvert = dm->getNumVerts(dm);
	totedge = dm->getNumEdges(dm);
	/* totface = dm->getNumPolys(dm); */ /* UNUSED */

	vtable = MEM_callocN(sizeof(void **) * totvert, __func__);
	etable = MEM_callocN(sizeof(void **) * totedge, __func__);

	/*do verts*/
	mv = mvert = is_cddm ? dm->getVertArray(dm) : dm->dupVertArray(dm);
	for (i = 0; i < totvert; i++, mv++) {
		v = BM_vert_create(bm, mv->co, NULL, BM_CREATE_SKIP_CD);
		normal_short_to_float_v3(v->no, mv->no);
		v->head.hflag = BM_vert_flag_from_mflag(mv->flag);
		BM_elem_index_set(v, i); /* set_inline */

		CustomData_to_bmesh_block(&dm->vertData, &bm->vdata, i, &v->head.data, true);
		vtable[i] = v;

		/* add bevel weight */
		if (cd_vert_bweight_offset != -1) BM_ELEM_CD_SET_FLOAT(v, cd_vert_bweight_offset, (float)mv->bweight / 255.0f);

		if (UNLIKELY(has_orig_hflag & BM_VERT)) {
			int *orig_index = CustomData_bmesh_get(&bm->vdata, v->head.data, CD_ORIGINDEX);
			*orig_index = ORIGINDEX_NONE;
		}
	}
	if (!is_cddm) MEM_freeN(mvert);
	if (is_init) bm->elem_index_dirty &= ~BM_VERT;

	/*do edges*/
	me = medge = is_cddm ? dm->getEdgeArray(dm) : dm->dupEdgeArray(dm);
	for (i = 0; i < totedge; i++, me++) {
		//BLI_assert(BM_edge_exists(vtable[me->v1], vtable[me->v2]) == NULL);
		e = BM_edge_create(bm, vtable[me->v1], vtable[me->v2], NULL, BM_CREATE_SKIP_CD);

		e->head.hflag = BM_edge_flag_from_mflag(me->flag);
		BM_elem_index_set(e, i); /* set_inline */

		CustomData_to_bmesh_block(&dm->edgeData, &bm->edata, i, &e->head.data, true);
		etable[i] = e;

		if (cd_edge_bweight_offset != -1) BM_ELEM_CD_SET_FLOAT(e, cd_edge_bweight_offset, (float)me->bweight / 255.0f);
		if (cd_edge_crease_offset  != -1) BM_ELEM_CD_SET_FLOAT(e, cd_edge_crease_offset,  (float)me->crease  / 255.0f);

		if (UNLIKELY(has_orig_hflag & BM_EDGE)) {
			int *orig_index = CustomData_bmesh_get(&bm->edata, e->head.data, CD_ORIGINDEX);
			*orig_index = ORIGINDEX_NONE;
		}
	}
	if (!is_cddm) MEM_freeN(medge);
	if (is_init) bm->elem_index_dirty &= ~BM_EDGE;

	/* do faces */
	/* note: i_alt is aligned with bmesh faces which may not always align with mpolys */
	mp = dm->getPolyArray(dm);
	mloop = dm->getLoopArray(dm);
	face_normals = (dm->dirty & DM_DIRTY_NORMALS) ? NULL : CustomData_get_layer(&dm->polyData, CD_NORMAL);
	for (i = 0; i < dm->numPolyData; i++, mp++) {
		BMLoop *l_iter;
		BMLoop *l_first;

		f = bm_face_create_from_mpoly(mp, mloop + mp->loopstart,
		                              bm, vtable, etable);

		if (UNLIKELY(f == NULL)) {
			continue;
		}

		f->head.hflag = BM_face_flag_from_mflag(mp->flag);
		BM_elem_index_set(f, bm->totface - 1); /* set_inline */
		f->mat_nr = mp->mat_nr;

		j = mp->loopstart;
		l_iter = l_first = BM_FACE_FIRST_LOOP(f);
		do {
			/* Save index of correspsonding MLoop */
			CustomData_to_bmesh_block(&dm->loopData, &bm->ldata, j++, &l_iter->head.data, true);
		} while ((l_iter = l_iter->next) != l_first);

		CustomData_to_bmesh_block(&dm->polyData, &bm->pdata, i, &f->head.data, true);

		if (calc_face_normal) {
			if (face_normals) {
				copy_v3_v3(f->no, face_normals[i]);
			}
			else {
				BM_face_normal_update(f);
			}
		}

		if (UNLIKELY(has_orig_hflag & BM_FACE)) {
			int *orig_index = CustomData_bmesh_get(&bm->pdata, f->head.data, CD_ORIGINDEX);
			*orig_index = ORIGINDEX_NONE;
		}
	}
	if (is_init) bm->elem_index_dirty &= ~BM_FACE;

	MEM_freeN(vtable);
	MEM_freeN(etable);
}