Changeset View
Standalone View
source/blender/editors/mesh/editmesh_select.c
| Show All 25 Lines | |||||
| #include "BLI_bitmap.h" | #include "BLI_bitmap.h" | ||||
| #include "BLI_bitmap_draw_2d.h" | #include "BLI_bitmap_draw_2d.h" | ||||
| #include "BLI_listbase.h" | #include "BLI_listbase.h" | ||||
| #include "BLI_linklist.h" | #include "BLI_linklist.h" | ||||
| #include "BLI_linklist_stack.h" | #include "BLI_linklist_stack.h" | ||||
| #include "BLI_math.h" | #include "BLI_math.h" | ||||
| #include "BLI_math_bits.h" | #include "BLI_math_bits.h" | ||||
| #include "BLI_rand.h" | #include "BLI_rand.h" | ||||
| #include "BLI_sort.h" | |||||
| #include "BLI_array.h" | #include "BLI_array.h" | ||||
| #include "BKE_bvhutils.h" | |||||
| #include "BKE_context.h" | #include "BKE_context.h" | ||||
| #include "BKE_report.h" | #include "BKE_report.h" | ||||
| #include "BKE_paint.h" | #include "BKE_paint.h" | ||||
| #include "BKE_editmesh.h" | #include "BKE_editmesh.h" | ||||
| #include "BKE_layer.h" | #include "BKE_layer.h" | ||||
| #include "IMB_imbuf_types.h" | #include "IMB_imbuf_types.h" | ||||
| #include "IMB_imbuf.h" | #include "IMB_imbuf.h" | ||||
| ▲ Show 20 Lines • Show All 144 Lines • ▼ Show 20 Lines | ok = BMO_op_callf(em->bm, | ||||
| hflag, | hflag, | ||||
| scene->toolsettings->doublimit); | scene->toolsettings->doublimit); | ||||
| if (LIKELY(ok) && update) { | if (LIKELY(ok) && update) { | ||||
| EDBM_update_generic(em, true, true); | EDBM_update_generic(em, true, true); | ||||
| } | } | ||||
| } | } | ||||
| struct EDBMSplitEdge { | |||||
| BMVert *v; | |||||
| BMEdge *e; | |||||
| float lambda; | |||||
| }; | |||||
| struct EDBMSplitEdgeData { | |||||
| BMesh *bm; | |||||
campbellbarton: Could use `BM_vert_pair_share_face_check_cb`. | |||||
| BMEdge *r_edge; | |||||
| float r_lambda; | |||||
| }; | |||||
Done Inline ActionsThis isn't checking if the face is hidden. campbellbarton: This isn't checking if the face is hidden. | |||||
| static bool embm_face_split_vert_pair(BMesh *bm, BMVert *v_a, BMVert *v_b) | |||||
Done Inline Actionsthis can be made into a utility function similar to BM_vert_pair_share_face_check_cb Would also pick the *best* face since this will cause an error with edges between concave faces, which may also be joined by an adjacent face which the edge crosses. There are a few ways this could be tested, one would be to calculate two normals on either side of the vertices with BM_face_calc_normal_subset, if they're flipped - the edge runs between concave part of a face and can be skipped, we could also check that this edge doesn't cross any edges of the face - although not sure that's needed. campbellbarton: this can be made into a utility function similar to `BM_vert_pair_share_face_check_cb`
Would… | |||||
Done Inline ActionsThis is no longer used. campbellbarton: This is no longer used. | |||||
| { | |||||
| if (v_a->e && v_b->e) { | |||||
| BMIter iter; | |||||
| BMLoop *l1, *l2; | |||||
| BM_ITER_ELEM (l1, &iter, v_a, BM_LOOPS_OF_VERT) { | |||||
| if (!BM_elem_flag_test(l1->f, BM_ELEM_HIDDEN)) { | |||||
| BMIter iter2; | |||||
| BM_ITER_ELEM (l2, &iter2, v_b, BM_LOOPS_OF_VERT) { | |||||
| if (l1->f == l2->f) { | |||||
| BM_face_split(bm, l1->f, l1, l2, NULL, NULL, true); | |||||
| return true; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| return false; | |||||
| } | |||||
| static bool edbm_automerge_check_and_split_faces(BMesh *bm, BMVert *v_src, BMVert *v_dst) | |||||
Done Inline Actionsshould be static. campbellbarton: should be static. | |||||
Done Inline Actionsf isn't used,. use UNUSED(f) campbellbarton: f isn't used,. use `UNUSED(f)` | |||||
| { | |||||
| BMIter iter; | |||||
| BMEdge *e_iter; | |||||
| BM_ITER_ELEM (e_iter, &iter, v_src, BM_EDGES_OF_VERT) { | |||||
| BMVert *vert_other = BM_edge_other_vert(e_iter, v_src); | |||||
| if (vert_other != v_dst && !BM_edge_exists(vert_other, v_dst) && | |||||
| embm_face_split_vert_pair(bm, vert_other, v_dst)) { | |||||
| return true; | |||||
| } | |||||
| } | |||||
| return false; | |||||
| } | |||||
| static void ebbm_automerge_and_split_find_duplicate_cb(void *userdata, | |||||
| int index, | |||||
| const float co[3], | |||||
| BVHTreeNearest *nearest) | |||||
| { | |||||
| struct EDBMSplitEdgeData *data = userdata; | |||||
| BMEdge *e = BM_edge_at_index(data->bm, index); | |||||
| float lambda = line_point_factor_v3_ex(co, e->v1->co, e->v2->co, 0.0f, -1.0f); | |||||
| if (IN_RANGE(lambda, 0.0f, 1.0f)) { | |||||
| float near_co[3]; | |||||
| interp_v3_v3v3(near_co, e->v1->co, e->v2->co, lambda); | |||||
| float dist_sq = len_squared_v3v3(near_co, co); | |||||
| if (dist_sq < nearest->dist_sq) { | |||||
| nearest->dist_sq = dist_sq; | |||||
| nearest->index = index; | |||||
| data->r_edge = e; | |||||
| data->r_lambda = lambda; | |||||
| } | |||||
| } | |||||
| } | |||||
| static int edbm_automerge_and_split_sort_cmp_by_keys_cb(const void *index1_v, | |||||
| const void *index2_v, | |||||
| void *keys_v) | |||||
| { | |||||
| const struct EDBMSplitEdge *cuts = keys_v; | |||||
| const int *index1 = (int *)index1_v; | |||||
| const int *index2 = (int *)index2_v; | |||||
| if (cuts[*index1].lambda > cuts[*index2].lambda) { | |||||
| return 1; | |||||
| } | |||||
| else { | |||||
| return -1; | |||||
| } | |||||
| } | |||||
| void EDBM_automerge_and_split( | |||||
| Scene *scene, Object *obedit, bool split_edges, bool update, const char hflag) | |||||
| { | |||||
| BMEditMesh *em = BKE_editmesh_from_object(obedit); | |||||
| //#define COMPARE_TIME | |||||
Done Inline ActionsWould call this DEBUG_TIME (typically used elsewhere). campbellbarton: Would call this `DEBUG_TIME` (typically used elsewhere). | |||||
| #ifdef COMPARE_TIME | |||||
| # include "PIL_time.h" | |||||
Done Inline ActionsEven with ifdef's like this, they should be outside the function otherwise adding inline function could cause an error - for eg. campbellbarton: Even with ifdef's like this, they should be outside the function otherwise adding inline… | |||||
| BMesh *bm_copy = BM_mesh_copy(em->bm); | |||||
| double t1 = PIL_check_seconds_timer(); | |||||
| BMO_op_callf(bm_copy, | |||||
| BMO_FLAG_DEFAULTS, | |||||
| "automerge verts=%hv dist=%f", | |||||
| hflag, | |||||
| scene->toolsettings->doublimit); | |||||
| t1 = PIL_check_seconds_timer() - t1; | |||||
| BM_mesh_free(bm_copy); | |||||
| double t2 = PIL_check_seconds_timer(); | |||||
| #endif | |||||
| bool ok = false; | |||||
| BMOperator findop, weldop; | |||||
| BMOpSlot *slot_targetmap; | |||||
| BMesh *bm = em->bm; | |||||
| float dist = scene->toolsettings->doublimit; | |||||
| BMIter iter; | |||||
| BMVert *v; | |||||
| /* tag and count the verts to be tested. */ | |||||
| BM_mesh_elem_toolflags_ensure(bm); | |||||
| int verts_len = 0; | |||||
| BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { | |||||
| if (BM_elem_flag_test(v, hflag)) { | |||||
| BM_elem_flag_enable(v, BM_ELEM_TAG); | |||||
| BMO_vert_flag_enable(bm, v, BMO_ELE_TAG); | |||||
| verts_len++; | |||||
| } | |||||
| else { | |||||
| BM_elem_flag_disable(v, BM_ELEM_TAG); | |||||
| } | |||||
| } | |||||
| /* Search for doubles among all vertices, but only merge non-BMO_ELE_TAG | |||||
| * vertices into BMO_ELE_TAG vertices. */ | |||||
| BMO_op_initf(bm, &findop, 0, "find_doubles verts=%av keep_verts=%Fv dist=%f", BMO_ELE_TAG, dist); | |||||
| BMO_op_exec(bm, &findop); | |||||
| /* Init weld_verts operator to later fill the targetmap. */ | |||||
| BMO_op_init(bm, &weldop, 0, "weld_verts"); | |||||
| BMO_slot_copy(&findop, slots_out, "targetmap.out", &weldop, slots_in, "targetmap"); | |||||
| slot_targetmap = BMO_slot_get(weldop.slots_in, "targetmap"); | |||||
| /* Remove duplicate vertices from the split edge test and check and split faces. */ | |||||
| GHashIterator gh_iter; | |||||
| GHash *ghash_targetmap = BMO_SLOT_AS_GHASH(slot_targetmap); | |||||
| GHASH_ITER (gh_iter, ghash_targetmap) { | |||||
| v = BLI_ghashIterator_getKey(&gh_iter); | |||||
| BMVert *v_dst = BLI_ghashIterator_getValue(&gh_iter); | |||||
| if (!BM_elem_flag_test(v, BM_ELEM_TAG)) { | |||||
| /* Should this happen? */ | |||||
| SWAP(BMVert *, v, v_dst); | |||||
| } | |||||
| BLI_assert(BM_elem_flag_test(v, BM_ELEM_TAG)); | |||||
| BM_elem_flag_disable(v, BM_ELEM_TAG); | |||||
| edbm_automerge_check_and_split_faces(bm, v, v_dst); | |||||
| ok = true; | |||||
| verts_len--; | |||||
| } | |||||
| int totedge = bm->totedge; | |||||
| if (totedge == 0 || verts_len == 0) { | |||||
| split_edges = false; | |||||
| } | |||||
| if (split_edges) { | |||||
| /* Count and tag edges. */ | |||||
| BMEdge *e; | |||||
| int edges_len = 0; | |||||
| BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { | |||||
| if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN) && !BM_elem_flag_test(e->v1, BM_ELEM_TAG) && | |||||
| !BM_elem_flag_test(e->v2, BM_ELEM_TAG)) { | |||||
| BM_elem_flag_enable(e, BM_ELEM_TAG); | |||||
| edges_len++; | |||||
| } | |||||
| else { | |||||
| BM_elem_flag_disable(e, BM_ELEM_TAG); | |||||
| } | |||||
| } | |||||
Done Inline ActionsPrefer to avoid adding numbers onto variable names and instead name them in a way that tells us how it's different. eg: cuts_iter_other or cuts_iter_adjacent. campbellbarton: Prefer to avoid adding numbers onto variable names and instead name them in a way that tells us… | |||||
| if (edges_len) { | |||||
| /* Create a BVHTree of edges with `dist` as epsilon. */ | |||||
| BVHTree *tree_edges = BLI_bvhtree_new(edges_len, dist, 2, 6); | |||||
| int i; | |||||
| BM_ITER_MESH_INDEX (e, &iter, bm, BM_EDGES_OF_MESH, i) { | |||||
| if (BM_elem_flag_test(e, BM_ELEM_TAG)) { | |||||
| float co[2][3]; | |||||
| copy_v3_v3(co[0], e->v1->co); | |||||
| copy_v3_v3(co[1], e->v2->co); | |||||
| BLI_bvhtree_insert(tree_edges, i, co[0], 2); | |||||
| /* Use `e->head.index` to count intersections. */ | |||||
| e->head.index = 0; | |||||
| } | |||||
| } | |||||
| BLI_bvhtree_balance(tree_edges); | |||||
| struct EDBMSplitEdge *cuts_iter, *cuts; | |||||
| /* Store all intersections in this array. */ | |||||
| cuts = MEM_mallocN(verts_len * sizeof(*cuts), __func__); | |||||
| cuts_iter = &cuts[0]; | |||||
| int cuts_len = 0; | |||||
| int cut_edges_len = 0; | |||||
| float dist_sq = SQUARE(dist); | |||||
| struct EDBMSplitEdgeData data = {bm}; | |||||
| /* Start the search for intersections. */ | |||||
| BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { | |||||
| if (BM_elem_flag_test(v, BM_ELEM_TAG)) { | |||||
| float co[3]; | |||||
| copy_v3_v3(co, v->co); | |||||
| int e_index = BLI_bvhtree_find_nearest_first( | |||||
| tree_edges, co, dist_sq, ebbm_automerge_and_split_find_duplicate_cb, &data); | |||||
| if (e_index != -1) { | |||||
| e = data.r_edge; | |||||
| e->head.index++; | |||||
| cuts_iter->v = v; | |||||
| cuts_iter->e = e; | |||||
| cuts_iter->lambda = data.r_lambda; | |||||
| cuts_iter++; | |||||
| cuts_len++; | |||||
| if (BM_elem_flag_test(e, BM_ELEM_TAG)) { | |||||
| BM_elem_flag_disable(e, BM_ELEM_TAG); | |||||
| cut_edges_len++; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| BLI_bvhtree_free(tree_edges); | |||||
| if (cuts_len) { | |||||
| /* Map intersections per edge. */ | |||||
| union { | |||||
| struct { | |||||
| int cuts_len; | |||||
| int cuts_index[]; | |||||
| }; | |||||
| int as_int[]; | |||||
| } * e_map_iter, *e_map; | |||||
| e_map = MEM_mallocN((cut_edges_len * sizeof(*e_map)) + | |||||
| (cuts_len * sizeof(*(e_map->cuts_index))), | |||||
| __func__); | |||||
| int map_len = 0; | |||||
| cuts_iter = &cuts[0]; | |||||
Done Inline ActionsSingle . campbellbarton: Single `.` | |||||
| for (i = 0; i < cuts_len; i++, cuts_iter++) { | |||||
| e = cuts_iter->e; | |||||
| if (!BM_elem_flag_test(e, BM_ELEM_TAG)) { | |||||
| BM_elem_flag_enable(e, BM_ELEM_TAG); | |||||
| int cuts_len = e->head.index; | |||||
| e_map_iter = (void *)&e_map->as_int[map_len]; | |||||
Done Inline ActionsThis gives me: /src/blender/source/blender/editors/mesh/editmesh_select.c: In function ‘EDBM_automerge_and_split’: /src/blender/source/blender/editors/mesh/editmesh_select.c:456:15: error: flexible array member in union 456 | int as_int[]; use int as_int[0]; campbellbarton: This gives me:
```
/src/blender/source/blender/editors/mesh/editmesh_select.c: In function… | |||||
| e_map_iter->cuts_len = cuts_len; | |||||
| e_map_iter->cuts_index[0] = i; | |||||
| /* Use `e->head.index` to indicate which slot to fill with the `cuts` index. */ | |||||
| e->head.index = map_len + 1; | |||||
| map_len += 1 + cuts_len; | |||||
| } | |||||
| else { | |||||
| e_map->as_int[++e->head.index] = i; | |||||
| } | |||||
| } | |||||
| /* Split Edges and Faces.. */ | |||||
Done Inline ActionsShadows previous value, needs to be named differently. campbellbarton: Shadows previous value, needs to be named differently. | |||||
| for (i = 0; i < map_len; | |||||
| e_map_iter = (void *)&e_map->as_int[i], i += 1 + e_map_iter->cuts_len) { | |||||
| /* sort by lambda! */ | |||||
| BLI_qsort_r(e_map_iter->cuts_index, | |||||
| e_map_iter->cuts_len, | |||||
| sizeof(*(e_map->cuts_index)), | |||||
| edbm_automerge_and_split_sort_cmp_by_keys_cb, | |||||
| cuts); | |||||
| float lambda, lambda_prev = 0.0f; | |||||
| for (int j = 0; j < e_map_iter->cuts_len; j++) { | |||||
| cuts_iter = &cuts[e_map_iter->cuts_index[j]]; | |||||
| lambda = (cuts_iter->lambda - lambda_prev) / (1.0f - lambda_prev); | |||||
| lambda_prev = cuts_iter->lambda; | |||||
| v = cuts_iter->v; | |||||
| e = cuts_iter->e; | |||||
| BMVert *v_new = BM_edge_split(bm, e, e->v1, NULL, lambda); | |||||
| edbm_automerge_check_and_split_faces(bm, v, v_new); | |||||
| BMO_slot_map_elem_insert(&weldop, slot_targetmap, v_new, v); | |||||
| } | |||||
| } | |||||
| ok = true; | |||||
| MEM_freeN(e_map); | |||||
| } | |||||
| MEM_freeN(cuts); | |||||
| } | |||||
| } | |||||
| BMO_op_exec(bm, &weldop); | |||||
| BMO_op_finish(bm, &findop); | |||||
| BMO_op_finish(bm, &weldop); | |||||
| #ifdef COMPARE_TIME | |||||
| t2 = PIL_check_seconds_timer() - t2; | |||||
| printf("t1: %lf; t2: %lf; fac: %lf\n", t1, t2, t1 / t2); | |||||
| #endif | |||||
| if (LIKELY(ok) && update) { | |||||
| EDBM_update_generic(em, true, true); | |||||
| } | |||||
| } | |||||
| /** \} */ | /** \} */ | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name Back-Buffer OpenGL Selection | /** \name Back-Buffer OpenGL Selection | ||||
| * \{ */ | * \{ */ | ||||
| static BMElem *edbm_select_id_bm_elem_get(Base **bases, const uint sel_id, uint *r_base_index) | static BMElem *edbm_select_id_bm_elem_get(Base **bases, const uint sel_id, uint *r_base_index) | ||||
| { | { | ||||
| ▲ Show 20 Lines • Show All 4,607 Lines • Show Last 20 Lines | |||||
Could use BM_vert_pair_share_face_check_cb.