Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/metaball/mball_edit.c
| Show All 32 Lines | |||||
| #include <string.h> | #include <string.h> | ||||
| #include "MEM_guardedalloc.h" | #include "MEM_guardedalloc.h" | ||||
| #include "BLI_blenlib.h" | #include "BLI_blenlib.h" | ||||
| #include "BLI_math.h" | #include "BLI_math.h" | ||||
| #include "BLI_rand.h" | #include "BLI_rand.h" | ||||
| #include "BLI_utildefines.h" | #include "BLI_utildefines.h" | ||||
| #include "BLI_kdtree.h" | |||||
| #include "DNA_defs.h" | #include "DNA_defs.h" | ||||
| #include "DNA_meta_types.h" | #include "DNA_meta_types.h" | ||||
| #include "DNA_object_types.h" | #include "DNA_object_types.h" | ||||
| #include "DNA_scene_types.h" | #include "DNA_scene_types.h" | ||||
| #include "RNA_define.h" | #include "RNA_define.h" | ||||
| #include "RNA_access.h" | #include "RNA_access.h" | ||||
| #include "BKE_context.h" | #include "BKE_context.h" | ||||
| #include "BKE_mball.h" | #include "BKE_mball.h" | ||||
| #include "BKE_layer.h" | #include "BKE_layer.h" | ||||
| #include "BKE_object.h" | |||||
| #include "DEG_depsgraph.h" | #include "DEG_depsgraph.h" | ||||
| #include "ED_mball.h" | #include "ED_mball.h" | ||||
| #include "ED_screen.h" | #include "ED_screen.h" | ||||
| #include "ED_select_utils.h" | #include "ED_select_utils.h" | ||||
| #include "ED_view3d.h" | #include "ED_view3d.h" | ||||
| ▲ Show 20 Lines • Show All 131 Lines • ▼ Show 20 Lines | |||||
| static const EnumPropertyItem prop_similar_types[] = { | static const EnumPropertyItem prop_similar_types[] = { | ||||
| {SIMMBALL_TYPE, "TYPE", 0, "Type", ""}, | {SIMMBALL_TYPE, "TYPE", 0, "Type", ""}, | ||||
| {SIMMBALL_RADIUS, "RADIUS", 0, "Radius", ""}, | {SIMMBALL_RADIUS, "RADIUS", 0, "Radius", ""}, | ||||
| {SIMMBALL_STIFFNESS, "STIFFNESS", 0, "Stiffness", ""}, | {SIMMBALL_STIFFNESS, "STIFFNESS", 0, "Stiffness", ""}, | ||||
| {SIMMBALL_ROTATION, "ROTATION", 0, "Rotation", ""}, | {SIMMBALL_ROTATION, "ROTATION", 0, "Rotation", ""}, | ||||
| {0, NULL, 0, NULL, NULL} | {0, NULL, 0, NULL, NULL} | ||||
| }; | }; | ||||
| static bool mball_select_similar_type(MetaBall *mb) | static void mball_select_similar_type_get(Object *obedit, MetaBall *mb, int type, KDTree *r_tree) | ||||
| { | { | ||||
| float tree_entry[3] = {0.0f, 0.0f, 0.0f}; | |||||
| MetaElem *ml; | MetaElem *ml; | ||||
| bool changed = false; | int tree_index = 0; | ||||
| for (ml = mb->editelems->first; ml; ml = ml->next) { | for (ml = mb->editelems->first; ml; ml = ml->next) { | ||||
| if (ml->flag & SELECT) { | if (ml->flag & SELECT) { | ||||
| MetaElem *ml_iter; | switch (type) { | ||||
| case SIMMBALL_RADIUS: | |||||
| for (ml_iter = mb->editelems->first; ml_iter; ml_iter = ml_iter->next) { | { | ||||
| if ((ml_iter->flag & SELECT) == 0) { | float radius = ml->rad; | ||||
| if (ml->type == ml_iter->type) { | /* Radius in world space. */ | ||||
| ml_iter->flag |= SELECT; | float smat[3][3]; | ||||
| changed = true; | float radius_vec[3] = {radius, radius, radius}; | ||||
| BKE_object_scale_to_mat3(obedit, smat); | |||||
| mul_m3_v3(smat, radius_vec); | |||||
| radius = (radius_vec[0] + radius_vec[1] + radius_vec[2]) / 3; | |||||
| tree_entry[0] = radius; | |||||
| break; | |||||
| } | } | ||||
| case SIMMBALL_STIFFNESS: | |||||
| { | |||||
| tree_entry[0] = ml->s; | |||||
| break; | |||||
| } | } | ||||
| break; | |||||
| case SIMMBALL_ROTATION: | |||||
| { | |||||
| float dir[3] = {1.0f, 0.0f, 0.0f}; | |||||
| float rmat[3][3]; | |||||
| mul_qt_v3(ml->quat, dir); | |||||
| BKE_object_rot_to_mat3(obedit, rmat, true); | |||||
| mul_m3_v3(rmat, dir); | |||||
| copy_v3_v3(tree_entry, dir); | |||||
| break; | |||||
| } | } | ||||
| } | } | ||||
| BLI_kdtree_insert(r_tree, tree_index++, tree_entry); | |||||
| } | |||||
| } | } | ||||
| return changed; | |||||
| } | } | ||||
| static bool mball_select_similar_radius(MetaBall *mb, const float thresh) | |||||
| { | |||||
| MetaElem *ml; | |||||
| bool changed = false; | |||||
| for (ml = mb->editelems->first; ml; ml = ml->next) { | /* ### TEMPORARY #### (delete after D3845 is accepted) */ | ||||
| if (ml->flag & SELECT) { | enum { | ||||
| MetaElem *ml_iter; | SIM_CMP_EQ = 0, | ||||
| SIM_CMP_GT, | |||||
| SIM_CMP_LT, | |||||
| }; | |||||
| for (ml_iter = mb->editelems->first; ml_iter; ml_iter = ml_iter->next) { | static int ED_select_similar_compare_float(const float delta, const float thresh, const int compare) | ||||
| if ((ml_iter->flag & SELECT) == 0) { | { | ||||
| if (fabsf(ml_iter->rad - ml->rad) <= (thresh * ml->rad)) { | switch (compare) { | ||||
| ml_iter->flag |= SELECT; | case SIM_CMP_EQ: | ||||
| changed = true; | return (fabsf(delta) < thresh + FLT_EPSILON); | ||||
| } | case SIM_CMP_GT: | ||||
| return ((delta + thresh) > -FLT_EPSILON); | |||||
| case SIM_CMP_LT: | |||||
| return ((delta - thresh) < FLT_EPSILON); | |||||
| default: | |||||
| BLI_assert(0); | |||||
| return 0; | |||||
| } | } | ||||
| } | } | ||||
| static bool ED_select_similar_compare_float_tree(const KDTree *tree, const float length, const float thresh, const int compare) | |||||
| { | |||||
| /* Length of the edge we want to compare against. */ | |||||
| float nearest_edge_length; | |||||
| switch (compare) { | |||||
| case SIM_CMP_EQ: | |||||
| /* Compare to the edge closest to the current edge. */ | |||||
| nearest_edge_length = length; | |||||
| break; | |||||
| case SIM_CMP_GT: | |||||
| /* Compare against the shortest edge. */ | |||||
| /* -FLT_MAX leads to some precision issues and the wrong edge being selected. | |||||
| * For example, in a tree with 1, 2 and 3, which is stored squared as 1, 4, 9, it returns as the nearest | |||||
| * length/node the "4" instead of "1". */ | |||||
| nearest_edge_length = -1.0f; | |||||
| break; | |||||
| case SIM_CMP_LT: | |||||
| /* Compare against the longest edge. */ | |||||
| nearest_edge_length = FLT_MAX; | |||||
| break; | |||||
| default: | |||||
| BLI_assert(0); | |||||
| return false; | |||||
| } | } | ||||
| KDTreeNearest nearest; | |||||
| float dummy[3] = {nearest_edge_length, 0.0f, 0.0f}; | |||||
| if (BLI_kdtree_find_nearest(tree, dummy, &nearest) != -1) { | |||||
| float delta = length - nearest.co[0]; | |||||
| return ED_select_similar_compare_float(delta, thresh, compare); | |||||
| } | } | ||||
| return changed; | return false; | ||||
| } | } | ||||
| /* ### END_TEMPORARY */ | |||||
| static bool mball_select_similar_stiffness(MetaBall *mb, const float thresh) | static bool mball_select_similar_type(Object *obedit, MetaBall *mb, int type, const KDTree *tree, const float thresh) | ||||
| { | { | ||||
| MetaElem *ml; | MetaElem *ml; | ||||
| bool changed = false; | bool changed = true; | ||||
dfelinto: changed should be initialized to false. | |||||
| for (ml = mb->editelems->first; ml; ml = ml->next) { | for (ml = mb->editelems->first; ml; ml = ml->next) { | ||||
| if (ml->flag & SELECT) { | bool select = false; | ||||
| MetaElem *ml_iter; | switch (type) { | ||||
| case SIMMBALL_RADIUS: | |||||
| { | |||||
| float radius = ml->rad; | |||||
| /* Radius in world space is the average of the | |||||
| * scaled radius in x, y and z directions. */ | |||||
| float smat[3][3]; | |||||
| float radius_vec[3] = {radius, radius, radius}; | |||||
| BKE_object_scale_to_mat3(obedit, smat); | |||||
| mul_m3_v3(smat, radius_vec); | |||||
| radius = (radius_vec[0] + radius_vec[1] + radius_vec[2]) / 3; | |||||
| for (ml_iter = mb->editelems->first; ml_iter; ml_iter = ml_iter->next) { | if(ED_select_similar_compare_float_tree(tree, radius, thresh, SIM_CMP_EQ)) { | ||||
| if ((ml_iter->flag & SELECT) == 0) { | select = true; | ||||
| if (fabsf(ml_iter->s - ml->s) <= thresh) { | } | ||||
| ml_iter->flag |= SELECT; | break; | ||||
| changed = true; | |||||
| } | } | ||||
| case SIMMBALL_STIFFNESS: | |||||
| { | |||||
| float s = ml->s; | |||||
| if(ED_select_similar_compare_float_tree(tree, s, thresh, SIM_CMP_EQ)) { | |||||
| select = true; | |||||
| } | } | ||||
| break; | |||||
| } | |||||
| case SIMMBALL_ROTATION: | |||||
| { | |||||
| float dir[3] = {1.0f, 0.0f, 0.0f}; | |||||
| float rmat[3][3]; | |||||
| mul_qt_v3(ml->quat, dir); | |||||
| BKE_object_rot_to_mat3(obedit, rmat, true); | |||||
| mul_m3_v3(rmat, dir); | |||||
| float thresh_cos = cosf(thresh * (float)M_PI_2); | |||||
| KDTreeNearest nearest; | |||||
| if (BLI_kdtree_find_nearest(tree, dir, &nearest) != -1) { | |||||
| float orient = angle_normalized_v3v3(dir, nearest.co); | |||||
| /* Map to 0-1 to compare orientation. */ | |||||
| float delta = thresh_cos - fabsf(cosf(orient)); | |||||
| if (ED_select_similar_compare_float(delta, thresh, SIM_CMP_EQ)) { | |||||
| select = true; | |||||
| } | } | ||||
| } | } | ||||
| break; | |||||
| } | |||||
| } | } | ||||
| if (select) { | |||||
| changed = true; | |||||
| ml->flag |= SELECT; | |||||
| } | |||||
| } | |||||
| return changed; | return changed; | ||||
| } | } | ||||
| static bool mball_select_similar_rotation(MetaBall *mb, const float thresh) | static int mball_select_similar_exec(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| const float thresh_rad = thresh * (float)M_PI_2; | const int type = RNA_enum_get(op->ptr, "type"); | ||||
| MetaElem *ml; | const float thresh = RNA_float_get(op->ptr, "threshold"); | ||||
| bool changed = false; | int tot_mball_selected_all = 0; | ||||
| for (ml = mb->editelems->first; ml; ml = ml->next) { | ViewLayer *view_layer = CTX_data_view_layer(C); | ||||
| if (ml->flag & SELECT) { | uint objects_len = 0; | ||||
| MetaElem *ml_iter; | Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); | ||||
| tot_mball_selected_all = BKE_mball_select_count_multi(objects, objects_len); | |||||
| float ml_mat[3][3]; | short type_ref = 0; | ||||
| KDTree *tree = NULL; | |||||
| unit_m3(ml_mat); | if (type != SIMMBALL_TYPE) { | ||||
| mul_qt_v3(ml->quat, ml_mat[0]); | tree = BLI_kdtree_new(tot_mball_selected_all); | ||||
| mul_qt_v3(ml->quat, ml_mat[1]); | } | ||||
| mul_qt_v3(ml->quat, ml_mat[2]); | |||||
| normalize_m3(ml_mat); | /* Get type of selected MetaBall */ | ||||
| for (uint ob_index = 0; ob_index < objects_len; ob_index++) { | |||||
| for (ml_iter = mb->editelems->first; ml_iter; ml_iter = ml_iter->next) { | Object *obedit = objects[ob_index]; | ||||
| if ((ml_iter->flag & SELECT) == 0) { | MetaBall *mb = (MetaBall *)obedit->data; | ||||
| float ml_iter_mat[3][3]; | |||||
| switch (type) { | |||||
| unit_m3(ml_iter_mat); | case SIMMBALL_TYPE: | ||||
| mul_qt_v3(ml_iter->quat, ml_iter_mat[0]); | |||||
| mul_qt_v3(ml_iter->quat, ml_iter_mat[1]); | |||||
| mul_qt_v3(ml_iter->quat, ml_iter_mat[2]); | |||||
| normalize_m3(ml_iter_mat); | |||||
| if ((angle_normalized_v3v3(ml_mat[0], ml_iter_mat[0]) + | |||||
| angle_normalized_v3v3(ml_mat[1], ml_iter_mat[1]) + | |||||
| angle_normalized_v3v3(ml_mat[2], ml_iter_mat[2])) < thresh_rad) | |||||
| { | { | ||||
| ml_iter->flag |= SELECT; | MetaElem *ml; | ||||
| changed = true; | for (ml = mb->editelems->first; ml; ml = ml->next) { | ||||
| if (ml->flag & SELECT) { | |||||
| short mball_type = 1 << (ml->type + 1); | |||||
| type_ref |= mball_type; | |||||
| } | } | ||||
| } | } | ||||
| break; | |||||
| } | } | ||||
| case SIMMBALL_RADIUS: | |||||
| case SIMMBALL_STIFFNESS: | |||||
| case SIMMBALL_ROTATION: | |||||
| mball_select_similar_type_get(obedit, mb, type, tree); | |||||
| break; | |||||
| default: | |||||
| BLI_assert(0); | |||||
| break; | |||||
| } | } | ||||
| } | } | ||||
| return changed; | if (tree != NULL) { | ||||
| BLI_kdtree_balance(tree); | |||||
| } | } | ||||
| /* Select MetaBalls with desired type. */ | |||||
| static int mball_select_similar_exec(bContext *C, wmOperator *op) | for (uint ob_index = 0; ob_index < objects_len; ob_index++) { | ||||
| { | Object *obedit = objects[ob_index]; | ||||
| Object *obedit = CTX_data_edit_object(C); | |||||
| MetaBall *mb = (MetaBall *)obedit->data; | MetaBall *mb = (MetaBall *)obedit->data; | ||||
| int type = RNA_enum_get(op->ptr, "type"); | |||||
| float thresh = RNA_float_get(op->ptr, "threshold"); | |||||
| bool changed = false; | bool changed = false; | ||||
| switch (type) { | switch(type) { | ||||
| case SIMMBALL_TYPE: | case SIMMBALL_TYPE: | ||||
| changed = mball_select_similar_type(mb); | { | ||||
| MetaElem *ml; | |||||
| for (ml = mb->editelems->first; ml; ml = ml->next) { | |||||
| short mball_type = 1 << (ml->type + 1); | |||||
| if (mball_type & type_ref) { | |||||
| ml->flag |= SELECT; | |||||
| } | |||||
| } | |||||
| break; | break; | ||||
| } | |||||
| case SIMMBALL_RADIUS: | case SIMMBALL_RADIUS: | ||||
| changed = mball_select_similar_radius(mb, thresh); | |||||
| break; | |||||
| case SIMMBALL_STIFFNESS: | case SIMMBALL_STIFFNESS: | ||||
| changed = mball_select_similar_stiffness(mb, thresh); | |||||
| break; | |||||
| case SIMMBALL_ROTATION: | case SIMMBALL_ROTATION: | ||||
| changed = mball_select_similar_rotation(mb, thresh); | changed = mball_select_similar_type(obedit, mb, type, tree, thresh); | ||||
| break; | break; | ||||
| default: | default: | ||||
| BLI_assert(0); | BLI_assert(0); | ||||
| break; | break; | ||||
| } | |||||
| if (changed) { | if (changed) { | ||||
dfelintoUnsubmitted Not Done Inline ActionsThis should be outside the switch {}. the way it is it never runs. dfelinto: This should be outside the switch {}. the way it is it never runs. | |||||
| DEG_id_tag_update(&mb->id, DEG_TAG_SELECT_UPDATE); | DEG_id_tag_update(&mb->id, DEG_TAG_SELECT_UPDATE); | ||||
| WM_event_add_notifier(C, NC_GEOM | ND_SELECT, mb); | WM_event_add_notifier(C, NC_GEOM | ND_SELECT, mb); | ||||
| } | } | ||||
| } | |||||
| } | |||||
| MEM_freeN(objects); | |||||
| if (tree != NULL) { | |||||
| BLI_kdtree_free(tree); | |||||
| } | |||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void MBALL_OT_select_similar(wmOperatorType *ot) | void MBALL_OT_select_similar(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Select Similar"; | ot->name = "Select Similar"; | ||||
| ot->idname = "MBALL_OT_select_similar"; | ot->idname = "MBALL_OT_select_similar"; | ||||
| /* callback functions */ | /* callback functions */ | ||||
| ot->invoke = WM_menu_invoke; | ot->invoke = WM_menu_invoke; | ||||
| ot->exec = mball_select_similar_exec; | ot->exec = mball_select_similar_exec; | ||||
| ot->poll = ED_operator_editmball; | ot->poll = ED_operator_editmball; | ||||
| ot->description = "Select similar metaballs by property types"; | ot->description = "Select similar metaballs by property types"; | ||||
| /* flags */ | /* flags */ | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| /* properties */ | /* properties */ | ||||
| ot->prop = RNA_def_enum(ot->srna, "type", prop_similar_types, 0, "Type", ""); | ot->prop = RNA_def_enum(ot->srna, "type", prop_similar_types, 0, "Type", ""); | ||||
| RNA_def_float(ot->srna, "threshold", 0.1, 0.0, 1.0, "Threshold", "", 0.01, 1.0); | RNA_def_float(ot->srna, "threshold", 0.1, 0.0, FLT_MAX, "Threshold", "", 0.01, 1.0); | ||||
| } | } | ||||
| /***************************** Select random operator *****************************/ | /***************************** Select random operator *****************************/ | ||||
| /* Random metaball selection */ | /* Random metaball selection */ | ||||
| static int select_random_metaelems_exec(bContext *C, wmOperator *op) | static int select_random_metaelems_exec(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| ▲ Show 20 Lines • Show All 315 Lines • Show Last 20 Lines | |||||
changed should be initialized to false.