/* SPDX-License-Identifier: GPL-2.0-or-later */ /** \file * \ingroup bmesh * * utility bmesh operators, e.g. transform, * translate, rotate, scale, etc. */ #include "MEM_guardedalloc.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "BLI_alloca.h" #include "BLI_math.h" #include "BKE_attribute.h" #include "BKE_customdata.h" #include "BKE_object.h" #include "bmesh.h" #include "intern/bmesh_operators_private.h" /* own include */ #define ELE_NEW 1 void bmo_create_vert_exec(BMesh *bm, BMOperator *op) { float vec[3]; BMO_slot_vec_get(op->slots_in, "co", vec); BMO_vert_flag_enable(bm, BM_vert_create(bm, vec, NULL, BM_CREATE_NOP), ELE_NEW); BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "vert.out", BM_VERT, ELE_NEW); } void bmo_transform_exec(BMesh *bm, BMOperator *op) { BMOIter iter; BMVert *v; float mat[4][4], mat_space[4][4], imat_space[4][4]; const uint shape_keys_len = BMO_slot_bool_get(op->slots_in, "use_shapekey") ? CustomData_number_of_layers(&bm->vdata, CD_SHAPEKEY) : 0; const uint cd_shape_key_offset = CustomData_get_offset(&bm->vdata, CD_SHAPEKEY); BMO_slot_mat4_get(op->slots_in, "matrix", mat); BMO_slot_mat4_get(op->slots_in, "space", mat_space); if (!is_zero_m4(mat_space)) { invert_m4_m4(imat_space, mat_space); mul_m4_series(mat, imat_space, mat, mat_space); } BMO_ITER (v, &iter, op->slots_in, "verts", BM_VERT) { mul_m4_v3(mat, v->co); if (shape_keys_len != 0) { float(*co_dst)[3] = BM_ELEM_CD_GET_VOID_P(v, cd_shape_key_offset); for (int i = 0; i < shape_keys_len; i++, co_dst++) { mul_m4_v3(mat, *co_dst); } } } } void bmo_translate_exec(BMesh *bm, BMOperator *op) { float mat[4][4], vec[3]; BMO_slot_vec_get(op->slots_in, "vec", vec); unit_m4(mat); copy_v3_v3(mat[3], vec); BMO_op_callf(bm, op->flag, "transform matrix=%m4 space=%s verts=%s use_shapekey=%s", mat, op, "space", op, "verts", op, "use_shapekey"); } void bmo_scale_exec(BMesh *bm, BMOperator *op) { float mat[3][3], vec[3]; BMO_slot_vec_get(op->slots_in, "vec", vec); unit_m3(mat); mat[0][0] = vec[0]; mat[1][1] = vec[1]; mat[2][2] = vec[2]; BMO_op_callf(bm, op->flag, "transform matrix=%m3 space=%s verts=%s use_shapekey=%s", mat, op, "space", op, "verts", op, "use_shapekey"); } void bmo_rotate_exec(BMesh *bm, BMOperator *op) { float center[3]; float mat[4][4]; BMO_slot_vec_get(op->slots_in, "cent", center); BMO_slot_mat4_get(op->slots_in, "matrix", mat); transform_pivot_set_m4(mat, center); BMO_op_callf(bm, op->flag, "transform matrix=%m4 space=%s verts=%s use_shapekey=%s", mat, op, "space", op, "verts", op, "use_shapekey"); } void bmo_reverse_faces_exec(BMesh *bm, BMOperator *op) { const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS); const bool use_loop_mdisp_flip = BMO_slot_bool_get(op->slots_in, "flip_multires"); BMOIter siter; BMFace *f; BMO_ITER (f, &siter, op->slots_in, "faces", BM_FACE) { BM_face_normal_flip_ex(bm, f, cd_loop_mdisp_offset, use_loop_mdisp_flip); } } #define SEL_FLAG 1 #define SEL_ORIG 2 static void bmo_face_flag_set_flush(BMesh *bm, BMFace *f, const short oflag, const bool value) { BMLoop *l_iter; BMLoop *l_first; BMO_face_flag_set(bm, f, oflag, value); l_iter = l_first = BM_FACE_FIRST_LOOP(f); do { BMO_edge_flag_set(bm, l_iter->e, oflag, value); BMO_vert_flag_set(bm, l_iter->v, oflag, value); } while ((l_iter = l_iter->next) != l_first); } static void bmo_region_extend_expand(BMesh *bm, BMOperator *op, const bool use_faces, const bool use_faces_step) { BMOIter siter; if (!use_faces) { BMVert *v; BMO_ITER (v, &siter, op->slots_in, "geom", BM_VERT) { bool found = false; { BMIter eiter; BMEdge *e; BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { if (!BMO_edge_flag_test(bm, e, SEL_ORIG) && !BM_elem_flag_test(e, BM_ELEM_HIDDEN)) { found = true; break; } } } if (found) { if (!use_faces_step) { BMIter eiter; BMEdge *e; BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { if (!BMO_edge_flag_test(bm, e, SEL_FLAG) && !BM_elem_flag_test(e, BM_ELEM_HIDDEN)) { BMO_edge_flag_enable(bm, e, SEL_FLAG); BMO_vert_flag_enable(bm, BM_edge_other_vert(e, v), SEL_FLAG); } } } else { BMIter fiter; BMFace *f; BM_ITER_ELEM (f, &fiter, v, BM_FACES_OF_VERT) { if (!BMO_face_flag_test(bm, f, SEL_FLAG) && !BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { bmo_face_flag_set_flush(bm, f, SEL_FLAG, true); } } /* handle wire edges (when stepping over faces) */ { BMIter eiter; BMEdge *e; BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { if (BM_edge_is_wire(e)) { if (!BMO_edge_flag_test(bm, e, SEL_FLAG) && !BM_elem_flag_test(e, BM_ELEM_HIDDEN)) { BMO_edge_flag_enable(bm, e, SEL_FLAG); BMO_vert_flag_enable(bm, BM_edge_other_vert(e, v), SEL_FLAG); } } } } } } } } else { BMFace *f; BMO_ITER (f, &siter, op->slots_in, "geom", BM_FACE) { BMIter liter; BMLoop *l; BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { if (!use_faces_step) { BMIter fiter; BMFace *f_other; BM_ITER_ELEM (f_other, &fiter, l->e, BM_FACES_OF_EDGE) { if (!BMO_face_flag_test(bm, f_other, SEL_ORIG | SEL_FLAG) && !BM_elem_flag_test(f_other, BM_ELEM_HIDDEN)) { BMO_face_flag_enable(bm, f_other, SEL_FLAG); } } } else { BMIter fiter; BMFace *f_other; BM_ITER_ELEM (f_other, &fiter, l->v, BM_FACES_OF_VERT) { if (!BMO_face_flag_test(bm, f_other, SEL_ORIG | SEL_FLAG) && !BM_elem_flag_test(f_other, BM_ELEM_HIDDEN)) { BMO_face_flag_enable(bm, f_other, SEL_FLAG); } } } } } } } static void bmo_region_extend_contract(BMesh *bm, BMOperator *op, const bool use_faces, const bool use_faces_step) { BMOIter siter; if (!use_faces) { BMVert *v; BMO_ITER (v, &siter, op->slots_in, "geom", BM_VERT) { bool found = false; if (!use_faces_step) { BMIter eiter; BMEdge *e; BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { if (!BMO_edge_flag_test(bm, e, SEL_ORIG)) { found = true; break; } } } else { BMIter fiter; BMFace *f; BM_ITER_ELEM (f, &fiter, v, BM_FACES_OF_VERT) { if (!BMO_face_flag_test(bm, f, SEL_ORIG)) { found = true; break; } } /* handle wire edges (when stepping over faces) */ if (!found) { BMIter eiter; BMEdge *e; BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { if (BM_edge_is_wire(e)) { if (!BMO_edge_flag_test(bm, e, SEL_ORIG)) { found = true; break; } } } } } if (found) { BMIter eiter; BMEdge *e; BMO_vert_flag_enable(bm, v, SEL_FLAG); BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { BMO_edge_flag_enable(bm, e, SEL_FLAG); } } } } else { BMFace *f; BMO_ITER (f, &siter, op->slots_in, "geom", BM_FACE) { BMIter liter; BMLoop *l; BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { if (!use_faces_step) { BMIter fiter; BMFace *f_other; BM_ITER_ELEM (f_other, &fiter, l->e, BM_FACES_OF_EDGE) { if (!BMO_face_flag_test(bm, f_other, SEL_ORIG)) { BMO_face_flag_enable(bm, f, SEL_FLAG); break; } } } else { BMIter fiter; BMFace *f_other; BM_ITER_ELEM (f_other, &fiter, l->v, BM_FACES_OF_VERT) { if (!BMO_face_flag_test(bm, f_other, SEL_ORIG)) { BMO_face_flag_enable(bm, f, SEL_FLAG); break; } } } } } } } void bmo_region_extend_exec(BMesh *bm, BMOperator *op) { const bool use_faces = BMO_slot_bool_get(op->slots_in, "use_faces"); const bool use_face_step = BMO_slot_bool_get(op->slots_in, "use_face_step"); const bool constrict = BMO_slot_bool_get(op->slots_in, "use_contract"); BMO_slot_buffer_flag_enable(bm, op->slots_in, "geom", BM_ALL_NOLOOP, SEL_ORIG); if (constrict) { bmo_region_extend_contract(bm, op, use_faces, use_face_step); } else { bmo_region_extend_expand(bm, op, use_faces, use_face_step); } BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "geom.out", BM_ALL_NOLOOP, SEL_FLAG); } void bmo_smooth_vert_exec(BMesh *UNUSED(bm), BMOperator *op) { BMOIter siter; BMIter iter; BMVert *v; BMEdge *e; float(*cos)[3] = MEM_mallocN(sizeof(*cos) * BMO_slot_buffer_len(op->slots_in, "verts"), __func__); float *co, *co2, clip_dist = BMO_slot_float_get(op->slots_in, "clip_dist"); const float fac = BMO_slot_float_get(op->slots_in, "factor"); int i, j, clipx, clipy, clipz; int xaxis, yaxis, zaxis; clipx = BMO_slot_bool_get(op->slots_in, "mirror_clip_x"); clipy = BMO_slot_bool_get(op->slots_in, "mirror_clip_y"); clipz = BMO_slot_bool_get(op->slots_in, "mirror_clip_z"); xaxis = BMO_slot_bool_get(op->slots_in, "use_axis_x"); yaxis = BMO_slot_bool_get(op->slots_in, "use_axis_y"); zaxis = BMO_slot_bool_get(op->slots_in, "use_axis_z"); i = 0; BMO_ITER (v, &siter, op->slots_in, "verts", BM_VERT) { co = cos[i]; zero_v3(co); j = 0; BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) { co2 = BM_edge_other_vert(e, v)->co; add_v3_v3v3(co, co, co2); j += 1; } if (!j) { copy_v3_v3(co, v->co); i++; continue; } mul_v3_fl(co, 1.0f / (float)j); interp_v3_v3v3(co, v->co, co, fac); if (clipx && fabsf(v->co[0]) <= clip_dist) { co[0] = 0.0f; } if (clipy && fabsf(v->co[1]) <= clip_dist) { co[1] = 0.0f; } if (clipz && fabsf(v->co[2]) <= clip_dist) { co[2] = 0.0f; } i++; } i = 0; BMO_ITER (v, &siter, op->slots_in, "verts", BM_VERT) { if (xaxis) { v->co[0] = cos[i][0]; } if (yaxis) { v->co[1] = cos[i][1]; } if (zaxis) { v->co[2] = cos[i][2]; } i++; } MEM_freeN(cos); } /**************************************************************************** * * Cycle UVs for a face **************************************************************************** */ void bmo_rotate_uvs_exec(BMesh *bm, BMOperator *op) { BMOIter fs_iter; /* selected faces iterator */ BMFace *fs; /* current face */ BMIter l_iter; /* iteration loop */ const bool use_ccw = BMO_slot_bool_get(op->slots_in, "use_ccw"); const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); if (cd_loop_uv_offset != -1) { BMO_ITER (fs, &fs_iter, op->slots_in, "faces", BM_FACE) { if (use_ccw == false) { /* same loops direction */ BMLoop *lf; /* current face loops */ MLoopUV *f_luv; /* first face loop uv */ float p_uv[2]; /* previous uvs */ float t_uv[2]; /* tmp uvs */ int n = 0; BM_ITER_ELEM (lf, &l_iter, fs, BM_LOOPS_OF_FACE) { /* current loop uv is the previous loop uv */ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(lf, cd_loop_uv_offset); if (n == 0) { f_luv = luv; copy_v2_v2(p_uv, luv->uv); } else { copy_v2_v2(t_uv, luv->uv); copy_v2_v2(luv->uv, p_uv); copy_v2_v2(p_uv, t_uv); } n++; } copy_v2_v2(f_luv->uv, p_uv); } else { /* counter loop direction */ BMLoop *lf; /* current face loops */ MLoopUV *p_luv; /* previous loop uv */ MLoopUV *luv; float t_uv[2]; /* current uvs */ int n = 0; BM_ITER_ELEM (lf, &l_iter, fs, BM_LOOPS_OF_FACE) { /* previous loop uv is the current loop uv */ luv = BM_ELEM_CD_GET_VOID_P(lf, cd_loop_uv_offset); if (n == 0) { p_luv = luv; copy_v2_v2(t_uv, luv->uv); } else { copy_v2_v2(p_luv->uv, luv->uv); p_luv = luv; } n++; } copy_v2_v2(luv->uv, t_uv); } } } } /**************************************************************************** * * Reverse UVs for a face **************************************************************************** */ static void bm_face_reverse_uvs(BMFace *f, const int cd_loop_uv_offset) { BMIter iter; BMLoop *l; int i; float(*uvs)[2] = BLI_array_alloca(uvs, f->len); BM_ITER_ELEM_INDEX (l, &iter, f, BM_LOOPS_OF_FACE, i) { MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); copy_v2_v2(uvs[i], luv->uv); } /* now that we have the uvs in the array, reverse! */ BM_ITER_ELEM_INDEX (l, &iter, f, BM_LOOPS_OF_FACE, i) { /* current loop uv is the previous loop uv */ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); copy_v2_v2(luv->uv, uvs[(f->len - i - 1)]); } } void bmo_reverse_uvs_exec(BMesh *bm, BMOperator *op) { BMOIter iter; BMFace *f; const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); if (cd_loop_uv_offset != -1) { BMO_ITER (f, &iter, op->slots_in, "faces", BM_FACE) { bm_face_reverse_uvs(f, cd_loop_uv_offset); } } } /**************************************************************************** * * Cycle colors for a face **************************************************************************** */ static void bmo_get_loop_color_ref(BMesh *bm, int index, int *r_cd_color_offset, int *r_cd_color_type) { Mesh me_query; BKE_id_attribute_copy_domains_temp( ID_ME, &bm->vdata, NULL, &bm->ldata, NULL, NULL, &me_query.id); CustomDataLayer *layer = BKE_id_attribute_from_index( &me_query.id, index, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL); if (!layer || BKE_id_attribute_domain(&me_query.id, layer) != ATTR_DOMAIN_CORNER) { *r_cd_color_offset = -1; return; } int layer_i = CustomData_get_layer_index(&bm->ldata, layer->type); *r_cd_color_offset = bm->ldata.layers[layer_i].offset; *r_cd_color_type = layer->type; } void bmo_rotate_colors_exec(BMesh *bm, BMOperator *op) { BMOIter fs_iter; /* selected faces iterator */ BMFace *fs; /* current face */ BMIter l_iter; /* iteration loop */ const bool use_ccw = BMO_slot_bool_get(op->slots_in, "use_ccw"); const int color_index = BMO_slot_int_get(op->slots_in, "color_index"); int cd_loop_color_offset; int cd_loop_color_type; bmo_get_loop_color_ref(bm, color_index, &cd_loop_color_offset, &cd_loop_color_type); if (cd_loop_color_offset == -1) { BMO_error_raise(bm, op, BMO_ERROR_CANCEL, "color_index is invalid"); return; } const size_t size = cd_loop_color_type == CD_PROP_COLOR ? sizeof(MPropCol) : sizeof(MLoopCol); void *p_col; /* previous color */ void *t_col = alloca(size); /* tmp color */ BMO_ITER (fs, &fs_iter, op->slots_in, "faces", BM_FACE) { if (use_ccw == false) { /* same loops direction */ BMLoop *lf; /* current face loops */ void *f_lcol; /* first face loop color */ int n = 0; BM_ITER_ELEM (lf, &l_iter, fs, BM_LOOPS_OF_FACE) { /* current loop color is the previous loop color */ void *lcol = BM_ELEM_CD_GET_VOID_P(lf, cd_loop_color_offset); if (n == 0) { f_lcol = lcol; p_col = lcol; } else { memcpy(t_col, lcol, size); memcpy(lcol, p_col, size); memcpy(p_col, t_col, size); } n++; } memcpy(f_lcol, p_col, size); } else { /* counter loop direction */ BMLoop *lf; /* current face loops */ void *lcol, *p_lcol; int n = 0; BM_ITER_ELEM (lf, &l_iter, fs, BM_LOOPS_OF_FACE) { /* previous loop color is the current loop color */ lcol = BM_ELEM_CD_GET_VOID_P(lf, cd_loop_color_offset); if (n == 0) { p_lcol = lcol; memcpy(t_col, lcol, size); } else { memcpy(p_lcol, lcol, size); p_lcol = lcol; } n++; } memcpy(lcol, t_col, size); } } } /*************************************************************************** * * Reverse colors for a face *************************************************************************** */ static void bm_face_reverse_colors(BMFace *f, const int cd_loop_color_offset, const int cd_loop_color_type) { BMIter iter; BMLoop *l; int i; const size_t size = cd_loop_color_type == CD_PROP_COLOR ? sizeof(MPropCol) : sizeof(MLoopCol); char *cols = alloca(size * f->len); char *col = cols; BM_ITER_ELEM_INDEX (l, &iter, f, BM_LOOPS_OF_FACE, i) { void *lcol = BM_ELEM_CD_GET_VOID_P(l, cd_loop_color_offset); memcpy((void *)col, lcol, size); col += size; } /* now that we have the uvs in the array, reverse! */ BM_ITER_ELEM_INDEX (l, &iter, f, BM_LOOPS_OF_FACE, i) { /* current loop uv is the previous loop color */ void *lcol = BM_ELEM_CD_GET_VOID_P(l, cd_loop_color_offset); col = cols + (f->len - i - 1) * size; memcpy(lcol, (void *)col, size); } } void bmo_reverse_colors_exec(BMesh *bm, BMOperator *op) { BMOIter iter; BMFace *f; const int color_index = BMO_slot_int_get(op->slots_in, "color_index"); int cd_loop_color_offset; int cd_loop_color_type; bmo_get_loop_color_ref(bm, color_index, &cd_loop_color_offset, &cd_loop_color_type); if (cd_loop_color_offset == -1) { BMO_error_raise(bm, op, BMO_ERROR_CANCEL, "color_index is invalid"); return; } BMO_ITER (f, &iter, op->slots_in, "faces", BM_FACE) { bm_face_reverse_colors(f, cd_loop_color_offset, cd_loop_color_type); } }