diff options
Diffstat (limited to 'source/blender/editors')
328 files changed, 12385 insertions, 7404 deletions
diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c index c2d517588b2..9eeabf2d05e 100644 --- a/source/blender/editors/animation/anim_channels_defines.c +++ b/source/blender/editors/animation/anim_channels_defines.c @@ -4351,15 +4351,15 @@ void ANIM_channel_setting_set(bAnimContext *ac, /* --------------------------- */ -/* size of icons */ +/** Size of icons. */ #define ICON_WIDTH (0.85f * U.widget_unit) -/* width of sliders */ +/** Width of sliders. */ #define SLIDER_WIDTH (4 * U.widget_unit) -/* min-width of rename textboxes */ +/** Min-width of rename text-boxes. */ #define RENAME_TEXT_MIN_WIDTH (U.widget_unit) -/* width of graph editor color bands */ +/** Width of graph editor color bands. */ #define GRAPH_COLOR_BAND_WIDTH (0.3f * U.widget_unit) -/* extra offset for the visibility icons in the graph editor */ +/** Extra offset for the visibility icons in the graph editor. */ #define GRAPH_ICON_VISIBILITY_OFFSET (GRAPH_COLOR_BAND_WIDTH * 1.5f) /* Helper - Check if a channel needs renaming */ diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c index f148bb5b77d..b223a1493fd 100644 --- a/source/blender/editors/animation/anim_channels_edit.c +++ b/source/blender/editors/animation/anim_channels_edit.c @@ -711,14 +711,14 @@ static bool animedit_poll_channels_active(bContext *C) /* channels region test */ /* TODO: could enhance with actually testing if channels region? */ if (ELEM(NULL, area, CTX_wm_region(C))) { - return 0; + return false; } /* animation editor test */ if (ELEM(area->spacetype, SPACE_ACTION, SPACE_GRAPH, SPACE_NLA) == 0) { - return 0; + return false; } - return 1; + return true; } /* Poll callback for Animation Editor channels list region + not in NLA-tweak-mode for NLA. */ @@ -730,21 +730,21 @@ static bool animedit_poll_channels_nla_tweakmode_off(bContext *C) /* channels region test */ /* TODO: could enhance with actually testing if channels region? */ if (ELEM(NULL, area, CTX_wm_region(C))) { - return 0; + return false; } /* animation editor test */ if (ELEM(area->spacetype, SPACE_ACTION, SPACE_GRAPH, SPACE_NLA) == 0) { - return 0; + return false; } /* NLA tweak-mode test. */ if (area->spacetype == SPACE_NLA) { if ((scene == NULL) || (scene->flag & SCE_NLA_EDIT_ON)) { - return 0; + return false; } } - return 1; + return true; } /* ****************** Rearrange Channels Operator ******************* */ @@ -791,7 +791,7 @@ static bool rearrange_island_ok(tReorderChannelIsland *island) { /* island must not be untouchable */ if (island->flag & REORDER_ISLAND_UNTOUCHABLE) { - return 0; + return false; } /* island should be selected to be moved */ @@ -809,10 +809,10 @@ static bool rearrange_island_top(ListBase *list, tReorderChannelIsland *island) /* make it first element */ BLI_insertlinkbefore(list, list->first, island); - return 1; + return true; } - return 0; + return false; } static bool rearrange_island_up(ListBase *list, tReorderChannelIsland *island) @@ -833,11 +833,11 @@ static bool rearrange_island_up(ListBase *list, tReorderChannelIsland *island) /* push it up */ BLI_insertlinkbefore(list, prev, island); - return 1; + return true; } } - return 0; + return false; } static bool rearrange_island_down(ListBase *list, tReorderChannelIsland *island) @@ -1083,7 +1083,7 @@ static bool rearrange_animchannel_islands(ListBase *list, /* don't waste effort on an empty list */ if (BLI_listbase_is_empty(list)) { - return 0; + return false; } /* group channels into islands */ @@ -1589,7 +1589,7 @@ static bool animchannels_grouping_poll(bContext *C) /* channels region test */ /* TODO: could enhance with actually testing if channels region? */ if (ELEM(NULL, area, CTX_wm_region(C))) { - return 0; + return false; } /* animation editor test - must be suitable modes only */ @@ -1602,7 +1602,7 @@ static bool animchannels_grouping_poll(bContext *C) /* dopesheet and action only - all others are for other datatypes or have no groups */ if (ELEM(saction->mode, SACTCONT_ACTION, SACTCONT_DOPESHEET) == 0) { - return 0; + return false; } break; @@ -1612,17 +1612,17 @@ static bool animchannels_grouping_poll(bContext *C) /* drivers can't have groups... */ if (sipo->mode != SIPO_MODE_ANIMATION) { - return 0; + return false; } break; } /* unsupported... */ default: - return 0; + return false; } - return 1; + return true; } /* ----------------------------------------------------------- */ @@ -2428,15 +2428,15 @@ static bool animchannels_enable_poll(bContext *C) /* channels region test */ /* TODO: could enhance with actually testing if channels region? */ if (ELEM(NULL, area, CTX_wm_region(C))) { - return 0; + return false; } /* animation editor test - Action/Dopesheet/etc. and Graph only */ if (ELEM(area->spacetype, SPACE_ACTION, SPACE_GRAPH) == 0) { - return 0; + return false; } - return 1; + return true; } static int animchannels_enable_exec(bContext *C, wmOperator *UNUSED(op)) @@ -2504,7 +2504,7 @@ static bool animchannels_select_filter_poll(bContext *C) ScrArea *area = CTX_wm_area(C); if (area == NULL) { - return 0; + return false; } /* animation editor with dopesheet */ @@ -2791,13 +2791,35 @@ static bool rename_anim_channels(bAnimContext *ac, int channel_index) return false; } - /* don't allow renaming linked channels */ - if ((ale->fcurve_owner_id != NULL && - (ID_IS_LINKED(ale->fcurve_owner_id) || ID_IS_OVERRIDE_LIBRARY(ale->fcurve_owner_id))) || - (ale->id != NULL && (ID_IS_LINKED(ale->id) || ID_IS_OVERRIDE_LIBRARY(ale->id)))) { + /* Don't allow renaming linked/liboverride channels. */ + if (ale->fcurve_owner_id != NULL && + (ID_IS_LINKED(ale->fcurve_owner_id) || ID_IS_OVERRIDE_LIBRARY(ale->fcurve_owner_id))) { ANIM_animdata_freelist(&anim_data); return false; } + if (ale->id != NULL) { + if (ID_IS_LINKED(ale->id)) { + ANIM_animdata_freelist(&anim_data); + return false; + } + /* There is one exception to not allowing renaming on liboverride channels: locally-inserted + * NLA tracks. */ + if (ID_IS_OVERRIDE_LIBRARY(ale->id)) { + switch (ale->type) { + case ANIMTYPE_NLATRACK: { + NlaTrack *nlt = (NlaTrack *)ale->data; + if ((nlt->flag & NLATRACK_OVERRIDELIBRARY_LOCAL) == 0) { + ANIM_animdata_freelist(&anim_data); + return false; + } + break; + } + default: + ANIM_animdata_freelist(&anim_data); + return false; + } + } + } /* check that channel can be renamed */ acf = ANIM_channel_get_typeinfo(ale); diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c index 1a3ab100768..ad06b185132 100644 --- a/source/blender/editors/animation/anim_markers.c +++ b/source/blender/editors/animation/anim_markers.c @@ -543,6 +543,8 @@ void ED_markers_draw(const bContext *C, int flag) View2D *v2d = UI_view2d_fromcontext(C); int cfra = CTX_data_scene(C)->r.cfra; + GPU_line_width(1.0f); + rctf markers_region_rect; get_marker_region_rect(v2d, &markers_region_rect); diff --git a/source/blender/editors/animation/anim_ops.c b/source/blender/editors/animation/anim_ops.c index 5b0c5eac11b..84f99ec0ac0 100644 --- a/source/blender/editors/animation/anim_ops.c +++ b/source/blender/editors/animation/anim_ops.c @@ -38,7 +38,6 @@ #include "SEQ_iterator.h" #include "SEQ_sequencer.h" #include "SEQ_time.h" -#include "SEQ_transform.h" #include "anim_intern.h" @@ -112,9 +111,9 @@ static int seq_frame_apply_snap(bContext *C, Scene *scene, const int timeline_fr Sequence *seq; SEQ_ITERATOR_FOREACH (seq, strips) { seq_frame_snap_update_best( - SEQ_transform_get_left_handle_frame(seq), timeline_frame, &best_frame, &best_distance); + SEQ_time_left_handle_frame_get(seq), timeline_frame, &best_frame, &best_distance); seq_frame_snap_update_best( - SEQ_transform_get_right_handle_frame(seq), timeline_frame, &best_frame, &best_distance); + SEQ_time_right_handle_frame_get(seq), timeline_frame, &best_frame, &best_distance); } SEQ_collection_free(strips); diff --git a/source/blender/editors/animation/drivers.c b/source/blender/editors/animation/drivers.c index 1b759f5dfa2..c6f68228807 100644 --- a/source/blender/editors/animation/drivers.c +++ b/source/blender/editors/animation/drivers.c @@ -180,7 +180,7 @@ static int add_driver_with_target(ReportList *UNUSED(reports), } else if ((RNA_property_unit(src_prop) == PROP_UNIT_ROTATION) && (RNA_property_unit(dst_prop) != PROP_UNIT_ROTATION)) { - /* Rotation Source: radians -> normal, so convert src to degrees + /* Rotation Source: radians -> normal, so convert src to degrees * (However, if both input and output is a rotation, don't apply such corrections) */ BLI_strncpy(driver->expression, "degrees(var)", sizeof(driver->expression)); diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c index 58d093c678d..786204a52ed 100644 --- a/source/blender/editors/animation/keyframes_draw.c +++ b/source/blender/editors/animation/keyframes_draw.c @@ -168,11 +168,11 @@ void draw_keyframe_shape(float x, /* Common attributes shared between the draw calls. */ typedef struct DrawKeylistUIData { float alpha; - float icon_sz; - float half_icon_sz; - float smaller_sz; - float ipo_sz; - float gpencil_sz; + float icon_size; + float half_icon_size; + float smaller_size; + float ipo_size; + float gpencil_size; float screenspace_margin; float sel_color[4]; float unsel_color[4]; @@ -195,11 +195,11 @@ static void draw_keylist_ui_data_init(DrawKeylistUIData *ctx, /* TODO: allow this opacity factor to be themed? */ ctx->alpha = channel_locked ? 0.25f : 1.0f; - ctx->icon_sz = U.widget_unit * 0.5f * yscale_fac; - ctx->half_icon_sz = 0.5f * ctx->icon_sz; - ctx->smaller_sz = 0.35f * ctx->icon_sz; - ctx->ipo_sz = 0.1f * ctx->icon_sz; - ctx->gpencil_sz = ctx->smaller_sz * 0.8f; + ctx->icon_size = U.widget_unit * 0.5f * yscale_fac; + ctx->half_icon_size = 0.5f * ctx->icon_size; + ctx->smaller_size = 0.35f * ctx->icon_size; + ctx->ipo_size = 0.1f * ctx->icon_size; + ctx->gpencil_size = ctx->smaller_size * 0.8f; ctx->screenspace_margin = (0.35f * (float)UI_UNIT_X) / UI_view2d_scale_get_x(v2d); ctx->show_ipo = (saction_flag & SACTION_SHOW_INTERPOLATION) != 0; @@ -242,8 +242,8 @@ static void draw_keylist_block_gpencil(const DrawKeylistUIData *ctx, &(const rctf){ .xmin = ab->cfra, .xmax = min_ff(ab->next->cfra - (ctx->screenspace_margin * size), ab->next->cfra), - .ymin = ypos - ctx->gpencil_sz, - .ymax = ypos + ctx->gpencil_sz, + .ymin = ypos - ctx->gpencil_size, + .ymax = ypos + ctx->gpencil_size, }, true, 0.25f * (float)UI_UNIT_X, @@ -259,8 +259,8 @@ static void draw_keylist_block_moving_hold(const DrawKeylistUIData *ctx, &(const rctf){ .xmin = ab->cfra, .xmax = ab->next->cfra, - .ymin = ypos - ctx->smaller_sz, - .ymax = ypos + ctx->smaller_sz, + .ymin = ypos - ctx->smaller_size, + .ymax = ypos + ctx->smaller_size, }, true, 3.0f, @@ -275,8 +275,8 @@ static void draw_keylist_block_standard(const DrawKeylistUIData *ctx, &(const rctf){ .xmin = ab->cfra, .xmax = ab->next->cfra, - .ymin = ypos - ctx->half_icon_sz, - .ymax = ypos + ctx->half_icon_sz, + .ymin = ypos - ctx->half_icon_size, + .ymax = ypos + ctx->half_icon_size, }, true, 3.0f, @@ -291,8 +291,8 @@ static void draw_keylist_block_interpolation_line(const DrawKeylistUIData *ctx, &(const rctf){ .xmin = ab->cfra, .xmax = ab->next->cfra, - .ymin = ypos - ctx->ipo_sz, - .ymax = ypos + ctx->ipo_sz, + .ymin = ypos - ctx->ipo_size, + .ymax = ypos + ctx->ipo_size, }, true, 3.0f, @@ -367,7 +367,7 @@ static void draw_keylist_keys(const DrawKeylistUIData *ctx, draw_keyframe_shape(ak->cfra, ypos, - ctx->icon_sz, + ctx->icon_size, (ak->sel & SELECT), ak->key_type, KEYFRAME_SHAPE_BOTH, diff --git a/source/blender/editors/animation/keyframes_keylist.cc b/source/blender/editors/animation/keyframes_keylist.cc index 3356ef4d47d..8dc598e6e2d 100644 --- a/source/blender/editors/animation/keyframes_keylist.cc +++ b/source/blender/editors/animation/keyframes_keylist.cc @@ -132,7 +132,7 @@ static void ED_keylist_runtime_init_listbase(AnimKeylist *keylist) return; } - keylist->runtime.list_wrapper.first = &keylist->runtime.key_columns[0]; + keylist->runtime.list_wrapper.first = keylist->runtime.key_columns.data(); keylist->runtime.list_wrapper.last = &keylist->runtime.key_columns[keylist->column_len - 1]; } @@ -309,7 +309,7 @@ static void keylist_first_last(const struct AnimKeylist *keylist, const struct ActKeyColumn **last_column) { if (keylist->is_runtime_initialized) { - *first_column = &keylist->runtime.key_columns[0]; + *first_column = keylist->runtime.key_columns.data(); *last_column = &keylist->runtime.key_columns[keylist->column_len - 1]; } else { diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index 14a3b958ea6..aa99a4e50c3 100644 --- a/source/blender/editors/animation/keyframing.c +++ b/source/blender/editors/animation/keyframing.c @@ -2051,6 +2051,8 @@ void ANIM_OT_keyframe_insert_by_name(wmOperatorType *ot) /* keyingset to use (idname) */ prop = RNA_def_string( ot->srna, "type", NULL, MAX_ID_NAME - 2, "Keying Set", "The Keying Set to use"); + RNA_def_property_string_search_func_runtime( + prop, ANIM_keyingset_visit_for_search_no_poll, PROP_STRING_SEARCH_SUGGESTION); RNA_def_property_flag(prop, PROP_HIDDEN); ot->prop = prop; } @@ -2246,6 +2248,8 @@ void ANIM_OT_keyframe_delete_by_name(wmOperatorType *ot) /* keyingset to use (idname) */ prop = RNA_def_string( ot->srna, "type", NULL, MAX_ID_NAME - 2, "Keying Set", "The Keying Set to use"); + RNA_def_property_string_search_func_runtime( + prop, ANIM_keyingset_visit_for_search_no_poll, PROP_STRING_SEARCH_SUGGESTION); RNA_def_property_flag(prop, PROP_HIDDEN); ot->prop = prop; } @@ -3090,8 +3094,13 @@ bool ED_autokeyframe_pchan( return false; } -bool ED_autokeyframe_property( - bContext *C, Scene *scene, PointerRNA *ptr, PropertyRNA *prop, int rnaindex, float cfra) +bool ED_autokeyframe_property(bContext *C, + Scene *scene, + PointerRNA *ptr, + PropertyRNA *prop, + int rnaindex, + float cfra, + const bool only_if_property_keyed) { Main *bmain = CTX_data_main(C); Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); @@ -3110,7 +3119,9 @@ bool ED_autokeyframe_property( fcu = BKE_fcurve_find_by_rna_context_ui( C, ptr, prop, rnaindex_check, NULL, &action, &driven, &special); - if (fcu == NULL) { + /* Only early out when we actually want an existing F-curve already + * (e.g. auto-keyframing from buttons). */ + if (fcu == NULL && (driven || special || only_if_property_keyed)) { return changed; } @@ -3146,23 +3157,28 @@ bool ED_autokeyframe_property( ReportList *reports = CTX_wm_reports(C); ToolSettings *ts = scene->toolsettings; const eInsertKeyFlags flag = ANIM_get_keyframing_flags(scene, true); + char *path = RNA_path_from_ID_to_property(ptr, prop); - /* NOTE: We use rnaindex instead of fcu->array_index, - * because a button may control all items of an array at once. - * E.g., color wheels (see T42567). */ - BLI_assert((fcu->array_index == rnaindex) || (rnaindex == -1)); + if (only_if_property_keyed) { + /* NOTE: We use rnaindex instead of fcu->array_index, + * because a button may control all items of an array at once. + * E.g., color wheels (see T42567). */ + BLI_assert((fcu->array_index == rnaindex) || (rnaindex == -1)); + } changed = insert_keyframe(bmain, reports, id, action, - ((fcu->grp) ? (fcu->grp->name) : (NULL)), - fcu->rna_path, + (fcu && fcu->grp) ? fcu->grp->name : NULL, + fcu ? fcu->rna_path : path, rnaindex, &anim_eval_context, ts->keyframe_type, NULL, flag) != 0; - + if (path) { + MEM_freeN(path); + } WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); } } diff --git a/source/blender/editors/animation/keyingsets.c b/source/blender/editors/animation/keyingsets.c index 6fcdd21bad8..97b81277008 100644 --- a/source/blender/editors/animation/keyingsets.c +++ b/source/blender/editors/animation/keyingsets.c @@ -708,6 +708,72 @@ KeyingSet *ANIM_get_keyingset_for_autokeying(const Scene *scene, const char *tra return ANIM_builtin_keyingset_get_named(NULL, transformKSName); } +static void anim_keyingset_visit_for_search_impl(const bContext *C, + StringPropertySearchVisitFunc visit_fn, + void *visit_user_data, + const bool use_poll) +{ + /* Poll requires context. */ + if (use_poll && (C == NULL)) { + return; + } + + Scene *scene = C ? CTX_data_scene(C) : NULL; + KeyingSet *ks; + + /* Active Keying Set. */ + if (!use_poll || (scene && scene->active_keyingset)) { + StringPropertySearchVisitParams visit_params = {NULL}; + visit_params.text = "__ACTIVE__"; + visit_params.info = "Active Keying Set"; + visit_fn(visit_user_data, &visit_params); + } + + /* User-defined Keying Sets. */ + if (scene && scene->keyingsets.first) { + for (ks = scene->keyingsets.first; ks; ks = ks->next) { + if (use_poll && !ANIM_keyingset_context_ok_poll((bContext *)C, ks)) { + continue; + } + StringPropertySearchVisitParams visit_params = {NULL}; + visit_params.text = ks->idname; + visit_params.info = ks->name; + visit_fn(visit_user_data, &visit_params); + } + } + + /* Builtin Keying Sets. */ + for (ks = builtin_keyingsets.first; ks; ks = ks->next) { + if (use_poll && !ANIM_keyingset_context_ok_poll((bContext *)C, ks)) { + continue; + } + StringPropertySearchVisitParams visit_params = {NULL}; + visit_params.text = ks->idname; + visit_params.info = ks->name; + visit_fn(visit_user_data, &visit_params); + } +} + +void ANIM_keyingset_visit_for_search(const bContext *C, + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + const char *UNUSED(edit_text), + StringPropertySearchVisitFunc visit_fn, + void *visit_user_data) +{ + anim_keyingset_visit_for_search_impl(C, visit_fn, visit_user_data, false); +} + +void ANIM_keyingset_visit_for_search_no_poll(const bContext *C, + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + const char *UNUSED(edit_text), + StringPropertySearchVisitFunc visit_fn, + void *visit_user_data) +{ + anim_keyingset_visit_for_search_impl(C, visit_fn, visit_user_data, true); +} + /* Menu of All Keying Sets ----------------------------- */ const EnumPropertyItem *ANIM_keying_sets_enum_itemf(bContext *C, diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c index f9b973733af..dd43e3e6613 100644 --- a/source/blender/editors/armature/armature_add.c +++ b/source/blender/editors/armature/armature_add.c @@ -375,13 +375,10 @@ static void updateDuplicateSubtarget(EditBone *dup_bone, /* does this constraint have a subtarget in * this armature? */ - const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(curcon); ListBase targets = {NULL, NULL}; bConstraintTarget *ct; - if (cti && cti->get_constraint_targets) { - cti->get_constraint_targets(curcon, &targets); - + if (BKE_constraint_targets_get(curcon, &targets)) { for (ct = targets.first; ct; ct = ct->next) { if ((ct->tar == ob) && (ct->subtarget[0])) { oldtarget = get_named_editbone(editbones, ct->subtarget); @@ -409,9 +406,7 @@ static void updateDuplicateSubtarget(EditBone *dup_bone, } } - if (cti->flush_constraint_targets) { - cti->flush_constraint_targets(curcon, &targets, 0); - } + BKE_constraint_targets_flush(curcon, &targets, 0); } } } @@ -1152,7 +1147,7 @@ static int armature_symmetrize_exec(bContext *C, wmOperator *op) } if (axis_delta == 0.0f) { - /* both mirrored bones exist and point to eachother and overlap exactly. + /* Both mirrored bones exist and point to each other and overlap exactly. * * in this case there's no well defined solution, so de-select both and skip. */ diff --git a/source/blender/editors/armature/armature_edit.c b/source/blender/editors/armature/armature_edit.c index 963d7ea1149..3c445f46902 100644 --- a/source/blender/editors/armature/armature_edit.c +++ b/source/blender/editors/armature/armature_edit.c @@ -229,7 +229,7 @@ typedef enum eCalcRollTypes { } eCalcRollTypes; static const EnumPropertyItem prop_calc_roll_types[] = { - {0, "", 0, N_("Positive"), ""}, + RNA_ENUM_ITEM_HEADING(N_("Positive"), NULL), {CALC_ROLL_TAN_POS_X, "POS_X", 0, "Local +X Tangent", ""}, {CALC_ROLL_TAN_POS_Z, "POS_Z", 0, "Local +Z Tangent", ""}, @@ -237,8 +237,7 @@ static const EnumPropertyItem prop_calc_roll_types[] = { {CALC_ROLL_POS_Y, "GLOBAL_POS_Y", 0, "Global +Y Axis", ""}, {CALC_ROLL_POS_Z, "GLOBAL_POS_Z", 0, "Global +Z Axis", ""}, - {0, "", 0, N_("Negative"), ""}, - + RNA_ENUM_ITEM_HEADING(N_("Negative"), NULL), {CALC_ROLL_TAN_NEG_X, "NEG_X", 0, "Local -X Tangent", ""}, {CALC_ROLL_TAN_NEG_Z, "NEG_Z", 0, "Local -Z Tangent", ""}, @@ -246,7 +245,7 @@ static const EnumPropertyItem prop_calc_roll_types[] = { {CALC_ROLL_NEG_Y, "GLOBAL_NEG_Y", 0, "Global -Y Axis", ""}, {CALC_ROLL_NEG_Z, "GLOBAL_NEG_Z", 0, "Global -Z Axis", ""}, - {0, "", 0, N_("Other"), ""}, + RNA_ENUM_ITEM_HEADING(N_("Other"), NULL), {CALC_ROLL_ACTIVE, "ACTIVE", 0, "Active Bone", ""}, {CALC_ROLL_VIEW, "VIEW", 0, "View Axis", ""}, {CALC_ROLL_CURSOR, "CURSOR", 0, "Cursor", ""}, diff --git a/source/blender/editors/armature/armature_naming.c b/source/blender/editors/armature/armature_naming.c index e1f2605481f..1f02e24666d 100644 --- a/source/blender/editors/armature/armature_naming.c +++ b/source/blender/editors/armature/armature_naming.c @@ -109,13 +109,10 @@ static void constraint_bone_name_fix(Object *ob, bConstraintTarget *ct; for (curcon = conlist->first; curcon; curcon = curcon->next) { - const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(curcon); ListBase targets = {NULL, NULL}; /* constraint targets */ - if (cti && cti->get_constraint_targets) { - cti->get_constraint_targets(curcon, &targets); - + if (BKE_constraint_targets_get(curcon, &targets)) { for (ct = targets.first; ct; ct = ct->next) { if (ct->tar == ob) { if (STREQ(ct->subtarget, oldname)) { @@ -124,9 +121,7 @@ static void constraint_bone_name_fix(Object *ob, } } - if (cti->flush_constraint_targets) { - cti->flush_constraint_targets(curcon, &targets, 0); - } + BKE_constraint_targets_flush(curcon, &targets, 0); } /* action constraints */ diff --git a/source/blender/editors/armature/armature_relations.c b/source/blender/editors/armature/armature_relations.c index be4829c02be..950178e865d 100644 --- a/source/blender/editors/armature/armature_relations.c +++ b/source/blender/editors/armature/armature_relations.c @@ -68,14 +68,11 @@ static void joined_armature_fix_links_constraints(Main *bmain, bool changed = false; for (con = lb->first; con; con = con->next) { - const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); ListBase targets = {NULL, NULL}; bConstraintTarget *ct; /* constraint targets */ - if (cti && cti->get_constraint_targets) { - cti->get_constraint_targets(con, &targets); - + if (BKE_constraint_targets_get(con, &targets)) { for (ct = targets.first; ct; ct = ct->next) { if (ct->tar == srcArm) { if (ct->subtarget[0] == '\0') { @@ -90,9 +87,7 @@ static void joined_armature_fix_links_constraints(Main *bmain, } } - if (cti->flush_constraint_targets) { - cti->flush_constraint_targets(con, &targets, 0); - } + BKE_constraint_targets_flush(con, &targets, 0); } /* action constraint? (pose constraints only) */ @@ -459,14 +454,11 @@ static void separated_armature_fix_links(Main *bmain, Object *origArm, Object *n if (ob->type == OB_ARMATURE) { for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { for (con = pchan->constraints.first; con; con = con->next) { - const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); ListBase targets = {NULL, NULL}; bConstraintTarget *ct; /* constraint targets */ - if (cti && cti->get_constraint_targets) { - cti->get_constraint_targets(con, &targets); - + if (BKE_constraint_targets_get(con, &targets)) { for (ct = targets.first; ct; ct = ct->next) { /* Any targets which point to original armature * are redirected to the new one only if: @@ -487,9 +479,7 @@ static void separated_armature_fix_links(Main *bmain, Object *origArm, Object *n } } - if (cti->flush_constraint_targets) { - cti->flush_constraint_targets(con, &targets, 0); - } + BKE_constraint_targets_flush(con, &targets, 0); } } } @@ -498,14 +488,11 @@ static void separated_armature_fix_links(Main *bmain, Object *origArm, Object *n /* fix object-level constraints */ if (ob != origArm) { for (con = ob->constraints.first; con; con = con->next) { - const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); ListBase targets = {NULL, NULL}; bConstraintTarget *ct; /* constraint targets */ - if (cti && cti->get_constraint_targets) { - cti->get_constraint_targets(con, &targets); - + if (BKE_constraint_targets_get(con, &targets)) { for (ct = targets.first; ct; ct = ct->next) { /* any targets which point to original armature are redirected to the new one only if: * - the target isn't origArm/newArm itself @@ -525,9 +512,7 @@ static void separated_armature_fix_links(Main *bmain, Object *origArm, Object *n } } - if (cti->flush_constraint_targets) { - cti->flush_constraint_targets(con, &targets, 0); - } + BKE_constraint_targets_flush(con, &targets, 0); } } } diff --git a/source/blender/editors/armature/armature_skinning.c b/source/blender/editors/armature/armature_skinning.c index 64035732a39..3a1e7419d3c 100644 --- a/source/blender/editors/armature/armature_skinning.c +++ b/source/blender/editors/armature/armature_skinning.c @@ -9,6 +9,7 @@ #include "DNA_armature_types.h" #include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" diff --git a/source/blender/editors/armature/pose_lib.c b/source/blender/editors/armature/pose_lib.c index e0e5693c79b..ea54c3014ca 100644 --- a/source/blender/editors/armature/pose_lib.c +++ b/source/blender/editors/armature/pose_lib.c @@ -1431,7 +1431,7 @@ static int poselib_preview_handle_event(bContext *UNUSED(C), wmOperator *op, con ret = OPERATOR_PASS_THROUGH; break; - /* quicky compare to original */ + /* Quickly compare to original. */ case EVT_TABKEY: pld->flag &= ~PL_PREVIEW_SHOWORIGINAL; pld->redraw = PL_PREVIEW_REDRAWALL; diff --git a/source/blender/editors/armature/pose_select.c b/source/blender/editors/armature/pose_select.c index 8790a10f3e5..b6b5d3ee495 100644 --- a/source/blender/editors/armature/pose_select.c +++ b/source/blender/editors/armature/pose_select.c @@ -680,13 +680,10 @@ static int pose_select_constraint_target_exec(bContext *C, wmOperator *UNUSED(op CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones) { if (pchan->bone->flag & BONE_SELECTED) { for (con = pchan->constraints.first; con; con = con->next) { - const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); ListBase targets = {NULL, NULL}; bConstraintTarget *ct; - if (cti && cti->get_constraint_targets) { - cti->get_constraint_targets(con, &targets); - + if (BKE_constraint_targets_get(con, &targets)) { for (ct = targets.first; ct; ct = ct->next) { Object *ob = ct->tar; @@ -702,9 +699,7 @@ static int pose_select_constraint_target_exec(bContext *C, wmOperator *UNUSED(op } } - if (cti->flush_constraint_targets) { - cti->flush_constraint_targets(con, &targets, 1); - } + BKE_constraint_targets_flush(con, &targets, 1); } } } diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index 9da9845116d..4c0df48f65b 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -4729,6 +4729,7 @@ void CURVE_OT_make_segment(wmOperatorType *ot) bool ED_curve_editnurb_select_pick(bContext *C, const int mval[2], const int dist_px, + const bool vert_without_handles, const struct SelectPick_Params *params) { Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); @@ -4744,6 +4745,9 @@ bool ED_curve_editnurb_select_pick(bContext *C, ED_view3d_viewcontext_init(C, &vc, depsgraph); copy_v2_v2_int(vc.mval, mval); + const bool use_handle_select = vert_without_handles && + (vc.v3d->overlay.handle_display != CURVE_HANDLE_NONE); + bool found = ED_curve_pick_vert_ex(&vc, 1, dist_px, &nu, &bezt, &bp, &hand, &basact); if (params->sel_op == SEL_OP_SET) { @@ -4779,7 +4783,12 @@ bool ED_curve_editnurb_select_pick(bContext *C, case SEL_OP_ADD: { if (bezt) { if (hand == 1) { - select_beztriple(bezt, SELECT, SELECT, HIDDEN); + if (use_handle_select) { + bezt->f2 |= SELECT; + } + else { + select_beztriple(bezt, SELECT, SELECT, HIDDEN); + } } else { if (hand == 0) { @@ -4800,7 +4809,12 @@ bool ED_curve_editnurb_select_pick(bContext *C, case SEL_OP_SUB: { if (bezt) { if (hand == 1) { - select_beztriple(bezt, DESELECT, SELECT, HIDDEN); + if (use_handle_select) { + bezt->f2 &= ~SELECT; + } + else { + select_beztriple(bezt, DESELECT, SELECT, HIDDEN); + } if (bezt == vert) { cu->actvert = CU_ACT_NONE; } @@ -4824,13 +4838,23 @@ bool ED_curve_editnurb_select_pick(bContext *C, if (bezt) { if (hand == 1) { if (bezt->f2 & SELECT) { - select_beztriple(bezt, DESELECT, SELECT, HIDDEN); + if (use_handle_select) { + bezt->f2 &= ~SELECT; + } + else { + select_beztriple(bezt, DESELECT, SELECT, HIDDEN); + } if (bezt == vert) { cu->actvert = CU_ACT_NONE; } } else { - select_beztriple(bezt, SELECT, SELECT, HIDDEN); + if (use_handle_select) { + bezt->f2 |= SELECT; + } + else { + select_beztriple(bezt, SELECT, SELECT, HIDDEN); + } BKE_curve_nurb_vert_active_set(cu, nu, bezt); } } @@ -4861,7 +4885,12 @@ bool ED_curve_editnurb_select_pick(bContext *C, if (bezt) { if (hand == 1) { - select_beztriple(bezt, SELECT, SELECT, HIDDEN); + if (use_handle_select) { + bezt->f2 |= SELECT; + } + else { + select_beztriple(bezt, SELECT, SELECT, HIDDEN); + } } else { if (hand == 0) { @@ -5568,7 +5597,8 @@ static int add_vertex_invoke(bContext *C, wmOperator *op, const wmEvent *event) vc.v3d, SCE_SNAP_MODE_FACE, &(const struct SnapObjectParams){ - .snap_select = (vc.obedit != NULL) ? SNAP_NOT_ACTIVE : SNAP_ALL, + .snap_target_select = (vc.obedit != NULL) ? SCE_SNAP_TARGET_NOT_ACTIVE : + SCE_SNAP_TARGET_ALL, .edit_mode_type = SNAP_GEOM_FINAL, }, mval, diff --git a/source/blender/editors/curve/editcurve_paint.c b/source/blender/editors/curve/editcurve_paint.c index 598779c6ace..6946c09e6f1 100644 --- a/source/blender/editors/curve/editcurve_paint.c +++ b/source/blender/editors/curve/editcurve_paint.c @@ -105,7 +105,7 @@ struct CurveDrawData { } radius; struct { - float mouse[2]; + float mval[2]; /* Used in case we can't calculate the depth. */ float location_world[3]; @@ -463,8 +463,8 @@ static void curve_draw_event_add(wmOperator *op, const wmEvent *event) } copy_v3_v3(cdd->prev.location_world, selem->location_world); - float len_sq = len_squared_v2v2(cdd->prev.mouse, selem->mval); - copy_v2_v2(cdd->prev.mouse, selem->mval); + float len_sq = len_squared_v2v2(cdd->prev.mval, selem->mval); + copy_v2_v2(cdd->prev.mval, selem->mval); if (cdd->sample.use_substeps && cdd->prev.selem) { const struct StrokeElem selem_target = *selem; @@ -1165,7 +1165,7 @@ static int curve_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { if (cdd->state == CURVE_DRAW_PAINTING) { const float mval_fl[2] = {UNPACK2(event->mval)}; - if (len_squared_v2v2(mval_fl, cdd->prev.mouse) > square_f(STROKE_SAMPLE_DIST_MIN_PX)) { + if (len_squared_v2v2(mval_fl, cdd->prev.mval) > square_f(STROKE_SAMPLE_DIST_MIN_PX)) { curve_draw_event_add(op, event); } } diff --git a/source/blender/editors/curve/editcurve_pen.c b/source/blender/editors/curve/editcurve_pen.c index fca850076ae..729ad46877a 100644 --- a/source/blender/editors/curve/editcurve_pen.c +++ b/source/blender/editors/curve/editcurve_pen.c @@ -843,7 +843,7 @@ static bool insert_point_to_segment(const ViewContext *vc, const wmEvent *event) { Curve *cu = vc->obedit->data; CutData cd = init_cut_data(event); - float mval[2] = {UNPACK2(event->mval)}; + const float mval[2] = {UNPACK2(event->mval)}; const float threshold_dist_px = ED_view3d_select_dist_px() * SEL_DIST_FACTOR; const bool near_spline = update_cut_data_for_all_nurbs( vc, BKE_curve_editNurbs_get(cu), mval, threshold_dist_px, &cd); @@ -985,7 +985,7 @@ static void extrude_vertices_from_selected_endpoints(EditNurb *editnurb, else { BPoint *last_bp = nu1->bp + nu1->pntsu - 1; const bool first_sel = nu1->bp->f1 & SELECT; - const bool last_sel = last_bp->f1 & SELECT; + const bool last_sel = last_bp->f1 & SELECT && nu1->pntsu > 1; if (first_sel) { if (last_sel) { BPoint *new_bp = (BPoint *)MEM_mallocN((nu1->pntsu + 2) * sizeof(BPoint), __func__); @@ -1134,7 +1134,7 @@ static bool is_spline_nearby(ViewContext *vc, ListBase *nurbs = BKE_curve_editNurbs_get(cu); CutData cd = init_cut_data(event); - float mval[2] = {UNPACK2(event->mval)}; + const float mval[2] = {UNPACK2(event->mval)}; const bool nearby = update_cut_data_for_all_nurbs(vc, nurbs, mval, sel_dist, &cd); if (nearby) { @@ -1655,7 +1655,7 @@ static int curve_pen_modal(bContext *C, wmOperator *op, const wmEvent *event) else if (ELEM(event->type, LEFTMOUSE)) { if (ELEM(event->val, KM_RELEASE, KM_DBL_CLICK)) { if (delete_point && !cpd->new_point && !cpd->dragging) { - if (ED_curve_editnurb_select_pick(C, event->mval, threshold_dist_px, ¶ms)) { + if (ED_curve_editnurb_select_pick(C, event->mval, threshold_dist_px, false, ¶ms)) { cpd->acted = delete_point_under_mouse(&vc, event); } } @@ -1714,7 +1714,7 @@ static int curve_pen_modal(bContext *C, wmOperator *op, const wmEvent *event) } } else if (select_point) { - ED_curve_editnurb_select_pick(C, event->mval, threshold_dist_px, ¶ms); + ED_curve_editnurb_select_pick(C, event->mval, threshold_dist_px, false, ¶ms); } } diff --git a/source/blender/editors/curves/CMakeLists.txt b/source/blender/editors/curves/CMakeLists.txt index 1731d224b3e..3c31e8014ff 100644 --- a/source/blender/editors/curves/CMakeLists.txt +++ b/source/blender/editors/curves/CMakeLists.txt @@ -7,10 +7,14 @@ set(INC ../../blentranslation ../../depsgraph ../../functions + ../../geometry ../../makesdna ../../makesrna ../../windowmanager ../../../../intern/guardedalloc + + # RNA_prototypes.h + ${CMAKE_BINARY_DIR}/source/blender/makesrna ) set(SRC @@ -24,3 +28,4 @@ set(LIB ) blender_add_lib(bf_editor_curves "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") +add_dependencies(bf_editor_curves bf_rna) diff --git a/source/blender/editors/curves/intern/curves_add.cc b/source/blender/editors/curves/intern/curves_add.cc index 7d9e663c444..552ef1d96c8 100644 --- a/source/blender/editors/curves/intern/curves_add.cc +++ b/source/blender/editors/curves/intern/curves_add.cc @@ -20,7 +20,7 @@ bke::CurvesGeometry primitive_random_sphere(const int curves_size, const int poi MutableSpan<float3> positions = curves.positions_for_write(); float *radius_data = (float *)CustomData_add_layer_named( - &curves.point_data, CD_PROP_FLOAT, CD_DEFAULT, nullptr, curves.point_size, "radius"); + &curves.point_data, CD_PROP_FLOAT, CD_DEFAULT, nullptr, curves.point_num, "radius"); MutableSpan<float> radii{radius_data, curves.points_num()}; for (const int i : offsets.index_range()) { diff --git a/source/blender/editors/curves/intern/curves_ops.cc b/source/blender/editors/curves/intern/curves_ops.cc index 7d07c211542..25bcba6cfb3 100644 --- a/source/blender/editors/curves/intern/curves_ops.cc +++ b/source/blender/editors/curves/intern/curves_ops.cc @@ -6,20 +6,27 @@ #include <atomic> +#include "BLI_devirtualize_parameters.hh" #include "BLI_utildefines.h" +#include "BLI_vector_set.hh" #include "ED_curves.h" #include "ED_object.h" #include "ED_screen.h" +#include "ED_select_utils.h" #include "WM_api.h" +#include "BKE_attribute_math.hh" #include "BKE_bvhutils.h" #include "BKE_context.h" #include "BKE_curves.hh" +#include "BKE_geometry_set.hh" #include "BKE_layer.h" +#include "BKE_lib_id.h" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" +#include "BKE_object.h" #include "BKE_paint.h" #include "BKE_particle.h" #include "BKE_report.h" @@ -32,9 +39,14 @@ #include "DNA_scene_types.h" #include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" #include "RNA_access.h" #include "RNA_define.h" +#include "RNA_enum_types.h" +#include "RNA_prototypes.h" + +#include "GEO_reverse_uv_sampler.hh" /** * The code below uses a suffix naming convention to indicate the coordinate space: @@ -46,6 +58,41 @@ namespace blender::ed::curves { +static bool object_has_editable_curves(const Main &bmain, const Object &object) +{ + if (object.type != OB_CURVES) { + return false; + } + if (!ELEM(object.mode, OB_MODE_SCULPT_CURVES, OB_MODE_EDIT)) { + return false; + } + if (!BKE_id_is_editable(&bmain, static_cast<const ID *>(object.data))) { + return false; + } + return true; +} + +static VectorSet<Curves *> get_unique_editable_curves(const bContext &C) +{ + VectorSet<Curves *> unique_curves; + + const Main &bmain = *CTX_data_main(&C); + + Object *object = CTX_data_active_object(&C); + if (object && object_has_editable_curves(bmain, *object)) { + unique_curves.add_new(static_cast<Curves *>(object->data)); + } + + CTX_DATA_BEGIN (&C, Object *, object, selected_objects) { + if (object_has_editable_curves(bmain, *object)) { + unique_curves.add(static_cast<Curves *>(object->data)); + } + } + CTX_DATA_END; + + return unique_curves; +} + using bke::CurvesGeometry; namespace convert_to_particle_system { @@ -140,25 +187,20 @@ static void try_convert_single_object(Object &curves_ob, } Mesh &surface_me = *static_cast<Mesh *>(surface_ob.data); + BVHTreeFromMesh surface_bvh; + BKE_bvhtree_from_mesh_get(&surface_bvh, &surface_me, BVHTREE_FROM_LOOPTRI, 2); + BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh); }); + const Span<float3> positions_cu = curves.positions(); - const VArray<int> looptri_indices = curves.surface_triangle_indices(); const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&surface_me), BKE_mesh_runtime_looptri_len(&surface_me)}; - /* Find indices of curves that can be transferred to the old hair system. */ - Vector<int> curves_indices_to_transfer; - for (const int curve_i : curves.curves_range()) { - const int looptri_i = looptri_indices[curve_i]; - if (looptri_i >= 0 && looptri_i < looptris.size()) { - curves_indices_to_transfer.append(curve_i); - } - else { - *r_could_not_convert_some_curves = true; - } + if (looptris.is_empty()) { + *r_could_not_convert_some_curves = true; } - const int hairs_num = curves_indices_to_transfer.size(); - if (hairs_num == 0) { + const int hair_num = curves.curves_num(); + if (hair_num == 0) { return; } @@ -184,15 +226,15 @@ static void try_convert_single_object(Object &curves_ob, psys_changed_type(&surface_ob, particle_system); MutableSpan<ParticleData> particles{ - static_cast<ParticleData *>(MEM_calloc_arrayN(hairs_num, sizeof(ParticleData), __func__)), - hairs_num}; + static_cast<ParticleData *>(MEM_calloc_arrayN(hair_num, sizeof(ParticleData), __func__)), + hair_num}; /* The old hair system still uses #MFace, so make sure those are available on the mesh. */ BKE_mesh_tessface_calc(&surface_me); - /* Prepare utility data structure to map hair roots to mfaces. */ + /* Prepare utility data structure to map hair roots to #MFace's. */ const Span<int> mface_to_poly_map{ - static_cast<int *>(CustomData_get_layer(&surface_me.fdata, CD_ORIGINDEX)), + static_cast<const int *>(CustomData_get_layer(&surface_me.fdata, CD_ORIGINDEX)), surface_me.totface}; Array<Vector<int>> poly_to_mface_map(surface_me.totpoly); for (const int mface_i : mface_to_poly_map.index_range()) { @@ -206,17 +248,23 @@ static void try_convert_single_object(Object &curves_ob, const float4x4 world_to_surface_mat = surface_to_world_mat.inverted(); const float4x4 curves_to_surface_mat = world_to_surface_mat * curves_to_world_mat; - for (const int new_hair_i : curves_indices_to_transfer.index_range()) { - const int curve_i = curves_indices_to_transfer[new_hair_i]; + for (const int new_hair_i : IndexRange(hair_num)) { + const int curve_i = new_hair_i; const IndexRange points = curves.points_for_curve(curve_i); - const int looptri_i = looptri_indices[curve_i]; - const MLoopTri &looptri = looptris[looptri_i]; - const int poly_i = looptri.poly; - const float3 &root_pos_cu = positions_cu[points.first()]; const float3 root_pos_su = curves_to_surface_mat * root_pos_cu; + BVHTreeNearest nearest; + nearest.dist_sq = FLT_MAX; + BLI_bvhtree_find_nearest( + surface_bvh.tree, root_pos_su, &nearest, surface_bvh.nearest_callback, &surface_bvh); + BLI_assert(nearest.index >= 0); + + const int looptri_i = nearest.index; + const MLoopTri &looptri = looptris[looptri_i]; + const int poly_i = looptri.poly; + const int mface_i = find_mface_for_root_position( surface_me, poly_to_mface_map[poly_i], root_pos_su); const MFace &mface = surface_me.mface[mface_i]; @@ -315,6 +363,140 @@ static void CURVES_OT_convert_to_particle_system(wmOperatorType *ot) ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; } +namespace convert_from_particle_system { + +static bke::CurvesGeometry particles_to_curves(Object &object, ParticleSystem &psys) +{ + ParticleSettings &settings = *psys.part; + if (psys.part->type != PART_HAIR) { + return {}; + } + + const bool transfer_parents = (settings.draw & PART_DRAW_PARENT) || settings.childtype == 0; + + const Span<ParticleCacheKey *> parents_cache{psys.pathcache, psys.totcached}; + const Span<ParticleCacheKey *> children_cache{psys.childcache, psys.totchildcache}; + + int points_num = 0; + Vector<int> curve_offsets; + Vector<int> parents_to_transfer; + Vector<int> children_to_transfer; + if (transfer_parents) { + for (const int parent_i : parents_cache.index_range()) { + const int segments = parents_cache[parent_i]->segments; + if (segments <= 0) { + continue; + } + parents_to_transfer.append(parent_i); + curve_offsets.append(points_num); + points_num += segments + 1; + } + } + for (const int child_i : children_cache.index_range()) { + const int segments = children_cache[child_i]->segments; + if (segments <= 0) { + continue; + } + children_to_transfer.append(child_i); + curve_offsets.append(points_num); + points_num += segments + 1; + } + const int curves_num = parents_to_transfer.size() + children_to_transfer.size(); + curve_offsets.append(points_num); + BLI_assert(curve_offsets.size() == curves_num + 1); + bke::CurvesGeometry curves(points_num, curves_num); + curves.offsets_for_write().copy_from(curve_offsets); + + const float4x4 object_to_world_mat = object.obmat; + const float4x4 world_to_object_mat = object_to_world_mat.inverted(); + + MutableSpan<float3> positions = curves.positions_for_write(); + + const auto copy_hair_to_curves = [&](const Span<ParticleCacheKey *> hair_cache, + const Span<int> indices_to_transfer, + const int curve_index_offset) { + threading::parallel_for(indices_to_transfer.index_range(), 256, [&](const IndexRange range) { + for (const int i : range) { + const int hair_i = indices_to_transfer[i]; + const int curve_i = i + curve_index_offset; + const IndexRange points = curves.points_for_curve(curve_i); + const Span<ParticleCacheKey> keys{hair_cache[hair_i], points.size()}; + for (const int key_i : keys.index_range()) { + const float3 key_pos_wo = keys[key_i].co; + positions[points[key_i]] = world_to_object_mat * key_pos_wo; + } + } + }); + }; + + if (transfer_parents) { + copy_hair_to_curves(parents_cache, parents_to_transfer, 0); + } + copy_hair_to_curves(children_cache, children_to_transfer, parents_to_transfer.size()); + + curves.update_curve_types(); + curves.tag_topology_changed(); + return curves; +} + +static int curves_convert_from_particle_system_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Main &bmain = *CTX_data_main(C); + ViewLayer &view_layer = *CTX_data_view_layer(C); + Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(C); + Object *ob_from_orig = ED_object_active_context(C); + ParticleSystem *psys_orig = static_cast<ParticleSystem *>( + CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem).data); + if (psys_orig == nullptr) { + psys_orig = psys_get_current(ob_from_orig); + } + if (psys_orig == nullptr) { + return OPERATOR_CANCELLED; + } + Object *ob_from_eval = DEG_get_evaluated_object(&depsgraph, ob_from_orig); + ParticleSystem *psys_eval = nullptr; + LISTBASE_FOREACH (ModifierData *, md, &ob_from_eval->modifiers) { + if (md->type != eModifierType_ParticleSystem) { + continue; + } + ParticleSystemModifierData *psmd = reinterpret_cast<ParticleSystemModifierData *>(md); + if (!STREQ(psmd->psys->name, psys_orig->name)) { + continue; + } + psys_eval = psmd->psys; + } + + Object *ob_new = BKE_object_add(&bmain, &view_layer, OB_CURVES, psys_eval->name); + ob_new->dtx |= OB_DRAWBOUNDOX; /* TODO: Remove once there is actual drawing. */ + Curves *curves_id = static_cast<Curves *>(ob_new->data); + BKE_object_apply_mat4(ob_new, ob_from_orig->obmat, true, false); + bke::CurvesGeometry::wrap(curves_id->geometry) = particles_to_curves(*ob_from_eval, *psys_eval); + + DEG_relations_tag_update(&bmain); + WM_main_add_notifier(NC_OBJECT | ND_DRAW, nullptr); + + return OPERATOR_FINISHED; +} + +static bool curves_convert_from_particle_system_poll(bContext *C) +{ + return ED_object_active_context(C) != nullptr; +} + +} // namespace convert_from_particle_system + +static void CURVES_OT_convert_from_particle_system(wmOperatorType *ot) +{ + ot->name = "Convert Particle System to Curves"; + ot->idname = "CURVES_OT_convert_from_particle_system"; + ot->description = "Add a new curves object based on the current state of the particle system"; + + ot->poll = convert_from_particle_system::curves_convert_from_particle_system_poll; + ot->exec = convert_from_particle_system::curves_convert_from_particle_system_exec; + + ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; +} + namespace snap_curves_to_surface { enum class AttachMode { @@ -342,7 +524,7 @@ static int snap_curves_to_surface_exec(bContext *C, wmOperator *op) { const AttachMode attach_mode = static_cast<AttachMode>(RNA_enum_get(op->ptr, "attach_mode")); - std::atomic<bool> found_invalid_looptri_index = false; + std::atomic<bool> found_invalid_uv = false; CTX_DATA_BEGIN (C, Object *, curves_ob, selected_objects) { if (curves_ob->type != OB_CURVES) { @@ -359,9 +541,19 @@ static int snap_curves_to_surface_exec(bContext *C, wmOperator *op) } Mesh &surface_mesh = *static_cast<Mesh *>(surface_ob.data); + MeshComponent surface_mesh_component; + surface_mesh_component.replace(&surface_mesh, GeometryOwnershipType::ReadOnly); + + VArray_Span<float2> surface_uv_map; + if (curves_id.surface_uv_map != nullptr) { + surface_uv_map = surface_mesh_component + .attribute_try_get_for_read( + curves_id.surface_uv_map, ATTR_DOMAIN_CORNER, CD_PROP_FLOAT2) + .typed<float2>(); + } + MutableSpan<float3> positions_cu = curves.positions_for_write(); - MutableSpan<int> surface_triangle_indices = curves.surface_triangle_indices_for_write(); - MutableSpan<float2> surface_triangle_coords = curves.surface_triangle_coords_for_write(); + MutableSpan<float2> surface_uv_coords = curves.surface_uv_coords_for_write(); const Span<MLoopTri> surface_looptris = {BKE_mesh_runtime_looptri_ensure(&surface_mesh), BKE_mesh_runtime_looptri_len(&surface_mesh)}; @@ -407,36 +599,51 @@ static int snap_curves_to_surface_exec(bContext *C, wmOperator *op) pos_cu += pos_diff_cu; } - surface_triangle_indices[curve_i] = looptri_index; - - const MLoopTri &looptri = surface_looptris[looptri_index]; - const float3 &p0_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[0]].v].co; - const float3 &p1_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[1]].v].co; - const float3 &p2_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[2]].v].co; - float3 bary_coords; - interp_weights_tri_v3(bary_coords, p0_su, p1_su, p2_su, new_first_point_pos_su); - surface_triangle_coords[curve_i] = bke::curves::encode_surface_bary_coord(bary_coords); + if (!surface_uv_map.is_empty()) { + const MLoopTri &looptri = surface_looptris[looptri_index]; + const int corner0 = looptri.tri[0]; + const int corner1 = looptri.tri[1]; + const int corner2 = looptri.tri[2]; + const float2 &uv0 = surface_uv_map[corner0]; + const float2 &uv1 = surface_uv_map[corner1]; + const float2 &uv2 = surface_uv_map[corner2]; + const float3 &p0_su = surface_mesh.mvert[surface_mesh.mloop[corner0].v].co; + const float3 &p1_su = surface_mesh.mvert[surface_mesh.mloop[corner1].v].co; + const float3 &p2_su = surface_mesh.mvert[surface_mesh.mloop[corner2].v].co; + float3 bary_coords; + interp_weights_tri_v3(bary_coords, p0_su, p1_su, p2_su, new_first_point_pos_su); + const float2 uv = attribute_math::mix3(bary_coords, uv0, uv1, uv2); + surface_uv_coords[curve_i] = uv; + } } }); break; } case AttachMode::Deform: { + if (surface_uv_map.is_empty()) { + BKE_report(op->reports, + RPT_ERROR, + "Curves do not have attachment information that can be used for deformation"); + break; + } + using geometry::ReverseUVSampler; + ReverseUVSampler reverse_uv_sampler{surface_uv_map, surface_looptris}; + threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) { for (const int curve_i : curves_range) { const IndexRange points = curves.points_for_curve(curve_i); const int first_point_i = points.first(); const float3 old_first_point_pos_cu = positions_cu[first_point_i]; - const int looptri_index = surface_triangle_indices[curve_i]; - if (!surface_looptris.index_range().contains(looptri_index)) { - found_invalid_looptri_index = true; + const float2 uv = surface_uv_coords[curve_i]; + ReverseUVSampler::Result lookup_result = reverse_uv_sampler.sample(uv); + if (lookup_result.type != ReverseUVSampler::ResultType::Ok) { + found_invalid_uv = true; continue; } - const MLoopTri &looptri = surface_looptris[looptri_index]; - - const float3 bary_coords = bke::curves::decode_surface_bary_coord( - surface_triangle_coords[curve_i]); + const MLoopTri &looptri = *lookup_result.looptri; + const float3 &bary_coords = lookup_result.bary_weights; const float3 &p0_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[0]].v].co; const float3 &p1_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[1]].v].co; @@ -460,7 +667,7 @@ static int snap_curves_to_surface_exec(bContext *C, wmOperator *op) } CTX_DATA_END; - if (found_invalid_looptri_index) { + if (found_invalid_uv) { BKE_report(op->reports, RPT_INFO, "Could not snap some curves to the surface"); } @@ -508,11 +715,231 @@ static void CURVES_OT_snap_curves_to_surface(wmOperatorType *ot) "How to find the point on the surface to attach to"); } +static bool selection_poll(bContext *C) +{ + const Object *object = CTX_data_active_object(C); + if (object == nullptr) { + return false; + } + if (object->type != OB_CURVES) { + return false; + } + if (!BKE_id_is_editable(CTX_data_main(C), static_cast<const ID *>(object->data))) { + return false; + } + return true; +} + +namespace set_selection_domain { + +static int curves_set_selection_domain_exec(bContext *C, wmOperator *op) +{ + const eAttrDomain domain = eAttrDomain(RNA_enum_get(op->ptr, "domain")); + + for (Curves *curves_id : get_unique_editable_curves(*C)) { + if (curves_id->selection_domain == domain && (curves_id->flag & CV_SCULPT_SELECTION_ENABLED)) { + continue; + } + + const eAttrDomain old_domain = eAttrDomain(curves_id->selection_domain); + curves_id->selection_domain = domain; + curves_id->flag |= CV_SCULPT_SELECTION_ENABLED; + + CurveComponent component; + component.replace(curves_id, GeometryOwnershipType::Editable); + CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry); + + if (old_domain == ATTR_DOMAIN_POINT && domain == ATTR_DOMAIN_CURVE) { + VArray<float> curve_selection = curves.adapt_domain( + curves.selection_point_float(), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE); + curve_selection.materialize(curves.selection_curve_float_for_write()); + component.attribute_try_delete(".selection_point_float"); + } + else if (old_domain == ATTR_DOMAIN_CURVE && domain == ATTR_DOMAIN_POINT) { + VArray<float> point_selection = curves.adapt_domain( + curves.selection_curve_float(), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT); + point_selection.materialize(curves.selection_point_float_for_write()); + component.attribute_try_delete(".selection_curve_float"); + } + + /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic + * attribute for now. */ + DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id); + } + + WM_main_add_notifier(NC_SPACE | ND_SPACE_VIEW3D, nullptr); + + return OPERATOR_FINISHED; +} + +} // namespace set_selection_domain + +static void CURVES_OT_set_selection_domain(wmOperatorType *ot) +{ + PropertyRNA *prop; + + ot->name = "Set Select Mode"; + ot->idname = __func__; + ot->description = "Change the mode used for selection masking in curves sculpt mode"; + + ot->exec = set_selection_domain::curves_set_selection_domain_exec; + ot->poll = selection_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = prop = RNA_def_enum( + ot->srna, "domain", rna_enum_attribute_curves_domain_items, 0, "Domain", ""); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); +} + +namespace disable_selection { + +static int curves_disable_selection_exec(bContext *C, wmOperator *UNUSED(op)) +{ + for (Curves *curves_id : get_unique_editable_curves(*C)) { + curves_id->flag &= ~CV_SCULPT_SELECTION_ENABLED; + + /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic + * attribute for now. */ + DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id); + } + + WM_main_add_notifier(NC_SPACE | ND_SPACE_VIEW3D, nullptr); + + return OPERATOR_FINISHED; +} + +} // namespace disable_selection + +static void CURVES_OT_disable_selection(wmOperatorType *ot) +{ + ot->name = "Disable Selection"; + ot->idname = __func__; + ot->description = "Disable the drawing of influence of selection in sculpt mode"; + + ot->exec = disable_selection::curves_disable_selection_exec; + ot->poll = selection_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +namespace select_all { + +static bool varray_contains_nonzero(const VArray<float> &data) +{ + bool contains_nonzero = false; + devirtualize_varray(data, [&](const auto array) { + for (const int i : data.index_range()) { + if (array[i] != 0.0f) { + contains_nonzero = true; + break; + } + } + }); + return contains_nonzero; +} + +static bool any_point_selected(const CurvesGeometry &curves) +{ + return varray_contains_nonzero(curves.selection_point_float()); +} + +static bool any_point_selected(const Span<Curves *> curves_ids) +{ + for (const Curves *curves_id : curves_ids) { + if (any_point_selected(CurvesGeometry::wrap(curves_id->geometry))) { + return true; + } + } + return false; +} + +static void invert_selection(MutableSpan<float> selection) +{ + threading::parallel_for(selection.index_range(), 2048, [&](IndexRange range) { + for (const int i : range) { + selection[i] = 1.0f - selection[i]; + } + }); +} + +static int select_all_exec(bContext *C, wmOperator *op) +{ + int action = RNA_enum_get(op->ptr, "action"); + + VectorSet<Curves *> unique_curves = get_unique_editable_curves(*C); + + if (action == SEL_TOGGLE) { + action = any_point_selected(unique_curves) ? SEL_DESELECT : SEL_SELECT; + } + + for (Curves *curves_id : unique_curves) { + if (action == SEL_SELECT) { + /* The optimization to avoid storing the selection when everything is selected causes too + * many problems at the moment, since there is no proper visualization yet. Keep the code but + * disable it for now. */ +#if 0 + CurveComponent component; + component.replace(curves_id, GeometryOwnershipType::Editable); + component.attribute_try_delete(".selection_point_float"); + component.attribute_try_delete(".selection_curve_float"); +#else + CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry); + MutableSpan<float> selection = curves_id->selection_domain == ATTR_DOMAIN_POINT ? + curves.selection_point_float_for_write() : + curves.selection_curve_float_for_write(); + selection.fill(1.0f); +#endif + } + else { + CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry); + MutableSpan<float> selection = curves_id->selection_domain == ATTR_DOMAIN_POINT ? + curves.selection_point_float_for_write() : + curves.selection_curve_float_for_write(); + if (action == SEL_DESELECT) { + selection.fill(0.0f); + } + else if (action == SEL_INVERT) { + invert_selection(selection); + } + } + + /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic + * attribute for now. */ + DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id); + } + + return OPERATOR_FINISHED; +} + +} // namespace select_all + +static void SCULPT_CURVES_OT_select_all(wmOperatorType *ot) +{ + ot->name = "(De)select All"; + ot->idname = __func__; + ot->description = "(De)select all control points"; + + ot->exec = select_all::select_all_exec; + ot->poll = selection_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + WM_operator_properties_select_all(ot); +} + } // namespace blender::ed::curves void ED_operatortypes_curves() { using namespace blender::ed::curves; WM_operatortype_append(CURVES_OT_convert_to_particle_system); + WM_operatortype_append(CURVES_OT_convert_from_particle_system); WM_operatortype_append(CURVES_OT_snap_curves_to_surface); + WM_operatortype_append(CURVES_OT_set_selection_domain); + WM_operatortype_append(SCULPT_CURVES_OT_select_all); + WM_operatortype_append(CURVES_OT_disable_selection); } diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt index d8ecb2c5ae0..9da2c4819a3 100644 --- a/source/blender/editors/datafiles/CMakeLists.txt +++ b/source/blender/editors/datafiles/CMakeLists.txt @@ -776,14 +776,19 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES ops.curves.sculpt_comb ops.curves.sculpt_cut ops.curves.sculpt_delete + ops.curves.sculpt_density ops.curves.sculpt_grow_shrink + ops.curves.sculpt_pinch ops.curves.sculpt_puff + ops.curves.sculpt_slide + ops.curves.sculpt_smooth ops.curves.sculpt_snake_hook ops.generic.cursor ops.generic.select ops.generic.select_box ops.generic.select_circle ops.generic.select_lasso + ops.generic.select_paint ops.gpencil.draw ops.gpencil.draw.eraser ops.gpencil.draw.line @@ -928,6 +933,7 @@ if(WITH_BLENDER) data_to_c_simple(../../../../release/datafiles/brushicons/mask.png SRC) data_to_c_simple(../../../../release/datafiles/brushicons/mix.png SRC) data_to_c_simple(../../../../release/datafiles/brushicons/nudge.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/paint_select.png SRC) data_to_c_simple(../../../../release/datafiles/brushicons/pinch.png SRC) data_to_c_simple(../../../../release/datafiles/brushicons/scrape.png SRC) data_to_c_simple(../../../../release/datafiles/brushicons/smear.png SRC) @@ -965,6 +971,19 @@ if(WITH_BLENDER) data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_erase_hard.png SRC) data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_erase_stroke.png SRC) + # curve sculpt + data_to_c_simple(../../../../release/datafiles/brushicons/curves_sculpt_add.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/curves_sculpt_comb.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/curves_sculpt_cut.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/curves_sculpt_delete.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/curves_sculpt_density.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/curves_sculpt_grow_shrink.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/curves_sculpt_pinch.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/curves_sculpt_puff.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/curves_sculpt_slide.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/curves_sculpt_smooth.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/curves_sculpt_snake_hook.png SRC) + endif() data_to_c_simple(../../../../release/datafiles/startup.blend SRC) diff --git a/source/blender/editors/geometry/geometry_attributes.cc b/source/blender/editors/geometry/geometry_attributes.cc index cfc158b117f..c7e782b7b89 100644 --- a/source/blender/editors/geometry/geometry_attributes.cc +++ b/source/blender/editors/geometry/geometry_attributes.cc @@ -33,6 +33,7 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "ED_geometry.h" #include "ED_object.h" #include "geometry_intern.hh" @@ -43,9 +44,9 @@ namespace blender::ed::geometry { static bool geometry_attributes_poll(bContext *C) { - Object *ob = ED_object_context(C); - Main *bmain = CTX_data_main(C); - ID *data = (ob) ? static_cast<ID *>(ob->data) : nullptr; + const Object *ob = ED_object_context(C); + const Main *bmain = CTX_data_main(C); + const ID *data = (ob) ? static_cast<ID *>(ob->data) : nullptr; return (ob && BKE_id_is_editable(bmain, &ob->id) && data && BKE_id_is_editable(bmain, data)) && BKE_id_attributes_supported(data); } @@ -89,8 +90,8 @@ static int geometry_attribute_add_exec(bContext *C, wmOperator *op) char name[MAX_NAME]; RNA_string_get(op->ptr, "name", name); - CustomDataType type = (CustomDataType)RNA_enum_get(op->ptr, "data_type"); - AttributeDomain domain = (AttributeDomain)RNA_enum_get(op->ptr, "domain"); + eCustomDataType type = (eCustomDataType)RNA_enum_get(op->ptr, "data_type"); + eAttrDomain domain = (eAttrDomain)RNA_enum_get(op->ptr, "domain"); CustomDataLayer *layer = BKE_id_attribute_new(id, name, type, domain, op->reports); if (layer == nullptr) { @@ -105,7 +106,7 @@ static int geometry_attribute_add_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -static void next_color_attribute(struct ID *id, CustomDataLayer *layer, bool is_render) +static void next_color_attribute(ID *id, CustomDataLayer *layer, bool is_render) { int index = BKE_id_attribute_to_index(id, layer, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL); @@ -128,7 +129,7 @@ static void next_color_attribute(struct ID *id, CustomDataLayer *layer, bool is_ } } -static void next_color_attributes(struct ID *id, CustomDataLayer *layer) +static void next_color_attributes(ID *id, CustomDataLayer *layer) { next_color_attribute(id, layer, false); /* active */ next_color_attribute(id, layer, true); /* render */ @@ -181,7 +182,7 @@ static int geometry_attribute_remove_exec(bContext *C, wmOperator *op) next_color_attributes(id, layer); - if (!BKE_id_attribute_remove(id, layer, op->reports)) { + if (!BKE_id_attribute_remove(id, layer->name, op->reports)) { return OPERATOR_CANCELLED; } @@ -218,8 +219,8 @@ static int geometry_color_attribute_add_exec(bContext *C, wmOperator *op) char name[MAX_NAME]; RNA_string_get(op->ptr, "name", name); - CustomDataType type = (CustomDataType)RNA_enum_get(op->ptr, "data_type"); - AttributeDomain domain = (AttributeDomain)RNA_enum_get(op->ptr, "domain"); + eCustomDataType type = (eCustomDataType)RNA_enum_get(op->ptr, "data_type"); + eAttrDomain domain = (eAttrDomain)RNA_enum_get(op->ptr, "domain"); CustomDataLayer *layer = BKE_id_attribute_new(id, name, type, domain, op->reports); float color[4]; @@ -260,8 +261,11 @@ static bool geometry_attribute_convert_poll(bContext *C) if (GS(data->name) != ID_ME) { return false; } - CustomDataLayer *layer = BKE_id_attributes_active_get(data); - if (layer == nullptr) { + if (CTX_data_edit_object(C) != nullptr) { + CTX_wm_operator_poll_msg_set(C, "Operation is not allowed in edit mode"); + return false; + } + if (BKE_id_attributes_active_get(data) == nullptr) { return false; } return true; @@ -288,9 +292,8 @@ static int geometry_attribute_convert_exec(bContext *C, wmOperator *op) * 4. Create a new attribute based on the previously copied data. */ switch (mode) { case ConvertAttributeMode::Generic: { - const AttributeDomain dst_domain = static_cast<AttributeDomain>( - RNA_enum_get(op->ptr, "domain")); - const CustomDataType dst_type = static_cast<CustomDataType>( + const eAttrDomain dst_domain = static_cast<eAttrDomain>(RNA_enum_get(op->ptr, "domain")); + const eCustomDataType dst_type = static_cast<eCustomDataType>( RNA_enum_get(op->ptr, "data_type")); if (ELEM(dst_type, CD_PROP_STRING)) { @@ -402,7 +405,7 @@ void GEOMETRY_OT_color_attribute_add(wmOperatorType *ot) static float default_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; prop = RNA_def_float_color( - ot->srna, "color", 4, NULL, 0.0f, FLT_MAX, "Color", "Default fill color", 0.0f, 1.0f); + ot->srna, "color", 4, nullptr, 0.0f, FLT_MAX, "Color", "Default fill color", 0.0f, 1.0f); RNA_def_property_subtype(prop, PROP_COLOR_GAMMA); RNA_def_property_float_array_default(prop, default_color); } @@ -465,7 +468,7 @@ static int geometry_color_attribute_remove_exec(bContext *C, wmOperator *op) next_color_attributes(id, layer); - if (!BKE_id_attribute_remove(id, layer, op->reports)) { + if (!BKE_id_attribute_remove(id, layer->name, op->reports)) { return OPERATOR_CANCELLED; } @@ -495,6 +498,7 @@ static bool geometry_color_attributes_remove_poll(bContext *C) return false; } + void GEOMETRY_OT_color_attribute_remove(wmOperatorType *ot) { /* identifiers */ @@ -510,6 +514,64 @@ void GEOMETRY_OT_color_attribute_remove(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +static int geometry_color_attribute_duplicate_exec(bContext *C, wmOperator *op) +{ + Object *ob = ED_object_context(C); + ID *id = static_cast<ID *>(ob->data); + const CustomDataLayer *layer = BKE_id_attributes_active_color_get(id); + + if (layer == nullptr) { + return OPERATOR_CANCELLED; + } + + CustomDataLayer *new_layer = BKE_id_attribute_duplicate(id, layer->name, op->reports); + if (new_layer == nullptr) { + return OPERATOR_CANCELLED; + } + + BKE_id_attributes_active_color_set(id, new_layer); + + DEG_id_tag_update(id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, id); + + return OPERATOR_FINISHED; +} + +static bool geometry_color_attributes_duplicate_poll(bContext *C) +{ + if (!geometry_attributes_poll(C)) { + return false; + } + if (CTX_data_edit_object(C) != nullptr) { + CTX_wm_operator_poll_msg_set(C, "Operation is not allowed in edit mode"); + return false; + } + + Object *ob = ED_object_context(C); + ID *data = ob ? static_cast<ID *>(ob->data) : nullptr; + + if (BKE_id_attributes_active_color_get(data) != nullptr) { + return true; + } + + return false; +} + +void GEOMETRY_OT_color_attribute_duplicate(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Duplicate Color Attribute"; + ot->description = "Duplicate color attribute"; + ot->idname = "GEOMETRY_OT_color_attribute_duplicate"; + + /* api callbacks */ + ot->exec = geometry_color_attribute_duplicate_exec; + ot->poll = geometry_color_attributes_duplicate_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + static void geometry_attribute_convert_ui(bContext *UNUSED(C), wmOperator *op) { uiLayout *layout = op->layout; @@ -572,3 +634,38 @@ void GEOMETRY_OT_attribute_convert(wmOperatorType *ot) } } // namespace blender::ed::geometry + +using blender::CPPType; +using blender::GVArray; + +bool ED_geometry_attribute_convert(Mesh *mesh, + const char *layer_name, + eCustomDataType old_type, + eAttrDomain old_domain, + eCustomDataType new_type, + eAttrDomain new_domain) +{ + CustomDataLayer *layer = BKE_id_attribute_find(&mesh->id, layer_name, old_type, old_domain); + const std::string name = layer->name; + + if (!layer) { + return false; + } + + MeshComponent mesh_component; + mesh_component.replace(mesh, GeometryOwnershipType::Editable); + GVArray src_varray = mesh_component.attribute_get_for_read(name, new_domain, new_type); + + const CPPType &cpp_type = src_varray.type(); + void *new_data = MEM_malloc_arrayN(src_varray.size(), cpp_type.size(), __func__); + src_varray.materialize_to_uninitialized(new_data); + mesh_component.attribute_try_delete(name); + mesh_component.attribute_try_create(name, new_domain, new_type, AttributeInitMove(new_data)); + + int *active_index = BKE_id_attributes_active_index_p(&mesh->id); + if (*active_index > 0) { + *active_index -= 1; + } + + return true; +} diff --git a/source/blender/editors/geometry/geometry_intern.hh b/source/blender/editors/geometry/geometry_intern.hh index bbcb682d6bf..a1000a5d01f 100644 --- a/source/blender/editors/geometry/geometry_intern.hh +++ b/source/blender/editors/geometry/geometry_intern.hh @@ -17,6 +17,7 @@ void GEOMETRY_OT_attribute_remove(struct wmOperatorType *ot); void GEOMETRY_OT_color_attribute_add(struct wmOperatorType *ot); void GEOMETRY_OT_color_attribute_remove(struct wmOperatorType *ot); void GEOMETRY_OT_color_attribute_render_set(struct wmOperatorType *ot); +void GEOMETRY_OT_color_attribute_duplicate(struct wmOperatorType *ot); void GEOMETRY_OT_attribute_convert(struct wmOperatorType *ot); } // namespace blender::ed::geometry diff --git a/source/blender/editors/geometry/geometry_ops.cc b/source/blender/editors/geometry/geometry_ops.cc index 23f6e6f29f4..acac757ecf1 100644 --- a/source/blender/editors/geometry/geometry_ops.cc +++ b/source/blender/editors/geometry/geometry_ops.cc @@ -22,5 +22,6 @@ void ED_operatortypes_geometry(void) WM_operatortype_append(GEOMETRY_OT_color_attribute_add); WM_operatortype_append(GEOMETRY_OT_color_attribute_remove); WM_operatortype_append(GEOMETRY_OT_color_attribute_render_set); + WM_operatortype_append(GEOMETRY_OT_color_attribute_duplicate); WM_operatortype_append(GEOMETRY_OT_attribute_convert); } diff --git a/source/blender/editors/gizmo_library/geometry/geom_arrow_gizmo.c b/source/blender/editors/gizmo_library/geometry/geom_arrow_gizmo.c index 5b514e02099..429bf39234f 100644 --- a/source/blender/editors/gizmo_library/geometry/geom_arrow_gizmo.c +++ b/source/blender/editors/gizmo_library/geometry/geom_arrow_gizmo.c @@ -7,6 +7,8 @@ #include "../gizmo_geometry.h" +/* The numerical values in the `verts` array are used in arrow3d_gizmo.c + * If you change this mesh geometry, update the selection code also. */ static float verts[][3] = { {-0.000000, 0.012320, 0.000000}, {-0.000000, 0.012320, 0.974306}, {0.008711, 0.008711, 0.000000}, {0.008711, 0.008711, 0.974306}, diff --git a/source/blender/editors/gizmo_library/gizmo_draw_utils.c b/source/blender/editors/gizmo_library/gizmo_draw_utils.c index c6303c197e7..178a2b52b62 100644 --- a/source/blender/editors/gizmo_library/gizmo_draw_utils.c +++ b/source/blender/editors/gizmo_library/gizmo_draw_utils.c @@ -95,7 +95,7 @@ void wm_gizmo_vec_draw( immEnd(); } else if (primitive_type == GPU_PRIM_TRI_FAN) { - /* Note(Metal): Tri-fan alternative for Metal. Triangle List is more efficient for small + /* NOTE(Metal): Tri-fan alternative for Metal. Triangle List is more efficient for small * primitive counts. */ int tri_count = vert_count - 2; immBegin(GPU_PRIM_TRIS, tri_count * 3); 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 a1fb0e205b1..cde702294d0 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c @@ -50,16 +50,19 @@ /* to use custom arrows exported to geom_arrow_gizmo.c */ //#define USE_GIZMO_CUSTOM_ARROWS -/** Margins to add when selecting the arrow stem. */ -#define ARROW_SELECT_THRESHOLD_PX_STEM (5 * UI_DPI_FAC) -/** Margins to add when selecting the arrow head. */ -#define ARROW_SELECT_THRESHOLD_PX_HEAD (12 * UI_DPI_FAC) +/* Margin to add when selecting the arrow. */ +#define ARROW_SELECT_THRESHOLD_PX (5) typedef struct ArrowGizmo3D { wmGizmo gizmo; GizmoCommonData data; } ArrowGizmo3D; +typedef struct ArrowGizmoInteraction { + GizmoInteraction inter; + float init_arrow_length; +} ArrowGizmoInteraction; + /* -------------------------------------------------------------------- */ static void gizmo_arrow_matrix_basis_get(const wmGizmo *gz, float r_matrix[4][4]) @@ -70,7 +73,10 @@ static void gizmo_arrow_matrix_basis_get(const wmGizmo *gz, float r_matrix[4][4] madd_v3_v3fl(r_matrix[3], arrow->gizmo.matrix_basis[2], arrow->data.offset); } -static void arrow_draw_geom(const ArrowGizmo3D *arrow, const bool select, const float color[4]) +static void arrow_draw_geom(const ArrowGizmo3D *arrow, + const bool select, + const float color[4], + const float arrow_length) { uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); bool unbind_shader = true; @@ -113,16 +119,14 @@ static void arrow_draw_geom(const ArrowGizmo3D *arrow, const bool select, const #ifdef USE_GIZMO_CUSTOM_ARROWS wm_gizmo_geometryinfo_draw(&wm_gizmo_geom_data_arrow, select, color); #else - const float arrow_length = RNA_float_get(arrow->gizmo.ptr, "length"); - const float vec[2][3] = { {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, arrow_length}, }; if (draw_options & ED_GIZMO_ARROW_DRAW_FLAG_STEM) { - const float stem_width = (arrow->gizmo.line_width * U.pixelsize) + - (select ? ARROW_SELECT_THRESHOLD_PX_STEM : 0); + const float stem_width = arrow->gizmo.line_width * U.pixelsize + + (select ? ARROW_SELECT_THRESHOLD_PX * U.dpi_fac : 0); immUniform1f("lineWidth", stem_width); wm_gizmo_vec_draw(color, vec, ARRAY_SIZE(vec), pos, GPU_PRIM_LINE_STRIP); } @@ -134,7 +138,7 @@ static void arrow_draw_geom(const ArrowGizmo3D *arrow, const bool select, const GPU_matrix_push(); - /* NOTE: ideally #ARROW_SELECT_THRESHOLD_PX_HEAD would be added here, however adding a + /* NOTE: ideally #ARROW_SELECT_THRESHOLD_PX would be added here, however adding a * margin in pixel space isn't so simple, nor is it as important as for the arrow stem. */ if (draw_style == ED_GIZMO_ARROW_STYLE_BOX) { const float size = 0.05f; @@ -178,6 +182,7 @@ static void arrow_draw_geom(const ArrowGizmo3D *arrow, const bool select, const static void arrow_draw_intern(ArrowGizmo3D *arrow, const bool select, const bool highlight) { wmGizmo *gz = &arrow->gizmo; + const float arrow_length = RNA_float_get(gz->ptr, "length"); float color[4]; float matrix_final[4][4]; @@ -188,19 +193,20 @@ static void arrow_draw_intern(ArrowGizmo3D *arrow, const bool select, const bool GPU_matrix_push(); GPU_matrix_mul(matrix_final); GPU_blend(GPU_BLEND_ALPHA); - arrow_draw_geom(arrow, select, color); + arrow_draw_geom(arrow, select, color, arrow_length); GPU_blend(GPU_BLEND_NONE); GPU_matrix_pop(); if (gz->interaction_data) { - GizmoInteraction *inter = gz->interaction_data; + ArrowGizmoInteraction *arrow_inter = gz->interaction_data; GPU_matrix_push(); - GPU_matrix_mul(inter->init_matrix_final); + GPU_matrix_mul(arrow_inter->inter.init_matrix_final); GPU_blend(GPU_BLEND_ALPHA); - arrow_draw_geom(arrow, select, (const float[4]){0.5f, 0.5f, 0.5f, 0.5f}); + arrow_draw_geom( + arrow, select, (const float[4]){0.5f, 0.5f, 0.5f, 0.5f}, arrow_inter->init_arrow_length); GPU_blend(GPU_BLEND_NONE); GPU_matrix_pop(); @@ -223,9 +229,15 @@ static void gizmo_arrow_draw(const bContext *UNUSED(C), wmGizmo *gz) */ static int gizmo_arrow_test_select(bContext *UNUSED(C), wmGizmo *gz, const int mval[2]) { + /* This following values are based on manual inspection of `verts[]` defined in + * geom_arrow_gizmo.c */ + const float head_center_z = (0.974306f + 1.268098f) / 2; + const float head_geo_x = 0.051304f; + const float stem_geo_x = 0.012320f; + /* Project into 2D space since it simplifies pixel threshold tests. */ ArrowGizmo3D *arrow = (ArrowGizmo3D *)gz; - const float arrow_length = RNA_float_get(arrow->gizmo.ptr, "length"); + const float arrow_length = RNA_float_get(arrow->gizmo.ptr, "length") * head_center_z; float matrix_final[4][4]; WM_gizmo_calc_matrix_final(gz, matrix_final); @@ -239,12 +251,15 @@ static int gizmo_arrow_test_select(bContext *UNUSED(C), wmGizmo *gz, const int m copy_v2_v2(arrow_end, co); } + const float scale_final = mat4_to_scale(matrix_final); + const float head_width = ARROW_SELECT_THRESHOLD_PX * scale_final * head_geo_x; + const float stem_width = ARROW_SELECT_THRESHOLD_PX * scale_final * stem_geo_x; + float select_threshold_base = gz->line_width * U.pixelsize; + const float mval_fl[2] = {UNPACK2(mval)}; - const float arrow_stem_threshold_px = ARROW_SELECT_THRESHOLD_PX_STEM; - const float arrow_head_threshold_px = ARROW_SELECT_THRESHOLD_PX_HEAD; /* Distance to arrow head. */ - if (len_squared_v2v2(mval_fl, arrow_end) < square_f(arrow_head_threshold_px)) { + if (len_squared_v2v2(mval_fl, arrow_end) < square_f(select_threshold_base + head_width)) { return 0; } @@ -253,8 +268,8 @@ static int gizmo_arrow_test_select(bContext *UNUSED(C), wmGizmo *gz, const int m const float lambda = closest_to_line_v2(co_isect, mval_fl, arrow_start, arrow_end); /* Clamp inside the line, to avoid overlapping with other gizmos, * especially around the start of the arrow. */ - if (lambda >= 0.0 && lambda <= 1.0) { - if (len_squared_v2v2(mval_fl, co_isect) < square_f(arrow_stem_threshold_px)) { + if (lambda >= 0.0f && lambda <= 1.0f) { + if (len_squared_v2v2(mval_fl, co_isect) < square_f(select_threshold_base + stem_width)) { return 0; } } @@ -373,7 +388,7 @@ static void gizmo_arrow_setup(wmGizmo *gz) static int gizmo_arrow_invoke(bContext *UNUSED(C), wmGizmo *gz, const wmEvent *event) { ArrowGizmo3D *arrow = (ArrowGizmo3D *)gz; - GizmoInteraction *inter = MEM_callocN(sizeof(GizmoInteraction), __func__); + GizmoInteraction *inter = MEM_callocN(sizeof(ArrowGizmoInteraction), __func__); wmGizmoProperty *gz_prop = WM_gizmo_target_property_find(gz, "offset"); /* Some gizmos don't use properties. */ @@ -389,6 +404,8 @@ static int gizmo_arrow_invoke(bContext *UNUSED(C), wmGizmo *gz, const wmEvent *e gizmo_arrow_matrix_basis_get(gz, inter->init_matrix_basis); WM_gizmo_calc_matrix_final(gz, inter->init_matrix_final); + ((ArrowGizmoInteraction *)inter)->init_arrow_length = RNA_float_get(gz->ptr, "length"); + gz->interaction_data = inter; return OPERATOR_RUNNING_MODAL; @@ -513,7 +530,8 @@ static void GIZMO_GT_arrow_3d(wmGizmoType *gzt) ""); RNA_def_enum_flag(gzt->srna, "transform", rna_enum_transform_items, 0, "Transform", ""); - RNA_def_float(gzt->srna, "length", 1.0f, 0.0f, FLT_MAX, "Arrow Line Length", "", 0.0f, FLT_MAX); + RNA_def_float( + gzt->srna, "length", 1.0f, -FLT_MAX, FLT_MAX, "Arrow Line Length", "", -FLT_MAX, FLT_MAX); RNA_def_float_vector( gzt->srna, "aspect", 2, NULL, 0, FLT_MAX, "Aspect", "Cone/box style only", 0.0f, FLT_MAX); diff --git a/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c index b326d6d1859..11309402220 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c @@ -57,8 +57,6 @@ typedef struct ButtonGizmo2D { GPUBatch *shape_batch[2]; } ButtonGizmo2D; -#define CIRCLE_RESOLUTION 32 - /** \} */ /* -------------------------------------------------------------------- */ @@ -68,27 +66,33 @@ typedef struct ButtonGizmo2D { static void button2d_geom_draw_backdrop(const wmGizmo *gz, const float color[4], const float fill_alpha, - const bool select) + const bool select, + const float screen_scale) { float viewport[4]; GPU_viewport_size_get_f(viewport); + const float max_pixel_error = 0.25f; + int nsegments = (int)(ceilf(M_PI / acosf(1.0f - max_pixel_error / screen_scale))); + nsegments = max_ff(nsegments, 8); + nsegments = min_ff(nsegments, 1000); + GPUVertFormat *format = immVertexFormat(); - /* Note(Metal): Prefer 3D coordinate for 2D rendering when using 3D shader. */ + /* NOTE(Metal): Prefer 3D coordinate for 2D rendering when using 3D shader. */ uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); /* TODO: other draw styles. */ if (color[3] == 1.0 && fill_alpha == 1.0 && select == false) { immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); immUniformColor4fv(color); - imm_draw_circle_fill_3d(pos, 0.0f, 0.0f, 1.0f, CIRCLE_RESOLUTION); + imm_draw_circle_fill_3d(pos, 0.0f, 0.0f, 1.0f, nsegments); immUnbindProgram(); immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR); immUniform2fv("viewportSize", &viewport[2]); immUniform1f("lineWidth", gz->line_width * U.pixelsize); immUniformColor4fv(color); - imm_draw_circle_wire_3d(pos, 0.0f, 0.0f, 1.0f, CIRCLE_RESOLUTION); + imm_draw_circle_wire_3d(pos, 0.0f, 0.0f, 1.0f, nsegments); immUnbindProgram(); } else { @@ -97,7 +101,7 @@ static void button2d_geom_draw_backdrop(const wmGizmo *gz, const float fill_color[4] = {UNPACK3(color), fill_alpha * color[3]}; immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); immUniformColor4fv(fill_color); - imm_draw_circle_fill_3d(pos, 0.0f, 0.0f, 1.0f, CIRCLE_RESOLUTION); + imm_draw_circle_fill_3d(pos, 0.0f, 0.0f, 1.0f, nsegments); immUnbindProgram(); } @@ -107,7 +111,7 @@ static void button2d_geom_draw_backdrop(const wmGizmo *gz, immUniform2fv("viewportSize", &viewport[2]); immUniform1f("lineWidth", gz->line_width * U.pixelsize); immUniformColor4fv(color); - imm_draw_circle_wire_3d(pos, 0.0f, 0.0f, 1.0f, CIRCLE_RESOLUTION); + imm_draw_circle_wire_3d(pos, 0.0f, 0.0f, 1.0f, nsegments); immUnbindProgram(); } } @@ -184,9 +188,11 @@ static void button2d_draw_intern(const bContext *C, GPU_matrix_mul(matrix_align); } + const float screen_scale = mat4_to_scale(matrix_final); + if (select) { BLI_assert(is_3d); - button2d_geom_draw_backdrop(gz, color, 1.0, select); + button2d_geom_draw_backdrop(gz, color, 1.0, select, screen_scale); } else { @@ -194,7 +200,7 @@ static void button2d_draw_intern(const bContext *C, if (draw_options & ED_GIZMO_BUTTON_SHOW_BACKDROP) { const float fill_alpha = RNA_float_get(gz->ptr, "backdrop_fill_alpha"); - button2d_geom_draw_backdrop(gz, color, fill_alpha, select); + button2d_geom_draw_backdrop(gz, color, fill_alpha, select, screen_scale); } if (button->shape_batch[0] != NULL) { @@ -315,10 +321,11 @@ static int gizmo_button2d_cursor_get(wmGizmo *gz) return WM_CURSOR_DEFAULT; } +#define CIRCLE_RESOLUTION_3D 32 static bool gizmo_button2d_bounds(bContext *C, wmGizmo *gz, rcti *r_bounding_box) { ScrArea *area = CTX_wm_area(C); - float rad = CIRCLE_RESOLUTION * U.dpi_fac / 2.0f; + float rad = CIRCLE_RESOLUTION_3D * U.dpi_fac / 2.0f; const float *co = NULL; float matrix_final[4][4]; float co_proj[3]; @@ -342,6 +349,7 @@ static bool gizmo_button2d_bounds(bContext *C, wmGizmo *gz, rcti *r_bounding_box } } else { + rad = mat4_to_scale(matrix_final); co = matrix_final[3]; } diff --git a/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c index a76242404ba..9b7b157dc7e 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c @@ -77,6 +77,21 @@ typedef struct DialInteraction { #define DIAL_CLIP_BIAS 0.02 /* -------------------------------------------------------------------- */ +struct Dial3dParams { + int draw_options; + float angle_ofs; + float angle_delta; + float angle_increment; + float arc_partial_angle; + float arc_inner_factor; + float *clip_plane; +}; +static void dial_3d_draw_util(const float matrix_basis[4][4], + const float matrix_final[4][4], + const float line_width, + const float color[4], + const bool select, + struct Dial3dParams *params); static void dial_geom_draw(const float color[4], const float line_width, @@ -96,7 +111,7 @@ static void dial_geom_draw(const float color[4], ED_GIZMO_DIAL_DRAW_FLAG_FILL))); GPUVertFormat *format = immVertexFormat(); - /* Note(Metal): Prefer using 3D coordinates with 3D shader, even if rendering 2D gizmo's. */ + /* NOTE(Metal): Prefer using 3D coordinates with 3D shader, even if rendering 2D gizmo's. */ uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); if (clip_plane) { @@ -411,23 +426,26 @@ static void dial_draw_intern( if (WM_gizmo_target_property_is_valid(gz_prop)) { angle_delta = WM_gizmo_target_property_float_get(gz, gz_prop); } + if (gz->state & WM_GIZMO_STATE_MODAL) { + angle_increment = RNA_float_get(gz->ptr, "incremental_angle"); + } } } - ED_gizmotypes_dial_3d_draw_util(gz->matrix_basis, - matrix_final, - gz->line_width, - color, - select, - &(struct Dial3dParams){ - .draw_options = draw_options, - .angle_ofs = angle_ofs, - .angle_delta = angle_delta, - .angle_increment = angle_increment, - .arc_partial_angle = arc_partial_angle, - .arc_inner_factor = arc_inner_factor, - .clip_plane = clip_plane, - }); + dial_3d_draw_util(gz->matrix_basis, + matrix_final, + gz->line_width, + color, + select, + &(struct Dial3dParams){ + .draw_options = draw_options, + .angle_ofs = angle_ofs, + .angle_delta = angle_delta, + .angle_increment = angle_increment, + .arc_partial_angle = arc_partial_angle, + .arc_inner_factor = arc_inner_factor, + .clip_plane = clip_plane, + }); } static void gizmo_dial_draw_select(const bContext *C, wmGizmo *gz, int select_id) @@ -479,6 +497,10 @@ static int gizmo_dial_modal(bContext *C, eWM_GizmoFlagTweak tweak_flag) { DialInteraction *inter = gz->interaction_data; + if (!inter) { + return OPERATOR_CANCELLED; + } + if ((event->type != MOUSEMOVE) && (inter->prev.tweak_flag == tweak_flag)) { return OPERATOR_RUNNING_MODAL; } @@ -519,30 +541,33 @@ static int gizmo_dial_modal(bContext *C, static void gizmo_dial_exit(bContext *C, wmGizmo *gz, const bool cancel) { DialInteraction *inter = gz->interaction_data; - bool use_reset_value = false; - float reset_value = 0.0f; - if (cancel) { - /* Set the property for the operator and call its modal function. */ - wmGizmoProperty *gz_prop = WM_gizmo_target_property_find(gz, "offset"); - if (WM_gizmo_target_property_is_valid(gz_prop)) { - use_reset_value = true; - reset_value = inter->init.prop_angle; - } - } - else { - if (inter->has_drag == false) { - PropertyRNA *prop = RNA_struct_find_property(gz->ptr, "click_value"); - if (RNA_property_is_set(gz->ptr, prop)) { + if (inter) { + bool use_reset_value = false; + float reset_value = 0.0f; + + if (cancel) { + /* Set the property for the operator and call its modal function. */ + wmGizmoProperty *gz_prop = WM_gizmo_target_property_find(gz, "offset"); + if (WM_gizmo_target_property_is_valid(gz_prop)) { use_reset_value = true; - reset_value = RNA_property_float_get(gz->ptr, prop); + reset_value = inter->init.prop_angle; + } + } + else { + if (inter->has_drag == false) { + PropertyRNA *prop = RNA_struct_find_property(gz->ptr, "click_value"); + if (RNA_property_is_set(gz->ptr, prop)) { + use_reset_value = true; + reset_value = RNA_property_float_get(gz->ptr, prop); + } } } - } - if (use_reset_value) { - wmGizmoProperty *gz_prop = WM_gizmo_target_property_find(gz, "offset"); - if (WM_gizmo_target_property_is_valid(gz_prop)) { - WM_gizmo_target_property_float_set(C, gz, gz_prop, reset_value); + if (use_reset_value) { + wmGizmoProperty *gz_prop = WM_gizmo_target_property_find(gz, "offset"); + if (WM_gizmo_target_property_is_valid(gz_prop)) { + WM_gizmo_target_property_float_set(C, gz, gz_prop, reset_value); + } } } @@ -564,6 +589,11 @@ static void gizmo_dial_setup(wmGizmo *gz) static int gizmo_dial_invoke(bContext *UNUSED(C), wmGizmo *gz, const wmEvent *event) { + if (gz->custom_modal) { + /* #DialInteraction is only used for the inner modal. */ + return OPERATOR_RUNNING_MODAL; + } + DialInteraction *inter = MEM_callocN(sizeof(DialInteraction), __func__); inter->init.mval[0] = event->mval[0]; @@ -583,12 +613,12 @@ static int gizmo_dial_invoke(bContext *UNUSED(C), wmGizmo *gz, const wmEvent *ev /** \name Dial Gizmo API * \{ */ -void ED_gizmotypes_dial_3d_draw_util(const float matrix_basis[4][4], - const float matrix_final[4][4], - const float line_width, - const float color[4], - const bool select, - struct Dial3dParams *params) +static void dial_3d_draw_util(const float matrix_basis[4][4], + const float matrix_final[4][4], + const float line_width, + const float color[4], + const bool select, + struct Dial3dParams *params) { GPU_matrix_push(); GPU_matrix_mul(matrix_final); 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 5fb1173521a..1ce67185c1e 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c @@ -98,7 +98,7 @@ static void move_geom_draw(const wmGizmo *gz, ED_GIZMO_MOVE_DRAW_FLAG_FILL))); GPUVertFormat *format = immVertexFormat(); - /* Note(Metal): Prefer using 3D coordinates with 3D shader, even if rendering 2D gizmo's. */ + /* NOTE(Metal): Prefer using 3D coordinates with 3D shader, even if rendering 2D gizmo's. */ uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); immBindBuiltinProgram(filled ? GPU_SHADER_3D_UNIFORM_COLOR : @@ -280,7 +280,7 @@ static int gizmo_move_modal(bContext *C, CTX_wm_view3d(C), (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE), &(const struct SnapObjectParams){ - .snap_select = SNAP_ALL, + .snap_target_select = SCE_SNAP_TARGET_ALL, .edit_mode_type = SNAP_GEOM_EDIT, .use_occlusion_test = true, }, diff --git a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c index 1659afb1254..d468906f127 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c @@ -96,7 +96,7 @@ void ED_gizmotypes_snap_3d_data_get(const struct bContext *C, float r_loc[3], float r_nor[3], int r_elem_index[3], - int *r_snap_elem) + eSnapMode *r_snap_elem) { if (C) { /* Snap values are updated too late at the cursor. Be sure to update ahead of time. */ @@ -283,7 +283,7 @@ static int snap_gizmo_test_select(bContext *C, wmGizmo *gz, const int mval[2]) ED_view3d_cursor_snap_data_update(snap_gizmo->snap_state, C, x, y); V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get(); - if (snap_data->snap_elem) { + if (snap_data->snap_elem != SCE_SNAP_MODE_NONE) { return 0; } return -1; diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c index 011a5a66695..24de50db397 100644 --- a/source/blender/editors/gpencil/drawgpencil.c +++ b/source/blender/editors/gpencil/drawgpencil.c @@ -80,9 +80,9 @@ typedef enum eDrawStrokeFlags { GP_DRAWDATA_ONLYI2D = (1 << 3), /** special hack for drawing strokes in Image Editor (weird coordinates) */ GP_DRAWDATA_IEDITHACK = (1 << 4), - /** don't draw xray in 3D view (which is default) */ + /** Don't draw XRAY in 3D view (which is default). */ GP_DRAWDATA_NO_XRAY = (1 << 5), - /** no onionskins should be drawn (for animation playback) */ + /** No onion-skins should be drawn (for animation playback). */ GP_DRAWDATA_NO_ONIONS = (1 << 6), /** draw strokes as "volumetric" circular billboards */ GP_DRAWDATA_VOLUMETRIC = (1 << 7), diff --git a/source/blender/editors/gpencil/gpencil_bake_animation.cc b/source/blender/editors/gpencil/gpencil_bake_animation.cc index dfc74f6d225..66f53bea326 100644 --- a/source/blender/editors/gpencil/gpencil_bake_animation.cc +++ b/source/blender/editors/gpencil/gpencil_bake_animation.cc @@ -320,6 +320,7 @@ static int gpencil_bake_grease_pencil_animation_exec(bContext *C, wmOperator *op /* Update point location to new object space. */ for (int j = 0; j < gps->totpoints; j++) { bGPDspoint *pt = &gps->points[j]; + mul_m4_v3(ob_eval->obmat, &pt->x); mul_m4_v3(invmat, &pt->x); } diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c index 4f9468cc9c4..0601d009bf7 100644 --- a/source/blender/editors/gpencil/gpencil_convert.c +++ b/source/blender/editors/gpencil/gpencil_convert.c @@ -1104,7 +1104,7 @@ static void gpencil_stroke_to_bezier(bContext *C, rad_fac, minmax_weights); - /* shift coord vects */ + /* Shift coord vectors. */ copy_v3_v3(p3d_prev, p3d_cur); copy_v3_v3(p3d_cur, p3d_next); diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 3c8e6d6e5f5..5028baf1589 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -1811,7 +1811,16 @@ static int gpencil_move_to_layer_exec(bContext *C, wmOperator *op) } else { /* Create a new layer. */ - target_layer = BKE_gpencil_layer_addnew(gpd, "GP_Layer", true, false); + PropertyRNA *prop; + char name[128]; + prop = RNA_struct_find_property(op->ptr, "new_layer_name"); + if (RNA_property_is_set(op->ptr, prop)) { + RNA_property_string_get(op->ptr, prop, name); + } + else { + strcpy(name, "GP_Layer"); + } + target_layer = BKE_gpencil_layer_addnew(gpd, name, true, false); } if (target_layer == NULL) { @@ -1888,8 +1897,46 @@ static int gpencil_move_to_layer_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +static void layer_new_name_get(bGPdata *gpd, char *rname) +{ + int index = 0; + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + if (strstr(gpl->info, "GP_Layer")) { + index++; + } + } + + if (index == 0) { + BLI_strncpy(rname, "GP_Layer", 128); + return; + } + char *name = BLI_sprintfN("%.*s.%03d", 128, "GP_Layer", index); + BLI_strncpy(rname, name, 128); + MEM_freeN(name); +} + +static int gpencil_move_to_layer_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + Object *ob = CTX_data_active_object(C); + PropertyRNA *prop; + if (RNA_int_get(op->ptr, "layer") == -1) { + prop = RNA_struct_find_property(op->ptr, "new_layer_name"); + if (!RNA_property_is_set(op->ptr, prop)) { + char name[MAX_NAME]; + bGPdata *gpd = ob->data; + layer_new_name_get(gpd, name); + RNA_property_string_set(op->ptr, prop, name); + return WM_operator_props_dialog_popup(C, op, 200); + } + } + + return gpencil_move_to_layer_exec(C, op); +} + void GPENCIL_OT_move_to_layer(wmOperatorType *ot) { + PropertyRNA *prop; + /* identifiers */ ot->name = "Move Strokes to Layer"; ot->idname = "GPENCIL_OT_move_to_layer"; @@ -1898,15 +1945,20 @@ void GPENCIL_OT_move_to_layer(wmOperatorType *ot) /* callbacks */ ot->exec = gpencil_move_to_layer_exec; - ot->poll = gpencil_stroke_edit_poll; /* XXX? */ + ot->invoke = gpencil_move_to_layer_invoke; + ot->poll = gpencil_stroke_edit_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* GPencil layer to use. */ - ot->prop = RNA_def_int( - ot->srna, "layer", 0, -1, INT_MAX, "Grease Pencil Layer", "", -1, INT_MAX); - RNA_def_property_flag(ot->prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_int(ot->srna, "layer", 0, -1, INT_MAX, "Grease Pencil Layer", "", -1, INT_MAX); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + + prop = RNA_def_string( + ot->srna, "new_layer_name", NULL, MAX_NAME, "Name", "Name of the newly added layer"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + ot->prop = prop; } /** \} */ diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index 56c94e4494d..d656241c463 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -726,16 +726,16 @@ struct GP_EditableStrokes_Iter { (void)0 /** - * Iterate over all editable editcurves in the current context, - * stopping on each usable layer + stroke + curve pair (i.e. gpl, gps and gpc) + * Iterate over all editable edit-curves in the current context, + * stopping on each usable layer + stroke + curve pair (i.e. `gpl`, `gps` and `gpc`) * to perform some operations on the curve. * * \param gpl: The identifier to use for the layer of the stroke being processed. - * Choose a suitable value to avoid name clashes. + * Choose a suitable value to avoid name clashes. * \param gps: The identifier to use for current stroke being processed. - * Choose a suitable value to avoid name clashes. + * Choose a suitable value to avoid name clashes. * \param gpc: The identifier to use for current editcurve being processed. - * Choose a suitable value to avoid name clashes. + * Choose a suitable value to avoid name clashes. */ #define GP_EDITABLE_CURVES_BEGIN(gpstroke_iter, C, gpl, gps, gpc) \ { \ diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c index 8630b7f23d4..0039dbae674 100644 --- a/source/blender/editors/gpencil/gpencil_interpolate.c +++ b/source/blender/editors/gpencil/gpencil_interpolate.c @@ -1482,8 +1482,8 @@ void GPENCIL_OT_interpolate_sequence(wmOperatorType *ot) * Changes here will likely apply there too. */ static const EnumPropertyItem gpencil_interpolation_type_items[] = { - /* interpolation */ - {0, "", 0, N_("Interpolation"), "Standard transitions between keyframes"}, + /* Interpolation. */ + RNA_ENUM_ITEM_HEADING(N_("Interpolation"), "Standard transitions between keyframes"), {GP_IPO_LINEAR, "LINEAR", ICON_IPO_LINEAR, @@ -1495,13 +1495,10 @@ void GPENCIL_OT_interpolate_sequence(wmOperatorType *ot) "Custom", "Custom interpolation defined using a curve map"}, - /* easing */ - {0, - "", - 0, - N_("Easing (by strength)"), - "Predefined inertial transitions, useful for motion graphics (from least to most " - "''dramatic'')"}, + /* Easing. */ + RNA_ENUM_ITEM_HEADING(N_("Easing (by strength)"), + "Predefined inertial transitions, useful for motion graphics " + "(from least to most \"dramatic\")"), {GP_IPO_SINE, "SINE", ICON_IPO_SINE, @@ -1518,7 +1515,7 @@ void GPENCIL_OT_interpolate_sequence(wmOperatorType *ot) "Circular", "Circular easing (strongest and most dynamic)"}, - {0, "", 0, N_("Dynamic Effects"), "Simple physics-inspired easing effects"}, + RNA_ENUM_ITEM_HEADING(N_("Dynamic Effects"), "Simple physics-inspired easing effects"), {GP_IPO_BACK, "BACK", ICON_IPO_BACK, "Back", "Cubic easing with overshoot and settle"}, {GP_IPO_BOUNCE, "BOUNCE", diff --git a/source/blender/editors/gpencil/gpencil_sculpt_paint.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c index 01ac02a9a1d..6d0848de36d 100644 --- a/source/blender/editors/gpencil/gpencil_sculpt_paint.c +++ b/source/blender/editors/gpencil/gpencil_sculpt_paint.c @@ -829,7 +829,7 @@ static bool gpencil_brush_randomize_apply(tGP_BrushEditData *gso, mul_v2_fl(svec, fac); } - /* convert to dataspace */ + /* Convert to data-space. */ if (gps->flag & GP_STROKE_3DSPACE) { /* 3D: Project to 3D space */ bool flip; diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c index c4fd34212c3..e903d11a1e0 100644 --- a/source/blender/editors/gpencil/gpencil_select.c +++ b/source/blender/editors/gpencil/gpencil_select.c @@ -1701,7 +1701,7 @@ static int gpencil_circle_select_exec(bContext *C, wmOperator *op) const bool select = (sel_op != SEL_OP_SUB); bool changed = false; - /* for bounding rect around circle (for quicky intersection testing) */ + /* For bounding `rect` around circle (for quickly intersection testing). */ rcti rect = {0}; rect.xmin = mx - radius; rect.ymin = my - radius; @@ -2681,6 +2681,7 @@ void GPENCIL_OT_select(wmOperatorType *ot) ot->invoke = gpencil_select_invoke; ot->exec = gpencil_select_exec; ot->poll = gpencil_select_poll; + ot->get_name = ED_select_pick_get_name; /* flag */ ot->flag = OPTYPE_UNDO; diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index 2dc12125f40..7b659511aaa 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -388,6 +388,17 @@ 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 */ @@ -405,17 +416,6 @@ 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; @@ -525,7 +525,7 @@ bool ED_gpencil_stroke_can_use_direct(const ScrArea *area, const bGPDstroke *gps return (area->spacetype == SPACE_IMAGE); } if (gps->flag & GP_STROKE_2DSPACE) { - /* 2D strokes (dataspace) - for any 2D view (i.e. everything other than 3D view) */ + /* 2D strokes (data-space) - for any 2D view (i.e. everything other than 3D view). */ return (area->spacetype != SPACE_VIEW3D); } /* view aligned - anything goes */ @@ -1149,7 +1149,7 @@ void ED_gpencil_stroke_reproject(Depsgraph *depsgraph, depsgraph, v3d, &(const struct SnapObjectParams){ - .snap_select = SNAP_ALL, + .snap_target_select = SCE_SNAP_TARGET_ALL, }, &ray_start[0], &ray_normal[0], @@ -1747,7 +1747,7 @@ static void gpencil_brush_cursor_draw(bContext *C, int x, int y, void *customdat float darkcolor[3]; float radius = 3.0f; - int mval_i[2] = {x, y}; + const int mval_i[2] = {x, y}; /* Check if cursor is in drawing region and has valid data-block. */ if ((!gpencil_check_cursor_region(C, mval_i)) || (gpd == NULL)) { return; @@ -2962,7 +2962,7 @@ void ED_gpencil_projected_2d_bound_box(const GP_SpaceConversion *gsc, bool ED_gpencil_stroke_check_collision(const GP_SpaceConversion *gsc, bGPDstroke *gps, - const float mouse[2], + const float mval[2], const int radius, const float diff_mat[4][4]) { @@ -2980,7 +2980,7 @@ bool ED_gpencil_stroke_check_collision(const GP_SpaceConversion *gsc, rcti rect_stroke = {boundbox_min[0], boundbox_max[0], boundbox_min[1], boundbox_max[1]}; /* For mouse, add a small offset to avoid false negative in corners. */ - rcti rect_mouse = {mouse[0] - offset, mouse[0] + offset, mouse[1] - offset, mouse[1] + offset}; + rcti rect_mouse = {mval[0] - offset, mval[0] + offset, mval[1] - offset, mval[1] + offset}; /* Check collision between both rectangles. */ return BLI_rcti_isect(&rect_stroke, &rect_mouse, NULL); @@ -2988,7 +2988,7 @@ bool ED_gpencil_stroke_check_collision(const GP_SpaceConversion *gsc, bool ED_gpencil_stroke_point_is_inside(const bGPDstroke *gps, const GP_SpaceConversion *gsc, - const int mouse[2], + const int mval[2], const float diff_mat[4][4]) { bool hit = false; @@ -3014,9 +3014,8 @@ bool ED_gpencil_stroke_point_is_inside(const bGPDstroke *gps, BLI_lasso_boundbox(&rect, mcoords, len); /* Test if point inside stroke. */ - hit = ((!ELEM(V2D_IS_CLIPPED, mouse[0], mouse[1])) && - BLI_rcti_isect_pt(&rect, mouse[0], mouse[1]) && - BLI_lasso_is_point_inside(mcoords, len, mouse[0], mouse[1], INT_MAX)); + hit = ((!ELEM(V2D_IS_CLIPPED, mval[0], mval[1])) && BLI_rcti_isect_pt(&rect, mval[0], mval[1]) && + BLI_lasso_is_point_inside(mcoords, len, mval[0], mval[1], INT_MAX)); /* Free memory. */ MEM_SAFE_FREE(mcoords); @@ -3077,6 +3076,11 @@ bGPDstroke *ED_gpencil_stroke_nearest_to_ends(bContext *C, continue; } + /* Check that stroke is not closed. Closed strokes must not be included in the merge. */ + if (gps_target->flag & GP_STROKE_CYCLIC) { + continue; + } + /* Check if one of the ends is inside target stroke bounding box. */ if ((!ED_gpencil_stroke_check_collision(gsc, gps_target, pt2d_start, radius, diff_mat)) && (!ED_gpencil_stroke_check_collision(gsc, gps_target, pt2d_end, radius, diff_mat))) { diff --git a/source/blender/editors/gpencil/gpencil_vertex_ops.c b/source/blender/editors/gpencil/gpencil_vertex_ops.c index c0888968a2d..865c4e360b5 100644 --- a/source/blender/editors/gpencil/gpencil_vertex_ops.c +++ b/source/blender/editors/gpencil/gpencil_vertex_ops.c @@ -587,7 +587,7 @@ static int gpencil_vertexpaint_set_exec(bContext *C, wmOperator *op) srgb_to_linearrgb_v4(color, color); for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { if ((!any_selected) || (pt->flag & GP_SPOINT_SELECT)) { - copy_v3_v3(pt->vert_color, color); + copy_v4_v4(pt->vert_color, color); } } } diff --git a/source/blender/editors/include/BIF_glutil.h b/source/blender/editors/include/BIF_glutil.h index 3f1b8d6ecda..84512653a24 100644 --- a/source/blender/editors/include/BIF_glutil.h +++ b/source/blender/editors/include/BIF_glutil.h @@ -55,17 +55,17 @@ IMMDrawPixelsTexState immDrawPixelsTexSetup(int builtin); * finished. */ void immDrawPixelsTexScaledFullSize(const IMMDrawPixelsTexState *state, - const float x, - const float y, - const int img_w, - const int img_h, - const eGPUTextureFormat gpu_format, - const bool use_filter, + float x, + float y, + int img_w, + int img_h, + eGPUTextureFormat gpu_format, + bool use_filter, const void *rect, - const float scaleX, - const float scaleY, - const float xzoom, - const float yzoom, + float scaleX, + float scaleY, + float xzoom, + float yzoom, const float color[4]); /** diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h index 60af05baed7..da40eef87fd 100644 --- a/source/blender/editors/include/ED_anim_api.h +++ b/source/blender/editors/include/ED_anim_api.h @@ -549,7 +549,7 @@ typedef enum eAnimChannels_SetFlag { /* types of settings for AnimChannels */ typedef enum eAnimChannel_Settings { ACHANNEL_SETTING_SELECT = 0, - /** warning: for drawing UI's, need to check if this is off (maybe inverse this later) */ + /** WARNING: for drawing UI's, need to check if this is off (maybe inverse this later). */ ACHANNEL_SETTING_PROTECT = 1, ACHANNEL_SETTING_MUTE = 2, ACHANNEL_SETTING_EXPAND = 3, diff --git a/source/blender/editors/include/ED_clip.h b/source/blender/editors/include/ED_clip.h index 2c67b02611b..0f981e270a0 100644 --- a/source/blender/editors/include/ED_clip.h +++ b/source/blender/editors/include/ED_clip.h @@ -22,15 +22,53 @@ struct bScreen; /* ** clip_editor.c ** */ -/* common poll functions */ +/* Returns true when the following conditions are met: + * - Current space is Space Clip. + * - There is a movie clip opened in it. */ bool ED_space_clip_poll(struct bContext *C); +/* Returns true when the following conditions are met: + * - Current space is Space Clip. + * - It is set to Clip view. + * + * It is not required to have movie clip opened for editing. */ bool ED_space_clip_view_clip_poll(struct bContext *C); +/* Returns true when the following conditions are met: + * - Current space is Space Clip. + * - It is set to Tracking mode. + * + * It is not required to have movie clip opened for editing. */ bool ED_space_clip_tracking_poll(struct bContext *C); + +/* Returns true when the following conditions are met: + * - Current space is Space Clip. + * - It is set to Mask mode. + * + * It is not required to have mask opened for editing. */ bool ED_space_clip_maskedit_poll(struct bContext *C); + +/* Returns true when the following conditions are met: + * - Current space is Space Clip. + * - It is set to Mask mode. + * - Mask has visible and editable splines. + * + * It is not required to have mask opened for editing. */ +bool ED_space_clip_maskedit_visible_splines_poll(struct bContext *C); + +/* Returns true when the following conditions are met: + * - Current space is Space Clip. + * - It is set to Mask mode. + * - The space has mask opened. */ bool ED_space_clip_maskedit_mask_poll(struct bContext *C); +/* Returns true when the following conditions are met: + * - Current space is Space Clip. + * - It is set to Mask mode. + * - The space has mask opened. + * - Mask has visible and editable splines. */ +bool ED_space_clip_maskedit_mask_visible_splines_poll(struct bContext *C); + void ED_space_clip_get_size(struct SpaceClip *sc, int *width, int *height); void ED_space_clip_get_size_fl(struct SpaceClip *sc, float size[2]); void ED_space_clip_get_zoom(struct SpaceClip *sc, @@ -60,7 +98,7 @@ bool ED_space_clip_get_position(struct SpaceClip *sc, */ bool ED_space_clip_color_sample(struct SpaceClip *sc, struct ARegion *region, - int mval[2], + const int mval[2], float r_col[3]); void ED_clip_update_frame(const struct Main *mainp, int cfra); diff --git a/source/blender/editors/include/ED_curve.h b/source/blender/editors/include/ED_curve.h index 9f4833bf1d7..061b783797d 100644 --- a/source/blender/editors/include/ED_curve.h +++ b/source/blender/editors/include/ED_curve.h @@ -49,10 +49,12 @@ void ED_curve_editnurb_free(struct Object *obedit); /** * \param dist_px: Maximum distance to pick (in pixels). + * \param vert_without_handles: When true, selecting the knot doesn't select the handles. */ bool ED_curve_editnurb_select_pick(struct bContext *C, const int mval[2], int dist_px, + bool vert_without_handles, const struct SelectPick_Params *params); struct Nurb *ED_curve_add_nurbs_primitive( diff --git a/source/blender/editors/include/ED_datafiles.h b/source/blender/editors/include/ED_datafiles.h index c3a66e4a7dd..e3b091430e8 100644 --- a/source/blender/editors/include/ED_datafiles.h +++ b/source/blender/editors/include/ED_datafiles.h @@ -103,6 +103,9 @@ extern const char datatoc_multiply_png[]; extern int datatoc_nudge_png_size; extern const char datatoc_nudge_png[]; +extern int datatoc_paint_select_png_size; +extern const char datatoc_paint_select_png[]; + extern int datatoc_pinch_png_size; extern const char datatoc_pinch_png[]; @@ -284,6 +287,41 @@ extern const char datatoc_gp_brush_erase_hard_png[]; extern int datatoc_gp_brush_erase_stroke_png_size; extern const char datatoc_gp_brush_erase_stroke_png[]; +/* curves sculpt brushes files */ + +extern int datatoc_curves_sculpt_add_png_size; +extern const char datatoc_curves_sculpt_add_png[]; + +extern int datatoc_curves_sculpt_comb_png_size; +extern const char datatoc_curves_sculpt_comb_png[]; + +extern int datatoc_curves_sculpt_cut_png_size; +extern const char datatoc_curves_sculpt_cut_png[]; + +extern int datatoc_curves_sculpt_delete_png_size; +extern const char datatoc_curves_sculpt_delete_png[]; + +extern int datatoc_curves_sculpt_density_png_size; +extern const char datatoc_curves_sculpt_density_png[]; + +extern int datatoc_curves_sculpt_grow_shrink_png_size; +extern const char datatoc_curves_sculpt_grow_shrink_png[]; + +extern int datatoc_curves_sculpt_pinch_png_size; +extern const char datatoc_curves_sculpt_pinch_png[]; + +extern int datatoc_curves_sculpt_puff_png_size; +extern const char datatoc_curves_sculpt_puff_png[]; + +extern int datatoc_curves_sculpt_slide_png_size; +extern const char datatoc_curves_sculpt_slide_png[]; + +extern int datatoc_curves_sculpt_smooth_png_size; +extern const char datatoc_curves_sculpt_smooth_png[]; + +extern int datatoc_curves_sculpt_snake_hook_png_size; +extern const char datatoc_curves_sculpt_snake_hook_png[]; + #ifdef __cplusplus } #endif diff --git a/source/blender/editors/include/ED_fileselect.h b/source/blender/editors/include/ED_fileselect.h index c367072e6e7..e9fcd2bd5fe 100644 --- a/source/blender/editors/include/ED_fileselect.h +++ b/source/blender/editors/include/ED_fileselect.h @@ -164,8 +164,16 @@ void ED_fileselect_window_params_get(const struct wmWindow *win, int win_size[2], bool *is_maximized); +/** + * Return the File Browser area in which \a file_operator is active. + */ struct ScrArea *ED_fileselect_handler_area_find(const struct wmWindow *win, const struct wmOperator *file_operator); +/** + * Check if there is any area in \a win that acts as a modal File Browser (#SpaceFile.op is set) + * and return it. + */ +struct ScrArea *ED_fileselect_handler_area_find_any_with_op(const struct wmWindow *win); /* TODO: Maybe we should move this to BLI? * On the other hand, it's using defines from space-file area, so not sure... */ diff --git a/source/blender/editors/include/ED_geometry.h b/source/blender/editors/include/ED_geometry.h index 74ff968828c..4620181894a 100644 --- a/source/blender/editors/include/ED_geometry.h +++ b/source/blender/editors/include/ED_geometry.h @@ -7,12 +7,22 @@ #pragma once +#include "BKE_attribute.h" +#include "DNA_customdata_types.h" + #ifdef __cplusplus extern "C" { #endif -void ED_operatortypes_geometry(void); +struct Mesh; +void ED_operatortypes_geometry(void); +bool ED_geometry_attribute_convert(struct Mesh *mesh, + const char *layer_name, + eCustomDataType old_type, + eAttrDomain old_domain, + eCustomDataType new_type, + eAttrDomain new_domain); #ifdef __cplusplus } #endif diff --git a/source/blender/editors/include/ED_gizmo_library.h b/source/blender/editors/include/ED_gizmo_library.h index 067e5ba4d7f..31c1f93f5bc 100644 --- a/source/blender/editors/include/ED_gizmo_library.h +++ b/source/blender/editors/include/ED_gizmo_library.h @@ -14,6 +14,8 @@ extern "C" { #endif +#include "DNA_scene_types.h" + /* initialize gizmos */ void ED_gizmotypes_arrow_3d(void); void ED_gizmotypes_button_2d(void); @@ -223,24 +225,6 @@ enum { /* -------------------------------------------------------------------- */ /* Specific gizmos utils */ -/* dial3d_gizmo.c */ - -struct Dial3dParams { - int draw_options; - float angle_ofs; - float angle_delta; - float angle_increment; - float arc_partial_angle; - float arc_inner_factor; - float *clip_plane; -}; -void ED_gizmotypes_dial_3d_draw_util(const float matrix_basis[4][4], - const float matrix_final[4][4], - float line_width, - const float color[4], - bool select, - struct Dial3dParams *params); - /* snap3d_gizmo.c */ struct SnapObjectContext *ED_gizmotypes_snap_3d_context_ensure(struct Scene *scene, @@ -258,7 +242,7 @@ void ED_gizmotypes_snap_3d_data_get(const struct bContext *C, float r_loc[3], float r_nor[3], int r_elem_index[3], - int *r_snap_elem); + eSnapMode *r_snap_elem); #ifdef __cplusplus } diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h index 01f60a81288..0943636a452 100644 --- a/source/blender/editors/include/ED_gpencil.h +++ b/source/blender/editors/include/ED_gpencil.h @@ -579,7 +579,7 @@ void ED_gpencil_init_random_settings(struct Brush *brush, */ bool ED_gpencil_stroke_check_collision(const struct GP_SpaceConversion *gsc, struct bGPDstroke *gps, - const float mouse[2], + const float mval[2], int radius, const float diff_mat[4][4]); /** @@ -587,13 +587,13 @@ bool ED_gpencil_stroke_check_collision(const struct GP_SpaceConversion *gsc, * * \param gps: Stroke to check. * \param gsc: Space conversion data. - * \param mouse: Mouse position. + * \param mval: Region relative cursor position. * \param diff_mat: View matrix. * \return True if the point is inside. */ bool ED_gpencil_stroke_point_is_inside(const struct bGPDstroke *gps, const struct GP_SpaceConversion *gsc, - const int mouse[2], + const int mval[2], const float diff_mat[4][4]); /** * Get the bigger 2D bound box points. diff --git a/source/blender/editors/include/ED_image.h b/source/blender/editors/include/ED_image.h index f79d3ce205d..91ae8286531 100644 --- a/source/blender/editors/include/ED_image.h +++ b/source/blender/editors/include/ED_image.h @@ -24,6 +24,7 @@ struct Scene; struct SpaceImage; struct View2D; struct bContext; +struct Paint; struct wmOperator; struct wmWindowManager; @@ -63,8 +64,11 @@ bool ED_space_image_get_position(struct SpaceImage *sima, /** * Returns color in linear space, matching #ED_space_node_color_sample(). */ -bool ED_space_image_color_sample( - struct SpaceImage *sima, struct ARegion *region, int mval[2], float r_col[3], bool *r_is_data); +bool ED_space_image_color_sample(struct SpaceImage *sima, + struct ARegion *region, + const int mval[2], + float r_col[3], + bool *r_is_data); struct ImBuf *ED_space_image_acquire_buffer(struct SpaceImage *sima, void **r_lock, int tile); /** * Get the #SpaceImage flag that is valid for the given ibuf. @@ -133,8 +137,39 @@ bool ED_space_image_paint_curve(const struct bContext *C); * Matches clip function. */ bool ED_space_image_check_show_maskedit(struct SpaceImage *sima, struct Object *obedit); + +/* Returns true when the following conditions are met: + * - Current space is Image Editor. + * - The image editor is not a UV Editor. + * - It is set to Mask mode. + * + * It is not required to have mask opened for editing. */ bool ED_space_image_maskedit_poll(struct bContext *C); + +/* Returns true when the following conditions are met: + * - Current space is Image Editor. + * - The image editor is not a UV Editor. + * - It is set to Mask mode. + * - Mask has visible and editable splines. + * + * It is not required to have mask opened for editing. */ +bool ED_space_image_maskedit_visible_splines_poll(struct bContext *C); + +/* Returns true when the following conditions are met: + * - Current space is Image Editor. + * - The image editor is not an UV Editor. + * - It is set to Mask mode. + * - The space has mask opened. */ bool ED_space_image_maskedit_mask_poll(struct bContext *C); + +/* Returns true when the following conditions are met: + * - Current space is Image Editor. + * - The image editor is not an UV Editor. + * - It is set to Mask mode. + * - The space has mask opened. + * - Mask has visible and editable splines. */ +bool ED_space_image_maskedit_mask_visible_splines_poll(struct bContext *C); + bool ED_space_image_cursor_poll(struct bContext *C); /** @@ -173,6 +208,7 @@ typedef struct ImageFrameRange { int length; int offset; /* UDIM tiles. */ + bool udims_detected; ListBase udim_tiles; /* Temporary data. */ @@ -186,6 +222,9 @@ ListBase ED_image_filesel_detect_sequences(struct Main *bmain, struct wmOperator *op, bool detect_udim); +bool ED_image_tools_paint_poll(struct bContext *C); +void ED_paint_cursor_start(struct Paint *p, bool (*poll)(struct bContext *C)); + #ifdef __cplusplus } #endif diff --git a/source/blender/editors/include/ED_keyframing.h b/source/blender/editors/include/ED_keyframing.h index 6a730225da9..a53042b70d6 100644 --- a/source/blender/editors/include/ED_keyframing.h +++ b/source/blender/editors/include/ED_keyframing.h @@ -351,6 +351,19 @@ int ANIM_scene_get_keyingset_index(struct Scene *scene, struct KeyingSet *ks); struct KeyingSet *ANIM_get_keyingset_for_autokeying(const struct Scene *scene, const char *transformKSName); +void ANIM_keyingset_visit_for_search(const struct bContext *C, + struct PointerRNA *ptr, + struct PropertyRNA *prop, + const char *edit_text, + StringPropertySearchVisitFunc visit_fn, + void *visit_user_data); + +void ANIM_keyingset_visit_for_search_no_poll(const struct bContext *C, + struct PointerRNA *ptr, + struct PropertyRNA *prop, + const char *edit_text, + StringPropertySearchVisitFunc visit_fn, + void *visit_user_data); /** * Dynamically populate an enum of Keying Sets. */ @@ -645,15 +658,20 @@ bool ED_autokeyframe_pchan(struct bContext *C, struct Object *ob, struct bPoseChannel *pchan, struct KeyingSet *ks); + /** - * Use for auto-key-framing from the UI. + * Use for auto-key-framing + * \param only_if_property_keyed: if true, auto-key-framing only creates keyframes on already keyed + * properties. This is by design when using buttons. For other callers such as gizmos or VSE + * preview transform, creating new animation/keyframes also on non-keyed properties is desired. */ bool ED_autokeyframe_property(struct bContext *C, struct Scene *scene, PointerRNA *ptr, PropertyRNA *prop, int rnaindex, - float cfra); + float cfra, + bool only_if_property_keyed); /* Names for builtin keying sets so we don't confuse these with labels/text, * defined in python script: `keyingsets_builtins.py`. */ diff --git a/source/blender/editors/include/ED_mask.h b/source/blender/editors/include/ED_mask.h index 7039d6d9fc7..8cadbfde185 100644 --- a/source/blender/editors/include/ED_mask.h +++ b/source/blender/editors/include/ED_mask.h @@ -22,6 +22,34 @@ struct wmKeyConfig; /* mask_edit.c */ +/* Returns true when the following conditions are met: + * - Current space supports mask editing. + * - The space is configured to interact with mask. + * + * It is not required to have mask opened for editing. */ +bool ED_maskedit_poll(struct bContext *C); + +/* Returns true when the following conditions are met: + * - Current space supports mask editing. + * - The space is configured to interact with mask. + * - Mask has visible and editable splines. + * + * It is not required to have mask opened for editing. */ +bool ED_maskedit_visible_splines_poll(struct bContext *C); + +/* Returns true when the following conditions are met: + * - Current space supports mask editing. + * - The space is configured to interact with mask. + * - The space has mask open for editing. */ +bool ED_maskedit_mask_poll(struct bContext *C); + +/* Returns true when the following conditions are met: + * - Current space supports mask editing. + * - The space is configured to interact with mask. + * - The space has mask opened. + * - Mask has visible and editable splines. */ +bool ED_maskedit_mask_visible_splines_poll(struct bContext *C); + void ED_mask_deselect_all(const struct bContext *C); void ED_operatortypes_mask(void); @@ -73,6 +101,7 @@ void ED_mask_draw_region(struct Depsgraph *depsgraph, char draw_flag, char draw_type, eMaskOverlayMode overlay_mode, + float blend_factor, int width_i, int height_i, float aspx, diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index 59a24ed22b6..30a98129ee6 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -384,7 +384,7 @@ void ED_operatormacros_mesh(void); */ void ED_keymap_mesh(struct wmKeyConfig *keyconf); -/* editface.c */ +/* editface.cc */ /** * Copy the face flags, most importantly selection from the mesh to the final derived mesh, @@ -520,7 +520,7 @@ float ED_vgroup_vert_weight(struct Object *ob, struct bDeformGroup *dg, int vert */ void ED_vgroup_vert_active_mirror(struct Object *ob, int def_nr); -/* mesh_data.c */ +/* mesh_data.cc */ void ED_mesh_verts_add(struct Mesh *mesh, struct ReportList *reports, int count); void ED_mesh_edges_add(struct Mesh *mesh, struct ReportList *reports, int count); @@ -536,12 +536,12 @@ void ED_mesh_geometry_clear(struct Mesh *mesh); 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( +void ED_mesh_uv_ensure(struct Mesh *me, const char *name); +int ED_mesh_uv_add( struct Mesh *me, const char *name, bool active_set, bool do_init, struct ReportList *reports); -bool ED_mesh_uv_texture_remove_index(struct Mesh *me, int n); -bool ED_mesh_uv_texture_remove_active(struct Mesh *me); -bool ED_mesh_uv_texture_remove_named(struct Mesh *me, const char *name); +bool ED_mesh_uv_remove_index(struct Mesh *me, int n); +bool ED_mesh_uv_remove_active(struct Mesh *me); +bool ED_mesh_uv_remove_named(struct Mesh *me, const char *name); void ED_mesh_uv_loop_reset(struct bContext *C, struct Mesh *me); /** * Without a #bContext, called when UV-editing. @@ -591,7 +591,7 @@ void EDBM_redo_state_restore_and_free(struct BMBackup *backup, bool recalc_looptri) ATTR_NONNULL(1, 2); void EDBM_redo_state_free(struct BMBackup *backup) ATTR_NONNULL(1); -/* *** meshtools.c *** */ +/* *** meshtools.cc *** */ int ED_mesh_join_objects_exec(struct bContext *C, struct wmOperator *op); int ED_mesh_shapes_join_objects_exec(struct bContext *C, struct wmOperator *op); diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h index 28452ba8db9..39c7ad3556c 100644 --- a/source/blender/editors/include/ED_object.h +++ b/source/blender/editors/include/ED_object.h @@ -234,7 +234,7 @@ struct Base *ED_object_add_duplicate(struct Main *bmain, void ED_object_parent(struct Object *ob, struct Object *parent, int type, const char *substr); char *ED_object_ot_drop_named_material_tooltip(struct bContext *C, - struct PointerRNA *properties, + const char *name, const int mval[2]); /* bitflags for enter/exit editmode */ @@ -536,12 +536,12 @@ bool ED_object_modifier_move_to_index(struct ReportList *reports, struct ModifierData *md, int index); -bool ED_object_modifier_convert(struct ReportList *reports, - struct Main *bmain, - struct Depsgraph *depsgraph, - struct ViewLayer *view_layer, - struct Object *ob, - struct ModifierData *md); +bool ED_object_modifier_convert_psys_to_mesh(struct ReportList *reports, + struct Main *bmain, + struct Depsgraph *depsgraph, + struct ViewLayer *view_layer, + struct Object *ob, + struct ModifierData *md); bool ED_object_modifier_apply(struct Main *bmain, struct ReportList *reports, struct Depsgraph *depsgraph, diff --git a/source/blender/editors/include/ED_scene.h b/source/blender/editors/include/ED_scene.h index f1a2e5795ee..f67bdb9c1d7 100644 --- a/source/blender/editors/include/ED_scene.h +++ b/source/blender/editors/include/ED_scene.h @@ -18,6 +18,11 @@ struct Scene *ED_scene_add(struct Main *bmain, struct bContext *C, struct wmWindow *win, enum eSceneCopyMethod method) ATTR_NONNULL(); +/** Special mode for adding a scene assigned to sequencer strip. */ +struct Scene *ED_scene_sequencer_add(struct Main *bmain, + struct bContext *C, + enum eSceneCopyMethod method, + const bool assign_strip); /** * \note Only call outside of area/region loops. * \return true if successful. diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index 41123aff79f..a24c8625a63 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -595,8 +595,7 @@ bool ED_operator_object_active_local_editable_posemode_exclusive(struct bContext bool ED_operator_posemode_context(struct bContext *C); bool ED_operator_posemode(struct bContext *C); bool ED_operator_posemode_local(struct bContext *C); -bool ED_operator_mask(struct bContext *C); -bool ED_operator_camera(struct bContext *C); +bool ED_operator_camera_poll(struct bContext *C); /* screen_user_menu.c */ @@ -666,7 +665,7 @@ bool ED_region_panel_category_gutter_calc_rect(const ARegion *region, rcti *r_re bool ED_region_panel_category_gutter_isect_xy(const ARegion *region, const int event_xy[2]); /** - * \note: This may return true for multiple overlapping regions. + * \note This may return true for multiple overlapping regions. * If it matters, check overlapped regions first (#ARegion.overlap). */ bool ED_region_contains_xy(const struct ARegion *region, const int event_xy[2]); diff --git a/source/blender/editors/include/ED_sculpt.h b/source/blender/editors/include/ED_sculpt.h index 54d67c50d5c..550040d2bc6 100644 --- a/source/blender/editors/include/ED_sculpt.h +++ b/source/blender/editors/include/ED_sculpt.h @@ -51,7 +51,7 @@ void ED_sculpt_face_sets_initialize_none_to_id(struct Mesh *mesh, int new_id); int ED_sculpt_face_sets_active_update_and_get(struct bContext *C, struct Object *ob, - const float mval[2]); + const float mval_fl[2]); /* Undo for changes happening on a base mesh for multires sculpting. * if there is no multi-res sculpt active regular undo is used. */ diff --git a/source/blender/editors/include/ED_select_utils.h b/source/blender/editors/include/ED_select_utils.h index dbe1b582132..8c856794ec8 100644 --- a/source/blender/editors/include/ED_select_utils.h +++ b/source/blender/editors/include/ED_select_utils.h @@ -14,6 +14,7 @@ extern "C" { struct KDTree_1d; struct wmOperator; +struct wmOperatorType; enum { SEL_TOGGLE = 0, @@ -39,11 +40,11 @@ typedef enum { } eSelectOp; /* Select Similar */ -enum { +typedef enum { SIM_CMP_EQ = 0, SIM_CMP_GT, SIM_CMP_LT, -}; +} eSimilarCmp; #define SEL_OP_USE_OUTSIDE(sel_op) (ELEM(sel_op, SEL_OP_AND)) #define SEL_OP_USE_PRE_DESELECT(sel_op) (ELEM(sel_op, SEL_OP_SET)) @@ -62,11 +63,11 @@ int ED_select_op_action(eSelectOp sel_op, bool is_select, bool is_inside); */ int ED_select_op_action_deselected(eSelectOp sel_op, bool is_select, bool is_inside); -int ED_select_similar_compare_float(float delta, float thresh, int compare); +bool ED_select_similar_compare_float(float delta, float thresh, eSimilarCmp compare); bool ED_select_similar_compare_float_tree(const struct KDTree_1d *tree, float length, float thresh, - int compare); + eSimilarCmp compare); /** * Utility to use for selection operations that run multiple times (circle select). @@ -96,16 +97,23 @@ struct SelectPick_Params { /** * Utility to get #eSelectPickMode from booleans for convenience. */ -eSelectOp ED_select_op_from_operator(struct wmOperator *op) +eSelectOp ED_select_op_from_operator(struct PointerRNA *ptr) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT; /** * Initialize `params` from `op`, * these properties are defined by #WM_operator_properties_mouse_select. */ -void ED_select_pick_params_from_operator(struct wmOperator *op, struct SelectPick_Params *params) +void ED_select_pick_params_from_operator(struct PointerRNA *ptr, struct SelectPick_Params *params) ATTR_NONNULL(1, 2); +/** + * Get-name callback for #wmOperatorType.get_name, this is mainly useful so the selection + * action is shown in the status-bar. + */ +const char *ED_select_pick_get_name(struct wmOperatorType *ot, PointerRNA *ptr); +const char *ED_select_circle_get_name(struct wmOperatorType *ot, PointerRNA *ptr); + #ifdef __cplusplus } #endif 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 2919001c5ce..20353c21f93 100644 --- a/source/blender/editors/include/ED_transform_snap_object_context.h +++ b/source/blender/editors/include/ED_transform_snap_object_context.h @@ -6,6 +6,8 @@ #pragma once +#include "DNA_scene_types.h" + #ifdef __cplusplus extern "C" { #endif @@ -25,14 +27,6 @@ struct View3D; /* ED_transform_snap_object_*** API */ -typedef enum eSnapSelect { - SNAP_ALL = 0, - SNAP_NOT_SELECTED = 1, - SNAP_NOT_ACTIVE = 2, - SNAP_NOT_EDITED = 3, - SNAP_SELECTABLE = 4, -} eSnapSelect; - typedef enum eSnapEditType { SNAP_GEOM_FINAL = 0, SNAP_GEOM_CAGE = 1, @@ -59,7 +53,7 @@ struct SnapObjectHitDepth { /** parameters that define which objects will be used to snap. */ struct SnapObjectParams { /* Special context sensitive handling for the active or selected object. */ - eSnapSelect snap_select; + eSnapTargetSelect snap_target_select; /* Geometry for snapping in edit mode. */ eSnapEditType edit_mode_type; /* snap to the closest element, use when using more than one snap type */ @@ -120,21 +114,21 @@ bool ED_transform_snap_object_project_ray_all(SnapObjectContext *sctx, bool sort, struct ListBase *r_hit_list); -short ED_transform_snap_object_project_view3d_ex(struct SnapObjectContext *sctx, - struct Depsgraph *depsgraph, - const ARegion *region, - const View3D *v3d, - 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], - int *r_index, - struct Object **r_ob, - float r_obmat[4][4], - float r_face_nor[3]); +eSnapMode ED_transform_snap_object_project_view3d_ex(struct SnapObjectContext *sctx, + struct Depsgraph *depsgraph, + const ARegion *region, + const View3D *v3d, + eSnapMode 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], + int *r_index, + struct Object **r_ob, + float r_obmat[4][4], + float r_face_nor[3]); /** * Convenience function for performing snapping. * @@ -148,18 +142,18 @@ short ED_transform_snap_object_project_view3d_ex(struct SnapObjectContext *sctx, * \param r_no: hit normal (optional). * \return Snap success. */ -short ED_transform_snap_object_project_view3d(struct SnapObjectContext *sctx, - struct Depsgraph *depsgraph, - const ARegion *region, - const View3D *v3d, - 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], - float r_no[3]); +eSnapMode ED_transform_snap_object_project_view3d(struct SnapObjectContext *sctx, + struct Depsgraph *depsgraph, + const ARegion *region, + const View3D *v3d, + eSnapMode snap_to, + const struct SnapObjectParams *params, + const float mval[2], + const float prev_co[3], + float *dist_px, + /* return args */ + float r_loc[3], + float r_no[3]); /** * see: #ED_transform_snap_object_project_ray_all diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h index 54d36f44f82..80a75da27f8 100644 --- a/source/blender/editors/include/ED_uvedit.h +++ b/source/blender/editors/include/ED_uvedit.h @@ -266,6 +266,10 @@ struct BMLoop **ED_uvedit_selected_verts(const struct Scene *scene, int *r_verts_len); void ED_uvedit_get_aspect(struct Object *obedit, float *r_aspx, float *r_aspy); +void ED_uvedit_get_aspect_from_material(Object *ob, + const int material_index, + float *r_aspx, + float *r_aspy); void ED_uvedit_active_vert_loop_set(struct BMesh *bm, struct BMLoop *l); struct BMLoop *ED_uvedit_active_vert_loop_get(struct BMesh *bm); diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 414643dd0d6..0298983ed26 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -7,6 +7,9 @@ #pragma once +#include "BLI_utildefines.h" +#include "DNA_scene_types.h" + #ifdef __cplusplus extern "C" { #endif @@ -256,6 +259,7 @@ typedef enum { */ V3D_PROJ_TEST_CLIP_CONTENT = (1 << 5), } eV3DProjTest; +ENUM_OPERATORS(eV3DProjTest, V3D_PROJ_TEST_CLIP_CONTENT); #define V3D_PROJ_TEST_CLIP_DEFAULT \ (V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_WIN | V3D_PROJ_TEST_CLIP_NEAR) @@ -296,7 +300,7 @@ typedef enum { } eV3DPlaceOrient; typedef struct V3DSnapCursorData { - short snap_elem; + eSnapMode snap_elem; float loc[3]; float nor[3]; float obmat[4][4]; @@ -319,7 +323,7 @@ typedef struct V3DSnapCursorState { struct wmGizmoGroupType *gzgrp_type; /* Force cursor to be drawn only when gizmo is available. */ float *prevpoint; float box_dimensions[3]; - short snap_elem_force; /* If zero, use scene settings. */ + eSnapMode snap_elem_force; /* If SCE_SNAP_MODE_NONE, use scene settings. */ short plane_axis; bool use_plane_axis_auto; bool draw_point; @@ -344,7 +348,7 @@ void ED_view3d_cursor_snap_draw_util(struct RegionView3D *rv3d, const float normal[3], const uchar color_line[4], const uchar color_point[4], - short snap_elem_type); + eSnapMode snap_elem_type); /* view3d_iterators.c */ @@ -443,14 +447,14 @@ void pose_foreachScreenBone(struct ViewContext *vc, void ED_view3d_project_float_v2_m4(const struct ARegion *region, const float co[3], float r_co[2], - float mat[4][4]); + const float mat[4][4]); /** * \note use #ED_view3d_ob_project_mat_get to get projecting mat */ void ED_view3d_project_float_v3_m4(const struct ARegion *region, const float co[3], float r_co[3], - float mat[4][4]); + const float mat[4][4]); eV3DProjStatus ED_view3d_project_base(const struct ARegion *region, struct Base *base); @@ -700,9 +704,9 @@ void ED_view3d_win_to_vector(const struct ARegion *region, const float mval[2], * \param do_clip_planes: Optionally clip the ray by the view clipping planes. * \return success, false if the segment is totally clipped. */ -bool ED_view3d_win_to_segment_clipped(struct Depsgraph *depsgraph, +bool ED_view3d_win_to_segment_clipped(const struct Depsgraph *depsgraph, const struct ARegion *region, - struct View3D *v3d, + const struct View3D *v3d, const float mval[2], float r_ray_start[3], float r_ray_end[3], @@ -733,7 +737,7 @@ void ED_view3d_dist_range_get(const struct View3D *v3d, float r_dist_range[2]); /** * \note copies logic of #ED_view3d_viewplane_get(), keep in sync. */ -bool ED_view3d_clip_range_get(struct Depsgraph *depsgraph, +bool ED_view3d_clip_range_get(const struct Depsgraph *depsgraph, const struct View3D *v3d, const struct RegionView3D *rv3d, float *r_clipsta, @@ -1080,6 +1084,16 @@ bool ED_view3d_quat_to_axis_view(const float viewquat[4], float epsilon, char *r_view, char *r_view_axis_rotation); +/** + * A version of #ED_view3d_quat_to_axis_view that updates `viewquat` + * if it's within `epsilon` to an axis-view. + * + * \note Include the special case function since most callers need to perform these operations. + */ +bool ED_view3d_quat_to_axis_view_and_reset_quat(float viewquat[4], + float epsilon, + char *r_view, + char *r_view_axis_rotation); char ED_view3d_lock_view_from_index(int index); char ED_view3d_axis_view_opposite(char view); diff --git a/source/blender/editors/include/UI_grid_view.hh b/source/blender/editors/include/UI_grid_view.hh new file mode 100644 index 00000000000..6f553f4fad1 --- /dev/null +++ b/source/blender/editors/include/UI_grid_view.hh @@ -0,0 +1,264 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup editorui + * + * API for simple creation of grid UIs, supporting typically needed features. + * https://wiki.blender.org/wiki/Source/Interface/Views/Grid_Views + */ + +#pragma once + +#include "BLI_function_ref.hh" +#include "BLI_map.hh" +#include "BLI_vector.hh" + +#include "UI_resources.h" + +struct bContext; +struct PreviewImage; +struct uiBlock; +struct uiButGridTile; +struct uiLayout; +struct View2D; +struct wmNotifier; + +namespace blender::ui { + +class AbstractGridView; + +/* ---------------------------------------------------------------------- */ +/** \name Grid-View Item Type + * \{ */ + +class AbstractGridViewItem { + friend class AbstractGridView; + friend class GridViewLayoutBuilder; + + const AbstractGridView *view_; + + bool is_active_ = false; + + protected: + /** Reference to a string that uniquely identifies this item in the view. */ + StringRef identifier_{}; + /** Every visible item gets a button of type #UI_BTYPE_GRID_TILE during the layout building. */ + uiButGridTile *grid_tile_but_ = nullptr; + + public: + virtual ~AbstractGridViewItem() = default; + + virtual void build_grid_tile(uiLayout &layout) const = 0; + + /** + * Compare this item's identifier to \a other to check if they represent the same data. + * Used to recognize an item from a previous redraw, to be able to keep its state (e.g. active, + * renaming, etc.). + */ + bool matches(const AbstractGridViewItem &other) const; + + const AbstractGridView &get_view() const; + + /** + * Requires the tree to have completed reconstruction, see #is_reconstructed(). Otherwise we + * can't be sure about the item state. + */ + bool is_active() const; + + protected: + AbstractGridViewItem(StringRef identifier); + + /** Called when the item's state changes from inactive to active. */ + virtual void on_activate(); + /** + * If the result is not empty, it controls whether the item should be active or not, + * usually depending on the data that the view represents. + */ + virtual std::optional<bool> should_be_active() const; + + /** + * Copy persistent state (e.g. active, selection, etc.) from a matching item of + * the last redraw to this item. If sub-classes introduce more advanced state they should + * override this and make it update their state accordingly. + */ + virtual void update_from_old(const AbstractGridViewItem &old); + + /** + * Activates this item, deactivates other items, and calls the + * #AbstractGridViewItem::on_activate() function. + * Requires the tree to have completed reconstruction, see #is_reconstructed(). Otherwise the + * actual item state is unknown, possibly calling state-change update functions incorrectly. + */ + void activate(); + void deactivate(); + + private: + /** See #AbstractTreeView::change_state_delayed() */ + void change_state_delayed(); + static void grid_tile_click_fn(bContext *, void *but_arg1, void *); + void add_grid_tile_button(uiBlock &block); +}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Grid-View Base Class + * \{ */ + +struct GridViewStyle { + GridViewStyle(int width, int height); + int tile_width = 0; + int tile_height = 0; +}; + +class AbstractGridView { + friend class AbstractGridViewItem; + friend class GridViewBuilder; + friend class GridViewLayoutBuilder; + + protected: + Vector<std::unique_ptr<AbstractGridViewItem>> items_; + /** <identifier, item> map to lookup items by identifier, used for efficient lookups in + * #update_from_old(). */ + Map<StringRef, AbstractGridViewItem *> item_map_; + GridViewStyle style_; + bool is_reconstructed_ = false; + + public: + AbstractGridView(); + virtual ~AbstractGridView() = default; + + using ItemIterFn = FunctionRef<void(AbstractGridViewItem &)>; + void foreach_item(ItemIterFn iter_fn) const; + + /** Listen to a notifier, returning true if a redraw is needed. */ + virtual bool listen(const wmNotifier &) const; + + /** + * Convenience wrapper constructing the item by forwarding given arguments to the constructor of + * the type (\a ItemT). + * + * E.g. if your grid-item type has the following constructor: + * \code{.cpp} + * MyGridItem(std::string str, int i); + * \endcode + * You can add an item like this: + * \code + * add_item<MyGridItem>("blabla", 42); + * \endcode + */ + template<class ItemT, typename... Args> inline ItemT &add_item(Args &&...args); + const GridViewStyle &get_style() const; + int get_item_count() const; + + protected: + virtual void build_items() = 0; + + /** + * Check if the view is fully (re-)constructed. That means, both #build_items() and + * #update_from_old() have finished. + */ + bool is_reconstructed() const; + + private: + /** + * Match the grid-view against an earlier version of itself (if any) and copy the old UI state + * (e.g. active, selected, renaming, etc.) to the new one. See + * #AbstractGridViewItem.update_from_old(). + */ + void update_from_old(uiBlock &new_block); + AbstractGridViewItem *find_matching_item(const AbstractGridViewItem &item_to_match, + const AbstractGridView &view_to_search_in) const; + /** + * Items may want to do additional work when state changes. But these state changes can only be + * reliably detected after the view has completed reconstruction (see #is_reconstructed()). So + * the actual state changes are done in a delayed manner through this function. + */ + void change_state_delayed(); + + /** + * Add an already constructed item, moving ownership to the grid-view. + * All items must be added through this, it handles important invariants! + */ + AbstractGridViewItem &add_item(std::unique_ptr<AbstractGridViewItem> item); +}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Grid-View Builder + * + * TODO unify this with `TreeViewBuilder` and call view-specific functions via type erased view? + * \{ */ + +class GridViewBuilder { + uiBlock &block_; + + public: + GridViewBuilder(uiBlock &block); + + /** Build \a grid_view into the previously provided block, clipped by \a view_bounds (view space, + * typically `View2D.cur`). */ + void build_grid_view(AbstractGridView &grid_view, const View2D &v2d); +}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Predefined Grid-View Item Types + * + * Common, Basic Grid-View Item Types. + * \{ */ + +/** + * A grid item that shows preview image icons at a nicely readable size (multiple of the normal UI + * unit size). + */ +class PreviewGridItem : public AbstractGridViewItem { + public: + using IsActiveFn = std::function<bool()>; + using ActivateFn = std::function<void(PreviewGridItem &new_active)>; + + protected: + /** See #set_on_activate_fn() */ + ActivateFn activate_fn_; + /** See #set_is_active_fn() */ + IsActiveFn is_active_fn_; + + public: + std::string label{}; + int preview_icon_id = ICON_NONE; + + PreviewGridItem(StringRef identifier, StringRef label, int preview_icon_id); + + void build_grid_tile(uiLayout &layout) const override; + + /** + * Set a custom callback to execute when activating this view item. This way users don't have to + * sub-class #PreviewGridItem, just to implement custom activation behavior (a common thing to + * do). + */ + void set_on_activate_fn(ActivateFn fn); + /** + * Set a custom callback to check if this item should be active. + */ + void set_is_active_fn(IsActiveFn fn); + + private: + std::optional<bool> should_be_active() const override; + void on_activate() override; +}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ + +template<class ItemT, typename... Args> inline ItemT &AbstractGridView::add_item(Args &&...args) +{ + static_assert(std::is_base_of<AbstractGridViewItem, ItemT>::value, + "Type must derive from and implement the AbstractGridViewItem interface"); + + return dynamic_cast<ItemT &>(add_item(std::make_unique<ItemT>(std::forward<Args>(args)...))); +} + +} // namespace blender::ui diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h index d1a6501408c..f1c0acf43f7 100644 --- a/source/blender/editors/include/UI_icons.h +++ b/source/blender/editors/include/UI_icons.h @@ -164,7 +164,7 @@ DEF_ICON(NLA) DEF_ICON(PREFERENCES) DEF_ICON(TIME) DEF_ICON(NODETREE) -DEF_ICON_BLANK(181) +DEF_ICON(GEOMETRY_NODES) DEF_ICON(CONSOLE) DEF_ICON_BLANK(183) DEF_ICON(TRACKER) @@ -891,6 +891,7 @@ DEF_ICON_COLOR(BRUSH_LAYER) DEF_ICON_COLOR(BRUSH_MASK) DEF_ICON_COLOR(BRUSH_MIX) DEF_ICON_COLOR(BRUSH_NUDGE) +DEF_ICON_COLOR(BRUSH_PAINT_SELECT) DEF_ICON_COLOR(BRUSH_PINCH) DEF_ICON_COLOR(BRUSH_SCRAPE) DEF_ICON_COLOR(BRUSH_SCULPT_DRAW) @@ -903,7 +904,6 @@ DEF_ICON_COLOR(BRUSH_TEXFILL) DEF_ICON_COLOR(BRUSH_TEXMASK) DEF_ICON_COLOR(BRUSH_THUMB) DEF_ICON_COLOR(BRUSH_ROTATE) -DEF_ICON_COLOR(BRUSH_PAINT) /* grease pencil sculpt */ DEF_ICON_COLOR(GPBRUSH_SMOOTH) @@ -929,6 +929,19 @@ DEF_ICON_COLOR(GPBRUSH_ERASE_SOFT) DEF_ICON_COLOR(GPBRUSH_ERASE_HARD) DEF_ICON_COLOR(GPBRUSH_ERASE_STROKE) +/* Curves sculpt. */ +DEF_ICON_COLOR(BRUSH_CURVES_ADD) +DEF_ICON_COLOR(BRUSH_CURVES_COMB) +DEF_ICON_COLOR(BRUSH_CURVES_CUT) +DEF_ICON_COLOR(BRUSH_CURVES_DELETE) +DEF_ICON_COLOR(BRUSH_CURVES_DENSITY) +DEF_ICON_COLOR(BRUSH_CURVES_GROW_SHRINK) +DEF_ICON_COLOR(BRUSH_CURVES_PINCH) +DEF_ICON_COLOR(BRUSH_CURVES_PUFF) +DEF_ICON_COLOR(BRUSH_CURVES_SLIDE) +DEF_ICON_COLOR(BRUSH_CURVES_SMOOTH) +DEF_ICON_COLOR(BRUSH_CURVES_SNAKE_HOOK) + /* Vector icons. */ DEF_ICON_VECTOR(KEYTYPE_KEYFRAME_VEC) DEF_ICON_VECTOR(KEYTYPE_BREAKDOWN_VEC) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 301171a284d..b2ec2102ddd 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -64,6 +64,7 @@ struct wmKeyMapItem; struct wmMsgBus; struct wmOperator; struct wmOperatorType; +struct wmRegionListenerParams; struct wmWindow; typedef struct uiBlock uiBlock; @@ -75,6 +76,10 @@ typedef struct uiPopupBlockHandle uiPopupBlockHandle; typedef struct uiTreeViewHandle uiTreeViewHandle; /* C handle for C++ #ui::AbstractTreeViewItem type. */ typedef struct uiTreeViewItemHandle uiTreeViewItemHandle; +/* C handle for C++ #ui::AbstractGridView type. */ +typedef struct uiGridViewHandle uiGridViewHandle; +/* C handle for C++ #ui::AbstractGridViewItem type. */ +typedef struct uiGridViewItemHandle uiGridViewItemHandle; /* Defines */ @@ -184,26 +189,26 @@ enum { /** #uiBut.flag general state flags. */ enum { - /* WARNING: the first 7 flags are internal (see #UI_SELECT definition). */ - UI_BUT_ICON_SUBMENU = 1 << 7, - UI_BUT_ICON_PREVIEW = 1 << 8, + /* WARNING: the first 8 flags are internal (see #UI_SELECT definition). */ + UI_BUT_ICON_SUBMENU = 1 << 8, + UI_BUT_ICON_PREVIEW = 1 << 9, - UI_BUT_NODE_LINK = 1 << 9, - UI_BUT_NODE_ACTIVE = 1 << 10, - UI_BUT_DRAG_LOCK = 1 << 11, + UI_BUT_NODE_LINK = 1 << 10, + UI_BUT_NODE_ACTIVE = 1 << 11, + UI_BUT_DRAG_LOCK = 1 << 12, /** Grayed out and un-editable. */ - UI_BUT_DISABLED = 1 << 12, + UI_BUT_DISABLED = 1 << 13, - UI_BUT_ANIMATED = 1 << 13, - UI_BUT_ANIMATED_KEY = 1 << 14, - UI_BUT_DRIVEN = 1 << 15, - UI_BUT_REDALERT = 1 << 16, + UI_BUT_ANIMATED = 1 << 14, + UI_BUT_ANIMATED_KEY = 1 << 15, + UI_BUT_DRIVEN = 1 << 16, + UI_BUT_REDALERT = 1 << 17, /** Grayed out but still editable. */ - UI_BUT_INACTIVE = 1 << 17, - UI_BUT_LAST_ACTIVE = 1 << 18, - UI_BUT_UNDO = 1 << 19, - UI_BUT_IMMEDIATE = 1 << 20, - UI_BUT_NO_UTF8 = 1 << 21, + UI_BUT_INACTIVE = 1 << 18, + UI_BUT_LAST_ACTIVE = 1 << 19, + UI_BUT_UNDO = 1 << 20, + /* UNUSED = 1 << 21, */ + UI_BUT_NO_UTF8 = 1 << 22, /** For popups, pressing return activates this button, overriding the highlighted button. * For non-popups this is just used as a display hint for the user to let them @@ -390,6 +395,8 @@ typedef enum { UI_BTYPE_DECORATOR = 58 << 9, /* An item in a tree view. Parent items may be collapsible. */ UI_BTYPE_TREEROW = 59 << 9, + /* An item in a grid view. */ + UI_BTYPE_GRID_TILE = 60 << 9, } eButType; #define BUTTYPE (63 << 9) @@ -454,7 +461,6 @@ void UI_draw_safe_areas(uint pos, enum { UI_SCROLL_PRESSED = 1 << 0, UI_SCROLL_ARROWS = 1 << 1, - UI_SCROLL_NO_OUTLINE = 1 << 2, }; /** * Function in use for buttons and for view2d sliders. @@ -852,33 +858,6 @@ void UI_block_translate(uiBlock *block, int x, int y); int UI_but_return_value_get(uiBut *but); -void UI_but_drag_set_id(uiBut *but, struct ID *id); -/** - * Set an image to display while dragging. This works for any drag type (`WM_DRAG_XXX`). - * Not to be confused with #UI_but_drag_set_image(), which sets up dragging of an image. - */ -void UI_but_drag_attach_image(uiBut *but, struct ImBuf *imb, float scale); -/** - * \param asset: May be passed from a temporary variable, drag data only stores a copy of this. - */ -void UI_but_drag_set_asset(uiBut *but, - const struct AssetHandle *asset, - const char *path, - struct AssetMetaData *metadata, - int import_type, /* eFileAssetImportType */ - int icon, - struct ImBuf *imb, - float scale); -void UI_but_drag_set_rna(uiBut *but, struct PointerRNA *ptr); -void UI_but_drag_set_path(uiBut *but, const char *path, bool use_free); -void UI_but_drag_set_name(uiBut *but, const char *name); -/** - * Value from button itself. - */ -void UI_but_drag_set_value(uiBut *but); -void UI_but_drag_set_image( - uiBut *but, const char *path, int icon, struct ImBuf *imb, float scale, bool use_free); - uiBut *UI_but_active_drop_name_button(const struct bContext *C); /** * Returns true if highlighted button allows drop of names. @@ -1539,31 +1518,6 @@ uiBut *uiDefIconTextBlockBut(uiBlock *block, short height, const char *tip); -uiBut *uiDefKeyevtButS(uiBlock *block, - int retval, - const char *str, - int x, - int y, - short width, - short height, - short *spoin, - const char *tip); - -/** - * Short pointers hard-coded. - * \param modkeypoin: will be set to #KM_SHIFT, #KM_ALT, #KM_CTRL, #KM_OSKEY bits. - */ -uiBut *uiDefHotKeyevtButS(uiBlock *block, - int retval, - const char *str, - int x, - int y, - short width, - short height, - short *keypoin, - const short *modkeypoin, - const char *tip); - /** * \param arg: A pointer to string/name, use #UI_but_func_search_set() below to make this work. * here `a1` and `a2`, if set, control thumbnail preview rows/cols. @@ -1674,15 +1628,16 @@ void UI_but_func_identity_compare_set(uiBut *but, uiButIdentityCompareFunc cmp_f * \param name: Text to display for the item. * \param poin: Opaque pointer (for use by the caller). * \param iconid: The icon, #ICON_NONE for no icon. - * \param state: The buttons state flag, compatible with #uiBut.flag, - * typically #UI_BUT_DISABLED / #UI_BUT_INACTIVE. + * \param but_flag: Button flags (#uiBut.flag) indicating the state of the item, typically + * #UI_BUT_DISABLED, #UI_BUT_INACTIVE or #UI_BUT_HAS_SEP_CHAR. + * * \return false if there is nothing to add. */ bool UI_search_item_add(uiSearchItems *items, const char *name, void *poin, int iconid, - int state, + int but_flag, uint8_t name_prefix_offset); /** @@ -1792,6 +1747,14 @@ struct PointerRNA *UI_but_extra_operator_icon_add(uiBut *but, struct wmOperatorType *UI_but_extra_operator_icon_optype_get(struct uiButExtraOpIcon *extra_icon); struct PointerRNA *UI_but_extra_operator_icon_opptr_get(struct uiButExtraOpIcon *extra_icon); +/** + * A decent size for a button (typically #UI_BTYPE_PREVIEW_TILE) to display a nicely readable + * preview with label in. + */ +int UI_preview_tile_size_x(void); +int UI_preview_tile_size_y(void); +int UI_preview_tile_size_y_no_label(void); + /* Autocomplete * * Tab complete helper functions, for use in uiButCompleteFunc callbacks. @@ -1808,6 +1771,38 @@ AutoComplete *UI_autocomplete_begin(const char *startname, size_t maxlen); void UI_autocomplete_update_name(AutoComplete *autocpl, const char *name); int UI_autocomplete_end(AutoComplete *autocpl, char *autoname); +/* Button drag-data (interface_drag.cc). + * + * Functions to set drag data for buttons. This enables dragging support, whereby the drag data is + * "dragged", not the button itself. */ + +void UI_but_drag_set_id(uiBut *but, struct ID *id); +/** + * Set an image to display while dragging. This works for any drag type (`WM_DRAG_XXX`). + * Not to be confused with #UI_but_drag_set_image(), which sets up dragging of an image. + */ +void UI_but_drag_attach_image(uiBut *but, struct ImBuf *imb, float scale); +/** + * \param asset: May be passed from a temporary variable, drag data only stores a copy of this. + */ +void UI_but_drag_set_asset(uiBut *but, + const struct AssetHandle *asset, + const char *path, + struct AssetMetaData *metadata, + int import_type, /* eFileAssetImportType */ + int icon, + struct ImBuf *imb, + float scale); +void UI_but_drag_set_rna(uiBut *but, struct PointerRNA *ptr); +void UI_but_drag_set_path(uiBut *but, const char *path, bool use_free); +void UI_but_drag_set_name(uiBut *but, const char *name); +/** + * Value from button itself. + */ +void UI_but_drag_set_value(uiBut *but); +void UI_but_drag_set_image( + uiBut *but, const char *path, int icon, struct ImBuf *imb, float scale, bool use_free); + /* Panels * * Functions for creating, freeing and drawing panels. The API here @@ -1891,7 +1886,7 @@ struct PointerRNA *UI_region_panel_custom_data_under_cursor(const struct bContex const struct wmEvent *event); void UI_panel_custom_data_set(struct Panel *panel, struct PointerRNA *custom_data); -/* Polyinstantiated panels for representing a list of data. */ +/* Poly-instantiated panels for representing a list of data. */ /** * Called in situations where panels need to be added dynamically rather than * having only one panel corresponding to each #PanelType. @@ -3205,7 +3200,12 @@ void UI_interface_tag_script_reload(void); /* Support click-drag motion which presses the button and closes a popover (like a menu). */ #define USE_UI_POPOVER_ONCE +void UI_block_views_listen(const uiBlock *block, + const struct wmRegionListenerParams *listener_params); + +bool UI_grid_view_item_is_active(const uiGridViewItemHandle *item_handle); bool UI_tree_view_item_is_active(const uiTreeViewItemHandle *item); +bool UI_grid_view_item_matches(const uiGridViewItemHandle *a, const uiGridViewItemHandle *b); bool UI_tree_view_item_matches(const uiTreeViewItemHandle *a, const uiTreeViewItemHandle *b); /** * Attempt to start dragging the tree-item \a item_. This will not work if the tree item doesn't @@ -3243,6 +3243,15 @@ uiTreeViewItemHandle *UI_block_tree_view_find_item_at(const struct ARegion *regi const int xy[2]) ATTR_NONNULL(1, 2); uiTreeViewItemHandle *UI_block_tree_view_find_active_item(const struct ARegion *region); +/** + * Listen to \a notifier, returning true if the region should redraw. + */ +bool UI_tree_view_listen_should_redraw(const uiTreeViewHandle *view, const wmNotifier *notifier); +/** + * Listen to \a notifier, returning true if the region should redraw. + */ +bool UI_grid_view_listen_should_redraw(const uiGridViewHandle *view, const wmNotifier *notifier); + #ifdef __cplusplus } #endif diff --git a/source/blender/editors/include/UI_interface.hh b/source/blender/editors/include/UI_interface.hh index db43ec54431..3dc56b01993 100644 --- a/source/blender/editors/include/UI_interface.hh +++ b/source/blender/editors/include/UI_interface.hh @@ -23,6 +23,7 @@ struct uiSearchItems; namespace blender::ui { +class AbstractGridView; class AbstractTreeView; /** @@ -55,6 +56,10 @@ void attribute_search_add_items( /** * Override this for all available tree types. */ +blender::ui::AbstractGridView *UI_block_add_view( + uiBlock &block, + blender::StringRef idname, + std::unique_ptr<blender::ui::AbstractGridView> tree_view); blender::ui::AbstractTreeView *UI_block_add_view( uiBlock &block, blender::StringRef idname, diff --git a/source/blender/editors/include/UI_interface_icons.h b/source/blender/editors/include/UI_interface_icons.h index 6416421f4f5..a1a98a4b08c 100644 --- a/source/blender/editors/include/UI_interface_icons.h +++ b/source/blender/editors/include/UI_interface_icons.h @@ -92,7 +92,7 @@ void UI_icon_render_id_ex(const struct bContext *C, int UI_icon_preview_to_render_size(enum eIconSizes size); /** - * Draws icon with dpi scale factor. + * Draws icon with DPI scale factor. */ void UI_icon_draw(float x, float y, int icon_id); void UI_icon_draw_alpha(float x, float y, int icon_id, float alpha); diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh index 5504e426f34..1aeb13ca5cc 100644 --- a/source/blender/editors/include/UI_tree_view.hh +++ b/source/blender/editors/include/UI_tree_view.hh @@ -28,6 +28,7 @@ struct uiButTreeRow; struct uiLayout; struct wmDrag; struct wmEvent; +struct wmNotifier; namespace blender::ui { @@ -128,6 +129,9 @@ class AbstractTreeView : public TreeViewItemContainer { void foreach_item(ItemIterFn iter_fn, IterOptions options = IterOptions::None) const; + /** Listen to a notifier, returning true if a redraw is needed. */ + virtual bool listen(const wmNotifier &) const; + /** Only one item can be renamed at a time. */ bool is_renaming() const; @@ -185,7 +189,7 @@ class AbstractTreeViewItem : public TreeViewItemContainer { bool is_renaming_ = false; protected: - /** This label is used for identifying an item within its parent. */ + /** This label is used as the default way to identifying an item within its parent. */ std::string label_{}; /** Every visible item gets a button of type #UI_BTYPE_TREEROW during the layout building. */ uiButTreeRow *tree_row_but_ = nullptr; diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt index 444bb08f3fb..2a1852bd6e7 100644 --- a/source/blender/editors/interface/CMakeLists.txt +++ b/source/blender/editors/interface/CMakeLists.txt @@ -25,12 +25,14 @@ set(INC ) set(SRC + grid_view.cc interface.cc interface_align.c interface_anim.c interface_button_group.c interface_context_menu.c interface_context_path.cc + interface_drag.cc interface_draw.c interface_dropboxes.cc interface_eyedropper.c diff --git a/source/blender/editors/interface/grid_view.cc b/source/blender/editors/interface/grid_view.cc new file mode 100644 index 00000000000..a82cb7798fe --- /dev/null +++ b/source/blender/editors/interface/grid_view.cc @@ -0,0 +1,525 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup edinterface + */ + +#include <limits> +#include <stdexcept> + +#include "BLI_index_range.hh" + +#include "WM_types.h" + +#include "UI_interface.h" +#include "interface_intern.h" + +#include "UI_grid_view.hh" + +namespace blender::ui { + +/* ---------------------------------------------------------------------- */ + +AbstractGridView::AbstractGridView() : style_(UI_preview_tile_size_x(), UI_preview_tile_size_y()) +{ +} + +AbstractGridViewItem &AbstractGridView::add_item(std::unique_ptr<AbstractGridViewItem> item) +{ + items_.append(std::move(item)); + + AbstractGridViewItem &added_item = *items_.last(); + added_item.view_ = this; + + item_map_.add(added_item.identifier_, &added_item); + + return added_item; +} + +void AbstractGridView::foreach_item(ItemIterFn iter_fn) const +{ + for (auto &item_ptr : items_) { + iter_fn(*item_ptr); + } +} + +bool AbstractGridView::listen(const wmNotifier &) const +{ + /* Nothing by default. */ + return false; +} + +AbstractGridViewItem *AbstractGridView::find_matching_item( + const AbstractGridViewItem &item_to_match, const AbstractGridView &view_to_search_in) const +{ + AbstractGridViewItem *const *match = view_to_search_in.item_map_.lookup_ptr( + item_to_match.identifier_); + BLI_assert(!match || item_to_match.matches(**match)); + + return match ? *match : nullptr; +} + +void AbstractGridView::change_state_delayed() +{ + BLI_assert_msg( + is_reconstructed(), + "These state changes are supposed to be delayed until reconstruction is completed"); + foreach_item([](AbstractGridViewItem &item) { item.change_state_delayed(); }); +} + +void AbstractGridView::update_from_old(uiBlock &new_block) +{ + uiGridViewHandle *old_view_handle = ui_block_grid_view_find_matching_in_old_block( + &new_block, reinterpret_cast<uiGridViewHandle *>(this)); + if (!old_view_handle) { + /* Initial construction, nothing to update. */ + is_reconstructed_ = true; + return; + } + + AbstractGridView &old_view = reinterpret_cast<AbstractGridView &>(*old_view_handle); + + foreach_item([this, &old_view](AbstractGridViewItem &new_item) { + const AbstractGridViewItem *matching_old_item = find_matching_item(new_item, old_view); + if (!matching_old_item) { + return; + } + + new_item.update_from_old(*matching_old_item); + }); + + /* Finished (re-)constructing the tree. */ + is_reconstructed_ = true; +} + +bool AbstractGridView::is_reconstructed() const +{ + return is_reconstructed_; +} + +const GridViewStyle &AbstractGridView::get_style() const +{ + return style_; +} + +int AbstractGridView::get_item_count() const +{ + return items_.size(); +} + +GridViewStyle::GridViewStyle(int width, int height) : tile_width(width), tile_height(height) +{ +} + +/* ---------------------------------------------------------------------- */ + +AbstractGridViewItem::AbstractGridViewItem(StringRef identifier) : identifier_(identifier) +{ +} + +bool AbstractGridViewItem::matches(const AbstractGridViewItem &other) const +{ + return identifier_ == other.identifier_; +} + +void AbstractGridViewItem::grid_tile_click_fn(struct bContext * /*C*/, + void *but_arg1, + void * /*arg2*/) +{ + uiButGridTile *grid_tile_but = (uiButGridTile *)but_arg1; + AbstractGridViewItem &grid_item = reinterpret_cast<AbstractGridViewItem &>( + *grid_tile_but->view_item); + + grid_item.activate(); +} + +void AbstractGridViewItem::add_grid_tile_button(uiBlock &block) +{ + const GridViewStyle &style = get_view().get_style(); + grid_tile_but_ = (uiButGridTile *)uiDefBut(&block, + UI_BTYPE_GRID_TILE, + 0, + "", + 0, + 0, + style.tile_width, + style.tile_height, + nullptr, + 0, + 0, + 0, + 0, + ""); + + grid_tile_but_->view_item = reinterpret_cast<uiGridViewItemHandle *>(this); + UI_but_func_set(&grid_tile_but_->but, grid_tile_click_fn, grid_tile_but_, nullptr); +} + +bool AbstractGridViewItem::is_active() const +{ + BLI_assert_msg(get_view().is_reconstructed(), + "State can't be queried until reconstruction is completed"); + return is_active_; +} + +void AbstractGridViewItem::on_activate() +{ + /* Do nothing by default. */ +} + +std::optional<bool> AbstractGridViewItem::should_be_active() const +{ + return std::nullopt; +} + +void AbstractGridViewItem::change_state_delayed() +{ + const std::optional<bool> should_be_active = this->should_be_active(); + if (should_be_active.has_value() && *should_be_active) { + activate(); + } +} + +void AbstractGridViewItem::update_from_old(const AbstractGridViewItem &old) +{ + is_active_ = old.is_active_; +} + +void AbstractGridViewItem::activate() +{ + BLI_assert_msg(get_view().is_reconstructed(), + "Item activation can't be done until reconstruction is completed"); + + if (is_active()) { + return; + } + + /* Deactivate other items in the tree. */ + get_view().foreach_item([](auto &item) { item.deactivate(); }); + + on_activate(); + + is_active_ = true; +} + +void AbstractGridViewItem::deactivate() +{ + is_active_ = false; +} + +const AbstractGridView &AbstractGridViewItem::get_view() const +{ + if (UNLIKELY(!view_)) { + throw std::runtime_error( + "Invalid state, item must be added through AbstractGridView::add_item()"); + } + return *view_; +} + +/* ---------------------------------------------------------------------- */ + +/** + * Helper for only adding layout items for grid items that are actually in view. 3 main functions: + * - #is_item_visible(): Query if an item of a given index is visible in the view (others should be + * skipped when building the layout). + * - #fill_layout_before_visible(): Add empty space to the layout before a visible row is drawn, so + * the layout height is the same as if all items were added (important to get the correct scroll + * height). + * - #fill_layout_after_visible(): Same thing, just adds empty space for after the last visible + * row. + * + * Does two assumptions: + * - Top-to-bottom flow (ymax = 0 and ymin < 0). If that's not good enough, View2D should + * probably provide queries for the scroll offset. + * - Only vertical scrolling. For horizontal scrolling, spacers would have to be added on the + * side(s) as well. + */ +class BuildOnlyVisibleButtonsHelper { + const View2D &v2d_; + const AbstractGridView &grid_view_; + const GridViewStyle &style_; + const int cols_per_row_ = 0; + /* Indices of items within the view. Calculated by constructor */ + IndexRange visible_items_range_{}; + + public: + BuildOnlyVisibleButtonsHelper(const View2D &, + const AbstractGridView &grid_view, + int cols_per_row); + + bool is_item_visible(int item_idx) const; + void fill_layout_before_visible(uiBlock &) const; + void fill_layout_after_visible(uiBlock &) const; + + private: + IndexRange get_visible_range() const; + void add_spacer_button(uiBlock &, int row_count) const; +}; + +BuildOnlyVisibleButtonsHelper::BuildOnlyVisibleButtonsHelper(const View2D &v2d, + const AbstractGridView &grid_view, + const int cols_per_row) + : v2d_(v2d), grid_view_(grid_view), style_(grid_view.get_style()), cols_per_row_(cols_per_row) +{ + visible_items_range_ = get_visible_range(); +} + +IndexRange BuildOnlyVisibleButtonsHelper::get_visible_range() const +{ + int first_idx_in_view = 0; + int max_items_in_view = 0; + + const float scroll_ofs_y = abs(v2d_.cur.ymax - v2d_.tot.ymax); + if (!IS_EQF(scroll_ofs_y, 0)) { + const int scrolled_away_rows = (int)scroll_ofs_y / style_.tile_height; + + first_idx_in_view = scrolled_away_rows * cols_per_row_; + } + + const float view_height = BLI_rctf_size_y(&v2d_.cur); + const int count_rows_in_view = std::max(round_fl_to_int(view_height / style_.tile_height), 1); + max_items_in_view = (count_rows_in_view + 1) * cols_per_row_; + + BLI_assert(max_items_in_view > 0); + return IndexRange(first_idx_in_view, max_items_in_view); +} + +bool BuildOnlyVisibleButtonsHelper::is_item_visible(const int item_idx) const +{ + return visible_items_range_.contains(item_idx); +} + +void BuildOnlyVisibleButtonsHelper::fill_layout_before_visible(uiBlock &block) const +{ + const float scroll_ofs_y = abs(v2d_.cur.ymax - v2d_.tot.ymax); + + if (IS_EQF(scroll_ofs_y, 0)) { + return; + } + + const int scrolled_away_rows = (int)scroll_ofs_y / style_.tile_height; + add_spacer_button(block, scrolled_away_rows); +} + +void BuildOnlyVisibleButtonsHelper::fill_layout_after_visible(uiBlock &block) const +{ + const int last_item_idx = grid_view_.get_item_count() - 1; + const int last_visible_idx = visible_items_range_.last(); + + if (last_item_idx > last_visible_idx) { + const int remaining_rows = (cols_per_row_ > 0) ? + (last_item_idx - last_visible_idx) / cols_per_row_ : + 0; + BuildOnlyVisibleButtonsHelper::add_spacer_button(block, remaining_rows); + } +} + +void BuildOnlyVisibleButtonsHelper::add_spacer_button(uiBlock &block, const int row_count) const +{ + /* UI code only supports button dimensions of `signed short` size, the layout height we want to + * fill may be bigger than that. So add multiple labels of the maximum size if necessary. */ + for (int remaining_rows = row_count; remaining_rows > 0;) { + const short row_count_this_iter = std::min( + std::numeric_limits<short>::max() / style_.tile_height, remaining_rows); + + uiDefBut(&block, + UI_BTYPE_LABEL, + 0, + "", + 0, + 0, + UI_UNIT_X, + row_count_this_iter * style_.tile_height, + nullptr, + 0, + 0, + 0, + 0, + ""); + remaining_rows -= row_count_this_iter; + } +} + +/* ---------------------------------------------------------------------- */ + +class GridViewLayoutBuilder { + uiBlock &block_; + + friend class GridViewBuilder; + + public: + GridViewLayoutBuilder(uiBlock &block); + + void build_from_view(const AbstractGridView &grid_view, const View2D &v2d) const; + + private: + void build_grid_tile(uiLayout &grid_layout, AbstractGridViewItem &item) const; + + uiLayout *current_layout() const; +}; + +GridViewLayoutBuilder::GridViewLayoutBuilder(uiBlock &block) : block_(block) +{ +} + +void GridViewLayoutBuilder::build_grid_tile(uiLayout &grid_layout, + AbstractGridViewItem &item) const +{ + uiLayout *overlap = uiLayoutOverlap(&grid_layout); + + item.add_grid_tile_button(block_); + item.build_grid_tile(*uiLayoutRow(overlap, false)); +} + +void GridViewLayoutBuilder::build_from_view(const AbstractGridView &grid_view, + const View2D &v2d) const +{ + uiLayout *prev_layout = current_layout(); + + uiLayout &layout = *uiLayoutColumn(current_layout(), false); + const GridViewStyle &style = grid_view.get_style(); + + const int cols_per_row = std::max(uiLayoutGetWidth(&layout) / style.tile_width, 1); + + BuildOnlyVisibleButtonsHelper build_visible_helper(v2d, grid_view, cols_per_row); + + build_visible_helper.fill_layout_before_visible(block_); + + /* Use `-cols_per_row` because the grid layout uses a multiple of the passed absolute value for + * the number of columns then, rather than distributing the number of items evenly over rows and + * stretching the items to fit (see #uiLayoutItemGridFlow.columns_len). */ + uiLayout *grid_layout = uiLayoutGridFlow(&layout, true, -cols_per_row, true, true, true); + + int item_idx = 0; + grid_view.foreach_item([&](AbstractGridViewItem &item) { + /* Skip if item isn't visible. */ + if (!build_visible_helper.is_item_visible(item_idx)) { + item_idx++; + return; + } + + build_grid_tile(*grid_layout, item); + item_idx++; + }); + + /* If there are not enough items to fill the layout, add padding items so the layout doesn't + * stretch over the entire width. */ + if (grid_view.get_item_count() < cols_per_row) { + for (int padding_item_idx = 0; padding_item_idx < (cols_per_row - grid_view.get_item_count()); + padding_item_idx++) { + uiItemS(grid_layout); + } + } + + UI_block_layout_set_current(&block_, prev_layout); + + build_visible_helper.fill_layout_after_visible(block_); +} + +uiLayout *GridViewLayoutBuilder::current_layout() const +{ + return block_.curlayout; +} + +/* ---------------------------------------------------------------------- */ + +GridViewBuilder::GridViewBuilder(uiBlock &block) : block_(block) +{ +} + +void GridViewBuilder::build_grid_view(AbstractGridView &grid_view, const View2D &v2d) +{ + grid_view.build_items(); + grid_view.update_from_old(block_); + grid_view.change_state_delayed(); + + GridViewLayoutBuilder builder(block_); + builder.build_from_view(grid_view, v2d); +} + +/* ---------------------------------------------------------------------- */ + +PreviewGridItem::PreviewGridItem(StringRef identifier, StringRef label, int preview_icon_id) + : AbstractGridViewItem(identifier), label(label), preview_icon_id(preview_icon_id) +{ +} + +void PreviewGridItem::build_grid_tile(uiLayout &layout) const +{ + const GridViewStyle &style = get_view().get_style(); + uiBlock *block = uiLayoutGetBlock(&layout); + + uiBut *but = uiDefBut(block, + UI_BTYPE_PREVIEW_TILE, + 0, + label.c_str(), + 0, + 0, + style.tile_width, + style.tile_height, + nullptr, + 0, + 0, + 0, + 0, + ""); + ui_def_but_icon(but, + preview_icon_id, + /* NOLINTNEXTLINE: bugprone-suspicious-enum-usage */ + UI_HAS_ICON | UI_BUT_ICON_PREVIEW); +} + +void PreviewGridItem::set_on_activate_fn(ActivateFn fn) +{ + activate_fn_ = fn; +} + +void PreviewGridItem::set_is_active_fn(IsActiveFn fn) +{ + is_active_fn_ = fn; +} + +void PreviewGridItem::on_activate() +{ + if (activate_fn_) { + activate_fn_(*this); + } +} + +std::optional<bool> PreviewGridItem::should_be_active() const +{ + if (is_active_fn_) { + return is_active_fn_(); + } + return std::nullopt; +} + +} // namespace blender::ui + +using namespace blender::ui; + +/* ---------------------------------------------------------------------- */ +/* C-API */ + +using namespace blender::ui; + +bool UI_grid_view_item_is_active(const uiGridViewItemHandle *item_handle) +{ + const AbstractGridViewItem &item = reinterpret_cast<const AbstractGridViewItem &>(*item_handle); + return item.is_active(); +} + +bool UI_grid_view_listen_should_redraw(const uiGridViewHandle *view_handle, + const wmNotifier *notifier) +{ + const AbstractGridView &view = *reinterpret_cast<const AbstractGridView *>(view_handle); + return view.listen(*notifier); +} + +bool UI_grid_view_item_matches(const uiGridViewItemHandle *a_handle, + const uiGridViewItemHandle *b_handle) +{ + const AbstractGridViewItem &a = reinterpret_cast<const AbstractGridViewItem &>(*a_handle); + const AbstractGridViewItem &b = reinterpret_cast<const AbstractGridViewItem &>(*b_handle); + return a.matches(b); +} diff --git a/source/blender/editors/interface/interface.cc b/source/blender/editors/interface/interface.cc index b7098c26bcd..3f623566807 100644 --- a/source/blender/editors/interface/interface.cc +++ b/source/blender/editors/interface/interface.cc @@ -778,6 +778,15 @@ static bool ui_but_equals_old(const uiBut *but, const uiBut *oldbut) } } + if ((but->type == UI_BTYPE_GRID_TILE) && (oldbut->type == UI_BTYPE_GRID_TILE)) { + uiButGridTile *but_gridtile = (uiButGridTile *)but; + uiButGridTile *oldbut_gridtile = (uiButGridTile *)oldbut; + if (!but_gridtile->view_item || !oldbut_gridtile->view_item || + !UI_grid_view_item_matches(but_gridtile->view_item, oldbut_gridtile->view_item)) { + return false; + } + } + return true; } @@ -904,6 +913,12 @@ static void ui_but_update_old_active_from_new(uiBut *oldbut, uiBut *but) SWAP(uiTreeViewItemHandle *, treerow_newbut->tree_item, treerow_oldbut->tree_item); break; } + case UI_BTYPE_GRID_TILE: { + uiButGridTile *gridtile_oldbut = (uiButGridTile *)oldbut; + uiButGridTile *gridtile_newbut = (uiButGridTile *)but; + SWAP(uiGridViewItemHandle *, gridtile_newbut->view_item, gridtile_oldbut->view_item); + break; + } default: break; } @@ -927,9 +942,12 @@ static void ui_but_update_old_active_from_new(uiBut *oldbut, uiBut *but) BLI_strncpy(oldbut->strdata, but->strdata, sizeof(oldbut->strdata)); } - if (but->dragpoin && (but->dragflag & UI_BUT_DRAGPOIN_FREE)) { + if (but->dragpoin) { SWAP(void *, but->dragpoin, oldbut->dragpoin); } + if (but->imb) { + SWAP(ImBuf *, but->imb, oldbut->imb); + } /* NOTE: if layout hasn't been applied yet, it uses old button pointers... */ } @@ -993,9 +1011,9 @@ static bool ui_but_update_from_old_block(const bContext *C, else { int flag_copy = UI_BUT_DRAG_MULTI; - /* Stupid special case: The active button may be inside (as in, overlapped on top) a tree-row + /* Stupid special case: The active button may be inside (as in, overlapped on top) a view-item * button which we also want to keep highlighted then. */ - if (but->type == UI_BTYPE_TREEROW) { + if (ui_but_is_view_item(but)) { flag_copy |= UI_ACTIVE; } @@ -1861,15 +1879,32 @@ bool ui_but_context_poll_operator_ex(bContext *C, const wmOperatorCallParams *optype_params) { bool result; + int old_but_flag = 0; - if (but && but->context) { - CTX_store_set(C, but->context); + if (but) { + old_but_flag = but->flag; + + /* Temporarily make this button override the active one, in case the poll acts on the active + * button. */ + const_cast<uiBut *>(but)->flag |= UI_BUT_ACTIVE_OVERRIDE; + + if (but->context) { + CTX_store_set(C, but->context); + } } result = WM_operator_poll_context(C, optype_params->optype, optype_params->opcontext); - if (but && but->context) { - CTX_store_set(C, nullptr); + if (but) { + BLI_assert_msg((but->flag & ~UI_BUT_ACTIVE_OVERRIDE) == + (old_but_flag & ~UI_BUT_ACTIVE_OVERRIDE), + "Operator polls shouldn't change button flags"); + + const_cast<uiBut *>(but)->flag = old_but_flag; + + if (but->context) { + CTX_store_set(C, nullptr); + } } return result; @@ -2219,6 +2254,15 @@ int ui_but_is_pushed_ex(uiBut *but, double *value) } break; } + case UI_BTYPE_GRID_TILE: { + uiButGridTile *grid_tile_but = (uiButGridTile *)but; + + is_push = -1; + if (grid_tile_but->view_item) { + is_push = UI_grid_view_item_is_active(grid_tile_but->view_item); + } + break; + } default: is_push = -1; break; @@ -3425,9 +3469,7 @@ static void ui_but_free(const bContext *C, uiBut *but) IMB_freeImBuf((struct ImBuf *)but->poin); } - if (but->dragpoin && (but->dragflag & UI_BUT_DRAGPOIN_FREE)) { - WM_drag_data_free(but->dragtype, but->dragpoin); - } + ui_but_drag_free(but); ui_but_extra_operator_icons_free(but); BLI_assert(UI_butstore_is_registered(but->block, but) == false); @@ -3832,21 +3874,22 @@ static void ui_but_update_ex(uiBut *but, const bool validate) } case UI_BTYPE_HOTKEY_EVENT: if (but->flag & UI_SELECT) { + const uiButHotkeyEvent *hotkey_but = (uiButHotkeyEvent *)but; - if (but->modifier_key) { + if (hotkey_but->modifier_key) { char *str = but->drawstr; but->drawstr[0] = '\0'; - if (but->modifier_key & KM_SHIFT) { + if (hotkey_but->modifier_key & KM_SHIFT) { str += BLI_strcpy_rlen(str, "Shift "); } - if (but->modifier_key & KM_CTRL) { + if (hotkey_but->modifier_key & KM_CTRL) { str += BLI_strcpy_rlen(str, "Ctrl "); } - if (but->modifier_key & KM_ALT) { + if (hotkey_but->modifier_key & KM_ALT) { str += BLI_strcpy_rlen(str, "Alt "); } - if (but->modifier_key & KM_OSKEY) { + if (hotkey_but->modifier_key & KM_OSKEY) { str += BLI_strcpy_rlen(str, "Cmd "); } @@ -3972,6 +4015,14 @@ static void ui_but_alloc_info(const eButType type, alloc_size = sizeof(uiButTreeRow); alloc_str = "uiButTreeRow"; break; + case UI_BTYPE_HOTKEY_EVENT: + alloc_size = sizeof(uiButHotkeyEvent); + alloc_str = "uiButHotkeyEvent"; + break; + case UI_BTYPE_GRID_TILE: + alloc_size = sizeof(uiButGridTile); + alloc_str = "uiButGridTile"; + break; default: alloc_size = sizeof(uiBut); alloc_str = "uiBut"; @@ -4946,6 +4997,33 @@ int UI_autocomplete_end(AutoComplete *autocpl, char *autoname) return match; } +#define PREVIEW_TILE_PAD (0.15f * UI_UNIT_X) + +int UI_preview_tile_size_x(void) +{ + const float pad = PREVIEW_TILE_PAD; + return round_fl_to_int((96.0f / 20.0f) * UI_UNIT_X + 2.0f * pad); +} + +int UI_preview_tile_size_y(void) +{ + const uiStyle *style = UI_style_get(); + const float font_height = style->widget.points * UI_DPI_FAC; + const float pad = PREVIEW_TILE_PAD; + + return round_fl_to_int(UI_preview_tile_size_y_no_label() + font_height + + /* Add some extra padding to make things less tight vertically. */ + pad); +} + +int UI_preview_tile_size_y_no_label(void) +{ + const float pad = PREVIEW_TILE_PAD; + return round_fl_to_int((96.0f / 20.0f) * UI_UNIT_Y + 2.0f * pad); +} + +#undef PREVIEW_TILE_PAD + static void ui_but_update_and_icon_set(uiBut *but, int icon) { if (icon) { @@ -5856,104 +5934,6 @@ int UI_but_return_value_get(uiBut *but) return but->retval; } -void UI_but_drag_set_id(uiBut *but, ID *id) -{ - but->dragtype = WM_DRAG_ID; - if (but->dragflag & UI_BUT_DRAGPOIN_FREE) { - WM_drag_data_free(but->dragtype, but->dragpoin); - but->dragflag &= ~UI_BUT_DRAGPOIN_FREE; - } - but->dragpoin = (void *)id; -} - -void UI_but_drag_attach_image(uiBut *but, struct ImBuf *imb, const float scale) -{ - but->imb = imb; - but->imb_scale = scale; -} - -void UI_but_drag_set_asset(uiBut *but, - const AssetHandle *asset, - const char *path, - struct AssetMetaData *metadata, - int import_type, - int icon, - struct ImBuf *imb, - float scale) -{ - wmDragAsset *asset_drag = WM_drag_create_asset_data(asset, metadata, path, import_type); - - /* FIXME: This is temporary evil solution to get scene/viewlayer/etc in the copy callback of the - * #wmDropBox. - * TODO: Handle link/append in operator called at the end of the drop process, and NOT in its - * copy callback. - * */ - asset_drag->evil_C = static_cast<bContext *>(but->block->evil_C); - - but->dragtype = WM_DRAG_ASSET; - ui_def_but_icon(but, icon, 0); /* no flag UI_HAS_ICON, so icon doesn't draw in button */ - if (but->dragflag & UI_BUT_DRAGPOIN_FREE) { - WM_drag_data_free(but->dragtype, but->dragpoin); - } - but->dragpoin = asset_drag; - but->dragflag |= UI_BUT_DRAGPOIN_FREE; - UI_but_drag_attach_image(but, imb, scale); -} - -void UI_but_drag_set_rna(uiBut *but, PointerRNA *ptr) -{ - but->dragtype = WM_DRAG_RNA; - if (but->dragflag & UI_BUT_DRAGPOIN_FREE) { - WM_drag_data_free(but->dragtype, but->dragpoin); - but->dragflag &= ~UI_BUT_DRAGPOIN_FREE; - } - but->dragpoin = (void *)ptr; -} - -void UI_but_drag_set_path(uiBut *but, const char *path, const bool use_free) -{ - but->dragtype = WM_DRAG_PATH; - if (but->dragflag & UI_BUT_DRAGPOIN_FREE) { - WM_drag_data_free(but->dragtype, but->dragpoin); - but->dragflag &= ~UI_BUT_DRAGPOIN_FREE; - } - but->dragpoin = (void *)path; - if (use_free) { - but->dragflag |= UI_BUT_DRAGPOIN_FREE; - } -} - -void UI_but_drag_set_name(uiBut *but, const char *name) -{ - but->dragtype = WM_DRAG_NAME; - if (but->dragflag & UI_BUT_DRAGPOIN_FREE) { - WM_drag_data_free(but->dragtype, but->dragpoin); - but->dragflag &= ~UI_BUT_DRAGPOIN_FREE; - } - but->dragpoin = (void *)name; -} - -void UI_but_drag_set_value(uiBut *but) -{ - but->dragtype = WM_DRAG_VALUE; -} - -void UI_but_drag_set_image( - uiBut *but, const char *path, int icon, struct ImBuf *imb, float scale, const bool use_free) -{ - but->dragtype = WM_DRAG_PATH; - ui_def_but_icon(but, icon, 0); /* no flag UI_HAS_ICON, so icon doesn't draw in button */ - if (but->dragflag & UI_BUT_DRAGPOIN_FREE) { - WM_drag_data_free(but->dragtype, but->dragpoin); - but->dragflag &= ~UI_BUT_DRAGPOIN_FREE; - } - but->dragpoin = (void *)path; - if (use_free) { - but->dragflag |= UI_BUT_DRAGPOIN_FREE; - } - UI_but_drag_attach_image(but, imb, scale); -} - PointerRNA *UI_but_operator_ptr_get(uiBut *but) { if (but->optype && !but->opptr) { @@ -6263,64 +6243,6 @@ uiBut *uiDefIconBlockBut(uiBlock *block, return but; } -uiBut *uiDefKeyevtButS(uiBlock *block, - int retval, - const char *str, - int x, - int y, - short width, - short height, - short *spoin, - const char *tip) -{ - uiBut *but = ui_def_but(block, - UI_BTYPE_KEY_EVENT | UI_BUT_POIN_SHORT, - retval, - str, - x, - y, - width, - height, - spoin, - 0.0, - 0.0, - 0.0, - 0.0, - tip); - ui_but_update(but); - return but; -} - -uiBut *uiDefHotKeyevtButS(uiBlock *block, - int retval, - const char *str, - int x, - int y, - short width, - short height, - short *keypoin, - const short *modkeypoin, - const char *tip) -{ - uiBut *but = ui_def_but(block, - UI_BTYPE_HOTKEY_EVENT | UI_BUT_POIN_SHORT, - retval, - str, - x, - y, - width, - height, - keypoin, - 0.0, - 0.0, - 0.0, - 0.0, - tip); - but->modifier_key = *modkeypoin; - ui_but_update(but); - return but; -} - uiBut *uiDefSearchBut(uiBlock *block, void *arg, int retval, diff --git a/source/blender/editors/interface/interface_anim.c b/source/blender/editors/interface/interface_anim.c index e838ce37d8e..0e69b4bb358 100644 --- a/source/blender/editors/interface/interface_anim.c +++ b/source/blender/editors/interface/interface_anim.c @@ -293,7 +293,7 @@ bool ui_but_anim_expression_create(uiBut *but, const char *str) void ui_but_anim_autokey(bContext *C, uiBut *but, Scene *scene, float cfra) { - ED_autokeyframe_property(C, scene, &but->rnapoin, but->rnaprop, but->rnaindex, cfra); + ED_autokeyframe_property(C, scene, &but->rnapoin, but->rnaprop, but->rnaindex, cfra, true); } void ui_but_anim_copy_driver(bContext *C) diff --git a/source/blender/editors/interface/interface_drag.cc b/source/blender/editors/interface/interface_drag.cc new file mode 100644 index 00000000000..4c68870b2c7 --- /dev/null +++ b/source/blender/editors/interface/interface_drag.cc @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup edinterface + */ + +#include "UI_interface.h" + +#include "WM_api.h" + +#include "interface_intern.h" + +void UI_but_drag_set_id(uiBut *but, ID *id) +{ + but->dragtype = WM_DRAG_ID; + if (but->dragflag & UI_BUT_DRAGPOIN_FREE) { + WM_drag_data_free(but->dragtype, but->dragpoin); + but->dragflag &= ~UI_BUT_DRAGPOIN_FREE; + } + but->dragpoin = (void *)id; +} + +void UI_but_drag_attach_image(uiBut *but, struct ImBuf *imb, const float scale) +{ + but->imb = imb; + but->imb_scale = scale; +} + +void UI_but_drag_set_asset(uiBut *but, + const AssetHandle *asset, + const char *path, + struct AssetMetaData *metadata, + int import_type, + int icon, + struct ImBuf *imb, + float scale) +{ + wmDragAsset *asset_drag = WM_drag_create_asset_data(asset, metadata, path, import_type); + + /* FIXME: This is temporary evil solution to get scene/viewlayer/etc in the copy callback of the + * #wmDropBox. + * TODO: Handle link/append in operator called at the end of the drop process, and NOT in its + * copy callback. + * */ + asset_drag->evil_C = static_cast<bContext *>(but->block->evil_C); + + but->dragtype = WM_DRAG_ASSET; + ui_def_but_icon(but, icon, 0); /* no flag UI_HAS_ICON, so icon doesn't draw in button */ + if (but->dragflag & UI_BUT_DRAGPOIN_FREE) { + WM_drag_data_free(but->dragtype, but->dragpoin); + } + but->dragpoin = asset_drag; + but->dragflag |= UI_BUT_DRAGPOIN_FREE; + UI_but_drag_attach_image(but, imb, scale); +} + +void UI_but_drag_set_rna(uiBut *but, PointerRNA *ptr) +{ + but->dragtype = WM_DRAG_RNA; + if (but->dragflag & UI_BUT_DRAGPOIN_FREE) { + WM_drag_data_free(but->dragtype, but->dragpoin); + but->dragflag &= ~UI_BUT_DRAGPOIN_FREE; + } + but->dragpoin = (void *)ptr; +} + +void UI_but_drag_set_path(uiBut *but, const char *path, const bool use_free) +{ + but->dragtype = WM_DRAG_PATH; + if (but->dragflag & UI_BUT_DRAGPOIN_FREE) { + WM_drag_data_free(but->dragtype, but->dragpoin); + but->dragflag &= ~UI_BUT_DRAGPOIN_FREE; + } + but->dragpoin = (void *)path; + if (use_free) { + but->dragflag |= UI_BUT_DRAGPOIN_FREE; + } +} + +void UI_but_drag_set_name(uiBut *but, const char *name) +{ + but->dragtype = WM_DRAG_NAME; + if (but->dragflag & UI_BUT_DRAGPOIN_FREE) { + WM_drag_data_free(but->dragtype, but->dragpoin); + but->dragflag &= ~UI_BUT_DRAGPOIN_FREE; + } + but->dragpoin = (void *)name; +} + +void UI_but_drag_set_value(uiBut *but) +{ + but->dragtype = WM_DRAG_VALUE; +} + +void UI_but_drag_set_image( + uiBut *but, const char *path, int icon, struct ImBuf *imb, float scale, const bool use_free) +{ + but->dragtype = WM_DRAG_PATH; + ui_def_but_icon(but, icon, 0); /* no flag UI_HAS_ICON, so icon doesn't draw in button */ + if (but->dragflag & UI_BUT_DRAGPOIN_FREE) { + WM_drag_data_free(but->dragtype, but->dragpoin); + but->dragflag &= ~UI_BUT_DRAGPOIN_FREE; + } + but->dragpoin = (void *)path; + if (use_free) { + but->dragflag |= UI_BUT_DRAGPOIN_FREE; + } + UI_but_drag_attach_image(but, imb, scale); +} + +void ui_but_drag_free(uiBut *but) +{ + if (but->dragpoin && (but->dragflag & UI_BUT_DRAGPOIN_FREE)) { + WM_drag_data_free(but->dragtype, but->dragpoin); + } +} + +bool ui_but_drag_is_draggable(const uiBut *but) +{ + return but->dragpoin != nullptr; +} + +void ui_but_drag_start(bContext *C, uiBut *but) +{ + wmDrag *drag = WM_event_start_drag(C, + but->icon, + but->dragtype, + but->dragpoin, + ui_but_value_get(but), + (but->dragflag & UI_BUT_DRAGPOIN_FREE) ? WM_DRAG_FREE_DATA : + WM_DRAG_NOP); + /* wmDrag has ownership over dragpoin now, stop messing with it. */ + but->dragpoin = NULL; + + if (but->imb) { + WM_event_drag_image(drag, but->imb, but->imb_scale); + } + + /* Special feature for assets: We add another drag item that supports multiple assets. It + * gets the assets from context. */ + if (ELEM(but->dragtype, WM_DRAG_ASSET, WM_DRAG_ID)) { + WM_event_start_drag(C, ICON_NONE, WM_DRAG_ASSET_LIST, NULL, 0, WM_DRAG_NOP); + } +} diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c index 18bad7949ee..d201820fbb6 100644 --- a/source/blender/editors/interface/interface_draw.c +++ b/source/blender/editors/interface/interface_draw.c @@ -130,7 +130,7 @@ void UI_draw_roundbox_4fv_ex(const rctf *rect, void UI_draw_roundbox_3ub_alpha( const rctf *rect, bool filled, float rad, const uchar col[3], uchar alpha) { - float colv[4] = { + const float colv[4] = { ((float)col[0]) / 255, ((float)col[1]) / 255, ((float)col[2]) / 255, @@ -142,7 +142,7 @@ void UI_draw_roundbox_3ub_alpha( void UI_draw_roundbox_3fv_alpha( const rctf *rect, bool filled, float rad, const float col[3], float alpha) { - float colv[4] = {col[0], col[1], col[2], alpha}; + const float colv[4] = {col[0], col[1], col[2], alpha}; UI_draw_roundbox_4fv_ex(rect, (filled) ? colv : NULL, NULL, 1.0f, colv, U.pixelsize, rad); } diff --git a/source/blender/editors/interface/interface_eyedropper_color.c b/source/blender/editors/interface/interface_eyedropper_color.c index 895f8d0d840..c015a60de89 100644 --- a/source/blender/editors/interface/interface_eyedropper_color.c +++ b/source/blender/editors/interface/interface_eyedropper_color.c @@ -50,8 +50,6 @@ #include "RE_pipeline.h" -#include "RE_pipeline.h" - #include "interface_eyedropper_intern.h" typedef struct Eyedropper { @@ -329,7 +327,7 @@ void eyedropper_color_sample_fl(bContext *C, const int m_xy[2], float r_col[3]) ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, mval); if (region) { SpaceImage *sima = area->spacedata.first; - int region_mval[2] = {mval[0] - region->winrct.xmin, mval[1] - region->winrct.ymin}; + const int region_mval[2] = {mval[0] - region->winrct.xmin, mval[1] - region->winrct.ymin}; if (ED_space_image_color_sample(sima, region, region_mval, r_col, NULL)) { return; @@ -340,7 +338,7 @@ void eyedropper_color_sample_fl(bContext *C, const int m_xy[2], float r_col[3]) ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, mval); if (region) { SpaceNode *snode = area->spacedata.first; - int region_mval[2] = {mval[0] - region->winrct.xmin, mval[1] - region->winrct.ymin}; + const int region_mval[2] = {mval[0] - region->winrct.xmin, mval[1] - region->winrct.ymin}; if (ED_space_node_color_sample(bmain, snode, region, region_mval, r_col)) { return; @@ -351,7 +349,7 @@ void eyedropper_color_sample_fl(bContext *C, const int m_xy[2], float r_col[3]) ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, mval); if (region) { SpaceClip *sc = area->spacedata.first; - int region_mval[2] = {mval[0] - region->winrct.xmin, mval[1] - region->winrct.ymin}; + const int region_mval[2] = {mval[0] - region->winrct.xmin, mval[1] - region->winrct.ymin}; if (ED_space_clip_color_sample(sc, region, region_mval, r_col)) { return; diff --git a/source/blender/editors/interface/interface_eyedropper_datablock.c b/source/blender/editors/interface/interface_eyedropper_datablock.c index aec8f56678a..01b958576b6 100644 --- a/source/blender/editors/interface/interface_eyedropper_datablock.c +++ b/source/blender/editors/interface/interface_eyedropper_datablock.c @@ -156,7 +156,7 @@ static void datadropper_id_sample_pt( CTX_wm_area_set(C, area); CTX_wm_region_set(C, region); - /* grr, always draw else we leave stale text */ + /* Unfortunately it's necessary to always draw else we leave stale text. */ ED_region_tag_redraw(region); if (area->spacetype == SPACE_VIEW3D) { diff --git a/source/blender/editors/interface/interface_eyedropper_depth.c b/source/blender/editors/interface/interface_eyedropper_depth.c index 56bc1a6d956..3c6f127582a 100644 --- a/source/blender/editors/interface/interface_eyedropper_depth.c +++ b/source/blender/editors/interface/interface_eyedropper_depth.c @@ -171,7 +171,7 @@ static void depthdropper_depth_sample_pt(bContext *C, CTX_wm_area_set(C, area); CTX_wm_region_set(C, region); - /* grr, always draw else we leave stale text */ + /* Unfortunately it's necessary to always draw otherwise we leave stale text. */ ED_region_tag_redraw(region); view3d_operator_needs_opengl(C); diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 2c408619fe7..7c00c4f1875 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -384,6 +384,12 @@ typedef struct uiHandleButtonData { /* True when alt is held and the preference for displaying tooltips should be ignored. */ bool tooltip_force; + /** + * Behave as if #UI_BUT_DISABLED is set (without drawing grayed out). + * Needed so non-interactive labels can be activated for the purpose of showing tool-tips, + * without them blocking interaction with nodes, see: T97386. + */ + bool disable_force; /* auto open */ bool used_mouse; @@ -536,7 +542,7 @@ static bool but_copypaste_profile_alive = false; bool ui_but_is_editing(const uiBut *but) { - uiHandleButtonData *data = but->active; + const uiHandleButtonData *data = but->active; return (data && ELEM(data->state, BUTTON_STATE_TEXT_EDITING, BUTTON_STATE_NUM_EDITING)); } @@ -654,21 +660,23 @@ static bool ui_but_dragedit_update_mval(uiHandleButtonData *data, int mx) static bool ui_rna_is_userdef(PointerRNA *ptr, PropertyRNA *prop) { /* Not very elegant, but ensures preference changes force re-save. */ - bool tag = false; - if (prop && !(RNA_property_flag(prop) & PROP_NO_DEG_UPDATE)) { - StructRNA *base = RNA_struct_base(ptr->type); - if (base == NULL) { - base = ptr->type; - } - if (ELEM(base, - &RNA_AddonPreferences, - &RNA_KeyConfigPreferences, - &RNA_KeyMapItem, - &RNA_UserAssetLibrary)) { - tag = true; - } + + if (!prop) { + return false; } - return tag; + if (RNA_property_flag(prop) & PROP_NO_DEG_UPDATE) { + return false; + } + + StructRNA *base = RNA_struct_base(ptr->type); + if (base == NULL) { + base = ptr->type; + } + return ELEM(base, + &RNA_AddonPreferences, + &RNA_KeyConfigPreferences, + &RNA_KeyMapItem, + &RNA_UserAssetLibrary); } bool UI_but_is_userdef(const uiBut *but) @@ -894,64 +902,66 @@ static void ui_apply_but_func(bContext *C, uiBut *but) /* typically call ui_apply_but_undo(), ui_apply_but_autokey() */ static void ui_apply_but_undo(uiBut *but) { - if (but->flag & UI_BUT_UNDO) { - const char *str = NULL; - size_t str_len_clip = SIZE_MAX - 1; - bool skip_undo = false; + if (!(but->flag & UI_BUT_UNDO)) { + return; + } - /* define which string to use for undo */ - if (but->type == UI_BTYPE_MENU) { - str = but->drawstr; - str_len_clip = ui_but_drawstr_len_without_sep_char(but); - } - else if (but->drawstr[0]) { - str = but->drawstr; - str_len_clip = ui_but_drawstr_len_without_sep_char(but); - } - else { - str = but->tip; - str_len_clip = ui_but_tip_len_only_first_line(but); - } + const char *str = NULL; + size_t str_len_clip = SIZE_MAX - 1; + bool skip_undo = false; - /* fallback, else we don't get an undo! */ - if (str == NULL || str[0] == '\0' || str_len_clip == 0) { - str = "Unknown Action"; - str_len_clip = strlen(str); - } + /* define which string to use for undo */ + if (but->type == UI_BTYPE_MENU) { + str = but->drawstr; + str_len_clip = ui_but_drawstr_len_without_sep_char(but); + } + else if (but->drawstr[0]) { + str = but->drawstr; + str_len_clip = ui_but_drawstr_len_without_sep_char(but); + } + else { + str = but->tip; + str_len_clip = ui_but_tip_len_only_first_line(but); + } - /* Optionally override undo when undo system doesn't support storing properties. */ - 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. */ - /* Exception for active shape-key, since changing this in edit-mode updates - * the shape key from object mode data. */ - if (ELEM(but->rnaprop, &rna_ID_name, &rna_Object_active_shape_key_index)) { - /* pass */ - } - else { - ID *id = but->rnapoin.owner_id; - if (!ED_undo_is_legacy_compatible_for_property(but->block->evil_C, id)) { - skip_undo = true; - } - } - } + /* fallback, else we don't get an undo! */ + if (str == NULL || str[0] == '\0' || str_len_clip == 0) { + str = "Unknown Action"; + str_len_clip = strlen(str); + } - if (skip_undo == false) { - /* XXX: disable all undo pushes from UI changes from sculpt mode as they cause memfile undo - * steps to be written which cause lag: T71434. */ - if (BKE_paintmode_get_active_from_context(but->block->evil_C) == PAINT_MODE_SCULPT) { + /* Optionally override undo when undo system doesn't support storing properties. */ + 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. */ + /* Exception for active shape-key, since changing this in edit-mode updates + * the shape key from object mode data. */ + if (ELEM(but->rnaprop, &rna_ID_name, &rna_Object_active_shape_key_index)) { + /* pass */ + } + else { + ID *id = but->rnapoin.owner_id; + if (!ED_undo_is_legacy_compatible_for_property(but->block->evil_C, id)) { skip_undo = true; } } + } - if (skip_undo) { - str = ""; + if (skip_undo == false) { + /* XXX: disable all undo pushes from UI changes from sculpt mode as they cause memfile undo + * steps to be written which cause lag: T71434. */ + if (BKE_paintmode_get_active_from_context(but->block->evil_C) == PAINT_MODE_SCULPT) { + skip_undo = true; } + } - /* delayed, after all other funcs run, popups are closed, etc */ - uiAfterFunc *after = ui_afterfunc_new(); - BLI_strncpy(after->undostr, str, min_zz(str_len_clip + 1, sizeof(after->undostr))); + if (skip_undo) { + str = ""; } + + /* delayed, after all other funcs run, popups are closed, etc */ + uiAfterFunc *after = ui_afterfunc_new(); + BLI_strncpy(after->undostr, str, min_zz(str_len_clip + 1, sizeof(after->undostr))); } static void ui_apply_but_autokey(bContext *C, uiBut *but) @@ -961,21 +971,21 @@ static void ui_apply_but_autokey(bContext *C, uiBut *but) /* try autokey */ ui_but_anim_autokey(C, but, scene, scene->r.cfra); - /* make a little report about what we've done! */ - if (but->rnaprop) { - char *buf; + if (!but->rnaprop) { + return; + } - if (RNA_property_subtype(but->rnaprop) == PROP_PASSWORD) { - return; - } + if (RNA_property_subtype(but->rnaprop) == PROP_PASSWORD) { + return; + } - buf = WM_prop_pystring_assign(C, &but->rnapoin, but->rnaprop, but->rnaindex); - if (buf) { - BKE_report(CTX_wm_reports(C), RPT_PROPERTY, buf); - MEM_freeN(buf); + /* make a little report about what we've done! */ + char *buf = WM_prop_pystring_assign(C, &but->rnapoin, but->rnaprop, but->rnaindex); + if (buf) { + BKE_report(CTX_wm_reports(C), RPT_PROPERTY, buf); + MEM_freeN(buf); - WM_event_add_notifier(C, NC_SPACE | ND_SPACE_INFO_REPORT, NULL); - } + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_INFO_REPORT, NULL); } } @@ -1470,9 +1480,9 @@ static void ui_multibut_states_create(uiBut *but_active, uiHandleButtonData *dat } } - /* edit buttons proportionally to eachother + /* Edit buttons proportionally to each other. * NOTE: if we mix buttons which are proportional and others which are not, - * this may work a bit strangely */ + * this may work a bit strangely. */ if ((but_active->rnaprop && (RNA_property_flag(but_active->rnaprop) & PROP_PROPORTIONAL)) || ELEM(but_active->unit_type, RNA_SUBTYPE_UNIT_VALUE(PROP_UNIT_LENGTH))) { if (data->origvalue != 0.0) { @@ -1625,29 +1635,34 @@ static bool ui_drag_toggle_set_xy_xy( LISTBASE_FOREACH (uiBut *, but, &block->buttons) { /* NOTE: ctrl is always true here because (at least for now) * we always want to consider text control in this case, even when not embossed. */ - if (ui_but_is_interactive(but, true)) { - if (BLI_rctf_isect_segment(&but->rect, xy_a_block, xy_b_block)) { - - /* execute the button */ - if (ui_drag_toggle_but_is_supported(but)) { - /* is it pressed? */ - const int pushed_state_but = ui_drag_toggle_but_pushed_state(but); - if (pushed_state_but != pushed_state) { - UI_but_execute(C, region, but); - if (do_check) { - ui_but_update_edited(but); - } - if (U.runtime.is_dirty == false) { - ui_but_update_preferences_dirty(but); - } - changed = true; - } - } - /* done */ - } + + if (!ui_but_is_interactive(but, true)) { + continue; } + if (!BLI_rctf_isect_segment(&but->rect, xy_a_block, xy_b_block)) { + continue; + } + if (!ui_drag_toggle_but_is_supported(but)) { + continue; + } + /* is it pressed? */ + const int pushed_state_but = ui_drag_toggle_but_pushed_state(but); + if (pushed_state_but == pushed_state) { + continue; + } + + /* execute the button */ + UI_but_execute(C, region, but); + if (do_check) { + ui_but_update_edited(but); + } + if (U.runtime.is_dirty == false) { + ui_but_update_preferences_dirty(but); + } + changed = true; } } + if (changed) { /* apply now, not on release (or if handlers are canceled for whatever reason) */ ui_apply_but_funcs_after(C); @@ -1669,7 +1684,7 @@ static void ui_drag_toggle_set(bContext *C, uiDragToggleHandle *drag_info, const */ if (drag_info->is_xy_lock_init == false) { /* first store the buttons original coords */ - uiBut *but = ui_but_find_mouse_over_ex(region, xy_input, true, NULL, NULL); + uiBut *but = ui_but_find_mouse_over_ex(region, xy_input, true, false, NULL, NULL); if (but) { if (but->flag & UI_BUT_DRAG_LOCK) { @@ -1739,7 +1754,7 @@ static int ui_handler_region_drag_toggle(bContext *C, const wmEvent *event, void if (done) { wmWindow *win = CTX_wm_window(C); const ARegion *region = CTX_wm_region(C); - uiBut *but = ui_but_find_mouse_over_ex(region, drag_info->xy_init, true, NULL, NULL); + uiBut *but = ui_but_find_mouse_over_ex(region, drag_info->xy_init, true, false, NULL, NULL); if (but) { ui_apply_but_undo(but); @@ -2120,25 +2135,7 @@ static bool ui_but_drag_init(bContext *C, } } else { - wmDrag *drag = WM_event_start_drag( - C, - but->icon, - but->dragtype, - but->dragpoin, - ui_but_value_get(but), - (but->dragflag & UI_BUT_DRAGPOIN_FREE) ? WM_DRAG_FREE_DATA : WM_DRAG_NOP); - /* wmDrag has ownership over dragpoin now, stop messing with it. */ - but->dragpoin = NULL; - - if (but->imb) { - WM_event_drag_image(drag, but->imb, but->imb_scale); - } - - /* Special feature for assets: We add another drag item that supports multiple assets. It - * gets the assets from context. */ - if (ELEM(but->dragtype, WM_DRAG_ASSET, WM_DRAG_ID)) { - WM_event_start_drag(C, ICON_NONE, WM_DRAG_ASSET_LIST, NULL, 0, WM_DRAG_NOP); - } + ui_but_drag_start(C, but); } return true; } @@ -2292,6 +2289,9 @@ static void ui_apply_but( case UI_BTYPE_ROW: ui_apply_but_ROW(C, block, but, data); break; + case UI_BTYPE_GRID_TILE: + ui_apply_but_ROW(C, block, but, data); + break; case UI_BTYPE_TREEROW: ui_apply_but_TREEROW(C, block, but, data); break; @@ -2948,6 +2948,9 @@ void ui_but_text_password_hide(char password_str[UI_MAX_PASSWORD_STR], void ui_but_set_string_interactive(bContext *C, uiBut *but, const char *value) { + /* Caller should check. */ + BLI_assert((but->flag & UI_BUT_DISABLED) == 0); + button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); ui_textedit_string_set(but, but->active, value); @@ -3492,7 +3495,7 @@ static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data) WM_cursor_modal_set(win, WM_CURSOR_TEXT_EDIT); #ifdef WITH_INPUT_IME - if (is_num_but == false && BLT_lang_is_ime_supported()) { + if (!is_num_but) { ui_textedit_ime_begin(win, but); } #endif @@ -3896,7 +3899,7 @@ static void ui_do_but_textedit( if ((event->ascii || event->utf8_buf[0]) && (retval == WM_UI_HANDLER_CONTINUE) #ifdef WITH_INPUT_IME - && !is_ime_composing && (!WM_event_is_ime_switch(event) || !BLT_lang_is_ime_supported()) + && !is_ime_composing && !WM_event_is_ime_switch(event) #endif ) { char ascii = event->ascii; @@ -4324,7 +4327,7 @@ static uiBut *ui_but_list_row_text_activate(bContext *C, uiButtonActivateType activate_type) { ARegion *region = CTX_wm_region(C); - uiBut *labelbut = ui_but_find_mouse_over_ex(region, event->xy, true, NULL, NULL); + uiBut *labelbut = ui_but_find_mouse_over_ex(region, event->xy, true, false, NULL, NULL); if (labelbut && labelbut->type == UI_BTYPE_TEXT && !(labelbut->flag & UI_BUT_DISABLED)) { /* exit listrow */ @@ -4346,14 +4349,14 @@ static uiBut *ui_but_list_row_text_activate(bContext *C, * \{ */ static uiButExtraOpIcon *ui_but_extra_operator_icon_mouse_over_get(uiBut *but, - uiHandleButtonData *data, + ARegion *region, const wmEvent *event) { float xmax = but->rect.xmax; const float icon_size = 0.8f * BLI_rctf_size_y(&but->rect); /* ICON_SIZE_FROM_BUTRECT */ int x = event->xy[0], y = event->xy[1]; - ui_window_to_block(data->region, but->block, &x, &y); + ui_window_to_block(region, but->block, &x, &y); if (!BLI_rctf_isect_pt(&but->rect, x, y)) { return NULL; } @@ -4382,7 +4385,7 @@ static bool ui_do_but_extra_operator_icon(bContext *C, uiHandleButtonData *data, const wmEvent *event) { - uiButExtraOpIcon *op_icon = ui_but_extra_operator_icon_mouse_over_get(but, data, event); + uiButExtraOpIcon *op_icon = ui_but_extra_operator_icon_mouse_over_get(but, data->region, event); if (!op_icon) { return false; @@ -4417,7 +4420,7 @@ static void ui_do_but_extra_operator_icons_mousemove(uiBut *but, op_icon->highlighted = false; } - uiButExtraOpIcon *hovered = ui_but_extra_operator_icon_mouse_over_get(but, data, event); + uiButExtraOpIcon *hovered = ui_but_extra_operator_icon_mouse_over_get(but, data->region, event); if (hovered) { hovered->highlighted = true; @@ -4501,10 +4504,14 @@ static int ui_do_but_HOTKEYEVT(bContext *C, uiHandleButtonData *data, const wmEvent *event) { + uiButHotkeyEvent *hotkey_but = (uiButHotkeyEvent *)but; + BLI_assert(but->type == UI_BTYPE_HOTKEY_EVENT); + if (data->state == BUTTON_STATE_HIGHLIGHT) { - if (ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY) && event->val == KM_PRESS) { + if (ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY, EVT_BUT_OPEN) && + (event->val == KM_PRESS)) { but->drawstr[0] = 0; - but->modifier_key = 0; + hotkey_but->modifier_key = 0; button_activate_state(C, but, BUTTON_STATE_WAIT_KEY_EVENT); return WM_UI_HANDLER_BREAK; } @@ -4525,20 +4532,16 @@ static int ui_do_but_HOTKEYEVT(bContext *C, if (event->type == LEFTMOUSE && event->val == KM_PRESS) { /* only cancel if click outside the button */ if (ui_but_contains_point_px(but, but->active->region, event->xy) == false) { - /* data->cancel doesn't work, this button opens immediate */ - if (but->flag & UI_BUT_IMMEDIATE) { - ui_but_value_set(but, 0); - } - else { - data->cancel = true; - } + data->cancel = true; + /* Close the containing popup (if any). */ + data->escapecancel = true; button_activate_state(C, but, BUTTON_STATE_EXIT); return WM_UI_HANDLER_BREAK; } } /* always set */ - but->modifier_key = event->modifier; + hotkey_but->modifier_key = event->modifier; ui_but_update(but); ED_region_tag_redraw(data->region); @@ -4656,7 +4659,7 @@ static int ui_do_but_TEX( /* pass */ } else { - if (!ui_but_extra_operator_icon_mouse_over_get(but, data, event)) { + if (!ui_but_extra_operator_icon_mouse_over_get(but, data->region, event)) { button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); } return WM_UI_HANDLER_BREAK; @@ -4782,7 +4785,7 @@ static int ui_do_but_TREEROW(bContext *C, switch (event->val) { case KM_PRESS: /* Extra icons have priority, don't mess with them. */ - if (ui_but_extra_operator_icon_mouse_over_get(but, data, event)) { + if (ui_but_extra_operator_icon_mouse_over_get(but, data->region, event)) { return WM_UI_HANDLER_BREAK; } button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG); @@ -4810,12 +4813,53 @@ static int ui_do_but_TREEROW(bContext *C, return WM_UI_HANDLER_CONTINUE; } +static int ui_do_but_GRIDTILE(bContext *C, + uiBut *but, + uiHandleButtonData *data, + const wmEvent *event) +{ + BLI_assert(but->type == UI_BTYPE_GRID_TILE); + + if (data->state == BUTTON_STATE_HIGHLIGHT) { + if (event->type == LEFTMOUSE) { + switch (event->val) { + case KM_PRESS: + /* Extra icons have priority, don't mess with them. */ + if (ui_but_extra_operator_icon_mouse_over_get(but, data->region, event)) { + return WM_UI_HANDLER_BREAK; + } + button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG); + data->dragstartx = event->xy[0]; + data->dragstarty = event->xy[1]; + return WM_UI_HANDLER_CONTINUE; + + case KM_CLICK: + button_activate_state(C, but, BUTTON_STATE_EXIT); + return WM_UI_HANDLER_BREAK; + + case KM_DBL_CLICK: + data->cancel = true; + // uiButGridTile *grid_tile_but = (uiButGridTile *)but; + // UI_tree_view_item_begin_rename(grid_tile_but->tree_item); + ED_region_tag_redraw(CTX_wm_region(C)); + return WM_UI_HANDLER_BREAK; + } + } + } + else if (data->state == BUTTON_STATE_WAIT_DRAG) { + /* Let "default" button handling take care of the drag logic. */ + return ui_do_but_EXIT(C, but, data, event); + } + + return WM_UI_HANDLER_CONTINUE; +} + static int ui_do_but_EXIT(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { if (data->state == BUTTON_STATE_HIGHLIGHT) { /* First handle click on icon-drag type button. */ - if ((event->type == LEFTMOUSE) && (event->val == KM_PRESS) && but->dragpoin) { + if ((event->type == LEFTMOUSE) && (event->val == KM_PRESS) && ui_but_drag_is_draggable(but)) { if (ui_but_contains_point_px_icon(but, data->region, event)) { /* tell the button to wait and keep checking further events to @@ -4838,7 +4882,8 @@ static int ui_do_but_EXIT(bContext *C, uiBut *but, uiHandleButtonData *data, con if (ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY) && event->val == KM_PRESS) { int ret = WM_UI_HANDLER_BREAK; /* XXX: (a bit ugly) Special case handling for file-browser drag button. */ - if (but->dragpoin && but->imb && ui_but_contains_point_px_icon(but, data->region, event)) { + if (ui_but_drag_is_draggable(but) && but->imb && + ui_but_contains_point_px_icon(but, data->region, event)) { ret = WM_UI_HANDLER_CONTINUE; } /* Same special case handling for UI lists. Return CONTINUE so that a tweak or CLICK event @@ -4850,6 +4895,10 @@ static int ui_do_but_EXIT(bContext *C, uiBut *but, uiHandleButtonData *data, con ret = WM_UI_HANDLER_CONTINUE; } } + const uiBut *view_but = ui_view_item_find_mouse_over(data->region, event->xy); + if (view_but) { + ret = WM_UI_HANDLER_CONTINUE; + } button_activate_state(C, but, BUTTON_STATE_EXIT); return ret; } @@ -5992,7 +6041,7 @@ static int ui_do_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data, co if (data->state == BUTTON_STATE_HIGHLIGHT) { /* First handle click on icon-drag type button. */ - if (event->type == LEFTMOUSE && but->dragpoin && event->val == KM_PRESS) { + if (event->type == LEFTMOUSE && ui_but_drag_is_draggable(but) && event->val == KM_PRESS) { if (ui_but_contains_point_px_icon(but, data->region, event)) { button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG); data->dragstartx = event->xy[0]; @@ -6178,7 +6227,7 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co if (data->state == BUTTON_STATE_HIGHLIGHT) { /* First handle click on icon-drag type button. */ - if (event->type == LEFTMOUSE && but->dragpoin && event->val == KM_PRESS) { + if (event->type == LEFTMOUSE && ui_but_drag_is_draggable(but) && event->val == KM_PRESS) { ui_palette_set_active(color_but); if (ui_but_contains_point_px_icon(but, data->region, event)) { button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG); @@ -6271,7 +6320,7 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) { RNA_property_float_get_array(&but->rnapoin, but->rnaprop, target); - IMB_colormanagement_srgb_to_scene_linear_v3(target); + IMB_colormanagement_srgb_to_scene_linear_v3(target, target); } else if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR) { RNA_property_float_get_array(&but->rnapoin, but->rnaprop, target); @@ -6288,7 +6337,7 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co } else if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR) { RNA_property_float_get_array(&but->rnapoin, but->rnaprop, color); - IMB_colormanagement_scene_linear_to_srgb_v3(color); + IMB_colormanagement_scene_linear_to_srgb_v3(color, color); BKE_brush_color_set(scene, brush, color); updated = true; } @@ -7393,8 +7442,7 @@ static bool ui_numedit_but_CURVEPROFILE(uiBlock *block, const float zoomy = BLI_rctf_size_y(&but->rect) / BLI_rctf_size_y(&profile->view_rect); if (snap) { - float d[2] = {mx - data->dragstartx, data->dragstarty}; - + const float d[2] = {mx - data->dragstartx, data->dragstarty}; if (len_squared_v2(d) < (9.0f * U.dpi_fac)) { snap = false; } @@ -7899,7 +7947,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * uiHandleButtonData *data = but->active; int retval = WM_UI_HANDLER_CONTINUE; - const bool is_disabled = but->flag & UI_BUT_DISABLED; + const bool is_disabled = but->flag & UI_BUT_DISABLED || data->disable_force; /* if but->pointype is set, but->poin should be too */ BLI_assert(!but->pointype || but->poin); @@ -8005,6 +8053,9 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * case UI_BTYPE_ROW: retval = ui_do_but_TOG(C, but, data, event); break; + case UI_BTYPE_GRID_TILE: + retval = ui_do_but_GRIDTILE(C, but, data, event); + break; case UI_BTYPE_TREEROW: retval = ui_do_but_TREEROW(C, but, data, event); break; @@ -8216,7 +8267,7 @@ static ARegion *ui_but_tooltip_init( if (but) { const wmWindow *win = CTX_wm_window(C); uiButExtraOpIcon *extra_icon = ui_but_extra_operator_icon_mouse_over_get( - but, but->active, win->eventstate); + but, but->active ? but->active->region : region, win->eventstate); return UI_tooltip_create_from_button_or_extra_icon(C, region, but, extra_icon, is_label); } @@ -8492,14 +8543,6 @@ static void button_activate_init(bContext *C, } button_activate_state(C, but, BUTTON_STATE_HIGHLIGHT); - /* activate right away */ - if (but->flag & UI_BUT_IMMEDIATE) { - if (but->type == UI_BTYPE_HOTKEY_EVENT) { - button_activate_state(C, but, BUTTON_STATE_WAIT_KEY_EVENT); - } - /* .. more to be added here */ - } - if (type == BUTTON_ACTIVATE_OPEN) { button_activate_state(C, but, BUTTON_STATE_MENU_OPEN); @@ -8703,20 +8746,38 @@ static uiBut *ui_context_button_active(const ARegion *region, bool (*but_check_c uiBut *but_found = NULL; while (region) { - uiBut *activebut = NULL; + /* Follow this exact priority (from highest to lowest priority): + * 1) Active-override button (#UI_BUT_ACTIVE_OVERRIDE). + * 2) The real active button. + * 3) The previously active button (#UI_BUT_LAST_ACTIVE). + */ + uiBut *active_but_override = NULL; + uiBut *active_but_real = NULL; + uiBut *active_but_last = NULL; /* find active button */ LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + if (but->flag & UI_BUT_ACTIVE_OVERRIDE) { + active_but_override = but; + } if (but->active) { - activebut = but; + active_but_real = but; } - else if (!activebut && (but->flag & UI_BUT_LAST_ACTIVE)) { - activebut = but; + if (but->flag & UI_BUT_LAST_ACTIVE) { + active_but_last = but; } } } + uiBut *activebut = active_but_override; + if (!activebut) { + activebut = active_but_real; + } + if (!activebut) { + activebut = active_but_last; + } + if (activebut && (but_check_cb == NULL || but_check_cb(activebut))) { uiHandleButtonData *data = activebut->active; @@ -8947,7 +9008,12 @@ static uiBut *ui_but_find_open_event(ARegion *region, const wmEvent *event) static int ui_handle_button_over(bContext *C, const wmEvent *event, ARegion *region) { if (event->type == MOUSEMOVE) { - uiBut *but = ui_but_find_mouse_over(region, event); + const bool labeledit = event->modifier & KM_CTRL; + /* Allow buttons to be activated to show the tool-tip, + * then force-disable them if they're not considered interactive + * so they don't swallow events but can still display tips. */ + const bool for_tooltip = true; + uiBut *but = ui_but_find_mouse_over_ex(region, event->xy, labeledit, for_tooltip, NULL, NULL); if (but) { button_activate_init(C, region, but, BUTTON_ACTIVATE_OVER); @@ -8956,6 +9022,10 @@ static int ui_handle_button_over(bContext *C, const wmEvent *event, ARegion *reg * preferences. */ but->active->tooltip_force = true; } + + if (but->active && !ui_but_is_interactive(but, labeledit)) { + but->active->disable_force = true; + } } } else if (event->type == EVT_BUT_OPEN) { @@ -9435,7 +9505,7 @@ static bool ui_list_is_hovering_draggable_but(bContext *C, int mouse_xy[2]; WM_event_drag_start_xy(event, mouse_xy); - const uiBut *hovered_but = ui_but_find_mouse_over_ex(region, mouse_xy, false, NULL, NULL); + const uiBut *hovered_but = ui_but_find_mouse_over_ex(region, mouse_xy, false, false, NULL, NULL); if (list->dyn_data->custom_drag_optype) { if (ui_but_context_poll_operator(C, list->dyn_data->custom_drag_optype, hovered_but)) { @@ -9443,7 +9513,7 @@ static bool ui_list_is_hovering_draggable_but(bContext *C, } } - return (hovered_but && hovered_but->dragpoin); + return (hovered_but && ui_but_drag_is_draggable(hovered_but)); } static int ui_list_handle_click_drag(bContext *C, @@ -9653,31 +9723,31 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi return retval; } -static int ui_handle_tree_hover(const wmEvent *event, const ARegion *region) +static int ui_handle_view_items_hover(const wmEvent *event, const ARegion *region) { - bool has_treerows = false; + bool has_view_item = false; LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { - /* Avoid unnecessary work: Tree-rows are assumed to be inside tree-views. */ + /* Avoid unnecessary work: view item buttons are assumed to be inside views. */ if (BLI_listbase_is_empty(&block->views)) { continue; } LISTBASE_FOREACH (uiBut *, but, &block->buttons) { - if (but->type == UI_BTYPE_TREEROW) { + if (ui_but_is_view_item(but)) { but->flag &= ~UI_ACTIVE; - has_treerows = true; + has_view_item = true; } } } - if (!has_treerows) { + if (!has_view_item) { /* Avoid unnecessary lookup. */ return WM_UI_HANDLER_CONTINUE; } - /* Always highlight the hovered tree-row, even if the mouse hovers another button inside of it. + /* Always highlight the hovered view item, even if the mouse hovers another button inside of it. */ - uiBut *hovered_row_but = ui_tree_row_find_mouse_over(region, event->xy); + uiBut *hovered_row_but = ui_view_item_find_mouse_over(region, event->xy); if (hovered_row_but) { hovered_row_but->flag |= UI_ACTIVE; } @@ -9685,6 +9755,21 @@ static int ui_handle_tree_hover(const wmEvent *event, const ARegion *region) return WM_UI_HANDLER_CONTINUE; } +static int ui_handle_view_item_event(bContext *C, + const wmEvent *event, + ARegion *region, + uiBut *view_but) +{ + BLI_assert(ui_but_is_view_item(view_but)); + if (event->type == LEFTMOUSE) { + /* Will free active button if there already is one. */ + ui_handle_button_activate(C, region, view_but, BUTTON_ACTIVATE_OVER); + return ui_do_button(C, view_but->block, view_but, event); + } + + return WM_UI_HANDLER_CONTINUE; +} + static void ui_handle_button_return_submenu(bContext *C, const wmEvent *event, uiBut *but) { uiHandleButtonData *data = but->active; @@ -11285,9 +11370,15 @@ static int ui_region_handler(bContext *C, const wmEvent *event, void *UNUSED(use ui_blocks_set_tooltips(region, true); } - /* Always do this, to reliably update tree-row highlighting, even if the mouse hovers a button - * inside the row (it's an overlapping layout). */ - ui_handle_tree_hover(event, region); + /* Always do this, to reliably update view item highlighting, even if the mouse hovers a button + * nested in the item (it's an overlapping layout). */ + ui_handle_view_items_hover(event, region); + if (retval == WM_UI_HANDLER_CONTINUE) { + uiBut *view_item = ui_view_item_find_mouse_over(region, event->xy); + if (view_item) { + retval = ui_handle_view_item_event(C, event, region, view_item); + } + } /* delayed apply callbacks */ ui_apply_but_funcs_after(C); diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index 332b9b44b0a..c19e842aad8 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -546,6 +546,7 @@ static void init_brush_icons(void) INIT_BRUSH_ICON(ICON_BRUSH_MASK, mask); INIT_BRUSH_ICON(ICON_BRUSH_MIX, mix); INIT_BRUSH_ICON(ICON_BRUSH_NUDGE, nudge); + INIT_BRUSH_ICON(ICON_BRUSH_PAINT_SELECT, paint_select); INIT_BRUSH_ICON(ICON_BRUSH_PINCH, pinch); INIT_BRUSH_ICON(ICON_BRUSH_SCRAPE, scrape); INIT_BRUSH_ICON(ICON_BRUSH_SMEAR, smear); @@ -584,6 +585,19 @@ static void init_brush_icons(void) INIT_BRUSH_ICON(ICON_GPBRUSH_ERASE_HARD, gp_brush_erase_hard); INIT_BRUSH_ICON(ICON_GPBRUSH_ERASE_STROKE, gp_brush_erase_stroke); + /* Curves sculpt. */ + INIT_BRUSH_ICON(ICON_BRUSH_CURVES_ADD, curves_sculpt_add); + INIT_BRUSH_ICON(ICON_BRUSH_CURVES_COMB, curves_sculpt_comb); + INIT_BRUSH_ICON(ICON_BRUSH_CURVES_CUT, curves_sculpt_cut); + INIT_BRUSH_ICON(ICON_BRUSH_CURVES_DELETE, curves_sculpt_delete); + INIT_BRUSH_ICON(ICON_BRUSH_CURVES_DENSITY, curves_sculpt_density); + INIT_BRUSH_ICON(ICON_BRUSH_CURVES_GROW_SHRINK, curves_sculpt_grow_shrink); + INIT_BRUSH_ICON(ICON_BRUSH_CURVES_PINCH, curves_sculpt_pinch); + INIT_BRUSH_ICON(ICON_BRUSH_CURVES_PUFF, curves_sculpt_puff); + INIT_BRUSH_ICON(ICON_BRUSH_CURVES_SLIDE, curves_sculpt_slide); + INIT_BRUSH_ICON(ICON_BRUSH_CURVES_SMOOTH, curves_sculpt_smooth); + INIT_BRUSH_ICON(ICON_BRUSH_CURVES_SNAKE_HOOK, curves_sculpt_snake_hook); + # undef INIT_BRUSH_ICON } @@ -2034,6 +2048,9 @@ static int ui_id_brush_get_icon(const bContext *C, ID *id) else if (ob->mode & OB_MODE_TEXTURE_PAINT) { paint_mode = PAINT_MODE_TEXTURE_3D; } + else if (ob->mode & OB_MODE_SCULPT_CURVES) { + paint_mode = PAINT_MODE_SCULPT_CURVES; + } } else if (space_type == SPACE_IMAGE) { if (area->spacetype == space_type) { diff --git a/source/blender/editors/interface/interface_icons_event.c b/source/blender/editors/interface/interface_icons_event.c index 00c1bcb5f6e..6ad5fe805ab 100644 --- a/source/blender/editors/interface/interface_icons_event.c +++ b/source/blender/editors/interface/interface_icons_event.c @@ -145,10 +145,10 @@ void icon_draw_rect_input(float x, SNPRINTF(str, "F%d", 1 + (event_type - EVT_F1KEY)); icon_draw_rect_input_text(&rect, color, str, event_type > EVT_F9KEY ? 8.0f : 10.0f); } - else if (event_type == EVT_LEFTSHIFTKEY) { + else if (event_type == EVT_LEFTSHIFTKEY) { /* Right Shift has already been converted to left. */ icon_draw_rect_input_symbol(&rect, color, (const char[]){0xe2, 0x87, 0xa7, 0x0}); } - else if (event_type == EVT_LEFTCTRLKEY) { + else if (event_type == EVT_LEFTCTRLKEY) { /* Right Shift has already been converted to left. */ if (platform == MACOS) { icon_draw_rect_input_symbol(&rect, color, (const char[]){0xe2, 0x8c, 0x83, 0x0}); } @@ -156,7 +156,7 @@ void icon_draw_rect_input(float x, icon_draw_rect_input_text(&rect, color, "Ctrl", 9.0f); } } - else if (event_type == EVT_LEFTALTKEY) { + else if (event_type == EVT_LEFTALTKEY) { /* Right Alt has already been converted to left. */ if (platform == MACOS) { icon_draw_rect_input_symbol(&rect, color, (const char[]){0xe2, 0x8c, 0xa5, 0x0}); } diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index c09ff68bbca..791e51b81a6 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -74,6 +74,12 @@ enum { UI_SELECT_DRAW = (1 << 5), /** Property search filter is active and the button does not match. */ UI_SEARCH_FILTER_NO_MATCH = (1 << 6), + + /** Temporarily override the active button for lookups in context, regions, etc. (everything + * using #ui_context_button_active()). For example, so that operators normally acting on the + * active button can be polled on non-active buttons to (e.g. for disabling). */ + UI_BUT_ACTIVE_OVERRIDE = (1 << 7), + /* WARNING: rest of #uiBut.flag in UI_interface.h */ }; @@ -223,7 +229,6 @@ struct uiBut { bool changed; /** so buttons can support unit systems which are not RNA */ uchar unit_type; - short modifier_key; short iconadd; /** #UI_BTYPE_BLOCK data */ @@ -346,6 +351,13 @@ typedef struct uiButTreeRow { int indentation; } uiButTreeRow; +/** Derived struct for #UI_BTYPE_GRID_TILE. */ +typedef struct uiButGridTile { + uiBut but; + + uiGridViewItemHandle *view_item; +} uiButGridTile; + /** Derived struct for #UI_BTYPE_HSVCUBE. */ typedef struct uiButHSVCube { uiBut but; @@ -375,6 +387,13 @@ typedef struct uiButCurveMapping { eButGradientType gradient_type; } uiButCurveMapping; +/** Derived struct for #UI_BTYPE_HOTKEY_EVENT. */ +typedef struct uiButHotkeyEvent { + uiBut but; + + short modifier_key; +} uiButHotkeyEvent; + /** * Additional, superimposed icon for a button, invoking an operator. */ @@ -1206,24 +1225,24 @@ typedef enum { /** * Helper call to draw a menu item without a button. * - * \param state: The state of the button, - * typically #UI_ACTIVE, #UI_BUT_DISABLED, #UI_BUT_INACTIVE. + * \param but_flag: Button flags (#uiBut.flag) indicating the state of the item, typically + * #UI_ACTIVE, #UI_BUT_DISABLED, #UI_BUT_INACTIVE. * \param separator_type: The kind of separator which controls if and how the string is clipped. - * \param r_xmax: The right hand position of the text, this takes into the icon, - * padding and text clipping when there is not enough room to display the full text. + * \param r_xmax: The right hand position of the text, this takes into the icon, padding and text + * clipping when there is not enough room to display the full text. */ void ui_draw_menu_item(const struct uiFontStyle *fstyle, rcti *rect, const char *name, int iconid, - int state, + int but_flag, uiMenuItemSeparatorType separator_type, int *r_xmax); void ui_draw_preview_item(const struct uiFontStyle *fstyle, rcti *rect, const char *name, int iconid, - int state, + int but_flag, eFontStyle_Align text_align); /** * Version of #ui_draw_preview_item() that does not draw the menu background and item text based on @@ -1309,6 +1328,12 @@ void ui_button_group_add_but(uiBlock *block, uiBut *but); void ui_button_group_replace_but_ptr(uiBlock *block, const void *old_but_ptr, uiBut *new_but); void ui_block_free_button_groups(uiBlock *block); +/* interface_drag.cc */ + +void ui_but_drag_free(uiBut *but); +bool ui_but_drag_is_draggable(const uiBut *but); +void ui_but_drag_start(struct bContext *C, uiBut *but); + /* interface_align.c */ bool ui_but_can_align(const uiBut *but) ATTR_WARN_UNUSED_RESULT; @@ -1347,11 +1372,13 @@ void ui_but_anim_decorate_update_from_flag(uiButDecorator *but); bool ui_but_is_editable(const uiBut *but) ATTR_WARN_UNUSED_RESULT; bool ui_but_is_editable_as_text(const uiBut *but) ATTR_WARN_UNUSED_RESULT; bool ui_but_is_toggle(const uiBut *but) ATTR_WARN_UNUSED_RESULT; +bool ui_but_is_view_item(const uiBut *but) ATTR_WARN_UNUSED_RESULT; /** * Can we mouse over the button or is it hidden/disabled/layout. * \note ctrl is kind of a hack currently, * so that non-embossed UI_BTYPE_TEXT button behaves as a label when ctrl is not pressed. */ +bool ui_but_is_interactive_ex(const uiBut *but, const bool labeledit, const bool for_tooltip); bool ui_but_is_interactive(const uiBut *but, bool labeledit) ATTR_WARN_UNUSED_RESULT; bool ui_but_is_popover_once_compat(const uiBut *but) ATTR_WARN_UNUSED_RESULT; bool ui_but_has_array_value(const uiBut *but) ATTR_WARN_UNUSED_RESULT; @@ -1377,6 +1404,8 @@ uiBut *ui_list_row_find_mouse_over(const struct ARegion *region, const int xy[2] uiBut *ui_list_row_find_from_index(const struct ARegion *region, int index, uiBut *listbox) ATTR_WARN_UNUSED_RESULT; +uiBut *ui_view_item_find_mouse_over(const struct ARegion *region, const int xy[2]) + ATTR_NONNULL(1, 2); uiBut *ui_tree_row_find_mouse_over(const struct ARegion *region, const int xy[2]) ATTR_NONNULL(1, 2); uiBut *ui_tree_row_find_active(const struct ARegion *region); @@ -1388,6 +1417,7 @@ typedef bool (*uiButFindPollFn)(const uiBut *but, const void *customdata); uiBut *ui_but_find_mouse_over_ex(const struct ARegion *region, const int xy[2], bool labeledit, + bool for_tooltip, const uiButFindPollFn find_poll, const void *find_custom_data) ATTR_NONNULL(1, 2) ATTR_WARN_UNUSED_RESULT; @@ -1513,8 +1543,10 @@ void ui_interface_tag_script_reload_queries(void); /* interface_view.cc */ void ui_block_free_views(struct uiBlock *block); -uiTreeViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_block, - const uiTreeViewHandle *new_view); +uiTreeViewHandle *ui_block_tree_view_find_matching_in_old_block(const uiBlock *new_block, + const uiTreeViewHandle *new_view); +uiGridViewHandle *ui_block_grid_view_find_matching_in_old_block( + const uiBlock *new_block, const uiGridViewHandle *new_view_handle); uiButTreeRow *ui_block_view_find_treerow_in_old_block(const uiBlock *new_block, const uiTreeViewItemHandle *new_item_handle); diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index c1bb2ed6d18..3465373c85d 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -961,11 +961,17 @@ static void ui_item_enum_expand_tabs(uiLayout *layout, static void ui_keymap_but_cb(bContext *UNUSED(C), void *but_v, void *UNUSED(key_v)) { uiBut *but = but_v; + BLI_assert(but->type == UI_BTYPE_HOTKEY_EVENT); + const uiButHotkeyEvent *hotkey_but = (uiButHotkeyEvent *)but; - RNA_int_set(&but->rnapoin, "shift", (but->modifier_key & KM_SHIFT) ? KM_MOD_HELD : KM_NOTHING); - RNA_int_set(&but->rnapoin, "ctrl", (but->modifier_key & KM_CTRL) ? KM_MOD_HELD : KM_NOTHING); - RNA_int_set(&but->rnapoin, "alt", (but->modifier_key & KM_ALT) ? KM_MOD_HELD : KM_NOTHING); - RNA_int_set(&but->rnapoin, "oskey", (but->modifier_key & KM_OSKEY) ? KM_MOD_HELD : KM_NOTHING); + RNA_int_set( + &but->rnapoin, "shift", (hotkey_but->modifier_key & KM_SHIFT) ? KM_MOD_HELD : KM_NOTHING); + RNA_int_set( + &but->rnapoin, "ctrl", (hotkey_but->modifier_key & KM_CTRL) ? KM_MOD_HELD : KM_NOTHING); + RNA_int_set( + &but->rnapoin, "alt", (hotkey_but->modifier_key & KM_ALT) ? KM_MOD_HELD : KM_NOTHING); + RNA_int_set( + &but->rnapoin, "oskey", (hotkey_but->modifier_key & KM_OSKEY) ? KM_MOD_HELD : KM_NOTHING); } /** @@ -1101,7 +1107,7 @@ static uiBut *ui_item_with_label(uiLayout *layout, NULL); UI_but_func_set(but, ui_keymap_but_cb, but, NULL); if (flag & UI_ITEM_R_IMMEDIATE) { - UI_but_flag_enable(but, UI_BUT_IMMEDIATE); + UI_but_flag_enable(but, UI_BUT_ACTIVATE_ON_INIT); } } else { @@ -2332,7 +2338,14 @@ void uiItemFullR(uiLayout *layout, /* property with separate label */ else if (ELEM(type, PROP_ENUM, PROP_STRING, PROP_POINTER)) { but = ui_item_with_label(layout, block, name, icon, ptr, prop, index, 0, 0, w, h, flag); - but = ui_but_add_search(but, ptr, prop, NULL, NULL, false); + bool results_are_suggestions = false; + if (type == PROP_STRING) { + const eStringPropertySearchFlag search_flag = RNA_property_string_search_flag(prop); + if (search_flag & PROP_STRING_SEARCH_SUGGESTION) { + results_are_suggestions = true; + } + } + but = ui_but_add_search(but, ptr, prop, NULL, NULL, results_are_suggestions); if (layout->redalert) { UI_but_flag_enable(but, UI_BUT_REDALERT); @@ -2705,11 +2718,16 @@ uiBut *ui_but_add_search(uiBut *but, PropertyRNA *prop, PointerRNA *searchptr, PropertyRNA *searchprop, - bool results_are_suggestions) + const bool results_are_suggestions) { /* for ID's we do automatic lookup */ + bool has_search_fn = false; + PointerRNA sptr; if (!searchprop) { + if (RNA_property_type(prop) == PROP_STRING) { + has_search_fn = (RNA_property_string_search_flag(prop) != 0); + } if (RNA_property_type(prop) == PROP_POINTER) { StructRNA *ptype = RNA_property_pointer_type(ptr, prop); search_id_collection(ptype, &sptr, &searchprop); @@ -2718,14 +2736,18 @@ uiBut *ui_but_add_search(uiBut *but, } /* turn button into search button */ - if (searchprop) { + if (has_search_fn || searchprop) { uiRNACollectionSearch *coll_search = MEM_mallocN(sizeof(*coll_search), __func__); uiButSearch *search_but; but = ui_but_change_type(but, UI_BTYPE_SEARCH_MENU); search_but = (uiButSearch *)but; - search_but->rnasearchpoin = *searchptr; - search_but->rnasearchprop = searchprop; + + if (searchptr) { + search_but->rnasearchpoin = *searchptr; + search_but->rnasearchprop = searchprop; + } + but->hardmax = MAX2(but->hardmax, 256.0f); but->drawflag |= UI_BUT_ICON_LEFT | UI_BUT_TEXT_LEFT; if (RNA_property_is_unlink(prop)) { @@ -2734,8 +2756,17 @@ uiBut *ui_but_add_search(uiBut *but, coll_search->target_ptr = *ptr; coll_search->target_prop = prop; - coll_search->search_ptr = *searchptr; - coll_search->search_prop = searchprop; + + if (searchptr) { + coll_search->search_ptr = *searchptr; + coll_search->search_prop = searchprop; + } + else { + /* Rely on `has_search_fn`. */ + coll_search->search_ptr = PointerRNA_NULL; + coll_search->search_prop = NULL; + } + coll_search->search_but = but; coll_search->butstore_block = but->block; coll_search->butstore = UI_butstore_create(coll_search->butstore_block); diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 5b97a80d513..aafb56119ae 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -1167,7 +1167,9 @@ static void UI_OT_copy_to_selected_button(wmOperatorType *ot) /* identifiers */ ot->name = "Copy to Selected"; ot->idname = "UI_OT_copy_to_selected_button"; - ot->description = "Copy property from this object to selected objects or bones"; + ot->description = + "Copy the property's value from the active item to the same property of all selected items " + "if the same property exists"; /* callbacks */ ot->poll = copy_to_selected_button_poll; @@ -1895,14 +1897,14 @@ static int drop_color_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) { if (!gamma) { - IMB_colormanagement_scene_linear_to_srgb_v3(color); + IMB_colormanagement_scene_linear_to_srgb_v3(color, color); } RNA_property_float_set_array(&but->rnapoin, but->rnaprop, color); RNA_property_update(C, &but->rnapoin, but->rnaprop); } else if (RNA_property_subtype(but->rnaprop) == PROP_COLOR) { if (gamma) { - IMB_colormanagement_srgb_to_scene_linear_v3(color); + IMB_colormanagement_srgb_to_scene_linear_v3(color, color); } RNA_property_float_set_array(&but->rnapoin, but->rnaprop, color); RNA_property_update(C, &but->rnapoin, but->rnaprop); @@ -1940,6 +1942,24 @@ static void UI_OT_drop_color(wmOperatorType *ot) /** \name Drop Name Operator * \{ */ +static bool drop_name_poll(bContext *C) +{ + if (!ED_operator_regionactive(C)) { + return false; + } + + const uiBut *but = UI_but_active_drop_name_button(C); + if (!but) { + return false; + } + + if (but->flag & UI_BUT_DISABLED) { + return false; + } + + return true; +} + static int drop_name_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { uiBut *but = UI_but_active_drop_name_button(C); @@ -1959,7 +1979,7 @@ static void UI_OT_drop_name(wmOperatorType *ot) ot->idname = "UI_OT_drop_name"; ot->description = "Drop name to button"; - ot->poll = ED_operator_regionactive; + ot->poll = drop_name_poll; ot->invoke = drop_name_invoke; ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL; @@ -2142,11 +2162,8 @@ static int ui_drop_material_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); - if (!RNA_struct_property_is_set(op->ptr, "session_uuid")) { - return OPERATOR_CANCELLED; - } - const uint32_t session_uuid = (uint32_t)RNA_int_get(op->ptr, "session_uuid"); - Material *ma = (Material *)BKE_libblock_find_session_uuid(bmain, ID_MA, session_uuid); + Material *ma = (Material *)WM_operator_properties_id_lookup_from_name_or_session_uuid( + bmain, op->ptr, ID_MA); if (ma == NULL) { return OPERATOR_CANCELLED; } @@ -2184,16 +2201,7 @@ static void UI_OT_drop_material(wmOperatorType *ot) ot->exec = ui_drop_material_exec; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; - PropertyRNA *prop = RNA_def_int(ot->srna, - "session_uuid", - 0, - INT32_MIN, - INT32_MAX, - "Session UUID", - "Session UUID of the data-block to assign", - INT32_MIN, - INT32_MAX); - RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); + WM_operator_properties_id_lookup(ot, false); } /** \} */ diff --git a/source/blender/editors/interface/interface_query.cc b/source/blender/editors/interface/interface_query.cc index 337b2852d57..71cf60985df 100644 --- a/source/blender/editors/interface/interface_query.cc +++ b/source/blender/editors/interface/interface_query.cc @@ -58,12 +58,28 @@ bool ui_but_is_toggle(const uiBut *but) UI_BTYPE_TREEROW); } -bool ui_but_is_interactive(const uiBut *but, const bool labeledit) +bool ui_but_is_view_item(const uiBut *but) +{ + return ELEM(but->type, UI_BTYPE_TREEROW, UI_BTYPE_GRID_TILE); +} + +bool ui_but_is_interactive_ex(const uiBut *but, const bool labeledit, const bool for_tooltip) { /* NOTE: #UI_BTYPE_LABEL is included for highlights, this allows drags. */ - if ((but->type == UI_BTYPE_LABEL) && but->dragpoin == nullptr && but->tip_func == nullptr) { - return false; + if (but->type == UI_BTYPE_LABEL) { + if (for_tooltip) { + /* It's important labels are considered interactive for the purpose of showing tooltip. */ + if (!ui_but_drag_is_draggable(but) && but->tip_func == nullptr) { + return false; + } + } + else { + if (!ui_but_drag_is_draggable(but)) { + return false; + } + } } + if (ELEM(but->type, UI_BTYPE_ROUNDBOX, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE, UI_BTYPE_LISTBOX)) { return false; } @@ -84,6 +100,11 @@ bool ui_but_is_interactive(const uiBut *but, const bool labeledit) return true; } +bool ui_but_is_interactive(const uiBut *but, const bool labeledit) +{ + return ui_but_is_interactive_ex(but, labeledit, false); +} + bool UI_but_is_utf8(const uiBut *but) { if (but->rnaprop) { @@ -266,6 +287,7 @@ static uiBut *ui_but_find(const ARegion *region, uiBut *ui_but_find_mouse_over_ex(const ARegion *region, const int xy[2], const bool labeledit, + const bool for_tooltip, const uiButFindPollFn find_poll, const void *find_custom_data) { @@ -282,7 +304,7 @@ uiBut *ui_but_find_mouse_over_ex(const ARegion *region, if (find_poll && find_poll(but, find_custom_data) == false) { continue; } - if (ui_but_is_interactive(but, labeledit)) { + if (ui_but_is_interactive_ex(but, labeledit, for_tooltip)) { if (but->pie_dir != UI_RADIAL_NONE) { if (ui_but_isect_pie_seg(block, but)) { butover = but; @@ -310,7 +332,8 @@ uiBut *ui_but_find_mouse_over_ex(const ARegion *region, uiBut *ui_but_find_mouse_over(const ARegion *region, const wmEvent *event) { - return ui_but_find_mouse_over_ex(region, event->xy, event->modifier & KM_CTRL, nullptr, nullptr); + return ui_but_find_mouse_over_ex( + region, event->xy, event->modifier & KM_CTRL, false, nullptr, nullptr); } uiBut *ui_but_find_rect_over(const struct ARegion *region, const rcti *rect_px) @@ -414,7 +437,7 @@ static bool ui_but_is_listrow(const uiBut *but, const void *UNUSED(customdata)) uiBut *ui_list_row_find_mouse_over(const ARegion *region, const int xy[2]) { - return ui_but_find_mouse_over_ex(region, xy, false, ui_but_is_listrow, nullptr); + return ui_but_find_mouse_over_ex(region, xy, false, false, ui_but_is_listrow, nullptr); } struct ListRowFindIndexData { @@ -444,9 +467,19 @@ static bool ui_but_is_treerow(const uiBut *but, const void *UNUSED(customdata)) return but->type == UI_BTYPE_TREEROW; } +static bool ui_but_is_view_item_fn(const uiBut *but, const void *UNUSED(customdata)) +{ + return ui_but_is_view_item(but); +} + +uiBut *ui_view_item_find_mouse_over(const ARegion *region, const int xy[2]) +{ + return ui_but_find_mouse_over_ex(region, xy, false, false, ui_but_is_view_item_fn, nullptr); +} + uiBut *ui_tree_row_find_mouse_over(const ARegion *region, const int xy[2]) { - return ui_but_find_mouse_over_ex(region, xy, false, ui_but_is_treerow, nullptr); + return ui_but_find_mouse_over_ex(region, xy, false, false, ui_but_is_treerow, nullptr); } static bool ui_but_is_active_treerow(const uiBut *but, const void *customdata) diff --git a/source/blender/editors/interface/interface_region_color_picker.cc b/source/blender/editors/interface/interface_region_color_picker.cc index ab0a6039cdc..db1e5e653de 100644 --- a/source/blender/editors/interface/interface_region_color_picker.cc +++ b/source/blender/editors/interface/interface_region_color_picker.cc @@ -116,7 +116,7 @@ void ui_scene_linear_to_perceptual_space(uiBut *but, float rgb[3]) * assuming it is more perceptually linear than the scene linear * space for intuitive color picking. */ if (!ui_but_is_color_gamma(but)) { - IMB_colormanagement_scene_linear_to_color_picking_v3(rgb); + IMB_colormanagement_scene_linear_to_color_picking_v3(rgb, rgb); ui_color_picker_rgb_round(rgb); } } @@ -124,7 +124,7 @@ void ui_scene_linear_to_perceptual_space(uiBut *but, float rgb[3]) void ui_perceptual_to_scene_linear_space(uiBut *but, float rgb[3]) { if (!ui_but_is_color_gamma(but)) { - IMB_colormanagement_color_picking_to_scene_linear_v3(rgb); + IMB_colormanagement_color_picking_to_scene_linear_v3(rgb, rgb); ui_color_picker_rgb_round(rgb); } } @@ -208,7 +208,7 @@ static void ui_update_color_picker_buts_rgb(uiBut *from_but, * (coming from other applications, web, etc) */ copy_v3_v3(rgb_hex, rgb_scene_linear); if (from_but && !ui_but_is_color_gamma(from_but)) { - IMB_colormanagement_scene_linear_to_srgb_v3(rgb_hex); + IMB_colormanagement_scene_linear_to_srgb_v3(rgb_hex, rgb_hex); ui_color_picker_rgb_round(rgb_hex); } @@ -291,7 +291,7 @@ static void ui_colorpicker_hex_rna_cb(bContext *UNUSED(C), void *bt1, void *hexc /* Hex code is assumed to be in sRGB space (coming from other applications, web, etc) */ if (!ui_but_is_color_gamma(but)) { - IMB_colormanagement_srgb_to_scene_linear_v3(rgb); + IMB_colormanagement_srgb_to_scene_linear_v3(rgb, rgb); ui_color_picker_rgb_round(rgb); } @@ -777,7 +777,7 @@ static void ui_block_colorpicker(uiBlock *block, copy_v3_v3(rgb_hex, rgba_scene_linear); if (!ui_but_is_color_gamma(from_but)) { - IMB_colormanagement_scene_linear_to_srgb_v3(rgb_hex); + IMB_colormanagement_scene_linear_to_srgb_v3(rgb_hex, rgb_hex); ui_color_picker_rgb_round(rgb_hex); } diff --git a/source/blender/editors/interface/interface_region_menu_popup.cc b/source/blender/editors/interface/interface_region_menu_popup.cc index e843a275d08..a22f7218203 100644 --- a/source/blender/editors/interface/interface_region_menu_popup.cc +++ b/source/blender/editors/interface/interface_region_menu_popup.cc @@ -129,6 +129,12 @@ static uiBut *ui_popup_menu_memory__internal(uiBlock *block, uiBut *but) /* get */ LISTBASE_FOREACH (uiBut *, but_iter, &block->buttons) { + /* Prevent labels (typically headings), from being returned in the case the text + * happens to matches one of the menu items. + * Skip separators too as checking them is redundant. */ + if (ELEM(but_iter->type, UI_BTYPE_LABEL, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE)) { + continue; + } if (mem[hash_mod] == ui_popup_string_hash(but_iter->str, but_iter->flag & UI_BUT_HAS_SEP_CHAR)) { return but_iter; diff --git a/source/blender/editors/interface/interface_region_search.cc b/source/blender/editors/interface/interface_region_search.cc index bc497e2647c..81c0c29d09a 100644 --- a/source/blender/editors/interface/interface_region_search.cc +++ b/source/blender/editors/interface/interface_region_search.cc @@ -58,7 +58,7 @@ struct uiSearchItems { char **names; void **pointers; int *icons; - int *states; + int *but_flags; uint8_t *name_prefix_offsets; /** Is there any item with an icon? */ @@ -94,7 +94,7 @@ bool UI_search_item_add(uiSearchItems *items, const char *name, void *poin, int iconid, - int state, + const int but_flag, const uint8_t name_prefix_offset) { /* hijack for autocomplete */ @@ -148,10 +148,10 @@ bool UI_search_item_add(uiSearchItems *items, /* Limit flags that can be set so flags such as 'UI_SELECT' aren't accidentally set * which will cause problems, add others as needed. */ - BLI_assert( - (state & ~(UI_BUT_DISABLED | UI_BUT_INACTIVE | UI_BUT_REDALERT | UI_BUT_HAS_SEP_CHAR)) == 0); - if (items->states) { - items->states[items->totitem] = state; + BLI_assert((but_flag & + ~(UI_BUT_DISABLED | UI_BUT_INACTIVE | UI_BUT_REDALERT | UI_BUT_HAS_SEP_CHAR)) == 0); + if (items->but_flags) { + items->but_flags[items->totitem] = but_flag; } items->totitem++; @@ -451,15 +451,16 @@ void ui_searchbox_update(bContext *C, ARegion *region, uiBut *but, const bool re /* reset vars */ data->items.totitem = 0; data->items.more = 0; - if (reset == false) { + if (!reset) { data->items.offset_i = data->items.offset; } else { data->items.offset_i = data->items.offset = 0; data->active = -1; - /* handle active */ - if (search_but->items_update_fn && search_but->item_active) { + /* On init, find and center active item. */ + const bool is_first_search = !search_but->but.changed; + if (is_first_search && search_but->items_update_fn && search_but->item_active) { data->items.active = search_but->item_active; ui_searchbox_update_fn(C, search_but, but->editstr, &data->items); data->items.active = nullptr; @@ -556,7 +557,7 @@ static void ui_searchbox_region_draw_fn(const bContext *C, ARegion *region) if (data->preview) { /* draw items */ for (int a = 0; a < data->items.totitem; a++) { - const int state = ((a == data->active) ? UI_ACTIVE : 0) | data->items.states[a]; + const int but_flag = ((a == data->active) ? UI_ACTIVE : 0) | data->items.but_flags[a]; /* ensure icon is up-to-date */ ui_icon_ensure_deferred(C, data->items.icons[a], data->preview); @@ -568,7 +569,7 @@ static void ui_searchbox_region_draw_fn(const bContext *C, ARegion *region) &rect, data->items.names[a], data->items.icons[a], - state, + but_flag, UI_STYLE_TEXT_LEFT); } @@ -590,7 +591,7 @@ static void ui_searchbox_region_draw_fn(const bContext *C, ARegion *region) const int search_sep_len = data->sep_string ? strlen(data->sep_string) : 0; /* draw items */ for (int a = 0; a < data->items.totitem; a++) { - const int state = ((a == data->active) ? UI_ACTIVE : 0) | data->items.states[a]; + const int but_flag = ((a == data->active) ? UI_ACTIVE : 0) | data->items.but_flags[a]; char *name = data->items.names[a]; int icon = data->items.icons[a]; char *name_sep_test = nullptr; @@ -600,7 +601,7 @@ static void ui_searchbox_region_draw_fn(const bContext *C, ARegion *region) separator_type = UI_MENU_ITEM_SEPARATOR_SHORTCUT; } /* Only set for displaying additional hint (e.g. library name of a linked data-block). */ - else if (state & UI_BUT_HAS_SEP_CHAR) { + else if (but_flag & UI_BUT_HAS_SEP_CHAR) { separator_type = UI_MENU_ITEM_SEPARATOR_HINT; } @@ -615,7 +616,7 @@ static void ui_searchbox_region_draw_fn(const bContext *C, ARegion *region) } /* Simple menu item. */ - ui_draw_menu_item(&data->fstyle, &rect, name, icon, state, separator_type, nullptr); + ui_draw_menu_item(&data->fstyle, &rect, name, icon, but_flag, separator_type, nullptr); } else { /* Split menu item, faded text before the separator. */ @@ -633,7 +634,7 @@ static void ui_searchbox_region_draw_fn(const bContext *C, ARegion *region) &rect, name, 0, - state | UI_BUT_INACTIVE, + but_flag | UI_BUT_INACTIVE, UI_MENU_ITEM_SEPARATOR_NONE, &name_width); *name_sep = name_sep_prev; @@ -646,7 +647,8 @@ static void ui_searchbox_region_draw_fn(const bContext *C, ARegion *region) } /* The previous menu item draws the active selection. */ - ui_draw_menu_item(&data->fstyle, &rect, name_sep, icon, state, separator_type, nullptr); + ui_draw_menu_item( + &data->fstyle, &rect, name_sep, icon, but_flag, separator_type, nullptr); } } /* indicate more */ @@ -677,7 +679,7 @@ static void ui_searchbox_region_free_fn(ARegion *region) MEM_freeN(data->items.names); MEM_freeN(data->items.pointers); MEM_freeN(data->items.icons); - MEM_freeN(data->items.states); + MEM_freeN(data->items.but_flags); if (data->items.name_prefix_offsets != nullptr) { MEM_freeN(data->items.name_prefix_offsets); @@ -847,7 +849,7 @@ static ARegion *ui_searchbox_create_generic_ex(bContext *C, data->items.names = (char **)MEM_callocN(data->items.maxitem * sizeof(void *), __func__); data->items.pointers = (void **)MEM_callocN(data->items.maxitem * sizeof(void *), __func__); data->items.icons = (int *)MEM_callocN(data->items.maxitem * sizeof(int), __func__); - data->items.states = (int *)MEM_callocN(data->items.maxitem * sizeof(int), __func__); + data->items.but_flags = (int *)MEM_callocN(data->items.maxitem * sizeof(int), __func__); data->items.name_prefix_offsets = nullptr; /* Lazy initialized as needed. */ for (int i = 0; i < data->items.maxitem; i++) { data->items.names[i] = (char *)MEM_callocN(data->items.maxstrlen + 1, __func__); @@ -913,7 +915,7 @@ static void ui_searchbox_region_draw_cb__operator(const bContext *UNUSED(C), ARe /* widget itself */ /* NOTE: i18n messages extracting tool does the same, please keep it in sync. */ { - const int state = ((a == data->active) ? UI_ACTIVE : 0) | data->items.states[a]; + const int but_flag = ((a == data->active) ? UI_ACTIVE : 0) | data->items.but_flags[a]; wmOperatorType *ot = static_cast<wmOperatorType *>(data->items.pointers[a]); char text_pre[128]; @@ -936,14 +938,14 @@ static void ui_searchbox_region_draw_cb__operator(const bContext *UNUSED(C), ARe &rect_pre, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, text_pre), data->items.icons[a], - state, + but_flag, UI_MENU_ITEM_SEPARATOR_NONE, nullptr); ui_draw_menu_item(&data->fstyle, &rect_post, data->items.names[a], 0, - state, + but_flag, data->use_shortcut_sep ? UI_MENU_ITEM_SEPARATOR_SHORTCUT : UI_MENU_ITEM_SEPARATOR_NONE, nullptr); diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c index c7ebecb178b..82d4405e1b5 100644 --- a/source/blender/editors/interface/interface_region_tooltip.c +++ b/source/blender/editors/interface/interface_region_tooltip.c @@ -800,7 +800,7 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C, .style = UI_TIP_STYLE_HEADER, .color_id = UI_TIP_LC_NORMAL, }); - field->text = BLI_sprintfN("%s", but_label.strinfo); + field->text = BLI_strdup(but_label.strinfo); } /* Tip */ diff --git a/source/blender/editors/interface/interface_style.cc b/source/blender/editors/interface/interface_style.cc index 0156a943015..904765f6dc4 100644 --- a/source/blender/editors/interface/interface_style.cc +++ b/source/blender/editors/interface/interface_style.cc @@ -376,25 +376,14 @@ void uiStyleInit(void) { const uiStyle *style = static_cast<uiStyle *>(U.uistyles.first); - /* recover from uninitialized dpi */ + /* Recover from uninitialized DPI. */ if (U.dpi == 0) { U.dpi = 72; } CLAMP(U.dpi, 48, 144); - LISTBASE_FOREACH (uiFont *, font, &U.uifonts) { - BLF_unload_id(font->blf_id); - } - - if (blf_mono_font != -1) { - BLF_unload_id(blf_mono_font); - blf_mono_font = -1; - } - - if (blf_mono_font_render != -1) { - BLF_unload_id(blf_mono_font_render); - blf_mono_font_render = -1; - } + /* Needed so that custom fonts are always first. */ + BLF_unload_all(); uiFont *font_first = static_cast<uiFont *>(U.uifonts.first); @@ -498,6 +487,9 @@ void uiStyleInit(void) const bool unique = true; blf_mono_font_render = BLF_load_mono_default(unique); } + + /* Load the fallback fonts last. */ + BLF_load_font_stack(); } void UI_fontstyle_set(const uiFontStyle *fs) diff --git a/source/blender/editors/interface/interface_template_attribute_search.cc b/source/blender/editors/interface/interface_template_attribute_search.cc index 384e9d1794e..4e587bd5338 100644 --- a/source/blender/editors/interface/interface_template_attribute_search.cc +++ b/source/blender/editors/interface/interface_template_attribute_search.cc @@ -24,14 +24,14 @@ using blender::nodes::geometry_nodes_eval_log::GeometryAttributeInfo; namespace blender::ui { -static StringRef attribute_data_type_string(const CustomDataType type) +static StringRef attribute_data_type_string(const eCustomDataType type) { const char *name = nullptr; RNA_enum_name_from_value(rna_enum_attribute_type_items, type, &name); return StringRef(IFACE_(name)); } -static StringRef attribute_domain_string(const AttributeDomain domain) +static StringRef attribute_domain_string(const eAttrDomain domain) { const char *name = nullptr; RNA_enum_name_from_value(rna_enum_attribute_domain_items, domain, &name); @@ -40,8 +40,8 @@ static StringRef attribute_domain_string(const AttributeDomain domain) static bool attribute_search_item_add(uiSearchItems *items, const GeometryAttributeInfo &item) { - const StringRef data_type_name = attribute_data_type_string(item.data_type); - const StringRef domain_name = attribute_domain_string(item.domain); + const StringRef data_type_name = attribute_data_type_string(*item.data_type); + const StringRef domain_name = attribute_domain_string(*item.domain); std::string search_item_text = domain_name + " " + UI_MENU_ARROW_SEP + item.name + UI_SEP_CHAR + data_type_name; @@ -91,6 +91,9 @@ void attribute_search_add_items(StringRefNull str, if (item->name == "normal" && item->domain == ATTR_DOMAIN_FACE) { continue; } + if (!bke::allow_procedural_attribute_access(item->name)) { + continue; + } BLI_string_search_add(search, item->name.c_str(), (void *)item, 0); } diff --git a/source/blender/editors/interface/interface_template_list.cc b/source/blender/editors/interface/interface_template_list.cc index 68a699c652a..e0b6bbb34c4 100644 --- a/source/blender/editors/interface/interface_template_list.cc +++ b/source/blender/editors/interface/interface_template_list.cc @@ -945,13 +945,8 @@ static void ui_template_list_layout_draw(bContext *C, const bool show_names = (flags & UI_TEMPLATE_LIST_NO_NAMES) == 0; - /* TODO ED_fileselect_init_layout(). Share somehow? */ - float size_x = (96.0f / 20.0f) * UI_UNIT_X; - float size_y = (96.0f / 20.0f) * UI_UNIT_Y; - - if (!show_names) { - size_y -= UI_UNIT_Y; - } + const int size_x = UI_preview_tile_size_x(); + const int size_y = show_names ? UI_preview_tile_size_y() : UI_preview_tile_size_y_no_label(); const int cols_per_row = MAX2((uiLayoutGetWidth(box) - V2D_SCROLL_WIDTH) / size_x, 1); uiLayout *grid = uiLayoutGridFlow(row, true, cols_per_row, true, true, true); diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 4e6437e043a..05ae5299e58 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -742,7 +742,7 @@ static void template_id_liboverride_hierarchy_create(bContext *C, object_active->id.tag |= LIB_TAG_DOIT; } BKE_lib_override_library_create( - bmain, scene, view_layer, NULL, id, &collection_active->id, NULL, &id_override); + bmain, scene, view_layer, NULL, id, &collection_active->id, NULL, &id_override, false); } else if (object_active != NULL && !ID_IS_LINKED(object_active) && &object_active->instance_collection->id == id) { @@ -754,7 +754,8 @@ static void template_id_liboverride_hierarchy_create(bContext *C, id, &object_active->id, &object_active->id, - &id_override); + &id_override, + false); } break; case ID_OB: @@ -765,7 +766,7 @@ static void template_id_liboverride_hierarchy_create(bContext *C, object_active->id.tag |= LIB_TAG_DOIT; } BKE_lib_override_library_create( - bmain, scene, view_layer, NULL, id, &collection_active->id, NULL, &id_override); + bmain, scene, view_layer, NULL, id, &collection_active->id, NULL, &id_override, false); } break; case ID_ME: @@ -787,13 +788,20 @@ static void template_id_liboverride_hierarchy_create(bContext *C, if (object_active != NULL) { object_active->id.tag |= LIB_TAG_DOIT; } - BKE_lib_override_library_create( - bmain, scene, view_layer, NULL, id, &collection_active->id, NULL, &id_override); + BKE_lib_override_library_create(bmain, + scene, + view_layer, + NULL, + id, + &collection_active->id, + NULL, + &id_override, + false); } else { object_active->id.tag |= LIB_TAG_DOIT; BKE_lib_override_library_create( - bmain, scene, view_layer, NULL, id, &object_active->id, NULL, &id_override); + bmain, scene, view_layer, NULL, id, &object_active->id, NULL, &id_override, false); } } break; @@ -812,8 +820,11 @@ static void template_id_liboverride_hierarchy_create(bContext *C, id_override->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED; *r_undo_push_label = "Make Library Override Hierarchy"; - WM_event_add_notifier(C, NC_WINDOW, NULL); - DEG_relations_tag_update(bmain); + /* Given `idptr` is re-assigned to owner property by caller to ensure proper updates etc. Here + * we also use it to ensure remapping of the owner property from the linked data to the newly + * created liboverride (note that in theory this remapping has already been done by code + * above). */ + RNA_id_pointer_create(id_override, idptr); } } @@ -1525,8 +1536,8 @@ static void template_ID_tabs(const bContext *C, 0.0f, ""); UI_but_funcN_set(&tab->but, template_ID_set_property_exec_fn, MEM_dupallocN(template), id); + UI_but_drag_set_id(&tab->but, id); tab->but.custom_data = (void *)id; - tab->but.dragpoin = id; tab->menu = mt; UI_but_drawflag_enable(&tab->but, but_align); diff --git a/source/blender/editors/interface/interface_utils.cc b/source/blender/editors/interface/interface_utils.cc index 993ccdf92f7..b7ca2d9aa11 100644 --- a/source/blender/editors/interface/interface_utils.cc +++ b/source/blender/editors/interface/interface_utils.cc @@ -24,6 +24,7 @@ #include "BLT_translation.h" #include "BKE_context.h" +#include "BKE_global.h" #include "BKE_lib_id.h" #include "BKE_report.h" @@ -516,71 +517,136 @@ void ui_rna_collection_search_update_fn( StringSearch *search = skip_filter ? nullptr : BLI_string_search_new(); - /* build a temporary list of relevant items first */ - int item_index = 0; - RNA_PROP_BEGIN (&data->search_ptr, itemptr, data->search_prop) { + if (data->search_prop != nullptr) { + /* build a temporary list of relevant items first */ + int item_index = 0; + RNA_PROP_BEGIN (&data->search_ptr, itemptr, data->search_prop) { - if (flag & PROP_ID_SELF_CHECK) { - if (itemptr.data == data->target_ptr.owner_id) { - continue; + if (flag & PROP_ID_SELF_CHECK) { + if (itemptr.data == data->target_ptr.owner_id) { + continue; + } } - } - /* use filter */ - if (is_ptr_target) { - if (RNA_property_pointer_poll(&data->target_ptr, data->target_prop, &itemptr) == 0) { - continue; + /* use filter */ + if (is_ptr_target) { + if (RNA_property_pointer_poll(&data->target_ptr, data->target_prop, &itemptr) == 0) { + continue; + } } - } - int name_prefix_offset = 0; - int iconid = ICON_NONE; - bool has_sep_char = false; - const bool is_id = itemptr.type && RNA_struct_is_ID(itemptr.type); + int name_prefix_offset = 0; + int iconid = ICON_NONE; + bool has_sep_char = false; + const bool is_id = itemptr.type && RNA_struct_is_ID(itemptr.type); - if (is_id) { - iconid = ui_id_icon_get(C, static_cast<ID *>(itemptr.data), false); - if (!ELEM(iconid, 0, ICON_BLANK1)) { - has_id_icon = true; - } + if (is_id) { + iconid = ui_id_icon_get(C, static_cast<ID *>(itemptr.data), false); + if (!ELEM(iconid, 0, ICON_BLANK1)) { + has_id_icon = true; + } - if (requires_exact_data_name) { - name = RNA_struct_name_get_alloc(&itemptr, name_buf, sizeof(name_buf), nullptr); + if (requires_exact_data_name) { + name = RNA_struct_name_get_alloc(&itemptr, name_buf, sizeof(name_buf), nullptr); + } + else { + const ID *id = static_cast<ID *>(itemptr.data); + BKE_id_full_name_ui_prefix_get(name_buf, id, true, UI_SEP_CHAR, &name_prefix_offset); + BLI_STATIC_ASSERT(sizeof(name_buf) >= MAX_ID_FULL_NAME_UI, + "Name string buffer should be big enough to hold full UI ID name"); + name = name_buf; + has_sep_char = ID_IS_LINKED(id); + } } else { - const ID *id = static_cast<ID *>(itemptr.data); - BKE_id_full_name_ui_prefix_get(name_buf, id, true, UI_SEP_CHAR, &name_prefix_offset); - BLI_STATIC_ASSERT(sizeof(name_buf) >= MAX_ID_FULL_NAME_UI, - "Name string buffer should be big enough to hold full UI ID name"); - name = name_buf; - has_sep_char = ID_IS_LINKED(id); + name = RNA_struct_name_get_alloc(&itemptr, name_buf, sizeof(name_buf), nullptr); } - } - else { - name = RNA_struct_name_get_alloc(&itemptr, name_buf, sizeof(name_buf), nullptr); - } - if (name) { - CollItemSearch *cis = MEM_cnew<CollItemSearch>(__func__); - cis->data = itemptr.data; - cis->name = BLI_strdup(name); - cis->index = item_index; - cis->iconid = iconid; - cis->is_id = is_id; - cis->name_prefix_offset = name_prefix_offset; - cis->has_sep_char = has_sep_char; - if (!skip_filter) { - BLI_string_search_add(search, name, cis, 0); + if (name) { + CollItemSearch *cis = MEM_cnew<CollItemSearch>(__func__); + cis->data = itemptr.data; + cis->name = BLI_strdup(name); + cis->index = item_index; + cis->iconid = iconid; + cis->is_id = is_id; + cis->name_prefix_offset = name_prefix_offset; + cis->has_sep_char = has_sep_char; + if (!skip_filter) { + BLI_string_search_add(search, name, cis, 0); + } + BLI_addtail(items_list, cis); + if (name != name_buf) { + MEM_freeN(name); + } } - BLI_addtail(items_list, cis); - if (name != name_buf) { - MEM_freeN(name); + + item_index++; + } + RNA_PROP_END; + } + else { + BLI_assert(RNA_property_type(data->target_prop) == PROP_STRING); + const eStringPropertySearchFlag search_flag = RNA_property_string_search_flag( + data->target_prop); + BLI_assert(search_flag & PROP_STRING_SEARCH_SUPPORTED); + + struct SearchVisitUserData { + StringSearch *search; + bool skip_filter; + int item_index; + ListBase *items_list; + const char *func_id; + } user_data = {nullptr}; + + user_data.search = search; + user_data.skip_filter = skip_filter; + user_data.items_list = items_list; + user_data.func_id = __func__; + + RNA_property_string_search( + C, + &data->target_ptr, + data->target_prop, + str, + [](void *user_data, const StringPropertySearchVisitParams *visit_params) { + const bool show_extra_info = (G.debug_value == 102); + + SearchVisitUserData *search_data = (struct SearchVisitUserData *)user_data; + CollItemSearch *cis = MEM_cnew<CollItemSearch>(search_data->func_id); + cis->data = nullptr; + if (visit_params->info && show_extra_info) { + cis->name = BLI_sprintfN( + "%s" UI_SEP_CHAR_S "%s", visit_params->text, visit_params->info); + } + else { + cis->name = BLI_strdup(visit_params->text); + } + cis->index = search_data->item_index; + cis->iconid = ICON_NONE; + cis->is_id = false; + cis->name_prefix_offset = 0; + cis->has_sep_char = visit_params->info != nullptr; + if (!search_data->skip_filter) { + BLI_string_search_add(search_data->search, visit_params->text, cis, 0); + } + BLI_addtail(search_data->items_list, cis); + search_data->item_index++; + }, + (void *)&user_data); + + if (search_flag & PROP_STRING_SEARCH_SORT) { + BLI_listbase_sort(items_list, [](const void *a_, const void *b_) -> int { + const CollItemSearch *cis_a = (const CollItemSearch *)a_; + const CollItemSearch *cis_b = (const CollItemSearch *)b_; + return BLI_strcasecmp_natural(cis_a->name, cis_b->name); + }); + int i = 0; + LISTBASE_FOREACH (CollItemSearch *, cis, items_list) { + cis->index = i; + i++; } } - - item_index++; } - RNA_PROP_END; if (skip_filter) { LISTBASE_FOREACH (CollItemSearch *, cis, items_list) { diff --git a/source/blender/editors/interface/interface_view.cc b/source/blender/editors/interface/interface_view.cc index 85e1dbe73a5..699ac0c2b53 100644 --- a/source/blender/editors/interface/interface_view.cc +++ b/source/blender/editors/interface/interface_view.cc @@ -10,15 +10,22 @@ */ #include <memory> +#include <type_traits> #include <variant> #include "DNA_screen_types.h" +#include "BKE_screen.h" + #include "BLI_listbase.h" +#include "ED_screen.h" + #include "interface_intern.h" #include "UI_interface.hh" + +#include "UI_grid_view.hh" #include "UI_tree_view.hh" using namespace blender; @@ -30,29 +37,51 @@ using namespace blender::ui; */ struct ViewLink : public Link { using TreeViewPtr = std::unique_ptr<AbstractTreeView>; + using GridViewPtr = std::unique_ptr<AbstractGridView>; std::string idname; /* NOTE: Can't use std::get() on this until minimum macOS deployment target is 10.14. */ - std::variant<TreeViewPtr> view; + std::variant<TreeViewPtr, GridViewPtr> view; }; +template<class T> constexpr void check_if_valid_view_type() +{ + static_assert(std::is_same_v<T, AbstractTreeView> || std::is_same_v<T, AbstractGridView>, + "Unsupported view type"); +} + template<class T> T *get_view_from_link(ViewLink &link) { auto *t_uptr = std::get_if<std::unique_ptr<T>>(&link.view); return t_uptr ? t_uptr->get() : nullptr; } -AbstractTreeView *UI_block_add_view(uiBlock &block, - StringRef idname, - std::unique_ptr<AbstractTreeView> tree_view) +template<class T> +static T *ui_block_add_view_impl(uiBlock &block, StringRef idname, std::unique_ptr<T> view) { + check_if_valid_view_type<T>(); + ViewLink *view_link = MEM_new<ViewLink>(__func__); BLI_addtail(&block.views, view_link); - view_link->view = std::move(tree_view); + view_link->view = std::move(view); view_link->idname = idname; - return get_view_from_link<AbstractTreeView>(*view_link); + return get_view_from_link<T>(*view_link); +} + +AbstractGridView *UI_block_add_view(uiBlock &block, + StringRef idname, + std::unique_ptr<AbstractGridView> tree_view) +{ + return ui_block_add_view_impl<AbstractGridView>(block, idname, std::move(tree_view)); +} + +AbstractTreeView *UI_block_add_view(uiBlock &block, + StringRef idname, + std::unique_ptr<AbstractTreeView> tree_view) +{ + return ui_block_add_view_impl<AbstractTreeView>(block, idname, std::move(tree_view)); } void ui_block_free_views(uiBlock *block) @@ -62,6 +91,26 @@ void ui_block_free_views(uiBlock *block) } } +void UI_block_views_listen(const uiBlock *block, const wmRegionListenerParams *listener_params) +{ + ARegion *region = listener_params->region; + + LISTBASE_FOREACH (ViewLink *, view_link, &block->views) { + if (AbstractGridView *grid_view = get_view_from_link<AbstractGridView>(*view_link)) { + if (UI_grid_view_listen_should_redraw(reinterpret_cast<uiGridViewHandle *>(grid_view), + listener_params->notifier)) { + ED_region_tag_redraw(region); + } + } + else if (AbstractTreeView *tree_view = get_view_from_link<AbstractTreeView>(*view_link)) { + if (UI_tree_view_listen_should_redraw(reinterpret_cast<uiTreeViewHandle *>(tree_view), + listener_params->notifier)) { + ED_region_tag_redraw(region); + } + } + } +} + uiTreeViewItemHandle *UI_block_tree_view_find_item_at(const ARegion *region, const int xy[2]) { uiButTreeRow *tree_row_but = (uiButTreeRow *)ui_tree_row_find_mouse_over(region, xy); @@ -82,11 +131,13 @@ uiTreeViewItemHandle *UI_block_tree_view_find_active_item(const ARegion *region) return tree_row_but->tree_item; } -static StringRef ui_block_view_find_idname(const uiBlock &block, const AbstractTreeView &view) +template<class T> static StringRef ui_block_view_find_idname(const uiBlock &block, const T &view) { + check_if_valid_view_type<T>(); + /* First get the idname the of the view we're looking for. */ LISTBASE_FOREACH (ViewLink *, view_link, &block.views) { - if (get_view_from_link<AbstractTreeView>(*view_link) == &view) { + if (get_view_from_link<T>(*view_link) == &view) { return view_link->idname; } } @@ -94,9 +145,11 @@ static StringRef ui_block_view_find_idname(const uiBlock &block, const AbstractT return {}; } -static AbstractTreeView *ui_block_view_find_matching_in_old_block(const uiBlock &new_block, - const AbstractTreeView &new_view) +template<class T> +static T *ui_block_view_find_matching_in_old_block(const uiBlock &new_block, const T &new_view) { + check_if_valid_view_type<T>(); + uiBlock *old_block = new_block.oldblock; if (!old_block) { return nullptr; @@ -109,15 +162,15 @@ static AbstractTreeView *ui_block_view_find_matching_in_old_block(const uiBlock LISTBASE_FOREACH (ViewLink *, old_view_link, &old_block->views) { if (old_view_link->idname == idname) { - return get_view_from_link<AbstractTreeView>(*old_view_link); + return get_view_from_link<T>(*old_view_link); } } return nullptr; } -uiTreeViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_block, - const uiTreeViewHandle *new_view_handle) +uiTreeViewHandle *ui_block_tree_view_find_matching_in_old_block( + const uiBlock *new_block, const uiTreeViewHandle *new_view_handle) { BLI_assert(new_block && new_view_handle); const AbstractTreeView &new_view = reinterpret_cast<const AbstractTreeView &>(*new_view_handle); @@ -126,6 +179,16 @@ uiTreeViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_bl return reinterpret_cast<uiTreeViewHandle *>(old_view); } +uiGridViewHandle *ui_block_grid_view_find_matching_in_old_block( + const uiBlock *new_block, const uiGridViewHandle *new_view_handle) +{ + BLI_assert(new_block && new_view_handle); + const AbstractGridView &new_view = reinterpret_cast<const AbstractGridView &>(*new_view_handle); + + AbstractGridView *old_view = ui_block_view_find_matching_in_old_block(*new_block, new_view); + return reinterpret_cast<uiGridViewHandle *>(old_view); +} + uiButTreeRow *ui_block_view_find_treerow_in_old_block(const uiBlock *new_block, const uiTreeViewItemHandle *new_item_handle) { diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 98ecf91adbc..e2df2d77817 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -105,24 +105,25 @@ typedef enum { UI_WTYPE_PROGRESSBAR, UI_WTYPE_NODESOCKET, UI_WTYPE_TREEROW, + UI_WTYPE_GRID_TILE, } uiWidgetTypeEnum; -/* Button state argument shares bits with 'uiBut.flag'. - * reuse flags that aren't needed for drawing to avoid collision. */ -enum { - /* Show that holding the button opens a menu. */ - UI_STATE_HOLD_ACTION = UI_BUT_UPDATE_DELAY, - UI_STATE_TEXT_INPUT = UI_BUT_UNDO, - UI_STATE_ACTIVE_LEFT = UI_BUT_VALUE_CLEAR, - UI_STATE_ACTIVE_RIGHT = UI_BUT_TEXTEDIT_UPDATE, - UI_STATE_TEXT_BEFORE_WIDGET = UI_BUT_IMMEDIATE, - - UI_STATE_FLAGS_ALL = (UI_STATE_HOLD_ACTION | UI_STATE_TEXT_INPUT | UI_STATE_ACTIVE_LEFT | - UI_STATE_ACTIVE_RIGHT | UI_STATE_TEXT_BEFORE_WIDGET), -}; -/* Prevent accidental use. */ -#define UI_BUT_UPDATE_DELAY ((void)0) -#define UI_BUT_UNDO ((void)0) +/** + * The button's state information adapted for drawing. Use #STATE_INFO_NULL for empty state. + */ +typedef struct { + /** Copy of #uiBut.flag (possibly with overrides for drawing). */ + int but_flag; + /** Copy of #uiBut.drawflag (possibly with overrides for drawing). */ + int but_drawflag; + + /** Show that holding the button opens a menu. */ + bool has_hold_action : 1; + /** The button is in text input mode. */ + bool is_text_input : 1; +} uiWidgetStateInfo; + +static const uiWidgetStateInfo STATE_INFO_NULL = {0}; /** \} */ @@ -256,10 +257,21 @@ typedef struct uiWidgetType { /* converted colors for state */ uiWidgetColors wcol; - void (*state)(struct uiWidgetType *, int state, int drawflag, eUIEmbossType emboss); - void (*draw)(uiWidgetColors *, rcti *, int state, int roundboxalign, const float zoom); - void (*custom)( - uiBut *, uiWidgetColors *, rcti *, int state, int roundboxalign, const float zoom); + void (*state)(struct uiWidgetType *, const uiWidgetStateInfo *state, eUIEmbossType emboss) + ATTR_NONNULL(); + void (*draw)(uiWidgetColors *, + rcti *, + const uiWidgetStateInfo *, + int roundboxalign, + const float zoom) ATTR_NONNULL(); + void (*custom)(uiBut *, + uiWidgetColors *, + rcti *, + const uiWidgetStateInfo *, + int roundboxalign, + const float zoom) ATTR_NONNULL(); + void (*draw_block)( + uiWidgetColors *, rcti *, int block_flag, int roundboxalign, const float zoom); void (*text)(const uiFontStyle *, const uiWidgetColors *, uiBut *, rcti *); } uiWidgetType; @@ -1289,16 +1301,16 @@ static void widgetbase_draw(uiWidgetBase *wtb, const uiWidgetColors *wcol) #define PREVIEW_PAD 4 -static float widget_alpha_factor(const int state) +static float widget_alpha_factor(const uiWidgetStateInfo *state) { - if (state & (UI_BUT_INACTIVE | UI_BUT_DISABLED)) { - if (state & UI_SEARCH_FILTER_NO_MATCH) { + if (state->but_flag & (UI_BUT_INACTIVE | UI_BUT_DISABLED)) { + if (state->but_flag & UI_SEARCH_FILTER_NO_MATCH) { return 0.25f; } return 0.5f; } - if (state & UI_SEARCH_FILTER_NO_MATCH) { + if (state->but_flag & UI_SEARCH_FILTER_NO_MATCH) { return 0.5f; } @@ -1367,7 +1379,10 @@ static void widget_draw_icon( } } else if (ELEM(but->type, UI_BTYPE_BUT, UI_BTYPE_DECORATOR)) { - alpha *= widget_alpha_factor(but->flag); + uiWidgetStateInfo state = {0}; + state.but_flag = but->flag; + state.but_drawflag = but->drawflag; + alpha *= widget_alpha_factor(&state); } GPU_blend(GPU_BLEND_ALPHA); @@ -1405,7 +1420,7 @@ static void widget_draw_icon( const bool has_theme = UI_icon_get_theme_color(icon, color); /* to indicate draggable */ - if (but->dragpoin && (but->flag & UI_ACTIVE)) { + if (ui_but_drag_is_draggable(but) && (but->flag & UI_ACTIVE)) { UI_icon_draw_ex(xs, ys, icon, aspect, 1.25f, 0.0f, color, has_theme); } else if ((but->flag & (UI_ACTIVE | UI_SELECT | UI_SELECT_DRAW))) { @@ -2446,7 +2461,7 @@ static void widget_draw_text_icon(const uiFontStyle *fstyle, * \{ */ /* put all widget colors on half alpha, use local storage */ -static void ui_widget_color_disabled(uiWidgetType *wt, const int state) +static void ui_widget_color_disabled(uiWidgetType *wt, const uiWidgetStateInfo *state) { static uiWidgetColors wcol_theme_s; @@ -2473,8 +2488,7 @@ static void widget_active_color(uiWidgetColors *wcol) } static const uchar *widget_color_blend_from_flags(const uiWidgetStateColors *wcol_state, - int state, - int drawflag, + const uiWidgetStateInfo *state, const eUIEmbossType emboss) { /* Explicitly require #UI_EMBOSS_NONE_OR_STATUS for color blending with no emboss. */ @@ -2482,44 +2496,44 @@ static const uchar *widget_color_blend_from_flags(const uiWidgetStateColors *wco return NULL; } - if (drawflag & UI_BUT_ANIMATED_CHANGED) { + if (state->but_drawflag & UI_BUT_ANIMATED_CHANGED) { return wcol_state->inner_changed_sel; } - if (state & UI_BUT_ANIMATED_KEY) { + if (state->but_flag & UI_BUT_ANIMATED_KEY) { return wcol_state->inner_key_sel; } - if (state & UI_BUT_ANIMATED) { + if (state->but_flag & UI_BUT_ANIMATED) { return wcol_state->inner_anim_sel; } - if (state & UI_BUT_DRIVEN) { + if (state->but_flag & UI_BUT_DRIVEN) { return wcol_state->inner_driven_sel; } - if (state & UI_BUT_OVERRIDDEN) { + if (state->but_flag & UI_BUT_OVERRIDDEN) { return wcol_state->inner_overridden_sel; } return NULL; } /* copy colors from theme, and set changes in it based on state */ -static void widget_state(uiWidgetType *wt, int state, int drawflag, eUIEmbossType emboss) +static void widget_state(uiWidgetType *wt, const uiWidgetStateInfo *state, eUIEmbossType emboss) { uiWidgetStateColors *wcol_state = wt->wcol_state; - if (state & UI_BUT_LIST_ITEM) { + if (state->but_flag & UI_BUT_LIST_ITEM) { /* Override default widget's colors. */ bTheme *btheme = UI_GetTheme(); wt->wcol_theme = &btheme->tui.wcol_list_item; - if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE | UI_SEARCH_FILTER_NO_MATCH)) { + if (state->but_flag & (UI_BUT_DISABLED | UI_BUT_INACTIVE | UI_SEARCH_FILTER_NO_MATCH)) { ui_widget_color_disabled(wt, state); } } wt->wcol = *(wt->wcol_theme); - const uchar *color_blend = widget_color_blend_from_flags(wcol_state, state, drawflag, emboss); + const uchar *color_blend = widget_color_blend_from_flags(wcol_state, state, emboss); - if (state & UI_SELECT) { + if (state->but_flag & UI_SELECT) { copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel); if (color_blend != NULL) { color_blend_v3_v3(wt->wcol.inner, color_blend, wcol_state->blend); @@ -2527,12 +2541,12 @@ static void widget_state(uiWidgetType *wt, int state, int drawflag, eUIEmbossTyp copy_v3_v3_uchar(wt->wcol.text, wt->wcol.text_sel); - if (state & UI_SELECT) { + if (state->but_flag & UI_SELECT) { SWAP(short, wt->wcol.shadetop, wt->wcol.shadedown); } } else { - if (state & UI_BUT_ACTIVE_DEFAULT) { + if (state->but_flag & 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); } @@ -2544,12 +2558,12 @@ static void widget_state(uiWidgetType *wt, int state, int drawflag, eUIEmbossTyp * even if UI_SELECT. But currently this causes some flickering * as buttons can be created and updated without respect to mouse * position and so can draw without UI_ACTIVE set. See D6503. */ - if (state & UI_ACTIVE) { + if (state->but_flag & UI_ACTIVE) { widget_active_color(&wt->wcol); } } - if (state & UI_BUT_REDALERT) { + if (state->but_flag & UI_BUT_REDALERT) { const uchar red[4] = {255, 0, 0}; if (wt->draw && emboss != UI_EMBOSS_NONE) { color_blend_v3_v3(wt->wcol.inner, red, 0.4f); @@ -2559,14 +2573,14 @@ static void widget_state(uiWidgetType *wt, int state, int drawflag, eUIEmbossTyp } } - if (state & UI_BUT_DRAG_MULTI) { + if (state->but_flag & UI_BUT_DRAG_MULTI) { /* the button isn't SELECT but we're editing this so draw with sel color */ copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel); SWAP(short, wt->wcol.shadetop, wt->wcol.shadedown); color_blend_v3_v3(wt->wcol.text, wt->wcol.text_sel, 0.85f); } - if (state & UI_BUT_NODE_ACTIVE) { + if (state->but_flag & UI_BUT_NODE_ACTIVE) { const uchar blue[4] = {86, 128, 194}; color_blend_v3_v3(wt->wcol.inner, blue, 0.3f); } @@ -2600,14 +2614,16 @@ static float widget_radius_from_rcti(const rcti *rect, const uiWidgetColors *wco * \{ */ /* sliders use special hack which sets 'item' as inner when drawing filling */ -static void widget_state_numslider(uiWidgetType *wt, int state, int drawflag, eUIEmbossType emboss) +static void widget_state_numslider(uiWidgetType *wt, + const uiWidgetStateInfo *state, + eUIEmbossType emboss) { uiWidgetStateColors *wcol_state = wt->wcol_state; /* call this for option button */ - widget_state(wt, state, drawflag, emboss); + widget_state(wt, state, emboss); - const uchar *color_blend = widget_color_blend_from_flags(wcol_state, state, drawflag, emboss); + const uchar *color_blend = widget_color_blend_from_flags(wcol_state, state, emboss); if (color_blend != NULL) { /* Set the slider 'item' so that it reflects state settings too. * De-saturate so the color of the slider doesn't conflict with the blend color, @@ -2617,15 +2633,14 @@ static void widget_state_numslider(uiWidgetType *wt, int state, int drawflag, eU color_ensure_contrast_v3(wt->wcol.item, wt->wcol.inner, 30); } - if (state & UI_SELECT) { + if (state->but_flag & UI_SELECT) { SWAP(short, wt->wcol.shadetop, wt->wcol.shadedown); } } /* labels use theme colors for text */ static void widget_state_option_menu(uiWidgetType *wt, - int state, - int drawflag, + const uiWidgetStateInfo *state, eUIEmbossType emboss) { const bTheme *btheme = UI_GetTheme(); @@ -2638,14 +2653,13 @@ static void widget_state_option_menu(uiWidgetType *wt, copy_v3_v3_uchar(wcol_menu_option.text_sel, btheme->tui.wcol_menu_back.text_sel); wt->wcol_theme = &wcol_menu_option; - widget_state(wt, state, drawflag, emboss); + widget_state(wt, state, emboss); wt->wcol_theme = old_wcol; } static void widget_state_nothing(uiWidgetType *wt, - int UNUSED(state), - int UNUSED(drawflag), + const uiWidgetStateInfo *UNUSED(state), eUIEmbossType UNUSED(emboss)) { wt->wcol = *(wt->wcol_theme); @@ -2653,8 +2667,7 @@ static void widget_state_nothing(uiWidgetType *wt, /* special case, button that calls pulldown */ static void widget_state_pulldown(uiWidgetType *wt, - int UNUSED(state), - int UNUSED(drawflag), + const uiWidgetStateInfo *UNUSED(state), eUIEmbossType UNUSED(emboss)) { wt->wcol = *(wt->wcol_theme); @@ -2662,14 +2675,13 @@ static void widget_state_pulldown(uiWidgetType *wt, /* special case, pie menu items */ static void widget_state_pie_menu_item(uiWidgetType *wt, - int state, - int UNUSED(drawflag), + const uiWidgetStateInfo *state, eUIEmbossType UNUSED(emboss)) { wt->wcol = *(wt->wcol_theme); /* active and disabled (not so common) */ - if ((state & UI_BUT_DISABLED) && (state & UI_ACTIVE)) { + if ((state->but_flag & UI_BUT_DISABLED) && (state->but_flag & UI_ACTIVE)) { color_blend_v3_v3(wt->wcol.text, wt->wcol.text_sel, 0.5f); /* draw the backdrop at low alpha, helps navigating with keys * when disabled items are active */ @@ -2678,18 +2690,18 @@ static void widget_state_pie_menu_item(uiWidgetType *wt, } else { /* regular active */ - if (state & (UI_SELECT | UI_ACTIVE)) { + if (state->but_flag & (UI_SELECT | UI_ACTIVE)) { copy_v3_v3_uchar(wt->wcol.text, wt->wcol.text_sel); } - else if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) { + else if (state->but_flag & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) { /* regular disabled */ color_blend_v3_v3(wt->wcol.text, wt->wcol.inner, 0.5f); } - if (state & UI_SELECT) { + if (state->but_flag & UI_SELECT) { copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel); } - else if (state & UI_ACTIVE) { + else if (state->but_flag & UI_ACTIVE) { copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.item); } } @@ -2697,14 +2709,13 @@ static void widget_state_pie_menu_item(uiWidgetType *wt, /* special case, menu items */ static void widget_state_menu_item(uiWidgetType *wt, - int state, - int UNUSED(drawflag), + const uiWidgetStateInfo *state, eUIEmbossType UNUSED(emboss)) { wt->wcol = *(wt->wcol_theme); /* active and disabled (not so common) */ - if ((state & UI_BUT_DISABLED) && (state & UI_ACTIVE)) { + if ((state->but_flag & UI_BUT_DISABLED) && (state->but_flag & UI_ACTIVE)) { /* draw the backdrop at low alpha, helps navigating with keys * when disabled items are active */ wt->wcol.text[3] = 128; @@ -2713,15 +2724,15 @@ static void widget_state_menu_item(uiWidgetType *wt, } else { /* regular active */ - if (state & UI_ACTIVE) { + if (state->but_flag & UI_ACTIVE) { copy_v3_v3_uchar(wt->wcol.text, wt->wcol.text_sel); } - else if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) { + else if (state->but_flag & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) { /* regular disabled */ color_blend_v3_v3(wt->wcol.text, wt->wcol.inner, 0.5f); } - if (state & UI_ACTIVE) { + if (state->but_flag & UI_ACTIVE) { copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel); } } @@ -2787,7 +2798,7 @@ static void widget_softshadow(const rcti *rect, int roundboxalign, const float r } static void widget_menu_back( - uiWidgetColors *wcol, rcti *rect, int flag, int direction, const float zoom) + uiWidgetColors *wcol, rcti *rect, const int block_flag, const int direction, const float zoom) { uiWidgetBase wtb; int roundboxalign = UI_CNR_ALL; @@ -2795,7 +2806,7 @@ static void widget_menu_back( widget_init(&wtb); /* menu is 2nd level or deeper */ - if (flag & UI_BLOCK_POPUP) { + if (block_flag & UI_BLOCK_POPUP) { // rect->ymin -= 4.0; // rect->ymax += 4.0; } @@ -3321,13 +3332,17 @@ static void ui_draw_separator(const rcti *rect, const uiWidgetColors *wcol) #define NUM_BUT_PADDING_FACTOR 0.425f -static void widget_numbut_draw( - uiWidgetColors *wcol, rcti *rect, const float zoom, int state, int roundboxalign, bool emboss) +static void widget_numbut_draw(uiWidgetColors *wcol, + rcti *rect, + const float zoom, + const uiWidgetStateInfo *state, + int roundboxalign, + bool emboss) { const float rad = widget_radius_from_zoom(zoom, wcol); const int handle_width = min_ii(BLI_rcti_size_x(rect) / 3, BLI_rcti_size_y(rect) * 0.7f); - if (state & UI_SELECT) { + if (state->but_flag & UI_SELECT) { SWAP(short, wcol->shadetop, wcol->shadedown); } @@ -3343,7 +3358,7 @@ static void widget_numbut_draw( } /* decoration */ - if ((state & UI_ACTIVE) && !(state & UI_STATE_TEXT_INPUT)) { + if ((state->but_flag & UI_ACTIVE) && !state->is_text_input) { uiWidgetColors wcol_zone; uiWidgetBase wtb_zone; rcti rect_zone; @@ -3356,7 +3371,7 @@ static void widget_numbut_draw( wcol_zone = *wcol; copy_v3_v3_uchar(wcol_zone.item, wcol->text); - if (state & UI_STATE_ACTIVE_LEFT) { + if (state->but_drawflag & UI_BUT_ACTIVE_LEFT) { widget_active_color(&wcol_zone); } @@ -3376,7 +3391,7 @@ static void widget_numbut_draw( wcol_zone = *wcol; copy_v3_v3_uchar(wcol_zone.item, wcol->text); - if (state & UI_STATE_ACTIVE_RIGHT) { + if (state->but_drawflag & UI_BUT_ACTIVE_RIGHT) { widget_active_color(&wcol_zone); } @@ -3395,7 +3410,7 @@ static void widget_numbut_draw( wcol_zone = *wcol; copy_v3_v3_uchar(wcol_zone.item, wcol->text); - if (!(state & (UI_STATE_ACTIVE_LEFT | UI_STATE_ACTIVE_RIGHT))) { + if (!(state->but_drawflag & (UI_BUT_ACTIVE_LEFT | UI_BUT_ACTIVE_RIGHT))) { widget_active_color(&wcol_zone); } @@ -3414,7 +3429,7 @@ static void widget_numbut_draw( widgetbase_draw(&wtb, wcol); } - if (!(state & UI_STATE_TEXT_INPUT)) { + if (!state->is_text_input) { const float text_padding = NUM_BUT_PADDING_FACTOR * BLI_rcti_size_y(rect); rect->xmin += text_padding; @@ -3422,14 +3437,20 @@ static void widget_numbut_draw( } } -static void widget_numbut( - uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom) +static void widget_numbut(uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *state, + int roundboxalign, + const float zoom) { widget_numbut_draw(wcol, rect, zoom, state, roundboxalign, false); } -static void widget_menubut( - uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign, const float zoom) +static void widget_menubut(uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *UNUSED(state), + int roundboxalign, + const float zoom) { uiWidgetBase wtb; widget_init(&wtb); @@ -3454,7 +3475,7 @@ static void widget_menubut( static void widget_menubut_embossn(const uiBut *UNUSED(but), uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + const uiWidgetStateInfo *UNUSED(state), int UNUSED(roundboxalign)) { uiWidgetBase wtb; @@ -3476,7 +3497,7 @@ static void widget_menubut_embossn(const uiBut *UNUSED(but), static void widget_numbut_embossn(const uiBut *UNUSED(but), uiWidgetColors *wcol, rcti *rect, - int state, + const uiWidgetStateInfo *state, int roundboxalign, const float zoom) { @@ -3486,7 +3507,6 @@ static void widget_numbut_embossn(const uiBut *UNUSED(but), void UI_draw_widget_scroll(uiWidgetColors *wcol, const rcti *rect, const rcti *slider, int state) { uiWidgetBase wtb; - bool outline = false; widget_init(&wtb); @@ -3531,11 +3551,6 @@ void UI_draw_widget_scroll(uiWidgetColors *wcol, const rcti *rect, const rcti *s /* draw */ wtb.draw_emboss = false; /* only emboss once */ - /* exception for progress bar */ - if (state & UI_SCROLL_NO_OUTLINE) { - SWAP(bool, outline, wtb.draw_outline); - } - round_box_edges(&wtb, UI_CNR_ALL, slider, rad); if (state & UI_SCROLL_ARROWS) { @@ -3563,17 +3578,13 @@ void UI_draw_widget_scroll(uiWidgetColors *wcol, const rcti *rect, const rcti *s } } widgetbase_draw(&wtb, wcol); - - if (state & UI_SCROLL_NO_OUTLINE) { - SWAP(bool, outline, wtb.draw_outline); - } } } static void widget_scroll(uiBut *but, uiWidgetColors *wcol, rcti *rect, - int state, + const uiWidgetStateInfo *state, int UNUSED(roundboxalign), const float UNUSED(zoom)) { @@ -3623,19 +3634,13 @@ static void widget_scroll(uiBut *but, } } - if (state & UI_SELECT) { - state = UI_SCROLL_PRESSED; - } - else { - state = 0; - } - UI_draw_widget_scroll(wcol, rect, &rect1, state); + UI_draw_widget_scroll(wcol, rect, &rect1, (state->but_flag & UI_SELECT) ? UI_SCROLL_PRESSED : 0); } static void widget_progressbar(uiBut *but, uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + const uiWidgetStateInfo *UNUSED(state), int roundboxalign, const float zoom) { @@ -3669,7 +3674,7 @@ static void widget_progressbar(uiBut *but, static void widget_treerow_exec(uiWidgetColors *wcol, rcti *rect, - int state, + const uiWidgetStateInfo *state, int UNUSED(roundboxalign), int indentation, const float zoom) @@ -3682,7 +3687,7 @@ static void widget_treerow_exec(uiWidgetColors *wcol, const float rad = widget_radius_from_zoom(zoom, wcol); round_box_edges(&wtb, UI_CNR_ALL, rect, rad); - if ((state & UI_ACTIVE) || (state & UI_SELECT)) { + if ((state->but_flag & UI_ACTIVE) || (state->but_flag & UI_SELECT)) { widgetbase_draw(&wtb, wcol); } @@ -3690,18 +3695,32 @@ static void widget_treerow_exec(uiWidgetColors *wcol, BLI_rcti_translate(rect, 0.5f * UI_UNIT_X * indentation, 0); } -static void widget_treerow( - uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom) +static void widget_treerow(uiBut *but, + uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *state, + int roundboxalign, + const float zoom) { uiButTreeRow *tree_row = (uiButTreeRow *)but; BLI_assert(but->type == UI_BTYPE_TREEROW); widget_treerow_exec(wcol, rect, state, roundboxalign, tree_row->indentation, zoom); } +static void widget_gridtile(uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *state, + int roundboxalign, + const float zoom) +{ + /* TODO Reuse tree-row drawing. */ + widget_treerow_exec(wcol, rect, state, roundboxalign, 0, zoom); +} + static void widget_nodesocket(uiBut *but, uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + const uiWidgetStateInfo *UNUSED(state), int UNUSED(roundboxalign), const float UNUSED(zoom)) { @@ -3737,8 +3756,12 @@ static void widget_nodesocket(uiBut *but, copy_v3_v3_uchar(wcol->outline, old_outline); } -static void widget_numslider( - uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom) +static void widget_numslider(uiBut *but, + uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *state, + int roundboxalign, + const float zoom) { uiWidgetBase wtb, wtb1; widget_init(&wtb); @@ -3752,7 +3775,7 @@ static void widget_numslider( widgetbase_draw(&wtb, wcol); /* Draw slider part only when not in text editing. */ - if (!(state & UI_STATE_TEXT_INPUT)) { + if (!state->is_text_input) { int roundboxalign_slider = roundboxalign; uchar outline[3]; @@ -3760,7 +3783,7 @@ static void widget_numslider( copy_v3_v3_uchar(wcol->outline, wcol->item); copy_v3_v3_uchar(wcol->inner, wcol->item); - if (!(state & UI_SELECT)) { + if (!(state->but_flag & UI_SELECT)) { SWAP(short, wcol->shadetop, wcol->shadedown); } @@ -3828,7 +3851,7 @@ static void widget_numslider( copy_v3_v3_uchar(wcol->outline, outline); - if (!(state & UI_SELECT)) { + if (!(state->but_flag & UI_SELECT)) { SWAP(short, wcol->shadetop, wcol->shadedown); } } @@ -3840,7 +3863,7 @@ static void widget_numslider( /* Add space at either side of the button so text aligns with number-buttons * (which have arrow icons). */ - if (!(state & UI_STATE_TEXT_INPUT)) { + if (!state->is_text_input) { const float text_padding = NUM_BUT_PADDING_FACTOR * BLI_rcti_size_y(rect); rect->xmax -= text_padding; rect->xmin += text_padding; @@ -3850,8 +3873,12 @@ static void widget_numslider( /* I think 3 is sufficient border to indicate keyed status */ #define SWATCH_KEYED_BORDER 3 -static void widget_swatch( - uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom) +static void widget_swatch(uiBut *but, + uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *state, + int roundboxalign, + const float zoom) { BLI_assert(but->type == UI_BTYPE_COLOR); uiButColor *color_but = (uiButColor *)but; @@ -3875,9 +3902,9 @@ static void widget_swatch( ui_but_v3_get(but, col); - if ((state & (UI_BUT_ANIMATED | UI_BUT_ANIMATED_KEY | UI_BUT_DRIVEN | UI_BUT_OVERRIDDEN | - UI_BUT_REDALERT)) || - (but->drawflag & UI_BUT_ANIMATED_CHANGED)) { + if ((state->but_flag & (UI_BUT_ANIMATED | UI_BUT_ANIMATED_KEY | UI_BUT_DRIVEN | + UI_BUT_OVERRIDDEN | UI_BUT_REDALERT)) || + (state->but_drawflag & UI_BUT_ANIMATED_CHANGED)) { /* draw based on state - color for keyed etc */ widgetbase_draw(&wtb, wcol); @@ -3938,7 +3965,7 @@ static void widget_swatch( static void widget_unitvec(uiBut *but, uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + const uiWidgetStateInfo *UNUSED(state), int UNUSED(roundboxalign), const float zoom) { @@ -3946,10 +3973,15 @@ static void widget_unitvec(uiBut *but, ui_draw_but_UNITVEC(but, wcol, rect, rad); } -static void widget_icon_has_anim( - uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom) +static void widget_icon_has_anim(uiBut *but, + uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *state, + int roundboxalign, + const float zoom) { - if (state & (UI_BUT_ANIMATED | UI_BUT_ANIMATED_KEY | UI_BUT_DRIVEN | UI_BUT_REDALERT) && + if (state->but_flag & + (UI_BUT_ANIMATED | UI_BUT_ANIMATED_KEY | UI_BUT_DRIVEN | UI_BUT_REDALERT) && but->emboss != UI_EMBOSS_NONE) { uiWidgetBase wtb; widget_init(&wtb); @@ -3970,10 +4002,13 @@ static void widget_icon_has_anim( } } -static void widget_textbut( - uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom) +static void widget_textbut(uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *state, + int roundboxalign, + const float zoom) { - if (state & UI_SELECT) { + if (state->but_flag & UI_SELECT) { SWAP(short, wcol->shadetop, wcol->shadedown); } @@ -3989,7 +4024,7 @@ static void widget_textbut( static void widget_preview_tile(uiBut *but, uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + const uiWidgetStateInfo *UNUSED(state), int UNUSED(roundboxalign), const float UNUSED(zoom)) { @@ -3998,8 +4033,11 @@ static void widget_preview_tile(uiBut *but, &style->widget, rect, but->drawstr, but->icon, wcol->text, UI_STYLE_TEXT_CENTER); } -static void widget_menuiconbut( - uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign, const float zoom) +static void widget_menuiconbut(uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *UNUSED(state), + int roundboxalign, + const float zoom) { uiWidgetBase wtb; widget_init(&wtb); @@ -4011,17 +4049,20 @@ static void widget_menuiconbut( widgetbase_draw(&wtb, wcol); } -static void widget_pulldownbut( - uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom) +static void widget_pulldownbut(uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *state, + int roundboxalign, + const float zoom) { float back[4]; UI_GetThemeColor4fv(TH_BACK, back); - if ((state & UI_ACTIVE) || (back[3] < 1.0f)) { + if ((state->but_flag & UI_ACTIVE) || (back[3] < 1.0f)) { uiWidgetBase wtb; const float rad = widget_radius_from_zoom(zoom, wcol); - if (state & UI_ACTIVE) { + if (state->but_flag & UI_ACTIVE) { copy_v4_v4_uchar(wcol->inner, wcol->inner_sel); copy_v3_v3_uchar(wcol->text, wcol->text_sel); copy_v3_v3_uchar(wcol->outline, wcol->inner); @@ -4042,7 +4083,7 @@ static void widget_pulldownbut( static void widget_menu_itembut(uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + const uiWidgetStateInfo *UNUSED(state), int UNUSED(roundboxalign), const float zoom) { @@ -4066,7 +4107,7 @@ static void widget_menu_itembut(uiWidgetColors *wcol, static void widget_menu_itembut_unpadded(uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + const uiWidgetStateInfo *UNUSED(state), int UNUSED(roundboxalign), const float zoom) { @@ -4088,7 +4129,7 @@ static void widget_menu_itembut_unpadded(uiWidgetColors *wcol, static void widget_menu_radial_itembut(uiBut *but, uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + const uiWidgetStateInfo *UNUSED(state), int UNUSED(roundboxalign), const float zoom) { @@ -4114,7 +4155,7 @@ static void widget_menu_radial_itembut(uiBut *but, static void widget_list_itembut(uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + const uiWidgetStateInfo *UNUSED(state), int UNUSED(roundboxalign), const float zoom) { @@ -4131,11 +4172,13 @@ static void widget_list_itembut(uiWidgetColors *wcol, static void widget_optionbut(uiWidgetColors *wcol, rcti *rect, - int state, + const uiWidgetStateInfo *state, int UNUSED(roundboxalign), const float UNUSED(zoom)) { - const bool text_before_widget = (state & UI_STATE_TEXT_BEFORE_WIDGET); + /* For a right aligned layout (signified by #UI_BUT_TEXT_RIGHT), draw the text on the left of the + * checkbox. */ + const bool text_before_widget = (state->but_drawflag & UI_BUT_TEXT_RIGHT); rcti recttemp = *rect; uiWidgetBase wtb; @@ -4160,7 +4203,7 @@ static void widget_optionbut(uiWidgetColors *wcol, round_box_edges(&wtb, UI_CNR_ALL, &recttemp, rad); /* decoration */ - if (state & UI_SELECT) { + if (state->but_flag & UI_SELECT) { shape_preset_trias_from_rect_checkmark(&wtb.tria1, &recttemp); } @@ -4177,19 +4220,21 @@ static void widget_optionbut(uiWidgetColors *wcol, } /* labels use Editor theme colors for text */ -static void widget_state_label(uiWidgetType *wt, int state, int drawflag, eUIEmbossType emboss) +static void widget_state_label(uiWidgetType *wt, + const uiWidgetStateInfo *state, + eUIEmbossType emboss) { - if (state & UI_BUT_LIST_ITEM) { + if (state->but_flag & UI_BUT_LIST_ITEM) { /* Override default label theme's colors. */ bTheme *btheme = UI_GetTheme(); wt->wcol_theme = &btheme->tui.wcol_list_item; /* call this for option button */ - widget_state(wt, state, drawflag, emboss); + widget_state(wt, state, emboss); } else { /* call this for option button */ - widget_state(wt, state, drawflag, emboss); - if (state & UI_SELECT) { + widget_state(wt, state, emboss); + if (state->but_flag & UI_SELECT) { UI_GetThemeColor3ubv(TH_TEXT_HI, wt->wcol.text); } else { @@ -4197,14 +4242,17 @@ static void widget_state_label(uiWidgetType *wt, int state, int drawflag, eUIEmb } } - if (state & UI_BUT_REDALERT) { + if (state->but_flag & UI_BUT_REDALERT) { const uchar red[4] = {255, 0, 0}; color_blend_v3_v3(wt->wcol.text, red, 0.4f); } } -static void widget_radiobut( - uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign, const float zoom) +static void widget_radiobut(uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *UNUSED(state), + int roundboxalign, + const float zoom) { uiWidgetBase wtb; widget_init(&wtb); @@ -4218,7 +4266,7 @@ static void widget_radiobut( static void widget_box(uiBut *but, uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + const uiWidgetStateInfo *UNUSED(state), int roundboxalign, const float zoom) { @@ -4244,8 +4292,11 @@ static void widget_box(uiBut *but, copy_v3_v3_uchar(wcol->inner, old_col); } -static void widget_but( - uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign, const float zoom) +static void widget_but(uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *UNUSED(state), + int roundboxalign, + const float zoom) { uiWidgetBase wtb; widget_init(&wtb); @@ -4271,13 +4322,16 @@ static void widget_roundbut(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), } #endif -static void widget_roundbut_exec( - uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom) +static void widget_roundbut_exec(uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *state, + int roundboxalign, + const float zoom) { uiWidgetBase wtb; widget_init(&wtb); - if (state & UI_STATE_HOLD_ACTION) { + if (state->has_hold_action) { /* Show that keeping pressed performs another action (typically a menu). */ shape_preset_init_hold_action(&wtb.tria1, rect, 0.75f, 'r'); } @@ -4290,11 +4344,14 @@ static void widget_roundbut_exec( widgetbase_draw(&wtb, wcol); } -static void widget_tab( - uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom) +static void widget_tab(uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *state, + int roundboxalign, + const float zoom) { const float rad = widget_radius_from_zoom(zoom, wcol); - const bool is_active = (state & UI_SELECT); + const bool is_active = (state->but_flag & UI_SELECT); /* Draw shaded outline - Disabled for now, * seems incorrect and also looks nicer without it IMHO ;). */ @@ -4442,7 +4499,7 @@ static uiWidgetType *widget_type(uiWidgetTypeEnum type) case UI_WTYPE_TOOLTIP: wt.wcol_theme = &btheme->tui.wcol_tooltip; - wt.draw = widget_menu_back; + wt.draw_block = widget_menu_back; break; /* strings */ @@ -4498,7 +4555,7 @@ static uiWidgetType *widget_type(uiWidgetTypeEnum type) case UI_WTYPE_MENU_BACK: wt.wcol_theme = &btheme->tui.wcol_menu_back; - wt.draw = widget_menu_back; + wt.draw_block = widget_menu_back; break; /* specials */ @@ -4552,9 +4609,15 @@ static uiWidgetType *widget_type(uiWidgetTypeEnum type) break; case UI_WTYPE_TREEROW: + wt.wcol_theme = &btheme->tui.wcol_view_item; wt.custom = widget_treerow; break; + case UI_WTYPE_GRID_TILE: + wt.wcol_theme = &btheme->tui.wcol_view_item; + wt.draw = widget_gridtile; + break; + case UI_WTYPE_NODESOCKET: wt.custom = widget_nodesocket; break; @@ -4891,6 +4954,11 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu fstyle = &style->widgetlabel; break; + case UI_BTYPE_GRID_TILE: + wt = widget_type(UI_WTYPE_GRID_TILE); + fstyle = &style->widgetlabel; + break; + case UI_BTYPE_SCROLL: wt = widget_type(UI_WTYPE_SCROLL); break; @@ -4921,62 +4989,50 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu const int roundboxalign = widget_roundbox_set(but, rect); - /* Mask out flags re-used for local state. */ - int state = but->flag & ~UI_STATE_FLAGS_ALL; - const int drawflag = but->drawflag; + uiWidgetStateInfo state = {0}; + state.but_flag = but->flag; + state.but_drawflag = but->drawflag; - if (state & UI_SELECT_DRAW) { - state |= UI_SELECT; + /* Override selected flag for drawing. */ + if (but->flag & UI_SELECT_DRAW) { + state.but_flag |= UI_SELECT; } if ((but->editstr) || (UNLIKELY(but->flag & UI_BUT_DRAG_MULTI) && ui_but_drag_multi_edit_get(but))) { - state |= UI_STATE_TEXT_INPUT; + state.is_text_input = true; } if (but->hold_func) { - state |= UI_STATE_HOLD_ACTION; - } - - if (state & UI_ACTIVE) { - if (but->drawflag & UI_BUT_ACTIVE_LEFT) { - state |= UI_STATE_ACTIVE_LEFT; - } - else if (but->drawflag & UI_BUT_ACTIVE_RIGHT) { - state |= UI_STATE_ACTIVE_RIGHT; - } + state.has_hold_action = true; } bool use_alpha_blend = false; if (but->emboss != UI_EMBOSS_PULLDOWN) { - if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE | UI_SEARCH_FILTER_NO_MATCH)) { + if (but->flag & (UI_BUT_DISABLED | UI_BUT_INACTIVE | UI_SEARCH_FILTER_NO_MATCH)) { use_alpha_blend = true; - ui_widget_color_disabled(wt, state); + ui_widget_color_disabled(wt, &state); } } - if (drawflag & UI_BUT_TEXT_RIGHT) { - state |= UI_STATE_TEXT_BEFORE_WIDGET; - } - #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; + if ((but->flag & UI_ACTIVE) && ui_but_is_popover_once_compat(but)) { + state.but_flag |= UI_BUT_ACTIVE_DEFAULT; } } #endif if (but->block->flag & UI_BLOCK_NO_DRAW_OVERRIDDEN_STATE) { - state &= ~UI_BUT_OVERRIDDEN; + state.but_flag &= ~UI_BUT_OVERRIDDEN; } const float zoom = 1.0f / but->block->aspect; - wt->state(wt, state, drawflag, but->emboss); + wt->state(wt, &state, but->emboss); if (wt->custom) { - wt->custom(but, &wt->wcol, rect, state, roundboxalign, zoom); + wt->custom(but, &wt->wcol, rect, &state, roundboxalign, zoom); } else if (wt->draw) { - wt->draw(&wt->wcol, rect, state, roundboxalign, zoom); + wt->draw(&wt->wcol, rect, &state, roundboxalign, zoom); } if (wt->text) { @@ -5017,13 +5073,13 @@ void ui_draw_menu_back(uiStyle *UNUSED(style), uiBlock *block, rcti *rect) { uiWidgetType *wt = widget_type(UI_WTYPE_MENU_BACK); - wt->state(wt, 0, 0, UI_EMBOSS_UNDEFINED); + wt->state(wt, &STATE_INFO_NULL, UI_EMBOSS_UNDEFINED); if (block) { const float zoom = 1.0f / block->aspect; - wt->draw(&wt->wcol, rect, block->flag, block->direction, zoom); + wt->draw_block(&wt->wcol, rect, block->flag, block->direction, zoom); } else { - wt->draw(&wt->wcol, rect, 0, 0, 1.0f); + wt->draw_block(&wt->wcol, rect, 0, 0, 1.0f); } ui_draw_clip_tri(block, rect, wt); @@ -5118,8 +5174,8 @@ void ui_draw_popover_back(struct ARegion *region, } else { const float zoom = 1.0f / block->aspect; - wt->state(wt, 0, 0, UI_EMBOSS_UNDEFINED); - wt->draw(&wt->wcol, rect, 0, 0, zoom); + wt->state(wt, &STATE_INFO_NULL, UI_EMBOSS_UNDEFINED); + wt->draw_block(&wt->wcol, rect, 0, 0, zoom); } ui_draw_clip_tri(block, rect, wt); @@ -5307,11 +5363,20 @@ static void ui_draw_widget_back_color(uiWidgetTypeEnum type, } rcti rect_copy = *rect; - wt->state(wt, 0, 0, UI_EMBOSS_UNDEFINED); + wt->state(wt, &STATE_INFO_NULL, UI_EMBOSS_UNDEFINED); if (color) { rgba_float_to_uchar(wt->wcol.inner, color); } - wt->draw(&wt->wcol, &rect_copy, 0, UI_CNR_ALL, 1.0f); + + if (wt->draw_block) { + wt->draw_block(&wt->wcol, &rect_copy, 0, UI_CNR_ALL, 1.0f); + } + else if (wt->draw) { + wt->draw(&wt->wcol, &rect_copy, &STATE_INFO_NULL, UI_CNR_ALL, 1.0f); + } + else { + BLI_assert_unreachable(); + } } void ui_draw_widget_menu_back_color(const rcti *rect, bool use_shadow, const float color[4]) { @@ -5326,16 +5391,16 @@ void ui_draw_widget_menu_back(const rcti *rect, bool use_shadow) void ui_draw_tooltip_background(const uiStyle *UNUSED(style), uiBlock *UNUSED(block), rcti *rect) { uiWidgetType *wt = widget_type(UI_WTYPE_TOOLTIP); - wt->state(wt, 0, 0, UI_EMBOSS_UNDEFINED); - /* wt->draw ends up using same function to draw the tooltip as menu_back */ - wt->draw(&wt->wcol, rect, 0, 0, 1.0f); + wt->state(wt, &STATE_INFO_NULL, UI_EMBOSS_UNDEFINED); + /* wt->draw_block ends up using same function to draw the tooltip as menu_back */ + wt->draw_block(&wt->wcol, rect, 0, 0, 1.0f); } void ui_draw_menu_item(const uiFontStyle *fstyle, rcti *rect, const char *name, int iconid, - int state, + int but_flag, uiMenuItemSeparatorType separator_type, int *r_xmax) { @@ -5346,8 +5411,11 @@ void ui_draw_menu_item(const uiFontStyle *fstyle, int padding = 0.25f * row_height; char *cpoin = NULL; - wt->state(wt, state, 0, UI_EMBOSS_UNDEFINED); - wt->draw(&wt->wcol, rect, 0, 0, 1.0f); + uiWidgetStateInfo state = {0}; + state.but_flag = but_flag; + + wt->state(wt, &state, UI_EMBOSS_UNDEFINED); + wt->draw(&wt->wcol, rect, &STATE_INFO_NULL, 0, 1.0f); UI_fontstyle_set(fstyle); @@ -5442,8 +5510,12 @@ void ui_draw_menu_item(const uiFontStyle *fstyle, /* part text right aligned */ if (separator_type != UI_MENU_ITEM_SEPARATOR_NONE) { if (cpoin) { + /* State info for the hint drawing. */ + uiWidgetStateInfo hint_state = state; /* Set inactive state for grayed out text. */ - wt->state(wt, state | UI_BUT_INACTIVE, 0, UI_EMBOSS_UNDEFINED); + hint_state.but_flag |= UI_BUT_INACTIVE; + + wt->state(wt, &hint_state, UI_EMBOSS_UNDEFINED); char hint_drawstr[UI_MAX_DRAW_STR]; { @@ -5528,14 +5600,17 @@ void ui_draw_preview_item(const uiFontStyle *fstyle, rcti *rect, const char *name, int iconid, - int state, + int but_flag, eFontStyle_Align text_align) { uiWidgetType *wt = widget_type(UI_WTYPE_MENU_ITEM_UNPADDED); + uiWidgetStateInfo state = {0}; + state.but_flag = but_flag; + /* drawing button background */ - wt->state(wt, state, 0, UI_EMBOSS_UNDEFINED); - wt->draw(&wt->wcol, rect, 0, 0, 1.0f); + wt->state(wt, &state, UI_EMBOSS_UNDEFINED); + wt->draw(&wt->wcol, rect, &STATE_INFO_NULL, 0, 1.0f); ui_draw_preview_item_stateless(fstyle, rect, name, iconid, wt->wcol.text, text_align); } diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc index bf756fb5838..f86d1c4d8bc 100644 --- a/source/blender/editors/interface/tree_view.cc +++ b/source/blender/editors/interface/tree_view.cc @@ -68,6 +68,12 @@ void AbstractTreeView::foreach_item(ItemIterFn iter_fn, IterOptions options) con foreach_item_recursive(iter_fn, options); } +bool AbstractTreeView::listen(const wmNotifier &) const +{ + /* Nothing by default. */ + return false; +} + bool AbstractTreeView::is_renaming() const { return rename_buffer_ != nullptr; @@ -82,7 +88,7 @@ void AbstractTreeView::update_from_old(uiBlock &new_block) return; } - uiTreeViewHandle *old_view_handle = ui_block_view_find_matching_in_old_block( + uiTreeViewHandle *old_view_handle = ui_block_tree_view_find_matching_in_old_block( &new_block, reinterpret_cast<uiTreeViewHandle *>(this)); if (old_view_handle == nullptr) { is_reconstructed_ = true; @@ -805,6 +811,13 @@ class TreeViewItemAPIWrapper { using namespace blender::ui; +bool UI_tree_view_listen_should_redraw(const uiTreeViewHandle *view_handle, + const wmNotifier *notifier) +{ + const AbstractTreeView &view = *reinterpret_cast<const AbstractTreeView *>(view_handle); + return view.listen(*notifier); +} + bool UI_tree_view_item_is_active(const uiTreeViewItemHandle *item_handle) { const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_handle); diff --git a/source/blender/editors/interface/view2d.cc b/source/blender/editors/interface/view2d.cc index 66171dc13e4..6ece7eb4ffa 100644 --- a/source/blender/editors/interface/view2d.cc +++ b/source/blender/editors/interface/view2d.cc @@ -2103,21 +2103,12 @@ void UI_view2d_text_cache_draw(ARegion *region) col_pack_prev = v2s->col.pack; } - if (v2s->rect.xmin >= v2s->rect.xmax) { - BLF_draw_default((float)(v2s->mval[0] + xofs), - (float)(v2s->mval[1] + yofs), - 0.0, - v2s->str, - BLF_DRAW_STR_DUMMY_MAX); - } - else { - BLF_enable(font_id, BLF_CLIPPING); - BLF_clipping( - font_id, v2s->rect.xmin - 4, v2s->rect.ymin - 4, v2s->rect.xmax + 4, v2s->rect.ymax + 4); - BLF_draw_default( - v2s->rect.xmin + xofs, v2s->rect.ymin + yofs, 0.0f, v2s->str, BLF_DRAW_STR_DUMMY_MAX); - BLF_disable(font_id, BLF_CLIPPING); - } + BLF_enable(font_id, BLF_CLIPPING); + BLF_clipping( + font_id, v2s->rect.xmin - 4, v2s->rect.ymin - 4, v2s->rect.xmax + 4, v2s->rect.ymax + 4); + BLF_draw_default( + v2s->rect.xmin + xofs, v2s->rect.ymin + yofs, 0.0f, v2s->str, BLF_DRAW_STR_DUMMY_MAX); + BLF_disable(font_id, BLF_CLIPPING); } g_v2d_strings = nullptr; diff --git a/source/blender/editors/io/CMakeLists.txt b/source/blender/editors/io/CMakeLists.txt index 418a399db28..a716c00d5d9 100644 --- a/source/blender/editors/io/CMakeLists.txt +++ b/source/blender/editors/io/CMakeLists.txt @@ -9,9 +9,11 @@ set(INC ../../depsgraph ../../io/alembic ../../io/collada + ../../io/common ../../io/gpencil ../../io/usd ../../io/wavefront_obj + ../../io/stl ../../makesdna ../../makesrna ../../windowmanager @@ -32,6 +34,7 @@ set(SRC io_obj.c io_ops.c io_usd.c + io_stl_ops.c io_alembic.h io_cache.h @@ -40,12 +43,12 @@ set(SRC io_obj.h io_ops.h io_usd.h + io_stl_ops.h ) set(LIB bf_blenkernel bf_blenlib - bf_wavefront_obj ) if(WITH_OPENCOLLADA) @@ -55,6 +58,27 @@ if(WITH_OPENCOLLADA) add_definitions(-DWITH_COLLADA) endif() +if(WITH_IO_WAVEFRONT_OBJ) + list(APPEND LIB + bf_wavefront_obj + ) + add_definitions(-DWITH_IO_WAVEFRONT_OBJ) +endif() + +if(WITH_IO_STL) + list(APPEND LIB + bf_stl + ) + add_definitions(-DWITH_IO_STL) +endif() + +if(WITH_IO_GPENCIL) + list(APPEND LIB + bf_gpencil + ) + add_definitions(-DWITH_IO_GPENCIL) +endif() + if(WITH_ALEMBIC) list(APPEND LIB bf_alembic @@ -77,6 +101,4 @@ if(WITH_HARU) add_definitions(-DWITH_HARU) endif() -list(APPEND LIB bf_gpencil) - blender_add_lib(bf_editor_io "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/io/io_alembic.c b/source/blender/editors/io/io_alembic.c index 87923d9fdf8..0e8e0f83597 100644 --- a/source/blender/editors/io/io_alembic.c +++ b/source/blender/editors/io/io_alembic.c @@ -282,7 +282,7 @@ void WM_OT_alembic_export(wmOperatorType *ot) ot->poll = WM_operator_winactive; ot->ui = wm_alembic_export_draw; ot->check = wm_alembic_export_check; - ot->flag |= OPTYPE_PRESET; + ot->flag = OPTYPE_PRESET; WM_operator_properties_filesel(ot, FILE_TYPE_FOLDER | FILE_TYPE_ALEMBIC, diff --git a/source/blender/editors/io/io_cache.c b/source/blender/editors/io/io_cache.c index 624b85358f5..969049313c9 100644 --- a/source/blender/editors/io/io_cache.c +++ b/source/blender/editors/io/io_cache.c @@ -75,17 +75,17 @@ static void open_cancel(bContext *UNUSED(C), wmOperator *op) static int cachefile_open_exec(bContext *C, wmOperator *op) { if (!RNA_struct_property_is_set(op->ptr, "filepath")) { - BKE_report(op->reports, RPT_ERROR, "No filename given"); + BKE_report(op->reports, RPT_ERROR, "No filepath given"); return OPERATOR_CANCELLED; } - char filename[FILE_MAX]; - RNA_string_get(op->ptr, "filepath", filename); + char filepath[FILE_MAX]; + RNA_string_get(op->ptr, "filepath", filepath); Main *bmain = CTX_data_main(C); - CacheFile *cache_file = BKE_libblock_alloc(bmain, ID_CF, BLI_path_basename(filename), 0); - BLI_strncpy(cache_file->filepath, filename, FILE_MAX); + CacheFile *cache_file = BKE_libblock_alloc(bmain, ID_CF, BLI_path_basename(filepath), 0); + BLI_strncpy(cache_file->filepath, filepath, FILE_MAX); DEG_id_tag_update(&cache_file->id, ID_RECALC_COPY_ON_WRITE); /* Will be set when running invoke, not exec directly. */ @@ -182,7 +182,7 @@ static int cachefile_layer_open_invoke(bContext *C, wmOperator *op, const wmEven static int cachefile_layer_add_exec(bContext *C, wmOperator *op) { if (!RNA_struct_property_is_set(op->ptr, "filepath")) { - BKE_report(op->reports, RPT_ERROR, "No filename given"); + BKE_report(op->reports, RPT_ERROR, "No filepath given"); return OPERATOR_CANCELLED; } @@ -192,10 +192,10 @@ static int cachefile_layer_add_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - char filename[FILE_MAX]; - RNA_string_get(op->ptr, "filepath", filename); + char filepath[FILE_MAX]; + RNA_string_get(op->ptr, "filepath", filepath); - CacheFileLayer *layer = BKE_cachefile_add_layer(cache_file, filename); + CacheFileLayer *layer = BKE_cachefile_add_layer(cache_file, filepath); if (!layer) { WM_report(RPT_ERROR, "Could not add a layer to the cache file"); diff --git a/source/blender/editors/io/io_collada.c b/source/blender/editors/io/io_collada.c index dc62212bf53..c491e7a5815 100644 --- a/source/blender/editors/io/io_collada.c +++ b/source/blender/editors/io/io_collada.c @@ -468,7 +468,7 @@ void WM_OT_collada_export(wmOperatorType *ot) ot->poll = WM_operator_winactive; ot->check = wm_collada_export_check; - ot->flag |= OPTYPE_PRESET; + ot->flag = OPTYPE_PRESET; ot->ui = wm_collada_export_draw; @@ -786,7 +786,7 @@ void WM_OT_collada_import(wmOperatorType *ot) ot->exec = wm_collada_import_exec; ot->poll = WM_operator_winactive; - // ot->flag |= OPTYPE_PRESET; + // ot->flag = OPTYPE_PRESET; ot->ui = wm_collada_import_draw; diff --git a/source/blender/editors/io/io_gpencil_export.c b/source/blender/editors/io/io_gpencil_export.c index 7ac05fcca3e..6e5ae9f3cba 100644 --- a/source/blender/editors/io/io_gpencil_export.c +++ b/source/blender/editors/io/io_gpencil_export.c @@ -5,36 +5,38 @@ * \ingroup editor/io */ -#include "BLI_path_util.h" -#include "BLI_string.h" +#ifdef WITH_IO_GPENCIL -#include "DNA_gpencil_types.h" -#include "DNA_space_types.h" +# include "BLI_path_util.h" +# include "BLI_string.h" -#include "BKE_gpencil.h" -#include "BKE_main.h" -#include "BKE_report.h" -#include "BKE_screen.h" +# include "DNA_gpencil_types.h" +# include "DNA_space_types.h" -#include "BLT_translation.h" +# include "BKE_gpencil.h" +# include "BKE_main.h" +# include "BKE_report.h" +# include "BKE_screen.h" -#include "RNA_access.h" -#include "RNA_define.h" +# include "BLT_translation.h" -#include "UI_interface.h" -#include "UI_resources.h" +# include "RNA_access.h" +# include "RNA_define.h" -#include "WM_api.h" -#include "WM_types.h" +# include "UI_interface.h" +# include "UI_resources.h" -#include "DEG_depsgraph.h" -#include "DEG_depsgraph_query.h" +# include "WM_api.h" +# include "WM_types.h" -#include "io_gpencil.h" +# include "DEG_depsgraph.h" +# include "DEG_depsgraph_query.h" -#include "gpencil_io.h" +# include "io_gpencil.h" -#if defined(WITH_PUGIXML) || defined(WITH_HARU) +# include "gpencil_io.h" + +# if defined(WITH_PUGIXML) || defined(WITH_HARU) /* Definition of enum elements to export. */ /* Common props for exporting. */ static void gpencil_export_common_props_definition(wmOperatorType *ot) @@ -87,10 +89,10 @@ static void set_export_filepath(bContext *C, wmOperator *op, const char *extensi RNA_string_set(op->ptr, "filepath", filepath); } } -#endif +# endif /* <-------- SVG single frame export. --------> */ -#ifdef WITH_PUGIXML +# ifdef WITH_PUGIXML static bool wm_gpencil_export_svg_common_check(bContext *UNUSED(C), wmOperator *op) { char filepath[FILE_MAX]; @@ -241,10 +243,10 @@ void WM_OT_gpencil_export_svg(wmOperatorType *ot) "Clip Camera", "Clip drawings to camera size when export in camera view"); } -#endif +# endif /* <-------- PDF single frame export. --------> */ -#ifdef WITH_HARU +# ifdef WITH_HARU static bool wm_gpencil_export_pdf_common_check(bContext *UNUSED(C), wmOperator *op) { @@ -406,4 +408,6 @@ void WM_OT_gpencil_export_pdf(wmOperatorType *ot) "Frames", "Which frames to include in the export"); } -#endif +# endif /* WITH_HARU */ + +#endif /* WITH_IO_GPENCIL */ diff --git a/source/blender/editors/io/io_gpencil_import.c b/source/blender/editors/io/io_gpencil_import.c index 8bed32ad6c3..45f5441616f 100644 --- a/source/blender/editors/io/io_gpencil_import.c +++ b/source/blender/editors/io/io_gpencil_import.c @@ -5,34 +5,36 @@ * \ingroup editor/io */ -#include "BLI_path_util.h" +#ifdef WITH_IO_GPENCIL -#include "DNA_gpencil_types.h" -#include "DNA_space_types.h" +# include "BLI_path_util.h" -#include "BKE_context.h" -#include "BKE_gpencil.h" -#include "BKE_report.h" +# include "DNA_gpencil_types.h" +# include "DNA_space_types.h" -#include "BLT_translation.h" +# include "BKE_context.h" +# include "BKE_gpencil.h" +# include "BKE_report.h" -#include "RNA_access.h" -#include "RNA_define.h" +# include "BLT_translation.h" -#include "UI_interface.h" -#include "UI_resources.h" +# include "RNA_access.h" +# include "RNA_define.h" -#include "WM_api.h" -#include "WM_types.h" +# include "UI_interface.h" +# include "UI_resources.h" -#include "DEG_depsgraph.h" -#include "DEG_depsgraph_query.h" +# include "WM_api.h" +# include "WM_types.h" -#include "ED_gpencil.h" +# include "DEG_depsgraph.h" +# include "DEG_depsgraph_query.h" -#include "io_gpencil.h" +# include "ED_gpencil.h" -#include "gpencil_io.h" +# include "io_gpencil.h" + +# include "gpencil_io.h" /* <-------- SVG single frame import. --------> */ static bool wm_gpencil_import_svg_common_check(bContext *UNUSED(C), wmOperator *op) @@ -174,3 +176,5 @@ void WM_OT_gpencil_import_svg(wmOperatorType *ot) 0.001f, 100.0f); } + +#endif /* WITH_IO_GPENCIL */ diff --git a/source/blender/editors/io/io_gpencil_utils.c b/source/blender/editors/io/io_gpencil_utils.c index fa5fcd79b96..9a88daef1a1 100644 --- a/source/blender/editors/io/io_gpencil_utils.c +++ b/source/blender/editors/io/io_gpencil_utils.c @@ -5,14 +5,16 @@ * \ingroup editor/io */ -#include "DNA_space_types.h" +#ifdef WITH_IO_GPENCIL -#include "BKE_context.h" -#include "BKE_screen.h" +# include "DNA_space_types.h" -#include "WM_api.h" +# include "BKE_context.h" +# include "BKE_screen.h" -#include "io_gpencil.h" +# include "WM_api.h" + +# include "io_gpencil.h" ARegion *get_invoke_region(bContext *C) { @@ -46,3 +48,5 @@ View3D *get_invoke_view3d(bContext *C) return NULL; } + +#endif /* WITH_IO_GPENCIL */ diff --git a/source/blender/editors/io/io_obj.c b/source/blender/editors/io/io_obj.c index 3345d422bd1..53fa4788d0e 100644 --- a/source/blender/editors/io/io_obj.c +++ b/source/blender/editors/io/io_obj.c @@ -4,51 +4,39 @@ * \ingroup editor/io */ -#include "DNA_space_types.h" +#ifdef WITH_IO_WAVEFRONT_OBJ -#include "BKE_context.h" -#include "BKE_main.h" -#include "BKE_report.h" +# include "DNA_space_types.h" -#include "BLI_path_util.h" -#include "BLI_string.h" -#include "BLI_utildefines.h" +# include "BKE_context.h" +# include "BKE_main.h" +# include "BKE_report.h" -#include "BLT_translation.h" +# include "BLI_path_util.h" +# include "BLI_string.h" +# include "BLI_utildefines.h" -#include "MEM_guardedalloc.h" +# include "BLT_translation.h" -#include "RNA_access.h" -#include "RNA_define.h" +# include "ED_outliner.h" -#include "UI_interface.h" -#include "UI_resources.h" +# include "MEM_guardedalloc.h" -#include "WM_api.h" -#include "WM_types.h" +# include "RNA_access.h" +# include "RNA_define.h" -#include "DEG_depsgraph.h" +# include "UI_interface.h" +# include "UI_resources.h" -#include "IO_wavefront_obj.h" -#include "io_obj.h" +# include "WM_api.h" +# include "WM_types.h" -static const EnumPropertyItem io_obj_transform_axis_forward[] = { - {OBJ_AXIS_X_FORWARD, "X_FORWARD", 0, "X", "Positive X axis"}, - {OBJ_AXIS_Y_FORWARD, "Y_FORWARD", 0, "Y", "Positive Y axis"}, - {OBJ_AXIS_Z_FORWARD, "Z_FORWARD", 0, "Z", "Positive Z axis"}, - {OBJ_AXIS_NEGATIVE_X_FORWARD, "NEGATIVE_X_FORWARD", 0, "-X", "Negative X axis"}, - {OBJ_AXIS_NEGATIVE_Y_FORWARD, "NEGATIVE_Y_FORWARD", 0, "-Y", "Negative Y axis"}, - {OBJ_AXIS_NEGATIVE_Z_FORWARD, "NEGATIVE_Z_FORWARD", 0, "-Z", "Negative Z axis"}, - {0, NULL, 0, NULL, NULL}}; +# include "DEG_depsgraph.h" -static const EnumPropertyItem io_obj_transform_axis_up[] = { - {OBJ_AXIS_X_UP, "X_UP", 0, "X", "Positive X axis"}, - {OBJ_AXIS_Y_UP, "Y_UP", 0, "Y", "Positive Y axis"}, - {OBJ_AXIS_Z_UP, "Z_UP", 0, "Z", "Positive Z axis"}, - {OBJ_AXIS_NEGATIVE_X_UP, "NEGATIVE_X_UP", 0, "-X", "Negative X axis"}, - {OBJ_AXIS_NEGATIVE_Y_UP, "NEGATIVE_Y_UP", 0, "-Y", "Negative Y axis"}, - {OBJ_AXIS_NEGATIVE_Z_UP, "NEGATIVE_Z_UP", 0, "-Z", "Negative Z axis"}, - {0, NULL, 0, NULL, NULL}}; +# include "IO_orientation.h" +# include "IO_path_util_types.h" +# include "IO_wavefront_obj.h" +# include "io_obj.h" static const EnumPropertyItem io_obj_export_evaluation_mode[] = { {DAG_EVAL_RENDER, "DAG_EVAL_RENDER", 0, "Render", "Export objects as they appear in render"}, @@ -59,6 +47,15 @@ static const EnumPropertyItem io_obj_export_evaluation_mode[] = { "Export objects as they appear in the viewport"}, {0, NULL, 0, NULL, NULL}}; +static const EnumPropertyItem io_obj_path_mode[] = { + {PATH_REFERENCE_AUTO, "AUTO", 0, "Auto", "Use Relative paths with subdirectories only"}, + {PATH_REFERENCE_ABSOLUTE, "ABSOLUTE", 0, "Absolute", "Always write absolute paths"}, + {PATH_REFERENCE_RELATIVE, "RELATIVE", 0, "Relative", "Write relative paths where possible"}, + {PATH_REFERENCE_MATCH, "MATCH", 0, "Match", "Match Absolute/Relative setting with input path"}, + {PATH_REFERENCE_STRIP, "STRIP", 0, "Strip", "Write filename only"}, + {PATH_REFERENCE_COPY, "COPY", 0, "Copy", "Copy the file to the destination path"}, + {0, NULL, 0, NULL, NULL}}; + static int wm_obj_export_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { if (!RNA_struct_property_is_set(op->ptr, "filepath")) { @@ -87,6 +84,7 @@ static int wm_obj_export_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } struct OBJExportParams export_params; + export_params.file_base_for_tests[0] = '\0'; RNA_string_get(op->ptr, "filepath", export_params.filepath); export_params.blen_filepath = CTX_data_main(C)->filepath; export_params.export_animation = RNA_boolean_get(op->ptr, "export_animation"); @@ -102,7 +100,9 @@ static int wm_obj_export_exec(bContext *C, wmOperator *op) export_params.export_selected_objects = RNA_boolean_get(op->ptr, "export_selected_objects"); export_params.export_uv = RNA_boolean_get(op->ptr, "export_uv"); export_params.export_normals = RNA_boolean_get(op->ptr, "export_normals"); + export_params.export_colors = RNA_boolean_get(op->ptr, "export_colors"); export_params.export_materials = RNA_boolean_get(op->ptr, "export_materials"); + export_params.path_mode = RNA_enum_get(op->ptr, "path_mode"); export_params.export_triangulated_mesh = RNA_boolean_get(op->ptr, "export_triangulated_mesh"); export_params.export_curves_as_nurbs = RNA_boolean_get(op->ptr, "export_curves_as_nurbs"); @@ -119,9 +119,9 @@ static int wm_obj_export_exec(bContext *C, wmOperator *op) static void ui_obj_export_settings(uiLayout *layout, PointerRNA *imfptr) { - const bool export_animation = RNA_boolean_get(imfptr, "export_animation"); const bool export_smooth_groups = RNA_boolean_get(imfptr, "export_smooth_groups"); + const bool export_materials = RNA_boolean_get(imfptr, "export_materials"); uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); @@ -150,6 +150,9 @@ static void ui_obj_export_settings(uiLayout *layout, PointerRNA *imfptr) uiItemR(sub, imfptr, "export_selected_objects", 0, IFACE_("Selected Only"), ICON_NONE); uiItemR(sub, imfptr, "apply_modifiers", 0, IFACE_("Apply Modifiers"), ICON_NONE); uiItemR(sub, imfptr, "export_eval_mode", 0, IFACE_("Properties"), ICON_NONE); + sub = uiLayoutColumn(sub, false); + uiLayoutSetEnabled(sub, export_materials); + uiItemR(sub, imfptr, "path_mode", 0, IFACE_("Path Mode"), ICON_NONE); /* Options for what to write. */ box = uiLayoutBox(layout); @@ -158,10 +161,12 @@ static void ui_obj_export_settings(uiLayout *layout, PointerRNA *imfptr) sub = uiLayoutColumnWithHeading(col, false, IFACE_("Export")); uiItemR(sub, imfptr, "export_uv", 0, IFACE_("UV Coordinates"), ICON_NONE); uiItemR(sub, imfptr, "export_normals", 0, IFACE_("Normals"), ICON_NONE); + uiItemR(sub, imfptr, "export_colors", 0, IFACE_("Colors"), ICON_NONE); uiItemR(sub, imfptr, "export_materials", 0, IFACE_("Materials"), ICON_NONE); uiItemR(sub, imfptr, "export_triangulated_mesh", 0, IFACE_("Triangulated Mesh"), ICON_NONE); uiItemR(sub, imfptr, "export_curves_as_nurbs", 0, IFACE_("Curves as NURBS"), ICON_NONE); + /* Grouping options. */ box = uiLayoutBox(layout); uiItemL(box, IFACE_("Grouping"), ICON_GROUP); col = uiLayoutColumn(box, false); @@ -231,6 +236,8 @@ static bool wm_obj_export_check(bContext *C, wmOperator *op) void WM_OT_obj_export(struct wmOperatorType *ot) { + PropertyRNA *prop; + ot->name = "Export Wavefront OBJ"; ot->description = "Save the scene to a Wavefront OBJ file"; ot->idname = "WM_OT_obj_export"; @@ -241,10 +248,10 @@ void WM_OT_obj_export(struct wmOperatorType *ot) ot->ui = wm_obj_export_draw; ot->check = wm_obj_export_check; - ot->flag |= OPTYPE_PRESET; + ot->flag = OPTYPE_PRESET; WM_operator_properties_filesel(ot, - FILE_TYPE_FOLDER | FILE_TYPE_OBJECT_IO, + FILE_TYPE_FOLDER, FILE_BLENDER, FILE_SAVE, WM_FILESEL_FILEPATH | WM_FILESEL_SHOW_PROPS, @@ -276,13 +283,9 @@ void WM_OT_obj_export(struct wmOperatorType *ot) INT_MIN, INT_MAX); /* Object transform options. */ - RNA_def_enum(ot->srna, - "forward_axis", - io_obj_transform_axis_forward, - OBJ_AXIS_NEGATIVE_Z_FORWARD, - "Forward Axis", - ""); - RNA_def_enum(ot->srna, "up_axis", io_obj_transform_axis_up, OBJ_AXIS_Y_UP, "Up Axis", ""); + RNA_def_enum( + ot->srna, "forward_axis", io_transform_axis, IO_AXIS_NEGATIVE_Z, "Forward Axis", ""); + RNA_def_enum(ot->srna, "up_axis", io_transform_axis, IO_AXIS_Y, "Up Axis", ""); RNA_def_float(ot->srna, "scaling_factor", 1.0f, @@ -314,12 +317,19 @@ void WM_OT_obj_export(struct wmOperatorType *ot) "Export Normals", "Export per-face normals if the face is flat-shaded, per-face-per-loop " "normals if smooth-shaded"); + RNA_def_boolean(ot->srna, "export_colors", false, "Export Colors", "Export per-vertex colors"); RNA_def_boolean(ot->srna, "export_materials", true, "Export Materials", "Export MTL library. There must be a Principled-BSDF node for image textures to " "be exported to the MTL file"); + RNA_def_enum(ot->srna, + "path_mode", + io_obj_path_mode, + PATH_REFERENCE_AUTO, + "Path Mode", + "Method used to reference paths"); RNA_def_boolean(ot->srna, "export_triangulated_mesh", false, @@ -358,6 +368,10 @@ void WM_OT_obj_export(struct wmOperatorType *ot) "Every smooth-shaded face is assigned group \"1\" and every flat-shaded face \"off\""); RNA_def_boolean( ot->srna, "smooth_group_bitflags", false, "Generate Bitflags for Smooth Groups", ""); + + /* Only show .obj or .mtl files by default. */ + prop = RNA_def_string(ot->srna, "filter_glob", "*.obj;*.mtl", 0, "Extension Filter", ""); + RNA_def_property_flag(prop, PROP_HIDDEN); } static int wm_obj_import_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) @@ -378,10 +392,17 @@ static int wm_obj_import_exec(bContext *C, wmOperator *op) import_params.clamp_size = RNA_float_get(op->ptr, "clamp_size"); import_params.forward_axis = RNA_enum_get(op->ptr, "forward_axis"); import_params.up_axis = RNA_enum_get(op->ptr, "up_axis"); + import_params.import_vertex_groups = RNA_boolean_get(op->ptr, "import_vertex_groups"); import_params.validate_meshes = RNA_boolean_get(op->ptr, "validate_meshes"); OBJ_import(C, &import_params); + Scene *scene = CTX_data_scene(C); + WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); + WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); + ED_outliner_select_sync_from_object_tag(C); + return OPERATOR_FINISHED; } @@ -402,6 +423,7 @@ static void ui_obj_import_settings(uiLayout *layout, PointerRNA *imfptr) box = uiLayoutBox(layout); uiItemL(box, IFACE_("Options"), ICON_EXPORT); col = uiLayoutColumn(box, false); + uiItemR(col, imfptr, "import_vertex_groups", 0, NULL, ICON_NONE); uiItemR(col, imfptr, "validate_meshes", 0, NULL, ICON_NONE); } @@ -415,9 +437,12 @@ static void wm_obj_import_draw(bContext *C, wmOperator *op) void WM_OT_obj_import(struct wmOperatorType *ot) { + PropertyRNA *prop; + ot->name = "Import Wavefront OBJ"; ot->description = "Load a Wavefront OBJ scene"; ot->idname = "WM_OT_obj_import"; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; ot->invoke = wm_obj_import_invoke; ot->exec = wm_obj_import_exec; @@ -425,7 +450,7 @@ void WM_OT_obj_import(struct wmOperatorType *ot) ot->ui = wm_obj_import_draw; WM_operator_properties_filesel(ot, - FILE_TYPE_FOLDER | FILE_TYPE_OBJECT_IO, + FILE_TYPE_FOLDER, FILE_BLENDER, FILE_OPENFILE, WM_FILESEL_FILEPATH | WM_FILESEL_SHOW_PROPS, @@ -441,16 +466,23 @@ void WM_OT_obj_import(struct wmOperatorType *ot) "Resize the objects to keep bounding box under this value. Value 0 disables clamping", 0.0f, 1000.0f); - RNA_def_enum(ot->srna, - "forward_axis", - io_obj_transform_axis_forward, - OBJ_AXIS_NEGATIVE_Z_FORWARD, - "Forward Axis", - ""); - RNA_def_enum(ot->srna, "up_axis", io_obj_transform_axis_up, OBJ_AXIS_Y_UP, "Up Axis", ""); + RNA_def_enum( + ot->srna, "forward_axis", io_transform_axis, IO_AXIS_NEGATIVE_Z, "Forward Axis", ""); + RNA_def_enum(ot->srna, "up_axis", io_transform_axis, IO_AXIS_Y, "Up Axis", ""); + RNA_def_boolean(ot->srna, + "import_vertex_groups", + false, + "Vertex Groups", + "Import OBJ groups as vertex groups"); RNA_def_boolean(ot->srna, "validate_meshes", false, "Validate Meshes", "Check imported mesh objects for invalid data (slow)"); + + /* Only show .obj or .mtl files by default. */ + prop = RNA_def_string(ot->srna, "filter_glob", "*.obj;*.mtl", 0, "Extension Filter", ""); + RNA_def_property_flag(prop, PROP_HIDDEN); } + +#endif /* WITH_IO_WAVEFRONT_OBJ */ diff --git a/source/blender/editors/io/io_ops.c b/source/blender/editors/io/io_ops.c index 094f89d1540..0340d0598d5 100644 --- a/source/blender/editors/io/io_ops.c +++ b/source/blender/editors/io/io_ops.c @@ -24,6 +24,7 @@ #include "io_cache.h" #include "io_gpencil.h" #include "io_obj.h" +#include "io_stl_ops.h" void ED_operatortypes_io(void) { @@ -41,14 +42,14 @@ void ED_operatortypes_io(void) WM_operatortype_append(WM_OT_usd_export); #endif +#ifdef WITH_IO_GPENCIL WM_operatortype_append(WM_OT_gpencil_import_svg); - -#ifdef WITH_PUGIXML +# ifdef WITH_PUGIXML WM_operatortype_append(WM_OT_gpencil_export_svg); -#endif - -#ifdef WITH_HARU +# endif +# ifdef WITH_HARU WM_operatortype_append(WM_OT_gpencil_export_pdf); +# endif #endif WM_operatortype_append(CACHEFILE_OT_open); @@ -58,6 +59,12 @@ void ED_operatortypes_io(void) WM_operatortype_append(CACHEFILE_OT_layer_remove); WM_operatortype_append(CACHEFILE_OT_layer_move); +#ifdef WITH_IO_WAVEFRONT_OBJ WM_operatortype_append(WM_OT_obj_export); WM_operatortype_append(WM_OT_obj_import); +#endif + +#ifdef WITH_IO_STL + WM_operatortype_append(WM_OT_stl_import); +#endif } diff --git a/source/blender/editors/io/io_stl_ops.c b/source/blender/editors/io/io_stl_ops.c new file mode 100644 index 00000000000..7db32cd6f18 --- /dev/null +++ b/source/blender/editors/io/io_stl_ops.c @@ -0,0 +1,133 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup editor/io + */ + +#ifdef WITH_IO_STL + +# include "BKE_context.h" +# include "BKE_report.h" + +# include "WM_api.h" +# include "WM_types.h" + +# include "DNA_space_types.h" + +# include "ED_outliner.h" + +# include "RNA_access.h" +# include "RNA_define.h" + +# include "IO_stl.h" +# include "io_stl_ops.h" + +static int wm_stl_import_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + return WM_operator_filesel(C, op, event); +} + +static int wm_stl_import_execute(bContext *C, wmOperator *op) +{ + struct STLImportParams params; + params.forward_axis = RNA_enum_get(op->ptr, "forward_axis"); + params.up_axis = RNA_enum_get(op->ptr, "up_axis"); + params.use_facet_normal = RNA_boolean_get(op->ptr, "use_facet_normal"); + params.use_scene_unit = RNA_boolean_get(op->ptr, "use_scene_unit"); + params.global_scale = RNA_float_get(op->ptr, "global_scale"); + params.use_mesh_validate = RNA_boolean_get(op->ptr, "use_mesh_validate"); + + int files_len = RNA_collection_length(op->ptr, "files"); + + if (files_len) { + PointerRNA fileptr; + PropertyRNA *prop; + char dir_only[FILE_MAX], file_only[FILE_MAX]; + + RNA_string_get(op->ptr, "directory", dir_only); + prop = RNA_struct_find_property(op->ptr, "files"); + for (int i = 0; i < files_len; i++) { + RNA_property_collection_lookup_int(op->ptr, prop, i, &fileptr); + RNA_string_get(&fileptr, "name", file_only); + BLI_join_dirfile(params.filepath, sizeof(params.filepath), dir_only, file_only); + STL_import(C, ¶ms); + } + } + else if (RNA_struct_property_is_set(op->ptr, "filepath")) { + RNA_string_get(op->ptr, "filepath", params.filepath); + STL_import(C, ¶ms); + } + else { + BKE_report(op->reports, RPT_ERROR, "No filename given"); + return OPERATOR_CANCELLED; + } + + Scene *scene = CTX_data_scene(C); + WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); + WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); + ED_outliner_select_sync_from_object_tag(C); + + return OPERATOR_FINISHED; +} + +static bool wm_stl_import_check(bContext *UNUSED(C), wmOperator *op) +{ + const int num_axes = 3; + /* Both forward and up axes cannot be the same (or same except opposite sign). */ + if (RNA_enum_get(op->ptr, "forward_axis") % num_axes == + (RNA_enum_get(op->ptr, "up_axis") % num_axes)) { + RNA_enum_set(op->ptr, "up_axis", RNA_enum_get(op->ptr, "up_axis") % num_axes + 1); + return true; + } + return false; +} + +void WM_OT_stl_import(struct wmOperatorType *ot) +{ + PropertyRNA *prop; + + ot->name = "Import STL"; + ot->description = "Import an STL file as an object"; + ot->idname = "WM_OT_stl_import"; + + ot->invoke = wm_stl_import_invoke; + ot->exec = wm_stl_import_execute; + ot->poll = WM_operator_winactive; + ot->check = wm_stl_import_check; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + WM_operator_properties_filesel(ot, + FILE_TYPE_FOLDER, + FILE_BLENDER, + FILE_OPENFILE, + WM_FILESEL_FILEPATH | WM_FILESEL_FILES | WM_FILESEL_DIRECTORY | + WM_FILESEL_SHOW_PROPS, + FILE_DEFAULTDISPLAY, + FILE_SORT_ALPHA); + + RNA_def_float(ot->srna, "global_scale", 1.0f, 1e-6f, 1e6f, "Scale", "", 0.001f, 1000.0f); + RNA_def_boolean(ot->srna, + "use_scene_unit", + false, + "Scene Unit", + "Apply current scene's unit (as defined by unit scale) to imported data"); + RNA_def_boolean(ot->srna, + "use_facet_normal", + false, + "Facet Normals", + "Use (import) facet normals (note that this will still give flat shading)"); + RNA_def_enum(ot->srna, "forward_axis", io_transform_axis, IO_AXIS_Y, "Forward Axis", ""); + RNA_def_enum(ot->srna, "up_axis", io_transform_axis, IO_AXIS_Z, "Up Axis", ""); + RNA_def_boolean(ot->srna, + "use_mesh_validate", + false, + "Validate Mesh", + "Validate and correct imported mesh (slow)"); + + /* Only show .stl files by default. */ + prop = RNA_def_string(ot->srna, "filter_glob", "*.stl", 0, "Extension Filter", ""); + RNA_def_property_flag(prop, PROP_HIDDEN); +} + +#endif /* WITH_IO_STL */ diff --git a/source/blender/editors/io/io_stl_ops.h b/source/blender/editors/io/io_stl_ops.h new file mode 100644 index 00000000000..8f548f75985 --- /dev/null +++ b/source/blender/editors/io/io_stl_ops.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup editor/io + */ + +#pragma once + +struct wmOperatorType; + +void WM_OT_stl_export(struct wmOperatorType *ot); +void WM_OT_stl_import(struct wmOperatorType *ot); diff --git a/source/blender/editors/io/io_usd.c b/source/blender/editors/io/io_usd.c index e0616a0cec3..a59cdf60243 100644 --- a/source/blender/editors/io/io_usd.c +++ b/source/blender/editors/io/io_usd.c @@ -57,6 +57,20 @@ const EnumPropertyItem rna_enum_usd_export_evaluation_mode_items[] = { {0, NULL, 0, NULL, NULL}, }; +const EnumPropertyItem rna_enum_usd_mtl_name_collision_mode_items[] = { + {USD_MTL_NAME_COLLISION_MAKE_UNIQUE, + "MAKE_UNIQUE", + 0, + "Make Unique", + "Import each USD material as a unique Blender material"}, + {USD_MTL_NAME_COLLISION_REFERENCE_EXISTING, + "REFERENCE_EXISTING", + 0, + "Reference Existing", + "If a material with the same name already exists, reference that instead of importing"}, + {0, NULL, 0, NULL, NULL}, +}; + /* Stored in the wmOperator's customdata field to indicate it should run as a background job. * This is set when the operator is invoked, and not set when it is only executed. */ enum { AS_BACKGROUND_JOB = 1 }; @@ -118,7 +132,7 @@ static int wm_usd_export_exec(bContext *C, wmOperator *op) const bool generate_preview_surface = RNA_boolean_get(op->ptr, "generate_preview_surface"); const bool export_textures = RNA_boolean_get(op->ptr, "export_textures"); const bool overwrite_textures = RNA_boolean_get(op->ptr, "overwrite_textures"); - const bool relative_texture_paths = RNA_boolean_get(op->ptr, "relative_texture_paths"); + const bool relative_paths = RNA_boolean_get(op->ptr, "relative_paths"); struct USDExportParams params = { export_animation, @@ -133,7 +147,7 @@ static int wm_usd_export_exec(bContext *C, wmOperator *op) generate_preview_surface, export_textures, overwrite_textures, - relative_texture_paths, + relative_paths, }; bool ok = USD_export(C, filename, ¶ms, as_background_job); @@ -181,15 +195,29 @@ static void wm_usd_export_draw(bContext *UNUSED(C), wmOperator *op) const bool export_tex = RNA_boolean_get(ptr, "export_textures"); uiLayoutSetActive(row, export_mtl && preview && export_tex); - row = uiLayoutRow(col, true); - uiItemR(row, ptr, "relative_texture_paths", 0, NULL, ICON_NONE); - uiLayoutSetActive(row, export_mtl && preview); + box = uiLayoutBox(layout); + col = uiLayoutColumnWithHeading(box, true, IFACE_("File References")); + uiItemR(col, ptr, "relative_paths", 0, NULL, ICON_NONE); box = uiLayoutBox(layout); uiItemL(box, IFACE_("Experimental"), ICON_NONE); uiItemR(box, ptr, "use_instancing", 0, NULL, ICON_NONE); } +static bool wm_usd_export_check(bContext *UNUSED(C), wmOperator *op) +{ + char filepath[FILE_MAX]; + RNA_string_get(op->ptr, "filepath", filepath); + + if (!BLI_path_extension_check_n(filepath, ".usd", ".usda", ".usdc", NULL)) { + BLI_path_extension_ensure(filepath, FILE_MAX, ".usdc"); + RNA_string_set(op->ptr, "filepath", filepath); + return true; + } + + return false; +} + void WM_OT_usd_export(struct wmOperatorType *ot) { ot->name = "Export USD"; @@ -200,8 +228,9 @@ void WM_OT_usd_export(struct wmOperatorType *ot) ot->exec = wm_usd_export_exec; ot->poll = WM_operator_winactive; ot->ui = wm_usd_export_draw; + ot->check = wm_usd_export_check; - ot->flag = OPTYPE_REGISTER; /* No UNDO possible. */ + ot->flag = OPTYPE_REGISTER | OPTYPE_PRESET; /* No UNDO possible. */ WM_operator_properties_filesel(ot, FILE_TYPE_FOLDER | FILE_TYPE_USD, @@ -282,10 +311,11 @@ void WM_OT_usd_export(struct wmOperatorType *ot) "Allow overwriting existing texture files when exporting textures"); RNA_def_boolean(ot->srna, - "relative_texture_paths", + "relative_paths", true, - "Relative Texture Paths", - "Make texture asset paths relative to the USD file"); + "Relative Paths", + "Use relative paths to reference external files (i.e. textures, volumes) in " + "USD, otherwise use absolute paths"); } /* ====== USD Import ====== */ @@ -355,6 +385,9 @@ static int wm_usd_import_exec(bContext *C, wmOperator *op) const float light_intensity_scale = RNA_float_get(op->ptr, "light_intensity_scale"); + const eUSDMtlNameCollisionMode mtl_name_collision_mode = RNA_enum_get(op->ptr, + "mtl_name_collision_mode"); + /* TODO(makowalski): Add support for sequences. */ const bool is_sequence = false; int offset = 0; @@ -393,7 +426,8 @@ static int wm_usd_import_exec(bContext *C, wmOperator *op) .use_instancing = use_instancing, .import_usd_preview = import_usd_preview, .set_material_blend = set_material_blend, - .light_intensity_scale = light_intensity_scale}; + .light_intensity_scale = light_intensity_scale, + .mtl_name_collision_mode = mtl_name_collision_mode}; const bool ok = USD_import(C, filename, ¶ms, as_background_job); @@ -436,6 +470,7 @@ static void wm_usd_import_draw(bContext *UNUSED(C), wmOperator *op) uiItemR(col, ptr, "relative_path", 0, NULL, ICON_NONE); uiItemR(col, ptr, "create_collection", 0, NULL, ICON_NONE); uiItemR(box, ptr, "light_intensity_scale", 0, NULL, ICON_NONE); + uiItemR(box, ptr, "mtl_name_collision_mode", 0, NULL, ICON_NONE); box = uiLayoutBox(layout); col = uiLayoutColumnWithHeading(box, true, IFACE_("Experimental")); @@ -559,6 +594,14 @@ void WM_OT_usd_import(struct wmOperatorType *ot) "Scale for the intensity of imported lights", 0.0001f, 1000.0f); + + RNA_def_enum( + ot->srna, + "mtl_name_collision_mode", + rna_enum_usd_mtl_name_collision_mode_items, + USD_MTL_NAME_COLLISION_MAKE_UNIQUE, + "Material Name Collision", + "Behavior when the name of an imported material conflicts with an existing material"); } #endif /* WITH_USD */ diff --git a/source/blender/editors/mask/mask_add.c b/source/blender/editors/mask/mask_add.c index 0b9261eac4f..37c1815fca3 100644 --- a/source/blender/editors/mask/mask_add.c +++ b/source/blender/editors/mask/mask_add.c @@ -31,7 +31,9 @@ #include "mask_intern.h" /* own include */ -/******************** add vertex *********************/ +/* -------------------------------------------------------------------- */ +/** \name Add Vertex + * \{ */ static void setup_vertex_point(Mask *mask, MaskSpline *spline, @@ -160,7 +162,11 @@ static void setup_vertex_point(Mask *mask, ED_mask_select_flush_all(mask); } -/* **** add extrude vertex **** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Add Extrude Vertex + * \{ */ static void finSelectedSplinePoint(MaskLayer *mask_layer, MaskSpline **spline, @@ -206,7 +212,11 @@ static void finSelectedSplinePoint(MaskLayer *mask_layer, } } -/* **** add subdivide vertex **** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Add Subdivide Vertex + * \{ */ static void mask_spline_add_point_at_index(MaskSpline *spline, int point_index) { @@ -230,7 +240,7 @@ static bool add_vertex_subdivide(const bContext *C, Mask *mask, const float co[2 MaskLayer *mask_layer; MaskSpline *spline; MaskSplinePoint *point = NULL; - const float threshold = 9; + const float threshold = 12; float tangent[2]; float u; @@ -390,7 +400,7 @@ static bool add_vertex_new(const bContext *C, Mask *mask, MaskLayer *mask_layer, MaskSplinePoint *new_point = NULL, *ref_point = NULL; if (!mask_layer) { - /* if there's no mask layer currently operationg on, create new one */ + /* If there's no mask layer currently operating on, create new one. */ mask_layer = BKE_mask_layer_new(mask, ""); mask->masklay_act = mask->masklay_tot - 1; } @@ -492,6 +502,12 @@ static int add_vertex_handle_cyclic( return OPERATOR_PASS_THROUGH; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Add Vertex Operator + * \{ */ + static int add_vertex_exec(bContext *C, wmOperator *op) { MaskViewLockState lock_state; @@ -567,7 +583,7 @@ void MASK_OT_add_vertex(wmOperatorType *ot) /* api callbacks */ ot->exec = add_vertex_exec; ot->invoke = add_vertex_invoke; - ot->poll = ED_operator_mask; + ot->poll = ED_maskedit_visible_splines_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -585,7 +601,11 @@ void MASK_OT_add_vertex(wmOperatorType *ot) 1.0f); } -/******************** add feather vertex *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Add Feather Vertex Operator + * \{ */ static int add_feather_vertex_exec(bContext *C, wmOperator *op) { @@ -593,7 +613,7 @@ static int add_feather_vertex_exec(bContext *C, wmOperator *op) MaskLayer *mask_layer; MaskSpline *spline; MaskSplinePoint *point = NULL; - const float threshold = 9; + const float threshold = 12; float co[2], u; RNA_float_get_array(op->ptr, "location", co); @@ -677,7 +697,11 @@ void MASK_OT_add_feather_vertex(wmOperatorType *ot) 1.0f); } -/******************** common primitive functions *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Common Primitive Functions + * \{ */ static BezTriple *points_to_bezier(const float (*points)[2], const int num_points, @@ -812,7 +836,11 @@ static void define_primitive_add_properties(wmOperatorType *ot) FLT_MAX); } -/******************** primitive add circle *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Primitive Add Circle Operator + * \{ */ static int primitive_circle_add_exec(bContext *C, wmOperator *op) { @@ -834,7 +862,7 @@ void MASK_OT_primitive_circle_add(wmOperatorType *ot) /* api callbacks */ ot->exec = primitive_circle_add_exec; ot->invoke = primitive_add_invoke; - ot->poll = ED_operator_mask; + ot->poll = ED_maskedit_visible_splines_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -843,7 +871,11 @@ void MASK_OT_primitive_circle_add(wmOperatorType *ot) define_primitive_add_properties(ot); } -/******************** primitive add suqare *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Primitive Add Suqare Operator + * \{ */ static int primitive_square_add_exec(bContext *C, wmOperator *op) { @@ -865,7 +897,7 @@ void MASK_OT_primitive_square_add(wmOperatorType *ot) /* api callbacks */ ot->exec = primitive_square_add_exec; ot->invoke = primitive_add_invoke; - ot->poll = ED_operator_mask; + ot->poll = ED_maskedit_visible_splines_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -873,3 +905,5 @@ void MASK_OT_primitive_square_add(wmOperatorType *ot) /* properties */ define_primitive_add_properties(ot); } + +/** \} */ diff --git a/source/blender/editors/mask/mask_draw.c b/source/blender/editors/mask/mask_draw.c index aab4007854f..a18419ad422 100644 --- a/source/blender/editors/mask/mask_draw.c +++ b/source/blender/editors/mask/mask_draw.c @@ -663,6 +663,7 @@ void ED_mask_draw_region( const char draw_flag, const char draw_type, const eMaskOverlayMode overlay_mode, + const float blend_factor, /* convert directly into aspect corrected vars */ const int width_i, const int height_i, @@ -721,12 +722,14 @@ void ED_mask_draw_region( } if (draw_flag & MASK_DRAWFLAG_OVERLAY) { - const float red[4] = {1.0f, 0.0f, 0.0f, 0.0f}; + float buf_col[4] = {1.0f, 0.0f, 0.0f, 0.0f}; float *buffer = mask_rasterize(mask_eval, width, height); if (overlay_mode != MASK_OVERLAY_ALPHACHANNEL) { /* More blending types could be supported in the future. */ - GPU_blend(GPU_BLEND_MULTIPLY); + GPU_blend(GPU_BLEND_ALPHA); + buf_col[0] = -1.0f; + buf_col[3] = 1.0f; } GPU_matrix_push(); @@ -737,10 +740,18 @@ void ED_mask_draw_region( } IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR); GPU_shader_uniform_vector( - state.shader, GPU_shader_get_uniform(state.shader, "shuffle"), 4, 1, red); - immDrawPixelsTexTiled( - &state, 0.0f, 0.0f, width, height, GPU_R16F, false, buffer, 1.0f, 1.0f, NULL); + state.shader, GPU_shader_get_uniform(state.shader, "shuffle"), 4, 1, buf_col); + if (overlay_mode == MASK_OVERLAY_COMBINED) { + const float blend_col[4] = {0.0f, 0.0f, 0.0f, blend_factor}; + + immDrawPixelsTexTiled( + &state, 0.0f, 0.0f, width, height, GPU_R16F, false, buffer, 1.0f, 1.0f, blend_col); + } + else { + immDrawPixelsTexTiled( + &state, 0.0f, 0.0f, width, height, GPU_R16F, false, buffer, 1.0f, 1.0f, NULL); + } GPU_matrix_pop(); if (overlay_mode != MASK_OVERLAY_ALPHACHANNEL) { @@ -765,7 +776,9 @@ void ED_mask_draw_region( } /* draw! */ - draw_mask_layers(C, mask_eval, draw_flag, draw_type, width, height); + if (draw_flag & MASK_DRAWFLAG_SPLINE) { + draw_mask_layers(C, mask_eval, draw_flag, draw_type, width, height); + } if (do_draw_cb) { ED_region_draw_cb_draw(C, region, REGION_DRAW_POST_VIEW); diff --git a/source/blender/editors/mask/mask_edit.c b/source/blender/editors/mask/mask_edit.c index b2d49bcc642..915f90a1537 100644 --- a/source/blender/editors/mask/mask_edit.c +++ b/source/blender/editors/mask/mask_edit.c @@ -42,6 +42,22 @@ bool ED_maskedit_poll(bContext *C) return false; } +bool ED_maskedit_visible_splines_poll(bContext *C) +{ + ScrArea *area = CTX_wm_area(C); + if (area) { + switch (area->spacetype) { + case SPACE_CLIP: + return ED_space_clip_maskedit_visible_splines_poll(C); + case SPACE_SEQ: + return ED_space_sequencer_maskedit_poll(C); + case SPACE_IMAGE: + return ED_space_image_maskedit_visible_splines_poll(C); + } + } + return false; +} + bool ED_maskedit_mask_poll(bContext *C) { ScrArea *area = CTX_wm_area(C); @@ -58,6 +74,22 @@ bool ED_maskedit_mask_poll(bContext *C) return false; } +bool ED_maskedit_mask_visible_splines_poll(bContext *C) +{ + const ScrArea *area = CTX_wm_area(C); + if (area) { + switch (area->spacetype) { + case SPACE_CLIP: + return ED_space_clip_maskedit_mask_visible_splines_poll(C); + case SPACE_SEQ: + return ED_space_sequencer_maskedit_mask_poll(C); + case SPACE_IMAGE: + return ED_space_image_maskedit_mask_visible_splines_poll(C); + } + } + return false; +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/mask/mask_intern.h b/source/blender/editors/mask/mask_intern.h index c620d781c7f..2e99b45f215 100644 --- a/source/blender/editors/mask/mask_intern.h +++ b/source/blender/editors/mask/mask_intern.h @@ -86,9 +86,6 @@ void ED_mask_select_flush_all(struct Mask *mask); /* mask_editor.c */ -bool ED_maskedit_poll(struct bContext *C); -bool ED_maskedit_mask_poll(struct bContext *C); - /* Generalized solution for preserving editor viewport when making changes while lock-to-selection * is enabled. * Any mask operator can use this API, without worrying that some editors do not have an idea of diff --git a/source/blender/editors/mask/mask_ops.c b/source/blender/editors/mask/mask_ops.c index 3c0e7ee399c..14976d860f6 100644 --- a/source/blender/editors/mask/mask_ops.c +++ b/source/blender/editors/mask/mask_ops.c @@ -113,7 +113,7 @@ void MASK_OT_new(wmOperatorType *ot) /* api callbacks */ ot->exec = mask_new_exec; - ot->poll = ED_operator_mask; + ot->poll = ED_maskedit_poll; /* properties */ RNA_def_string(ot->srna, "name", NULL, MAX_ID_NAME - 2, "Name", "Name of new mask"); @@ -146,7 +146,7 @@ void MASK_OT_layer_new(wmOperatorType *ot) /* api callbacks */ ot->exec = mask_layer_new_exec; - ot->poll = ED_maskedit_poll; + ot->poll = ED_maskedit_mask_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -181,7 +181,7 @@ void MASK_OT_layer_remove(wmOperatorType *ot) /* api callbacks */ ot->exec = mask_layer_remove_exec; - ot->poll = ED_maskedit_poll; + ot->poll = ED_maskedit_mask_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -907,7 +907,7 @@ void MASK_OT_slide_point(wmOperatorType *ot) /* api callbacks */ ot->invoke = slide_point_invoke; ot->modal = slide_point_modal; - ot->poll = ED_operator_mask; + ot->poll = ED_maskedit_mask_visible_splines_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -1297,7 +1297,7 @@ void MASK_OT_slide_spline_curvature(wmOperatorType *ot) /* api callbacks */ ot->invoke = slide_spline_curvature_invoke; ot->modal = slide_spline_curvature_modal; - ot->poll = ED_operator_mask; + ot->poll = ED_maskedit_mask_visible_splines_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -1336,7 +1336,7 @@ void MASK_OT_cyclic_toggle(wmOperatorType *ot) /* api callbacks */ ot->exec = cyclic_toggle_exec; - ot->poll = ED_maskedit_mask_poll; + ot->poll = ED_maskedit_mask_visible_splines_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -1493,7 +1493,7 @@ void MASK_OT_delete(wmOperatorType *ot) /* api callbacks */ ot->invoke = WM_operator_confirm; ot->exec = delete_exec; - ot->poll = ED_maskedit_mask_poll; + ot->poll = ED_maskedit_mask_visible_splines_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -1551,7 +1551,7 @@ void MASK_OT_switch_direction(wmOperatorType *ot) /* api callbacks */ ot->exec = mask_switch_direction_exec; - ot->poll = ED_maskedit_mask_poll; + ot->poll = ED_maskedit_mask_visible_splines_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -1613,7 +1613,7 @@ void MASK_OT_normals_make_consistent(wmOperatorType *ot) /* api callbacks */ ot->exec = mask_normals_make_consistent_exec; - ot->poll = ED_maskedit_mask_poll; + ot->poll = ED_maskedit_mask_visible_splines_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -1693,7 +1693,7 @@ void MASK_OT_handle_type_set(wmOperatorType *ot) /* api callbacks */ ot->invoke = WM_menu_invoke; ot->exec = set_handle_type_exec; - ot->poll = ED_maskedit_mask_poll; + ot->poll = ED_maskedit_mask_visible_splines_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -1849,7 +1849,7 @@ void MASK_OT_feather_weight_clear(wmOperatorType *ot) /* api callbacks */ ot->exec = mask_feather_weight_clear_exec; - ot->poll = ED_maskedit_mask_poll; + ot->poll = ED_maskedit_mask_visible_splines_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -2043,7 +2043,7 @@ void MASK_OT_duplicate(wmOperatorType *ot) /* api callbacks */ ot->exec = mask_duplicate_exec; - ot->poll = ED_maskedit_mask_poll; + ot->poll = ED_maskedit_mask_visible_splines_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -2084,7 +2084,7 @@ void MASK_OT_copy_splines(wmOperatorType *ot) static bool paste_splines_poll(bContext *C) { - if (ED_maskedit_mask_poll(C)) { + if (ED_maskedit_mask_visible_splines_poll(C)) { return BKE_mask_clipboard_is_empty() == false; } diff --git a/source/blender/editors/mask/mask_query.c b/source/blender/editors/mask/mask_query.c index afe457a8502..02e1524e23e 100644 --- a/source/blender/editors/mask/mask_query.c +++ b/source/blender/editors/mask/mask_query.c @@ -45,6 +45,8 @@ bool ED_mask_find_nearest_diff_point(const bContext *C, float *r_u, float *r_score) { + const float threshold_sq = threshold * threshold; + ScrArea *area = CTX_wm_area(C); ARegion *region = CTX_wm_region(C); @@ -139,7 +141,7 @@ bool ED_mask_find_nearest_diff_point(const bContext *C, } } - if (point && dist_best_sq < threshold) { + if (point && dist_best_sq < threshold_sq) { if (r_mask_layer) { *r_mask_layer = point_mask_layer; } @@ -613,7 +615,7 @@ bool ED_mask_selected_minmax(const bContext *C, /* Use evaluated mask to take animation into account. * The animation of splies is not "flushed" back to original, so need to explicitly - * sue evaluated datablock here. */ + * use evaluated datablock here. */ Mask *mask_eval = (Mask *)DEG_get_evaluated_id(depsgraph, &mask->id); INIT_MINMAX2(min, max); diff --git a/source/blender/editors/mask/mask_relationships.c b/source/blender/editors/mask/mask_relationships.c index 9d8b84de66b..1f175ce51fc 100644 --- a/source/blender/editors/mask/mask_relationships.c +++ b/source/blender/editors/mask/mask_relationships.c @@ -21,6 +21,7 @@ #include "WM_types.h" #include "ED_clip.h" /* frame remapping functions */ +#include "ED_mask.h" #include "ED_screen.h" #include "mask_intern.h" /* own include */ @@ -61,7 +62,7 @@ void MASK_OT_parent_clear(wmOperatorType *ot) /* api callbacks */ ot->exec = mask_parent_clear_exec; - ot->poll = ED_maskedit_mask_poll; + ot->poll = ED_maskedit_mask_visible_splines_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; diff --git a/source/blender/editors/mask/mask_select.c b/source/blender/editors/mask/mask_select.c index 0bd054e1b89..4bd80208b9e 100644 --- a/source/blender/editors/mask/mask_select.c +++ b/source/blender/editors/mask/mask_select.c @@ -222,7 +222,7 @@ void MASK_OT_select_all(wmOperatorType *ot) /* api callbacks */ ot->exec = select_all_exec; - ot->poll = ED_maskedit_mask_poll; + ot->poll = ED_maskedit_mask_visible_splines_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -364,12 +364,9 @@ static int select_exec(bContext *C, wmOperator *op) return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED; } if (deselect_all) { - /* For clip editor tracks, leave deselect all to clip editor. */ - if (!ED_clip_can_select(C)) { - ED_mask_deselect_all(C); - ED_mask_view_lock_state_restore_no_jump(C, &lock_state); - return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED; - } + ED_mask_deselect_all(C); + ED_mask_view_lock_state_restore_no_jump(C, &lock_state); + return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED; } return OPERATOR_PASS_THROUGH; @@ -401,7 +398,8 @@ void MASK_OT_select(wmOperatorType *ot) /* api callbacks */ ot->exec = select_exec; ot->invoke = select_invoke; - ot->poll = ED_maskedit_mask_poll; + ot->poll = ED_maskedit_mask_visible_splines_poll; + ot->get_name = ED_select_pick_get_name; /* flags */ ot->flag = OPTYPE_UNDO; @@ -506,7 +504,7 @@ void MASK_OT_select_box(wmOperatorType *ot) ot->invoke = WM_gesture_box_invoke; ot->exec = box_select_exec; ot->modal = WM_gesture_box_modal; - ot->poll = ED_maskedit_mask_poll; + ot->poll = ED_maskedit_mask_visible_splines_poll; /* flags */ ot->flag = OPTYPE_UNDO; @@ -629,7 +627,7 @@ void MASK_OT_select_lasso(wmOperatorType *ot) ot->invoke = WM_gesture_lasso_invoke; ot->modal = WM_gesture_lasso_modal; ot->exec = clip_lasso_select_exec; - ot->poll = ED_maskedit_mask_poll; + ot->poll = ED_maskedit_mask_visible_splines_poll; ot->cancel = WM_gesture_lasso_cancel; /* flags */ @@ -747,7 +745,8 @@ void MASK_OT_select_circle(wmOperatorType *ot) ot->invoke = WM_gesture_circle_invoke; ot->modal = WM_gesture_circle_modal; ot->exec = circle_select_exec; - ot->poll = ED_maskedit_mask_poll; + ot->poll = ED_maskedit_mask_visible_splines_poll; + ot->get_name = ED_select_circle_get_name; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -810,7 +809,7 @@ void MASK_OT_select_linked_pick(wmOperatorType *ot) /* api callbacks */ ot->invoke = mask_select_linked_pick_invoke; - ot->poll = ED_maskedit_mask_poll; + ot->poll = ED_maskedit_mask_visible_splines_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -865,7 +864,7 @@ void MASK_OT_select_linked(wmOperatorType *ot) /* api callbacks */ ot->exec = mask_select_linked_exec; - ot->poll = ED_maskedit_mask_poll; + ot->poll = ED_maskedit_mask_visible_splines_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -964,7 +963,7 @@ void MASK_OT_select_more(wmOperatorType *ot) /* api callbacks */ ot->exec = mask_select_more_exec; - ot->poll = ED_maskedit_mask_poll; + ot->poll = ED_maskedit_mask_visible_splines_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -984,7 +983,7 @@ void MASK_OT_select_less(wmOperatorType *ot) /* api callbacks */ ot->exec = mask_select_less_exec; - ot->poll = ED_maskedit_mask_poll; + ot->poll = ED_maskedit_mask_visible_splines_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; diff --git a/source/blender/editors/mask/mask_shapekey.c b/source/blender/editors/mask/mask_shapekey.c index dd54d84a90b..55145f27012 100644 --- a/source/blender/editors/mask/mask_shapekey.c +++ b/source/blender/editors/mask/mask_shapekey.c @@ -67,7 +67,7 @@ void MASK_OT_shape_key_insert(wmOperatorType *ot) /* api callbacks */ ot->exec = mask_shape_key_insert_exec; - ot->poll = ED_maskedit_mask_poll; + ot->poll = ED_maskedit_mask_visible_splines_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -113,7 +113,7 @@ void MASK_OT_shape_key_clear(wmOperatorType *ot) /* api callbacks */ ot->exec = mask_shape_key_clear_exec; - ot->poll = ED_maskedit_mask_poll; + ot->poll = ED_maskedit_mask_visible_splines_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -197,7 +197,7 @@ void MASK_OT_shape_key_feather_reset(wmOperatorType *ot) /* api callbacks */ ot->exec = mask_shape_key_feather_reset_exec; - ot->poll = ED_maskedit_mask_poll; + ot->poll = ED_maskedit_mask_visible_splines_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -356,7 +356,7 @@ void MASK_OT_shape_key_rekey(wmOperatorType *ot) /* api callbacks */ ot->exec = mask_shape_key_rekey_exec; - ot->poll = ED_maskedit_mask_poll; + ot->poll = ED_maskedit_mask_visible_splines_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; diff --git a/source/blender/editors/mesh/CMakeLists.txt b/source/blender/editors/mesh/CMakeLists.txt index ed09e5a6334..28ac913a3e3 100644 --- a/source/blender/editors/mesh/CMakeLists.txt +++ b/source/blender/editors/mesh/CMakeLists.txt @@ -24,7 +24,7 @@ set(INC ) set(SRC - editface.c + editface.cc editmesh_add.c editmesh_add_gizmo.c editmesh_automerge.c @@ -51,10 +51,10 @@ set(SRC editmesh_tools.c editmesh_undo.c editmesh_utils.c - mesh_data.c + mesh_data.cc mesh_mirror.c mesh_ops.c - meshtools.c + meshtools.cc mesh_intern.h ) diff --git a/source/blender/editors/mesh/editface.c b/source/blender/editors/mesh/editface.cc index a5c6adaa43e..69fe69fe117 100644 --- a/source/blender/editors/mesh/editface.c +++ b/source/blender/editors/mesh/editface.cc @@ -36,16 +36,16 @@ /* own include */ -void paintface_flush_flags(struct bContext *C, Object *ob, short flag) +void paintface_flush_flags(bContext *C, Object *ob, short flag) { Mesh *me = BKE_mesh_from_object(ob); MPoly *polys, *mp_orig; - const int *index_array = NULL; + const int *index_array = nullptr; int totpoly; BLI_assert((flag & ~(SELECT | ME_HIDE)) == 0); - if (me == NULL) { + if (me == nullptr) { return; } @@ -60,7 +60,7 @@ void paintface_flush_flags(struct bContext *C, Object *ob, short flag) Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); - if (ob_eval == NULL) { + if (ob_eval == nullptr) { return; } @@ -68,7 +68,7 @@ void paintface_flush_flags(struct bContext *C, Object *ob, short flag) Mesh *me_eval = (Mesh *)ob_eval->runtime.data_eval; bool updated = false; - if (me_orig != NULL && me_eval != NULL && me_orig->totpoly == me->totpoly) { + if (me_orig != nullptr && me_eval != nullptr && me_orig->totpoly == me->totpoly) { /* Update the COW copy of the mesh. */ for (int i = 0; i < me->totpoly; i++) { me_orig->mpoly[i].flag = me->mpoly[i].flag; @@ -79,7 +79,7 @@ void paintface_flush_flags(struct bContext *C, Object *ob, short flag) updated = true; } /* Mesh polys => Final derived polys */ - else if ((index_array = CustomData_get_layer(&me_eval->pdata, CD_ORIGINDEX))) { + else if ((index_array = (const int *)CustomData_get_layer(&me_eval->pdata, CD_ORIGINDEX))) { polys = me_eval->mpoly; totpoly = me_eval->totpoly; @@ -104,10 +104,10 @@ void paintface_flush_flags(struct bContext *C, Object *ob, short flag) BKE_mesh_batch_cache_dirty_tag(me_eval, BKE_MESH_BATCH_DIRTY_SELECT_PAINT); } - DEG_id_tag_update(ob->data, ID_RECALC_SELECT); + DEG_id_tag_update(static_cast<ID *>(ob->data), ID_RECALC_SELECT); } else { - DEG_id_tag_update(ob->data, ID_RECALC_COPY_ON_WRITE | ID_RECALC_SELECT); + DEG_id_tag_update(static_cast<ID *>(ob->data), ID_RECALC_COPY_ON_WRITE | ID_RECALC_SELECT); } WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob->data); @@ -115,18 +115,13 @@ void paintface_flush_flags(struct bContext *C, Object *ob, short flag) void paintface_hide(bContext *C, Object *ob, const bool unselected) { - Mesh *me; - MPoly *mpoly; - int a; - - me = BKE_mesh_from_object(ob); - if (me == NULL || me->totpoly == 0) { + Mesh *me = BKE_mesh_from_object(ob); + if (me == nullptr || me->totpoly == 0) { return; } - mpoly = me->mpoly; - a = me->totpoly; - while (a--) { + for (int i = 0; i < me->totpoly; i++) { + MPoly *mpoly = &me->mpoly[i]; if ((mpoly->flag & ME_HIDE) == 0) { if (((mpoly->flag & ME_FACE_SEL) == 0) == unselected) { mpoly->flag |= ME_HIDE; @@ -136,8 +131,6 @@ void paintface_hide(bContext *C, Object *ob, const bool unselected) if (mpoly->flag & ME_HIDE) { mpoly->flag &= ~ME_FACE_SEL; } - - mpoly++; } BKE_mesh_flush_hidden_from_polys(me); @@ -147,23 +140,17 @@ void paintface_hide(bContext *C, Object *ob, const bool unselected) void paintface_reveal(bContext *C, Object *ob, const bool select) { - Mesh *me; - MPoly *mpoly; - int a; - - me = BKE_mesh_from_object(ob); - if (me == NULL || me->totpoly == 0) { + Mesh *me = BKE_mesh_from_object(ob); + if (me == nullptr || me->totpoly == 0) { return; } - mpoly = me->mpoly; - a = me->totpoly; - while (a--) { + for (int i = 0; i < me->totpoly; i++) { + MPoly *mpoly = &me->mpoly[i]; if (mpoly->flag & ME_HIDE) { SET_FLAG_FROM_TEST(mpoly->flag, select, ME_FACE_SEL); mpoly->flag &= ~ME_HIDE; } - mpoly++; } BKE_mesh_flush_hidden_from_polys(me); @@ -171,13 +158,10 @@ void paintface_reveal(bContext *C, Object *ob, const bool select) paintface_flush_flags(C, ob, SELECT | ME_HIDE); } -/* Set tface seams based on edge data, uses hash table to find seam edges. */ +/* Set object-mode face selection seams based on edge data, uses hash table to find seam edges. */ static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bool select) { - MPoly *mp; - MLoop *ml; - int a, b; bool do_it = true; bool mark = false; @@ -186,20 +170,20 @@ static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bo if (index != (uint)-1) { /* only put face under cursor in array */ - mp = &me->mpoly[index]; + MPoly *mp = &me->mpoly[index]; BKE_mesh_poly_edgebitmap_insert(edge_tag, mp, me->mloop + mp->loopstart); BLI_BITMAP_ENABLE(poly_tag, index); } else { /* fill array by selection */ - mp = me->mpoly; - for (a = 0; a < me->totpoly; a++, mp++) { + for (int i = 0; i < me->totpoly; i++) { + MPoly *mp = &me->mpoly[i]; if (mp->flag & ME_HIDE) { /* pass */ } else if (mp->flag & ME_FACE_SEL) { BKE_mesh_poly_edgebitmap_insert(edge_tag, mp, me->mloop + mp->loopstart); - BLI_BITMAP_ENABLE(poly_tag, a); + BLI_BITMAP_ENABLE(poly_tag, i); } } } @@ -208,17 +192,17 @@ static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bo do_it = false; /* expand selection */ - mp = me->mpoly; - for (a = 0; a < me->totpoly; a++, mp++) { + for (int i = 0; i < me->totpoly; i++) { + MPoly *mp = &me->mpoly[i]; if (mp->flag & ME_HIDE) { continue; } - if (!BLI_BITMAP_TEST(poly_tag, a)) { + if (!BLI_BITMAP_TEST(poly_tag, i)) { mark = false; - ml = me->mloop + mp->loopstart; - for (b = 0; b < mp->totloop; b++, ml++) { + MLoop *ml = me->mloop + mp->loopstart; + for (int b = 0; b < mp->totloop; b++, ml++) { if ((me->medge[ml->e].flag & ME_SEAM) == 0) { if (BLI_BITMAP_TEST(edge_tag, ml->e)) { mark = true; @@ -228,7 +212,7 @@ static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bo } if (mark) { - BLI_BITMAP_ENABLE(poly_tag, a); + BLI_BITMAP_ENABLE(poly_tag, i); BKE_mesh_poly_edgebitmap_insert(edge_tag, mp, me->mloop + mp->loopstart); do_it = true; } @@ -238,8 +222,9 @@ static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bo MEM_freeN(edge_tag); - for (a = 0, mp = me->mpoly; a < me->totpoly; a++, mp++) { - if (BLI_BITMAP_TEST(poly_tag, a)) { + for (int i = 0; i < me->totpoly; i++) { + MPoly *mp = &me->mpoly[i]; + if (BLI_BITMAP_TEST(poly_tag, i)) { SET_FLAG_FROM_TEST(mp->flag, select, ME_FACE_SEL); } } @@ -249,11 +234,10 @@ static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bo void paintface_select_linked(bContext *C, Object *ob, const int mval[2], const bool select) { - Mesh *me; uint index = (uint)-1; - me = BKE_mesh_from_object(ob); - if (me == NULL || me->totpoly == 0) { + Mesh *me = BKE_mesh_from_object(ob); + if (me == nullptr || me->totpoly == 0) { return; } @@ -270,34 +254,27 @@ void paintface_select_linked(bContext *C, Object *ob, const int mval[2], const b bool paintface_deselect_all_visible(bContext *C, Object *ob, int action, bool flush_flags) { - Mesh *me; - MPoly *mpoly; - int a; - - me = BKE_mesh_from_object(ob); - if (me == NULL) { + Mesh *me = BKE_mesh_from_object(ob); + if (me == nullptr) { return false; } if (action == SEL_TOGGLE) { action = SEL_SELECT; - mpoly = me->mpoly; - a = me->totpoly; - while (a--) { + for (int i = 0; i < me->totpoly; i++) { + MPoly *mpoly = &me->mpoly[i]; if ((mpoly->flag & ME_HIDE) == 0 && mpoly->flag & ME_FACE_SEL) { action = SEL_DESELECT; break; } - mpoly++; } } bool changed = false; - mpoly = me->mpoly; - a = me->totpoly; - while (a--) { + for (int i = 0; i < me->totpoly; i++) { + MPoly *mpoly = &me->mpoly[i]; if ((mpoly->flag & ME_HIDE) == 0) { switch (action) { case SEL_SELECT: @@ -318,7 +295,6 @@ bool paintface_deselect_all_visible(bContext *C, Object *ob, int action, bool fl break; } } - mpoly++; } if (changed) { @@ -331,30 +307,25 @@ bool paintface_deselect_all_visible(bContext *C, Object *ob, int action, bool fl bool paintface_minmax(Object *ob, float r_min[3], float r_max[3]) { - const Mesh *me; - const MPoly *mp; - const MLoop *ml; - const MVert *mvert; - int a, b; bool ok = false; float vec[3], bmat[3][3]; - me = BKE_mesh_from_object(ob); + const Mesh *me = BKE_mesh_from_object(ob); if (!me || !me->mloopuv) { return ok; } + const MVert *mvert = me->mvert; copy_m3_m4(bmat, ob->obmat); - mvert = me->mvert; - mp = me->mpoly; - for (a = me->totpoly; a > 0; a--, mp++) { + for (int i = 0; i < me->totpoly; i++) { + MPoly *mp = &me->mpoly[i]; if (mp->flag & ME_HIDE || !(mp->flag & ME_FACE_SEL)) { continue; } - ml = me->mloop + mp->loopstart; - for (b = 0; b < mp->totloop; b++, ml++) { + const MLoop *ml = me->mloop + mp->loopstart; + for (int b = 0; b < mp->totloop; b++, ml++) { mul_v3_m3v3(vec, bmat, mvert[ml->v].co); add_v3_v3v3(vec, vec, ob->obmat[3]); minmax_v3v3_v3(r_min, r_max, vec); @@ -366,19 +337,18 @@ bool paintface_minmax(Object *ob, float r_min[3], float r_max[3]) return ok; } -bool paintface_mouse_select(struct bContext *C, +bool paintface_mouse_select(bContext *C, const int mval[2], - const struct SelectPick_Params *params, + const SelectPick_Params *params, Object *ob) { - Mesh *me; - MPoly *mpoly_sel = NULL; + MPoly *mpoly_sel = nullptr; uint index; bool changed = false; bool found = false; /* Get the face under the cursor */ - me = BKE_mesh_from_object(ob); + Mesh *me = BKE_mesh_from_object(ob); if (ED_mesh_pick_face(C, ob, mval, ED_MESH_PICK_DEFAULT_FACE_DIST, &index)) { if (index < me->totpoly) { @@ -444,11 +414,11 @@ void paintvert_flush_flags(Object *ob) Mesh *me = BKE_mesh_from_object(ob); Mesh *me_eval = BKE_object_get_evaluated_mesh(ob); MVert *mvert_eval, *mv; - const int *index_array = NULL; + const int *index_array = nullptr; int totvert; int i; - if (me == NULL) { + if (me == nullptr) { return; } @@ -456,11 +426,11 @@ void paintvert_flush_flags(Object *ob) * since this could become slow for realtime updates (circle-select for eg) */ BKE_mesh_flush_select_from_verts(me); - if (me_eval == NULL) { + if (me_eval == nullptr) { return; } - index_array = CustomData_get_layer(&me_eval->vdata, CD_ORIGINDEX); + index_array = (const int *)CustomData_get_layer(&me_eval->vdata, CD_ORIGINDEX); mvert_eval = me_eval->mvert; totvert = me_eval->totvert; @@ -485,41 +455,34 @@ void paintvert_flush_flags(Object *ob) BKE_mesh_batch_cache_dirty_tag(me, BKE_MESH_BATCH_DIRTY_ALL); } -void paintvert_tag_select_update(struct bContext *C, struct Object *ob) +void paintvert_tag_select_update(bContext *C, Object *ob) { - DEG_id_tag_update(ob->data, ID_RECALC_COPY_ON_WRITE | ID_RECALC_SELECT); + DEG_id_tag_update(static_cast<ID *>(ob->data), ID_RECALC_COPY_ON_WRITE | ID_RECALC_SELECT); WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob->data); } bool paintvert_deselect_all_visible(Object *ob, int action, bool flush_flags) { - Mesh *me; - MVert *mvert; - int a; - - me = BKE_mesh_from_object(ob); - if (me == NULL) { + Mesh *me = BKE_mesh_from_object(ob); + if (me == nullptr) { return false; } if (action == SEL_TOGGLE) { action = SEL_SELECT; - mvert = me->mvert; - a = me->totvert; - while (a--) { + for (int i = 0; i < me->totvert; i++) { + MVert *mvert = &me->mvert[i]; if ((mvert->flag & ME_HIDE) == 0 && mvert->flag & SELECT) { action = SEL_DESELECT; break; } - mvert++; } } bool changed = false; - mvert = me->mvert; - a = me->totvert; - while (a--) { + for (int i = 0; i < me->totvert; i++) { + MVert *mvert = &me->mvert[i]; if ((mvert->flag & ME_HIDE) == 0) { switch (action) { case SEL_SELECT: @@ -540,7 +503,6 @@ bool paintvert_deselect_all_visible(Object *ob, int action, bool flush_flags) break; } } - mvert++; } if (changed) { @@ -565,11 +527,8 @@ bool paintvert_deselect_all_visible(Object *ob, int action, bool flush_flags) void paintvert_select_ungrouped(Object *ob, bool extend, bool flush_flags) { Mesh *me = BKE_mesh_from_object(ob); - MVert *mv; - MDeformVert *dv; - int a, tot; - if (me == NULL || me->dvert == NULL) { + if (me == nullptr || me->dvert == nullptr) { return; } @@ -577,12 +536,11 @@ void paintvert_select_ungrouped(Object *ob, bool extend, bool flush_flags) paintvert_deselect_all_visible(ob, SEL_DESELECT, false); } - dv = me->dvert; - tot = me->totvert; - - for (a = 0, mv = me->mvert; a < tot; a++, mv++, dv++) { + for (int i = 0; i < me->totvert; i++) { + MVert *mv = &me->mvert[i]; + MDeformVert *dv = &me->dvert[i]; if ((mv->flag & ME_HIDE) == 0) { - if (dv->dw == NULL) { + if (dv->dw == nullptr) { /* if null weight then not grouped */ mv->flag |= SELECT; } diff --git a/source/blender/editors/mesh/editmesh_add.c b/source/blender/editors/mesh/editmesh_add.c index b2f33428b21..57e0d04727c 100644 --- a/source/blender/editors/mesh/editmesh_add.c +++ b/source/blender/editors/mesh/editmesh_add.c @@ -113,7 +113,7 @@ static int add_primitive_plane_exec(bContext *C, wmOperator *op) em = BKE_editmesh_from_object(obedit); if (calc_uvs) { - ED_mesh_uv_texture_ensure(obedit->data, NULL); + ED_mesh_uv_ensure(obedit->data, NULL); } if (!EDBM_op_call_and_selectf( @@ -178,7 +178,7 @@ static int add_primitive_cube_exec(bContext *C, wmOperator *op) em = BKE_editmesh_from_object(obedit); if (calc_uvs) { - ED_mesh_uv_texture_ensure(obedit->data, NULL); + ED_mesh_uv_ensure(obedit->data, NULL); } if (!EDBM_op_call_and_selectf(em, @@ -252,7 +252,7 @@ static int add_primitive_circle_exec(bContext *C, wmOperator *op) em = BKE_editmesh_from_object(obedit); if (calc_uvs) { - ED_mesh_uv_texture_ensure(obedit->data, NULL); + ED_mesh_uv_ensure(obedit->data, NULL); } if (!EDBM_op_call_and_selectf( @@ -324,7 +324,7 @@ static int add_primitive_cylinder_exec(bContext *C, wmOperator *op) em = BKE_editmesh_from_object(obedit); if (calc_uvs) { - ED_mesh_uv_texture_ensure(obedit->data, NULL); + ED_mesh_uv_ensure(obedit->data, NULL); } if (!EDBM_op_call_and_selectf(em, @@ -400,7 +400,7 @@ static int add_primitive_cone_exec(bContext *C, wmOperator *op) em = BKE_editmesh_from_object(obedit); if (calc_uvs) { - ED_mesh_uv_texture_ensure(obedit->data, NULL); + ED_mesh_uv_ensure(obedit->data, NULL); } if (!EDBM_op_call_and_selectf(em, @@ -476,7 +476,7 @@ static int add_primitive_grid_exec(bContext *C, wmOperator *op) em = BKE_editmesh_from_object(obedit); if (calc_uvs) { - ED_mesh_uv_texture_ensure(obedit->data, NULL); + ED_mesh_uv_ensure(obedit->data, NULL); } if (!EDBM_op_call_and_selectf( @@ -553,7 +553,7 @@ static int add_primitive_monkey_exec(bContext *C, wmOperator *op) em = BKE_editmesh_from_object(obedit); if (calc_uvs) { - ED_mesh_uv_texture_ensure(obedit->data, NULL); + ED_mesh_uv_ensure(obedit->data, NULL); } if (!EDBM_op_call_and_selectf(em, @@ -614,7 +614,7 @@ static int add_primitive_uvsphere_exec(bContext *C, wmOperator *op) em = BKE_editmesh_from_object(obedit); if (calc_uvs) { - ED_mesh_uv_texture_ensure(obedit->data, NULL); + ED_mesh_uv_ensure(obedit->data, NULL); } if (!EDBM_op_call_and_selectf( @@ -682,7 +682,7 @@ static int add_primitive_icosphere_exec(bContext *C, wmOperator *op) em = BKE_editmesh_from_object(obedit); if (calc_uvs) { - ED_mesh_uv_texture_ensure(obedit->data, NULL); + ED_mesh_uv_ensure(obedit->data, NULL); } if (!EDBM_op_call_and_selectf( diff --git a/source/blender/editors/mesh/editmesh_add_gizmo.c b/source/blender/editors/mesh/editmesh_add_gizmo.c index d0f37314661..f5090c0143d 100644 --- a/source/blender/editors/mesh/editmesh_add_gizmo.c +++ b/source/blender/editors/mesh/editmesh_add_gizmo.c @@ -328,7 +328,7 @@ static int add_primitive_cube_gizmo_exec(bContext *C, wmOperator *op) const bool calc_uvs = RNA_boolean_get(op->ptr, "calc_uvs"); if (calc_uvs) { - ED_mesh_uv_texture_ensure(obedit->data, NULL); + ED_mesh_uv_ensure(obedit->data, NULL); } if (!EDBM_op_call_and_selectf(em, diff --git a/source/blender/editors/mesh/editmesh_bevel.c b/source/blender/editors/mesh/editmesh_bevel.c index 969d5b5912c..e7891450bd6 100644 --- a/source/blender/editors/mesh/editmesh_bevel.c +++ b/source/blender/editors/mesh/editmesh_bevel.c @@ -669,7 +669,7 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event) short etype = event->type; short eval = event->val; - /* When activated from toolbar, need to convert leftmouse release to confirm */ + /* When activated from toolbar, need to convert left-mouse release to confirm. */ if (ELEM(etype, LEFTMOUSE, opdata->launch_event) && (eval == KM_RELEASE) && RNA_boolean_get(op->ptr, "release_confirm")) { etype = EVT_MODAL_MAP; diff --git a/source/blender/editors/mesh/editmesh_intersect.c b/source/blender/editors/mesh/editmesh_intersect.c index 2642a613e92..166eb40a7db 100644 --- a/source/blender/editors/mesh/editmesh_intersect.c +++ b/source/blender/editors/mesh/editmesh_intersect.c @@ -99,8 +99,8 @@ static void edbm_intersect_select(BMEditMesh *em, struct Mesh *me, bool do_selec BM_edge_select_set(em->bm, e, true); } } + EDBM_selectmode_flush(em); } - EDBM_select_flush(em); } EDBM_update(me, diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index ee40431c101..5680865ae67 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -190,6 +190,22 @@ typedef struct KnifeBVH { } KnifeBVH; +/** Additional per-object data. */ +typedef struct KnifeObjectInfo { + const float (*cagecos)[3]; + + /** + * Optionally allocate triangle indices, these are needed for non-interactive knife + * projection as multiple cuts are made without the BVH being updated. + * Using these indices the it's possible to access `cagecos` even if the face has been cut + * and the loops in `em->looptris` no longer refer to the original triangles, see: T97153. + */ + const int (*tri_indices)[3]; + + /** Only assigned for convenient access. */ + BMEditMesh *em; +} KnifeObjectInfo; + /* struct for properties used while drawing */ typedef struct KnifeTool_OpData { ARegion *region; /* Region that knifetool was activated in. */ @@ -203,6 +219,9 @@ typedef struct KnifeTool_OpData { Object **objects; uint objects_len; + /** Array `objects_len` length of additional per-object data. */ + KnifeObjectInfo *objects_info; + MemArena *arena; /* Reused for edge-net filling. */ @@ -220,7 +239,6 @@ typedef struct KnifeTool_OpData { GHash *facetrimap; KnifeBVH bvh; - const float (**cagecos)[3]; BLI_mempool *kverts; BLI_mempool *kedges; @@ -359,45 +377,45 @@ static void knifetool_raycast_planes(const KnifeTool_OpData *kcd, float r_v1[3], kcd->vc.rv3d->persmat, planes[2], planes[0], planes[1], planes[3], NULL, NULL); /* Ray-cast all planes. */ + float ray_dir[3]; + float ray_hit_best[2][3] = {{UNPACK3(kcd->prev.cage)}, {UNPACK3(kcd->curr.cage)}}; + float lambda_best[2] = {-FLT_MAX, FLT_MAX}; + int i; + { - float ray_dir[3]; - float ray_hit_best[2][3] = {{UNPACK3(kcd->prev.cage)}, {UNPACK3(kcd->curr.cage)}}; - float lambda_best[2] = {-FLT_MAX, FLT_MAX}; - int i; + float curr_cage_adjust[3]; + float co_depth[3]; - { - float curr_cage_adjust[3]; - float co_depth[3]; + copy_v3_v3(co_depth, kcd->prev.cage); + ED_view3d_win_to_3d(kcd->vc.v3d, kcd->region, co_depth, kcd->curr.mval, curr_cage_adjust); - copy_v3_v3(co_depth, kcd->prev.cage); - ED_view3d_win_to_3d(kcd->vc.v3d, kcd->region, co_depth, kcd->curr.mval, curr_cage_adjust); + sub_v3_v3v3(ray_dir, curr_cage_adjust, kcd->prev.cage); + } - sub_v3_v3v3(ray_dir, curr_cage_adjust, kcd->prev.cage); + for (i = 0; i < 4; i++) { + float ray_hit[3]; + float lambda_test; + if (!isect_ray_plane_v3(kcd->prev.cage, ray_dir, planes[i], &lambda_test, false)) { + continue; } - for (i = 0; i < 4; i++) { - float ray_hit[3]; - float lambda_test; - if (isect_ray_plane_v3(kcd->prev.cage, ray_dir, planes[i], &lambda_test, false)) { - madd_v3_v3v3fl(ray_hit, kcd->prev.cage, ray_dir, lambda_test); - if (lambda_test < 0.0f) { - if (lambda_test > lambda_best[0]) { - copy_v3_v3(ray_hit_best[0], ray_hit); - lambda_best[0] = lambda_test; - } - } - else { - if (lambda_test < lambda_best[1]) { - copy_v3_v3(ray_hit_best[1], ray_hit); - lambda_best[1] = lambda_test; - } - } + madd_v3_v3v3fl(ray_hit, kcd->prev.cage, ray_dir, lambda_test); + if (lambda_test < 0.0f) { + if (lambda_test > lambda_best[0]) { + copy_v3_v3(ray_hit_best[0], ray_hit); + lambda_best[0] = lambda_test; + } + } + else { + if (lambda_test < lambda_best[1]) { + copy_v3_v3(ray_hit_best[1], ray_hit); + lambda_best[1] = lambda_test; } } - - copy_v3_v3(r_v1, ray_hit_best[0]); - copy_v3_v3(r_v2, ray_hit_best[1]); } + + copy_v3_v3(r_v1, ray_hit_best[0]); + copy_v3_v3(r_v2, ray_hit_best[1]); } static void knifetool_draw_angle_snapping(const KnifeTool_OpData *kcd) @@ -422,43 +440,45 @@ static void knifetool_draw_angle_snapping(const KnifeTool_OpData *kcd) static void knifetool_draw_orientation_locking(const KnifeTool_OpData *kcd) { - if (!compare_v3v3(kcd->prev.cage, kcd->curr.cage, KNIFE_FLT_EPSBIG)) { - float v1[3], v2[3]; + if (compare_v3v3(kcd->prev.cage, kcd->curr.cage, KNIFE_FLT_EPSBIG)) { + return; + } - /* This is causing buggy behavior when `prev.cage` and `curr.cage` are too close together. */ - knifetool_raycast_planes(kcd, v1, v2); + float v1[3], v2[3]; - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + /* This is causing buggy behavior when `prev.cage` and `curr.cage` are too close together. */ + knifetool_raycast_planes(kcd, v1, v2); - switch (kcd->constrain_axis) { - case KNF_CONSTRAIN_AXIS_X: { - immUniformColor3ubv(kcd->colors.xaxis); - break; - } - case KNF_CONSTRAIN_AXIS_Y: { - immUniformColor3ubv(kcd->colors.yaxis); - break; - } - case KNF_CONSTRAIN_AXIS_Z: { - immUniformColor3ubv(kcd->colors.zaxis); - break; - } - default: { - immUniformColor3ubv(kcd->colors.axis_extra); - break; - } + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + + switch (kcd->constrain_axis) { + case KNF_CONSTRAIN_AXIS_X: { + immUniformColor3ubv(kcd->colors.xaxis); + break; } + case KNF_CONSTRAIN_AXIS_Y: { + immUniformColor3ubv(kcd->colors.yaxis); + break; + } + case KNF_CONSTRAIN_AXIS_Z: { + immUniformColor3ubv(kcd->colors.zaxis); + break; + } + default: { + immUniformColor3ubv(kcd->colors.axis_extra); + break; + } + } - GPU_line_width(2.0); + GPU_line_width(2.0); - immBegin(GPU_PRIM_LINES, 2); - immVertex3fv(pos, v1); - immVertex3fv(pos, v2); - immEnd(); + immBegin(GPU_PRIM_LINES, 2); + immVertex3fv(pos, v1); + immVertex3fv(pos, v2); + immEnd(); - immUnbindProgram(); - } + immUnbindProgram(); } static void knifetool_draw_visible_distances(const KnifeTool_OpData *kcd) @@ -1134,6 +1154,52 @@ static void knife_update_header(bContext *C, wmOperator *op, KnifeTool_OpData *k /** \} */ /* -------------------------------------------------------------------- */ +/** \name Knife Object Info Accessors (#KnifeObjectInfo) + * \{ */ + +static const int *knife_bm_tri_index_get(const KnifeTool_OpData *kcd, + int base_index, + int tri_index, + int tri_index_buf[3]) +{ + const KnifeObjectInfo *obinfo = &kcd->objects_info[base_index]; + if (obinfo->tri_indices) { + return obinfo->tri_indices[tri_index]; + } + for (int i = 0; i < 3; i++) { + tri_index_buf[i] = BM_elem_index_get(obinfo->em->looptris[tri_index][i]->v); + } + return tri_index_buf; +} + +static void knife_bm_tri_cagecos_get(const KnifeTool_OpData *kcd, + int base_index, + int tri_index, + float cos[3][3]) +{ + const KnifeObjectInfo *obinfo = &kcd->objects_info[base_index]; + int tri_ind_buf[3]; + const int *tri_ind = knife_bm_tri_index_get(kcd, base_index, tri_index, tri_ind_buf); + for (int i = 0; i < 3; i++) { + copy_v3_v3(cos[i], obinfo->cagecos[tri_ind[i]]); + } +} + +static void knife_bm_tri_cagecos_get_worldspace(const KnifeTool_OpData *kcd, + int base_index, + int tri_index, + float cos[3][3]) +{ + knife_bm_tri_cagecos_get(kcd, base_index, tri_index, cos); + const Object *ob = kcd->objects[base_index]; + for (int i = 0; i < 3; i++) { + mul_m4_v3(ob->obmat, cos[i]); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Knife BVH Utils * \{ */ @@ -1162,7 +1228,6 @@ static void knife_bvh_init(KnifeTool_OpData *kcd) } /* Construct BVH Tree. */ - float cos[3][3]; const float epsilon = FLT_EPSILON * 2.0f; int tottri = 0; int ob_tottri = 0; @@ -1220,16 +1285,9 @@ static void knife_bvh_init(KnifeTool_OpData *kcd) continue; } - copy_v3_v3(cos[0], kcd->cagecos[b][BM_elem_index_get(looptris[i][0]->v)]); - copy_v3_v3(cos[1], kcd->cagecos[b][BM_elem_index_get(looptris[i][1]->v)]); - copy_v3_v3(cos[2], kcd->cagecos[b][BM_elem_index_get(looptris[i][2]->v)]); - - /* Convert to world-space. */ - mul_m4_v3(ob->obmat, cos[0]); - mul_m4_v3(ob->obmat, cos[1]); - mul_m4_v3(ob->obmat, cos[2]); - - BLI_bvhtree_insert(kcd->bvh.tree, i + tottri, (float *)cos, 3); + float tri_cos[3][3]; + knife_bm_tri_cagecos_get_worldspace(kcd, b, i, tri_cos); + BLI_bvhtree_insert(kcd->bvh.tree, i + tottri, &tri_cos[0][0], 3); } tottri += em->tottri; @@ -1252,6 +1310,10 @@ static void knife_bvh_raycast_cb(void *userdata, const BVHTreeRay *ray, BVHTreeRayHit *hit) { + if (index == -1) { + return; + } + KnifeTool_OpData *kcd = userdata; BMLoop **ltri; Object *ob; @@ -1260,65 +1322,55 @@ static void knife_bvh_raycast_cb(void *userdata, float dist, uv[2]; bool isect; int tottri; - float tri_cos[3][3]; - if (index != -1) { - tottri = 0; - uint b = 0; - for (; b < kcd->objects_len; b++) { - index -= tottri; - ob = kcd->objects[b]; - em = BKE_editmesh_from_object(ob); - tottri = em->tottri; - if (index < tottri) { - ltri = em->looptris[index]; - break; - } + tottri = 0; + uint b = 0; + for (; b < kcd->objects_len; b++) { + index -= tottri; + ob = kcd->objects[b]; + em = BKE_editmesh_from_object(ob); + tottri = em->tottri; + if (index < tottri) { + ltri = em->looptris[index]; + break; } + } - if (kcd->bvh.filter_cb) { - if (!kcd->bvh.filter_cb(ltri[0]->f, kcd->bvh.filter_data)) { - return; - } + if (kcd->bvh.filter_cb) { + if (!kcd->bvh.filter_cb(ltri[0]->f, kcd->bvh.filter_data)) { + return; } + } - copy_v3_v3(tri_cos[0], kcd->cagecos[b][BM_elem_index_get(ltri[0]->v)]); - copy_v3_v3(tri_cos[1], kcd->cagecos[b][BM_elem_index_get(ltri[1]->v)]); - copy_v3_v3(tri_cos[2], kcd->cagecos[b][BM_elem_index_get(ltri[2]->v)]); - mul_m4_v3(ob->obmat, tri_cos[0]); - mul_m4_v3(ob->obmat, tri_cos[1]); - mul_m4_v3(ob->obmat, tri_cos[2]); - - isect = - (ray->radius > 0.0f ? - isect_ray_tri_epsilon_v3(ray->origin, - ray->direction, - tri_cos[0], - tri_cos[1], - tri_cos[2], - &dist, - uv, - ray->radius) : + float tri_cos[3][3]; + knife_bm_tri_cagecos_get_worldspace(kcd, b, index, tri_cos); + isect = (ray->radius > 0.0f ? + isect_ray_tri_epsilon_v3( + ray->origin, ray->direction, UNPACK3(tri_cos), &dist, uv, ray->radius) : #ifdef USE_KDOPBVH_WATERTIGHT - isect_ray_tri_watertight_v3( - ray->origin, ray->isect_precalc, tri_cos[0], tri_cos[1], tri_cos[2], &dist, uv)); + isect_ray_tri_watertight_v3( + ray->origin, ray->isect_precalc, UNPACK3(tri_cos), &dist, uv)); #else - isect_ray_tri_v3( - ray->origin, ray->direction, tri_cos[0], tri_cos[1], tri_cos[2], &dist, uv)); + isect_ray_tri_v3(ray->origin, ray->direction, UNPACK3(tri_cos), &dist, uv); #endif - if (isect && dist < hit->dist) { - hit->dist = dist; - hit->index = index; + if (isect && dist < hit->dist) { + madd_v3_v3v3fl(hit->co, ray->origin, ray->direction, dist); - copy_v3_v3(hit->no, ltri[0]->f->no); + /* Discard clipped points. */ + if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d) && + ED_view3d_clipping_test(kcd->vc.rv3d, hit->co, false)) { + return; + } - madd_v3_v3v3fl(hit->co, ray->origin, ray->direction, dist); + hit->dist = dist; + hit->index = index; - kcd->bvh.looptris = em->looptris; - copy_v2_v2(kcd->bvh.uv, uv); - kcd->bvh.base_index = b; - } + copy_v3_v3(hit->no, ltri[0]->f->no); + + kcd->bvh.looptris = em->looptris; + copy_v2_v2(kcd->bvh.uv, uv); + kcd->bvh.base_index = b; } } @@ -1333,7 +1385,6 @@ static BMFace *knife_bvh_raycast(KnifeTool_OpData *kcd, uint *r_base_index) { BMFace *face; - BMLoop **ltri; BVHTreeRayHit hit; const float dist = r_dist ? *r_dist : FLT_MAX; hit.dist = dist; @@ -1347,8 +1398,9 @@ static BMFace *knife_bvh_raycast(KnifeTool_OpData *kcd, /* Hits returned in world space. */ if (r_hitout) { - ltri = kcd->bvh.looptris[hit.index]; - interp_v3_v3v3v3_uv(r_hitout, ltri[0]->v->co, ltri[1]->v->co, ltri[2]->v->co, kcd->bvh.uv); + float tri_cos[3][3]; + knife_bm_tri_cagecos_get_worldspace(kcd, kcd->bvh.base_index, hit.index, tri_cos); + interp_v3_v3v3v3_uv(r_hitout, UNPACK3(tri_cos), kcd->bvh.uv); if (r_cagehit) { copy_v3_v3(r_cagehit, hit.co); @@ -1384,7 +1436,6 @@ static BMFace *knife_bvh_raycast_filter(KnifeTool_OpData *kcd, kcd->bvh.filter_data = filter_userdata; BMFace *face; - BMLoop **ltri; BVHTreeRayHit hit; const float dist = r_dist ? *r_dist : FLT_MAX; hit.dist = dist; @@ -1401,8 +1452,9 @@ static BMFace *knife_bvh_raycast_filter(KnifeTool_OpData *kcd, /* Hits returned in world space. */ if (r_hitout) { - ltri = kcd->bvh.looptris[hit.index]; - interp_v3_v3v3v3_uv(r_hitout, ltri[0]->v->co, ltri[1]->v->co, ltri[2]->v->co, kcd->bvh.uv); + float tri_cos[3][3]; + knife_bm_tri_cagecos_get_worldspace(kcd, kcd->bvh.base_index, hit.index, tri_cos); + interp_v3_v3v3v3_uv(r_hitout, UNPACK3(tri_cos), kcd->bvh.uv); if (r_cagehit) { copy_v3_v3(r_cagehit, hit.co); @@ -1684,7 +1736,7 @@ static KnifeVert *get_bm_knife_vert(KnifeTool_OpData *kcd, BMVert *v, Object *ob BMFace *f; if (BM_elem_index_get(v) >= 0) { - cageco = kcd->cagecos[base_index][BM_elem_index_get(v)]; + cageco = kcd->objects_info[base_index].cagecos[BM_elem_index_get(v)]; } else { cageco = v->co; @@ -1928,29 +1980,30 @@ static void prepare_linehits_for_cut(KnifeTool_OpData *kcd) * Also remove all but one of a series of vertex hits for the same vertex. */ for (int i = 0; i < n; i++) { KnifeLineHit *lhi = &linehits[i]; - if (lhi->v) { - for (int j = i - 1; j >= 0; j--) { - KnifeLineHit *lhj = &linehits[j]; - if (!lhj->kfe || fabsf(lhi->l - lhj->l) > KNIFE_FLT_EPSBIG || - fabsf(lhi->m - lhj->m) > KNIFE_FLT_EPSBIG) { - break; - } + if (lhi->v == NULL) { + continue; + } - if (lhi->kfe == lhj->kfe) { - lhj->l = -1.0f; - is_double = true; - } + for (int j = i - 1; j >= 0; j--) { + KnifeLineHit *lhj = &linehits[j]; + if (!lhj->kfe || fabsf(lhi->l - lhj->l) > KNIFE_FLT_EPSBIG || + fabsf(lhi->m - lhj->m) > KNIFE_FLT_EPSBIG) { + break; } - for (int j = i + 1; j < n; j++) { - KnifeLineHit *lhj = &linehits[j]; - if (fabsf(lhi->l - lhj->l) > KNIFE_FLT_EPSBIG || - fabsf(lhi->m - lhj->m) > KNIFE_FLT_EPSBIG) { - break; - } - if ((lhj->kfe && (lhi->kfe == lhj->kfe)) || (lhi->v == lhj->v)) { - lhj->l = -1.0f; - is_double = true; - } + + if (lhi->kfe == lhj->kfe) { + lhj->l = -1.0f; + is_double = true; + } + } + for (int j = i + 1; j < n; j++) { + KnifeLineHit *lhj = &linehits[j]; + if (fabsf(lhi->l - lhj->l) > KNIFE_FLT_EPSBIG || fabsf(lhi->m - lhj->m) > KNIFE_FLT_EPSBIG) { + break; + } + if ((lhj->kfe && (lhi->kfe == lhj->kfe)) || (lhi->v == lhj->v)) { + lhj->l = -1.0f; + is_double = true; } } } @@ -2222,11 +2275,12 @@ static void knife_make_face_cuts(KnifeTool_OpData *kcd, BMesh *bm, BMFace *f, Li /* Remove dangling edges, not essential - but nice for users. */ for (i = 0; i < edge_array_len_orig; i++) { - if (kfe_array[i]) { - if (BM_edge_is_wire(kfe_array[i]->e)) { - BM_edge_kill(bm, kfe_array[i]->e); - kfe_array[i]->e = NULL; - } + if (kfe_array[i] == NULL) { + continue; + } + if (BM_edge_is_wire(kfe_array[i]->e)) { + BM_edge_kill(bm, kfe_array[i]->e); + kfe_array[i]->e = NULL; } } @@ -2538,34 +2592,30 @@ static bool knife_ray_intersect_face(KnifeTool_OpData *kcd, BLI_assert(tri_i >= 0 && tri_i < tottri); for (; tri_i < tottri; tri_i++) { - float lv[3][3]; + float tri_cos[3][3]; float ray_tri_uv[2]; tri = em->looptris[tri_i]; if (tri[0]->f != f) { break; } - copy_v3_v3(lv[0], kcd->cagecos[base_index][BM_elem_index_get(tri[0]->v)]); - copy_v3_v3(lv[1], kcd->cagecos[base_index][BM_elem_index_get(tri[1]->v)]); - copy_v3_v3(lv[2], kcd->cagecos[base_index][BM_elem_index_get(tri[2]->v)]); - mul_m4_v3(ob->obmat, lv[0]); - mul_m4_v3(ob->obmat, lv[1]); - mul_m4_v3(ob->obmat, lv[2]); + + knife_bm_tri_cagecos_get_worldspace(kcd, base_index, tri_i, tri_cos); /* Using epsilon test in case ray is directly through an internal * tessellation edge and might not hit either tessellation tri with * an exact test; * We will exclude hits near real edges by a later test. */ if (isect_ray_tri_epsilon_v3( - v1, raydir, lv[0], lv[1], lv[2], &lambda, ray_tri_uv, KNIFE_FLT_EPS)) { + v1, raydir, UNPACK3(tri_cos), &lambda, ray_tri_uv, KNIFE_FLT_EPS)) { /* Check if line coplanar with tri. */ - normal_tri_v3(tri_norm, lv[0], lv[1], lv[2]); - plane_from_point_normal_v3(tri_plane, lv[0], tri_norm); + normal_tri_v3(tri_norm, UNPACK3(tri_cos)); + plane_from_point_normal_v3(tri_plane, tri_cos[0], tri_norm); if ((dist_squared_to_plane_v3(v1, tri_plane) < KNIFE_FLT_EPS) && (dist_squared_to_plane_v3(v2, tri_plane) < KNIFE_FLT_EPS)) { return false; } - interp_v3_v3v3v3_uv(hit_cageco, lv[0], lv[1], lv[2], ray_tri_uv); + interp_v3_v3v3v3_uv(hit_cageco, UNPACK3(tri_cos), ray_tri_uv); /* Now check that far enough away from verts and edges. */ list = knife_get_face_kedges(kcd, ob, base_index, f); for (ref = list->first; ref; ref = ref->next) { @@ -2605,9 +2655,10 @@ static void calc_ortho_extent(KnifeTool_OpData *kcd) ob = kcd->objects[b]; em = BKE_editmesh_from_object(ob); - if (kcd->cagecos[b]) { + const float(*cagecos)[3] = kcd->objects_info[b].cagecos; + if (cagecos) { for (int i = 0; i < em->bm->totvert; i++) { - copy_v3_v3(ws, kcd->cagecos[b][i]); + copy_v3_v3(ws, cagecos[i]); mul_m4_v3(ob->obmat, ws); minmax_v3v3_v3(min, max, ws); } @@ -3763,7 +3814,7 @@ static void knife_reset_snap_angle_input(KnifeTool_OpData *kcd) * If scene orientation is set to anything other than global it takes priority. * Otherwise kcd->constrain_axis_mode is used. */ -static void knife_constrain_axis(bContext *C, KnifeTool_OpData *kcd) +static void knife_constrain_axis(KnifeTool_OpData *kcd) { /* Obtain current mouse position in world space. */ float curr_cage_adjusted[3]; @@ -3772,7 +3823,7 @@ static void knife_constrain_axis(bContext *C, KnifeTool_OpData *kcd) /* Constrain axes. */ Scene *scene = kcd->scene; - ViewLayer *view_layer = CTX_data_view_layer(C); + ViewLayer *view_layer = kcd->vc.view_layer; Object *obedit = (kcd->prev.ob) ? kcd->prev.ob : kcd->vc.obedit; RegionView3D *rv3d = kcd->region->regiondata; const short scene_orientation = BKE_scene_orientation_get_index(scene, SCE_ORIENT_DEFAULT); @@ -3824,7 +3875,7 @@ static void knife_constrain_axis(bContext *C, KnifeTool_OpData *kcd) * In this case the selection-buffer is used to select the face, * then the closest `vert` or `edge` is set, and those will enable `is_co_set`. */ -static bool knife_snap_update_from_mval(bContext *C, KnifeTool_OpData *kcd, const float mval[2]) +static bool knife_snap_update_from_mval(KnifeTool_OpData *kcd, const float mval[2]) { knife_pos_data_clear(&kcd->curr); copy_v2_v2(kcd->curr.mval, mval); @@ -3847,7 +3898,7 @@ static bool knife_snap_update_from_mval(bContext *C, KnifeTool_OpData *kcd, cons } if (kcd->axis_constrained) { - knife_constrain_axis(C, kcd); + knife_constrain_axis(kcd); } } @@ -3961,10 +4012,13 @@ static void knifetool_undo(KnifeTool_OpData *kcd) /** \} */ /* -------------------------------------------------------------------- */ -/** \name #KnifeTool_OpData (#op->customdata) Init and Free +/** \name #KnifeObjectInfo (#kcd->objects_info) Init and Free * \{ */ -static void knifetool_init_cagecos(KnifeTool_OpData *kcd, Object *ob, uint base_index) +static void knifetool_init_obinfo(KnifeTool_OpData *kcd, + Object *ob, + uint base_index, + bool use_tri_indices) { Scene *scene_eval = (Scene *)DEG_get_evaluated_id(kcd->vc.depsgraph, &kcd->scene->id); @@ -3973,18 +4027,36 @@ static void knifetool_init_cagecos(KnifeTool_OpData *kcd, Object *ob, uint base_ BM_mesh_elem_index_ensure(em_eval->bm, BM_VERT); - kcd->cagecos[base_index] = (const float(*)[3])BKE_editmesh_vert_coords_alloc( + KnifeObjectInfo *obinfo = &kcd->objects_info[base_index]; + obinfo->em = em_eval; + obinfo->cagecos = (const float(*)[3])BKE_editmesh_vert_coords_alloc( kcd->vc.depsgraph, em_eval, scene_eval, obedit_eval, NULL); + + if (use_tri_indices) { + BMLoop *(*looptris)[3] = em_eval->looptris; + int(*tri_indices)[3] = MEM_mallocN(sizeof(int[3]) * em_eval->tottri, __func__); + for (int i = 0; i < em_eval->tottri; i++) { + BMLoop **tri = looptris[i]; + tri_indices[i][0] = BM_elem_index_get(tri[0]->v); + tri_indices[i][1] = BM_elem_index_get(tri[1]->v); + tri_indices[i][2] = BM_elem_index_get(tri[2]->v); + } + obinfo->tri_indices = tri_indices; + } } -static void knifetool_free_cagecos(KnifeTool_OpData *kcd, uint base_index) +static void knifetool_free_obinfo(KnifeTool_OpData *kcd, uint base_index) { - if (kcd->cagecos[base_index]) { - MEM_freeN((void *)kcd->cagecos[base_index]); - kcd->cagecos[base_index] = NULL; - } + MEM_SAFE_FREE(kcd->objects_info[base_index].cagecos); + MEM_SAFE_FREE(kcd->objects_info[base_index].tri_indices); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #KnifeTool_OpData (#op->customdata) Init and Free + * \{ */ + static void knife_init_colors(KnifeColors *colors) { /* Possible BMESH_TODO: add explicit themes or calculate these by @@ -4007,8 +4079,7 @@ static void knife_init_colors(KnifeColors *colors) } /* called when modal loop selection gets set up... */ -static void knifetool_init(bContext *C, - ViewContext *vc, +static void knifetool_init(ViewContext *vc, KnifeTool_OpData *kcd, const bool only_select, const bool cut_through, @@ -4018,6 +4089,10 @@ static void knifetool_init(bContext *C, const float angle_snapping_increment, const bool is_interactive) { + /* Needed so multiple non-interactive cuts (also called knife-project) + * doesn't access indices of loops that were created by cutting, see: T97153. */ + bool use_tri_indices = !is_interactive; + kcd->vc = *vc; Scene *scene = vc->scene; @@ -4027,15 +4102,15 @@ static void knifetool_init(bContext *C, kcd->region = vc->region; kcd->objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( - CTX_data_view_layer(C), CTX_wm_view3d(C), &kcd->objects_len); + vc->view_layer, vc->v3d, &kcd->objects_len); Object *ob; BMEditMesh *em; - kcd->cagecos = MEM_callocN(sizeof(*kcd->cagecos) * kcd->objects_len, "knife cagecos"); + kcd->objects_info = MEM_callocN(sizeof(*kcd->objects_info) * kcd->objects_len, "knife cagecos"); for (uint b = 0; b < kcd->objects_len; b++) { ob = kcd->objects[b]; em = BKE_editmesh_from_object(ob); - knifetool_init_cagecos(kcd, ob, b); + knifetool_init_obinfo(kcd, ob, b, use_tri_indices); /* Can't usefully select resulting edges in face mode. */ kcd->select_result = (em->selectmode != SCE_SELECT_FACE); @@ -4139,9 +4214,9 @@ static void knifetool_exit_ex(KnifeTool_OpData *kcd) /* Knife BVH cleanup. */ for (int i = 0; i < kcd->objects_len; i++) { - knifetool_free_cagecos(kcd, i); + knifetool_free_obinfo(kcd, i); } - MEM_freeN((void *)kcd->cagecos); + MEM_freeN((void *)kcd->objects_info); knife_bvh_free(kcd); /* Line-hits cleanup. */ @@ -4169,14 +4244,14 @@ static void knifetool_exit(wmOperator *op) /** \name Mouse-Moving Event Updates * \{ */ -/* Update active knife edge/vert pointers. */ -static int knife_update_active(bContext *C, KnifeTool_OpData *kcd) +/** Update active knife edge/vert pointers. */ +static int knife_update_active(KnifeTool_OpData *kcd) { /* If no hits are found this would normally default to (0, 0, 0) so instead * get a point at the mouse ray closest to the previous point. * Note that drawing lines in `free-space` isn't properly supported * but there's no guarantee (0, 0, 0) has any geometry either - campbell */ - if (!knife_snap_update_from_mval(C, kcd, kcd->mval)) { + if (!knife_snap_update_from_mval(kcd, kcd->mval)) { float origin[3]; float origin_ofs[3]; @@ -4197,20 +4272,20 @@ static int knife_update_active(bContext *C, KnifeTool_OpData *kcd) return 1; } -static void knifetool_update_mval(bContext *C, KnifeTool_OpData *kcd, const float mval[2]) +static void knifetool_update_mval(KnifeTool_OpData *kcd, const float mval[2]) { knife_recalc_ortho(kcd); copy_v2_v2(kcd->mval, mval); - if (knife_update_active(C, kcd)) { + if (knife_update_active(kcd)) { ED_region_tag_redraw(kcd->region); } } -static void knifetool_update_mval_i(bContext *C, KnifeTool_OpData *kcd, const int mval_i[2]) +static void knifetool_update_mval_i(KnifeTool_OpData *kcd, const int mval_i[2]) { const float mval[2] = {UNPACK2(mval_i)}; - knifetool_update_mval(C, kcd, mval); + knifetool_update_mval(kcd, mval); } /** \} */ @@ -4219,33 +4294,18 @@ static void knifetool_update_mval_i(bContext *C, KnifeTool_OpData *kcd, const in /** \name Finalization * \{ */ -/* Called on tool confirmation. */ -static void knifetool_finish_ex(KnifeTool_OpData *kcd) +static void knifetool_finish_single_pre(KnifeTool_OpData *kcd, Object *ob) { - Object *ob; - BMEditMesh *em; - for (uint b = 0; b < kcd->objects_len; b++) { - ob = kcd->objects[b]; - em = BKE_editmesh_from_object(ob); - - knife_make_cuts(kcd, ob); - - EDBM_selectmode_flush(em); - EDBM_update(ob->data, - &(const struct EDBMUpdate_Params){ - .calc_looptri = true, - .calc_normals = true, - .is_destructive = true, - }); - } + knife_make_cuts(kcd, ob); } -static void knifetool_finish_single_ex(KnifeTool_OpData *kcd, Object *ob, uint UNUSED(base_index)) +/** + * A post version is needed to to delay recalculating tessellation after making cuts. + * Without this, knife-project can't use the BVH tree to select geometry after a cut, see: T98349. + */ +static void knifetool_finish_single_post(KnifeTool_OpData *UNUSED(kcd), Object *ob) { - knife_make_cuts(kcd, ob); - BMEditMesh *em = BKE_editmesh_from_object(ob); - EDBM_selectmode_flush(em); EDBM_update(ob->data, &(const struct EDBMUpdate_Params){ @@ -4255,6 +4315,16 @@ static void knifetool_finish_single_ex(KnifeTool_OpData *kcd, Object *ob, uint U }); } +/* Called on tool confirmation. */ +static void knifetool_finish_ex(KnifeTool_OpData *kcd) +{ + for (uint b = 0; b < kcd->objects_len; b++) { + Object *ob = kcd->objects[b]; + knifetool_finish_single_pre(kcd, ob); + knifetool_finish_single_post(kcd, ob); + } +} + static void knifetool_finish(wmOperator *op) { KnifeTool_OpData *kcd = op->customdata; @@ -4373,7 +4443,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) snapping_increment_temp <= KNIFE_MAX_ANGLE_SNAPPING_INCREMENT) { kcd->angle_snapping_increment = snapping_increment_temp; } - knife_update_active(C, kcd); + knife_update_active(kcd); knife_update_header(C, op, kcd); ED_region_tag_redraw(kcd->region); return OPERATOR_RUNNING_MODAL; @@ -4391,7 +4461,8 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) ED_workspace_status_text(C, NULL); return OPERATOR_CANCELLED; - case KNF_MODAL_CONFIRM: + case KNF_MODAL_CONFIRM: { + const bool changed = (kcd->totkvert != 0); /* finish */ ED_region_tag_redraw(kcd->region); @@ -4400,11 +4471,11 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) ED_workspace_status_text(C, NULL); /* Cancel to prevent undo push for empty cuts. */ - if (kcd->totkvert == 0) { + if (!changed) { return OPERATOR_CANCELLED; } - return OPERATOR_FINISHED; + } case KNF_MODAL_UNDO: if (BLI_stack_is_empty(kcd->undostack)) { ED_region_tag_redraw(kcd->region); @@ -4413,7 +4484,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_CANCELLED; } knifetool_undo(kcd); - knife_update_active(C, kcd); + knife_update_active(kcd); ED_region_tag_redraw(kcd->region); handled = true; break; @@ -4421,7 +4492,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) kcd->snap_midpoints = true; knife_recalc_ortho(kcd); - knife_update_active(C, kcd); + knife_update_active(kcd); knife_update_header(C, op, kcd); ED_region_tag_redraw(kcd->region); do_refresh = true; @@ -4431,7 +4502,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) kcd->snap_midpoints = false; knife_recalc_ortho(kcd); - knife_update_active(C, kcd); + knife_update_active(kcd); knife_update_header(C, op, kcd); ED_region_tag_redraw(kcd->region); do_refresh = true; @@ -4465,7 +4536,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) RNA_float_get(op->ptr, "angle_snapping_increment")); knifetool_disable_orientation_locking(kcd); knife_reset_snap_angle_input(kcd); - knife_update_active(C, kcd); + knife_update_active(kcd); knife_update_header(C, op, kcd); ED_region_tag_redraw(kcd->region); do_refresh = true; @@ -4550,7 +4621,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) kcd->is_drag_undo = false; /* Needed because the last face 'hit' is ignored when dragging. */ - knifetool_update_mval(C, kcd, kcd->curr.mval); + knifetool_update_mval(kcd, kcd->curr.mval); } ED_region_tag_redraw(kcd->region); @@ -4563,14 +4634,14 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) if (kcd->is_drag_hold) { kcd->is_drag_hold = false; kcd->is_drag_undo = false; - knifetool_update_mval(C, kcd, kcd->curr.mval); + knifetool_update_mval(kcd, kcd->curr.mval); } kcd->prev = kcd->curr; kcd->curr = kcd->init; knife_project_v2(kcd, kcd->curr.cage, kcd->curr.mval); - knifetool_update_mval(C, kcd, kcd->curr.mval); + knifetool_update_mval(kcd, kcd->curr.mval); knife_add_cut(kcd); @@ -4606,7 +4677,8 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_PASS_THROUGH; case MOUSEMOVE: /* Mouse moved somewhere to select another loop. */ if (kcd->mode != MODE_PANNING) { - knifetool_update_mval_i(C, kcd, event->mval); + knifetool_update_mval_i(kcd, event->mval); + knife_update_header(C, op, kcd); if (kcd->is_drag_hold) { if (kcd->totlinehit >= 2) { @@ -4633,7 +4705,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) snapping_increment_temp <= KNIFE_MAX_ANGLE_SNAPPING_INCREMENT) { kcd->angle_snapping_increment = snapping_increment_temp; } - knife_update_active(C, kcd); + knife_update_active(kcd); knife_update_header(C, op, kcd); ED_region_tag_redraw(kcd->region); return OPERATOR_RUNNING_MODAL; @@ -4688,7 +4760,8 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) if (do_refresh) { /* We don't really need to update mval, * but this happens to be the best way to refresh at the moment. */ - knifetool_update_mval_i(C, kcd, event->mval); + knifetool_update_mval_i(kcd, event->mval); + knife_update_header(C, op, kcd); } /* Keep going until the user confirms. */ @@ -4714,8 +4787,7 @@ static int knifetool_invoke(bContext *C, wmOperator *op, const wmEvent *event) /* alloc new customdata */ kcd = op->customdata = MEM_callocN(sizeof(KnifeTool_OpData), __func__); - knifetool_init(C, - &vc, + knifetool_init(&vc, kcd, only_select, cut_through, @@ -4751,7 +4823,7 @@ static int knifetool_invoke(bContext *C, wmOperator *op, const wmEvent *event) WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_KNIFE); WM_event_add_modal_handler(C, op); - knifetool_update_mval_i(C, kcd, event->mval); + knifetool_update_mval_i(kcd, event->mval); if (wait_for_input == false) { /* Avoid copy-paste logic. */ @@ -4869,7 +4941,7 @@ static bool edbm_mesh_knife_point_isect(LinkNode *polys, const float cent_ss[2]) return false; } -void EDBM_mesh_knife(bContext *C, ViewContext *vc, LinkNode *polys, bool use_tag, bool cut_through) +void EDBM_mesh_knife(ViewContext *vc, LinkNode *polys, bool use_tag, bool cut_through) { KnifeTool_OpData *kcd; @@ -4884,8 +4956,7 @@ void EDBM_mesh_knife(bContext *C, ViewContext *vc, LinkNode *polys, bool use_tag kcd = MEM_callocN(sizeof(KnifeTool_OpData), __func__); - knifetool_init(C, - vc, + knifetool_init(vc, kcd, only_select, cut_through, @@ -4911,7 +4982,7 @@ void EDBM_mesh_knife(bContext *C, ViewContext *vc, LinkNode *polys, bool use_tag int i; for (i = 0; i < mval_tot; i++) { - knifetool_update_mval(C, kcd, mval_fl[i]); + knifetool_update_mval(kcd, mval_fl[i]); if (i == 0) { knife_start_cut(kcd); kcd->mode = MODE_DRAGGING; @@ -4939,7 +5010,7 @@ void EDBM_mesh_knife(bContext *C, ViewContext *vc, LinkNode *polys, bool use_tag BM_mesh_elem_hflag_enable_all(em->bm, BM_EDGE, BM_ELEM_TAG, false); } - knifetool_finish_single_ex(kcd, ob, b); + knifetool_finish_single_pre(kcd, ob); /* Tag faces inside! */ if (use_tag) { @@ -4963,17 +5034,19 @@ void EDBM_mesh_knife(bContext *C, ViewContext *vc, LinkNode *polys, bool use_tag /* Tag all faces linked to cut edges. */ BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { /* Check are we tagged?, then we are an original face. */ - if (BM_elem_flag_test(e, BM_ELEM_TAG) == false) { - BMFace *f; - BMIter fiter; - BM_ITER_ELEM (f, &fiter, e, BM_FACES_OF_EDGE) { - float cent[3], cent_ss[2]; - BM_face_calc_point_in_face(f, cent); - mul_m4_v3(ob->obmat, cent); - knife_project_v2(kcd, cent, cent_ss); - if (edbm_mesh_knife_point_isect(polys, cent_ss)) { - BM_elem_flag_enable(f, BM_ELEM_TAG); - } + if (BM_elem_flag_test(e, BM_ELEM_TAG)) { + continue; + } + + BMFace *f; + BMIter fiter; + BM_ITER_ELEM (f, &fiter, e, BM_FACES_OF_EDGE) { + float cent[3], cent_ss[2]; + BM_face_calc_point_in_face(f, cent); + mul_m4_v3(ob->obmat, cent); + knife_project_v2(kcd, cent, cent_ss); + if (edbm_mesh_knife_point_isect(polys, cent_ss)) { + BM_elem_flag_enable(f, BM_ELEM_TAG); } } } @@ -4983,43 +5056,45 @@ void EDBM_mesh_knife(bContext *C, ViewContext *vc, LinkNode *polys, bool use_tag BMFace *f; keep_search = false; BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - if (BM_elem_flag_test(f, BM_ELEM_TAG) == false && (F_ISECT_IS_UNKNOWN(f))) { - /* Am I connected to a tagged face via an un-tagged edge - * (ie, not across a cut)? */ - BMLoop *l_first = BM_FACE_FIRST_LOOP(f); - BMLoop *l_iter = l_first; - bool found = false; - - do { - if (BM_elem_flag_test(l_iter->e, BM_ELEM_TAG) != false) { - /* Now check if the adjacent faces is tagged. */ - BMLoop *l_radial_iter = l_iter->radial_next; - if (l_radial_iter != l_iter) { - do { - if (BM_elem_flag_test(l_radial_iter->f, BM_ELEM_TAG)) { - found = true; - } - } while ((l_radial_iter = l_radial_iter->radial_next) != l_iter && - (found == false)); - } - } - } while ((l_iter = l_iter->next) != l_first && (found == false)); - - if (found) { - float cent[3], cent_ss[2]; - BM_face_calc_point_in_face(f, cent); - mul_m4_v3(ob->obmat, cent); - knife_project_v2(kcd, cent, cent_ss); - if ((kcd->cut_through || point_is_visible(kcd, cent, cent_ss, (BMElem *)f)) && - edbm_mesh_knife_point_isect(polys, cent_ss)) { - BM_elem_flag_enable(f, BM_ELEM_TAG); - keep_search = true; - } - else { - /* Don't lose time on this face again, set it as outside. */ - F_ISECT_SET_OUTSIDE(f); + if (BM_elem_flag_test(f, BM_ELEM_TAG) || !F_ISECT_IS_UNKNOWN(f)) { + continue; + } + + /* Am I connected to a tagged face via an un-tagged edge + * (ie, not across a cut)? */ + BMLoop *l_first = BM_FACE_FIRST_LOOP(f); + BMLoop *l_iter = l_first; + bool found = false; + + do { + if (BM_elem_flag_test(l_iter->e, BM_ELEM_TAG) != false) { + /* Now check if the adjacent faces is tagged. */ + BMLoop *l_radial_iter = l_iter->radial_next; + if (l_radial_iter != l_iter) { + do { + if (BM_elem_flag_test(l_radial_iter->f, BM_ELEM_TAG)) { + found = true; + } + } while ((l_radial_iter = l_radial_iter->radial_next) != l_iter && + (found == false)); } } + } while ((l_iter = l_iter->next) != l_first && (found == false)); + + if (found) { + float cent[3], cent_ss[2]; + BM_face_calc_point_in_face(f, cent); + mul_m4_v3(ob->obmat, cent); + knife_project_v2(kcd, cent, cent_ss); + if ((kcd->cut_through || point_is_visible(kcd, cent, cent_ss, (BMElem *)f)) && + edbm_mesh_knife_point_isect(polys, cent_ss)) { + BM_elem_flag_enable(f, BM_ELEM_TAG); + keep_search = true; + } + else { + /* Don't lose time on this face again, set it as outside. */ + F_ISECT_SET_OUTSIDE(f); + } } } } while (keep_search); @@ -5028,6 +5103,10 @@ void EDBM_mesh_knife(bContext *C, ViewContext *vc, LinkNode *polys, bool use_tag #undef F_ISECT_SET_UNKNOWN #undef F_ISECT_SET_OUTSIDE } + + /* Defer freeing data until the BVH tree is finished with, see: #point_is_visible and + * the doc-string for #knifetool_finish_single_post. */ + knifetool_finish_single_post(kcd, ob); } knifetool_exit_ex(kcd); diff --git a/source/blender/editors/mesh/editmesh_knife_project.c b/source/blender/editors/mesh/editmesh_knife_project.c index bce46dd7cf7..c32b1fa99c0 100644 --- a/source/blender/editors/mesh/editmesh_knife_project.c +++ b/source/blender/editors/mesh/editmesh_knife_project.c @@ -142,7 +142,7 @@ static int knifeproject_exec(bContext *C, wmOperator *op) ED_view3d_viewcontext_init_object(&vc, obedit); BMEditMesh *em = BKE_editmesh_from_object(obedit); - EDBM_mesh_knife(C, &vc, polys, true, cut_through); + EDBM_mesh_knife(&vc, polys, true, cut_through); /* select only tagged faces */ BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false); diff --git a/source/blender/editors/mesh/editmesh_select_similar.c b/source/blender/editors/mesh/editmesh_select_similar.c index 3f05c27e8fa..c931cb4948b 100644 --- a/source/blender/editors/mesh/editmesh_select_similar.c +++ b/source/blender/editors/mesh/editmesh_select_similar.c @@ -1370,8 +1370,14 @@ static bool edbm_select_similar_poll_property(const bContext *UNUSED(C), const char *prop_id = RNA_property_identifier(prop); const int type = RNA_enum_get(op->ptr, "type"); + /* Only show compare when it is used. */ + if (STREQ(prop_id, "compare")) { + if (type == SIMVERT_VGROUP) { + return false; + } + } /* Only show threshold when it is used. */ - if (STREQ(prop_id, "threshold")) { + else if (STREQ(prop_id, "threshold")) { if (!ELEM(type, SIMVERT_NORMAL, SIMEDGE_BEVEL, diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index cafcc4ec578..1febc429edc 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -3092,8 +3092,8 @@ static int edbm_rotate_colors_exec(bContext *C, wmOperator *op) BMOperator bmop; - Mesh *me = BKE_object_get_original_mesh(ob); - CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id); + const Mesh *me = BKE_object_get_original_mesh(ob); + const CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id); if (!layer || BKE_id_attribute_domain(&me->id, layer) != ATTR_DOMAIN_CORNER) { continue; @@ -3144,8 +3144,8 @@ static int edbm_reverse_colors_exec(bContext *C, wmOperator *op) continue; } - Mesh *me = BKE_object_get_original_mesh(obedit); - CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id); + const Mesh *me = BKE_object_get_original_mesh(obedit); + const CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id); if (!layer || BKE_id_attribute_domain(&me->id, layer) != ATTR_DOMAIN_CORNER) { continue; @@ -7032,6 +7032,14 @@ static void sort_bmelem_flag(bContext *C, } BM_mesh_remap(em->bm, map[0], map[1], map[2]); + + EDBM_update(ob->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = (totelem[2] != 0), + .calc_normals = false, + .is_destructive = true, + }); + DEG_id_tag_update(ob->data, ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c index ecc5f8f8ef5..d75c92f963f 100644 --- a/source/blender/editors/mesh/editmesh_undo.c +++ b/source/blender/editors/mesh/editmesh_undo.c @@ -72,7 +72,7 @@ static CLG_LogRef LOG = {"ed.undo.mesh"}; /* Single linked list of layers stored per type */ typedef struct BArrayCustomData { struct BArrayCustomData *next; - CustomDataType type; + eCustomDataType type; int states_len; /* number of layers for each type */ BArrayState *states[0]; } BArrayCustomData; @@ -149,7 +149,7 @@ static void um_arraystore_cd_compact(struct CustomData *cdata, const BArrayCustomData *bcd_reference_current = bcd_reference; BArrayCustomData *bcd = NULL, *bcd_first = NULL, *bcd_prev = NULL; for (int layer_start = 0, layer_end; layer_start < cdata->totlayer; layer_start = layer_end) { - const CustomDataType type = cdata->layers[layer_start].type; + const eCustomDataType type = cdata->layers[layer_start].type; /* Perform a full copy on dynamic layers. * diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index c3d5f33705c..04030583f5c 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -9,6 +9,7 @@ #include "DNA_key_types.h" #include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "BLI_alloca.h" @@ -19,6 +20,7 @@ #include "BKE_DerivedMesh.h" #include "BKE_context.h" +#include "BKE_customdata.h" #include "BKE_editmesh.h" #include "BKE_editmesh_bvh.h" #include "BKE_global.h" @@ -1639,21 +1641,22 @@ void EDBM_project_snap_verts( float mval[2], co_proj[3]; if (ED_view3d_project_float_object(region, eve->co, mval, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { - if (ED_transform_snap_object_project_view3d(snap_context, - depsgraph, - region, - CTX_wm_view3d(C), - SCE_SNAP_MODE_FACE, - &(const struct SnapObjectParams){ - .snap_select = SNAP_NOT_ACTIVE, - .edit_mode_type = SNAP_GEOM_FINAL, - .use_occlusion_test = true, - }, - mval, - NULL, - NULL, - co_proj, - NULL)) { + if (ED_transform_snap_object_project_view3d( + snap_context, + depsgraph, + region, + CTX_wm_view3d(C), + SCE_SNAP_MODE_FACE, + &(const struct SnapObjectParams){ + .snap_target_select = SCE_SNAP_TARGET_NOT_ACTIVE, + .edit_mode_type = SNAP_GEOM_FINAL, + .use_occlusion_test = true, + }, + mval, + NULL, + NULL, + co_proj, + NULL)) { mul_v3_m4v3(eve->co, obedit->imat, co_proj); } } diff --git a/source/blender/editors/mesh/mesh_data.c b/source/blender/editors/mesh/mesh_data.cc index d11f0b490c1..67834bf05ce 100644 --- a/source/blender/editors/mesh/mesh_data.c +++ b/source/blender/editors/mesh/mesh_data.cc @@ -13,7 +13,7 @@ #include "DNA_scene_types.h" #include "DNA_view3d_types.h" -#include "BLI_alloca.h" +#include "BLI_array.hh" #include "BLI_math.h" #include "BLI_utildefines.h" @@ -43,10 +43,12 @@ #include "mesh_intern.h" /* own include */ +using blender::Array; + static CustomData *mesh_customdata_get_type(Mesh *me, const char htype, int *r_tot) { CustomData *data; - BMesh *bm = (me->edit_mesh) ? me->edit_mesh->bm : NULL; + BMesh *bm = (me->edit_mesh) ? me->edit_mesh->bm : nullptr; int tot; switch (htype) { @@ -93,7 +95,7 @@ static CustomData *mesh_customdata_get_type(Mesh *me, const char htype, int *r_t default: BLI_assert(0); tot = 0; - data = NULL; + data = nullptr; break; } @@ -172,7 +174,7 @@ static void mesh_uv_reset_array(float **fuv, const int len) static void mesh_uv_reset_bmface(BMFace *f, const int cd_loop_uv_offset) { - float **fuv = BLI_array_alloca(fuv, f->len); + Array<float *, BM_DEFAULT_NGON_STACK_SIZE> fuv(f->len); BMIter liter; BMLoop *l; int i; @@ -181,21 +183,21 @@ static void mesh_uv_reset_bmface(BMFace *f, const int cd_loop_uv_offset) fuv[i] = ((MLoopUV *)BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset))->uv; } - mesh_uv_reset_array(fuv, f->len); + mesh_uv_reset_array(fuv.data(), f->len); } static void mesh_uv_reset_mface(MPoly *mp, MLoopUV *mloopuv) { - float **fuv = BLI_array_alloca(fuv, mp->totloop); + Array<float *, BM_DEFAULT_NGON_STACK_SIZE> fuv(mp->totloop); for (int i = 0; i < mp->totloop; i++) { fuv[i] = mloopuv[mp->loopstart + i].uv; } - mesh_uv_reset_array(fuv, mp->totloop); + mesh_uv_reset_array(fuv.data(), mp->totloop); } -void ED_mesh_uv_loop_reset_ex(struct Mesh *me, const int layernum) +void ED_mesh_uv_loop_reset_ex(Mesh *me, const int layernum) { BMEditMesh *em = me->edit_mesh; @@ -219,7 +221,7 @@ void ED_mesh_uv_loop_reset_ex(struct Mesh *me, const int layernum) else { /* Collect Mesh UVs */ BLI_assert(CustomData_has_layer(&me->ldata, CD_MLOOPUV)); - MLoopUV *mloopuv = CustomData_get_layer_n(&me->ldata, CD_MLOOPUV, layernum); + MLoopUV *mloopuv = (MLoopUV *)CustomData_get_layer_n(&me->ldata, CD_MLOOPUV, layernum); for (int i = 0; i < me->totpoly; i++) { mesh_uv_reset_mface(&me->mpoly[i], mloopuv); @@ -229,7 +231,7 @@ void ED_mesh_uv_loop_reset_ex(struct Mesh *me, const int layernum) DEG_id_tag_update(&me->id, 0); } -void ED_mesh_uv_loop_reset(struct bContext *C, struct Mesh *me) +void ED_mesh_uv_loop_reset(bContext *C, Mesh *me) { /* could be ldata or pdata */ CustomData *ldata = GET_CD_DATA(me, ldata); @@ -239,7 +241,7 @@ void ED_mesh_uv_loop_reset(struct bContext *C, struct Mesh *me) WM_event_add_notifier(C, NC_GEOM | ND_DATA, me); } -int ED_mesh_uv_texture_add( +int ED_mesh_uv_add( Mesh *me, const char *name, const bool active_set, const bool do_init, ReportList *reports) { /* NOTE: keep in sync with #ED_mesh_color_add. */ @@ -284,7 +286,7 @@ int ED_mesh_uv_texture_add( is_init = true; } else { - CustomData_add_layer_named(&me->ldata, CD_MLOOPUV, CD_DEFAULT, NULL, me->totloop, name); + CustomData_add_layer_named(&me->ldata, CD_MLOOPUV, CD_DEFAULT, nullptr, me->totloop, name); } if (active_set || layernum_dst == 0) { @@ -305,7 +307,7 @@ int ED_mesh_uv_texture_add( return layernum_dst; } -void ED_mesh_uv_texture_ensure(struct Mesh *me, const char *name) +void ED_mesh_uv_ensure(Mesh *me, const char *name) { BMEditMesh *em; int layernum_dst; @@ -315,25 +317,25 @@ void ED_mesh_uv_texture_ensure(struct Mesh *me, const char *name) layernum_dst = CustomData_number_of_layers(&em->bm->ldata, CD_MLOOPUV); if (layernum_dst == 0) { - ED_mesh_uv_texture_add(me, name, true, true, NULL); + ED_mesh_uv_add(me, name, true, true, nullptr); } } else { layernum_dst = CustomData_number_of_layers(&me->ldata, CD_MLOOPUV); if (layernum_dst == 0) { - ED_mesh_uv_texture_add(me, name, true, true, NULL); + ED_mesh_uv_add(me, name, true, true, nullptr); } } } -bool ED_mesh_uv_texture_remove_index(Mesh *me, const int n) +bool ED_mesh_uv_remove_index(Mesh *me, const int n) { CustomData *ldata = GET_CD_DATA(me, ldata); CustomDataLayer *cdlu; int index; index = CustomData_get_layer_index_n(ldata, CD_MLOOPUV, n); - cdlu = (index == -1) ? NULL : &ldata->layers[index]; + cdlu = (index == -1) ? nullptr : &ldata->layers[index]; if (!cdlu) { return false; @@ -346,24 +348,22 @@ bool ED_mesh_uv_texture_remove_index(Mesh *me, const int n) return true; } -bool ED_mesh_uv_texture_remove_active(Mesh *me) +bool ED_mesh_uv_remove_active(Mesh *me) { - /* texpoly/uv are assumed to be in sync */ CustomData *ldata = GET_CD_DATA(me, ldata); const int n = CustomData_get_active_layer(ldata, CD_MLOOPUV); if (n != -1) { - return ED_mesh_uv_texture_remove_index(me, n); + return ED_mesh_uv_remove_index(me, n); } return false; } -bool ED_mesh_uv_texture_remove_named(Mesh *me, const char *name) +bool ED_mesh_uv_remove_named(Mesh *me, const char *name) { - /* texpoly/uv are assumed to be in sync */ CustomData *ldata = GET_CD_DATA(me, ldata); const int n = CustomData_get_named_layer(ldata, CD_MLOOPUV, name); if (n != -1) { - return ED_mesh_uv_texture_remove_index(me, n); + return ED_mesh_uv_remove_index(me, n); } return false; } @@ -371,7 +371,7 @@ bool ED_mesh_uv_texture_remove_named(Mesh *me, const char *name) int ED_mesh_color_add( Mesh *me, const char *name, const bool active_set, const bool do_init, ReportList *reports) { - /* NOTE: keep in sync with #ED_mesh_uv_texture_add. */ + /* NOTE: keep in sync with #ED_mesh_uv_add. */ BMEditMesh *em; int layernum; @@ -409,7 +409,7 @@ int ED_mesh_color_add( } else { CustomData_add_layer_named( - &me->ldata, CD_PROP_BYTE_COLOR, CD_DEFAULT, NULL, me->totloop, name); + &me->ldata, CD_PROP_BYTE_COLOR, CD_DEFAULT, nullptr, me->totloop, name); } if (active_set || layernum == 0) { @@ -425,14 +425,14 @@ int ED_mesh_color_add( return layernum; } -bool ED_mesh_color_ensure(struct Mesh *me, const char *name) +bool ED_mesh_color_ensure(Mesh *me, const char *name) { - BLI_assert(me->edit_mesh == NULL); + BLI_assert(me->edit_mesh == nullptr); CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id); if (!layer) { CustomData_add_layer_named( - &me->ldata, CD_PROP_BYTE_COLOR, CD_DEFAULT, NULL, me->totloop, name); + &me->ldata, CD_PROP_BYTE_COLOR, CD_DEFAULT, nullptr, me->totloop, name); layer = me->ldata.layers + CustomData_get_layer_index(&me->ldata, CD_PROP_BYTE_COLOR); BKE_id_attributes_active_color_set(&me->id, layer); @@ -441,7 +441,7 @@ bool ED_mesh_color_ensure(struct Mesh *me, const char *name) DEG_id_tag_update(&me->id, 0); - return (layer != NULL); + return (layer != nullptr); } bool ED_mesh_color_remove_index(Mesh *me, const int n) @@ -451,7 +451,7 @@ bool ED_mesh_color_remove_index(Mesh *me, const int n) int index; index = CustomData_get_layer_index_n(ldata, CD_PROP_BYTE_COLOR, n); - cdl = (index == -1) ? NULL : &ldata->layers[index]; + cdl = (index == -1) ? nullptr : &ldata->layers[index]; if (!cdl) { return false; @@ -487,7 +487,7 @@ bool ED_mesh_color_remove_named(Mesh *me, const char *name) static bool layers_poll(bContext *C) { Object *ob = ED_object_context(C); - ID *data = (ob) ? ob->data : NULL; + ID *data = (ob) ? static_cast<ID *>(ob->data) : nullptr; return (ob && !ID_IS_LINKED(ob) && !ID_IS_OVERRIDE_LIBRARY(ob) && ob->type == OB_MESH && data && !ID_IS_LINKED(data) && !ID_IS_OVERRIDE_LIBRARY(data)); } @@ -501,7 +501,7 @@ static bool sculpt_vertex_color_remove_poll(bContext *C) } Object *ob = ED_object_context(C); - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); CustomData *vdata = GET_CD_DATA(me, vdata); const int active = CustomData_get_active_layer(vdata, CD_PROP_COLOR); if (active != -1) { @@ -514,7 +514,7 @@ static bool sculpt_vertex_color_remove_poll(bContext *C) int ED_mesh_sculpt_color_add( Mesh *me, const char *name, const bool active_set, const bool do_init, ReportList *reports) { - /* NOTE: keep in sync with #ED_mesh_uv_texture_add. */ + /* NOTE: keep in sync with #ED_mesh_uv_add. */ BMEditMesh *em; int layernum; @@ -549,12 +549,14 @@ int ED_mesh_sculpt_color_add( } if (CustomData_has_layer(&me->vdata, CD_PROP_COLOR) && do_init) { - MPropCol *color_data = CustomData_get_layer(&me->vdata, CD_PROP_COLOR); + const MPropCol *color_data = (const MPropCol *)CustomData_get_layer(&me->vdata, + CD_PROP_COLOR); CustomData_add_layer_named( - &me->vdata, CD_PROP_COLOR, CD_DUPLICATE, color_data, me->totvert, name); + &me->vdata, CD_PROP_COLOR, CD_DUPLICATE, (MPropCol *)color_data, me->totvert, name); } else { - CustomData_add_layer_named(&me->vdata, CD_PROP_COLOR, CD_DEFAULT, NULL, me->totvert, name); + CustomData_add_layer_named( + &me->vdata, CD_PROP_COLOR, CD_DEFAULT, nullptr, me->totvert, name); } if (active_set || layernum == 0) { @@ -570,18 +572,18 @@ int ED_mesh_sculpt_color_add( return layernum; } -bool ED_mesh_sculpt_color_ensure(struct Mesh *me, const char *name) +bool ED_mesh_sculpt_color_ensure(Mesh *me, const char *name) { - BLI_assert(me->edit_mesh == NULL); + BLI_assert(me->edit_mesh == nullptr); if (me->totvert && !CustomData_has_layer(&me->vdata, CD_PROP_COLOR)) { - CustomData_add_layer_named(&me->vdata, CD_PROP_COLOR, CD_DEFAULT, NULL, me->totvert, name); + CustomData_add_layer_named(&me->vdata, CD_PROP_COLOR, CD_DEFAULT, nullptr, me->totvert, name); BKE_mesh_update_customdata_pointers(me, true); } DEG_id_tag_update(&me->id, 0); - return (me->mloopcol != NULL); + return (me->mloopcol != nullptr); } bool ED_mesh_sculpt_color_remove_index(Mesh *me, const int n) @@ -591,7 +593,7 @@ bool ED_mesh_sculpt_color_remove_index(Mesh *me, const int n) int index; index = CustomData_get_layer_index_n(vdata, CD_PROP_COLOR, n); - cdl = (index == -1) ? NULL : &vdata->layers[index]; + cdl = (index == -1) ? nullptr : &vdata->layers[index]; if (!cdl) { return false; @@ -631,7 +633,7 @@ static bool uv_texture_remove_poll(bContext *C) } Object *ob = ED_object_context(C); - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); CustomData *ldata = GET_CD_DATA(me, ldata); const int active = CustomData_get_active_layer(ldata, CD_MLOOPUV); if (active != -1) { @@ -644,16 +646,16 @@ static bool uv_texture_remove_poll(bContext *C) static int mesh_uv_texture_add_exec(bContext *C, wmOperator *op) { Object *ob = ED_object_context(C); - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); - if (ED_mesh_uv_texture_add(me, NULL, true, true, op->reports) == -1) { + if (ED_mesh_uv_add(me, nullptr, true, true, op->reports) == -1) { return OPERATOR_CANCELLED; } if (ob->mode & OB_MODE_TEXTURE_PAINT) { Scene *scene = CTX_data_scene(C); - ED_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL); - WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL); + ED_paint_proj_mesh_data_check(scene, ob, nullptr, nullptr, nullptr, nullptr); + WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, nullptr); } return OPERATOR_FINISHED; @@ -677,16 +679,16 @@ void MESH_OT_uv_texture_add(wmOperatorType *ot) static int mesh_uv_texture_remove_exec(bContext *C, wmOperator *UNUSED(op)) { Object *ob = ED_object_context(C); - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); - if (!ED_mesh_uv_texture_remove_active(me)) { + if (!ED_mesh_uv_remove_active(me)) { return OPERATOR_CANCELLED; } if (ob->mode & OB_MODE_TEXTURE_PAINT) { Scene *scene = CTX_data_scene(C); - ED_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL); - WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL); + ED_paint_proj_mesh_data_check(scene, ob, nullptr, nullptr, nullptr, nullptr); + WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, nullptr); } return OPERATOR_FINISHED; @@ -716,7 +718,7 @@ static bool vertex_color_remove_poll(bContext *C) } Object *ob = ED_object_context(C); - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); CustomData *ldata = GET_CD_DATA(me, ldata); const int active = CustomData_get_active_layer(ldata, CD_PROP_BYTE_COLOR); if (active != -1) { @@ -729,9 +731,9 @@ static bool vertex_color_remove_poll(bContext *C) static int mesh_vertex_color_add_exec(bContext *C, wmOperator *op) { Object *ob = ED_object_context(C); - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); - if (ED_mesh_color_add(me, NULL, true, true, op->reports) == -1) { + if (ED_mesh_color_add(me, nullptr, true, true, op->reports) == -1) { return OPERATOR_CANCELLED; } @@ -756,7 +758,7 @@ void MESH_OT_vertex_color_add(wmOperatorType *ot) static int mesh_vertex_color_remove_exec(bContext *C, wmOperator *UNUSED(op)) { Object *ob = ED_object_context(C); - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); if (!ED_mesh_color_remove_active(me)) { return OPERATOR_CANCELLED; @@ -785,9 +787,9 @@ void MESH_OT_vertex_color_remove(wmOperatorType *ot) static int mesh_sculpt_vertex_color_add_exec(bContext *C, wmOperator *op) { Object *ob = ED_object_context(C); - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); - if (ED_mesh_sculpt_color_add(me, NULL, true, true, op->reports) == -1) { + if (ED_mesh_sculpt_color_add(me, nullptr, true, true, op->reports) == -1) { return OPERATOR_CANCELLED; } @@ -812,7 +814,7 @@ void MESH_OT_sculpt_vertex_color_add(wmOperatorType *ot) static int mesh_sculpt_vertex_color_remove_exec(bContext *C, wmOperator *UNUSED(op)) { Object *ob = ED_object_context(C); - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); if (!ED_mesh_sculpt_color_remove_active(me)) { return OPERATOR_CANCELLED; @@ -868,7 +870,7 @@ static bool mesh_customdata_mask_clear_poll(bContext *C) { Object *ob = ED_object_context(C); if (ob && ob->type == OB_MESH) { - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); /* special case - can't run this if we're in sculpt mode */ if (ob->mode & OB_MODE_SCULPT) { @@ -925,7 +927,7 @@ static int mesh_customdata_skin_state(bContext *C) Object *ob = ED_object_context(C); if (ob && ob->type == OB_MESH) { - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); if (!ID_IS_LINKED(me) && !ID_IS_OVERRIDE_LIBRARY(me)) { CustomData *data = GET_CD_DATA(me, vdata); return CustomData_has_layer(data, CD_MVERT_SKIN); @@ -942,7 +944,7 @@ static bool mesh_customdata_skin_add_poll(bContext *C) static int mesh_customdata_skin_add_exec(bContext *C, wmOperator *UNUSED(op)) { Object *ob = ED_object_context(C); - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); BKE_mesh_ensure_skin_customdata(me); @@ -1025,7 +1027,7 @@ static int mesh_customdata_custom_splitnormals_add_exec(bContext *C, wmOperator me->smoothresh); } - CustomData_add_layer(data, CD_CUSTOMLOOPNORMAL, CD_DEFAULT, NULL, me->totloop); + CustomData_add_layer(data, CD_CUSTOMLOOPNORMAL, CD_DEFAULT, nullptr, me->totloop); } DEG_id_tag_update(&me->id, 0); @@ -1057,7 +1059,7 @@ static int mesh_customdata_custom_splitnormals_clear_exec(bContext *C, wmOperato if (BKE_mesh_has_custom_loop_normals(me)) { BMEditMesh *em = me->edit_mesh; - if (em != NULL && em->bm->lnor_spacearr != NULL) { + if (em != nullptr && em->bm->lnor_spacearr != nullptr) { BKE_lnor_spacearr_clear(em->bm->lnor_spacearr); } return mesh_customdata_clear_exec__internal(C, BM_LOOP, CD_CUSTOMLOOPNORMAL); @@ -1114,7 +1116,7 @@ static void mesh_add_verts(Mesh *mesh, int len) CustomData_copy_data(&mesh->vdata, &vdata, 0, 0, mesh->totvert); if (!CustomData_has_layer(&vdata, CD_MVERT)) { - CustomData_add_layer(&vdata, CD_MVERT, CD_CALLOC, NULL, totvert); + CustomData_add_layer(&vdata, CD_MVERT, CD_CALLOC, nullptr, totvert); } CustomData_free(&mesh->vdata, mesh->totvert); @@ -1152,7 +1154,7 @@ static void mesh_add_edges(Mesh *mesh, int len) CustomData_copy_data(&mesh->edata, &edata, 0, 0, mesh->totedge); if (!CustomData_has_layer(&edata, CD_MEDGE)) { - CustomData_add_layer(&edata, CD_MEDGE, CD_CALLOC, NULL, totedge); + CustomData_add_layer(&edata, CD_MEDGE, CD_CALLOC, nullptr, totedge); } CustomData_free(&mesh->edata, mesh->totedge); @@ -1186,7 +1188,7 @@ static void mesh_add_loops(Mesh *mesh, int len) CustomData_copy_data(&mesh->ldata, &ldata, 0, 0, mesh->totloop); if (!CustomData_has_layer(&ldata, CD_MLOOP)) { - CustomData_add_layer(&ldata, CD_MLOOP, CD_CALLOC, NULL, totloop); + CustomData_add_layer(&ldata, CD_MLOOP, CD_CALLOC, nullptr, totloop); } BKE_mesh_runtime_clear_cache(mesh); @@ -1215,7 +1217,7 @@ static void mesh_add_polys(Mesh *mesh, int len) CustomData_copy_data(&mesh->pdata, &pdata, 0, 0, mesh->totpoly); if (!CustomData_has_layer(&pdata, CD_MPOLY)) { - CustomData_add_layer(&pdata, CD_MPOLY, CD_CALLOC, NULL, totpoly); + CustomData_add_layer(&pdata, CD_MPOLY, CD_CALLOC, nullptr, totpoly); } CustomData_free(&mesh->pdata, mesh->totpoly); @@ -1413,21 +1415,21 @@ void ED_mesh_report_mirror(wmOperator *op, int totmirr, int totfail) ED_mesh_report_mirror_ex(op, totmirr, totfail, SCE_SELECT_VERTEX); } -Mesh *ED_mesh_context(struct bContext *C) +Mesh *ED_mesh_context(bContext *C) { - Mesh *mesh = CTX_data_pointer_get_type(C, "mesh", &RNA_Mesh).data; - if (mesh != NULL) { + Mesh *mesh = static_cast<Mesh *>(CTX_data_pointer_get_type(C, "mesh", &RNA_Mesh).data); + if (mesh != nullptr) { return mesh; } Object *ob = ED_object_active_context(C); - if (ob == NULL) { - return NULL; + if (ob == nullptr) { + return nullptr; } ID *data = (ID *)ob->data; - if (data == NULL || GS(data->name) != ID_ME) { - return NULL; + if (data == nullptr || GS(data->name) != ID_ME) { + return nullptr; } return (Mesh *)data; diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h index b39a0b90f6d..303234df48c 100644 --- a/source/blender/editors/mesh/mesh_intern.h +++ b/source/blender/editors/mesh/mesh_intern.h @@ -9,6 +9,10 @@ #pragma once +#ifdef __cplusplus +extern "C" { +#endif + struct BMEditMesh; struct BMElem; struct BMOperator; @@ -170,8 +174,7 @@ void MESH_OT_knife_project(struct wmOperatorType *ot); /** * \param use_tag: When set, tag all faces inside the polylines. */ -void EDBM_mesh_knife(struct bContext *C, - struct ViewContext *vc, +void EDBM_mesh_knife(struct ViewContext *vc, struct LinkNode *polys, bool use_tag, bool cut_through); @@ -301,7 +304,7 @@ void MESH_OT_mark_freestyle_edge(struct wmOperatorType *ot); void MESH_OT_mark_freestyle_face(struct wmOperatorType *ot); #endif -/* *** mesh_data.c *** */ +/* *** mesh_data.cc *** */ void MESH_OT_uv_texture_add(struct wmOperatorType *ot); void MESH_OT_uv_texture_remove(struct wmOperatorType *ot); @@ -314,3 +317,7 @@ void MESH_OT_customdata_skin_add(struct wmOperatorType *ot); void MESH_OT_customdata_skin_clear(struct wmOperatorType *ot); void MESH_OT_customdata_custom_splitnormals_add(struct wmOperatorType *ot); void MESH_OT_customdata_custom_splitnormals_clear(struct wmOperatorType *ot); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/editors/mesh/meshtools.c b/source/blender/editors/mesh/meshtools.cc index 9575fde68f0..9e28e1bafdd 100644 --- a/source/blender/editors/mesh/meshtools.c +++ b/source/blender/editors/mesh/meshtools.cc @@ -21,10 +21,8 @@ #include "DNA_view3d_types.h" #include "DNA_workspace_types.h" -#include "BLI_blenlib.h" -#include "BLI_math.h" - #include "BKE_context.h" +#include "BKE_customdata.h" #include "BKE_deform.h" #include "BKE_editmesh.h" #include "BKE_key.h" @@ -91,7 +89,7 @@ static void join_mesh_single(Depsgraph *depsgraph, { int a, b; - Mesh *me = ob_src->data; + Mesh *me = static_cast<Mesh *>(ob_src->data); MVert *mvert = *mvert_pp; MEdge *medge = *medge_pp; MLoop *mloop = *mloop_pp; @@ -106,12 +104,13 @@ static void join_mesh_single(Depsgraph *depsgraph, CustomData_copy_data_named(&me->vdata, vdata, 0, *vertofs, me->totvert); /* vertex groups */ - MDeformVert *dvert = CustomData_get(vdata, *vertofs, CD_MDEFORMVERT); - MDeformVert *dvert_src = CustomData_get(&me->vdata, 0, CD_MDEFORMVERT); + MDeformVert *dvert = (MDeformVert *)CustomData_get(vdata, *vertofs, CD_MDEFORMVERT); + const MDeformVert *dvert_src = (const MDeformVert *)CustomData_get( + &me->vdata, 0, CD_MDEFORMVERT); /* Remap to correct new vgroup indices, if needed. */ if (dvert_src) { - BLI_assert(dvert != NULL); + BLI_assert(dvert != nullptr); /* Build src to merged mapping of vgroup indices. */ int *vgroup_index_map; @@ -120,7 +119,7 @@ static void join_mesh_single(Depsgraph *depsgraph, ob_src, ob_dst, &vgroup_index_map_len); BKE_object_defgroup_index_map_apply( dvert, me->totvert, vgroup_index_map, vgroup_index_map_len); - if (vgroup_index_map != NULL) { + if (vgroup_index_map != nullptr) { MEM_freeN(vgroup_index_map); } } @@ -150,11 +149,11 @@ static void join_mesh_single(Depsgraph *depsgraph, float(*cos)[3] = ((float(*)[3])kb->data) + *vertofs; /* Check if this mesh has such a shape-key. */ - KeyBlock *okb = me->key ? BKE_keyblock_find_name(me->key, kb->name) : NULL; + KeyBlock *okb = me->key ? BKE_keyblock_find_name(me->key, kb->name) : nullptr; if (okb) { /* copy this mesh's shape-key to the destination shape-key * (need to transform first) */ - float(*ocos)[3] = okb->data; + float(*ocos)[3] = static_cast<float(*)[3]>(okb->data); for (a = 0; a < me->totvert; a++, cos++, ocos++) { copy_v3_v3(*cos, *ocos); mul_m4_v3(cmat, *cos); @@ -180,10 +179,10 @@ static void join_mesh_single(Depsgraph *depsgraph, float(*cos)[3] = ((float(*)[3])kb->data) + *vertofs; /* Check if this was one of the original shape-keys. */ - KeyBlock *okb = nkey ? BKE_keyblock_find_name(nkey, kb->name) : NULL; + KeyBlock *okb = nkey ? BKE_keyblock_find_name(nkey, kb->name) : nullptr; if (okb) { /* copy this mesh's shape-key to the destination shape-key */ - float(*ocos)[3] = okb->data; + float(*ocos)[3] = static_cast<float(*)[3]>(okb->data); for (a = 0; a < me->totvert; a++, cos++, ocos++) { copy_v3_v3(*cos, *ocos); } @@ -254,17 +253,17 @@ static void join_mesh_single(Depsgraph *depsgraph, } /* Face maps. */ - int *fmap = CustomData_get(pdata, *polyofs, CD_FACEMAP); - int *fmap_src = CustomData_get(&me->pdata, 0, CD_FACEMAP); + int *fmap = (int *)CustomData_get(pdata, *polyofs, CD_FACEMAP); + const int *fmap_src = (const int *)CustomData_get(&me->pdata, 0, CD_FACEMAP); /* Remap to correct new face-map indices, if needed. */ if (fmap_src) { - BLI_assert(fmap != NULL); + BLI_assert(fmap != nullptr); int *fmap_index_map; int fmap_index_map_len; fmap_index_map = BKE_object_facemap_index_map_create(ob_src, ob_dst, &fmap_index_map_len); BKE_object_facemap_index_map_apply(fmap, me->totpoly, fmap_index_map, fmap_index_map_len); - if (fmap_index_map != NULL) { + if (fmap_index_map != nullptr) { MEM_freeN(fmap_index_map); } } @@ -290,7 +289,7 @@ static void mesh_join_offset_face_sets_ID(const Mesh *mesh, int *face_set_offset return; } - int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS); + int *face_sets = (int *)CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS); if (!face_sets) { return; } @@ -317,20 +316,18 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); Object *ob = CTX_data_active_object(C); - Material **matar = NULL, *ma; + Material **matar = nullptr, *ma; Mesh *me; - MVert *mvert = NULL; - MEdge *medge = NULL; - MPoly *mpoly = NULL; - MLoop *mloop = NULL; - Key *key, *nkey = NULL; - KeyBlock *kb, *kbn; + MVert *mvert = nullptr; + MEdge *medge = nullptr; + MPoly *mpoly = nullptr; + MLoop *mloop = nullptr; + Key *key, *nkey = nullptr; float imat[4][4]; int a, b, totcol, totmat = 0, totedge = 0, totvert = 0; - int totloop = 0, totpoly = 0, vertofs, *matmap = NULL; + int totloop = 0, totpoly = 0, vertofs, *matmap = nullptr; int i, haskey = 0, edgeofs, loopofs, polyofs; bool ok = false, join_parent = false; - bDeformGroup *dg, *odg; CustomData vdata, edata, fdata, ldata, pdata; if (ob->mode & OB_MODE_EDIT) { @@ -349,7 +346,7 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) /* count & check */ CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) { if (ob_iter->type == OB_MESH) { - me = ob_iter->data; + me = static_cast<Mesh *>(ob_iter->data); totvert += me->totvert; totedge += me->totedge; @@ -361,7 +358,7 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) ok = true; } - if ((ob->parent != NULL) && (ob_iter == ob->parent)) { + if ((ob->parent != nullptr) && (ob_iter == ob->parent)) { join_parent = true; } @@ -376,7 +373,7 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) /* Apply parent transform if the active object's parent was joined to it. * NOTE: This doesn't apply recursive parenting. */ if (join_parent) { - ob->parent = NULL; + ob->parent = nullptr; BKE_object_apply_mat4_ex(ob, ob->obmat, ob->parent, ob->parentinv, false); } @@ -416,12 +413,12 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) /* new material indices and material array */ if (totmat) { - matar = MEM_callocN(sizeof(*matar) * totmat, "join_mesh matar"); - matmap = MEM_callocN(sizeof(*matmap) * totmat, "join_mesh matmap"); + matar = static_cast<Material **>(MEM_callocN(sizeof(*matar) * totmat, __func__)); + matmap = static_cast<int *>(MEM_callocN(sizeof(*matmap) * totmat, __func__)); } totcol = ob->totcol; - /* obact materials in new main array, is nicer start! */ + /* Active object materials in new main array, is nicer start! */ for (a = 0; a < ob->totcol; a++) { matar[a] = BKE_object_material_get(ob, a + 1); id_us_plus((ID *)matar[a]); @@ -438,7 +435,7 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) nkey = (Key *)BKE_id_copy(bmain, &key->id); /* for all keys in old block, clear data-arrays */ - for (kb = key->block.first; kb; kb = kb->next) { + LISTBASE_FOREACH (KeyBlock *, kb, &key->block) { if (kb->data) { MEM_freeN(kb->data); } @@ -462,13 +459,14 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) { /* only act if a mesh, and not the one we're joining to */ if ((ob != ob_iter) && (ob_iter->type == OB_MESH)) { - me = ob_iter->data; + me = static_cast<Mesh *>(ob_iter->data); /* Join this object's vertex groups to the base one's */ - for (dg = me->vertex_group_names.first; dg; dg = dg->next) { + LISTBASE_FOREACH (bDeformGroup *, dg, &me->vertex_group_names) { /* See if this group exists in the object (if it doesn't, add it to the end) */ if (!BKE_object_defgroup_find_name(ob, dg->name)) { - odg = MEM_mallocN(sizeof(bDeformGroup), "join deformGroup"); + bDeformGroup *odg = static_cast<bDeformGroup *>( + MEM_mallocN(sizeof(bDeformGroup), __func__)); memcpy(odg, dg, sizeof(bDeformGroup)); BLI_addtail(&mesh_active->vertex_group_names, odg); } @@ -481,8 +479,8 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) /* Join this object's face maps to the base one's. */ LISTBASE_FOREACH (bFaceMap *, fmap, &ob_iter->fmaps) { /* See if this group exists in the object (if it doesn't, add it to the end) */ - if (BKE_object_facemap_find_name(ob, fmap->name) == NULL) { - bFaceMap *fmap_new = MEM_mallocN(sizeof(bFaceMap), "join faceMap"); + if (BKE_object_facemap_find_name(ob, fmap->name) == nullptr) { + bFaceMap *fmap_new = static_cast<bFaceMap *>(MEM_mallocN(sizeof(bFaceMap), __func__)); memcpy(fmap_new, fmap, sizeof(bFaceMap)); BLI_addtail(&ob->fmaps, fmap_new); } @@ -522,13 +520,15 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) * check if destination mesh already has matching entries too. */ if (me->key && key) { /* for remapping KeyBlock.relative */ - int *index_map = MEM_mallocN(sizeof(int) * me->key->totkey, __func__); - KeyBlock **kb_map = MEM_mallocN(sizeof(KeyBlock *) * me->key->totkey, __func__); + int *index_map = static_cast<int *>( + MEM_mallocN(sizeof(int) * me->key->totkey, __func__)); + KeyBlock **kb_map = static_cast<KeyBlock **>( + MEM_mallocN(sizeof(KeyBlock *) * me->key->totkey, __func__)); - for (kb = me->key->block.first, i = 0; kb; kb = kb->next, i++) { + LISTBASE_FOREACH_INDEX (KeyBlock *, kb, &me->key->block, i) { BLI_assert(i < me->key->totkey); - kbn = BKE_keyblock_find_name(key, kb->name); + KeyBlock *kbn = BKE_keyblock_find_name(key, kb->name); /* if key doesn't exist in destination mesh, add it */ if (kbn) { index_map[i] = BLI_findindex(&key->block, kbn); @@ -549,7 +549,7 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) } /* remap relative index values */ - for (kb = me->key->block.first, i = 0; kb; kb = kb->next, i++) { + LISTBASE_FOREACH_INDEX (KeyBlock *, kb, &me->key->block, i) { /* sanity check, should always be true */ if (LIKELY(kb->relative < me->key->totkey)) { kb_map[i]->relative = index_map[kb->relative]; @@ -571,10 +571,10 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) CustomData_reset(&ldata); CustomData_reset(&pdata); - mvert = CustomData_add_layer(&vdata, CD_MVERT, CD_CALLOC, NULL, totvert); - medge = CustomData_add_layer(&edata, CD_MEDGE, CD_CALLOC, NULL, totedge); - mloop = CustomData_add_layer(&ldata, CD_MLOOP, CD_CALLOC, NULL, totloop); - mpoly = CustomData_add_layer(&pdata, CD_MPOLY, CD_CALLOC, NULL, totpoly); + mvert = (MVert *)CustomData_add_layer(&vdata, CD_MVERT, CD_CALLOC, nullptr, totvert); + medge = (MEdge *)CustomData_add_layer(&edata, CD_MEDGE, CD_CALLOC, nullptr, totedge); + mloop = (MLoop *)CustomData_add_layer(&ldata, CD_MLOOP, CD_CALLOC, nullptr, totloop); + mpoly = (MPoly *)CustomData_add_layer(&pdata, CD_MPOLY, CD_CALLOC, nullptr, totpoly); vertofs = 0; edgeofs = 0; @@ -661,7 +661,7 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) CTX_DATA_END; /* return to mesh we're merging to */ - me = ob->data; + me = static_cast<Mesh *>(ob->data); CustomData_free(&me->vdata, me->totvert); CustomData_free(&me->edata, me->totedge); @@ -703,8 +703,8 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) if (totcol) { me->mat = matar; - ob->mat = MEM_callocN(sizeof(*ob->mat) * totcol, "join obmatar"); - ob->matbits = MEM_callocN(sizeof(*ob->matbits) * totcol, "join obmatbits"); + ob->mat = static_cast<Material **>(MEM_callocN(sizeof(*ob->mat) * totcol, __func__)); + ob->matbits = static_cast<char *>(MEM_callocN(sizeof(*ob->matbits) * totcol, __func__)); MEM_freeN(matmap); } @@ -751,8 +751,8 @@ int ED_mesh_shapes_join_objects_exec(bContext *C, wmOperator *op) Object *ob_active = CTX_data_active_object(C); Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Mesh *me = (Mesh *)ob_active->data; - Mesh *selme = NULL; - Mesh *me_deformed = NULL; + Mesh *selme = nullptr; + Mesh *me_deformed = nullptr; Key *key = me->key; KeyBlock *kb; bool ok = false, nonequal_verts = false; @@ -769,7 +769,7 @@ int ED_mesh_shapes_join_objects_exec(bContext *C, wmOperator *op) ok = true; } else { - nonequal_verts = 1; + nonequal_verts = true; } } } @@ -787,12 +787,12 @@ int ED_mesh_shapes_join_objects_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - if (key == NULL) { + if (key == nullptr) { key = me->key = BKE_key_add(bmain, (ID *)me); key->type = KEY_RELATIVE; /* first key added, so it was the basis. initialize it with the existing mesh */ - kb = BKE_keyblock_add(key, NULL); + kb = BKE_keyblock_add(key, nullptr); BKE_keyblock_convert_from_mesh(me, key, kb); } @@ -835,21 +835,21 @@ int ED_mesh_shapes_join_objects_exec(bContext *C, wmOperator *op) /** \name Mesh Topology Mirror API * \{ */ -static MirrTopoStore_t mesh_topo_store = {NULL, -1. - 1, -1}; +static MirrTopoStore_t mesh_topo_store = {nullptr, -1, -1, false}; BLI_INLINE void mesh_mirror_topo_table_get_meshes(Object *ob, Mesh *me_eval, Mesh **r_me_mirror, BMEditMesh **r_em_mirror) { - Mesh *me_mirror = NULL; - BMEditMesh *em_mirror = NULL; + Mesh *me_mirror = nullptr; + BMEditMesh *em_mirror = nullptr; - Mesh *me = ob->data; - if (me_eval != NULL) { + Mesh *me = static_cast<Mesh *>(ob->data); + if (me_eval != nullptr) { me_mirror = me_eval; } - else if (me->edit_mesh != NULL) { + else if (me->edit_mesh != nullptr) { em_mirror = me->edit_mesh; } else { @@ -892,7 +892,7 @@ static bool ed_mesh_mirror_topo_table_update(Object *ob, Mesh *me_eval) static int mesh_get_x_mirror_vert_spatial(Object *ob, Mesh *me_eval, int index) { - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); MVert *mvert = me_eval ? me_eval->mvert : me->mvert; float vec[3]; @@ -901,7 +901,7 @@ static int mesh_get_x_mirror_vert_spatial(Object *ob, Mesh *me_eval, int index) vec[1] = mvert->co[1]; vec[2] = mvert->co[2]; - return ED_mesh_mirror_spatial_table_lookup(ob, NULL, me_eval, vec); + return ED_mesh_mirror_spatial_table_lookup(ob, nullptr, me_eval, vec); } static int mesh_get_x_mirror_vert_topo(Object *ob, Mesh *mesh, int index) @@ -928,28 +928,25 @@ static BMVert *editbmesh_get_x_mirror_vert_spatial(Object *ob, BMEditMesh *em, c /* ignore nan verts */ if ((isfinite(co[0]) == false) || (isfinite(co[1]) == false) || (isfinite(co[2]) == false)) { - return NULL; + return nullptr; } vec[0] = -co[0]; vec[1] = co[1]; vec[2] = co[2]; - i = ED_mesh_mirror_spatial_table_lookup(ob, em, NULL, vec); + i = ED_mesh_mirror_spatial_table_lookup(ob, em, nullptr, vec); if (i != -1) { return BM_vert_at_index(em->bm, i); } - return NULL; + return nullptr; } -static BMVert *editbmesh_get_x_mirror_vert_topo(Object *ob, - struct BMEditMesh *em, - BMVert *eve, - int index) +static BMVert *editbmesh_get_x_mirror_vert_topo(Object *ob, BMEditMesh *em, BMVert *eve, int index) { intptr_t poinval; - if (!ed_mesh_mirror_topo_table_update(ob, NULL)) { - return NULL; + if (!ed_mesh_mirror_topo_table_update(ob, nullptr)) { + return nullptr; } if (index == -1) { @@ -965,7 +962,7 @@ static BMVert *editbmesh_get_x_mirror_vert_topo(Object *ob, } if (index == em->bm->totvert) { - return NULL; + return nullptr; } } @@ -974,15 +971,11 @@ static BMVert *editbmesh_get_x_mirror_vert_topo(Object *ob, if (poinval != -1) { return (BMVert *)(poinval); } - return NULL; + return nullptr; } -BMVert *editbmesh_get_x_mirror_vert(Object *ob, - struct BMEditMesh *em, - BMVert *eve, - const float co[3], - int index, - const bool use_topology) +BMVert *editbmesh_get_x_mirror_vert( + Object *ob, BMEditMesh *em, BMVert *eve, const float co[3], int index, const bool use_topology) { if (use_topology) { return editbmesh_get_x_mirror_vert_topo(ob, em, eve, index); @@ -992,7 +985,7 @@ BMVert *editbmesh_get_x_mirror_vert(Object *ob, int ED_mesh_mirror_get_vert(Object *ob, int index) { - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); BMEditMesh *em = me->edit_mesh; bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; int index_mirr; @@ -1004,7 +997,7 @@ int ED_mesh_mirror_get_vert(Object *ob, int index) index_mirr = eve_mirr ? BM_elem_index_get(eve_mirr) : -1; } else { - index_mirr = mesh_get_x_mirror_vert(ob, NULL, index, use_topology); + index_mirr = mesh_get_x_mirror_vert(ob, nullptr, index, use_topology); } return index_mirr; @@ -1021,7 +1014,7 @@ static float *editmesh_get_mirror_uv( /* ignore nan verts */ if (isnan(uv[0]) || !isfinite(uv[0]) || isnan(uv[1]) || !isfinite(uv[1])) { - return NULL; + return nullptr; } if (axis) { @@ -1061,14 +1054,14 @@ static float *editmesh_get_mirror_uv( } } - return NULL; + return nullptr; } #endif static uint mirror_facehash(const void *ptr) { - const MFace *mf = ptr; + const MFace *mf = static_cast<const MFace *>(ptr); uint v0, v1; if (mf->v4) { @@ -1121,21 +1114,21 @@ static bool mirror_facecmp(const void *a, const void *b) int *mesh_get_x_mirror_faces(Object *ob, BMEditMesh *em, Mesh *me_eval) { - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); MVert *mv, *mvert; MFace mirrormf, *mf, *hashmf, *mface; GHash *fhash; int *mirrorverts, *mirrorfaces; - BLI_assert(em == NULL); /* Does not work otherwise, currently... */ + BLI_assert(em == nullptr); /* Does not work otherwise, currently... */ const bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; const int totvert = me_eval ? me_eval->totvert : me->totvert; const int totface = me_eval ? me_eval->totface : me->totface; int a; - mirrorverts = MEM_callocN(sizeof(int) * totvert, "MirrorVerts"); - mirrorfaces = MEM_callocN(sizeof(int[2]) * totface, "MirrorFaces"); + mirrorverts = static_cast<int *>(MEM_callocN(sizeof(int) * totvert, "MirrorVerts")); + mirrorfaces = static_cast<int *>(MEM_callocN(sizeof(int[2]) * totface, "MirrorFaces")); mvert = me_eval ? me_eval->mvert : me->mvert; mface = me_eval ? me_eval->mface : me->mface; @@ -1165,7 +1158,7 @@ int *mesh_get_x_mirror_faces(Object *ob, BMEditMesh *em, Mesh *me_eval) SWAP(uint, mirrormf.v2, mirrormf.v4); } - hashmf = BLI_ghash_lookup(fhash, &mirrormf); + hashmf = static_cast<MFace *>(BLI_ghash_lookup(fhash, &mirrormf)); if (hashmf) { mirrorfaces[a * 2] = hashmf - mface; mirrorfaces[a * 2 + 1] = mirror_facerotation(&mirrormf, hashmf); @@ -1175,7 +1168,7 @@ int *mesh_get_x_mirror_faces(Object *ob, BMEditMesh *em, Mesh *me_eval) } } - BLI_ghash_free(fhash, NULL, NULL); + BLI_ghash_free(fhash, nullptr, nullptr); MEM_freeN(mirrorverts); return mirrorfaces; @@ -1186,7 +1179,7 @@ int *mesh_get_x_mirror_faces(Object *ob, BMEditMesh *em, Mesh *me_eval) bool ED_mesh_pick_face(bContext *C, Object *ob, const int mval[2], uint dist_px, uint *r_index) { ViewContext vc; - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); BLI_assert(me && GS(me->id.name) == ID_ME); @@ -1199,8 +1192,8 @@ bool ED_mesh_pick_face(bContext *C, Object *ob, const int mval[2], uint dist_px, ED_view3d_select_id_validate(&vc); if (dist_px) { - /* sample rect to increase chances of selecting, so that when clicking - * on an edge in the backbuf, we can still select a face */ + /* Sample rect to increase chances of selecting, so that when clicking + * on an edge in the back-buffer, we can still select a face. */ *r_index = DRW_select_buffer_find_nearest_to_point( vc.depsgraph, vc.region, vc.v3d, mval, 1, me->totpoly + 1, &dist_px); } @@ -1220,7 +1213,7 @@ bool ED_mesh_pick_face(bContext *C, Object *ob, const int mval[2], uint dist_px, static void ed_mesh_pick_face_vert__mpoly_find( /* context */ - struct ARegion *region, + ARegion *region, const float mval[2], /* mesh data (evaluated) */ const MPoly *mp, @@ -1250,14 +1243,14 @@ bool ED_mesh_pick_face_vert( { Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); uint poly_index; - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); BLI_assert(me && GS(me->id.name) == ID_ME); if (ED_mesh_pick_face(C, ob, mval, dist_px, &poly_index)) { Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); - struct ARegion *region = CTX_wm_region(C); + ARegion *region = CTX_wm_region(C); /* derived mesh to find deformed locations */ Mesh *me_eval = mesh_get_eval_final( @@ -1266,14 +1259,13 @@ bool ED_mesh_pick_face_vert( int v_idx_best = ORIGINDEX_NONE; /* find the vert closest to 'mval' */ - const float mval_f[2] = {UNPACK2(mval)}; + const float mval_f[2] = {(float)mval[0], (float)mval[1]}; float len_best = FLT_MAX; MPoly *me_eval_mpoly; MLoop *me_eval_mloop; MVert *me_eval_mvert; uint me_eval_mpoly_len; - const int *index_mp_to_orig; me_eval_mpoly = me_eval->mpoly; me_eval_mloop = me_eval->mloop; @@ -1281,7 +1273,7 @@ bool ED_mesh_pick_face_vert( me_eval_mpoly_len = me_eval->totpoly; - index_mp_to_orig = CustomData_get_layer(&me_eval->pdata, CD_ORIGINDEX); + const int *index_mp_to_orig = (const int *)CustomData_get_layer(&me_eval->pdata, CD_ORIGINDEX); /* tag all verts using this face */ if (index_mp_to_orig) { @@ -1313,8 +1305,8 @@ bool ED_mesh_pick_face_vert( /* map 'dm -> me' r_index if possible */ if (v_idx_best != ORIGINDEX_NONE) { - const int *index_mv_to_orig; - index_mv_to_orig = CustomData_get_layer(&me_eval->vdata, CD_ORIGINDEX); + const int *index_mv_to_orig = (const int *)CustomData_get_layer(&me_eval->vdata, + CD_ORIGINDEX); if (index_mv_to_orig) { v_idx_best = index_mv_to_orig[v_idx_best]; } @@ -1335,7 +1327,7 @@ bool ED_mesh_pick_face_vert( * * \return boolean true == Found */ -typedef struct VertPickData { +struct VertPickData { const MVert *mvert; const float *mval_f; /* [2] */ ARegion *region; @@ -1343,14 +1335,14 @@ typedef struct VertPickData { /* runtime */ float len_best; int v_idx_best; -} VertPickData; +}; static void ed_mesh_pick_vert__mapFunc(void *userData, int index, const float co[3], const float UNUSED(no[3])) { - VertPickData *data = userData; + VertPickData *data = static_cast<VertPickData *>(userData); if ((data->mvert[index].flag & ME_HIDE) == 0) { float sco[2]; @@ -1368,7 +1360,7 @@ bool ED_mesh_pick_vert( bContext *C, Object *ob, const int mval[2], uint dist_px, bool use_zbuf, uint *r_index) { ViewContext vc; - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); BLI_assert(me && GS(me->id.name) == ID_ME); @@ -1382,8 +1374,8 @@ bool ED_mesh_pick_vert( if (use_zbuf) { if (dist_px > 0) { - /* sample rect to increase chances of selecting, so that when clicking - * on an face in the backbuf, we can still select a vert */ + /* Sample rectangle to increase chances of selecting, so that when clicking + * on an face in the back-buffer, we can still select a vert. */ *r_index = DRW_select_buffer_find_nearest_to_point( vc.depsgraph, vc.region, vc.v3d, mval, 1, me->totvert + 1, &dist_px); } @@ -1405,16 +1397,16 @@ bool ED_mesh_pick_vert( /* derived mesh to find deformed locations */ Mesh *me_eval = mesh_get_eval_final(vc.depsgraph, scene_eval, ob_eval, &CD_MASK_BAREMESH); ARegion *region = vc.region; - RegionView3D *rv3d = region->regiondata; + RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata); /* find the vert closest to 'mval' */ const float mval_f[2] = {(float)mval[0], (float)mval[1]}; - VertPickData data = {NULL}; + VertPickData data = {nullptr}; ED_view3d_init_mats_rv3d(ob, rv3d); - if (me_eval == NULL) { + if (me_eval == nullptr) { return false; } @@ -1440,7 +1432,7 @@ bool ED_mesh_pick_vert( MDeformVert *ED_mesh_active_dvert_get_em(Object *ob, BMVert **r_eve) { if (ob->mode & OB_MODE_EDIT && ob->type == OB_MESH) { - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); if (!BLI_listbase_is_empty(&me->vertex_group_names)) { BMesh *bm = me->edit_mesh->bm; const int cd_dvert_offset = CustomData_get_offset(&bm->vdata, CD_MDEFORMVERT); @@ -1452,27 +1444,27 @@ MDeformVert *ED_mesh_active_dvert_get_em(Object *ob, BMVert **r_eve) if (r_eve) { *r_eve = eve; } - return BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset); + return static_cast<MDeformVert *>(BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset)); } } } } if (r_eve) { - *r_eve = NULL; + *r_eve = nullptr; } - return NULL; + return nullptr; } MDeformVert *ED_mesh_active_dvert_get_ob(Object *ob, int *r_index) { - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); int index = BKE_mesh_mselect_active_get(me, ME_VSEL); if (r_index) { *r_index = index; } - if (index == -1 || me->dvert == NULL) { - return NULL; + if (index == -1 || me->dvert == nullptr) { + return nullptr; } return me->dvert + index; } @@ -1481,14 +1473,14 @@ MDeformVert *ED_mesh_active_dvert_get_only(Object *ob) { if (ob->type == OB_MESH) { if (ob->mode & OB_MODE_EDIT) { - return ED_mesh_active_dvert_get_em(ob, NULL); + return ED_mesh_active_dvert_get_em(ob, nullptr); } - return ED_mesh_active_dvert_get_ob(ob, NULL); + return ED_mesh_active_dvert_get_ob(ob, nullptr); } - return NULL; + return nullptr; } -void EDBM_mesh_stats_multi(struct Object **objects, +void EDBM_mesh_stats_multi(Object **objects, const uint objects_len, int totelem[3], int totelem_sel[3]) diff --git a/source/blender/editors/object/object_add.cc b/source/blender/editors/object/object_add.cc index db8860efdd8..041a1383b28 100644 --- a/source/blender/editors/object/object_add.cc +++ b/source/blender/editors/object/object_add.cc @@ -621,9 +621,16 @@ Object *ED_object_add_type_with_obdata(bContext *C, else { ob = BKE_object_add(bmain, view_layer, type, name); } - BASACT(view_layer)->local_view_bits = local_view_bits; - /* editor level activate, notifiers */ - ED_object_base_activate(C, view_layer->basact); + + Base *ob_base_act = BASACT(view_layer); + /* While not getting a valid base is not a good thing, it can happen in convoluted corner cases, + * better not crash on it in releases. */ + BLI_assert(ob_base_act != nullptr); + if (ob_base_act != nullptr) { + ob_base_act->local_view_bits = local_view_bits; + /* editor level activate, notifiers */ + ED_object_base_activate(C, ob_base_act); + } /* more editor stuff */ ED_object_base_init_transform_on_add(ob, loc, rot); @@ -1265,9 +1272,9 @@ void OBJECT_OT_drop_named_image(wmOperatorType *ot) "Relative Path", "Select the file relative to the blend file"); RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); - prop = RNA_def_string( - ot->srna, "name", nullptr, MAX_ID_NAME - 2, "Name", "Image name to assign"); - RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); + + WM_operator_properties_id_lookup(ot, true); + ED_object_add_generic_props(ot, false); } @@ -1651,19 +1658,12 @@ static std::optional<CollectionAddInfo> collection_add_info_get_from_op(bContext Main *bmain = CTX_data_main(C); PropertyRNA *prop_location = RNA_struct_find_property(op->ptr, "location"); - PropertyRNA *prop_session_uuid = RNA_struct_find_property(op->ptr, "session_uuid"); - PropertyRNA *prop_name = RNA_struct_find_property(op->ptr, "name"); + + add_info.collection = reinterpret_cast<Collection *>( + WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_GR)); bool update_location_if_necessary = false; - if (prop_name && RNA_property_is_set(op->ptr, prop_name)) { - char name[MAX_ID_NAME - 2]; - RNA_property_string_get(op->ptr, prop_name, name); - add_info.collection = (Collection *)BKE_libblock_find_name(bmain, ID_GR, name); - update_location_if_necessary = true; - } - else if (RNA_property_is_set(op->ptr, prop_session_uuid)) { - const uint32_t session_uuid = (uint32_t)RNA_property_int_get(op->ptr, prop_session_uuid); - add_info.collection = (Collection *)BKE_libblock_find_session_uuid(bmain, ID_GR, session_uuid); + if (add_info.collection) { update_location_if_necessary = true; } else { @@ -1736,8 +1736,7 @@ static int object_instance_add_invoke(bContext *C, wmOperator *op, const wmEvent RNA_int_set(op->ptr, "drop_y", event->xy[1]); } - if (!RNA_struct_property_is_set(op->ptr, "name") && - !RNA_struct_property_is_set(op->ptr, "session_uuid")) { + if (!WM_operator_properties_id_lookup_is_set(op->ptr)) { return WM_enum_search_invoke(C, op, event); } return op->type->exec(C, op); @@ -1769,16 +1768,7 @@ void OBJECT_OT_collection_instance_add(wmOperatorType *ot) ot->prop = prop; ED_object_add_generic_props(ot, false); - prop = RNA_def_int(ot->srna, - "session_uuid", - 0, - INT32_MIN, - INT32_MAX, - "Session UUID", - "Session UUID of the collection to add", - INT32_MIN, - INT32_MAX); - RNA_def_property_flag(prop, (PropertyFlag)(PROP_SKIP_SAVE | PROP_HIDDEN)); + WM_operator_properties_id_lookup(ot, false); object_add_drop_xy_props(ot); } @@ -1875,20 +1865,11 @@ void OBJECT_OT_collection_external_asset_drop(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; /* properties */ - prop = RNA_def_int(ot->srna, - "session_uuid", - 0, - INT32_MIN, - INT32_MAX, - "Session UUID", - "Session UUID of the collection to add", - INT32_MIN, - INT32_MAX); - RNA_def_property_flag(prop, (PropertyFlag)(PROP_SKIP_SAVE | PROP_HIDDEN)); + WM_operator_properties_id_lookup(ot, false); ED_object_add_generic_props(ot, false); - /* Important: Instancing option. Intentionally remembered across executions (no #PROP_SKIP_SAVE). + /* IMPORTANT: Instancing option. Intentionally remembered across executions (no #PROP_SKIP_SAVE). */ RNA_def_boolean(ot->srna, "use_instance", @@ -1920,18 +1901,12 @@ static int object_data_instance_add_exec(bContext *C, wmOperator *op) ushort local_view_bits; float loc[3], rot[3]; - PropertyRNA *prop_name = RNA_struct_find_property(op->ptr, "name"); PropertyRNA *prop_type = RNA_struct_find_property(op->ptr, "type"); PropertyRNA *prop_location = RNA_struct_find_property(op->ptr, "location"); - /* These shouldn't fail when created by outliner dropping as it checks the ID is valid. */ - if (!RNA_property_is_set(op->ptr, prop_name) || !RNA_property_is_set(op->ptr, prop_type)) { - return OPERATOR_CANCELLED; - } const short id_type = RNA_property_enum_get(op->ptr, prop_type); - char name[MAX_ID_NAME - 2]; - RNA_property_string_get(op->ptr, prop_name, name); - id = BKE_libblock_find_name(bmain, id_type, name); + id = WM_operator_properties_id_lookup_from_name_or_session_uuid( + bmain, op->ptr, (ID_Type)id_type); if (id == nullptr) { return OPERATOR_CANCELLED; } @@ -1974,7 +1949,7 @@ void OBJECT_OT_data_instance_add(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - RNA_def_string(ot->srna, "name", "Name", MAX_ID_NAME - 2, "Name", "ID name to add"); + WM_operator_properties_id_lookup(ot, true); PropertyRNA *prop = RNA_def_enum(ot->srna, "type", rna_enum_id_type_items, 0, "Type", ""); RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_ID); ED_object_add_generic_props(ot, false); @@ -2111,11 +2086,33 @@ static int object_curves_empty_hair_add_exec(bContext *C, wmOperator *op) Curves *curves_id = static_cast<Curves *>(object->data); curves_id->surface = surface_ob; id_us_plus(&surface_ob->id); + + Mesh *surface_mesh = static_cast<Mesh *>(surface_ob->data); + const char *uv_name = CustomData_get_active_layer_name(&surface_mesh->ldata, CD_MLOOPUV); + if (uv_name != nullptr) { + curves_id->surface_uv_map = BLI_strdup(uv_name); + } } return OPERATOR_FINISHED; } +static bool object_curves_empty_hair_add_poll(bContext *C) +{ + if (!U.experimental.use_new_curves_type) { + return false; + } + if (!ED_operator_objectmode(C)) { + return false; + } + Object *ob = CTX_data_active_object(C); + if (ob == nullptr || ob->type != OB_MESH) { + CTX_wm_operator_poll_msg_set(C, "No active mesh object"); + return false; + } + return true; +} + void OBJECT_OT_curves_empty_hair_add(wmOperatorType *ot) { ot->name = "Add Empty Curves"; @@ -2123,7 +2120,7 @@ void OBJECT_OT_curves_empty_hair_add(wmOperatorType *ot) ot->idname = "OBJECT_OT_curves_empty_hair_add"; ot->exec = object_curves_empty_hair_add_exec; - ot->poll = object_curves_add_poll; + ot->poll = object_curves_empty_hair_add_poll; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -2762,9 +2759,32 @@ static const EnumPropertyItem convert_target_items[] = { "Point Cloud", "Point Cloud from Mesh objects"}, #endif + {OB_CURVES, "CURVES", ICON_OUTLINER_OB_CURVES, "Curves", "Curves from evaluated curve data"}, {0, nullptr, 0, nullptr, nullptr}, }; +static const EnumPropertyItem *convert_target_items_fn(bContext *UNUSED(C), + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + bool *r_free) +{ + EnumPropertyItem *items = nullptr; + int items_num = 0; + for (const EnumPropertyItem *item = convert_target_items; item->identifier != nullptr; item++) { + if (item->value == OB_CURVES) { + if (U.experimental.use_new_curves_type) { + RNA_enum_item_add(&items, &items_num, item); + } + } + else { + RNA_enum_item_add(&items, &items_num, item); + } + } + RNA_enum_item_end(&items, &items_num); + *r_free = true; + return items; +} + static void object_data_convert_ensure_curve_cache(Depsgraph *depsgraph, Scene *scene, Object *ob) { if (ob->runtime.curve_cache == nullptr) { @@ -3065,6 +3085,50 @@ static int object_convert_exec(bContext *C, wmOperator *op) } ob_gpencil->actcol = actcol; } + else if (target == OB_CURVES) { + ob->flag |= OB_DONE; + + Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); + GeometrySet geometry; + if (ob_eval->runtime.geometry_set_eval != nullptr) { + geometry = *ob_eval->runtime.geometry_set_eval; + } + + if (geometry.has_curves()) { + if (keep_original) { + basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, nullptr); + newob = basen->object; + + /* Decrement original curve's usage count. */ + Curve *legacy_curve = static_cast<Curve *>(newob->data); + id_us_min(&legacy_curve->id); + + /* Make a copy of the curve. */ + newob->data = BKE_id_copy(bmain, &legacy_curve->id); + } + else { + newob = ob; + } + + const CurveComponent &curve_component = *geometry.get_component_for_read<CurveComponent>(); + const Curves *curves_eval = curve_component.get_for_read(); + Curves *new_curves = static_cast<Curves *>(BKE_id_new(bmain, ID_CV, newob->id.name + 2)); + + newob->data = new_curves; + newob->type = OB_CURVES; + + blender::bke::CurvesGeometry::wrap( + new_curves->geometry) = blender::bke::CurvesGeometry::wrap(curves_eval->geometry); + BKE_object_material_from_eval_data(bmain, newob, &curves_eval->id); + + BKE_object_free_derived_caches(newob); + BKE_object_free_modifiers(newob, 0); + } + else { + BKE_reportf( + op->reports, RPT_WARNING, "Object '%s' has no evaluated curves data", ob->id.name + 2); + } + } else if (ob->type == OB_MESH && target == OB_POINTCLOUD) { ob->flag |= OB_DONE; @@ -3480,6 +3544,7 @@ void OBJECT_OT_convert(wmOperatorType *ot) /* properties */ ot->prop = RNA_def_enum( ot->srna, "target", convert_target_items, OB_MESH, "Target", "Type of object to convert to"); + RNA_def_enum_funcs(ot->prop, convert_target_items_fn); RNA_def_boolean(ot->srna, "keep_original", false, @@ -3718,15 +3783,13 @@ static int object_add_named_exec(bContext *C, wmOperator *op) Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); - Base *basen; - Object *ob; const bool linked = RNA_boolean_get(op->ptr, "linked"); const eDupli_ID_Flags dupflag = (linked) ? (eDupli_ID_Flags)0 : (eDupli_ID_Flags)U.dupflag; - char name[MAX_ID_NAME - 2]; - /* find object, create fake base */ - RNA_string_get(op->ptr, "name", name); - ob = (Object *)BKE_libblock_find_name(bmain, ID_OB, name); + /* Find object, create fake base. */ + + Object *ob = reinterpret_cast<Object *>( + WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_OB)); if (ob == nullptr) { BKE_report(op->reports, RPT_ERROR, "Object not found"); @@ -3734,7 +3797,7 @@ static int object_add_named_exec(bContext *C, wmOperator *op) } /* prepare dupli */ - basen = object_add_duplicate_internal( + Base *basen = object_add_duplicate_internal( bmain, scene, view_layer, @@ -3814,7 +3877,7 @@ void OBJECT_OT_add_named(wmOperatorType *ot) "Linked", "Duplicate object but not object data, linking to the original data"); - RNA_def_string(ot->srna, "name", nullptr, MAX_ID_NAME - 2, "Name", "Object name to add"); + WM_operator_properties_id_lookup(ot, true); prop = RNA_def_float_matrix( ot->srna, "matrix", 4, 4, nullptr, 0.0f, 0.0f, "Matrix", "", 0.0f, 0.0f); @@ -3836,14 +3899,11 @@ static int object_transform_to_mouse_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob; - if (RNA_struct_property_is_set(op->ptr, "name")) { - char name[MAX_ID_NAME - 2]; - RNA_string_get(op->ptr, "name", name); - ob = (Object *)BKE_libblock_find_name(bmain, ID_OB, name); - } - else { + Object *ob = reinterpret_cast<Object *>( + WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_OB)); + + if (!ob) { ob = OBACT(view_layer); } @@ -3922,12 +3982,25 @@ void OBJECT_OT_transform_to_mouse(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; PropertyRNA *prop; - RNA_def_string(ot->srna, - "name", - nullptr, - MAX_ID_NAME - 2, - "Name", - "Object name to place (when unset use the active object)"); + prop = RNA_def_string( + ot->srna, + "name", + nullptr, + MAX_ID_NAME - 2, + "Name", + "Object name to place (uses the active object when this and 'session_uuid' are unset)"); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_SKIP_SAVE | PROP_HIDDEN)); + prop = RNA_def_int(ot->srna, + "session_uuid", + 0, + INT32_MIN, + INT32_MAX, + "Session UUID", + "Session UUID of the object to place (uses the active object when this and " + "'name' are unset)", + INT32_MIN, + INT32_MAX); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_SKIP_SAVE | PROP_HIDDEN)); prop = RNA_def_float_matrix( ot->srna, "matrix", 4, 4, nullptr, 0.0f, 0.0f, "Matrix", "", 0.0f, 0.0f); diff --git a/source/blender/editors/object/object_bake.c b/source/blender/editors/object/object_bake.c index 1483c24ac70..effbde41c38 100644 --- a/source/blender/editors/object/object_bake.c +++ b/source/blender/editors/object/object_bake.c @@ -220,22 +220,22 @@ static DerivedMesh *multiresbake_create_loresdm(Scene *scene, Object *ob, int *l MultiresModifierData *mmd = get_multires_modifier(scene, ob, 0); Mesh *me = (Mesh *)ob->data; MultiresModifierData tmp_mmd = *mmd; - DerivedMesh *cddm = CDDM_from_mesh(me); - DM_set_only_copy(cddm, &CD_MASK_BAREMESH); + *lvl = mmd->lvl; if (mmd->lvl == 0) { - dm = CDDM_copy(cddm); - } - else { - tmp_mmd.lvl = mmd->lvl; - tmp_mmd.sculptlvl = mmd->lvl; - dm = multires_make_derived_from_derived(cddm, &tmp_mmd, scene, ob, 0); + DerivedMesh *cddm = CDDM_from_mesh(me); + DM_set_only_copy(cddm, &CD_MASK_BAREMESH); + return cddm; } - cddm->release(cddm); + DerivedMesh *cddm = CDDM_from_mesh(me); + DM_set_only_copy(cddm, &CD_MASK_BAREMESH); + tmp_mmd.lvl = mmd->lvl; + tmp_mmd.sculptlvl = mmd->lvl; + dm = multires_make_derived_from_derived(cddm, &tmp_mmd, scene, ob, 0); - *lvl = mmd->lvl; + cddm->release(cddm); return dm; } diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index a7379d7e492..58daf753679 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -21,6 +21,8 @@ #include "BLI_path_util.h" #include "BLI_string.h" +#include "BKE_attribute.h" +#include "BKE_callbacks.h" #include "BKE_context.h" #include "BKE_global.h" #include "BKE_image.h" @@ -446,14 +448,11 @@ static bool bake_object_check(ViewLayer *view_layer, } if (target == R_BAKE_TARGET_VERTEX_COLORS) { - MPropCol *mcol = CustomData_get_layer(&me->vdata, CD_PROP_COLOR); - const bool mcol_valid = (mcol != NULL); - MLoopCol *mloopcol = CustomData_get_layer(&me->ldata, CD_PROP_BYTE_COLOR); - if (mloopcol == NULL && !mcol_valid) { + if (BKE_id_attributes_active_color_get(&me->id) == NULL) { BKE_reportf(reports, RPT_ERROR, - "No vertex colors layer found in the object \"%s\"", - ob->id.name + 2); + "Mesh does not have an active color attribute \"%s\"", + me->id.name + 2); return false; } } @@ -937,17 +936,13 @@ static bool bake_targets_output_external(const BakeAPIRender *bkr, static bool bake_targets_init_vertex_colors(BakeTargets *targets, Object *ob, ReportList *reports) { if (ob->type != OB_MESH) { - BKE_report( - reports, RPT_ERROR, "Vertex color baking not support with object types other than mesh"); + BKE_report(reports, RPT_ERROR, "Color attribute baking is only supported for mesh objects"); return false; } Mesh *me = ob->data; - MPropCol *mcol = CustomData_get_layer(&me->vdata, CD_PROP_COLOR); - const bool mcol_valid = (mcol != NULL); - MLoopCol *mloopcol = CustomData_get_layer(&me->ldata, CD_PROP_BYTE_COLOR); - if (mloopcol == NULL && !mcol_valid) { - BKE_report(reports, RPT_ERROR, "No vertex colors layer found to bake to"); + if (BKE_id_attributes_active_color_get(&me->id) == NULL) { + BKE_report(reports, RPT_ERROR, "No active color attribute to bake to"); return false; } @@ -996,10 +991,10 @@ static int find_original_loop(const Mesh *me_orig, return ORIGINDEX_NONE; } -static void bake_targets_populate_pixels_vertex_colors(BakeTargets *targets, - Object *ob, - Mesh *me_eval, - BakePixel *pixel_array) +static void bake_targets_populate_pixels_color_attributes(BakeTargets *targets, + Object *ob, + Mesh *me_eval, + BakePixel *pixel_array) { Mesh *me = ob->data; const int pixels_num = targets->pixels_num; @@ -1094,19 +1089,42 @@ static void bake_result_add_to_rgba(float rgba[4], const float *result, const in } } +static void convert_float_color_to_byte_color(const MPropCol *float_colors, + const int num, + const bool is_noncolor, + MLoopCol *byte_colors) +{ + if (is_noncolor) { + for (int i = 0; i < num; i++) { + unit_float_to_uchar_clamp_v4(&byte_colors->r, float_colors[i].color); + } + } + else { + for (int i = 0; i < num; i++) { + linearrgb_to_srgb_uchar4(&byte_colors[i].r, float_colors[i].color); + } + } +} + static bool bake_targets_output_vertex_colors(BakeTargets *targets, Object *ob) { Mesh *me = ob->data; - MPropCol *mcol = CustomData_get_layer(&me->vdata, CD_PROP_COLOR); - const bool mcol_valid = (mcol != NULL); - MLoopCol *mloopcol = CustomData_get_layer(&me->ldata, CD_PROP_BYTE_COLOR); + CustomDataLayer *active_color_layer = BKE_id_attributes_active_color_get(&me->id); + BLI_assert(active_color_layer != NULL); + const eAttrDomain domain = BKE_id_attribute_domain(&me->id, active_color_layer); + const int channels_num = targets->channels_num; + const bool is_noncolor = targets->is_noncolor; const float *result = targets->result; - if (mcol_valid) { + if (domain == ATTR_DOMAIN_POINT) { const int totvert = me->totvert; const int totloop = me->totloop; + MPropCol *mcol = active_color_layer->type == CD_PROP_COLOR ? + active_color_layer->data : + MEM_malloc_arrayN(totvert, sizeof(MPropCol), __func__); + /* Accumulate float vertex colors in scene linear color space. */ int *num_loops_for_vertex = MEM_callocN(sizeof(int) * me->totvert, "num_loops_for_vertex"); memset(mcol, 0, sizeof(MPropCol) * me->totvert); @@ -1125,25 +1143,35 @@ static bool bake_targets_output_vertex_colors(BakeTargets *targets, Object *ob) } } + if (mcol != active_color_layer->data) { + convert_float_color_to_byte_color(mcol, totvert, is_noncolor, active_color_layer->data); + MEM_freeN(mcol); + } + MEM_SAFE_FREE(num_loops_for_vertex); } - else { - /* Byte loop colors in sRGB colors space. */ - MLoop *mloop = me->mloop; - const int totloop = me->totloop; - const bool is_noncolor = targets->is_noncolor; - - for (int i = 0; i < totloop; i++, mloop++, mloopcol++) { - float rgba[4]; - zero_v4(rgba); - bake_result_add_to_rgba(rgba, &result[i * channels_num], channels_num); - - if (is_noncolor) { - unit_float_to_uchar_clamp_v4(&mloopcol->r, rgba); + else if (domain == ATTR_DOMAIN_CORNER) { + switch (active_color_layer->type) { + case CD_PROP_COLOR: { + MPropCol *colors = active_color_layer->data; + for (int i = 0; i < me->totloop; i++) { + zero_v4(colors[i].color); + bake_result_add_to_rgba(colors[i].color, &result[i * channels_num], channels_num); + } + break; } - else { - linearrgb_to_srgb_uchar4(&mloopcol->r, rgba); + case CD_PROP_BYTE_COLOR: { + MLoopCol *colors = active_color_layer->data; + for (int i = 0; i < me->totloop; i++) { + MPropCol color; + zero_v4(color.color); + bake_result_add_to_rgba(color.color, &result[i * channels_num], channels_num); + convert_float_color_to_byte_color(&color, 1, is_noncolor, &colors[i]); + } + break; } + default: + BLI_assert_unreachable(); } } @@ -1197,7 +1225,7 @@ static void bake_targets_populate_pixels(const BakeAPIRender *bkr, BakePixel *pixel_array) { if (bkr->target == R_BAKE_TARGET_VERTEX_COLORS) { - bake_targets_populate_pixels_vertex_colors(targets, ob, me_eval, pixel_array); + bake_targets_populate_pixels_color_attributes(targets, ob, me_eval, pixel_array); } else { RE_bake_pixels_populate(me_eval, pixel_array, targets->pixels_num, targets, bkr->uv_layer); @@ -1535,7 +1563,7 @@ static int bake(const BakeAPIRender *bkr, ob_low_eval->obmat); } else { - /* from multiresolution */ + /* From multi-resolution. */ Mesh *me_nores = NULL; ModifierData *md = NULL; int mode; @@ -1806,6 +1834,17 @@ static void bake_startjob(void *bkv, short *UNUSED(stop), short *do_update, floa RE_SetReports(bkr->render, NULL); } +static void bake_job_complete(void *bkv) +{ + BakeAPIRender *bkr = (BakeAPIRender *)bkv; + BKE_callback_exec_id(bkr->main, &bkr->ob->id, BKE_CB_EVT_OBJECT_BAKE_COMPLETE); +} +static void bake_job_canceled(void *bkv) +{ + BakeAPIRender *bkr = (BakeAPIRender *)bkv; + BKE_callback_exec_id(bkr->main, &bkr->ob->id, BKE_CB_EVT_OBJECT_BAKE_CANCEL); +} + static void bake_freejob(void *bkv) { BakeAPIRender *bkr = (BakeAPIRender *)bkv; @@ -1941,6 +1980,7 @@ static int bake_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event) /* init bake render */ bake_init_api_data(op, C, bkr); + BKE_callback_exec_id(CTX_data_main(C), &bkr->ob->id, BKE_CB_EVT_OBJECT_BAKE_PRE); re = bkr->render; /* setup new render */ @@ -1958,7 +1998,8 @@ static int bake_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event) /* TODO: only draw bake image, can we enforce this. */ WM_jobs_timer( wm_job, 0.5, (bkr->target == R_BAKE_TARGET_VERTEX_COLORS) ? NC_GEOM | ND_DATA : NC_IMAGE, 0); - WM_jobs_callbacks(wm_job, bake_startjob, NULL, NULL, NULL); + WM_jobs_callbacks_ex( + wm_job, bake_startjob, NULL, NULL, NULL, bake_job_complete, bake_job_canceled); G.is_break = false; G.is_rendering = true; diff --git a/source/blender/editors/object/object_collection.c b/source/blender/editors/object/object_collection.c index 054c9e1de46..39951c2ab6e 100644 --- a/source/blender/editors/object/object_collection.c +++ b/source/blender/editors/object/object_collection.c @@ -526,7 +526,7 @@ void OBJECT_OT_collection_link(wmOperatorType *ot) ot->prop = prop; } -static int collection_remove_exec(bContext *C, wmOperator *UNUSED(op)) +static int collection_remove_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Object *ob = ED_object_context(C); @@ -535,6 +535,12 @@ static int collection_remove_exec(bContext *C, wmOperator *UNUSED(op)) if (!ob || !collection) { return OPERATOR_CANCELLED; } + if (ID_IS_LINKED(collection) || ID_IS_OVERRIDE_LIBRARY(collection)) { + BKE_report(op->reports, + RPT_ERROR, + "Cannot remove an object from a linked or library override collection"); + return OPERATOR_CANCELLED; + } BKE_collection_object_remove(bmain, collection, ob, false); @@ -561,7 +567,7 @@ void OBJECT_OT_collection_remove(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -static int collection_unlink_exec(bContext *C, wmOperator *UNUSED(op)) +static int collection_unlink_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Collection *collection = CTX_data_pointer_get_type(C, "collection", &RNA_Collection).data; @@ -569,6 +575,14 @@ static int collection_unlink_exec(bContext *C, wmOperator *UNUSED(op)) if (!collection) { return OPERATOR_CANCELLED; } + if (ID_IS_OVERRIDE_LIBRARY(collection) && + collection->id.override_library->hierarchy_root != &collection->id) { + BKE_report(op->reports, + RPT_ERROR, + "Cannot unlink a library override collection which is not the root of its override " + "hierarchy"); + return OPERATOR_CANCELLED; + } BKE_id_delete(bmain, collection); diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c index d982d86fe77..bf3b71178e8 100644 --- a/source/blender/editors/object/object_constraint.c +++ b/source/blender/editors/object/object_constraint.c @@ -245,13 +245,11 @@ static void set_constraint_nth_target(bConstraint *con, const char subtarget[], int index) { - const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); ListBase targets = {NULL, NULL}; bConstraintTarget *ct; int num_targets, i; - if (cti && cti->get_constraint_targets) { - cti->get_constraint_targets(con, &targets); + if (BKE_constraint_targets_get(con, &targets)) { num_targets = BLI_listbase_count(&targets); if (index < 0) { @@ -274,9 +272,7 @@ static void set_constraint_nth_target(bConstraint *con, } } - if (cti->flush_constraint_targets) { - cti->flush_constraint_targets(con, &targets, 0); - } + BKE_constraint_targets_flush(con, &targets, 0); } } @@ -289,7 +285,6 @@ static void set_constraint_nth_target(bConstraint *con, static void test_constraint( Main *bmain, Object *owner, bPoseChannel *pchan, bConstraint *con, int type) { - const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); ListBase targets = {NULL, NULL}; bConstraintTarget *ct; bool check_targets = true; @@ -465,14 +460,7 @@ static void test_constraint( } /* Check targets for constraints */ - if (check_targets && cti && cti->get_constraint_targets) { - cti->get_constraint_targets(con, &targets); - - /* constraints with empty target list that actually require targets */ - if (!targets.first && ELEM(con->type, CONSTRAINT_TYPE_ARMATURE)) { - con->flag |= CONSTRAINT_DISABLE; - } - + if (check_targets && BKE_constraint_targets_get(con, &targets)) { /* disable and clear constraints targets that are incorrect */ for (ct = targets.first; ct; ct = ct->next) { /* general validity checks (for those constraints that need this) */ @@ -543,8 +531,12 @@ static void test_constraint( } /* free any temporary targets */ - if (cti->flush_constraint_targets) { - cti->flush_constraint_targets(con, &targets, 0); + BKE_constraint_targets_flush(con, &targets, 0); + } + else if (check_targets) { + /* constraints with empty target list that actually require targets */ + if (ELEM(con->type, CONSTRAINT_TYPE_ARMATURE)) { + con->flag |= CONSTRAINT_DISABLE; } } } diff --git a/source/blender/editors/object/object_data_transfer.c b/source/blender/editors/object/object_data_transfer.c index dfe858e5bd9..4837b538bf6 100644 --- a/source/blender/editors/object/object_data_transfer.c +++ b/source/blender/editors/object/object_data_transfer.c @@ -45,7 +45,7 @@ * Note some are 'fake' ones, i.e. they are not hold by real CDLayers. */ /* Not shared with modifier, since we use a usual enum here, not a multi-choice one. */ static const EnumPropertyItem DT_layer_items[] = { - {0, "", 0, "Vertex Data", ""}, + RNA_ENUM_ITEM_HEADING("Vertex Data", NULL), {DT_TYPE_MDEFORMVERT, "VGROUP_WEIGHTS", 0, @@ -60,7 +60,8 @@ static const EnumPropertyItem DT_layer_items[] = { {DT_TYPE_SKIN, "SKIN", 0, "Skin Weight", "Transfer skin weights"}, #endif {DT_TYPE_BWEIGHT_VERT, "BEVEL_WEIGHT_VERT", 0, "Bevel Weight", "Transfer bevel weights"}, - {0, "", 0, "Edge Data", ""}, + + RNA_ENUM_ITEM_HEADING("Edge Data", NULL), {DT_TYPE_SHARP_EDGE, "SHARP_EDGE", 0, "Sharp", "Transfer sharp mark"}, {DT_TYPE_SEAM, "SEAM", 0, "UV Seam", "Transfer UV seam mark"}, {DT_TYPE_CREASE, "CREASE", 0, "Subdivision Crease", "Transfer crease values"}, @@ -70,11 +71,13 @@ static const EnumPropertyItem DT_layer_items[] = { 0, "Freestyle Mark", "Transfer Freestyle edge mark"}, - {0, "", 0, "Face Corner Data", ""}, + + RNA_ENUM_ITEM_HEADING("Face Corner Data", NULL), {DT_TYPE_LNOR, "CUSTOM_NORMAL", 0, "Custom Normals", "Transfer custom normals"}, {DT_TYPE_MPROPCOL_LOOP | DT_TYPE_MLOOPCOL_LOOP, "VCOL", 0, "Colors", "Color Attributes"}, {DT_TYPE_UV, "UV", 0, "UVs", "Transfer UV layers"}, - {0, "", 0, "Face Data", ""}, + + RNA_ENUM_ITEM_HEADING("Face Data", NULL), {DT_TYPE_SHARP_FACE, "SMOOTH", 0, "Smooth", "Transfer flat/smooth mark"}, {DT_TYPE_FREESTYLE_FACE, "FREESTYLE_FACE", @@ -85,14 +88,14 @@ static const EnumPropertyItem DT_layer_items[] = { }; static void dt_add_vcol_layers(CustomData *cdata, - CustomDataMask mask, + eCustomDataMask mask, EnumPropertyItem **r_item, int *r_totitem) { int types[2] = {CD_PROP_COLOR, CD_PROP_BYTE_COLOR}; for (int i = 0; i < 2; i++) { - CustomDataType type = types[i]; + eCustomDataType type = types[i]; if (!(mask & CD_TYPE_AS_MASK(type))) { continue; diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index cb0e76c11e4..e5dd9fb2c8b 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -17,6 +17,7 @@ #include "BLI_blenlib.h" #include "BLI_ghash.h" +#include "BLI_math_rotation.h" #include "BLI_utildefines.h" #include "BLT_translation.h" @@ -86,6 +87,7 @@ #include "RNA_access.h" #include "RNA_define.h" #include "RNA_enum_types.h" +#include "RNA_types.h" #include "UI_interface_icons.h" @@ -1241,6 +1243,9 @@ static int object_calculate_paths_exec(bContext *C, wmOperator *op) ED_objects_recalculate_paths_selected(C, scene, OBJECT_PATH_CALC_RANGE_FULL); /* notifiers for updates */ + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW_ANIMVIZ, NULL); + /* Note: the notifier below isn't actually correct, but kept around just to be on the safe side. + * If further testing shows it's not necessary (for both bones and objects) removal is fine. */ WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM | ND_POSE, NULL); return OPERATOR_FINISHED; @@ -1310,6 +1315,9 @@ static int object_update_paths_exec(bContext *C, wmOperator *op) ED_objects_recalculate_paths_selected(C, scene, OBJECT_PATH_CALC_RANGE_FULL); /* notifiers for updates */ + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW_ANIMVIZ, NULL); + /* Note: the notifier below isn't actually correct, but kept around just to be on the safe side. + * If further testing shows it's not necessary (for both bones and objects) removal is fine. */ WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM | ND_POSE, NULL); return OPERATOR_FINISHED; @@ -1461,6 +1469,8 @@ void OBJECT_OT_paths_clear(wmOperatorType *ot) static int shade_smooth_exec(bContext *C, wmOperator *op) { const bool use_smooth = STREQ(op->idname, "OBJECT_OT_shade_smooth"); + const bool use_auto_smooth = RNA_boolean_get(op->ptr, "use_auto_smooth"); + const float auto_smooth_angle = RNA_float_get(op->ptr, "auto_smooth_angle"); bool changed_multi = false; bool has_linked_data = false; @@ -1508,6 +1518,7 @@ static int shade_smooth_exec(bContext *C, wmOperator *op) bool changed = false; if (ob->type == OB_MESH) { BKE_mesh_smooth_flag_set(ob->data, use_smooth); + BKE_mesh_auto_smooth_flag_set(ob->data, use_auto_smooth, auto_smooth_angle); BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); changed = true; } @@ -1577,6 +1588,25 @@ void OBJECT_OT_shade_smooth(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + PropertyRNA *prop; + + prop = RNA_def_boolean( + ot->srna, + "use_auto_smooth", + false, + "Auto Smooth", + "Enable automatic smooth based on smooth/sharp faces/edges and angle between faces"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + + prop = RNA_def_property(ot->srna, "auto_smooth_angle", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_range(prop, 0.0f, DEG2RADF(180.0f)); + RNA_def_property_float_default(prop, DEG2RADF(30.0f)); + RNA_def_property_ui_text(prop, + "Angle", + "Maximum angle between face normals that will be considered as smooth" + "(unused if custom split normals data are available)"); } /** \} */ @@ -1811,6 +1841,11 @@ static int move_to_collection_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + if (ID_IS_OVERRIDE_LIBRARY(collection)) { + BKE_report(op->reports, RPT_ERROR, "Cannot add objects to a library override collection"); + return OPERATOR_CANCELLED; + } + ListBase objects = selected_objects_get(C); if (is_new) { diff --git a/source/blender/editors/object/object_modes.c b/source/blender/editors/object/object_modes.c index fdf2cae026d..0055cdf9ea1 100644 --- a/source/blender/editors/object/object_modes.c +++ b/source/blender/editors/object/object_modes.c @@ -458,7 +458,7 @@ static bool object_transfer_mode_to_base(bContext *C, wmOperator *op, Base *base return false; } - bool mode_transfered = false; + bool mode_transferred = false; ED_undo_group_begin(C); @@ -480,11 +480,11 @@ static bool object_transfer_mode_to_base(bContext *C, wmOperator *op, Base *base WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); WM_toolsystem_update_from_context_view3d(C); - mode_transfered = true; + mode_transferred = true; } ED_undo_group_end(C); - return mode_transfered; + return mode_transferred; } static int object_transfer_mode_invoke(bContext *C, wmOperator *op, const wmEvent *event) @@ -493,8 +493,8 @@ static int object_transfer_mode_invoke(bContext *C, wmOperator *op, const wmEven const eObjectMode src_mode = (eObjectMode)ob_src->mode; Base *base_dst = ED_view3d_give_base_under_cursor(C, event->mval); - const bool mode_transfered = object_transfer_mode_to_base(C, op, base_dst); - if (!mode_transfered) { + const bool mode_transferred = object_transfer_mode_to_base(C, op, base_dst); + if (!mode_transferred) { return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/object/object_modifier.cc b/source/blender/editors/object/object_modifier.cc index 3328fe65f2e..202c6d96a47 100644 --- a/source/blender/editors/object/object_modifier.cc +++ b/source/blender/editors/object/object_modifier.cc @@ -515,12 +515,12 @@ void ED_object_modifier_copy_to_object(bContext *C, DEG_relations_tag_update(bmain); } -bool ED_object_modifier_convert(ReportList *UNUSED(reports), - Main *bmain, - Depsgraph *depsgraph, - ViewLayer *view_layer, - Object *ob, - ModifierData *md) +bool ED_object_modifier_convert_psys_to_mesh(ReportList *UNUSED(reports), + Main *bmain, + Depsgraph *depsgraph, + ViewLayer *view_layer, + Object *ob, + ModifierData *md) { int cvert = 0; @@ -740,7 +740,13 @@ static bool modifier_apply_obdata( } else { Mesh *mesh_applied = modifier_apply_create_mesh_for_modifier( - depsgraph, ob, md_eval, true, true); + depsgraph, + ob, + md_eval, + /* It's important not to apply virtual modifiers (e.g. shape-keys) because they're kept, + * causing them to be applied twice, see: T97758. */ + false, + true); if (!mesh_applied) { BKE_report(reports, RPT_ERROR, "Modifier returned error, skipping apply"); return false; @@ -1462,7 +1468,7 @@ static int modifier_apply_exec_ex(bContext *C, wmOperator *op, int apply_as, boo return OPERATOR_CANCELLED; } - if (do_merge_customdata && + if (ob->type == OB_MESH && do_merge_customdata && (mti->type & (eModifierTypeType_Constructive | eModifierTypeType_Nonconstructive))) { BKE_mesh_merge_customdata_for_apply_modifier((Mesh *)ob->data); } @@ -1525,12 +1531,12 @@ void OBJECT_OT_modifier_apply(wmOperatorType *ot) edit_modifier_properties(ot); edit_modifier_report_property(ot); - RNA_def_boolean( - ot->srna, - "merge_customdata", - true, - "Merge UV's", - "Merge UV coordinates that share a vertex to account for imprecision in some modifiers"); + RNA_def_boolean(ot->srna, + "merge_customdata", + true, + "Merge UV's", + "For mesh objects, merge UV coordinates that share a vertex to account for " + "imprecision in some modifiers"); PropertyRNA *prop = RNA_def_boolean(ot->srna, "single_user", false, @@ -1602,7 +1608,7 @@ void OBJECT_OT_modifier_apply_as_shapekey(wmOperatorType *ot) /** \} */ /* ------------------------------------------------------------------- */ -/** \name Convert Modifier Operator +/** \name Convert Particle System Modifier to Mesh Operator * \{ */ static int modifier_convert_exec(bContext *C, wmOperator *op) @@ -1612,18 +1618,12 @@ static int modifier_convert_exec(bContext *C, wmOperator *op) ViewLayer *view_layer = CTX_data_view_layer(C); Object *ob = ED_object_active_context(C); ModifierData *md = edit_modifier_property_get(op, ob, 0); - const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type); - const bool do_merge_customdata = RNA_boolean_get(op->ptr, "merge_customdata"); - if (!md || !ED_object_modifier_convert(op->reports, bmain, depsgraph, view_layer, ob, md)) { + if (!md || !ED_object_modifier_convert_psys_to_mesh( + op->reports, bmain, depsgraph, view_layer, ob, md)) { return OPERATOR_CANCELLED; } - if (do_merge_customdata && - (mti->type & (eModifierTypeType_Constructive | eModifierTypeType_Nonconstructive))) { - BKE_mesh_merge_customdata_for_apply_modifier((Mesh *)ob->data); - } - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); @@ -1640,7 +1640,7 @@ static int modifier_convert_invoke(bContext *C, wmOperator *op, const wmEvent *U void OBJECT_OT_modifier_convert(wmOperatorType *ot) { - ot->name = "Convert Modifier"; + ot->name = "Convert Particles to Mesh"; ot->description = "Convert particles to a mesh object"; ot->idname = "OBJECT_OT_modifier_convert"; @@ -1651,13 +1651,6 @@ void OBJECT_OT_modifier_convert(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; edit_modifier_properties(ot); - - RNA_def_boolean( - ot->srna, - "merge_customdata", - true, - "Merge UV's", - "Merge UV coordinates that share a vertex to account for imprecision in some modifiers"); } /** \} */ @@ -3393,6 +3386,7 @@ static int geometry_node_tree_copy_assign_exec(bContext *C, wmOperator *UNUSED(o nmd->node_group = new_tree; id_us_min(&tree->id); + DEG_relations_tag_update(bmain); WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index 1067c2d6a95..8a0d380ff2f 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -281,7 +281,7 @@ void ED_operatormacros_object(void) ot = WM_operatortype_append_macro("OBJECT_OT_duplicate_move", "Duplicate Objects", - "Duplicate selected objects and move them", + "Duplicate the selected objects and move them", OPTYPE_UNDO | OPTYPE_REGISTER); if (ot) { WM_operatortype_macro_define(ot, "OBJECT_OT_duplicate"); @@ -289,11 +289,11 @@ void ED_operatormacros_object(void) RNA_boolean_set(otmacro->ptr, "use_proportional_edit", false); } - /* grr, should be able to pass options on... */ - ot = WM_operatortype_append_macro("OBJECT_OT_duplicate_move_linked", - "Duplicate Linked", - "Duplicate selected objects and move them", - OPTYPE_UNDO | OPTYPE_REGISTER); + ot = WM_operatortype_append_macro( + "OBJECT_OT_duplicate_move_linked", + "Duplicate Linked", + "Duplicate the selected objects, but not their object data, and move them", + OPTYPE_UNDO | OPTYPE_REGISTER); if (ot) { otmacro = WM_operatortype_macro_define(ot, "OBJECT_OT_duplicate"); RNA_boolean_set(otmacro->ptr, "linked", true); diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index 5ef8e573e27..f55ffdf0fcd 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -1653,7 +1653,7 @@ void OBJECT_OT_make_links_data(wmOperatorType *ot) "Link Instance Collection", "Replace assigned Collection Instance"}, {MAKE_LINKS_FONTS, "FONTS", 0, "Link Fonts to Text", "Replace Text object Fonts"}, - {0, "", 0, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {MAKE_LINKS_MODIFIERS, "MODIFIERS", 0, "Copy Modifiers", "Replace Modifiers"}, {MAKE_LINKS_SHADERFX, "EFFECTS", @@ -1964,7 +1964,7 @@ static void single_objectdata_action_users( ID *id_act = (ID *)adt->action; if (single_data_needs_duplication(id_act)) { DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - BKE_animdata_duplicate_id_action(bmain, &ob->id, USER_DUP_ACT | USER_DUP_LINKED_ID); + BKE_animdata_duplicate_id_action(bmain, id_obdata, USER_DUP_ACT | USER_DUP_LINKED_ID); } } } @@ -2258,49 +2258,6 @@ static bool make_override_library_object_overridable_check(Main *bmain, Object * return false; } -/* Set the object to override. */ -static int make_override_library_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - Object *obact = ED_object_active_context(C); - - /* Sanity checks. */ - if (!scene || ID_IS_LINKED(scene) || !obact) { - return OPERATOR_CANCELLED; - } - - if ((!ID_IS_LINKED(obact) && obact->instance_collection != NULL && - ID_IS_OVERRIDABLE_LIBRARY(obact->instance_collection)) || - make_override_library_object_overridable_check(bmain, obact)) { - uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("OK?"), ICON_QUESTION); - uiLayout *layout = UI_popup_menu_layout(pup); - - /* Create operator menu item with relevant properties filled in. */ - PointerRNA opptr_dummy; - uiItemFullO_ptr( - layout, op->type, op->type->name, ICON_NONE, NULL, WM_OP_EXEC_REGION_WIN, 0, &opptr_dummy); - - /* Present the menu and be done... */ - UI_popup_menu_end(C, pup); - - /* This invoke just calls another instance of this operator... */ - return OPERATOR_INTERFACE; - } - - if (ID_IS_LINKED(obact)) { - /* Show menu with list of directly linked collections containing the active object. */ - WM_enum_search_invoke(C, op, event); - return OPERATOR_CANCELLED; - } - - /* Error.. cannot continue. */ - BKE_report(op->reports, - RPT_ERROR, - "Can only make library override for a referenced object or collection"); - return OPERATOR_CANCELLED; -} - static int make_override_library_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); @@ -2310,8 +2267,12 @@ static int make_override_library_exec(bContext *C, wmOperator *op) ID *id_root = NULL; bool is_override_instancing_object = false; - GSet *user_overrides_objects_uids = BLI_gset_new( - BLI_ghashutil_inthash_p, BLI_ghashutil_intcmp, __func__); + const bool do_fully_editable = RNA_boolean_get(op->ptr, "do_fully_editable"); + + GSet *user_overrides_objects_uids = do_fully_editable ? NULL : + BLI_gset_new(BLI_ghashutil_inthash_p, + BLI_ghashutil_intcmp, + __func__); bool user_overrides_from_selected_objects = false; if (!ID_IS_LINKED(obact) && obact->instance_collection != NULL && @@ -2329,27 +2290,19 @@ static int make_override_library_exec(bContext *C, wmOperator *op) user_overrides_from_selected_objects = false; } else if (!make_override_library_object_overridable_check(bmain, obact)) { - const int i = RNA_property_enum_get(op->ptr, op->type->prop); - const uint collection_session_uuid = *((uint *)&i); + const int i = RNA_property_int_get(op->ptr, op->type->prop); + const uint collection_session_uuid = *((const uint *)&i); if (collection_session_uuid == MAIN_ID_SESSION_UUID_UNSET) { BKE_reportf(op->reports, RPT_ERROR_INVALID_INPUT, - "Active object '%s' is not overridable", + "Could not find an overridable root hierarchy for object '%s'", obact->id.name + 2); return OPERATOR_CANCELLED; } - Collection *collection = BLI_listbase_bytes_find(&bmain->collections, &collection_session_uuid, sizeof(collection_session_uuid), offsetof(ID, session_uuid)); - if (!ID_IS_OVERRIDABLE_LIBRARY(collection)) { - BKE_reportf(op->reports, - RPT_ERROR_INVALID_INPUT, - "Could not find an overridable collection containing object '%s'", - obact->id.name + 2); - return OPERATOR_CANCELLED; - } id_root = &collection->id; user_overrides_from_selected_objects = true; } @@ -2359,7 +2312,10 @@ static int make_override_library_exec(bContext *C, wmOperator *op) user_overrides_from_selected_objects = true; } - if (user_overrides_from_selected_objects) { + if (do_fully_editable) { + /* Pass. */ + } + else if (user_overrides_from_selected_objects) { /* Only selected objects can be 'user overrides'. */ FOREACH_SELECTED_OBJECT_BEGIN (view_layer, CTX_wm_view3d(C), ob_iter) { BLI_gset_add(user_overrides_objects_uids, POINTER_FROM_UINT(ob_iter->id.session_uuid)); @@ -2379,30 +2335,65 @@ static int make_override_library_exec(bContext *C, wmOperator *op) BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); ID *id_root_override; - const bool success = BKE_lib_override_library_create( - bmain, scene, view_layer, NULL, id_root, id_root, &obact->id, &id_root_override); - - /* Define liboverrides from selected/validated objects as user defined. */ - ID *id_hierarchy_root_override = id_root_override->override_library->hierarchy_root; - ID *id_iter; - FOREACH_MAIN_ID_BEGIN (bmain, id_iter) { - if (ID_IS_LINKED(id_iter) || !ID_IS_OVERRIDE_LIBRARY_REAL(id_iter) || - id_iter->override_library->hierarchy_root != id_hierarchy_root_override) { - continue; - } - if (BLI_gset_haskey(user_overrides_objects_uids, - POINTER_FROM_UINT(id_iter->override_library->reference->session_uuid))) { - id_iter->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED; + const bool success = BKE_lib_override_library_create(bmain, + scene, + view_layer, + NULL, + id_root, + id_root, + &obact->id, + &id_root_override, + do_fully_editable); + + if (!do_fully_editable) { + /* Define liboverrides from selected/validated objects as user defined. */ + ID *id_hierarchy_root_override = id_root_override->override_library->hierarchy_root; + ID *id_iter; + FOREACH_MAIN_ID_BEGIN (bmain, id_iter) { + if (ID_IS_LINKED(id_iter) || !ID_IS_OVERRIDE_LIBRARY_REAL(id_iter) || + id_iter->override_library->hierarchy_root != id_hierarchy_root_override) { + continue; + } + if (BLI_gset_haskey(user_overrides_objects_uids, + POINTER_FROM_UINT(id_iter->override_library->reference->session_uuid))) { + id_iter->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED; + } } - } - FOREACH_MAIN_ID_END; + FOREACH_MAIN_ID_END; - BLI_gset_free(user_overrides_objects_uids, NULL); + BLI_gset_free(user_overrides_objects_uids, NULL); + } - /* Remove the instance empty from this scene, the items now have an overridden collection - * instead. */ - if (success && is_override_instancing_object) { - ED_object_base_free_and_unlink(bmain, scene, obact); + if (success) { + if (is_override_instancing_object) { + /* Remove the instance empty from this scene, the items now have an overridden collection + * instead. */ + ED_object_base_free_and_unlink(bmain, scene, obact); + } + else { + /* Remove the found root ID from the view layer. */ + switch (GS(id_root->name)) { + case ID_GR: { + Collection *collection_root = (Collection *)id_root; + LISTBASE_FOREACH_MUTABLE ( + CollectionParent *, collection_parent, &collection_root->parents) { + if (ID_IS_LINKED(collection_parent->collection) || + !BKE_view_layer_has_collection(view_layer, collection_parent->collection)) { + continue; + } + BKE_collection_child_remove(bmain, collection_parent->collection, collection_root); + } + break; + } + case ID_OB: { + /* TODO: Not sure how well we can handle this case, when we don't have the collections as + * reference containers... */ + break; + } + default: + break; + } + } } DEG_id_tag_update(&CTX_data_scene(C)->id, ID_RECALC_BASE_FLAGS | ID_RECALC_COPY_ON_WRITE); @@ -2411,46 +2402,68 @@ static int make_override_library_exec(bContext *C, wmOperator *op) return success ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } -static bool make_override_library_poll(bContext *C) +/* Set the object to override. */ +static int make_override_library_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { - Object *obact = CTX_data_active_object(C); - - /* Object must be directly linked to be overridable. */ - return (ED_operator_objectmode(C) && obact != NULL && - (ID_IS_LINKED(obact) || (obact->instance_collection != NULL && - ID_IS_OVERRIDABLE_LIBRARY(obact->instance_collection) && - !ID_IS_OVERRIDE_LIBRARY(obact)))); -} + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *obact = ED_object_active_context(C); -static const EnumPropertyItem *make_override_collections_of_linked_object_itemf( - bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) -{ - EnumPropertyItem item_tmp = {0}, *item = NULL; - int totitem = 0; + /* Sanity checks. */ + if (!scene || ID_IS_LINKED(scene) || !obact) { + return OPERATOR_CANCELLED; + } - Object *object = ED_object_active_context(C); - Main *bmain = CTX_data_main(C); + if ((!ID_IS_LINKED(obact) && obact->instance_collection != NULL && + ID_IS_OVERRIDABLE_LIBRARY(obact->instance_collection)) || + make_override_library_object_overridable_check(bmain, obact)) { + return make_override_library_exec(C, op); + } - if (!object || !ID_IS_LINKED(object)) { - return DummyRNA_DEFAULT_items; + if (!ID_IS_LINKED(obact)) { + BKE_report(op->reports, RPT_ERROR, "Cannot make library override from a local object"); + return OPERATOR_CANCELLED; } + int potential_root_collections_num = 0; + uint collection_session_uuid = MAIN_ID_SESSION_UUID_UNSET; LISTBASE_FOREACH (Collection *, collection, &bmain->collections) { /* Only check for directly linked collections. */ - if (!ID_IS_LINKED(&collection->id) || (collection->id.tag & LIB_TAG_INDIRECT) != 0) { + if (!ID_IS_LINKED(&collection->id) || (collection->id.tag & LIB_TAG_INDIRECT) != 0 || + !BKE_view_layer_has_collection(view_layer, collection)) { continue; } - if (BKE_collection_has_object_recursive(collection, object)) { - item_tmp.identifier = item_tmp.name = collection->id.name + 2; - item_tmp.value = *((int *)&collection->id.session_uuid); - RNA_enum_item_add(&item, &totitem, &item_tmp); + if (BKE_collection_has_object_recursive(collection, obact)) { + if (potential_root_collections_num == 0) { + collection_session_uuid = collection->id.session_uuid; + } + potential_root_collections_num++; } } - RNA_enum_item_end(&item, &totitem); - *r_free = true; + if (potential_root_collections_num <= 1) { + RNA_property_int_set(op->ptr, op->type->prop, *((int *)&collection_session_uuid)); + return make_override_library_exec(C, op); + } - return item; + BKE_reportf(op->reports, + RPT_ERROR, + "Too many potential root collections (%d) for the override hierarchy, " + "please use the Outliner instead", + potential_root_collections_num); + return OPERATOR_CANCELLED; +} + +static bool make_override_library_poll(bContext *C) +{ + Object *obact = CTX_data_active_object(C); + + /* Object must be directly linked to be overridable. */ + return (ED_operator_objectmode(C) && obact != NULL && + (ID_IS_LINKED(obact) || (obact->instance_collection != NULL && + ID_IS_OVERRIDABLE_LIBRARY(obact->instance_collection) && + !ID_IS_OVERRIDE_LIBRARY(obact)))); } void OBJECT_OT_make_override_library(wmOperatorType *ot) @@ -2470,16 +2483,25 @@ void OBJECT_OT_make_override_library(wmOperatorType *ot) /* properties */ PropertyRNA *prop; - prop = RNA_def_enum(ot->srna, - "collection", - DummyRNA_DEFAULT_items, - MAIN_ID_SESSION_UUID_UNSET, - "Override Collection", - "Name of directly linked collection containing the selected object, to make " - "an override from"); - RNA_def_enum_funcs(prop, make_override_collections_of_linked_object_itemf); - RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); + prop = RNA_def_int(ot->srna, + "collection", + MAIN_ID_SESSION_UUID_UNSET, + INT_MIN, + INT_MAX, + "Override Collection", + "Session UUID of the directly linked collection containing the selected " + "object, to make an override from", + INT_MIN, + INT_MAX); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); ot->prop = prop; + + prop = RNA_def_boolean(ot->srna, + "do_fully_editable", + false, + "Create Fully Editable", + "Make all created override data-blocks fully editable"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } /** \} */ @@ -2594,9 +2616,7 @@ void OBJECT_OT_make_single_user(wmOperatorType *ot) /** \name Drop Named Material on Object Operator * \{ */ -char *ED_object_ot_drop_named_material_tooltip(bContext *C, - PointerRNA *properties, - const int mval[2]) +char *ED_object_ot_drop_named_material_tooltip(bContext *C, const char *name, const int mval[2]) { int mat_slot = 0; Object *ob = ED_view3d_give_material_slot_under_cursor(C, mval, &mat_slot); @@ -2605,9 +2625,6 @@ char *ED_object_ot_drop_named_material_tooltip(bContext *C, } mat_slot = max_ii(mat_slot, 1); - char name[MAX_ID_NAME - 2]; - RNA_string_get(properties, "name", name); - Material *prev_mat = BKE_object_material_get(ob, mat_slot); char *result; @@ -2629,11 +2646,9 @@ static int drop_named_material_invoke(bContext *C, wmOperator *op, const wmEvent Object *ob = ED_view3d_give_material_slot_under_cursor(C, event->mval, &mat_slot); mat_slot = max_ii(mat_slot, 1); - Material *ma; - char name[MAX_ID_NAME - 2]; + Material *ma = (Material *)WM_operator_properties_id_lookup_from_name_or_session_uuid( + bmain, op->ptr, ID_MA); - RNA_string_get(op->ptr, "name", name); - ma = (Material *)BKE_libblock_find_name(bmain, ID_MA, name); if (ob == NULL || ma == NULL) { return OPERATOR_CANCELLED; } @@ -2663,7 +2678,7 @@ void OBJECT_OT_drop_named_material(wmOperatorType *ot) ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL; /* properties */ - RNA_def_string(ot->srna, "name", "Material", MAX_ID_NAME - 2, "Name", "Material name to assign"); + WM_operator_properties_id_lookup(ot, true); } /** \} */ diff --git a/source/blender/editors/object/object_remesh.cc b/source/blender/editors/object/object_remesh.cc index ba2efa6e517..8a7138b25ac 100644 --- a/source/blender/editors/object/object_remesh.cc +++ b/source/blender/editors/object/object_remesh.cc @@ -344,7 +344,7 @@ static void voxel_size_edit_draw(const bContext *C, ARegion *UNUSED(ar), void *a BKE_unit_value_as_string(str, VOXEL_SIZE_EDIT_MAX_STR_LEN, (double)(cd->voxel_size * unit->scale_length), - 4, + -3, B_UNIT_LENGTH, unit, true); @@ -415,15 +415,15 @@ static int voxel_size_edit_modal(bContext *C, wmOperator *op, const wmEvent *eve } if (event->modifier & KM_CTRL) { - /* Linear mode, enables jumping to any voxel size. */ - d = d * 0.0005f; - } - else { /* Multiply d by the initial voxel size to prevent uncontrollable speeds when using low voxel * sizes. */ /* When the voxel size is slower, it needs more precision. */ d = d * min_ff(pow2f(cd->init_voxel_size), 0.1f) * 0.05f; } + else { + /* Linear mode, enables jumping to any voxel size. */ + d = d * 0.0005f; + } if (cd->slow_mode) { cd->voxel_size = cd->slow_voxel_size + d * 0.05f; } @@ -592,7 +592,8 @@ static int voxel_size_edit_invoke(bContext *C, wmOperator *op, const wmEvent *ev ED_region_tag_redraw(region); const char *status_str = TIP_( - "Move the mouse to change the voxel size. LMB: confirm size, ESC/RMB: cancel"); + "Move the mouse to change the voxel size. CTRL: Relative Scale, SHIFT: Precision Mode, " + "ENTER/LMB: Confirm Size, ESC/RMB: Cancel"); ED_workspace_status_text(C, status_str); return OPERATOR_RUNNING_MODAL; diff --git a/source/blender/editors/object/object_transform.cc b/source/blender/editors/object/object_transform.cc index da3df159c33..82e67231ec1 100644 --- a/source/blender/editors/object/object_transform.cc +++ b/source/blender/editors/object/object_transform.cc @@ -1125,7 +1125,7 @@ static int object_transform_apply_invoke(bContext *C, wmOperator *op, const wmEv bool can_handle_multiuser = apply_objects_internal_can_multiuser(C); bool need_single_user = can_handle_multiuser && apply_objects_internal_need_single_user(C); - if ((ob->data != nullptr) && need_single_user) { + if ((ob != nullptr) && (ob->data != nullptr) && need_single_user) { PropertyRNA *prop = RNA_struct_find_property(op->ptr, "isolate_users"); if (!RNA_property_is_set(op->ptr, prop)) { RNA_property_boolean_set(op->ptr, prop, true); diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c index 492ece0b40e..9ad36cacc7d 100644 --- a/source/blender/editors/object/object_vgroup.c +++ b/source/blender/editors/object/object_vgroup.c @@ -4266,7 +4266,6 @@ static void vgroup_copy_active_to_sel_single(Object *ob, const int def_nr) Mesh *me = ob->data; BMEditMesh *em = me->edit_mesh; - float weight_act; int i; if (em) { @@ -4278,18 +4277,15 @@ static void vgroup_copy_active_to_sel_single(Object *ob, const int def_nr) if (dvert_act == NULL) { return; } - weight_act = BKE_defvert_find_weight(dvert_act, def_nr); BM_ITER_MESH_INDEX (eve, &iter, em->bm, BM_VERTS_OF_MESH, i) { if (BM_elem_flag_test(eve, BM_ELEM_SELECT) && (eve != eve_act)) { - MDeformVert *dv = BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset); - MDeformWeight *dw = BKE_defvert_find_index(dv, def_nr); - if (dw) { - dw->weight = weight_act; + MDeformVert *dvert_dst = BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset); - if (me->symmetry & ME_SYMMETRY_X) { - ED_mesh_defvert_mirror_update_em(ob, eve, -1, i, cd_dvert_offset); - } + BKE_defvert_copy_index(dvert_dst, def_nr, dvert_act, def_nr); + + if (me->symmetry & ME_SYMMETRY_X) { + ED_mesh_defvert_mirror_update_em(ob, eve, -1, i, cd_dvert_offset); } } } @@ -4306,17 +4302,15 @@ static void vgroup_copy_active_to_sel_single(Object *ob, const int def_nr) if (dvert_act == NULL) { return; } - weight_act = BKE_defvert_find_weight(dvert_act, def_nr); dv = me->dvert; for (i = 0; i < me->totvert; i++, dv++) { if ((me->mvert[i].flag & SELECT) && (dv != dvert_act)) { - MDeformWeight *dw = BKE_defvert_find_index(dv, def_nr); - if (dw) { - dw->weight = weight_act; - if (me->symmetry & ME_SYMMETRY_X) { - ED_mesh_defvert_mirror_update_ob(ob, -1, i); - } + + BKE_defvert_copy_index(dv, def_nr, dvert_act, def_nr); + + if (me->symmetry & ME_SYMMETRY_X) { + ED_mesh_defvert_mirror_update_ob(ob, -1, i); } } } diff --git a/source/blender/editors/render/render_preview.cc b/source/blender/editors/render/render_preview.cc index a56f513e98f..addcedc4481 100644 --- a/source/blender/editors/render/render_preview.cc +++ b/source/blender/editors/render/render_preview.cc @@ -1306,7 +1306,7 @@ static ImBuf *icon_preview_imbuf_from_brush(Brush *brush) { static const int flags = IB_rect | IB_multilayer | IB_metadata; - char path[FILE_MAX]; + char filepath[FILE_MAX]; const char *folder; if (!(brush->icon_imbuf)) { @@ -1315,22 +1315,22 @@ static ImBuf *icon_preview_imbuf_from_brush(Brush *brush) if (brush->icon_filepath[0]) { /* First use the path directly to try and load the file. */ - BLI_strncpy(path, brush->icon_filepath, sizeof(brush->icon_filepath)); - BLI_path_abs(path, ID_BLEND_PATH_FROM_GLOBAL(&brush->id)); + BLI_strncpy(filepath, brush->icon_filepath, sizeof(brush->icon_filepath)); + BLI_path_abs(filepath, ID_BLEND_PATH_FROM_GLOBAL(&brush->id)); /* Use default color-spaces for brushes. */ - brush->icon_imbuf = IMB_loadiffname(path, flags, nullptr); + brush->icon_imbuf = IMB_loadiffname(filepath, flags, nullptr); /* otherwise lets try to find it in other directories */ if (!(brush->icon_imbuf)) { folder = BKE_appdir_folder_id(BLENDER_DATAFILES, "brushicons"); BLI_make_file_string( - BKE_main_blendfile_path_from_global(), path, folder, brush->icon_filepath); + BKE_main_blendfile_path_from_global(), filepath, folder, brush->icon_filepath); - if (path[0]) { + if (filepath[0]) { /* Use default color spaces. */ - brush->icon_imbuf = IMB_loadiffname(path, flags, nullptr); + brush->icon_imbuf = IMB_loadiffname(filepath, flags, nullptr); } } @@ -1821,13 +1821,13 @@ void PreviewLoadJob::run_fn(void *customdata, const char *deferred_data = static_cast<char *>(PRV_DEFERRED_DATA(preview)); const ThumbSource source = static_cast<ThumbSource>(deferred_data[0]); - const char *path = &deferred_data[1]; + const char *filepath = &deferred_data[1]; - // printf("loading deferred %d×%d preview for %s\n", request->sizex, request->sizey, path); + // printf("loading deferred %d×%d preview for %s\n", request->sizex, request->sizey, filepath); - IMB_thumb_path_lock(path); - ImBuf *thumb = IMB_thumb_manage(path, THB_LARGE, source); - IMB_thumb_path_unlock(path); + IMB_thumb_path_lock(filepath); + ImBuf *thumb = IMB_thumb_manage(filepath, THB_LARGE, source); + IMB_thumb_path_unlock(filepath); if (thumb) { /* PreviewImage assumes premultiplied alpha... */ diff --git a/source/blender/editors/scene/scene_edit.c b/source/blender/editors/scene/scene_edit.c index a13177942a8..57a9e6be917 100644 --- a/source/blender/editors/scene/scene_edit.c +++ b/source/blender/editors/scene/scene_edit.c @@ -67,7 +67,10 @@ static Scene *scene_add(Main *bmain, Scene *scene_old, eSceneCopyMethod method) } /** Add a new scene in the sequence editor. */ -static Scene *ED_scene_sequencer_add(Main *bmain, bContext *C, eSceneCopyMethod method) +Scene *ED_scene_sequencer_add(Main *bmain, + bContext *C, + eSceneCopyMethod method, + const bool assign_strip) { Sequence *seq = NULL; Scene *scene_active = CTX_data_scene(C); @@ -88,6 +91,11 @@ static Scene *ED_scene_sequencer_add(Main *bmain, bContext *C, eSceneCopyMethod Scene *scene_new = scene_add(bmain, scene_strip, method); + /* If don't need assign the scene to the strip, nothing else to do. */ + if (!assign_strip) { + return scene_new; + } + /* As the scene is created in sequencer, do not set the new scene as active. * This is useful for story-boarding where we want to keep actual scene active. * The new scene is linked to the active strip and the viewport updated. */ @@ -291,7 +299,7 @@ static int scene_new_sequencer_exec(bContext *C, wmOperator *op) Main *bmain = CTX_data_main(C); int type = RNA_enum_get(op->ptr, "type"); - if (ED_scene_sequencer_add(bmain, C, type) == NULL) { + if (ED_scene_sequencer_add(bmain, C, type, true) == NULL) { return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index ad815f0d998..f58baa0e25c 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -145,6 +145,10 @@ void ED_region_do_listen(wmRegionListenerParams *params) region->type->listener(params); } + LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { + UI_block_views_listen(block, params); + } + LISTBASE_FOREACH (uiList *, list, ®ion->ui_lists) { if (list->type && list->type->listener) { list->type->listener(list, params); @@ -3454,9 +3458,9 @@ ScrArea *ED_area_find_under_cursor(const bContext *C, int spacetype, const int x if (!area) { /* Check all windows except the active one. */ int scr_pos[2]; - wmWindow *r_win = WM_window_find_under_cursor(win, xy, scr_pos); - if (r_win && r_win != win) { - win = r_win; + wmWindow *win_other = WM_window_find_under_cursor(win, xy, scr_pos); + if (win_other && win_other != win) { + win = win_other; screen = WM_window_get_active_screen(win); area = BKE_screen_find_area_xy(screen, spacetype, scr_pos); } diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index d5385181055..08c8c863729 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -626,8 +626,8 @@ void ED_screen_refresh(wmWindowManager *wm, wmWindow *win) screen_geom_vertices_scale(win, screen); ED_screen_areas_iter (win, screen, area) { - /* set spacetype and region callbacks, calls init() */ - /* sets subwindows for regions, adds handlers */ + /* Set space-type and region callbacks, calls init() */ + /* Sets sub-windows for regions, adds handlers. */ ED_area_init(wm, win, area); } @@ -1315,7 +1315,7 @@ void ED_screen_full_restore(bContext *C, ScrArea *area) else { ED_screen_state_toggle(C, win, area, state); } - /* warning: 'area' may be freed */ + /* WARNING: 'area' may be freed */ } /* otherwise just tile the area again */ else { diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index d7cf09ca89a..212fb846b43 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -659,36 +659,10 @@ bool ED_operator_editmball(bContext *C) return false; } -bool ED_operator_mask(bContext *C) -{ - ScrArea *area = CTX_wm_area(C); - if (area && area->spacedata.first) { - switch (area->spacetype) { - case SPACE_CLIP: { - SpaceClip *screen = area->spacedata.first; - return ED_space_clip_check_show_maskedit(screen); - } - case SPACE_SEQ: { - SpaceSeq *sseq = area->spacedata.first; - Scene *scene = CTX_data_scene(C); - return ED_space_sequencer_check_show_maskedit(sseq, scene); - } - case SPACE_IMAGE: { - SpaceImage *sima = area->spacedata.first; - ViewLayer *view_layer = CTX_data_view_layer(C); - Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer); - return ED_space_image_check_show_maskedit(sima, obedit); - } - } - } - - return false; -} - -bool ED_operator_camera(bContext *C) +bool ED_operator_camera_poll(bContext *C) { struct Camera *cam = CTX_data_pointer_get_type(C, "camera", &RNA_Camera).data; - return (cam != NULL); + return (cam != NULL && !ID_IS_LINKED(cam)); } /** \} */ @@ -1477,7 +1451,7 @@ static int area_close_exec(bContext *C, wmOperator *op) bScreen *screen = CTX_wm_screen(C); ScrArea *area = CTX_wm_area(C); - /* This operator is scriptable, so the area passed could be invalid. */ + /* This operator is script-able, so the area passed could be invalid. */ if (BLI_findindex(&screen->areabase, area) == -1) { BKE_report(op->reports, RPT_ERROR, "Area not found in the active screen"); return OPERATOR_CANCELLED; @@ -2490,6 +2464,7 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_CANCELLED; case EVT_LEFTCTRLKEY: + case EVT_RIGHTCTRLKEY: sd->do_snap = event->val == KM_PRESS; update_factor = true; break; @@ -2722,7 +2697,7 @@ static int region_scale_invoke(bContext *C, wmOperator *op, const wmEvent *event rmd->region->sizey = rmd->region->winy; } - /* now copy to regionmovedata */ + /* Now copy to region-move-data. */ if (ELEM(rmd->edge, AE_LEFT_TO_TOPRIGHT, AE_RIGHT_TO_TOPLEFT)) { rmd->origval = rmd->region->sizex; } diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index d3bf28798c4..8879161d2af 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -10,6 +10,7 @@ set(INC ../../depsgraph ../../draw ../../functions + ../../geometry ../../gpu ../../imbuf ../../makesdna @@ -33,6 +34,8 @@ set(SRC curves_sculpt_delete.cc curves_sculpt_grow_shrink.cc curves_sculpt_ops.cc + curves_sculpt_selection.cc + curves_sculpt_selection_paint.cc curves_sculpt_snake_hook.cc paint_canvas.cc paint_cursor.c @@ -49,13 +52,13 @@ set(SRC paint_stroke.c paint_utils.c paint_vertex.cc - paint_vertex_color_ops.c + paint_vertex_color_ops.cc paint_vertex_color_utils.c paint_vertex_proj.c paint_vertex_weight_ops.c paint_vertex_weight_utils.c sculpt.c - sculpt_automasking.c + sculpt_automasking.cc sculpt_boundary.c sculpt_brush_types.c sculpt_cloth.c diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc index 1fdecf47bbd..bac03de77e7 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc @@ -18,11 +18,13 @@ #include "BKE_bvhutils.h" #include "BKE_context.h" #include "BKE_curves.hh" +#include "BKE_curves_utils.hh" +#include "BKE_geometry_set.hh" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" +#include "BKE_mesh_sample.hh" #include "BKE_paint.h" #include "BKE_report.h" -#include "BKE_spline.hh" #include "DNA_brush_enums.h" #include "DNA_brush_types.h" @@ -36,8 +38,12 @@ #include "ED_screen.h" #include "ED_view3d.h" +#include "GEO_add_curves_on_mesh.hh" + +#include "WM_api.h" + /** - * The code below uses a prefix naming convention to indicate the coordinate space: + * The code below uses a suffix naming convention to indicate the coordinate space: * cu: Local space of the curves object that is being edited. * su: Local space of the surface object. * wo: World space. @@ -63,80 +69,52 @@ class AddOperation : public CurvesSculptStrokeOperation { } } - void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override; + void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override; }; -static void initialize_straight_curve_positions(const float3 &p1, - const float3 &p2, - MutableSpan<float3> r_positions) -{ - const float step = 1.0f / (float)(r_positions.size() - 1); - for (const int i : r_positions.index_range()) { - r_positions[i] = math::interpolate(p1, p2, i * step); - } -} - /** * Utility class that actually executes the update when the stroke is updated. That's useful * because it avoids passing a very large number of parameters between functions. */ struct AddOperationExecutor { AddOperation *self_ = nullptr; - Depsgraph *depsgraph_ = nullptr; - Scene *scene_ = nullptr; + CurvesSculptCommonContext ctx_; + Object *object_ = nullptr; - ARegion *region_ = nullptr; - View3D *v3d_ = nullptr; Curves *curves_id_ = nullptr; CurvesGeometry *curves_ = nullptr; Object *surface_ob_ = nullptr; Mesh *surface_ = nullptr; Span<MLoopTri> surface_looptris_; - Span<float3> corner_normals_su_; - CurvesSculpt *curves_sculpt_ = nullptr; - Brush *brush_ = nullptr; - BrushCurvesSculptSettings *brush_settings_ = nullptr; + const CurvesSculpt *curves_sculpt_ = nullptr; + const Brush *brush_ = nullptr; + const BrushCurvesSculptSettings *brush_settings_ = nullptr; + int add_amount_; + bool use_front_face_; float brush_radius_re_; float2 brush_pos_re_; - bool use_front_face_; - bool interpolate_length_; - bool interpolate_shape_; - bool use_interpolation_; - float new_curve_length_; - int add_amount_; - int points_per_curve_; - - /** Various matrices to convert between coordinate spaces. */ - float4x4 curves_to_world_mat_; - float4x4 world_to_curves_mat_; - float4x4 world_to_surface_mat_; - float4x4 surface_to_world_mat_; - float4x4 surface_to_curves_mat_; - float4x4 surface_to_curves_normal_mat_; + CurvesSculptTransforms transforms_; BVHTreeFromMesh surface_bvh_; - int tot_old_curves_; - int tot_old_points_; - struct AddedPoints { Vector<float3> positions_cu; Vector<float3> bary_coords; Vector<int> looptri_indices; }; - void execute(AddOperation &self, bContext *C, const StrokeExtension &stroke_extension) + AddOperationExecutor(const bContext &C) : ctx_(C) + { + } + + void execute(AddOperation &self, const bContext &C, const StrokeExtension &stroke_extension) { self_ = &self; - depsgraph_ = CTX_data_depsgraph_pointer(C); - scene_ = CTX_data_scene(C); - object_ = CTX_data_active_object(C); - region_ = CTX_wm_region(C); - v3d_ = CTX_wm_view3d(C); + object_ = CTX_data_active_object(&C); curves_id_ = static_cast<Curves *>(object_->data); curves_ = &CurvesGeometry::wrap(curves_id_->geometry); @@ -145,41 +123,21 @@ struct AddOperationExecutor { return; } - curves_to_world_mat_ = object_->obmat; - world_to_curves_mat_ = curves_to_world_mat_.inverted(); + transforms_ = CurvesSculptTransforms(*object_, curves_id_->surface); surface_ob_ = curves_id_->surface; surface_ = static_cast<Mesh *>(surface_ob_->data); - surface_to_world_mat_ = surface_ob_->obmat; - world_to_surface_mat_ = surface_to_world_mat_.inverted(); - surface_to_curves_mat_ = world_to_curves_mat_ * surface_to_world_mat_; - surface_to_curves_normal_mat_ = surface_to_curves_mat_.inverted().transposed(); - - if (!CustomData_has_layer(&surface_->ldata, CD_NORMAL)) { - BKE_mesh_calc_normals_split(surface_); - } - corner_normals_su_ = { - reinterpret_cast<const float3 *>(CustomData_get_layer(&surface_->ldata, CD_NORMAL)), - surface_->totloop}; - curves_sculpt_ = scene_->toolsettings->curves_sculpt; - brush_ = BKE_paint_brush(&curves_sculpt_->paint); + curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt; + brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint); brush_settings_ = brush_->curves_sculpt_settings; - brush_radius_re_ = BKE_brush_size_get(scene_, brush_); + brush_radius_re_ = brush_radius_get(*ctx_.scene, *brush_, stroke_extension); brush_pos_re_ = stroke_extension.mouse_position; use_front_face_ = brush_->flag & BRUSH_FRONTFACE; const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>( brush_->falloff_shape); add_amount_ = std::max(0, brush_settings_->add_amount); - points_per_curve_ = std::max(2, brush_settings_->points_per_curve); - interpolate_length_ = brush_settings_->flag & BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH; - interpolate_shape_ = brush_settings_->flag & BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE; - use_interpolation_ = interpolate_length_ || interpolate_shape_; - new_curve_length_ = brush_settings_->curve_length; - - tot_old_curves_ = curves_->curves_num(); - tot_old_points_ = curves_->points_num(); if (add_amount_ == 0) { return; @@ -215,31 +173,56 @@ struct AddOperationExecutor { return; } - if (use_interpolation_) { - this->ensure_curve_roots_kdtree(); + /* Find UV map. */ + VArray_Span<float2> surface_uv_map; + if (curves_id_->surface_uv_map != nullptr) { + MeshComponent surface_component; + surface_component.replace(surface_, GeometryOwnershipType::ReadOnly); + surface_uv_map = surface_component + .attribute_try_get_for_read(curves_id_->surface_uv_map, + ATTR_DOMAIN_CORNER) + .typed<float2>(); } - const int tot_added_curves = added_points.bary_coords.size(); - const int tot_added_points = tot_added_curves * points_per_curve_; + /* Find normals. */ + if (!CustomData_has_layer(&surface_->ldata, CD_NORMAL)) { + BKE_mesh_calc_normals_split(surface_); + } + const Span<float3> corner_normals_su = { + reinterpret_cast<const float3 *>(CustomData_get_layer(&surface_->ldata, CD_NORMAL)), + surface_->totloop}; - curves_->resize(curves_->points_num() + tot_added_points, - curves_->curves_num() + tot_added_curves); + geometry::AddCurvesOnMeshInputs add_inputs; + add_inputs.root_positions_cu = added_points.positions_cu; + add_inputs.bary_coords = added_points.bary_coords; + add_inputs.looptri_indices = added_points.looptri_indices; + add_inputs.interpolate_length = brush_settings_->flag & + BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH; + add_inputs.interpolate_shape = brush_settings_->flag & + BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE; + add_inputs.interpolate_point_count = brush_settings_->flag & + BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_POINT_COUNT; + add_inputs.fallback_curve_length = brush_settings_->curve_length; + add_inputs.fallback_point_count = std::max(2, brush_settings_->points_per_curve); + add_inputs.surface = surface_; + add_inputs.surface_bvh = &surface_bvh_; + add_inputs.surface_looptris = surface_looptris_; + add_inputs.surface_uv_map = surface_uv_map; + add_inputs.corner_normals_su = corner_normals_su; + add_inputs.curves_to_surface_mat = transforms_.curves_to_surface; + add_inputs.surface_to_curves_normal_mat = transforms_.surface_to_curves_normal; + + if (add_inputs.interpolate_length || add_inputs.interpolate_shape || + add_inputs.interpolate_point_count) { + this->ensure_curve_roots_kdtree(); + add_inputs.old_roots_kdtree = self_->curve_roots_kdtree_; + } - threading::parallel_invoke([&]() { this->initialize_curve_offsets(tot_added_curves); }, - [&]() { this->initialize_attributes(added_points); }); + geometry::add_curves_on_mesh(*curves_, add_inputs); DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); - ED_region_tag_redraw(region_); - } - - float3 get_bary_coords(const Mesh &mesh, const MLoopTri &looptri, const float3 position) const - { - const float3 &v0 = mesh.mvert[mesh.mloop[looptri.tri[0]].v].co; - const float3 &v1 = mesh.mvert[mesh.mloop[looptri.tri[1]].v].co; - const float3 &v2 = mesh.mvert[mesh.mloop[looptri.tri[2]].v].co; - float3 bary_coords; - interp_weights_tri_v3(bary_coords, v0, v1, v2, position); - return bary_coords; + WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); + ED_region_tag_redraw(ctx_.region); } /** @@ -249,16 +232,16 @@ struct AddOperationExecutor { { float3 ray_start_wo, ray_end_wo; ED_view3d_win_to_segment_clipped( - depsgraph_, region_, v3d_, brush_pos_re_, ray_start_wo, ray_end_wo, true); - const float3 ray_start_su = world_to_surface_mat_ * ray_start_wo; - const float3 ray_end_su = world_to_surface_mat_ * ray_end_wo; + ctx_.depsgraph, ctx_.region, ctx_.v3d, brush_pos_re_, ray_start_wo, ray_end_wo, true); + const float3 ray_start_cu = transforms_.world_to_curves * ray_start_wo; + const float3 ray_end_cu = transforms_.world_to_curves * ray_end_wo; const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( eCurvesSymmetryType(curves_id_->symmetry)); for (const float4x4 &brush_transform : symmetry_brush_transforms) { - this->sample_in_center( - r_added_points, brush_transform * ray_start_su, brush_transform * ray_end_su); + const float4x4 transform = transforms_.curves_to_surface * brush_transform; + this->sample_in_center(r_added_points, transform * ray_start_cu, transform * ray_end_cu); } } @@ -285,10 +268,10 @@ struct AddOperationExecutor { const int looptri_index = ray_hit.index; const float3 brush_pos_su = ray_hit.co; - const float3 bary_coords = this->get_bary_coords( + const float3 bary_coords = bke::mesh_surface_sample::compute_bary_coord_in_triangle( *surface_, surface_looptris_[looptri_index], brush_pos_su); - const float3 brush_pos_cu = surface_to_curves_mat_ * brush_pos_su; + const float3 brush_pos_cu = transforms_.surface_to_curves * brush_pos_su; r_added_points.positions_cu.append(brush_pos_cu); r_added_points.bary_coords.append(bary_coords); @@ -312,57 +295,37 @@ struct AddOperationExecutor { const float4x4 &brush_transform) { const int old_amount = r_added_points.bary_coords.size(); - const int max_iterations = std::max(100'000, add_amount_ * 10); + const int max_iterations = 100; int current_iteration = 0; while (r_added_points.bary_coords.size() < old_amount + add_amount_) { if (current_iteration++ >= max_iterations) { break; } - - const float r = brush_radius_re_ * std::sqrt(rng.get_float()); - const float angle = rng.get_float() * 2.0f * M_PI; - const float2 pos_re = brush_pos_re_ + r * float2(std::cos(angle), std::sin(angle)); - - float3 ray_start_wo, ray_end_wo; - ED_view3d_win_to_segment_clipped( - depsgraph_, region_, v3d_, pos_re, ray_start_wo, ray_end_wo, true); - const float3 ray_start_su = brush_transform * (world_to_surface_mat_ * ray_start_wo); - const float3 ray_end_su = brush_transform * (world_to_surface_mat_ * ray_end_wo); - const float3 ray_direction_su = math::normalize(ray_end_su - ray_start_su); - - BVHTreeRayHit ray_hit; - ray_hit.dist = FLT_MAX; - ray_hit.index = -1; - BLI_bvhtree_ray_cast(surface_bvh_.tree, - ray_start_su, - ray_direction_su, - 0.0f, - &ray_hit, - surface_bvh_.raycast_callback, - &surface_bvh_); - - if (ray_hit.index == -1) { - continue; - } - - if (use_front_face_) { - const float3 normal_su = ray_hit.no; - if (math::dot(ray_direction_su, normal_su) >= 0.0f) { - continue; - } + const int missing_amount = add_amount_ + old_amount - r_added_points.bary_coords.size(); + const int new_points = bke::mesh_surface_sample::sample_surface_points_projected( + rng, + *surface_, + surface_bvh_, + brush_pos_re_, + brush_radius_re_, + [&](const float2 &pos_re, float3 &r_start_su, float3 &r_end_su) { + float3 start_wo, end_wo; + ED_view3d_win_to_segment_clipped( + ctx_.depsgraph, ctx_.region, ctx_.v3d, pos_re, start_wo, end_wo, true); + const float3 start_cu = brush_transform * (transforms_.world_to_curves * start_wo); + const float3 end_cu = brush_transform * (transforms_.world_to_curves * end_wo); + r_start_su = transforms_.curves_to_surface * start_cu; + r_end_su = transforms_.curves_to_surface * end_cu; + }, + use_front_face_, + add_amount_, + missing_amount, + r_added_points.bary_coords, + r_added_points.looptri_indices, + r_added_points.positions_cu); + for (float3 &pos : r_added_points.positions_cu.as_mutable_span().take_back(new_points)) { + pos = transforms_.surface_to_curves * pos; } - - const int looptri_index = ray_hit.index; - const float3 pos_su = ray_hit.co; - - const float3 bary_coords = this->get_bary_coords( - *surface_, surface_looptris_[looptri_index], pos_su); - - const float3 pos_cu = surface_to_curves_mat_ * pos_su; - - r_added_points.positions_cu.append(pos_cu); - r_added_points.bary_coords.append(bary_coords); - r_added_points.looptri_indices.append(looptri_index); } } @@ -371,66 +334,47 @@ struct AddOperationExecutor { */ void sample_spherical_with_symmetry(RandomNumberGenerator &rng, AddedPoints &r_added_points) { - /* Find ray that starts in the center of the brush. */ - float3 brush_ray_start_wo, brush_ray_end_wo; - ED_view3d_win_to_segment_clipped( - depsgraph_, region_, v3d_, brush_pos_re_, brush_ray_start_wo, brush_ray_end_wo, true); - const float3 brush_ray_start_su = world_to_surface_mat_ * brush_ray_start_wo; - const float3 brush_ray_end_su = world_to_surface_mat_ * brush_ray_end_wo; - - /* Find ray that starts on the boundary of the brush. That is used to compute the brush radius - * in 3D. */ - float3 brush_radius_ray_start_wo, brush_radius_ray_end_wo; - ED_view3d_win_to_segment_clipped(depsgraph_, - region_, - v3d_, - brush_pos_re_ + float2(brush_radius_re_, 0), - brush_radius_ray_start_wo, - brush_radius_ray_end_wo, + const std::optional<CurvesBrush3D> brush_3d = sample_curves_surface_3d_brush(*ctx_.depsgraph, + *ctx_.region, + *ctx_.v3d, + transforms_, + surface_bvh_, + brush_pos_re_, + brush_radius_re_); + if (!brush_3d.has_value()) { + return; + } + + float3 view_ray_start_wo, view_ray_end_wo; + ED_view3d_win_to_segment_clipped(ctx_.depsgraph, + ctx_.region, + ctx_.v3d, + brush_pos_re_, + view_ray_start_wo, + view_ray_end_wo, true); - const float3 brush_radius_ray_start_su = world_to_surface_mat_ * brush_radius_ray_start_wo; - const float3 brush_radius_ray_end_su = world_to_surface_mat_ * brush_radius_ray_end_wo; + const float3 view_direction_su = math::normalize( + transforms_.world_to_surface * view_ray_end_wo - + transforms_.world_to_surface * view_ray_start_wo); const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( eCurvesSymmetryType(curves_id_->symmetry)); for (const float4x4 &brush_transform : symmetry_brush_transforms) { - this->sample_spherical(rng, - r_added_points, - brush_transform * brush_ray_start_su, - brush_transform * brush_ray_end_su, - brush_transform * brush_radius_ray_start_su, - brush_transform * brush_radius_ray_end_su); + const float4x4 transform = transforms_.curves_to_surface * brush_transform; + const float3 brush_pos_su = transform * brush_3d->position_cu; + const float brush_radius_su = transform_brush_radius( + transform, brush_3d->position_cu, brush_3d->radius_cu); + this->sample_spherical( + rng, r_added_points, brush_pos_su, brush_radius_su, view_direction_su); } } void sample_spherical(RandomNumberGenerator &rng, AddedPoints &r_added_points, - const float3 &brush_ray_start_su, - const float3 &brush_ray_end_su, - const float3 &brush_radius_ray_start_su, - const float3 &brush_radius_ray_end_su) + const float3 &brush_pos_su, + const float brush_radius_su, + const float3 &view_direction_su) { - const float3 brush_ray_direction_su = math::normalize(brush_ray_end_su - brush_ray_start_su); - - BVHTreeRayHit ray_hit; - ray_hit.dist = FLT_MAX; - ray_hit.index = -1; - BLI_bvhtree_ray_cast(surface_bvh_.tree, - brush_ray_start_su, - brush_ray_direction_su, - 0.0f, - &ray_hit, - surface_bvh_.raycast_callback, - &surface_bvh_); - - if (ray_hit.index == -1) { - return; - } - - /* Compute brush radius. */ - const float3 brush_pos_su = ray_hit.co; - const float brush_radius_su = dist_to_line_v3( - brush_pos_su, brush_radius_ray_start_su, brush_radius_ray_end_su); const float brush_radius_sq_su = pow2f(brush_radius_su); /* Find surface triangles within brush radius. */ @@ -447,7 +391,7 @@ struct AddOperationExecutor { const float3 v2_su = surface_->mvert[surface_->mloop[looptri.tri[2]].v].co; float3 normal_su; normal_tri_v3(normal_su, v0_su, v1_su, v2_su); - if (math::dot(normal_su, brush_ray_direction_su) >= 0.0f) { + if (math::dot(normal_su, view_direction_su) >= 0.0f) { return; } looptri_indices.append(index); @@ -469,9 +413,6 @@ struct AddOperationExecutor { const float brush_plane_area_su = M_PI * brush_radius_sq_su; const float approximate_density_su = add_amount_ / brush_plane_area_su; - /* Used for switching between two triangle sampling strategies. */ - const float area_threshold = brush_plane_area_su; - /* Usually one or two iterations should be enough. */ const int max_iterations = 5; int current_iteration = 0; @@ -481,78 +422,18 @@ struct AddOperationExecutor { if (current_iteration++ >= max_iterations) { break; } - - for (const int looptri_index : looptri_indices) { - const MLoopTri &looptri = surface_looptris_[looptri_index]; - - const float3 v0_su = surface_->mvert[surface_->mloop[looptri.tri[0]].v].co; - const float3 v1_su = surface_->mvert[surface_->mloop[looptri.tri[1]].v].co; - const float3 v2_su = surface_->mvert[surface_->mloop[looptri.tri[2]].v].co; - - const float looptri_area_su = area_tri_v3(v0_su, v1_su, v2_su); - - if (looptri_area_su < area_threshold) { - /* The triangle is small compared to the brush radius. Sample by generating random - * barycentric coordinates. */ - const int amount = rng.round_probabilistic(approximate_density_su * looptri_area_su); - for ([[maybe_unused]] const int i : IndexRange(amount)) { - const float3 bary_coord = rng.get_barycentric_coordinates(); - const float3 point_pos_su = attribute_math::mix3(bary_coord, v0_su, v1_su, v2_su); - const float distance_to_brush_sq_su = math::distance_squared(point_pos_su, - brush_pos_su); - if (distance_to_brush_sq_su > brush_radius_sq_su) { - continue; - } - - r_added_points.bary_coords.append(bary_coord); - r_added_points.looptri_indices.append(looptri_index); - r_added_points.positions_cu.append(surface_to_curves_mat_ * point_pos_su); - } - } - else { - /* The triangle is large compared to the brush radius. Sample by generating random points - * on the triangle plane within the brush radius. */ - float3 normal_su; - normal_tri_v3(normal_su, v0_su, v1_su, v2_su); - - float3 brush_pos_proj_su = brush_pos_su; - project_v3_plane(brush_pos_proj_su, normal_su, v0_su); - - const float proj_distance_sq_su = math::distance_squared(brush_pos_proj_su, - brush_pos_su); - const float brush_radius_factor_sq = 1.0f - - std::min(1.0f, - proj_distance_sq_su / brush_radius_sq_su); - const float radius_proj_sq_su = brush_radius_sq_su * brush_radius_factor_sq; - const float radius_proj_su = std::sqrt(radius_proj_sq_su); - const float circle_area_su = M_PI * radius_proj_su; - - const int amount = rng.round_probabilistic(approximate_density_su * circle_area_su); - - const float3 axis_1_su = math::normalize(v1_su - v0_su) * radius_proj_su; - const float3 axis_2_su = math::normalize(math::cross( - axis_1_su, math::cross(axis_1_su, v2_su - v0_su))) * - radius_proj_su; - - for ([[maybe_unused]] const int i : IndexRange(amount)) { - const float r = std::sqrt(rng.get_float()); - const float angle = rng.get_float() * 2.0f * M_PI; - const float x = r * std::cos(angle); - const float y = r * std::sin(angle); - const float3 point_pos_su = brush_pos_proj_su + axis_1_su * x + axis_2_su * y; - if (!isect_point_tri_prism_v3(point_pos_su, v0_su, v1_su, v2_su)) { - /* Sampled point is not in the triangle. */ - continue; - } - - float3 bary_coord; - interp_weights_tri_v3(bary_coord, v0_su, v1_su, v2_su, point_pos_su); - - r_added_points.bary_coords.append(bary_coord); - r_added_points.looptri_indices.append(looptri_index); - r_added_points.positions_cu.append(surface_to_curves_mat_ * point_pos_su); - } - } + const int new_points = bke::mesh_surface_sample::sample_surface_points_spherical( + rng, + *surface_, + looptri_indices, + brush_pos_su, + brush_radius_su, + approximate_density_su, + r_added_points.bary_coords, + r_added_points.looptri_indices, + r_added_points.positions_cu); + for (float3 &pos : r_added_points.positions_cu.as_mutable_span().take_back(new_points)) { + pos = transforms_.surface_to_curves * pos; } } @@ -577,271 +458,20 @@ struct AddOperationExecutor { BLI_kdtree_3d_balance(self_->curve_roots_kdtree_); } } - - void initialize_curve_offsets(const int tot_added_curves) - { - MutableSpan<int> offsets = curves_->offsets_for_write(); - threading::parallel_for(IndexRange(tot_added_curves), 1024, [&](const IndexRange range) { - for (const int i : range) { - const int curve_i = tot_old_curves_ + i; - offsets[curve_i + 1] = tot_old_points_ + (i + 1) * points_per_curve_; - } - }); - } - - struct NeighborInfo { - /* Curve index of the neighbor. */ - int index; - /* The weights of all neighbors of a new curve add up to 1. */ - float weight; - }; - static constexpr int max_neighbors = 5; - using NeighborsVector = Vector<NeighborInfo, max_neighbors>; - - void initialize_attributes(const AddedPoints &added_points) - { - Array<NeighborsVector> neighbors_per_curve; - if (use_interpolation_) { - neighbors_per_curve = this->find_curve_neighbors(added_points); - } - - Array<float> new_lengths_cu(added_points.bary_coords.size()); - if (interpolate_length_) { - this->interpolate_lengths(neighbors_per_curve, new_lengths_cu); - } - else { - new_lengths_cu.fill(new_curve_length_); - } - - Array<float3> new_normals_su = this->compute_normals_for_added_curves_su(added_points); - this->initialize_surface_attachment(added_points); - - if (interpolate_shape_) { - this->initialize_position_with_interpolation( - added_points, neighbors_per_curve, new_normals_su, new_lengths_cu); - } - else { - this->initialize_position_without_interpolation( - added_points, new_lengths_cu, new_normals_su); - } - } - - Array<NeighborsVector> find_curve_neighbors(const AddedPoints &added_points) - { - const int tot_added_curves = added_points.bary_coords.size(); - Array<NeighborsVector> neighbors_per_curve(tot_added_curves); - threading::parallel_for(IndexRange(tot_added_curves), 128, [&](const IndexRange range) { - for (const int i : range) { - const float3 root_cu = added_points.positions_cu[i]; - std::array<KDTreeNearest_3d, max_neighbors> nearest_n; - const int found_neighbors = BLI_kdtree_3d_find_nearest_n( - self_->curve_roots_kdtree_, root_cu, nearest_n.data(), max_neighbors); - float tot_weight = 0.0f; - for (const int neighbor_i : IndexRange(found_neighbors)) { - KDTreeNearest_3d &nearest = nearest_n[neighbor_i]; - const float weight = 1.0f / std::max(nearest.dist, 0.00001f); - tot_weight += weight; - neighbors_per_curve[i].append({nearest.index, weight}); - } - /* Normalize weights. */ - for (NeighborInfo &neighbor : neighbors_per_curve[i]) { - neighbor.weight /= tot_weight; - } - } - }); - return neighbors_per_curve; - } - - void interpolate_lengths(const Span<NeighborsVector> neighbors_per_curve, - MutableSpan<float> r_lengths) - { - const Span<float3> positions_cu = curves_->positions(); - - threading::parallel_for(r_lengths.index_range(), 128, [&](const IndexRange range) { - for (const int added_curve_i : range) { - const Span<NeighborInfo> neighbors = neighbors_per_curve[added_curve_i]; - float length_sum = 0.0f; - for (const NeighborInfo &neighbor : neighbors) { - const IndexRange neighbor_points = curves_->points_for_curve(neighbor.index); - float neighbor_length = 0.0f; - const int tot_segments = neighbor_points.size() - 1; - for (const int segment_i : IndexRange(tot_segments)) { - const float3 &p1 = positions_cu[neighbor_points[segment_i]]; - const float3 &p2 = positions_cu[neighbor_points[segment_i] + 1]; - neighbor_length += math::distance(p1, p2); - } - length_sum += neighbor.weight * neighbor_length; - } - const float length = neighbors.is_empty() ? new_curve_length_ : length_sum; - r_lengths[added_curve_i] = length; - } - }); - } - - float3 compute_point_normal_su(const int looptri_index, const float3 &bary_coord) - { - const MLoopTri &looptri = surface_looptris_[looptri_index]; - const int l0 = looptri.tri[0]; - const int l1 = looptri.tri[1]; - const int l2 = looptri.tri[2]; - - const float3 &l0_normal_su = corner_normals_su_[l0]; - const float3 &l1_normal_su = corner_normals_su_[l1]; - const float3 &l2_normal_su = corner_normals_su_[l2]; - - const float3 normal_su = math::normalize( - attribute_math::mix3(bary_coord, l0_normal_su, l1_normal_su, l2_normal_su)); - return normal_su; - } - - Array<float3> compute_normals_for_added_curves_su(const AddedPoints &added_points) - { - Array<float3> normals_su(added_points.bary_coords.size()); - threading::parallel_for(normals_su.index_range(), 256, [&](const IndexRange range) { - for (const int i : range) { - const int looptri_index = added_points.looptri_indices[i]; - const float3 &bary_coord = added_points.bary_coords[i]; - normals_su[i] = this->compute_point_normal_su(looptri_index, bary_coord); - } - }); - return normals_su; - } - - void initialize_surface_attachment(const AddedPoints &added_points) - { - MutableSpan<int> surface_triangle_indices = curves_->surface_triangle_indices_for_write(); - MutableSpan<float2> surface_triangle_coords = curves_->surface_triangle_coords_for_write(); - threading::parallel_for( - added_points.bary_coords.index_range(), 1024, [&](const IndexRange range) { - for (const int i : range) { - const int curve_i = tot_old_curves_ + i; - surface_triangle_indices[curve_i] = added_points.looptri_indices[i]; - surface_triangle_coords[curve_i] = float2(added_points.bary_coords[i]); - } - }); - } - - /** - * Initialize new curves so that they are just a straight line in the normal direction. - */ - void initialize_position_without_interpolation(const AddedPoints &added_points, - const Span<float> lengths_cu, - const MutableSpan<float3> normals_su) - { - MutableSpan<float3> positions_cu = curves_->positions_for_write(); - - threading::parallel_for( - added_points.bary_coords.index_range(), 256, [&](const IndexRange range) { - for (const int i : range) { - const int first_point_i = tot_old_points_ + i * points_per_curve_; - const float3 &root_cu = added_points.positions_cu[i]; - const float length = lengths_cu[i]; - const float3 &normal_su = normals_su[i]; - const float3 normal_cu = math::normalize(surface_to_curves_normal_mat_ * normal_su); - const float3 tip_cu = root_cu + length * normal_cu; - - initialize_straight_curve_positions( - root_cu, tip_cu, positions_cu.slice(first_point_i, points_per_curve_)); - } - }); - } - - /** - * Use neighboring curves to determine the shape. - */ - void initialize_position_with_interpolation(const AddedPoints &added_points, - const Span<NeighborsVector> neighbors_per_curve, - const Span<float3> new_normals_su, - const Span<float> new_lengths_cu) - { - MutableSpan<float3> positions_cu = curves_->positions_for_write(); - const VArray_Span<int> surface_triangle_indices{curves_->surface_triangle_indices()}; - const Span<float2> surface_triangle_coords = curves_->surface_triangle_coords(); - - threading::parallel_for( - added_points.bary_coords.index_range(), 256, [&](const IndexRange range) { - for (const int i : range) { - const Span<NeighborInfo> neighbors = neighbors_per_curve[i]; - - const float length_cu = new_lengths_cu[i]; - const float3 &normal_su = new_normals_su[i]; - const float3 normal_cu = math::normalize(surface_to_curves_normal_mat_ * normal_su); - - const float3 &root_cu = added_points.positions_cu[i]; - const int first_point_i = tot_old_points_ + i * points_per_curve_; - - if (neighbors.is_empty()) { - /* If there are no neighbors, just make a straight line. */ - const float3 tip_cu = root_cu + length_cu * normal_cu; - initialize_straight_curve_positions( - root_cu, tip_cu, positions_cu.slice(first_point_i, points_per_curve_)); - continue; - } - - positions_cu.slice(first_point_i, points_per_curve_).fill(root_cu); - - for (const NeighborInfo &neighbor : neighbors) { - const int neighbor_curve_i = neighbor.index; - const int neighbor_looptri_index = surface_triangle_indices[neighbor_curve_i]; - - float3 neighbor_bary_coord{surface_triangle_coords[neighbor_curve_i]}; - neighbor_bary_coord.z = 1.0f - neighbor_bary_coord.x - neighbor_bary_coord.y; - - const float3 neighbor_normal_su = this->compute_point_normal_su( - neighbor_looptri_index, neighbor_bary_coord); - const float3 neighbor_normal_cu = math::normalize(surface_to_curves_normal_mat_ * - neighbor_normal_su); - - /* The rotation matrix used to transform relative coordinates of the neighbor curve - * to the new curve. */ - float normal_rotation_cu[3][3]; - rotation_between_vecs_to_mat3(normal_rotation_cu, neighbor_normal_cu, normal_cu); - - const IndexRange neighbor_points = curves_->points_for_curve(neighbor_curve_i); - const float3 &neighbor_root_cu = positions_cu[neighbor_points[0]]; - - /* Use a temporary #PolySpline, because that's the easiest way to resample an - * existing curve right now. Resampling is necessary if the length of the new curve - * does not match the length of the neighbors or the number of handle points is - * different. */ - PolySpline neighbor_spline; - neighbor_spline.resize(neighbor_points.size()); - neighbor_spline.positions().copy_from(positions_cu.slice(neighbor_points)); - neighbor_spline.mark_cache_invalid(); - - const float neighbor_length_cu = neighbor_spline.length(); - const float length_factor = std::min(1.0f, length_cu / neighbor_length_cu); - - const float resample_factor = (1.0f / (points_per_curve_ - 1.0f)) * length_factor; - for (const int j : IndexRange(points_per_curve_)) { - const Spline::LookupResult lookup = neighbor_spline.lookup_evaluated_factor( - j * resample_factor); - const float index_factor = lookup.evaluated_index + lookup.factor; - float3 p; - neighbor_spline.sample_with_index_factors<float3>( - neighbor_spline.positions(), {&index_factor, 1}, {&p, 1}); - const float3 relative_coord = p - neighbor_root_cu; - float3 rotated_relative_coord = relative_coord; - mul_m3_v3(normal_rotation_cu, rotated_relative_coord); - positions_cu[first_point_i + j] += neighbor.weight * rotated_relative_coord; - } - } - } - }); - } }; -void AddOperation::on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) +void AddOperation::on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) { - AddOperationExecutor executor; + AddOperationExecutor executor{C}; executor.execute(*this, C, stroke_extension); } -std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation(bContext &C, ReportList *reports) +std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation(const bContext &C, + ReportList *reports) { - Object &ob_active = *CTX_data_active_object(&C); + const Object &ob_active = *CTX_data_active_object(&C); BLI_assert(ob_active.type == OB_CURVES); - Curves &curves_id = *static_cast<Curves *>(ob_active.data); + const Curves &curves_id = *static_cast<Curves *>(ob_active.data); if (curves_id.surface == nullptr || curves_id.surface->type != OB_MESH) { BKE_report(reports, RPT_WARNING, "Can not use Add brush when there is no surface mesh"); return {}; diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc b/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc index 89470772e1c..8c6ef34ef26 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc @@ -4,15 +4,22 @@ #include "curves_sculpt_intern.hh" +#include "BKE_attribute_math.hh" #include "BKE_bvhutils.h" #include "BKE_context.h" #include "BKE_curves.hh" +#include "DNA_meshdata_types.h" + #include "ED_view3d.h" #include "UI_interface.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + #include "BLI_enumerable_thread_specific.hh" +#include "BLI_length_parameterize.hh" #include "BLI_task.hh" /** @@ -41,9 +48,9 @@ static std::optional<float3> find_curves_brush_position(const CurvesGeometry &cu const float3 &ray_start_cu, const float3 &ray_end_cu, const float brush_radius_re, - ARegion ®ion, - RegionView3D &rv3d, - Object &object) + const ARegion ®ion, + const RegionView3D &rv3d, + const Object &object) { /* This value might have to be adjusted based on user feedback. */ const float brush_inner_radius_re = std::min<float>(brush_radius_re, (float)UI_UNIT_X / 3.0f); @@ -94,11 +101,30 @@ static std::optional<float3> find_curves_brush_position(const CurvesGeometry &cu for (const int curve_i : curves_range) { const IndexRange points = curves.points_for_curve(curve_i); - const int tot_segments = points.size() - 1; - for (const int segment_i : IndexRange(tot_segments)) { - const float3 &p1_cu = positions[points[segment_i]]; - const float3 &p2_cu = positions[points[segment_i] + 1]; + if (points.size() == 1) { + const float3 &pos_cu = positions[points.first()]; + + const float depth_sq_cu = math::distance_squared(ray_start_cu, pos_cu); + if (depth_sq_cu > max_depth_sq_cu) { + continue; + } + + float2 pos_re; + ED_view3d_project_float_v2_m4(®ion, pos_cu, pos_re, projection.values); + + BrushPositionCandidate candidate; + candidate.position_cu = pos_cu; + candidate.depth_sq_cu = depth_sq_cu; + candidate.distance_sq_re = math::distance_squared(brush_pos_re, pos_re); + + update_if_better(best_candidate, candidate); + continue; + } + + for (const int segment_i : points.drop_back(1)) { + const float3 &p1_cu = positions[segment_i]; + const float3 &p2_cu = positions[segment_i + 1]; float2 p1_re, p2_re; ED_view3d_project_float_v2_m4(®ion, p1_cu, p1_re, projection.values); @@ -141,23 +167,21 @@ static std::optional<float3> find_curves_brush_position(const CurvesGeometry &cu return best_candidate.position_cu; } -std::optional<CurvesBrush3D> sample_curves_3d_brush(bContext &C, - Object &curves_object, +std::optional<CurvesBrush3D> sample_curves_3d_brush(const Depsgraph &depsgraph, + const ARegion ®ion, + const View3D &v3d, + const RegionView3D &rv3d, + const Object &curves_object, const float2 &brush_pos_re, const float brush_radius_re) { - Depsgraph *depsgraph = CTX_data_depsgraph_pointer(&C); - ARegion *region = CTX_wm_region(&C); - View3D *v3d = CTX_wm_view3d(&C); - RegionView3D *rv3d = CTX_wm_region_view3d(&C); - - Curves &curves_id = *static_cast<Curves *>(curves_object.data); - CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); - Object *surface_object = curves_id.surface; + const Curves &curves_id = *static_cast<Curves *>(curves_object.data); + const CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); + const Object *surface_object = curves_id.surface; float3 center_ray_start_wo, center_ray_end_wo; ED_view3d_win_to_segment_clipped( - depsgraph, region, v3d, brush_pos_re, center_ray_start_wo, center_ray_end_wo, true); + &depsgraph, ®ion, &v3d, brush_pos_re, center_ray_start_wo, center_ray_end_wo, true); /* Shorten ray when the surface object is hit. */ if (surface_object != nullptr) { @@ -205,8 +229,8 @@ std::optional<CurvesBrush3D> sample_curves_3d_brush(bContext &C, center_ray_start_cu, center_ray_end_cu, brush_radius_re, - *region, - *rv3d, + region, + rv3d, curves_object); if (!brush_position_optional_cu.has_value()) { /* Nothing found. */ @@ -216,9 +240,9 @@ std::optional<CurvesBrush3D> sample_curves_3d_brush(bContext &C, /* Determine the 3D brush radius. */ float3 radius_ray_start_wo, radius_ray_end_wo; - ED_view3d_win_to_segment_clipped(depsgraph, - region, - v3d, + ED_view3d_win_to_segment_clipped(&depsgraph, + ®ion, + &v3d, brush_pos_re + float2(brush_radius_re, 0.0f), radius_ray_start_wo, radius_ray_end_wo, @@ -232,6 +256,55 @@ std::optional<CurvesBrush3D> sample_curves_3d_brush(bContext &C, return brush_3d; } +std::optional<CurvesBrush3D> sample_curves_surface_3d_brush( + const Depsgraph &depsgraph, + const ARegion ®ion, + const View3D &v3d, + const CurvesSculptTransforms &transforms, + const BVHTreeFromMesh &surface_bvh, + const float2 &brush_pos_re, + const float brush_radius_re) +{ + float3 brush_ray_start_wo, brush_ray_end_wo; + ED_view3d_win_to_segment_clipped( + &depsgraph, ®ion, &v3d, brush_pos_re, brush_ray_start_wo, brush_ray_end_wo, true); + const float3 brush_ray_start_su = transforms.world_to_surface * brush_ray_start_wo; + const float3 brush_ray_end_su = transforms.world_to_surface * brush_ray_end_wo; + + const float3 brush_ray_direction_su = math::normalize(brush_ray_end_su - brush_ray_start_su); + + BVHTreeRayHit ray_hit; + ray_hit.dist = FLT_MAX; + ray_hit.index = -1; + BLI_bvhtree_ray_cast(surface_bvh.tree, + brush_ray_start_su, + brush_ray_direction_su, + 0.0f, + &ray_hit, + surface_bvh.raycast_callback, + const_cast<void *>(static_cast<const void *>(&surface_bvh))); + if (ray_hit.index == -1) { + return std::nullopt; + } + + float3 brush_radius_ray_start_wo, brush_radius_ray_end_wo; + ED_view3d_win_to_segment_clipped(&depsgraph, + ®ion, + &v3d, + brush_pos_re + float2(brush_radius_re, 0), + brush_radius_ray_start_wo, + brush_radius_ray_end_wo, + true); + const float3 brush_radius_ray_start_cu = transforms.world_to_curves * brush_radius_ray_start_wo; + const float3 brush_radius_ray_end_cu = transforms.world_to_curves * brush_radius_ray_end_wo; + + const float3 brush_pos_su = ray_hit.co; + const float3 brush_pos_cu = transforms.surface_to_curves * brush_pos_su; + const float brush_radius_cu = dist_to_line_v3( + brush_pos_cu, brush_radius_ray_start_cu, brush_radius_ray_end_cu); + return CurvesBrush3D{brush_pos_cu, brush_radius_cu}; +} + Vector<float4x4> get_symmetry_brush_transforms(const eCurvesSymmetryType symmetry) { Vector<float4x4> matrices; @@ -260,4 +333,68 @@ Vector<float4x4> get_symmetry_brush_transforms(const eCurvesSymmetryType symmetr return matrices; } +float transform_brush_radius(const float4x4 &transform, + const float3 &brush_position, + const float old_radius) +{ + const float3 offset_position = brush_position + float3(old_radius, 0.0f, 0.0f); + const float3 new_position = transform * brush_position; + const float3 new_offset_position = transform * offset_position; + return math::distance(new_position, new_offset_position); +} + +void move_last_point_and_resample(MutableSpan<float3> positions, const float3 &new_last_position) +{ + /* Find the accumulated length of each point in the original curve, + * treating it as a poly curve for performance reasons and simplicity. */ + Array<float> orig_lengths(length_parameterize::lengths_num(positions.size(), false)); + length_parameterize::accumulate_lengths<float3>(positions, false, orig_lengths); + const float orig_total_length = orig_lengths.last(); + + /* Find the factor by which the new curve is shorter or longer than the original. */ + const float new_last_segment_length = math::distance(positions.last(1), new_last_position); + const float new_total_length = orig_lengths.last(1) + new_last_segment_length; + const float length_factor = safe_divide(new_total_length, orig_total_length); + + /* Calculate the lengths to sample the original curve with by scaling the original lengths. */ + Array<float> new_lengths(positions.size() - 1); + new_lengths.first() = 0.0f; + for (const int i : new_lengths.index_range().drop_front(1)) { + new_lengths[i] = orig_lengths[i - 1] * length_factor; + } + + Array<int> indices(positions.size() - 1); + Array<float> factors(positions.size() - 1); + length_parameterize::create_samples_from_sorted_lengths( + orig_lengths, new_lengths, false, indices, factors); + + Array<float3> new_positions(positions.size() - 1); + length_parameterize::linear_interpolation<float3>(positions, indices, factors, new_positions); + positions.drop_back(1).copy_from(new_positions); + positions.last() = new_last_position; +} + +CurvesSculptCommonContext::CurvesSculptCommonContext(const bContext &C) +{ + this->depsgraph = CTX_data_depsgraph_pointer(&C); + this->scene = CTX_data_scene(&C); + this->region = CTX_wm_region(&C); + this->v3d = CTX_wm_view3d(&C); + this->rv3d = CTX_wm_region_view3d(&C); +} + +CurvesSculptTransforms::CurvesSculptTransforms(const Object &curves_ob, const Object *surface_ob) +{ + this->curves_to_world = curves_ob.obmat; + this->world_to_curves = this->curves_to_world.inverted(); + + if (surface_ob != nullptr) { + this->surface_to_world = surface_ob->obmat; + this->world_to_surface = this->surface_to_world.inverted(); + this->surface_to_curves = this->world_to_curves * this->surface_to_world; + this->curves_to_surface = this->world_to_surface * this->curves_to_world; + this->surface_to_curves_normal = this->surface_to_curves.inverted().transposed(); + } +} + } // namespace blender::ed::sculpt_paint diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc b/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc index 28258a26f74..541bf9d8253 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc @@ -37,6 +37,8 @@ #include "UI_interface.h" +#include "WM_api.h" + /** * The code below uses a prefix naming convention to indicate the coordinate space: * cu: Local space of the curves object that is being edited. @@ -67,7 +69,7 @@ class CombOperation : public CurvesSculptStrokeOperation { friend struct CombOperationExecutor; public: - void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override; + void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override; }; /** @@ -76,61 +78,47 @@ class CombOperation : public CurvesSculptStrokeOperation { */ struct CombOperationExecutor { CombOperation *self_ = nullptr; - bContext *C_ = nullptr; - Depsgraph *depsgraph_ = nullptr; - Scene *scene_ = nullptr; - Object *object_ = nullptr; - ARegion *region_ = nullptr; - View3D *v3d_ = nullptr; - RegionView3D *rv3d_ = nullptr; + CurvesSculptCommonContext ctx_; - CurvesSculpt *curves_sculpt_ = nullptr; - Brush *brush_ = nullptr; - float brush_radius_re_; + const CurvesSculpt *curves_sculpt_ = nullptr; + const Brush *brush_ = nullptr; + float brush_radius_base_re_; + float brush_radius_factor_; float brush_strength_; eBrushFalloffShape falloff_shape_; + Object *object_ = nullptr; Curves *curves_id_ = nullptr; CurvesGeometry *curves_ = nullptr; - const Object *surface_ob_ = nullptr; - const Mesh *surface_ = nullptr; - Span<MLoopTri> surface_looptris_; + VArray<float> point_factors_; + Vector<int64_t> selected_curve_indices_; + IndexMask curve_selection_; float2 brush_pos_prev_re_; float2 brush_pos_re_; float2 brush_pos_diff_re_; - float brush_pos_diff_length_re_; - float4x4 curves_to_world_mat_; - float4x4 world_to_curves_mat_; - float4x4 surface_to_world_mat_; - float4x4 world_to_surface_mat_; + CurvesSculptTransforms transforms_; - BVHTreeFromMesh surface_bvh_; + CombOperationExecutor(const bContext &C) : ctx_(C) + { + } - void execute(CombOperation &self, bContext *C, const StrokeExtension &stroke_extension) + void execute(CombOperation &self, const bContext &C, const StrokeExtension &stroke_extension) { self_ = &self; BLI_SCOPED_DEFER([&]() { self_->brush_pos_last_re_ = stroke_extension.mouse_position; }); - C_ = C; - depsgraph_ = CTX_data_depsgraph_pointer(C); - scene_ = CTX_data_scene(C); - object_ = CTX_data_active_object(C); - region_ = CTX_wm_region(C); - v3d_ = CTX_wm_view3d(C); - rv3d_ = CTX_wm_region_view3d(C); - - curves_sculpt_ = scene_->toolsettings->curves_sculpt; - brush_ = BKE_paint_brush(&curves_sculpt_->paint); - brush_radius_re_ = BKE_brush_size_get(scene_, brush_); - brush_strength_ = BKE_brush_alpha_get(scene_, brush_); + object_ = CTX_data_active_object(&C); - curves_to_world_mat_ = object_->obmat; - world_to_curves_mat_ = curves_to_world_mat_.inverted(); + curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt; + brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint); + brush_radius_base_re_ = BKE_brush_size_get(ctx_.scene, brush_); + brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension); + brush_strength_ = brush_strength_get(*ctx_.scene, *brush_, stroke_extension); falloff_shape_ = static_cast<eBrushFalloffShape>(brush_->falloff_shape); @@ -140,26 +128,14 @@ struct CombOperationExecutor { return; } + transforms_ = CurvesSculptTransforms(*object_, curves_id_->surface); + + point_factors_ = get_point_selection(*curves_id_); + curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_); + brush_pos_prev_re_ = self_->brush_pos_last_re_; brush_pos_re_ = stroke_extension.mouse_position; brush_pos_diff_re_ = brush_pos_re_ - brush_pos_prev_re_; - brush_pos_diff_length_re_ = math::length(brush_pos_diff_re_); - - surface_ob_ = curves_id_->surface; - if (surface_ob_ != nullptr) { - surface_ = static_cast<const Mesh *>(surface_ob_->data); - surface_looptris_ = {BKE_mesh_runtime_looptri_ensure(surface_), - BKE_mesh_runtime_looptri_len(surface_)}; - surface_to_world_mat_ = surface_ob_->obmat; - world_to_surface_mat_ = surface_to_world_mat_.inverted(); - BKE_bvhtree_from_mesh_get(&surface_bvh_, surface_, BVHTREE_FROM_LOOPTRI, 2); - } - - BLI_SCOPED_DEFER([&]() { - if (surface_ob_ != nullptr) { - free_bvhtree_from_mesh(&surface_bvh_); - } - }); if (stroke_extension.is_first) { if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) { @@ -186,7 +162,8 @@ struct CombOperationExecutor { curves_->tag_positions_changed(); DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); - ED_region_tag_redraw(region_); + WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); + ED_region_tag_redraw(ctx_.region); } /** @@ -209,13 +186,14 @@ struct CombOperationExecutor { MutableSpan<float3> positions_cu = curves_->positions_for_write(); float4x4 projection; - ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values); + ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); - const float brush_radius_sq_re = pow2f(brush_radius_re_); + const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; + const float brush_radius_sq_re = pow2f(brush_radius_re); - threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) { + threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) { Vector<int> &local_changed_curves = r_changed_curves.local(); - for (const int curve_i : curves_range) { + for (const int curve_i : curve_selection_.slice(range)) { bool curve_changed = false; const IndexRange points = curves_->points_for_curve(curve_i); for (const int point_i : points.drop_front(1)) { @@ -223,7 +201,7 @@ struct CombOperationExecutor { /* Find the position of the point in screen space. */ float2 old_pos_re; - ED_view3d_project_float_v2_m4(region_, old_pos_cu, old_pos_re, projection.values); + ED_view3d_project_float_v2_m4(ctx_.region, old_pos_cu, old_pos_re, projection.values); const float distance_to_brush_sq_re = dist_squared_to_line_segment_v2( old_pos_re, brush_pos_prev_re_, brush_pos_re_); @@ -235,17 +213,21 @@ struct CombOperationExecutor { const float distance_to_brush_re = std::sqrt(distance_to_brush_sq_re); /* A falloff that is based on how far away the point is from the stroke. */ const float radius_falloff = BKE_brush_curve_strength( - brush_, distance_to_brush_re, brush_radius_re_); + brush_, distance_to_brush_re, brush_radius_re); /* Combine the falloff and brush strength. */ - const float weight = brush_strength_ * radius_falloff; + const float weight = brush_strength_ * radius_falloff * point_factors_[point_i]; - /* Offset the old point position in screen space and transform it back into 3D space. */ + /* Offset the old point position in screen space and transform it back into 3D space. + */ const float2 new_position_re = old_pos_re + brush_pos_diff_re_ * weight; float3 new_position_wo; - ED_view3d_win_to_3d( - v3d_, region_, curves_to_world_mat_ * old_pos_cu, new_position_re, new_position_wo); + ED_view3d_win_to_3d(ctx_.v3d, + ctx_.region, + transforms_.curves_to_world * old_pos_cu, + new_position_re, + new_position_wo); const float3 new_position_cu = brush_transform * - (world_to_curves_mat_ * new_position_wo); + (transforms_.world_to_curves * new_position_wo); positions_cu[point_i] = new_position_cu; curve_changed = true; @@ -263,23 +245,23 @@ struct CombOperationExecutor { void comb_spherical_with_symmetry(EnumerableThreadSpecific<Vector<int>> &r_changed_curves) { float4x4 projection; - ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values); + ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); float3 brush_start_wo, brush_end_wo; - ED_view3d_win_to_3d(v3d_, - region_, - curves_to_world_mat_ * self_->brush_3d_.position_cu, + ED_view3d_win_to_3d(ctx_.v3d, + ctx_.region, + transforms_.curves_to_world * self_->brush_3d_.position_cu, brush_pos_prev_re_, brush_start_wo); - ED_view3d_win_to_3d(v3d_, - region_, - curves_to_world_mat_ * self_->brush_3d_.position_cu, + ED_view3d_win_to_3d(ctx_.v3d, + ctx_.region, + transforms_.curves_to_world * self_->brush_3d_.position_cu, brush_pos_re_, brush_end_wo); - const float3 brush_start_cu = world_to_curves_mat_ * brush_start_wo; - const float3 brush_end_cu = world_to_curves_mat_ * brush_end_wo; + const float3 brush_start_cu = transforms_.world_to_curves * brush_start_wo; + const float3 brush_end_cu = transforms_.world_to_curves * brush_end_wo; - const float brush_radius_cu = self_->brush_3d_.radius_cu; + const float brush_radius_cu = self_->brush_3d_.radius_cu * brush_radius_factor_; const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( eCurvesSymmetryType(curves_id_->symmetry)); @@ -300,9 +282,9 @@ struct CombOperationExecutor { const float brush_radius_sq_cu = pow2f(brush_radius_cu); const float3 brush_diff_cu = brush_end_cu - brush_start_cu; - threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) { + threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) { Vector<int> &local_changed_curves = r_changed_curves.local(); - for (const int curve_i : curves_range) { + for (const int curve_i : curve_selection_.slice(range)) { bool curve_changed = false; const IndexRange points = curves_->points_for_curve(curve_i); for (const int point_i : points.drop_front(1)) { @@ -322,7 +304,7 @@ struct CombOperationExecutor { const float radius_falloff = BKE_brush_curve_strength( brush_, distance_to_brush_cu, brush_radius_cu); /* Combine the falloff and brush strength. */ - const float weight = brush_strength_ * radius_falloff; + const float weight = brush_strength_ * radius_falloff * point_factors_[point_i]; /* Update the point position. */ positions_cu[point_i] = pos_old_cu + weight * brush_diff_cu; @@ -340,8 +322,13 @@ struct CombOperationExecutor { */ void initialize_spherical_brush_reference_point() { - std::optional<CurvesBrush3D> brush_3d = sample_curves_3d_brush( - *C_, *object_, brush_pos_re_, brush_radius_re_); + std::optional<CurvesBrush3D> brush_3d = sample_curves_3d_brush(*ctx_.depsgraph, + *ctx_.region, + *ctx_.v3d, + *ctx_.rv3d, + *object_, + brush_pos_re_, + brush_radius_base_re_); if (brush_3d.has_value()) { self_->brush_3d_ = *brush_3d; } @@ -380,11 +367,11 @@ struct CombOperationExecutor { threading::parallel_for(changed_curves.index_range(), 256, [&](const IndexRange range) { for (const int curve_i : changed_curves.as_span().slice(range)) { const IndexRange points = curves_->points_for_curve(curve_i); - for (const int segment_i : IndexRange(points.size() - 1)) { - const float3 &p1_cu = positions_cu[points[segment_i]]; - float3 &p2_cu = positions_cu[points[segment_i] + 1]; + for (const int segment_i : points.drop_back(1)) { + const float3 &p1_cu = positions_cu[segment_i]; + float3 &p2_cu = positions_cu[segment_i + 1]; const float3 direction = math::normalize(p2_cu - p1_cu); - const float expected_length_cu = expected_lengths_cu[points[segment_i]]; + const float expected_length_cu = expected_lengths_cu[segment_i]; p2_cu = p1_cu + direction * expected_length_cu; } } @@ -393,9 +380,9 @@ struct CombOperationExecutor { } }; -void CombOperation::on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) +void CombOperation::on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) { - CombOperationExecutor executor; + CombOperationExecutor executor{C}; executor.execute(*this, C, stroke_extension); } diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc b/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc index 3f3c48ecbb6..eab7dabcd22 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc @@ -35,6 +35,8 @@ #include "ED_screen.h" #include "ED_view3d.h" +#include "WM_api.h" + /** * The code below uses a suffix naming convention to indicate the coordinate space: * cu: Local space of the curves object that is being edited. @@ -48,65 +50,57 @@ using blender::bke::CurvesGeometry; class DeleteOperation : public CurvesSculptStrokeOperation { private: - float2 brush_pos_prev_re_; - CurvesBrush3D brush_3d_; friend struct DeleteOperationExecutor; public: - void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override; + void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override; }; struct DeleteOperationExecutor { DeleteOperation *self_ = nullptr; - bContext *C_ = nullptr; - Depsgraph *depsgraph_ = nullptr; - Scene *scene_ = nullptr; - Object *object_ = nullptr; - ARegion *region_ = nullptr; - View3D *v3d_ = nullptr; - RegionView3D *rv3d_ = nullptr; + CurvesSculptCommonContext ctx_; + Object *object_ = nullptr; Curves *curves_id_ = nullptr; CurvesGeometry *curves_ = nullptr; - CurvesSculpt *curves_sculpt_ = nullptr; - Brush *brush_ = nullptr; - float brush_radius_re_; + Vector<int64_t> selected_curve_indices_; + IndexMask curve_selection_; + + const CurvesSculpt *curves_sculpt_ = nullptr; + const Brush *brush_ = nullptr; + float brush_radius_base_re_; + float brush_radius_factor_; float2 brush_pos_re_; - float2 brush_pos_prev_re_; - float4x4 curves_to_world_mat_; - float4x4 world_to_curves_mat_; + CurvesSculptTransforms transforms_; - void execute(DeleteOperation &self, bContext *C, const StrokeExtension &stroke_extension) + DeleteOperationExecutor(const bContext &C) : ctx_(C) { - BLI_SCOPED_DEFER([&]() { self.brush_pos_prev_re_ = stroke_extension.mouse_position; }); + } + void execute(DeleteOperation &self, const bContext &C, const StrokeExtension &stroke_extension) + { self_ = &self; - C_ = C; - depsgraph_ = CTX_data_depsgraph_pointer(C); - scene_ = CTX_data_scene(C); - object_ = CTX_data_active_object(C); - region_ = CTX_wm_region(C); - v3d_ = CTX_wm_view3d(C); - rv3d_ = CTX_wm_region_view3d(C); + object_ = CTX_data_active_object(&C); curves_id_ = static_cast<Curves *>(object_->data); curves_ = &CurvesGeometry::wrap(curves_id_->geometry); - curves_sculpt_ = scene_->toolsettings->curves_sculpt; - brush_ = BKE_paint_brush(&curves_sculpt_->paint); - brush_radius_re_ = BKE_brush_size_get(scene_, brush_); + selected_curve_indices_.clear(); + curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_); + + curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt; + brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint); + brush_radius_base_re_ = BKE_brush_size_get(ctx_.scene, brush_); + brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension); brush_pos_re_ = stroke_extension.mouse_position; - brush_pos_prev_re_ = stroke_extension.is_first ? stroke_extension.mouse_position : - self.brush_pos_prev_re_; - curves_to_world_mat_ = object_->obmat; - world_to_curves_mat_ = curves_to_world_mat_.inverted(); + transforms_ = CurvesSculptTransforms(*object_, curves_id_->surface); const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>( brush_->falloff_shape); @@ -137,7 +131,8 @@ struct DeleteOperationExecutor { curves_->remove_curves(mask); DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); - ED_region_tag_redraw(region_); + WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); + ED_region_tag_redraw(ctx_.region); } void delete_projected_with_symmetry(MutableSpan<bool> curves_to_delete) @@ -145,32 +140,47 @@ struct DeleteOperationExecutor { const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( eCurvesSymmetryType(curves_id_->symmetry)); for (const float4x4 &brush_transform : symmetry_brush_transforms) { - this->delete_projected(curves_to_delete, brush_transform); + this->delete_projected(brush_transform, curves_to_delete); } } - void delete_projected(MutableSpan<bool> curves_to_delete, const float4x4 &brush_transform) + void delete_projected(const float4x4 &brush_transform, MutableSpan<bool> curves_to_delete) { const float4x4 brush_transform_inv = brush_transform.inverted(); float4x4 projection; - ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values); + ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); Span<float3> positions_cu = curves_->positions(); - threading::parallel_for(curves_->curves_range(), 512, [&](IndexRange curve_range) { - for (const int curve_i : curve_range) { - const IndexRange point_range = curves_->points_for_curve(curve_i); - for (const int segment_i : IndexRange(point_range.size() - 1)) { - const float3 pos1_cu = brush_transform_inv * positions_cu[point_range[segment_i]]; - const float3 pos2_cu = brush_transform_inv * positions_cu[point_range[segment_i + 1]]; + const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; + const float brush_radius_sq_re = pow2f(brush_radius_re); + + threading::parallel_for(curve_selection_.index_range(), 512, [&](const IndexRange range) { + for (const int curve_i : curve_selection_.slice(range)) { + const IndexRange points = curves_->points_for_curve(curve_i); + if (points.size() == 1) { + const float3 pos_cu = brush_transform_inv * positions_cu[points.first()]; + float2 pos_re; + ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, pos_re, projection.values); + + if (math::distance_squared(brush_pos_re_, pos_re) <= brush_radius_sq_re) { + curves_to_delete[curve_i] = true; + } + continue; + } + + for (const int segment_i : points.drop_back(1)) { + const float3 pos1_cu = brush_transform_inv * positions_cu[segment_i]; + const float3 pos2_cu = brush_transform_inv * positions_cu[segment_i + 1]; float2 pos1_re, pos2_re; - ED_view3d_project_float_v2_m4(region_, pos1_cu, pos1_re, projection.values); - ED_view3d_project_float_v2_m4(region_, pos2_cu, pos2_re, projection.values); + ED_view3d_project_float_v2_m4(ctx_.region, pos1_cu, pos1_re, projection.values); + ED_view3d_project_float_v2_m4(ctx_.region, pos2_cu, pos2_re, projection.values); - const float dist = dist_seg_seg_v2(pos1_re, pos2_re, brush_pos_prev_re_, brush_pos_re_); - if (dist <= brush_radius_re_) { + const float dist_sq_re = dist_squared_to_line_segment_v2( + brush_pos_re_, pos1_re, pos2_re); + if (dist_sq_re <= brush_radius_sq_re) { curves_to_delete[curve_i] = true; break; } @@ -182,57 +192,50 @@ struct DeleteOperationExecutor { void delete_spherical_with_symmetry(MutableSpan<bool> curves_to_delete) { float4x4 projection; - ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values); - - float3 brush_start_wo, brush_end_wo; - ED_view3d_win_to_3d(v3d_, - region_, - curves_to_world_mat_ * self_->brush_3d_.position_cu, - brush_pos_prev_re_, - brush_start_wo); - ED_view3d_win_to_3d(v3d_, - region_, - curves_to_world_mat_ * self_->brush_3d_.position_cu, + ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); + + float3 brush_wo; + ED_view3d_win_to_3d(ctx_.v3d, + ctx_.region, + transforms_.curves_to_world * self_->brush_3d_.position_cu, brush_pos_re_, - brush_end_wo); - const float3 brush_start_cu = world_to_curves_mat_ * brush_start_wo; - const float3 brush_end_cu = world_to_curves_mat_ * brush_end_wo; + brush_wo); + const float3 brush_cu = transforms_.world_to_curves * brush_wo; const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( eCurvesSymmetryType(curves_id_->symmetry)); for (const float4x4 &brush_transform : symmetry_brush_transforms) { - this->delete_spherical( - curves_to_delete, brush_transform * brush_start_cu, brush_transform * brush_end_cu); + this->delete_spherical(brush_transform * brush_cu, curves_to_delete); } } - void delete_spherical(MutableSpan<bool> curves_to_delete, - const float3 &brush_start_cu, - const float3 &brush_end_cu) + void delete_spherical(const float3 &brush_cu, MutableSpan<bool> curves_to_delete) { Span<float3> positions_cu = curves_->positions(); - const float brush_radius_cu = self_->brush_3d_.radius_cu; + const float brush_radius_cu = self_->brush_3d_.radius_cu * brush_radius_factor_; const float brush_radius_sq_cu = pow2f(brush_radius_cu); - threading::parallel_for(curves_->curves_range(), 512, [&](IndexRange curve_range) { - for (const int curve_i : curve_range) { + threading::parallel_for(curve_selection_.index_range(), 512, [&](const IndexRange range) { + for (const int curve_i : curve_selection_.slice(range)) { const IndexRange points = curves_->points_for_curve(curve_i); - for (const int segment_i : IndexRange(points.size() - 1)) { - const float3 pos1_cu = positions_cu[points[segment_i]]; - const float3 pos2_cu = positions_cu[points[segment_i] + 1]; - - float3 closest_segment_cu, closest_brush_cu; - isect_seg_seg_v3(pos1_cu, - pos2_cu, - brush_start_cu, - brush_end_cu, - closest_segment_cu, - closest_brush_cu); - const float distance_to_brush_sq_cu = math::distance_squared(closest_segment_cu, - closest_brush_cu); - if (distance_to_brush_sq_cu > brush_radius_sq_cu) { + + if (points.size() == 1) { + const float3 &pos_cu = positions_cu[points.first()]; + const float distance_sq_cu = math::distance_squared(pos_cu, brush_cu); + if (distance_sq_cu < brush_radius_sq_cu) { + curves_to_delete[curve_i] = true; + } + continue; + } + + for (const int segment_i : points.drop_back(1)) { + const float3 &pos1_cu = positions_cu[segment_i]; + const float3 &pos2_cu = positions_cu[segment_i + 1]; + + const float distance_sq_cu = dist_squared_to_line_segment_v3(brush_cu, pos1_cu, pos2_cu); + if (distance_sq_cu > brush_radius_sq_cu) { continue; } curves_to_delete[curve_i] = true; @@ -244,17 +247,23 @@ struct DeleteOperationExecutor { void initialize_spherical_brush_reference_point() { - std::optional<CurvesBrush3D> brush_3d = sample_curves_3d_brush( - *C_, *object_, brush_pos_re_, brush_radius_re_); + std::optional<CurvesBrush3D> brush_3d = sample_curves_3d_brush(*ctx_.depsgraph, + *ctx_.region, + *ctx_.v3d, + *ctx_.rv3d, + *object_, + brush_pos_re_, + brush_radius_base_re_); if (brush_3d.has_value()) { self_->brush_3d_ = *brush_3d; } } }; -void DeleteOperation::on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) +void DeleteOperation::on_stroke_extended(const bContext &C, + const StrokeExtension &stroke_extension) { - DeleteOperationExecutor executor; + DeleteOperationExecutor executor{C}; executor.execute(*this, C, stroke_extension); } diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc b/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc index 4ac4559cbbc..cf893f09fc6 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc @@ -2,8 +2,6 @@ #include <algorithm> -#include "curves_sculpt_intern.hh" - #include "BLI_enumerable_thread_specific.hh" #include "BLI_float4x4.hh" #include "BLI_kdtree.h" @@ -36,6 +34,8 @@ #include "ED_screen.h" #include "ED_view3d.h" +#include "WM_api.h" + #include "curves_sculpt_intern.hh" /** @@ -68,10 +68,10 @@ class CurvesEffect { */ class ShrinkCurvesEffect : public CurvesEffect { private: - Brush &brush_; + const Brush &brush_; public: - ShrinkCurvesEffect(Brush &brush) : brush_(brush) + ShrinkCurvesEffect(const Brush &brush) : brush_(brush) { } @@ -151,50 +151,10 @@ class ExtrapolateCurvesEffect : public CurvesEffect { const float3 direction = math::normalize(old_last_pos_cu - direction_reference_point); const float3 new_last_pos_cu = old_last_pos_cu + direction * move_distance_cu; - this->move_last_point_and_resample(positions_cu, curve_points, new_last_pos_cu); + move_last_point_and_resample(positions_cu.slice(curve_points), new_last_pos_cu); } }); } - - void move_last_point_and_resample(MutableSpan<float3> positions, - const IndexRange curve_points, - const float3 &new_last_point_position) const - { - Vector<float> old_lengths; - old_lengths.append(0.0f); - /* Used to (1) normalize the segment sizes over time and (2) support making zero-length - * segments */ - const float extra_length = 0.001f; - for (const int segment_i : IndexRange(curve_points.size() - 1)) { - const float3 &p1 = positions[curve_points[segment_i]]; - const float3 &p2 = positions[curve_points[segment_i] + 1]; - const float length = math::distance(p1, p2); - old_lengths.append(old_lengths.last() + length + extra_length); - } - Vector<float> point_factors; - for (float &old_length : old_lengths) { - point_factors.append(old_length / old_lengths.last()); - } - - PolySpline new_spline; - new_spline.resize(curve_points.size()); - MutableSpan<float3> new_spline_positions = new_spline.positions(); - for (const int i : IndexRange(curve_points.size() - 1)) { - new_spline_positions[i] = positions[curve_points[i]]; - } - new_spline_positions.last() = new_last_point_position; - new_spline.mark_cache_invalid(); - - for (const int i : IndexRange(curve_points.size())) { - const float factor = point_factors[i]; - const Spline::LookupResult lookup = new_spline.lookup_evaluated_factor(factor); - const float index_factor = lookup.evaluated_index + lookup.factor; - float3 p; - new_spline.sample_with_index_factors<float3>( - new_spline_positions, {&index_factor, 1}, {&p, 1}); - positions[curve_points[i]] = p; - } - } }; /** @@ -203,10 +163,10 @@ class ExtrapolateCurvesEffect : public CurvesEffect { class ScaleCurvesEffect : public CurvesEffect { private: bool scale_up_; - Brush &brush_; + const Brush &brush_; public: - ScaleCurvesEffect(bool scale_up, Brush &brush) : scale_up_(scale_up), brush_(brush) + ScaleCurvesEffect(bool scale_up, const Brush &brush) : scale_up_(scale_up), brush_(brush) { } @@ -261,7 +221,7 @@ class CurvesEffectOperation : public CurvesSculptStrokeOperation { { } - void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override; + void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override; }; /** @@ -270,24 +230,24 @@ class CurvesEffectOperation : public CurvesSculptStrokeOperation { */ struct CurvesEffectOperationExecutor { CurvesEffectOperation *self_ = nullptr; - Depsgraph *depsgraph_ = nullptr; - Scene *scene_ = nullptr; - Object *object_ = nullptr; - ARegion *region_ = nullptr; - View3D *v3d_ = nullptr; - RegionView3D *rv3d_ = nullptr; + CurvesSculptCommonContext ctx_; + Object *object_ = nullptr; Curves *curves_id_ = nullptr; CurvesGeometry *curves_ = nullptr; - Brush *brush_ = nullptr; - float brush_radius_re_; - float brush_radius_sq_re_; + VArray<float> curve_selection_factors_; + Vector<int64_t> selected_curve_indices_; + IndexMask curve_selection_; + + const Brush *brush_ = nullptr; + float brush_radius_base_re_; + float brush_radius_factor_; float brush_strength_; + eBrushFalloffShape falloff_shape_; - float4x4 curves_to_world_mat_; - float4x4 world_to_curves_mat_; + CurvesSculptTransforms transforms_; float2 brush_pos_start_re_; float2 brush_pos_end_re_; @@ -297,17 +257,18 @@ struct CurvesEffectOperationExecutor { Vector<float> move_distances_cu; }; - void execute(CurvesEffectOperation &self, bContext *C, const StrokeExtension &stroke_extension) + CurvesEffectOperationExecutor(const bContext &C) : ctx_(C) + { + } + + void execute(CurvesEffectOperation &self, + const bContext &C, + const StrokeExtension &stroke_extension) { BLI_SCOPED_DEFER([&]() { self.last_mouse_position_ = stroke_extension.mouse_position; }); self_ = &self; - depsgraph_ = CTX_data_depsgraph_pointer(C); - scene_ = CTX_data_scene(C); - object_ = CTX_data_active_object(C); - region_ = CTX_wm_region(C); - v3d_ = CTX_wm_view3d(C); - rv3d_ = CTX_wm_region_view3d(C); + object_ = CTX_data_active_object(&C); curves_id_ = static_cast<Curves *>(object_->data); curves_ = &CurvesGeometry::wrap(curves_id_->geometry); @@ -315,15 +276,20 @@ struct CurvesEffectOperationExecutor { return; } - CurvesSculpt &curves_sculpt = *scene_->toolsettings->curves_sculpt; - brush_ = BKE_paint_brush(&curves_sculpt.paint); - brush_radius_re_ = BKE_brush_size_get(scene_, brush_); - brush_strength_ = BKE_brush_alpha_get(scene_, brush_); - brush_radius_sq_re_ = pow2f(brush_radius_re_); + curve_selection_factors_ = get_curves_selection(*curves_id_); + curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_); + + const CurvesSculpt &curves_sculpt = *ctx_.scene->toolsettings->curves_sculpt; + brush_ = BKE_paint_brush_for_read(&curves_sculpt.paint); + brush_strength_ = brush_strength_get(*ctx_.scene, *brush_, stroke_extension); + + brush_radius_base_re_ = BKE_brush_size_get(ctx_.scene, brush_); + brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension); + brush_strength_ = brush_strength_get(*ctx_.scene, *brush_, stroke_extension); + falloff_shape_ = eBrushFalloffShape(brush_->falloff_shape); - curves_to_world_mat_ = object_->obmat; - world_to_curves_mat_ = curves_to_world_mat_.inverted(); + transforms_ = CurvesSculptTransforms(*object_, curves_id_->surface); brush_pos_start_re_ = self.last_mouse_position_; brush_pos_end_re_ = stroke_extension.mouse_position; @@ -331,7 +297,13 @@ struct CurvesEffectOperationExecutor { if (stroke_extension.is_first) { if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) { if (std::optional<CurvesBrush3D> brush_3d = sample_curves_3d_brush( - *C, *object_, stroke_extension.mouse_position, brush_radius_re_)) { + *ctx_.depsgraph, + *ctx_.region, + *ctx_.v3d, + *ctx_.rv3d, + *object_, + stroke_extension.mouse_position, + brush_radius_base_re_)) { self.brush_3d_ = *brush_3d; } } @@ -356,7 +328,8 @@ struct CurvesEffectOperationExecutor { curves_->tag_positions_changed(); DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); - ED_region_tag_redraw(region_); + WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); + ED_region_tag_redraw(ctx_.region); } void gather_influences_projected( @@ -365,7 +338,7 @@ struct CurvesEffectOperationExecutor { const Span<float3> positions_cu = curves_->positions(); float4x4 projection; - ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values); + ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( eCurvesSymmetryType(curves_id_->symmetry)); @@ -374,22 +347,26 @@ struct CurvesEffectOperationExecutor { symmetry_brush_transforms_inv.append(brush_transform.inverted()); } + const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; + const float brush_radius_sq_re = pow2f(brush_radius_re); + threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) { Influences &local_influences = influences_for_thread.local(); for (const int curve_i : curves_range) { const IndexRange points = curves_->points_for_curve(curve_i); - const int tot_segments = points.size() - 1; - float max_move_distance_cu = 0.0f; + const float curve_selection_factor = curve_selection_factors_[curve_i]; + + float max_move_distance_cu = 0.0f; for (const float4x4 &brush_transform_inv : symmetry_brush_transforms_inv) { - for (const int segment_i : IndexRange(tot_segments)) { - const float3 &p1_cu = brush_transform_inv * positions_cu[points[segment_i]]; - const float3 &p2_cu = brush_transform_inv * positions_cu[points[segment_i] + 1]; + for (const int segment_i : points.drop_back(1)) { + const float3 p1_cu = brush_transform_inv * positions_cu[segment_i]; + const float3 p2_cu = brush_transform_inv * positions_cu[segment_i + 1]; float2 p1_re, p2_re; - ED_view3d_project_float_v2_m4(region_, p1_cu, p1_re, projection.values); - ED_view3d_project_float_v2_m4(region_, p2_cu, p2_re, projection.values); + ED_view3d_project_float_v2_m4(ctx_.region, p1_cu, p1_re, projection.values); + ED_view3d_project_float_v2_m4(ctx_.region, p2_cu, p2_re, projection.values); float2 closest_on_brush_re; float2 closest_on_segment_re; @@ -404,31 +381,31 @@ struct CurvesEffectOperationExecutor { p1_re, p2_re); - if (dist_to_brush_sq_re > brush_radius_sq_re_) { + if (dist_to_brush_sq_re > brush_radius_sq_re) { continue; } const float dist_to_brush_re = std::sqrt(dist_to_brush_sq_re); const float radius_falloff = BKE_brush_curve_strength( - brush_, dist_to_brush_re, brush_radius_re_); - const float weight = brush_strength_ * radius_falloff; + brush_, dist_to_brush_re, brush_radius_re); + const float weight = brush_strength_ * radius_falloff * curve_selection_factor; const float3 closest_on_segment_cu = math::interpolate( p1_cu, p2_cu, lambda_on_segment); float3 brush_start_pos_wo, brush_end_pos_wo; - ED_view3d_win_to_3d(v3d_, - region_, - curves_to_world_mat_ * closest_on_segment_cu, + ED_view3d_win_to_3d(ctx_.v3d, + ctx_.region, + transforms_.curves_to_world * closest_on_segment_cu, brush_pos_start_re_, brush_start_pos_wo); - ED_view3d_win_to_3d(v3d_, - region_, - curves_to_world_mat_ * closest_on_segment_cu, + ED_view3d_win_to_3d(ctx_.v3d, + ctx_.region, + transforms_.curves_to_world * closest_on_segment_cu, brush_pos_end_re_, brush_end_pos_wo); - const float3 brush_start_pos_cu = world_to_curves_mat_ * brush_start_pos_wo; - const float3 brush_end_pos_cu = world_to_curves_mat_ * brush_end_pos_wo; + const float3 brush_start_pos_cu = transforms_.world_to_curves * brush_start_pos_wo; + const float3 brush_end_pos_cu = transforms_.world_to_curves * brush_end_pos_wo; const float move_distance_cu = weight * math::distance(brush_start_pos_cu, brush_end_pos_cu); @@ -449,21 +426,21 @@ struct CurvesEffectOperationExecutor { const Span<float3> positions_cu = curves_->positions(); float3 brush_pos_start_wo, brush_pos_end_wo; - ED_view3d_win_to_3d(v3d_, - region_, - curves_to_world_mat_ * self_->brush_3d_.position_cu, + ED_view3d_win_to_3d(ctx_.v3d, + ctx_.region, + transforms_.curves_to_world * self_->brush_3d_.position_cu, brush_pos_start_re_, brush_pos_start_wo); - ED_view3d_win_to_3d(v3d_, - region_, - curves_to_world_mat_ * self_->brush_3d_.position_cu, + ED_view3d_win_to_3d(ctx_.v3d, + ctx_.region, + transforms_.curves_to_world * self_->brush_3d_.position_cu, brush_pos_end_re_, brush_pos_end_wo); - const float3 brush_pos_start_cu = world_to_curves_mat_ * brush_pos_start_wo; - const float3 brush_pos_end_cu = world_to_curves_mat_ * brush_pos_end_wo; + const float3 brush_pos_start_cu = transforms_.world_to_curves * brush_pos_start_wo; + const float3 brush_pos_end_cu = transforms_.world_to_curves * brush_pos_end_wo; const float3 brush_pos_diff_cu = brush_pos_end_cu - brush_pos_start_cu; const float brush_pos_diff_length_cu = math::length(brush_pos_diff_cu); - const float brush_radius_cu = self_->brush_3d_.radius_cu; + const float brush_radius_cu = self_->brush_3d_.radius_cu * brush_radius_factor_; const float brush_radius_sq_cu = pow2f(brush_radius_cu); const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( @@ -474,15 +451,18 @@ struct CurvesEffectOperationExecutor { for (const int curve_i : curves_range) { const IndexRange points = curves_->points_for_curve(curve_i); - const int tot_segments = points.size() - 1; + float max_move_distance_cu = 0.0f; + + const float curve_selection_factor = curve_selection_factors_[curve_i]; + for (const float4x4 &brush_transform : symmetry_brush_transforms) { const float3 brush_pos_start_transformed_cu = brush_transform * brush_pos_start_cu; const float3 brush_pos_end_transformed_cu = brush_transform * brush_pos_end_cu; - for (const int segment_i : IndexRange(tot_segments)) { - const float3 &p1_cu = positions_cu[points[segment_i]]; - const float3 &p2_cu = positions_cu[points[segment_i] + 1]; + for (const int segment_i : points.drop_back(1)) { + const float3 &p1_cu = positions_cu[segment_i]; + const float3 &p2_cu = positions_cu[segment_i + 1]; float3 closest_on_segment_cu; float3 closest_on_brush_cu; @@ -502,7 +482,7 @@ struct CurvesEffectOperationExecutor { const float dist_to_brush_cu = std::sqrt(dist_to_brush_sq_cu); const float radius_falloff = BKE_brush_curve_strength( brush_, dist_to_brush_cu, brush_radius_cu); - const float weight = brush_strength_ * radius_falloff; + const float weight = brush_strength_ * radius_falloff * curve_selection_factor; const float move_distance_cu = weight * brush_pos_diff_length_cu; max_move_distance_cu = std::max(max_move_distance_cu, move_distance_cu); @@ -517,18 +497,18 @@ struct CurvesEffectOperationExecutor { } }; -void CurvesEffectOperation::on_stroke_extended(bContext *C, +void CurvesEffectOperation::on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) { - CurvesEffectOperationExecutor executor; + CurvesEffectOperationExecutor executor{C}; executor.execute(*this, C, stroke_extension); } std::unique_ptr<CurvesSculptStrokeOperation> new_grow_shrink_operation( - const BrushStrokeMode brush_mode, bContext *C) + const BrushStrokeMode brush_mode, const bContext &C) { - Scene &scene = *CTX_data_scene(C); - Brush &brush = *BKE_paint_brush(&scene.toolsettings->curves_sculpt->paint); + const Scene &scene = *CTX_data_scene(&C); + const Brush &brush = *BKE_paint_brush_for_read(&scene.toolsettings->curves_sculpt->paint); const bool use_scale_uniform = brush.curves_sculpt_settings->flag & BRUSH_CURVES_SCULPT_FLAG_SCALE_UNIFORM; const bool use_grow = (brush_mode == BRUSH_STROKE_INVERT) == ((brush.flag & BRUSH_DIR_IN) != 0); diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh index 9d000649d62..ad3871bee45 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh @@ -8,12 +8,20 @@ #include "paint_intern.h" #include "BLI_math_vector.hh" +#include "BLI_vector.hh" +#include "BLI_virtual_array.hh" +#include "BKE_attribute.h" #include "BKE_curves.hh" struct ARegion; struct RegionView3D; +struct Depsgraph; +struct View3D; struct Object; +struct Brush; +struct Scene; +struct BVHTreeFromMesh; namespace blender::ed::sculpt_paint { @@ -22,23 +30,44 @@ using bke::CurvesGeometry; struct StrokeExtension { bool is_first; float2 mouse_position; + float pressure; }; +float brush_radius_factor(const Brush &brush, const StrokeExtension &stroke_extension); +float brush_radius_get(const Scene &scene, + const Brush &brush, + const StrokeExtension &stroke_extension); + +float brush_strength_factor(const Brush &brush, const StrokeExtension &stroke_extension); +float brush_strength_get(const Scene &scene, + const Brush &brush, + const StrokeExtension &stroke_extension); + /** * Base class for stroke based operations in curves sculpt mode. */ class CurvesSculptStrokeOperation { public: virtual ~CurvesSculptStrokeOperation() = default; - virtual void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) = 0; + virtual void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) = 0; }; -std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation(bContext &C, ReportList *reports); +std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation(const bContext &C, + ReportList *reports); std::unique_ptr<CurvesSculptStrokeOperation> new_comb_operation(); std::unique_ptr<CurvesSculptStrokeOperation> new_delete_operation(); std::unique_ptr<CurvesSculptStrokeOperation> new_snake_hook_operation(); std::unique_ptr<CurvesSculptStrokeOperation> new_grow_shrink_operation( - const BrushStrokeMode brush_mode, bContext *C); + const BrushStrokeMode brush_mode, const bContext &C); +std::unique_ptr<CurvesSculptStrokeOperation> new_selection_paint_operation( + const BrushStrokeMode brush_mode, const bContext &C); +std::unique_ptr<CurvesSculptStrokeOperation> new_pinch_operation(const BrushStrokeMode brush_mode, + const bContext &C); +std::unique_ptr<CurvesSculptStrokeOperation> new_smooth_operation(); +std::unique_ptr<CurvesSculptStrokeOperation> new_puff_operation(); +std::unique_ptr<CurvesSculptStrokeOperation> new_density_operation( + const BrushStrokeMode brush_mode, const bContext &C); +std::unique_ptr<CurvesSculptStrokeOperation> new_slide_operation(); struct CurvesBrush3D { float3 position_cu; @@ -48,11 +77,69 @@ struct CurvesBrush3D { /** * Find 3d brush position based on cursor position for curves sculpting. */ -std::optional<CurvesBrush3D> sample_curves_3d_brush(bContext &C, - Object &curves_object, +std::optional<CurvesBrush3D> sample_curves_3d_brush(const Depsgraph &depsgraph, + const ARegion ®ion, + const View3D &v3d, + const RegionView3D &rv3d, + const Object &curves_object, const float2 &brush_pos_re, - float brush_radius_re); + const float brush_radius_re); Vector<float4x4> get_symmetry_brush_transforms(eCurvesSymmetryType symmetry); +/** + * Get the floating point selection on the curve domain, averaged from points if necessary. + */ +VArray<float> get_curves_selection(const Curves &curves_id); + +/** + * Get the floating point selection on the curve domain, copied from curves if necessary. + */ +VArray<float> get_point_selection(const Curves &curves_id); + +/** + * Find curves that have any point selected (a selection factor greater than zero), + * or curves that have their own selection factor greater than zero. + */ +IndexMask retrieve_selected_curves(const Curves &curves_id, Vector<int64_t> &r_indices); + +void move_last_point_and_resample(MutableSpan<float3> positions, const float3 &new_last_position); + +class CurvesSculptCommonContext { + public: + const Depsgraph *depsgraph = nullptr; + const Scene *scene = nullptr; + ARegion *region = nullptr; + const View3D *v3d = nullptr; + const RegionView3D *rv3d = nullptr; + + CurvesSculptCommonContext(const bContext &C); +}; + +struct CurvesSculptTransforms { + float4x4 curves_to_world; + float4x4 curves_to_surface; + float4x4 world_to_curves; + float4x4 world_to_surface; + float4x4 surface_to_world; + float4x4 surface_to_curves; + float4x4 surface_to_curves_normal; + + CurvesSculptTransforms() = default; + CurvesSculptTransforms(const Object &curves_ob, const Object *surface_ob); +}; + +std::optional<CurvesBrush3D> sample_curves_surface_3d_brush( + const Depsgraph &depsgraph, + const ARegion ®ion, + const View3D &v3d, + const CurvesSculptTransforms &transforms, + const BVHTreeFromMesh &surface_bvh, + const float2 &brush_pos_re, + const float brush_radius_re); + +float transform_brush_radius(const float4x4 &transform, + const float3 &brush_position, + const float old_radius); + } // namespace blender::ed::sculpt_paint diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc index d8713c8eb1d..5d6ffa67005 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc @@ -18,6 +18,7 @@ #include "WM_toolsystem.h" #include "ED_curves_sculpt.h" +#include "ED_image.h" #include "ED_object.h" #include "ED_screen.h" #include "ED_view3d.h" @@ -49,7 +50,7 @@ bool CURVES_SCULPT_mode_poll(bContext *C) { - Object *ob = CTX_data_active_object(C); + const Object *ob = CTX_data_active_object(C); return ob && ob->mode & OB_MODE_SCULPT_CURVES; } @@ -74,14 +75,44 @@ using blender::bke::CurvesGeometry; /** \name * SCULPT_CURVES_OT_brush_stroke * \{ */ +float brush_radius_factor(const Brush &brush, const StrokeExtension &stroke_extension) +{ + if (BKE_brush_use_size_pressure(&brush)) { + return stroke_extension.pressure; + } + return 1.0f; +} + +float brush_radius_get(const Scene &scene, + const Brush &brush, + const StrokeExtension &stroke_extension) +{ + return BKE_brush_size_get(&scene, &brush) * brush_radius_factor(brush, stroke_extension); +} + +float brush_strength_factor(const Brush &brush, const StrokeExtension &stroke_extension) +{ + if (BKE_brush_use_alpha_pressure(&brush)) { + return stroke_extension.pressure; + } + return 1.0f; +} + +float brush_strength_get(const Scene &scene, + const Brush &brush, + const StrokeExtension &stroke_extension) +{ + return BKE_brush_alpha_get(&scene, &brush) * brush_strength_factor(brush, stroke_extension); +} + static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation(bContext &C, wmOperator &op) { const BrushStrokeMode mode = static_cast<BrushStrokeMode>(RNA_enum_get(op.ptr, "mode")); - Scene &scene = *CTX_data_scene(&C); - CurvesSculpt &curves_sculpt = *scene.toolsettings->curves_sculpt; - Brush &brush = *BKE_paint_brush(&curves_sculpt.paint); + const Scene &scene = *CTX_data_scene(&C); + const CurvesSculpt &curves_sculpt = *scene.toolsettings->curves_sculpt; + const Brush &brush = *BKE_paint_brush_for_read(&curves_sculpt.paint); switch (brush.curves_sculpt_tool) { case CURVES_SCULPT_TOOL_COMB: return new_comb_operation(); @@ -92,7 +123,9 @@ static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation(bConte case CURVES_SCULPT_TOOL_ADD: return new_add_operation(C, op.reports); case CURVES_SCULPT_TOOL_GROW_SHRINK: - return new_grow_shrink_operation(mode, &C); + return new_grow_shrink_operation(mode, C); + case CURVES_SCULPT_TOOL_SELECTION_PAINT: + return new_selection_paint_operation(mode, C); } BLI_assert_unreachable(); return {}; @@ -128,6 +161,7 @@ static void stroke_update_step(bContext *C, StrokeExtension stroke_extension; RNA_float_get_array(stroke_element, "mouse", stroke_extension.mouse_position); + stroke_extension.pressure = RNA_float_get(stroke_element, "pressure"); if (!op_data->operation) { stroke_extension.is_first = true; @@ -138,7 +172,7 @@ static void stroke_update_step(bContext *C, } if (op_data->operation) { - op_data->operation->on_stroke_extended(C, stroke_extension); + op_data->operation->on_stroke_extended(*C, stroke_extension); } } @@ -149,8 +183,8 @@ static void stroke_done(const bContext *C, PaintStroke *stroke) static int sculpt_curves_stroke_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - Paint *paint = BKE_paint_get_active_from_context(C); - Brush *brush = BKE_paint_brush(paint); + const Paint *paint = BKE_paint_get_active_from_context(C); + const Brush *brush = BKE_paint_brush_for_read(paint); if (brush == nullptr) { return OPERATOR_CANCELLED; } @@ -219,7 +253,7 @@ static void SCULPT_CURVES_OT_brush_stroke(struct wmOperatorType *ot) static bool curves_sculptmode_toggle_poll(bContext *C) { - Object *ob = CTX_data_active_object(C); + const Object *ob = CTX_data_active_object(C); if (ob == nullptr) { return false; } @@ -238,7 +272,7 @@ static void curves_sculptmode_enter(bContext *C) ob->mode = OB_MODE_SCULPT_CURVES; - paint_cursor_start(&curves_sculpt->paint, CURVES_SCULPT_mode_poll_view3d); + ED_paint_cursor_start(&curves_sculpt->paint, CURVES_SCULPT_mode_poll_view3d); /* Update for mode change. */ DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_selection.cc b/source/blender/editors/sculpt_paint/curves_sculpt_selection.cc new file mode 100644 index 00000000000..f620fed5761 --- /dev/null +++ b/source/blender/editors/sculpt_paint/curves_sculpt_selection.cc @@ -0,0 +1,105 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_index_mask_ops.hh" + +#include "BKE_curves.hh" + +#include "curves_sculpt_intern.hh" + +namespace blender::ed::sculpt_paint { + +static VArray<float> get_curves_selection(const CurvesGeometry &curves, const eAttrDomain domain) +{ + switch (domain) { + case ATTR_DOMAIN_CURVE: + return curves.selection_curve_float(); + case ATTR_DOMAIN_POINT: + return curves.adapt_domain( + curves.selection_point_float(), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE); + default: + BLI_assert_unreachable(); + return {}; + } +} + +VArray<float> get_curves_selection(const Curves &curves_id) +{ + if (!(curves_id.flag & CV_SCULPT_SELECTION_ENABLED)) { + return VArray<float>::ForSingle(1.0f, CurvesGeometry::wrap(curves_id.geometry).curves_num()); + } + return get_curves_selection(CurvesGeometry::wrap(curves_id.geometry), + eAttrDomain(curves_id.selection_domain)); +} + +static VArray<float> get_point_selection(const CurvesGeometry &curves, const eAttrDomain domain) +{ + switch (domain) { + case ATTR_DOMAIN_CURVE: + return curves.adapt_domain( + curves.selection_curve_float(), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT); + case ATTR_DOMAIN_POINT: + return curves.selection_point_float(); + default: + BLI_assert_unreachable(); + return {}; + } +} + +VArray<float> get_point_selection(const Curves &curves_id) +{ + if (!(curves_id.flag & CV_SCULPT_SELECTION_ENABLED)) { + return VArray<float>::ForSingle(1.0f, CurvesGeometry::wrap(curves_id.geometry).points_num()); + } + return get_point_selection(CurvesGeometry::wrap(curves_id.geometry), + eAttrDomain(curves_id.selection_domain)); +} + +static IndexMask retrieve_selected_curves(const CurvesGeometry &curves, + const eAttrDomain domain, + Vector<int64_t> &r_indices) +{ + switch (domain) { + case ATTR_DOMAIN_POINT: { + const VArray<float> selection = curves.selection_point_float(); + if (selection.is_single()) { + return selection.get_internal_single() == 0.0f ? IndexMask(0) : + IndexMask(curves.curves_num()); + } + return index_mask_ops::find_indices_based_on_predicate( + curves.curves_range(), 512, r_indices, [&](const int curve_i) { + for (const int i : curves.points_for_curve(curve_i)) { + if (selection[i] > 0.0f) { + return true; + } + } + return false; + }); + } + case ATTR_DOMAIN_CURVE: { + const VArray<float> selection = curves.selection_curve_float(); + if (selection.is_single()) { + return selection.get_internal_single() == 0.0f ? IndexMask(0) : + IndexMask(curves.curves_num()); + } + return index_mask_ops::find_indices_based_on_predicate( + curves.curves_range(), 2048, r_indices, [&](const int i) { + return selection[i] > 0.0f; + }); + } + default: + BLI_assert_unreachable(); + return {}; + } +} + +IndexMask retrieve_selected_curves(const Curves &curves_id, Vector<int64_t> &r_indices) +{ + if (!(curves_id.flag & CV_SCULPT_SELECTION_ENABLED)) { + return CurvesGeometry::wrap(curves_id.geometry).curves_range(); + } + return retrieve_selected_curves(CurvesGeometry::wrap(curves_id.geometry), + eAttrDomain(curves_id.selection_domain), + r_indices); +} + +} // namespace blender::ed::sculpt_paint diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_selection_paint.cc b/source/blender/editors/sculpt_paint/curves_sculpt_selection_paint.cc new file mode 100644 index 00000000000..b40aebcaaf1 --- /dev/null +++ b/source/blender/editors/sculpt_paint/curves_sculpt_selection_paint.cc @@ -0,0 +1,394 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <algorithm> +#include <numeric> + +#include "BLI_memory_utils.hh" +#include "BLI_task.hh" + +#include "DNA_brush_types.h" + +#include "BKE_brush.h" +#include "BKE_context.h" +#include "BKE_curves.hh" + +#include "DEG_depsgraph.h" + +#include "ED_screen.h" +#include "ED_view3d.h" + +#include "WM_api.h" + +#include "curves_sculpt_intern.hh" + +/** + * The code below uses a suffix naming convention to indicate the coordinate space: + * cu: Local space of the curves object that is being edited. + * wo: World space. + * re: 2D coordinates within the region. + */ + +namespace blender::ed::sculpt_paint { + +using bke::CurvesGeometry; + +class SelectionPaintOperation : public CurvesSculptStrokeOperation { + private: + bool use_select_; + bool clear_selection_; + + CurvesBrush3D brush_3d_; + + friend struct SelectionPaintOperationExecutor; + + public: + SelectionPaintOperation(const bool use_select, const bool clear_selection) + : use_select_(use_select), clear_selection_(clear_selection) + { + } + void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override; +}; + +struct SelectionPaintOperationExecutor { + SelectionPaintOperation *self_ = nullptr; + CurvesSculptCommonContext ctx_; + + Object *object_ = nullptr; + Curves *curves_id_ = nullptr; + CurvesGeometry *curves_ = nullptr; + + const Brush *brush_ = nullptr; + float brush_radius_base_re_; + float brush_radius_factor_; + float brush_strength_; + + float selection_goal_; + + float2 brush_pos_re_; + + CurvesSculptTransforms transforms_; + + SelectionPaintOperationExecutor(const bContext &C) : ctx_(C) + { + } + + void execute(SelectionPaintOperation &self, + const bContext &C, + const StrokeExtension &stroke_extension) + { + self_ = &self; + object_ = CTX_data_active_object(&C); + + curves_id_ = static_cast<Curves *>(object_->data); + curves_ = &CurvesGeometry::wrap(curves_id_->geometry); + curves_id_->flag |= CV_SCULPT_SELECTION_ENABLED; + if (curves_->curves_num() == 0) { + return; + } + + brush_ = BKE_paint_brush_for_read(&ctx_.scene->toolsettings->curves_sculpt->paint); + brush_radius_base_re_ = BKE_brush_size_get(ctx_.scene, brush_); + brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension); + brush_strength_ = BKE_brush_alpha_get(ctx_.scene, brush_); + + brush_pos_re_ = stroke_extension.mouse_position; + + if (self.clear_selection_) { + if (stroke_extension.is_first) { + if (curves_id_->selection_domain == ATTR_DOMAIN_POINT) { + curves_->selection_point_float_for_write().fill(0.0f); + } + else if (curves_id_->selection_domain == ATTR_DOMAIN_CURVE) { + curves_->selection_curve_float_for_write().fill(0.0f); + } + } + } + + transforms_ = CurvesSculptTransforms(*object_, curves_id_->surface); + + const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>( + brush_->falloff_shape); + + selection_goal_ = self_->use_select_ ? 1.0f : 0.0f; + + if (stroke_extension.is_first) { + if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { + this->initialize_spherical_brush_reference_point(); + } + } + + if (curves_id_->selection_domain == ATTR_DOMAIN_POINT) { + MutableSpan<float> selection = curves_->selection_point_float_for_write(); + if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { + this->paint_point_selection_projected_with_symmetry(selection); + } + else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { + this->paint_point_selection_spherical_with_symmetry(selection); + } + } + else { + MutableSpan<float> selection = curves_->selection_curve_float_for_write(); + if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { + this->paint_curve_selection_projected_with_symmetry(selection); + } + else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { + this->paint_curve_selection_spherical_with_symmetry(selection); + } + } + + /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because + * selection is handled as a generic attribute for now. */ + DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); + ED_region_tag_redraw(ctx_.region); + } + + void paint_point_selection_projected_with_symmetry(MutableSpan<float> selection) + { + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->paint_point_selection_projected(brush_transform, selection); + } + } + + void paint_point_selection_projected(const float4x4 &brush_transform, + MutableSpan<float> selection) + { + const float4x4 brush_transform_inv = brush_transform.inverted(); + + float4x4 projection; + ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); + + Span<float3> positions_cu = curves_->positions(); + + const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; + const float brush_radius_sq_re = pow2f(brush_radius_re); + + threading::parallel_for(curves_->points_range(), 1024, [&](const IndexRange point_range) { + for (const int point_i : point_range) { + const float3 pos_cu = brush_transform_inv * positions_cu[point_i]; + + /* Find the position of the point in screen space. */ + float2 pos_re; + ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, pos_re, projection.values); + + const float distance_to_brush_sq_re = math::distance_squared(pos_re, brush_pos_re_); + if (distance_to_brush_sq_re > brush_radius_sq_re) { + /* Ignore the point because it's too far away. */ + continue; + } + + const float distance_to_brush_re = std::sqrt(distance_to_brush_sq_re); + /* A falloff that is based on how far away the point is from the stroke. */ + const float radius_falloff = BKE_brush_curve_strength( + brush_, distance_to_brush_re, brush_radius_re); + /* Combine the falloff and brush strength. */ + const float weight = brush_strength_ * radius_falloff; + + selection[point_i] = math::interpolate(selection[point_i], selection_goal_, weight); + } + }); + } + + void paint_point_selection_spherical_with_symmetry(MutableSpan<float> selection) + { + float4x4 projection; + ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); + + float3 brush_wo; + ED_view3d_win_to_3d(ctx_.v3d, + ctx_.region, + transforms_.curves_to_world * self_->brush_3d_.position_cu, + brush_pos_re_, + brush_wo); + const float3 brush_cu = transforms_.world_to_curves * brush_wo; + + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->paint_point_selection_spherical(selection, brush_transform * brush_cu); + } + } + + void paint_point_selection_spherical(MutableSpan<float> selection, const float3 &brush_cu) + { + Span<float3> positions_cu = curves_->positions(); + + const float brush_radius_cu = self_->brush_3d_.radius_cu; + const float brush_radius_sq_cu = pow2f(brush_radius_cu); + + threading::parallel_for(curves_->points_range(), 1024, [&](const IndexRange point_range) { + for (const int i : point_range) { + const float3 pos_old_cu = positions_cu[i]; + + /* Compute distance to the brush. */ + const float distance_to_brush_sq_cu = math::distance_squared(pos_old_cu, brush_cu); + if (distance_to_brush_sq_cu > brush_radius_sq_cu) { + /* Ignore the point because it's too far away. */ + continue; + } + + const float distance_to_brush_cu = std::sqrt(distance_to_brush_sq_cu); + + /* A falloff that is based on how far away the point is from the stroke. */ + const float radius_falloff = BKE_brush_curve_strength( + brush_, distance_to_brush_cu, brush_radius_cu); + /* Combine the falloff and brush strength. */ + const float weight = brush_strength_ * radius_falloff; + + selection[i] = math::interpolate(selection[i], selection_goal_, weight); + } + }); + } + + void paint_curve_selection_projected_with_symmetry(MutableSpan<float> selection) + { + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->paint_curve_selection_projected(brush_transform, selection); + } + } + + void paint_curve_selection_projected(const float4x4 &brush_transform, + MutableSpan<float> selection) + { + const Span<float3> positions_cu = curves_->positions(); + const float4x4 brush_transform_inv = brush_transform.inverted(); + + float4x4 projection; + ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); + + const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; + const float brush_radius_sq_re = pow2f(brush_radius_re); + + threading::parallel_for(curves_->curves_range(), 1024, [&](const IndexRange curves_range) { + for (const int curve_i : curves_range) { + const float max_weight = threading::parallel_reduce( + curves_->points_for_curve(curve_i).drop_back(1), + 1024, + 0.0f, + [&](const IndexRange segment_range, const float init) { + float max_weight = init; + for (const int segment_i : segment_range) { + const float3 pos1_cu = brush_transform_inv * positions_cu[segment_i]; + const float3 pos2_cu = brush_transform_inv * positions_cu[segment_i + 1]; + + float2 pos1_re; + float2 pos2_re; + ED_view3d_project_float_v2_m4(ctx_.region, pos1_cu, pos1_re, projection.values); + ED_view3d_project_float_v2_m4(ctx_.region, pos2_cu, pos2_re, projection.values); + + const float distance_sq_re = dist_squared_to_line_segment_v2( + brush_pos_re_, pos1_re, pos2_re); + if (distance_sq_re > brush_radius_sq_re) { + continue; + } + const float radius_falloff = BKE_brush_curve_strength( + brush_, std::sqrt(distance_sq_re), brush_radius_re); + const float weight = brush_strength_ * radius_falloff; + max_weight = std::max(max_weight, weight); + } + return max_weight; + }, + [](float a, float b) { return std::max(a, b); }); + selection[curve_i] = math::interpolate(selection[curve_i], selection_goal_, max_weight); + } + }); + } + + void paint_curve_selection_spherical_with_symmetry(MutableSpan<float> selection) + { + float4x4 projection; + ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); + + float3 brush_wo; + ED_view3d_win_to_3d(ctx_.v3d, + ctx_.region, + transforms_.curves_to_world * self_->brush_3d_.position_cu, + brush_pos_re_, + brush_wo); + const float3 brush_cu = transforms_.world_to_curves * brush_wo; + + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->paint_curve_selection_spherical(selection, brush_transform * brush_cu); + } + } + + void paint_curve_selection_spherical(MutableSpan<float> selection, const float3 &brush_cu) + { + const Span<float3> positions_cu = curves_->positions(); + + const float brush_radius_cu = self_->brush_3d_.radius_cu; + const float brush_radius_sq_cu = pow2f(brush_radius_cu); + + threading::parallel_for(curves_->curves_range(), 1024, [&](const IndexRange curves_range) { + for (const int curve_i : curves_range) { + const float max_weight = threading::parallel_reduce( + curves_->points_for_curve(curve_i).drop_back(1), + 1024, + 0.0f, + [&](const IndexRange segment_range, const float init) { + float max_weight = init; + for (const int segment_i : segment_range) { + const float3 &pos1_cu = positions_cu[segment_i]; + const float3 &pos2_cu = positions_cu[segment_i + 1]; + + const float distance_sq_cu = dist_squared_to_line_segment_v3( + brush_cu, pos1_cu, pos2_cu); + if (distance_sq_cu > brush_radius_sq_cu) { + continue; + } + const float radius_falloff = BKE_brush_curve_strength( + brush_, std::sqrt(distance_sq_cu), brush_radius_cu); + const float weight = brush_strength_ * radius_falloff; + max_weight = std::max(max_weight, weight); + } + return max_weight; + }, + [](float a, float b) { return std::max(a, b); }); + selection[curve_i] = math::interpolate(selection[curve_i], selection_goal_, max_weight); + } + }); + } + + void initialize_spherical_brush_reference_point() + { + std::optional<CurvesBrush3D> brush_3d = sample_curves_3d_brush(*ctx_.depsgraph, + *ctx_.region, + *ctx_.v3d, + *ctx_.rv3d, + *object_, + brush_pos_re_, + brush_radius_base_re_); + if (brush_3d.has_value()) { + self_->brush_3d_ = *brush_3d; + } + } +}; + +void SelectionPaintOperation::on_stroke_extended(const bContext &C, + const StrokeExtension &stroke_extension) +{ + SelectionPaintOperationExecutor executor{C}; + executor.execute(*this, C, stroke_extension); +} + +std::unique_ptr<CurvesSculptStrokeOperation> new_selection_paint_operation( + const BrushStrokeMode brush_mode, const bContext &C) +{ + Scene &scene = *CTX_data_scene(&C); + Brush &brush = *BKE_paint_brush(&scene.toolsettings->curves_sculpt->paint); + const bool use_select = ELEM(brush_mode, BRUSH_STROKE_INVERT) == + ((brush.flag & BRUSH_DIR_IN) != 0); + const bool clear_selection = use_select && brush_mode != BRUSH_STROKE_SMOOTH; + + return std::make_unique<SelectionPaintOperation>(use_select, clear_selection); +} + +} // namespace blender::ed::sculpt_paint diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc b/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc index d2814c17682..b63e5a7756b 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc @@ -23,7 +23,6 @@ #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" #include "BKE_paint.h" -#include "BKE_spline.hh" #include "DNA_brush_enums.h" #include "DNA_brush_types.h" @@ -37,6 +36,8 @@ #include "ED_screen.h" #include "ED_view3d.h" +#include "WM_api.h" + /** * The code below uses a prefix naming convention to indicate the coordinate space: * - `cu`: Local space of the curves object that is being edited. @@ -61,7 +62,7 @@ class SnakeHookOperation : public CurvesSculptStrokeOperation { friend struct SnakeHookOperatorExecutor; public: - void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override; + void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override; }; /** @@ -70,49 +71,51 @@ class SnakeHookOperation : public CurvesSculptStrokeOperation { */ struct SnakeHookOperatorExecutor { SnakeHookOperation *self_ = nullptr; - bContext *C_ = nullptr; - Scene *scene_ = nullptr; - Object *object_ = nullptr; - ARegion *region_ = nullptr; - View3D *v3d_ = nullptr; - RegionView3D *rv3d_ = nullptr; + CurvesSculptCommonContext ctx_; - CurvesSculpt *curves_sculpt_ = nullptr; - Brush *brush_ = nullptr; - float brush_radius_re_; + const CurvesSculpt *curves_sculpt_ = nullptr; + const Brush *brush_ = nullptr; + float brush_radius_base_re_; + float brush_radius_factor_; float brush_strength_; + eBrushFalloffShape falloff_shape_; + Object *object_ = nullptr; Curves *curves_id_ = nullptr; CurvesGeometry *curves_ = nullptr; - float4x4 curves_to_world_mat_; - float4x4 world_to_curves_mat_; + VArray<float> curve_factors_; + Vector<int64_t> selected_curve_indices_; + IndexMask curve_selection_; + + CurvesSculptTransforms transforms_; float2 brush_pos_prev_re_; float2 brush_pos_re_; float2 brush_pos_diff_re_; - void execute(SnakeHookOperation &self, bContext *C, const StrokeExtension &stroke_extension) + SnakeHookOperatorExecutor(const bContext &C) : ctx_(C) + { + } + + void execute(SnakeHookOperation &self, + const bContext &C, + const StrokeExtension &stroke_extension) { BLI_SCOPED_DEFER([&]() { self.last_mouse_position_re_ = stroke_extension.mouse_position; }); self_ = &self; - C_ = C; - scene_ = CTX_data_scene(C); - object_ = CTX_data_active_object(C); - region_ = CTX_wm_region(C); - v3d_ = CTX_wm_view3d(C); - rv3d_ = CTX_wm_region_view3d(C); - - curves_sculpt_ = scene_->toolsettings->curves_sculpt; - brush_ = BKE_paint_brush(&curves_sculpt_->paint); - brush_radius_re_ = BKE_brush_size_get(scene_, brush_); - brush_strength_ = BKE_brush_alpha_get(scene_, brush_); - falloff_shape_ = static_cast<eBrushFalloffShape>(brush_->falloff_shape); + object_ = CTX_data_active_object(&C); + + curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt; + brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint); + + brush_radius_base_re_ = BKE_brush_size_get(ctx_.scene, brush_); + brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension); + brush_strength_ = brush_strength_get(*ctx_.scene, *brush_, stroke_extension); - curves_to_world_mat_ = object_->obmat; - world_to_curves_mat_ = curves_to_world_mat_.inverted(); + falloff_shape_ = static_cast<eBrushFalloffShape>(brush_->falloff_shape); curves_id_ = static_cast<Curves *>(object_->data); curves_ = &CurvesGeometry::wrap(curves_id_->geometry); @@ -120,14 +123,24 @@ struct SnakeHookOperatorExecutor { return; } + transforms_ = CurvesSculptTransforms(*object_, curves_id_->surface); + + curve_factors_ = get_curves_selection(*curves_id_); + curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_); + brush_pos_prev_re_ = self.last_mouse_position_re_; brush_pos_re_ = stroke_extension.mouse_position; brush_pos_diff_re_ = brush_pos_re_ - brush_pos_prev_re_; if (stroke_extension.is_first) { if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) { - std::optional<CurvesBrush3D> brush_3d = sample_curves_3d_brush( - *C_, *object_, brush_pos_re_, brush_radius_re_); + std::optional<CurvesBrush3D> brush_3d = sample_curves_3d_brush(*ctx_.depsgraph, + *ctx_.region, + *ctx_.v3d, + *ctx_.rv3d, + *object_, + brush_pos_re_, + brush_radius_base_re_); if (brush_3d.has_value()) { self_->brush_3d_ = *brush_3d; } @@ -147,7 +160,8 @@ struct SnakeHookOperatorExecutor { curves_->tag_positions_changed(); DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); - ED_region_tag_redraw(region_); + WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); + ED_region_tag_redraw(ctx_.region); } void projected_snake_hook_with_symmetry() @@ -166,7 +180,10 @@ struct SnakeHookOperatorExecutor { MutableSpan<float3> positions_cu = curves_->positions_for_write(); float4x4 projection; - ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values); + ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); + + const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; + const float brush_radius_sq_re = pow2f(brush_radius_re); threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) { for (const int curve_i : curves_range) { @@ -175,24 +192,29 @@ struct SnakeHookOperatorExecutor { const float3 old_pos_cu = brush_transform_inv * positions_cu[last_point_i]; float2 old_pos_re; - ED_view3d_project_float_v2_m4(region_, old_pos_cu, old_pos_re, projection.values); + ED_view3d_project_float_v2_m4(ctx_.region, old_pos_cu, old_pos_re, projection.values); - const float distance_to_brush_re = math::distance(old_pos_re, brush_pos_prev_re_); - if (distance_to_brush_re > brush_radius_re_) { + const float distance_to_brush_sq_re = math::distance_squared(old_pos_re, + brush_pos_prev_re_); + if (distance_to_brush_sq_re > brush_radius_sq_re) { continue; } const float radius_falloff = BKE_brush_curve_strength( - brush_, distance_to_brush_re, brush_radius_re_); - const float weight = brush_strength_ * radius_falloff; + brush_, std::sqrt(distance_to_brush_sq_re), brush_radius_re); + const float weight = brush_strength_ * radius_falloff * curve_factors_[curve_i]; const float2 new_position_re = old_pos_re + brush_pos_diff_re_ * weight; float3 new_position_wo; - ED_view3d_win_to_3d( - v3d_, region_, curves_to_world_mat_ * old_pos_cu, new_position_re, new_position_wo); - const float3 new_position_cu = brush_transform * (world_to_curves_mat_ * new_position_wo); - - this->move_last_point_and_resample(positions_cu.slice(points), new_position_cu); + ED_view3d_win_to_3d(ctx_.v3d, + ctx_.region, + transforms_.curves_to_world * old_pos_cu, + new_position_re, + new_position_wo); + const float3 new_position_cu = brush_transform * + (transforms_.world_to_curves * new_position_wo); + + move_last_point_and_resample(positions_cu.slice(points), new_position_cu); } }); } @@ -200,23 +222,23 @@ struct SnakeHookOperatorExecutor { void spherical_snake_hook_with_symmetry() { float4x4 projection; - ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values); + ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); float3 brush_start_wo, brush_end_wo; - ED_view3d_win_to_3d(v3d_, - region_, - curves_to_world_mat_ * self_->brush_3d_.position_cu, + ED_view3d_win_to_3d(ctx_.v3d, + ctx_.region, + transforms_.curves_to_world * self_->brush_3d_.position_cu, brush_pos_prev_re_, brush_start_wo); - ED_view3d_win_to_3d(v3d_, - region_, - curves_to_world_mat_ * self_->brush_3d_.position_cu, + ED_view3d_win_to_3d(ctx_.v3d, + ctx_.region, + transforms_.curves_to_world * self_->brush_3d_.position_cu, brush_pos_re_, brush_end_wo); - const float3 brush_start_cu = world_to_curves_mat_ * brush_start_wo; - const float3 brush_end_cu = world_to_curves_mat_ * brush_end_wo; + const float3 brush_start_cu = transforms_.world_to_curves * brush_start_wo; + const float3 brush_end_cu = transforms_.world_to_curves * brush_end_wo; - const float brush_radius_cu = self_->brush_3d_.radius_cu; + const float brush_radius_cu = self_->brush_3d_.radius_cu * brush_radius_factor_; const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( eCurvesSymmetryType(curves_id_->symmetry)); @@ -250,51 +272,20 @@ struct SnakeHookOperatorExecutor { const float radius_falloff = BKE_brush_curve_strength( brush_, distance_to_brush_cu, brush_radius_cu); - const float weight = brush_strength_ * radius_falloff; + const float weight = brush_strength_ * radius_falloff * curve_factors_[curve_i]; const float3 new_pos_cu = old_pos_cu + weight * brush_diff_cu; - this->move_last_point_and_resample(positions_cu.slice(points), new_pos_cu); + move_last_point_and_resample(positions_cu.slice(points), new_pos_cu); } }); } - - void move_last_point_and_resample(MutableSpan<float3> positions, - const float3 &new_last_position) const - { - /* Find the accumulated length of each point in the original curve, - * treating it as a poly curve for performance reasons and simplicity. */ - Array<float> orig_lengths(length_parameterize::lengths_num(positions.size(), false)); - length_parameterize::accumulate_lengths<float3>(positions, false, orig_lengths); - const float orig_total_length = orig_lengths.last(); - - /* Find the factor by which the new curve is shorter or longer than the original. */ - const float new_last_segment_length = math::distance(positions.last(1), new_last_position); - const float new_total_length = orig_lengths.last(1) + new_last_segment_length; - const float length_factor = new_total_length / orig_total_length; - - /* Calculate the lengths to sample the original curve with by scaling the original lengths. */ - Array<float> new_lengths(positions.size() - 1); - new_lengths.first() = 0.0f; - for (const int i : new_lengths.index_range().drop_front(1)) { - new_lengths[i] = orig_lengths[i - 1] * length_factor; - } - - Array<int> indices(positions.size() - 1); - Array<float> factors(positions.size() - 1); - length_parameterize::create_samples_from_sorted_lengths( - orig_lengths, new_lengths, false, indices, factors); - - Array<float3> new_positions(positions.size() - 1); - length_parameterize::linear_interpolation<float3>(positions, indices, factors, new_positions); - positions.drop_back(1).copy_from(new_positions); - positions.last() = new_last_position; - } }; -void SnakeHookOperation::on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) +void SnakeHookOperation::on_stroke_extended(const bContext &C, + const StrokeExtension &stroke_extension) { - SnakeHookOperatorExecutor executor; + SnakeHookOperatorExecutor executor{C}; executor.execute(*this, C, stroke_extension); } diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index 861da185975..c5ebcf870a3 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -38,6 +38,7 @@ #include "IMB_imbuf_types.h" +#include "ED_image.h" #include "ED_view3d.h" #include "DEG_depsgraph.h" @@ -1358,7 +1359,7 @@ static void paint_cursor_sculpt_session_update_and_init(PaintCursorContext *pcon ViewContext *vc = &pcontext->vc; SculptCursorGeometryInfo gi; - const float mouse[2] = { + const float mval_fl[2] = { pcontext->x - pcontext->region->winrct.xmin, pcontext->y - pcontext->region->winrct.ymin, }; @@ -1368,7 +1369,7 @@ static void paint_cursor_sculpt_session_update_and_init(PaintCursorContext *pcon pcontext->prev_active_vertex_index = ss->active_vertex_index; if (!ups->stroke_active) { pcontext->is_cursor_over_mesh = SCULPT_cursor_geometry_info_update( - C, &gi, mouse, (pcontext->brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE)); + C, &gi, mval_fl, (pcontext->brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE)); copy_v3_v3(pcontext->location, gi.location); copy_v3_v3(pcontext->normal, gi.normal); } @@ -1932,7 +1933,7 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) /* Public API */ -void paint_cursor_start(Paint *p, bool (*poll)(bContext *C)) +void ED_paint_cursor_start(Paint *p, bool (*poll)(bContext *C)) { if (p && !p->paint_cursor) { p->paint_cursor = WM_paint_cursor_activate( diff --git a/source/blender/editors/sculpt_paint/paint_image.cc b/source/blender/editors/sculpt_paint/paint_image.cc index 572e5b78b74..56cd4751881 100644 --- a/source/blender/editors/sculpt_paint/paint_image.cc +++ b/source/blender/editors/sculpt_paint/paint_image.cc @@ -287,7 +287,7 @@ static bool image_paint_poll_ex(bContext *C, bool check_tool) return false; } -bool image_paint_poll(bContext *C) +bool ED_image_tools_paint_poll(bContext *C) { return image_paint_poll_ex(C, true); } @@ -301,7 +301,7 @@ static bool image_paint_2d_clone_poll(bContext *C) { Brush *brush = image_paint_brush(C); - if (!CTX_wm_region_view3d(C) && image_paint_poll(C)) { + if (!CTX_wm_region_view3d(C) && ED_image_tools_paint_poll(C)) { if (brush && (brush->imagepaint_tool == PAINT_TOOL_CLONE)) { if (brush->clone.image) { return true; @@ -359,9 +359,7 @@ void paint_brush_color_get(struct Scene *scene, } /* Gradient / Color-band colors are not considered #PROP_COLOR_GAMMA. * Brush colors are expected to be in sRGB though. */ - IMB_colormanagement_scene_linear_to_srgb_v3(color_gr); - - copy_v3_v3(color, color_gr); + IMB_colormanagement_scene_linear_to_srgb_v3(color, color_gr); } else { copy_v3_v3(color, BKE_brush_color_get(scene, br)); @@ -432,7 +430,7 @@ static void toggle_paint_cursor(Scene *scene, bool enable) paint_cursor_delete_textures(); } else if (enable) { - paint_cursor_start(p, image_paint_poll); + ED_paint_cursor_start(p, ED_image_tools_paint_poll); } } @@ -457,7 +455,7 @@ void ED_space_image_paint_update(Main *bmain, wmWindowManager *wm, Scene *scene) if (enabled) { BKE_paint_init(bmain, scene, PAINT_MODE_TEXTURE_2D, PAINT_CURSOR_TEXTURE_PAINT); - paint_cursor_start(&imapaint->paint, image_paint_poll); + ED_paint_cursor_start(&imapaint->paint, ED_image_tools_paint_poll); } else { paint_cursor_delete_textures(); @@ -738,7 +736,7 @@ void PAINT_OT_sample_color(wmOperatorType *ot) ot->poll = sample_color_poll; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER; /* properties */ PropertyRNA *prop; @@ -927,7 +925,7 @@ static int brush_colors_flip_exec(bContext *C, wmOperator *UNUSED(op)) static bool brush_colors_flip_poll(bContext *C) { - if (image_paint_poll(C)) { + if (ED_image_tools_paint_poll(C)) { Brush *br = image_paint_brush(C); if (ELEM(br->imagepaint_tool, PAINT_TOOL_DRAW, PAINT_TOOL_FILL)) { return true; @@ -956,7 +954,7 @@ void PAINT_OT_brush_colors_flip(wmOperatorType *ot) ot->poll = brush_colors_flip_poll; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER; } void ED_imapaint_bucket_fill(struct bContext *C, @@ -993,7 +991,7 @@ static bool texture_paint_poll(bContext *C) bool image_texture_paint_poll(bContext *C) { - return (texture_paint_poll(C) || image_paint_poll(C)); + return (texture_paint_poll(C) || ED_image_tools_paint_poll(C)); } bool facemask_paint_poll(bContext *C) diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c index d30aa4dfab1..fae2e6863fa 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -621,39 +621,38 @@ static void brush_painter_imbuf_partial_update(BrushPainter *painter, } static void brush_painter_2d_tex_mapping(ImagePaintState *s, - ImBuf *canvas, + ImagePaintTile *tile, const int diameter, - const float startpos[2], const float pos[2], const float mouse[2], int mapmode, rctf *mapping) { - float invw = 1.0f / (float)canvas->x; - float invh = 1.0f / (float)canvas->y; - int xmin, ymin, xmax, ymax; - int ipos[2]; + float invw = 1.0f / (float)tile->canvas->x; + float invh = 1.0f / (float)tile->canvas->y; + float start[2]; /* find start coordinate of brush in canvas */ - ipos[0] = (int)floorf((pos[0] - diameter / 2) + 1.0f); - ipos[1] = (int)floorf((pos[1] - diameter / 2) + 1.0f); + start[0] = pos[0] - diameter / 2.0f; + start[1] = pos[1] - diameter / 2.0f; if (mapmode == MTEX_MAP_MODE_STENCIL) { /* map from view coordinates of brush to region coordinates */ - UI_view2d_view_to_region(s->v2d, ipos[0] * invw, ipos[1] * invh, &xmin, &ymin); - UI_view2d_view_to_region( - s->v2d, (ipos[0] + diameter) * invw, (ipos[1] + diameter) * invh, &xmax, &ymax); + float xmin, ymin, xmax, ymax; + UI_view2d_view_to_region_fl(s->v2d, start[0] * invw, start[1] * invh, &xmin, &ymin); + UI_view2d_view_to_region_fl( + s->v2d, (start[0] + diameter) * invw, (start[1] + diameter) * invh, &xmax, &ymax); /* output mapping from brush ibuf x/y to region coordinates */ - mapping->xmin = xmin; - mapping->ymin = ymin; mapping->xmax = (xmax - xmin) / (float)diameter; mapping->ymax = (ymax - ymin) / (float)diameter; + mapping->xmin = xmin + (tile->uv_origin[0] * tile->size[0] * mapping->xmax); + mapping->ymin = ymin + (tile->uv_origin[1] * tile->size[1] * mapping->ymax); } else if (mapmode == MTEX_MAP_MODE_3D) { /* 3D mapping, just mapping to canvas 0..1. */ - mapping->xmin = 2.0f * (ipos[0] * invw - 0.5f); - mapping->ymin = 2.0f * (ipos[1] * invh - 0.5f); + mapping->xmin = 2.0f * (start[0] * invw - 0.5f); + mapping->ymin = 2.0f * (start[1] * invh - 0.5f); mapping->xmax = 2.0f * invw; mapping->ymax = 2.0f * invh; } @@ -665,8 +664,10 @@ static void brush_painter_2d_tex_mapping(ImagePaintState *s, mapping->ymax = 1.0f; } else /* if (mapmode == MTEX_MAP_MODE_TILED) */ { - mapping->xmin = (int)(-diameter * 0.5) + (int)floorf(pos[0]) - (int)floorf(startpos[0]); - mapping->ymin = (int)(-diameter * 0.5) + (int)floorf(pos[1]) - (int)floorf(startpos[1]); + mapping->xmin = (int)(-diameter * 0.5) + (int)floorf(pos[0]) - + (int)floorf(tile->start_paintpos[0]); + mapping->ymin = (int)(-diameter * 0.5) + (int)floorf(pos[1]) - + (int)floorf(tile->start_paintpos[1]); mapping->xmax = 1.0f; mapping->ymax = 1.0f; } @@ -711,14 +712,8 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, do_partial_update = true; } - brush_painter_2d_tex_mapping(s, - tile->canvas, - diameter, - tile->start_paintpos, - pos, - mouse, - brush->mtex.brush_map_mode, - &painter->tex_mapping); + brush_painter_2d_tex_mapping( + s, tile, diameter, pos, mouse, brush->mtex.brush_map_mode, &painter->tex_mapping); } if (cache->is_maskbrush) { @@ -745,14 +740,8 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, renew_maxmask) { MEM_SAFE_FREE(cache->tex_mask); - brush_painter_2d_tex_mapping(s, - tile->canvas, - diameter, - tile->start_paintpos, - pos, - mouse, - brush->mask_mtex.brush_map_mode, - &painter->mask_mapping); + brush_painter_2d_tex_mapping( + s, tile, diameter, pos, mouse, brush->mask_mtex.brush_map_mode, &painter->mask_mapping); if (do_partial_update_mask) { brush_painter_mask_imbuf_partial_update(painter, tile, pos, diameter); diff --git a/source/blender/editors/sculpt_paint/paint_image_ops_paint.cc b/source/blender/editors/sculpt_paint/paint_image_ops_paint.cc index 8ddf1614e41..a671c24c514 100644 --- a/source/blender/editors/sculpt_paint/paint_image_ops_paint.cc +++ b/source/blender/editors/sculpt_paint/paint_image_ops_paint.cc @@ -34,6 +34,8 @@ #include "WM_toolsystem.h" #include "WM_types.h" +#include "ED_image.h" + #include "paint_intern.h" namespace blender::ed::sculpt_paint::image::ops::paint { @@ -316,7 +318,7 @@ static PaintOperation *texture_paint_init(bContext *C, wmOperator *op, const flo if ((brush->imagepaint_tool == PAINT_TOOL_FILL) && (brush->flag & BRUSH_USE_GRADIENT)) { pop->cursor = WM_paint_cursor_activate( - SPACE_TYPE_ANY, RGN_TYPE_ANY, image_paint_poll, gradient_draw_line, pop); + SPACE_TYPE_ANY, RGN_TYPE_ANY, ED_image_tools_paint_poll, gradient_draw_line, pop); } settings->imapaint.flag |= IMAGEPAINT_DRAWING; @@ -520,7 +522,7 @@ void PAINT_OT_image_paint(wmOperatorType *ot) ot->invoke = paint_invoke; ot->modal = paint_modal; ot->exec = paint_exec; - ot->poll = image_paint_poll; + ot->poll = ED_image_tools_paint_poll; ot->cancel = paint_cancel; /* flags */ diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 1303da71435..50480b8aef0 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -7,6 +7,7 @@ */ #include <float.h> +#include <limits.h> #include <math.h> #include <stdio.h> #include <string.h> @@ -35,12 +36,17 @@ #include "IMB_imbuf_types.h" #include "DNA_brush_types.h" +#include "DNA_customdata_types.h" +#include "DNA_defs.h" #include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_node_types.h" +#include "DNA_object_enums.h" #include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "BKE_attribute.h" #include "BKE_brush.h" #include "BKE_camera.h" #include "BKE_colorband.h" @@ -61,6 +67,8 @@ #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_screen.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" @@ -76,12 +84,18 @@ #include "GPU_capabilities.h" #include "GPU_init_exit.h" +#include "NOD_shader.h" + +#include "UI_interface.h" +#include "UI_resources.h" + #include "WM_api.h" #include "WM_types.h" #include "RNA_access.h" #include "RNA_define.h" #include "RNA_enum_types.h" +#include "RNA_types.h" #include "IMB_colormanagement.h" @@ -3118,7 +3132,7 @@ static void project_paint_face_init(const ProjPaintState *ps, } } - /* Is this UV visible from the view? - raytrace */ + /* Is this UV visible from the view? - ray-trace */ /* project_paint_PickFace is less complex, use for testing */ // if (project_paint_PickFace(ps, pixelScreenCo, w, &side) == tri_index) { if ((ps->do_occlude == false) || @@ -3208,7 +3222,7 @@ static void project_paint_face_init(const ProjPaintState *ps, float seam_subsection[4][2]; float fac1, fac2; - /* Pixelspace UVs. */ + /* Pixel-space UV's. */ float lt_puv[3][2]; lt_puv[0][0] = lt_uv_pxoffset[0][0] * ibuf->x; @@ -4068,7 +4082,7 @@ typedef struct { static void proj_paint_layer_clone_init(ProjPaintState *ps, ProjPaintLayerClone *layer_clone) { - MLoopUV *mloopuv_clone_base = NULL; + const MLoopUV *mloopuv_clone_base = NULL; /* use clone mtface? */ if (ps->do_layer_clone) { @@ -4478,7 +4492,8 @@ static void project_paint_begin(const bContext *C, } } - /* when using subsurf or multires, mface arrays are thrown away, we need to keep a copy */ + /* when using sub-surface or multi-resolution, + * mesh-data arrays are thrown away, we need to keep a copy. */ if (ps->is_shared_user == false) { proj_paint_state_cavity_init(ps); } @@ -6422,6 +6437,17 @@ static const EnumPropertyItem layer_type_items[] = { {0, NULL, 0, NULL, NULL}, }; +static Material *get_or_create_current_material(bContext *C, Object *ob) +{ + Material *ma = BKE_object_material_get(ob, ob->actcol); + if (!ma) { + Main *bmain = CTX_data_main(C); + ma = BKE_material_add(bmain, "Material"); + BKE_object_material_assign(bmain, ob, ma, ob->actcol, BKE_MAT_ASSIGN_USERPREF); + } + return ma; +} + static Image *proj_paint_image_create(wmOperator *op, Main *bmain, bool is_data) { Image *ima; @@ -6459,55 +6485,97 @@ static Image *proj_paint_image_create(wmOperator *op, Main *bmain, bool is_data) return ima; } -static void proj_paint_default_color(wmOperator *op, int type, Material *ma) +static CustomDataLayer *proj_paint_color_attribute_create(wmOperator *op, Object *ob) { - if (RNA_struct_property_is_set(op->ptr, "color")) { - return; + char name[MAX_NAME] = ""; + float color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + eAttrDomain domain = ATTR_DOMAIN_POINT; + eCustomDataType type = CD_PROP_COLOR; + + if (op) { + RNA_string_get(op->ptr, "name", name); + RNA_float_get_array(op->ptr, "color", color); + domain = (eAttrDomain)RNA_enum_get(op->ptr, "domain"); + type = (eCustomDataType)RNA_enum_get(op->ptr, "data_type"); } - bNode *in_node = ntreeFindType(ma->nodetree, SH_NODE_BSDF_PRINCIPLED); - if (in_node == NULL) { - return; + ID *id = (ID *)ob->data; + CustomDataLayer *layer = BKE_id_attribute_new(id, name, type, domain, op->reports); + + if (!layer) { + return NULL; } - float color[4]; + BKE_id_attributes_active_color_set(id, layer); - if (type >= LAYER_BASE_COLOR && type < LAYER_NORMAL) { - /* Copy color from node, so result is unchanged after assigning textures. */ - bNodeSocket *in_sock = nodeFindSocket(in_node, SOCK_IN, layer_type_items[type].name); + if (!BKE_id_attributes_render_color_get(id)) { + BKE_id_attributes_render_color_set(id, layer); + } - switch (in_sock->type) { - case SOCK_FLOAT: { - bNodeSocketValueFloat *socket_data = in_sock->default_value; - copy_v3_fl(color, socket_data->value); - color[3] = 1.0f; - break; + BKE_object_attributes_active_color_fill(ob, color, false); + + return layer; +} + +/** + * Get a default color for the paint slot layer from a material's Principled BSDF. + * + * \param layer_type: The layer type of the paint slot + * \param ma: The material to attempt using as the default color source. + * If this fails or \p ma is null, a default Principled BSDF is used instead. + */ +static void default_paint_slot_color_get(int layer_type, Material *ma, float color[4]) +{ + switch (layer_type) { + case LAYER_BASE_COLOR: + case LAYER_SPECULAR: + case LAYER_ROUGHNESS: + case LAYER_METALLIC: { + bNodeTree *ntree = NULL; + bNode *in_node = ma ? ntreeFindType(ma->nodetree, SH_NODE_BSDF_PRINCIPLED) : NULL; + if (!in_node) { + /* An existing material or Principled BSDF node could not be found. + * Copy default color values from a default Principled BSDF instead. */ + ntree = ntreeAddTree(NULL, "Temporary Shader Nodetree", ntreeType_Shader->idname); + in_node = nodeAddStaticNode(NULL, ntree, SH_NODE_BSDF_PRINCIPLED); } - case SOCK_VECTOR: - case SOCK_RGBA: { - bNodeSocketValueRGBA *socket_data = in_sock->default_value; - copy_v3_v3(color, socket_data->value); - color[3] = 1.0f; - break; + bNodeSocket *in_sock = nodeFindSocket(in_node, SOCK_IN, layer_type_items[layer_type].name); + switch (in_sock->type) { + case SOCK_FLOAT: { + bNodeSocketValueFloat *socket_data = in_sock->default_value; + copy_v3_fl(color, socket_data->value); + color[3] = 1.0f; + break; + } + case SOCK_VECTOR: + case SOCK_RGBA: { + bNodeSocketValueRGBA *socket_data = in_sock->default_value; + copy_v3_v3(color, socket_data->value); + color[3] = 1.0f; + break; + } + default: + BLI_assert_unreachable(); + rgba_float_args_set(color, 0.0f, 0.0f, 0.0f, 1.0f); + break; } - default: { - return; + /* Cleanup */ + if (ntree) { + ntreeFreeTree(ntree); + MEM_freeN(ntree); } + return; } + case LAYER_NORMAL: + /* Neutral tangent space normal map. */ + rgba_float_args_set(color, 0.5f, 0.5f, 1.0f, 1.0f); + break; + case LAYER_BUMP: + case LAYER_DISPLACEMENT: + /* Neutral displacement and bump map. */ + rgba_float_args_set(color, 0.5f, 0.5f, 0.5f, 1.0f); + break; } - else if (type == LAYER_NORMAL) { - /* Neutral tangent space normal map. */ - rgba_float_args_set(color, 0.5f, 0.5f, 1.0f, 1.0f); - } - else if (ELEM(type, LAYER_BUMP, LAYER_DISPLACEMENT)) { - /* Neutral displacement and bump map. */ - rgba_float_args_set(color, 0.5f, 0.5f, 0.5f, 1.0f); - } - else { - return; - } - - RNA_float_set_array(op->ptr, "color", color); } static bool proj_paint_add_slot(bContext *C, wmOperator *op) @@ -6516,19 +6584,20 @@ static bool proj_paint_add_slot(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); Material *ma; Image *ima = NULL; + CustomDataLayer *layer = NULL; if (!ob) { return false; } - ma = BKE_object_material_get(ob, ob->actcol); + ma = get_or_create_current_material(C, ob); if (ma) { Main *bmain = CTX_data_main(C); int type = RNA_enum_get(op->ptr, "type"); bool is_data = (type > LAYER_BASE_COLOR); - bNode *imanode; + bNode *new_node; bNodeTree *ntree = ma->nodetree; if (!ntree) { @@ -6538,17 +6607,36 @@ static bool proj_paint_add_slot(bContext *C, wmOperator *op) ma->use_nodes = true; - /* try to add an image node */ - imanode = nodeAddStaticNode(C, ntree, SH_NODE_TEX_IMAGE); - - ima = proj_paint_image_create(op, bmain, is_data); - imanode->id = &ima->id; - - nodeSetActive(ntree, imanode); + const ePaintCanvasSource slot_type = ob->mode == OB_MODE_SCULPT ? + (ePaintCanvasSource)RNA_enum_get(op->ptr, + "slot_type") : + PAINT_CANVAS_SOURCE_IMAGE; + + /* Create a new node. */ + switch (slot_type) { + case PAINT_CANVAS_SOURCE_IMAGE: { + new_node = nodeAddStaticNode(C, ntree, SH_NODE_TEX_IMAGE); + ima = proj_paint_image_create(op, bmain, is_data); + new_node->id = &ima->id; + break; + } + case PAINT_CANVAS_SOURCE_COLOR_ATTRIBUTE: { + new_node = nodeAddStaticNode(C, ntree, SH_NODE_ATTRIBUTE); + if ((layer = proj_paint_color_attribute_create(op, ob))) { + BLI_strncpy_utf8( + ((NodeShaderAttribute *)new_node->storage)->name, layer->name, MAX_NAME); + } + break; + } + case PAINT_CANVAS_SOURCE_MATERIAL: + BLI_assert_unreachable(); + return false; + } + nodeSetActive(ntree, new_node); /* Connect to first available principled BSDF node. */ bNode *in_node = ntreeFindType(ntree, SH_NODE_BSDF_PRINCIPLED); - bNode *out_node = imanode; + bNode *out_node = new_node; if (in_node != NULL) { bNodeSocket *out_sock = nodeFindSocket(out_node, SOCK_OUT, "Color"); @@ -6611,6 +6699,11 @@ static bool proj_paint_add_slot(bContext *C, wmOperator *op) BKE_image_signal(bmain, ima, NULL, IMA_SIGNAL_USER_NEW_IMAGE); WM_event_add_notifier(C, NC_IMAGE | NA_ADDED, ima); } + if (layer) { + BKE_texpaint_slot_refresh_cache(scene, ma, ob); + DEG_id_tag_update(ob->data, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, ob->data); + } DEG_id_tag_update(&ntree->id, 0); DEG_id_tag_update(&ma->id, ID_RECALC_SHADING); @@ -6632,25 +6725,8 @@ static int get_texture_layer_type(wmOperator *op, const char *prop_name) return type; } -static Material *get_or_create_current_material(bContext *C, Object *ob) -{ - Material *ma = BKE_object_material_get(ob, ob->actcol); - if (!ma) { - Main *bmain = CTX_data_main(C); - ma = BKE_material_add(bmain, "Material"); - BKE_object_material_assign(bmain, ob, ma, ob->actcol, BKE_MAT_ASSIGN_USERPREF); - } - return ma; -} - static int texture_paint_add_texture_paint_slot_exec(bContext *C, wmOperator *op) { - Object *ob = ED_object_active_context(C); - Material *ma = get_or_create_current_material(C, ob); - - int type = get_texture_layer_type(op, "type"); - proj_paint_default_color(op, type, ma); - if (proj_paint_add_slot(C, op)) { return OPERATOR_FINISHED; } @@ -6671,20 +6747,62 @@ static int texture_paint_add_texture_paint_slot_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { - /* Get material and default color to display in the popup. */ Object *ob = ED_object_active_context(C); - Material *ma = get_or_create_current_material(C, ob); + Material *ma = BKE_object_material_get(ob, ob->actcol); int type = get_texture_layer_type(op, "type"); - proj_paint_default_color(op, type, ma); + /* Set default name. */ char imagename[MAX_ID_NAME - 2]; get_default_texture_layer_name_for_object(ob, type, (char *)&imagename, sizeof(imagename)); RNA_string_set(op->ptr, "name", imagename); + /* Set default color. Copy the color from nodes, so it matches the existing material. */ + float color[4]; + default_paint_slot_color_get(type, ma, color); + RNA_float_set_array(op->ptr, "color", color); + return WM_operator_props_dialog_popup(C, op, 300); } +static void texture_paint_add_texture_paint_slot_ui(bContext *C, wmOperator *op) +{ + uiLayout *layout = op->layout; + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + Object *ob = ED_object_active_context(C); + ePaintCanvasSource slot_type = PAINT_CANVAS_SOURCE_IMAGE; + + if (ob->mode == OB_MODE_SCULPT) { + slot_type = (ePaintCanvasSource)RNA_enum_get(op->ptr, "slot_type"); + uiItemR(layout, op->ptr, "slot_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + } + + uiItemR(layout, op->ptr, "name", 0, NULL, ICON_NONE); + + switch (slot_type) { + case PAINT_CANVAS_SOURCE_IMAGE: { + uiLayout *col = uiLayoutColumn(layout, true); + uiItemR(col, op->ptr, "width", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "height", 0, NULL, ICON_NONE); + + uiItemR(layout, op->ptr, "alpha", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "generated_type", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "float", 0, NULL, ICON_NONE); + break; + } + case PAINT_CANVAS_SOURCE_COLOR_ATTRIBUTE: + uiItemR(layout, op->ptr, "domain", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "data_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + break; + case PAINT_CANVAS_SOURCE_MATERIAL: + BLI_assert_unreachable(); + break; + } + + uiItemR(layout, op->ptr, "color", 0, NULL, ICON_NONE); +} + #define IMA_DEF_NAME N_("Untitled") void PAINT_OT_add_texture_paint_slot(wmOperatorType *ot) @@ -6692,40 +6810,92 @@ void PAINT_OT_add_texture_paint_slot(wmOperatorType *ot) PropertyRNA *prop; static float default_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + static const EnumPropertyItem slot_type_items[3] = { + {PAINT_CANVAS_SOURCE_IMAGE, "IMAGE", 0, "Image", ""}, + {PAINT_CANVAS_SOURCE_COLOR_ATTRIBUTE, "COLOR_ATTRIBUTE", 0, "Color Attribute", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem domain_items[3] = { + {ATTR_DOMAIN_POINT, "POINT", 0, "Vertex", ""}, + {ATTR_DOMAIN_CORNER, "CORNER", 0, "Face Corner", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem attribute_type_items[3] = { + {CD_PROP_COLOR, "COLOR", 0, "Color", ""}, + {CD_PROP_BYTE_COLOR, "BYTE_COLOR", 0, "Byte Color", ""}, + {0, NULL, 0, NULL, NULL}, + }; + /* identifiers */ - ot->name = "Add Texture Paint Slot"; - ot->description = "Add a texture paint slot"; + ot->name = "Add Paint Slot"; + ot->description = "Add a paint slot"; ot->idname = "PAINT_OT_add_texture_paint_slot"; /* api callbacks */ ot->invoke = texture_paint_add_texture_paint_slot_invoke; ot->exec = texture_paint_add_texture_paint_slot_exec; ot->poll = ED_operator_object_active_editable_mesh; + ot->ui = texture_paint_add_texture_paint_slot_ui; /* flags */ ot->flag = OPTYPE_UNDO; - /* properties */ - prop = RNA_def_enum(ot->srna, "type", layer_type_items, 0, "Type", "Merge method to use"); + /* Shared Properties */ + prop = RNA_def_enum(ot->srna, + "type", + layer_type_items, + 0, + "Material Layer Type", + "Material layer type of new paint slot"); RNA_def_property_flag(prop, PROP_HIDDEN); - RNA_def_string(ot->srna, "name", IMA_DEF_NAME, MAX_ID_NAME - 2, "Name", "Image data-block name"); - prop = RNA_def_int(ot->srna, "width", 1024, 1, INT_MAX, "Width", "Image width", 1, 16384); - RNA_def_property_subtype(prop, PROP_PIXEL); - prop = RNA_def_int(ot->srna, "height", 1024, 1, INT_MAX, "Height", "Image height", 1, 16384); - RNA_def_property_subtype(prop, PROP_PIXEL); + + prop = RNA_def_enum( + ot->srna, "slot_type", slot_type_items, 0, "Slot Type", "Type of new paint slot"); + + prop = RNA_def_string( + ot->srna, "name", IMA_DEF_NAME, MAX_NAME, "Name", "Name for new paint slot source"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_float_color( ot->srna, "color", 4, NULL, 0.0f, FLT_MAX, "Color", "Default fill color", 0.0f, 1.0f); RNA_def_property_subtype(prop, PROP_COLOR_GAMMA); RNA_def_property_float_array_default(prop, default_color); + + /* Image Properties */ + prop = RNA_def_int(ot->srna, "width", 1024, 1, INT_MAX, "Width", "Image width", 1, 16384); + RNA_def_property_subtype(prop, PROP_PIXEL); + + prop = RNA_def_int(ot->srna, "height", 1024, 1, INT_MAX, "Height", "Image height", 1, 16384); + RNA_def_property_subtype(prop, PROP_PIXEL); + RNA_def_boolean(ot->srna, "alpha", true, "Alpha", "Create an image with an alpha channel"); + RNA_def_enum(ot->srna, "generated_type", rna_enum_image_generated_type_items, IMA_GENTYPE_BLANK, "Generated Type", "Fill the image with a grid for UV map testing"); + RNA_def_boolean( ot->srna, "float", 0, "32-bit Float", "Create image with 32-bit floating-point bit depth"); + + /* Color Attribute Properties */ + RNA_def_enum(ot->srna, + "domain", + domain_items, + ATTR_DOMAIN_POINT, + "Domain", + "Type of element that attribute is stored on"); + + RNA_def_enum(ot->srna, + "data_type", + attribute_type_items, + CD_PROP_COLOR, + "Data Type", + "Type of data stored in attribute"); } static int add_simple_uvs_exec(bContext *C, wmOperator *UNUSED(op)) diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index 187f793eefe..3f4e660b229 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -98,7 +98,6 @@ void *paint_stroke_mode_data(struct PaintStroke *stroke); float paint_stroke_distance_get(struct PaintStroke *stroke); void paint_stroke_set_mode_data(struct PaintStroke *stroke, void *mode_data); bool PAINT_brush_tool_poll(struct bContext *C); -void paint_cursor_start(struct Paint *p, bool (*poll)(struct bContext *C)); /** * Delete overlay cursor textures to preserve memory and invalidate all overlay flags. */ diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c index fb5e76b578f..89bbf2a3c92 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -1584,9 +1584,9 @@ static int sculpt_trim_gesture_box_invoke(bContext *C, wmOperator *op, const wmE SculptSession *ss = ob->sculpt; SculptCursorGeometryInfo sgi; - float mouse[2] = {event->mval[0], event->mval[1]}; + const float mval_fl[2] = {UNPACK2(event->mval)}; SCULPT_vertex_random_access_ensure(ss); - ss->gesture_initial_hit = SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false); + ss->gesture_initial_hit = SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false); if (ss->gesture_initial_hit) { copy_v3_v3(ss->gesture_initial_location, sgi.location); copy_v3_v3(ss->gesture_initial_normal, sgi.normal); @@ -1625,9 +1625,9 @@ static int sculpt_trim_gesture_lasso_invoke(bContext *C, wmOperator *op, const w SculptSession *ss = ob->sculpt; SculptCursorGeometryInfo sgi; - float mouse[2] = {event->mval[0], event->mval[1]}; + const float mval_fl[2] = {UNPACK2(event->mval)}; SCULPT_vertex_random_access_ensure(ss); - ss->gesture_initial_hit = SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false); + ss->gesture_initial_hit = SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false); if (ss->gesture_initial_hit) { copy_v3_v3(ss->gesture_initial_location, sgi.location); copy_v3_v3(ss->gesture_initial_normal, sgi.normal); diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index 926a564184a..0f2b02ed3ab 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.c +++ b/source/blender/editors/sculpt_paint/paint_ops.c @@ -82,11 +82,86 @@ static void BRUSH_OT_add(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +static eGPBrush_Presets gpencil_get_brush_preset_from_tool(bToolRef *tool, + enum eContextObjectMode mode) +{ + switch (mode) { + case CTX_MODE_PAINT_GPENCIL: { + if (STREQ(tool->runtime->data_block, "DRAW")) { + return GP_BRUSH_PRESET_PENCIL; + } + else if (STREQ(tool->runtime->data_block, "FILL")) { + return GP_BRUSH_PRESET_FILL_AREA; + } + else if (STREQ(tool->runtime->data_block, "ERASE")) { + return GP_BRUSH_PRESET_ERASER_SOFT; + } + else if (STREQ(tool->runtime->data_block, "TINT")) { + return GP_BRUSH_PRESET_TINT; + } + break; + } + case CTX_MODE_SCULPT_GPENCIL: { + if (STREQ(tool->runtime->data_block, "SMOOTH")) { + return GP_BRUSH_PRESET_SMOOTH_STROKE; + } + else if (STREQ(tool->runtime->data_block, "STRENGTH")) { + return GP_BRUSH_PRESET_STRENGTH_STROKE; + } + else if (STREQ(tool->runtime->data_block, "THICKNESS")) { + return GP_BRUSH_PRESET_THICKNESS_STROKE; + } + else if (STREQ(tool->runtime->data_block, "GRAB")) { + return GP_BRUSH_PRESET_GRAB_STROKE; + } + else if (STREQ(tool->runtime->data_block, "PUSH")) { + return GP_BRUSH_PRESET_PUSH_STROKE; + } + else if (STREQ(tool->runtime->data_block, "TWIST")) { + return GP_BRUSH_PRESET_TWIST_STROKE; + } + else if (STREQ(tool->runtime->data_block, "PINCH")) { + return GP_BRUSH_PRESET_PINCH_STROKE; + } + else if (STREQ(tool->runtime->data_block, "RANDOMIZE")) { + return GP_BRUSH_PRESET_RANDOMIZE_STROKE; + } + else if (STREQ(tool->runtime->data_block, "CLONE")) { + return GP_BRUSH_PRESET_CLONE_STROKE; + } + break; + } + case CTX_MODE_WEIGHT_GPENCIL: { + return GP_BRUSH_PRESET_DRAW_WEIGHT; + } + case CTX_MODE_VERTEX_GPENCIL: { + if (STREQ(tool->runtime->data_block, "DRAW")) { + return GP_BRUSH_PRESET_VERTEX_DRAW; + } + else if (STREQ(tool->runtime->data_block, "BLUR")) { + return GP_BRUSH_PRESET_VERTEX_BLUR; + } + else if (STREQ(tool->runtime->data_block, "AVERAGE")) { + return GP_BRUSH_PRESET_VERTEX_AVERAGE; + } + else if (STREQ(tool->runtime->data_block, "SMEAR")) { + return GP_BRUSH_PRESET_VERTEX_SMEAR; + } + else if (STREQ(tool->runtime->data_block, "REPLACE")) { + return GP_BRUSH_PRESET_VERTEX_REPLACE; + } + break; + } + default: + return GP_BRUSH_PRESET_UNKNOWN; + break; + } + return GP_BRUSH_PRESET_UNKNOWN; +} + static int brush_add_gpencil_exec(bContext *C, wmOperator *UNUSED(op)) { - // int type = RNA_enum_get(op->ptr, "type"); - ToolSettings *ts = CTX_data_tool_settings(C); - Paint *paint = &ts->gp_paint->paint; + Paint *paint = BKE_paint_get_active_from_context(C); Brush *br = BKE_paint_brush(paint); Main *bmain = CTX_data_main(C); @@ -94,15 +169,70 @@ static int brush_add_gpencil_exec(bContext *C, wmOperator *UNUSED(op)) br = (Brush *)BKE_id_copy(bmain, &br->id); } else { - br = BKE_brush_add(bmain, "Brush", OB_MODE_PAINT_GPENCIL); + /* Get the active tool to determine what type of brush is active. */ + bScreen *screen = CTX_wm_screen(C); + if (screen == NULL) { + return OPERATOR_CANCELLED; + } - /* Init grease pencil specific data. */ - BKE_brush_init_gpencil_settings(br); - } + bToolRef *tool = NULL; + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + if (area->spacetype == SPACE_VIEW3D) { + /* Check the current tool is a brush. */ + bToolRef *tref = area->runtime.tool; + if (tref && tref->runtime && tref->runtime->data_block[0]) { + tool = tref; + break; + } + } + } - id_us_min(&br->id); /* fake user only */ + if (tool == NULL) { + return OPERATOR_CANCELLED; + } - BKE_paint_brush_set(paint, br); + /* Get Brush mode base on context mode. */ + const enum eContextObjectMode mode = CTX_data_mode_enum(C); + eObjectMode obmode = OB_MODE_PAINT_GPENCIL; + switch (mode) { + case CTX_MODE_PAINT_GPENCIL: + obmode = OB_MODE_PAINT_GPENCIL; + break; + case CTX_MODE_SCULPT_GPENCIL: + obmode = OB_MODE_SCULPT_GPENCIL; + break; + case CTX_MODE_WEIGHT_GPENCIL: + obmode = OB_MODE_WEIGHT_GPENCIL; + break; + case CTX_MODE_VERTEX_GPENCIL: + obmode = OB_MODE_VERTEX_GPENCIL; + break; + default: + return OPERATOR_CANCELLED; + break; + } + + /* Get brush preset using the actual tool. */ + eGPBrush_Presets preset = gpencil_get_brush_preset_from_tool(tool, mode); + + /* Capitalize Brush name first letter using the tool name. */ + char name[64]; + BLI_strncpy(name, tool->runtime->data_block, sizeof(name)); + BLI_str_tolower_ascii(name, sizeof(name)); + name[0] = BLI_toupper_ascii(name[0]); + + /* Create the brush and assign default values. */ + br = BKE_brush_add(bmain, name, obmode); + if (br) { + BKE_brush_init_gpencil_settings(br); + BKE_gpencil_brush_preset_set(bmain, br, preset); + } + } + + if (br) { + id_us_min(&br->id); /* fake user only */ + BKE_paint_brush_set(paint, br); + } return OPERATOR_FINISHED; } diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index d2005473512..63e6dc7e965 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -506,7 +506,7 @@ static bool paint_stroke_use_jitter(ePaintMode mode, Brush *brush, bool invert) /* Put the location of the next stroke dot into the stroke RNA and apply it to the mesh */ static void paint_brush_stroke_add_step( - bContext *C, wmOperator *op, PaintStroke *stroke, const float mouse_in[2], float pressure) + bContext *C, wmOperator *op, PaintStroke *stroke, const float mval[2], float pressure) { Scene *scene = CTX_data_scene(C); Paint *paint = BKE_paint_get_active_from_context(C); @@ -546,7 +546,7 @@ static void paint_brush_stroke_add_step( /* copy last position -before- jittering, or space fill code * will create too many dabs */ - copy_v2_v2(stroke->last_mouse_position, mouse_in); + copy_v2_v2(stroke->last_mouse_position, mval); stroke->last_pressure = pressure; if (paint_stroke_use_scene_spacing(brush, mode)) { @@ -562,24 +562,24 @@ static void paint_brush_stroke_add_step( factor *= pressure; } - BKE_brush_jitter_pos(scene, brush, mouse_in, mouse_out); + BKE_brush_jitter_pos(scene, brush, mval, mouse_out); /* XXX: meh, this is round about because * BKE_brush_jitter_pos isn't written in the best way to * be reused here */ if (factor != 1.0f) { - sub_v2_v2v2(delta, mouse_out, mouse_in); + sub_v2_v2v2(delta, mouse_out, mval); mul_v2_fl(delta, factor); - add_v2_v2v2(mouse_out, mouse_in, delta); + add_v2_v2v2(mouse_out, mval, delta); } } else { - copy_v2_v2(mouse_out, mouse_in); + copy_v2_v2(mouse_out, mval); } bool is_location_is_set; ups->last_hit = paint_brush_update( - C, brush, mode, stroke, mouse_in, mouse_out, pressure, location, &is_location_is_set); + C, brush, mode, stroke, mval, mouse_out, pressure, location, &is_location_is_set); if (is_location_is_set) { copy_v3_v3(ups->last_location, location); } @@ -605,7 +605,7 @@ static void paint_brush_stroke_add_step( /* Mouse coordinates modified by the stroke type options. */ RNA_float_set_array(&itemptr, "mouse", mouse_out); /* Original mouse coordinates. */ - RNA_float_set_array(&itemptr, "mouse_event", mouse_in); + RNA_float_set_array(&itemptr, "mouse_event", mval); RNA_boolean_set(&itemptr, "pen_flip", stroke->pen_flip); RNA_float_set(&itemptr, "pressure", pressure); RNA_float_set(&itemptr, "x_tilt", stroke->x_tilt); @@ -912,8 +912,13 @@ PaintStroke *paint_stroke_new(bContext *C, rv3d->rflag |= RV3D_PAINTING; } - zero_v3(ups->average_stroke_accum); - ups->average_stroke_counter = 0; + /* Preserve location from last stroke while applying and resetting + * ups->average_stroke_counter to 1. + */ + if (ups->average_stroke_counter) { + mul_v3_fl(ups->average_stroke_accum, 1.0f / (float)ups->average_stroke_counter); + ups->average_stroke_counter = 1; + } /* initialize here to avoid initialization conflict with threaded strokes */ BKE_curvemapping_init(br->curve); @@ -923,6 +928,8 @@ PaintStroke *paint_stroke_new(bContext *C, BKE_paint_set_overlay_override(br->overlay_flags); + ups->start_pixel_radius = BKE_brush_size_get(CTX_data_scene(C), br); + return stroke; } diff --git a/source/blender/editors/sculpt_paint/paint_vertex.cc b/source/blender/editors/sculpt_paint/paint_vertex.cc index 16b22775b9e..67b60acc667 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.cc +++ b/source/blender/editors/sculpt_paint/paint_vertex.cc @@ -56,6 +56,7 @@ #include "WM_types.h" #include "ED_armature.h" +#include "ED_image.h" #include "ED_mesh.h" #include "ED_object.h" #include "ED_screen.h" @@ -139,8 +140,8 @@ struct NormalAnglePrecalc { /* Returns number of elements. */ static int get_vcol_elements(Mesh *me, size_t *r_elem_size) { - CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id); - AttributeDomain domain = BKE_id_attribute_domain(&me->id, layer); + const CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id); + const eAttrDomain domain = BKE_id_attribute_domain(&me->id, layer); if (r_elem_size) { *r_elem_size = layer->type == CD_PROP_COLOR ? sizeof(float) * 4ULL : 4ULL; @@ -217,7 +218,7 @@ static MDeformVert *defweight_prev_init(MDeformVert *dvert_prev, MDeformVert *dvert_curr, int index) { - MDeformVert *dv_curr = &dvert_curr[index]; + const MDeformVert *dv_curr = &dvert_curr[index]; MDeformVert *dv_prev = &dvert_prev[index]; if (dv_prev->flag == 1) { dv_prev->flag = 0; @@ -233,7 +234,7 @@ static bool vertex_paint_use_fast_update_check(Object *ob) const Mesh *me_eval = BKE_object_get_evaluated_mesh(ob); if (me_eval != nullptr) { - Mesh *me = BKE_mesh_from_object(ob); + const Mesh *me = BKE_mesh_from_object(ob); if (me && me->mloopcol) { return (me->mloopcol == CustomData_get_layer(&me_eval->ldata, CD_PROP_BYTE_COLOR)); } @@ -252,13 +253,14 @@ static void paint_last_stroke_update(Scene *scene, const float location[3]) bool vertex_paint_mode_poll(bContext *C) { - Object *ob = CTX_data_active_object(C); + const Object *ob = CTX_data_active_object(C); - if (!(ob && ob->mode == OB_MODE_VERTEX_PAINT && ((Mesh *)ob->data)->totpoly)) { + if (!(ob && ob->mode == OB_MODE_VERTEX_PAINT && ((const Mesh *)ob->data)->totpoly)) { return false; } - CustomDataLayer *layer = BKE_id_attributes_active_color_get((ID *)ob->data); + const CustomDataLayer *layer = BKE_id_attributes_active_color_get( + static_cast<const ID *>(ob->data)); return layer != nullptr; } @@ -291,15 +293,15 @@ bool vertex_paint_poll_ignore_tool(bContext *C) bool weight_paint_mode_poll(bContext *C) { - Object *ob = CTX_data_active_object(C); + const Object *ob = CTX_data_active_object(C); - return ob && ob->mode == OB_MODE_WEIGHT_PAINT && ((Mesh *)ob->data)->totpoly; + return ob && ob->mode == OB_MODE_WEIGHT_PAINT && ((const Mesh *)ob->data)->totpoly; } static bool weight_paint_poll_ex(bContext *C, bool check_tool) { - Object *ob = CTX_data_active_object(C); - ScrArea *area; + const Object *ob = CTX_data_active_object(C); + const ScrArea *area; if ((ob != nullptr) && (ob->mode & OB_MODE_WEIGHT_PAINT) && (BKE_paint_brush(&CTX_data_tool_settings(C)->wpaint->paint) != nullptr) && @@ -324,15 +326,14 @@ bool weight_paint_poll_ignore_tool(bContext *C) return weight_paint_poll_ex(C, false); } -template<typename Color, typename Traits, AttributeDomain domain> +template<typename Color, typename Traits, eAttrDomain domain> static Color vpaint_get_current_col(Scene *scene, VPaint *vp, bool secondary) { - Brush *brush = BKE_paint_brush(&vp->paint); + const Brush *brush = BKE_paint_brush_for_read(&vp->paint); float color[4]; const float *brush_color = secondary ? BKE_brush_secondary_color_get(scene, brush) : BKE_brush_color_get(scene, brush); - copy_v3_v3(color, brush_color); - IMB_colormanagement_srgb_to_scene_linear_v3(color); + IMB_colormanagement_srgb_to_scene_linear_v3(color, brush_color); color[3] = 1.0f; /* alpha isn't used, could even be removed to speedup paint a little */ @@ -361,7 +362,7 @@ static Color vpaint_blend(const VPaint *vp, const Brush *brush = vp->paint.brush; const IMB_BlendMode blend = (IMB_BlendMode)brush->blend; - Color color_blend = BLI_mix_colors<Color, Traits>(blend, color_curr, color_paint, alpha); + const Color color_blend = BLI_mix_colors<Color, Traits>(blend, color_curr, color_paint, alpha); /* If no accumulate, clip color adding with `color_orig` & `color_test`. */ if (!brush_use_accumulate(vp)) { @@ -1322,7 +1323,7 @@ static void ed_vwpaintmode_enter_generic( BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->vpaint); Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode); - paint_cursor_start(paint, vertex_paint_poll); + ED_paint_cursor_start(paint, vertex_paint_poll); BKE_paint_init(bmain, scene, paint_mode, PAINT_CURSOR_VERTEX_PAINT); } else if (mode_flag == OB_MODE_WEIGHT_PAINT) { @@ -1330,7 +1331,7 @@ static void ed_vwpaintmode_enter_generic( BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->wpaint); Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode); - paint_cursor_start(paint, weight_paint_poll); + ED_paint_cursor_start(paint, weight_paint_poll); BKE_paint_init(bmain, scene, paint_mode, PAINT_CURSOR_WEIGHT_PAINT); /* weight paint specific */ @@ -1607,10 +1608,10 @@ static void smooth_brush_toggle_off(const bContext *C, Paint *paint, StrokeCache /* Initialize the stroke cache invariants from operator properties */ static void vwpaint_update_cache_invariants( - bContext *C, VPaint *vp, SculptSession *ss, wmOperator *op, const float mouse[2]) + bContext *C, VPaint *vp, SculptSession *ss, wmOperator *op, const float mval[2]) { StrokeCache *cache; - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; ViewContext *vc = paint_stroke_view_context((PaintStroke *)op->customdata); Object *ob = CTX_data_active_object(C); @@ -1628,8 +1629,8 @@ static void vwpaint_update_cache_invariants( } /* Initial mouse location */ - if (mouse) { - copy_v2_v2(cache->initial_mouse, mouse); + if (mval) { + copy_v2_v2(cache->initial_mouse, mval); } else { zero_v2(cache->initial_mouse); @@ -1652,7 +1653,7 @@ static void vwpaint_update_cache_invariants( } copy_v2_v2(cache->mouse, cache->initial_mouse); - Brush *brush = vp->paint.brush; + const Brush *brush = vp->paint.brush; /* Truly temporary data that isn't stored in properties */ cache->vc = vc; cache->brush = brush; @@ -2824,11 +2825,11 @@ void PAINT_OT_vertex_paint_toggle(wmOperatorType *ot) struct VPaintDataBase { ViewContext vc; - AttributeDomain domain; - CustomDataType type; + eAttrDomain domain; + eCustomDataType type; }; -template<typename Color, typename Traits, AttributeDomain domain> +template<typename Color, typename Traits, eAttrDomain domain> struct VPaintData : public VPaintDataBase { NormalAnglePrecalc normal_angle_precalc; @@ -2856,7 +2857,7 @@ struct VPaintData : public VPaintDataBase { } smear; }; -template<typename Color, typename Traits, AttributeDomain domain> +template<typename Color, typename Traits, eAttrDomain domain> static void *vpaint_init_vpaint(bContext *C, wmOperator *op, Scene *scene, @@ -2950,7 +2951,7 @@ static bool vpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo return false; } - AttributeDomain domain = BKE_id_attribute_domain(&me->id, layer); + eAttrDomain domain = BKE_id_attribute_domain(&me->id, layer); void *vpd = nullptr; if (domain == ATTR_DOMAIN_POINT) { @@ -3273,7 +3274,7 @@ static void do_vpaint_brush_blur_verts(bContext *C, }); } -template<typename Color = ColorPaint4b, typename Traits, AttributeDomain domain> +template<typename Color = ColorPaint4b, typename Traits, eAttrDomain domain> static void do_vpaint_brush_smear(bContext *C, Sculpt *UNUSED(sd), VPaint *vp, @@ -3452,7 +3453,7 @@ static void do_vpaint_brush_smear(bContext *C, }); } -template<typename Color, typename Traits, AttributeDomain domain> +template<typename Color, typename Traits, eAttrDomain domain> static void calculate_average_color(VPaintData<Color, Traits, domain> *vpd, Object *ob, Mesh *me, @@ -3542,7 +3543,7 @@ static void calculate_average_color(VPaintData<Color, Traits, domain> *vpd, } } -template<typename Color, typename Traits, AttributeDomain domain> +template<typename Color, typename Traits, eAttrDomain domain> static float paint_and_tex_color_alpha(VPaint *vp, VPaintData<Color, Traits, domain> *vpd, const float v_co[3], @@ -3560,7 +3561,7 @@ static float paint_and_tex_color_alpha(VPaint *vp, return rgba[3]; } -template<typename Color, typename Traits, AttributeDomain domain> +template<typename Color, typename Traits, eAttrDomain domain> static void vpaint_do_draw(bContext *C, Sculpt *UNUSED(sd), VPaint *vp, @@ -3700,7 +3701,7 @@ static void vpaint_do_draw(bContext *C, }); } -template<typename Color, typename Traits, AttributeDomain domain> +template<typename Color, typename Traits, eAttrDomain domain> static void vpaint_do_blur(bContext *C, Sculpt *sd, VPaint *vp, @@ -3719,7 +3720,7 @@ static void vpaint_do_blur(bContext *C, } } -template<typename Color, typename Traits, AttributeDomain domain> +template<typename Color, typename Traits, eAttrDomain domain> static void vpaint_paint_leaves(bContext *C, Sculpt *sd, VPaint *vp, @@ -3755,7 +3756,7 @@ static void vpaint_paint_leaves(bContext *C, } } -template<typename Color, typename Traits, AttributeDomain domain> +template<typename Color, typename Traits, eAttrDomain domain> static void vpaint_do_paint(bContext *C, Sculpt *sd, VPaint *vp, @@ -3786,7 +3787,7 @@ static void vpaint_do_paint(bContext *C, } } -template<typename Color, typename Traits, AttributeDomain domain> +template<typename Color, typename Traits, eAttrDomain domain> static void vpaint_do_radial_symmetry(bContext *C, Sculpt *sd, VPaint *vp, @@ -3805,7 +3806,7 @@ static void vpaint_do_radial_symmetry(bContext *C, /* near duplicate of: sculpt.c's, * 'do_symmetrical_brush_actions' and 'wpaint_do_symmetrical_brush_actions'. */ -template<typename Color, typename Traits, AttributeDomain domain> +template<typename Color, typename Traits, eAttrDomain domain> static void vpaint_do_symmetrical_brush_actions( bContext *C, Sculpt *sd, VPaint *vp, VPaintData<Color, Traits, domain> *vpd, Object *ob) { @@ -3852,7 +3853,7 @@ static void vpaint_do_symmetrical_brush_actions( cache->is_last_valid = true; } -template<typename Color, typename Traits, AttributeDomain domain> +template<typename Color, typename Traits, eAttrDomain domain> static void vpaint_stroke_update_step_intern(bContext *C, PaintStroke *stroke, PointerRNA *itemptr) { Scene *scene = CTX_data_scene(C); @@ -3940,7 +3941,7 @@ static void vpaint_stroke_update_step(bContext *C, } } -template<typename Color, typename Traits, AttributeDomain domain> +template<typename Color, typename Traits, eAttrDomain domain> static void vpaint_free_vpaintdata(Object *UNUSED(ob), void *_vpd) { VPaintData<Color, Traits, domain> *vpd = static_cast<VPaintData<Color, Traits, domain> *>(_vpd); @@ -4097,7 +4098,7 @@ void PAINT_OT_vertex_paint(wmOperatorType *ot) /** \name Set Vertex Colors Operator * \{ */ -template<typename Color, typename Traits, AttributeDomain domain> +template<typename Color, typename Traits, eAttrDomain domain> static bool vertex_color_set(Object *ob, ColorPaint4f paintcol_in, Color *color_layer) { Mesh *me; @@ -4165,7 +4166,7 @@ static bool paint_object_attributes_active_color_fill_ex(Object *ob, me->editflag &= ~ME_EDIT_PAINT_FACE_SEL; me->editflag &= ~ME_EDIT_PAINT_VERT_SEL; } - AttributeDomain domain = BKE_id_attribute_domain(&me->id, layer); + eAttrDomain domain = BKE_id_attribute_domain(&me->id, layer); bool ok = false; if (domain == ATTR_DOMAIN_POINT) { if (layer->type == CD_PROP_COLOR) { diff --git a/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.cc index a2e1adff50a..6a47aceb2b0 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c +++ b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.cc @@ -11,6 +11,7 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "BLI_array.hh" #include "BLI_math_base.h" #include "BLI_math_color.h" @@ -30,6 +31,8 @@ #include "paint_intern.h" /* own include */ +using blender::Array; + /* -------------------------------------------------------------------- */ /** \name Internal Utility Functions * \{ */ @@ -45,7 +48,7 @@ static bool vertex_weight_paint_mode_poll(bContext *C) static void tag_object_after_update(Object *object) { BLI_assert(object->type == OB_MESH); - Mesh *mesh = object->data; + Mesh *mesh = static_cast<Mesh *>(object->data); DEG_id_tag_update(&mesh->id, ID_RECALC_COPY_ON_WRITE); /* NOTE: Original mesh is used for display, so tag it directly here. */ BKE_mesh_batch_cache_dirty_tag(mesh, BKE_MESH_BATCH_DIRTY_ALL); @@ -63,7 +66,8 @@ static bool vertex_paint_from_weight(Object *ob) const MPoly *mp; int vgroup_active; - if (((me = BKE_mesh_from_object(ob)) == NULL || (ED_mesh_color_ensure(me, NULL)) == false)) { + if (((me = BKE_mesh_from_object(ob)) == nullptr || + (ED_mesh_color_ensure(me, nullptr)) == false)) { return false; } @@ -128,14 +132,13 @@ static void vertex_color_smooth_looptag(Mesh *me, const bool *mlooptag) { const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; const MPoly *mp; - int(*scol)[4]; bool has_shared = false; - if (me->mloopcol == NULL || me->totvert == 0 || me->totpoly == 0) { + if (me->mloopcol == nullptr || me->totvert == 0 || me->totpoly == 0) { return; } - scol = MEM_callocN(sizeof(int) * me->totvert * 5, "scol"); + int(*scol)[4] = static_cast<int(*)[4]>(MEM_callocN(sizeof(int) * me->totvert * 5, "scol")); int i; for (i = 0, mp = me->mpoly; i < me->totpoly; i++, mp++) { @@ -185,16 +188,15 @@ static bool vertex_color_smooth(Object *ob) const MPoly *mp; int i, j; - bool *mlooptag; - - if (((me = BKE_mesh_from_object(ob)) == NULL) || (ED_mesh_color_ensure(me, NULL) == false)) { + if (((me = BKE_mesh_from_object(ob)) == nullptr) || + (ED_mesh_color_ensure(me, nullptr) == false)) { return false; } const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; - mlooptag = MEM_callocN(sizeof(bool) * me->totloop, "VPaintData mlooptag"); + Array<bool> loop_tag(me->totloop, false); /* simply tag loops of selected faces */ mp = me->mpoly; @@ -208,7 +210,7 @@ static bool vertex_color_smooth(Object *ob) j = 0; do { if (!(use_vert_sel && !(me->mvert[ml->v].flag & SELECT))) { - mlooptag[mp->loopstart + j] = true; + loop_tag[mp->loopstart + j] = true; } ml++; j++; @@ -218,9 +220,7 @@ static bool vertex_color_smooth(Object *ob) /* remove stale me->mcol, will be added later */ BKE_mesh_tessface_clear(me); - vertex_color_smooth_looptag(me, mlooptag); - - MEM_freeN(mlooptag); + vertex_color_smooth_looptag(me, loop_tag.data()); tag_object_after_update(ob); @@ -268,7 +268,8 @@ static void vpaint_tx_brightness_contrast(const float col[3], const void *user_data, float r_col[3]) { - const struct VPaintTx_BrightContrastData *data = user_data; + const VPaintTx_BrightContrastData *data = static_cast<const VPaintTx_BrightContrastData *>( + user_data); for (int i = 0; i < 3; i++) { r_col[i] = data->gain * col[i] + data->offset; @@ -302,10 +303,9 @@ static int vertex_color_brightness_contrast_exec(bContext *C, wmOperator *op) } } - const struct VPaintTx_BrightContrastData user_data = { - .gain = gain, - .offset = offset, - }; + VPaintTx_BrightContrastData user_data{}; + user_data.gain = gain; + user_data.offset = offset; if (ED_vpaint_color_transform(obact, vpaint_tx_brightness_contrast, &user_data)) { WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); @@ -345,7 +345,7 @@ struct VPaintTx_HueSatData { static void vpaint_tx_hsv(const float col[3], const void *user_data, float r_col[3]) { - const struct VPaintTx_HueSatData *data = user_data; + const VPaintTx_HueSatData *data = static_cast<const VPaintTx_HueSatData *>(user_data); float hsv[3]; rgb_to_hsv_v(col, hsv); @@ -366,11 +366,10 @@ static int vertex_color_hsv_exec(bContext *C, wmOperator *op) { Object *obact = CTX_data_active_object(C); - const struct VPaintTx_HueSatData user_data = { - .hue = RNA_float_get(op->ptr, "h"), - .sat = RNA_float_get(op->ptr, "s"), - .val = RNA_float_get(op->ptr, "v"), - }; + VPaintTx_HueSatData user_data{}; + user_data.hue = RNA_float_get(op->ptr, "h"); + user_data.sat = RNA_float_get(op->ptr, "s"); + user_data.val = RNA_float_get(op->ptr, "v"); if (ED_vpaint_color_transform(obact, vpaint_tx_hsv, &user_data)) { WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); @@ -410,7 +409,7 @@ static int vertex_color_invert_exec(bContext *C, wmOperator *UNUSED(op)) { Object *obact = CTX_data_active_object(C); - if (ED_vpaint_color_transform(obact, vpaint_tx_invert, NULL)) { + if (ED_vpaint_color_transform(obact, vpaint_tx_invert, nullptr)) { WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); return OPERATOR_FINISHED; } @@ -439,7 +438,7 @@ struct VPaintTx_LevelsData { static void vpaint_tx_levels(const float col[3], const void *user_data, float r_col[3]) { - const struct VPaintTx_LevelsData *data = user_data; + const VPaintTx_LevelsData *data = static_cast<const VPaintTx_LevelsData *>(user_data); for (int i = 0; i < 3; i++) { r_col[i] = data->gain * (col[i] + data->offset); } @@ -449,10 +448,9 @@ static int vertex_color_levels_exec(bContext *C, wmOperator *op) { Object *obact = CTX_data_active_object(C); - const struct VPaintTx_LevelsData user_data = { - .gain = RNA_float_get(op->ptr, "gain"), - .offset = RNA_float_get(op->ptr, "offset"), - }; + VPaintTx_LevelsData user_data{}; + user_data.gain = RNA_float_get(op->ptr, "gain"); + user_data.offset = RNA_float_get(op->ptr, "offset"); if (ED_vpaint_color_transform(obact, vpaint_tx_levels, &user_data)) { WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 32b7047c2b0..9ce80e4a433 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -148,7 +148,7 @@ const float *SCULPT_vertex_co_get(SculptSession *ss, int index) bool SCULPT_has_loop_colors(const Object *ob) { Mesh *me = BKE_object_get_original_mesh(ob); - CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id); + const CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id); return layer && BKE_id_attribute_domain(&me->id, layer) == ATTR_DOMAIN_CORNER; } @@ -1354,8 +1354,23 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata, SculptSession *ss = data->ob->sculpt; SculptUndoNode *unode; - SculptUndoType type = (data->brush->sculpt_tool == SCULPT_TOOL_MASK ? SCULPT_UNDO_MASK : - SCULPT_UNDO_COORDS); + SculptUndoType type; + + switch (data->brush->sculpt_tool) { + case SCULPT_TOOL_MASK: + type = SCULPT_UNDO_MASK; + BKE_pbvh_node_mark_update_mask(data->nodes[n]); + break; + case SCULPT_TOOL_PAINT: + case SCULPT_TOOL_SMEAR: + type = SCULPT_UNDO_COLOR; + BKE_pbvh_node_mark_update_color(data->nodes[n]); + break; + default: + type = SCULPT_UNDO_COORDS; + BKE_pbvh_node_mark_update(data->nodes[n]); + break; + } if (ss->bm) { unode = SCULPT_undo_push_node(data->ob, data->nodes[n], type); @@ -1397,8 +1412,6 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata, } } BKE_pbvh_vertex_iter_end; - - BKE_pbvh_node_mark_update(data->nodes[n]); } static void paint_mesh_restore_co(Sculpt *sd, Object *ob) @@ -3248,8 +3261,8 @@ static void do_brush_action(Sculpt *sd, } nodes = sculpt_pbvh_gather_generic(ob, sd, brush, use_original, radius_scale, &totnode); } - - if (sculpt_needs_pbvh_pixels(paint_mode_settings, brush, ob)) { + const bool use_pixels = sculpt_needs_pbvh_pixels(paint_mode_settings, brush, ob); + if (use_pixels) { sculpt_pbvh_update_pixels(paint_mode_settings, ss, ob); } @@ -3302,16 +3315,18 @@ static void do_brush_action(Sculpt *sd, } float location[3]; - SculptThreadedTaskData task_data = { - .sd = sd, - .ob = ob, - .brush = brush, - .nodes = nodes, - }; + if (!use_pixels) { + SculptThreadedTaskData task_data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + }; - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &task_data, do_brush_action_task_cb, &settings); + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &task_data, do_brush_action_task_cb, &settings); + } if (sculpt_brush_needs_normal(ss, brush)) { update_sculpt_normal(sd, ob, nodes, totnode); @@ -3520,15 +3535,7 @@ static void sculpt_combine_proxies_task_cb(void *__restrict userdata, SculptSession *ss = data->ob->sculpt; Sculpt *sd = data->sd; Object *ob = data->ob; - - /* These brushes start from original coordinates. */ - const bool use_orco = ELEM(data->brush->sculpt_tool, - SCULPT_TOOL_GRAB, - SCULPT_TOOL_ROTATE, - SCULPT_TOOL_THUMB, - SCULPT_TOOL_ELASTIC_DEFORM, - SCULPT_TOOL_BOUNDARY, - SCULPT_TOOL_POSE); + const bool use_orco = data->use_proxies_orco; PBVHVertexIter vd; PBVHProxyNode *proxies; @@ -3583,12 +3590,23 @@ static void sculpt_combine_proxies(Sculpt *sd, Object *ob) return; } + /* First line is tools that don't support proxies. */ + const bool use_orco = ELEM(brush->sculpt_tool, + SCULPT_TOOL_GRAB, + SCULPT_TOOL_ROTATE, + SCULPT_TOOL_THUMB, + SCULPT_TOOL_ELASTIC_DEFORM, + SCULPT_TOOL_BOUNDARY, + SCULPT_TOOL_POSE); + BKE_pbvh_gather_proxies(ss->pbvh, &nodes, &totnode); + SculptThreadedTaskData data = { .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, + .use_proxies_orco = use_orco, }; TaskParallelSettings settings; @@ -3597,6 +3615,27 @@ static void sculpt_combine_proxies(Sculpt *sd, Object *ob) MEM_SAFE_FREE(nodes); } +void SCULPT_combine_transform_proxies(Sculpt *sd, Object *ob) +{ + SculptSession *ss = ob->sculpt; + PBVHNode **nodes; + int totnode; + + BKE_pbvh_gather_proxies(ss->pbvh, &nodes, &totnode); + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .nodes = nodes, + .use_proxies_orco = false, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, sculpt_combine_proxies_task_cb, &settings); + + MEM_SAFE_FREE(nodes); +} + /** * Copy the modified vertices from the #PBVH to the active key. */ @@ -4135,7 +4174,7 @@ static void smooth_brush_toggle_off(const bContext *C, Paint *paint, StrokeCache /* Initialize the stroke cache invariants from operator properties. */ static void sculpt_update_cache_invariants( - bContext *C, Sculpt *sd, SculptSession *ss, wmOperator *op, const float mouse[2]) + bContext *C, Sculpt *sd, SculptSession *ss, wmOperator *op, const float mval[2]) { StrokeCache *cache = MEM_callocN(sizeof(StrokeCache), "stroke cache"); UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; @@ -4165,8 +4204,8 @@ static void sculpt_update_cache_invariants( sculpt_init_mirror_clipping(ob, ss); /* Initial mouse location. */ - if (mouse) { - copy_v2_v2(cache->initial_mouse, mouse); + if (mval) { + copy_v2_v2(cache->initial_mouse, mval); } else { zero_v2(cache->initial_mouse); @@ -4304,7 +4343,8 @@ static bool sculpt_needs_delta_from_anchored_origin(Brush *brush) SCULPT_TOOL_POSE, SCULPT_TOOL_BOUNDARY, SCULPT_TOOL_THUMB, - SCULPT_TOOL_ELASTIC_DEFORM)) { + SCULPT_TOOL_ELASTIC_DEFORM, + SCULPT_TOOL_SMEAR)) { return true; } if (brush->sculpt_tool == SCULPT_TOOL_CLOTH && @@ -4334,7 +4374,7 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru { SculptSession *ss = ob->sculpt; StrokeCache *cache = ss->cache; - const float mouse[2] = { + const float mval[2] = { cache->mouse_event[0], cache->mouse_event[1], }; @@ -4353,6 +4393,7 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru SCULPT_TOOL_SNAKE_HOOK, SCULPT_TOOL_POSE, SCULPT_TOOL_BOUNDARY, + SCULPT_TOOL_SMEAR, SCULPT_TOOL_THUMB) && !sculpt_brush_use_topology_rake(ss, brush)) { return; @@ -4374,9 +4415,9 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru add_v3_v3(cache->true_location, cache->grab_delta); } - /* Compute 3d coordinate at same z from original location + mouse. */ + /* Compute 3d coordinate at same z from original location + mval. */ mul_v3_m4v3(loc, ob->obmat, cache->orig_grab_location); - ED_view3d_win_to_3d(cache->vc->v3d, cache->vc->region, loc, mouse, grab_location); + ED_view3d_win_to_3d(cache->vc->v3d, cache->vc->region, loc, mval, grab_location); /* Compute delta to move verts by. */ if (!SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) { @@ -4744,7 +4785,7 @@ static void sculpt_find_nearest_to_ray_cb(PBVHNode *node, void *data_v, float *t } float SCULPT_raycast_init(ViewContext *vc, - const float mouse[2], + const float mval[2], float ray_start[3], float ray_end[3], float ray_normal[3], @@ -4758,7 +4799,7 @@ float SCULPT_raycast_init(ViewContext *vc, /* TODO: what if the segment is totally clipped? (return == 0). */ ED_view3d_win_to_segment_clipped( - vc->depsgraph, vc->region, vc->v3d, mouse, ray_start, ray_end, true); + vc->depsgraph, vc->region, vc->v3d, mval, ray_start, ray_end, true); invert_m4_m4(obimat, ob->obmat); mul_m4_v3(obimat, ray_start); @@ -4782,7 +4823,7 @@ float SCULPT_raycast_init(ViewContext *vc, bool SCULPT_cursor_geometry_info_update(bContext *C, SculptCursorGeometryInfo *out, - const float mouse[2], + const float mval[2], bool use_sampled_normal) { Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); @@ -4811,7 +4852,7 @@ bool SCULPT_cursor_geometry_info_update(bContext *C, } /* PBVH raycast to get active vertex and face normal. */ - depth = SCULPT_raycast_init(&vc, mouse, ray_start, ray_end, ray_normal, original); + depth = SCULPT_raycast_init(&vc, mval, ray_start, ray_end, ray_normal, original); SCULPT_stroke_modifiers_check(C, ob, brush); SculptRaycastData srd = { @@ -4909,7 +4950,7 @@ bool SCULPT_cursor_geometry_info_update(bContext *C, return true; } -bool SCULPT_stroke_get_location(bContext *C, float out[3], const float mouse[2]) +bool SCULPT_stroke_get_location(bContext *C, float out[3], const float mval[2]) { Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Object *ob; @@ -4931,7 +4972,7 @@ bool SCULPT_stroke_get_location(bContext *C, float out[3], const float mouse[2]) SCULPT_stroke_modifiers_check(C, ob, brush); - depth = SCULPT_raycast_init(&vc, mouse, ray_start, ray_end, ray_normal, original); + depth = SCULPT_raycast_init(&vc, mval, ray_start, ray_end, ray_normal, original); if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { BM_mesh_elem_table_ensure(ss->bm, BM_VERT); @@ -5236,14 +5277,10 @@ void SCULPT_flush_update_done(const bContext *C, Object *ob, SculptUpdateType up /* Returns whether the mouse/stylus is over the mesh (1) * or over the background (0). */ -static bool over_mesh(bContext *C, struct wmOperator *UNUSED(op), float x, float y) +static bool over_mesh(bContext *C, struct wmOperator *UNUSED(op), const float mval[2]) { - float mouse[2], co[3]; - - mouse[0] = x; - mouse[1] = y; - - return SCULPT_stroke_get_location(C, co, mouse); + float co_dummy[3]; + return SCULPT_stroke_get_location(C, co_dummy, mval); } bool SCULPT_handles_colors_report(SculptSession *ss, ReportList *reports) @@ -5264,36 +5301,44 @@ bool SCULPT_handles_colors_report(SculptSession *ss, ReportList *reports) return false; } -static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const float mouse[2]) +static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const float mval[2]) { - /* Don't start the stroke until mouse goes over the mesh. - * NOTE: mouse will only be null when re-executing the saved stroke. - * We have exception for 'exec' strokes since they may not set 'mouse', + /* Don't start the stroke until `mval` goes over the mesh. + * NOTE: `mval` will only be null when re-executing the saved stroke. + * We have exception for 'exec' strokes since they may not set `mval`, * only 'location', see: T52195. */ - if (((op->flag & OP_IS_INVOKE) == 0) || (mouse == NULL) || - over_mesh(C, op, mouse[0], mouse[1])) { + if (((op->flag & OP_IS_INVOKE) == 0) || (mval == NULL) || over_mesh(C, op, mval)) { Object *ob = CTX_data_active_object(C); SculptSession *ss = ob->sculpt; Sculpt *sd = CTX_data_tool_settings(C)->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); + ToolSettings *tool_settings = CTX_data_tool_settings(C); /* NOTE: This should be removed when paint mode is available. Paint mode can force based on the * canvas it is painting on. (ref. use_sculpt_texture_paint). */ if (brush && SCULPT_TOOL_NEEDS_COLOR(brush->sculpt_tool)) { View3D *v3d = CTX_wm_view3d(C); - if (v3d) { + if (v3d->shading.type == OB_SOLID) { v3d->shading.color_type = V3D_SHADING_VERTEX_COLOR; } } ED_view3d_init_mats_rv3d(ob, CTX_wm_region_view3d(C)); - sculpt_update_cache_invariants(C, sd, ss, op, mouse); + sculpt_update_cache_invariants(C, sd, ss, op, mval); SculptCursorGeometryInfo sgi; - SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false); + SCULPT_cursor_geometry_info_update(C, &sgi, mval, false); - SCULPT_undo_push_begin(ob, sculpt_tool_name(sd)); + /* Setup the correct undo system. Image painting and sculpting are mutual exclusive. + * Color attributes are part of the sculpting undo system. */ + if (brush && brush->sculpt_tool == SCULPT_TOOL_PAINT && + SCULPT_use_image_paint_brush(&tool_settings->paint_mode, ob)) { + ED_image_undo_push_begin(op->type->name, PAINT_MODE_SCULPT); + } + else { + SCULPT_undo_push_begin(ob, sculpt_tool_name(sd)); + } return true; } @@ -5420,7 +5465,13 @@ static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(str SCULPT_cache_free(ss->cache); ss->cache = NULL; - SCULPT_undo_push_end(ob); + if (brush && brush->sculpt_tool == SCULPT_TOOL_PAINT && + SCULPT_use_image_paint_brush(&tool_settings->paint_mode, ob)) { + ED_image_undo_push_end(); + } + else { + SCULPT_undo_push_end(ob); + } if (brush->sculpt_tool == SCULPT_TOOL_MASK) { SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK); @@ -5469,7 +5520,7 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent /* For tablet rotation. */ ignore_background_click = RNA_boolean_get(op->ptr, "ignore_background_click"); - if (ignore_background_click && !over_mesh(C, op, event->xy[0], event->xy[1])) { + if (ignore_background_click && !over_mesh(C, op, (const float[2]){UNPACK2(event->mval)})) { paint_stroke_free(C, op, op->customdata); return OPERATOR_PASS_THROUGH; } @@ -5729,7 +5780,8 @@ void SCULPT_connected_components_ensure(Object *ob) { SculptSession *ss = ob->sculpt; - /* Topology IDs already initialized. They only need to be recalculated when the PBVH is rebuild. + /* Topology IDs already initialized. They only need to be recalculated when the PBVH is + * rebuild. */ if (ss->vertex_info.connected_component) { return; @@ -5793,7 +5845,8 @@ void SCULPT_fake_neighbors_ensure(Sculpt *sd, Object *ob, const float max_dist) SculptSession *ss = ob->sculpt; const int totvert = SCULPT_vertex_count_get(ss); - /* Fake neighbors were already initialized with the same distance, so no need to be recalculated. + /* Fake neighbors were already initialized with the same distance, so no need to be + * recalculated. */ if (ss->fake_neighbors.fake_neighbor_index && ss->fake_neighbors.current_max_distance == max_dist) { diff --git a/source/blender/editors/sculpt_paint/sculpt_automasking.c b/source/blender/editors/sculpt_paint/sculpt_automasking.cc index d7a2a18504c..bb101717c9b 100644 --- a/source/blender/editors/sculpt_paint/sculpt_automasking.c +++ b/source/blender/editors/sculpt_paint/sculpt_automasking.cc @@ -9,6 +9,7 @@ #include "BLI_blenlib.h" #include "BLI_hash.h" +#include "BLI_index_range.hh" #include "BLI_math.h" #include "BLI_task.h" @@ -43,8 +44,10 @@ #include "bmesh.h" -#include <math.h> -#include <stdlib.h> +#include <cmath> +#include <cstdlib> + +using blender::IndexRange; AutomaskingCache *SCULPT_automasking_active_cache_get(SculptSession *ss) { @@ -54,7 +57,7 @@ AutomaskingCache *SCULPT_automasking_active_cache_get(SculptSession *ss) if (ss->filter_cache) { return ss->filter_cache->automasking; } - return NULL; + return nullptr; } bool SCULPT_is_automasking_mode_enabled(const Sculpt *sd, @@ -167,18 +170,18 @@ static bool sculpt_automasking_is_constrained_by_radius(Brush *br) return false; } -typedef struct AutomaskFloodFillData { +struct AutomaskFloodFillData { float *automask_factor; float radius; bool use_radius; float location[3]; char symm; -} AutomaskFloodFillData; +}; static bool automask_floodfill_cb( SculptSession *ss, int from_v, int to_v, bool UNUSED(is_duplicate), void *userdata) { - AutomaskFloodFillData *data = userdata; + AutomaskFloodFillData *data = (AutomaskFloodFillData *)userdata; data->automask_factor[to_v] = 1.0f; data->automask_factor[from_v] = 1.0f; @@ -194,11 +197,11 @@ static float *SCULPT_topology_automasking_init(Sculpt *sd, Object *ob, float *au if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) { BLI_assert_msg(0, "Topology masking: pmap missing"); - return NULL; + return nullptr; } const int totvert = SCULPT_vertex_count_get(ss); - for (int i = 0; i < totvert; i++) { + for (int i : IndexRange(totvert)) { automask_factor[i] = 0.0f; } @@ -209,12 +212,13 @@ static float *SCULPT_topology_automasking_init(Sculpt *sd, Object *ob, float *au const float radius = ss->cache ? ss->cache->radius : FLT_MAX; SCULPT_floodfill_add_active(sd, ob, ss, &flood, radius); - AutomaskFloodFillData fdata = { - .automask_factor = automask_factor, - .radius = radius, - .use_radius = ss->cache && sculpt_automasking_is_constrained_by_radius(brush), - .symm = SCULPT_mesh_symmetry_xyz_get(ob), - }; + AutomaskFloodFillData fdata = {nullptr}; + + fdata.automask_factor = automask_factor; + fdata.radius = radius; + fdata.use_radius = ss->cache && sculpt_automasking_is_constrained_by_radius(brush); + fdata.symm = SCULPT_mesh_symmetry_xyz_get(ob); + copy_v3_v3(fdata.location, SCULPT_active_vertex_co_get(ss)); SCULPT_floodfill_execute(ss, &flood, automask_floodfill_cb, &fdata); SCULPT_floodfill_free(&flood); @@ -228,17 +232,17 @@ static float *sculpt_face_sets_automasking_init(Sculpt *sd, Object *ob, float *a Brush *brush = BKE_paint_brush(&sd->paint); if (!SCULPT_is_automasking_enabled(sd, ss, brush)) { - return NULL; + return nullptr; } if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) { BLI_assert_msg(0, "Face Sets automasking: pmap missing"); - return NULL; + return nullptr; } int tot_vert = SCULPT_vertex_count_get(ss); int active_face_set = SCULPT_active_face_set_get(ss); - for (int i = 0; i < tot_vert; i++) { + for (int i : IndexRange(tot_vert)) { if (!SCULPT_vertex_has_face_set(ss, i, active_face_set)) { automask_factor[i] *= 0.0f; } @@ -258,13 +262,13 @@ float *SCULPT_boundary_automasking_init(Object *ob, if (!ss->pmap) { BLI_assert_msg(0, "Boundary Edges masking: pmap missing"); - return NULL; + return nullptr; } const int totvert = SCULPT_vertex_count_get(ss); - int *edge_distance = MEM_callocN(sizeof(int) * totvert, "automask_factor"); + int *edge_distance = (int *)MEM_callocN(sizeof(int) * totvert, "automask_factor"); - for (int i = 0; i < totvert; i++) { + for (int i : IndexRange(totvert)) { edge_distance[i] = EDGE_DISTANCE_INF; switch (mode) { case AUTOMASK_INIT_BOUNDARY_EDGES: @@ -280,8 +284,8 @@ float *SCULPT_boundary_automasking_init(Object *ob, } } - for (int propagation_it = 0; propagation_it < propagation_steps; propagation_it++) { - for (int i = 0; i < totvert; i++) { + for (int propagation_it : IndexRange(propagation_steps)) { + for (int i : IndexRange(totvert)) { if (edge_distance[i] != EDGE_DISTANCE_INF) { continue; } @@ -295,7 +299,7 @@ float *SCULPT_boundary_automasking_init(Object *ob, } } - for (int i = 0; i < totvert; i++) { + for (int i : IndexRange(totvert)) { if (edge_distance[i] == EDGE_DISTANCE_INF) { continue; } @@ -323,10 +327,11 @@ AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object const int totvert = SCULPT_vertex_count_get(ss); if (!SCULPT_is_automasking_enabled(sd, ss, brush)) { - return NULL; + return nullptr; } - AutomaskingCache *automasking = MEM_callocN(sizeof(AutomaskingCache), "automasking cache"); + AutomaskingCache *automasking = (AutomaskingCache *)MEM_callocN(sizeof(AutomaskingCache), + "automasking cache"); SCULPT_automasking_cache_settings_update(automasking, ss, sd, brush); SCULPT_boundary_info_ensure(ob); @@ -334,8 +339,8 @@ AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object return automasking; } - automasking->factor = MEM_malloc_arrayN(totvert, sizeof(float), "automask_factor"); - for (int i = 0; i < totvert; i++) { + automasking->factor = (float *)MEM_malloc_arrayN(totvert, sizeof(float), "automask_factor"); + for (int i : IndexRange(totvert)) { automasking->factor[i] = 1.0f; } diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c index dcf90f9e819..9d231f2ccd2 100644 --- a/source/blender/editors/sculpt_paint/sculpt_cloth.c +++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c @@ -1553,11 +1553,9 @@ static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent const eSculptClothFilterType filter_type = RNA_enum_get(op->ptr, "type"); /* Update the active vertex */ - float mouse[2]; + float mval_fl[2] = {UNPACK2(event->mval)}; SculptCursorGeometryInfo sgi; - mouse[0] = event->mval[0]; - mouse[1] = event->mval[1]; - SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false); + SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false); SCULPT_vertex_random_access_ensure(ss); diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.c b/source/blender/editors/sculpt_paint/sculpt_detail.c index fe69cf6b84f..00503087e39 100644 --- a/source/blender/editors/sculpt_paint/sculpt_detail.c +++ b/source/blender/editors/sculpt_paint/sculpt_detail.c @@ -158,7 +158,7 @@ static EnumPropertyItem prop_sculpt_sample_detail_mode_types[] = { {0, NULL, 0, NULL, NULL}, }; -static void sample_detail_voxel(bContext *C, ViewContext *vc, int mx, int my) +static void sample_detail_voxel(bContext *C, ViewContext *vc, const int mval[2]) { Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Object *ob = vc->obact; @@ -169,8 +169,8 @@ static void sample_detail_voxel(bContext *C, ViewContext *vc, int mx, int my) SCULPT_vertex_random_access_ensure(ss); /* Update the active vertex. */ - const float mouse[2] = {mx, my}; - SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false); + const float mval_fl[2] = {UNPACK2(mval)}; + SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false); BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false); /* Average the edge length of the connected edges to the active vertex. */ @@ -201,7 +201,7 @@ static void sculpt_raycast_detail_cb(PBVHNode *node, void *data_v, float *tmin) } } -static void sample_detail_dyntopo(bContext *C, ViewContext *vc, ARegion *region, int mx, int my) +static void sample_detail_dyntopo(bContext *C, ViewContext *vc, const int mval[2]) { Sculpt *sd = CTX_data_tool_settings(C)->sculpt; Object *ob = vc->obact; @@ -209,9 +209,9 @@ static void sample_detail_dyntopo(bContext *C, ViewContext *vc, ARegion *region, SCULPT_stroke_modifiers_check(C, ob, brush); - const float mouse[2] = {mx - region->winrct.xmin, my - region->winrct.ymin}; + const float mval_fl[2] = {UNPACK2(mval)}; float ray_start[3], ray_end[3], ray_normal[3]; - float depth = SCULPT_raycast_init(vc, mouse, ray_start, ray_end, ray_normal, false); + float depth = SCULPT_raycast_init(vc, mval_fl, ray_start, ray_end, ray_normal, false); SculptDetailRaycastData srd; srd.hit = 0; @@ -228,14 +228,12 @@ static void sample_detail_dyntopo(bContext *C, ViewContext *vc, ARegion *region, } } -static int sample_detail(bContext *C, int mx, int my, int mode) +static int sample_detail(bContext *C, const int event_xy[2], int mode) { /* Find 3D view to pick from. */ bScreen *screen = CTX_wm_screen(C); - ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_VIEW3D, (const int[2]){mx, my}); - ARegion *region = (area) ? - BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, (const int[2]){mx, my}) : - NULL; + ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_VIEW3D, event_xy); + ARegion *region = (area) ? BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, event_xy) : NULL; if (region == NULL) { return OPERATOR_CANCELLED; } @@ -260,6 +258,11 @@ static int sample_detail(bContext *C, int mx, int my, int mode) return OPERATOR_CANCELLED; } + const int mval[2] = { + event_xy[0] - region->winrct.xmin, + event_xy[1] - region->winrct.ymin, + }; + /* Pick sample detail. */ switch (mode) { case SAMPLE_DETAIL_DYNTOPO: @@ -268,7 +271,7 @@ static int sample_detail(bContext *C, int mx, int my, int mode) CTX_wm_region_set(C, prev_region); return OPERATOR_CANCELLED; } - sample_detail_dyntopo(C, &vc, region, mx, my); + sample_detail_dyntopo(C, &vc, mval); break; case SAMPLE_DETAIL_VOXEL: if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) { @@ -276,7 +279,7 @@ static int sample_detail(bContext *C, int mx, int my, int mode) CTX_wm_region_set(C, prev_region); return OPERATOR_CANCELLED; } - sample_detail_voxel(C, &vc, mx, my); + sample_detail_voxel(C, &vc, mval); break; } @@ -292,7 +295,7 @@ static int sculpt_sample_detail_size_exec(bContext *C, wmOperator *op) int ss_co[2]; RNA_int_get_array(op->ptr, "location", ss_co); int mode = RNA_enum_get(op->ptr, "mode"); - return sample_detail(C, ss_co[0], ss_co[1], mode); + return sample_detail(C, ss_co, mode); } static int sculpt_sample_detail_size_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(e)) @@ -308,12 +311,10 @@ static int sculpt_sample_detail_size_modal(bContext *C, wmOperator *op, const wm switch (event->type) { case LEFTMOUSE: if (event->val == KM_PRESS) { - const int ss_co[2] = {event->xy[0], event->xy[1]}; - int mode = RNA_enum_get(op->ptr, "mode"); - sample_detail(C, ss_co[0], ss_co[1], mode); + sample_detail(C, event->xy, mode); - RNA_int_set_array(op->ptr, "location", ss_co); + RNA_int_set_array(op->ptr, "location", event->xy); WM_cursor_modal_restore(CTX_wm_window(C)); ED_workspace_status_text(C, NULL); WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, NULL); @@ -657,11 +658,13 @@ static int dyntopo_detail_size_edit_modal(bContext *C, wmOperator *op, const wmE ED_region_tag_redraw(region); - if (event->type == EVT_LEFTCTRLKEY && event->val == KM_PRESS) { - cd->sample_mode = true; - } - if (event->type == EVT_LEFTCTRLKEY && event->val == KM_RELEASE) { - cd->sample_mode = false; + if (ELEM(event->type, EVT_LEFTCTRLKEY, EVT_RIGHTCTRLKEY)) { + if (event->val == KM_PRESS) { + cd->sample_mode = true; + } + else if (event->val == KM_RELEASE) { + cd->sample_mode = false; + } } /* Sample mode sets the detail size sampling the average edge length under the surface. */ diff --git a/source/blender/editors/sculpt_paint/sculpt_expand.c b/source/blender/editors/sculpt_paint/sculpt_expand.c index 46940b619e6..fbf988d63e8 100644 --- a/source/blender/editors/sculpt_paint/sculpt_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_expand.c @@ -1450,13 +1450,11 @@ static void sculpt_expand_update_for_vertex(bContext *C, Object *ob, const int v * Updates the #SculptSession cursor data and gets the active vertex * if the cursor is over the mesh. */ -static int sculpt_expand_target_vertex_update_and_get(bContext *C, - Object *ob, - const float mouse[2]) +static int sculpt_expand_target_vertex_update_and_get(bContext *C, Object *ob, const float mval[2]) { SculptSession *ss = ob->sculpt; SculptCursorGeometryInfo sgi; - if (SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false)) { + if (SCULPT_cursor_geometry_info_update(C, &sgi, mval, false)) { return SCULPT_active_vertex_get(ss); } return SCULPT_EXPAND_VERTEX_NONE; @@ -1588,16 +1586,16 @@ static void sculpt_expand_find_active_connected_components_from_vert(Object *ob, static void sculpt_expand_set_initial_components_for_mouse(bContext *C, Object *ob, ExpandCache *expand_cache, - const float mouse[2]) + const float mval[2]) { SculptSession *ss = ob->sculpt; - int initial_vertex = sculpt_expand_target_vertex_update_and_get(C, ob, mouse); + int initial_vertex = sculpt_expand_target_vertex_update_and_get(C, ob, mval); if (initial_vertex == SCULPT_EXPAND_VERTEX_NONE) { /* Cursor not over the mesh, for creating valid initial falloffs, fallback to the last active * vertex in the sculpt session. */ initial_vertex = SCULPT_active_vertex_get(ss); } - copy_v2_v2(ss->expand_cache->initial_mouse, mouse); + copy_v2_v2(ss->expand_cache->initial_mouse, mval); expand_cache->initial_active_vertex = initial_vertex; expand_cache->initial_active_face_set = SCULPT_active_face_set_get(ss); @@ -1629,14 +1627,14 @@ static void sculpt_expand_move_propagation_origin(bContext *C, { Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - const float mouse[2] = {event->mval[0], event->mval[1]}; + const float mval_fl[2] = {UNPACK2(event->mval)}; float move_disp[2]; - sub_v2_v2v2(move_disp, mouse, expand_cache->initial_mouse_move); + sub_v2_v2v2(move_disp, mval_fl, expand_cache->initial_mouse_move); - float new_mouse[2]; - add_v2_v2v2(new_mouse, move_disp, expand_cache->original_mouse_move); + float new_mval[2]; + add_v2_v2v2(new_mval, move_disp, expand_cache->original_mouse_move); - sculpt_expand_set_initial_components_for_mouse(C, ob, expand_cache, new_mouse); + sculpt_expand_set_initial_components_for_mouse(C, ob, expand_cache, new_mval); sculpt_expand_falloff_factors_from_vertex_and_symm_create( expand_cache, sd, @@ -1697,8 +1695,8 @@ static int sculpt_expand_modal(bContext *C, wmOperator *op, const wmEvent *event sculpt_expand_ensure_sculptsession_data(ob); /* Update and get the active vertex (and face) from the cursor. */ - const float mouse[2] = {event->mval[0], event->mval[1]}; - const int target_expand_vertex = sculpt_expand_target_vertex_update_and_get(C, ob, mouse); + const float mval_fl[2] = {UNPACK2(event->mval)}; + const int target_expand_vertex = sculpt_expand_target_vertex_update_and_get(C, ob, mval_fl); /* Handle the modal keymap state changes. */ ExpandCache *expand_cache = ss->expand_cache; @@ -1756,7 +1754,7 @@ static int sculpt_expand_modal(bContext *C, wmOperator *op, const wmEvent *event } expand_cache->move = true; expand_cache->move_original_falloff_type = expand_cache->falloff_type; - copy_v2_v2(expand_cache->initial_mouse_move, mouse); + copy_v2_v2(expand_cache->initial_mouse_move, mval_fl); copy_v2_v2(expand_cache->original_mouse_move, expand_cache->initial_mouse); if (expand_cache->falloff_type == SCULPT_EXPAND_FALLOFF_GEODESIC && SCULPT_vertex_count_get(ss) > expand_cache->max_geodesic_move_preview) { @@ -2001,7 +1999,7 @@ static void sculpt_expand_cache_initial_config_set(bContext *C, BKE_curvemapping_init(expand_cache->brush->curve); copy_v4_fl(expand_cache->fill_color, 1.0f); copy_v3_v3(expand_cache->fill_color, BKE_brush_color_get(ss->scene, expand_cache->brush)); - IMB_colormanagement_srgb_to_scene_linear_v3(expand_cache->fill_color); + IMB_colormanagement_srgb_to_scene_linear_v3(expand_cache->fill_color, expand_cache->fill_color); expand_cache->scene = CTX_data_scene(C); expand_cache->mtex = &expand_cache->brush->mtex; diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.c index 7171c241534..ce704e619ea 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.c +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.c @@ -60,7 +60,7 @@ int ED_sculpt_face_sets_find_next_available_id(struct Mesh *mesh) { - int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS); + const int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS); if (!face_sets) { return SCULPT_FACE_SET_NONE; } @@ -150,25 +150,22 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, } } } - else if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { - { - if (!sculpt_brush_test_sq_fn(&test, vd.co)) { - continue; - } - const float fade = bstrength * SCULPT_brush_strength_factor(ss, - brush, - vd.co, - sqrtf(test.dist), - vd.no, - vd.fno, - vd.mask ? *vd.mask : 0.0f, - vd.index, - thread_id); - - if (fade > 0.05f) { - SCULPT_vertex_face_set_set(ss, vd.index, ss->cache->paint_face_set); - } + if (!sculpt_brush_test_sq_fn(&test, vd.co)) { + continue; + } + const float fade = bstrength * SCULPT_brush_strength_factor(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + vd.index, + thread_id); + + if (fade > 0.05f) { + SCULPT_vertex_face_set_set(ss, vd.index, ss->cache->paint_face_set); } } } @@ -952,11 +949,9 @@ static int sculpt_face_sets_change_visibility_invoke(bContext *C, /* Update the active vertex and Face Set using the cursor position to avoid relying on the paint * cursor updates. */ SculptCursorGeometryInfo sgi; - float mouse[2]; - mouse[0] = event->mval[0]; - mouse[1] = event->mval[1]; + const float mval_fl[2] = {UNPACK2(event->mval)}; SCULPT_vertex_random_access_ensure(ss); - SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false); + SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false); return sculpt_face_sets_change_visibility_exec(C, op); } @@ -1404,8 +1399,8 @@ static int sculpt_face_set_edit_invoke(bContext *C, wmOperator *op, const wmEven /* Update the current active Face Set and Vertex as the operator can be used directly from the * tool without brush cursor. */ SculptCursorGeometryInfo sgi; - const float mouse[2] = {event->mval[0], event->mval[1]}; - if (!SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false)) { + const float mval_fl[2] = {UNPACK2(event->mval)}; + if (!SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false)) { /* The cursor is not over the mesh. Cancel to avoid editing the last updated Face Set ID. */ return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c index 09f13ff110d..95c01d24c6d 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c @@ -124,8 +124,8 @@ static void color_filter_task_cb(void *__restrict userdata, } case COLOR_FILTER_HUE: rgb_to_hsv_v(orig_color, hsv_color); - hue = hsv_color[0] + fade; - hsv_color[0] = fabs((hsv_color[0] + fade) - hue); + hue = hsv_color[0]; + hsv_color[0] = fmod((hsv_color[0] + fabs(fade)) - hue, 1); hsv_to_rgb_v(hsv_color, final_color); break; case COLOR_FILTER_SATURATION: @@ -298,7 +298,7 @@ static int sculpt_color_filter_modal(bContext *C, wmOperator *op, const wmEvent float fill_color[3]; RNA_float_get_array(op->ptr, "fill_color", fill_color); - IMB_colormanagement_srgb_to_scene_linear_v3(fill_color); + IMB_colormanagement_srgb_to_scene_linear_v3(fill_color, fill_color); if (filter_strength < 0.0 && !ss->filter_cache->pre_smoothed_color) { sculpt_color_presmooth_init(ss); @@ -328,18 +328,20 @@ static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent { Object *ob = CTX_data_active_object(C); Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + View3D *v3d = CTX_wm_view3d(C); SculptSession *ss = ob->sculpt; PBVH *pbvh = ob->sculpt->pbvh; + if (v3d->shading.type == OB_SOLID) { + v3d->shading.color_type = V3D_SHADING_VERTEX_COLOR; + } const bool use_automasking = SCULPT_is_automasking_enabled(sd, ss, NULL); if (use_automasking) { /* Update the active face set manually as the paint cursor is not enabled when using the Mesh * Filter Tool. */ - float mouse[2]; + float mval_fl[2] = {UNPACK2(event->mval)}; SculptCursorGeometryInfo sgi; - mouse[0] = event->mval[0]; - mouse[1] = event->mval[1]; - SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false); + SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false); } /* Disable for multires and dyntopo for now */ @@ -374,7 +376,7 @@ void SCULPT_OT_color_filter(struct wmOperatorType *ot) /* identifiers */ ot->name = "Filter Color"; ot->idname = "SCULPT_OT_color_filter"; - ot->description = "Applies a filter to modify the current sculpt vertex colors"; + ot->description = "Applies a filter to modify the active color attribute"; /* api callbacks */ ot->invoke = sculpt_color_filter_invoke; diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c index a32b1c3015b..dbed5624adf 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c @@ -675,11 +675,9 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent if (use_automasking) { /* Update the active face set manually as the paint cursor is not enabled when using the Mesh * Filter Tool. */ - float mouse[2]; + float mval_fl[2] = {UNPACK2(event->mval)}; SculptCursorGeometryInfo sgi; - mouse[0] = event->mval[0]; - mouse[1] = event->mval[1]; - SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false); + SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false); } SCULPT_vertex_random_access_ensure(ss); diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index f9633c91087..0693b445fe5 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -247,6 +247,10 @@ typedef struct SculptThreadedTaskData { float (*mat)[4]; float (*vertCos)[3]; + /* When true, the displacement stored in the proxies will be aplied to the original coordinates + * instead of to the current coordinates. */ + bool use_proxies_orco; + /* X and Z vectors aligned to the stroke direction for operations where perpendicular vectors to * the stroke direction are needed. */ float (*stroke_xz)[3]; @@ -290,6 +294,10 @@ typedef struct SculptThreadedTaskData { bool mask_expand_create_face_set; float transform_mats[8][4][4]; + float elastic_transform_mat[4][4]; + float elastic_transform_pivot[3]; + float elastic_transform_pivot_init[3]; + float elastic_transform_radius; /* Boundary brush */ float boundary_deform_strength; @@ -372,6 +380,14 @@ typedef enum SculptFilterOrientation { SCULPT_FILTER_ORIENTATION_VIEW = 2, } SculptFilterOrientation; +/* Defines how transform tools are going to apply its displacement. */ +typedef enum SculptTransformDisplacementMode { + /* Displaces the elements from their original coordinates. */ + SCULPT_TRANSFORM_DISPLACEMENT_ORIGINAL = 0, + /* Displaces the elements incrementally from their previous position. */ + SCULPT_TRANSFORM_DISPLACEMENT_INCREMENTAL = 1, +} SculptTransformDisplacementMode; + #define SCULPT_CLAY_STABILIZER_LEN 10 typedef struct AutomaskingSettings { @@ -440,6 +456,8 @@ typedef struct FilterCache { int active_face_set; + SculptTransformDisplacementMode transform_displacement_mode; + /* Auto-masking. */ AutomaskingCache *automasking; @@ -1137,12 +1155,15 @@ bool SCULPT_search_sphere_cb(PBVHNode *node, void *data_v); */ bool SCULPT_search_circle_cb(PBVHNode *node, void *data_v); +void SCULPT_combine_transform_proxies(Sculpt *sd, Object *ob); + /** * Initialize a point-in-brush test with a given falloff shape. * * \param falloff_shape: #PAINT_FALLOFF_SHAPE_SPHERE or #PAINT_FALLOFF_SHAPE_TUBE. * \return The brush falloff function. */ + SculptBrushTestFn SCULPT_brush_test_init_with_falloff_shape(SculptSession *ss, SculptBrushTest *test, char falloff_shape); @@ -1652,11 +1673,11 @@ void SCULPT_do_paint_brush(struct PaintModeSettings *paint_mode_settings, int totnode) ATTR_NONNULL(); /** - * @brief Get the image canvas for painting on the given object. + * \brief Get the image canvas for painting on the given object. * - * @return #true if an image is found. The #r_image and #r_image_user fields are filled with the + * \return #true if an image is found. The #r_image and #r_image_user fields are filled with the * image and image user. Returns false when the image isn't found. In the later case the r_image - * and r_image_user are set to nullptr/NULL. + * and r_image_user are set to NULL. */ bool SCULPT_paint_image_canvas_get(struct PaintModeSettings *paint_mode_settings, struct Object *ob, diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c index 201e02b8235..4593c6a8b60 100644 --- a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c @@ -167,10 +167,8 @@ static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent * if (RNA_boolean_get(op->ptr, "use_cursor")) { SculptCursorGeometryInfo sgi; - float mouse[2]; - mouse[0] = event->mval[0]; - mouse[1] = event->mval[1]; - if (SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false)) { + const float mval_fl[2] = {UNPACK2(event->mval)}; + if (SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false)) { /* The cursor is over the mesh, get the update iteration from the updated active vertex. */ mask_expand_update_it = ss->filter_cache->mask_update_it[(int)SCULPT_active_vertex_get(ss)]; } @@ -340,16 +338,14 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent const bool create_face_set = RNA_boolean_get(op->ptr, "create_face_set"); SculptCursorGeometryInfo sgi; - float mouse[2]; - mouse[0] = event->mval[0]; - mouse[1] = event->mval[1]; + const float mval_fl[2] = {UNPACK2(event->mval)}; SCULPT_vertex_random_access_ensure(ss); op->customdata = MEM_mallocN(sizeof(float[2]), "initial mouse position"); - copy_v2_v2(op->customdata, mouse); + copy_v2_v2(op->customdata, mval_fl); - SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false); + SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false); BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.c b/source/blender/editors/sculpt_paint/sculpt_ops.c index 2b5a20205bd..f16763be735 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.c +++ b/source/blender/editors/sculpt_paint/sculpt_ops.c @@ -84,6 +84,7 @@ #include "WM_toolsystem.h" #include "WM_types.h" +#include "ED_image.h" #include "ED_object.h" #include "ED_screen.h" #include "ED_sculpt.h" @@ -349,7 +350,7 @@ void ED_object_sculptmode_enter_ex(Main *bmain, Paint *paint = BKE_paint_get_active_from_paintmode(scene, PAINT_MODE_SCULPT); BKE_paint_init(bmain, scene, PAINT_MODE_SCULPT, PAINT_CURSOR_SCULPT); - paint_cursor_start(paint, SCULPT_mode_poll_view3d); + ED_paint_cursor_start(paint, SCULPT_mode_poll_view3d); /* Check dynamic-topology flag; re-enter dynamic-topology mode when changing modes, * As long as no data was added that is not supported. */ @@ -637,16 +638,16 @@ static int vertex_to_loop_colors_exec(bContext *C, wmOperator *UNUSED(op)) if (MPropCol_layer_n == -1) { return OPERATOR_CANCELLED; } - MPropCol *vertcols = CustomData_get_layer_n(&mesh->vdata, CD_PROP_COLOR, MPropCol_layer_n); + const MPropCol *vertcols = CustomData_get_layer_n(&mesh->vdata, CD_PROP_COLOR, MPropCol_layer_n); - MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP); - MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY); + const MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP); + const MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY); for (int i = 0; i < mesh->totpoly; i++) { - MPoly *c_poly = &polys[i]; + const MPoly *c_poly = &polys[i]; for (int j = 0; j < c_poly->totloop; j++) { int loop_index = c_poly->loopstart + j; - MLoop *c_loop = &loops[c_poly->loopstart + j]; + const MLoop *c_loop = &loops[c_poly->loopstart + j]; float srgb_color[4]; linearrgb_to_srgb_v4(srgb_color, vertcols[c_loop->v].color); loopcols[loop_index].r = (char)(srgb_color[0] * 255); @@ -711,7 +712,8 @@ static int loop_to_vertex_colors_exec(bContext *C, wmOperator *UNUSED(op)) if (mloopcol_layer_n == -1) { return OPERATOR_CANCELLED; } - MLoopCol *loopcols = CustomData_get_layer_n(&mesh->ldata, CD_PROP_BYTE_COLOR, mloopcol_layer_n); + const MLoopCol *loopcols = CustomData_get_layer_n( + &mesh->ldata, CD_PROP_BYTE_COLOR, mloopcol_layer_n); const int MPropCol_layer_n = CustomData_get_active_layer(&mesh->vdata, CD_PROP_COLOR); if (MPropCol_layer_n == -1) { @@ -719,14 +721,14 @@ static int loop_to_vertex_colors_exec(bContext *C, wmOperator *UNUSED(op)) } MPropCol *vertcols = CustomData_get_layer_n(&mesh->vdata, CD_PROP_COLOR, MPropCol_layer_n); - MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP); - MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY); + const MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP); + const MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY); for (int i = 0; i < mesh->totpoly; i++) { - MPoly *c_poly = &polys[i]; + const MPoly *c_poly = &polys[i]; for (int j = 0; j < c_poly->totloop; j++) { int loop_index = c_poly->loopstart + j; - MLoop *c_loop = &loops[c_poly->loopstart + j]; + const MLoop *c_loop = &loops[c_poly->loopstart + j]; vertcols[c_loop->v].color[0] = (loopcols[loop_index].r / 255.0f); vertcols[c_loop->v].color[1] = (loopcols[loop_index].g / 255.0f); vertcols[c_loop->v].color[2] = (loopcols[loop_index].b / 255.0f); @@ -780,8 +782,7 @@ static int sculpt_sample_color_invoke(bContext *C, wmOperator *op, const wmEvent } float color_srgb[3]; - copy_v3_v3(color_srgb, active_vertex_color); - IMB_colormanagement_scene_linear_to_srgb_v3(color_srgb); + IMB_colormanagement_scene_linear_to_srgb_v3(color_srgb, active_vertex_color); BKE_brush_color_set(scene, brush, color_srgb); WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, brush); @@ -1043,6 +1044,10 @@ static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEven Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Object *ob = CTX_data_active_object(C); SculptSession *ss = ob->sculpt; + View3D *v3d = CTX_wm_view3d(C); + if (v3d->shading.type == OB_SOLID) { + v3d->shading.color_type = V3D_SHADING_VERTEX_COLOR; + } BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); @@ -1056,10 +1061,8 @@ static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEven /* Tools that are not brushes do not have the brush gizmo to update the vertex as the mouse move, * so it needs to be updated here. */ SculptCursorGeometryInfo sgi; - float mouse[2]; - mouse[0] = event->mval[0]; - mouse[1] = event->mval[1]; - SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false); + const float mval_fl[2] = {UNPACK2(event->mval)}; + SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false); SCULPT_undo_push_begin(ob, "Mask by color"); BKE_sculpt_color_layer_create_if_needed(ob); @@ -1094,7 +1097,7 @@ static void SCULPT_OT_mask_by_color(wmOperatorType *ot) /* identifiers */ ot->name = "Mask by Color"; ot->idname = "SCULPT_OT_mask_by_color"; - ot->description = "Creates a mask based on the sculpt vertex colors"; + ot->description = "Creates a mask based on the active color attribute"; /* api callbacks */ ot->invoke = sculpt_mask_by_color_invoke; diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.c b/source/blender/editors/sculpt_paint/sculpt_paint_color.c index 7a8a6e8e484..fa9f24377da 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.c @@ -124,7 +124,7 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata, copy_v3_v3(brush_color, ss->cache->invert ? BKE_brush_secondary_color_get(ss->scene, brush) : BKE_brush_color_get(ss->scene, brush)); - IMB_colormanagement_srgb_to_scene_linear_v3(brush_color); + IMB_colormanagement_srgb_to_scene_linear_v3(brush_color, brush_color); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); @@ -380,6 +380,15 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + float brush_delta[3]; + + if (brush->flag & BRUSH_ANCHORED) { + copy_v3_v3(brush_delta, ss->cache->grab_delta_symmetry); + } + else { + sub_v3_v3v3(brush_delta, ss->cache->location, ss->cache->last_location); + } + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; @@ -404,7 +413,7 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata, switch (brush->smear_deform_type) { case BRUSH_SMEAR_DEFORM_DRAG: - sub_v3_v3v3(current_disp, ss->cache->location, ss->cache->last_location); + copy_v3_v3(current_disp, brush_delta); break; case BRUSH_SMEAR_DEFORM_PINCH: sub_v3_v3v3(current_disp, ss->cache->location, vd.co); @@ -529,12 +538,10 @@ void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode const int totvert = SCULPT_vertex_count_get(ss); - if (SCULPT_stroke_is_first_brush_step(ss->cache)) { - if (!ss->cache->prev_colors) { - ss->cache->prev_colors = MEM_callocN(sizeof(float[4]) * totvert, "prev colors"); - for (int i = 0; i < totvert; i++) { - SCULPT_vertex_color_get(ss, i, ss->cache->prev_colors[i]); - } + if (!ss->cache->prev_colors) { + ss->cache->prev_colors = MEM_callocN(sizeof(float[4]) * totvert, "prev colors"); + for (int i = 0; i < totvert; i++) { + SCULPT_vertex_color_get(ss, i, ss->cache->prev_colors[i]); } } diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_image.cc b/source/blender/editors/sculpt_paint/sculpt_paint_image.cc index df1ccc0fbe9..975a8f21aaf 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_image.cc +++ b/source/blender/editors/sculpt_paint/sculpt_paint_image.cc @@ -360,6 +360,86 @@ static void do_paint_pixels(void *__restrict userdata, node_data.flags.dirty |= pixels_updated; } +static void undo_region_tiles( + ImBuf *ibuf, int x, int y, int w, int h, int *tx, int *ty, int *tw, int *th) +{ + int srcx = 0, srcy = 0; + IMB_rectclip(ibuf, nullptr, &x, &y, &srcx, &srcy, &w, &h); + *tw = ((x + w - 1) >> ED_IMAGE_UNDO_TILE_BITS); + *th = ((y + h - 1) >> ED_IMAGE_UNDO_TILE_BITS); + *tx = (x >> ED_IMAGE_UNDO_TILE_BITS); + *ty = (y >> ED_IMAGE_UNDO_TILE_BITS); +} + +static void push_undo(const NodeData &node_data, + Image &image, + ImageUser &image_user, + const image::ImageTileWrapper &image_tile, + ImBuf &image_buffer, + ImBuf **tmpibuf) +{ + for (const UDIMTileUndo &tile_undo : node_data.undo_regions) { + if (tile_undo.tile_number != image_tile.get_tile_number()) { + continue; + } + int tilex, tiley, tilew, tileh; + ListBase *undo_tiles = ED_image_paint_tile_list_get(); + undo_region_tiles(&image_buffer, + tile_undo.region.xmin, + tile_undo.region.ymin, + BLI_rcti_size_x(&tile_undo.region), + BLI_rcti_size_y(&tile_undo.region), + &tilex, + &tiley, + &tilew, + &tileh); + for (int ty = tiley; ty <= tileh; ty++) { + for (int tx = tilex; tx <= tilew; tx++) { + ED_image_paint_tile_push(undo_tiles, + &image, + &image_buffer, + tmpibuf, + &image_user, + tx, + ty, + nullptr, + nullptr, + true, + true); + } + } + } +} + +static void do_push_undo_tile(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + TexturePaintingUserData *data = static_cast<TexturePaintingUserData *>(userdata); + PBVHNode *node = data->nodes[n]; + + NodeData &node_data = BKE_pbvh_pixels_node_data_get(*node); + Image *image = data->image_data.image; + ImageUser *image_user = data->image_data.image_user; + + ImBuf *tmpibuf = nullptr; + ImageUser local_image_user = *image_user; + LISTBASE_FOREACH (ImageTile *, tile, &image->tiles) { + image::ImageTileWrapper image_tile(tile); + local_image_user.tile = image_tile.get_tile_number(); + ImBuf *image_buffer = BKE_image_acquire_ibuf(image, &local_image_user, nullptr); + if (image_buffer == nullptr) { + continue; + } + + push_undo(node_data, *image, *image_user, image_tile, *image_buffer, &tmpibuf); + BKE_image_release_ibuf(image, image_buffer, nullptr); + } + if (tmpibuf) { + IMB_freeImBuf(tmpibuf); + } +} + static void do_mark_dirty_regions(void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls)) @@ -380,8 +460,9 @@ bool SCULPT_paint_image_canvas_get(PaintModeSettings *paint_mode_settings, Image **r_image, ImageUser **r_image_user) { - BLI_assert(r_image); - BLI_assert(r_image_user); + *r_image = nullptr; + *r_image_user = nullptr; + ImageData image_data; if (!ImageData::init_active_image(ob, &image_data, paint_mode_settings)) { return false; @@ -421,6 +502,7 @@ void SCULPT_do_paint_brush_image( TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, do_push_undo_tile, &settings); BLI_task_parallel_range(0, totnode, &data, do_paint_pixels, &settings); TaskParallelSettings settings_flush; diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.c b/source/blender/editors/sculpt_paint/sculpt_transform.c index b3616254b26..48033f3407e 100644 --- a/source/blender/editors/sculpt_paint/sculpt_transform.c +++ b/source/blender/editors/sculpt_paint/sculpt_transform.c @@ -16,6 +16,7 @@ #include "BKE_brush.h" #include "BKE_context.h" +#include "BKE_kelvinlet.h" #include "BKE_mesh.h" #include "BKE_mesh_mapping.h" #include "BKE_object.h" @@ -33,6 +34,7 @@ #include "ED_object.h" #include "ED_screen.h" #include "ED_sculpt.h" +#include "ED_view3d.h" #include "paint_intern.h" #include "sculpt_intern.h" @@ -54,6 +56,10 @@ void ED_sculpt_init_transform(struct bContext *C, Object *ob) copy_v4_v4(ss->init_pivot_rot, ss->pivot_rot); copy_v3_v3(ss->init_pivot_scale, ss->pivot_scale); + copy_v3_v3(ss->prev_pivot_pos, ss->pivot_pos); + copy_v4_v4(ss->prev_pivot_rot, ss->pivot_rot); + copy_v3_v3(ss->prev_pivot_scale, ss->pivot_scale); + SCULPT_undo_push_begin(ob, "Transform"); BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false); @@ -61,10 +67,18 @@ void ED_sculpt_init_transform(struct bContext *C, Object *ob) SCULPT_vertex_random_access_ensure(ss); SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS); + + if (sd->transform_mode == SCULPT_TRANSFORM_MODE_RADIUS_ELASTIC) { + ss->filter_cache->transform_displacement_mode = SCULPT_TRANSFORM_DISPLACEMENT_INCREMENTAL; + } + else { + ss->filter_cache->transform_displacement_mode = SCULPT_TRANSFORM_DISPLACEMENT_ORIGINAL; + } } static void sculpt_transform_matrices_init(SculptSession *ss, const char symm, + const SculptTransformDisplacementMode t_mode, float r_transform_mats[8][4][4]) { @@ -73,9 +87,18 @@ static void sculpt_transform_matrices_init(SculptSession *ss, transform_mat[4][4]; float start_pivot_pos[3], start_pivot_rot[4], start_pivot_scale[3]; - copy_v3_v3(start_pivot_pos, ss->init_pivot_pos); - copy_v4_v4(start_pivot_rot, ss->init_pivot_rot); - copy_v3_v3(start_pivot_scale, ss->init_pivot_scale); + switch (t_mode) { + case SCULPT_TRANSFORM_DISPLACEMENT_ORIGINAL: + copy_v3_v3(start_pivot_pos, ss->init_pivot_pos); + copy_v4_v4(start_pivot_rot, ss->init_pivot_rot); + copy_v3_v3(start_pivot_scale, ss->init_pivot_scale); + break; + case SCULPT_TRANSFORM_DISPLACEMENT_INCREMENTAL: + copy_v3_v3(start_pivot_pos, ss->prev_pivot_pos); + copy_v4_v4(start_pivot_rot, ss->prev_pivot_rot); + copy_v3_v3(start_pivot_scale, ss->prev_pivot_scale); + break; + } for (int i = 0; i < PAINT_SYMM_AREAS; i++) { ePaintSymmetryAreas v_symm = i; @@ -134,16 +157,26 @@ static void sculpt_transform_task_cb(void *__restrict userdata, SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_COORDS); BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); + float *start_co; float transformed_co[3], orig_co[3], disp[3]; float fade = vd.mask ? *vd.mask : 0.0f; copy_v3_v3(orig_co, orig_data.co); char symm_area = SCULPT_get_vertex_symm_area(orig_co); - copy_v3_v3(transformed_co, orig_co); + switch (ss->filter_cache->transform_displacement_mode) { + case SCULPT_TRANSFORM_DISPLACEMENT_ORIGINAL: + start_co = orig_co; + break; + case SCULPT_TRANSFORM_DISPLACEMENT_INCREMENTAL: + start_co = vd.co; + break; + } + + copy_v3_v3(transformed_co, start_co); mul_m4_v3(data->transform_mats[(int)symm_area], transformed_co); - sub_v3_v3v3(disp, transformed_co, orig_co); + sub_v3_v3v3(disp, transformed_co, start_co); mul_v3_fl(disp, 1.0f - fade); - add_v3_v3v3(vd.co, orig_co, disp); + add_v3_v3v3(vd.co, start_co, disp); if (vd.mvert) { BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); @@ -165,7 +198,8 @@ static void sculpt_transform_all_vertices(Sculpt *sd, Object *ob) .nodes = ss->filter_cache->nodes, }; - sculpt_transform_matrices_init(ss, symm, data.transform_mats); + sculpt_transform_matrices_init( + ss, symm, ss->filter_cache->transform_displacement_mode, data.transform_mats); /* Regular transform applies all symmetry passes at once as it is split by symmetry areas * (each vertex can only be transformed once by the transform matrix of its area). */ @@ -175,6 +209,98 @@ static void sculpt_transform_all_vertices(Sculpt *sd, Object *ob) 0, ss->filter_cache->totnode, &data, sculpt_transform_task_cb, &settings); } +static void sculpt_elastic_transform_task_cb(void *__restrict userdata, + const int i, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + PBVHNode *node = data->nodes[i]; + + float(*proxy)[3] = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[i])->co; + + SculptOrigVertData orig_data; + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i]); + + KelvinletParams params; + /* TODO(pablodp606): These parameters can be exposed if needed as transform strength and volume + * preservation like in the elastic deform brushes. Setting them to the same default as elastic + * deform triscale grab because they work well in most cases. */ + const float force = 1.0f; + const float shear_modulus = 1.0f; + const float poisson_ratio = 0.4f; + BKE_kelvinlet_init_params( + ¶ms, data->elastic_transform_radius, force, shear_modulus, poisson_ratio); + + SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_COORDS); + + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { + SCULPT_orig_vert_data_update(&orig_data, &vd); + float transformed_co[3], orig_co[3], disp[3]; + const float fade = vd.mask ? *vd.mask : 0.0f; + copy_v3_v3(orig_co, orig_data.co); + + copy_v3_v3(transformed_co, vd.co); + mul_m4_v3(data->elastic_transform_mat, transformed_co); + sub_v3_v3v3(disp, transformed_co, vd.co); + + float final_disp[3]; + BKE_kelvinlet_grab_triscale(final_disp, ¶ms, vd.co, data->elastic_transform_pivot, disp); + mul_v3_fl(final_disp, 20.0f * (1.0f - fade)); + + copy_v3_v3(proxy[vd.i], final_disp); + + if (vd.mvert) { + BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + } + } + BKE_pbvh_vertex_iter_end; + + BKE_pbvh_node_mark_update(node); +} + +static void sculpt_transform_radius_elastic(Sculpt *sd, Object *ob, const float transform_radius) +{ + SculptSession *ss = ob->sculpt; + BLI_assert(ss->filter_cache->transform_displacement_mode == + SCULPT_TRANSFORM_DISPLACEMENT_INCREMENTAL); + + const char symm = SCULPT_mesh_symmetry_xyz_get(ob); + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .nodes = ss->filter_cache->nodes, + .elastic_transform_radius = transform_radius, + }; + + sculpt_transform_matrices_init( + ss, symm, ss->filter_cache->transform_displacement_mode, data.transform_mats); + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->totnode); + + /* Elastic transform needs to apply all transform matrices to all vertices and then combine the + * displacement proxies as all vertices are modified by all symmetry passes. */ + for (ePaintSymmetryFlags symmpass = 0; symmpass <= symm; symmpass++) { + if (SCULPT_is_symmetry_iteration_valid(symmpass, symm)) { + flip_v3_v3(data.elastic_transform_pivot, ss->pivot_pos, symmpass); + flip_v3_v3(data.elastic_transform_pivot_init, ss->init_pivot_pos, symmpass); + + printf( + "%.2f %.2f %.2f\n", ss->init_pivot_pos[0], ss->init_pivot_pos[1], ss->init_pivot_pos[2]); + + const int symm_area = SCULPT_get_vertex_symm_area(data.elastic_transform_pivot); + copy_m4_m4(data.elastic_transform_mat, data.transform_mats[symm_area]); + BLI_task_parallel_range( + 0, ss->filter_cache->totnode, &data, sculpt_elastic_transform_task_cb, &settings); + } + } + SCULPT_combine_transform_proxies(sd, ob); +} + void ED_sculpt_update_modal_transform(struct bContext *C, Object *ob) { Sculpt *sd = CTX_data_tool_settings(C)->sculpt; @@ -184,7 +310,36 @@ void ED_sculpt_update_modal_transform(struct bContext *C, Object *ob) SCULPT_vertex_random_access_ensure(ss); BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false); - sculpt_transform_all_vertices(sd, ob); + switch (sd->transform_mode) { + case SCULPT_TRANSFORM_MODE_ALL_VERTICES: { + sculpt_transform_all_vertices(sd, ob); + break; + } + case SCULPT_TRANSFORM_MODE_RADIUS_ELASTIC: { + Brush *brush = BKE_paint_brush(&sd->paint); + Scene *scene = CTX_data_scene(C); + float transform_radius; + + if (BKE_brush_use_locked_size(scene, brush)) { + transform_radius = BKE_brush_unprojected_radius_get(scene, brush); + } + else { + ViewContext vc; + + ED_view3d_viewcontext_init(C, &vc, depsgraph); + + transform_radius = paint_calc_object_space_radius( + &vc, ss->init_pivot_pos, BKE_brush_size_get(scene, brush)); + } + + sculpt_transform_radius_elastic(sd, ob, transform_radius); + break; + } + } + + copy_v3_v3(ss->prev_pivot_pos, ss->pivot_pos); + copy_v4_v4(ss->prev_pivot_rot, ss->pivot_rot); + copy_v3_v3(ss->prev_pivot_scale, ss->pivot_scale); if (ss->deform_modifiers_active || ss->shapekey_active) { SCULPT_flush_stroke_deform(sd, ob, true); @@ -267,10 +422,11 @@ static int sculpt_set_pivot_position_exec(bContext *C, wmOperator *op) /* Pivot to ray-cast surface. */ else if (mode == SCULPT_PIVOT_POSITION_CURSOR_SURFACE) { float stroke_location[3]; - float mouse[2]; - mouse[0] = RNA_float_get(op->ptr, "mouse_x"); - mouse[1] = RNA_float_get(op->ptr, "mouse_y"); - if (SCULPT_stroke_get_location(C, stroke_location, mouse)) { + const float mval[2] = { + RNA_float_get(op->ptr, "mouse_x"), + RNA_float_get(op->ptr, "mouse_y"), + }; + if (SCULPT_stroke_get_location(C, stroke_location, mval)) { copy_v3_v3(ss->pivot_pos, stroke_location); } } diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index 5867dc558de..e82f14b1ca7 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -50,6 +50,7 @@ #include "WM_api.h" #include "WM_types.h" +#include "ED_geometry.h" #include "ED_object.h" #include "ED_sculpt.h" #include "ED_undo.h" @@ -105,7 +106,7 @@ typedef struct UndoSculpt { } UndoSculpt; typedef struct SculptAttrRef { - AttributeDomain domain; + eAttrDomain domain; int type; char name[MAX_CUSTOMDATA_LAYER_NAME]; bool was_set; @@ -1265,7 +1266,7 @@ static SculptUndoNode *sculpt_undo_face_sets_push(Object *ob, SculptUndoType typ unode->face_sets = MEM_callocN(me->totpoly * sizeof(int), "sculpt face sets"); - int *face_sets = CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS); + const int *face_sets = CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS); for (int i = 0; i < me->totpoly; i++) { unode->face_sets[i] = face_sets[i]; } @@ -1464,7 +1465,7 @@ static bool sculpt_attribute_ref_equals(SculptAttrRef *a, SculptAttrRef *b) static void sculpt_save_active_attribute(Object *ob, SculptAttrRef *attr) { Mesh *me = BKE_object_get_original_mesh(ob); - CustomDataLayer *layer; + const CustomDataLayer *layer; if (ob && me && (layer = BKE_id_attributes_active_color_get((ID *)me))) { attr->domain = BKE_id_attribute_domain((ID *)me, layer); @@ -1565,6 +1566,24 @@ static void sculpt_undo_set_active_layer(struct bContext *C, SculptAttrRef *attr CustomDataLayer *layer; layer = BKE_id_attribute_find(&me->id, attr->name, attr->type, attr->domain); + /* Temporary fix for T97408. This is a fundamental + * bug in the undo stack; the operator code needs to push + * an extra undo step before running an operator if a + * non-memfile undo system is active. + * + * For now, detect if the layer does exist but with a different + * domain and just unconvert it. + */ + if (!layer) { + layer = BKE_id_attribute_search(&me->id, attr->name, CD_MASK_PROP_ALL, ATTR_DOMAIN_MASK_ALL); + eAttrDomain domain = layer ? BKE_id_attribute_domain(&me->id, layer) : ATTR_DOMAIN_NUM; + + if (layer && ED_geometry_attribute_convert( + me, attr->name, layer->type, domain, attr->type, attr->domain)) { + layer = BKE_id_attribute_find(&me->id, attr->name, attr->type, attr->domain); + } + } + if (!layer) { /* Memfile undo killed the layer; re-create it. */ CustomData *cdata = attr->domain == ATTR_DOMAIN_POINT ? &me->vdata : &me->ldata; diff --git a/source/blender/editors/sound/sound_ops.c b/source/blender/editors/sound/sound_ops.c index a63ed142ed8..2a8a2be8b65 100644 --- a/source/blender/editors/sound/sound_ops.c +++ b/source/blender/editors/sound/sound_ops.c @@ -761,7 +761,8 @@ static int sound_pack_exec(bContext *C, wmOperator *op) sound->packedfile = BKE_packedfile_new( op->reports, sound->filepath, ID_BLEND_PATH(bmain, &sound->id)); - BKE_sound_load(bmain, sound); + + DEG_id_tag_update_ex(bmain, &sound->id, ID_RECALC_AUDIO); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c index b972ccbaf89..aff888818e0 100644 --- a/source/blender/editors/space_action/action_select.c +++ b/source/blender/editors/space_action/action_select.c @@ -887,6 +887,7 @@ void ACTION_OT_select_circle(wmOperatorType *ot) ot->exec = action_circle_select_exec; ot->poll = ED_operator_action_active; ot->cancel = WM_gesture_circle_cancel; + ot->get_name = ED_select_circle_get_name; /* flags */ ot->flag = OPTYPE_UNDO; diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c index 1d0061ab7d8..052af39319c 100644 --- a/source/blender/editors/space_buttons/space_buttons.c +++ b/source/blender/editors/space_buttons/space_buttons.c @@ -724,6 +724,9 @@ static void buttons_area_listener(const wmSpaceTypeListenerParams *params) /* Needed to refresh context path when changing active particle system index. */ buttons_area_redraw(area, BCONTEXT_PARTICLE); break; + case ND_DRAW_ANIMVIZ: + buttons_area_redraw(area, BCONTEXT_OBJECT); + break; default: /* Not all object RNA props have a ND_ notifier (yet) */ ED_area_tag_redraw(area); diff --git a/source/blender/editors/space_clip/clip_buttons.c b/source/blender/editors/space_clip/clip_buttons.c index e24d461019b..72df2b74b11 100644 --- a/source/blender/editors/space_clip/clip_buttons.c +++ b/source/blender/editors/space_clip/clip_buttons.c @@ -303,7 +303,7 @@ static void marker_block_handler(bContext *C, void *arg_cb, int event) cb->marker->pattern_corners[a][1] *= scale_y; } - BKE_tracking_marker_clamp(cb->marker, CLAMP_PAT_DIM); + BKE_tracking_marker_clamp_search_size(cb->marker); ok = true; } @@ -319,7 +319,7 @@ static void marker_block_handler(bContext *C, void *arg_cb, int event) sub_v2_v2v2(cb->marker->search_min, delta, side); add_v2_v2v2(cb->marker->search_max, delta, side); - BKE_tracking_marker_clamp(cb->marker, CLAMP_SEARCH_POS); + BKE_tracking_marker_clamp_search_position(cb->marker); ok = true; } @@ -340,7 +340,7 @@ static void marker_block_handler(bContext *C, void *arg_cb, int event) cb->marker->search_max[0] += dim[0]; cb->marker->search_max[1] += dim[1]; - BKE_tracking_marker_clamp(cb->marker, CLAMP_SEARCH_DIM); + BKE_tracking_marker_clamp_search_size(cb->marker); ok = true; } diff --git a/source/blender/editors/space_clip/clip_draw.c b/source/blender/editors/space_clip/clip_draw.c index 99524184323..7800ce797aa 100644 --- a/source/blender/editors/space_clip/clip_draw.c +++ b/source/blender/editors/space_clip/clip_draw.c @@ -676,6 +676,40 @@ static void track_colors(MovieTrackingTrack *track, int act, float col[3], float } } +static void set_draw_marker_area_color(const MovieTrackingTrack *track, + const MovieTrackingMarker *marker, + const bool is_track_active, + const bool is_area_selected, + const float color[3], + const float selected_color[3]) +{ + if (track->flag & TRACK_LOCKED) { + if (is_track_active) { + immUniformThemeColor(TH_ACT_MARKER); + } + else if (is_area_selected) { + immUniformThemeColorShade(TH_LOCK_MARKER, 64); + } + else { + immUniformThemeColor(TH_LOCK_MARKER); + } + } + else if (marker->flag & MARKER_DISABLED) { + if (is_track_active) { + immUniformThemeColor(TH_ACT_MARKER); + } + else if (is_area_selected) { + immUniformThemeColorShade(TH_DIS_MARKER, 128); + } + else { + immUniformThemeColor(TH_DIS_MARKER); + } + } + else { + immUniformColor3fv(is_area_selected ? selected_color : color); + } +} + static void draw_marker_areas(SpaceClip *sc, MovieTrackingTrack *track, MovieTrackingMarker *marker, @@ -785,31 +819,7 @@ static void draw_marker_areas(SpaceClip *sc, GPU_matrix_push(); GPU_matrix_translate_2fv(marker_pos); - if (track->flag & TRACK_LOCKED) { - if (act) { - immUniformThemeColor(TH_ACT_MARKER); - } - else if (track->pat_flag & SELECT) { - immUniformThemeColorShade(TH_LOCK_MARKER, 64); - } - else { - immUniformThemeColor(TH_LOCK_MARKER); - } - } - else if (marker->flag & MARKER_DISABLED) { - if (act) { - immUniformThemeColor(TH_ACT_MARKER); - } - else if (track->pat_flag & SELECT) { - immUniformThemeColorShade(TH_DIS_MARKER, 128); - } - else { - immUniformThemeColor(TH_DIS_MARKER); - } - } - else { - immUniformColor3fv((track->pat_flag & SELECT) ? scol : col); - } + set_draw_marker_area_color(track, marker, act, track->pat_flag & SELECT, col, scol); if (tiny) { immUniform1f("dash_width", 6.0f); @@ -834,6 +844,8 @@ static void draw_marker_areas(SpaceClip *sc, 0; if ((track->search_flag & SELECT) == sel && (sc->flag & SC_SHOW_MARKER_SEARCH) && show_search) { + set_draw_marker_area_color(track, marker, act, track->search_flag & SELECT, col, scol); + imm_draw_box_wire_2d(shdr_pos, marker->search_min[0], marker->search_min[1], diff --git a/source/blender/editors/space_clip/clip_editor.c b/source/blender/editors/space_clip/clip_editor.c index 87e88d094d7..cf7c3b51ae3 100644 --- a/source/blender/editors/space_clip/clip_editor.c +++ b/source/blender/editors/space_clip/clip_editor.c @@ -102,6 +102,16 @@ bool ED_space_clip_maskedit_poll(bContext *C) return false; } +bool ED_space_clip_maskedit_visible_splines_poll(bContext *C) +{ + if (!ED_space_clip_maskedit_poll(C)) { + return false; + } + + const SpaceClip *space_clip = CTX_wm_space_clip(C); + return space_clip->mask_info.draw_flag & MASK_DRAWFLAG_SPLINE; +} + bool ED_space_clip_maskedit_mask_poll(bContext *C) { if (ED_space_clip_maskedit_poll(C)) { @@ -117,6 +127,16 @@ bool ED_space_clip_maskedit_mask_poll(bContext *C) return false; } +bool ED_space_clip_maskedit_mask_visible_splines_poll(bContext *C) +{ + if (!ED_space_clip_maskedit_mask_poll(C)) { + return false; + } + + const SpaceClip *space_clip = CTX_wm_space_clip(C); + return space_clip->mask_info.draw_flag & MASK_DRAWFLAG_SPLINE; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -272,7 +292,7 @@ bool ED_space_clip_get_position(struct SpaceClip *sc, return true; } -bool ED_space_clip_color_sample(SpaceClip *sc, ARegion *region, int mval[2], float r_col[3]) +bool ED_space_clip_color_sample(SpaceClip *sc, ARegion *region, const int mval[2], float r_col[3]) { ImBuf *ibuf; float fx, fy, co[2]; diff --git a/source/blender/editors/space_clip/clip_intern.h b/source/blender/editors/space_clip/clip_intern.h index dd01c095479..7f9cf61b748 100644 --- a/source/blender/editors/space_clip/clip_intern.h +++ b/source/blender/editors/space_clip/clip_intern.h @@ -187,8 +187,10 @@ void clip_draw_sfra_efra(struct View2D *v2d, struct Scene *scene); /* tracking_ops.c */ -struct MovieTrackingTrack *tracking_marker_check_slide( - struct bContext *C, const struct wmEvent *event, int *r_area, int *r_action, int *r_corner); +/* Find track which can be slid in a proximity of the given event. + * Uses the same distance tolerance rule as the "Slide Marker" operator. */ +struct MovieTrackingTrack *tracking_find_slidable_track_in_proximity(struct bContext *C, + const float co[2]); void CLIP_OT_add_marker(struct wmOperatorType *ot); void CLIP_OT_add_marker_at_click(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_clip/clip_ops.c b/source/blender/editors/space_clip/clip_ops.c index e61c264ca06..f5bf850791a 100644 --- a/source/blender/editors/space_clip/clip_ops.c +++ b/source/blender/editors/space_clip/clip_ops.c @@ -777,7 +777,7 @@ void CLIP_OT_view_zoom_in(wmOperatorType *ot) ot->poll = ED_space_clip_view_clip_poll; /* flags */ - ot->flag |= OPTYPE_LOCK_BYPASS; + ot->flag = OPTYPE_LOCK_BYPASS; /* properties */ prop = RNA_def_float_vector(ot->srna, @@ -834,7 +834,7 @@ void CLIP_OT_view_zoom_out(wmOperatorType *ot) ot->poll = ED_space_clip_view_clip_poll; /* flags */ - ot->flag |= OPTYPE_LOCK_BYPASS; + ot->flag = OPTYPE_LOCK_BYPASS; /* properties */ prop = RNA_def_float_vector(ot->srna, @@ -883,7 +883,7 @@ void CLIP_OT_view_zoom_ratio(wmOperatorType *ot) ot->poll = ED_space_clip_view_clip_poll; /* flags */ - ot->flag |= OPTYPE_LOCK_BYPASS; + ot->flag = OPTYPE_LOCK_BYPASS; /* properties */ RNA_def_float(ot->srna, diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c index 91fef23019c..a73883e7624 100644 --- a/source/blender/editors/space_clip/space_clip.c +++ b/source/blender/editors/space_clip/space_clip.c @@ -619,6 +619,44 @@ static void clip_dropboxes(void) WM_dropbox_add(lb, "CLIP_OT_open", clip_drop_poll, clip_drop_copy, NULL, NULL); } +static bool clip_set_region_visible(const bContext *C, + ARegion *region, + const bool is_visible, + const short alignment, + const bool view_all_on_show) +{ + bool view_changed = false; + + if (is_visible) { + if (region && (region->flag & RGN_FLAG_HIDDEN)) { + region->flag &= ~RGN_FLAG_HIDDEN; + region->v2d.flag &= ~V2D_IS_INIT; + if (view_all_on_show) { + region->v2d.cur = region->v2d.tot; + } + view_changed = true; + } + if (region && region->alignment != alignment) { + region->alignment = alignment; + view_changed = true; + } + } + else { + if (region && !(region->flag & RGN_FLAG_HIDDEN)) { + region->flag |= RGN_FLAG_HIDDEN; + region->v2d.flag &= ~V2D_IS_INIT; + WM_event_remove_handlers((bContext *)C, ®ion->handlers); + view_changed = true; + } + if (region && region->alignment != RGN_ALIGN_NONE) { + region->alignment = RGN_ALIGN_NONE; + view_changed = true; + } + } + + return view_changed; +} + static void clip_refresh(const bContext *C, ScrArea *area) { wmWindowManager *wm = CTX_wm_manager(C); @@ -662,127 +700,14 @@ static void clip_refresh(const bContext *C, ScrArea *area) break; } - if (main_visible) { - if (region_main && (region_main->flag & RGN_FLAG_HIDDEN)) { - region_main->flag &= ~RGN_FLAG_HIDDEN; - region_main->v2d.flag &= ~V2D_IS_INIT; - view_changed = true; - } - - if (region_main && region_main->alignment != RGN_ALIGN_NONE) { - region_main->alignment = RGN_ALIGN_NONE; - view_changed = true; - } - } - else { - if (region_main && !(region_main->flag & RGN_FLAG_HIDDEN)) { - region_main->flag |= RGN_FLAG_HIDDEN; - region_main->v2d.flag &= ~V2D_IS_INIT; - WM_event_remove_handlers((bContext *)C, ®ion_main->handlers); - view_changed = true; - } - if (region_main && region_main->alignment != RGN_ALIGN_NONE) { - region_main->alignment = RGN_ALIGN_NONE; - view_changed = true; - } - } - - if (properties_visible) { - if (region_properties && (region_properties->flag & RGN_FLAG_HIDDEN)) { - region_properties->flag &= ~RGN_FLAG_HIDDEN; - region_properties->v2d.flag &= ~V2D_IS_INIT; - view_changed = true; - } - if (region_properties && region_properties->alignment != RGN_ALIGN_RIGHT) { - region_properties->alignment = RGN_ALIGN_RIGHT; - view_changed = true; - } - } - else { - if (region_properties && !(region_properties->flag & RGN_FLAG_HIDDEN)) { - region_properties->flag |= RGN_FLAG_HIDDEN; - region_properties->v2d.flag &= ~V2D_IS_INIT; - WM_event_remove_handlers((bContext *)C, ®ion_properties->handlers); - view_changed = true; - } - if (region_properties && region_properties->alignment != RGN_ALIGN_NONE) { - region_properties->alignment = RGN_ALIGN_NONE; - view_changed = true; - } - } - - if (tools_visible) { - if (region_tools && (region_tools->flag & RGN_FLAG_HIDDEN)) { - region_tools->flag &= ~RGN_FLAG_HIDDEN; - region_tools->v2d.flag &= ~V2D_IS_INIT; - view_changed = true; - } - if (region_tools && region_tools->alignment != RGN_ALIGN_LEFT) { - region_tools->alignment = RGN_ALIGN_LEFT; - view_changed = true; - } - } - else { - if (region_tools && !(region_tools->flag & RGN_FLAG_HIDDEN)) { - region_tools->flag |= RGN_FLAG_HIDDEN; - region_tools->v2d.flag &= ~V2D_IS_INIT; - WM_event_remove_handlers((bContext *)C, ®ion_tools->handlers); - view_changed = true; - } - if (region_tools && region_tools->alignment != RGN_ALIGN_NONE) { - region_tools->alignment = RGN_ALIGN_NONE; - view_changed = true; - } - } - - if (preview_visible) { - if (region_preview && (region_preview->flag & RGN_FLAG_HIDDEN)) { - region_preview->flag &= ~RGN_FLAG_HIDDEN; - region_preview->v2d.flag &= ~V2D_IS_INIT; - region_preview->v2d.cur = region_preview->v2d.tot; - view_changed = true; - } - if (region_preview && region_preview->alignment != RGN_ALIGN_NONE) { - region_preview->alignment = RGN_ALIGN_NONE; - view_changed = true; - } - } - else { - if (region_preview && !(region_preview->flag & RGN_FLAG_HIDDEN)) { - region_preview->flag |= RGN_FLAG_HIDDEN; - region_preview->v2d.flag &= ~V2D_IS_INIT; - WM_event_remove_handlers((bContext *)C, ®ion_preview->handlers); - view_changed = true; - } - if (region_preview && region_preview->alignment != RGN_ALIGN_NONE) { - region_preview->alignment = RGN_ALIGN_NONE; - view_changed = true; - } - } - - if (channels_visible) { - if (region_channels && (region_channels->flag & RGN_FLAG_HIDDEN)) { - region_channels->flag &= ~RGN_FLAG_HIDDEN; - region_channels->v2d.flag &= ~V2D_IS_INIT; - view_changed = true; - } - if (region_channels && region_channels->alignment != RGN_ALIGN_LEFT) { - region_channels->alignment = RGN_ALIGN_LEFT; - view_changed = true; - } - } - else { - if (region_channels && !(region_channels->flag & RGN_FLAG_HIDDEN)) { - region_channels->flag |= RGN_FLAG_HIDDEN; - region_channels->v2d.flag &= ~V2D_IS_INIT; - WM_event_remove_handlers((bContext *)C, ®ion_channels->handlers); - view_changed = true; - } - if (region_channels && region_channels->alignment != RGN_ALIGN_NONE) { - region_channels->alignment = RGN_ALIGN_NONE; - view_changed = true; - } - } + view_changed |= clip_set_region_visible(C, region_main, main_visible, RGN_ALIGN_NONE, false); + view_changed |= clip_set_region_visible( + C, region_properties, properties_visible, RGN_ALIGN_RIGHT, false); + view_changed |= clip_set_region_visible(C, region_tools, tools_visible, RGN_ALIGN_LEFT, false); + view_changed |= clip_set_region_visible( + C, region_preview, preview_visible, RGN_ALIGN_NONE, true); + view_changed |= clip_set_region_visible( + C, region_channels, channels_visible, RGN_ALIGN_LEFT, false); if (view_changed) { ED_area_init(wm, window, area); @@ -935,6 +860,7 @@ static void clip_main_region_draw(const bContext *C, ARegion *region) sc->mask_info.draw_flag, sc->mask_info.draw_type, sc->mask_info.overlay_mode, + sc->mask_info.blend_factor, mask_width, mask_height, aspx, diff --git a/source/blender/editors/space_clip/tracking_ops.c b/source/blender/editors/space_clip/tracking_ops.c index e7bdbfe7c68..ca224b04da5 100644 --- a/source/blender/editors/space_clip/tracking_ops.c +++ b/source/blender/editors/space_clip/tracking_ops.c @@ -336,35 +336,44 @@ void CLIP_OT_delete_marker(wmOperatorType *ot) /** \name Slide Marker Operator * \{ */ -enum { - SLIDE_ACTION_POS = 0, +typedef enum eSlideAction { + SLIDE_ACTION_NONE, + + SLIDE_ACTION_POS, SLIDE_ACTION_SIZE, SLIDE_ACTION_OFFSET, SLIDE_ACTION_TILT_SIZE, -}; +} eSlideAction; typedef struct { - short area, action; + short area; + eSlideAction action; MovieTrackingTrack *track; MovieTrackingMarker *marker; int mval[2]; int width, height; - float *min, *max, *pos, *offset, (*corners)[2]; - float spos[2]; + float *min, *max, *pos, (*corners)[2]; bool lock, accurate; /* Data to restore on cancel. */ - float old_search_min[2], old_search_max[2], old_pos[2], old_offset[2]; + float old_search_min[2], old_search_max[2], old_pos[2]; float old_corners[4][2]; float (*old_markers)[2]; } SlideMarkerData; -static void slide_marker_tilt_slider(const MovieTrackingMarker *marker, float r_slider[2]) +static void slide_marker_tilt_slider_relative(const float pattern_corners[4][2], float r_slider[2]) +{ + add_v2_v2v2(r_slider, pattern_corners[1], pattern_corners[2]); +} + +static void slide_marker_tilt_slider(const float marker_pos[2], + const float pattern_corners[4][2], + float r_slider[2]) { - add_v2_v2v2(r_slider, marker->pattern_corners[1], marker->pattern_corners[2]); - add_v2_v2(r_slider, marker->pos); + slide_marker_tilt_slider_relative(pattern_corners, r_slider); + add_v2_v2(r_slider, marker_pos); } static SlideMarkerData *create_slide_marker_data(SpaceClip *sc, @@ -373,7 +382,7 @@ static SlideMarkerData *create_slide_marker_data(SpaceClip *sc, const wmEvent *event, int area, int corner, - int action, + eSlideAction action, int width, int height) { @@ -389,29 +398,14 @@ static SlideMarkerData *create_slide_marker_data(SpaceClip *sc, if (area == TRACK_AREA_POINT) { data->pos = marker->pos; - data->offset = track->offset; } else if (area == TRACK_AREA_PAT) { - if (action == SLIDE_ACTION_SIZE) { - data->corners = marker->pattern_corners; - } - else if (action == SLIDE_ACTION_OFFSET) { - data->pos = marker->pos; - data->offset = track->offset; - data->old_markers = MEM_callocN(sizeof(*data->old_markers) * track->markersnr, - "slide markers"); - for (int a = 0; a < track->markersnr; a++) { - copy_v2_v2(data->old_markers[a], track->markers[a].pos); - } - } - else if (action == SLIDE_ACTION_POS) { + if (action == SLIDE_ACTION_POS) { data->corners = marker->pattern_corners; data->pos = marker->pattern_corners[corner]; - copy_v2_v2(data->spos, data->pos); } else if (action == SLIDE_ACTION_TILT_SIZE) { data->corners = marker->pattern_corners; - slide_marker_tilt_slider(marker, data->spos); } } else if (area == TRACK_AREA_SEARCH) { @@ -434,7 +428,6 @@ static SlideMarkerData *create_slide_marker_data(SpaceClip *sc, copy_v2_v2(data->old_search_min, marker->search_min); copy_v2_v2(data->old_search_max, marker->search_max); copy_v2_v2(data->old_pos, marker->pos); - copy_v2_v2(data->old_offset, track->offset); return data; } @@ -497,7 +490,7 @@ static int mouse_to_tilt_distance_squared(const MovieTrackingMarker *marker, int height) { float slider[2]; - slide_marker_tilt_slider(marker, slider); + slide_marker_tilt_slider(marker->pos, marker->pattern_corners, slider); return mouse_to_slide_zone_distance_squared(co, slider, width, height); } @@ -534,117 +527,96 @@ static bool slide_check_corners(float (*corners)[2]) return true; } -MovieTrackingTrack *tracking_marker_check_slide( - bContext *C, const wmEvent *event, int *r_area, int *r_action, int *r_corner) +static MovieTrackingTrack *tracking_marker_check_slide( + bContext *C, const float co[2], int *r_area, eSlideAction *r_action, int *r_corner) { const float distance_clip_squared = 12.0f * 12.0f; SpaceClip *sc = CTX_wm_space_clip(C); - ARegion *region = CTX_wm_region(C); - MovieClip *clip = ED_space_clip_get_clip(sc); - MovieTrackingTrack *track; - int width, height; - float co[2]; ListBase *tracksbase = BKE_tracking_get_active_tracks(&clip->tracking); - int framenr = ED_space_clip_get_clip_frame_number(sc); + const int framenr = ED_space_clip_get_clip_frame_number(sc); float global_min_distance_squared = FLT_MAX; - /* Sliding zone designator which is the closest to the mouse - * across all the tracks. - */ - int min_action = -1, min_area = 0, min_corner = -1; + /* Sliding zone designator which is the closest to the mouse across all the tracks. */ + eSlideAction min_action; + int min_area = 0, min_corner = -1; MovieTrackingTrack *min_track = NULL; + int width, height; ED_space_clip_get_size(sc, &width, &height); - if (width == 0 || height == 0) { return NULL; } - ED_clip_mouse_pos(sc, region, event->mval, co); + LISTBASE_FOREACH (MovieTrackingTrack *, track, tracksbase) { + if (!TRACK_VIEW_SELECTED(sc, track) || (track->flag & TRACK_LOCKED)) { + continue; + } - track = tracksbase->first; - while (track) { - if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_LOCKED) == 0) { - const MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr); - /* Sliding zone designator which is the closest to the mouse for - * the current tracks. - */ - float min_distance_squared = FLT_MAX; - int action = -1, area = 0, corner = -1; - - if ((marker->flag & MARKER_DISABLED) == 0) { - float distance_squared; - - /* We start checking with whether the mouse is close enough - * to the pattern offset area. - */ - distance_squared = mouse_to_offset_distance_squared(track, marker, co, width, height); - area = TRACK_AREA_POINT; - action = SLIDE_ACTION_POS; + const MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr); + if (marker->flag & MARKER_DISABLED) { + continue; + } - /* NOTE: All checks here are assuming there's no maximum distance - * limit, so checks are quite simple here. - * Actual distance clipping happens later once all the sliding - * zones are checked. - */ + /* We start checking with whether the mouse is close enough to the pattern offset area. */ + float distance_squared = mouse_to_offset_distance_squared(track, marker, co, width, height); + + /* Sliding zone designator which is the closest to the mouse for the current tracks. + * + * NOTE: All checks here are assuming there's no maximum distance limit, so checks are quite + * simple here. Actual distance clipping happens later once all the sliding zones are checked. + */ + float min_distance_squared = distance_squared; + int area = TRACK_AREA_POINT; + int action = SLIDE_ACTION_POS; + int corner = -1; + + /* If search area is visible, check how close to its sliding zones mouse is. */ + if (sc->flag & SC_SHOW_MARKER_SEARCH) { + distance_squared = mouse_to_search_corner_distance_squared(marker, co, 1, width, height); + if (distance_squared < min_distance_squared) { + area = TRACK_AREA_SEARCH; + action = SLIDE_ACTION_OFFSET; min_distance_squared = distance_squared; + } - /* If search area is visible, check how close to its sliding - * zones mouse is. - */ - if (sc->flag & SC_SHOW_MARKER_SEARCH) { - distance_squared = mouse_to_search_corner_distance_squared(marker, co, 1, width, height); - if (distance_squared < min_distance_squared) { - area = TRACK_AREA_SEARCH; - action = SLIDE_ACTION_OFFSET; - min_distance_squared = distance_squared; - } - - distance_squared = mouse_to_search_corner_distance_squared(marker, co, 0, width, height); - if (distance_squared < min_distance_squared) { - area = TRACK_AREA_SEARCH; - action = SLIDE_ACTION_SIZE; - min_distance_squared = distance_squared; - } - } - - /* If pattern area is visible, check which corner is closest to - * the mouse. - */ - if (sc->flag & SC_SHOW_MARKER_PATTERN) { - int current_corner = -1; - distance_squared = mouse_to_closest_pattern_corner_distance_squared( - marker, co, width, height, ¤t_corner); - if (distance_squared < min_distance_squared) { - area = TRACK_AREA_PAT; - action = SLIDE_ACTION_POS; - corner = current_corner; - min_distance_squared = distance_squared; - } + distance_squared = mouse_to_search_corner_distance_squared(marker, co, 0, width, height); + if (distance_squared < min_distance_squared) { + area = TRACK_AREA_SEARCH; + action = SLIDE_ACTION_SIZE; + min_distance_squared = distance_squared; + } + } - /* Here we also check whether the mouse is actually closer to - * the widget which controls scale and tilt. - */ - distance_squared = mouse_to_tilt_distance_squared(marker, co, width, height); - if (distance_squared < min_distance_squared) { - area = TRACK_AREA_PAT; - action = SLIDE_ACTION_TILT_SIZE; - min_distance_squared = distance_squared; - } - } + /* If pattern area is visible, check which corner is closest to the mouse. */ + if (sc->flag & SC_SHOW_MARKER_PATTERN) { + int current_corner = -1; + distance_squared = mouse_to_closest_pattern_corner_distance_squared( + marker, co, width, height, ¤t_corner); + if (distance_squared < min_distance_squared) { + area = TRACK_AREA_PAT; + action = SLIDE_ACTION_POS; + corner = current_corner; + min_distance_squared = distance_squared; + } - if (min_distance_squared < global_min_distance_squared) { - min_area = area; - min_action = action; - min_corner = corner; - min_track = track; - global_min_distance_squared = min_distance_squared; - } + /* Here we also check whether the mouse is actually closer to the widget which controls scale + * and tilt. */ + distance_squared = mouse_to_tilt_distance_squared(marker, co, width, height); + if (distance_squared < min_distance_squared) { + area = TRACK_AREA_PAT; + action = SLIDE_ACTION_TILT_SIZE; + min_distance_squared = distance_squared; } } - track = track->next; + if (min_distance_squared < global_min_distance_squared) { + min_area = area; + min_action = action; + min_corner = corner; + min_track = track; + global_min_distance_squared = min_distance_squared; + } } if (global_min_distance_squared < distance_clip_squared / sc->zoom) { @@ -659,9 +631,16 @@ MovieTrackingTrack *tracking_marker_check_slide( } return min_track; } + return NULL; } +struct MovieTrackingTrack *tracking_find_slidable_track_in_proximity(struct bContext *C, + const float co[2]) +{ + return tracking_marker_check_slide(C, co, NULL, NULL, NULL); +} + static void *slide_marker_customdata(bContext *C, const wmEvent *event) { SpaceClip *sc = CTX_wm_space_clip(C); @@ -672,7 +651,8 @@ static void *slide_marker_customdata(bContext *C, const wmEvent *event) float co[2]; void *customdata = NULL; int framenr = ED_space_clip_get_clip_frame_number(sc); - int area, action, corner; + eSlideAction action; + int area, corner; ED_space_clip_get_size(sc, &width, &height); @@ -682,7 +662,7 @@ static void *slide_marker_customdata(bContext *C, const wmEvent *event) ED_clip_mouse_pos(sc, region, event->mval, co); - track = tracking_marker_check_slide(C, event, &area, &action, &corner); + track = tracking_marker_check_slide(C, co, &area, &action, &corner); if (track != NULL) { MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr); customdata = create_slide_marker_data( @@ -718,14 +698,12 @@ static int slide_marker_invoke(bContext *C, wmOperator *op, const wmEvent *event static void cancel_mouse_slide(SlideMarkerData *data) { - MovieTrackingTrack *track = data->track; MovieTrackingMarker *marker = data->marker; memcpy(marker->pattern_corners, data->old_corners, sizeof(marker->pattern_corners)); copy_v2_v2(marker->search_min, data->old_search_min); copy_v2_v2(marker->search_max, data->old_search_max); copy_v2_v2(marker->pos, data->old_pos); - copy_v2_v2(track->offset, data->old_offset); if (data->old_markers != NULL) { for (int a = 0; a < data->track->markersnr; a++) { @@ -765,7 +743,6 @@ static void free_slide_data(SlideMarkerData *data) static int slide_marker_modal(bContext *C, wmOperator *op, const wmEvent *event) { SpaceClip *sc = CTX_wm_space_clip(C); - ARegion *region = CTX_wm_region(C); SlideMarkerData *data = (SlideMarkerData *)op->customdata; float dx, dy, mdelta[2]; @@ -804,110 +781,66 @@ static int slide_marker_modal(bContext *C, wmOperator *op, const wmEvent *event) } if (data->area == TRACK_AREA_POINT) { - if (data->action == SLIDE_ACTION_OFFSET) { - data->offset[0] = data->old_offset[0] + dx; - data->offset[1] = data->old_offset[1] + dy; - } - else { - data->pos[0] = data->old_pos[0] + dx; - data->pos[1] = data->old_pos[1] + dy; - } + data->pos[0] += dx; + data->pos[1] += dy; WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); DEG_id_tag_update(&sc->clip->id, 0); } else if (data->area == TRACK_AREA_PAT) { - if (data->action == SLIDE_ACTION_SIZE) { - float start[2], end[2]; - float scale; - - ED_clip_point_stable_pos(sc, region, data->mval[0], data->mval[1], &start[0], &start[1]); - - sub_v2_v2(start, data->old_pos); - - if (len_squared_v2(start) != 0.0f) { - float mval[2]; - - if (data->accurate) { - mval[0] = data->mval[0] + (event->mval[0] - data->mval[0]) / 5.0f; - mval[1] = data->mval[1] + (event->mval[1] - data->mval[1]) / 5.0f; - } - else { - mval[0] = event->mval[0]; - mval[1] = event->mval[1]; - } + if (data->action == SLIDE_ACTION_POS) { + float prev_pos[2]; + copy_v2_v2(prev_pos, data->pos); - ED_clip_point_stable_pos(sc, region, mval[0], mval[1], &end[0], &end[1]); - - sub_v2_v2(end, data->old_pos); - scale = len_v2(end) / len_v2(start); - - if (scale > 0.0f) { - for (int a = 0; a < 4; a++) { - mul_v2_v2fl(data->corners[a], data->old_corners[a], scale); - } - } - } - - BKE_tracking_marker_clamp(data->marker, CLAMP_PAT_DIM); - } - else if (data->action == SLIDE_ACTION_OFFSET) { - const float d[2] = {dx, dy}; - for (int a = 0; a < data->track->markersnr; a++) { - add_v2_v2v2(data->track->markers[a].pos, data->old_markers[a], d); - } - sub_v2_v2v2(data->offset, data->old_offset, d); - } - else if (data->action == SLIDE_ACTION_POS) { - float spos[2]; - - copy_v2_v2(spos, data->pos); - - data->pos[0] = data->spos[0] + dx; - data->pos[1] = data->spos[1] + dy; + data->pos[0] += dx; + data->pos[1] += dy; if (!slide_check_corners(data->corners)) { - copy_v2_v2(data->pos, spos); + copy_v2_v2(data->pos, prev_pos); } - /* Currently only patterns are allowed to have such - * combination of event and data. - */ - BKE_tracking_marker_clamp(data->marker, CLAMP_PAT_DIM); + /* Allow pattern to be arbitrary size and resize search area if needed. */ + BKE_tracking_marker_clamp_search_size(data->marker); } else if (data->action == SLIDE_ACTION_TILT_SIZE) { - const float mouse_delta[2] = {dx, dy}; - - /* Vector which connects marker position with tilt/scale sliding area before sliding - * began. */ - float start[2]; - sub_v2_v2v2(start, data->spos, data->old_pos); - start[0] *= data->width; - start[1] *= data->height; - - /* Vector which connects marker position with tilt/scale sliding area with the sliding - * delta applied. */ - float end[2]; - add_v2_v2v2(end, data->spos, mouse_delta); - sub_v2_v2(end, data->old_pos); - end[0] *= data->width; - end[1] *= data->height; + const float delta[2] = {dx, dy}; + + /* Slider position relative to the marker position using current state of pattern + * corners. */ + float slider[2]; + slide_marker_tilt_slider_relative(data->corners, slider); + + /* Vector which connects marker position with the slider state at the current corners + * state. + * The coordinate is in the pixel space. */ + float start_px[2]; + copy_v2_v2(start_px, slider); + start_px[0] *= data->width; + start_px[1] *= data->height; + + /* Vector which connects marker position with the slider state with the new mouse delta + * taken into account. + * The coordinate is in the pixel space. */ + float end_px[2]; + add_v2_v2v2(end_px, slider, delta); + end_px[0] *= data->width; + end_px[1] *= data->height; float scale = 1.0f; - if (len_squared_v2(start) != 0.0f) { - scale = len_v2(end) / len_v2(start); + if (len_squared_v2(start_px) != 0.0f) { + scale = len_v2(end_px) / len_v2(start_px); if (scale < 0.0f) { scale = 0.0; } } - const float angle = -angle_signed_v2v2(start, end); + const float angle = -angle_signed_v2v2(start_px, end_px); for (int a = 0; a < 4; a++) { float vec[2]; - mul_v2_v2fl(data->corners[a], data->old_corners[a], scale); + mul_v2_fl(data->corners[a], scale); copy_v2_v2(vec, data->corners[a]); vec[0] *= data->width; @@ -917,30 +850,32 @@ static int slide_marker_modal(bContext *C, wmOperator *op, const wmEvent *event) data->corners[a][1] = (vec[1] * cosf(angle) + vec[0] * sinf(angle)) / data->height; } - BKE_tracking_marker_clamp(data->marker, CLAMP_PAT_DIM); + BKE_tracking_marker_clamp_search_size(data->marker); } } else if (data->area == TRACK_AREA_SEARCH) { if (data->action == SLIDE_ACTION_SIZE) { - data->min[0] = data->old_search_min[0] - dx; - data->max[0] = data->old_search_max[0] + dx; + data->min[0] -= dx; + data->min[1] += dy; - data->min[1] = data->old_search_min[1] + dy; - data->max[1] = data->old_search_max[1] - dy; + data->max[0] += dx; + data->max[1] -= dy; - BKE_tracking_marker_clamp(data->marker, CLAMP_SEARCH_DIM); - } - else if (data->area == TRACK_AREA_SEARCH) { - const float d[2] = {dx, dy}; - add_v2_v2v2(data->min, data->old_search_min, d); - add_v2_v2v2(data->max, data->old_search_max, d); + BKE_tracking_marker_clamp_search_size(data->marker); } + else if (data->action == SLIDE_ACTION_OFFSET) { + const float delta[2] = {dx, dy}; + add_v2_v2(data->min, delta); + add_v2_v2(data->max, delta); - BKE_tracking_marker_clamp(data->marker, CLAMP_SEARCH_POS); + BKE_tracking_marker_clamp_search_position(data->marker); + } } data->marker->flag &= ~MARKER_TRACKED; + copy_v2_v2_int(data->mval, event->mval); + WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, NULL); break; @@ -1012,9 +947,9 @@ static int clear_track_path_exec(bContext *C, wmOperator *op) MovieClip *clip = ED_space_clip_get_clip(sc); MovieTracking *tracking = &clip->tracking; ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking); - int action = RNA_enum_get(op->ptr, "action"); + const eTrackClearAction action = RNA_enum_get(op->ptr, "action"); const bool clear_active = RNA_boolean_get(op->ptr, "clear_active"); - int framenr = ED_space_clip_get_clip_frame_number(sc); + const int framenr = ED_space_clip_get_clip_frame_number(sc); if (clear_active) { MovieTrackingTrack *track = BKE_tracking_track_get_active(tracking); diff --git a/source/blender/editors/space_clip/tracking_select.c b/source/blender/editors/space_clip/tracking_select.c index 5f940b817a9..28e304bfa74 100644 --- a/source/blender/editors/space_clip/tracking_select.c +++ b/source/blender/editors/space_clip/tracking_select.c @@ -273,7 +273,18 @@ void ed_tracking_deselect_all_plane_tracks(ListBase *plane_tracks_base) } } -static int mouse_select(bContext *C, const float co[2], const bool extend, const bool deselect_all) +static bool select_poll(bContext *C) +{ + SpaceClip *sc = CTX_wm_space_clip(C); + + if (sc) { + return sc->clip && sc->view == SC_VIEW_CLIP; + } + + return false; +} + +static int select_exec(bContext *C, wmOperator *op) { SpaceClip *sc = CTX_wm_space_clip(C); MovieClip *clip = ED_space_clip_get_clip(sc); @@ -281,12 +292,35 @@ static int mouse_select(bContext *C, const float co[2], const bool extend, const ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking); ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking); MovieTrackingTrack *act_track = BKE_tracking_track_get_active(tracking); - MovieTrackingTrack *track; - MovieTrackingPlaneTrack *plane_track; + const bool extend = RNA_boolean_get(op->ptr, "extend"); + const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all"); + + float co[2]; + RNA_float_get_array(op->ptr, "location", co); + + /* Special code which allows to slide a marker which belongs to currently selected but not yet + * active track. If such track is found activate it and return pass-though so that marker slide + * operator can be used immediately after. + * This logic makes it convenient to slide markers when left mouse selection is used. */ + if (!extend) { + MovieTrackingTrack *track = tracking_find_slidable_track_in_proximity(C, co); + if (track != NULL) { + MovieClip *clip_iter = ED_space_clip_get_clip(sc); + + clip_iter->tracking.act_track = track; + + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL); + DEG_id_tag_update(&clip_iter->id, ID_RECALC_SELECT); + + return OPERATOR_PASS_THROUGH; + } + } + float distance_to_track, distance_to_plane_track; - track = find_nearest_track(sc, tracksbase, co, &distance_to_track); - plane_track = find_nearest_plane_track(sc, plane_tracks_base, co, &distance_to_plane_track); + MovieTrackingTrack *track = find_nearest_track(sc, tracksbase, co, &distance_to_track); + MovieTrackingPlaneTrack *plane_track = find_nearest_plane_track( + sc, plane_tracks_base, co, &distance_to_plane_track); ClipViewLockState lock_state; ED_clip_view_lock_state_store(C, &lock_state); @@ -360,8 +394,6 @@ static int mouse_select(bContext *C, const float co[2], const bool extend, const else if (deselect_all) { ed_tracking_deselect_all_tracks(tracksbase); ed_tracking_deselect_all_plane_tracks(plane_tracks_base); - /* Mask as well if we are in combined mask / track view. */ - ED_mask_deselect_all(C); } ED_clip_view_lock_state_restore_no_jump(C, &lock_state); @@ -375,51 +407,12 @@ static int mouse_select(bContext *C, const float co[2], const bool extend, const return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH; } -static bool select_poll(bContext *C) -{ - SpaceClip *sc = CTX_wm_space_clip(C); - - if (sc) { - return sc->clip && sc->view == SC_VIEW_CLIP; - } - - return false; -} - -static int select_exec(bContext *C, wmOperator *op) -{ - float co[2]; - - RNA_float_get_array(op->ptr, "location", co); - const bool extend = RNA_boolean_get(op->ptr, "extend"); - const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all"); - - return mouse_select(C, co, extend, deselect_all); -} - static int select_invoke(bContext *C, wmOperator *op, const wmEvent *event) { SpaceClip *sc = CTX_wm_space_clip(C); ARegion *region = CTX_wm_region(C); float co[2]; - const bool extend = RNA_boolean_get(op->ptr, "extend"); - - if (!extend) { - MovieTrackingTrack *track = tracking_marker_check_slide(C, event, NULL, NULL, NULL); - - if (track) { - MovieClip *clip = ED_space_clip_get_clip(sc); - - clip->tracking.act_track = track; - - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL); - DEG_id_tag_update(&clip->id, ID_RECALC_SELECT); - - return OPERATOR_PASS_THROUGH; - } - } - ED_clip_mouse_pos(sc, region, event->mval, co); RNA_float_set_array(op->ptr, "location", co); @@ -835,6 +828,7 @@ void CLIP_OT_select_circle(wmOperatorType *ot) ot->modal = WM_gesture_circle_modal; ot->exec = circle_select_exec; ot->poll = ED_space_clip_tracking_poll; + ot->get_name = ED_select_circle_get_name; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc index e5a4919f548..87595ecdb88 100644 --- a/source/blender/editors/space_file/asset_catalog_tree_view.cc +++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc @@ -407,8 +407,15 @@ std::string AssetCatalogDropController::drop_tooltip_asset_list(const wmDrag &dr std::string basic_tip = is_multiple_assets ? TIP_("Move assets to catalog") : TIP_("Move asset to catalog"); - return basic_tip + ": " + catalog_item_.get_name() + " (" + catalog_item_.catalog_path().str() + - ")"; + basic_tip += ": " + catalog_item_.get_name(); + + /* Display the full catalog path, but only if it's not exactly the same as the already shown name + * (i.e. not a root level catalog with no parent). */ + if (catalog_item_.get_name() != catalog_item_.catalog_path().str()) { + basic_tip += " (" + catalog_item_.catalog_path().str() + ")"; + } + + return basic_tip; } bool AssetCatalogDropController::on_drop(struct bContext *C, const wmDrag &drag) diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h index 1ee6445f4ba..655a7983e2b 100644 --- a/source/blender/editors/space_file/file_intern.h +++ b/source/blender/editors/space_file/file_intern.h @@ -83,6 +83,7 @@ void FILE_OT_rename(struct wmOperatorType *ot); void FILE_OT_smoothscroll(struct wmOperatorType *ot); void FILE_OT_filepath_drop(struct wmOperatorType *ot); void FILE_OT_start_filter(struct wmOperatorType *ot); +void FILE_OT_edit_directory_path(struct wmOperatorType *ot); void FILE_OT_view_selected(struct wmOperatorType *ot); void file_directory_enter_handle(bContext *C, void *arg_unused, void *arg_but); diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index 578288ca289..62bdd583bc1 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -2914,9 +2914,9 @@ void FILE_OT_delete(struct wmOperatorType *ot) static int file_start_filter_exec(bContext *C, wmOperator *UNUSED(op)) { - ScrArea *area = CTX_wm_area(C); - SpaceFile *sfile = CTX_wm_space_file(C); - FileSelectParams *params = ED_fileselect_get_active_params(sfile); + const ScrArea *area = CTX_wm_area(C); + const SpaceFile *sfile = CTX_wm_space_file(C); + const FileSelectParams *params = ED_fileselect_get_active_params(sfile); ARegion *region_ctx = CTX_wm_region(C); @@ -2950,6 +2950,46 @@ void FILE_OT_start_filter(struct wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Edit Directory Path Operator + * \{ */ + +static int file_edit_directory_path_exec(bContext *C, wmOperator *UNUSED(op)) +{ + const ScrArea *area = CTX_wm_area(C); + const SpaceFile *sfile = CTX_wm_space_file(C); + const FileSelectParams *params = ED_fileselect_get_active_params(sfile); + + ARegion *region_ctx = CTX_wm_region(C); + + if (area) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { + CTX_wm_region_set(C, region); + if (UI_textbutton_activate_rna(C, region, params, "directory")) { + break; + } + } + } + + CTX_wm_region_set(C, region_ctx); + + return OPERATOR_FINISHED; +} + +void FILE_OT_edit_directory_path(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Edit Directory Path"; + ot->description = "Start editing directory field"; + ot->idname = "FILE_OT_edit_directory_path"; + + /* api callbacks */ + ot->exec = file_edit_directory_path_exec; + ot->poll = ED_operator_file_active; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Macro Operators * \{ */ diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 9f71d6f77c7..183af0c14f5 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -336,7 +336,7 @@ enum { }; typedef struct FileListEntryPreview { - char path[FILE_MAX]; + char filepath[FILE_MAX]; uint flags; int index; int attributes; /* from FileDirEntry. */ @@ -1636,13 +1636,13 @@ static void filelist_cache_preview_runf(TaskPool *__restrict pool, void *taskdat source = THB_SOURCE_FONT; } - IMB_thumb_path_lock(preview->path); + IMB_thumb_path_lock(preview->filepath); /* Always generate biggest preview size for now, it's simpler and avoids having to re-generate * in case user switch to a bigger preview size. Do not create preview when file is offline. */ ImBuf *imbuf = (preview->attributes & FILE_ATTR_OFFLINE) ? - IMB_thumb_read(preview->path, THB_LARGE) : - IMB_thumb_manage(preview->path, THB_LARGE, source); - IMB_thumb_path_unlock(preview->path); + IMB_thumb_read(preview->filepath, THB_LARGE) : + IMB_thumb_manage(preview->filepath, THB_LARGE, source); + IMB_thumb_path_unlock(preview->filepath); if (imbuf) { preview->icon_id = BKE_icon_imbuf_create(imbuf); } @@ -1753,7 +1753,7 @@ static void filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry if (preview_in_memory) { /* TODO(mano-wii): No need to use the thread API here. */ BLI_assert(BKE_previewimg_is_finished(preview_in_memory, ICON_SIZE_PREVIEW)); - preview->path[0] = '\0'; + preview->filepath[0] = '\0'; ImBuf *imbuf = BKE_previewimg_to_imbuf(preview_in_memory, ICON_SIZE_PREVIEW); if (imbuf) { preview->icon_id = BKE_icon_imbuf_create(imbuf); @@ -1762,13 +1762,13 @@ static void filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry } else { if (entry->redirection_path) { - BLI_strncpy(preview->path, entry->redirection_path, FILE_MAXDIR); + BLI_strncpy(preview->filepath, entry->redirection_path, FILE_MAXDIR); } else { BLI_join_dirfile( - preview->path, sizeof(preview->path), filelist->filelist.root, entry->relpath); + preview->filepath, sizeof(preview->filepath), filelist->filelist.root, entry->relpath); } - // printf("%s: %d - %s\n", __func__, preview->index, preview->path); + // printf("%s: %d - %s\n", __func__, preview->index, preview->filepath); FileListEntryPreviewTaskData *preview_taskdata = MEM_mallocN(sizeof(*preview_taskdata), __func__); @@ -2667,7 +2667,7 @@ bool filelist_cache_previews_update(FileList *filelist) * we do not want to cache it again here. */ entry = filelist_file_ex(filelist, preview->index, false); - // printf("%s: %d - %s - %p\n", __func__, preview->index, preview->path, preview->img); + // printf("%s: %d - %s - %p\n", __func__, preview->index, preview->filepath, preview->img); if (entry) { if (preview->icon_id) { @@ -2802,7 +2802,8 @@ int ED_path_extension_type(const char *path) if (BLI_path_extension_check(path, ".zip")) { return FILE_TYPE_ARCHIVE; } - if (BLI_path_extension_check_n(path, ".obj", ".3ds", ".fbx", ".glb", ".gltf", ".svg", NULL)) { + if (BLI_path_extension_check_n( + path, ".obj", ".mtl", ".3ds", ".fbx", ".glb", ".gltf", ".svg", ".stl", NULL)) { return FILE_TYPE_OBJECT_IO; } if (BLI_path_extension_check_array(path, imb_ext_image)) { @@ -3074,7 +3075,7 @@ static int filelist_readjob_list_dir(const char *root, } target = entry->redirection_path; #ifdef WIN32 - /* On Windows don't show ".lnk" extension for valid shortcuts. */ + /* On Windows don't show `.lnk` extension for valid shortcuts. */ BLI_path_extension_replace(entry->relpath, FILE_MAXDIR, ""); #endif } diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index 011506368ee..e42e1e98660 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -983,6 +983,7 @@ void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *region) if (params->display == FILE_IMGDISPLAY) { const float pad_fac = compact ? 0.15f : 0.3f; + /* Matches UI_preview_tile_size_x()/_y() by default. */ 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 = pad_fac * UI_UNIT_X; @@ -1009,6 +1010,7 @@ void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *region) else if (params->display == FILE_VERTICALDISPLAY) { int rowcount; + /* Matches UI_preview_tile_size_x()/_y() by default. */ 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; @@ -1030,6 +1032,7 @@ void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *region) layout->flag = FILE_LAYOUT_VER; } else if (params->display == FILE_HORIZONTALDISPLAY) { + /* Matches UI_preview_tile_size_x()/_y() by default. */ 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; @@ -1364,3 +1367,21 @@ ScrArea *ED_fileselect_handler_area_find(const wmWindow *win, const wmOperator * return NULL; } + +ScrArea *ED_fileselect_handler_area_find_any_with_op(const wmWindow *win) +{ + const bScreen *screen = WM_window_get_active_screen(win); + + ED_screen_areas_iter (win, screen, area) { + if (area->spacetype != SPACE_FILE) { + continue; + } + + const SpaceFile *sfile = area->spacedata.first; + if (sfile->op) { + return area; + } + } + + return NULL; +} diff --git a/source/blender/editors/space_file/fsmenu.c b/source/blender/editors/space_file/fsmenu.c index 65354591034..310c688383b 100644 --- a/source/blender/editors/space_file/fsmenu.c +++ b/source/blender/editors/space_file/fsmenu.c @@ -519,13 +519,13 @@ void fsmenu_remove_entry(struct FSMenu *fsmenu, FSMenuCategory category, int idx } } -void fsmenu_write_file(struct FSMenu *fsmenu, const char *filename) +void fsmenu_write_file(struct FSMenu *fsmenu, const char *filepath) { FSMenuEntry *fsm_iter = NULL; char fsm_name[FILE_MAX]; int nwritten = 0; - FILE *fp = BLI_fopen(filename, "w"); + FILE *fp = BLI_fopen(filepath, "w"); if (!fp) { return; } @@ -556,14 +556,14 @@ void fsmenu_write_file(struct FSMenu *fsmenu, const char *filename) fclose(fp); } -void fsmenu_read_bookmarks(struct FSMenu *fsmenu, const char *filename) +void fsmenu_read_bookmarks(struct FSMenu *fsmenu, const char *filepath) { char line[FILE_MAXDIR]; char name[FILE_MAXFILE]; FSMenuCategory category = FS_CATEGORY_BOOKMARKS; FILE *fp; - fp = BLI_fopen(filename, "r"); + fp = BLI_fopen(filepath, "r"); if (!fp) { return; } @@ -890,7 +890,7 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks) continue; } - /* Exclude "all my files" as it makes no sense in blender fileselector */ + /* Exclude "all my files" as it makes no sense in blender file-selector. */ /* Exclude "airdrop" if wlan not active as it would show "" ) */ if (!strstr(line, "myDocuments.cannedSearch") && (*line != '\0')) { fsmenu_insert_entry( diff --git a/source/blender/editors/space_file/fsmenu.h b/source/blender/editors/space_file/fsmenu.h index 861c37ecaa1..6e980a326fc 100644 --- a/source/blender/editors/space_file/fsmenu.h +++ b/source/blender/editors/space_file/fsmenu.h @@ -38,10 +38,10 @@ short fsmenu_can_save(struct FSMenu *fsmenu, enum FSMenuCategory category, int i void fsmenu_remove_entry(struct FSMenu *fsmenu, enum FSMenuCategory category, int idx); /** saves the 'bookmarks' to the specified file */ -void fsmenu_write_file(struct FSMenu *fsmenu, const char *filename); +void fsmenu_write_file(struct FSMenu *fsmenu, const char *filepath); /** reads the 'bookmarks' from the specified file */ -void fsmenu_read_bookmarks(struct FSMenu *fsmenu, const char *filename); +void fsmenu_read_bookmarks(struct FSMenu *fsmenu, const char *filepath); /** adds system specific directories */ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks); diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index 0170361f244..a462476aae0 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -695,6 +695,7 @@ static void file_operatortypes(void) WM_operatortype_append(FILE_OT_smoothscroll); WM_operatortype_append(FILE_OT_filepath_drop); WM_operatortype_append(FILE_OT_start_filter); + WM_operatortype_append(FILE_OT_edit_directory_path); WM_operatortype_append(FILE_OT_view_selected); } diff --git a/source/blender/editors/space_graph/graph_select.c b/source/blender/editors/space_graph/graph_select.c index 39b980ac4c3..e71c5114b0a 100644 --- a/source/blender/editors/space_graph/graph_select.c +++ b/source/blender/editors/space_graph/graph_select.c @@ -1080,6 +1080,7 @@ void GRAPH_OT_select_circle(wmOperatorType *ot) ot->exec = graph_circle_select_exec; ot->poll = graphop_visible_keyframes_poll; ot->cancel = WM_gesture_circle_cancel; + ot->get_name = ED_select_circle_get_name; /* flags */ ot->flag = OPTYPE_UNDO; diff --git a/source/blender/editors/space_image/CMakeLists.txt b/source/blender/editors/space_image/CMakeLists.txt index c385420b18e..39fb41245bf 100644 --- a/source/blender/editors/space_image/CMakeLists.txt +++ b/source/blender/editors/space_image/CMakeLists.txt @@ -61,7 +61,7 @@ if(WITH_IMAGE_CINEON) endif() if(WITH_IMAGE_WEBP) - add_definitions(-DWITH_WEBP) + add_definitions(-DWITH_WEBP) endif() blender_add_lib(bf_editor_space_image "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c index 208928afc1f..d0c21f85472 100644 --- a/source/blender/editors/space_image/image_buttons.c +++ b/source/blender/editors/space_image/image_buttons.c @@ -845,7 +845,10 @@ void uiTemplateImage(uiLayout *layout, row = uiLayoutRow(row, true); uiLayoutSetEnabled(row, is_packed == false); - uiItemR(row, &imaptr, "filepath", 0, "", ICON_NONE); + + prop = RNA_struct_find_property(&imaptr, "filepath"); + uiDefAutoButR(block, &imaptr, prop, -1, "", ICON_NONE, 0, 0, 200, UI_UNIT_Y); + uiItemO(row, "", ICON_FILEBROWSER, "image.file_browse"); uiItemO(row, "", ICON_FILE_REFRESH, "image.reload"); } diff --git a/source/blender/editors/space_image/image_edit.c b/source/blender/editors/space_image/image_edit.c index e851b99d3ba..950acd77f6a 100644 --- a/source/blender/editors/space_image/image_edit.c +++ b/source/blender/editors/space_image/image_edit.c @@ -483,6 +483,16 @@ bool ED_space_image_maskedit_poll(bContext *C) return false; } +bool ED_space_image_maskedit_visible_splines_poll(bContext *C) +{ + if (!ED_space_image_maskedit_poll(C)) { + return false; + } + + const SpaceImage *space_image = CTX_wm_space_image(C); + return space_image->mask_info.draw_flag & MASK_DRAWFLAG_SPLINE; +} + bool ED_space_image_paint_curve(const bContext *C) { SpaceImage *sima = CTX_wm_space_image(C); @@ -508,6 +518,16 @@ bool ED_space_image_maskedit_mask_poll(bContext *C) return false; } +bool ED_space_image_maskedit_mask_visible_splines_poll(bContext *C) +{ + if (!ED_space_image_maskedit_mask_poll(C)) { + return false; + } + + const SpaceImage *space_image = CTX_wm_space_image(C); + return space_image->mask_info.draw_flag & MASK_DRAWFLAG_SPLINE; +} + bool ED_space_image_cursor_poll(bContext *C) { return ED_operator_uvedit_space_image(C) || ED_space_image_maskedit_poll(C) || diff --git a/source/blender/editors/space_image/image_intern.h b/source/blender/editors/space_image/image_intern.h index 2322420069e..364bec1377d 100644 --- a/source/blender/editors/space_image/image_intern.h +++ b/source/blender/editors/space_image/image_intern.h @@ -49,6 +49,7 @@ void IMAGE_OT_new(struct wmOperatorType *ot); * Called by other space types too. */ void IMAGE_OT_open(struct wmOperatorType *ot); +void IMAGE_OT_file_browse(struct wmOperatorType *ot); /** * Called by other space types too. */ diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index aa77aab2283..537132ac428 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -197,6 +197,20 @@ static ImageUser *image_user_from_context(const bContext *C) return (sima) ? &sima->iuser : NULL; } +static ImageUser image_user_from_active_tile(Image *ima) +{ + ImageUser iuser; + BKE_imageuser_default(&iuser); + + /* Use the file associated with the active tile. Otherwise use the first tile. */ + if (ima && ima->source == IMA_SRC_TILED) { + const ImageTile *active = (ImageTile *)BLI_findlink(&ima->tiles, ima->active_tile_index); + iuser.tile = active ? active->tile_number : ((ImageTile *)ima->tiles.first)->tile_number; + } + + return iuser; +} + static bool image_from_context_has_data_poll(bContext *C) { Image *ima = image_from_context(C); @@ -214,13 +228,14 @@ static bool image_from_context_has_data_poll(bContext *C) } /** - * Use this when the image buffer is accessed without the image user. + * Use this when the image buffer is accessing the active tile without the image user. */ -static bool image_from_context_has_data_poll_no_image_user(bContext *C) +static bool image_from_context_has_data_poll_active_tile(bContext *C) { Image *ima = image_from_context(C); + ImageUser iuser = image_user_from_active_tile(ima); - return BKE_image_has_ibuf(ima, NULL); + return BKE_image_has_ibuf(ima, &iuser); } static bool image_not_packed_poll(bContext *C) @@ -949,7 +964,7 @@ static int image_view_selected_exec(bContext *C, wmOperator *UNUSED(op)) static bool image_view_selected_poll(bContext *C) { - return (space_image_main_region_poll(C) && (ED_operator_uvedit(C) || ED_operator_mask(C))); + return (space_image_main_region_poll(C) && (ED_operator_uvedit(C) || ED_maskedit_poll(C))); } void IMAGE_OT_view_selected(wmOperatorType *ot) @@ -1274,8 +1289,8 @@ static Image *image_open_single(Main *bmain, BKE_image_free_views(ima); } - if ((range->length > 1) && (ima->source == IMA_SRC_FILE)) { - if (range->udim_tiles.first) { + if (ima->source == IMA_SRC_FILE) { + if (range->udims_detected && range->udim_tiles.first) { ima->source = IMA_SRC_TILED; ImageTile *first_tile = ima->tiles.first; first_tile->tile_number = range->offset; @@ -1283,7 +1298,7 @@ static Image *image_open_single(Main *bmain, BKE_image_add_tile(ima, POINTER_AS_INT(node->data), NULL); } } - else { + else if (range->length > 1) { ima->source = IMA_SRC_SEQUENCE; } } @@ -1446,7 +1461,7 @@ static int image_open_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED( image_open_init(C, op); - /* show multiview save options only if scene has multiviews */ + /* Show multi-view save options only if scene has multi-views. */ PropertyRNA *prop; prop = RNA_struct_find_property(op->ptr, "show_multiview"); RNA_property_boolean_set(op->ptr, prop, (scene->r.scemode & R_MULTIVIEW) != 0); @@ -1535,6 +1550,115 @@ void IMAGE_OT_open(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Browse Image Operator + * \{ */ + +static int image_file_browse_exec(bContext *C, wmOperator *op) +{ + Image *ima = op->customdata; + if (ima == NULL) { + return OPERATOR_CANCELLED; + } + + char filepath[FILE_MAX]; + RNA_string_get(op->ptr, "filepath", filepath); + + /* If loading into a tiled texture, ensure that the filename is tokenized. */ + if (ima->source == IMA_SRC_TILED) { + char *filename = (char *)BLI_path_basename(filepath); + BKE_image_ensure_tile_token(filename); + } + + PointerRNA imaptr; + PropertyRNA *imaprop; + RNA_id_pointer_create(&ima->id, &imaptr); + imaprop = RNA_struct_find_property(&imaptr, "filepath"); + + RNA_property_string_set(&imaptr, imaprop, filepath); + RNA_property_update(C, &imaptr, imaprop); + + return OPERATOR_FINISHED; +} + +static int image_file_browse_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + Image *ima = image_from_context(C); + if (!ima) { + return OPERATOR_CANCELLED; + } + + char filepath[FILE_MAX]; + BLI_strncpy(filepath, ima->filepath, sizeof(filepath)); + + /* Shift+Click to open the file, Alt+Click to browse a folder in the OS's browser. */ + if (event->modifier & (KM_SHIFT | KM_ALT)) { + wmOperatorType *ot = WM_operatortype_find("WM_OT_path_open", true); + PointerRNA props_ptr; + + if (event->modifier & KM_ALT) { + char *lslash = (char *)BLI_path_slash_rfind(filepath); + if (lslash) { + *lslash = '\0'; + } + } + else if (ima->source == IMA_SRC_TILED) { + ImageUser iuser = image_user_from_active_tile(ima); + BKE_image_user_file_path(&iuser, ima, filepath); + } + + WM_operator_properties_create_ptr(&props_ptr, ot); + RNA_string_set(&props_ptr, "filepath", filepath); + WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, &props_ptr, NULL); + WM_operator_properties_free(&props_ptr); + + return OPERATOR_CANCELLED; + } + + /* The image is typically passed to the operator via layout/button context (e.g. + * #uiLayoutSetContextPointer()). The File Browser doesn't support restoring this context + * when calling `exec()` though, so we have to pass it the image via custom data. */ + op->customdata = ima; + + image_filesel(C, op, filepath); + + return OPERATOR_RUNNING_MODAL; +} + +static bool image_file_browse_poll(bContext *C) +{ + return image_from_context(C) != NULL; +} + +void IMAGE_OT_file_browse(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Browse Image"; + ot->description = + "Open an image file browser, hold Shift to open the file, Alt to browse containing " + "directory"; + ot->idname = "IMAGE_OT_file_browse"; + + /* api callbacks */ + ot->exec = image_file_browse_exec; + ot->invoke = image_file_browse_invoke; + ot->poll = image_file_browse_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO; + + /* properties */ + WM_operator_properties_filesel(ot, + FILE_TYPE_FOLDER | FILE_TYPE_IMAGE | FILE_TYPE_MOVIE, + FILE_SPECIAL, + FILE_OPENFILE, + WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH, + FILE_DEFAULTDISPLAY, + FILE_SORT_DEFAULT); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Match Movie Length Operator * \{ */ @@ -1680,184 +1804,27 @@ void IMAGE_OT_replace(wmOperatorType *ot) typedef struct ImageSaveData { ImageUser *iuser; Image *image; - ImageFormatData im_format; + ImageSaveOptions opts; } ImageSaveData; -static char imtype_best_depth(ImBuf *ibuf, const char imtype) -{ - const char depth_ok = BKE_imtype_valid_depths(imtype); - - if (ibuf->rect_float) { - if (depth_ok & R_IMF_CHAN_DEPTH_32) { - return R_IMF_CHAN_DEPTH_32; - } - if (depth_ok & R_IMF_CHAN_DEPTH_24) { - return R_IMF_CHAN_DEPTH_24; - } - if (depth_ok & R_IMF_CHAN_DEPTH_16) { - return R_IMF_CHAN_DEPTH_16; - } - if (depth_ok & R_IMF_CHAN_DEPTH_12) { - return R_IMF_CHAN_DEPTH_12; - } - return R_IMF_CHAN_DEPTH_8; - } - - if (depth_ok & R_IMF_CHAN_DEPTH_8) { - return R_IMF_CHAN_DEPTH_8; - } - if (depth_ok & R_IMF_CHAN_DEPTH_12) { - return R_IMF_CHAN_DEPTH_12; - } - if (depth_ok & R_IMF_CHAN_DEPTH_16) { - return R_IMF_CHAN_DEPTH_16; - } - if (depth_ok & R_IMF_CHAN_DEPTH_24) { - return R_IMF_CHAN_DEPTH_24; - } - if (depth_ok & R_IMF_CHAN_DEPTH_32) { - return R_IMF_CHAN_DEPTH_32; - } - return R_IMF_CHAN_DEPTH_8; /* fallback, should not get here */ -} - -static int image_save_options_init(Main *bmain, - ImageSaveOptions *opts, - Image *ima, - ImageUser *iuser, - const bool guess_path, - const bool save_as_render) +static void image_save_options_from_op(Main *bmain, ImageSaveOptions *opts, wmOperator *op) { - void *lock; - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, &lock); - - if (ibuf) { - Scene *scene = opts->scene; - bool is_depth_set = false; - - if (ELEM(ima->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)) { - /* imtype */ - BKE_image_format_init_for_write(&opts->im_format, scene, NULL); - is_depth_set = true; - if (!BKE_image_is_multiview(ima)) { - /* In case multiview is disabled, - * render settings would be invalid for render result in this area. */ - opts->im_format.stereo3d_format = *ima->stereo3d_format; - opts->im_format.views_format = ima->views_format; - } - } - else { - if (ima->source == IMA_SRC_GENERATED) { - opts->im_format.imtype = R_IMF_IMTYPE_PNG; - opts->im_format.compress = ibuf->foptions.quality; - opts->im_format.planes = ibuf->planes; - } - else { - BKE_image_format_from_imbuf(&opts->im_format, ibuf); - } - - /* use the multiview image settings as the default */ - opts->im_format.stereo3d_format = *ima->stereo3d_format; - opts->im_format.views_format = ima->views_format; - - BKE_image_format_color_management_copy_from_scene(&opts->im_format, scene); - } - - opts->im_format.color_management = R_IMF_COLOR_MANAGEMENT_FOLLOW_SCENE; - - if (ima->source == IMA_SRC_TILED) { - BLI_strncpy(opts->filepath, ima->filepath, sizeof(opts->filepath)); - BLI_path_abs(opts->filepath, ID_BLEND_PATH_FROM_GLOBAL(&ima->id)); - } - else { - BLI_strncpy(opts->filepath, ibuf->name, sizeof(opts->filepath)); - } - - /* sanitize all settings */ - - /* unlikely but just in case */ - if (ELEM(opts->im_format.planes, R_IMF_PLANES_BW, R_IMF_PLANES_RGB, R_IMF_PLANES_RGBA) == 0) { - opts->im_format.planes = R_IMF_PLANES_RGBA; - } - - /* depth, account for float buffer and format support */ - if (is_depth_set == false) { - opts->im_format.depth = imtype_best_depth(ibuf, opts->im_format.imtype); - } - - /* some formats don't use quality so fallback to scenes quality */ - if (opts->im_format.quality == 0) { - opts->im_format.quality = scene->r.im_format.quality; - } - - /* check for empty path */ - if (guess_path && opts->filepath[0] == 0) { - const bool is_prev_save = !STREQ(G.ima, "//"); - if (save_as_render) { - if (is_prev_save) { - BLI_strncpy(opts->filepath, G.ima, sizeof(opts->filepath)); - } - else { - BLI_strncpy(opts->filepath, "//untitled", sizeof(opts->filepath)); - BLI_path_abs(opts->filepath, BKE_main_blendfile_path(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)); - } - - /* append UDIM marker if not present */ - if (ima->source == IMA_SRC_TILED && strstr(opts->filepath, "<UDIM>") == NULL) { - int len = strlen(opts->filepath); - STR_CONCAT(opts->filepath, len, ".<UDIM>"); - } - } - } - - BKE_image_release_ibuf(ima, ibuf, lock); - - return (ibuf != NULL); -} - -static void image_save_options_from_op(Main *bmain, - ImageSaveOptions *opts, - wmOperator *op, - ImageFormatData *imf) -{ - if (imf) { - BKE_image_format_free(&opts->im_format); - BKE_image_format_copy(&opts->im_format, imf); - } - if (RNA_struct_property_is_set(op->ptr, "filepath")) { RNA_string_get(op->ptr, "filepath", opts->filepath); BLI_path_abs(opts->filepath, BKE_main_blendfile_path(bmain)); } -} - -static void image_save_options_to_op(ImageSaveOptions *opts, wmOperator *op) -{ - if (op->customdata) { - ImageSaveData *isd = op->customdata; - BKE_image_format_free(&isd->im_format); - BKE_image_format_copy(&isd->im_format, &opts->im_format); - } - RNA_string_set(op->ptr, "filepath", opts->filepath); -} - -static bool save_image_op( - Main *bmain, Image *ima, ImageUser *iuser, wmOperator *op, ImageSaveOptions *opts) -{ opts->relative = (RNA_struct_find_property(op->ptr, "relative_path") && RNA_boolean_get(op->ptr, "relative_path")); opts->save_copy = (RNA_struct_find_property(op->ptr, "copy") && RNA_boolean_get(op->ptr, "copy")); opts->save_as_render = (RNA_struct_find_property(op->ptr, "save_as_render") && RNA_boolean_get(op->ptr, "save_as_render")); +} +static bool save_image_op( + Main *bmain, Image *ima, ImageUser *iuser, wmOperator *op, ImageSaveOptions *opts) +{ WM_cursor_wait(true); bool ok = BKE_image_save(op->reports, bmain, ima, iuser, opts); @@ -1872,11 +1839,56 @@ static bool save_image_op( return ok; } +static ImageSaveData *image_save_as_init(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Image *image = image_from_context(C); + ImageUser *iuser = image_user_from_context(C); + Scene *scene = CTX_data_scene(C); + + ImageSaveData *isd = MEM_callocN(sizeof(*isd), __func__); + isd->image = image; + isd->iuser = iuser; + + if (!BKE_image_save_options_init(&isd->opts, bmain, scene, image, iuser, true, false)) { + BKE_image_save_options_free(&isd->opts); + MEM_freeN(isd); + return NULL; + } + + isd->opts.do_newpath = true; + + if (!RNA_struct_property_is_set(op->ptr, "filepath")) { + RNA_string_set(op->ptr, "filepath", isd->opts.filepath); + } + + /* Enable save_copy by default for render results. */ + if (ELEM(image->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE) && + !RNA_struct_property_is_set(op->ptr, "copy")) { + RNA_boolean_set(op->ptr, "copy", true); + } + + if (!RNA_struct_property_is_set(op->ptr, "save_as_render")) { + RNA_boolean_set(op->ptr, "save_as_render", isd->opts.save_as_render); + } + + /* Show multiview save options only if image has multiviews. */ + PropertyRNA *prop; + prop = RNA_struct_find_property(op->ptr, "show_multiview"); + RNA_property_boolean_set(op->ptr, prop, BKE_image_is_multiview(image)); + prop = RNA_struct_find_property(op->ptr, "use_multiview"); + RNA_property_boolean_set(op->ptr, prop, BKE_image_is_multiview(image)); + + op->customdata = isd; + + return isd; +} + static void image_save_as_free(wmOperator *op) { if (op->customdata) { ImageSaveData *isd = op->customdata; - BKE_image_format_free(&isd->im_format); + BKE_image_save_options_free(&isd->opts); MEM_freeN(op->customdata); op->customdata = NULL; @@ -1886,96 +1898,55 @@ static void image_save_as_free(wmOperator *op) static int image_save_as_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - ImageSaveOptions opts; + ImageSaveData *isd; - Image *image = NULL; - ImageUser *iuser = NULL; - ImageFormatData *imf = NULL; if (op->customdata) { - ImageSaveData *isd = op->customdata; - image = isd->image; - iuser = isd->iuser; - imf = &isd->im_format; + isd = op->customdata; } else { - image = image_from_context(C); - iuser = image_user_from_context(C); + isd = image_save_as_init(C, op); + if (isd == NULL) { + return OPERATOR_CANCELLED; + } } - BKE_image_save_options_init(&opts, bmain, scene); - - /* just in case to initialize values, - * these should be set on invoke or by the caller. */ - image_save_options_init(bmain, &opts, image, iuser, false, false); - - image_save_options_from_op(bmain, &opts, op, imf); - opts.do_newpath = true; + image_save_options_from_op(bmain, &isd->opts, op); + BKE_image_save_options_update(&isd->opts, isd->image); - save_image_op(bmain, image, iuser, op, &opts); + save_image_op(bmain, isd->image, isd->iuser, op, &isd->opts); - if (opts.save_copy == false) { - BKE_image_free_packedfiles(image); + if (isd->opts.save_copy == false) { + BKE_image_free_packedfiles(isd->image); } - BKE_image_save_options_free(&opts); - image_save_as_free(op); return OPERATOR_FINISHED; } -static bool image_save_as_check(bContext *UNUSED(C), wmOperator *op) +static bool image_save_as_check(bContext *C, wmOperator *op) { + Main *bmain = CTX_data_main(C); ImageSaveData *isd = op->customdata; - return WM_operator_filesel_ensure_ext_imtype(op, &isd->im_format); + + image_save_options_from_op(bmain, &isd->opts, op); + BKE_image_save_options_update(&isd->opts, isd->image); + + return WM_operator_filesel_ensure_ext_imtype(op, &isd->opts.im_format); } static int image_save_as_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { - Main *bmain = CTX_data_main(C); - Image *ima = image_from_context(C); - ImageUser *iuser = image_user_from_context(C); - Scene *scene = CTX_data_scene(C); - ImageSaveOptions opts; - PropertyRNA *prop; - const bool save_as_render = (ima->source == IMA_SRC_VIEWER); - if (RNA_struct_property_is_set(op->ptr, "filepath")) { return image_save_as_exec(C, op); } - BKE_image_save_options_init(&opts, bmain, scene); - - if (image_save_options_init(bmain, &opts, ima, iuser, true, save_as_render) == 0) { - BKE_image_save_options_free(&opts); + ImageSaveData *isd = image_save_as_init(C, op); + if (isd == NULL) { return OPERATOR_CANCELLED; } - image_save_options_to_op(&opts, op); - /* enable save_copy by default for render results */ - if (ELEM(ima->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE) && - !RNA_struct_property_is_set(op->ptr, "copy")) { - RNA_boolean_set(op->ptr, "copy", true); - } - - RNA_boolean_set(op->ptr, "save_as_render", save_as_render); - - ImageSaveData *isd = MEM_callocN(sizeof(*isd), __func__); - isd->image = ima; - isd->iuser = iuser; - - BKE_image_format_copy(&isd->im_format, &opts.im_format); - op->customdata = isd; - - /* show multiview save options only if image has multiviews */ - prop = RNA_struct_find_property(op->ptr, "show_multiview"); - RNA_property_boolean_set(op->ptr, prop, BKE_image_is_multiview(ima)); - prop = RNA_struct_find_property(op->ptr, "use_multiview"); - RNA_property_boolean_set(op->ptr, prop, BKE_image_is_multiview(ima)); - - image_filesel(C, op, opts.filepath); - BKE_image_save_options_free(&opts); + image_filesel(C, op, isd->opts.filepath); return OPERATOR_RUNNING_MODAL; } @@ -2003,7 +1974,7 @@ static void image_save_as_draw(bContext *UNUSED(C), wmOperator *op) ImageSaveData *isd = op->customdata; PointerRNA imf_ptr; const bool is_multiview = RNA_boolean_get(op->ptr, "show_multiview"); - const bool use_color_management = RNA_boolean_get(op->ptr, "save_as_render"); + const bool save_as_render = RNA_boolean_get(op->ptr, "save_as_render"); uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); @@ -2015,8 +1986,15 @@ static void image_save_as_draw(bContext *UNUSED(C), wmOperator *op) uiItemS(layout); /* image template */ - RNA_pointer_create(NULL, &RNA_ImageFormatSettings, &isd->im_format, &imf_ptr); - uiTemplateImageSettings(layout, &imf_ptr, use_color_management); + RNA_pointer_create(NULL, &RNA_ImageFormatSettings, &isd->opts.im_format, &imf_ptr); + uiTemplateImageSettings(layout, &imf_ptr, save_as_render); + + if (!save_as_render) { + PointerRNA linear_settings_ptr = RNA_pointer_get(&imf_ptr, "linear_colorspace_settings"); + uiLayout *col = uiLayoutColumn(layout, true); + uiItemS(col); + uiItemR(col, &linear_settings_ptr, "name", 0, IFACE_("Color Space"), ICON_NONE); + } /* multiview template */ if (is_multiview) { @@ -2062,16 +2040,19 @@ void IMAGE_OT_save_as(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - RNA_def_boolean(ot->srna, - "save_as_render", - 0, - "Save As Render", - "Apply render part of display transform when saving byte image"); - RNA_def_boolean(ot->srna, - "copy", - 0, - "Copy", - "Create a new image file without modifying the current image in blender"); + PropertyRNA *prop; + prop = RNA_def_boolean(ot->srna, + "save_as_render", + 0, + "Save As Render", + "Apply render part of display transform when saving byte image"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, + "copy", + 0, + "Copy", + "Create a new image file without modifying the current image in blender"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); image_operator_prop_allow_tokens(ot); WM_operator_properties_filesel(ot, @@ -2138,12 +2119,11 @@ static int image_save_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } - BKE_image_save_options_init(&opts, bmain, scene); - if (image_save_options_init(bmain, &opts, image, iuser, false, false) == 0) { + if (!BKE_image_save_options_init(&opts, bmain, scene, image, iuser, false, false)) { BKE_image_save_options_free(&opts); return OPERATOR_CANCELLED; } - image_save_options_from_op(bmain, &opts, op, NULL); + image_save_options_from_op(bmain, &opts, op); /* Check if file write permission is ok. */ if (BLI_exists(opts.filepath) && !BLI_file_is_writable(opts.filepath)) { @@ -2316,6 +2296,14 @@ static bool image_has_valid_path(Image *ima) return strchr(ima->filepath, '\\') || strchr(ima->filepath, '/'); } +static bool image_should_pack_during_save_all(const Image *ima) +{ + /* Images without a filepath (implied with IMA_SRC_GENERATED) should + * be packed during a save_all operation. */ + return (ima->source == IMA_SRC_GENERATED) || + (ima->source == IMA_SRC_TILED && !BKE_image_has_filepath(ima)); +} + bool ED_image_should_save_modified(const Main *bmain) { ReportList reports; @@ -2339,7 +2327,7 @@ int ED_image_save_all_modified_info(const Main *bmain, ReportList *reports) bool is_format_writable; if (image_should_be_saved(ima, &is_format_writable)) { - if (BKE_image_has_packedfile(ima) || (ima->source == IMA_SRC_GENERATED)) { + if (BKE_image_has_packedfile(ima) || image_should_pack_during_save_all(ima)) { if (!ID_IS_LINKED(ima)) { num_saveable_images++; } @@ -2396,15 +2384,14 @@ bool ED_image_save_all_modified(const bContext *C, ReportList *reports) bool is_format_writable; if (image_should_be_saved(ima, &is_format_writable)) { - if (BKE_image_has_packedfile(ima) || (ima->source == IMA_SRC_GENERATED)) { + if (BKE_image_has_packedfile(ima) || image_should_pack_during_save_all(ima)) { BKE_image_memorypack(ima); } else if (is_format_writable) { if (image_has_valid_path(ima)) { ImageSaveOptions opts; Scene *scene = CTX_data_scene(C); - BKE_image_save_options_init(&opts, bmain, scene); - if (image_save_options_init(bmain, &opts, ima, NULL, false, false)) { + if (!BKE_image_save_options_init(&opts, bmain, scene, ima, NULL, false, false)) { bool saved_successfully = BKE_image_save(reports, bmain, ima, NULL, &opts); ok = ok && saved_successfully; } @@ -2711,7 +2698,8 @@ void IMAGE_OT_new(wmOperatorType *ot) static int image_flip_exec(bContext *C, wmOperator *op) { Image *ima = image_from_context(C); - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); + ImageUser iuser = image_user_from_active_tile(ima); + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL); SpaceImage *sima = CTX_wm_space_image(C); const bool is_paint = ((sima != NULL) && (sima->mode == SI_MODE_PAINT)); @@ -2808,7 +2796,7 @@ void IMAGE_OT_flip(wmOperatorType *ot) /* api callbacks */ ot->exec = image_flip_exec; - ot->poll = image_from_context_has_data_poll_no_image_user; + ot->poll = image_from_context_has_data_poll_active_tile; /* properties */ PropertyRNA *prop; @@ -2831,7 +2819,8 @@ void IMAGE_OT_flip(wmOperatorType *ot) static int image_invert_exec(bContext *C, wmOperator *op) { Image *ima = image_from_context(C); - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); + ImageUser iuser = image_user_from_active_tile(ima); + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL); SpaceImage *sima = CTX_wm_space_image(C); const bool is_paint = ((sima != NULL) && (sima->mode == SI_MODE_PAINT)); @@ -2929,7 +2918,7 @@ void IMAGE_OT_invert(wmOperatorType *ot) /* api callbacks */ ot->exec = image_invert_exec; - ot->poll = image_from_context_has_data_poll_no_image_user; + ot->poll = image_from_context_has_data_poll_active_tile; /* properties */ prop = RNA_def_boolean(ot->srna, "invert_r", 0, "Red", "Invert red channel"); @@ -2954,9 +2943,10 @@ void IMAGE_OT_invert(wmOperatorType *ot) static int image_scale_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { Image *ima = image_from_context(C); + ImageUser iuser = image_user_from_active_tile(ima); PropertyRNA *prop = RNA_struct_find_property(op->ptr, "size"); if (!RNA_property_is_set(op->ptr, prop)) { - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL); const int size[2] = {ibuf->x, ibuf->y}; RNA_property_int_set_array(op->ptr, prop, size); BKE_image_release_ibuf(ima, ibuf, NULL); @@ -2967,7 +2957,8 @@ static int image_scale_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED static int image_scale_exec(bContext *C, wmOperator *op) { Image *ima = image_from_context(C); - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); + ImageUser iuser = image_user_from_active_tile(ima); + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL); SpaceImage *sima = CTX_wm_space_image(C); const bool is_paint = ((sima != NULL) && (sima->mode == SI_MODE_PAINT)); @@ -3017,7 +3008,7 @@ void IMAGE_OT_resize(wmOperatorType *ot) /* api callbacks */ ot->invoke = image_scale_invoke; ot->exec = image_scale_exec; - ot->poll = image_from_context_has_data_poll_no_image_user; + ot->poll = image_from_context_has_data_poll_active_tile; /* properties */ RNA_def_int_vector(ot->srna, "size", 2, NULL, 1, INT_MAX, "Size", "", 1, SHRT_MAX); @@ -3040,9 +3031,8 @@ static bool image_pack_test(bContext *C, wmOperator *op) return false; } - if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE, IMA_SRC_TILED)) { - BKE_report( - op->reports, RPT_ERROR, "Packing movies, image sequences or tiled images not supported"); + if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE)) { + BKE_report(op->reports, RPT_ERROR, "Packing movies or image sequences not supported"); return false; } @@ -3110,9 +3100,8 @@ static int image_unpack_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE, IMA_SRC_TILED)) { - BKE_report( - op->reports, RPT_ERROR, "Unpacking movies, image sequences or tiled images not supported"); + if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE)) { + BKE_report(op->reports, RPT_ERROR, "Unpacking movies or image sequences not supported"); return OPERATOR_CANCELLED; } @@ -3144,9 +3133,8 @@ static int image_unpack_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE return OPERATOR_CANCELLED; } - if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE, IMA_SRC_TILED)) { - BKE_report( - op->reports, RPT_ERROR, "Unpacking movies, image sequences or tiled images not supported"); + if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE)) { + BKE_report(op->reports, RPT_ERROR, "Unpacking movies or image sequences not supported"); return OPERATOR_CANCELLED; } @@ -3216,7 +3204,7 @@ bool ED_space_image_get_position(SpaceImage *sima, } bool ED_space_image_color_sample( - SpaceImage *sima, ARegion *region, int mval[2], float r_col[3], bool *r_is_data) + SpaceImage *sima, ARegion *region, const int mval[2], float r_col[3], bool *r_is_data) { if (r_is_data) { *r_is_data = false; diff --git a/source/blender/editors/space_image/image_sequence.c b/source/blender/editors/space_image/image_sequence.c index 365cf2542b2..fbeef47e278 100644 --- a/source/blender/editors/space_image/image_sequence.c +++ b/source/blender/editors/space_image/image_sequence.c @@ -107,10 +107,10 @@ static void image_detect_frame_range(ImageFrameRange *range, const bool detect_u /* UDIM */ if (detect_udim) { int udim_start, udim_range; - bool result = BKE_image_get_tile_info( + range->udims_detected = BKE_image_get_tile_info( range->filepath, &range->udim_tiles, &udim_start, &udim_range); - if (result) { + if (range->udims_detected) { range->offset = udim_start; range->length = udim_range; return; diff --git a/source/blender/editors/space_image/image_undo.c b/source/blender/editors/space_image/image_undo.c index c3a48abcae1..a7a8bde1115 100644 --- a/source/blender/editors/space_image/image_undo.c +++ b/source/blender/editors/space_image/image_undo.c @@ -287,7 +287,7 @@ static void ptile_restore_runtime_list(ListBase *paint_tiles) ibuf->userflags |= IB_RECT_INVALID; /* force recreate of char rect */ } if (ibuf->mipmap[0]) { - ibuf->userflags |= IB_MIPMAP_INVALID; /* force mip-map recreation. */ + ibuf->userflags |= IB_MIPMAP_INVALID; /* Force MIP-MAP recreation. */ } ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; @@ -1040,7 +1040,7 @@ static ImageUndoStep *image_undo_push_begin(const char *name, int paint_mode) bContext *C = NULL; /* special case, we never read from this. */ UndoStep *us_p = BKE_undosys_step_push_init_with_type(ustack, C, name, BKE_UNDOSYS_TYPE_IMAGE); ImageUndoStep *us = (ImageUndoStep *)us_p; - BLI_assert(ELEM(paint_mode, PAINT_MODE_TEXTURE_2D, PAINT_MODE_TEXTURE_3D)); + BLI_assert(ELEM(paint_mode, PAINT_MODE_TEXTURE_2D, PAINT_MODE_TEXTURE_3D, PAINT_MODE_SCULPT)); us->paint_mode = paint_mode; return us; } diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index 568bd064e3e..785a5419e04 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -103,7 +103,7 @@ static SpaceLink *image_create(const ScrArea *UNUSED(area), const Scene *UNUSED( simage->overlay.flag = SI_OVERLAY_SHOW_OVERLAYS | SI_OVERLAY_SHOW_GRID_BACKGROUND; BKE_imageuser_default(&simage->iuser); - simage->iuser.flag = IMA_SHOW_STEREO | IMA_ANIM_ALWAYS | IMA_SHOW_MAX_RESOLUTION; + simage->iuser.flag = IMA_SHOW_STEREO | IMA_ANIM_ALWAYS; BKE_scopes_new(&simage->scopes); simage->sample_line_hist.height = 100; @@ -199,6 +199,7 @@ static void image_operatortypes(void) WM_operatortype_append(IMAGE_OT_new); WM_operatortype_append(IMAGE_OT_open); + WM_operatortype_append(IMAGE_OT_file_browse); WM_operatortype_append(IMAGE_OT_match_movie_length); WM_operatortype_append(IMAGE_OT_replace); WM_operatortype_append(IMAGE_OT_reload); @@ -315,6 +316,9 @@ static void image_listener(const wmSpaceTypeListenerParams *params) ED_area_tag_redraw(area); break; case ND_MODE: + ED_paint_cursor_start(¶ms->scene->toolsettings->imapaint.paint, + ED_image_tools_paint_poll); + if (wmn->subtype == NS_EDITMODE_MESH) { ED_area_tag_refresh(area); } @@ -690,6 +694,7 @@ static void image_main_region_draw(const bContext *C, ARegion *region) sima->mask_info.draw_flag & ~MASK_DRAWFLAG_OVERLAY, sima->mask_info.draw_type, sima->mask_info.overlay_mode, + sima->mask_info.blend_factor, width, height, aspx, diff --git a/source/blender/editors/space_info/info_stats.cc b/source/blender/editors/space_info/info_stats.cc index b817ff887ce..29a7eb150a1 100644 --- a/source/blender/editors/space_info/info_stats.cc +++ b/source/blender/editors/space_info/info_stats.cc @@ -46,6 +46,7 @@ #include "BKE_pbvh.h" #include "BKE_scene.h" #include "BKE_subdiv_ccg.h" +#include "BKE_subdiv_modifier.h" #include "DEG_depsgraph_query.h" @@ -92,15 +93,18 @@ static bool stats_mesheval(const Mesh *me_eval, bool is_selected, SceneStats *st } int totvert, totedge, totface, totloop; - if (me_eval->runtime.subdiv_ccg != nullptr) { - const SubdivCCG *subdiv_ccg = me_eval->runtime.subdiv_ccg; + + const SubdivCCG *subdiv_ccg = me_eval->runtime.subdiv_ccg; + const SubsurfRuntimeData *subsurf_runtime_data = me_eval->runtime.subsurf_runtime_data; + + if (subdiv_ccg != nullptr) { BKE_subdiv_ccg_topology_counters(subdiv_ccg, &totvert, &totedge, &totface, &totloop); } - else if (me_eval->runtime.subsurf_resolution != 0) { - totvert = me_eval->runtime.subsurf_totvert; - totedge = me_eval->runtime.subsurf_totedge; - totface = me_eval->runtime.subsurf_totpoly; - totloop = me_eval->runtime.subsurf_totloop; + else if (subsurf_runtime_data && subsurf_runtime_data->resolution != 0) { + totvert = subsurf_runtime_data->stats_totvert; + totedge = subsurf_runtime_data->stats_totedge; + totface = subsurf_runtime_data->stats_totpoly; + totloop = subsurf_runtime_data->stats_totloop; } else { totvert = me_eval->totvert; diff --git a/source/blender/editors/space_nla/nla_channels.c b/source/blender/editors/space_nla/nla_channels.c index 8b059b33a9a..40082b08806 100644 --- a/source/blender/editors/space_nla/nla_channels.c +++ b/source/blender/editors/space_nla/nla_channels.c @@ -58,14 +58,12 @@ * --> Most channels are now selection only. */ -static int mouse_nla_channels( - bContext *C, bAnimContext *ac, float x, int channel_index, short selectmode) +static int mouse_nla_channels(bContext *C, bAnimContext *ac, int channel_index, short selectmode) { ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; int filter; - View2D *v2d = &ac->region->v2d; int notifierFlags = 0; /* get the channel that was clicked on */ @@ -203,47 +201,8 @@ static int mouse_nla_channels( } case ANIMTYPE_NLATRACK: { NlaTrack *nlt = (NlaTrack *)ale->data; - AnimData *adt = ale->adt; - short offset; - - /* offset for start of channel (on LHS of channel-list) */ - if (ale->id) { - /* special exception for materials and particles */ - if (ELEM(GS(ale->id->name), ID_MA, ID_PA)) { - offset = 21 + NLACHANNEL_BUTTON_WIDTH; - } - else { - offset = 14; - } - } - else { - offset = 0; - } - if (x >= (v2d->cur.xmax - NLACHANNEL_BUTTON_WIDTH)) { - /* toggle protection (only if there's a toggle there) */ - nlt->flag ^= NLATRACK_PROTECTED; - - /* notifier flags - channel was edited */ - notifierFlags |= (ND_ANIMCHAN | NA_EDITED); - } - else if (x >= (v2d->cur.xmax - 2 * NLACHANNEL_BUTTON_WIDTH)) { - /* toggle mute */ - nlt->flag ^= NLATRACK_MUTED; - - /* notifier flags - channel was edited */ - notifierFlags |= (ND_ANIMCHAN | NA_EDITED); - ale->update |= ANIM_UPDATE_DEPS; - } - else if (x <= ((NLACHANNEL_BUTTON_WIDTH * 2) + offset)) { - /* toggle 'solo' */ - BKE_nlatrack_solo_toggle(adt, nlt); - - /* notifier flags - channel was edited */ - notifierFlags |= (ND_ANIMCHAN | NA_EDITED); - ale->update |= ANIM_UPDATE_DEPS; - } - else if (nlaedit_is_tweakmode_on(ac) == 0) { + if (nlaedit_is_tweakmode_on(ac) == 0) { /* set selection */ if (selectmode == SELECT_INVERT) { /* inverse selection status of this F-Curve only */ @@ -269,61 +228,40 @@ static int mouse_nla_channels( case ANIMTYPE_NLAACTION: { AnimData *adt = BKE_animdata_from_id(ale->id); - /* button region... */ - if (x >= (v2d->cur.xmax - NLACHANNEL_BUTTON_WIDTH)) { - if (nlaedit_is_tweakmode_on(ac) == 0) { - /* 'push-down' action - only usable when not in tweak-mode */ - /* TODO: make this use the operator instead of calling the function directly - * however, calling the operator requires that we supply the args, - * and that works with proper buttons only */ - BKE_nla_action_pushdown(adt, ID_IS_OVERRIDE_LIBRARY(ale->id)); - } - else { - /* When in tweak-mode, this button becomes the toggle for mapped editing. */ - adt->flag ^= ADT_NLA_EDIT_NOMAP; - } + /* NOTE: rest of NLA-Action name doubles for operating on the AnimData block + * - this is useful when there's no clear divider, and makes more sense in + * the case of users trying to use this to change actions + * - in tweak-mode, clicking here gets us out of tweak-mode, as changing selection + * while in tweak-mode is really evil! + * - we disable "solo" flags too, to make it easier to work with stashed actions + * with less trouble + */ + if (nlaedit_is_tweakmode_on(ac)) { + /* Exit tweak-mode immediately. */ + nlaedit_disable_tweakmode(ac, true); /* changes to NLA-Action occurred */ notifierFlags |= ND_NLA_ACTCHANGE; ale->update |= ANIM_UPDATE_DEPS; } - /* OR rest of name... */ else { - /* NOTE: rest of NLA-Action name doubles for operating on the AnimData block - * - this is useful when there's no clear divider, and makes more sense in - * the case of users trying to use this to change actions - * - in tweak-mode, clicking here gets us out of tweak-mode, as changing selection - * while in tweak-mode is really evil! - * - we disable "solo" flags too, to make it easier to work with stashed actions - * with less trouble - */ - if (nlaedit_is_tweakmode_on(ac)) { - /* Exit tweak-mode immediately. */ - nlaedit_disable_tweakmode(ac, true); - - /* changes to NLA-Action occurred */ - notifierFlags |= ND_NLA_ACTCHANGE; - ale->update |= ANIM_UPDATE_DEPS; + /* select/deselect */ + if (selectmode == SELECT_INVERT) { + /* inverse selection status of this AnimData block only */ + adt->flag ^= ADT_UI_SELECTED; } else { - /* select/deselect */ - if (selectmode == SELECT_INVERT) { - /* inverse selection status of this AnimData block only */ - adt->flag ^= ADT_UI_SELECTED; - } - else { - /* select AnimData block by itself */ - ANIM_anim_channels_select_set(ac, ACHANNEL_SETFLAG_CLEAR); - adt->flag |= ADT_UI_SELECTED; - } - - /* set active? */ - if (adt->flag & ADT_UI_SELECTED) { - adt->flag |= ADT_UI_ACTIVE; - } + /* select AnimData block by itself */ + ANIM_anim_channels_select_set(ac, ACHANNEL_SETFLAG_CLEAR); + adt->flag |= ADT_UI_SELECTED; + } - notifierFlags |= (ND_ANIMCHAN | NA_SELECTED); + /* set active? */ + if (adt->flag & ADT_UI_SELECTED) { + adt->flag |= ADT_UI_ACTIVE; } + + notifierFlags |= (ND_ANIMCHAN | NA_SELECTED); } break; } @@ -386,7 +324,7 @@ static int nlachannels_mouseclick_invoke(bContext *C, wmOperator *op, const wmEv &channel_index); /* handle mouse-click in the relevant channel then */ - notifierFlags = mouse_nla_channels(C, &ac, x, channel_index, selectmode); + notifierFlags = mouse_nla_channels(C, &ac, channel_index, selectmode); /* set notifier that things have changed */ WM_event_add_notifier(C, NC_ANIMATION | notifierFlags, NULL); diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c index 6c631f46069..bb9e201d94a 100644 --- a/source/blender/editors/space_nla/nla_draw.c +++ b/source/blender/editors/space_nla/nla_draw.c @@ -621,7 +621,6 @@ static void nla_draw_strip(SpaceNla *snla, static void nla_draw_strip_text(AnimData *adt, NlaTrack *nlt, NlaStrip *strip, - int index, View2D *v2d, float xminc, float xmaxc, @@ -636,7 +635,7 @@ static void nla_draw_strip_text(AnimData *adt, /* just print the name and the range */ if (strip->flag & NLASTRIP_FLAG_TEMP_META) { - str_len = BLI_snprintf_rlen(str, sizeof(str), "%d) Temp-Meta", index); + str_len = BLI_snprintf_rlen(str, sizeof(str), "Temp-Meta"); } else { str_len = BLI_strncpy_rlen(str, strip->name, sizeof(str)); @@ -702,6 +701,89 @@ static void nla_draw_strip_frames_text( /* ---------------------- */ +/** + * Gets the first and last visible NLA strips on a track. + * Note that this also includes tracks that might only be + * visible because of their extendmode. + */ +static ListBase get_visible_nla_strips(NlaTrack *nlt, View2D *v2d) +{ + if (BLI_listbase_is_empty(&nlt->strips)) { + ListBase empty = {NULL, NULL}; + return empty; + } + + NlaStrip *first = NULL; + NlaStrip *last = NULL; + + /* Find the first strip that is within the bounds of the view. */ + LISTBASE_FOREACH (NlaStrip *, strip, &nlt->strips) { + if (BKE_nlastrip_within_bounds(strip, v2d->cur.xmin, v2d->cur.xmax)) { + first = last = strip; + break; + } + } + + const bool has_strips_within_bounds = first != NULL; + + if (has_strips_within_bounds) { + /* Find the last visible strip. */ + for (NlaStrip *strip = first->next; strip; strip = strip->next) { + if (!BKE_nlastrip_within_bounds(strip, v2d->cur.xmin, v2d->cur.xmax)) { + break; + } + last = strip; + } + /* Check if the first strip is adjacent to a strip outside the view to the left + * that has an extendmode region that should be drawn. + * If so, adjust the first strip to include drawing that strip as well. + */ + NlaStrip *prev = first->prev; + if (prev && prev->extendmode != NLASTRIP_EXTEND_NOTHING) { + first = prev; + } + } + else { + /* No immediately visible strips. + * Figure out where our view is relative to the strips, then determine + * if the view is adjacent to a strip that should have its extendmode + * rendered. + */ + NlaStrip *first_strip = nlt->strips.first; + NlaStrip *last_strip = nlt->strips.last; + if (first_strip && v2d->cur.xmax < first_strip->start && + first_strip->extendmode == NLASTRIP_EXTEND_HOLD) { + /* The view is to the left of all strips and the first strip has an + * extendmode that should be drawn. + */ + first = last = first_strip; + } + else if (last_strip && v2d->cur.xmin > last_strip->end && + last_strip->extendmode != NLASTRIP_EXTEND_NOTHING) { + /* The view is to the right of all strips and the last strip has an + * extendmode that should be drawn. + */ + first = last = last_strip; + } + else { + /* The view is in the middle of two strips. */ + LISTBASE_FOREACH (NlaStrip *, strip, &nlt->strips) { + /* Find the strip to the left by finding the strip to the right and getting its prev. */ + if (v2d->cur.xmax < strip->start) { + /* If the strip to the left has an extendmode, set that as the only visible strip. */ + if (strip->prev && strip->prev->extendmode != NLASTRIP_EXTEND_NOTHING) { + first = last = strip->prev; + } + break; + } + } + } + } + + ListBase visible_strips = {first, last}; + return visible_strips; +} + void draw_nla_main_data(bAnimContext *ac, SpaceNla *snla, ARegion *region) { View2D *v2d = ®ion->v2d; @@ -737,29 +819,26 @@ void draw_nla_main_data(bAnimContext *ac, SpaceNla *snla, ARegion *region) case ANIMTYPE_NLATRACK: { AnimData *adt = ale->adt; NlaTrack *nlt = (NlaTrack *)ale->data; - NlaStrip *strip; - int index; - - /* draw each strip in the track (if visible) */ - for (strip = nlt->strips.first, index = 1; strip; strip = strip->next, index++) { - if (BKE_nlastrip_within_bounds(strip, v2d->cur.xmin, v2d->cur.xmax)) { - const float xminc = strip->start + text_margin_x; - const float xmaxc = strip->end - text_margin_x; - - /* draw the visualization of the strip */ - nla_draw_strip(snla, adt, nlt, strip, v2d, ymin, ymax); - - /* add the text for this strip to the cache */ - if (xminc < xmaxc) { - nla_draw_strip_text(adt, nlt, strip, index, v2d, xminc, xmaxc, ymin, ymax); - } - - /* if transforming strips (only real reason for temp-metas currently), - * add to the cache the frame numbers of the strip's extents - */ - if (strip->flag & NLASTRIP_FLAG_TEMP_META) { - nla_draw_strip_frames_text(nlt, strip, v2d, ymin, ymax); - } + ListBase visible_nla_strips = get_visible_nla_strips(nlt, v2d); + + /* Draw each visible strip in the track. */ + LISTBASE_FOREACH (NlaStrip *, strip, &visible_nla_strips) { + const float xminc = strip->start + text_margin_x; + const float xmaxc = strip->end - text_margin_x; + + /* draw the visualization of the strip */ + nla_draw_strip(snla, adt, nlt, strip, v2d, ymin, ymax); + + /* add the text for this strip to the cache */ + if (xminc < xmaxc) { + nla_draw_strip_text(adt, nlt, strip, v2d, xminc, xmaxc, ymin, ymax); + } + + /* if transforming strips (only real reason for temp-metas currently), + * add to the cache the frame numbers of the strip's extents + */ + if (strip->flag & NLASTRIP_FLAG_TEMP_META) { + nla_draw_strip_frames_text(nlt, strip, v2d, ymin, ymax); } } break; diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c index 2aa9b347ed7..81520445000 100644 --- a/source/blender/editors/space_nla/nla_edit.c +++ b/source/blender/editors/space_nla/nla_edit.c @@ -1004,7 +1004,7 @@ static int nlaedit_add_meta_exec(bContext *C, wmOperator *UNUSED(op)) NlaStrip *strip; if (BKE_nlatrack_is_nonlocal_in_liboverride(ale->id, nlt)) { - /* No making metastrips in non-local tracks of override data. */ + /* No making meta-strips in non-local tracks of override data. */ continue; } @@ -1078,7 +1078,7 @@ static int nlaedit_remove_meta_exec(bContext *C, wmOperator *UNUSED(op)) NlaTrack *nlt = (NlaTrack *)ale->data; if (BKE_nlatrack_is_nonlocal_in_liboverride(ale->id, nlt)) { - /* No removing metastrips from non-local tracks of override data. */ + /* No removing meta-strips from non-local tracks of override data. */ continue; } @@ -1714,7 +1714,7 @@ static int nlaedit_swap_exec(bContext *C, wmOperator *op) BKE_nlatrack_add_strip(nlt, sb, is_liboverride); } - /* clear (temp) metastrips */ + /* Clear (temp) meta-strips. */ BKE_nlastrips_clear_metas(&nlt->strips, 0, 1); } diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index d5507619e0d..6806d715004 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -20,6 +20,7 @@ #include "BKE_image.h" #include "BKE_main.h" #include "BKE_node.h" +#include "BKE_node_runtime.hh" #include "BKE_node_tree_update.h" #include "BKE_scene.h" #include "BKE_tracking.h" @@ -1271,14 +1272,14 @@ static void node_file_output_socket_draw(bContext *C, static bool socket_needs_attribute_search(bNode &node, bNodeSocket &socket) { - if (node.declaration == nullptr) { + if (node.runtime->declaration == nullptr) { return false; } if (socket.in_out == SOCK_OUT) { return false; } const int socket_index = BLI_findindex(&node.inputs, &socket); - return node.declaration->inputs()[socket_index]->is_attribute_name(); + return node.runtime->declaration->inputs()[socket_index]->is_attribute_name(); } static void std_node_socket_draw( diff --git a/source/blender/editors/space_node/node_add.cc b/source/blender/editors/space_node/node_add.cc index 7fb15d69ab5..975d4eda7e3 100644 --- a/source/blender/editors/space_node/node_add.cc +++ b/source/blender/editors/space_node/node_add.cc @@ -303,10 +303,8 @@ static bNodeTree *node_add_group_get_and_poll_group_node_tree(Main *bmain, wmOperator *op, bNodeTree *ntree) { - char name[MAX_ID_NAME - 2]; - RNA_string_get(op->ptr, "name", name); - - bNodeTree *node_group = (bNodeTree *)BKE_libblock_find_name(bmain, ID_NT, name); + bNodeTree *node_group = reinterpret_cast<bNodeTree *>( + WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_NT)); if (!node_group) { return nullptr; } @@ -348,8 +346,14 @@ static int node_add_group_exec(bContext *C, wmOperator *op) ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + const char *node_idname = node_group_idname(C); + if (node_idname[0] == '\0') { + BKE_report(op->reports, RPT_WARNING, "Could not determine type of group node"); + return OPERATOR_CANCELLED; + } + bNode *group_node = node_add_node(*C, - node_group_idname(C), + node_idname, (node_group->type == NTREE_CUSTOM) ? NODE_CUSTOM_GROUP : NODE_GROUP, snode->runtime->cursor[0], @@ -365,9 +369,25 @@ static int node_add_group_exec(bContext *C, wmOperator *op) nodeSetActive(ntree, group_node); ED_node_tree_propagate_change(C, bmain, nullptr); + DEG_relations_tag_update(bmain); return OPERATOR_FINISHED; } +static bool node_add_group_poll(bContext *C) +{ + if (!ED_operator_node_editable(C)) { + return false; + } + const SpaceNode *snode = CTX_wm_space_node(C); + if (snode->edittree->type == NTREE_CUSTOM) { + CTX_wm_operator_poll_msg_set(C, + "This node editor displays a custom (Python defined) node tree. " + "Dropping node groups isn't supported for this."); + return false; + } + return true; +} + static int node_add_group_invoke(bContext *C, wmOperator *op, const wmEvent *event) { ARegion *region = CTX_wm_region(C); @@ -396,12 +416,12 @@ void NODE_OT_add_group(wmOperatorType *ot) /* callbacks */ ot->exec = node_add_group_exec; ot->invoke = node_add_group_invoke; - ot->poll = ED_operator_node_editable; + ot->poll = node_add_group_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; - RNA_def_string(ot->srna, "name", "Mask", MAX_ID_NAME - 2, "Name", "Data-block name to assign"); + WM_operator_properties_id_lookup(ot, true); } /** \} */ @@ -410,26 +430,16 @@ void NODE_OT_add_group(wmOperatorType *ot) /** \name Add Node Object Operator * \{ */ -static Object *node_add_object_get_and_poll_object_node_tree(Main *bmain, wmOperator *op) -{ - if (RNA_struct_property_is_set(op->ptr, "session_uuid")) { - const uint32_t session_uuid = (uint32_t)RNA_int_get(op->ptr, "session_uuid"); - return (Object *)BKE_libblock_find_session_uuid(bmain, ID_OB, session_uuid); - } - - char name[MAX_ID_NAME - 2]; - RNA_string_get(op->ptr, "name", name); - return (Object *)BKE_libblock_find_name(bmain, ID_OB, name); -} - static int node_add_object_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); SpaceNode *snode = CTX_wm_space_node(C); bNodeTree *ntree = snode->edittree; - Object *object; - if (!(object = node_add_object_get_and_poll_object_node_tree(bmain, op))) { + Object *object = reinterpret_cast<Object *>( + WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_OB)); + + if (!object) { return OPERATOR_CANCELLED; } @@ -486,8 +496,6 @@ static bool node_add_object_poll(bContext *C) void NODE_OT_add_object(wmOperatorType *ot) { - PropertyRNA *prop; - /* identifiers */ ot->name = "Add Node Object"; ot->description = "Add an object info node to the current node editor"; @@ -501,17 +509,7 @@ void NODE_OT_add_object(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; - RNA_def_string(ot->srna, "name", "Object", MAX_ID_NAME - 2, "Name", "Data-block name to assign"); - prop = RNA_def_int(ot->srna, - "session_uuid", - 0, - INT32_MIN, - INT32_MAX, - "Session UUID", - "Session UUID of the data-block to assign", - INT32_MIN, - INT32_MAX); - RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); + WM_operator_properties_id_lookup(ot, true); } /** \} */ @@ -520,27 +518,16 @@ void NODE_OT_add_object(wmOperatorType *ot) /** \name Add Node Collection Operator * \{ */ -static Collection *node_add_collection_get_and_poll_collection_node_tree(Main *bmain, - wmOperator *op) -{ - if (RNA_struct_property_is_set(op->ptr, "session_uuid")) { - const uint32_t session_uuid = (uint32_t)RNA_int_get(op->ptr, "session_uuid"); - return (Collection *)BKE_libblock_find_session_uuid(bmain, ID_GR, session_uuid); - } - - char name[MAX_ID_NAME - 2]; - RNA_string_get(op->ptr, "name", name); - return (Collection *)BKE_libblock_find_name(bmain, ID_GR, name); -} - static int node_add_collection_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); SpaceNode &snode = *CTX_wm_space_node(C); bNodeTree *ntree = snode.edittree; - Collection *collection; - if (!(collection = node_add_collection_get_and_poll_collection_node_tree(bmain, op))) { + Collection *collection = reinterpret_cast<Collection *>( + WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_GR)); + + if (!collection) { return OPERATOR_CANCELLED; } @@ -597,8 +584,6 @@ static bool node_add_collection_poll(bContext *C) void NODE_OT_add_collection(wmOperatorType *ot) { - PropertyRNA *prop; - /* identifiers */ ot->name = "Add Node Collection"; ot->description = "Add an collection info node to the current node editor"; @@ -612,18 +597,7 @@ void NODE_OT_add_collection(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; - RNA_def_string( - ot->srna, "name", "Collection", MAX_ID_NAME - 2, "Name", "Data-block name to assign"); - prop = RNA_def_int(ot->srna, - "session_uuid", - 0, - INT32_MIN, - INT32_MAX, - "Session UUID", - "Session UUID of the data-block to assign", - INT32_MIN, - INT32_MAX); - RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); + WM_operator_properties_id_lookup(ot, true); } /** \} */ @@ -745,7 +719,7 @@ void NODE_OT_add_file(wmOperatorType *ot) WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH, FILE_DEFAULTDISPLAY, FILE_SORT_DEFAULT); - RNA_def_string(ot->srna, "name", "Image", MAX_ID_NAME - 2, "Name", "Data-block name to assign"); + WM_operator_properties_id_lookup(ot, true); } /** \} */ @@ -754,18 +728,6 @@ void NODE_OT_add_file(wmOperatorType *ot) /** \name Add Mask Node Operator * \{ */ -static ID *node_add_mask_get_and_poll_mask(Main *bmain, wmOperator *op) -{ - if (RNA_struct_property_is_set(op->ptr, "session_uuid")) { - const uint32_t session_uuid = (uint32_t)RNA_int_get(op->ptr, "session_uuid"); - return BKE_libblock_find_session_uuid(bmain, ID_MSK, session_uuid); - } - - char name[MAX_ID_NAME - 2]; - RNA_string_get(op->ptr, "name", name); - return BKE_libblock_find_name(bmain, ID_MSK, name); -} - static bool node_add_mask_poll(bContext *C) { SpaceNode *snode = CTX_wm_space_node(C); @@ -779,7 +741,7 @@ static int node_add_mask_exec(bContext *C, wmOperator *op) SpaceNode &snode = *CTX_wm_space_node(C); bNode *node; - ID *mask = node_add_mask_get_and_poll_mask(bmain, op); + ID *mask = WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_MSK); if (!mask) { return OPERATOR_CANCELLED; } @@ -805,8 +767,6 @@ static int node_add_mask_exec(bContext *C, wmOperator *op) void NODE_OT_add_mask(wmOperatorType *ot) { - PropertyRNA *prop; - /* identifiers */ ot->name = "Add Mask Node"; ot->description = "Add a mask node to the current node editor"; @@ -819,17 +779,7 @@ void NODE_OT_add_mask(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; - RNA_def_string(ot->srna, "name", "Mask", MAX_ID_NAME - 2, "Name", "Data-block name to assign"); - prop = RNA_def_int(ot->srna, - "session_uuid", - 0, - INT32_MIN, - INT32_MAX, - "Session UUID", - "Session UUID of the data-block to assign", - INT32_MIN, - INT32_MAX); - RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); + WM_operator_properties_id_lookup(ot, true); } /** \} */ diff --git a/source/blender/editors/space_node/node_context_path.cc b/source/blender/editors/space_node/node_context_path.cc index 4247d5a1fbc..b9bee3ed15e 100644 --- a/source/blender/editors/space_node/node_context_path.cc +++ b/source/blender/editors/space_node/node_context_path.cc @@ -26,8 +26,6 @@ #include "UI_interface.hh" #include "UI_resources.h" -#include "UI_interface.hh" - #include "node_intern.hh" struct Curve; @@ -139,7 +137,7 @@ static void get_context_path_node_geometry(const bContext &C, Object *object = CTX_data_active_object(&C); ui::context_path_add_generic(path, RNA_Object, object); ModifierData *modifier = BKE_object_active_modifier(object); - ui::context_path_add_generic(path, RNA_Modifier, modifier, ICON_MODIFIER); + ui::context_path_add_generic(path, RNA_Modifier, modifier, ICON_GEOMETRY_NODES); context_path_add_node_tree_and_node_groups(snode, path); } } diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 9076b17a926..7003d51b2b6 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -66,6 +66,7 @@ #include "NOD_geometry_nodes_eval_log.hh" #include "NOD_node_declaration.hh" +#include "NOD_socket_declarations_geometry.hh" #include "FN_field.hh" #include "FN_field_cpp_type.hh" @@ -75,7 +76,7 @@ using blender::GPointer; using blender::fn::GField; namespace geo_log = blender::nodes::geometry_nodes_eval_log; -using geo_log::NamedAttributeUsage; +using geo_log::eNamedAttrUsage; extern "C" { /* XXX interface.h */ @@ -871,7 +872,8 @@ static void create_inspection_string_for_gfield(const geo_log::GFieldValueLog &v } static void create_inspection_string_for_geometry(const geo_log::GeometryValueLog &value_log, - std::stringstream &ss) + std::stringstream &ss, + const nodes::decl::Geometry *geometry) { Span<GeometryComponentType> component_types = value_log.component_types(); if (component_types.is_empty()) { @@ -895,9 +897,9 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo BLI_snprintf(line, sizeof(line), TIP_("\u2022 Mesh: %s vertices, %s edges, %s faces"), - to_string(mesh_info.tot_verts).c_str(), - to_string(mesh_info.tot_edges).c_str(), - to_string(mesh_info.tot_faces).c_str()); + to_string(mesh_info.verts_num).c_str(), + to_string(mesh_info.edges_num).c_str(), + to_string(mesh_info.faces_num).c_str()); ss << line << line_end; break; } @@ -908,7 +910,7 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo BLI_snprintf(line, sizeof(line), TIP_("\u2022 Point Cloud: %s points"), - to_string(pointcloud_info.tot_points).c_str()); + to_string(pointcloud_info.points_num).c_str()); ss << line << line_end; break; } @@ -918,7 +920,7 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo BLI_snprintf(line, sizeof(line), TIP_("\u2022 Curve: %s splines"), - to_string(curve_info.tot_splines).c_str()); + to_string(curve_info.splines_num).c_str()); ss << line << line_end; break; } @@ -928,7 +930,7 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo BLI_snprintf(line, sizeof(line), TIP_("\u2022 Instances: %s"), - to_string(instances_info.tot_instances).c_str()); + to_string(instances_info.instances_num).c_str()); ss << line << line_end; break; } @@ -938,6 +940,45 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo } } } + + /* If the geometry declaration is null, as is the case for input to group output, + * or it is an output socket don't show supported types. */ + if (geometry == nullptr || geometry->in_out() == SOCK_OUT) { + return; + } + + Span<GeometryComponentType> supported_types = geometry->supported_types(); + if (supported_types.is_empty()) { + ss << ".\n\n" << TIP_("Supported: All Types"); + return; + } + + ss << ".\n\n" << TIP_("Supported: "); + for (GeometryComponentType type : supported_types) { + switch (type) { + case GEO_COMPONENT_TYPE_MESH: { + ss << TIP_("Mesh"); + break; + } + case GEO_COMPONENT_TYPE_POINT_CLOUD: { + ss << TIP_("Point Cloud"); + break; + } + case GEO_COMPONENT_TYPE_CURVE: { + ss << TIP_("Curve"); + break; + } + case GEO_COMPONENT_TYPE_INSTANCES: { + ss << TIP_("Instances"); + break; + } + case GEO_COMPONENT_TYPE_VOLUME: { + ss << TIP_("Volume"); + break; + } + } + ss << ((type == supported_types.last()) ? "" : ", "); + } } static std::optional<std::string> create_socket_inspection_string(bContext *C, @@ -970,7 +1011,10 @@ static std::optional<std::string> create_socket_inspection_string(bContext *C, } else if (const geo_log::GeometryValueLog *geo_value_log = dynamic_cast<const geo_log::GeometryValueLog *>(value_log)) { - create_inspection_string_for_geometry(*geo_value_log, ss); + create_inspection_string_for_geometry( + *geo_value_log, + ss, + dynamic_cast<const nodes::decl::Geometry *>(socket.runtime->declaration)); } return ss.str(); @@ -982,8 +1026,8 @@ static bool node_socket_has_tooltip(bNodeTree *ntree, bNodeSocket *socket) return true; } - if (socket->declaration != nullptr) { - const blender::nodes::SocketDeclaration &socket_decl = *socket->declaration; + if (socket->runtime->declaration != nullptr) { + const blender::nodes::SocketDeclaration &socket_decl = *socket->runtime->declaration; return !socket_decl.description().is_empty(); } @@ -996,8 +1040,8 @@ static char *node_socket_get_tooltip(bContext *C, bNodeSocket *socket) { std::stringstream output; - if (socket->declaration != nullptr) { - const blender::nodes::SocketDeclaration &socket_decl = *socket->declaration; + if (socket->runtime->declaration != nullptr) { + const blender::nodes::SocketDeclaration &socket_decl = *socket->runtime->declaration; blender::StringRef description = socket_decl.description(); if (!description.is_empty()) { output << TIP_(description.data()); @@ -1695,7 +1739,7 @@ struct NodeExtraInfoRow { }; struct NamedAttributeTooltipArg { - Map<std::string, NamedAttributeUsage> usage_by_attribute; + Map<std::string, eNamedAttrUsage> usage_by_attribute; }; static char *named_attribute_tooltip(bContext *UNUSED(C), void *argN, const char *UNUSED(tip)) @@ -1707,7 +1751,7 @@ static char *named_attribute_tooltip(bContext *UNUSED(C), void *argN, const char struct NameWithUsage { StringRefNull name; - NamedAttributeUsage usage; + eNamedAttrUsage usage; }; Vector<NameWithUsage> sorted_used_attribute; @@ -1722,16 +1766,16 @@ static char *named_attribute_tooltip(bContext *UNUSED(C), void *argN, const char for (const NameWithUsage &attribute : sorted_used_attribute) { const StringRefNull name = attribute.name; - const NamedAttributeUsage usage = attribute.usage; + const eNamedAttrUsage usage = attribute.usage; ss << " \u2022 \"" << name << "\": "; Vector<std::string> usages; - if ((usage & NamedAttributeUsage::Read) != NamedAttributeUsage::None) { + if ((usage & eNamedAttrUsage::Read) != eNamedAttrUsage::None) { usages.append(TIP_("read")); } - if ((usage & NamedAttributeUsage::Write) != NamedAttributeUsage::None) { + if ((usage & eNamedAttrUsage::Write) != eNamedAttrUsage::None) { usages.append(TIP_("write")); } - if ((usage & NamedAttributeUsage::Remove) != NamedAttributeUsage::None) { + if ((usage & eNamedAttrUsage::Remove) != eNamedAttrUsage::None) { usages.append(TIP_("remove")); } for (const int i : usages.index_range()) { @@ -1749,7 +1793,7 @@ static char *named_attribute_tooltip(bContext *UNUSED(C), void *argN, const char } static NodeExtraInfoRow row_from_used_named_attribute( - const Map<std::string, NamedAttributeUsage> &usage_by_attribute_name) + const Map<std::string, eNamedAttrUsage> &usage_by_attribute_name) { const int attributes_num = usage_by_attribute_name.size(); @@ -1777,7 +1821,7 @@ static std::optional<NodeExtraInfoRow> node_get_accessed_attributes_row(const Sp return std::nullopt; } - Map<std::string, NamedAttributeUsage> usage_by_attribute; + Map<std::string, eNamedAttrUsage> usage_by_attribute; tree_log->foreach_node_log([&](const geo_log::NodeLog &node_log) { for (const geo_log::UsedNamedAttribute &used_attribute : node_log.used_named_attributes()) { usage_by_attribute.lookup_or_add_as(used_attribute.name, @@ -1807,7 +1851,7 @@ static std::optional<NodeExtraInfoRow> node_get_accessed_attributes_row(const Sp if (node_log == nullptr) { return std::nullopt; } - Map<std::string, NamedAttributeUsage> usage_by_attribute; + Map<std::string, eNamedAttrUsage> usage_by_attribute; for (const geo_log::UsedNamedAttribute &used_attribute : node_log->used_named_attributes()) { usage_by_attribute.lookup_or_add_as(used_attribute.name, used_attribute.usage) |= used_attribute.usage; @@ -2654,7 +2698,7 @@ static void frame_node_draw_label(const bNodeTree &ntree, BLF_enable(fontid, BLF_ASPECT); BLF_aspect(fontid, aspect, aspect, 1.0f); /* clamp otherwise it can suck up a LOT of memory */ - BLF_size(fontid, MIN2(24.0f, font_size), U.dpi); + BLF_size(fontid, MIN2(24.0f, font_size) * U.pixelsize, U.dpi); /* title color */ int color_id = node_get_colorid(node); diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index 2d7972e2291..ffc9befc81c 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -15,6 +15,7 @@ #include "DNA_text_types.h" #include "DNA_world_types.h" +#include "BKE_callbacks.h" #include "BKE_context.h" #include "BKE_global.h" #include "BKE_image.h" @@ -66,7 +67,9 @@ namespace blender::ed::space_node { #define USE_ESC_COMPO -/* ***************** composite job manager ********************** */ +/* -------------------------------------------------------------------- */ +/** \name Composite Job Manager + * \{ */ enum { COM_RECALC_COMPOSITE = 1, @@ -273,6 +276,7 @@ static void compo_startjob(void *cjv, // XXX BIF_store_spare(); /* 1 is do_previews */ + BKE_callback_exec_id(cj->bmain, &scene->id, BKE_CB_EVT_COMPOSITE_PRE); if ((cj->scene->r.scemode & R_MULTIVIEW) == 0) { ntreeCompositExecTree(cj->scene, ntree, &cj->scene->r, false, true, ""); @@ -291,8 +295,30 @@ static void compo_startjob(void *cjv, ntree->progress = nullptr; } +static void compo_canceljob(void *cjv) +{ + CompoJob *cj = (CompoJob *)cjv; + Main *bmain = cj->bmain; + Scene *scene = cj->scene; + BKE_callback_exec_id(bmain, &scene->id, BKE_CB_EVT_COMPOSITE_CANCEL); +} + +static void compo_completejob(void *cjv) +{ + CompoJob *cj = (CompoJob *)cjv; + Main *bmain = cj->bmain; + Scene *scene = cj->scene; + BKE_callback_exec_id(bmain, &scene->id, BKE_CB_EVT_COMPOSITE_POST); +} + +/** \} */ + } // namespace blender::ed::space_node +/* -------------------------------------------------------------------- */ +/** \name Composite Job C API + * \{ */ + void ED_node_composite_job(const bContext *C, struct bNodeTree *nodetree, Scene *scene_owner) { using namespace blender::ed::space_node; @@ -331,14 +357,24 @@ void ED_node_composite_job(const bContext *C, struct bNodeTree *nodetree, Scene /* setup job */ WM_jobs_customdata_set(wm_job, cj, compo_freejob); WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_COMPO_RESULT, NC_SCENE | ND_COMPO_RESULT); - WM_jobs_callbacks(wm_job, compo_startjob, compo_initjob, compo_updatejob, nullptr); + WM_jobs_callbacks_ex(wm_job, + compo_startjob, + compo_initjob, + compo_updatejob, + nullptr, + compo_completejob, + compo_canceljob); WM_jobs_start(CTX_wm_manager(C), wm_job); } +/** \} */ + namespace blender::ed::space_node { -/* ***************************************** */ +/* -------------------------------------------------------------------- */ +/** \name Composite Poll & Utility Functions + * \{ */ bool composite_node_active(bContext *C) { @@ -388,8 +424,14 @@ static void send_notifiers_after_tree_change(ID *id, bNodeTree *ntree) } } +/** \} */ + } // namespace blender::ed::space_node +/* -------------------------------------------------------------------- */ +/** \name Node Editor Public API Functions + * \{ */ + void ED_node_tree_propagate_change(const bContext *C, Main *bmain, bNodeTree *root_ntree) { if (C != nullptr) { @@ -783,9 +825,13 @@ void ED_node_post_apply_transform(bContext *UNUSED(C), bNodeTree *UNUSED(ntree)) // node_update_nodetree(C, ntree, 0.0f, 0.0f); } +/** \} */ + namespace blender::ed::space_node { -/* ***************** generic operator functions for nodes ***************** */ +/* -------------------------------------------------------------------- */ +/** \name Generic Operator Functions for Nodes + * \{ */ #if 0 /* UNUSED */ @@ -861,7 +907,11 @@ static void edit_node_properties_get( } #endif -/* ************************** Node generic ************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Generic + * \{ */ /* is rct in visible part of node? */ static bNode *visible_node(SpaceNode &snode, const rctf &rct) @@ -874,7 +924,11 @@ static bNode *visible_node(SpaceNode &snode, const rctf &rct) return nullptr; } -/* ********************** size widget operator ******************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Size Widget Operator + * \{ */ struct NodeSizeWidget { float mxstart, mystart; @@ -1077,7 +1131,11 @@ void NODE_OT_resize(wmOperatorType *ot) ot->flag = OPTYPE_BLOCKING; } -/* ********************** hidden sockets ******************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Hidden Sockets + * \{ */ bool node_has_hidden_sockets(bNode *node) { @@ -1211,7 +1269,11 @@ bool node_find_indicated_socket(SpaceNode &snode, return false; } -/* ****************** Link Dimming *********************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Link Dimming + * \{ */ float node_link_dim_factor(const View2D &v2d, const bNodeLink &link) { @@ -1237,7 +1299,11 @@ bool node_link_is_hidden_or_dimmed(const View2D &v2d, const bNodeLink &link) return nodeLinkIsHidden(&link) || node_link_dim_factor(v2d, link) < 0.5f; } -/* ****************** Duplicate *********************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Duplicate Operator + * \{ */ static void node_duplicate_reparent_recursive(const Map<const bNode *, bNode *> &node_map, bNode *node) @@ -1422,8 +1488,7 @@ void node_select_all(ListBase *lb, int action) } } -/* ******************************** */ -/* XXX some code needing updating to operators. */ +/* XXX: some code needing updating to operators. */ /* goes over all scenes, reads render layers */ static int node_read_viewlayers_exec(bContext *C, wmOperator *UNUSED(op)) @@ -1526,7 +1591,11 @@ void NODE_OT_render_changed(wmOperatorType *ot) ot->flag = 0; } -/* ****************** Hide operator *********************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Hide Operator + * \{ */ static void node_flag_toggle_exec(SpaceNode *snode, int toggle_flag) { @@ -1722,7 +1791,11 @@ void NODE_OT_hide_socket_toggle(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Mute operator *********************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Mute Operator + * \{ */ static int node_mute_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -1758,7 +1831,11 @@ void NODE_OT_mute_toggle(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Delete operator ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Delete Operator + * \{ */ static int node_delete_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -1793,7 +1870,11 @@ void NODE_OT_delete(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Switch View ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Switch View + * \{ */ static bool node_switch_view_poll(bContext *C) { @@ -1837,7 +1918,12 @@ void NODE_OT_switch_view_update(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Delete with reconnect ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Delete with Reconnect Operator + * \{ */ + static int node_delete_reconnect_exec(bContext *C, wmOperator *UNUSED(op)) { Main *bmain = CTX_data_main(C); @@ -1872,7 +1958,11 @@ void NODE_OT_delete_reconnect(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** File Output Add Socket ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node File Output Add Socket Operator + * \{ */ static int node_output_file_add_socket_exec(bContext *C, wmOperator *op) { @@ -1922,7 +2012,11 @@ void NODE_OT_output_file_add_socket(wmOperatorType *ot) ot->srna, "file_path", "Image", MAX_NAME, "File Path", "Subpath of the output file"); } -/* ****************** Multi File Output Remove Socket ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Multi File Output Remove Socket Operator + * \{ */ static int node_output_file_remove_active_socket_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -1968,7 +2062,11 @@ void NODE_OT_output_file_remove_active_socket(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Multi File Output Move Socket ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Multi File Output Move Socket Node + * \{ */ static int node_output_file_move_active_socket_exec(bContext *C, wmOperator *op) { @@ -2040,7 +2138,11 @@ void NODE_OT_output_file_move_active_socket(wmOperatorType *ot) RNA_def_enum(ot->srna, "direction", direction_items, 2, "Direction", ""); } -/* ****************** Copy Node Color ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Copy Node Color Operator + * \{ */ static int node_copy_color_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -2085,7 +2187,11 @@ void NODE_OT_node_copy_color(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Copy to clipboard ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Copy to Clipboard Operator + * \{ */ static int node_clipboard_copy_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -2163,7 +2269,11 @@ void NODE_OT_clipboard_copy(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Paste from clipboard ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Paste from Clipboard + * \{ */ static int node_clipboard_paste_exec(bContext *C, wmOperator *op) { @@ -2287,7 +2397,11 @@ void NODE_OT_clipboard_paste(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/********************** Add interface socket operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node-Tree Add Interface Socket Operator + * \{ */ static bNodeSocket *ntree_get_active_interface_socket(ListBase *lb) { @@ -2357,7 +2471,11 @@ void NODE_OT_tree_socket_add(wmOperatorType *ot) RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Type", ""); } -/********************** Remove interface socket operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node-Tree Remove Interface Socket Operator + * \{ */ static int ntree_socket_remove_exec(bContext *C, wmOperator *op) { @@ -2403,7 +2521,11 @@ void NODE_OT_tree_socket_remove(wmOperatorType *ot) RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Type", ""); } -/********************** Change interface socket type operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node-Tree Change Interface Socket Type Operator + * \{ */ static int ntree_socket_change_type_exec(bContext *C, wmOperator *op) { @@ -2503,7 +2625,11 @@ void NODE_OT_tree_socket_change_type(wmOperatorType *ot) ot->prop = prop; } -/********************** Move interface socket operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node-Tree Move Interface Socket Operator + * \{ */ static const EnumPropertyItem move_direction_items[] = { {1, "UP", 0, "Up", ""}, @@ -2577,7 +2703,11 @@ void NODE_OT_tree_socket_move(wmOperatorType *ot) RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Type", ""); } -/* ********************** Shader Script Update ******************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Shader Script Update + * \{ */ static bool node_shader_script_update_poll(bContext *C) { @@ -2722,7 +2852,11 @@ void NODE_OT_shader_script_update(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ********************** Viewer border ******************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Viewer Border + * \{ */ static void viewer_border_corner_to_backdrop(SpaceNode *snode, ARegion *region, @@ -2844,7 +2978,11 @@ void NODE_OT_clear_viewer_border(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Cryptomatte Add Socket ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Cryptomatte Add Socket + * \{ */ static int node_cryptomatte_add_socket_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -2888,7 +3026,11 @@ void NODE_OT_cryptomatte_layer_add(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Cryptomatte Remove Socket ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Cryptomatte Remove Socket + * \{ */ static int node_cryptomatte_remove_socket_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -2933,4 +3075,7 @@ void NODE_OT_cryptomatte_layer_remove(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } + +/** \} */ + } // namespace blender::ed::space_node diff --git a/source/blender/editors/space_node/node_geometry_attribute_search.cc b/source/blender/editors/space_node/node_geometry_attribute_search.cc index fee64da0459..f08e23c8371 100644 --- a/source/blender/editors/space_node/node_geometry_attribute_search.cc +++ b/source/blender/editors/space_node/node_geometry_attribute_search.cc @@ -82,8 +82,10 @@ static Vector<const GeometryAttributeInfo *> get_attribute_info_from_context( if (const geo_log::GeometryValueLog *geo_value_log = dynamic_cast<const geo_log::GeometryValueLog *>(value_log)) { for (const GeometryAttributeInfo &attribute : geo_value_log->attributes()) { - if (names.add(attribute.name)) { - attributes.append(&attribute); + if (bke::allow_procedural_attribute_access(attribute.name)) { + if (names.add(attribute.name)) { + attributes.append(&attribute); + } } } } @@ -126,7 +128,7 @@ static void attribute_search_update_fn( * Some custom data types don't correspond to node types and therefore can't be * used by the named attribute input node. Find the best option or fallback to float. */ -static CustomDataType data_type_in_attribute_input_node(const CustomDataType type) +static eCustomDataType data_type_in_attribute_input_node(const eCustomDataType type) { switch (type) { case CD_PROP_FLOAT: @@ -183,9 +185,9 @@ static void attribute_search_exec_fn(bContext *C, void *data_v, void *item_v) BLI_assert(socket->type == SOCK_STRING); /* For the attribute input node, also adjust the type and links connected to the output. */ - if (node->type == GEO_NODE_INPUT_NAMED_ATTRIBUTE) { + if (node->type == GEO_NODE_INPUT_NAMED_ATTRIBUTE && item->data_type.has_value()) { NodeGeometryInputNamedAttribute &storage = *(NodeGeometryInputNamedAttribute *)node->storage; - const CustomDataType new_type = data_type_in_attribute_input_node(item->data_type); + const eCustomDataType new_type = data_type_in_attribute_input_node(*item->data_type); if (new_type != storage.data_type) { storage.data_type = new_type; /* Make the output socket with the new type on the attribute input node active. */ diff --git a/source/blender/editors/space_node/node_intern.hh b/source/blender/editors/space_node/node_intern.hh index 10d4ad36d95..924537d0e8a 100644 --- a/source/blender/editors/space_node/node_intern.hh +++ b/source/blender/editors/space_node/node_intern.hh @@ -31,7 +31,9 @@ struct wmKeyConfig; struct wmWindow; /* Outside of blender namespace to avoid Python documentation build error with `ctypes`. */ +extern "C" { extern const char *node_context_dir[]; +}; namespace blender::ed::space_node { @@ -74,8 +76,18 @@ struct SpaceNode_Runtime { /** Mouse position for drawing socket-less links and adding nodes. */ float2 cursor; - /** For auto compositing. */ - bool recalc; + /** + * Indicates that the compositing tree in the space needs to be re-evaluated using the + * auto-compositing pipeline. + * Takes priority over the regular compositing. + */ + bool recalc_auto_compositing; + + /** + * Indicates that the compositing int the space tree needs to be re-evaluated using + * regular compositing pipeline. + */ + bool recalc_regular_compositing; /** Temporary data for modal linking operator. */ std::unique_ptr<bNodeLinkDrag> linkdrag; @@ -94,7 +106,7 @@ enum NodeResizeDirection { }; ENUM_OPERATORS(NodeResizeDirection, NODE_RESIZE_LEFT); -/* Nodes draw without dpi - the view zoom is flexible. */ +/* Nodes draw without DPI - the view zoom is flexible. */ #define HIDDEN_RAD (0.75f * U.widget_unit) #define BASIS_RAD (0.2f * U.widget_unit) #define NODE_DYS (U.widget_unit / 2) @@ -117,8 +129,6 @@ ENUM_OPERATORS(NodeResizeDirection, NODE_RESIZE_LEFT); */ float2 space_node_group_offset(const SpaceNode &snode); -rctf node_frame_rect_inside(const bNode &node); - int node_get_resize_cursor(NodeResizeDirection directions); /** * Usual convention here would be #node_socket_get_color(), @@ -155,6 +165,9 @@ void node_keymap(wmKeyConfig *keyconf); /* node_select.cc */ +rctf node_frame_rect_inside(const bNode &node); +bool node_or_socket_isect_event(bContext *C, const wmEvent *event); + void node_deselect_all(SpaceNode &snode); void node_socket_select(bNode *node, bNodeSocket &sock); void node_socket_deselect(bNode *node, bNodeSocket &sock, bool deselect_node); diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc index c757fb46407..e10bedb18f4 100644 --- a/source/blender/editors/space_node/node_relationships.cc +++ b/source/blender/editors/space_node/node_relationships.cc @@ -454,7 +454,7 @@ static bool socket_can_be_viewed(const OutputSocketRef &socket) SOCK_RGBA); } -static CustomDataType socket_type_to_custom_data_type(const eNodeSocketDatatype socket_type) +static eCustomDataType socket_type_to_custom_data_type(const eNodeSocketDatatype socket_type) { switch (socket_type) { case SOCK_FLOAT: @@ -491,7 +491,7 @@ static bNodeSocket *node_link_viewer_get_socket(bNodeTree &ntree, return viewer_socket; } NodeGeometryViewer *storage = (NodeGeometryViewer *)viewer_node.storage; - const CustomDataType data_type = socket_type_to_custom_data_type( + const eCustomDataType data_type = socket_type_to_custom_data_type( (eNodeSocketDatatype)src_socket.type); BLI_assert(data_type != CD_AUTO_FROM_NAME); storage->data_type = data_type; @@ -2048,7 +2048,7 @@ static bNodeSocket *get_main_socket(bNodeTree &ntree, bNode &node, eNodeSocketIn /* Try to get the main socket based on the socket declaration. */ nodeDeclarationEnsure(&ntree, &node); - const nodes::NodeDeclaration *node_decl = node.declaration; + const nodes::NodeDeclaration *node_decl = node.runtime->declaration; if (node_decl != nullptr) { Span<nodes::SocketDeclarationPtr> socket_decls = (in_out == SOCK_IN) ? node_decl->inputs() : node_decl->outputs(); diff --git a/source/blender/editors/space_node/node_select.cc b/source/blender/editors/space_node/node_select.cc index 1d0097068f1..9d73156edab 100644 --- a/source/blender/editors/space_node/node_select.cc +++ b/source/blender/editors/space_node/node_select.cc @@ -48,6 +48,8 @@ namespace blender::ed::space_node { +static bool is_event_over_node_or_socket(bContext *C, const wmEvent *event); + /** * Function to detect if there is a visible view3d that uses workbench in texture mode. * This function is for fixing T76970 for Blender 2.83. The actual fix should add a mechanism in @@ -98,6 +100,11 @@ rctf node_frame_rect_inside(const bNode &node) return frame_inside; } +bool node_or_socket_isect_event(bContext *C, const wmEvent *event) +{ + return is_event_over_node_or_socket(C, event); +} + static bool node_frame_select_isect_mouse(bNode *node, const float2 &mouse) { /* Frame nodes are selectable by their borders (including their whole rect - as for other nodes - @@ -193,11 +200,6 @@ static bool is_event_over_node_or_socket(bContext *C, const wmEvent *event) return is_position_over_node_or_socket(*snode, mouse); } -static void node_toggle(bNode *node) -{ - nodeSetSelected(node, !(node->flag & SELECT)); -} - void node_socket_select(bNode *node, bNodeSocket &sock) { sock.flag |= SELECT; @@ -510,10 +512,10 @@ void node_select_single(bContext &C, bNode &node) WM_event_add_notifier(&C, NC_NODE | NA_SELECTED, nullptr); } -static int node_mouse_select(bContext *C, - wmOperator *op, - const int mval[2], - bool wait_to_deselect_others) +static bool node_mouse_select(bContext *C, + wmOperator *op, + const int mval[2], + struct SelectPick_Params *params) { Main &bmain = *CTX_data_main(C); SpaceNode &snode = *CTX_wm_space_node(C); @@ -525,36 +527,38 @@ static int node_mouse_select(bContext *C, bNodeSocket *sock = nullptr; bNodeSocket *tsock; float cursor[2]; - int ret_value = OPERATOR_CANCELLED; - const bool extend = RNA_boolean_get(op->ptr, "extend"); /* always do socket_select when extending selection. */ - const bool socket_select = extend || RNA_boolean_get(op->ptr, "socket_select"); - const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all"); - - /* These cases are never modal. */ - if (extend || socket_select) { - wait_to_deselect_others = false; - } + const bool socket_select = (params->sel_op == SEL_OP_XOR) || + RNA_boolean_get(op->ptr, "socket_select"); + bool changed = false; + bool found = false; + bool node_was_selected = false; /* get mouse coordinates in view2d space */ UI_view2d_region_to_view(®ion.v2d, mval[0], mval[1], &cursor[0], &cursor[1]); /* first do socket selection, these generally overlap with nodes. */ if (socket_select) { + /* NOTE: unlike nodes #SelectPick_Params isn't fully supported. */ + const bool extend = (params->sel_op == SEL_OP_XOR); if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_IN)) { + found = true; + node_was_selected = node->flag & SELECT; + /* NOTE: SOCK_IN does not take into account the extend case... * This feature is not really used anyway currently? */ node_socket_toggle(node, *sock, true); - ret_value = OPERATOR_FINISHED; + changed = true; } else if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_OUT)) { + found = true; + node_was_selected = node->flag & SELECT; + if (sock->flag & SELECT) { if (extend) { node_socket_deselect(node, *sock, true); - } - else { - ret_value = OPERATOR_FINISHED; + changed = true; } } else { @@ -566,6 +570,7 @@ static int node_mouse_select(bContext *C, continue; } node_socket_deselect(node, *tsock, true); + changed = true; } } if (!extend) { @@ -575,69 +580,71 @@ static int node_mouse_select(bContext *C, } for (tsock = (bNodeSocket *)tnode->outputs.first; tsock; tsock = tsock->next) { node_socket_deselect(tnode, *tsock, true); + changed = true; } } } node_socket_select(node, *sock); - ret_value = OPERATOR_FINISHED; + changed = true; } } } if (!sock) { + /* find the closest visible node */ node = node_under_mouse_select(*snode.edittree, (int)cursor[0], (int)cursor[1]); + found = (node != nullptr); + node_was_selected = node && (node->flag & SELECT); - if (extend) { - if (node != nullptr) { - /* If node is selected but not active, we want to make it active, - * but not toggle (deselect) it. */ - if (!((node->flag & SELECT) && (node->flag & NODE_ACTIVE) == 0)) { - node_toggle(node); - } - ret_value = OPERATOR_FINISHED; - } - } - else if (deselect_all && node == nullptr) { - /* Rather than deselecting others, users may want to drag to box-select (drag from empty - * space) or tweak-translate an already selected item. If these cases may apply, delay - * deselection. */ - if (wait_to_deselect_others) { - ret_value = OPERATOR_RUNNING_MODAL; + if (params->sel_op == SEL_OP_SET) { + if ((found && params->select_passthrough) && (node->flag & SELECT)) { + found = false; } - else { - /* Deselect in empty space. */ + else if (found || params->deselect_all) { + /* Deselect everything. */ for (tnode = (bNode *)snode.edittree->nodes.first; tnode; tnode = tnode->next) { nodeSetSelected(tnode, false); } - ret_value = OPERATOR_FINISHED; + changed = true; } } - else if (node != nullptr) { - /* When clicking on an already selected node, we want to wait to deselect - * others and allow the user to start moving the node without that. */ - if (wait_to_deselect_others && (node->flag & SELECT)) { - ret_value = OPERATOR_RUNNING_MODAL; - } - else { - nodeSetSelected(node, true); - for (tnode = (bNode *)snode.edittree->nodes.first; tnode; tnode = tnode->next) { - if (tnode != node) { - nodeSetSelected(tnode, false); - } + if (found) { + switch (params->sel_op) { + case SEL_OP_ADD: { + nodeSetSelected(node, true); + break; + } + case SEL_OP_SUB: { + nodeSetSelected(node, false); + break; + } + case SEL_OP_XOR: { + /* Check active so clicking on an inactive node activates it. */ + bool is_selected = (node->flag & NODE_SELECT) && (node->flag & NODE_ACTIVE); + nodeSetSelected(node, !is_selected); + break; + } + case SEL_OP_SET: { + nodeSetSelected(node, true); + break; + } + case SEL_OP_AND: { + BLI_assert_unreachable(); /* Doesn't make sense for picking. */ + break; } - - ret_value = OPERATOR_FINISHED; } + + changed = true; } } /* update node order */ - if (ret_value != OPERATOR_CANCELLED) { + if (changed || found) { bool active_texture_changed = false; bool viewer_node_changed = false; - if (node != nullptr && ret_value != OPERATOR_RUNNING_MODAL) { + if ((node != nullptr) && (node_was_selected == false || params->select_passthrough == false)) { viewer_node_changed = (node->flag & NODE_DO_OUTPUT) == 0 && node->type == GEO_NODE_VIEWER; ED_node_set_active(&bmain, &snode, snode.edittree, node, &active_texture_changed); } @@ -654,23 +661,35 @@ static int node_mouse_select(bContext *C, WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr); } - return ret_value; + return changed || found; } static int node_select_exec(bContext *C, wmOperator *op) { - const bool wait_to_deselect_others = RNA_boolean_get(op->ptr, "wait_to_deselect_others"); - /* get settings from RNA properties for operator */ int mval[2]; - mval[0] = RNA_int_get(op->ptr, "mouse_x"); - mval[1] = RNA_int_get(op->ptr, "mouse_y"); + RNA_int_get_array(op->ptr, "location", mval); + + struct SelectPick_Params params = {}; + ED_select_pick_params_from_operator(op->ptr, ¶ms); /* perform the select */ - const int ret_value = node_mouse_select(C, op, mval, wait_to_deselect_others); + const bool changed = node_mouse_select(C, op, mval, ¶ms); + + if (changed) { + return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED; + } + /* Nothing selected, just passthrough. */ + return OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED; +} + +static int node_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + RNA_int_set_array(op->ptr, "location", event->mval); - /* allow tweak event to work too */ - return ret_value | OPERATOR_PASS_THROUGH; + const int retval = node_select_exec(C, op); + + return WM_operator_flag_only_pass_through_on_press(retval, event); } void NODE_OT_select(wmOperatorType *ot) @@ -684,24 +703,29 @@ void NODE_OT_select(wmOperatorType *ot) /* api callbacks */ ot->exec = node_select_exec; - ot->invoke = WM_generic_select_invoke; - ot->modal = WM_generic_select_modal; + ot->invoke = node_select_invoke; ot->poll = ED_operator_node_active; + ot->get_name = ED_select_pick_get_name; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - WM_operator_properties_generic_select(ot); - prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", ""); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); + WM_operator_properties_mouse_select(ot); + + prop = RNA_def_int_vector(ot->srna, + "location", + 2, + nullptr, + INT_MIN, + INT_MAX, + "Location", + "Mouse location", + INT_MIN, + INT_MAX); + RNA_def_property_flag(prop, PROP_HIDDEN); + RNA_def_boolean(ot->srna, "socket_select", false, "Socket Select", ""); - prop = RNA_def_boolean(ot->srna, - "deselect_all", - false, - "Deselect On Nothing", - "Deselect all when nothing under the cursor"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); } /** \} */ @@ -871,8 +895,8 @@ void NODE_OT_select_circle(wmOperatorType *ot) ot->invoke = WM_gesture_circle_invoke; ot->exec = node_circleselect_exec; ot->modal = WM_gesture_circle_modal; - ot->poll = ED_operator_node_active; + ot->get_name = ED_select_circle_get_name; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; diff --git a/source/blender/editors/space_node/node_view.cc b/source/blender/editors/space_node/node_view.cc index 91a21527ac9..6f30632244b 100644 --- a/source/blender/editors/space_node/node_view.cc +++ b/source/blender/editors/space_node/node_view.cc @@ -643,6 +643,12 @@ static int sample_invoke(bContext *C, wmOperator *op, const wmEvent *event) ARegion *region = CTX_wm_region(C); ImageSampleInfo *info; + /* Don't handle events intended for nodes (which rely on click/drag distinction). + * which this operator would use since sampling is normally activated on press, see: T98191. */ + if (node_or_socket_isect_event(C, event)) { + return OPERATOR_PASS_THROUGH; + } + if (!ED_node_is_compositor(snode) || !(snode->flag & SNODE_BACKDRAW)) { return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/space_node/space_node.cc b/source/blender/editors/space_node/space_node.cc index 34f357ae5c3..15afd024766 100644 --- a/source/blender/editors/space_node/space_node.cc +++ b/source/blender/editors/space_node/space_node.cc @@ -324,6 +324,41 @@ static bool any_node_uses_id(const bNodeTree *ntree, const ID *id) return false; } +/** + * Tag the space to recalculate the compositing tree using auto-compositing pipeline. + * + * Will check the space to be using a compositing tree, and check whether auto-compositing + * is enabled. If the checks do not pass then the function has no affect. + */ +static void node_area_tag_recalc_auto_compositing(SpaceNode *snode, ScrArea *area) +{ + if (!ED_node_is_compositor(snode)) { + return; + } + + if (snode->flag & SNODE_AUTO_RENDER) { + snode->runtime->recalc_auto_compositing = true; + ED_area_tag_refresh(area); + } +} + +/** + * Tag the space to recalculate the current tree. + * + * For all node trees this will do `snode_set_context()` which takes care of setting an active + * tree. This will be done in the area refresh callback. + * + * For compositor tree this will additionally start of the compositor job. + */ +static void node_area_tag_tree_recalc(SpaceNode *snode, ScrArea *area) +{ + if (ED_node_is_compositor(snode)) { + snode->runtime->recalc_regular_compositing = true; + } + + ED_area_tag_refresh(area); +} + static void node_area_listener(const wmSpaceTypeListenerParams *params) { ScrArea *area = params->area; @@ -346,25 +381,20 @@ static void node_area_listener(const wmSpaceTypeListenerParams *params) UI_view2d_center_set(®ion->v2d, path->view_center[0], path->view_center[1]); } - ED_area_tag_refresh(area); + node_area_tag_tree_recalc(snode, area); break; } case ND_FRAME: - ED_area_tag_refresh(area); + node_area_tag_tree_recalc(snode, area); break; case ND_COMPO_RESULT: ED_area_tag_redraw(area); break; case ND_TRANSFORM_DONE: - if (ED_node_is_compositor(snode)) { - if (snode->flag & SNODE_AUTO_RENDER) { - snode->runtime->recalc = true; - ED_area_tag_refresh(area); - } - } + node_area_tag_recalc_auto_compositing(snode, area); break; case ND_LAYER_CONTENT: - ED_area_tag_refresh(area); + node_area_tag_tree_recalc(snode, area); break; } break; @@ -373,46 +403,46 @@ static void node_area_listener(const wmSpaceTypeListenerParams *params) case NC_MATERIAL: if (ED_node_is_shader(snode)) { if (wmn->data == ND_SHADING) { - ED_area_tag_refresh(area); + node_area_tag_tree_recalc(snode, area); } else if (wmn->data == ND_SHADING_DRAW) { - ED_area_tag_refresh(area); + node_area_tag_tree_recalc(snode, area); } else if (wmn->data == ND_SHADING_LINKS) { - ED_area_tag_refresh(area); + node_area_tag_tree_recalc(snode, area); } } break; case NC_TEXTURE: if (ED_node_is_shader(snode) || ED_node_is_texture(snode)) { if (wmn->data == ND_NODES) { - ED_area_tag_refresh(area); + node_area_tag_tree_recalc(snode, area); } } break; case NC_WORLD: if (ED_node_is_shader(snode) && shader_type == SNODE_SHADER_WORLD) { - ED_area_tag_refresh(area); + node_area_tag_tree_recalc(snode, area); } break; case NC_OBJECT: if (ED_node_is_shader(snode)) { if (wmn->data == ND_OB_SHADING) { - ED_area_tag_refresh(area); + node_area_tag_tree_recalc(snode, area); } } else if (ED_node_is_geometry(snode)) { /* Rather strict check: only redraw when the reference matches the current editor's ID. */ if (wmn->data == ND_MODIFIER) { if (wmn->reference == snode->id || snode->id == nullptr) { - ED_area_tag_refresh(area); + node_area_tag_tree_recalc(snode, area); } } } break; case NC_SPACE: if (wmn->data == ND_SPACE_NODE) { - ED_area_tag_refresh(area); + node_area_tag_tree_recalc(snode, area); } else if (wmn->data == ND_SPACE_NODE_VIEW) { ED_area_tag_redraw(area); @@ -420,7 +450,7 @@ static void node_area_listener(const wmSpaceTypeListenerParams *params) break; case NC_NODE: if (wmn->action == NA_EDITED) { - ED_area_tag_refresh(area); + node_area_tag_tree_recalc(snode, area); } else if (wmn->action == NA_SELECTED) { ED_area_tag_redraw(area); @@ -429,14 +459,14 @@ static void node_area_listener(const wmSpaceTypeListenerParams *params) case NC_SCREEN: switch (wmn->data) { case ND_ANIMPLAY: - ED_area_tag_refresh(area); + node_area_tag_tree_recalc(snode, area); break; } break; case NC_MASK: if (wmn->action == NA_EDITED) { if (snode->nodetree && snode->nodetree->type == NTREE_COMPOSIT) { - ED_area_tag_refresh(area); + node_area_tag_tree_recalc(snode, area); } } break; @@ -447,7 +477,7 @@ static void node_area_listener(const wmSpaceTypeListenerParams *params) /* Without this check drawing on an image could become very slow when the compositor is * open. */ if (any_node_uses_id(snode->nodetree, (ID *)wmn->reference)) { - ED_area_tag_refresh(area); + node_area_tag_tree_recalc(snode, area); } } } @@ -457,7 +487,7 @@ static void node_area_listener(const wmSpaceTypeListenerParams *params) if (wmn->action == NA_EDITED) { if (ED_node_is_compositor(snode)) { if (any_node_uses_id(snode->nodetree, (ID *)wmn->reference)) { - ED_area_tag_refresh(area); + node_area_tag_tree_recalc(snode, area); } } } @@ -465,12 +495,12 @@ static void node_area_listener(const wmSpaceTypeListenerParams *params) case NC_LINESTYLE: if (ED_node_is_shader(snode) && shader_type == SNODE_SHADER_LINESTYLE) { - ED_area_tag_refresh(area); + node_area_tag_tree_recalc(snode, area); } break; case NC_WM: if (wmn->data == ND_UNDO) { - ED_area_tag_refresh(area); + node_area_tag_tree_recalc(snode, area); } break; case NC_GPENCIL: @@ -493,11 +523,13 @@ static void node_area_refresh(const struct bContext *C, ScrArea *area) Scene *scene = (Scene *)snode->id; if (scene->use_nodes) { /* recalc is set on 3d view changes for auto compo */ - if (snode->runtime->recalc) { - snode->runtime->recalc = false; + if (snode->runtime->recalc_auto_compositing) { + snode->runtime->recalc_auto_compositing = false; + snode->runtime->recalc_regular_compositing = false; node_render_changed_exec((struct bContext *)C, nullptr); } - else { + else if (snode->runtime->recalc_regular_compositing) { + snode->runtime->recalc_regular_compositing = false; ED_node_composite_job(C, snode->nodetree, scene); } } @@ -640,7 +672,7 @@ static void node_group_drop_copy(bContext *UNUSED(C), wmDrag *drag, wmDropBox *d { ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, 0); - RNA_string_set(drop->ptr, "name", id->name + 2); + RNA_int_set(drop->ptr, "session_uuid", (int)id->session_uuid); } static void node_id_drop_copy(bContext *UNUSED(C), wmDrag *drag, wmDropBox *drop) @@ -655,7 +687,7 @@ static void node_id_path_drop_copy(bContext *UNUSED(C), wmDrag *drag, wmDropBox ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, 0); if (id) { - RNA_string_set(drop->ptr, "name", id->name + 2); + RNA_int_set(drop->ptr, "session_uuid", (int)id->session_uuid); RNA_struct_property_unset(drop->ptr, "filepath"); } else if (drag->path[0]) { @@ -794,8 +826,10 @@ static void node_region_listener(const wmRegionListenerParams *params) } // namespace blender::ed::space_node /* Outside of blender namespace to avoid Python documentation build error with `ctypes`. */ +extern "C" { const char *node_context_dir[] = { "selected_nodes", "active_node", "light", "material", "world", nullptr}; +}; namespace blender::ed::space_node { diff --git a/source/blender/editors/space_outliner/CMakeLists.txt b/source/blender/editors/space_outliner/CMakeLists.txt index 59f6bd85d59..97d2957eed2 100644 --- a/source/blender/editors/space_outliner/CMakeLists.txt +++ b/source/blender/editors/space_outliner/CMakeLists.txt @@ -27,6 +27,7 @@ set(SRC outliner_draw.cc outliner_edit.cc outliner_ops.cc + outliner_query.cc outliner_select.cc outliner_sync.cc outliner_tools.cc @@ -57,6 +58,7 @@ set(SRC tree/tree_element_scene_objects.cc tree/tree_element_seq.cc tree/tree_element_view_layer.cc + tree/tree_iterator.cc outliner_intern.hh tree/common.hh @@ -75,6 +77,7 @@ set(SRC tree/tree_element_scene_objects.hh tree/tree_element_seq.hh tree/tree_element_view_layer.hh + tree/tree_iterator.hh ) set(LIB diff --git a/source/blender/editors/space_outliner/outliner_context.cc b/source/blender/editors/space_outliner/outliner_context.cc index d07b6641836..1a804cb58b8 100644 --- a/source/blender/editors/space_outliner/outliner_context.cc +++ b/source/blender/editors/space_outliner/outliner_context.cc @@ -12,23 +12,25 @@ #include "DNA_space_types.h" #include "outliner_intern.hh" +#include "tree/tree_iterator.hh" -static void outliner_context_selected_ids_recursive(const ListBase *subtree, +using namespace blender::ed::outliner; + +static void outliner_context_selected_ids_recursive(const SpaceOutliner &space_outliner, bContextDataResult *result) { - LISTBASE_FOREACH (const TreeElement *, te, subtree) { + tree_iterator::all(space_outliner, [&](const TreeElement *te) { const TreeStoreElem *tse = TREESTORE(te); if ((tse->flag & TSE_SELECTED) && (ELEM(tse->type, TSE_SOME_ID, TSE_LAYER_COLLECTION))) { CTX_data_id_list_add(result, tse->id); } - outliner_context_selected_ids_recursive(&te->subtree, result); - } + }); } static void outliner_context_selected_ids(const SpaceOutliner *space_outliner, bContextDataResult *result) { - outliner_context_selected_ids_recursive(&space_outliner->tree, result); + outliner_context_selected_ids_recursive(*space_outliner, result); CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); } diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.cc b/source/blender/editors/space_outliner/outliner_dragdrop.cc index 88640210ea3..e20958c1b1e 100644 --- a/source/blender/editors/space_outliner/outliner_dragdrop.cc +++ b/source/blender/editors/space_outliner/outliner_dragdrop.cc @@ -170,7 +170,8 @@ static TreeElement *outliner_drop_insert_find(bContext *C, *r_insert_type = TE_INSERT_BEFORE; return first; } - BLI_assert(0); + + BLI_assert_unreachable(); return nullptr; } @@ -315,7 +316,7 @@ static bool parent_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); - bool changed = outliner_flag_set(&space_outliner->tree, TSE_DRAG_ANY, false); + bool changed = outliner_flag_set(*space_outliner, TSE_DRAG_ANY, false); if (changed) { ED_region_tag_redraw_no_rebuild(CTX_wm_region(C)); } @@ -846,8 +847,7 @@ static bool datastack_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); ARegion *region = CTX_wm_region(C); - bool changed = outliner_flag_set( - &space_outliner->tree, TSE_HIGHLIGHTED_ANY | TSE_DRAG_ANY, false); + bool changed = outliner_flag_set(*space_outliner, TSE_HIGHLIGHTED_ANY | TSE_DRAG_ANY, false); StackDropData *drop_data = reinterpret_cast<StackDropData *>(drag->poin); if (!drop_data) { @@ -1194,8 +1194,7 @@ static bool collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event { SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); ARegion *region = CTX_wm_region(C); - bool changed = outliner_flag_set( - &space_outliner->tree, TSE_HIGHLIGHTED_ANY | TSE_DRAG_ANY, false); + bool changed = outliner_flag_set(*space_outliner, TSE_HIGHLIGHTED_ANY | TSE_DRAG_ANY, false); CollectionDrop data; if (((event->modifier & KM_SHIFT) == 0) && @@ -1460,7 +1459,7 @@ static int outliner_item_drag_drop_invoke(bContext *C, /* Only drag element under mouse if it was not selected before. */ if ((tselem->flag & TSE_SELECTED) == 0) { - outliner_flag_set(&space_outliner->tree, TSE_SELECTED, 0); + outliner_flag_set(*space_outliner, TSE_SELECTED, 0); tselem->flag |= TSE_SELECTED; } diff --git a/source/blender/editors/space_outliner/outliner_draw.cc b/source/blender/editors/space_outliner/outliner_draw.cc index d898be4eb2c..753de83a10d 100644 --- a/source/blender/editors/space_outliner/outliner_draw.cc +++ b/source/blender/editors/space_outliner/outliner_draw.cc @@ -38,6 +38,7 @@ #include "BKE_library.h" #include "BKE_main.h" #include "BKE_modifier.h" +#include "BKE_node.h" #include "BKE_object.h" #include "BKE_particle.h" #include "BKE_report.h" @@ -70,7 +71,9 @@ #include "tree/tree_element_id.hh" #include "tree/tree_element_overrides.hh" #include "tree/tree_element_rna.hh" +#include "tree/tree_iterator.hh" +using namespace blender; using namespace blender::ed::outliner; /* -------------------------------------------------------------------- */ @@ -1713,72 +1716,70 @@ static void outliner_draw_restrictbuts(uiBlock *block, } static void outliner_draw_userbuts(uiBlock *block, - ARegion *region, - SpaceOutliner *space_outliner, - ListBase *lb) + const ARegion *region, + const SpaceOutliner *space_outliner) { + tree_iterator::all_open(*space_outliner, [&](const TreeElement *te) { + if (!outliner_is_element_in_view(te, ®ion->v2d)) { + return; + } - LISTBASE_FOREACH (TreeElement *, te, lb) { - TreeStoreElem *tselem = TREESTORE(te); - if (outliner_is_element_in_view(te, ®ion->v2d)) { - if (tselem->type == TSE_SOME_ID) { - uiBut *bt; - ID *id = tselem->id; - const char *tip = nullptr; - char buf[16] = ""; - int but_flag = UI_BUT_DRAG_LOCK; + const TreeStoreElem *tselem = TREESTORE(te); + if (tselem->type != TSE_SOME_ID) { + return; + } - if (ID_IS_LINKED(id)) { - but_flag |= UI_BUT_DISABLED; - } + uiBut *bt; + ID *id = tselem->id; + const char *tip = nullptr; + char buf[16] = ""; + int but_flag = UI_BUT_DRAG_LOCK; - BLI_str_format_int_grouped(buf, id->us); - bt = uiDefBut(block, - UI_BTYPE_BUT, - 1, - buf, - (int)(region->v2d.cur.xmax - OL_TOG_USER_BUTS_USERS), - te->ys, - UI_UNIT_X, - UI_UNIT_Y, - nullptr, - 0.0, - 0.0, - 0, - 0, - TIP_("Number of users of this data-block")); - UI_but_flag_enable(bt, but_flag); - - if (id->flag & LIB_FAKEUSER) { - tip = TIP_("Data-block will be retained using a fake user"); - } - else { - tip = TIP_("Data-block has no users and will be deleted"); - } - bt = uiDefIconButBitS(block, - UI_BTYPE_ICON_TOGGLE, - LIB_FAKEUSER, - 1, - ICON_FAKE_USER_OFF, - (int)(region->v2d.cur.xmax - OL_TOG_USER_BUTS_STATUS), - te->ys, - UI_UNIT_X, - UI_UNIT_Y, - &id->flag, - 0, - 0, - 0, - 0, - tip); - UI_but_func_set(bt, restrictbutton_id_user_toggle, id, nullptr); - UI_but_flag_enable(bt, but_flag); - } + if (ID_IS_LINKED(id)) { + but_flag |= UI_BUT_DISABLED; } - if (TSELEM_OPEN(tselem, space_outliner)) { - outliner_draw_userbuts(block, region, space_outliner, &te->subtree); + BLI_str_format_int_grouped(buf, id->us); + bt = uiDefBut(block, + UI_BTYPE_BUT, + 1, + buf, + (int)(region->v2d.cur.xmax - OL_TOG_USER_BUTS_USERS), + te->ys, + UI_UNIT_X, + UI_UNIT_Y, + nullptr, + 0.0, + 0.0, + 0, + 0, + TIP_("Number of users of this data-block")); + UI_but_flag_enable(bt, but_flag); + + if (id->flag & LIB_FAKEUSER) { + tip = TIP_("Data-block will be retained using a fake user"); } - } + else { + tip = TIP_("Data-block has no users and will be deleted"); + } + bt = uiDefIconButBitS(block, + UI_BTYPE_ICON_TOGGLE, + LIB_FAKEUSER, + 1, + ICON_FAKE_USER_OFF, + (int)(region->v2d.cur.xmax - OL_TOG_USER_BUTS_STATUS), + te->ys, + UI_UNIT_X, + UI_UNIT_Y, + &id->flag, + 0, + 0, + 0, + 0, + tip); + UI_but_func_set(bt, restrictbutton_id_user_toggle, id, nullptr); + UI_but_flag_enable(bt, but_flag); + }); } static void outliner_draw_overrides_rna_buts(uiBlock *block, @@ -1808,6 +1809,25 @@ static void outliner_draw_overrides_rna_buts(uiBlock *block, TreeElementOverridesProperty &override_elem = *tree_element_cast<TreeElementOverridesProperty>( te); + if (!override_elem.is_rna_path_valid) { + uiBut *but = uiDefBut(block, + UI_BTYPE_LABEL, + 0, + override_elem.rna_path.c_str(), + x + pad_x, + te->ys + pad_y, + item_max_width, + item_height, + nullptr, + 0.0f, + 0.0f, + 0.0f, + 0.0f, + ""); + UI_but_flag_enable(but, UI_BUT_REDALERT); + continue; + } + PointerRNA *ptr = &override_elem.override_rna_ptr; PropertyRNA *prop = &override_elem.override_rna_prop; const PropertyType prop_type = RNA_property_type(prop); @@ -1900,90 +1920,6 @@ static void outliner_draw_overrides_restrictbuts(Main *bmain, } } -static bool outliner_draw_overrides_warning_buts(uiBlock *block, - ARegion *region, - SpaceOutliner *space_outliner, - ListBase *lb, - const bool is_open) -{ - bool any_item_has_warnings = false; - - LISTBASE_FOREACH (TreeElement *, te, lb) { - bool item_has_warnings = false; - const bool do_draw = outliner_is_element_in_view(te, ®ion->v2d); - int but_flag = UI_BUT_DRAG_LOCK; - const char *tip = nullptr; - - TreeStoreElem *tselem = TREESTORE(te); - switch (tselem->type) { - case TSE_LIBRARY_OVERRIDE_BASE: { - ID *id = tselem->id; - - if (id->flag & LIB_LIB_OVERRIDE_RESYNC_LEFTOVER) { - item_has_warnings = true; - if (do_draw) { - tip = TIP_( - "This override data-block is not needed anymore, but was detected as user-edited"); - } - } - else if (ID_IS_OVERRIDE_LIBRARY_REAL(id) && ID_REAL_USERS(id) == 0) { - item_has_warnings = true; - if (do_draw) { - tip = TIP_("This override data-block is unused"); - } - } - break; - } - case TSE_LIBRARY_OVERRIDE: { - const bool is_rna_path_valid = (bool)(POINTER_AS_UINT(te->directdata)); - if (!is_rna_path_valid) { - item_has_warnings = true; - if (do_draw) { - tip = TIP_( - "This override property does not exist in current data, it will be removed on " - "next .blend file save"); - } - } - break; - } - default: - break; - } - - const bool any_child_has_warnings = outliner_draw_overrides_warning_buts( - block, - region, - space_outliner, - &te->subtree, - is_open && TSELEM_OPEN(tselem, space_outliner)); - - if (do_draw && - (item_has_warnings || (any_child_has_warnings && !TSELEM_OPEN(tselem, space_outliner)))) { - if (tip == nullptr) { - tip = TIP_("Some sub-items require attention"); - } - uiBut *bt = uiDefIconBut(block, - UI_BTYPE_BUT, - 1, - ICON_ERROR, - (int)(region->v2d.cur.xmax - OL_TOG_USER_BUTS_STATUS), - te->ys, - UI_UNIT_X, - UI_UNIT_Y, - nullptr, - 0.0, - 0.0, - 0.0, - 0.0, - tip); - UI_but_flag_enable(bt, but_flag); - } - any_item_has_warnings = any_item_has_warnings || item_has_warnings || any_child_has_warnings; - } - - return any_item_has_warnings; -} - static void outliner_draw_separator(ARegion *region, const int x) { View2D *v2d = ®ion->v2d; @@ -2004,81 +1940,82 @@ static void outliner_draw_separator(ARegion *region, const int x) immUnbindProgram(); } -static void outliner_draw_rnabuts( - uiBlock *block, ARegion *region, SpaceOutliner *space_outliner, int sizex, ListBase *lb) +static void outliner_draw_rnabuts(uiBlock *block, + ARegion *region, + SpaceOutliner *space_outliner, + int sizex) { PointerRNA ptr; PropertyRNA *prop; - LISTBASE_FOREACH (TreeElement *, te, lb) { + tree_iterator::all_open(*space_outliner, [&](TreeElement *te) { TreeStoreElem *tselem = TREESTORE(te); - if (outliner_is_element_in_view(te, ®ion->v2d)) { - if (TreeElementRNAProperty *te_rna_prop = tree_element_cast<TreeElementRNAProperty>(te)) { - ptr = te_rna_prop->getPointerRNA(); - prop = te_rna_prop->getPropertyRNA(); - - if (!TSELEM_OPEN(tselem, space_outliner)) { - if (RNA_property_type(prop) == PROP_POINTER) { - uiBut *but = uiDefAutoButR(block, - &ptr, - prop, - -1, - "", - ICON_NONE, - sizex, - te->ys, - OL_RNA_COL_SIZEX, - UI_UNIT_Y - 1); - UI_but_flag_enable(but, UI_BUT_DISABLED); - } - else if (RNA_property_type(prop) == PROP_ENUM) { - uiDefAutoButR(block, - &ptr, - prop, - -1, - nullptr, - ICON_NONE, - sizex, - te->ys, - OL_RNA_COL_SIZEX, - UI_UNIT_Y - 1); - } - else { - uiDefAutoButR(block, - &ptr, - prop, - -1, - "", - ICON_NONE, - sizex, - te->ys, - OL_RNA_COL_SIZEX, - UI_UNIT_Y - 1); - } - } - } - else if (TreeElementRNAArrayElement *te_rna_array_elem = - tree_element_cast<TreeElementRNAArrayElement>(te)) { - ptr = te_rna_array_elem->getPointerRNA(); - prop = te_rna_array_elem->getPropertyRNA(); - - uiDefAutoButR(block, - &ptr, - prop, - te->index, - "", - ICON_NONE, - sizex, - te->ys, - OL_RNA_COL_SIZEX, - UI_UNIT_Y - 1); - } - } - if (TSELEM_OPEN(tselem, space_outliner)) { - outliner_draw_rnabuts(block, region, space_outliner, sizex, &te->subtree); - } - } + if (!outliner_is_element_in_view(te, ®ion->v2d)) { + return; + } + + if (TreeElementRNAProperty *te_rna_prop = tree_element_cast<TreeElementRNAProperty>(te)) { + ptr = te_rna_prop->getPointerRNA(); + prop = te_rna_prop->getPropertyRNA(); + + if (!TSELEM_OPEN(tselem, space_outliner)) { + if (RNA_property_type(prop) == PROP_POINTER) { + uiBut *but = uiDefAutoButR(block, + &ptr, + prop, + -1, + "", + ICON_NONE, + sizex, + te->ys, + OL_RNA_COL_SIZEX, + UI_UNIT_Y - 1); + UI_but_flag_enable(but, UI_BUT_DISABLED); + } + else if (RNA_property_type(prop) == PROP_ENUM) { + uiDefAutoButR(block, + &ptr, + prop, + -1, + nullptr, + ICON_NONE, + sizex, + te->ys, + OL_RNA_COL_SIZEX, + UI_UNIT_Y - 1); + } + else { + uiDefAutoButR(block, + &ptr, + prop, + -1, + "", + ICON_NONE, + sizex, + te->ys, + OL_RNA_COL_SIZEX, + UI_UNIT_Y - 1); + } + } + } + else if (TreeElementRNAArrayElement *te_rna_array_elem = + tree_element_cast<TreeElementRNAArrayElement>(te)) { + ptr = te_rna_array_elem->getPointerRNA(); + prop = te_rna_array_elem->getPropertyRNA(); + + uiDefAutoButR(block, + &ptr, + prop, + te->index, + "", + ICON_NONE, + sizex, + te->ys, + OL_RNA_COL_SIZEX, + UI_UNIT_Y - 1); + } + }); } static void outliner_buttons(const bContext *C, @@ -2164,9 +2101,9 @@ static void outliner_mode_toggle_fn(bContext *C, void *tselem_poin, void *UNUSED static void outliner_draw_mode_column_toggle(uiBlock *block, TreeViewContext *tvc, TreeElement *te, - TreeStoreElem *tselem, const bool lock_object_modes) { + TreeStoreElem *tselem = TREESTORE(te); if ((tselem->type != TSE_SOME_ID) || (te->idcode != ID_OB)) { return; } @@ -2237,59 +2174,63 @@ static void outliner_draw_mode_column_toggle(uiBlock *block, } } -static void outliner_draw_mode_column(const bContext *C, - uiBlock *block, +static void outliner_draw_mode_column(uiBlock *block, TreeViewContext *tvc, - SpaceOutliner *space_outliner, - ListBase *tree) + SpaceOutliner *space_outliner) { - TreeStoreElem *tselem; const bool lock_object_modes = tvc->scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK; - LISTBASE_FOREACH (TreeElement *, te, tree) { - tselem = TREESTORE(te); - + tree_iterator::all_open(*space_outliner, [&](TreeElement *te) { if (tvc->obact && tvc->obact->mode != OB_MODE_OBJECT) { - outliner_draw_mode_column_toggle(block, tvc, te, tselem, lock_object_modes); + outliner_draw_mode_column_toggle(block, tvc, te, lock_object_modes); } + }); +} - if (TSELEM_OPEN(tselem, space_outliner)) { - outliner_draw_mode_column(C, block, tvc, space_outliner, &te->subtree); +static StringRefNull outliner_draw_get_warning_tree_element_subtree(const TreeElement *parent_te) +{ + LISTBASE_FOREACH (const TreeElement *, sub_te, &parent_te->subtree) { + const AbstractTreeElement *abstract_te = tree_element_cast<AbstractTreeElement>(sub_te); + StringRefNull warning_msg = abstract_te ? abstract_te->getWarning() : ""; + + if (!warning_msg.is_empty()) { + return warning_msg; + } + + warning_msg = outliner_draw_get_warning_tree_element_subtree(sub_te); + if (!warning_msg.is_empty()) { + return warning_msg; } } + + return ""; } -/* Returns `true` if some warning was drawn for that element or one of its sub-elements (if it is - * not open). */ -static bool outliner_draw_warning_tree_element(uiBlock *block, - SpaceOutliner *space_outliner, - TreeElement *te, - TreeStoreElem *tselem, - const bool use_mode_column, - const int te_ys) +static StringRefNull outliner_draw_get_warning_tree_element(const SpaceOutliner &space_outliner, + const TreeElement *te) { - if ((te->flag & TE_HAS_WARNING) == 0) { - /* If given element has no warning, recursively try to display the first sub-elements' warning. - */ - if (!TSELEM_OPEN(tselem, space_outliner)) { - LISTBASE_FOREACH (TreeElement *, sub_te, &te->subtree) { - TreeStoreElem *sub_tselem = TREESTORE(sub_te); + const AbstractTreeElement *abstract_te = tree_element_cast<AbstractTreeElement>(te); + const StringRefNull warning_msg = abstract_te ? abstract_te->getWarning() : ""; - if (outliner_draw_warning_tree_element( - block, space_outliner, sub_te, sub_tselem, use_mode_column, te_ys)) { - return true; - } - } - } - return false; + if (!warning_msg.is_empty()) { + return warning_msg; } - int icon = ICON_NONE; - const char *tip = ""; - const bool has_warning = tree_element_warnings_get(te, &icon, &tip); - BLI_assert(has_warning); - UNUSED_VARS_NDEBUG(has_warning); + /* If given element has no warning, recursively try to display the first sub-element's warning. + */ + if (!TSELEM_OPEN(te->store_elem, &space_outliner)) { + return outliner_draw_get_warning_tree_element_subtree(te); + } + return ""; +} + +static void outliner_draw_warning_tree_element(uiBlock *block, + const SpaceOutliner *space_outliner, + StringRefNull warning_msg, + const bool use_mode_column, + const int te_ys) +{ /* Move the warnings a unit left in view layer mode. */ const short mode_column_offset = (use_mode_column && (space_outliner->outlinevis == SO_SCENES)) ? UI_UNIT_X : @@ -2299,7 +2240,7 @@ static bool outliner_draw_warning_tree_element(uiBlock *block, uiBut *but = uiDefIconBut(block, UI_BTYPE_ICON_TOGGLE, 0, - icon, + ICON_ERROR, mode_column_offset, te_ys, UI_UNIT_X, @@ -2309,28 +2250,25 @@ static bool outliner_draw_warning_tree_element(uiBlock *block, 0.0, 0.0, 0.0, - tip); + warning_msg.c_str()); /* No need for undo here, this is a pure info widget. */ UI_but_flag_disable(but, UI_BUT_UNDO); - - return true; } -static void outliner_draw_warning_column(const bContext *C, - uiBlock *block, - SpaceOutliner *space_outliner, - const bool use_mode_column, - ListBase *tree) +static void outliner_draw_warning_column(uiBlock *block, + const SpaceOutliner *space_outliner, + const bool use_mode_column) { - LISTBASE_FOREACH (TreeElement *, te, tree) { - TreeStoreElem *tselem = TREESTORE(te); + tree_iterator::all_open(*space_outliner, [&](const TreeElement *te) { + /* Get warning for this element, or if there is none and the element is collapsed, the first + * warning in the collapsed sub-tree. */ + StringRefNull warning_msg = outliner_draw_get_warning_tree_element(*space_outliner, te); - outliner_draw_warning_tree_element(block, space_outliner, te, tselem, use_mode_column, te->ys); - - if (TSELEM_OPEN(tselem, space_outliner)) { - outliner_draw_warning_column(C, block, space_outliner, use_mode_column, &te->subtree); + if (!warning_msg.is_empty()) { + outliner_draw_warning_tree_element( + block, space_outliner, warning_msg, use_mode_column, te->ys); } - } + }); } /** \} */ @@ -2504,6 +2442,11 @@ static BIFIconID tree_element_get_icon_from_id(const ID *id) return ICON_WORKSPACE; case ID_MSK: return ICON_MOD_MASK; + case ID_NT: { + const bNodeTree *ntree = (bNodeTree *)id; + const bNodeTreeType *ntreetype = ntree->typeinfo; + return (BIFIconID)ntreetype->ui_icon; + } case ID_MC: return ICON_SEQUENCE; case ID_PC: @@ -3206,18 +3149,16 @@ static void outliner_draw_iconrow(bContext *C, } /* closed tree element */ -static void outliner_set_coord_tree_element(TreeElement *te, int startx, int starty) +static void outliner_set_subtree_coords(const TreeElement *te) { - /* closed items may be displayed in row of parent, don't change their coordinate! */ - if ((te->flag & TE_ICONROW) == 0 && (te->flag & TE_ICONROW_MERGED) == 0) { - te->xs = 0; - te->ys = 0; - te->xend = 0; - } - - LISTBASE_FOREACH (TreeElement *, ten, &te->subtree) { - outliner_set_coord_tree_element(ten, startx + UI_UNIT_X, starty); - } + tree_iterator::all(te->subtree, [&](TreeElement *te) { + /* closed items may be displayed in row of parent, don't change their coordinate! */ + if ((te->flag & TE_ICONROW) == 0 && (te->flag & TE_ICONROW_MERGED) == 0) { + te->xs = 0; + te->ys = 0; + te->xend = 0; + } + }); } static bool element_should_draw_faded(const TreeViewContext *tvc, @@ -3469,10 +3410,7 @@ static void outliner_draw_tree_element(bContext *C, } } else { - LISTBASE_FOREACH (TreeElement *, ten, &te->subtree) { - outliner_set_coord_tree_element(ten, startx, *starty); - } - + outliner_set_subtree_coords(te); *starty -= UI_UNIT_Y; } } @@ -3628,22 +3566,21 @@ static void outliner_draw_struct_marks(ARegion *region, } } -static void outliner_draw_highlights_recursive(uint pos, - const ARegion *region, - const SpaceOutliner *space_outliner, - 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, - int *io_start_y) +static void outliner_draw_highlights(uint pos, + const ARegion *region, + const SpaceOutliner *space_outliner, + const float col_selection[4], + const float col_active[4], + const float col_highlight[4], + const float col_searchmatch[4], + int start_x, + int *io_start_y) { const bool is_searching = (SEARCHING_OUTLINER(space_outliner) || (space_outliner->outlinevis == SO_DATA_API && space_outliner->search_string[0] != 0)); - LISTBASE_FOREACH (TreeElement *, te, lb) { + tree_iterator::all_open(*space_outliner, [&](const TreeElement *te) { const TreeStoreElem *tselem = TREESTORE(te); const int start_y = *io_start_y; @@ -3699,19 +3636,7 @@ static void outliner_draw_highlights_recursive(uint pos, } *io_start_y -= UI_UNIT_Y; - if (TSELEM_OPEN(tselem, space_outliner)) { - outliner_draw_highlights_recursive(pos, - region, - space_outliner, - &te->subtree, - col_selection, - col_active, - col_highlight, - col_searchmatch, - start_x + UI_UNIT_X, - io_start_y); - } - } + }); } static void outliner_draw_highlights(ARegion *region, @@ -3733,16 +3658,15 @@ static void outliner_draw_highlights(ARegion *region, 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, - region, - space_outliner, - &space_outliner->tree, - col_selection, - col_active, - col_highlight, - col_searchmatch, - startx, - starty); + outliner_draw_highlights(pos, + region, + space_outliner, + col_selection, + col_active, + col_highlight, + col_searchmatch, + startx, + starty); immUnbindProgram(); GPU_blend(GPU_BLEND_NONE); } @@ -3935,13 +3859,8 @@ void draw_outliner(const bContext *C) UI_view2d_view_ortho(v2d); /* Only show mode column in View Layers and Scenes view. */ - const bool use_mode_column = (space_outliner->flag & SO_MODE_COLUMN) && - (ELEM(space_outliner->outlinevis, SO_VIEW_LAYER, SO_SCENES)); - - const bool use_warning_column = ELEM(space_outliner->outlinevis, - SO_LIBRARIES, - SO_OVERRIDES_LIBRARY) && - space_outliner->runtime->tree_display->hasWarnings(); + const bool use_mode_column = outliner_shows_mode_column(*space_outliner); + const bool use_warning_column = outliner_has_element_warnings(*space_outliner); /* Draw outliner stuff (background, hierarchy lines and names). */ const float right_column_width = outliner_right_columns_width(space_outliner); @@ -3971,18 +3890,14 @@ void draw_outliner(const bContext *C) outliner_draw_separator(region, buttons_start_x + OL_RNA_COL_SIZEX); UI_block_emboss_set(block, UI_EMBOSS); - outliner_draw_rnabuts(block, region, space_outliner, buttons_start_x, &space_outliner->tree); + outliner_draw_rnabuts(block, region, space_outliner, buttons_start_x); UI_block_emboss_set(block, UI_EMBOSS_NONE_OR_STATUS); } else if (space_outliner->outlinevis == SO_ID_ORPHANS) { /* draw user toggle columns */ - outliner_draw_userbuts(block, region, space_outliner, &space_outliner->tree); + outliner_draw_userbuts(block, region, space_outliner); } else if (space_outliner->outlinevis == SO_OVERRIDES_LIBRARY) { - /* Draw overrides status columns. */ - outliner_draw_overrides_warning_buts( - block, region, space_outliner, &space_outliner->tree, true); - const int x = region->v2d.cur.xmax - right_column_width; outliner_draw_separator(region, x); if (space_outliner->lib_override_view_mode == SO_LIB_OVERRIDE_VIEW_PROPERTIES) { @@ -4011,12 +3926,12 @@ void draw_outliner(const bContext *C) /* Draw mode icons */ if (use_mode_column) { - outliner_draw_mode_column(C, block, &tvc, space_outliner, &space_outliner->tree); + outliner_draw_mode_column(block, &tvc, space_outliner); } /* Draw warning icons */ if (use_warning_column) { - outliner_draw_warning_column(C, block, space_outliner, use_mode_column, &space_outliner->tree); + outliner_draw_warning_column(block, space_outliner, use_mode_column); } UI_block_emboss_set(block, UI_EMBOSS); diff --git a/source/blender/editors/space_outliner/outliner_edit.cc b/source/blender/editors/space_outliner/outliner_edit.cc index d6c5901b546..c4a9398a5f7 100644 --- a/source/blender/editors/space_outliner/outliner_edit.cc +++ b/source/blender/editors/space_outliner/outliner_edit.cc @@ -60,6 +60,7 @@ #include "outliner_intern.hh" #include "tree/tree_element_rna.hh" +#include "tree/tree_iterator.hh" using namespace blender::ed::outliner; @@ -107,7 +108,7 @@ static int outliner_highlight_update(bContext *C, wmOperator *UNUSED(op), const if (!hovered_te || !is_over_icon || !(hovered_te->store_elem->flag & TSE_HIGHLIGHTED) || !(icon_te->store_elem->flag & TSE_HIGHLIGHTED_ICON)) { /* Clear highlights when nothing is hovered or when a new item is hovered. */ - changed = outliner_flag_set(&space_outliner->tree, TSE_HIGHLIGHTED_ANY | TSE_DRAG_ANY, false); + changed = outliner_flag_set(*space_outliner, TSE_HIGHLIGHTED_ANY | TSE_DRAG_ANY, false); if (hovered_te) { hovered_te->store_elem->flag |= TSE_HIGHLIGHTED; changed = true; @@ -167,7 +168,7 @@ void outliner_item_openclose(SpaceOutliner *space_outliner, } if (toggle_all) { - outliner_flag_set(&te->subtree, TSE_CLOSED, !open); + outliner_flag_set(te->subtree, TSE_CLOSED, !open); } } @@ -334,8 +335,16 @@ static void do_item_rename(ARegion *region, add_textbut = true; } } - else if (te->idcode == ID_LI && ((Library *)tselem->id)->parent) { - BKE_report(reports, RPT_WARNING, "Cannot edit the path of an indirectly linked library"); + else if (te->idcode == ID_LI) { + if (reinterpret_cast<Library *>(tselem->id)->parent) { + BKE_report(reports, RPT_WARNING, "Cannot edit the path of an indirectly linked library"); + } + else { + BKE_report( + reports, + RPT_WARNING, + "Library path is not editable from here anymore, please use Relocate operation instead"); + } } else { add_textbut = true; @@ -1069,11 +1078,16 @@ int outliner_flag_is_any_test(ListBase *lb, short flag, const int curlevel) return 0; } -bool outliner_flag_set(ListBase *lb, short flag, short set) +bool outliner_flag_set(const SpaceOutliner &space_outliner, const short flag, const short set) +{ + return outliner_flag_set(space_outliner.tree, flag, set); +} + +bool outliner_flag_set(const ListBase &lb, const short flag, const short set) { bool changed = false; - LISTBASE_FOREACH (TreeElement *, te, lb) { + tree_iterator::all(lb, [&](TreeElement *te) { TreeStoreElem *tselem = TREESTORE(te); bool has_flag = (tselem->flag & flag); if (set == 0) { @@ -1086,21 +1100,24 @@ bool outliner_flag_set(ListBase *lb, short flag, short set) tselem->flag |= flag; changed = true; } - changed |= outliner_flag_set(&te->subtree, flag, set); - } + }); return changed; } -bool outliner_flag_flip(ListBase *lb, short flag) +bool outliner_flag_flip(const SpaceOutliner &space_outliner, const short flag) +{ + return outliner_flag_flip(space_outliner.tree, flag); +} + +bool outliner_flag_flip(const ListBase &lb, const short flag) { bool changed = false; - LISTBASE_FOREACH (TreeElement *, te, lb) { + tree_iterator::all(lb, [&](TreeElement *te) { TreeStoreElem *tselem = TREESTORE(te); tselem->flag ^= flag; - changed |= outliner_flag_flip(&te->subtree, flag); - } + }); return changed; } @@ -1117,10 +1134,10 @@ static int outliner_toggle_expanded_exec(bContext *C, wmOperator *UNUSED(op)) ARegion *region = CTX_wm_region(C); if (outliner_flag_is_any_test(&space_outliner->tree, TSE_CLOSED, 1)) { - outliner_flag_set(&space_outliner->tree, TSE_CLOSED, 0); + outliner_flag_set(*space_outliner, TSE_CLOSED, 0); } else { - outliner_flag_set(&space_outliner->tree, TSE_CLOSED, 1); + outliner_flag_set(*space_outliner, TSE_CLOSED, 1); } ED_region_tag_redraw(region); @@ -1161,13 +1178,13 @@ static int outliner_select_all_exec(bContext *C, wmOperator *op) switch (action) { case SEL_SELECT: - outliner_flag_set(&space_outliner->tree, TSE_SELECTED, 1); + outliner_flag_set(*space_outliner, TSE_SELECTED, 1); break; case SEL_DESELECT: - outliner_flag_set(&space_outliner->tree, TSE_SELECTED, 0); + outliner_flag_set(*space_outliner, TSE_SELECTED, 0); break; case SEL_INVERT: - outliner_flag_flip(&space_outliner->tree, TSE_SELECTED); + outliner_flag_flip(*space_outliner, TSE_SELECTED); break; } @@ -1203,32 +1220,16 @@ void OUTLINER_OT_select_all(wmOperatorType *ot) /** \name View Show Active (Outliner) Operator * \{ */ -static void outliner_set_coordinates_element_recursive(SpaceOutliner *space_outliner, - TreeElement *te, - int startx, - int *starty) -{ - TreeStoreElem *tselem = TREESTORE(te); - - /* store coord and continue, we need coordinates for elements outside view too */ - te->xs = (float)startx; - te->ys = (float)(*starty); - *starty -= UI_UNIT_Y; - - if (TSELEM_OPEN(tselem, space_outliner)) { - LISTBASE_FOREACH (TreeElement *, ten, &te->subtree) { - outliner_set_coordinates_element_recursive(space_outliner, ten, startx + UI_UNIT_X, starty); - } - } -} - -void outliner_set_coordinates(ARegion *region, SpaceOutliner *space_outliner) +void outliner_set_coordinates(const ARegion *region, const SpaceOutliner *space_outliner) { int starty = (int)(region->v2d.tot.ymax) - UI_UNIT_Y; - LISTBASE_FOREACH (TreeElement *, te, &space_outliner->tree) { - outliner_set_coordinates_element_recursive(space_outliner, te, 0, &starty); - } + tree_iterator::all_open(*space_outliner, [&](TreeElement *te) { + /* store coord and continue, we need coordinates for elements outside view too */ + te->xs = 0; + te->ys = (float)starty; + starty -= UI_UNIT_Y; + }); } /* return 1 when levels were opened */ @@ -1616,11 +1617,11 @@ static int subtree_has_objects(ListBase *lb) return 0; } -/* recursive helper function for Show Hierarchy operator */ -static void tree_element_show_hierarchy(Scene *scene, SpaceOutliner *space_outliner, ListBase *lb) +/* Helper function for Show Hierarchy operator */ +static void tree_element_show_hierarchy(Scene *scene, SpaceOutliner *space_outliner) { /* open all object elems, close others */ - LISTBASE_FOREACH (TreeElement *, te, lb) { + tree_iterator::all_open(*space_outliner, [&](TreeElement *te) { TreeStoreElem *tselem = TREESTORE(te); if (ELEM(tselem->type, @@ -1648,11 +1649,7 @@ static void tree_element_show_hierarchy(Scene *scene, SpaceOutliner *space_outli else { tselem->flag |= TSE_CLOSED; } - - if (TSELEM_OPEN(tselem, space_outliner)) { - tree_element_show_hierarchy(scene, space_outliner, &te->subtree); - } - } + }); } /* show entire object level hierarchy */ @@ -1663,7 +1660,7 @@ static int outliner_show_hierarchy_exec(bContext *C, wmOperator *UNUSED(op)) Scene *scene = CTX_data_scene(C); /* recursively open/close levels */ - tree_element_show_hierarchy(scene, space_outliner, &space_outliner->tree); + tree_element_show_hierarchy(scene, space_outliner); ED_region_tag_redraw(region); @@ -1865,79 +1862,75 @@ enum { DRIVERS_EDITMODE_REMOVE, } /*eDrivers_EditModes*/; -/* Recursively iterate over tree, finding and working on selected items */ +/* Iterate over tree, finding and working on selected items */ static void do_outliner_drivers_editop(SpaceOutliner *space_outliner, - ListBase *tree, ReportList *reports, short mode) { - LISTBASE_FOREACH (TreeElement *, te, tree) { + tree_iterator::all_open(*space_outliner, [&](TreeElement *te) { TreeStoreElem *tselem = TREESTORE(te); /* if item is selected, perform operation */ - if (tselem->flag & TSE_SELECTED) { - ID *id = nullptr; - char *path = nullptr; - int array_index = 0; - short flag = 0; - short groupmode = KSP_GROUP_KSNAME; - - TreeElementRNACommon *te_rna = tree_element_cast<TreeElementRNACommon>(te); - PointerRNA ptr = te_rna ? te_rna->getPointerRNA() : PointerRNA_NULL; - PropertyRNA *prop = te_rna ? te_rna->getPropertyRNA() : nullptr; - - /* check if RNA-property described by this selected element is an animatable prop */ - if (prop && RNA_property_animateable(&ptr, prop)) { - /* get id + path + index info from the selected element */ - tree_element_to_path(te, tselem, &id, &path, &array_index, &flag, &groupmode); - } + if (!(tselem->flag & TSE_SELECTED)) { + return; + } - /* only if ID and path were set, should we perform any actions */ - if (id && path) { - short dflags = CREATEDRIVER_WITH_DEFAULT_DVAR; - int arraylen = 1; + ID *id = nullptr; + char *path = nullptr; + int array_index = 0; + short flag = 0; + short groupmode = KSP_GROUP_KSNAME; - /* array checks */ - if (flag & KSP_FLAG_WHOLE_ARRAY) { - /* entire array was selected, so add drivers for all */ - arraylen = RNA_property_array_length(&ptr, prop); - } - else { - arraylen = array_index; - } + TreeElementRNACommon *te_rna = tree_element_cast<TreeElementRNACommon>(te); + PointerRNA ptr = te_rna ? te_rna->getPointerRNA() : PointerRNA_NULL; + PropertyRNA *prop = te_rna ? te_rna->getPropertyRNA() : nullptr; - /* we should do at least one step */ - if (arraylen == array_index) { - arraylen++; - } + /* check if RNA-property described by this selected element is an animatable prop */ + if (prop && RNA_property_animateable(&ptr, prop)) { + /* get id + path + index info from the selected element */ + tree_element_to_path(te, tselem, &id, &path, &array_index, &flag, &groupmode); + } - /* for each array element we should affect, add driver */ - for (; array_index < arraylen; array_index++) { - /* action depends on mode */ - switch (mode) { - case DRIVERS_EDITMODE_ADD: { - /* add a new driver with the information obtained (only if valid) */ - ANIM_add_driver(reports, id, path, array_index, dflags, DRIVER_TYPE_PYTHON); - break; - } - case DRIVERS_EDITMODE_REMOVE: { - /* remove driver matching the information obtained (only if valid) */ - ANIM_remove_driver(reports, id, path, array_index, dflags); - break; - } + /* only if ID and path were set, should we perform any actions */ + if (id && path) { + short dflags = CREATEDRIVER_WITH_DEFAULT_DVAR; + int arraylen = 1; + + /* array checks */ + if (flag & KSP_FLAG_WHOLE_ARRAY) { + /* entire array was selected, so add drivers for all */ + arraylen = RNA_property_array_length(&ptr, prop); + } + else { + arraylen = array_index; + } + + /* we should do at least one step */ + if (arraylen == array_index) { + arraylen++; + } + + /* for each array element we should affect, add driver */ + for (; array_index < arraylen; array_index++) { + /* action depends on mode */ + switch (mode) { + case DRIVERS_EDITMODE_ADD: { + /* add a new driver with the information obtained (only if valid) */ + ANIM_add_driver(reports, id, path, array_index, dflags, DRIVER_TYPE_PYTHON); + break; + } + case DRIVERS_EDITMODE_REMOVE: { + /* remove driver matching the information obtained (only if valid) */ + ANIM_remove_driver(reports, id, path, array_index, dflags); + break; } } - - /* free path, since it had to be generated */ - MEM_freeN(path); } - } - /* go over sub-tree */ - if (TSELEM_OPEN(tselem, space_outliner)) { - do_outliner_drivers_editop(space_outliner, &te->subtree, reports, mode); + /* free path, since it had to be generated */ + MEM_freeN(path); } - } + }); } /** \} */ @@ -1956,8 +1949,7 @@ static int outliner_drivers_addsel_exec(bContext *C, wmOperator *op) } /* recursively go into tree, adding selected items */ - do_outliner_drivers_editop( - space_outliner, &space_outliner->tree, op->reports, DRIVERS_EDITMODE_ADD); + do_outliner_drivers_editop(space_outliner, op->reports, DRIVERS_EDITMODE_ADD); /* send notifiers */ WM_event_add_notifier(C, NC_ANIMATION | ND_FCURVES_ORDER, nullptr); /* XXX */ @@ -1996,8 +1988,7 @@ static int outliner_drivers_deletesel_exec(bContext *C, wmOperator *op) } /* recursively go into tree, adding selected items */ - do_outliner_drivers_editop( - space_outliner, &space_outliner->tree, op->reports, DRIVERS_EDITMODE_REMOVE); + do_outliner_drivers_editop(space_outliner, op->reports, DRIVERS_EDITMODE_REMOVE); /* send notifiers */ WM_event_add_notifier(C, ND_KEYS, nullptr); /* XXX */ @@ -2064,68 +2055,64 @@ static KeyingSet *verify_active_keyingset(Scene *scene, short add) return ks; } -/* Recursively iterate over tree, finding and working on selected items */ +/* Iterate over tree, finding and working on selected items */ static void do_outliner_keyingset_editop(SpaceOutliner *space_outliner, KeyingSet *ks, - ListBase *tree, - short mode) + const short mode) { - LISTBASE_FOREACH (TreeElement *, te, tree) { + tree_iterator::all_open(*space_outliner, [&](TreeElement *te) { TreeStoreElem *tselem = TREESTORE(te); /* if item is selected, perform operation */ - if (tselem->flag & TSE_SELECTED) { - ID *id = nullptr; - char *path = nullptr; - int array_index = 0; - short flag = 0; - short groupmode = KSP_GROUP_KSNAME; - - /* check if RNA-property described by this selected element is an animatable prop */ - const TreeElementRNACommon *te_rna = tree_element_cast<TreeElementRNACommon>(te); - PointerRNA ptr = te_rna->getPointerRNA(); - if (te_rna && te_rna->getPropertyRNA() && - RNA_property_animateable(&ptr, te_rna->getPropertyRNA())) { - /* get id + path + index info from the selected element */ - tree_element_to_path(te, tselem, &id, &path, &array_index, &flag, &groupmode); - } + if (!(tselem->flag & TSE_SELECTED)) { + return; + } - /* only if ID and path were set, should we perform any actions */ - if (id && path) { - /* action depends on mode */ - switch (mode) { - case KEYINGSET_EDITMODE_ADD: { - /* add a new path with the information obtained (only if valid) */ - /* TODO: what do we do with group name? - * for now, we don't supply one, and just let this use the KeyingSet name */ - BKE_keyingset_add_path(ks, id, nullptr, path, array_index, flag, groupmode); - ks->active_path = BLI_listbase_count(&ks->paths); - break; - } - case KEYINGSET_EDITMODE_REMOVE: { - /* find the relevant path, then remove it from the KeyingSet */ - KS_Path *ksp = BKE_keyingset_find_path(ks, id, nullptr, path, array_index, groupmode); + ID *id = nullptr; + char *path = nullptr; + int array_index = 0; + short flag = 0; + short groupmode = KSP_GROUP_KSNAME; + + /* check if RNA-property described by this selected element is an animatable prop */ + const TreeElementRNACommon *te_rna = tree_element_cast<TreeElementRNACommon>(te); + PointerRNA ptr = te_rna->getPointerRNA(); + if (te_rna && te_rna->getPropertyRNA() && + RNA_property_animateable(&ptr, te_rna->getPropertyRNA())) { + /* get id + path + index info from the selected element */ + tree_element_to_path(te, tselem, &id, &path, &array_index, &flag, &groupmode); + } - if (ksp) { - /* free path's data */ - BKE_keyingset_free_path(ks, ksp); + /* only if ID and path were set, should we perform any actions */ + if (id && path) { + /* action depends on mode */ + switch (mode) { + case KEYINGSET_EDITMODE_ADD: { + /* add a new path with the information obtained (only if valid) */ + /* TODO: what do we do with group name? + * for now, we don't supply one, and just let this use the KeyingSet name */ + BKE_keyingset_add_path(ks, id, nullptr, path, array_index, flag, groupmode); + ks->active_path = BLI_listbase_count(&ks->paths); + break; + } + case KEYINGSET_EDITMODE_REMOVE: { + /* find the relevant path, then remove it from the KeyingSet */ + KS_Path *ksp = BKE_keyingset_find_path(ks, id, nullptr, path, array_index, groupmode); - ks->active_path = 0; - } - break; + if (ksp) { + /* free path's data */ + BKE_keyingset_free_path(ks, ksp); + + ks->active_path = 0; } + break; } - - /* free path, since it had to be generated */ - MEM_freeN(path); } - } - /* go over sub-tree */ - if (TSELEM_OPEN(tselem, space_outliner)) { - do_outliner_keyingset_editop(space_outliner, ks, &te->subtree, mode); + /* free path, since it had to be generated */ + MEM_freeN(path); } - } + }); } /** \} */ @@ -2150,7 +2137,7 @@ static int outliner_keyingset_additems_exec(bContext *C, wmOperator *op) } /* recursively go into tree, adding selected items */ - do_outliner_keyingset_editop(space_outliner, ks, &space_outliner->tree, KEYINGSET_EDITMODE_ADD); + do_outliner_keyingset_editop(space_outliner, ks, KEYINGSET_EDITMODE_ADD); /* send notifiers */ WM_event_add_notifier(C, NC_SCENE | ND_KEYINGSET, nullptr); @@ -2191,8 +2178,7 @@ static int outliner_keyingset_removeitems_exec(bContext *C, wmOperator *UNUSED(o } /* recursively go into tree, adding selected items */ - do_outliner_keyingset_editop( - space_outliner, ks, &space_outliner->tree, KEYINGSET_EDITMODE_REMOVE); + do_outliner_keyingset_editop(space_outliner, ks, KEYINGSET_EDITMODE_REMOVE); /* send notifiers */ WM_event_add_notifier(C, NC_SCENE | ND_KEYINGSET, nullptr); diff --git a/source/blender/editors/space_outliner/outliner_intern.hh b/source/blender/editors/space_outliner/outliner_intern.hh index f3bcb7b0f1e..a0dcb49aa43 100644 --- a/source/blender/editors/space_outliner/outliner_intern.hh +++ b/source/blender/editors/space_outliner/outliner_intern.hh @@ -160,8 +160,6 @@ enum { /* 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), - /* This element has some warning to be displayed. */ - TE_HAS_WARNING = (1 << 8), }; /* button events */ @@ -410,8 +408,12 @@ int outliner_flag_is_any_test(ListBase *lb, short flag, int curlevel); * Set or unset \a flag for all outliner elements in \a lb and sub-trees. * \return if any flag was modified. */ -bool outliner_flag_set(ListBase *lb, short flag, short set); -bool outliner_flag_flip(ListBase *lb, short flag); +extern "C++" { +bool outliner_flag_set(const SpaceOutliner &space_outliner, short flag, short set); +bool outliner_flag_set(const ListBase &lb, short flag, short set); +bool outliner_flag_flip(const SpaceOutliner &space_outliner, short flag); +bool outliner_flag_flip(const ListBase &lb, short flag); +} void item_rename_fn(struct bContext *C, struct ReportList *reports, @@ -453,7 +455,8 @@ void id_remap_fn(struct bContext *C, /** * To retrieve coordinates with redrawing the entire tree. */ -void outliner_set_coordinates(struct ARegion *region, struct SpaceOutliner *space_outliner); +void outliner_set_coordinates(const struct ARegion *region, + const struct SpaceOutliner *space_outliner); /** * Open or close a tree element, optionally toggling all children recursively. @@ -510,6 +513,11 @@ void OUTLINER_OT_drivers_delete_selected(struct wmOperatorType *ot); void OUTLINER_OT_orphans_purge(struct wmOperatorType *ot); +/* outliner_query.cc ---------------------------------------------- */ + +bool outliner_shows_mode_column(const SpaceOutliner &space_outliner); +bool outliner_has_element_warnings(const SpaceOutliner &space_outliner); + /* outliner_tools.c ---------------------------------------------- */ void merged_element_search_menu_invoke(struct bContext *C, diff --git a/source/blender/editors/space_outliner/outliner_query.cc b/source/blender/editors/space_outliner/outliner_query.cc new file mode 100644 index 00000000000..d6483c44fce --- /dev/null +++ b/source/blender/editors/space_outliner/outliner_query.cc @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup spoutliner + */ + +#include <functional> + +#include "BLI_listbase.h" + +#include "DNA_space_types.h" + +#include "outliner_intern.hh" +#include "tree/tree_display.hh" + +using namespace blender::ed::outliner; + +bool outliner_shows_mode_column(const SpaceOutliner &space_outliner) +{ + const AbstractTreeDisplay &tree_display = *space_outliner.runtime->tree_display; + + return tree_display.supportsModeColumn() && (space_outliner.flag & SO_MODE_COLUMN); +} + +/** + * Iterate over the entire tree (including collapsed sub-elements), probing if any of the elements + * has a warning to be displayed. + */ +bool outliner_has_element_warnings(const SpaceOutliner &space_outliner) +{ + std::function<bool(const ListBase &)> recursive_fn; + + recursive_fn = [&](const ListBase &lb) { + LISTBASE_FOREACH (const TreeElement *, te, &lb) { + if (te->abstract_element && !te->abstract_element->getWarning().is_empty()) { + return true; + } + + if (recursive_fn(te->subtree)) { + return true; + } + } + + return false; + }; + + return recursive_fn(space_outliner.tree); +} diff --git a/source/blender/editors/space_outliner/outliner_select.cc b/source/blender/editors/space_outliner/outliner_select.cc index fd0ee422df0..877e0fc325c 100644 --- a/source/blender/editors/space_outliner/outliner_select.cc +++ b/source/blender/editors/space_outliner/outliner_select.cc @@ -66,7 +66,9 @@ #include "RNA_prototypes.h" #include "outliner_intern.hh" +#include "tree/tree_display.hh" #include "tree/tree_element_seq.hh" +#include "tree/tree_iterator.hh" using namespace blender::ed::outliner; @@ -1456,7 +1458,7 @@ void outliner_item_select(bContext *C, /* Clear previous active when activating and clear selection when not extending selection */ const short clear_flag = (activate ? TSE_ACTIVE : 0) | (extend ? 0 : TSE_SELECTED); if (clear_flag) { - outliner_flag_set(&space_outliner->tree, clear_flag, false); + outliner_flag_set(*space_outliner, clear_flag, false); } if (select_flag & OL_ITEM_SELECT) { @@ -1530,7 +1532,7 @@ static void do_outliner_range_select(bContext *C, const bool active_selected = (tselem->flag & TSE_SELECTED); if (!extend) { - outliner_flag_set(&space_outliner->tree, TSE_SELECTED, false); + outliner_flag_set(*space_outliner, TSE_SELECTED, false); } /* Select active if under cursor */ @@ -1557,12 +1559,11 @@ static bool outliner_is_co_within_restrict_columns(const SpaceOutliner *space_ou bool outliner_is_co_within_mode_column(SpaceOutliner *space_outliner, const float view_mval[2]) { - /* Mode toggles only show in View Layer and Scenes modes. */ - if (!ELEM(space_outliner->outlinevis, SO_VIEW_LAYER, SO_SCENES)) { + if (!outliner_shows_mode_column(*space_outliner)) { return false; } - return space_outliner->flag & SO_MODE_COLUMN && view_mval[0] < UI_UNIT_X; + return view_mval[0] < UI_UNIT_X; } static bool outliner_is_co_within_active_mode_column(bContext *C, @@ -1604,7 +1605,7 @@ static int outliner_item_do_activate_from_cursor(bContext *C, if (!(te = outliner_find_item_at_y(space_outliner, &space_outliner->tree, view_mval[1]))) { if (deselect_all) { - changed |= outliner_flag_set(&space_outliner->tree, TSE_SELECTED, false); + changed |= outliner_flag_set(*space_outliner, TSE_SELECTED, false); } } /* Don't allow toggle on scene collection */ @@ -1692,7 +1693,7 @@ void OUTLINER_OT_item_activate(wmOperatorType *ot) ot->poll = ED_operator_outliner_active; - ot->flag |= OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; PropertyRNA *prop; prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend selection for activation"); @@ -1715,26 +1716,17 @@ void OUTLINER_OT_item_activate(wmOperatorType *ot) /** \name Box Select Operator * \{ */ -static void outliner_item_box_select(bContext *C, - SpaceOutliner *space_outliner, - Scene *scene, - rctf *rectf, - TreeElement *te, - bool select) +static void outliner_box_select(bContext *C, + SpaceOutliner *space_outliner, + const rctf *rectf, + const bool select) { - TreeStoreElem *tselem = TREESTORE(te); - - if (te->ys <= rectf->ymax && te->ys + UI_UNIT_Y >= rectf->ymin) { - outliner_item_select( - C, space_outliner, te, (select ? OL_ITEM_SELECT : OL_ITEM_DESELECT) | OL_ITEM_EXTEND); - } - - /* Look at its children. */ - if (TSELEM_OPEN(tselem, space_outliner)) { - LISTBASE_FOREACH (TreeElement *, te_sub, &te->subtree) { - outliner_item_box_select(C, space_outliner, scene, rectf, te_sub, select); + tree_iterator::all_open(*space_outliner, [&](TreeElement *te) { + if (te->ys <= rectf->ymax && te->ys + UI_UNIT_Y >= rectf->ymin) { + outliner_item_select( + C, space_outliner, te, (select ? OL_ITEM_SELECT : OL_ITEM_DESELECT) | OL_ITEM_EXTEND); } - } + }); } static int outliner_box_select_exec(bContext *C, wmOperator *op) @@ -1747,15 +1739,13 @@ static int outliner_box_select_exec(bContext *C, wmOperator *op) const eSelectOp sel_op = (eSelectOp)RNA_enum_get(op->ptr, "mode"); const bool select = (sel_op != SEL_OP_SUB); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { - outliner_flag_set(&space_outliner->tree, TSE_SELECTED, 0); + outliner_flag_set(*space_outliner, TSE_SELECTED, 0); } WM_operator_properties_border_to_rctf(op, &rectf); UI_view2d_region_to_view_rctf(®ion->v2d, &rectf, &rectf); - LISTBASE_FOREACH (TreeElement *, te, &space_outliner->tree) { - outliner_item_box_select(C, space_outliner, scene, &rectf, te, select); - } + outliner_box_select(C, space_outliner, &rectf, select); DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); @@ -2038,7 +2028,7 @@ void OUTLINER_OT_select_walk(wmOperatorType *ot) ot->invoke = outliner_walk_select_invoke; ot->poll = ED_operator_outliner_active; - ot->flag |= OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ PropertyRNA *prop; diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc index 5da64177e51..ec19e8d5e5b 100644 --- a/source/blender/editors/space_outliner/outliner_tools.cc +++ b/source/blender/editors/space_outliner/outliner_tools.cc @@ -84,6 +84,7 @@ #include "outliner_intern.hh" #include "tree/tree_element_rna.hh" #include "tree/tree_element_seq.hh" +#include "tree/tree_iterator.hh" static CLG_LogRef LOG = {"ed.outliner.tools"}; @@ -221,53 +222,81 @@ static void unlink_action_fn(bContext *C, } static void unlink_material_fn(bContext *UNUSED(C), - ReportList *UNUSED(reports), + ReportList *reports, Scene *UNUSED(scene), TreeElement *te, TreeStoreElem *tsep, - TreeStoreElem *UNUSED(tselem), + TreeStoreElem *tselem, void *UNUSED(user_data)) { + const bool te_is_material = TSE_IS_REAL_ID(tselem) && (GS(tselem->id->name) == ID_MA); + + if (!te_is_material) { + /* Just fail silently. Another element may be selected that is a material, we don't want to + * confuse users with an error in that case. */ + return; + } + + if (!tsep || !TSE_IS_REAL_ID(tsep)) { + /* Valid case, no parent element of the material or it is not an ID (could be a #TSE_ID_BASE + * for example) so there's no data to unlink from. */ + BKE_reportf(reports, + RPT_WARNING, + "Cannot unlink material '%s'. It's not clear which object or object-data it " + "should be unlinked from, there's no object or object-data as parent in the " + "Outliner tree", + tselem->id->name + 2); + return; + } + Material **matar = nullptr; int a, totcol = 0; - if (GS(tsep->id->name) == ID_OB) { - Object *ob = (Object *)tsep->id; - totcol = ob->totcol; - matar = ob->mat; - } - else if (GS(tsep->id->name) == ID_ME) { - Mesh *me = (Mesh *)tsep->id; - totcol = me->totcol; - matar = me->mat; - } - else if (GS(tsep->id->name) == ID_CU_LEGACY) { - Curve *cu = (Curve *)tsep->id; - totcol = cu->totcol; - matar = cu->mat; - } - else if (GS(tsep->id->name) == ID_MB) { - MetaBall *mb = (MetaBall *)tsep->id; - totcol = mb->totcol; - matar = mb->mat; - } - else if (GS(tsep->id->name) == ID_CV) { - Curves *curves = (Curves *)tsep->id; - totcol = curves->totcol; - matar = curves->mat; - } - else if (GS(tsep->id->name) == ID_PT) { - PointCloud *pointcloud = (PointCloud *)tsep->id; - totcol = pointcloud->totcol; - matar = pointcloud->mat; - } - else if (GS(tsep->id->name) == ID_VO) { - Volume *volume = (Volume *)tsep->id; - totcol = volume->totcol; - matar = volume->mat; - } - else { - BLI_assert(0); + switch (GS(tsep->id->name)) { + case ID_OB: { + Object *ob = (Object *)tsep->id; + totcol = ob->totcol; + matar = ob->mat; + break; + } + case ID_ME: { + Mesh *me = (Mesh *)tsep->id; + totcol = me->totcol; + matar = me->mat; + break; + } + case ID_CU_LEGACY: { + Curve *cu = (Curve *)tsep->id; + totcol = cu->totcol; + matar = cu->mat; + break; + } + case ID_MB: { + MetaBall *mb = (MetaBall *)tsep->id; + totcol = mb->totcol; + matar = mb->mat; + break; + } + case ID_CV: { + Curves *curves = (Curves *)tsep->id; + totcol = curves->totcol; + matar = curves->mat; + break; + } + case ID_PT: { + PointCloud *pointcloud = (PointCloud *)tsep->id; + totcol = pointcloud->totcol; + matar = pointcloud->mat; + break; + } + case ID_VO: { + Volume *volume = (Volume *)tsep->id; + totcol = volume->totcol; + matar = volume->mat; + break; + } + default: + BLI_assert_unreachable(); } if (LIKELY(matar != nullptr)) { @@ -404,11 +433,10 @@ static void outliner_do_libdata_operation(bContext *C, ReportList *reports, Scene *scene, SpaceOutliner *space_outliner, - ListBase *lb, outliner_operation_fn operation_fn, void *user_data) { - LISTBASE_FOREACH (TreeElement *, te, lb) { + tree_iterator::all_open(*space_outliner, [&](TreeElement *te) { TreeStoreElem *tselem = TREESTORE(te); if (tselem->flag & TSE_SELECTED) { if (((tselem->type == TSE_SOME_ID) && (te->idcode != 0)) || @@ -417,11 +445,7 @@ static void outliner_do_libdata_operation(bContext *C, operation_fn(C, reports, scene, te, tsep, tselem, user_data); } } - if (TSELEM_OPEN(tselem, space_outliner)) { - outliner_do_libdata_operation( - C, reports, scene, space_outliner, &te->subtree, operation_fn, user_data); - } - } + }); } /** \} */ @@ -492,7 +516,7 @@ static int outliner_scene_operation_exec(bContext *C, wmOperator *op) ED_undo_push(C, "Delete Scene(s)"); } else { - BLI_assert(0); + BLI_assert_unreachable(); return OPERATOR_CANCELLED; } @@ -755,6 +779,10 @@ static void id_local_fn(bContext *C, struct OutlinerLibOverrideData { bool do_hierarchy; + + /** When creating new overrides, make them all user-editable. */ + bool do_fully_editable; + /** * For resync operation, force keeping newly created override IDs (or original linked IDs) * instead of re-applying relevant existing ID pointer property override operations. Helps @@ -949,7 +977,8 @@ static void id_override_library_create_fn(bContext *C, id_root_reference, id_hierarchy_root_reference, id_instance_hint, - &id_root_override); + &id_root_override, + data->do_fully_editable); BLI_assert(id_root_override != nullptr); BLI_assert(!ID_IS_LINKED(id_root_override)); @@ -1589,21 +1618,17 @@ static void outliner_do_data_operation( SpaceOutliner *space_outliner, int type, int event, - ListBase *lb, void (*operation_fn)(int, TreeElement *, TreeStoreElem *, void *), void *arg) { - LISTBASE_FOREACH (TreeElement *, te, lb) { + tree_iterator::all_open(*space_outliner, [&](TreeElement *te) { TreeStoreElem *tselem = TREESTORE(te); if (tselem->flag & TSE_SELECTED) { if (tselem->type == type) { operation_fn(event, te, tselem, arg); } } - if (TSELEM_OPEN(tselem, space_outliner)) { - outliner_do_data_operation(space_outliner, type, event, &te->subtree, operation_fn, arg); - } - } + }); } static Base *outliner_batch_delete_hierarchy( @@ -1726,53 +1751,59 @@ static int outliner_object_operation_exec(bContext *C, wmOperator *op) event = RNA_enum_get(op->ptr, "type"); - if (event == OL_OP_SELECT) { - Scene *sce = scene; /* To be able to delete, scenes are set... */ - outliner_do_object_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, object_select_fn); - if (scene != sce) { - WM_window_set_active_scene(bmain, C, win, sce); - } + switch (event) { + case OL_OP_SELECT: { + Scene *sce = scene; /* To be able to delete, scenes are set... */ + outliner_do_object_operation( + C, op->reports, scene, space_outliner, &space_outliner->tree, object_select_fn); + /* FIXME: This is most certainly broken, maybe check should rather be + * `if (CTX_data_scene(C) != scene)` ? */ + if (scene != sce) { + WM_window_set_active_scene(bmain, C, win, sce); + } - str = "Select Objects"; - selection_changed = true; - } - else if (event == OL_OP_SELECT_HIERARCHY) { - Scene *sce = scene; /* To be able to delete, scenes are set... */ - outliner_do_object_operation_ex(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - object_select_hierarchy_fn, - nullptr, - false); - if (scene != sce) { - WM_window_set_active_scene(bmain, C, win, sce); - } - str = "Select Object Hierarchy"; - selection_changed = true; - } - else if (event == OL_OP_DESELECT) { - outliner_do_object_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, object_deselect_fn); - str = "Deselect Objects"; - selection_changed = true; - } - else if (event == OL_OP_REMAP) { - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, id_remap_fn, nullptr); - /* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth - * trick does not work here). */ - } - else if (event == OL_OP_RENAME) { - outliner_do_object_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, item_rename_fn); - str = "Rename Object"; - } - else { - BLI_assert(0); - return OPERATOR_CANCELLED; + str = "Select Objects"; + selection_changed = true; + break; + } + case OL_OP_SELECT_HIERARCHY: { + Scene *sce = scene; /* To be able to delete, scenes are set... */ + outliner_do_object_operation_ex(C, + op->reports, + scene, + space_outliner, + &space_outliner->tree, + object_select_hierarchy_fn, + nullptr, + false); + /* FIXME: This is most certainly broken, maybe check should rather be + * `if (CTX_data_scene(C) != scene)` ? */ + if (scene != sce) { + WM_window_set_active_scene(bmain, C, win, sce); + } + str = "Select Object Hierarchy"; + selection_changed = true; + break; + } + case OL_OP_DESELECT: + outliner_do_object_operation( + C, op->reports, scene, space_outliner, &space_outliner->tree, object_deselect_fn); + str = "Deselect Objects"; + selection_changed = true; + break; + case OL_OP_REMAP: + outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_remap_fn, nullptr); + /* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth + * trick does not work here). */ + break; + case OL_OP_RENAME: + outliner_do_object_operation( + C, op->reports, scene, space_outliner, &space_outliner->tree, item_rename_fn); + str = "Rename Object"; + break; + default: + BLI_assert_unreachable(); + return OPERATOR_CANCELLED; } if (selection_changed) { @@ -1943,7 +1974,7 @@ void OUTLINER_OT_delete(wmOperatorType *ot) ot->poll = ED_operator_outliner_active; /* flags */ - ot->flag |= OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ PropertyRNA *prop = RNA_def_boolean( @@ -1964,6 +1995,7 @@ enum eOutlinerIdOpTypes { OUTLINER_IDOP_LOCAL, OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE, OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY, + OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY_FULLY_EDITABLE, OUTLINER_IDOP_OVERRIDE_LIBRARY_MAKE_EDITABLE, OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET, OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY, @@ -1996,7 +2028,7 @@ static const EnumPropertyItem prop_id_op_types[] = { 0, "Remap Users", "Make all users of selected data-blocks to use instead current (clicked) one"}, - {0, "", 0, nullptr, nullptr}, + RNA_ENUM_ITEM_SEPR, {OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE, "OVERRIDE_LIBRARY_CREATE", 0, @@ -2009,6 +2041,12 @@ static const EnumPropertyItem prop_id_op_types[] = { "Make Library Override Hierarchy", "Make a local override of this linked data-block, and its hierarchy of dependencies - only " "applies to active Outliner item"}, + {OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY_FULLY_EDITABLE, + "OVERRIDE_LIBRARY_CREATE_HIERARCHY_FULLY_EDITABLE", + 0, + "Make Library Override Hierarchy Fully Editable", + "Make a local override of this linked data-block, and its hierarchy of dependencies, making " + "them all fully user-editable - only applies to active Outliner item"}, {OUTLINER_IDOP_OVERRIDE_LIBRARY_MAKE_EDITABLE, "OVERRIDE_LIBRARY_MAKE_EDITABLE", 0, @@ -2049,10 +2087,10 @@ static const EnumPropertyItem prop_id_op_types[] = { "Clear Library Override Hierarchy", "Delete this local override (including its hierarchy of override dependencies) and relink " "its usages to the linked data-blocks"}, - {0, "", 0, nullptr, nullptr}, + RNA_ENUM_ITEM_SEPR, {OUTLINER_IDOP_COPY, "COPY", ICON_COPYDOWN, "Copy", ""}, {OUTLINER_IDOP_PASTE, "PASTE", ICON_PASTEDOWN, "Paste", ""}, - {0, "", 0, nullptr, nullptr}, + RNA_ENUM_ITEM_SEPR, {OUTLINER_IDOP_FAKE_ADD, "ADD_FAKE", 0, @@ -2088,6 +2126,7 @@ static bool outliner_id_operation_item_poll(bContext *C, } return false; case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY: + case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY_FULLY_EDITABLE: if (ID_IS_OVERRIDABLE_LIBRARY(tselem->id) || (ID_IS_LINKED(tselem->id))) { return true; } @@ -2164,13 +2203,8 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) case OUTLINER_IDOP_UNLINK: { /* unlink datablock from its parent */ if (objectlevel) { - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - unlink_object_fn, - nullptr); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, unlink_object_fn, nullptr); WM_event_add_notifier(C, NC_SCENE | ND_LAYER, nullptr); ED_undo_push(C, "Unlink Object"); @@ -2179,61 +2213,36 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) switch (idlevel) { case ID_AC: - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - unlink_action_fn, - nullptr); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, unlink_action_fn, nullptr); WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, nullptr); ED_undo_push(C, "Unlink action"); break; case ID_MA: - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - unlink_material_fn, - nullptr); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, unlink_material_fn, nullptr); WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, nullptr); ED_undo_push(C, "Unlink material"); break; case ID_TE: - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - unlink_texture_fn, - nullptr); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, unlink_texture_fn, nullptr); WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, nullptr); ED_undo_push(C, "Unlink texture"); break; case ID_WO: - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - unlink_world_fn, - nullptr); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, unlink_world_fn, nullptr); WM_event_add_notifier(C, NC_SCENE | ND_WORLD, nullptr); ED_undo_push(C, "Unlink world"); break; case ID_GR: - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - unlink_collection_fn, - nullptr); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, unlink_collection_fn, nullptr); WM_event_add_notifier(C, NC_SCENE | ND_LAYER, nullptr); ED_undo_push(C, "Unlink Collection"); @@ -2246,20 +2255,14 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) } case OUTLINER_IDOP_LOCAL: { /* make local */ - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, id_local_fn, nullptr); + outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_local_fn, nullptr); ED_undo_push(C, "Localized Data"); break; } case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE: { OutlinerLibOverrideData override_data{}; - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - id_override_library_create_fn, - &override_data); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, id_override_library_create_fn, &override_data); ED_undo_push(C, "Overridden Data"); break; } @@ -2270,19 +2273,30 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) op->reports, scene, space_outliner, - &space_outliner->tree, id_override_library_create_hierarchy_pre_process_fn, &override_data); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, id_override_library_create_fn, &override_data); + id_override_library_create_hierarchy_post_process(C, &override_data); + + ED_undo_push(C, "Overridden Data Hierarchy"); + break; + } + case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY_FULLY_EDITABLE: { + OutlinerLibOverrideData override_data{}; + override_data.do_hierarchy = true; + override_data.do_fully_editable = true; outliner_do_libdata_operation(C, op->reports, scene, space_outliner, - &space_outliner->tree, - id_override_library_create_fn, + id_override_library_create_hierarchy_pre_process_fn, &override_data); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, id_override_library_create_fn, &override_data); id_override_library_create_hierarchy_post_process(C, &override_data); - ED_undo_push(C, "Overridden Data Hierarchy"); + ED_undo_push(C, "Overridden Data Hierarchy Fully Editable"); break; } case OUTLINER_IDOP_OVERRIDE_LIBRARY_MAKE_EDITABLE: { @@ -2290,7 +2304,6 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) op->reports, scene, space_outliner, - &space_outliner->tree, id_override_library_toggle_flag_fn, POINTER_FROM_UINT(IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED)); @@ -2299,39 +2312,24 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) } case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET: { OutlinerLibOverrideData override_data{}; - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - id_override_library_reset_fn, - &override_data); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, id_override_library_reset_fn, &override_data); ED_undo_push(C, "Reset Overridden Data"); break; } case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY: { OutlinerLibOverrideData override_data{}; override_data.do_hierarchy = true; - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - id_override_library_reset_fn, - &override_data); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, id_override_library_reset_fn, &override_data); ED_undo_push(C, "Reset Overridden Data Hierarchy"); break; } case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY: { OutlinerLibOverrideData override_data{}; override_data.do_hierarchy = true; - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - id_override_library_resync_fn, - &override_data); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, id_override_library_resync_fn, &override_data); ED_undo_push(C, "Resync Overridden Data Hierarchy"); break; } @@ -2339,35 +2337,20 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) OutlinerLibOverrideData override_data{}; override_data.do_hierarchy = true; override_data.do_resync_hierarchy_enforce = true; - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - id_override_library_resync_fn, - &override_data); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, id_override_library_resync_fn, &override_data); ED_undo_push(C, "Resync Overridden Data Hierarchy"); break; } case OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_HIERARCHY: { - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - id_override_library_clear_hierarchy_fn, - nullptr); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, id_override_library_clear_hierarchy_fn, nullptr); ED_undo_push(C, "Clear Overridden Data Hierarchy"); break; } case OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_SINGLE: { - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - id_override_library_clear_single_fn, - nullptr); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, id_override_library_clear_single_fn, nullptr); ED_undo_push(C, "Clear Overridden Data Hierarchy"); break; } @@ -2375,26 +2358,16 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) /* make single user */ switch (idlevel) { case ID_AC: - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - singleuser_action_fn, - nullptr); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, singleuser_action_fn, nullptr); WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, nullptr); ED_undo_push(C, "Single-User Action"); break; case ID_WO: - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - singleuser_world_fn, - nullptr); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, singleuser_world_fn, nullptr); WM_event_add_notifier(C, NC_SCENE | ND_WORLD, nullptr); ED_undo_push(C, "Single-User World"); @@ -2409,15 +2382,14 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) case OUTLINER_IDOP_DELETE: { if (idlevel > 0) { outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, id_delete_fn, nullptr); + C, op->reports, scene, space_outliner, id_delete_fn, nullptr); ED_undo_push(C, "Delete"); } break; } case OUTLINER_IDOP_REMAP: { if (idlevel > 0) { - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, id_remap_fn, nullptr); + outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_remap_fn, nullptr); /* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth * trick does not work here). */ } @@ -2440,13 +2412,8 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) } case OUTLINER_IDOP_FAKE_ADD: { /* set fake user */ - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - id_fake_user_set_fn, - nullptr); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, id_fake_user_set_fn, nullptr); WM_event_add_notifier(C, NC_ID | NA_EDITED, nullptr); ED_undo_push(C, "Add Fake User"); @@ -2454,13 +2421,8 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) } case OUTLINER_IDOP_FAKE_CLEAR: { /* clear fake user */ - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - id_fake_user_clear_fn, - nullptr); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, id_fake_user_clear_fn, nullptr); WM_event_add_notifier(C, NC_ID | NA_EDITED, nullptr); ED_undo_push(C, "Clear Fake User"); @@ -2469,20 +2431,15 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) case OUTLINER_IDOP_RENAME: { /* rename */ outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, item_rename_fn, nullptr); + C, op->reports, scene, space_outliner, item_rename_fn, nullptr); WM_event_add_notifier(C, NC_ID | NA_EDITED, nullptr); ED_undo_push(C, "Rename"); break; } case OUTLINER_IDOP_SELECT_LINKED: - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - id_select_linked_fn, - nullptr); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, id_select_linked_fn, nullptr); ED_outliner_select_sync_from_all_tag(C); ED_undo_push(C, "Select"); break; @@ -2527,14 +2484,12 @@ void OUTLINER_OT_id_operation(wmOperatorType *ot) enum eOutlinerLibOpTypes { OL_LIB_INVALID = 0, - OL_LIB_RENAME, OL_LIB_DELETE, OL_LIB_RELOCATE, OL_LIB_RELOAD, }; static const EnumPropertyItem outliner_lib_op_type_items[] = { - {OL_LIB_RENAME, "RENAME", 0, "Rename", ""}, {OL_LIB_DELETE, "DELETE", ICON_X, @@ -2566,30 +2521,20 @@ static int outliner_lib_operation_exec(bContext *C, wmOperator *op) eOutlinerLibOpTypes event = (eOutlinerLibOpTypes)RNA_enum_get(op->ptr, "type"); switch (event) { - case OL_LIB_RENAME: { - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, item_rename_fn, nullptr); - - WM_event_add_notifier(C, NC_ID | NA_EDITED, nullptr); - ED_undo_push(C, "Rename Library"); - break; - } case OL_LIB_DELETE: { - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, id_delete_fn, nullptr); + outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_delete_fn, nullptr); ED_undo_push(C, "Delete Library"); break; } case OL_LIB_RELOCATE: { outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, lib_relocate_fn, nullptr); + C, op->reports, scene, space_outliner, lib_relocate_fn, nullptr); /* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth * trick does not work here). */ break; } case OL_LIB_RELOAD: { - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, lib_reload_fn, nullptr); + outliner_do_libdata_operation(C, op->reports, scene, space_outliner, lib_reload_fn, nullptr); /* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth * trick does not work here). */ break; @@ -2632,11 +2577,10 @@ void OUTLINER_OT_lib_operation(wmOperatorType *ot) static void outliner_do_id_set_operation( SpaceOutliner *space_outliner, int type, - ListBase *lb, ID *newid, void (*operation_fn)(TreeElement *, TreeStoreElem *, TreeStoreElem *, ID *)) { - LISTBASE_FOREACH (TreeElement *, te, lb) { + tree_iterator::all_open(*space_outliner, [&](TreeElement *te) { TreeStoreElem *tselem = TREESTORE(te); if (tselem->flag & TSE_SELECTED) { if (tselem->type == type) { @@ -2644,10 +2588,7 @@ static void outliner_do_id_set_operation( operation_fn(te, tselem, tsep, newid); } } - if (TSELEM_OPEN(tselem, space_outliner)) { - outliner_do_id_set_operation(space_outliner, type, &te->subtree, newid, operation_fn); - } - } + }); } static void actionset_id_fn(TreeElement *UNUSED(te), @@ -2702,12 +2643,10 @@ static int outliner_action_set_exec(bContext *C, wmOperator *op) /* perform action if valid channel */ if (datalevel == TSE_ANIM_DATA) { - outliner_do_id_set_operation( - space_outliner, datalevel, &space_outliner->tree, (ID *)act, actionset_id_fn); + outliner_do_id_set_operation(space_outliner, datalevel, (ID *)act, actionset_id_fn); } else if (idlevel == ID_AC) { - outliner_do_id_set_operation( - space_outliner, idlevel, &space_outliner->tree, (ID *)act, actionset_id_fn); + outliner_do_id_set_operation(space_outliner, idlevel, (ID *)act, actionset_id_fn); } else { return OPERATOR_CANCELLED; @@ -2794,8 +2733,7 @@ static int outliner_animdata_operation_exec(bContext *C, wmOperator *op) switch (event) { case OUTLINER_ANIMOP_CLEAR_ADT: /* Remove Animation Data - this may remove the active action, in some cases... */ - outliner_do_data_operation( - space_outliner, datalevel, event, &space_outliner->tree, clear_animdata_fn, nullptr); + outliner_do_data_operation(space_outliner, datalevel, event, clear_animdata_fn, nullptr); WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, nullptr); ED_undo_push(C, "Clear Animation Data"); @@ -2812,32 +2750,23 @@ static int outliner_animdata_operation_exec(bContext *C, wmOperator *op) case OUTLINER_ANIMOP_CLEAR_ACT: /* clear active action - using standard rules */ - outliner_do_data_operation( - space_outliner, datalevel, event, &space_outliner->tree, unlinkact_animdata_fn, nullptr); + outliner_do_data_operation(space_outliner, datalevel, event, unlinkact_animdata_fn, nullptr); WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, nullptr); ED_undo_push(C, "Unlink action"); break; case OUTLINER_ANIMOP_REFRESH_DRV: - outliner_do_data_operation(space_outliner, - datalevel, - event, - &space_outliner->tree, - refreshdrivers_animdata_fn, - nullptr); + outliner_do_data_operation( + space_outliner, datalevel, event, refreshdrivers_animdata_fn, nullptr); WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, nullptr); // ED_undo_push(C, "Refresh Drivers"); /* No undo needed - shouldn't have any impact? */ break; case OUTLINER_ANIMOP_CLEAR_DRV: - outliner_do_data_operation(space_outliner, - datalevel, - event, - &space_outliner->tree, - cleardrivers_animdata_fn, - nullptr); + outliner_do_data_operation( + space_outliner, datalevel, event, cleardrivers_animdata_fn, nullptr); WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, nullptr); ED_undo_push(C, "Clear Drivers"); @@ -2887,8 +2816,7 @@ static int outliner_constraint_operation_exec(bContext *C, wmOperator *op) SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); eOutliner_PropConstraintOps event = (eOutliner_PropConstraintOps)RNA_enum_get(op->ptr, "type"); - outliner_do_data_operation( - space_outliner, TSE_CONSTRAINT, event, &space_outliner->tree, constraint_fn, C); + outliner_do_data_operation(space_outliner, TSE_CONSTRAINT, event, constraint_fn, C); if (event == OL_CONSTRAINTOP_DELETE) { outliner_cleanup_tree(space_outliner); @@ -2934,8 +2862,7 @@ static int outliner_modifier_operation_exec(bContext *C, wmOperator *op) SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); eOutliner_PropModifierOps event = (eOutliner_PropModifierOps)RNA_enum_get(op->ptr, "type"); - outliner_do_data_operation( - space_outliner, TSE_MODIFIER, event, &space_outliner->tree, modifier_fn, C); + outliner_do_data_operation(space_outliner, TSE_MODIFIER, event, modifier_fn, C); if (event == OL_MODIFIER_OP_DELETE) { outliner_cleanup_tree(space_outliner); @@ -2978,24 +2905,21 @@ static int outliner_data_operation_exec(bContext *C, wmOperator *op) eOutliner_PropDataOps event = (eOutliner_PropDataOps)RNA_enum_get(op->ptr, "type"); switch (datalevel) { case TSE_POSE_CHANNEL: { - outliner_do_data_operation( - space_outliner, datalevel, event, &space_outliner->tree, pchan_fn, nullptr); + outliner_do_data_operation(space_outliner, datalevel, event, pchan_fn, nullptr); WM_event_add_notifier(C, NC_OBJECT | ND_POSE, nullptr); ED_undo_push(C, "PoseChannel operation"); break; } case TSE_BONE: { - outliner_do_data_operation( - space_outliner, datalevel, event, &space_outliner->tree, bone_fn, nullptr); + outliner_do_data_operation(space_outliner, datalevel, event, bone_fn, nullptr); WM_event_add_notifier(C, NC_OBJECT | ND_POSE, nullptr); ED_undo_push(C, "Bone operation"); break; } case TSE_EBONE: { - outliner_do_data_operation( - space_outliner, datalevel, event, &space_outliner->tree, ebone_fn, nullptr); + outliner_do_data_operation(space_outliner, datalevel, event, ebone_fn, nullptr); WM_event_add_notifier(C, NC_OBJECT | ND_POSE, nullptr); ED_undo_push(C, "EditBone operation"); @@ -3003,16 +2927,14 @@ static int outliner_data_operation_exec(bContext *C, wmOperator *op) } case TSE_SEQUENCE: { Scene *scene = CTX_data_scene(C); - outliner_do_data_operation( - space_outliner, datalevel, event, &space_outliner->tree, sequence_fn, scene); + outliner_do_data_operation(space_outliner, datalevel, event, sequence_fn, scene); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); ED_undo_push(C, "Sequencer operation"); break; } case TSE_GP_LAYER: { - outliner_do_data_operation( - space_outliner, datalevel, event, &space_outliner->tree, gpencil_layer_fn, nullptr); + outliner_do_data_operation(space_outliner, datalevel, event, gpencil_layer_fn, nullptr); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, nullptr); ED_undo_push(C, "Grease Pencil Layer operation"); @@ -3020,8 +2942,7 @@ static int outliner_data_operation_exec(bContext *C, wmOperator *op) } case TSE_RNA_STRUCT: if (event == OL_DOP_SELECT_LINKED) { - outliner_do_data_operation( - space_outliner, datalevel, event, &space_outliner->tree, data_select_linked_fn, C); + outliner_do_data_operation(space_outliner, datalevel, event, data_select_linked_fn, C); } break; diff --git a/source/blender/editors/space_outliner/outliner_tree.cc b/source/blender/editors/space_outliner/outliner_tree.cc index bbd9b48c260..aa739758ecb 100644 --- a/source/blender/editors/space_outliner/outliner_tree.cc +++ b/source/blender/editors/space_outliner/outliner_tree.cc @@ -802,7 +802,8 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner, void *idv, TreeElement *parent, short type, - short index) + short index, + const bool expand) { ID *id = reinterpret_cast<ID *>(idv); @@ -894,10 +895,10 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner, te->idcode = GS(id->name); } - if (te->abstract_element && te->abstract_element->isExpandValid()) { + if (expand && te->abstract_element && te->abstract_element->isExpandValid()) { tree_element_expand(*te->abstract_element, *space_outliner); } - else if (type == TSE_SOME_ID) { + else if (expand && (type == TSE_SOME_ID)) { /* ID types not (fully) ported to new design yet. */ if (te->abstract_element->expandPoll(*space_outliner)) { outliner_add_id_contents(space_outliner, te, tselem, id); @@ -919,10 +920,6 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner, BLI_assert_msg(false, "Element type should already use new AbstractTreeElement design"); } - if (tree_element_warnings_get(te, nullptr, nullptr)) { - te->flag |= TE_HAS_WARNING; - } - return te; } @@ -1686,6 +1683,9 @@ void outliner_build_tree(Main *mainvar, space_outliner->storeflag &= ~SO_TREESTORE_REBUILD; if (region->do_draw & RGN_DRAW_NO_REBUILD) { + BLI_assert_msg(space_outliner->runtime->tree_display != nullptr, + "Skipping rebuild before tree was built properly, a full redraw should be " + "triggered instead"); return; } diff --git a/source/blender/editors/space_outliner/outliner_utils.cc b/source/blender/editors/space_outliner/outliner_utils.cc index 4b947154864..0db612ce6db 100644 --- a/source/blender/editors/space_outliner/outliner_utils.cc +++ b/source/blender/editors/space_outliner/outliner_utils.cc @@ -27,6 +27,9 @@ #include "UI_view2d.h" #include "outliner_intern.hh" +#include "tree/tree_iterator.hh" + +using namespace blender::ed::outliner; /* -------------------------------------------------------------------- */ /** \name Tree View Context diff --git a/source/blender/editors/space_outliner/space_outliner.cc b/source/blender/editors/space_outliner/space_outliner.cc index 97dc659155f..5bcd1edebc0 100644 --- a/source/blender/editors/space_outliner/space_outliner.cc +++ b/source/blender/editors/space_outliner/space_outliner.cc @@ -438,7 +438,7 @@ static void outliner_deactivate(struct ScrArea *area) { /* Remove hover highlights */ SpaceOutliner *space_outliner = reinterpret_cast<SpaceOutliner *>(area->spacedata.first); - outliner_flag_set(&space_outliner->tree, TSE_HIGHLIGHTED_ANY, false); + outliner_flag_set(*space_outliner, TSE_HIGHLIGHTED_ANY, false); ED_region_tag_redraw_no_rebuild(BKE_area_find_region_type(area, RGN_TYPE_WINDOW)); } diff --git a/source/blender/editors/space_outliner/tree/tree_display.cc b/source/blender/editors/space_outliner/tree/tree_display.cc index 141c68594e8..6ab497b3fbb 100644 --- a/source/blender/editors/space_outliner/tree/tree_display.cc +++ b/source/blender/editors/space_outliner/tree/tree_display.cc @@ -45,9 +45,9 @@ std::unique_ptr<AbstractTreeDisplay> AbstractTreeDisplay::createFromDisplayMode( return nullptr; } -bool AbstractTreeDisplay::hasWarnings() const +bool AbstractTreeDisplay::supportsModeColumn() const { - return has_warnings; + return false; } } // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_display.hh b/source/blender/editors/space_outliner/tree/tree_display.hh index 327f29aa15e..190e35c81d6 100644 --- a/source/blender/editors/space_outliner/tree/tree_display.hh +++ b/source/blender/editors/space_outliner/tree/tree_display.hh @@ -75,12 +75,16 @@ class AbstractTreeDisplay { */ virtual ListBase buildTree(const TreeSourceData &source_data) = 0; - /** Accessor to whether given tree has some warnings to display. */ - bool hasWarnings() const; + /** + * Define if the display mode should be allowed to show a mode column on the left. This column + * adds an icon to indicate which objects are in the current mode (edit mode, pose mode, etc.) + * and allows adding other objects to the mode by clicking the icon. + * + * Returns false by default. + */ + virtual bool supportsModeColumn() const; protected: - bool has_warnings = false; - /** All derived classes will need a handle to this, so storing it in the base for convenience. */ SpaceOutliner &space_outliner_; }; @@ -100,6 +104,8 @@ class TreeDisplayViewLayer final : public AbstractTreeDisplay { ListBase buildTree(const TreeSourceData &source_data) override; + bool supportsModeColumn() const override; + private: void add_view_layer(Scene &, ListBase &, TreeElement *); void add_layer_collections_recursive(ListBase &, ListBase &, TreeElement &); @@ -212,6 +218,8 @@ class TreeDisplayScenes final : public AbstractTreeDisplay { TreeDisplayScenes(SpaceOutliner &space_outliner); ListBase buildTree(const TreeSourceData &source_data) override; + + bool supportsModeColumn() const override; }; /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/space_outliner/tree/tree_display_libraries.cc b/source/blender/editors/space_outliner/tree/tree_display_libraries.cc index 476bbdb63ae..405f1dd73f4 100644 --- a/source/blender/editors/space_outliner/tree/tree_display_libraries.cc +++ b/source/blender/editors/space_outliner/tree/tree_display_libraries.cc @@ -116,6 +116,11 @@ TreeElement *TreeDisplayLibraries::add_library_contents(Main &mainvar, ListBase ID *id = static_cast<ID *>(lbarray[a]->first); const bool is_library = (GS(id->name) == ID_LI) && (lib != nullptr); + /* Don't show deprecated types. */ + if (ID_TYPE_IS_DEPRECATED(GS(id->name))) { + continue; + } + /* check if there's data in current lib */ for (ID *id_iter : List<ID>(lbarray[a])) { if (id_iter->lib == lib) { @@ -136,9 +141,6 @@ TreeElement *TreeDisplayLibraries::add_library_contents(Main &mainvar, ListBase tenlib = outliner_add_element(&space_outliner_, &lb, &mainvar, nullptr, TSE_ID_BASE, 0); tenlib->name = IFACE_("Current File"); } - if (tenlib->flag & TE_HAS_WARNING) { - has_warnings = true; - } } /* Create data-block list parent element on demand. */ diff --git a/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc b/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc index 38025b58fd2..67798e978ab 100644 --- a/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc +++ b/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc @@ -34,13 +34,6 @@ TreeDisplayOverrideLibraryHierarchies::TreeDisplayOverrideLibraryHierarchies( { } -/* XXX Remove expanded subtree, we add our own items here. Expanding should probably be - * optional. */ -static void remove_expanded_children(TreeElement &te) -{ - outliner_free_tree(&te.subtree); -} - ListBase TreeDisplayOverrideLibraryHierarchies::buildTree(const TreeSourceData &source_data) { ListBase tree = {nullptr}; @@ -114,8 +107,7 @@ ListBase TreeDisplayOverrideLibraryHierarchies::build_hierarchy_for_lib_or_main( }); TreeElement *new_id_te = outliner_add_element( - &space_outliner_, &new_base_te->subtree, iter_id, new_base_te, TSE_SOME_ID, 0); - remove_expanded_children(*new_id_te); + &space_outliner_, &new_base_te->subtree, iter_id, new_base_te, TSE_SOME_ID, 0, false); build_hierarchy_for_ID(bmain, *iter_id, *tree_element_cast<TreeElementID>(new_id_te)); } @@ -193,8 +185,8 @@ static int build_hierarchy_foreach_ID_cb(LibraryIDLinkCallbackData *cb_data) &id, &build_data.parent_te->getLegacyElement(), TSE_SOME_ID, - 0); - remove_expanded_children(*new_te); + 0, + false); build_data.sibling_ids.add(&id); BuildHierarchyForeachIDCbData child_build_data{build_data.bmain, diff --git a/source/blender/editors/space_outliner/tree/tree_display_scenes.cc b/source/blender/editors/space_outliner/tree/tree_display_scenes.cc index 9e00a425a5a..6b1de7f8b95 100644 --- a/source/blender/editors/space_outliner/tree/tree_display_scenes.cc +++ b/source/blender/editors/space_outliner/tree/tree_display_scenes.cc @@ -26,6 +26,11 @@ TreeDisplayScenes::TreeDisplayScenes(SpaceOutliner &space_outliner) { } +bool TreeDisplayScenes::supportsModeColumn() const +{ + return true; +} + ListBase TreeDisplayScenes::buildTree(const TreeSourceData &source_data) { /* On first view we open scenes. */ diff --git a/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc b/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc index 19811e45b90..c8869d90eca 100644 --- a/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc +++ b/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc @@ -55,6 +55,11 @@ TreeDisplayViewLayer::TreeDisplayViewLayer(SpaceOutliner &space_outliner) { } +bool TreeDisplayViewLayer::supportsModeColumn() const +{ + return true; +} + ListBase TreeDisplayViewLayer::buildTree(const TreeSourceData &source_data) { ListBase tree = {nullptr}; @@ -266,14 +271,14 @@ void ObjectsChildrenBuilder::make_object_parent_hierarchy_collections() if (!found) { /* We add the child in the tree even if it is not in the collection. - * We deliberately clear its sub-tree though, to make it less prominent. */ + * We don't expand its sub-tree though, to make it less prominent. */ TreeElement *child_ob_tree_element = outliner_add_element(&outliner_, &parent_ob_tree_element->subtree, child, parent_ob_tree_element, TSE_SOME_ID, - 0); - outliner_free_tree(&child_ob_tree_element->subtree); + 0, + false); child_ob_tree_element->flag |= TE_CHILD_NOT_IN_COLLECTION; child_ob_tree_elements.append(child_ob_tree_element); } diff --git a/source/blender/editors/space_outliner/tree/tree_element.cc b/source/blender/editors/space_outliner/tree/tree_element.cc index 1e3fd2df7c2..94d55b70e3c 100644 --- a/source/blender/editors/space_outliner/tree/tree_element.cc +++ b/source/blender/editors/space_outliner/tree/tree_element.cc @@ -100,6 +100,11 @@ std::unique_ptr<AbstractTreeElement> AbstractTreeElement::createFromType(const i return nullptr; } +StringRefNull AbstractTreeElement::getWarning() const +{ + return ""; +} + void AbstractTreeElement::uncollapse_by_default(TreeElement *legacy_te) { if (!TREESTORE(legacy_te)->used) { @@ -118,39 +123,4 @@ void tree_element_expand(const AbstractTreeElement &tree_element, SpaceOutliner tree_element.expand(space_outliner); } -bool tree_element_warnings_get(TreeElement *te, int *r_icon, const char **r_message) -{ - TreeStoreElem *tselem = te->store_elem; - - if (tselem->type != TSE_SOME_ID) { - return false; - } - if (te->idcode != ID_LI) { - return false; - } - - Library *library = (Library *)tselem->id; - if (library->tag & LIBRARY_TAG_RESYNC_REQUIRED) { - if (r_icon) { - *r_icon = ICON_ERROR; - } - if (r_message) { - *r_message = TIP_( - "Contains linked library overrides that need to be resynced, updating the library is " - "recommended"); - } - return true; - } - if (library->id.tag & LIB_TAG_MISSING) { - if (r_icon) { - *r_icon = ICON_ERROR; - } - if (r_message) { - *r_message = TIP_("Missing library"); - } - return true; - } - return false; -} - } // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_element.hh b/source/blender/editors/space_outliner/tree/tree_element.hh index c6593a517dd..1098068d628 100644 --- a/source/blender/editors/space_outliner/tree/tree_element.hh +++ b/source/blender/editors/space_outliner/tree/tree_element.hh @@ -8,6 +8,8 @@ #include <memory> +#include "BLI_string_ref.hh" + struct ListBase; struct SpaceOutliner; struct TreeElement; @@ -56,6 +58,12 @@ class AbstractTreeElement { } /** + * By letting this return a warning message, the tree element will display a warning icon with + * the message in the tooltip. + */ + virtual StringRefNull getWarning() const; + + /** * Expand this tree element if it is displayed for the first time (as identified by its * tree-store element). * @@ -86,23 +94,20 @@ class AbstractTreeElement { * \note "ID" is not always a real ID. * \note If child items are only added to the tree if the item is open, * the `TSE_` type _must_ be added to #outliner_element_needs_rebuild_on_open_change(). + * + * \param expand: If true, the element may add its own sub-tree. E.g. objects will list their + * animation data, object data, constraints, modifiers, ... This often adds visual + * noise, and can be expensive to add in big scenes. So prefer setting this to + * false. */ struct TreeElement *outliner_add_element(SpaceOutliner *space_outliner, ListBase *lb, void *idv, struct TreeElement *parent, short type, - short index); + short index, + const bool expand = true); void tree_element_expand(const AbstractTreeElement &tree_element, SpaceOutliner &space_outliner); -/** - * Get actual warning data of a tree element, if any. - * - * \param r_icon: The icon to display as warning. - * \param r_message: The message to display as warning. - * \return true if there is a warning, false otherwise. - */ -bool tree_element_warnings_get(struct TreeElement *te, int *r_icon, const char **r_message); - } // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_element_id.cc b/source/blender/editors/space_outliner/tree/tree_element_id.cc index ef5e056f229..86f5fd4eff5 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_id.cc +++ b/source/blender/editors/space_outliner/tree/tree_element_id.cc @@ -27,6 +27,11 @@ namespace blender::ed::outliner { std::unique_ptr<TreeElementID> TreeElementID::createFromID(TreeElement &legacy_te, ID &id) { + if (ID_TYPE_IS_DEPRECATED(GS(id.name))) { + BLI_assert_msg(0, "Outliner trying to build tree-element for deprecated ID type"); + return nullptr; + } + switch (ID_Type type = GS(id.name); type) { case ID_LI: return std::make_unique<TreeElementIDLibrary>(legacy_te, (Library &)id); @@ -70,10 +75,9 @@ std::unique_ptr<TreeElementID> TreeElementID::createFromID(TreeElement &legacy_t case ID_PC: case ID_CF: return std::make_unique<TreeElementID>(legacy_te, id); - /* Deprecated */ case ID_IP: - BLI_assert_msg(0, "Outliner trying to build tree-element for deprecated ID type"); - return nullptr; + BLI_assert_unreachable(); + break; } return nullptr; diff --git a/source/blender/editors/space_outliner/tree/tree_element_id_library.cc b/source/blender/editors/space_outliner/tree/tree_element_id_library.cc index 0dcaec0385a..4f1b951ccaf 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_id_library.cc +++ b/source/blender/editors/space_outliner/tree/tree_element_id_library.cc @@ -4,6 +4,8 @@ * \ingroup spoutliner */ +#include "BLT_translation.h" + #include "DNA_ID.h" #include "DNA_listBase.h" @@ -24,4 +26,21 @@ bool TreeElementIDLibrary::isExpandValid() const return true; } +StringRefNull TreeElementIDLibrary::getWarning() const +{ + Library &library = reinterpret_cast<Library &>(id_); + + if (library.tag & LIBRARY_TAG_RESYNC_REQUIRED) { + return TIP_( + "Contains linked library overrides that need to be resynced, updating the library is " + "recommended"); + } + + if (library.id.tag & LIB_TAG_MISSING) { + return TIP_("Missing library"); + } + + return {}; +} + } // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_element_id_library.hh b/source/blender/editors/space_outliner/tree/tree_element_id_library.hh index ed599cf04da..2d89b55813f 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_id_library.hh +++ b/source/blender/editors/space_outliner/tree/tree_element_id_library.hh @@ -17,6 +17,8 @@ class TreeElementIDLibrary final : public TreeElementID { TreeElementIDLibrary(TreeElement &legacy_te, Library &library); bool isExpandValid() const override; + + blender::StringRefNull getWarning() const override; }; } // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_element_overrides.cc b/source/blender/editors/space_outliner/tree/tree_element_overrides.cc index 857f5577e59..871de39b1dd 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_overrides.cc +++ b/source/blender/editors/space_outliner/tree/tree_element_overrides.cc @@ -38,6 +38,19 @@ TreeElementOverridesBase::TreeElementOverridesBase(TreeElement &legacy_te, ID &i } } +StringRefNull TreeElementOverridesBase::getWarning() const +{ + if (id.flag & LIB_LIB_OVERRIDE_RESYNC_LEFTOVER) { + return TIP_("This override data-block is not needed anymore, but was detected as user-edited"); + } + + if (ID_IS_OVERRIDE_LIBRARY_REAL(&id) && ID_REAL_USERS(&id) == 0) { + return TIP_("This override data-block is unused"); + } + + return {}; +} + void TreeElementOverridesBase::expand(SpaceOutliner &space_outliner) const { BLI_assert(id.override_library != nullptr); @@ -54,21 +67,38 @@ void TreeElementOverridesBase::expand(SpaceOutliner &space_outliner) const for (auto *override_prop : ListBaseWrapper<IDOverrideLibraryProperty>(id.override_library->properties)) { + int rnaprop_index = 0; const bool is_rna_path_valid = BKE_lib_override_rna_property_find( - &idpoin, override_prop, &override_rna_ptr, &override_rna_prop); - if (is_rna_path_valid && !show_system_overrides && - ELEM(override_prop->rna_prop_type, PROP_POINTER, PROP_COLLECTION) && - RNA_struct_is_ID(RNA_property_pointer_type(&override_rna_ptr, override_rna_prop))) { - bool do_continue = true; - for (auto *override_prop_op : - ListBaseWrapper<IDOverrideLibraryPropertyOperation>(override_prop->operations)) { - if ((override_prop_op->flag & IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) == 0) { - do_continue = false; - break; + &idpoin, override_prop, &override_rna_ptr, &override_rna_prop, &rnaprop_index); + + /* Check for conditions where the liboverride property should be considered as a system + * override, if needed. */ + if (is_rna_path_valid && !show_system_overrides) { + bool do_skip = true; + bool is_system_override = false; + + /* Matching ID pointers are considered as system overrides. */ + if (ELEM(override_prop->rna_prop_type, PROP_POINTER, PROP_COLLECTION) && + RNA_struct_is_ID(RNA_property_pointer_type(&override_rna_ptr, override_rna_prop))) { + for (auto *override_prop_op : + ListBaseWrapper<IDOverrideLibraryPropertyOperation>(override_prop->operations)) { + if ((override_prop_op->flag & IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) == 0) { + do_skip = false; + break; + } + else { + is_system_override = true; + } } } - if (do_continue) { + /* Animated/driven properties are considered as system overrides. */ + if (!is_system_override && !BKE_lib_override_library_property_is_animated( + &id, override_prop, override_rna_prop, rnaprop_index)) { + do_skip = false; + } + + if (do_skip) { continue; } } @@ -84,14 +114,24 @@ TreeElementOverridesProperty::TreeElementOverridesProperty(TreeElement &legacy_t TreeElementOverridesData &override_data) : AbstractTreeElement(legacy_te), override_rna_ptr(override_data.override_rna_ptr), - override_rna_prop(override_data.override_rna_prop) + override_rna_prop(override_data.override_rna_prop), + rna_path(override_data.override_property.rna_path), + is_rna_path_valid(override_data.is_rna_path_valid) { BLI_assert(legacy_te.store_elem->type == TSE_LIBRARY_OVERRIDE); legacy_te.name = override_data.override_property.rna_path; - /* Abusing this for now, better way to do it is also pending current refactor of the whole tree - * code to use C++. */ - legacy_te.directdata = POINTER_FROM_UINT(override_data.is_rna_path_valid); +} + +StringRefNull TreeElementOverridesProperty::getWarning() const +{ + if (!is_rna_path_valid) { + return TIP_( + "This override property does not exist in current data, it will be removed on " + "next .blend file save"); + } + + return {}; } } // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_element_overrides.hh b/source/blender/editors/space_outliner/tree/tree_element_overrides.hh index a2d1409f193..1db46d9af1d 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_overrides.hh +++ b/source/blender/editors/space_outliner/tree/tree_element_overrides.hh @@ -8,6 +8,8 @@ #include "RNA_types.h" +#include "BLI_string_ref.hh" + #include "tree_element.hh" struct ID; @@ -32,6 +34,8 @@ class TreeElementOverridesBase final : public AbstractTreeElement { TreeElementOverridesBase(TreeElement &legacy_te, ID &id); void expand(SpaceOutliner &) const override; + + StringRefNull getWarning() const override; }; class TreeElementOverridesProperty final : public AbstractTreeElement { @@ -39,8 +43,13 @@ class TreeElementOverridesProperty final : public AbstractTreeElement { PointerRNA override_rna_ptr; PropertyRNA &override_rna_prop; + StringRefNull rna_path; + bool is_rna_path_valid; + public: TreeElementOverridesProperty(TreeElement &legacy_te, TreeElementOverridesData &override_data); + + StringRefNull getWarning() const override; }; } // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_iterator.cc b/source/blender/editors/space_outliner/tree/tree_iterator.cc new file mode 100644 index 00000000000..8d2b0b21433 --- /dev/null +++ b/source/blender/editors/space_outliner/tree/tree_iterator.cc @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup spoutliner + */ + +#include "DNA_space_types.h" + +#include "BLI_listbase.h" + +#include "../outliner_intern.hh" + +#include "tree_iterator.hh" + +namespace blender::ed::outliner::tree_iterator { + +void all(const SpaceOutliner &space_outliner, const VisitorFn visitor) +{ + all_open(space_outliner, space_outliner.tree, visitor); +} + +void all(const ListBase &subtree, const VisitorFn visitor) +{ + LISTBASE_FOREACH_MUTABLE (TreeElement *, element, &subtree) { + /* Get needed data out in case element gets freed. */ + const ListBase subtree = element->subtree; + + visitor(element); + /* Don't access element from now on, it may be freed. */ + + all(subtree, visitor); + } +} + +void all_open(const SpaceOutliner &space_outliner, const VisitorFn visitor) +{ + all_open(space_outliner, space_outliner.tree, visitor); +} + +void all_open(const SpaceOutliner &space_outliner, + const ListBase &subtree, + const VisitorFn visitor) +{ + LISTBASE_FOREACH_MUTABLE (TreeElement *, element, &subtree) { + /* Get needed data out in case element gets freed. */ + const TreeStoreElem *tselem = TREESTORE(element); + const ListBase subtree = element->subtree; + + visitor(element); + /* Don't access element from now on, it may be freed. Note that the open/collapsed state may + * also have been changed in the visitor callback. */ + + if (TSELEM_OPEN(tselem, &space_outliner)) { + all_open(space_outliner, subtree, visitor); + } + } +} + +} // namespace blender::ed::outliner::tree_iterator diff --git a/source/blender/editors/space_outliner/tree/tree_iterator.hh b/source/blender/editors/space_outliner/tree/tree_iterator.hh new file mode 100644 index 00000000000..de5bcd2c462 --- /dev/null +++ b/source/blender/editors/space_outliner/tree/tree_iterator.hh @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup spoutliner + */ + +#pragma once + +#include "BLI_function_ref.hh" + +struct ListBase; +struct SpaceOutliner; +struct TreeElement; + +namespace blender::ed::outliner { +namespace tree_iterator { + +using VisitorFn = FunctionRef<void(TreeElement *)>; + +/** + * Preorder (meaning depth-first) traversal of all elements (regardless of collapsed state). + * Freeing the currently visited element in \a visitor is fine. + */ +void all(const SpaceOutliner &space_outliner, VisitorFn visitor); +void all(const ListBase &subtree, VisitorFn visitor); + +/** + * Preorder (meaning depth-first) traversal of all elements not part of a collapsed sub-tree. + * Freeing the currently visited element in \a visitor is fine (but not its tree-store element). + */ +void all_open(const SpaceOutliner &, VisitorFn visitor); +void all_open(const SpaceOutliner &, const ListBase &subtree, VisitorFn visitor); + +} // namespace tree_iterator +} // namespace blender::ed::outliner diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index 9298eb83b46..647d13a4d56 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -31,6 +31,7 @@ #include "BKE_mask.h" #include "BKE_movieclip.h" #include "BKE_report.h" +#include "BKE_scene.h" #include "BKE_sound.h" #include "IMB_imbuf.h" @@ -54,6 +55,7 @@ #include "SEQ_transform.h" #include "SEQ_utils.h" +#include "ED_scene.h" /* For menu, popup, icons, etc. */ #include "ED_screen.h" #include "ED_sequencer.h" @@ -136,7 +138,7 @@ static void sequencer_generic_props__internal(wmOperatorType *ot, int flag) ot->srna, "overlap_shuffle_override", false, - "Override Overlap Shuffle Behaviour", + "Override Overlap Shuffle Behavior", "Use the overlap_mode tool settings to determine how to shuffle overlapping strips"); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); @@ -189,10 +191,11 @@ static int sequencer_generic_invoke_xy_guess_channel(bContext *C, int type) } for (seq = ed->seqbasep->first; seq; seq = seq->next) { - if ((ELEM(type, -1, seq->type)) && (seq->enddisp < timeline_frame) && - (timeline_frame - seq->enddisp < proximity)) { + const int strip_end = SEQ_time_right_handle_frame_get(seq); + if ((ELEM(type, -1, seq->type)) && (strip_end < timeline_frame) && + (timeline_frame - strip_end < proximity)) { tgt = seq; - proximity = timeline_frame - seq->enddisp; + proximity = timeline_frame - strip_end; } } @@ -337,7 +340,7 @@ static void seq_load_apply_generic_options(bContext *C, wmOperator *op, Sequence ScrArea *area = CTX_wm_area(C); const bool use_sync_markers = (((SpaceSeq *)area->spacedata.first)->flag & SEQ_MARKER_TRANS) != 0; - SEQ_transform_handle_overlap(scene, ed->seqbasep, strip_col, use_sync_markers); + SEQ_transform_handle_overlap(scene, ed->seqbasep, strip_col, NULL, use_sync_markers); SEQ_collection_free(strip_col); } @@ -469,6 +472,125 @@ void SEQUENCER_OT_scene_strip_add(struct wmOperatorType *ot) ot->prop = prop; } +static EnumPropertyItem strip_new_scene_items[] = { + {SCE_COPY_NEW, "NEW", 0, "New", "Add new Strip with a new empty Scene with default settings"}, + {SCE_COPY_EMPTY, + "EMPTY", + 0, + "Copy Settings", + "Add a new Strip, with an empty scene, and copy settings from the current scene"}, + {SCE_COPY_LINK_COLLECTION, + "LINK_COPY", + 0, + "Linked Copy", + "Add a Strip and link in the collections from the current scene (shallow copy)"}, + {SCE_COPY_FULL, + "FULL_COPY", + 0, + "Full Copy", + "Add a Strip and make a full copy of the current scene"}, + {0, NULL, 0, NULL, NULL}, +}; + +static int sequencer_add_scene_strip_new_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + const Editing *ed = SEQ_editing_ensure(scene); + + if (RNA_boolean_get(op->ptr, "replace_sel")) { + ED_sequencer_deselect_all(scene); + } + + SeqLoadData load_data; + load_data_init_from_operator(&load_data, C, op); + + int type = RNA_enum_get(op->ptr, "type"); + Scene *scene_new = ED_scene_sequencer_add(bmain, C, type, false); + if (scene_new == NULL) { + return OPERATOR_CANCELLED; + } + load_data.scene = scene_new; + + Sequence *seq = SEQ_add_scene_strip(scene, ed->seqbasep, &load_data); + seq_load_apply_generic_options(C, op, seq); + + DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); + DEG_relations_tag_update(bmain); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); + + return OPERATOR_FINISHED; +} + +static int sequencer_add_scene_strip_new_invoke(bContext *C, + wmOperator *op, + const wmEvent *UNUSED(event)) +{ + sequencer_disable_one_time_properties(C, op); + sequencer_generic_invoke_xy__internal(C, op, 0, SEQ_TYPE_SCENE); + return sequencer_add_scene_strip_new_exec(C, op); +} + +static const EnumPropertyItem *strip_new_sequencer_enum_itemf(bContext *C, + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + bool *r_free) +{ + EnumPropertyItem *item = NULL; + int totitem = 0; + uint item_index; + + item_index = RNA_enum_from_value(strip_new_scene_items, SCE_COPY_NEW); + RNA_enum_item_add(&item, &totitem, &strip_new_scene_items[item_index]); + + bool has_scene_or_no_context = false; + if (C == NULL) { + /* For documentation generation. */ + has_scene_or_no_context = true; + } + else { + Scene *scene = CTX_data_scene(C); + Sequence *seq = SEQ_select_active_get(scene); + if ((seq && (seq->type == SEQ_TYPE_SCENE) && (seq->scene != NULL))) { + has_scene_or_no_context = true; + } + } + + if (has_scene_or_no_context) { + int values[] = {SCE_COPY_EMPTY, SCE_COPY_LINK_COLLECTION, SCE_COPY_FULL}; + for (int i = 0; i < ARRAY_SIZE(values); i++) { + item_index = RNA_enum_from_value(strip_new_scene_items, values[i]); + RNA_enum_item_add(&item, &totitem, &strip_new_scene_items[item_index]); + } + } + + RNA_enum_item_end(&item, &totitem); + *r_free = true; + return item; +} + +void SEQUENCER_OT_scene_strip_add_new(struct wmOperatorType *ot) +{ + /* Identifiers. */ + ot->name = "Add Strip with a new Scene"; + ot->idname = "SEQUENCER_OT_scene_strip_add_new"; + ot->description = "Create a new Strip and add a assign a new Scene as source"; + + /* Api callbacks. */ + ot->invoke = sequencer_add_scene_strip_new_invoke; + ot->exec = sequencer_add_scene_strip_new_exec; + ot->poll = ED_operator_sequencer_active_editable; + + /* Flags. */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + sequencer_generic_props__internal(ot, SEQPROP_STARTFRAME); + + ot->prop = RNA_def_enum(ot->srna, "type", strip_new_scene_items, SCE_COPY_NEW, "Type", ""); + RNA_def_enum_funcs(ot->prop, strip_new_sequencer_enum_itemf); + RNA_def_property_flag(ot->prop, PROP_ENUM_NO_TRANSLATE); +} + static int sequencer_add_movieclip_strip_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); @@ -664,7 +786,6 @@ static void seq_build_proxy(bContext *C, SeqCollection *movie_strips) } static void sequencer_add_movie_clamp_sound_strip_length(Scene *scene, - ListBase *seqbase, Sequence *seq_movie, Sequence *seq_sound) { @@ -672,9 +793,8 @@ static void sequencer_add_movie_clamp_sound_strip_length(Scene *scene, return; } - SEQ_transform_set_right_handle_frame(seq_sound, SEQ_transform_get_right_handle_frame(seq_movie)); - SEQ_transform_set_left_handle_frame(seq_sound, SEQ_transform_get_left_handle_frame(seq_movie)); - SEQ_time_update_sequence(scene, seqbase, seq_sound); + SEQ_time_right_handle_frame_set(scene, seq_sound, SEQ_time_right_handle_frame_get(seq_movie)); + SEQ_time_left_handle_frame_set(scene, seq_sound, SEQ_time_left_handle_frame_get(seq_movie)); } static void sequencer_add_movie_multiple_strips(bContext *C, @@ -712,7 +832,7 @@ static void sequencer_add_movie_multiple_strips(bContext *C, else { if (RNA_boolean_get(op->ptr, "sound")) { seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data); - sequencer_add_movie_clamp_sound_strip_length(scene, ed->seqbasep, seq_movie, seq_sound); + sequencer_add_movie_clamp_sound_strip_length(scene, seq_movie, seq_sound); if (seq_sound) { /* The video has sound, shift the video strip up a channel to make room for the sound @@ -721,7 +841,8 @@ static void sequencer_add_movie_multiple_strips(bContext *C, } } - load_data->start_frame += seq_movie->enddisp - seq_movie->startdisp; + load_data->start_frame += SEQ_time_right_handle_frame_get(seq_movie) - + SEQ_time_left_handle_frame_get(seq_movie); if (overlap_shuffle_override) { has_seq_overlap |= seq_load_apply_generic_options_only_test_overlap( C, op, seq_sound, strip_col); @@ -742,7 +863,7 @@ static void sequencer_add_movie_multiple_strips(bContext *C, ScrArea *area = CTX_wm_area(C); const bool use_sync_markers = (((SpaceSeq *)area->spacedata.first)->flag & SEQ_MARKER_TRANS) != 0; - SEQ_transform_handle_overlap(scene, ed->seqbasep, strip_col, use_sync_markers); + SEQ_transform_handle_overlap(scene, ed->seqbasep, strip_col, NULL, use_sync_markers); } SEQ_collection_free(strip_col); @@ -769,7 +890,7 @@ static bool sequencer_add_movie_single_strip(bContext *C, } if (RNA_boolean_get(op->ptr, "sound")) { seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data); - sequencer_add_movie_clamp_sound_strip_length(scene, ed->seqbasep, seq_movie, seq_sound); + sequencer_add_movie_clamp_sound_strip_length(scene, seq_movie, seq_sound); if (seq_sound) { /* The video has sound, shift the video strip up a channel to make room for the sound * strip. */ @@ -792,7 +913,7 @@ static bool sequencer_add_movie_single_strip(bContext *C, ScrArea *area = CTX_wm_area(C); const bool use_sync_markers = (((SpaceSeq *)area->spacedata.first)->flag & SEQ_MARKER_TRANS) != 0; - SEQ_transform_handle_overlap(scene, ed->seqbasep, strip_col, use_sync_markers); + SEQ_transform_handle_overlap(scene, ed->seqbasep, strip_col, NULL, use_sync_markers); } SEQ_collection_free(strip_col); @@ -952,7 +1073,8 @@ static void sequencer_add_sound_multiple_strips(bContext *C, } else { seq_load_apply_generic_options(C, op, seq); - load_data->start_frame += seq->enddisp - seq->startdisp; + load_data->start_frame += SEQ_time_right_handle_frame_get(seq) - + SEQ_time_left_handle_frame_get(seq); } } RNA_END; @@ -1179,8 +1301,7 @@ static int sequencer_add_image_strip_exec(bContext *C, wmOperator *op) /* Adjust length. */ if (load_data.image.len == 1) { - SEQ_transform_set_right_handle_frame(seq, load_data.image.end_frame); - SEQ_time_update_sequence(scene, SEQ_active_seqbase_get(ed), seq); + SEQ_time_right_handle_frame_set(scene, seq, load_data.image.end_frame); } seq_load_apply_generic_options(C, op, seq); diff --git a/source/blender/editors/space_sequencer/sequencer_channels_draw.c b/source/blender/editors/space_sequencer/sequencer_channels_draw.c index a777132e9fc..c11388e8555 100644 --- a/source/blender/editors/space_sequencer/sequencer_channels_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_channels_draw.c @@ -200,7 +200,7 @@ static float text_size_get(const SeqChannelDrawContext *context) return UI_fontstyle_height_max(&style->widget) * 1.5f * context->scale; } -/* Todo: decide what gets priority - label or buttons */ +/* TODO: decide what gets priority - label or buttons. */ static rctf label_rect_init(const SeqChannelDrawContext *context, const int channel_index, const float used_width) @@ -286,7 +286,7 @@ static void draw_channel_labels(const SeqChannelDrawContext *context, } } -/* Todo: different text/buttons alignment */ +/* TODO: different text/buttons alignment. */ static void draw_channel_header(const SeqChannelDrawContext *context, uiBlock *block, const int channel_index) diff --git a/source/blender/editors/space_sequencer/sequencer_drag_drop.c b/source/blender/editors/space_sequencer/sequencer_drag_drop.c index 645c0dc9a1d..8dadb9360e3 100644 --- a/source/blender/editors/space_sequencer/sequencer_drag_drop.c +++ b/source/blender/editors/space_sequencer/sequencer_drag_drop.c @@ -231,9 +231,8 @@ static void update_overlay_strip_poistion_data(bContext *C, const int mval[2]) else { /* Check if there is a strip that would intersect with the new strip(s). */ coords->is_intersecting = false; - Sequence dummy_seq = {.machine = coords->channel, - .startdisp = coords->start_frame, - .enddisp = coords->start_frame + coords->strip_len}; + Sequence dummy_seq = { + .machine = coords->channel, .start = coords->start_frame, .len = coords->strip_len}; Editing *ed = SEQ_editing_get(scene); for (int i = 0; i < coords->channel_len && !coords->is_intersecting; i++) { diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 4cb41c702da..25701c323b9 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -572,8 +572,6 @@ static void drawmeta_contents(Scene *scene, float y2, const bool show_strip_color_tag) { - Editing *ed = SEQ_editing_get(scene); - ListBase *channels = SEQ_channels_displayed_get(ed); Sequence *seq; uchar col[4]; @@ -582,11 +580,16 @@ static void drawmeta_contents(Scene *scene, int chan_range = 0; float draw_range = y2 - y1; float draw_height; - ListBase *seqbase; + + Editing *ed = SEQ_editing_get(scene); + ListBase *channels = SEQ_channels_displayed_get(ed); + ListBase *meta_seqbase; + ListBase *meta_channels; int offset; - seqbase = SEQ_get_seqbase_from_sequence(seqm, &offset); - if (!seqbase || BLI_listbase_is_empty(seqbase)) { + meta_seqbase = SEQ_get_seqbase_from_sequence(seqm, &meta_channels, &offset); + + if (!meta_seqbase || BLI_listbase_is_empty(meta_seqbase)) { return; } @@ -599,7 +602,7 @@ static void drawmeta_contents(Scene *scene, GPU_blend(GPU_BLEND_ALPHA); - for (seq = seqbase->first; seq; seq = seq->next) { + for (seq = meta_seqbase->first; seq; seq = seq->next) { chan_min = min_ii(chan_min, seq->machine); chan_max = max_ii(chan_max, seq->machine); } @@ -613,9 +616,9 @@ static void drawmeta_contents(Scene *scene, immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); /* Draw only immediate children (1 level depth). */ - for (seq = seqbase->first; seq; seq = seq->next) { - const int startdisp = seq->startdisp + offset; - const int enddisp = seq->enddisp + offset; + for (seq = meta_seqbase->first; seq; seq = seq->next) { + const int startdisp = SEQ_time_left_handle_frame_get(seq) + offset; + const int enddisp = SEQ_time_right_handle_frame_get(seq) + offset; if ((startdisp > x2 || enddisp < x1) == 0) { float y_chan = (seq->machine - chan_min) / (float)(chan_range)*draw_range; @@ -631,7 +634,7 @@ static void drawmeta_contents(Scene *scene, color3ubv_from_seq(scene, seq, show_strip_color_tag, col); } - if (SEQ_render_is_muted(channels, seqm) || SEQ_render_is_muted(&seqm->channels, seq)) { + if (SEQ_render_is_muted(channels, seqm) || SEQ_render_is_muted(meta_channels, seq)) { col[3] = 64; } else { @@ -665,7 +668,10 @@ float sequence_handle_size_get_clamped(Sequence *seq, const float pixelx) const float maxhandle = (pixelx * SEQ_HANDLE_SIZE) * U.pixelsize; /* Ensure that handle is not wider, than quarter of strip. */ - return min_ff(maxhandle, ((float)(seq->enddisp - seq->startdisp) / 4.0f)); + return min_ff( + maxhandle, + ((float)(SEQ_time_right_handle_frame_get(seq) - SEQ_time_left_handle_frame_get(seq)) / + 4.0f)); } /* Draw a handle, on left or right side of strip. */ @@ -683,8 +689,8 @@ static void draw_seq_handle(View2D *v2d, uint whichsel = 0; uchar col[4]; - x1 = seq->startdisp; - x2 = seq->enddisp; + x1 = SEQ_time_left_handle_frame_get(seq); + x2 = SEQ_time_right_handle_frame_get(seq); y1 = seq->machine + SEQ_STRIP_OFSBOTTOM; y2 = seq->machine + SEQ_STRIP_OFSTOP; @@ -736,7 +742,11 @@ static void draw_seq_handle(View2D *v2d, BLF_set_default(); /* Calculate if strip is wide enough for showing the labels. */ - numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), "%d%d", seq->startdisp, seq->enddisp); + numstr_len = BLI_snprintf_rlen(numstr, + sizeof(numstr), + "%d%d", + SEQ_time_left_handle_frame_get(seq), + SEQ_time_right_handle_frame_get(seq)); float tot_width = BLF_width(fontid, numstr, numstr_len); if ((x2 - x1) / pixelx > 20 + tot_width) { @@ -744,12 +754,14 @@ static void draw_seq_handle(View2D *v2d, float text_margin = 1.2f * handsize_clamped; if (direction == SEQ_LEFTHANDLE) { - numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), "%d", seq->startdisp); + numstr_len = BLI_snprintf_rlen( + numstr, sizeof(numstr), "%d", SEQ_time_left_handle_frame_get(seq)); x1 += text_margin; y1 += 0.09f; } else { - numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), "%d", seq->enddisp - 1); + numstr_len = BLI_snprintf_rlen( + numstr, sizeof(numstr), "%d", SEQ_time_right_handle_frame_get(seq) - 1); x1 = x2 - (text_margin + pixelx * BLF_width(fontid, numstr, numstr_len)); y1 += 0.09f; } @@ -910,7 +922,8 @@ static size_t draw_seq_text_get_overlay_string(SpaceSeq *sseq, char strip_duration_text[16]; if (sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_STRIP_DURATION) { - const int strip_duration = seq->enddisp - seq->startdisp; + const int strip_duration = SEQ_time_right_handle_frame_get(seq) - + SEQ_time_left_handle_frame_get(seq); SNPRINTF(strip_duration_text, "%d", strip_duration); if (i != 0) { text_array[i++] = text_sep; @@ -977,8 +990,8 @@ static void draw_sequence_extensions_overlay( float x1, x2, y1, y2; uchar col[4], blend_col[3]; - x1 = seq->startdisp; - x2 = seq->enddisp; + x1 = SEQ_time_left_handle_frame_get(seq); + x2 = SEQ_time_right_handle_frame_get(seq); y1 = seq->machine + SEQ_STRIP_OFSBOTTOM; y2 = seq->machine + SEQ_STRIP_OFSTOP; @@ -1035,15 +1048,19 @@ static void draw_color_strip_band( immUniformColor4ubv(col); - immRectf(pos, seq->startdisp, y1, seq->enddisp, text_margin_y); + immRectf(pos, + SEQ_time_left_handle_frame_get(seq), + y1, + SEQ_time_right_handle_frame_get(seq), + text_margin_y); /* 1px line to better separate the color band. */ UI_GetColorPtrShade3ubv(col, col, -20); immUniformColor4ubv(col); immBegin(GPU_PRIM_LINES, 2); - immVertex2f(pos, seq->startdisp, text_margin_y); - immVertex2f(pos, seq->enddisp, text_margin_y); + immVertex2f(pos, SEQ_time_left_handle_frame_get(seq), text_margin_y); + immVertex2f(pos, SEQ_time_right_handle_frame_get(seq), text_margin_y); immEnd(); GPU_blend(GPU_BLEND_NONE); @@ -1095,28 +1112,25 @@ static void draw_seq_background(Scene *scene, /* Draw the main strip body. */ if (is_single_image) { - immRectf(pos, - SEQ_transform_get_left_handle_frame(seq), - y1, - SEQ_transform_get_right_handle_frame(seq), - y2); + immRectf( + pos, SEQ_time_left_handle_frame_get(seq), y1, SEQ_time_right_handle_frame_get(seq), y2); } else { immRectf(pos, x1, y1, x2, y2); } /* Draw background for hold still regions. */ - if (!is_single_image && (seq->startstill || seq->endstill)) { + if (!is_single_image && SEQ_time_has_still_frames(seq)) { UI_GetColorPtrShade3ubv(col, col, -35); immUniformColor4ubv(col); - if (seq->startstill) { - const float content_start = min_ff(seq->enddisp, seq->start); - immRectf(pos, seq->startdisp, y1, content_start, y2); + if (SEQ_time_has_left_still_frames(seq)) { + const float content_start = min_ff(SEQ_time_right_handle_frame_get(seq), seq->start); + immRectf(pos, SEQ_time_left_handle_frame_get(seq), y1, content_start, y2); } - if (seq->endstill) { - const float content_end = max_ff(seq->startdisp, seq->start + seq->len); - immRectf(pos, content_end, y1, seq->enddisp, y2); + if (SEQ_time_has_right_still_frames(seq)) { + const float content_end = max_ff(SEQ_time_left_handle_frame_get(seq), seq->start + seq->len); + immRectf(pos, content_end, y1, SEQ_time_right_handle_frame_get(seq), y2); } } @@ -1333,14 +1347,15 @@ static void draw_seq_strip(const bContext *C, SEQ_TIMELINE_SHOW_STRIP_COLOR_TAG); /* Draw strip body. */ - x1 = (seq->startstill) ? seq->start : seq->startdisp; + x1 = SEQ_time_has_left_still_frames(seq) ? seq->start : SEQ_time_left_handle_frame_get(seq); y1 = seq->machine + SEQ_STRIP_OFSBOTTOM; - x2 = (seq->endstill) ? (seq->start + seq->len) : seq->enddisp; + x2 = SEQ_time_has_right_still_frames(seq) ? (seq->start + seq->len) : + SEQ_time_right_handle_frame_get(seq); y2 = seq->machine + SEQ_STRIP_OFSTOP; /* Limit body to strip bounds. Meta strip can end up with content outside of strip range. */ - x1 = min_ff(x1, seq->enddisp); - x2 = max_ff(x2, seq->startdisp); + x1 = min_ff(x1, SEQ_time_right_handle_frame_get(seq)); + x2 = max_ff(x2, SEQ_time_left_handle_frame_get(seq)); float text_margin_y; bool y_threshold; @@ -1380,8 +1395,8 @@ static void draw_seq_strip(const bContext *C, } immUnbindProgram(); - x1 = seq->startdisp; - x2 = seq->enddisp; + x1 = SEQ_time_left_handle_frame_get(seq); + x2 = SEQ_time_right_handle_frame_get(seq); if ((seq->type == SEQ_TYPE_META) || ((seq->type == SEQ_TYPE_SCENE) && (seq->flag & SEQ_SCENE_STRIPS))) { @@ -1471,23 +1486,23 @@ static void draw_effect_inputs_highlight(Sequence *seq) immUniformColor4ub(255, 255, 255, 48); immRectf(pos, - seq1->startdisp, + SEQ_time_left_handle_frame_get(seq1), seq1->machine + SEQ_STRIP_OFSBOTTOM, - seq1->enddisp, + SEQ_time_right_handle_frame_get(seq1), seq1->machine + SEQ_STRIP_OFSTOP); if (seq2 && seq2 != seq1) { immRectf(pos, - seq2->startdisp, + SEQ_time_left_handle_frame_get(seq2), seq2->machine + SEQ_STRIP_OFSBOTTOM, - seq2->enddisp, + SEQ_time_right_handle_frame_get(seq2), seq2->machine + SEQ_STRIP_OFSTOP); } if (seq3 && !ELEM(seq3, seq1, seq2)) { immRectf(pos, - seq3->startdisp, + SEQ_time_left_handle_frame_get(seq3), seq3->machine + SEQ_STRIP_OFSBOTTOM, - seq3->enddisp, + SEQ_time_right_handle_frame_get(seq3), seq3->machine + SEQ_STRIP_OFSTOP); } immUnbindProgram(); @@ -2081,10 +2096,10 @@ static int sequencer_draw_get_transform_preview_frame(Scene *scene) int preview_frame; if (last_seq->flag & SEQ_RIGHTSEL) { - preview_frame = last_seq->enddisp - 1; + preview_frame = SEQ_time_right_handle_frame_get(last_seq) - 1; } else { - preview_frame = last_seq->startdisp; + preview_frame = SEQ_time_left_handle_frame_get(last_seq); } return preview_frame; @@ -2310,10 +2325,10 @@ static void draw_seq_strips(const bContext *C, Editing *ed, ARegion *region) if (seq == last_seq && (last_seq->flag & SELECT)) { continue; } - if (min_ii(seq->startdisp, seq->start) > v2d->cur.xmax) { + if (min_ii(SEQ_time_left_handle_frame_get(seq), seq->start) > v2d->cur.xmax) { continue; } - if (max_ii(seq->enddisp, seq->start + seq->len) < v2d->cur.xmin) { + if (max_ii(SEQ_time_right_handle_frame_get(seq), seq->start + seq->len) < v2d->cur.xmin) { continue; } if (seq->machine + 1.0f < v2d->cur.ymin) { @@ -2368,9 +2383,9 @@ static void draw_seq_strips(const bContext *C, Editing *ed, ARegion *region) immUniformColor4ub(255, 255, 255, 48); immRectf(pos, - seq->startdisp, + SEQ_time_left_handle_frame_get(seq), seq->machine + SEQ_STRIP_OFSBOTTOM, - seq->enddisp, + SEQ_time_right_handle_frame_get(seq), seq->machine + SEQ_STRIP_OFSTOP); immUnbindProgram(); @@ -2597,7 +2612,8 @@ static void draw_cache_view(const bContext *C) continue; } - if (seq->startdisp > v2d->cur.xmax || seq->enddisp < v2d->cur.xmin) { + if (SEQ_time_left_handle_frame_get(seq) > v2d->cur.xmax || + SEQ_time_right_handle_frame_get(seq) < v2d->cur.xmin) { continue; } @@ -2607,7 +2623,11 @@ static void draw_cache_view(const bContext *C) if (scene->ed->cache_flag & SEQ_CACHE_VIEW_RAW) { const float bg_color[4] = {1.0f, 0.1f, 0.02f, 0.1f}; immUniformColor4f(bg_color[0], bg_color[1], bg_color[2], bg_color[3]); - immRectf(pos, seq->startdisp, stripe_bot, seq->enddisp, stripe_top); + immRectf(pos, + SEQ_time_left_handle_frame_get(seq), + stripe_bot, + SEQ_time_right_handle_frame_get(seq), + stripe_top); } stripe_bot += stripe_ht + stripe_ofs_y; @@ -2616,7 +2636,11 @@ static void draw_cache_view(const bContext *C) if (scene->ed->cache_flag & SEQ_CACHE_VIEW_PREPROCESSED) { const float bg_color[4] = {0.1f, 0.1f, 0.75f, 0.1f}; immUniformColor4f(bg_color[0], bg_color[1], bg_color[2], bg_color[3]); - immRectf(pos, seq->startdisp, stripe_bot, seq->enddisp, stripe_top); + immRectf(pos, + SEQ_time_left_handle_frame_get(seq), + stripe_bot, + SEQ_time_right_handle_frame_get(seq), + stripe_top); } stripe_top = seq->machine + SEQ_STRIP_OFSTOP - stripe_ofs_y; @@ -2625,7 +2649,11 @@ static void draw_cache_view(const bContext *C) if (scene->ed->cache_flag & SEQ_CACHE_VIEW_COMPOSITE) { const float bg_color[4] = {1.0f, 0.6f, 0.0f, 0.1f}; immUniformColor4f(bg_color[0], bg_color[1], bg_color[2], bg_color[3]); - immRectf(pos, seq->startdisp, stripe_bot, seq->enddisp, stripe_top); + immRectf(pos, + SEQ_time_left_handle_frame_get(seq), + stripe_bot, + SEQ_time_right_handle_frame_get(seq), + stripe_top); } } diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 0305ad279a0..86c438c616e 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -57,6 +57,7 @@ #include "ED_keyframing.h" #include "ED_numinput.h" #include "ED_outliner.h" +#include "ED_scene.h" #include "ED_screen.h" #include "ED_sequencer.h" @@ -76,8 +77,6 @@ typedef struct TransSeq { int start, machine; - int startstill, endstill; - int startdisp, enddisp; int startofs, endofs; int anim_startofs, anim_endofs; /* int final_left, final_right; */ /* UNUSED */ @@ -345,7 +344,6 @@ static int sequencer_snap_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); Editing *ed = SEQ_editing_get(scene); - ListBase *seqbase = SEQ_active_seqbase_get(ed); ListBase *channels = SEQ_channels_displayed_get(ed); Sequence *seq; int snap_frame; @@ -357,20 +355,19 @@ static int sequencer_snap_exec(bContext *C, wmOperator *op) if (seq->flag & SELECT && !SEQ_transform_is_locked(channels, seq) && SEQ_transform_sequence_can_be_translated(seq)) { if ((seq->flag & (SEQ_LEFTSEL + SEQ_RIGHTSEL)) == 0) { - SEQ_transform_translate_sequence( - scene, seq, (snap_frame - seq->startofs + seq->startstill) - seq->start); + SEQ_transform_translate_sequence(scene, seq, (snap_frame - seq->startofs) - seq->start); } else { if (seq->flag & SEQ_LEFTSEL) { - SEQ_transform_set_left_handle_frame(seq, snap_frame); + SEQ_time_left_handle_frame_set(scene, seq, snap_frame); } else { /* SEQ_RIGHTSEL */ - SEQ_transform_set_right_handle_frame(seq, snap_frame); + SEQ_time_right_handle_frame_set(scene, seq, snap_frame); } - SEQ_transform_handle_xlimits(seq, seq->flag & SEQ_LEFTSEL, seq->flag & SEQ_RIGHTSEL); - SEQ_transform_fix_single_image_seq_offsets(seq); + SEQ_transform_handle_xlimits( + scene, seq, seq->flag & SEQ_LEFTSEL, seq->flag & SEQ_RIGHTSEL); + SEQ_transform_fix_single_image_seq_offsets(scene, seq); } - SEQ_time_update_sequence(scene, seqbase, seq); } } @@ -391,27 +388,22 @@ static int sequencer_snap_exec(bContext *C, wmOperator *op) if (seq->seq1 && (seq->seq1->flag & SELECT)) { if (!either_handle_selected) { - SEQ_offset_animdata(scene, seq, (snap_frame - seq->startdisp)); + SEQ_offset_animdata(scene, seq, (snap_frame - SEQ_time_left_handle_frame_get(seq))); } - SEQ_time_update_sequence(scene, seqbase, seq); } else if (seq->seq2 && (seq->seq2->flag & SELECT)) { if (!either_handle_selected) { - SEQ_offset_animdata(scene, seq, (snap_frame - seq->startdisp)); + SEQ_offset_animdata(scene, seq, (snap_frame - SEQ_time_left_handle_frame_get(seq))); } - SEQ_time_update_sequence(scene, seqbase, seq); } else if (seq->seq3 && (seq->seq3->flag & SELECT)) { if (!either_handle_selected) { - SEQ_offset_animdata(scene, seq, (snap_frame - seq->startdisp)); + SEQ_offset_animdata(scene, seq, (snap_frame - SEQ_time_left_handle_frame_get(seq))); } - SEQ_time_update_sequence(scene, seqbase, seq); } } } - SEQ_sort(SEQ_active_seqbase_get(ed)); - DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); @@ -478,10 +470,6 @@ static void transseq_backup(TransSeq *ts, Sequence *seq) { ts->start = seq->start; ts->machine = seq->machine; - ts->startstill = seq->startstill; - ts->endstill = seq->endstill; - ts->startdisp = seq->startdisp; - ts->enddisp = seq->enddisp; ts->startofs = seq->startofs; ts->endofs = seq->endofs; ts->anim_startofs = seq->anim_startofs; @@ -493,10 +481,6 @@ static void transseq_restore(TransSeq *ts, Sequence *seq) { seq->start = ts->start; seq->machine = ts->machine; - seq->startstill = ts->startstill; - seq->endstill = ts->endstill; - seq->startdisp = ts->startdisp; - seq->enddisp = ts->enddisp; seq->startofs = ts->startofs; seq->endofs = ts->endofs; seq->anim_startofs = ts->anim_startofs; @@ -595,69 +579,22 @@ static int sequencer_slip_invoke(bContext *C, wmOperator *op, const wmEvent *eve return OPERATOR_RUNNING_MODAL; } -static bool sequencer_slip_recursively(Scene *scene, SlipData *data, int offset) +static void sequencer_slip_recursively(Scene *scene, SlipData *data, int offset) { - /* Only data types supported for now. */ - bool changed = false; - - /* Iterate in reverse so meta-strips are iterated after their children. */ for (int i = data->num_seq - 1; i >= 0; i--) { Sequence *seq = data->seq_array[i]; - int endframe; - /* Offset seq start. */ seq->start = data->ts[i].start + offset; - if (data->trim[i]) { - /* Find the end-frame. */ - endframe = seq->start + seq->len; - - /* Compute the sequence offsets. */ - if (endframe > seq->enddisp) { - seq->endstill = 0; - seq->endofs = endframe - seq->enddisp; - changed = true; - } - else { - seq->endstill = seq->enddisp - endframe; - seq->endofs = 0; - changed = true; - } - - if (seq->start > seq->startdisp) { - seq->startstill = seq->start - seq->startdisp; - seq->startofs = 0; - changed = true; - } - else { - seq->startstill = 0; - seq->startofs = seq->startdisp - seq->start; - changed = true; - } - } - else { - /* No transform data (likely effect strip). Only move start and end. */ - seq->startdisp = data->ts[i].startdisp + offset; - seq->enddisp = data->ts[i].enddisp + offset; - changed = true; - } - - /* Effects are only added if we they are in a meta-strip. - * In this case, dependent strips will just be transformed and - * we can skip calculating for effects. - * This way we can avoid an extra loop just for effects. */ - if (!(seq->type & SEQ_TYPE_EFFECT)) { - ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene)); - SEQ_time_update_sequence(scene, seqbase, seq); + seq->startofs = data->ts[i].startofs - offset; + seq->endofs = data->ts[i].endofs + offset; } } - if (changed) { - for (int i = data->num_seq - 1; i >= 0; i--) { - Sequence *seq = data->seq_array[i]; - SEQ_relations_invalidate_cache_preprocessed(scene, seq); - } + + for (int i = data->num_seq - 1; i >= 0; i--) { + Sequence *seq = data->seq_array[i]; + SEQ_relations_invalidate_cache_preprocessed(scene, seq); } - return changed; } /* Make sure, that each strip contains at least 1 frame of content. */ @@ -670,12 +607,12 @@ static void sequencer_slip_apply_limits(SlipData *data, int *offset) int seq_content_end = seq_content_start + seq->len + seq->anim_startofs + seq->anim_endofs; int diff = 0; - if (seq_content_start >= seq->enddisp) { - diff = seq->enddisp - seq_content_start - 1; + if (seq_content_start >= SEQ_time_right_handle_frame_get(seq)) { + diff = SEQ_time_right_handle_frame_get(seq) - seq_content_start - 1; } - if (seq_content_end <= seq->startdisp) { - diff = seq->startdisp - seq_content_end + 1; + if (seq_content_end <= SEQ_time_left_handle_frame_get(seq)) { + diff = SEQ_time_left_handle_frame_get(seq) - seq_content_end + 1; } *offset += diff; } @@ -687,7 +624,6 @@ static int sequencer_slip_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); Editing *ed = SEQ_editing_get(scene); int offset = RNA_int_get(op->ptr, "offset"); - bool success = false; /* Recursively count the trimmed elements. */ int num_seq = slip_count_sequences_recursive(ed->seqbasep, true); @@ -709,19 +645,16 @@ static int sequencer_slip_exec(bContext *C, wmOperator *op) } sequencer_slip_apply_limits(data, &offset); - success = sequencer_slip_recursively(scene, data, offset); + sequencer_slip_recursively(scene, data, offset); MEM_freeN(data->seq_array); MEM_freeN(data->trim); MEM_freeN(data->ts); MEM_freeN(data); - if (success) { - WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); - DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); - return OPERATOR_FINISHED; - } - return OPERATOR_CANCELLED; + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); + DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); + return OPERATOR_FINISHED; } static void sequencer_slip_update_header(Scene *scene, ScrArea *area, SlipData *data, int offset) @@ -762,9 +695,8 @@ static int sequencer_slip_modal(bContext *C, wmOperator *op, const wmEvent *even RNA_int_set(op->ptr, "offset", offset); - if (sequencer_slip_recursively(scene, data, offset)) { - WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); - } + sequencer_slip_recursively(scene, data, offset); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); return OPERATOR_RUNNING_MODAL; } @@ -795,9 +727,8 @@ static int sequencer_slip_modal(bContext *C, wmOperator *op, const wmEvent *even RNA_int_set(op->ptr, "offset", offset); - if (sequencer_slip_recursively(scene, data, offset)) { - WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); - } + sequencer_slip_recursively(scene, data, offset); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); } break; } @@ -827,8 +758,6 @@ static int sequencer_slip_modal(bContext *C, wmOperator *op, const wmEvent *even for (int i = 0; i < data->num_seq; i++) { Sequence *seq = data->seq_array[i]; SEQ_add_reload_new_file(bmain, scene, seq, false); - ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene)); - SEQ_time_update_sequence(scene, seqbase, seq); } MEM_freeN(data->seq_array); @@ -875,9 +804,8 @@ static int sequencer_slip_modal(bContext *C, wmOperator *op, const wmEvent *even RNA_int_set(op->ptr, "offset", offset); - if (sequencer_slip_recursively(scene, data, offset)) { - WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); - } + sequencer_slip_recursively(scene, data, offset); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); } return OPERATOR_RUNNING_MODAL; @@ -1325,7 +1253,6 @@ static int sequencer_reassign_inputs_exec(bContext *C, wmOperator *op) last_seq->seq3 = seq3; int old_start = last_seq->start; - SEQ_time_update_recursive(scene, last_seq); SEQ_relations_invalidate_cache_preprocessed(scene, last_seq); SEQ_offset_animdata(scene, last_seq, (last_seq->start - old_start)); @@ -1483,13 +1410,15 @@ static int sequencer_split_exec(bContext *C, wmOperator *op) if (ignore_selection) { if (use_cursor_position) { LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { - if (seq->enddisp == split_frame && seq->machine == split_channel) { + if (SEQ_time_right_handle_frame_get(seq) == split_frame && + seq->machine == split_channel) { seq_selected = seq->flag & SEQ_ALLSEL; } } if (!seq_selected) { LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { - if (seq->startdisp == split_frame && seq->machine == split_channel) { + if (SEQ_time_left_handle_frame_get(seq) == split_frame && + seq->machine == split_channel) { seq->flag &= ~SEQ_ALLSEL; } } @@ -1500,20 +1429,18 @@ static int sequencer_split_exec(bContext *C, wmOperator *op) if (split_side != SEQ_SIDE_BOTH) { LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { if (split_side == SEQ_SIDE_LEFT) { - if (seq->startdisp >= split_frame) { + if (SEQ_time_left_handle_frame_get(seq) >= split_frame) { seq->flag &= ~SEQ_ALLSEL; } } else { - if (seq->enddisp <= split_frame) { + if (SEQ_time_right_handle_frame_get(seq) <= split_frame) { seq->flag &= ~SEQ_ALLSEL; } } } } } - - SEQ_sort(SEQ_active_seqbase_get(ed)); } if (changed) { WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); @@ -1719,11 +1646,26 @@ void SEQUENCER_OT_duplicate(wmOperatorType *ot) /** \name Erase Strips Operator * \{ */ -static int sequencer_delete_exec(bContext *C, wmOperator *UNUSED(op)) +static void sequencer_delete_strip_data(bContext *C, Sequence *seq) +{ + if (seq->type != SEQ_TYPE_SCENE) { + return; + } + + Main *bmain = CTX_data_main(C); + if (seq->scene) { + if (ED_scene_delete(C, bmain, seq->scene)) { + WM_event_add_notifier(C, NC_SCENE | NA_REMOVED, seq->scene); + } + } +} + +static int sequencer_delete_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); ListBase *seqbasep = SEQ_active_seqbase_get(SEQ_editing_get(scene)); + const bool delete_data = RNA_boolean_get(op->ptr, "delete_data"); if (sequencer_view_has_preview_poll(C) && !sequencer_view_preview_only_poll(C)) { return OPERATOR_CANCELLED; @@ -1736,6 +1678,9 @@ static int sequencer_delete_exec(bContext *C, wmOperator *UNUSED(op)) SEQ_ITERATOR_FOREACH (seq, selected_strips) { SEQ_edit_flag_for_removal(scene, seqbasep, seq); + if (delete_data) { + sequencer_delete_strip_data(C, seq); + } } SEQ_edit_remove_flagged_sequences(scene, seqbasep); @@ -1778,6 +1723,14 @@ void SEQUENCER_OT_delete(wmOperatorType *ot) /* Flags. */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* Properties. */ + ot->prop = RNA_def_boolean(ot->srna, + "delete_data", + false, + "Delete Data", + "After removing the Strip, delete the associated data also"); + RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE); } /** \} */ @@ -1795,15 +1748,13 @@ static int sequencer_offset_clear_exec(bContext *C, wmOperator *UNUSED(op)) /* For effects, try to find a replacement input. */ for (seq = ed->seqbasep->first; seq; seq = seq->next) { if ((seq->type & SEQ_TYPE_EFFECT) == 0 && (seq->flag & SELECT)) { - seq->startofs = seq->endofs = seq->startstill = seq->endstill = 0; + seq->startofs = seq->endofs = 0; } } /* Update lengths, etc. */ seq = ed->seqbasep->first; while (seq) { - ListBase *seqbase = SEQ_active_seqbase_get(ed); - SEQ_time_update_sequence(scene, seqbase, seq); SEQ_relations_invalidate_cache_preprocessed(scene, seq); seq = seq->next; } @@ -1869,8 +1820,8 @@ static int sequencer_separate_images_exec(bContext *C, wmOperator *op) /* TODO: remove f-curve and assign to split image strips. * The old animation system would remove the user of `seq->ipo`. */ - start_ofs = timeline_frame = SEQ_transform_get_left_handle_frame(seq); - frame_end = SEQ_transform_get_right_handle_frame(seq); + start_ofs = timeline_frame = SEQ_time_left_handle_frame_get(seq); + frame_end = SEQ_time_right_handle_frame_get(seq); while (timeline_frame < frame_end) { /* New seq. */ @@ -1881,7 +1832,7 @@ static int sequencer_separate_images_exec(bContext *C, wmOperator *op) seq_new->start = start_ofs; seq_new->type = SEQ_TYPE_IMAGE; seq_new->len = 1; - seq_new->endstill = step - 1; + seq_new->endofs = 1 - step; /* New strip. */ strip_new = seq_new->strip; @@ -1894,8 +1845,6 @@ static int sequencer_separate_images_exec(bContext *C, wmOperator *op) BLI_strncpy(se_new->name, se->name, sizeof(se_new->name)); strip_new->stripdata = se_new; - SEQ_time_update_sequence(scene, seqbase, seq_new); - if (step > 1) { seq_new->flag &= ~SEQ_OVERLAP; if (SEQ_transform_test_overlap(seqbase, seq_new)) { @@ -1919,9 +1868,6 @@ static int sequencer_separate_images_exec(bContext *C, wmOperator *op) } SEQ_edit_remove_flagged_sequences(scene, seqbase); - - SEQ_sort(seqbase); - WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); return OPERATOR_FINISHED; @@ -2031,8 +1977,8 @@ static int sequencer_meta_make_exec(bContext *C, wmOperator *op) BLI_addtail(&seqm->seqbase, seq); SEQ_relations_invalidate_cache_preprocessed(scene, seq); channel_max = max_ii(seq->machine, channel_max); - meta_start_frame = min_ii(seq->startdisp, meta_start_frame); - meta_end_frame = max_ii(seq->enddisp, meta_end_frame); + meta_start_frame = min_ii(SEQ_time_left_handle_frame_get(seq), meta_start_frame); + meta_end_frame = max_ii(SEQ_time_right_handle_frame_get(seq), meta_end_frame); } } @@ -2041,7 +1987,6 @@ static int sequencer_meta_make_exec(bContext *C, wmOperator *op) SEQ_sequence_base_unique_name_recursive(scene, &ed->seqbase, seqm); seqm->start = meta_start_frame; seqm->len = meta_end_frame - meta_start_frame; - SEQ_time_update_sequence(scene, active_seqbase, seqm); SEQ_select_active_set(scene, seqm); if (SEQ_transform_test_overlap(active_seqbase, seqm)) { SEQ_transform_seqbase_shuffle(active_seqbase, seqm, scene); @@ -2109,7 +2054,6 @@ static int sequencer_meta_separate_exec(bContext *C, wmOperator *UNUSED(op)) } } - SEQ_sort(active_seqbase); DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); @@ -2215,19 +2159,18 @@ static const EnumPropertyItem prop_side_lr_types[] = { static void swap_sequence(Scene *scene, Sequence *seqa, Sequence *seqb) { - ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene)); - int gap = seqb->startdisp - seqa->enddisp; + int gap = SEQ_time_left_handle_frame_get(seqb) - SEQ_time_right_handle_frame_get(seqa); int seq_a_start; int seq_b_start; - seq_b_start = (seqb->start - seqb->startdisp) + seqa->startdisp; + seq_b_start = (seqb->start - SEQ_time_left_handle_frame_get(seqb)) + + SEQ_time_left_handle_frame_get(seqa); SEQ_transform_translate_sequence(scene, seqb, seq_b_start - seqb->start); - SEQ_time_update_sequence(scene, seqbase, seqb); SEQ_relations_invalidate_cache_preprocessed(scene, seqb); - seq_a_start = (seqa->start - seqa->startdisp) + seqb->enddisp + gap; + seq_a_start = (seqa->start - SEQ_time_left_handle_frame_get(seqa)) + + SEQ_time_right_handle_frame_get(seqb) + gap; SEQ_transform_translate_sequence(scene, seqa, seq_a_start - seqa->start); - SEQ_time_update_sequence(scene, seqbase, seqa); SEQ_relations_invalidate_cache_preprocessed(scene, seqa); } @@ -2252,13 +2195,13 @@ static Sequence *find_next_prev_sequence(Scene *scene, Sequence *test, int lr, i switch (lr) { case SEQ_SIDE_LEFT: - if (seq->enddisp <= test->startdisp) { - dist = test->enddisp - seq->startdisp; + if (SEQ_time_right_handle_frame_get(seq) <= SEQ_time_left_handle_frame_get(test)) { + dist = SEQ_time_right_handle_frame_get(test) - SEQ_time_left_handle_frame_get(seq); } break; case SEQ_SIDE_RIGHT: - if (seq->startdisp >= test->enddisp) { - dist = seq->startdisp - test->enddisp; + if (SEQ_time_left_handle_frame_get(seq) >= SEQ_time_right_handle_frame_get(test)) { + dist = SEQ_time_left_handle_frame_get(seq) - SEQ_time_right_handle_frame_get(test); } break; } @@ -2318,14 +2261,6 @@ static int sequencer_swap_exec(bContext *C, wmOperator *op) break; } - /* XXX: Should be a generic function. */ - for (iseq = seqbase->first; iseq; iseq = iseq->next) { - if ((iseq->type & SEQ_TYPE_EFFECT) && - (seq_is_parent(iseq, active_seq) || seq_is_parent(iseq, seq))) { - SEQ_time_update_sequence(scene, seqbase, iseq); - } - } - /* Do this in a new loop since both effects need to be calculated first. */ for (iseq = seqbase->first; iseq; iseq = iseq->next) { if ((iseq->type & SEQ_TYPE_EFFECT) && @@ -2337,10 +2272,7 @@ static int sequencer_swap_exec(bContext *C, wmOperator *op) } } - SEQ_sort(SEQ_active_seqbase_get(ed)); - WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); - return OPERATOR_FINISHED; } @@ -2457,6 +2389,13 @@ static void sequencer_copy_animation(Scene *scene, Sequence *seq) return; } + /* Add curves for strips inside meta strip. */ + if (seq->type == SEQ_TYPE_META) { + LISTBASE_FOREACH (Sequence *, meta_child, &seq->seqbase) { + sequencer_copy_animation(scene, meta_child); + } + } + GSet *fcurves = SEQ_fcurves_by_strip_get(seq, &scene->adt->action->curves); if (fcurves == NULL) { return; @@ -2466,6 +2405,7 @@ static void sequencer_copy_animation(Scene *scene, Sequence *seq) BLI_addtail(&fcurves_clipboard, BKE_fcurve_copy(fcu)); } GSET_FOREACH_END(); + BLI_gset_free(fcurves, NULL); } @@ -2587,8 +2527,8 @@ static int sequencer_paste_exec(bContext *C, wmOperator *op) else { int min_seq_startdisp = INT_MAX; LISTBASE_FOREACH (Sequence *, seq, &seqbase_clipboard) { - if (seq->startdisp < min_seq_startdisp) { - min_seq_startdisp = seq->startdisp; + if (SEQ_time_left_handle_frame_get(seq) < min_seq_startdisp) { + min_seq_startdisp = SEQ_time_left_handle_frame_get(seq); } } /* Paste strips relative to the current-frame. */ @@ -2596,7 +2536,7 @@ static int sequencer_paste_exec(bContext *C, wmOperator *op) } /* Paste animation. - * Note: Only fcurves are copied. Drivers and NLA action strips are not copied. + * NOTE: Only fcurves are copied. Drivers and NLA action strips are not copied. * First backup original curves from scene and move curves from clipboard into scene. This way, * when pasted strips are renamed, pasted fcurves are renamed with them. Finally restore original * curves from backup. @@ -2619,13 +2559,17 @@ static int sequencer_paste_exec(bContext *C, wmOperator *op) * in the new list. */ BLI_movelisttolist(ed->seqbasep, &nseqbase); + /* Make sure, that pasted strips have unique names. This has to be done immediately after adding + * strips to seqbase, for lookup cache to work correctly. */ + for (iseq = iseq_first; iseq; iseq = iseq->next) { + SEQ_ensure_unique_name(iseq, scene); + } + for (iseq = iseq_first; iseq; iseq = iseq->next) { if (SEQ_clipboard_pasted_seq_was_active(iseq)) { SEQ_select_active_set(scene, iseq); } - /* Make sure, that pasted strips have unique names. */ - SEQ_ensure_unique_name(iseq, scene); /* Translate after name has been changed, otherwise this will affect animdata of original * strip. */ SEQ_transform_translate_sequence(scene, iseq, ofs); @@ -2703,10 +2647,6 @@ static int sequencer_swap_data_exec(bContext *C, wmOperator *op) seq_act->scene_sound = NULL; seq_other->scene_sound = NULL; - ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene)); - SEQ_time_update_sequence(scene, seqbase, seq_act); - SEQ_time_update_sequence(scene, seqbase, seq_other); - if (seq_act->sound) { BKE_sound_add_scene_sound_defaults(scene, seq_act); } @@ -2949,9 +2889,6 @@ static int sequencer_change_path_exec(bContext *C, wmOperator *op) /* Correct start/end frames so we don't move. * Important not to set seq->len = len; allow the function to handle it. */ SEQ_add_reload_new_file(bmain, scene, seq, true); - - ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene)); - SEQ_time_update_sequence(scene, seqbase, seq); } else if (ELEM(seq->type, SEQ_TYPE_SOUND_RAM, SEQ_TYPE_SOUND_HD)) { bSound *sound = seq->sound; @@ -3040,6 +2977,81 @@ void SEQUENCER_OT_change_path(struct wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Change Strip Scene Operator + * \{ */ + +static bool sequencer_strip_change_scene_poll(bContext *C) +{ + Editing *ed = SEQ_editing_get(CTX_data_scene(C)); + if (ed == NULL) { + return false; + } + Sequence *seq = ed->act_seq; + return ((seq != NULL) && (seq->type == SEQ_TYPE_SCENE)); +} +static int sequencer_change_scene_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + Scene *scene_seq = BLI_findlink(&bmain->scenes, RNA_enum_get(op->ptr, "scene")); + + if (scene_seq == NULL) { + BKE_report(op->reports, RPT_ERROR, "Scene not found"); + return OPERATOR_CANCELLED; + } + + /* Assign new scene. */ + Sequence *seq = SEQ_select_active_get(scene); + if (seq) { + seq->scene = scene_seq; + /* Do a refresh of the sequencer data. */ + SEQ_relations_invalidate_cache_raw(scene, seq); + DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO | ID_RECALC_SEQUENCER_STRIPS); + DEG_relations_tag_update(bmain); + } + + WM_event_add_notifier(C, NC_SCENE | ND_SCENEBROWSE, scene); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); + + return OPERATOR_FINISHED; +} + +static int sequencer_change_scene_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (!RNA_struct_property_is_set(op->ptr, "scene")) { + return WM_enum_search_invoke(C, op, event); + } + + return sequencer_change_scene_exec(C, op); +} + +void SEQUENCER_OT_change_scene(struct wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* Identifiers. */ + ot->name = "Change Scene"; + ot->idname = "SEQUENCER_OT_change_scene"; + ot->description = "Change Scene assigned to Strip"; + + /* Api callbacks. */ + ot->exec = sequencer_change_scene_exec; + ot->invoke = sequencer_change_scene_invoke; + ot->poll = sequencer_strip_change_scene_poll; + + /* Flags. */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* Properties. */ + prop = RNA_def_enum(ot->srna, "scene", DummyRNA_NULL_items, 0, "Scene", ""); + RNA_def_enum_funcs(prop, RNA_scene_without_active_itemf); + RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); + ot->prop = prop; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Export Subtitles Operator * \{ */ @@ -3049,8 +3061,8 @@ static int seq_cmp_time_startdisp_channel(const void *a, const void *b) Sequence *seq_a = (Sequence *)a; Sequence *seq_b = (Sequence *)b; - int seq_a_start = SEQ_transform_get_left_handle_frame(seq_a); - int seq_b_start = SEQ_transform_get_left_handle_frame(seq_b); + int seq_a_start = SEQ_time_left_handle_frame_get(seq_a); + int seq_b_start = SEQ_time_left_handle_frame_get(seq_b); /* If strips have the same start frame favor the one with a higher channel. */ if (seq_a_start == seq_b_start) { @@ -3096,7 +3108,7 @@ static bool seq_get_text_strip_cb(Sequence *seq, void *user_data) ListBase *channels = SEQ_channels_displayed_get(ed); /* Only text strips that are not muted and don't end with negative frame. */ if ((seq->type == SEQ_TYPE_TEXT) && !SEQ_render_is_muted(channels, seq) && - (seq->enddisp > cd->scene->r.sfra)) { + (SEQ_time_right_handle_frame_get(seq) > cd->scene->r.sfra)) { BLI_addtail(cd->text_seq, MEM_dupallocN(seq)); } return true; @@ -3154,16 +3166,17 @@ static int sequencer_export_subtitles_exec(bContext *C, wmOperator *op) char timecode_str_end[32]; /* Write time-code relative to start frame of scene. Don't allow negative time-codes. */ - BLI_timecode_string_from_time(timecode_str_start, - sizeof(timecode_str_start), - -2, - FRA2TIME(max_ii(seq->startdisp - scene->r.sfra, 0)), - FPS, - USER_TIMECODE_SUBRIP); + BLI_timecode_string_from_time( + timecode_str_start, + sizeof(timecode_str_start), + -2, + FRA2TIME(max_ii(SEQ_time_left_handle_frame_get(seq) - scene->r.sfra, 0)), + FPS, + USER_TIMECODE_SUBRIP); BLI_timecode_string_from_time(timecode_str_end, sizeof(timecode_str_end), -2, - FRA2TIME(seq->enddisp - scene->r.sfra), + FRA2TIME(SEQ_time_right_handle_frame_get(seq) - scene->r.sfra), FPS, USER_TIMECODE_SUBRIP); @@ -3231,8 +3244,8 @@ static int sequencer_set_range_to_strips_exec(bContext *C, wmOperator *op) for (seq = ed->seqbasep->first; seq; seq = seq->next) { if (seq->flag & SELECT) { selected = true; - sfra = min_ii(sfra, seq->startdisp); - efra = max_ii(efra, seq->enddisp - 1); + sfra = min_ii(sfra, SEQ_time_left_handle_frame_get(seq)); + efra = max_ii(efra, SEQ_time_right_handle_frame_get(seq) - 1); } } diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h index 67df065ef35..3307c3fde2f 100644 --- a/source/blender/editors/space_sequencer/sequencer_intern.h +++ b/source/blender/editors/space_sequencer/sequencer_intern.h @@ -197,6 +197,7 @@ void SEQUENCER_OT_rendersize(struct wmOperatorType *ot); void SEQUENCER_OT_change_effect_input(struct wmOperatorType *ot); void SEQUENCER_OT_change_effect_type(struct wmOperatorType *ot); void SEQUENCER_OT_change_path(struct wmOperatorType *ot); +void SEQUENCER_OT_change_scene(struct wmOperatorType *ot); void SEQUENCER_OT_copy(struct wmOperatorType *ot); void SEQUENCER_OT_paste(struct wmOperatorType *ot); @@ -231,6 +232,7 @@ void SEQUENCER_OT_select_grouped(struct wmOperatorType *ot); /* sequencer_add.c */ void SEQUENCER_OT_scene_strip_add(struct wmOperatorType *ot); +void SEQUENCER_OT_scene_strip_add_new(struct wmOperatorType *ot); void SEQUENCER_OT_movie_strip_add(struct wmOperatorType *ot); void SEQUENCER_OT_movieclip_strip_add(struct wmOperatorType *ot); void SEQUENCER_OT_mask_strip_add(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_sequencer/sequencer_ops.c b/source/blender/editors/space_sequencer/sequencer_ops.c index 1aa2991f07a..f7a9bcf41e6 100644 --- a/source/blender/editors/space_sequencer/sequencer_ops.c +++ b/source/blender/editors/space_sequencer/sequencer_ops.c @@ -58,6 +58,7 @@ void sequencer_operatortypes(void) WM_operatortype_append(SEQUENCER_OT_change_effect_input); WM_operatortype_append(SEQUENCER_OT_change_effect_type); WM_operatortype_append(SEQUENCER_OT_change_path); + WM_operatortype_append(SEQUENCER_OT_change_scene); WM_operatortype_append(SEQUENCER_OT_set_range_to_strips); WM_operatortype_append(SEQUENCER_OT_strip_transform_clear); @@ -81,6 +82,7 @@ void sequencer_operatortypes(void) /* sequencer_add.c */ WM_operatortype_append(SEQUENCER_OT_scene_strip_add); + WM_operatortype_append(SEQUENCER_OT_scene_strip_add_new); WM_operatortype_append(SEQUENCER_OT_movieclip_strip_add); WM_operatortype_append(SEQUENCER_OT_mask_strip_add); WM_operatortype_append(SEQUENCER_OT_movie_strip_add); diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index 95707f83d85..f237fbc0a12 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -116,13 +116,13 @@ static void select_active_side(ListBase *seqbase, int sel_side, int channel, int if (channel == seq->machine) { switch (sel_side) { case SEQ_SIDE_LEFT: - if (frame > (seq->startdisp)) { + if (frame > (SEQ_time_left_handle_frame_get(seq))) { seq->flag &= ~(SEQ_RIGHTSEL | SEQ_LEFTSEL); seq->flag |= SELECT; } break; case SEQ_SIDE_RIGHT: - if (frame < (seq->startdisp)) { + if (frame < (SEQ_time_left_handle_frame_get(seq))) { seq->flag &= ~(SEQ_RIGHTSEL | SEQ_LEFTSEL); seq->flag |= SELECT; } @@ -152,13 +152,13 @@ static void select_active_side_range(ListBase *seqbase, } switch (sel_side) { case SEQ_SIDE_LEFT: - if (frame > (seq->startdisp)) { + if (frame > (SEQ_time_left_handle_frame_get(seq))) { seq->flag &= ~(SEQ_RIGHTSEL | SEQ_LEFTSEL); seq->flag |= SELECT; } break; case SEQ_SIDE_RIGHT: - if (frame < (seq->startdisp)) { + if (frame < (SEQ_time_left_handle_frame_get(seq))) { seq->flag &= ~(SEQ_RIGHTSEL | SEQ_LEFTSEL); seq->flag |= SELECT; } @@ -179,8 +179,8 @@ static void select_linked_time(ListBase *seqbase, Sequence *seq_link) for (seq = seqbase->first; seq; seq = seq->next) { if (seq_link->machine != seq->machine) { - int left_match = (seq->startdisp == seq_link->startdisp) ? 1 : 0; - int right_match = (seq->enddisp == seq_link->enddisp) ? 1 : 0; + int left_match = (SEQ_time_left_handle_frame_get(seq) == seq_link->startdisp) ? 1 : 0; + int right_match = (SEQ_time_right_handle_frame_get(seq) == seq_link->enddisp) ? 1 : 0; if (left_match && right_match) { /* Direct match, copy the selection settings. */ @@ -247,8 +247,8 @@ void ED_sequencer_select_sequence_single(Scene *scene, Sequence *seq, bool desel void seq_rectf(Sequence *seq, rctf *rect) { - rect->xmin = seq->startdisp; - rect->xmax = seq->enddisp; + rect->xmin = SEQ_time_left_handle_frame_get(seq); + rect->xmax = SEQ_time_right_handle_frame_get(seq); rect->ymin = seq->machine + SEQ_STRIP_OFSBOTTOM; rect->ymax = seq->machine + SEQ_STRIP_OFSTOP; } @@ -273,12 +273,12 @@ Sequence *find_neighboring_sequence(Scene *scene, Sequence *test, int lr, int se (sel == 0 && (seq->flag & SELECT) == 0))) { switch (lr) { case SEQ_SIDE_LEFT: - if (test->startdisp == (seq->enddisp)) { + if (SEQ_time_left_handle_frame_get(test) == (SEQ_time_right_handle_frame_get(seq))) { return seq; } break; case SEQ_SIDE_RIGHT: - if (test->enddisp == (seq->startdisp)) { + if (SEQ_time_right_handle_frame_get(test) == (SEQ_time_left_handle_frame_get(seq))) { return seq; } break; @@ -311,13 +311,18 @@ Sequence *find_nearest_seq(Scene *scene, View2D *v2d, int *hand, const int mval[ while (seq) { if (seq->machine == (int)y) { /* Check for both normal strips, and strips that have been flipped horizontally. */ - if (((seq->startdisp < seq->enddisp) && (seq->startdisp <= x && seq->enddisp >= x)) || - ((seq->startdisp > seq->enddisp) && (seq->startdisp >= x && seq->enddisp <= x))) { + if (((SEQ_time_left_handle_frame_get(seq) < SEQ_time_right_handle_frame_get(seq)) && + (SEQ_time_left_handle_frame_get(seq) <= x && + SEQ_time_right_handle_frame_get(seq) >= x)) || + ((SEQ_time_left_handle_frame_get(seq) > SEQ_time_right_handle_frame_get(seq)) && + (SEQ_time_left_handle_frame_get(seq) >= x && + SEQ_time_right_handle_frame_get(seq) <= x))) { if (SEQ_transform_sequence_can_be_translated(seq)) { /* Clamp handles to defined size in pixel space. */ handsize = 2.0f * sequence_handle_size_get_clamped(seq, pixelx); - displen = (float)abs(seq->startdisp - seq->enddisp); + displen = (float)abs(SEQ_time_left_handle_frame_get(seq) - + SEQ_time_right_handle_frame_get(seq)); /* Don't even try to grab the handles of small strips. */ if (displen / pixelx > 16) { @@ -332,10 +337,10 @@ Sequence *find_nearest_seq(Scene *scene, View2D *v2d, int *hand, const int mval[ CLAMP(handsize, 7 * pixelx, 30 * pixelx); } - if (handsize + seq->startdisp >= x) { + if (handsize + SEQ_time_left_handle_frame_get(seq) >= x) { *hand = SEQ_SIDE_LEFT; } - else if (-handsize + seq->enddisp <= x) { + else if (-handsize + SEQ_time_right_handle_frame_get(seq) <= x) { *hand = SEQ_SIDE_RIGHT; } } @@ -578,8 +583,8 @@ static void sequencer_select_side_of_frame(const bContext *C, const float x = UI_view2d_region_to_view_x(v2d, mval[0]); LISTBASE_FOREACH (Sequence *, seq_iter, SEQ_active_seqbase_get(ed)) { - if (((x < CFRA) && (seq_iter->enddisp <= CFRA)) || - ((x >= CFRA) && (seq_iter->startdisp >= CFRA))) { + if (((x < CFRA) && (SEQ_time_right_handle_frame_get(seq_iter) <= CFRA)) || + ((x >= CFRA) && (SEQ_time_left_handle_frame_get(seq_iter) >= CFRA))) { /* Select left or right. */ seq_iter->flag |= SELECT; recurs_sel_seq(seq_iter); @@ -634,7 +639,8 @@ static void sequencer_select_linked_handle(const bContext *C, case SEQ_SIDE_LEFT: if ((seq->flag & SEQ_LEFTSEL) && (neighbor->flag & SEQ_RIGHTSEL)) { seq->flag |= SELECT; - select_active_side(ed->seqbasep, SEQ_SIDE_LEFT, seq->machine, seq->startdisp); + select_active_side( + ed->seqbasep, SEQ_SIDE_LEFT, seq->machine, SEQ_time_left_handle_frame_get(seq)); } else { seq->flag |= SELECT; @@ -647,7 +653,8 @@ static void sequencer_select_linked_handle(const bContext *C, case SEQ_SIDE_RIGHT: if ((seq->flag & SEQ_RIGHTSEL) && (neighbor->flag & SEQ_LEFTSEL)) { seq->flag |= SELECT; - select_active_side(ed->seqbasep, SEQ_SIDE_RIGHT, seq->machine, seq->startdisp); + select_active_side( + ed->seqbasep, SEQ_SIDE_RIGHT, seq->machine, SEQ_time_left_handle_frame_get(seq)); } else { seq->flag |= SELECT; @@ -661,7 +668,8 @@ static void sequencer_select_linked_handle(const bContext *C, } else { - select_active_side(ed->seqbasep, sel_side, seq->machine, seq->startdisp); + select_active_side( + ed->seqbasep, sel_side, seq->machine, SEQ_time_left_handle_frame_get(seq)); } } } @@ -996,6 +1004,7 @@ void SEQUENCER_OT_select(wmOperatorType *ot) ot->invoke = sequencer_select_invoke; ot->modal = WM_generic_select_modal; ot->poll = ED_operator_sequencer_active; + ot->get_name = ED_select_pick_get_name; /* Flags. */ ot->flag = OPTYPE_UNDO; @@ -1427,10 +1436,10 @@ static int sequencer_select_side_of_frame_exec(bContext *C, wmOperator *op) bool test = false; switch (side) { case -1: - test = (timeline_frame >= seq->enddisp); + test = (timeline_frame >= SEQ_time_right_handle_frame_get(seq)); break; case 1: - test = (timeline_frame <= seq->startdisp); + test = (timeline_frame <= SEQ_time_left_handle_frame_get(seq)); break; case 2: test = SEQ_time_strip_intersects_frame(seq, timeline_frame); @@ -1504,10 +1513,10 @@ static int sequencer_select_side_exec(bContext *C, wmOperator *op) if (seq->flag & SELECT) { selected = true; if (sel_side == SEQ_SIDE_LEFT) { - *frame_limit_p = max_ii(*frame_limit_p, seq->startdisp); + *frame_limit_p = max_ii(*frame_limit_p, SEQ_time_left_handle_frame_get(seq)); } else { - *frame_limit_p = min_ii(*frame_limit_p, seq->startdisp); + *frame_limit_p = min_ii(*frame_limit_p, SEQ_time_left_handle_frame_get(seq)); } } } @@ -1647,7 +1656,7 @@ static int sequencer_box_select_exec(bContext *C, wmOperator *op) float handsize = sequence_handle_size_get_clamped(seq, pixelx); /* Right handle. */ - if (rectf.xmax > (seq->enddisp - handsize)) { + if (rectf.xmax > (SEQ_time_right_handle_frame_get(seq) - handsize)) { if (select) { seq->flag |= SELECT | SEQ_RIGHTSEL; } @@ -1660,7 +1669,7 @@ static int sequencer_box_select_exec(bContext *C, wmOperator *op) } } /* Left handle. */ - if (rectf.xmin < (seq->startdisp + handsize)) { + if (rectf.xmin < (SEQ_time_left_handle_frame_get(seq) + handsize)) { if (select) { seq->flag |= SELECT | SEQ_LEFTSEL; } @@ -1952,7 +1961,8 @@ static bool select_grouped_time_overlap(SeqCollection *strips, Sequence *seq; SEQ_ITERATOR_FOREACH (seq, strips) { - if (seq->startdisp < actseq->enddisp && seq->enddisp > actseq->startdisp) { + if (SEQ_time_left_handle_frame_get(seq) < SEQ_time_right_handle_frame_get(actseq) && + SEQ_time_right_handle_frame_get(seq) > SEQ_time_left_handle_frame_get(actseq)) { seq->flag |= SELECT; changed = true; } @@ -1970,8 +1980,10 @@ static void query_lower_channel_strips(Sequence *seq_reference, if (seq_test->machine > seq_reference->machine) { continue; /* Not lower channel. */ } - if (seq_test->enddisp <= seq_reference->startdisp || - seq_test->startdisp >= seq_reference->enddisp) { + if (SEQ_time_right_handle_frame_get(seq_test) <= + SEQ_time_left_handle_frame_get(seq_reference) || + SEQ_time_left_handle_frame_get(seq_test) >= + SEQ_time_right_handle_frame_get(seq_reference)) { continue; /* Not intersecting in time. */ } SEQ_collection_append_strip(seq_test, collection); diff --git a/source/blender/editors/space_sequencer/sequencer_thumbnails.c b/source/blender/editors/space_sequencer/sequencer_thumbnails.c index 43c5e004040..984d3b1f374 100644 --- a/source/blender/editors/space_sequencer/sequencer_thumbnails.c +++ b/source/blender/editors/space_sequencer/sequencer_thumbnails.c @@ -23,6 +23,7 @@ #include "SEQ_relations.h" #include "SEQ_render.h" #include "SEQ_sequencer.h" +#include "SEQ_time.h" #include "WM_api.h" #include "WM_types.h" @@ -73,10 +74,10 @@ static bool check_seq_need_thumbnails(Sequence *seq, rctf *view_area) if (!ELEM(seq->type, SEQ_TYPE_MOVIE, SEQ_TYPE_IMAGE)) { return false; } - if (min_ii(seq->startdisp, seq->start) > view_area->xmax) { + if (min_ii(SEQ_time_left_handle_frame_get(seq), seq->start) > view_area->xmax) { return false; } - if (max_ii(seq->enddisp, seq->start + seq->len) < view_area->xmin) { + if (max_ii(SEQ_time_right_handle_frame_get(seq), seq->start + seq->len) < view_area->xmin) { return false; } if (seq->machine + 1.0f < view_area->ymin) { @@ -205,7 +206,7 @@ static GHash *sequencer_thumbnail_ghash_init(const bContext *C, View2D *v2d, Edi else { if (val_need_update != NULL) { val_need_update->seq_dupli->start = seq->start; - val_need_update->seq_dupli->startdisp = seq->startdisp; + val_need_update->seq_dupli->startdisp = SEQ_time_left_handle_frame_get(seq); } } } @@ -362,15 +363,16 @@ static int sequencer_thumbnail_closest_previous_frame_get(int timeline_frame, static int sequencer_thumbnail_closest_guaranteed_frame_get(Sequence *seq, int timeline_frame) { - if (timeline_frame <= seq->startdisp) { - return seq->startdisp; + if (timeline_frame <= SEQ_time_left_handle_frame_get(seq)) { + return SEQ_time_left_handle_frame_get(seq); } /* Set of "guaranteed" thumbnails. */ - const int frame_index = timeline_frame - seq->startdisp; + const int frame_index = timeline_frame - SEQ_time_left_handle_frame_get(seq); const int frame_step = SEQ_render_thumbnails_guaranteed_set_frame_step_get(seq); const int relative_base_frame = round_fl_to_int((frame_index / (float)frame_step)) * frame_step; - const int nearest_guaranted_absolute_frame = relative_base_frame + seq->startdisp; + const int nearest_guaranted_absolute_frame = relative_base_frame + + SEQ_time_left_handle_frame_get(seq); return nearest_guaranted_absolute_frame; } @@ -443,9 +445,11 @@ void draw_seq_strip_thumbnail(View2D *v2d, float thumb_y_end = y1 + thumb_height; float cut_off = 0; - float upper_thumb_bound = (seq->endstill) ? (seq->start + seq->len) : seq->enddisp; + float upper_thumb_bound = SEQ_time_has_right_still_frames(seq) ? + (seq->start + seq->len) : + SEQ_time_right_handle_frame_get(seq); if (seq->type == SEQ_TYPE_IMAGE) { - upper_thumb_bound = seq->enddisp; + upper_thumb_bound = SEQ_time_right_handle_frame_get(seq); } float timeline_frame = SEQ_render_thumbnail_first_frame_get(seq, thumb_width, &v2d->cur); @@ -471,8 +475,8 @@ void draw_seq_strip_thumbnail(View2D *v2d, } /* Set the clipping bound to show the left handle moving over thumbs and not shift thumbs. */ - if (IN_RANGE_INCL(seq->startdisp, timeline_frame, thumb_x_end)) { - cut_off = seq->startdisp - timeline_frame; + if (IN_RANGE_INCL(SEQ_time_left_handle_frame_get(seq), timeline_frame, thumb_x_end)) { + cut_off = SEQ_time_left_handle_frame_get(seq) - timeline_frame; clipped = true; } diff --git a/source/blender/editors/space_sequencer/sequencer_view.c b/source/blender/editors/space_sequencer/sequencer_view.c index b3dbc3c72d1..857ca6d989b 100644 --- a/source/blender/editors/space_sequencer/sequencer_view.c +++ b/source/blender/editors/space_sequencer/sequencer_view.c @@ -20,8 +20,6 @@ #include "UI_view2d.h" -#include "RNA_define.h" - #include "SEQ_iterator.h" #include "SEQ_select.h" #include "SEQ_sequencer.h" @@ -308,8 +306,8 @@ static void seq_view_collection_rect_timeline(Scene *scene, SeqCollection *strip int xmargin = FPS; SEQ_ITERATOR_FOREACH (seq, strips) { - xmin = min_ii(xmin, seq->startdisp); - xmax = max_ii(xmax, seq->enddisp); + xmin = min_ii(xmin, SEQ_time_left_handle_frame_get(seq)); + xmax = max_ii(xmax, SEQ_time_right_handle_frame_get(seq)); ymin = min_ii(ymin, seq->machine); ymax = max_ii(ymax, seq->machine); diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc index b3d6c395e89..dd3aac1eae9 100644 --- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc +++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc @@ -32,8 +32,6 @@ #include "BLF_api.h" -#include "spreadsheet_intern.hh" - #include "spreadsheet_context.hh" #include "spreadsheet_data_source_geometry.hh" #include "spreadsheet_dataset_draw.hh" @@ -304,6 +302,7 @@ static float get_default_column_width(const ColumnValues &values) switch (values.type()) { case SPREADSHEET_VALUE_TYPE_BOOL: return 2.0f; + case SPREADSHEET_VALUE_TYPE_INT8: case SPREADSHEET_VALUE_TYPE_INT32: return float_width; case SPREADSHEET_VALUE_TYPE_FLOAT: diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column.cc b/source/blender/editors/space_spreadsheet/spreadsheet_column.cc index a29aa1fd026..46e98acb8e8 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_column.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_column.cc @@ -23,6 +23,9 @@ eSpreadsheetColumnValueType cpp_type_to_column_type(const CPPType &type) if (type.is<bool>()) { return SPREADSHEET_VALUE_TYPE_BOOL; } + if (type.is<int8_t>()) { + return SPREADSHEET_VALUE_TYPE_INT8; + } if (type.is<int>()) { return SPREADSHEET_VALUE_TYPE_INT32; } diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc index 4afa70d9ef6..f5315b616d0 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -64,7 +64,7 @@ std::unique_ptr<ColumnValues> ExtraColumns::get_column_values( void GeometryDataSource::foreach_default_column_ids( FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const { - if (component_->attribute_domain_size(domain_) == 0) { + if (component_->attribute_domain_num(domain_) == 0) { return; } @@ -81,6 +81,9 @@ void GeometryDataSource::foreach_default_column_ids( if (attribute_id.is_anonymous()) { return true; } + if (!bke::allow_procedural_attribute_access(attribute_id.name())) { + return true; + } SpreadsheetColumnID column_id; column_id.name = (char *)attribute_id.name().data(); fn(column_id, false); @@ -110,8 +113,8 @@ void GeometryDataSource::foreach_default_column_ids( std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( const SpreadsheetColumnID &column_id) const { - const int domain_size = component_->attribute_domain_size(domain_); - if (domain_size == 0) { + const int domain_num = component_->attribute_domain_num(domain_); + if (domain_num == 0) { return {}; } @@ -129,7 +132,7 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( Span<InstanceReference> references = instances.references(); return std::make_unique<ColumnValues>( column_id.name, - VArray<InstanceReference>::ForFunc(domain_size, + VArray<InstanceReference>::ForFunc(domain_num, [reference_handles, references](int64_t index) { return references[reference_handles[index]]; })); @@ -137,13 +140,13 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( Span<float4x4> transforms = instances.instance_transforms(); if (STREQ(column_id.name, "Rotation")) { return std::make_unique<ColumnValues>( - column_id.name, VArray<float3>::ForFunc(domain_size, [transforms](int64_t index) { + column_id.name, VArray<float3>::ForFunc(domain_num, [transforms](int64_t index) { return transforms[index].to_euler(); })); } if (STREQ(column_id.name, "Scale")) { return std::make_unique<ColumnValues>( - column_id.name, VArray<float3>::ForFunc(domain_size, [transforms](int64_t index) { + column_id.name, VArray<float3>::ForFunc(domain_num, [transforms](int64_t index) { return transforms[index].scale(); })); } @@ -210,7 +213,7 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( int GeometryDataSource::tot_rows() const { - return component_->attribute_domain_size(domain_); + return component_->attribute_domain_num(domain_); } /** @@ -256,7 +259,7 @@ IndexMask GeometryDataSource::apply_selection_filter(Vector<int64_t> &indices) c BMesh *bm = mesh_orig->edit_mesh->bm; BM_mesh_elem_table_ensure(bm, BM_VERT); - int *orig_indices = (int *)CustomData_get_layer(&mesh_eval->vdata, CD_ORIGINDEX); + const int *orig_indices = (int *)CustomData_get_layer(&mesh_eval->vdata, CD_ORIGINDEX); if (orig_indices != nullptr) { /* Use CD_ORIGINDEX layer if it exists. */ VArray<bool> selection = mesh_component->attribute_try_adapt_domain<bool>( @@ -509,7 +512,7 @@ class GeometryComponentCacheValue : public SpreadsheetCache::Value { public: /* Stores the result of fields evaluated on a geometry component. Without this, fields would have * to be reevaluated on every redraw. */ - Map<std::pair<AttributeDomain, GField>, GArray<>> arrays; + Map<std::pair<eAttrDomain, GField>, GArray<>> arrays; }; static void add_fields_as_extra_columns(SpaceSpreadsheet *sspreadsheet, @@ -523,18 +526,18 @@ static void add_fields_as_extra_columns(SpaceSpreadsheet *sspreadsheet, sspreadsheet->runtime->cache.lookup_or_add<GeometryComponentCacheValue>( std::make_unique<GeometryComponentCacheKey>(component)); - const AttributeDomain domain = (AttributeDomain)sspreadsheet->attribute_domain; - const int domain_size = component.attribute_domain_size(domain); + const eAttrDomain domain = (eAttrDomain)sspreadsheet->attribute_domain; + const int domain_num = component.attribute_domain_num(domain); for (const auto item : fields_to_show.items()) { StringRef name = item.key; const GField &field = item.value; /* Use the cached evaluated array if it exists, otherwise evaluate the field now. */ GArray<> &evaluated_array = cache.arrays.lookup_or_add_cb({domain, field}, [&]() { - GArray<> evaluated_array(field.cpp_type(), domain_size); + GArray<> evaluated_array(field.cpp_type(), domain_num); bke::GeometryComponentFieldContext field_context{component, domain}; - fn::FieldEvaluator field_evaluator{field_context, domain_size}; + fn::FieldEvaluator field_evaluator{field_context, domain_num}; field_evaluator.add_with_destination(field, evaluated_array); field_evaluator.evaluate(); return evaluated_array; @@ -547,7 +550,7 @@ static void add_fields_as_extra_columns(SpaceSpreadsheet *sspreadsheet, std::unique_ptr<DataSource> data_source_from_geometry(const bContext *C, Object *object_eval) { SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); - const AttributeDomain domain = (AttributeDomain)sspreadsheet->attribute_domain; + const eAttrDomain domain = (eAttrDomain)sspreadsheet->attribute_domain; const GeometryComponentType component_type = get_display_component_type(C, object_eval); GeometrySet geometry_set = spreadsheet_get_display_geometry_set(sspreadsheet, object_eval); diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh index 8b281e5a558..04b4f6d8d06 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh @@ -40,7 +40,7 @@ class GeometryDataSource : public DataSource { Object *object_eval_; const GeometrySet geometry_set_; const GeometryComponent *component_; - AttributeDomain domain_; + eAttrDomain domain_; ExtraColumns extra_columns_; /* Some data is computed on the fly only when it is requested. Computing it does not change the @@ -53,7 +53,7 @@ class GeometryDataSource : public DataSource { GeometryDataSource(Object *object_eval, GeometrySet geometry_set, const GeometryComponentType component_type, - const AttributeDomain domain, + const eAttrDomain domain, ExtraColumns extra_columns) : object_eval_(object_eval), geometry_set_(std::move(geometry_set)), diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc index c4b5228758c..ee22f4173ab 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc @@ -27,7 +27,7 @@ class GeometryDataSetTreeView; class GeometryDataSetTreeViewItem : public ui::AbstractTreeViewItem { GeometryComponentType component_type_; - std::optional<AttributeDomain> domain_; + std::optional<eAttrDomain> domain_; BIFIconID icon_; public: @@ -35,7 +35,7 @@ class GeometryDataSetTreeViewItem : public ui::AbstractTreeViewItem { StringRef label, BIFIconID icon); GeometryDataSetTreeViewItem(GeometryComponentType component_type, - AttributeDomain domain, + eAttrDomain domain, StringRef label, BIFIconID icon); @@ -113,7 +113,7 @@ GeometryDataSetTreeViewItem::GeometryDataSetTreeViewItem(GeometryComponentType c this->set_collapsed(false); } GeometryDataSetTreeViewItem::GeometryDataSetTreeViewItem(GeometryComponentType component_type, - AttributeDomain domain, + eAttrDomain domain, StringRef label, BIFIconID icon) : component_type_(component_type), domain_(domain), icon_(icon) @@ -144,7 +144,7 @@ void GeometryDataSetTreeViewItem::build_row(uiLayout &row) /* Using the tree row button instead of a separate right aligned button gives padding * to the right side of the number, which it didn't have with the button. */ char element_count[7]; - BLI_str_format_attribute_domain_size(element_count, *count); + BLI_str_format_decimal_unit(element_count, *count); UI_but_hint_drawstr_set((uiBut *)this->tree_row_button(), element_count); } } @@ -194,7 +194,7 @@ std::optional<int> GeometryDataSetTreeViewItem::count() const } if (const GeometryComponent *component = geometry.get_component_for_read(component_type_)) { - return component->attribute_domain_size(*domain_); + return component->attribute_domain_num(*domain_); } return 0; diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_ops.cc b/source/blender/editors/space_spreadsheet/spreadsheet_ops.cc index 4d6dc3b4166..166c5de9fc3 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_ops.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_ops.cc @@ -5,12 +5,6 @@ #include "ED_screen.h" -#include "RNA_access.h" -#include "RNA_define.h" - -#include "WM_api.h" -#include "WM_types.h" - #include "BLI_listbase.h" #include "MEM_guardedalloc.h" @@ -20,8 +14,6 @@ #include "RNA_access.h" #include "RNA_define.h" -#include "ED_screen.h" - #include "WM_api.h" #include "WM_types.h" @@ -92,7 +84,7 @@ static int select_component_domain_invoke(bContext *C, { GeometryComponentType component_type = static_cast<GeometryComponentType>( RNA_int_get(op->ptr, "component_type")); - AttributeDomain attribute_domain = static_cast<AttributeDomain>( + eAttrDomain attribute_domain = static_cast<eAttrDomain>( RNA_int_get(op->ptr, "attribute_domain_type")); SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc index eb8f111baa0..e1ff4b59b14 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc @@ -14,8 +14,6 @@ #include "RNA_access.h" -#include "spreadsheet_intern.hh" - #include "spreadsheet_data_source_geometry.hh" #include "spreadsheet_intern.hh" #include "spreadsheet_layout.hh" @@ -73,6 +71,35 @@ static void apply_row_filter(const SpreadsheetRowFilter &row_filter, } } } + else if (column_data.type().is<int8_t>()) { + const int value = row_filter.value_int; + switch (row_filter.operation) { + case SPREADSHEET_ROW_FILTER_EQUAL: { + apply_filter_operation( + column_data.typed<int8_t>(), + [&](const int cell) { return cell == value; }, + prev_mask, + new_indices); + break; + } + case SPREADSHEET_ROW_FILTER_GREATER: { + apply_filter_operation( + column_data.typed<int8_t>(), + [value](const int cell) { return cell > value; }, + prev_mask, + new_indices); + break; + } + case SPREADSHEET_ROW_FILTER_LESS: { + apply_filter_operation( + column_data.typed<int8_t>(), + [&](const int cell) { return cell < value; }, + prev_mask, + new_indices); + break; + } + } + } else if (column_data.type().is<int>()) { const int value = row_filter.value_int; switch (row_filter.operation) { @@ -190,33 +217,29 @@ static void apply_row_filter(const SpreadsheetRowFilter &row_filter, } else if (column_data.type().is<InstanceReference>()) { const StringRef value = row_filter.value_string; - switch (row_filter.operation) { - case SPREADSHEET_ROW_FILTER_EQUAL: { - apply_filter_operation( - column_data.typed<InstanceReference>(), - [&](const InstanceReference cell) { - switch (cell.type()) { - case InstanceReference::Type::Object: { - return value == (reinterpret_cast<ID &>(cell.object()).name + 2); - } - case InstanceReference::Type::Collection: { - return value == (reinterpret_cast<ID &>(cell.collection()).name + 2); - } - case InstanceReference::Type::GeometrySet: { - return false; - } - case InstanceReference::Type::None: { - return false; - } - } - BLI_assert_unreachable(); + + apply_filter_operation( + column_data.typed<InstanceReference>(), + [&](const InstanceReference cell) { + switch (cell.type()) { + case InstanceReference::Type::Object: { + return value == (reinterpret_cast<ID &>(cell.object()).name + 2); + } + case InstanceReference::Type::Collection: { + return value == (reinterpret_cast<ID &>(cell.collection()).name + 2); + } + case InstanceReference::Type::GeometrySet: { return false; - }, - prev_mask, - new_indices); - break; - } - } + } + case InstanceReference::Type::None: { + return false; + } + } + BLI_assert_unreachable(); + return false; + }, + prev_mask, + new_indices); } } diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc index 7c1ac024c12..d42a371c666 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc @@ -63,6 +63,7 @@ static std::string value_string(const SpreadsheetRowFilter &row_filter, const eSpreadsheetColumnValueType data_type) { switch (data_type) { + case SPREADSHEET_VALUE_TYPE_INT8: case SPREADSHEET_VALUE_TYPE_INT32: return std::to_string(row_filter.value_int); case SPREADSHEET_VALUE_TYPE_FLOAT: { @@ -200,6 +201,10 @@ static void spreadsheet_filter_panel_draw(const bContext *C, Panel *panel) } switch (static_cast<eSpreadsheetColumnValueType>(column->data_type)) { + case SPREADSHEET_VALUE_TYPE_INT8: + uiItemR(layout, filter_ptr, "operation", 0, nullptr, ICON_NONE); + uiItemR(layout, filter_ptr, "value_int8", 0, IFACE_("Value"), ICON_NONE); + break; case SPREADSHEET_VALUE_TYPE_INT32: uiItemR(layout, filter_ptr, "operation", 0, nullptr, ICON_NONE); uiItemR(layout, filter_ptr, "value_int", 0, IFACE_("Value"), ICON_NONE); diff --git a/source/blender/editors/space_text/text_draw.c b/source/blender/editors/space_text/text_draw.c index 36eafedbc80..c93ffccd477 100644 --- a/source/blender/editors/space_text/text_draw.c +++ b/source/blender/editors/space_text/text_draw.c @@ -1603,7 +1603,7 @@ void draw_text_main(SpaceText *st, ARegion *region) return; } - /* dpi controlled line height and font size */ + /* DPI controlled line height and font size. */ st->runtime.lheight_px = (U.widget_unit * st->lheight) / 20; /* don't draw lines below this */ diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c index 7776119a594..8add6886584 100644 --- a/source/blender/editors/space_view3d/drawobject.c +++ b/source/blender/editors/space_view3d/drawobject.c @@ -6,12 +6,14 @@ */ #include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" -#include "BLI_math.h" +#include "BLI_math_vector.h" #include "BKE_DerivedMesh.h" +#include "BKE_customdata.h" #include "BKE_editmesh.h" #include "BKE_global.h" #include "BKE_object.h" diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index a02058fde6b..9ed2fec96db 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -590,16 +590,15 @@ static bool view3d_mat_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event static char *view3d_mat_drop_tooltip(bContext *C, wmDrag *drag, const int xy[2], - struct wmDropBox *drop) + wmDropBox *UNUSED(drop)) { const char *name = WM_drag_get_item_name(drag); ARegion *region = CTX_wm_region(C); - RNA_string_set(drop->ptr, "name", name); - int mval[2] = { + const int mval[2] = { xy[0] - region->winrct.xmin, xy[1] - region->winrct.ymin, }; - return ED_object_ot_drop_named_material_tooltip(C, drop->ptr, mval); + return ED_object_ot_drop_named_material_tooltip(C, name, mval); } static bool view3d_world_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) @@ -716,7 +715,7 @@ static void view3d_ob_drop_copy_local_id(bContext *UNUSED(C), wmDrag *drag, wmDr { ID *id = WM_drag_get_local_ID(drag, ID_OB); - RNA_string_set(drop->ptr, "name", id->name + 2); + RNA_int_set(drop->ptr, "session_uuid", id->session_uuid); /* Don't duplicate ID's which were just imported. Only do that for existing, local IDs. */ BLI_assert(drag->type != WM_DRAG_ASSET); @@ -751,7 +750,7 @@ static void view3d_ob_drop_copy_external_asset(bContext *UNUSED(C), wmDrag *drag DEG_relations_tag_update(CTX_data_main(C)); WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); - RNA_string_set(drop->ptr, "name", id->name + 2); + RNA_int_set(drop->ptr, "session_uuid", id->session_uuid); Base *base = BKE_view_layer_base_find(view_layer, (Object *)id); if (base != NULL) { @@ -823,15 +822,15 @@ static void view3d_id_drop_copy(bContext *UNUSED(C), wmDrag *drag, wmDropBox *dr { ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, 0); - RNA_string_set(drop->ptr, "name", id->name + 2); + WM_operator_properties_id_lookup_set_from_id(drop->ptr, id); } static void view3d_id_drop_copy_with_type(bContext *UNUSED(C), wmDrag *drag, wmDropBox *drop) { ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, 0); - RNA_string_set(drop->ptr, "name", id->name + 2); RNA_enum_set(drop->ptr, "type", GS(id->name)); + WM_operator_properties_id_lookup_set_from_id(drop->ptr, id); } static void view3d_id_path_drop_copy(bContext *UNUSED(C), wmDrag *drag, wmDropBox *drop) @@ -839,7 +838,7 @@ static void view3d_id_path_drop_copy(bContext *UNUSED(C), wmDrag *drag, wmDropBo ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, 0); if (id) { - RNA_string_set(drop->ptr, "name", id->name + 2); + WM_operator_properties_id_lookup_set_from_id(drop->ptr, id); RNA_struct_property_unset(drop->ptr, "filepath"); } else if (drag->path[0]) { diff --git a/source/blender/editors/space_view3d/view3d_cursor_snap.c b/source/blender/editors/space_view3d/view3d_cursor_snap.c index 96a193cc10d..395df42b2cb 100644 --- a/source/blender/editors/space_view3d/view3d_cursor_snap.c +++ b/source/blender/editors/space_view3d/view3d_cursor_snap.c @@ -8,11 +8,9 @@ */ #include "DNA_object_types.h" -#include "DNA_scene_types.h" #include "BLI_listbase.h" #include "BLI_rect.h" -#include "DNA_scene_types.h" #include "MEM_guardedalloc.h" @@ -54,7 +52,7 @@ typedef struct SnapCursorDataIntern { struct SnapObjectContext *snap_context_v3d; const Scene *scene; - short snap_elem_hidden; + eSnapMode snap_elem_hidden; float prevpoint_stack[3]; @@ -374,7 +372,7 @@ void ED_view3d_cursor_snap_draw_util(RegionView3D *rv3d, const float normal[3], const uchar color_line[4], const uchar color_point[4], - const short snap_elem_type) + const eSnapMode snap_elem_type) { if (!loc_prev && !loc_curr) { return; @@ -539,9 +537,9 @@ static bool v3d_cursor_is_snap_invert(SnapCursorDataIntern *data_intern, const w /** \name Update * \{ */ -static short v3d_cursor_snap_elements(V3DSnapCursorState *snap_state, Scene *scene) +static eSnapMode v3d_cursor_snap_elements(V3DSnapCursorState *snap_state, Scene *scene) { - short snap_elements = snap_state->snap_elem_force; + eSnapMode snap_elements = snap_state->snap_elem_force; if (!snap_elements) { return scene->toolsettings->snap_mode; } @@ -587,7 +585,7 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state, v3d_cursor_snap_context_ensure(scene); float co[3], no[3], face_nor[3], obmat[4][4], omat[3][3]; - short snap_elem = 0; + eSnapMode snap_elem = SCE_SNAP_MODE_NONE; int snap_elem_index[3] = {-1, -1, -1}; int index = -1; @@ -596,8 +594,8 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state, zero_v3(face_nor); unit_m3(omat); - ushort snap_elements = v3d_cursor_snap_elements(state, scene); - data_intern->snap_elem_hidden = 0; + eSnapMode snap_elements = v3d_cursor_snap_elements(state, scene); + data_intern->snap_elem_hidden = SCE_SNAP_MODE_NONE; const bool calc_plane_omat = v3d_cursor_snap_calc_plane(); if (calc_plane_omat && !(snap_elements & SCE_SNAP_MODE_FACE)) { data_intern->snap_elem_hidden = SCE_SNAP_MODE_FACE; @@ -613,7 +611,7 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state, if (snap_data->is_snap_invert != !(ts->snap_flag & SCE_SNAP)) { snap_data->is_enabled = false; if (!calc_plane_omat) { - snap_data->snap_elem = 0; + snap_data->snap_elem = SCE_SNAP_MODE_NONE; return; } snap_elements = data_intern->snap_elem_hidden = SCE_SNAP_MODE_FACE; @@ -621,8 +619,7 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state, } #endif - if (snap_elements & (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE | - SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR)) { + if (snap_elements & SCE_SNAP_MODE_GEOM) { float prev_co[3] = {0.0f}; if (state->prevpoint) { copy_v3_v3(prev_co, state->prevpoint); @@ -648,7 +645,7 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state, v3d, snap_elements, &(const struct SnapObjectParams){ - .snap_select = SNAP_ALL, + .snap_target_select = SCE_SNAP_TARGET_ALL, .edit_mode_type = edit_mode_type, .use_occlusion_test = use_occlusion_test, }, @@ -669,7 +666,8 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state, if (calc_plane_omat) { RegionView3D *rv3d = region->regiondata; - bool orient_surface = snap_elem && (state->plane_orient == V3D_PLACE_ORIENT_SURFACE); + bool orient_surface = (snap_elem != SCE_SNAP_MODE_NONE) && + (state->plane_orient == V3D_PLACE_ORIENT_SURFACE); if (orient_surface) { copy_m3_m4(omat, obmat); } @@ -700,7 +698,7 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state, } } - float *co_depth = snap_elem ? co : scene->cursor.location; + float *co_depth = (snap_elem != SCE_SNAP_MODE_NONE) ? co : scene->cursor.location; snap_elem &= ~data_intern->snap_elem_hidden; if (snap_elem == 0) { RegionView3D *rv3d = region->regiondata; @@ -816,7 +814,7 @@ static void v3d_cursor_snap_draw_fn(bContext *C, int x, int y, void *UNUSED(cust } const bool draw_plane = state->draw_plane || state->draw_box; - if (!snap_data->snap_elem && !draw_plane) { + if (snap_data->snap_elem == SCE_SNAP_MODE_NONE && !draw_plane) { return; } @@ -834,7 +832,7 @@ static void v3d_cursor_snap_draw_fn(bContext *C, int x, int y, void *UNUSED(cust v3d_cursor_plane_draw(rv3d, state->plane_axis, matrix); } - if (snap_data->snap_elem && (state->draw_point || state->draw_box)) { + if (snap_data->snap_elem != SCE_SNAP_MODE_NONE && (state->draw_point || state->draw_box)) { const float *prev_point = (snap_data->snap_elem & SCE_SNAP_MODE_EDGE_PERPENDICULAR) ? state->prevpoint : NULL; diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 5b068750d76..b9e4c19295d 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -1786,11 +1786,13 @@ void ED_view3d_draw_offscreen_simple(Depsgraph *depsgraph, } /* Disable other overlays (set all available _HIDE_ flags). */ v3d.overlay.flag |= V3D_OVERLAY_HIDE_CURSOR | V3D_OVERLAY_HIDE_TEXT | - V3D_OVERLAY_HIDE_MOTION_PATHS | V3D_OVERLAY_HIDE_BONES | - V3D_OVERLAY_HIDE_OBJECT_ORIGINS; + V3D_OVERLAY_HIDE_MOTION_PATHS | V3D_OVERLAY_HIDE_OBJECT_ORIGINS; if ((draw_flags & V3D_OFSDRAW_SHOW_OBJECT_EXTRAS) == 0) { v3d.overlay.flag |= V3D_OVERLAY_HIDE_OBJECT_XTRAS; } + if ((object_type_exclude_viewport_override & (1 << OB_ARMATURE)) != 0) { + v3d.overlay.flag |= V3D_OVERLAY_HIDE_BONES; + } v3d.flag |= V3D_HIDE_HELPLINES; } diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index e52ff062302..5fbdbef676c 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -608,7 +608,7 @@ void VIEW3D_OT_background_image_add(wmOperatorType *ot) ot->flag = OPTYPE_UNDO; /* properties */ - RNA_def_string(ot->srna, "name", "Image", MAX_ID_NAME - 2, "Name", "Image name to assign"); + WM_operator_properties_id_lookup(ot, true); WM_operator_properties_filesel(ot, FILE_TYPE_FOLDER | FILE_TYPE_IMAGE | FILE_TYPE_MOVIE, FILE_SPECIAL, @@ -631,13 +631,20 @@ static int background_image_remove_exec(bContext *C, wmOperator *op) CameraBGImage *bgpic_rem = BLI_findlink(&cam->bg_images, index); if (bgpic_rem) { - if (bgpic_rem->source == CAM_BGIMG_SOURCE_IMAGE) { - id_us_min((ID *)bgpic_rem->ima); - } - else if (bgpic_rem->source == CAM_BGIMG_SOURCE_MOVIE) { - id_us_min((ID *)bgpic_rem->clip); + if (ID_IS_OVERRIDE_LIBRARY(cam) && + (bgpic_rem->flag & CAM_BGIMG_FLAG_OVERRIDE_LIBRARY_LOCAL) == 0) { + BKE_reportf(op->reports, + RPT_WARNING, + "Cannot remove background image %d from camera '%s', as it is from the linked " + "reference data", + index, + cam->id.name + 2); + return OPERATOR_CANCELLED; } + id_us_min((ID *)bgpic_rem->ima); + id_us_min((ID *)bgpic_rem->clip); + BKE_camera_background_image_remove(cam, bgpic_rem); WM_event_add_notifier(C, NC_CAMERA | ND_DRAW_RENDER_VIEWPORT, cam); @@ -657,7 +664,7 @@ void VIEW3D_OT_background_image_remove(wmOperatorType *ot) /* api callbacks */ ot->exec = background_image_remove_exec; - ot->poll = ED_operator_camera; + ot->poll = ED_operator_camera_poll; /* flags */ ot->flag = 0; @@ -678,10 +685,8 @@ static int drop_world_exec(bContext *C, wmOperator *op) Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); - char name[MAX_ID_NAME - 2]; - - RNA_string_get(op->ptr, "name", name); - World *world = (World *)BKE_libblock_find_name(bmain, ID_WO, name); + World *world = (World *)WM_operator_properties_id_lookup_from_name_or_session_uuid( + bmain, op->ptr, ID_WO); if (world == NULL) { return OPERATOR_CANCELLED; } @@ -718,7 +723,7 @@ void VIEW3D_OT_drop_world(wmOperatorType *ot) ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL; /* properties */ - RNA_def_string(ot->srna, "name", "World", MAX_ID_NAME - 2, "Name", "World to assign"); + WM_operator_properties_id_lookup(ot, true); } /** \} */ @@ -905,7 +910,7 @@ void ED_view3d_cursor3d_position_rotation(bContext *C, v3d, SCE_SNAP_MODE_FACE, &(const struct SnapObjectParams){ - .snap_select = SNAP_ALL, + .snap_target_select = SCE_SNAP_TARGET_ALL, .edit_mode_type = SNAP_GEOM_FINAL, .use_occlusion_test = true, }, diff --git a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c index 567022cc9d1..9aae30c4a7e 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c @@ -359,7 +359,7 @@ static bool view3d_ruler_item_mousemove(const bContext *C, v3d, SCE_SNAP_MODE_FACE, &(const struct SnapObjectParams){ - .snap_select = SNAP_ALL, + .snap_target_select = SCE_SNAP_TARGET_ALL, .edit_mode_type = SNAP_GEOM_CAGE, }, mval_fl, @@ -374,7 +374,7 @@ static bool view3d_ruler_item_mousemove(const bContext *C, depsgraph, v3d, &(const struct SnapObjectParams){ - .snap_select = SNAP_ALL, + .snap_target_select = SCE_SNAP_TARGET_ALL, .edit_mode_type = SNAP_GEOM_CAGE, }, ray_start, @@ -1228,11 +1228,7 @@ static void WIDGETGROUP_ruler_setup(const bContext *C, wmGizmoGroup *gzgroup) gzt_snap = WM_gizmotype_find("GIZMO_GT_snap_3d", true); gizmo = WM_gizmo_new_ptr(gzt_snap, gzgroup, NULL); - RNA_enum_set(gizmo->ptr, - "snap_elements_force", - (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE | - /* SCE_SNAP_MODE_VOLUME | SCE_SNAP_MODE_GRID | SCE_SNAP_MODE_INCREMENT | */ - SCE_SNAP_MODE_EDGE_PERPENDICULAR | SCE_SNAP_MODE_EDGE_MIDPOINT)); + RNA_enum_set(gizmo->ptr, "snap_elements_force", SCE_SNAP_MODE_GEOM); ED_gizmotypes_snap_3d_flag_set(gizmo, V3D_SNAPCURSOR_SNAP_EDIT_GEOM_CAGE); WM_gizmo_set_color(gizmo, (float[4]){1.0f, 1.0f, 1.0f, 1.0f}); diff --git a/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c b/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c index f0557205e8f..aa287403e23 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c @@ -19,8 +19,6 @@ #include "MEM_guardedalloc.h" -#include "WM_toolsystem.h" - #include "RNA_access.h" #include "RNA_define.h" diff --git a/source/blender/editors/space_view3d/view3d_navigate_roll.c b/source/blender/editors/space_view3d/view3d_navigate_roll.c index ea21eed6445..087ca72211e 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_roll.c +++ b/source/blender/editors/space_view3d/view3d_navigate_roll.c @@ -48,11 +48,7 @@ static void view_roll_angle(ARegion *region, normalize_qt(quat); if (use_axis_view && RV3D_VIEW_IS_AXIS(rv3d->view) && (fabsf(angle) == (float)M_PI_2)) { - if (ED_view3d_quat_to_axis_view(quat, 0.01f, &rv3d->view, &rv3d->view_axis_roll)) { - if (rv3d->view != RV3D_VIEW_USER) { - ED_view3d_quat_from_axis_view(rv3d->view, rv3d->view_axis_roll, quat_mul); - } - } + ED_view3d_quat_to_axis_view_and_reset_quat(quat, 0.01f, &rv3d->view, &rv3d->view_axis_roll); } else { rv3d->view = RV3D_VIEW_USER; diff --git a/source/blender/editors/space_view3d/view3d_navigate_rotate.c b/source/blender/editors/space_view3d/view3d_navigate_rotate.c index c9ef6422982..989fa152acc 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_rotate.c +++ b/source/blender/editors/space_view3d/view3d_navigate_rotate.c @@ -162,10 +162,8 @@ static void viewrotate_apply_snap(ViewOpsData *vod) if (found) { /* lock 'quat_best' to an axis view if we can */ - ED_view3d_quat_to_axis_view(quat_best, 0.01f, &rv3d->view, &rv3d->view_axis_roll); - if (rv3d->view != RV3D_VIEW_USER) { - ED_view3d_quat_from_axis_view(rv3d->view, rv3d->view_axis_roll, quat_best); - } + ED_view3d_quat_to_axis_view_and_reset_quat( + quat_best, 0.01f, &rv3d->view, &rv3d->view_axis_roll); } else { copy_qt_qt(quat_best, viewquat_align); diff --git a/source/blender/editors/space_view3d/view3d_navigate_walk.c b/source/blender/editors/space_view3d/view3d_navigate_walk.c index 19e064438fc..c4eff01375b 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_walk.c +++ b/source/blender/editors/space_view3d/view3d_navigate_walk.c @@ -412,7 +412,7 @@ static bool walk_floor_distance_get(RegionView3D *rv3d, walk->depsgraph, walk->v3d, &(const struct SnapObjectParams){ - .snap_select = SNAP_ALL, + .snap_target_select = SCE_SNAP_TARGET_ALL, /* Avoid having to convert the edit-mesh to a regular mesh. */ .edit_mode_type = SNAP_GEOM_EDIT, }, @@ -454,7 +454,7 @@ static bool walk_ray_cast(RegionView3D *rv3d, walk->depsgraph, walk->v3d, &(const struct SnapObjectParams){ - .snap_select = SNAP_ALL, + .snap_target_select = SCE_SNAP_TARGET_ALL, }, ray_start, ray_normal, diff --git a/source/blender/editors/space_view3d/view3d_placement.c b/source/blender/editors/space_view3d/view3d_placement.c index eefc085bdf8..d3b82476d09 100644 --- a/source/blender/editors/space_view3d/view3d_placement.c +++ b/source/blender/editors/space_view3d/view3d_placement.c @@ -695,7 +695,7 @@ static bool view3d_interactive_add_calc_snap(bContext *UNUSED(C), if (r_is_snap_invert) { *r_is_snap_invert = snap_data->is_snap_invert; } - return snap_data->snap_elem != 0; + return snap_data->snap_elem != SCE_SNAP_MODE_NONE; } /** \} */ @@ -1303,7 +1303,7 @@ static int idp_rna_snap_target_get_fn(struct PointerRNA *UNUSED(ptr), struct PropertyRNA *UNUSED(prop)) { V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); - if (!snap_state->snap_elem_force) { + if (snap_state->snap_elem_force == SCE_SNAP_MODE_NONE) { return PLACE_SNAP_TO_DEFAULT; } @@ -1316,7 +1316,7 @@ static void idp_rna_snap_target_set_fn(struct PointerRNA *UNUSED(ptr), struct PropertyRNA *UNUSED(prop), int value) { - short snap_mode = 0; /* #toolsettings->snap_mode. */ + eSnapMode snap_mode = SCE_SNAP_MODE_NONE; /* #toolsettings->snap_mode. */ const enum ePlace_SnapTo snap_to = value; if (snap_to == PLACE_SNAP_TO_GEOMETRY) { snap_mode = SCE_SNAP_MODE_GEOM; diff --git a/source/blender/editors/space_view3d/view3d_project.c b/source/blender/editors/space_view3d/view3d_project.c index 85d239507ce..1230515a180 100644 --- a/source/blender/editors/space_view3d/view3d_project.c +++ b/source/blender/editors/space_view3d/view3d_project.c @@ -30,7 +30,7 @@ void ED_view3d_project_float_v2_m4(const ARegion *region, const float co[3], float r_co[2], - float mat[4][4]) + const float mat[4][4]) { float vec4[4]; @@ -52,7 +52,7 @@ void ED_view3d_project_float_v2_m4(const ARegion *region, void ED_view3d_project_float_v3_m4(const ARegion *region, const float co[3], float r_co[3], - float mat[4][4]) + const float mat[4][4]) { float vec4[4]; @@ -312,7 +312,7 @@ float ED_view3d_calc_depth_for_comparison(const RegionView3D *rv3d, const float return -dot_v3v3(rv3d->viewinv[2], co); } -static void view3d_win_to_ray_segment(struct Depsgraph *depsgraph, +static void view3d_win_to_ray_segment(const struct Depsgraph *depsgraph, const ARegion *region, const View3D *v3d, const float mval[2], @@ -642,9 +642,9 @@ void ED_view3d_win_to_vector(const ARegion *region, const float mval[2], float r normalize_v3(r_out); } -bool ED_view3d_win_to_segment_clipped(struct Depsgraph *depsgraph, +bool ED_view3d_win_to_segment_clipped(const struct Depsgraph *depsgraph, const ARegion *region, - View3D *v3d, + const View3D *v3d, const float mval[2], float r_ray_start[3], float r_ray_end[3], diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index 7a022441876..8eff9ee472f 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -57,8 +57,6 @@ #include "BKE_tracking.h" #include "BKE_workspace.h" -#include "DEG_depsgraph.h" - #include "WM_api.h" #include "WM_toolsystem.h" #include "WM_types.h" @@ -1647,7 +1645,7 @@ static int bone_select_menu_exec(bContext *C, wmOperator *op) const int name_index = RNA_enum_get(op->ptr, "name"); const struct SelectPick_Params params = { - .sel_op = ED_select_op_from_operator(op), + .sel_op = ED_select_op_from_operator(op->ptr), }; View3D *v3d = CTX_wm_view3d(C); @@ -2876,8 +2874,9 @@ static int view3d_select_exec(bContext *C, wmOperator *op) Object *obact = CTX_data_active_object(C); struct SelectPick_Params params = {0}; - ED_select_pick_params_from_operator(op, ¶ms); + ED_select_pick_params_from_operator(op->ptr, ¶ms); + const bool vert_without_handles = RNA_boolean_get(op->ptr, "vert_without_handles"); bool center = RNA_boolean_get(op->ptr, "center"); bool enumerate = RNA_boolean_get(op->ptr, "enumerate"); /* Only force object select for edit-mode to support vertex parenting, @@ -2932,7 +2931,8 @@ static int view3d_select_exec(bContext *C, wmOperator *op) changed = ED_lattice_select_pick(C, mval, ¶ms); } else if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) { - changed = ED_curve_editnurb_select_pick(C, mval, ED_view3d_select_dist_px(), ¶ms); + changed = ED_curve_editnurb_select_pick( + C, mval, ED_view3d_select_dist_px(), vert_without_handles, ¶ms); } else if (obedit->type == OB_MBALL) { changed = ED_mball_select_pick(C, mval, ¶ms); @@ -2988,6 +2988,7 @@ void VIEW3D_OT_select(wmOperatorType *ot) ot->invoke = view3d_select_invoke; ot->exec = view3d_select_exec; ot->poll = ED_operator_view3d_active; + ot->get_name = ED_select_pick_get_name; /* flags */ ot->flag = OPTYPE_UNDO; @@ -3008,6 +3009,15 @@ void VIEW3D_OT_select(wmOperatorType *ot) prop = RNA_def_boolean(ot->srna, "object", 0, "Object", "Use object selection (edit mode only)"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); + /* Needed for select-through to usefully drag handles, see: T98254. + * NOTE: this option may be removed and become default behavior, see design task: T98552. */ + prop = RNA_def_boolean(ot->srna, + "vert_without_handles", + 0, + "Control Point Without Handles", + "Only select the curve control point, not it's handles"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_int_vector(ot->srna, "location", 2, @@ -4740,6 +4750,7 @@ void VIEW3D_OT_select_circle(wmOperatorType *ot) ot->exec = view3d_circle_select_exec; ot->poll = view3d_selectable_data; ot->cancel = view3d_circle_select_cancel; + ot->get_name = ED_select_circle_get_name; /* flags */ ot->flag = OPTYPE_UNDO; diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c index e6895c0f4a3..306394ce53d 100644 --- a/source/blender/editors/space_view3d/view3d_utils.c +++ b/source/blender/editors/space_view3d/view3d_utils.c @@ -106,7 +106,7 @@ void ED_view3d_dist_range_get(const View3D *v3d, float r_dist_range[2]) r_dist_range[1] = v3d->clip_end * 10.0f; } -bool ED_view3d_clip_range_get(Depsgraph *depsgraph, +bool ED_view3d_clip_range_get(const Depsgraph *depsgraph, const View3D *v3d, const RegionView3D *rv3d, float *r_clipsta, @@ -1317,22 +1317,59 @@ bool ED_view3d_quat_to_axis_view(const float quat[4], *r_view = RV3D_VIEW_USER; *r_view_axis_roll = RV3D_VIEW_AXIS_ROLL_0; - /* quat values are all unit length */ - for (int view = RV3D_VIEW_FRONT; view <= RV3D_VIEW_BOTTOM; view++) { - for (int view_axis_roll = RV3D_VIEW_AXIS_ROLL_0; view_axis_roll <= RV3D_VIEW_AXIS_ROLL_270; - view_axis_roll++) { - if (fabsf(angle_signed_qtqt( - quat, view3d_quat_axis[view - RV3D_VIEW_FRONT][view_axis_roll])) < epsilon) { - *r_view = view; - *r_view_axis_roll = view_axis_roll; - return true; + /* Quaternion values are all unit length. */ + + if (epsilon < M_PI_4) { + /* Under 45 degrees, just pick the closest value. */ + for (int view = RV3D_VIEW_FRONT; view <= RV3D_VIEW_BOTTOM; view++) { + for (int view_axis_roll = RV3D_VIEW_AXIS_ROLL_0; view_axis_roll <= RV3D_VIEW_AXIS_ROLL_270; + view_axis_roll++) { + if (fabsf(angle_signed_qtqt( + quat, view3d_quat_axis[view - RV3D_VIEW_FRONT][view_axis_roll])) < epsilon) { + *r_view = view; + *r_view_axis_roll = view_axis_roll; + return true; + } + } + } + } + else { + /* Epsilon over 45 degrees, check all & find use the closest. */ + float delta_best = FLT_MAX; + for (int view = RV3D_VIEW_FRONT; view <= RV3D_VIEW_BOTTOM; view++) { + for (int view_axis_roll = RV3D_VIEW_AXIS_ROLL_0; view_axis_roll <= RV3D_VIEW_AXIS_ROLL_270; + view_axis_roll++) { + const float delta_test = fabsf( + angle_signed_qtqt(quat, view3d_quat_axis[view - RV3D_VIEW_FRONT][view_axis_roll])); + if (delta_best > delta_test) { + delta_best = delta_test; + *r_view = view; + *r_view_axis_roll = view_axis_roll; + } } } + if (*r_view != RV3D_VIEW_USER) { + return true; + } } return false; } +bool ED_view3d_quat_to_axis_view_and_reset_quat(float quat[4], + const float epsilon, + char *r_view, + char *r_view_axis_roll) +{ + const bool is_axis_view = ED_view3d_quat_to_axis_view(quat, epsilon, r_view, r_view_axis_roll); + if (is_axis_view) { + /* Reset `quat` to it's view axis, so axis-aligned views are always *exactly* aligned. */ + BLI_assert(*r_view != RV3D_VIEW_USER); + ED_view3d_quat_from_axis_view(*r_view, *r_view_axis_roll, quat); + } + return is_axis_view; +} + char ED_view3d_lock_view_from_index(int index) { switch (index) { diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 7a2a2a8a5e8..960d9a25ca7 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -1172,12 +1172,20 @@ int transformEvent(TransInfo *t, const wmEvent *event) MOD_CONSTRAINT_SELECT_PLANE; if (t->con.mode & CON_APPLY) { stopConstraint(t); + initSelectConstraint(t); + + /* In this case we might just want to remove the constraint, + * so set #TREDRAW_SOFT to only select the constraint on the next mouse move event. + * This way we can kind of "cancel" due to confirmation without constraint. */ + t->redraw = TREDRAW_SOFT; } + else { + initSelectConstraint(t); - initSelectConstraint(t); - /* Use #TREDRAW_SOFT so that #selectConstraint is only called on the next event. - * This allows us to "deselect" the constraint. */ - t->redraw = TREDRAW_SOFT; + /* When first called, set #TREDRAW_HARD to select constraint immediately in + * #selectConstraint. */ + BLI_assert(t->redraw == TREDRAW_HARD); + } } } handled = true; @@ -1417,9 +1425,6 @@ static void drawTransformView(const struct bContext *C, ARegion *region, void *a /* edge slide, vert slide */ drawEdgeSlide(t); drawVertSlide(t); - - /* Rotation */ - drawDial3d(t); } } @@ -1571,6 +1576,7 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op) /* do we check for parameter? */ if (transformModeUseSnap(t)) { if (!(t->modifiers & MOD_SNAP) != !(t->tsnap.flag & SCE_SNAP)) { + /* Type is #eSnapFlag, but type must match various snap attributes in #ToolSettings. */ char *snap_flag_ptr; wmMsgParams_RNA msg_key_params = {{0}}; @@ -1778,13 +1784,6 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve t->launch_event = event ? WM_userdef_event_type_from_keymap_type(event->type) : -1; t->is_launch_event_drag = event ? (event->val == KM_CLICK_DRAG) : false; - /* XXX Remove this when wm_operator_call_internal doesn't use window->eventstate - * (which can have type = 0) */ - /* For gizmo only, so assume LEFTMOUSE. */ - if (t->launch_event == 0) { - t->launch_event = LEFTMOUSE; - } - unit_m3(t->spacemtx); initTransInfo(C, t, op, event); @@ -2053,3 +2052,17 @@ bool checkUseAxisMatrix(TransInfo *t) return false; } + +bool transform_apply_matrix(TransInfo *t, float mat[4][4]) +{ + if (t->transform_matrix != NULL) { + t->transform_matrix(t, mat); + return true; + } + return false; +} + +void transform_final_value_get(const TransInfo *t, float *value, const int value_num) +{ + memcpy(value, t->values_final, sizeof(float) * value_num); +} diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index a3df6a44682..a35942aedec 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -13,6 +13,7 @@ #include "DNA_listBase.h" #include "DNA_object_enums.h" +#include "DNA_scene_types.h" #include "DEG_depsgraph.h" @@ -153,7 +154,8 @@ typedef enum { } eTModifier; /** #TransSnap.status */ -typedef enum { +typedef enum eTSnap { + SNAP_RESETTED = 0, SNAP_FORCED = 1 << 0, TARGET_INIT = 1 << 1, /* Special flag for snap to grid. */ @@ -294,11 +296,14 @@ typedef struct TransSnapPoint { } TransSnapPoint; typedef struct TransSnap { - char flag; - char mode; - short target; - short modePoint; - short modeSelect; + /* Snapping options stored as flags */ + eSnapFlag flag; + /* Method(s) used for snapping source to target */ + eSnapMode mode; + /* Part of source to snap to target */ + eSnapSourceSelect source_select; + /* Determines which objects are possible target */ + eSnapTargetSelect target_select; bool align; bool project; bool snap_self; @@ -306,7 +311,7 @@ typedef struct TransSnap { bool use_backface_culling; eTSnap status; /* Snapped Element Type (currently for objects only). */ - char snapElem; + eSnapMode snapElem; /** snapping from this point (in global-space). */ float snapTarget[3]; /** to this point (in global-space). */ @@ -533,6 +538,13 @@ typedef struct TransInfo { /* Event handler function that determines whether the viewport needs to be redrawn. */ eRedrawFlag (*handleEvent)(struct TransInfo *, const struct wmEvent *); + /** + * Optional callback to transform a single matrix. + * + * \note used by the gizmo to transform the matrix used to position it. + */ + void (*transform_matrix)(struct TransInfo *t, float mat_xform[4][4]); + /** Constraint Data. */ TransCon con; @@ -713,6 +725,12 @@ void removeAspectRatio(TransInfo *t, float vec[2]); */ struct wmKeyMap *transform_modal_keymap(struct wmKeyConfig *keyconf); +/** + * Transform a single matrix using the current `t->final_values`. + */ +bool transform_apply_matrix(TransInfo *t, float mat[4][4]); +void transform_final_value_get(const TransInfo *t, float *value, int value_num); + /** \} */ /* -------------------------------------------------------------------- */ @@ -725,7 +743,6 @@ struct wmKeyMap *transform_modal_keymap(struct wmKeyConfig *keyconf); bool gimbal_axis_pose(struct Object *ob, const struct bPoseChannel *pchan, float gmat[3][3]); bool gimbal_axis_object(struct Object *ob, float gmat[3][3]); -void drawDial3d(const TransInfo *t); /** \} */ diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index dbe67bd0d66..d9b971c5478 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -479,6 +479,45 @@ TransDataCurveHandleFlags *initTransDataCurveHandles(TransData *td, struct BezTr /** \name UV Coordinates * \{ */ +/** + * Find the correction for the scaling factor when "Constrain to Bounds" is active. + * \param numerator: How far the UV boundary (unit square) is from the origin of the scale. + * \param denominator: How far the AABB is from the origin of the scale. + * \param scale: Scale parameter to update. + */ +static void constrain_scale_to_boundary(const float numerator, + const float denominator, + float *scale) +{ + if (denominator == 0.0f) { + /* The origin of the scale is on the edge of the boundary. */ + if (numerator < 0.0f) { + /* Negative scale will wrap around and put us outside the boundary. */ + *scale = 0.0f; /* Hold at the boundary instead. */ + } + return; /* Nothing else we can do without more info. */ + } + + const float correction = numerator / denominator; + if (correction < 0.0f || !isfinite(correction)) { + /* TODO: Correction is negative or invalid, but we lack context to fix `*scale`. */ + return; + } + + if (denominator < 0.0f) { + /* Scale origin is outside boundary, only make scale bigger. */ + if (*scale < correction) { + *scale = correction; + } + return; + } + + /* Scale origin is inside boundary, the "regular" case, limit maximum scale. */ + if (*scale > correction) { + *scale = correction; + } +} + bool clipUVTransform(TransInfo *t, float vec[2], const bool resize) { bool clipx = true, clipy = true; @@ -517,31 +556,29 @@ bool clipUVTransform(TransInfo *t, float vec[2], const bool resize) } if (resize) { - if (min[0] < base_offset[0] && t->center_global[0] > base_offset[0] && - t->center_global[0] < base_offset[0] + (t->aspect[0] * 0.5f)) { - vec[0] *= (t->center_global[0] - base_offset[0]) / (t->center_global[0] - min[0]); - } - else if (max[0] > (base_offset[0] + t->aspect[0]) && - t->center_global[0] < (base_offset[0] + t->aspect[0])) { - vec[0] *= (t->center_global[0] - (base_offset[0] + t->aspect[0])) / - (t->center_global[0] - max[0]); - } - else { - clipx = 0; - } - - if (min[1] < base_offset[1] && t->center_global[1] > base_offset[1] && - t->center_global[1] < base_offset[1] + (t->aspect[1] * 0.5f)) { - vec[1] *= (t->center_global[1] - base_offset[1]) / (t->center_global[1] - min[1]); - } - else if (max[1] > (base_offset[1] + t->aspect[1]) && - t->center_global[1] < (base_offset[1] + t->aspect[1])) { - vec[1] *= (t->center_global[1] - (base_offset[1] + t->aspect[1])) / - (t->center_global[1] - max[1]); - } - else { - clipy = 0; - } + /* Assume no change is required. */ + float scalex = 1.0f; + float scaley = 1.0f; + + /* Update U against the left border. */ + constrain_scale_to_boundary( + t->center_global[0] - base_offset[0], t->center_global[0] - min[0], &scalex); + /* Now the right border, negated, because `-1.0 / -1.0 = 1.0` */ + constrain_scale_to_boundary(base_offset[0] + t->aspect[0] - t->center_global[0], + max[0] - t->center_global[0], + &scalex); + + /* Do the same for the V co-ordinate, which is called `y`. */ + constrain_scale_to_boundary( + t->center_global[1] - base_offset[1], t->center_global[1] - min[1], &scaley); + constrain_scale_to_boundary(base_offset[1] + t->aspect[1] - t->center_global[1], + max[1] - t->center_global[1], + &scaley); + + clipx = (scalex != 1.0f); + clipy = (scaley != 1.0f); + vec[0] *= scalex; + vec[1] *= scaley; } else { if (min[0] < base_offset[0]) { @@ -1570,10 +1607,9 @@ void transform_convert_clip_mirror_modifier_apply(TransDataContainer *tc) } } -void animrecord_check_state(TransInfo *t, struct Object *ob) +void animrecord_check_state(TransInfo *t, struct ID *id) { Scene *scene = t->scene; - ID *id = &ob->id; wmTimer *animtimer = t->animtimer; ScreenAnimData *sad = (animtimer) ? animtimer->customdata : NULL; diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h index 7080deaec66..037fbe26c77 100644 --- a/source/blender/editors/transform/transform_convert.h +++ b/source/blender/editors/transform/transform_convert.h @@ -95,7 +95,7 @@ void transform_convert_clip_mirror_modifier_apply(TransDataContainer *tc); /** * For the realtime animation recording feature, handle overlapping data. */ -void animrecord_check_state(TransInfo *t, struct Object *ob); +void animrecord_check_state(TransInfo *t, struct ID *id); /* transform_convert_action.c */ diff --git a/source/blender/editors/transform/transform_convert_armature.c b/source/blender/editors/transform/transform_convert_armature.c index 3c1101d48a5..e1b25acb21e 100644 --- a/source/blender/editors/transform/transform_convert_armature.c +++ b/source/blender/editors/transform/transform_convert_armature.c @@ -1471,7 +1471,7 @@ void recalcData_pose(TransInfo *t) /* XXX: this currently doesn't work, since flags aren't set yet! */ int targetless_ik = (t->flag & T_AUTOIK); - animrecord_check_state(t, ob); + animrecord_check_state(t, &ob->id); autokeyframe_pose(t->context, t->scene, ob, t->mode, targetless_ik); } diff --git a/source/blender/editors/transform/transform_convert_graph.c b/source/blender/editors/transform/transform_convert_graph.c index 54222fbb117..2039daee386 100644 --- a/source/blender/editors/transform/transform_convert_graph.c +++ b/source/blender/editors/transform/transform_convert_graph.c @@ -24,12 +24,9 @@ #include "UI_view2d.h" #include "transform.h" -#include "transform_snap.h" - #include "transform_convert.h" -#include "transform_snap.h" - #include "transform_mode.h" +#include "transform_snap.h" typedef struct TransDataGraph { float unit_scale; diff --git a/source/blender/editors/transform/transform_convert_mask.c b/source/blender/editors/transform/transform_convert_mask.c index 5255cc4412e..b07d2bda0c5 100644 --- a/source/blender/editors/transform/transform_convert_mask.c +++ b/source/blender/editors/transform/transform_convert_mask.c @@ -261,18 +261,10 @@ void createTransMaskingData(bContext *C, TransInfo *t) tc->data_len = 0; - if (!mask) { + if (!ED_maskedit_mask_visible_splines_poll(C)) { return; } - if (t->spacetype == SPACE_CLIP) { - SpaceClip *sc = t->area->spacedata.first; - MovieClip *clip = ED_space_clip_get_clip(sc); - if (!clip) { - return; - } - } - /* count */ for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { MaskSpline *spline; diff --git a/source/blender/editors/transform/transform_convert_nla.c b/source/blender/editors/transform/transform_convert_nla.c index 685c35489de..2fa8fcf65ba 100644 --- a/source/blender/editors/transform/transform_convert_nla.c +++ b/source/blender/editors/transform/transform_convert_nla.c @@ -26,12 +26,9 @@ #include "RNA_prototypes.h" #include "transform.h" -#include "transform_snap.h" - #include "transform_convert.h" -#include "transform_snap.h" - #include "transform_mode.h" +#include "transform_snap.h" /** Used for NLA transform (stored in #TransData.extra pointer). */ typedef struct TransDataNla { diff --git a/source/blender/editors/transform/transform_convert_node.c b/source/blender/editors/transform/transform_convert_node.c index d5d79bedbf4..8281052c314 100644 --- a/source/blender/editors/transform/transform_convert_node.c +++ b/source/blender/editors/transform/transform_convert_node.c @@ -46,7 +46,7 @@ static void NodeToTransData(TransData *td, TransData2D *td2d, bNode *node, const } /* use top-left corner as the transform origin for nodes */ - /* weirdo - but the node system is a mix of free 2d elements and dpi sensitive UI */ + /* Weirdo - but the node system is a mix of free 2d elements and DPI sensitive UI. */ #ifdef USE_NODE_CENTER td2d->loc[0] = (locx * dpi_fac) + (BLI_rctf_size_x(&node->totr) * +0.5f); td2d->loc[1] = (locy * dpi_fac) + (BLI_rctf_size_y(&node->totr) * -0.5f); @@ -194,7 +194,7 @@ void flushTransNodes(TransInfo *t) loc[1] += 0.5f * BLI_rctf_size_y(&node->totr); #endif - /* weirdo - but the node system is a mix of free 2d elements and dpi sensitive UI */ + /* Weirdo - but the node system is a mix of free 2d elements and DPI sensitive UI. */ loc[0] /= dpi_fac; loc[1] /= dpi_fac; diff --git a/source/blender/editors/transform/transform_convert_object.c b/source/blender/editors/transform/transform_convert_object.c index d2585493679..5879a65eb4b 100644 --- a/source/blender/editors/transform/transform_convert_object.c +++ b/source/blender/editors/transform/transform_convert_object.c @@ -884,7 +884,7 @@ void recalcData_objects(TransInfo *t) /* TODO: autokeyframe calls need some setting to specify to add samples * (FPoints) instead of keyframes? */ if ((t->animtimer) && IS_AUTOKEY_ON(t->scene)) { - animrecord_check_state(t, ob); + animrecord_check_state(t, &ob->id); autokeyframe_object(t->context, t->scene, t->view_layer, ob, t->mode); } diff --git a/source/blender/editors/transform/transform_convert_sequencer.c b/source/blender/editors/transform/transform_convert_sequencer.c index 5964af7ed4b..226b0f84f14 100644 --- a/source/blender/editors/transform/transform_convert_sequencer.c +++ b/source/blender/editors/transform/transform_convert_sequencer.c @@ -91,8 +91,8 @@ static void SeqTransInfo(TransInfo *t, Sequence *seq, int *r_count, int *r_flag) /* *** Extend Transform *** */ int cfra = CFRA; - int left = SEQ_transform_get_left_handle_frame(seq); - int right = SEQ_transform_get_right_handle_frame(seq); + int left = SEQ_time_left_handle_frame_get(seq); + int right = SEQ_time_right_handle_frame_get(seq); if (((seq->flag & SELECT) == 0 || SEQ_transform_is_locked(channels, seq))) { *r_count = 0; @@ -173,16 +173,16 @@ static TransData *SeqToTransData( /* Use seq_tx_get_final_left() and an offset here * so transform has the left hand location of the strip. * tdsq->start_offset is used when flushing the tx data back */ - start_left = SEQ_transform_get_left_handle_frame(seq); + start_left = SEQ_time_left_handle_frame_get(seq); td2d->loc[0] = start_left; tdsq->start_offset = start_left - seq->start; /* use to apply the original location */ break; case SEQ_LEFTSEL: - start_left = SEQ_transform_get_left_handle_frame(seq); + start_left = SEQ_time_left_handle_frame_get(seq); td2d->loc[0] = start_left; break; case SEQ_RIGHTSEL: - td2d->loc[0] = SEQ_transform_get_right_handle_frame(seq); + td2d->loc[0] = SEQ_time_right_handle_frame_get(seq); break; } @@ -278,76 +278,7 @@ static void seq_transform_cancel(TransInfo *t, SeqCollection *transformed_strips if (SEQ_transform_test_overlap(seqbase, seq)) { SEQ_transform_seqbase_shuffle(seqbase, seq, t->scene); } - - SEQ_time_update_sequence(t->scene, seqbase, seq); - } -} - -static bool seq_transform_check_overlap(SeqCollection *transformed_strips) -{ - Sequence *seq; - SEQ_ITERATOR_FOREACH (seq, transformed_strips) { - if (seq->flag & SEQ_OVERLAP) { - return true; - } - } - return false; -} - -static SeqCollection *extract_standalone_strips(SeqCollection *transformed_strips) -{ - SeqCollection *collection = SEQ_collection_create(__func__); - Sequence *seq; - SEQ_ITERATOR_FOREACH (seq, transformed_strips) { - if ((seq->type & SEQ_TYPE_EFFECT) == 0 || seq->seq1 == NULL) { - SEQ_collection_append_strip(seq, collection); - } - } - return collection; -} - -/* Query strips positioned after left edge of transformed strips bound-box. */ -static SeqCollection *query_right_side_strips(ListBase *seqbase, SeqCollection *transformed_strips) -{ - int minframe = MAXFRAME; - { - Sequence *seq; - SEQ_ITERATOR_FOREACH (seq, transformed_strips) { - minframe = min_ii(minframe, seq->startdisp); - } } - - SeqCollection *collection = SEQ_collection_create(__func__); - LISTBASE_FOREACH (Sequence *, seq, seqbase) { - if ((seq->flag & SELECT) == 0 && seq->startdisp >= minframe) { - SEQ_collection_append_strip(seq, collection); - } - } - return collection; -} - -static void seq_transform_update_effects(Scene *scene, - ListBase *seqbasep, - SeqCollection *collection) -{ - Sequence *seq; - SEQ_ITERATOR_FOREACH (seq, collection) { - if ((seq->type & SEQ_TYPE_EFFECT) && (seq->seq1 || seq->seq2 || seq->seq3)) { - SEQ_time_update_sequence(scene, seqbasep, seq); - } - } -} - -/* Check if effect strips with input are transformed. */ -static bool seq_transform_check_strip_effects(SeqCollection *transformed_strips) -{ - Sequence *seq; - SEQ_ITERATOR_FOREACH (seq, transformed_strips) { - if ((seq->type & SEQ_TYPE_EFFECT) && (seq->seq1 || seq->seq2 || seq->seq3)) { - return true; - } - } - return false; } static ListBase *seqbase_active_get(const TransInfo *t) @@ -356,241 +287,15 @@ static ListBase *seqbase_active_get(const TransInfo *t) return SEQ_active_seqbase_get(ed); } -/* Offset all strips positioned after left edge of transformed strips bound-box by amount equal - * to overlap of transformed strips. */ -static void seq_transform_handle_expand_to_fit(Scene *scene, - ListBase *seqbasep, - SeqCollection *transformed_strips, - bool use_sync_markers) -{ - ListBase *markers = &scene->markers; - - SeqCollection *right_side_strips = query_right_side_strips(seqbasep, transformed_strips); - - /* Temporarily move right side strips beyond timeline boundary. */ - Sequence *seq; - SEQ_ITERATOR_FOREACH (seq, right_side_strips) { - seq->machine += MAXSEQ * 2; - } - - /* Shuffle transformed standalone strips. This is because transformed strips can overlap with - * strips on left side. */ - SeqCollection *standalone_strips = extract_standalone_strips(transformed_strips); - SEQ_transform_seqbase_shuffle_time( - standalone_strips, seqbasep, scene, markers, use_sync_markers); - SEQ_collection_free(standalone_strips); - - /* Move temporarily moved strips back to their original place and tag for shuffling. */ - SEQ_ITERATOR_FOREACH (seq, right_side_strips) { - seq->machine -= MAXSEQ * 2; - } - /* Shuffle again to displace strips on right side. Final effect shuffling is done in - * SEQ_transform_handle_overlap. */ - SEQ_transform_seqbase_shuffle_time( - right_side_strips, seqbasep, scene, markers, use_sync_markers); - seq_transform_update_effects(scene, seqbasep, right_side_strips); - SEQ_collection_free(right_side_strips); -} - -static SeqCollection *query_overwrite_targets(ListBase *seqbasep, - SeqCollection *transformed_strips) -{ - SeqCollection *collection = SEQ_query_unselected_strips(seqbasep); - - Sequence *seq, *seq_transformed; - SEQ_ITERATOR_FOREACH (seq, collection) { - bool does_overlap = false; - - SEQ_ITERATOR_FOREACH (seq_transformed, transformed_strips) { - /* Effects of transformed strips can be unselected. These must not be included. */ - if (seq == seq_transformed) { - SEQ_collection_remove_strip(seq, collection); - } - if (SEQ_transform_test_overlap_seq_seq(seq, seq_transformed)) { - does_overlap = true; - } - } - - if (!does_overlap) { - SEQ_collection_remove_strip(seq, collection); - } - } - - return collection; -} - -typedef enum eOvelapDescrition { - /* No overlap. */ - STRIP_OVERLAP_NONE, - /* Overlapping strip covers overlapped completely. */ - STRIP_OVERLAP_IS_FULL, - /* Overlapping strip is inside overlapped. */ - STRIP_OVERLAP_IS_INSIDE, - /* Partial overlap between 2 strips. */ - STRIP_OVERLAP_LEFT_SIDE, - STRIP_OVERLAP_RIGHT_SIDE, -} eOvelapDescrition; - -static eOvelapDescrition overlap_description_get(const Sequence *transformed, - const Sequence *target) -{ - if (transformed->startdisp <= target->startdisp && transformed->enddisp >= target->enddisp) { - return STRIP_OVERLAP_IS_FULL; - } - if (transformed->startdisp > target->startdisp && transformed->enddisp < target->enddisp) { - return STRIP_OVERLAP_IS_INSIDE; - } - if (transformed->startdisp <= target->startdisp && target->startdisp <= transformed->enddisp) { - return STRIP_OVERLAP_LEFT_SIDE; - } - if (transformed->startdisp <= target->enddisp && target->enddisp <= transformed->enddisp) { - return STRIP_OVERLAP_RIGHT_SIDE; - } - return STRIP_OVERLAP_NONE; -} - -/* Split strip in 3 parts, remove middle part and fit transformed inside. */ -static void seq_transform_handle_overwrite_split(Scene *scene, - ListBase *seqbasep, - const Sequence *transformed, - Sequence *target) -{ - /* Because we are doing a soft split, bmain is not used in SEQ_edit_strip_split, so we can pass - * NULL here. */ - Main *bmain = NULL; - - Sequence *split_strip = SEQ_edit_strip_split( - bmain, scene, seqbasep, target, transformed->startdisp, SEQ_SPLIT_SOFT, NULL); - SEQ_edit_strip_split( - bmain, scene, seqbasep, split_strip, transformed->enddisp, SEQ_SPLIT_SOFT, NULL); - SEQ_edit_flag_for_removal(scene, seqbasep, split_strip); - SEQ_edit_remove_flagged_sequences(scene, seqbasep); -} - -/* Trim strips by adjusting handle position. - * This is bit more complicated in case overlap happens on effect. */ -static void seq_transform_handle_overwrite_trim(Scene *scene, - ListBase *seqbasep, - const Sequence *transformed, - Sequence *target, - const eOvelapDescrition overlap) -{ - SeqCollection *targets = SEQ_query_by_reference(target, seqbasep, SEQ_query_strip_effect_chain); - - /* Expand collection by adding all target's children, effects and their children. */ - if ((target->type & SEQ_TYPE_EFFECT) != 0) { - SEQ_collection_expand(seqbasep, targets, SEQ_query_strip_effect_chain); - } - - /* Trim all non effects, that have influence on effect length which is overlapping. */ - Sequence *seq; - SEQ_ITERATOR_FOREACH (seq, targets) { - if ((seq->type & SEQ_TYPE_EFFECT) != 0 && SEQ_effect_get_num_inputs(seq->type) > 0) { - continue; - } - if (overlap == STRIP_OVERLAP_LEFT_SIDE) { - SEQ_transform_set_left_handle_frame(seq, transformed->enddisp); - } - else { - BLI_assert(overlap == STRIP_OVERLAP_RIGHT_SIDE); - SEQ_transform_set_right_handle_frame(seq, transformed->startdisp); - } - - SEQ_time_update_sequence(scene, seqbasep, seq); - } - SEQ_collection_free(targets); -} - -static void seq_transform_handle_overwrite(Scene *scene, - ListBase *seqbasep, - SeqCollection *transformed_strips) -{ - SeqCollection *targets = query_overwrite_targets(seqbasep, transformed_strips); - SeqCollection *strips_to_delete = SEQ_collection_create(__func__); - - Sequence *target; - Sequence *transformed; - SEQ_ITERATOR_FOREACH (target, targets) { - SEQ_ITERATOR_FOREACH (transformed, transformed_strips) { - if (transformed->machine != target->machine) { - continue; - } - - const eOvelapDescrition overlap = overlap_description_get(transformed, target); - - if (overlap == STRIP_OVERLAP_IS_FULL) { - SEQ_collection_append_strip(target, strips_to_delete); - } - else if (overlap == STRIP_OVERLAP_IS_INSIDE) { - seq_transform_handle_overwrite_split(scene, seqbasep, transformed, target); - } - else if (ELEM(overlap, STRIP_OVERLAP_LEFT_SIDE, STRIP_OVERLAP_RIGHT_SIDE)) { - seq_transform_handle_overwrite_trim(scene, seqbasep, transformed, target, overlap); - } - } - } - - SEQ_collection_free(targets); - - /* Remove covered strips. This must be done in separate loop, because `SEQ_edit_strip_split()` - * also uses `SEQ_edit_remove_flagged_sequences()`. See T91096. */ - if (SEQ_collection_len(strips_to_delete) > 0) { - Sequence *seq; - SEQ_ITERATOR_FOREACH (seq, strips_to_delete) { - SEQ_edit_flag_for_removal(scene, seqbasep, seq); - } - SEQ_edit_remove_flagged_sequences(scene, seqbasep); - } - SEQ_collection_free(strips_to_delete); -} - -static void seq_transform_handle_overlap_shuffle(Scene *scene, - ListBase *seqbasep, - SeqCollection *transformed_strips, - bool use_sync_markers) -{ - ListBase *markers = &scene->markers; - - /* Shuffle non strips with no effects attached. */ - SeqCollection *standalone_strips = extract_standalone_strips(transformed_strips); - SEQ_transform_seqbase_shuffle_time( - standalone_strips, seqbasep, scene, markers, use_sync_markers); - SEQ_collection_free(standalone_strips); -} - -void SEQ_transform_handle_overlap(Scene *scene, - ListBase *seqbasep, - SeqCollection *transformed_strips, - bool use_sync_markers) +static bool seq_transform_check_overlap(SeqCollection *transformed_strips) { - const eSeqOverlapMode overlap_mode = SEQ_tool_settings_overlap_mode_get(scene); - - switch (overlap_mode) { - case SEQ_OVERLAP_EXPAND: - seq_transform_handle_expand_to_fit(scene, seqbasep, transformed_strips, use_sync_markers); - break; - case SEQ_OVERLAP_OVERWRITE: - seq_transform_handle_overwrite(scene, seqbasep, transformed_strips); - break; - case SEQ_OVERLAP_SHUFFLE: - seq_transform_handle_overlap_shuffle(scene, seqbasep, transformed_strips, use_sync_markers); - break; - } - - if (seq_transform_check_strip_effects(transformed_strips)) { - /* Update effect strips based on strips just moved in time. */ - seq_transform_update_effects(scene, seqbasep, transformed_strips); - } - - /* If any effects still overlap, we need to move them up. - * In some cases other strips can be overlapping still, see T90646. */ Sequence *seq; SEQ_ITERATOR_FOREACH (seq, transformed_strips) { - if (SEQ_transform_test_overlap(seqbasep, seq)) { - SEQ_transform_seqbase_shuffle(seqbasep, seq, scene); + if (seq->flag & SEQ_OVERLAP) { + return true; } - seq->flag &= ~SEQ_OVERLAP; } + return false; } static SeqCollection *seq_transform_collection_from_transdata(TransDataContainer *tc) @@ -627,18 +332,17 @@ static void freeSeqData(TransInfo *t, TransDataContainer *tc, TransCustomData *c return; } + TransSeq *ts = tc->custom.type.data; ListBase *seqbasep = seqbase_active_get(t); Scene *scene = t->scene; const bool use_sync_markers = (((SpaceSeq *)t->area->spacedata.first)->flag & SEQ_MARKER_TRANS) != 0; if (seq_transform_check_overlap(transformed_strips)) { - SEQ_transform_handle_overlap(scene, seqbasep, transformed_strips, use_sync_markers); + SEQ_transform_handle_overlap( + scene, seqbasep, transformed_strips, ts->time_dependent_strips, use_sync_markers); } - seq_transform_update_effects(scene, seqbasep, transformed_strips); SEQ_collection_free(transformed_strips); - - SEQ_sort(ed->seqbasep); DEG_id_tag_update(&t->scene->id, ID_RECALC_SEQUENCER_STRIPS); free_transform_custom_data(custom_data); } @@ -662,7 +366,10 @@ typedef enum SeqInputSide { static Sequence *effect_input_get(Sequence *effect, SeqInputSide side) { Sequence *input = effect->seq1; - if (effect->seq2 && (effect->seq2->startdisp - effect->seq1->startdisp) * side > 0) { + if (effect->seq2 && (SEQ_time_left_handle_frame_get(effect->seq2) - + SEQ_time_left_handle_frame_get(effect->seq1)) * + side > + 0) { input = effect->seq2; } return input; @@ -825,23 +532,6 @@ void createTransSeqData(TransInfo *t) /** \name UVs Transform Flush * \{ */ -/* commented _only_ because the meta may have animation data which - * needs moving too T28158. */ - -BLI_INLINE void trans_update_seq(Scene *sce, Sequence *seq, int old_start, int sel_flag) -{ - /* Calculate this strip and all nested strips. - * Children are ALWAYS transformed first so we don't need to do this in another loop. - */ - - ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(sce)); - SEQ_time_update_sequence(sce, seqbase, seq); - - if (sel_flag == SELECT) { - SEQ_offset_animdata(sce, seq, seq->start - old_start); - } -} - static void view2d_edge_pan_loc_compensate(TransInfo *t, float loc_in[2], float r_loc[2]) { TransSeq *ts = (TransSeq *)TRANS_DATA_CONTAINER_FIRST_SINGLE(t)->custom.type.data; @@ -914,24 +604,24 @@ static void flushTransSeq(TransInfo *t) break; } case SEQ_LEFTSEL: { /* No vertical transform. */ - int old_startdisp = seq->startdisp; - SEQ_transform_set_left_handle_frame(seq, new_frame); - SEQ_transform_handle_xlimits(seq, tdsq->flag & SEQ_LEFTSEL, tdsq->flag & SEQ_RIGHTSEL); - SEQ_transform_fix_single_image_seq_offsets(seq); - SEQ_time_update_sequence(t->scene, seqbasep, seq); - if (abs(seq->startdisp - old_startdisp) > abs(max_offset)) { - max_offset = seq->startdisp - old_startdisp; + int old_startdisp = SEQ_time_left_handle_frame_get(seq); + SEQ_time_left_handle_frame_set(t->scene, seq, new_frame); + SEQ_transform_handle_xlimits( + t->scene, seq, tdsq->flag & SEQ_LEFTSEL, tdsq->flag & SEQ_RIGHTSEL); + SEQ_transform_fix_single_image_seq_offsets(t->scene, seq); + if (abs(SEQ_time_left_handle_frame_get(seq) - old_startdisp) > abs(max_offset)) { + max_offset = SEQ_time_left_handle_frame_get(seq) - old_startdisp; } break; } case SEQ_RIGHTSEL: { /* No vertical transform. */ - int old_enddisp = seq->enddisp; - SEQ_transform_set_right_handle_frame(seq, new_frame); - SEQ_transform_handle_xlimits(seq, tdsq->flag & SEQ_LEFTSEL, tdsq->flag & SEQ_RIGHTSEL); - SEQ_transform_fix_single_image_seq_offsets(seq); - SEQ_time_update_sequence(t->scene, seqbasep, seq); - if (abs(seq->enddisp - old_enddisp) > abs(max_offset)) { - max_offset = seq->enddisp - old_enddisp; + int old_enddisp = SEQ_time_right_handle_frame_get(seq); + SEQ_time_right_handle_frame_set(t->scene, seq, new_frame); + SEQ_transform_handle_xlimits( + t->scene, seq, tdsq->flag & SEQ_LEFTSEL, tdsq->flag & SEQ_RIGHTSEL); + SEQ_transform_fix_single_image_seq_offsets(t->scene, seq); + if (abs(SEQ_time_right_handle_frame_get(seq) - old_enddisp) > abs(max_offset)) { + max_offset = SEQ_time_right_handle_frame_get(seq) - old_enddisp; } break; } @@ -945,15 +635,6 @@ static void flushTransSeq(TransInfo *t) SEQ_offset_animdata(t->scene, seq, max_offset); } - /* Update effect length and position. */ - if (ELEM(t->mode, TFM_SEQ_SLIDE, TFM_TIME_TRANSLATE)) { - for (seq = seqbasep->first; seq; seq = seq->next) { - if (seq->seq1 || seq->seq2 || seq->seq3) { - SEQ_time_update_sequence(t->scene, seqbasep, seq); - } - } - } - /* need to do the overlap check in a new loop otherwise adjacent strips * will not be updated and we'll get false positives */ SeqCollection *transformed_strips = seq_transform_collection_from_transdata(tc); diff --git a/source/blender/editors/transform/transform_convert_sequencer_image.c b/source/blender/editors/transform/transform_convert_sequencer_image.c index cbc2cab0a7a..76c6632039a 100644 --- a/source/blender/editors/transform/transform_convert_sequencer_image.c +++ b/source/blender/editors/transform/transform_convert_sequencer_image.c @@ -154,6 +154,42 @@ void createTransSeqImageData(TransInfo *t) SEQ_collection_free(strips); } +static bool autokeyframe_sequencer_image(bContext *C, + Scene *scene, + StripTransform *transform, + const int tmode) +{ + PointerRNA ptr; + PropertyRNA *prop; + RNA_pointer_create(&scene->id, &RNA_SequenceTransform, transform, &ptr); + + const bool around_cursor = scene->toolsettings->sequencer_tool_settings->pivot_point == + V3D_AROUND_CURSOR; + const bool do_loc = tmode == TFM_TRANSLATION || around_cursor; + const bool do_rot = tmode == TFM_ROTATION; + const bool do_scale = tmode == TFM_RESIZE; + + bool changed = false; + if (do_rot) { + prop = RNA_struct_find_property(&ptr, "rotation"); + changed |= ED_autokeyframe_property(C, scene, &ptr, prop, -1, CFRA, false); + } + if (do_loc) { + prop = RNA_struct_find_property(&ptr, "offset_x"); + changed |= ED_autokeyframe_property(C, scene, &ptr, prop, -1, CFRA, false); + prop = RNA_struct_find_property(&ptr, "offset_y"); + changed |= ED_autokeyframe_property(C, scene, &ptr, prop, -1, CFRA, false); + } + if (do_scale) { + prop = RNA_struct_find_property(&ptr, "scale_x"); + changed |= ED_autokeyframe_property(C, scene, &ptr, prop, -1, CFRA, false); + prop = RNA_struct_find_property(&ptr, "scale_y"); + changed |= ED_autokeyframe_property(C, scene, &ptr, prop, -1, CFRA, false); + } + + return changed; +} + void recalcData_sequencer_image(TransInfo *t) { TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); @@ -187,6 +223,7 @@ void recalcData_sequencer_image(TransInfo *t) copy_v2_v2(translation, tdseq->orig_origin_position); sub_v2_v2(translation, origin); mul_v2_v2(translation, mirror); + translation[0] *= t->scene->r.yasp / t->scene->r.xasp; transform->xofs = tdseq->orig_translation[0] - translation[0]; transform->yofs = tdseq->orig_translation[1] - translation[1]; @@ -197,53 +234,39 @@ void recalcData_sequencer_image(TransInfo *t) /* Rotation. Scaling can cause negative rotation. */ if (t->mode == TFM_ROTATION) { - const float orig_dir[2] = {cosf(tdseq->orig_rotation), sinf(tdseq->orig_rotation)}; - float rotation = angle_signed_v2v2(handle_x, orig_dir) * mirror[0] * mirror[1]; - transform->rotation = tdseq->orig_rotation + rotation; - transform->rotation += DEG2RAD(360.0); - transform->rotation = fmod(transform->rotation, DEG2RAD(360.0)); + transform->rotation = tdseq->orig_rotation - t->values_final[0]; + } + + if ((t->animtimer) && IS_AUTOKEY_ON(t->scene)) { + animrecord_check_state(t, &t->scene->id); + autokeyframe_sequencer_image(t->context, t->scene, transform, t->mode); } + SEQ_relations_invalidate_cache_preprocessed(t->scene, seq); } } void special_aftertrans_update__sequencer_image(bContext *UNUSED(C), TransInfo *t) { - if (t->state == TRANS_CANCEL) { - return; - } TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); TransData *td = NULL; TransData2D *td2d = NULL; int i; - PointerRNA ptr; - PropertyRNA *prop; - for (i = 0, td = tc->data, td2d = tc->data_2d; i < tc->data_len; i++, td++, td2d++) { TransDataSeq *tdseq = td->extra; Sequence *seq = tdseq->seq; StripTransform *transform = seq->strip->transform; - Scene *scene = t->scene; - - RNA_pointer_create(&scene->id, &RNA_SequenceTransform, transform, &ptr); - - if (t->mode == TFM_ROTATION) { - prop = RNA_struct_find_property(&ptr, "rotation"); - ED_autokeyframe_property(t->context, scene, &ptr, prop, -1, CFRA); + if (t->state == TRANS_CANCEL) { + if (t->mode == TFM_ROTATION) { + transform->rotation = tdseq->orig_rotation; + } + continue; } - if (t->mode == TFM_TRANSLATION) { - prop = RNA_struct_find_property(&ptr, "offset_x"); - ED_autokeyframe_property(t->context, scene, &ptr, prop, -1, CFRA); - prop = RNA_struct_find_property(&ptr, "offset_y"); - ED_autokeyframe_property(t->context, scene, &ptr, prop, -1, CFRA); - } - if (t->mode == TFM_RESIZE) { - prop = RNA_struct_find_property(&ptr, "scale_x"); - ED_autokeyframe_property(t->context, scene, &ptr, prop, -1, CFRA); - prop = RNA_struct_find_property(&ptr, "scale_y"); - ED_autokeyframe_property(t->context, scene, &ptr, prop, -1, CFRA); + + if (IS_AUTOKEY_ON(t->scene)) { + autokeyframe_sequencer_image(t->context, t->scene, transform, t->mode); } } } diff --git a/source/blender/editors/transform/transform_convert_tracking.c b/source/blender/editors/transform/transform_convert_tracking.c index 45f17512c09..d447cd71a40 100644 --- a/source/blender/editors/transform/transform_convert_tracking.c +++ b/source/blender/editors/transform/transform_convert_tracking.c @@ -621,7 +621,7 @@ static void flushTransTracking(TransInfo *t) TransData *td; TransData2D *td2d; TransDataTracking *tdt; - int a; + int td_index; if (t->state == TRANS_CANCEL) { cancelTransTracking(t); @@ -630,8 +630,9 @@ static void flushTransTracking(TransInfo *t) TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); /* flush to 2d vector from internally used 3d vector */ - for (a = 0, td = tc->data, td2d = tc->data_2d, tdt = tc->custom.type.data; a < tc->data_len; - a++, td2d++, td++, tdt++) { + for (td_index = 0, td = tc->data, td2d = tc->data_2d, tdt = tc->custom.type.data; + td_index < tc->data_len; + td_index++, td2d++, td++, tdt++) { if (tdt->mode == transDataTracking_ModeTracks) { float loc2d[2]; @@ -655,7 +656,7 @@ static void flushTransTracking(TransInfo *t) if (!tdt->smarkers) { tdt->smarkers = MEM_callocN(sizeof(*tdt->smarkers) * tdt->markersnr, "flushTransTracking markers"); - for (a = 0; a < tdt->markersnr; a++) { + for (int a = 0; a < tdt->markersnr; a++) { copy_v2_v2(tdt->smarkers[a], tdt->markers[a].pos); } } @@ -665,7 +666,7 @@ static void flushTransTracking(TransInfo *t) sub_v2_v2v2(d2, loc2d, tdt->srelative); - for (a = 0; a < tdt->markersnr; a++) { + for (int a = 0; a < tdt->markersnr; a++) { add_v2_v2v2(tdt->markers[a].pos, tdt->smarkers[a], d2); } @@ -712,23 +713,23 @@ void recalcData_tracking(TransInfo *t) if (t->mode == TFM_TRANSLATION) { if (TRACK_AREA_SELECTED(track, TRACK_AREA_PAT)) { - BKE_tracking_marker_clamp(marker, CLAMP_PAT_POS); + BKE_tracking_marker_clamp_pattern_position(marker); } if (TRACK_AREA_SELECTED(track, TRACK_AREA_SEARCH)) { - BKE_tracking_marker_clamp(marker, CLAMP_SEARCH_POS); + BKE_tracking_marker_clamp_search_position(marker); } } else if (t->mode == TFM_RESIZE) { if (TRACK_AREA_SELECTED(track, TRACK_AREA_PAT)) { - BKE_tracking_marker_clamp(marker, CLAMP_PAT_DIM); + BKE_tracking_marker_clamp_search_size(marker); } if (TRACK_AREA_SELECTED(track, TRACK_AREA_SEARCH)) { - BKE_tracking_marker_clamp(marker, CLAMP_SEARCH_DIM); + BKE_tracking_marker_clamp_search_size(marker); } } else if (t->mode == TFM_ROTATION) { if (TRACK_AREA_SELECTED(track, TRACK_AREA_PAT)) { - BKE_tracking_marker_clamp(marker, CLAMP_PAT_POS); + BKE_tracking_marker_clamp_pattern_position(marker); } } } diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index 975dbc2e986..e45cac36736 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -358,6 +358,10 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve } else if (t->spacetype == SPACE_SEQ && region->regiontype == RGN_TYPE_PREVIEW) { t->options |= CTX_SEQUENCER_IMAGE; + + /* Needed for autokeying transforms in preview during playback. */ + bScreen *animscreen = ED_screen_animation_playing(CTX_wm_manager(C)); + t->animtimer = (animscreen) ? animscreen->animtimer : NULL; } setTransformViewAspect(t, t->aspect); diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c index aa8dad2b95f..5b749e05052 100644 --- a/source/blender/editors/transform/transform_gizmo_3d.c +++ b/source/blender/editors/transform/transform_gizmo_3d.c @@ -45,7 +45,6 @@ #include "WM_api.h" #include "WM_message.h" #include "WM_types.h" -#include "wm.h" #include "ED_armature.h" #include "ED_gizmo_library.h" @@ -71,6 +70,10 @@ #include "GPU_state.h" +static void gizmo_refresh_from_matrix(wmGizmoGroup *gzgroup, + const float twmat[4][4], + const float scale[3]); + /* return codes for select, and drawing flags */ #define MAN_TRANS_X (1 << 0) @@ -155,6 +158,9 @@ typedef struct GizmoGroup { float viewinv_m3[3][3]; } prev; + /* Only for Rotate operator. */ + float rotation; + struct wmGizmo *gizmos[MAN_AXIS_LAST]; } GizmoGroup; @@ -1269,103 +1275,31 @@ static void gizmo_xform_message_subscribe(wmGizmoGroup *gzgroup, WM_msg_subscribe_rna_anon_prop(mbus, EditBone, lock, &msg_sub_value_gz_tag_refresh); } -void drawDial3d(const TransInfo *t) +static void gizmo_3d_dial_matrixbasis_calc(const ARegion *region, + float axis[3], + float center_global[3], + float mval_init[2], + float r_mat_basis[4][4]) { - if (t->mode == TFM_ROTATION && t->spacetype == SPACE_VIEW3D) { - if (t->options & CTX_PAINT_CURVE) { - /* Matrices are in the screen space. Not supported. */ - return; - } - - wmGizmo *gz = wm_gizmomap_modal_get(t->region->gizmo_map); - if (gz == NULL) { - /* We only draw Dial3d if the operator has been called by a gizmo. */ - return; - } - - float mat_basis[4][4]; - float mat_final[4][4]; - float color[4]; - float increment = 0.0f; - float line_with = GIZMO_AXIS_LINE_WIDTH + 1.0f; - float scale = UI_DPI_FAC * U.gizmo_size; - - int axis_idx; - - const TransCon *tc = &(t->con); - if (tc->mode & CON_APPLY) { - if (tc->mode & CON_AXIS0) { - axis_idx = MAN_AXIS_ROT_X; - negate_v3_v3(mat_basis[2], t->spacemtx[0]); - } - else if (tc->mode & CON_AXIS1) { - axis_idx = MAN_AXIS_ROT_Y; - negate_v3_v3(mat_basis[2], t->spacemtx[1]); - } - else { - BLI_assert((tc->mode & CON_AXIS2) != 0); - axis_idx = MAN_AXIS_ROT_Z; - negate_v3_v3(mat_basis[2], t->spacemtx[2]); - } - } - else { - axis_idx = MAN_AXIS_ROT_C; - copy_v3_v3(mat_basis[2], t->spacemtx[t->orient_axis]); - scale *= 1.2f; - line_with -= 1.0f; - } - - copy_v3_v3(mat_basis[3], t->center_global); - mat_basis[2][3] = -dot_v3v3(mat_basis[2], mat_basis[3]); - - if (ED_view3d_win_to_3d_on_plane( - t->region, mat_basis[2], (float[2]){UNPACK2(t->mouse.imval)}, false, mat_basis[1])) { - sub_v3_v3(mat_basis[1], mat_basis[3]); - normalize_v3(mat_basis[1]); - cross_v3_v3v3(mat_basis[0], mat_basis[1], mat_basis[2]); - } - else { - /* The plane and the mouse direction are parallel. - * Calculate a matrix orthogonal to the axis. */ - ortho_basis_v3v3_v3(mat_basis[0], mat_basis[1], mat_basis[2]); - } - - mat_basis[0][3] = 0.0f; - mat_basis[1][3] = 0.0f; - mat_basis[2][3] = 0.0f; - mat_basis[3][3] = 1.0f; - - copy_m4_m4(mat_final, mat_basis); - scale *= ED_view3d_pixel_size_no_ui_scale(t->region->regiondata, mat_final[3]); - mul_mat3_m4_fl(mat_final, scale); + copy_v3_v3(r_mat_basis[2], axis); + copy_v3_v3(r_mat_basis[3], center_global); + r_mat_basis[2][3] = -dot_v3v3(axis, center_global); - if (activeSnap(t) && (!transformModeUseSnap(t) || - (t->tsnap.mode & (SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID)))) { - increment = (t->modifiers & MOD_PRECISION) ? t->snap[1] : t->snap[0]; - } - - BLI_assert(axis_idx >= MAN_AXIS_RANGE_ROT_START && axis_idx < MAN_AXIS_RANGE_ROT_END); - gizmo_get_axis_color(axis_idx, NULL, color, color); - - GPU_depth_test(GPU_DEPTH_NONE); - GPU_blend(GPU_BLEND_ALPHA); - GPU_line_smooth(true); - - ED_gizmotypes_dial_3d_draw_util(mat_basis, - mat_final, - line_with, - color, - false, - &(struct Dial3dParams){ - .draw_options = ED_GIZMO_DIAL_DRAW_FLAG_ANGLE_VALUE, - .angle_delta = t->values_final[0], - .angle_increment = increment, - }); - - GPU_line_smooth(false); - GPU_depth_test(GPU_DEPTH_LESS_EQUAL); - GPU_blend(GPU_BLEND_NONE); + if (ED_view3d_win_to_3d_on_plane(region, axis, mval_init, false, r_mat_basis[1])) { + sub_v3_v3(r_mat_basis[1], center_global); + normalize_v3(r_mat_basis[1]); + cross_v3_v3v3(r_mat_basis[0], r_mat_basis[1], r_mat_basis[2]); + } + else { + /* The plane and the mouse direction are parallel. + * Calculate a matrix orthogonal to the axis. */ + ortho_basis_v3v3_v3(r_mat_basis[0], r_mat_basis[1], r_mat_basis[2]); } + + r_mat_basis[0][3] = 0.0f; + r_mat_basis[1][3] = 0.0f; + r_mat_basis[2][3] = 0.0f; + r_mat_basis[3][3] = 1.0f; } /** \} */ @@ -1374,6 +1308,23 @@ void drawDial3d(const TransInfo *t) /** \name Transform Gizmo * \{ */ +/** Scale of the two-axis planes. */ +#define MAN_AXIS_SCALE_PLANE_SCALE 0.07f +/** Offset of the two-axis planes, depends on the gizmos scale. Define to avoid repeating. */ +#define MAN_AXIS_SCALE_PLANE_OFFSET 7.0f + +static void rotation_get_fn(const wmGizmo *UNUSED(gz), wmGizmoProperty *gz_prop, void *value) +{ + const GizmoGroup *ggd = (const GizmoGroup *)gz_prop->custom_func.user_data; + *(float *)value = ggd->rotation; +} + +static void rotation_set_fn(const wmGizmo *UNUSED(gz), wmGizmoProperty *gz_prop, const void *value) +{ + GizmoGroup *ggd = (GizmoGroup *)gz_prop->custom_func.user_data; + ggd->rotation = *(const float *)value; +} + static GizmoGroup *gizmogroup_init(wmGizmoGroup *gzgroup) { GizmoGroup *ggd; @@ -1384,6 +1335,9 @@ static GizmoGroup *gizmogroup_init(wmGizmoGroup *gzgroup) const wmGizmoType *gzt_dial = WM_gizmotype_find("GIZMO_GT_dial_3d", true); const wmGizmoType *gzt_prim = WM_gizmotype_find("GIZMO_GT_primitive_3d", true); + wmGizmoPropertyFnParams params = { + .value_get_fn = rotation_get_fn, .value_set_fn = rotation_set_fn, .user_data = ggd}; + #define GIZMO_NEW_ARROW(v, draw_style) \ { \ ggd->gizmos[v] = WM_gizmo_new_ptr(gzt_arrow, gzgroup, NULL); \ @@ -1394,6 +1348,7 @@ static GizmoGroup *gizmogroup_init(wmGizmoGroup *gzgroup) { \ ggd->gizmos[v] = WM_gizmo_new_ptr(gzt_dial, gzgroup, NULL); \ RNA_enum_set(ggd->gizmos[v]->ptr, "draw_options", draw_options); \ + WM_gizmo_target_property_def_func(ggd->gizmos[v], "offset", ¶ms); \ } \ ((void)0) #define GIZMO_NEW_PRIM(v, draw_style) \ @@ -1461,18 +1416,104 @@ static int gizmo_modal(bContext *C, ARegion *region = CTX_wm_region(C); RegionView3D *rv3d = region->regiondata; - struct TransformBounds tbounds; - - if (ED_transform_calc_gizmo_stats(C, - &(struct TransformCalcParams){ - .use_only_center = true, - }, - &tbounds)) { - gizmo_prepare_mat(C, rv3d, &tbounds); - WM_gizmo_set_matrix_location(widget, rv3d->twmat[3]); + wmGizmoGroup *gzgroup = widget->parent_gzgroup; + + /* Recalculating the orientation has two problems. + * - The matrix calculated based on the transformed selection may not match the matrix + * that was set when transform started. + * - Inspecting the selection for every update is expensive (for *every* redraw). + * + * Instead, use #transform_apply_matrix to transform `rv3d->twmat` or the final scale value + * when scaling. + */ + if (false) { + struct TransformBounds tbounds; + + if (ED_transform_calc_gizmo_stats(C, + &(struct TransformCalcParams){ + .use_only_center = true, + }, + &tbounds)) { + gizmo_prepare_mat(C, rv3d, &tbounds); + for (wmGizmo *gz = gzgroup->gizmos.first; gz; gz = gz->next) { + WM_gizmo_set_matrix_location(gz, rv3d->twmat[3]); + } + } } + else { + GizmoGroup *ggd = gzgroup->customdata; + + short axis_type = 0; + MAN_ITER_AXES_BEGIN (axis, axis_idx) { + if (axis == widget) { + axis_type = gizmo_get_axis_type(axis_idx); + break; + } + } + MAN_ITER_AXES_END; + + /* Showing axes which aren't being manipulated doesn't always work so well. + * + * For rotate: global axis will reset after finish. + * Also, gimbal axis isn't properly recalculated while transforming. + */ + if (axis_type == MAN_AXES_ROTATE) { + MAN_ITER_AXES_BEGIN (axis, axis_idx) { + if (axis == widget) { + continue; + } + + bool is_plane_dummy; + const uint aidx_norm = gizmo_orientation_axis(axis_idx, &is_plane_dummy); + /* Always show the axis-aligned handle as it's distracting when it's disabled. */ + if (aidx_norm == 3) { + continue; + } + WM_gizmo_set_flag(axis, WM_GIZMO_HIDDEN, true); + } + MAN_ITER_AXES_END; + } - ED_region_tag_redraw_editor_overlays(region); + wmWindow *win = CTX_wm_window(C); + wmOperator *op = NULL; + for (int i = 0; i < widget->op_data_len; i++) { + wmGizmoOpElem *gzop = WM_gizmo_operator_get(widget, i); + op = WM_operator_find_modal_by_type(win, gzop->type); + if (op != NULL) { + break; + } + } + + if (op != NULL) { + float twmat[4][4]; + float scale_buf[3]; + float *scale = NULL; + bool update = false; + copy_m4_m4(twmat, rv3d->twmat); + + if (axis_type == MAN_AXES_SCALE) { + scale = scale_buf; + transform_final_value_get(op->customdata, scale, 3); + update = true; + } + else if (axis_type == MAN_AXES_ROTATE) { + transform_final_value_get(op->customdata, &ggd->rotation, 1); + if (widget != ggd->gizmos[MAN_AXIS_ROT_C]) { + ggd->rotation *= -1; + } + RNA_float_set( + widget->ptr, "incremental_angle", transform_snap_increment_get(op->customdata)); + } + else if (transform_apply_matrix(op->customdata, twmat)) { + update = true; + } + + if (update) { + gizmo_refresh_from_matrix(gzgroup, twmat, scale); + ED_region_tag_redraw_editor_overlays(region); + } + } + } return OPERATOR_RUNNING_MODAL; } @@ -1524,9 +1565,8 @@ static void gizmogroup_init_properties_from_twtype(wmGizmoGroup *gzgroup) case MAN_AXIS_SCALE_XY: case MAN_AXIS_SCALE_YZ: case MAN_AXIS_SCALE_ZX: { - const float ofs_ax = 7.0f; - const float ofs[3] = {ofs_ax, ofs_ax, 0.0f}; - WM_gizmo_set_scale(axis, 0.07f); + const float ofs[3] = {MAN_AXIS_SCALE_PLANE_OFFSET, MAN_AXIS_SCALE_PLANE_OFFSET, 0.0f}; + WM_gizmo_set_scale(axis, MAN_AXIS_SCALE_PLANE_SCALE); WM_gizmo_set_matrix_offset_location(axis, ofs); WM_gizmo_set_flag(axis, WM_GIZMO_DRAW_OFFSET_SCALE, true); break; @@ -1638,46 +1678,23 @@ static void WIDGETGROUP_gizmo_setup(const bContext *C, wmGizmoGroup *gzgroup) gizmogroup_init_properties_from_twtype(gzgroup); } -static void WIDGETGROUP_gizmo_refresh(const bContext *C, wmGizmoGroup *gzgroup) +/** + * Set properties for axes. + * + * \param twmat: The transform matrix (typically #RegionView3D.twmat). + * \param scale: Optional scale, to show scale while modally dragging the scale handles. + */ +static void gizmo_refresh_from_matrix(wmGizmoGroup *gzgroup, + const float twmat[4][4], + const float scale[3]) { GizmoGroup *ggd = gzgroup->customdata; - Scene *scene = CTX_data_scene(C); - ScrArea *area = CTX_wm_area(C); - View3D *v3d = area->spacedata.first; - ARegion *region = CTX_wm_region(C); - RegionView3D *rv3d = region->regiondata; - struct TransformBounds tbounds; - - if (ggd->use_twtype_refresh) { - ggd->twtype = v3d->gizmo_show_object & ggd->twtype_init; - if (ggd->twtype != ggd->twtype_prev) { - ggd->twtype_prev = ggd->twtype; - gizmogroup_init_properties_from_twtype(gzgroup); - } - } - - const int orient_index = BKE_scene_orientation_get_index_from_flag(scene, ggd->twtype_init); - - /* skip, we don't draw anything anyway */ - if ((ggd->all_hidden = (ED_transform_calc_gizmo_stats(C, - &(struct TransformCalcParams){ - .use_only_center = true, - .orientation_index = orient_index + 1, - }, - &tbounds) == 0))) { - return; - } - - gizmo_prepare_mat(C, rv3d, &tbounds); - - /* *** set properties for axes *** */ MAN_ITER_AXES_BEGIN (axis, axis_idx) { const short axis_type = gizmo_get_axis_type(axis_idx); const int aidx_norm = gizmo_orientation_axis(axis_idx, NULL); - WM_gizmo_set_matrix_location(axis, rv3d->twmat[3]); - + WM_gizmo_set_matrix_location(axis, twmat[3]); switch (axis_idx) { case MAN_AXIS_TRANS_X: case MAN_AXIS_TRANS_Y: @@ -1690,8 +1707,17 @@ static void WIDGETGROUP_gizmo_refresh(const bContext *C, wmGizmoGroup *gzgroup) gizmo_line_range(ggd->twtype, axis_type, &start_co[2], &len); - WM_gizmo_set_matrix_rotation_from_z_axis(axis, rv3d->twmat[aidx_norm]); - RNA_float_set(axis->ptr, "length", len); + const float *z_axis = twmat[aidx_norm]; + if (axis_type == MAN_AXES_SCALE) { + /* Scale handles are cubes that don't look right when not aligned with other axes. + * This is noticeable when the axis is rotated to something besides the global-axis. */ + const int aidx_norm_y = (aidx_norm + 2) % 3; + const float *y_axis = twmat[aidx_norm_y]; + WM_gizmo_set_matrix_rotation_from_yz_axis(axis, y_axis, z_axis); + } + else { + WM_gizmo_set_matrix_rotation_from_z_axis(axis, z_axis); + } if (axis_idx >= MAN_AXIS_RANGE_TRANS_START && axis_idx < MAN_AXIS_RANGE_TRANS_END) { if (ggd->twtype & V3D_GIZMO_SHOW_OBJECT_ROTATE) { @@ -1699,24 +1725,56 @@ static void WIDGETGROUP_gizmo_refresh(const bContext *C, wmGizmoGroup *gzgroup) start_co[2] += 0.215f; } } + + if (scale) { + if (axis_type == MAN_AXES_SCALE) { + len = ((start_co[2] + len) * scale[aidx_norm]) - start_co[2]; + } + } + + RNA_float_set(axis->ptr, "length", len); + WM_gizmo_set_matrix_offset_location(axis, start_co); + WM_gizmo_set_flag(axis, WM_GIZMO_DRAW_OFFSET_SCALE, true); + break; } case MAN_AXIS_ROT_X: case MAN_AXIS_ROT_Y: case MAN_AXIS_ROT_Z: - WM_gizmo_set_matrix_rotation_from_z_axis(axis, rv3d->twmat[aidx_norm]); - break; + case MAN_AXIS_ROT_C: { + if (axis_idx != MAN_AXIS_ROT_C) { + WM_gizmo_set_matrix_rotation_from_z_axis(axis, twmat[aidx_norm]); + } + + /* Remove #ED_GIZMO_DIAL_DRAW_FLAG_ANGLE_VALUE. It is used only for modal drawing. */ + PropertyRNA *prop = RNA_struct_find_property(axis->ptr, "draw_options"); + RNA_property_enum_set(axis->ptr, + prop, + RNA_property_enum_get(axis->ptr, prop) & + ~ED_GIZMO_DIAL_DRAW_FLAG_ANGLE_VALUE); + } break; case MAN_AXIS_TRANS_XY: case MAN_AXIS_TRANS_YZ: case MAN_AXIS_TRANS_ZX: case MAN_AXIS_SCALE_XY: case MAN_AXIS_SCALE_YZ: case MAN_AXIS_SCALE_ZX: { - const float *y_axis = rv3d->twmat[aidx_norm - 1 < 0 ? 2 : aidx_norm - 1]; - const float *z_axis = rv3d->twmat[aidx_norm]; + const int aidx_norm_x = (aidx_norm + 1) % 3; + const int aidx_norm_y = (aidx_norm + 2) % 3; + const float *y_axis = twmat[aidx_norm_y]; + const float *z_axis = twmat[aidx_norm]; WM_gizmo_set_matrix_rotation_from_yz_axis(axis, y_axis, z_axis); + + if (axis_type == MAN_AXES_SCALE) { + float ofs[3] = {MAN_AXIS_SCALE_PLANE_OFFSET, MAN_AXIS_SCALE_PLANE_OFFSET, 0.0f}; + if (scale) { + ofs[0] *= scale[aidx_norm_x]; + ofs[1] *= scale[aidx_norm_y]; + } + WM_gizmo_set_matrix_offset_location(axis, ofs); + } break; } } @@ -1733,6 +1791,49 @@ static void WIDGETGROUP_gizmo_refresh(const bContext *C, wmGizmoGroup *gzgroup) } } +static void WIDGETGROUP_gizmo_refresh(const bContext *C, wmGizmoGroup *gzgroup) +{ + ARegion *region = CTX_wm_region(C); + + { + wmGizmo *gz = WM_gizmomap_get_modal(region->gizmo_map); + if (gz && gz->parent_gzgroup == gzgroup) { + return; + } + } + + GizmoGroup *ggd = gzgroup->customdata; + Scene *scene = CTX_data_scene(C); + ScrArea *area = CTX_wm_area(C); + View3D *v3d = area->spacedata.first; + RegionView3D *rv3d = region->regiondata; + struct TransformBounds tbounds; + + if (ggd->use_twtype_refresh) { + ggd->twtype = v3d->gizmo_show_object & ggd->twtype_init; + if (ggd->twtype != ggd->twtype_prev) { + ggd->twtype_prev = ggd->twtype; + gizmogroup_init_properties_from_twtype(gzgroup); + } + } + + const int orient_index = BKE_scene_orientation_get_index_from_flag(scene, ggd->twtype_init); + + /* skip, we don't draw anything anyway */ + if ((ggd->all_hidden = (ED_transform_calc_gizmo_stats(C, + &(struct TransformCalcParams){ + .use_only_center = true, + .orientation_index = orient_index + 1, + }, + &tbounds) == 0))) { + return; + } + + gizmo_prepare_mat(C, rv3d, &tbounds); + + gizmo_refresh_from_matrix(gzgroup, rv3d->twmat, NULL); +} + static void WIDGETGROUP_gizmo_message_subscribe(const bContext *C, wmGizmoGroup *gzgroup, struct wmMsgBus *mbus) @@ -1756,11 +1857,22 @@ static void WIDGETGROUP_gizmo_draw_prepare(const bContext *C, wmGizmoGroup *gzgr copy_m3_m4(viewinv_m3, rv3d->viewinv); float idot[3]; + /* Re-calculate hidden unless modal. */ + bool is_modal = false; + { + wmGizmo *gz = WM_gizmomap_get_modal(region->gizmo_map); + if (gz && gz->parent_gzgroup == gzgroup) { + is_modal = true; + } + } + /* when looking through a selected camera, the gizmo can be at the * exact same position as the view, skip so we don't break selection */ if (ggd->all_hidden || fabsf(ED_view3d_pixel_size(rv3d, rv3d->twmat[3])) < 5e-7f) { MAN_ITER_AXES_BEGIN (axis, axis_idx) { - WM_gizmo_set_flag(axis, WM_GIZMO_HIDDEN, true); + if (!is_modal) { + WM_gizmo_set_flag(axis, WM_GIZMO_HIDDEN, true); + } } MAN_ITER_AXES_END; return; @@ -1771,12 +1883,15 @@ static void WIDGETGROUP_gizmo_draw_prepare(const bContext *C, wmGizmoGroup *gzgr MAN_ITER_AXES_BEGIN (axis, axis_idx) { const short axis_type = gizmo_get_axis_type(axis_idx); /* XXX maybe unset _HIDDEN flag on redraw? */ - if (gizmo_is_axis_visible(rv3d, ggd->twtype, idot, axis_type, axis_idx)) { - WM_gizmo_set_flag(axis, WM_GIZMO_HIDDEN, false); + if (!is_modal) { + WM_gizmo_set_flag(axis, WM_GIZMO_HIDDEN, false); + } } else { - WM_gizmo_set_flag(axis, WM_GIZMO_HIDDEN, true); + if (!is_modal) { + WM_gizmo_set_flag(axis, WM_GIZMO_HIDDEN, true); + } continue; } @@ -1785,13 +1900,15 @@ static void WIDGETGROUP_gizmo_draw_prepare(const bContext *C, wmGizmoGroup *gzgr WM_gizmo_set_color(axis, color); WM_gizmo_set_color_highlight(axis, color_hi); - switch (axis_idx) { - case MAN_AXIS_TRANS_C: - case MAN_AXIS_ROT_C: - case MAN_AXIS_SCALE_C: - case MAN_AXIS_ROT_T: - WM_gizmo_set_matrix_rotation_from_z_axis(axis, rv3d->viewinv[2]); - break; + if (!is_modal) { + switch (axis_idx) { + case MAN_AXIS_TRANS_C: + case MAN_AXIS_ROT_C: + case MAN_AXIS_SCALE_C: + case MAN_AXIS_ROT_T: + WM_gizmo_set_matrix_rotation_from_z_axis(axis, rv3d->viewinv[2]); + break; + } } } MAN_ITER_AXES_END; @@ -1879,6 +1996,17 @@ static void WIDGETGROUP_gizmo_invoke_prepare(const bContext *C, } } } + else if (ELEM(axis_idx, MAN_AXIS_ROT_X, MAN_AXIS_ROT_Y, MAN_AXIS_ROT_Z, MAN_AXIS_ROT_C)) { + gizmo_3d_dial_matrixbasis_calc(CTX_wm_region(C), + gz->matrix_basis[2], + gz->matrix_basis[3], + (float[2]){UNPACK2(event->mval)}, + gz->matrix_basis); + PropertyRNA *prop = RNA_struct_find_property(gz->ptr, "draw_options"); + RNA_property_enum_set( + gz->ptr, prop, RNA_property_enum_get(gz->ptr, prop) | ED_GIZMO_DIAL_DRAW_FLAG_ANGLE_VALUE); + RNA_float_set(gz->ptr, "incremental_angle", 0.0f); + } } static bool WIDGETGROUP_gizmo_poll_generic(View3D *v3d) @@ -1944,8 +2072,8 @@ void VIEW3D_GGT_xform_gizmo(wmGizmoGroupType *gzgt) gzgt->name = "3D View: Transform Gizmo"; gzgt->idname = "VIEW3D_GGT_xform_gizmo"; - gzgt->flag = WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_DRAW_MODAL_EXCLUDE | - WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK; + gzgt->flag = WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | + WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK; gzgt->gzmap_params.spaceid = SPACE_VIEW3D; gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW; diff --git a/source/blender/editors/transform/transform_mode.c b/source/blender/editors/transform/transform_mode.c index 2fd81486bb6..83f1bd35f81 100644 --- a/source/blender/editors/transform/transform_mode.c +++ b/source/blender/editors/transform/transform_mode.c @@ -35,7 +35,7 @@ /* Own include. */ #include "transform_mode.h" -int transform_mode_really_used(bContext *C, int mode) +eTfmMode transform_mode_really_used(bContext *C, eTfmMode mode) { if (mode == TFM_BONESIZE) { Object *ob = CTX_data_active_object(C); diff --git a/source/blender/editors/transform/transform_mode.h b/source/blender/editors/transform/transform_mode.h index 6930b87090c..eac6734ed88 100644 --- a/source/blender/editors/transform/transform_mode.h +++ b/source/blender/editors/transform/transform_mode.h @@ -25,7 +25,7 @@ typedef struct TransDataGenericSlideVert { /* transform_mode.c */ -int transform_mode_really_used(struct bContext *C, int mode); +eTfmMode transform_mode_really_used(struct bContext *C, eTfmMode mode); bool transdata_check_local_center(const TransInfo *t, short around); /** * Informs if the mode can be switched during modal. diff --git a/source/blender/editors/transform/transform_mode_rotate.c b/source/blender/editors/transform/transform_mode_rotate.c index 4b86adc4f46..94caaa288e5 100644 --- a/source/blender/editors/transform/transform_mode_rotate.c +++ b/source/blender/editors/transform/transform_mode_rotate.c @@ -322,10 +322,30 @@ static void applyRotation(TransInfo *t, const int UNUSED(mval[2])) ED_area_status_text(t->area, str); } +static void applyRotationMatrix(TransInfo *t, float mat_xform[4][4]) +{ + float axis_final[3]; + const float angle_final = t->values_final[0]; + if ((t->con.mode & CON_APPLY) && t->con.applyRot) { + t->con.applyRot(t, NULL, NULL, axis_final, NULL); + } + else { + negate_v3_v3(axis_final, t->spacemtx[t->orient_axis]); + } + + float mat3[3][3]; + float mat4[4][4]; + axis_angle_normalized_to_mat3(mat3, axis_final, angle_final); + copy_m4_m3(mat4, mat3); + transform_pivot_set_m4(mat4, t->center_global); + mul_m4_m4m4(mat_xform, mat4, mat_xform); +} + void initRotation(TransInfo *t) { t->mode = TFM_ROTATION; t->transform = applyRotation; + t->transform_matrix = applyRotationMatrix; t->tsnap.applySnap = ApplySnapRotation; t->tsnap.distance = RotationBetween; diff --git a/source/blender/editors/transform/transform_mode_trackball.c b/source/blender/editors/transform/transform_mode_trackball.c index 2e405449bc3..3716826800b 100644 --- a/source/blender/editors/transform/transform_mode_trackball.c +++ b/source/blender/editors/transform/transform_mode_trackball.c @@ -75,19 +75,25 @@ static void transdata_elem_trackball_fn(void *__restrict iter_data_v, /** \name Transform (Rotation - Trackball) * \{ */ -static void applyTrackballValue(TransInfo *t, - const float axis1[3], - const float axis2[3], - const float angles[2]) +static void applyTrackballValue_calc_axis_angle(const TransInfo *t, + const float phi[2], + float r_axis[3], + float *r_angle) +{ + float axis1[3], axis2[3]; + normalize_v3_v3(axis1, t->persinv[0]); + normalize_v3_v3(axis2, t->persinv[1]); + + mul_v3_v3fl(r_axis, axis1, phi[0]); + madd_v3_v3fl(r_axis, axis2, phi[1]); + *r_angle = normalize_v3(r_axis); +} + +static void applyTrackballValue(TransInfo *t, const float axis[3], const float angle) { float mat_final[3][3]; - float axis[3]; - float angle; int i; - mul_v3_v3fl(axis, axis1, angles[0]); - madd_v3_v3fl(axis, axis2, angles[1]); - angle = normalize_v3(axis); axis_angle_normalized_to_mat3(mat_final, axis, angle); FOREACH_TRANS_DATA_CONTAINER (t, tc) { @@ -120,17 +126,8 @@ static void applyTrackball(TransInfo *t, const int UNUSED(mval[2])) { char str[UI_MAX_DRAW_STR]; size_t ofs = 0; - float axis1[3], axis2[3]; -#if 0 /* UNUSED */ - float mat[3][3], totmat[3][3], smat[3][3]; -#endif float phi[2]; - copy_v3_v3(axis1, t->persinv[0]); - copy_v3_v3(axis2, t->persinv[1]); - normalize_v3(axis1); - normalize_v3(axis2); - copy_v2_v2(phi, t->values); transform_snap_increment(t, phi); @@ -165,27 +162,35 @@ static void applyTrackball(TransInfo *t, const int UNUSED(mval[2])) str + ofs, sizeof(str) - ofs, TIP_(" Proportional size: %.2f"), t->prop_size); } -#if 0 /* UNUSED */ - axis_angle_normalized_to_mat3(smat, axis1, phi[0]); - axis_angle_normalized_to_mat3(totmat, axis2, phi[1]); + float axis_final[3], angle_final; + applyTrackballValue_calc_axis_angle(t, phi, axis_final, &angle_final); + applyTrackballValue(t, axis_final, angle_final); - mul_m3_m3m3(mat, smat, totmat); + recalcData(t); - /* TRANSFORM_FIX_ME */ - // copy_m3_m3(t->mat, mat); /* used in gizmo. */ -#endif + ED_area_status_text(t->area, str); +} - applyTrackballValue(t, axis1, axis2, phi); +static void applyTrackballMatrix(TransInfo *t, float mat_xform[4][4]) +{ + const float phi[2] = {UNPACK2(t->values_final)}; - recalcData(t); + float axis_final[3], angle_final; + applyTrackballValue_calc_axis_angle(t, phi, axis_final, &angle_final); - ED_area_status_text(t->area, str); + float mat3[3][3], mat4[4][4]; + axis_angle_normalized_to_mat3(mat3, axis_final, angle_final); + + copy_m4_m3(mat4, mat3); + transform_pivot_set_m4(mat4, t->center_global); + mul_m4_m4m4(mat_xform, mat4, mat_xform); } void initTrackball(TransInfo *t) { t->mode = TFM_TRACKBALL; t->transform = applyTrackball; + t->transform_matrix = applyTrackballMatrix; initMouseInputMode(t, &t->mouse, INPUT_TRACKBALL); diff --git a/source/blender/editors/transform/transform_mode_translate.c b/source/blender/editors/transform/transform_mode_translate.c index c881cde351e..3c6b6ea4117 100644 --- a/source/blender/editors/transform/transform_mode_translate.c +++ b/source/blender/editors/transform/transform_mode_translate.c @@ -469,7 +469,7 @@ static void applyTranslation(TransInfo *t, const int UNUSED(mval[2])) add_v3_v3(global_dir, values_ofs); } - t->tsnap.snapElem = 0; + t->tsnap.snapElem = SCE_SNAP_MODE_NONE; applySnapping(t, global_dir); transform_snap_grid(t, global_dir); @@ -486,7 +486,7 @@ static void applyTranslation(TransInfo *t, const int UNUSED(mval[2])) /* Test for mixed snap with grid. */ float snap_dist_sq = FLT_MAX; - if (t->tsnap.snapElem != 0) { + if (t->tsnap.snapElem != SCE_SNAP_MODE_NONE) { snap_dist_sq = len_squared_v3v3(t->values, global_dir); } if ((snap_dist_sq == FLT_MAX) || (len_squared_v3v3(global_dir, incr_dir) < snap_dist_sq)) { @@ -518,6 +518,13 @@ static void applyTranslation(TransInfo *t, const int UNUSED(mval[2])) ED_area_status_text(t->area, str); } +static void applyTranslationMatrix(TransInfo *t, float mat_xform[4][4]) +{ + float delta[3]; + mul_v3_m3v3(delta, t->spacemtx, t->values_final); + add_v3_v3(mat_xform[3], delta); +} + void initTranslation(TransInfo *t) { if (t->spacetype == SPACE_ACTION) { @@ -530,6 +537,7 @@ void initTranslation(TransInfo *t) } t->transform = applyTranslation; + t->transform_matrix = applyTranslationMatrix; t->tsnap.applySnap = ApplySnapTranslation; t->tsnap.distance = transform_snap_distance_len_squared_fn; diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c index 600f525b428..cd8a2f17554 100644 --- a/source/blender/editors/transform/transform_ops.c +++ b/source/blender/editors/transform/transform_ops.c @@ -419,7 +419,7 @@ static int transform_modal(bContext *C, wmOperator *op, const wmEvent *event) /* XXX, workaround: active needs to be calculated before transforming, * since we're not reading from 'td->center' in this case. see: T40241 */ - if (t->tsnap.target == SCE_SNAP_TARGET_ACTIVE) { + if (t->tsnap.source_select == SCE_SNAP_SOURCE_ACTIVE) { /* In camera view, tsnap callback is not set * (see #initSnappingMode() in transform_snap.c, and T40348). */ if (t->tsnap.targetSnap && ((t->tsnap.status & TARGET_INIT) == 0)) { @@ -648,7 +648,11 @@ void Transform_Properties(struct wmOperatorType *ot, int flags) RNA_def_property_flag(prop, PROP_HIDDEN); if (flags & P_GEO_SNAP) { - prop = RNA_def_enum(ot->srna, "snap_target", rna_enum_snap_target_items, 0, "Target", ""); + /* TODO(@gfxcoder): Rename `snap_target` to `snap_source` to avoid + * previous ambiguity of "target" (now, "source" is geometry to be moved and "target" is + * geometry to which moved geometry is snapped). Use "Source snap point" and "Point on + * source that will snap to target" for name and description, respectively. */ + prop = RNA_def_enum(ot->srna, "snap_target", rna_enum_snap_source_items, 0, "Target", ""); RNA_def_property_flag(prop, PROP_HIDDEN); prop = RNA_def_float_vector( ot->srna, "snap_point", 3, NULL, -FLT_MAX, FLT_MAX, "Point", "", -FLT_MAX, FLT_MAX); diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index e119b264ae7..649217092aa 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -79,8 +79,8 @@ static void TargetSnapActive(TransInfo *t); /** \name Implementations * \{ */ -static bool snapNodeTest(View2D *v2d, bNode *node, eSnapSelect snap_select); -static NodeBorder snapNodeBorder(int snap_node_mode); +static bool snapNodeTest(View2D *v2d, bNode *node, eSnapTargetSelect snap_target_select); +static NodeBorder snapNodeBorder(eSnapMode snap_node_mode); #if 0 int BIF_snappingSupported(Object *obedit) @@ -377,56 +377,59 @@ void applyProject(TransInfo *t) if (ED_view3d_project_float_global(t->region, iloc, mval_fl, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { - if (ED_transform_snap_object_project_view3d( - t->tsnap.object_context, - t->depsgraph, - t->region, - t->view, - SCE_SNAP_MODE_FACE, - &(const struct SnapObjectParams){ - .snap_select = t->tsnap.modeSelect, - .edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL, - .use_occlusion_test = false, - .use_backface_culling = t->tsnap.use_backface_culling, - }, - mval_fl, - NULL, - 0, - loc, - no)) { + eSnapMode hit = ED_transform_snap_object_project_view3d( + t->tsnap.object_context, + t->depsgraph, + t->region, + t->view, + SCE_SNAP_MODE_FACE, + &(const struct SnapObjectParams){ + .snap_target_select = t->tsnap.target_select, + .edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL, + .use_occlusion_test = false, + .use_backface_culling = t->tsnap.use_backface_culling, + }, + mval_fl, + NULL, + 0, + loc, + no); + if (hit != SCE_SNAP_MODE_FACE) { + return; + } + #if 0 - if (tc->use_local_mat) { - mul_m4_v3(tc->imat, loc); - } + if (tc->use_local_mat) { + mul_m4_v3(tc->imat, loc); + } #endif - sub_v3_v3v3(tvec, loc, iloc); + sub_v3_v3v3(tvec, loc, iloc); - mul_m3_v3(td->smtx, tvec); + mul_m3_v3(td->smtx, tvec); - add_v3_v3(td->loc, tvec); + add_v3_v3(td->loc, tvec); - if (t->tsnap.align && (t->options & CTX_OBJECT)) { - /* handle alignment as well */ - const float *original_normal; - float mat[3][3]; + if (t->tsnap.align && (t->options & CTX_OBJECT)) { + /* handle alignment as well */ + const float *original_normal; + float mat[3][3]; - /* In pose mode, we want to align normals with Y axis of bones... */ - original_normal = td->axismtx[2]; + /* In pose mode, we want to align normals with Y axis of bones... */ + original_normal = td->axismtx[2]; - rotation_between_vecs_to_mat3(mat, original_normal, no); + rotation_between_vecs_to_mat3(mat, original_normal, no); - transform_data_ext_rotate(td, mat, true); + transform_data_ext_rotate(td, mat, true); - /* TODO: support constraints for rotation too? see #ElementRotation. */ - } + /* TODO: support constraints for rotation too? see #ElementRotation. */ } } + } #if 0 /* TODO: support this? */ - constraintTransLim(t, td); + constraintTransLim(t, td); #endif - } } } @@ -522,13 +525,13 @@ void applySnapping(TransInfo *t, float *vec) void resetSnapping(TransInfo *t) { - t->tsnap.status = 0; - t->tsnap.snapElem = 0; + t->tsnap.status = SNAP_RESETTED; + t->tsnap.snapElem = SCE_SNAP_MODE_NONE; t->tsnap.align = false; - t->tsnap.project = 0; - t->tsnap.mode = 0; - t->tsnap.modeSelect = 0; - t->tsnap.target = 0; + t->tsnap.project = false; + t->tsnap.mode = SCE_SNAP_MODE_NONE; + t->tsnap.target_select = SCE_SNAP_TARGET_ALL; + t->tsnap.source_select = SCE_SNAP_SOURCE_CLOSEST; t->tsnap.last = 0; t->tsnap.snapNormal[0] = 0; @@ -581,7 +584,7 @@ static bool bm_face_is_snap_target(BMFace *f, void *UNUSED(user_data)) return true; } -static char snap_flag_from_spacetype(TransInfo *t) +static eSnapFlag snap_flag_from_spacetype(TransInfo *t) { ToolSettings *ts = t->settings; if (t->spacetype == SPACE_NODE) { @@ -596,7 +599,7 @@ static char snap_flag_from_spacetype(TransInfo *t) return ts->snap_flag; } -static short snap_mode_from_spacetype(TransInfo *t) +static eSnapMode snap_mode_from_spacetype(TransInfo *t) { ToolSettings *ts = t->settings; @@ -605,7 +608,7 @@ static short snap_mode_from_spacetype(TransInfo *t) } if (t->spacetype == SPACE_IMAGE) { - short snap_mode = ts->snap_uv_mode; + eSnapMode snap_mode = ts->snap_uv_mode; if ((snap_mode & SCE_SNAP_MODE_INCREMENT) && (ts->snap_uv_flag & SCE_SNAP_ABS_GRID) && (t->mode == TFM_TRANSLATION)) { snap_mode &= ~SCE_SNAP_MODE_INCREMENT; @@ -623,7 +626,7 @@ static short snap_mode_from_spacetype(TransInfo *t) return SCE_SNAP_MODE_INCREMENT; } - short snap_mode = ts->snap_mode; + eSnapMode snap_mode = ts->snap_mode; if ((snap_mode & SCE_SNAP_MODE_INCREMENT) && (ts->snap_flag & SCE_SNAP_ABS_GRID) && (t->mode == TFM_TRANSLATION)) { /* Special case in which snap to increments is transformed to snap to grid. */ @@ -641,7 +644,7 @@ static short snap_mode_from_spacetype(TransInfo *t) return SCE_SNAP_MODE_INCREMENT; } -static short snap_select_type_get(TransInfo *t) +static eSnapTargetSelect snap_select_type_get(TransInfo *t) { ViewLayer *view_layer = t->view_layer; Base *base_act = view_layer->basact; @@ -652,7 +655,7 @@ static short snap_select_type_get(TransInfo *t) * TODO: perform self snap in gpencil_strokes. * * When we're moving the origins, allow snapping onto our own geometry (see T69132). */ - return SNAP_ALL; + return SCE_SNAP_TARGET_ALL; } const int obedit_type = t->obedit_type; @@ -669,44 +672,44 @@ static short snap_select_type_get(TransInfo *t) if ((obedit_type == OB_MESH) && (t->flag & T_PROP_EDIT)) { /* Exclude editmesh if using proportional edit */ - return SNAP_NOT_EDITED; + return SCE_SNAP_TARGET_NOT_EDITED; } if (!t->tsnap.snap_self) { - return SNAP_NOT_ACTIVE; + return SCE_SNAP_TARGET_NOT_ACTIVE; } - return SNAP_NOT_SELECTED; + return SCE_SNAP_TARGET_NOT_SELECTED; } - return SNAP_ALL; + return SCE_SNAP_TARGET_ALL; } if (base_act && (base_act->object->mode & OB_MODE_PARTICLE_EDIT)) { /* Particles edit mode. */ - return SNAP_ALL; + return SCE_SNAP_TARGET_ALL; } /* Object or pose mode. */ - return SNAP_NOT_SELECTED; + return SCE_SNAP_TARGET_NOT_SELECTED; } if (ELEM(t->spacetype, SPACE_NODE, SPACE_SEQ)) { - return SNAP_NOT_SELECTED; + return SCE_SNAP_TARGET_NOT_SELECTED; } - return SNAP_ALL; + return SCE_SNAP_TARGET_ALL; } static void initSnappingMode(TransInfo *t) { ToolSettings *ts = t->settings; t->tsnap.mode = snap_mode_from_spacetype(t); - t->tsnap.modeSelect = snap_select_type_get(t); + t->tsnap.target_select = snap_select_type_get(t); if ((t->spacetype != SPACE_VIEW3D) || !(ts->snap_mode & SCE_SNAP_MODE_FACE)) { /* Force project off when not supported. */ - t->tsnap.project = 0; + t->tsnap.project = false; } if (ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE, SPACE_NODE, SPACE_SEQ)) { @@ -752,7 +755,7 @@ void initSnapping(TransInfo *t, wmOperator *op) { resetSnapping(t); t->tsnap.flag = snap_flag_from_spacetype(t); - short snap_target = t->settings->snap_target; + eSnapSourceSelect snap_source = t->settings->snap_target; /* if snap property exists */ PropertyRNA *prop; @@ -763,7 +766,10 @@ void initSnapping(TransInfo *t, wmOperator *op) if ((prop = RNA_struct_find_property(op->ptr, "snap_target")) && RNA_property_is_set(op->ptr, prop)) { - snap_target = RNA_property_enum_get(op->ptr, prop); + /* TODO(@gfxcoder): Rename `snap_target` to `snap_source` to avoid + * previous ambiguity of "target" (now, "source" is geometry to be moved and "target" is + * geometry to which moved geometry is snapped). */ + snap_source = RNA_property_enum_get(op->ptr, prop); } if ((prop = RNA_struct_find_property(op->ptr, "snap_point")) && @@ -803,7 +809,7 @@ void initSnapping(TransInfo *t, wmOperator *op) t->tsnap.peel = ((t->tsnap.flag & SCE_SNAP_PROJECT) != 0); } - t->tsnap.target = snap_target; + t->tsnap.source_select = snap_source; initSnappingMode(t); } @@ -844,11 +850,11 @@ static void setSnappingCallback(TransInfo *t) return; } - switch (t->tsnap.target) { - case SCE_SNAP_TARGET_CLOSEST: + switch (t->tsnap.source_select) { + case SCE_SNAP_SOURCE_CLOSEST: t->tsnap.targetSnap = TargetSnapClosest; break; - case SCE_SNAP_TARGET_CENTER: + case SCE_SNAP_SOURCE_CENTER: if (!ELEM(t->mode, TFM_ROTATION, TFM_RESIZE)) { t->tsnap.targetSnap = TargetSnapCenter; break; @@ -856,10 +862,10 @@ static void setSnappingCallback(TransInfo *t) /* Can't do TARGET_CENTER with these modes, * use TARGET_MEDIAN instead. */ ATTR_FALLTHROUGH; - case SCE_SNAP_TARGET_MEDIAN: + case SCE_SNAP_SOURCE_MEDIAN: t->tsnap.targetSnap = TargetSnapMedian; break; - case SCE_SNAP_TARGET_ACTIVE: + case SCE_SNAP_SOURCE_ACTIVE: t->tsnap.targetSnap = TargetSnapActive; break; } @@ -973,7 +979,7 @@ static void snap_calc_view3d_fn(TransInfo *t, float *UNUSED(vec)) float no[3]; float mval[2]; bool found = false; - short snap_elem = 0; + eSnapMode snap_elem = SCE_SNAP_MODE_NONE; float dist_px = SNAP_MIN_DISTANCE; /* Use a user defined value here. */ mval[0] = t->mval[0]; @@ -982,7 +988,7 @@ static void snap_calc_view3d_fn(TransInfo *t, float *UNUSED(vec)) if (t->tsnap.mode & SCE_SNAP_MODE_GEOM) { zero_v3(no); /* objects won't set this */ snap_elem = snapObjectsTransform(t, mval, &dist_px, loc, no); - found = snap_elem != 0; + found = (snap_elem != SCE_SNAP_MODE_NONE); } if ((found == false) && (t->tsnap.mode & SCE_SNAP_MODE_VOLUME)) { found = peelObjectsTransform( @@ -1003,7 +1009,7 @@ static void snap_calc_view3d_fn(TransInfo *t, float *UNUSED(vec)) t->tsnap.status &= ~POINT_INIT; } - t->tsnap.snapElem = (char)snap_elem; + t->tsnap.snapElem = snap_elem; } static void snap_calc_uv_fn(TransInfo *t, float *UNUSED(vec)) @@ -1020,7 +1026,7 @@ static void snap_calc_uv_fn(TransInfo *t, float *UNUSED(vec)) objects, objects_len, t->mval, - true, + t->tsnap.target_select == SCE_SNAP_TARGET_NOT_SELECTED, &dist_sq, t->tsnap.snapPoint)) { t->tsnap.snapPoint[0] *= t->aspect[0]; @@ -1190,7 +1196,7 @@ static void TargetSnapActive(TransInfo *t) } /* No active, default to median */ else { - t->tsnap.target = SCE_SNAP_TARGET_MEDIAN; + t->tsnap.source_select = SCE_SNAP_SOURCE_MEDIAN; t->tsnap.targetSnap = TargetSnapMedian; TargetSnapMedian(t); } @@ -1302,7 +1308,7 @@ static void TargetSnapClosest(TransInfo *t) /** \name Snap Objects * \{ */ -short snapObjectsTransform( +eSnapMode snapObjectsTransform( TransInfo *t, const float mval[2], float *dist_px, float r_loc[3], float r_no[3]) { float *target = (t->tsnap.status & TARGET_INIT) ? t->tsnap.snapTarget : t->center_global; @@ -1313,7 +1319,7 @@ short snapObjectsTransform( t->view, t->tsnap.mode, &(const struct SnapObjectParams){ - .snap_select = t->tsnap.modeSelect, + .snap_target_select = t->tsnap.target_select, .edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL, .use_occlusion_test = t->settings->snap_mode != SCE_SNAP_MODE_FACE, .use_backface_culling = t->tsnap.use_backface_culling, @@ -1346,7 +1352,7 @@ bool peelObjectsTransform(TransInfo *t, t->region, t->view, &(const struct SnapObjectParams){ - .snap_select = t->tsnap.modeSelect, + .snap_target_select = t->tsnap.target_select, .edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL, }, mval, @@ -1414,16 +1420,16 @@ bool peelObjectsTransform(TransInfo *t, /** \name snap Nodes * \{ */ -static bool snapNodeTest(View2D *v2d, bNode *node, eSnapSelect snap_select) +static bool snapNodeTest(View2D *v2d, bNode *node, eSnapTargetSelect snap_target_select) { /* node is use for snapping only if a) snap mode matches and b) node is inside the view */ - return ((snap_select == SNAP_NOT_SELECTED && !(node->flag & NODE_SELECT)) || - (snap_select == SNAP_ALL && !(node->flag & NODE_ACTIVE))) && + return ((snap_target_select == SCE_SNAP_TARGET_NOT_SELECTED && !(node->flag & NODE_SELECT)) || + (snap_target_select == SCE_SNAP_TARGET_ALL && !(node->flag & NODE_ACTIVE))) && (node->totr.xmin < v2d->cur.xmax && node->totr.xmax > v2d->cur.xmin && node->totr.ymin < v2d->cur.ymax && node->totr.ymax > v2d->cur.ymin); } -static NodeBorder snapNodeBorder(int snap_node_mode) +static NodeBorder snapNodeBorder(eSnapMode snap_node_mode) { NodeBorder flag = 0; if (snap_node_mode & SCE_SNAP_MODE_NODE_X) { @@ -1499,7 +1505,7 @@ static bool snapNodes(ToolSettings *ts, SpaceNode *snode, ARegion *region, const int mval[2], - eSnapSelect snap_select, + eSnapTargetSelect snap_target_select, float r_loc[2], float *r_dist_px, char *r_node_border) @@ -1511,7 +1517,7 @@ static bool snapNodes(ToolSettings *ts, *r_node_border = 0; for (node = ntree->nodes.first; node; node = node->next) { - if (snapNodeTest(®ion->v2d, node, snap_select)) { + if (snapNodeTest(®ion->v2d, node, snap_target_select)) { retval |= snapNode(ts, snode, region, node, mval, r_loc, r_dist_px, r_node_border); } } @@ -1526,7 +1532,7 @@ bool snapNodesTransform( t->area->spacedata.first, t->region, mval, - t->tsnap.modeSelect, + t->tsnap.target_select, r_loc, r_dist_px, r_node_border); @@ -1548,7 +1554,7 @@ static void snap_grid_apply( float in[3]; if (t->con.mode & CON_APPLY) { - BLI_assert(t->tsnap.snapElem == 0); + BLI_assert(t->tsnap.snapElem == SCE_SNAP_MODE_NONE); t->con.applyVec(t, NULL, NULL, loc, in); } else { @@ -1676,6 +1682,16 @@ bool transform_snap_increment(const TransInfo *t, float *r_val) return transform_snap_increment_ex(t, false, r_val); } +float transform_snap_increment_get(const TransInfo *t) +{ + if (activeSnap(t) && (!transformModeUseSnap(t) || + (t->tsnap.mode & (SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID)))) { + return (t->modifiers & MOD_PRECISION) ? t->snap[1] : t->snap[0]; + } + + return 0.0f; +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/transform/transform_snap.h b/source/blender/editors/transform/transform_snap.h index cde97d14be4..6db027df067 100644 --- a/source/blender/editors/transform/transform_snap.h +++ b/source/blender/editors/transform/transform_snap.h @@ -19,12 +19,12 @@ bool peelObjectsTransform(struct TransInfo *t, float r_no[3], float *r_thickness); -short snapObjectsTransform(struct TransInfo *t, - const float mval[2], - float *dist_px, - /* return args */ - float r_loc[3], - float r_no[3]); +eSnapMode 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 */ @@ -36,6 +36,7 @@ bool transformModeUseSnap(const TransInfo *t); bool transform_snap_increment_ex(const TransInfo *t, bool use_local_space, float *r_val); bool transform_snap_increment(const TransInfo *t, float *val); +float transform_snap_increment_get(const TransInfo *t); bool transform_snap_grid(TransInfo *t, float *val); bool activeSnap(const TransInfo *t); diff --git a/source/blender/editors/transform/transform_snap_object.cc b/source/blender/editors/transform/transform_snap_object.cc index fade7f47d9c..cf99d4b2ef3 100644 --- a/source/blender/editors/transform/transform_snap_object.cc +++ b/source/blender/editors/transform/transform_snap_object.cc @@ -157,7 +157,7 @@ struct SnapObjectContext { enum eViewProj view_proj; float clip_plane[MAX_CLIPPLANE_LEN][4]; short clip_plane_len; - short snap_to_flag; + eSnapMode snap_to_flag; bool has_occlusion_plane; /* Ignore plane of occlusion in curves. */ } runtime; }; @@ -168,8 +168,13 @@ struct SnapObjectContext { /** \name Utilities * \{ */ -/* Mesh used for snapping. - * If nullptr the BMesh should be used. */ +/** + * Mesh used for snapping. + * + * - When the return value is null the `BKE_editmesh_from_object(ob_eval)` should be used. + * - In rare cases there is no evaluated mesh available and a null result doesn't imply an + * edit-mesh, so callers need to account for a null edit-mesh too, see: T96536. + */ static const Mesh *mesh_for_snap(Object *ob_eval, eSnapEditType edit_mode_type, bool *r_use_hide) { const Mesh *me_eval = BKE_object_get_evaluated_mesh(ob_eval); @@ -278,8 +283,10 @@ static SnapData_Mesh *snap_object_data_mesh_get(SnapObjectContext *sctx, } } else { - /* Any existing #SnapData_EditMesh is now invalid. */ - sctx->editmesh_caches.remove(BKE_editmesh_from_object(ob_eval)); + if (ob_eval->type == OB_MESH) { + /* Any existing #SnapData_EditMesh is now invalid. */ + sctx->editmesh_caches.remove(BKE_editmesh_from_object(ob_eval)); + } std::unique_ptr<SnapData_Mesh> sod_ptr = std::make_unique<SnapData_Mesh>(); sod = sod_ptr.get(); @@ -412,7 +419,7 @@ using IterSnapObjsCallback = void (*)(SnapObjectContext *sctx, void *data); static bool snap_object_is_snappable(const SnapObjectContext *sctx, - const eSnapSelect snap_select, + const eSnapTargetSelect snap_select, const Base *base_act, const Base *base, const bool is_in_object_mode) @@ -421,7 +428,7 @@ static bool snap_object_is_snappable(const SnapObjectContext *sctx, return false; } - if ((snap_select == SNAP_ALL) || (base->flag_legacy & BA_TRANSFORM_LOCKED_IN_PLACE)) { + if ((snap_select == SCE_SNAP_TARGET_ALL) || (base->flag_legacy & BA_TRANSFORM_LOCKED_IN_PLACE)) { return true; } @@ -429,15 +436,15 @@ static bool snap_object_is_snappable(const SnapObjectContext *sctx, return false; } - if (snap_select == SNAP_NOT_ACTIVE) { + if (snap_select == SCE_SNAP_TARGET_NOT_ACTIVE) { return base_act != base; } - if (snap_select == SNAP_NOT_EDITED) { + if (snap_select == SCE_SNAP_TARGET_NOT_EDITED) { return base->object->mode != OB_MODE_EDIT; } - if (snap_select == SNAP_NOT_SELECTED) { + if (snap_select == SCE_SNAP_TARGET_NOT_SELECTED) { if (is_in_object_mode) { return !((base->flag & BASE_SELECTED) || (base->flag_legacy & BA_WAS_SEL)); } @@ -446,7 +453,7 @@ static bool snap_object_is_snappable(const SnapObjectContext *sctx, return true; } - if (snap_select == SNAP_SELECTABLE) { + if (snap_select == SCE_SNAP_TARGET_ONLY_SELECTABLE) { return (base->flag & BASE_SELECTABLE) != 0; } @@ -462,12 +469,12 @@ static void iter_snap_objects(SnapObjectContext *sctx, void *data) { ViewLayer *view_layer = DEG_get_input_view_layer(sctx->runtime.depsgraph); - const eSnapSelect snap_select = params->snap_select; + const eSnapTargetSelect snap_target_select = params->snap_target_select; Base *base_act = view_layer->basact; const bool is_in_object_mode = !base_act || base_act->object->mode == OB_MODE_OBJECT; LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { - if (!snap_object_is_snappable(sctx, snap_select, base_act, base, is_in_object_mode)) { + if (!snap_object_is_snappable(sctx, snap_target_select, base_act, base, is_in_object_mode)) { continue; } @@ -998,6 +1005,9 @@ static void raycast_obj_fn(SnapObjectContext *sctx, const Mesh *me_eval = mesh_for_snap(ob_eval, edit_mode_type, &use_hide); if (me_eval == nullptr) { BMEditMesh *em = BKE_editmesh_from_object(ob_eval); + if (UNLIKELY(!em)) { /* See #mesh_for_snap doc-string. */ + return; + } BLI_assert_msg(em == BKE_editmesh_from_object(DEG_get_original_object(ob_eval)), "Make sure there is only one pointer for looptris"); retval = raycastEditMesh(sctx, @@ -1525,18 +1535,18 @@ static void nearest2d_data_init_editmesh(SnapData_EditMesh *sod, /** \name Internal Object Snapping API * \{ */ -static short snap_mesh_polygon(SnapObjectContext *sctx, - const SnapObjectParams *params, - Object *ob_eval, - const float obmat[4][4], - /* read/write args */ - float *dist_px, - /* return args */ - float r_loc[3], - float r_no[3], - int *r_index) +static eSnapMode snap_mesh_polygon(SnapObjectContext *sctx, + const SnapObjectParams *params, + Object *ob_eval, + const float obmat[4][4], + /* read/write args */ + float *dist_px, + /* return args */ + float r_loc[3], + float r_no[3], + int *r_index) { - short elem = 0; + eSnapMode elem = SCE_SNAP_MODE_NONE; float lpmat[4][4]; mul_m4_m4m4(lpmat, sctx->runtime.pmat, obmat); @@ -1655,23 +1665,23 @@ static short snap_mesh_polygon(SnapObjectContext *sctx, return elem; } - return 0; + return SCE_SNAP_MODE_NONE; } -static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx, - const SnapObjectParams *params, - Object *ob_eval, - const float obmat[4][4], - float original_dist_px, - const float prev_co[3], - /* read/write args */ - float *dist_px, - /* return args */ - float r_loc[3], - float r_no[3], - int *r_index) +static eSnapMode snap_mesh_edge_verts_mixed(SnapObjectContext *sctx, + const SnapObjectParams *params, + Object *ob_eval, + const float obmat[4][4], + float original_dist_px, + const float prev_co[3], + /* read/write args */ + float *dist_px, + /* return args */ + float r_loc[3], + float r_no[3], + int *r_index) { - short elem = SCE_SNAP_MODE_EDGE; + eSnapMode elem = SCE_SNAP_MODE_EDGE; if (ob_eval->type != OB_MESH) { return elem; @@ -1818,19 +1828,19 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx, return elem; } -static short snapArmature(SnapObjectContext *sctx, - const SnapObjectParams *params, - Object *ob_eval, - const float obmat[4][4], - bool is_object_active, - /* read/write args */ - float *dist_px, - /* return args */ - float r_loc[3], - float *UNUSED(r_no), - int *r_index) +static eSnapMode snapArmature(SnapObjectContext *sctx, + const SnapObjectParams *params, + Object *ob_eval, + const float obmat[4][4], + bool is_object_active, + /* read/write args */ + float *dist_px, + /* return args */ + float r_loc[3], + float *UNUSED(r_no), + int *r_index) { - short retval = 0; + eSnapMode retval = SCE_SNAP_MODE_NONE; if (sctx->runtime.snap_to_flag == SCE_SNAP_MODE_FACE) { /* Currently only edge and vert */ return retval; @@ -1867,7 +1877,7 @@ static short snapArmature(SnapObjectContext *sctx, const bool is_posemode = is_object_active && (ob_eval->mode & OB_MODE_POSE); const bool skip_selected = (is_editmode || is_posemode) && - (params->snap_select == SNAP_NOT_SELECTED); + (params->snap_target_select & SCE_SNAP_TARGET_NOT_SELECTED); const bool is_persp = sctx->runtime.view_proj == VIEW_PROJ_PERSP; if (arm->edbo) { @@ -1982,25 +1992,25 @@ static short snapArmature(SnapObjectContext *sctx, return retval; } - return 0; + return SCE_SNAP_MODE_NONE; } -static short snapCurve(SnapObjectContext *sctx, - const SnapObjectParams *params, - Object *ob_eval, - const float obmat[4][4], - /* read/write args */ - float *dist_px, - /* return args */ - float r_loc[3], - float *UNUSED(r_no), - int *r_index) +static eSnapMode snapCurve(SnapObjectContext *sctx, + const SnapObjectParams *params, + Object *ob_eval, + const float obmat[4][4], + /* read/write args */ + float *dist_px, + /* return args */ + float r_loc[3], + float *UNUSED(r_no), + int *r_index) { bool has_snap = false; /* only vertex snapping mode (eg control points and handles) supported for now) */ if ((sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX) == 0) { - return 0; + return SCE_SNAP_MODE_NONE; } Curve *cu = static_cast<Curve *>(ob_eval->data); @@ -2024,7 +2034,7 @@ static short snapCurve(SnapObjectContext *sctx, sctx->runtime.win_size, sctx->runtime.mval, dist_px_sq)) { - return 0; + return SCE_SNAP_MODE_NONE; } } @@ -2046,7 +2056,7 @@ static short snapCurve(SnapObjectContext *sctx, } bool is_persp = sctx->runtime.view_proj == VIEW_PROJ_PERSP; - bool skip_selected = params->snap_select == SNAP_NOT_SELECTED; + bool skip_selected = params->snap_target_select & SCE_SNAP_TARGET_NOT_SELECTED; LISTBASE_FOREACH (Nurb *, nu, (use_obedit ? &cu->editnurb->nurbs : &cu->nurb)) { for (int u = 0; u < nu->pntsu; u++) { @@ -2069,11 +2079,14 @@ static short snapCurve(SnapObjectContext *sctx, nu->bezt[u].vec[1], &dist_px_sq, r_loc); + /* Don't snap if handle is selected (moving), * or if it is aligning to a moving handle. */ - is_selected = (!(nu->bezt[u].f1 & SELECT) && - !(nu->bezt[u].h1 & HD_ALIGN && nu->bezt[u].f3 & SELECT)) != 0; - if (!(is_selected && skip_selected)) { + bool is_selected_h1 = (nu->bezt[u].f1 & SELECT) != 0; + bool is_selected_h2 = (nu->bezt[u].f3 & SELECT) != 0; + bool is_autoalign_h1 = (nu->bezt[u].h1 & HD_ALIGN) != 0; + bool is_autoalign_h2 = (nu->bezt[u].h2 & HD_ALIGN) != 0; + if (!skip_selected || !(is_selected_h1 || (is_autoalign_h1 && is_selected_h2))) { has_snap |= test_projected_vert_dist(&neasrest_precalc, clip_planes_local, clip_plane_len, @@ -2083,9 +2096,7 @@ static short snapCurve(SnapObjectContext *sctx, r_loc); } - is_selected = (!(nu->bezt[u].f3 & SELECT) && - !(nu->bezt[u].h2 & HD_ALIGN && nu->bezt[u].f1 & SELECT)) != 0; - if (!(is_selected && skip_selected)) { + if (!skip_selected || !(is_selected_h2 || (is_autoalign_h2 && is_selected_h1))) { has_snap |= test_projected_vert_dist(&neasrest_precalc, clip_planes_local, clip_plane_len, @@ -2151,21 +2162,21 @@ static short snapCurve(SnapObjectContext *sctx, return SCE_SNAP_MODE_VERTEX; } - return 0; + return SCE_SNAP_MODE_NONE; } /* may extend later (for now just snaps to empty center) */ -static short snap_object_center(const SnapObjectContext *sctx, - Object *ob_eval, - const float obmat[4][4], - /* read/write args */ - float *dist_px, - /* return args */ - float r_loc[3], - float *UNUSED(r_no), - int *r_index) +static eSnapMode snap_object_center(const SnapObjectContext *sctx, + Object *ob_eval, + const float obmat[4][4], + /* read/write args */ + float *dist_px, + /* return args */ + float r_loc[3], + float *UNUSED(r_no), + int *r_index) { - short retval = 0; + eSnapMode retval = SCE_SNAP_MODE_NONE; if (ob_eval->transflag & OB_DUPLI) { return retval; @@ -2207,20 +2218,20 @@ static short snap_object_center(const SnapObjectContext *sctx, return retval; } - return 0; + return SCE_SNAP_MODE_NONE; } -static short snapCamera(const SnapObjectContext *sctx, - Object *object, - const float obmat[4][4], - /* read/write args */ - float *dist_px, - /* return args */ - float r_loc[3], - float *r_no, - int *r_index) +static eSnapMode snapCamera(const SnapObjectContext *sctx, + Object *object, + const float obmat[4][4], + /* read/write args */ + float *dist_px, + /* return args */ + float r_loc[3], + float *r_no, + int *r_index) { - short retval = 0; + eSnapMode retval = SCE_SNAP_MODE_NONE; Scene *scene = sctx->scene; @@ -2301,28 +2312,28 @@ static short snapCamera(const SnapObjectContext *sctx, return retval; } - return 0; + return SCE_SNAP_MODE_NONE; } -static short snapMesh(SnapObjectContext *sctx, - const SnapObjectParams *params, - Object *ob_eval, - const Mesh *me_eval, - const float obmat[4][4], - bool use_hide, - /* read/write args */ - float *dist_px, - /* return args */ - float r_loc[3], - float r_no[3], - int *r_index) +static eSnapMode snapMesh(SnapObjectContext *sctx, + const SnapObjectParams *params, + Object *ob_eval, + const Mesh *me_eval, + const float obmat[4][4], + bool use_hide, + /* read/write args */ + float *dist_px, + /* return args */ + float r_loc[3], + float r_no[3], + int *r_index) { BLI_assert(sctx->runtime.snap_to_flag != SCE_SNAP_MODE_FACE); if (me_eval->totvert == 0) { - return 0; + return SCE_SNAP_MODE_NONE; } if (me_eval->totedge == 0 && !(sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX)) { - return 0; + return SCE_SNAP_MODE_NONE; } float lpmat[4][4]; @@ -2335,7 +2346,7 @@ static short snapMesh(SnapObjectContext *sctx, if (bb && !snap_bound_box_check_dist( bb->vec[0], bb->vec[6], lpmat, sctx->runtime.win_size, sctx->runtime.mval, dist_px_sq)) { - return 0; + return SCE_SNAP_MODE_NONE; } SnapData_Mesh *sod = snap_object_data_mesh_get(sctx, ob_eval, me_eval, use_hide); @@ -2376,7 +2387,7 @@ static short snapMesh(SnapObjectContext *sctx, nearest.dist_sq = dist_px_sq; int last_index = nearest.index; - short elem = SCE_SNAP_MODE_VERTEX; + eSnapMode elem = SCE_SNAP_MODE_VERTEX; float tobmat[4][4], clip_planes_local[MAX_CLIPPLANE_LEN][4]; transpose_m4_m4(tobmat, obmat); @@ -2480,31 +2491,31 @@ static short snapMesh(SnapObjectContext *sctx, return elem; } - return 0; + return SCE_SNAP_MODE_NONE; } -static short snapEditMesh(SnapObjectContext *sctx, - const SnapObjectParams *params, - Object *ob_eval, - BMEditMesh *em, - const float obmat[4][4], - /* read/write args */ - float *dist_px, - /* return args */ - float r_loc[3], - float r_no[3], - int *r_index) +static eSnapMode snapEditMesh(SnapObjectContext *sctx, + const SnapObjectParams *params, + Object *ob_eval, + BMEditMesh *em, + const float obmat[4][4], + /* read/write args */ + float *dist_px, + /* return args */ + float r_loc[3], + float r_no[3], + int *r_index) { BLI_assert(sctx->runtime.snap_to_flag != SCE_SNAP_MODE_FACE); if ((sctx->runtime.snap_to_flag & ~SCE_SNAP_MODE_FACE) == SCE_SNAP_MODE_VERTEX) { if (em->bm->totvert == 0) { - return 0; + return SCE_SNAP_MODE_NONE; } } else { if (em->bm->totedge == 0) { - return 0; + return SCE_SNAP_MODE_NONE; } } @@ -2520,7 +2531,7 @@ static short snapEditMesh(SnapObjectContext *sctx, /* was BKE_boundbox_ray_hit_check, see: cf6ca226fa58 */ if (!snap_bound_box_check_dist( sod->min, sod->max, lpmat, sctx->runtime.win_size, sctx->runtime.mval, dist_px_sq)) { - return 0; + return SCE_SNAP_MODE_NONE; } if (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX) { @@ -2595,7 +2606,7 @@ static short snapEditMesh(SnapObjectContext *sctx, nearest.index = -1; nearest.dist_sq = dist_px_sq; - short elem = SCE_SNAP_MODE_VERTEX; + eSnapMode elem = SCE_SNAP_MODE_VERTEX; float tobmat[4][4], clip_planes_local[MAX_CLIPPLANE_LEN][4]; transpose_m4_m4(tobmat, obmat); @@ -2661,7 +2672,7 @@ static short snapEditMesh(SnapObjectContext *sctx, return elem; } - return 0; + return SCE_SNAP_MODE_NONE; } struct SnapObjUserData { @@ -2673,7 +2684,7 @@ struct SnapObjUserData { int *r_index; Object **r_ob; float (*r_obmat)[4]; - short ret; + eSnapMode ret; }; /** @@ -2687,7 +2698,7 @@ static void snap_obj_fn(SnapObjectContext *sctx, void *data) { SnapObjUserData *dt = static_cast<SnapObjUserData *>(data); - short retval = 0; + eSnapMode retval = SCE_SNAP_MODE_NONE; switch (ob_eval->type) { case OB_MESH: { @@ -2696,6 +2707,9 @@ static void snap_obj_fn(SnapObjectContext *sctx, const Mesh *me_eval = mesh_for_snap(ob_eval, edit_mode_type, &use_hide); if (me_eval == nullptr) { BMEditMesh *em = BKE_editmesh_from_object(ob_eval); + if (UNLIKELY(!em)) { /* See #mesh_for_snap doc-string. */ + return; + } BLI_assert_msg(em == BKE_editmesh_from_object(DEG_get_original_object(ob_eval)), "Make sure there is only one pointer for looptris"); retval = snapEditMesh( @@ -2731,11 +2745,8 @@ static void snap_obj_fn(SnapObjectContext *sctx, dt->r_index); break; case OB_CURVES_LEGACY: - retval = snapCurve( - sctx, params, ob_eval, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index); - break; /* Use ATTR_FALLTHROUGH if we want to snap to the generated mesh. */ case OB_SURF: - if (BKE_object_is_in_editmode(ob_eval)) { + if (ob_eval->type == OB_CURVES_LEGACY || BKE_object_is_in_editmode(ob_eval)) { retval = snapCurve( sctx, params, ob_eval, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index); if (params->edit_mode_type != SNAP_GEOM_FINAL) { @@ -2804,18 +2815,18 @@ static void snap_obj_fn(SnapObjectContext *sctx, * \param r_ob: Hit object. * \param r_obmat: Object matrix (may not be #Object.obmat with dupli-instances). */ -static short snapObjectsRay(SnapObjectContext *sctx, - const SnapObjectParams *params, - /* read/write args */ - /* Parameters below cannot be const, because they are assigned to a - * non-const variable (readability-non-const-parameter). */ - float *dist_px /* NOLINT */, - /* return args */ - float r_loc[3] /* NOLINT */, - float r_no[3] /* NOLINT */, - int *r_index /* NOLINT */, - Object **r_ob, - float r_obmat[4][4]) +static eSnapMode snapObjectsRay(SnapObjectContext *sctx, + const SnapObjectParams *params, + /* read/write args */ + /* Parameters below cannot be const, because they are assigned to a + * non-const variable (readability-non-const-parameter). */ + float *dist_px /* NOLINT */, + /* return args */ + float r_loc[3] /* NOLINT */, + float r_no[3] /* NOLINT */, + int *r_index /* NOLINT */, + Object **r_ob, + float r_obmat[4][4]) { SnapObjUserData data = {}; data.dist_px = dist_px; @@ -2824,7 +2835,7 @@ static short snapObjectsRay(SnapObjectContext *sctx, data.r_ob = r_ob; data.r_index = r_index; data.r_obmat = r_obmat; - data.ret = 0; + data.ret = SCE_SNAP_MODE_NONE; iter_snap_objects(sctx, params, snap_obj_fn, &data); @@ -2997,31 +3008,29 @@ bool ED_transform_snap_object_project_ray(SnapObjectContext *sctx, sctx, depsgraph, v3d, params, ray_origin, ray_direction, ray_depth, r_co, r_no); } -static short transform_snap_context_project_view3d_mixed_impl(SnapObjectContext *sctx, - Depsgraph *depsgraph, - const ARegion *region, - const View3D *v3d, - const ushort snap_to_flag, - const SnapObjectParams *params, - const float mval[2], - const float prev_co[3], - float *dist_px, - float r_loc[3], - float r_no[3], - int *r_index, - Object **r_ob, - float r_obmat[4][4], - float r_face_nor[3]) +static eSnapMode transform_snap_context_project_view3d_mixed_impl(SnapObjectContext *sctx, + Depsgraph *depsgraph, + const ARegion *region, + const View3D *v3d, + const eSnapMode snap_to_flag, + const SnapObjectParams *params, + const float mval[2], + const float prev_co[3], + float *dist_px, + float r_loc[3], + float r_no[3], + int *r_index, + Object **r_ob, + float r_obmat[4][4], + float r_face_nor[3]) { sctx->runtime.depsgraph = depsgraph; sctx->runtime.region = region; sctx->runtime.v3d = v3d; - 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); + BLI_assert((snap_to_flag & SCE_SNAP_MODE_GEOM) != 0); - short retval = 0; + eSnapMode retval = SCE_SNAP_MODE_NONE; bool has_hit = false; Object *ob_eval = nullptr; @@ -3040,7 +3049,7 @@ static short transform_snap_context_project_view3d_mixed_impl(SnapObjectContext float ray_start[3], ray_normal[3]; if (!ED_view3d_win_to_ray_clipped_ex( depsgraph, region, v3d, mval, nullptr, ray_normal, ray_start, true)) { - return 0; + return SCE_SNAP_MODE_NONE; } float dummy_ray_depth = BVH_RAYCAST_DIST_MAX; @@ -3084,7 +3093,7 @@ static short transform_snap_context_project_view3d_mixed_impl(SnapObjectContext if (snap_to_flag & (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR)) { - short elem_test, elem = 0; + eSnapMode elem_test, elem = SCE_SNAP_MODE_NONE; float dist_px_tmp = *dist_px; copy_m4_m4(sctx->runtime.pmat, rv3d->persmat); @@ -3094,9 +3103,11 @@ static short transform_snap_context_project_view3d_mixed_impl(SnapObjectContext sctx->runtime.view_proj = rv3d->is_persp ? VIEW_PROJ_PERSP : VIEW_PROJ_ORTHO; /* First snap to edge instead of middle or perpendicular. */ - sctx->runtime.snap_to_flag = snap_to_flag & (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE); + sctx->runtime.snap_to_flag = static_cast<eSnapMode>( + snap_to_flag & (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE)); if (snap_to_flag & (SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR)) { - sctx->runtime.snap_to_flag |= SCE_SNAP_MODE_EDGE; + sctx->runtime.snap_to_flag = static_cast<eSnapMode>(sctx->runtime.snap_to_flag | + SCE_SNAP_MODE_EDGE); } planes_from_projmat(sctx->runtime.pmat, @@ -3180,21 +3191,21 @@ static short transform_snap_context_project_view3d_mixed_impl(SnapObjectContext return retval; } -short ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx, - Depsgraph *depsgraph, - const ARegion *region, - const View3D *v3d, - const ushort snap_to, - const SnapObjectParams *params, - const float mval[2], - const float prev_co[3], - float *dist_px, - float r_loc[3], - float r_no[3], - int *r_index, - Object **r_ob, - float r_obmat[4][4], - float r_face_nor[3]) +eSnapMode ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx, + Depsgraph *depsgraph, + const ARegion *region, + const View3D *v3d, + const eSnapMode snap_to, + const SnapObjectParams *params, + const float mval[2], + const float prev_co[3], + float *dist_px, + float r_loc[3], + float r_no[3], + int *r_index, + Object **r_ob, + float r_obmat[4][4], + float r_face_nor[3]) { return transform_snap_context_project_view3d_mixed_impl(sctx, depsgraph, @@ -3213,17 +3224,17 @@ short ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx, r_face_nor); } -short ED_transform_snap_object_project_view3d(SnapObjectContext *sctx, - Depsgraph *depsgraph, - const ARegion *region, - const View3D *v3d, - const ushort snap_to, - const SnapObjectParams *params, - const float mval[2], - const float prev_co[3], - float *dist_px, - float r_loc[3], - float r_no[3]) +eSnapMode ED_transform_snap_object_project_view3d(SnapObjectContext *sctx, + Depsgraph *depsgraph, + const ARegion *region, + const View3D *v3d, + const eSnapMode snap_to, + const 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, depsgraph, diff --git a/source/blender/editors/transform/transform_snap_sequencer.c b/source/blender/editors/transform/transform_snap_sequencer.c index 7dc361ff5bb..dbcae2b6320 100644 --- a/source/blender/editors/transform/transform_snap_sequencer.c +++ b/source/blender/editors/transform/transform_snap_sequencer.c @@ -25,6 +25,7 @@ #include "SEQ_relations.h" #include "SEQ_render.h" #include "SEQ_sequencer.h" +#include "SEQ_time.h" #include "transform.h" #include "transform_snap.h" @@ -65,14 +66,14 @@ static void seq_snap_source_points_build(TransSeqSnapData *snap_data, SeqCollect SEQ_ITERATOR_FOREACH (seq, snap_sources) { int left = 0, right = 0; if (seq->flag & SEQ_LEFTSEL) { - left = right = seq->startdisp; + left = right = SEQ_time_left_handle_frame_get(seq); } else if (seq->flag & SEQ_RIGHTSEL) { - left = right = seq->enddisp; + left = right = SEQ_time_right_handle_frame_get(seq); } else { - left = seq->startdisp; - right = seq->enddisp; + left = SEQ_time_left_handle_frame_get(seq); + right = SEQ_time_right_handle_frame_get(seq); } snap_data->source_snap_points[i] = left; @@ -193,21 +194,24 @@ static void seq_snap_target_points_build(Scene *scene, Sequence *seq; SEQ_ITERATOR_FOREACH (seq, snap_targets) { - snap_data->target_snap_points[i] = seq->startdisp; - snap_data->target_snap_points[i + 1] = seq->enddisp; + snap_data->target_snap_points[i] = SEQ_time_left_handle_frame_get(seq); + snap_data->target_snap_points[i + 1] = SEQ_time_right_handle_frame_get(seq); i += 2; if (snap_mode & SEQ_SNAP_TO_STRIP_HOLD) { - int content_start = min_ii(seq->enddisp, seq->start); - int content_end = max_ii(seq->startdisp, seq->start + seq->len); + int content_start = min_ii(SEQ_time_right_handle_frame_get(seq), seq->start); + int content_end = max_ii(SEQ_time_left_handle_frame_get(seq), seq->start + seq->len); /* Effects and single image strips produce incorrect content length. Skip these strips. */ if ((seq->type & SEQ_TYPE_EFFECT) != 0 || seq->len == 1) { - content_start = seq->startdisp; - content_end = seq->enddisp; + content_start = SEQ_time_left_handle_frame_get(seq); + content_end = SEQ_time_right_handle_frame_get(seq); } - CLAMP(content_start, seq->startdisp, seq->enddisp); - CLAMP(content_end, seq->startdisp, seq->enddisp); + CLAMP(content_start, + SEQ_time_left_handle_frame_get(seq), + SEQ_time_right_handle_frame_get(seq)); + CLAMP( + content_end, SEQ_time_left_handle_frame_get(seq), SEQ_time_right_handle_frame_get(seq)); snap_data->target_snap_points[i] = content_start; snap_data->target_snap_points[i + 1] = content_end; diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt index 89d80d582f8..5c2a3374aa1 100644 --- a/source/blender/editors/util/CMakeLists.txt +++ b/source/blender/editors/util/CMakeLists.txt @@ -91,6 +91,7 @@ set(SRC ../include/ED_uvedit.h ../include/ED_view3d.h ../include/ED_view3d_offscreen.h + ../include/UI_grid_view.hh ../include/UI_icons.h ../include/UI_interface.h ../include/UI_interface.hh diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c index f07364cd4d3..3cfe6bd61a4 100644 --- a/source/blender/editors/util/ed_util.c +++ b/source/blender/editors/util/ed_util.c @@ -19,6 +19,7 @@ #include "BKE_collection.h" #include "BKE_global.h" +#include "BKE_layer.h" #include "BKE_lib_id.h" #include "BKE_lib_remap.h" #include "BKE_main.h" @@ -40,6 +41,7 @@ #include "ED_mesh.h" #include "ED_object.h" #include "ED_paint.h" +#include "ED_screen.h" #include "ED_space_api.h" #include "ED_util.h" @@ -137,8 +139,12 @@ void ED_editors_init(bContext *C) ED_object_posemode_enter_ex(bmain, ob); } - /* Other edit/paint/etc. modes are only settable for objects in active scene currently. */ - if (!BKE_collection_has_object_recursive(scene->master_collection, ob)) { + /* Other edit/paint/etc. modes are only settable for objects visible in active scene currently. + * Otherwise, they (and their obdata) may not be (fully) evaluated, which is mandatory for some + * modes like Sculpt. + * Ref. T98225. */ + if (!BKE_collection_has_object_recursive(scene->master_collection, ob) || + !BKE_scene_has_object(scene, ob) || (ob->visibility_flag & OB_HIDE_VIEWPORT) != 0) { continue; } @@ -182,6 +188,18 @@ void ED_editors_init(bContext *C) ED_space_image_paint_update(bmain, wm, scene); } + /* Enforce a full redraw for the first time areas/regions get drawn. Further region init/refresh + * just triggers non-rebuild redraws (#RGN_DRAW_NO_REBUILD). Usually a full redraw would be + * triggered by a `NC_WM | ND_FILEREAD` notifier, but if a startup script calls an operator that + * redraws the window, notifiers are not handled before the operator runs. See T98461. */ + LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { + const bScreen *screen = WM_window_get_active_screen(win); + + ED_screen_areas_iter (win, screen, area) { + ED_area_tag_redraw(area); + } + } + ED_assetlist_storage_tag_main_data_dirty(); SWAP(int, reports->flag, reports_flag_prev); diff --git a/source/blender/editors/util/ed_util_ops.cc b/source/blender/editors/util/ed_util_ops.cc index 803e65590a0..af3589e50f0 100644 --- a/source/blender/editors/util/ed_util_ops.cc +++ b/source/blender/editors/util/ed_util_ops.cc @@ -67,19 +67,19 @@ static bool lib_id_preview_editing_poll(bContext *C) static int lib_id_load_custom_preview_exec(bContext *C, wmOperator *op) { - char path[FILE_MAX]; + char filepath[FILE_MAX]; - RNA_string_get(op->ptr, "filepath", path); + RNA_string_get(op->ptr, "filepath", filepath); - if (!BLI_is_file(path)) { - BKE_reportf(op->reports, RPT_ERROR, "File not found '%s'", path); + if (!BLI_is_file(filepath)) { + BKE_reportf(op->reports, RPT_ERROR, "File not found '%s'", filepath); return OPERATOR_CANCELLED; } PointerRNA idptr = CTX_data_pointer_get(C, "id"); ID *id = (ID *)idptr.data; - BKE_previewimg_id_custom_set(id, path); + BKE_previewimg_id_custom_set(id, filepath); WM_event_add_notifier(C, NC_ASSET | NA_EDITED, nullptr); diff --git a/source/blender/editors/util/select_utils.c b/source/blender/editors/util/select_utils.c index 53f9aca8e8d..660afa4c3d7 100644 --- a/source/blender/editors/util/select_utils.c +++ b/source/blender/editors/util/select_utils.c @@ -66,15 +66,18 @@ eSelectOp ED_select_op_modal(const eSelectOp sel_op, const bool is_first) return sel_op; } -int ED_select_similar_compare_float(const float delta, const float thresh, const int compare) +bool ED_select_similar_compare_float(const float delta, + const float thresh, + const eSimilarCmp compare) { + BLI_assert(thresh >= 0.0f); switch (compare) { case SIM_CMP_EQ: return (fabsf(delta) <= thresh); case SIM_CMP_GT: - return ((delta + thresh) >= 0.0); + return ((delta + thresh) >= 0.0f); case SIM_CMP_LT: - return ((delta - thresh) <= 0.0); + return ((delta - thresh) <= 0.0f); default: BLI_assert_unreachable(); return 0; @@ -84,8 +87,10 @@ int ED_select_similar_compare_float(const float delta, const float thresh, const bool ED_select_similar_compare_float_tree(const KDTree_1d *tree, const float length, const float thresh, - const int compare) + const eSimilarCmp compare) { + BLI_assert(compare == SIM_CMP_EQ || length >= 0.0f); /* See precision note below. */ + /* Length of the edge we want to compare against. */ float nearest_edge_length; @@ -112,6 +117,7 @@ bool ED_select_similar_compare_float_tree(const KDTree_1d *tree, KDTreeNearest_1d nearest; if (BLI_kdtree_1d_find_nearest(tree, &nearest_edge_length, &nearest) != -1) { + BLI_assert(compare == SIM_CMP_EQ || nearest.co[0] >= 0.0f); /* See precision note above. */ float delta = length - nearest.co[0]; return ED_select_similar_compare_float(delta, thresh, compare); } @@ -119,11 +125,11 @@ bool ED_select_similar_compare_float_tree(const KDTree_1d *tree, return false; } -eSelectOp ED_select_op_from_operator(wmOperator *op) +eSelectOp ED_select_op_from_operator(PointerRNA *ptr) { - const bool extend = RNA_boolean_get(op->ptr, "extend"); - const bool deselect = RNA_boolean_get(op->ptr, "deselect"); - const bool toggle = RNA_boolean_get(op->ptr, "toggle"); + const bool extend = RNA_boolean_get(ptr, "extend"); + const bool deselect = RNA_boolean_get(ptr, "deselect"); + const bool toggle = RNA_boolean_get(ptr, "toggle"); if (extend) { return SEL_OP_ADD; @@ -137,10 +143,56 @@ eSelectOp ED_select_op_from_operator(wmOperator *op) return SEL_OP_SET; } -void ED_select_pick_params_from_operator(wmOperator *op, struct SelectPick_Params *params) +void ED_select_pick_params_from_operator(PointerRNA *ptr, struct SelectPick_Params *params) { memset(params, 0x0, sizeof(*params)); - params->sel_op = ED_select_op_from_operator(op); - params->deselect_all = RNA_boolean_get(op->ptr, "deselect_all"); - params->select_passthrough = RNA_boolean_get(op->ptr, "select_passthrough"); + params->sel_op = ED_select_op_from_operator(ptr); + params->deselect_all = RNA_boolean_get(ptr, "deselect_all"); + params->select_passthrough = RNA_boolean_get(ptr, "select_passthrough"); +} + +/* -------------------------------------------------------------------- */ +/** \name Operator Naming Callbacks + * \{ */ + +const char *ED_select_pick_get_name(wmOperatorType *UNUSED(ot), PointerRNA *ptr) +{ + struct SelectPick_Params params = {0}; + ED_select_pick_params_from_operator(ptr, ¶ms); + switch (params.sel_op) { + case SEL_OP_ADD: + return "Select (Extend)"; + case SEL_OP_SUB: + return "Select (Deselect)"; + case SEL_OP_XOR: + return "Select (Toggle)"; + case SEL_OP_AND: + BLI_assert_unreachable(); + ATTR_FALLTHROUGH; + case SEL_OP_SET: + break; + } + return "Select"; } + +const char *ED_select_circle_get_name(wmOperatorType *UNUSED(ot), PointerRNA *ptr) +{ + /* Matches options in #WM_operator_properties_select_operation_simple */ + const eSelectOp sel_op = RNA_enum_get(ptr, "mode"); + switch (sel_op) { + case SEL_OP_ADD: + return "Circle Select (Extend)"; + case SEL_OP_SUB: + return "Circle Select (Deselect)"; + case SEL_OP_XOR: + ATTR_FALLTHROUGH; + case SEL_OP_AND: + BLI_assert_unreachable(); + ATTR_FALLTHROUGH; + case SEL_OP_SET: + break; + } + return "Circle Select"; +} + +/** \} */ diff --git a/source/blender/editors/uvedit/uvedit_intern.h b/source/blender/editors/uvedit/uvedit_intern.h index 181982f0b2c..04128cf378c 100644 --- a/source/blender/editors/uvedit/uvedit_intern.h +++ b/source/blender/editors/uvedit/uvedit_intern.h @@ -182,5 +182,6 @@ void UV_OT_select_circle(struct wmOperatorType *ot); void UV_OT_select_more(struct wmOperatorType *ot); void UV_OT_select_less(struct wmOperatorType *ot); void UV_OT_select_overlap(struct wmOperatorType *ot); +void UV_OT_select_similar(struct wmOperatorType *ot); /* Used only when UV sync select is disabled. */ void UV_OT_select_mode(struct wmOperatorType *ot); diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index fe6f9f0d513..0b5d6592426 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -2044,6 +2044,7 @@ void ED_operatortypes_uvedit(void) WM_operatortype_append(UV_OT_select_pinned); WM_operatortype_append(UV_OT_select_box); WM_operatortype_append(UV_OT_select_lasso); + WM_operatortype_append(UV_OT_select_similar); WM_operatortype_append(UV_OT_select_circle); WM_operatortype_append(UV_OT_select_more); WM_operatortype_append(UV_OT_select_less); diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c index 13dac431b57..8dcf2ceb679 100644 --- a/source/blender/editors/uvedit/uvedit_select.c +++ b/source/blender/editors/uvedit/uvedit_select.c @@ -12,6 +12,7 @@ #include "MEM_guardedalloc.h" #include "DNA_image_types.h" +#include "DNA_material_types.h" #include "DNA_meshdata_types.h" #include "DNA_node_types.h" #include "DNA_object_types.h" @@ -22,6 +23,7 @@ #include "BLI_blenlib.h" #include "BLI_hash.h" #include "BLI_kdopbvh.h" +#include "BLI_kdtree.h" #include "BLI_lasso_2d.h" #include "BLI_math.h" #include "BLI_polyfill_2d.h" @@ -31,6 +33,7 @@ #include "BKE_customdata.h" #include "BKE_editmesh.h" #include "BKE_layer.h" +#include "BKE_material.h" #include "BKE_mesh.h" #include "BKE_mesh_mapping.h" #include "BKE_report.h" @@ -75,6 +78,16 @@ static void uv_select_tag_update_for_object(Depsgraph *depsgraph, const ToolSettings *ts, Object *obedit); +typedef enum { + UV_SSIM_AREA_UV = 1000, + UV_SSIM_AREA_3D, + UV_SSIM_LENGTH_UV, + UV_SSIM_LENGTH_3D, + UV_SSIM_SIDES, + UV_SSIM_PIN, + UV_SSIM_MATERIAL, +} eUVSelectSimilar; + /* -------------------------------------------------------------------- */ /** \name Active Selection Tracking * @@ -586,12 +599,25 @@ bool uvedit_uv_select_test_ex(const ToolSettings *ts, BMLoop *l, const int cd_lo if (ts->selectmode & SCE_SELECT_FACE) { return BM_elem_flag_test_bool(l->f, BM_ELEM_SELECT); } + if (ts->selectmode & SCE_SELECT_EDGE) { + /* Are you looking for `uvedit_edge_select_test(...)` instead? */ + } return BM_elem_flag_test_bool(l->v, BM_ELEM_SELECT); } MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + + if (ts->selectmode & SCE_SELECT_FACE) { + /* Are you looking for `uvedit_face_select_test(...)` instead? */ + } + + if (ts->selectmode & SCE_SELECT_EDGE) { + /* Are you looking for `uvedit_edge_select_test(...)` instead? */ + } + return (luv->flag & MLOOPUV_VERTSEL) != 0; } + bool uvedit_uv_select_test(const Scene *scene, BMLoop *l, const int cd_loop_uv_offset) { return uvedit_uv_select_test_ex(scene->toolsettings, l, cd_loop_uv_offset); @@ -653,6 +679,9 @@ void uvedit_uv_select_shared_vert(const Scene *scene, e_first = e_iter = l->e; do { BMLoop *l_radial_iter = e_iter->l; + if (!l_radial_iter) { + continue; /* Skip wire edges with no loops. */ + } do { if (l_radial_iter->v == l->v) { if (uvedit_face_visible_test(scene, l_radial_iter->f)) { @@ -696,6 +725,10 @@ void uvedit_uv_select_enable(const Scene *scene, { const ToolSettings *ts = scene->toolsettings; + if (ts->selectmode & SCE_SELECT_EDGE) { + /* Are you looking for `uvedit_edge_select_set(...)` instead? */ + } + if (ts->uv_flag & UV_SYNC_SELECTION) { if (ts->selectmode & SCE_SELECT_FACE) { BM_face_select_set(em->bm, l->f, true); @@ -2422,7 +2455,7 @@ static bool uv_mouse_select_multi(bContext *C, UvNearestHit hit = UV_NEAREST_HIT_INIT_DIST_PX(®ion->v2d, 75.0f); int selectmode, sticky; bool found_item = false; - /* 0 == don't flush, 1 == sel, -1 == desel; only use when selection sync is enabled */ + /* 0 == don't flush, 1 == sel, -1 == deselect; only use when selection sync is enabled. */ int flush = 0; /* Penalty (in pixels) applied to elements that are already selected @@ -2511,8 +2544,15 @@ static bool uv_mouse_select_multi(bContext *C, else if (selectmode == UV_SELECT_EDGE) { is_selected = uvedit_edge_select_test(scene, hit.l, cd_loop_uv_offset); } - else { /* Vertex or island. */ - is_selected = uvedit_uv_select_test(scene, hit.l, cd_loop_uv_offset); + else { + /* Vertex or island. For island (if we were using #uv_find_nearest_face_multi_ex, see above), + * `hit.l` is NULL, use `hit.efa` instead. */ + if (hit.l != NULL) { + is_selected = uvedit_uv_select_test(scene, hit.l, cd_loop_uv_offset); + } + else { + is_selected = uvedit_face_select_test(scene, hit.efa, cd_loop_uv_offset); + } } } @@ -2645,7 +2685,7 @@ static int uv_select_exec(bContext *C, wmOperator *op) RNA_float_get_array(op->ptr, "location", co); struct SelectPick_Params params = {0}; - ED_select_pick_params_from_operator(op, ¶ms); + ED_select_pick_params_from_operator(op->ptr, ¶ms); const bool changed = uv_mouse_select(C, co, ¶ms); @@ -2680,6 +2720,7 @@ void UV_OT_select(wmOperatorType *ot) ot->exec = uv_select_exec; ot->invoke = uv_select_invoke; ot->poll = ED_operator_uvedit; /* requires space image */ + ot->get_name = ED_select_pick_get_name; /* properties */ PropertyRNA *prop; @@ -2724,7 +2765,7 @@ static int uv_mouse_select_loop_generic_multi(bContext *C, const ToolSettings *ts = scene->toolsettings; UvNearestHit hit = UV_NEAREST_HIT_INIT_MAX(®ion->v2d); bool found_item = false; - /* 0 == don't flush, 1 == sel, -1 == desel; only use when selection sync is enabled */ + /* 0 == don't flush, 1 == sel, -1 == deselect; only use when selection sync is enabled. */ int flush = 0; /* Find edge. */ @@ -3278,8 +3319,6 @@ static void uv_select_flush_from_tag_face(const Scene *scene, Object *obedit, co BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, efa_index) { if (BM_elem_flag_test(efa, BM_ELEM_TAG)) { - /* tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); */ /* UNUSED */ - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); if (select) { @@ -3349,8 +3388,6 @@ static void uv_select_flush_from_tag_loop(const Scene *scene, Object *obedit, co /* now select tagged verts */ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - /* tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); */ /* UNUSED */ - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { if (BM_elem_flag_test(l->v, BM_ELEM_TAG)) { uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset); @@ -3369,8 +3406,6 @@ static void uv_select_flush_from_tag_loop(const Scene *scene, Object *obedit, co } BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, efa_index) { - /* tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); */ /* UNUSED */ - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { if (BM_elem_flag_test(l, BM_ELEM_TAG)) { uv_select_flush_from_tag_sticky_loc_internal( @@ -3849,6 +3884,7 @@ void UV_OT_select_circle(wmOperatorType *ot) ot->exec = uv_circle_select_exec; ot->poll = ED_operator_uvedit_space_image; /* requires space image */ ot->cancel = WM_gesture_circle_cancel; + ot->get_name = ED_select_circle_get_name; /* flags */ ot->flag = OPTYPE_UNDO; @@ -4406,6 +4442,550 @@ void UV_OT_select_overlap(wmOperatorType *ot) } /** \} */ +/** \name Select Similar Operator + * \{ */ + +static float get_uv_vert_needle(const eUVSelectSimilar type, + BMVert *vert, + const float ob_m3[3][3], + MLoopUV *luv, + const int cd_loop_uv_offset) +{ + float result = 0.0f; + switch (type) { + case UV_SSIM_AREA_UV: { + BMFace *f; + BMIter iter; + BM_ITER_ELEM (f, &iter, vert, BM_FACES_OF_VERT) { + result += BM_face_calc_area_uv(f, cd_loop_uv_offset); + } + } break; + case UV_SSIM_AREA_3D: { + BMFace *f; + BMIter iter; + BM_ITER_ELEM (f, &iter, vert, BM_FACES_OF_VERT) { + result += BM_face_calc_area_with_mat3(f, ob_m3); + } + } break; + case UV_SSIM_SIDES: { + BMEdge *e; + BMIter iter; + BM_ITER_ELEM (e, &iter, vert, BM_EDGES_OF_VERT) { + result += 1.0f; + } + } break; + case UV_SSIM_PIN: + return (luv->flag & MLOOPUV_PINNED) ? 1.0f : 0.0f; + default: + BLI_assert_unreachable(); + return false; + } + + return result; +} + +static float get_uv_edge_needle(const eUVSelectSimilar type, + BMEdge *edge, + const float ob_m3[3][3], + MLoopUV *luv_a, + MLoopUV *luv_b, + const int cd_loop_uv_offset) +{ + float result = 0.0f; + switch (type) { + case UV_SSIM_AREA_UV: { + BMFace *f; + BMIter iter; + BM_ITER_ELEM (f, &iter, edge, BM_FACES_OF_EDGE) { + result += BM_face_calc_area_uv(f, cd_loop_uv_offset); + } + } break; + case UV_SSIM_AREA_3D: { + BMFace *f; + BMIter iter; + BM_ITER_ELEM (f, &iter, edge, BM_FACES_OF_EDGE) { + result += BM_face_calc_area_with_mat3(f, ob_m3); + } + } break; + case UV_SSIM_LENGTH_UV: + return len_v2v2(luv_a->uv, luv_b->uv); + case UV_SSIM_LENGTH_3D: + return len_v3v3(edge->v1->co, edge->v2->co); + case UV_SSIM_SIDES: { + BMEdge *e; + BMIter iter; + BM_ITER_ELEM (e, &iter, edge, BM_FACES_OF_EDGE) { + result += 1.0f; + } + } break; + case UV_SSIM_PIN: + if (luv_a->flag & MLOOPUV_PINNED) { + result += 1.0f; + } + if (luv_b->flag & MLOOPUV_PINNED) { + result += 1.0f; + } + break; + default: + BLI_assert_unreachable(); + return false; + } + + return result; +} + +static float get_uv_face_needle(const eUVSelectSimilar type, + BMFace *face, + const float ob_m3[3][3], + const int cd_loop_uv_offset) +{ + float result = 0.0f; + switch (type) { + case UV_SSIM_AREA_UV: + return BM_face_calc_area_uv(face, cd_loop_uv_offset); + case UV_SSIM_AREA_3D: + return BM_face_calc_area_with_mat3(face, ob_m3); + case UV_SSIM_SIDES: + return face->len; + case UV_SSIM_PIN: { + BMLoop *l; + BMIter liter; + BM_ITER_ELEM (l, &liter, face, BM_LOOPS_OF_FACE) { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + if (luv->flag & MLOOPUV_PINNED) { + result += 1.0f; + } + } + } break; + case UV_SSIM_MATERIAL: + return face->mat_nr; + default: + BLI_assert_unreachable(); + return false; + } + return result; +} + +static int uv_select_similar_vert_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + ToolSettings *ts = CTX_data_tool_settings(C); + + const eUVSelectSimilar type = RNA_enum_get(op->ptr, "type"); + const float threshold = RNA_float_get(op->ptr, "threshold"); + const eSimilarCmp compare = RNA_enum_get(op->ptr, "compare"); + + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( + view_layer, ((View3D *)NULL), &objects_len); + + int max_verts_selected_all = 0; + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(ob); + BMFace *face; + BMIter iter; + BM_ITER_MESH (face, &iter, em->bm, BM_FACES_OF_MESH) { + if (!uvedit_face_visible_test(scene, face)) { + continue; + } + max_verts_selected_all += face->len; + } + /* TODO: Get a tighter bounds */ + } + + int tree_index = 0; + KDTree_1d *tree_1d = BLI_kdtree_1d_new(max_verts_selected_all); + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(ob); + BMesh *bm = em->bm; + if (bm->totvertsel == 0) { + continue; + } + + const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); + float ob_m3[3][3]; + copy_m3_m4(ob_m3, ob->obmat); + + BMFace *face; + BMIter iter; + BM_ITER_MESH (face, &iter, em->bm, BM_FACES_OF_MESH) { + if (!uvedit_face_visible_test(scene, face)) { + continue; + } + BMLoop *l; + BMIter liter; + BM_ITER_ELEM (l, &liter, face, BM_LOOPS_OF_FACE) { + if (!uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { + continue; + } + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + float needle = get_uv_vert_needle(type, l->v, ob_m3, luv, cd_loop_uv_offset); + BLI_kdtree_1d_insert(tree_1d, tree_index++, &needle); + } + } + } + + if (tree_1d != NULL) { + BLI_kdtree_1d_deduplicate(tree_1d); + BLI_kdtree_1d_balance(tree_1d); + } + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(ob); + BMesh *bm = em->bm; + if (bm->totvertsel == 0) { + continue; + } + + bool changed = false; + const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); + float ob_m3[3][3]; + copy_m3_m4(ob_m3, ob->obmat); + + BMFace *face; + BMIter iter; + BM_ITER_MESH (face, &iter, em->bm, BM_FACES_OF_MESH) { + if (!uvedit_face_visible_test(scene, face)) { + continue; + } + BMLoop *l; + BMIter liter; + BM_ITER_ELEM (l, &liter, face, BM_LOOPS_OF_FACE) { + if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { + continue; /* Already selected. */ + } + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + const float needle = get_uv_vert_needle(type, l->v, ob_m3, luv, cd_loop_uv_offset); + bool select = ED_select_similar_compare_float_tree(tree_1d, needle, threshold, compare); + if (select) { + uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset); + changed = true; + } + } + if (changed) { + uv_select_tag_update_for_object(depsgraph, ts, ob); + } + } + } + + MEM_SAFE_FREE(objects); + BLI_kdtree_1d_free(tree_1d); + return OPERATOR_FINISHED; +} + +static int uv_select_similar_edge_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + ToolSettings *ts = CTX_data_tool_settings(C); + + const eUVSelectSimilar type = RNA_enum_get(op->ptr, "type"); + const float threshold = RNA_float_get(op->ptr, "threshold"); + const eSimilarCmp compare = RNA_enum_get(op->ptr, "compare"); + + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( + view_layer, ((View3D *)NULL), &objects_len); + + int max_edges_selected_all = 0; + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(ob); + BMFace *face; + BMIter iter; + BM_ITER_MESH (face, &iter, em->bm, BM_FACES_OF_MESH) { + if (!uvedit_face_visible_test(scene, face)) { + continue; + } + max_edges_selected_all += face->len; + } + /* TODO: Get a tighter bounds. */ + } + + int tree_index = 0; + KDTree_1d *tree_1d = BLI_kdtree_1d_new(max_edges_selected_all); + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(ob); + BMesh *bm = em->bm; + if (bm->totvertsel == 0) { + continue; + } + + const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); + float ob_m3[3][3]; + copy_m3_m4(ob_m3, ob->obmat); + + BMFace *face; + BMIter iter; + BM_ITER_MESH (face, &iter, em->bm, BM_FACES_OF_MESH) { + if (!uvedit_face_visible_test(scene, face)) { + continue; + } + BMLoop *l; + BMIter liter; + BM_ITER_ELEM (l, &liter, face, BM_LOOPS_OF_FACE) { + if (!uvedit_edge_select_test(scene, l, cd_loop_uv_offset)) { + continue; + } + + MLoopUV *luv_a = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + MLoopUV *luv_b = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset); + float needle = get_uv_edge_needle(type, l->e, ob_m3, luv_a, luv_b, cd_loop_uv_offset); + if (tree_1d) { + BLI_kdtree_1d_insert(tree_1d, tree_index++, &needle); + } + } + } + } + + if (tree_1d != NULL) { + BLI_kdtree_1d_deduplicate(tree_1d); + BLI_kdtree_1d_balance(tree_1d); + } + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(ob); + BMesh *bm = em->bm; + if (bm->totvertsel == 0) { + continue; + } + + bool changed = false; + const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); + float ob_m3[3][3]; + copy_m3_m4(ob_m3, ob->obmat); + + BMFace *face; + BMIter iter; + BM_ITER_MESH (face, &iter, em->bm, BM_FACES_OF_MESH) { + if (!uvedit_face_visible_test(scene, face)) { + continue; + } + BMLoop *l; + BMIter liter; + BM_ITER_ELEM (l, &liter, face, BM_LOOPS_OF_FACE) { + if (uvedit_edge_select_test(scene, l, cd_loop_uv_offset)) { + continue; /* Already selected. */ + } + + MLoopUV *luv_a = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + MLoopUV *luv_b = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset); + float needle = get_uv_edge_needle(type, l->e, ob_m3, luv_a, luv_b, cd_loop_uv_offset); + bool select = ED_select_similar_compare_float_tree(tree_1d, needle, threshold, compare); + if (select) { + uvedit_edge_select_set(scene, em, l, select, false, cd_loop_uv_offset); + changed = true; + } + } + if (changed) { + uv_select_tag_update_for_object(depsgraph, ts, ob); + } + } + } + + MEM_SAFE_FREE(objects); + BLI_kdtree_1d_free(tree_1d); + return OPERATOR_FINISHED; +} + +static int uv_select_similar_face_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + ToolSettings *ts = CTX_data_tool_settings(C); + + const eUVSelectSimilar type = RNA_enum_get(op->ptr, "type"); + const float threshold = RNA_float_get(op->ptr, "threshold"); + const eSimilarCmp compare = RNA_enum_get(op->ptr, "compare"); + + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( + view_layer, ((View3D *)NULL), &objects_len); + + int max_faces_selected_all = 0; + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(ob); + max_faces_selected_all += em->bm->totfacesel; + /* TODO: Get a tighter bounds */ + } + + int tree_index = 0; + KDTree_1d *tree_1d = BLI_kdtree_1d_new(max_faces_selected_all); + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob = objects[ob_index]; + + BMEditMesh *em = BKE_editmesh_from_object(ob); + BMesh *bm = em->bm; + + float ob_m3[3][3]; + copy_m3_m4(ob_m3, ob->obmat); + + const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); + + BMFace *face; + BMIter iter; + BM_ITER_MESH (face, &iter, bm, BM_FACES_OF_MESH) { + if (!uvedit_face_visible_test(scene, face)) { + continue; + } + if (!uvedit_face_select_test(scene, face, cd_loop_uv_offset)) { + continue; + } + + float needle = get_uv_face_needle(type, face, ob_m3, cd_loop_uv_offset); + if (tree_1d) { + BLI_kdtree_1d_insert(tree_1d, tree_index++, &needle); + } + } + } + + if (tree_1d != NULL) { + BLI_kdtree_1d_deduplicate(tree_1d); + BLI_kdtree_1d_balance(tree_1d); + } + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob = objects[ob_index]; + + BMEditMesh *em = BKE_editmesh_from_object(ob); + BMesh *bm = em->bm; + bool changed = false; + bool do_history = false; + const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); + + float ob_m3[3][3]; + copy_m3_m4(ob_m3, ob->obmat); + + BMFace *face; + BMIter iter; + BM_ITER_MESH (face, &iter, bm, BM_FACES_OF_MESH) { + if (!uvedit_face_visible_test(scene, face)) { + continue; + } + if (uvedit_face_select_test(scene, face, cd_loop_uv_offset)) { + continue; + } + + float needle = get_uv_face_needle(type, face, ob_m3, cd_loop_uv_offset); + + bool select = ED_select_similar_compare_float_tree(tree_1d, needle, threshold, compare); + if (select) { + uvedit_face_select_set(scene, em, face, select, do_history, cd_loop_uv_offset); + changed = true; + } + } + if (changed) { + uv_select_tag_update_for_object(depsgraph, ts, ob); + } + } + + MEM_SAFE_FREE(objects); + BLI_kdtree_1d_free(tree_1d); + return OPERATOR_FINISHED; +} + +/* Select similar UV faces/edges/verts based on current selection. */ +static int uv_select_similar_exec(bContext *C, wmOperator *op) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "threshold"); + + if (!RNA_property_is_set(op->ptr, prop)) { + RNA_property_float_set(op->ptr, prop, ts->select_thresh); + } + else { + ts->select_thresh = RNA_property_float_get(op->ptr, prop); + } + + int selectmode = (ts->uv_flag & UV_SYNC_SELECTION) ? ts->selectmode : ts->uv_selectmode; + if (selectmode & UV_SELECT_EDGE) { + return uv_select_similar_edge_exec(C, op); + } + else if (selectmode & UV_SELECT_FACE) { + return uv_select_similar_face_exec(C, op); + } + if (selectmode & UV_SELECT_ISLAND) { + // return uv_select_similar_island_exec(C, op); + } + + return uv_select_similar_vert_exec(C, op); +} + +static EnumPropertyItem prop_vert_similar_types[] = {{UV_SSIM_PIN, "PIN", 0, "Pinned", ""}, {0}}; + +static EnumPropertyItem prop_edge_similar_types[] = { + {UV_SSIM_LENGTH_UV, "LENGTH", 0, "Length", ""}, + {UV_SSIM_LENGTH_3D, "LENGTH_3D", 0, "Length 3D", ""}, + {UV_SSIM_PIN, "PIN", 0, "Pinned", ""}, + {0}}; + +static EnumPropertyItem prop_face_similar_types[] = { + {UV_SSIM_AREA_UV, "AREA", 0, "Area", ""}, + {UV_SSIM_AREA_3D, "AREA_3D", 0, "Area 3D", ""}, + {UV_SSIM_SIDES, "SIDES", 0, "Polygon Sides", ""}, + {UV_SSIM_MATERIAL, "MATERIAL", 0, "Material", ""}, + {0}}; + +static EnumPropertyItem prop_similar_compare_types[] = {{SIM_CMP_EQ, "EQUAL", 0, "Equal", ""}, + {SIM_CMP_GT, "GREATER", 0, "Greater", ""}, + {SIM_CMP_LT, "LESS", 0, "Less", ""}, + {0}}; + +static const EnumPropertyItem *uv_select_similar_type_itemf(bContext *C, + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + bool *UNUSED(r_free)) +{ + const ToolSettings *ts = CTX_data_tool_settings(C); + if (ts) { + int selectmode = (ts->uv_flag & UV_SYNC_SELECTION) ? ts->selectmode : ts->uv_selectmode; + if (selectmode & UV_SELECT_EDGE) { + return prop_edge_similar_types; + } + if (selectmode & UV_SELECT_FACE) { + return prop_face_similar_types; + } + } + + return prop_vert_similar_types; +} +void UV_OT_select_similar(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Similar"; + ot->description = "Select similar UVs by property types"; + ot->idname = "UV_OT_select_similar"; + + /* api callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = uv_select_similar_exec; + ot->poll = ED_operator_uvedit_space_image; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + PropertyRNA *prop = ot->prop = RNA_def_enum( + ot->srna, "type", prop_vert_similar_types, SIMVERT_NORMAL, "Type", ""); + RNA_def_enum_funcs(prop, uv_select_similar_type_itemf); + RNA_def_enum(ot->srna, "compare", prop_similar_compare_types, SIM_CMP_EQ, "Compare", ""); + RNA_def_float(ot->srna, "threshold", 0.0f, 0.0f, 1.0f, "Threshold", "", 0.0f, 1.0f); +} + +/** \} */ /* -------------------------------------------------------------------- */ /** \name Selected Elements as Arrays (Vertex, Edge & Faces) diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c index bacf321fce1..55e44607f6f 100644 --- a/source/blender/editors/uvedit/uvedit_smart_stitch.c +++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c @@ -1716,7 +1716,7 @@ static void stitch_draw_vbo(GPUVertBuf *vbo, GPUPrimType prim_type, const float GPU_batch_discard(batch); } -/* TODO: make things pretier : store batches inside StitchPreviewer instead of the bare verts pos +/* TODO: make things prettier : store batches inside StitchPreviewer instead of the bare verts pos */ static void stitch_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), void *arg) { diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c index 34fae2ffb2a..3618286ec01 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c @@ -103,7 +103,7 @@ static bool ED_uvedit_ensure_uvs(Object *obedit) int cd_loop_uv_offset; if (em && em->bm->totface && !CustomData_has_layer(&em->bm->ldata, CD_MLOOPUV)) { - ED_mesh_uv_texture_add(obedit->data, NULL, true, true, NULL); + ED_mesh_uv_add(obedit->data, NULL, true, true, NULL); } /* Happens when there are no faces. */ @@ -267,60 +267,104 @@ static bool uvedit_have_selection_multi(const Scene *scene, return have_select; } +void ED_uvedit_get_aspect_from_material(Object *ob, + const int material_index, + float *r_aspx, + float *r_aspy) +{ + if (UNLIKELY(material_index < 0 || material_index >= ob->totcol)) { + *r_aspx = 1.0f; + *r_aspy = 1.0f; + return; + } + Image *ima; + ED_object_get_active_image(ob, material_index + 1, &ima, NULL, NULL, NULL); + ED_image_get_uv_aspect(ima, NULL, r_aspx, r_aspy); +} + void ED_uvedit_get_aspect(Object *ob, float *r_aspx, float *r_aspy) { BMEditMesh *em = BKE_editmesh_from_object(ob); BLI_assert(em != NULL); bool sloppy = true; bool selected = false; - BMFace *efa; - Image *ima; + BMFace *efa = BM_mesh_active_face_get(em->bm, sloppy, selected); + if (!efa) { + *r_aspx = 1.0f; + *r_aspy = 1.0f; + return; + } - efa = BM_mesh_active_face_get(em->bm, sloppy, selected); + ED_uvedit_get_aspect_from_material(ob, efa->mat_nr, r_aspx, r_aspy); +} - if (efa) { - ED_object_get_active_image(ob, efa->mat_nr + 1, &ima, NULL, NULL, NULL); +static bool uvedit_is_face_affected(const Scene *scene, + BMFace *efa, + const UnwrapOptions *options, + const int cd_loop_uv_offset) +{ + if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { + return false; + } - ED_image_get_uv_aspect(ima, NULL, r_aspx, r_aspy); + if (options->only_selected_faces && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) { + return false; } - else { - *r_aspx = 1.0f; - *r_aspy = 1.0f; + + if (options->topology_from_uvs && options->only_selected_uvs && + !uvedit_face_select_test(scene, efa, cd_loop_uv_offset)) { + return false; + } + + return true; +} + +/* Prepare unique indices for each unique pinned UV, even if it shares a BMVert. + */ +static void uvedit_prepare_pinned_indices(ParamHandle *handle, + BMFace *efa, + const int cd_loop_uv_offset) +{ + BMIter liter; + BMLoop *l; + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + if (luv->flag & MLOOPUV_PINNED) { + int bmvertindex = BM_elem_index_get(l->v); + GEO_uv_prepare_pin_index(handle, bmvertindex, luv->uv); + } } } static void construct_param_handle_face_add(ParamHandle *handle, const Scene *scene, BMFace *efa, - int face_index, + ParamKey face_index, const int cd_loop_uv_offset) { - ParamKey key; ParamKey *vkeys = BLI_array_alloca(vkeys, efa->len); - ParamBool *pin = BLI_array_alloca(pin, efa->len); - ParamBool *select = BLI_array_alloca(select, efa->len); - float **co = BLI_array_alloca(co, efa->len); + bool *pin = BLI_array_alloca(pin, efa->len); + bool *select = BLI_array_alloca(select, efa->len); + const float **co = BLI_array_alloca(co, efa->len); float **uv = BLI_array_alloca(uv, efa->len); int i; BMIter liter; BMLoop *l; - key = (ParamKey)face_index; - /* let parametrizer split the ngon, it can make better decisions * about which split is best for unwrapping than poly-fill. */ BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) { MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - vkeys[i] = (ParamKey)BM_elem_index_get(l->v); + vkeys[i] = GEO_uv_find_pin_index(handle, BM_elem_index_get(l->v), luv->uv); co[i] = l->v->co; uv[i] = luv->uv; pin[i] = (luv->flag & MLOOPUV_PINNED) != 0; select[i] = uvedit_uv_select_test(scene, l, cd_loop_uv_offset); } - GEO_uv_parametrizer_face_add(handle, key, i, vkeys, co, uv, pin, select); + GEO_uv_parametrizer_face_add(handle, face_index, i, vkeys, co, uv, pin, select); } /* See: construct_param_handle_multi to handle multiple objects at once. */ @@ -330,16 +374,12 @@ static ParamHandle *construct_param_handle(const Scene *scene, const UnwrapOptions *options, UnwrapResultInfo *result_info) { - ParamHandle *handle; BMFace *efa; - BMLoop *l; BMEdge *eed; - BMIter iter, liter; + BMIter iter; int i; - const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); - - handle = GEO_uv_parametrizer_construct_begin(); + ParamHandle *handle = GEO_uv_parametrizer_construct_begin(); if (options->correct_aspect) { float aspx, aspy; @@ -354,30 +394,17 @@ static ParamHandle *construct_param_handle(const Scene *scene, /* we need the vert indices */ BM_mesh_elem_index_ensure(bm, BM_VERT); + const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) { - - if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) || - (options->only_selected_faces && BM_elem_flag_test(efa, BM_ELEM_SELECT) == 0)) { - continue; + if (uvedit_is_face_affected(scene, efa, options, cd_loop_uv_offset)) { + uvedit_prepare_pinned_indices(handle, efa, cd_loop_uv_offset); } + } - if (options->topology_from_uvs) { - bool is_loopsel = false; - - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - if (options->only_selected_uvs && - (uvedit_uv_select_test(scene, l, cd_loop_uv_offset) == false)) { - continue; - } - is_loopsel = true; - break; - } - if (is_loopsel == false) { - continue; - } + BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) { + if (uvedit_is_face_affected(scene, efa, options, cd_loop_uv_offset)) { + construct_param_handle_face_add(handle, scene, efa, i, cd_loop_uv_offset); } - - construct_param_handle_face_add(handle, scene, efa, i, cd_loop_uv_offset); } if (!options->topology_from_uvs || options->topology_from_uvs_use_seams) { @@ -408,14 +435,12 @@ static ParamHandle *construct_param_handle_multi(const Scene *scene, const UnwrapOptions *options, int *count_fail) { - ParamHandle *handle; BMFace *efa; - BMLoop *l; BMEdge *eed; - BMIter iter, liter; + BMIter iter; int i; - handle = GEO_uv_parametrizer_construct_begin(); + ParamHandle *handle = GEO_uv_parametrizer_construct_begin(); if (options->correct_aspect) { Object *ob = objects[0]; @@ -444,29 +469,15 @@ static ParamHandle *construct_param_handle_multi(const Scene *scene, } BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) { - - if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) || - (options->only_selected_faces && BM_elem_flag_test(efa, BM_ELEM_SELECT) == 0)) { - continue; + if (uvedit_is_face_affected(scene, efa, options, cd_loop_uv_offset)) { + uvedit_prepare_pinned_indices(handle, efa, cd_loop_uv_offset); } + } - if (options->topology_from_uvs) { - bool is_loopsel = false; - - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - if (options->only_selected_uvs && - (uvedit_uv_select_test(scene, l, cd_loop_uv_offset) == false)) { - continue; - } - is_loopsel = true; - break; - } - if (is_loopsel == false) { - continue; - } + BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) { + if (uvedit_is_face_affected(scene, efa, options, cd_loop_uv_offset)) { + construct_param_handle_face_add(handle, scene, efa, i + offset, cd_loop_uv_offset); } - - construct_param_handle_face_add(handle, scene, efa, i + offset, cd_loop_uv_offset); } if (!options->topology_from_uvs || options->topology_from_uvs_use_seams) { @@ -493,8 +504,8 @@ static void texface_from_original_index(const Scene *scene, BMFace *efa, int index, float **r_uv, - ParamBool *r_pin, - ParamBool *r_select) + bool *r_pin, + bool *r_select) { BMLoop *l; BMIter liter; @@ -530,7 +541,6 @@ static ParamHandle *construct_param_handle_subsurfed(const Scene *scene, const UnwrapOptions *options, UnwrapResultInfo *result_info) { - ParamHandle *handle; /* index pointers */ MPoly *mpoly; MLoop *mloop; @@ -562,7 +572,7 @@ static ParamHandle *construct_param_handle_subsurfed(const Scene *scene, const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - handle = GEO_uv_parametrizer_construct_begin(); + ParamHandle *handle = GEO_uv_parametrizer_construct_begin(); if (options->correct_aspect) { float aspx, aspy; @@ -627,8 +637,8 @@ static ParamHandle *construct_param_handle_subsurfed(const Scene *scene, /* Prepare and feed faces to the solver */ for (i = 0, mpoly = subsurfedPolys; i < numOfFaces; i++, mpoly++) { ParamKey key, vkeys[4]; - ParamBool pin[4], select[4]; - float *co[4]; + bool pin[4], select[4]; + const float *co[4]; float *uv[4]; BMFace *origFace = faceMap[i]; @@ -646,7 +656,7 @@ static ParamHandle *construct_param_handle_subsurfed(const Scene *scene, mloop = &subsurfedLoops[mpoly->loopstart]; - /* We will not check for v4 here. Subsurfed mfaces always have 4 vertices. */ + /* We will not check for v4 here. Sub-surface faces always have 4 vertices. */ BLI_assert(mpoly->totloop == 4); key = (ParamKey)i; vkeys[0] = (ParamKey)mloop[0].v; @@ -1014,8 +1024,7 @@ static void uvedit_pack_islands(const Scene *scene, Object *ob, BMesh *bm) bool rotate = true; bool ignore_pinned = false; - ParamHandle *handle; - handle = construct_param_handle(scene, ob, bm, &options, NULL); + ParamHandle *handle = construct_param_handle(scene, ob, bm, &options, NULL); GEO_uv_parametrizer_pack(handle, scene->toolsettings->uvcalc_margin, rotate, ignore_pinned); GEO_uv_parametrizer_flush(handle); GEO_uv_parametrizer_delete(handle); @@ -1034,8 +1043,7 @@ static void uvedit_pack_islands_multi(const Scene *scene, bool rotate, bool ignore_pinned) { - ParamHandle *handle; - handle = construct_param_handle_multi(scene, objects, objects_len, options, NULL); + ParamHandle *handle = construct_param_handle_multi(scene, objects, objects_len, options, NULL); GEO_uv_parametrizer_pack(handle, scene->toolsettings->uvcalc_margin, rotate, ignore_pinned); GEO_uv_parametrizer_flush(handle); GEO_uv_parametrizer_delete(handle); @@ -1241,7 +1249,7 @@ void ED_uvedit_live_unwrap_begin(Scene *scene, Object *obedit) handle = construct_param_handle(scene, obedit, em->bm, &options, NULL); } - GEO_uv_parametrizer_lscm_begin(handle, PARAM_TRUE, abf); + GEO_uv_parametrizer_lscm_begin(handle, true, abf); /* Create or increase size of g_live_unwrap.handles array */ if (g_live_unwrap.handles == NULL) { @@ -1527,49 +1535,88 @@ static void uv_transform_properties(wmOperatorType *ot, int radius) } } -static void correct_uv_aspect(Object *ob, BMEditMesh *em) +static void shrink_loop_uv_by_aspect_ratio(BMFace *efa, + const int cd_loop_uv_offset, + const float aspect_y) { + BLI_assert(aspect_y != 1.0f); /* Nothing to do, should be handled by caller. */ + BLI_assert(aspect_y > 0.0f); /* Negative aspect ratios are not supported. */ + BMLoop *l; - BMIter iter, liter; - MLoopUV *luv; - BMFace *efa; - float scale, aspx, aspy; + BMIter iter; + BM_ITER_ELEM (l, &iter, efa, BM_LOOPS_OF_FACE) { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + if (aspect_y > 1.0f) { + /* Reduce round-off error, i.e. `u = (u - 0.5) / aspect_y + 0.5`. */ + luv->uv[0] = luv->uv[0] / aspect_y + (0.5f - 0.5f / aspect_y); + } + else { + /* Reduce round-off error, i.e. `v = (v - 0.5) * aspect_y + 0.5`. */ + luv->uv[1] = luv->uv[1] * aspect_y + (0.5f - 0.5f * aspect_y); + } + } +} +static void correct_uv_aspect(Object *ob, BMEditMesh *em) +{ const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - + float aspx, aspy; ED_uvedit_get_aspect(ob, &aspx, &aspy); + const float aspect_y = aspx / aspy; + if (aspect_y == 1.0f) { + /* Scaling by 1.0 has no effect. */ + return; + } + BMFace *efa; + BMIter iter; + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) { + shrink_loop_uv_by_aspect_ratio(efa, cd_loop_uv_offset, aspect_y); + } + } +} - if (aspx == aspy) { +static void correct_uv_aspect_per_face(Object *ob, BMEditMesh *em) +{ + const int materials_num = ob->totcol; + if (materials_num == 0) { + /* Without any materials, there is no aspect_y information and nothing to do. */ return; } - if (aspx > aspy) { - scale = aspy / aspx; + float *material_aspect_y = BLI_array_alloca(material_aspect_y, materials_num); + /* Lazily initialize aspect ratio for materials. */ + copy_vn_fl(material_aspect_y, materials_num, -1.0f); - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) { - continue; - } + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - luv->uv[0] = ((luv->uv[0] - 0.5f) * scale) + 0.5f; - } + BMFace *efa; + BMIter iter; + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) { + continue; } - } - else { - scale = aspx / aspy; - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) { - continue; - } + const int material_index = efa->mat_nr; + if (UNLIKELY(material_index < 0 || material_index >= materials_num)) { + /* The index might be for a material slot which is not currently setup. */ + continue; + } - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - luv->uv[1] = ((luv->uv[1] - 0.5f) * scale) + 0.5f; - } + float aspect_y = material_aspect_y[material_index]; + if (aspect_y == -1.0f) { + /* Lazily initialize aspect ratio for materials. */ + float aspx, aspy; + ED_uvedit_get_aspect_from_material(ob, material_index, &aspx, &aspy); + aspect_y = aspx / aspy; + material_aspect_y[material_index] = aspect_y; + } + + if (aspect_y == 1.0f) { + /* Scaling by 1.0 has no effect. */ + continue; } + shrink_loop_uv_by_aspect_ratio(efa, cd_loop_uv_offset, aspect_y); } } @@ -1613,7 +1660,17 @@ static void uv_map_clip_correct_properties(wmOperatorType *ot) uv_map_clip_correct_properties_ex(ot, true); } -static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOperator *op) +/** + * \param per_face_aspect: Calculate the aspect ratio per-face, + * otherwise use a single aspect for all UV's based on the material of the active face. + * TODO: using per-face aspect may split UV islands so more advanced UV projection methods + * such as "Unwrap" & "Smart UV Projections" will need to handle aspect correction themselves. + * For now keep using a single aspect for all faces in this case. + */ +static void uv_map_clip_correct_multi(Object **objects, + uint objects_len, + wmOperator *op, + bool per_face_aspect) { BMFace *efa; BMLoop *l; @@ -1633,9 +1690,14 @@ static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOper BMEditMesh *em = BKE_editmesh_from_object(ob); const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - /* correct for image aspect ratio */ + /* Correct for image aspect ratio. */ if (correct_aspect) { - correct_uv_aspect(ob, em); + if (per_face_aspect) { + correct_uv_aspect_per_face(ob, em); + } + else { + correct_uv_aspect(ob, em); + } } if (scale_to_bounds) { @@ -1678,6 +1740,11 @@ static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOper dy = 1.0f / dy; } + if (dx == 1.0f && dy == 1.0f) { + /* Scaling by 1.0 has no effect. */ + return; + } + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { Object *ob = objects[ob_index]; @@ -1702,7 +1769,7 @@ static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOper static void uv_map_clip_correct(Object *ob, wmOperator *op) { - uv_map_clip_correct_multi(&ob, 1, op); + uv_map_clip_correct_multi(&ob, 1, op, true); } /** \} */ @@ -1733,7 +1800,7 @@ static void uvedit_unwrap(const Scene *scene, handle = construct_param_handle(scene, obedit, em->bm, options, result_info); } - GEO_uv_parametrizer_lscm_begin(handle, PARAM_FALSE, scene->toolsettings->unwrapper == 0); + GEO_uv_parametrizer_lscm_begin(handle, false, scene->toolsettings->unwrapper == 0); GEO_uv_parametrizer_lscm_solve(handle, result_info ? &result_info->count_changed : NULL, result_info ? &result_info->count_failed : NULL); @@ -2283,7 +2350,9 @@ static int smart_project_exec(bContext *C, wmOperator *op) .use_seams = true, }); - uv_map_clip_correct_multi(objects_changed, object_changed_len, op); + /* #ED_uvedit_pack_islands_multi only supports `per_face_aspect = false`. */ + const bool per_face_aspect = false; + uv_map_clip_correct_multi(objects_changed, object_changed_len, op, per_face_aspect); } MEM_freeN(objects_changed); @@ -2485,7 +2554,7 @@ static int uv_from_view_exec(bContext *C, wmOperator *op) } if (changed_multi) { - uv_map_clip_correct_multi(objects, objects_len, op); + uv_map_clip_correct_multi(objects, objects_len, op, true); } MEM_freeN(objects); @@ -2968,7 +3037,7 @@ void ED_uvedit_add_simple_uvs(Main *bmain, const Scene *scene, Object *ob) * since we are not in edit mode we need to ensure only the uv flags are tested */ scene->toolsettings->uv_flag &= ~UV_SYNC_SELECTION; - ED_mesh_uv_texture_ensure(me, NULL); + ED_mesh_uv_ensure(me, NULL); BM_mesh_bm_from_me(bm, me, |