/** * \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); }
/** * 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; }
/* 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; }
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); }
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; }
void bmiter__edge_of_face_begin(BMIter *iter) { init_iterator(iter); iter->firstloop = iter->nextloop = BM_FACE_FIRST_LOOP(iter->pdata); }
/** * \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); }
/** * \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; }
/* 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; }
/** * 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); }