diff options
author | YimingWu <xp8110@outlook.com> | 2019-09-04 06:30:16 +0300 |
---|---|---|
committer | YimingWu <xp8110@outlook.com> | 2019-09-04 06:30:16 +0300 |
commit | 55a6fc0be3ffa823052aba45bec12c6902e7bd53 (patch) | |
tree | fb8cb4e411e22fcb49e443ea4da636660618877f /source/blender/editors | |
parent | afac2afbfcdb06a2d2655e63d0b8bc35b1aba18e (diff) | |
parent | da25aca2677ec2b566cad1809eebceee22b28b53 (diff) |
Merge remote-tracking branch 'origin/master' into soc-2019-npr
Diffstat (limited to 'source/blender/editors')
208 files changed, 7599 insertions, 2713 deletions
diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c index 1649744ba8d..d80b96f0d74 100644 --- a/source/blender/editors/animation/anim_channels_defines.c +++ b/source/blender/editors/animation/anim_channels_defines.c @@ -4847,7 +4847,7 @@ void ANIM_channel_draw_widgets(const bContext *C, /* step 4) draw text - check if renaming widget is in use... */ if (is_being_renamed) { - PointerRNA ptr = {{NULL}}; + PointerRNA ptr = {NULL}; PropertyRNA *prop = NULL; /* draw renaming widget if we can get RNA pointer for it diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c index 7e913014a87..61b8e4a2341 100644 --- a/source/blender/editors/animation/anim_channels_edit.c +++ b/source/blender/editors/animation/anim_channels_edit.c @@ -3114,6 +3114,7 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index, ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, gpl, ANIMTYPE_GPLAYER); /* update other layer status */ BKE_gpencil_layer_setactive(gpd, gpl); + BKE_gpencil_layer_autolock_set(gpd); } /* Grease Pencil updates */ diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c index a78a63f1347..48493c9e961 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -630,9 +630,8 @@ static bAnimListElem *make_new_animlistelem(void *data, /* do specifics */ switch (datatype) { case ANIMTYPE_SUMMARY: { - /* nothing to include for now... this is just a dummy wrappy around all the other channels - * in the DopeSheet, and gets included at the start of the list - */ + /* Nothing to include for now... this is just a dummy wrapper around + * all the other channels in the DopeSheet, and gets included at the start of the list. */ ale->key_data = NULL; ale->datatype = ALE_ALL; break; diff --git a/source/blender/editors/animation/anim_ipo_utils.c b/source/blender/editors/animation/anim_ipo_utils.c index fad9a1a8e49..5b729c856c0 100644 --- a/source/blender/editors/animation/anim_ipo_utils.c +++ b/source/blender/editors/animation/anim_ipo_utils.c @@ -122,7 +122,7 @@ int getname_anim_fcurve(char *name, ID *id, FCurve *fcu) MEM_freeN(constName); } } - else if (ptr.data != ptr.id.data) { + else if (ptr.data != ptr.owner_id) { PropertyRNA *nameprop = RNA_struct_name_property(ptr.type); if (nameprop) { /* this gets a string which will need to be freed */ diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c index ded59466370..8c873eb6b45 100644 --- a/source/blender/editors/animation/anim_markers.c +++ b/source/blender/editors/animation/anim_markers.c @@ -486,7 +486,7 @@ static void draw_marker( float name_y = UI_DPI_FAC * 18; /* Give an offset to the marker name when selected, * or when near the current frame (5 frames range, starting from the current one). */ - if ((marker->flag & SELECT) || (IN_RANGE_INCL(marker->frame, cfra, cfra - 4))) { + if ((marker->flag & SELECT) || (IN_RANGE_INCL(marker->frame, cfra - 4, cfra))) { name_y += UI_DPI_FAC * 10; } draw_marker_name(fstyle, marker, xpos, name_y); diff --git a/source/blender/editors/animation/anim_motion_paths.c b/source/blender/editors/animation/anim_motion_paths.c index 7a5b57b1ce6..bd4886817cd 100644 --- a/source/blender/editors/animation/anim_motion_paths.c +++ b/source/blender/editors/animation/anim_motion_paths.c @@ -60,9 +60,8 @@ typedef struct MPathTarget { Object *ob; /* source object */ bPoseChannel *pchan; /* source posechannel (if applicable) */ - /* "Evaluated" Copies (these come from the background COW copie - * that provide all the coordinates we want to save off) - */ + /* "Evaluated" Copies (these come from the background COW copy + * that provide all the coordinates we want to save off). */ Object *ob_eval; /* evaluated object */ } MPathTarget; diff --git a/source/blender/editors/animation/drivers.c b/source/blender/editors/animation/drivers.c index 935d11a388f..7b9e6a10f44 100644 --- a/source/blender/editors/animation/drivers.c +++ b/source/blender/editors/animation/drivers.c @@ -218,7 +218,7 @@ static int add_driver_with_target(ReportList *UNUSED(reports), /* Create a driver variable for the target * - For transform properties, we want to automatically use "transform channel" instead - * (The only issue is with quat rotations vs euler channels...) + * (The only issue is with quaternion rotations vs euler channels...) * - To avoid problems with transform properties depending on the final transform that they * control (thus creating pseudo-cycles - see T48734), we don't use transform channels * when both the source and destinations are in same places. @@ -934,7 +934,7 @@ static const EnumPropertyItem *driver_mapping_type_itemsf(bContext *C, EnumPropertyItem *input = prop_driver_create_mapping_types; EnumPropertyItem *item = NULL; - PointerRNA ptr = {{NULL}}; + PointerRNA ptr = {NULL}; PropertyRNA *prop = NULL; int index; @@ -946,7 +946,7 @@ static const EnumPropertyItem *driver_mapping_type_itemsf(bContext *C, UI_context_active_but_prop_get(C, &ptr, &prop, &index); - if (ptr.id.data && ptr.data && prop && RNA_property_animateable(&ptr, prop)) { + if (ptr.owner_id && ptr.data && prop && RNA_property_animateable(&ptr, prop)) { const bool is_array = RNA_property_array_check(prop); while (input->identifier) { @@ -971,7 +971,7 @@ static const EnumPropertyItem *driver_mapping_type_itemsf(bContext *C, static bool add_driver_button_poll(bContext *C) { - PointerRNA ptr = {{NULL}}; + PointerRNA ptr = {NULL}; PropertyRNA *prop = NULL; int index; bool driven, special; @@ -979,7 +979,7 @@ static bool add_driver_button_poll(bContext *C) /* this operator can only run if there's a property button active, and it can be animated */ UI_context_active_but_prop_get(C, &ptr, &prop, &index); - if (!(ptr.id.data && ptr.data && prop)) { + if (!(ptr.owner_id && ptr.data && prop)) { return false; } if (!RNA_property_animateable(&ptr, prop)) { @@ -995,7 +995,7 @@ static bool add_driver_button_poll(bContext *C) * (i.e. "manual/add later"). */ static int add_driver_button_none(bContext *C, wmOperator *op, short mapping_type) { - PointerRNA ptr = {{NULL}}; + PointerRNA ptr = {NULL}; PropertyRNA *prop = NULL; int index; int success = 0; @@ -1006,12 +1006,13 @@ static int add_driver_button_none(bContext *C, wmOperator *op, short mapping_typ index = -1; } - if (ptr.id.data && ptr.data && prop && RNA_property_animateable(&ptr, prop)) { + if (ptr.owner_id && ptr.data && prop && RNA_property_animateable(&ptr, prop)) { char *path = BKE_animdata_driver_path_hack(C, &ptr, prop, NULL); short flags = CREATEDRIVER_WITH_DEFAULT_DVAR; if (path) { - success += ANIM_add_driver(op->reports, ptr.id.data, path, index, flags, DRIVER_TYPE_PYTHON); + success += ANIM_add_driver( + op->reports, ptr.owner_id, path, index, flags, DRIVER_TYPE_PYTHON); MEM_freeN(path); } } @@ -1095,28 +1096,29 @@ static void UNUSED_FUNCTION(ANIM_OT_driver_button_add_menu)(wmOperatorType *ot) static int add_driver_button_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { - PointerRNA ptr = {{NULL}}; + PointerRNA ptr = {NULL}; PropertyRNA *prop = NULL; int index; /* try to find driver using property retrieved from UI */ UI_context_active_but_prop_get(C, &ptr, &prop, &index); - if (ptr.id.data && ptr.data && prop && RNA_property_animateable(&ptr, prop)) { + if (ptr.owner_id && ptr.data && prop && RNA_property_animateable(&ptr, prop)) { /* 1) Create a new "empty" driver for this property */ char *path = BKE_animdata_driver_path_hack(C, &ptr, prop, NULL); short flags = CREATEDRIVER_WITH_DEFAULT_DVAR; short success = 0; if (path) { - success += ANIM_add_driver(op->reports, ptr.id.data, path, index, flags, DRIVER_TYPE_PYTHON); + success += ANIM_add_driver( + op->reports, ptr.owner_id, path, index, flags, DRIVER_TYPE_PYTHON); MEM_freeN(path); } if (success) { /* send updates */ UI_context_update_anim_flag(C); - DEG_id_tag_update(ptr.id.data, ID_RECALC_COPY_ON_WRITE); + DEG_id_tag_update(ptr.owner_id, ID_RECALC_COPY_ON_WRITE); DEG_relations_tag_update(CTX_data_main(C)); WM_event_add_notifier(C, NC_ANIMATION | ND_FCURVES_ORDER, NULL); } @@ -1149,7 +1151,7 @@ void ANIM_OT_driver_button_add(wmOperatorType *ot) static int remove_driver_button_exec(bContext *C, wmOperator *op) { - PointerRNA ptr = {{NULL}}; + PointerRNA ptr = {NULL}; PropertyRNA *prop = NULL; short success = 0; int index; @@ -1162,11 +1164,11 @@ static int remove_driver_button_exec(bContext *C, wmOperator *op) index = -1; } - if (ptr.id.data && ptr.data && prop) { + if (ptr.owner_id && ptr.data && prop) { char *path = BKE_animdata_driver_path_hack(C, &ptr, prop, NULL); if (path) { - success = ANIM_remove_driver(op->reports, ptr.id.data, path, index, 0); + success = ANIM_remove_driver(op->reports, ptr.owner_id, path, index, 0); MEM_freeN(path); } @@ -1205,14 +1207,14 @@ void ANIM_OT_driver_button_remove(wmOperatorType *ot) static int edit_driver_button_exec(bContext *C, wmOperator *op) { - PointerRNA ptr = {{NULL}}; + PointerRNA ptr = {NULL}; PropertyRNA *prop = NULL; int index; /* try to find driver using property retrieved from UI */ UI_context_active_but_prop_get(C, &ptr, &prop, &index); - if (ptr.id.data && ptr.data && prop) { + if (ptr.owner_id && ptr.data && prop) { UI_popover_panel_invoke(C, "GRAPH_PT_drivers_popover", true, op->reports); } @@ -1239,7 +1241,7 @@ void ANIM_OT_driver_button_edit(wmOperatorType *ot) static int copy_driver_button_exec(bContext *C, wmOperator *op) { - PointerRNA ptr = {{NULL}}; + PointerRNA ptr = {NULL}; PropertyRNA *prop = NULL; short success = 0; int index; @@ -1247,12 +1249,12 @@ static int copy_driver_button_exec(bContext *C, wmOperator *op) /* try to create driver using property retrieved from UI */ UI_context_active_but_prop_get(C, &ptr, &prop, &index); - if (ptr.id.data && ptr.data && prop && RNA_property_animateable(&ptr, prop)) { + if (ptr.owner_id && ptr.data && prop && RNA_property_animateable(&ptr, prop)) { char *path = BKE_animdata_driver_path_hack(C, &ptr, prop, NULL); if (path) { /* only copy the driver for the button that this was involved for */ - success = ANIM_copy_driver(op->reports, ptr.id.data, path, index, 0); + success = ANIM_copy_driver(op->reports, ptr.owner_id, path, index, 0); UI_context_update_anim_flag(C); @@ -1283,7 +1285,7 @@ void ANIM_OT_copy_driver_button(wmOperatorType *ot) static int paste_driver_button_exec(bContext *C, wmOperator *op) { - PointerRNA ptr = {{NULL}}; + PointerRNA ptr = {NULL}; PropertyRNA *prop = NULL; short success = 0; int index; @@ -1291,18 +1293,18 @@ static int paste_driver_button_exec(bContext *C, wmOperator *op) /* try to create driver using property retrieved from UI */ UI_context_active_but_prop_get(C, &ptr, &prop, &index); - if (ptr.id.data && ptr.data && prop && RNA_property_animateable(&ptr, prop)) { + if (ptr.owner_id && ptr.data && prop && RNA_property_animateable(&ptr, prop)) { char *path = BKE_animdata_driver_path_hack(C, &ptr, prop, NULL); if (path) { /* only copy the driver for the button that this was involved for */ - success = ANIM_paste_driver(op->reports, ptr.id.data, path, index, 0); + success = ANIM_paste_driver(op->reports, ptr.owner_id, path, index, 0); UI_context_update_anim_flag(C); DEG_relations_tag_update(CTX_data_main(C)); - DEG_id_tag_update(ptr.id.data, ID_RECALC_ANIMATION); + DEG_id_tag_update(ptr.owner_id, ID_RECALC_ANIMATION); WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME_PROP, NULL); // XXX diff --git a/source/blender/editors/animation/fmodifier_ui.c b/source/blender/editors/animation/fmodifier_ui.c index b42e8102c5b..705351522f8 100644 --- a/source/blender/editors/animation/fmodifier_ui.c +++ b/source/blender/editors/animation/fmodifier_ui.c @@ -197,7 +197,7 @@ static void draw_modifier__generator(uiLayout *layout, &data->poly_order, 1, 100, - 0, + 1, 0, TIP_("'Order' of the Polynomial (for a polynomial with n terms, 'order' is n-1)")); UI_but_func_set(but, validate_fmodifier_cb, fcm, fcurve_owner_id); @@ -335,7 +335,7 @@ static void draw_modifier__generator(uiLayout *layout, &data->poly_order, 1, 100, - 0, + 1, 0, TIP_("'Order' of the Polynomial (for a polynomial with n terms, 'order' is n-1)")); UI_but_func_set(but, validate_fmodifier_cb, fcm, fcurve_owner_id); diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c index 889d27480df..ca7e0eae136 100644 --- a/source/blender/editors/animation/keyframes_draw.c +++ b/source/blender/editors/animation/keyframes_draw.c @@ -814,8 +814,10 @@ static void draw_keylist(View2D *v2d, uint outline_color_id = GPU_vertformat_attr_add( format, "outlineColor", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); uint flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT); - immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND); + GPU_program_point_size(true); + immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND); + immUniform1f("outline_scale", 1.0f); immUniform2f( "ViewportSize", BLI_rcti_size_x(&v2d->mask) + 1, BLI_rcti_size_y(&v2d->mask) + 1); immBegin(GPU_PRIM_POINTS, key_len); diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index dcc596e67e1..0f8b8742659 100644 --- a/source/blender/editors/animation/keyframing.c +++ b/source/blender/editors/animation/keyframing.c @@ -222,7 +222,7 @@ FCurve *verify_fcurve(Main *bmain, /* sync bone group colors if applicable */ if (ptr && (ptr->type == &RNA_PoseBone)) { - Object *ob = (Object *)ptr->id.data; + Object *ob = (Object *)ptr->owner_id; bPoseChannel *pchan = (bPoseChannel *)ptr->data; bPose *pose = ob->pose; bActionGroup *grp; @@ -286,7 +286,7 @@ void update_autoflags_fcurve(FCurve *fcu, bContext *C, ReportList *reports, Poin PropertyRNA *prop; int old_flag = fcu->flag; - if ((ptr->id.data == NULL) && (ptr->data == NULL)) { + if ((ptr->owner_id == NULL) && (ptr->data == NULL)) { BKE_report(reports, RPT_ERROR, "No RNA pointer available to retrieve values for this fcurve"); return; } @@ -294,7 +294,7 @@ void update_autoflags_fcurve(FCurve *fcu, bContext *C, ReportList *reports, Poin /* try to get property we should be affecting */ if (RNA_path_resolve_property(ptr, fcu->rna_path, &tmp_ptr, &prop) == false) { /* property not found... */ - const char *idname = (ptr->id.data) ? ((ID *)ptr->id.data)->name : TIP_("<No ID pointer>"); + const char *idname = (ptr->owner_id) ? ptr->owner_id->name : TIP_("<No ID pointer>"); BKE_reportf(reports, RPT_ERROR, @@ -1203,7 +1203,7 @@ bool insert_keyframe_direct(ReportList *reports, } /* if no property given yet, try to validate from F-Curve info */ - if ((ptr.id.data == NULL) && (ptr.data == NULL)) { + if ((ptr.owner_id == NULL) && (ptr.data == NULL)) { BKE_report( reports, RPT_ERROR, "No RNA pointer available to retrieve values for keyframing from"); return false; @@ -1214,7 +1214,7 @@ bool insert_keyframe_direct(ReportList *reports, /* try to get property we should be affecting */ if (RNA_path_resolve_property(&ptr, fcu->rna_path, &tmp_ptr, &prop) == false) { /* property not found... */ - const char *idname = (ptr.id.data) ? ((ID *)ptr.id.data)->name : TIP_("<No ID pointer>"); + const char *idname = (ptr.owner_id) ? ptr.owner_id->name : TIP_("<No ID pointer>"); BKE_reportf(reports, RPT_ERROR, @@ -1544,9 +1544,9 @@ static bool delete_keyframe_fcurve(AnimData *adt, FCurve *fcu, float cfra) static void deg_tag_after_keyframe_delete(Main *bmain, ID *id, AnimData *adt) { if (adt->action == NULL) { - /* In the case last f-curve wes removed need to inform dependency graph + /* In the case last f-curve was removed need to inform dependency graph * about relations update, since it needs to get rid of animation operation - * for this datablock. */ + * for this data-block. */ DEG_id_tag_update_ex(bmain, id, ID_RECALC_ANIMATION_NO_FLUSH); DEG_relations_tag_update(bmain); } @@ -2350,7 +2350,7 @@ static int insert_key_button_exec(bContext *C, wmOperator *op) Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); ToolSettings *ts = scene->toolsettings; - PointerRNA ptr = {{NULL}}; + PointerRNA ptr = {NULL}; PropertyRNA *prop = NULL; char *path; uiBut *but; @@ -2369,7 +2369,7 @@ static int insert_key_button_exec(bContext *C, wmOperator *op) return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH); } - if ((ptr.id.data && ptr.data && prop) && RNA_property_animateable(&ptr, prop)) { + if ((ptr.owner_id && ptr.data && prop) && RNA_property_animateable(&ptr, prop)) { if (ptr.type == &RNA_NlaStrip) { /* Handle special properties for NLA Strips, whose F-Curves are stored on the * strips themselves. These are stored separately or else the properties will @@ -2435,7 +2435,7 @@ static int insert_key_button_exec(bContext *C, wmOperator *op) success = insert_keyframe(bmain, op->reports, - ptr.id.data, + ptr.owner_id, NULL, group, path, @@ -2473,7 +2473,7 @@ static int insert_key_button_exec(bContext *C, wmOperator *op) } if (success) { - ID *id = ptr.id.data; + ID *id = ptr.owner_id; AnimData *adt = BKE_animdata_from_id(id); if (adt->action != NULL) { DEG_id_tag_update(&adt->action->id, ID_RECALC_ANIMATION_NO_FLUSH); @@ -2513,7 +2513,7 @@ void ANIM_OT_keyframe_insert_button(wmOperatorType *ot) static int delete_key_button_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - PointerRNA ptr = {{NULL}}; + PointerRNA ptr = {NULL}; PropertyRNA *prop = NULL; Main *bmain = CTX_data_main(C); char *path; @@ -2528,13 +2528,13 @@ static int delete_key_button_exec(bContext *C, wmOperator *op) return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH); } - if (ptr.id.data && ptr.data && prop) { + if (ptr.owner_id && ptr.data && prop) { if (BKE_nlastrip_has_curves_for_property(&ptr, prop)) { /* Handle special properties for NLA Strips, whose F-Curves are stored on the * strips themselves. These are stored separately or else the properties will * not have any effect. */ - ID *id = ptr.id.data; + ID *id = ptr.owner_id; NlaStrip *strip = (NlaStrip *)ptr.data; FCurve *fcu = list_find_fcurve(&strip->fcurves, RNA_property_identifier(prop), 0); @@ -2577,7 +2577,7 @@ static int delete_key_button_exec(bContext *C, wmOperator *op) } success = delete_keyframe( - bmain, op->reports, ptr.id.data, NULL, NULL, path, index, cfra, 0); + bmain, op->reports, ptr.owner_id, NULL, NULL, path, index, cfra, 0); MEM_freeN(path); } else if (G.debug & G_DEBUG) { @@ -2622,7 +2622,7 @@ void ANIM_OT_keyframe_delete_button(wmOperatorType *ot) static int clear_key_button_exec(bContext *C, wmOperator *op) { - PointerRNA ptr = {{NULL}}; + PointerRNA ptr = {NULL}; PropertyRNA *prop = NULL; Main *bmain = CTX_data_main(C); char *path; @@ -2636,7 +2636,7 @@ static int clear_key_button_exec(bContext *C, wmOperator *op) return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH); } - if (ptr.id.data && ptr.data && prop) { + if (ptr.owner_id && ptr.data && prop) { path = RNA_path_from_ID_to_property(&ptr, prop); if (path) { @@ -2645,7 +2645,7 @@ static int clear_key_button_exec(bContext *C, wmOperator *op) index = -1; } - success += clear_keyframe(bmain, op->reports, ptr.id.data, NULL, NULL, path, index, 0); + success += clear_keyframe(bmain, op->reports, ptr.owner_id, NULL, NULL, path, index, 0); MEM_freeN(path); } else if (G.debug & G_DEBUG) { diff --git a/source/blender/editors/animation/keyingsets.c b/source/blender/editors/animation/keyingsets.c index ccd0fc54611..258c0e4f1f6 100644 --- a/source/blender/editors/animation/keyingsets.c +++ b/source/blender/editors/animation/keyingsets.c @@ -287,7 +287,7 @@ static int add_keyingset_button_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); KeyingSet *ks = NULL; PropertyRNA *prop = NULL; - PointerRNA ptr = {{NULL}}; + PointerRNA ptr = {NULL}; char *path = NULL; short success = 0; int index = 0, pflag = 0; @@ -332,7 +332,7 @@ static int add_keyingset_button_exec(bContext *C, wmOperator *op) } /* check if property is able to be added */ - if (ptr.id.data && ptr.data && prop && RNA_property_animateable(&ptr, prop)) { + if (ptr.owner_id && ptr.data && prop && RNA_property_animateable(&ptr, prop)) { path = RNA_path_from_ID_to_property(&ptr, prop); if (path) { @@ -348,7 +348,7 @@ static int add_keyingset_button_exec(bContext *C, wmOperator *op) } /* add path to this setting */ - BKE_keyingset_add_path(ks, ptr.id.data, NULL, path, index, pflag, KSP_GROUP_KSNAME); + BKE_keyingset_add_path(ks, ptr.owner_id, NULL, path, index, pflag, KSP_GROUP_KSNAME); ks->active_path = BLI_listbase_count(&ks->paths); success = 1; @@ -393,7 +393,7 @@ static int remove_keyingset_button_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); KeyingSet *ks = NULL; PropertyRNA *prop = NULL; - PointerRNA ptr = {{NULL}}; + PointerRNA ptr = {NULL}; char *path = NULL; short success = 0; int index = 0; @@ -420,14 +420,14 @@ static int remove_keyingset_button_exec(bContext *C, wmOperator *op) ks = BLI_findlink(&scene->keyingsets, scene->active_keyingset - 1); } - if (ptr.id.data && ptr.data && prop) { + if (ptr.owner_id && ptr.data && prop) { path = RNA_path_from_ID_to_property(&ptr, prop); if (path) { KS_Path *ksp; /* try to find a path matching this description */ - ksp = BKE_keyingset_find_path(ks, ptr.id.data, ks->name, path, index, KSP_GROUP_KSNAME); + ksp = BKE_keyingset_find_path(ks, ptr.owner_id, ks->name, path, index, KSP_GROUP_KSNAME); if (ksp) { BKE_keyingset_free_path(ks, ksp); @@ -712,9 +712,9 @@ int ANIM_scene_get_keyingset_index(Scene *scene, KeyingSet *ks) } } - /* still here, so try builtins list too - * - builtins are from (<= -1) - * - none/invalid is (= 0) + /* Still here, so try built-ins list too: + * - Built-ins are from (<= -1). + * - None/Invalid is (= 0). */ index = BLI_findindex(&builtin_keyingsets, ks); if (index != -1) { diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c index d2fa77f90be..1073034383d 100644 --- a/source/blender/editors/armature/armature_add.c +++ b/source/blender/editors/armature/armature_add.c @@ -769,7 +769,7 @@ static int armature_symmetrize_exec(bContext *C, wmOperator *op) EditBone *ebone = ebone_iter->temp.ebone; - /* copy flags incase bone is pre-existing data */ + /* Copy flags in case bone is pre-existing data. */ ebone->flag = (ebone->flag & ~flag_copy) | (ebone_iter->flag & flag_copy); if (ebone_iter->parent == NULL) { diff --git a/source/blender/editors/armature/armature_edit.c b/source/blender/editors/armature/armature_edit.c index 4e6661b1d15..c4c10549da3 100644 --- a/source/blender/editors/armature/armature_edit.c +++ b/source/blender/editors/armature/armature_edit.c @@ -62,22 +62,10 @@ /* ************************** Object Tools Exports ******************************* */ /* NOTE: these functions are exported to the Object module to be called from the tools there */ -void ED_armature_transform_apply(Main *bmain, Object *ob, float mat[4][4], const bool do_props) -{ - bArmature *arm = ob->data; - - /* Put the armature into editmode */ - ED_armature_to_edit(arm); - - /* Transform the bones */ - ED_armature_transform_bones(arm, mat, do_props); - - /* Turn the list into an armature */ - ED_armature_from_edit(bmain, arm); - ED_armature_edit_free(arm); -} - -void ED_armature_transform_bones(struct bArmature *arm, float mat[4][4], const bool do_props) +/** + * See #BKE_armature_transform for object-mode transform. + */ +void ED_armature_edit_transform(bArmature *arm, const float mat[4][4], const bool do_props) { EditBone *ebone; float scale = mat4_to_scale(mat); /* store the scale of the matrix here to use on envelopes */ @@ -114,21 +102,13 @@ void ED_armature_transform_bones(struct bArmature *arm, float mat[4][4], const b } } -void ED_armature_transform(Main *bmain, bArmature *arm, float mat[4][4], const bool do_props) +void ED_armature_transform(bArmature *arm, const float mat[4][4], const bool do_props) { if (arm->edbo) { - ED_armature_transform_bones(arm, mat, do_props); + ED_armature_edit_transform(arm, mat, do_props); } else { - /* Put the armature into editmode */ - ED_armature_to_edit(arm); - - /* Transform the bones */ - ED_armature_transform_bones(arm, mat, do_props); - - /* Go back to object mode*/ - ED_armature_from_edit(bmain, arm); - ED_armature_edit_free(arm); + BKE_armature_transform(arm, mat, do_props); } } diff --git a/source/blender/editors/armature/armature_intern.h b/source/blender/editors/armature/armature_intern.h index 569eb7e2e04..fa562ab0f44 100644 --- a/source/blender/editors/armature/armature_intern.h +++ b/source/blender/editors/armature/armature_intern.h @@ -30,7 +30,6 @@ struct wmOperatorType; struct Base; struct Object; struct Scene; -struct bAction; struct bContext; struct bPoseChannel; diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c index 23ddf77e63d..eff621d7b71 100644 --- a/source/blender/editors/armature/armature_select.c +++ b/source/blender/editors/armature/armature_select.c @@ -46,6 +46,7 @@ #include "ED_armature.h" #include "ED_object.h" +#include "ED_outliner.h" #include "ED_screen.h" #include "ED_select_utils.h" #include "ED_view3d.h" @@ -356,6 +357,8 @@ static int armature_select_linked_invoke(bContext *C, wmOperator *op, const wmEv } } + ED_outliner_select_sync_from_edit_bone_tag(C); + ED_armature_edit_sync_selection(arm->edbo); WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, base->object); @@ -1027,6 +1030,8 @@ static int armature_de_select_all_exec(bContext *C, wmOperator *op) } CTX_DATA_END; + ED_outliner_select_sync_from_edit_bone_tag(C); + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, NULL); return OPERATOR_FINISHED; @@ -1148,6 +1153,8 @@ static int armature_de_select_more_exec(bContext *C, wmOperator *UNUSED(op)) WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); } MEM_freeN(objects); + + ED_outliner_select_sync_from_edit_bone_tag(C); return OPERATOR_FINISHED; } @@ -1178,6 +1185,8 @@ static int armature_de_select_less_exec(bContext *C, wmOperator *UNUSED(op)) WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); } MEM_freeN(objects); + + ED_outliner_select_sync_from_edit_bone_tag(C); return OPERATOR_FINISHED; } @@ -1569,6 +1578,8 @@ static int armature_select_similar_exec(bContext *C, wmOperator *op) #undef STRUCT_SIZE_AND_OFFSET + ED_outliner_select_sync_from_edit_bone_tag(C); + return OPERATOR_FINISHED; } @@ -1663,6 +1674,8 @@ static int armature_select_hierarchy_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + ED_outliner_select_sync_from_edit_bone_tag(C); + ED_armature_edit_sync_selection(arm->edbo); WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); @@ -1748,6 +1761,8 @@ static int armature_select_mirror_exec(bContext *C, wmOperator *op) arm->act_edbone = ebone_mirror_act; } + ED_outliner_select_sync_from_edit_bone_tag(C); + ED_armature_edit_sync_selection(arm->edbo); WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); @@ -1876,6 +1891,7 @@ static int armature_shortest_path_pick_invoke(bContext *C, wmOperator *op, const if (changed) { arm->act_edbone = ebone_dst; + ED_outliner_select_sync_from_edit_bone_tag(C); ED_armature_edit_sync_selection(arm->edbo); WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); diff --git a/source/blender/editors/armature/armature_utils.c b/source/blender/editors/armature/armature_utils.c index d8777b7e0b7..cd299906b4c 100644 --- a/source/blender/editors/armature/armature_utils.c +++ b/source/blender/editors/armature/armature_utils.c @@ -381,23 +381,37 @@ void armature_tag_unselect(bArmature *arm) void ED_armature_ebone_transform_mirror_update(bArmature *arm, EditBone *ebo, bool check_select) { + /* TODO When this function is called by property updates, + * cancelling the value change will not restore mirrored bone correctly. */ + + /* Currently check_select==true when this function is called from a transform operator, + * eg. from 3d viewport. */ + /* no layer check, correct mirror is more important */ if (!check_select || ebo->flag & (BONE_TIPSEL | BONE_ROOTSEL)) { EditBone *eboflip = ED_armature_ebone_get_mirrored(arm->edbo, ebo); if (eboflip) { - /* we assume X-axis flipping for now */ - if (check_select && ebo->flag & BONE_TIPSEL) { - EditBone *children; + /* We assume X-axis flipping for now. */ + + /* Always mirror roll, since it can be changed by moving either head or tail. */ + eboflip->roll = -ebo->roll; + + if (!check_select || ebo->flag & BONE_TIPSEL) { + /* Mirror tail properties. */ eboflip->tail[0] = -ebo->tail[0]; eboflip->tail[1] = ebo->tail[1]; eboflip->tail[2] = ebo->tail[2]; eboflip->rad_tail = ebo->rad_tail; - eboflip->roll = -ebo->roll; eboflip->curve_out_x = -ebo->curve_out_x; + eboflip->curve_out_y = ebo->curve_out_y; + eboflip->scale_out_x = ebo->scale_out_x; + eboflip->scale_out_y = ebo->scale_out_y; + eboflip->ease2 = ebo->ease2; eboflip->roll2 = -ebo->roll2; - /* Also move connected children, in case children's name aren't mirrored properly */ + /* Also move connected children, in case children's name aren't mirrored properly. */ + EditBone *children; for (children = arm->edbo->first; children; children = children->next) { if (children->parent == eboflip && children->flag & BONE_CONNECTED) { copy_v3_v3(children->head, eboflip->tail); @@ -405,32 +419,39 @@ void ED_armature_ebone_transform_mirror_update(bArmature *arm, EditBone *ebo, bo } } } + if (!check_select || ebo->flag & BONE_ROOTSEL) { + /* Mirror head properties. */ eboflip->head[0] = -ebo->head[0]; eboflip->head[1] = ebo->head[1]; eboflip->head[2] = ebo->head[2]; eboflip->rad_head = ebo->rad_head; - eboflip->roll = -ebo->roll; + eboflip->curve_in_x = -ebo->curve_in_x; + eboflip->curve_in_y = ebo->curve_in_y; + eboflip->scale_in_x = ebo->scale_in_x; + eboflip->scale_in_y = ebo->scale_in_y; + eboflip->ease1 = ebo->ease1; eboflip->roll1 = -ebo->roll1; - /* Also move connected parent, in case parent's name isn't mirrored properly */ + /* Also move connected parent, in case parent's name isn't mirrored properly. */ if (eboflip->parent && eboflip->flag & BONE_CONNECTED) { EditBone *parent = eboflip->parent; copy_v3_v3(parent->tail, eboflip->head); parent->rad_tail = ebo->rad_head; } } + if (!check_select || ebo->flag & BONE_SELECTED) { + /* Mirror bone body properties (both head and tail are selected). */ + /* TODO: These values can also be changed from pose mode, + * so only mirroring them in edit mode is not ideal. */ eboflip->dist = ebo->dist; - eboflip->roll = -ebo->roll; + eboflip->weight = ebo->weight; + + eboflip->segments = ebo->segments; eboflip->xwidth = ebo->xwidth; eboflip->zwidth = ebo->zwidth; - - eboflip->curve_in_x = -ebo->curve_in_x; - eboflip->curve_out_x = -ebo->curve_out_x; - eboflip->roll1 = -ebo->roll1; - eboflip->roll2 = -ebo->roll2; } } } diff --git a/source/blender/editors/armature/pose_select.c b/source/blender/editors/armature/pose_select.c index 8434fee6e78..a59067e60c1 100644 --- a/source/blender/editors/armature/pose_select.c +++ b/source/blender/editors/armature/pose_select.c @@ -54,6 +54,7 @@ #include "ED_keyframing.h" #include "ED_mesh.h" #include "ED_object.h" +#include "ED_outliner.h" #include "ED_screen.h" #include "ED_select_utils.h" #include "ED_view3d.h" @@ -449,6 +450,8 @@ static int pose_select_connected_invoke(bContext *C, wmOperator *op, const wmEve selectconnected_posebonechildren(base->object, curBone, extend); } + ED_outliner_select_sync_from_pose_bone_tag(C); + ED_pose_bone_select_tag_update(base->object); return OPERATOR_FINISHED; @@ -514,6 +517,8 @@ static int pose_de_select_all_exec(bContext *C, wmOperator *op) } CTX_DATA_END; + ED_outliner_select_sync_from_pose_bone_tag(C); + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, NULL); return OPERATOR_FINISHED; @@ -560,6 +565,8 @@ static int pose_select_parent_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_CANCELLED; } + ED_outliner_select_sync_from_pose_bone_tag(C); + ED_pose_bone_select_tag_update(ob); return OPERATOR_FINISHED; } @@ -624,6 +631,8 @@ static int pose_select_constraint_target_exec(bContext *C, wmOperator *UNUSED(op return OPERATOR_CANCELLED; } + ED_outliner_select_sync_from_pose_bone_tag(C); + return OPERATOR_FINISHED; } @@ -712,6 +721,8 @@ static int pose_select_hierarchy_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + ED_outliner_select_sync_from_pose_bone_tag(C); + ED_pose_bone_select_tag_update(ob); return OPERATOR_FINISHED; @@ -796,7 +807,7 @@ static bool pose_select_same_group(bContext *C, bool extend) group_flags = NULL; ob_index = -1; ob_prev = NULL; - CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, visible_pose_bones, Object, *ob) { + CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, visible_pose_bones, Object *, ob) { if (ob != ob_prev) { ob_index++; group_flags = group_flags_array + (ob_index * groups_len); @@ -1061,6 +1072,8 @@ static int pose_select_grouped_exec(bContext *C, wmOperator *op) /* report done status */ if (changed) { + ED_outliner_select_sync_from_pose_bone_tag(C); + return OPERATOR_FINISHED; } else { @@ -1172,6 +1185,8 @@ static int pose_select_mirror_exec(bContext *C, wmOperator *op) } MEM_freeN(objects); + ED_outliner_select_sync_from_pose_bone_tag(C); + return OPERATOR_FINISHED; } diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c index c8e79b879a4..6274eb549da 100644 --- a/source/blender/editors/armature/pose_slide.c +++ b/source/blender/editors/armature/pose_slide.c @@ -442,7 +442,7 @@ static void pose_slide_apply_props(tPoseSlideOp *pso, tPChanFCurveLink *pfl, const char prop_prefix[]) { - PointerRNA ptr = {{NULL}}; + PointerRNA ptr = {NULL}; LinkData *ld; int len = strlen(pfl->pchan_path); diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index c93531bb6cc..d7650db546d 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -51,6 +51,7 @@ #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" +#include "DEG_depsgraph_query.h" #include "WM_api.h" #include "WM_types.h" @@ -5679,6 +5680,7 @@ static int add_vertex_invoke(bContext *C, wmOperator *op, const wmEvent *event) }, mval, NULL, + NULL, location, NULL); @@ -7104,14 +7106,15 @@ static int match_texture_space_exec(bContext *C, wmOperator *UNUSED(op)) (void)depsgraph; Object *object = CTX_data_active_object(C); + Object *object_eval = DEG_get_evaluated_object(depsgraph, object); Curve *curve = (Curve *)object->data; float min[3], max[3], size[3], loc[3]; int a; - BLI_assert(object->runtime.curve_cache != NULL); + BLI_assert(object_eval->runtime.curve_cache != NULL); INIT_MINMAX(min, max); - BKE_displist_minmax(&object->runtime.curve_cache->disp, min, max); + BKE_displist_minmax(&object_eval->runtime.curve_cache->disp, min, max); mid_v3_v3v3(loc, min, max); @@ -7138,6 +7141,7 @@ static int match_texture_space_exec(bContext *C, wmOperator *UNUSED(op)) curve->texflag &= ~CU_AUTOSPACE; WM_event_add_notifier(C, NC_GEOM | ND_DATA, curve); + DEG_id_tag_update(&curve->id, ID_RECALC_GEOMETRY); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/curve/editcurve_paint.c b/source/blender/editors/curve/editcurve_paint.c index 5e0053782d4..c7c19aa2d02 100644 --- a/source/blender/editors/curve/editcurve_paint.c +++ b/source/blender/editors/curve/editcurve_paint.c @@ -120,7 +120,7 @@ struct CurveDrawData { struct { float mouse[2]; - /* used incase we can't calculate the depth */ + /* Used in case we can't calculate the depth. */ float location_world[3]; float location_world_valid[3]; @@ -1053,7 +1053,7 @@ static int curve_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event) const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input"); - /* fallback (incase we can't find the depth on first test) */ + /* Fallback (in case we can't find the depth on first test). */ { const float mval_fl[2] = {UNPACK2(event->mval)}; float center[3]; diff --git a/source/blender/editors/curve/editcurve_select.c b/source/blender/editors/curve/editcurve_select.c index d3f0ebfda3c..f67ccf1e4bd 100644 --- a/source/blender/editors/curve/editcurve_select.c +++ b/source/blender/editors/curve/editcurve_select.c @@ -1417,7 +1417,7 @@ void CURVE_OT_select_nth(wmOperatorType *ot) { /* identifiers */ ot->name = "Checker Deselect"; - ot->description = "Deselect every other vertex"; + ot->description = "Deselect every Nth point starting from the active one"; ot->idname = "CURVE_OT_select_nth"; /* api callbacks */ diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c index f0c1abff201..781eb2634fb 100644 --- a/source/blender/editors/curve/editfont.c +++ b/source/blender/editors/curve/editfont.c @@ -71,7 +71,11 @@ static int kill_selection(Object *obedit, int ins); -/************************* utilities ******************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Internal Utilities + * \{ */ static wchar_t findaccent(wchar_t char1, unsigned int code) { @@ -440,8 +444,48 @@ static void text_update_edited(bContext *C, Object *obedit, int mode) WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data); } +static int kill_selection(Object *obedit, int ins) /* 1 == new character */ +{ + Curve *cu = obedit->data; + EditFont *ef = cu->editfont; + int selend, selstart, direction; + int offset = 0; + int getfrom; + + direction = BKE_vfont_select_get(obedit, &selstart, &selend); + if (direction) { + int size; + if (ins) { + offset = 1; + } + if (ef->pos >= selstart) { + ef->pos = selstart + offset; + } + if ((direction == -1) && ins) { + selstart++; + selend++; + } + getfrom = selend + offset; + if (ins == 0) { + getfrom++; + } + size = (ef->len * sizeof(wchar_t)) - (selstart * sizeof(wchar_t)) + (offset * sizeof(wchar_t)); + memmove(ef->textbuf + selstart, ef->textbuf + getfrom, size); + memmove(ef->textbufinfo + selstart, + ef->textbufinfo + getfrom, + ((ef->len - selstart) + offset) * sizeof(CharInfo)); + ef->len -= ((selend - selstart) + 1); + ef->selstart = ef->selend = 0; + } + + return (direction); +} + +/** \} */ + /* -------------------------------------------------------------------- */ -/* Generic Paste Functions */ +/** \name Generic Paste Functions + * \{ */ /* text_update_edited(C, scene, obedit, 1, FO_EDIT); */ static bool font_paste_wchar(Object *obedit, @@ -506,8 +550,11 @@ static bool font_paste_utf8(bContext *C, const char *str, const size_t str_len) return retval; } +/** \} */ + /* -------------------------------------------------------------------- */ -/* Paste From File*/ +/** \name Paste From File Operator + * \{ */ static int paste_from_file(bContext *C, ReportList *reports, const char *filename) { @@ -585,7 +632,11 @@ void FONT_OT_text_paste_from_file(wmOperatorType *ot) FILE_SORT_ALPHA); } -/******************* text to object operator ********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Text To Object + * \{ */ static void txt_add_object(bContext *C, TextLine *firstline, int totline, const float offset[3]) { @@ -703,46 +754,11 @@ void ED_text_to_object(bContext *C, Text *text, const bool split_lines) } } -/********************** utilities ***************************/ - -static int kill_selection(Object *obedit, int ins) /* 1 == new character */ -{ - Curve *cu = obedit->data; - EditFont *ef = cu->editfont; - int selend, selstart, direction; - int offset = 0; - int getfrom; - - direction = BKE_vfont_select_get(obedit, &selstart, &selend); - if (direction) { - int size; - if (ins) { - offset = 1; - } - if (ef->pos >= selstart) { - ef->pos = selstart + offset; - } - if ((direction == -1) && ins) { - selstart++; - selend++; - } - getfrom = selend + offset; - if (ins == 0) { - getfrom++; - } - size = (ef->len * sizeof(wchar_t)) - (selstart * sizeof(wchar_t)) + (offset * sizeof(wchar_t)); - memmove(ef->textbuf + selstart, ef->textbuf + getfrom, size); - memmove(ef->textbufinfo + selstart, - ef->textbufinfo + getfrom, - ((ef->len - selstart) + offset) * sizeof(CharInfo)); - ef->len -= ((selend - selstart) + 1); - ef->selstart = ef->selend = 0; - } - - return (direction); -} +/** \} */ -/******************* set style operator ********************/ +/* -------------------------------------------------------------------- */ +/** \name Set Style Operator + * \{ */ static const EnumPropertyItem style_items[] = { {CU_CHINFO_BOLD, "BOLD", 0, "Bold", ""}, @@ -806,7 +822,11 @@ void FONT_OT_style_set(wmOperatorType *ot) RNA_def_boolean(ot->srna, "clear", 0, "Clear", "Clear style rather than setting it"); } -/******************* toggle style operator ********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Toggle Style Operator + * \{ */ static int toggle_style_exec(bContext *C, wmOperator *op) { @@ -845,8 +865,11 @@ void FONT_OT_style_toggle(wmOperatorType *ot) ot->srna, "style", style_items, CU_CHINFO_BOLD, "Style", "Style to set selection to"); } +/** \} */ + /* -------------------------------------------------------------------- */ -/* Select All */ +/** \name Select All Operator + * \{ */ static int font_select_all_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -883,7 +906,11 @@ void FONT_OT_select_all(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/******************* copy text operator ********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Copy Text Operator + * \{ */ static void copy_selection(Object *obedit) { @@ -932,7 +959,11 @@ void FONT_OT_text_copy(wmOperatorType *ot) ot->poll = ED_operator_editfont; } -/******************* cut text operator ********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Cut Text Operator + * \{ */ static int cut_text_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -966,7 +997,11 @@ void FONT_OT_text_cut(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/******************* paste text operator ********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Paste Text Operator + * \{ */ static bool paste_selection(Object *obedit, ReportList *reports) { @@ -1066,7 +1101,11 @@ void FONT_OT_text_paste(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/************************ move operator ************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Move Operator + * \{ */ static const EnumPropertyItem move_type_items[] = { {LINE_BEGIN, "LINE_BEGIN", 0, "Line Begin", ""}, @@ -1232,7 +1271,11 @@ void FONT_OT_move(wmOperatorType *ot) RNA_def_enum(ot->srna, "type", move_type_items, LINE_BEGIN, "Type", "Where to move cursor to"); } -/******************* move select operator ********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Move Select Operator + * \{ */ static int move_select_exec(bContext *C, wmOperator *op) { @@ -1264,7 +1307,11 @@ void FONT_OT_move_select(wmOperatorType *ot) "Where to move cursor to, to make a selection"); } -/************************* change spacing **********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Change Spacing + * \{ */ static int change_spacing_exec(bContext *C, wmOperator *op) { @@ -1314,7 +1361,11 @@ void FONT_OT_change_spacing(wmOperatorType *ot) 20); } -/************************* change character **********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Change Character + * \{ */ static int change_character_exec(bContext *C, wmOperator *op) { @@ -1368,7 +1419,11 @@ void FONT_OT_change_character(wmOperatorType *ot) 255); } -/******************* line break operator ********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Line Break Operator + * \{ */ static int line_break_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -1400,7 +1455,11 @@ void FONT_OT_line_break(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/******************* delete operator **********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Delete Operator + * \{ */ static const EnumPropertyItem delete_type_items[] = { {DEL_NEXT_CHAR, "NEXT_CHARACTER", 0, "Next Character", ""}, @@ -1549,7 +1608,11 @@ void FONT_OT_delete(wmOperatorType *ot) "Which part of the text to delete"); } -/*********************** insert text operator *************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Insert Text Operator + * \{ */ static int insert_text_exec(bContext *C, wmOperator *op) { @@ -1700,7 +1763,12 @@ void FONT_OT_text_insert(wmOperatorType *ot) "Next typed character will strike through previous, for special character input"); } -/*********************** textbox add operator *************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Textbox Add Operator + * \{ */ + static int textbox_add_exec(bContext *C, wmOperator *UNUSED(op)) { Object *obedit = CTX_data_active_object(C); @@ -1736,7 +1804,11 @@ void FONT_OT_textbox_add(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/*********************** textbox remove operator *************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Textbox Remove Operator + * \{ */ static int textbox_remove_exec(bContext *C, wmOperator *op) { @@ -1778,7 +1850,11 @@ void FONT_OT_textbox_remove(wmOperatorType *ot) RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "The current text box", 0, INT_MAX); } -/***************** editmode enter/exit ********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Editmode Enter/Exit + * \{ */ void ED_curve_editfont_make(Object *obedit) { @@ -1851,7 +1927,11 @@ void ED_curve_editfont_free(Object *obedit) BKE_curve_editfont_free((Curve *)obedit->data); } -/********************** set case operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Set Case Operator + * \{ */ static const EnumPropertyItem case_items[] = { {CASE_LOWER, "LOWER", 0, "Lower", ""}, @@ -1920,7 +2000,11 @@ void FONT_OT_case_set(wmOperatorType *ot) RNA_def_enum(ot->srna, "case", case_items, CASE_LOWER, "Case", "Lower or upper case"); } -/********************** toggle case operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Toggle Case Operator + * \{ */ static int toggle_case_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -2031,7 +2115,7 @@ static int open_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event) if (pprop->prop) { idptr = RNA_property_pointer_get((PointerRNA *)pprop, pprop->prop); - vfont = idptr.id.data; + vfont = (VFont *)idptr.owner_id; } path = (vfont && !BKE_vfont_is_builtin(vfont)) ? vfont->name : U.fontdir; @@ -2071,7 +2155,11 @@ void FONT_OT_open(wmOperatorType *ot) FILE_SORT_ALPHA); } -/******************* delete operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Delete Operator + * \{ */ static int font_unlink_exec(bContext *C, wmOperator *op) { @@ -2190,3 +2278,5 @@ bool ED_curve_editfont_select_pick( return false; } } + +/** \} */ diff --git a/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c index d23965269ab..fa9c0f1fbb2 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c @@ -363,8 +363,8 @@ static void gizmo_arrow_exit(bContext *C, wmGizmo *gz, const bool cancel) const bool is_prop_valid = WM_gizmo_target_property_is_valid(gz_prop); if (!cancel) { - /* Assign incase applying the operation needs an updated offset - * editmesh bisect needs this. */ + /* Assign in case applying the operation needs an updated offset + * edit-mesh bisect needs this. */ if (is_prop_valid) { const int transform_flag = RNA_enum_get(arrow->gizmo.ptr, "transform"); const bool constrained = (transform_flag & ED_GIZMO_ARROW_XFORM_FLAG_CONSTRAINED) != 0; diff --git a/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c index e2a86469da1..406f76bc65e 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c @@ -358,7 +358,7 @@ static void gizmo_cage3d_draw_intern( bool show = false; if (gz->highlight_part == ED_GIZMO_CAGE3D_PART_TRANSLATE) { /* Only show if we're drawing the center handle - * otherwise the entire rectangle is the hotspot. */ + * otherwise the entire rectangle is the hot-spot. */ if (draw_options & ED_GIZMO_CAGE2D_DRAW_FLAG_XFORM_CENTER_HANDLE) { show = true; } diff --git a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c index 21f779b72b1..37ee95d5058 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c @@ -283,6 +283,7 @@ static int gizmo_move_modal(bContext *C, .use_occlusion_test = true, }, mval_fl, + NULL, &dist_px, co, NULL)) { diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c index 07b61751b22..22f1753a810 100644 --- a/source/blender/editors/gpencil/annotate_paint.c +++ b/source/blender/editors/gpencil/annotate_paint.c @@ -1156,7 +1156,7 @@ static tGPsdata *gp_session_initpaint(bContext *C) /* create new context data */ p = MEM_callocN(sizeof(tGPsdata), "Annotation Drawing Data"); - /* Try to initialise context data + /* Try to initialize context data * WARNING: This may not always succeed (e.g. using GP in an annotation-only context) */ if (gp_session_initdata(C, p) == 0) { @@ -1252,15 +1252,6 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps /* Ensure active frame is set correctly... */ p->gpf = p->gpl->actframe; - /* Restrict eraser to only affecting selected strokes, if the "selection mask" is on - * (though this is only available in editmode) - */ - if (p->gpd->flag & GP_DATA_STROKE_EDITMODE) { - if (ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_SELECT_MASK) { - p->flags |= GP_PAINTFLAG_SELECTMASK; - } - } - if (has_layer_to_erase == false) { p->status = GP_STATUS_CAPTURE; // if (G.debug & G_DEBUG) diff --git a/source/blender/editors/gpencil/gpencil_add_monkey.c b/source/blender/editors/gpencil/gpencil_add_monkey.c index c4528518009..139697ad0e3 100644 --- a/source/blender/editors/gpencil/gpencil_add_monkey.c +++ b/source/blender/editors/gpencil/gpencil_add_monkey.c @@ -54,7 +54,7 @@ static int gpencil_monkey_color( short *totcol = give_totcolp(ob); Material *ma = NULL; for (short i = 0; i < *totcol; i++) { - ma = give_current_material(ob, i + 1); + ma = BKE_material_gpencil_get(ob, i + 1); if (STREQ(ma->id.name, pct->name)) { return i; } diff --git a/source/blender/editors/gpencil/gpencil_add_stroke.c b/source/blender/editors/gpencil/gpencil_add_stroke.c index 80e239c9ae5..74617599eaa 100644 --- a/source/blender/editors/gpencil/gpencil_add_stroke.c +++ b/source/blender/editors/gpencil/gpencil_add_stroke.c @@ -53,7 +53,7 @@ static int gp_stroke_material(Main *bmain, Object *ob, const ColorTemplate *pct, short *totcol = give_totcolp(ob); Material *ma = NULL; for (short i = 0; i < *totcol; i++) { - ma = give_current_material(ob, i + 1); + ma = BKE_material_gpencil_get(ob, i + 1); if (STREQ(ma->id.name, pct->name)) { return i; } diff --git a/source/blender/editors/gpencil/gpencil_brush.c b/source/blender/editors/gpencil/gpencil_brush.c index 9124c0f5d51..d1c4f271321 100644 --- a/source/blender/editors/gpencil/gpencil_brush.c +++ b/source/blender/editors/gpencil/gpencil_brush.c @@ -105,6 +105,7 @@ typedef struct tGP_BrushEditData { eGP_Sculpt_Types brush_type; eGP_Sculpt_Types brush_type_old; eGP_Sculpt_Flag flag; + eGP_Sculpt_SelectMaskFlag mask; /* Space Conversion Data */ GP_SpaceConversion gsc; @@ -133,8 +134,8 @@ typedef struct tGP_BrushEditData { /* - effect vector (e.g. 2D/3D translation for grab brush) */ float dvec[3]; - /* rotation for derived data */ - float rot; + /* rotation for evaluated data */ + float rot_eval; /* - multiframe falloff factor */ float mf_falloff; @@ -300,6 +301,20 @@ static float gp_brush_influence_calc(tGP_BrushEditData *gso, const int radius, c return influence; } +/* Force recal filling data */ +static void gp_recalc_geometry(bGPDstroke *gps) +{ + bGPDstroke *gps_orig = gps->runtime.gps_orig; + if (gps_orig) { + gps_orig->flag |= GP_STROKE_RECALC_GEOMETRY; + gps_orig->tot_triangles = 0; + } + else { + gps->flag |= GP_STROKE_RECALC_GEOMETRY; + gps->tot_triangles = 0; + } +} + /* ************************************************ */ /* Brush Callbacks */ /* This section defines the callbacks used by each brush to perform their magic. @@ -313,7 +328,7 @@ static float gp_brush_influence_calc(tGP_BrushEditData *gso, const int radius, c * smooth-brush implementation to test the algorithm for stroke smoothing. */ static bool gp_brush_smooth_apply(tGP_BrushEditData *gso, bGPDstroke *gps, - float UNUSED(rot), + float UNUSED(rot_eval), int pt_index, const int radius, const int co[2]) @@ -341,7 +356,7 @@ static bool gp_brush_smooth_apply(tGP_BrushEditData *gso, BKE_gpencil_smooth_stroke_uv(gps, pt_index, inf); } - gps->flag |= GP_STROKE_RECALC_GEOMETRY; + gp_recalc_geometry(gps); return true; } @@ -352,7 +367,7 @@ static bool gp_brush_smooth_apply(tGP_BrushEditData *gso, /* Make lines thicker or thinner by the specified amounts */ static bool gp_brush_thickness_apply(tGP_BrushEditData *gso, bGPDstroke *gps, - float UNUSED(rot), + float UNUSED(rot_eval), int pt_index, const int radius, const int co[2]) @@ -396,7 +411,7 @@ static bool gp_brush_thickness_apply(tGP_BrushEditData *gso, /* Make color more or less transparent by the specified amounts */ static bool gp_brush_strength_apply(tGP_BrushEditData *gso, bGPDstroke *gps, - float UNUSED(rot), + float UNUSED(rot_eval), int pt_index, const int radius, const int co[2]) @@ -443,7 +458,7 @@ typedef struct tGPSB_Grab_StrokeData { /* array of influence weights for each of the included points */ float *weights; /* angles to calc transformation */ - float *rot; + float *rot_eval; /* capacity of the arrays */ int capacity; @@ -471,7 +486,7 @@ static void gp_brush_grab_stroke_init(tGP_BrushEditData *gso, bGPDstroke *gps) memset(data->points, 0, sizeof(int) * data->capacity); memset(data->weights, 0, sizeof(float) * data->capacity); - memset(data->rot, 0, sizeof(float) * data->capacity); + memset(data->rot_eval, 0, sizeof(float) * data->capacity); } else { /* Create new instance */ @@ -482,7 +497,7 @@ static void gp_brush_grab_stroke_init(tGP_BrushEditData *gso, bGPDstroke *gps) data->points = MEM_callocN(sizeof(int) * data->capacity, "GP Stroke Grab Indices"); data->weights = MEM_callocN(sizeof(float) * data->capacity, "GP Stroke Grab Weights"); - data->rot = MEM_callocN(sizeof(float) * data->capacity, "GP Stroke Grab X"); + data->rot_eval = MEM_callocN(sizeof(float) * data->capacity, "GP Stroke Grab Rotations"); /* hook up to the cache */ BLI_ghash_insert(gso->stroke_customdata, gps, data); @@ -492,7 +507,7 @@ static void gp_brush_grab_stroke_init(tGP_BrushEditData *gso, bGPDstroke *gps) /* store references to stroke points in the initial stage */ static bool gp_brush_grab_store_points(tGP_BrushEditData *gso, bGPDstroke *gps, - float rot, + float rot_eval, int pt_index, const int radius, const int co[2]) @@ -506,7 +521,7 @@ static bool gp_brush_grab_store_points(tGP_BrushEditData *gso, /* insert this point into the set of affected points */ data->points[data->size] = pt_index; data->weights[data->size] = inf; - data->rot[data->size] = rot; + data->rot_eval[data->size] = rot_eval; data->size++; /* done */ @@ -530,10 +545,10 @@ static void gp_brush_grab_calc_dvec(tGP_BrushEditData *gso) mval_f[0] = (float)(gso->mval[0] - gso->mval_prev[0]); mval_f[1] = (float)(gso->mval[1] - gso->mval_prev[1]); - /* apply derived data transformation */ - if (gso->rot != 0.0f) { - const float cval = cos(gso->rot); - const float sval = sin(gso->rot); + /* apply evaluated data transformation */ + if (gso->rot_eval != 0.0f) { + const float cval = cos(gso->rot_eval); + const float sval = sin(gso->rot_eval); float r[2]; r[0] = (mval_f[0] * cval) - (mval_f[1] * sval); r[1] = (mval_f[0] * sval) + (mval_f[1] * cval); @@ -564,8 +579,8 @@ static void gp_brush_grab_apply_cached(tGP_BrushEditData *gso, bGPDspoint *pt = &gps->points[data->points[i]]; float delta[3] = {0.0f}; - /* get derived transformation */ - gso->rot = data->rot[i]; + /* get evaluated transformation */ + gso->rot_eval = data->rot_eval[i]; gp_brush_grab_calc_dvec(gso); /* adjust the amount of displacement to apply */ @@ -586,7 +601,7 @@ static void gp_brush_grab_apply_cached(tGP_BrushEditData *gso, /* compute lock axis */ gpsculpt_compute_lock_axis(gso, pt, save_pt); } - gps->flag |= GP_STROKE_RECALC_GEOMETRY; + gp_recalc_geometry(gps); } /* free customdata used for handling this stroke */ @@ -597,7 +612,7 @@ static void gp_brush_grab_stroke_free(void *ptr) /* free arrays */ MEM_SAFE_FREE(data->points); MEM_SAFE_FREE(data->weights); - MEM_SAFE_FREE(data->rot); + MEM_SAFE_FREE(data->rot_eval); /* ... and this item itself, since it was also allocated */ MEM_freeN(data); @@ -608,7 +623,7 @@ static void gp_brush_grab_stroke_free(void *ptr) /* NOTE: Depends on gp_brush_grab_calc_dvec() */ static bool gp_brush_push_apply(tGP_BrushEditData *gso, bGPDstroke *gps, - float UNUSED(rot), + float UNUSED(rot_eval), int pt_index, const int radius, const int co[2]) @@ -676,7 +691,7 @@ static void gp_brush_calc_midpoint(tGP_BrushEditData *gso) /* Shrink distance between midpoint and this point... */ static bool gp_brush_pinch_apply(tGP_BrushEditData *gso, bGPDstroke *gps, - float UNUSED(rot), + float UNUSED(rot_eval), int pt_index, const int radius, const int co[2]) @@ -721,7 +736,7 @@ static bool gp_brush_pinch_apply(tGP_BrushEditData *gso, /* compute lock axis */ gpsculpt_compute_lock_axis(gso, pt, save_pt); - gps->flag |= GP_STROKE_RECALC_GEOMETRY; + gp_recalc_geometry(gps); /* done */ return true; @@ -735,7 +750,7 @@ static bool gp_brush_pinch_apply(tGP_BrushEditData *gso, static bool gp_brush_twist_apply(tGP_BrushEditData *gso, bGPDstroke *gps, - float UNUSED(rot), + float UNUSED(rot_eval), int pt_index, const int radius, const int co[2]) @@ -809,7 +824,7 @@ static bool gp_brush_twist_apply(tGP_BrushEditData *gso, } } - gps->flag |= GP_STROKE_RECALC_GEOMETRY; + gp_recalc_geometry(gps); /* done */ return true; @@ -820,7 +835,7 @@ static bool gp_brush_twist_apply(tGP_BrushEditData *gso, /* Apply some random jitter to the point */ static bool gp_brush_randomize_apply(tGP_BrushEditData *gso, bGPDstroke *gps, - float UNUSED(rot), + float UNUSED(rot_eval), int pt_index, const int radius, const int co[2]) @@ -929,7 +944,7 @@ static bool gp_brush_randomize_apply(tGP_BrushEditData *gso, CLAMP(pt->uv_rot, -M_PI_2, M_PI_2); } - gps->flag |= GP_STROKE_RECALC_GEOMETRY; + gp_recalc_geometry(gps); /* done */ return true; @@ -939,7 +954,7 @@ static bool gp_brush_randomize_apply(tGP_BrushEditData *gso, /* Change weight paint for vertex groups */ static bool gp_brush_weight_apply(tGP_BrushEditData *gso, bGPDstroke *gps, - float UNUSED(rot), + float UNUSED(rot_eval), int pt_index, const int radius, const int co[2]) @@ -1175,7 +1190,7 @@ static void gp_brush_clone_adjust(tGP_BrushEditData *gso) size_t snum; /* Compute the amount of movement to apply (overwrites dvec) */ - gso->rot = 0.0f; + gso->rot_eval = 0.0f; gp_brush_grab_calc_dvec(gso); /* For each of the stored strokes, apply the offset to each point */ @@ -1323,6 +1338,9 @@ static bool gpsculpt_brush_init(bContext *C, wmOperator *op) gso->sa = CTX_wm_area(C); gso->ar = CTX_wm_region(C); + /* save mask */ + gso->mask = ts->gpencil_selectmode_sculpt; + /* multiframe settings */ gso->is_multiframe = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gso->gpd); gso->use_multiframe_falloff = (ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_FRAME_FALLOFF) != 0; @@ -1486,35 +1504,38 @@ static void gpsculpt_brush_init_stroke(tGP_BrushEditData *gso) /* Apply ----------------------------------------------- */ -/* Get angle of the segment relative to the original segment before any transformation */ -static float gpsculpt_transform_rot_get(GP_SpaceConversion *gsc, - bGPDstroke *gps_derived, - bGPDspoint *pt_derived, - int idx_derived) +/* Get angle of the segment relative to the original segment before any transformation + * For strokes with one point only this is impossible to calculate because there isn't a + * valid reference point. + */ +static float gpsculpt_rotation_eval_get(GP_SpaceConversion *gsc, + bGPDstroke *gps_eval, + bGPDspoint *pt_eval, + int idx_eval) { - bGPDstroke *gps_orig = gps_derived->runtime.gps_orig; - bGPDspoint *pt_orig = &gps_orig->points[pt_derived->runtime.idx_orig]; - bGPDspoint *pt_derived_prev = NULL; + bGPDstroke *gps_orig = gps_eval->runtime.gps_orig; + bGPDspoint *pt_orig = &gps_orig->points[pt_eval->runtime.idx_orig]; + bGPDspoint *pt_prev_eval = NULL; bGPDspoint *pt_orig_prev = NULL; - if (idx_derived != 0) { - pt_derived_prev = &gps_derived->points[idx_derived - 1]; + if (idx_eval != 0) { + pt_prev_eval = &gps_eval->points[idx_eval - 1]; } else { - if (gps_derived->totpoints > 1) { - pt_derived_prev = &gps_derived->points[idx_derived + 1]; + if (gps_eval->totpoints > 1) { + pt_prev_eval = &gps_eval->points[idx_eval + 1]; } else { return 0.0f; } } - if (pt_derived->runtime.idx_orig != 0) { - pt_orig_prev = &gps_orig->points[pt_derived->runtime.idx_orig - 1]; + if (pt_eval->runtime.idx_orig != 0) { + pt_orig_prev = &gps_orig->points[pt_eval->runtime.idx_orig - 1]; } else { if (gps_orig->totpoints > 1) { - pt_orig_prev = &gps_orig->points[pt_derived->runtime.idx_orig + 1]; + pt_orig_prev = &gps_orig->points[pt_eval->runtime.idx_orig + 1]; } else { return 0.0f; @@ -1522,17 +1543,17 @@ static float gpsculpt_transform_rot_get(GP_SpaceConversion *gsc, } /* create 2D vectors of the stroke segments */ - float v_orig_a[2], v_orig_b[2], v_derived_a[2], v_derived_b[2]; + float v_orig_a[2], v_orig_b[2], v_eval_a[2], v_eval_b[2]; gp_point_3d_to_xy(gsc, GP_STROKE_3DSPACE, &pt_orig->x, v_orig_a); gp_point_3d_to_xy(gsc, GP_STROKE_3DSPACE, &pt_orig_prev->x, v_orig_b); sub_v2_v2(v_orig_a, v_orig_b); - gp_point_3d_to_xy(gsc, GP_STROKE_3DSPACE, &pt_derived->x, v_derived_a); - gp_point_3d_to_xy(gsc, GP_STROKE_3DSPACE, &pt_derived_prev->x, v_derived_b); - sub_v2_v2(v_derived_a, v_derived_b); + gp_point_3d_to_xy(gsc, GP_STROKE_3DSPACE, &pt_eval->x, v_eval_a); + gp_point_3d_to_xy(gsc, GP_STROKE_3DSPACE, &pt_prev_eval->x, v_eval_b); + sub_v2_v2(v_eval_a, v_eval_b); - return angle_v2v2(v_orig_a, v_derived_a); + return angle_v2v2(v_orig_a, v_eval_a); } /* Apply brush operation to points in this stroke */ @@ -1555,7 +1576,7 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso, int i; bool include_last = false; bool changed = false; - float rot = 0.0f; + float rot_eval = 0.0f; if (gps->totpoints == 1) { bGPDspoint pt_temp; pt = &gps->points[0]; @@ -1570,8 +1591,8 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso, if (len_v2v2_int(mval_i, pc1) <= radius) { /* apply operation to this point */ if (pt->runtime.pt_orig != NULL) { - rot = gpsculpt_transform_rot_get(&gso->gsc, gps, pt, 0); - changed = apply(gso, gps->runtime.gps_orig, rot, pt->runtime.idx_orig, radius, pc1); + rot_eval = gpsculpt_rotation_eval_get(&gso->gsc, gps, pt, 0); + changed = apply(gso, gps->runtime.gps_orig, rot_eval, pt->runtime.idx_orig, radius, pc1); } } } @@ -1587,7 +1608,7 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso, /* Skip if neither one is selected * (and we are only allowed to edit/consider selected points) */ - if ((gso->settings->flag & GP_SCULPT_SETT_FLAG_SELECT_MASK) && (!gso->is_weight_mode)) { + if ((GPENCIL_ANY_SCULPT_MASK(gso->mask)) && (!gso->is_weight_mode)) { if (!(pt1->flag & GP_SPOINT_SELECT) && !(pt2->flag & GP_SPOINT_SELECT)) { include_last = false; continue; @@ -1615,8 +1636,8 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso, /* To each point individually... */ pt = &gps->points[i]; if (pt->runtime.pt_orig != NULL) { - rot = gpsculpt_transform_rot_get(&gso->gsc, gps, pt, i); - ok = apply(gso, gps->runtime.gps_orig, rot, pt->runtime.idx_orig, radius, pc1); + rot_eval = gpsculpt_rotation_eval_get(&gso->gsc, gps, pt, i); + ok = apply(gso, gps->runtime.gps_orig, rot_eval, pt->runtime.idx_orig, radius, pc1); } /* Only do the second point if this is the last segment, @@ -1630,8 +1651,8 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso, if (i + 1 == gps->totpoints - 1) { pt = &gps->points[i + 1]; if (pt->runtime.pt_orig != NULL) { - rot = gpsculpt_transform_rot_get(&gso->gsc, gps, pt, i + 1); - ok |= apply(gso, gps->runtime.gps_orig, rot, pt->runtime.idx_orig, radius, pc2); + rot_eval = gpsculpt_rotation_eval_get(&gso->gsc, gps, pt, i + 1); + ok |= apply(gso, gps->runtime.gps_orig, rot_eval, pt->runtime.idx_orig, radius, pc2); include_last = false; } } @@ -1649,8 +1670,9 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso, */ pt = &gps->points[i]; if (pt->runtime.pt_orig != NULL) { - rot = gpsculpt_transform_rot_get(&gso->gsc, gps, pt, i); - changed |= apply(gso, gps->runtime.gps_orig, rot, pt->runtime.idx_orig, radius, pc1); + rot_eval = gpsculpt_rotation_eval_get(&gso->gsc, gps, pt, i); + changed |= apply( + gso, gps->runtime.gps_orig, rot_eval, pt->runtime.idx_orig, radius, pc1); include_last = false; } } @@ -1752,10 +1774,7 @@ static bool gpsculpt_brush_do_frame( break; } /* Triangulation must be calculated if changed */ - if (changed) { - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - gps->tot_triangles = 0; - } + gp_recalc_geometry(gps); } return changed; @@ -1777,7 +1796,7 @@ static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso) case GP_SCULPT_TYPE_PUSH: /* Push points */ { /* calculate amount of displacement to apply */ - gso->rot = 0.0f; + gso->rot_eval = 0.0f; gp_brush_grab_calc_dvec(gso); break; } @@ -1793,7 +1812,7 @@ static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso) case GP_SCULPT_TYPE_RANDOMIZE: /* Random jitter */ { /* compute the displacement vector for the cursor (in data space) */ - gso->rot = 0.0f; + gso->rot_eval = 0.0f; gp_brush_grab_calc_dvec(gso); break; } @@ -1808,10 +1827,10 @@ static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso) if (gpl->actframe == NULL) { continue; } - /* Get derived frames array data */ - int derived_idx = BLI_findindex(&gpd->layers, gpl); - bGPDframe *derived_gpf = &ob_eval->runtime.derived_frames[derived_idx]; - if (derived_gpf == NULL) { + /* Get evaluated frames array data */ + int idx_eval = BLI_findindex(&gpd->layers, gpl); + bGPDframe *gpf_eval = &ob_eval->runtime.gpencil_evaluated_frames[idx_eval]; + if (gpf_eval == NULL) { continue; } @@ -1845,14 +1864,14 @@ static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso) /* affect strokes in this frame */ changed |= gpsculpt_brush_do_frame( - C, gso, gpl, (gpf == gpl->actframe) ? derived_gpf : gpf, diff_mat); + C, gso, gpl, (gpf == gpl->actframe) ? gpf_eval : gpf, diff_mat); } } } else { /* Apply to active frame's strokes */ gso->mf_falloff = 1.0f; - changed |= gpsculpt_brush_do_frame(C, gso, gpl, derived_gpf, diff_mat); + changed |= gpsculpt_brush_do_frame(C, gso, gpl, gpf_eval, diff_mat); } } CTX_DATA_END; diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c index 0928913aad5..e763eec1b0d 100644 --- a/source/blender/editors/gpencil/gpencil_data.c +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -105,7 +105,7 @@ static bool gp_data_add_poll(bContext *C) /* add new datablock - wrapper around API */ static int gp_data_add_exec(bContext *C, wmOperator *op) { - PointerRNA gpd_owner = {{NULL}}; + PointerRNA gpd_owner = {NULL}; bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, &gpd_owner); bool is_annotation = ED_gpencil_data_owner_is_annotation(&gpd_owner); @@ -231,7 +231,7 @@ void GPENCIL_OT_data_unlink(wmOperatorType *ot) /* add new layer - wrapper around API */ static int gp_layer_add_exec(bContext *C, wmOperator *op) { - PointerRNA gpd_owner = {{NULL}}; + PointerRNA gpd_owner = {NULL}; bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, &gpd_owner); bool is_annotation = ED_gpencil_data_owner_is_annotation(&gpd_owner); @@ -538,10 +538,12 @@ static int gp_layer_duplicate_object_exec(bContext *C, wmOperator *op) * otherwise add the slot with the material */ Material *ma_src = give_current_material(ob_src, gps_src->mat_nr + 1); - int idx = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma_src); + if (ma_src != NULL) { + int idx = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma_src); - /* Reassign the stroke material to the right slot in destination object. */ - gps_dst->mat_nr = idx; + /* Reassign the stroke material to the right slot in destination object. */ + gps_dst->mat_nr = idx; + } /* add new stroke to frame */ BLI_addtail(&gpf_dst->strokes, gps_dst); @@ -840,6 +842,10 @@ static int gp_hide_exec(bContext *C, wmOperator *op) if (gpl != layer) { gpl->flag |= GP_LAYER_HIDE; } + else { + /* Be sure the active layer is unhidden. */ + gpl->flag &= ~GP_LAYER_HIDE; + } } } else { @@ -1419,7 +1425,7 @@ static int gp_stroke_change_color_exec(bContext *C, wmOperator *op) bGPdata *gpd = ED_gpencil_data_get_active(C); Object *ob = CTX_data_active_object(C); if (name[0] == '\0') { - ma = give_current_material(ob, ob->actcol); + ma = BKE_material_gpencil_get(ob, ob->actcol); } else { ma = (Material *)BKE_libblock_find_name(bmain, ID_MA, name); @@ -1539,9 +1545,10 @@ static int gp_stroke_lock_color_exec(bContext *C, wmOperator *UNUSED(op)) } /* unlock color */ Material *tmp_ma = give_current_material(ob, gps->mat_nr + 1); - - tmp_ma->gp_style->flag &= ~GP_STYLE_COLOR_LOCKED; - DEG_id_tag_update(&tmp_ma->id, ID_RECALC_COPY_ON_WRITE); + if (tmp_ma) { + tmp_ma->gp_style->flag &= ~GP_STYLE_COLOR_LOCKED; + DEG_id_tag_update(&tmp_ma->id, ID_RECALC_COPY_ON_WRITE); + } } } } @@ -2291,7 +2298,7 @@ int ED_gpencil_join_objects_exec(bContext *C, wmOperator *op) short *totcol = give_totcolp(ob_src); for (short i = 0; i < *totcol; i++) { - Material *tmp_ma = give_current_material(ob_src, i + 1); + Material *tmp_ma = BKE_material_gpencil_get(ob_src, i + 1); BKE_gpencil_object_material_ensure(bmain, ob_dst, tmp_ma); } @@ -2325,7 +2332,7 @@ int ED_gpencil_join_objects_exec(bContext *C, wmOperator *op) for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { /* Reassign material. Look old material and try to find in destination. */ - ma_src = give_current_material(ob_src, gps->mat_nr + 1); + ma_src = BKE_material_gpencil_get(ob_src, gps->mat_nr + 1); gps->mat_nr = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma_src); bGPDspoint *pt; @@ -2433,7 +2440,7 @@ static int gpencil_lock_layer_exec(bContext *C, wmOperator *UNUSED(op)) } for (short i = 0; i < *totcol; i++) { - ma = give_current_material(ob, i + 1); + ma = BKE_material_gpencil_get(ob, i + 1); if (ma) { gp_style = ma->gp_style; gp_style->flag |= GP_STYLE_COLOR_LOCKED; @@ -2453,7 +2460,7 @@ static int gpencil_lock_layer_exec(bContext *C, wmOperator *UNUSED(op)) continue; } - ma = give_current_material(ob, gps->mat_nr + 1); + ma = BKE_material_gpencil_get(ob, gps->mat_nr + 1); DEG_id_tag_update(&ma->id, ID_RECALC_COPY_ON_WRITE); gp_style = ma->gp_style; @@ -2496,7 +2503,7 @@ static int gpencil_color_isolate_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); Object *ob = CTX_data_active_object(C); - Material *active_ma = give_current_material(ob, ob->actcol); + Material *active_ma = BKE_material_gpencil_get(ob, ob->actcol); MaterialGPencilStyle *active_color = BKE_material_gpencil_settings_get(ob, ob->actcol); MaterialGPencilStyle *gp_style; @@ -2516,7 +2523,7 @@ static int gpencil_color_isolate_exec(bContext *C, wmOperator *op) Material *ma = NULL; short *totcol = give_totcolp(ob); for (short i = 0; i < *totcol; i++) { - ma = give_current_material(ob, i + 1); + ma = BKE_material_gpencil_get(ob, i + 1); /* Skip if this is the active one */ if ((ma == NULL) || (ma == active_ma)) { continue; @@ -2536,7 +2543,7 @@ static int gpencil_color_isolate_exec(bContext *C, wmOperator *op) if (isolate) { /* Set flags on all "other" colors */ for (short i = 0; i < *totcol; i++) { - ma = give_current_material(ob, i + 1); + ma = BKE_material_gpencil_get(ob, i + 1); if (ma == NULL) { continue; } @@ -2553,7 +2560,7 @@ static int gpencil_color_isolate_exec(bContext *C, wmOperator *op) else { /* Clear flags - Restore everything else */ for (short i = 0; i < *totcol; i++) { - ma = give_current_material(ob, i + 1); + ma = BKE_material_gpencil_get(ob, i + 1); if (ma == NULL) { continue; } @@ -2618,7 +2625,7 @@ static int gpencil_color_hide_exec(bContext *C, wmOperator *op) /* hide unselected */ MaterialGPencilStyle *color = NULL; for (short i = 0; i < *totcol; i++) { - ma = give_current_material(ob, i + 1); + ma = BKE_material_gpencil_get(ob, i + 1); if (ma) { color = ma->gp_style; if (active_color != color) { @@ -2681,7 +2688,7 @@ static int gpencil_color_reveal_exec(bContext *C, wmOperator *UNUSED(op)) MaterialGPencilStyle *gp_style = NULL; for (short i = 0; i < *totcol; i++) { - ma = give_current_material(ob, i + 1); + ma = BKE_material_gpencil_get(ob, i + 1); if (ma) { gp_style = ma->gp_style; gp_style->flag &= ~GP_STYLE_COLOR_HIDE; @@ -2734,7 +2741,7 @@ static int gpencil_color_lock_all_exec(bContext *C, wmOperator *UNUSED(op)) MaterialGPencilStyle *gp_style = NULL; for (short i = 0; i < *totcol; i++) { - ma = give_current_material(ob, i + 1); + ma = BKE_material_gpencil_get(ob, i + 1); if (ma) { gp_style = ma->gp_style; gp_style->flag |= GP_STYLE_COLOR_LOCKED; @@ -2787,7 +2794,7 @@ static int gpencil_color_unlock_all_exec(bContext *C, wmOperator *UNUSED(op)) MaterialGPencilStyle *gp_style = NULL; for (short i = 0; i < *totcol; i++) { - ma = give_current_material(ob, i + 1); + ma = BKE_material_gpencil_get(ob, i + 1); if (ma) { gp_style = ma->gp_style; gp_style->flag &= ~GP_STYLE_COLOR_LOCKED; diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index cc229fcb383..12b3792e36e 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -222,6 +222,17 @@ void GPENCIL_OT_editmode_toggle(wmOperatorType *ot) } /* set select mode */ +static bool gpencil_selectmode_toggle_poll(bContext *C) +{ + /* edit only supported with grease pencil objects */ + Object *ob = CTX_data_active_object(C); + if ((ob == NULL) || (ob->type != OB_GPENCIL) || (ob->mode != OB_MODE_EDIT_GPENCIL)) { + return false; + } + + return ED_operator_view3d_active(C); +} + static int gpencil_selectmode_toggle_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); @@ -229,7 +240,7 @@ static int gpencil_selectmode_toggle_exec(bContext *C, wmOperator *op) const int mode = RNA_int_get(op->ptr, "mode"); /* Just set mode */ - ts->gpencil_selectmode = mode; + ts->gpencil_selectmode_edit = mode; WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, NULL); DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE); @@ -248,7 +259,7 @@ void GPENCIL_OT_selectmode_toggle(wmOperatorType *ot) /* callbacks */ ot->exec = gpencil_selectmode_toggle_exec; - ot->poll = gp_strokes_edit3d_poll; + ot->poll = gpencil_selectmode_toggle_poll; /* flags */ ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; @@ -1180,10 +1191,15 @@ static int gp_strokes_copy_exec(bContext *C, wmOperator *op) GHash *ma_to_name = gp_strokes_copypastebuf_colors_material_to_name_create(bmain); for (bGPDstroke *gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { if (ED_gpencil_stroke_can_use(C, gps)) { + Material *ma = give_current_material(ob, gps->mat_nr + 1); + /* Avoid default material. */ + if (ma == NULL) { + continue; + } + char **ma_name_val; if (!BLI_ghash_ensure_p( gp_strokes_copypastebuf_colors, &gps->mat_nr, (void ***)&ma_name_val)) { - Material *ma = give_current_material(ob, gps->mat_nr + 1); char *ma_name = BLI_ghash_lookup(ma_to_name, ma); *ma_name_val = MEM_dupallocN(ma_name); } @@ -1230,8 +1246,8 @@ static bool gp_strokes_paste_poll(bContext *C) } typedef enum eGP_PasteMode { - GP_COPY_ONLY = -1, - GP_COPY_MERGE = 1, + GP_COPY_BY_LAYER = -1, + GP_COPY_TO_ACTIVE = 1, } eGP_PasteMode; static int gp_strokes_paste_exec(bContext *C, wmOperator *op) @@ -1264,7 +1280,7 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) /* no active layer - let's just create one */ gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true); } - else if ((gpencil_layer_is_editable(gpl) == false) && (type == GP_COPY_MERGE)) { + else if ((gpencil_layer_is_editable(gpl) == false) && (type == GP_COPY_TO_ACTIVE)) { BKE_report( op->reports, RPT_ERROR, "Can not paste strokes when active layer is hidden or locked"); return OPERATOR_CANCELLED; @@ -1317,7 +1333,7 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) for (bGPDstroke *gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { if (ED_gpencil_stroke_can_use(C, gps)) { /* Need to verify if layer exists */ - if (type != GP_COPY_MERGE) { + if (type != GP_COPY_TO_ACTIVE) { gpl = BLI_findstring(&gpd->layers, gps->runtime.tmp_layerinfo, offsetof(bGPDlayer, info)); if (gpl == NULL) { /* no layer - use active (only if layer deleted before paste) */ @@ -1350,7 +1366,7 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) /* Remap material */ Material *ma = BLI_ghash_lookup(new_colors, POINTER_FROM_INT(new_stroke->mat_nr)); new_stroke->mat_nr = BKE_gpencil_object_material_get_index(ob, ma); - BLI_assert(new_stroke->mat_nr >= 0); /* have to add the material first */ + CLAMP_MIN(new_stroke->mat_nr, 0); } } } @@ -1368,15 +1384,15 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) void GPENCIL_OT_paste(wmOperatorType *ot) { static const EnumPropertyItem copy_type[] = { - {GP_COPY_ONLY, "COPY", 0, "Copy", ""}, - {GP_COPY_MERGE, "MERGE", 0, "Merge", ""}, + {GP_COPY_TO_ACTIVE, "ACTIVE", 0, "Paste to Active", ""}, + {GP_COPY_BY_LAYER, "LAYER", 0, "Paste by Layer", ""}, {0, NULL, 0, NULL, NULL}, }; /* identifiers */ ot->name = "Paste Strokes"; ot->idname = "GPENCIL_OT_paste"; - ot->description = "Paste previously copied strokes or copy and merge in active layer"; + ot->description = "Paste previously copied strokes to active layer or to original layer"; /* callbacks */ ot->exec = gp_strokes_paste_exec; @@ -1386,33 +1402,18 @@ void GPENCIL_OT_paste(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - ot->prop = RNA_def_enum(ot->srna, "type", copy_type, 0, "Type", ""); + ot->prop = RNA_def_enum(ot->srna, "type", copy_type, GP_COPY_TO_ACTIVE, "Type", ""); } /* ******************* Move To Layer ****************************** */ -static int gp_move_to_layer_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt)) -{ - uiPopupMenu *pup; - uiLayout *layout; - - /* call the menu, which will call this operator again, hence the canceled */ - pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE); - layout = UI_popup_menu_layout(pup); - uiItemsEnumO(layout, "GPENCIL_OT_move_to_layer", "layer"); - UI_popup_menu_end(C, pup); - - return OPERATOR_INTERFACE; -} - -// FIXME: allow moving partial strokes static int gp_move_to_layer_exec(bContext *C, wmOperator *op) { bGPdata *gpd = CTX_data_gpencil_data(C); Scene *scene = CTX_data_scene(C); bGPDlayer *target_layer = NULL; ListBase strokes = {NULL, NULL}; - int layer_num = RNA_enum_get(op->ptr, "layer"); + int layer_num = RNA_int_get(op->ptr, "layer"); const bool use_autolock = (bool)(gpd->flag & GP_DATA_AUTOLOCK_LAYERS); if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) { @@ -1425,23 +1426,16 @@ static int gp_move_to_layer_exec(bContext *C, wmOperator *op) gpd->flag &= ~GP_DATA_AUTOLOCK_LAYERS; } - /* Get layer or create new one */ - if (layer_num == -1) { - /* Create layer */ - target_layer = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true); - } - else { - /* Try to get layer */ - target_layer = BLI_findlink(&gpd->layers, layer_num); + /* Try to get layer */ + target_layer = BLI_findlink(&gpd->layers, layer_num); - if (target_layer == NULL) { - /* back autolock status */ - if (use_autolock) { - gpd->flag |= GP_DATA_AUTOLOCK_LAYERS; - } - BKE_reportf(op->reports, RPT_ERROR, "There is no layer number %d", layer_num); - return OPERATOR_CANCELLED; + if (target_layer == NULL) { + /* back autolock status */ + if (use_autolock) { + gpd->flag |= GP_DATA_AUTOLOCK_LAYERS; } + BKE_reportf(op->reports, RPT_ERROR, "There is no layer number %d", layer_num); + return OPERATOR_CANCELLED; } /* Extract all strokes to move to this layer @@ -1509,16 +1503,14 @@ void GPENCIL_OT_move_to_layer(wmOperatorType *ot) "Move selected strokes to another layer"; // XXX: allow moving individual points too? /* callbacks */ - ot->invoke = gp_move_to_layer_invoke; ot->exec = gp_move_to_layer_exec; ot->poll = gp_stroke_edit_poll; // XXX? /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - /* gp layer to use (dynamic enum) */ - ot->prop = RNA_def_enum(ot->srna, "layer", DummyRNA_DEFAULT_items, 0, "Grease Pencil Layer", ""); - RNA_def_enum_funcs(ot->prop, ED_gpencil_layers_with_new_enum_itemf); + /* GPencil layer to use. */ + ot->prop = RNA_def_int(ot->srna, "layer", 0, 0, INT_MAX, "Grease Pencil Layer", "", 0, INT_MAX); } /* ********************* Add Blank Frame *************************** */ @@ -3333,7 +3325,7 @@ typedef enum eGP_ReprojectModes { GP_REPROJECT_FRONT = 0, GP_REPROJECT_SIDE, GP_REPROJECT_TOP, - /* On same plane, parallel to viewplane */ + /* On same plane, parallel to view-plane. */ GP_REPROJECT_VIEW, /* Reprojected on to the scene geometry */ GP_REPROJECT_SURFACE, @@ -3344,11 +3336,14 @@ static int gp_strokes_reproject_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); Scene *scene = CTX_data_scene(C); + Main *bmain = CTX_data_main(C); ToolSettings *ts = CTX_data_tool_settings(C); Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Object *ob = CTX_data_active_object(C); ARegion *ar = CTX_wm_region(C); RegionView3D *rv3d = ar->regiondata; + SnapObjectContext *sctx = NULL; + int oldframe = (int)DEG_get_ctime(depsgraph); GP_SpaceConversion gsc = {NULL}; eGP_ReprojectModes mode = RNA_enum_get(op->ptr, "type"); @@ -3358,28 +3353,33 @@ static int gp_strokes_reproject_exec(bContext *C, wmOperator *op) /* init space conversion stuff */ gp_point_conversion_init(C, &gsc); - /* init autodist for geometry projection */ - if (mode == GP_REPROJECT_SURFACE) { - view3d_region_operator_needs_opengl(CTX_wm_window(C), gsc.ar); - ED_view3d_autodist_init(depsgraph, gsc.ar, CTX_wm_view3d(C), 0); - } - - // TODO: For deforming geometry workflow, create new frames? + int cfra_prv = INT_MIN; + /* init snap context for geometry projection */ + sctx = ED_transform_snap_object_context_create_view3d( + bmain, scene, depsgraph, 0, ar, CTX_wm_view3d(C)); /* Go through each editable + selected stroke, adjusting each of its points one by one... */ GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) { if (gps->flag & GP_STROKE_SELECT) { + + /* update frame to get the new location of objects */ + if ((mode == GP_REPROJECT_SURFACE) && (cfra_prv != gpf_->framenum)) { + cfra_prv = gpf_->framenum; + CFRA = gpf_->framenum; + BKE_scene_graph_update_for_newframe(depsgraph, bmain); + } + bGPDspoint *pt; int i; /* Adjust each point */ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { float xy[2]; - /* 3D to Screenspace */ - /* Note: We can't use gp_point_to_xy() here because that uses ints for the screenspace - * coordinates, resulting in lost precision, which in turn causes stairstepping - * artifacts in the final points. - */ + /* 3D to Screen-space */ + /* Note: We can't use gp_point_to_xy() here because that uses ints for the screen-space + * coordinates, resulting in lost precision, which in turn causes stair-stepping + * artifacts in the final points. */ + bGPDspoint pt2; gp_point_to_parent_space(pt, gpstroke_iter.diff_mat, &pt2); gp_point_to_xy_fl(&gsc, gps, &pt2, &xy[0], &xy[1]); @@ -3428,26 +3428,34 @@ static int gp_strokes_reproject_exec(bContext *C, wmOperator *op) /* apply parent again */ gp_apply_parent_point(depsgraph, ob, gpd, gpl, pt); } - /* Project screenspace back to 3D space (from current perspective) - * so that all points have been treated the same way - */ + /* Project screen-space back to 3D space (from current perspective) + * so that all points have been treated the same way. */ else if (mode == GP_REPROJECT_VIEW) { - /* Planar - All on same plane parallel to the viewplane */ + /* Planar - All on same plane parallel to the view-plane. */ gp_point_xy_to_3d(&gsc, scene, xy, &pt->x); } else { /* Geometry - Snap to surfaces of visible geometry */ - /* XXX: There will be precision loss (possible stairstep artifacts) - * from this conversion to satisfy the API's */ - const int screen_co[2] = {(int)xy[0], (int)xy[1]}; - - int depth_margin = 0; // XXX: 4 for strokes, 0 for normal - float depth; - - /* XXX: The proper procedure computes the depths into an array, - * to have smooth transitions when all else fails... */ - if (ED_view3d_autodist_depth(gsc.ar, screen_co, depth_margin, &depth)) { - ED_view3d_autodist_simple(gsc.ar, screen_co, &pt->x, 0, &depth); + float ray_start[3]; + float ray_normal[3]; + /* magic value for initial depth copied from the default + * value of Python's Scene.ray_cast function + */ + float depth = 1.70141e+38f; + float location[3] = {0.0f, 0.0f, 0.0f}; + float normal[3] = {0.0f, 0.0f, 0.0f}; + + ED_view3d_win_to_ray(ar, xy, &ray_start[0], &ray_normal[0]); + if (ED_transform_snap_object_project_ray(sctx, + &(const struct SnapObjectParams){ + .snap_select = SNAP_ALL, + }, + &ray_start[0], + &ray_normal[0], + &depth, + &location[0], + &normal[0])) { + copy_v3_v3(&pt->x, location); } else { /* Default to planar */ @@ -3464,6 +3472,15 @@ static int gp_strokes_reproject_exec(bContext *C, wmOperator *op) } GP_EDITABLE_STROKES_END(gpstroke_iter); + /* return frame state and DB to original state */ + CFRA = oldframe; + BKE_scene_graph_update_for_newframe(depsgraph, bmain); + + if (sctx != NULL) { + ED_transform_snap_object_context_destroy(sctx); + } + + /* update changed data */ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -3549,7 +3566,7 @@ static void gp_smooth_stroke(bContext *C, wmOperator *op) } if (smooth_thickness) { /* thickness need to repeat process several times */ - for (int r2 = 0; r2 < r * 10; r2++) { + for (int r2 = 0; r2 < r * 20; r2++) { BKE_gpencil_smooth_stroke_thickness(gps, i, factor); } } @@ -4055,7 +4072,7 @@ static int gp_stroke_separate_exec(bContext *C, wmOperator *op) /* add duplicate materials */ /* XXX same material can be in multiple slots. */ - ma = give_current_material(ob, gps->mat_nr + 1); + ma = BKE_material_gpencil_get(ob, gps->mat_nr + 1); idx = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma); @@ -4128,7 +4145,7 @@ static int gp_stroke_separate_exec(bContext *C, wmOperator *op) if (ED_gpencil_stroke_can_use(C, gps) == false) { continue; } - ma = give_current_material(ob, gps->mat_nr + 1); + ma = BKE_material_gpencil_get(ob, gps->mat_nr + 1); gps->mat_nr = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma); } } @@ -4305,7 +4322,7 @@ void GPENCIL_OT_stroke_smooth(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - prop = RNA_def_int(ot->srna, "repeat", 1, 1, 10, "Repeat", "", 1, 5); + prop = RNA_def_int(ot->srna, "repeat", 1, 1, 50, "Repeat", "", 1, 20); RNA_def_property_flag(prop, PROP_SKIP_SAVE); RNA_def_float(ot->srna, "factor", 0.5f, 0.0f, 2.0f, "Factor", "", 0.0f, 2.0f); @@ -4441,7 +4458,7 @@ static int gpencil_cutter_lasso_select(bContext *C, if ((pt->flag & GP_SPOINT_SELECT) || (pt->flag & GP_SPOINT_TAG)) { continue; } - /* convert point coords to screenspace */ + /* convert point coords to screen-space */ const bool is_inside = is_inside_fn(gps, pt, &gsc, gpstroke_iter.diff_mat, user_data); if (is_inside) { tot_inside++; diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index 5637e755198..372973e1040 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -48,6 +48,7 @@ #include "BKE_context.h" #include "BKE_screen.h" #include "BKE_paint.h" +#include "BKE_report.h" #include "ED_gpencil.h" #include "ED_screen.h" @@ -1038,7 +1039,15 @@ static void gpencil_stroke_from_buffer(tGPDfill *tgpf) gps->flag |= GP_STROKE_CYCLIC; gps->flag |= GP_STROKE_3DSPACE; - gps->mat_nr = BKE_gpencil_object_material_ensure(tgpf->bmain, tgpf->ob, tgpf->mat); + gps->mat_nr = BKE_gpencil_object_material_get_index_from_brush(tgpf->ob, brush); + if (gps->mat_nr < 0) { + if (tgpf->ob->actcol - 1 < 0) { + gps->mat_nr = 0; + } + else { + gps->mat_nr = tgpf->ob->actcol - 1; + } + } /* allocate memory for storage points */ gps->totpoints = tgpf->sbuffer_used; @@ -1346,8 +1355,28 @@ static int gpencil_fill_init(bContext *C, wmOperator *op) /* start of interactive part of operator */ static int gpencil_fill_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { + Object *ob = CTX_data_active_object(C); + ToolSettings *ts = CTX_data_tool_settings(C); + Brush *brush = BKE_paint_brush(&ts->gp_paint->paint); tGPDfill *tgpf = NULL; + /* Fill tool needs a material (cannot use default material) */ + bool valid = true; + if ((brush) && (brush->gpencil_settings->flag & GP_BRUSH_MATERIAL_PINNED)) { + if (brush->gpencil_settings->material == NULL) { + valid = false; + } + } + else { + if (give_current_material(ob, ob->actcol) == NULL) { + valid = false; + } + } + if (!valid) { + BKE_report(op->reports, RPT_ERROR, "Fill tool needs active material."); + return OPERATOR_CANCELLED; + } + /* try to initialize context data needed */ if (!gpencil_fill_init(C, op)) { gpencil_fill_exit(C, op); diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index 7f36eb50802..f7fc7e34460 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -637,8 +637,12 @@ struct GP_EditableStrokes_Iter { } \ (void)0 +#define GPENCIL_ANY_SCULPT_MASK(flag) \ + ((flag & (GP_SCULPT_MASK_SELECTMODE_POINT | GP_SCULPT_MASK_SELECTMODE_STROKE | \ + GP_SCULPT_MASK_SELECTMODE_SEGMENT))) + /** - * Iterate over all editable strokes using derived data in the current context, + * Iterate over all editable strokes using evaluated data in the current context, * stopping on each usable layer + stroke pair (i.e. gpl and gps) * to perform some operations on the stroke. * @@ -647,15 +651,15 @@ struct GP_EditableStrokes_Iter { * \param gps: The identifier to use for current stroke being processed. * Choose a suitable value to avoid name clashes. */ -#define GP_DERIVED_STROKES_BEGIN(gpstroke_iter, C, gpl, gps) \ +#define GP_EVALUATED_STROKES_BEGIN(gpstroke_iter, C, gpl, gps) \ { \ struct GP_EditableStrokes_Iter gpstroke_iter = {{{0}}}; \ - Depsgraph *depsgraph_ = CTX_data_depsgraph_pointer(C); \ + Depsgraph *depsgraph_ = CTX_data_ensure_evaluated_depsgraph(C); \ Object *obact_ = CTX_data_active_object(C); \ Object *obeval_ = DEG_get_evaluated_object(depsgraph_, obact_); \ bGPdata *gpd_ = CTX_data_gpencil_data(C); \ const bool is_multiedit_ = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd_); \ - int derived_idx = 0; \ + int idx_eval = 0; \ for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { \ if (gpencil_layer_is_editable(gpl)) { \ bGPDframe *init_gpf_ = gpl->actframe; \ @@ -665,10 +669,11 @@ struct GP_EditableStrokes_Iter { for (bGPDframe *gpf_ = init_gpf_; gpf_; gpf_ = gpf_->next) { \ if ((gpf_ == gpl->actframe) || ((gpf_->flag & GP_FRAME_SELECT) && is_multiedit_)) { \ ED_gpencil_parent_location(depsgraph_, obact_, gpd_, gpl, gpstroke_iter.diff_mat); \ - /* get derived frame with modifiers applied */ \ - bGPDframe *derived_gpf_ = &obeval_->runtime.derived_frames[derived_idx]; \ + invert_m4_m4(gpstroke_iter.inverse_diff_mat, gpstroke_iter.diff_mat); \ + /* get evaluated frame with modifiers applied */ \ + bGPDframe *gpf_eval_ = &obeval_->runtime.gpencil_evaluated_frames[idx_eval]; \ /* loop over strokes */ \ - for (bGPDstroke *gps = derived_gpf_->strokes.first; gps; gps = gps->next) { \ + for (bGPDstroke *gps = gpf_eval_->strokes.first; gps; gps = gps->next) { \ /* skip strokes that are invalid for current view */ \ if (ED_gpencil_stroke_can_use(C, gps) == false) \ continue; \ @@ -677,7 +682,7 @@ struct GP_EditableStrokes_Iter { continue; \ /* ... Do Stuff With Strokes ... */ -#define GP_DERIVED_STROKES_END(gpstroke_iter) \ +#define GP_EVALUATED_STROKES_END(gpstroke_iter) \ } \ } \ if (!is_multiedit_) { \ @@ -685,7 +690,7 @@ struct GP_EditableStrokes_Iter { } \ } \ } \ - derived_idx++; \ + idx_eval++; \ } \ } \ (void)0 diff --git a/source/blender/editors/gpencil/gpencil_merge.c b/source/blender/editors/gpencil/gpencil_merge.c index cb11bb4cd63..930911ffac5 100644 --- a/source/blender/editors/gpencil/gpencil_merge.c +++ b/source/blender/editors/gpencil/gpencil_merge.c @@ -449,7 +449,7 @@ static bool gp_strokes_merge_poll(bContext *C) /* check material */ Material *ma = NULL; - ma = give_current_material(ob, ob->actcol); + ma = BKE_material_gpencil_get(ob, ob->actcol); if ((ma == NULL) || (ma->gp_style == NULL)) { return false; } diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index a5425d64c2e..3217c94eebd 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -423,7 +423,7 @@ static void gp_stroke_convertcoords(tGPsdata *p, const float mval[2], float out[ /* add small offset to keep stroke over the surface */ if ((depth) && (gpd->zdepth_offset > 0.0f) && (*p->align_flag & GP_PROJECT_DEPTH_VIEW)) { - *depth *= (1.0f - gpd->zdepth_offset); + *depth *= (1.0f - (gpd->zdepth_offset / 1000.0f)); } int mval_i[2]; @@ -563,7 +563,7 @@ static void gp_brush_angle(bGPdata *gpd, Brush *brush, tGPspoint *pt, const floa static void gp_smooth_buffer(tGPsdata *p, float inf, int idx) { bGPdata *gpd = p->gpd; - short num_points = gpd->runtime.sbuffer_used; + const short num_points = gpd->runtime.sbuffer_used; /* Do nothing if not enough points to smooth out */ if ((num_points < 3) || (idx < 3) || (inf == 0.0f)) { @@ -571,10 +571,7 @@ static void gp_smooth_buffer(tGPsdata *p, float inf, int idx) } tGPspoint *points = (tGPspoint *)gpd->runtime.sbuffer; - float steps = 4.0f; - if (idx < 4) { - steps--; - } + const float steps = (idx < 4) ? 3.0f : 4.0f; tGPspoint *pta = idx >= 4 ? &points[idx - 4] : NULL; tGPspoint *ptb = idx >= 3 ? &points[idx - 3] : NULL; @@ -583,29 +580,36 @@ static void gp_smooth_buffer(tGPsdata *p, float inf, int idx) float sco[2] = {0.0f}; float a[2], b[2], c[2], d[2]; + float pressure = 0.0f; const float average_fac = 1.0f / steps; /* Compute smoothed coordinate by taking the ones nearby */ if (pta) { copy_v2_v2(a, &pta->x); madd_v2_v2fl(sco, a, average_fac); + pressure += pta->pressure * average_fac; } if (ptb) { copy_v2_v2(b, &ptb->x); madd_v2_v2fl(sco, b, average_fac); + pressure += ptb->pressure * average_fac; } if (ptc) { copy_v2_v2(c, &ptc->x); madd_v2_v2fl(sco, c, average_fac); + pressure += ptc->pressure * average_fac; } if (ptd) { copy_v2_v2(d, &ptd->x); madd_v2_v2fl(sco, d, average_fac); + pressure += ptd->pressure * average_fac; } - /* Based on influence factor, blend between original and optimal smoothed coordinate */ + /* Based on influence factor, blend between original and optimal smoothed coordinate. */ interp_v2_v2v2(c, c, sco, inf); copy_v2_v2(&ptc->x, c); + /* Interpolate pressure. */ + ptc->pressure = interpf(ptc->pressure, pressure, inf); } /* add current stroke-point to buffer (returns whether point was successfully added) */ @@ -1240,6 +1244,14 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) /* Save material index */ gps->mat_nr = BKE_gpencil_object_material_get_index_from_brush(p->ob, p->brush); + if (gps->mat_nr < 0) { + if (p->ob->actcol - 1 < 0) { + gps->mat_nr = 0; + } + else { + gps->mat_nr = p->ob->actcol - 1; + } + } /* calculate UVs along the stroke */ ED_gpencil_calc_stroke_uv(obact, gps); @@ -2128,15 +2140,6 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps /* Ensure this gets set... */ p->gpf = p->gpl->actframe; - /* Restrict eraser to only affecting selected strokes, if the "selection mask" is on - * (though this is only available in editmode) - */ - if (p->gpd->flag & GP_DATA_STROKE_EDITMODE) { - if (ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_SELECT_MASK) { - p->flags |= GP_PAINTFLAG_SELECTMASK; - } - } - if (has_layer_to_erase == false) { p->status = GP_STATUS_ERROR; return; diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c index 25913fe821a..8d4c75d2e8c 100644 --- a/source/blender/editors/gpencil/gpencil_primitive.c +++ b/source/blender/editors/gpencil/gpencil_primitive.c @@ -330,7 +330,15 @@ static void gp_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi) gps->flag |= GP_STROKE_3DSPACE; - gps->mat_nr = BKE_gpencil_object_material_get_index(tgpi->ob, tgpi->mat); + gps->mat_nr = BKE_gpencil_object_material_get_index_from_brush(tgpi->ob, tgpi->brush); + if (gps->mat_nr < 0) { + if (tgpi->ob->actcol - 1 < 0) { + gps->mat_nr = 0; + } + else { + gps->mat_nr = tgpi->ob->actcol - 1; + } + } /* allocate memory for storage points, but keep empty */ gps->totpoints = 0; @@ -959,7 +967,7 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi) /* add small offset to keep stroke over the surface */ if ((depth_arr) && (gpd->zdepth_offset > 0.0f)) { - depth_arr[i] *= (1.0f - gpd->zdepth_offset); + depth_arr[i] *= (1.0f - (gpd->zdepth_offset / 1000.0f)); } /* convert screen-coordinates to 3D coordinates */ diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c index 01a2ac757d6..f4484624d5a 100644 --- a/source/blender/editors/gpencil/gpencil_select.c +++ b/source/blender/editors/gpencil/gpencil_select.c @@ -67,13 +67,30 @@ /** \name Shared Utilities * \{ */ +/* Convert sculpt mask mode to Select mode */ +static int gpencil_select_mode_from_sculpt(eGP_Sculpt_SelectMaskFlag mode) +{ + if (mode & GP_SCULPT_MASK_SELECTMODE_POINT) { + return GP_SELECTMODE_POINT; + } + else if (mode & GP_SCULPT_MASK_SELECTMODE_STROKE) { + return GP_SELECTMODE_STROKE; + } + else if (mode & GP_SCULPT_MASK_SELECTMODE_SEGMENT) { + return GP_SELECTMODE_SEGMENT; + } + else { + return GP_SELECTMODE_POINT; + } +} + static bool gpencil_select_poll(bContext *C) { bGPdata *gpd = ED_gpencil_data_get_active(C); if (GPENCIL_SCULPT_MODE(gpd)) { ToolSettings *ts = CTX_data_tool_settings(C); - if ((ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_SELECT_MASK) == 0) { + if (!(GPENCIL_ANY_SCULPT_MASK(ts->gpencil_selectmode_sculpt))) { return false; } } @@ -94,6 +111,19 @@ static bool gpencil_select_poll(bContext *C) /* -------------------------------------------------------------------- */ /** \name Select All Operator * \{ */ +static bool gpencil_select_all_poll(bContext *C) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + + /* we just need some visible strokes, and to be in editmode or other modes only to catch event */ + if (GPENCIL_ANY_MODE(gpd)) { + if (gpd->layers.first) { + return true; + } + } + + return false; +} static int gpencil_select_all_exec(bContext *C, wmOperator *op) { @@ -110,6 +140,14 @@ static int gpencil_select_all_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + /* For sculpt mode, if mask is disable, only allows deselect */ + if (GPENCIL_SCULPT_MODE(gpd)) { + ToolSettings *ts = CTX_data_tool_settings(C); + if ((!(GPENCIL_ANY_SCULPT_MASK(ts->gpencil_selectmode_sculpt))) && (action != SEL_DESELECT)) { + return OPERATOR_CANCELLED; + } + } + ED_gpencil_select_toggle_all(C, action); /* updates */ @@ -132,7 +170,7 @@ void GPENCIL_OT_select_all(wmOperatorType *ot) /* callbacks */ ot->exec = gpencil_select_all_exec; - ot->poll = gpencil_select_poll; + ot->poll = gpencil_select_all_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -928,7 +966,11 @@ static int gpencil_circle_select_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); ToolSettings *ts = CTX_data_tool_settings(C); - const int selectmode = ts->gpencil_selectmode; + Object *ob = CTX_data_active_object(C); + + const int selectmode = (ob && ob->mode == OB_MODE_SCULPT_GPENCIL) ? + gpencil_select_mode_from_sculpt(ts->gpencil_selectmode_sculpt) : + ts->gpencil_selectmode_edit; const float scale = ts->gp_sculpt.isect_threshold; /* if not edit/sculpt mode, the event is catched but not processed */ @@ -972,12 +1014,12 @@ static int gpencil_circle_select_exec(bContext *C, wmOperator *op) rect.ymax = my + radius; /* find visible strokes, and select if hit */ - GP_DERIVED_STROKES_BEGIN(gpstroke_iter, C, gpl, gps) + GP_EVALUATED_STROKES_BEGIN(gpstroke_iter, C, gpl, gps) { changed |= gp_stroke_do_circle_sel( gpl, gps, &gsc, mx, my, radius, select, &rect, gpstroke_iter.diff_mat, selectmode, scale); } - GP_DERIVED_STROKES_END(gpstroke_iter); + GP_EVALUATED_STROKES_END(gpstroke_iter); /* updates */ if (changed) { @@ -1035,12 +1077,18 @@ static int gpencil_generic_select_exec(bContext *C, GPencilTestFn is_inside_fn, void *user_data) { + Object *ob = CTX_data_active_object(C); bGPdata *gpd = ED_gpencil_data_get_active(C); ToolSettings *ts = CTX_data_tool_settings(C); ScrArea *sa = CTX_wm_area(C); - const bool strokemode = ((ts->gpencil_selectmode == GP_SELECTMODE_STROKE) && + + const short selectmode = (ob && ob->mode == OB_MODE_SCULPT_GPENCIL) ? + gpencil_select_mode_from_sculpt(ts->gpencil_selectmode_sculpt) : + ts->gpencil_selectmode_edit; + + const bool strokemode = ((selectmode == GP_SELECTMODE_STROKE) && ((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0)); - const bool segmentmode = ((ts->gpencil_selectmode == GP_SELECTMODE_SEGMENT) && + const bool segmentmode = ((selectmode == GP_SELECTMODE_SEGMENT) && ((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0)); const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode"); const float scale = ts->gp_sculpt.isect_threshold; @@ -1075,7 +1123,7 @@ static int gpencil_generic_select_exec(bContext *C, } /* select/deselect points */ - GP_DERIVED_STROKES_BEGIN(gpstroke_iter, C, gpl, gps) + GP_EVALUATED_STROKES_BEGIN(gpstroke_iter, C, gpl, gps) { bGPDspoint *pt; @@ -1088,7 +1136,6 @@ static int gpencil_generic_select_exec(bContext *C, /* convert point coords to screenspace */ const bool is_inside = is_inside_fn(gps, pt, &gsc, gpstroke_iter.diff_mat, user_data); - if (strokemode == false) { const bool is_select = (pt->runtime.pt_orig->flag & GP_SPOINT_SELECT) != 0; const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); @@ -1143,7 +1190,7 @@ static int gpencil_generic_select_exec(bContext *C, /* Ensure that stroke selection is in sync with its points */ BKE_gpencil_stroke_sync_selection(gps->runtime.gps_orig); } - GP_DERIVED_STROKES_END(gpstroke_iter); + GP_EVALUATED_STROKES_END(gpstroke_iter); /* if paint mode,delete selected points */ if (gpd->flag & GP_DATA_STROKE_PAINTMODE) { @@ -1318,6 +1365,7 @@ static void deselect_all_selected(bContext *C) static int gpencil_select_exec(bContext *C, wmOperator *op) { ScrArea *sa = CTX_wm_area(C); + Object *ob = CTX_data_active_object(C); bGPdata *gpd = ED_gpencil_data_get_active(C); ToolSettings *ts = CTX_data_tool_settings(C); const float scale = ts->gp_sculpt.isect_threshold; @@ -1348,8 +1396,12 @@ static int gpencil_select_exec(bContext *C, wmOperator *op) } /* if select mode is stroke, use whole stroke */ - if (ts->gpencil_selectmode == GP_SELECTMODE_STROKE) { - whole = true; + if ((ob) && (ob->mode == OB_MODE_SCULPT_GPENCIL)) { + whole = (bool)(gpencil_select_mode_from_sculpt(ts->gpencil_selectmode_sculpt) == + GP_SELECTMODE_STROKE); + } + else { + whole = (bool)(ts->gpencil_selectmode_edit == GP_SELECTMODE_STROKE); } /* init space conversion stuff */ @@ -1360,7 +1412,7 @@ static int gpencil_select_exec(bContext *C, wmOperator *op) /* First Pass: Find stroke point which gets hit */ /* XXX: maybe we should go from the top of the stack down instead... */ - GP_DERIVED_STROKES_BEGIN(gpstroke_iter, C, gpl, gps) + GP_EVALUATED_STROKES_BEGIN(gpstroke_iter, C, gpl, gps) { bGPDspoint *pt; int i; @@ -1393,7 +1445,7 @@ static int gpencil_select_exec(bContext *C, wmOperator *op) } } } - GP_DERIVED_STROKES_END(gpstroke_iter); + GP_EVALUATED_STROKES_END(gpstroke_iter); /* Abort if nothing hit... */ if (ELEM(NULL, hit_stroke, hit_point)) { @@ -1454,7 +1506,11 @@ static int gpencil_select_exec(bContext *C, wmOperator *op) hit_stroke->flag |= GP_STROKE_SELECT; /* expand selection to segment */ - if (ts->gpencil_selectmode == GP_SELECTMODE_SEGMENT) { + const short selectmode = (ob && ob->mode == OB_MODE_SCULPT_GPENCIL) ? + gpencil_select_mode_from_sculpt(ts->gpencil_selectmode_sculpt) : + ts->gpencil_selectmode_edit; + + if (selectmode == GP_SELECTMODE_SEGMENT) { float r_hita[3], r_hitb[3]; bool hit_select = (bool)(hit_point->flag & GP_SPOINT_SELECT); ED_gpencil_select_stroke_segment( diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index 2a0f16a4bbf..d7492b5be4d 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -401,17 +401,7 @@ const EnumPropertyItem *ED_gpencil_layers_with_new_enum_itemf(bContext *C, /* Create new layer */ /* TODO: have some way of specifying that we don't want this? */ - { - /* "New Layer" entry */ - item_tmp.identifier = "__CREATE__"; - item_tmp.name = "New Layer"; - item_tmp.value = -1; - item_tmp.icon = ICON_ADD; - RNA_enum_item_add(&item, &totitem, &item_tmp); - /* separator */ - RNA_enum_item_add_separator(&item, &totitem); - } const int tot = BLI_listbase_count(&gpd->layers); /* Existing layers */ for (gpl = gpd->layers.last, i = 0; gpl; gpl = gpl->prev, i++) { @@ -428,6 +418,17 @@ const EnumPropertyItem *ED_gpencil_layers_with_new_enum_itemf(bContext *C, RNA_enum_item_add(&item, &totitem, &item_tmp); } + { + /* separator */ + RNA_enum_item_add_separator(&item, &totitem); + + /* "New Layer" entry */ + item_tmp.identifier = "__CREATE__"; + item_tmp.name = "New Layer"; + item_tmp.value = -1; + item_tmp.icon = ICON_ADD; + RNA_enum_item_add(&item, &totitem, &item_tmp); + } RNA_enum_item_end(&item, &totitem); *r_free = true; @@ -2063,7 +2064,7 @@ void ED_gpencil_update_color_uv(Main *bmain, Material *mat) if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { continue; } - gps_ma = give_current_material(ob, gps->mat_nr + 1); + gps_ma = BKE_material_gpencil_get(ob, gps->mat_nr + 1); /* update */ if ((gps_ma) && (gps_ma == mat)) { ED_gpencil_calc_stroke_uv(ob, gps); diff --git a/source/blender/editors/include/BIF_glutil.h b/source/blender/editors/include/BIF_glutil.h index 101a65d151a..a49f02d28fe 100644 --- a/source/blender/editors/include/BIF_glutil.h +++ b/source/blender/editors/include/BIF_glutil.h @@ -24,7 +24,6 @@ #ifndef __BIF_GLUTIL_H__ #define __BIF_GLUTIL_H__ -struct rctf; struct rcti; struct ColorManagedDisplaySettings; diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h index 6629eed8328..48d0a5fe8be 100644 --- a/source/blender/editors/include/ED_armature.h +++ b/source/blender/editors/include/ED_armature.h @@ -42,7 +42,6 @@ struct ReportList; struct Scene; struct UndoType; struct View3D; -struct ViewContext; struct ViewLayer; struct bArmature; struct bContext; @@ -75,12 +74,14 @@ typedef struct EditBone { int flag; int layer; + /* Envelope distance & weight */ float dist, weight; /** put them in order! transform uses this as scale */ float xwidth, length, zwidth; float rad_head, rad_tail; /* Bendy-Bone parameters */ + short segments; float roll1, roll2; float curve_in_x, curve_in_y; float curve_out_x, curve_out_y; @@ -91,8 +92,6 @@ typedef struct EditBone { /** for envelope scaling */ float oldlength; - short segments; - /** Type of next/prev bone handles */ char bbone_prev_type; char bbone_next_type; @@ -229,15 +228,9 @@ void ED_armature_edit_transform_mirror_update(struct Object *obedit); void ED_armature_origin_set( struct Main *bmain, struct Object *ob, const float cursor[3], int centermode, int around); -void ED_armature_transform_bones(struct bArmature *arm, float mat[4][4], const bool do_props); -void ED_armature_transform_apply(struct Main *bmain, - struct Object *ob, - float mat[4][4], - const bool do_props); -void ED_armature_transform(struct Main *bmain, - struct bArmature *arm, - float mat[4][4], - const bool do_props); +void ED_armature_edit_transform(struct bArmature *arm, const float mat[4][4], const bool do_props); + +void ED_armature_transform(struct bArmature *arm, const float mat[4][4], const bool do_props); #define ARM_GROUPS_NAME 1 #define ARM_GROUPS_ENVELOPE 2 diff --git a/source/blender/editors/include/ED_clip.h b/source/blender/editors/include/ED_clip.h index 7cee5a358f4..22bf22e04d3 100644 --- a/source/blender/editors/include/ED_clip.h +++ b/source/blender/editors/include/ED_clip.h @@ -29,7 +29,6 @@ struct ImBuf; struct Main; struct Mask; struct MovieClip; -struct Scene; struct SpaceClip; struct bContext; struct bScreen; diff --git a/source/blender/editors/include/ED_fileselect.h b/source/blender/editors/include/ED_fileselect.h index 7273f857a41..dc42237d804 100644 --- a/source/blender/editors/include/ED_fileselect.h +++ b/source/blender/editors/include/ED_fileselect.h @@ -34,17 +34,32 @@ struct wmWindowManager; #define FILE_LAYOUT_HOR 1 #define FILE_LAYOUT_VER 2 -#define MAX_FILE_COLUMN 4 - -typedef enum FileListColumns { +typedef enum FileAttributeColumnType { + COLUMN_NONE = -1, COLUMN_NAME = 0, - COLUMN_DATE, - COLUMN_TIME, + COLUMN_DATETIME, COLUMN_SIZE, -} FileListColumns; + + ATTRIBUTE_COLUMN_MAX +} FileAttributeColumnType; + +typedef struct FileAttributeColumn { + /** UI name for this column */ + const char *name; + + float width; + /* The sort type to use when sorting by this column. */ + int sort_type; /* eFileSortType */ + + /* Alignment of column texts, header text is always left aligned */ + int text_align; /* eFontStyle_Align */ +} FileAttributeColumn; typedef struct FileLayout { /* view settings - XXX - move into own struct */ + int offset_top; + /* Height of the header for the different FileAttributeColumn's. */ + int attribute_column_header_h; int prv_w; int prv_h; int tile_w; @@ -54,13 +69,17 @@ typedef struct FileLayout { int prv_border_x; int prv_border_y; int rows; - int columns; + /* Those are the major layout columns the files are distributed across, not to be confused with + * 'attribute_columns' array below. */ + int flow_columns; int width; int height; int flag; int dirty; int textheight; - float column_widths[MAX_FILE_COLUMN]; + /* The columns for each item (name, modification date/time, size). Not to be confused with the + * 'flow_columns' above. */ + FileAttributeColumn attribute_columns[ATTRIBUTE_COLUMN_MAX]; /* When we change display size, we may have to update static strings like size of files... */ short curr_size; @@ -72,6 +91,7 @@ typedef struct FileSelection { } FileSelection; struct rcti; +struct View2D; struct FileSelectParams *ED_fileselect_get_params(struct SpaceFile *sfile); @@ -87,6 +107,17 @@ int ED_fileselect_layout_numfiles(FileLayout *layout, struct ARegion *ar); int ED_fileselect_layout_offset(FileLayout *layout, int x, int y); FileSelection ED_fileselect_layout_offset_rect(FileLayout *layout, const struct rcti *rect); +void ED_fileselect_layout_maskrect(const FileLayout *layout, + const struct View2D *v2d, + struct rcti *r_rect); +bool ED_fileselect_layout_is_inside_pt(const FileLayout *layout, + const struct View2D *v2d, + int x, + int y); +bool ED_fileselect_layout_isect_rect(const FileLayout *layout, + const struct View2D *v2d, + const struct rcti *rect, + struct rcti *r_dst); void ED_fileselect_layout_tilepos(FileLayout *layout, int tile, int *x, int *y); void ED_operatormacros_file(void); diff --git a/source/blender/editors/include/ED_gizmo_library.h b/source/blender/editors/include/ED_gizmo_library.h index f14648bc026..a3e2e643509 100644 --- a/source/blender/editors/include/ED_gizmo_library.h +++ b/source/blender/editors/include/ED_gizmo_library.h @@ -43,10 +43,8 @@ void ED_gizmotypes_value_2d(void); void ED_gizmogrouptypes_value_2d(void); struct Object; -struct Scene; struct bContext; struct wmGizmo; -struct wmGizmoGroup; /* -------------------------------------------------------------------- */ /* Shape Presets diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h index 6410e6630a7..470eb58c72b 100644 --- a/source/blender/editors/include/ED_gpencil.h +++ b/source/blender/editors/include/ED_gpencil.h @@ -27,7 +27,6 @@ struct ID; struct ListBase; struct PointerRNA; -struct rcti; struct Brush; struct bGPDframe; @@ -39,13 +38,11 @@ struct tGPspoint; struct ARegion; struct Depsgraph; -struct EvaluationContext; struct Main; struct RegionView3D; struct ReportList; struct Scene; struct ScrArea; -struct ToolSettings; struct View3D; struct ViewLayer; struct bContext; @@ -58,8 +55,6 @@ struct bAnimContext; struct wmKeyConfig; struct wmOperator; -struct wmWindow; -struct wmWindowManager; /* ------------- Grease-Pencil Runtime Data ---------------- */ diff --git a/source/blender/editors/include/ED_image.h b/source/blender/editors/include/ED_image.h index a09e1d579fd..7d69f86dbf8 100644 --- a/source/blender/editors/include/ED_image.h +++ b/source/blender/editors/include/ED_image.h @@ -31,7 +31,6 @@ struct ImageUser; struct ReportList; struct Scene; struct SpaceImage; -struct ToolSettings; struct ViewLayer; struct bContext; struct wmWindowManager; diff --git a/source/blender/editors/include/ED_keyframes_draw.h b/source/blender/editors/include/ED_keyframes_draw.h index 05f641abe91..2630feaa9d3 100644 --- a/source/blender/editors/include/ED_keyframes_draw.h +++ b/source/blender/editors/include/ED_keyframes_draw.h @@ -31,7 +31,6 @@ struct FCurve; struct ListBase; struct MaskLayer; struct Object; -struct Palette; struct Scene; struct View2D; struct bAction; diff --git a/source/blender/editors/include/ED_keyframing.h b/source/blender/editors/include/ED_keyframing.h index bbeeeade822..42e5add2ef0 100644 --- a/source/blender/editors/include/ED_keyframing.h +++ b/source/blender/editors/include/ED_keyframing.h @@ -44,8 +44,6 @@ struct bPoseChannel; struct ReportList; struct bContext; -struct Depsgraph; - struct EnumPropertyItem; struct PointerRNA; struct PropertyRNA; diff --git a/source/blender/editors/include/ED_markers.h b/source/blender/editors/include/ED_markers.h index a883187327f..a9e57ecc13d 100644 --- a/source/blender/editors/include/ED_markers.h +++ b/source/blender/editors/include/ED_markers.h @@ -29,7 +29,6 @@ struct TimeMarker; struct bAnimContext; struct bContext; struct wmKeyConfig; -struct wmKeyMap; /* Drawing API ------------------------------ */ diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index 5ebba4e5d25..ee4798430ef 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -45,7 +45,6 @@ struct Mesh; struct Object; struct ReportList; struct Scene; -struct ToolSettings; struct UndoType; struct UvMapVert; struct UvMapVert; @@ -55,7 +54,6 @@ struct View3D; struct ViewContext; struct bContext; struct bDeformGroup; -struct rcti; struct wmKeyConfig; struct wmOperator; @@ -105,7 +103,9 @@ bool EDBM_vert_color_check(struct BMEditMesh *em); bool EDBM_mesh_hide(struct BMEditMesh *em, bool swap); bool EDBM_mesh_reveal(struct BMEditMesh *em, bool select); -void EDBM_update_generic(struct BMEditMesh *em, const bool do_tessface, const bool is_destructive); +void EDBM_update_generic(struct BMEditMesh *em, + const bool do_tessellation, + const bool is_destructive); struct UvElementMap *BM_uv_element_map_create(struct BMesh *bm, const bool selected, @@ -138,13 +138,21 @@ bool BMBVH_EdgeVisible(struct BMBVHTree *tree, struct View3D *v3d, struct Object *obedit); +/* editmesh_automerge.c */ +void EDBM_automerge(struct Object *ob, bool update, const char hflag, const float dist); +void EDBM_automerge_and_split(struct Object *ob, + bool split_edges, + bool split_faces, + bool update, + const char hflag, + const float dist); + /* editmesh_undo.c */ void ED_mesh_undosys_type(struct UndoType *ut); /* editmesh_select.c */ void EDBM_select_mirrored( struct BMEditMesh *em, const int axis, const bool extend, int *r_totmirr, int *r_totfail); -void EDBM_automerge(struct Scene *scene, struct Object *ob, bool update, const char hflag); struct BMVert *EDBM_vert_find_nearest_ex(struct ViewContext *vc, float *r_dist, @@ -188,8 +196,11 @@ bool EDBM_unified_findnearest(struct ViewContext *vc, bool EDBM_unified_findnearest_from_raycast(struct ViewContext *vc, struct Base **bases, const uint bases_len, - bool use_boundary, - int *r_base_index, + bool use_boundary_vertices, + bool use_boundary_edges, + int *r_base_index_vert, + int *r_base_index_edge, + int *r_base_index_face, struct BMVert **r_eve, struct BMEdge **r_eed, struct BMFace **r_efa); @@ -239,15 +250,30 @@ void EDBM_preselect_edgering_update_from_edge(struct EditMesh_PreSelEdgeRing *ps /* editmesh_preselect_elem.c */ struct EditMesh_PreSelElem; +typedef enum eEditMesh_PreSelPreviewAction { + PRESELECT_ACTION_TRANSFORM = 1, + PRESELECT_ACTION_CREATE = 2, + PRESELECT_ACTION_DELETE = 3, +} eEditMesh_PreSelPreviewAction; + struct EditMesh_PreSelElem *EDBM_preselect_elem_create(void); void EDBM_preselect_elem_destroy(struct EditMesh_PreSelElem *psel); void EDBM_preselect_elem_clear(struct EditMesh_PreSelElem *psel); +void EDBM_preselect_preview_clear(struct EditMesh_PreSelElem *psel); void EDBM_preselect_elem_draw(struct EditMesh_PreSelElem *psel, const float matrix[4][4]); void EDBM_preselect_elem_update_from_single(struct EditMesh_PreSelElem *psel, struct BMesh *bm, struct BMElem *ele, const float (*coords)[3]); +void EDBM_preselect_elem_update_preview(struct EditMesh_PreSelElem *psel, + struct ViewContext *vc, + struct BMesh *bm, + struct BMElem *ele, + const int mval[2]); +void EDBM_preselect_action_set(struct EditMesh_PreSelElem *psel, + eEditMesh_PreSelPreviewAction action); +eEditMesh_PreSelPreviewAction EDBM_preselect_action_get(struct EditMesh_PreSelElem *psel); /* mesh_ops.c */ void ED_operatortypes_mesh(void); void ED_operatormacros_mesh(void); @@ -358,21 +384,14 @@ void ED_mesh_geometry_add( struct Mesh *mesh, struct ReportList *reports, int verts, int edges, int faces); #endif void ED_mesh_polys_add(struct Mesh *mesh, struct ReportList *reports, int count); -void ED_mesh_tessfaces_add(struct Mesh *mesh, struct ReportList *reports, int count); void ED_mesh_edges_add(struct Mesh *mesh, struct ReportList *reports, int count); void ED_mesh_loops_add(struct Mesh *mesh, struct ReportList *reports, int count); void ED_mesh_vertices_add(struct Mesh *mesh, struct ReportList *reports, int count); -void ED_mesh_faces_remove(struct Mesh *mesh, struct ReportList *reports, int count); void ED_mesh_edges_remove(struct Mesh *mesh, struct ReportList *reports, int count); void ED_mesh_vertices_remove(struct Mesh *mesh, struct ReportList *reports, int count); -void ED_mesh_calc_tessface(struct Mesh *mesh, bool free_mpoly); -void ED_mesh_update(struct Mesh *mesh, - struct bContext *C, - bool calc_edges, - bool calc_edges_loose, - bool calc_tessface); +void ED_mesh_update(struct Mesh *mesh, struct bContext *C, bool calc_edges, bool calc_edges_loose); void ED_mesh_uv_texture_ensure(struct Mesh *me, const char *name); int ED_mesh_uv_texture_add(struct Mesh *me, diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h index 7b9a96e4d07..c481c19a552 100644 --- a/source/blender/editors/include/ED_object.h +++ b/source/blender/editors/include/ED_object.h @@ -34,7 +34,6 @@ struct EnumPropertyItem; struct EnumPropertyItem; struct ID; struct Main; -struct Menu; struct ModifierData; struct Object; struct PointerRNA; @@ -44,16 +43,15 @@ struct Scene; struct ShaderFxData; struct View3D; struct ViewLayer; +struct XFormObjectData; struct bConstraint; struct bContext; struct bFaceMap; struct bPoseChannel; struct uiLayout; struct wmKeyConfig; -struct wmKeyMap; struct wmOperator; struct wmOperatorType; -struct wmWindow; struct wmWindowManager; #include "DNA_object_enums.h" @@ -403,6 +401,12 @@ bool ED_object_jump_to_bone(struct bContext *C, void ED_object_facemap_face_add(struct Object *ob, struct bFaceMap *fmap, int facenum); void ED_object_facemap_face_remove(struct Object *ob, struct bFaceMap *fmap, int facenum); +/* object_data_transform.c */ +struct XFormObjectData *ED_object_data_xform_create(struct ID *id); +void ED_object_data_xform_destroy(struct XFormObjectData *xod); + +void ED_object_data_xform_by_mat4(struct XFormObjectData *xod, const float mat[4][4]); + #ifdef __cplusplus } #endif diff --git a/source/blender/editors/include/ED_outliner.h b/source/blender/editors/include/ED_outliner.h index e94aedc2b2b..3015d8d9cdc 100644 --- a/source/blender/editors/include/ED_outliner.h +++ b/source/blender/editors/include/ED_outliner.h @@ -30,4 +30,18 @@ bool ED_outliner_collections_editor_poll(struct bContext *C); void ED_outliner_selected_objects_get(const struct bContext *C, struct ListBase *objects); +Base *ED_outliner_give_base_under_cursor(struct bContext *C, const int mval[2]); + +void ED_outliner_select_sync_from_object_tag(struct bContext *C); +void ED_outliner_select_sync_from_edit_bone_tag(struct bContext *C); +void ED_outliner_select_sync_from_pose_bone_tag(struct bContext *C); +void ED_outliner_select_sync_from_sequence_tag(struct bContext *C); +void ED_outliner_select_sync_from_all_tag(struct bContext *C); + +bool ED_outliner_select_sync_is_dirty(const struct bContext *C); + +void ED_outliner_select_sync_from_outliner(struct bContext *C, struct SpaceOutliner *soops); + +void ED_outliner_select_sync_flag_outliners(const struct bContext *C); + #endif /* __ED_OUTLINER_H__ */ diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index c7ee7be49b5..d0fab134dcc 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -39,10 +39,8 @@ struct Depsgraph; struct IDProperty; struct Main; struct MenuType; -struct PropertyRNA; struct Scene; struct SpaceLink; -struct ViewLayer; struct WorkSpace; struct WorkSpaceInstanceHook; struct bContext; @@ -50,7 +48,6 @@ struct bScreen; struct rcti; struct uiBlock; struct uiLayout; -struct wmEvent; struct wmKeyConfig; struct wmMsgBus; struct wmMsgSubscribeKey; diff --git a/source/blender/editors/include/ED_sculpt.h b/source/blender/editors/include/ED_sculpt.h index 034e002f86a..d907ba4e581 100644 --- a/source/blender/editors/include/ED_sculpt.h +++ b/source/blender/editors/include/ED_sculpt.h @@ -25,10 +25,7 @@ #define __ED_SCULPT_H__ struct ARegion; -struct ListBase; struct Object; -struct RegionView3D; -struct UndoStep; struct UndoType; struct ViewContext; struct bContext; diff --git a/source/blender/editors/include/ED_time_scrub_ui.h b/source/blender/editors/include/ED_time_scrub_ui.h index b43e674224c..f010c45d939 100644 --- a/source/blender/editors/include/ED_time_scrub_ui.h +++ b/source/blender/editors/include/ED_time_scrub_ui.h @@ -24,7 +24,6 @@ #ifndef __ED_TIME_SCRUB_UI_H__ #define __ED_TIME_SCRUB_UI_H__ -struct View2DGrid; struct bContext; struct bDopeSheet; struct wmEvent; diff --git a/source/blender/editors/include/ED_transform.h b/source/blender/editors/include/ED_transform.h index 3605a245187..d8b65aa5975 100644 --- a/source/blender/editors/include/ED_transform.h +++ b/source/blender/editors/include/ED_transform.h @@ -26,18 +26,11 @@ /* ******************* Registration Function ********************** */ -struct ARegion; -struct ListBase; -struct Main; struct Object; struct SnapObjectContext; struct SnapObjectParams; -struct View3D; -struct WorkSpace; struct bContext; -struct wmEvent; struct wmKeyConfig; -struct wmKeyMap; struct wmOperatorType; void ED_keymap_transform(struct wmKeyConfig *keyconf); @@ -97,6 +90,10 @@ enum TfmMode { #define CTX_PAINT_CURVE (1 << 8) #define CTX_GPENCIL_STROKES (1 << 9) #define CTX_CURSOR (1 << 10) +/** When transforming object's, adjust the object data so it stays in the same place. */ +#define CTX_OBMODE_XFORM_OBDATA (1 << 11) +/** Transform object parents without moving their children. */ +#define CTX_OBMODE_XFORM_SKIP_CHILDREN (1 << 12) /* Standalone call to get the transformation center corresponding to the current situation * returns 1 if successful, 0 otherwise (usually means there's no selection) @@ -112,7 +109,6 @@ struct Scene; struct TransInfo; struct wmGizmoGroup; struct wmGizmoGroupType; -struct wmOperator; /* UNUSED */ // int BIF_snappingSupported(struct Object *obedit); @@ -197,12 +193,12 @@ bool peelObjectsSnapContext(struct SnapObjectContext *sctx, float r_no[3], float *r_thickness); -bool snapObjectsTransform(struct TransInfo *t, - const float mval[2], - float *dist_px, - /* return args */ - float r_loc[3], - float r_no[3]); +short snapObjectsTransform(struct TransInfo *t, + const float mval[2], + float *dist_px, + /* return args */ + float r_loc[3], + float r_no[3]); bool snapNodesTransform(struct TransInfo *t, const int mval[2], /* return args */ diff --git a/source/blender/editors/include/ED_transform_snap_object_context.h b/source/blender/editors/include/ED_transform_snap_object_context.h index f27523bb1f8..40e0005b487 100644 --- a/source/blender/editors/include/ED_transform_snap_object_context.h +++ b/source/blender/editors/include/ED_transform_snap_object_context.h @@ -32,8 +32,6 @@ struct Main; struct Object; struct Scene; struct View3D; -struct ViewLayer; -struct bContext; /* transform_snap_object.c */ @@ -125,6 +123,7 @@ short ED_transform_snap_object_project_view3d_ex(struct SnapObjectContext *sctx, const unsigned short snap_to, const struct SnapObjectParams *params, const float mval[2], + const float prev_co[3], float *dist_px, float r_loc[3], float r_no[3], @@ -135,6 +134,7 @@ bool ED_transform_snap_object_project_view3d(struct SnapObjectContext *sctx, const unsigned short snap_to, const struct SnapObjectParams *params, const float mval[2], + const float prev_co[3], float *dist_px, /* return args */ float r_loc[3], diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h index 8dd2dab5209..1856ad8454b 100644 --- a/source/blender/editors/include/ED_uvedit.h +++ b/source/blender/editors/include/ED_uvedit.h @@ -37,7 +37,6 @@ struct Object; struct Scene; struct SpaceImage; struct ToolSettings; -struct View3D; struct ViewLayer; struct bNode; struct wmKeyConfig; diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 67dfb184d19..139b306b533 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -38,8 +38,6 @@ struct Camera; struct CustomData_MeshMasks; struct Depsgraph; struct EditBone; -struct GPUFX; -struct GPUFXSettings; struct GPUOffScreen; struct GPUViewport; struct ID; @@ -58,7 +56,6 @@ struct View3D; struct View3DShading; struct ViewContext; struct ViewLayer; -struct WorkSpace; struct bContext; struct bPoseChannel; struct bScreen; @@ -690,6 +687,10 @@ bool ED_view3d_distance_set_from_location(struct RegionView3D *rv3d, float ED_scene_grid_scale(struct Scene *scene, const char **grid_unit); float ED_view3d_grid_scale(struct Scene *scene, struct View3D *v3d, const char **grid_unit); +void ED_view3d_grid_steps(struct Scene *scene, + struct View3D *v3d, + struct RegionView3D *rv3d, + float *r_grid_steps); float ED_view3d_grid_view_scale(struct Scene *scene, struct View3D *v3d, struct RegionView3D *rv3d, diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h index 03a49943ed3..41ac1b6b452 100644 --- a/source/blender/editors/include/UI_icons.h +++ b/source/blender/editors/include/UI_icons.h @@ -483,7 +483,7 @@ DEF_ICON(UNDERLINE) DEF_ICON(SMALL_CAPS) DEF_ICON_BLANK(742) DEF_ICON_BLANK(743) -DEF_ICON_BLANK(744) +DEF_ICON_MODIFIER(CON_ACTION) DEF_ICON_BLANK(745) DEF_ICON_BLANK(746) DEF_ICON_BLANK(747) @@ -667,8 +667,8 @@ DEF_ICON(PARTICLE_PATH) /* EDITING */ DEF_ICON_BLANK(669) DEF_ICON_BLANK(670) -DEF_ICON_BLANK(671) -DEF_ICON_BLANK(672) +DEF_ICON(SNAP_PERPENDICULAR) +DEF_ICON(SNAP_MIDPOINT) DEF_ICON(SNAP_OFF) DEF_ICON(SNAP_ON) DEF_ICON(SNAP_NORMAL) @@ -866,8 +866,8 @@ DEF_ICON(SEQ_LUMA_WAVEFORM) DEF_ICON(SEQ_CHROMA_SCOPE) DEF_ICON(SEQ_HISTOGRAM) DEF_ICON(SEQ_SPLITVIEW) -DEF_ICON_BLANK(870) -DEF_ICON_BLANK(871) +DEF_ICON(SEQ_STRIP_META) +DEF_ICON(SEQ_STRIP_DUPLICATE) DEF_ICON_BLANK(872) DEF_ICON(IMAGE_RGB) // XXX CHANGE TO STRAIGHT ALPHA, Z ETC DEF_ICON(IMAGE_RGB_ALPHA) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 5ef3e5d8987..aa1044647c8 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -31,7 +31,6 @@ /* Struct Declarations */ struct ARegion; -struct ARegionType; struct AutoComplete; struct ID; struct IDProperty; @@ -90,7 +89,7 @@ typedef struct uiPopupBlockHandle uiPopupBlockHandle; * For #ARegion.overlap regions, pass events though if they don't overlap * the regions contents (the usable part of the #View2D and buttons). * - * The margin is needed so it's not possible to accidentally click inbetween buttons. + * The margin is needed so it's not possible to accidentally click in between buttons. */ #define UI_REGION_OVERLAP_MARGIN (U.widget_unit / 3) diff --git a/source/blender/editors/include/UI_resources.h b/source/blender/editors/include/UI_resources.h index 1db584f0f59..a73e2655b7d 100644 --- a/source/blender/editors/include/UI_resources.h +++ b/source/blender/editors/include/UI_resources.h @@ -255,6 +255,7 @@ typedef enum ThemeColorID { TH_MATCH, /* highlight color for search matches */ TH_SELECT_HIGHLIGHT, /* highlight color for selected outliner item */ + TH_SELECT_ACTIVE, /* highlight color for active outliner item */ TH_SELECTED_OBJECT, /* selected object color for outliner */ TH_ACTIVE_OBJECT, /* active object color for outliner */ TH_EDITED_OBJECT, /* edited object color for outliner */ diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 7a123599be5..ee354df3a25 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -908,12 +908,12 @@ void UI_but_execute(const bContext *C, ARegion *ar, uiBut *but) * returns false if undo needs to be disabled. */ static bool ui_but_is_rna_undo(const uiBut *but) { - if (but->rnapoin.id.data) { + if (but->rnapoin.owner_id) { /* avoid undo push for buttons who's ID are screen or wm level * we could disable undo for buttons with no ID too but may have * unforeseen consequences, so best check for ID's we _know_ are not * handled by undo - campbell */ - ID *id = but->rnapoin.id.data; + ID *id = but->rnapoin.owner_id; if (ID_CHECK_UNDO(id) == false) { return false; } @@ -1226,8 +1226,8 @@ static bool ui_but_event_property_operator_string(const bContext *C, */ char *data_path = NULL; - if (ptr->id.data) { - ID *id = ptr->id.data; + if (ptr->owner_id) { + ID *id = ptr->owner_id; if (GS(id->name) == ID_SCR) { /* screen/editor property @@ -1731,7 +1731,7 @@ static void ui_block_message_subscribe(ARegion *ar, struct wmMsgBus *mbus, uiBlo if ((but_prev && (but_prev->rnaprop == but->rnaprop) && (but_prev->rnapoin.type == but->rnapoin.type) && (but_prev->rnapoin.data == but->rnapoin.data) && - (but_prev->rnapoin.id.data == but->rnapoin.id.data)) == false) { + (but_prev->rnapoin.owner_id == but->rnapoin.owner_id)) == false) { /* TODO: could make this into utility function. */ WM_msg_subscribe_rna(mbus, &but->rnapoin, @@ -2055,7 +2055,7 @@ bool ui_but_is_compatible(const uiBut *but_a, const uiBut *but_b) } if (but_a->rnaprop) { - /* skip 'rnapoin.data', 'rnapoin.id.data' + /* skip 'rnapoin.data', 'rnapoin.owner_id' * allow different data to have the same props edited at once */ if (but_a->rnapoin.type != but_b->rnapoin.type) { return false; @@ -2479,7 +2479,7 @@ void ui_but_string_get_ex(uiBut *but, /* uiBut.custom_data points to data this tab represents (e.g. workspace). * uiBut.rnapoin/prop store an active value (e.g. active workspace). */ - RNA_pointer_create(but->rnapoin.id.data, ptr_type, but->custom_data, &ptr); + RNA_pointer_create(but->rnapoin.owner_id, ptr_type, but->custom_data, &ptr); buf = RNA_struct_name_get_alloc(&ptr, str, maxlen, &buf_len); } else if (type == PROP_STRING) { @@ -2822,7 +2822,7 @@ bool ui_but_string_set(bContext *C, uiBut *but, const char *str) /* uiBut.custom_data points to data this tab represents (e.g. workspace). * uiBut.rnapoin/prop store an active value (e.g. active workspace). */ - RNA_pointer_create(but->rnapoin.id.data, ptr_type, but->custom_data, &ptr); + RNA_pointer_create(but->rnapoin.owner_id, ptr_type, but->custom_data, &ptr); prop = RNA_struct_name_property(ptr_type); if (RNA_property_editable(&ptr, prop)) { RNA_property_string_set(&ptr, prop, str); @@ -3586,6 +3586,14 @@ static uiBut *ui_def_but(uiBlock *block, (a1 != 0.0f && a1 != 1.0f)) == false); } + /* Number buttons must have a click-step, + * assert instead of correcting the value to ensure the caller knows what they're doing. */ + if ((type & BUTTYPE) == UI_BTYPE_NUM) { + if (ELEM((type & UI_BUT_POIN_TYPES), UI_BUT_POIN_CHAR, UI_BUT_POIN_SHORT, UI_BUT_POIN_INT)) { + BLI_assert((int)a1 > 0); + } + } + if (type & UI_BUT_POIN_TYPES) { /* a pointer is required */ if (poin == NULL) { BLI_assert(0); diff --git a/source/blender/editors/interface/interface_anim.c b/source/blender/editors/interface/interface_anim.c index 4d87cc22ef2..168c6051327 100644 --- a/source/blender/editors/interface/interface_anim.c +++ b/source/blender/editors/interface/interface_anim.c @@ -228,7 +228,7 @@ bool ui_but_anim_expression_create(uiBut *but, const char *str) /* make sure we have animdata for this */ /* FIXME: until materials can be handled by depsgraph, * don't allow drivers to be created for them */ - id = (ID *)but->rnapoin.id.data; + id = but->rnapoin.owner_id; if ((id == NULL) || (GS(id->name) == ID_MA) || (GS(id->name) == ID_TE)) { if (G.debug & G_DEBUG) { printf("ERROR: create expression failed - invalid data-block for adding drivers (%p)\n", id); @@ -314,7 +314,7 @@ void ui_but_anim_autokey(bContext *C, uiBut *but, Scene *scene, float cfra) } } else { - id = but->rnapoin.id.data; + id = but->rnapoin.owner_id; /* TODO: this should probably respect the keyingset only option for anim */ if (autokeyframe_cfra_can_key(scene, id)) { diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c index 22b75da4968..580ff527bf6 100644 --- a/source/blender/editors/interface/interface_context_menu.c +++ b/source/blender/editors/interface/interface_context_menu.c @@ -176,11 +176,21 @@ static uiBlock *menu_change_shortcut(bContext *C, ARegion *ar, void *arg) UI_block_flag_enable(block, UI_BLOCK_MOVEMOUSE_QUIT); UI_block_direction_set(block, UI_DIR_CENTER_Y); - layout = UI_block_layout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, 200, 20, 0, style); - + layout = UI_block_layout(block, + UI_LAYOUT_VERTICAL, + UI_LAYOUT_PANEL, + 0, + 0, + U.widget_unit * 10, + U.widget_unit * 2, + 0, + style); + + uiItemL(layout, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Change Shortcut"), ICON_HAND); uiItemR(layout, &ptr, "type", UI_ITEM_R_FULL_EVENT | UI_ITEM_R_IMMEDIATE, "", ICON_NONE); - UI_block_bounds_set_popup(block, 6, (const int[2]){-50, 26}); + UI_block_bounds_set_popup( + block, 6 * U.dpi_fac, (const int[2]){-100 * U.dpi_fac, 36 * U.dpi_fac}); shortcut_free_operator_property(prop); @@ -227,11 +237,21 @@ static uiBlock *menu_add_shortcut(bContext *C, ARegion *ar, void *arg) UI_block_func_handle_set(block, but_shortcut_name_func, but); UI_block_direction_set(block, UI_DIR_CENTER_Y); - layout = UI_block_layout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, 200, 20, 0, style); - + layout = UI_block_layout(block, + UI_LAYOUT_VERTICAL, + UI_LAYOUT_PANEL, + 0, + 0, + U.widget_unit * 10, + U.widget_unit * 2, + 0, + style); + + uiItemL(layout, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Assign Shortcut"), ICON_HAND); uiItemR(layout, &ptr, "type", UI_ITEM_R_FULL_EVENT | UI_ITEM_R_IMMEDIATE, "", ICON_NONE); - UI_block_bounds_set_popup(block, 6, (const int[2]){-50, 26}); + UI_block_bounds_set_popup( + block, 6 * U.dpi_fac, (const int[2]){-100 * U.dpi_fac, 36 * U.dpi_fac}); #ifdef USE_KEYMAP_ADD_HACK g_kmi_id_hack = kmi_id; @@ -470,8 +490,9 @@ static void ui_but_menu_add_path_operators(uiLayout *layout, PointerRNA *ptr, Pr bool ui_popup_context_menu_for_button(bContext *C, uiBut *but) { - /* having this menu for some buttons makes no sense */ - if (but->type == UI_BTYPE_IMAGE) { + /* ui_but_is_interactive() may let some buttons through that should not get a context menu - it + * doesn't make sense for them. */ + if (ELEM(but->type, UI_BTYPE_LABEL, UI_BTYPE_IMAGE)) { return false; } @@ -904,7 +925,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but) ICON_NONE, "UI_OT_copy_data_path_button"); - if (ptr->id.data && !is_whole_array && + if (ptr->owner_id && !is_whole_array && ELEM(type, PROP_BOOLEAN, PROP_INT, PROP_FLOAT, PROP_ENUM)) { uiItemO(layout, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy As New Driver"), @@ -1222,7 +1243,7 @@ void ui_popup_context_menu_for_panel(bContext *C, ARegion *ar, Panel *pa) sizeof(tmpstr), "%s" UI_SEP_CHAR_S "%s", IFACE_("Pin"), - IFACE_("Shift+Left Mouse")); + IFACE_("Shift Left Mouse")); uiItemR(layout, &ptr, "use_pin", 0, tmpstr, ICON_NONE); /* evil, force shortcut flag */ @@ -1230,6 +1251,7 @@ void ui_popup_context_menu_for_panel(bContext *C, ARegion *ar, Panel *pa) uiBlock *block = uiLayoutGetBlock(layout); uiBut *but = block->buttons.last; but->flag |= UI_BUT_HAS_SEP_CHAR; + but->drawflag |= UI_BUT_HAS_SHORTCUT; } } UI_popup_menu_end(C, pup); diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c index 76630de96db..b7a0812c5f2 100644 --- a/source/blender/editors/interface/interface_draw.c +++ b/source/blender/editors/interface/interface_draw.c @@ -750,7 +750,7 @@ void ui_draw_but_IMAGE(ARegion *UNUSED(ar), /** * Draw title and text safe areas. * - * \Note This functionn is to be used with the 2D dashed shader enabled. + * \note This function is to be used with the 2D dashed shader enabled. * * \param pos: is a PRIM_FLOAT, 2, GPU_FETCH_FLOAT vertex attribute. * \param line_origin: is a PRIM_FLOAT, 2, GPU_FETCH_FLOAT vertex attribute. diff --git a/source/blender/editors/interface/interface_eyedropper_datablock.c b/source/blender/editors/interface/interface_eyedropper_datablock.c index 658aa4f67f9..336fae45895 100644 --- a/source/blender/editors/interface/interface_eyedropper_datablock.c +++ b/source/blender/editors/interface/interface_eyedropper_datablock.c @@ -51,6 +51,7 @@ #include "ED_space_api.h" #include "ED_screen.h" #include "ED_view3d.h" +#include "ED_outliner.h" #include "interface_intern.h" #include "interface_eyedropper_intern.h" @@ -67,6 +68,7 @@ typedef struct DataDropper { ID *init_id; /* for resetting on cancel */ + ScrArea *cursor_area; /* Area under the cursor */ ARegionType *art; void *draw_handle_pixel; char name[200]; @@ -103,6 +105,7 @@ static int datadropper_init(bContext *C, wmOperator *op) ddr->is_undo = UI_but_flag_is_set(but, UI_BUT_UNDO); + ddr->cursor_area = CTX_wm_area(C); ddr->art = art; ddr->draw_handle_pixel = ED_region_draw_cb_activate( art, datadropper_draw_cb, ddr, REGION_DRAW_POST_PIXEL); @@ -115,7 +118,7 @@ static int datadropper_init(bContext *C, wmOperator *op) ddr->idcode_name = TIP_(BKE_idcode_to_name(ddr->idcode)); PointerRNA ptr = RNA_property_pointer_get(&ddr->ptr, ddr->prop); - ddr->init_id = ptr.id.data; + ddr->init_id = ptr.owner_id; return true; } @@ -141,7 +144,7 @@ static void datadropper_exit(bContext *C, wmOperator *op) /* *** datadropper id helper functions *** */ /** - * \brief get the ID from the screen. + * \brief get the ID from the 3D view or outliner. */ static void datadropper_id_sample_pt(bContext *C, DataDropper *ddr, int mx, int my, ID **r_id) { @@ -155,7 +158,7 @@ static void datadropper_id_sample_pt(bContext *C, DataDropper *ddr, int mx, int ddr->name[0] = '\0'; if (sa) { - if (sa->spacetype == SPACE_VIEW3D) { + if (ELEM(sa->spacetype, SPACE_VIEW3D, SPACE_OUTLINER)) { ARegion *ar = BKE_area_find_region_xy(sa, RGN_TYPE_WINDOW, mx, my); if (ar) { const int mval[2] = {mx - ar->winrct.xmin, my - ar->winrct.ymin}; @@ -167,7 +170,13 @@ static void datadropper_id_sample_pt(bContext *C, DataDropper *ddr, int mx, int /* grr, always draw else we leave stale text */ ED_region_tag_redraw(ar); - base = ED_view3d_give_base_under_cursor(C, mval); + if (sa->spacetype == SPACE_VIEW3D) { + base = ED_view3d_give_base_under_cursor(C, mval); + } + else { + base = ED_outliner_give_base_under_cursor(C, mval); + } + if (base) { Object *ob = base->object; ID *id = NULL; @@ -213,7 +222,7 @@ static bool datadropper_id_set(bContext *C, DataDropper *ddr, ID *id) ptr_value = RNA_property_pointer_get(&ddr->ptr, ddr->prop); - return (ptr_value.id.data == id); + return (ptr_value.owner_id == id); } /* single point sample & set */ @@ -232,6 +241,36 @@ static void datadropper_cancel(bContext *C, wmOperator *op) datadropper_exit(C, op); } +/* To switch the draw callback when region under mouse event changes */ +static void datadropper_set_draw_callback_region(bContext *C, + DataDropper *ddr, + const int mx, + const int my) +{ + bScreen *screen = CTX_wm_screen(C); + ScrArea *sa = BKE_screen_find_area_xy(screen, -1, mx, my); + + if (sa) { + /* If spacetype changed */ + if (sa->spacetype != ddr->cursor_area->spacetype) { + /* Remove old callback */ + ED_region_draw_cb_exit(ddr->art, ddr->draw_handle_pixel); + + /* Redraw old area */ + ARegion *ar = BKE_area_find_region_type(ddr->cursor_area, RGN_TYPE_WINDOW); + ED_region_tag_redraw(ar); + + /* Set draw callback in new region */ + ARegionType *art = BKE_regiontype_from_id(sa->type, RGN_TYPE_WINDOW); + + ddr->cursor_area = sa; + ddr->art = art; + ddr->draw_handle_pixel = ED_region_draw_cb_activate( + art, datadropper_draw_cb, ddr, REGION_DRAW_POST_PIXEL); + } + } +} + /* main modal status check */ static int datadropper_modal(bContext *C, wmOperator *op, const wmEvent *event) { @@ -260,6 +299,10 @@ static int datadropper_modal(bContext *C, wmOperator *op, const wmEvent *event) } else if (event->type == MOUSEMOVE) { ID *id = NULL; + + /* Set the region for eyedropper cursor text drawing */ + datadropper_set_draw_callback_region(C, ddr, event->x, event->y); + datadropper_id_sample_pt(C, ddr, event->x, event->y, &id); } diff --git a/source/blender/editors/interface/interface_eyedropper_driver.c b/source/blender/editors/interface/interface_eyedropper_driver.c index c1aee190cd3..e6fc52bc3bc 100644 --- a/source/blender/editors/interface/interface_eyedropper_driver.c +++ b/source/blender/editors/interface/interface_eyedropper_driver.c @@ -116,10 +116,10 @@ static void driverdropper_sample(bContext *C, wmOperator *op, const wmEvent *eve /* Now create driver(s) */ if (target_path && dst_path) { int success = ANIM_add_driver_with_target(op->reports, - ddr->ptr.id.data, + ddr->ptr.owner_id, dst_path, ddr->index, - target_ptr->id.data, + target_ptr->owner_id, target_path, target_index, flag, @@ -130,7 +130,7 @@ static void driverdropper_sample(bContext *C, wmOperator *op, const wmEvent *eve /* send updates */ UI_context_update_anim_flag(C); DEG_relations_tag_update(CTX_data_main(C)); - DEG_id_tag_update(ddr->ptr.id.data, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + DEG_id_tag_update(ddr->ptr.owner_id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_ANIMATION | ND_FCURVES_ORDER, NULL); // XXX } } diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 0cb0dbdc85c..55980099116 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -757,7 +757,7 @@ static void ui_apply_but_undo(uiBut *but) } /* Optionally override undo when undo system doesn't support storing properties. */ - if (but->rnapoin.id.data) { + if (but->rnapoin.owner_id) { /* Exception for renaming ID data, we always need undo pushes in this case, * because undo systems track data by their ID, see: T67002. */ extern PropertyRNA rna_ID_name; @@ -765,7 +765,7 @@ static void ui_apply_but_undo(uiBut *but) /* pass */ } else { - ID *id = but->rnapoin.id.data; + ID *id = but->rnapoin.owner_id; if (!ED_undo_is_legacy_compatible_for_property(but->block->evil_C, id)) { str = ""; } @@ -1558,7 +1558,7 @@ static bool ui_selectcontext_begin(bContext *C, uiBut *but, uiSelectContextStore if (use_path_from_id) { /* Path relative to ID. */ lprop = NULL; - RNA_id_pointer_create(link->ptr.id.data, &idptr); + RNA_id_pointer_create(link->ptr.owner_id, &idptr); RNA_path_resolve_property(&idptr, path, &lptr, &lprop); } else if (path) { @@ -2760,7 +2760,7 @@ static void ui_textedit_set_cursor_pos(uiBut *but, uiHandleButtonData *data, con startx += UI_DPI_ICON_SIZE / aspect; } } - /* but this extra .05 makes clicks inbetween characters feel nicer */ + /* But this extra .05 makes clicks in between characters feel nicer. */ startx += ((UI_TEXT_MARGIN_X + 0.05f) * U.widget_unit) / aspect; /* mouse dragged outside the widget to the left */ @@ -4800,18 +4800,19 @@ static int ui_do_but_NUM( if (click) { /* we can click on the side arrows to increment/decrement, * or click inside to edit the value directly */ - const float softmin = but->softmin; - const float softmax = but->softmax; if (!ui_but_is_float(but)) { /* Integer Value. */ if (but->drawflag & (UI_BUT_ACTIVE_LEFT | UI_BUT_ACTIVE_RIGHT)) { button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); + const int value_step = (int)but->a1; BLI_assert(value_step > 0); + const int softmin = round_fl_to_int_clamp(but->softmin); + const int softmax = round_fl_to_int_clamp(but->softmax); const double value_test = (but->drawflag & UI_BUT_ACTIVE_LEFT) ? - (double)max_ii((int)softmin, (int)data->value - value_step) : - (double)min_ii((int)softmax, (int)data->value + value_step); + (double)max_ii(softmin, (int)data->value - value_step) : + (double)min_ii(softmax, (int)data->value + value_step); if (value_test != data->value) { data->value = (double)value_test; } @@ -4828,11 +4829,14 @@ static int ui_do_but_NUM( /* Float Value. */ if (but->drawflag & (UI_BUT_ACTIVE_LEFT | UI_BUT_ACTIVE_RIGHT)) { button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); + const double value_step = (double)but->a1 * UI_PRECISION_FLOAT_SCALE; BLI_assert(value_step > 0.0f); const double value_test = (but->drawflag & UI_BUT_ACTIVE_LEFT) ? - (double)max_ff(softmin, (float)(data->value - value_step)) : - (double)min_ff(softmax, (float)(data->value + value_step)); + (double)max_ff(but->softmin, + (float)(data->value - value_step)) : + (double)min_ff(but->softmax, + (float)(data->value + value_step)); if (value_test != data->value) { data->value = value_test; } @@ -5484,7 +5488,7 @@ static bool ui_numedit_but_UNITVEC( static void ui_palette_set_active(uiBut *but) { if ((int)(but->a1) == UI_PALETTE_COLOR) { - Palette *palette = but->rnapoin.id.data; + Palette *palette = (Palette *)but->rnapoin.owner_id; PaletteColor *color = but->rnapoin.data; palette->active_color = BLI_findindex(&palette->colors, color); } @@ -5547,7 +5551,7 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co } else if ((int)(but->a1) == UI_PALETTE_COLOR && event->type == DELKEY && event->val == KM_PRESS) { - Palette *palette = but->rnapoin.id.data; + Palette *palette = (Palette *)but->rnapoin.owner_id; PaletteColor *color = but->rnapoin.data; BKE_palette_color_remove(palette, color); @@ -8184,7 +8188,7 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but) else { /* Do this so we can still mouse-up, closing the menu and running the button. * This is nice to support but there are times when the button gets left pressed. - * Keep disavled for now. */ + * Keep disabled for now. */ WM_event_remove_timer(data->wm, data->window, data->hold_action_timer); data->hold_action_timer = NULL; } diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index e9aa18394fa..7ada4755a64 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -154,8 +154,8 @@ typedef struct IconType { } IconType; /* ******************* STATIC LOCAL VARS ******************* */ -/* static here to cache results of icon directory scan, so it's not - * scanning the filesystem each time the menu is drawn */ +/* Static here to cache results of icon directory scan, so it's not + * scanning the file-system each time the menu is drawn. */ static struct ListBase iconfilelist = {NULL, NULL}; static IconTexture icongltex = {{0, 0}, 0, 0, 0, 0.0f, 0.0f}; @@ -317,8 +317,9 @@ static void vicon_keytype_draw_wrapper( format, "outlineColor", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); uint flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT); - immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND); GPU_program_point_size(true); + immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND); + immUniform1f("outline_scale", 1.0f); immUniform2f("ViewportSize", -1.0f, -1.0f); immBegin(GPU_PRIM_POINTS, 1); @@ -2136,7 +2137,7 @@ int UI_rnaptr_icon_get(bContext *C, PointerRNA *ptr, int rnaicon, const bool big /* try ID, material, texture or dynapaint slot */ if (RNA_struct_is_ID(ptr->type)) { - id = ptr->id.data; + id = ptr->owner_id; } else if (RNA_struct_is_a(ptr->type, &RNA_MaterialSlot)) { id = RNA_pointer_get(ptr, "material").data; @@ -2243,6 +2244,8 @@ int UI_idcode_icon_get(const int idcode) return ICON_FONT_DATA; case ID_WO: return ICON_WORLD_DATA; + case ID_WS: + return ICON_WORKSPACE; default: return ICON_NONE; } diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index 78eed98eb77..529cb0712df 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -1180,11 +1180,6 @@ static uiBut *uiItemFullO_ptr_ex(uiLayout *layout, assert(but->optype != NULL); - /* text alignment for toolbar buttons */ - if ((layout->root->type == UI_LAYOUT_TOOLBAR) && !icon) { - but->drawflag |= UI_BUT_TEXT_LEFT; - } - if (flag & UI_ITEM_R_NO_BG) { layout->emboss = prev_emboss; } @@ -1878,7 +1873,8 @@ void uiItemFullR(uiLayout *layout, uiBut *but; } ui_decorate = { .use_prop_decorate = (((layout->item.flag & UI_ITEM_PROP_DECORATE) != 0) && - (use_prop_sep && ptr->id.data && id_can_have_animdata(ptr->id.data))), + (use_prop_sep && ptr->owner_id && + id_can_have_animdata(ptr->owner_id))), }; #endif /* UI_PROP_DECORATE */ @@ -2951,7 +2947,7 @@ void uiItemLDrag(uiLayout *layout, PointerRNA *ptr, const char *name, int icon) if (ptr && ptr->type) { if (RNA_struct_is_ID(ptr->type)) { - UI_but_drag_set_id(but, ptr->id.data); + UI_but_drag_set_id(but, ptr->owner_id); } } } diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 6051bf5ca40..68d21e88211 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -27,7 +27,7 @@ #include "DNA_armature_types.h" #include "DNA_screen_types.h" -#include "DNA_text_types.h" /* for UI_OT_reports_to_text */ +#include "DNA_text_types.h" #include "DNA_object_types.h" /* for OB_DATA_SUPPORT_ID */ #include "BLI_blenlib.h" @@ -45,7 +45,7 @@ #include "BKE_node.h" #include "BKE_report.h" #include "BKE_screen.h" -#include "BKE_text.h" /* for UI_OT_reports_to_text */ +#include "BKE_text.h" #include "IMB_colormanagement.h" @@ -86,7 +86,7 @@ static bool copy_data_path_button_poll(bContext *C) UI_context_active_but_prop_get(C, &ptr, &prop, &index); - if (ptr.id.data && ptr.data && prop) { + if (ptr.owner_id && ptr.data && prop) { path = RNA_path_from_ID_to_property(&ptr, prop); if (path) { @@ -100,29 +100,33 @@ static bool copy_data_path_button_poll(bContext *C) static int copy_data_path_button_exec(bContext *C, wmOperator *op) { + Main *bmain = CTX_data_main(C); PointerRNA ptr; PropertyRNA *prop; char *path; int index; + ID *id; const bool full_path = RNA_boolean_get(op->ptr, "full_path"); /* try to create driver using property retrieved from UI */ UI_context_active_but_prop_get(C, &ptr, &prop, &index); - if (ptr.id.data != NULL) { - + if (ptr.owner_id != NULL) { if (full_path) { - if (prop) { - path = RNA_path_full_property_py_ex(&ptr, prop, index, true); + path = RNA_path_full_property_py_ex(bmain, &ptr, prop, index, true); } else { - path = RNA_path_full_struct_py(&ptr); + path = RNA_path_full_struct_py(bmain, &ptr); } } else { - path = RNA_path_from_ID_to_property(&ptr, prop); + path = RNA_path_from_real_ID_to_property_index(bmain, &ptr, prop, 0, -1, &id); + + if (!path) { + path = RNA_path_from_ID_to_property(&ptr, prop); + } } if (path) { @@ -171,7 +175,7 @@ static bool copy_as_driver_button_poll(bContext *C) UI_context_active_but_prop_get(C, &ptr, &prop, &index); - if (ptr.id.data && ptr.data && prop && + if (ptr.owner_id && ptr.data && prop && ELEM(RNA_property_type(prop), PROP_BOOLEAN, PROP_INT, PROP_FLOAT, PROP_ENUM) && (index >= 0 || !RNA_property_array_check(prop))) { path = RNA_path_from_ID_to_property(&ptr, prop); @@ -185,8 +189,9 @@ static bool copy_as_driver_button_poll(bContext *C) return 0; } -static int copy_as_driver_button_exec(bContext *C, wmOperator *UNUSED(op)) +static int copy_as_driver_button_exec(bContext *C, wmOperator *op) { + Main *bmain = CTX_data_main(C); PointerRNA ptr; PropertyRNA *prop; int index; @@ -194,15 +199,20 @@ static int copy_as_driver_button_exec(bContext *C, wmOperator *UNUSED(op)) /* try to create driver using property retrieved from UI */ UI_context_active_but_prop_get(C, &ptr, &prop, &index); - if (ptr.id.data && ptr.data && prop) { + if (ptr.owner_id && ptr.data && prop) { + ID *id; int dim = RNA_property_array_dimension(&ptr, prop, NULL); - char *path = RNA_path_from_ID_to_property_index(&ptr, prop, dim, index); + char *path = RNA_path_from_real_ID_to_property_index(bmain, &ptr, prop, dim, index, &id); if (path) { - ANIM_copy_as_driver(ptr.id.data, path, RNA_property_identifier(prop)); + ANIM_copy_as_driver(id, path, RNA_property_identifier(prop)); MEM_freeN(path); return OPERATOR_FINISHED; } + else { + BKE_reportf(op->reports, RPT_ERROR, "Could not compute a valid data path"); + return OPERATOR_CANCELLED; + } } return OPERATOR_CANCELLED; @@ -287,7 +297,7 @@ static void UI_OT_copy_python_command_button(wmOperatorType *ot) static int operator_button_property_finish(bContext *C, PointerRNA *ptr, PropertyRNA *prop) { - ID *id = ptr->id.data; + ID *id = ptr->owner_id; /* perform updates required for this property */ RNA_property_update(C, ptr, prop); @@ -542,7 +552,7 @@ static int override_type_set_button_exec(bContext *C, wmOperator *op) /* try to reset the nominated setting to its default value */ UI_context_active_but_prop_get(C, &ptr, &prop, &index); - BLI_assert(ptr.id.data != NULL); + BLI_assert(ptr.owner_id != NULL); if (all) { index = -1; @@ -605,7 +615,7 @@ static bool override_remove_button_poll(bContext *C) const int override_status = RNA_property_override_library_status(&ptr, prop, index); - return (ptr.data && ptr.id.data && prop && (override_status & RNA_OVERRIDE_STATUS_OVERRIDDEN)); + return (ptr.data && ptr.owner_id && prop && (override_status & RNA_OVERRIDE_STATUS_OVERRIDDEN)); } static int override_remove_button_exec(bContext *C, wmOperator *op) @@ -619,7 +629,7 @@ static int override_remove_button_exec(bContext *C, wmOperator *op) /* try to reset the nominated setting to its default value */ UI_context_active_but_prop_get(C, &ptr, &prop, &index); - ID *id = ptr.id.data; + ID *id = ptr.owner_id; IDOverrideLibraryProperty *oprop = RNA_property_override_property_find(&ptr, prop); BLI_assert(oprop != NULL); BLI_assert(id != NULL && id->override_library != NULL); @@ -629,8 +639,9 @@ static int override_remove_button_exec(bContext *C, wmOperator *op) /* We need source (i.e. linked data) to restore values of deleted overrides... * If this is an override template, we obviously do not need to restore anything. */ if (!is_template) { + PropertyRNA *src_prop; RNA_id_pointer_create(id->override_library->reference, &id_refptr); - if (!RNA_path_resolve(&id_refptr, oprop->rna_path, &src, NULL)) { + if (!RNA_path_resolve_property(&id_refptr, oprop->rna_path, &src, &src_prop)) { BLI_assert(0 && "Failed to create matching source (linked data) RNA pointer"); } } @@ -720,7 +731,7 @@ bool UI_context_copy_to_selected_list(bContext *C, CollectionPointerLink *link; for (link = lb.first; link; link = link->next) { bPoseChannel *pchan = link->ptr.data; - RNA_pointer_create(link->ptr.id.data, &RNA_Bone, pchan->bone, &link->ptr); + RNA_pointer_create(link->ptr.owner_id, &RNA_Bone, pchan->bone, &link->ptr); } } @@ -739,7 +750,7 @@ bool UI_context_copy_to_selected_list(bContext *C, /* Get the node we're editing */ if (RNA_struct_is_a(ptr->type, &RNA_NodeSocket)) { - bNodeTree *ntree = ptr->id.data; + bNodeTree *ntree = (bNodeTree *)ptr->owner_id; bNodeSocket *sock = ptr->data; if (nodeFindNode(ntree, sock, &node, NULL)) { if ((path = RNA_path_resolve_from_type_to_property(ptr, prop, &RNA_Node)) != NULL) { @@ -773,8 +784,8 @@ bool UI_context_copy_to_selected_list(bContext *C, *r_lb = lb; *r_path = path; } - else if (ptr->id.data) { - ID *id = ptr->id.data; + else if (ptr->owner_id) { + ID *id = ptr->owner_id; if (GS(id->name) == ID_OB) { *r_lb = CTX_data_collection_get(C, "selected_editable_objects"); @@ -792,7 +803,7 @@ bool UI_context_copy_to_selected_list(bContext *C, CollectionPointerLink *link, *link_next; for (link = lb.first; link; link = link->next) { - Object *ob = link->ptr.id.data; + Object *ob = (Object *)link->ptr.owner_id; if (ob->data) { ID *id_data = ob->data; id_data->tag |= LIB_TAG_DOIT; @@ -800,7 +811,7 @@ bool UI_context_copy_to_selected_list(bContext *C, } for (link = lb.first; link; link = link_next) { - Object *ob = link->ptr.id.data; + Object *ob = (Object *)link->ptr.owner_id; ID *id_data = ob->data; link_next = link->next; @@ -872,7 +883,7 @@ static bool copy_to_selected_button(bContext *C, bool all, bool poll) if (use_path_from_id) { /* Path relative to ID. */ lprop = NULL; - RNA_id_pointer_create(link->ptr.id.data, &idptr); + RNA_id_pointer_create(link->ptr.owner_id, &idptr); RNA_path_resolve_property(&idptr, path, &lptr, &lprop); } else if (path) { @@ -982,12 +993,12 @@ static bool jump_to_target_ptr(bContext *C, PointerRNA ptr, const bool poll) /* Find the containing Object. */ ViewLayer *view_layer = CTX_data_view_layer(C); Base *base = NULL; - const short id_type = GS(((ID *)ptr.id.data)->name); + const short id_type = GS(ptr.owner_id->name); if (id_type == ID_OB) { - base = BKE_view_layer_base_find(view_layer, ptr.id.data); + base = BKE_view_layer_base_find(view_layer, (Object *)ptr.owner_id); } else if (OB_DATA_SUPPORT_ID(id_type)) { - base = ED_object_find_first_by_data_id(view_layer, ptr.id.data); + base = ED_object_find_first_by_data_id(view_layer, ptr.owner_id); } bool ok = false; @@ -1096,60 +1107,6 @@ static void UI_OT_jump_to_target_button(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Reports to Textblock Operator - * \{ */ - -/* FIXME: this is just a temporary operator so that we can see all the reports somewhere - * when there are too many to display... - */ - -static bool reports_to_text_poll(bContext *C) -{ - return CTX_wm_reports(C) != NULL; -} - -static int reports_to_text_exec(bContext *C, wmOperator *UNUSED(op)) -{ - ReportList *reports = CTX_wm_reports(C); - Main *bmain = CTX_data_main(C); - Text *txt; - char *str; - - /* create new text-block to write to */ - txt = BKE_text_add(bmain, "Recent Reports"); - - /* convert entire list to a display string, and add this to the text-block - * - if commandline debug option enabled, show debug reports too - * - otherwise, up to info (which is what users normally see) - */ - str = BKE_reports_string(reports, (G.debug & G_DEBUG) ? RPT_DEBUG : RPT_INFO); - - if (str) { - BKE_text_write(txt, str); - MEM_freeN(str); - - return OPERATOR_FINISHED; - } - else { - return OPERATOR_CANCELLED; - } -} - -static void UI_OT_reports_to_textblock(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Reports to Text Block"; - ot->idname = "UI_OT_reports_to_textblock"; - ot->description = "Write the reports "; - - /* callbacks */ - ot->poll = reports_to_text_poll; - ot->exec = reports_to_text_exec; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name Edit Python Source Operator * \{ */ @@ -1750,7 +1707,6 @@ void ED_operatortypes_ui(void) WM_operatortype_append(UI_OT_override_remove_button); WM_operatortype_append(UI_OT_copy_to_selected_button); WM_operatortype_append(UI_OT_jump_to_target_button); - WM_operatortype_append(UI_OT_reports_to_textblock); /* XXX: temp? */ WM_operatortype_append(UI_OT_drop_color); #ifdef WITH_PYTHON WM_operatortype_append(UI_OT_editsource); diff --git a/source/blender/editors/interface/interface_region_popup.c b/source/blender/editors/interface/interface_region_popup.c index f3e18cc2f24..2073117d51c 100644 --- a/source/blender/editors/interface/interface_region_popup.c +++ b/source/blender/editors/interface/interface_region_popup.c @@ -690,9 +690,11 @@ uiBlock *ui_popup_block_refresh(bContext *C, /* Avoid menu moving down and losing cursor focus by keeping it at * the same height. */ if (handle->refresh && handle->prev_block_rect.ymax > block->rect.ymax) { - float offset = handle->prev_block_rect.ymax - block->rect.ymax; - UI_block_translate(block, 0, offset); - block->rect.ymin = handle->prev_block_rect.ymin; + if (block->bounds_type != UI_BLOCK_BOUNDS_POPUP_CENTER) { + float offset = handle->prev_block_rect.ymax - block->rect.ymax; + UI_block_translate(block, 0, offset); + block->rect.ymin = handle->prev_block_rect.ymin; + } } handle->prev_block_rect = block->rect; diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c index 6aad9e41e7d..7387fe5eb1c 100644 --- a/source/blender/editors/interface/interface_region_tooltip.c +++ b/source/blender/editors/interface/interface_region_tooltip.c @@ -765,8 +765,8 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) } } - if (but->rnapoin.id.data) { - const ID *id = but->rnapoin.id.data; + if (but->rnapoin.owner_id) { + const ID *id = but->rnapoin.owner_id; if (ID_IS_LINKED(id)) { uiTooltipField *field = text_field_add(data, &(uiTooltipFormat){ @@ -847,7 +847,7 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) } } - if (but->rnapoin.id.data) { + if (but->rnapoin.owner_id) { uiTooltipField *field = text_field_add(data, &(uiTooltipFormat){ .style = UI_TIP_STYLE_MONO, @@ -860,10 +860,10 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) /* move ownership (no need for re-alloc) */ if (but->rnaprop) { field->text = RNA_path_full_property_py_ex( - &but->rnapoin, but->rnaprop, but->rnaindex, true); + CTX_data_main(C), &but->rnapoin, but->rnaprop, but->rnaindex, true); } else { - field->text = RNA_path_full_struct_py(&but->rnapoin); + field->text = RNA_path_full_struct_py(CTX_data_main(C), &but->rnapoin); } } } diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 37eb1770f68..f53bef877c4 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -121,7 +121,7 @@ static void template_add_button_search_menu(const bContext *C, { PointerRNA active_ptr = RNA_property_pointer_get(ptr, prop); ID *id = (active_ptr.data && RNA_struct_is_ID(active_ptr.type)) ? active_ptr.data : NULL; - const ID *idfrom = ptr->id.data; + const ID *idfrom = ptr->owner_id; const StructRNA *type = active_ptr.type ? active_ptr.type : RNA_property_pointer_type(ptr, prop); uiBut *but; @@ -310,7 +310,7 @@ static bool id_search_add(const bContext *C, uiSearchItems *items, ID *id) { - ID *id_from = template_ui->ptr.id.data; + ID *id_from = template_ui->ptr.owner_id; if (!((flag & PROP_ID_SELF_CHECK) && id == id_from)) { @@ -401,7 +401,7 @@ static void id_search_cb_objects_from_scene(const bContext *C, TemplateID *template_ui = (TemplateID *)arg_template; ListBase *lb = template_ui->idlb; Scene *scene = NULL; - ID *id_from = template_ui->ptr.id.data; + ID *id_from = template_ui->ptr.owner_id; if (id_from && GS(id_from->name) == ID_SCE) { scene = (Scene *)id_from; @@ -538,7 +538,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) break; case UI_ID_OVERRIDE: if (id && id->override_library) { - BKE_override_library_free(&id->override_library); + BKE_override_library_free(&id->override_library, true); /* reassign to get get proper updates/notifiers */ idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop); RNA_property_pointer_set(&template_ui->ptr, template_ui->prop, idptr, NULL); @@ -667,7 +667,7 @@ static uiBut *template_id_def_new_but(uiBlock *block, const bool use_tab_but, int but_height) { - ID *idfrom = template_ui->ptr.id.data; + ID *idfrom = template_ui->ptr.owner_id; uiBut *but; const int w = id ? UI_UNIT_X : id_open ? UI_UNIT_X * 3 : UI_UNIT_X * 6; const int but_type = use_tab_but ? UI_BTYPE_TAB : UI_BTYPE_BUT; @@ -769,7 +769,7 @@ static void template_ID(bContext *C, idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop); id = idptr.data; - idfrom = template_ui->ptr.id.data; + idfrom = template_ui->ptr.owner_id; // lb = template_ui->idlb; block = uiLayoutGetBlock(layout); @@ -2037,7 +2037,7 @@ uiLayout *uiTemplateModifier(uiLayout *layout, bContext *C, PointerRNA *ptr) return NULL; } - ob = ptr->id.data; + ob = (Object *)ptr->owner_id; md = ptr->data; if (!ob || !(GS(ob->id.name) == ID_OB)) { @@ -2179,7 +2179,7 @@ uiLayout *uiTemplateGpencilModifier(uiLayout *layout, bContext *UNUSED(C), Point return NULL; } - ob = ptr->id.data; + ob = (Object *)ptr->owner_id; md = ptr->data; if (!ob || !(GS(ob->id.name) == ID_OB)) { @@ -2297,7 +2297,7 @@ uiLayout *uiTemplateShaderFx(uiLayout *layout, bContext *UNUSED(C), PointerRNA * return NULL; } - ob = ptr->id.data; + ob = (Object *)ptr->owner_id; fx = ptr->data; if (!ob || !(GS(ob->id.name) == ID_OB)) { @@ -2523,7 +2523,7 @@ static uiLayout *draw_constraint(uiLayout *layout, Object *ob, bConstraint *con) /* enabled */ UI_block_emboss_set(block, UI_EMBOSS_NONE); - uiItemR(row, &ptr, "mute", 0, "", (con->flag & CONSTRAINT_OFF) ? ICON_HIDE_ON : ICON_HIDE_OFF); + uiItemR(row, &ptr, "mute", 0, "", 0); UI_block_emboss_set(block, UI_EMBOSS); uiLayoutSetOperatorContext(row, WM_OP_INVOKE_DEFAULT); @@ -2579,7 +2579,7 @@ uiLayout *uiTemplateConstraint(uiLayout *layout, PointerRNA *ptr) return NULL; } - ob = ptr->id.data; + ob = (Object *)ptr->owner_id; con = ptr->data; if (!ob || !(GS(ob->id.name) == ID_OB)) { @@ -3098,7 +3098,7 @@ static void colorband_buttons_layout(uiLayout *layout, float ys = butr->ymin; PointerRNA ptr; - RNA_pointer_create(cb->ptr.id.data, &RNA_ColorRamp, coba, &ptr); + RNA_pointer_create(cb->ptr.owner_id, &RNA_ColorRamp, coba, &ptr); split = uiLayoutSplit(layout, 0.4f, false); @@ -3190,7 +3190,7 @@ static void colorband_buttons_layout(uiLayout *layout, if (coba->tot) { CBData *cbd = coba->data + coba->cur; - RNA_pointer_create(cb->ptr.id.data, &RNA_ColorRampElement, cbd, &ptr); + RNA_pointer_create(cb->ptr.owner_id, &RNA_ColorRampElement, cbd, &ptr); if (!expand) { split = uiLayoutSplit(layout, 0.3f, false); @@ -3207,7 +3207,7 @@ static void colorband_buttons_layout(uiLayout *layout, &coba->cur, 0.0, (float)(MAX2(0, coba->tot - 1)), - 0, + 1, 0, TIP_("Choose active color stop")); row = uiLayoutRow(split, false); @@ -3237,7 +3237,7 @@ static void colorband_buttons_layout(uiLayout *layout, &coba->cur, 0.0, (float)(MAX2(0, coba->tot - 1)), - 0, + 1, 0, TIP_("Choose active color stop")); row = uiLayoutRow(subsplit, false); @@ -3283,7 +3283,7 @@ void uiTemplateColorRamp(uiLayout *layout, PointerRNA *ptr, const char *propname block = uiLayoutAbsoluteBlock(layout); - id = cptr.id.data; + id = cptr.owner_id; UI_block_lock_set(block, (id && ID_IS_LINKED(id)), ERROR_LIBDATA_MESSAGE); colorband_buttons_layout(layout, block, cptr.data, &rect, cb, expand); @@ -3745,12 +3745,11 @@ static uiBlock *curvemap_clipping_func(bContext *C, ARegion *ar, void *cumap_v) float width = 8 * UI_UNIT_X; block = UI_block_begin(C, ar, __func__, UI_EMBOSS); - - /* use this for a fake extra empty space around the buttons */ - uiDefBut(block, UI_BTYPE_LABEL, 0, "", -4, 16, width + 8, 6 * UI_UNIT_Y, NULL, 0, 0, 0, 0, ""); + UI_block_flag_enable(block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_MOVEMOUSE_QUIT); + UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP); bt = uiDefButBitI(block, - UI_BTYPE_TOGGLE, + UI_BTYPE_CHECKBOX, CUMA_DO_CLIP, 1, IFACE_("Use Clipping"), @@ -3770,7 +3769,7 @@ static uiBlock *curvemap_clipping_func(bContext *C, ARegion *ar, void *cumap_v) uiDefButF(block, UI_BTYPE_NUM, 0, - IFACE_("Min X "), + IFACE_("Min X:"), 0, 4 * UI_UNIT_Y, width, @@ -3784,7 +3783,7 @@ static uiBlock *curvemap_clipping_func(bContext *C, ARegion *ar, void *cumap_v) uiDefButF(block, UI_BTYPE_NUM, 0, - IFACE_("Min Y "), + IFACE_("Min Y:"), 0, 3 * UI_UNIT_Y, width, @@ -3798,7 +3797,7 @@ static uiBlock *curvemap_clipping_func(bContext *C, ARegion *ar, void *cumap_v) uiDefButF(block, UI_BTYPE_NUM, 0, - IFACE_("Max X "), + IFACE_("Max X:"), 0, 2 * UI_UNIT_Y, width, @@ -3812,7 +3811,7 @@ static uiBlock *curvemap_clipping_func(bContext *C, ARegion *ar, void *cumap_v) uiDefButF(block, UI_BTYPE_NUM, 0, - IFACE_("Max Y "), + IFACE_("Max Y:"), 0, UI_UNIT_Y, width, @@ -3824,7 +3823,8 @@ static uiBlock *curvemap_clipping_func(bContext *C, ARegion *ar, void *cumap_v) 2, ""); - UI_block_direction_set(block, UI_DIR_RIGHT); + UI_block_bounds_set_normal(block, 0.3f * U.widget_unit); + UI_block_direction_set(block, UI_DIR_DOWN); return block; } @@ -4405,7 +4405,7 @@ void uiTemplateCurveMapping(uiLayout *layout, cb->ptr = *ptr; cb->prop = prop; - id = cptr.id.data; + id = cptr.owner_id; UI_block_lock_set(block, (id && ID_IS_LINKED(id)), ERROR_LIBDATA_MESSAGE); curvemap_buttons_layout(layout, &cptr, type, levels, brush, neg_slope, tone, cb); @@ -6378,7 +6378,7 @@ void uiTemplateReportsBanner(uiLayout *layout, bContext *C) if (reports->list.first != reports->list.last) { uiDefIconButO(block, UI_BTYPE_BUT, - "UI_OT_reports_to_textblock", + "SCREEN_OT_info_log_show", WM_OP_INVOKE_REGION_WIN, icon, 2, @@ -6392,22 +6392,17 @@ void uiTemplateReportsBanner(uiLayout *layout, bContext *C) block, UI_BTYPE_LABEL, 0, icon, 2, 0, UI_UNIT_X, UI_UNIT_Y, NULL, 0.0f, 0.0f, 0, 0, ""); } - UI_block_emboss_set(block, UI_EMBOSS); - - uiDefBut(block, - UI_BTYPE_LABEL, - 0, - report->message, - UI_UNIT_X + 5, - 0, - UI_UNIT_X + width, - UI_UNIT_Y, - NULL, - 0.0f, - 0.0f, - 0, - 0, - ""); + but = uiDefButO(block, + UI_BTYPE_BUT, + "SCREEN_OT_info_log_show", + WM_OP_INVOKE_REGION_WIN, + report->message, + UI_UNIT_X + 5, + 0, + UI_UNIT_X + width, + UI_UNIT_Y, + "Show in Info Log"); + rgba_float_to_uchar(but->col, rti->col); } void uiTemplateInputStatus(uiLayout *layout, struct bContext *C) @@ -6627,7 +6622,7 @@ static uiBlock *component_menu(bContext *C, ARegion *ar, void *args_v) uiItemR(layout, &args->ptr, args->propname, UI_ITEM_R_EXPAND, "", ICON_NONE); - UI_block_bounds_set_normal(block, 6); + UI_block_bounds_set_normal(block, 0.3f * U.widget_unit); UI_block_direction_set(block, UI_DIR_DOWN); return block; diff --git a/source/blender/editors/interface/interface_utils.c b/source/blender/editors/interface/interface_utils.c index 9c8787d002f..63e382e2280 100644 --- a/source/blender/editors/interface/interface_utils.c +++ b/source/blender/editors/interface/interface_utils.c @@ -405,7 +405,7 @@ void ui_rna_collection_search_cb(const struct bContext *C, RNA_PROP_BEGIN (&data->search_ptr, itemptr, data->search_prop) { if (flag & PROP_ID_SELF_CHECK) { - if (itemptr.data == data->target_ptr.id.data) { + if (itemptr.data == data->target_ptr.owner_id) { continue; } } @@ -566,7 +566,7 @@ int UI_calc_float_precision(int prec, double value) bool UI_but_online_manual_id(const uiBut *but, char *r_str, size_t maxlength) { - if (but->rnapoin.id.data && but->rnapoin.data && but->rnaprop) { + if (but->rnapoin.owner_id && but->rnapoin.data && but->rnaprop) { BLI_snprintf(r_str, maxlength, "%s.%s", diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 9c4d628cec5..8d22c5b85bb 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -2188,7 +2188,7 @@ static void widget_draw_text(const uiFontStyle *fstyle, drawstr_right = strrchr(drawstr + but->ofs, ':'); if (drawstr_right) { drawstr_right++; - drawstr_left_len = (drawstr_right - drawstr); + drawstr_left_len = (drawstr_right - drawstr - 1); while (*drawstr_right == ' ') { drawstr_right++; @@ -2587,6 +2587,10 @@ static void widget_state(uiWidgetType *wt, int state, int drawflag) } } else { + if (state & UI_BUT_ACTIVE_DEFAULT) { + copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel); + copy_v4_v4_uchar(wt->wcol.text, wt->wcol.text_sel); + } if (color_blend != NULL) { widget_state_blend(wt->wcol.inner, color_blend, wcol_state->blend); } @@ -2709,10 +2713,10 @@ static void widget_state_menu_item(uiWidgetType *wt, int state, int UNUSED(drawf /* active and disabled (not so common) */ if ((state & UI_BUT_DISABLED) && (state & UI_ACTIVE)) { - widget_state_blend(wt->wcol.text, wt->wcol.text_sel, 0.5f); /* draw the backdrop at low alpha, helps navigating with keys * when disabled items are active */ - copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel); + wt->wcol.text[3] = 128; + widget_state_blend(wt->wcol.inner, wt->wcol.text, 0.5f); wt->wcol.inner[3] = 64; } else { @@ -3268,20 +3272,6 @@ static void ui_draw_but_HSV_v(uiBut *but, const rcti *rect) ui_hsv_cursor(x, y); } -/* Generic round-box drawing. */ -static void ui_draw_roundbox(const rcti *rect, const float rad, const uiWidgetColors *wcol) -{ - uiWidgetBase wtb; - widget_init(&wtb); - round_box_edges(&wtb, UI_CNR_ALL, rect, rad); - widgetbase_draw(&wtb, wcol); - - /* We are drawing on top of widget bases. Flush cache. */ - GPU_blend(true); - UI_widgetbase_draw_cache_flush(); - GPU_blend(false); -} - /* ************ separator, for menus etc ***************** */ static void ui_draw_separator(const rcti *rect, const uiWidgetColors *wcol) { @@ -3802,7 +3792,7 @@ static void widget_swatch( widgetbase_draw_ex(&wtb, wcol, show_alpha_checkers); if (but->a1 == UI_PALETTE_COLOR && - ((Palette *)but->rnapoin.id.data)->active_color == (int)but->a2) { + ((Palette *)but->rnapoin.owner_id)->active_color == (int)but->a2) { float width = rect->xmax - rect->xmin; float height = rect->ymax - rect->ymin; /* find color luminance and change it slightly */ @@ -4497,10 +4487,6 @@ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rct const uiFontStyle *fstyle = &style->widget; uiWidgetType *wt = NULL; -#ifdef USE_UI_POPOVER_ONCE - const rcti rect_orig = *rect; -#endif - /* handle menus separately */ if (but->dt == UI_EMBOSS_PULLDOWN) { switch (but->type) { @@ -4786,6 +4772,14 @@ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rct ui_widget_color_disabled(wt); } +#ifdef USE_UI_POPOVER_ONCE + if (but->block->flag & UI_BLOCK_POPOVER_ONCE) { + if ((state & UI_ACTIVE) && ui_but_is_popover_once_compat(but)) { + state |= UI_BUT_ACTIVE_DEFAULT; + } + } +#endif + wt->state(wt, state, drawflag); if (wt->custom) { wt->custom(but, &wt->wcol, rect, state, roundboxalign); @@ -4798,42 +4792,10 @@ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rct GPU_blend(true); } - bool show_semi_highlight = false; - -#ifdef USE_UI_POPOVER_ONCE - if (but->block->flag & UI_BLOCK_POPOVER_ONCE) { - if ((state & UI_ACTIVE) && ui_but_is_popover_once_compat(but)) { - show_semi_highlight = true; - } - } -#endif - if (but->flag & UI_BUT_ACTIVE_DEFAULT) { - show_semi_highlight = true; - } - - if (show_semi_highlight) { - uiWidgetType wt_back = *wt; - uiWidgetType *wt_temp = widget_type(UI_WTYPE_MENU_ITEM); - wt_temp->state(wt_temp, state, drawflag); - copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel); - wt->wcol.inner[3] = 128; - wt->wcol.roundness = 0.5f; - ui_draw_roundbox(&rect_orig, - 0.25f * min_ff(BLI_rcti_size_x(&rect_orig), BLI_rcti_size_y(&rect_orig)), - &wt_temp->wcol); - *wt = wt_back; - } - wt->text(fstyle, &wt->wcol, but, rect); if (disabled) { GPU_blend(false); } - - // if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) { - // if (but->dt != UI_EMBOSS_PULLDOWN) { - // widget_disabled(&disablerect); - // } - // } } } diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c index 1e74ee50dab..93a93fb6918 100644 --- a/source/blender/editors/interface/resources.c +++ b/source/blender/editors/interface/resources.c @@ -794,6 +794,10 @@ const uchar *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colorid) cp = ts->selected_highlight; break; + case TH_SELECT_ACTIVE: + cp = ts->active; + break; + case TH_SELECTED_OBJECT: cp = ts->selected_object; break; diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c index f32fcffabd4..032fb7e4cc2 100644 --- a/source/blender/editors/interface/view2d_ops.c +++ b/source/blender/editors/interface/view2d_ops.c @@ -1844,7 +1844,7 @@ static void scroller_activate_init(bContext *C, */ scrollers = UI_view2d_scrollers_calc(v2d, NULL); - /* use a union of 'cur' & 'tot' incase the current view is far outside 'tot'. In this cases + /* Use a union of 'cur' & 'tot' in case the current view is far outside 'tot'. In this cases * moving the scroll bars has far too little effect and the view can get stuck T31476. */ tot_cur_union = v2d->tot; BLI_rctf_union(&tot_cur_union, &v2d->cur); diff --git a/source/blender/editors/mask/mask_shapekey.c b/source/blender/editors/mask/mask_shapekey.c index edc22943124..1abe805192d 100644 --- a/source/blender/editors/mask/mask_shapekey.c +++ b/source/blender/editors/mask/mask_shapekey.c @@ -81,6 +81,7 @@ void MASK_OT_shape_key_insert(wmOperatorType *ot) { /* identifiers */ ot->name = "Insert Shape Key"; + ot->description = "Insert mask shape keyframe for active mask layer at the current frame"; ot->idname = "MASK_OT_shape_key_insert"; /* api callbacks */ @@ -129,6 +130,7 @@ void MASK_OT_shape_key_clear(wmOperatorType *ot) { /* identifiers */ ot->name = "Clear Shape Key"; + ot->description = "Remove mask shape keyframe for active mask layer at the current frame"; ot->idname = "MASK_OT_shape_key_clear"; /* api callbacks */ diff --git a/source/blender/editors/mesh/CMakeLists.txt b/source/blender/editors/mesh/CMakeLists.txt index 9a779db4812..d7d020ae19d 100644 --- a/source/blender/editors/mesh/CMakeLists.txt +++ b/source/blender/editors/mesh/CMakeLists.txt @@ -43,6 +43,7 @@ set(SRC editface.c editmesh_add.c editmesh_add_gizmo.c + editmesh_automerge.c editmesh_bevel.c editmesh_bisect.c editmesh_extrude.c diff --git a/source/blender/editors/mesh/editmesh_automerge.c b/source/blender/editors/mesh/editmesh_automerge.c new file mode 100644 index 00000000000..82f53aafad8 --- /dev/null +++ b/source/blender/editors/mesh/editmesh_automerge.c @@ -0,0 +1,517 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2019 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup edmesh + * + * Utility functions for merging geometry once transform has finished: + * + * - #EDBM_automerge + * - #EDBM_automerge_and_split + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_bitmap.h" +#include "BLI_math.h" +#include "BLI_sort.h" + +#include "BKE_bvhutils.h" +#include "BKE_editmesh.h" + +#include "WM_api.h" + +#include "ED_mesh.h" +#include "ED_screen.h" +#include "ED_transform.h" + +#include "DNA_object_types.h" + +#include "DEG_depsgraph.h" + +/* use bmesh operator flags for a few operators */ +#define BMO_ELE_TAG 1 + +/* -------------------------------------------------------------------- */ +/** \name Auto-Merge Selection + * + * Used after transform operations. + * \{ */ + +void EDBM_automerge(Object *obedit, bool update, const char hflag, const float dist) +{ + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + int totvert_prev = bm->totvert; + + BMOperator findop, weldop; + + /* Search for doubles among all vertices, but only merge non-VERT_KEEP + * vertices into VERT_KEEP vertices. */ + BMO_op_initf(bm, + &findop, + BMO_FLAG_DEFAULTS, + "find_doubles verts=%av keep_verts=%Hv dist=%f", + hflag, + dist); + + BMO_op_exec(bm, &findop); + + /* weld the vertices */ + BMO_op_init(bm, &weldop, BMO_FLAG_DEFAULTS, "weld_verts"); + BMO_slot_copy(&findop, slots_out, "targetmap.out", &weldop, slots_in, "targetmap"); + BMO_op_exec(bm, &weldop); + + BMO_op_finish(bm, &findop); + BMO_op_finish(bm, &weldop); + + if ((totvert_prev != bm->totvert) && update) { + EDBM_update_generic(em, true, true); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Auto-Merge & Split Selection + * + * Used after transform operations. + * \{ */ + +struct EDBMSplitEdge { + BMVert *v; + BMEdge *e; + float lambda; +}; + +struct EDBMSplitBestFaceData { + BMEdge **edgenet; + int edgenet_len; + + /** + * Track the range of vertices in edgenet along the faces normal, + * find the lowest since it's most likely to be most co-planar with the face. + */ + float best_face_range_on_normal_axis; + BMFace *r_best_face; +}; + +struct EDBMSplitEdgeData { + BMesh *bm; + + BMEdge *r_edge; + float r_lambda; +}; + +static bool edbm_vert_pair_share_best_splittable_face_cb(BMFace *f, + BMLoop *UNUSED(l_a), + BMLoop *UNUSED(l_b), + void *userdata) +{ + struct EDBMSplitBestFaceData *data = userdata; + float no[3], min = FLT_MAX, max = -FLT_MAX; + copy_v3_v3(no, f->no); + + BMVert *verts[2] = {NULL}; + BMEdge **e_iter = &data->edgenet[0]; + for (int i = data->edgenet_len; i--; e_iter++) { + BMIter iter; + BMVert *v; + BM_ITER_ELEM (v, &iter, *e_iter, BM_VERTS_OF_EDGE) { + if (!ELEM(v, verts[0], verts[1])) { + float dot = dot_v3v3(v->co, no); + if (dot < min) { + min = dot; + } + if (dot > max) { + max = dot; + } + } + } + verts[0] = (*e_iter)->v1; + verts[1] = (*e_iter)->v2; + } + + const float test_face_range_on_normal_axis = max - min; + if (test_face_range_on_normal_axis < data->best_face_range_on_normal_axis) { + data->best_face_range_on_normal_axis = test_face_range_on_normal_axis; + data->r_best_face = f; + } + + return false; +} + +/* find the best splittable face between the two vertices. */ +static bool edbm_vert_pair_share_splittable_face_cb(BMFace *UNUSED(f), + BMLoop *l_a, + BMLoop *l_b, + void *userdata) +{ + float(*data)[3] = userdata; + float *v_a_co = data[0]; + float *v_a_b_dir = data[1]; + + float lambda; + if (isect_ray_seg_v3(v_a_co, v_a_b_dir, l_a->prev->v->co, l_a->next->v->co, &lambda)) { + if (IN_RANGE(lambda, 0.0f, 1.0f)) { + return true; + } + else if (isect_ray_seg_v3(v_a_co, v_a_b_dir, l_b->prev->v->co, l_b->next->v->co, &lambda)) { + return IN_RANGE(lambda, 0.0f, 1.0f); + } + } + return false; +} + +static void edbm_automerge_weld_linked_wire_edges_into_linked_faces(BMesh *bm, + BMVert *v, + BMEdge **r_edgenet[], + int *r_edgenet_alloc_len) +{ + BMEdge **edgenet = *r_edgenet; + int edgenet_alloc_len = *r_edgenet_alloc_len; + + BMIter iter; + BMEdge *e; + BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) { + int edgenet_len = 0; + BMVert *v_other = v; + while (BM_edge_is_wire(e)) { + if (edgenet_alloc_len == edgenet_len) { + edgenet_alloc_len = (edgenet_alloc_len + 1) * 2; + edgenet = MEM_reallocN(edgenet, (edgenet_alloc_len) * sizeof(*edgenet)); + } + edgenet[edgenet_len++] = e; + v_other = BM_edge_other_vert(e, v_other); + if (v_other == v) { + /* Endless loop. */ + break; + } + + BMEdge *e_next = BM_DISK_EDGE_NEXT(e, v_other); + if (e_next == e) { + /* Vert is wire_endpoint */ + edgenet_len = 0; + break; + } + e = e_next; + } + + BMLoop *dummy; + BMFace *best_face; + if (edgenet_len == 0) { + /* Nothing to do. */ + continue; + } + if (edgenet_len == 1) { + float data[2][3]; + copy_v3_v3(data[0], v_other->co); + sub_v3_v3v3(data[1], v->co, data[0]); + best_face = BM_vert_pair_shared_face_cb( + v_other, v, true, edbm_vert_pair_share_splittable_face_cb, &data, &dummy, &dummy); + } + else { + struct EDBMSplitBestFaceData data = { + .edgenet = edgenet, + .edgenet_len = edgenet_len, + .best_face_range_on_normal_axis = FLT_MAX, + .r_best_face = NULL, + }; + BM_vert_pair_shared_face_cb( + v_other, v, true, edbm_vert_pair_share_best_splittable_face_cb, &data, &dummy, &dummy); + + best_face = data.r_best_face; + } + + if (best_face) { + BM_face_split_edgenet(bm, best_face, edgenet, edgenet_len, NULL, NULL); + } + } + + *r_edgenet = edgenet; + *r_edgenet_alloc_len = edgenet_alloc_len; +} + +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(Object *obedit, + bool split_edges, + bool split_faces, + bool update, + const char hflag, + const float dist) +{ + bool ok = false; + + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + BMOperator findop, weldop; + BMOpSlot *slot_targetmap; + 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); + + 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); + } + } + + if (edges_len) { + /* Use `e->head.index` to count intersections. */ + bm->elem_index_dirty &= ~BM_EDGE; + + /* 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); + + 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[0]; + } * 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]; + 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 e_cuts_len = e->head.index; + + e_map_iter = (void *)&e_map->as_int[map_len]; + e_map_iter->cuts_len = e_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 + e_cuts_len; + } + else { + e_map->as_int[++e->head.index] = i; + } + } + + /* Split Edges and Faces. */ + 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); + + 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); + + BMEdge **edgenet = NULL; + int edgenet_alloc_len = 0; + if (split_faces) { + GHASH_ITER (gh_iter, ghash_targetmap) { + v = BLI_ghashIterator_getValue(&gh_iter); + BLI_assert(BM_elem_flag_test(v, hflag) || hflag == BM_ELEM_TAG); + edbm_automerge_weld_linked_wire_edges_into_linked_faces(bm, v, &edgenet, &edgenet_alloc_len); + } + } + + if (edgenet) { + MEM_freeN(edgenet); + } + + BMO_op_finish(bm, &findop); + BMO_op_finish(bm, &weldop); + + if (LIKELY(ok) && update) { + EDBM_update_generic(em, true, true); + } +} + +/** \} */ diff --git a/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c b/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c index 74700e59e99..7155348fed5 100644 --- a/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c +++ b/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c @@ -1008,8 +1008,17 @@ static void gizmo_mesh_spin_redo_setup(const bContext *C, wmGizmoGroup *gzgroup) }); } - /* Become modal as soon as it's started. */ - gizmo_mesh_spin_redo_modal_from_setup(C, gzgroup); + wmWindow *win = CTX_wm_window(C); + if (win && win->active) { + bScreen *screen = WM_window_get_active_screen(win); + if (screen->active_region) { + ARegion *ar = CTX_wm_region(C); + if (screen->active_region == ar) { + /* Become modal as soon as it's started. */ + gizmo_mesh_spin_redo_modal_from_setup(C, gzgroup); + } + } + } } static void gizmo_mesh_spin_redo_draw_prepare(const bContext *UNUSED(C), wmGizmoGroup *gzgroup) diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index bb584094580..61f9dc43c0f 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -2658,7 +2658,7 @@ static void knifetool_init_bmbvh(KnifeTool_OpData *kcd) Object *obedit_eval = (Object *)DEG_get_evaluated_id(kcd->vc.depsgraph, &kcd->em->ob->id); BMEditMesh *em_eval = BKE_editmesh_from_object(obedit_eval); - kcd->cagecos = (const float(*)[3])BKE_editmesh_vertexCos_get( + kcd->cagecos = (const float(*)[3])BKE_editmesh_vert_coords_alloc( kcd->vc.depsgraph, em_eval, scene_eval, NULL); kcd->bmbvh = BKE_bmbvh_new_from_editmesh( @@ -2949,7 +2949,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) case KNF_MODAL_ADD_CUT_CLOSED: if (kcd->mode == MODE_DRAGGING) { - /* shouldn't be possible with default key-layout, just incase... */ + /* Shouldn't be possible with default key-layout, just in case. */ if (kcd->is_drag_hold) { kcd->is_drag_hold = false; knifetool_update_mval(kcd, kcd->curr.mval); diff --git a/source/blender/editors/mesh/editmesh_polybuild.c b/source/blender/editors/mesh/editmesh_polybuild.c index 088d1672cc9..21c850160dd 100644 --- a/source/blender/editors/mesh/editmesh_polybuild.c +++ b/source/blender/editors/mesh/editmesh_polybuild.c @@ -122,15 +122,160 @@ static bool edbm_preselect_or_active_init_viewcontext(bContext *C, return ok; } +static int edbm_polybuild_transform_at_cursor_invoke(bContext *C, + wmOperator *UNUSED(op), + const wmEvent *UNUSED(event)) +{ + ViewContext vc; + Base *basact = NULL; + BMElem *ele_act = NULL; + edbm_preselect_or_active_init_viewcontext(C, &vc, &basact, &ele_act); + BMEditMesh *em = vc.em; + BMesh *bm = em->bm; + + invert_m4_m4(vc.obedit->imat, vc.obedit->obmat); + ED_view3d_init_mats_rv3d(vc.obedit, vc.rv3d); + + if (!ele_act) { + return OPERATOR_CANCELLED; + } + + edbm_selectmode_ensure(vc.scene, vc.em, SCE_SELECT_VERTEX); + + edbm_flag_disable_all_multi(vc.view_layer, vc.v3d, BM_ELEM_SELECT); + + if (ele_act->head.htype == BM_VERT) { + BM_vert_select_set(bm, (BMVert *)ele_act, true); + } + if (ele_act->head.htype == BM_EDGE) { + BM_edge_select_set(bm, (BMEdge *)ele_act, true); + } + if (ele_act->head.htype == BM_FACE) { + BM_face_select_set(bm, (BMFace *)ele_act, true); + } + + EDBM_mesh_normals_update(em); + EDBM_update_generic(em, true, true); + if (basact != NULL) { + if (vc.view_layer->basact != basact) { + ED_object_base_activate(C, basact); + } + } + BM_select_history_store(bm, ele_act); + WM_event_add_mousemove(C); + return OPERATOR_FINISHED; +} + +void MESH_OT_polybuild_transform_at_cursor(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Poly Build Transform at Cursor"; + ot->idname = "MESH_OT_polybuild_transform_at_cursor"; + + /* api callbacks */ + ot->invoke = edbm_polybuild_transform_at_cursor_invoke; + ot->poll = EDBM_view3d_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* to give to transform */ + Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR_DUMMY); +} + +static int edbm_polybuild_delete_at_cursor_invoke(bContext *C, + wmOperator *op, + const wmEvent *UNUSED(event)) +{ + bool changed = false; + + ViewContext vc; + Base *basact = NULL; + BMElem *ele_act = NULL; + edbm_preselect_or_active_init_viewcontext(C, &vc, &basact, &ele_act); + BMEditMesh *em = vc.em; + BMesh *bm = em->bm; + + invert_m4_m4(vc.obedit->imat, vc.obedit->obmat); + ED_view3d_init_mats_rv3d(vc.obedit, vc.rv3d); + + if (!ele_act) { + return OPERATOR_CANCELLED; + } + + edbm_selectmode_ensure(vc.scene, vc.em, SCE_SELECT_VERTEX); + + if (ele_act->head.htype == BM_FACE) { + BMFace *f_act = (BMFace *)ele_act; + EDBM_flag_disable_all(em, BM_ELEM_TAG); + BM_elem_flag_enable(f_act, BM_ELEM_TAG); + if (!EDBM_op_callf(em, op, "delete geom=%hf context=%i", BM_ELEM_TAG, DEL_FACES)) { + return OPERATOR_CANCELLED; + } + changed = true; + } + if (ele_act->head.htype == BM_VERT) { + BMVert *v_act = (BMVert *)ele_act; + if (BM_vert_is_edge_pair(v_act)) { + BM_edge_collapse(bm, v_act->e, v_act, true, true); + changed = true; + } + else { + EDBM_flag_disable_all(em, BM_ELEM_TAG); + BM_elem_flag_enable(v_act, BM_ELEM_TAG); + + if (!EDBM_op_callf(em, + op, + "dissolve_verts verts=%hv use_face_split=%b use_boundary_tear=%b", + BM_ELEM_TAG, + false, + false)) { + return OPERATOR_CANCELLED; + } + changed = true; + } + } + + if (changed) { + EDBM_mesh_normals_update(em); + EDBM_update_generic(em, true, true); + if (basact != NULL) { + if (vc.view_layer->basact != basact) { + ED_object_base_activate(C, basact); + } + } + WM_event_add_mousemove(C); + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } +} + +void MESH_OT_polybuild_delete_at_cursor(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Poly Build Delete at Cursor"; + ot->idname = "MESH_OT_polybuild_delete_at_cursor"; + + /* api callbacks */ + ot->invoke = edbm_polybuild_delete_at_cursor_invoke; + ot->poll = EDBM_view3d_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* to give to transform */ + Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR_DUMMY); +} + /** \} */ /* -------------------------------------------------------------------- */ /** \name Face at Cursor * \{ */ -static int edbm_polybuild_face_at_cursor_invoke(bContext *C, - wmOperator *UNUSED(op), - const wmEvent *event) +static int edbm_polybuild_face_at_cursor_invoke(bContext *C, wmOperator *op, const wmEvent *event) { float center[3]; bool changed = false; @@ -168,20 +313,27 @@ static int edbm_polybuild_face_at_cursor_invoke(bContext *C, mul_m4_v3(vc.obedit->obmat, center); ED_view3d_win_to_3d_int(vc.v3d, vc.ar, center, event->mval, center); mul_m4_v3(vc.obedit->imat, center); - - BMVert *v_tri[3]; - v_tri[0] = e_act->v1; - v_tri[1] = e_act->v2; - v_tri[2] = BM_vert_create(bm, center, NULL, BM_CREATE_NOP); - if (e_act->l && e_act->l->v == v_tri[0]) { - SWAP(BMVert *, v_tri[0], v_tri[1]); + if (f_reference->len == 3 && RNA_boolean_get(op->ptr, "create_quads")) { + const float fac = line_point_factor_v3(center, e_act->v1->co, e_act->v2->co); + BMVert *v_new = BM_edge_split(bm, e_act, e_act->v1, NULL, CLAMPIS(fac, 0.0f, 1.0f)); + copy_v3_v3(v_new->co, center); + edbm_flag_disable_all_multi(vc.view_layer, vc.v3d, BM_ELEM_SELECT); + BM_vert_select_set(bm, v_new, true); + BM_select_history_store(bm, v_new); + } + else { + BMVert *v_tri[3]; + v_tri[0] = e_act->v1; + v_tri[1] = e_act->v2; + v_tri[2] = BM_vert_create(bm, center, NULL, BM_CREATE_NOP); + if (e_act->l && e_act->l->v == v_tri[0]) { + SWAP(BMVert *, v_tri[0], v_tri[1]); + } + BM_face_create_verts(bm, v_tri, 3, f_reference, BM_CREATE_NOP, true); + edbm_flag_disable_all_multi(vc.view_layer, vc.v3d, BM_ELEM_SELECT); + BM_vert_select_set(bm, v_tri[2], true); + BM_select_history_store(bm, v_tri[2]); } - // BMFace *f_new = - BM_face_create_verts(bm, v_tri, 3, f_reference, BM_CREATE_NOP, true); - - edbm_flag_disable_all_multi(vc.view_layer, vc.v3d, BM_ELEM_SELECT); - BM_vert_select_set(bm, v_tri[2], true); - BM_select_history_store(bm, v_tri[2]); changed = true; } else if (ele_act->head.htype == BM_VERT) { @@ -281,6 +433,11 @@ void MESH_OT_polybuild_face_at_cursor(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + RNA_def_boolean(ot->srna, + "create_quads", + true, + "Create quads", + "Automatically split edges in triangles to maintain quad topology"); /* to give to transform */ Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR_DUMMY); } diff --git a/source/blender/editors/mesh/editmesh_preselect_elem.c b/source/blender/editors/mesh/editmesh_preselect_elem.c index a3e684a5493..05c4da68355 100644 --- a/source/blender/editors/mesh/editmesh_preselect_elem.c +++ b/source/blender/editors/mesh/editmesh_preselect_elem.c @@ -75,20 +75,49 @@ struct EditMesh_PreSelElem { float (*verts)[3]; int verts_len; + + float (*preview_tris)[3][3]; + int preview_tris_len; + float (*preview_lines)[2][3]; + int preview_lines_len; + + eEditMesh_PreSelPreviewAction preview_action; }; +void EDBM_preselect_action_set(struct EditMesh_PreSelElem *psel, + eEditMesh_PreSelPreviewAction action) +{ + psel->preview_action = action; +} + +eEditMesh_PreSelPreviewAction EDBM_preselect_action_get(struct EditMesh_PreSelElem *psel) +{ + return psel->preview_action; +} + struct EditMesh_PreSelElem *EDBM_preselect_elem_create(void) { struct EditMesh_PreSelElem *psel = MEM_callocN(sizeof(*psel), __func__); + psel->preview_action = PRESELECT_ACTION_TRANSFORM; return psel; } void EDBM_preselect_elem_destroy(struct EditMesh_PreSelElem *psel) { EDBM_preselect_elem_clear(psel); + EDBM_preselect_preview_clear(psel); MEM_freeN(psel); } +void EDBM_preselect_preview_clear(struct EditMesh_PreSelElem *psel) +{ + MEM_SAFE_FREE(psel->preview_tris); + psel->preview_tris_len = 0; + + MEM_SAFE_FREE(psel->preview_lines); + psel->preview_lines_len = 0; +} + void EDBM_preselect_elem_clear(struct EditMesh_PreSelElem *psel) { MEM_SAFE_FREE(psel->edges); @@ -112,9 +141,42 @@ void EDBM_preselect_elem_draw(struct EditMesh_PreSelElem *psel, const float matr uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); - immUniformColor3ub(255, 0, 255); + + immUniformColor4ub(141, 171, 186, 100); + if (psel->preview_action != PRESELECT_ACTION_TRANSFORM) { + if (psel->preview_tris_len > 0) { + immBegin(GPU_PRIM_TRIS, psel->preview_tris_len * 3); + + for (int i = 0; i < psel->preview_tris_len; i++) { + immVertex3fv(pos, psel->preview_tris[i][0]); + immVertex3fv(pos, psel->preview_tris[i][1]); + immVertex3fv(pos, psel->preview_tris[i][2]); + } + immEnd(); + } + + if (psel->preview_lines_len > 0) { + + immUniformColor4ub(3, 161, 252, 200); + GPU_line_width(2.0f); + immBegin(GPU_PRIM_LINES, psel->preview_lines_len * 2); + for (int i = 0; i < psel->preview_lines_len; i++) { + immVertex3fv(pos, psel->preview_lines[i][0]); + immVertex3fv(pos, psel->preview_lines[i][1]); + } + immEnd(); + } + } + + if (psel->preview_action == PRESELECT_ACTION_DELETE) { + immUniformColor4ub(252, 49, 10, 200); + } + else { + immUniformColor4ub(3, 161, 252, 200); + } if (psel->edges_len > 0) { + GPU_line_width(3.0f); immBegin(GPU_PRIM_LINES, psel->edges_len * 2); for (int i = 0; i < psel->edges_len; i++) { @@ -126,7 +188,7 @@ void EDBM_preselect_elem_draw(struct EditMesh_PreSelElem *psel, const float matr } if (psel->verts_len > 0) { - GPU_point_size(3.0f); + GPU_point_size(4.0f); immBegin(GPU_PRIM_POINTS, psel->verts_len); @@ -167,6 +229,122 @@ static void view3d_preselect_mesh_elem_update_from_edge(struct EditMesh_PreSelEl psel->edges_len = 1; } +static void view3d_preselect_update_preview_triangle_from_vert(struct EditMesh_PreSelElem *psel, + ViewContext *vc, + BMesh *UNUSED(bm), + BMVert *eed, + const int mval[2]) +{ + BMVert *v_act = eed; + BMEdge *e_pair[2] = {NULL}; + float center[3]; + + if (v_act->e != NULL) { + for (uint allow_wire = 0; allow_wire < 2 && (e_pair[1] == NULL); allow_wire++) { + int i = 0; + BMEdge *e_iter = v_act->e; + do { + if ((BM_elem_flag_test(e_iter, BM_ELEM_HIDDEN) == false) && + (allow_wire ? BM_edge_is_wire(e_iter) : BM_edge_is_boundary(e_iter))) { + if (i == 2) { + e_pair[0] = e_pair[1] = NULL; + break; + } + e_pair[i++] = e_iter; + } + } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v_act)) != v_act->e); + } + } + + if (e_pair[1] != NULL) { + mul_v3_m4v3(center, vc->obedit->obmat, v_act->co); + ED_view3d_win_to_3d_int(vc->v3d, vc->ar, center, mval, center); + mul_m4_v3(vc->obedit->imat, center); + + psel->preview_tris = MEM_mallocN(sizeof(*psel->preview_tris) * 2, __func__); + psel->preview_lines = MEM_mallocN(sizeof(*psel->preview_lines) * 4, __func__); + + copy_v3_v3(psel->preview_tris[0][0], e_pair[0]->v1->co); + copy_v3_v3(psel->preview_tris[0][1], e_pair[0]->v2->co); + copy_v3_v3(psel->preview_tris[0][2], center); + + copy_v3_v3(psel->preview_tris[1][0], e_pair[1]->v1->co); + copy_v3_v3(psel->preview_tris[1][1], e_pair[1]->v2->co); + copy_v3_v3(psel->preview_tris[1][2], center); + + copy_v3_v3(psel->preview_lines[0][0], e_pair[0]->v1->co); + copy_v3_v3(psel->preview_lines[0][1], e_pair[0]->v2->co); + + copy_v3_v3(psel->preview_lines[1][0], e_pair[1]->v1->co); + copy_v3_v3(psel->preview_lines[1][1], e_pair[1]->v2->co); + + copy_v3_v3(psel->preview_lines[2][0], center); + if (e_pair[0]->v1 == v_act) { + copy_v3_v3(psel->preview_lines[2][1], e_pair[0]->v2->co); + } + else { + copy_v3_v3(psel->preview_lines[2][1], e_pair[0]->v1->co); + } + + copy_v3_v3(psel->preview_lines[3][0], center); + if (e_pair[1]->v1 == v_act) { + copy_v3_v3(psel->preview_lines[3][1], e_pair[1]->v2->co); + } + else { + copy_v3_v3(psel->preview_lines[3][1], e_pair[1]->v1->co); + } + psel->preview_tris_len = 2; + psel->preview_lines_len = 4; + } +} + +static void view3d_preselect_update_preview_triangle_from_face(struct EditMesh_PreSelElem *psel, + ViewContext *UNUSED(vc), + BMesh *UNUSED(bm), + BMFace *efa, + const int UNUSED(mval[2])) +{ + float(*preview_lines)[2][3] = MEM_mallocN(sizeof(*psel->edges) * efa->len, __func__); + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(efa); + int i = 0; + do { + vcos_get_pair(&l_iter->e->v1, preview_lines[i++], NULL); + } while ((l_iter = l_iter->next) != l_first); + psel->preview_lines = preview_lines; + psel->preview_lines_len = efa->len; +} + +static void view3d_preselect_update_preview_triangle_from_edge(struct EditMesh_PreSelElem *psel, + ViewContext *vc, + BMesh *UNUSED(bm), + BMEdge *eed, + const int mval[2]) +{ + float center[3]; + psel->preview_tris = MEM_mallocN(sizeof(*psel->preview_tris), __func__); + psel->preview_lines = MEM_mallocN(sizeof(*psel->preview_lines) * 3, __func__); + mid_v3_v3v3(center, eed->v1->co, eed->v2->co); + mul_m4_v3(vc->obedit->obmat, center); + ED_view3d_win_to_3d_int(vc->v3d, vc->ar, center, mval, center); + mul_m4_v3(vc->obedit->imat, center); + + copy_v3_v3(psel->preview_tris[0][0], eed->v1->co); + copy_v3_v3(psel->preview_tris[0][1], eed->v2->co); + copy_v3_v3(psel->preview_tris[0][2], center); + + copy_v3_v3(psel->preview_lines[0][0], eed->v1->co); + copy_v3_v3(psel->preview_lines[0][1], eed->v2->co); + + copy_v3_v3(psel->preview_lines[1][0], eed->v2->co); + copy_v3_v3(psel->preview_lines[1][1], center); + + copy_v3_v3(psel->preview_lines[2][0], center); + copy_v3_v3(psel->preview_lines[2][1], eed->v1->co); + psel->preview_tris_len = 1; + psel->preview_lines_len = 3; +} + static void view3d_preselect_mesh_elem_update_from_face(struct EditMesh_PreSelElem *psel, BMesh *UNUSED(bm), BMFace *efa, @@ -209,4 +387,28 @@ void EDBM_preselect_elem_update_from_single(struct EditMesh_PreSelElem *psel, } } +void EDBM_preselect_elem_update_preview(struct EditMesh_PreSelElem *psel, + struct ViewContext *vc, + struct BMesh *bm, + struct BMElem *ele, + const int mval[2]) +{ + EDBM_preselect_preview_clear(psel); + + switch (ele->head.htype) { + case BM_VERT: + if (EDBM_preselect_action_get(psel) == PRESELECT_ACTION_CREATE) { + view3d_preselect_update_preview_triangle_from_vert(psel, vc, bm, (BMVert *)ele, mval); + } + break; + case BM_EDGE: + view3d_preselect_update_preview_triangle_from_edge(psel, vc, bm, (BMEdge *)ele, mval); + break; + case BM_FACE: + view3d_preselect_update_preview_triangle_from_face(psel, vc, bm, (BMFace *)ele, mval); + break; + default: + BLI_assert(0); + } +} /** \} */ diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index 94ffd9a34d6..c0bd9e9f14c 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -24,7 +24,6 @@ #include "MEM_guardedalloc.h" #include "BLI_bitmap.h" -#include "BLI_bitmap_draw_2d.h" #include "BLI_listbase.h" #include "BLI_linklist.h" #include "BLI_linklist_stack.h" @@ -35,13 +34,9 @@ #include "BKE_context.h" #include "BKE_report.h" -#include "BKE_paint.h" #include "BKE_editmesh.h" #include "BKE_layer.h" -#include "IMB_imbuf_types.h" -#include "IMB_imbuf.h" - #include "WM_api.h" #include "WM_types.h" @@ -67,7 +62,6 @@ #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" -#include "DRW_engine.h" #include "DRW_select_buffer.h" #include "mesh_intern.h" /* own include */ @@ -172,30 +166,6 @@ void EDBM_select_mirrored( /** \} */ /* -------------------------------------------------------------------- */ -/** \name Select Auto-Merge - * - * Used after transform operations. - * \{ */ - -void EDBM_automerge(Scene *scene, Object *obedit, bool update, const char hflag) -{ - bool ok; - BMEditMesh *em = BKE_editmesh_from_object(obedit); - - ok = BMO_op_callf(em->bm, - BMO_FLAG_DEFAULTS, - "automerge verts=%hv dist=%f", - hflag, - scene->toolsettings->doublimit); - - if (LIKELY(ok) && update) { - EDBM_update_generic(em, true, true); - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name Back-Buffer OpenGL Selection * \{ */ @@ -1031,8 +1001,11 @@ bool EDBM_unified_findnearest(ViewContext *vc, bool EDBM_unified_findnearest_from_raycast(ViewContext *vc, Base **bases, const uint bases_len, - bool use_boundary, - int *r_base_index, + bool use_boundary_vertices, + bool use_boundary_edges, + int *r_base_index_vert, + int *r_base_index_edge, + int *r_base_index_face, struct BMVert **r_eve, struct BMEdge **r_eed, struct BMFace **r_efa) @@ -1045,10 +1018,30 @@ bool EDBM_unified_findnearest_from_raycast(ViewContext *vc, uint base_index; BMElem *ele; } best = {0, NULL}; + /* Currently unused, keep since we may want to pick the best. */ + UNUSED_VARS(best); + + struct { + uint base_index; + BMElem *ele; + } best_vert = {0, NULL}; + + struct { + uint base_index; + BMElem *ele; + } best_edge = {0, NULL}; + + struct { + uint base_index; + BMElem *ele; + } best_face = {0, NULL}; if (ED_view3d_win_to_ray_clipped( vc->depsgraph, vc->ar, vc->v3d, mval_fl, ray_origin, ray_direction, true)) { float dist_sq_best = FLT_MAX; + float dist_sq_best_vert = FLT_MAX; + float dist_sq_best_edge = FLT_MAX; + float dist_sq_best_face = FLT_MAX; const bool use_vert = (r_eve != NULL); const bool use_edge = (r_eed != NULL); @@ -1078,18 +1071,23 @@ bool EDBM_unified_findnearest_from_raycast(ViewContext *vc, BM_mesh_elem_index_ensure(bm, BM_VERT); } - if (use_boundary && (use_vert || use_edge)) { + if ((use_boundary_vertices || use_boundary_edges) && (use_vert || use_edge)) { BMEdge *e; BMIter eiter; BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) { if ((BM_elem_flag_test(e, BM_ELEM_HIDDEN) == false) && (BM_edge_is_boundary(e))) { - if (use_vert) { + if (use_vert && use_boundary_vertices) { for (uint j = 0; j < 2; j++) { BMVert *v = *((&e->v1) + j); float point[3]; mul_v3_m4v3(point, obedit->obmat, coords ? coords[BM_elem_index_get(v)] : v->co); const float dist_sq_test = dist_squared_to_ray_v3_normalized( ray_origin, ray_direction, point); + if (dist_sq_test < dist_sq_best_vert) { + dist_sq_best_vert = dist_sq_test; + best_vert.base_index = base_index; + best_vert.ele = (BMElem *)v; + } if (dist_sq_test < dist_sq_best) { dist_sq_best = dist_sq_test; best.base_index = base_index; @@ -1098,7 +1096,7 @@ bool EDBM_unified_findnearest_from_raycast(ViewContext *vc, } } - if (use_edge) { + if (use_edge && use_boundary_edges) { float point[3]; #if 0 const float dist_sq_test = dist_squared_ray_to_seg_v3( @@ -1114,6 +1112,11 @@ bool EDBM_unified_findnearest_from_raycast(ViewContext *vc, mul_m4_v3(obedit->obmat, point); const float dist_sq_test = dist_squared_to_ray_v3_normalized( ray_origin, ray_direction, point); + if (dist_sq_test < dist_sq_best_edge) { + dist_sq_best_edge = dist_sq_test; + best_edge.base_index = base_index; + best_edge.ele = (BMElem *)e; + } if (dist_sq_test < dist_sq_best) { dist_sq_best = dist_sq_test; best.base_index = base_index; @@ -1124,46 +1127,55 @@ bool EDBM_unified_findnearest_from_raycast(ViewContext *vc, } } } - else { - /* Non boundary case. */ - if (use_vert) { - BMVert *v; - BMIter viter; - BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) { - if (BM_elem_flag_test(v, BM_ELEM_HIDDEN) == false) { - float point[3]; - mul_v3_m4v3(point, obedit->obmat, v->co); - const float dist_sq_test = dist_squared_to_ray_v3_normalized( - ray_origin, ray_direction, v->co); - if (dist_sq_test < dist_sq_best) { - dist_sq_best = dist_sq_test; - best.base_index = base_index; - best.ele = (BMElem *)v; - } + /* Non boundary case. */ + if (use_vert && !use_boundary_vertices) { + BMVert *v; + BMIter viter; + BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) { + if (BM_elem_flag_test(v, BM_ELEM_HIDDEN) == false) { + float point[3]; + mul_v3_m4v3(point, obedit->obmat, coords ? coords[BM_elem_index_get(v)] : v->co); + const float dist_sq_test = dist_squared_to_ray_v3_normalized( + ray_origin, ray_direction, point); + if (dist_sq_test < dist_sq_best_vert) { + dist_sq_best_vert = dist_sq_test; + best_vert.base_index = base_index; + best_vert.ele = (BMElem *)v; + } + if (dist_sq_test < dist_sq_best) { + dist_sq_best = dist_sq_test; + best.base_index = base_index; + best.ele = (BMElem *)v; } } } - if (use_edge) { - BMEdge *e; - BMIter eiter; - BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) { - if (BM_elem_flag_test(e, BM_ELEM_HIDDEN) == false) { - float point[3]; - if (coords) { - mid_v3_v3v3( - point, coords[BM_elem_index_get(e->v1)], coords[BM_elem_index_get(e->v2)]); - } - else { - mid_v3_v3v3(point, e->v1->co, e->v2->co); - } - mul_m4_v3(obedit->obmat, point); - const float dist_sq_test = dist_squared_to_ray_v3_normalized( - ray_origin, ray_direction, point); - if (dist_sq_test < dist_sq_best) { - dist_sq_best = dist_sq_test; - best.base_index = base_index; - best.ele = (BMElem *)e; - } + } + + if (use_edge && !use_boundary_edges) { + BMEdge *e; + BMIter eiter; + BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) { + if (BM_elem_flag_test(e, BM_ELEM_HIDDEN) == false) { + float point[3]; + if (coords) { + mid_v3_v3v3( + point, coords[BM_elem_index_get(e->v1)], coords[BM_elem_index_get(e->v2)]); + } + else { + mid_v3_v3v3(point, e->v1->co, e->v2->co); + } + mul_m4_v3(obedit->obmat, point); + const float dist_sq_test = dist_squared_to_ray_v3_normalized( + ray_origin, ray_direction, point); + if (dist_sq_test < dist_sq_best_edge) { + dist_sq_best_edge = dist_sq_test; + best_edge.base_index = base_index; + best_edge.ele = (BMElem *)e; + } + if (dist_sq_test < dist_sq_best) { + dist_sq_best = dist_sq_test; + best.base_index = base_index; + best.ele = (BMElem *)e; } } } @@ -1184,6 +1196,11 @@ bool EDBM_unified_findnearest_from_raycast(ViewContext *vc, mul_m4_v3(obedit->obmat, point); const float dist_sq_test = dist_squared_to_ray_v3_normalized( ray_origin, ray_direction, point); + if (dist_sq_test < dist_sq_best_face) { + dist_sq_best_face = dist_sq_test; + best_face.base_index = base_index; + best_face.ele = (BMElem *)f; + } if (dist_sq_test < dist_sq_best) { dist_sq_best = dist_sq_test; best.base_index = base_index; @@ -1195,7 +1212,10 @@ bool EDBM_unified_findnearest_from_raycast(ViewContext *vc, } } - *r_base_index = best.base_index; + *r_base_index_vert = best_vert.base_index; + *r_base_index_edge = best_edge.base_index; + *r_base_index_face = best_face.base_index; + if (r_eve) { *r_eve = NULL; } @@ -1206,22 +1226,17 @@ bool EDBM_unified_findnearest_from_raycast(ViewContext *vc, *r_efa = NULL; } - if (best.ele) { - switch (best.ele->head.htype) { - case BM_VERT: - *r_eve = (BMVert *)best.ele; - break; - case BM_EDGE: - *r_eed = (BMEdge *)best.ele; - break; - case BM_FACE: - *r_efa = (BMFace *)best.ele; - break; - default: - BLI_assert(0); - } + if (best_vert.ele) { + *r_eve = (BMVert *)best_vert.ele; + } + if (best_edge.ele) { + *r_eed = (BMEdge *)best_edge.ele; } - return (best.ele != NULL); + if (best_face.ele) { + *r_efa = (BMFace *)best_face.ele; + } + + return (best_vert.ele != NULL || best_edge.ele != NULL || best_face.ele != NULL); } /** \} */ diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 65c058556f5..0c4db012786 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -437,6 +437,7 @@ void EDBM_project_snap_verts(bContext *C, Depsgraph *depsgraph, ARegion *ar, BME }, mval, NULL, + NULL, co_proj, NULL)) { mul_v3_m4v3(eve->co, obedit->imat, co_proj); @@ -3130,12 +3131,7 @@ static int edbm_remove_doubles_exec(bContext *C, wmOperator *op) BM_mesh_elem_hflag_enable_test(em->bm, htype_select, BM_ELEM_TAG, true, true, BM_ELEM_SELECT); if (use_unselected) { - EDBM_op_init(em, &bmop, op, "automerge verts=%hv dist=%f", BM_ELEM_SELECT, threshold); - BMO_op_exec(em->bm, &bmop); - - if (!EDBM_op_finish(em, &bmop, op, true)) { - continue; - } + EDBM_automerge(obedit, false, BM_ELEM_SELECT, threshold); } else { EDBM_op_init(em, &bmop, op, "find_doubles verts=%hv dist=%f", BM_ELEM_SELECT, threshold); @@ -4040,7 +4036,7 @@ static void mesh_separate_material_assign_mat_nr(Main *bmain, Object *ob, const ma_obdata = NULL; } - BKE_material_clear_id(bmain, obdata, true); + BKE_material_clear_id(bmain, obdata); BKE_material_resize_object(bmain, ob, 1, true); BKE_material_resize_id(bmain, obdata, 1, true); @@ -4051,7 +4047,7 @@ static void mesh_separate_material_assign_mat_nr(Main *bmain, Object *ob, const id_us_plus((ID *)ma_obdata); } else { - BKE_material_clear_id(bmain, obdata, true); + BKE_material_clear_id(bmain, obdata); BKE_material_resize_object(bmain, ob, 0, true); BKE_material_resize_id(bmain, obdata, 0, true); } @@ -5710,7 +5706,7 @@ void MESH_OT_dissolve_limited(wmOperatorType *ot) "use_dissolve_boundaries", false, "All Boundaries", - "Dissolve all vertices inbetween face boundaries"); + "Dissolve all vertices in between face boundaries"); RNA_def_enum_flag(ot->srna, "delimit", rna_enum_mesh_delimit_mode_items, diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index d7ed14184fa..522c2f32d27 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -85,7 +85,7 @@ void EDBM_redo_state_restore(BMBackup backup, BMEditMesh *em, int recalctess) tmpbm = NULL; if (recalctess) { - BKE_editmesh_tessface_calc(em); + BKE_editmesh_looptri_calc(em); } } @@ -105,7 +105,7 @@ void EDBM_redo_state_free(BMBackup *backup, BMEditMesh *em, int recalctess) backup->bmcopy = NULL; if (recalctess && em) { - BKE_editmesh_tessface_calc(em); + BKE_editmesh_looptri_calc(em); } } @@ -162,7 +162,7 @@ bool EDBM_op_finish(BMEditMesh *em, BMOperator *bmop, wmOperator *op, const bool /* when copying, tessellation isn't to for faster copying, * but means we need to re-tessellate here */ if (em->looptris == NULL) { - BKE_editmesh_tessface_calc(em); + BKE_editmesh_looptri_calc(em); } if (em->ob) { @@ -292,10 +292,6 @@ void EDBM_mesh_make(Object *ob, const int select_mode, const bool add_key_index) Mesh *me = ob->data; BMesh *bm; - if (UNLIKELY(!me->mpoly && me->totface)) { - BKE_mesh_convert_mfaces_to_mpolys(me); - } - bm = BKE_mesh_to_bmesh(me, ob, add_key_index, @@ -347,10 +343,6 @@ void EDBM_mesh_load(Main *bmain, Object *ob) .calc_object_remap = true, })); -#ifdef USE_TESSFACE_DEFAULT - BKE_mesh_tessface_calc(me); -#endif - /* Free derived mesh. usually this would happen through depsgraph but there * are exceptions like file save that will not cause this, and we want to * avoid ending up with an invalid derived mesh then. @@ -1397,15 +1389,15 @@ void EDBM_stats_update(BMEditMesh *em) /* so many tools call these that we better make it a generic function. */ -void EDBM_update_generic(BMEditMesh *em, const bool do_tessface, const bool is_destructive) +void EDBM_update_generic(BMEditMesh *em, const bool do_tessellation, const bool is_destructive) { Object *ob = em->ob; /* order of calling isn't important */ DEG_id_tag_update(ob->data, ID_RECALC_GEOMETRY); WM_main_add_notifier(NC_GEOM | ND_DATA, ob->data); - if (do_tessface) { - BKE_editmesh_tessface_calc(em); + if (do_tessellation) { + BKE_editmesh_looptri_calc(em); } if (is_destructive) { diff --git a/source/blender/editors/mesh/mesh_data.c b/source/blender/editors/mesh/mesh_data.c index ee8de9d8ea9..569994bead1 100644 --- a/source/blender/editors/mesh/mesh_data.c +++ b/source/blender/editors/mesh/mesh_data.c @@ -289,18 +289,14 @@ int ED_mesh_uv_texture_add(Mesh *me, const char *name, const bool active_set, co if (me->mloopuv && do_init) { CustomData_add_layer_named( &me->ldata, CD_MLOOPUV, CD_DUPLICATE, me->mloopuv, me->totloop, name); - CustomData_add_layer_named( - &me->fdata, CD_MTFACE, CD_DUPLICATE, me->mtface, me->totface, name); is_init = true; } else { CustomData_add_layer_named(&me->ldata, CD_MLOOPUV, CD_DEFAULT, NULL, me->totloop, name); - CustomData_add_layer_named(&me->fdata, CD_MTFACE, CD_DEFAULT, NULL, me->totface, name); } if (active_set || layernum_dst == 0) { CustomData_set_layer_active(&me->ldata, CD_MLOOPUV, layernum_dst); - CustomData_set_layer_active(&me->fdata, CD_MTFACE, layernum_dst); } BKE_mesh_update_customdata_pointers(me, true); @@ -418,16 +414,13 @@ int ED_mesh_color_add(Mesh *me, const char *name, const bool active_set, const b if (me->mloopcol && do_init) { CustomData_add_layer_named( &me->ldata, CD_MLOOPCOL, CD_DUPLICATE, me->mloopcol, me->totloop, name); - CustomData_add_layer_named(&me->fdata, CD_MCOL, CD_DUPLICATE, me->mcol, me->totface, name); } else { CustomData_add_layer_named(&me->ldata, CD_MLOOPCOL, CD_DEFAULT, NULL, me->totloop, name); - CustomData_add_layer_named(&me->fdata, CD_MCOL, CD_DEFAULT, NULL, me->totface, name); } if (active_set || layernum == 0) { CustomData_set_layer_active(&me->ldata, CD_MLOOPCOL, layernum); - CustomData_set_layer_active(&me->fdata, CD_MCOL, layernum); } BKE_mesh_update_customdata_pointers(me, true); @@ -885,18 +878,8 @@ void MESH_OT_customdata_custom_splitnormals_clear(wmOperatorType *ot) /************************** Add Geometry Layers *************************/ -void ED_mesh_update( - Mesh *mesh, bContext *C, bool calc_edges, bool calc_edges_loose, bool calc_tessface) +void ED_mesh_update(Mesh *mesh, bContext *C, bool calc_edges, bool calc_edges_loose) { - bool tessface_input = false; - - if (mesh->totface > 0 && mesh->totpoly == 0) { - BKE_mesh_convert_mfaces_to_mpolys(mesh); - - /* would only be converting back again, don't bother */ - tessface_input = true; - } - if (calc_edges_loose && mesh->totedge) { BKE_mesh_calc_edges_loose(mesh); } @@ -905,15 +888,8 @@ void ED_mesh_update( BKE_mesh_calc_edges(mesh, calc_edges, true); } - if (calc_tessface) { - if (tessface_input == false) { - BKE_mesh_tessface_calc(mesh); - } - } - else { - /* default state is not to have tessface's so make sure this is the case */ - BKE_mesh_tessface_clear(mesh); - } + /* Default state is not to have tessface's so make sure this is the case. */ + BKE_mesh_tessface_clear(mesh); BKE_mesh_calc_normals(mesh); @@ -988,39 +964,6 @@ static void mesh_add_edges(Mesh *mesh, int len) mesh->totedge = totedge; } -static void mesh_add_tessfaces(Mesh *mesh, int len) -{ - CustomData fdata; - MFace *mface; - int i, totface; - - if (len == 0) { - return; - } - - totface = mesh->totface + len; /* new face count */ - - /* update customdata */ - CustomData_copy(&mesh->fdata, &fdata, CD_MASK_MESH.fmask, CD_DEFAULT, totface); - CustomData_copy_data(&mesh->fdata, &fdata, 0, 0, mesh->totface); - - if (!CustomData_has_layer(&fdata, CD_MFACE)) { - CustomData_add_layer(&fdata, CD_MFACE, CD_CALLOC, NULL, totface); - } - - CustomData_free(&mesh->fdata, mesh->totface); - mesh->fdata = fdata; - BKE_mesh_update_customdata_pointers(mesh, true); - - /* set default flags */ - mface = &mesh->mface[mesh->totface]; - for (i = 0; i < len; i++, mface++) { - mface->flag = ME_FACE_SEL; - } - - mesh->totface = totface; -} - static void mesh_add_loops(Mesh *mesh, int len) { CustomData ldata; @@ -1109,20 +1052,6 @@ static void mesh_remove_edges(Mesh *mesh, int len) mesh->totedge = totedge; } -static void mesh_remove_faces(Mesh *mesh, int len) -{ - int totface; - - if (len == 0) { - return; - } - - totface = mesh->totface - len; /* new face count */ - CustomData_free_elem(&mesh->fdata, totface, len); - - mesh->totface = totface; -} - #if 0 void ED_mesh_geometry_add(Mesh *mesh, ReportList *reports, int verts, int edges, int faces) { @@ -1143,21 +1072,6 @@ void ED_mesh_geometry_add(Mesh *mesh, ReportList *reports, int verts, int edges, } #endif -void ED_mesh_tessfaces_add(Mesh *mesh, ReportList *reports, int count) -{ - if (mesh->edit_mesh) { - BKE_report(reports, RPT_ERROR, "Cannot add tessfaces in edit mode"); - return; - } - - if (mesh->mpoly) { - BKE_report(reports, RPT_ERROR, "Cannot add tessfaces to a mesh that already has polygons"); - return; - } - - mesh_add_tessfaces(mesh, count); -} - void ED_mesh_edges_add(Mesh *mesh, ReportList *reports, int count) { if (mesh->edit_mesh) { @@ -1178,20 +1092,6 @@ void ED_mesh_vertices_add(Mesh *mesh, ReportList *reports, int count) mesh_add_verts(mesh, count); } -void ED_mesh_faces_remove(Mesh *mesh, ReportList *reports, int count) -{ - if (mesh->edit_mesh) { - BKE_report(reports, RPT_ERROR, "Cannot remove faces in edit mode"); - return; - } - else if (count > mesh->totface) { - BKE_report(reports, RPT_ERROR, "Cannot remove more faces than the mesh contains"); - return; - } - - mesh_remove_faces(mesh, count); -} - void ED_mesh_edges_remove(Mesh *mesh, ReportList *reports, int count) { if (mesh->edit_mesh) { @@ -1240,26 +1140,6 @@ void ED_mesh_polys_add(Mesh *mesh, ReportList *reports, int count) mesh_add_polys(mesh, count); } -void ED_mesh_calc_tessface(Mesh *mesh, bool free_mpoly) -{ - if (mesh->edit_mesh) { - BKE_editmesh_tessface_calc(mesh->edit_mesh); - } - else { - BKE_mesh_tessface_calc(mesh); - } - if (free_mpoly) { - CustomData_free(&mesh->ldata, mesh->totloop); - CustomData_free(&mesh->pdata, mesh->totpoly); - mesh->totloop = 0; - mesh->totpoly = 0; - mesh->mloop = NULL; - mesh->mloopcol = NULL; - mesh->mloopuv = NULL; - mesh->mpoly = NULL; - } -} - void ED_mesh_report_mirror_ex(wmOperator *op, int totmirr, int totfail, char selectmode) { const char *elem_type; diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h index 8d585977105..3558a07c6fb 100644 --- a/source/blender/editors/mesh/mesh_intern.h +++ b/source/blender/editors/mesh/mesh_intern.h @@ -122,6 +122,8 @@ void MESH_GGT_spin_redo(struct wmGizmoGroupType *gzgt); void MESH_OT_polybuild_face_at_cursor(struct wmOperatorType *ot); void MESH_OT_polybuild_split_at_cursor(struct wmOperatorType *ot); void MESH_OT_polybuild_dissolve_at_cursor(struct wmOperatorType *ot); +void MESH_OT_polybuild_transform_at_cursor(struct wmOperatorType *ot); +void MESH_OT_polybuild_delete_at_cursor(struct wmOperatorType *ot); /* *** editmesh_inset.c *** */ void MESH_OT_inset(struct wmOperatorType *ot); diff --git a/source/blender/editors/mesh/mesh_ops.c b/source/blender/editors/mesh/mesh_ops.c index c3387dcfc09..9b6e991a9f5 100644 --- a/source/blender/editors/mesh/mesh_ops.c +++ b/source/blender/editors/mesh/mesh_ops.c @@ -145,6 +145,8 @@ void ED_operatortypes_mesh(void) WM_operatortype_append(MESH_OT_polybuild_face_at_cursor); WM_operatortype_append(MESH_OT_polybuild_split_at_cursor); WM_operatortype_append(MESH_OT_polybuild_dissolve_at_cursor); + WM_operatortype_append(MESH_OT_polybuild_transform_at_cursor); + WM_operatortype_append(MESH_OT_polybuild_delete_at_cursor); WM_operatortype_append(MESH_OT_uv_texture_add); WM_operatortype_append(MESH_OT_uv_texture_remove); @@ -331,6 +333,25 @@ void ED_operatormacros_mesh(void) otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); RNA_boolean_set(otmacro->ptr, "use_proportional_edit", false); RNA_boolean_set(otmacro->ptr, "mirror", false); + + ot = WM_operatortype_append_macro("MESH_OT_polybuild_transform_at_cursor_move", + "Transform at Cursor Move", + "", + OPTYPE_UNDO | OPTYPE_REGISTER); + WM_operatortype_macro_define(ot, "MESH_OT_polybuild_transform_at_cursor"); + otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); + RNA_boolean_set(otmacro->ptr, "use_proportional_edit", false); + RNA_boolean_set(otmacro->ptr, "mirror", false); + + ot = WM_operatortype_append_macro("MESH_OT_polybuild_extrude_at_cursor_move", + "Extrude at Cursor Move", + "", + OPTYPE_UNDO | OPTYPE_REGISTER); + WM_operatortype_macro_define(ot, "MESH_OT_polybuild_transform_at_cursor"); + otmacro = WM_operatortype_macro_define(ot, "MESH_OT_extrude_edges_indiv"); + otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); + RNA_boolean_set(otmacro->ptr, "use_proportional_edit", false); + RNA_boolean_set(otmacro->ptr, "mirror", false); } /* note mesh keymap also for other space? */ diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt index 2490f88b5eb..aabfa78cf58 100644 --- a/source/blender/editors/object/CMakeLists.txt +++ b/source/blender/editors/object/CMakeLists.txt @@ -48,6 +48,7 @@ set(SRC object_collection.c object_constraint.c object_data_transfer.c + object_data_transform.c object_edit.c object_facemap_ops.c object_gpencil_modifier.c diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index db1aa6ca37f..3d5ec3d4ed5 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -103,6 +103,7 @@ #include "ED_mesh.h" #include "ED_node.h" #include "ED_object.h" +#include "ED_outliner.h" #include "ED_physics.h" #include "ED_render.h" #include "ED_screen.h" @@ -502,6 +503,8 @@ Object *ED_object_add_type(bContext *C, /* TODO(sergey): Use proper flag for tagging here. */ DEG_id_tag_update(&scene->id, 0); + ED_outliner_select_sync_from_object_tag(C); + return ob; } @@ -2383,13 +2386,22 @@ static int convert_exec(bContext *C, wmOperator *op) BKE_object_free_curve_cache(newob); } else if (target == OB_GPENCIL) { - /* Create a new grease pencil object */ - if (gpencil_ob == NULL) { - const float *cur = scene->cursor.location; - ushort local_view_bits = (v3d && v3d->localvd) ? v3d->local_view_uuid : 0; - gpencil_ob = ED_gpencil_add_object(C, scene, cur, local_view_bits); + if (ob->type != OB_CURVE) { + BKE_report( + op->reports, RPT_ERROR, "Convert Surfaces to Grease Pencil is not supported."); + } + else { + /* Create a new grease pencil object only if it was not created before. + * All curves selected are converted as strokes of the same grease pencil object. + * Nurbs Surface are not supported. + */ + if (gpencil_ob == NULL) { + const float *cur = scene->cursor.location; + ushort local_view_bits = (v3d && v3d->localvd) ? v3d->local_view_uuid : 0; + gpencil_ob = ED_gpencil_add_object(C, scene, cur, local_view_bits); + } + BKE_gpencil_convert_curve(bmain, scene, gpencil_ob, ob, false, false, true); } - BKE_gpencil_convert_curve(bmain, scene, gpencil_ob, ob, gpencil_lines, use_collections); } } else if (ob->type == OB_MBALL && target == OB_MESH) { diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c index 7343dba420f..8981221cb9c 100644 --- a/source/blender/editors/object/object_constraint.c +++ b/source/blender/editors/object/object_constraint.c @@ -493,8 +493,12 @@ static void test_constraint( CONSTRAINT_TYPE_CLAMPTO, CONSTRAINT_TYPE_SPLINEIK)) { if (ct->tar) { + /* The object type check is only needed here in case we have a placeholder + * object assigned (because the library containing the curve is missing). + * + * In other cases it should be impossible to have a type mismatch. + */ if (ct->tar->type != OB_CURVE) { - ct->tar = NULL; con->flag |= CONSTRAINT_DISABLE; } else { @@ -507,8 +511,12 @@ static void test_constraint( } else if (con->type == CONSTRAINT_TYPE_ARMATURE) { if (ct->tar) { + /* The object type check is only needed here in case we have a placeholder + * object assigned (because the library containing the armature is missing). + * + * In other cases it should be impossible to have a type mismatch. + */ if (ct->tar->type != OB_ARMATURE) { - ct->tar = NULL; con->flag |= CONSTRAINT_DISABLE; } else if (!BKE_armature_find_bone_name(BKE_armature_from_object(ct->tar), @@ -636,7 +644,7 @@ static const EnumPropertyItem constraint_owner_items[] = { static bool edit_constraint_poll_generic(bContext *C, StructRNA *rna_type) { PointerRNA ptr = CTX_data_pointer_get_type(C, "constraint", rna_type); - Object *ob = (ptr.id.data) ? ptr.id.data : ED_object_active_context(C); + Object *ob = (ptr.owner_id) ? (Object *)ptr.owner_id : ED_object_active_context(C); if (!ptr.data) { CTX_wm_operator_poll_msg_set(C, "Context missing 'constraint'"); @@ -648,7 +656,7 @@ static bool edit_constraint_poll_generic(bContext *C, StructRNA *rna_type) return 0; } - if (ID_IS_LINKED(ob) || (ptr.id.data && ID_IS_LINKED(ptr.id.data))) { + if (ID_IS_LINKED(ob) || (ptr.owner_id && ID_IS_LINKED(ptr.owner_id))) { CTX_wm_operator_poll_msg_set(C, "Cannot edit library data"); return 0; } @@ -680,7 +688,7 @@ static void edit_constraint_properties(wmOperatorType *ot) static int edit_constraint_invoke_properties(bContext *C, wmOperator *op) { PointerRNA ptr = CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint); - Object *ob = (ptr.id.data) ? ptr.id.data : ED_object_active_context(C); + Object *ob = (ptr.owner_id) ? (Object *)ptr.owner_id : ED_object_active_context(C); bConstraint *con; ListBase *list; @@ -1422,14 +1430,14 @@ void ED_object_constraint_dependency_tag_update(Main *bmain, Object *ob, bConstr static bool constraint_poll(bContext *C) { PointerRNA ptr = CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint); - return (ptr.id.data && ptr.data); + return (ptr.owner_id && ptr.data); } static int constraint_delete_exec(bContext *C, wmOperator *UNUSED(op)) { Main *bmain = CTX_data_main(C); PointerRNA ptr = CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint); - Object *ob = ptr.id.data; + Object *ob = (Object *)ptr.owner_id; bConstraint *con = ptr.data; ListBase *lb = get_constraint_lb(ob, con, NULL); diff --git a/source/blender/editors/object/object_data_transform.c b/source/blender/editors/object/object_data_transform.c new file mode 100644 index 00000000000..ee86c79ead5 --- /dev/null +++ b/source/blender/editors/object/object_data_transform.c @@ -0,0 +1,333 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup edobj + * + * Use to transform object origins only. + * + * This is a small API to store & apply transformations to object data, + * where a transformation matrix can be continually applied ontop of the original values + * so we don't loose precision over time. + */ + +#include <stdlib.h> +#include <string.h> + +#include "DNA_anim_types.h" +#include "DNA_armature_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meta_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_collection_types.h" +#include "DNA_lattice_types.h" + +#include "BLI_math.h" +#include "BLI_listbase.h" +#include "BLI_utildefines.h" + +#include "BKE_curve.h" +#include "BKE_mball.h" +#include "BKE_mesh.h" +#include "BKE_armature.h" +#include "BKE_lattice.h" + +#include "DEG_depsgraph.h" + +#include "WM_types.h" + +#include "ED_object.h" + +#include "MEM_guardedalloc.h" + +/* -------------------------------------------------------------------- */ +/** \name Internal Transform Get/Apply + * + * Some object data types don't have utility functions to access their transformation data. + * Define these locally. + * + * \{ */ + +/* Armature */ + +struct ElemData_Armature { + float tail[3]; + float head[3]; + float roll; + float arm_tail[3]; + float arm_head[3]; + float arm_roll; + float rad_tail; + float rad_head; + float dist; + float xwidth; + float zwidth; +}; + +static struct ElemData_Armature *armature_coords_and_quats_get_recurse( + const ListBase *bone_base, struct ElemData_Armature *elem_array) +{ + struct ElemData_Armature *elem = elem_array; + for (const Bone *bone = bone_base->first; bone; bone = bone->next) { + +#define COPY_PTR(member) memcpy(elem->member, bone->member, sizeof(bone->member)) +#define COPY_VAL(member) memcpy(&elem->member, &bone->member, sizeof(bone->member)) + COPY_PTR(head); + COPY_PTR(tail); + COPY_VAL(roll); + COPY_PTR(arm_head); + COPY_PTR(arm_tail); + COPY_VAL(arm_roll); + COPY_VAL(rad_tail); + COPY_VAL(rad_head); + COPY_VAL(dist); + COPY_VAL(xwidth); + COPY_VAL(zwidth); +#undef COPY_PTR +#undef COPY_VAL + + elem = armature_coords_and_quats_get_recurse(&bone->childbase, elem + 1); + } + return elem; +} + +static void armature_coords_and_quats_get(const bArmature *arm, + struct ElemData_Armature *elem_array) +{ + armature_coords_and_quats_get_recurse(&arm->bonebase, elem_array); +} + +static const struct ElemData_Armature *armature_coords_and_quats_apply_with_mat4_recurse( + ListBase *bone_base, const struct ElemData_Armature *elem_array, const float mat[4][4]) +{ + const struct ElemData_Armature *elem = elem_array; + for (Bone *bone = bone_base->first; bone; bone = bone->next) { + +#define COPY_PTR(member) memcpy(bone->member, elem->member, sizeof(bone->member)) +#define COPY_VAL(member) memcpy(&bone->member, &elem->member, sizeof(bone->member)) + COPY_PTR(head); + COPY_PTR(tail); + COPY_VAL(roll); + COPY_PTR(arm_head); + COPY_PTR(arm_tail); + COPY_VAL(arm_roll); + COPY_VAL(rad_tail); + COPY_VAL(rad_head); + COPY_VAL(dist); + COPY_VAL(xwidth); + COPY_VAL(zwidth); +#undef COPY_PTR +#undef COPY_VAL + + elem = armature_coords_and_quats_apply_with_mat4_recurse(&bone->childbase, elem + 1, mat); + } + return elem; +} + +static void armature_coords_and_quats_apply_with_mat4(bArmature *arm, + const struct ElemData_Armature *elem_array, + const float mat[4][4]) +{ + armature_coords_and_quats_apply_with_mat4_recurse(&arm->bonebase, elem_array, mat); + BKE_armature_transform(arm, mat, true); +} + +/* MetaBall */ + +struct ElemData_MetaBall { + float co[3]; + float quat[4]; + float exp[3]; + float rad; +}; + +static void metaball_coords_and_quats_get(const MetaBall *mb, struct ElemData_MetaBall *elem_array) +{ + struct ElemData_MetaBall *elem = elem_array; + for (const MetaElem *ml = mb->elems.first; ml; ml = ml->next, elem++) { + copy_v3_v3(elem->co, &ml->x); + copy_qt_qt(elem->quat, ml->quat); + copy_v3_v3(elem->exp, &ml->expx); + elem->rad = ml->rad; + } +} + +static void metaball_coords_and_quats_apply_with_mat4(MetaBall *mb, + const struct ElemData_MetaBall *elem_array, + const float mat[4][4]) +{ + const struct ElemData_MetaBall *elem = elem_array; + for (MetaElem *ml = mb->elems.first; ml; ml = ml->next, elem++) { + copy_v3_v3(&ml->x, elem->co); + copy_qt_qt(ml->quat, elem->quat); + copy_v3_v3(&ml->expx, elem->exp); + ml->rad = elem->rad; + } + BKE_mball_transform(mb, mat, true); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Public Object Data Storage API + * + * Used for interactively transforming object data. + * + * Store object data transformation in an opaque struct. + * \{ */ + +struct XFormObjectData { + ID *id; +}; + +struct XFormObjectData_Mesh { + struct XFormObjectData base; + float elem_array[0][3]; +}; + +struct XFormObjectData_Lattice { + struct XFormObjectData base; + float elem_array[0][3]; +}; + +struct XFormObjectData_Curve { + struct XFormObjectData base; + float elem_array[0][3]; +}; + +struct XFormObjectData_Armature { + struct XFormObjectData base; + struct ElemData_Armature elem_array[0]; +}; + +struct XFormObjectData_MetaBall { + struct XFormObjectData base; + struct ElemData_MetaBall elem_array[0]; +}; + +struct XFormObjectData *ED_object_data_xform_create(ID *id) +{ + struct XFormObjectData *xod_base = NULL; + switch (GS(id->name)) { + case ID_ME: { + Mesh *me = (Mesh *)id; + const int elem_array_len = me->totvert; + struct XFormObjectData_Mesh *xod = MEM_mallocN( + sizeof(*xod) + (sizeof(*xod->elem_array) * elem_array_len), __func__); + BKE_mesh_vert_coords_get(me, xod->elem_array); + xod_base = &xod->base; + break; + } + case ID_LT: { + Lattice *lt = (Lattice *)id; + const int elem_array_len = lt->pntsu * lt->pntsv * lt->pntsw; + struct XFormObjectData_Lattice *xod = MEM_mallocN( + sizeof(*xod) + (sizeof(*xod->elem_array) * elem_array_len), __func__); + BKE_lattice_vert_coords_get(lt, xod->elem_array); + xod_base = &xod->base; + break; + } + case ID_CU: { + Curve *cu = (Curve *)id; + const short ob_type = BKE_curve_type_get(cu); + if (ob_type == OB_FONT) { + /* We could support translation. */ + break; + } + const int elem_array_len = BKE_nurbList_verts_count(&cu->nurb); + struct XFormObjectData_Curve *xod = MEM_mallocN( + sizeof(*xod) + (sizeof(*xod->elem_array) * elem_array_len), __func__); + BKE_curve_nurbs_vert_coords_get(&cu->nurb, xod->elem_array, elem_array_len); + xod_base = &xod->base; + break; + } + case ID_AR: { + bArmature *arm = (bArmature *)id; + const int elem_array_len = BKE_armature_bonelist_count(&arm->bonebase); + struct XFormObjectData_Armature *xod = MEM_mallocN( + sizeof(*xod) + (sizeof(*xod->elem_array) * elem_array_len), __func__); + armature_coords_and_quats_get(arm, xod->elem_array); + xod_base = &xod->base; + break; + } + case ID_MB: { + MetaBall *mb = (MetaBall *)id; + const int elem_array_len = BLI_listbase_count(&mb->elems); + struct XFormObjectData_MetaBall *xod = MEM_mallocN( + sizeof(*xod) + (sizeof(*xod->elem_array) * elem_array_len), __func__); + metaball_coords_and_quats_get(mb, xod->elem_array); + xod_base = &xod->base; + break; + } + default: { + break; + } + } + if (xod_base) { + xod_base->id = id; + } + return xod_base; +} + +void ED_object_data_xform_destroy(struct XFormObjectData *xod) +{ + MEM_freeN(xod); +} + +void ED_object_data_xform_by_mat4(struct XFormObjectData *xod_base, const float mat[4][4]) +{ + switch (GS(xod_base->id->name)) { + case ID_ME: { + Mesh *me = (Mesh *)xod_base->id; + struct XFormObjectData_Mesh *xod = (struct XFormObjectData_Mesh *)xod_base; + BKE_mesh_vert_coords_apply_with_mat4(me, xod->elem_array, mat); + break; + } + case ID_LT: { + Lattice *lt = (Lattice *)xod_base->id; + struct XFormObjectData_Lattice *xod = (struct XFormObjectData_Lattice *)xod_base; + BKE_lattice_vert_coords_apply_with_mat4(lt, xod->elem_array, mat); + break; + } + case ID_CU: { + Curve *cu = (Curve *)xod_base->id; + struct XFormObjectData_Curve *xod = (struct XFormObjectData_Curve *)xod_base; + BKE_curve_nurbs_vert_coords_apply_with_mat4(&cu->nurb, xod->elem_array, mat, true); + break; + } + case ID_AR: { + bArmature *arm = (bArmature *)xod_base->id; + struct XFormObjectData_Armature *xod = (struct XFormObjectData_Armature *)xod_base; + armature_coords_and_quats_apply_with_mat4(arm, xod->elem_array, mat); + break; + } + case ID_MB: { + MetaBall *mb = (MetaBall *)xod_base->id; + struct XFormObjectData_MetaBall *xod = (struct XFormObjectData_MetaBall *)xod_base; + metaball_coords_and_quats_apply_with_mat4(mb, xod->elem_array, mat); + break; + } + default: { + break; + } + } +} + +/** \} */ diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index ed40a4eb948..33c7ffefb8b 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -116,6 +116,7 @@ typedef struct MoveToCollectionData MoveToCollectionData; static void move_to_collection_menus_items(struct uiLayout *layout, struct MoveToCollectionData *menu); +static ListBase selected_objects_get(bContext *C); /* ************* XXX **************** */ static void error(const char *UNUSED(arg)) @@ -593,7 +594,7 @@ bool ED_object_editmode_enter_ex(Main *bmain, Scene *scene, Object *ob, int flag if (LIKELY(em)) { /* order doesn't matter */ EDBM_mesh_normals_update(em); - BKE_editmesh_tessface_calc(em); + BKE_editmesh_looptri_calc(em); } WM_main_add_notifier(NC_SCENE | ND_MODE | NS_EDITMODE_MESH, NULL); @@ -1199,7 +1200,7 @@ static int shade_smooth_exec(bContext *C, wmOperator *op) } if (ob->type == OB_MESH) { - BKE_mesh_smooth_flag_set(ob, !clear); + BKE_mesh_smooth_flag_set(ob->data, !clear); BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); @@ -1460,6 +1461,23 @@ void OBJECT_OT_mode_set_or_submode(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } +static ListBase selected_objects_get(bContext *C) +{ + ListBase objects = {NULL}; + + if (CTX_wm_space_outliner(C) != NULL) { + ED_outliner_selected_objects_get(C, &objects); + } + else { + CTX_DATA_BEGIN (C, Object *, ob, selected_objects) { + BLI_addtail(&objects, BLI_genericNodeN(ob)); + } + CTX_DATA_END; + } + + return objects; +} + static bool move_to_collection_poll(bContext *C) { if (CTX_wm_space_outliner(C) != NULL) { @@ -1472,7 +1490,7 @@ static bool move_to_collection_poll(bContext *C) return false; } - return ED_operator_object_active_editable(C); + return ED_operator_objectmode(C); } } @@ -1498,15 +1516,7 @@ static int move_to_collection_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - if (CTX_wm_space_outliner(C) != NULL) { - ED_outliner_selected_objects_get(C, &objects); - } - else { - CTX_DATA_BEGIN (C, Object *, ob, selected_objects) { - BLI_addtail(&objects, BLI_genericNodeN(ob)); - } - CTX_DATA_END; - } + objects = selected_objects_get(C); if (is_new) { char new_collection_name[MAX_NAME]; @@ -1650,6 +1660,13 @@ static int move_to_collection_invoke(bContext *C, wmOperator *op, const wmEvent { Scene *scene = CTX_data_scene(C); + ListBase objects = selected_objects_get(C); + if (BLI_listbase_is_empty(&objects)) { + BKE_report(op->reports, RPT_ERROR, "No objects selected"); + return OPERATOR_CANCELLED; + } + BLI_freelistN(&objects); + /* Reset the menus data for the current master collection, and free previously allocated data. */ move_to_collection_menus_free(&master_collection_menu); @@ -1674,7 +1691,7 @@ static int move_to_collection_invoke(bContext *C, wmOperator *op, const wmEvent return move_to_collection_exec(C, op); } - Collection *master_collection = BKE_collection_master(scene); + Collection *master_collection = scene->master_collection; /* We need the data to be allocated so it's available during menu drawing. * Technically we could use wmOperator->customdata. However there is no free callback diff --git a/source/blender/editors/object/object_gpencil_modifier.c b/source/blender/editors/object/object_gpencil_modifier.c index 075cd5acad6..9138e65dd2f 100644 --- a/source/blender/editors/object/object_gpencil_modifier.c +++ b/source/blender/editors/object/object_gpencil_modifier.c @@ -390,7 +390,7 @@ void OBJECT_OT_gpencil_modifier_add(wmOperatorType *ot) static int gpencil_edit_modifier_poll_generic(bContext *C, StructRNA *rna_type, int obtype_flag) { PointerRNA ptr = CTX_data_pointer_get_type(C, "modifier", rna_type); - Object *ob = (ptr.id.data) ? ptr.id.data : ED_object_active_context(C); + Object *ob = (ptr.owner_id) ? (Object *)ptr.owner_id : ED_object_active_context(C); if (!ob || ID_IS_LINKED(ob)) { return 0; @@ -398,7 +398,7 @@ static int gpencil_edit_modifier_poll_generic(bContext *C, StructRNA *rna_type, if (obtype_flag && ((1 << ob->type) & obtype_flag) == 0) { return 0; } - if (ptr.id.data && ID_IS_LINKED(ptr.id.data)) { + if (ptr.owner_id && ID_IS_LINKED(ptr.owner_id)) { return 0; } diff --git a/source/blender/editors/object/object_hook.c b/source/blender/editors/object/object_hook.c index 15c5ea40991..bcbf7ddf9a6 100644 --- a/source/blender/editors/object/object_hook.c +++ b/source/blender/editors/object/object_hook.c @@ -352,7 +352,7 @@ static bool object_hook_index_array(Main *bmain, em = me->edit_mesh; EDBM_mesh_normals_update(em); - BKE_editmesh_tessface_calc(em); + BKE_editmesh_looptri_calc(em); /* check selected vertices first */ if (return_editmesh_indexar(em, r_tot, r_indexar, r_cent) == 0) { @@ -436,7 +436,7 @@ static void object_hook_from_context( HookModifierData *hmd; if (ptr->data) { /* if modifier context is available, use that */ - ob = ptr->id.data; + ob = (Object *)ptr->owner_id; hmd = ptr->data; } else { /* use the provided property */ diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c index 8818fd71190..88d01936882 100644 --- a/source/blender/editors/object/object_modifier.c +++ b/source/blender/editors/object/object_modifier.c @@ -52,6 +52,7 @@ #include "BKE_editmesh.h" #include "BKE_effect.h" #include "BKE_global.h" +#include "BKE_gpencil_modifier.h" #include "BKE_key.h" #include "BKE_lattice.h" #include "BKE_library.h" @@ -110,6 +111,9 @@ static void object_force_modifier_update_for_bind(Depsgraph *depsgraph, Object * else if (ELEM(ob->type, OB_CURVE, OB_SURF, OB_FONT)) { BKE_displist_make_curveTypes(depsgraph, scene_eval, ob_eval, false, false); } + else if (ob->type == OB_GPENCIL) { + BKE_gpencil_modifiers_calc(depsgraph, scene_eval, ob_eval); + } } static void object_force_modifier_bind_simple_options(Depsgraph *depsgraph, @@ -714,9 +718,9 @@ static int modifier_apply_obdata( RPT_INFO, "Applied modifier only changed CV points, not tessellated/bevel vertices"); - vertexCos = BKE_curve_nurbs_vertexCos_get(&curve_eval->nurb, &numVerts); + vertexCos = BKE_curve_nurbs_vert_coords_alloc(&curve_eval->nurb, &numVerts); mti->deformVerts(md_eval, &mectx, NULL, vertexCos, numVerts); - BK_curve_nurbs_vertexCos_apply(&curve->nurb, vertexCos); + BKE_curve_nurbs_vert_coords_apply(&curve->nurb, vertexCos, false); MEM_freeN(vertexCos); @@ -918,7 +922,7 @@ bool edit_modifier_poll_generic(bContext *C, const bool is_editmode_allowed) { PointerRNA ptr = CTX_data_pointer_get_type(C, "modifier", rna_type); - Object *ob = (ptr.id.data) ? ptr.id.data : ED_object_active_context(C); + Object *ob = (ptr.owner_id) ? (Object *)ptr.owner_id : ED_object_active_context(C); if (!ob || ID_IS_LINKED(ob)) { return 0; @@ -926,7 +930,7 @@ bool edit_modifier_poll_generic(bContext *C, if (obtype_flag && ((1 << ob->type) & obtype_flag) == 0) { return 0; } - if (ptr.id.data && ID_IS_LINKED(ptr.id.data)) { + if (ptr.owner_id && ID_IS_LINKED(ptr.owner_id)) { return 0; } diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index a69f4872e72..06c360ed1cd 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -149,7 +149,7 @@ static int vertex_parent_set_exec(bContext *C, wmOperator *op) em = me->edit_mesh; EDBM_mesh_normals_update(em); - BKE_editmesh_tessface_calc(em); + BKE_editmesh_looptri_calc(em); /* Make sure the evaluated mesh is updated. * @@ -1434,7 +1434,7 @@ static int make_links_scene_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - Collection *collection_to = BKE_collection_master(scene_to); + Collection *collection_to = scene_to->master_collection; CTX_DATA_BEGIN (C, Base *, base, selected_bases) { BKE_collection_object_add(bmain, collection_to, base->object); } @@ -1744,7 +1744,7 @@ static Collection *single_object_users_collection(Main *bmain, } /* Since master collection has already be duplicated as part of scene copy, - * we do not duplictae it here. + * we do not duplicate it here. * However, this means its children need to be re-added manually here, * otherwise their parent lists are empty (which will lead to crashes, see T63101). */ CollectionChild *child_next, *child = collection->children.first; @@ -1771,7 +1771,7 @@ static void single_object_users( Main *bmain, Scene *scene, View3D *v3d, const int flag, const bool copy_collections) { /* duplicate all the objects of the scene (and matching collections, if required). */ - Collection *master_collection = BKE_collection_master(scene); + Collection *master_collection = scene->master_collection; single_object_users_collection(bmain, scene, master_collection, flag, copy_collections, true); /* duplicate collections that consist entirely of duplicated objects */ @@ -2426,6 +2426,8 @@ static int make_override_library_exec(bContext *C, wmOperator *op) if (!ID_IS_LINKED(obact) && obact->instance_collection != NULL && ID_IS_LINKED(obact->instance_collection)) { + BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); + Object *obcollection = obact; Collection *collection = obcollection->instance_collection; @@ -2471,7 +2473,11 @@ static int make_override_library_exec(bContext *C, wmOperator *op) DEG_id_tag_update_ex(bmain, &new_ob->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS); } /* parent to 'collection' empty */ - if (new_ob->parent == NULL) { + /* Disabled for now, according to some artist this is probably not really useful anyway. + * And it breaks things like objects parented to bones + * (most likely due to missing proper setting of inverse parent matrix?)... */ + /* Note: we might even actually want to get rid of that instanciating empty... */ + if (0 && new_ob->parent == NULL) { new_ob->parent = obcollection; } if (new_ob == (Object *)obact->id.newid) { @@ -2499,7 +2505,7 @@ static int make_override_library_exec(bContext *C, wmOperator *op) /* Cleanup. */ BKE_main_id_clear_newpoins(bmain); - BKE_main_id_tag_listbase(&bmain->objects, LIB_TAG_DOIT, false); + BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); } /* Else, poll func ensures us that ID_IS_LINKED(obact) is true. */ else if (obact->type == OB_ARMATURE) { @@ -2518,7 +2524,7 @@ static int make_override_library_exec(bContext *C, wmOperator *op) /* Cleanup. */ BKE_main_id_clear_newpoins(bmain); - BKE_main_id_tag_listbase(&bmain->objects, LIB_TAG_DOIT, false); + BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); } /* TODO: probably more cases where we want to do automated smart things in the future! */ else { @@ -2706,7 +2712,7 @@ static int object_unlink_data_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - id = pprop.ptr.id.data; + id = pprop.ptr.owner_id; if (GS(id->name) == ID_OB) { Object *ob = (Object *)id; diff --git a/source/blender/editors/object/object_remesh.c b/source/blender/editors/object/object_remesh.c index 1bcac9b4739..5f464084a9b 100644 --- a/source/blender/editors/object/object_remesh.c +++ b/source/blender/editors/object/object_remesh.c @@ -108,7 +108,7 @@ static int voxel_remesh_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - Mesh *obj_mesh_copy; + Mesh *obj_mesh_copy = NULL; if (mesh->flag & ME_REMESH_REPROJECT_PAINT_MASK) { obj_mesh_copy = BKE_mesh_new_nomain_from_template(mesh, mesh->totvert, 0, 0, 0, 0); CustomData_copy( @@ -126,7 +126,7 @@ static int voxel_remesh_exec(bContext *C, wmOperator *op) } if (mesh->flag & ME_REMESH_SMOOTH_NORMALS) { - BKE_mesh_smooth_flag_set(ob, true); + BKE_mesh_smooth_flag_set(ob->data, true); } if (ob->mode == OB_MODE_SCULPT) { diff --git a/source/blender/editors/object/object_select.c b/source/blender/editors/object/object_select.c index da06707ebac..28242b986f1 100644 --- a/source/blender/editors/object/object_select.c +++ b/source/blender/editors/object/object_select.c @@ -69,6 +69,7 @@ #include "ED_armature.h" #include "ED_object.h" +#include "ED_outliner.h" #include "ED_screen.h" #include "ED_select_utils.h" #include "ED_keyframing.h" @@ -436,6 +437,8 @@ static int object_select_by_type_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + ED_outliner_select_sync_from_object_tag(C); + return OPERATOR_FINISHED; } @@ -717,6 +720,7 @@ static int object_select_linked_exec(bContext *C, wmOperator *op) if (changed) { DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + ED_outliner_select_sync_from_object_tag(C); return OPERATOR_FINISHED; } @@ -1100,6 +1104,7 @@ static int object_select_grouped_exec(bContext *C, wmOperator *op) if (changed) { DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + ED_outliner_select_sync_from_object_tag(C); return OPERATOR_FINISHED; } @@ -1150,6 +1155,8 @@ static int object_select_all_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + ED_outliner_select_sync_from_object_tag(C); + return OPERATOR_FINISHED; } else if (any_visible == false) { @@ -1218,6 +1225,8 @@ static int object_select_same_collection_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + ED_outliner_select_sync_from_object_tag(C); + return OPERATOR_FINISHED; } @@ -1281,6 +1290,8 @@ static int object_select_mirror_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + ED_outliner_select_sync_from_object_tag(C); + return OPERATOR_FINISHED; } @@ -1369,6 +1380,9 @@ static int object_select_more_exec(bContext *C, wmOperator *UNUSED(op)) Scene *scene = CTX_data_scene(C); DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + + ED_outliner_select_sync_from_object_tag(C); + return OPERATOR_FINISHED; } else { @@ -1399,6 +1413,9 @@ static int object_select_less_exec(bContext *C, wmOperator *UNUSED(op)) Scene *scene = CTX_data_scene(C); DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + + ED_outliner_select_sync_from_object_tag(C); + return OPERATOR_FINISHED; } else { @@ -1448,6 +1465,8 @@ static int object_select_random_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + ED_outliner_select_sync_from_object_tag(C); + return OPERATOR_FINISHED; } diff --git a/source/blender/editors/object/object_shader_fx.c b/source/blender/editors/object/object_shader_fx.c index 457d2421253..db0c8c54186 100644 --- a/source/blender/editors/object/object_shader_fx.c +++ b/source/blender/editors/object/object_shader_fx.c @@ -294,7 +294,7 @@ void OBJECT_OT_shaderfx_add(wmOperatorType *ot) static bool edit_shaderfx_poll_generic(bContext *C, StructRNA *rna_type, int obtype_flag) { PointerRNA ptr = CTX_data_pointer_get_type(C, "shaderfx", rna_type); - Object *ob = (ptr.id.data) ? ptr.id.data : ED_object_active_context(C); + Object *ob = (ptr.owner_id) ? (Object *)ptr.owner_id : ED_object_active_context(C); if (!ptr.data) { CTX_wm_operator_poll_msg_set(C, "Context missing 'shaderfx'"); @@ -307,7 +307,7 @@ static bool edit_shaderfx_poll_generic(bContext *C, StructRNA *rna_type, int obt if (obtype_flag && ((1 << ob->type) & obtype_flag) == 0) { return 0; } - if (ptr.id.data && ID_IS_LINKED(ptr.id.data)) { + if (ptr.owner_id && ID_IS_LINKED(ptr.owner_id)) { return 0; } diff --git a/source/blender/editors/object/object_transform.c b/source/blender/editors/object/object_transform.c index 975aa0f5bac..2a8b306b085 100644 --- a/source/blender/editors/object/object_transform.c +++ b/source/blender/editors/object/object_transform.c @@ -77,7 +77,9 @@ #include "object_intern.h" -/*************************** Clear Transformation ****************************/ +/* -------------------------------------------------------------------- */ +/** \name Clear Transformation Utilities + * \{ */ /* clear location of object */ static void object_clear_loc(Object *ob, const bool clear_delta) @@ -284,8 +286,6 @@ static void object_clear_scale(Object *ob, const bool clear_delta) } } -/* --------------- */ - /* generic exec for clear-transform operators */ static int object_clear_transform_generic_exec(bContext *C, wmOperator *op, @@ -329,7 +329,11 @@ static int object_clear_transform_generic_exec(bContext *C, return OPERATOR_FINISHED; } -/* --------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Clear Location Operator + * \{ */ static int object_location_clear_exec(bContext *C, wmOperator *op) { @@ -359,6 +363,12 @@ void OBJECT_OT_location_clear(wmOperatorType *ot) "Clear delta location in addition to clearing the normal location transform"); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Clear Rotation Operator + * \{ */ + static int object_rotation_clear_exec(bContext *C, wmOperator *op) { return object_clear_transform_generic_exec(C, op, object_clear_rot, ANIM_KS_ROTATION_ID); @@ -387,6 +397,12 @@ void OBJECT_OT_rotation_clear(wmOperatorType *ot) "Clear delta rotation in addition to clearing the normal rotation transform"); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Clear Scale Operator + * \{ */ + static int object_scale_clear_exec(bContext *C, wmOperator *op) { return object_clear_transform_generic_exec(C, op, object_clear_scale, ANIM_KS_SCALING_ID); @@ -415,7 +431,11 @@ void OBJECT_OT_scale_clear(wmOperatorType *ot) "Clear delta scale in addition to clearing the normal scale transform"); } -/* --------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Clear Origin Operator + * \{ */ static int object_origin_clear_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -457,7 +477,11 @@ void OBJECT_OT_origin_clear(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/*************************** Apply Transformation ****************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Apply Transformation Operator + * \{ */ /* use this when the loc/size/rot of the parent has changed but the children * should stay in the same place, e.g. for apply-size-rot or object center */ @@ -744,7 +768,8 @@ static int apply_objects_internal(bContext *C, BKE_mesh_calc_normals(me); } else if (ob->type == OB_ARMATURE) { - ED_armature_transform_apply(bmain, ob, mat, do_props); + bArmature *arm = ob->data; + BKE_armature_transform(arm, mat, do_props); } else if (ob->type == OB_LATTICE) { Lattice *lt = ob->data; @@ -960,7 +985,11 @@ void OBJECT_OT_transform_apply(wmOperatorType *ot) "Modify properties such as curve vertex radius, font size and bone envelope"); } -/********************* Set Object Center ************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Set Object Center Operator + * \{ */ enum { GEOMETRY_TO_ORIGIN = 0, @@ -1152,7 +1181,8 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { Curve *cu = ob->data; - if (centermode == ORIGIN_TO_CURSOR) { /* done */ + if (centermode == ORIGIN_TO_CURSOR) { + /* done */ } else if (around == V3D_AROUND_CENTER_MEDIAN) { BKE_curve_center_median(cu, cent); @@ -1244,7 +1274,8 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) else if (ob->type == OB_MBALL) { MetaBall *mb = ob->data; - if (centermode == ORIGIN_TO_CURSOR) { /* done */ + if (centermode == ORIGIN_TO_CURSOR) { + /* done */ } else if (around == V3D_AROUND_CENTER_MEDIAN) { BKE_mball_center_median(mb, cent); @@ -1270,7 +1301,8 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) else if (ob->type == OB_LATTICE) { Lattice *lt = ob->data; - if (centermode == ORIGIN_TO_CURSOR) { /* done */ + if (centermode == ORIGIN_TO_CURSOR) { + /* done */ } else if (around == V3D_AROUND_CENTER_MEDIAN) { BKE_lattice_center_median(lt, cent); @@ -1323,11 +1355,6 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) invert_m4_m4(inverse_diff_mat, diff_mat); for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) { - continue; - } - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { float mpt[3]; mul_v3_m4v3(mpt, inverse_diff_mat, &pt->x); @@ -1509,6 +1536,8 @@ void OBJECT_OT_origin_set(wmOperatorType *ot) RNA_def_enum(ot->srna, "center", prop_set_bounds_types, V3D_AROUND_CENTER_MEDIAN, "Center", ""); } +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Transform Axis Target * diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c index 08fe5e818b2..ce385b12608 100644 --- a/source/blender/editors/object/object_vgroup.c +++ b/source/blender/editors/object/object_vgroup.c @@ -3794,7 +3794,7 @@ static int vgroup_sort_name(const void *def_a_ptr, const void *def_b_ptr) const bDeformGroup *def_a = def_a_ptr; const bDeformGroup *def_b = def_b_ptr; - return BLI_natstrcmp(def_a->name, def_b->name); + return BLI_strcasecmp_natural(def_a->name, def_b->name); } /** diff --git a/source/blender/editors/physics/particle_object.c b/source/blender/editors/physics/particle_object.c index 0999d1f3588..fbaf02b7b3f 100644 --- a/source/blender/editors/physics/particle_object.c +++ b/source/blender/editors/physics/particle_object.c @@ -186,7 +186,7 @@ static int new_particle_settings_exec(bContext *C, wmOperator *UNUSED(op)) part = BKE_particlesettings_add(bmain, "ParticleSettings"); } - ob = ptr.id.data; + ob = (Object *)ptr.owner_id; if (psys->part) { id_us_min(&psys->part->id); @@ -226,7 +226,7 @@ static int new_particle_target_exec(bContext *C, wmOperator *UNUSED(op)) Main *bmain = CTX_data_main(C); PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem); ParticleSystem *psys = ptr.data; - Object *ob = ptr.id.data; + Object *ob = (Object *)ptr.owner_id; ParticleTarget *pt; @@ -273,7 +273,7 @@ static int remove_particle_target_exec(bContext *C, wmOperator *UNUSED(op)) Main *bmain = CTX_data_main(C); PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem); ParticleSystem *psys = ptr.data; - Object *ob = ptr.id.data; + Object *ob = (Object *)ptr.owner_id; ParticleTarget *pt; @@ -323,7 +323,7 @@ static int target_move_up_exec(bContext *C, wmOperator *UNUSED(op)) { PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem); ParticleSystem *psys = ptr.data; - Object *ob = ptr.id.data; + Object *ob = (Object *)ptr.owner_id; ParticleTarget *pt; if (!psys) { @@ -363,7 +363,7 @@ static int target_move_down_exec(bContext *C, wmOperator *UNUSED(op)) { PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem); ParticleSystem *psys = ptr.data; - Object *ob = ptr.id.data; + Object *ob = (Object *)ptr.owner_id; ParticleTarget *pt; if (!psys) { @@ -1140,6 +1140,7 @@ static bool copy_particle_systems_to_object(const bContext *C, /* append to the object */ BLI_addtail(&ob_to->particlesystem, psys); + psys_unique_name(ob_to, psys, psys->name); /* add a particle system modifier for each system */ md = modifier_new(eModifierType_ParticleSystem); diff --git a/source/blender/editors/physics/physics_intern.h b/source/blender/editors/physics/physics_intern.h index 9ebbba07fdf..fc2f3d21bb6 100644 --- a/source/blender/editors/physics/physics_intern.h +++ b/source/blender/editors/physics/physics_intern.h @@ -30,7 +30,6 @@ struct PTCacheEdit; struct ParticleSystem; struct PointCache; struct Scene; -struct ViewLayer; struct wmOperatorType; /* particle_edit.c */ diff --git a/source/blender/editors/physics/physics_pointcache.c b/source/blender/editors/physics/physics_pointcache.c index bc2f1d6cef6..700a94e4f93 100644 --- a/source/blender/editors/physics/physics_pointcache.c +++ b/source/blender/editors/physics/physics_pointcache.c @@ -57,7 +57,7 @@ static bool ptcache_bake_all_poll(bContext *C) static bool ptcache_poll(bContext *C) { PointerRNA ptr = CTX_data_pointer_get_type(C, "point_cache", &RNA_PointCache); - return (ptr.data && ptr.id.data); + return (ptr.data && ptr.owner_id); } typedef struct PointCacheJob { @@ -165,7 +165,7 @@ static PTCacheBaker *ptcache_baker_create(bContext *C, wmOperator *op, bool all) if (!all) { PointerRNA ptr = CTX_data_pointer_get_type(C, "point_cache", &RNA_PointCache); - Object *ob = ptr.id.data; + Object *ob = (Object *)ptr.owner_id; PointCache *cache = ptr.data; baker->pid = BKE_ptcache_id_find(ob, baker->scene, cache); } @@ -300,7 +300,7 @@ static int ptcache_free_bake_exec(bContext *C, wmOperator *UNUSED(op)) { PointerRNA ptr = CTX_data_pointer_get_type(C, "point_cache", &RNA_PointCache); PointCache *cache = ptr.data; - Object *ob = ptr.id.data; + Object *ob = (Object *)ptr.owner_id; ptcache_free_bake(cache); @@ -312,7 +312,7 @@ static int ptcache_bake_from_cache_exec(bContext *C, wmOperator *UNUSED(op)) { PointerRNA ptr = CTX_data_pointer_get_type(C, "point_cache", &RNA_PointCache); PointCache *cache = ptr.data; - Object *ob = ptr.id.data; + Object *ob = (Object *)ptr.owner_id; cache->flag |= PTCACHE_BAKED; @@ -372,7 +372,7 @@ static int ptcache_add_new_exec(bContext *C, wmOperator *UNUSED(op)) { Scene *scene = CTX_data_scene(C); PointerRNA ptr = CTX_data_pointer_get_type(C, "point_cache", &RNA_PointCache); - Object *ob = ptr.id.data; + Object *ob = (Object *)ptr.owner_id; PointCache *cache = ptr.data; PTCacheID pid = BKE_ptcache_id_find(ob, scene, cache); @@ -392,7 +392,7 @@ static int ptcache_remove_exec(bContext *C, wmOperator *UNUSED(op)) { PointerRNA ptr = CTX_data_pointer_get_type(C, "point_cache", &RNA_PointCache); Scene *scene = CTX_data_scene(C); - Object *ob = ptr.id.data; + Object *ob = (Object *)ptr.owner_id; PointCache *cache = ptr.data; PTCacheID pid = BKE_ptcache_id_find(ob, scene, cache); diff --git a/source/blender/editors/render/render_intern.h b/source/blender/editors/render/render_intern.h index 50f0b53c037..88b913b84ca 100644 --- a/source/blender/editors/render/render_intern.h +++ b/source/blender/editors/render/render_intern.h @@ -24,7 +24,6 @@ #ifndef __RENDER_INTERN_H__ #define __RENDER_INTERN_H__ -struct RenderEngine; struct ScrArea; struct bContext; struct wmOperatorType; diff --git a/source/blender/editors/render/render_shading.c b/source/blender/editors/render/render_shading.c index 01740c13c9d..9f13431f25a 100644 --- a/source/blender/editors/render/render_shading.c +++ b/source/blender/editors/render/render_shading.c @@ -1836,7 +1836,7 @@ void SCENE_OT_freestyle_stroke_material_create(wmOperatorType *ot) static int texture_slot_move_exec(bContext *C, wmOperator *op) { - ID *id = CTX_data_pointer_get_type(C, "texture_slot", &RNA_TextureSlot).id.data; + ID *id = CTX_data_pointer_get_type(C, "texture_slot", &RNA_TextureSlot).owner_id; if (id) { MTex **mtex_ar, *mtexswap; @@ -2031,7 +2031,7 @@ static void paste_mtex_copybuf(ID *id) static int copy_mtex_exec(bContext *C, wmOperator *UNUSED(op)) { - ID *id = CTX_data_pointer_get_type(C, "texture_slot", &RNA_TextureSlot).id.data; + ID *id = CTX_data_pointer_get_type(C, "texture_slot", &RNA_TextureSlot).owner_id; if (id == NULL) { /* copying empty slot */ @@ -2046,7 +2046,7 @@ static int copy_mtex_exec(bContext *C, wmOperator *UNUSED(op)) static bool copy_mtex_poll(bContext *C) { - ID *id = CTX_data_pointer_get_type(C, "texture_slot", &RNA_TextureSlot).id.data; + ID *id = CTX_data_pointer_get_type(C, "texture_slot", &RNA_TextureSlot).owner_id; return (id != NULL); } @@ -2069,7 +2069,7 @@ void TEXTURE_OT_slot_copy(wmOperatorType *ot) static int paste_mtex_exec(bContext *C, wmOperator *UNUSED(op)) { - ID *id = CTX_data_pointer_get_type(C, "texture_slot", &RNA_TextureSlot).id.data; + ID *id = CTX_data_pointer_get_type(C, "texture_slot", &RNA_TextureSlot).owner_id; if (id == NULL) { Material *ma = CTX_data_pointer_get_type(C, "material", &RNA_Material).data; diff --git a/source/blender/editors/render/render_view.c b/source/blender/editors/render/render_view.c index cd5edcdc3f4..3154d5d0985 100644 --- a/source/blender/editors/render/render_view.c +++ b/source/blender/editors/render/render_view.c @@ -223,8 +223,8 @@ ScrArea *render_view_open(bContext *C, int mx, int my, ReportList *reports) /* get the correct image, and scale it */ sima->image = BKE_image_verify_viewer(bmain, IMA_TYPE_R_RESULT, "Render Result"); - /* if we're rendering to full screen, set appropriate hints on image editor - * so it can restore properly on pressing esc */ + /* If we're rendering to full screen, set appropriate hints on image editor + * so it can restore properly on pressing escape. */ if (sa->full) { sima->flag |= SI_FULLWINDOW; diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 1775a0c55a2..b0c9e3b9378 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -1052,16 +1052,11 @@ static void region_azones_scrollbars_initialize(ScrArea *sa, ARegion *ar) } /* *************************************************************** */ - -static void region_azones_add(const bScreen *screen, ScrArea *sa, ARegion *ar, const int alignment) +static void region_azones_add_edge(ScrArea *sa, + ARegion *ar, + const int alignment, + const bool is_fullscreen) { - const bool is_fullscreen = screen->state == SCREENFULL; - - /* Only display tab or icons when the header region is hidden - * (not the tool header - they overlap). */ - if (ar->regiontype == RGN_TYPE_TOOL_HEADER) { - return; - } /* edge code (t b l r) is along which area edge azone will be drawn */ if (alignment == RGN_ALIGN_TOP) { @@ -1076,6 +1071,25 @@ static void region_azones_add(const bScreen *screen, ScrArea *sa, ARegion *ar, c else if (alignment == RGN_ALIGN_LEFT) { region_azone_edge_initialize(sa, ar, AE_RIGHT_TO_TOPLEFT, is_fullscreen); } +} + +static void region_azones_add(const bScreen *screen, ScrArea *sa, ARegion *ar) +{ + const bool is_fullscreen = screen->state == SCREENFULL; + + /* Only display tab or icons when the header region is hidden + * (not the tool header - they overlap). */ + if (ar->regiontype == RGN_TYPE_TOOL_HEADER) { + return; + } + + region_azones_add_edge(sa, ar, RGN_ALIGN_ENUM_FROM_MASK(ar->alignment), is_fullscreen); + + /* For a split region also continue the azone edge from the next region if this region is aligned + * with the next */ + if ((ar->alignment & RGN_SPLIT_PREV) && ar->prev) { + region_azones_add_edge(sa, ar, RGN_ALIGN_ENUM_FROM_MASK(ar->prev->alignment), is_fullscreen); + } if (is_fullscreen) { fullscreen_azone_initialize(sa, ar); @@ -1251,9 +1265,6 @@ static void region_rect_recursive( else if (ED_area_is_global(sa)) { prefsizey = ED_region_global_size_y(); } - else if (ar->regiontype == RGN_TYPE_UI && sa->spacetype == SPACE_FILE) { - prefsizey = UI_UNIT_Y * 2 + (UI_UNIT_Y / 2); - } else { prefsizey = UI_DPI_FAC * (ar->sizey > 1 ? ar->sizey + 0.5f : ar->type->prefsizey); } @@ -1695,7 +1706,7 @@ void ED_area_update_region_sizes(wmWindowManager *wm, wmWindow *win, ScrArea *ar } /* Some AZones use View2D data which is only updated in region init, so call that first! */ - region_azones_add(screen, area, ar, RGN_ALIGN_ENUM_FROM_MASK(ar->alignment)); + region_azones_add(screen, area, ar); } ED_area_azones_update(area, &win->eventstate->x); @@ -1766,7 +1777,7 @@ void ED_area_initialize(wmWindowManager *wm, wmWindow *win, ScrArea *sa) } /* Some AZones use View2D data which is only updated in region init, so call that first! */ - region_azones_add(screen, sa, ar, RGN_ALIGN_ENUM_FROM_MASK(ar->alignment)); + region_azones_add(screen, sa, ar); } /* Avoid re-initializing tools while resizing the window. */ diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c index 2eb7f732534..6f8b25f782b 100644 --- a/source/blender/editors/screen/screen_context.c +++ b/source/blender/editors/screen/screen_context.c @@ -533,7 +533,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult gpd_ptr = ED_gpencil_data_get_pointers_direct((ID *)sc, sa, scene, obact, &ptr); if (gpd_ptr) { - CTX_data_pointer_set(result, ptr.id.data, ptr.type, ptr.data); + CTX_data_pointer_set(result, ptr.owner_id, ptr.type, ptr.data); return 1; } } diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index dddc33e3ad0..b37aa47aba6 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -292,33 +292,45 @@ void screen_new_activate_prepare(const wmWindow *win, bScreen *screen_new) /* used with join operator */ int area_getorientation(ScrArea *sa, ScrArea *sb) { - ScrVert *sav1, *sav2, *sav3, *sav4; - ScrVert *sbv1, *sbv2, *sbv3, *sbv4; - if (sa == NULL || sb == NULL) { return -1; } - sav1 = sa->v1; - sav2 = sa->v2; - sav3 = sa->v3; - sav4 = sa->v4; - sbv1 = sb->v1; - sbv2 = sb->v2; - sbv3 = sb->v3; - sbv4 = sb->v4; + ScrVert *saBL = sa->v1; + ScrVert *saTL = sa->v2; + ScrVert *saTR = sa->v3; + ScrVert *saBR = sa->v4; - if (sav1 == sbv4 && sav2 == sbv3) { /* sa to right of sb = W */ - return 0; + ScrVert *sbBL = sb->v1; + ScrVert *sbTL = sb->v2; + ScrVert *sbTR = sb->v3; + ScrVert *sbBR = sb->v4; + + int tolerance = U.pixelsize * 4; + + if (saBL->vec.x == sbBR->vec.x && saTL->vec.x == sbTR->vec.x) { /* sa to right of sb = W */ + if ((ABS(saBL->vec.y - sbBR->vec.y) <= tolerance) && + (ABS(saTL->vec.y - sbTR->vec.y) <= tolerance)) { + return 0; + } } - else if (sav2 == sbv1 && sav3 == sbv4) { /* sa to bottom of sb = N */ - return 1; + else if (saTL->vec.y == sbBL->vec.y && saTR->vec.y == sbBR->vec.y) { /* sa to bottom of sb = N */ + if ((ABS(saTL->vec.x - sbBL->vec.x) <= tolerance) && + (ABS(saTR->vec.x - sbBR->vec.x) <= tolerance)) { + return 1; + } } - else if (sav3 == sbv2 && sav4 == sbv1) { /* sa to left of sb = E */ - return 2; + else if (saTR->vec.x == sbTL->vec.x && saBR->vec.x == sbBL->vec.x) { /* sa to left of sb = E */ + if ((ABS(saTR->vec.y - sbTL->vec.y) <= tolerance) && + (ABS(saBR->vec.y - sbBL->vec.y) <= tolerance)) { + return 2; + } } - else if (sav1 == sbv2 && sav4 == sbv3) { /* sa on top of sb = S*/ - return 3; + else if (saBL->vec.y == sbTL->vec.y && saBR->vec.y == sbTR->vec.y) { /* sa on top of sb = S*/ + if ((ABS(saBL->vec.x - sbTL->vec.x) <= tolerance) && + (ABS(saBR->vec.x - sbTR->vec.x) <= tolerance)) { + return 3; + } } return -1; @@ -329,36 +341,50 @@ int area_getorientation(ScrArea *sa, ScrArea *sb) */ int screen_area_join(bContext *C, bScreen *scr, ScrArea *sa1, ScrArea *sa2) { - int dir; - - dir = area_getorientation(sa1, sa2); - /*printf("dir is : %i\n", dir);*/ + int dir = area_getorientation(sa1, sa2); if (dir == -1) { return 0; } - if (dir == 0) { - sa1->v1 = sa2->v1; - sa1->v2 = sa2->v2; + /* Align areas if they are not. Do sanity checking before getting here. */ + + if (dir == 0 || dir == 2) { + /* horizontal join, so vertically align source vert to target */ + sa2->v1->vec.y = sa1->v1->vec.y; /* vertical align sa1 BL */ + sa2->v2->vec.y = sa1->v2->vec.y; /* vertical align sa1 TL */ + sa2->v3->vec.y = sa1->v3->vec.y; /* vertical align sa1 TR */ + sa2->v4->vec.y = sa1->v4->vec.y; /* vertical align sa1 BR */ + } + else { + /* vertical join, so horizontally align source verts to target */ + sa2->v1->vec.x = sa1->v1->vec.x; /* vertical align sa1 BL */ + sa2->v2->vec.x = sa1->v2->vec.x; /* vertical align sa1 TL */ + sa2->v3->vec.x = sa1->v3->vec.x; /* vertical align sa1 TR */ + sa2->v4->vec.x = sa1->v4->vec.x; /* vertical align sa1 BR */ + } + + if (dir == 0) { /* sa1 to right of sa2 = W */ + sa1->v1 = sa2->v1; /* BL */ + sa1->v2 = sa2->v2; /* TL */ screen_geom_edge_add(scr, sa1->v2, sa1->v3); screen_geom_edge_add(scr, sa1->v1, sa1->v4); } - else if (dir == 1) { - sa1->v2 = sa2->v2; - sa1->v3 = sa2->v3; + else if (dir == 1) { /* sa1 to bottom of sa2 = N */ + sa1->v2 = sa2->v2; /* TL */ + sa1->v3 = sa2->v3; /* TR */ screen_geom_edge_add(scr, sa1->v1, sa1->v2); screen_geom_edge_add(scr, sa1->v3, sa1->v4); } - else if (dir == 2) { - sa1->v3 = sa2->v3; - sa1->v4 = sa2->v4; + else if (dir == 2) { /* sa1 to left of sa2 = E */ + sa1->v3 = sa2->v3; /* TR */ + sa1->v4 = sa2->v4; /* BR */ screen_geom_edge_add(scr, sa1->v2, sa1->v3); screen_geom_edge_add(scr, sa1->v1, sa1->v4); } - else if (dir == 3) { - sa1->v1 = sa2->v1; - sa1->v4 = sa2->v4; + else if (dir == 3) { /* sa1 on top of sa2 = S */ + sa1->v1 = sa2->v1; /* BL */ + sa1->v4 = sa2->v4; /* BR */ screen_geom_edge_add(scr, sa1->v1, sa1->v2); screen_geom_edge_add(scr, sa1->v3, sa1->v4); } @@ -1116,7 +1142,7 @@ void ED_screen_full_prevspace(bContext *C, ScrArea *sa) void ED_screen_restore_temp_type(bContext *C, ScrArea *sa) { - /* incase nether functions below run */ + /* In case nether functions below run. */ ED_area_tag_redraw(sa); if (sa->flag & AREA_FLAG_TEMP_TYPE) { diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 8377fd0e128..50e5597ac0c 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -1121,6 +1121,47 @@ static void SCREEN_OT_actionzone(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Area edge detection utility + * \{ */ + +static ScrEdge *screen_area_edge_from_cursor(const bContext *C, + const int cursor[2], + ScrArea **r_sa1, + ScrArea **r_sa2) +{ + wmWindow *win = CTX_wm_window(C); + bScreen *sc = CTX_wm_screen(C); + ScrEdge *actedge; + rcti window_rect; + WM_window_rect_calc(win, &window_rect); + actedge = screen_geom_area_map_find_active_scredge( + AREAMAP_FROM_SCREEN(sc), &window_rect, cursor[0], cursor[1]); + *r_sa1 = NULL; + *r_sa2 = NULL; + if (actedge == NULL) { + return NULL; + } + int borderwidth = (4 * UI_DPI_FAC); + ScrArea *sa1, *sa2; + if (screen_geom_edge_is_horizontal(actedge)) { + sa1 = BKE_screen_find_area_xy(sc, SPACE_TYPE_ANY, cursor[0], cursor[1] + borderwidth); + sa2 = BKE_screen_find_area_xy(sc, SPACE_TYPE_ANY, cursor[0], cursor[1] - borderwidth); + } + else { + sa1 = BKE_screen_find_area_xy(sc, SPACE_TYPE_ANY, cursor[0] + borderwidth, cursor[1]); + sa2 = BKE_screen_find_area_xy(sc, SPACE_TYPE_ANY, cursor[0] - borderwidth, cursor[1]); + } + bool isGlobal = ((sa1 && ED_area_is_global(sa1)) || (sa2 && ED_area_is_global(sa2))); + if (!isGlobal) { + *r_sa1 = sa1; + *r_sa2 = sa2; + } + return actedge; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Swap Area Operator * \{ */ @@ -1139,6 +1180,7 @@ static void SCREEN_OT_actionzone(wmOperatorType *ot) * callbacks: * * invoke() gets called on shift+lmb drag in action-zone + * exec() execute without any user interaction, based on properties * call init(), add handler * * modal() accept modal events while doing it @@ -1229,6 +1271,19 @@ static int area_swap_modal(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_RUNNING_MODAL; } +static int area_swap_exec(bContext *C, wmOperator *op) +{ + ScrArea *sa1, *sa2; + int cursor[2]; + RNA_int_get_array(op->ptr, "cursor", cursor); + screen_area_edge_from_cursor(C, cursor, &sa1, &sa2); + if (sa1 == NULL || sa2 == NULL) { + return OPERATOR_CANCELLED; + } + ED_area_swapspace(C, sa1, sa2); + return OPERATOR_FINISHED; +} + static void SCREEN_OT_area_swap(wmOperatorType *ot) { ot->name = "Swap Areas"; @@ -1237,10 +1292,15 @@ static void SCREEN_OT_area_swap(wmOperatorType *ot) ot->invoke = area_swap_invoke; ot->modal = area_swap_modal; - ot->poll = ED_operator_areaactive; + ot->exec = area_swap_exec; + ot->poll = screen_active_editable; ot->cancel = area_swap_cancel; ot->flag = OPTYPE_BLOCKING; + + /* rna */ + RNA_def_int_vector( + ot->srna, "cursor", 2, NULL, INT_MIN, INT_MAX, "Cursor", "", INT_MIN, INT_MAX); } /** \} */ @@ -2458,6 +2518,14 @@ static int area_max_regionsize(ScrArea *sa, ARegion *scalear, AZEdge edge) return dist; } +static bool is_split_edge(const int alignment, const AZEdge edge) +{ + return ((alignment == RGN_ALIGN_BOTTOM) && (edge == AE_TOP_TO_BOTTOMRIGHT)) || + ((alignment == RGN_ALIGN_TOP) && (edge == AE_BOTTOM_TO_TOPLEFT)) || + ((alignment == RGN_ALIGN_LEFT) && (edge == AE_RIGHT_TO_TOPLEFT)) || + ((alignment == RGN_ALIGN_RIGHT) && (edge == AE_LEFT_TO_TOPRIGHT)); +} + static int region_scale_invoke(bContext *C, wmOperator *op, const wmEvent *event) { sActionzoneData *sad = event->customdata; @@ -2476,7 +2544,16 @@ static int region_scale_invoke(bContext *C, wmOperator *op, const wmEvent *event op->customdata = rmd; rmd->az = az; - rmd->ar = az->ar; + /* special case for region within region - this allows the scale of + * the parent region if the azone edge is not the edge splitting + * both regions */ + if ((az->ar->alignment & RGN_SPLIT_PREV) && az->ar->prev && + !is_split_edge(RGN_ALIGN_ENUM_FROM_MASK(az->ar->alignment), az->edge)) { + rmd->ar = az->ar->prev; + } + else { + rmd->ar = az->ar; + } rmd->sa = sad->sa1; rmd->edge = az->edge; rmd->origx = event->x; @@ -3163,40 +3240,19 @@ static void area_join_draw_cb(const struct wmWindow *UNUSED(win), void *userdata /* validate selection inside screen, set variables OK */ /* return 0: init failed */ -/* XXX todo: find edge based on (x,y) and set other area? */ -static int area_join_init(bContext *C, wmOperator *op) +static int area_join_init(bContext *C, wmOperator *op, ScrArea *sa1, ScrArea *sa2) { - const wmWindow *win = CTX_wm_window(C); - bScreen *screen = CTX_wm_screen(C); - ScrArea *sa1, *sa2; - sAreaJoinData *jd = NULL; - int x1, y1; - int x2, y2; - - /* required properties, make negative to get return 0 if not set by caller */ - x1 = RNA_int_get(op->ptr, "min_x"); - y1 = RNA_int_get(op->ptr, "min_y"); - x2 = RNA_int_get(op->ptr, "max_x"); - y2 = RNA_int_get(op->ptr, "max_y"); - - sa1 = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, x1, y1); - if (sa1 == NULL) { - sa1 = BKE_screen_area_map_find_area_xy(&win->global_areas, SPACE_TYPE_ANY, x1, y1); - } - sa2 = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, x2, y2); - if (sa2 == NULL) { - sa2 = BKE_screen_area_map_find_area_xy(&win->global_areas, SPACE_TYPE_ANY, x2, y2); - } - if ((sa1 && ED_area_is_global(sa1)) || (sa2 && ED_area_is_global(sa2))) { - BKE_report( - op->reports, RPT_ERROR, "Global areas (Top Bar, Status Bar) do not support joining"); - return 0; + if (sa1 == NULL || sa2 == NULL) { + /* Get areas from cursor location if not specified. */ + int cursor[2]; + RNA_int_get_array(op->ptr, "cursor", cursor); + screen_area_edge_from_cursor(C, cursor, &sa1, &sa2); } - else if (sa1 == NULL || sa2 == NULL || sa1 == sa2) { + if (sa1 == NULL || sa2 == NULL) { return 0; } - jd = (sAreaJoinData *)MEM_callocN(sizeof(sAreaJoinData), "op_area_join"); + sAreaJoinData *jd = MEM_callocN(sizeof(sAreaJoinData), "op_area_join"); jd->sa1 = sa1; jd->sa2 = sa2; @@ -3249,7 +3305,7 @@ static void area_join_exit(bContext *C, wmOperator *op) static int area_join_exec(bContext *C, wmOperator *op) { - if (!area_join_init(C, op)) { + if (!area_join_init(C, op, NULL, NULL)) { return OPERATOR_CANCELLED; } @@ -3279,16 +3335,11 @@ static int area_join_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (sad->sa1 == sad->sa2) { return OPERATOR_PASS_THROUGH; } - - /* prepare operator state vars */ - RNA_int_set(op->ptr, "min_x", sad->sa1->totrct.xmin); - RNA_int_set(op->ptr, "min_y", sad->sa1->totrct.ymin); - RNA_int_set(op->ptr, "max_x", sad->sa2->totrct.xmin); - RNA_int_set(op->ptr, "max_y", sad->sa2->totrct.ymin); - } - - if (!area_join_init(C, op)) { - return OPERATOR_CANCELLED; + else { + if (!area_join_init(C, op, sad->sa1, sad->sa2)) { + return OPERATOR_CANCELLED; + } + } } /* add temp handler */ @@ -3309,7 +3360,14 @@ static int area_join_modal(bContext *C, wmOperator *op, const wmEvent *event) { bScreen *sc = CTX_wm_screen(C); wmWindow *win = CTX_wm_window(C); - sAreaJoinData *jd = (sAreaJoinData *)op->customdata; + sAreaJoinData *jd; + + if (op->customdata == NULL) { + if (!area_join_init(C, op, NULL, NULL)) { + return OPERATOR_CANCELLED; + } + } + jd = (sAreaJoinData *)op->customdata; /* execute the events */ switch (event->type) { @@ -3419,10 +3477,8 @@ static void SCREEN_OT_area_join(wmOperatorType *ot) ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL; /* rna */ - RNA_def_int(ot->srna, "min_x", -100, INT_MIN, INT_MAX, "X 1", "", INT_MIN, INT_MAX); - RNA_def_int(ot->srna, "min_y", -100, INT_MIN, INT_MAX, "Y 1", "", INT_MIN, INT_MAX); - RNA_def_int(ot->srna, "max_x", -100, INT_MIN, INT_MAX, "X 2", "", INT_MIN, INT_MAX); - RNA_def_int(ot->srna, "max_y", -100, INT_MIN, INT_MAX, "Y 2", "", INT_MIN, INT_MAX); + RNA_def_int_vector( + ot->srna, "cursor", 2, NULL, INT_MIN, INT_MAX, "Cursor", "", INT_MIN, INT_MAX); } /** \} */ @@ -3433,36 +3489,74 @@ static void SCREEN_OT_area_join(wmOperatorType *ot) static int screen_area_options_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - const wmWindow *win = CTX_wm_window(C); - const bScreen *sc = CTX_wm_screen(C); uiPopupMenu *pup; uiLayout *layout; PointerRNA ptr; - ScrEdge *actedge; - rcti window_rect; - WM_window_rect_calc(win, &window_rect); - actedge = screen_geom_area_map_find_active_scredge( - AREAMAP_FROM_SCREEN(sc), &window_rect, event->x, event->y); + ScrArea *sa1, *sa2; - if (actedge == NULL) { + if (screen_area_edge_from_cursor(C, &event->x, &sa1, &sa2) == NULL) { return OPERATOR_CANCELLED; } pup = UI_popup_menu_begin(C, WM_operatortype_name(op->type, op->ptr), ICON_NONE); layout = UI_popup_menu_layout(pup); - uiItemFullO( - layout, "SCREEN_OT_area_split", NULL, ICON_NONE, NULL, WM_OP_INVOKE_DEFAULT, 0, &ptr); - /* store initial mouse cursor position */ + /* Vertical Split */ + uiItemFullO(layout, + "SCREEN_OT_area_split", + IFACE_("Vertical Split"), + ICON_NONE, + NULL, + WM_OP_INVOKE_DEFAULT, + 0, + &ptr); + /* store initial mouse cursor position. */ + RNA_int_set_array(&ptr, "cursor", &event->x); + RNA_enum_set(&ptr, "direction", 'v'); + + /* Horizontal Split */ + uiItemFullO(layout, + "SCREEN_OT_area_split", + IFACE_("Horizontal Split"), + ICON_NONE, + NULL, + WM_OP_INVOKE_DEFAULT, + 0, + &ptr); + /* store initial mouse cursor position. */ RNA_int_set_array(&ptr, "cursor", &event->x); + RNA_enum_set(&ptr, "direction", 'h'); - uiItemFullO(layout, "SCREEN_OT_area_join", NULL, ICON_NONE, NULL, WM_OP_INVOKE_DEFAULT, 0, &ptr); - /* mouse cursor on edge, '4' can fail on wide edges... */ - RNA_int_set(&ptr, "min_x", event->x + 4); - RNA_int_set(&ptr, "min_y", event->y + 4); - RNA_int_set(&ptr, "max_x", event->x - 4); - RNA_int_set(&ptr, "max_y", event->y - 4); + if (sa1 && sa2) { + uiItemS(layout); + } + + /* Join needs two very similar areas. */ + if (sa1 && sa2 && (area_getorientation(sa1, sa2) != -1)) { + uiItemFullO(layout, + "SCREEN_OT_area_join", + IFACE_("Join Areas"), + ICON_NONE, + NULL, + WM_OP_INVOKE_DEFAULT, + 0, + &ptr); + RNA_int_set_array(&ptr, "cursor", &event->x); + } + + /* Swap just needs two areas. */ + if (sa1 && sa2) { + uiItemFullO(layout, + "SCREEN_OT_area_swap", + IFACE_("Swap Areas"), + ICON_NONE, + NULL, + WM_OP_EXEC_DEFAULT, + 0, + &ptr); + RNA_int_set_array(&ptr, "cursor", &event->x); + } UI_popup_menu_end(C, pup); @@ -3660,7 +3754,7 @@ static void SCREEN_OT_redo_last(wmOperatorType *ot) { /* identifiers */ ot->name = "Redo Last"; - ot->description = "Display menu for last action performed"; + ot->description = "Display parameters for last action performed"; ot->idname = "SCREEN_OT_redo_last"; /* api callbacks */ @@ -3711,7 +3805,7 @@ static int region_quadview_exec(bContext *C, wmOperator *op) /* some rules... */ if (ar->regiontype != RGN_TYPE_WINDOW) { - BKE_report(op->reports, RPT_ERROR, "Only window region can be 4-splitted"); + BKE_report(op->reports, RPT_ERROR, "Only window region can be 4-split"); } else if (ar->alignment == RGN_ALIGN_QSPLIT) { /* Exit quad-view */ @@ -3742,7 +3836,7 @@ static int region_quadview_exec(bContext *C, wmOperator *op) rv3d->viewlock = 0; rv3d->rflag &= ~RV3D_CLIPPING; - /* accumulate locks, incase they're mixed */ + /* Accumulate locks, in case they're mixed. */ for (ar_iter = sa->regionbase.first; ar_iter; ar_iter = ar_iter->next) { if (ar_iter->regiontype == RGN_TYPE_WINDOW) { RegionView3D *rv3d_iter = ar_iter->regiondata; @@ -3764,7 +3858,7 @@ static int region_quadview_exec(bContext *C, wmOperator *op) WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL); } else if (ar->next) { - BKE_report(op->reports, RPT_ERROR, "Only last region can be 4-splitted"); + BKE_report(op->reports, RPT_ERROR, "Only last region can be 4-split"); } else { /* Enter quad-view */ @@ -3842,6 +3936,65 @@ static void SCREEN_OT_region_quadview(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Region Toggle Operator + * \{ */ + +static int region_toggle_exec(bContext *C, wmOperator *op) +{ + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "region_type"); + ARegion *region; + + if (RNA_property_is_set(op->ptr, prop)) { + region = BKE_area_find_region_type(CTX_wm_area(C), RNA_property_enum_get(op->ptr, prop)); + } + else { + region = CTX_wm_region(C); + } + + if (region) { + ED_region_toggle_hidden(C, region); + } + ED_region_tag_redraw(region); + + return OPERATOR_FINISHED; +} + +static bool region_toggle_poll(bContext *C) +{ + ScrArea *area = CTX_wm_area(C); + + /* don't flip anything around in topbar */ + if (area && area->spacetype == SPACE_TOPBAR) { + CTX_wm_operator_poll_msg_set(C, "Toggling regions in the Top-bar is not allowed"); + return 0; + } + + return ED_operator_areaactive(C); +} + +static void SCREEN_OT_region_toggle(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Toggle Region"; + ot->idname = "SCREEN_OT_region_toggle"; + ot->description = "Hide or unhide the region"; + + /* api callbacks */ + ot->exec = region_toggle_exec; + ot->poll = region_toggle_poll; + ot->flag = 0; + + RNA_def_enum(ot->srna, + "region_type", + rna_enum_region_type_items, + 0, + "Region Type", + "Type of the region to toggle"); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Region Flip Operator * \{ */ @@ -4714,7 +4867,7 @@ static void SCREEN_OT_userpref_show(struct wmOperatorType *ot) static int drivers_editor_show_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - PointerRNA ptr = {{NULL}}; + PointerRNA ptr = {NULL}; PropertyRNA *prop = NULL; int index = -1; uiBut *but = NULL; @@ -4775,6 +4928,40 @@ static void SCREEN_OT_drivers_editor_show(struct wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Show Info Log Operator + * \{ */ + +static int info_log_show_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + int sizex = 900 * UI_DPI_FAC; + int sizey = 580 * UI_DPI_FAC; + int shift_y = 480; + + /* changes context! */ + if (WM_window_open_temp(C, event->x, event->y + shift_y, sizex, sizey, WM_WINDOW_INFO) != NULL) { + return OPERATOR_FINISHED; + } + else { + BKE_report(op->reports, RPT_ERROR, "Failed to open window!"); + return OPERATOR_CANCELLED; + } +} + +static void SCREEN_OT_info_log_show(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Show Info Log"; + ot->description = "Show info log in a separate window"; + ot->idname = "SCREEN_OT_info_log_show"; + + /* api callbacks */ + ot->invoke = info_log_show_invoke; + ot->poll = ED_operator_screenactive; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name New Screen Operator * \{ */ @@ -4903,7 +5090,9 @@ static void region_blend_end(bContext *C, ARegion *ar, const bool is_running) WM_event_remove_timer(CTX_wm_manager(C), NULL, ar->regiontimer); /* frees rgi */ ar->regiontimer = NULL; } -/* assumes that *ar itself is not a splitted version from previous region */ +/** + * \note Assumes that \a ar itself is not a split version from previous region. + */ void ED_region_visibility_change_update_animated(bContext *C, ScrArea *sa, ARegion *ar) { wmWindowManager *wm = CTX_wm_manager(C); @@ -5231,6 +5420,7 @@ void ED_operatortypes_screen(void) WM_operatortype_append(SCREEN_OT_area_swap); WM_operatortype_append(SCREEN_OT_region_quadview); WM_operatortype_append(SCREEN_OT_region_scale); + WM_operatortype_append(SCREEN_OT_region_toggle); WM_operatortype_append(SCREEN_OT_region_flip); WM_operatortype_append(SCREEN_OT_header_toggle_menus); WM_operatortype_append(SCREEN_OT_region_context_menu); @@ -5241,6 +5431,7 @@ void ED_operatortypes_screen(void) WM_operatortype_append(SCREEN_OT_screenshot); WM_operatortype_append(SCREEN_OT_userpref_show); WM_operatortype_append(SCREEN_OT_drivers_editor_show); + WM_operatortype_append(SCREEN_OT_info_log_show); WM_operatortype_append(SCREEN_OT_region_blend); WM_operatortype_append(SCREEN_OT_space_type_set_or_cycle); WM_operatortype_append(SCREEN_OT_space_context_cycle); diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index 65e10f98753..4a552fb3744 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -45,13 +45,17 @@ #include "BKE_node.h" #include "BKE_paint.h" #include "BKE_colortools.h" +#include "BKE_object.h" #include "WM_api.h" +#include "wm_cursors.h" #include "IMB_imbuf_types.h" #include "ED_view3d.h" +#include "DEG_depsgraph.h" + #include "GPU_draw.h" #include "GPU_immediate.h" #include "GPU_immediate_util.h" @@ -602,7 +606,7 @@ static bool sculpt_get_brush_geometry(bContext *C, /* Draw an overlay that shows what effect the brush's texture will * have on brush strength */ -static void paint_draw_tex_overlay(UnifiedPaintSettings *ups, +static bool paint_draw_tex_overlay(UnifiedPaintSettings *ups, Brush *brush, ViewContext *vc, int x, @@ -622,7 +626,7 @@ static void paint_draw_tex_overlay(UnifiedPaintSettings *ups, if (!(mtex->tex) || !((mtex->brush_map_mode == MTEX_MAP_MODE_STENCIL) || (valid && ELEM(mtex->brush_map_mode, MTEX_MAP_MODE_VIEW, MTEX_MAP_MODE_TILED)))) { - return; + return false; } if (load_tex(brush, vc, zoom, col, primary)) { @@ -728,18 +732,19 @@ static void paint_draw_tex_overlay(UnifiedPaintSettings *ups, GPU_matrix_pop(); } } + return true; } /* Draw an overlay that shows what effect the brush's texture will * have on brush strength */ -static void paint_draw_cursor_overlay( +static bool paint_draw_cursor_overlay( UnifiedPaintSettings *ups, Brush *brush, ViewContext *vc, int x, int y, float zoom) { rctf quad; /* check for overlay mode */ if (!(brush->overlay_flags & BRUSH_OVERLAY_CURSOR)) { - return; + return false; } if (load_tex_cursor(brush, vc, zoom)) { @@ -811,9 +816,10 @@ static void paint_draw_cursor_overlay( GPU_matrix_pop(); } } + return true; } -static void paint_draw_alpha_overlay(UnifiedPaintSettings *ups, +static bool paint_draw_alpha_overlay(UnifiedPaintSettings *ups, Brush *brush, ViewContext *vc, int x, @@ -824,6 +830,9 @@ static void paint_draw_alpha_overlay(UnifiedPaintSettings *ups, /* Color means that primary brush texture is colored and * secondary is used for alpha/mask control. */ bool col = ELEM(mode, PAINT_MODE_TEXTURE_3D, PAINT_MODE_TEXTURE_2D, PAINT_MODE_VERTEX); + + bool alpha_overlay_active = false; + eOverlayControlFlags flags = BKE_paint_get_overlay_flags(); gpuPushAttr(GPU_DEPTH_BUFFER_BIT | GPU_BLEND_BIT); @@ -836,26 +845,28 @@ static void paint_draw_alpha_overlay(UnifiedPaintSettings *ups, /* Colored overlay should be drawn separately. */ if (col) { if (!(flags & PAINT_OVERLAY_OVERRIDE_PRIMARY)) { - paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, true, true); + alpha_overlay_active = paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, true, true); } if (!(flags & PAINT_OVERLAY_OVERRIDE_SECONDARY)) { - paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, false, false); + alpha_overlay_active = paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, false, false); } if (!(flags & PAINT_OVERLAY_OVERRIDE_CURSOR)) { - paint_draw_cursor_overlay(ups, brush, vc, x, y, zoom); + alpha_overlay_active = paint_draw_cursor_overlay(ups, brush, vc, x, y, zoom); } } else { if (!(flags & PAINT_OVERLAY_OVERRIDE_PRIMARY) && (mode != PAINT_MODE_WEIGHT)) { - paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, false, true); + alpha_overlay_active = paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, false, true); } if (!(flags & PAINT_OVERLAY_OVERRIDE_CURSOR)) { - paint_draw_cursor_overlay(ups, brush, vc, x, y, zoom); + alpha_overlay_active = paint_draw_cursor_overlay(ups, brush, vc, x, y, zoom); } } GPU_matrix_pop(); gpuPopAttr(); + + return alpha_overlay_active; } BLI_INLINE void draw_tri_point( @@ -1074,6 +1085,98 @@ static bool ommit_cursor_drawing(Paint *paint, ePaintMode mode, Brush *brush) return true; } +static void cursor_draw_point_screen_space(const uint gpuattr, + const ARegion *ar, + float true_location[3], + float obmat[4][4]) +{ + float translation_vertex_cursor[3], location[3]; + copy_v3_v3(location, true_location); + mul_m4_v3(obmat, location); + ED_view3d_project(ar, location, translation_vertex_cursor); + imm_draw_circle_fill_3d( + gpuattr, translation_vertex_cursor[0], translation_vertex_cursor[1], 3, 10); +} + +static void cursor_draw_tiling_preview(const uint gpuattr, + const ARegion *ar, + float true_location[3], + Sculpt *sd, + Object *ob, + float radius) +{ + BoundBox *bb = BKE_object_boundbox_get(ob); + float orgLoc[3], location[3]; + int dim, tile_pass = 0; + int start[3]; + int end[3]; + int cur[3]; + const float *bbMin = bb->vec[0]; + const float *bbMax = bb->vec[6]; + const float *step = sd->paint.tile_offset; + + copy_v3_v3(orgLoc, true_location); + for (dim = 0; dim < 3; ++dim) { + if ((sd->paint.symmetry_flags & (PAINT_TILE_X << dim)) && step[dim] > 0) { + start[dim] = (bbMin[dim] - orgLoc[dim] - radius) / step[dim]; + end[dim] = (bbMax[dim] - orgLoc[dim] + radius) / step[dim]; + } + else + start[dim] = end[dim] = 0; + } + copy_v3_v3_int(cur, start); + for (cur[0] = start[0]; cur[0] <= end[0]; cur[0]++) { + for (cur[1] = start[1]; cur[1] <= end[1]; cur[1]++) { + for (cur[2] = start[2]; cur[2] <= end[2]; cur[2]++) { + if (!cur[0] && !cur[1] && !cur[2]) + continue; /* skip tile at orgLoc, this was already handled before all others */ + tile_pass++; + for (dim = 0; dim < 3; dim++) { + location[dim] = cur[dim] * step[dim] + orgLoc[dim]; + } + cursor_draw_point_screen_space(gpuattr, ar, location, ob->obmat); + } + } + } +} + +static void cursor_draw_point_with_symmetry(const uint gpuattr, + const ARegion *ar, + const float true_location[3], + Sculpt *sd, + Object *ob, + float radius) +{ + const char symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; + float location[3], symm_rot_mat[4][4]; + + for (int i = 0; i <= symm; ++i) { + if (i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) { + + /* Axis Symmetry */ + flip_v3_v3(location, true_location, (char)i); + cursor_draw_point_screen_space(gpuattr, ar, location, ob->obmat); + + /* Tiling */ + cursor_draw_tiling_preview(gpuattr, ar, location, sd, ob, radius); + + /* Radial Symmetry */ + for (char raxis = 0; raxis < 3; raxis++) { + for (int r = 1; r < sd->radial_symm[raxis]; r++) { + float angle = 2 * M_PI * r / sd->radial_symm[(int)raxis]; + flip_v3_v3(location, true_location, (char)i); + unit_m4(symm_rot_mat); + rotate_m4(symm_rot_mat, raxis + 'X', angle); + mul_m4_v3(symm_rot_mat, location); + + cursor_draw_tiling_preview(gpuattr, ar, location, sd, ob, radius); + cursor_draw_point_screen_space(gpuattr, ar, location, ob->obmat); + } + } + } + } +} + static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) { Scene *scene = CTX_data_scene(C); @@ -1121,7 +1224,7 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) } /* draw overlay */ - paint_draw_alpha_overlay(ups, brush, &vc, x, y, zoomx, mode); + bool alpha_overlay_active = paint_draw_alpha_overlay(ups, brush, &vc, x, y, zoomx, mode); /* TODO: as sculpt and other paint modes are unified, this * special mode of drawing will go away */ @@ -1158,12 +1261,12 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) } /* make lines pretty */ - GPU_line_width(1.0f); + GPU_line_width(2.0f); GPU_blend(true); /* TODO: also set blend mode? */ GPU_line_smooth(true); - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); /* set brush color */ immUniformColor3fvAlpha(outline_col, outline_alpha); @@ -1176,7 +1279,103 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) /* outer at half alpha */ immUniformColor3fvAlpha(outline_col, outline_alpha * 0.5f); } - imm_draw_circle_wire_2d(pos, translation[0], translation[1], final_radius, 40); + + /* Only sculpt mode cursor for now */ + + /* Disable for PBVH_GRIDS */ + SculptSession *ss = vc.obact->sculpt; + bool is_multires = ss && ss->pbvh && BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS; + + if ((mode == PAINT_MODE_SCULPT) && ss && !is_multires && + !(brush->falloff_shape & BRUSH_AIRBRUSH)) { + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + wmWindow *win = CTX_wm_window(C); + + /* Update WM mouse cursor, disable when the 3D brush cursor is enabled */ + if (sd->paint.brush->overlay_flags & BRUSH_OVERLAY_CURSOR) { + WM_cursor_set(win, CURSOR_STD); + } + else { + WM_cursor_set(win, CURSOR_EDIT); + } + + if (!ups->stroke_active) { + SculptCursorGeometryInfo gi; + float mouse[2] = {x - ar->winrct.xmin, y - ar->winrct.ymin}; + if (sculpt_cursor_geometry_info_update(C, &gi, mouse, true) && !alpha_overlay_active) { + + float rds; + if (!BKE_brush_use_locked_size(scene, brush)) { + rds = paint_calc_object_space_radius(&vc, gi.location, BKE_brush_size_get(scene, brush)); + } + else { + rds = BKE_brush_unprojected_radius_get(scene, brush); + } + + wmViewport(&ar->winrct); + + /* Draw 3D active vertex preview with symmetry*/ + if (len_v3v3(gi.active_vertex_co, gi.location) < rds) { + cursor_draw_point_with_symmetry(pos, ar, gi.active_vertex_co, sd, vc.obact, rds); + } + + /* Draw 3D brush cursor */ + GPU_matrix_push_projection(); + ED_view3d_draw_setup_view(CTX_wm_window(C), + CTX_data_depsgraph_pointer(C), + CTX_data_scene(C), + ar, + CTX_wm_view3d(C), + NULL, + NULL, + NULL); + + float cursor_trans[4][4], cursor_rot[4][4]; + float z_axis[4] = {0.0f, 0.0f, 1.0f, 0.0f}; + float quat[4]; + + copy_m4_m4(cursor_trans, vc.obact->obmat); + translate_m4(cursor_trans, gi.location[0], gi.location[1], gi.location[2]); + rotation_between_vecs_to_quat(quat, z_axis, gi.normal); + quat_to_mat4(cursor_rot, quat); + + GPU_matrix_push(); + GPU_matrix_mul(cursor_trans); + GPU_matrix_mul(cursor_rot); + imm_draw_circle_wire_3d(pos, 0, 0, rds, 40); + GPU_matrix_pop(); + + GPU_matrix_pop_projection(); + + wmWindowViewport(win); + } + else { + /* Draw default cursor when the mouse is not over the mesh or there are no supported + * overlays active */ + GPU_line_width(1.0f); + imm_draw_circle_wire_3d(pos, translation[0], translation[1], final_radius, 40); + } + } + else { + if (vc.obact->sculpt->cache && !vc.obact->sculpt->cache->first_time) { + /* Draw cursor location preview when the stroke is active using the data from StrokeCache + */ + float cursor_location[3]; + wmViewport(&ar->winrct); + copy_v3_v3(cursor_location, ss->cache->true_location); + if (ss->cache->brush->sculpt_tool == SCULPT_TOOL_GRAB) { + add_v3_v3(cursor_location, ss->cache->grab_delta); + } + cursor_draw_point_with_symmetry(pos, ar, cursor_location, sd, vc.obact, ss->cache->radius); + wmWindowViewport(win); + } + } + } + else { + /* Draw default cursor in unsupported modes */ + GPU_line_width(1.0f); + imm_draw_circle_wire_3d(pos, translation[0], translation[1], final_radius, 40); + } immUnbindProgram(); diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 342d0b6e820..397b2981ace 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -1719,9 +1719,9 @@ static float project_paint_uvpixel_mask(const ProjPaintState *ps, normalize_v3(no); } else { - /* incase the */ #if 1 - /* normalizing per pixel isn't optimal, we could cache or check ps->*/ + /* In case the normalizing per pixel isn't optimal, + * we could cache or access from evaluated mesh. */ normal_tri_v3(no, ps->mvert_eval[lt_vtri[0]].co, ps->mvert_eval[lt_vtri[1]].co, diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index ea4814857ba..5efedf69fe4 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -33,7 +33,6 @@ struct ListBase; struct MTex; struct Object; struct Paint; -struct PaintCurve; struct PaintStroke; struct PointerRNA; struct RegionView3D; diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index 694dae49d30..b4b1ae3b0af 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -57,6 +57,7 @@ #include "IMB_imbuf_types.h" #include "paint_intern.h" +#include "sculpt_intern.h" #include <float.h> #include <math.h> @@ -93,6 +94,8 @@ typedef struct PaintStroke { int cur_sample; float last_mouse_position[2]; + float last_scene_space_position[3]; + bool stroke_over_mesh; /* space distance covered so far */ float stroke_distance; @@ -233,6 +236,17 @@ static bool paint_tool_require_location(Brush *brush, ePaintMode mode) return true; } +static bool paint_stroke_use_scene_spacing(Brush *brush, ePaintMode mode) +{ + switch (mode) { + case PAINT_MODE_SCULPT: + return brush->flag & BRUSH_SCENE_SPACING; + default: + break; + } + return false; +} + static bool paint_tool_require_inbetween_mouse_events(Brush *brush, ePaintMode mode) { switch (mode) { @@ -523,6 +537,10 @@ static void paint_brush_stroke_add_step(bContext *C, copy_v2_v2(stroke->last_mouse_position, mouse_in); stroke->last_pressure = pressure; + if (paint_stroke_use_scene_spacing(brush, mode)) { + sculpt_stroke_get_location(C, stroke->last_scene_space_position, stroke->last_mouse_position); + } + if (paint_stroke_use_jitter(mode, brush, stroke->stroke_mode == BRUSH_STROKE_INVERT)) { float delta[2]; float factor = stroke->zoom_2d; @@ -600,14 +618,32 @@ static bool paint_smooth_stroke(PaintStroke *stroke, return true; } -static float paint_space_stroke_spacing(const Scene *scene, +static float paint_space_stroke_spacing(bContext *C, + const Scene *scene, PaintStroke *stroke, float size_pressure, float spacing_pressure) { - /* brushes can have a minimum size of 1.0 but with pressure it can be smaller then a pixel - * causing very high step sizes, hanging blender [#32381] */ - const float size_clamp = max_ff(1.0f, BKE_brush_size_get(scene, stroke->brush) * size_pressure); + Paint *paint = BKE_paint_get_active_from_context(C); + ePaintMode mode = BKE_paintmode_get_active_from_context(C); + Brush *brush = BKE_paint_brush(paint); + float size_clamp = 0.0f; + float size = BKE_brush_size_get(scene, stroke->brush) * size_pressure; + if (paint_stroke_use_scene_spacing(brush, mode)) { + if (!BKE_brush_use_locked_size(scene, brush)) { + size_clamp = paint_calc_object_space_radius( + &stroke->vc, stroke->last_scene_space_position, size); + } + else { + size_clamp = BKE_brush_unprojected_radius_get(scene, brush) * size_pressure; + } + } + else { + /* brushes can have a minimum size of 1.0 but with pressure it can be smaller then a pixel + * causing very high step sizes, hanging blender [#32381] */ + size_clamp = max_ff(1.0f, size); + } + float spacing = stroke->brush->spacing; /* apply spacing pressure */ @@ -619,7 +655,12 @@ static float paint_space_stroke_spacing(const Scene *scene, * the fact that brush can be scaled there. */ spacing *= stroke->zoom_2d; - return max_ff(1.0, size_clamp * spacing / 50.0f); + if (paint_stroke_use_scene_spacing(brush, mode)) { + return size_clamp * spacing / 50.0f; + } + else { + return max_ff(1.0, size_clamp * spacing / 50.0f); + } } static float paint_stroke_overlapped_curve(Brush *br, float x, float spacing) @@ -677,14 +718,18 @@ static float paint_stroke_integrate_overlap(Brush *br, float factor) } } -static float paint_space_stroke_spacing_variable( - const Scene *scene, PaintStroke *stroke, float pressure, float dpressure, float length) +static float paint_space_stroke_spacing_variable(bContext *C, + const Scene *scene, + PaintStroke *stroke, + float pressure, + float dpressure, + float length) { if (BKE_brush_use_size_pressure(scene, stroke->brush)) { /* use pressure to modify size. set spacing so that at 100%, the circles * are aligned nicely with no overlap. for this the spacing needs to be * the average of the previous and next size. */ - float s = paint_space_stroke_spacing(scene, stroke, 1.0f, pressure); + float s = paint_space_stroke_spacing(C, scene, stroke, 1.0f, pressure); float q = s * dpressure / (2.0f * length); float pressure_fac = (1.0f + q) / (1.0f - q); @@ -692,14 +737,15 @@ static float paint_space_stroke_spacing_variable( float new_size_pressure = stroke->last_pressure * pressure_fac; /* average spacing */ - float last_spacing = paint_space_stroke_spacing(scene, stroke, last_size_pressure, pressure); - float new_spacing = paint_space_stroke_spacing(scene, stroke, new_size_pressure, pressure); + float last_spacing = paint_space_stroke_spacing( + C, scene, stroke, last_size_pressure, pressure); + float new_spacing = paint_space_stroke_spacing(C, scene, stroke, new_size_pressure, pressure); return 0.5f * (last_spacing + new_spacing); } else { /* no size pressure */ - return paint_space_stroke_spacing(scene, stroke, 1.0f, pressure); + return paint_space_stroke_spacing(C, scene, stroke, 1.0f, pressure); } } @@ -711,29 +757,57 @@ static int paint_space_stroke(bContext *C, float final_pressure) { const Scene *scene = CTX_data_scene(C); + ARegion *ar = CTX_wm_region(C); PaintStroke *stroke = op->customdata; UnifiedPaintSettings *ups = stroke->ups; + Paint *paint = BKE_paint_get_active_from_context(C); + ePaintMode mode = BKE_paintmode_get_active_from_context(C); + Brush *brush = BKE_paint_brush(paint); int cnt = 0; float pressure, dpressure; float mouse[2], dmouse[2]; + float scene_space_position[3], d_scene_space_position[3], final_scene_space_position[3]; float length; - float no_pressure_spacing = paint_space_stroke_spacing(scene, stroke, 1.0f, 1.0f); - - sub_v2_v2v2(dmouse, final_mouse, stroke->last_mouse_position); - + float no_pressure_spacing = paint_space_stroke_spacing(C, scene, stroke, 1.0f, 1.0f); pressure = stroke->last_pressure; dpressure = final_pressure - stroke->last_pressure; - + sub_v2_v2v2(dmouse, final_mouse, stroke->last_mouse_position); length = normalize_v2(dmouse); + if (paint_stroke_use_scene_spacing(brush, mode)) { + bool hit = sculpt_stroke_get_location(C, scene_space_position, final_mouse); + if (hit && stroke->stroke_over_mesh) { + sub_v3_v3v3(d_scene_space_position, scene_space_position, stroke->last_scene_space_position); + length = len_v3(d_scene_space_position); + stroke->stroke_over_mesh = true; + } + else { + length = 0.0f; + stroke->stroke_over_mesh = hit; + if (stroke->stroke_over_mesh) { + copy_v3_v3(stroke->last_scene_space_position, scene_space_position); + } + } + } + while (length > 0.0f) { float spacing = paint_space_stroke_spacing_variable( - scene, stroke, pressure, dpressure, length); + C, scene, stroke, pressure, dpressure, length); if (length >= spacing) { - mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing; - mouse[1] = stroke->last_mouse_position[1] + dmouse[1] * spacing; + if (paint_stroke_use_scene_spacing(brush, mode)) { + normalize_v3(d_scene_space_position); + mul_v3_v3fl(final_scene_space_position, d_scene_space_position, spacing); + add_v3_v3v3(final_scene_space_position, + stroke->last_scene_space_position, + final_scene_space_position); + ED_view3d_project(ar, final_scene_space_position, mouse); + } + else { + mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing; + mouse[1] = stroke->last_mouse_position[1] + dmouse[1] * spacing; + } pressure = stroke->last_pressure + (spacing / length) * dpressure; ups->overlap_factor = paint_stroke_integrate_overlap(stroke->brush, @@ -1079,7 +1153,7 @@ static bool paint_stroke_curve_end(bContext *C, wmOperator *op, PaintStroke *str if (br->flag & BRUSH_CURVE) { UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; const Scene *scene = CTX_data_scene(C); - const float spacing = paint_space_stroke_spacing(scene, stroke, 1.0f, 1.0f); + const float spacing = paint_space_stroke_spacing(C, scene, stroke, 1.0f, 1.0f); PaintCurve *pc = br->paint_curve; PaintCurvePoint *pcp; float length_residue = 0.0f; @@ -1250,6 +1324,10 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) if (!stroke->stroke_started) { stroke->last_pressure = sample_average.pressure; copy_v2_v2(stroke->last_mouse_position, sample_average.mouse); + if (paint_stroke_use_scene_spacing(br, mode)) { + stroke->stroke_over_mesh = sculpt_stroke_get_location( + C, stroke->last_scene_space_position, sample_average.mouse); + } stroke->stroke_started = stroke->test_start(C, op, sample_average.mouse); BLI_assert((stroke->stroke_started & ~1) == 0); /* 0/1 */ diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 440c4d42cae..5aa913ad006 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -96,7 +96,18 @@ /* Do not use these functions while working with PBVH_GRIDS data in SculptSession */ -/* TODO: why is this kept, should it be removed? */ +static float *sculpt_vertex_co_get(SculptSession *ss, int index) +{ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_FACES: + return ss->mvert[index].co; + case PBVH_BMESH: + return BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index)->co; + default: + return NULL; + } +} + #if 0 /* UNUSED */ static int sculpt_active_vertex_get(SculptSession *ss) @@ -136,18 +147,6 @@ static void sculpt_vertex_normal_get(SculptSession *ss, int index, float no[3]) } } -static float *sculpt_vertex_co_get(SculptSession *ss, int index) -{ - switch (BKE_pbvh_type(ss->pbvh)) { - case PBVH_FACES: - return ss->mvert[index].co; - case PBVH_BMESH: - return BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index)->co; - default: - return NULL; - } -} - static void sculpt_vertex_co_set(SculptSession *ss, int index, float co[3]) { switch (BKE_pbvh_type(ss->pbvh)) { @@ -748,17 +747,26 @@ void ED_sculpt_redraw_planes_get(float planes[4][4], ARegion *ar, Object *ob) void sculpt_brush_test_init(SculptSession *ss, SculptBrushTest *test) { - RegionView3D *rv3d = ss->cache->vc->rv3d; + RegionView3D *rv3d = ss->cache ? ss->cache->vc->rv3d : ss->rv3d; + + test->radius_squared = ss->cache ? ss->cache->radius_squared : + ss->cursor_radius * ss->cursor_radius; + if (ss->cache) { + copy_v3_v3(test->location, ss->cache->location); + test->mirror_symmetry_pass = ss->cache->mirror_symmetry_pass; + } + else { + copy_v3_v3(test->location, ss->cursor_location); + test->mirror_symmetry_pass = 0; + } - test->radius_squared = ss->cache->radius_squared; - copy_v3_v3(test->location, ss->cache->location); test->dist = 0.0f; /* just for initialize */ /* Only for 2D projection. */ zero_v4(test->plane_view); zero_v4(test->plane_tool); - test->mirror_symmetry_pass = ss->cache->mirror_symmetry_pass; + test->mirror_symmetry_pass = ss->cache ? ss->cache->mirror_symmetry_pass : 0; if (rv3d->rflag & RV3D_CLIPPING) { test->clip_rv3d = rv3d; @@ -1050,7 +1058,7 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata, int private_count[2] = {0}; bool use_original = false; - if (ss->cache->original) { + if (ss->cache && ss->cache->original) { unode = sculpt_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS); use_original = (unode->co || unode->bm_entry); } @@ -1059,6 +1067,13 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata, SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( ss, &test, data->brush->falloff_shape); + /* Update the test radius to sample the normal using the normal radius of the brush */ + if (data->brush->ob_mode == OB_MODE_SCULPT) { + float test_radius = sqrtf(test.radius_squared); + test_radius *= data->brush->normal_radius_factor; + test.radius_squared = test_radius * test_radius; + } + /* when the mesh is edited we can't rely on original coords * (original mesh may not even have verts in brush radius) */ if (use_original && data->has_bm_orco) { @@ -1120,6 +1135,8 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata, const float *no; int flip_index; + data->any_vertex_sampled = true; + if (use_original) { normal_short_to_float_v3(no_buf, no_s); no = no_buf; @@ -1134,7 +1151,8 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata, } } - flip_index = (dot_v3v3(ss->cache->view_normal, no) <= 0.0f); + flip_index = (dot_v3v3(ss->cache ? ss->cache->view_normal : ss->cursor_view_normal, no) <= + 0.0f); if (area_cos) { add_v3_v3(private_co[flip_index], co); } @@ -1223,7 +1241,7 @@ static void calc_area_normal( } /* expose 'calc_area_normal' externally. */ -void sculpt_pbvh_calc_area_normal(const Brush *brush, +bool sculpt_pbvh_calc_area_normal(const Brush *brush, Object *ob, PBVHNode **nodes, int totnode, @@ -1249,6 +1267,7 @@ void sculpt_pbvh_calc_area_normal(const Brush *brush, .area_cos = NULL, .area_nos = area_nos, .count = count, + .any_vertex_sampled = false, }; BLI_mutex_init(&data.mutex); @@ -1265,6 +1284,8 @@ void sculpt_pbvh_calc_area_normal(const Brush *brush, break; } } + + return data.any_vertex_sampled; } /* this calculates flatten center and area normal together, @@ -1508,7 +1529,8 @@ float tex_strength(SculptSession *ss, bool sculpt_search_sphere_cb(PBVHNode *node, void *data_v) { SculptSearchSphereData *data = data_v; - float *center = data->ss->cache->location, nearest[3]; + float *center, nearest[3]; + center = data->ss->cache ? data->ss->cache->location : data->ss->cursor_location; float t[3], bb_min[3], bb_max[3]; int i; @@ -1585,12 +1607,13 @@ static PBVHNode **sculpt_pbvh_gather_generic(Object *ob, SculptSession *ss = ob->sculpt; PBVHNode **nodes = NULL; - /* Build a list of all nodes that are potentially within the brush's area of influence */ + /* Build a list of all nodes that are potentially within the cursor or brush's area of influence + */ if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { SculptSearchSphereData data = { .ss = ss, .sd = sd, - .radius_squared = SQUARE(ss->cache->radius * radius_scale), + .radius_squared = ss->cache ? SQUARE(ss->cache->radius * radius_scale) : ss->cursor_radius, .original = use_original, }; BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, r_totnode); @@ -1602,7 +1625,7 @@ static PBVHNode **sculpt_pbvh_gather_generic(Object *ob, SculptSearchCircleData data = { .ss = ss, .sd = sd, - .radius_squared = SQUARE(ss->cache->radius * radius_scale), + .radius_squared = ss->cache ? SQUARE(ss->cache->radius * radius_scale) : ss->cursor_radius, .original = use_original, .dist_ray_to_aabb_precalc = &dist_ray_to_aabb_precalc, }; @@ -1964,10 +1987,14 @@ typedef struct SculptDoBrushSmoothGridDataChunk { typedef struct { SculptSession *ss; const float *ray_start; + const float *ray_normal; bool hit; float depth; bool original; + int active_vertex_index; + float *face_normal; + struct IsectRayPrecalc isect_precalc; } SculptRaycastData; @@ -3987,7 +4014,7 @@ static void do_gravity(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, fl BLI_task_parallel_range(0, totnode, &data, do_gravity_task_cb_ex, &settings); } -void sculpt_vertcos_to_key(Object *ob, KeyBlock *kb, float (*vertCos)[3]) +void sculpt_vertcos_to_key(Object *ob, KeyBlock *kb, const float (*vertCos)[3]) { Mesh *me = (Mesh *)ob->data; float(*ofs)[3] = NULL; @@ -4349,7 +4376,7 @@ static void sculpt_update_keyblock(Object *ob) vertCos = ss->orig_cos; } else { - vertCos = BKE_pbvh_get_vertCos(ss->pbvh); + vertCos = BKE_pbvh_vert_coords_alloc(ss->pbvh); } if (vertCos) { @@ -4513,7 +4540,7 @@ static void do_tiled( float orgLoc[3]; /* position of the "prototype" stroke for tiling */ copy_v3_v3(orgLoc, cache->location); - for (dim = 0; dim < 3; ++dim) { + for (dim = 0; dim < 3; dim++) { if ((sd->paint.symmetry_flags & (PAINT_TILE_X << dim)) && step[dim] > 0) { start[dim] = (bbMin[dim] - orgLoc[dim] - radius) / step[dim]; end[dim] = (bbMax[dim] - orgLoc[dim] + radius) / step[dim]; @@ -4529,16 +4556,16 @@ static void do_tiled( /* now do it for all the tiles */ copy_v3_v3_int(cur, start); - for (cur[0] = start[0]; cur[0] <= end[0]; ++cur[0]) { - for (cur[1] = start[1]; cur[1] <= end[1]; ++cur[1]) { - for (cur[2] = start[2]; cur[2] <= end[2]; ++cur[2]) { + for (cur[0] = start[0]; cur[0] <= end[0]; cur[0]++) { + for (cur[1] = start[1]; cur[1] <= end[1]; cur[1]++) { + for (cur[2] = start[2]; cur[2] <= end[2]; cur[2]++) { if (!cur[0] && !cur[1] && !cur[2]) { continue; /* skip tile at orgLoc, this was already handled before all others */ } ++cache->tile_pass; - for (dim = 0; dim < 3; ++dim) { + for (dim = 0; dim < 3; dim++) { cache->location[dim] = cur[dim] * step[dim] + orgLoc[dim]; cache->plane_offset[dim] = cur[dim] * step[dim]; } @@ -5154,10 +5181,11 @@ static bool sculpt_any_smooth_mode(const Brush *brush, StrokeCache *cache, int s static void sculpt_stroke_modifiers_check(const bContext *C, Object *ob, const Brush *brush) { SculptSession *ss = ob->sculpt; + View3D *v3d = CTX_wm_view3d(C); - if (ss->kb || ss->modifiers_active) { + bool need_pmap = sculpt_any_smooth_mode(brush, ss->cache, 0); + if (ss->kb || ss->modifiers_active || (!BKE_sculptsession_use_pbvh_draw(ob, v3d) && need_pmap)) { Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); - bool need_pmap = sculpt_any_smooth_mode(brush, ss->cache, 0); BKE_sculpt_update_object_for_edit(depsgraph, ob, need_pmap, false); } } @@ -5186,8 +5214,11 @@ static void sculpt_raycast_cb(PBVHNode *node, void *data_v, float *tmin) origco, use_origco, srd->ray_start, + srd->ray_normal, &srd->isect_precalc, - &srd->depth)) { + &srd->depth, + &srd->active_vertex_index, + srd->face_normal)) { srd->hit = 1; *tmin = srd->depth; } @@ -5275,6 +5306,120 @@ static float sculpt_raycast_init(ViewContext *vc, return dist; } +/* Gets the normal, location and active vertex location of the geometry under the cursor. This also + * updates + * the active vertex and cursor related data of the SculptSession using the mouse position */ +bool sculpt_cursor_geometry_info_update(bContext *C, + SculptCursorGeometryInfo *out, + const float mouse[2], + bool use_sampled_normal) +{ + Scene *scene = CTX_data_scene(C); + Sculpt *sd = scene->toolsettings->sculpt; + Object *ob; + SculptSession *ss; + ViewContext vc; + const Brush *brush = BKE_paint_brush(BKE_paint_get_active_from_context(C)); + float ray_start[3], ray_end[3], ray_normal[3], depth, face_normal[3], sampled_normal[3], + mat[3][3]; + float viewDir[3] = {0.0f, 0.0f, 1.0f}; + int totnode; + bool original = false, hit = false; + + ED_view3d_viewcontext_init(C, &vc); + + ob = vc.obact; + ss = ob->sculpt; + + if (!ss->pbvh) { + copy_v3_fl(out->location, 0.0f); + copy_v3_fl(out->normal, 0.0f); + copy_v3_fl(out->active_vertex_co, 0.0f); + return false; + } + + /* PBVH raycast to get active vertex and face normal */ + depth = sculpt_raycast_init(&vc, mouse, ray_start, ray_end, ray_normal, original); + sculpt_stroke_modifiers_check(C, ob, brush); + + SculptRaycastData srd = { + .original = original, + .ss = ob->sculpt, + .hit = 0, + .ray_start = ray_start, + .ray_normal = ray_normal, + .depth = depth, + .face_normal = face_normal, + }; + isect_ray_tri_watertight_v3_precalc(&srd.isect_precalc, ray_normal); + BKE_pbvh_raycast(ss->pbvh, sculpt_raycast_cb, &srd, ray_start, ray_normal, srd.original); + + /* Cursor is not over the mesh, return default values */ + if (!srd.hit) { + copy_v3_fl(out->location, 0.0f); + copy_v3_fl(out->normal, 0.0f); + copy_v3_fl(out->active_vertex_co, 0.0f); + return false; + } + + /* Update the active vertex of the SculptSession */ + ss->active_vertex_index = srd.active_vertex_index; + + copy_v3_v3(out->active_vertex_co, sculpt_vertex_co_get(ss, srd.active_vertex_index)); + copy_v3_v3(out->location, ray_normal); + mul_v3_fl(out->location, srd.depth); + add_v3_v3(out->location, ray_start); + + /* Option to return the face normal directly for performance o accuracy reasons */ + if (!use_sampled_normal) { + copy_v3_v3(out->normal, srd.face_normal); + return hit; + } + + /* Sampled normal calculation */ + const float radius_scale = 1.0f; + float radius; + + /* Update cursor data in SculptSession */ + invert_m4_m4(ob->imat, ob->obmat); + copy_m3_m4(mat, vc.rv3d->viewinv); + mul_m3_v3(mat, viewDir); + copy_m3_m4(mat, ob->imat); + mul_m3_v3(mat, viewDir); + normalize_v3_v3(ss->cursor_view_normal, viewDir); + copy_v3_v3(ss->cursor_normal, srd.face_normal); + copy_v3_v3(ss->cursor_location, out->location); + ss->rv3d = vc.rv3d; + + if (!BKE_brush_use_locked_size(scene, brush)) { + radius = paint_calc_object_space_radius(&vc, out->location, BKE_brush_size_get(scene, brush)); + } + else { + radius = BKE_brush_unprojected_radius_get(scene, brush); + } + ss->cursor_radius = radius; + + PBVHNode **nodes = sculpt_pbvh_gather_generic(ob, sd, brush, original, radius_scale, &totnode); + + /* In case there are no nodes under the cursor, return the face normal */ + if (!totnode) { + MEM_freeN(nodes); + copy_v3_v3(out->normal, srd.face_normal); + return true; + } + + /* Calculate the sampled normal */ + if (sculpt_pbvh_calc_area_normal(brush, ob, nodes, totnode, true, sampled_normal)) { + copy_v3_v3(out->normal, sampled_normal); + } + else { + /* Use face normal when there are no vertices to sample inside the cursor radius */ + copy_v3_v3(out->normal, srd.face_normal); + } + MEM_freeN(nodes); + return true; +} + /* Do a raycast in the tree to find the 3d brush location * (This allows us to ignore the GL depth buffer) * Returns 0 if the ray doesn't hit the mesh, non-zero otherwise @@ -5284,7 +5429,7 @@ bool sculpt_stroke_get_location(bContext *C, float out[3], const float mouse[2]) Object *ob; SculptSession *ss; StrokeCache *cache; - float ray_start[3], ray_end[3], ray_normal[3], depth; + float ray_start[3], ray_end[3], ray_normal[3], depth, face_normal[3]; bool original; ViewContext vc; @@ -5302,14 +5447,21 @@ bool sculpt_stroke_get_location(bContext *C, float out[3], const float mouse[2]) depth = sculpt_raycast_init(&vc, mouse, ray_start, ray_end, ray_normal, original); + if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { + BM_mesh_elem_table_ensure(ss->bm, BM_VERT); + BM_mesh_elem_index_ensure(ss->bm, BM_VERT); + } + bool hit = false; { SculptRaycastData srd; srd.ss = ob->sculpt; srd.ray_start = ray_start; + srd.ray_normal = ray_normal; srd.hit = 0; srd.depth = depth; srd.original = original; + srd.face_normal = face_normal; isect_ray_tri_watertight_v3_precalc(&srd.isect_precalc, ray_normal); BKE_pbvh_raycast(ss->pbvh, sculpt_raycast_cb, &srd, ray_start, ray_normal, srd.original); @@ -5577,7 +5729,6 @@ static void sculpt_stroke_update_step(bContext *C, } do_symmetrical_brush_actions(sd, ob, do_brush_action, ups); - sculpt_combine_proxies(sd, ob); /* hack to fix noise texture tearing mesh */ diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index e646accf108..9f1cb7a53a4 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -34,9 +34,7 @@ #include "BKE_pbvh.h" struct KeyBlock; -struct Main; struct Object; -struct SculptOrigVertData; struct SculptUndoNode; struct bContext; @@ -47,7 +45,18 @@ bool sculpt_poll(struct bContext *C); bool sculpt_poll_view3d(struct bContext *C); /* Stroke */ + +typedef struct SculptCursorGeometryInfo { + float location[3]; + float normal[3]; + float active_vertex_co[3]; +} SculptCursorGeometryInfo; + bool sculpt_stroke_get_location(struct bContext *C, float out[3], const float mouse[2]); +bool sculpt_cursor_geometry_info_update(bContext *C, + SculptCursorGeometryInfo *out, + const float mouse[2], + bool use_sampled_normal); /* Dynamic topology */ void sculpt_pbvh_clear(Object *ob); @@ -164,6 +173,7 @@ typedef struct SculptThreadedTaskData { float (*area_cos)[3]; float (*area_nos)[3]; int *count; + bool any_vertex_sampled; ThreadMutex mutex; @@ -228,7 +238,7 @@ float tex_strength(struct SculptSession *ss, const int thread_id); /* just for vertex paint. */ -void sculpt_pbvh_calc_area_normal(const struct Brush *brush, +bool sculpt_pbvh_calc_area_normal(const struct Brush *brush, Object *ob, PBVHNode **nodes, int totnode, @@ -342,7 +352,7 @@ SculptUndoNode *sculpt_undo_get_node(PBVHNode *node); void sculpt_undo_push_begin(const char *name); void sculpt_undo_push_end(void); -void sculpt_vertcos_to_key(Object *ob, KeyBlock *kb, float (*vertCos)[3]); +void sculpt_vertcos_to_key(Object *ob, KeyBlock *kb, const float (*vertCos)[3]); void sculpt_update_object_bounding_box(struct Object *ob); diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index 3a3487227a3..cb8afd5d7fa 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -189,7 +189,7 @@ static bool sculpt_undo_restore_coords(bContext *C, Depsgraph *depsgraph, Sculpt /* pbvh uses it's own mvert array, so coords should be */ /* propagated to pbvh here */ - BKE_pbvh_apply_vertCos(ss->pbvh, vertCos, ss->kb->totelem); + BKE_pbvh_vert_coords_apply(ss->pbvh, vertCos, ss->kb->totelem); MEM_freeN(vertCos); } diff --git a/source/blender/editors/sound/sound_ops.c b/source/blender/editors/sound/sound_ops.c index 51231ccf634..4e710d31cbb 100644 --- a/source/blender/editors/sound/sound_ops.c +++ b/source/blender/editors/sound/sound_ops.c @@ -710,7 +710,7 @@ static void SOUND_OT_mixdown(wmOperatorType *ot) FILE_TYPE_FOLDER | FILE_TYPE_SOUND, FILE_SPECIAL, FILE_SAVE, - WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH, + WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH | WM_FILESEL_SHOW_PROPS, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); #ifdef WITH_AUDASPACE diff --git a/source/blender/editors/space_action/action_data.c b/source/blender/editors/space_action/action_data.c index bc7f8a0f79d..5ceaefd6309 100644 --- a/source/blender/editors/space_action/action_data.c +++ b/source/blender/editors/space_action/action_data.c @@ -220,7 +220,7 @@ static int action_new_exec(bContext *C, wmOperator *UNUSED(op)) PointerRNA oldptr; oldptr = RNA_property_pointer_get(&ptr, prop); - oldact = (bAction *)oldptr.id.data; + oldact = (bAction *)oldptr.owner_id; /* stash the old action to prevent it from being lost */ if (ptr.type == &RNA_AnimData) { diff --git a/source/blender/editors/space_action/action_draw.c b/source/blender/editors/space_action/action_draw.c index a7b6f399187..0c57113956d 100644 --- a/source/blender/editors/space_action/action_draw.c +++ b/source/blender/editors/space_action/action_draw.c @@ -352,179 +352,231 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *ar) /* ************************************************************************* */ /* Timeline - Caches */ -void timeline_draw_cache(SpaceAction *saction, Object *ob, Scene *scene) +static bool timeline_cache_is_hidden_by_setting(SpaceAction *saction, PTCacheID *pid) { - PTCacheID *pid; - ListBase pidlist; - const float cache_draw_height = (4.0f * UI_DPI_FAC * U.pixelsize); - float yoffs = 0.f; - - if (!(saction->cache_display & TIME_CACHE_DISPLAY) || (!ob)) { - return; + switch (pid->type) { + case PTCACHE_TYPE_SOFTBODY: + if ((saction->cache_display & TIME_CACHE_SOFTBODY) == 0) { + return true; + } + break; + case PTCACHE_TYPE_PARTICLES: + if ((saction->cache_display & TIME_CACHE_PARTICLES) == 0) { + return true; + } + break; + case PTCACHE_TYPE_CLOTH: + if ((saction->cache_display & TIME_CACHE_CLOTH) == 0) { + return true; + } + break; + case PTCACHE_TYPE_SMOKE_DOMAIN: + case PTCACHE_TYPE_SMOKE_HIGHRES: + if ((saction->cache_display & TIME_CACHE_SMOKE) == 0) { + return true; + } + break; + case PTCACHE_TYPE_DYNAMICPAINT: + if ((saction->cache_display & TIME_CACHE_DYNAMICPAINT) == 0) { + return true; + } + break; + case PTCACHE_TYPE_RIGIDBODY: + if ((saction->cache_display & TIME_CACHE_RIGIDBODY) == 0) { + return true; + } + break; } + return false; +} - BKE_ptcache_ids_from_object(&pidlist, ob, scene, 0); +static void timeline_cache_color_get(PTCacheID *pid, float color[4]) +{ + switch (pid->type) { + case PTCACHE_TYPE_SOFTBODY: + color[0] = 1.0; + color[1] = 0.4; + color[2] = 0.02; + color[3] = 0.1; + break; + case PTCACHE_TYPE_PARTICLES: + color[0] = 1.0; + color[1] = 0.1; + color[2] = 0.02; + color[3] = 0.1; + break; + case PTCACHE_TYPE_CLOTH: + color[0] = 0.1; + color[1] = 0.1; + color[2] = 0.75; + color[3] = 0.1; + break; + case PTCACHE_TYPE_SMOKE_DOMAIN: + case PTCACHE_TYPE_SMOKE_HIGHRES: + color[0] = 0.2; + color[1] = 0.2; + color[2] = 0.2; + color[3] = 0.1; + break; + case PTCACHE_TYPE_DYNAMICPAINT: + color[0] = 1.0; + color[1] = 0.1; + color[2] = 0.75; + color[3] = 0.1; + break; + case PTCACHE_TYPE_RIGIDBODY: + color[0] = 1.0; + color[1] = 0.6; + color[2] = 0.0; + color[3] = 0.1; + break; + default: + color[0] = 1.0; + color[1] = 0.0; + color[2] = 1.0; + color[3] = 0.1; + BLI_assert(0); + break; + } +} - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); +static void timeline_cache_modify_color_based_on_state(PointCache *cache, float color[4]) +{ + if (cache->flag & PTCACHE_BAKED) { + color[0] -= 0.4f; + color[1] -= 0.4f; + color[2] -= 0.4f; + } + else if (cache->flag & PTCACHE_OUTDATED) { + color[0] += 0.4f; + color[1] += 0.4f; + color[2] += 0.4f; + } +} - /* iterate over pointcaches on the active object, and draw each one's range */ - for (pid = pidlist.first; pid; pid = pid->next) { - float col[4]; +static bool timeline_cache_find_next_cached_segment(PointCache *cache, + int search_start_frame, + int *r_segment_start, + int *r_segment_end) +{ + int offset = cache->startframe; + int current = search_start_frame; - switch (pid->type) { - case PTCACHE_TYPE_SOFTBODY: - if (!(saction->cache_display & TIME_CACHE_SOFTBODY)) { - continue; - } - break; - case PTCACHE_TYPE_PARTICLES: - if (!(saction->cache_display & TIME_CACHE_PARTICLES)) { - continue; - } - break; - case PTCACHE_TYPE_CLOTH: - if (!(saction->cache_display & TIME_CACHE_CLOTH)) { - continue; - } - break; - case PTCACHE_TYPE_SMOKE_DOMAIN: - case PTCACHE_TYPE_SMOKE_HIGHRES: - if (!(saction->cache_display & TIME_CACHE_SMOKE)) { - continue; - } - break; - case PTCACHE_TYPE_DYNAMICPAINT: - if (!(saction->cache_display & TIME_CACHE_DYNAMICPAINT)) { - continue; - } - break; - case PTCACHE_TYPE_RIGIDBODY: - if (!(saction->cache_display & TIME_CACHE_RIGIDBODY)) { - continue; - } - break; + /* Find segment start frame. */ + while (true) { + if (current > cache->endframe) { + return false; } - - if (pid->cache->cached_frames == NULL) { - continue; + if (cache->cached_frames[current - offset]) { + *r_segment_start = current; + break; } + current++; + } - GPU_matrix_push(); - GPU_matrix_translate_2f(0.0, (float)V2D_SCROLL_HANDLE_HEIGHT + yoffs); - GPU_matrix_scale_2f(1.0, cache_draw_height); - - switch (pid->type) { - case PTCACHE_TYPE_SOFTBODY: - col[0] = 1.0; - col[1] = 0.4; - col[2] = 0.02; - col[3] = 0.1; - break; - case PTCACHE_TYPE_PARTICLES: - col[0] = 1.0; - col[1] = 0.1; - col[2] = 0.02; - col[3] = 0.1; - break; - case PTCACHE_TYPE_CLOTH: - col[0] = 0.1; - col[1] = 0.1; - col[2] = 0.75; - col[3] = 0.1; - break; - case PTCACHE_TYPE_SMOKE_DOMAIN: - case PTCACHE_TYPE_SMOKE_HIGHRES: - col[0] = 0.2; - col[1] = 0.2; - col[2] = 0.2; - col[3] = 0.1; - break; - case PTCACHE_TYPE_DYNAMICPAINT: - col[0] = 1.0; - col[1] = 0.1; - col[2] = 0.75; - col[3] = 0.1; - break; - case PTCACHE_TYPE_RIGIDBODY: - col[0] = 1.0; - col[1] = 0.6; - col[2] = 0.0; - col[3] = 0.1; - break; - default: - col[0] = 1.0; - col[1] = 0.0; - col[2] = 1.0; - col[3] = 0.1; - BLI_assert(0); - break; + /* Find segment end frame. */ + while (true) { + if (current > cache->endframe) { + *r_segment_end = current - 1; + return true; + } + if (!cache->cached_frames[current - offset]) { + *r_segment_end = current - 1; + return true; } + current++; + } +} + +static uint timeline_cache_segments_count(PointCache *cache) +{ + uint count = 0; + + int current = cache->startframe; + int segment_start; + int segment_end; + while (timeline_cache_find_next_cached_segment(cache, current, &segment_start, &segment_end)) { + count++; + current = segment_end + 1; + } + + return count; +} - const int sta = pid->cache->startframe, end = pid->cache->endframe; +static void timeline_cache_draw_cached_segments(PointCache *cache, uint pos_id) +{ + uint segments_count = timeline_cache_segments_count(cache); + if (segments_count == 0) { + return; + } - GPU_blend(true); + immBeginAtMost(GPU_PRIM_TRIS, segments_count * 6); - immUniformColor4fv(col); - immRectf(pos, (float)sta, 0.0, (float)end, 1.0); + int current = cache->startframe; + int segment_start; + int segment_end; + while (timeline_cache_find_next_cached_segment(cache, current, &segment_start, &segment_end)) { + immRectf_fast(pos_id, segment_start - 0.5f, 0, segment_end + 0.5f, 1.0f); + current = segment_end + 1; + } - col[3] = 0.4f; - if (pid->cache->flag & PTCACHE_BAKED) { - col[0] -= 0.4f; - col[1] -= 0.4f; - col[2] -= 0.4f; - } - else if (pid->cache->flag & PTCACHE_OUTDATED) { - col[0] += 0.4f; - col[1] += 0.4f; - col[2] += 0.4f; - } + immEnd(); +} - immUniformColor4fv(col); +static void timeline_cache_draw_single(PTCacheID *pid, float y_offset, float height, uint pos_id) +{ + GPU_matrix_push(); + GPU_matrix_translate_2f(0.0, (float)V2D_SCROLL_HANDLE_HEIGHT + y_offset); + GPU_matrix_scale_2f(1.0, height); - { - /* draw a quad for each chunk of consecutive cached frames */ - const int chunk_tot = 32; - int chunk_len = 0; - int ista = 0, iend = -1; + float color[4]; + timeline_cache_color_get(pid, color); - for (int i = sta; i <= end; i++) { - if (pid->cache->cached_frames[i - sta]) { - if (chunk_len == 0) { - immBeginAtMost(GPU_PRIM_TRIS, chunk_tot * 6); - } - if (ista > iend) { - chunk_len++; - ista = i; - } - iend = i; - } - else { - if (ista <= iend) { - immRectf_fast(pos, (float)ista - 0.5f, 0.0f, (float)iend + 0.5f, 1.0f); - iend = ista - 1; - } - if (chunk_len >= chunk_tot) { - immEnd(); - chunk_len = 0; - } - } - } - if (ista <= iend) { - immRectf_fast(pos, (float)ista - 0.5f, 0.0f, (float)iend + 0.5f, 1.0f); - } - if (chunk_len != 0) { - immEnd(); - } + immUniformColor4fv(color); + immRectf(pos_id, (float)pid->cache->startframe, 0.0, (float)pid->cache->endframe, 1.0); + + color[3] = 0.4f; + timeline_cache_modify_color_based_on_state(pid->cache, color); + immUniformColor4fv(color); + + timeline_cache_draw_cached_segments(pid->cache, pos_id); + + GPU_matrix_pop(); +} + +void timeline_draw_cache(SpaceAction *saction, Object *ob, Scene *scene) +{ + if ((saction->cache_display & TIME_CACHE_DISPLAY) == 0 || ob == NULL) { + return; + } + + ListBase pidlist; + BKE_ptcache_ids_from_object(&pidlist, ob, scene, 0); + + uint pos_id = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + GPU_blend(true); + + /* Iterate over pointcaches on the active object, and draw each one's range. */ + float y_offset = 0.0f; + const float cache_draw_height = 4.0f * UI_DPI_FAC * U.pixelsize; + for (PTCacheID *pid = pidlist.first; pid; pid = pid->next) { + if (timeline_cache_is_hidden_by_setting(saction, pid)) { + continue; } - GPU_blend(false); + if (pid->cache->cached_frames == NULL) { + continue; + } - GPU_matrix_pop(); + timeline_cache_draw_single(pid, y_offset, cache_draw_height, pos_id); - yoffs += cache_draw_height; + y_offset += cache_draw_height; } + GPU_blend(false); immUnbindProgram(); BLI_freelistN(&pidlist); diff --git a/source/blender/editors/space_action/action_intern.h b/source/blender/editors/space_action/action_intern.h index eaca7968a34..c227b794ae7 100644 --- a/source/blender/editors/space_action/action_intern.h +++ b/source/blender/editors/space_action/action_intern.h @@ -28,9 +28,7 @@ struct ARegion; struct ARegionType; struct Object; struct Scene; -struct ScrArea; struct SpaceAction; -struct View2D; struct bAnimContext; struct bContext; struct wmOperatorType; diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c index 9e2634b183a..cbf4d0628e6 100644 --- a/source/blender/editors/space_action/action_select.c +++ b/source/blender/editors/space_action/action_select.c @@ -1717,22 +1717,26 @@ static void mouse_action_keys(bAnimContext *ac, /* Highlight GPencil Layer */ if (ale != NULL && ale->data != NULL && ale->type == ANIMTYPE_GPLAYER) { + bGPdata *gpd = (bGPdata *)ale->id; bGPDlayer *gpl = ale->data; gpl->flag |= GP_LAYER_SELECT; - // gpencil_layer_setactive(gpd, gpl); + /* Update other layer status. */ + if (BKE_gpencil_layer_getactive(gpd) != gpl) { + BKE_gpencil_layer_setactive(gpd, gpl); + BKE_gpencil_layer_autolock_set(gpd); + WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + } } } else if (ac->datatype == ANIMCONT_MASK) { /* deselect all other channels first */ ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR); - /* Highlight GPencil Layer */ if (ale != NULL && ale->data != NULL && ale->type == ANIMTYPE_MASKLAYER) { MaskLayer *masklay = ale->data; masklay->flag |= MASK_LAYERFLAG_SELECT; - // gpencil_layer_setactive(gpd, gpl); } } } diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c index 3c879b03126..1685852dd02 100644 --- a/source/blender/editors/space_action/space_action.c +++ b/source/blender/editors/space_action/space_action.c @@ -373,7 +373,7 @@ static void saction_channel_region_message_subscribe(const struct bContext *UNUS * so just whitelist the entire structs for updates */ { - wmMsgParams_RNA msg_key_params = {{{0}}}; + wmMsgParams_RNA msg_key_params = {{0}}; StructRNA *type_array[] = { &RNA_DopeSheet, /* dopesheet filters */ diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c index 78d5ed0f98a..56a0f472010 100644 --- a/source/blender/editors/space_buttons/buttons_context.c +++ b/source/blender/editors/space_buttons/buttons_context.c @@ -76,7 +76,7 @@ static int set_pointer_type(ButsContextPath *path, bContextDataResult *result, S ptr = &path->ptr[a]; if (RNA_struct_is_a(ptr->type, type)) { - CTX_data_pointer_set(result, ptr->id.data, ptr->type, ptr->data); + CTX_data_pointer_set(result, ptr->owner_id, ptr->type, ptr->data); return 1; } } @@ -930,7 +930,7 @@ int buttons_context(const bContext *C, const char *member, bContextDataResult *r if (ct->user && ct->user->ptr.data) { ButsTextureUser *user = ct->user; - CTX_data_pointer_set(result, user->ptr.id.data, user->ptr.type, user->ptr.data); + CTX_data_pointer_set(result, user->ptr.owner_id, user->ptr.type, user->ptr.data); } return 1; @@ -1018,7 +1018,7 @@ int buttons_context(const bContext *C, const char *member, bContextDataResult *r PointerRNA *ptr = get_pointer_type(path, &RNA_ParticleSettings); if (ptr && ptr->data) { - CTX_data_pointer_set(result, ptr->id.data, &RNA_ParticleSettings, ptr->data); + CTX_data_pointer_set(result, ptr->owner_id, &RNA_ParticleSettings, ptr->data); return 1; } else { @@ -1027,7 +1027,7 @@ int buttons_context(const bContext *C, const char *member, bContextDataResult *r if (ptr && ptr->data) { ParticleSettings *part = ((ParticleSystem *)ptr->data)->part; - CTX_data_pointer_set(result, ptr->id.data, &RNA_ParticleSettings, part); + CTX_data_pointer_set(result, ptr->owner_id, &RNA_ParticleSettings, part); return 1; } } @@ -1285,8 +1285,8 @@ ID *buttons_context_id_path(const bContext *C) } } - if (ptr->id.data) { - return ptr->id.data; + if (ptr->owner_id) { + return ptr->owner_id; } } } diff --git a/source/blender/editors/space_buttons/buttons_ops.c b/source/blender/editors/space_buttons/buttons_ops.c index 500efe4bb4d..3ea6d183969 100644 --- a/source/blender/editors/space_buttons/buttons_ops.c +++ b/source/blender/editors/space_buttons/buttons_ops.c @@ -103,7 +103,7 @@ static int file_browse_exec(bContext *C, wmOperator *op) /* add slash for directories, important for some properties */ if (RNA_property_subtype(fbo->prop) == PROP_DIRPATH) { const bool is_relative = RNA_boolean_get(op->ptr, "relative_path"); - id = fbo->ptr.id.data; + id = fbo->ptr.owner_id; BLI_strncpy(path, str, FILE_MAX); BLI_path_abs(path, id ? ID_BLEND_PATH(bmain, id) : BKE_main_blendfile_path(bmain)); diff --git a/source/blender/editors/space_buttons/buttons_texture.c b/source/blender/editors/space_buttons/buttons_texture.c index 87ea011e0a7..03c8fbb4e66 100644 --- a/source/blender/editors/space_buttons/buttons_texture.c +++ b/source/blender/editors/space_buttons/buttons_texture.c @@ -385,7 +385,7 @@ static void template_texture_select(bContext *C, void *user_p, void *UNUSED(arg) if (user->ptr.type == &RNA_ParticleSettingsTextureSlot) { /* stupid exception for particle systems which still uses influence * from the old texture system, set the active texture slots as well */ - ParticleSettings *part = user->ptr.id.data; + ParticleSettings *part = (ParticleSettings *)user->ptr.owner_id; int a; for (a = 0; a < MAX_MTEX; a++) { diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c index b3690bf2fe3..6008d06e376 100644 --- a/source/blender/editors/space_buttons/space_buttons.c +++ b/source/blender/editors/space_buttons/space_buttons.c @@ -404,8 +404,6 @@ static void buttons_area_listener(wmWindow *UNUSED(win), buttons_area_redraw(sa, BCONTEXT_DATA); /* autotexpace flag */ break; case ND_POSE: - buttons_area_redraw(sa, BCONTEXT_DATA); - break; case ND_BONE_ACTIVE: case ND_BONE_SELECT: buttons_area_redraw(sa, BCONTEXT_BONE); @@ -567,7 +565,7 @@ static void buttons_id_remap(ScrArea *UNUSED(sa), SpaceLink *slink, ID *old_id, int i; for (i = 0; i < path->len; i++) { - if (path->ptr[i].id.data == old_id) { + if (path->ptr[i].owner_id == old_id) { break; } } diff --git a/source/blender/editors/space_clip/clip_dopesheet_draw.c b/source/blender/editors/space_clip/clip_dopesheet_draw.c index 599a92ff77f..3c2d3eb1d97 100644 --- a/source/blender/editors/space_clip/clip_dopesheet_draw.c +++ b/source/blender/editors/space_clip/clip_dopesheet_draw.c @@ -222,8 +222,9 @@ void clip_draw_dopesheet_main(SpaceClip *sc, ARegion *ar, Scene *scene) format, "outlineColor", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); uint flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT); - immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND); GPU_program_point_size(true); + immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND); + immUniform1f("outline_scale", 1.0f); immUniform2f( "ViewportSize", BLI_rcti_size_x(&v2d->mask) + 1, BLI_rcti_size_y(&v2d->mask) + 1); immBegin(GPU_PRIM_POINTS, keyframe_len); diff --git a/source/blender/editors/space_console/space_console.c b/source/blender/editors/space_console/space_console.c index 999255aef88..f5c02dbd724 100644 --- a/source/blender/editors/space_console/space_console.c +++ b/source/blender/editors/space_console/space_console.c @@ -26,6 +26,7 @@ #include "BLI_blenlib.h" #include "BLI_utildefines.h" +#include "BKE_global.h" #include "BKE_context.h" #include "BKE_screen.h" @@ -173,7 +174,7 @@ static void id_drop_copy(wmDrag *drag, wmDropBox *drop) ID *id = WM_drag_ID(drag, 0); /* copy drag path to properties */ - char *text = RNA_path_full_ID_py(id); + char *text = RNA_path_full_ID_py(G_MAIN, id); RNA_string_set(drop->ptr, "text", text); MEM_freeN(text); } diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c index 3a6d59c1dbf..0083fc244d8 100644 --- a/source/blender/editors/space_file/file_draw.c +++ b/source/blender/editors/space_file/file_draw.c @@ -26,6 +26,7 @@ #include <errno.h> #include "BLI_blenlib.h" +#include "BLI_fileops_types.h" #include "BLI_utildefines.h" #include "BLI_math.h" @@ -77,234 +78,6 @@ static char *file_draw_tooltip_func(bContext *UNUSED(C), void *argN, const char return BLI_strdup(dyn_tooltip); } -/* Note: This function uses pixelspace (0, 0, winx, winy), not view2d. - * The controls are laid out as follows: - * - * ------------------------------------------- - * | Directory input | execute | - * ------------------------------------------- - * | Filename input | + | - | cancel | - * ------------------------------------------- - * - * The input widgets will stretch to fill any excess space. - * When there isn't enough space for all controls to be shown, they are - * hidden in this order: x/-, execute/cancel, input widgets. - */ -void file_draw_buttons(const bContext *C, ARegion *ar) -{ - /* Button layout. */ - const int max_x = ar->winx - 10; - const int line1_y = ar->winy - (IMASEL_BUTTONS_HEIGHT / 2 + IMASEL_BUTTONS_MARGIN); - const int line2_y = line1_y - (IMASEL_BUTTONS_HEIGHT / 2 + IMASEL_BUTTONS_MARGIN); - const int input_minw = 20; - const int btn_h = UI_UNIT_Y; - const int btn_fn_w = UI_UNIT_X; - const int btn_minw = 80; - const int btn_margin = 20; - const int separator = 4; - - /* Additional locals. */ - char uiblockstr[32]; - int loadbutton; - int fnumbuttons; - int min_x = 10; - int chan_offs = 0; - int available_w = max_x - min_x; - int line1_w = available_w; - int line2_w = available_w; - - uiBut *but; - uiBlock *block; - SpaceFile *sfile = CTX_wm_space_file(C); - FileSelectParams *params = ED_fileselect_get_params(sfile); - ARegion *artmp; - const bool is_browse_only = (sfile->op == NULL); - - /* Initialize UI block. */ - BLI_snprintf(uiblockstr, sizeof(uiblockstr), "win %p", (void *)ar); - block = UI_block_begin(C, ar, uiblockstr, UI_EMBOSS); - - /* exception to make space for collapsed region icon */ - for (artmp = CTX_wm_area(C)->regionbase.first; artmp; artmp = artmp->next) { - if (artmp->regiontype == RGN_TYPE_TOOLS && artmp->flag & RGN_FLAG_HIDDEN) { - chan_offs = 16; - min_x += chan_offs; - available_w -= chan_offs; - } - } - - /* Is there enough space for the execute / cancel buttons? */ - - if (is_browse_only) { - loadbutton = 0; - } - else { - const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; - loadbutton = UI_fontstyle_string_width(fstyle, params->title) + btn_margin; - CLAMP_MIN(loadbutton, btn_minw); - if (available_w <= loadbutton + separator + input_minw) { - loadbutton = 0; - } - } - - if (loadbutton) { - line1_w -= (loadbutton + separator); - line2_w = line1_w; - } - - /* Is there enough space for file number increment/decrement buttons? */ - fnumbuttons = 2 * btn_fn_w; - if (!loadbutton || line2_w <= fnumbuttons + separator + input_minw) { - fnumbuttons = 0; - } - else { - line2_w -= (fnumbuttons + separator); - } - - /* Text input fields for directory and file. */ - if (available_w > 0) { - const struct FileDirEntry *file = sfile->files ? - filelist_file(sfile->files, params->active_file) : - NULL; - int overwrite_alert = file_draw_check_exists(sfile); - const bool is_active_dir = file && (file->typeflag & FILE_TYPE_FOLDER); - - /* callbacks for operator check functions */ - UI_block_func_set(block, file_draw_check_cb, NULL, NULL); - - but = uiDefBut(block, - UI_BTYPE_TEXT, - -1, - "", - min_x, - line1_y, - line1_w - chan_offs, - btn_h, - params->dir, - 0.0, - (float)FILE_MAX, - 0, - 0, - TIP_("File path")); - UI_but_func_complete_set(but, autocomplete_directory, NULL); - UI_but_flag_enable(but, UI_BUT_NO_UTF8); - UI_but_flag_disable(but, UI_BUT_UNDO); - UI_but_funcN_set(but, file_directory_enter_handle, NULL, but); - - /* TODO, directory editing is non-functional while a library is loaded - * until this is properly supported just disable it. */ - if (sfile->files && filelist_lib(sfile->files)) { - UI_but_flag_enable(but, UI_BUT_DISABLED); - } - - if ((params->flag & FILE_DIRSEL_ONLY) == 0) { - but = uiDefBut( - block, - UI_BTYPE_TEXT, - -1, - "", - min_x, - line2_y, - line2_w - chan_offs, - btn_h, - is_active_dir ? (char *)"" : params->file, - 0.0, - (float)FILE_MAXFILE, - 0, - 0, - TIP_(overwrite_alert ? N_("File name, overwrite existing") : N_("File name"))); - UI_but_func_complete_set(but, autocomplete_file, NULL); - UI_but_flag_enable(but, UI_BUT_NO_UTF8); - UI_but_flag_disable(but, UI_BUT_UNDO); - /* silly workaround calling NFunc to ensure this does not get called - * immediate ui_apply_but_func but only after button deactivates */ - UI_but_funcN_set(but, file_filename_enter_handle, NULL, but); - - /* check if this overrides a file and if the operator option is used */ - if (overwrite_alert) { - UI_but_flag_enable(but, UI_BUT_REDALERT); - } - } - - /* clear func */ - UI_block_func_set(block, NULL, NULL, NULL); - } - - /* Filename number increment / decrement buttons. */ - if (fnumbuttons && (params->flag & FILE_DIRSEL_ONLY) == 0) { - UI_block_align_begin(block); - but = uiDefIconButO(block, - UI_BTYPE_BUT, - "FILE_OT_filenum", - 0, - ICON_REMOVE, - min_x + line2_w + separator - chan_offs, - line2_y, - btn_fn_w, - btn_h, - TIP_("Decrement the filename number")); - RNA_int_set(UI_but_operator_ptr_get(but), "increment", -1); - - but = uiDefIconButO(block, - UI_BTYPE_BUT, - "FILE_OT_filenum", - 0, - ICON_ADD, - min_x + line2_w + separator + btn_fn_w - chan_offs, - line2_y, - btn_fn_w, - btn_h, - TIP_("Increment the filename number")); - RNA_int_set(UI_but_operator_ptr_get(but), "increment", 1); - UI_block_align_end(block); - } - - /* Execute / cancel buttons. */ - if (loadbutton) { - const struct FileDirEntry *file = sfile->files ? - filelist_file(sfile->files, params->active_file) : - NULL; - char const *str_exec; - - if (file && FILENAME_IS_PARENT(file->relpath)) { - str_exec = IFACE_("Parent Directory"); - } - else if (file && file->typeflag & FILE_TYPE_DIR) { - str_exec = IFACE_("Open Directory"); - } - else { - str_exec = params->title; /* params->title is already translated! */ - } - - but = uiDefButO(block, - UI_BTYPE_BUT, - "FILE_OT_execute", - WM_OP_EXEC_REGION_WIN, - str_exec, - max_x - loadbutton, - line1_y, - loadbutton, - btn_h, - ""); - /* Just a display hint. */ - UI_but_flag_enable(but, UI_BUT_ACTIVE_DEFAULT); - - uiDefButO(block, - UI_BTYPE_BUT, - "FILE_OT_cancel", - WM_OP_EXEC_REGION_WIN, - IFACE_("Cancel"), - max_x - loadbutton, - line2_y, - loadbutton, - btn_h, - ""); - } - - UI_block_end(C, block); - UI_block_draw(C, block); -} - static void draw_tile(int sx, int sy, int width, int height, int colorid, int shade) { float color[4]; @@ -349,7 +122,7 @@ static void file_draw_string(int sx, rcti rect; char fname[FILE_MAXFILE]; - if (string[0] == '\0') { + if (string[0] == '\0' || width < 1) { return; } @@ -362,7 +135,7 @@ static void file_draw_string(int sx, /* no text clipping needed, UI_fontstyle_draw does it but is a bit too strict * (for buttons it works) */ rect.xmin = sx; - rect.xmax = (int)(sx + ceil(width + 5.0f / UI_DPI_FAC)); + rect.xmax = sx + round_fl_to_int(width); rect.ymin = sy - height; rect.ymax = sy; @@ -404,8 +177,8 @@ static void file_draw_preview(uiBlock *block, float scaledx, scaledy; float scale; int ex, ey; - bool use_dropshadow = !is_icon && (typeflags & FILE_TYPE_IMAGE); - float col[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + bool use_dropshadow = !is_icon && + (typeflags & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_BLENDER)); BLI_assert(imb != NULL); @@ -442,13 +215,27 @@ static void file_draw_preview(uiBlock *block, /* shadow */ if (use_dropshadow) { - UI_draw_box_shadow(220, (float)xco, (float)yco, (float)(xco + ex), (float)(yco + ey)); + UI_draw_box_shadow(128, (float)xco, (float)yco, (float)(xco + ex), (float)(yco + ey)); } GPU_blend(true); - /* the image */ - if (!is_icon && typeflags & FILE_TYPE_FTFONT) { + /* the large image */ + + float col[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + if (is_icon) { + /* File and Folder icons draw with lowered opacity until we add themes */ + col[3] = 0.6f; + /* Use dark images if background is light */ + float bg[3]; + UI_GetThemeColor3fv(TH_BACK, bg); + if (rgb_to_grayscale(bg) > 0.5f) { + col[0] = 0; + col[1] = 0; + col[2] = 0; + } + } + else if (typeflags & FILE_TYPE_FTFONT) { UI_GetThemeColor4fv(TH_TEXT, col); } @@ -477,30 +264,61 @@ static void file_draw_preview(uiBlock *block, GPU_blend_set_func_separate( GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - if (icon) { - UI_icon_draw_ex((float)xco + (7 * UI_DPI_FAC), - (float)yco + (7 * UI_DPI_FAC), - icon, - icon_aspect, - 1.0f, - 0.0f, - NULL, - false); + if (icon && (icon != ICON_FILE_FONT)) { + /* size of center icon is scaled to fit container and UI scale */ + float icon_x, icon_y; + + if (is_icon) { + const float icon_size = 16.0f / icon_aspect * U.dpi_fac; + float icon_opacity = MIN2(icon_aspect, 0.7); + uchar icon_color[4] = {255, 255, 255, 255}; + float bg[3]; + /* base this off theme color of file or folder later */ + UI_GetThemeColor3fv(TH_BACK, bg); + if (rgb_to_grayscale(bg) > 0.5f) { + icon_color[0] = 0; + icon_color[1] = 0; + icon_color[2] = 0; + } + icon_x = xco + (ex / 2.0f) - (icon_size / 2.0f); + icon_y = yco + (ey / 2.0f) - (icon_size * ((typeflags & FILE_TYPE_DIR) ? 0.78f : 0.65f)); + UI_icon_draw_ex( + icon_x, icon_y, icon, icon_aspect / U.dpi_fac, icon_opacity, 0.0f, icon_color, false); + } + else { + const uchar dark[4] = {0, 0, 0, 255}; + const uchar light[4] = {255, 255, 255, 255}; + + /* Smaller, fainter icon for preview image thumbnail. */ + icon_x = xco + (2.0f * UI_DPI_FAC); + icon_y = yco + (2.0f * UI_DPI_FAC); + + UI_icon_draw_ex(icon_x + 1, icon_y - 1, icon, 1.0f / U.dpi_fac, 0.2f, 0.0f, dark, false); + UI_icon_draw_ex(icon_x, icon_y, icon, 1.0f / U.dpi_fac, 0.6f, 0.0f, light, false); + } } /* border */ if (use_dropshadow) { GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + uint pos_attr = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + uint col_attr = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - immUniformColor4f(0.0f, 0.0f, 0.0f, 0.4f); - imm_draw_box_wire_2d(pos, (float)xco, (float)yco, (float)(xco + ex), (float)(yco + ey)); + immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR); + immBegin(GPU_PRIM_LINE_LOOP, 4); + immAttr4f(col_attr, 1.0f, 1.0f, 1.0f, 0.15f); + immVertex2f(pos_attr, (float)xco + 1, (float)(yco + ey)); + immAttr4f(col_attr, 1.0f, 1.0f, 1.0f, 0.2f); + immVertex2f(pos_attr, (float)(xco + ex), (float)(yco + ey)); + immAttr4f(col_attr, 0.0f, 0.0f, 0.0f, 0.2f); + immVertex2f(pos_attr, (float)(xco + ex), (float)yco + 1); + immAttr4f(col_attr, 0.0f, 0.0f, 0.0f, 0.3f); + immVertex2f(pos_attr, (float)xco + 1, (float)yco + 1); + immEnd(); immUnbindProgram(); } but = uiDefBut(block, UI_BTYPE_LABEL, 0, "", xco, yco, ex, ey, NULL, 0.0, 0.0, 0, 0, NULL); - UI_but_func_tooltip_set(but, file_draw_tooltip_func, BLI_strdup(path)); /* dragregion */ if (drag) { @@ -557,6 +375,7 @@ static void renamebutton_cb(bContext *C, void *UNUSED(arg1), char *oldname) static void draw_background(FileLayout *layout, View2D *v2d) { + const int item_height = layout->tile_h + (2 * layout->tile_border_y); int i; int sy; @@ -565,9 +384,11 @@ static void draw_background(FileLayout *layout, View2D *v2d) immUniformThemeColorShade(TH_BACK, -7); /* alternating flat shade background */ - for (i = 0; (i <= layout->rows); i += 2) { - sy = (int)v2d->cur.ymax - i * (layout->tile_h + 2 * layout->tile_border_y) - - layout->tile_border_y; + for (i = 2; (i <= layout->rows + 1); i += 2) { + sy = (int)v2d->cur.ymax - layout->offset_top - i * item_height - layout->tile_border_y; + + /* Offsett pattern slightly to add scroll effect. */ + sy += round_fl_to_int(item_height * (v2d->tot.ymax - v2d->cur.ymax) / item_height); immRectf(pos, v2d->cur.xmin, @@ -632,6 +453,176 @@ static void draw_dividers(FileLayout *layout, View2D *v2d) } } +static void draw_columnheader_background(const FileLayout *layout, const View2D *v2d) +{ + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformThemeColorShade(TH_BACK, 11); + + immRectf(pos, + v2d->cur.xmin, + v2d->cur.ymax - layout->attribute_column_header_h, + v2d->cur.xmax, + v2d->cur.ymax); + + immUnbindProgram(); +} + +static void draw_columnheader_columns(const FileSelectParams *params, + FileLayout *layout, + const View2D *v2d, + const uchar text_col[4]) +{ + const float divider_pad = 0.2 * layout->attribute_column_header_h; + int sx = v2d->cur.xmin, sy = v2d->cur.ymax; + + for (FileAttributeColumnType column_type = 0; column_type < ATTRIBUTE_COLUMN_MAX; + column_type++) { + if (!file_attribute_column_type_enabled(params, column_type)) { + continue; + } + const FileAttributeColumn *column = &layout->attribute_columns[column_type]; + + /* Active sort type triangle */ + if (params->sort == column->sort_type) { + float tri_color[4]; + + rgba_uchar_to_float(tri_color, text_col); + UI_draw_icon_tri(sx + column->width - (0.3f * U.widget_unit) - + ATTRIBUTE_COLUMN_PADDING / 2.0f, + sy + (0.1f * U.widget_unit) - (layout->attribute_column_header_h / 2), + (params->flag & FILE_SORT_INVERT) ? 't' : 'v', + tri_color); + } + + file_draw_string(sx + ATTRIBUTE_COLUMN_PADDING, + sy - layout->tile_border_y, + IFACE_(column->name), + column->width - 2 * ATTRIBUTE_COLUMN_PADDING, + layout->attribute_column_header_h - layout->tile_border_y, + UI_STYLE_TEXT_LEFT, + text_col); + + /* Separator line */ + if (column_type != COLUMN_NAME) { + uint pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformThemeColorShade(TH_BACK, -10); + immBegin(GPU_PRIM_LINES, 2); + immVertex2f(pos, sx - 1, sy - divider_pad); + immVertex2f(pos, sx - 1, sy - layout->attribute_column_header_h + divider_pad); + immEnd(); + immUnbindProgram(); + } + + sx += column->width; + } + + /* Vertical separator lines line */ + { + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformThemeColorShade(TH_BACK, -10); + immBegin(GPU_PRIM_LINES, 4); + immVertex2f(pos, v2d->cur.xmin, sy); + immVertex2f(pos, v2d->cur.xmax, sy); + immVertex2f(pos, v2d->cur.xmin, sy - layout->attribute_column_header_h); + immVertex2f(pos, v2d->cur.xmax, sy - layout->attribute_column_header_h); + immEnd(); + immUnbindProgram(); + } +} + +/** + * Updates the stat string stored in file->entry if necessary. + */ +static const char *filelist_get_details_column_string(FileAttributeColumnType column, + const FileDirEntry *file, + const bool small_size, + const bool update_stat_strings) +{ + switch (column) { + case COLUMN_DATETIME: + if (!(file->typeflag & FILE_TYPE_BLENDERLIB) && !FILENAME_IS_CURRPAR(file->relpath)) { + if ((file->entry->datetime_str[0] == '\0') || update_stat_strings) { + char date[FILELIST_DIRENTRY_DATE_LEN], time[FILELIST_DIRENTRY_TIME_LEN]; + bool is_today, is_yesterday; + + BLI_filelist_entry_datetime_to_string( + NULL, file->entry->time, small_size, time, date, &is_today, &is_yesterday); + + if (is_today || is_yesterday) { + BLI_strncpy(date, is_today ? N_("Today") : N_("Yesterday"), sizeof(date)); + } + BLI_snprintf( + file->entry->datetime_str, sizeof(file->entry->datetime_str), "%s %s", date, time); + } + + return file->entry->datetime_str; + } + break; + case COLUMN_SIZE: + if ((file->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) || + !(file->typeflag & (FILE_TYPE_DIR | FILE_TYPE_BLENDERLIB))) { + if ((file->entry->size_str[0] == '\0') || update_stat_strings) { + BLI_filelist_entry_size_to_string( + NULL, file->entry->size, small_size, file->entry->size_str); + } + + return file->entry->size_str; + } + break; + default: + break; + } + + return NULL; +} + +static void draw_details_columns(const FileSelectParams *params, + const FileLayout *layout, + const FileDirEntry *file, + const int pos_x, + const int pos_y, + const uchar text_col[4]) +{ + const bool small_size = SMALL_SIZE_CHECK(params->thumbnail_size); + const bool update_stat_strings = small_size != SMALL_SIZE_CHECK(layout->curr_size); + int sx = pos_x - layout->tile_border_x - (UI_UNIT_X * 0.1f), sy = pos_y; + + for (FileAttributeColumnType column_type = 0; column_type < ATTRIBUTE_COLUMN_MAX; + column_type++) { + const FileAttributeColumn *column = &layout->attribute_columns[column_type]; + + /* Name column is not a detail column (should already be drawn), always skip here. */ + if (column_type == COLUMN_NAME) { + sx += column->width; + continue; + } + if (!file_attribute_column_type_enabled(params, column_type)) { + continue; + } + + const char *str = filelist_get_details_column_string( + column_type, file, small_size, update_stat_strings); + + if (str) { + file_draw_string(sx + ATTRIBUTE_COLUMN_PADDING, + sy - layout->tile_border_y, + IFACE_(str), + column->width - 2 * ATTRIBUTE_COLUMN_PADDING, + layout->tile_h, + column->text_align, + text_col); + } + + sx += column->width; + } +} + void file_draw_list(const bContext *C, ARegion *ar) { SpaceFile *sfile = CTX_wm_space_file(C); @@ -652,18 +643,14 @@ void file_draw_list(const bContext *C, ARegion *ar) bool is_icon; eFontStyle_Align align; bool do_drag; - int column_space = 0.6f * UI_UNIT_X; unsigned char text_col[4]; - const bool small_size = SMALL_SIZE_CHECK(params->thumbnail_size); - const bool update_stat_strings = small_size != SMALL_SIZE_CHECK(layout->curr_size); - const float thumb_icon_aspect = sqrtf(64.0f / (float)(params->thumbnail_size)); + const bool draw_columnheader = (params->display == FILE_VERTICALDISPLAY); + const float thumb_icon_aspect = MIN2(64.0f / (float)(params->thumbnail_size), 1.0f); numfiles = filelist_files_ensure(files); if (params->display != FILE_IMGDISPLAY) { - draw_background(layout, v2d); - draw_dividers(layout, v2d); } @@ -679,13 +666,14 @@ void file_draw_list(const bContext *C, ARegion *ar) numfiles_layout += layout->rows; } else { - numfiles_layout += layout->columns; + numfiles_layout += layout->flow_columns; } filelist_file_cache_slidingwindow_set(files, numfiles_layout); - textwidth = (FILE_IMGDISPLAY == params->display) ? layout->tile_w : - (int)layout->column_widths[COLUMN_NAME]; + textwidth = (FILE_IMGDISPLAY == params->display) ? + layout->tile_w : + round_fl_to_int(layout->attribute_columns[COLUMN_NAME].width); textheight = (int)(layout->textheight * 3.0 / 2.0 + 0.5); align = (FILE_IMGDISPLAY == params->display) ? UI_STYLE_TEXT_CENTER : UI_STYLE_TEXT_LEFT; @@ -719,11 +707,16 @@ void file_draw_list(const bContext *C, ARegion *ar) BLF_batch_draw_begin(); + UI_GetThemeColor4ubv(TH_TEXT, text_col); + for (i = offset; (i < numfiles) && (i < offset + numfiles_layout); i++) { unsigned int file_selflag; char path[FILE_MAX_LIBEXTRA]; + int padx = 0.1f * UI_UNIT_X; + int icon_ofs = 0; + ED_fileselect_layout_tilepos(layout, i, &sx, &sy); - sx += (int)(v2d->tot.xmin + 0.1f * UI_UNIT_X); + sx += (int)(v2d->tot.xmin + padx); sy = (int)(v2d->tot.ymax - sy); file = filelist_file(files, i); @@ -737,15 +730,14 @@ void file_draw_list(const bContext *C, ARegion *ar) int colorid = (file_selflag & FILE_SEL_SELECTED) ? TH_HILITE : TH_BACK; int shade = (params->highlight_file == i) || (file_selflag & FILE_SEL_HIGHLIGHTED) ? 35 : 0; + const short width = ELEM(params->display, FILE_VERTICALDISPLAY, FILE_HORIZONTALDISPLAY) ? + layout->tile_w - (2 * padx) : + layout->tile_w; BLI_assert(i == 0 || !FILENAME_IS_CURRPAR(file->relpath)); - draw_tile(sx, - sy - 1, - layout->tile_w + 4, - sfile->layout->tile_h + layout->tile_border_y, - colorid, - shade); + draw_tile( + sx, sy - 1, width, sfile->layout->tile_h + layout->tile_border_y, colorid, shade); } } UI_draw_roundbox_corner_set(UI_CNR_NONE); @@ -778,38 +770,28 @@ void file_draw_list(const bContext *C, ARegion *ar) file_draw_icon(block, path, sx, - sy - (UI_UNIT_Y / 6), + sy - layout->tile_border_y, filelist_geticon(files, i, true), ICON_DEFAULT_WIDTH_SCALE, ICON_DEFAULT_HEIGHT_SCALE, do_drag); - sx += ICON_DEFAULT_WIDTH_SCALE + 0.2f * UI_UNIT_X; + icon_ofs += ICON_DEFAULT_WIDTH_SCALE + 0.2f * UI_UNIT_X; } - UI_GetThemeColor4ubv(TH_TEXT, text_col); - if (file_selflag & FILE_SEL_EDITING) { uiBut *but; - short width; - - if (params->display == FILE_SHORTDISPLAY) { - width = layout->tile_w - (ICON_DEFAULT_WIDTH_SCALE + 0.2f * UI_UNIT_X); - } - else if (params->display == FILE_LONGDISPLAY) { - width = layout->column_widths[COLUMN_NAME] + (column_space * 3.5f); - } - else { - BLI_assert(params->display == FILE_IMGDISPLAY); - width = textwidth; - } + const short width = (params->display == FILE_IMGDISPLAY) ? + textwidth : + layout->attribute_columns[COLUMN_NAME].width - + ATTRIBUTE_COLUMN_PADDING; but = uiDefBut(block, UI_BTYPE_TEXT, 1, "", - sx, + sx + icon_ofs, sy - layout->tile_h - 0.15f * UI_UNIT_X, - width, + width - icon_ofs, textheight, sfile->params->renamefile, 1.0f, @@ -825,74 +807,19 @@ void file_draw_list(const bContext *C, ARegion *ar) sfile->files, file, FILE_SEL_REMOVE, FILE_SEL_EDITING, CHECK_ALL); } } - - if (!(file_selflag & FILE_SEL_EDITING)) { - int tpos = (FILE_IMGDISPLAY == params->display) ? sy - layout->tile_h + layout->textheight : - sy; - file_draw_string(sx + 1, tpos, file->name, (float)textwidth, textheight, align, text_col); - } - - sx += (int)layout->column_widths[COLUMN_NAME] + column_space; - if (params->display == FILE_SHORTDISPLAY) { - if ((file->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) || - !(file->typeflag & (FILE_TYPE_DIR | FILE_TYPE_BLENDERLIB))) { - if ((file->entry->size_str[0] == '\0') || update_stat_strings) { - BLI_filelist_entry_size_to_string( - NULL, file->entry->size, small_size, file->entry->size_str); - } - file_draw_string(sx, - sy, - file->entry->size_str, - layout->column_widths[COLUMN_SIZE], - layout->tile_h, - align, - text_col); - } - sx += (int)layout->column_widths[COLUMN_SIZE] + column_space; + else { + const int txpos = (params->display == FILE_IMGDISPLAY) ? sx : sx + 1 + icon_ofs; + const int typos = (params->display == FILE_IMGDISPLAY) ? + sy - layout->tile_h + layout->textheight : + sy - layout->tile_border_y; + const int twidth = (params->display == FILE_IMGDISPLAY) ? + textwidth : + textwidth - 1 - icon_ofs - padx - layout->tile_border_x; + file_draw_string(txpos, typos, file->name, (float)twidth, textheight, align, text_col); } - else if (params->display == FILE_LONGDISPLAY) { - if (!(file->typeflag & FILE_TYPE_BLENDERLIB) && !FILENAME_IS_CURRPAR(file->relpath)) { - if ((file->entry->date_str[0] == '\0') || update_stat_strings) { - BLI_filelist_entry_datetime_to_string( - NULL, file->entry->time, small_size, file->entry->time_str, file->entry->date_str); - } - file_draw_string(sx, - sy, - file->entry->date_str, - layout->column_widths[COLUMN_DATE], - layout->tile_h, - align, - text_col); - sx += (int)layout->column_widths[COLUMN_DATE] + column_space; - file_draw_string(sx, - sy, - file->entry->time_str, - layout->column_widths[COLUMN_TIME], - layout->tile_h, - align, - text_col); - sx += (int)layout->column_widths[COLUMN_TIME] + column_space; - } - else { - sx += (int)layout->column_widths[COLUMN_DATE] + column_space; - sx += (int)layout->column_widths[COLUMN_TIME] + column_space; - } - if ((file->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) || - !(file->typeflag & (FILE_TYPE_DIR | FILE_TYPE_BLENDERLIB))) { - if ((file->entry->size_str[0] == '\0') || update_stat_strings) { - BLI_filelist_entry_size_to_string( - NULL, file->entry->size, small_size, file->entry->size_str); - } - file_draw_string(sx, - sy, - file->entry->size_str, - layout->column_widths[COLUMN_SIZE], - layout->tile_h, - align, - text_col); - } - sx += (int)layout->column_widths[COLUMN_SIZE] + column_space; + if (params->display != FILE_IMGDISPLAY) { + draw_details_columns(params, layout, file, sx, sy, text_col); } } @@ -901,5 +828,11 @@ void file_draw_list(const bContext *C, ARegion *ar) UI_block_end(C, block); UI_block_draw(C, block); + /* Draw last, on top of file list. */ + if (draw_columnheader) { + draw_columnheader_background(layout, v2d); + draw_columnheader_columns(params, layout, v2d, text_col); + } + layout->curr_size = params->thumbnail_size; } diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h index bad25511dd5..61f13098783 100644 --- a/source/blender/editors/space_file/file_intern.h +++ b/source/blender/editors/space_file/file_intern.h @@ -30,9 +30,11 @@ struct ARegion; struct ARegionType; struct FileSelectParams; struct SpaceFile; +struct View2D; /* file_ops.c */ struct ARegion *file_tools_region(struct ScrArea *sa); +struct ARegion *file_tool_props_region(struct ScrArea *sa); /* file_draw.c */ #define TILE_BORDER_X (UI_UNIT_X / 4) @@ -42,9 +44,10 @@ struct ARegion *file_tools_region(struct ScrArea *sa); #define IMASEL_BUTTONS_HEIGHT (UI_UNIT_Y * 2) #define IMASEL_BUTTONS_MARGIN (UI_UNIT_Y / 6) +#define ATTRIBUTE_COLUMN_PADDING (0.5f * UI_UNIT_X) + #define SMALL_SIZE_CHECK(_size) ((_size) < 64) /* Related to FileSelectParams.thumbnail_size. */ -void file_draw_buttons(const bContext *C, ARegion *ar); void file_calc_previews(const bContext *C, ARegion *ar); void file_draw_list(const bContext *C, ARegion *ar); @@ -64,6 +67,7 @@ typedef enum WalkSelectDirection { } WalkSelectDirections; void FILE_OT_highlight(struct wmOperatorType *ot); +void FILE_OT_sort_column_ui_context(struct wmOperatorType *ot); void FILE_OT_select(struct wmOperatorType *ot); void FILE_OT_select_walk(struct wmOperatorType *ot); void FILE_OT_select_all(struct wmOperatorType *ot); @@ -112,6 +116,16 @@ void file_operator_to_sfile(bContext *C, struct SpaceFile *sfile, struct wmOpera /* filesel.c */ void fileselect_file_set(SpaceFile *sfile, const int index); +bool file_attribute_column_type_enabled(const FileSelectParams *params, + FileAttributeColumnType column); +bool file_attribute_column_header_is_inside(const struct View2D *v2d, + const FileLayout *layout, + int x, + int y); +FileAttributeColumnType file_attribute_column_type_find_isect(const View2D *v2d, + const FileSelectParams *params, + FileLayout *layout, + int x); float file_string_width(const char *str); float file_font_pointsize(void); diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index eb5f02b6e13..5168300d21e 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -78,7 +78,12 @@ static FileSelection find_file_mouse_rect(SpaceFile *sfile, ARegion *ar, const r BLI_rctf_rcti_copy(&rect_region_fl, rect_region); + /* Okay, manipulating v2d rects here is hacky... */ + v2d->mask.ymax -= sfile->layout->offset_top; + v2d->cur.ymax -= sfile->layout->offset_top; UI_view2d_region_to_view_rctf(v2d, &rect_region_fl, &rect_view_fl); + v2d->mask.ymax += sfile->layout->offset_top; + v2d->cur.ymax += sfile->layout->offset_top; BLI_rcti_init(&rect_view, (int)(v2d->tot.xmin + rect_view_fl.xmin), @@ -190,7 +195,6 @@ static FileSelect file_select_do(bContext *C, int selected_idx, bool do_diropen) const bool is_parent_dir = FILENAME_IS_PARENT(file->relpath); if (do_diropen == false) { - params->file[0] = '\0'; retval = FILE_SELECT_DIR; } /* the path is too long and we are not going up! */ @@ -262,8 +266,8 @@ static void file_ensure_inside_viewbounds(ARegion *ar, SpaceFile *sfile, const i cur->ymax = cur->ymin + ar->winy; } /* up */ - else if (cur->ymax < rect.ymax) { - cur->ymax = rect.ymax + layout->tile_border_y; + else if ((cur->ymax - layout->offset_top) < rect.ymax) { + cur->ymax = rect.ymax + layout->tile_border_y + layout->offset_top; cur->ymin = cur->ymax - ar->winy; } /* left - also use if tile is wider than viewbounds so view is aligned to file name */ @@ -278,7 +282,7 @@ static void file_ensure_inside_viewbounds(ARegion *ar, SpaceFile *sfile, const i } else { BLI_assert(cur->xmin <= rect.xmin && cur->xmax >= rect.xmax && cur->ymin <= rect.ymin && - cur->ymax >= rect.ymax); + (cur->ymax - layout->offset_top) >= rect.ymax); changed = false; } @@ -384,7 +388,7 @@ static int file_box_select_modal(bContext *C, wmOperator *op, const wmEvent *eve if (result == OPERATOR_RUNNING_MODAL) { WM_operator_properties_border_to_rcti(op, &rect); - BLI_rcti_isect(&(ar->v2d.mask), &rect, &rect); + ED_fileselect_layout_isect_rect(sfile->layout, &ar->v2d, &rect, &rect); sel = file_selection_get(C, &rect, 0); if ((sel.first != params->sel_first) || (sel.last != params->sel_last)) { @@ -440,13 +444,13 @@ static int file_box_select_exec(bContext *C, wmOperator *op) file_deselect_all(sfile, FILE_SEL_SELECTED); } - BLI_rcti_isect(&(ar->v2d.mask), &rect, &rect); + ED_fileselect_layout_isect_rect(sfile->layout, &ar->v2d, &rect, &rect); ret = file_select(C, &rect, select ? FILE_SEL_ADD : FILE_SEL_REMOVE, false, false); /* unselect '..' parent entry - it's not supposed to be selected if more than * one file is selected */ - filelist_entry_select_index_set(sfile->files, 0, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL); + filelist_entry_parent_select_set(sfile->files, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL); if (FILE_SELECT_DIR == ret) { WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL); @@ -493,7 +497,7 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) rect.xmin = rect.xmax = event->mval[0]; rect.ymin = rect.ymax = event->mval[1]; - if (!BLI_rcti_isect_pt(&ar->v2d.mask, rect.xmin, rect.ymin)) { + if (!ED_fileselect_layout_is_inside_pt(sfile->layout, &ar->v2d, rect.xmin, rect.ymin)) { return OPERATOR_CANCELLED; } @@ -514,8 +518,7 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (extend) { /* unselect '..' parent entry - it's not supposed to be selected if more * than one file is selected */ - filelist_entry_select_index_set( - sfile->files, 0, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL); + filelist_entry_parent_select_set(sfile->files, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL); } if (FILE_SELECT_DIR == ret) { @@ -580,6 +583,11 @@ static bool file_walk_select_selection_set(bContext *C, BLI_assert(params); + if (numfiles == 0) { + /* No files visible, nothing to do. */ + return false; + } + if (has_selection) { if (extend && filelist_entry_select_index_get(files, active_old, CHECK_ALL) && filelist_entry_select_index_get(files, active_new, CHECK_ALL)) { @@ -609,7 +617,7 @@ static bool file_walk_select_selection_set(bContext *C, } /* select first file */ else if (ELEM(direction, FILE_SELECT_WALK_DOWN, FILE_SELECT_WALK_RIGHT)) { - params->active_file = active = extend ? 1 : 0; + params->active_file = active = 0; } else { BLI_assert(0); @@ -626,7 +634,7 @@ static bool file_walk_select_selection_set(bContext *C, /* unselect '..' parent entry - it's not supposed to be selected if more * than one file is selected */ - filelist_entry_select_index_set(files, 0, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL); + filelist_entry_parent_select_set(files, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL); } else { /* deselect all first */ @@ -641,11 +649,6 @@ static bool file_walk_select_selection_set(bContext *C, if (fill) { FileSelection sel = {MIN2(active, last_sel), MAX2(active, last_sel)}; - /* clamping selection to not include '..' parent entry */ - if (sel.first == 0) { - sel.first = 1; - } - /* fill selection between last and first selected file */ filelist_entries_select_index_range_set( files, &sel, deselect ? FILE_SEL_REMOVE : FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL); @@ -653,6 +656,12 @@ static bool file_walk_select_selection_set(bContext *C, if (deselect) { filelist_entry_select_index_set(files, active, FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL); } + + /* unselect '..' parent entry - it's not supposed to be selected if more + * than one file is selected */ + if ((sel.last - sel.first) > 1) { + filelist_entry_parent_select_set(files, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL); + } } else { filelist_entry_select_index_set( @@ -688,10 +697,15 @@ static bool file_walk_select_do(bContext *C, /* *** get all needed files for handling selection *** */ + if (numfiles == 0) { + /* No files visible, nothing to do. */ + return false; + } + if (has_selection) { ARegion *ar = CTX_wm_region(C); FileLayout *layout = ED_fileselect_get_layout(sfile, ar); - const int idx_shift = (layout->flag & FILE_LAYOUT_HOR) ? layout->rows : layout->columns; + const int idx_shift = (layout->flag & FILE_LAYOUT_HOR) ? layout->rows : layout->flow_columns; if ((layout->flag & FILE_LAYOUT_HOR && direction == FILE_SELECT_WALK_UP) || (layout->flag & FILE_LAYOUT_VER && direction == FILE_SELECT_WALK_LEFT)) { @@ -718,7 +732,7 @@ static bool file_walk_select_do(bContext *C, BLI_assert(0); } - if (!IN_RANGE(active_new, 0, numfiles)) { + if (!IN_RANGE(active_new, -1, numfiles)) { if (extend) { /* extend to invalid file -> abort */ return false; @@ -1185,7 +1199,7 @@ int file_highlight_set(SpaceFile *sfile, ARegion *ar, int mx, int my) mx -= ar->winrct.xmin; my -= ar->winrct.ymin; - if (BLI_rcti_isect_pt(&ar->v2d.mask, mx, my)) { + if (ED_fileselect_layout_is_inside_pt(sfile->layout, v2d, mx, my)) { float fx, fy; int highlight_file; @@ -1234,6 +1248,53 @@ void FILE_OT_highlight(struct wmOperatorType *ot) ot->poll = ED_operator_file_active; } +static int file_column_sort_ui_context_invoke(bContext *C, + wmOperator *UNUSED(op), + const wmEvent *event) +{ + const ARegion *ar = CTX_wm_region(C); + SpaceFile *sfile = CTX_wm_space_file(C); + + if (file_attribute_column_header_is_inside( + &ar->v2d, sfile->layout, event->mval[0], event->mval[1])) { + const FileAttributeColumnType column_type = file_attribute_column_type_find_isect( + &ar->v2d, sfile->params, sfile->layout, event->mval[0]); + + if (column_type != COLUMN_NONE) { + const FileAttributeColumn *column = &sfile->layout->attribute_columns[column_type]; + + if (column->sort_type != FILE_SORT_NONE) { + if (sfile->params->sort == column->sort_type) { + /* Already sorting by selected column -> toggle sort invert (three state logic). */ + sfile->params->flag ^= FILE_SORT_INVERT; + } + else { + sfile->params->sort = column->sort_type; + sfile->params->flag &= ~FILE_SORT_INVERT; + } + + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL); + } + } + } + + return OPERATOR_PASS_THROUGH; +} + +void FILE_OT_sort_column_ui_context(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Sort from Column"; + ot->description = "Change sorting to use column under cursor"; + ot->idname = "FILE_OT_sort_column_ui_context"; + + /* api callbacks */ + ot->invoke = file_column_sort_ui_context_invoke; + ot->poll = ED_operator_file_active; + + ot->flag = OPTYPE_INTERNAL; +} + int file_cancel_exec(bContext *C, wmOperator *UNUSED(unused)) { wmWindowManager *wm = CTX_wm_manager(C); @@ -1713,7 +1774,7 @@ static int file_smoothscroll_invoke(bContext *C, wmOperator *UNUSED(op), const w /* Number of items in a block (i.e. lines in a column in horizontal layout, or columns in a line * in vertical layout). */ - const int items_block_size = is_horizontal ? sfile->layout->rows : sfile->layout->columns; + const int items_block_size = is_horizontal ? sfile->layout->rows : sfile->layout->flow_columns; /* Scroll offset is the first file in the row/column we are editing in. */ if (sfile->scroll_offset == 0) { @@ -1998,7 +2059,6 @@ void FILE_OT_directory_new(struct wmOperatorType *ot) ot->idname = "FILE_OT_directory_new"; /* api callbacks */ - ot->invoke = WM_operator_confirm; ot->exec = file_directory_new_exec; ot->poll = ED_operator_file_active; /* <- important, handler is on window level */ @@ -2260,10 +2320,29 @@ ARegion *file_tools_region(ScrArea *sa) arnew->regiontype = RGN_TYPE_TOOLS; arnew->alignment = RGN_ALIGN_LEFT; - ar = MEM_callocN(sizeof(ARegion), "tool props for file"); - BLI_insertlinkafter(&sa->regionbase, arnew, ar); - ar->regiontype = RGN_TYPE_TOOL_PROPS; - ar->alignment = RGN_ALIGN_BOTTOM | RGN_SPLIT_PREV; + return arnew; +} + +ARegion *file_tool_props_region(ScrArea *sa) +{ + ARegion *ar, *arnew; + + if ((ar = BKE_area_find_region_type(sa, RGN_TYPE_TOOL_PROPS)) != NULL) { + return ar; + } + + /* add subdiv level; after execute region */ + ar = BKE_area_find_region_type(sa, RGN_TYPE_EXECUTE); + + /* is error! */ + if (ar == NULL) { + return NULL; + } + + arnew = MEM_callocN(sizeof(ARegion), "tool props for file"); + BLI_insertlinkafter(&sa->regionbase, ar, arnew); + arnew->regiontype = RGN_TYPE_TOOL_PROPS; + arnew->alignment = RGN_ALIGN_RIGHT; return arnew; } @@ -2292,6 +2371,17 @@ void FILE_OT_bookmark_toggle(struct wmOperatorType *ot) ot->poll = ED_operator_file_active; /* <- important, handler is on window level */ } +static bool file_filenum_poll(bContext *C) +{ + SpaceFile *sfile = CTX_wm_space_file(C); + + if (!ED_operator_file_active(C)) { + return false; + } + + return sfile->params && (sfile->params->action_type == FILE_SAVE); +} + /** * Looks for a string of digits within name (using BLI_stringdec) and adjusts it by add. */ @@ -2349,7 +2439,7 @@ void FILE_OT_filenum(struct wmOperatorType *ot) /* api callbacks */ ot->exec = file_filenum_exec; - ot->poll = ED_operator_file_active; /* <- important, handler is on window level */ + ot->poll = file_filenum_poll; /* props */ RNA_def_int(ot->srna, "increment", 1, -100, 100, "Increment", "", -100, 100); diff --git a/source/blender/editors/space_file/file_panels.c b/source/blender/editors/space_file/file_panels.c index d9a6e70121f..b41358f575f 100644 --- a/source/blender/editors/space_file/file_panels.c +++ b/source/blender/editors/space_file/file_panels.c @@ -103,6 +103,7 @@ void file_panels_register(ARegionType *art) strcpy(pt->idname, "FILE_PT_operator"); strcpy(pt->label, N_("Operator")); strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA); + pt->flag = PNL_NO_HEADER; pt->poll = file_panel_operator_poll; pt->draw_header = file_panel_operator_header; pt->draw = file_panel_operator; diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index f7dda1defe8..46302e0d087 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -329,25 +329,20 @@ enum { FL_IS_PENDING = 1 << 2, FL_NEED_SORTING = 1 << 3, FL_NEED_FILTERING = 1 << 4, + FL_SORT_INVERT = 1 << 5, }; -#define SPECIAL_IMG_SIZE 48 -#define SPECIAL_IMG_ROWS 4 -#define SPECIAL_IMG_COLS 4 +#define SPECIAL_IMG_SIZE 256 +#define SPECIAL_IMG_ROWS 1 +#define SPECIAL_IMG_COLS 6 enum { - SPECIAL_IMG_FOLDER = 0, - SPECIAL_IMG_PARENT = 1, - SPECIAL_IMG_REFRESH = 2, - SPECIAL_IMG_BLENDFILE = 3, - SPECIAL_IMG_SOUNDFILE = 4, - SPECIAL_IMG_MOVIEFILE = 5, - SPECIAL_IMG_PYTHONFILE = 6, - SPECIAL_IMG_TEXTFILE = 7, - SPECIAL_IMG_FONTFILE = 8, - SPECIAL_IMG_UNKNOWNFILE = 9, - SPECIAL_IMG_LOADING = 10, - SPECIAL_IMG_BACKUP = 11, + SPECIAL_IMG_DOCUMENT = 0, + SPECIAL_IMG_FOLDER = 1, + SPECIAL_IMG_PARENT = 2, + SPECIAL_IMG_DRIVE_FIXED = 3, + SPECIAL_IMG_DRIVE_ATTACHED = 4, + SPECIAL_IMG_DRIVE_REMOTE = 5, SPECIAL_IMG_MAX, }; @@ -369,6 +364,19 @@ static void filelist_cache_clear(FileListEntryCache *cache, size_t new_size); /* ********** Sort helpers ********** */ +struct FileSortData { + bool inverted; +}; + +static int compare_apply_inverted(int val, const struct FileSortData *sort_data) +{ + return sort_data->inverted ? -val : val; +} + +/** + * Handles inverted sorting itself (currently there's nothing to invert), so if this returns non-0, + * it should be used as-is and not inverted. + */ static int compare_direntry_generic(const FileListInternEntry *entry1, const FileListInternEntry *entry2) { @@ -420,10 +428,11 @@ static int compare_direntry_generic(const FileListInternEntry *entry1, return 0; } -static int compare_name(void *UNUSED(user_data), const void *a1, const void *a2) +static int compare_name(void *user_data, const void *a1, const void *a2) { const FileListInternEntry *entry1 = a1; const FileListInternEntry *entry2 = a2; + const struct FileSortData *sort_data = user_data; char *name1, *name2; int ret; @@ -434,13 +443,14 @@ static int compare_name(void *UNUSED(user_data), const void *a1, const void *a2) name1 = entry1->name; name2 = entry2->name; - return BLI_natstrcmp(name1, name2); + return compare_apply_inverted(BLI_strcasecmp_natural(name1, name2), sort_data); } -static int compare_date(void *UNUSED(user_data), const void *a1, const void *a2) +static int compare_date(void *user_data, const void *a1, const void *a2) { const FileListInternEntry *entry1 = a1; const FileListInternEntry *entry2 = a2; + const struct FileSortData *sort_data = user_data; char *name1, *name2; int64_t time1, time2; int ret; @@ -452,22 +462,23 @@ static int compare_date(void *UNUSED(user_data), const void *a1, const void *a2) time1 = (int64_t)entry1->st.st_mtime; time2 = (int64_t)entry2->st.st_mtime; if (time1 < time2) { - return 1; + return compare_apply_inverted(1, sort_data); } if (time1 > time2) { - return -1; + return compare_apply_inverted(-1, sort_data); } name1 = entry1->name; name2 = entry2->name; - return BLI_natstrcmp(name1, name2); + return compare_apply_inverted(BLI_strcasecmp_natural(name1, name2), sort_data); } -static int compare_size(void *UNUSED(user_data), const void *a1, const void *a2) +static int compare_size(void *user_data, const void *a1, const void *a2) { const FileListInternEntry *entry1 = a1; const FileListInternEntry *entry2 = a2; + const struct FileSortData *sort_data = user_data; char *name1, *name2; uint64_t size1, size2; int ret; @@ -479,22 +490,23 @@ static int compare_size(void *UNUSED(user_data), const void *a1, const void *a2) size1 = entry1->st.st_size; size2 = entry2->st.st_size; if (size1 < size2) { - return 1; + return compare_apply_inverted(1, sort_data); } if (size1 > size2) { - return -1; + return compare_apply_inverted(-1, sort_data); } name1 = entry1->name; name2 = entry2->name; - return BLI_natstrcmp(name1, name2); + return compare_apply_inverted(BLI_strcasecmp_natural(name1, name2), sort_data); } -static int compare_extension(void *UNUSED(user_data), const void *a1, const void *a2) +static int compare_extension(void *user_data, const void *a1, const void *a2) { const FileListInternEntry *entry1 = a1; const FileListInternEntry *entry2 = a2; + const struct FileSortData *sort_data = user_data; char *name1, *name2; int ret; @@ -516,10 +528,10 @@ static int compare_extension(void *UNUSED(user_data), const void *a1, const void return -1; } if (entry1->blentype < entry2->blentype) { - return -1; + return compare_apply_inverted(-1, sort_data); } if (entry1->blentype > entry2->blentype) { - return 1; + return compare_apply_inverted(1, sort_data); } } else { @@ -539,48 +551,58 @@ static int compare_extension(void *UNUSED(user_data), const void *a1, const void } if ((ret = BLI_strcasecmp(sufix1, sufix2))) { - return ret; + return compare_apply_inverted(ret, sort_data); } } name1 = entry1->name; name2 = entry2->name; - return BLI_natstrcmp(name1, name2); + return compare_apply_inverted(BLI_strcasecmp_natural(name1, name2), sort_data); } void filelist_sort(struct FileList *filelist) { if ((filelist->flags & FL_NEED_SORTING) && (filelist->sort != FILE_SORT_NONE)) { + void *sort_cb = NULL; + switch (filelist->sort) { case FILE_SORT_ALPHA: - BLI_listbase_sort_r(&filelist->filelist_intern.entries, compare_name, NULL); + sort_cb = compare_name; break; case FILE_SORT_TIME: - BLI_listbase_sort_r(&filelist->filelist_intern.entries, compare_date, NULL); + sort_cb = compare_date; break; case FILE_SORT_SIZE: - BLI_listbase_sort_r(&filelist->filelist_intern.entries, compare_size, NULL); + sort_cb = compare_size; break; case FILE_SORT_EXTENSION: - BLI_listbase_sort_r(&filelist->filelist_intern.entries, compare_extension, NULL); + sort_cb = compare_extension; break; case FILE_SORT_NONE: /* Should never reach this point! */ default: BLI_assert(0); break; } + BLI_listbase_sort_r( + &filelist->filelist_intern.entries, + sort_cb, + &(struct FileSortData){.inverted = (filelist->flags & FL_SORT_INVERT) != 0}); filelist_filter_clear(filelist); filelist->flags &= ~FL_NEED_SORTING; } } -void filelist_setsorting(struct FileList *filelist, const short sort) +void filelist_setsorting(struct FileList *filelist, const short sort, bool invert_sort) { - if (filelist->sort != sort) { + const bool was_invert_sort = filelist->flags & FL_SORT_INVERT; + + if ((filelist->sort != sort) || (was_invert_sort != invert_sort)) { filelist->sort = sort; filelist->flags |= FL_NEED_SORTING; + filelist->flags = invert_sort ? (filelist->flags | FL_SORT_INVERT) : + (filelist->flags & ~FL_SORT_INVERT); } } @@ -635,9 +657,9 @@ static bool is_filtered_file(FileListInternEntry *file, { bool is_filtered = !is_hidden_file(file->relpath, filter); - if (is_filtered && (filter->flags & FLF_DO_FILTER) && !FILENAME_IS_CURRPAR(file->relpath)) { + if (is_filtered && !FILENAME_IS_CURRPAR(file->relpath)) { /* We only check for types if some type are enabled in filtering. */ - if (filter->filter) { + if (filter->filter && (filter->flags & FLF_DO_FILTER)) { if (file->typeflag & FILE_TYPE_DIR) { if (file->typeflag & (FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) { @@ -657,6 +679,7 @@ static bool is_filtered_file(FileListInternEntry *file, } } } + /* If there's a filter string, apply it as filter even if FLF_DO_FILTER is not set. */ if (is_filtered && (filter->filter_search[0] != '\0')) { if (fnmatch(filter->filter_search, file->relpath, FNM_CASEFOLD) != 0) { is_filtered = false; @@ -676,9 +699,9 @@ static bool is_filtered_lib(FileListInternEntry *file, const char *root, FileLis if (BLO_library_path_explode(path, dir, &group, &name)) { is_filtered = !is_hidden_file(file->relpath, filter); - if (is_filtered && (filter->flags & FLF_DO_FILTER) && !FILENAME_IS_CURRPAR(file->relpath)) { + if (is_filtered && !FILENAME_IS_CURRPAR(file->relpath)) { /* We only check for types if some type are enabled in filtering. */ - if (filter->filter || filter->filter_id) { + if ((filter->filter || filter->filter_id) && (filter->flags & FLF_DO_FILTER)) { if (file->typeflag & FILE_TYPE_DIR) { if (file->typeflag & (FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) { @@ -704,6 +727,7 @@ static bool is_filtered_lib(FileListInternEntry *file, const char *root, FileLis } } } + /* If there's a filter string, apply it as filter even if FLF_DO_FILTER is not set. */ if (is_filtered && (filter->filter_search[0] != '\0')) { if (fnmatch(filter->filter_search, file->relpath, FNM_CASEFOLD) != 0) { is_filtered = false; @@ -904,42 +928,12 @@ static ImBuf *filelist_geticon_image_ex(const unsigned int typeflag, const char if (FILENAME_IS_PARENT(relpath)) { ibuf = gSpecialFileImages[SPECIAL_IMG_PARENT]; } - else if (FILENAME_IS_CURRENT(relpath)) { - ibuf = gSpecialFileImages[SPECIAL_IMG_REFRESH]; - } else { ibuf = gSpecialFileImages[SPECIAL_IMG_FOLDER]; } } - else if (typeflag & FILE_TYPE_BLENDER) { - ibuf = gSpecialFileImages[SPECIAL_IMG_BLENDFILE]; - } - else if (typeflag & FILE_TYPE_BLENDERLIB) { - ibuf = gSpecialFileImages[SPECIAL_IMG_UNKNOWNFILE]; - } - else if (typeflag & (FILE_TYPE_MOVIE)) { - ibuf = gSpecialFileImages[SPECIAL_IMG_MOVIEFILE]; - } - else if (typeflag & FILE_TYPE_SOUND) { - ibuf = gSpecialFileImages[SPECIAL_IMG_SOUNDFILE]; - } - else if (typeflag & FILE_TYPE_PYSCRIPT) { - ibuf = gSpecialFileImages[SPECIAL_IMG_PYTHONFILE]; - } - else if (typeflag & FILE_TYPE_FTFONT) { - ibuf = gSpecialFileImages[SPECIAL_IMG_FONTFILE]; - } - else if (typeflag & FILE_TYPE_TEXT) { - ibuf = gSpecialFileImages[SPECIAL_IMG_TEXTFILE]; - } - else if (typeflag & FILE_TYPE_IMAGE) { - ibuf = gSpecialFileImages[SPECIAL_IMG_LOADING]; - } - else if (typeflag & FILE_TYPE_BLENDER_BACKUP) { - ibuf = gSpecialFileImages[SPECIAL_IMG_BACKUP]; - } else { - ibuf = gSpecialFileImages[SPECIAL_IMG_UNKNOWNFILE]; + ibuf = gSpecialFileImages[SPECIAL_IMG_DOCUMENT]; } return ibuf; @@ -1001,10 +995,13 @@ static int filelist_geticon_ex(const int typeflag, return ICON_FILE_BLANK; } else if (typeflag & FILE_TYPE_COLLADA) { - return ICON_FILE_BLANK; + return ICON_FILE_3D; } else if (typeflag & FILE_TYPE_ALEMBIC) { - return ICON_FILE_BLANK; + return ICON_FILE_3D; + } + else if (typeflag & FILE_TYPE_OBJECT_IO) { + return ICON_FILE_3D; } else if (typeflag & FILE_TYPE_TEXT) { return ICON_FILE_TEXT; @@ -1243,7 +1240,8 @@ static void filelist_cache_previews_clear(FileListEntryCache *cache) BLI_task_pool_cancel(cache->previews_pool); while ((preview = BLI_thread_queue_pop_timeout(cache->previews_done, 0))) { - // printf("%s: DONE %d - %s - %p\n", __func__, preview->index, preview->path, preview->img); + // printf("%s: DONE %d - %s - %p\n", __func__, preview->index, preview->path, + // preview->img); if (preview->img) { IMB_freeImBuf(preview->img); } @@ -2128,6 +2126,9 @@ int ED_path_extension_type(const char *path) else if (BLI_path_extension_check(path, ".abc")) { return FILE_TYPE_ALEMBIC; } + else if (BLI_path_extension_check_n(path, ".obj", ".3ds", ".fbx", ".glb", ".gltf", NULL)) { + return FILE_TYPE_OBJECT_IO; + } else if (BLI_path_extension_check_array(path, imb_ext_image)) { return FILE_TYPE_IMAGE; } @@ -2177,9 +2178,9 @@ int ED_file_extension_icon(const char *path) case FILE_TYPE_BTX: return ICON_FILE_BLANK; case FILE_TYPE_COLLADA: - return ICON_FILE_BLANK; case FILE_TYPE_ALEMBIC: - return ICON_FILE_BLANK; + case FILE_TYPE_OBJECT_IO: + return ICON_FILE_3D; case FILE_TYPE_TEXT: return ICON_FILE_TEXT; default: @@ -2298,6 +2299,19 @@ unsigned int filelist_entry_select_index_get(FileList *filelist, return 0; } +/** + * Set selection of the '..' parent entry, but only if it's actually visible. + */ +void filelist_entry_parent_select_set(FileList *filelist, + FileSelType select, + unsigned int flag, + FileCheckType check) +{ + if ((filelist->filter_data.flags & FLF_HIDE_PARENT) == 0) { + filelist_entry_select_index_set(filelist, 0, select, flag, check); + } +} + /* WARNING! dir must be FILE_MAX_LIBEXTRA long! */ bool filelist_islibrary(struct FileList *filelist, char *dir, char **group) { diff --git a/source/blender/editors/space_file/filelist.h b/source/blender/editors/space_file/filelist.h index caf77246797..9b1107294ff 100644 --- a/source/blender/editors/space_file/filelist.h +++ b/source/blender/editors/space_file/filelist.h @@ -55,7 +55,7 @@ void folderlist_pushdir(struct ListBase *folderlist, const char *dir); const char *folderlist_peeklastdir(struct ListBase *folderdist); int folderlist_clear_next(struct SpaceFile *sfile); -void filelist_setsorting(struct FileList *filelist, const short sort); +void filelist_setsorting(struct FileList *filelist, const short sort, bool invert_sort); void filelist_sort(struct FileList *filelist); void filelist_setfilter_options(struct FileList *filelist, @@ -117,6 +117,10 @@ unsigned int filelist_entry_select_get(struct FileList *filelist, unsigned int filelist_entry_select_index_get(struct FileList *filelist, const int index, FileCheckType check); +void filelist_entry_parent_select_set(struct FileList *filelist, + FileSelType select, + unsigned int flag, + FileCheckType check); void filelist_setrecursion(struct FileList *filelist, const int recursion_level); diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index db42d007b8e..3223fe0c6ce 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -49,6 +49,8 @@ #include "BLI_utildefines.h" #include "BLI_fnmatch.h" +#include "BLT_translation.h" + #include "BKE_appdir.h" #include "BKE_context.h" #include "BKE_main.h" @@ -69,6 +71,8 @@ #include "file_intern.h" #include "filelist.h" +#define VERTLIST_MAJORCOLUMN_WIDTH (25 * UI_UNIT_X) + FileSelectParams *ED_fileselect_get_params(struct SpaceFile *sfile) { if (!sfile->params) { @@ -99,6 +103,8 @@ short ED_fileselect_set_params(SpaceFile *sfile) sfile->params->filter_glob[0] = '\0'; /* set the default thumbnails size */ sfile->params->thumbnail_size = 128; + /* Show size column by default. */ + sfile->params->details_flags = FILE_DETAILS_SIZE | FILE_DETAILS_DATETIME; } params = sfile->params; @@ -161,6 +167,10 @@ short ED_fileselect_set_params(SpaceFile *sfile) params->flag &= ~FILE_DIRSEL_ONLY; } + if ((prop = RNA_struct_find_property(op->ptr, "hide_props_region"))) { + params->flag |= RNA_property_boolean_get(op->ptr, prop) ? FILE_HIDE_TOOL_PROPS : 0; + } + params->filter = 0; if ((prop = RNA_struct_find_property(op->ptr, "filter_blender"))) { params->filter |= RNA_property_boolean_get(op->ptr, prop) ? FILE_TYPE_BLENDER : 0; @@ -261,6 +271,10 @@ short ED_fileselect_set_params(SpaceFile *sfile) params->sort = FILE_SORT_ALPHA; } + if ((prop = RNA_struct_find_property(op->ptr, "action_type"))) { + params->action_type = RNA_property_enum_get(op->ptr, prop); + } + if (params->display == FILE_DEFAULTDISPLAY) { if (params->display_previous == FILE_DEFAULTDISPLAY) { if (U.uiflag & USER_SHOW_THUMBNAILS) { @@ -268,11 +282,11 @@ short ED_fileselect_set_params(SpaceFile *sfile) params->display = FILE_IMGDISPLAY; } else { - params->display = FILE_SHORTDISPLAY; + params->display = FILE_VERTICALDISPLAY; } } else { - params->display = FILE_SHORTDISPLAY; + params->display = FILE_VERTICALDISPLAY; } } else { @@ -293,7 +307,7 @@ short ED_fileselect_set_params(SpaceFile *sfile) params->type = FILE_UNIX; params->flag |= FILE_HIDE_DOT; params->flag &= ~FILE_DIRSEL_ONLY; - params->display = FILE_SHORTDISPLAY; + params->display = FILE_VERTICALDISPLAY; params->display_previous = FILE_DEFAULTDISPLAY; params->sort = FILE_SORT_ALPHA; params->filter = 0; @@ -344,7 +358,7 @@ void ED_fileselect_reset_params(SpaceFile *sfile) void fileselect_file_set(SpaceFile *sfile, const int index) { const struct FileDirEntry *file = filelist_file(sfile->files, index); - if (file && file->relpath && file->relpath[0] && !(file->typeflag & FILE_TYPE_FOLDER)) { + if (file && file->relpath && file->relpath[0] && !(file->typeflag & FILE_TYPE_DIR)) { BLI_strncpy(sfile->params->file, file->relpath, FILE_MAXFILE); } } @@ -372,10 +386,10 @@ int ED_fileselect_layout_numfiles(FileLayout *layout, ARegion *ar) } else { const int y_item = layout->tile_h + (2 * layout->tile_border_y); - const int y_view = (int)(BLI_rctf_size_y(&ar->v2d.cur)); + const int y_view = (int)(BLI_rctf_size_y(&ar->v2d.cur)) - layout->offset_top; const int y_over = y_item - (y_view % y_item); numfiles = (int)((float)(y_view + y_over) / (float)(y_item)); - return numfiles * layout->columns; + return numfiles * layout->flow_columns; } } @@ -395,19 +409,19 @@ FileSelection ED_fileselect_layout_offset_rect(FileLayout *layout, const rcti *r } colmin = (rect->xmin) / (layout->tile_w + 2 * layout->tile_border_x); - rowmin = (rect->ymin) / (layout->tile_h + 2 * layout->tile_border_y); + rowmin = (rect->ymin - layout->offset_top) / (layout->tile_h + 2 * layout->tile_border_y); colmax = (rect->xmax) / (layout->tile_w + 2 * layout->tile_border_x); - rowmax = (rect->ymax) / (layout->tile_h + 2 * layout->tile_border_y); + rowmax = (rect->ymax - layout->offset_top) / (layout->tile_h + 2 * layout->tile_border_y); - if (is_inside(colmin, rowmin, layout->columns, layout->rows) || - is_inside(colmax, rowmax, layout->columns, layout->rows)) { - CLAMP(colmin, 0, layout->columns - 1); + if (is_inside(colmin, rowmin, layout->flow_columns, layout->rows) || + is_inside(colmax, rowmax, layout->flow_columns, layout->rows)) { + CLAMP(colmin, 0, layout->flow_columns - 1); CLAMP(rowmin, 0, layout->rows - 1); - CLAMP(colmax, 0, layout->columns - 1); + CLAMP(colmax, 0, layout->flow_columns - 1); CLAMP(rowmax, 0, layout->rows - 1); } - if ((colmin > layout->columns - 1) || (rowmin > layout->rows - 1)) { + if ((colmin > layout->flow_columns - 1) || (rowmin > layout->rows - 1)) { sel.first = -1; } else { @@ -415,10 +429,10 @@ FileSelection ED_fileselect_layout_offset_rect(FileLayout *layout, const rcti *r sel.first = layout->rows * colmin + rowmin; } else { - sel.first = colmin + layout->columns * rowmin; + sel.first = colmin + layout->flow_columns * rowmin; } } - if ((colmax > layout->columns - 1) || (rowmax > layout->rows - 1)) { + if ((colmax > layout->flow_columns - 1) || (rowmax > layout->rows - 1)) { sel.last = -1; } else { @@ -426,7 +440,7 @@ FileSelection ED_fileselect_layout_offset_rect(FileLayout *layout, const rcti *r sel.last = layout->rows * colmax + rowmax; } else { - sel.last = colmax + layout->columns * rowmax; + sel.last = colmax + layout->flow_columns * rowmax; } } @@ -443,9 +457,9 @@ int ED_fileselect_layout_offset(FileLayout *layout, int x, int y) } offsetx = (x) / (layout->tile_w + 2 * layout->tile_border_x); - offsety = (y) / (layout->tile_h + 2 * layout->tile_border_y); + offsety = (y - layout->offset_top) / (layout->tile_h + 2 * layout->tile_border_y); - if (offsetx > layout->columns - 1) { + if (offsetx > layout->flow_columns - 1) { return -1; } if (offsety > layout->rows - 1) { @@ -456,25 +470,121 @@ int ED_fileselect_layout_offset(FileLayout *layout, int x, int y) active_file = layout->rows * offsetx + offsety; } else { - active_file = offsetx + layout->columns * offsety; + active_file = offsetx + layout->flow_columns * offsety; } return active_file; } +/** + * Get the currently visible bounds of the layout in screen space. Matches View2D.mask minus the + * top column-header row. + */ +void ED_fileselect_layout_maskrect(const FileLayout *layout, const View2D *v2d, rcti *r_rect) +{ + *r_rect = v2d->mask; + r_rect->ymax -= layout->offset_top; +} + +bool ED_fileselect_layout_is_inside_pt(const FileLayout *layout, const View2D *v2d, int x, int y) +{ + rcti maskrect; + ED_fileselect_layout_maskrect(layout, v2d, &maskrect); + return BLI_rcti_isect_pt(&maskrect, x, y); +} + +bool ED_fileselect_layout_isect_rect(const FileLayout *layout, + const View2D *v2d, + const rcti *rect, + rcti *r_dst) +{ + rcti maskrect; + ED_fileselect_layout_maskrect(layout, v2d, &maskrect); + return BLI_rcti_isect(&maskrect, rect, r_dst); +} + void ED_fileselect_layout_tilepos(FileLayout *layout, int tile, int *x, int *y) { if (layout->flag == FILE_LAYOUT_HOR) { *x = layout->tile_border_x + (tile / layout->rows) * (layout->tile_w + 2 * layout->tile_border_x); - *y = layout->tile_border_y + + *y = layout->offset_top + layout->tile_border_y + (tile % layout->rows) * (layout->tile_h + 2 * layout->tile_border_y); } else { *x = layout->tile_border_x + - ((tile) % layout->columns) * (layout->tile_w + 2 * layout->tile_border_x); - *y = layout->tile_border_y + - ((tile) / layout->columns) * (layout->tile_h + 2 * layout->tile_border_y); + ((tile) % layout->flow_columns) * (layout->tile_w + 2 * layout->tile_border_x); + *y = layout->offset_top + layout->tile_border_y + + ((tile) / layout->flow_columns) * (layout->tile_h + 2 * layout->tile_border_y); + } +} + +/** + * Check if the region coordinate defined by \a x and \a y are inside the column header. + */ +bool file_attribute_column_header_is_inside(const View2D *v2d, + const FileLayout *layout, + int x, + int y) +{ + rcti header_rect = v2d->mask; + header_rect.ymin = header_rect.ymax - layout->attribute_column_header_h; + return BLI_rcti_isect_pt(&header_rect, x, y); +} + +bool file_attribute_column_type_enabled(const FileSelectParams *params, + FileAttributeColumnType column) +{ + switch (column) { + case COLUMN_NAME: + /* Always enabled */ + return true; + case COLUMN_DATETIME: + return (params->details_flags & FILE_DETAILS_DATETIME) != 0; + case COLUMN_SIZE: + return (params->details_flags & FILE_DETAILS_SIZE) != 0; + default: + return false; + } +} + +/** + * Find the column type at region coordinate given by \a x (y doesn't matter for this). + */ +FileAttributeColumnType file_attribute_column_type_find_isect(const View2D *v2d, + const FileSelectParams *params, + FileLayout *layout, + int x) +{ + float mx, my; + int offset_tile; + + UI_view2d_region_to_view(v2d, x, v2d->mask.ymax - layout->offset_top - 1, &mx, &my); + offset_tile = ED_fileselect_layout_offset( + layout, (int)(v2d->tot.xmin + mx), (int)(v2d->tot.ymax - my)); + if (offset_tile > -1) { + int tile_x, tile_y; + int pos_x = 0; + int rel_x; /* x relative to the hovered tile */ + + ED_fileselect_layout_tilepos(layout, offset_tile, &tile_x, &tile_y); + /* Column header drawing doesn't use left tile border, so subtract it. */ + rel_x = mx - (tile_x - layout->tile_border_x); + + for (FileAttributeColumnType column = 0; column < ATTRIBUTE_COLUMN_MAX; column++) { + if (!file_attribute_column_type_enabled(params, column)) { + continue; + } + const int width = layout->attribute_columns[column].width; + + if (IN_RANGE(rel_x, pos_x, pos_x + width)) { + return column; + } + + pos_x += width; + } } + + return COLUMN_NONE; } float file_string_width(const char *str) @@ -512,20 +622,52 @@ float file_font_pointsize(void) #endif } -static void column_widths(FileSelectParams *params, struct FileLayout *layout) +static void file_attribute_columns_widths(const FileSelectParams *params, FileLayout *layout) { - int i; + FileAttributeColumn *columns = layout->attribute_columns; const bool small_size = SMALL_SIZE_CHECK(params->thumbnail_size); + const int pad = small_size ? 0 : ATTRIBUTE_COLUMN_PADDING * 2; - for (i = 0; i < MAX_FILE_COLUMN; ++i) { - layout->column_widths[i] = 0; + for (int i = 0; i < ATTRIBUTE_COLUMN_MAX; ++i) { + layout->attribute_columns[i].width = 0; } - layout->column_widths[COLUMN_NAME] = ((float)params->thumbnail_size / 8.0f) * UI_UNIT_X; /* Biggest possible reasonable values... */ - layout->column_widths[COLUMN_DATE] = file_string_width(small_size ? "23/08/89" : "23-Dec-89"); - layout->column_widths[COLUMN_TIME] = file_string_width("23:59"); - layout->column_widths[COLUMN_SIZE] = file_string_width(small_size ? "98.7 M" : "98.7 MiB"); + columns[COLUMN_DATETIME].width = file_string_width(small_size ? "23/08/89" : + "23 Dec 6789, 23:59") + + pad; + columns[COLUMN_SIZE].width = file_string_width(small_size ? "98.7 M" : "098.7 MB") + pad; + if (params->display == FILE_IMGDISPLAY) { + columns[COLUMN_NAME].width = ((float)params->thumbnail_size / 8.0f) * UI_UNIT_X; + } + /* Name column uses remaining width */ + else { + int remwidth = layout->tile_w; + for (FileAttributeColumnType column_type = ATTRIBUTE_COLUMN_MAX - 1; column_type >= 0; + column_type--) { + if ((column_type == COLUMN_NAME) || + !file_attribute_column_type_enabled(params, column_type)) { + continue; + } + remwidth -= columns[column_type].width; + } + columns[COLUMN_NAME].width = remwidth; + } +} + +static void file_attribute_columns_init(const FileSelectParams *params, FileLayout *layout) +{ + file_attribute_columns_widths(params, layout); + + layout->attribute_columns[COLUMN_NAME].name = N_("Name"); + layout->attribute_columns[COLUMN_NAME].sort_type = FILE_SORT_ALPHA; + layout->attribute_columns[COLUMN_NAME].text_align = UI_STYLE_TEXT_LEFT; + layout->attribute_columns[COLUMN_DATETIME].name = N_("Date Modified"); + layout->attribute_columns[COLUMN_DATETIME].sort_type = FILE_SORT_TIME; + layout->attribute_columns[COLUMN_DATETIME].text_align = UI_STYLE_TEXT_LEFT; + layout->attribute_columns[COLUMN_SIZE].name = N_("Size"); + layout->attribute_columns[COLUMN_SIZE].sort_type = FILE_SORT_SIZE; + layout->attribute_columns[COLUMN_SIZE].text_align = UI_STYLE_TEXT_RIGHT; } void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *ar) @@ -533,7 +675,6 @@ void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *ar) FileSelectParams *params = ED_fileselect_get_params(sfile); FileLayout *layout = NULL; View2D *v2d = &ar->v2d; - int maxlen = 0; int numfiles; int textheight; @@ -560,57 +701,66 @@ void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *ar) layout->tile_w = layout->prv_w + 2 * layout->prv_border_x; layout->tile_h = layout->prv_h + 2 * layout->prv_border_y + textheight; layout->width = (int)(BLI_rctf_size_x(&v2d->cur) - 2 * layout->tile_border_x); - layout->columns = layout->width / (layout->tile_w + 2 * layout->tile_border_x); - if (layout->columns > 0) { - layout->rows = numfiles / layout->columns + 1; // XXX dirty, modulo is zero + layout->flow_columns = layout->width / (layout->tile_w + 2 * layout->tile_border_x); + layout->attribute_column_header_h = 0; + layout->offset_top = 0; + if (layout->flow_columns > 0) { + layout->rows = numfiles / layout->flow_columns + 1; // XXX dirty, modulo is zero } else { - layout->columns = 1; + layout->flow_columns = 1; layout->rows = numfiles + 1; // XXX dirty, modulo is zero } layout->height = sfile->layout->rows * (layout->tile_h + 2 * layout->tile_border_y) + - layout->tile_border_y * 2; + layout->tile_border_y * 2 - layout->offset_top; layout->flag = FILE_LAYOUT_VER; } - else { - int column_space = 0.6f * UI_UNIT_X; - int column_icon_space = 0.2f * UI_UNIT_X; + else if (params->display == FILE_VERTICALDISPLAY) { + int rowcount; - layout->prv_w = 0; - layout->prv_h = 0; + layout->prv_w = ((float)params->thumbnail_size / 20.0f) * UI_UNIT_X; + layout->prv_h = ((float)params->thumbnail_size / 20.0f) * UI_UNIT_Y; + layout->tile_border_x = 0.4f * UI_UNIT_X; + layout->tile_border_y = 0.1f * UI_UNIT_Y; + layout->tile_h = textheight * 3 / 2; + layout->width = (int)(BLI_rctf_size_x(&v2d->cur) - 2 * layout->tile_border_x); + layout->tile_w = layout->width; + layout->flow_columns = 1; + layout->attribute_column_header_h = layout->tile_h * 1.2f + 2 * layout->tile_border_y; + layout->offset_top = layout->attribute_column_header_h; + rowcount = (int)(BLI_rctf_size_y(&v2d->cur) - layout->offset_top - 2 * layout->tile_border_y) / + (layout->tile_h + 2 * layout->tile_border_y); + file_attribute_columns_init(params, layout); + + layout->rows = MAX2(rowcount, numfiles); + BLI_assert(layout->rows != 0); + layout->height = sfile->layout->rows * (layout->tile_h + 2 * layout->tile_border_y) + + layout->tile_border_y * 2 + layout->offset_top; + layout->flag = FILE_LAYOUT_VER; + } + else if (params->display == FILE_HORIZONTALDISPLAY) { + layout->prv_w = ((float)params->thumbnail_size / 20.0f) * UI_UNIT_X; + layout->prv_h = ((float)params->thumbnail_size / 20.0f) * UI_UNIT_Y; layout->tile_border_x = 0.4f * UI_UNIT_X; layout->tile_border_y = 0.1f * UI_UNIT_Y; - layout->prv_border_x = 0; - layout->prv_border_y = 0; layout->tile_h = textheight * 3 / 2; + layout->attribute_column_header_h = 0; + layout->offset_top = layout->attribute_column_header_h; layout->height = (int)(BLI_rctf_size_y(&v2d->cur) - 2 * layout->tile_border_y); /* Padding by full scrollbar H is too much, can overlap tile border Y. */ layout->rows = (layout->height - V2D_SCROLL_HEIGHT + layout->tile_border_y) / (layout->tile_h + 2 * layout->tile_border_y); + layout->tile_w = VERTLIST_MAJORCOLUMN_WIDTH; + file_attribute_columns_init(params, layout); - column_widths(params, layout); - - if (params->display == FILE_SHORTDISPLAY) { - maxlen = ICON_DEFAULT_WIDTH_SCALE + column_icon_space + - (int)layout->column_widths[COLUMN_NAME] + column_space + - (int)layout->column_widths[COLUMN_SIZE] + column_space; - } - else { - maxlen = ICON_DEFAULT_WIDTH_SCALE + column_icon_space + - (int)layout->column_widths[COLUMN_NAME] + column_space + - (int)layout->column_widths[COLUMN_DATE] + column_space + - (int)layout->column_widths[COLUMN_TIME] + column_space + - (int)layout->column_widths[COLUMN_SIZE] + column_space; - } - layout->tile_w = maxlen; if (layout->rows > 0) { - layout->columns = numfiles / layout->rows + 1; // XXX dirty, modulo is zero + layout->flow_columns = numfiles / layout->rows + 1; // XXX dirty, modulo is zero } else { layout->rows = 1; - layout->columns = numfiles + 1; // XXX dirty, modulo is zero + layout->flow_columns = numfiles + 1; // XXX dirty, modulo is zero } - layout->width = sfile->layout->columns * (layout->tile_w + 2 * layout->tile_border_x) + + layout->width = sfile->layout->flow_columns * (layout->tile_w + 2 * layout->tile_border_x) + layout->tile_border_x * 2; layout->flag = FILE_LAYOUT_HOR; } diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index 1fd878e4662..1befdd52d7d 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -72,23 +72,40 @@ static SpaceLink *file_new(const ScrArea *UNUSED(area), const Scene *UNUSED(scen /* Ignore user preference "USER_HEADER_BOTTOM" here (always show top for new types). */ ar->alignment = RGN_ALIGN_TOP; + /* ui list region */ + ar = MEM_callocN(sizeof(ARegion), "ui region for file"); + BLI_addtail(&sfile->regionbase, ar); + ar->regiontype = RGN_TYPE_UI; + ar->alignment = RGN_ALIGN_TOP; + ar->flag |= RGN_FLAG_DYNAMIC_SIZE; + /* Tools region */ ar = MEM_callocN(sizeof(ARegion), "tools region for file"); BLI_addtail(&sfile->regionbase, ar); ar->regiontype = RGN_TYPE_TOOLS; ar->alignment = RGN_ALIGN_LEFT; + /* Tools region (lower split region) */ + ar = MEM_callocN(sizeof(ARegion), "lower tools region for file"); + BLI_addtail(&sfile->regionbase, ar); + ar->regiontype = RGN_TYPE_TOOLS; + ar->alignment = RGN_ALIGN_BOTTOM | RGN_SPLIT_PREV; + ar->flag |= RGN_FLAG_DYNAMIC_SIZE; + + /* Execute region */ + ar = MEM_callocN(sizeof(ARegion), "execute region for file"); + BLI_addtail(&sfile->regionbase, ar); + ar->regiontype = RGN_TYPE_EXECUTE; + ar->alignment = RGN_ALIGN_BOTTOM; + ar->flag |= RGN_FLAG_DYNAMIC_SIZE; + /* Tool props region is added as needed. */ +#if 0 /* Tool props (aka operator) region */ ar = MEM_callocN(sizeof(ARegion), "tool props region for file"); BLI_addtail(&sfile->regionbase, ar); ar->regiontype = RGN_TYPE_TOOL_PROPS; - ar->alignment = RGN_ALIGN_BOTTOM | RGN_SPLIT_PREV; - - /* ui list region */ - ar = MEM_callocN(sizeof(ARegion), "ui region for file"); - BLI_addtail(&sfile->regionbase, ar); - ar->regiontype = RGN_TYPE_UI; - ar->alignment = RGN_ALIGN_TOP; + ar->alignment = RGN_ALIGN_RIGHT; +#endif /* main region */ ar = MEM_callocN(sizeof(ARegion), "main region for file"); @@ -204,6 +221,7 @@ static SpaceLink *file_duplicate(SpaceLink *sl) static void file_refresh(const bContext *C, ScrArea *sa) { wmWindowManager *wm = CTX_wm_manager(C); + wmWindow *win = CTX_wm_window(C); SpaceFile *sfile = CTX_wm_space_file(C); FileSelectParams *params = ED_fileselect_get_params(sfile); struct FSMenu *fsmenu = ED_fsmenu_get(); @@ -217,15 +235,16 @@ static void file_refresh(const bContext *C, ScrArea *sa) } filelist_setdir(sfile->files, params->dir); filelist_setrecursion(sfile->files, params->recursion_level); - filelist_setsorting(sfile->files, params->sort); - filelist_setfilter_options(sfile->files, - (params->flag & FILE_FILTER) != 0, - (params->flag & FILE_HIDE_DOT) != 0, - false, /* TODO hide_parent, should be controllable? */ - params->filter, - params->filter_id, - params->filter_glob, - params->filter_search); + filelist_setsorting(sfile->files, params->sort, params->flag & FILE_SORT_INVERT); + filelist_setfilter_options( + sfile->files, + (params->flag & FILE_FILTER) != 0, + (params->flag & FILE_HIDE_DOT) != 0, + true, /* Just always hide parent, prefer to not add an extra user option for this. */ + params->filter, + params->filter_id, + params->filter_glob, + params->filter_search); /* Update the active indices of bookmarks & co. */ sfile->systemnr = fsmenu_get_active_indices(fsmenu, FS_CATEGORY_SYSTEM, params->dir); @@ -254,7 +273,7 @@ static void file_refresh(const bContext *C, ScrArea *sa) else { filelist_cache_previews_set(sfile->files, false); if (sfile->previews_timer) { - WM_event_remove_timer_notifier(wm, CTX_wm_window(C), sfile->previews_timer); + WM_event_remove_timer_notifier(wm, win, sfile->previews_timer); sfile->previews_timer = NULL; } } @@ -269,10 +288,20 @@ static void file_refresh(const bContext *C, ScrArea *sa) /* Might be called with NULL sa, see file_main_region_draw() below. */ if (sa && BKE_area_find_region_type(sa, RGN_TYPE_TOOLS) == NULL) { - /* Create TOOLS/TOOL_PROPS regions. */ + /* Create TOOLS region. */ file_tools_region(sa); - ED_area_initialize(wm, CTX_wm_window(C), sa); + ED_area_initialize(wm, win, sa); + } + if (sa && sfile->op && BKE_area_find_region_type(sa, RGN_TYPE_TOOL_PROPS) == NULL) { + /* Create TOOL_PROPS region. */ + ARegion *region_props = file_tool_props_region(sa); + + if (params->flag & FILE_HIDE_TOOL_PROPS) { + region_props->flag |= RGN_FLAG_HIDDEN; + } + + ED_area_initialize(wm, win, sa); } ED_area_tag_redraw(sa); @@ -406,6 +435,11 @@ static void file_main_region_draw(const bContext *C, ARegion *ar) v2d->keepofs &= ~V2D_LOCKOFS_Y; v2d->keepofs |= V2D_LOCKOFS_X; } + else if (params->display == FILE_VERTICALDISPLAY) { + v2d->scroll = V2D_SCROLL_RIGHT; + v2d->keepofs &= ~V2D_LOCKOFS_Y; + v2d->keepofs |= V2D_LOCKOFS_X; + } else { v2d->scroll = V2D_SCROLL_BOTTOM; v2d->keepofs &= ~V2D_LOCKOFS_X; @@ -439,7 +473,9 @@ static void file_main_region_draw(const bContext *C, ARegion *ar) UI_view2d_view_restore(C); /* scrollers */ - scrollers = UI_view2d_scrollers_calc(v2d, NULL); + rcti view_rect; + ED_fileselect_layout_maskrect(sfile->layout, v2d, &view_rect); + scrollers = UI_view2d_scrollers_calc(v2d, &view_rect); UI_view2d_scrollers_draw(v2d, scrollers); UI_view2d_scrollers_free(scrollers); } @@ -452,6 +488,7 @@ static void file_operatortypes(void) WM_operatortype_append(FILE_OT_select_box); WM_operatortype_append(FILE_OT_select_bookmark); WM_operatortype_append(FILE_OT_highlight); + WM_operatortype_append(FILE_OT_sort_column_ui_context); WM_operatortype_append(FILE_OT_execute); WM_operatortype_append(FILE_OT_cancel); WM_operatortype_append(FILE_OT_parent); @@ -538,7 +575,8 @@ static void file_ui_region_init(wmWindowManager *wm, ARegion *ar) { wmKeyMap *keymap; - UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_HEADER, ar->winx, ar->winy); + ED_region_panels_init(wm, ar); + ar->v2d.keepzoom |= V2D_LOCKZOOM_X | V2D_LOCKZOOM_Y; /* own keymap */ keymap = WM_keymap_ensure(wm->defaultconf, "File Browser", SPACE_FILE, 0); @@ -550,22 +588,18 @@ static void file_ui_region_init(wmWindowManager *wm, ARegion *ar) static void file_ui_region_draw(const bContext *C, ARegion *ar) { - float col[3]; - /* clear */ - UI_GetThemeColor3fv(TH_BACK, col); - GPU_clear_color(col[0], col[1], col[2], 0.0); - GPU_clear(GPU_COLOR_BIT); - - /* scrolling here is just annoying, disable it */ - ar->v2d.cur.ymax = BLI_rctf_size_y(&ar->v2d.cur); - ar->v2d.cur.ymin = 0; - - /* set view2d view matrix for scrolling (without scrollers) */ - UI_view2d_view_ortho(&ar->v2d); + ED_region_panels(C, ar); +} - file_draw_buttons(C, ar); +static void file_execution_region_init(wmWindowManager *wm, ARegion *ar) +{ + ED_region_panels_init(wm, ar); + ar->v2d.keepzoom |= V2D_LOCKZOOM_X | V2D_LOCKZOOM_Y; +} - UI_view2d_view_restore(C); +static void file_execution_region_draw(const bContext *C, ARegion *ar) +{ + ED_region_panels(C, ar); } static void file_ui_region_listener(wmWindow *UNUSED(win), @@ -656,13 +690,21 @@ void ED_spacetype_file(void) /* regions: ui */ art = MEM_callocN(sizeof(ARegionType), "spacetype file region"); art->regionid = RGN_TYPE_UI; - art->prefsizey = 60; art->keymapflag = ED_KEYMAP_UI; art->listener = file_ui_region_listener; art->init = file_ui_region_init; art->draw = file_ui_region_draw; BLI_addhead(&st->regiontypes, art); + /* regions: execution */ + art = MEM_callocN(sizeof(ARegionType), "spacetype file region"); + art->regionid = RGN_TYPE_EXECUTE; + art->keymapflag = ED_KEYMAP_UI; + art->listener = file_ui_region_listener; + art->init = file_execution_region_init; + art->draw = file_execution_region_draw; + BLI_addhead(&st->regiontypes, art); + /* regions: channels (directories) */ art = MEM_callocN(sizeof(ARegionType), "spacetype file region"); art->regionid = RGN_TYPE_TOOLS; @@ -677,8 +719,8 @@ void ED_spacetype_file(void) /* regions: tool properties */ art = MEM_callocN(sizeof(ARegionType), "spacetype file operator region"); art->regionid = RGN_TYPE_TOOL_PROPS; - art->prefsizex = 0; - art->prefsizey = 360; + art->prefsizex = 240; + art->prefsizey = 60; art->keymapflag = ED_KEYMAP_UI; art->listener = file_tools_region_listener; art->init = file_tools_region_init; diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c index c727c5225c9..708d91a82bb 100644 --- a/source/blender/editors/space_graph/graph_buttons.c +++ b/source/blender/editors/space_graph/graph_buttons.c @@ -861,6 +861,11 @@ static void graph_panel_driverVar__transChan(uiLayout *layout, ID *id, DriverVar sub = uiLayoutColumn(layout, true); uiItemR(sub, &dtar_ptr, "transform_type", 0, NULL, ICON_NONE); + + if (ELEM(dtar->transChan, DTAR_TRANSCHAN_ROTX, DTAR_TRANSCHAN_ROTY, DTAR_TRANSCHAN_ROTZ)) { + uiItemR(sub, &dtar_ptr, "rotation_mode", 0, IFACE_("Mode"), ICON_NONE); + } + uiItemR(sub, &dtar_ptr, "transform_space", 0, IFACE_("Space"), ICON_NONE); } @@ -1236,7 +1241,7 @@ static void graph_panel_drivers_popover(const bContext *C, Panel *pa) { uiLayout *layout = pa->layout; - PointerRNA ptr = {{NULL}}; + PointerRNA ptr = {NULL}; PropertyRNA *prop = NULL; int index = -1; uiBut *but = NULL; @@ -1258,7 +1263,7 @@ static void graph_panel_drivers_popover(const bContext *C, Panel *pa) /* Populate Panel - With a combination of the contents of the Driven and Driver panels */ if (fcu && fcu->driver) { - ID *id = ptr.id.data; + ID *id = ptr.owner_id; PointerRNA ptr_fcurve; RNA_pointer_create(id, &RNA_FCurve, fcu, &ptr_fcurve); diff --git a/source/blender/editors/space_graph/graph_draw.c b/source/blender/editors/space_graph/graph_draw.c index e7ba498fc11..e18a440f7c0 100644 --- a/source/blender/editors/space_graph/graph_draw.c +++ b/source/blender/editors/space_graph/graph_draw.c @@ -205,7 +205,7 @@ static void draw_fcurve_keyframe_vertices(FCurve *fcu, View2D *v2d, bool edit, u { immBindBuiltinProgram(GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA); - immUniform1f("size", UI_GetThemeValuef(TH_VERTEX_SIZE) * U.pixelsize); + immUniform1f("size", UI_GetThemeValuef(TH_VERTEX_SIZE) * U.dpi_fac); draw_fcurve_selected_keyframe_vertices(fcu, v2d, edit, false, pos); draw_fcurve_selected_keyframe_vertices(fcu, v2d, edit, true, pos); @@ -269,8 +269,8 @@ static void draw_fcurve_handle_vertices(FCurve *fcu, immBindBuiltinProgram(GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_OUTLINE_AA); /* set handle size */ - immUniform1f("size", (1.4f * UI_GetThemeValuef(TH_HANDLE_VERTEX_SIZE)) * U.pixelsize); - immUniform1f("outlineWidth", 1.5f * U.pixelsize); + immUniform1f("size", (1.4f * UI_GetThemeValuef(TH_HANDLE_VERTEX_SIZE)) * U.dpi_fac); + immUniform1f("outlineWidth", 1.5f * U.dpi_fac); draw_fcurve_selected_handle_vertices(fcu, v2d, false, sel_handle_only, pos); draw_fcurve_selected_handle_vertices(fcu, v2d, true, sel_handle_only, pos); diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c index b624e21937f..329067de545 100644 --- a/source/blender/editors/space_graph/graph_edit.c +++ b/source/blender/editors/space_graph/graph_edit.c @@ -1538,7 +1538,7 @@ void GRAPH_OT_sound_bake(wmOperatorType *ot) FILE_TYPE_FOLDER | FILE_TYPE_SOUND | FILE_TYPE_MOVIE, FILE_SPECIAL, FILE_OPENFILE, - WM_FILESEL_FILEPATH, + WM_FILESEL_FILEPATH | WM_FILESEL_SHOW_PROPS, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); RNA_def_float(ot->srna, diff --git a/source/blender/editors/space_graph/graph_intern.h b/source/blender/editors/space_graph/graph_intern.h index 168c38b66a4..320240221b5 100644 --- a/source/blender/editors/space_graph/graph_intern.h +++ b/source/blender/editors/space_graph/graph_intern.h @@ -26,7 +26,6 @@ struct ARegion; struct ARegionType; -struct ScrArea; struct SpaceGraph; struct bAnimContext; struct bAnimListElem; diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c index 6c5ebf77bf4..91e5ab61dd9 100644 --- a/source/blender/editors/space_graph/space_graph.c +++ b/source/blender/editors/space_graph/space_graph.c @@ -527,7 +527,7 @@ static void graph_region_message_subscribe(const struct bContext *UNUSED(C), * so just whitelist the entire structs for updates */ { - wmMsgParams_RNA msg_key_params = {{{0}}}; + wmMsgParams_RNA msg_key_params = {{0}}; StructRNA *type_array[] = { &RNA_DopeSheet, /* dopesheet filters */ diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c index f1a29a1542d..64e1c02590e 100644 --- a/source/blender/editors/space_image/image_buttons.c +++ b/source/blender/editors/space_image/image_buttons.c @@ -991,7 +991,7 @@ void uiTemplateImage(uiLayout *layout, void uiTemplateImageSettings(uiLayout *layout, PointerRNA *imfptr, bool color_management) { ImageFormatData *imf = imfptr->data; - ID *id = imfptr->id.data; + ID *id = imfptr->owner_id; PointerRNA display_settings_ptr; PropertyRNA *prop; const int depth_ok = BKE_imtype_valid_depths(imf->imtype); diff --git a/source/blender/editors/space_image/image_intern.h b/source/blender/editors/space_image/image_intern.h index 2c723f45e94..1abb6715fdb 100644 --- a/source/blender/editors/space_image/image_intern.h +++ b/source/blender/editors/space_image/image_intern.h @@ -27,7 +27,6 @@ /* internal exports only */ struct ARegion; struct ARegionType; -struct ScrArea; struct SpaceImage; struct bContext; struct bNodeTree; diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 05ba82b8bde..e338a450db6 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -1431,7 +1431,7 @@ static int image_open_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED( Image *oldima; oldptr = RNA_property_pointer_get(&ptr, prop); - oldima = (Image *)oldptr.id.data; + oldima = (Image *)oldptr.owner_id; /* unlikely to fail but better avoid strange crash */ if (oldima && GS(oldima->id.name) == ID_IM) { ima = oldima; @@ -1773,6 +1773,7 @@ static int image_save_options_init(Main *bmain, } else { BLI_snprintf(opts->filepath, sizeof(opts->filepath), "//%s", ima->id.name + 2); + BLI_path_make_safe(opts->filepath); BLI_path_abs(opts->filepath, is_prev_save ? G.ima : BKE_main_blendfile_path(bmain)); } } diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index 33e77d3623e..5fa4fe3e077 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -586,7 +586,7 @@ static void image_main_region_draw(const bContext *C, ARegion *ar) float col[3]; /* XXX This is in order to draw UI batches with the DRW - * olg context since we now use it for drawing the entire area */ + * old context since we now use it for drawing the entire area. */ gpu_batch_presets_reset(); GPUViewport *viewport = diff --git a/source/blender/editors/space_info/info_stats.c b/source/blender/editors/space_info/info_stats.c index 946274de882..106edc290d5 100644 --- a/source/blender/editors/space_info/info_stats.c +++ b/source/blender/editors/space_info/info_stats.c @@ -574,7 +574,7 @@ void ED_info_stats_clear(ViewLayer *view_layer) const char *ED_info_stats_string(Main *bmain, Scene *scene, ViewLayer *view_layer) { - /* Loopin through dependency graph when interface is locked in not safe. + /* Looping through dependency graph when interface is locked in not safe. * Thew interface is marked as locked when jobs wants to modify the * dependency graph. */ wmWindowManager *wm = bmain->wm.first; diff --git a/source/blender/editors/space_info/textview.c b/source/blender/editors/space_info/textview.c index 24d3008b1e7..97d5faa9c13 100644 --- a/source/blender/editors/space_info/textview.c +++ b/source/blender/editors/space_info/textview.c @@ -313,7 +313,7 @@ int textview_draw( /* constants for the sequencer context */ cdc.font_id = font_id; cdc.cwidth = (int)BLF_fixed_width(font_id); - assert(cdc.cwidth > 0); + BLI_assert(cdc.cwidth > 0); cdc.lheight = tvc->lheight; cdc.lofs = -BLF_descender(font_id); /* note, scroll bar must be already subtracted () */ diff --git a/source/blender/editors/space_nla/nla_buttons.c b/source/blender/editors/space_nla/nla_buttons.c index 719efc7eeac..126b20a028e 100644 --- a/source/blender/editors/space_nla/nla_buttons.c +++ b/source/blender/editors/space_nla/nla_buttons.c @@ -268,8 +268,8 @@ static void nla_panel_animdata(const bContext *C, Panel *pa) /* icon + id-block name of block where AnimData came from to prevent * accidentally changing the properties of the wrong action */ - if (adt_ptr.id.data) { - ID *id = adt_ptr.id.data; + if (adt_ptr.owner_id) { + ID *id = adt_ptr.owner_id; PointerRNA id_ptr; RNA_id_pointer_create(id, &id_ptr); @@ -513,7 +513,7 @@ static void nla_panel_modifiers(const bContext *C, Panel *pa) for (fcm = strip->modifiers.first; fcm; fcm = fcm->next) { col = uiLayoutColumn(pa->layout, true); - ANIM_uiTemplate_fmodifier_draw(col, strip_ptr.id.data, &strip->modifiers, fcm); + ANIM_uiTemplate_fmodifier_draw(col, strip_ptr.owner_id, &strip->modifiers, fcm); } } diff --git a/source/blender/editors/space_nla/nla_channels.c b/source/blender/editors/space_nla/nla_channels.c index e5c116e85de..456eb783706 100644 --- a/source/blender/editors/space_nla/nla_channels.c +++ b/source/blender/editors/space_nla/nla_channels.c @@ -446,7 +446,7 @@ static int nlachannels_pushdown_exec(bContext *C, wmOperator *op) /* get anim-channel to use (or more specifically, the animdata block behind it) */ if (channel_index == -1) { - PointerRNA adt_ptr = {{NULL}}; + PointerRNA adt_ptr = {NULL}; /* active animdata block */ if (nla_panel_context(C, &adt_ptr, NULL, NULL) == 0 || (adt_ptr.data == NULL)) { @@ -458,7 +458,7 @@ static int nlachannels_pushdown_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } else { - id = adt_ptr.id.data; + id = adt_ptr.owner_id; adt = adt_ptr.data; } } @@ -581,7 +581,7 @@ static int nla_action_unlink_exec(bContext *C, wmOperator *op) /* do unlinking */ if (adt && adt->action) { bool force_delete = RNA_boolean_get(op->ptr, "force_delete"); - ED_animedit_unlink_action(C, adt_ptr.id.data, adt, adt->action, op->reports, force_delete); + ED_animedit_unlink_action(C, adt_ptr.owner_id, adt, adt->action, op->reports, force_delete); } return OPERATOR_FINISHED; diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c index 5cf9646210e..9d6ccd6fe35 100644 --- a/source/blender/editors/space_nla/nla_draw.c +++ b/source/blender/editors/space_nla/nla_draw.c @@ -141,8 +141,10 @@ static void nla_action_draw_keyframes( uint outline_color_id = GPU_vertformat_attr_add( format, "outlineColor", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); uint flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT); - immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND); + GPU_program_point_size(true); + immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND); + immUniform1f("outline_scale", 1.0f); immUniform2f("ViewportSize", BLI_rcti_size_x(&v2d->mask) + 1, BLI_rcti_size_y(&v2d->mask) + 1); immBegin(GPU_PRIM_POINTS, key_len); diff --git a/source/blender/editors/space_nla/space_nla.c b/source/blender/editors/space_nla/space_nla.c index 3bdd2804efc..f274f3c93ec 100644 --- a/source/blender/editors/space_nla/space_nla.c +++ b/source/blender/editors/space_nla/space_nla.c @@ -522,7 +522,7 @@ static void nla_channel_region_message_subscribe(const struct bContext *UNUSED(C * so just whitelist the entire struct for updates */ { - wmMsgParams_RNA msg_key_params = {{{0}}}; + wmMsgParams_RNA msg_key_params = {{0}}; StructRNA *type_array[] = { &RNA_DopeSheet, }; diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index 748e485f614..b89f163f579 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -24,6 +24,7 @@ #include "BLI_blenlib.h" #include "BLI_math.h" +#include "BLI_system.h" #include "DNA_node_types.h" #include "DNA_object_types.h" @@ -90,7 +91,7 @@ static void node_buts_value(uiLayout *layout, bContext *UNUSED(C), PointerRNA *p /* first output stores value */ bNodeSocket *output = node->outputs.first; PointerRNA sockptr; - RNA_pointer_create(ptr->id.data, &RNA_NodeSocket, output, &sockptr); + RNA_pointer_create(ptr->owner_id, &RNA_NodeSocket, output, &sockptr); uiItemR(layout, &sockptr, "default_value", 0, "", ICON_NONE); } @@ -102,7 +103,7 @@ static void node_buts_rgb(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr bNodeSocket *output = node->outputs.first; PointerRNA sockptr; uiLayout *col; - RNA_pointer_create(ptr->id.data, &RNA_NodeSocket, output, &sockptr); + RNA_pointer_create(ptr->owner_id, &RNA_NodeSocket, output, &sockptr); col = uiLayoutColumn(layout, false); uiTemplateColorPicker(col, &sockptr, "default_value", 1, 0, 0, 0); @@ -113,7 +114,7 @@ static void node_buts_mix_rgb(uiLayout *layout, bContext *UNUSED(C), PointerRNA { uiLayout *row, *col; - bNodeTree *ntree = (bNodeTree *)ptr->id.data; + bNodeTree *ntree = (bNodeTree *)ptr->owner_id; col = uiLayoutColumn(layout, false); row = uiLayoutRow(col, true); @@ -193,7 +194,7 @@ static void node_buts_normal(uiLayout *layout, bContext *UNUSED(C), PointerRNA * /* first output stores normal */ bNodeSocket *output = node->outputs.first; PointerRNA sockptr; - RNA_pointer_create(ptr->id.data, &RNA_NodeSocket, output, &sockptr); + RNA_pointer_create(ptr->owner_id, &RNA_NodeSocket, output, &sockptr); uiItemR(layout, &sockptr, "default_value", 0, "", ICON_NONE); } @@ -1186,6 +1187,11 @@ static void node_shader_buts_ambient_occlusion(uiLayout *layout, uiItemR(layout, ptr, "only_local", 0, NULL, ICON_NONE); } +static void node_shader_buts_white_noise(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "dimensions", 0, "", ICON_NONE); +} + /* only once called */ static void node_shader_set_butfunc(bNodeType *ntype) { @@ -1220,7 +1226,7 @@ static void node_shader_set_butfunc(bNodeType *ntype) case SH_NODE_MATH: ntype->draw_buttons = node_buts_math; break; - case SH_NODE_VECT_MATH: + case SH_NODE_VECTOR_MATH: ntype->draw_buttons = node_shader_buts_vect_math; break; case SH_NODE_VECT_TRANSFORM: @@ -1330,6 +1336,9 @@ static void node_shader_set_butfunc(bNodeType *ntype) case SH_NODE_AMBIENT_OCCLUSION: ntype->draw_buttons = node_shader_buts_ambient_occlusion; break; + case SH_NODE_TEX_WHITE_NOISE: + ntype->draw_buttons = node_shader_buts_white_noise; + break; } } @@ -1363,7 +1372,7 @@ static void node_composit_buts_image(uiLayout *layout, bContext *C, PointerRNA * bNode *node = ptr->data; PointerRNA imaptr, iuserptr; - RNA_pointer_create((ID *)ptr->id.data, &RNA_ImageUser, node->storage, &iuserptr); + RNA_pointer_create(ptr->owner_id, &RNA_ImageUser, node->storage, &iuserptr); uiLayoutSetContextPointer(layout, "image_user", &iuserptr); uiTemplateID(layout, C, @@ -1390,7 +1399,7 @@ static void node_composit_buts_image_ex(uiLayout *layout, bContext *C, PointerRN bNode *node = ptr->data; PointerRNA iuserptr; - RNA_pointer_create((ID *)ptr->id.data, &RNA_ImageUser, node->storage, &iuserptr); + RNA_pointer_create(ptr->owner_id, &RNA_ImageUser, node->storage, &iuserptr); uiLayoutSetContextPointer(layout, "image_user", &iuserptr); uiTemplateImage(layout, C, ptr, "image", &iuserptr, 0, 1); } @@ -1951,7 +1960,7 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi } /* XXX collection lookup does not return the ID part of the pointer, * setting this manually here */ - active_input_ptr.id.data = ptr->id.data; + active_input_ptr.owner_id = ptr->owner_id; col = uiLayoutColumn(row, true); ot = WM_operatortype_find("NODE_OT_output_file_move_active_socket", false); @@ -2695,6 +2704,10 @@ static void node_composit_buts_denoise(uiLayout *layout, bContext *UNUSED(C), Po { #ifndef WITH_OPENIMAGEDENOISE uiItemL(layout, IFACE_("Disabled, built without OpenImageDenoise"), ICON_ERROR); +#else + if (!BLI_cpu_support_sse41()) { + uiItemL(layout, IFACE_("Disabled, CPU with SSE4.1 is required"), ICON_ERROR); + } #endif uiItemR(layout, ptr, "use_hdr", 0, NULL, ICON_NONE); @@ -2959,7 +2972,7 @@ static void node_texture_buts_proc(uiLayout *layout, bContext *UNUSED(C), Pointe { PointerRNA tex_ptr; bNode *node = ptr->data; - ID *id = ptr->id.data; + ID *id = ptr->owner_id; Tex *tex = (Tex *)node->storage; uiLayout *col, *row; @@ -3053,7 +3066,7 @@ static void node_texture_buts_image_ex(uiLayout *layout, bContext *C, PointerRNA bNode *node = ptr->data; PointerRNA iuserptr; - RNA_pointer_create((ID *)ptr->id.data, &RNA_ImageUser, node->storage, &iuserptr); + RNA_pointer_create(ptr->owner_id, &RNA_ImageUser, node->storage, &iuserptr); uiTemplateImage(layout, C, ptr, "image", &iuserptr, 0, 0); } @@ -3115,7 +3128,7 @@ static void node_texture_set_butfunc(bNodeType *ntype) static void node_property_update_default(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) { - bNodeTree *ntree = ptr->id.data; + bNodeTree *ntree = (bNodeTree *)ptr->owner_id; bNode *node = ptr->data; ED_node_tag_update_nodetree(bmain, ntree, node); } @@ -3285,7 +3298,7 @@ static void node_file_output_socket_draw(bContext *C, PointerRNA *ptr, PointerRNA *node_ptr) { - bNodeTree *ntree = ptr->id.data; + bNodeTree *ntree = (bNodeTree *)ptr->owner_id; bNodeSocket *sock = ptr->data; uiLayout *row; PointerRNA inputptr, imfptr; @@ -3358,8 +3371,13 @@ static void std_node_socket_draw( uiTemplateComponentMenu(layout, ptr, "default_value", text); } else { - uiLayout *column = uiLayoutColumn(layout, true); - uiItemR(column, ptr, "default_value", 0, text, 0); + if (sock->typeinfo->subtype == PROP_DIRECTION) { + uiItemR(layout, ptr, "default_value", 0, "", ICON_NONE); + } + else { + uiLayout *column = uiLayoutColumn(layout, true); + uiItemR(column, ptr, "default_value", 0, text, ICON_NONE); + } } break; case SOCK_RGBA: diff --git a/source/blender/editors/space_node/node_draw.c b/source/blender/editors/space_node/node_draw.c index 1c3544077c4..6dc2182b684 100644 --- a/source/blender/editors/space_node/node_draw.c +++ b/source/blender/editors/space_node/node_draw.c @@ -713,15 +713,28 @@ static void node_draw_mute_line(View2D *v2d, SpaceNode *snode, bNode *node) GPU_blend(false); } -static void node_socket_circle_draw(const bContext *C, - bNodeTree *ntree, - PointerRNA node_ptr, - bNodeSocket *sock, - unsigned pos, - unsigned col) +/* flags used in gpu_shader_keyframe_diamond_frag.glsl */ +#define MARKER_SHAPE_DIAMOND 0x1 +#define MARKER_SHAPE_SQUARE 0xC +#define MARKER_SHAPE_CIRCLE 0x2 +#define MARKER_SHAPE_INNER_DOT 0x10 + +static void node_socket_draw(const bContext *C, + bNodeTree *ntree, + PointerRNA node_ptr, + bNodeSocket *sock, + unsigned pos_id, + unsigned col_id, + unsigned shape_id, + unsigned size_id, + unsigned outline_col_id, + float size, + bool selected) { PointerRNA ptr; float color[4]; + float outline_color[4]; + unsigned int flags = 0; RNA_pointer_create((ID *)ntree, &RNA_NodeSocket, sock, &ptr); sock->typeinfo->draw_color((bContext *)C, &ptr, &node_ptr, color); @@ -731,8 +744,44 @@ static void node_socket_circle_draw(const bContext *C, color[3] *= 0.25f; } - immAttr4fv(col, color); - immVertex2f(pos, sock->locx, sock->locy); + if (selected) { + UI_GetThemeColor4fv(TH_TEXT_HI, outline_color); + outline_color[3] = 0.9f; + } + else { + copy_v4_fl(outline_color, 0.0f); + outline_color[3] = 0.6f; + } + + /* sets shape flags */ + switch (sock->display_shape) { + case SOCK_DISPLAY_SHAPE_DIAMOND: + case SOCK_DISPLAY_SHAPE_DIAMOND_DOT: + flags = MARKER_SHAPE_DIAMOND; + break; + case SOCK_DISPLAY_SHAPE_SQUARE: + case SOCK_DISPLAY_SHAPE_SQUARE_DOT: + flags = MARKER_SHAPE_SQUARE; + break; + default: + case SOCK_DISPLAY_SHAPE_CIRCLE: + case SOCK_DISPLAY_SHAPE_CIRCLE_DOT: + flags = MARKER_SHAPE_CIRCLE; + break; + } + + if (ELEM(sock->display_shape, + SOCK_DISPLAY_SHAPE_DIAMOND_DOT, + SOCK_DISPLAY_SHAPE_SQUARE_DOT, + SOCK_DISPLAY_SHAPE_CIRCLE_DOT)) { + flags |= MARKER_SHAPE_INNER_DOT; + } + + immAttr4fv(col_id, color); + immAttr1u(shape_id, flags); + immAttr1f(size_id, size); + immAttr4fv(outline_col_id, outline_color); + immVertex2f(pos_id, sock->locx, sock->locy); } /* ************** Socket callbacks *********** */ @@ -888,26 +937,28 @@ void node_draw_sockets(View2D *v2d, PointerRNA node_ptr; RNA_pointer_create((ID *)ntree, &RNA_Node, node, &node_ptr); - float scale; - UI_view2d_scale_get(v2d, &scale, NULL); + bool selected = false; GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + uint pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + uint col_id = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + uint shape_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT); + uint size_id = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + uint outline_col_id = GPU_vertformat_attr_add( + format, "outlineColor", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); GPU_blend(true); GPU_program_point_size(true); - - immBindBuiltinProgram(GPU_SHADER_2D_POINT_UNIFORM_SIZE_VARYING_COLOR_OUTLINE_AA); + immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND); + immUniform1f("outline_scale", 0.7f); + immUniform2f("ViewportSize", -1.0f, -1.0f); /* set handle size */ - immUniform1f("size", 2.0f * NODE_SOCKSIZE * scale); /* 2 * size to have diameter */ + float scale; + UI_view2d_scale_get(v2d, &scale, NULL); + scale *= 2.25f * NODE_SOCKSIZE; if (!select_all) { - /* outline for unselected sockets */ - immUniform1f("outlineWidth", 1.0f); - immUniform4f("outlineColor", 0.0f, 0.0f, 0.0f, 0.6f); - immBeginAtMost(GPU_PRIM_POINTS, total_input_len + total_output_len); } @@ -923,7 +974,17 @@ void node_draw_sockets(View2D *v2d, continue; } - node_socket_circle_draw(C, ntree, node_ptr, sock, pos, col); + node_socket_draw(C, + ntree, + node_ptr, + sock, + pos_id, + col_id, + shape_id, + size_id, + outline_col_id, + scale, + selected); } /* socket outputs */ @@ -938,7 +999,17 @@ void node_draw_sockets(View2D *v2d, continue; } - node_socket_circle_draw(C, ntree, node_ptr, sock, pos, col); + node_socket_draw(C, + ntree, + node_ptr, + sock, + pos_id, + col_id, + shape_id, + size_id, + outline_col_id, + scale, + selected); } } @@ -949,10 +1020,8 @@ void node_draw_sockets(View2D *v2d, /* go back and draw selected sockets */ if (selected_input_len + selected_output_len > 0) { /* outline for selected sockets */ - float c[3]; - UI_GetThemeColor3fv(TH_TEXT_HI, c); - immUniform4f("outlineColor", c[0], c[1], c[2], 1.0f); - immUniform1f("outlineWidth", 1.5f); + + selected = true; immBegin(GPU_PRIM_POINTS, selected_input_len + selected_output_len); @@ -963,7 +1032,17 @@ void node_draw_sockets(View2D *v2d, continue; } if (select_all || (sock->flag & SELECT)) { - node_socket_circle_draw(C, ntree, node_ptr, sock, pos, col); + node_socket_draw(C, + ntree, + node_ptr, + sock, + pos_id, + col_id, + shape_id, + size_id, + outline_col_id, + scale, + selected); if (--selected_input_len == 0) { break; /* stop as soon as last one is drawn */ } @@ -978,7 +1057,17 @@ void node_draw_sockets(View2D *v2d, continue; } if (select_all || (sock->flag & SELECT)) { - node_socket_circle_draw(C, ntree, node_ptr, sock, pos, col); + node_socket_draw(C, + ntree, + node_ptr, + sock, + pos_id, + col_id, + shape_id, + size_id, + outline_col_id, + scale, + selected); if (--selected_output_len == 0) { break; /* stop as soon as last one is drawn */ } diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c index d31256a1425..dcf901216f5 100644 --- a/source/blender/editors/space_node/node_edit.c +++ b/source/blender/editors/space_node/node_edit.c @@ -1838,7 +1838,7 @@ static int node_output_file_add_socket_exec(bContext *C, wmOperator *op) if (ptr.data) { node = ptr.data; - ntree = ptr.id.data; + ntree = (bNodeTree *)ptr.owner_id; } else if (snode && snode->edittree) { ntree = snode->edittree; @@ -1886,7 +1886,7 @@ static int node_output_file_remove_active_socket_exec(bContext *C, wmOperator *U if (ptr.data) { node = ptr.data; - ntree = ptr.id.data; + ntree = (bNodeTree *)ptr.owner_id; } else if (snode && snode->edittree) { ntree = snode->edittree; @@ -2535,7 +2535,7 @@ static int node_shader_script_update_exec(bContext *C, wmOperator *op) /* get node */ if (nodeptr.data) { - ntree_base = nodeptr.id.data; + ntree_base = (bNodeTree *)nodeptr.owner_id; node = nodeptr.data; } else if (snode && snode->edittree) { @@ -2734,7 +2734,7 @@ static int node_cryptomatte_add_socket_exec(bContext *C, wmOperator *UNUSED(op)) if (ptr.data) { node = ptr.data; - ntree = ptr.id.data; + ntree = (bNodeTree *)ptr.owner_id; } else if (snode && snode->edittree) { ntree = snode->edittree; @@ -2778,7 +2778,7 @@ static int node_cryptomatte_remove_socket_exec(bContext *C, wmOperator *UNUSED(o if (ptr.data) { node = ptr.data; - ntree = ptr.id.data; + ntree = (bNodeTree *)ptr.owner_id; } else if (snode && snode->edittree) { ntree = snode->edittree; diff --git a/source/blender/editors/space_node/node_select.c b/source/blender/editors/space_node/node_select.c index 78f36719880..450cf28cce1 100644 --- a/source/blender/editors/space_node/node_select.c +++ b/source/blender/editors/space_node/node_select.c @@ -1234,8 +1234,18 @@ static uiBlock *node_find_menu(bContext *C, ARegion *ar, void *arg_op) UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_SEARCH_MENU); UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP); - but = uiDefSearchBut( - block, search, 0, ICON_VIEWZOOM, sizeof(search), 10, 10, 9 * UI_UNIT_X, UI_UNIT_Y, 0, 0, ""); + but = uiDefSearchBut(block, + search, + 0, + ICON_VIEWZOOM, + sizeof(search), + 10, + 10, + UI_searchbox_size_x(), + UI_UNIT_Y, + 0, + 0, + ""); UI_but_func_search_set(but, NULL, node_find_cb, op->type, false, node_find_call_cb, NULL); UI_but_flag_enable(but, UI_BUT_ACTIVATE_ON_INIT); @@ -1256,7 +1266,7 @@ static uiBlock *node_find_menu(bContext *C, ARegion *ar, void *arg_op) NULL); /* Move it downwards, mouse over button. */ - UI_block_bounds_set_popup(block, 6, (const int[2]){0, -UI_UNIT_Y}); + UI_block_bounds_set_popup(block, 0.3f * U.widget_unit, (const int[2]){0, -UI_UNIT_Y}); return block; } diff --git a/source/blender/editors/space_node/node_templates.c b/source/blender/editors/space_node/node_templates.c index 8cc57a82fe0..423dec13c69 100644 --- a/source/blender/editors/space_node/node_templates.c +++ b/source/blender/editors/space_node/node_templates.c @@ -441,7 +441,7 @@ static int ui_node_item_name_compare(const void *a, const void *b) { const bNodeType *type_a = *(const bNodeType **)a; const bNodeType *type_b = *(const bNodeType **)b; - return BLI_natstrcmp(type_a->ui_name, type_b->ui_name); + return BLI_strcasecmp_natural(type_a->ui_name, type_b->ui_name); } static bool ui_node_item_special_poll(const bNodeTree *UNUSED(ntree), const bNodeType *ntype) diff --git a/source/blender/editors/space_outliner/CMakeLists.txt b/source/blender/editors/space_outliner/CMakeLists.txt index d235dd47136..616915dbc2c 100644 --- a/source/blender/editors/space_outliner/CMakeLists.txt +++ b/source/blender/editors/space_outliner/CMakeLists.txt @@ -41,6 +41,7 @@ set(SRC outliner_edit.c outliner_ops.c outliner_select.c + outliner_sync.c outliner_tools.c outliner_tree.c outliner_utils.c diff --git a/source/blender/editors/space_outliner/outliner_collections.c b/source/blender/editors/space_outliner/outliner_collections.c index 4740c412083..309446db83b 100644 --- a/source/blender/editors/space_outliner/outliner_collections.c +++ b/source/blender/editors/space_outliner/outliner_collections.c @@ -89,7 +89,7 @@ Collection *outliner_collection_from_tree_element(const TreeElement *te) } else if (ELEM(tselem->type, TSE_SCENE_COLLECTION_BASE, TSE_VIEW_COLLECTION_BASE)) { Scene *scene = (Scene *)tselem->id; - return BKE_collection_master(scene); + return scene->master_collection; } else if (tselem->type == 0 && te->idcode == ID_GR) { return (Collection *)tselem->id; @@ -199,7 +199,7 @@ static int collection_new_exec(bContext *C, wmOperator *op) } if (data.collection == NULL || ID_IS_LINKED(data.collection)) { - data.collection = BKE_collection_master(scene); + data.collection = scene->master_collection; } if (ID_IS_LINKED(scene)) { @@ -514,14 +514,14 @@ static int collection_duplicate_exec(bContext *C, wmOperator *op) * This can happen when a whole scene is linked e.g. */ if (parent != NULL && ID_IS_LINKED(parent)) { Scene *scene = CTX_data_scene(C); - parent = ID_IS_LINKED(scene) ? NULL : BKE_collection_master(scene); + parent = ID_IS_LINKED(scene) ? NULL : scene->master_collection; } else if (parent != NULL && (parent->flag & COLLECTION_IS_MASTER) != 0) { Scene *scene = BKE_collection_master_scene_search(bmain, parent); BLI_assert(scene != NULL); if (ID_IS_LINKED(scene)) { scene = CTX_data_scene(C); - parent = ID_IS_LINKED(scene) ? NULL : BKE_collection_master(scene); + parent = ID_IS_LINKED(scene) ? NULL : scene->master_collection; } } diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.c b/source/blender/editors/space_outliner/outliner_dragdrop.c index d8276aa2bbc..6e30157d216 100644 --- a/source/blender/editors/space_outliner/outliner_dragdrop.c +++ b/source/blender/editors/space_outliner/outliner_dragdrop.c @@ -326,38 +326,57 @@ static bool parent_drop_poll(bContext *C, return false; } -static int parent_drop_exec(bContext *C, wmOperator *op) +static void parent_drop_set_parents( + bContext *C, ReportList *reports, wmDragID *drag, Object *parent, short parent_type) { - Object *par = NULL, *ob = NULL; Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - int partype = -1; - char parname[MAX_NAME], childname[MAX_NAME]; + SpaceOutliner *soops = CTX_wm_space_outliner(C); - partype = RNA_enum_get(op->ptr, "type"); - RNA_string_get(op->ptr, "parent", parname); - par = (Object *)BKE_libblock_find_name(bmain, ID_OB, parname); - RNA_string_get(op->ptr, "child", childname); - ob = (Object *)BKE_libblock_find_name(bmain, ID_OB, childname); + TreeElement *te = outliner_find_id(soops, &soops->tree, &parent->id); + Scene *scene = (Scene *)outliner_search_back(soops, te, ID_SCE); - if (ID_IS_LINKED(ob)) { - BKE_report(op->reports, RPT_INFO, "Can't edit library linked object"); - return OPERATOR_CANCELLED; + if (scene == NULL) { + /* currently outliner organized in a way, that if there's no parent scene + * element for object it means that all displayed objects belong to + * active scene and parenting them is allowed (sergey) + */ + + scene = CTX_data_scene(C); } - ED_object_parent_set(op->reports, C, scene, ob, par, partype, false, false, NULL); + bool parent_set = false; + bool linked_objects = false; - DEG_relations_tag_update(bmain); - WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); - WM_event_add_notifier(C, NC_OBJECT | ND_PARENT, NULL); + for (wmDragID *drag_id = drag; drag_id; drag_id = drag_id->next) { + if (GS(drag_id->id->name) == ID_OB) { + Object *object = (Object *)drag_id->id; - return OPERATOR_FINISHED; + /* Do nothing to linked data */ + if (ID_IS_LINKED(object)) { + linked_objects = true; + continue; + } + + if (ED_object_parent_set( + reports, C, scene, object, parent, parent_type, false, false, NULL)) { + parent_set = true; + } + } + } + + if (linked_objects) { + BKE_report(reports, RPT_INFO, "Can't edit library linked object(s)"); + } + + if (parent_set) { + DEG_relations_tag_update(bmain); + WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); + WM_event_add_notifier(C, NC_OBJECT | ND_PARENT, NULL); + } } static int parent_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - Main *bmain = CTX_data_main(C); - SpaceOutliner *soops = CTX_wm_space_outliner(C); TreeElement *te = outliner_drop_find(C, event); TreeStoreElem *tselem = te ? TREESTORE(te) : NULL; @@ -374,107 +393,15 @@ static int parent_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (ob == par) { return OPERATOR_CANCELLED; } - if (ID_IS_LINKED(ob)) { - BKE_report(op->reports, RPT_INFO, "Can't edit library linked object"); - return OPERATOR_CANCELLED; - } - - char childname[MAX_NAME]; - char parname[MAX_NAME]; - STRNCPY(childname, ob->id.name + 2); - STRNCPY(parname, par->id.name + 2); - RNA_string_set(op->ptr, "child", childname); - RNA_string_set(op->ptr, "parent", parname); - Scene *scene = (Scene *)outliner_search_back(soops, te, ID_SCE); - - if (scene == NULL) { - /* currently outlier organized in a way, that if there's no parent scene - * element for object it means that all displayed objects belong to - * active scene and parenting them is allowed (sergey) - */ - - scene = CTX_data_scene(C); + if (event->custom != EVT_DATA_DRAGDROP) { + return OPERATOR_CANCELLED; } - if ((par->type != OB_ARMATURE) && (par->type != OB_CURVE) && (par->type != OB_LATTICE)) { - int partype = 0; - if (ED_object_parent_set(op->reports, C, scene, ob, par, partype, false, false, NULL)) { - DEG_relations_tag_update(bmain); - WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); - WM_event_add_notifier(C, NC_OBJECT | ND_PARENT, NULL); - } - } - else { - /* Menu creation */ - wmOperatorType *ot = WM_operatortype_find("OUTLINER_OT_parent_drop", false); - uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("Set Parent To"), ICON_NONE); - uiLayout *layout = UI_popup_menu_layout(pup); - PointerRNA ptr; - - /* Cannot use uiItemEnumO()... have multiple properties to set. */ - uiItemFullO_ptr(layout, ot, IFACE_("Object"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); - RNA_string_set(&ptr, "parent", parname); - RNA_string_set(&ptr, "child", childname); - RNA_enum_set(&ptr, "type", PAR_OBJECT); - - /* par becomes parent, make the associated menus */ - if (par->type == OB_ARMATURE) { - uiItemFullO_ptr(layout, ot, IFACE_("Armature Deform"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); - RNA_string_set(&ptr, "parent", parname); - RNA_string_set(&ptr, "child", childname); - RNA_enum_set(&ptr, "type", PAR_ARMATURE); - - uiItemFullO_ptr( - layout, ot, IFACE_(" With Empty Groups"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); - RNA_string_set(&ptr, "parent", parname); - RNA_string_set(&ptr, "child", childname); - RNA_enum_set(&ptr, "type", PAR_ARMATURE_NAME); - - uiItemFullO_ptr( - layout, ot, IFACE_(" With Envelope Weights"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); - RNA_string_set(&ptr, "parent", parname); - RNA_string_set(&ptr, "child", childname); - RNA_enum_set(&ptr, "type", PAR_ARMATURE_ENVELOPE); - - uiItemFullO_ptr( - layout, ot, IFACE_(" With Automatic Weights"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); - RNA_string_set(&ptr, "parent", parname); - RNA_string_set(&ptr, "child", childname); - RNA_enum_set(&ptr, "type", PAR_ARMATURE_AUTO); - - uiItemFullO_ptr(layout, ot, IFACE_("Bone"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); - RNA_string_set(&ptr, "parent", parname); - RNA_string_set(&ptr, "child", childname); - RNA_enum_set(&ptr, "type", PAR_BONE); - } - else if (par->type == OB_CURVE) { - uiItemFullO_ptr(layout, ot, IFACE_("Curve Deform"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); - RNA_string_set(&ptr, "parent", parname); - RNA_string_set(&ptr, "child", childname); - RNA_enum_set(&ptr, "type", PAR_CURVE); - - uiItemFullO_ptr(layout, ot, IFACE_("Follow Path"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); - RNA_string_set(&ptr, "parent", parname); - RNA_string_set(&ptr, "child", childname); - RNA_enum_set(&ptr, "type", PAR_FOLLOW); - - uiItemFullO_ptr(layout, ot, IFACE_("Path Constraint"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); - RNA_string_set(&ptr, "parent", parname); - RNA_string_set(&ptr, "child", childname); - RNA_enum_set(&ptr, "type", PAR_PATH_CONST); - } - else if (par->type == OB_LATTICE) { - uiItemFullO_ptr(layout, ot, IFACE_("Lattice Deform"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); - RNA_string_set(&ptr, "parent", parname); - RNA_string_set(&ptr, "child", childname); - RNA_enum_set(&ptr, "type", PAR_LATTICE); - } - - UI_popup_menu_end(C, pup); + ListBase *lb = event->customdata; + wmDrag *drag = lb->first; - return OPERATOR_INTERFACE; - } + parent_drop_set_parents(C, op->reports, drag->ids.first, par, PAR_OBJECT); return OPERATOR_FINISHED; } @@ -488,17 +415,11 @@ void OUTLINER_OT_parent_drop(wmOperatorType *ot) /* api callbacks */ ot->invoke = parent_drop_invoke; - ot->exec = parent_drop_exec; ot->poll = ED_operator_outliner_active; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; - - /* properties */ - RNA_def_string(ot->srna, "child", "Object", MAX_NAME, "Child", "Child Object"); - RNA_def_string(ot->srna, "parent", "Object", MAX_NAME, "Parent", "Parent Object"); - RNA_def_enum(ot->srna, "type", prop_make_parent_types, 0, "Type", ""); } /* ******************** Parent Clear Operator *********************** */ @@ -549,13 +470,21 @@ static bool parent_clear_poll(bContext *C, static int parent_clear_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) { Main *bmain = CTX_data_main(C); - Object *ob = (Object *)WM_drag_ID_from_event(event, ID_OB); - if (ob == NULL) { + if (event->custom != EVT_DATA_DRAGDROP) { return OPERATOR_CANCELLED; } - ED_object_parent_clear(ob, 0); + ListBase *lb = event->customdata; + wmDrag *drag = lb->first; + + for (wmDragID *drag_id = drag->ids.first; drag_id; drag_id = drag_id->next) { + if (GS(drag_id->id->name) == ID_OB) { + Object *object = (Object *)drag_id->id; + + ED_object_parent_clear(object, 0); + } + } DEG_relations_tag_update(bmain); WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); @@ -608,7 +537,7 @@ static int scene_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent Collection *collection; if (scene != CTX_data_scene(C)) { /* when linking to an inactive scene link to the master collection */ - collection = BKE_collection_master(scene); + collection = scene->master_collection; } else { collection = CTX_data_collection(C); @@ -966,6 +895,12 @@ static int outliner_item_drag_drop_invoke(bContext *C, return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH); } + float view_mval[2]; + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &view_mval[0], &view_mval[1]); + if (outliner_item_is_co_within_close_toggle(te, view_mval[0])) { + return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH); + } + wmDrag *drag = WM_event_start_drag(C, data.icon, WM_DRAG_ID, NULL, 0.0, WM_DRAG_NOP); if (ELEM(GS(data.drag_id->name), ID_OB, ID_GR)) { @@ -1032,7 +967,7 @@ static int outliner_item_drag_drop_invoke(bContext *C, } else { Scene *scene = CTX_data_scene(C); - parent = BKE_collection_master(scene); + parent = scene->master_collection; } WM_drag_add_ID(drag, id, &parent->id); diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index e4881a6f13d..a2ca3254b30 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -31,6 +31,7 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_sequence_types.h" +#include "DNA_constraint_types.h" #include "DNA_object_force_types.h" #include "BLI_math.h" @@ -60,6 +61,7 @@ #include "ED_armature.h" #include "ED_keyframing.h" #include "ED_object.h" +#include "ED_outliner.h" #include "ED_screen.h" #include "WM_api.h" @@ -175,8 +177,9 @@ static void restrictbutton_r_lay_cb(bContext *C, void *poin, void *UNUSED(poin2) WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, poin); } -static void restrictbutton_bone_visibility_cb(bContext *C, void *UNUSED(poin), void *poin2) +static void restrictbutton_bone_visibility_cb(bContext *C, void *poin, void *poin2) { + bArmature *arm = (bArmature *)poin; Bone *bone = (Bone *)poin2; if (bone->flag & BONE_HIDDEN_P) { bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); @@ -187,6 +190,7 @@ static void restrictbutton_bone_visibility_cb(bContext *C, void *UNUSED(poin), v } WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL); + DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE); } static void restrictbutton_bone_select_cb(bContext *C, void *UNUSED(poin), void *poin2) @@ -848,6 +852,7 @@ typedef struct RestrictProperties { PropertyRNA *layer_collection_holdout, *layer_collection_indirect_only, *layer_collection_hide_viewport; PropertyRNA *modifier_show_viewport, *modifier_show_render; + PropertyRNA *constraint_enable; } RestrictProperties; /* We don't care about the value of the property @@ -865,6 +870,7 @@ typedef struct RestrictPropertiesActive { bool layer_collection_hide_viewport; bool modifier_show_viewport; bool modifier_show_render; + bool constraint_enable; } RestrictPropertiesActive; static void outliner_restrict_properties_enable_collection_set( @@ -878,6 +884,7 @@ static void outliner_restrict_properties_enable_collection_set( props_active->layer_collection_indirect_only = false; props_active->object_hide_render = false; props_active->modifier_show_render = false; + props_active->constraint_enable = false; } } @@ -891,6 +898,7 @@ static void outliner_restrict_properties_enable_collection_set( props_active->object_hide_viewport = false; props_active->base_hide_viewport = false; props_active->modifier_show_viewport = false; + props_active->constraint_enable = false; } } @@ -995,6 +1003,8 @@ static void outliner_draw_restrictbuts(uiBlock *block, props.modifier_show_viewport = RNA_struct_type_find_property(&RNA_Modifier, "show_viewport"); props.modifier_show_render = RNA_struct_type_find_property(&RNA_Modifier, "show_render"); + props.constraint_enable = RNA_struct_type_find_property(&RNA_Constraint, "mute"); + props.initialized = true; } @@ -1181,6 +1191,35 @@ static void outliner_draw_restrictbuts(uiBlock *block, } } } + else if (tselem->type == TSE_CONSTRAINT) { + bConstraint *con = (bConstraint *)te->directdata; + + PointerRNA ptr; + RNA_pointer_create(tselem->id, &RNA_Constraint, con, &ptr); + + if (soops->show_restrict_flags & SO_RESTRICT_HIDE) { + bt = uiDefIconButR_prop(block, + UI_BTYPE_ICON_TOGGLE, + 0, + 0, + (int)(ar->v2d.cur.xmax - restrict_offsets.hide), + te->ys, + UI_UNIT_X, + UI_UNIT_Y, + &ptr, + props.constraint_enable, + -1, + 0, + 0, + -1, + -1, + NULL); + UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK); + if (!props_active.constraint_enable) { + UI_but_flag_enable(bt, UI_BUT_INACTIVE); + } + } + } else if (tselem->type == TSE_MODIFIER) { ModifierData *md = (ModifierData *)te->directdata; @@ -1243,7 +1282,7 @@ static void outliner_draw_restrictbuts(uiBlock *block, UI_BTYPE_ICON_TOGGLE, BONE_HIDDEN_P, 0, - ICON_HIDE_OFF, + ICON_RESTRICT_VIEW_OFF, (int)(ar->v2d.cur.xmax - restrict_offsets.viewport), te->ys, UI_UNIT_X, @@ -1878,6 +1917,9 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) case TSE_DEFGROUP_BASE: data.icon = ICON_GROUP_VERTEX; break; + case TSE_DEFGROUP: + data.icon = ICON_GROUP_VERTEX; + break; case TSE_BONE: case TSE_EBONE: data.icon = ICON_BONE_DATA; @@ -1885,6 +1927,100 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) case TSE_CONSTRAINT_BASE: data.icon = ICON_CONSTRAINT; break; + case TSE_CONSTRAINT: { + bConstraint *con = te->directdata; + switch ((eBConstraint_Types)con->type) { + case CONSTRAINT_TYPE_CAMERASOLVER: + data.icon = ICON_CON_CAMERASOLVER; + break; + case CONSTRAINT_TYPE_FOLLOWTRACK: + data.icon = ICON_CON_FOLLOWTRACK; + break; + case CONSTRAINT_TYPE_OBJECTSOLVER: + data.icon = ICON_CON_OBJECTSOLVER; + break; + case CONSTRAINT_TYPE_LOCLIKE: + data.icon = ICON_CON_LOCLIKE; + break; + case CONSTRAINT_TYPE_ROTLIKE: + data.icon = ICON_CON_ROTLIKE; + break; + case CONSTRAINT_TYPE_SIZELIKE: + data.icon = ICON_CON_SIZELIKE; + break; + case CONSTRAINT_TYPE_TRANSLIKE: + data.icon = ICON_CON_TRANSLIKE; + break; + case CONSTRAINT_TYPE_DISTLIMIT: + data.icon = ICON_CON_DISTLIMIT; + break; + case CONSTRAINT_TYPE_LOCLIMIT: + data.icon = ICON_CON_LOCLIMIT; + break; + case CONSTRAINT_TYPE_ROTLIMIT: + data.icon = ICON_CON_ROTLIMIT; + break; + case CONSTRAINT_TYPE_SIZELIMIT: + data.icon = ICON_CON_SIZELIMIT; + break; + case CONSTRAINT_TYPE_SAMEVOL: + data.icon = ICON_CON_SAMEVOL; + break; + case CONSTRAINT_TYPE_TRANSFORM: + data.icon = ICON_CON_TRANSFORM; + break; + case CONSTRAINT_TYPE_TRANSFORM_CACHE: + data.icon = ICON_CON_TRANSFORM_CACHE; + break; + case CONSTRAINT_TYPE_CLAMPTO: + data.icon = ICON_CON_CLAMPTO; + break; + case CONSTRAINT_TYPE_DAMPTRACK: + data.icon = ICON_CON_TRACKTO; + break; + case CONSTRAINT_TYPE_KINEMATIC: + data.icon = ICON_CON_KINEMATIC; + break; + case CONSTRAINT_TYPE_LOCKTRACK: + data.icon = ICON_CON_LOCKTRACK; + break; + case CONSTRAINT_TYPE_SPLINEIK: + data.icon = ICON_CON_SPLINEIK; + break; + case CONSTRAINT_TYPE_STRETCHTO: + data.icon = ICON_CON_STRETCHTO; + break; + case CONSTRAINT_TYPE_TRACKTO: + data.icon = ICON_CON_TRACKTO; + break; + case CONSTRAINT_TYPE_ACTION: + data.icon = ICON_CON_ACTION; + break; + case CONSTRAINT_TYPE_ARMATURE: + data.icon = ICON_CON_ARMATURE; + break; + case CONSTRAINT_TYPE_CHILDOF: + data.icon = ICON_CON_CHILDOF; + break; + case CONSTRAINT_TYPE_MINMAX: + data.icon = ICON_CON_FLOOR; + break; + case CONSTRAINT_TYPE_FOLLOWPATH: + data.icon = ICON_CON_FOLLOWPATH; + break; + case CONSTRAINT_TYPE_PIVOT: + data.icon = ICON_CON_PIVOT; + break; + case CONSTRAINT_TYPE_SHRINKWRAP: + data.icon = ICON_CON_SHRINKWRAP; + break; + + default: + data.icon = ICON_DOT; + break; + } + break; + } case TSE_MODIFIER_BASE: data.icon = ICON_MODIFIER_DATA; break; @@ -2137,30 +2273,64 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) data.icon = ICON_GROUP_BONE; break; case TSE_SEQUENCE: - if (te->idcode == SEQ_TYPE_MOVIE) { - data.icon = ICON_SEQUENCE; - } - else if (te->idcode == SEQ_TYPE_META) { - data.icon = ICON_DOT; - } - else if (te->idcode == SEQ_TYPE_SCENE) { - data.icon = ICON_SCENE; - } - else if (te->idcode == SEQ_TYPE_SOUND_RAM) { - data.icon = ICON_SOUND; - } - else if (te->idcode == SEQ_TYPE_IMAGE) { - data.icon = ICON_IMAGE; - } - else { - data.icon = ICON_PARTICLES; + switch (te->idcode) { + case SEQ_TYPE_SCENE: + data.icon = ICON_SCENE_DATA; + break; + case SEQ_TYPE_MOVIECLIP: + data.icon = ICON_TRACKER; + break; + case SEQ_TYPE_MASK: + data.icon = ICON_MOD_MASK; + break; + case SEQ_TYPE_MOVIE: + data.icon = ICON_FILE_MOVIE; + break; + case SEQ_TYPE_SOUND_RAM: + data.icon = ICON_SOUND; + break; + case SEQ_TYPE_IMAGE: + data.icon = ICON_FILE_IMAGE; + break; + case SEQ_TYPE_COLOR: + case SEQ_TYPE_ADJUSTMENT: + data.icon = ICON_COLOR; + break; + case SEQ_TYPE_TEXT: + data.icon = ICON_FONT_DATA; + break; + case SEQ_TYPE_ADD: + case SEQ_TYPE_SUB: + case SEQ_TYPE_MUL: + case SEQ_TYPE_OVERDROP: + case SEQ_TYPE_ALPHAOVER: + case SEQ_TYPE_ALPHAUNDER: + case SEQ_TYPE_COLORMIX: + case SEQ_TYPE_MULTICAM: + case SEQ_TYPE_TRANSFORM: + case SEQ_TYPE_SPEED: + case SEQ_TYPE_GLOW: + case SEQ_TYPE_GAUSSIAN_BLUR: + data.icon = ICON_SHADERFX; + break; + case SEQ_TYPE_CROSS: + case SEQ_TYPE_GAMCROSS: + case SEQ_TYPE_WIPE: + data.icon = ICON_ARROW_LEFTRIGHT; + break; + case SEQ_TYPE_META: + data.icon = ICON_SEQ_STRIP_META; + break; + default: + data.icon = ICON_DOT; + break; } break; case TSE_SEQ_STRIP: data.icon = ICON_LIBRARY_DATA_DIRECT; break; case TSE_SEQUENCE_DUP: - data.icon = ICON_OBJECT_DATA; + data.icon = ICON_SEQ_STRIP_DUPLICATE; break; case TSE_RNA_STRUCT: if (RNA_struct_is_ID(te->rnaptr.type)) { @@ -2459,7 +2629,11 @@ static void tselem_draw_icon(uiBlock *block, return; } + /* Icon is covered by restrict buttons */ if (!is_clickable || x >= xmax) { + /* Reduce alpha to match icon buttons */ + alpha *= 0.8f; + /* placement of icons, copied from interface_widgets.c */ float aspect = (0.8f * UI_UNIT_Y) / ICON_DEFAULT_HEIGHT; x += 2.0f * aspect; @@ -2567,7 +2741,6 @@ static void outliner_draw_iconrow_doit(uiBlock *block, float ufac = UI_UNIT_X / 20.0f; float icon_color[4], icon_border[4]; outliner_icon_background_colors(icon_color, icon_border); - icon_color[3] *= alpha_fac; if (active == OL_DRAWSEL_ACTIVE) { UI_GetThemeColor4fv(TH_EDITED_OBJECT, icon_color); icon_border[3] = 0.3f; @@ -2592,6 +2765,9 @@ static void outliner_draw_iconrow_doit(uiBlock *block, GPU_blend(true); /* Roundbox disables. */ } + if (tselem->flag & TSE_HIGHLIGHTED) { + alpha_fac += 0.5; + } tselem_draw_icon(block, xmax, (float)*offsx, (float)ys, tselem, te, alpha_fac, false); te->xs = *offsx; te->ys = ys; @@ -2599,7 +2775,12 @@ static void outliner_draw_iconrow_doit(uiBlock *block, if (num_elements > 1) { outliner_draw_iconrow_number(fstyle, *offsx, ys, num_elements); + te->flag |= TE_ICONROW_MERGED; } + else { + te->flag |= TE_ICONROW; + } + (*offsx) += UI_UNIT_X; } @@ -2609,7 +2790,7 @@ static void outliner_draw_iconrow_doit(uiBlock *block, * We use a continuum of indices until we get to the object data-blocks * and we then make room for the object types. */ -static int tree_element_id_type_to_index(TreeElement *te) +int tree_element_id_type_to_index(TreeElement *te) { TreeStoreElem *tselem = TREESTORE(te); @@ -2739,7 +2920,7 @@ static void outliner_set_coord_tree_element(TreeElement *te, int startx, int sta TreeElement *ten; /* closed items may be displayed in row of parent, don't change their coordinate! */ - if ((te->flag & TE_ICONROW) == 0) { + if ((te->flag & TE_ICONROW) == 0 && (te->flag & TE_ICONROW_MERGED) == 0) { /* store coord and continue, we need coordinates for elements outside view too */ te->xs = startx; te->ys = starty; @@ -3193,6 +3374,7 @@ static void outliner_draw_highlights_recursive(unsigned pos, const SpaceOutliner *soops, const ListBase *lb, const float col_selection[4], + const float col_active[4], const float col_highlight[4], const float col_searchmatch[4], int start_x, @@ -3206,7 +3388,11 @@ static void outliner_draw_highlights_recursive(unsigned pos, const int start_y = *io_start_y; /* selection status */ - if (tselem->flag & TSE_SELECTED) { + if ((tselem->flag & TSE_ACTIVE) && (tselem->flag & TSE_SELECTED)) { + immUniformColor4fv(col_active); + immRecti(pos, 0, start_y, (int)ar->v2d.cur.xmax, start_y + UI_UNIT_Y); + } + else if (tselem->flag & TSE_SELECTED) { immUniformColor4fv(col_selection); immRecti(pos, 0, start_y, (int)ar->v2d.cur.xmax, start_y + UI_UNIT_Y); } @@ -3260,6 +3446,7 @@ static void outliner_draw_highlights_recursive(unsigned pos, soops, &te->subtree, col_selection, + col_active, col_highlight, col_searchmatch, start_x + UI_UNIT_X, @@ -3271,10 +3458,12 @@ static void outliner_draw_highlights_recursive(unsigned pos, static void outliner_draw_highlights(ARegion *ar, SpaceOutliner *soops, int startx, int *starty) { const float col_highlight[4] = {1.0f, 1.0f, 1.0f, 0.13f}; - float col_selection[4], col_searchmatch[4]; + float col_selection[4], col_active[4], col_searchmatch[4]; UI_GetThemeColor3fv(TH_SELECT_HIGHLIGHT, col_selection); col_selection[3] = 1.0f; /* no alpha */ + UI_GetThemeColor3fv(TH_SELECT_ACTIVE, col_active); + col_active[3] = 1.0f; /* no alpha */ UI_GetThemeColor4fv(TH_MATCH, col_searchmatch); col_searchmatch[3] = 0.5f; @@ -3282,8 +3471,16 @@ static void outliner_draw_highlights(ARegion *ar, SpaceOutliner *soops, int star GPUVertFormat *format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - outliner_draw_highlights_recursive( - pos, ar, soops, &soops->tree, col_selection, col_highlight, col_searchmatch, startx, starty); + outliner_draw_highlights_recursive(pos, + ar, + soops, + &soops->tree, + col_selection, + col_active, + col_highlight, + col_searchmatch, + startx, + starty); immUnbindProgram(); GPU_blend(false); } @@ -3439,6 +3636,17 @@ void draw_outliner(const bContext *C) outliner_build_tree(mainvar, scene, view_layer, soops, ar); // always + /* If global sync select is dirty, flag other outliners */ + if (ED_outliner_select_sync_is_dirty(C)) { + ED_outliner_select_sync_flag_outliners(C); + } + + /* Sync selection state from view layer */ + if (!ELEM(soops->outlinevis, SO_LIBRARIES, SO_DATA_API, SO_ID_ORPHANS) && + soops->flag & SO_SYNC_SELECT) { + outliner_sync_selection(C, soops); + } + /* force display to pixel coords */ v2d->flag |= (V2D_PIXELOFS_X | V2D_PIXELOFS_Y); /* set matrix for 2d-view controls */ diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c index de6e89e47c4..943993cb810 100644 --- a/source/blender/editors/space_outliner/outliner_edit.c +++ b/source/blender/editors/space_outliner/outliner_edit.c @@ -101,9 +101,15 @@ static int outliner_highlight_update(bContext *C, wmOperator *UNUSED(op), const ARegion *ar = CTX_wm_region(C); SpaceOutliner *soops = CTX_wm_space_outliner(C); - const float my = UI_view2d_region_to_view_y(&ar->v2d, event->mval[1]); - TreeElement *hovered_te = outliner_find_item_at_y(soops, &soops->tree, my); + float view_mval[2]; + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &view_mval[0], &view_mval[1]); + + TreeElement *hovered_te = outliner_find_item_at_y(soops, &soops->tree, view_mval[1]); + + if (hovered_te) { + hovered_te = outliner_find_item_at_x_in_row(soops, hovered_te, view_mval[0], NULL); + } bool changed = false; if (!hovered_te || !(hovered_te->store_elem->flag & TSE_HIGHLIGHTED)) { @@ -134,59 +140,108 @@ void OUTLINER_OT_highlight_update(wmOperatorType *ot) /* Toggle Open/Closed ------------------------------------------- */ -static int do_outliner_item_openclose( - bContext *C, SpaceOutliner *soops, TreeElement *te, const bool all, const float mval[2]) +/* Open or close a tree element, optionally toggling all children recursively */ +void outliner_item_openclose(TreeElement *te, bool open, bool toggle_all) { + TreeStoreElem *tselem = TREESTORE(te); - if (mval[1] > te->ys && mval[1] < te->ys + UI_UNIT_Y) { - TreeStoreElem *tselem = TREESTORE(te); + if (open) { + tselem->flag &= ~TSE_CLOSED; + } + else { + tselem->flag |= TSE_CLOSED; + } - /* all below close/open? */ - if (all) { - tselem->flag &= ~TSE_CLOSED; - outliner_flag_set( - &te->subtree, TSE_CLOSED, !outliner_flag_is_any_test(&te->subtree, TSE_CLOSED, 1)); - } - else { - if (tselem->flag & TSE_CLOSED) { - tselem->flag &= ~TSE_CLOSED; - } - else { - tselem->flag |= TSE_CLOSED; + if (toggle_all) { + outliner_flag_set(&te->subtree, TSE_CLOSED, !open); + } +} + +typedef struct OpenCloseData { + TreeStoreElem *prev_tselem; + bool open; + int x_location; +} OpenCloseData; + +static int outliner_item_openclose_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + ARegion *ar = CTX_wm_region(C); + SpaceOutliner *soops = CTX_wm_space_outliner(C); + + float view_mval[2]; + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &view_mval[0], &view_mval[1]); + + if (event->type == MOUSEMOVE) { + TreeElement *te = outliner_find_item_at_y(soops, &soops->tree, view_mval[1]); + + OpenCloseData *data = (OpenCloseData *)op->customdata; + + /* Only openclose if mouse is not over the previously toggled element */ + if (te && TREESTORE(te) != data->prev_tselem) { + + /* Only toggle openclose on the same level as the first clicked element */ + if (te->xs == data->x_location) { + outliner_item_openclose(te, data->open, false); + ED_region_tag_redraw(ar); } } - return 1; + if (te) { + data->prev_tselem = TREESTORE(te); + } + else { + data->prev_tselem = NULL; + } } + else if (event->val == KM_RELEASE) { + MEM_freeN(op->customdata); - for (te = te->subtree.first; te; te = te->next) { - if (do_outliner_item_openclose(C, soops, te, all, mval)) { - return 1; - } + return OPERATOR_FINISHED; } - return 0; + + return OPERATOR_RUNNING_MODAL; } -/* event can enterkey, then it opens/closes */ -static int outliner_item_openclose(bContext *C, wmOperator *op, const wmEvent *event) +static int outliner_item_openclose_invoke(bContext *C, wmOperator *op, const wmEvent *event) { ARegion *ar = CTX_wm_region(C); SpaceOutliner *soops = CTX_wm_space_outliner(C); - TreeElement *te; - float fmval[2]; - const bool all = RNA_boolean_get(op->ptr, "all"); - UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); + const bool toggle_all = RNA_boolean_get(op->ptr, "all"); - for (te = soops->tree.first; te; te = te->next) { - if (do_outliner_item_openclose(C, soops, te, all, fmval)) { - break; + float view_mval[2]; + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &view_mval[0], &view_mval[1]); + + TreeElement *te = outliner_find_item_at_y(soops, &soops->tree, view_mval[1]); + + if (te && outliner_item_is_co_within_close_toggle(te, view_mval[0])) { + TreeStoreElem *tselem = TREESTORE(te); + + const bool open = (tselem->flag & TSE_CLOSED) || + (toggle_all && (outliner_flag_is_any_test(&te->subtree, TSE_CLOSED, 1))); + + outliner_item_openclose(te, open, toggle_all); + ED_region_tag_redraw(ar); + + /* Only toggle once for single click toggling */ + if (event->type == LEFTMOUSE) { + return OPERATOR_FINISHED; } - } - ED_region_tag_redraw(ar); + /* Store last expanded tselem and x coordinate of disclosure triangle */ + OpenCloseData *toggle_data = MEM_callocN(sizeof(OpenCloseData), "open_close_data"); + toggle_data->prev_tselem = tselem; + toggle_data->open = open; + toggle_data->x_location = te->xs; - return OPERATOR_FINISHED; + /* Store the first clicked on element */ + op->customdata = toggle_data; + + WM_event_add_modal_handler(C, op); + return OPERATOR_RUNNING_MODAL; + } + + return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; } void OUTLINER_OT_item_openclose(wmOperatorType *ot) @@ -195,11 +250,12 @@ void OUTLINER_OT_item_openclose(wmOperatorType *ot) ot->idname = "OUTLINER_OT_item_openclose"; ot->description = "Toggle whether item under cursor is enabled or closed"; - ot->invoke = outliner_item_openclose; + ot->invoke = outliner_item_openclose_invoke; + ot->modal = outliner_item_openclose_modal; ot->poll = ED_operator_outliner_active; - RNA_def_boolean(ot->srna, "all", 1, "All", "Close or open all items"); + RNA_def_boolean(ot->srna, "all", false, "All", "Close or open all items"); } /* -------------------------------------------------------------------- */ @@ -330,10 +386,10 @@ void item_rename_cb(bContext *C, do_item_rename(ar, te, tselem, reports); } -static int do_outliner_item_rename(ReportList *reports, - ARegion *ar, - TreeElement *te, - const float mval[2]) +static void do_outliner_item_rename(ReportList *reports, + ARegion *ar, + TreeElement *te, + const float mval[2]) { if (mval[1] > te->ys && mval[1] < te->ys + UI_UNIT_Y) { TreeStoreElem *tselem = TREESTORE(te); @@ -341,17 +397,12 @@ static int do_outliner_item_rename(ReportList *reports, /* click on name */ if (mval[0] > te->xs + UI_UNIT_X * 2 && mval[0] < te->xend) { do_item_rename(ar, te, tselem, reports); - return 1; } - return 0; } for (te = te->subtree.first; te; te = te->next) { - if (do_outliner_item_rename(reports, ar, te, mval)) { - return 1; - } + do_outliner_item_rename(reports, ar, te, mval); } - return 0; } static int outliner_item_rename(bContext *C, wmOperator *op, const wmEvent *event) @@ -360,25 +411,34 @@ static int outliner_item_rename(bContext *C, wmOperator *op, const wmEvent *even SpaceOutliner *soops = CTX_wm_space_outliner(C); TreeElement *te; float fmval[2]; - bool changed = false; - UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); + /* Rename active element if key pressed, otherwise rename element at cursor coordinates */ + if (event->val == KM_PRESS) { + TreeElement *active_element = outliner_find_element_with_flag(&soops->tree, TSE_ACTIVE); - for (te = soops->tree.first; te; te = te->next) { - if (do_outliner_item_rename(op->reports, ar, te, fmval)) { - changed = true; - break; + if (active_element) { + do_item_rename(ar, active_element, TREESTORE(active_element), op->reports); + } + else { + BKE_report(op->reports, RPT_WARNING, "No active item to rename"); } } + else { + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); - return changed ? OPERATOR_FINISHED : OPERATOR_PASS_THROUGH; + for (te = soops->tree.first; te; te = te->next) { + do_outliner_item_rename(op->reports, ar, te, fmval); + } + } + + return OPERATOR_FINISHED; } void OUTLINER_OT_item_rename(wmOperatorType *ot) { ot->name = "Rename"; ot->idname = "OUTLINER_OT_item_rename"; - ot->description = "Rename item under cursor"; + ot->description = "Rename the active element"; ot->invoke = outliner_item_rename; @@ -1103,6 +1163,10 @@ static int outliner_select_all_exec(bContext *C, wmOperator *op) break; } + if (soops->flag & SO_SYNC_SELECT) { + ED_outliner_select_sync_from_outliner(C, soops); + } + DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); ED_region_tag_redraw_no_rebuild(ar); @@ -1179,20 +1243,17 @@ static int outliner_open_back(TreeElement *te) return retval; } -static int outliner_show_active_exec(bContext *C, wmOperator *UNUSED(op)) +/* Return element representing the active base or bone in the outliner, or NULL if none exists */ +static TreeElement *outliner_show_active_get_element(bContext *C, + SpaceOutliner *so, + ViewLayer *view_layer) { - SpaceOutliner *so = CTX_wm_space_outliner(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - ARegion *ar = CTX_wm_region(C); - View2D *v2d = &ar->v2d; - TreeElement *te; - int xdelta, ytop; Object *obact = OBACT(view_layer); if (!obact) { - return OPERATOR_CANCELLED; + return NULL; } te = outliner_find_id(so, &so->tree, &obact->id); @@ -1215,25 +1276,50 @@ static int outliner_show_active_exec(bContext *C, wmOperator *UNUSED(op)) } } - if (te) { - /* open up tree to active object/bone */ + return te; +} + +static void outliner_show_active(SpaceOutliner *so, ARegion *ar, TreeElement *te, ID *id) +{ + /* open up tree to active object/bone */ + if (TREESTORE(te)->id == id) { if (outliner_open_back(te)) { outliner_set_coordinates(ar, so); } + return; + } + + for (TreeElement *ten = te->subtree.first; ten; ten = ten->next) { + outliner_show_active(so, ar, ten, id); + } +} + +static int outliner_show_active_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceOutliner *so = CTX_wm_space_outliner(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + ARegion *ar = CTX_wm_region(C); + View2D *v2d = &ar->v2d; + + TreeElement *active_element = outliner_show_active_get_element(C, so, view_layer); - /* make te->ys center of view */ - ytop = te->ys + BLI_rcti_size_y(&v2d->mask) / 2; - if (ytop > 0) { - ytop = 0; + if (active_element) { + ID *id = TREESTORE(active_element)->id; + + /* Expand all elements in the outliner with matching ID */ + for (TreeElement *te = so->tree.first; te; te = te->next) { + outliner_show_active(so, ar, te, id); } - v2d->cur.ymax = (float)ytop; - v2d->cur.ymin = (float)(ytop - BLI_rcti_size_y(&v2d->mask)); + /* Center view on first element found */ + int size_y = BLI_rcti_size_y(&v2d->mask) + 1; + int ytop = (active_element->ys + (size_y / 2)); + int delta_y = ytop - v2d->cur.ymax; - /* make te->xs ==> te->xend center of view */ - xdelta = (int)(te->xs - v2d->cur.xmin); - v2d->cur.xmin += xdelta; - v2d->cur.xmax += xdelta; + outliner_scroll_view(ar, delta_y); + } + else { + return OPERATOR_CANCELLED; } ED_region_tag_redraw_no_rebuild(ar); @@ -1259,18 +1345,15 @@ void OUTLINER_OT_show_active(wmOperatorType *ot) static int outliner_scroll_page_exec(bContext *C, wmOperator *op) { ARegion *ar = CTX_wm_region(C); - int dy = BLI_rcti_size_y(&ar->v2d.mask); - int up = 0; + int size_y = BLI_rcti_size_y(&ar->v2d.mask) + 1; - if (RNA_boolean_get(op->ptr, "up")) { - up = 1; - } + bool up = RNA_boolean_get(op->ptr, "up"); - if (up == 0) { - dy = -dy; + if (!up) { + size_y = -size_y; } - ar->v2d.cur.ymin += dy; - ar->v2d.cur.ymax += dy; + + outliner_scroll_view(ar, size_y); ED_region_tag_redraw_no_rebuild(ar); @@ -1697,7 +1780,7 @@ static void tree_element_to_path(TreeElement *te, /* no ID, so check if entry is RNA-struct, * and if that RNA-struct is an ID datablock to extract info from. */ if (tse->type == TSE_RNA_STRUCT) { - /* ptr->data not ptr->id.data seems to be the one we want, + /* ptr->data not ptr->owner_id seems to be the one we want, * since ptr->data is sometimes the owner of this ID? */ if (RNA_struct_is_ID(ptr->type)) { *id = (ID *)ptr->data; diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h index fa28d119244..95e37dea249 100644 --- a/source/blender/editors/space_outliner/outliner_intern.h +++ b/source/blender/editors/space_outliner/outliner_intern.h @@ -40,7 +40,6 @@ struct TreeStoreElem; struct ViewLayer; struct bContext; struct bPoseChannel; -struct wmEvent; struct wmKeyConfig; struct wmOperatorType; @@ -50,6 +49,14 @@ typedef enum TreeElementInsertType { TE_INSERT_INTO, } TreeElementInsertType; +/* Use generic walk select after D4771 is committed */ +typedef enum WalkSelectDirection { + OUTLINER_SELECT_WALK_UP, + OUTLINER_SELECT_WALK_DOWN, + OUTLINER_SELECT_WALK_LEFT, + OUTLINER_SELECT_WALK_RIGHT, +} WalkSelectDirection; + typedef enum TreeTraversalAction { /* Continue traversal regularly, don't skip children. */ TRAVERSE_CONTINUE = 0, @@ -131,6 +138,9 @@ enum { TE_DISABLED = (1 << 4), TE_DRAGGING = (1 << 5), TE_CHILD_NOT_IN_COLLECTION = (1 << 6), + /* Child elements of the same type in the icon-row are drawn merged as one icon. + * This flag is set for an element that is part of these merged child icons. */ + TE_ICONROW_MERGED = (1 << 7), }; /* button events */ @@ -223,6 +233,8 @@ void outliner_collection_isolate_flag(struct Scene *scene, const char *propname, const bool value); +int tree_element_id_type_to_index(TreeElement *te); + /* outliner_select.c -------------------------------------------- */ eOLDrawState tree_element_type_active(struct bContext *C, struct Scene *scene, @@ -253,6 +265,10 @@ void outliner_object_mode_toggle(struct bContext *C, ViewLayer *view_layer, Base *base); +void outliner_element_activate(struct SpaceOutliner *soops, struct TreeStoreElem *tselem); + +bool outliner_item_is_co_within_close_toggle(TreeElement *te, float view_co_x); + /* outliner_edit.c ---------------------------------------------- */ typedef void (*outliner_operation_cb)(struct bContext *C, struct ReportList *, @@ -337,6 +353,8 @@ void item_object_mode_exit_cb(struct bContext *C, void outliner_set_coordinates(struct ARegion *ar, struct SpaceOutliner *soops); +void outliner_item_openclose(TreeElement *te, bool open, bool toggle_all); + /* outliner_dragdrop.c */ void outliner_dropboxes(void); @@ -364,6 +382,7 @@ void OUTLINER_OT_show_active(struct wmOperatorType *ot); void OUTLINER_OT_show_hierarchy(struct wmOperatorType *ot); void OUTLINER_OT_select_box(struct wmOperatorType *ot); +void OUTLINER_OT_select_walk(struct wmOperatorType *ot); void OUTLINER_OT_select_all(struct wmOperatorType *ot); void OUTLINER_OT_expanded_toggle(struct wmOperatorType *ot); @@ -380,6 +399,10 @@ void OUTLINER_OT_orphans_purge(struct wmOperatorType *ot); /* outliner_tools.c ---------------------------------------------- */ +void merged_element_search_menu_invoke(struct bContext *C, + TreeElement *parent_te, + TreeElement *activate_te); + void OUTLINER_OT_operation(struct wmOperatorType *ot); void OUTLINER_OT_scene_operation(struct wmOperatorType *ot); void OUTLINER_OT_object_operation(struct wmOperatorType *ot); @@ -439,7 +462,8 @@ TreeElement *outliner_find_item_at_y(const SpaceOutliner *soops, float view_co_y); TreeElement *outliner_find_item_at_x_in_row(const SpaceOutliner *soops, const TreeElement *parent_te, - float view_co_x); + float view_co_x, + bool *multiple_objects); TreeElement *outliner_find_tse(struct SpaceOutliner *soops, const TreeStoreElem *tse); TreeElement *outliner_find_tree_element(ListBase *lb, const TreeStoreElem *store_elem); TreeElement *outliner_find_parent_element(ListBase *lb, @@ -456,5 +480,12 @@ bool outliner_tree_traverse(const SpaceOutliner *soops, TreeTraversalFunc func, void *customdata); float outliner_restrict_columns_width(const struct SpaceOutliner *soops); +TreeElement *outliner_find_element_with_flag(const ListBase *lb, short flag); +bool outliner_is_element_visible(const TreeElement *te); +void outliner_scroll_view(struct ARegion *ar, int delta_y); + +/* outliner_sync.c ---------------------------------------------- */ + +void outliner_sync_selection(const struct bContext *C, struct SpaceOutliner *soops); #endif /* __OUTLINER_INTERN_H__ */ diff --git a/source/blender/editors/space_outliner/outliner_ops.c b/source/blender/editors/space_outliner/outliner_ops.c index f155a2d5f89..4b57d4ad771 100644 --- a/source/blender/editors/space_outliner/outliner_ops.c +++ b/source/blender/editors/space_outliner/outliner_ops.c @@ -50,6 +50,7 @@ void outliner_operatortypes(void) WM_operatortype_append(OUTLINER_OT_highlight_update); WM_operatortype_append(OUTLINER_OT_item_activate); WM_operatortype_append(OUTLINER_OT_select_box); + WM_operatortype_append(OUTLINER_OT_select_walk); WM_operatortype_append(OUTLINER_OT_item_openclose); WM_operatortype_append(OUTLINER_OT_item_rename); WM_operatortype_append(OUTLINER_OT_item_drag_drop); diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c index 7f45c4d22fa..44e67fa1508 100644 --- a/source/blender/editors/space_outliner/outliner_select.c +++ b/source/blender/editors/space_outliner/outliner_select.c @@ -51,14 +51,16 @@ #include "BKE_workspace.h" #include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" #include "ED_armature.h" +#include "ED_gpencil.h" #include "ED_object.h" +#include "ED_outliner.h" #include "ED_screen.h" #include "ED_select_utils.h" #include "ED_sequencer.h" #include "ED_undo.h" -#include "ED_gpencil.h" #include "WM_api.h" #include "WM_types.h" @@ -251,9 +253,7 @@ static eOLDrawState active_viewlayer(bContext *C, } /** - * Select object tree: - * CTRL+LMB: Select/Deselect object and all children. - * CTRL+SHIFT+LMB: Add/Remove object and all children. + * Select object tree */ static void do_outliner_object_select_recursive(ViewLayer *view_layer, Object *ob_parent, @@ -450,9 +450,9 @@ static eOLDrawState tree_element_active_material(bContext *C, return OL_DRAWSEL_NONE; } -static eOLDrawState tree_element_active_camera(bContext *UNUSED(C), +static eOLDrawState tree_element_active_camera(bContext *C, Scene *scene, - ViewLayer *UNUSED(sl), + ViewLayer *UNUSED(view_layer), SpaceOutliner *soops, TreeElement *te, const eOLSetState set) @@ -460,10 +460,21 @@ static eOLDrawState tree_element_active_camera(bContext *UNUSED(C), Object *ob = (Object *)outliner_search_back(soops, te, ID_OB); if (set != OL_SETSEL_NONE) { + scene->camera = ob; + + Main *bmain = CTX_data_main(C); + wmWindowManager *wm = bmain->wm.first; + + WM_windows_scene_data_sync(&wm->windows, scene); + DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE); + DEG_relations_tag_update(bmain); + WM_event_add_notifier(C, NC_SCENE | NA_EDITED, NULL); + return OL_DRAWSEL_NONE; } - - return scene->camera == ob; + else { + return scene->camera == ob; + } } static eOLDrawState tree_element_active_world(bContext *C, @@ -1083,6 +1094,13 @@ eOLDrawState tree_element_type_active(bContext *C, /* ================================================ */ +/* Activate a tree store element and set the walk navigation start element */ +void outliner_element_activate(SpaceOutliner *soops, TreeStoreElem *tselem) +{ + outliner_flag_set(&soops->tree, TSE_ACTIVE | TSE_ACTIVE_WALK, false); + tselem->flag |= TSE_ACTIVE | TSE_ACTIVE_WALK; +} + /** * Action when clicking to activate an item (typically under the mouse cursor), * but don't do any cursor intersection checks. @@ -1114,7 +1132,8 @@ static void do_outliner_item_activate_tree_element(bContext *C, else if (tselem->type == TSE_POSE_BASE) { /* Support pose mode toggle, keeping the active object as is. */ } - else { + else if (soops->flag & SO_SYNC_SELECT) { + /* Only activate when synced selection is enabled */ tree_element_set_active_object(C, scene, view_layer, @@ -1125,6 +1144,9 @@ static void do_outliner_item_activate_tree_element(bContext *C, recursive && tselem->type == 0); } + /* Mark as active in the outliner */ + outliner_element_activate(soops, tselem); + if (tselem->type == 0) { // the lib blocks /* editmode? */ if (te->idcode == ID_SCE) { @@ -1189,7 +1211,7 @@ static void do_outliner_item_activate_tree_element(bContext *C, tree_element_active(C, scene, view_layer, soops, te, OL_SETSEL_NORMAL, false); } } - else { + else if (soops->flag & SO_SYNC_SELECT) { tree_element_type_active(C, scene, view_layer, @@ -1211,7 +1233,8 @@ void outliner_item_select(SpaceOutliner *soops, const bool toggle) { TreeStoreElem *tselem = TREESTORE(te); - const short new_flag = toggle ? (tselem->flag ^ TSE_SELECTED) : (tselem->flag | TSE_SELECTED); + const short new_flag = (toggle && (tselem->flag & TSE_ACTIVE)) ? (tselem->flag ^ TSE_SELECTED) : + (tselem->flag | TSE_SELECTED); if (extend == false) { outliner_flag_set(&soops->tree, TSE_SELECTED, false); @@ -1219,24 +1242,72 @@ void outliner_item_select(SpaceOutliner *soops, tselem->flag = new_flag; } -static void outliner_item_toggle_closed(TreeElement *te, const bool toggle_children) +static bool do_outliner_range_select_recursive(ListBase *lb, + TreeElement *active, + TreeElement *cursor, + bool selecting) { - TreeStoreElem *tselem = TREESTORE(te); - if (toggle_children) { - tselem->flag &= ~TSE_CLOSED; + for (TreeElement *te = lb->first; te; te = te->next) { + TreeStoreElem *tselem = TREESTORE(te); - const bool all_opened = !outliner_flag_is_any_test(&te->subtree, TSE_CLOSED, 1); - outliner_flag_set(&te->subtree, TSE_CLOSED, all_opened); - } - else { - tselem->flag ^= TSE_CLOSED; + if (selecting) { + tselem->flag |= TSE_SELECTED; + } + + /* Set state for selection */ + if (te == active || te == cursor) { + selecting = !selecting; + } + + if (selecting) { + tselem->flag |= TSE_SELECTED; + } + + /* Don't look inside closed elements */ + if (!(tselem->flag & TSE_CLOSED)) { + selecting = do_outliner_range_select_recursive(&te->subtree, active, cursor, selecting); + } } + + return selecting; } -static bool outliner_item_is_co_within_close_toggle(TreeElement *te, float view_co_x) +/* Select a range of items between cursor and active element */ +static void do_outliner_range_select(bContext *C, + SpaceOutliner *soops, + TreeElement *cursor, + const bool extend) { - return ((te->flag & TE_ICONROW) == 0) && (view_co_x > te->xs) && - (view_co_x < te->xs + UI_UNIT_X); + TreeElement *active = outliner_find_element_with_flag(&soops->tree, TSE_ACTIVE); + + /* If no active element exists, activate the element under the cursor */ + if (!active) { + outliner_item_select(soops, cursor, false, false); + outliner_item_do_activate_from_tree_element(C, cursor, TREESTORE(cursor), false, false); + return; + } + + TreeStoreElem *tselem = TREESTORE(active); + const bool active_selected = (tselem->flag & TSE_SELECTED); + + if (!extend) { + outliner_flag_set(&soops->tree, TSE_SELECTED, false); + } + + /* Select active if under cursor */ + if (active == cursor) { + TREESTORE(cursor)->flag |= TSE_SELECTED; + return; + } + + /* If active is not selected, select the element under the cursor */ + if (!active_selected || !outliner_is_element_visible(active)) { + outliner_item_select(soops, cursor, false, false); + outliner_item_do_activate_from_tree_element(C, cursor, TREESTORE(cursor), false, false); + return; + } + + do_outliner_range_select_recursive(&soops->tree, active, cursor, false); } static bool outliner_is_co_within_restrict_columns(const SpaceOutliner *soops, @@ -1247,7 +1318,7 @@ static bool outliner_is_co_within_restrict_columns(const SpaceOutliner *soops, } /** - * A version of #outliner_item_do_acticate_from_cursor that takes the tree element directly. + * A version of #outliner_item_do_activate_from_cursor that takes the tree element directly. * and doesn't depend on the pointer position. * * This allows us to simulate clicking on an item without dealing with the mouse cursor. @@ -1271,10 +1342,11 @@ void outliner_item_do_activate_from_tree_element( static int outliner_item_do_activate_from_cursor(bContext *C, const int mval[2], const bool extend, - const bool recursive, + const bool use_range, const bool deselect_all) { ARegion *ar = CTX_wm_region(C); + Scene *scene = CTX_data_scene(C); SpaceOutliner *soops = CTX_wm_space_outliner(C); TreeElement *te; float view_mval[2]; @@ -1292,28 +1364,36 @@ static int outliner_item_do_activate_from_cursor(bContext *C, changed = true; } } - else if (outliner_item_is_co_within_close_toggle(te, view_mval[0])) { - outliner_item_toggle_closed(te, extend); - changed = true; - rebuild_tree = true; + /* Don't allow toggle on scene collection */ + else if ((TREESTORE(te)->type != TSE_VIEW_COLLECTION_BASE) && + outliner_item_is_co_within_close_toggle(te, view_mval[0])) { + return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; } else { - Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); - /* the row may also contain children, if one is hovered we want this instead of current te */ - TreeElement *activate_te = outliner_find_item_at_x_in_row(soops, te, view_mval[0]); - TreeStoreElem *activate_tselem = TREESTORE(activate_te); - outliner_item_select(soops, activate_te, extend, extend); + /* The row may also contain children, if one is hovered we want this instead of current te */ + bool merged_elements = false; + TreeElement *activate_te = outliner_find_item_at_x_in_row( + soops, te, view_mval[0], &merged_elements); - /* Only change modes when clicking on the icon/text, - * otherwise we can't easily select without changing modes. */ - if ((te->flag & TE_ICONROW) == 0) { - if (view_mval[0] >= te->xs && view_mval[0] <= te->xend) { - do_outliner_item_activate_tree_element( - C, scene, view_layer, soops, activate_te, activate_tselem, extend, recursive); - } + /* If the selected icon was an aggregate of multiple elements, run the search popup */ + if (merged_elements) { + merged_element_search_menu_invoke(C, te, activate_te); + return OPERATOR_CANCELLED; + } + + TreeStoreElem *activate_tselem = TREESTORE(activate_te); + + if (use_range) { + do_outliner_range_select(C, soops, activate_te, extend); + } + else { + outliner_item_select(soops, activate_te, extend, extend); + do_outliner_item_activate_tree_element( + C, scene, view_layer, soops, activate_te, activate_tselem, extend, false); } + changed = true; } @@ -1324,7 +1404,10 @@ static int outliner_item_do_activate_from_cursor(bContext *C, else { ED_region_tag_redraw_no_rebuild(ar); } - ED_undo_push(C, "Outliner selection change"); + + if (soops->flag & SO_SYNC_SELECT) { + ED_outliner_select_sync_from_outliner(C, soops); + } } return OPERATOR_FINISHED; @@ -1334,9 +1417,9 @@ static int outliner_item_do_activate_from_cursor(bContext *C, static int outliner_item_activate_invoke(bContext *C, wmOperator *op, const wmEvent *event) { const bool extend = RNA_boolean_get(op->ptr, "extend"); - const bool recursive = RNA_boolean_get(op->ptr, "recursive"); + const bool use_range = RNA_boolean_get(op->ptr, "extend_range"); const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all"); - return outliner_item_do_activate_from_cursor(C, event->mval, extend, recursive, deselect_all); + return outliner_item_do_activate_from_cursor(C, event->mval, extend, use_range, deselect_all); } void OUTLINER_OT_item_activate(wmOperatorType *ot) @@ -1349,9 +1432,14 @@ void OUTLINER_OT_item_activate(wmOperatorType *ot) ot->poll = ED_operator_outliner_active; + ot->flag |= OPTYPE_REGISTER | OPTYPE_UNDO; + PropertyRNA *prop; RNA_def_boolean(ot->srna, "extend", true, "Extend", "Extend selection for activation"); - RNA_def_boolean(ot->srna, "recursive", false, "Recursive", "Select Objects and their children"); + prop = RNA_def_boolean( + ot->srna, "extend_range", false, "Extend Range", "Select a range from active element"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "deselect_all", false, @@ -1409,9 +1497,44 @@ static int outliner_box_select_exec(bContext *C, wmOperator *op) WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); ED_region_tag_redraw(ar); + if (soops->flag & SO_SYNC_SELECT) { + ED_outliner_select_sync_from_outliner(C, soops); + } + return OPERATOR_FINISHED; } +/* Find if x coordinate is over an icon or name */ +static bool outliner_item_is_co_over_name_icons(TreeElement *te, float view_co_x) +{ + /* Special case: count area left of Scene Collection as empty space */ + bool outside_left = (TREESTORE(te)->type == TSE_VIEW_COLLECTION_BASE) ? + (view_co_x > te->xs + UI_UNIT_X) : + (view_co_x > te->xs); + + return outside_left && (view_co_x < te->xend); +} + +static int outliner_box_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + SpaceOutliner *soops = CTX_wm_space_outliner(C); + ARegion *ar = CTX_wm_region(C); + float view_mval[2]; + const bool tweak = RNA_boolean_get(op->ptr, "tweak"); + + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &view_mval[0], &view_mval[1]); + + /* Find element clicked on */ + TreeElement *te = outliner_find_item_at_y(soops, &soops->tree, view_mval[1]); + + /* Pass through if click is over name or icons, or not tweak event */ + if (te && tweak && outliner_item_is_co_over_name_icons(te, view_mval[0])) { + return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; + } + + return WM_gesture_box_invoke(C, op, event); +} + void OUTLINER_OT_select_box(wmOperatorType *ot) { /* identifiers */ @@ -1420,7 +1543,7 @@ void OUTLINER_OT_select_box(wmOperatorType *ot) ot->description = "Use box selection to select tree elements"; /* api callbacks */ - ot->invoke = WM_gesture_box_invoke; + ot->invoke = outliner_box_select_invoke; ot->exec = outliner_box_select_exec; ot->modal = WM_gesture_box_modal; ot->cancel = WM_gesture_box_cancel; @@ -1431,8 +1554,242 @@ void OUTLINER_OT_select_box(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ + PropertyRNA *prop; + + prop = RNA_def_boolean( + ot->srna, "tweak", false, "Tweak", "Tweak gesture from empty space for box selection"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + WM_operator_properties_gesture_box(ot); WM_operator_properties_select_operation_simple(ot); } /* ****************************************************** */ + +/* **************** Walk Select Tool ****************** */ + +/* Given a tree element return the rightmost child that is visible in the outliner */ +static TreeElement *outliner_find_rightmost_visible_child(SpaceOutliner *soops, TreeElement *te) +{ + while (te->subtree.last) { + if (TSELEM_OPEN(TREESTORE(te), soops)) { + te = te->subtree.last; + } + else { + break; + } + } + return te; +} + +/* Find previous visible element in the tree */ +static TreeElement *outliner_find_previous_element(SpaceOutliner *soops, TreeElement *walk_element) +{ + if (walk_element->prev) { + walk_element = outliner_find_rightmost_visible_child(soops, walk_element->prev); + } + else if (walk_element->parent) { + /* Use parent if at beginning of list */ + walk_element = walk_element->parent; + } + + return walk_element; +} + +/* Recursively search up the tree until a successor to a given element is found */ +static TreeElement *outliner_element_find_successor_in_parents(TreeElement *te) +{ + TreeElement *successor = te; + while (successor->parent) { + if (successor->parent->next) { + te = successor->parent->next; + break; + } + else { + successor = successor->parent; + } + } + + return te; +} + +/* Find next visible element in the tree */ +static TreeElement *outliner_find_next_element(SpaceOutliner *soops, TreeElement *walk_element) +{ + TreeStoreElem *tselem = TREESTORE(walk_element); + + if (TSELEM_OPEN(tselem, soops) && walk_element->subtree.first) { + walk_element = walk_element->subtree.first; + } + else if (walk_element->next) { + walk_element = walk_element->next; + } + else { + walk_element = outliner_element_find_successor_in_parents(walk_element); + } + + return walk_element; +} + +static TreeElement *do_outliner_select_walk(SpaceOutliner *soops, + TreeElement *walk_element, + const int direction, + const bool extend, + const bool toggle_all) +{ + TreeStoreElem *tselem = TREESTORE(walk_element); + + if (!extend) { + outliner_flag_set(&soops->tree, TSE_SELECTED, false); + } + tselem->flag &= ~TSE_ACTIVE_WALK; + + switch (direction) { + case OUTLINER_SELECT_WALK_UP: + walk_element = outliner_find_previous_element(soops, walk_element); + break; + case OUTLINER_SELECT_WALK_DOWN: + walk_element = outliner_find_next_element(soops, walk_element); + break; + case OUTLINER_SELECT_WALK_LEFT: + outliner_item_openclose(walk_element, false, toggle_all); + break; + case OUTLINER_SELECT_WALK_RIGHT: + outliner_item_openclose(walk_element, true, toggle_all); + break; + } + + TreeStoreElem *tselem_new = TREESTORE(walk_element); + + /* If new element is already selected, deselect the previous element */ + if (extend) { + tselem->flag = (tselem_new->flag & TSE_SELECTED) ? (tselem->flag & ~TSE_SELECTED) : + (tselem->flag | TSE_SELECTED); + } + + tselem_new->flag |= TSE_SELECTED | TSE_ACTIVE_WALK; + + return walk_element; +} + +/* Find walk select element, or set it if it does not exist. + * Changed is set to true if walk element is found, false if it was set */ +static TreeElement *find_walk_select_start_element(SpaceOutliner *soops, bool *changed) +{ + TreeElement *walk_element = outliner_find_element_with_flag(&soops->tree, TSE_ACTIVE_WALK); + + *changed = false; + + /* If no walk element exists, start from active */ + if (!walk_element) { + TreeElement *active_element = outliner_find_element_with_flag(&soops->tree, TSE_ACTIVE); + + /* If no active element exists, use the first element in the tree */ + if (!active_element) { + walk_element = soops->tree.first; + } + else { + walk_element = active_element; + } + + *changed = true; + } + + /* If walk element is not visible, set that element's first visible parent as walk element */ + if (!outliner_is_element_visible(walk_element)) { + TREESTORE(walk_element)->flag &= ~TSE_ACTIVE_WALK; + + while (!outliner_is_element_visible(walk_element)) { + walk_element = walk_element->parent; + } + *changed = true; + } + + return walk_element; +} + +/* Scroll the outliner when the walk element reaches the top or bottom boundary */ +static void outliner_walk_scroll(ARegion *ar, TreeElement *te) +{ + /* Account for the header height */ + int y_max = ar->v2d.cur.ymax - UI_UNIT_Y; + int y_min = ar->v2d.cur.ymin; + + /* Scroll if walked position is beyond the border */ + if (te->ys > y_max) { + outliner_scroll_view(ar, te->ys - y_max); + } + else if (te->ys < y_min) { + outliner_scroll_view(ar, -(y_min - te->ys)); + } +} + +static int outliner_walk_select_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + SpaceOutliner *soops = CTX_wm_space_outliner(C); + ARegion *ar = CTX_wm_region(C); + + const short direction = RNA_enum_get(op->ptr, "direction"); + const bool extend = RNA_boolean_get(op->ptr, "extend"); + const bool toggle_all = RNA_boolean_get(op->ptr, "toggle_all"); + + bool changed; + TreeElement *walk_element = find_walk_select_start_element(soops, &changed); + + /* If finding the starting walk select element did not move the element, proceed to walk */ + if (!changed) { + walk_element = do_outliner_select_walk(soops, walk_element, direction, extend, toggle_all); + } + else { + TREESTORE(walk_element)->flag |= TSE_SELECTED | TSE_ACTIVE_WALK; + } + + /* Scroll outliner to focus on walk element */ + outliner_walk_scroll(ar, walk_element); + + if (soops->flag & SO_SYNC_SELECT) { + ED_outliner_select_sync_from_outliner(C, soops); + } + ED_region_tag_redraw(ar); + + return OPERATOR_FINISHED; +} + +void OUTLINER_OT_select_walk(wmOperatorType *ot) +{ + static const EnumPropertyItem direction_items[] = { + {OUTLINER_SELECT_WALK_UP, "UP", 0, "Up", ""}, + {OUTLINER_SELECT_WALK_DOWN, "DOWN", 0, "Down", ""}, + {OUTLINER_SELECT_WALK_LEFT, "LEFT", 0, "Left", ""}, + {OUTLINER_SELECT_WALK_RIGHT, "RIGHT", 0, "Right", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "Walk Select"; + ot->idname = "OUTLINER_OT_select_walk"; + ot->description = "Use walk navigation to select tree elements"; + + /* api callbacks */ + ot->invoke = outliner_walk_select_invoke; + ot->poll = ED_operator_outliner_active; + + ot->flag |= OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + PropertyRNA *prop; + prop = RNA_def_enum(ot->srna, + "direction", + direction_items, + 0, + "Walk Direction", + "Select element in this direction"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend selection on walk"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean( + ot->srna, "toggle_all", false, "Toggle All", "Toggle open/close hierarchy"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); +} + +/* ****************************************************** */ diff --git a/source/blender/editors/space_outliner/outliner_sync.c b/source/blender/editors/space_outliner/outliner_sync.c new file mode 100644 index 00000000000..29c820bce92 --- /dev/null +++ b/source/blender/editors/space_outliner/outliner_sync.c @@ -0,0 +1,575 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2004 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup spoutliner + */ + +#include <stdio.h> + +#include "DNA_armature_types.h" +#include "DNA_layer_types.h" +#include "DNA_outliner_types.h" +#include "DNA_screen_types.h" +#include "DNA_sequence_types.h" +#include "DNA_space_types.h" + +#include "BLI_compiler_compat.h" +#include "BLI_ghash.h" + +#include "BKE_armature.h" +#include "BKE_context.h" +#include "BKE_layer.h" +#include "BKE_main.h" +#include "BKE_sequencer.h" + +#include "DEG_depsgraph.h" + +#include "ED_armature.h" +#include "ED_object.h" +#include "ED_outliner.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "outliner_intern.h" + +/* Functions for tagging outliner selection syncing is dirty from operators */ +void ED_outliner_select_sync_from_object_tag(bContext *C) +{ + wmWindowManager *wm = CTX_wm_manager(C); + wm->outliner_sync_select_dirty |= WM_OUTLINER_SYNC_SELECT_FROM_OBJECT; +} + +void ED_outliner_select_sync_from_edit_bone_tag(bContext *C) +{ + wmWindowManager *wm = CTX_wm_manager(C); + wm->outliner_sync_select_dirty |= WM_OUTLINER_SYNC_SELECT_FROM_EDIT_BONE; +} + +void ED_outliner_select_sync_from_pose_bone_tag(bContext *C) +{ + wmWindowManager *wm = CTX_wm_manager(C); + wm->outliner_sync_select_dirty |= WM_OUTLINER_SYNC_SELECT_FROM_POSE_BONE; +} + +void ED_outliner_select_sync_from_sequence_tag(bContext *C) +{ + wmWindowManager *wm = CTX_wm_manager(C); + wm->outliner_sync_select_dirty |= WM_OUTLINER_SYNC_SELECT_FROM_SEQUENCE; +} + +void ED_outliner_select_sync_from_all_tag(bContext *C) +{ + wmWindowManager *wm = CTX_wm_manager(C); + wm->outliner_sync_select_dirty |= WM_OUTLINER_SYNC_SELECT_FROM_ALL; +} + +bool ED_outliner_select_sync_is_dirty(const bContext *C) +{ + wmWindowManager *wm = CTX_wm_manager(C); + return wm->outliner_sync_select_dirty & WM_OUTLINER_SYNC_SELECT_FROM_ALL; +} + +/* Copy sync select dirty flag from window manager to all outliners to be synced lazily on draw */ +void ED_outliner_select_sync_flag_outliners(const bContext *C) +{ + Main *bmain = CTX_data_main(C); + wmWindowManager *wm = CTX_wm_manager(C); + + for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { + for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { + for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) { + if (sl->spacetype == SPACE_OUTLINER) { + SpaceOutliner *soutliner = (SpaceOutliner *)sl; + + soutliner->sync_select_dirty |= wm->outliner_sync_select_dirty; + } + } + } + } + + /* Clear global sync flag */ + wm->outliner_sync_select_dirty = 0; +} + +/** + * Outliner sync select dirty flags are not enough to determine which types to sync, + * outliner display mode also needs to be considered. This stores the types of data + * to sync to increase code clarity. + */ +typedef struct SyncSelectTypes { + bool object; + bool edit_bone; + bool pose_bone; + bool sequence; +} SyncSelectTypes; + +/** + * Set which types of data to sync when syncing selection from the outliner based on object + * interaction mode and outliner display mode + */ +static void outliner_sync_select_from_outliner_set_types(bContext *C, + SpaceOutliner *soops, + SyncSelectTypes *sync_types) +{ + Object *obact = CTX_data_active_object(C); + Object *obedit = CTX_data_edit_object(C); + + const bool sequence_view = soops->outlinevis == SO_SEQUENCE; + + sync_types->object = !sequence_view; + sync_types->edit_bone = !sequence_view && (obedit && obedit->type == OB_ARMATURE); + sync_types->pose_bone = !sequence_view && (obact && obact->mode == OB_MODE_POSE); + sync_types->sequence = sequence_view; +} + +/** + * Current dirty flags and outliner display mode determine which type of syncing should occur. + * This is to ensure sync flag data is not lost on sync in the wrong display mode. + * Returns true if a sync is needed. + */ +static bool outliner_sync_select_to_outliner_set_types(const bContext *C, + SpaceOutliner *soops, + SyncSelectTypes *sync_types) +{ + Object *obact = CTX_data_active_object(C); + Object *obedit = CTX_data_edit_object(C); + + const bool sequence_view = soops->outlinevis == SO_SEQUENCE; + + sync_types->object = !sequence_view && + (soops->sync_select_dirty & WM_OUTLINER_SYNC_SELECT_FROM_OBJECT); + sync_types->edit_bone = !sequence_view && (obedit && obedit->type == OB_ARMATURE) && + (soops->sync_select_dirty & WM_OUTLINER_SYNC_SELECT_FROM_EDIT_BONE); + sync_types->pose_bone = !sequence_view && (obact && obact->mode == OB_MODE_POSE) && + (soops->sync_select_dirty & WM_OUTLINER_SYNC_SELECT_FROM_POSE_BONE); + sync_types->sequence = sequence_view && + (soops->sync_select_dirty & WM_OUTLINER_SYNC_SELECT_FROM_SEQUENCE); + + return sync_types->object || sync_types->edit_bone || sync_types->pose_bone || + sync_types->sequence; +} + +/** + * Stores items selected from a sync from the outliner. Prevents syncing the selection + * state of the last instance of an object linked in multiple collections. + */ +typedef struct SelectedItems { + GSet *objects; + GSet *edit_bones; + GSet *pose_bones; +} SelectedItems; + +static void selected_items_init(SelectedItems *selected_items) +{ + selected_items->objects = BLI_gset_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__); + selected_items->edit_bones = BLI_gset_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__); + selected_items->pose_bones = BLI_gset_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__); +} + +static void selected_items_free(SelectedItems *selected_items) +{ + BLI_gset_free(selected_items->objects, NULL); + BLI_gset_free(selected_items->edit_bones, NULL); + BLI_gset_free(selected_items->pose_bones, NULL); +} + +/* Check if an instance of this object been selected by the sync */ +static bool is_object_selected(GSet *selected_objects, Base *base) +{ + return BLI_gset_haskey(selected_objects, base); +} + +/* Check if an instance of this edit bone been selected by the sync */ +static bool is_edit_bone_selected(GSet *selected_ebones, EditBone *ebone) +{ + return BLI_gset_haskey(selected_ebones, ebone); +} + +/* Check if an instance of this pose bone been selected by the sync */ +static bool is_pose_bone_selected(GSet *selected_pbones, bPoseChannel *pchan) +{ + return BLI_gset_haskey(selected_pbones, pchan); +} + +/* Add element's data to selected item set */ +static void add_selected_item(GSet *selected, void *data) +{ + BLI_gset_add(selected, data); +} + +static void outliner_select_sync_to_object(ViewLayer *view_layer, + TreeElement *te, + TreeStoreElem *tselem, + GSet *selected_objects) +{ + Object *ob = (Object *)tselem->id; + Base *base = (te->directdata) ? (Base *)te->directdata : + BKE_view_layer_base_find(view_layer, ob); + + if (base && (base->flag & BASE_SELECTABLE)) { + if (tselem->flag & TSE_SELECTED) { + ED_object_base_select(base, BA_SELECT); + + add_selected_item(selected_objects, base); + } + else if (!is_object_selected(selected_objects, base)) { + ED_object_base_select(base, BA_DESELECT); + } + } +} + +static void outliner_select_sync_to_edit_bone(ViewLayer *view_layer, + TreeElement *te, + TreeStoreElem *tselem, + GSet *selected_ebones) +{ + bArmature *arm = (bArmature *)tselem->id; + EditBone *ebone = (EditBone *)te->directdata; + + short bone_flag = ebone->flag; + + if (EBONE_SELECTABLE(arm, ebone)) { + if (tselem->flag & TSE_SELECTED) { + ebone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + + add_selected_item(selected_ebones, ebone); + } + else if (!is_edit_bone_selected(selected_ebones, ebone)) { + ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + } + } + + /* Tag if selection changed */ + if (bone_flag != ebone->flag) { + Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer); + DEG_id_tag_update(&arm->id, ID_RECALC_SELECT); + WM_main_add_notifier(NC_OBJECT | ND_BONE_SELECT, obedit); + } +} + +static void outliner_select_sync_to_pose_bone(TreeElement *te, + TreeStoreElem *tselem, + GSet *selected_pbones) +{ + Object *ob = (Object *)tselem->id; + bArmature *arm = ob->data; + bPoseChannel *pchan = (bPoseChannel *)te->directdata; + + short bone_flag = pchan->bone->flag; + + if (PBONE_SELECTABLE(arm, pchan->bone)) { + if (tselem->flag & TSE_SELECTED) { + pchan->bone->flag |= BONE_SELECTED; + + add_selected_item(selected_pbones, pchan); + } + else if (!is_pose_bone_selected(selected_pbones, pchan)) { + pchan->bone->flag &= ~BONE_SELECTED; + } + } + + /* Tag if selection changed */ + if (bone_flag != pchan->bone->flag) { + DEG_id_tag_update(&arm->id, ID_RECALC_SELECT); + WM_main_add_notifier(NC_OBJECT | ND_BONE_SELECT, ob); + } +} + +static void outliner_select_sync_to_sequence(Scene *scene, TreeStoreElem *tselem) +{ + Sequence *seq = (Sequence *)tselem->id; + + if (tselem->flag & TSE_ACTIVE) { + BKE_sequencer_active_set(scene, seq); + } + + if (tselem->flag & TSE_SELECTED) { + seq->flag |= SELECT; + } + else { + seq->flag &= ~SELECT; + } +} + +/** Sync select and active flags from outliner to active view layer, bones, and sequencer. */ +static void outliner_sync_selection_from_outliner(Scene *scene, + ViewLayer *view_layer, + ListBase *tree, + const SyncSelectTypes *sync_types, + SelectedItems *selected_items) +{ + + for (TreeElement *te = tree->first; te; te = te->next) { + TreeStoreElem *tselem = TREESTORE(te); + + if (tselem->type == 0 && te->idcode == ID_OB) { + if (sync_types->object) { + outliner_select_sync_to_object(view_layer, te, tselem, selected_items->objects); + } + } + else if (tselem->type == TSE_EBONE) { + if (sync_types->edit_bone) { + outliner_select_sync_to_edit_bone(view_layer, te, tselem, selected_items->edit_bones); + } + } + else if (tselem->type == TSE_POSE_CHANNEL) { + if (sync_types->pose_bone) { + outliner_select_sync_to_pose_bone(te, tselem, selected_items->pose_bones); + } + } + else if (tselem->type == TSE_SEQUENCE) { + if (sync_types->sequence) { + outliner_select_sync_to_sequence(scene, tselem); + } + } + + outliner_sync_selection_from_outliner( + scene, view_layer, &te->subtree, sync_types, selected_items); + } +} + +/* Set clean outliner and mark other outliners for syncing */ +void ED_outliner_select_sync_from_outliner(bContext *C, SpaceOutliner *soops) +{ + /* Don't sync in certain outliner display modes */ + if (ELEM(soops->outlinevis, SO_LIBRARIES, SO_DATA_API, SO_ID_ORPHANS)) { + return; + } + + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + + SyncSelectTypes sync_types; + outliner_sync_select_from_outliner_set_types(C, soops, &sync_types); + + /* To store elements that have been selected to prevent linked object sync errors */ + SelectedItems selected_items; + + selected_items_init(&selected_items); + + outliner_sync_selection_from_outliner( + scene, view_layer, &soops->tree, &sync_types, &selected_items); + + selected_items_free(&selected_items); + + /* Tag for updates and clear dirty flag toprevent a sync to the outliner on draw */ + if (sync_types.object) { + soops->sync_select_dirty &= ~WM_OUTLINER_SYNC_SELECT_FROM_OBJECT; + DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); + WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + } + else if (sync_types.edit_bone) { + soops->sync_select_dirty &= ~WM_OUTLINER_SYNC_SELECT_FROM_EDIT_BONE; + } + else if (sync_types.pose_bone) { + soops->sync_select_dirty &= ~WM_OUTLINER_SYNC_SELECT_FROM_POSE_BONE; + } + if (sync_types.sequence) { + soops->sync_select_dirty &= ~WM_OUTLINER_SYNC_SELECT_FROM_SEQUENCE; + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); + } +} + +static void outliner_select_sync_from_object(ViewLayer *view_layer, + SpaceOutliner *soops, + Object *obact, + TreeElement *te, + TreeStoreElem *tselem) +{ + Object *ob = (Object *)tselem->id; + Base *base = (te->directdata) ? (Base *)te->directdata : + BKE_view_layer_base_find(view_layer, ob); + const bool is_selected = (base != NULL) && ((base->flag & BASE_SELECTED) != 0); + + if (base && (ob == obact)) { + outliner_element_activate(soops, tselem); + } + else { + tselem->flag &= ~TSE_ACTIVE; + } + + if (is_selected) { + tselem->flag |= TSE_SELECTED; + } + else { + tselem->flag &= ~TSE_SELECTED; + } +} + +static void outliner_select_sync_from_edit_bone(SpaceOutliner *soops, + EditBone *ebone_active, + TreeElement *te, + TreeStoreElem *tselem) +{ + EditBone *ebone = (EditBone *)te->directdata; + + if (ebone == ebone_active) { + outliner_element_activate(soops, tselem); + } + else { + tselem->flag &= ~TSE_ACTIVE; + } + + if (ebone->flag & BONE_SELECTED) { + tselem->flag |= TSE_SELECTED; + } + else { + tselem->flag &= ~TSE_SELECTED; + } +} + +static void outliner_select_sync_from_pose_bone(SpaceOutliner *soops, + bPoseChannel *pchan_active, + TreeElement *te, + TreeStoreElem *tselem) +{ + bPoseChannel *pchan = (bPoseChannel *)te->directdata; + Bone *bone = pchan->bone; + + if (pchan == pchan_active) { + outliner_element_activate(soops, tselem); + } + else { + tselem->flag &= ~TSE_ACTIVE; + } + + if (bone->flag & BONE_SELECTED) { + tselem->flag |= TSE_SELECTED; + } + else { + tselem->flag &= ~TSE_SELECTED; + } +} + +static void outliner_select_sync_from_sequence(SpaceOutliner *soops, + Sequence *sequence_active, + TreeStoreElem *tselem) +{ + Sequence *seq = (Sequence *)tselem->id; + + if (seq == sequence_active) { + outliner_element_activate(soops, tselem); + } + else { + tselem->flag &= ~TSE_ACTIVE; + } + + if (seq->flag & SELECT) { + tselem->flag |= TSE_SELECTED; + } + else { + tselem->flag &= ~TSE_SELECTED; + } +} + +/** + * Contains active object, bones, and sequence for syncing to prevent getting active data + * repeatedly throughout syncing to the outliner. + */ +typedef struct SyncSelectActiveData { + Object *object; + EditBone *edit_bone; + bPoseChannel *pose_channel; + Sequence *sequence; +} SyncSelectActiveData; + +/** Sync select and active flags from active view layer, bones, and sequences to the outliner. */ +static void outliner_sync_selection_to_outliner(ViewLayer *view_layer, + SpaceOutliner *soops, + ListBase *tree, + SyncSelectActiveData *active_data, + const SyncSelectTypes *sync_types) +{ + for (TreeElement *te = tree->first; te; te = te->next) { + TreeStoreElem *tselem = TREESTORE(te); + + if (tselem->type == 0 && te->idcode == ID_OB) { + if (sync_types->object) { + outliner_select_sync_from_object(view_layer, soops, active_data->object, te, tselem); + } + } + else if (tselem->type == TSE_EBONE) { + if (sync_types->edit_bone) { + outliner_select_sync_from_edit_bone(soops, active_data->edit_bone, te, tselem); + } + } + else if (tselem->type == TSE_POSE_CHANNEL) { + if (sync_types->pose_bone) { + outliner_select_sync_from_pose_bone(soops, active_data->pose_channel, te, tselem); + } + } + else if (tselem->type == TSE_SEQUENCE) { + if (sync_types->sequence) { + outliner_select_sync_from_sequence(soops, active_data->sequence, tselem); + } + } + else { + tselem->flag &= ~TSE_SELECTED; + } + + /* Sync subtree elements */ + outliner_sync_selection_to_outliner(view_layer, soops, &te->subtree, active_data, sync_types); + } +} + +/* Get active data from context */ +static void get_sync_select_active_data(const bContext *C, SyncSelectActiveData *active_data) +{ + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + active_data->object = OBACT(view_layer); + active_data->edit_bone = CTX_data_active_bone(C); + active_data->pose_channel = CTX_data_active_pose_bone(C); + active_data->sequence = BKE_sequencer_active_get(scene); +} + +/* If outliner is dirty sync selection from view layer and sequwncer */ +void outliner_sync_selection(const bContext *C, SpaceOutliner *soops) +{ + /* Set which types of data to sync from sync dirty flag and outliner display mode */ + SyncSelectTypes sync_types; + const bool sync_required = outliner_sync_select_to_outliner_set_types(C, soops, &sync_types); + + if (sync_required) { + ViewLayer *view_layer = CTX_data_view_layer(C); + + /* Store active object, bones, and sequence */ + SyncSelectActiveData active_data; + get_sync_select_active_data(C, &active_data); + + outliner_sync_selection_to_outliner( + view_layer, soops, &soops->tree, &active_data, &sync_types); + + /* Keep any unsynced data in the dirty flag */ + if (sync_types.object) { + soops->sync_select_dirty &= ~WM_OUTLINER_SYNC_SELECT_FROM_OBJECT; + } + if (sync_types.edit_bone) { + soops->sync_select_dirty &= ~WM_OUTLINER_SYNC_SELECT_FROM_EDIT_BONE; + } + if (sync_types.pose_bone) { + soops->sync_select_dirty &= ~WM_OUTLINER_SYNC_SELECT_FROM_POSE_BONE; + } + if (sync_types.sequence) { + soops->sync_select_dirty &= ~WM_OUTLINER_SYNC_SELECT_FROM_SEQUENCE; + } + } +} diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index f9905cc4fcd..a2d988f1142 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -63,6 +63,7 @@ #include "ED_armature.h" #include "ED_object.h" +#include "ED_outliner.h" #include "ED_scene.h" #include "ED_screen.h" #include "ED_sequencer.h" @@ -298,7 +299,7 @@ static void unlink_collection_cb(bContext *C, } else if (GS(tsep->id->name) == ID_SCE) { Scene *scene = (Scene *)tsep->id; - Collection *parent = BKE_collection_master(scene); + Collection *parent = scene->master_collection; id_fake_user_set(&collection->id); BKE_collection_child_remove(bmain, parent, collection); DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE); @@ -337,7 +338,7 @@ static void unlink_object_cb(bContext *C, } else if (GS(tsep->id->name) == ID_SCE) { Scene *scene = (Scene *)tsep->id; - Collection *parent = BKE_collection_master(scene); + Collection *parent = scene->master_collection; BKE_collection_object_remove(bmain, parent, ob, true); DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE); DEG_relations_tag_update(bmain); @@ -478,6 +479,129 @@ void OUTLINER_OT_scene_operation(wmOperatorType *ot) } /* ******************************************** */ +/** + * Stores the parent and a child element of a merged icon-row icon for + * the merged select popup menu. The sub-tree of the parent is searched and + * the child is needed to only show elements of the same type in the popup. + */ +typedef struct MergedSearchData { + TreeElement *parent_element; + TreeElement *select_element; +} MergedSearchData; + +static void merged_element_search_cb_recursive( + const ListBase *tree, short tselem_type, short type, const char *str, uiSearchItems *items) +{ + char name[64]; + int iconid; + + for (TreeElement *te = tree->first; te; te = te->next) { + TreeStoreElem *tselem = TREESTORE(te); + + if (tree_element_id_type_to_index(te) == type && tselem_type == tselem->type) { + if (BLI_strcasestr(te->name, str)) { + BLI_strncpy(name, te->name, 64); + + iconid = tree_element_get_icon(tselem, te).icon; + + /* Don't allow duplicate named items */ + if (UI_search_items_find_index(items, name) == -1) { + if (!UI_search_item_add(items, name, te, iconid)) { + break; + } + } + } + } + + merged_element_search_cb_recursive(&te->subtree, tselem_type, type, str, items); + } +} + +/* Get a list of elements that match the search string */ +static void merged_element_search_cb(const bContext *UNUSED(C), + void *data, + const char *str, + uiSearchItems *items) +{ + MergedSearchData *search_data = (MergedSearchData *)data; + TreeElement *parent = search_data->parent_element; + TreeElement *te = search_data->select_element; + + int type = tree_element_id_type_to_index(te); + + merged_element_search_cb_recursive(&parent->subtree, TREESTORE(te)->type, type, str, items); +} + +/* Activate an element from the merged element search menu */ +static void merged_element_search_call_cb(struct bContext *C, void *UNUSED(arg1), void *element) +{ + SpaceOutliner *soops = CTX_wm_space_outliner(C); + TreeElement *te = (TreeElement *)element; + + outliner_item_select(soops, te, false, false); + outliner_item_do_activate_from_tree_element(C, te, te->store_elem, false, false); + + if (soops->flag & SO_SYNC_SELECT) { + ED_outliner_select_sync_from_outliner(C, soops); + } +} + +/** Merged element search menu + * Created on activation of a merged or aggregated icon-row icon. + */ +static uiBlock *merged_element_search_menu(bContext *C, ARegion *ar, void *data) +{ + static char search[64] = ""; + uiBlock *block; + uiBut *but; + + /* Clear search on each menu creation */ + *search = '\0'; + + block = UI_block_begin(C, ar, __func__, UI_EMBOSS); + UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_SEARCH_MENU); + UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP); + + short menu_width = 10 * UI_UNIT_X; + but = uiDefSearchBut( + block, search, 0, ICON_VIEWZOOM, sizeof(search), 10, 10, menu_width, UI_UNIT_Y, 0, 0, ""); + UI_but_func_search_set( + but, NULL, merged_element_search_cb, data, false, merged_element_search_call_cb, NULL); + UI_but_flag_enable(but, UI_BUT_ACTIVATE_ON_INIT); + + /* Fake button to hold space for search items */ + uiDefBut(block, + UI_BTYPE_LABEL, + 0, + "", + 10, + 10 - UI_searchbox_size_y(), + menu_width, + UI_searchbox_size_y(), + NULL, + 0, + 0, + 0, + 0, + NULL); + + /* Center the menu on the cursor */ + UI_block_bounds_set_popup(block, 6, (const int[2]){-(menu_width / 2), 0}); + + return block; +} + +void merged_element_search_menu_invoke(bContext *C, + TreeElement *parent_te, + TreeElement *activate_te) +{ + MergedSearchData *select_data = MEM_callocN(sizeof(MergedSearchData), "merge_search_data"); + select_data->parent_element = parent_te; + select_data->select_element = activate_te; + + UI_popup_block_invoke(C, merged_element_search_menu, select_data, MEM_freeN); +} + static void object_select_cb(bContext *C, ReportList *UNUSED(reports), Scene *UNUSED(scene), @@ -556,12 +680,7 @@ static void object_delete_cb(bContext *C, if (ob == CTX_data_edit_object(C)) { ED_object_editmode_exit(C, EM_FREEDATA); } - ED_object_base_free_and_unlink(CTX_data_main(C), scene, ob); - /* leave for ED_outliner_id_unref to handle */ -#if 0 - te->directdata = NULL; - tselem->id = NULL; -#endif + BKE_id_delete(bmain, ob); } } @@ -654,7 +773,7 @@ static void singleuser_action_cb(bContext *C, if (id) { IdAdtTemplate *iat = (IdAdtTemplate *)tsep->id; - PointerRNA ptr = {{NULL}}; + PointerRNA ptr = {NULL}; PropertyRNA *prop; RNA_pointer_create(&iat->id, &RNA_AnimData, iat->adt, &ptr); @@ -677,7 +796,7 @@ static void singleuser_world_cb(bContext *C, /* need to use parent scene not just scene, otherwise may end up getting wrong one */ if (id) { Scene *parscene = (Scene *)tsep->id; - PointerRNA ptr = {{NULL}}; + PointerRNA ptr = {NULL}; PropertyRNA *prop; RNA_id_pointer_create(&parscene->id, &ptr); @@ -1080,11 +1199,6 @@ static void object_delete_hierarchy_cb(bContext *C, } outline_delete_hierarchy(C, reports, scene, base); - /* leave for ED_outliner_id_unref to handle */ -#if 0 - te->directdata = NULL; - tselem->id = NULL; -#endif } DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); @@ -1168,11 +1282,6 @@ static void object_batch_delete_hierarchy_cb(bContext *C, } outline_batch_delete_hierarchy(reports, CTX_data_main(C), view_layer, scene, base); - /* leave for ED_outliner_id_unref to handle */ -#if 0 - te->directdata = NULL; - tselem->id = NULL; -#endif } } diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index cc062467dbe..fd6a052b84d 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -297,7 +297,7 @@ static void outliner_add_scene_contents(SpaceOutliner *soops, ViewLayer *view_layer; for (view_layer = sce->view_layers.first; view_layer; view_layer = view_layer->next) { - TreeElement *tenlay = outliner_add_element(soops, &ten->subtree, sce, te, TSE_R_LAYER, 0); + TreeElement *tenlay = outliner_add_element(soops, &ten->subtree, sce, ten, TSE_R_LAYER, 0); tenlay->name = view_layer->name; tenlay->directdata = view_layer; } @@ -314,7 +314,7 @@ static void outliner_add_scene_contents(SpaceOutliner *soops, ten = outliner_add_element(soops, lb, sce, te, TSE_SCENE_OBJECTS_BASE, 0); ten->name = IFACE_("Objects"); FOREACH_SCENE_OBJECT_BEGIN (sce, ob) { - outliner_add_element(soops, &ten->subtree, ob, NULL, 0, 0); + outliner_add_element(soops, &ten->subtree, ob, ten, 0, 0); } FOREACH_SCENE_OBJECT_END; outliner_make_object_parent_hierarchy(&ten->subtree); @@ -766,7 +766,7 @@ static TreeElement *outliner_add_element( ID *id = idv; if (ELEM(type, TSE_RNA_STRUCT, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM)) { - id = ((PointerRNA *)idv)->id.data; + id = ((PointerRNA *)idv)->owner_id; if (!id) { id = ((PointerRNA *)idv)->data; } @@ -1601,7 +1601,7 @@ typedef struct tTreeSort { short idcode; } tTreeSort; -/* alphabetical comparator, tryping to put objects first */ +/* alphabetical comparator, trying to put objects first */ static int treesort_alpha_ob(const void *v1, const void *v2) { const tTreeSort *x1 = v1, *x2 = v2; @@ -1627,7 +1627,7 @@ static int treesort_alpha_ob(const void *v1, const void *v2) return (x1->te->flag & TE_CHILD_NOT_IN_COLLECTION) ? 1 : -1; } - comp = strcmp(x1->name, x2->name); + comp = BLI_strcasecmp_natural(x1->name, x2->name); if (comp > 0) { return 1; @@ -1659,7 +1659,7 @@ static int treesort_alpha(const void *v1, const void *v2) const tTreeSort *x1 = v1, *x2 = v2; int comp; - comp = strcmp(x1->name, x2->name); + comp = BLI_strcasecmp_natural(x1->name, x2->name); if (comp > 0) { return 1; @@ -1697,7 +1697,7 @@ static int treesort_obtype_alpha(const void *v1, const void *v2) } } else { - int comp = strcmp(x1->name, x2->name); + int comp = BLI_strcasecmp_natural(x1->name, x2->name); if (comp > 0) { return 1; @@ -2008,6 +2008,9 @@ static int outliner_exclude_filter_get(SpaceOutliner *soops) case SO_FILTER_OB_VISIBLE: exclude_filter |= SO_FILTER_OB_STATE_VISIBLE; break; + case SO_FILTER_OB_HIDDEN: + exclude_filter |= SO_FILTER_OB_STATE_HIDDEN; + break; case SO_FILTER_OB_SELECTED: exclude_filter |= SO_FILTER_OB_STATE_SELECTED; break; @@ -2086,6 +2089,11 @@ static bool outliner_element_visible_get(ViewLayer *view_layer, return false; } } + else if (exclude_filter & SO_FILTER_OB_STATE_HIDDEN) { + if ((base->flag & BASE_VISIBLE) != 0) { + return false; + } + } else if (exclude_filter & SO_FILTER_OB_STATE_SELECTED) { if ((base->flag & BASE_SELECTED) == 0) { return false; @@ -2339,7 +2347,8 @@ void outliner_build_tree( te = outliner_add_element(soops, &soops->tree, sce, NULL, 0, 0); tselem = TREESTORE(te); - if (sce == scene && show_opened) { + /* New scene elements open by default */ + if ((sce == scene && show_opened) || !tselem->used) { tselem->flag &= ~TSE_CLOSED; } diff --git a/source/blender/editors/space_outliner/outliner_utils.c b/source/blender/editors/space_outliner/outliner_utils.c index f57dce97b38..5dfdf6f129b 100644 --- a/source/blender/editors/space_outliner/outliner_utils.c +++ b/source/blender/editors/space_outliner/outliner_utils.c @@ -24,11 +24,15 @@ #include "BLI_utildefines.h" #include "DNA_action_types.h" +#include "DNA_screen_types.h" #include "DNA_space_types.h" +#include "BKE_context.h" #include "BKE_outliner_treehash.h" +#include "BKE_layer.h" #include "ED_armature.h" +#include "ED_outliner.h" #include "UI_interface.h" #include "UI_view2d.h" @@ -62,6 +66,38 @@ TreeElement *outliner_find_item_at_y(const SpaceOutliner *soops, return NULL; } +static TreeElement *outliner_find_item_at_x_in_row_recursive(const TreeElement *parent_te, + float view_co_x, + bool *r_merged) +{ + TreeElement *child_te = parent_te->subtree.first; + + bool over_element = false; + + while (child_te) { + over_element = (view_co_x > child_te->xs) && (view_co_x < child_te->xend); + if ((child_te->flag & TE_ICONROW) && over_element) { + return child_te; + } + else if ((child_te->flag & TE_ICONROW_MERGED) && over_element) { + if (r_merged) { + *r_merged = true; + } + return child_te; + } + + TreeElement *te = outliner_find_item_at_x_in_row_recursive(child_te, view_co_x, r_merged); + if (te != child_te) { + return te; + } + + child_te = child_te->next; + } + + /* return parent if no child is hovered */ + return (TreeElement *)parent_te; +} + /** * Collapsed items can show their children as click-able icons. This function tries to find * such an icon that represents the child item at x-coordinate \a view_co_x (view-space). @@ -70,24 +106,14 @@ TreeElement *outliner_find_item_at_y(const SpaceOutliner *soops, */ TreeElement *outliner_find_item_at_x_in_row(const SpaceOutliner *soops, const TreeElement *parent_te, - float view_co_x) + float view_co_x, + bool *r_merged) { - /* if parent_te is opened, it doesn't show childs in row */ + /* if parent_te is opened, it doesn't show children in row */ if (!TSELEM_OPEN(TREESTORE(parent_te), soops)) { - /* no recursion, items can only display their direct children in the row */ - for (TreeElement *child_te = parent_te->subtree.first; - /* don't look further if co_x is smaller than child position*/ - child_te && view_co_x >= child_te->xs; - - child_te = child_te->next) { - if ((child_te->flag & TE_ICONROW) && (view_co_x > child_te->xs) && - (view_co_x < child_te->xend)) { - return child_te; - } - } + return outliner_find_item_at_x_in_row_recursive(parent_te, view_co_x, r_merged); } - /* return parent if no child is hovered */ return (TreeElement *)parent_te; } @@ -300,3 +326,89 @@ float outliner_restrict_columns_width(const SpaceOutliner *soops) } return (num_columns * UI_UNIT_X + V2D_SCROLL_WIDTH); } + +/* Find first tree element in tree with matching treestore flag */ +TreeElement *outliner_find_element_with_flag(const ListBase *lb, short flag) +{ + for (TreeElement *te = lb->first; te; te = te->next) { + if ((TREESTORE(te)->flag & flag) == flag) { + return te; + } + TreeElement *active_element = outliner_find_element_with_flag(&te->subtree, flag); + if (active_element) { + return active_element; + } + } + return NULL; +} + +/* Find if element is visible in the outliner tree */ +bool outliner_is_element_visible(const TreeElement *te) +{ + TreeStoreElem *tselem; + + while (te->parent) { + tselem = TREESTORE(te->parent); + + if (tselem->flag & TSE_CLOSED) { + return false; + } + else { + te = te->parent; + } + } + + return true; +} + +/* Find if x coordinate is over element disclosure toggle */ +bool outliner_item_is_co_within_close_toggle(TreeElement *te, float view_co_x) +{ + return (view_co_x > te->xs) && (view_co_x < te->xs + UI_UNIT_X); +} + +/* Scroll view vertically while keeping within total bounds */ +void outliner_scroll_view(ARegion *ar, int delta_y) +{ + int y_min = MIN2(ar->v2d.cur.ymin, ar->v2d.tot.ymin); + + ar->v2d.cur.ymax += delta_y; + ar->v2d.cur.ymin += delta_y; + + /* Adjust view if delta placed view outside total area */ + int offset; + if (ar->v2d.cur.ymax > -UI_UNIT_Y) { + offset = ar->v2d.cur.ymax; + ar->v2d.cur.ymax -= offset; + ar->v2d.cur.ymin -= offset; + } + else if (ar->v2d.cur.ymin < y_min) { + offset = y_min - ar->v2d.cur.ymin; + ar->v2d.cur.ymax += offset; + ar->v2d.cur.ymin += offset; + } +} + +/* Get base of object under cursor. Used for eyedropper tool */ +Base *ED_outliner_give_base_under_cursor(bContext *C, const int mval[2]) +{ + ARegion *ar = CTX_wm_region(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + SpaceOutliner *soops = CTX_wm_space_outliner(C); + TreeElement *te; + Base *base = NULL; + float view_mval[2]; + + UI_view2d_region_to_view(&ar->v2d, mval[0], mval[1], &view_mval[0], &view_mval[1]); + + te = outliner_find_item_at_y(soops, &soops->tree, view_mval[1]); + if (te) { + TreeStoreElem *tselem = TREESTORE(te); + if (tselem->type == 0) { + Object *ob = (Object *)tselem->id; + base = (te->directdata) ? (Base *)te->directdata : BKE_view_layer_base_find(view_layer, ob); + } + } + + return base; +} diff --git a/source/blender/editors/space_outliner/space_outliner.c b/source/blender/editors/space_outliner/space_outliner.c index 091efc56c09..79880c68120 100644 --- a/source/blender/editors/space_outliner/space_outliner.c +++ b/source/blender/editors/space_outliner/space_outliner.c @@ -131,6 +131,9 @@ static void outliner_main_region_listener(wmWindow *UNUSED(win), ED_region_tag_redraw(ar); break; } + if (wmn->action & NA_EDITED) { + ED_region_tag_redraw(ar); + } break; case NC_OBJECT: switch (wmn->data) { @@ -145,13 +148,8 @@ static void outliner_main_region_listener(wmWindow *UNUSED(win), ED_region_tag_redraw(ar); break; case ND_CONSTRAINT: - switch (wmn->action) { - case NA_ADDED: - case NA_REMOVED: - case NA_RENAME: - ED_region_tag_redraw(ar); - break; - } + /* all constraint actions now, for reordering */ + ED_region_tag_redraw(ar); break; case ND_MODIFIER: /* all modifier actions now */ @@ -304,6 +302,8 @@ static SpaceLink *outliner_new(const ScrArea *UNUSED(area), const Scene *UNUSED( soutliner->filter_id_type = ID_GR; soutliner->show_restrict_flags = SO_RESTRICT_ENABLE | SO_RESTRICT_HIDE; soutliner->outlinevis = SO_VIEW_LAYER; + soutliner->sync_select_dirty |= WM_OUTLINER_SYNC_SELECT_FROM_ALL; + soutliner->flag |= SO_SYNC_SELECT; /* header */ ar = MEM_callocN(sizeof(ARegion), "header for outliner"); @@ -349,6 +349,9 @@ static SpaceLink *outliner_duplicate(SpaceLink *sl) soutlinern->treestore = NULL; soutlinern->treehash = NULL; + soutlinern->flag |= (soutliner->flag & SO_SYNC_SELECT); + soutlinern->sync_select_dirty = WM_OUTLINER_SYNC_SELECT_FROM_ALL; + return (SpaceLink *)soutlinern; } @@ -415,7 +418,7 @@ void ED_spacetype_outliner(void) /* regions: main window */ art = MEM_callocN(sizeof(ARegionType), "spacetype outliner region"); art->regionid = RGN_TYPE_WINDOW; - art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES; + art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D; art->init = outliner_main_region_init; art->draw = outliner_main_region_draw; @@ -428,7 +431,7 @@ void ED_spacetype_outliner(void) art = MEM_callocN(sizeof(ARegionType), "spacetype outliner header region"); art->regionid = RGN_TYPE_HEADER; art->prefsizey = HEADERY; - art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES | ED_KEYMAP_HEADER; + art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_HEADER; art->init = outliner_header_region_init; art->draw = outliner_header_region_draw; diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 07350b5269e..b15acb12d00 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -1045,9 +1045,8 @@ ImBuf *sequencer_ibuf_get(struct Main *bmain, bmain, depsgraph, scene, rectx, recty, proxy_size, false, &context); context.view_id = BKE_scene_multiview_view_id_get(&scene->r, viewname); - /* sequencer could start rendering, in this case we need to be sure it wouldn't be canceled - * by Esc pressed somewhere in the past - */ + /* Sequencer could start rendering, in this case we need to be sure it wouldn't be canceled + * by Escape pressed somewhere in the past. */ G.is_break = false; /* Rendering can change OGL context. Save & Restore framebuffer. */ diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index ed0303564c6..22b73c32bfe 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -3405,6 +3405,7 @@ void SEQUENCER_OT_copy(wmOperatorType *ot) /* identifiers */ ot->name = "Copy"; ot->idname = "SEQUENCER_OT_copy"; + ot->description = "Copy selected strips to clipboard"; /* api callbacks */ ot->exec = sequencer_copy_exec; @@ -3470,6 +3471,7 @@ void SEQUENCER_OT_paste(wmOperatorType *ot) /* identifiers */ ot->name = "Paste"; ot->idname = "SEQUENCER_OT_paste"; + ot->description = "Paste strips from clipboard"; /* api callbacks */ ot->exec = sequencer_paste_exec; diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h index dd04260d80e..851d3b5f3aa 100644 --- a/source/blender/editors/space_sequencer/sequencer_intern.h +++ b/source/blender/editors/space_sequencer/sequencer_intern.h @@ -34,7 +34,6 @@ struct ARegionType; struct Depsgraph; struct Main; struct Scene; -struct ScrArea; struct Sequence; struct SpaceSeq; struct StripElem; diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index 57f86059d9d..affb6d3fd88 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -41,6 +41,7 @@ /* for menu/popup icons etc etc*/ +#include "ED_outliner.h" #include "ED_screen.h" #include "ED_sequencer.h" #include "ED_select_utils.h" @@ -49,6 +50,7 @@ /* own include */ #include "sequencer_intern.h" + static void *find_nearest_marker(int UNUSED(d1), int UNUSED(d2)) { return NULL; @@ -254,6 +256,8 @@ static int sequencer_de_select_all_exec(bContext *C, wmOperator *op) } } + ED_outliner_select_sync_from_sequence_tag(C); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); return OPERATOR_FINISHED; @@ -293,6 +297,8 @@ static int sequencer_select_inverse_exec(bContext *C, wmOperator *UNUSED(op)) } } + ED_outliner_select_sync_from_sequence_tag(C); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); return OPERATOR_FINISHED; @@ -542,6 +548,8 @@ static int sequencer_select_invoke(bContext *C, wmOperator *op, const wmEvent *e } } + ED_outliner_select_sync_from_sequence_tag(C); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); /* allowing tweaks */ @@ -668,6 +676,8 @@ static int sequencer_select_more_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_CANCELLED; } + ED_outliner_select_sync_from_sequence_tag(C); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); return OPERATOR_FINISHED; @@ -699,6 +709,8 @@ static int sequencer_select_less_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_CANCELLED; } + ED_outliner_select_sync_from_sequence_tag(C); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); return OPERATOR_FINISHED; @@ -750,6 +762,8 @@ static int sequencer_select_linked_pick_invoke(bContext *C, wmOperator *op, cons selected = select_more_less_seq__internal(scene, 1, 1); } + ED_outliner_select_sync_from_sequence_tag(C); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); return OPERATOR_FINISHED; @@ -784,6 +798,8 @@ static int sequencer_select_linked_exec(bContext *C, wmOperator *UNUSED(op)) selected = select_more_less_seq__internal(scene, true, true); } + ED_outliner_select_sync_from_sequence_tag(C); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); return OPERATOR_FINISHED; @@ -832,6 +848,8 @@ static int sequencer_select_handles_exec(bContext *C, wmOperator *op) } } + ED_outliner_select_sync_from_sequence_tag(C); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); return OPERATOR_FINISHED; @@ -876,6 +894,8 @@ static int sequencer_select_active_side_exec(bContext *C, wmOperator *op) select_active_side( ed->seqbasep, RNA_enum_get(op->ptr, "side"), seq_act->machine, seq_act->startdisp); + ED_outliner_select_sync_from_sequence_tag(C); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); return OPERATOR_FINISHED; @@ -934,6 +954,8 @@ static int sequencer_box_select_exec(bContext *C, wmOperator *op) } } + ED_outliner_select_sync_from_sequence_tag(C); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); return OPERATOR_FINISHED; @@ -1311,6 +1333,7 @@ static int sequencer_select_grouped_exec(bContext *C, wmOperator *op) } if (changed) { + ED_outliner_select_sync_from_sequence_tag(C); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index 9aa9d14cbc8..e1cf6d00b90 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -597,7 +597,7 @@ static void sequencer_main_region_message_subscribe(const struct bContext *UNUSE &RNA_SequenceModifier, &RNA_SequenceColorBalanceData, }; - wmMsgParams_RNA msg_key_params = {{{0}}}; + wmMsgParams_RNA msg_key_params = {{0}}; for (int i = 0; i < ARRAY_SIZE(type_array); i++) { msg_key_params.ptr.type = type_array[i]; WM_msg_subscribe_rna_params( diff --git a/source/blender/editors/space_text/space_text.c b/source/blender/editors/space_text/space_text.c index c1a3c79b0d8..9f39313b9ab 100644 --- a/source/blender/editors/space_text/space_text.c +++ b/source/blender/editors/space_text/space_text.c @@ -29,6 +29,7 @@ #include "BLI_blenlib.h" +#include "BKE_global.h" #include "BKE_context.h" #include "BKE_library.h" #include "BKE_screen.h" @@ -356,7 +357,7 @@ static void text_drop_paste(wmDrag *drag, wmDropBox *drop) ID *id = WM_drag_ID(drag, 0); /* copy drag path to properties */ - text = RNA_path_full_ID_py(id); + text = RNA_path_full_ID_py(G_MAIN, id); RNA_string_set(drop->ptr, "text", text); MEM_freeN(text); } diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c index e1550deb659..caefb5070fb 100644 --- a/source/blender/editors/space_text/text_ops.c +++ b/source/blender/editors/space_text/text_ops.c @@ -142,7 +142,7 @@ static char *buf_tabs_to_spaces(const char *in_buf, const int tab_size) BLI_INLINE int text_pixel_x_to_column(SpaceText *st, const int x) { - /* add half the char width so mouse cursor selection is inbetween letters */ + /* Add half the char width so mouse cursor selection is in between letters. */ return (x + (st->cwidth / 2)) / st->cwidth; } diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c index 38e8d285c77..b412a72cce1 100644 --- a/source/blender/editors/space_view3d/drawobject.c +++ b/source/blender/editors/space_view3d/drawobject.c @@ -145,7 +145,7 @@ void ED_draw_object_facemap(Depsgraph *depsgraph, facemap_data = CustomData_get_layer(&me->pdata, CD_FACEMAP); - /* use gawain immediate mode fore now */ + /* Make a batch and free it each time for now. */ const int looptris_len = poly_to_tri_count(mpoly_len, mloop_len); const int vbo_len_capacity = looptris_len * 3; int vbo_len_used = 0; diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 7acff998e2b..8c4f2f62602 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -992,7 +992,7 @@ static void view3d_main_region_message_subscribe(const struct bContext *C, * accepting some redundant redraws. * * For other space types we might try avoid this, keep the 3D view as an exceptional case! */ - wmMsgParams_RNA msg_key_params = {{{0}}}; + wmMsgParams_RNA msg_key_params = {{0}}; /* Only subscribe to types. */ StructRNA *type_array[] = { @@ -1177,7 +1177,7 @@ static void view3d_header_region_message_subscribe(const struct bContext *UNUSED struct ARegion *ar, struct wmMsgBus *mbus) { - wmMsgParams_RNA msg_key_params = {{{0}}}; + wmMsgParams_RNA msg_key_params = {{0}}; /* Only subscribe to types. */ StructRNA *type_array[] = { diff --git a/source/blender/editors/space_view3d/view3d_camera_control.c b/source/blender/editors/space_view3d/view3d_camera_control.c index 0045094542f..260546738f4 100644 --- a/source/blender/editors/space_view3d/view3d_camera_control.c +++ b/source/blender/editors/space_view3d/view3d_camera_control.c @@ -87,8 +87,7 @@ typedef struct View3DCameraControl { float ofs_backup[3]; /* backup the views offset in case the user cancels flying in non camera mode */ - /* backup the views quat in case the user cancels flying in non camera mode. - * (quat for view, eul for camera) */ + /* backup the views quat in case the user cancels flying in non camera mode. */ float rot_backup[4]; /* remember if were ortho or not, only used for restoring the view if it was a ortho view */ char persp_backup; @@ -209,8 +208,8 @@ void ED_view3d_cameracontrol_update(View3DCameraControl *vctrl, const bool do_rotate, const bool do_translate) { - /* we are in camera view so apply the view ofs and quat to the view matrix and set the camera - * to the view */ + /* We are in camera view so apply the view offset and rotation to the view matrix + * and set the camera to the view. */ Scene *scene = vctrl->ctx_scene; View3D *v3d = vctrl->ctx_v3d; diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 6c534ee1b98..f6afa112f08 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -859,6 +859,51 @@ float ED_view3d_grid_scale(Scene *scene, View3D *v3d, const char **grid_unit) return v3d->grid * ED_scene_grid_scale(scene, grid_unit); } +#define STEPS_LEN 8 +void ED_view3d_grid_steps(Scene *scene, + View3D *v3d, + RegionView3D *rv3d, + float r_grid_steps[STEPS_LEN]) +{ + const void *usys; + int i, len; + bUnit_GetSystem(scene->unit.system, B_UNIT_LENGTH, &usys, &len); + float grid_scale = v3d->grid; + + if (usys) { + if (rv3d->view == RV3D_VIEW_USER) { + /* Skip steps */ + len = bUnit_GetBaseUnit(usys) + 1; + } + + grid_scale /= scene->unit.scale_length; + + for (i = 0; i < len; i++) { + r_grid_steps[i] = (float)bUnit_GetScaler(usys, len - 1 - i) * grid_scale; + } + for (; i < STEPS_LEN; i++) { + /* Fill last slots */ + r_grid_steps[i] = 10.0f * r_grid_steps[i - 1]; + } + } + else { + if (rv3d->view != RV3D_VIEW_USER) { + /* Allow 3 more subdivisions. */ + grid_scale /= powf(v3d->gridsubdiv, 3); + } + int subdiv = 1; + for (i = 0;; i++) { + r_grid_steps[i] = grid_scale * subdiv; + + if (i == STEPS_LEN - 1) { + break; + } + subdiv *= v3d->gridsubdiv; + } + } +} +#undef STEPS_LEN + /* Simulates the grid scale that is actually viewed. * The actual code is seen in `object_grid_frag.glsl` (see `grid_res`). * Currently the simulation is only done when RV3D_VIEW_IS_AXIS. */ @@ -867,24 +912,35 @@ float ED_view3d_grid_view_scale(Scene *scene, RegionView3D *rv3d, const char **grid_unit) { - float grid_scale = ED_view3d_grid_scale(scene, v3d, grid_unit); + float grid_scale; if (!rv3d->is_persp && RV3D_VIEW_IS_AXIS(rv3d->view)) { /* Decrease the distance between grid snap points depending on zoom. */ - float grid_subdiv = v3d->gridsubdiv; - if (grid_subdiv > 1) { - /* Allow 3 more subdivisions (see OBJECT_engine_init). */ - grid_scale /= powf(grid_subdiv, 3); - - /* `3.0` was a value obtained by trial and error in order to get - * a nice snap distance.*/ - float grid_res = 3.0 * (rv3d->dist / v3d->lens); - float lvl = (logf(grid_res / grid_scale) / logf(grid_subdiv)); + /* `0.38` was a value visually obtained in order to get a snap distance + * that matches previous versions Blender.*/ + float min_dist = 0.38f * (rv3d->dist / v3d->lens); + float grid_steps[8]; + ED_view3d_grid_steps(scene, v3d, rv3d, grid_steps); + int i; + for (i = 0; i < ARRAY_SIZE(grid_steps); i++) { + grid_scale = grid_steps[i]; + if (grid_scale > min_dist) { + break; + } + } - CLAMP_MIN(lvl, 0.0f); + if (grid_unit) { + const void *usys; + int len; + bUnit_GetSystem(scene->unit.system, B_UNIT_LENGTH, &usys, &len); - grid_scale *= pow(grid_subdiv, (int)lvl); + if (usys) { + *grid_unit = bUnit_GetNameDisplay(usys, len - i - 1); + } } } + else { + grid_scale = ED_view3d_grid_scale(scene, v3d, grid_unit); + } return grid_scale; } @@ -1360,6 +1416,27 @@ static void draw_selected_name( BLF_disable(font_id, BLF_SHADOW); } +static void draw_grid_unit_name( + Scene *scene, RegionView3D *rv3d, View3D *v3d, int xoffset, int *yoffset) +{ + if (!rv3d->is_persp && RV3D_VIEW_IS_AXIS(rv3d->view)) { + const char *grid_unit = NULL; + ED_view3d_grid_view_scale(scene, v3d, rv3d, &grid_unit); + + if (grid_unit) { + char numstr[32] = ""; + UI_FontThemeColor(BLF_default(), TH_TEXT_HI); + if (v3d->grid != 1.0f) { + BLI_snprintf(numstr, sizeof(numstr), "%s x %.4g", grid_unit, v3d->grid); + } + + *yoffset -= U.widget_unit; + BLF_draw_default_ascii( + xoffset, *yoffset, 0.0f, numstr[0] ? numstr : grid_unit, sizeof(numstr)); + } + } +} + /** * Information drawn on top of the solid plates and composed data */ @@ -1421,19 +1498,10 @@ void view3d_draw_region_info(const bContext *C, ARegion *ar) draw_selected_name(scene, view_layer, ob, xoffset, &yoffset); } -#if 0 /* TODO */ - if (grid_unit) { /* draw below the viewport name */ - char numstr[32] = ""; - - UI_FontThemeColor(BLF_default(), TH_TEXT_HI); - if (v3d->grid != 1.0f) { - BLI_snprintf(numstr, sizeof(numstr), "%s x %.4g", grid_unit, v3d->grid); - } - - *yoffset -= U.widget_unit; - BLF_draw_default_ascii(xoffset, *yoffset, numstr[0] ? numstr : grid_unit, sizeof(numstr)); + if (v3d->gridflag & (V3D_SHOW_FLOOR | V3D_SHOW_X | V3D_SHOW_Y | V3D_SHOW_Z)) { + /* draw below the viewport name */ + draw_grid_unit_name(scene, rv3d, v3d, xoffset, &yoffset); } -#endif } if ((v3d->overlay.flag & V3D_OVERLAY_HIDE_TEXT) == 0) { @@ -1492,7 +1560,7 @@ void view3d_main_region_draw(const bContext *C, ARegion *ar) GPU_pass_cache_garbage_collect(); /* XXX This is in order to draw UI batches with the DRW - * olg context since we now use it for drawing the entire area */ + * old context since we now use it for drawing the entire area. */ gpu_batch_presets_reset(); /* No depth test for drawing action zones afterwards. */ diff --git a/source/blender/editors/space_view3d/view3d_draw_legacy.c b/source/blender/editors/space_view3d/view3d_draw_legacy.c index 02ad481e6ce..d5772e5052a 100644 --- a/source/blender/editors/space_view3d/view3d_draw_legacy.c +++ b/source/blender/editors/space_view3d/view3d_draw_legacy.c @@ -162,6 +162,7 @@ static void validate_object_select_id( Object *obact_eval = DEG_get_evaluated_object(depsgraph, obact); BLI_assert(ar->regiontype == RGN_TYPE_WINDOW); + UNUSED_VARS_NDEBUG(ar); if (obact_eval && (obact_eval->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT) || BKE_paint_select_face_test(obact_eval))) { diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 9a9fe81a13e..e76ef2b0458 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -404,9 +404,7 @@ static void viewops_data_create(bContext *C, if (viewops_flag & VIEWOPS_FLAG_PERSP_ENSURE) { if (ED_view3d_persp_ensure(depsgraph, vod->v3d, vod->ar)) { /* If we're switching from camera view to the perspective one, - * need to tag viewport update, so camera vuew and borders - * are properly updated. - */ + * need to tag viewport update, so camera view and borders are properly updated. */ ED_region_tag_redraw(vod->ar); } } @@ -4946,6 +4944,7 @@ void ED_view3d_cursor3d_position_rotation(bContext *C, .use_object_edit_cage = false, }, mval_fl, + NULL, &dist_px, ray_co, ray_no, @@ -5052,7 +5051,7 @@ void ED_view3d_cursor3d_update(bContext *C, { struct wmMsgBus *mbus = CTX_wm_message_bus(C); - wmMsgParams_RNA msg_key_params = {{{0}}}; + wmMsgParams_RNA msg_key_params = {{0}}; RNA_pointer_create(&scene->id, &RNA_View3DCursor, &scene->cursor, &msg_key_params.ptr); WM_msg_publish_rna_params(mbus, &msg_key_params); } diff --git a/source/blender/editors/space_view3d/view3d_gizmo_camera.c b/source/blender/editors/space_view3d/view3d_gizmo_camera.c index 1bb10697936..42931d5abb5 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_camera.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_camera.c @@ -100,7 +100,7 @@ static void WIDGETGROUP_camera_setup(const bContext *C, wmGizmoGroup *gzgroup) wmGizmo *gz; gz = cagzgroup->dop_dist = WM_gizmo_new_ptr(gzt_arrow, gzgroup, NULL); RNA_enum_set(gz->ptr, "draw_style", ED_GIZMO_ARROW_STYLE_CROSS); - WM_gizmo_set_flag(gz, WM_GIZMO_DRAW_HOVER, true); + WM_gizmo_set_flag(gz, WM_GIZMO_DRAW_HOVER | WM_GIZMO_DRAW_NO_SCALE, true); UI_GetThemeColor3fv(TH_GIZMO_A, gz->color); UI_GetThemeColor3fv(TH_GIZMO_HI, gz->color_hi); @@ -152,9 +152,11 @@ static void WIDGETGROUP_camera_refresh(const bContext *C, wmGizmoGroup *gzgroup) WM_gizmo_set_scale(cagzgroup->dop_dist, ca->drawsize); WM_gizmo_set_flag(cagzgroup->dop_dist, WM_GIZMO_HIDDEN, false); - /* need to set property here for undo. TODO would prefer to do this in _init */ + /* Need to set property here for undo. TODO would prefer to do this in _init */ + PointerRNA camera_dof_ptr; + RNA_pointer_create(&ca->id, &RNA_CameraDOFSettings, &ca->dof, &camera_dof_ptr); WM_gizmo_target_property_def_rna( - cagzgroup->dop_dist, "offset", &camera_ptr, "dof.focus_distance", -1); + cagzgroup->dop_dist, "offset", &camera_dof_ptr, "focus_distance", -1); } else { WM_gizmo_set_flag(cagzgroup->dop_dist, WM_GIZMO_HIDDEN, true); diff --git a/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c b/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c index a984e339305..68159e2d684 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c @@ -77,7 +77,20 @@ static void gizmo_preselect_elem_draw(const bContext *UNUSED(C), wmGizmo *gz) static int gizmo_preselect_elem_test_select(bContext *C, wmGizmo *gz, const int mval[2]) { + wmEvent *event = CTX_wm_window(C)->eventstate; MeshElemGizmo3D *gz_ele = (MeshElemGizmo3D *)gz; + + /* Hack: Switch action mode based on key input */ + const bool is_ctrl_pressed = WM_event_modifier_flag(event) & KM_CTRL; + const bool is_shift_pressed = WM_event_modifier_flag(event) & KM_SHIFT; + EDBM_preselect_action_set(gz_ele->psel, PRESELECT_ACTION_TRANSFORM); + if (is_ctrl_pressed && !is_shift_pressed) { + EDBM_preselect_action_set(gz_ele->psel, PRESELECT_ACTION_CREATE); + } + if (!is_ctrl_pressed && is_shift_pressed) { + EDBM_preselect_action_set(gz_ele->psel, PRESELECT_ACTION_DELETE); + } + struct { Object *ob; BMElem *ele; @@ -87,18 +100,6 @@ static int gizmo_preselect_elem_test_select(bContext *C, wmGizmo *gz, const int .dist = ED_view3d_select_dist_px(), }; - struct { - int base_index; - int vert_index; - int edge_index; - int face_index; - } prev = { - .base_index = gz_ele->base_index, - .vert_index = gz_ele->vert_index, - .edge_index = gz_ele->edge_index, - .face_index = gz_ele->face_index, - }; - { ViewLayer *view_layer = CTX_data_view_layer(C); View3D *v3d = CTX_wm_view3d(C); @@ -115,32 +116,66 @@ static int gizmo_preselect_elem_test_select(bContext *C, wmGizmo *gz, const int { /* TODO: support faces. */ - int base_index = -1; + int base_index_vert = -1; + int base_index_edge = -1; + int base_index_face = -1; BMVert *eve_test; BMEdge *eed_test; + BMFace *efa_test; if (EDBM_unified_findnearest_from_raycast(&vc, gz_ele->bases, gz_ele->bases_len, + false, true, - &base_index, + &base_index_vert, + &base_index_edge, + &base_index_face, &eve_test, &eed_test, - NULL)) { - Base *base = gz_ele->bases[base_index]; - best.ob = base->object; - if (eve_test) { - best.ele = (BMElem *)eve_test; - } - else if (eed_test) { - best.ele = (BMElem *)eed_test; + &efa_test)) { + if (EDBM_preselect_action_get(gz_ele->psel) == PRESELECT_ACTION_DELETE) { + /* Delete action */ + if (efa_test) { + best.ele = (BMElem *)efa_test; + best.base_index = base_index_face; + } } + else { - BLI_assert(0); + /* Transform and create action */ + if (eed_test) { + best.ele = (BMElem *)eed_test; + best.base_index = base_index_edge; + } + } + + /* All actions use same vertex pre-selection. */ + /* Re-topology should always prioritize edge pre-selection. + * Only pre-select a vertex when the cursor is really close to it. */ + if (eve_test) { + BMVert *vert = (BMVert *)eve_test; + float vert_p_co[3], vert_co[3]; + float mval_f[2] = {UNPACK2(vc.mval)}; + mul_v3_m4v3(vert_co, gz_ele->bases[base_index_vert]->object->obmat, vert->co); + ED_view3d_project(vc.ar, vert_co, vert_p_co); + float len = len_v2v2(vert_p_co, mval_f); + if (len < 35) { + best.ele = (BMElem *)eve_test; + best.base_index = base_index_vert; + } + if (!BM_vert_is_boundary(vert) && + EDBM_preselect_action_get(gz_ele->psel) != PRESELECT_ACTION_DELETE) { + best.ele = (BMElem *)eve_test; + best.base_index = base_index_vert; + } } - best.base_index = base_index; + /* Check above should never fail, if it does it's an internal error. */ BLI_assert(best.base_index != -1); + + Base *base = gz_ele->bases[best.base_index]; + best.ob = base->object; } } @@ -167,32 +202,30 @@ static int gizmo_preselect_elem_test_select(bContext *C, wmGizmo *gz, const int } } - if ((prev.base_index == gz_ele->base_index) && (prev.vert_index == gz_ele->vert_index) && - (prev.edge_index == gz_ele->edge_index) && (prev.face_index == gz_ele->face_index)) { - /* pass (only recalculate on change) */ - } - else { - if (best.ele) { - const float(*coords)[3] = NULL; - { - Object *ob = gz_ele->bases[gz_ele->base_index]->object; - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - Mesh *me_eval = (Mesh *)DEG_get_evaluated_id(depsgraph, ob->data); - if (me_eval->runtime.edit_data) { - coords = me_eval->runtime.edit_data->vertexCos; - } + if (best.ele) { + const float(*coords)[3] = NULL; + { + Object *ob = gz_ele->bases[gz_ele->base_index]->object; + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Mesh *me_eval = (Mesh *)DEG_get_evaluated_id(depsgraph, ob->data); + if (me_eval->runtime.edit_data) { + coords = me_eval->runtime.edit_data->vertexCos; } - EDBM_preselect_elem_update_from_single(gz_ele->psel, bm, best.ele, coords); - } - else { - EDBM_preselect_elem_clear(gz_ele->psel); } + EDBM_preselect_elem_update_from_single(gz_ele->psel, bm, best.ele, coords); + EDBM_preselect_elem_update_preview(gz_ele->psel, &vc, bm, best.ele, mval); + } + else { + EDBM_preselect_elem_clear(gz_ele->psel); + EDBM_preselect_preview_clear(gz_ele->psel); + } - RNA_int_set(gz->ptr, "object_index", gz_ele->base_index); - RNA_int_set(gz->ptr, "vert_index", gz_ele->vert_index); - RNA_int_set(gz->ptr, "edge_index", gz_ele->edge_index); - RNA_int_set(gz->ptr, "face_index", gz_ele->face_index); + RNA_int_set(gz->ptr, "object_index", gz_ele->base_index); + RNA_int_set(gz->ptr, "vert_index", gz_ele->vert_index); + RNA_int_set(gz->ptr, "edge_index", gz_ele->edge_index); + RNA_int_set(gz->ptr, "face_index", gz_ele->face_index); + if (best.ele) { ARegion *ar = CTX_wm_region(C); ED_region_tag_redraw(ar); } @@ -471,5 +504,4 @@ void ED_view3d_gizmo_mesh_preselect_get_active(bContext *C, } } } - /** \} */ diff --git a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c index 97a12c7100e..f4e3dc85447 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c @@ -343,6 +343,7 @@ static bool view3d_ruler_item_mousemove(RulerInfo *ruler_info, .use_object_edit_cage = true, }, mval_fl, + NULL, &dist_px, co, ray_normal)) { @@ -363,16 +364,31 @@ static bool view3d_ruler_item_mousemove(RulerInfo *ruler_info, } else if (do_snap) { const float mval_fl[2] = {UNPACK2(mval)}; + float *prev_point = NULL; + + if (inter->co_index != 1) { + if (ruler_item->flag & RULERITEM_USE_ANGLE) { + prev_point = ruler_item->co[1]; + } + else if (inter->co_index == 0) { + prev_point = ruler_item->co[2]; + } + else { + prev_point = ruler_item->co[0]; + } + } if (ED_transform_snap_object_project_view3d( ruler_info->snap_context, - (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE), + (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE | + SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR), &(const struct SnapObjectParams){ .snap_select = SNAP_ALL, .use_object_edit_cage = true, .use_occlusion_test = true, }, mval_fl, + prev_point, &dist_px, co, NULL)) { diff --git a/source/blender/editors/space_view3d/view3d_intern.h b/source/blender/editors/space_view3d/view3d_intern.h index 3a6825aacb4..6b5c27b68f4 100644 --- a/source/blender/editors/space_view3d/view3d_intern.h +++ b/source/blender/editors/space_view3d/view3d_intern.h @@ -30,18 +30,11 @@ struct ARegion; struct ARegionType; -struct Base; struct BoundBox; struct Depsgraph; -struct GPUBatch; -struct Mesh; struct Object; -struct SmokeDomainSettings; struct ViewLayer; -struct bAnimVizSettings; struct bContext; -struct bMotionPath; -struct bPoseChannel; struct wmGizmoGroupType; struct wmGizmoType; struct wmKeyConfig; diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index 725f655c8ce..1c9ce142165 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -88,6 +88,7 @@ #include "ED_particle.h" #include "ED_mesh.h" #include "ED_object.h" +#include "ED_outliner.h" #include "ED_screen.h" #include "ED_select_utils.h" #include "ED_sculpt.h" @@ -1280,9 +1281,15 @@ static bool view3d_lasso_select( } else if (ob && (ob->mode & OB_MODE_POSE)) { changed_multi |= do_lasso_select_pose(vc, mcords, moves, sel_op); + if (changed_multi) { + ED_outliner_select_sync_from_pose_bone_tag(C); + } } else { changed_multi |= do_lasso_select_objects(vc, mcords, moves, sel_op); + if (changed_multi) { + ED_outliner_select_sync_from_object_tag(C); + } } } else { /* Edit Mode */ @@ -1303,12 +1310,15 @@ static bool view3d_lasso_select( break; case OB_ARMATURE: changed = do_lasso_select_armature(vc, mcords, moves, sel_op); + if (changed) { + ED_outliner_select_sync_from_edit_bone_tag(C); + } break; case OB_MBALL: changed = do_lasso_select_meta(vc, mcords, moves, sel_op); break; default: - assert(!"lasso select on incorrect object type"); + BLI_assert(!"lasso select on incorrect object type"); break; } @@ -1488,6 +1498,9 @@ static int object_select_menu_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + + ED_outliner_select_sync_from_object_tag(C); + return OPERATOR_FINISHED; } else { @@ -2350,6 +2363,9 @@ static int view3d_select_exec(bContext *C, wmOperator *op) if (!retval && deselect_all) { retval = ED_armature_edit_deselect_all_visible_multi(C); } + if (retval) { + ED_outliner_select_sync_from_edit_bone_tag(C); + } } else if (obedit->type == OB_LATTICE) { retval = ED_lattice_select_pick(C, location, extend, deselect, toggle); @@ -2410,6 +2426,15 @@ static int view3d_select_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); } } + + if (retval) { + if (obact && obact->mode & OB_MODE_POSE) { + ED_outliner_select_sync_from_pose_bone_tag(C); + } + else { + ED_outliner_select_sync_from_object_tag(C); + } + } } /* Pass-through allows tweaks @@ -3230,6 +3255,7 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op) if (changed) { DEG_id_tag_update(&vc.obedit->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, vc.obedit); + ED_outliner_select_sync_from_edit_bone_tag(C); } break; case OB_LATTICE: @@ -3240,7 +3266,7 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op) } break; default: - assert(!"box select on incorrect object type"); + BLI_assert(!"box select on incorrect object type"); break; } changed_multi |= changed; @@ -3264,9 +3290,15 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op) } else if (vc.obact && vc.obact->mode & OB_MODE_POSE) { changed_multi = do_pose_box_select(C, &vc, &rect, sel_op); + if (changed_multi) { + ED_outliner_select_sync_from_pose_bone_tag(C); + } } else { /* object mode with none active */ changed_multi = do_object_box_select(C, &vc, &rect, sel_op); + if (changed_multi) { + ED_outliner_select_sync_from_object_tag(C); + } } } @@ -3890,7 +3922,8 @@ static bool mball_circle_select(ViewContext *vc, /** Callbacks for circle selection in Editmode */ -static bool obedit_circle_select(ViewContext *vc, +static bool obedit_circle_select(bContext *C, + ViewContext *vc, wmGenericUserData *wm_userdata, const eSelectOp sel_op, const int mval[2], @@ -3911,6 +3944,9 @@ static bool obedit_circle_select(ViewContext *vc, break; case OB_ARMATURE: changed = armature_circle_select(vc, sel_op, mval, rad); + if (changed) { + ED_outliner_select_sync_from_edit_bone_tag(C); + } break; case OB_MBALL: changed = mball_circle_select(vc, sel_op, mval, rad); @@ -3999,7 +4035,7 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op) obedit = vc.obedit; if (obedit) { - obedit_circle_select(&vc, wm_userdata, sel_op, mval, (float)radius); + obedit_circle_select(C, &vc, wm_userdata, sel_op, mval, (float)radius); } else if (BKE_paint_select_face_test(obact)) { paint_facesel_circle_select(&vc, wm_userdata, sel_op, mval, (float)radius); @@ -4009,6 +4045,7 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op) } else if (obact->mode & OB_MODE_POSE) { pose_circle_select(&vc, sel_op, mval, (float)radius); + ED_outliner_select_sync_from_pose_bone_tag(C); } else { BLI_assert(0); @@ -4029,6 +4066,8 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op) if (object_circle_select(&vc, sel_op, mval, (float)radius)) { DEG_id_tag_update(&vc.scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, vc.scene); + + ED_outliner_select_sync_from_object_tag(C); } } diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c index 7f930f1d876..1af94e3ade5 100644 --- a/source/blender/editors/space_view3d/view3d_utils.c +++ b/source/blender/editors/space_view3d/view3d_utils.c @@ -622,8 +622,8 @@ bool ED_view3d_camera_autokey( /** * Call after modifying a locked view. * - * \note Not every view edit currently auto-keys (numpad for eg), - * this is complicated because of smoothview. + * \note Not every view edit currently auto-keys (num-pad for eg), + * this is complicated because of smooth-view. */ bool ED_view3d_camera_lock_autokey(View3D *v3d, RegionView3D *rv3d, diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 14995144c5c..c344b6c8649 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -3520,7 +3520,7 @@ static void initShear_mouseInputMode(TransInfo *t) copy_v3_v3(dir, t->orient_matrix[t->orient_axis_ortho]); } else { - cross_v3_v3v3(dir, t->orient_matrix[t->orient_axis_ortho], t->orient_matrix[t->orient_axis]); + cross_v3_v3v3(dir, t->orient_matrix[t->orient_axis], t->orient_matrix[t->orient_axis_ortho]); } /* Without this, half the gizmo handles move in the opposite direction. */ @@ -6601,7 +6601,7 @@ static void slide_origdata_interp_data_vert(SlideOrigData *sod, * and we do not want to mess up other shape keys */ BM_loop_interp_from_face(bm, l, f_copy, false, false); - /* make sure face-attributes are correct (e.g. MTexPoly) */ + /* make sure face-attributes are correct (e.g. #MLoopUV, #MLoopCol) */ BM_elem_attrs_copy_ex(sod->bm_origfaces, bm, f_copy, l->f, 0x0, CD_MASK_NORMAL); /* weight the loop */ diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index b023199fa1b..45efde24d83 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -42,7 +42,6 @@ struct EditBone; struct NumInput; struct Object; struct RNG; -struct RenderEngineType; struct ReportList; struct Scene; struct ScrArea; @@ -52,7 +51,6 @@ struct TransDataContainer; struct TransInfo; struct TransSnap; struct TransformOrientation; -struct View3D; struct ViewLayer; struct bConstraint; struct bContext; @@ -85,7 +83,9 @@ typedef struct TransSnap { bool snap_self; bool peel; bool snap_spatial_grid; - short status; + char status; + /* Snapped Element Type (currently for objects only). */ + char snapElem; /** snapping from this point (in global-space). */ float snapPoint[3]; /** to this point (in global-space). */ @@ -375,6 +375,7 @@ typedef struct VertSlideParams { typedef struct BoneInitData { struct EditBone *bone; float tail[3]; + float rad_head; float rad_tail; float roll; float head[3]; @@ -1164,4 +1165,7 @@ bool checkUseAxisMatrix(TransInfo *t); th != tc_end; \ th++, i++) +void trans_obdata_in_obmode_update_all(struct TransInfo *t); +void trans_obchild_in_obmode_update_all(struct TransInfo *t); + #endif diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c index ef9d23d1db8..611992c1d3c 100644 --- a/source/blender/editors/transform/transform_conversions.c +++ b/source/blender/editors/transform/transform_conversions.c @@ -52,6 +52,7 @@ #include "BLI_string.h" #include "BLI_bitmap.h" #include "BLI_rect.h" +#include "BLI_kdtree.h" #include "BKE_action.h" #include "BKE_animsys.h" @@ -235,8 +236,9 @@ static void sort_trans_data_selected_first(TransInfo *t) } } -/* distance calculated from not-selected vertex to nearest selected vertex - * warning; this is loops inside loop, has minor N^2 issues, but by sorting list it is OK */ +/** + * Distance calculated from not-selected vertex to nearest selected vertex. + */ static void set_prop_dist(TransInfo *t, const bool with_dist) { int a; @@ -255,54 +257,124 @@ static void set_prop_dist(TransInfo *t, const bool with_dist) } } + /* Count number of selected. */ + int td_table_len = 0; FOREACH_TRANS_DATA_CONTAINER (t, tc) { - TransData *tob = tc->data; - for (a = 0; a < tc->data_len; a++, tob++) { + TransData *td = tc->data; + for (a = 0; a < tc->data_len; a++, td++) { + if (td->flag & TD_SELECTED) { + td_table_len++; + } + else { + /* By definition transform-data has selected items in beginning. */ + break; + } + } + } - tob->rdist = 0.0f; // init, it was mallocced + /* Pointers to selected's #TransData. + * Used to find #TransData from the index returned by #BLI_kdtree_find_nearest. */ + TransData **td_table = MEM_mallocN(sizeof(*td_table) * td_table_len, __func__); - if ((tob->flag & TD_SELECTED) == 0) { - TransData *td; - int i; - float dist_sq, vec[3]; + /* Create and fill kd-tree of selected's positions - in global or proj_vec space. */ + KDTree_3d *td_tree = BLI_kdtree_3d_new(td_table_len); - tob->rdist = -1.0f; // signal for next loop + int td_table_index = 0; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (a = 0; a < tc->data_len; a++, td++) { + if (td->flag & TD_SELECTED) { + /* Initialize, it was mallocced. */ + float vec[3]; + td->rdist = 0.0f; + + if (use_island) { + if (tc->use_local_mat) { + mul_v3_m4v3(vec, tc->mat, td->iloc); + } + else { + mul_v3_m3v3(vec, td->mtx, td->iloc); + } + } + else { + if (tc->use_local_mat) { + mul_v3_m4v3(vec, tc->mat, td->center); + } + else { + mul_v3_m3v3(vec, td->mtx, td->center); + } + } - for (i = 0, td = tc->data; i < tc->data_len; i++, td++) { - if (td->flag & TD_SELECTED) { - if (use_island) { - sub_v3_v3v3(vec, tob->iloc, td->iloc); - } - else { - sub_v3_v3v3(vec, tob->center, td->center); - } - mul_m3_v3(tob->mtx, vec); + if (proj_vec) { + float vec_p[3]; + project_v3_v3v3(vec_p, vec, proj_vec); + sub_v3_v3(vec, vec_p); + } - if (proj_vec) { - float vec_p[3]; - project_v3_v3v3(vec_p, vec, proj_vec); - sub_v3_v3(vec, vec_p); - } + BLI_kdtree_3d_insert(td_tree, td_table_index, vec); + td_table[td_table_index++] = td; + } + else { + /* By definition transform-data has selected items in beginning. */ + break; + } + } + } + BLI_assert(td_table_index == td_table_len); - dist_sq = len_squared_v3(vec); - if ((tob->rdist == -1.0f) || (dist_sq < SQUARE(tob->rdist))) { - tob->rdist = sqrtf(dist_sq); - if (use_island) { - copy_v3_v3(tob->center, td->center); - copy_m3_m3(tob->axismtx, td->axismtx); - } - } + BLI_kdtree_3d_balance(td_tree); + + /* For each non-selected vertex, find distance to the nearest selected vertex. */ + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (a = 0; a < tc->data_len; a++, td++) { + if ((td->flag & TD_SELECTED) == 0) { + float vec[3]; + + if (use_island) { + if (tc->use_local_mat) { + mul_v3_m4v3(vec, tc->mat, td->iloc); } else { - break; /* by definition transdata has selected items in beginning */ + mul_v3_m3v3(vec, td->mtx, td->iloc); } } + else { + if (tc->use_local_mat) { + mul_v3_m4v3(vec, tc->mat, td->center); + } + else { + mul_v3_m3v3(vec, td->mtx, td->center); + } + } + + if (proj_vec) { + float vec_p[3]; + project_v3_v3v3(vec_p, vec, proj_vec); + sub_v3_v3(vec, vec_p); + } + + KDTreeNearest_3d nearest; + const int td_index = BLI_kdtree_3d_find_nearest(td_tree, vec, &nearest); + + td->rdist = -1.0f; + if (td_index != -1) { + td->rdist = nearest.dist; + if (use_island) { + copy_v3_v3(td->center, td_table[td_index]->center); + copy_m3_m3(td->axismtx, td_table[td_index]->axismtx); + } + } + if (with_dist) { - tob->dist = tob->rdist; + td->dist = td->rdist; } } } } + + BLI_kdtree_3d_free(td_tree); + MEM_freeN(td_table); } /* ************************** CONVERSIONS ************************* */ @@ -1445,6 +1517,7 @@ void restoreBones(TransDataContainer *tc) ebo = bid->bone; ebo->dist = bid->dist; + ebo->rad_head = bid->rad_head; ebo->rad_tail = bid->rad_tail; ebo->roll = bid->roll; ebo->xwidth = bid->xwidth; @@ -1710,6 +1783,7 @@ static void createTransArmatureVerts(TransInfo *t) if (eboflip) { bid[i].bone = eboflip; bid[i].dist = eboflip->dist; + bid[i].rad_head = eboflip->rad_head; bid[i].rad_tail = eboflip->rad_tail; bid[i].roll = eboflip->roll; bid[i].xwidth = eboflip->xwidth; @@ -6462,11 +6536,14 @@ static void flush_trans_object_base_deps_flag(Depsgraph *depsgraph, Object *obje depsgraph, &object->id, DEG_OB_COMP_TRANSFORM, set_trans_object_base_deps_flag_cb, NULL); } -static void trans_object_base_deps_flag_finish(ViewLayer *view_layer) +static void trans_object_base_deps_flag_finish(const TransInfo *t, ViewLayer *view_layer) { - for (Base *base = view_layer->object_bases.first; base; base = base->next) { - if (base->object->id.tag & LIB_TAG_DOIT) { - base->flag_legacy |= BA_SNAP_FIX_DEPS_FIASCO; + + if ((t->options & CTX_OBMODE_XFORM_OBDATA) == 0) { + for (Base *base = view_layer->object_bases.first; base; base = base->next) { + if (base->object->id.tag & LIB_TAG_DOIT) { + base->flag_legacy |= BA_SNAP_FIX_DEPS_FIASCO; + } } } } @@ -6495,7 +6572,7 @@ static void set_trans_object_base_flags(TransInfo *t) trans_object_base_deps_flag_prepare(view_layer); /* Traverse all bases and set all possible flags. */ for (Base *base = view_layer->object_bases.first; base; base = base->next) { - base->flag_legacy &= ~BA_WAS_SEL; + base->flag_legacy &= ~(BA_WAS_SEL | BA_TRANSFORM_LOCKED_IN_PLACE); if (BASE_SELECTED_EDITABLE(v3d, base)) { Object *ob = base->object; Object *parsel = ob->parent; @@ -6528,7 +6605,7 @@ static void set_trans_object_base_flags(TransInfo *t) /* Store temporary bits in base indicating that base is being modified * (directly or indirectly) by transforming objects. */ - trans_object_base_deps_flag_finish(view_layer); + trans_object_base_deps_flag_finish(t, view_layer); } static bool mark_children(Object *ob) @@ -6596,7 +6673,7 @@ static int count_proportional_objects(TransInfo *t) /* Store temporary bits in base indicating that base is being modified * (directly or indirectly) by transforming objects. */ - trans_object_base_deps_flag_finish(view_layer); + trans_object_base_deps_flag_finish(t, view_layer); return total; } @@ -6611,7 +6688,8 @@ static void clear_trans_object_base_flags(TransInfo *t) } base->flag_legacy &= ~(BA_WAS_SEL | BA_SNAP_FIX_DEPS_FIASCO | BA_TEMP_TAG | - BA_TRANSFORM_CHILD | BA_TRANSFORM_PARENT); + BA_TRANSFORM_CHILD | BA_TRANSFORM_PARENT | + BA_TRANSFORM_LOCKED_IN_PLACE); } } @@ -7049,7 +7127,15 @@ static void special_aftertrans_update__mesh(bContext *UNUSED(C), TransInfo *t) hflag = BM_ELEM_SELECT; } - EDBM_automerge(t->scene, tc->obedit, true, hflag); + if (t->scene->toolsettings->automerge & AUTO_MERGE) { + if (t->scene->toolsettings->automerge & AUTO_MERGE_AND_SPLIT) { + EDBM_automerge_and_split( + tc->obedit, true, true, true, hflag, t->scene->toolsettings->doublimit); + } + else { + EDBM_automerge(tc->obedit, true, hflag, t->scene->toolsettings->doublimit); + } + } /* Special case, this is needed or faces won't re-select. * Flush selected edges to faces. */ @@ -7433,7 +7519,7 @@ void special_aftertrans_update(bContext *C, TransInfo *t) else if (t->flag & T_POSE && (t->mode == TFM_BONESIZE)) { /* Handle the exception where for TFM_BONESIZE in edit mode we pretend to be * in pose mode (to use bone orientation matrix), - * in that case we don't do operations like autokeyframing. */ + * in that case we don't do operations like auto-keyframing. */ FOREACH_TRANS_DATA_CONTAINER (t, tc) { ob = tc->poseobj; DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); @@ -7604,6 +7690,278 @@ int special_transform_moving(TransInfo *t) return 0; } +/* -------------------------------------------------------------------- */ +/** \name Object Mode Custom Data + * \{ */ + +typedef struct TransDataObject { + + /** + * Object to object data transform table. + * Don't add these to transform data because we may want to include child objects + * which aren't being transformed. + * - The key is object data #ID. + * - The value is #XFormObjectData_Extra. + */ + struct GHash *obdata_in_obmode_map; + + /** + * Transform + * - The key is object data #Object. + * - The value is #XFormObjectSkipChild. + */ + struct GHash *obchild_in_obmode_map; + +} TransDataObject; + +/** \} */ + +static void trans_obdata_in_obmode_free_all(TransDataObject *tdo); +static void trans_obchild_in_obmode_free_all(TransDataObject *tdo); + +static void freeTransObjectCustomData(TransInfo *t, + TransDataContainer *UNUSED(tc), + TransCustomData *custom_data) +{ + TransDataObject *tdo = custom_data->data; + custom_data->data = NULL; + + if (t->options & CTX_OBMODE_XFORM_OBDATA) { + trans_obdata_in_obmode_free_all(tdo); + } + + if (t->options & CTX_OBMODE_XFORM_SKIP_CHILDREN) { + trans_obchild_in_obmode_free_all(tdo); + } + MEM_freeN(tdo); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Object Data in Object Mode + * + * Use to implement 'Affect Only Origins' feature. + * We need this to be detached from transform data because, + * unlike transforming regular objects, we need to transform the children. + * + * \{ */ + +struct XFormObjectData_Extra { + Object *ob; + float obmat_orig[4][4]; + struct XFormObjectData *xod; +}; + +static void trans_obdata_in_obmode_ensure_object(TransDataObject *tdo, Object *ob) +{ + if (tdo->obdata_in_obmode_map == NULL) { + tdo->obdata_in_obmode_map = BLI_ghash_ptr_new(__func__); + } + + void **xf_p; + if (!BLI_ghash_ensure_p(tdo->obdata_in_obmode_map, ob->data, &xf_p)) { + struct XFormObjectData_Extra *xf = MEM_mallocN(sizeof(*xf), __func__); + copy_m4_m4(xf->obmat_orig, ob->obmat); + xf->ob = ob; + /* Result may be NULL, that's OK. */ + xf->xod = ED_object_data_xform_create(ob->data); + *xf_p = xf; + } +} + +void trans_obdata_in_obmode_update_all(TransInfo *t) +{ + TransDataObject *tdo = t->custom.type.data; + if (tdo->obdata_in_obmode_map == NULL) { + return; + } + + struct Main *bmain = CTX_data_main(t->context); + BKE_scene_graph_evaluated_ensure(t->depsgraph, bmain); + + GHashIterator gh_iter; + GHASH_ITER (gh_iter, tdo->obdata_in_obmode_map) { + ID *id = BLI_ghashIterator_getKey(&gh_iter); + struct XFormObjectData_Extra *xf = BLI_ghashIterator_getValue(&gh_iter); + if (xf->xod == NULL) { + continue; + } + + Object *ob_eval = DEG_get_evaluated_object(t->depsgraph, xf->ob); + float imat[4][4], dmat[4][4]; + invert_m4_m4(imat, xf->obmat_orig); + mul_m4_m4m4(dmat, imat, ob_eval->obmat); + invert_m4(dmat); + + ED_object_data_xform_by_mat4(xf->xod, dmat); + DEG_id_tag_update(id, ID_RECALC_GEOMETRY); + } +} + +/** Callback for #GHash free. */ +static void trans_obdata_in_obmode_free_elem(void *xf_p) +{ + struct XFormObjectData_Extra *xf = xf_p; + if (xf->xod) { + ED_object_data_xform_destroy(xf->xod); + } + MEM_freeN(xf); +} + +static void trans_obdata_in_obmode_free_all(TransDataObject *tdo) +{ + if (tdo->obdata_in_obmode_map != NULL) { + BLI_ghash_free(tdo->obdata_in_obmode_map, NULL, trans_obdata_in_obmode_free_elem); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Object Child Skip + * + * Don't transform unselected children, this is done using the parent inverse matrix. + * + * \note The complex logic here is caused by mixed selection within a single selection chain, + * otherwise we only need #OB_SKIP_CHILD_PARENT_IS_XFORM for single objects. + * + * \{ */ + +enum { + /** + * The parent is transformed, this is held in place. + */ + OB_SKIP_CHILD_PARENT_IS_XFORM = 1, + /** + * The same as #OB_SKIP_CHILD_PARENT_IS_XFORM, + * however this objects parent isn't transformed directly. + */ + OB_SKIP_CHILD_PARENT_IS_XFORM_INDIRECT = 3, + /** + * Use the parent invert matrix to apply transformation, + * this is needed, because breaks in the selection chain prevents this from being transformed. + * This is used to add the transform which would have been added + * if there weren't breaks in the parent/child chain. + */ + OB_SKIP_CHILD_PARENT_APPLY_TRANSFORM = 2, +}; + +struct XFormObjectSkipChild { + float obmat_orig[4][4]; + float parent_obmat_orig[4][4]; + float parent_obmat_inv_orig[4][4]; + float parent_recurse_obmat_orig[4][4]; + float parentinv_orig[4][4]; + Object *ob_parent_recurse; + int mode; +}; + +static void trans_obchild_in_obmode_ensure_object(TransDataObject *tdo, + Object *ob, + Object *ob_parent_recurse, + int mode) +{ + if (tdo->obchild_in_obmode_map == NULL) { + tdo->obchild_in_obmode_map = BLI_ghash_ptr_new(__func__); + } + + void **xf_p; + if (!BLI_ghash_ensure_p(tdo->obchild_in_obmode_map, ob, &xf_p)) { + struct XFormObjectSkipChild *xf = MEM_mallocN(sizeof(*xf), __func__); + copy_m4_m4(xf->parentinv_orig, ob->parentinv); + copy_m4_m4(xf->obmat_orig, ob->obmat); + copy_m4_m4(xf->parent_obmat_orig, ob->parent->obmat); + invert_m4_m4(xf->parent_obmat_inv_orig, ob->parent->obmat); + if (ob_parent_recurse) { + copy_m4_m4(xf->parent_recurse_obmat_orig, ob_parent_recurse->obmat); + } + xf->mode = mode; + xf->ob_parent_recurse = ob_parent_recurse; + *xf_p = xf; + } +} + +void trans_obchild_in_obmode_update_all(TransInfo *t) +{ + TransDataObject *tdo = t->custom.type.data; + if (tdo->obchild_in_obmode_map == NULL) { + return; + } + + struct Main *bmain = CTX_data_main(t->context); + BKE_scene_graph_evaluated_ensure(t->depsgraph, bmain); + + GHashIterator gh_iter; + GHASH_ITER (gh_iter, tdo->obchild_in_obmode_map) { + Object *ob = BLI_ghashIterator_getKey(&gh_iter); + struct XFormObjectSkipChild *xf = BLI_ghashIterator_getValue(&gh_iter); + + /* The following blocks below assign 'dmat'. */ + float dmat[4][4]; + + if (xf->mode == OB_SKIP_CHILD_PARENT_IS_XFORM) { + /* Parent is transformed, this isn't so compensate. */ + Object *ob_parent_eval = DEG_get_evaluated_object(t->depsgraph, ob->parent); + mul_m4_m4m4(dmat, xf->parent_obmat_inv_orig, ob_parent_eval->obmat); + invert_m4(dmat); + } + else if (xf->mode == OB_SKIP_CHILD_PARENT_IS_XFORM_INDIRECT) { + /* Calculate parent matrix (from the root transform). */ + Object *ob_parent_recurse_eval = DEG_get_evaluated_object(t->depsgraph, + xf->ob_parent_recurse); + float parent_recurse_obmat_inv[4][4]; + invert_m4_m4(parent_recurse_obmat_inv, ob_parent_recurse_eval->obmat); + mul_m4_m4m4(dmat, xf->parent_recurse_obmat_orig, parent_recurse_obmat_inv); + invert_m4(dmat); + float parent_obmat_calc[4][4]; + mul_m4_m4m4(parent_obmat_calc, dmat, xf->parent_obmat_orig); + + /* Apply to the parent inverse matrix. */ + mul_m4_m4m4(dmat, xf->parent_obmat_inv_orig, parent_obmat_calc); + invert_m4(dmat); + } + else { + BLI_assert(xf->mode == OB_SKIP_CHILD_PARENT_APPLY_TRANSFORM); + /* Transform this - without transform data. */ + Object *ob_parent_recurse_eval = DEG_get_evaluated_object(t->depsgraph, + xf->ob_parent_recurse); + float parent_recurse_obmat_inv[4][4]; + invert_m4_m4(parent_recurse_obmat_inv, ob_parent_recurse_eval->obmat); + mul_m4_m4m4(dmat, xf->parent_recurse_obmat_orig, parent_recurse_obmat_inv); + invert_m4(dmat); + float obmat_calc[4][4]; + mul_m4_m4m4(obmat_calc, dmat, xf->obmat_orig); + /* obmat_calc is just obmat. */ + + /* Get the matrices relative to the parent. */ + float obmat_parent_relative_orig[4][4]; + float obmat_parent_relative_calc[4][4]; + float obmat_parent_relative_inv_orig[4][4]; + + mul_m4_m4m4(obmat_parent_relative_orig, xf->parent_obmat_inv_orig, xf->obmat_orig); + mul_m4_m4m4(obmat_parent_relative_calc, xf->parent_obmat_inv_orig, obmat_calc); + invert_m4_m4(obmat_parent_relative_inv_orig, obmat_parent_relative_orig); + + /* Apply to the parent inverse matrix. */ + mul_m4_m4m4(dmat, obmat_parent_relative_calc, obmat_parent_relative_inv_orig); + } + + mul_m4_m4m4(ob->parentinv, dmat, xf->parentinv_orig); + + DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); + } +} + +static void trans_obchild_in_obmode_free_all(TransDataObject *tdo) +{ + if (tdo->obchild_in_obmode_map != NULL) { + BLI_ghash_free(tdo->obchild_in_obmode_map, NULL, MEM_freeN); + } +} + +/** \} */ + static void createTransObject(bContext *C, TransInfo *t) { TransData *td = NULL; @@ -7630,6 +7988,10 @@ static void createTransObject(bContext *C, TransInfo *t) td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransOb"); tx = tc->data_ext = MEM_callocN(tc->data_len * sizeof(TransDataExtension), "TransObExtension"); + TransDataObject *tdo = MEM_callocN(sizeof(*tdo), __func__); + t->custom.type.data = tdo; + t->custom.type.free_cb = freeTransObjectCustomData; + CTX_DATA_BEGIN (C, Base *, base, selected_bases) { Object *ob = base->object; @@ -7648,6 +8010,24 @@ static void createTransObject(bContext *C, TransInfo *t) td->flag |= TD_SKIP; } + if (t->options & CTX_OBMODE_XFORM_OBDATA) { + ID *id = ob->data; + if (!id || id->lib) { + td->flag |= TD_SKIP; + } + else if (BKE_object_is_in_editmode(ob)) { + /* The object could have edit-mode data from another view-layer, + * it's such a corner-case it can be skipped for now - Campbell. */ + td->flag |= TD_SKIP; + } + } + + if (t->options & CTX_OBMODE_XFORM_OBDATA) { + if ((td->flag & TD_SKIP) == 0) { + trans_obdata_in_obmode_ensure_object(tdo, ob); + } + } + ObjectToTransData(t, td, ob); td->val = NULL; td++; @@ -7679,6 +8059,123 @@ static void createTransObject(bContext *C, TransInfo *t) } } } + + if (t->options & CTX_OBMODE_XFORM_OBDATA) { + GSet *objects_in_transdata = BLI_gset_ptr_new_ex(__func__, tc->data_len); + td = tc->data; + for (int i = 0; i < tc->data_len; i++, td++) { + if ((td->flag & TD_SKIP) == 0) { + BLI_gset_add(objects_in_transdata, td->ob); + } + } + + ViewLayer *view_layer = t->view_layer; + View3D *v3d = t->view; + + for (Base *base = view_layer->object_bases.first; base; base = base->next) { + Object *ob = base->object; + + /* if base is not selected, not a parent of selection + * or not a child of selection and it is editable and selectable */ + if ((base->flag_legacy & BA_WAS_SEL) && (base->flag & BASE_SELECTED) == 0 && + BASE_EDITABLE(v3d, base) && BASE_SELECTABLE(v3d, base)) { + + Object *ob_parent = ob->parent; + if (ob_parent != NULL) { + if (!BLI_gset_haskey(objects_in_transdata, ob)) { + bool parent_in_transdata = false; + while (ob_parent != NULL) { + if (BLI_gset_haskey(objects_in_transdata, ob_parent)) { + parent_in_transdata = true; + break; + } + ob_parent = ob_parent->parent; + } + if (parent_in_transdata) { + trans_obdata_in_obmode_ensure_object(tdo, ob); + } + } + } + } + } + BLI_gset_free(objects_in_transdata, NULL); + } + + if (t->options & CTX_OBMODE_XFORM_SKIP_CHILDREN) { + +#define BASE_XFORM_INDIRECT(base) \ + ((base->flag_legacy & BA_WAS_SEL) && (base->flag & BASE_SELECTED) == 0) + + GSet *objects_in_transdata = BLI_gset_ptr_new_ex(__func__, tc->data_len); + GHash *objects_parent_root = BLI_ghash_ptr_new_ex(__func__, tc->data_len); + td = tc->data; + for (int i = 0; i < tc->data_len; i++, td++) { + if ((td->flag & TD_SKIP) == 0) { + BLI_gset_add(objects_in_transdata, td->ob); + } + } + + ViewLayer *view_layer = t->view_layer; + + for (Base *base = view_layer->object_bases.first; base; base = base->next) { + Object *ob = base->object; + if (ob->parent != NULL) { + if (ob->parent && !BLI_gset_haskey(objects_in_transdata, ob->parent) && + !BLI_gset_haskey(objects_in_transdata, ob)) { + if (((base->flag_legacy & BA_WAS_SEL) && (base->flag & BASE_SELECTED) == 0)) { + Base *base_parent = BKE_view_layer_base_find(view_layer, ob->parent); + if (base_parent && !BASE_XFORM_INDIRECT(base_parent)) { + Object *ob_parent_recurse = ob->parent; + if (ob_parent_recurse != NULL) { + while (ob_parent_recurse != NULL) { + if (BLI_gset_haskey(objects_in_transdata, ob_parent_recurse)) { + break; + } + ob_parent_recurse = ob_parent_recurse->parent; + } + + if (ob_parent_recurse) { + trans_obchild_in_obmode_ensure_object( + tdo, ob, ob_parent_recurse, OB_SKIP_CHILD_PARENT_APPLY_TRANSFORM); + BLI_ghash_insert(objects_parent_root, ob, ob_parent_recurse); + base->flag_legacy |= BA_TRANSFORM_LOCKED_IN_PLACE; + } + } + } + } + } + } + } + + for (Base *base = view_layer->object_bases.first; base; base = base->next) { + Object *ob = base->object; + + if (BASE_XFORM_INDIRECT(base) || BLI_gset_haskey(objects_in_transdata, ob)) { + /* pass. */ + } + else if (ob->parent != NULL) { + Base *base_parent = BKE_view_layer_base_find(view_layer, ob->parent); + if (base_parent) { + if (BASE_XFORM_INDIRECT(base_parent) || + BLI_gset_haskey(objects_in_transdata, ob->parent)) { + trans_obchild_in_obmode_ensure_object(tdo, ob, NULL, OB_SKIP_CHILD_PARENT_IS_XFORM); + base->flag_legacy |= BA_TRANSFORM_LOCKED_IN_PLACE; + } + else { + Object *ob_parent_recurse = BLI_ghash_lookup(objects_parent_root, ob->parent); + if (ob_parent_recurse) { + trans_obchild_in_obmode_ensure_object( + tdo, ob, ob_parent_recurse, OB_SKIP_CHILD_PARENT_IS_XFORM_INDIRECT); + } + } + } + } + } + BLI_gset_free(objects_in_transdata, NULL); + BLI_ghash_free(objects_parent_root, NULL, NULL); + +#undef BASE_XFORM_INDIRECT + } } /* transcribe given node into TransData2D for Transforming */ @@ -9665,6 +10162,13 @@ void createTransData(bContext *C, TransInfo *t) /* Needed for correct Object.obmat after duplication, see: T62135. */ BKE_scene_graph_evaluated_ensure(t->depsgraph, CTX_data_main(t->context)); + if ((scene->toolsettings->transform_flag & SCE_XFORM_DATA_ORIGIN) != 0) { + t->options |= CTX_OBMODE_XFORM_OBDATA; + } + if ((scene->toolsettings->transform_flag & SCE_XFORM_SKIP_CHILDREN) != 0) { + t->options |= CTX_OBMODE_XFORM_SKIP_CHILDREN; + } + createTransObject(C, t); countAndCleanTransDataContainer(t); t->flag |= T_OBJECT; diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index 969e2558abb..7ae140e1815 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -81,6 +81,7 @@ #include "BKE_workspace.h" #include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" #include "ED_anim_api.h" #include "ED_armature.h" @@ -900,7 +901,7 @@ static void recalcData_objects(TransInfo *t) DEG_id_tag_update(tc->obedit->data, 0); /* sets recalc flags */ BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); EDBM_mesh_normals_update(em); - BKE_editmesh_tessface_calc(em); + BKE_editmesh_looptri_calc(em); } } else if (t->obedit_type == OB_ARMATURE) { /* no recalc flag, does pose */ @@ -1149,6 +1150,14 @@ static void recalcData_objects(TransInfo *t) /* Update motion paths once for all transformed objects. */ ED_objects_recalculate_paths(t->context, t->scene, true); } + + if (t->options & CTX_OBMODE_XFORM_SKIP_CHILDREN) { + trans_obchild_in_obmode_update_all(t); + } + + if (t->options & CTX_OBMODE_XFORM_OBDATA) { + trans_obdata_in_obmode_update_all(t); + } } } diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c index fbfeb4f53de..355cf139abf 100644 --- a/source/blender/editors/transform/transform_gizmo_3d.c +++ b/source/blender/editors/transform/transform_gizmo_3d.c @@ -1392,7 +1392,8 @@ void drawDial3d(const TransInfo *t) scale *= ED_view3d_pixel_size_no_ui_scale(t->ar->regiondata, mat_final[3]); mul_mat3_m4_fl(mat_final, scale); - if ((t->tsnap.mode & (SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID)) && activeSnap(t)) { + if (activeSnap(t) && (!transformModeUseSnap(t) || + (t->tsnap.mode & (SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID)))) { increment = (t->modifiers & MOD_PRECISION) ? t->snap[2] : t->snap[1]; } else { diff --git a/source/blender/editors/transform/transform_input.c b/source/blender/editors/transform/transform_input.c index 6ebed88878f..e771fe43bd8 100644 --- a/source/blender/editors/transform/transform_input.c +++ b/source/blender/editors/transform/transform_input.c @@ -299,7 +299,7 @@ static void calcSpringFactor(MouseInput *mi) void initMouseInputMode(TransInfo *t, MouseInput *mi, MouseInputMode mode) { - /* incase we allocate a new value */ + /* In case we allocate a new value. */ void *mi_data_prev = mi->data; mi->use_virtual_mval = true; diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c index f8e33fe70ad..604c5bbc083 100644 --- a/source/blender/editors/transform/transform_ops.c +++ b/source/blender/editors/transform/transform_ops.c @@ -139,22 +139,22 @@ const EnumPropertyItem rna_enum_transform_mode_types[] = { {TFM_CREASE, "CREASE", 0, "Crease", ""}, {TFM_MIRROR, "MIRROR", 0, "Mirror", ""}, {TFM_BONESIZE, "BONE_SIZE", 0, "Bonesize", ""}, - {TFM_BONE_ENVELOPE, "BONE_ENVELOPE", 0, "Bone_Envelope", ""}, - {TFM_BONE_ENVELOPE_DIST, "BONE_ENVELOPE_DIST", 0, "Bone_Envelope_Distance", ""}, - {TFM_CURVE_SHRINKFATTEN, "CURVE_SHRINKFATTEN", 0, "Curve_Shrinkfatten", ""}, - {TFM_MASK_SHRINKFATTEN, "MASK_SHRINKFATTEN", 0, "Mask_Shrinkfatten", ""}, - {TFM_GPENCIL_SHRINKFATTEN, "GPENCIL_SHRINKFATTEN", 0, "GPencil_Shrinkfatten", ""}, - {TFM_BONE_ROLL, "BONE_ROLL", 0, "Bone_Roll", ""}, - {TFM_TIME_TRANSLATE, "TIME_TRANSLATE", 0, "Time_Translate", ""}, - {TFM_TIME_SLIDE, "TIME_SLIDE", 0, "Time_Slide", ""}, - {TFM_TIME_SCALE, "TIME_SCALE", 0, "Time_Scale", ""}, - {TFM_TIME_EXTEND, "TIME_EXTEND", 0, "Time_Extend", ""}, - {TFM_BAKE_TIME, "BAKE_TIME", 0, "Bake_Time", ""}, + {TFM_BONE_ENVELOPE, "BONE_ENVELOPE", 0, "Bone Envelope", ""}, + {TFM_BONE_ENVELOPE_DIST, "BONE_ENVELOPE_DIST", 0, "Bone Envelope Distance", ""}, + {TFM_CURVE_SHRINKFATTEN, "CURVE_SHRINKFATTEN", 0, "Curve Shrinkfatten", ""}, + {TFM_MASK_SHRINKFATTEN, "MASK_SHRINKFATTEN", 0, "Mask Shrinkfatten", ""}, + {TFM_GPENCIL_SHRINKFATTEN, "GPENCIL_SHRINKFATTEN", 0, "GPencil Shrinkfatten", ""}, + {TFM_BONE_ROLL, "BONE_ROLL", 0, "Bone Roll", ""}, + {TFM_TIME_TRANSLATE, "TIME_TRANSLATE", 0, "Time Translate", ""}, + {TFM_TIME_SLIDE, "TIME_SLIDE", 0, "Time Slide", ""}, + {TFM_TIME_SCALE, "TIME_SCALE", 0, "Time Scale", ""}, + {TFM_TIME_EXTEND, "TIME_EXTEND", 0, "Time Extend", ""}, + {TFM_BAKE_TIME, "BAKE_TIME", 0, "Bake Time", ""}, {TFM_BWEIGHT, "BWEIGHT", 0, "Bweight", ""}, {TFM_ALIGN, "ALIGN", 0, "Align", ""}, {TFM_EDGE_SLIDE, "EDGESLIDE", 0, "Edge Slide", ""}, {TFM_SEQ_SLIDE, "SEQSLIDE", 0, "Sequence Slide", ""}, - {TFM_GPENCIL_OPACITY, "GPENCIL_OPACITY", 0, "GPencil_Opacity", ""}, + {TFM_GPENCIL_OPACITY, "GPENCIL_OPACITY", 0, "GPencil Opacity", ""}, {0, NULL, 0, NULL, NULL}, }; @@ -573,7 +573,7 @@ void Transform_Properties(struct wmOperatorType *ot, int flags) if (flags & P_ORIENT_AXIS_ORTHO) { prop = RNA_def_property(ot->srna, "orient_axis_ortho", PROP_ENUM, PROP_NONE); RNA_def_property_ui_text(prop, "Axis Ortho", ""); - RNA_def_property_enum_default(prop, 1); + RNA_def_property_enum_default(prop, 0); RNA_def_property_enum_items(prop, rna_enum_axis_xyz_items); RNA_def_property_flag(prop, PROP_SKIP_SAVE); } diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index dbcc6c1b04a..571ce7a6bc2 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -166,7 +166,10 @@ void drawSnapping(const struct bContext *C, TransInfo *t) activeCol[3] = 192; if (t->spacetype == SPACE_VIEW3D) { - if (validSnap(t)) { + bool draw_target = (t->tsnap.status & TARGET_INIT) && + (t->scene->toolsettings->snap_mode & SCE_SNAP_MODE_EDGE_PERPENDICULAR); + + if (draw_target || validSnap(t)) { TransSnapPoint *p; RegionView3D *rv3d = CTX_wm_region_view3d(C); float imat[4][4]; @@ -214,6 +217,48 @@ void drawSnapping(const struct bContext *C, TransInfo *t) immEnd(); } + if (draw_target) { + /* Draw snapTarget */ + float targ_co[3], vx[3], vy[3], v1[3], v2[3], v3[3], v4[4]; + copy_v3_v3(targ_co, t->tsnap.snapTarget); + float px_size = 0.75f * size * ED_view3d_pixel_size(rv3d, targ_co); + + mul_v3_v3fl(vx, imat[0], px_size); + mul_v3_v3fl(vy, imat[1], px_size); + + add_v3_v3v3(v1, vx, vy); + sub_v3_v3v3(v2, vx, vy); + negate_v3_v3(v3, v1); + negate_v3_v3(v4, v2); + + add_v3_v3(v1, targ_co); + add_v3_v3(v2, targ_co); + add_v3_v3(v3, targ_co); + add_v3_v3(v4, targ_co); + + immUniformColor4ubv(col); + immBegin(GPU_PRIM_LINES, 4); + immVertex3fv(pos, v3); + immVertex3fv(pos, v1); + immVertex3fv(pos, v4); + immVertex3fv(pos, v2); + immEnd(); + + if (t->tsnap.snapElem & SCE_SNAP_MODE_EDGE_PERPENDICULAR) { + immUnbindProgram(); + + immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR); + immUniform1f("dash_width", 6.0f * U.pixelsize); + immUniform1f("dash_factor", 1.0f / 4.0f); + immUniformColor4ubv(col); + + immBegin(GPU_PRIM_LINES, 2); + immVertex3fv(pos, targ_co); + immVertex3fv(pos, t->tsnap.snapPoint); + immEnd(); + } + } + immUnbindProgram(); GPU_depth_test(true); @@ -328,6 +373,7 @@ void applyProject(TransInfo *t) .use_occlusion_test = false, }, mval_fl, + NULL, 0, loc, no)) { @@ -586,12 +632,18 @@ static void initSnappingMode(TransInfo *t) else if (t->tsnap.applySnap != NULL && // A snapping function actually exist (obedit_type == -1)) // Object Mode { - /* In "Edit Strokes" mode, - * snap tool can perform snap to selected or active objects (see T49632) - * TODO: perform self snap in gpencil_strokes */ - t->tsnap.modeSelect = (((t->options & (CTX_GPENCIL_STROKES | CTX_CURSOR)) != 0) ? - SNAP_ALL : - SNAP_NOT_SELECTED); + + if (t->options & (CTX_GPENCIL_STROKES | CTX_CURSOR | CTX_OBMODE_XFORM_OBDATA)) { + /* In "Edit Strokes" mode, + * snap tool can perform snap to selected or active objects (see T49632) + * TODO: perform self snap in gpencil_strokes. + * + * When we're moving the origins, allow snapping onto our own geometry (see T69132). */ + t->tsnap.modeSelect = SNAP_ALL; + } + else { + t->tsnap.modeSelect = SNAP_NOT_SELECTED; + } } else { /* Grid if snap is not possible */ @@ -993,18 +1045,25 @@ static void CalcSnapGeometry(TransInfo *t, float *UNUSED(vec)) float no[3]; float mval[2]; bool found = false; + short snap_elem = 0; float dist_px = SNAP_MIN_DISTANCE; // Use a user defined value here mval[0] = t->mval[0]; mval[1] = t->mval[1]; - if (t->tsnap.mode & (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE)) { + if (t->tsnap.mode & (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE | + SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR)) { zero_v3(no); /* objects won't set this */ - found = snapObjectsTransform(t, mval, &dist_px, loc, no); + snap_elem = snapObjectsTransform(t, mval, &dist_px, loc, no); + found = snap_elem != 0; } if ((found == false) && (t->tsnap.mode & SCE_SNAP_MODE_VOLUME)) { found = peelObjectsTransform( t, mval, (t->settings->snap_flag & SCE_SNAP_PEEL_OBJECT) != 0, loc, no, NULL); + + if (found) { + snap_elem = SCE_SNAP_MODE_VOLUME; + } } if (found == true) { @@ -1016,6 +1075,8 @@ static void CalcSnapGeometry(TransInfo *t, float *UNUSED(vec)) else { t->tsnap.status &= ~POINT_INIT; } + + t->tsnap.snapElem = (char)snap_elem; } else if (t->spacetype == SPACE_IMAGE && t->obedit_type == OB_MESH) { if (t->tsnap.mode & SCE_SNAP_MODE_VERTEX) { @@ -1186,7 +1247,11 @@ static void TargetSnapClosest(TransInfo *t) FOREACH_TRANS_DATA_CONTAINER (t, tc) { TransData *td = tc->data; for (td = tc->data, i = 0; i < tc->data_len && td->flag & TD_SELECTED; i++, td++) { - struct BoundBox *bb = BKE_object_boundbox_get(td->ob); + const BoundBox *bb = NULL; + + if ((t->options & CTX_OBMODE_XFORM_OBDATA) == 0) { + bb = BKE_object_boundbox_get(td->ob); + } /* use boundbox if possible */ if (bb) { @@ -1259,10 +1324,10 @@ static void TargetSnapClosest(TransInfo *t) } } -bool snapObjectsTransform( +short snapObjectsTransform( TransInfo *t, const float mval[2], float *dist_px, float r_loc[3], float r_no[3]) { - return ED_transform_snap_object_project_view3d( + return ED_transform_snap_object_project_view3d_ex( t->tsnap.object_context, t->scene->toolsettings->snap_mode, &(const struct SnapObjectParams){ @@ -1271,9 +1336,13 @@ bool snapObjectsTransform( .use_occlusion_test = t->scene->toolsettings->snap_mode != SCE_SNAP_MODE_FACE, }, mval, + t->tsnap.snapTarget, dist_px, r_loc, - r_no); + r_no, + NULL, + NULL, + NULL); } /******************** PEELING *********************************/ diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c index 67bd107ab49..d3cf52ce3fd 100644 --- a/source/blender/editors/transform/transform_snap_object.c +++ b/source/blender/editors/transform/transform_snap_object.c @@ -221,22 +221,40 @@ static void iter_snap_objects(SnapObjectContext *sctx, Base *base_act = view_layer->basact; for (Base *base = view_layer->object_bases.first; base != NULL; base = base->next) { - if ((BASE_VISIBLE(v3d, base)) && (base->flag_legacy & BA_SNAP_FIX_DEPS_FIASCO) == 0 && - !((snap_select == SNAP_NOT_SELECTED && - ((base->flag & BASE_SELECTED) || (base->flag_legacy & BA_WAS_SEL))) || - (snap_select == SNAP_NOT_ACTIVE && base == base_act))) { - Object *obj_eval = DEG_get_evaluated_object(sctx->depsgraph, base->object); - if (obj_eval->transflag & OB_DUPLI) { - DupliObject *dupli_ob; - ListBase *lb = object_duplilist(sctx->depsgraph, sctx->scene, obj_eval); - for (dupli_ob = lb->first; dupli_ob; dupli_ob = dupli_ob->next) { - sob_callback(sctx, use_object_edit_cage, dupli_ob->ob, dupli_ob->mat, data); - } - free_object_duplilist(lb); + + if (!BASE_VISIBLE(v3d, base)) { + continue; + } + + if (base->flag_legacy & BA_TRANSFORM_LOCKED_IN_PLACE) { + /* pass */ + } + else if (base->flag_legacy & BA_SNAP_FIX_DEPS_FIASCO) { + continue; + } + + if (snap_select == SNAP_NOT_SELECTED) { + if ((base->flag & BASE_SELECTED) || (base->flag_legacy & BA_WAS_SEL)) { + continue; + } + } + else if (snap_select == SNAP_NOT_ACTIVE) { + if (base == base_act) { + continue; } + } - sob_callback(sctx, use_object_edit_cage, obj_eval, obj_eval->obmat, data); + Object *obj_eval = DEG_get_evaluated_object(sctx->depsgraph, base->object); + if (obj_eval->transflag & OB_DUPLI) { + DupliObject *dupli_ob; + ListBase *lb = object_duplilist(sctx->depsgraph, sctx->scene, obj_eval); + for (dupli_ob = lb->first; dupli_ob; dupli_ob = dupli_ob->next) { + sob_callback(sctx, use_object_edit_cage, dupli_ob->ob, dupli_ob->mat, data); + } + free_object_duplilist(lb); } + + sob_callback(sctx, use_object_edit_cage, obj_eval, obj_eval->obmat, data); } } @@ -583,37 +601,31 @@ static bool raycastEditMesh(SnapObjectContext *sctx, } if (treedata->tree == NULL) { - BVHCache **bvh_cache = NULL; - BLI_bitmap *elem_mask = NULL; - BMEditMesh *em_orig; - int looptri_num_active = -1; - /* Get original version of the edit_mesh. */ - em_orig = BKE_editmesh_from_object(DEG_get_original_object(ob)); + BMEditMesh *em_orig = BKE_editmesh_from_object(DEG_get_original_object(ob)); if (sctx->callbacks.edit_mesh.test_face_fn) { BMesh *bm = em_orig->bm; BLI_assert(poly_to_tri_count(bm->totface, bm->totloop) == em_orig->tottri); - elem_mask = BLI_BITMAP_NEW(em_orig->tottri, __func__); - looptri_num_active = BM_iter_mesh_bitmap_from_filter_tessface( + BLI_bitmap *elem_mask = BLI_BITMAP_NEW(em_orig->tottri, __func__); + int looptri_num_active = BM_iter_mesh_bitmap_from_filter_tessface( bm, elem_mask, sctx->callbacks.edit_mesh.test_face_fn, sctx->callbacks.edit_mesh.user_data); + + bvhtree_from_editmesh_looptri_ex( + treedata, em_orig, elem_mask, looptri_num_active, 0.0f, 4, 6, 0, NULL); + + MEM_freeN(elem_mask); } else { /* Only cache if bvhtree is created without a mask. * This helps keep a standardized bvhtree in cache. */ - bvh_cache = em_bvh_cache; + BKE_bvhtree_from_editmesh_get(treedata, em_orig, 4, BVHTREE_FROM_EM_LOOPTRI, em_bvh_cache); } - bvhtree_from_editmesh_looptri_ex( - treedata, em_orig, elem_mask, looptri_num_active, 0.0f, 4, 6, bvh_cache); - - if (elem_mask) { - MEM_freeN(elem_mask); - } if (treedata->tree == NULL) { return retval; } @@ -1260,7 +1272,8 @@ static short snap_mesh_polygon(SnapObjectContext *sctx, const MPoly *mp = &((SnapObjectData_Mesh *)sod)->poly[*r_index]; const MLoop *ml = &treedata->loop[mp->loopstart]; - if (snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE) { + if (snapdata->snap_to_flag & + (SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR)) { elem = SCE_SNAP_MODE_EDGE; BLI_assert(treedata->edge != NULL); for (int i = mp->totloop; i--; ml++) { @@ -1297,7 +1310,8 @@ static short snap_mesh_polygon(SnapObjectContext *sctx, BMFace *f = BM_face_at_index(em->bm, *r_index); BMLoop *l_iter, *l_first; l_iter = l_first = BM_FACE_FIRST_LOOP(f); - if (snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE) { + if (snapdata->snap_to_flag & + (SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR)) { elem = SCE_SNAP_MODE_EDGE; BM_mesh_elem_index_ensure(em->bm, BM_EDGE); BM_mesh_elem_table_ensure(em->bm, BM_VERT | BM_EDGE); @@ -1352,6 +1366,7 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx, Object *ob, float obmat[4][4], float original_dist_px, + const float prev_co[3], /* read/write args */ float *dist_px, /* return args */ @@ -1420,19 +1435,74 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx, &lambda)) { /* do nothing */ } - else if (lambda < 0.25f || 0.75f < lambda) { - int v_id = lambda < 0.5f ? 0 : 1; + else { + if (snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX) { + if (lambda < 0.25f || 0.75f < lambda) { + int v_id = lambda < 0.5f ? 0 : 1; - if (test_projected_vert_dist(&neasrest_precalc, - NULL, - 0, - nearest2d.is_persp, - v_pair[v_id], - &nearest.dist_sq, - nearest.co)) { - nearest.index = vindex[v_id]; - nearest2d.copy_vert_no(vindex[v_id], nearest.no, nearest2d.userdata); - elem = SCE_SNAP_MODE_VERTEX; + if (test_projected_vert_dist(&neasrest_precalc, + NULL, + 0, + nearest2d.is_persp, + v_pair[v_id], + &nearest.dist_sq, + nearest.co)) { + nearest.index = vindex[v_id]; + nearest2d.copy_vert_no(vindex[v_id], nearest.no, nearest2d.userdata); + elem = SCE_SNAP_MODE_VERTEX; + } + } + } + + if (snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE_MIDPOINT) { + if (0.375f < lambda && lambda < 0.625f) { + float vmid[3]; + mid_v3_v3v3(vmid, v_pair[0], v_pair[1]); + + if (test_projected_vert_dist(&neasrest_precalc, + NULL, + 0, + nearest2d.is_persp, + vmid, + &nearest.dist_sq, + nearest.co)) { + float v_nor[2][3]; + nearest2d.copy_vert_no(vindex[0], v_nor[0], nearest2d.userdata); + nearest2d.copy_vert_no(vindex[1], v_nor[1], nearest2d.userdata); + mid_v3_v3v3(nearest.no, v_nor[0], v_nor[1]); + nearest.index = *r_index; + elem = SCE_SNAP_MODE_EDGE_MIDPOINT; + } + } + } + + if (prev_co && (snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE_PERPENDICULAR)) { + float v_near[3], va_g[3], vb_g[3]; + + mul_v3_m4v3(va_g, obmat, v_pair[0]); + mul_v3_m4v3(vb_g, obmat, v_pair[1]); + lambda = line_point_factor_v3(prev_co, va_g, vb_g); + + if (IN_RANGE(lambda, 0.0f, 1.0f)) { + interp_v3_v3v3(v_near, va_g, vb_g, lambda); + + if ((len_squared_v3v3(prev_co, v_near) > FLT_EPSILON) && + test_projected_vert_dist(&neasrest_precalc, + NULL, + 0, + nearest2d.is_persp, + v_near, + &nearest.dist_sq, + nearest.co)) { + float v_nor[2][3]; + nearest2d.copy_vert_no(vindex[0], v_nor[0], nearest2d.userdata); + nearest2d.copy_vert_no(vindex[1], v_nor[1], nearest2d.userdata); + mid_v3_v3v3(nearest.no, v_nor[0], v_nor[1]); + + nearest.index = *r_index; + elem = SCE_SNAP_MODE_EDGE_PERPENDICULAR; + } + } } } @@ -1440,7 +1510,9 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx, *dist_px = sqrtf(nearest.dist_sq); copy_v3_v3(r_loc, nearest.co); - mul_m4_v3(obmat, r_loc); + if (elem != SCE_SNAP_MODE_EDGE_PERPENDICULAR) { + mul_m4_v3(obmat, r_loc); + } if (r_no) { float imat[4][4]; @@ -1926,13 +1998,13 @@ static short snapMesh(SnapObjectContext *sctx, { BLI_assert(snapdata->snap_to_flag != SCE_SNAP_MODE_FACE); - if ((snapdata->snap_to_flag & ~SCE_SNAP_MODE_FACE) == SCE_SNAP_MODE_EDGE) { - if (me->totedge == 0) { + if ((snapdata->snap_to_flag & ~SCE_SNAP_MODE_FACE) == SCE_SNAP_MODE_VERTEX) { + if (me->totvert == 0) { return 0; } } else { - if (me->totvert == 0) { + if (me->totedge == 0) { return 0; } } @@ -2066,7 +2138,8 @@ static short snapMesh(SnapObjectContext *sctx, last_index = nearest.index; } - if (snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE) { + if (snapdata->snap_to_flag & + (SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR)) { if (bvhtree[0]) { /* snap to loose edges */ BLI_bvhtree_find_nearest_projected(bvhtree[0], @@ -2164,13 +2237,13 @@ static short snapEditMesh(SnapObjectContext *sctx, { BLI_assert(snapdata->snap_to_flag != SCE_SNAP_MODE_FACE); - if ((snapdata->snap_to_flag & ~SCE_SNAP_MODE_FACE) == SCE_SNAP_MODE_EDGE) { - if (em->bm->totedge == 0) { + if ((snapdata->snap_to_flag & ~SCE_SNAP_MODE_FACE) == SCE_SNAP_MODE_VERTEX) { + if (em->bm->totvert == 0) { return 0; } } else { - if (em->bm->totvert == 0) { + if (em->bm->totedge == 0) { return 0; } } @@ -2223,16 +2296,17 @@ static short snapEditMesh(SnapObjectContext *sctx, sctx->callbacks.edit_mesh.user_data); bvhtree_from_editmesh_verts_ex( - treedata_vert, em, verts_mask, verts_num_active, 0.0f, 2, 6); + treedata_vert, em, verts_mask, verts_num_active, 0.0f, 2, 6, 0, NULL); MEM_freeN(verts_mask); } else { - bvhtree_from_editmesh_verts(treedata_vert, em, 0.0f, 2, 6, em_bvh_cache); + BKE_bvhtree_from_editmesh_get(treedata_vert, em, 2, BVHTREE_FROM_EM_VERTS, em_bvh_cache); } } } - if (snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE) { + if (snapdata->snap_to_flag & + (SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR)) { if (sod->bvh_trees[1] == NULL) { sod->bvh_trees[1] = BLI_memarena_calloc(sctx->cache.mem_arena, sizeof(**sod->bvh_trees)); } @@ -2258,11 +2332,11 @@ static short snapEditMesh(SnapObjectContext *sctx, sctx->callbacks.edit_mesh.user_data); bvhtree_from_editmesh_edges_ex( - treedata_edge, em, edges_mask, edges_num_active, 0.0f, 2, 6); + treedata_edge, em, edges_mask, edges_num_active, 0.0f, 2, 6, 0, NULL); MEM_freeN(edges_mask); } else { - bvhtree_from_editmesh_edges(treedata_edge, em, 0.0f, 2, 6, em_bvh_cache); + BKE_bvhtree_from_editmesh_get(treedata_edge, em, 2, BVHTREE_FROM_EM_EDGES, em_bvh_cache); } } } @@ -2279,7 +2353,6 @@ static short snapEditMesh(SnapObjectContext *sctx, .index = -1, .dist_sq = dist_px_sq, }; - int last_index = nearest.index; short elem = SCE_SNAP_MODE_VERTEX; float tobmat[4][4], clip_planes_local[MAX_CLIPPLANE_LEN][4]; @@ -2300,11 +2373,12 @@ static short snapEditMesh(SnapObjectContext *sctx, &nearest, cb_snap_vert, &nearest2d); - - last_index = nearest.index; } - if (treedata_edge && snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE) { + if (treedata_edge && snapdata->snap_to_flag & (SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_EDGE_MIDPOINT | + SCE_SNAP_MODE_EDGE_PERPENDICULAR)) { + int last_index = nearest.index; + nearest.index = -1; BM_mesh_elem_table_ensure(em->bm, BM_EDGE | BM_VERT); BLI_bvhtree_find_nearest_projected(treedata_edge->tree, lpmat, @@ -2316,9 +2390,12 @@ static short snapEditMesh(SnapObjectContext *sctx, cb_snap_edge, &nearest2d); - if (last_index != nearest.index) { + if (nearest.index != -1) { elem = SCE_SNAP_MODE_EDGE; } + else { + nearest.index = last_index; + } } if (nearest.index != -1) { @@ -2700,6 +2777,7 @@ static short transform_snap_context_project_view3d_mixed_impl( const unsigned short snap_to_flag, const struct SnapObjectParams *params, const float mval[2], + const float prev_co[3], float *dist_px, float r_loc[3], float r_no[3], @@ -2707,7 +2785,8 @@ static short transform_snap_context_project_view3d_mixed_impl( Object **r_ob, float r_obmat[4][4]) { - BLI_assert((snap_to_flag & (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE)) != + BLI_assert((snap_to_flag & (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE | + SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR)) != 0); short retval = 0; @@ -2733,7 +2812,7 @@ static short transform_snap_context_project_view3d_mixed_impl( ray_normal, ray_start, true)) { - return false; + return 0; } float dummy_ray_depth = BVH_RAYCAST_DIST_MAX; @@ -2746,7 +2825,8 @@ static short transform_snap_context_project_view3d_mixed_impl( } } - if (snap_to_flag & (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE)) { + if (snap_to_flag & (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_EDGE_MIDPOINT | + SCE_SNAP_MODE_EDGE_PERPENDICULAR)) { short elem; float dist_px_tmp = *dist_px; @@ -2799,11 +2879,18 @@ static short transform_snap_context_project_view3d_mixed_impl( retval = elem; } - if ((retval == SCE_SNAP_MODE_EDGE) && (snapdata.snap_to_flag & SCE_SNAP_MODE_VERTEX)) { - retval = snap_mesh_edge_verts_mixed( - sctx, &snapdata, ob, obmat, *dist_px, &dist_px_tmp, loc, no, &index); + if ((retval == SCE_SNAP_MODE_EDGE) && + (snap_to_flag & (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE_MIDPOINT | + SCE_SNAP_MODE_EDGE_PERPENDICULAR))) { + elem = snap_mesh_edge_verts_mixed( + sctx, &snapdata, ob, obmat, *dist_px, prev_co, &dist_px_tmp, loc, no, &index); + } + + if (elem) { + retval = elem; } + retval &= snap_to_flag; *dist_px = dist_px_tmp; } @@ -2831,6 +2918,7 @@ short ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx, const unsigned short snap_to, const struct SnapObjectParams *params, const float mval[2], + const float prev_co[3], float *dist_px, float r_loc[3], float r_no[3], @@ -2839,7 +2927,7 @@ short ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx, float r_obmat[4][4]) { return transform_snap_context_project_view3d_mixed_impl( - sctx, snap_to, params, mval, dist_px, r_loc, r_no, r_index, r_ob, r_obmat) != 0; + sctx, snap_to, params, mval, prev_co, dist_px, r_loc, r_no, r_index, r_ob, r_obmat); } /** @@ -2849,6 +2937,7 @@ short ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx, * * \param sctx: Snap context. * \param mval: Screenspace coordinate. + * \param prev_co: Coordinate for perpendicular point calculation (optional). * \param dist_px: Maximum distance to snap (in pixels). * \param r_co: hit location. * \param r_no: hit normal (optional). @@ -2858,12 +2947,13 @@ bool ED_transform_snap_object_project_view3d(SnapObjectContext *sctx, const unsigned short snap_to, const struct SnapObjectParams *params, const float mval[2], + const float prev_co[3], float *dist_px, float r_loc[3], float r_no[3]) { return ED_transform_snap_object_project_view3d_ex( - sctx, snap_to, params, mval, dist_px, r_loc, r_no, NULL, NULL, NULL) != 0; + sctx, snap_to, params, mval, prev_co, dist_px, r_loc, r_no, NULL, NULL, NULL) != 0; } /** diff --git a/source/blender/editors/undo/ed_undo.c b/source/blender/editors/undo/ed_undo.c index c84d7539cce..e0f4d1cf465 100644 --- a/source/blender/editors/undo/ed_undo.c +++ b/source/blender/editors/undo/ed_undo.c @@ -53,6 +53,7 @@ #include "ED_gpencil.h" #include "ED_render.h" #include "ED_object.h" +#include "ED_outliner.h" #include "ED_screen.h" #include "ED_undo.h" @@ -390,6 +391,8 @@ static int ed_undo_exec(bContext *C, wmOperator *op) /* Keep button under the cursor active. */ WM_event_add_mousemove(C); } + + ED_outliner_select_sync_from_all_tag(C); return ret; } @@ -417,6 +420,8 @@ static int ed_redo_exec(bContext *C, wmOperator *op) /* Keep button under the cursor active. */ WM_event_add_mousemove(C); } + + ED_outliner_select_sync_from_all_tag(C); return ret; } diff --git a/source/blender/editors/uvedit/uvedit_draw.c b/source/blender/editors/uvedit/uvedit_draw.c index d97714061c0..fe761f65702 100644 --- a/source/blender/editors/uvedit/uvedit_draw.c +++ b/source/blender/editors/uvedit/uvedit_draw.c @@ -205,14 +205,14 @@ static void draw_uvs_shadow(SpaceImage *UNUSED(sima), Object *obedit, Depsgraph *depsgraph) { - Object *eval_ob = DEG_get_evaluated_object(depsgraph, obedit); - Mesh *me = eval_ob->data; + Object *ob_eval = DEG_get_evaluated_object(depsgraph, obedit); + Mesh *me = ob_eval->data; float col[4]; UI_GetThemeColor4fv(TH_UV_SHADOW, col); DRW_mesh_batch_cache_validate(me); GPUBatch *edges = DRW_mesh_batch_cache_get_uv_edges(me); - DRW_mesh_batch_cache_create_requested(eval_ob, me, scene, false, false); + DRW_mesh_batch_cache_create_requested(ob_eval, me, scene, false, false); if (edges) { GPU_batch_program_set_builtin(edges, GPU_SHADER_2D_UV_UNIFORM_COLOR); @@ -223,8 +223,8 @@ static void draw_uvs_shadow(SpaceImage *UNUSED(sima), static void draw_uvs_texpaint(Scene *scene, Object *ob, Depsgraph *depsgraph) { - Object *eval_ob = DEG_get_evaluated_object(depsgraph, ob); - Mesh *me = eval_ob->data; + Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); + Mesh *me = ob_eval->data; ToolSettings *ts = scene->toolsettings; float col[4]; UI_GetThemeColor4fv(TH_UV_SHADOW, col); @@ -235,7 +235,7 @@ static void draw_uvs_texpaint(Scene *scene, Object *ob, Depsgraph *depsgraph) DRW_mesh_batch_cache_validate(me); GPUBatch *geom = DRW_mesh_batch_cache_get_uv_edges(me); - DRW_mesh_batch_cache_create_requested(eval_ob, me, scene, false, false); + DRW_mesh_batch_cache_create_requested(ob_eval, me, scene, false, false); GPU_batch_program_set_builtin(geom, GPU_SHADER_2D_UV_UNIFORM_COLOR); GPU_batch_uniform_4fv(geom, "color", col); @@ -246,7 +246,7 @@ static void draw_uvs_texpaint(Scene *scene, Object *ob, Depsgraph *depsgraph) MPoly *mpoly = me->mpoly; uint draw_start = 0; uint idx = 0; - bool prev_ma_match = (mpoly->mat_nr == (eval_ob->actcol - 1)); + bool prev_ma_match = (mpoly->mat_nr == (ob_eval->actcol - 1)); GPU_matrix_bind(geom->interface); GPU_batch_bind(geom); @@ -255,7 +255,7 @@ static void draw_uvs_texpaint(Scene *scene, Object *ob, Depsgraph *depsgraph) * we can use multi draw indirect drawcalls for this. * (not implemented in GPU module at the time of writing). */ for (int a = 0; a < me->totpoly; a++, mpoly++) { - bool ma_match = (mpoly->mat_nr == (eval_ob->actcol - 1)); + bool ma_match = (mpoly->mat_nr == (ob_eval->actcol - 1)); if (ma_match != prev_ma_match) { if (ma_match == false) { GPU_batch_draw_advanced(geom, draw_start, idx - draw_start, 0, 0); @@ -282,13 +282,13 @@ static void draw_uvs_texpaint(Scene *scene, Object *ob, Depsgraph *depsgraph) static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit, Depsgraph *depsgraph) { GPUBatch *faces, *edges, *verts, *facedots; - Object *eval_ob = DEG_get_evaluated_object(depsgraph, obedit); + Object *ob_eval = DEG_get_evaluated_object(depsgraph, obedit); const ToolSettings *ts = scene->toolsettings; float col1[4], col2[4], col3[4], transparent[4] = {0.0f, 0.0f, 0.0f, 0.0f}; if (sima->flag & SI_DRAWSHADOW) { bool is_cage_like_final_meshes = false; - Mesh *me = (Mesh *)eval_ob->data; + Mesh *me = (Mesh *)ob_eval->data; BMEditMesh *embm = me->edit_mesh; is_cage_like_final_meshes = embm && embm->mesh_eval_final && embm->mesh_eval_final->runtime.is_original; @@ -300,7 +300,7 @@ static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit, Depsgraph * } } - uvedit_get_batches(eval_ob, sima, scene, &faces, &edges, &verts, &facedots); + uvedit_get_batches(ob_eval, sima, scene, &faces, &edges, &verts, &facedots); bool interpedges; bool draw_stretch = (sima->flag & SI_DRAW_STRETCH) != 0; @@ -367,33 +367,33 @@ static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit, Depsgraph * break; } case SI_UVDT_BLACK: - case SI_UVDT_WHITE: { - GPU_line_width(1.0f); - GPU_batch_program_set_builtin(edges, GPU_SHADER_2D_UNIFORM_COLOR); - if (sima->dt_uv == SI_UVDT_WHITE) { - GPU_batch_uniform_4f(edges, "color", 1.0f, 1.0f, 1.0f, 1.0f); - } - else { - GPU_batch_uniform_4f(edges, "color", 0.0f, 0.0f, 0.0f, 1.0f); - } - GPU_batch_draw(edges); - break; - } + case SI_UVDT_WHITE: case SI_UVDT_OUTLINE: { /* We could modify the vbo's data filling * instead of modifying the provoking vert. */ glProvokingVertex(GL_FIRST_VERTEX_CONVENTION); - UI_GetThemeColor4fv(TH_WIRE_EDIT, col1); UI_GetThemeColor4fv(TH_EDGE_SELECT, col2); GPU_batch_program_set_builtin( edges, (interpedges) ? GPU_SHADER_2D_UV_EDGES_SMOOTH : GPU_SHADER_2D_UV_EDGES); - /* Black Outline. */ - GPU_line_width(3.0f); - GPU_batch_uniform_4f(edges, "edgeColor", 0.0f, 0.0f, 0.0f, 1.0f); - GPU_batch_uniform_4f(edges, "selectColor", 0.0f, 0.0f, 0.0f, 1.0f); - GPU_batch_draw(edges); + + if (sima->dt_uv == SI_UVDT_OUTLINE) { + /* Black Outline. */ + GPU_line_width(3.0f); + GPU_batch_uniform_4f(edges, "edgeColor", 0.0f, 0.0f, 0.0f, 1.0f); + GPU_batch_uniform_4f(edges, "selectColor", 0.0f, 0.0f, 0.0f, 1.0f); + GPU_batch_draw(edges); + + UI_GetThemeColor4fv(TH_WIRE_EDIT, col1); + } + else if (sima->dt_uv == SI_UVDT_WHITE) { + copy_v4_fl4(col1, 1.0f, 1.0f, 1.0f, 1.0f); + } + else { + copy_v4_fl4(col1, 0.0f, 0.0f, 0.0f, 1.0f); + } + /* Inner Line. Use depth test to insure selection is drawn on top. */ GPU_depth_test(true); GPU_line_width(1.0f); diff --git a/source/blender/editors/uvedit/uvedit_intern.h b/source/blender/editors/uvedit/uvedit_intern.h index 3f544ad90d1..7bc6b048585 100644 --- a/source/blender/editors/uvedit/uvedit_intern.h +++ b/source/blender/editors/uvedit/uvedit_intern.h @@ -24,7 +24,6 @@ #ifndef __UVEDIT_INTERN_H__ #define __UVEDIT_INTERN_H__ -struct BMEditMesh; struct BMFace; struct BMLoop; struct Image; diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index dcf1d04ffb3..cd7e5f9ba09 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -138,7 +138,7 @@ static int UNUSED_FUNCTION(ED_operator_uvmap_mesh)(bContext *C) if (ob && ob->type == OB_MESH) { Mesh *me = ob->data; - if (CustomData_get_layer(&me->fdata, CD_MTFACE) != NULL) { + if (CustomData_get_layer(&me->ldata, CD_MLOOPUV) != NULL) { return 1; } } diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c index a42a6eba3ff..2d8f40ea5af 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c @@ -524,7 +524,7 @@ static ParamHandle *construct_param_handle_subsurfed(Scene *scene, smd.levels = smd_real->levels; smd.subdivType = smd_real->subdivType; - initialDerived = CDDM_from_editbmesh(em, false, false); + initialDerived = CDDM_from_editbmesh(em, false); derivedMesh = subsurf_make_derived_from_derived( initialDerived, &smd, scene, NULL, SUBSURF_IN_EDIT_MODE); |