Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/mesh/editmesh_utils.c
| Show First 20 Lines • Show All 62 Lines • ▼ Show 20 Lines | |||||
| /** \name Redo API | /** \name Redo API | ||||
| * \{ */ | * \{ */ | ||||
| /* Mesh backup implementation. | /* Mesh backup implementation. | ||||
| * This would greatly benefit from some sort of binary diffing | * This would greatly benefit from some sort of binary diffing | ||||
| * just as the undo stack would. | * just as the undo stack would. | ||||
| * So leaving this as an interface for further work */ | * So leaving this as an interface for further work */ | ||||
| /** | |||||
| * Save a copy of the #BMesh for restoring later. | |||||
| */ | |||||
| BMBackup EDBM_redo_state_store(BMEditMesh *em) | BMBackup EDBM_redo_state_store(BMEditMesh *em) | ||||
| { | { | ||||
| BMBackup backup; | BMBackup backup; | ||||
| backup.bmcopy = BM_mesh_copy(em->bm); | backup.bmcopy = BM_mesh_copy(em->bm); | ||||
| return backup; | return backup; | ||||
| } | } | ||||
| void EDBM_redo_state_restore(BMBackup *backup, BMEditMesh *em, bool recalc_looptri) | void EDBM_redo_state_restore(BMBackup *backup, BMEditMesh *em, bool recalc_looptri) | ||||
| { | { | ||||
| BMesh *tmpbm; | BMesh *tmpbm; | ||||
| BM_mesh_data_free(em->bm); | BM_mesh_data_free(em->bm); | ||||
| tmpbm = BM_mesh_copy(backup->bmcopy); | tmpbm = BM_mesh_copy(backup->bmcopy); | ||||
| *em->bm = *tmpbm; | *em->bm = *tmpbm; | ||||
| MEM_freeN(tmpbm); | MEM_freeN(tmpbm); | ||||
| tmpbm = NULL; | tmpbm = NULL; | ||||
| if (recalc_looptri) { | if (recalc_looptri) { | ||||
| BKE_editmesh_looptri_calc(em); | BKE_editmesh_looptri_calc(em); | ||||
| } | } | ||||
| } | } | ||||
| /** | |||||
| * Delete the backup, flushing it to an edit-mesh. | |||||
| */ | |||||
| void EDBM_redo_state_restore_and_free(BMBackup *backup, BMEditMesh *em, bool recalc_looptri) | void EDBM_redo_state_restore_and_free(BMBackup *backup, BMEditMesh *em, bool recalc_looptri) | ||||
| { | { | ||||
| BM_mesh_data_free(em->bm); | BM_mesh_data_free(em->bm); | ||||
| *em->bm = *backup->bmcopy; | *em->bm = *backup->bmcopy; | ||||
| MEM_freeN(backup->bmcopy); | MEM_freeN(backup->bmcopy); | ||||
| backup->bmcopy = NULL; | backup->bmcopy = NULL; | ||||
| if (recalc_looptri) { | if (recalc_looptri) { | ||||
| BKE_editmesh_looptri_calc(em); | BKE_editmesh_looptri_calc(em); | ||||
| Show All 27 Lines | if (!BMO_op_vinitf(bm, bmop, BMO_FLAG_DEFAULTS, fmt, list)) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| va_end(list); | va_end(list); | ||||
| return true; | return true; | ||||
| } | } | ||||
| /** | |||||
| * The return value: | |||||
| * - False on error (the mesh must not be changed). | |||||
| * - True on success, executes and finishes a #BMesh operator. | |||||
| */ | |||||
| bool EDBM_op_finish(BMEditMesh *em, BMOperator *bmop, wmOperator *op, const bool do_report) | bool EDBM_op_finish(BMEditMesh *em, BMOperator *bmop, wmOperator *op, const bool do_report) | ||||
| { | { | ||||
| const char *errmsg; | const char *errmsg; | ||||
| #ifndef NDEBUG | #ifndef NDEBUG | ||||
| struct { | struct { | ||||
| int verts_len, edges_len, loops_len, faces_len; | int verts_len, edges_len, loops_len, faces_len; | ||||
| } em_state_prev = { | } em_state_prev = { | ||||
| ▲ Show 20 Lines • Show All 160 Lines • ▼ Show 20 Lines | void EDBM_mesh_make(Object *ob, const int select_mode, const bool add_key_index) | ||||
| me->edit_mesh->selectmode = me->edit_mesh->bm->selectmode = select_mode; | me->edit_mesh->selectmode = me->edit_mesh->bm->selectmode = select_mode; | ||||
| me->edit_mesh->mat_nr = (ob->actcol > 0) ? ob->actcol - 1 : 0; | me->edit_mesh->mat_nr = (ob->actcol > 0) ? ob->actcol - 1 : 0; | ||||
| /* we need to flush selection because the mode may have changed from when last in editmode */ | /* we need to flush selection because the mode may have changed from when last in editmode */ | ||||
| EDBM_selectmode_flush(me->edit_mesh); | EDBM_selectmode_flush(me->edit_mesh); | ||||
| } | } | ||||
| /** | |||||
| * \warning This can invalidate the #Mesh runtime cache of other objects (for linked duplicates). | |||||
| * Most callers should run #DEG_id_tag_update on `ob->data`, see: T46738, T46913. | |||||
| * This ensures #BKE_object_free_derived_caches runs on all objects that use this mesh. | |||||
| */ | |||||
| void EDBM_mesh_load_ex(Main *bmain, Object *ob, bool free_data) | void EDBM_mesh_load_ex(Main *bmain, Object *ob, bool free_data) | ||||
| { | { | ||||
| Mesh *me = ob->data; | Mesh *me = ob->data; | ||||
| BMesh *bm = me->edit_mesh->bm; | BMesh *bm = me->edit_mesh->bm; | ||||
| /* Workaround for T42360, 'ob->shapenr' should be 1 in this case. | /* Workaround for T42360, 'ob->shapenr' should be 1 in this case. | ||||
| * however this isn't synchronized between objects at the moment. */ | * however this isn't synchronized between objects at the moment. */ | ||||
| if (UNLIKELY((ob->shapenr == 0) && (me->key && !BLI_listbase_is_empty(&me->key->block)))) { | if (UNLIKELY((ob->shapenr == 0) && (me->key && !BLI_listbase_is_empty(&me->key->block)))) { | ||||
| Show All 22 Lines | void EDBM_mesh_clear(BMEditMesh *em) | ||||
| MEM_SAFE_FREE(em->looptris); | MEM_SAFE_FREE(em->looptris); | ||||
| } | } | ||||
| void EDBM_mesh_load(Main *bmain, Object *ob) | void EDBM_mesh_load(Main *bmain, Object *ob) | ||||
| { | { | ||||
| EDBM_mesh_load_ex(bmain, ob, true); | EDBM_mesh_load_ex(bmain, ob, true); | ||||
| } | } | ||||
| /** | |||||
| * Should only be called on the active edit-mesh, otherwise call #BKE_editmesh_free_data. | |||||
| */ | |||||
| void EDBM_mesh_free_data(BMEditMesh *em) | void EDBM_mesh_free_data(BMEditMesh *em) | ||||
| { | { | ||||
| /* These tables aren't used yet, so it's not strictly necessary | /* These tables aren't used yet, so it's not strictly necessary | ||||
| * to 'end' them but if someone tries to start using them, | * to 'end' them but if someone tries to start using them, | ||||
| * having these in place will save a lot of pain. */ | * having these in place will save a lot of pain. */ | ||||
| ED_mesh_mirror_spatial_table_end(NULL); | ED_mesh_mirror_spatial_table_end(NULL); | ||||
| ED_mesh_mirror_topo_table_end(NULL); | ED_mesh_mirror_topo_table_end(NULL); | ||||
| ▲ Show 20 Lines • Show All 104 Lines • ▼ Show 20 Lines | |||||
| } | } | ||||
| /** \} */ | /** \} */ | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name UV Vertex Map API | /** \name UV Vertex Map API | ||||
| * \{ */ | * \{ */ | ||||
| /** | |||||
| * Return a new #UvVertMap from the edit-mesh. | |||||
| */ | |||||
| UvVertMap *BM_uv_vert_map_create(BMesh *bm, const bool use_select, const bool use_winding) | UvVertMap *BM_uv_vert_map_create(BMesh *bm, const bool use_select, const bool use_winding) | ||||
| { | { | ||||
| BMVert *ev; | BMVert *ev; | ||||
| BMFace *efa; | BMFace *efa; | ||||
| BMLoop *l; | BMLoop *l; | ||||
| BMIter iter, liter; | BMIter iter, liter; | ||||
| /* vars from original func */ | /* vars from original func */ | ||||
| UvVertMap *vmap; | UvVertMap *vmap; | ||||
| ▲ Show 20 Lines • Show All 127 Lines • ▼ Show 20 Lines | UvVertMap *BM_uv_vert_map_create(BMesh *bm, const bool use_select, const bool use_winding) | ||||
| return vmap; | return vmap; | ||||
| } | } | ||||
| UvMapVert *BM_uv_vert_map_at_index(UvVertMap *vmap, uint v) | UvMapVert *BM_uv_vert_map_at_index(UvVertMap *vmap, uint v) | ||||
| { | { | ||||
| return vmap->vert[v]; | return vmap->vert[v]; | ||||
| } | } | ||||
| /* A specialized vert map used by stitch operator */ | |||||
| UvElementMap *BM_uv_element_map_create(BMesh *bm, | UvElementMap *BM_uv_element_map_create(BMesh *bm, | ||||
| const Scene *scene, | const Scene *scene, | ||||
| const bool face_selected, | const bool face_selected, | ||||
| const bool uv_selected, | const bool uv_selected, | ||||
| const bool use_winding, | const bool use_winding, | ||||
| const bool do_islands) | const bool do_islands) | ||||
| { | { | ||||
| BMVert *ev; | BMVert *ev; | ||||
| ▲ Show 20 Lines • Show All 304 Lines • ▼ Show 20 Lines | |||||
| } | } | ||||
| /** \} */ | /** \} */ | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name Data Layer Checks | /** \name Data Layer Checks | ||||
| * \{ */ | * \{ */ | ||||
| /** | |||||
| * last_sel, use em->act_face otherwise get the last selected face in the editselections | |||||
| * at the moment, last_sel is mainly useful for making sure the space image doesn't flicker. | |||||
| */ | |||||
| BMFace *EDBM_uv_active_face_get(BMEditMesh *em, const bool sloppy, const bool selected) | BMFace *EDBM_uv_active_face_get(BMEditMesh *em, const bool sloppy, const bool selected) | ||||
| { | { | ||||
| BMFace *efa = NULL; | BMFace *efa = NULL; | ||||
| if (!EDBM_uv_check(em)) { | if (!EDBM_uv_check(em)) { | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| efa = BM_mesh_active_face_get(em->bm, sloppy, selected); | efa = BM_mesh_active_face_get(em->bm, sloppy, selected); | ||||
| if (efa) { | if (efa) { | ||||
| return efa; | return efa; | ||||
| } | } | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| /* Can we edit UV's for this mesh? */ | |||||
| bool EDBM_uv_check(BMEditMesh *em) | bool EDBM_uv_check(BMEditMesh *em) | ||||
| { | { | ||||
| /* some of these checks could be a touch overkill */ | /* some of these checks could be a touch overkill */ | ||||
| return em && em->bm->totface && CustomData_has_layer(&em->bm->ldata, CD_MLOOPUV); | return em && em->bm->totface && CustomData_has_layer(&em->bm->ldata, CD_MLOOPUV); | ||||
| } | } | ||||
| bool EDBM_vert_color_check(BMEditMesh *em) | bool EDBM_vert_color_check(BMEditMesh *em) | ||||
| { | { | ||||
| Show All 24 Lines | |||||
| * e_mirror = EDBM_verts_mirror_get_edge(em, e); | * e_mirror = EDBM_verts_mirror_get_edge(em, e); | ||||
| * f_mirror = EDBM_verts_mirror_get_face(em, f); | * f_mirror = EDBM_verts_mirror_get_face(em, f); | ||||
| * } | * } | ||||
| * | * | ||||
| * EDBM_verts_mirror_cache_end(em); | * EDBM_verts_mirror_cache_end(em); | ||||
| * \endcode | * \endcode | ||||
| */ | */ | ||||
| /* BM_SEARCH_MAXDIST is too big, copied from 2.6x MOC_THRESH, should become a | /* BM_SEARCH_MAXDIST is too big, copied from 2.6x MOC_THRESH, should become a preference. */ | ||||
| * preference */ | |||||
| #define BM_SEARCH_MAXDIST_MIRR 0.00002f | #define BM_SEARCH_MAXDIST_MIRR 0.00002f | ||||
| #define BM_CD_LAYER_ID "__mirror_index" | #define BM_CD_LAYER_ID "__mirror_index" | ||||
| /** | |||||
| * \param em: Editmesh. | |||||
| * \param use_self: Allow a vertex to point to its self (middle verts). | |||||
| * \param use_select: Restrict to selected verts. | |||||
| * \param respecthide: Skip hidden vertices. | |||||
| * \param use_topology: Use topology mirror. | |||||
| * \param maxdist: Distance for close point test. | |||||
| * \param r_index: Optional array to write into, as an alternative to a customdata layer | |||||
| * (length of total verts). | |||||
| */ | |||||
| void EDBM_verts_mirror_cache_begin_ex(BMEditMesh *em, | void EDBM_verts_mirror_cache_begin_ex(BMEditMesh *em, | ||||
| const int axis, | const int axis, | ||||
| const bool use_self, | const bool use_self, | ||||
| const bool use_select, | const bool use_select, | ||||
| const bool respecthide, | const bool respecthide, | ||||
| /* extra args */ | /* extra args */ | ||||
| const bool use_topology, | const bool use_topology, | ||||
| float maxdist, | float maxdist, | ||||
| ▲ Show 20 Lines • Show All 210 Lines • ▼ Show 20 Lines | |||||
| } | } | ||||
| /** \} */ | /** \} */ | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name Hide/Reveal API | /** \name Hide/Reveal API | ||||
| * \{ */ | * \{ */ | ||||
| /* swap is 0 or 1, if 1 it hides not selected */ | |||||
| bool EDBM_mesh_hide(BMEditMesh *em, bool swap) | bool EDBM_mesh_hide(BMEditMesh *em, bool swap) | ||||
| { | { | ||||
| BMIter iter; | BMIter iter; | ||||
| BMElem *ele; | BMElem *ele; | ||||
| int itermode; | int itermode; | ||||
| char hflag_swap = swap ? BM_ELEM_SELECT : 0; | char hflag_swap = swap ? BM_ELEM_SELECT : 0; | ||||
| bool changed = true; | bool changed = true; | ||||
| ▲ Show 20 Lines • Show All 138 Lines • ▼ Show 20 Lines | for (i = 0; i < 3; i++) { | ||||
| for (; ele; ele = BM_iter_step(&iter)) { | for (; ele; ele = BM_iter_step(&iter)) { | ||||
| if (BM_elem_flag_test(ele, BM_ELEM_SELECT)) { | if (BM_elem_flag_test(ele, BM_ELEM_SELECT)) { | ||||
| (*tots[i])++; | (*tots[i])++; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /** | |||||
| * So many tools call these that we better make it a generic function. | |||||
| */ | |||||
| void EDBM_update(Mesh *mesh, const struct EDBMUpdate_Params *params) | void EDBM_update(Mesh *mesh, const struct EDBMUpdate_Params *params) | ||||
| { | { | ||||
| BMEditMesh *em = mesh->edit_mesh; | BMEditMesh *em = mesh->edit_mesh; | ||||
| /* Order of calling isn't important. */ | /* Order of calling isn't important. */ | ||||
| DEG_id_tag_update(&mesh->id, ID_RECALC_GEOMETRY); | DEG_id_tag_update(&mesh->id, ID_RECALC_GEOMETRY); | ||||
| WM_main_add_notifier(NC_GEOM | ND_DATA, &mesh->id); | WM_main_add_notifier(NC_GEOM | ND_DATA, &mesh->id); | ||||
| if (params->calc_normals && params->calc_looptri) { | if (params->calc_normals && params->calc_looptri) { | ||||
| Show All 30 Lines | #ifdef DEBUG | ||||
| BMEditSelection *ese; | BMEditSelection *ese; | ||||
| for (ese = em->bm->selected.first; ese; ese = ese->next) { | for (ese = em->bm->selected.first; ese; ese = ese->next) { | ||||
| BLI_assert(BM_elem_flag_test(ese->ele, BM_ELEM_SELECT)); | BLI_assert(BM_elem_flag_test(ese->ele, BM_ELEM_SELECT)); | ||||
| } | } | ||||
| } | } | ||||
| #endif | #endif | ||||
| } | } | ||||
| /* Bad level call from Python API. */ | |||||
| void EDBM_update_extern(struct Mesh *me, const bool do_tessellation, const bool is_destructive) | void EDBM_update_extern(struct Mesh *me, const bool do_tessellation, const bool is_destructive) | ||||
| { | { | ||||
| EDBM_update(me, | EDBM_update(me, | ||||
| &(const struct EDBMUpdate_Params){ | &(const struct EDBMUpdate_Params){ | ||||
| .calc_looptri = do_tessellation, | .calc_looptri = do_tessellation, | ||||
| .calc_normals = false, | .calc_normals = false, | ||||
| .is_destructive = is_destructive, | .is_destructive = is_destructive, | ||||
| }); | }); | ||||
| } | } | ||||
| /** \} */ | /** \} */ | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name Operator Helpers | /** \name Operator Helpers | ||||
| * \{ */ | * \{ */ | ||||
| /* poll call for mesh operators requiring a view3d context */ | |||||
| bool EDBM_view3d_poll(bContext *C) | bool EDBM_view3d_poll(bContext *C) | ||||
| { | { | ||||
| if (ED_operator_editmesh(C) && ED_operator_view3d_active(C)) { | if (ED_operator_editmesh(C) && ED_operator_view3d_active(C)) { | ||||
| return 1; | return 1; | ||||
| } | } | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| Show All 16 Lines | BMElem *EDBM_elem_from_selectmode(BMEditMesh *em, BMVert *eve, BMEdge *eed, BMFace *efa) | ||||
| } | } | ||||
| else if ((em->selectmode & SCE_SELECT_FACE) && efa) { | else if ((em->selectmode & SCE_SELECT_FACE) && efa) { | ||||
| ele = (BMElem *)efa; | ele = (BMElem *)efa; | ||||
| } | } | ||||
| return ele; | return ele; | ||||
| } | } | ||||
| /** | |||||
| * Used when we want to store a single index for any vert/edge/face. | |||||
| * | |||||
| * Intended for use with operators. | |||||
| */ | |||||
| int EDBM_elem_to_index_any(BMEditMesh *em, BMElem *ele) | int EDBM_elem_to_index_any(BMEditMesh *em, BMElem *ele) | ||||
| { | { | ||||
| BMesh *bm = em->bm; | BMesh *bm = em->bm; | ||||
| int index = BM_elem_index_get(ele); | int index = BM_elem_index_get(ele); | ||||
| if (ele->head.htype == BM_VERT) { | if (ele->head.htype == BM_VERT) { | ||||
| BLI_assert(!(bm->elem_index_dirty & BM_VERT)); | BLI_assert(!(bm->elem_index_dirty & BM_VERT)); | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 212 Lines • Show Last 20 Lines | |||||