Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/mesh/editmesh_select_similar.c
| Show All 18 Lines | |||||
| /** \file | /** \file | ||||
| * \ingroup edmesh | * \ingroup edmesh | ||||
| */ | */ | ||||
| #include "MEM_guardedalloc.h" | #include "MEM_guardedalloc.h" | ||||
| #include "BLI_kdtree.h" | #include "BLI_kdtree.h" | ||||
| #include "BLI_kdtree_nd.h" | |||||
| #include "BLI_listbase.h" | #include "BLI_listbase.h" | ||||
| #include "BLI_math.h" | #include "BLI_math.h" | ||||
| #include "BKE_context.h" | #include "BKE_context.h" | ||||
| #include "BKE_editmesh.h" | #include "BKE_editmesh.h" | ||||
| #include "BKE_layer.h" | #include "BKE_layer.h" | ||||
| #include "BKE_material.h" | #include "BKE_material.h" | ||||
| #include "BKE_report.h" | #include "BKE_report.h" | ||||
| ▲ Show 20 Lines • Show All 103 Lines • ▼ Show 20 Lines | |||||
| } | } | ||||
| /** | /** | ||||
| * Note: This is not normal, but the face direction itself and always in | * Note: This is not normal, but the face direction itself and always in | ||||
| * a positive quadrant (tries z, y then x). | * a positive quadrant (tries z, y then x). | ||||
| * Also, unlike edge_pos_direction_worldspace_get we don't normalize the direction. | * Also, unlike edge_pos_direction_worldspace_get we don't normalize the direction. | ||||
| * In fact we scale the direction by the distance of the face center to the origin. | * In fact we scale the direction by the distance of the face center to the origin. | ||||
| */ | */ | ||||
| static void face_pos_direction_worldspace_scaled_get(Object *ob, BMFace *face, float *r_dir) | static void face_to_plane(const Object *ob, BMFace *face, float r_plane[4]) | ||||
| { | { | ||||
| float distance; | float normal[3], co[3]; | ||||
| float center[3]; | copy_v3_v3(normal, face->no); | ||||
| mul_transposed_mat3_m4_v3(ob->imat, normal); | |||||
| copy_v3_v3(r_dir, face->no); | normalize_v3(normal); | ||||
| normalize_v3(r_dir); | mul_v3_m4v3(co, ob->obmat, BM_FACE_FIRST_LOOP(face)->v->co); | ||||
| plane_from_point_normal_v3(r_plane, co, normal); | |||||
| BM_face_calc_center_median(face, center); | |||||
| mul_m4_v3(ob->obmat, center); | |||||
| distance = dot_v3v3(r_dir, center); | |||||
| mul_v3_fl(r_dir, distance); | |||||
| /* Make sure we have a consistent direction regardless of the face orientation. | |||||
| * This spares us from storing dir and -dir in the tree. */ | |||||
| if (fabs(r_dir[2]) < FLT_EPSILON) { | |||||
| if (fabs(r_dir[1]) < FLT_EPSILON) { | |||||
| if (r_dir[0] < 0.0f) { | |||||
| mul_v3_fl(r_dir, -1.0f); | |||||
| } | |||||
| } | |||||
| else if (r_dir[1] < 0.0f) { | |||||
| mul_v3_fl(r_dir, -1.0f); | |||||
| } | |||||
| } | |||||
| else if (r_dir[2] < 0.0f) { | |||||
| mul_v3_fl(r_dir, -1.0f); | |||||
| } | |||||
| } | } | ||||
| /* TODO(dfelinto): `types` that should technically be compared in world space but are not: | /* TODO(dfelinto): `types` that should technically be compared in world space but are not: | ||||
| * -SIMFACE_AREA | * -SIMFACE_AREA | ||||
| * -SIMFACE_PERIMETER | * -SIMFACE_PERIMETER | ||||
| */ | */ | ||||
| static int similar_face_select_exec(bContext *C, wmOperator *op) | static int similar_face_select_exec(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| Show All 16 Lines | static int similar_face_select_exec(bContext *C, wmOperator *op) | ||||
| if (tot_faces_selected_all == 0) { | if (tot_faces_selected_all == 0) { | ||||
| BKE_report(op->reports, RPT_ERROR, "No face selected"); | BKE_report(op->reports, RPT_ERROR, "No face selected"); | ||||
| MEM_freeN(objects); | MEM_freeN(objects); | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| KDTree *tree = NULL; | KDTree *tree = NULL; | ||||
| KDTreeND *tree_plane = NULL; | |||||
| GSet *gset = NULL; | GSet *gset = NULL; | ||||
| GSet **gset_array = NULL; | GSet **gset_array = NULL; | ||||
| int face_data_value = SIMFACE_DATA_NONE; | int face_data_value = SIMFACE_DATA_NONE; | ||||
| switch (type) { | switch (type) { | ||||
| case SIMFACE_AREA: | case SIMFACE_AREA: | ||||
| case SIMFACE_PERIMETER: | case SIMFACE_PERIMETER: | ||||
| case SIMFACE_NORMAL: | case SIMFACE_NORMAL: | ||||
| case SIMFACE_COPLANAR: | |||||
| tree = BLI_kdtree_new(tot_faces_selected_all); | tree = BLI_kdtree_new(tot_faces_selected_all); | ||||
| break; | break; | ||||
| case SIMFACE_COPLANAR: | |||||
| /* 4 components for a plane. */ | |||||
| tree_plane = BLI_kdtree_nd_new(tot_faces_selected_all, 4); | |||||
| break; | |||||
| case SIMFACE_SIDES: | case SIMFACE_SIDES: | ||||
| case SIMFACE_MATERIAL: | case SIMFACE_MATERIAL: | ||||
| gset = BLI_gset_ptr_new("Select similar face"); | gset = BLI_gset_ptr_new("Select similar face"); | ||||
| break; | break; | ||||
| case SIMFACE_FACEMAP: | case SIMFACE_FACEMAP: | ||||
| gset_array = MEM_callocN(sizeof(GSet *) * objects_len, "Select similar face: facemap gset array"); | gset_array = MEM_callocN(sizeof(GSet *) * objects_len, "Select similar face: facemap gset array"); | ||||
| break; | break; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 81 Lines • ▼ Show 20 Lines | BM_ITER_MESH (face, &iter, bm, BM_FACES_OF_MESH) { | ||||
| mul_transposed_mat3_m4_v3(ob->imat, normal); | mul_transposed_mat3_m4_v3(ob->imat, normal); | ||||
| normalize_v3(normal); | normalize_v3(normal); | ||||
| BLI_kdtree_insert(tree, tree_index++, normal); | BLI_kdtree_insert(tree, tree_index++, normal); | ||||
| break; | break; | ||||
| } | } | ||||
| case SIMFACE_COPLANAR: | case SIMFACE_COPLANAR: | ||||
| { | { | ||||
| float dir[3]; | float plane[4]; | ||||
| face_pos_direction_worldspace_scaled_get(ob, face, dir); | face_to_plane(ob, face, plane); | ||||
| BLI_kdtree_insert(tree, tree_index++, dir); | BLI_kdtree_nd_insert(tree_plane, tree_index++, plane); | ||||
| break; | break; | ||||
| } | } | ||||
| case SIMFACE_SMOOTH: | case SIMFACE_SMOOTH: | ||||
| { | { | ||||
| if (!face_data_value_set(face, BM_ELEM_SMOOTH, &face_data_value)) { | if (!face_data_value_set(face, BM_ELEM_SMOOTH, &face_data_value)) { | ||||
| goto face_select_all; | goto face_select_all; | ||||
| } | } | ||||
| break; | break; | ||||
| Show All 25 Lines | for (uint ob_index = 0; ob_index < objects_len; ob_index++) { | ||||
| } | } | ||||
| } | } | ||||
| BLI_assert((type != SIMFACE_FREESTYLE) || (face_data_value != SIMFACE_DATA_NONE)); | BLI_assert((type != SIMFACE_FREESTYLE) || (face_data_value != SIMFACE_DATA_NONE)); | ||||
| if (tree != NULL) { | if (tree != NULL) { | ||||
| BLI_kdtree_balance(tree); | BLI_kdtree_balance(tree); | ||||
| } | } | ||||
| if (tree_plane != NULL) { | |||||
| BLI_kdtree_nd_balance(tree_plane); | |||||
| } | |||||
| for (uint ob_index = 0; ob_index < objects_len; ob_index++) { | for (uint ob_index = 0; ob_index < objects_len; ob_index++) { | ||||
| Object *ob = objects[ob_index]; | Object *ob = objects[ob_index]; | ||||
| BMEditMesh *em = BKE_editmesh_from_object(ob); | BMEditMesh *em = BKE_editmesh_from_object(ob); | ||||
| BMesh *bm = em->bm; | BMesh *bm = em->bm; | ||||
| bool changed = false; | bool changed = false; | ||||
| Material ***material_array = NULL; | Material ***material_array = NULL; | ||||
| int custom_data_offset; | int custom_data_offset; | ||||
| ▲ Show 20 Lines • Show All 98 Lines • ▼ Show 20 Lines | BM_ITER_MESH (face, &iter, bm, BM_FACES_OF_MESH) { | ||||
| if (angle_normalized_v3v3(normal, nearest.co) <= thresh_radians) { | if (angle_normalized_v3v3(normal, nearest.co) <= thresh_radians) { | ||||
| select = true; | select = true; | ||||
| } | } | ||||
| } | } | ||||
| break; | break; | ||||
| } | } | ||||
| case SIMFACE_COPLANAR: | case SIMFACE_COPLANAR: | ||||
| { | { | ||||
| float diff[3]; | float plane[4]; | ||||
| float dir[3]; | face_to_plane(ob, face, plane); | ||||
| face_pos_direction_worldspace_scaled_get(ob, face, dir); | |||||
| /* We are treating the direction as coordinates, the "nearest" one will | struct { | ||||
| * also be the one closest to the angle. | KDTreeNearestND_Base base; | ||||
| * And since the direction is scaled by the face center distance to the origin, | float co[4]; | ||||
| * the nearest point will also be the closest between the planes. */ | } nearest; | ||||
| KDTreeNearest nearest; | if (BLI_kdtree_nd_find_nearest(tree_plane, plane, &nearest.base) != -1) { | ||||
| if (BLI_kdtree_find_nearest(tree, dir, &nearest) != -1) { | if (nearest.base.dist <= thresh) { | ||||
| sub_v3_v3v3(diff, dir, nearest.co); | if (angle_v3v3(plane, nearest.co) <= thresh_radians) { | ||||
| if (len_v3(diff) <= thresh) { | |||||
| if (angle_v3v3(dir, nearest.co) <= thresh_radians) { | |||||
| select = true; | select = true; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| break; | break; | ||||
| } | } | ||||
| case SIMFACE_SMOOTH: | case SIMFACE_SMOOTH: | ||||
| if ((BM_elem_flag_test(face, BM_ELEM_SMOOTH) != 0) == | if ((BM_elem_flag_test(face, BM_ELEM_SMOOTH) != 0) == | ||||
| ▲ Show 20 Lines • Show All 70 Lines • ▼ Show 20 Lines | for (uint ob_index = 0; ob_index < objects_len; ob_index++) { | ||||
| } | } | ||||
| EDBM_selectmode_flush(em); | EDBM_selectmode_flush(em); | ||||
| EDBM_update_generic(em, false, false); | EDBM_update_generic(em, false, false); | ||||
| } | } | ||||
| } | } | ||||
| MEM_freeN(objects); | MEM_freeN(objects); | ||||
| BLI_kdtree_free(tree); | BLI_kdtree_free(tree); | ||||
| BLI_kdtree_nd_free(tree_plane); | |||||
| if (gset != NULL) { | if (gset != NULL) { | ||||
| BLI_gset_free(gset, NULL); | BLI_gset_free(gset, NULL); | ||||
| } | } | ||||
| if (gset_array != NULL) { | if (gset_array != NULL) { | ||||
| for (uint ob_index = 0; ob_index < objects_len; ob_index++) { | for (uint ob_index = 0; ob_index < objects_len; ob_index++) { | ||||
| if (gset_array[ob_index] != NULL) { | if (gset_array[ob_index] != NULL) { | ||||
| BLI_gset_free(gset_array[ob_index], NULL); | BLI_gset_free(gset_array[ob_index], NULL); | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 788 Lines • Show Last 20 Lines | |||||