diff options
Diffstat (limited to 'source')
-rw-r--r-- | source/blender/blenkernel/BKE_mesh.h | 7 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/mesh_evaluate.c | 215 | ||||
-rw-r--r-- | source/blender/bmesh/intern/bmesh_mesh.c | 56 | ||||
-rw-r--r-- | source/blender/bmesh/intern/bmesh_mesh.h | 2 | ||||
-rw-r--r-- | source/blender/editors/mesh/mesh_data.c | 29 | ||||
-rw-r--r-- | source/blender/editors/space_action/action_select.c | 4 | ||||
-rw-r--r-- | source/blender/editors/transform/transform_conversions.c | 39 |
7 files changed, 260 insertions, 92 deletions
diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index ce5d4e3ea64..9ff842e36f1 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -194,6 +194,13 @@ void BKE_mesh_calc_normals_looptri( const struct MLoopTri *looptri, int looptri_num, float (*r_tri_nors)[3]); +void BKE_edges_sharp_from_angle_set( + const struct MVert *mverts, const int numVerts, + struct MEdge *medges, const int numEdges, + struct MLoop *mloops, const int numLoops, + struct MPoly *mpolys, const float (*polynors)[3], const int numPolys, + const float split_angle); + /** * References a contiguous loop-fan with normal offset vars. */ diff --git a/source/blender/blenkernel/intern/mesh_evaluate.c b/source/blender/blenkernel/intern/mesh_evaluate.c index a3469ecd5cd..bf96f5db818 100644 --- a/source/blender/blenkernel/intern/mesh_evaluate.c +++ b/source/blender/blenkernel/intern/mesh_evaluate.c @@ -680,10 +680,11 @@ typedef struct LoopSplitTaskDataCommon { const MEdge *medges; const MLoop *mloops; const MPoly *mpolys; - const int (*edge_to_loops)[2]; - const int *loop_to_poly; + int (*edge_to_loops)[2]; + int *loop_to_poly; const float (*polynors)[3]; + int numEdges; int numLoops; int numPolys; } LoopSplitTaskDataCommon; @@ -693,6 +694,154 @@ typedef struct LoopSplitTaskDataCommon { /* See comment about edge_to_loops below. */ #define IS_EDGE_SHARP(_e2l) (ELEM((_e2l)[1], INDEX_UNSET, INDEX_INVALID)) +static void mesh_edges_sharp_tag( + LoopSplitTaskDataCommon *data, + const bool check_angle, const float split_angle, const bool do_sharp_edges_tag) +{ + MVert *mverts = data->mverts; + MEdge *medges = data->medges; + MLoop *mloops = data->mloops; + MPoly *mpolys = data->mpolys; + + const int numEdges = data->numEdges; + const int numPolys = data->numPolys; + + float (*loopnors)[3] = data->loopnors; /* Note: loopnors may be NULL here. */ + const float (*polynors)[3] = data->polynors; + + int (*edge_to_loops)[2] = data->edge_to_loops; + int *loop_to_poly = data->loop_to_poly; + + BLI_bitmap *sharp_edges = do_sharp_edges_tag ? BLI_BITMAP_NEW(numEdges, __func__) : NULL; + + MPoly *mp; + int mp_index; + + const float split_angle_cos = check_angle ? cosf(split_angle) : -1.0f; + + for (mp = mpolys, mp_index = 0; mp_index < numPolys; mp++, mp_index++) { + MLoop *ml_curr; + int *e2l; + int ml_curr_index = mp->loopstart; + const int ml_last_index = (ml_curr_index + mp->totloop) - 1; + + ml_curr = &mloops[ml_curr_index]; + + for (; ml_curr_index <= ml_last_index; ml_curr++, ml_curr_index++) { + MEdge *me = &medges[ml_curr->e]; + + e2l = edge_to_loops[ml_curr->e]; + + loop_to_poly[ml_curr_index] = mp_index; + + /* Pre-populate all loop normals as if their verts were all-smooth, this way we don't have to compute + * those later! + */ + if (loopnors) { + normal_short_to_float_v3(loopnors[ml_curr_index], mverts[ml_curr->v].no); + } + + /* Check whether current edge might be smooth or sharp */ + if ((e2l[0] | e2l[1]) == 0) { + /* 'Empty' edge until now, set e2l[0] (and e2l[1] to INDEX_UNSET to tag it as unset). */ + e2l[0] = ml_curr_index; + /* We have to check this here too, else we might miss some flat faces!!! */ + e2l[1] = (mp->flag & ME_SMOOTH) ? INDEX_UNSET : INDEX_INVALID; + } + else if (e2l[1] == INDEX_UNSET) { + const bool is_angle_sharp = (check_angle && + dot_v3v3(polynors[loop_to_poly[e2l[0]]], polynors[mp_index]) < split_angle_cos); + + /* Second loop using this edge, time to test its sharpness. + * An edge is sharp if it is tagged as such, or its face is not smooth, + * or both poly have opposed (flipped) normals, i.e. both loops on the same edge share the same vertex, + * or angle between both its polys' normals is above split_angle value. + */ + if (!(mp->flag & ME_SMOOTH) || (me->flag & ME_SHARP) || + ml_curr->v == mloops[e2l[0]].v || + is_angle_sharp) + { + /* Note: we are sure that loop != 0 here ;) */ + e2l[1] = INDEX_INVALID; + + /* We want to avoid tagging edges as sharp when it is already defined as such by + * other causes than angle threshold... */ + if (do_sharp_edges_tag && is_angle_sharp) { + BLI_BITMAP_SET(sharp_edges, ml_curr->e, true); + } + } + else { + e2l[1] = ml_curr_index; + } + } + else if (!IS_EDGE_SHARP(e2l)) { + /* More than two loops using this edge, tag as sharp if not yet done. */ + e2l[1] = INDEX_INVALID; + + /* We want to avoid tagging edges as sharp when it is already defined as such by + * other causes than angle threshold... */ + if (do_sharp_edges_tag) { + BLI_BITMAP_SET(sharp_edges, ml_curr->e, false); + } + } + /* Else, edge is already 'disqualified' (i.e. sharp)! */ + } + } + + /* If requested, do actual tagging of edges as sharp in another loop. */ + if (do_sharp_edges_tag) { + MEdge *me; + int me_index; + for (me = medges, me_index = 0; me_index < numEdges; me++, me_index++) { + if (BLI_BITMAP_TEST(sharp_edges, me_index)) { + me->flag |= ME_SHARP; + } + } + + MEM_freeN(sharp_edges); + } +} + +/** Define sharp edges as needed to mimic 'autosmooth' from angle threshold. + * + * Used when defining an empty custom loop normals data layer, to keep same shading as with autosmooth! + */ +void BKE_edges_sharp_from_angle_set( + const struct MVert *mverts, const int UNUSED(numVerts), + struct MEdge *medges, const int numEdges, + struct MLoop *mloops, const int numLoops, + struct MPoly *mpolys, const float (*polynors)[3], const int numPolys, + const float split_angle) +{ + if (split_angle >= (float)M_PI) { + /* Nothing to do! */ + return; + } + + /* Mapping edge -> loops. See BKE_mesh_normals_loop_split() for details. */ + int (*edge_to_loops)[2] = MEM_calloc_arrayN((size_t)numEdges, sizeof(*edge_to_loops), __func__); + + /* Simple mapping from a loop to its polygon index. */ + int *loop_to_poly = MEM_malloc_arrayN((size_t)numLoops, sizeof(*loop_to_poly), __func__); + + LoopSplitTaskDataCommon common_data = { + .mverts = mverts, + .medges = medges, + .mloops = mloops, + .mpolys = mpolys, + .edge_to_loops = edge_to_loops, + .loop_to_poly = loop_to_poly, + .polynors = polynors, + .numEdges = numEdges, + .numPolys = numPolys, + }; + + mesh_edges_sharp_tag(&common_data, true, split_angle, true); + + MEM_freeN(edge_to_loops); + MEM_freeN(loop_to_poly); +} + static void loop_manifold_fan_around_vert_next( const MLoop *mloops, const MPoly *mpolys, const int *loop_to_poly, const int *e2lfan_curr, const uint mv_pivot_index, @@ -1313,9 +1462,6 @@ void BKE_mesh_normals_loop_split( /* Simple mapping from a loop to its polygon index. */ int *loop_to_poly = r_loop_to_poly ? r_loop_to_poly : MEM_malloc_arrayN((size_t)numLoops, sizeof(*loop_to_poly), __func__); - MPoly *mp; - int mp_index; - /* When using custom loop normals, disable the angle feature! */ const bool check_angle = (split_angle < (float)M_PI) && (clnors_data == NULL); @@ -1325,8 +1471,6 @@ void BKE_mesh_normals_loop_split( TIMEIT_START_AVERAGED(BKE_mesh_normals_loop_split); #endif - const float split_angle_cos = check_angle ? cosf(split_angle) : -1.0f; - if (!r_lnors_spacearr && clnors_data) { /* We need to compute lnor spacearr if some custom lnor data are given to us! */ r_lnors_spacearr = &_lnors_spacearr; @@ -1335,57 +1479,6 @@ void BKE_mesh_normals_loop_split( BKE_lnor_spacearr_init(r_lnors_spacearr, numLoops); } - /* This first loop check which edges are actually smooth, and compute edge vectors. */ - for (mp = mpolys, mp_index = 0; mp_index < numPolys; mp++, mp_index++) { - MLoop *ml_curr; - int *e2l; - int ml_curr_index = mp->loopstart; - const int ml_last_index = (ml_curr_index + mp->totloop) - 1; - - ml_curr = &mloops[ml_curr_index]; - - for (; ml_curr_index <= ml_last_index; ml_curr++, ml_curr_index++) { - e2l = edge_to_loops[ml_curr->e]; - - loop_to_poly[ml_curr_index] = mp_index; - - /* Pre-populate all loop normals as if their verts were all-smooth, this way we don't have to compute - * those later! - */ - normal_short_to_float_v3(r_loopnors[ml_curr_index], mverts[ml_curr->v].no); - - /* Check whether current edge might be smooth or sharp */ - if ((e2l[0] | e2l[1]) == 0) { - /* 'Empty' edge until now, set e2l[0] (and e2l[1] to INDEX_UNSET to tag it as unset). */ - e2l[0] = ml_curr_index; - /* We have to check this here too, else we might miss some flat faces!!! */ - e2l[1] = (mp->flag & ME_SMOOTH) ? INDEX_UNSET : INDEX_INVALID; - } - else if (e2l[1] == INDEX_UNSET) { - /* Second loop using this edge, time to test its sharpness. - * An edge is sharp if it is tagged as such, or its face is not smooth, - * or both poly have opposed (flipped) normals, i.e. both loops on the same edge share the same vertex, - * or angle between both its polys' normals is above split_angle value. - */ - if (!(mp->flag & ME_SMOOTH) || (medges[ml_curr->e].flag & ME_SHARP) || - ml_curr->v == mloops[e2l[0]].v || - (check_angle && dot_v3v3(polynors[loop_to_poly[e2l[0]]], polynors[mp_index]) < split_angle_cos)) - { - /* Note: we are sure that loop != 0 here ;) */ - e2l[1] = INDEX_INVALID; - } - else { - e2l[1] = ml_curr_index; - } - } - else if (!IS_EDGE_SHARP(e2l)) { - /* More than two loops using this edge, tag as sharp if not yet done. */ - e2l[1] = INDEX_INVALID; - } - /* Else, edge is already 'disqualified' (i.e. sharp)! */ - } - } - /* Init data common to all tasks. */ LoopSplitTaskDataCommon common_data = { .lnors_spacearr = r_lnors_spacearr, @@ -1395,13 +1488,17 @@ void BKE_mesh_normals_loop_split( .medges = medges, .mloops = mloops, .mpolys = mpolys, - .edge_to_loops = (const int(*)[2])edge_to_loops, + .edge_to_loops = edge_to_loops, .loop_to_poly = loop_to_poly, .polynors = polynors, + .numEdges = numEdges, .numLoops = numLoops, .numPolys = numPolys, }; + /* This first loop check which edges are actually smooth, and compute edge vectors. */ + mesh_edges_sharp_tag(&common_data, check_angle, split_angle, false); + if (numLoops < LOOP_SPLIT_TASK_BLOCK_SIZE * 8) { /* Not enough loops to be worth the whole threading overhead... */ loop_split_generator(NULL, &common_data); diff --git a/source/blender/bmesh/intern/bmesh_mesh.c b/source/blender/bmesh/intern/bmesh_mesh.c index 8533083af22..d178b48ab60 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.c +++ b/source/blender/bmesh/intern/bmesh_mesh.c @@ -524,18 +524,16 @@ void BM_verts_calc_normal_vcos(BMesh *bm, const float (*fnos)[3], const float (* * Helpers for #BM_mesh_loop_normals_update and #BM_loops_calc_normals_vnos */ static void bm_mesh_edges_sharp_tag( - BMesh *bm, const float (*vnos)[3], const float (*fnos)[3], float split_angle, - float (*r_lnos)[3]) + BMesh *bm, + const float (*vnos)[3], const float (*fnos)[3], float (*r_lnos)[3], + const float split_angle, const bool do_sharp_edges_tag) { BMIter eiter; BMEdge *e; int i; const bool check_angle = (split_angle < (float)M_PI); - - if (check_angle) { - split_angle = cosf(split_angle); - } + const float split_angle_cos = check_angle ? cosf(split_angle) : -1.0f; { char htype = BM_VERT | BM_LOOP; @@ -560,7 +558,7 @@ static void bm_mesh_edges_sharp_tag( if (check_angle) { const float *no_a = fnos ? fnos[BM_elem_index_get(l_a->f)] : l_a->f->no; const float *no_b = fnos ? fnos[BM_elem_index_get(l_b->f)] : l_b->f->no; - is_angle_smooth = (dot_v3v3(no_a, no_b) >= split_angle); + is_angle_smooth = (dot_v3v3(no_a, no_b) >= split_angle_cos); } /* We only tag edges that are *really* smooth: @@ -570,20 +568,28 @@ static void bm_mesh_edges_sharp_tag( * and both its faces have compatible (non-flipped) normals, * i.e. both loops on the same edge do not share the same vertex. */ - if (is_angle_smooth && - BM_elem_flag_test(e, BM_ELEM_SMOOTH) && + if (BM_elem_flag_test(e, BM_ELEM_SMOOTH) && BM_elem_flag_test(l_a->f, BM_ELEM_SMOOTH) && BM_elem_flag_test(l_b->f, BM_ELEM_SMOOTH) && l_a->v != l_b->v) { - const float *no; - BM_elem_flag_enable(e, BM_ELEM_TAG); - - /* linked vertices might be fully smooth, copy their normals to loop ones. */ - no = vnos ? vnos[BM_elem_index_get(l_a->v)] : l_a->v->no; - copy_v3_v3(r_lnos[BM_elem_index_get(l_a)], no); - no = vnos ? vnos[BM_elem_index_get(l_b->v)] : l_b->v->no; - copy_v3_v3(r_lnos[BM_elem_index_get(l_b)], no); + if (is_angle_smooth) { + const float *no; + BM_elem_flag_enable(e, BM_ELEM_TAG); + + /* linked vertices might be fully smooth, copy their normals to loop ones. */ + if (r_lnos) { + no = vnos ? vnos[BM_elem_index_get(l_a->v)] : l_a->v->no; + copy_v3_v3(r_lnos[BM_elem_index_get(l_a)], no); + no = vnos ? vnos[BM_elem_index_get(l_b->v)] : l_b->v->no; + copy_v3_v3(r_lnos[BM_elem_index_get(l_b)], no); + } + } + else if (do_sharp_edges_tag) { + /* Note that we do not care about the other sharp-edge cases (sharp poly, non-manifold edge, etc.), + * only tag edge as sharp when it is due to angle threashold. */ + BM_elem_flag_disable(e, BM_ELEM_SMOOTH); + } } } } @@ -1008,7 +1014,7 @@ void BM_loops_calc_normal_vcos( if (use_split_normals) { /* Tag smooth edges and set lnos from vnos when they might be completely smooth... * When using custom loop normals, disable the angle feature! */ - bm_mesh_edges_sharp_tag(bm, vnos, fnos, has_clnors ? (float)M_PI : split_angle, r_lnos); + bm_mesh_edges_sharp_tag(bm, vnos, fnos, r_lnos, has_clnors ? (float)M_PI : split_angle, false); /* Finish computing lnos by accumulating face normals in each fan of faces defined by sharp edges. */ bm_mesh_loops_calc_normals(bm, vcos, fnos, r_lnos, r_lnors_spacearr, clnors_data, cd_loop_clnors_offset); @@ -1019,6 +1025,20 @@ void BM_loops_calc_normal_vcos( } } +/** Define sharp edges as needed to mimic 'autosmooth' from angle threshold. + * + * Used when defining an empty custom loop normals data layer, to keep same shading as with autosmooth! + */ +void BM_edges_sharp_from_angle_set(BMesh *bm, const float split_angle) +{ + if (split_angle >= (float)M_PI) { + /* Nothing to do! */ + return; + } + + bm_mesh_edges_sharp_tag(bm, NULL, NULL, NULL, split_angle, true); +} + static void UNUSED_FUNCTION(bm_mdisps_space_set)( Object *ob, BMesh *bm, int from, int to, eObjectMode object_mode) { diff --git a/source/blender/bmesh/intern/bmesh_mesh.h b/source/blender/bmesh/intern/bmesh_mesh.h index 01f11f6f942..8326e82af00 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.h +++ b/source/blender/bmesh/intern/bmesh_mesh.h @@ -52,6 +52,8 @@ void BM_loops_calc_normal_vcos( const bool use_split_normals, const float split_angle, float (*r_lnos)[3], struct MLoopNorSpaceArray *r_lnors_spacearr, short (*clnors_data)[2], const int cd_loop_clnors_offset); +void BM_edges_sharp_from_angle_set(BMesh *bm, const float split_angle); + void bmesh_edit_begin(BMesh *bm, const BMOpTypeFlag type_flag); void bmesh_edit_end(BMesh *bm, const BMOpTypeFlag type_flag); diff --git a/source/blender/editors/mesh/mesh_data.c b/source/blender/editors/mesh/mesh_data.c index bd3d2f652c0..305c18f5b62 100644 --- a/source/blender/editors/mesh/mesh_data.c +++ b/source/blender/editors/mesh/mesh_data.c @@ -882,9 +882,38 @@ static int mesh_customdata_custom_splitnormals_add_exec(bContext *C, wmOperator CustomData *data = GET_CD_DATA(me, ldata); if (me->edit_btmesh) { + /* Tag edges as sharp according to smooth threshold if needed, to preserve autosmooth shading. */ + if (me->flag & ME_AUTOSMOOTH) { + BM_edges_sharp_from_angle_set(me->edit_btmesh->bm, me->smoothresh); + + me->drawflag |= ME_DRAWSHARP; + } + BM_data_layer_add(me->edit_btmesh->bm, data, CD_CUSTOMLOOPNORMAL); } else { + /* Tag edges as sharp according to smooth threshold if needed, to preserve autosmooth shading. */ + if (me->flag & ME_AUTOSMOOTH) { + float (*polynors)[3] = MEM_mallocN(sizeof(*polynors) * (size_t)me->totpoly, __func__); + + BKE_mesh_calc_normals_poly( + me->mvert, NULL, me->totvert, + me->mloop, me->mpoly, + me->totloop, me->totpoly, + polynors, true); + + BKE_edges_sharp_from_angle_set( + me->mvert, me->totvert, + me->medge, me->totedge, + me->mloop, me->totloop, + me->mpoly, polynors, me->totpoly, + me->smoothresh); + + MEM_freeN(polynors); + + me->drawflag |= ME_DRAWSHARP; + } + CustomData_add_layer(data, CD_CUSTOMLOOPNORMAL, CD_DEFAULT, NULL, me->totloop); } diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c index 3346c628336..2b974ac73d7 100644 --- a/source/blender/editors/space_action/action_select.c +++ b/source/blender/editors/space_action/action_select.c @@ -220,7 +220,7 @@ static void borderselect_action(bAnimContext *ac, const rcti rect, short mode, s UI_view2d_region_to_view(v2d, rect.xmax, rect.ymax - 2, &rectf.xmax, &rectf.ymax); /* filter data */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* get beztriple editing/validation funcs */ @@ -401,7 +401,7 @@ static void region_select_action_keys(bAnimContext *ac, const rctf *rectf_view, UI_view2d_region_to_view_rctf(v2d, rectf_view, &rectf); /* filter data */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* get beztriple editing/validation funcs */ diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c index 9c3ed801381..d73d5e82d0f 100644 --- a/source/blender/editors/transform/transform_conversions.c +++ b/source/blender/editors/transform/transform_conversions.c @@ -3580,27 +3580,40 @@ static void posttrans_fcurve_clean(FCurve *fcu, const bool use_handle) for (int i = fcu->totvert - 1; i >= 0; i--) { BezTriple *bezt = &fcu->bezt[i]; - /* Is this a candidate for deletion? */ + /* Is this keyframe a candidate for deletion? */ /* TODO: Replace loop with an O(1) lookup instead */ for (tRetainedKeyframe *rk = retained_keys.last; rk; rk = rk->prev) { if (IS_EQT(bezt->vec[1][0], rk->frame, BEZT_BINARYSEARCH_THRESH)) { - /* Delete this keyframe, unless it's the last selected one on this frame, - * in which case, we'll update its value instead - */ - if (BEZT_ISSEL_ANY(bezt) && (rk->del_count == rk->tot_count - 1)) { - /* Update keyframe */ - if (can_average_points) { - /* TODO: update handles too? */ - bezt->vec[1][1] = rk->val; + /* Selected keys are treated with greater care than unselected ones... */ + if (BEZT_ISSEL_ANY(bezt)) { + /* - If this is the last selected key left (based on rk->del_count) ==> UPDATE IT + * (or else we wouldn't have any keyframe left here) + * - Otherwise, there are still other selected keyframes on this frame + * to be merged down still ==> DELETE IT + */ + if (rk->del_count == rk->tot_count - 1) { + /* Update keyframe... */ + if (can_average_points) { + /* TODO: update handles too? */ + bezt->vec[1][1] = rk->val; + } + } + else { + /* Delete Keyframe */ + delete_fcurve_key(fcu, i, 0); } + + /* Update count of how many we've deleted + * - It should only matter that we're doing this for all but the last one + */ + rk->del_count++; } else { - /* Delete keyframe */ + /* Always delete - Unselected keys don't matter */ delete_fcurve_key(fcu, i, 0); } - - /* Stop searching for matching RK's */ - rk->del_count++; + + /* Stop the RK search... we've found our match now */ break; } } |