diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 2f4a182024f..17d75dca41b 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -1476,6 +1476,7 @@ class VIEW3D_MT_edit_mesh(Menu): layout.operator("view3d.edit_mesh_extrude_move_normal", text="Extrude Region") layout.operator("view3d.edit_mesh_extrude_individual_move", text="Extrude Individual") + layout.operator("mesh.dissolve_limited") # BMESH ONLY layout.operator("mesh.duplicate_move") layout.operator("mesh.delete", text="Delete...") @@ -1511,6 +1512,7 @@ class VIEW3D_MT_edit_mesh_specials(Menu): """ layout.operator("mesh.merge", text="Merge...") layout.operator("mesh.remove_doubles") + layout.operator("mesh.dissolve_limited") # BMESH ONLY layout.operator("mesh.hide", text="Hide") layout.operator("mesh.reveal", text="Reveal") layout.operator("mesh.select_all").action = 'INVERT' diff --git a/source/blender/blenlib/intern/math_vector.c b/source/blender/blenlib/intern/math_vector.c index 47deb705def..df9496eca91 100644 --- a/source/blender/blenlib/intern/math_vector.c +++ b/source/blender/blenlib/intern/math_vector.c @@ -118,7 +118,7 @@ float angle_v3v3v3(const float v1[3], const float v2[3], const float v3[3]) { float vec1[3], vec2[3]; - sub_v3_v3v3(vec1, v2, v1); + sub_v3_v3v3(vec1, v1, v2); /* BMESH_TODO!!! merge this line into trunk, cant now, too close to release - campbell */ sub_v3_v3v3(vec2, v2, v3); normalize_v3(vec1); normalize_v3(vec2); diff --git a/source/blender/bmesh/bmesh_queries.h b/source/blender/bmesh/bmesh_queries.h index d9c0e76b899..040d0f00923 100644 --- a/source/blender/bmesh/bmesh_queries.h +++ b/source/blender/bmesh/bmesh_queries.h @@ -88,6 +88,10 @@ int BM_edge_is_boundry(struct BMEdge *e); * exactly two faces sharing the edge.*/ float BM_edge_face_angle(struct BMesh *bm, struct BMEdge *e); +/* returns angle of two faces surrounding edges. note there must be + * exactly two edges sharing the vertex.*/ +float BM_vert_edge_angle(struct BMesh *bm, struct BMVert *v); + /* checks overlapping of existing faces with the verts in varr. */ int BM_face_exists_overlap(struct BMesh *bm, struct BMVert **varr, int len, struct BMFace **existface); diff --git a/source/blender/bmesh/intern/bmesh_opdefines.c b/source/blender/bmesh/intern/bmesh_opdefines.c index 7308e44840a..19e9971619a 100644 --- a/source/blender/bmesh/intern/bmesh_opdefines.c +++ b/source/blender/bmesh/intern/bmesh_opdefines.c @@ -659,6 +659,15 @@ static BMOpDefine def_dissolvefacesop = { BMO_OP_FLAG_UNTAN_MULTIRES }; +static BMOpDefine def_dissolvelimitop = { + "dissolvelimit", + {{BMO_OP_SLOT_FLT, "angle_limit"}, /* total rotation angle (degrees) */ + {BMO_OP_SLOT_ELEMENT_BUF, "verts"}, + {BMO_OP_SLOT_ELEMENT_BUF, "edges"}, + {0} /* null-terminating sentine */}, + dissolvelimit_exec, + BMO_OP_FLAG_UNTAN_MULTIRES +}; static BMOpDefine def_triangop = { "triangulate", @@ -1075,6 +1084,7 @@ BMOpDefine *opdefines[] = { &def_dissolveedgessop, &def_dissolveedgeloopsop, &def_dissolvevertsop, + &def_dissolvelimitop, &def_extrudefaceregion, &def_connectverts, //&def_makeprim, diff --git a/source/blender/bmesh/intern/bmesh_operators_private.h b/source/blender/bmesh/intern/bmesh_operators_private.h index 13bd9179955..4cc338bfdd0 100644 --- a/source/blender/bmesh/intern/bmesh_operators_private.h +++ b/source/blender/bmesh/intern/bmesh_operators_private.h @@ -43,6 +43,7 @@ void bmesh2edit_exec(BMesh *bmesh, BMOperator *op); void triangulate_exec(BMesh *bmesh, BMOperator *op); void dissolvefaces_exec(BMesh *bmesh, BMOperator *op); void dissolveverts_exec(BMesh *bmesh, BMOperator *op); +void dissolvelimit_exec(BMesh *bmesh, BMOperator *op); void bmesh_make_fgons_exec(BMesh *bmesh, BMOperator *op); void extrude_edge_context_exec(BMesh *bm, BMOperator *op); void connectverts_exec(BMesh *bm, BMOperator *op); diff --git a/source/blender/bmesh/intern/bmesh_queries.c b/source/blender/bmesh/intern/bmesh_queries.c index 47f25f6d22e..33f0fdeb55e 100644 --- a/source/blender/bmesh/intern/bmesh_queries.c +++ b/source/blender/bmesh/intern/bmesh_queries.c @@ -517,11 +517,11 @@ void BM_edge_ordered_verts(BMEdge *edge, BMVert **r_v1, BMVert **r_v2) } } -/** +/* * BMESH FACE ANGLE * - * Calculates the angle between two faces. Assumes - * That face normals are correct. + * Calculates the angle between two faces. + * Assumes the face normals are correct. * * Returns - * Float. @@ -532,7 +532,37 @@ float BM_edge_face_angle(BMesh *UNUSED(bm), BMEdge *e) if (BM_edge_face_count(e) == 2) { BMLoop *l1 = e->l; BMLoop *l2 = e->l->radial_next; - return acosf(dot_v3v3(l1->f->no, l2->f->no)); + return angle_normalized_v3v3(l1->f->no, l2->f->no); + } + else { + return (float)M_PI / 2.0f; /* acos(0.0) */ + } +} + +/* + * BMESH FACE ANGLE + * + * Calculates the angle a verts 2 edges. + * + * Returns - + * Float. + */ +float BM_vert_edge_angle(BMesh *UNUSED(bm), BMVert *v) +{ + BMEdge *e1, *e2; + + /* saves BM_vert_edge_count(v) and and edge iterator, + * get the edges and count them both at once */ + + if ((e1 = v->e) && + (e2 = bmesh_disk_nextedge(e1, v)) && + /* make sure we come full circle and only have 2 connected edges */ + (e1 == bmesh_disk_nextedge(e2, v))) + { + BMVert *v1 = BM_edge_other_vert(e1, v); + BMVert *v2 = BM_edge_other_vert(e2, v); + + return angle_v3v3v3(v1->co, v->co, v2->co); } else { return (float)M_PI / 2.0f; /* acos(0.0) */ diff --git a/source/blender/bmesh/operators/bmo_dissolve.c b/source/blender/bmesh/operators/bmo_dissolve.c index 494cb2eb908..05aead466d1 100644 --- a/source/blender/bmesh/operators/bmo_dissolve.c +++ b/source/blender/bmesh/operators/bmo_dissolve.c @@ -23,6 +23,7 @@ #include "MEM_guardedalloc.h" #include "BLI_array.h" +#include "BLI_math.h" #include "bmesh.h" #include "bmesh_private.h" @@ -397,6 +398,9 @@ void dissolveverts_exec(BMesh *bm, BMOperator *op) /* this code is for cleaning up two-edged faces, it shall become * it's own function one day */ #if 0 +void dummy_exec(BMesh *bm, BMOperator *op) +{ + { /* clean up two-edged face */ /* basic idea is to keep joining 2-edged faces until their * gone. this however relies on joining two 2-edged faces @@ -456,5 +460,100 @@ void dissolveverts_exec(BMesh *bm, BMOperator *op) } if (oldlen == len) break; oldlen = len; + } +} #endif + +/**/ +typedef struct DissolveElemWeight_t { + BMHeader *ele; + float weight; +} DissolveElemWeight_t; + +static int dissolve_elem_cmp(const void *a1, const void *a2) +{ + const struct DissolveElemWeight_t *d1 = a1, *d2 = a2; + + if (d1->weight > d2->weight) return 1; + else if (d1->weight < d2->weight) return -1; + return 0; +} + +void dissolvelimit_exec(BMesh *bm, BMOperator *op) +{ + BMOpSlot *einput = BMO_slot_get(op, "edges"); + BMOpSlot *vinput = BMO_slot_get(op, "verts"); + const float angle_max = (float)M_PI / 2.0f; + const float angle_limit = minf(angle_max, BMO_slot_float_get(op, "angle_limit")); + DissolveElemWeight_t *weight_elems = MEM_mallocN(MAX2(einput->len, vinput->len) * + sizeof(DissolveElemWeight_t), __func__); + int i, tot_found; + + /* --- first edges --- */ + + /* go through and split edge */ + for (i = 0, tot_found = 0; i < einput->len; i++) { + BMEdge *e = ((BMEdge **)einput->data.p)[i]; + const float angle = BM_edge_face_angle(bm, e); + + if (angle < angle_limit) { + weight_elems[i].ele = (BMHeader *)e; + weight_elems[i].weight = angle; + tot_found++; + } + else { + weight_elems[i].ele = NULL; + weight_elems[i].weight = angle_max; + } + } + + if (tot_found != 0) { + qsort(weight_elems, einput->len, sizeof(DissolveElemWeight_t), dissolve_elem_cmp); + + for (i = 0; i < tot_found; i++) { + BMEdge *e = (BMEdge *)weight_elems[i].ele; + /* check twice because cumulative effect could disolve over angle limit */ + if (BM_edge_face_angle(bm, e) < angle_limit) { + BMFace *nf = BM_faces_join_pair(bm, e->l->f, + e->l->radial_next->f, + e); /* join faces */ + + /* there may be some errors, we dont mind, just move on */ + if (nf == NULL) { + BMO_error_clear(bm); + } + } + } + } + + /* --- second verts --- */ + for (i = 0, tot_found = 0; i < vinput->len; i++) { + BMVert *v = ((BMVert **)vinput->data.p)[i]; + const float angle = BM_vert_edge_angle(bm, v); + + if (angle < angle_limit) { + weight_elems[i].ele = (BMHeader *)v; + weight_elems[i].weight = angle; + tot_found++; + } + else { + weight_elems[i].ele = NULL; + weight_elems[i].weight = angle_max; + } + } + + if (tot_found != 0) { + qsort(weight_elems, vinput->len, sizeof(DissolveElemWeight_t), dissolve_elem_cmp); + + for (i = 0; i < tot_found; i++) { + BMVert *v = (BMVert *)weight_elems[i].ele; + /* check twice because cumulative effect could disolve over angle limit */ + if (BM_vert_edge_angle(bm, v) < angle_limit) { + BM_vert_collapse_edges(bm, v->e, v); /* join edges */ + } + } + } + + MEM_freeN(weight_elems); +} diff --git a/source/blender/editors/mesh/bmesh_tools.c b/source/blender/editors/mesh/bmesh_tools.c index f71913922e6..e7db9b0ed37 100644 --- a/source/blender/editors/mesh/bmesh_tools.c +++ b/source/blender/editors/mesh/bmesh_tools.c @@ -3522,7 +3522,46 @@ void MESH_OT_tris_convert_to_quads(wmOperatorType *ot) RNA_def_boolean(ot->srna, "vcols", 0, "Compare VCols", ""); RNA_def_boolean(ot->srna, "sharp", 0, "Compare Sharp", ""); RNA_def_boolean(ot->srna, "materials", 0, "Compare Materials", ""); +} +static int dissolve_limited_exec(bContext *C, wmOperator *op) +{ + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh; + float angle_limit = RNA_float_get(op->ptr, "angle_limit"); + + if (!EDBM_CallOpf(em, op, + "dissolvelimit edges=%he verts=%hv angle_limit=%f", + BM_ELEM_SELECT, BM_ELEM_SELECT, angle_limit)) + { + return OPERATOR_CANCELLED; + } + + DAG_id_tag_update(obedit->data, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); + + return OPERATOR_FINISHED; +} + +void MESH_OT_dissolve_limited(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Limited Dissolve"; + ot->idname = "MESH_OT_dissolve_limited"; + ot->description = "Dissolve selected edges and verts, limited by the angle of surrounding geometry"; + + /* api callbacks */ + ot->exec = dissolve_limited_exec; + ot->poll = ED_operator_editmesh; + + /* flags */ + ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; + + prop = RNA_def_float_rotation(ot->srna, "angle_limit", 0, NULL, 0.0f, DEG2RADF(180.0f), + "Max Angle", "Angle Limit in Degrees", 0.0f, DEG2RADF(180.0f)); + RNA_def_property_float_default(prop, DEG2RADF(15.0f)); } static int edge_flip_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h index 4d03503de18..7334acfdfb0 100644 --- a/source/blender/editors/mesh/mesh_intern.h +++ b/source/blender/editors/mesh/mesh_intern.h @@ -252,6 +252,7 @@ void MESH_OT_fill(struct wmOperatorType *ot); void MESH_OT_beautify_fill(struct wmOperatorType *ot); void MESH_OT_quads_convert_to_tris(struct wmOperatorType *ot); void MESH_OT_tris_convert_to_quads(struct wmOperatorType *ot); +void MESH_OT_dissolve_limited(struct wmOperatorType *ot); void MESH_OT_edge_flip(struct wmOperatorType *ot); void MESH_OT_faces_shade_smooth(struct wmOperatorType *ot); void MESH_OT_faces_shade_flat(struct wmOperatorType *ot); diff --git a/source/blender/editors/mesh/mesh_ops.c b/source/blender/editors/mesh/mesh_ops.c index 82f3f5b74d6..ce9d9e37ce0 100644 --- a/source/blender/editors/mesh/mesh_ops.c +++ b/source/blender/editors/mesh/mesh_ops.c @@ -115,6 +115,7 @@ void ED_operatortypes_mesh(void) WM_operatortype_append(MESH_OT_beautify_fill); WM_operatortype_append(MESH_OT_quads_convert_to_tris); WM_operatortype_append(MESH_OT_tris_convert_to_quads); + WM_operatortype_append(MESH_OT_dissolve_limited); WM_operatortype_append(MESH_OT_edge_flip); WM_operatortype_append(MESH_OT_faces_shade_smooth); WM_operatortype_append(MESH_OT_faces_shade_flat);