diff options
author | YimingWu <xp8110@outlook.com> | 2019-10-24 15:36:28 +0300 |
---|---|---|
committer | YimingWu <xp8110@outlook.com> | 2019-10-24 15:36:28 +0300 |
commit | e910924b37a3fdffeb38ac14c656c16f107a9c0d (patch) | |
tree | ab626478bbb650837fd54140cfbb30a04edb6a26 /source/blender/editors | |
parent | f60406ff257c3309418413b5af76373010fb2a6f (diff) | |
parent | 42eef8f81a6aea27094985347ab802dc6bb91a61 (diff) |
Merge remote-tracking branch 'origin/master' into soc-2019-npr
Diffstat (limited to 'source/blender/editors')
232 files changed, 7038 insertions, 4243 deletions
diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c index d80b96f0d74..8951677b32f 100644 --- a/source/blender/editors/animation/anim_channels_defines.c +++ b/source/blender/editors/animation/anim_channels_defines.c @@ -3411,7 +3411,7 @@ static void acf_nlatrack_color(bAnimContext *UNUSED(ac), bAnimListElem *ale, flo } /* set color for nla track */ - UI_GetThemeColorShade3fv(TH_HEADER, ((nonSolo == false) ? 20 : -20), r_color); + UI_GetThemeColorShade3fv(TH_NLA_TRACK, ((nonSolo == false) ? 20 : -20), r_color); } /* name for nla track entries */ diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c index afd2cdc2a2b..0fc137295d0 100644 --- a/source/blender/editors/animation/anim_channels_edit.c +++ b/source/blender/editors/animation/anim_channels_edit.c @@ -3339,7 +3339,7 @@ static void ANIM_OT_channel_select_keys(wmOperatorType *ot) PropertyRNA *prop; /* identifiers */ - ot->name = "Select Channel keyframes"; + ot->name = "Select Channel Keyframes"; ot->idname = "ANIM_OT_channel_select_keys"; ot->description = "Select all keyframes of channel under mouse"; diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c index 48493c9e961..f73c8a5b71a 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -1818,7 +1818,7 @@ static size_t animdata_filter_gpencil(bAnimContext *ac, !(ads->filterflag & ADS_FILTER_INCL_HIDDEN)) { /* Layer visibility - we check both object and base, * since these may not be in sync yet. */ - if ((base->flag & BASE_VISIBLE) == 0) { + if ((base->flag & BASE_VISIBLE_DEPSGRAPH) == 0) { continue; } @@ -3017,7 +3017,7 @@ static bool animdata_filter_base_is_ok(bDopeSheet *ads, Base *base, int filter_m */ if ((filter_mode & ANIMFILTER_DATA_VISIBLE) && !(ads->filterflag & ADS_FILTER_INCL_HIDDEN)) { /* layer visibility - we check both object and base, since these may not be in sync yet */ - if ((base->flag & BASE_VISIBLE) == 0) { + if ((base->flag & BASE_VISIBLE_DEPSGRAPH) == 0) { return false; } diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c index 8c873eb6b45..36583ecf060 100644 --- a/source/blender/editors/animation/anim_markers.c +++ b/source/blender/editors/animation/anim_markers.c @@ -486,7 +486,7 @@ static void draw_marker( float name_y = UI_DPI_FAC * 18; /* Give an offset to the marker name when selected, * or when near the current frame (5 frames range, starting from the current one). */ - if ((marker->flag & SELECT) || (IN_RANGE_INCL(marker->frame, cfra - 4, cfra))) { + if ((marker->flag & SELECT) || (cfra - 4 <= marker->frame && marker->frame <= cfra)) { name_y += UI_DPI_FAC * 10; } draw_marker_name(fstyle, marker, xpos, name_y); @@ -1148,33 +1148,48 @@ static void deselect_markers(ListBase *markers) } /* select/deselect TimeMarker at current frame */ -static void select_timeline_marker_frame(ListBase *markers, int frame, bool extend) +static int select_timeline_marker_frame(ListBase *markers, + int frame, + bool extend, + bool wait_to_deselect_others) { - TimeMarker *marker, *marker_first = NULL; + TimeMarker *marker, *marker_selected = NULL; + int ret_val = OPERATOR_FINISHED; + + if (extend) { + wait_to_deselect_others = false; + } /* support for selection cycling */ for (marker = markers->first; marker; marker = marker->next) { if (marker->frame == frame) { if (marker->flag & SELECT) { - marker_first = marker->next; + marker_selected = marker->next; break; } } } - /* if extend is not set, then deselect markers */ - if (extend == false) { - deselect_markers(markers); + if (wait_to_deselect_others && marker_selected) { + ret_val = OPERATOR_RUNNING_MODAL; } + /* if extend is not set, then deselect markers */ + else { + if (extend == false) { + deselect_markers(markers); + } - LISTBASE_CIRCULAR_FORWARD_BEGIN (markers, marker, marker_first) { - /* this way a not-extend select will always give 1 selected marker */ - if (marker->frame == frame) { - marker->flag ^= SELECT; - break; + LISTBASE_CIRCULAR_FORWARD_BEGIN (markers, marker, marker_selected) { + /* this way a not-extend select will always give 1 selected marker */ + if (marker->frame == frame) { + marker->flag ^= SELECT; + break; + } } + LISTBASE_CIRCULAR_FORWARD_END(markers, marker, marker_selected); } - LISTBASE_CIRCULAR_FORWARD_END(markers, marker, marker_first); + + return ret_val; } static void select_marker_camera_switch( @@ -1221,17 +1236,17 @@ static void select_marker_camera_switch( #endif } -static int ed_marker_select(bContext *C, const wmEvent *event, bool extend, bool camera) +static int ed_marker_select( + bContext *C, const int mval[2], bool extend, bool camera, bool wait_to_deselect_others) { ListBase *markers = ED_context_get_markers(C); - ARegion *ar = CTX_wm_region(C); View2D *v2d = UI_view2d_fromcontext(C); + int ret_val = OPERATOR_FINISHED; - float mouse_region_x = event->x - ar->winrct.xmin; - if (region_position_is_over_marker(v2d, markers, mouse_region_x)) { - float frame_at_mouse_position = UI_view2d_region_to_view_x(v2d, mouse_region_x); + if (region_position_is_over_marker(v2d, markers, mval[0])) { + float frame_at_mouse_position = UI_view2d_region_to_view_x(v2d, mval[0]); int cfra = ED_markers_find_nearest_marker_time(markers, frame_at_mouse_position); - select_timeline_marker_frame(markers, cfra, extend); + ret_val = select_timeline_marker_frame(markers, cfra, extend, wait_to_deselect_others); select_marker_camera_switch(C, camera, extend, markers, cfra); } @@ -1243,17 +1258,22 @@ static int ed_marker_select(bContext *C, const wmEvent *event, bool extend, bool WM_event_add_notifier(C, NC_ANIMATION | ND_MARKERS, NULL); /* allowing tweaks, but needs OPERATOR_FINISHED, otherwise renaming fails... [#25987] */ - return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH; + return ret_val | OPERATOR_PASS_THROUGH; } -static int ed_marker_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) +static int ed_marker_select_exec(bContext *C, wmOperator *op) { const bool extend = RNA_boolean_get(op->ptr, "extend"); + const bool wait_to_deselect_others = RNA_boolean_get(op->ptr, "wait_to_deselect_others"); bool camera = false; #ifdef DURIAN_CAMERA_SWITCH camera = RNA_boolean_get(op->ptr, "camera"); #endif - return ed_marker_select(C, event, extend, camera); + int mval[2]; + mval[0] = RNA_int_get(op->ptr, "mouse_x"); + mval[1] = RNA_int_get(op->ptr, "mouse_y"); + + return ed_marker_select(C, mval, extend, camera, wait_to_deselect_others); } static void MARKER_OT_select(wmOperatorType *ot) @@ -1266,12 +1286,15 @@ static void MARKER_OT_select(wmOperatorType *ot) ot->idname = "MARKER_OT_select"; /* api callbacks */ - ot->invoke = ed_marker_select_invoke; ot->poll = ed_markers_poll_markers_exist; + ot->exec = ed_marker_select_exec; + ot->invoke = WM_generic_select_invoke; + ot->modal = WM_generic_select_modal; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + WM_operator_properties_generic_select(ot); prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the selection"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); #ifdef DURIAN_CAMERA_SWITCH diff --git a/source/blender/editors/animation/anim_motion_paths.c b/source/blender/editors/animation/anim_motion_paths.c index bd4886817cd..30bf837f6c0 100644 --- a/source/blender/editors/animation/anim_motion_paths.c +++ b/source/blender/editors/animation/anim_motion_paths.c @@ -36,6 +36,7 @@ #include "BKE_scene.h" #include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" #include "DEG_depsgraph_query.h" #include "GPU_batch.h" @@ -67,6 +68,37 @@ typedef struct MPathTarget { /* ........ */ +/* update scene for current frame */ +static void motionpaths_calc_update_scene(Main *bmain, struct Depsgraph *depsgraph) +{ + BKE_scene_graph_update_for_newframe(depsgraph, bmain); +} + +Depsgraph *animviz_depsgraph_build(Main *bmain, + Scene *scene, + ViewLayer *view_layer, + ListBase *targets) +{ + /* Allocate dependency graph. */ + Depsgraph *depsgraph = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_VIEWPORT); + + /* Make a flat array of IDs for the DEG API. */ + const int num_ids = BLI_listbase_count(targets); + ID **ids = MEM_malloc_arrayN(sizeof(ID *), num_ids, "animviz IDS"); + int current_id_index = 0; + for (MPathTarget *mpt = targets->first; mpt != NULL; mpt = mpt->next) { + ids[current_id_index++] = &mpt->ob->id; + } + + /* Build graph from all requested IDs. */ + DEG_graph_build_from_ids(depsgraph, bmain, scene, view_layer, ids, num_ids); + MEM_freeN(ids); + + /* Update once so we can access pointers of evaluated animation data. */ + motionpaths_calc_update_scene(bmain, depsgraph); + return depsgraph; +} + /* get list of motion paths to be baked for the given object * - assumes the given list is ready to be used */ @@ -106,24 +138,6 @@ void animviz_get_object_motionpaths(Object *ob, ListBase *targets) /* ........ */ -/* update scene for current frame */ -static void motionpaths_calc_update_scene(Main *bmain, struct Depsgraph *depsgraph) -{ - /* Do all updates - * - if this is too slow, resort to using a more efficient way - * that doesn't force complete update, but for now, this is the - * most accurate way! - * - * TODO(segey): Bring back partial updates, which became impossible - * with the new depsgraph due to unsorted nature of bases. - * - * TODO(sergey): Use evaluation context dedicated to motion paths. - */ - BKE_scene_graph_update_for_newframe(depsgraph, bmain); -} - -/* ........ */ - /* perform baking for the targets on the current frame */ static void motionpaths_calc_bake_targets(ListBase *targets, int cframe) { @@ -201,6 +215,135 @@ static void motionpaths_calc_bake_targets(ListBase *targets, int cframe) } } +/* Get pointer to animviz settings for the given target. */ +static bAnimVizSettings *animviz_target_settings_get(MPathTarget *mpt) +{ + if (mpt->pchan != NULL) { + return &mpt->ob->pose->avs; + } + return &mpt->ob->avs; +} + +static void motionpath_get_global_framerange(ListBase *targets, int *r_sfra, int *r_efra) +{ + *r_sfra = INT_MAX; + *r_efra = INT_MIN; + for (MPathTarget *mpt = targets->first; mpt; mpt = mpt->next) { + *r_sfra = min_ii(*r_sfra, mpt->mpath->start_frame); + *r_efra = max_ii(*r_efra, mpt->mpath->end_frame); + } +} + +static int motionpath_get_prev_keyframe(MPathTarget *mpt, DLRBT_Tree *fcu_keys, int current_frame) +{ + /* If the current frame is outside of the configured motion path range we ignore update of this + * motion path by using invalid frame range where start frame is above the end frame. */ + if (current_frame <= mpt->mpath->start_frame) { + return INT_MAX; + } + + float current_frame_float = current_frame; + DLRBT_Node *node = BLI_dlrbTree_search_prev(fcu_keys, compare_ak_cfraPtr, ¤t_frame_float); + if (node == NULL) { + return mpt->mpath->start_frame; + } + + ActKeyColumn *key_data = (ActKeyColumn *)node; + return key_data->cfra; +} + +static int motionpath_get_prev_prev_keyframe(MPathTarget *mpt, + DLRBT_Tree *fcu_keys, + int current_frame) +{ + int frame = motionpath_get_prev_keyframe(mpt, fcu_keys, current_frame); + return motionpath_get_prev_keyframe(mpt, fcu_keys, frame); +} + +static int motionpath_get_next_keyframe(MPathTarget *mpt, DLRBT_Tree *fcu_keys, int current_frame) +{ + /* If the current frame is outside of the configured motion path range we ignore update of this + * motion path by using invalid frame range where start frame is above the end frame. */ + if (current_frame >= mpt->mpath->end_frame) { + return INT_MIN; + } + + float current_frame_float = current_frame; + DLRBT_Node *node = BLI_dlrbTree_search_next(fcu_keys, compare_ak_cfraPtr, ¤t_frame_float); + if (node == NULL) { + return mpt->mpath->end_frame; + } + + ActKeyColumn *key_data = (ActKeyColumn *)node; + return key_data->cfra; +} + +static int motionpath_get_next_next_keyframe(MPathTarget *mpt, + DLRBT_Tree *fcu_keys, + int current_frame) +{ + int frame = motionpath_get_next_keyframe(mpt, fcu_keys, current_frame); + return motionpath_get_next_keyframe(mpt, fcu_keys, frame); +} + +static bool motionpath_check_can_use_keyframe_range(MPathTarget *UNUSED(mpt), + AnimData *adt, + ListBase *fcurve_list) +{ + if (adt == NULL || fcurve_list == NULL) { + return false; + } + /* NOTE: We might needed to do a full frame range update if there is a specific setup of NLA + * or drivers or modifiers on the f-curves. */ + return true; +} + +static void motionpath_calculate_update_range(MPathTarget *mpt, + AnimData *adt, + ListBase *fcurve_list, + int current_frame, + int *r_sfra, + int *r_efra) +{ + /* Similar to the case when there is only a single keyframe: need to update en entire range to + * a constant value. */ + if (!motionpath_check_can_use_keyframe_range(mpt, adt, fcurve_list)) { + *r_sfra = mpt->mpath->start_frame; + *r_efra = mpt->mpath->end_frame; + return; + } + + *r_sfra = INT_MAX; + *r_efra = INT_MIN; + + /* NOTE: Iterate over individual f-curves, and check their keyframes individually and pick a + * widest range from them. This is because it's possible to have more narrow keyframe on a + * channel which wasn't edited. + * Could be optimized further by storing some flags about which channels has been modified so + * we ignore all others (which can potentially make an update range unnecessary wide). */ + for (FCurve *fcu = fcurve_list->first; fcu != NULL; fcu = fcu->next) { + DLRBT_Tree fcu_keys; + BLI_dlrbTree_init(&fcu_keys); + fcurve_to_keylist(adt, fcu, &fcu_keys, 0); + + int fcu_sfra = motionpath_get_prev_prev_keyframe(mpt, &fcu_keys, current_frame); + int fcu_efra = motionpath_get_next_next_keyframe(mpt, &fcu_keys, current_frame); + + /* Extend range furher, since accelleration compensation propagates even further away. */ + if (fcu->auto_smoothing != FCURVE_SMOOTH_NONE) { + fcu_sfra = motionpath_get_prev_prev_keyframe(mpt, &fcu_keys, fcu_sfra); + fcu_efra = motionpath_get_next_next_keyframe(mpt, &fcu_keys, fcu_efra); + } + + if (fcu_sfra <= fcu_efra) { + *r_sfra = min_ii(*r_sfra, fcu_sfra); + *r_efra = max_ii(*r_efra, fcu_efra); + } + + BLI_dlrbTree_free(&fcu_keys); + } +} + /* Perform baking of the given object's and/or its bones' transforms to motion paths * - scene: current scene * - ob: object whose flagged motionpaths should get calculated @@ -211,39 +354,36 @@ void animviz_calc_motionpaths(Depsgraph *depsgraph, Main *bmain, Scene *scene, ListBase *targets, - bool restore, - bool current_frame_only) + eAnimvizCalcRange range, + bool restore) { - /* sanity check */ + /* Sanity check. */ if (ELEM(NULL, targets, targets->first)) { return; } - /* Compute frame range to bake within. - * TODO: this method could be improved... - * 1) max range for standard baking - * 2) minimum range for recalc baking (i.e. between keyframes, but how?) */ - int sfra = INT_MAX; - int efra = INT_MIN; - - for (MPathTarget *mpt = targets->first; mpt; mpt = mpt->next) { - /* try to increase area to do (only as much as needed) */ - sfra = MIN2(sfra, mpt->mpath->start_frame); - efra = MAX2(efra, mpt->mpath->end_frame); - } - - if (efra <= sfra) { - return; - } - - /* Limit frame range if we are updating just the current frame. */ - /* set frame values */ - int cfra = CFRA; - if (current_frame_only) { - if (cfra < sfra || cfra > efra) { - return; - } - sfra = efra = cfra; + const int cfra = CFRA; + int sfra = INT_MAX, efra = INT_MIN; + switch (range) { + case ANIMVIZ_CALC_RANGE_CURRENT_FRAME: + motionpath_get_global_framerange(targets, &sfra, &efra); + if (sfra > efra) { + return; + } + if (cfra < sfra || cfra > efra) { + return; + } + sfra = efra = cfra; + break; + case ANIMVIZ_CALC_RANGE_CHANGED: + /* Nothing to do here, will be handled later when iterating through the targets. */ + break; + case ANIMVIZ_CALC_RANGE_FULL: + motionpath_get_global_framerange(targets, &sfra, &efra); + if (sfra > efra) { + return; + } + break; } /* get copies of objects/bones to get the calculated results from @@ -271,16 +411,10 @@ void animviz_calc_motionpaths(Depsgraph *depsgraph, /* build list of all keyframes in active action for object or pchan */ BLI_dlrbTree_init(&mpt->keys); + ListBase *fcurve_list = NULL; if (adt) { - bAnimVizSettings *avs; - /* get pointer to animviz settings for each target */ - if (mpt->pchan) { - avs = &mpt->ob->pose->avs; - } - else { - avs = &mpt->ob->avs; - } + bAnimVizSettings *avs = animviz_target_settings_get(mpt); /* it is assumed that keyframes for bones are all grouped in a single group * unless an option is set to always use the whole action @@ -289,13 +423,28 @@ void animviz_calc_motionpaths(Depsgraph *depsgraph, bActionGroup *agrp = BKE_action_group_find_name(adt->action, mpt->pchan->name); if (agrp) { + fcurve_list = &agrp->channels; agroup_to_keylist(adt, agrp, &mpt->keys, 0); } } else { + fcurve_list = &adt->action->curves; action_to_keylist(adt, adt->action, &mpt->keys, 0); } } + + if (range == ANIMVIZ_CALC_RANGE_CHANGED) { + int mpt_sfra, mpt_efra; + motionpath_calculate_update_range(mpt, adt, fcurve_list, cfra, &mpt_sfra, &mpt_efra); + if (mpt_sfra <= mpt_efra) { + sfra = min_ii(sfra, mpt_sfra); + efra = max_ii(efra, mpt_efra); + } + } + } + + if (sfra > efra) { + return; } /* calculate path over requested range */ @@ -306,7 +455,7 @@ void animviz_calc_motionpaths(Depsgraph *depsgraph, efra, efra - sfra + 1); for (CFRA = sfra; CFRA <= efra; CFRA++) { - if (current_frame_only) { + if (range == ANIMVIZ_CALC_RANGE_CURRENT_FRAME) { /* For current frame, only update tagged. */ BKE_scene_graph_update_tagged(depsgraph, bmain); } @@ -324,7 +473,7 @@ void animviz_calc_motionpaths(Depsgraph *depsgraph, * may be a temporary one that works on a subset of the data. * We always have to restore the current frame though. */ CFRA = cfra; - if (!current_frame_only && restore) { + if (range != ANIMVIZ_CALC_RANGE_CURRENT_FRAME && restore) { motionpaths_calc_update_scene(bmain, depsgraph); } @@ -334,16 +483,10 @@ void animviz_calc_motionpaths(Depsgraph *depsgraph, /* clear recalc flags from targets */ for (MPathTarget *mpt = targets->first; mpt; mpt = mpt->next) { - bAnimVizSettings *avs; bMotionPath *mpath = mpt->mpath; /* get pointer to animviz settings for each target */ - if (mpt->pchan) { - avs = &mpt->ob->pose->avs; - } - else { - avs = &mpt->ob->avs; - } + bAnimVizSettings *avs = animviz_target_settings_get(mpt); /* clear the flag requesting recalculation of targets */ avs->recalc &= ~ANIMVIZ_RECALC_PATHS; diff --git a/source/blender/editors/animation/drivers.c b/source/blender/editors/animation/drivers.c index 7b9e6a10f44..64f7fe034dc 100644 --- a/source/blender/editors/animation/drivers.c +++ b/source/blender/editors/animation/drivers.c @@ -62,13 +62,11 @@ /* Get (or add relevant data to be able to do so) F-Curve from the driver stack, * for the given Animation Data block. This assumes that all the destinations are valid. - * - * - add: 0 - don't add anything if not found, - * 1 - add new Driver FCurve (with keyframes for visual tweaking), - * 2 - add new Driver FCurve (with generator, for script backwards compatibility) - * -1 - add new Driver FCurve without driver stuff (for pasting) */ -FCurve *verify_driver_fcurve(ID *id, const char rna_path[], const int array_index, short add) +FCurve *verify_driver_fcurve(ID *id, + const char rna_path[], + const int array_index, + eDriverFCurveCreationMode creation_mode) { AnimData *adt; FCurve *fcu; @@ -80,7 +78,7 @@ FCurve *verify_driver_fcurve(ID *id, const char rna_path[], const int array_inde /* init animdata if none available yet */ adt = BKE_animdata_from_id(id); - if ((adt == NULL) && (add)) { + if (adt == NULL && creation_mode != DRIVER_FCURVE_LOOKUP_ONLY) { adt = BKE_animdata_add_id(id); } if (adt == NULL) { @@ -94,9 +92,9 @@ FCurve *verify_driver_fcurve(ID *id, const char rna_path[], const int array_inde */ fcu = list_find_fcurve(&adt->drivers, rna_path, array_index); - if ((fcu == NULL) && (add)) { + if (fcu == NULL && creation_mode != DRIVER_FCURVE_LOOKUP_ONLY) { /* use default settings to make a F-Curve */ - fcu = alloc_driver_fcurve(rna_path, array_index, add); + fcu = alloc_driver_fcurve(rna_path, array_index, creation_mode); /* just add F-Curve to end of driver list */ BLI_addtail(&adt->drivers, fcu); @@ -106,12 +104,14 @@ FCurve *verify_driver_fcurve(ID *id, const char rna_path[], const int array_inde return fcu; } -struct FCurve *alloc_driver_fcurve(const char rna_path[], const int array_index, short add) +struct FCurve *alloc_driver_fcurve(const char rna_path[], + const int array_index, + eDriverFCurveCreationMode creation_mode) { FCurve *fcu = MEM_callocN(sizeof(FCurve), "FCurve"); fcu->flag = (FCURVE_VISIBLE | FCURVE_SELECTED); - fcu->auto_smoothing = FCURVE_SMOOTH_CONT_ACCEL; + fcu->auto_smoothing = U.auto_smoothing_new; /* store path - make copy, and store that */ if (rna_path) { @@ -119,18 +119,12 @@ struct FCurve *alloc_driver_fcurve(const char rna_path[], const int array_index, } fcu->array_index = array_index; - /* If add is negative, don't init this data yet, - * since it will be filled in by the pasted driver. */ - if (add > 0) { - BezTriple *bezt; - size_t i; - + if (!ELEM(creation_mode, DRIVER_FCURVE_LOOKUP_ONLY, DRIVER_FCURVE_EMPTY)) { /* add some new driver data */ fcu->driver = MEM_callocN(sizeof(ChannelDriver), "ChannelDriver"); /* F-Modifier or Keyframes? */ - // FIXME: replace these magic numbers with defines - if (add == 2) { + if (creation_mode == DRIVER_FCURVE_GENERATOR) { /* Python API Backwards compatibility hack: * Create FModifier so that old scripts won't break * for now before 2.7 series -- (September 4, 2013) @@ -142,14 +136,10 @@ struct FCurve *alloc_driver_fcurve(const char rna_path[], const int array_index, * - These are configured to 0,0 and 1,1 to give a 1-1 mapping * which can be easily tweaked from there. */ - insert_vert_fcurve(fcu, 0.0f, 0.0f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_FAST); - insert_vert_fcurve(fcu, 1.0f, 1.0f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_FAST); - - /* configure this curve to extrapolate */ - for (i = 0, bezt = fcu->bezt; (i < fcu->totvert) && bezt; i++, bezt++) { - bezt->h1 = bezt->h2 = HD_VECT; - } - + insert_vert_fcurve( + fcu, 0.0f, 0.0f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_FAST | INSERTKEY_NO_USERPREF); + insert_vert_fcurve( + fcu, 1.0f, 1.0f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_FAST | INSERTKEY_NO_USERPREF); fcu->extend = FCURVE_EXTRAPOLATE_LINEAR; calchandles_fcurve(fcu); } @@ -177,7 +167,8 @@ static int add_driver_with_target(ReportList *UNUSED(reports), int driver_type) { FCurve *fcu; - short add_mode = (flag & CREATEDRIVER_WITH_FMODIFIER) ? 2 : 1; + short add_mode = (flag & CREATEDRIVER_WITH_FMODIFIER) ? DRIVER_FCURVE_GENERATOR : + DRIVER_FCURVE_KEYFRAMES; const char *prop_name = RNA_property_identifier(src_prop); /* Create F-Curve with Driver */ @@ -593,7 +584,7 @@ bool ANIM_remove_driver(ReportList *UNUSED(reports), * Note: here is one of the places where we don't want new F-Curve + Driver added! * so 'add' var must be 0 */ - fcu = verify_driver_fcurve(id, rna_path, array_index, 0); + fcu = verify_driver_fcurve(id, rna_path, array_index, DRIVER_FCURVE_LOOKUP_ONLY); if (fcu) { BLI_remlink(&adt->drivers, fcu); free_fcurve(fcu); @@ -653,7 +644,7 @@ bool ANIM_copy_driver( } /* try to get F-Curve with Driver */ - fcu = verify_driver_fcurve(id, rna_path, array_index, 0); + fcu = verify_driver_fcurve(id, rna_path, array_index, DRIVER_FCURVE_LOOKUP_ONLY); /* clear copy/paste buffer first (for consistency with other copy/paste buffers) */ ANIM_drivers_copybuf_free(); @@ -711,7 +702,7 @@ bool ANIM_paste_driver( } /* create Driver F-Curve, but without data which will be copied across... */ - fcu = verify_driver_fcurve(id, rna_path, array_index, -1); + fcu = verify_driver_fcurve(id, rna_path, array_index, DRIVER_FCURVE_EMPTY); if (fcu) { /* copy across the curve data from the buffer curve @@ -855,7 +846,7 @@ void ANIM_copy_as_driver(struct ID *target_id, const char *target_path, const ch ANIM_driver_vars_copybuf_free(); /* Create a dummy driver F-Curve. */ - FCurve *fcu = alloc_driver_fcurve(NULL, 0, 1); + FCurve *fcu = alloc_driver_fcurve(NULL, 0, DRIVER_FCURVE_KEYFRAMES); ChannelDriver *driver = fcu->driver; /* Create a variable. */ diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c index c174ce83bea..479e7192b0e 100644 --- a/source/blender/editors/animation/keyframes_draw.c +++ b/source/blender/editors/animation/keyframes_draw.c @@ -545,8 +545,7 @@ int actkeyblock_get_valid_hold(ActKeyColumn *ac) return 0; } - const int hold_mask = (ACTKEYBLOCK_FLAG_ANY_HOLD | ACTKEYBLOCK_FLAG_STATIC_HOLD | - ACTKEYBLOCK_FLAG_ANY_HOLD); + const int hold_mask = (ACTKEYBLOCK_FLAG_ANY_HOLD | ACTKEYBLOCK_FLAG_STATIC_HOLD); return (ac->block.flag & ~ac->block.conflict) & hold_mask; } diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index 0f8b8742659..8203a9131fa 100644 --- a/source/blender/editors/animation/keyframing.c +++ b/source/blender/editors/animation/keyframing.c @@ -202,7 +202,7 @@ FCurve *verify_fcurve(Main *bmain, fcu = MEM_callocN(sizeof(FCurve), "FCurve"); fcu->flag = (FCURVE_VISIBLE | FCURVE_SELECTED); - fcu->auto_smoothing = FCURVE_SMOOTH_CONT_ACCEL; + fcu->auto_smoothing = U.auto_smoothing_new; if (BLI_listbase_is_empty(&act->curves)) { fcu->flag |= FCURVE_ACTIVE; /* first one added active */ } diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c index eff621d7b71..c37f9ce126b 100644 --- a/source/blender/editors/armature/armature_select.c +++ b/source/blender/editors/armature/armature_select.c @@ -251,12 +251,13 @@ void *get_bone_from_selectbuffer(Base **bases, /* x and y are mouse coords (area space) */ void *get_nearest_bone(bContext *C, const int xy[2], bool findunsel, Base **r_base) { + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewContext vc; rcti rect; unsigned int buffer[MAXPICKBUF]; short hits; - ED_view3d_viewcontext_init(C, &vc); + ED_view3d_viewcontext_init(C, &vc, depsgraph); // rect.xmin = ... mouseco! rect.xmin = rect.xmax = xy[0]; @@ -648,8 +649,9 @@ bool ED_armature_edit_deselect_all_visible_multi_ex(struct Base **bases, uint ba bool ED_armature_edit_deselect_all_visible_multi(bContext *C) { + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewContext vc; - ED_view3d_viewcontext_init(C, &vc); + ED_view3d_viewcontext_init(C, &vc, depsgraph); uint bases_len = 0; Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data( vc.view_layer, vc.v3d, &bases_len); @@ -674,12 +676,13 @@ static int ebone_select_flag(EditBone *ebone) bool ED_armature_edit_select_pick( bContext *C, const int mval[2], bool extend, bool deselect, bool toggle) { + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewContext vc; EditBone *nearBone = NULL; int selmask; Base *basact = NULL; - ED_view3d_viewcontext_init(C, &vc); + ED_view3d_viewcontext_init(C, &vc, depsgraph); vc.mval[0] = mval[0]; vc.mval[1] = mval[1]; diff --git a/source/blender/editors/armature/pose_edit.c b/source/blender/editors/armature/pose_edit.c index 8f4896c0b82..ad115896a43 100644 --- a/source/blender/editors/armature/pose_edit.c +++ b/source/blender/editors/armature/pose_edit.c @@ -182,12 +182,25 @@ static bool pose_has_protected_selected(Object *ob, short warn) /* ********************************************** */ /* Motion Paths */ +static eAnimvizCalcRange pose_path_convert_range(ePosePathCalcRange range) +{ + switch (range) { + case POSE_PATH_CALC_RANGE_CURRENT_FRAME: + return ANIMVIZ_CALC_RANGE_CURRENT_FRAME; + case POSE_PATH_CALC_RANGE_CHANGED: + return ANIMVIZ_CALC_RANGE_CHANGED; + case POSE_PATH_CALC_RANGE_FULL: + return ANIMVIZ_CALC_RANGE_FULL; + } + return ANIMVIZ_CALC_RANGE_FULL; +} + /* For the object with pose/action: update paths for those that have got them * This should selectively update paths that exist... * * To be called from various tools that do incremental updates */ -void ED_pose_recalculate_paths(bContext *C, Scene *scene, Object *ob, bool current_frame_only) +void ED_pose_recalculate_paths(bContext *C, Scene *scene, Object *ob, ePosePathCalcRange range) { /* Transform doesn't always have context available to do update. */ if (C == NULL) { @@ -195,12 +208,12 @@ void ED_pose_recalculate_paths(bContext *C, Scene *scene, Object *ob, bool curre } Main *bmain = CTX_data_main(C); - /* NOTE: Dependency graph will be evaluated at all the frames, but we first need to access some - * nested pointers, like animation data. */ - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - ListBase targets = {NULL, NULL}; + ViewLayer *view_layer = CTX_data_view_layer(C); + + Depsgraph *depsgraph; bool free_depsgraph = false; + ListBase targets = {NULL, NULL}; /* set flag to force recalc, then grab the relevant bones to target */ ob->pose->avs.recalc |= ANIMVIZ_RECALC_PATHS; animviz_get_object_motionpaths(ob, &targets); @@ -210,7 +223,21 @@ void ED_pose_recalculate_paths(bContext *C, Scene *scene, Object *ob, bool curre TIMEIT_START(pose_path_calc); #endif - animviz_calc_motionpaths(depsgraph, bmain, scene, &targets, !free_depsgraph, current_frame_only); + /* For a single frame update it's faster to re-use existing dependency graph and avoid overhead + * of building all the relations and so on for a temporary one. */ + if (range == POSE_PATH_CALC_RANGE_CURRENT_FRAME) { + /* NOTE: Dependency graph will be evaluated at all the frames, but we first need to access some + * nested pointers, like animation data. */ + depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + free_depsgraph = false; + } + else { + depsgraph = animviz_depsgraph_build(bmain, scene, view_layer, &targets); + free_depsgraph = true; + } + + animviz_calc_motionpaths( + depsgraph, bmain, scene, &targets, pose_path_convert_range(range), !free_depsgraph); #ifdef DEBUG_TIME TIMEIT_END(pose_path_calc); @@ -218,13 +245,13 @@ void ED_pose_recalculate_paths(bContext *C, Scene *scene, Object *ob, bool curre BLI_freelistN(&targets); - if (!current_frame_only) { + if (range != POSE_PATH_CALC_RANGE_CURRENT_FRAME) { /* Tag armature object for copy on write - so paths will draw/redraw. * For currently frame only we update evaluated object directly. */ DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); } - /* Free temporary depsgraph instance */ + /* Free temporary depsgraph. */ if (free_depsgraph) { DEG_graph_free(depsgraph); } @@ -293,7 +320,7 @@ static int pose_calculate_paths_exec(bContext *C, wmOperator *op) /* calculate the bones that now have motionpaths... */ /* TODO: only make for the selected bones? */ - ED_pose_recalculate_paths(C, scene, ob, false); + ED_pose_recalculate_paths(C, scene, ob, POSE_PATH_CALC_RANGE_FULL); #ifdef DEBUG_TIME TIMEIT_END(recalc_pose_paths); @@ -371,7 +398,7 @@ static int pose_update_paths_exec(bContext *C, wmOperator *UNUSED(op)) /* calculate the bones that now have motionpaths... */ /* TODO: only make for the selected bones? */ - ED_pose_recalculate_paths(C, scene, ob, false); + ED_pose_recalculate_paths(C, scene, ob, POSE_PATH_CALC_RANGE_FULL); /* notifiers for updates */ WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); diff --git a/source/blender/editors/armature/pose_select.c b/source/blender/editors/armature/pose_select.c index a59067e60c1..c3a7d45f598 100644 --- a/source/blender/editors/armature/pose_select.c +++ b/source/blender/editors/armature/pose_select.c @@ -368,8 +368,9 @@ bool ED_pose_deselect_all_multi_ex(Base **bases, bool ED_pose_deselect_all_multi(bContext *C, int select_mode, const bool ignore_visibility) { + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewContext vc; - ED_view3d_viewcontext_init(C, &vc); + ED_view3d_viewcontext_init(C, &vc, depsgraph); uint bases_len = 0; Base **bases = BKE_view_layer_array_from_bases_in_mode(vc.view_layer, vc.v3d, diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c index 6274eb549da..7ed41b5b4d0 100644 --- a/source/blender/editors/armature/pose_slide.c +++ b/source/blender/editors/armature/pose_slide.c @@ -368,30 +368,14 @@ static void pose_slide_apply_val(tPoseSlideOp *pso, FCurve *fcu, Object *ob, flo switch (pso->mode) { case POSESLIDE_PUSH: /* make the current pose more pronounced */ { - /* perform a weighted average here, favoring the middle pose - * - numerator should be larger than denominator to 'expand' the result - * - perform this weighting a number of times given by the percentage... - */ - /* TODO: maybe a sensitivity ctrl on top of this is needed */ - int iters = (int)ceil(10.0f * pso->percentage); - - while (iters-- > 0) { - (*val) = (-((sVal * w2) + (eVal * w1)) + ((*val) * 6.0f)) / 5.0f; - } + /* Slide the pose away from the breakdown pose in the timeline */ + (*val) -= ((sVal * w2) + (eVal * w1) - (*val)) * pso->percentage; break; } case POSESLIDE_RELAX: /* make the current pose more like its surrounding ones */ { - /* perform a weighted average here, favoring the middle pose - * - numerator should be smaller than denominator to 'relax' the result - * - perform this weighting a number of times given by the percentage... - */ - /* TODO: maybe a sensitivity ctrl on top of this is needed */ - int iters = (int)ceil(10.0f * pso->percentage); - - while (iters-- > 0) { - (*val) = (((sVal * w2) + (eVal * w1)) + ((*val) * 5.0f)) / 6.0f; - } + /* Slide the pose towards the breakdown pose in the timeline */ + (*val) += ((sVal * w2) + (eVal * w1) - (*val)) * pso->percentage; break; } case POSESLIDE_BREAKDOWN: /* make the current pose slide around between the endpoints */ @@ -1003,7 +987,7 @@ static int pose_slide_invoke_common(bContext *C, wmOperator *op, tPoseSlideOp *p pose_slide_refresh(C, pso); /* set cursor to indicate modal */ - WM_cursor_modal_set(win, BC_EW_SCROLLCURSOR); + WM_cursor_modal_set(win, WM_CURSOR_EW_SCROLL); /* header print */ pose_slide_draw_status(pso); diff --git a/source/blender/editors/armature/pose_transform.c b/source/blender/editors/armature/pose_transform.c index 52a3f7711c4..55c9b661074 100644 --- a/source/blender/editors/armature/pose_transform.c +++ b/source/blender/editors/armature/pose_transform.c @@ -543,16 +543,14 @@ static void set_pose_keys(Object *ob) * \param chan: Bone that pose to paste comes from * \param selOnly: Only paste on selected bones * \param flip: Flip on x-axis - * \return Whether the bone that we pasted to if we succeeded + * \return The channel of the bone that was pasted to, or NULL if no paste was performed. */ static bPoseChannel *pose_bone_do_paste(Object *ob, bPoseChannel *chan, const bool selOnly, const bool flip) { - bPoseChannel *pchan; char name[MAXBONENAME]; - short paste_ok; /* get the name - if flipping, we must flip this first */ if (flip) { @@ -567,131 +565,126 @@ static bPoseChannel *pose_bone_do_paste(Object *ob, * 2) if selection-masking is on, channel is selected - * only selected bones get pasted on, allowing making both sides symmetrical. */ - pchan = BKE_pose_channel_find_name(ob->pose, name); - - if (selOnly) { - paste_ok = ((pchan) && (pchan->bone->flag & BONE_SELECTED)); + bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, name); + if (pchan == NULL) { + return NULL; } - else { - paste_ok = (pchan != NULL); + if (selOnly && (pchan->bone->flag & BONE_SELECTED) == 0) { + return NULL; } - /* continue? */ - if (paste_ok) { - /* only loc rot size - * - only copies transform info for the pose - */ - copy_v3_v3(pchan->loc, chan->loc); - copy_v3_v3(pchan->size, chan->size); - pchan->flag = chan->flag; - - /* check if rotation modes are compatible (i.e. do they need any conversions) */ - if (pchan->rotmode == chan->rotmode) { - /* copy the type of rotation in use */ - if (pchan->rotmode > 0) { - copy_v3_v3(pchan->eul, chan->eul); - } - else if (pchan->rotmode == ROT_MODE_AXISANGLE) { - copy_v3_v3(pchan->rotAxis, chan->rotAxis); - pchan->rotAngle = chan->rotAngle; - } - else { - copy_qt_qt(pchan->quat, chan->quat); - } - } - else if (pchan->rotmode > 0) { - /* quat/axis-angle to euler */ - if (chan->rotmode == ROT_MODE_AXISANGLE) { - axis_angle_to_eulO(pchan->eul, pchan->rotmode, chan->rotAxis, chan->rotAngle); - } - else { - quat_to_eulO(pchan->eul, pchan->rotmode, chan->quat); - } + /* only loc rot size + * - only copies transform info for the pose + */ + copy_v3_v3(pchan->loc, chan->loc); + copy_v3_v3(pchan->size, chan->size); + pchan->flag = chan->flag; + + /* check if rotation modes are compatible (i.e. do they need any conversions) */ + if (pchan->rotmode == chan->rotmode) { + /* copy the type of rotation in use */ + if (pchan->rotmode > 0) { + copy_v3_v3(pchan->eul, chan->eul); } else if (pchan->rotmode == ROT_MODE_AXISANGLE) { - /* quat/euler to axis angle */ - if (chan->rotmode > 0) { - eulO_to_axis_angle(pchan->rotAxis, &pchan->rotAngle, chan->eul, chan->rotmode); - } - else { - quat_to_axis_angle(pchan->rotAxis, &pchan->rotAngle, chan->quat); - } + copy_v3_v3(pchan->rotAxis, chan->rotAxis); + pchan->rotAngle = chan->rotAngle; } else { - /* euler/axis-angle to quat */ - if (chan->rotmode > 0) { - eulO_to_quat(pchan->quat, chan->eul, chan->rotmode); - } - else { - axis_angle_to_quat(pchan->quat, chan->rotAxis, pchan->rotAngle); - } + copy_qt_qt(pchan->quat, chan->quat); + } + } + else if (pchan->rotmode > 0) { + /* quat/axis-angle to euler */ + if (chan->rotmode == ROT_MODE_AXISANGLE) { + axis_angle_to_eulO(pchan->eul, pchan->rotmode, chan->rotAxis, chan->rotAngle); + } + else { + quat_to_eulO(pchan->eul, pchan->rotmode, chan->quat); + } + } + else if (pchan->rotmode == ROT_MODE_AXISANGLE) { + /* quat/euler to axis angle */ + if (chan->rotmode > 0) { + eulO_to_axis_angle(pchan->rotAxis, &pchan->rotAngle, chan->eul, chan->rotmode); + } + else { + quat_to_axis_angle(pchan->rotAxis, &pchan->rotAngle, chan->quat); + } + } + else { + /* euler/axis-angle to quat */ + if (chan->rotmode > 0) { + eulO_to_quat(pchan->quat, chan->eul, chan->rotmode); } + else { + axis_angle_to_quat(pchan->quat, chan->rotAxis, pchan->rotAngle); + } + } - /* B-Bone posing options should also be included... */ - pchan->curve_in_x = chan->curve_in_x; - pchan->curve_in_y = chan->curve_in_y; - pchan->curve_out_x = chan->curve_out_x; - pchan->curve_out_y = chan->curve_out_y; - - pchan->roll1 = chan->roll1; - pchan->roll2 = chan->roll2; - pchan->ease1 = chan->ease1; - pchan->ease2 = chan->ease2; - pchan->scale_in_x = chan->scale_in_x; - pchan->scale_in_y = chan->scale_in_y; - pchan->scale_out_x = chan->scale_out_x; - pchan->scale_out_y = chan->scale_out_y; - - /* paste flipped pose? */ - if (flip) { - pchan->loc[0] *= -1; - - pchan->curve_in_x *= -1; - pchan->curve_out_x *= -1; - pchan->roll1 *= -1; // XXX? - pchan->roll2 *= -1; // XXX? - - /* has to be done as eulers... */ - if (pchan->rotmode > 0) { - pchan->eul[1] *= -1; - pchan->eul[2] *= -1; - } - else if (pchan->rotmode == ROT_MODE_AXISANGLE) { - float eul[3]; + /* B-Bone posing options should also be included... */ + pchan->curve_in_x = chan->curve_in_x; + pchan->curve_in_y = chan->curve_in_y; + pchan->curve_out_x = chan->curve_out_x; + pchan->curve_out_y = chan->curve_out_y; + + pchan->roll1 = chan->roll1; + pchan->roll2 = chan->roll2; + pchan->ease1 = chan->ease1; + pchan->ease2 = chan->ease2; + pchan->scale_in_x = chan->scale_in_x; + pchan->scale_in_y = chan->scale_in_y; + pchan->scale_out_x = chan->scale_out_x; + pchan->scale_out_y = chan->scale_out_y; + + /* paste flipped pose? */ + if (flip) { + pchan->loc[0] *= -1; - axis_angle_to_eulO(eul, EULER_ORDER_DEFAULT, pchan->rotAxis, pchan->rotAngle); - eul[1] *= -1; - eul[2] *= -1; - eulO_to_axis_angle(pchan->rotAxis, &pchan->rotAngle, eul, EULER_ORDER_DEFAULT); - } - else { - float eul[3]; + pchan->curve_in_x *= -1; + pchan->curve_out_x *= -1; + pchan->roll1 *= -1; // XXX? + pchan->roll2 *= -1; // XXX? - normalize_qt(pchan->quat); - quat_to_eul(eul, pchan->quat); - eul[1] *= -1; - eul[2] *= -1; - eul_to_quat(pchan->quat, eul); - } + /* has to be done as eulers... */ + if (pchan->rotmode > 0) { + pchan->eul[1] *= -1; + pchan->eul[2] *= -1; } + else if (pchan->rotmode == ROT_MODE_AXISANGLE) { + float eul[3]; - /* ID properties */ - if (chan->prop) { - if (pchan->prop) { - /* if we have existing properties on a bone, just copy over the values of - * matching properties (i.e. ones which will have some impact) on to the - * target instead of just blinding replacing all [ - */ - IDP_SyncGroupValues(pchan->prop, chan->prop); - } - else { - /* no existing properties, so assume that we want copies too? */ - pchan->prop = IDP_CopyProperty(chan->prop); - } + axis_angle_to_eulO(eul, EULER_ORDER_DEFAULT, pchan->rotAxis, pchan->rotAngle); + eul[1] *= -1; + eul[2] *= -1; + eulO_to_axis_angle(pchan->rotAxis, &pchan->rotAngle, eul, EULER_ORDER_DEFAULT); + } + else { + float eul[3]; + + normalize_qt(pchan->quat); + quat_to_eul(eul, pchan->quat); + eul[1] *= -1; + eul[2] *= -1; + eul_to_quat(pchan->quat, eul); + } + } + + /* ID properties */ + if (chan->prop) { + if (pchan->prop) { + /* if we have existing properties on a bone, just copy over the values of + * matching properties (i.e. ones which will have some impact) on to the + * target instead of just blinding replacing all [ + */ + IDP_SyncGroupValues(pchan->prop, chan->prop); + } + else { + /* no existing properties, so assume that we want copies too? */ + pchan->prop = IDP_CopyProperty(chan->prop); } } - /* return whether paste went ahead */ return pchan; } @@ -831,7 +824,7 @@ static int pose_paste_exec(bContext *C, wmOperator *op) /* Recalculate paths if any of the bones have paths... */ if ((ob->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS)) { - ED_pose_recalculate_paths(C, scene, ob, false); + ED_pose_recalculate_paths(C, scene, ob, POSE_PATH_CALC_RANGE_FULL); } /* Notifiers for updates, */ @@ -1112,7 +1105,7 @@ static int pose_clear_transform_generic_exec(bContext *C, /* now recalculate paths */ if ((ob_iter->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS)) { - ED_pose_recalculate_paths(C, scene, ob_iter, false); + ED_pose_recalculate_paths(C, scene, ob_iter, POSE_PATH_CALC_RANGE_FULL); } BLI_freelistN(&dsources); diff --git a/source/blender/editors/armature/pose_utils.c b/source/blender/editors/armature/pose_utils.c index fbaf2c896d0..8d2d7d790d2 100644 --- a/source/blender/editors/armature/pose_utils.c +++ b/source/blender/editors/armature/pose_utils.c @@ -32,6 +32,7 @@ #include "DNA_scene_types.h" #include "BKE_action.h" +#include "BKE_animsys.h" #include "BKE_armature.h" #include "BKE_idprop.h" #include "BKE_layer.h" @@ -223,6 +224,11 @@ void poseAnim_mapping_refresh(bContext *C, Scene *UNUSED(scene), Object *ob) { DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + + AnimData *adt = BKE_animdata_from_id(&ob->id); + if (adt && adt->action) { + DEG_id_tag_update(&adt->action->id, ID_RECALC_ANIMATION_NO_FLUSH); + } } /* reset changes made to current pose */ @@ -327,7 +333,8 @@ void poseAnim_mapping_autoKeyframe(bContext *C, Scene *scene, ListBase *pfLinks, if (ob->id.tag & LIB_TAG_DOIT) { if (ob->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS) { // ED_pose_clear_paths(C, ob); // XXX for now, don't need to clear - ED_pose_recalculate_paths(C, scene, ob, false); + /* TODO(sergey): Should ensure we can use more narrow update range here. */ + ED_pose_recalculate_paths(C, scene, ob, POSE_PATH_CALC_RANGE_FULL); } } } diff --git a/source/blender/editors/curve/curve_ops.c b/source/blender/editors/curve/curve_ops.c index 3d2ac009072..81c9c759188 100644 --- a/source/blender/editors/curve/curve_ops.c +++ b/source/blender/editors/curve/curve_ops.c @@ -33,10 +33,7 @@ #include "WM_types.h" #include "ED_curve.h" -#include "ED_object.h" #include "ED_screen.h" -#include "ED_select_utils.h" -#include "ED_transform.h" #include "curve_intern.h" diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index 7e878d0f905..994d844287c 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -56,13 +56,11 @@ #include "WM_api.h" #include "WM_types.h" -#include "ED_keyframes_edit.h" #include "ED_object.h" #include "ED_screen.h" #include "ED_transform.h" #include "ED_transform_snap_object_context.h" #include "ED_types.h" -#include "ED_util.h" #include "ED_view3d.h" #include "ED_curve.h" @@ -4876,6 +4874,7 @@ void CURVE_OT_make_segment(wmOperatorType *ot) bool ED_curve_editnurb_select_pick( bContext *C, const int mval[2], bool extend, bool deselect, bool toggle) { + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewContext vc; Nurb *nu; BezTriple *bezt = NULL; @@ -4884,7 +4883,7 @@ bool ED_curve_editnurb_select_pick( short hand; view3d_operator_needs_opengl(C); - ED_view3d_viewcontext_init(C, &vc); + ED_view3d_viewcontext_init(C, &vc, depsgraph); copy_v2_v2_int(vc.mval, mval); if (ED_curve_pick_vert(&vc, 1, &nu, &bezt, &bp, &hand, &basact)) { @@ -5635,9 +5634,10 @@ static int add_vertex_exec(bContext *C, wmOperator *op) static int add_vertex_invoke(bContext *C, wmOperator *op, const wmEvent *event) { + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewContext vc; - ED_view3d_viewcontext_init(C, &vc); + ED_view3d_viewcontext_init(C, &vc, depsgraph); if (vc.rv3d && !RNA_struct_property_is_set(op->ptr, "location")) { Curve *cu; @@ -7136,7 +7136,6 @@ static int match_texture_space_exec(bContext *C, wmOperator *UNUSED(op)) copy_v3_v3(curve->loc, loc); copy_v3_v3(curve->size, size); - zero_v3(curve->rot); curve->texflag &= ~CU_AUTOSPACE; diff --git a/source/blender/editors/curve/editcurve_add.c b/source/blender/editors/curve/editcurve_add.c index 4912ae5451d..d97223de9b8 100644 --- a/source/blender/editors/curve/editcurve_add.c +++ b/source/blender/editors/curve/editcurve_add.c @@ -44,7 +44,6 @@ #include "ED_object.h" #include "ED_screen.h" -#include "ED_undo.h" #include "ED_view3d.h" #include "ED_curve.h" diff --git a/source/blender/editors/curve/editcurve_paint.c b/source/blender/editors/curve/editcurve_paint.c index c7c19aa2d02..4c4bac6a249 100644 --- a/source/blender/editors/curve/editcurve_paint.c +++ b/source/blender/editors/curve/editcurve_paint.c @@ -31,7 +31,6 @@ #include "BKE_curve.h" #include "BKE_fcurve.h" #include "BKE_report.h" -#include "BKE_layer.h" #include "DEG_depsgraph.h" @@ -575,9 +574,10 @@ static bool curve_draw_init(bContext *C, wmOperator *op, bool is_invoke) BLI_assert(op->customdata == NULL); struct CurveDrawData *cdd = MEM_callocN(sizeof(*cdd), __func__); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); if (is_invoke) { - ED_view3d_viewcontext_init(C, &cdd->vc); + ED_view3d_viewcontext_init(C, &cdd->vc, depsgraph); if (ELEM(NULL, cdd->vc.ar, cdd->vc.rv3d, cdd->vc.v3d, cdd->vc.win, cdd->vc.scene)) { MEM_freeN(cdd); BKE_report(op->reports, RPT_ERROR, "Unable to access 3D viewport"); @@ -586,7 +586,7 @@ static bool curve_draw_init(bContext *C, wmOperator *op, bool is_invoke) } else { cdd->vc.bmain = CTX_data_main(C); - cdd->vc.depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + cdd->vc.depsgraph = depsgraph; cdd->vc.scene = CTX_data_scene(C); cdd->vc.view_layer = CTX_data_view_layer(C); cdd->vc.obedit = CTX_data_edit_object(C); @@ -1064,7 +1064,7 @@ static int curve_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event) cdd->draw_handle_view = ED_region_draw_cb_activate( cdd->vc.ar->type, curve_draw_stroke_3d, op, REGION_DRAW_POST_VIEW); - WM_cursor_modal_set(cdd->vc.win, BC_PAINTBRUSHCURSOR); + WM_cursor_modal_set(cdd->vc.win, WM_CURSOR_PAINT_BRUSH); { View3D *v3d = cdd->vc.v3d; diff --git a/source/blender/editors/curve/editcurve_select.c b/source/blender/editors/curve/editcurve_select.c index d0abcf32107..9df79a082e1 100644 --- a/source/blender/editors/curve/editcurve_select.c +++ b/source/blender/editors/curve/editcurve_select.c @@ -37,7 +37,6 @@ #include "BKE_fcurve.h" #include "BKE_layer.h" #include "BKE_report.h" -#include "BKE_object.h" #include "WM_api.h" #include "WM_types.h" @@ -278,8 +277,9 @@ bool ED_curve_deselect_all_multi_ex(Base **bases, int bases_len) bool ED_curve_deselect_all_multi(struct bContext *C) { + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewContext vc; - ED_view3d_viewcontext_init(C, &vc); + ED_view3d_viewcontext_init(C, &vc, depsgraph); uint bases_len = 0; Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data( vc.view_layer, vc.v3d, &bases_len); @@ -684,6 +684,7 @@ void CURVE_OT_select_linked(wmOperatorType *ot) static int select_linked_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event) { + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewContext vc; Nurb *nu; BezTriple *bezt; @@ -693,7 +694,7 @@ static int select_linked_pick_invoke(bContext *C, wmOperator *op, const wmEvent Base *basact = NULL; view3d_operator_needs_opengl(C); - ED_view3d_viewcontext_init(C, &vc); + ED_view3d_viewcontext_init(C, &vc, depsgraph); copy_v2_v2_int(vc.mval, event->mval); if (!ED_curve_pick_vert(&vc, 1, &nu, &bezt, &bp, NULL, &basact)) { @@ -1960,6 +1961,7 @@ static void curve_select_shortest_path_surf(Nurb *nu, int vert_src, int vert_dst static int edcu_shortest_path_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event) { + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewContext vc; Nurb *nu_dst; BezTriple *bezt_dst; @@ -1969,7 +1971,7 @@ static int edcu_shortest_path_pick_invoke(bContext *C, wmOperator *op, const wmE Base *basact = NULL; view3d_operator_needs_opengl(C); - ED_view3d_viewcontext_init(C, &vc); + ED_view3d_viewcontext_init(C, &vc, depsgraph); copy_v2_v2_int(vc.mval, event->mval); if (!ED_curve_pick_vert(&vc, 1, &nu_dst, &bezt_dst, &bp_dst, NULL, &basact)) { diff --git a/source/blender/editors/curve/editcurve_undo.c b/source/blender/editors/curve/editcurve_undo.c index 835abd1a630..f21b4f06246 100644 --- a/source/blender/editors/curve/editcurve_undo.c +++ b/source/blender/editors/curve/editcurve_undo.c @@ -40,9 +40,7 @@ #include "DEG_depsgraph.h" -#include "ED_object.h" #include "ED_undo.h" -#include "ED_util.h" #include "ED_curve.h" #include "WM_types.h" diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c index 781eb2634fb..e11807d818f 100644 --- a/source/blender/editors/curve/editfont.c +++ b/source/blender/editors/curve/editfont.c @@ -60,7 +60,6 @@ #include "ED_curve.h" #include "ED_object.h" #include "ED_screen.h" -#include "ED_util.h" #include "ED_view3d.h" #include "UI_interface.h" @@ -1877,6 +1876,7 @@ void ED_curve_editfont_make(Object *obedit) memcpy(ef->textbufinfo, cu->strinfo, ef->len * sizeof(CharInfo)); + ef->pos = cu->pos; if (ef->pos > ef->len) { ef->pos = ef->len; } @@ -1884,7 +1884,6 @@ void ED_curve_editfont_make(Object *obedit) cu->curinfo = ef->textbufinfo[ef->pos ? ef->pos - 1 : 0]; /* Other vars */ - ef->pos = cu->pos; ef->selstart = cu->selstart; ef->selend = cu->selend; @@ -2201,6 +2200,7 @@ void FONT_OT_unlink(wmOperatorType *ot) bool ED_curve_editfont_select_pick( bContext *C, const int mval[2], bool extend, bool deselect, bool toggle) { + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Object *obedit = CTX_data_edit_object(C); Curve *cu = obedit->data; ViewContext vc; @@ -2212,7 +2212,7 @@ bool ED_curve_editfont_select_pick( const float dist = ED_view3d_select_dist_px(); float dist_sq_best = dist * dist; - ED_view3d_viewcontext_init(C, &vc); + ED_view3d_viewcontext_init(C, &vc, depsgraph); ED_view3d_init_mats_rv3d(vc.obedit, vc.rv3d); diff --git a/source/blender/editors/curve/editfont_undo.c b/source/blender/editors/curve/editfont_undo.c index 2f8f15bc6c7..ae858ec4c24 100644 --- a/source/blender/editors/curve/editfont_undo.c +++ b/source/blender/editors/curve/editfont_undo.c @@ -29,7 +29,6 @@ #include "DNA_curve_types.h" #include "DNA_object_types.h" -#include "DNA_scene_types.h" #include "BKE_context.h" #include "BKE_font.h" @@ -39,7 +38,6 @@ #include "ED_object.h" #include "ED_curve.h" -#include "ED_util.h" #include "WM_types.h" #include "WM_api.h" diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt index 765362b374e..8a3a078bdd2 100644 --- a/source/blender/editors/datafiles/CMakeLists.txt +++ b/source/blender/editors/datafiles/CMakeLists.txt @@ -645,6 +645,8 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES brush.sculpt.clay_strips brush.sculpt.crease brush.sculpt.draw + brush.sculpt.draw_sharp + brush.sculpt.elastic_deform brush.sculpt.fill brush.sculpt.flatten brush.sculpt.grab @@ -653,6 +655,7 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES brush.sculpt.mask brush.sculpt.nudge brush.sculpt.pinch + brush.sculpt.pose brush.sculpt.rotate brush.sculpt.scrape brush.sculpt.simplify @@ -736,6 +739,7 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES ops.sculpt.border_hide ops.sculpt.border_mask ops.sculpt.lasso_mask + ops.sculpt.mesh_filter ops.transform.bone_envelope ops.transform.bone_size ops.transform.edge_slide 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 1a132c2957a..ecbc503e084 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c @@ -258,9 +258,9 @@ static int gizmo_button2d_test_select(bContext *C, wmGizmo *gz, const int mval[2 static int gizmo_button2d_cursor_get(wmGizmo *gz) { if (RNA_boolean_get(gz->ptr, "show_drag")) { - return BC_NSEW_SCROLLCURSOR; + return WM_CURSOR_NSEW_SCROLL; } - return CURSOR_STD; + return WM_CURSOR_DEFAULT; } static void gizmo_button2d_free(wmGizmo *gz) diff --git a/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c index ba3b8c2602e..ef4fd23b64d 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c @@ -752,30 +752,30 @@ static int gizmo_cage2d_get_cursor(wmGizmo *gz) int highlight_part = gz->highlight_part; if (gz->parent_gzgroup->type->flag & WM_GIZMOGROUPTYPE_3D) { - return BC_NSEW_SCROLLCURSOR; + return WM_CURSOR_NSEW_SCROLL; } switch (highlight_part) { case ED_GIZMO_CAGE2D_PART_TRANSLATE: - return BC_NSEW_SCROLLCURSOR; + return WM_CURSOR_NSEW_SCROLL; case ED_GIZMO_CAGE2D_PART_SCALE_MIN_X: case ED_GIZMO_CAGE2D_PART_SCALE_MAX_X: - return CURSOR_X_MOVE; + return WM_CURSOR_X_MOVE; case ED_GIZMO_CAGE2D_PART_SCALE_MIN_Y: case ED_GIZMO_CAGE2D_PART_SCALE_MAX_Y: - return CURSOR_Y_MOVE; + return WM_CURSOR_Y_MOVE; /* TODO diagonal cursor */ case ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MIN_Y: case ED_GIZMO_CAGE2D_PART_SCALE_MAX_X_MIN_Y: - return BC_NSEW_SCROLLCURSOR; + return WM_CURSOR_NSEW_SCROLL; case ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MAX_Y: case ED_GIZMO_CAGE2D_PART_SCALE_MAX_X_MAX_Y: - return BC_NSEW_SCROLLCURSOR; + return WM_CURSOR_NSEW_SCROLL; case ED_GIZMO_CAGE2D_PART_ROTATE: - return BC_CROSSCURSOR; + return WM_CURSOR_CROSS; default: - return CURSOR_STD; + return WM_CURSOR_DEFAULT; } } diff --git a/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c index 406f76bc65e..723be3cfe6b 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c @@ -424,10 +424,10 @@ static void gizmo_cage3d_draw(const bContext *C, wmGizmo *gz) static int gizmo_cage3d_get_cursor(wmGizmo *gz) { if (gz->parent_gzgroup->type->flag & WM_GIZMOGROUPTYPE_3D) { - return BC_NSEW_SCROLLCURSOR; + return WM_CURSOR_NSEW_SCROLL; } - return CURSOR_STD; + return WM_CURSOR_DEFAULT; } typedef struct RectTransformInteraction { 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 37ee95d5058..5342f8695b2 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c @@ -412,7 +412,7 @@ static void gizmo_move_property_update(wmGizmo *gz, wmGizmoProperty *gz_prop) static int gizmo_move_cursor_get(wmGizmo *UNUSED(gz)) { - return BC_NSEW_SCROLLCURSOR; + return WM_CURSOR_NSEW_SCROLL; } /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c index 22f1753a810..7a10547f35c 100644 --- a/source/blender/editors/gpencil/annotate_paint.c +++ b/source/blender/editors/gpencil/annotate_paint.c @@ -1560,10 +1560,10 @@ static int gpencil_draw_init(bContext *C, wmOperator *op, const wmEvent *event) static void gpencil_draw_cursor_set(tGPsdata *p) { if (p->paintmode == GP_PAINTMODE_ERASER) { - WM_cursor_modal_set(p->win, BC_CROSSCURSOR); /* XXX need a better cursor */ + WM_cursor_modal_set(p->win, WM_CURSOR_ERASER); } else { - WM_cursor_modal_set(p->win, BC_PAINTBRUSHCURSOR); + WM_cursor_modal_set(p->win, WM_CURSOR_PAINT_BRUSH); } } diff --git a/source/blender/editors/gpencil/gpencil_brush.c b/source/blender/editors/gpencil/gpencil_brush.c index 2a2e7c21df4..af9cadfb938 100644 --- a/source/blender/editors/gpencil/gpencil_brush.c +++ b/source/blender/editors/gpencil/gpencil_brush.c @@ -52,6 +52,7 @@ #include "BKE_context.h" #include "BKE_deform.h" #include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" #include "BKE_material.h" #include "BKE_object_deform.h" #include "BKE_report.h" @@ -109,6 +110,7 @@ typedef struct tGP_BrushEditData { /* Is the brush currently painting? */ bool is_painting; bool is_weight_mode; + bool is_transformed; /* Start of new sculpt stroke */ bool first; @@ -532,7 +534,7 @@ static void gp_brush_grab_calc_dvec(tGP_BrushEditData *gso) // XXX: screen-space strokes in 3D space will suffer! if (gso->sa->spacetype == SPACE_VIEW3D) { RegionView3D *rv3d = gso->ar->regiondata; - float *rvec = gso->scene->cursor.location; + float *rvec = gso->object->loc; float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL); float mval_f[2]; @@ -657,7 +659,7 @@ static void gp_brush_calc_midpoint(tGP_BrushEditData *gso) * See: gpencil_paint.c :: gp_stroke_convertcoords() */ RegionView3D *rv3d = gso->ar->regiondata; - const float *rvec = gso->scene->cursor.location; + const float *rvec = gso->object->loc; float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL); float mval_f[2]; @@ -1326,9 +1328,12 @@ static bool gpsculpt_brush_init(bContext *C, wmOperator *op) if (!BLI_findlink(&ob->defbase, gso->vrgroup)) { gso->vrgroup = -1; } + /* Check if some modifier can transform the stroke. */ + gso->is_transformed = BKE_gpencil_has_transform_modifiers(ob); } else { gso->vrgroup = -1; + gso->is_transformed = false; } gso->sa = CTX_wm_area(C); @@ -1397,7 +1402,7 @@ static bool gpsculpt_brush_init(bContext *C, wmOperator *op) gpsculpt_brush_header_set(C, gso); /* setup cursor drawing */ - // WM_cursor_modal_set(CTX_wm_window(C), BC_CROSSCURSOR); + // WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_CROSS); if (gso->sa->spacetype != SPACE_VIEW3D) { ED_gpencil_toggle_brush_cursor(C, true, NULL); } @@ -1463,7 +1468,7 @@ static bool gpsculpt_brush_poll(bContext *C) /* Init Sculpt Stroke ---------------------------------- */ -static void gpsculpt_brush_init_stroke(tGP_BrushEditData *gso) +static void gpsculpt_brush_init_stroke(bContext *C, tGP_BrushEditData *gso) { bGPdata *gpd = gso->gpd; @@ -1487,9 +1492,11 @@ static void gpsculpt_brush_init_stroke(tGP_BrushEditData *gso) * - This is useful when animating as it saves that "uh-oh" moment when you realize you've * spent too much time editing the wrong frame. */ - // XXX: should this be allowed when framelock is enabled? if (gpf->framenum != cfra) { BKE_gpencil_frame_addcopy(gpl, cfra); + /* Need tag to recalculate evaluated data to avoid crashes. */ + DEG_id_tag_update(&gso->gpd->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); } } } @@ -1504,12 +1511,17 @@ static void gpsculpt_brush_init_stroke(tGP_BrushEditData *gso) * For strokes with one point only this is impossible to calculate because there isn't a * valid reference point. */ -static float gpsculpt_rotation_eval_get(GP_SpaceConversion *gsc, +static float gpsculpt_rotation_eval_get(tGP_BrushEditData *gso, bGPDstroke *gps_eval, bGPDspoint *pt_eval, int idx_eval) { + /* If multiframe or no modifiers, return 0. */ + if ((GPENCIL_MULTIEDIT_SESSIONS_ON(gso->gpd)) || (!gso->is_transformed)) { + return 0.0f; + } + GP_SpaceConversion *gsc = &gso->gsc; bGPDstroke *gps_orig = gps_eval->runtime.gps_orig; bGPDspoint *pt_orig = &gps_orig->points[pt_eval->runtime.idx_orig]; bGPDspoint *pt_prev_eval = NULL; @@ -1564,12 +1576,16 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso, const int radius = (gp_brush->flag & GP_SCULPT_FLAG_PRESSURE_RADIUS) ? gso->gp_brush->size * gso->pressure : gso->gp_brush->size; + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gso->gpd); + bGPDstroke *gps_active = (!is_multiedit) ? gps->runtime.gps_orig : gps; + bGPDspoint *pt_active = NULL; bGPDspoint *pt1, *pt2; bGPDspoint *pt = NULL; int pc1[2] = {0}; int pc2[2] = {0}; int i; + int index; bool include_last = false; bool changed = false; float rot_eval = 0.0f; @@ -1579,6 +1595,7 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso, gp_point_to_parent_space(gps->points, diff_mat, &pt_temp); gp_point_to_xy(gsc, gps, &pt_temp, &pc1[0], &pc1[1]); + pt_active = (!is_multiedit) ? pt->runtime.pt_orig : pt; /* do boundbox check first */ if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) { /* only check if point is inside */ @@ -1586,9 +1603,9 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso, round_v2i_v2fl(mval_i, gso->mval); if (len_v2v2_int(mval_i, pc1) <= radius) { /* apply operation to this point */ - if (pt->runtime.pt_orig != NULL) { - rot_eval = gpsculpt_rotation_eval_get(&gso->gsc, gps, pt, 0); - changed = apply(gso, gps->runtime.gps_orig, rot_eval, pt->runtime.idx_orig, radius, pc1); + if (pt_active != NULL) { + rot_eval = gpsculpt_rotation_eval_get(gso, gps, pt, 0); + changed = apply(gso, gps_active, rot_eval, 0, radius, pc1); } } } @@ -1631,9 +1648,11 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso, /* To each point individually... */ pt = &gps->points[i]; - if (pt->runtime.pt_orig != NULL) { - rot_eval = gpsculpt_rotation_eval_get(&gso->gsc, gps, pt, i); - ok = apply(gso, gps->runtime.gps_orig, rot_eval, pt->runtime.idx_orig, radius, pc1); + pt_active = (!is_multiedit) ? pt->runtime.pt_orig : pt; + index = (!is_multiedit) ? pt->runtime.idx_orig : i; + if (pt_active != NULL) { + rot_eval = gpsculpt_rotation_eval_get(gso, gps, pt, i); + ok = apply(gso, gps_active, rot_eval, index, radius, pc1); } /* Only do the second point if this is the last segment, @@ -1646,9 +1665,11 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso, */ if (i + 1 == gps->totpoints - 1) { pt = &gps->points[i + 1]; - if (pt->runtime.pt_orig != NULL) { - rot_eval = gpsculpt_rotation_eval_get(&gso->gsc, gps, pt, i + 1); - ok |= apply(gso, gps->runtime.gps_orig, rot_eval, pt->runtime.idx_orig, radius, pc2); + pt_active = (!is_multiedit) ? pt->runtime.pt_orig : pt; + index = (!is_multiedit) ? pt->runtime.idx_orig : i + 1; + if (pt_active != NULL) { + rot_eval = gpsculpt_rotation_eval_get(gso, gps, pt, i + 1); + ok |= apply(gso, gps_active, rot_eval, index, radius, pc2); include_last = false; } } @@ -1665,10 +1686,11 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso, * (but wasn't added then, to avoid double-ups). */ pt = &gps->points[i]; - if (pt->runtime.pt_orig != NULL) { - rot_eval = gpsculpt_rotation_eval_get(&gso->gsc, gps, pt, i); - changed |= apply( - gso, gps->runtime.gps_orig, rot_eval, pt->runtime.idx_orig, radius, pc1); + pt_active = (!is_multiedit) ? pt->runtime.pt_orig : pt; + index = (!is_multiedit) ? pt->runtime.idx_orig : i; + if (pt_active != NULL) { + rot_eval = gpsculpt_rotation_eval_get(gso, gps, pt, i); + changed |= apply(gso, gps_active, rot_eval, index, radius, pc1); include_last = false; } } @@ -1688,6 +1710,7 @@ static bool gpsculpt_brush_do_frame(bContext *C, { bool changed = false; Object *ob = CTX_data_active_object(C); + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gso->gpd); for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { /* skip strokes that are invalid for current view */ @@ -1720,18 +1743,19 @@ static bool gpsculpt_brush_do_frame(bContext *C, case GP_SCULPT_TYPE_GRAB: /* Grab points */ { - if (gps->runtime.gps_orig != NULL) { + bGPDstroke *gps_active = (!is_multiedit) ? gps->runtime.gps_orig : gps; + if (gps_active != NULL) { if (gso->first) { /* First time this brush stroke is being applied: * 1) Prepare data buffers (init/clear) for this stroke * 2) Use the points now under the cursor */ - gp_brush_grab_stroke_init(gso, gps->runtime.gps_orig); + gp_brush_grab_stroke_init(gso, gps_active); changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_grab_store_points); } else { /* Apply effect to the stored points */ - gp_brush_grab_apply_cached(gso, gps->runtime.gps_orig, diff_mat); + gp_brush_grab_apply_cached(gso, gps_active, diff_mat); changed |= true; } } @@ -1862,8 +1886,7 @@ static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso) } /* affect strokes in this frame */ - changed |= gpsculpt_brush_do_frame( - C, gso, gpl, (gpf == gpl->actframe) ? gpf_eval : gpf, diff_mat); + changed |= gpsculpt_brush_do_frame(C, gso, gpl, gpf, diff_mat); } } } @@ -2079,7 +2102,7 @@ static int gpsculpt_brush_invoke(bContext *C, wmOperator *op, const wmEvent *eve ARegion *ar = CTX_wm_region(C); /* ensure that we'll have a new frame to draw on */ - gpsculpt_brush_init_stroke(gso); + gpsculpt_brush_init_stroke(C, gso); /* apply first dab... */ gso->is_painting = true; @@ -2194,7 +2217,7 @@ static int gpsculpt_brush_modal(bContext *C, wmOperator *op, const wmEvent *even gso->is_painting = true; gso->first = true; - gpsculpt_brush_init_stroke(gso); + gpsculpt_brush_init_stroke(C, gso); gpsculpt_brush_apply_event(C, op, event); break; diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c index d72f61c64b1..67ffc6adc9f 100644 --- a/source/blender/editors/gpencil/gpencil_data.c +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -492,51 +492,44 @@ static int gp_layer_duplicate_object_exec(bContext *C, wmOperator *op) bGPdata *gpd_src = (bGPdata *)ob_src->data; bGPDlayer *gpl_src = BKE_gpencil_layer_getactive(gpd_src); - /* sanity checks */ + /* Sanity checks. */ if (ELEM(NULL, gpd_src, gpl_src, ob_dst)) { return OPERATOR_CANCELLED; } - /* cannot copy itself and check destination type */ + /* Cannot copy itself and check destination type. */ if ((ob_src == ob_dst) || (ob_dst->type != OB_GPENCIL)) { return OPERATOR_CANCELLED; } bGPdata *gpd_dst = (bGPdata *)ob_dst->data; - /* make copy of layer */ - bGPDlayer *gpl_dst = MEM_dupallocN(gpl_src); - gpl_dst->prev = gpl_dst->next = NULL; - BLI_addtail(&gpd_dst->layers, gpl_dst); - BLI_uniquename(&gpd_dst->layers, - gpl_dst, - DATA_("GP_Layer"), - '.', - offsetof(bGPDlayer, info), - sizeof(gpl_dst->info)); + /* Create new layer. */ + bGPDlayer *gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl_src->info, true); + /* Need to copy some variables (not all). */ + gpl_dst->onion_flag = gpl_src->onion_flag; + gpl_dst->thickness = gpl_src->thickness; + gpl_dst->line_change = gpl_src->line_change; + copy_v4_v4(gpl_dst->tintcolor, gpl_src->tintcolor); + gpl_dst->opacity = gpl_src->opacity; - /* copy frames */ - BLI_listbase_clear(&gpl_dst->frames); + /* Create all frames. */ for (bGPDframe *gpf_src = gpl_src->frames.first; gpf_src; gpf_src = gpf_src->next) { if ((mode == GP_LAYER_COPY_OBJECT_ACT_FRAME) && (gpf_src != gpl_src->actframe)) { continue; } - /* make a copy of source frame */ - bGPDframe *gpf_dst = MEM_dupallocN(gpf_src); - gpf_dst->prev = gpf_dst->next = NULL; - BLI_addtail(&gpl_dst->frames, gpf_dst); + /* Create new frame. */ + bGPDframe *gpf_dst = BKE_gpencil_frame_addnew(gpl_dst, gpf_src->framenum); - /* copy strokes */ - BLI_listbase_clear(&gpf_dst->strokes); + /* Copy strokes. */ for (bGPDstroke *gps_src = gpf_src->strokes.first; gps_src; gps_src = gps_src->next) { - /* make copy of source stroke */ + /* Make copy of source stroke. */ bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps_src); - /* check if material is in destination object, - * otherwise add the slot with the material - */ + /* Check if material is in destination object, + * otherwise add the slot with the material. */ Material *ma_src = give_current_material(ob_src, gps_src->mat_nr + 1); if (ma_src != NULL) { int idx = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma_src); @@ -545,7 +538,7 @@ static int gp_layer_duplicate_object_exec(bContext *C, wmOperator *op) gps_dst->mat_nr = idx; } - /* add new stroke to frame */ + /* Add new stroke to frame. */ BLI_addtail(&gpf_dst->strokes, gps_dst); } } @@ -568,7 +561,7 @@ void GPENCIL_OT_layer_duplicate_object(wmOperatorType *ot) }; /* identifiers */ - ot->name = "Duplicate Layer to new Object"; + ot->name = "Duplicate Layer to New Object"; ot->idname = "GPENCIL_OT_layer_duplicate_object"; ot->description = "Make a copy of the active Grease Pencil layer to new object"; @@ -799,7 +792,7 @@ static int gp_frame_clean_loose_exec(bContext *C, wmOperator *op) void GPENCIL_OT_frame_clean_loose(wmOperatorType *ot) { /* identifiers */ - ot->name = "Clean Loose points"; + ot->name = "Clean Loose Points"; ot->idname = "GPENCIL_OT_frame_clean_loose"; ot->description = "Remove loose points"; @@ -1587,7 +1580,9 @@ void GPENCIL_OT_stroke_lock_color(wmOperatorType *ot) /* ******************* Brush create presets ************************** */ static int gp_brush_presets_create_exec(bContext *C, wmOperator *UNUSED(op)) { - BKE_brush_gpencil_presets(C); + Main *bmain = CTX_data_main(C); + ToolSettings *ts = CTX_data_tool_settings(C); + BKE_brush_gpencil_presets(bmain, ts); /* notifiers */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index a5e9fe9b166..91ecbed7b5b 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -237,11 +237,38 @@ static int gpencil_selectmode_toggle_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); ToolSettings *ts = CTX_data_tool_settings(C); + Object *ob = CTX_data_active_object(C); const int mode = RNA_int_get(op->ptr, "mode"); + bool changed = false; + + if (ts->gpencil_selectmode_edit == mode) { + return OPERATOR_FINISHED; + } /* Just set mode */ ts->gpencil_selectmode_edit = mode; + /* If the mode is Stroke, extend selection. */ + if ((ob) && (ts->gpencil_selectmode_edit == GP_SELECTMODE_STROKE)) { + bGPdata *gpd = (bGPdata *)ob->data; + /* Extend selection to all points in all selected strokes. */ + CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) { + if ((gps->flag & GP_STROKE_SELECT) && (gps->totpoints > 1)) { + changed = true; + bGPDspoint *pt; + for (int i = 0; i < gps->totpoints; i++) { + pt = &gps->points[i]; + pt->flag |= GP_SPOINT_SELECT; + } + } + } + CTX_DATA_END; + if (changed) { + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + } + } + + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, NULL); DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE); @@ -328,7 +355,7 @@ static int gpencil_paintmode_toggle_exec(bContext *C, wmOperator *op) Paint *paint = &ts->gp_paint->paint; /* if not exist, create a new one */ if ((paint->brush == NULL) || (paint->brush->gpencil_settings == NULL)) { - BKE_brush_gpencil_presets(C); + BKE_brush_gpencil_presets(bmain, ts); } BKE_paint_toolslots_brush_validate(bmain, &ts->gp_paint->paint); } diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index ea93f861c6e..993ec15248f 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -1394,7 +1394,7 @@ static int gpencil_fill_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE tgpf->ar->type, gpencil_fill_draw_3d, tgpf, REGION_DRAW_POST_VIEW); } - WM_cursor_modal_set(CTX_wm_window(C), BC_PAINTBRUSHCURSOR); + WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_PAINT_BRUSH); gpencil_fill_status_indicators(C, tgpf); diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index 0bbabafb2b0..7ff6243b0d2 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -214,6 +214,8 @@ typedef struct tGPDprimitive { int sel_cp; /** flag to determine operations in progress */ int flag; + /** flag to determine operations previous mode */ + int prev_flag; /** recorded mouse-position */ float mval[2]; /** previous recorded mouse-position */ @@ -467,6 +469,7 @@ enum { GP_STROKE_CIRCLE = 2, GP_STROKE_ARC = 3, GP_STROKE_CURVE = 4, + GP_STROKE_POLYLINE = 5, }; enum { @@ -672,7 +675,9 @@ struct GP_EditableStrokes_Iter { ED_gpencil_parent_location(depsgraph_, obact_, gpd_, gpl, gpstroke_iter.diff_mat); \ invert_m4_m4(gpstroke_iter.inverse_diff_mat, gpstroke_iter.diff_mat); \ /* get evaluated frame with modifiers applied */ \ - bGPDframe *gpf_eval_ = &obeval_->runtime.gpencil_evaluated_frames[idx_eval]; \ + bGPDframe *gpf_eval_ = (!is_multiedit_) ? \ + &obeval_->runtime.gpencil_evaluated_frames[idx_eval] : \ + gpf_; \ /* loop over strokes */ \ for (bGPDstroke *gps = gpf_eval_->strokes.first; gps; gps = gps->next) { \ /* skip strokes that are invalid for current view */ \ diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c index 86de9a75a56..1438c33a972 100644 --- a/source/blender/editors/gpencil/gpencil_interpolate.c +++ b/source/blender/editors/gpencil/gpencil_interpolate.c @@ -560,7 +560,7 @@ static int gpencil_interpolate_invoke(bContext *C, wmOperator *op, const wmEvent tgpi->ar->type, gpencil_interpolate_draw_3d, tgpi, REGION_DRAW_POST_VIEW); /* set cursor to indicate modal */ - WM_cursor_modal_set(win, BC_EW_SCROLLCURSOR); + WM_cursor_modal_set(win, WM_CURSOR_EW_SCROLL); /* update shift indicator in header */ gpencil_interpolate_status_indicators(C, tgpi); diff --git a/source/blender/editors/gpencil/gpencil_merge.c b/source/blender/editors/gpencil/gpencil_merge.c index 930911ffac5..36cef3ccdc0 100644 --- a/source/blender/editors/gpencil/gpencil_merge.c +++ b/source/blender/editors/gpencil/gpencil_merge.c @@ -35,6 +35,7 @@ #include "BKE_brush.h" #include "BKE_context.h" #include "BKE_gpencil.h" +#include "BKE_main.h" #include "BKE_material.h" #include "WM_api.h" @@ -97,6 +98,7 @@ static void gpencil_insert_points_to_stroke(bGPDstroke *gps, static bGPDstroke *gpencil_prepare_stroke(bContext *C, wmOperator *op, int totpoints) { + Main *bmain = CTX_data_main(C); ToolSettings *ts = CTX_data_tool_settings(C); Object *ob = CTX_data_active_object(C); bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); @@ -111,7 +113,7 @@ static bGPDstroke *gpencil_prepare_stroke(bContext *C, wmOperator *op, int totpo /* if not exist, create a new one */ if ((paint->brush == NULL) || (paint->brush->gpencil_settings == NULL)) { /* create new brushes */ - BKE_brush_gpencil_presets(C); + BKE_brush_gpencil_presets(bmain, ts); } Brush *brush = paint->brush; diff --git a/source/blender/editors/gpencil/gpencil_ops_versioning.c b/source/blender/editors/gpencil/gpencil_ops_versioning.c index af49587f9ad..3d56cb0fcb1 100644 --- a/source/blender/editors/gpencil/gpencil_ops_versioning.c +++ b/source/blender/editors/gpencil/gpencil_ops_versioning.c @@ -54,6 +54,9 @@ #include "ED_object.h" #include "ED_gpencil.h" +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + #include "gpencil_intern.h" /* Free all of a gp-colors */ @@ -111,6 +114,7 @@ static int gpencil_convert_old_files_exec(bContext *C, wmOperator *op) ob = BKE_object_add_for_data( bmain, view_layer, OB_GPENCIL, "GP_Scene", &scene->gpd->id, false); zero_v3(ob->loc); + DEG_relations_tag_update(bmain); /* added object */ /* convert grease pencil palettes (version >= 2.78) to materials and weights */ for (const bGPDpalette *palette = gpd->palettes.first; palette; palette = palette->next) { diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 0a4b068a926..f29e782c618 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -598,6 +598,7 @@ static void gp_smooth_buffer(tGPsdata *p, float inf, int idx) float sco[2] = {0.0f}; float a[2], b[2], c[2], d[2]; float pressure = 0.0f; + float strength = 0.0f; const float average_fac = 1.0f / steps; /* Compute smoothed coordinate by taking the ones nearby */ @@ -605,21 +606,25 @@ static void gp_smooth_buffer(tGPsdata *p, float inf, int idx) copy_v2_v2(a, &pta->x); madd_v2_v2fl(sco, a, average_fac); pressure += pta->pressure * average_fac; + strength += pta->strength * average_fac; } if (ptb) { copy_v2_v2(b, &ptb->x); madd_v2_v2fl(sco, b, average_fac); pressure += ptb->pressure * average_fac; + strength += ptb->strength * average_fac; } if (ptc) { copy_v2_v2(c, &ptc->x); madd_v2_v2fl(sco, c, average_fac); pressure += ptc->pressure * average_fac; + strength += ptc->strength * average_fac; } if (ptd) { copy_v2_v2(d, &ptd->x); madd_v2_v2fl(sco, d, average_fac); pressure += ptd->pressure * average_fac; + strength += ptd->strength * average_fac; } /* Based on influence factor, blend between original and optimal smoothed coordinate. */ @@ -627,10 +632,156 @@ static void gp_smooth_buffer(tGPsdata *p, float inf, int idx) copy_v2_v2(&ptc->x, c); /* Interpolate pressure. */ ptc->pressure = interpf(ptc->pressure, pressure, inf); + /* Interpolate strength. */ + ptc->strength = interpf(ptc->strength, strength, inf); +} + +/* Helper: Apply smooth to segment from Index to Index */ +static void gp_smooth_segment(bGPdata *gpd, const float inf, int from_idx, int to_idx) +{ + const short num_points = to_idx - from_idx; + /* Do nothing if not enough points to smooth out */ + if ((num_points < 3) || (inf == 0.0f)) { + return; + } + + if (from_idx <= 2) { + return; + } + + tGPspoint *points = (tGPspoint *)gpd->runtime.sbuffer; + const float average_fac = 0.25f; + + for (int i = from_idx; i < to_idx + 1; i++) { + + tGPspoint *pta = i >= 3 ? &points[i - 3] : NULL; + tGPspoint *ptb = i >= 2 ? &points[i - 2] : NULL; + tGPspoint *ptc = i >= 1 ? &points[i - 1] : &points[i]; + tGPspoint *ptd = &points[i]; + + float sco[2] = {0.0f}; + + /* Compute smoothed coordinate by taking the ones nearby */ + if (pta) { + madd_v2_v2fl(sco, &pta->x, average_fac); + } + else { + madd_v2_v2fl(sco, &ptc->x, average_fac); + } + + if (ptb) { + madd_v2_v2fl(sco, &ptb->x, average_fac); + } + else { + madd_v2_v2fl(sco, &ptc->x, average_fac); + } + + madd_v2_v2fl(sco, &ptc->x, average_fac); + + madd_v2_v2fl(sco, &ptd->x, average_fac); + + /* Based on influence factor, blend between original and optimal smoothed coordinate. */ + interp_v2_v2v2(&ptc->x, &ptc->x, sco, inf); + } +} + +/* Smooth all the sections created with fake events to avoid abrupt transitions. + * + * As the fake events add points between two real events, this produces a straight line, but if + * there is 3 or more real points that used fakes, the stroke is not smooth and produces abrupt + * angles. + * This function reads these segments and finds the real points and smooth with the surrounding + * points. */ +static void gp_smooth_fake_segments(tGPsdata *p) +{ + bGPdata *gpd = p->gpd; + Brush *brush = p->brush; + + if (brush->gpencil_settings->input_samples < 2) { + return; + } + + tGPspoint *points = (tGPspoint *)gpd->runtime.sbuffer; + tGPspoint *pt = NULL; + /* Index where segment starts. */ + int from_idx = 0; + /* Index where segment ends. */ + int to_idx = 0; + + bool doit = false; + /* Loop all points except the extremes. */ + for (int i = 1; i < gpd->runtime.sbuffer_used - 1; i++) { + pt = &points[i]; + bool is_fake = (bool)(pt->tflag & GP_TPOINT_FAKE); + to_idx = i; + + /* Detect fake points in the stroke. */ + if ((!doit) && (is_fake)) { + from_idx = i; + doit = true; + } + /* If detect control point after fake points, select a segment with same length in both sides, + * except if it is more than stroke length. */ + if ((doit) && (!is_fake)) { + if (i + (i - from_idx) < gpd->runtime.sbuffer_used - 1) { + to_idx = i + (i - from_idx); + /* Smooth this segments (need loop to get cumulative smooth). */ + for (int r = 0; r < 5; r++) { + gp_smooth_segment(gpd, 0.1f, from_idx, to_idx); + } + } + else { + break; + } + /* Reset to new segments. */ + from_idx = i; + doit = false; + } + } +} + +/* Smooth the section added with fake events when pen moves very fast. */ +static void gp_smooth_fake_events(tGPsdata *p, int size_before, int size_after) +{ + bGPdata *gpd = p->gpd; + const short totpoints = size_after - size_before - 1; + /* Do nothing if not enough data to smooth out. */ + if (totpoints < 1) { + return; + } + + /* Back two points to get smoother effect. */ + size_before -= 2; + CLAMP_MIN(size_before, 1); + + tGPspoint *points = (tGPspoint *)gpd->runtime.sbuffer; + /* Extreme points. */ + const tGPspoint *pta = &points[size_before - 1]; + const tGPspoint *ptb = &points[size_after - 1]; + tGPspoint *pt1, *pt2; + int i; + + /* Get total length of the segment to smooth. */ + float totlen = 0.0f; + for (i = size_before; i < size_after; i++) { + pt1 = &points[i - 1]; + pt2 = &points[i]; + totlen += len_v2v2(&pt1->x, &pt2->x); + } + /* Smooth interpolating the position of the points. */ + float pointlen = 0.0f; + for (i = size_before; i < size_after - 1; i++) { + pt1 = &points[i - 1]; + pt2 = &points[i]; + pointlen += len_v2v2(&pt1->x, &pt2->x); + pt2->pressure = interpf(ptb->pressure, pta->pressure, pointlen / totlen); + pt2->strength = interpf(ptb->strength, pta->strength, pointlen / totlen); + } } /* add current stroke-point to buffer (returns whether point was successfully added) */ -static short gp_stroke_addpoint(tGPsdata *p, const float mval[2], float pressure, double curtime) +static short gp_stroke_addpoint( + tGPsdata *p, const float mval[2], float pressure, double curtime, bool is_fake) { bGPdata *gpd = p->gpd; Brush *brush = p->brush; @@ -689,6 +840,14 @@ static short gp_stroke_addpoint(tGPsdata *p, const float mval[2], float pressure /* get pointer to destination point */ pt = ((tGPspoint *)(gpd->runtime.sbuffer) + gpd->runtime.sbuffer_used); + /* Set if point was created by fake events. */ + if (is_fake) { + pt->tflag |= GP_TPOINT_FAKE; + } + else { + pt->tflag &= ~GP_TPOINT_FAKE; + } + /* store settings */ /* pressure */ if (brush->gpencil_settings->flag & GP_BRUSH_USE_PRESSURE) { @@ -846,7 +1005,7 @@ static short gp_stroke_addpoint(tGPsdata *p, const float mval[2], float pressure bGPDspoint *pts; MDeformVert *dvert = NULL; - /* first time point is adding to temporary buffer -- need to allocate new point in stroke */ + /* First time point is adding to temporary buffer (need to allocate new point in stroke) */ if (gpd->runtime.sbuffer_used == 0) { gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1)); if (gps->dvert != NULL) { @@ -937,6 +1096,22 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) (!is_depth); int i, totelem; + /* For very low pressure at the end, truncate stroke. */ + if (p->paintmode == GP_PAINTMODE_DRAW) { + int last_i = gpd->runtime.sbuffer_used - 1; + while (last_i > 0) { + ptc = (tGPspoint *)gpd->runtime.sbuffer + last_i; + if (ptc->pressure > 0.001f) { + break; + } + else { + gpd->runtime.sbuffer_used = last_i - 1; + CLAMP_MIN(gpd->runtime.sbuffer_used, 1); + } + + last_i--; + } + } /* Since strokes are so fine, * when using their depth we need a margin otherwise they might get missed. */ int depth_margin = (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 4 : 0; @@ -1187,6 +1362,9 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) } } + /* Smooth any point created with fake events when the mouse/pen move very fast. */ + gp_smooth_fake_segments(p); + pt = gps->points; dvert = gps->dvert; @@ -1834,6 +2012,7 @@ static void gp_set_default_eraser(Main *bmain, Brush *brush_dft) /* initialize a drawing brush */ static void gp_init_drawing_brush(bContext *C, tGPsdata *p) { + Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); ToolSettings *ts = CTX_data_tool_settings(C); @@ -1842,7 +2021,7 @@ static void gp_init_drawing_brush(bContext *C, tGPsdata *p) /* if not exist, create a new one */ if ((paint->brush == NULL) || (paint->brush->gpencil_settings == NULL)) { /* create new brushes */ - BKE_brush_gpencil_presets(C); + BKE_brush_gpencil_presets(bmain, ts); changed = true; } /* be sure curves are initializated */ @@ -2489,10 +2668,10 @@ static void gpencil_draw_cursor_set(tGPsdata *p) #if 0 Brush *brush = p->brush; if ((p->paintmode == GP_PAINTMODE_ERASER) || (brush->gpencil_tool == GPAINT_TOOL_ERASE)) { - WM_cursor_modal_set(p->win, BC_CROSSCURSOR); /* XXX need a better cursor */ + WM_cursor_modal_set(p->win, WM_CURSOR_CROSS); /* XXX need a better cursor */ } else { - WM_cursor_modal_set(p->win, CURSOR_NONE); + WM_cursor_modal_set(p->win, WM_CURSOR_NONE); } #endif } @@ -2569,7 +2748,8 @@ static void gpencil_draw_status_indicators(bContext *C, tGPsdata *p) /* ------------------------------- */ /* create a new stroke point at the point indicated by the painting context */ -static void gpencil_draw_apply(bContext *C, wmOperator *op, tGPsdata *p, Depsgraph *depsgraph) +static void gpencil_draw_apply( + bContext *C, wmOperator *op, tGPsdata *p, Depsgraph *depsgraph, bool is_fake) { bGPdata *gpd = p->gpd; tGPspoint *pt = NULL; @@ -2598,7 +2778,7 @@ static void gpencil_draw_apply(bContext *C, wmOperator *op, tGPsdata *p, Depsgra } /* try to add point */ - short ok = gp_stroke_addpoint(p, p->mval, p->pressure, p->curtime); + short ok = gp_stroke_addpoint(p, p->mval, p->pressure, p->curtime, is_fake); /* handle errors while adding point */ if ((ok == GP_STROKEADD_FULL) || (ok == GP_STROKEADD_OVERFLOW)) { @@ -2612,12 +2792,12 @@ static void gpencil_draw_apply(bContext *C, wmOperator *op, tGPsdata *p, Depsgra /* XXX We only need to reuse previous point if overflow! */ if (ok == GP_STROKEADD_OVERFLOW) { p->inittime = p->ocurtime; - gp_stroke_addpoint(p, p->mvalo, p->opressure, p->ocurtime); + gp_stroke_addpoint(p, p->mvalo, p->opressure, p->ocurtime, is_fake); } else { p->inittime = p->curtime; } - gp_stroke_addpoint(p, p->mval, p->pressure, p->curtime); + gp_stroke_addpoint(p, p->mval, p->pressure, p->curtime, is_fake); } else if (ok == GP_STROKEADD_INVALID) { /* the painting operation cannot continue... */ @@ -2823,8 +3003,13 @@ static void gpencil_speed_guide(tGPsdata *p, GP_Sculpt_Guide *guide) } /* handle draw event */ -static void gpencil_draw_apply_event( - bContext *C, wmOperator *op, const wmEvent *event, Depsgraph *depsgraph, float x, float y) +static void gpencil_draw_apply_event(bContext *C, + wmOperator *op, + const wmEvent *event, + Depsgraph *depsgraph, + float x, + float y, + const bool is_fake) { tGPsdata *p = op->customdata; GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide; @@ -2977,10 +3162,10 @@ static void gpencil_draw_apply_event( /* create fake events */ float tmp[2]; copy_v2_v2(tmp, p->mval); - gpencil_draw_apply_event(C, op, event, depsgraph, pt[0], pt[1]); + gpencil_draw_apply_event(C, op, event, depsgraph, pt[0], pt[1], false); if (len_v2v2(p->mval, p->mvalo)) { sub_v2_v2v2(pt, p->mval, p->mvalo); - gpencil_draw_apply_event(C, op, event, depsgraph, pt[0], pt[1]); + gpencil_draw_apply_event(C, op, event, depsgraph, pt[0], pt[1], false); } copy_v2_v2(p->mval, tmp); } @@ -3011,7 +3196,7 @@ static void gpencil_draw_apply_event( RNA_float_set(&itemptr, "time", p->curtime - p->inittime); /* apply the current latest drawing point */ - gpencil_draw_apply(C, op, p, depsgraph); + gpencil_draw_apply(C, op, p, depsgraph, is_fake); /* force refresh */ /* just active area for now, since doing whole screen is too slow */ @@ -3077,7 +3262,7 @@ static int gpencil_draw_exec(bContext *C, wmOperator *op) } /* apply this data as necessary now (as per usual) */ - gpencil_draw_apply(C, op, p, depsgraph); + gpencil_draw_apply(C, op, p, depsgraph, false); } RNA_END; @@ -3211,7 +3396,7 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event if (paintmode != GP_PAINTMODE_ERASER) { bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); if ((gpl) && ((gpl->flag & GP_LAYER_LOCKED) || (gpl->flag & GP_LAYER_HIDE))) { - BKE_report(op->reports, RPT_ERROR, "Active layer is locked or hide"); + BKE_report(op->reports, RPT_ERROR, "Active layer is locked or hidden"); return OPERATOR_CANCELLED; } } @@ -3250,8 +3435,6 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event /* TODO: set any additional settings that we can take from the events? * TODO? if tablet is erasing, force eraser to be on? */ - /* TODO: move cursor setting stuff to stroke-start so that paintmode can be changed midway... */ - /* if eraser is on, draw radial aid */ if (p->paintmode == GP_PAINTMODE_ERASER) { gpencil_draw_toggle_eraser_cursor(C, p, true); @@ -3272,7 +3455,8 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event p->status = GP_STATUS_PAINTING; /* handle the initial drawing - i.e. for just doing a simple dot */ - gpencil_draw_apply_event(C, op, event, CTX_data_ensure_evaluated_depsgraph(C), 0.0f, 0.0f); + gpencil_draw_apply_event( + C, op, event, CTX_data_ensure_evaluated_depsgraph(C), 0.0f, 0.0f, false); op->flag |= OP_IS_MODAL_CURSOR_REGION; } else { @@ -3389,24 +3573,21 @@ static void gpencil_move_last_stroke_to_back(bContext *C) BLI_insertlinkbefore(&gpf->strokes, gpf->strokes.first, gps); } -/* add events for missing mouse movements when the artist draw very fast */ -static void gpencil_add_missing_events(bContext *C, - wmOperator *op, - const wmEvent *event, - tGPsdata *p) +/* Add fake events for missing mouse movements when the artist draw very fast */ +static bool gpencil_add_fake_events(bContext *C, wmOperator *op, const wmEvent *event, tGPsdata *p) { Brush *brush = p->brush; GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide; Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); int input_samples = brush->gpencil_settings->input_samples; - + bool added_events = false; /* ensure sampling when using circular guide */ if (guide->use_guide && (guide->type == GP_GUIDE_CIRCULAR)) { input_samples = GP_MAX_INPUT_SAMPLES; } if (input_samples == 0) { - return; + return added_events; } RegionView3D *rv3d = p->ar->regiondata; @@ -3456,7 +3637,8 @@ static void gpencil_add_missing_events(bContext *C, interp_v2_v2v2(pt, a, b, 0.5f); sub_v2_v2v2(pt, b, pt); /* create fake event */ - gpencil_draw_apply_event(C, op, event, depsgraph, pt[0], pt[1]); + gpencil_draw_apply_event(C, op, event, depsgraph, pt[0], pt[1], true); + added_events = true; } else if (dist >= factor) { int slices = 2 + (int)((dist - 1.0) / factor); @@ -3465,9 +3647,11 @@ static void gpencil_add_missing_events(bContext *C, interp_v2_v2v2(pt, a, b, n * i); sub_v2_v2v2(pt, b, pt); /* create fake event */ - gpencil_draw_apply_event(C, op, event, depsgraph, pt[0], pt[1]); + gpencil_draw_apply_event(C, op, event, depsgraph, pt[0], pt[1], true); + added_events = true; } } + return added_events; } /* events handling during interactive drawing part of operator */ @@ -3476,6 +3660,8 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) tGPsdata *p = op->customdata; ToolSettings *ts = CTX_data_tool_settings(C); GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide; + tGPspoint *points = (tGPspoint *)p->gpd->runtime.sbuffer; + /* default exit state - pass through to support MMB view nav, etc. */ int estate = OPERATOR_PASS_THROUGH; @@ -3500,7 +3686,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) /* special mode for editing control points */ if (p->paintmode == GP_PAINTMODE_SET_CP) { wmWindow *win = p->win; - WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR); + WM_cursor_modal_set(win, WM_CURSOR_NSEW_SCROLL); bool drawmode = false; switch (event->type) { @@ -3598,7 +3784,8 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) } /* toggle painting mode upon mouse-button movement - * - LEFTMOUSE = standard drawing (all) / straight line drawing (all) / polyline (toolbox only) + * - LEFTMOUSE = standard drawing (all) / straight line drawing (all) / polyline (toolbox + * only) * - RIGHTMOUSE = polyline (hotkey) / eraser (all) * (Disabling RIGHTMOUSE case here results in bugs like [#32647]) * also making sure we have a valid event value, to not exit too early @@ -3772,11 +3959,23 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) /* handle drawing event */ /* printf("\t\tGP - add point\n"); */ + int size_before = p->gpd->runtime.sbuffer_used; + bool added_events = false; if (((p->flags & GP_PAINTFLAG_FIRSTRUN) == 0) && (p->paintmode != GP_PAINTMODE_ERASER)) { - gpencil_add_missing_events(C, op, event, p); + added_events = gpencil_add_fake_events(C, op, event, p); } - gpencil_draw_apply_event(C, op, event, CTX_data_depsgraph_pointer(C), 0.0f, 0.0f); + gpencil_draw_apply_event(C, op, event, CTX_data_depsgraph_pointer(C), 0.0f, 0.0f, false); + int size_after = p->gpd->runtime.sbuffer_used; + + /* Last point of the event is always real (not fake). */ + tGPspoint *pt = &points[size_after - 1]; + pt->tflag &= ~GP_TPOINT_FAKE; + + /* Smooth the fake events to get smoother strokes, specially at ends. */ + if (added_events) { + gp_smooth_fake_events(p, size_before, size_after); + } /* finish painting operation if anything went wrong just now */ if (p->status == GP_STATUS_ERROR) { diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c index 8d4c75d2e8c..bf7b2edb025 100644 --- a/source/blender/editors/gpencil/gpencil_primitive.c +++ b/source/blender/editors/gpencil/gpencil_primitive.c @@ -90,6 +90,7 @@ #define IN_MOVE 3 #define IN_BRUSH_SIZE 4 #define IN_BRUSH_STRENGTH 5 +#define IN_POLYLINE 6 #define SELECT_NONE 0 #define SELECT_START 1 @@ -184,6 +185,29 @@ static void gpencil_primitive_to_square(tGPDprimitive *tgpi, const float x, cons } } +/* Helper to constrain a primitive */ +static void gpencil_primitive_constrain(tGPDprimitive *tgpi, bool line_mode) +{ + float x = tgpi->end[0] - tgpi->origin[0]; + float y = tgpi->end[1] - tgpi->origin[1]; + + if (line_mode) { + float angle = fabsf(atan2f(y, x)); + if (angle < 0.4f || angle > (M_PI - 0.4f)) { + tgpi->end[1] = tgpi->origin[1]; + } + else if (angle > (M_PI_2 - 0.4f) && angle < (M_PI_2 + 0.4f)) { + tgpi->end[0] = tgpi->origin[0]; + } + else { + gpencil_primitive_to_square(tgpi, x, y); + } + } + else { + gpencil_primitive_to_square(tgpi, x, y); + } + } + /* Helper to rotate point around origin */ static void gp_rotate_v2_v2v2fl(float v[2], const float p[2], @@ -404,6 +428,11 @@ static void gpencil_primitive_status_indicators(bContext *C, tGPDprimitive *tgpi "adjust subdivision number, Shift to align, Alt to center, E: extrude"), UI_MAX_DRAW_STR); } + else if (tgpi->type == GP_STROKE_POLYLINE) { + BLI_strncpy(msg_str, + TIP_("Line: ESC to cancel, LMB to set, Enter/MMB to confirm, Shift to align"), + UI_MAX_DRAW_STR); + } else if (tgpi->type == GP_STROKE_BOX) { BLI_strncpy(msg_str, TIP_("Rectangle: ESC to cancel, LMB set origin, Enter/MMB to confirm, WHEEL/+- " @@ -429,7 +458,7 @@ static void gpencil_primitive_status_indicators(bContext *C, tGPDprimitive *tgpi UI_MAX_DRAW_STR); } - if (ELEM(tgpi->type, GP_STROKE_CIRCLE, GP_STROKE_ARC, GP_STROKE_LINE, GP_STROKE_BOX)) { + if (ELEM(tgpi->type, GP_STROKE_CIRCLE, GP_STROKE_ARC, GP_STROKE_LINE, GP_STROKE_BOX, GP_STROKE_POLYLINE)) { if (hasNumInput(&tgpi->num)) { char str_offs[NUM_STR_REP_LEN]; @@ -528,7 +557,7 @@ static void gp_primitive_rectangle(tGPDprimitive *tgpi, tGPspoint *points2D) } /* create a line */ -static void gp_primitive_line(tGPDprimitive *tgpi, tGPspoint *points2D) +static void gp_primitive_line(tGPDprimitive *tgpi, tGPspoint *points2D, bool editable) { const int totpoints = (tgpi->tot_edges + tgpi->tot_stored_edges); const float step = 1.0f / (float)(tgpi->tot_edges - 1); @@ -540,15 +569,22 @@ static void gp_primitive_line(tGPDprimitive *tgpi, tGPspoint *points2D) a += step; } - float color[4]; - UI_GetThemeColor4fv(TH_GIZMO_PRIMARY, color); - gp_primitive_set_cp(tgpi, tgpi->end, color, BIG_SIZE_CTL); - if (tgpi->tot_stored_edges) { - UI_GetThemeColor4fv(TH_REDALERT, color); - gp_primitive_set_cp(tgpi, tgpi->start, color, SMALL_SIZE_CTL); + if (editable) { + float color[4]; + UI_GetThemeColor4fv(TH_GIZMO_PRIMARY, color); + gp_primitive_set_cp(tgpi, tgpi->end, color, BIG_SIZE_CTL); + if (tgpi->tot_stored_edges) { + UI_GetThemeColor4fv(TH_REDALERT, color); + gp_primitive_set_cp(tgpi, tgpi->start, color, SMALL_SIZE_CTL); + } + else { + gp_primitive_set_cp(tgpi, tgpi->start, color, BIG_SIZE_CTL); + } } else { - gp_primitive_set_cp(tgpi, tgpi->start, color, BIG_SIZE_CTL); + float color[4]; + UI_GetThemeColor4fv(TH_REDALERT, color); + gp_primitive_set_cp(tgpi, tgpi->start, color, SMALL_SIZE_CTL); } } @@ -693,7 +729,10 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi) gp_primitive_rectangle(tgpi, points2D); break; case GP_STROKE_LINE: - gp_primitive_line(tgpi, points2D); + gp_primitive_line(tgpi, points2D, true); + break; + case GP_STROKE_POLYLINE: + gp_primitive_line(tgpi, points2D, false); break; case GP_STROKE_CIRCLE: gp_primitive_circle(tgpi, points2D); @@ -1041,6 +1080,7 @@ static void gpencil_primitive_update(bContext *C, wmOperator *op, tGPDprimitive gp_primitive_update_strokes(C, tgpi); } +/* Initialise mouse points */ static void gpencil_primitive_interaction_begin(tGPDprimitive *tgpi, const wmEvent *event) { copy_v2fl_v2i(tgpi->mval, event->mval); @@ -1134,7 +1174,7 @@ static void gpencil_primitive_init(bContext *C, wmOperator *op) /* if brush doesn't exist, create a new set (fix damaged files from old versions) */ if ((paint->brush == NULL) || (paint->brush->gpencil_settings == NULL)) { - BKE_brush_gpencil_presets(C); + BKE_brush_gpencil_presets(bmain, ts); } /* Set Draw brush. */ @@ -1164,6 +1204,10 @@ static void gpencil_primitive_init(bContext *C, wmOperator *op) /* set default edge count */ switch (tgpi->type) { + case GP_STROKE_POLYLINE: { + RNA_int_set(op->ptr, "edges", 8); + break; + } case GP_STROKE_LINE: { RNA_int_set(op->ptr, "edges", 8); break; @@ -1214,7 +1258,7 @@ static int gpencil_primitive_invoke(bContext *C, wmOperator *op, const wmEvent * op->flag |= OP_IS_MODAL_CURSOR_REGION; /* set cursor to indicate modal */ - WM_cursor_modal_set(win, BC_CROSSCURSOR); + WM_cursor_modal_set(win, WM_CURSOR_CROSS); /* update sindicator in header */ gpencil_primitive_status_indicators(C, tgpi); @@ -1319,21 +1363,57 @@ static void gpencil_primitive_edit_event_handling( if (tgpi->flag == IN_CURVE_EDIT) { if ((a < BIG_SIZE_CTL && tgpi->tot_stored_edges == 0) || b < BIG_SIZE_CTL) { move = MOVE_ENDS; - WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR); + WM_cursor_modal_set(win, WM_CURSOR_NSEW_SCROLL); } else if (tgpi->curve) { move = MOVE_CP; - WM_cursor_modal_set(win, BC_HANDCURSOR); + WM_cursor_modal_set(win, WM_CURSOR_HAND); } else { - WM_cursor_modal_set(win, BC_CROSSCURSOR); + WM_cursor_modal_set(win, WM_CURSOR_CROSS); } } else if (tgpi->flag == IN_PROGRESS) { - WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR); + WM_cursor_modal_set(win, WM_CURSOR_NSEW_SCROLL); } switch (event->type) { + case LEFTMOUSE: { + if ((event->val == KM_RELEASE) && (tgpi->flag == IN_PROGRESS)) { + /* set control points and enter edit mode */ + if ((ELEM(tgpi->type, GP_STROKE_POLYLINE))) { + gpencil_primitive_add_segment(tgpi); + copy_v2_v2(tgpi->start, tgpi->end); + copy_v2_v2(tgpi->origin, tgpi->start); + gp_primitive_update_cps(tgpi); + + tgpi->flag = IN_POLYLINE; + WM_cursor_modal_set(win, WM_CURSOR_CROSS); + } + else { + tgpi->flag = IN_CURVE_EDIT; + gp_primitive_update_cps(tgpi); + gpencil_primitive_update(C, op, tgpi); + } + } + else if ((event->val == KM_PRESS) && !ELEM(tgpi->type, GP_STROKE_POLYLINE)) { + /* find nearest cp based on stroke end points */ + if (move == MOVE_ENDS) { + tgpi->sel_cp = (a < b) ? SELECT_START : SELECT_END; + } + else if (move == MOVE_CP) { + tgpi->sel_cp = (c < d) ? SELECT_CP1 : SELECT_CP2; + } + else { + tgpi->sel_cp = SELECT_NONE; + } + break; + } + else { + tgpi->sel_cp = SELECT_NONE; + } + break; + } case MOUSEMOVE: { if ((event->val == KM_PRESS) && tgpi->sel_cp != SELECT_NONE) { if (tgpi->sel_cp == SELECT_START && tgpi->tot_stored_edges == 0) { @@ -1366,31 +1446,6 @@ static void gpencil_primitive_edit_event_handling( } break; } - case LEFTMOUSE: { - if ((event->val == KM_PRESS)) { - /* find nearest cp based on stroke end points */ - if (move == MOVE_ENDS) { - tgpi->sel_cp = (a < b) ? SELECT_START : SELECT_END; - } - else if (move == MOVE_CP) { - tgpi->sel_cp = (c < d) ? SELECT_CP1 : SELECT_CP2; - } - else { - tgpi->sel_cp = SELECT_NONE; - } - break; - } - else if ((event->val == KM_RELEASE) && (tgpi->flag == IN_PROGRESS)) { - /* set control points and enter edit mode */ - tgpi->flag = IN_CURVE_EDIT; - gp_primitive_update_cps(tgpi); - gpencil_primitive_update(C, op, tgpi); - } - else { - tgpi->sel_cp = SELECT_NONE; - } - break; - } case MKEY: { if ((event->val == KM_PRESS) && (tgpi->curve) && (ELEM(tgpi->orign_type, GP_STROKE_ARC))) { tgpi->flip ^= 1; @@ -1402,7 +1457,7 @@ static void gpencil_primitive_edit_event_handling( case EKEY: { if (tgpi->flag == IN_CURVE_EDIT && !ELEM(tgpi->type, GP_STROKE_BOX, GP_STROKE_CIRCLE)) { tgpi->flag = IN_PROGRESS; - WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR); + WM_cursor_modal_set(win, WM_CURSOR_NSEW_SCROLL); gpencil_primitive_add_segment(tgpi); copy_v2_v2(tgpi->start, tgpi->end); copy_v2_v2(tgpi->origin, tgpi->start); @@ -1524,6 +1579,96 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e copy_v2_v2(tgpi->mvalo, tgpi->mval); return OPERATOR_RUNNING_MODAL; } + else if (tgpi->flag == IN_POLYLINE) { + + switch (event->type) { + + case ESCKEY: { + /* return to normal cursor and header status */ + ED_workspace_status_text(C, NULL); + WM_cursor_modal_restore(win); + + /* clean up temp data */ + gpencil_primitive_exit(C, op); + + /* canceled! */ + return OPERATOR_CANCELLED; + } + case LEFTMOUSE: { + if (event->val == KM_PRESS) { + WM_cursor_modal_set(win, WM_CURSOR_CROSS); + gpencil_primitive_add_segment(tgpi); + + gpencil_primitive_update(C, op, tgpi); + copy_v2_v2(tgpi->start, tgpi->end); + copy_v2_v2(tgpi->origin, tgpi->end); + } + break; + } + case SPACEKEY: /* confirm */ + case MIDDLEMOUSE: + case RETKEY: + case RIGHTMOUSE: { + if (event->val == KM_PRESS) { + tgpi->flag = IDLE; + tgpi->tot_edges = tgpi->tot_stored_edges ? 1 : 0; + gp_primitive_update_strokes(C, tgpi); + gpencil_primitive_interaction_end(C, op, win, tgpi); + return OPERATOR_FINISHED; + } + break; + } + case MOUSEMOVE: { + WM_cursor_modal_set(win, WM_CURSOR_NSEW_SCROLL); + copy_v2_v2(tgpi->end, tgpi->mval); + + if (event->shift) { + gpencil_primitive_constrain(tgpi, true); + } + + gpencil_primitive_update(C, op, tgpi); + break; + } + case PADPLUSKEY: + case WHEELUPMOUSE: { + if ((event->val != KM_RELEASE)) { + tgpi->tot_edges = tgpi->tot_edges + 1; + CLAMP(tgpi->tot_edges, MIN_EDGES, MAX_EDGES); + RNA_int_set(op->ptr, "edges", tgpi->tot_edges); + gpencil_primitive_update(C, op, tgpi); + } + break; + } + case PADMINUS: + case WHEELDOWNMOUSE: { + if ((event->val != KM_RELEASE)) { + tgpi->tot_edges = tgpi->tot_edges - 1; + CLAMP(tgpi->tot_edges, MIN_EDGES, MAX_EDGES); + RNA_int_set(op->ptr, "edges", tgpi->tot_edges); + gpencil_primitive_update(C, op, tgpi); + } + break; + } + case FKEY: /* brush thickness/ brush strength */ + { + if ((event->val == KM_PRESS)) { + if (event->shift) { + tgpi->prev_flag = tgpi->flag; + tgpi->flag = IN_BRUSH_STRENGTH; + } + else { + tgpi->prev_flag = tgpi->flag; + tgpi->flag = IN_BRUSH_SIZE; + } + WM_cursor_modal_set(win, WM_CURSOR_NS_SCROLL); + } + break; + } + } + + copy_v2_v2(tgpi->mvalo, tgpi->mval); + return OPERATOR_RUNNING_MODAL; + } else if (tgpi->flag == IN_BRUSH_SIZE) { switch (event->type) { case MOUSEMOVE: @@ -1534,11 +1679,11 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e case MIDDLEMOUSE: case LEFTMOUSE: tgpi->brush_size = 0; - tgpi->flag = IN_CURVE_EDIT; + tgpi->flag = tgpi->prev_flag; break; case RIGHTMOUSE: if (event->val == KM_RELEASE) { - tgpi->flag = IN_CURVE_EDIT; + tgpi->flag = tgpi->prev_flag; gpencil_primitive_size(tgpi, true); gpencil_primitive_update(C, op, tgpi); } @@ -1557,11 +1702,11 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e case MIDDLEMOUSE: case LEFTMOUSE: tgpi->brush_strength = 0.0f; - tgpi->flag = IN_CURVE_EDIT; + tgpi->flag = tgpi->prev_flag; break; case RIGHTMOUSE: if (event->val == KM_RELEASE) { - tgpi->flag = IN_CURVE_EDIT; + tgpi->flag = tgpi->prev_flag; gpencil_primitive_strength(tgpi, true); gpencil_primitive_update(C, op, tgpi); } @@ -1570,7 +1715,7 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e copy_v2_v2(tgpi->mvalo, tgpi->mval); return OPERATOR_RUNNING_MODAL; } - else if (tgpi->flag != IDLE) { + else if (!ELEM(tgpi->flag, IDLE) && !ELEM(tgpi->type, GP_STROKE_POLYLINE)) { gpencil_primitive_edit_event_handling(C, op, win, event, tgpi); } @@ -1581,24 +1726,38 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e /* TODO: Ignore if not in main region yet */ tgpi->flag = IN_PROGRESS; gpencil_primitive_interaction_begin(tgpi, event); + if (ELEM(tgpi->type, GP_STROKE_POLYLINE)) { + tgpi->flag = IN_POLYLINE; + gpencil_primitive_update(C, op, tgpi); + return OPERATOR_RUNNING_MODAL; + } } - else if ((event->val == KM_RELEASE) && (tgpi->flag == IN_MOVE)) { - tgpi->flag = IN_CURVE_EDIT; - } - else if ((event->val == KM_RELEASE) && (tgpi->flag == IN_PROGRESS)) { + else if ((event->val == KM_RELEASE) && (tgpi->flag == IN_PROGRESS) && + (!ELEM(tgpi->type, GP_STROKE_POLYLINE))) { /* set control points and enter edit mode */ tgpi->flag = IN_CURVE_EDIT; gp_primitive_update_cps(tgpi); gpencil_primitive_update(C, op, tgpi); } else if ((event->val == KM_RELEASE) && (tgpi->flag == IN_PROGRESS) && - (tgpi->type != GP_STROKE_CURVE)) { + (!ELEM(tgpi->type, GP_STROKE_CURVE, GP_STROKE_POLYLINE))) { /* stop drawing primitive */ tgpi->flag = IDLE; gpencil_primitive_interaction_end(C, op, win, tgpi); /* done! */ return OPERATOR_FINISHED; } + else if ((event->val == KM_RELEASE) && (tgpi->flag == IN_PROGRESS) && + (ELEM(tgpi->type, GP_STROKE_POLYLINE))) { + /* set control points and enter edit mode */ + tgpi->flag = IN_POLYLINE; + gpencil_primitive_update(C, op, tgpi); + copy_v2_v2(tgpi->mvalo, tgpi->mval); + return OPERATOR_RUNNING_MODAL; + } + else if ((event->val == KM_RELEASE) && (tgpi->flag == IN_MOVE)) { + tgpi->flag = IN_CURVE_EDIT; + } else { if (G.debug & G_DEBUG) { printf("GP Add Primitive Modal: LEFTMOUSE %d, Status = %d\n", event->val, tgpi->flag); @@ -1618,7 +1777,7 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e /* exception to cancel current stroke when we have previous strokes in buffer */ if (tgpi->tot_stored_edges > 0) { tgpi->flag = IDLE; - tgpi->tot_edges = 0; + tgpi->tot_edges = tgpi->tot_stored_edges ? 1 : 0; gp_primitive_update_strokes(C, tgpi); gpencil_primitive_interaction_end(C, op, win, tgpi); /* done! */ @@ -1665,7 +1824,7 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e { if ((event->val == KM_PRESS)) { tgpi->flag = IN_MOVE; - WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR); + WM_cursor_modal_set(win, WM_CURSOR_NSEW_SCROLL); } break; } @@ -1673,12 +1832,14 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e { if ((event->val == KM_PRESS)) { if (event->shift) { + tgpi->prev_flag = tgpi->flag; tgpi->flag = IN_BRUSH_STRENGTH; } else { + tgpi->prev_flag = tgpi->flag; tgpi->flag = IN_BRUSH_SIZE; } - WM_cursor_modal_set(win, BC_NS_SCROLLCURSOR); + WM_cursor_modal_set(win, WM_CURSOR_NS_SCROLL); } break; } @@ -1704,7 +1865,7 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e case TABKEY: { if (tgpi->flag == IN_CURVE_EDIT) { tgpi->flag = IN_PROGRESS; - WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR); + WM_cursor_modal_set(win, WM_CURSOR_NSEW_SCROLL); gp_primitive_update_cps(tgpi); gpencil_primitive_update(C, op, tgpi); } @@ -1712,7 +1873,7 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e } case MOUSEMOVE: /* calculate new position */ { - if (tgpi->flag == IN_CURVE_EDIT) { + if (ELEM(tgpi->flag, IN_CURVE_EDIT)) { break; } /* only handle mousemove if not doing numinput */ @@ -1725,26 +1886,11 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e } /* Keep square if shift key */ if (event->shift) { - float x = tgpi->end[0] - tgpi->origin[0]; - float y = tgpi->end[1] - tgpi->origin[1]; - if (tgpi->type == GP_STROKE_LINE || tgpi->curve) { - float angle = fabsf(atan2f(y, x)); - if (angle < 0.4f || angle > (M_PI - 0.4f)) { - tgpi->end[1] = tgpi->origin[1]; - } - else if (angle > (M_PI_2 - 0.4f) && angle < (M_PI_2 + 0.4f)) { - tgpi->end[0] = tgpi->origin[0]; - } - else { - gpencil_primitive_to_square(tgpi, x, y); - } - } - else { - gpencil_primitive_to_square(tgpi, x, y); - } + gpencil_primitive_constrain( + tgpi, (ELEM(tgpi->type, GP_STROKE_LINE, GP_STROKE_POLYLINE) || tgpi->curve)); } /* Center primitive if alt key */ - if (event->alt) { + if (event->alt && !ELEM(tgpi->type, GP_STROKE_POLYLINE)) { tgpi->start[0] = tgpi->origin[0] - (tgpi->end[0] - tgpi->origin[0]); tgpi->start[1] = tgpi->origin[1] - (tgpi->end[1] - tgpi->origin[1]); } @@ -1796,6 +1942,7 @@ void GPENCIL_OT_primitive(wmOperatorType *ot) static EnumPropertyItem primitive_type[] = { {GP_STROKE_BOX, "BOX", 0, "Box", ""}, {GP_STROKE_LINE, "LINE", 0, "Line", ""}, + {GP_STROKE_POLYLINE, "POLYLINE", 0, "Polyline", ""}, {GP_STROKE_CIRCLE, "CIRCLE", 0, "Circle", ""}, {GP_STROKE_ARC, "ARC", 0, "Arc", ""}, {GP_STROKE_CURVE, "CURVE", 0, "Curve", ""}, diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c index b7b73c1b501..be265ed4bd5 100644 --- a/source/blender/editors/gpencil/gpencil_select.c +++ b/source/blender/editors/gpencil/gpencil_select.c @@ -832,7 +832,8 @@ void GPENCIL_OT_select_less(wmOperatorType *ot) * from gpencil_paint.c #gp_stroke_eraser_dostroke(). * It would be great to de-duplicate the logic here sometime, but that can wait. */ -static bool gp_stroke_do_circle_sel(bGPDlayer *gpl, +static bool gp_stroke_do_circle_sel(bGPdata *gpd, + bGPDlayer *gpl, bGPDstroke *gps, GP_SpaceConversion *gsc, const int mx, @@ -844,12 +845,14 @@ static bool gp_stroke_do_circle_sel(bGPDlayer *gpl, const int selectmode, const float scale) { + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); bGPDspoint *pt1 = NULL; bGPDspoint *pt2 = NULL; - bGPDstroke *gps_orig = gps->runtime.gps_orig; int x0 = 0, y0 = 0, x1 = 0, y1 = 0; int i; bool changed = false; + bGPDstroke *gps_active = (!is_multiedit) ? gps->runtime.gps_orig : gps; + bGPDspoint *pt_active = NULL; if (gps->totpoints == 1) { bGPDspoint pt_temp; @@ -862,12 +865,12 @@ static bool gp_stroke_do_circle_sel(bGPDlayer *gpl, if (((x0 - mx) * (x0 - mx) + (y0 - my) * (y0 - my)) <= radius * radius) { /* change selection */ if (select) { - gps_orig->points->flag |= GP_SPOINT_SELECT; - gps_orig->flag |= GP_STROKE_SELECT; + gps_active->points->flag |= GP_SPOINT_SELECT; + gps_active->flag |= GP_STROKE_SELECT; } else { - gps_orig->points->flag &= ~GP_SPOINT_SELECT; - gps_orig->flag &= ~GP_STROKE_SELECT; + gps_active->points->flag &= ~GP_SPOINT_SELECT; + gps_active->flag &= ~GP_STROKE_SELECT; } return true; @@ -907,20 +910,24 @@ static bool gp_stroke_do_circle_sel(bGPDlayer *gpl, */ hit = true; if (select) { - if (pt1->runtime.pt_orig != NULL) { - pt1->runtime.pt_orig->flag |= GP_SPOINT_SELECT; + pt_active = (!is_multiedit) ? pt1->runtime.pt_orig : pt1; + if (pt_active != NULL) { + pt_active->flag |= GP_SPOINT_SELECT; } - if (pt2->runtime.pt_orig != NULL) { - pt2->runtime.pt_orig->flag |= GP_SPOINT_SELECT; + pt_active = (!is_multiedit) ? pt2->runtime.pt_orig : pt2; + if (pt_active != NULL) { + pt_active->flag |= GP_SPOINT_SELECT; } changed = true; } else { - if (pt1->runtime.pt_orig != NULL) { - pt1->runtime.pt_orig->flag &= ~GP_SPOINT_SELECT; + pt_active = (!is_multiedit) ? pt1->runtime.pt_orig : pt1; + if (pt_active != NULL) { + pt_active->flag &= ~GP_SPOINT_SELECT; } - if (pt2->runtime.pt_orig != NULL) { - pt2->runtime.pt_orig->flag &= ~GP_SPOINT_SELECT; + pt_active = (!is_multiedit) ? pt2->runtime.pt_orig : pt2; + if (pt_active != NULL) { + pt_active->flag &= ~GP_SPOINT_SELECT; } changed = true; } @@ -935,28 +942,29 @@ static bool gp_stroke_do_circle_sel(bGPDlayer *gpl, /* if stroke mode expand selection */ if ((hit) && (selectmode == GP_SELECTMODE_STROKE)) { for (i = 0, pt1 = gps->points; i < gps->totpoints; i++, pt1++) { - if (pt1->runtime.pt_orig != NULL) { + pt_active = (!is_multiedit) ? pt1->runtime.pt_orig : pt1; + if (pt_active != NULL) { if (select) { - pt1->runtime.pt_orig->flag |= GP_SPOINT_SELECT; + pt_active->flag |= GP_SPOINT_SELECT; } else { - pt1->runtime.pt_orig->flag &= ~GP_SPOINT_SELECT; + pt_active->flag &= ~GP_SPOINT_SELECT; } } } } /* expand selection to segment */ - if ((hit) && (selectmode == GP_SELECTMODE_SEGMENT) && (select) && - (pt1->runtime.pt_orig != NULL)) { + pt_active = (!is_multiedit) ? pt1->runtime.pt_orig : pt1; + if ((hit) && (selectmode == GP_SELECTMODE_SEGMENT) && (select) && (pt_active != NULL)) { float r_hita[3], r_hitb[3]; bool hit_select = (bool)(pt1->flag & GP_SPOINT_SELECT); ED_gpencil_select_stroke_segment( - gpl, gps_orig, pt1->runtime.pt_orig, hit_select, false, scale, r_hita, r_hitb); + gpl, gps_active, pt_active, hit_select, false, scale, r_hita, r_hitb); } /* Ensure that stroke selection is in sync with its points */ - BKE_gpencil_stroke_sync_selection(gps_orig); + BKE_gpencil_stroke_sync_selection(gps_active); } return changed; @@ -1016,8 +1024,18 @@ static int gpencil_circle_select_exec(bContext *C, wmOperator *op) /* find visible strokes, and select if hit */ GP_EVALUATED_STROKES_BEGIN(gpstroke_iter, C, gpl, gps) { - changed |= gp_stroke_do_circle_sel( - gpl, gps, &gsc, mx, my, radius, select, &rect, gpstroke_iter.diff_mat, selectmode, scale); + changed |= gp_stroke_do_circle_sel(gpd, + gpl, + gps, + &gsc, + mx, + my, + radius, + select, + &rect, + gpstroke_iter.diff_mat, + selectmode, + scale); } GP_EVALUATED_STROKES_END(gpstroke_iter); @@ -1090,6 +1108,8 @@ static int gpencil_generic_select_exec(bContext *C, ((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0)); const bool segmentmode = ((selectmode == GP_SELECTMODE_SEGMENT) && ((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0)); + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode"); const float scale = ts->gp_sculpt.isect_threshold; @@ -1125,36 +1145,32 @@ static int gpencil_generic_select_exec(bContext *C, /* select/deselect points */ GP_EVALUATED_STROKES_BEGIN(gpstroke_iter, C, gpl, gps) { + bGPDstroke *gps_active = (!is_multiedit) ? gps->runtime.gps_orig : gps; bGPDspoint *pt; int i; bool hit = false; for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - if (pt->runtime.pt_orig == NULL) { + if ((!is_multiedit) && (pt->runtime.pt_orig == NULL)) { continue; } + bGPDspoint *pt_active = (!is_multiedit) ? pt->runtime.pt_orig : pt; /* convert point coords to screenspace */ const bool is_inside = is_inside_fn(gps, pt, &gsc, gpstroke_iter.diff_mat, user_data); if (strokemode == false) { - const bool is_select = (pt->runtime.pt_orig->flag & GP_SPOINT_SELECT) != 0; + const bool is_select = (pt_active->flag & GP_SPOINT_SELECT) != 0; const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); if (sel_op_result != -1) { - SET_FLAG_FROM_TEST(pt->runtime.pt_orig->flag, sel_op_result, GP_SPOINT_SELECT); + SET_FLAG_FROM_TEST(pt_active->flag, sel_op_result, GP_SPOINT_SELECT); changed = true; /* expand selection to segment */ if ((sel_op_result != -1) && (segmentmode)) { - bool hit_select = (bool)(pt->runtime.pt_orig->flag & GP_SPOINT_SELECT); + bool hit_select = (bool)(pt_active->flag & GP_SPOINT_SELECT); float r_hita[3], r_hitb[3]; - ED_gpencil_select_stroke_segment(gpl, - gps->runtime.gps_orig, - pt->runtime.pt_orig, - hit_select, - false, - scale, - r_hita, - r_hitb); + ED_gpencil_select_stroke_segment( + gpl, gps_active, pt_active, hit_select, false, scale, r_hita, r_hitb); } } } @@ -1168,19 +1184,21 @@ static int gpencil_generic_select_exec(bContext *C, /* if stroke mode expand selection */ if (strokemode) { - const bool is_select = BKE_gpencil_stroke_select_check(gps->runtime.gps_orig); + const bool is_select = BKE_gpencil_stroke_select_check(gps_active); const bool is_inside = hit; const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); if (sel_op_result != -1) { for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - if (pt->runtime.pt_orig == NULL) { + if ((!is_multiedit) && (pt->runtime.pt_orig == NULL)) { continue; } + bGPDspoint *pt_active = (!is_multiedit) ? pt->runtime.pt_orig : pt; + if (sel_op_result) { - pt->runtime.pt_orig->flag |= GP_SPOINT_SELECT; + pt_active->flag |= GP_SPOINT_SELECT; } else { - pt->runtime.pt_orig->flag &= ~GP_SPOINT_SELECT; + pt_active->flag &= ~GP_SPOINT_SELECT; } } changed = true; @@ -1188,7 +1206,7 @@ static int gpencil_generic_select_exec(bContext *C, } /* Ensure that stroke selection is in sync with its points */ - BKE_gpencil_stroke_sync_selection(gps->runtime.gps_orig); + BKE_gpencil_stroke_sync_selection(gps_active); } GP_EVALUATED_STROKES_END(gpstroke_iter); @@ -1369,6 +1387,7 @@ static int gpencil_select_exec(bContext *C, wmOperator *op) bGPdata *gpd = ED_gpencil_data_get_active(C); ToolSettings *ts = CTX_data_tool_settings(C); const float scale = ts->gp_sculpt.isect_threshold; + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); /* "radius" is simply a threshold (screen space) to make it easier to test with a tolerance */ const float radius = 0.50f * U.widget_unit; @@ -1414,13 +1433,14 @@ static int gpencil_select_exec(bContext *C, wmOperator *op) /* XXX: maybe we should go from the top of the stack down instead... */ GP_EVALUATED_STROKES_BEGIN(gpstroke_iter, C, gpl, gps) { + bGPDstroke *gps_active = (!is_multiedit) ? gps->runtime.gps_orig : gps; bGPDspoint *pt; int i; /* firstly, check for hit-point */ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { int xy[2]; - if (pt->runtime.pt_orig == NULL) { + if ((!is_multiedit) && (pt->runtime.pt_orig == NULL)) { continue; } @@ -1437,8 +1457,8 @@ static int gpencil_select_exec(bContext *C, wmOperator *op) /* only use this point if it is a better match than the current hit - T44685 */ if (pt_distance < hit_distance) { hit_layer = gpl; - hit_stroke = gps->runtime.gps_orig; - hit_point = pt->runtime.pt_orig; + hit_stroke = gps_active; + hit_point = (!is_multiedit) ? pt->runtime.pt_orig : pt; hit_distance = pt_distance; } } diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index 89a2a987f60..b194d28a8b8 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -1391,7 +1391,7 @@ void ED_gpencil_add_defaults(bContext *C, Object *ob) /* if not exist, create a new one */ if ((paint->brush == NULL) || (paint->brush->gpencil_settings == NULL)) { /* create new brushes */ - BKE_brush_gpencil_presets(C); + BKE_brush_gpencil_presets(bmain, ts); } /* ensure a color exists and is assigned to object */ diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h index cb6c66ed795..bf9b69f12e1 100644 --- a/source/blender/editors/include/ED_anim_api.h +++ b/source/blender/editors/include/ED_anim_api.h @@ -834,12 +834,28 @@ void ED_drivers_editor_init(struct bContext *C, struct ScrArea *sa); /* ************************************************ */ +typedef enum eAnimvizCalcRange { + /* Update motion paths at the current frame only. */ + ANIMVIZ_CALC_RANGE_CURRENT_FRAME, + + /* Try to limit updates to a close neighborhood of the current frame. */ + ANIMVIZ_CALC_RANGE_CHANGED, + + /* Update an entire range of the motion paths. */ + ANIMVIZ_CALC_RANGE_FULL, +} eAnimvizCalcRange; + +struct Depsgraph *animviz_depsgraph_build(struct Main *bmain, + struct Scene *scene, + struct ViewLayer *view_layer, + struct ListBase *targets); + void animviz_calc_motionpaths(struct Depsgraph *depsgraph, struct Main *bmain, struct Scene *scene, ListBase *targets, - bool restore, - bool current_frame_only); + eAnimvizCalcRange range, + bool restore); void animviz_get_object_motionpaths(struct Object *ob, ListBase *targets); diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h index 173ba65fc47..7ac42967dda 100644 --- a/source/blender/editors/include/ED_armature.h +++ b/source/blender/editors/include/ED_armature.h @@ -284,10 +284,18 @@ bool ED_pose_deselect_all_multi(struct bContext *C, int select_mode, const bool bool ED_pose_deselect_all(struct Object *ob, int select_mode, const bool ignore_visibility); void ED_pose_bone_select_tag_update(struct Object *ob); void ED_pose_bone_select(struct Object *ob, struct bPoseChannel *pchan, bool select); + +/* Corresponds to eAnimvizCalcRange. */ +typedef enum ePosePathCalcRange { + POSE_PATH_CALC_RANGE_CURRENT_FRAME, + POSE_PATH_CALC_RANGE_CHANGED, + POSE_PATH_CALC_RANGE_FULL, +} ePosePathCalcRange; void ED_pose_recalculate_paths(struct bContext *C, struct Scene *scene, struct Object *ob, - bool current_frame_only); + ePosePathCalcRange range); + struct Object *ED_pose_object_from_context(struct bContext *C); /* meshlaplacian.c */ diff --git a/source/blender/editors/include/ED_fileselect.h b/source/blender/editors/include/ED_fileselect.h index 37090ce3421..83890c1621c 100644 --- a/source/blender/editors/include/ED_fileselect.h +++ b/source/blender/editors/include/ED_fileselect.h @@ -98,6 +98,8 @@ struct rcti; struct FileSelectParams *ED_fileselect_get_params(struct SpaceFile *sfile); short ED_fileselect_set_params(struct SpaceFile *sfile); +void ED_fileselect_set_params_from_userdef(struct SpaceFile *sfile); +void ED_fileselect_params_to_userdef(struct SpaceFile *sfile, int temp_win_size[]); void ED_fileselect_reset_params(struct SpaceFile *sfile); diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h index 63ddc669ab2..0ff1b8bb40b 100644 --- a/source/blender/editors/include/ED_gpencil.h +++ b/source/blender/editors/include/ED_gpencil.h @@ -71,8 +71,15 @@ typedef struct tGPspoint { float uv_rot; /* uv rotation for dor mode */ float rnd[3]; /* rnd value */ bool rnd_dirty; /* rnd flag */ + short tflag; /* Internal flag */ } tGPspoint; +/* tGPspoint->flag */ +typedef enum etGPspoint_tFlag { + /* Created by Fake event (used when mouse/pen move very fast while drawing). */ + GP_TPOINT_FAKE = (1 << 0), +} etGPspoint_tFlag; + /* used to sort by zdepth gpencil objects in viewport */ /* TODO: this could be a system parameter in userprefs screen */ #define GP_CACHE_BLOCK_SIZE 16 diff --git a/source/blender/editors/include/ED_image.h b/source/blender/editors/include/ED_image.h index 7d69f86dbf8..69742af9f50 100644 --- a/source/blender/editors/include/ED_image.h +++ b/source/blender/editors/include/ED_image.h @@ -77,6 +77,7 @@ void ED_image_mouse_pos(struct SpaceImage *sima, struct ARegion *ar, const int mval[2], float co[2]); +void ED_image_view_center_to_point(struct SpaceImage *sima, float x, float y); void ED_image_point_pos( struct SpaceImage *sima, struct ARegion *ar, float x, float y, float *xr, float *yr); void ED_image_point_pos__reverse(struct SpaceImage *sima, @@ -94,6 +95,7 @@ bool ED_space_image_paint_curve(const struct bContext *C); bool ED_space_image_check_show_maskedit(struct SpaceImage *sima, struct ViewLayer *view_layer); bool ED_space_image_maskedit_poll(struct bContext *C); bool ED_space_image_maskedit_mask_poll(struct bContext *C); +bool ED_space_image_cursor_poll(struct bContext *C); void ED_image_draw_info(struct Scene *scene, struct ARegion *ar, diff --git a/source/blender/editors/include/ED_keyframing.h b/source/blender/editors/include/ED_keyframing.h index 42e5add2ef0..16b3c9c240a 100644 --- a/source/blender/editors/include/ED_keyframing.h +++ b/source/blender/editors/include/ED_keyframing.h @@ -310,15 +310,24 @@ extern EnumPropertyItem prop_driver_create_mapping_types[]; /* -------- */ +typedef enum eDriverFCurveCreationMode { + DRIVER_FCURVE_LOOKUP_ONLY = 0, /* Don't add anything if not found. */ + DRIVER_FCURVE_KEYFRAMES = 1, /* Add with keyframes, for visual tweaking. */ + DRIVER_FCURVE_GENERATOR = 2, /* Add with generator, for script backwards compatibility. */ + DRIVER_FCURVE_EMPTY = 3 /* Add without data, for pasting. */ +} eDriverFCurveCreationMode; + /* Low-level call to add a new driver F-Curve. This shouldn't be used directly for most tools, * although there are special cases where this approach is preferable. */ struct FCurve *verify_driver_fcurve(struct ID *id, const char rna_path[], const int array_index, - short add); + eDriverFCurveCreationMode creation_mode); -struct FCurve *alloc_driver_fcurve(const char rna_path[], const int array_index, short add); +struct FCurve *alloc_driver_fcurve(const char rna_path[], + const int array_index, + eDriverFCurveCreationMode creation_mode); /* -------- */ diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index d8d62ad6f08..fc7b0d8be8f 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -380,17 +380,17 @@ 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 */ -#if 0 -void ED_mesh_geometry_add( - struct Mesh *mesh, struct ReportList *reports, int verts, int edges, int faces); -#endif -void ED_mesh_polys_add(struct Mesh *mesh, struct ReportList *reports, int count); +void ED_mesh_verts_add(struct Mesh *mesh, struct ReportList *reports, int count); void ED_mesh_edges_add(struct Mesh *mesh, struct ReportList *reports, int count); void ED_mesh_loops_add(struct Mesh *mesh, struct ReportList *reports, int count); -void ED_mesh_vertices_add(struct Mesh *mesh, struct ReportList *reports, int count); +void ED_mesh_polys_add(struct Mesh *mesh, struct ReportList *reports, int count); +void ED_mesh_verts_remove(struct Mesh *mesh, struct ReportList *reports, int count); void ED_mesh_edges_remove(struct Mesh *mesh, struct ReportList *reports, int count); -void ED_mesh_vertices_remove(struct Mesh *mesh, struct ReportList *reports, int count); +void ED_mesh_loops_remove(struct Mesh *mesh, struct ReportList *reports, int count); +void ED_mesh_polys_remove(struct Mesh *mesh, struct ReportList *reports, int count); + +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); diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h index c481c19a552..38d75aa57e9 100644 --- a/source/blender/editors/include/ED_object.h +++ b/source/blender/editors/include/ED_object.h @@ -238,9 +238,17 @@ void ED_object_single_user(struct Main *bmain, struct Scene *scene, struct Objec /* object motion paths */ void ED_objects_clear_paths(struct bContext *C, bool only_selected); + +/* Corresponds to eAnimvizCalcRange. */ +typedef enum eObjectPathCalcRange { + OBJECT_PATH_CALC_RANGE_CURRENT_FRAME, + OBJECT_PATH_CALC_RANGE_CHANGED, + OBJECT_PATH_CALC_RANGE_FULL, +} eObjectPathCalcRange; + void ED_objects_recalculate_paths(struct bContext *C, struct Scene *scene, - bool current_frame_only); + eObjectPathCalcRange range); /* constraints */ struct ListBase *get_active_constraints(struct Object *ob); diff --git a/source/blender/editors/include/ED_paint.h b/source/blender/editors/include/ED_paint.h index 88cc8a85897..fec4beea809 100644 --- a/source/blender/editors/include/ED_paint.h +++ b/source/blender/editors/include/ED_paint.h @@ -40,13 +40,44 @@ void ED_imapaint_dirty_region( struct Image *ima, struct ImBuf *ibuf, int x, int y, int w, int h, bool find_old); void ED_imapaint_bucket_fill(struct bContext *C, float color[3], struct wmOperator *op); -/* paint_image_undo.c */ +/* image_undo.c */ void ED_image_undo_push_begin(const char *name, int paint_mode); +void ED_image_undo_push_begin_with_image(const char *name, + struct Image *image, + struct ImBuf *ibuf); + void ED_image_undo_push_end(void); void ED_image_undo_restore(struct UndoStep *us); void ED_image_undosys_type(struct UndoType *ut); +void *ED_image_paint_tile_find(struct ListBase *undo_tiles, + struct Image *ima, + struct ImBuf *ibuf, + int x_tile, + int y_tile, + unsigned short **r_mask, + bool validate); +void *ED_image_paint_tile_push(struct ListBase *undo_tiles, + struct Image *ima, + struct ImBuf *ibuf, + struct ImBuf **tmpibuf, + int x_tile, + int y_tile, + unsigned short **r_mask, + bool **r_valid, + bool use_thread_lock, + bool find_prev); +void ED_image_paint_tile_lock_init(void); +void ED_image_paint_tile_lock_end(void); + +struct ListBase *ED_image_paint_tile_list_get(void); + +#define ED_IMAGE_UNDO_TILE_BITS 6 +#define ED_IMAGE_UNDO_TILE_SIZE (1 << ED_IMAGE_UNDO_TILE_BITS) +#define ED_IMAGE_UNDO_TILE_NUMBER(size) \ + (((size) + ED_IMAGE_UNDO_TILE_SIZE - 1) >> ED_IMAGE_UNDO_TILE_BITS) + /* paint_curve_undo.c */ void ED_paintcurve_undo_push_begin(const char *name); void ED_paintcurve_undo_push_end(void); diff --git a/source/blender/editors/include/ED_particle.h b/source/blender/editors/include/ED_particle.h index 3ef3c0ba937..0c973f4ca88 100644 --- a/source/blender/editors/include/ED_particle.h +++ b/source/blender/editors/include/ED_particle.h @@ -40,12 +40,18 @@ int PE_start_edit(struct PTCacheEdit *edit); /* access */ struct PTCacheEdit *PE_get_current_from_psys(struct ParticleSystem *psys); -struct PTCacheEdit *PE_get_current(struct Scene *scene, struct Object *ob); +struct PTCacheEdit *PE_get_current(struct Depsgraph *depsgraph, + struct Scene *scene, + struct Object *ob); struct PTCacheEdit *PE_create_current(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *ob); void PE_current_changed(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *ob); -int PE_minmax(struct Scene *scene, struct ViewLayer *view_layer, float min[3], float max[3]); +int PE_minmax(struct Depsgraph *depsgraph, + struct Scene *scene, + struct ViewLayer *view_layer, + float min[3], + float max[3]); struct ParticleEditSettings *PE_settings(struct Scene *scene); /* update calls */ diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index d0fab134dcc..c3e61f5f2b2 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -66,6 +66,7 @@ void ED_region_do_listen(struct wmWindow *win, void ED_region_do_layout(struct bContext *C, struct ARegion *ar); void ED_region_do_draw(struct bContext *C, struct ARegion *ar); void ED_region_exit(struct bContext *C, struct ARegion *ar); +void ED_region_remove(struct bContext *C, struct ScrArea *sa, struct ARegion *ar); void ED_region_pixelspace(struct ARegion *ar); void ED_region_update_rect(struct ARegion *ar); void ED_region_init(struct ARegion *ar); @@ -238,6 +239,15 @@ struct ScrArea *ED_screen_state_toggle(struct bContext *C, struct wmWindow *win, struct ScrArea *sa, const short state); +ScrArea *ED_screen_temp_space_open(struct bContext *C, + const char *title, + int x, + int y, + int sizex, + int sizey, + eSpace_Type space_type, + int display_type, + bool dialog); void ED_screens_header_tools_menu_create(struct bContext *C, struct uiLayout *layout, void *arg); void ED_screens_footer_tools_menu_create(struct bContext *C, struct uiLayout *layout, void *arg); void ED_screens_navigation_bar_tools_menu_create(struct bContext *C, diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index dc7b25392e8..28280fae3a8 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -521,7 +521,9 @@ int view3d_opengl_select(struct ViewContext *vc, /* view3d_select.c */ float ED_view3d_select_dist_px(void); -void ED_view3d_viewcontext_init(struct bContext *C, struct ViewContext *vc); +void ED_view3d_viewcontext_init(struct bContext *C, + struct ViewContext *vc, + struct Depsgraph *depsgraph); void ED_view3d_viewcontext_init_object(struct ViewContext *vc, struct Object *obact); void view3d_operator_needs_opengl(const struct bContext *C); void view3d_region_operator_needs_opengl(struct wmWindow *win, struct ARegion *ar); diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h index ffabace48c0..4e4db46adf6 100644 --- a/source/blender/editors/include/UI_icons.h +++ b/source/blender/editors/include/UI_icons.h @@ -858,9 +858,9 @@ DEF_ICON_BLANK(855) DEF_ICON_BLANK(856) DEF_ICON_BLANK(857) DEF_ICON_BLANK(858) -DEF_ICON_BLANK(859) -DEF_ICON_BLANK(860) -DEF_ICON_BLANK(861) +DEF_ICON(DESKTOP) +DEF_ICON(EXTERNAL_DRIVE) +DEF_ICON(NETWORK_DRIVE) /* SEQUENCE / IMAGE EDITOR */ DEF_ICON(SEQ_SEQUENCER) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 702d319817f..f5721c008b2 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -524,6 +524,7 @@ bool UI_but_is_tool(const uiBut *but); bool UI_but_is_utf8(const uiBut *but); #define UI_but_is_decorator(but) ((but)->func == ui_but_anim_decorate_cb) +bool UI_block_is_empty_ex(const uiBlock *block, const bool skip_title); bool UI_block_is_empty(const uiBlock *block); bool UI_block_can_add_separator(const uiBlock *block); @@ -1810,6 +1811,7 @@ void uiLayoutSetActivateInit(uiLayout *layout, bool active); void uiLayoutSetEnabled(uiLayout *layout, bool enabled); void uiLayoutSetRedAlert(uiLayout *layout, bool redalert); void uiLayoutSetAlignment(uiLayout *layout, char alignment); +void uiLayoutSetFixedSize(uiLayout *layout, bool fixed_size); void uiLayoutSetKeepAspect(uiLayout *layout, bool keepaspect); void uiLayoutSetScaleX(uiLayout *layout, float scale); void uiLayoutSetScaleY(uiLayout *layout, float scale); @@ -1827,6 +1829,7 @@ bool uiLayoutGetActivateInit(uiLayout *layout); bool uiLayoutGetEnabled(uiLayout *layout); bool uiLayoutGetRedAlert(uiLayout *layout); int uiLayoutGetAlignment(uiLayout *layout); +bool uiLayoutGetFixedSize(uiLayout *layout); bool uiLayoutGetKeepAspect(uiLayout *layout); int uiLayoutGetWidth(uiLayout *layout); float uiLayoutGetScaleX(uiLayout *layout); @@ -2451,6 +2454,8 @@ void UI_widgetbase_draw_cache_end(void); void UI_theme_init_default(void); void UI_style_init_default(void); +void UI_interface_tag_script_reload(void); + /* Special drawing for toolbar, mainly workarounds for inflexible icon sizing. */ #define USE_UI_TOOLBAR_HACK diff --git a/source/blender/editors/include/UI_resources.h b/source/blender/editors/include/UI_resources.h index efa35c84b9e..8e04db8127e 100644 --- a/source/blender/editors/include/UI_resources.h +++ b/source/blender/editors/include/UI_resources.h @@ -234,6 +234,8 @@ typedef enum ThemeColorID { TH_DIS_MARKER, TH_PATH_BEFORE, TH_PATH_AFTER, + TH_PATH_KEYFRAME_BEFORE, + TH_PATH_KEYFRAME_AFTER, TH_CAMERA_PATH, TH_LOCK_MARKER, @@ -281,6 +283,7 @@ typedef enum ThemeColorID { TH_NLA_TWEAK, /* 'tweaking' track in NLA */ TH_NLA_TWEAK_DUPLI, /* error/warning flag for other strips referencing dupli strip */ + TH_NLA_TRACK, TH_NLA_TRANSITION, TH_NLA_TRANSITION_SEL, TH_NLA_META, diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt index bc8d25e8d9e..d33023c69a1 100644 --- a/source/blender/editors/interface/CMakeLists.txt +++ b/source/blender/editors/interface/CMakeLists.txt @@ -50,6 +50,7 @@ set(SRC interface_eyedropper_datablock.c interface_eyedropper_depth.c interface_eyedropper_driver.c + interface_eyedropper_gpencil_color.c interface_handlers.c interface_icons.c interface_icons_event.c diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 27a33a38b15..54fd91e5361 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -786,6 +786,8 @@ static bool ui_but_update_from_old_block(const bContext *C, oldbut->flag = (oldbut->flag & ~flag_copy) | (but->flag & flag_copy); oldbut->drawflag = (oldbut->drawflag & ~drawflag_copy) | (but->drawflag & drawflag_copy); + SWAP(ListBase, but->extra_op_icons, oldbut->extra_op_icons); + /* copy hardmin for list rows to prevent 'sticking' highlight to mouse position * when scrolling without moving mouse (see [#28432]) */ if (ELEM(oldbut->type, UI_BTYPE_ROW, UI_BTYPE_LISTROW)) { @@ -3389,7 +3391,7 @@ static void ui_but_build_drawstr_float(uiBut *but, double value) if (value == (double)FLT_MAX) { STR_CONCAT(but->drawstr, slen, "inf"); } - else if (value == (double)-FLT_MIN) { + else if (value == (double)-FLT_MAX) { STR_CONCAT(but->drawstr, slen, "-inf"); } else if (subtype == PROP_PERCENTAGE) { @@ -6689,3 +6691,8 @@ void UI_exit(void) ui_resources_free(); ui_but_clipboard_free(); } + +void UI_interface_tag_script_reload(void) +{ + ui_interface_tag_script_reload_queries(); +} diff --git a/source/blender/editors/interface/interface_anim.c b/source/blender/editors/interface/interface_anim.c index 168c6051327..c8baa1a7c7b 100644 --- a/source/blender/editors/interface/interface_anim.c +++ b/source/blender/editors/interface/interface_anim.c @@ -243,7 +243,7 @@ bool ui_but_anim_expression_create(uiBut *but, const char *str) } /* create driver */ - fcu = verify_driver_fcurve(id, path, but->rnaindex, 1); + fcu = verify_driver_fcurve(id, path, but->rnaindex, DRIVER_FCURVE_KEYFRAMES); if (fcu) { ChannelDriver *driver = fcu->driver; diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c index 580ff527bf6..ae6a71f17e6 100644 --- a/source/blender/editors/interface/interface_context_menu.c +++ b/source/blender/editors/interface/interface_context_menu.c @@ -63,7 +63,14 @@ static IDProperty *shortcut_property_from_rna(bContext *C, uiBut *but) { /* Compute data path from context to property. */ + + /* If this returns null, we won't be able to bind shortcuts to these RNA properties. + * Support can be added at #wm_context_member_from_ptr. */ const char *member_id = WM_context_member_from_ptr(C, &but->rnapoin); + if (member_id == NULL) { + return NULL; + } + const char *data_path = RNA_path_from_ID_to_struct(&but->rnapoin); const char *member_id_data_path = member_id; @@ -90,27 +97,35 @@ static IDProperty *shortcut_property_from_rna(bContext *C, uiBut *but) return prop; } -static const char *shortcut_get_operator_property(bContext *C, uiBut *but, IDProperty **prop) +static const char *shortcut_get_operator_property(bContext *C, uiBut *but, IDProperty **r_prop) { if (but->optype) { /* Operator */ - *prop = (but->opptr && but->opptr->data) ? IDP_CopyProperty(but->opptr->data) : NULL; + *r_prop = (but->opptr && but->opptr->data) ? IDP_CopyProperty(but->opptr->data) : NULL; return but->optype->idname; } else if (but->rnaprop) { - if (RNA_property_type(but->rnaprop) == PROP_BOOLEAN) { + const PropertyType rnaprop_type = RNA_property_type(but->rnaprop); + + if (rnaprop_type == PROP_BOOLEAN) { /* Boolean */ - *prop = shortcut_property_from_rna(C, but); + *r_prop = shortcut_property_from_rna(C, but); + if (*r_prop == NULL) { + return NULL; + } return "WM_OT_context_toggle"; } - else if (RNA_property_type(but->rnaprop) == PROP_ENUM) { + else if (rnaprop_type == PROP_ENUM) { /* Enum */ - *prop = shortcut_property_from_rna(C, but); + *r_prop = shortcut_property_from_rna(C, but); + if (*r_prop == NULL) { + return NULL; + } return "WM_OT_context_menu_enum"; } } - *prop = NULL; + *r_prop = NULL; return NULL; } @@ -899,13 +914,13 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but) if (is_array_component) { uiItemBooleanO(layout, - CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy All To Selected"), + CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy All to Selected"), ICON_NONE, "UI_OT_copy_to_selected_button", "all", true); uiItemBooleanO(layout, - CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy Single To Selected"), + CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy Single to Selected"), ICON_NONE, "UI_OT_copy_to_selected_button", "all", @@ -913,7 +928,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but) } else { uiItemBooleanO(layout, - CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy To Selected"), + CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy to Selected"), ICON_NONE, "UI_OT_copy_to_selected_button", "all", @@ -928,7 +943,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but) if (ptr->owner_id && !is_whole_array && ELEM(type, PROP_BOOLEAN, PROP_INT, PROP_FLOAT, PROP_ENUM)) { uiItemO(layout, - CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy As New Driver"), + CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy as New Driver"), ICON_NONE, "UI_OT_copy_as_driver_button"); } @@ -950,7 +965,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but) but->search_func == ui_rna_collection_search_cb)) && ui_jump_to_target_button_poll(C)) { uiItemO(layout, - CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Jump To Target"), + CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Jump to Target"), ICON_NONE, "UI_OT_jump_to_target_button"); uiItemS(layout); diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c index 551e25a5986..72c31c7b39e 100644 --- a/source/blender/editors/interface/interface_draw.c +++ b/source/blender/editors/interface/interface_draw.c @@ -1849,9 +1849,17 @@ void ui_draw_but_CURVE(ARegion *ar, uiBut *but, const uiWidgetColors *wcol, cons cumap = (CurveMapping *)but->poin; } + float clip_size_x = BLI_rctf_size_x(&cumap->curr); + float clip_size_y = BLI_rctf_size_y(&cumap->curr); + + /* zero-sized curve */ + if (clip_size_x == 0.0f || clip_size_y == 0.0f) { + return; + } + /* calculate offset and zoom */ - float zoomx = (BLI_rcti_size_x(rect) - 2.0f) / BLI_rctf_size_x(&cumap->curr); - float zoomy = (BLI_rcti_size_y(rect) - 2.0f) / BLI_rctf_size_y(&cumap->curr); + float zoomx = (BLI_rcti_size_x(rect) - 2.0f) / clip_size_x; + float zoomy = (BLI_rcti_size_y(rect) - 2.0f) / clip_size_y; float offsx = cumap->curr.xmin - (1.0f / zoomx); float offsy = cumap->curr.ymin - (1.0f / zoomy); diff --git a/source/blender/editors/interface/interface_eyedropper.c b/source/blender/editors/interface/interface_eyedropper.c index 3c26c37b610..988dea270f5 100644 --- a/source/blender/editors/interface/interface_eyedropper.c +++ b/source/blender/editors/interface/interface_eyedropper.c @@ -69,6 +69,7 @@ wmKeyMap *eyedropper_modal_keymap(wmKeyConfig *keyconf) WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_id"); WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_depth"); WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_driver"); + WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_gpencil_color"); return keymap; } diff --git a/source/blender/editors/interface/interface_eyedropper_color.c b/source/blender/editors/interface/interface_eyedropper_color.c index 68c12fe7652..0cf357c508b 100644 --- a/source/blender/editors/interface/interface_eyedropper_color.c +++ b/source/blender/editors/interface/interface_eyedropper_color.c @@ -290,7 +290,7 @@ static int eyedropper_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED( { /* init */ if (eyedropper_init(C, op)) { - WM_cursor_modal_set(CTX_wm_window(C), BC_EYEDROPPER_CURSOR); + WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_EYEDROPPER); /* add temp handler */ WM_event_add_modal_handler(C, op); @@ -332,7 +332,7 @@ void UI_OT_eyedropper_color(wmOperatorType *ot) /* identifiers */ ot->name = "Eyedropper"; ot->idname = "UI_OT_eyedropper_color"; - ot->description = "Sample a color from the Blender Window to store in a property"; + ot->description = "Sample a color from the Blender window to store in a property"; /* api callbacks */ ot->invoke = eyedropper_invoke; diff --git a/source/blender/editors/interface/interface_eyedropper_colorband.c b/source/blender/editors/interface/interface_eyedropper_colorband.c index ffe93e48936..479cf9ccffe 100644 --- a/source/blender/editors/interface/interface_eyedropper_colorband.c +++ b/source/blender/editors/interface/interface_eyedropper_colorband.c @@ -304,7 +304,7 @@ static int eyedropper_colorband_invoke(bContext *C, wmOperator *op, const wmEven { /* init */ if (eyedropper_colorband_init(C, op)) { - WM_cursor_modal_set(CTX_wm_window(C), BC_EYEDROPPER_CURSOR); + WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_EYEDROPPER); /* add temp handler */ WM_event_add_modal_handler(C, op); diff --git a/source/blender/editors/interface/interface_eyedropper_datablock.c b/source/blender/editors/interface/interface_eyedropper_datablock.c index 336fae45895..fd5a46e7716 100644 --- a/source/blender/editors/interface/interface_eyedropper_datablock.c +++ b/source/blender/editors/interface/interface_eyedropper_datablock.c @@ -314,7 +314,7 @@ static int datadropper_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED { /* init */ if (datadropper_init(C, op)) { - WM_cursor_modal_set(CTX_wm_window(C), BC_EYEDROPPER_CURSOR); + WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_EYEDROPPER); /* add temp handler */ WM_event_add_modal_handler(C, op); diff --git a/source/blender/editors/interface/interface_eyedropper_depth.c b/source/blender/editors/interface/interface_eyedropper_depth.c index 2e51701e01d..8a48ca19db2 100644 --- a/source/blender/editors/interface/interface_eyedropper_depth.c +++ b/source/blender/editors/interface/interface_eyedropper_depth.c @@ -311,7 +311,7 @@ static int depthdropper_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE { /* init */ if (depthdropper_init(C, op)) { - WM_cursor_modal_set(CTX_wm_window(C), BC_EYEDROPPER_CURSOR); + WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_EYEDROPPER); /* add temp handler */ WM_event_add_modal_handler(C, op); diff --git a/source/blender/editors/interface/interface_eyedropper_driver.c b/source/blender/editors/interface/interface_eyedropper_driver.c index e6fc52bc3bc..cc13367c190 100644 --- a/source/blender/editors/interface/interface_eyedropper_driver.c +++ b/source/blender/editors/interface/interface_eyedropper_driver.c @@ -180,7 +180,7 @@ static int driverdropper_invoke(bContext *C, wmOperator *op, const wmEvent *UNUS { /* init */ if (driverdropper_init(C, op)) { - WM_cursor_modal_set(CTX_wm_window(C), BC_EYEDROPPER_CURSOR); + WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_EYEDROPPER); /* add temp handler */ WM_event_add_modal_handler(C, op); diff --git a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c b/source/blender/editors/interface/interface_eyedropper_gpencil_color.c new file mode 100644 index 00000000000..02d4596e93c --- /dev/null +++ b/source/blender/editors/interface/interface_eyedropper_gpencil_color.c @@ -0,0 +1,324 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2009 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup edinterface + * + * Eyedropper (RGB Color) + * + * Defines: + * - #UI_OT_eyedropper_gpencil_color + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_string.h" + +#include "BLT_translation.h" + +#include "DNA_gpencil_types.h" +#include "DNA_space_types.h" + +#include "BKE_context.h" +#include "BKE_gpencil.h" +#include "BKE_main.h" +#include "BKE_material.h" +#include "BKE_report.h" + +#include "UI_interface.h" + +#include "IMB_colormanagement.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "ED_gpencil.h" +#include "ED_screen.h" +#include "ED_undo.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" + +#include "interface_intern.h" +#include "interface_eyedropper_intern.h" + +typedef struct EyedropperGPencil { + struct ColorManagedDisplay *display; + /** color under cursor RGB */ + float color[3]; +} EyedropperGPencil; + +/* Helper: Draw status message while the user is running the operator */ +static void eyedropper_gpencil_status_indicators(bContext *C) +{ + char msg_str[UI_MAX_DRAW_STR]; + BLI_strncpy( + msg_str, TIP_("LMB: Stroke - Shift: Fill - Shift+Ctrl: Stroke + Fill"), UI_MAX_DRAW_STR); + + ED_workspace_status_text(C, msg_str); +} + +/* Initialize. */ +static bool eyedropper_gpencil_init(bContext *C, wmOperator *op) +{ + EyedropperGPencil *eye = MEM_callocN(sizeof(EyedropperGPencil), __func__); + + op->customdata = eye; + Scene *scene = CTX_data_scene(C); + + const char *display_device; + display_device = scene->display_settings.display_device; + eye->display = IMB_colormanagement_display_get_named(display_device); + + return true; +} + +/* Exit and free memory. */ +static void eyedropper_gpencil_exit(bContext *C, wmOperator *op) +{ + /* Clear status message area. */ + ED_workspace_status_text(C, NULL); + + MEM_SAFE_FREE(op->customdata); +} + +/* Set the material. */ +static void eyedropper_gpencil_color_set(bContext *C, const wmEvent *event, EyedropperGPencil *eye) +{ + Main *bmain = CTX_data_main(C); + Object *ob = CTX_data_active_object(C); + Material *ma = NULL; + + const bool only_stroke = ((!event->ctrl) && (!event->shift)); + const bool only_fill = ((!event->ctrl) && (event->shift)); + const bool both = ((event->ctrl) && (event->shift)); + + float col_conv[4]; + bool found = false; + + /* Convert from linear rgb space to display space because grease pencil colors are in display + * space, and this conversion is needed to undo the conversion to linear performed by + * eyedropper_color_sample_fl. */ + if (eye->display) { + copy_v3_v3(col_conv, eye->color); + IMB_colormanagement_scene_linear_to_display_v3(col_conv, eye->display); + } + else { + copy_v3_v3(col_conv, eye->color); + } + + /* Look for a similar material in grease pencil slots. */ + short *totcol = give_totcolp(ob); + for (short i = 0; i < *totcol; i++) { + ma = give_current_material(ob, i + 1); + if (ma == NULL) { + continue; + } + + MaterialGPencilStyle *gp_style = ma->gp_style; + if (gp_style != NULL) { + /* Check stroke color. */ + bool found_stroke = compare_v3v3(gp_style->stroke_rgba, col_conv, 0.01f) && + (gp_style->flag & GP_STYLE_STROKE_SHOW); + /* Check fill color. */ + bool found_fill = compare_v3v3(gp_style->fill_rgba, col_conv, 0.01f) && + (gp_style->flag & GP_STYLE_FILL_SHOW); + + if ((only_stroke) && (found_stroke) && ((gp_style->flag & GP_STYLE_FILL_SHOW) == 0)) { + found = true; + } + else if ((only_fill) && (found_fill) && ((gp_style->flag & GP_STYLE_STROKE_SHOW) == 0)) { + found = true; + } + else if ((both) && (found_stroke) && (found_fill)) { + found = true; + } + + /* Found existing material. */ + if (found) { + ob->actcol = i + 1; + WM_main_add_notifier(NC_MATERIAL | ND_SHADING_LINKS, NULL); + WM_main_add_notifier(NC_SPACE | ND_SPACE_VIEW3D, NULL); + return; + } + } + } + + /* If material was not found add a new material with stroke and/or fill color + * depending of the secondary key (LMB: Stroke, Shift: Fill, Shift+Ctrl: Stroke/Fill) + */ + int idx; + Material *ma_new = BKE_gpencil_object_material_new(bmain, ob, "Material", &idx); + WM_main_add_notifier(NC_OBJECT | ND_OB_SHADING, &ob->id); + WM_main_add_notifier(NC_MATERIAL | ND_SHADING_LINKS, NULL); + DEG_relations_tag_update(bmain); + + BLI_assert(ma_new != NULL); + + MaterialGPencilStyle *gp_style_new = ma_new->gp_style; + BLI_assert(gp_style_new != NULL); + + /* Only create Stroke (default option). */ + if (only_stroke) { + /* Stroke color. */ + gp_style_new->flag |= GP_STYLE_STROKE_SHOW; + gp_style_new->flag &= ~GP_STYLE_FILL_SHOW; + copy_v3_v3(gp_style_new->stroke_rgba, col_conv); + zero_v4(gp_style_new->fill_rgba); + } + /* Fill Only. */ + else if (only_fill) { + /* Fill color. */ + gp_style_new->flag &= ~GP_STYLE_STROKE_SHOW; + gp_style_new->flag |= GP_STYLE_FILL_SHOW; + zero_v4(gp_style_new->stroke_rgba); + copy_v3_v3(gp_style_new->fill_rgba, col_conv); + } + /* Stroke and Fill. */ + else if (both) { + gp_style_new->flag |= GP_STYLE_STROKE_SHOW | GP_STYLE_FILL_SHOW; + copy_v3_v3(gp_style_new->stroke_rgba, col_conv); + copy_v3_v3(gp_style_new->fill_rgba, col_conv); + } + /* Push undo for new created material. */ + ED_undo_push(C, "Add Grease Pencil Material"); +} + +/* Sample the color below cursor. */ +static void eyedropper_gpencil_color_sample(bContext *C, EyedropperGPencil *eye, int mx, int my) +{ + eyedropper_color_sample_fl(C, mx, my, eye->color); +} + +/* Cancel operator. */ +static void eyedropper_gpencil_cancel(bContext *C, wmOperator *op) +{ + eyedropper_gpencil_exit(C, op); +} + +/* Main modal status check. */ +static int eyedropper_gpencil_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + EyedropperGPencil *eye = (EyedropperGPencil *)op->customdata; + /* Handle modal keymap */ + switch (event->type) { + case EVT_MODAL_MAP: { + switch (event->val) { + case EYE_MODAL_SAMPLE_BEGIN: { + return OPERATOR_RUNNING_MODAL; + } + case EYE_MODAL_CANCEL: { + eyedropper_gpencil_cancel(C, op); + return OPERATOR_CANCELLED; + } + case EYE_MODAL_SAMPLE_CONFIRM: { + eyedropper_gpencil_color_sample(C, eye, event->x, event->y); + + /* Create material. */ + eyedropper_gpencil_color_set(C, event, eye); + WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + eyedropper_gpencil_exit(C, op); + return OPERATOR_FINISHED; + break; + } + default: { + break; + } + } + break; + } + case MOUSEMOVE: + case INBETWEEN_MOUSEMOVE: { + eyedropper_gpencil_color_sample(C, eye, event->x, event->y); + break; + } + default: { + break; + } + } + + return OPERATOR_RUNNING_MODAL; +} + +/* Modal Operator init */ +static int eyedropper_gpencil_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + /* Init. */ + if (eyedropper_gpencil_init(C, op)) { + /* Add modal temp handler. */ + WM_event_add_modal_handler(C, op); + /* Status message. */ + eyedropper_gpencil_status_indicators(C); + + return OPERATOR_RUNNING_MODAL; + } + else { + return OPERATOR_PASS_THROUGH; + } +} + +/* Repeat operator */ +static int eyedropper_gpencil_exec(bContext *C, wmOperator *op) +{ + /* init */ + if (eyedropper_gpencil_init(C, op)) { + + /* cleanup */ + eyedropper_gpencil_exit(C, op); + + return OPERATOR_FINISHED; + } + else { + return OPERATOR_PASS_THROUGH; + } +} + +static bool eyedropper_gpencil_poll(bContext *C) +{ + /* Only valid if the current active object is grease pencil. */ + Object *obact = CTX_data_active_object(C); + if ((obact == NULL) || (obact->type != OB_GPENCIL)) { + return false; + } + + /* Test we have a window below. */ + return (CTX_wm_window(C) != NULL); +} + +void UI_OT_eyedropper_gpencil_color(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Grease Pencil Eyedropper"; + ot->idname = "UI_OT_eyedropper_gpencil_color"; + ot->description = "Sample a color from the Blender Window and create Grease Pencil material"; + + /* api callbacks */ + ot->invoke = eyedropper_gpencil_invoke; + ot->modal = eyedropper_gpencil_modal; + ot->cancel = eyedropper_gpencil_cancel; + ot->exec = eyedropper_gpencil_exec; + ot->poll = eyedropper_gpencil_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; +} diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index cc13c4004a4..83820c919c8 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -108,6 +108,7 @@ static int ui_do_but_EXIT(bContext *C, const wmEvent *event); static bool ui_but_find_select_in_enum__cmp(const uiBut *but_a, const uiBut *but_b); static void ui_textedit_string_set(uiBut *but, struct uiHandleButtonData *data, const char *str); +static void button_tooltip_timer_reset(bContext *C, uiBut *but); #ifdef USE_KEYNAV_LIMIT static void ui_mouse_motion_keynav_init(struct uiKeyNavLock *keynav, const wmEvent *event); @@ -3258,7 +3259,7 @@ static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data) ui_but_update(but); - WM_cursor_modal_set(win, BC_TEXTEDITCURSOR); + WM_cursor_modal_set(win, WM_CURSOR_TEXT_EDIT); #ifdef WITH_INPUT_IME if (is_num_but == false && BLT_lang_is_ime_supported()) { @@ -3967,8 +3968,11 @@ static bool ui_do_but_extra_operator_icon(bContext *C, uiButExtraOpIcon *op_icon = ui_but_extra_operator_icon_mouse_over_get(but, data, event); if (op_icon) { + ED_region_tag_redraw(data->region); + button_tooltip_timer_reset(C, but); + ui_but_extra_operator_icon_apply(C, but, op_icon); - button_activate_exit(C, but, data, false, false); + /* Note: 'but', 'data' may now be freed, don't access. */ return true; } @@ -4217,9 +4221,8 @@ static int ui_do_but_TEX( } else if (!ui_but_extra_operator_icon_mouse_over_get(but, data, event)) { button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); + return WM_UI_HANDLER_BREAK; } - - return WM_UI_HANDLER_BREAK; } } else if (data->state == BUTTON_STATE_TEXT_EDITING) { @@ -4686,7 +4689,7 @@ static void ui_numedit_set_active(uiBut *but) } else { if (data->changed_cursor == false) { - WM_cursor_modal_set(data->window, CURSOR_X_MOVE); + WM_cursor_modal_set(data->window, WM_CURSOR_X_MOVE); data->changed_cursor = true; } } @@ -7494,6 +7497,7 @@ static void button_activate_init(bContext *C, ARegion *ar, uiBut *but, uiButtonA data = MEM_callocN(sizeof(uiHandleButtonData), "uiHandleButtonData"); data->wm = CTX_wm_manager(C); data->window = CTX_wm_window(C); + BLI_assert(ar != NULL); data->region = ar; #ifdef USE_CONT_MOUSE_CORRECT @@ -7565,7 +7569,7 @@ static void button_activate_init(bContext *C, ARegion *ar, uiBut *but, uiButtonA if (but->type == UI_BTYPE_GRIP) { const bool horizontal = (BLI_rctf_size_x(&but->rect) < BLI_rctf_size_y(&but->rect)); - WM_cursor_modal_set(data->window, horizontal ? CURSOR_X_MOVE : CURSOR_Y_MOVE); + WM_cursor_modal_set(data->window, horizontal ? WM_CURSOR_X_MOVE : WM_CURSOR_Y_MOVE); } else if (but->type == UI_BTYPE_NUM) { ui_numedit_set_active(but); @@ -8006,6 +8010,7 @@ void ui_but_execute_begin(struct bContext *UNUSED(C), *active_back = but->active; data = MEM_callocN(sizeof(uiHandleButtonData), "uiHandleButtonData_Fake"); but->active = data; + BLI_assert(ar != NULL); data->region = ar; } @@ -9817,9 +9822,7 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle if (but && (U.pie_menu_confirm > 0) && (dist >= U.dpi_fac * (U.pie_menu_threshold + U.pie_menu_confirm))) { - if (but) { - return ui_but_pie_menu_apply(C, menu, but, true); - } + return ui_but_pie_menu_apply(C, menu, but, true); } retval = ui_but_pie_menu_apply(C, menu, but, true); diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index b844e237366..1495fb7e716 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -1583,7 +1583,6 @@ static struct { IconTextureDrawCall normal; IconTextureDrawCall border; bool enabled; - float mat[4][4]; } g_icon_draw_cache = {{{{{0}}}}}; void UI_icon_draw_cache_begin(void) diff --git a/source/blender/editors/interface/interface_icons_event.c b/source/blender/editors/interface/interface_icons_event.c index e1ce77b8b61..b7fd953ed63 100644 --- a/source/blender/editors/interface/interface_icons_event.c +++ b/source/blender/editors/interface/interface_icons_event.c @@ -171,12 +171,13 @@ void icon_draw_rect_input(float x, const bool simple_text = false; - if ((event_type >= AKEY) || (ZKEY <= event_type)) { + if ((event_type >= AKEY) && (event_type <= ZKEY)) { char str[2] = {'A' + (event_type - AKEY), '\0'}; icon_draw_rect_input_default_text(&rect, color, margin, str); } - if ((event_type >= F1KEY) || (F12KEY <= event_type)) { - char str[3] = {'F', '1' + (event_type - F1KEY), '\0'}; + else if ((event_type >= F1KEY) && (event_type <= F12KEY)) { + char str[4]; + SNPRINTF(str, "F%d", 1 + (event_type - F1KEY)); icon_draw_rect_input_default_text(&rect, color, margin, str); } else if (event_type == LEFTSHIFTKEY) { diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 5c73b41b778..81979b1fc8f 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -971,6 +971,9 @@ void UI_OT_eyedropper_depth(struct wmOperatorType *ot); /* interface_eyedropper_driver.c */ void UI_OT_eyedropper_driver(struct wmOperatorType *ot); +/* interface_eyedropper_gpencil_color.c */ +void UI_OT_eyedropper_gpencil_color(struct wmOperatorType *ot); + /* interface_util.c */ /** @@ -993,4 +996,7 @@ void ui_rna_collection_search_cb(const struct bContext *C, /* interface_ops.c */ bool ui_jump_to_target_button_poll(struct bContext *C); +/* interface_queries.c */ +void ui_interface_tag_script_reload_queries(void); + #endif /* __INTERFACE_INTERN_H__ */ diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index 6a707b56f36..a6f8ba4560d 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -130,8 +130,8 @@ typedef struct uiItem { } uiItem; enum { - UI_ITEM_FIXED = 1 << 0, - UI_ITEM_MIN = 1 << 1, + UI_ITEM_AUTO_FIXED_SIZE = 1 << 0, + UI_ITEM_FIXED_SIZE = 1 << 1, UI_ITEM_BOX_ITEM = 1 << 2, /* The item is "inside" a box item */ UI_ITEM_PROP_SEP = 1 << 3, @@ -307,7 +307,7 @@ static int ui_text_icon_width(uiLayout *layout, const char *name, int icon, bool return unit_x; /* No icon or name. */ } if (layout->alignment != UI_LAYOUT_ALIGN_EXPAND) { - layout->item.flag |= UI_ITEM_MIN; + layout->item.flag |= UI_ITEM_FIXED_SIZE; } const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; float margin = compact ? 1.25 : 1.50; @@ -3269,7 +3269,7 @@ static void ui_litem_estimate_row(uiLayout *litem) for (item = litem->items.first; item; item = item->next) { ui_item_size(item, &itemw, &itemh); - min_size_flag = min_size_flag && (item->flag & UI_ITEM_MIN); + min_size_flag = min_size_flag && (item->flag & UI_ITEM_FIXED_SIZE); litem->w += itemw; litem->h = MAX2(itemh, litem->h); @@ -3280,7 +3280,7 @@ static void ui_litem_estimate_row(uiLayout *litem) } if (min_size_flag) { - litem->item.flag |= UI_ITEM_MIN; + litem->item.flag |= UI_ITEM_FIXED_SIZE; } } @@ -3326,7 +3326,7 @@ static void ui_litem_layout_row(uiLayout *litem) extra_pixel = 0.0f; for (item = litem->items.first; item; item = item->next) { - if (item->flag & UI_ITEM_FIXED) { + if (item->flag & UI_ITEM_AUTO_FIXED_SIZE) { continue; } @@ -3342,18 +3342,19 @@ static void ui_litem_layout_row(uiLayout *litem) x += neww; - bool min_flag = item->flag & UI_ITEM_MIN; + bool min_flag = item->flag & UI_ITEM_FIXED_SIZE; /* ignore min flag for rows with right or center alignment */ if (item->type != ITEM_BUTTON && ELEM(((uiLayout *)item)->alignment, UI_LAYOUT_ALIGN_RIGHT, UI_LAYOUT_ALIGN_CENTER) && - litem->alignment == UI_LAYOUT_ALIGN_EXPAND && ((uiItem *)litem)->flag & UI_ITEM_MIN) { + litem->alignment == UI_LAYOUT_ALIGN_EXPAND && + ((uiItem *)litem)->flag & UI_ITEM_FIXED_SIZE) { min_flag = false; } if ((neww < minw || min_flag) && w != 0) { /* fixed size */ - item->flag |= UI_ITEM_FIXED; - if (item->type != ITEM_BUTTON && item->flag & UI_ITEM_MIN) { + item->flag |= UI_ITEM_AUTO_FIXED_SIZE; + if (item->type != ITEM_BUTTON && item->flag & UI_ITEM_FIXED_SIZE) { minw = itemw; } fixedw += minw; @@ -3362,7 +3363,7 @@ static void ui_litem_layout_row(uiLayout *litem) } else { /* keep free size */ - item->flag &= ~UI_ITEM_FIXED; + item->flag &= ~UI_ITEM_AUTO_FIXED_SIZE; freew += itemw; } } @@ -3380,9 +3381,9 @@ static void ui_litem_layout_row(uiLayout *litem) ui_item_size(item, &itemw, &itemh); minw = ui_litem_min_width(itemw); - if (item->flag & UI_ITEM_FIXED) { + if (item->flag & UI_ITEM_AUTO_FIXED_SIZE) { /* fixed minimum size items */ - if (item->type != ITEM_BUTTON && item->flag & UI_ITEM_MIN) { + if (item->type != ITEM_BUTTON && item->flag & UI_ITEM_FIXED_SIZE) { minw = itemw; } itemw = ui_item_fit( @@ -3423,7 +3424,7 @@ static void ui_litem_layout_row(uiLayout *litem) uiItem *last_item = litem->items.last; extra_pixel = litem->w - (x - litem->x); if (extra_pixel > 0 && litem->alignment == UI_LAYOUT_ALIGN_EXPAND && last_free_item && - last_item && last_item->flag & UI_ITEM_FIXED) { + last_item && last_item->flag & UI_ITEM_AUTO_FIXED_SIZE) { ui_item_move(last_free_item, 0, extra_pixel); for (item = last_free_item->next; item; item = item->next) { ui_item_move(item, extra_pixel, extra_pixel); @@ -3449,7 +3450,7 @@ static void ui_litem_estimate_column(uiLayout *litem, bool is_box) for (item = litem->items.first; item; item = item->next) { ui_item_size(item, &itemw, &itemh); - min_size_flag = min_size_flag && (item->flag & UI_ITEM_MIN); + min_size_flag = min_size_flag && (item->flag & UI_ITEM_FIXED_SIZE); litem->w = MAX2(litem->w, itemw); litem->h += itemh; @@ -3460,7 +3461,7 @@ static void ui_litem_estimate_column(uiLayout *litem, bool is_box) } if (min_size_flag) { - litem->item.flag |= UI_ITEM_MIN; + litem->item.flag |= UI_ITEM_FIXED_SIZE; } } @@ -4279,7 +4280,7 @@ static void ui_litem_layout_absolute(uiLayout *litem) static void ui_litem_estimate_split(uiLayout *litem) { ui_litem_estimate_row(litem); - litem->item.flag &= ~UI_ITEM_MIN; + litem->item.flag &= ~UI_ITEM_FIXED_SIZE; } static void ui_litem_layout_split(uiLayout *litem) @@ -5099,7 +5100,7 @@ void ui_layout_add_but(uiLayout *layout, uiBut *but) /* XXX uiBut hasn't scaled yet * we can flag the button as not expandable, depending on its size */ if (w <= 2 * UI_UNIT_X && (!but->str || but->str[0] == '\0')) { - bitem->item.flag |= UI_ITEM_MIN; + bitem->item.flag |= UI_ITEM_FIXED_SIZE; } if (layout->child_items_layout) { @@ -5119,6 +5120,21 @@ void ui_layout_add_but(uiLayout *layout, uiBut *but) } } +void uiLayoutSetFixedSize(uiLayout *layout, bool fixed_size) +{ + if (fixed_size) { + layout->item.flag |= UI_ITEM_FIXED_SIZE; + } + else { + layout->item.flag &= ~UI_ITEM_FIXED_SIZE; + } +} + +bool uiLayoutGetFixedSize(uiLayout *layout) +{ + return (layout->item.flag & UI_ITEM_FIXED_SIZE) != 0; +} + void uiLayoutSetOperatorContext(uiLayout *layout, int opcontext) { layout->root->opcontext = opcontext; diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index b3c46dda4c3..7ce4242c697 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -221,7 +221,7 @@ static int copy_as_driver_button_exec(bContext *C, wmOperator *op) static void UI_OT_copy_as_driver_button(wmOperatorType *ot) { /* identifiers */ - ot->name = "Copy As New Driver"; + ot->name = "Copy as New Driver"; ot->idname = "UI_OT_copy_as_driver_button"; ot->description = "Create a new driver with this property as input, and copy it to the " @@ -453,7 +453,7 @@ static int unset_property_button_exec(bContext *C, wmOperator *UNUSED(op)) static void UI_OT_unset_property_button(wmOperatorType *ot) { /* identifiers */ - ot->name = "Unset property"; + ot->name = "Unset Property"; ot->idname = "UI_OT_unset_property_button"; ot->description = "Clear the property and use default or generated value in operators"; @@ -944,7 +944,7 @@ static int copy_to_selected_button_exec(bContext *C, wmOperator *op) static void UI_OT_copy_to_selected_button(wmOperatorType *ot) { /* identifiers */ - ot->name = "Copy To Selected"; + 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"; @@ -1092,7 +1092,7 @@ static int jump_to_target_button_exec(bContext *C, wmOperator *UNUSED(op)) static void UI_OT_jump_to_target_button(wmOperatorType *ot) { /* identifiers */ - ot->name = "Jump To Target"; + ot->name = "Jump to Target"; ot->idname = "UI_OT_jump_to_target_button"; ot->description = "Switch to the target object or bone"; @@ -1751,6 +1751,7 @@ void ED_operatortypes_ui(void) WM_operatortype_append(UI_OT_eyedropper_id); WM_operatortype_append(UI_OT_eyedropper_depth); WM_operatortype_append(UI_OT_eyedropper_driver); + WM_operatortype_append(UI_OT_eyedropper_gpencil_color); } /** diff --git a/source/blender/editors/interface/interface_query.c b/source/blender/editors/interface/interface_query.c index 1c3d99d8bd2..34b1070f8b4 100644 --- a/source/blender/editors/interface/interface_query.c +++ b/source/blender/editors/interface/interface_query.c @@ -137,15 +137,15 @@ bool ui_but_has_array_value(const uiBut *but) PROP_COORDS)); } +static wmOperatorType *g_ot_tool_set_by_id = NULL; bool UI_but_is_tool(const uiBut *but) { /* very evil! */ if (but->optype != NULL) { - static wmOperatorType *ot = NULL; - if (ot == NULL) { - ot = WM_operatortype_find("WM_OT_tool_set_by_id", false); + if (g_ot_tool_set_by_id == NULL) { + g_ot_tool_set_by_id = WM_operatortype_find("WM_OT_tool_set_by_id", false); } - if (but->optype == ot) { + if (but->optype == g_ot_tool_set_by_id) { return true; } } @@ -463,14 +463,33 @@ bool ui_block_is_popup_any(const uiBlock *block) return (ui_block_is_menu(block) || ui_block_is_popover(block) || ui_block_is_pie_menu(block)); } -bool UI_block_is_empty(const uiBlock *block) +static const uiBut *ui_but_next_non_separator(const uiBut *but) { - for (const uiBut *but = block->buttons.first; but; but = but->next) { + for (; but; but = but->next) { if (!ELEM(but->type, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE)) { - return false; + return but; } } - return true; + return NULL; +} + +bool UI_block_is_empty_ex(const uiBlock *block, const bool skip_title) +{ + const uiBut *but = block->buttons.first; + if (skip_title) { + /* Skip the first label, since popups often have a title, + * we may want to consider the block empty in this case. */ + but = ui_but_next_non_separator(but); + if (but && but->type == UI_BTYPE_LABEL) { + but = but->next; + } + } + return (ui_but_next_non_separator(but) == NULL); +} + +bool UI_block_is_empty(const uiBlock *block) +{ + return UI_block_is_empty_ex(block, false); } bool UI_block_can_add_separator(const uiBlock *block) @@ -596,3 +615,14 @@ ARegion *ui_screen_region_find_mouse_over(bScreen *screen, const wmEvent *event) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Manage Internal State + * \{ */ + +void ui_interface_tag_script_reload_queries(void) +{ + g_ot_tool_set_by_id = NULL; +} + +/** \} */ diff --git a/source/blender/editors/interface/interface_region_menu_popup.c b/source/blender/editors/interface/interface_region_menu_popup.c index ab3a86ec9e1..fed3c0b3d11 100644 --- a/source/blender/editors/interface/interface_region_menu_popup.c +++ b/source/blender/editors/interface/interface_region_menu_popup.c @@ -474,7 +474,7 @@ void UI_popup_menu_end(bContext *C, uiPopupMenu *pup) bool UI_popup_menu_end_or_cancel(bContext *C, uiPopupMenu *pup) { - if (!UI_block_is_empty(pup->block)) { + if (!UI_block_is_empty_ex(pup->block, true)) { UI_popup_menu_end(C, pup); return true; } diff --git a/source/blender/editors/interface/interface_region_popover.c b/source/blender/editors/interface/interface_region_popover.c index 028d99ac052..cd0421dde09 100644 --- a/source/blender/editors/interface/interface_region_popover.c +++ b/source/blender/editors/interface/interface_region_popover.c @@ -334,7 +334,8 @@ int UI_popover_panel_invoke(bContext *C, const char *idname, bool keep_open, Rep } if (block) { - UI_block_active_only_flagged_buttons(C, CTX_wm_region(C), block); + uiPopupBlockHandle *handle = block->handle; + UI_block_active_only_flagged_buttons(C, handle->region, block); } return OPERATOR_INTERFACE; } diff --git a/source/blender/editors/interface/interface_region_popup.c b/source/blender/editors/interface/interface_region_popup.c index 2073117d51c..63dee77e90e 100644 --- a/source/blender/editors/interface/interface_region_popup.c +++ b/source/blender/editors/interface/interface_region_popup.c @@ -765,7 +765,7 @@ uiPopupBlockHandle *ui_popup_block_create(bContext *C, UI_but_tooltip_timer_remove(C, activebut); } /* standard cursor by default */ - WM_cursor_set(window, CURSOR_STD); + WM_cursor_set(window, WM_CURSOR_DEFAULT); /* create handle */ handle = MEM_callocN(sizeof(uiPopupBlockHandle), "uiPopupBlockHandle"); diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index aac018db24e..fe484676ddd 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -3674,7 +3674,7 @@ void uiTemplateWaveform(uiLayout *layout, PointerRNA *ptr, const char *propname) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Vectorscope Template +/** \name Vector-Scope Template * \{ */ void uiTemplateVectorscope(uiLayout *layout, PointerRNA *ptr, const char *propname) diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 0ee2ed0f338..b3e039292e1 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -4521,7 +4521,7 @@ static int widget_roundbox_set(uiBut *but, rcti *rect) } /* align with open menu */ - if (but->active && (but->type != UI_BTYPE_POPOVER)) { + if (but->active && (but->type != UI_BTYPE_POPOVER) && !ui_but_menu_draw_as_popover(but)) { int direction = ui_but_menu_direction(but); if (direction == UI_DIR_UP) { @@ -4638,9 +4638,6 @@ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rct case UI_BTYPE_SEARCH_MENU: wt = widget_type(UI_WTYPE_NAME); - if (but->block->theme_style == UI_BLOCK_THEME_STYLE_POPUP) { - wt->wcol_theme = &btheme->tui.wcol_menu_back; - } break; case UI_BTYPE_TAB: @@ -4914,7 +4911,10 @@ static void ui_draw_popover_back_impl(const uiWidgetColors *wcol, { /* tsk, this isn't nice. */ const float unit_half = unit_size / 2; - const float cent_x = mval_origin ? mval_origin[0] : BLI_rcti_cent_x(rect); + const float cent_x = mval_origin ? CLAMPIS(mval_origin[0], + rect->xmin + unit_size, + rect->xmax - unit_size) : + BLI_rcti_cent_x(rect); rect->ymax -= unit_half; rect->ymin += unit_half; diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c index bea9af99c2e..c6125478571 100644 --- a/source/blender/editors/interface/resources.c +++ b/source/blender/editors/interface/resources.c @@ -371,7 +371,6 @@ const uchar *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colorid) case TH_OBCENTER_DIA: cp = &ts->obcenter_dia; break; - break; case TH_EDGE: cp = ts->edge; break; @@ -779,6 +778,12 @@ const uchar *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colorid) case TH_PATH_AFTER: cp = ts->path_after; break; + case TH_PATH_KEYFRAME_BEFORE: + cp = ts->path_keyframe_before; + break; + case TH_PATH_KEYFRAME_AFTER: + cp = ts->path_keyframe_after; + break; case TH_CAMERA_PATH: cp = ts->camera_path; break; @@ -835,6 +840,9 @@ const uchar *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colorid) cp = ts->nla_tweakdupli; break; + case TH_NLA_TRACK: + cp = ts->nla_track; + break; case TH_NLA_TRANSITION: cp = ts->nla_transition; break; diff --git a/source/blender/editors/interface/view2d.c b/source/blender/editors/interface/view2d.c index 4bfff5f02cf..b1a060089ee 100644 --- a/source/blender/editors/interface/view2d.c +++ b/source/blender/editors/interface/view2d.c @@ -1263,7 +1263,7 @@ void UI_view2d_view_restore(const bContext *C) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Gridline Drawing +/** \name Grid-Line Drawing * \{ */ /* Draw a constant grid in given 2d-region */ diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c index 032fb7e4cc2..5cf7cb4e7c4 100644 --- a/source/blender/editors/interface/view2d_ops.c +++ b/source/blender/editors/interface/view2d_ops.c @@ -256,13 +256,13 @@ static int view_pan_invoke(bContext *C, wmOperator *op, const wmEvent *event) RNA_int_set(op->ptr, "deltay", 0); if (v2d->keepofs & V2D_LOCKOFS_X) { - WM_cursor_modal_set(window, BC_NS_SCROLLCURSOR); + WM_cursor_modal_set(window, WM_CURSOR_NS_SCROLL); } else if (v2d->keepofs & V2D_LOCKOFS_Y) { - WM_cursor_modal_set(window, BC_EW_SCROLLCURSOR); + WM_cursor_modal_set(window, WM_CURSOR_EW_SCROLL); } else { - WM_cursor_modal_set(window, BC_NSEW_SCROLLCURSOR); + WM_cursor_modal_set(window, WM_CURSOR_NSEW_SCROLL); } /* add temp handler */ @@ -1113,13 +1113,13 @@ static int view_zoomdrag_invoke(bContext *C, wmOperator *op, const wmEvent *even } if (v2d->keepofs & V2D_LOCKOFS_X) { - WM_cursor_modal_set(window, BC_NS_SCROLLCURSOR); + WM_cursor_modal_set(window, WM_CURSOR_NS_SCROLL); } else if (v2d->keepofs & V2D_LOCKOFS_Y) { - WM_cursor_modal_set(window, BC_EW_SCROLLCURSOR); + WM_cursor_modal_set(window, WM_CURSOR_EW_SCROLL); } else { - WM_cursor_modal_set(window, BC_NSEW_SCROLLCURSOR); + WM_cursor_modal_set(window, WM_CURSOR_NSEW_SCROLL); } /* add temp handler */ diff --git a/source/blender/editors/io/io_alembic.c b/source/blender/editors/io/io_alembic.c index aac4da12658..573dfcde88a 100644 --- a/source/blender/editors/io/io_alembic.c +++ b/source/blender/editors/io/io_alembic.c @@ -604,6 +604,9 @@ static void ui_alembic_import_settings(uiLayout *layout, PointerRNA *imfptr) uiItemL(row, IFACE_("Options:"), ICON_NONE); row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "relative_path", 0, NULL, ICON_NONE); + + row = uiLayoutRow(box, false); uiItemR(row, imfptr, "set_frame_range", 0, NULL, ICON_NONE); row = uiLayoutRow(box, false); @@ -691,7 +694,7 @@ void WM_OT_alembic_import(wmOperatorType *ot) FILE_TYPE_FOLDER | FILE_TYPE_ALEMBIC, FILE_BLENDER, FILE_SAVE, - WM_FILESEL_FILEPATH, + WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); diff --git a/source/blender/editors/lattice/editlattice_select.c b/source/blender/editors/lattice/editlattice_select.c index 7e89ed9511f..837c5b1c6bd 100644 --- a/source/blender/editors/lattice/editlattice_select.c +++ b/source/blender/editors/lattice/editlattice_select.c @@ -89,8 +89,9 @@ bool ED_lattice_deselect_all_multi_ex(struct Base **bases, const uint bases_len) bool ED_lattice_deselect_all_multi(struct bContext *C) { + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewContext vc; - ED_view3d_viewcontext_init(C, &vc); + ED_view3d_viewcontext_init(C, &vc, depsgraph); uint bases_len = 0; Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data( vc.view_layer, vc.v3d, &bases_len); @@ -634,11 +635,12 @@ static BPoint *findnearestLattvert(ViewContext *vc, int sel, Base **r_base) bool ED_lattice_select_pick( bContext *C, const int mval[2], bool extend, bool deselect, bool toggle) { + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewContext vc; BPoint *bp = NULL; Base *basact = NULL; - ED_view3d_viewcontext_init(C, &vc); + ED_view3d_viewcontext_init(C, &vc, depsgraph); vc.mval[0] = mval[0]; vc.mval[1] = mval[1]; diff --git a/source/blender/editors/mesh/editmesh_bevel.c b/source/blender/editors/mesh/editmesh_bevel.c index b4ef2620895..7afd72f33c9 100644 --- a/source/blender/editors/mesh/editmesh_bevel.c +++ b/source/blender/editors/mesh/editmesh_bevel.c @@ -148,9 +148,10 @@ static void edbm_bevel_update_header(bContext *C, wmOperator *op) BLI_snprintf(offset_str, NUM_STR_REP_LEN, "%.1f%%", RNA_float_get(op->ptr, "offset_pct")); } else { + double offset_val = (double)RNA_float_get(op->ptr, "offset"); bUnit_AsString2(offset_str, NUM_STR_REP_LEN, - (double)RNA_float_get(op->ptr, "offset"), + offset_val * sce->unit.scale_length, 3, B_UNIT_LENGTH, &sce->unit, diff --git a/source/blender/editors/mesh/editmesh_bisect.c b/source/blender/editors/mesh/editmesh_bisect.c index 283e147b77b..4a511bbb5a2 100644 --- a/source/blender/editors/mesh/editmesh_bisect.c +++ b/source/blender/editors/mesh/editmesh_bisect.c @@ -455,7 +455,7 @@ void MESH_OT_bisect(struct wmOperatorType *ot) 0.00001, 0.1); - WM_operator_properties_gesture_straightline(ot, CURSOR_EDIT); + WM_operator_properties_gesture_straightline(ot, WM_CURSOR_EDIT); #ifdef USE_GIZMO WM_gizmogrouptype_append(MESH_GGT_bisect); diff --git a/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c b/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c index 7155348fed5..993898bddd5 100644 --- a/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c +++ b/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c @@ -522,7 +522,6 @@ typedef struct GizmoGroupData_SpinRedo { PropertyRNA *prop_axis_no; PropertyRNA *prop_angle; - float rotate_axis[3]; #ifdef USE_ANGLE_Z_ORIENT /* Apply 'orient_mat' for the final value. */ float orient_axis_relative[3]; diff --git a/source/blender/editors/mesh/editmesh_intersect.c b/source/blender/editors/mesh/editmesh_intersect.c index 370cc6a2a6d..ec740447f93 100644 --- a/source/blender/editors/mesh/editmesh_intersect.c +++ b/source/blender/editors/mesh/editmesh_intersect.c @@ -101,17 +101,19 @@ static int bm_face_isect_pair_swap(BMFace *f, void *UNUSED(user_data)) /** * Use for intersect and boolean. */ -static void edbm_intersect_select(BMEditMesh *em) +static void edbm_intersect_select(BMEditMesh *em, bool do_select) { - BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false); + if (do_select) { + BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false); - if (em->bm->selectmode & (SCE_SELECT_VERTEX | SCE_SELECT_EDGE)) { - BMIter iter; - BMEdge *e; + if (em->bm->selectmode & (SCE_SELECT_VERTEX | SCE_SELECT_EDGE)) { + BMIter iter; + BMEdge *e; - BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) { - if (BM_elem_flag_test(e, BM_ELEM_TAG)) { - BM_edge_select_set(em->bm, e, true); + BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) { + if (BM_elem_flag_test(e, BM_ELEM_TAG)) { + BM_edge_select_set(em->bm, e, true); + } } } } @@ -210,10 +212,9 @@ static int edbm_intersect_exec(bContext *C, wmOperator *op) em->bm, BM_elem_cb_check_hflag_enabled_simple(const BMFace *, BM_ELEM_SELECT)); } - if (has_isect) { - edbm_intersect_select(em); - } - else { + edbm_intersect_select(em, has_isect); + + if (!has_isect) { isect_len++; } } @@ -317,10 +318,9 @@ static int edbm_intersect_boolean_exec(bContext *C, wmOperator *op) boolean_operation, eps); - if (has_isect) { - edbm_intersect_select(em); - } - else { + edbm_intersect_select(em, has_isect); + + if (!has_isect) { isect_len++; } } diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index 61f9dc43c0f..395c614f328 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -2777,7 +2777,7 @@ static int knifetool_invoke(bContext *C, wmOperator *op, const wmEvent *event) op->flag |= OP_IS_MODAL_CURSOR_REGION; /* add a modal handler for this operator - handles loop selection */ - WM_cursor_modal_set(CTX_wm_window(C), BC_KNIFECURSOR); + WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_KNIFE); WM_event_add_modal_handler(C, op); knifetool_update_mval_i(kcd, event->mval); @@ -2804,8 +2804,8 @@ wmKeyMap *knifetool_modal_keymap(wmKeyConfig *keyconf) static const EnumPropertyItem modal_items[] = { {KNF_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""}, {KNF_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""}, - {KNF_MODAL_MIDPOINT_ON, "SNAP_MIDPOINTS_ON", 0, "Snap To Midpoints On", ""}, - {KNF_MODAL_MIDPOINT_OFF, "SNAP_MIDPOINTS_OFF", 0, "Snap To Midpoints Off", ""}, + {KNF_MODAL_MIDPOINT_ON, "SNAP_MIDPOINTS_ON", 0, "Snap to Midpoints On", ""}, + {KNF_MODAL_MIDPOINT_OFF, "SNAP_MIDPOINTS_OFF", 0, "Snap to Midpoints Off", ""}, {KNF_MODEL_IGNORE_SNAP_ON, "IGNORE_SNAP_ON", 0, "Ignore Snapping On", ""}, {KNF_MODEL_IGNORE_SNAP_OFF, "IGNORE_SNAP_OFF", 0, "Ignore Snapping Off", ""}, {KNF_MODAL_ANGLE_SNAP_TOGGLE, "ANGLE_SNAP_TOGGLE", 0, "Toggle Angle Snapping", ""}, diff --git a/source/blender/editors/mesh/editmesh_knife_project.c b/source/blender/editors/mesh/editmesh_knife_project.c index 3d34a4ad3b5..a709bd010aa 100644 --- a/source/blender/editors/mesh/editmesh_knife_project.c +++ b/source/blender/editors/mesh/editmesh_knife_project.c @@ -22,6 +22,7 @@ */ #include "DNA_curve_types.h" +#include "DNA_mesh_types.h" #include "DNA_object_types.h" #include "BLI_math.h" @@ -142,10 +143,21 @@ static int knifeproject_exec(bContext *C, wmOperator *op) /* select only tagged faces */ BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false); - /* not essential, but switch out of vertex mode since the - * selected regions wont be nicely isolated after flushing. - * note: call after de-select to avoid selection flushing */ - EDBM_selectmode_disable(scene, em, SCE_SELECT_VERTEX, SCE_SELECT_EDGE); + CTX_DATA_BEGIN (C, Object *, ob, selected_objects) { + if (ob->type == OB_MESH) { + Mesh *me = (Mesh *)ob->data; + BMEditMesh *embm = me->edit_mesh; + if (embm) { + /* not essential, but switch out of vertex mode since the + * selected regions wont be nicely isolated after flushing. + * note: call after de-select to avoid selection flushing. + * note: do this on all participating meshes so this is in sync + * e.g. for later selection picking, see T68852.*/ + EDBM_selectmode_disable(scene, embm, SCE_SELECT_VERTEX, SCE_SELECT_EDGE); + } + } + } + CTX_DATA_END; BM_mesh_elem_hflag_enable_test(em->bm, BM_FACE, BM_ELEM_SELECT, true, false, BM_ELEM_TAG); diff --git a/source/blender/editors/mesh/editmesh_loopcut.c b/source/blender/editors/mesh/editmesh_loopcut.c index 3be94cf99c1..3c3e91e8afe 100644 --- a/source/blender/editors/mesh/editmesh_loopcut.c +++ b/source/blender/editors/mesh/editmesh_loopcut.c @@ -386,7 +386,6 @@ static int loopcut_init(bContext *C, wmOperator *op, const wmEvent *event) bool ok = true; if (is_interactive == false) { if (exec_data.base_index >= bases_len) { - return OPERATOR_CANCELLED; ok = false; } else { diff --git a/source/blender/editors/mesh/editmesh_mask_extract.c b/source/blender/editors/mesh/editmesh_mask_extract.c index 6d51e1d3393..8d98a3bf231 100644 --- a/source/blender/editors/mesh/editmesh_mask_extract.c +++ b/source/blender/editors/mesh/editmesh_mask_extract.c @@ -101,16 +101,21 @@ static int paint_mask_extract_exec(bContext *C, wmOperator *op) BMIter face_iter; /* Delete all unmasked faces */ + const int cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK); + BLI_assert(cd_vert_mask_offset != -1); BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false); float mask_threshold = RNA_float_get(op->ptr, "mask_threshold"); BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - bool delete_face = false; + bool keep_face = true; BM_ITER_ELEM (v, &face_iter, f, BM_VERTS_OF_FACE) { - float mask = BM_elem_float_data_get(&bm->vdata, v, CD_PAINT_MASK); - delete_face = mask < mask_threshold; + const float mask = BM_ELEM_CD_GET_FLOAT(v, cd_vert_mask_offset); + if (mask < mask_threshold) { + keep_face = false; + break; + } } - BM_elem_flag_set(f, BM_ELEM_TAG, delete_face); + BM_elem_flag_set(f, BM_ELEM_TAG, !keep_face); } BM_mesh_delete_hflag_context(bm, BM_ELEM_TAG, DEL_FACES); @@ -173,15 +178,16 @@ static int paint_mask_extract_exec(bContext *C, wmOperator *op) } BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false); - BKE_editmesh_free_derivedmesh(em); BKE_mesh_free(new_mesh); new_mesh = BKE_mesh_from_bmesh_nomain(bm, (&(struct BMeshToMeshParams){ .calc_object_remap = false, - })); + }), + mesh); - BM_mesh_free(bm); + BKE_editmesh_free(em); + MEM_freeN(em); if (new_mesh->totvert == 0) { BKE_mesh_free(new_mesh); @@ -195,8 +201,6 @@ static int paint_mask_extract_exec(bContext *C, wmOperator *op) Object *new_ob = ED_object_add_type(C, OB_MESH, NULL, ob->loc, ob->rot, false, local_view_bits); BKE_mesh_nomain_to_mesh(new_mesh, new_ob->data, new_ob, &CD_MASK_EVERYTHING, true); - BKE_mesh_free(new_mesh); - if (RNA_boolean_get(op->ptr, "apply_shrinkwrap")) { BKE_shrinkwrap_mesh_nearest_surface_deform(C, new_ob, ob); } @@ -211,6 +215,8 @@ static int paint_mask_extract_exec(bContext *C, wmOperator *op) } } + BKE_mesh_calc_normals(new_ob->data); + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, new_ob); BKE_mesh_batch_cache_dirty_tag(new_ob->data, BKE_MESH_BATCH_DIRTY_ALL); DEG_relations_tag_update(bmain); diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index cad9e9a3d06..3e59a884696 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -2642,8 +2642,9 @@ bool EDBM_mesh_deselect_all_multi_ex(struct Base **bases, const uint bases_len) bool EDBM_mesh_deselect_all_multi(struct bContext *C) { + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewContext vc; - ED_view3d_viewcontext_init(C, &vc); + ED_view3d_viewcontext_init(C, &vc, depsgraph); uint bases_len = 0; Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data( vc.view_layer, vc.v3d, &bases_len); @@ -4219,7 +4220,8 @@ void MESH_OT_select_nth(wmOperatorType *ot) void em_setup_viewcontext(bContext *C, ViewContext *vc) { - ED_view3d_viewcontext_init(C, vc); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + ED_view3d_viewcontext_init(C, vc, depsgraph); if (vc->obedit) { vc->em = BKE_editmesh_from_object(vc->obedit); diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 0c4db012786..a20ae5fc1ac 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -3161,7 +3161,7 @@ static int edbm_remove_doubles_exec(bContext *C, wmOperator *op) } MEM_freeN(objects); - BKE_reportf(op->reports, RPT_INFO, "Removed %d vertices", count_multi); + BKE_reportf(op->reports, RPT_INFO, "Removed %d vertice(s)", count_multi); return OPERATOR_FINISHED; } @@ -3872,7 +3872,7 @@ void MESH_OT_knife_cut(wmOperatorType *ot) /* internal */ RNA_def_int( - ot->srna, "cursor", BC_KNIFECURSOR, 0, BC_NUMCURSORS, "Cursor", "", 0, BC_NUMCURSORS); + ot->srna, "cursor", WM_CURSOR_KNIFE, 0, WM_CURSOR_NUM, "Cursor", "", 0, WM_CURSOR_NUM); } /** \} */ @@ -3946,6 +3946,61 @@ static Base *mesh_separate_tagged( return base_new; } +static Base *mesh_separate_arrays(Main *bmain, + Scene *scene, + ViewLayer *view_layer, + Base *base_old, + BMesh *bm_old, + BMVert **verts, + uint verts_len, + BMEdge **edges, + uint edges_len, + BMFace **faces, + uint faces_len) +{ + Base *base_new; + Object *obedit = base_old->object; + BMesh *bm_new; + + bm_new = BM_mesh_create(&bm_mesh_allocsize_default, + &((struct BMeshCreateParams){ + .use_toolflags = true, + })); + + CustomData_copy(&bm_old->vdata, &bm_new->vdata, CD_MASK_BMESH.vmask, CD_CALLOC, 0); + CustomData_copy(&bm_old->edata, &bm_new->edata, CD_MASK_BMESH.emask, CD_CALLOC, 0); + CustomData_copy(&bm_old->ldata, &bm_new->ldata, CD_MASK_BMESH.lmask, CD_CALLOC, 0); + CustomData_copy(&bm_old->pdata, &bm_new->pdata, CD_MASK_BMESH.pmask, CD_CALLOC, 0); + + CustomData_bmesh_init_pool(&bm_new->vdata, verts_len, BM_VERT); + CustomData_bmesh_init_pool(&bm_new->edata, edges_len, BM_EDGE); + CustomData_bmesh_init_pool(&bm_new->ldata, faces_len * 3, BM_LOOP); + CustomData_bmesh_init_pool(&bm_new->pdata, faces_len, BM_FACE); + + base_new = ED_object_add_duplicate(bmain, scene, view_layer, base_old, USER_DUP_MESH); + + /* normally would call directly after but in this case delay recalc */ + /* DAG_relations_tag_update(bmain); */ + + /* new in 2.5 */ + assign_matarar(bmain, base_new->object, give_matarar(obedit), *give_totcolp(obedit)); + + ED_object_base_select(base_new, BA_SELECT); + + BM_mesh_copy_arrays(bm_old, bm_new, verts, verts_len, edges, edges_len, faces, faces_len); + + for (uint i = 0; i < verts_len; i++) { + BM_vert_kill(bm_old, verts[i]); + } + + BM_mesh_bm_to_me(bmain, bm_new, base_new->object->data, (&(struct BMeshToMeshParams){0})); + + BM_mesh_free(bm_new); + ((Mesh *)base_new->object->data)->edit_mesh = NULL; + + return base_new; +} + static bool mesh_separate_selected( Main *bmain, Scene *scene, ViewLayer *view_layer, Base *base_old, BMesh *bm_old) { @@ -3959,41 +4014,6 @@ static bool mesh_separate_selected( return (mesh_separate_tagged(bmain, scene, view_layer, base_old, bm_old) != NULL); } -/* flush a hflag to from verts to edges/faces */ -static void bm_mesh_hflag_flush_vert(BMesh *bm, const char hflag) -{ - BMEdge *e; - BMLoop *l_iter; - BMLoop *l_first; - BMFace *f; - - BMIter eiter; - BMIter fiter; - - bool ok; - - BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) { - if (BM_elem_flag_test(e->v1, hflag) && BM_elem_flag_test(e->v2, hflag)) { - BM_elem_flag_enable(e, hflag); - } - else { - BM_elem_flag_disable(e, hflag); - } - } - BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) { - ok = true; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - if (!BM_elem_flag_test(l_iter->v, hflag)) { - ok = false; - break; - } - } while ((l_iter = l_iter->next) != l_first); - - BM_elem_flag_set(f, hflag, ok); - } -} - /** * Sets an object to a single material. from one of its slots. * @@ -4109,72 +4129,64 @@ static bool mesh_separate_material( static bool mesh_separate_loose( Main *bmain, Scene *scene, ViewLayer *view_layer, Base *base_old, BMesh *bm_old) { - int i; - BMEdge *e; - BMVert *v_seed; - BMWalker walker; - bool result = false; - int max_iter = bm_old->totvert; + /* Without this, we duplicate the object mode mesh for each loose part. + * This can get very slow especially for large meshes with many parts + * which would duplicate the mesh on entering edit-mode. */ + const bool clear_object_data = true; - /* Clear all selected vertices */ - BM_mesh_elem_hflag_disable_all(bm_old, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false); + bool result = false; - /* A "while (true)" loop should work here as each iteration should - * select and remove at least one vertex and when all vertices - * are selected the loop will break out. But guard against bad - * behavior by limiting iterations to the number of vertices in the - * original mesh.*/ - for (i = 0; i < max_iter; i++) { - int tot = 0; - /* Get a seed vertex to start the walk */ - v_seed = BM_iter_at_index(bm_old, BM_VERTS_OF_MESH, NULL, 0); + BMVert **vert_groups = MEM_mallocN(sizeof(*vert_groups) * bm_old->totvert, __func__); + BMEdge **edge_groups = MEM_mallocN(sizeof(*edge_groups) * bm_old->totedge, __func__); + BMFace **face_groups = MEM_mallocN(sizeof(*face_groups) * bm_old->totface, __func__); + + int(*groups)[3] = NULL; + int groups_len = BM_mesh_calc_edge_groups_as_arrays( + bm_old, vert_groups, edge_groups, face_groups, &groups); + if (groups_len <= 1) { + goto finally; + } + + if (clear_object_data) { + ED_mesh_geometry_clear(base_old->object->data); + } + + /* Separate out all groups except the first. */ + uint group_ofs[3] = {UNPACK3(groups[0])}; + for (int i = 1; i < groups_len; i++) { + Base *base_new = mesh_separate_arrays(bmain, + scene, + view_layer, + base_old, + bm_old, + vert_groups + group_ofs[0], + groups[i][0], + edge_groups + group_ofs[1], + groups[i][1], + face_groups + group_ofs[2], + groups[i][2]); + result |= (base_new != NULL); - /* No vertices available, can't do anything */ - if (v_seed == NULL) { - break; - } + group_ofs[0] += groups[i][0]; + group_ofs[1] += groups[i][1]; + group_ofs[2] += groups[i][2]; + } - /* Select the seed explicitly, in case it has no edges */ - if (!BM_elem_flag_test(v_seed, BM_ELEM_TAG)) { - BM_elem_flag_enable(v_seed, BM_ELEM_TAG); - tot++; - } + Mesh *me_old = base_old->object->data; + BMEditMesh *em_old = me_old->edit_mesh; - /* Walk from the single vertex, selecting everything connected - * to it */ - BMW_init(&walker, - bm_old, - BMW_VERT_SHELL, - BMW_MASK_NOP, - BMW_MASK_NOP, - BMW_MASK_NOP, - BMW_FLAG_NOP, - BMW_NIL_LAY); + BM_mesh_elem_hflag_disable_all(em_old->bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false); - for (e = BMW_begin(&walker, v_seed); e; e = BMW_step(&walker)) { - if (!BM_elem_flag_test(e->v1, BM_ELEM_TAG)) { - BM_elem_flag_enable(e->v1, BM_ELEM_TAG); - tot++; - } - if (!BM_elem_flag_test(e->v2, BM_ELEM_TAG)) { - BM_elem_flag_enable(e->v2, BM_ELEM_TAG); - tot++; - } - } - BMW_end(&walker); - - if (bm_old->totvert == tot) { - /* Every vertex selected, nothing to separate, work is done */ - break; - } + if (clear_object_data) { + BM_mesh_bm_to_me(NULL, em_old->bm, me_old, (&(struct BMeshToMeshParams){0})); + } - /* Flush the selection to get edge/face selections matching - * the vertex selection */ - bm_mesh_hflag_flush_vert(bm_old, BM_ELEM_TAG); +finally: + MEM_freeN(vert_groups); + MEM_freeN(edge_groups); + MEM_freeN(face_groups); - /* Move selection into a separate object */ - result |= (mesh_separate_tagged(bmain, scene, view_layer, base_old, bm_old) != NULL); - } + MEM_freeN(groups); return result; } diff --git a/source/blender/editors/mesh/mesh_data.c b/source/blender/editors/mesh/mesh_data.c index 569994bead1..7007ff29401 100644 --- a/source/blender/editors/mesh/mesh_data.c +++ b/source/blender/editors/mesh/mesh_data.c @@ -880,14 +880,14 @@ void MESH_OT_customdata_custom_splitnormals_clear(wmOperatorType *ot) void ED_mesh_update(Mesh *mesh, bContext *C, bool calc_edges, bool calc_edges_loose) { - if (calc_edges_loose && mesh->totedge) { - BKE_mesh_calc_edges_loose(mesh); - } - if (calc_edges || ((mesh->totpoly || mesh->totface) && mesh->totedge == 0)) { BKE_mesh_calc_edges(mesh, calc_edges, true); } + if (calc_edges_loose && mesh->totedge) { + BKE_mesh_calc_edges_loose(mesh); + } + /* Default state is not to have tessface's so make sure this is the case. */ BKE_mesh_tessface_clear(mesh); @@ -1023,73 +1023,104 @@ static void mesh_add_polys(Mesh *mesh, int len) mesh->totpoly = totpoly; } -static void mesh_remove_verts(Mesh *mesh, int len) +/* -------------------------------------------------------------------- */ +/** \name Add Geometry + * \{ */ + +void ED_mesh_verts_add(Mesh *mesh, ReportList *reports, int count) { - int totvert; + if (mesh->edit_mesh) { + BKE_report(reports, RPT_ERROR, "Cannot add vertices in edit mode"); + return; + } + mesh_add_verts(mesh, count); +} - if (len == 0) { +void ED_mesh_edges_add(Mesh *mesh, ReportList *reports, int count) +{ + if (mesh->edit_mesh) { + BKE_report(reports, RPT_ERROR, "Cannot add edges in edit mode"); + return; + } + mesh_add_edges(mesh, count); +} + +void ED_mesh_loops_add(Mesh *mesh, ReportList *reports, int count) +{ + if (mesh->edit_mesh) { + BKE_report(reports, RPT_ERROR, "Cannot add loops in edit mode"); return; } + mesh_add_loops(mesh, count); +} - totvert = mesh->totvert - len; - CustomData_free_elem(&mesh->vdata, totvert, len); +void ED_mesh_polys_add(Mesh *mesh, ReportList *reports, int count) +{ + if (mesh->edit_mesh) { + BKE_report(reports, RPT_ERROR, "Cannot add polygons in edit mode"); + return; + } + mesh_add_polys(mesh, count); +} - /* set final vertex list size */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Remove Geometry + * \{ */ + +static void mesh_remove_verts(Mesh *mesh, int len) +{ + if (len == 0) { + return; + } + const int totvert = mesh->totvert - len; + CustomData_free_elem(&mesh->vdata, totvert, len); mesh->totvert = totvert; } static void mesh_remove_edges(Mesh *mesh, int len) { - int totedge; - if (len == 0) { return; } - - totedge = mesh->totedge - len; + const int totedge = mesh->totedge - len; CustomData_free_elem(&mesh->edata, totedge, len); - mesh->totedge = totedge; } -#if 0 -void ED_mesh_geometry_add(Mesh *mesh, ReportList *reports, int verts, int edges, int faces) +static void mesh_remove_loops(Mesh *mesh, int len) { - if (mesh->edit_mesh) { - BKE_report(reports, RPT_ERROR, "Cannot add geometry in edit mode"); + if (len == 0) { return; } - - if (verts) { - mesh_add_verts(mesh, verts); - } - if (edges) { - mesh_add_edges(mesh, edges); - } - if (faces) { - mesh_add_faces(mesh, faces); - } + const int totloop = mesh->totloop - len; + CustomData_free_elem(&mesh->ldata, totloop, len); + mesh->totloop = totloop; } -#endif -void ED_mesh_edges_add(Mesh *mesh, ReportList *reports, int count) +static void mesh_remove_polys(Mesh *mesh, int len) { - if (mesh->edit_mesh) { - BKE_report(reports, RPT_ERROR, "Cannot add edges in edit mode"); + if (len == 0) { return; } - - mesh_add_edges(mesh, count); + const int totpoly = mesh->totpoly - len; + CustomData_free_elem(&mesh->pdata, totpoly, len); + mesh->totpoly = totpoly; } -void ED_mesh_vertices_add(Mesh *mesh, ReportList *reports, int count) +void ED_mesh_verts_remove(Mesh *mesh, ReportList *reports, int count) { if (mesh->edit_mesh) { - BKE_report(reports, RPT_ERROR, "Cannot add vertices in edit mode"); + BKE_report(reports, RPT_ERROR, "Cannot remove vertices in edit mode"); + return; + } + else if (count > mesh->totvert) { + BKE_report(reports, RPT_ERROR, "Cannot remove more vertices than the mesh contains"); return; } - mesh_add_verts(mesh, count); + mesh_remove_verts(mesh, count); } void ED_mesh_edges_remove(Mesh *mesh, ReportList *reports, int count) @@ -1106,40 +1137,44 @@ void ED_mesh_edges_remove(Mesh *mesh, ReportList *reports, int count) mesh_remove_edges(mesh, count); } -void ED_mesh_vertices_remove(Mesh *mesh, ReportList *reports, int count) +void ED_mesh_loops_remove(Mesh *mesh, ReportList *reports, int count) { if (mesh->edit_mesh) { - BKE_report(reports, RPT_ERROR, "Cannot remove vertices in edit mode"); + BKE_report(reports, RPT_ERROR, "Cannot remove loops in edit mode"); return; } - else if (count > mesh->totvert) { - BKE_report(reports, RPT_ERROR, "Cannot remove more vertices than the mesh contains"); + else if (count > mesh->totloop) { + BKE_report(reports, RPT_ERROR, "Cannot remove more loops than the mesh contains"); return; } - mesh_remove_verts(mesh, count); + mesh_remove_loops(mesh, count); } -void ED_mesh_loops_add(Mesh *mesh, ReportList *reports, int count) +void ED_mesh_polys_remove(Mesh *mesh, ReportList *reports, int count) { if (mesh->edit_mesh) { - BKE_report(reports, RPT_ERROR, "Cannot add loops in edit mode"); + BKE_report(reports, RPT_ERROR, "Cannot remove polys in edit mode"); + return; + } + else if (count > mesh->totpoly) { + BKE_report(reports, RPT_ERROR, "Cannot remove more polys than the mesh contains"); return; } - mesh_add_loops(mesh, count); + mesh_remove_polys(mesh, count); } -void ED_mesh_polys_add(Mesh *mesh, ReportList *reports, int count) +void ED_mesh_geometry_clear(Mesh *mesh) { - if (mesh->edit_mesh) { - BKE_report(reports, RPT_ERROR, "Cannot add polygons in edit mode"); - return; - } - - mesh_add_polys(mesh, count); + mesh_remove_verts(mesh, mesh->totvert); + mesh_remove_edges(mesh, mesh->totedge); + mesh_remove_loops(mesh, mesh->totloop); + mesh_remove_polys(mesh, mesh->totpoly); } +/** \} */ + void ED_mesh_report_mirror_ex(wmOperator *op, int totmirr, int totfail, char selectmode) { const char *elem_type; diff --git a/source/blender/editors/mesh/meshtools.c b/source/blender/editors/mesh/meshtools.c index c68f5963cbd..a918996563f 100644 --- a/source/blender/editors/mesh/meshtools.c +++ b/source/blender/editors/mesh/meshtools.c @@ -54,6 +54,7 @@ #include "BKE_multires.h" #include "BKE_object.h" #include "BKE_object_deform.h" +#include "BKE_object_facemap.h" #include "BKE_report.h" #include "DEG_depsgraph.h" @@ -267,6 +268,22 @@ static void join_mesh_single(Depsgraph *depsgraph, mpoly->loopstart += *loopofs; mpoly->mat_nr = matmap ? matmap[mpoly->mat_nr] : 0; } + + /* Face maps. */ + int *fmap = CustomData_get(pdata, *polyofs, CD_FACEMAP); + int *fmap_src = CustomData_get(&me->pdata, 0, CD_FACEMAP); + + /* Remap to correct new face-map indices, if needed. */ + if (fmap_src) { + BLI_assert(fmap != NULL); + 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) { + MEM_freeN(fmap_index_map); + } + } } /* these are used for relinking (cannot be set earlier, or else reattaching goes wrong) */ @@ -403,7 +420,7 @@ int join_mesh_exec(bContext *C, wmOperator *op) key->type = KEY_RELATIVE; } - /* first pass over objects - copying materials and vertexgroups across */ + /* First pass over objects: Copying materials, vertex-groups & face-maps across. */ 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)) { @@ -422,6 +439,19 @@ int join_mesh_exec(bContext *C, wmOperator *op) ob->actdef = 1; } + /* Join this object's face maps to the base one's. */ + for (bFaceMap *fmap = ob_iter->fmaps.first; fmap; fmap = fmap->next) { + /* 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_callocN(sizeof(bFaceMap), "join faceMap"); + memcpy(fmap_new, fmap, sizeof(bFaceMap)); + BLI_addtail(&ob->fmaps, fmap_new); + } + } + if (ob->fmaps.first && ob->actfmap == 0) { + ob->actfmap = 1; + } + if (me->totvert) { /* Add this object's materials to the base one's if they don't exist already * (but only if limits not exceeded yet) */ @@ -1110,7 +1140,8 @@ bool ED_mesh_pick_face(bContext *C, Object *ob, const int mval[2], uint dist_px, return false; } - ED_view3d_viewcontext_init(C, &vc); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + ED_view3d_viewcontext_init(C, &vc, depsgraph); ED_view3d_select_id_validate(&vc); if (dist_px) { @@ -1291,7 +1322,8 @@ bool ED_mesh_pick_vert( return false; } - ED_view3d_viewcontext_init(C, &vc); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + ED_view3d_viewcontext_init(C, &vc, depsgraph); ED_view3d_select_id_validate(&vc); if (use_zbuf) { diff --git a/source/blender/editors/metaball/mball_edit.c b/source/blender/editors/metaball/mball_edit.c index 18ff7ae1a5e..64ae75a0ee8 100644 --- a/source/blender/editors/metaball/mball_edit.c +++ b/source/blender/editors/metaball/mball_edit.c @@ -693,13 +693,14 @@ void MBALL_OT_reveal_metaelems(wmOperatorType *ot) * stiffness circle) */ bool ED_mball_select_pick(bContext *C, const int mval[2], bool extend, bool deselect, bool toggle) { + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); static MetaElem *startelem = NULL; ViewContext vc; int a, hits; unsigned int buffer[MAXPICKBUF]; rcti rect; - ED_view3d_viewcontext_init(C, &vc); + ED_view3d_viewcontext_init(C, &vc, depsgraph); BLI_rcti_init_pt_radius(&rect, mval, 12); @@ -835,8 +836,9 @@ bool ED_mball_select_pick(bContext *C, const int mval[2], bool extend, bool dese bool ED_mball_deselect_all_multi(bContext *C) { + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewContext vc; - ED_view3d_viewcontext_init(C, &vc); + ED_view3d_viewcontext_init(C, &vc, depsgraph); uint bases_len = 0; Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data( vc.view_layer, vc.v3d, &bases_len); diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 3d5ec3d4ed5..716ff94bbae 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -1030,7 +1030,7 @@ static int empty_drop_named_image_invoke(bContext *C, wmOperator *op, const wmEv return OPERATOR_CANCELLED; } /* handled below */ - id_us_min((ID *)ima); + id_us_min(&ima->id); Object *ob = NULL; Object *ob_cursor = ED_view3d_give_object_under_cursor(C, event->mval); @@ -1071,7 +1071,7 @@ void OBJECT_OT_drop_named_image(wmOperatorType *ot) PropertyRNA *prop; /* identifiers */ - ot->name = "Add Empty Image/Drop Image To Empty"; + ot->name = "Add Empty Image/Drop Image to Empty"; ot->description = "Add an empty image type to scene with data"; ot->idname = "OBJECT_OT_drop_named_image"; @@ -2147,6 +2147,7 @@ static int convert_exec(bContext *C, wmOperator *op) const bool gpencil_lines = RNA_boolean_get(op->ptr, "gpencil_lines"); const bool use_collections = RNA_boolean_get(op->ptr, "use_collections"); int a, mballConverted = 0; + bool gpencilConverted = false; /* don't forget multiple users! */ @@ -2387,20 +2388,20 @@ static int convert_exec(bContext *C, wmOperator *op) } else if (target == OB_GPENCIL) { if (ob->type != OB_CURVE) { + ob->flag &= ~OB_DONE; BKE_report( op->reports, RPT_ERROR, "Convert Surfaces to Grease Pencil is not supported."); } else { - /* Create a new grease pencil object only if it was not created before. - * All curves selected are converted as strokes of the same grease pencil object. + /* Create a new grease pencil object and copy transformations. * Nurbs Surface are not supported. */ - if (gpencil_ob == NULL) { - const float *cur = scene->cursor.location; - ushort local_view_bits = (v3d && v3d->localvd) ? v3d->local_view_uuid : 0; - gpencil_ob = ED_gpencil_add_object(C, scene, cur, local_view_bits); - } + ushort local_view_bits = (v3d && v3d->localvd) ? v3d->local_view_uuid : 0; + gpencil_ob = ED_gpencil_add_object(C, scene, ob->loc, local_view_bits); + copy_v3_v3(gpencil_ob->rot, ob->rot); + copy_v3_v3(gpencil_ob->scale, ob->scale); BKE_gpencil_convert_curve(bmain, scene, gpencil_ob, ob, false, false, true); + gpencilConverted = true; } } } @@ -2500,6 +2501,17 @@ static int convert_exec(bContext *C, wmOperator *op) } FOREACH_SCENE_OBJECT_END; } + /* Remove curves converted to Grease Pencil object. */ + if (gpencilConverted) { + FOREACH_SCENE_OBJECT_BEGIN (scene, ob_curve) { + if (ob_curve->type == OB_CURVE) { + if (ob_curve->flag & OB_DONE) { + ED_object_base_free_and_unlink(bmain, scene, ob_curve); + } + } + } + FOREACH_SCENE_OBJECT_END; + } } // XXX ED_object_editmode_enter(C, 0); @@ -2585,7 +2597,7 @@ static Base *object_add_duplicate_internal( DEG_id_tag_update(&obn->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); base = BKE_view_layer_base_find(view_layer, ob); - if ((base != NULL) && (base->flag & BASE_VISIBLE)) { + if ((base != NULL) && (base->flag & BASE_VISIBLE_DEPSGRAPH)) { BKE_collection_object_add_from(bmain, scene, ob, obn); } else { diff --git a/source/blender/editors/object/object_bake.c b/source/blender/editors/object/object_bake.c index 70a9870e6ae..bc79521ee9b 100644 --- a/source/blender/editors/object/object_bake.c +++ b/source/blender/editors/object/object_bake.c @@ -371,7 +371,7 @@ static int multiresbake_image_exec_locked(bContext *C, wmOperator *op) ob = base->object; - multires_force_update(ob); + multires_flush_sculpt_updates(ob); /* copy data stored in job descriptor */ bkr.scene = scene; @@ -435,7 +435,7 @@ static void init_multiresbake_job(bContext *C, MultiresBakeJob *bkj) ob = base->object; - multires_force_update(ob); + multires_flush_sculpt_updates(ob); data = MEM_callocN(sizeof(MultiresBakerJobData), "multiresBaker derivedMesh_data"); diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index d9baec7c3ca..9e9cfe1beed 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -879,7 +879,7 @@ static int bake(Render *re, else { ob_cage_eval = DEG_get_evaluated_object(depsgraph, ob_cage); ob_cage_eval->restrictflag |= OB_RESTRICT_RENDER; - ob_cage_eval->base_flag &= ~(BASE_VISIBLE | BASE_ENABLED_RENDER); + ob_cage_eval->base_flag &= ~(BASE_VISIBLE_DEPSGRAPH | BASE_ENABLED_RENDER); } } } @@ -976,7 +976,7 @@ static int bake(Render *re, highpoly[i].ob = ob_iter; highpoly[i].ob_eval = DEG_get_evaluated_object(depsgraph, ob_iter); highpoly[i].ob_eval->restrictflag &= ~OB_RESTRICT_RENDER; - highpoly[i].ob_eval->base_flag |= (BASE_VISIBLE | BASE_ENABLED_RENDER); + highpoly[i].ob_eval->base_flag |= (BASE_VISIBLE_DEPSGRAPH | BASE_ENABLED_RENDER); highpoly[i].me = BKE_mesh_new_from_object(NULL, highpoly[i].ob_eval, false); /* lowpoly to highpoly transformation matrix */ @@ -992,10 +992,10 @@ static int bake(Render *re, if (ob_cage != NULL) { ob_cage_eval->restrictflag |= OB_RESTRICT_RENDER; - ob_cage_eval->base_flag &= ~(BASE_VISIBLE | BASE_ENABLED_RENDER); + ob_cage_eval->base_flag &= ~(BASE_VISIBLE_DEPSGRAPH | BASE_ENABLED_RENDER); } ob_low_eval->restrictflag |= OB_RESTRICT_RENDER; - ob_low_eval->base_flag &= ~(BASE_VISIBLE | BASE_ENABLED_RENDER); + ob_low_eval->base_flag &= ~(BASE_VISIBLE_DEPSGRAPH | BASE_ENABLED_RENDER); /* populate the pixel arrays with the corresponding face data for each high poly object */ if (!RE_bake_pixels_populate_from_objects(me_low, diff --git a/source/blender/editors/object/object_collection.c b/source/blender/editors/object/object_collection.c index fcaefaf220d..a00e5e7b198 100644 --- a/source/blender/editors/object/object_collection.c +++ b/source/blender/editors/object/object_collection.c @@ -186,7 +186,7 @@ void COLLECTION_OT_objects_add_active(wmOperatorType *ot) PropertyRNA *prop; /* identifiers */ - ot->name = "Add Selected To Active Collection"; + ot->name = "Add Selected to Active Collection"; ot->description = "Add the object to an object collection that contains the active object"; ot->idname = "COLLECTION_OT_objects_add_active"; @@ -259,7 +259,7 @@ void COLLECTION_OT_objects_remove_active(wmOperatorType *ot) PropertyRNA *prop; /* identifiers */ - ot->name = "Remove Selected From Active Collection"; + ot->name = "Remove Selected from Active Collection"; ot->description = "Remove the object from an object collection that contains the active object"; ot->idname = "COLLECTION_OT_objects_remove_active"; @@ -302,7 +302,7 @@ static int collection_objects_remove_all_exec(bContext *C, wmOperator *UNUSED(op void COLLECTION_OT_objects_remove_all(wmOperatorType *ot) { /* identifiers */ - ot->name = "Remove From All Unlinked Collections"; + ot->name = "Remove from All Unlinked Collections"; ot->description = "Remove selected objects from all collections not used in a scene"; ot->idname = "COLLECTION_OT_objects_remove_all"; @@ -361,7 +361,7 @@ void COLLECTION_OT_objects_remove(wmOperatorType *ot) PropertyRNA *prop; /* identifiers */ - ot->name = "Remove From Collection"; + ot->name = "Remove from Collection"; ot->description = "Remove selected objects from a collection"; ot->idname = "COLLECTION_OT_objects_remove"; diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index 74abe104134..70d024c7902 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -218,7 +218,7 @@ static int object_hide_view_set_exec(bContext *C, wmOperator *op) /* Hide selected or unselected objects. */ for (Base *base = view_layer->object_bases.first; base; base = base->next) { - if (!(base->flag & BASE_VISIBLE)) { + if (!(base->flag & BASE_VISIBLE_DEPSGRAPH)) { continue; } @@ -292,7 +292,7 @@ static int object_hide_collection_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS); if (v3d->flag & V3D_LOCAL_COLLECTIONS) { - if ((lc->runtime_flag & LAYER_COLLECTION_VISIBLE) == 0) { + if (lc->runtime_flag & LAYER_COLLECTION_RESTRICT_VIEWPORT) { return OPERATOR_CANCELLED; } if (toggle) { @@ -300,11 +300,11 @@ static int object_hide_collection_exec(bContext *C, wmOperator *op) BKE_layer_collection_local_sync(view_layer, v3d); } else { - BKE_layer_collection_local_isolate(view_layer, v3d, lc, extend); + BKE_layer_collection_isolate_local(view_layer, v3d, lc, extend); } } else { - BKE_layer_collection_isolate(scene, view_layer, lc, extend); + BKE_layer_collection_isolate_global(scene, view_layer, lc, extend); } WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); @@ -910,12 +910,25 @@ void OBJECT_OT_forcefield_toggle(wmOperatorType *ot) /* ********************************************** */ /* Motion Paths */ +static eAnimvizCalcRange object_path_convert_range(eObjectPathCalcRange range) +{ + switch (range) { + case OBJECT_PATH_CALC_RANGE_CURRENT_FRAME: + return ANIMVIZ_CALC_RANGE_CURRENT_FRAME; + case OBJECT_PATH_CALC_RANGE_CHANGED: + return ANIMVIZ_CALC_RANGE_CHANGED; + case OBJECT_PATH_CALC_RANGE_FULL: + return ANIMVIZ_CALC_RANGE_FULL; + } + return ANIMVIZ_CALC_RANGE_FULL; +} + /* For the objects with animation: update paths for those that have got them * This should selectively update paths that exist... * * To be called from various tools that do incremental updates */ -void ED_objects_recalculate_paths(bContext *C, Scene *scene, bool current_frame_only) +void ED_objects_recalculate_paths(bContext *C, Scene *scene, eObjectPathCalcRange range) { /* Transform doesn't always have context available to do update. */ if (C == NULL) { @@ -923,11 +936,9 @@ void ED_objects_recalculate_paths(bContext *C, Scene *scene, bool current_frame_ } Main *bmain = CTX_data_main(C); - /* NOTE: Dependency graph will be evaluated at all the frames, but we first need to access some - * nested pointers, like animation data. */ - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - ListBase targets = {NULL, NULL}; + ViewLayer *view_layer = CTX_data_view_layer(C); + ListBase targets = {NULL, NULL}; /* loop over objects in scene */ CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) { /* set flag to force recalc, then grab path(s) from object */ @@ -936,11 +947,27 @@ void ED_objects_recalculate_paths(bContext *C, Scene *scene, bool current_frame_ } CTX_DATA_END; + Depsgraph *depsgraph; + bool free_depsgraph = false; + /* For a single frame update it's faster to re-use existing dependency graph and avoid overhead + * of building all the relations and so on for a temporary one. */ + if (range == OBJECT_PATH_CALC_RANGE_CURRENT_FRAME) { + /* NOTE: Dependency graph will be evaluated at all the frames, but we first need to access some + * nested pointers, like animation data. */ + depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + free_depsgraph = false; + } + else { + depsgraph = animviz_depsgraph_build(bmain, scene, view_layer, &targets); + free_depsgraph = true; + } + /* recalculate paths, then free */ - animviz_calc_motionpaths(depsgraph, bmain, scene, &targets, true, current_frame_only); + animviz_calc_motionpaths( + depsgraph, bmain, scene, &targets, object_path_convert_range(range), true); BLI_freelistN(&targets); - if (!current_frame_only) { + if (range != OBJECT_PATH_CALC_RANGE_CURRENT_FRAME) { /* Tag objects for copy on write - so paths will draw/redraw * For currently frame only we update evaluated object directly. */ CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) { @@ -950,6 +977,11 @@ void ED_objects_recalculate_paths(bContext *C, Scene *scene, bool current_frame_ } CTX_DATA_END; } + + /* Free temporary depsgraph. */ + if (free_depsgraph) { + DEG_graph_free(depsgraph); + } } /* show popup to determine settings */ @@ -995,7 +1027,7 @@ static int object_calculate_paths_exec(bContext *C, wmOperator *op) CTX_DATA_END; /* calculate the paths for objects that have them (and are tagged to get refreshed) */ - ED_objects_recalculate_paths(C, scene, false); + ED_objects_recalculate_paths(C, scene, OBJECT_PATH_CALC_RANGE_FULL); /* notifiers for updates */ WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); @@ -1060,7 +1092,7 @@ static int object_update_paths_exec(bContext *C, wmOperator *UNUSED(op)) } /* calculate the paths for objects that have them (and are tagged to get refreshed) */ - ED_objects_recalculate_paths(C, scene, false); + ED_objects_recalculate_paths(C, scene, OBJECT_PATH_CALC_RANGE_FULL); /* notifiers for updates */ WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); @@ -1515,7 +1547,7 @@ static int move_to_collection_exec(bContext *C, wmOperator *op) } int collection_index = RNA_property_int_get(op->ptr, prop); - collection = BKE_collection_from_index(CTX_data_scene(C), collection_index); + collection = BKE_collection_from_index(scene, collection_index); if (collection == NULL) { BKE_report(op->reports, RPT_ERROR, "Unexpected error, collection not found"); return OPERATOR_CANCELLED; diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c index 88d01936882..abcb4afa37d 100644 --- a/source/blender/editors/object/object_modifier.c +++ b/source/blender/editors/object/object_modifier.c @@ -677,7 +677,7 @@ static int modifier_apply_obdata( /* Multires: ensure that recent sculpting is applied */ if (md_eval->type == eModifierType_Multires) { - multires_force_update(ob); + multires_force_sculpt_rebuild(ob); } if (mmd && mmd->totlvl && mti->type == eModifierTypeType_OnlyDeform) { @@ -2022,7 +2022,7 @@ static int correctivesmooth_bind_exec(bContext *C, wmOperator *op) is_bind = (csmd->bind_coords != NULL); MEM_SAFE_FREE(csmd->bind_coords); - MEM_SAFE_FREE(csmd->delta_cache); + MEM_SAFE_FREE(csmd->delta_cache.deltas); if (is_bind) { /* toggle off */ diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index 0d20a07dcee..c030c551374 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -1817,6 +1817,10 @@ static void single_object_users( if (v3d) { ID_NEW_REMAP(v3d->camera); } + /* Camera pointers of markers. */ + for (TimeMarker *marker = scene->markers.first; marker; marker = marker->next) { + ID_NEW_REMAP(marker->camera); + } /* Making single user may affect other scenes if they share * with current one some collections in their ViewLayer. */ @@ -2046,6 +2050,13 @@ void ED_object_single_users(Main *bmain, single_obdata_users(bmain, scene, NULL, NULL, 0); single_object_action_users(bmain, scene, NULL, NULL, 0); single_mat_users_expand(bmain); + /* Duplicating obdata and other IDs may require another update of the collections and objects + * pointers, especially regarding drivers and custom props, see T66641. + * Note that this whole scene duplication code and 'make single user' functions have te be + * rewritten at some point to make use of proper modern ID management code, + * but that is no small task. + * For now we are doomed to that kind of band-aid to try to cover most of remapping cases. */ + libblock_relink_collection(scene->master_collection); } /* Relink nodetrees' pointers that have been duplicated. */ diff --git a/source/blender/editors/object/object_remesh.c b/source/blender/editors/object/object_remesh.c index 815cc618d4b..35762c5861e 100644 --- a/source/blender/editors/object/object_remesh.c +++ b/source/blender/editors/object/object_remesh.c @@ -40,12 +40,17 @@ #include "BKE_context.h" #include "BKE_global.h" +#include "BKE_library.h" #include "BKE_main.h" #include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" +#include "BKE_mirror.h" +#include "BKE_modifier.h" #include "BKE_object.h" #include "BKE_paint.h" #include "BKE_report.h" #include "BKE_scene.h" +#include "BKE_shrinkwrap.h" #include "BKE_customdata.h" #include "BKE_mesh_remesh_voxel.h" @@ -73,13 +78,24 @@ static bool object_remesh_poll(bContext *C) { Object *ob = CTX_data_active_object(C); + if (ob == NULL) { + return false; + } + if (BKE_object_is_in_editmode(ob)) { - CTX_wm_operator_poll_msg_set(C, "The voxel remesher cannot run from edit mode."); + CTX_wm_operator_poll_msg_set(C, "The remesher cannot run from edit mode."); return false; } if (ob->mode == OB_MODE_SCULPT && ob->sculpt->bm) { - CTX_wm_operator_poll_msg_set(C, "The voxel remesher cannot run with dyntopo activated."); + CTX_wm_operator_poll_msg_set(C, "The remesher cannot run with dyntopo activated."); + return false; + } + + if (modifiers_usesMultires(ob)) { + CTX_wm_operator_poll_msg_set( + C, "The remesher cannot run with a Multires modifier in the modifier stack."); + return false; } return ED_operator_object_active_editable_mesh(C); @@ -101,29 +117,35 @@ static int voxel_remesh_exec(bContext *C, wmOperator *op) ED_sculpt_undo_geometry_begin(ob); } - new_mesh = BKE_mesh_remesh_voxel_to_mesh_nomain(mesh, mesh->remesh_voxel_size); + float isovalue = 0.0f; + if (mesh->flag & ME_REMESH_REPROJECT_VOLUME) { + isovalue = mesh->remesh_voxel_size * 0.3f; + } + + new_mesh = BKE_mesh_remesh_voxel_to_mesh_nomain( + mesh, mesh->remesh_voxel_size, mesh->remesh_voxel_adaptivity, isovalue); if (!new_mesh) { return OPERATOR_CANCELLED; } - Mesh *obj_mesh_copy = NULL; - if (mesh->flag & ME_REMESH_REPROJECT_PAINT_MASK) { - obj_mesh_copy = BKE_mesh_new_nomain_from_template(mesh, mesh->totvert, 0, 0, 0, 0); - CustomData_copy( - &mesh->vdata, &obj_mesh_copy->vdata, CD_MASK_MESH.vmask, CD_DUPLICATE, mesh->totvert); - for (int i = 0; i < mesh->totvert; i++) { - copy_v3_v3(obj_mesh_copy->mvert[i].co, mesh->mvert[i].co); - } + if (mesh->flag & ME_REMESH_FIX_POLES && mesh->remesh_voxel_adaptivity <= 0.0f) { + new_mesh = BKE_mesh_remesh_voxel_fix_poles(new_mesh); + BKE_mesh_calc_normals(new_mesh); } - BKE_mesh_nomain_to_mesh(new_mesh, mesh, ob, &CD_MASK_MESH, true); + if (mesh->flag & ME_REMESH_REPROJECT_VOLUME) { + BKE_mesh_runtime_clear_geometry(mesh); + BKE_shrinkwrap_remesh_target_project(new_mesh, mesh, ob); + } if (mesh->flag & ME_REMESH_REPROJECT_PAINT_MASK) { - BKE_remesh_reproject_paint_mask(mesh, obj_mesh_copy); - BKE_mesh_free(obj_mesh_copy); + BKE_mesh_runtime_clear_geometry(mesh); + BKE_remesh_reproject_paint_mask(new_mesh, mesh); } + BKE_mesh_nomain_to_mesh(new_mesh, mesh, ob, &CD_MASK_MESH, true); + if (mesh->flag & ME_REMESH_SMOOTH_NORMALS) { BKE_mesh_smooth_flag_set(ob->data, true); } @@ -163,14 +185,26 @@ enum { /****************** quadriflow remesh operator *********************/ +#define QUADRIFLOW_MIRROR_BISECT_TOLERANCE 0.005f + +typedef enum eSymmetryAxes { + SYMMETRY_AXES_X = (1 << 0), + SYMMETRY_AXES_Y = (1 << 1), + SYMMETRY_AXES_Z = (1 << 2), +} eSymmetryAxes; + typedef struct QuadriFlowJob { /* from wmJob */ struct Object *owner; + struct Main *bmain; short *stop, *do_update; float *progress; int target_faces; int seed; + bool use_paint_symmetry; + eSymmetryAxes symmetry_axes; + bool use_preserve_sharp; bool use_preserve_boundary; bool use_mesh_curvature; @@ -181,6 +215,57 @@ typedef struct QuadriFlowJob { int success; } QuadriFlowJob; +static bool mesh_is_manifold_consistent(Mesh *mesh) +{ + /* In this check we count boundary edges as manifold. Additionally, we also + * check that the direction of the faces are consistent and doesn't suddenly + * flip + */ + + bool is_manifold_consistent = true; + const MLoop *mloop = mesh->mloop; + char *edge_faces = (char *)MEM_callocN(mesh->totedge * sizeof(char), "remesh_manifold_check"); + int *edge_vert = (int *)MEM_malloc_arrayN( + mesh->totedge, sizeof(unsigned int), "remesh_consistent_check"); + + for (unsigned int i = 0; i < mesh->totedge; i++) { + edge_vert[i] = -1; + } + + for (unsigned int loop_idx = 0; loop_idx < mesh->totloop; loop_idx++) { + const MLoop *loop = &mloop[loop_idx]; + edge_faces[loop->e] += 1; + if (edge_faces[loop->e] > 2) { + is_manifold_consistent = false; + break; + } + + if (edge_vert[loop->e] == -1) { + edge_vert[loop->e] = loop->v; + } + else if (edge_vert[loop->e] == loop->v) { + /* Mesh has flips in the surface so it is non consistent */ + is_manifold_consistent = false; + break; + } + } + + if (is_manifold_consistent) { + /* check for wire edges */ + for (unsigned int i = 0; i < mesh->totedge; i++) { + if (edge_faces[i] == 0) { + is_manifold_consistent = false; + break; + } + } + } + + MEM_freeN(edge_faces); + MEM_freeN(edge_vert); + + return is_manifold_consistent; +} + static void quadriflow_free_job(void *customdata) { QuadriFlowJob *qj = customdata; @@ -221,6 +306,66 @@ static void quadriflow_update_job(void *customdata, float progress, int *cancel) *(qj->progress) = progress; } +static Mesh *remesh_symmetry_bisect(Main *bmain, Mesh *mesh, eSymmetryAxes symmetry_axes) +{ + MirrorModifierData mmd = {0}; + mmd.tolerance = QUADRIFLOW_MIRROR_BISECT_TOLERANCE; + + Mesh *mesh_bisect, *mesh_bisect_temp; + mesh_bisect = BKE_mesh_copy(bmain, mesh); + + int axis; + float plane_co[3], plane_no[3]; + zero_v3(plane_co); + + for (char i = 0; i < 3; i++) { + eSymmetryAxes symm_it = (eSymmetryAxes)(1 << i); + if (symmetry_axes & symm_it) { + axis = i; + mmd.flag = 0; + mmd.flag &= MOD_MIR_BISECT_AXIS_X << i; + zero_v3(plane_no); + plane_no[axis] = -1.0f; + mesh_bisect_temp = mesh_bisect; + mesh_bisect = BKE_mirror_bisect_on_mirror_plane(&mmd, mesh_bisect, axis, plane_co, plane_no); + if (mesh_bisect_temp != mesh_bisect) { + BKE_id_free(bmain, mesh_bisect_temp); + } + } + } + + BKE_id_free(bmain, mesh); + + return mesh_bisect; +} + +static Mesh *remesh_symmetry_mirror(Object *ob, Mesh *mesh, eSymmetryAxes symmetry_axes) +{ + MirrorModifierData mmd = {0}; + mmd.tolerance = QUADRIFLOW_MIRROR_BISECT_TOLERANCE; + Mesh *mesh_mirror, *mesh_mirror_temp; + + mesh_mirror = mesh; + + int axis; + + for (char i = 0; i < 3; i++) { + eSymmetryAxes symm_it = (eSymmetryAxes)(1 << i); + if (symmetry_axes & symm_it) { + axis = i; + mmd.flag = 0; + mmd.flag &= MOD_MIR_AXIS_X << i; + mesh_mirror_temp = mesh_mirror; + mesh_mirror = BKE_mirror_apply_mirror_on_axis(&mmd, NULL, ob, mesh_mirror, axis); + if (mesh_mirror_temp != mesh_mirror) { + BKE_id_free(NULL, mesh_mirror_temp); + } + } + } + + return mesh_mirror; +} + static void quadriflow_start_job(void *customdata, short *stop, short *do_update, float *progress) { QuadriFlowJob *qj = customdata; @@ -235,16 +380,33 @@ static void quadriflow_start_job(void *customdata, short *stop, short *do_update Object *ob = qj->owner; Mesh *mesh = ob->data; Mesh *new_mesh; + Mesh *bisect_mesh; + + /* Check if the mesh is manifold. Quadriflow requires manifold meshes */ + if (!mesh_is_manifold_consistent(mesh)) { + qj->success = -2; + return; + } + + /* Run Quadriflow bisect operations on a copy of the mesh to keep the code readable without + * freeing the original ID */ + bisect_mesh = BKE_mesh_copy(qj->bmain, mesh); - new_mesh = BKE_mesh_remesh_quadriflow_to_mesh_nomain(mesh, + /* Bisect the input mesh using the paint symmetry settings */ + bisect_mesh = remesh_symmetry_bisect(qj->bmain, bisect_mesh, qj->symmetry_axes); + + new_mesh = BKE_mesh_remesh_quadriflow_to_mesh_nomain(bisect_mesh, qj->target_faces, qj->seed, qj->use_preserve_sharp, - qj->use_preserve_boundary, + qj->use_preserve_boundary || + qj->use_paint_symmetry, qj->use_mesh_curvature, quadriflow_update_job, (void *)qj); + BKE_id_free(qj->bmain, bisect_mesh); + if (!new_mesh) { *do_update = true; *stop = 0; @@ -255,28 +417,26 @@ static void quadriflow_start_job(void *customdata, short *stop, short *do_update return; } + /* Mirror the Quadriflow result to build the final mesh */ + if (new_mesh) { + new_mesh = remesh_symmetry_mirror(qj->owner, new_mesh, qj->symmetry_axes); + } + if (ob->mode == OB_MODE_SCULPT) { ED_sculpt_undo_geometry_begin(ob); } - Mesh *obj_mesh_copy = NULL; if (qj->preserve_paint_mask) { - obj_mesh_copy = BKE_mesh_new_nomain_from_template(mesh, mesh->totvert, 0, 0, 0, 0); - CustomData_copy( - &mesh->vdata, &obj_mesh_copy->vdata, CD_MASK_MESH.vmask, CD_DUPLICATE, mesh->totvert); - for (int i = 0; i < mesh->totvert; i++) { - copy_v3_v3(obj_mesh_copy->mvert[i].co, mesh->mvert[i].co); - } + BKE_mesh_runtime_clear_geometry(mesh); + BKE_remesh_reproject_paint_mask(new_mesh, mesh); } BKE_mesh_nomain_to_mesh(new_mesh, mesh, ob, &CD_MASK_MESH, true); - if (qj->preserve_paint_mask) { - BKE_remesh_reproject_paint_mask(mesh, obj_mesh_copy); - BKE_mesh_free(obj_mesh_copy); - } - if (qj->smooth_normals) { + if (qj->use_paint_symmetry) { + BKE_mesh_calc_normals(ob->data); + } BKE_mesh_smooth_flag_set(ob->data, true); } @@ -298,17 +458,22 @@ static void quadriflow_end_job(void *customdata) WM_set_locked_interface(G_MAIN->wm.first, false); - if (qj->success > 0) { - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - WM_reportf(RPT_INFO, "QuadriFlow: Completed remeshing!"); - } - else { - if (qj->success == 0) { + switch (qj->success) { + case 1: + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + WM_reportf(RPT_INFO, "QuadriFlow: Completed remeshing!"); + break; + case 0: WM_reportf(RPT_ERROR, "QuadriFlow: remeshing failed!"); - } - else { + break; + case -1: WM_report(RPT_WARNING, "QuadriFlow: remeshing canceled!"); - } + break; + case -2: + WM_report(RPT_WARNING, + "QuadriFlow: The mesh needs to be manifold and have face normals that point in a " + "consistent direction."); + break; } } @@ -317,10 +482,13 @@ static int quadriflow_remesh_exec(bContext *C, wmOperator *op) QuadriFlowJob *job = MEM_mallocN(sizeof(QuadriFlowJob), "QuadriFlowJob"); job->owner = CTX_data_active_object(C); + job->bmain = CTX_data_main(C); job->target_faces = RNA_int_get(op->ptr, "target_faces"); job->seed = RNA_int_get(op->ptr, "seed"); + job->use_paint_symmetry = RNA_boolean_get(op->ptr, "use_paint_symmetry"); + job->use_preserve_sharp = RNA_boolean_get(op->ptr, "use_preserve_sharp"); job->use_preserve_boundary = RNA_boolean_get(op->ptr, "use_preserve_boundary"); @@ -329,6 +497,22 @@ static int quadriflow_remesh_exec(bContext *C, wmOperator *op) job->preserve_paint_mask = RNA_boolean_get(op->ptr, "preserve_paint_mask"); job->smooth_normals = RNA_boolean_get(op->ptr, "smooth_normals"); + /* Update the target face count if symmetry is enabled */ + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + if (sd && job->use_paint_symmetry) { + job->symmetry_axes = (eSymmetryAxes)(sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL); + for (char i = 0; i < 3; i++) { + eSymmetryAxes symm_it = (eSymmetryAxes)(1 << i); + if (job->symmetry_axes & symm_it) { + job->target_faces = job->target_faces / 2; + } + } + } + else { + job->use_paint_symmetry = false; + job->symmetry_axes = 0; + } + wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), CTX_data_scene(C), @@ -453,6 +637,12 @@ void OBJECT_OT_quadriflow_remesh(wmOperatorType *ot) /* properties */ RNA_def_boolean(ot->srna, + "use_paint_symmetry", + true, + "Use Paint Symmetry", + "Generates a symmetrycal mesh using the paint symmetry configuration"); + + RNA_def_boolean(ot->srna, "use_preserve_sharp", false, "Preserve Sharp", @@ -485,7 +675,7 @@ void OBJECT_OT_quadriflow_remesh(wmOperatorType *ot) RNA_def_enum(ot->srna, "mode", mode_type_items, - 0, + QUADRIFLOW_REMESH_FACES, "Mode", "How to specify the amount of detail for the new mesh"); @@ -511,7 +701,7 @@ void OBJECT_OT_quadriflow_remesh(wmOperatorType *ot) prop = RNA_def_int(ot->srna, "target_faces", - 1, + 4000, 1, INT_MAX, "Number of Faces", diff --git a/source/blender/editors/object/object_select.c b/source/blender/editors/object/object_select.c index 28242b986f1..40fa11994f4 100644 --- a/source/blender/editors/object/object_select.c +++ b/source/blender/editors/object/object_select.c @@ -214,7 +214,7 @@ bool ED_object_base_deselect_all(ViewLayer *view_layer, View3D *v3d, int action) static int get_base_select_priority(Base *base) { - if (base->flag & BASE_VISIBLE) { + if (base->flag & BASE_VISIBLE_DEPSGRAPH) { if (base->flag & BASE_SELECTABLE) { return 3; } @@ -288,7 +288,7 @@ bool ED_object_jump_to_object(bContext *C, Object *ob, const bool UNUSED(reveal_ if (!(base->flag & BASE_SELECTED)) { ED_object_base_deselect_all(view_layer, v3d, SEL_DESELECT); - if (base->flag & BASE_VISIBLE) { + if (BASE_VISIBLE(v3d, base)) { ED_object_base_select(base, BA_SELECT); } diff --git a/source/blender/editors/object/object_transform.c b/source/blender/editors/object/object_transform.c index 1cd16b1b0bf..b534e1b9683 100644 --- a/source/blender/editors/object/object_transform.c +++ b/source/blender/editors/object/object_transform.c @@ -1715,8 +1715,9 @@ static void object_transform_axis_target_cancel(bContext *C, wmOperator *op) static int object_transform_axis_target_invoke(bContext *C, wmOperator *op, const wmEvent *event) { + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewContext vc; - ED_view3d_viewcontext_init(C, &vc); + ED_view3d_viewcontext_init(C, &vc, depsgraph); if (vc.obact == NULL || !object_is_target_compat(vc.obact)) { /* Falls back to texture space transform. */ diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c index 2047ecb9e0a..f16a372cb3c 100644 --- a/source/blender/editors/physics/particle_edit.c +++ b/source/blender/editors/physics/particle_edit.c @@ -93,6 +93,7 @@ bool PE_poll(bContext *C) { + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Scene *scene = CTX_data_scene(C); Object *ob = CTX_data_active_object(C); @@ -100,7 +101,7 @@ bool PE_poll(bContext *C) return false; } - PTCacheEdit *edit = PE_get_current(scene, ob); + PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob); if (edit == NULL) { return false; } @@ -113,6 +114,7 @@ bool PE_poll(bContext *C) bool PE_hair_poll(bContext *C) { + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Scene *scene = CTX_data_scene(C); Object *ob = CTX_data_active_object(C); @@ -120,7 +122,7 @@ bool PE_hair_poll(bContext *C) return false; } - PTCacheEdit *edit = PE_get_current(scene, ob); + PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob); if (edit == NULL || edit->psys == NULL) { return false; } @@ -149,8 +151,7 @@ void PE_free_ptcache_edit(PTCacheEdit *edit) } if (edit->points) { - LOOP_POINTS - { + LOOP_POINTS { if (point->keys) { MEM_freeN(point->keys); } @@ -356,9 +357,9 @@ static PTCacheEdit *pe_get_current(Depsgraph *depsgraph, Scene *scene, Object *o return edit; } -PTCacheEdit *PE_get_current(Scene *scene, Object *ob) +PTCacheEdit *PE_get_current(Depsgraph *depsgraph, Scene *scene, Object *ob) { - return pe_get_current(NULL, scene, ob, 0); + return pe_get_current(depsgraph, scene, ob, 0); } PTCacheEdit *PE_create_current(Depsgraph *depsgraph, Scene *scene, Object *ob) @@ -380,10 +381,8 @@ void PE_hide_keys_time(Scene *scene, PTCacheEdit *edit, float cfra) KEY_K; if (pset->flag & PE_FADE_TIME && pset->selectmode == SCE_SELECT_POINT) { - LOOP_POINTS - { - LOOP_KEYS - { + LOOP_POINTS { + LOOP_KEYS { if (fabsf(cfra - *key->time) < pset->fade_frames) { key->flag &= ~PEK_HIDE; } @@ -395,10 +394,8 @@ void PE_hide_keys_time(Scene *scene, PTCacheEdit *edit, float cfra) } } else { - LOOP_POINTS - { - LOOP_KEYS - { + LOOP_POINTS { + LOOP_KEYS { key->flag &= ~PEK_HIDE; } } @@ -466,14 +463,14 @@ static void PE_set_data(bContext *C, PEData *data) data->view_layer = CTX_data_view_layer(C); data->ob = CTX_data_active_object(C); data->depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - data->edit = PE_get_current(data->scene, data->ob); + data->edit = PE_get_current(data->depsgraph, data->scene, data->ob); } static void PE_set_view3d_data(bContext *C, PEData *data) { PE_set_data(C, data); - ED_view3d_viewcontext_init(C, &data->vc); + ED_view3d_viewcontext_init(C, &data->vc, data->depsgraph); if (!XRAY_ENABLED(data->vc.v3d)) { if (data->vc.v3d->flag & V3D_INVALID_BACKBUF) { @@ -633,8 +630,7 @@ static bool point_is_selected(PTCacheEditPoint *point) return 0; } - LOOP_SELECTED_KEYS - { + LOOP_SELECTED_KEYS { return 1; } @@ -684,8 +680,7 @@ static void for_mouse_hit_keys(PEData *data, ForKeyFunc func, const enum ePartic nearest_point = -1; nearest_key = -1; - LOOP_VISIBLE_POINTS - { + LOOP_VISIBLE_POINTS { if (pset->selectmode == SCE_SELECT_END) { if (point->totkey) { /* only do end keys */ @@ -707,8 +702,7 @@ static void for_mouse_hit_keys(PEData *data, ForKeyFunc func, const enum ePartic } else { /* do all keys */ - LOOP_VISIBLE_KEYS - { + LOOP_VISIBLE_KEYS { if (flag & PSEL_NEAREST) { if (key_inside_circle(data, dist, KEY_WCO, &dist)) { nearest_point = p; @@ -745,8 +739,7 @@ static void foreach_mouse_hit_point(PEData *data, ForHitPointFunc func, int sele selected = 0; } - LOOP_VISIBLE_POINTS - { + LOOP_VISIBLE_POINTS { if (pset->selectmode == SCE_SELECT_END) { if (point->totkey) { /* only do end keys */ @@ -762,8 +755,7 @@ static void foreach_mouse_hit_point(PEData *data, ForHitPointFunc func, int sele } else { /* do all keys */ - LOOP_VISIBLE_KEYS - { + LOOP_VISIBLE_KEYS { if (selected == 0 || key->flag & PEK_SELECT) { float mouse_distance; if (key_inside_circle(data, data->rad, KEY_WCO, &mouse_distance)) { @@ -823,8 +815,7 @@ static void foreach_mouse_hit_key_iter(void *__restrict iter_data_v, /* do all keys */ PTCacheEditKey *key; int k; - LOOP_VISIBLE_KEYS - { + LOOP_VISIBLE_KEYS { if (selected == 0 || key->flag & PEK_SELECT) { float mouse_distance; if (key_inside_circle(data, data->rad, KEY_WCO, &mouse_distance)) { @@ -866,8 +857,7 @@ static void foreach_selected_point(PEData *data, ForPointFunc func) PTCacheEdit *edit = data->edit; POINT_P; - LOOP_SELECTED_POINTS - { + LOOP_SELECTED_POINTS { func(data, p); } } @@ -878,10 +868,8 @@ static void foreach_selected_key(PEData *data, ForKeyFunc func) POINT_P; KEY_K; - LOOP_VISIBLE_POINTS - { - LOOP_SELECTED_KEYS - { + LOOP_VISIBLE_POINTS { + LOOP_SELECTED_KEYS { func(data, p, k, true); } } @@ -892,8 +880,7 @@ static void foreach_point(PEData *data, ForPointFunc func) PTCacheEdit *edit = data->edit; POINT_P; - LOOP_POINTS - { + LOOP_POINTS { func(data, p); } } @@ -905,11 +892,9 @@ static int count_selected_keys(Scene *scene, PTCacheEdit *edit) KEY_K; int sel = 0; - LOOP_VISIBLE_POINTS - { + LOOP_VISIBLE_POINTS { if (pset->selectmode == SCE_SELECT_POINT) { - LOOP_SELECTED_KEYS - { + LOOP_SELECTED_KEYS { sel++; } } @@ -1117,8 +1102,7 @@ static void PE_apply_mirror(Object *ob, ParticleSystem *psys) /* we delay settings the PARS_EDIT_RECALC for mirrored particles * to avoid doing mirror twice */ - LOOP_POINTS - { + LOOP_POINTS { if (point->flag & PEP_EDIT_RECALC) { PE_mirror_particle(ob, psmd_eval->mesh_final, psys, psys->particles + p, NULL); @@ -1128,8 +1112,7 @@ static void PE_apply_mirror(Object *ob, ParticleSystem *psys) } } - LOOP_POINTS - { + LOOP_POINTS { if (point->flag & PEP_EDIT_RECALC) { if (edit->mirror_cache[p] != -1) { edit->points[edit->mirror_cache[p]].flag |= PEP_EDIT_RECALC; @@ -1173,13 +1156,11 @@ static void deflect_emitter_iter(void *__restrict iter_data_v, psys_mat_hair_to_object( object, psmd_eval->mesh_final, psys->part->from, psys->particles + iter, hairmat); - LOOP_KEYS - { + LOOP_KEYS { mul_m4_v3(hairmat, key->co); } - LOOP_KEYS - { + LOOP_KEYS { if (k == 0) { dist_1st = len_v3v3((key + 1)->co, key->co); dist_1st *= dist * emitterdist; @@ -1215,8 +1196,7 @@ static void deflect_emitter_iter(void *__restrict iter_data_v, invert_m4_m4(hairimat, hairmat); - LOOP_KEYS - { + LOOP_KEYS { mul_m4_v3(hairimat, key->co); } } @@ -1268,8 +1248,7 @@ static void apply_lengths_iter(void *__restrict iter_data_v, } PTCacheEditKey *key; int k; - LOOP_KEYS - { + LOOP_KEYS { if (k) { float dv1[3]; sub_v3_v3v3(dv1, key->co, (key - 1)->co); @@ -1387,8 +1366,7 @@ void recalc_lengths(PTCacheEdit *edit) return; } - LOOP_EDITED_POINTS - { + LOOP_EDITED_POINTS { key = point->keys; for (k = 0; k < point->totkey - 1; k++, key++) { key->length = len_v3v3(key->co, (key + 1)->co); @@ -1461,15 +1439,14 @@ void recalc_emitter_field(Depsgraph *UNUSED(depsgraph), Object *UNUSED(ob), Part static void PE_update_selection(Depsgraph *depsgraph, Scene *scene, Object *ob, int useflag) { - PTCacheEdit *edit = PE_get_current(scene, ob); + PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob); HairKey *hkey; POINT_P; KEY_K; /* flag all particles to be updated if not using flag */ if (!useflag) { - LOOP_POINTS - { + LOOP_POINTS { point->flag |= PEP_EDIT_RECALC; } } @@ -1477,11 +1454,9 @@ static void PE_update_selection(Depsgraph *depsgraph, Scene *scene, Object *ob, /* flush edit key flag to hair key flag to preserve selection * on save */ if (edit->psys) { - LOOP_POINTS - { + LOOP_POINTS { hkey = edit->psys->particles[p].hair; - LOOP_KEYS - { + LOOP_KEYS { hkey->editflag = key->flag; hkey++; } @@ -1491,8 +1466,7 @@ static void PE_update_selection(Depsgraph *depsgraph, Scene *scene, Object *ob, psys_cache_edit_paths(depsgraph, scene, ob, edit, CFRA, G.is_rendering); /* disable update flag */ - LOOP_POINTS - { + LOOP_POINTS { point->flag &= ~PEP_EDIT_RECALC; } @@ -1511,15 +1485,13 @@ void update_world_cos(Object *ob, PTCacheEdit *edit) return; } - LOOP_POINTS - { + LOOP_POINTS { if (!(psys->flag & PSYS_GLOBAL_HAIR)) { psys_mat_hair_to_global( ob, psmd_eval->mesh_final, psys->part->from, psys->particles + p, hairmat); } - LOOP_KEYS - { + LOOP_KEYS { copy_v3_v3(key->world_co, key->co); if (!(psys->flag & PSYS_GLOBAL_HAIR)) { mul_m4_v3(hairmat, key->world_co); @@ -1541,10 +1513,8 @@ static void update_velocities(PTCacheEdit *edit) frs_sec = edit->pid.flag & PTCACHE_VEL_PER_SEC ? 25.0f : 1.0f; - LOOP_EDITED_POINTS - { - LOOP_KEYS - { + LOOP_EDITED_POINTS { + LOOP_KEYS { if (k == 0) { dfra = *(key + 1)->time - *key->time; @@ -1596,7 +1566,7 @@ void PE_update_object(Depsgraph *depsgraph, Scene *scene, Object *ob, int usefla /* use this to do partial particle updates, not usable when adding or * removing, then a full redo is necessary and calling this may crash */ ParticleEditSettings *pset = PE_settings(scene); - PTCacheEdit *edit = PE_get_current(scene, ob); + PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob); POINT_P; if (!edit) { @@ -1605,8 +1575,7 @@ void PE_update_object(Depsgraph *depsgraph, Scene *scene, Object *ob, int usefla /* flag all particles to be updated if not using flag */ if (!useflag) { - LOOP_POINTS - { + LOOP_POINTS { point->flag |= PEP_EDIT_RECALC; } } @@ -1624,14 +1593,19 @@ void PE_update_object(Depsgraph *depsgraph, Scene *scene, Object *ob, int usefla if (pset->flag & PE_AUTO_VELOCITY) { update_velocities(edit); } - PE_hide_keys_time(scene, edit, CFRA); + + /* Only do this for emitter particles because drawing PE_FADE_TIME is not respected in 2.8 yet + * and flagging with PEK_HIDE will prevent selection. This might get restored once this is + * supported in drawing (but doesn't make much sense for hair anyways). */ + if (edit->psys->part->type == PART_EMITTER) { + PE_hide_keys_time(scene, edit, CFRA); + } /* regenerate path caches */ psys_cache_edit_paths(depsgraph, scene, ob, edit, CFRA, G.is_rendering); /* disable update flag */ - LOOP_POINTS - { + LOOP_POINTS { point->flag &= ~PEP_EDIT_RECALC; } @@ -1686,8 +1660,7 @@ static void select_keys(PEData *data, PTCacheEditPoint *point = edit->points + point_index; KEY_K; - LOOP_KEYS - { + LOOP_KEYS { if (data->select) { key->flag |= PEK_SELECT; } @@ -1780,17 +1753,15 @@ static int pe_select_all_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Object *ob = CTX_data_active_object(C); - PTCacheEdit *edit = PE_get_current(scene, ob); + PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob); POINT_P; KEY_K; int action = RNA_enum_get(op->ptr, "action"); if (action == SEL_TOGGLE) { action = SEL_SELECT; - LOOP_VISIBLE_POINTS - { - LOOP_SELECTED_KEYS - { + LOOP_VISIBLE_POINTS { + LOOP_SELECTED_KEYS { action = SEL_DESELECT; break; } @@ -1802,10 +1773,8 @@ static int pe_select_all_exec(bContext *C, wmOperator *op) } bool changed = false; - LOOP_VISIBLE_POINTS - { - LOOP_VISIBLE_KEYS - { + LOOP_VISIBLE_POINTS { + LOOP_VISIBLE_KEYS { changed |= select_action_apply(point, key, action); } } @@ -1841,26 +1810,26 @@ bool PE_mouse_particles(bContext *C, const int mval[2], bool extend, bool desele PEData data; Scene *scene = CTX_data_scene(C); Object *ob = CTX_data_active_object(C); - PTCacheEdit *edit = PE_get_current(scene, ob); POINT_P; KEY_K; + PE_set_view3d_data(C, &data); + + PTCacheEdit *edit = PE_get_current(data.depsgraph, scene, ob); + if (!PE_start_edit(edit)) { return false; } if (!extend && !deselect && !toggle) { - LOOP_VISIBLE_POINTS - { - LOOP_SELECTED_KEYS - { + LOOP_VISIBLE_POINTS { + LOOP_SELECTED_KEYS { key->flag &= ~PEK_SELECT; point->flag |= PEP_EDIT_RECALC; } } } - PE_set_view3d_data(C, &data); data.mval = mval; data.rad = ED_view3d_select_dist_px(); @@ -2048,26 +2017,22 @@ static int select_random_exec(bContext *C, wmOperator *op) PE_set_data(C, &data); data.select_action = SEL_SELECT; - edit = PE_get_current(data.scene, data.ob); + edit = PE_get_current(data.depsgraph, data.scene, data.ob); rng = BLI_rng_new_srandom(seed); switch (type) { case RAN_HAIR: - LOOP_VISIBLE_POINTS - { + LOOP_VISIBLE_POINTS { int flag = ((BLI_rng_get_float(rng) < randfac) == select) ? SEL_SELECT : SEL_DESELECT; - LOOP_KEYS - { + LOOP_KEYS { data.is_changed |= select_action_apply(point, key, flag); } } break; case RAN_POINTS: - LOOP_VISIBLE_POINTS - { - LOOP_VISIBLE_KEYS - { + LOOP_VISIBLE_POINTS { + LOOP_VISIBLE_KEYS { int flag = ((BLI_rng_get_float(rng) < randfac) == select) ? SEL_SELECT : SEL_DESELECT; data.is_changed |= select_action_apply(point, key, flag); } @@ -2166,10 +2131,8 @@ bool PE_deselect_all_visible_ex(PTCacheEdit *edit) POINT_P; KEY_K; - LOOP_VISIBLE_POINTS - { - LOOP_SELECTED_KEYS - { + LOOP_VISIBLE_POINTS { + LOOP_SELECTED_KEYS { if ((key->flag & PEK_SELECT) != 0) { key->flag &= ~PEK_SELECT; point->flag |= PEP_EDIT_RECALC; @@ -2182,9 +2145,10 @@ bool PE_deselect_all_visible_ex(PTCacheEdit *edit) bool PE_deselect_all_visible(bContext *C) { + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Scene *scene = CTX_data_scene(C); Object *ob = CTX_data_active_object(C); - PTCacheEdit *edit = PE_get_current(scene, ob); + PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob); if (!PE_start_edit(edit)) { return false; } @@ -2193,9 +2157,10 @@ bool PE_deselect_all_visible(bContext *C) bool PE_box_select(bContext *C, const rcti *rect, const int sel_op) { + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Scene *scene = CTX_data_scene(C); Object *ob = CTX_data_active_object(C); - PTCacheEdit *edit = PE_get_current(scene, ob); + PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob); PEData data; if (!PE_start_edit(edit)) { @@ -2229,9 +2194,10 @@ bool PE_box_select(bContext *C, const rcti *rect, const int sel_op) bool PE_circle_select(bContext *C, const int sel_op, const int mval[2], float rad) { BLI_assert(ELEM(sel_op, SEL_OP_SET, SEL_OP_ADD, SEL_OP_SUB)); + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Scene *scene = CTX_data_scene(C); Object *ob = CTX_data_active_object(C); - PTCacheEdit *edit = PE_get_current(scene, ob); + PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob); PEData data; if (!PE_start_edit(edit)) { @@ -2260,11 +2226,12 @@ bool PE_circle_select(bContext *C, const int sel_op, const int mval[2], float ra int PE_lasso_select(bContext *C, const int mcords[][2], const short moves, const int sel_op) { + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Scene *scene = CTX_data_scene(C); Object *ob = CTX_data_active_object(C); ARegion *ar = CTX_wm_region(C); ParticleEditSettings *pset = PE_settings(scene); - PTCacheEdit *edit = PE_get_current(scene, ob); + PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob); ParticleSystem *psys = edit->psys; ParticleSystemModifierData *psmd_eval = edit->psmd_eval; POINT_P; @@ -2287,16 +2254,14 @@ int PE_lasso_select(bContext *C, const int mcords[][2], const short moves, const data.is_changed |= PE_deselect_all_visible_ex(edit); } - LOOP_VISIBLE_POINTS - { + LOOP_VISIBLE_POINTS { if (edit->psys && !(psys->flag & PSYS_GLOBAL_HAIR)) { psys_mat_hair_to_global( ob, psmd_eval->mesh_final, psys->part->from, psys->particles + p, mat); } if (pset->selectmode == SCE_SELECT_POINT) { - LOOP_KEYS - { + LOOP_VISIBLE_KEYS { copy_v3_v3(co, key->co); mul_m4_v3(mat, co); const bool is_select = key->flag & PEK_SELECT; @@ -2350,30 +2315,26 @@ static int hide_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - PTCacheEdit *edit = PE_get_current(scene, ob); + PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob); POINT_P; KEY_K; if (RNA_boolean_get(op->ptr, "unselected")) { - LOOP_UNSELECTED_POINTS - { + LOOP_UNSELECTED_POINTS { point->flag |= PEP_HIDE; point->flag |= PEP_EDIT_RECALC; - LOOP_KEYS - { + LOOP_KEYS { key->flag &= ~PEK_SELECT; } } } else { - LOOP_SELECTED_POINTS - { + LOOP_SELECTED_POINTS { point->flag |= PEP_HIDE; point->flag |= PEP_EDIT_RECALC; - LOOP_KEYS - { + LOOP_KEYS { key->flag &= ~PEK_SELECT; } } @@ -2410,19 +2371,17 @@ static int reveal_exec(bContext *C, wmOperator *op) Object *ob = CTX_data_active_object(C); Scene *scene = CTX_data_scene(C); Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - PTCacheEdit *edit = PE_get_current(scene, ob); + PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob); const bool select = RNA_boolean_get(op->ptr, "select"); POINT_P; KEY_K; - LOOP_POINTS - { + LOOP_POINTS { if (point->flag & PEP_HIDE) { point->flag &= ~PEP_HIDE; point->flag |= PEP_EDIT_RECALC; - LOOP_KEYS - { + LOOP_KEYS { SET_FLAG_FROM_TEST(key->flag, select, PEK_SELECT); } } @@ -2460,8 +2419,7 @@ static void select_less_keys(PEData *data, int point_index) PTCacheEditPoint *point = edit->points + point_index; KEY_K; - LOOP_SELECTED_KEYS - { + LOOP_SELECTED_KEYS { if (k == 0) { if (((key + 1)->flag & PEK_SELECT) == 0) { key->flag |= PEK_TAG; @@ -2479,8 +2437,7 @@ static void select_less_keys(PEData *data, int point_index) } } - LOOP_KEYS - { + LOOP_KEYS { if ((key->flag & PEK_TAG) && (key->flag & PEK_SELECT)) { key->flag &= ~(PEK_TAG | PEK_SELECT); point->flag |= PEP_EDIT_RECALC; /* redraw selection only */ @@ -2525,8 +2482,7 @@ static void select_more_keys(PEData *data, int point_index) PTCacheEditPoint *point = edit->points + point_index; KEY_K; - LOOP_KEYS - { + LOOP_KEYS { if (key->flag & PEK_SELECT) { continue; } @@ -2548,8 +2504,7 @@ static void select_more_keys(PEData *data, int point_index) } } - LOOP_KEYS - { + LOOP_KEYS { if ((key->flag & PEK_TAG) && (key->flag & PEK_SELECT) == 0) { key->flag &= ~PEK_TAG; key->flag |= PEK_SELECT; @@ -2694,7 +2649,8 @@ void PARTICLE_OT_rekey(wmOperatorType *ot) static void rekey_particle_to_time( const bContext *C, Scene *scene, Object *ob, int pa_index, float path_time) { - PTCacheEdit *edit = PE_get_current(scene, ob); + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob); ParticleSystem *psys; ParticleSimulationData sim = {0}; ParticleData *pa; @@ -2709,7 +2665,7 @@ static void rekey_particle_to_time( psys = edit->psys; - sim.depsgraph = CTX_data_depsgraph_pointer(C); + sim.depsgraph = depsgraph; sim.scene = scene; sim.ob = ob; sim.psys = psys; @@ -2758,14 +2714,12 @@ static int remove_tagged_particles(Object *ob, ParticleSystem *psys, int mirror) /* mirror tags */ psmd_eval = edit->psmd_eval; - LOOP_TAGGED_POINTS - { + LOOP_TAGGED_POINTS { PE_mirror_particle(ob, psmd_eval->mesh_final, psys, psys->particles + p, NULL); } } - LOOP_TAGGED_POINTS - { + LOOP_TAGGED_POINTS { new_totpart--; removed++; } @@ -2850,21 +2804,17 @@ static void remove_tagged_keys(Depsgraph *depsgraph, Object *ob, ParticleSystem ParticleSystemModifierData *psmd_eval = (ParticleSystemModifierData *)modifier_get_evaluated( depsgraph, ob, &psmd->modifier); - LOOP_POINTS - { - LOOP_TAGGED_KEYS - { + LOOP_POINTS { + LOOP_TAGGED_KEYS { PE_mirror_particle(ob, psmd_eval->mesh_final, psys, psys->particles + p, NULL); break; } } } - LOOP_POINTS - { + LOOP_POINTS { new_totkey = point->totkey; - LOOP_TAGGED_KEYS - { + LOOP_TAGGED_KEYS { new_totkey--; } /* we can't have elements with less than two keys*/ @@ -2874,13 +2824,11 @@ static void remove_tagged_keys(Depsgraph *depsgraph, Object *ob, ParticleSystem } remove_tagged_particles(ob, psys, pe_x_mirror(ob)); - LOOP_POINTS - { + LOOP_POINTS { pa = psys->particles + p; new_totkey = pa->totkey; - LOOP_TAGGED_KEYS - { + LOOP_TAGGED_KEYS { new_totkey--; } @@ -2889,8 +2837,7 @@ static void remove_tagged_keys(Depsgraph *depsgraph, Object *ob, ParticleSystem nkey = new_keys = MEM_callocN(new_totkey * sizeof(PTCacheEditKey), "particle edit keys"); hkey = pa->hair; - LOOP_KEYS - { + LOOP_KEYS { while (key->flag & PEK_TAG && hkey < pa->hair + pa->totkey) { key++; hkey++; @@ -3062,9 +3009,10 @@ void PARTICLE_OT_subdivide(wmOperatorType *ot) static int remove_doubles_exec(bContext *C, wmOperator *op) { + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Scene *scene = CTX_data_scene(C); Object *ob = CTX_data_active_object(C); - PTCacheEdit *edit = PE_get_current(scene, ob); + PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob); ParticleSystem *psys = edit->psys; ParticleSystemModifierData *psmd_eval; KDTree_3d *tree; @@ -3087,8 +3035,7 @@ static int remove_doubles_exec(bContext *C, wmOperator *op) tree = BLI_kdtree_3d_new(psys->totpart); /* insert particles into kd tree */ - LOOP_SELECTED_POINTS - { + LOOP_SELECTED_POINTS { psys_mat_hair_to_object( ob, psmd_eval->mesh_final, psys->part->from, psys->particles + p, mat); copy_v3_v3(co, point->keys->co); @@ -3099,8 +3046,7 @@ static int remove_doubles_exec(bContext *C, wmOperator *op) BLI_kdtree_3d_balance(tree); /* tag particles to be removed */ - LOOP_SELECTED_POINTS - { + LOOP_SELECTED_POINTS { psys_mat_hair_to_object( ob, psmd_eval->mesh_final, psys->part->from, psys->particles + p, mat); copy_v3_v3(co, point->keys->co); @@ -3130,7 +3076,7 @@ static int remove_doubles_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - BKE_reportf(op->reports, RPT_INFO, "Removed %d double particles", totremoved); + BKE_reportf(op->reports, RPT_INFO, "Removed %d double particle(s)", totremoved); DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_EDITED, ob); @@ -3166,10 +3112,11 @@ void PARTICLE_OT_remove_doubles(wmOperatorType *ot) static int weight_set_exec(bContext *C, wmOperator *op) { + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Scene *scene = CTX_data_scene(C); ParticleEditSettings *pset = PE_settings(scene); Object *ob = CTX_data_active_object(C); - PTCacheEdit *edit = PE_get_current(scene, ob); + PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob); ParticleSystem *psys = edit->psys; POINT_P; KEY_K; @@ -3181,12 +3128,10 @@ static int weight_set_exec(bContext *C, wmOperator *op) weight = brush->strength; edit = psys->edit; - LOOP_SELECTED_POINTS - { + LOOP_SELECTED_POINTS { ParticleData *pa = psys->particles + p; - LOOP_SELECTED_KEYS - { + LOOP_SELECTED_KEYS { hkey = pa->hair + k; hkey->weight = interpf(weight, hkey->weight, factor); } @@ -3315,6 +3260,7 @@ static int delete_exec(bContext *C, wmOperator *op) } DEG_id_tag_update(&data.ob->id, ID_RECALC_GEOMETRY); + BKE_particle_batch_cache_dirty_tag(data.edit->psys, BKE_PARTICLE_BATCH_DIRTY_ALL); WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_EDITED, data.ob); return OPERATOR_FINISHED; @@ -3346,11 +3292,11 @@ void PARTICLE_OT_delete(wmOperatorType *ot) /*************************** mirror operator **************************/ -static void PE_mirror_x(Scene *scene, Object *ob, int tagged) +static void PE_mirror_x(Depsgraph *depsgraph, Scene *scene, Object *ob, int tagged) { Mesh *me = (Mesh *)(ob->data); ParticleSystemModifierData *psmd_eval; - PTCacheEdit *edit = PE_get_current(scene, ob); + PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob); ParticleSystem *psys = edit->psys; ParticleData *pa, *newpa, *new_pars; PTCacheEditPoint *newpoint, *new_points; @@ -3386,8 +3332,7 @@ static void PE_mirror_x(Scene *scene, Object *ob, int tagged) totpart = psys->totpart; newtotpart = psys->totpart; - LOOP_VISIBLE_POINTS - { + LOOP_VISIBLE_POINTS { pa = psys->particles + p; if (!tagged) { @@ -3503,8 +3448,7 @@ static void PE_mirror_x(Scene *scene, Object *ob, int tagged) } } - LOOP_POINTS - { + LOOP_POINTS { point->flag &= ~PEP_TAG; } @@ -3513,14 +3457,18 @@ static void PE_mirror_x(Scene *scene, Object *ob, int tagged) static int mirror_exec(bContext *C, wmOperator *UNUSED(op)) { + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Scene *scene = CTX_data_scene(C); Object *ob = CTX_data_active_object(C); - PTCacheEdit *edit = PE_get_current(scene, ob); + PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob); - PE_mirror_x(scene, ob, 0); + PE_mirror_x(depsgraph, scene, ob, 0); update_world_cos(ob, edit); + psys_free_path_cache(NULL, edit); + WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_EDITED, ob); + BKE_particle_batch_cache_dirty_tag(edit->psys, BKE_PARTICLE_BATCH_DIRTY_ALL); DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); return OPERATOR_FINISHED; @@ -3681,8 +3629,7 @@ static void brush_length(PEData *data, int point_index, float UNUSED(mouse_dista KEY_K; float dvec[3], pvec[3] = {0.0f, 0.0f, 0.0f}; - LOOP_KEYS - { + LOOP_KEYS { if (k == 0) { copy_v3_v3(pvec, key->co); } @@ -3731,8 +3678,7 @@ static void brush_puff(PEData *data, int point_index, float mouse_distance) unit_m4(imat); } - LOOP_KEYS - { + LOOP_KEYS { float kco[3]; if (k == 0) { @@ -4504,10 +4450,11 @@ typedef struct BrushEdit { static int brush_edit_init(bContext *C, wmOperator *op) { + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); Object *ob = CTX_data_active_object(C); - PTCacheEdit *edit = PE_get_current(scene, ob); + PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob); ARegion *ar = CTX_wm_region(C); BrushEdit *bedit; float min[3], max[3]; @@ -4518,7 +4465,7 @@ static int brush_edit_init(bContext *C, wmOperator *op) /* set the 'distance factor' for grabbing (used in comb etc) */ INIT_MINMAX(min, max); - PE_minmax(scene, view_layer, min, max); + PE_minmax(depsgraph, scene, view_layer, min, max); mid_v3_v3v3(min, min, max); bedit = MEM_callocN(sizeof(BrushEdit), "BrushEdit"); @@ -4744,7 +4691,7 @@ static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr) if (ELEM(pset->brushtype, PE_BRUSH_ADD, PE_BRUSH_CUT) && (added || removed)) { if (pset->brushtype == PE_BRUSH_ADD && pe_x_mirror(ob)) { - PE_mirror_x(scene, ob, 1); + PE_mirror_x(depsgraph, scene, ob, 1); } update_world_cos(ob, edit); @@ -4993,10 +4940,11 @@ static void shape_cut(PEData *data, int pa_index) static int shape_cut_exec(bContext *C, wmOperator *UNUSED(op)) { + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Scene *scene = CTX_data_scene(C); Object *ob = CTX_data_active_object(C); ParticleEditSettings *pset = PE_settings(scene); - PTCacheEdit *edit = PE_get_current(scene, ob); + PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob); Object *shapeob = pset->shape_object; int selected = count_selected_keys(scene, edit); int lock_root = pset->flag & PE_LOCK_FIRST; @@ -5074,10 +5022,11 @@ void PARTICLE_OT_shape_cut(wmOperatorType *ot) /************************ utilities ******************************/ -int PE_minmax(Scene *scene, ViewLayer *view_layer, float min[3], float max[3]) +int PE_minmax( + Depsgraph *depsgraph, Scene *scene, ViewLayer *view_layer, float min[3], float max[3]) { Object *ob = OBACT(view_layer); - PTCacheEdit *edit = PE_get_current(scene, ob); + PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob); ParticleSystem *psys; ParticleSystemModifierData *psmd_eval = NULL; POINT_P; @@ -5096,15 +5045,13 @@ int PE_minmax(Scene *scene, ViewLayer *view_layer, float min[3], float max[3]) unit_m4(mat); } - LOOP_VISIBLE_POINTS - { + LOOP_VISIBLE_POINTS { if (psys) { psys_mat_hair_to_global( ob, psmd_eval->mesh_final, psys->part->from, psys->particles + p, mat); } - LOOP_SELECTED_KEYS - { + LOOP_SELECTED_KEYS { copy_v3_v3(co, key->co); mul_m4_v3(mat, co); DO_MINMAX(co, min, max); @@ -5181,15 +5128,13 @@ void PE_create_particle_edit( BLI_listbase_clear(&edit->pathcachebufs); pa = psys->particles; - LOOP_POINTS - { + LOOP_POINTS { point->totkey = pa->totkey; point->keys = MEM_callocN(point->totkey * sizeof(PTCacheEditKey), "ParticleEditKeys"); point->flag |= PEP_EDIT_RECALC; hkey = pa->hair; - LOOP_KEYS - { + LOOP_KEYS { key->co = hkey->co; key->time = &hkey->time; key->flag = hkey->editflag; @@ -5217,8 +5162,7 @@ void PE_create_particle_edit( } for (pm = cache->mem_cache.first; pm; pm = pm->next) { - LOOP_POINTS - { + LOOP_POINTS { if (BKE_ptcache_mem_pointers_seek(p, pm) == 0) { continue; } @@ -5421,8 +5365,7 @@ static float calculate_point_length(PTCacheEditPoint *point) { float length = 0.0f; KEY_K; - LOOP_KEYS - { + LOOP_KEYS { if (k > 0) { length += len_v3v3((key - 1)->co, key->co); } @@ -5435,8 +5378,7 @@ static float calculate_average_length(PTCacheEdit *edit) int num_selected = 0; float total_length = 0; POINT_P; - LOOP_SELECTED_POINTS - { + LOOP_SELECTED_POINTS { total_length += calculate_point_length(point); num_selected++; } @@ -5450,8 +5392,7 @@ static void scale_point_factor(PTCacheEditPoint *point, float factor) { float orig_prev_co[3], prev_co[3]; KEY_K; - LOOP_KEYS - { + LOOP_KEYS { if (k == 0) { copy_v3_v3(orig_prev_co, key->co); copy_v3_v3(prev_co, key->co); @@ -5484,8 +5425,7 @@ static void scale_point_to_length(PTCacheEditPoint *point, float length) static void scale_points_to_length(PTCacheEdit *edit, float length) { POINT_P; - LOOP_SELECTED_POINTS - { + LOOP_SELECTED_POINTS { scale_point_to_length(point, length); } recalc_lengths(edit); @@ -5497,7 +5437,7 @@ static int unify_length_exec(bContext *C, wmOperator *UNUSED(op)) Scene *scene = CTX_data_scene(C); Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); - PTCacheEdit *edit = PE_get_current(scene, ob); + PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob); float average_length = calculate_average_length(edit); if (average_length == 0.0f) { diff --git a/source/blender/editors/physics/particle_edit_undo.c b/source/blender/editors/physics/particle_edit_undo.c index 40d90676487..aee79523c87 100644 --- a/source/blender/editors/physics/particle_edit_undo.c +++ b/source/blender/editors/physics/particle_edit_undo.c @@ -109,8 +109,7 @@ static void undoptcache_to_editcache(PTCacheUndo *undo, PTCacheEdit *edit) POINT_P; KEY_K; - LOOP_POINTS - { + LOOP_POINTS { if (psys && psys->particles[p].hair) { MEM_freeN(psys->particles[p].hair); } @@ -133,8 +132,7 @@ static void undoptcache_to_editcache(PTCacheUndo *undo, PTCacheEdit *edit) edit->points = MEM_dupallocN(undo->points); edit->totpoint = undo->totpoint; - LOOP_POINTS - { + LOOP_POINTS { point->keys = MEM_dupallocN(point->keys); } @@ -143,13 +141,11 @@ static void undoptcache_to_editcache(PTCacheUndo *undo, PTCacheEdit *edit) psys->totpart = undo->totpoint; - LOOP_POINTS - { + LOOP_POINTS { pa = psys->particles + p; hkey = pa->hair = MEM_dupallocN(pa->hair); - LOOP_KEYS - { + LOOP_KEYS { key->co = hkey->co; key->time = &hkey->time; hkey++; @@ -174,10 +170,8 @@ static void undoptcache_to_editcache(PTCacheUndo *undo, PTCacheEdit *edit) } BKE_ptcache_mem_pointers_init(pm); - LOOP_POINTS - { - LOOP_KEYS - { + LOOP_POINTS { + LOOP_KEYS { if ((int)key->ftime == (int)pm->frame) { key->co = pm->cur[BPHYS_DATA_LOCATION]; key->vel = pm->cur[BPHYS_DATA_VELOCITY]; @@ -228,10 +222,11 @@ typedef struct ParticleUndoStep { static bool particle_undosys_poll(struct bContext *C) { + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); Object *ob = OBACT(view_layer); - PTCacheEdit *edit = PE_get_current(scene, ob); + PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob); return (edit != NULL); } @@ -240,11 +235,12 @@ static bool particle_undosys_step_encode(struct bContext *C, struct Main *UNUSED(bmain), UndoStep *us_p) { + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); ParticleUndoStep *us = (ParticleUndoStep *)us_p; ViewLayer *view_layer = CTX_data_view_layer(C); us->scene_ref.ptr = CTX_data_scene(C); us->object_ref.ptr = OBACT(view_layer); - PTCacheEdit *edit = PE_get_current(us->scene_ref.ptr, us->object_ref.ptr); + PTCacheEdit *edit = PE_get_current(depsgraph, us->scene_ref.ptr, us->object_ref.ptr); undoptcache_from_editcache(&us->data, edit); return true; } @@ -255,6 +251,7 @@ static void particle_undosys_step_decode(struct bContext *C, int UNUSED(dir), bool UNUSED(is_final)) { + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); /* TODO(campbell): undo_system: use low-level API to set mode. */ ED_object_mode_set(C, OB_MODE_PARTICLE_EDIT); BLI_assert(particle_undosys_poll(C)); @@ -262,7 +259,7 @@ static void particle_undosys_step_decode(struct bContext *C, ParticleUndoStep *us = (ParticleUndoStep *)us_p; Scene *scene = us->scene_ref.ptr; Object *ob = us->object_ref.ptr; - PTCacheEdit *edit = PE_get_current(scene, ob); + PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob); if (edit) { undoptcache_to_editcache(&us->data, edit); DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); diff --git a/source/blender/editors/physics/particle_object.c b/source/blender/editors/physics/particle_object.c index 5a2009845f8..cfb3a400f47 100644 --- a/source/blender/editors/physics/particle_object.c +++ b/source/blender/editors/physics/particle_object.c @@ -1017,13 +1017,11 @@ static void copy_particle_edit(Depsgraph *depsgraph, edit->points = MEM_dupallocN(edit_from->points); pa = psys->particles; - LOOP_POINTS - { + LOOP_POINTS { HairKey *hkey = pa->hair; point->keys = MEM_dupallocN(point->keys); - LOOP_KEYS - { + LOOP_KEYS { key->co = hkey->co; key->time = &hkey->time; key->flag = hkey->editflag; diff --git a/source/blender/editors/physics/rigidbody_constraint.c b/source/blender/editors/physics/rigidbody_constraint.c index 4b1d51ee6c2..303a0714388 100644 --- a/source/blender/editors/physics/rigidbody_constraint.c +++ b/source/blender/editors/physics/rigidbody_constraint.c @@ -115,13 +115,7 @@ bool ED_rigidbody_constraint_add( void ED_rigidbody_constraint_remove(Main *bmain, Scene *scene, Object *ob) { - RigidBodyWorld *rbw = BKE_rigidbody_get_world(scene); - - BKE_rigidbody_remove_constraint(scene, ob); - if (rbw) { - BKE_collection_object_remove(bmain, rbw->constraints, ob, false); - DEG_id_tag_update(&rbw->constraints->id, ID_RECALC_COPY_ON_WRITE); - } + BKE_rigidbody_remove_constraint(bmain, scene, ob, false); DEG_relations_tag_update(bmain); DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); diff --git a/source/blender/editors/physics/rigidbody_object.c b/source/blender/editors/physics/rigidbody_object.c index bc8a1799fa0..43ca421b9d0 100644 --- a/source/blender/editors/physics/rigidbody_object.c +++ b/source/blender/editors/physics/rigidbody_object.c @@ -105,7 +105,7 @@ bool ED_rigidbody_object_add(Main *bmain, Scene *scene, Object *ob, int type, Re void ED_rigidbody_object_remove(Main *bmain, Scene *scene, Object *ob) { - BKE_rigidbody_remove_object(bmain, scene, ob); + BKE_rigidbody_remove_object(bmain, scene, ob, false); DEG_relations_tag_update(bmain); DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c index 053ca3d8f9f..7106af25a82 100644 --- a/source/blender/editors/render/render_internal.c +++ b/source/blender/editors/render/render_internal.c @@ -859,7 +859,7 @@ static void screen_render_cancel(bContext *C, wmOperator *op) static void clean_viewport_memory_base(Base *base) { - if ((base->flag & BASE_VISIBLE) == 0) { + if ((base->flag & BASE_VISIBLE_DEPSGRAPH) == 0) { return; } diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c index 3e001ef25b5..6dc3a1ec1ac 100644 --- a/source/blender/editors/render/render_preview.c +++ b/source/blender/editors/render/render_preview.c @@ -115,7 +115,7 @@ ImBuf *get_brush_icon(Brush *brush) // 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, BKE_main_blendfile_path_from_global()); + BLI_path_abs(path, ID_BLEND_PATH_FROM_GLOBAL(&brush->id)); /* use default colorspaces for brushes */ brush->icon_imbuf = IMB_loadiffname(path, flags, NULL); @@ -474,7 +474,7 @@ static Scene *preview_prepare_scene( } } else if (base->object->type == OB_LAMP) { - base->flag |= BASE_VISIBLE; + base->flag |= BASE_VISIBLE_DEPSGRAPH; } } } diff --git a/source/blender/editors/render/render_shading.c b/source/blender/editors/render/render_shading.c index 9f13431f25a..7970d491877 100644 --- a/source/blender/editors/render/render_shading.c +++ b/source/blender/editors/render/render_shading.c @@ -103,15 +103,17 @@ static Object **object_array_for_shading(bContext *C, uint *r_objects_len) ScrArea *sa = CTX_wm_area(C); SpaceProperties *sbuts = NULL; View3D *v3d = NULL; - if (sa->spacetype == SPACE_PROPERTIES) { - sbuts = sa->spacedata.first; - } - else if (sa->spacetype == SPACE_VIEW3D) { - v3d = sa->spacedata.first; + if (sa != NULL) { + if (sa->spacetype == SPACE_PROPERTIES) { + sbuts = sa->spacedata.first; + } + else if (sa->spacetype == SPACE_VIEW3D) { + v3d = sa->spacedata.first; + } } Object **objects; - if (sbuts && sbuts->pinid && GS(sbuts->pinid->name) == ID_OB) { + if (sbuts != NULL && sbuts->pinid && GS(sbuts->pinid->name) == ID_OB) { objects = MEM_mallocN(sizeof(*objects), __func__); objects[0] = (Object *)sbuts->pinid; *r_objects_len = 1; diff --git a/source/blender/editors/render/render_view.c b/source/blender/editors/render/render_view.c index 3154d5d0985..a54701f8725 100644 --- a/source/blender/editors/render/render_view.c +++ b/source/blender/editors/render/render_view.c @@ -36,6 +36,8 @@ #include "BKE_screen.h" #include "BKE_report.h" +#include "BLT_translation.h" + #include "WM_api.h" #include "WM_types.h" @@ -137,11 +139,11 @@ ScrArea *render_view_open(bContext *C, int mx, int my, ReportList *reports) SpaceImage *sima; bool area_was_image = false; - if (scene->r.displaymode == R_OUTPUT_NONE) { + if (U.render_display_type == USER_RENDER_DISPLAY_NONE) { return NULL; } - if (scene->r.displaymode == R_OUTPUT_WINDOW) { + if (U.render_display_type == USER_RENDER_DISPLAY_WINDOW) { int sizex = 30 * UI_DPI_FAC + (scene->r.xsch * scene->r.size) / 100; int sizey = 60 * UI_DPI_FAC + (scene->r.ysch * scene->r.size) / 100; @@ -154,14 +156,15 @@ ScrArea *render_view_open(bContext *C, int mx, int my, ReportList *reports) } /* changes context! */ - if (WM_window_open_temp(C, mx, my, sizex, sizey, WM_WINDOW_RENDER) == NULL) { + if (WM_window_open_temp( + C, IFACE_("Blender Render"), mx, my, sizex, sizey, SPACE_IMAGE, false) == NULL) { BKE_report(reports, RPT_ERROR, "Failed to open window!"); return NULL; } sa = CTX_wm_area(C); } - else if (scene->r.displaymode == R_OUTPUT_SCREEN) { + else if (U.render_display_type == USER_RENDER_DISPLAY_SCREEN) { sa = CTX_wm_area(C); /* if the active screen is already in fullscreen mode, skip this and diff --git a/source/blender/editors/scene/scene_edit.c b/source/blender/editors/scene/scene_edit.c index 84d6610242a..7705278443f 100644 --- a/source/blender/editors/scene/scene_edit.c +++ b/source/blender/editors/scene/scene_edit.c @@ -29,21 +29,16 @@ #include "BKE_global.h" #include "BKE_layer.h" #include "BKE_library.h" -#include "BKE_library_remap.h" #include "BKE_main.h" #include "BKE_node.h" #include "BKE_report.h" #include "BKE_scene.h" -#include "BKE_workspace.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" #include "BLT_translation.h" -#include "DNA_object_types.h" -#include "DNA_workspace_types.h" - #include "ED_object.h" #include "ED_render.h" #include "ED_scene.h" diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index cb87b076d79..9957fe0515c 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -30,6 +30,7 @@ #include "BLI_blenlib.h" #include "BLI_math.h" +#include "BLI_rand.h" #include "BLI_utildefines.h" #include "BLI_linklist_stack.h" @@ -60,7 +61,6 @@ #include "BLF_api.h" -#include "IMB_imbuf.h" #include "IMB_imbuf_types.h" #include "IMB_metadata.h" @@ -543,20 +543,20 @@ void ED_region_do_draw(bContext *C, ARegion *ar) region_draw_azones(sa, ar); /* for debugging unneeded area redraws and partial redraw */ -#if 0 - GPU_blend(true); - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - immUniformColor4f(drand48(), drand48(), drand48(), 0.1f); - immRectf(pos, - ar->drawrct.xmin - ar->winrct.xmin, - ar->drawrct.ymin - ar->winrct.ymin, - ar->drawrct.xmax - ar->winrct.xmin, - ar->drawrct.ymax - ar->winrct.ymin); - immUnbindProgram(); - GPU_blend(false); -#endif + if (G.debug_value == 888) { + GPU_blend(true); + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor4f(BLI_thread_frand(0), BLI_thread_frand(0), BLI_thread_frand(0), 0.1f); + immRectf(pos, + ar->drawrct.xmin - ar->winrct.xmin, + ar->drawrct.ymin - ar->winrct.ymin, + ar->drawrct.xmax - ar->winrct.xmin, + ar->drawrct.ymax - ar->winrct.ymin); + immUnbindProgram(); + GPU_blend(false); + } memset(&ar->drawrct, 0, sizeof(ar->drawrct)); @@ -1827,7 +1827,7 @@ void ED_region_cursor_set(wmWindow *win, ScrArea *sa, ARegion *ar) if (WM_cursor_set_from_tool(win, sa, ar)) { return; } - WM_cursor_set(win, CURSOR_STD); + WM_cursor_set(win, WM_CURSOR_DEFAULT); } } @@ -2869,7 +2869,7 @@ void ED_region_info_draw(ARegion *ar, float fill_color[4], const bool full_redraw) { - ED_region_info_draw_multiline(ar, (const char * [2]){text, NULL}, fill_color, full_redraw); + ED_region_info_draw_multiline(ar, (const char *[2]){text, NULL}, fill_color, full_redraw); } #define MAX_METADATA_STR 1024 diff --git a/source/blender/editors/screen/area_query.c b/source/blender/editors/screen/area_query.c index 420d70e63fb..46559efc614 100644 --- a/source/blender/editors/screen/area_query.c +++ b/source/blender/editors/screen/area_query.c @@ -28,8 +28,6 @@ #include "RNA_types.h" -#include "WM_api.h" - #include "ED_screen.h" #include "UI_interface.h" diff --git a/source/blender/editors/screen/area_utils.c b/source/blender/editors/screen/area_utils.c index 1a99210b73d..61fb9d5a3a8 100644 --- a/source/blender/editors/screen/area_utils.c +++ b/source/blender/editors/screen/area_utils.c @@ -28,7 +28,6 @@ #include "RNA_access.h" #include "RNA_types.h" -#include "WM_api.h" #include "WM_message.h" #include "ED_screen.h" diff --git a/source/blender/editors/screen/glutil.c b/source/blender/editors/screen/glutil.c index d8926bfc460..dc435efd86b 100644 --- a/source/blender/editors/screen/glutil.c +++ b/source/blender/editors/screen/glutil.c @@ -24,8 +24,6 @@ #include <stdio.h> #include <string.h> -#include "MEM_guardedalloc.h" - #include "DNA_userdef_types.h" #include "DNA_vec_types.h" diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c index 6f8b25f782b..f7742c5e50a 100644 --- a/source/blender/editors/screen/screen_context.c +++ b/source/blender/editors/screen/screen_context.c @@ -29,36 +29,28 @@ #include "DNA_object_types.h" #include "DNA_armature_types.h" -#include "DNA_brush_types.h" #include "DNA_gpencil_types.h" #include "DNA_sequence_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" #include "DNA_space_types.h" #include "DNA_windowmanager_types.h" -#include "DNA_workspace_types.h" #include "BLI_utildefines.h" -#include "BKE_brush.h" #include "BKE_context.h" #include "BKE_object.h" #include "BKE_action.h" #include "BKE_armature.h" -#include "BKE_paint.h" #include "BKE_gpencil.h" #include "BKE_layer.h" #include "BKE_sequencer.h" -#include "BKE_workspace.h" - -#include "DEG_depsgraph.h" #include "RNA_access.h" #include "ED_armature.h" #include "ED_gpencil.h" #include "ED_anim_api.h" -#include "ED_uvedit.h" #include "WM_api.h" #include "UI_interface.h" diff --git a/source/blender/editors/screen/screen_draw.c b/source/blender/editors/screen/screen_draw.c index db744df02e7..a6b8bba73e3 100644 --- a/source/blender/editors/screen/screen_draw.c +++ b/source/blender/editors/screen/screen_draw.c @@ -21,7 +21,7 @@ #include "ED_screen.h" #include "GPU_batch_presets.h" -#include "GPU_extensions.h" +#include "GPU_platform.h" #include "GPU_framebuffer.h" #include "GPU_immediate.h" #include "GPU_matrix.h" @@ -32,9 +32,7 @@ #include "BLI_rect.h" #include "WM_api.h" -#include "WM_types.h" -#include "UI_interface.h" #include "UI_resources.h" #include "screen_intern.h" diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index b37aa47aba6..bbdddfadc30 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -41,20 +41,18 @@ #include "BKE_layer.h" #include "BKE_library.h" #include "BKE_main.h" -#include "BKE_node.h" #include "BKE_screen.h" #include "BKE_scene.h" +#include "BKE_sound.h" #include "BKE_workspace.h" #include "WM_api.h" #include "WM_types.h" -#include "ED_object.h" #include "ED_screen.h" #include "ED_screen_types.h" #include "ED_clip.h" #include "ED_node.h" -#include "ED_render.h" #include "UI_interface.h" @@ -518,6 +516,17 @@ void ED_screen_ensure_updated(wmWindowManager *wm, wmWindow *win, bScreen *scree } } +/** + * Utility to exit and free an area-region. Screen level regions (menus/popups) need to be treated + * slightly differently, see #ui_region_temp_remove(). + */ +void ED_region_remove(bContext *C, ScrArea *sa, ARegion *ar) +{ + ED_region_exit(C, ar); + BKE_area_region_free(sa->type, ar); + BLI_freelinkN(&sa->regionbase, ar); +} + /* *********** exit calls are for closing running stuff ******** */ void ED_region_exit(bContext *C, ARegion *ar) @@ -583,6 +592,11 @@ void ED_screen_exit(bContext *C, wmWindow *window, bScreen *screen) if (screen->animtimer) { WM_event_remove_timer(wm, window, screen->animtimer); + + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + Scene *scene = WM_window_get_active_scene(prevwin); + Scene *scene_eval = (Scene *)DEG_get_evaluated_id(depsgraph, &scene->id); + BKE_sound_stop_scene(scene_eval); } screen->animtimer = NULL; screen->scrubbing = false; @@ -630,14 +644,14 @@ static void screen_cursor_set(wmWindow *win, const int xy[2]) if (sa) { if (az->type == AZONE_AREA) { - WM_cursor_set(win, CURSOR_EDIT); + WM_cursor_set(win, WM_CURSOR_EDIT); } else if (az->type == AZONE_REGION) { if (az->edge == AE_LEFT_TO_TOPRIGHT || az->edge == AE_RIGHT_TO_TOPLEFT) { - WM_cursor_set(win, CURSOR_X_MOVE); + WM_cursor_set(win, WM_CURSOR_X_MOVE); } else { - WM_cursor_set(win, CURSOR_Y_MOVE); + WM_cursor_set(win, WM_CURSOR_Y_MOVE); } } } @@ -646,14 +660,14 @@ static void screen_cursor_set(wmWindow *win, const int xy[2]) if (actedge) { if (screen_geom_edge_is_horizontal(actedge)) { - WM_cursor_set(win, CURSOR_Y_MOVE); + WM_cursor_set(win, WM_CURSOR_Y_MOVE); } else { - WM_cursor_set(win, CURSOR_X_MOVE); + WM_cursor_set(win, WM_CURSOR_X_MOVE); } } else { - WM_cursor_set(win, CURSOR_STD); + WM_cursor_set(win, WM_CURSOR_DEFAULT); } } } @@ -1346,6 +1360,54 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *sa, const s return sc->areabase.first; } +/** + * Wrapper to open a temporary space either as fullscreen space, or as separate window, as defined + * by \a display_type. + * + * \param title: Title to set for the window, if a window is spawned. + * \param x, y: Position of the window, if a window is spawned. + * \param sizex, sizey: Dimensions of the window, if a window is spawned. + */ +ScrArea *ED_screen_temp_space_open(bContext *C, + const char *title, + int x, + int y, + int sizex, + int sizey, + eSpace_Type space_type, + int display_type, + bool dialog) +{ + ScrArea *sa = NULL; + + switch (display_type) { + case USER_TEMP_SPACE_DISPLAY_WINDOW: + if (WM_window_open_temp(C, title, x, y, sizex, sizey, (int)space_type, dialog)) { + sa = CTX_wm_area(C); + } + break; + case USER_TEMP_SPACE_DISPLAY_FULLSCREEN: { + ScrArea *ctx_sa = CTX_wm_area(C); + + if (ctx_sa->full) { + sa = ctx_sa; + ED_area_newspace(C, ctx_sa, space_type, true); + /* we already had a fullscreen here -> mark new space as a stacked fullscreen */ + sa->flag |= (AREA_FLAG_STACKED_FULLSCREEN | AREA_FLAG_TEMP_TYPE); + } + else if (ctx_sa->spacetype == space_type) { + sa = ED_screen_state_toggle(C, CTX_wm_window(C), ctx_sa, SCREENMAXIMIZED); + } + else { + sa = ED_screen_full_newspace(C, ctx_sa, (int)space_type); + } + break; + } + } + + return sa; +} + /* update frame rate info for viewport drawing */ void ED_refresh_viewport_fps(bContext *C) { diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index f29535a7f0b..cc1f53eabde 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -38,7 +38,6 @@ #include "DNA_lattice_types.h" #include "DNA_object_types.h" #include "DNA_curve_types.h" -#include "DNA_gpencil_types.h" #include "DNA_scene_types.h" #include "DNA_meta_types.h" #include "DNA_mesh_types.h" @@ -48,7 +47,6 @@ #include "DNA_userdef_types.h" #include "BKE_context.h" -#include "BKE_customdata.h" #include "BKE_editmesh.h" #include "BKE_fcurve.h" #include "BKE_global.h" @@ -1043,28 +1041,28 @@ static int actionzone_modal(bContext *C, wmOperator *op, const wmEvent *event) if (BKE_screen_find_area_xy(sc, SPACE_TYPE_ANY, event->x, event->y) == sad->sa1) { /* Same area, so possible split. */ WM_cursor_set( - win, (ELEM(sad->gesture_dir, 'n', 's')) ? BC_V_SPLITCURSOR : BC_H_SPLITCURSOR); + win, (ELEM(sad->gesture_dir, 'n', 's')) ? WM_CURSOR_H_SPLIT : WM_CURSOR_V_SPLIT); is_gesture = (delta_max > split_threshold); } else { /* Different area, so possible join. */ if (sad->gesture_dir == 'n') { - WM_cursor_set(win, BC_N_ARROWCURSOR); + WM_cursor_set(win, WM_CURSOR_N_ARROW); } else if (sad->gesture_dir == 's') { - WM_cursor_set(win, BC_S_ARROWCURSOR); + WM_cursor_set(win, WM_CURSOR_S_ARROW); } else if (sad->gesture_dir == 'e') { - WM_cursor_set(win, BC_E_ARROWCURSOR); + WM_cursor_set(win, WM_CURSOR_E_ARROW); } else { - WM_cursor_set(win, BC_W_ARROWCURSOR); + WM_cursor_set(win, WM_CURSOR_W_ARROW); } is_gesture = (delta_max > join_threshold); } } else { - WM_cursor_set(CTX_wm_window(C), BC_CROSSCURSOR); + WM_cursor_set(CTX_wm_window(C), WM_CURSOR_CROSS); is_gesture = false; } } @@ -1229,7 +1227,7 @@ static int area_swap_invoke(bContext *C, wmOperator *op, const wmEvent *event) } /* add modal handler */ - WM_cursor_modal_set(CTX_wm_window(C), BC_SWAPAREA_CURSOR); + WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_SWAP_AREA); WM_event_add_modal_handler(C, op); return OPERATOR_RUNNING_MODAL; @@ -2121,7 +2119,7 @@ static void area_split_preview_update_cursor(bContext *C, wmOperator *op) { wmWindow *win = CTX_wm_window(C); int dir = RNA_enum_get(op->ptr, "direction"); - WM_cursor_set(win, (dir == 'n' || dir == 's') ? BC_V_SPLITCURSOR : BC_H_SPLITCURSOR); + WM_cursor_set(win, (dir == 'n' || dir == 's') ? WM_CURSOR_H_SPLIT : WM_CURSOR_V_SPLIT); } /* UI callback, adds new handler */ @@ -3420,19 +3418,19 @@ static int area_join_modal(bContext *C, wmOperator *op, const wmEvent *event) } if (dir == 1) { - WM_cursor_set(win, BC_N_ARROWCURSOR); + WM_cursor_set(win, WM_CURSOR_N_ARROW); } else if (dir == 3) { - WM_cursor_set(win, BC_S_ARROWCURSOR); + WM_cursor_set(win, WM_CURSOR_S_ARROW); } else if (dir == 2) { - WM_cursor_set(win, BC_E_ARROWCURSOR); + WM_cursor_set(win, WM_CURSOR_E_ARROW); } else if (dir == 0) { - WM_cursor_set(win, BC_W_ARROWCURSOR); + WM_cursor_set(win, WM_CURSOR_W_ARROW); } else { - WM_cursor_set(win, BC_STOPCURSOR); + WM_cursor_set(win, WM_CURSOR_STOP); } break; @@ -3848,10 +3846,7 @@ static int region_quadview_exec(bContext *C, wmOperator *op) for (ar = sa->regionbase.first; ar; ar = arn) { arn = ar->next; if (ar->alignment == RGN_ALIGN_QSPLIT) { - ED_region_exit(C, ar); - BKE_area_region_free(sa->type, ar); - BLI_remlink(&sa->regionbase, ar); - MEM_freeN(ar); + ED_region_remove(C, sa, ar); } } ED_area_tag_redraw(sa); @@ -3951,7 +3946,7 @@ static int region_toggle_exec(bContext *C, wmOperator *op) region = CTX_wm_region(C); } - if (region) { + if (region && (region->alignment != RGN_ALIGN_NONE)) { ED_region_toggle_hidden(C, region); } ED_region_tag_redraw(region); @@ -4833,7 +4828,14 @@ static int userpref_show_invoke(bContext *C, wmOperator *op, const wmEvent *even int sizey = 520 * UI_DPI_FAC; /* changes context! */ - if (WM_window_open_temp(C, event->x, event->y, sizex, sizey, WM_WINDOW_USERPREFS) != NULL) { + if (WM_window_open_temp(C, + IFACE_("Blender Preferences"), + event->x, + event->y, + sizex, + sizey, + SPACE_USERPREF, + false) != NULL) { /* The header only contains the editor switcher and looks empty. * So hiding in the temp window makes sense. */ ScrArea *area = CTX_wm_area(C); @@ -4882,7 +4884,16 @@ static int drivers_editor_show_invoke(bContext *C, wmOperator *op, const wmEvent but = UI_context_active_but_prop_get(C, &ptr, &prop, &index); /* changes context! */ - if (WM_window_open_temp(C, event->x, event->y, sizex, sizey, WM_WINDOW_DRIVERS) != NULL) { + if (WM_window_open_temp(C, + IFACE_("Blender Drivers Editor"), + event->x, + event->y, + sizex, + sizey, + SPACE_GRAPH, + false) != NULL) { + ED_drivers_editor_init(C, CTX_wm_area(C)); + /* activate driver F-Curve for the property under the cursor */ if (but) { FCurve *fcu; @@ -4938,7 +4949,14 @@ static int info_log_show_invoke(bContext *C, wmOperator *op, const wmEvent *even int shift_y = 480; /* changes context! */ - if (WM_window_open_temp(C, event->x, event->y + shift_y, sizex, sizey, WM_WINDOW_INFO) != NULL) { + if (WM_window_open_temp(C, + IFACE_("Blender Info Log"), + event->x, + event->y + shift_y, + sizex, + sizey, + SPACE_INFO, + false) != NULL) { return OPERATOR_FINISHED; } else { diff --git a/source/blender/editors/screen/screen_user_menu.c b/source/blender/editors/screen/screen_user_menu.c index 26849edeb44..661c17f55d2 100644 --- a/source/blender/editors/screen/screen_user_menu.c +++ b/source/blender/editors/screen/screen_user_menu.c @@ -52,27 +52,43 @@ #include "RNA_access.h" /* -------------------------------------------------------------------- */ +/** \name Internal Utilities + * \{ */ + +static const char *screen_menu_context_string(const bContext *C, const SpaceLink *sl) +{ + if (sl->spacetype == SPACE_NODE) { + const SpaceNode *snode = (const SpaceNode *)sl; + return snode->tree_idname; + } + return CTX_data_mode_string(C); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Menu Type * \{ */ bUserMenu **ED_screen_user_menus_find(const bContext *C, uint *r_len) { SpaceLink *sl = CTX_wm_space_data(C); - const char *context = CTX_data_mode_string(C); if (sl == NULL) { *r_len = 0; return NULL; } + const char *context_mode = CTX_data_mode_string(C); + const char *context = screen_menu_context_string(C, sl); uint array_len = 3; bUserMenu **um_array = MEM_calloc_arrayN(array_len, sizeof(*um_array), __func__); um_array[0] = BKE_blender_user_menu_find(&U.user_menus, sl->spacetype, context); um_array[1] = (sl->spacetype != SPACE_TOPBAR) ? - BKE_blender_user_menu_find(&U.user_menus, SPACE_TOPBAR, context) : + BKE_blender_user_menu_find(&U.user_menus, SPACE_TOPBAR, context_mode) : NULL; um_array[2] = (sl->spacetype == SPACE_VIEW3D) ? - BKE_blender_user_menu_find(&U.user_menus, SPACE_PROPERTIES, context) : + BKE_blender_user_menu_find(&U.user_menus, SPACE_PROPERTIES, context_mode) : NULL; *r_len = array_len; @@ -82,7 +98,7 @@ bUserMenu **ED_screen_user_menus_find(const bContext *C, uint *r_len) bUserMenu *ED_screen_user_menu_ensure(bContext *C) { SpaceLink *sl = CTX_wm_space_data(C); - const char *context = CTX_data_mode_string(C); + const char *context = screen_menu_context_string(C, sl); return BKE_blender_user_menu_ensure(&U.user_menus, sl->spacetype, context); } diff --git a/source/blender/editors/screen/workspace_edit.c b/source/blender/editors/screen/workspace_edit.c index 61b737589c8..bbb959c27ff 100644 --- a/source/blender/editors/screen/workspace_edit.c +++ b/source/blender/editors/screen/workspace_edit.c @@ -29,18 +29,13 @@ #include "BKE_appdir.h" #include "BKE_blendfile.h" #include "BKE_context.h" -#include "BKE_idcode.h" -#include "BKE_layer.h" #include "BKE_library.h" #include "BKE_main.h" -#include "BKE_report.h" -#include "BKE_scene.h" #include "BKE_screen.h" #include "BKE_workspace.h" #include "BLO_readfile.h" -#include "DNA_object_types.h" #include "DNA_screen_types.h" #include "DNA_windowmanager_types.h" #include "DNA_workspace_types.h" @@ -49,13 +44,9 @@ #include "ED_object.h" #include "ED_screen.h" -#include "MEM_guardedalloc.h" - #include "RNA_access.h" #include "RNA_define.h" -#include "DEG_depsgraph.h" - #include "UI_interface.h" #include "UI_resources.h" @@ -63,7 +54,6 @@ #include "WM_api.h" #include "WM_types.h" -#include "WM_toolsystem.h" #include "screen_intern.h" diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index 752a5c36010..a5cc262ddcd 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -31,6 +31,7 @@ set(INC ../../render/extern/include ../../windowmanager ../../../../intern/atomic + ../../../../intern/clog ../../../../intern/glew-mx ../../../../intern/guardedalloc ) @@ -47,7 +48,6 @@ set(SRC paint_image.c paint_image_2d.c paint_image_proj.c - paint_image_undo.c paint_mask.c paint_ops.c paint_stroke.c diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index 5c83bdf0012..c59ab6279cd 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -91,6 +91,7 @@ typedef struct CursorSnapshot { GLuint overlay_texture; int size; int zoom; + int curve_preset; } CursorSnapshot; static TexSnapshot primary_snap = {0}; @@ -409,7 +410,7 @@ static void load_tex_cursor_task_cb(void *__restrict userdata, if (len <= 1.0f) { float avg = BKE_brush_curve_strength_clamped(br, len, 1.0f); /* Falloff curve */ - buffer[index] = 255 - (GLubyte)(255 * avg); + buffer[index] = (GLubyte)(255 * avg); } else { buffer[index] = 0; @@ -426,7 +427,8 @@ static int load_tex_cursor(Brush *br, ViewContext *vc, float zoom) int size; const bool refresh = !cursor_snap.overlay_texture || - (overlay_flags & PAINT_OVERLAY_INVALID_CURVE) || cursor_snap.zoom != zoom; + (overlay_flags & PAINT_OVERLAY_INVALID_CURVE) || cursor_snap.zoom != zoom || + cursor_snap.curve_preset != br->curve_preset; init = (cursor_snap.overlay_texture != 0); @@ -506,6 +508,7 @@ static int load_tex_cursor(Brush *br, ViewContext *vc, float zoom) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + cursor_snap.curve_preset = br->curve_preset; BKE_paint_reset_overlay_invalid(PAINT_OVERLAY_INVALID_CURVE); return 1; @@ -1093,17 +1096,15 @@ static bool ommit_cursor_drawing(Paint *paint, ePaintMode mode, Brush *brush) return true; } -static void cursor_draw_point_screen_space(const uint gpuattr, - const ARegion *ar, - const float true_location[3], - const float obmat[4][4]) +static void cursor_draw_point_screen_space( + const uint gpuattr, const ARegion *ar, float true_location[3], float obmat[4][4], int size) { float translation_vertex_cursor[3], location[3]; copy_v3_v3(location, true_location); mul_m4_v3(obmat, location); ED_view3d_project(ar, location, translation_vertex_cursor); imm_draw_circle_fill_3d( - gpuattr, translation_vertex_cursor[0], translation_vertex_cursor[1], 3, 10); + gpuattr, translation_vertex_cursor[0], translation_vertex_cursor[1], size, 10); } static void cursor_draw_tiling_preview(const uint gpuattr, @@ -1145,7 +1146,7 @@ static void cursor_draw_tiling_preview(const uint gpuattr, for (dim = 0; dim < 3; dim++) { location[dim] = cur[dim] * step[dim] + orgLoc[dim]; } - cursor_draw_point_screen_space(gpuattr, ar, location, ob->obmat); + cursor_draw_point_screen_space(gpuattr, ar, location, ob->obmat, 3); } } } @@ -1166,7 +1167,7 @@ static void cursor_draw_point_with_symmetry(const uint gpuattr, /* Axis Symmetry */ flip_v3_v3(location, true_location, (char)i); - cursor_draw_point_screen_space(gpuattr, ar, location, ob->obmat); + cursor_draw_point_screen_space(gpuattr, ar, location, ob->obmat, 3); /* Tiling */ cursor_draw_tiling_preview(gpuattr, ar, location, sd, ob, radius); @@ -1181,7 +1182,7 @@ static void cursor_draw_point_with_symmetry(const uint gpuattr, mul_m4_v3(symm_rot_mat, location); cursor_draw_tiling_preview(gpuattr, ar, location, sd, ob, radius); - cursor_draw_point_screen_space(gpuattr, ar, location, ob->obmat); + cursor_draw_point_screen_space(gpuattr, ar, location, ob->obmat, 3); } } } @@ -1191,7 +1192,13 @@ static void cursor_draw_point_with_symmetry(const uint gpuattr, static void sculpt_geometry_preview_lines_draw(const uint gpuattr, SculptSession *ss) { immUniformColor4f(1.0f, 1.0f, 1.0f, 0.6f); - GPU_depth_test(true); + + /* Cursor normally draws on top, but for this part we need depth tests. */ + const bool depth_test = GPU_depth_test_enabled(); + if (!depth_test) { + GPU_depth_test(true); + } + GPU_line_width(1.0f); if (ss->preview_vert_index_count > 0) { immBegin(GPU_PRIM_LINES, ss->preview_vert_index_count); @@ -1200,10 +1207,24 @@ static void sculpt_geometry_preview_lines_draw(const uint gpuattr, SculptSession } immEnd(); } + + /* Restore depth test value. */ + if (!depth_test) { + GPU_depth_test(false); + } +} + +static bool paint_use_2d_cursor(ePaintMode mode) +{ + if (mode >= PAINT_MODE_TEXTURE_3D) { + return true; + } + return false; } static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) { + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Scene *scene = CTX_data_scene(C); ARegion *ar = CTX_wm_region(C); UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; @@ -1211,6 +1232,9 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) Brush *brush = BKE_paint_brush(paint); ePaintMode mode = BKE_paintmode_get_active_from_context(C); + /* 2d or 3d painting? */ + const bool use_2d_cursor = paint_use_2d_cursor(mode); + /* check that brush drawing is enabled */ if (ommit_cursor_drawing(paint, mode, brush)) { return; @@ -1219,7 +1243,7 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) /* can't use stroke vc here because this will be called during * mouse over too, not just during a stroke */ ViewContext vc; - ED_view3d_viewcontext_init(C, &vc); + ED_view3d_viewcontext_init(C, &vc, depsgraph); if (vc.rv3d && (vc.rv3d->rflag & RV3D_NAVIGATING)) { return; @@ -1237,7 +1261,7 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) /* set various defaults */ const float *outline_col = brush->add_col; - const float outline_alpha = 0.5f; + const float outline_alpha = 0.7f; float translation[2] = {x, y}; float final_radius = (BKE_brush_size_get(scene, brush) * zoomx); @@ -1251,33 +1275,6 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) /* draw overlay */ bool alpha_overlay_active = paint_draw_alpha_overlay(ups, brush, &vc, x, y, zoomx, mode); - /* TODO: as sculpt and other paint modes are unified, this - * special mode of drawing will go away */ - if ((mode == PAINT_MODE_SCULPT) && vc.obact->sculpt) { - float location[3]; - int pixel_radius; - - /* test if brush is over the mesh */ - bool hit = sculpt_get_brush_geometry(C, &vc, x, y, &pixel_radius, location, ups); - - if (BKE_brush_use_locked_size(scene, brush)) { - BKE_brush_size_set(scene, brush, pixel_radius); - } - - /* check if brush is subtracting, use different color then */ - /* TODO: no way currently to know state of pen flip or - * invert key modifier without starting a stroke */ - if (((ups->draw_inverted == 0) ^ ((brush->flag & BRUSH_DIR_IN) == 0)) && - BKE_brush_sculpt_has_secondary_color(brush)) { - outline_col = brush->sub_col; - } - - /* only do if brush is over the mesh */ - if (hit) { - paint_cursor_on_hit(ups, brush, &vc, location); - } - } - if (ups->draw_anchored) { final_radius = ups->anchored_size; copy_v2_fl2(translation, @@ -1290,148 +1287,239 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) GPU_blend(true); /* TODO: also set blend mode? */ GPU_line_smooth(true); - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + if (use_2d_cursor) { + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - /* set brush color */ - immUniformColor3fvAlpha(outline_col, outline_alpha); + immUniformColor3fvAlpha(outline_col, outline_alpha); - /* draw brush outline */ - if (ups->stroke_active && BKE_brush_use_size_pressure(scene, brush)) { - /* inner at full alpha */ - imm_draw_circle_wire_2d( - pos, translation[0], translation[1], final_radius * ups->size_pressure_value, 40); - /* outer at half alpha */ - immUniformColor3fvAlpha(outline_col, outline_alpha * 0.5f); + /* draw brush outline */ + if (ups->stroke_active && BKE_brush_use_size_pressure(scene, brush)) { + imm_draw_circle_wire_2d( + pos, translation[0], translation[1], final_radius * ups->size_pressure_value, 40); + /* outer at half alpha */ + immUniformColor3fvAlpha(outline_col, outline_alpha * 0.5f); + } + + GPU_line_width(1.0f); + imm_draw_circle_wire_2d(pos, translation[0], translation[1], final_radius, 40); } + else { /* 3d painting */ + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + + /* TODO: as sculpt and other paint modes are unified, this + * special mode of drawing will go away */ + Object *obact = vc.obact; + SculptSession *ss = obact ? obact->sculpt : NULL; + if ((mode == PAINT_MODE_SCULPT) && ss) { + float location[3]; + int pixel_radius; + + /* test if brush is over the mesh */ + bool hit = sculpt_get_brush_geometry(C, &vc, x, y, &pixel_radius, location, ups); + + if (BKE_brush_use_locked_size(scene, brush)) { + BKE_brush_size_set(scene, brush, pixel_radius); + } - /* Only sculpt mode cursor for now */ + /* check if brush is subtracting, use different color then */ + /* TODO: no way currently to know state of pen flip or + * invert key modifier without starting a stroke */ + if (((ups->draw_inverted == 0) ^ ((brush->flag & BRUSH_DIR_IN) == 0)) && + BKE_brush_sculpt_has_secondary_color(brush)) { + outline_col = brush->sub_col; + } - /* Disable for PBVH_GRIDS */ - SculptSession *ss = vc.obact->sculpt; - bool is_multires = ss && ss->pbvh && BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS; + /* only do if brush is over the mesh */ + if (hit) { + paint_cursor_on_hit(ups, brush, &vc, location); + } + } - if ((mode == PAINT_MODE_SCULPT) && ss && !is_multires && - !(brush->falloff_shape & BRUSH_AIRBRUSH)) { - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - wmWindow *win = CTX_wm_window(C); + immUniformColor3fvAlpha(outline_col, outline_alpha); - /* Update WM mouse cursor, disable when the 3D brush cursor is enabled */ - if (sd->paint.brush->overlay_flags & BRUSH_OVERLAY_CURSOR) { - WM_cursor_set(win, CURSOR_STD); + if (ups->stroke_active && BKE_brush_use_size_pressure(scene, brush)) { + imm_draw_circle_wire_3d( + pos, translation[0], translation[1], final_radius * ups->size_pressure_value, 40); + /* outer at half alpha */ + immUniformColor3fvAlpha(outline_col, outline_alpha * 0.5f); } - else { - WM_cursor_set(win, CURSOR_EDIT); + + /* Only sculpt mode cursor for now */ + /* Disable for PBVH_GRIDS */ + bool is_multires = ss && ss->pbvh && BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS; + + SculptCursorGeometryInfo gi; + float mouse[2] = {x - ar->winrct.xmin, y - ar->winrct.ymin}; + int prev_active_vertex_index = -1; + bool is_cursor_over_mesh = false; + + /* Update the active vertex */ + if ((mode == PAINT_MODE_SCULPT) && ss && !ups->stroke_active) { + prev_active_vertex_index = ss->active_vertex_index; + is_cursor_over_mesh = sculpt_cursor_geometry_info_update( + C, &gi, mouse, (brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE)); } + /* Use special paint crosshair cursor in all paint modes*/ + wmWindow *win = CTX_wm_window(C); + WM_cursor_set(win, WM_CURSOR_PAINT); - if (!ups->stroke_active) { - SculptCursorGeometryInfo gi; - float mouse[2] = {x - ar->winrct.xmin, y - ar->winrct.ymin}; - if (sculpt_cursor_geometry_info_update(C, &gi, mouse, true) && !alpha_overlay_active) { + if ((mode == PAINT_MODE_SCULPT) && ss && + (brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE)) { + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - float rds; - if (!BKE_brush_use_locked_size(scene, brush)) { - rds = paint_calc_object_space_radius(&vc, gi.location, BKE_brush_size_get(scene, brush)); - } - else { - rds = BKE_brush_unprojected_radius_get(scene, brush); - } + if (!ups->stroke_active) { + bool update_previews = false; + if (is_cursor_over_mesh && !alpha_overlay_active) { - wmViewport(&ar->winrct); + if (prev_active_vertex_index != ss->active_vertex_index) { + update_previews = true; + } - /* Draw 3D active vertex preview with symmetry*/ - if (len_v3v3(gi.active_vertex_co, gi.location) < rds) { - cursor_draw_point_with_symmetry(pos, ar, gi.active_vertex_co, sd, vc.obact, rds); - } + float rds; + if (!BKE_brush_use_locked_size(scene, brush)) { + rds = paint_calc_object_space_radius( + &vc, gi.location, BKE_brush_size_get(scene, brush)); + } + else { + rds = BKE_brush_unprojected_radius_get(scene, brush); + } - /* Draw 3D brush cursor */ - GPU_matrix_push_projection(); - ED_view3d_draw_setup_view(CTX_wm_window(C), - CTX_data_depsgraph_pointer(C), - CTX_data_scene(C), - ar, - CTX_wm_view3d(C), - NULL, - NULL, - NULL); - - float cursor_trans[4][4], cursor_rot[4][4]; - float z_axis[4] = {0.0f, 0.0f, 1.0f, 0.0f}; - float quat[4]; - - copy_m4_m4(cursor_trans, vc.obact->obmat); - translate_m4(cursor_trans, gi.location[0], gi.location[1], gi.location[2]); - rotation_between_vecs_to_quat(quat, z_axis, gi.normal); - quat_to_mat4(cursor_rot, quat); - - GPU_matrix_push(); - GPU_matrix_mul(cursor_trans); - GPU_matrix_mul(cursor_rot); - imm_draw_circle_wire_3d(pos, 0, 0, rds, 40); - GPU_matrix_pop(); - - /* Update and draw dynamic mesh preview lines */ - GPU_matrix_push(); - GPU_matrix_mul(vc.obact->obmat); - if (brush->sculpt_tool == SCULPT_TOOL_GRAB && brush->flag & BRUSH_GRAB_ACTIVE_VERTEX) { - if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && ss->modifiers_active) { - sculpt_geometry_preview_lines_update(C, ss, rds); - sculpt_geometry_preview_lines_draw(pos, ss); + wmViewport(&ar->winrct); + + /* Draw 3D active vertex preview with symmetry*/ + if (len_v3v3(gi.active_vertex_co, gi.location) < rds) { + cursor_draw_point_with_symmetry(pos, ar, gi.active_vertex_co, sd, vc.obact, rds); + } + + /* Draw pose brush origin */ + if (brush->sculpt_tool == SCULPT_TOOL_POSE) { + immUniformColor4f(1.0f, 1.0f, 1.0f, 0.8f); + if (update_previews) { + BKE_sculpt_update_object_for_edit(depsgraph, vc.obact, true, false); + sculpt_pose_calc_pose_data( + sd, vc.obact, ss, gi.location, rds, brush->pose_offset, ss->pose_origin, NULL); + } + cursor_draw_point_screen_space(pos, ar, ss->pose_origin, vc.obact->obmat, 5); + } + + /* Draw 3D brush cursor */ + GPU_matrix_push_projection(); + ED_view3d_draw_setup_view(CTX_wm_window(C), + CTX_data_depsgraph_pointer(C), + CTX_data_scene(C), + ar, + CTX_wm_view3d(C), + NULL, + NULL, + NULL); + + float cursor_trans[4][4], cursor_rot[4][4]; + float z_axis[4] = {0.0f, 0.0f, 1.0f, 0.0f}; + float quat[4]; + + copy_m4_m4(cursor_trans, vc.obact->obmat); + translate_m4(cursor_trans, gi.location[0], gi.location[1], gi.location[2]); + rotation_between_vecs_to_quat(quat, z_axis, gi.normal); + quat_to_mat4(cursor_rot, quat); + + GPU_matrix_push(); + GPU_matrix_mul(cursor_trans); + GPU_matrix_mul(cursor_rot); + immUniformColor3fvAlpha(outline_col, outline_alpha); + GPU_line_width(2.0f); + imm_draw_circle_wire_3d(pos, 0, 0, rds, 80); + GPU_line_width(1.0f); + immUniformColor3fvAlpha(outline_col, outline_alpha * 0.5f); + imm_draw_circle_wire_3d(pos, 0, 0, rds * clamp_f(brush->alpha, 0.0f, 1.0f), 80); + GPU_matrix_pop(); + + /* Update and draw dynamic mesh preview lines */ + GPU_matrix_push(); + GPU_matrix_mul(vc.obact->obmat); + if (brush->sculpt_tool == SCULPT_TOOL_GRAB && (brush->flag & BRUSH_GRAB_ACTIVE_VERTEX) && + !is_multires) { + if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && ss->deform_modifiers_active) { + sculpt_geometry_preview_lines_update(C, ss, rds); + sculpt_geometry_preview_lines_draw(pos, ss); + } + } + + /* Draw pose brush line preview */ + if (brush->sculpt_tool == SCULPT_TOOL_POSE) { + immUniformColor4f(1.0f, 1.0f, 1.0f, 0.8f); + GPU_line_width(2.0f); + immBegin(GPU_PRIM_LINES, 2); + immVertex3fv(pos, ss->pose_origin); + immVertex3fv(pos, gi.location); + immEnd(); } - } - GPU_matrix_pop(); - GPU_matrix_pop_projection(); + GPU_matrix_pop(); - wmWindowViewport(win); + GPU_matrix_pop_projection(); + + wmWindowViewport(win); + } + else { + /* Draw default cursor when the mouse is not over the mesh or there are no supported + * overlays active */ + GPU_line_width(1.0f); + /* Reduce alpha to increase the contrast when the cursor is over the mesh */ + immUniformColor3fvAlpha(outline_col, outline_alpha * 0.8); + imm_draw_circle_wire_3d(pos, translation[0], translation[1], final_radius, 80); + immUniformColor3fvAlpha(outline_col, outline_alpha * 0.35f); + imm_draw_circle_wire_3d(pos, + translation[0], + translation[1], + final_radius * clamp_f(brush->alpha, 0.0f, 1.0f), + 80); + } } else { - /* Draw default cursor when the mouse is not over the mesh or there are no supported - * overlays active */ - GPU_line_width(1.0f); - imm_draw_circle_wire_3d(pos, translation[0], translation[1], final_radius, 40); - } - } - else { - if (vc.obact->sculpt->cache && !vc.obact->sculpt->cache->first_time) { - /* Draw cursor location preview when the stroke is active using the data from StrokeCache - */ - float cursor_location[3]; - wmViewport(&ar->winrct); - copy_v3_v3(cursor_location, ss->cache->true_location); - if (ss->cache->brush->sculpt_tool == SCULPT_TOOL_GRAB) { - add_v3_v3(cursor_location, ss->cache->grab_delta); - } - cursor_draw_point_with_symmetry(pos, ar, cursor_location, sd, vc.obact, ss->cache->radius); - - /* Draw cached dynamic mesh preview lines */ - if (brush->sculpt_tool == SCULPT_TOOL_GRAB && brush->flag & BRUSH_GRAB_ACTIVE_VERTEX) { - if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && ss->modifiers_active) { - GPU_matrix_push_projection(); - ED_view3d_draw_setup_view(CTX_wm_window(C), - CTX_data_depsgraph_pointer(C), - CTX_data_scene(C), - ar, - CTX_wm_view3d(C), - NULL, - NULL, - NULL); - GPU_matrix_push(); - GPU_matrix_mul(vc.obact->obmat); - sculpt_geometry_preview_lines_draw(pos, ss); - GPU_matrix_pop(); - GPU_matrix_pop_projection(); + if (vc.obact->sculpt->cache && !vc.obact->sculpt->cache->first_time) { + /* Draw cursor location preview when the stroke is active using the data from StrokeCache + */ + float cursor_location[3]; + wmViewport(&ar->winrct); + copy_v3_v3(cursor_location, ss->cache->true_location); + if (ss->cache->brush->sculpt_tool == SCULPT_TOOL_GRAB) { + add_v3_v3(cursor_location, ss->cache->grab_delta); + } + cursor_draw_point_with_symmetry( + pos, ar, cursor_location, sd, vc.obact, ss->cache->radius); + + /* Draw cached dynamic mesh preview lines */ + if (brush->sculpt_tool == SCULPT_TOOL_GRAB && (brush->flag & BRUSH_GRAB_ACTIVE_VERTEX) && + !is_multires) { + if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && ss->deform_modifiers_active) { + GPU_matrix_push_projection(); + ED_view3d_draw_setup_view(CTX_wm_window(C), + CTX_data_depsgraph_pointer(C), + CTX_data_scene(C), + ar, + CTX_wm_view3d(C), + NULL, + NULL, + NULL); + GPU_matrix_push(); + GPU_matrix_mul(vc.obact->obmat); + sculpt_geometry_preview_lines_draw(pos, ss); + GPU_matrix_pop(); + GPU_matrix_pop_projection(); + } } - } - wmWindowViewport(win); + wmWindowViewport(win); + } } } - } - else { - /* Draw default cursor in unsupported modes */ - GPU_line_width(1.0f); - imm_draw_circle_wire_3d(pos, translation[0], translation[1], final_radius, 40); + else { + /* Draw default cursor in unsupported modes */ + GPU_line_width(1.0f); + imm_draw_circle_wire_3d(pos, translation[0], translation[1], final_radius, 40); + } } immUnbindProgram(); diff --git a/source/blender/editors/sculpt_paint/paint_curve.c b/source/blender/editors/sculpt_paint/paint_curve.c index d9fd194e96f..62c31c91f8d 100644 --- a/source/blender/editors/sculpt_paint/paint_curve.c +++ b/source/blender/editors/sculpt_paint/paint_curve.c @@ -35,8 +35,6 @@ #include "BKE_main.h" #include "BKE_paint.h" -#include "DEG_depsgraph.h" - #include "ED_view3d.h" #include "ED_paint.h" diff --git a/source/blender/editors/sculpt_paint/paint_curve_undo.c b/source/blender/editors/sculpt_paint/paint_curve_undo.c index bd62a59e73f..c14ccd27804 100644 --- a/source/blender/editors/sculpt_paint/paint_curve_undo.c +++ b/source/blender/editors/sculpt_paint/paint_curve_undo.c @@ -35,7 +35,6 @@ #include "ED_undo.h" #include "WM_api.h" -#include "WM_types.h" #include "paint_intern.h" diff --git a/source/blender/editors/sculpt_paint/paint_hide.c b/source/blender/editors/sculpt_paint/paint_hide.c index 5852012891d..026dc39c668 100644 --- a/source/blender/editors/sculpt_paint/paint_hide.c +++ b/source/blender/editors/sculpt_paint/paint_hide.c @@ -37,7 +37,6 @@ #include "BKE_ccg.h" #include "BKE_context.h" #include "BKE_mesh.h" -#include "BKE_mesh_runtime.h" #include "BKE_multires.h" #include "BKE_paint.h" #include "BKE_subsurf.h" @@ -135,7 +134,6 @@ static void partialvis_update_grids(Depsgraph *depsgraph, float planes[4][4]) { CCGElem **grids; - CCGKey key; BLI_bitmap **grid_hidden; int *grid_indices, totgrid, i; bool any_changed = false, any_visible = false; @@ -143,7 +141,7 @@ static void partialvis_update_grids(Depsgraph *depsgraph, /* get PBVH data */ BKE_pbvh_node_get_grids(pbvh, node, &grid_indices, &totgrid, NULL, NULL, &grids); grid_hidden = BKE_pbvh_grid_hidden(pbvh); - BKE_pbvh_get_grid_key(pbvh, &key); + CCGKey key = *BKE_pbvh_get_grid_key(pbvh); sculpt_undo_push_node(ob, node, SCULPT_UNDO_HIDDEN); @@ -299,15 +297,17 @@ static void rect_from_props(rcti *rect, PointerRNA *ptr) rect->ymax = RNA_int_get(ptr, "ymax"); } -static void clip_planes_from_rect(bContext *C, float clip_planes[4][4], const rcti *rect) +static void clip_planes_from_rect(bContext *C, + Depsgraph *depsgraph, + float clip_planes[4][4], + const rcti *rect) { ViewContext vc; BoundBox bb; view3d_operator_needs_opengl(C); - ED_view3d_viewcontext_init(C, &vc); + ED_view3d_viewcontext_init(C, &vc, depsgraph); ED_view3d_clipping_calc(&bb, clip_planes, vc.ar, vc.obact, rect); - negate_m4(clip_planes); } /* If mode is inside, get all PBVH nodes that lie at least partially @@ -322,17 +322,18 @@ static void get_pbvh_nodes( /* select search callback */ switch (mode) { case PARTIALVIS_INSIDE: - cb = BKE_pbvh_node_planes_contain_AABB; + cb = BKE_pbvh_node_frustum_contain_AABB; break; case PARTIALVIS_OUTSIDE: - cb = BKE_pbvh_node_planes_exclude_AABB; + cb = BKE_pbvh_node_frustum_exclude_AABB; break; case PARTIALVIS_ALL: case PARTIALVIS_MASKED: break; } - BKE_pbvh_search_gather(pbvh, cb, clip_planes, nodes, totnode); + PBVHFrustumPlanes frustum = {.planes = clip_planes, .num_planes = 4}; + BKE_pbvh_search_gather(pbvh, cb, &frustum, nodes, totnode); } static int hide_show_exec(bContext *C, wmOperator *op) @@ -355,7 +356,7 @@ static int hide_show_exec(bContext *C, wmOperator *op) area = RNA_enum_get(op->ptr, "area"); rect_from_props(&rect, op->ptr); - clip_planes_from_rect(C, clip_planes, &rect); + clip_planes_from_rect(C, depsgraph, clip_planes, &rect); pbvh = BKE_sculpt_object_pbvh_ensure(depsgraph, ob); BLI_assert(ob->sculpt->pbvh == pbvh); @@ -363,6 +364,8 @@ static int hide_show_exec(bContext *C, wmOperator *op) get_pbvh_nodes(pbvh, &nodes, &totnode, clip_planes, area); pbvh_type = BKE_pbvh_type(pbvh); + negate_m4(clip_planes); + /* start undo */ switch (action) { case PARTIALVIS_HIDE: diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index f3a6cfa0d5c..b4388f6c324 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -113,10 +113,10 @@ void imapaint_region_tiles( IMB_rectclip(ibuf, NULL, &x, &y, &srcx, &srcy, &w, &h); - *tw = ((x + w - 1) >> IMAPAINT_TILE_BITS); - *th = ((y + h - 1) >> IMAPAINT_TILE_BITS); - *tx = (x >> IMAPAINT_TILE_BITS); - *ty = (y >> IMAPAINT_TILE_BITS); + *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); } void ED_imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w, int h, bool find_old) @@ -147,11 +147,12 @@ void ED_imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w, int imapaint_region_tiles(ibuf, x, y, w, h, &tilex, &tiley, &tilew, &tileh); - ListBase *undo_tiles = ED_image_undo_get_tiles(); + ListBase *undo_tiles = ED_image_paint_tile_list_get(); for (ty = tiley; ty <= tileh; ty++) { for (tx = tilex; tx <= tilew; tx++) { - image_undo_push_tile(undo_tiles, ima, ibuf, &tmpibuf, tx, ty, NULL, NULL, false, find_old); + ED_image_paint_tile_push( + undo_tiles, ima, ibuf, &tmpibuf, tx, ty, NULL, NULL, false, find_old); } } @@ -467,12 +468,13 @@ static void gradient_draw_line(bContext *UNUSED(C), int x, int y, void *customda static PaintOperation *texture_paint_init(bContext *C, wmOperator *op, const float mouse[2]) { + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Scene *scene = CTX_data_scene(C); ToolSettings *settings = scene->toolsettings; PaintOperation *pop = MEM_callocN(sizeof(PaintOperation), "PaintOperation"); /* caller frees */ Brush *brush = BKE_paint_brush(&settings->imapaint.paint); int mode = RNA_enum_get(op->ptr, "mode"); - ED_view3d_viewcontext_init(C, &pop->vc); + ED_view3d_viewcontext_init(C, &pop->vc, depsgraph); copy_v2_v2(pop->prevmouse, mouse); copy_v2_v2(pop->startmouse, mouse); @@ -538,7 +540,7 @@ static void paint_stroke_update_step(bContext *C, struct PaintStroke *stroke, Po RNA_float_get_array(itemptr, "mouse", mouse); pressure = RNA_float_get(itemptr, "pressure"); eraser = RNA_boolean_get(itemptr, "pen_flip"); - size = max_ff(1.0f, RNA_float_get(itemptr, "size")); + size = RNA_float_get(itemptr, "size"); /* stroking with fill tool only acts on stroke end */ if (brush->imagepaint_tool == PAINT_TOOL_FILL) { @@ -699,7 +701,7 @@ static int paint_invoke(bContext *C, wmOperator *op, const wmEvent *event) event->type); if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { - paint_stroke_data_free(op); + paint_stroke_free(C, op); return OPERATOR_FINISHED; } /* add modal handler */ @@ -1022,7 +1024,7 @@ static int sample_color_invoke(bContext *C, wmOperator *op, const wmEvent *event !RNA_boolean_get(op->ptr, "merged"); paint_sample_color(C, ar, event->mval[0], event->mval[1], use_sample_texture, false); - WM_cursor_modal_set(win, BC_EYEDROPPER_CURSOR); + WM_cursor_modal_set(win, WM_CURSOR_EYEDROPPER); WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, brush); diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c index 8f1156295a3..9c95a3cee4d 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -374,25 +374,65 @@ static void brush_painter_mask_imbuf_partial_update(BrushPainter *painter, /* create a mask with the falloff strength */ static unsigned short *brush_painter_curve_mask_new(BrushPainter *painter, int diameter, - float radius) + float radius, + const float pos[2]) { Brush *brush = painter->brush; - int xoff = -radius; - int yoff = -radius; + int offset = (int)floorf(diameter / 2.0f); unsigned short *mask, *m; - int x, y; mask = MEM_mallocN(sizeof(unsigned short) * diameter * diameter, "brush_painter_mask"); m = mask; - for (y = 0; y < diameter; y++) { - for (x = 0; x < diameter; x++, m++) { - float xy[2] = {x + xoff, y + yoff}; - float len = len_v2(xy); + int aa_samples = 1.0f / (radius * 0.20f); + aa_samples = clamp_i(aa_samples, 3, 16); - *m = (unsigned short)(65535.0f * BKE_brush_curve_strength_clamped(brush, len, radius)); + /* Temporal until we have the brush properties */ + const float hardness = 1.0f; + const float rotation = 0.0f; + + float aa_offset = 1.0f / (2.0f * (float)aa_samples); + float aa_step = 1.0f / (float)aa_samples; + + float bpos[2]; + bpos[0] = pos[0] - floorf(pos[0]) + offset + aa_offset; + bpos[1] = pos[1] - floorf(pos[1]) + offset + aa_offset; + + const float co = cosf(DEG2RADF(rotation)); + const float si = sinf(DEG2RADF(rotation)); + + float norm_factor = 65535.0f / (float)(aa_samples * aa_samples); + + for (int y = 0; y < diameter; y++) { + for (int x = 0; x < diameter; x++, m++) { + float total_samples = 0; + for (int i = 0; i < aa_samples; i++) { + for (int j = 0; j < aa_samples; j++) { + float pixel_xy[2] = {x + (aa_step * i), y + (aa_step * j)}; + float xy_rot[2]; + sub_v2_v2(pixel_xy, bpos); + + xy_rot[0] = co * pixel_xy[0] - si * pixel_xy[1]; + xy_rot[1] = si * pixel_xy[0] + co * pixel_xy[1]; + + float len = len_v2(xy_rot); + float p = len / radius; + if (hardness < 1.0f) { + p = (p - hardness) / (1 - hardness); + p = 1.0f - p; + CLAMP(p, 0, 1); + } + else { + p = 1.0; + } + float hardness_factor = 3.0f * p * p - 2.0f * p * p * p; + float curve = BKE_brush_curve_strength_clamped(brush, len, radius); + total_samples += curve * hardness_factor; + } + } + *m = (unsigned short)(total_samples * norm_factor); } } @@ -721,7 +761,8 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; Brush *brush = painter->brush; BrushPainterCache *cache = &painter->cache; - const int diameter = 2 * size; + /* Adding 4 pixels of padding for brush antialiasing */ + const int diameter = MAX2(1, size * 2) + 4; bool do_random = false; bool do_partial_update = false; @@ -802,15 +843,13 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, } /* curve mask can only change if the size changes */ - if (diameter != cache->lastdiameter) { - if (cache->curve_mask) { - MEM_freeN(cache->curve_mask); - cache->curve_mask = NULL; - } - - cache->curve_mask = brush_painter_curve_mask_new(painter, diameter, size); + if (cache->curve_mask) { + MEM_freeN(cache->curve_mask); + cache->curve_mask = NULL; } + cache->curve_mask = brush_painter_curve_mask_new(painter, diameter, size, pos); + /* detect if we need to recreate image brush buffer */ if ((diameter != cache->lastdiameter) || (tex_rotation != cache->last_tex_rotation) || do_random || update_color) { @@ -1197,23 +1236,24 @@ static void paint_2d_do_making_brush(ImagePaintState *s, int tileh) { ImBuf tmpbuf; - IMB_initImBuf(&tmpbuf, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, 0); + IMB_initImBuf(&tmpbuf, ED_IMAGE_UNDO_TILE_SIZE, ED_IMAGE_UNDO_TILE_SIZE, 32, 0); - ListBase *undo_tiles = ED_image_undo_get_tiles(); + ListBase *undo_tiles = ED_image_paint_tile_list_get(); for (int ty = tiley; ty <= tileh; ty++) { for (int tx = tilex; tx <= tilew; tx++) { /* retrieve original pixels + mask from undo buffer */ unsigned short *mask; - int origx = region->destx - tx * IMAPAINT_TILE_SIZE; - int origy = region->desty - ty * IMAPAINT_TILE_SIZE; + int origx = region->destx - tx * ED_IMAGE_UNDO_TILE_SIZE; + int origy = region->desty - ty * ED_IMAGE_UNDO_TILE_SIZE; if (s->canvas->rect_float) { - tmpbuf.rect_float = image_undo_find_tile( + tmpbuf.rect_float = ED_image_paint_tile_find( undo_tiles, s->image, s->canvas, tx, ty, &mask, false); } else { - tmpbuf.rect = image_undo_find_tile(undo_tiles, s->image, s->canvas, tx, ty, &mask, false); + tmpbuf.rect = ED_image_paint_tile_find( + undo_tiles, s->image, s->canvas, tx, ty, &mask, false); } IMB_rectblend(s->canvas, @@ -1454,8 +1494,6 @@ static void paint_2d_canvas_free(ImagePaintState *s) paint_delete_blur_kernel(s->blurkernel); MEM_freeN(s->blurkernel); } - - image_undo_remove_masks(); } void paint_2d_stroke(void *ps, diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 04a54ad5137..4c7e5e18257 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -77,15 +77,11 @@ #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_screen.h" -#include "BKE_texture.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" -#include "UI_interface.h" - #include "ED_object.h" -#include "ED_mesh.h" #include "ED_node.h" #include "ED_paint.h" #include "ED_screen.h" @@ -93,6 +89,7 @@ #include "ED_view3d.h" #include "GPU_extensions.h" +#include "GPU_init_exit.h" #include "WM_api.h" #include "WM_types.h" @@ -105,7 +102,6 @@ #include "IMB_colormanagement.h" -#include "bmesh.h" //#include "bmesh_tools.h" #include "paint_intern.h" @@ -1812,31 +1808,31 @@ static int project_paint_undo_subtiles(const TileInfo *tinf, int tx, int ty) } if (generate_tile) { - ListBase *undo_tiles = ED_image_undo_get_tiles(); + ListBase *undo_tiles = ED_image_paint_tile_list_get(); volatile void *undorect; if (tinf->masked) { - undorect = image_undo_push_tile(undo_tiles, - pjIma->ima, - pjIma->ibuf, - tinf->tmpibuf, - tx, - ty, - &pjIma->maskRect[tile_index], - &pjIma->valid[tile_index], - true, - false); + undorect = ED_image_paint_tile_push(undo_tiles, + pjIma->ima, + pjIma->ibuf, + tinf->tmpibuf, + tx, + ty, + &pjIma->maskRect[tile_index], + &pjIma->valid[tile_index], + true, + false); } else { - undorect = image_undo_push_tile(undo_tiles, - pjIma->ima, - pjIma->ibuf, - tinf->tmpibuf, - tx, - ty, - NULL, - &pjIma->valid[tile_index], - true, - false); + undorect = ED_image_paint_tile_push(undo_tiles, + pjIma->ima, + pjIma->ibuf, + tinf->tmpibuf, + tx, + ty, + NULL, + &pjIma->valid[tile_index], + true, + false); } BKE_image_mark_dirty(pjIma->ima, pjIma->ibuf); @@ -1885,14 +1881,14 @@ static ProjPixel *project_paint_uvpixel_init(const ProjPaintState *ps, /* calculate the undo tile offset of the pixel, used to store the original * pixel color and accumulated mask if any */ - x_tile = x_px >> IMAPAINT_TILE_BITS; - y_tile = y_px >> IMAPAINT_TILE_BITS; + x_tile = x_px >> ED_IMAGE_UNDO_TILE_BITS; + y_tile = y_px >> ED_IMAGE_UNDO_TILE_BITS; - x_round = x_tile * IMAPAINT_TILE_SIZE; - y_round = y_tile * IMAPAINT_TILE_SIZE; + x_round = x_tile * ED_IMAGE_UNDO_TILE_SIZE; + y_round = y_tile * ED_IMAGE_UNDO_TILE_SIZE; // memset(projPixel, 0, size); - tile_offset = (x_px - x_round) + (y_px - y_round) * IMAPAINT_TILE_SIZE; + tile_offset = (x_px - x_round) + (y_px - y_round) * ED_IMAGE_UNDO_TILE_SIZE; tile_index = project_paint_undo_subtiles(tinf, x_tile, y_tile); /* other thread may be initializing the tile so wait here */ @@ -1900,8 +1896,9 @@ static ProjPixel *project_paint_uvpixel_init(const ProjPaintState *ps, /* pass */ } - BLI_assert(tile_index < (IMAPAINT_TILE_NUMBER(ibuf->x) * IMAPAINT_TILE_NUMBER(ibuf->y))); - BLI_assert(tile_offset < (IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE)); + BLI_assert(tile_index < + (ED_IMAGE_UNDO_TILE_NUMBER(ibuf->x) * ED_IMAGE_UNDO_TILE_NUMBER(ibuf->y))); + BLI_assert(tile_offset < (ED_IMAGE_UNDO_TILE_SIZE * ED_IMAGE_UNDO_TILE_SIZE)); projPixel->valid = projima->valid[tile_index]; @@ -2979,7 +2976,7 @@ static void project_paint_face_init(const ProjPaintState *ps, TileInfo tinf = { ps->tile_lock, ps->do_masking, - IMAPAINT_TILE_NUMBER(ibuf->x), + ED_IMAGE_UNDO_TILE_NUMBER(ibuf->x), tmpibuf, ps->projImages + image_index, }; @@ -3931,7 +3928,7 @@ static void proj_paint_state_thread_init(ProjPaintState *ps, const bool reset_th BLI_spin_init(ps->tile_lock); } - image_undo_init_locks(); + ED_image_paint_tile_lock_init(); } for (a = 0; a < ps->thread_tot; a++) { @@ -4249,8 +4246,8 @@ static void project_paint_build_proj_ima(ProjPaintState *ps, projIma->ima = node->link; projIma->touch = 0; projIma->ibuf = BKE_image_acquire_ibuf(projIma->ima, NULL, NULL); - size = sizeof(void **) * IMAPAINT_TILE_NUMBER(projIma->ibuf->x) * - IMAPAINT_TILE_NUMBER(projIma->ibuf->y); + size = sizeof(void **) * ED_IMAGE_UNDO_TILE_NUMBER(projIma->ibuf->x) * + ED_IMAGE_UNDO_TILE_NUMBER(projIma->ibuf->y); projIma->partRedrawRect = BLI_memarena_alloc( arena, sizeof(ImagePaintPartialRedraw) * PROJ_BOUNDBOX_SQUARED); partial_redraw_array_init(projIma->partRedrawRect); @@ -4540,8 +4537,6 @@ static void project_paint_end(ProjPaintState *ps) { int a; - image_undo_remove_masks(); - /* dereference used image buffers */ if (ps->is_shared_user == false) { ProjPaintImage *projIma; @@ -4583,7 +4578,7 @@ static void project_paint_end(ProjPaintState *ps) MEM_freeN((void *)ps->tile_lock); } - image_undo_end_locks(); + ED_image_paint_tile_lock_end(); #ifndef PROJ_DEBUG_NOSEAMBLEED if (ps->seam_bleed_px > 0.0f) { @@ -6143,6 +6138,9 @@ static bool texture_paint_image_from_view_poll(bContext *C) CTX_wm_operator_poll_msg_set(C, "No 3D viewport found to create image from"); return false; } + if (!GPU_is_initialized()) { + return false; + } return true; } diff --git a/source/blender/editors/sculpt_paint/paint_image_undo.c b/source/blender/editors/sculpt_paint/paint_image_undo.c deleted file mode 100644 index 93dcd3ad0f6..00000000000 --- a/source/blender/editors/sculpt_paint/paint_image_undo.c +++ /dev/null @@ -1,625 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup edsculpt - */ - -#include "MEM_guardedalloc.h" - -#include "BLI_math.h" -#include "BLI_blenlib.h" -#include "BLI_utildefines.h" -#include "BLI_threads.h" - -#include "DNA_image_types.h" -#include "DNA_windowmanager_types.h" -#include "DNA_object_types.h" -#include "DNA_screen_types.h" -#include "DNA_space_types.h" -#include "DNA_workspace_types.h" - -#include "IMB_imbuf.h" -#include "IMB_imbuf_types.h" - -#include "BKE_context.h" -#include "BKE_image.h" -#include "BKE_paint.h" -#include "BKE_undo_system.h" - -#include "DEG_depsgraph.h" - -#include "ED_paint.h" -#include "ED_undo.h" -#include "ED_util.h" -#include "ED_object.h" - -#include "GPU_draw.h" - -#include "WM_api.h" - -#include "paint_intern.h" - -/* -------------------------------------------------------------------- */ -/** \name Undo Conversion - * \{ */ - -typedef struct UndoImageTile { - struct UndoImageTile *next, *prev; - - char ibufname[IMB_FILENAME_SIZE]; - - union { - float *fp; - unsigned int *uint; - void *pt; - } rect; - - unsigned short *mask; - - int x, y; - - /* TODO(campbell): avoid storing the ID per tile, - * adds unnecessary overhead restoring undo steps when most tiles share the same image. */ - UndoRefID_Image image_ref; - - short source; - bool use_float; - char gen_type; - bool valid; - - size_t undo_size; -} UndoImageTile; - -/* this is a static resource for non-globality, - * Maybe it should be exposed as part of the - * paint operation, but for now just give a public interface */ -static SpinLock undolock; - -void image_undo_init_locks(void) -{ - BLI_spin_init(&undolock); -} - -void image_undo_end_locks(void) -{ - BLI_spin_end(&undolock); -} - -/* UNDO */ -typedef enum { - COPY = 0, - RESTORE = 1, - RESTORE_COPY = 2, -} CopyMode; - -static void undo_copy_tile(UndoImageTile *tile, ImBuf *tmpibuf, ImBuf *ibuf, CopyMode mode) -{ - if (mode == COPY) { - /* copy or swap contents of tile->rect and region in ibuf->rect */ - IMB_rectcpy(tmpibuf, - ibuf, - 0, - 0, - tile->x * IMAPAINT_TILE_SIZE, - tile->y * IMAPAINT_TILE_SIZE, - IMAPAINT_TILE_SIZE, - IMAPAINT_TILE_SIZE); - - if (ibuf->rect_float) { - SWAP(float *, tmpibuf->rect_float, tile->rect.fp); - } - else { - SWAP(unsigned int *, tmpibuf->rect, tile->rect.uint); - } - } - else { - if (mode == RESTORE_COPY) { - IMB_rectcpy(tmpibuf, - ibuf, - 0, - 0, - tile->x * IMAPAINT_TILE_SIZE, - tile->y * IMAPAINT_TILE_SIZE, - IMAPAINT_TILE_SIZE, - IMAPAINT_TILE_SIZE); - } - /* swap to the tmpbuf for easy copying */ - if (ibuf->rect_float) { - SWAP(float *, tmpibuf->rect_float, tile->rect.fp); - } - else { - SWAP(unsigned int *, tmpibuf->rect, tile->rect.uint); - } - - IMB_rectcpy(ibuf, - tmpibuf, - tile->x * IMAPAINT_TILE_SIZE, - tile->y * IMAPAINT_TILE_SIZE, - 0, - 0, - IMAPAINT_TILE_SIZE, - IMAPAINT_TILE_SIZE); - - if (mode == RESTORE) { - if (ibuf->rect_float) { - SWAP(float *, tmpibuf->rect_float, tile->rect.fp); - } - else { - SWAP(unsigned int *, tmpibuf->rect, tile->rect.uint); - } - } - } -} - -void *image_undo_find_tile(ListBase *undo_tiles, - Image *ima, - ImBuf *ibuf, - int x_tile, - int y_tile, - unsigned short **mask, - bool validate) -{ - UndoImageTile *tile; - const bool use_float = (ibuf->rect_float != NULL); - - for (tile = undo_tiles->first; tile; tile = tile->next) { - if (tile->x == x_tile && tile->y == y_tile && ima->gen_type == tile->gen_type && - ima->source == tile->source) { - if (tile->use_float == use_float) { - if (STREQ(tile->ibufname, ibuf->name)) { - if (mask) { - /* allocate mask if requested */ - if (!tile->mask) { - tile->mask = MEM_callocN(sizeof(unsigned short) * IMAPAINT_TILE_SIZE * - IMAPAINT_TILE_SIZE, - "UndoImageTile.mask"); - } - - *mask = tile->mask; - } - if (validate) { - tile->valid = true; - } - return tile->rect.pt; - } - } - } - } - - return NULL; -} - -void *image_undo_push_tile(ListBase *undo_tiles, - Image *ima, - ImBuf *ibuf, - ImBuf **tmpibuf, - int x_tile, - int y_tile, - unsigned short **mask, - bool **valid, - bool proj, - bool find_prev) -{ - UndoImageTile *tile; - int allocsize; - const bool use_float = (ibuf->rect_float != NULL); - void *data; - - /* check if tile is already pushed */ - - /* in projective painting we keep accounting of tiles, so if we need one pushed, just push! */ - if (find_prev) { - data = image_undo_find_tile(undo_tiles, ima, ibuf, x_tile, y_tile, mask, true); - if (data) { - return data; - } - } - - if (*tmpibuf == NULL) { - *tmpibuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, IB_rectfloat | IB_rect); - } - - tile = MEM_callocN(sizeof(UndoImageTile), "UndoImageTile"); - tile->x = x_tile; - tile->y = y_tile; - - /* add mask explicitly here */ - if (mask) { - *mask = tile->mask = MEM_callocN( - sizeof(unsigned short) * IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE, "UndoImageTile.mask"); - } - allocsize = IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE * 4; - allocsize *= (ibuf->rect_float) ? sizeof(float) : sizeof(char); - tile->rect.pt = MEM_mapallocN(allocsize, "UndeImageTile.rect"); - - BLI_strncpy(tile->ibufname, ibuf->name, sizeof(tile->ibufname)); - - tile->gen_type = ima->gen_type; - tile->source = ima->source; - tile->use_float = use_float; - tile->valid = true; - tile->image_ref.ptr = ima; - - if (valid) { - *valid = &tile->valid; - } - undo_copy_tile(tile, *tmpibuf, ibuf, COPY); - - if (proj) { - BLI_spin_lock(&undolock); - } - BLI_addtail(undo_tiles, tile); - - if (proj) { - BLI_spin_unlock(&undolock); - } - return tile->rect.pt; -} - -void image_undo_remove_masks(void) -{ - ListBase *undo_tiles = ED_image_undo_get_tiles(); - UndoImageTile *tile; - - for (tile = undo_tiles->first; tile; tile = tile->next) { - if (tile->mask) { - MEM_freeN(tile->mask); - tile->mask = NULL; - } - } -} - -static void image_undo_restore_runtime(ListBase *lb) -{ - ImBuf *ibuf, *tmpibuf; - UndoImageTile *tile; - - tmpibuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, IB_rectfloat | IB_rect); - - for (tile = lb->first; tile; tile = tile->next) { - Image *ima = tile->image_ref.ptr; - ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); - - undo_copy_tile(tile, tmpibuf, ibuf, RESTORE); - - GPU_free_image(ima); /* force OpenGL reload (maybe partial update will operate better?) */ - if (ibuf->rect_float) { - ibuf->userflags |= IB_RECT_INVALID; /* force recreate of char rect */ - } - if (ibuf->mipmap[0]) { - ibuf->userflags |= IB_MIPMAP_INVALID; /* force mipmap recreatiom */ - } - ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; - - BKE_image_release_ibuf(ima, ibuf, NULL); - } - - IMB_freeImBuf(tmpibuf); -} - -static void image_undo_restore_list(ListBase *lb) -{ - ImBuf *tmpibuf = IMB_allocImBuf( - IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, IB_rectfloat | IB_rect); - - for (UndoImageTile *tile = lb->first; tile; tile = tile->next) { - - Image *ima = tile->image_ref.ptr; - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); - - if (ima && ibuf && !STREQ(tile->ibufname, ibuf->name)) { - /* current ImBuf filename was changed, probably current frame - * was changed when painting on image sequence, rather than storing - * full image user (which isn't so obvious, btw) try to find ImBuf with - * matched file name in list of already loaded images */ - - BKE_image_release_ibuf(ima, ibuf, NULL); - - ibuf = BKE_image_get_ibuf_with_name(ima, tile->ibufname); - } - - if (!ima || !ibuf || !(ibuf->rect || ibuf->rect_float)) { - BKE_image_release_ibuf(ima, ibuf, NULL); - continue; - } - - if (ima->gen_type != tile->gen_type || ima->source != tile->source) { - BKE_image_release_ibuf(ima, ibuf, NULL); - continue; - } - - const bool use_float = (ibuf->rect_float != NULL); - - if (use_float != tile->use_float) { - BKE_image_release_ibuf(ima, ibuf, NULL); - continue; - } - - undo_copy_tile(tile, tmpibuf, ibuf, RESTORE_COPY); - - BKE_image_mark_dirty(ima, ibuf); - GPU_free_image(ima); /* force OpenGL reload */ - - if (ibuf->rect_float) { - ibuf->userflags |= IB_RECT_INVALID; /* force recreate of char rect */ - } - if (ibuf->mipmap[0]) { - ibuf->userflags |= IB_MIPMAP_INVALID; /* force mipmap recreatiom */ - } - ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; - - DEG_id_tag_update(&ima->id, 0); - - BKE_image_release_ibuf(ima, ibuf, NULL); - } - - IMB_freeImBuf(tmpibuf); -} - -static void image_undo_free_list(ListBase *lb) -{ - for (UndoImageTile *tile = lb->first, *tile_next; tile; tile = tile_next) { - tile_next = tile->next; - MEM_freeN(tile->rect.pt); - MEM_freeN(tile); - } -} - -static void image_undo_invalidate(void) -{ - UndoImageTile *tile; - ListBase *lb = ED_image_undo_get_tiles(); - - for (tile = lb->first; tile; tile = tile->next) { - tile->valid = false; - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Implements ED Undo System - * \{ */ - -typedef struct ImageUndoStep { - UndoStep step; - ListBase tiles; - bool is_encode_init; - ePaintMode paint_mode; -} ImageUndoStep; - -static bool image_undosys_poll(bContext *C) -{ - Object *obact = CTX_data_active_object(C); - - ScrArea *sa = CTX_wm_area(C); - if (sa && (sa->spacetype == SPACE_IMAGE)) { - SpaceImage *sima = (SpaceImage *)sa->spacedata.first; - if ((obact && (obact->mode & OB_MODE_TEXTURE_PAINT)) || (sima->mode == SI_MODE_PAINT)) { - return true; - } - } - else { - if (obact && (obact->mode & OB_MODE_TEXTURE_PAINT)) { - return true; - } - } - return false; -} - -static void image_undosys_step_encode_init(struct bContext *UNUSED(C), UndoStep *us_p) -{ - ImageUndoStep *us = (ImageUndoStep *)us_p; - /* dummy, memory is cleared anyway. */ - us->is_encode_init = true; - BLI_listbase_clear(&us->tiles); -} - -static bool image_undosys_step_encode(struct bContext *C, - struct Main *UNUSED(bmain), - UndoStep *us_p) -{ - /* dummy, encoding is done along the way by adding tiles - * to the current 'ImageUndoStep' added by encode_init. */ - ImageUndoStep *us = (ImageUndoStep *)us_p; - - BLI_assert(us->step.data_size == 0); - - int allocsize = IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE * 4; - - if (us->is_encode_init) { - /* first dispose of invalid tiles (may happen due to drag dot for instance) */ - for (UndoImageTile *tile = us->tiles.first; tile;) { - if (!tile->valid) { - UndoImageTile *tmp_tile = tile->next; - MEM_freeN(tile->rect.pt); - BLI_freelinkN(&us->tiles, tile); - tile = tmp_tile; - } - else { - us->step.data_size += allocsize * (tile->use_float ? sizeof(float) : sizeof(char)); - tile = tile->next; - } - } - } - else { - /* Happens when switching modes. */ - ePaintMode paint_mode = BKE_paintmode_get_active_from_context(C); - BLI_assert(ELEM(paint_mode, PAINT_MODE_TEXTURE_2D, PAINT_MODE_TEXTURE_3D)); - us->paint_mode = paint_mode; - } - - us_p->is_applied = true; - - return true; -} - -static void image_undosys_step_decode_undo_impl(ImageUndoStep *us) -{ - BLI_assert(us->step.is_applied == true); - image_undo_restore_list(&us->tiles); - us->step.is_applied = false; -} - -static void image_undosys_step_decode_redo_impl(ImageUndoStep *us) -{ - BLI_assert(us->step.is_applied == false); - image_undo_restore_list(&us->tiles); - us->step.is_applied = true; -} - -static void image_undosys_step_decode_undo(ImageUndoStep *us, bool is_final) -{ - ImageUndoStep *us_iter = us; - while (us_iter->step.next && (us_iter->step.next->type == us_iter->step.type)) { - if (us_iter->step.next->is_applied == false) { - break; - } - us_iter = (ImageUndoStep *)us_iter->step.next; - } - while (us_iter != us || (!is_final && us_iter == us)) { - image_undosys_step_decode_undo_impl(us_iter); - if (us_iter == us) { - break; - } - us_iter = (ImageUndoStep *)us_iter->step.prev; - } -} - -static void image_undosys_step_decode_redo(ImageUndoStep *us) -{ - ImageUndoStep *us_iter = us; - while (us_iter->step.prev && (us_iter->step.prev->type == us_iter->step.type)) { - if (us_iter->step.prev->is_applied == true) { - break; - } - us_iter = (ImageUndoStep *)us_iter->step.prev; - } - while (us_iter && (us_iter->step.is_applied == false)) { - image_undosys_step_decode_redo_impl(us_iter); - if (us_iter == us) { - break; - } - us_iter = (ImageUndoStep *)us_iter->step.next; - } -} - -static void image_undosys_step_decode( - struct bContext *C, struct Main *bmain, UndoStep *us_p, int dir, bool is_final) -{ - ImageUndoStep *us = (ImageUndoStep *)us_p; - if (dir < 0) { - image_undosys_step_decode_undo(us, is_final); - } - else { - image_undosys_step_decode_redo(us); - } - - if (us->paint_mode == PAINT_MODE_TEXTURE_3D) { - ED_object_mode_set(C, OB_MODE_TEXTURE_PAINT); - } - - /* Refresh texture slots. */ - ED_editors_init_for_undo(bmain); -} - -static void image_undosys_step_free(UndoStep *us_p) -{ - ImageUndoStep *us = (ImageUndoStep *)us_p; - image_undo_free_list(&us->tiles); -} - -static void image_undosys_foreach_ID_ref(UndoStep *us_p, - UndoTypeForEachIDRefFn foreach_ID_ref_fn, - void *user_data) -{ - ImageUndoStep *us = (ImageUndoStep *)us_p; - for (UndoImageTile *tile = us->tiles.first; tile; tile = tile->next) { - foreach_ID_ref_fn(user_data, ((UndoRefID *)&tile->image_ref)); - } -} - -/* Export for ED_undo_sys. */ -void ED_image_undosys_type(UndoType *ut) -{ - ut->name = "Image"; - ut->poll = image_undosys_poll; - ut->step_encode_init = image_undosys_step_encode_init; - ut->step_encode = image_undosys_step_encode; - ut->step_decode = image_undosys_step_decode; - ut->step_free = image_undosys_step_free; - - ut->step_foreach_ID_ref = image_undosys_foreach_ID_ref; - - ut->use_context = true; - - ut->step_size = sizeof(ImageUndoStep); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Utilities - * \{ */ - -ListBase *ED_image_undosys_step_get_tiles(UndoStep *us_p) -{ - ImageUndoStep *us = (ImageUndoStep *)us_p; - return &us->tiles; -} - -ListBase *ED_image_undo_get_tiles(void) -{ - UndoStack *ustack = ED_undo_stack_get(); - UndoStep *us_prev = ustack->step_init; - UndoStep *us_p = BKE_undosys_stack_init_or_active_with_type(ustack, BKE_UNDOSYS_TYPE_IMAGE); - ImageUndoStep *us = (ImageUndoStep *)us_p; - /* We should always have an undo push started when accessing tiles, - * not doing this means we won't have paint_mode correctly set. */ - BLI_assert(us_p == us_prev); - if (us_p != us_prev) { - /* Fallback value until we can be sure this never happens. */ - us->paint_mode = PAINT_MODE_TEXTURE_2D; - } - return ED_image_undosys_step_get_tiles(us_p); -} - -/* restore painting image to previous state. Used for anchored and drag-dot style brushes*/ -void ED_image_undo_restore(UndoStep *us) -{ - ListBase *lb = ED_image_undosys_step_get_tiles(us); - image_undo_restore_runtime(lb); - image_undo_invalidate(); -} - -void ED_image_undo_push_begin(const char *name, int paint_mode) -{ - UndoStack *ustack = ED_undo_stack_get(); - 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)); - us->paint_mode = paint_mode; -} - -void ED_image_undo_push_end(void) -{ - UndoStack *ustack = ED_undo_stack_get(); - BKE_undosys_step_push(ustack, NULL, NULL); - WM_file_tag_modified(); -} - -/** \} */ diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index 5efedf69fe4..19380fb9022 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -29,7 +29,6 @@ struct Brush; struct ColorManagedDisplay; struct ColorSpace; struct ImagePool; -struct ListBase; struct MTex; struct Object; struct Paint; @@ -37,7 +36,6 @@ struct PaintStroke; struct PointerRNA; struct RegionView3D; struct Scene; -struct UndoStep; struct VPaint; struct ViewContext; struct bContext; @@ -70,7 +68,7 @@ struct PaintStroke *paint_stroke_new(struct bContext *C, StrokeRedraw redraw, StrokeDone done, int event_type); -void paint_stroke_data_free(struct wmOperator *op); +void paint_stroke_free(struct bContext *C, struct wmOperator *op); bool paint_space_stroke_enabled(struct Brush *br, enum ePaintMode mode); bool paint_supports_dynamic_size(struct Brush *br, enum ePaintMode mode); @@ -184,10 +182,6 @@ typedef struct ImagePaintPartialRedraw { int enabled; } ImagePaintPartialRedraw; -#define IMAPAINT_TILE_BITS 6 -#define IMAPAINT_TILE_SIZE (1 << IMAPAINT_TILE_BITS) -#define IMAPAINT_TILE_NUMBER(size) (((size) + IMAPAINT_TILE_SIZE - 1) >> IMAPAINT_TILE_BITS) - bool image_texture_paint_poll(struct bContext *C); void imapaint_image_update(struct SpaceImage *sima, struct Image *image, @@ -252,31 +246,6 @@ void PAINT_OT_add_texture_paint_slot(struct wmOperatorType *ot); void PAINT_OT_image_paint(struct wmOperatorType *ot); void PAINT_OT_add_simple_uvs(struct wmOperatorType *ot); -/* paint_image_undo.c */ -void *image_undo_find_tile(ListBase *undo_tiles, - struct Image *ima, - struct ImBuf *ibuf, - int x_tile, - int y_tile, - unsigned short **mask, - bool validate); -void *image_undo_push_tile(ListBase *undo_tiles, - struct Image *ima, - struct ImBuf *ibuf, - struct ImBuf **tmpibuf, - int x_tile, - int y_tile, - unsigned short **, - bool **valid, - bool proj, - bool find_prev); -void image_undo_remove_masks(void); -void image_undo_init_locks(void); -void image_undo_end_locks(void); - -struct ListBase *ED_image_undosys_step_get_tiles(struct UndoStep *us_p); -struct ListBase *ED_image_undo_get_tiles(void); - /* sculpt_uv.c */ void SCULPT_OT_uv_sculpt_stroke(struct wmOperatorType *ot); diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c index 0e8d4d75360..d160fba4013 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -109,6 +109,7 @@ static void mask_flood_fill_task_cb(void *__restrict userdata, const PaintMaskFloodMode mode = data->mode; const float value = data->value; + bool redraw = false; PBVHVertexIter vi; @@ -116,13 +117,19 @@ static void mask_flood_fill_task_cb(void *__restrict userdata, BKE_pbvh_vertex_iter_begin(data->pbvh, node, vi, PBVH_ITER_UNIQUE) { + float prevmask = *vi.mask; mask_flood_fill_set_elem(vi.mask, mode, value); + if (prevmask != *vi.mask) { + redraw = true; + } } BKE_pbvh_vertex_iter_end; - BKE_pbvh_node_mark_redraw(node); - if (data->multires) { - BKE_pbvh_node_mark_normals_update(node); + if (redraw) { + BKE_pbvh_node_mark_update_mask(node); + if (data->multires) { + BKE_pbvh_node_mark_normals_update(node); + } } } @@ -159,17 +166,16 @@ static int mask_flood_fill_exec(bContext *C, wmOperator *op) .value = value, }; - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - - 0, totnode, &data, mask_flood_fill_task_cb, &settings); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BKE_pbvh_parallel_range(0, totnode, &data, mask_flood_fill_task_cb, &settings); if (multires) { multires_mark_as_modified(depsgraph, ob, MULTIRES_COORDS_MODIFIED); } + BKE_pbvh_update_vertex_data(pbvh, PBVH_UpdateMask); + sculpt_undo_push_end(); if (nodes) { @@ -255,24 +261,32 @@ static void mask_box_select_task_cb(void *__restrict userdata, PBVHVertexIter vi; bool any_masked = false; + bool redraw = false; BKE_pbvh_vertex_iter_begin(data->pbvh, node, vi, PBVH_ITER_UNIQUE) { if (is_effected(clip_planes_final, vi.co)) { + float prevmask = *vi.mask; if (!any_masked) { any_masked = true; sculpt_undo_push_node(data->ob, node, SCULPT_UNDO_MASK); - BKE_pbvh_node_mark_redraw(node); if (data->multires) { BKE_pbvh_node_mark_normals_update(node); } } mask_flood_fill_set_elem(vi.mask, mode, value); + if (prevmask != *vi.mask) { + redraw = true; + } } } BKE_pbvh_vertex_iter_end; + + if (redraw) { + BKE_pbvh_node_mark_update_mask(node); + } } bool ED_sculpt_mask_box_select(struct bContext *C, ViewContext *vc, const rcti *rect, bool select) @@ -297,7 +311,6 @@ bool ED_sculpt_mask_box_select(struct bContext *C, ViewContext *vc, const rcti * /* transform the clip planes in object space */ ED_view3d_clipping_calc(&bb, clip_planes, vc->ar, vc->obact, rect); - negate_m4(clip_planes); BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true); pbvh = ob->sculpt->pbvh; @@ -315,8 +328,10 @@ bool ED_sculpt_mask_box_select(struct bContext *C, ViewContext *vc, const rcti * flip_plane(clip_planes_final[j], clip_planes[j], symmpass); } - BKE_pbvh_search_gather( - pbvh, BKE_pbvh_node_planes_contain_AABB, clip_planes_final, &nodes, &totnode); + PBVHFrustumPlanes frustum = {.planes = clip_planes_final, .num_planes = 4}; + BKE_pbvh_search_gather(pbvh, BKE_pbvh_node_frustum_contain_AABB, &frustum, &nodes, &totnode); + + negate_m4(clip_planes_final); MaskTaskData data = { .ob = ob, @@ -328,11 +343,9 @@ bool ED_sculpt_mask_box_select(struct bContext *C, ViewContext *vc, const rcti * .clip_planes_final = clip_planes_final, }; - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && - totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range(0, totnode, &data, mask_box_select_task_cb, &settings); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BKE_pbvh_parallel_range(0, totnode, &data, mask_box_select_task_cb, &settings); if (nodes) { MEM_freeN(nodes); @@ -344,6 +357,8 @@ bool ED_sculpt_mask_box_select(struct bContext *C, ViewContext *vc, const rcti * multires_mark_as_modified(depsgraph, ob, MULTIRES_COORDS_MODIFIED); } + BKE_pbvh_update_vertex_data(pbvh, PBVH_UpdateMask); + sculpt_undo_push_end(); ED_region_tag_redraw(ar); @@ -462,7 +477,7 @@ static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op) /* Calculations of individual vertices are done in 2D screen space to diminish the amount of * calculations done. Bounding box PBVH collision is not computed against enclosing rectangle * of lasso */ - ED_view3d_viewcontext_init(C, &vc); + ED_view3d_viewcontext_init(C, &vc, depsgraph); /* lasso data calculations */ data.vc = &vc; @@ -483,7 +498,6 @@ static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op) &data); ED_view3d_clipping_calc(&bb, clip_planes, vc.ar, vc.obact, &data.rect); - negate_m4(clip_planes); BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true); pbvh = ob->sculpt->pbvh; @@ -505,8 +519,11 @@ static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op) /* gather nodes inside lasso's enclosing rectangle * (should greatly help with bigger meshes) */ + PBVHFrustumPlanes frustum = {.planes = clip_planes_final, .num_planes = 4}; BKE_pbvh_search_gather( - pbvh, BKE_pbvh_node_planes_contain_AABB, clip_planes_final, &nodes, &totnode); + pbvh, BKE_pbvh_node_frustum_contain_AABB, &frustum, &nodes, &totnode); + + negate_m4(clip_planes_final); data.task_data.ob = ob; data.task_data.pbvh = pbvh; @@ -515,11 +532,9 @@ static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op) data.task_data.mode = mode; data.task_data.value = value; - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && - (totnode > SCULPT_THREADED_LIMIT)); - BLI_task_parallel_range(0, totnode, &data, mask_gesture_lasso_task_cb, &settings); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BKE_pbvh_parallel_range(0, totnode, &data, mask_gesture_lasso_task_cb, &settings); if (nodes) { MEM_freeN(nodes); @@ -531,6 +546,8 @@ static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op) multires_mark_as_modified(depsgraph, ob, MULTIRES_COORDS_MODIFIED); } + BKE_pbvh_update_vertex_data(pbvh, PBVH_UpdateMask); + sculpt_undo_push_end(); ED_region_tag_redraw(vc.ar); diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index f58afcdadc1..97455d479dc 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.c +++ b/source/blender/editors/sculpt_paint/paint_ops.c @@ -29,32 +29,22 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_brush_types.h" -#include "DNA_gpencil_types.h" #include "BKE_brush.h" #include "BKE_context.h" -#include "BKE_gpencil.h" #include "BKE_library.h" #include "BKE_main.h" #include "BKE_paint.h" -#include "BKE_report.h" - -#include "DEG_depsgraph.h" #include "ED_paint.h" #include "ED_screen.h" -#include "ED_select_utils.h" #include "ED_image.h" -#include "ED_gpencil.h" -#include "UI_resources.h" #include "WM_api.h" #include "WM_types.h" -#include "WM_toolsystem.h" #include "RNA_access.h" #include "RNA_define.h" -#include "RNA_enum_types.h" #include "paint_intern.h" #include "sculpt_intern.h" diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index c764933fcf0..36418045551 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -43,7 +43,6 @@ #include "BKE_curve.h" #include "BKE_colortools.h" #include "BKE_image.h" -#include "BKE_mesh.h" #include "WM_api.h" #include "WM_types.h" @@ -257,6 +256,7 @@ static bool paint_tool_require_inbetween_mouse_events(Brush *brush, ePaintMode m SCULPT_TOOL_GRAB, SCULPT_TOOL_ROTATE, SCULPT_TOOL_THUMB, + SCULPT_TOOL_SNAKE_HOOK, SCULPT_TOOL_ELASTIC_DEFORM, SCULPT_TOOL_POSE)) { return false; @@ -669,7 +669,7 @@ static float paint_space_stroke_spacing(bContext *C, return max_ff(0.001f, size_clamp * spacing / 50.f); } else { - return max_ff(1.0, size_clamp * spacing / 50.0f); + return max_ff(stroke->zoom_2d, size_clamp * spacing / 50.0f); } } @@ -808,7 +808,7 @@ static int paint_space_stroke(bContext *C, while (length > 0.0f) { float spacing = paint_space_stroke_spacing_variable( C, scene, stroke, pressure, dpressure, length); - float mouse[2]; + float mouse[3]; if (length >= spacing) { if (use_scene_spacing) { @@ -857,14 +857,16 @@ PaintStroke *paint_stroke_new(bContext *C, StrokeDone done, int event_type) { + struct Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); PaintStroke *stroke = MEM_callocN(sizeof(PaintStroke), "PaintStroke"); ToolSettings *toolsettings = CTX_data_tool_settings(C); UnifiedPaintSettings *ups = &toolsettings->unified_paint_settings; Paint *p = BKE_paint_get_active_from_context(C); Brush *br = stroke->brush = BKE_paint_brush(p); + RegionView3D *rv3d = CTX_wm_region_view3d(C); float zoomx, zoomy; - ED_view3d_viewcontext_init(C, &stroke->vc); + ED_view3d_viewcontext_init(C, &stroke->vc, depsgraph); stroke->get_location = get_location; stroke->test_start = test_start; @@ -887,6 +889,10 @@ PaintStroke *paint_stroke_new(bContext *C, ups->overlap_factor = 1.0; ups->stroke_active = true; + if (rv3d) { + rv3d->rflag |= RV3D_PAINTING; + } + zero_v3(ups->average_stroke_accum); ups->average_stroke_counter = 0; @@ -901,20 +907,46 @@ PaintStroke *paint_stroke_new(bContext *C, return stroke; } -void paint_stroke_data_free(struct wmOperator *op) +void paint_stroke_free(bContext *C, wmOperator *op) { + RegionView3D *rv3d = CTX_wm_region_view3d(C); + if (rv3d) { + rv3d->rflag &= ~RV3D_PAINTING; + } + BKE_paint_set_overlay_override(0); + + PaintStroke *stroke = op->customdata; + if (stroke == NULL) { + return; + } + + UnifiedPaintSettings *ups = stroke->ups; + ups->draw_anchored = false; + ups->stroke_active = false; + + if (stroke->timer) { + WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), stroke->timer); + } + + if (stroke->rng) { + BLI_rng_free(stroke->rng); + } + + if (stroke->stroke_cursor) { + WM_paint_cursor_end(CTX_wm_manager(C), stroke->stroke_cursor); + } + + BLI_freelistN(&stroke->line); + MEM_SAFE_FREE(op->customdata); } -static void stroke_done(struct bContext *C, struct wmOperator *op) +static void stroke_done(bContext *C, wmOperator *op) { - struct PaintStroke *stroke = op->customdata; + PaintStroke *stroke = op->customdata; UnifiedPaintSettings *ups = stroke->ups; - ups->draw_anchored = false; - ups->stroke_active = false; - /* reset rotation here to avoid doing so in cursor display */ if (!(stroke->brush->mtex.brush_angle_mode & MTEX_ANGLE_RAKE)) { ups->brush_rotation = 0.0f; @@ -934,21 +966,7 @@ static void stroke_done(struct bContext *C, struct wmOperator *op) } } - if (stroke->timer) { - WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), stroke->timer); - } - - if (stroke->rng) { - BLI_rng_free(stroke->rng); - } - - if (stroke->stroke_cursor) { - WM_paint_cursor_end(CTX_wm_manager(C), stroke->stroke_cursor); - } - - BLI_freelistN(&stroke->line); - - paint_stroke_data_free(op); + paint_stroke_free(C, op); } /* Returns zero if the stroke dots should not be spaced, non-zero otherwise */ @@ -962,6 +980,7 @@ static bool sculpt_is_grab_tool(Brush *br) return ELEM(br->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_ELASTIC_DEFORM, + SCULPT_TOOL_POSE, SCULPT_TOOL_THUMB, SCULPT_TOOL_ROTATE, SCULPT_TOOL_SNAKE_HOOK); diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c index 6459325e6ee..a014fe7fdff 100644 --- a/source/blender/editors/sculpt_paint/paint_utils.c +++ b/source/blender/editors/sculpt_paint/paint_utils.c @@ -46,7 +46,6 @@ #include "BKE_image.h" #include "BKE_material.h" #include "BKE_mesh_runtime.h" -#include "BKE_object.h" #include "BKE_paint.h" #include "BKE_report.h" @@ -145,7 +144,6 @@ void paint_calc_redraw_planes(float planes[4][4], rect.ymax += 2; ED_view3d_clipping_calc(&bb, planes, ar, ob, &rect); - negate_m4(planes); } float paint_calc_object_space_radius(ViewContext *vc, const float center[3], float pixel_radius) @@ -506,7 +504,7 @@ void paint_sample_color( unsigned int totpoly = me->totpoly; if (CustomData_has_layer(&me_eval->ldata, CD_MLOOPUV)) { - ED_view3d_viewcontext_init(C, &vc); + ED_view3d_viewcontext_init(C, &vc, depsgraph); view3d_operator_needs_opengl(C); diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c index 12da8790b91..77c95c6acb3 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.c +++ b/source/blender/editors/sculpt_paint/paint_vertex.c @@ -34,7 +34,6 @@ #include "BLI_array_utils.h" #include "BLI_task.h" -#include "DNA_armature_types.h" #include "DNA_mesh_types.h" #include "DNA_particle_types.h" #include "DNA_scene_types.h" @@ -42,7 +41,6 @@ #include "DNA_object_types.h" #include "RNA_access.h" -#include "RNA_define.h" #include "BKE_brush.h" #include "BKE_context.h" @@ -195,17 +193,12 @@ static bool vertex_paint_use_fast_update_check(Object *ob) return false; } -static void paint_last_stroke_update(Scene *scene, ARegion *ar, const float mval[2]) +static void paint_last_stroke_update(Scene *scene, const float location[3]) { - const int mval_i[2] = {mval[0], mval[1]}; - float world[3]; - - if (ED_view3d_autodist_simple(ar, mval_i, world, 0, NULL)) { - UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; - ups->average_stroke_counter++; - add_v3_v3(ups->average_stroke_accum, world); - ups->last_stroke_valid = true; - } + UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; + ups->average_stroke_counter++; + add_v3_v3(ups->average_stroke_accum, location); + ups->last_stroke_valid = true; } /* polling - retrieve whether cursor should be set or operator should be done */ @@ -260,7 +253,7 @@ static bool weight_paint_poll_ex(bContext *C, bool check_tool) (BKE_paint_brush(&CTX_data_tool_settings(C)->wpaint->paint) != NULL) && (sa = CTX_wm_area(C)) && (sa->spacetype == SPACE_VIEW3D)) { ARegion *ar = CTX_wm_region(C); - if (ar->regiontype == RGN_TYPE_WINDOW) { + if (ELEM(ar->regiontype, RGN_TYPE_WINDOW, RGN_TYPE_HUD)) { if (!check_tool || WM_toolsystem_active_tool_is_brush(C)) { return 1; } @@ -1610,7 +1603,7 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo /* make mode data storage */ wpd = MEM_callocN(sizeof(struct WPaintData), "WPaintData"); paint_stroke_set_mode_data(stroke, wpd); - ED_view3d_viewcontext_init(C, &wpd->vc); + ED_view3d_viewcontext_init(C, &wpd->vc, depsgraph); view_angle_limits_init(&wpd->normal_angle_precalc, vp->paint.brush->falloff_angle, (vp->paint.brush->flag & BRUSH_FRONTFACE_FALLOFF) != 0); @@ -2079,11 +2072,9 @@ static void calculate_average_weight(SculptThreadedTaskData *data, struct WPaintAverageAccum *accum = MEM_mallocN(sizeof(*accum) * totnode, __func__); data->custom_data = accum; - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((data->sd->flags & SCULPT_USE_OPENMP) && - totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range(0, totnode, data, do_wpaint_brush_calc_average_weight_cb_ex, &settings); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (data->sd->flags & SCULPT_USE_OPENMP), totnode); + BKE_pbvh_parallel_range(0, totnode, data, do_wpaint_brush_calc_average_weight_cb_ex, &settings); uint accum_len = 0; double accum_weight = 0.0; @@ -2128,24 +2119,23 @@ static void wpaint_paint_leaves(bContext *C, /* Use this so average can modify its weight without touching the brush. */ data.strength = BKE_brush_weight_get(scene, brush); - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); /* NOTE: current mirroring code cannot be run in parallel */ - settings.use_threading = !(me->editflag & ME_EDIT_MIRROR_X); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, !(me->editflag & ME_EDIT_MIRROR_X), totnode); switch ((eBrushWeightPaintTool)brush->weightpaint_tool) { case WPAINT_TOOL_AVERAGE: calculate_average_weight(&data, nodes, totnode); - BLI_task_parallel_range(0, totnode, &data, do_wpaint_brush_draw_task_cb_ex, &settings); + BKE_pbvh_parallel_range(0, totnode, &data, do_wpaint_brush_draw_task_cb_ex, &settings); break; case WPAINT_TOOL_SMEAR: - BLI_task_parallel_range(0, totnode, &data, do_wpaint_brush_smear_task_cb_ex, &settings); + BKE_pbvh_parallel_range(0, totnode, &data, do_wpaint_brush_smear_task_cb_ex, &settings); break; case WPAINT_TOOL_BLUR: - BLI_task_parallel_range(0, totnode, &data, do_wpaint_brush_blur_task_cb_ex, &settings); + BKE_pbvh_parallel_range(0, totnode, &data, do_wpaint_brush_blur_task_cb_ex, &settings); break; case WPAINT_TOOL_DRAW: - BLI_task_parallel_range(0, totnode, &data, do_wpaint_brush_draw_task_cb_ex, &settings); + BKE_pbvh_parallel_range(0, totnode, &data, do_wpaint_brush_draw_task_cb_ex, &settings); break; } } @@ -2351,7 +2341,7 @@ static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P /* calculate pivot for rotation around seletion if needed */ /* also needed for "View Selected" on last stroke */ - paint_last_stroke_update(scene, vc->ar, ss->cache->mouse); + paint_last_stroke_update(scene, ss->cache->location); BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); @@ -2446,7 +2436,7 @@ static int wpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event) event->type); if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { - paint_stroke_data_free(op); + paint_stroke_free(C, op); return OPERATOR_FINISHED; } /* add modal handler */ @@ -2597,11 +2587,6 @@ void PAINT_OT_vertex_paint_toggle(wmOperatorType *ot) * - revise whether op->customdata should be added in object, in set_vpaint */ -typedef struct PolyFaceMap { - struct PolyFaceMap *next, *prev; - int facenr; -} PolyFaceMap; - struct VPaintData { ViewContext vc; struct NormalAnglePrecalc normal_angle_precalc; @@ -2655,7 +2640,7 @@ static bool vpaint_stroke_test_start(bContext *C, struct wmOperator *op, const f /* make mode data storage */ vpd = MEM_callocN(sizeof(*vpd), "VPaintData"); paint_stroke_set_mode_data(stroke, vpd); - ED_view3d_viewcontext_init(C, &vpd->vc); + ED_view3d_viewcontext_init(C, &vpd->vc, depsgraph); view_angle_limits_init(&vpd->normal_angle_precalc, vp->paint.brush->falloff_angle, (vp->paint.brush->flag & BRUSH_FRONTFACE_FALLOFF) != 0); @@ -3141,9 +3126,9 @@ static void calculate_average_color(SculptThreadedTaskData *data, struct VPaintAverageAccum *accum = MEM_mallocN(sizeof(*accum) * totnode, __func__); data->custom_data = accum; - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - BLI_task_parallel_range(0, totnode, data, do_vpaint_brush_calc_average_color_cb_ex, &settings); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BKE_pbvh_parallel_range(0, totnode, data, do_vpaint_brush_calc_average_color_cb_ex, &settings); uint accum_len = 0; uint accum_value[3] = {0}; @@ -3187,21 +3172,21 @@ static void vpaint_paint_leaves(bContext *C, .lcol = (uint *)me->mloopcol, .me = me, }; - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); switch ((eBrushVertexPaintTool)brush->vertexpaint_tool) { case VPAINT_TOOL_AVERAGE: calculate_average_color(&data, nodes, totnode); - BLI_task_parallel_range(0, totnode, &data, do_vpaint_brush_draw_task_cb_ex, &settings); + BKE_pbvh_parallel_range(0, totnode, &data, do_vpaint_brush_draw_task_cb_ex, &settings); break; case VPAINT_TOOL_BLUR: - BLI_task_parallel_range(0, totnode, &data, do_vpaint_brush_blur_task_cb_ex, &settings); + BKE_pbvh_parallel_range(0, totnode, &data, do_vpaint_brush_blur_task_cb_ex, &settings); break; case VPAINT_TOOL_SMEAR: - BLI_task_parallel_range(0, totnode, &data, do_vpaint_brush_smear_task_cb_ex, &settings); + BKE_pbvh_parallel_range(0, totnode, &data, do_vpaint_brush_smear_task_cb_ex, &settings); break; case VPAINT_TOOL_DRAW: - BLI_task_parallel_range(0, totnode, &data, do_vpaint_brush_draw_task_cb_ex, &settings); + BKE_pbvh_parallel_range(0, totnode, &data, do_vpaint_brush_draw_task_cb_ex, &settings); break; } } @@ -3331,7 +3316,7 @@ static void vpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P /* calculate pivot for rotation around seletion if needed */ /* also needed for "View Selected" on last stroke */ - paint_last_stroke_update(scene, vc->ar, ss->cache->mouse); + paint_last_stroke_update(scene, ss->cache->location); ED_region_tag_redraw(vc->ar); @@ -3388,7 +3373,7 @@ static int vpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event) event->type); if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { - paint_stroke_data_free(op); + paint_stroke_free(C, op); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/sculpt_paint/paint_vertex_color_utils.c b/source/blender/editors/sculpt_paint/paint_vertex_color_utils.c index 068c36abdaa..71865d0de73 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_color_utils.c +++ b/source/blender/editors/sculpt_paint/paint_vertex_color_utils.c @@ -20,13 +20,9 @@ * Intended for use by `paint_vertex.c` & `paint_vertex_color_ops.c`. */ -#include "MEM_guardedalloc.h" - -#include "DNA_brush_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" -#include "DNA_scene_types.h" #include "BLI_math_base.h" #include "BLI_math_color.h" diff --git a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c index 4aa9dc8a295..f0fe2d4ebdc 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c +++ b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c @@ -24,13 +24,8 @@ #include "BLI_math.h" #include "BLI_bitmap.h" -#include "IMB_imbuf.h" -#include "IMB_imbuf_types.h" -#include "IMB_colormanagement.h" - #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" -#include "DNA_particle_types.h" #include "DNA_brush_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" @@ -44,7 +39,6 @@ #include "BKE_deform.h" #include "BKE_mesh.h" #include "BKE_mesh_iterators.h" -#include "BKE_mesh_mapping.h" #include "BKE_mesh_runtime.h" #include "BKE_modifier.h" #include "BKE_object_deform.h" @@ -178,11 +172,12 @@ void PAINT_OT_weight_from_bones(wmOperatorType *ot) /* note: we cant sample frontbuf, weight colors are interpolated too unpredictable */ static int weight_sample_invoke(bContext *C, wmOperator *op, const wmEvent *event) { + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewContext vc; Mesh *me; bool changed = false; - ED_view3d_viewcontext_init(C, &vc); + ED_view3d_viewcontext_init(C, &vc, depsgraph); me = BKE_mesh_from_object(vc.obact); if (me && me->dvert && vc.v3d && vc.rv3d && (vc.obact->actdef != 0)) { @@ -308,10 +303,11 @@ static const EnumPropertyItem *weight_paint_sample_enum_itemf(bContext *C, if (C) { wmWindow *win = CTX_wm_window(C); if (win && win->eventstate) { + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); ViewContext vc; Mesh *me; - ED_view3d_viewcontext_init(C, &vc); + ED_view3d_viewcontext_init(C, &vc, depsgraph); me = BKE_mesh_from_object(vc.obact); if (me && me->dvert && vc.v3d && vc.rv3d && vc.obact->defbase.first) { @@ -379,8 +375,9 @@ static const EnumPropertyItem *weight_paint_sample_enum_itemf(bContext *C, static int weight_sample_group_exec(bContext *C, wmOperator *op) { int type = RNA_enum_get(op->ptr, "group"); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewContext vc; - ED_view3d_viewcontext_init(C, &vc); + ED_view3d_viewcontext_init(C, &vc, depsgraph); BLI_assert(type + 1 >= 0); vc.obact->actdef = type + 1; @@ -885,7 +882,7 @@ void PAINT_OT_weight_gradient(wmOperatorType *ot) prop = RNA_def_enum(ot->srna, "type", gradient_types, 0, "Type", ""); RNA_def_property_flag(prop, PROP_SKIP_SAVE); - WM_operator_properties_gesture_straightline(ot, CURSOR_EDIT); + WM_operator_properties_gesture_straightline(ot, WM_CURSOR_EDIT); } /** \} */ diff --git a/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c b/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c index c71315872f6..28699b45add 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c +++ b/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c @@ -20,16 +20,12 @@ * Intended for use by `paint_vertex.c` & `paint_vertex_weight_ops.c`. */ -#include "MEM_guardedalloc.h" - #include "BLI_listbase.h" #include "BLI_math.h" #include "BLI_string_utils.h" #include "DNA_armature_types.h" #include "DNA_mesh_types.h" -#include "DNA_scene_types.h" -#include "DNA_brush_types.h" #include "DNA_object_types.h" #include "BKE_action.h" diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 351e4c0482b..06fa03ccbc6 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -27,13 +27,11 @@ #include "BLI_math.h" #include "BLI_blenlib.h" #include "BLI_dial_2d.h" -#include "BLI_hash.h" #include "BLI_gsqueue.h" -#include "BLI_stack.h" +#include "BLI_ghash.h" +#include "BLI_hash.h" #include "BLI_task.h" -#include "BLI_stack.h" #include "BLI_utildefines.h" -#include "BLI_ghash.h" #include "BLT_translation.h" @@ -66,10 +64,10 @@ #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_screen.h" +#include "BKE_subdiv_ccg.h" #include "BKE_subsurf.h" #include "DEG_depsgraph.h" -#include "DEG_depsgraph_query.h" #include "WM_api.h" #include "WM_types.h" @@ -96,21 +94,13 @@ #include <stdlib.h> #include <string.h> -/* Sculpt PBVH abstraction API */ - -/* Do not use these functions while working with PBVH_GRIDS data in SculptSession */ - -float *sculpt_vertex_co_get(SculptSession *ss, int index) -{ - switch (BKE_pbvh_type(ss->pbvh)) { - case PBVH_FACES: - return ss->mvert[index].co; - case PBVH_BMESH: - return BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index)->co; - default: - return NULL; - } -} +/* Sculpt PBVH abstraction API + * + * This is read-only, for writing use PBVH vertex iterators. There vd.index matches + * the indices used here. + * + * For multires, the same vertex in multiple grids is counted multiple times, with + * different index for each grid. */ static void sculpt_vertex_random_access_init(SculptSession *ss) { @@ -119,28 +109,36 @@ static void sculpt_vertex_random_access_init(SculptSession *ss) } } -static int sculpt_active_vertex_get(SculptSession *ss) +static int sculpt_vertex_count_get(SculptSession *ss) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: - return ss->active_vertex_index; + return ss->totvert; case PBVH_BMESH: - return ss->active_vertex_index; - default: - return 0; + return BM_mesh_elem_count(BKE_pbvh_get_bmesh(ss->pbvh), BM_VERT); + case PBVH_GRIDS: + return BKE_pbvh_get_grid_num_vertices(ss->pbvh); } + + return 0; } -static int sculpt_vertex_count_get(SculptSession *ss) +const float *sculpt_vertex_co_get(SculptSession *ss, int index) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: - return ss->totvert; + return ss->mvert[index].co; case PBVH_BMESH: - return BM_mesh_elem_count(BKE_pbvh_get_bmesh(ss->pbvh), BM_VERT); - default: - return 0; + return BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index)->co; + case PBVH_GRIDS: { + const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); + const int grid_index = index / key->grid_area; + const int vertex_index = index - grid_index * key->grid_area; + CCGElem *elem = BKE_pbvh_get_grids(ss->pbvh)[grid_index]; + return CCG_elem_co(key, CCG_elem_offset(key, elem, vertex_index)); + } } + return NULL; } static void sculpt_vertex_normal_get(SculptSession *ss, int index, float no[3]) @@ -152,27 +150,14 @@ static void sculpt_vertex_normal_get(SculptSession *ss, int index, float no[3]) case PBVH_BMESH: copy_v3_v3(no, BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index)->no); break; - default: - zero_v3(no); - return; - } -} - -static void sculpt_vertex_mask_set(SculptSession *ss, int index, float mask) -{ - BMVert *v; - float *mask_p; - switch (BKE_pbvh_type(ss->pbvh)) { - case PBVH_FACES: - ss->vmask[index] = mask; - return; - case PBVH_BMESH: - v = BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index); - mask_p = BM_ELEM_CD_GET_VOID_P(v, CustomData_get_offset(&ss->bm->vdata, CD_PAINT_MASK)); - *(mask_p) = mask; - return; - default: - return; + case PBVH_GRIDS: { + const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); + const int grid_index = index / key->grid_area; + const int vertex_index = index - grid_index * key->grid_area; + CCGElem *elem = BKE_pbvh_get_grids(ss->pbvh)[grid_index]; + copy_v3_v3(no, CCG_elem_no(key, CCG_elem_offset(key, elem, vertex_index))); + break; + } } } @@ -187,49 +172,58 @@ static float sculpt_vertex_mask_get(SculptSession *ss, int index) v = BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index); mask = BM_ELEM_CD_GET_VOID_P(v, CustomData_get_offset(&ss->bm->vdata, CD_PAINT_MASK)); return *mask; - default: - return 0; + case PBVH_GRIDS: { + const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); + const int grid_index = index / key->grid_area; + const int vertex_index = index - grid_index * key->grid_area; + CCGElem *elem = BKE_pbvh_get_grids(ss->pbvh)[grid_index]; + return *CCG_elem_mask(key, CCG_elem_offset(key, elem, vertex_index)); + } } + + return 0.0f; } -static void UNUSED_FUNCTION(sculpt_vertex_co_set)(SculptSession *ss, int index, float co[3]) +static int sculpt_active_vertex_get(SculptSession *ss) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: - copy_v3_v3(ss->mvert[index].co, co); - return; + return ss->active_vertex_index; case PBVH_BMESH: - copy_v3_v3(BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index)->co, co); - return; - default: - return; + return ss->active_vertex_index; + case PBVH_GRIDS: + return ss->active_vertex_index; } + + return 0; } -static void UNUSED_FUNCTION(sculpt_vertex_tag_update)(SculptSession *ss, int index) +static const float *sculpt_active_vertex_co_get(SculptSession *ss) { - switch (BKE_pbvh_type(ss->pbvh)) { - case PBVH_FACES: - ss->mvert[index].flag |= ME_VERT_PBVH_UPDATE; - return; - case PBVH_BMESH: - return; - default: - return; - } + return sculpt_vertex_co_get(ss, sculpt_active_vertex_get(ss)); +} + +static void sculpt_active_vertex_normal_get(SculptSession *ss, float normal[3]) +{ + sculpt_vertex_normal_get(ss, sculpt_active_vertex_get(ss), normal); } #define SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY 256 typedef struct SculptVertexNeighborIter { + /* Storage */ int *neighbors; int size; int capacity; - int neighbors_fixed[SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY]; - int index; + /* Internal iterator. */ + int num_duplicates; int i; + + /* Public */ + int index; + bool is_duplicate; } SculptVertexNeighborIter; static void sculpt_vertex_neighbor_add(SculptVertexNeighborIter *iter, int neighbor_index) @@ -265,6 +259,7 @@ static void sculpt_vertex_neighbors_get_bmesh(SculptSession *ss, BMIter liter; BMLoop *l; iter->size = 0; + iter->num_duplicates = 0; iter->capacity = SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY; iter->neighbors = iter->neighbors_fixed; @@ -287,6 +282,7 @@ static void sculpt_vertex_neighbors_get_faces(SculptSession *ss, int i; MeshElemMap *vert_map = &ss->pmap[(int)index]; iter->size = 0; + iter->num_duplicates = 0; iter->capacity = SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY; iter->neighbors = iter->neighbors_fixed; @@ -296,18 +292,52 @@ static void sculpt_vertex_neighbors_get_faces(SculptSession *ss, if (poly_get_adj_loops_from_vert(p, ss->mloop, (int)index, f_adj_v) != -1) { int j; for (j = 0; j < ARRAY_SIZE(f_adj_v); j += 1) { - if (vert_map->count != 2 || ss->pmap[f_adj_v[j]].count <= 2) { - if (f_adj_v[j] != (int)index) { - sculpt_vertex_neighbor_add(iter, f_adj_v[j]); - } + if (f_adj_v[j] != (int)index) { + sculpt_vertex_neighbor_add(iter, f_adj_v[j]); } } } } } +static void sculpt_vertex_neighbors_get_grids(SculptSession *ss, + const int index, + const bool include_duplicates, + SculptVertexNeighborIter *iter) +{ + /* TODO: optimize this. We could fill SculptVertexNeighborIter directly, + * maybe provide coordinate and mask pointers directly rather than converting + * back and forth between CCGElem and global index. */ + const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); + const int grid_index = index / key->grid_area; + const int vertex_index = index - grid_index * key->grid_area; + + SubdivCCGCoord coord = {.grid_index = grid_index, + .x = vertex_index % key->grid_size, + .y = vertex_index / key->grid_size}; + + SubdivCCGNeighbors neighbors; + BKE_subdiv_ccg_neighbor_coords_get(ss->subdiv_ccg, &coord, include_duplicates, &neighbors); + + iter->size = 0; + iter->num_duplicates = neighbors.num_duplicates; + iter->capacity = SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY; + iter->neighbors = iter->neighbors_fixed; + + for (int i = 0; i < neighbors.size; i++) { + sculpt_vertex_neighbor_add(iter, + neighbors.coords[i].grid_index * key->grid_area + + neighbors.coords[i].y * key->grid_size + neighbors.coords[i].x); + } + + if (neighbors.coords != neighbors.coords_fixed) { + MEM_freeN(neighbors.coords); + } +} + static void sculpt_vertex_neighbors_get(SculptSession *ss, - int index, + const int index, + const bool include_duplicates, SculptVertexNeighborIter *iter) { switch (BKE_pbvh_type(ss->pbvh)) { @@ -317,17 +347,29 @@ static void sculpt_vertex_neighbors_get(SculptSession *ss, case PBVH_BMESH: sculpt_vertex_neighbors_get_bmesh(ss, index, iter); return; - default: - break; + case PBVH_GRIDS: + sculpt_vertex_neighbors_get_grids(ss, index, include_duplicates, iter); + return; } } +/* Iterator over neighboring vertices. */ #define sculpt_vertex_neighbors_iter_begin(ss, v_index, neighbor_iterator) \ - sculpt_vertex_neighbors_get(ss, v_index, &neighbor_iterator); \ + sculpt_vertex_neighbors_get(ss, v_index, false, &neighbor_iterator); \ for (neighbor_iterator.i = 0; neighbor_iterator.i < neighbor_iterator.size; \ neighbor_iterator.i++) { \ neighbor_iterator.index = ni.neighbors[ni.i]; +/* Iterate over neighboring and duplicate vertices (for PBVH_GRIDS). Duplicates come + * first since they are nearest for floodfill. */ +#define sculpt_vertex_duplicates_and_neighbors_iter_begin(ss, v_index, neighbor_iterator) \ + sculpt_vertex_neighbors_get(ss, v_index, true, &neighbor_iterator); \ + for (neighbor_iterator.i = neighbor_iterator.size - 1; neighbor_iterator.i >= 0; \ + neighbor_iterator.i--) { \ + neighbor_iterator.index = ni.neighbors[ni.i]; \ + neighbor_iterator.is_duplicate = (ni.i >= \ + neighbor_iterator.size - neighbor_iterator.num_duplicates); + #define sculpt_vertex_neighbors_iter_end(neighbor_iterator) \ } \ if (neighbor_iterator.neighbors != neighbor_iterator.neighbors_fixed) { \ @@ -336,17 +378,6 @@ static void sculpt_vertex_neighbors_get(SculptSession *ss, ((void)0) /* Utils */ -static void sculpt_vertex_mask_clamp(SculptSession *ss, int index, float min, float max) -{ - float mask = sculpt_vertex_mask_get(ss, index); - if (mask > max) { - sculpt_vertex_mask_set(ss, index, max); - } - else if (mask < min) { - sculpt_vertex_mask_set(ss, index, min); - } -} - static bool check_vertex_pivot_symmetry(const float vco[3], const float pco[3], const char symm) { bool is_in_symmetry_area = true; @@ -366,37 +397,46 @@ static bool check_vertex_pivot_symmetry(const float vco[3], const float pco[3], return is_in_symmetry_area; } +typedef struct NearestVertexTLSData { + int nearest_vertex_index; + float nearest_vertex_distance_squared; +} NearestVertexTLSData; + static void do_nearest_vertex_get_task_cb(void *__restrict userdata, const int n, - const TaskParallelTLS *__restrict UNUSED(tls)) + const TaskParallelTLS *__restrict tls) { SculptThreadedTaskData *data = userdata; SculptSession *ss = data->ob->sculpt; + NearestVertexTLSData *nvtd = tls->userdata_chunk; PBVHVertexIter vd; - int node_nearest_vertex_index = -1; - float node_nearest_vertex_distance_squared = FLT_MAX; BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { float distance_squared = len_squared_v3v3(vd.co, data->nearest_vertex_search_co); - if (distance_squared < node_nearest_vertex_distance_squared && + if (distance_squared < nvtd->nearest_vertex_distance_squared && distance_squared < data->max_distance_squared) { - node_nearest_vertex_index = vd.index; - node_nearest_vertex_distance_squared = distance_squared; + nvtd->nearest_vertex_index = vd.index; + nvtd->nearest_vertex_distance_squared = distance_squared; } } BKE_pbvh_vertex_iter_end; +} - BLI_mutex_lock(&data->mutex); - if (data->nearest_vertex_index == -1) { - data->nearest_vertex_index = node_nearest_vertex_index; +static void nearest_vertex_get_reduce(const void *__restrict UNUSED(userdata), + void *__restrict chunk_join, + void *__restrict chunk) +{ + NearestVertexTLSData *join = chunk_join; + NearestVertexTLSData *nvtd = chunk; + if (join->nearest_vertex_index == -1) { + join->nearest_vertex_index = nvtd->nearest_vertex_index; + join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared; } - else if (node_nearest_vertex_distance_squared < - len_squared_v3v3(data->nearest_vertex_search_co, - sculpt_vertex_co_get(ss, data->nearest_vertex_index))) { - data->nearest_vertex_index = node_nearest_vertex_index; + else if (nvtd->nearest_vertex_distance_squared < join->nearest_vertex_distance_squared) { + join->nearest_vertex_index = nvtd->nearest_vertex_index; + join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared; } - BLI_mutex_unlock(&data->mutex); } static int sculpt_nearest_vertex_get( @@ -422,19 +462,23 @@ static int sculpt_nearest_vertex_get( .ob = ob, .nodes = nodes, .max_distance_squared = max_distance * max_distance, - .nearest_vertex_index = -1, }; copy_v3_v3(task_data.nearest_vertex_search_co, co); + NearestVertexTLSData nvtd; + nvtd.nearest_vertex_index = -1; + nvtd.nearest_vertex_distance_squared = FLT_MAX; - BLI_mutex_init(&task_data.mutex); - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range(0, totnode, &task_data, do_nearest_vertex_get_task_cb, &settings); - BLI_mutex_end(&task_data.mutex); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + settings.func_reduce = nearest_vertex_get_reduce; + settings.userdata_chunk = &nvtd; + settings.userdata_chunk_size = sizeof(NearestVertexTLSData); + BKE_pbvh_parallel_range(0, totnode, &task_data, do_nearest_vertex_get_task_cb, &settings); - return task_data.nearest_vertex_index; + MEM_SAFE_FREE(nodes); + + return nvtd.nearest_vertex_index; } static bool is_symmetry_iteration_valid(char i, char symm) @@ -443,7 +487,7 @@ static bool is_symmetry_iteration_valid(char i, char symm) } /* Checks if a vertex is inside the brush radius from any of its mirrored axis */ -static bool sculpt_is_vertex_inside_brush_radius_symm(float vertex[3], +static bool sculpt_is_vertex_inside_brush_radius_symm(const float vertex[3], const float br_co[3], float radius, char symm) @@ -460,6 +504,85 @@ static bool sculpt_is_vertex_inside_brush_radius_symm(float vertex[3], return false; } +/* Sculpt Flood Fill API + * + * Iterate over connected vertices, starting from one or more initial vertices. */ + +typedef struct SculptFloodFill { + GSQueue *queue; + char *visited_vertices; +} SculptFloodFill; + +static void sculpt_floodfill_init(SculptSession *ss, SculptFloodFill *flood) +{ + int vertex_count = sculpt_vertex_count_get(ss); + sculpt_vertex_random_access_init(ss); + + flood->queue = BLI_gsqueue_new(sizeof(int)); + flood->visited_vertices = MEM_callocN(vertex_count * sizeof(char), "visited vertices"); +} + +static void sculpt_floodfill_add_initial(SculptFloodFill *flood, int index) +{ + BLI_gsqueue_push(flood->queue, &index); +} + +static void sculpt_floodfill_add_active( + Sculpt *sd, Object *ob, SculptSession *ss, SculptFloodFill *flood, float radius) +{ + /* Add active vertex and symmetric vertices to the queue. */ + const char symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; + for (char i = 0; i <= symm; ++i) { + if (is_symmetry_iteration_valid(i, symm)) { + int v = -1; + if (i == 0) { + v = sculpt_active_vertex_get(ss); + } + else if (radius > 0.0f) { + float radius_squared = (radius == FLT_MAX) ? FLT_MAX : radius * radius; + float location[3]; + flip_v3_v3(location, sculpt_active_vertex_co_get(ss), i); + v = sculpt_nearest_vertex_get(sd, ob, location, radius_squared, false); + } + if (v != -1) { + sculpt_floodfill_add_initial(flood, v); + } + } + } +} + +static void sculpt_floodfill_execute( + SculptSession *ss, + SculptFloodFill *flood, + bool (*func)(SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata), + void *userdata) +{ + while (!BLI_gsqueue_is_empty(flood->queue)) { + int from_v; + BLI_gsqueue_pop(flood->queue, &from_v); + SculptVertexNeighborIter ni; + sculpt_vertex_duplicates_and_neighbors_iter_begin(ss, from_v, ni) + { + const int to_v = ni.index; + if (flood->visited_vertices[to_v] == 0) { + flood->visited_vertices[to_v] = 1; + + if (func(ss, from_v, to_v, ni.is_duplicate, userdata)) { + BLI_gsqueue_push(flood->queue, &to_v); + } + } + } + sculpt_vertex_neighbors_iter_end(ni); + } +} + +static void sculpt_floodfill_free(SculptFloodFill *flood) +{ + MEM_SAFE_FREE(flood->visited_vertices); + BLI_gsqueue_free(flood->queue); + flood->queue = NULL; +} + /** \name Tool Capabilities * * Avoid duplicate checks, internal logic only, @@ -814,15 +937,11 @@ static void paint_mesh_restore_co(Sculpt *sd, Object *ob) .nodes = nodes, }; - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && !ss->bm && - totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range(0, totnode, &data, paint_mesh_restore_co_task_cb, &settings); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP) && !ss->bm, totnode); + BKE_pbvh_parallel_range(0, totnode, &data, paint_mesh_restore_co_task_cb, &settings); - if (nodes) { - MEM_freeN(nodes); - } + MEM_SAFE_FREE(nodes); } /*** BVH Tree ***/ @@ -1003,9 +1122,8 @@ bool sculpt_brush_test_cube(SculptBrushTest *test, const float co[3], float loca local_co[1] = fabsf(local_co[1]); local_co[2] = fabsf(local_co[2]); + const float p = 8.0f; if (local_co[0] <= side && local_co[1] <= side && local_co[2] <= side) { - float p = 4.0f; - test->dist = ((powf(local_co[0], p) + powf(local_co[1], p) + powf(local_co[2], p)) / powf(side, p)); @@ -1100,11 +1218,6 @@ static bool sculpt_brush_test_cyl(SculptBrushTest *test, static bool sculpt_automasking_enabled(SculptSession *ss, const Brush *br) { - // REMOVE WITH PBVH_GRIDS - if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { - return false; - } - if (sculpt_stroke_is_dynamic_topology(ss, br)) { return false; } @@ -1134,21 +1247,38 @@ static void sculpt_automasking_end(Object *ob) static bool sculpt_automasking_is_constrained_by_radius(Brush *br) { + /* 2D falloff is not constrained by radius */ + if (br->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { + return false; + } + if (ELEM(br->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB)) { return true; } return false; } -typedef struct VertexTopologyIterator { - int v; - int it; - float edge_factor; -} VertexTopologyIterator; +typedef struct AutomaskFloodFillData { + float *automask_factor; + float radius; + bool use_radius; + float location[3]; + char symm; +} AutomaskFloodFillData; -static float *sculpt_topology_automasking_init(Sculpt *sd, Object *ob, float *automask_factor) +static bool automask_floodfill_cb( + SculptSession *ss, int UNUSED(from_v), int to_v, bool UNUSED(is_duplicate), void *userdata) { + AutomaskFloodFillData *data = userdata; + data->automask_factor[to_v] = 1.0f; + return (!data->use_radius || + sculpt_is_vertex_inside_brush_radius_symm( + sculpt_vertex_co_get(ss, to_v), data->location, data->radius, data->symm)); +} + +static float *sculpt_topology_automasking_init(Sculpt *sd, Object *ob, float *automask_factor) +{ SculptSession *ss = ob->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); @@ -1161,63 +1291,21 @@ static float *sculpt_topology_automasking_init(Sculpt *sd, Object *ob, float *au return NULL; } - bool *visited_vertices = MEM_callocN(sculpt_vertex_count_get(ss) * sizeof(bool), - "visited vertices"); - - BLI_Stack *not_visited_vertices = BLI_stack_new(sizeof(VertexTopologyIterator), - "not vertices stack"); - - VertexTopologyIterator mevit; - - /* Add active vertex and symmetric vertices to the stack. */ - float location[3]; - const char symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; - for (char i = 0; i <= symm; ++i) { - if (is_symmetry_iteration_valid(i, symm)) { - flip_v3_v3(location, sculpt_vertex_co_get(ss, sculpt_active_vertex_get(ss)), i); - if (i == 0) { - mevit.v = sculpt_active_vertex_get(ss); - } - else { - mevit.v = sculpt_nearest_vertex_get( - sd, ob, location, ss->cache->radius * ss->cache->radius, false); - } - if (mevit.v != -1) { - mevit.it = 1; - BLI_stack_push(not_visited_vertices, &mevit); - } - } - } + /* Flood fill automask to connected vertices. Limited to vertices inside + * the brush radius if the tool requires it */ + SculptFloodFill flood; + sculpt_floodfill_init(ss, &flood); + sculpt_floodfill_add_active(sd, ob, ss, &flood, ss->cache->radius); - copy_v3_v3(location, sculpt_vertex_co_get(ss, sculpt_active_vertex_get(ss))); - bool use_radius = sculpt_automasking_is_constrained_by_radius(brush); - - /* Flood fill automask to connected vertices. Limited to vertices inside the brush radius if the - * tool requires it */ - while (!BLI_stack_is_empty(not_visited_vertices)) { - VertexTopologyIterator c_mevit; - BLI_stack_pop(not_visited_vertices, &c_mevit); - SculptVertexNeighborIter ni; - sculpt_vertex_neighbors_iter_begin(ss, c_mevit.v, ni) - { - if (!visited_vertices[(int)ni.index]) { - VertexTopologyIterator new_entry; - new_entry.v = ni.index; - automask_factor[new_entry.v] = 1.0f; - visited_vertices[(int)ni.index] = true; - if (!use_radius || - sculpt_is_vertex_inside_brush_radius_symm( - sculpt_vertex_co_get(ss, new_entry.v), location, ss->cache->radius, symm)) { - BLI_stack_push(not_visited_vertices, &new_entry); - } - } - } - sculpt_vertex_neighbors_iter_end(ni); - } - - BLI_stack_free(not_visited_vertices); - - MEM_freeN(visited_vertices); + AutomaskFloodFillData fdata = { + .automask_factor = automask_factor, + .radius = ss->cache->radius, + .use_radius = sculpt_automasking_is_constrained_by_radius(brush), + .symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL, + }; + copy_v3_v3(fdata.location, sculpt_active_vertex_co_get(ss)); + sculpt_floodfill_execute(ss, &flood, automask_floodfill_cb, &fdata); + sculpt_floodfill_free(&flood); return automask_factor; } @@ -1324,21 +1412,26 @@ static float calc_symmetry_feather(Sculpt *sd, StrokeCache *cache) * \note These are all _very_ similar, when changing one, check others. * \{ */ +typedef struct AreaNormalCenterTLSData { + /* 0=towards view, 1=flipped */ + float area_cos[2][3]; + float area_nos[2][3]; + int area_count[2]; +} AreaNormalCenterTLSData; + static void calc_area_normal_and_center_task_cb(void *__restrict userdata, const int n, - const TaskParallelTLS *__restrict UNUSED(tls)) + const TaskParallelTLS *__restrict tls) { SculptThreadedTaskData *data = userdata; SculptSession *ss = data->ob->sculpt; - float(*area_nos)[3] = data->area_nos; - float(*area_cos)[3] = data->area_cos; + AreaNormalCenterTLSData *anctd = tls->userdata_chunk; + const bool use_area_nos = data->use_area_nos; + const bool use_area_cos = data->use_area_cos; PBVHVertexIter vd; SculptUndoNode *unode = NULL; - float private_co[2][3] = {{0.0f}}; - float private_no[2][3] = {{0.0f}}; - int private_count[2] = {0}; bool use_original = false; if (ss->cache && ss->cache->original) { @@ -1353,7 +1446,10 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata, /* Update the test radius to sample the normal using the normal radius of the brush */ if (data->brush->ob_mode == OB_MODE_SCULPT) { float test_radius = sqrtf(test.radius_squared); - test_radius *= data->brush->normal_radius_factor; + /* Layer brush produces artifacts with normal radius */ + if (!(ss->cache && data->brush->sculpt_tool == SCULPT_TOOL_LAYER)) { + test_radius *= data->brush->normal_radius_factor; + } test.radius_squared = test_radius * test_radius; } @@ -1384,13 +1480,13 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata, normal_tri_v3(no, UNPACK3(co_tri)); flip_index = (dot_v3v3(ss->cache->view_normal, no) <= 0.0f); - if (area_cos) { - add_v3_v3(private_co[flip_index], co); + if (use_area_cos) { + add_v3_v3(anctd->area_cos[flip_index], co); } - if (area_nos) { - add_v3_v3(private_no[flip_index], no); + if (use_area_nos) { + add_v3_v3(anctd->area_nos[flip_index], no); } - private_count[flip_index] += 1; + anctd->area_count[flip_index] += 1; } } } @@ -1436,37 +1532,37 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata, flip_index = (dot_v3v3(ss->cache ? ss->cache->view_normal : ss->cursor_view_normal, no) <= 0.0f); - if (area_cos) { - add_v3_v3(private_co[flip_index], co); + if (use_area_cos) { + add_v3_v3(anctd->area_cos[flip_index], co); } - if (area_nos) { - add_v3_v3(private_no[flip_index], no); + if (use_area_nos) { + add_v3_v3(anctd->area_nos[flip_index], no); } - private_count[flip_index] += 1; + anctd->area_count[flip_index] += 1; } } BKE_pbvh_vertex_iter_end; } +} - BLI_mutex_lock(&data->mutex); +static void calc_area_normal_and_center_reduce(const void *__restrict UNUSED(userdata), + void *__restrict chunk_join, + void *__restrict chunk) +{ + AreaNormalCenterTLSData *join = chunk_join; + AreaNormalCenterTLSData *anctd = chunk; /* for flatten center */ - if (area_cos) { - add_v3_v3(area_cos[0], private_co[0]); - add_v3_v3(area_cos[1], private_co[1]); - } + add_v3_v3(join->area_cos[0], anctd->area_cos[0]); + add_v3_v3(join->area_cos[1], anctd->area_cos[1]); /* for area normal */ - if (area_nos) { - add_v3_v3(area_nos[0], private_no[0]); - add_v3_v3(area_nos[1], private_no[1]); - } + add_v3_v3(join->area_nos[0], anctd->area_nos[0]); + add_v3_v3(join->area_nos[1], anctd->area_nos[1]); /* weights */ - data->count[0] += private_count[0]; - data->count[1] += private_count[1]; - - BLI_mutex_unlock(&data->mutex); + join->area_count[0] += anctd->area_count[0]; + join->area_count[1] += anctd->area_count[1]; } static void calc_area_center( @@ -1477,11 +1573,6 @@ static void calc_area_center( const bool has_bm_orco = ss->bm && sculpt_stroke_is_dynamic_topology(ss, brush); int n; - /* 0=towards view, 1=flipped */ - float area_cos[2][3] = {{0.0f}}; - - int count[2] = {0}; - /* Intentionally set 'sd' to NULL since we share logic with vertex paint. */ SculptThreadedTaskData data = { .sd = NULL, @@ -1490,23 +1581,22 @@ static void calc_area_center( .nodes = nodes, .totnode = totnode, .has_bm_orco = has_bm_orco, - .area_cos = area_cos, - .area_nos = NULL, - .count = count, + .use_area_cos = true, }; - BLI_mutex_init(&data.mutex); - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range(0, totnode, &data, calc_area_normal_and_center_task_cb, &settings); + AreaNormalCenterTLSData anctd = {{{0}}}; - BLI_mutex_end(&data.mutex); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + settings.func_reduce = calc_area_normal_and_center_reduce; + settings.userdata_chunk = &anctd; + settings.userdata_chunk_size = sizeof(AreaNormalCenterTLSData); + BKE_pbvh_parallel_range(0, totnode, &data, calc_area_normal_and_center_task_cb, &settings); /* for flatten center */ - for (n = 0; n < ARRAY_SIZE(area_cos); n++) { - if (count[n] != 0) { - mul_v3_v3fl(r_area_co, area_cos[n], 1.0f / count[n]); + for (n = 0; n < ARRAY_SIZE(anctd.area_cos); n++) { + if (anctd.area_count[n] != 0) { + mul_v3_v3fl(r_area_co, anctd.area_cos[n], 1.0f / anctd.area_count[n]); break; } } @@ -1519,7 +1609,7 @@ static void calc_area_normal( Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_no[3]) { const Brush *brush = BKE_paint_brush(&sd->paint); - bool use_threading = (sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT; + bool use_threading = (sd->flags & SCULPT_USE_OPENMP); sculpt_pbvh_calc_area_normal(brush, ob, nodes, totnode, use_threading, r_area_no); } @@ -1534,11 +1624,6 @@ bool sculpt_pbvh_calc_area_normal(const Brush *brush, SculptSession *ss = ob->sculpt; const bool has_bm_orco = ss->bm && sculpt_stroke_is_dynamic_topology(ss, brush); - /* 0=towards view, 1=flipped */ - float area_nos[2][3] = {{0.0f}}; - - int count[2] = {0}; - /* Intentionally set 'sd' to NULL since this is used for vertex paint too. */ SculptThreadedTaskData data = { .sd = NULL, @@ -1547,23 +1632,22 @@ bool sculpt_pbvh_calc_area_normal(const Brush *brush, .nodes = nodes, .totnode = totnode, .has_bm_orco = has_bm_orco, - .area_cos = NULL, - .area_nos = area_nos, - .count = count, + .use_area_nos = true, .any_vertex_sampled = false, }; - BLI_mutex_init(&data.mutex); - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = use_threading; - BLI_task_parallel_range(0, totnode, &data, calc_area_normal_and_center_task_cb, &settings); + AreaNormalCenterTLSData anctd = {{{0}}}; - BLI_mutex_end(&data.mutex); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, use_threading, totnode); + settings.func_reduce = calc_area_normal_and_center_reduce; + settings.userdata_chunk = &anctd; + settings.userdata_chunk_size = sizeof(AreaNormalCenterTLSData); + BKE_pbvh_parallel_range(0, totnode, &data, calc_area_normal_and_center_task_cb, &settings); /* for area normal */ - for (int i = 0; i < ARRAY_SIZE(area_nos); i++) { - if (normalize_v3_v3(r_area_no, area_nos[i]) != 0.0f) { + for (int i = 0; i < ARRAY_SIZE(anctd.area_nos); i++) { + if (normalize_v3_v3(r_area_no, anctd.area_nos[i]) != 0.0f) { break; } } @@ -1581,12 +1665,6 @@ static void calc_area_normal_and_center( const bool has_bm_orco = ss->bm && sculpt_stroke_is_dynamic_topology(ss, brush); int n; - /* 0=towards view, 1=flipped */ - float area_cos[2][3] = {{0.0f}}; - float area_nos[2][3] = {{0.0f}}; - - int count[2] = {0}; - /* Intentionally set 'sd' to NULL since this is used for vertex paint too. */ SculptThreadedTaskData data = { .sd = NULL, @@ -1595,23 +1673,23 @@ static void calc_area_normal_and_center( .nodes = nodes, .totnode = totnode, .has_bm_orco = has_bm_orco, - .area_cos = area_cos, - .area_nos = area_nos, - .count = count, + .use_area_cos = true, + .use_area_nos = true, }; - BLI_mutex_init(&data.mutex); - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range(0, totnode, &data, calc_area_normal_and_center_task_cb, &settings); + AreaNormalCenterTLSData anctd = {{{0}}}; - BLI_mutex_end(&data.mutex); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + settings.func_reduce = calc_area_normal_and_center_reduce; + settings.userdata_chunk = &anctd; + settings.userdata_chunk_size = sizeof(AreaNormalCenterTLSData); + BKE_pbvh_parallel_range(0, totnode, &data, calc_area_normal_and_center_task_cb, &settings); /* for flatten center */ - for (n = 0; n < ARRAY_SIZE(area_cos); n++) { - if (count[n] != 0) { - mul_v3_v3fl(r_area_co, area_cos[n], 1.0f / count[n]); + for (n = 0; n < ARRAY_SIZE(anctd.area_cos); n++) { + if (anctd.area_count[n] != 0) { + mul_v3_v3fl(r_area_co, anctd.area_cos[n], 1.0f / anctd.area_count[n]); break; } } @@ -1620,8 +1698,8 @@ static void calc_area_normal_and_center( } /* for area normal */ - for (n = 0; n < ARRAY_SIZE(area_nos); n++) { - if (normalize_v3_v3(r_area_no, area_nos[n]) != 0.0f) { + for (n = 0; n < ARRAY_SIZE(anctd.area_nos); n++) { + if (normalize_v3_v3(r_area_no, anctd.area_nos[n]) != 0.0f) { break; } } @@ -1655,11 +1733,13 @@ static float brush_strength(const Sculpt *sd, switch (brush->sculpt_tool) { case SCULPT_TOOL_CLAY: - case SCULPT_TOOL_CLAY_STRIPS: case SCULPT_TOOL_DRAW: case SCULPT_TOOL_DRAW_SHARP: case SCULPT_TOOL_LAYER: return alpha * flip * pressure * overlap * feather; + case SCULPT_TOOL_CLAY_STRIPS: + /* Clay Strips needs extra strength to compensate for its default normal radius */ + return alpha * flip * pressure * overlap * feather * 1.3f; case SCULPT_TOOL_MASK: overlap = (1 + overlap) / 2; @@ -1831,6 +1911,12 @@ bool sculpt_search_sphere_cb(PBVHNode *node, void *data_v) float t[3], bb_min[3], bb_max[3]; int i; + if (data->ignore_fully_masked) { + if (BKE_pbvh_node_fully_masked_get(node)) { + return false; + } + } + if (data->original) { BKE_pbvh_node_get_original_BB(node, bb_min, bb_max); } @@ -1861,6 +1947,12 @@ bool sculpt_search_circle_cb(PBVHNode *node, void *data_v) SculptSearchCircleData *data = data_v; float bb_min[3], bb_max[3]; + if (data->ignore_fully_masked) { + if (BKE_pbvh_node_fully_masked_get(node)) { + return false; + } + } + if (data->original) { BKE_pbvh_node_get_original_BB(node, bb_min, bb_max); } @@ -1894,6 +1986,25 @@ static void sculpt_clip(Sculpt *sd, SculptSession *ss, float co[3], const float } } +static PBVHNode **sculpt_pbvh_gather_cursor_update(Object *ob, + Sculpt *sd, + bool use_original, + int *r_totnode) +{ + SculptSession *ss = ob->sculpt; + PBVHNode **nodes = NULL; + SculptSearchSphereData data = { + .ss = ss, + .sd = sd, + .radius_squared = ss->cursor_radius, + .original = use_original, + .ignore_fully_masked = false, + .center = NULL, + }; + BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, r_totnode); + return nodes; +} + static PBVHNode **sculpt_pbvh_gather_generic(Object *ob, Sculpt *sd, const Brush *brush, @@ -1910,8 +2021,9 @@ static PBVHNode **sculpt_pbvh_gather_generic(Object *ob, SculptSearchSphereData data = { .ss = ss, .sd = sd, - .radius_squared = ss->cache ? SQUARE(ss->cache->radius * radius_scale) : ss->cursor_radius, + .radius_squared = SQUARE(ss->cache->radius * radius_scale), .original = use_original, + .ignore_fully_masked = brush->sculpt_tool != SCULPT_TOOL_MASK, .center = NULL, }; BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, r_totnode); @@ -1926,6 +2038,7 @@ static PBVHNode **sculpt_pbvh_gather_generic(Object *ob, .radius_squared = ss->cache ? SQUARE(ss->cache->radius * radius_scale) : ss->cursor_radius, .original = use_original, .dist_ray_to_aabb_precalc = &dist_ray_to_aabb_precalc, + .ignore_fully_masked = brush->sculpt_tool != SCULPT_TOOL_MASK, }; BKE_pbvh_search_gather(ss->pbvh, sculpt_search_circle_cb, &data, &nodes, r_totnode); } @@ -1969,9 +2082,15 @@ static void update_sculpt_normal(Sculpt *sd, Object *ob, PBVHNode **nodes, int t { const Brush *brush = BKE_paint_brush(&sd->paint); StrokeCache *cache = ob->sculpt->cache; + /* Grab brush does not update the sculpt normal during a stroke */ + const bool update_normal = !(brush->flag & BRUSH_ORIGINAL_NORMAL) && + !(brush->sculpt_tool == SCULPT_TOOL_GRAB) && + !(brush->sculpt_tool == SCULPT_TOOL_ELASTIC_DEFORM) && + !(brush->sculpt_tool == SCULPT_TOOL_SNAKE_HOOK && + cache->normal_weight > 0.0f); if (cache->mirror_symmetry_pass == 0 && cache->radial_symmetry_pass == 0 && - (cache->first_time || !(brush->flag & BRUSH_ORIGINAL_NORMAL))) { + (cache->first_time || update_normal)) { calc_sculpt_normal(sd, ob, nodes, totnode, cache->sculpt_normal); if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { project_plane_v3_v3v3(cache->sculpt_normal, cache->sculpt_normal, cache->view_normal); @@ -2160,92 +2279,51 @@ static void bmesh_neighbor_average(float avg[3], BMVert *v) copy_v3_v3(avg, v->co); } -/* For bmesh: average only the four most aligned (parallel and perpendicular) edges - * relative to a direction. Naturally converges to a quad-like tessellation. */ +/* For bmesh: Average surrounding verts based on an orthogonality measure. + * Naturally converges to a quad-like structure. */ static void bmesh_four_neighbor_average(float avg[3], float direction[3], BMVert *v) { - /* Logic for 3 or more is identical. */ - const int vfcount = BM_vert_face_count_at_most(v, 3); - - /* Don't modify corner vertices. */ - if (vfcount < 2) { - copy_v3_v3(avg, v->co); - return; - } - - /* Project the direction to the vertex normal and create an additional - * parallel vector. */ - float dir_a[3], dir_b[3]; - cross_v3_v3v3(dir_a, direction, v->no); - cross_v3_v3v3(dir_b, dir_a, v->no); - - /* The four vectors which will be used for smoothing. - * Occasionally less than 4 verts match the requirements in that case - * use 'v' as fallback. */ - BMVert *pos_a = v; - BMVert *neg_a = v; - BMVert *pos_b = v; - BMVert *neg_b = v; - - float pos_score_a = 0.0f; - float neg_score_a = 0.0f; - float pos_score_b = 0.0f; - float neg_score_b = 0.0f; - - BMIter liter; - BMLoop *l; - BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) { - BMVert *adj_v[2] = {l->prev->v, l->next->v}; - - for (int i = 0; i < ARRAY_SIZE(adj_v); i++) { - BMVert *v_other = adj_v[i]; - - if (vfcount != 2 || BM_vert_face_count_at_most(v_other, 2) <= 2) { - float vec[3]; - sub_v3_v3v3(vec, v_other->co, v->co); - normalize_v3(vec); + float avg_co[3] = {0, 0, 0}; + float tot_co = 0; - /* The score is a measure of how orthogonal the edge is. */ - float score = dot_v3v3(vec, dir_a); + BMIter eiter; + BMEdge *e; - if (score >= pos_score_a) { - pos_a = v_other; - pos_score_a = score; - } - else if (score < neg_score_a) { - neg_a = v_other; - neg_score_a = score; - } - /* The same scoring but for the perpendicular direction. */ - score = dot_v3v3(vec, dir_b); - - if (score >= pos_score_b) { - pos_b = v_other; - pos_score_b = score; - } - else if (score < neg_score_b) { - neg_b = v_other; - neg_score_b = score; - } - } + BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { + if (BM_edge_is_boundary(e)) { + copy_v3_v3(avg, v->co); + return; } + BMVert *v_other = (e->v1 == v) ? e->v2 : e->v1; + float vec[3]; + sub_v3_v3v3(vec, v_other->co, v->co); + madd_v3_v3fl(vec, v->no, -dot_v3v3(vec, v->no)); + normalize_v3(vec); + + /* fac is a measure of how orthogonal or parallel the edge is + * relative to the direction */ + float fac = dot_v3v3(vec, direction); + fac = fac * fac - 0.5f; + fac *= fac; + madd_v3_v3fl(avg_co, v_other->co, fac); + tot_co += fac; } - /* Average everything together. */ - zero_v3(avg); - add_v3_v3(avg, pos_a->co); - add_v3_v3(avg, neg_a->co); - add_v3_v3(avg, pos_b->co); - add_v3_v3(avg, neg_b->co); - mul_v3_fl(avg, 0.25f); + /* In case vert has no Edge s */ + if (tot_co > 0) { + mul_v3_v3fl(avg, avg_co, 1.0f / tot_co); - /* Preserve volume. */ - float vec[3]; - sub_v3_v3(avg, v->co); - mul_v3_v3fl(vec, v->no, dot_v3v3(avg, v->no)); - sub_v3_v3(avg, vec); - add_v3_v3(avg, v->co); + /* Preserve volume. */ + float vec[3]; + sub_v3_v3(avg, v->co); + mul_v3_v3fl(vec, v->no, dot_v3v3(avg, v->no)); + sub_v3_v3(avg, vec); + add_v3_v3(avg, v->co); + } + else { + zero_v3(avg); + } } /* Same logic as neighbor_average_mask(), but for bmesh rather than mesh */ @@ -2277,6 +2355,48 @@ static float bmesh_neighbor_average_mask(BMVert *v, const int cd_vert_mask_offse } } +static void grids_neighbor_average(SculptSession *ss, float result[3], int index) +{ + float avg[3] = {0.0f, 0.0f, 0.0f}; + int total = 0; + + SculptVertexNeighborIter ni; + sculpt_vertex_neighbors_iter_begin(ss, index, ni) + { + add_v3_v3(avg, sculpt_vertex_co_get(ss, ni.index)); + total++; + } + sculpt_vertex_neighbors_iter_end(ni); + + if (total > 0) { + mul_v3_v3fl(result, avg, 1.0f / (float)total); + } + else { + copy_v3_v3(result, sculpt_vertex_co_get(ss, index)); + } +} + +static float grids_neighbor_average_mask(SculptSession *ss, int index) +{ + float avg = 0.0f; + int total = 0; + + SculptVertexNeighborIter ni; + sculpt_vertex_neighbors_iter_begin(ss, index, ni) + { + avg += sculpt_vertex_mask_get(ss, ni.index); + total++; + } + sculpt_vertex_neighbors_iter_end(ni); + + if (total > 0) { + return avg / (float)total; + } + else { + return sculpt_vertex_mask_get(ss, index); + } +} + /* Note: uses after-struct allocated mem to store actual cache... */ typedef struct SculptDoBrushSmoothGridDataChunk { size_t tmpgrid_size; @@ -2442,6 +2562,7 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, mul_v3_v3fl( tmp, ss->cache->sculpt_normal_symm, dot_v3v3(ss->cache->sculpt_normal_symm, direction)); sub_v3_v3(direction, tmp); + normalize_v3(direction); /* Cancel if there's no grab data. */ if (is_zero_v3(direction)) { @@ -2502,7 +2623,6 @@ static void do_smooth_brush_multires_task_cb_ex(void *__restrict userdata, float bstrength = data->strength; CCGElem **griddata, *gddata; - CCGKey key; float(*tmpgrid_co)[3] = NULL; float tmprow_co[2][3]; @@ -2521,7 +2641,7 @@ static void do_smooth_brush_multires_task_cb_ex(void *__restrict userdata, BKE_pbvh_node_get_grids( ss->pbvh, data->nodes[n], &grid_indices, &totgrid, NULL, &gridsize, &griddata); - BKE_pbvh_get_grid_key(ss->pbvh, &key); + CCGKey key = *BKE_pbvh_get_grid_key(ss->pbvh); grid_hidden = BKE_pbvh_grid_hidden(ss->pbvh); @@ -2681,9 +2801,8 @@ static void smooth(Sculpt *sd, .strength = strength, }; - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); switch (type) { case PBVH_GRIDS: { @@ -2700,16 +2819,16 @@ static void smooth(Sculpt *sd, settings.userdata_chunk = data_chunk; settings.userdata_chunk_size = size; - BLI_task_parallel_range(0, totnode, &data, do_smooth_brush_multires_task_cb_ex, &settings); + BKE_pbvh_parallel_range(0, totnode, &data, do_smooth_brush_multires_task_cb_ex, &settings); MEM_freeN(data_chunk); break; } case PBVH_FACES: - BLI_task_parallel_range(0, totnode, &data, do_smooth_brush_mesh_task_cb_ex, &settings); + BKE_pbvh_parallel_range(0, totnode, &data, do_smooth_brush_mesh_task_cb_ex, &settings); break; case PBVH_BMESH: - BLI_task_parallel_range(0, totnode, &data, do_smooth_brush_bmesh_task_cb_ex, &settings); + BKE_pbvh_parallel_range(0, totnode, &data, do_smooth_brush_bmesh_task_cb_ex, &settings); break; } @@ -2741,11 +2860,10 @@ static void bmesh_topology_rake( .nodes = nodes, .strength = factor, }; - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BLI_task_parallel_range(0, totnode, &data, do_topology_rake_bmesh_task_cb_ex, &settings); + BKE_pbvh_parallel_range(0, totnode, &data, do_topology_rake_bmesh_task_cb_ex, &settings); } } @@ -2799,10 +2917,9 @@ static void do_mask_brush_draw(Sculpt *sd, Object *ob, PBVHNode **nodes, int tot .nodes = nodes, }; - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range(0, totnode, &data, do_mask_brush_draw_task_cb_ex, &settings); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BKE_pbvh_parallel_range(0, totnode, &data, do_mask_brush_draw_task_cb_ex, &settings); } static void do_mask_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) @@ -2887,10 +3004,9 @@ static void do_draw_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) .offset = offset, }; - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range(0, totnode, &data, do_draw_brush_task_cb_ex, &settings); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BKE_pbvh_parallel_range(0, totnode, &data, do_draw_brush_task_cb_ex, &settings); } static void do_draw_sharp_brush_task_cb_ex(void *__restrict userdata, @@ -2964,10 +3080,9 @@ static void do_draw_sharp_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int to .offset = offset, }; - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range(0, totnode, &data, do_draw_sharp_brush_task_cb_ex, &settings); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BKE_pbvh_parallel_range(0, totnode, &data, do_draw_sharp_brush_task_cb_ex, &settings); } /** @@ -3081,10 +3196,9 @@ static void do_crease_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod .flippedbstrength = flippedbstrength, }; - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range(0, totnode, &data, do_crease_brush_task_cb_ex, &settings); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BKE_pbvh_parallel_range(0, totnode, &data, do_crease_brush_task_cb_ex, &settings); } static void do_pinch_brush_task_cb_ex(void *__restrict userdata, @@ -3144,10 +3258,9 @@ static void do_pinch_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode .nodes = nodes, }; - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range(0, totnode, &data, do_pinch_brush_task_cb_ex, &settings); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BKE_pbvh_parallel_range(0, totnode, &data, do_pinch_brush_task_cb_ex, &settings); } static void do_grab_brush_task_cb_ex(void *__restrict userdata, @@ -3217,10 +3330,9 @@ static void do_grab_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) .grab_delta = grab_delta, }; - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range(0, totnode, &data, do_grab_brush_task_cb_ex, &settings); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BKE_pbvh_parallel_range(0, totnode, &data, do_grab_brush_task_cb_ex, &settings); } /* Regularized Kelvinlets: Sculpting Brushes based on Fundamental Solutions of Elasticity @@ -3459,6 +3571,10 @@ static void do_elastic_deform_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); + if (ss->cache->normal_weight > 0.0f) { + sculpt_project_v3_normal_align(ss, ss->cache->normal_weight, grab_delta); + } + SculptThreadedTaskData data = { .sd = sd, .ob = ob, @@ -3467,10 +3583,9 @@ static void do_elastic_deform_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in .grab_delta = grab_delta, }; - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range(0, totnode, &data, do_elastic_deform_brush_task_cb_ex, &settings); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BKE_pbvh_parallel_range(0, totnode, &data, do_elastic_deform_brush_task_cb_ex, &settings); } static void do_pose_brush_task_cb_ex(void *__restrict userdata, @@ -3522,10 +3637,6 @@ static void do_pose_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) float pose_initial_co[3]; float transform_rot[4][4], transform_trans[4][4], transform_trans_inv[4][4]; - if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { - return; - } - copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); copy_v3_v3(pose_origin, ss->cache->pose_origin); @@ -3561,39 +3672,115 @@ static void do_pose_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) .transform_trans_inv = transform_trans_inv, }; - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range(0, totnode, &data, do_pose_brush_task_cb_ex, &settings); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BKE_pbvh_parallel_range(0, totnode, &data, do_pose_brush_task_cb_ex, &settings); } -static void pose_brush_init_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict UNUSED(tls)) +typedef struct PoseGrowFactorTLSData { + float pos_avg[3]; + int pos_count; +} PoseGrowFactorTLSData; + +static void pose_brush_grow_factor_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) { SculptThreadedTaskData *data = userdata; + PoseGrowFactorTLSData *gftd = tls->userdata_chunk; SculptSession *ss = data->ob->sculpt; + const char symm = data->sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; + const float *active_co = sculpt_active_vertex_co_get(ss); PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SculptVertexNeighborIter ni; - float avg = 0; - int total = 0; + float max = 0.0f; sculpt_vertex_neighbors_iter_begin(ss, vd.index, ni) { - avg += ss->cache->pose_factor[ni.index]; - total++; + float vmask_f = data->prev_mask[ni.index]; + if (vmask_f > max) { + max = vmask_f; + } } sculpt_vertex_neighbors_iter_end(ni); - - if (total > 0) { - ss->cache->pose_factor[vd.index] = avg / (float)total; + if (max != data->prev_mask[vd.index]) { + data->pose_factor[vd.index] = max; + if (check_vertex_pivot_symmetry(vd.co, active_co, symm)) { + add_v3_v3(gftd->pos_avg, vd.co); + gftd->pos_count++; + } } } + BKE_pbvh_vertex_iter_end; } -static bool sculpt_pose_brush_is_vertex_inside_brush_radius(float vertex[3], +static void pose_brush_grow_factor_reduce(const void *__restrict UNUSED(userdata), + void *__restrict chunk_join, + void *__restrict chunk) +{ + PoseGrowFactorTLSData *join = chunk_join; + PoseGrowFactorTLSData *gftd = chunk; + add_v3_v3(join->pos_avg, gftd->pos_avg); + join->pos_count += gftd->pos_count; +} + +/* Grow the factor until its boundary is near to the offset pose origin */ +static void sculpt_pose_grow_pose_factor( + Sculpt *sd, Object *ob, SculptSession *ss, float pose_origin[3], float *pose_factor) +{ + PBVHNode **nodes; + PBVH *pbvh = ob->sculpt->pbvh; + int totnode; + + BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .nodes = nodes, + .totnode = totnode, + .pose_factor = pose_factor, + }; + PBVHParallelSettings settings; + PoseGrowFactorTLSData gftd; + gftd.pos_count = 0; + zero_v3(gftd.pos_avg); + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + settings.func_reduce = pose_brush_grow_factor_reduce; + settings.userdata_chunk = &gftd; + settings.userdata_chunk_size = sizeof(PoseGrowFactorTLSData); + + bool grow_next_iteration = true; + float prev_len = FLT_MAX; + data.prev_mask = MEM_mallocN(sculpt_vertex_count_get(ss) * sizeof(float), "prev mask"); + while (grow_next_iteration) { + zero_v3(gftd.pos_avg); + gftd.pos_count = 0; + memcpy(data.prev_mask, pose_factor, sculpt_vertex_count_get(ss) * sizeof(float)); + BKE_pbvh_parallel_range(0, totnode, &data, pose_brush_grow_factor_task_cb_ex, &settings); + if (gftd.pos_count != 0) { + mul_v3_fl(gftd.pos_avg, 1.0f / (float)gftd.pos_count); + float len = len_v3v3(gftd.pos_avg, pose_origin); + if (len < prev_len) { + prev_len = len; + grow_next_iteration = true; + } + else { + grow_next_iteration = false; + memcpy(pose_factor, data.prev_mask, sculpt_vertex_count_get(ss) * sizeof(float)); + } + } + else { + grow_next_iteration = false; + } + } + MEM_freeN(data.prev_mask); + + MEM_SAFE_FREE(nodes); +} + +static bool sculpt_pose_brush_is_vertex_inside_brush_radius(const float vertex[3], const float br_co[3], float radius, char symm) @@ -3610,85 +3797,123 @@ static bool sculpt_pose_brush_is_vertex_inside_brush_radius(float vertex[3], return false; } -static void sculpt_pose_brush_init(Sculpt *sd, Object *ob, SculptSession *ss, Brush *br) +/* Calculate the pose origin and (Optionaly the pose factor) that is used when using the pose brush + * + * r_pose_origin must be a valid pointer. the r_pose_factor is optional. When set to NULL it won't + * be calculated. */ +typedef struct PoseFloodFillData { + float pose_initial_co[3]; + float radius; + int symm; + + float *pose_factor; + float pose_origin[3]; + int tot_co; +} PoseFloodFillData; + +static bool pose_floodfill_cb( + SculptSession *ss, int UNUSED(from_v), int to_v, bool is_duplicate, void *userdata) { - sculpt_vertex_random_access_init(ss); + PoseFloodFillData *data = userdata; - ss->cache->pose_factor = MEM_callocN(sculpt_vertex_count_get(ss) * sizeof(float), "Pose factor"); + if (data->pose_factor) { + data->pose_factor[to_v] = 1.0f; + } - copy_v3_v3(ss->cache->pose_initial_co, ss->cache->location); + const float *co = sculpt_vertex_co_get(ss, to_v); + if (sculpt_pose_brush_is_vertex_inside_brush_radius( + co, data->pose_initial_co, data->radius, data->symm)) { + return true; + } + else if (check_vertex_pivot_symmetry(co, data->pose_initial_co, data->symm)) { + if (!is_duplicate) { + add_v3_v3(data->pose_origin, co); + data->tot_co++; + } + } - char *visited_vertices = MEM_callocN(sculpt_vertex_count_get(ss) * sizeof(char), - "Visited vertices"); - BLI_Stack *not_visited_vertices = BLI_stack_new(sizeof(VertexTopologyIterator), - "not visited vertices stack"); + return false; +} - float tot_co = 0; - zero_v3(ss->cache->pose_origin); +void sculpt_pose_calc_pose_data(Sculpt *sd, + Object *ob, + SculptSession *ss, + float initial_location[3], + float radius, + float pose_offset, + float *r_pose_origin, + float *r_pose_factor) +{ + sculpt_vertex_random_access_init(ss); - VertexTopologyIterator mevit; + /* Calculate the pose rotation point based on the boundaries of the brush factor. */ + SculptFloodFill flood; + sculpt_floodfill_init(ss, &flood); + sculpt_floodfill_add_active(sd, ob, ss, &flood, (r_pose_factor) ? radius : 0.0f); - /* Add active vertex and symmetric vertices to the stack. */ - const char symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; - for (char i = 0; i <= symm; ++i) { - if (is_symmetry_iteration_valid(i, symm)) { - float location[3]; - flip_v3_v3(location, sculpt_vertex_co_get(ss, sculpt_active_vertex_get(ss)), (char)i); - if (i == 0) { - mevit.v = sculpt_active_vertex_get(ss); - } - else { - mevit.v = sculpt_nearest_vertex_get( - sd, ob, location, ss->cache->radius * ss->cache->radius, false); - } - if (mevit.v != -1) { - mevit.it = 1; - BLI_stack_push(not_visited_vertices, &mevit); - } - } + PoseFloodFillData fdata = { + .radius = radius, + .symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL, + .pose_factor = r_pose_factor, + .tot_co = 0, + }; + zero_v3(fdata.pose_origin); + copy_v3_v3(fdata.pose_initial_co, initial_location); + sculpt_floodfill_execute(ss, &flood, pose_floodfill_cb, &fdata); + sculpt_floodfill_free(&flood); + + if (fdata.tot_co > 0) { + mul_v3_fl(fdata.pose_origin, 1.0f / (float)fdata.tot_co); } - /* Flood fill the internal pose brush factor. Calculate the pose rotation point based on the - * boundaries of the brush factor*/ - while (!BLI_stack_is_empty(not_visited_vertices)) { - VertexTopologyIterator c_mevit; - BLI_stack_pop(not_visited_vertices, &c_mevit); + /* Offset the pose origin */ + float pose_d[3]; + sub_v3_v3v3(pose_d, fdata.pose_origin, fdata.pose_initial_co); + normalize_v3(pose_d); + madd_v3_v3fl(fdata.pose_origin, pose_d, radius * pose_offset); + copy_v3_v3(r_pose_origin, fdata.pose_origin); + + if (pose_offset != 0.0f && r_pose_factor) { + sculpt_pose_grow_pose_factor(sd, ob, ss, fdata.pose_origin, r_pose_factor); + } +} + +static void pose_brush_init_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { SculptVertexNeighborIter ni; - sculpt_vertex_neighbors_iter_begin(ss, c_mevit.v, ni) + float avg = 0; + int total = 0; + sculpt_vertex_neighbors_iter_begin(ss, vd.index, ni) { - if (visited_vertices[(int)ni.index] == 0) { - VertexTopologyIterator new_entry; - new_entry.v = ni.index; - new_entry.it = c_mevit.it + 1; - ss->cache->pose_factor[new_entry.v] = 1.0f; - visited_vertices[(int)ni.index] = 1; - if (sculpt_pose_brush_is_vertex_inside_brush_radius(sculpt_vertex_co_get(ss, new_entry.v), - ss->cache->pose_initial_co, - ss->cache->radius, - symm)) { - BLI_stack_push(not_visited_vertices, &new_entry); - } - else { - if (check_vertex_pivot_symmetry( - sculpt_vertex_co_get(ss, new_entry.v), ss->cache->pose_initial_co, symm)) { - tot_co++; - add_v3_v3(ss->cache->pose_origin, sculpt_vertex_co_get(ss, new_entry.v)); - } - } - } + avg += ss->cache->pose_factor[ni.index]; + total++; } sculpt_vertex_neighbors_iter_end(ni); - } - BLI_stack_free(not_visited_vertices); + if (total > 0) { + ss->cache->pose_factor[vd.index] = avg / (float)total; + } + } + BKE_pbvh_vertex_iter_end; +} - MEM_freeN(visited_vertices); +static void sculpt_pose_brush_init( + Sculpt *sd, Object *ob, SculptSession *ss, Brush *br, float initial_location[3], float radius) +{ + float *pose_factor = MEM_callocN(sculpt_vertex_count_get(ss) * sizeof(float), "Pose factor"); - if (tot_co > 0) { - mul_v3_fl(ss->cache->pose_origin, 1.0f / (float)tot_co); - } + sculpt_pose_calc_pose_data( + sd, ob, ss, initial_location, radius, br->pose_offset, ss->cache->pose_origin, pose_factor); - /* Smooth the pose brush factor for cleaner deformation */ + copy_v3_v3(ss->cache->pose_initial_co, initial_location); + ss->cache->pose_factor = pose_factor; PBVHNode **nodes; PBVH *pbvh = ob->sculpt->pbvh; @@ -3703,12 +3928,14 @@ static void sculpt_pose_brush_init(Sculpt *sd, Object *ob, SculptSession *ss, Br .nodes = nodes, }; + /* Smooth the pose brush factor for cleaner deformation */ for (int i = 0; i < 4; i++) { - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range(0, totnode, &data, pose_brush_init_task_cb_ex, &settings); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BKE_pbvh_parallel_range(0, totnode, &data, pose_brush_init_task_cb_ex, &settings); } + + MEM_SAFE_FREE(nodes); } static void do_nudge_brush_task_cb_ex(void *__restrict userdata, @@ -3773,10 +4000,9 @@ static void do_nudge_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode .cono = cono, }; - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range(0, totnode, &data, do_nudge_brush_task_cb_ex, &settings); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BKE_pbvh_parallel_range(0, totnode, &data, do_nudge_brush_task_cb_ex, &settings); } static void do_snake_hook_brush_task_cb_ex(void *__restrict userdata, @@ -3895,10 +4121,9 @@ static void do_snake_hook_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int to .grab_delta = grab_delta, }; - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range(0, totnode, &data, do_snake_hook_brush_task_cb_ex, &settings); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BKE_pbvh_parallel_range(0, totnode, &data, do_snake_hook_brush_task_cb_ex, &settings); } static void do_thumb_brush_task_cb_ex(void *__restrict userdata, @@ -3968,10 +4193,9 @@ static void do_thumb_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode .cono = cono, }; - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range(0, totnode, &data, do_thumb_brush_task_cb_ex, &settings); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BKE_pbvh_parallel_range(0, totnode, &data, do_thumb_brush_task_cb_ex, &settings); } static void do_rotate_brush_task_cb_ex(void *__restrict userdata, @@ -4042,10 +4266,9 @@ static void do_rotate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod .angle = angle, }; - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range(0, totnode, &data, do_rotate_brush_task_cb_ex, &settings); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BKE_pbvh_parallel_range(0, totnode, &data, do_rotate_brush_task_cb_ex, &settings); } static void do_layer_brush_task_cb_ex(void *__restrict userdata, @@ -4140,10 +4363,9 @@ static void do_layer_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode }; BLI_mutex_init(&data.mutex); - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range(0, totnode, &data, do_layer_brush_task_cb_ex, &settings); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BKE_pbvh_parallel_range(0, totnode, &data, do_layer_brush_task_cb_ex, &settings); BLI_mutex_end(&data.mutex); } @@ -4209,10 +4431,9 @@ static void do_inflate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totno .nodes = nodes, }; - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range(0, totnode, &data, do_inflate_brush_task_cb_ex, &settings); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BKE_pbvh_parallel_range(0, totnode, &data, do_inflate_brush_task_cb_ex, &settings); } static void calc_sculpt_plane( @@ -4223,7 +4444,8 @@ static void calc_sculpt_plane( if (ss->cache->mirror_symmetry_pass == 0 && ss->cache->radial_symmetry_pass == 0 && ss->cache->tile_pass == 0 && - (ss->cache->first_time || !(brush->flag & BRUSH_ORIGINAL_NORMAL))) { + (ss->cache->first_time || !(brush->flag & BRUSH_ORIGINAL_PLANE) || + !(brush->flag & BRUSH_ORIGINAL_NORMAL))) { switch (brush->sculpt_plane) { case SCULPT_DISP_DIR_VIEW: copy_v3_v3(r_area_no, ss->cache->true_view_normal); @@ -4260,10 +4482,20 @@ static void calc_sculpt_plane( } /* for area normal */ - copy_v3_v3(ss->cache->sculpt_normal, r_area_no); + if ((!ss->cache->first_time) && (brush->flag & BRUSH_ORIGINAL_NORMAL)) { + copy_v3_v3(r_area_no, ss->cache->sculpt_normal); + } + else { + copy_v3_v3(ss->cache->sculpt_normal, r_area_no); + } /* for flatten center */ - copy_v3_v3(ss->cache->last_center, r_area_co); + if ((!ss->cache->first_time) && (brush->flag & BRUSH_ORIGINAL_PLANE)) { + copy_v3_v3(r_area_co, ss->cache->last_center); + } + else { + copy_v3_v3(ss->cache->last_center, r_area_co); + } } else { /* for area normal */ @@ -4408,10 +4640,9 @@ static void do_flatten_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totno .area_co = area_co, }; - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range(0, totnode, &data, do_flatten_brush_task_cb_ex, &settings); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BKE_pbvh_parallel_range(0, totnode, &data, do_flatten_brush_task_cb_ex, &settings); } static void do_clay_brush_task_cb_ex(void *__restrict userdata, @@ -4507,10 +4738,9 @@ static void do_clay_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) .area_co = area_co, }; - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range(0, totnode, &data, do_clay_brush_task_cb_ex, &settings); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BKE_pbvh_parallel_range(0, totnode, &data, do_clay_brush_task_cb_ex, &settings); } static void do_clay_strips_brush_task_cb_ex(void *__restrict userdata, @@ -4638,10 +4868,9 @@ static void do_clay_strips_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int t .mat = mat, }; - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range(0, totnode, &data, do_clay_strips_brush_task_cb_ex, &settings); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BKE_pbvh_parallel_range(0, totnode, &data, do_clay_strips_brush_task_cb_ex, &settings); } static void do_fill_brush_task_cb_ex(void *__restrict userdata, @@ -4732,10 +4961,9 @@ static void do_fill_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) .area_co = area_co, }; - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range(0, totnode, &data, do_fill_brush_task_cb_ex, &settings); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BKE_pbvh_parallel_range(0, totnode, &data, do_fill_brush_task_cb_ex, &settings); } static void do_scrape_brush_task_cb_ex(void *__restrict userdata, @@ -4825,10 +5053,9 @@ static void do_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod .area_co = area_co, }; - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range(0, totnode, &data, do_scrape_brush_task_cb_ex, &settings); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BKE_pbvh_parallel_range(0, totnode, &data, do_scrape_brush_task_cb_ex, &settings); } static void do_gravity_task_cb_ex(void *__restrict userdata, @@ -4895,10 +5122,9 @@ static void do_gravity(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, fl .offset = offset, }; - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range(0, totnode, &data, do_gravity_task_cb_ex, &settings); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BKE_pbvh_parallel_range(0, totnode, &data, do_gravity_task_cb_ex, &settings); } void sculpt_vertcos_to_key(Object *ob, KeyBlock *kb, const float (*vertCos)[3]) @@ -4999,7 +5225,7 @@ static void sculpt_topology_update(Sculpt *sd, (brush->falloff_shape != PAINT_FALLOFF_SHAPE_SPHERE)); } - MEM_freeN(nodes); + MEM_SAFE_FREE(nodes); /* update average stroke position */ copy_v3_v3(location, ss->cache->true_location); @@ -5017,7 +5243,12 @@ static void do_brush_action_task_cb(void *__restrict userdata, data->nodes[n], data->brush->sculpt_tool == SCULPT_TOOL_MASK ? SCULPT_UNDO_MASK : SCULPT_UNDO_COORDS); - BKE_pbvh_node_mark_update(data->nodes[n]); + if (data->brush->sculpt_tool == SCULPT_TOOL_MASK) { + BKE_pbvh_node_mark_update_mask(data->nodes[n]); + } + else { + BKE_pbvh_node_mark_update(data->nodes[n]); + } } static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSettings *ups) @@ -5030,18 +5261,27 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe /* These brushes need to update all nodes as they are not constrained by the brush radius */ if (brush->sculpt_tool == SCULPT_TOOL_ELASTIC_DEFORM) { + BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); + } + else if (brush->sculpt_tool == SCULPT_TOOL_POSE) { + float final_radius = ss->cache->radius * (1 + brush->pose_offset); SculptSearchSphereData data = { .ss = ss, .sd = sd, - .radius_squared = FLT_MAX, + .radius_squared = final_radius * final_radius, .original = true, }; - BKE_pbvh_search_gather(ss->pbvh, NULL, &data, &nodes, &totnode); + BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, &totnode); } else { const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true : ss->cache->original; - const float radius_scale = 1.0f; + float radius_scale = 1.0f; + /* With these options enabled not all required nodes are inside the original brush radius, so + * the brush can produce artifacts in some situations */ + if (brush->sculpt_tool == SCULPT_TOOL_DRAW && brush->flag & BRUSH_ORIGINAL_NORMAL) { + radius_scale = 2.0f; + } nodes = sculpt_pbvh_gather_generic(ob, sd, brush, use_original, radius_scale, &totnode); } @@ -5056,10 +5296,9 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe .nodes = nodes, }; - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range(0, totnode, &task_data, do_brush_action_task_cb, &settings); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BKE_pbvh_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); @@ -5077,9 +5316,7 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe if (brush->sculpt_tool == SCULPT_TOOL_POSE && ss->cache->first_time && ss->cache->mirror_symmetry_pass == 0) { - if (BKE_pbvh_type(ss->pbvh) != PBVH_GRIDS) { - sculpt_pose_brush_init(sd, ob, ss, brush); - } + sculpt_pose_brush_init(sd, ob, ss, brush, ss->cache->location, ss->cache->radius); } /* Apply one type of brush action */ @@ -5168,7 +5405,7 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe do_gravity(sd, ob, nodes, totnode, sd->gravity_factor); } - MEM_freeN(nodes); + MEM_SAFE_FREE(nodes); /* update average stroke position */ copy_v3_v3(location, ss->cache->true_location); @@ -5196,7 +5433,7 @@ static void sculpt_flush_pbvhvert_deform(Object *ob, PBVHVertexIter *vd) copy_v3_v3(ss->deform_cos[index], vd->co); copy_v3_v3(ss->orig_cos[index], newco); - if (!ss->kb) { + if (!ss->shapekey_active) { copy_v3_v3(me->mvert[index].co, newco); } } @@ -5252,7 +5489,7 @@ static void sculpt_combine_proxies_task_cb(void *__restrict userdata, sculpt_clip(sd, ss, vd.co, val); - if (ss->modifiers_active) { + if (ss->deform_modifiers_active) { sculpt_flush_pbvhvert_deform(ob, &vd); } } @@ -5279,15 +5516,12 @@ static void sculpt_combine_proxies(Sculpt *sd, Object *ob) .nodes = nodes, }; - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range(0, totnode, &data, sculpt_combine_proxies_task_cb, &settings); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BKE_pbvh_parallel_range(0, totnode, &data, sculpt_combine_proxies_task_cb, &settings); } - if (nodes) { - MEM_freeN(nodes); - } + MEM_SAFE_FREE(nodes); } /* copy the modified vertices from bvh to the active key */ @@ -5306,7 +5540,7 @@ static void sculpt_update_keyblock(Object *ob) } if (vertCos) { - sculpt_vertcos_to_key(ob, ss->kb, vertCos); + sculpt_vertcos_to_key(ob, ss->shapekey_active, vertCos); if (vertCos != ss->orig_cos) { MEM_freeN(vertCos); @@ -5352,7 +5586,7 @@ static void sculpt_flush_stroke_deform(Sculpt *sd, Object *ob, bool is_proxy_use PBVHNode **nodes; float(*vertCos)[3] = NULL; - if (ss->kb) { + if (ss->shapekey_active) { vertCos = MEM_mallocN(sizeof(*vertCos) * me->totvert, "flushStrokeDeofrm keyVerts"); /* mesh could have isolated verts which wouldn't be in BVH, @@ -5372,24 +5606,23 @@ static void sculpt_flush_stroke_deform(Sculpt *sd, Object *ob, bool is_proxy_use .vertCos = vertCos, }; - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range(0, totnode, &data, sculpt_flush_stroke_deform_task_cb, &settings); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BKE_pbvh_parallel_range(0, totnode, &data, sculpt_flush_stroke_deform_task_cb, &settings); if (vertCos) { - sculpt_vertcos_to_key(ob, ss->kb, vertCos); + sculpt_vertcos_to_key(ob, ss->shapekey_active, vertCos); MEM_freeN(vertCos); } - MEM_freeN(nodes); + MEM_SAFE_FREE(nodes); /* Modifiers could depend on mesh normals, so we should update them/ * Note, then if sculpting happens on locked key, normals should be re-calculated * after applying coords from keyblock on base mesh */ BKE_mesh_calc_normals(me); } - else if (ss->kb) { + else if (ss->shapekey_active) { sculpt_update_keyblock(ob); } } @@ -5676,6 +5909,9 @@ void sculpt_cache_free(StrokeCache *cache) if (cache->dial) { MEM_freeN(cache->dial); } + if (cache->pose_factor) { + MEM_freeN(cache->pose_factor); + } MEM_freeN(cache); } @@ -5869,12 +6105,21 @@ static void sculpt_update_cache_invariants( /* Make copies of the mesh vertex locations and normals for some tools */ if (brush->flag & BRUSH_ANCHORED) { - cache->original = 1; + cache->original = true; + } + + /* Draw sharp does not need the original coordinates to produce the accumulate effect, so it + * should work the opposite way. */ + if (brush->sculpt_tool == SCULPT_TOOL_DRAW_SHARP) { + cache->original = true; } if (SCULPT_TOOL_HAS_ACCUMULATE(brush->sculpt_tool)) { if (!(brush->flag & BRUSH_ACCUMULATE)) { - cache->original = 1; + cache->original = true; + if (brush->sculpt_tool == SCULPT_TOOL_DRAW_SHARP) { + cache->original = false; + } } } @@ -5911,8 +6156,7 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru if (cache->first_time) { if (tool == SCULPT_TOOL_GRAB && brush->flag & BRUSH_GRAB_ACTIVE_VERTEX) { - copy_v3_v3(cache->orig_grab_location, - sculpt_vertex_co_get(ss, sculpt_active_vertex_get(ss))); + copy_v3_v3(cache->orig_grab_location, sculpt_active_vertex_co_get(ss)); } else { copy_v3_v3(cache->orig_grab_location, cache->true_location); @@ -6125,9 +6369,9 @@ static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob, Po /* Returns true if any of the smoothing modes are active (currently * one of smooth brush, autosmooth, mask smooth, or shift-key * smooth) */ -static bool sculpt_needs_conectivity_info(const Brush *brush, SculptSession *ss, int stroke_mode) +static bool sculpt_needs_connectivity_info(const Brush *brush, SculptSession *ss, int stroke_mode) { - if (ss && sculpt_automasking_enabled(ss, brush)) { + if (ss && ss->pbvh && sculpt_automasking_enabled(ss, brush)) { return true; } return ((stroke_mode == BRUSH_STROKE_SMOOTH) || (ss && ss->cache && ss->cache->alt_smooth) || @@ -6141,8 +6385,9 @@ static void sculpt_stroke_modifiers_check(const bContext *C, Object *ob, const B SculptSession *ss = ob->sculpt; View3D *v3d = CTX_wm_view3d(C); - bool need_pmap = sculpt_needs_conectivity_info(brush, ss, 0); - if (ss->kb || ss->modifiers_active || (!BKE_sculptsession_use_pbvh_draw(ob, v3d) && need_pmap)) { + bool need_pmap = sculpt_needs_connectivity_info(brush, ss, 0); + if (ss->shapekey_active || ss->deform_modifiers_active || + (!BKE_sculptsession_use_pbvh_draw(ob, v3d) && need_pmap)) { Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); BKE_sculpt_update_object_for_edit(depsgraph, ob, need_pmap, false); } @@ -6272,6 +6517,7 @@ bool sculpt_cursor_geometry_info_update(bContext *C, const float mouse[2], bool use_sampled_normal) { + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Scene *scene = CTX_data_scene(C); Sculpt *sd = scene->toolsettings->sculpt; Object *ob; @@ -6284,15 +6530,15 @@ bool sculpt_cursor_geometry_info_update(bContext *C, int totnode; bool original = false, hit = false; - ED_view3d_viewcontext_init(C, &vc); + ED_view3d_viewcontext_init(C, &vc, depsgraph); ob = vc.obact; ss = ob->sculpt; if (!ss->pbvh) { - copy_v3_fl(out->location, 0.0f); - copy_v3_fl(out->normal, 0.0f); - copy_v3_fl(out->active_vertex_co, 0.0f); + zero_v3(out->location); + zero_v3(out->normal); + zero_v3(out->active_vertex_co); return false; } @@ -6314,16 +6560,16 @@ bool sculpt_cursor_geometry_info_update(bContext *C, /* Cursor is not over the mesh, return default values */ if (!srd.hit) { - copy_v3_fl(out->location, 0.0f); - copy_v3_fl(out->normal, 0.0f); - copy_v3_fl(out->active_vertex_co, 0.0f); + zero_v3(out->location); + zero_v3(out->normal); + zero_v3(out->active_vertex_co); return false; } /* Update the active vertex of the SculptSession */ ss->active_vertex_index = srd.active_vertex_index; + copy_v3_v3(out->active_vertex_co, sculpt_active_vertex_co_get(ss)); - copy_v3_v3(out->active_vertex_co, sculpt_vertex_co_get(ss, srd.active_vertex_index)); copy_v3_v3(out->location, ray_normal); mul_v3_fl(out->location, srd.depth); add_v3_v3(out->location, ray_start); @@ -6335,7 +6581,6 @@ bool sculpt_cursor_geometry_info_update(bContext *C, } /* Sampled normal calculation */ - const float radius_scale = 1.0f; float radius; /* Update cursor data in SculptSession */ @@ -6357,11 +6602,11 @@ bool sculpt_cursor_geometry_info_update(bContext *C, } ss->cursor_radius = radius; - PBVHNode **nodes = sculpt_pbvh_gather_generic(ob, sd, brush, original, radius_scale, &totnode); + PBVHNode **nodes = sculpt_pbvh_gather_cursor_update(ob, sd, original, &totnode); /* In case there are no nodes under the cursor, return the face normal */ if (!totnode) { - MEM_freeN(nodes); + MEM_SAFE_FREE(nodes); copy_v3_v3(out->normal, srd.face_normal); return true; } @@ -6374,7 +6619,7 @@ bool sculpt_cursor_geometry_info_update(bContext *C, /* Use face normal when there are no vertices to sample inside the cursor radius */ copy_v3_v3(out->normal, srd.face_normal); } - MEM_freeN(nodes); + MEM_SAFE_FREE(nodes); return true; } @@ -6384,6 +6629,7 @@ bool sculpt_cursor_geometry_info_update(bContext *C, */ bool sculpt_stroke_get_location(bContext *C, float out[3], const float mouse[2]) { + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Object *ob; SculptSession *ss; StrokeCache *cache; @@ -6391,13 +6637,13 @@ bool sculpt_stroke_get_location(bContext *C, float out[3], const float mouse[2]) bool original; ViewContext vc; - ED_view3d_viewcontext_init(C, &vc); + ED_view3d_viewcontext_init(C, &vc, depsgraph); ob = vc.obact; ss = ob->sculpt; cache = ss->cache; - original = (cache) ? cache->original : 0; + original = (cache) ? cache->original : false; const Brush *brush = BKE_paint_brush(BKE_paint_get_active_from_context(C)); @@ -6453,10 +6699,6 @@ bool sculpt_stroke_get_location(bContext *C, float out[3], const float mouse[2]) } } - if (cache && hit) { - copy_v3_v3(cache->true_location, out); - } - return hit; } @@ -6496,7 +6738,7 @@ static void sculpt_brush_stroke_init(bContext *C, wmOperator *op) view3d_operator_needs_opengl(C); sculpt_brush_init_tex(scene, sd, ss); - is_smooth = sculpt_needs_conectivity_info(brush, ss, mode); + is_smooth = sculpt_needs_connectivity_info(brush, ss, mode); BKE_sculpt_update_object_for_edit(depsgraph, ob, is_smooth, need_mask); } @@ -6526,7 +6768,7 @@ void sculpt_update_object_bounding_box(Object *ob) } } -static void sculpt_flush_update_step(bContext *C) +static void sculpt_flush_update_step(bContext *C, SculptUpdateType update_flags) { Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Object *ob = CTX_data_active_object(C); @@ -6534,6 +6776,12 @@ static void sculpt_flush_update_step(bContext *C) ARegion *ar = CTX_wm_region(C); MultiresModifierData *mmd = ss->multires; View3D *v3d = CTX_wm_view3d(C); + RegionView3D *rv3d = CTX_wm_region_view3d(C); + + if (rv3d) { + /* Mark for faster 3D viewport redraws. */ + rv3d->rflag |= RV3D_PAINTING; + } if (mmd != NULL) { multires_mark_as_modified(depsgraph, ob, MULTIRES_COORDS_MODIFIED); @@ -6554,11 +6802,13 @@ static void sculpt_flush_update_step(bContext *C) * only the part of the 3D viewport where changes happened. */ rcti r; - BKE_pbvh_update_bounds(ss->pbvh, PBVH_UpdateBB); - /* Update the object's bounding box too so that the object - * doesn't get incorrectly clipped during drawing in - * draw_mesh_object(). [#33790] */ - sculpt_update_object_bounding_box(ob); + if (update_flags & SCULPT_UPDATE_COORDS) { + BKE_pbvh_update_bounds(ss->pbvh, PBVH_UpdateBB); + /* Update the object's bounding box too so that the object + * doesn't get incorrectly clipped during drawing in + * draw_mesh_object(). [#33790] */ + sculpt_update_object_bounding_box(ob); + } if (sculpt_get_redraw_rect(ar, CTX_wm_region_view3d(C), ob, &r)) { if (ss->cache) { @@ -6578,16 +6828,21 @@ static void sculpt_flush_update_step(bContext *C) } } -static void sculpt_flush_update_done(const bContext *C, Object *ob) +static void sculpt_flush_update_done(const bContext *C, Object *ob, SculptUpdateType update_flags) { /* After we are done drawing the stroke, check if we need to do a more * expensive depsgraph tag to update geometry. */ wmWindowManager *wm = CTX_wm_manager(C); View3D *current_v3d = CTX_wm_view3d(C); + RegionView3D *rv3d = CTX_wm_region_view3d(C); SculptSession *ss = ob->sculpt; Mesh *mesh = ob->data; bool need_tag = (mesh->id.us > 1); /* Always needed for linked duplicates. */ + if (rv3d) { + rv3d->rflag &= ~RV3D_PAINTING; + } + for (wmWindow *win = wm->windows.first; win; win = win->next) { bScreen *screen = WM_window_get_active_screen(win); for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { @@ -6597,11 +6852,26 @@ static void sculpt_flush_update_done(const bContext *C, Object *ob) if (v3d != current_v3d) { need_tag |= !BKE_sculptsession_use_pbvh_draw(ob, v3d); } + + /* Tag all 3D viewports for redraw now that we are done. Others + * viewports did not get a full redraw, and anti-aliasing for the + * current viewport was deactivated. */ + for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) { + if (ar->regiontype == RGN_TYPE_WINDOW) { + ED_region_tag_redraw(ar); + } + } } } } - BKE_pbvh_update_bounds(ss->pbvh, PBVH_UpdateOriginalBB); + if (update_flags & SCULPT_UPDATE_COORDS) { + BKE_pbvh_update_bounds(ss->pbvh, PBVH_UpdateOriginalBB); + } + + if (update_flags & SCULPT_UPDATE_MASK) { + BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateMask); + } if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { BKE_pbvh_bmesh_after_stroke(ss->pbvh); @@ -6610,7 +6880,7 @@ static void sculpt_flush_update_done(const bContext *C, Object *ob) /* optimization: if there is locked key and active modifiers present in */ /* the stack, keyblock is updating at each step. otherwise we could update */ /* keyblock only when stroke is finished */ - if (ss->kb && !ss->modifiers_active) { + if (ss->shapekey_active && !ss->deform_modifiers_active) { sculpt_update_keyblock(ob); } @@ -6703,17 +6973,22 @@ static void sculpt_stroke_update_step(bContext *C, * Same applies to the DEG_id_tag_update() invoked from * sculpt_flush_update_step(). */ - if (ss->modifiers_active) { + if (ss->deform_modifiers_active) { sculpt_flush_stroke_deform(sd, ob, sculpt_tool_is_proxy_used(brush->sculpt_tool)); } - else if (ss->kb) { + else if (ss->shapekey_active) { sculpt_update_keyblock(ob); } ss->cache->first_time = false; /* Cleanup */ - sculpt_flush_update_step(C); + if (brush->sculpt_tool == SCULPT_TOOL_MASK) { + sculpt_flush_update_step(C, SCULPT_UPDATE_MASK); + } + else { + sculpt_flush_update_step(C, SCULPT_UPDATE_COORDS); + } } static void sculpt_brush_exit_tex(Sculpt *sd) @@ -6766,7 +7041,12 @@ static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(str sculpt_undo_push_end(); - sculpt_flush_update_done(C, ob); + if (brush->sculpt_tool == SCULPT_TOOL_MASK) { + sculpt_flush_update_done(C, ob, SCULPT_UPDATE_MASK); + } + else { + sculpt_flush_update_done(C, ob, SCULPT_UPDATE_COORDS); + } WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); } @@ -6797,12 +7077,12 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent ignore_background_click = RNA_boolean_get(op->ptr, "ignore_background_click"); if (ignore_background_click && !over_mesh(C, op, event->x, event->y)) { - paint_stroke_data_free(op); + paint_stroke_free(C, op); return OPERATOR_PASS_THROUGH; } if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { - paint_stroke_data_free(op); + paint_stroke_free(C, op); return OPERATOR_FINISHED; } /* add modal handler */ @@ -6931,8 +7211,19 @@ void sculpt_pbvh_clear(Object *ob) /* Clear out any existing DM and PBVH */ if (ss->pbvh) { BKE_pbvh_free(ss->pbvh); + ss->pbvh = NULL; + } + + if (ss->pmap) { + MEM_freeN(ss->pmap); + ss->pmap = NULL; } - ss->pbvh = NULL; + + if (ss->pmap_mem) { + MEM_freeN(ss->pmap_mem); + ss->pmap_mem = NULL; + } + BKE_object_free_derived_caches(ob); /* Tag to rebuild PBVH in depsgraph. */ @@ -7502,13 +7793,11 @@ void ED_object_sculptmode_exit_ex(Main *bmain, Depsgraph *depsgraph, Scene *scen const int mode_flag = OB_MODE_SCULPT; Mesh *me = BKE_mesh_from_object(ob); - MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob); - if (mmd) { - multires_force_update(ob); - } + multires_flush_sculpt_updates(ob); /* Not needed for now. */ #if 0 + MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob); const int flush_recalc = ed_object_sculptmode_flush_recalc_flag(scene, ob, mmd); #endif @@ -7653,7 +7942,7 @@ static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *UNUSED(op)) } } - MEM_freeN(nodes); + MEM_SAFE_FREE(nodes); sculpt_undo_push_end(); /* force rebuild of pbvh for better BB placement */ @@ -7694,8 +7983,9 @@ static void sample_detail(bContext *C, int mx, int my) CTX_wm_area_set(C, sa); CTX_wm_region_set(C, ar); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewContext vc; - ED_view3d_viewcontext_init(C, &vc); + ED_view3d_viewcontext_init(C, &vc, depsgraph); /* Pick sample detail. */ Sculpt *sd = CTX_data_tool_settings(C)->sculpt; @@ -7738,7 +8028,7 @@ static int sculpt_sample_detail_size_exec(bContext *C, wmOperator *op) static int sculpt_sample_detail_size_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(e)) { ED_workspace_status_text(C, TIP_("Click on the mesh to set the detail")); - WM_cursor_modal_set(CTX_wm_window(C), BC_EYEDROPPER_CURSOR); + WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_EYEDROPPER); WM_event_add_modal_handler(C, op); return OPERATOR_RUNNING_MODAL; } @@ -7859,43 +8149,36 @@ static void filter_cache_init_task_cb(void *__restrict userdata, const TaskParallelTLS *__restrict UNUSED(tls)) { SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; PBVHNode *node = data->nodes[i]; - PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE) - { - if (!vd.mask || (vd.mask && *vd.mask < 1.0f)) { - data->node_mask[i] = 1; - } - } - BKE_pbvh_vertex_iter_end; - - if (data->node_mask[i] == 1) { - sculpt_undo_push_node(data->ob, node, SCULPT_UNDO_COORDS); - } + sculpt_undo_push_node(data->ob, node, SCULPT_UNDO_COORDS); } static void sculpt_filter_cache_init(Object *ob, Sculpt *sd) { SculptSession *ss = ob->sculpt; PBVH *pbvh = ob->sculpt->pbvh; - PBVHNode **nodes; - int totnode; ss->filter_cache = MEM_callocN(sizeof(FilterCache), "filter cache"); ss->filter_cache->random_seed = rand(); + float center[3] = {0.0f}; SculptSearchSphereData search_data = { .original = true, - }; - BKE_pbvh_search_gather(pbvh, NULL, &search_data, &nodes, &totnode); + .center = center, + .radius_squared = FLT_MAX, + .ignore_fully_masked = true, - int *node_mask = MEM_callocN((unsigned int)totnode * sizeof(int), "node mask"); + }; + BKE_pbvh_search_gather(pbvh, + sculpt_search_sphere_cb, + &search_data, + &ss->filter_cache->nodes, + &ss->filter_cache->totnode); - for (int i = 0; i < totnode; i++) { - BKE_pbvh_node_mark_normals_update(nodes[i]); + for (int i = 0; i < ss->filter_cache->totnode; i++) { + BKE_pbvh_node_mark_normals_update(ss->filter_cache->nodes[i]); } /* mesh->runtime.subdiv_ccg is not available. Updating of the normals is done during drawing. @@ -7907,45 +8190,14 @@ static void sculpt_filter_cache_init(Object *ob, Sculpt *sd) SculptThreadedTaskData data = { .sd = sd, .ob = ob, - .nodes = nodes, - .node_mask = node_mask, + .nodes = ss->filter_cache->nodes, }; - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range(0, totnode, &data, filter_cache_init_task_cb, &settings); - - int tot_active_nodes = 0; - int active_node_index = 0; - PBVHNode **active_nodes; - - /* Count number of PBVH nodes that are not fully masked */ - for (int i = 0; i < totnode; i++) { - if (node_mask[i] == 1) { - tot_active_nodes++; - } - } - - /* Create the final list of nodes that is going to be processed in the filter */ - active_nodes = MEM_callocN(tot_active_nodes * sizeof(PBVHNode *), "active nodes"); - - for (int i = 0; i < totnode; i++) { - if (node_mask[i] == 1) { - active_nodes[active_node_index] = nodes[i]; - active_node_index++; - } - } - - ss->filter_cache->nodes = active_nodes; - ss->filter_cache->totnode = tot_active_nodes; - - if (nodes) { - MEM_freeN(nodes); - } - if (node_mask) { - MEM_freeN(node_mask); - } + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings( + &settings, (sd->flags & SCULPT_USE_OPENMP), ss->filter_cache->totnode); + BKE_pbvh_parallel_range( + 0, ss->filter_cache->totnode, &data, filter_cache_init_task_cb, &settings); } static void sculpt_filter_cache_free(SculptSession *ss) @@ -8017,15 +8269,25 @@ static void mesh_filter_task_cb(void *__restrict userdata, float fade = vd.mask ? *vd.mask : 0.0f; fade = 1 - fade; fade *= data->filter_strength; + + if (fade == 0.0f) { + continue; + } + copy_v3_v3(orig_co, orig_data.co); switch (filter_type) { case MESH_FILTER_SMOOTH: CLAMP(fade, -1.0f, 1.0f); - if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) { - neighbor_average(ss, avg, vd.index); - } - else if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { - bmesh_neighbor_average(avg, vd.bm_vert); + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_FACES: + neighbor_average(ss, avg, vd.index); + break; + case PBVH_BMESH: + bmesh_neighbor_average(avg, vd.bm_vert); + break; + case PBVH_GRIDS: + grids_neighbor_average(ss, avg, vd.index); + break; } sub_v3_v3v3(val, avg, orig_co); madd_v3_v3v3fl(val, orig_co, val, fade); @@ -8064,11 +8326,16 @@ static void mesh_filter_task_cb(void *__restrict userdata, mid_v3_v3v3(disp, disp, disp2); break; - case MESH_FILTER_RANDOM: + case MESH_FILTER_RANDOM: { normal_short_to_float_v3(normal, orig_data.no); - mul_v3_fl(normal, BLI_hash_int_01(vd.index ^ ss->filter_cache->random_seed) - 0.5f); + /* Index is not unique for multires, so hash by vertex coordinates. */ + const uint *hash_co = (const uint *)orig_co; + const uint hash = BLI_hash_int_2d(hash_co[0], hash_co[1]) ^ + BLI_hash_int_2d(hash_co[2], ss->filter_cache->random_seed); + mul_v3_fl(normal, hash * (1.0f / (float)0xFFFFFFFF) - 0.5f); mul_v3_v3fl(disp, normal, fade); break; + } } for (int it = 0; it < 3; it++) { @@ -8079,8 +8346,9 @@ static void mesh_filter_task_cb(void *__restrict userdata, add_v3_v3v3(final_pos, orig_co, disp); copy_v3_v3(vd.co, final_pos); - if (vd.mvert) + if (vd.mvert) { vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } } BKE_pbvh_vertex_iter_end; @@ -8100,7 +8368,7 @@ static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent * if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { sculpt_filter_cache_free(ss); sculpt_undo_push_end(); - sculpt_flush_update_done(C, ob); + sculpt_flush_update_done(C, ob, SCULPT_UPDATE_COORDS); return OPERATOR_FINISHED; } @@ -8124,17 +8392,16 @@ static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent * .filter_strength = filter_strength, }; - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && - ss->filter_cache->totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range(0, ss->filter_cache->totnode, &data, mesh_filter_task_cb, &settings); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings( + &settings, (sd->flags & SCULPT_USE_OPENMP), ss->filter_cache->totnode); + BKE_pbvh_parallel_range(0, ss->filter_cache->totnode, &data, mesh_filter_task_cb, &settings); - if (ss->modifiers_active || ss->kb) { + if (ss->deform_modifiers_active || ss->shapekey_active) { sculpt_flush_stroke_deform(sd, ob, true); } - sculpt_flush_update_step(C); + sculpt_flush_update_step(C, SCULPT_UPDATE_COORDS); return OPERATOR_RUNNING_MODAL; } @@ -8148,10 +8415,6 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent SculptSession *ss = ob->sculpt; PBVH *pbvh = ob->sculpt->pbvh; - if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { - return OPERATOR_CANCELLED; - } - int deform_axis = RNA_enum_get(op->ptr, "deform_axis"); if (deform_axis == 0) { return OPERATOR_CANCELLED; @@ -8260,35 +8523,42 @@ static void mask_filter_task_cb(void *__restrict userdata, BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { - float val; float delta, gain, offset, max, min; float prev_val = *vd.mask; SculptVertexNeighborIter ni; switch (mode) { case MASK_FILTER_SMOOTH: - if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) { - val = neighbor_average_mask(ss, vd.index) - *vd.mask; + case MASK_FILTER_SHARPEN: { + float val = 0.0f; + + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_FACES: + val = neighbor_average_mask(ss, vd.index); + break; + case PBVH_BMESH: + val = bmesh_neighbor_average_mask(vd.bm_vert, vd.cd_vert_mask_offset); + break; + case PBVH_GRIDS: + val = grids_neighbor_average_mask(ss, vd.index); + break; } - else { - val = bmesh_neighbor_average_mask(vd.bm_vert, vd.cd_vert_mask_offset) - *vd.mask; - } - *vd.mask += val; - break; - case MASK_FILTER_SHARPEN: - if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) { - val = neighbor_average_mask(ss, vd.index) - *vd.mask; - } - else { - val = bmesh_neighbor_average_mask(vd.bm_vert, vd.cd_vert_mask_offset) - *vd.mask; - } - if (*vd.mask > 0.5f) { - *vd.mask += 0.05f; + + val -= *vd.mask; + + if (mode == MASK_FILTER_SMOOTH) { + *vd.mask += val; } - else { - *vd.mask -= 0.05f; + else if (mode == MASK_FILTER_SHARPEN) { + if (*vd.mask > 0.5f) { + *vd.mask += 0.05f; + } + else { + *vd.mask -= 0.05f; + } + *vd.mask += val / 2; } - *vd.mask += val / 2; break; + } case MASK_FILTER_GROW: max = 0.0f; sculpt_vertex_neighbors_iter_begin(ss, vd.index, ni) @@ -8339,7 +8609,7 @@ static void mask_filter_task_cb(void *__restrict userdata, BKE_pbvh_vertex_iter_end; if (update) { - BKE_pbvh_node_mark_redraw(node); + BKE_pbvh_node_mark_update_mask(node); } } @@ -8355,10 +8625,6 @@ static int sculpt_mask_filter_exec(bContext *C, wmOperator *op) int totnode; int filter_type = RNA_enum_get(op->ptr, "filter_type"); - if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { - return OPERATOR_CANCELLED; - } - BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true); sculpt_vertex_random_access_init(ss); @@ -8380,16 +8646,16 @@ static int sculpt_mask_filter_exec(bContext *C, wmOperator *op) int iterations = RNA_int_get(op->ptr, "iterations"); /* Auto iteration count calculates the number of iteration based on the vertices of the mesh to - * avoid adding an unnecesary ammount of undo steps when using the operator from a shortcut. One - * iteration per 50000 vertices in the mesh should be fine in most cases. Maybe we want this to - * be configurable */ + * avoid adding an unnecessary amount of undo steps when using the operator from a shortcut. + * One iteration per 50000 vertices in the mesh should be fine in most cases. + * Maybe we want this to be configurable. */ if (RNA_boolean_get(op->ptr, "auto_iteration_count")) { iterations = (int)(num_verts / 50000.0f) + 1; } for (int i = 0; i < iterations; i++) { if (ELEM(filter_type, MASK_FILTER_GROW, MASK_FILTER_SHRINK)) { - prev_mask = MEM_mallocN((unsigned long)num_verts * sizeof(float), "prevmask"); + prev_mask = MEM_mallocN(num_verts * sizeof(float), "prevmask"); for (int j = 0; j < num_verts; j++) { prev_mask[j] = sculpt_vertex_mask_get(ss, j); } @@ -8403,19 +8669,16 @@ static int sculpt_mask_filter_exec(bContext *C, wmOperator *op) .prev_mask = prev_mask, }; - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range(0, totnode, &data, mask_filter_task_cb, &settings); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BKE_pbvh_parallel_range(0, totnode, &data, mask_filter_task_cb, &settings); if (ELEM(filter_type, MASK_FILTER_GROW, MASK_FILTER_SHRINK)) { MEM_freeN(prev_mask); } } - if (nodes) { - MEM_freeN(nodes); - } + MEM_SAFE_FREE(nodes); sculpt_undo_push_end(); @@ -8461,17 +8724,17 @@ static void SCULPT_OT_mask_filter(struct wmOperatorType *ot) "Use a automatic number of iterations based on the number of vertices of the sculpt"); } -static float neighbor_dirty_mask(SculptSession *ss, const int vert) +static float neighbor_dirty_mask(SculptSession *ss, PBVHVertexIter *vd) { int total = 0; float avg[3]; zero_v3(avg); SculptVertexNeighborIter ni; - sculpt_vertex_neighbors_iter_begin(ss, vert, ni) + sculpt_vertex_neighbors_iter_begin(ss, vd->index, ni) { float normalized[3]; - sub_v3_v3v3(normalized, sculpt_vertex_co_get(ss, ni.index), sculpt_vertex_co_get(ss, vert)); + sub_v3_v3v3(normalized, sculpt_vertex_co_get(ss, ni.index), vd->co); normalize_v3(normalized); add_v3_v3(avg, normalized); total++; @@ -8481,7 +8744,12 @@ static float neighbor_dirty_mask(SculptSession *ss, const int vert) if (total > 0) { mul_v3_fl(avg, 1.0f / total); float normal[3]; - sculpt_vertex_normal_get(ss, vert, normal); + if (vd->no) { + normal_short_to_float_v3(normal, vd->no); + } + else { + copy_v3_v3(normal, vd->fno); + } float dot = dot_v3v3(avg, normal); float angle = max_ff(saacosf(dot), 0.0f); return angle; @@ -8489,25 +8757,75 @@ static float neighbor_dirty_mask(SculptSession *ss, const int vert) return 0; } -static void dirty_mask_task_cb(void *__restrict userdata, - const int i, - const TaskParallelTLS *__restrict UNUSED(tls)) +typedef struct DirtyMaskRangeData { + float min, max; +} DirtyMaskRangeData; + +static void dirty_mask_compute_range_task_cb(void *__restrict userdata, + const int i, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + PBVHNode *node = data->nodes[i]; + DirtyMaskRangeData *range = tls->userdata_chunk; + PBVHVertexIter vd; + + BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE) + { + float dirty_mask = neighbor_dirty_mask(ss, &vd); + range->min = min_ff(dirty_mask, range->min); + range->max = max_ff(dirty_mask, range->max); + } + BKE_pbvh_vertex_iter_end; +} + +static void dirty_mask_compute_range_reduce(const void *__restrict UNUSED(userdata), + void *__restrict chunk_join, + void *__restrict chunk) +{ + DirtyMaskRangeData *join = chunk_join; + DirtyMaskRangeData *range = chunk; + join->min = min_ff(range->min, join->min); + join->max = max_ff(range->max, join->max); +} + +static void dirty_mask_apply_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]; PBVHVertexIter vd; + + const bool dirty_only = data->dirty_mask_dirty_only; + const float min = data->dirty_mask_min; + const float max = data->dirty_mask_max; + + float range = max - min; + if (range < 0.0001f) { + range = 0; + } + else { + range = 1.0f / range; + } + BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { - float val; - val = neighbor_dirty_mask(ss, vd.index); - data->prev_mask[vd.index] = val; + float dirty_mask = neighbor_dirty_mask(ss, &vd); + float mask = *vd.mask + (1 - ((dirty_mask - min) * range)); + if (dirty_only) { + mask = fminf(mask, 0.5f) * 2.0f; + } + *vd.mask = CLAMPIS(mask, 0.0f, 1.0f); + if (vd.mvert) { vd.mvert->flag |= ME_VERT_PBVH_UPDATE; } } BKE_pbvh_vertex_iter_end; - BKE_pbvh_node_mark_redraw(node); + BKE_pbvh_node_mark_update_mask(node); } static int sculpt_dirty_mask_exec(bContext *C, wmOperator *op) @@ -8521,10 +8839,6 @@ static int sculpt_dirty_mask_exec(bContext *C, wmOperator *op) Sculpt *sd = CTX_data_tool_settings(C)->sculpt; int totnode; - if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { - return OPERATOR_CANCELLED; - } - BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true); sculpt_vertex_random_access_init(ss); @@ -8533,8 +8847,6 @@ static int sculpt_dirty_mask_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - int num_verts = sculpt_vertex_count_get(ss); - BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); sculpt_undo_push_begin("Dirty Mask"); @@ -8542,59 +8854,32 @@ static int sculpt_dirty_mask_exec(bContext *C, wmOperator *op) sculpt_undo_push_node(ob, nodes[i], SCULPT_UNDO_MASK); } - float *prev_mask = NULL; - - prev_mask = MEM_mallocN((unsigned long)num_verts * sizeof(float), "prevmask"); - for (int j = 0; j < num_verts; j++) { - prev_mask[j] = sculpt_vertex_mask_get(ss, j); - } - SculptThreadedTaskData data = { .sd = sd, .ob = ob, .nodes = nodes, - .prev_mask = prev_mask, + .dirty_mask_dirty_only = RNA_boolean_get(op->ptr, "dirty_only"), + }; + DirtyMaskRangeData range = { + .min = FLT_MAX, + .max = -FLT_MAX, }; - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range(0, totnode, &data, dirty_mask_task_cb, &settings); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - float min = FLT_MAX; - float max = FLT_MIN; - for (int i = 0; i < num_verts; i++) { - float val = prev_mask[i]; - if (val < min) { - min = val; - } - if (val > max) { - max = val; - } - } + settings.func_reduce = dirty_mask_compute_range_reduce; + settings.userdata_chunk = ⦥ + settings.userdata_chunk_size = sizeof(DirtyMaskRangeData); - float range = max - min; - if (range < 0.0001f) { - range = 0; - } - else { - range = 1.0f / range; - } + BKE_pbvh_parallel_range(0, totnode, &data, dirty_mask_compute_range_task_cb, &settings); + data.dirty_mask_min = range.min; + data.dirty_mask_max = range.max; + BKE_pbvh_parallel_range(0, totnode, &data, dirty_mask_apply_task_cb, &settings); - bool dirty_only = RNA_boolean_get(op->ptr, "dirty_only"); - for (int i = 0; i < num_verts; i++) { - sculpt_vertex_mask_set( - ss, i, sculpt_vertex_mask_get(ss, i) + (1 - ((prev_mask[i] - min) * range))); - if (dirty_only) { - sculpt_vertex_mask_set(ss, i, fminf(sculpt_vertex_mask_get(ss, i), 0.5f) * 2.0f); - } - sculpt_vertex_mask_clamp(ss, i, 0.0f, 1.0f); - } - MEM_freeN(prev_mask); + MEM_SAFE_FREE(nodes); - if (nodes) { - MEM_freeN(nodes); - } + BKE_pbvh_update_vertex_data(pbvh, SCULPT_UPDATE_MASK); sculpt_undo_push_end(); @@ -8630,19 +8915,22 @@ static void sculpt_mask_expand_cancel(bContext *C, wmOperator *op) MEM_freeN(op->customdata); - int vert_count = sculpt_vertex_count_get(ss); - for (int i = 0; i < vert_count; i++) { - sculpt_vertex_mask_set(ss, i, ss->filter_cache->prev_mask[i]); - } + for (int n = 0; n < ss->filter_cache->totnode; n++) { + PBVHNode *node = ss->filter_cache->nodes[n]; + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE) + { + *vd.mask = ss->filter_cache->prev_mask[vd.index]; + } + BKE_pbvh_vertex_iter_end; - for (int i = 0; i < ss->filter_cache->totnode; i++) { - BKE_pbvh_node_mark_redraw(ss->filter_cache->nodes[i]); + BKE_pbvh_node_mark_redraw(node); } - sculpt_flush_update_step(C); + sculpt_flush_update_step(C, SCULPT_UPDATE_MASK); sculpt_filter_cache_free(ss); sculpt_undo_push_end(); - sculpt_flush_update_done(C, ob); + sculpt_flush_update_done(C, ob, SCULPT_UPDATE_MASK); ED_workspace_status_text(C, NULL); } @@ -8692,7 +8980,7 @@ static void sculpt_expand_task_cb(void *__restrict userdata, vd.mvert->flag |= ME_VERT_PBVH_UPDATE; } *vd.mask = final_mask; - BKE_pbvh_node_mark_redraw(node); + BKE_pbvh_node_mark_update_mask(node); } } BKE_pbvh_vertex_iter_end; @@ -8724,8 +9012,11 @@ static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent * if ((event->type == ESCKEY && event->val == KM_PRESS) || (event->type == RIGHTMOUSE && event->val == KM_PRESS)) { + /* Returning OPERATOR_CANCELLED will leak memory due to not finishing + * undo. Better solution could be to make paint_mesh_restore_co work + * for this case. */ sculpt_mask_expand_cancel(C, op); - return OPERATOR_CANCELLED; + return OPERATOR_FINISHED; } if ((event->type == LEFTMOUSE && event->val == KM_RELEASE) || @@ -8743,30 +9034,36 @@ static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent * int smooth_iterations = RNA_int_get(op->ptr, "smooth_iterations"); BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false); for (int i = 0; i < smooth_iterations; i++) { - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && - ss->filter_cache->totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range(0, ss->filter_cache->totnode, &data, mask_filter_task_cb, &settings); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings( + &settings, (sd->flags & SCULPT_USE_OPENMP), ss->filter_cache->totnode); + BKE_pbvh_parallel_range(0, ss->filter_cache->totnode, &data, mask_filter_task_cb, &settings); } /* Pivot position */ if (RNA_boolean_get(op->ptr, "update_pivot")) { const char symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; + const float threshold = 0.2f; float avg[3]; int total = 0; - float threshold = 0.2f; zero_v3(avg); - int vertex_count = sculpt_vertex_count_get(ss); - for (int i = 0; i < vertex_count; i++) { - if (sculpt_vertex_mask_get(ss, i) < (0.5f + threshold) && - sculpt_vertex_mask_get(ss, i) > (0.5f - threshold) && - check_vertex_pivot_symmetry( - sculpt_vertex_co_get(ss, i), ss->filter_cache->mask_expand_initial_co, symm)) { - total++; - add_v3_v3(avg, sculpt_vertex_co_get(ss, i)); + + for (int n = 0; n < ss->filter_cache->totnode; n++) { + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin(ss->pbvh, ss->filter_cache->nodes[n], vd, PBVH_ITER_UNIQUE) + { + const float mask = (vd.mask) ? *vd.mask : 0.0f; + if (mask < (0.5f + threshold) && mask > (0.5f - threshold)) { + if (check_vertex_pivot_symmetry( + vd.co, ss->filter_cache->mask_expand_initial_co, symm)) { + add_v3_v3(avg, vd.co); + total++; + } + } } + BKE_pbvh_vertex_iter_end; } + if (total > 0) { mul_v3_fl(avg, 1.0f / total); copy_v3_v3(ss->pivot_pos, avg); @@ -8783,7 +9080,7 @@ static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent * sculpt_filter_cache_free(ss); sculpt_undo_push_end(); - sculpt_flush_update_done(C, ob); + sculpt_flush_update_done(C, ob, SCULPT_UPDATE_MASK); ED_workspace_status_text(C, NULL); return OPERATOR_FINISHED; } @@ -8806,20 +9103,60 @@ static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent * .mask_expand_invert_mask = RNA_boolean_get(op->ptr, "invert"), .mask_expand_keep_prev_mask = RNA_boolean_get(op->ptr, "keep_previous_mask"), }; - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && - ss->filter_cache->totnode > SCULPT_THREADED_LIMIT); - - BLI_task_parallel_range(0, ss->filter_cache->totnode, &data, sculpt_expand_task_cb, &settings); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings( + &settings, (sd->flags & SCULPT_USE_OPENMP), ss->filter_cache->totnode); + BKE_pbvh_parallel_range(0, ss->filter_cache->totnode, &data, sculpt_expand_task_cb, &settings); ss->filter_cache->mask_update_current_it = mask_expand_update_it; } - sculpt_flush_update_step(C); + sculpt_flush_update_step(C, SCULPT_UPDATE_MASK); return OPERATOR_RUNNING_MODAL; } +typedef struct MaskExpandFloodFillData { + float original_normal[3]; + float edge_sensitivity; + bool use_normals; +} MaskExpandFloodFillData; + +static bool mask_expand_floodfill_cb( + SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata) +{ + MaskExpandFloodFillData *data = userdata; + + if (!is_duplicate) { + int to_it = ss->filter_cache->mask_update_it[from_v] + 1; + ss->filter_cache->mask_update_it[to_v] = to_it; + if (to_it > ss->filter_cache->mask_update_last_it) { + ss->filter_cache->mask_update_last_it = to_it; + } + + if (data->use_normals) { + float current_normal[3], prev_normal[3]; + sculpt_vertex_normal_get(ss, to_v, current_normal); + sculpt_vertex_normal_get(ss, from_v, prev_normal); + const float from_edge_factor = ss->filter_cache->edge_factor[from_v]; + ss->filter_cache->edge_factor[to_v] = dot_v3v3(current_normal, prev_normal) * + from_edge_factor; + ss->filter_cache->normal_factor[to_v] = dot_v3v3(data->original_normal, current_normal) * + powf(from_edge_factor, data->edge_sensitivity); + CLAMP(ss->filter_cache->normal_factor[to_v], 0.0f, 1.0f); + } + } + else { + /* PBVH_GRIDS duplicate handling */ + ss->filter_cache->mask_update_it[to_v] = ss->filter_cache->mask_update_it[from_v]; + if (data->use_normals) { + ss->filter_cache->edge_factor[to_v] = ss->filter_cache->edge_factor[from_v]; + ss->filter_cache->normal_factor[to_v] = ss->filter_cache->normal_factor[from_v]; + } + } + + return true; +} + static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent *event) { Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); @@ -8827,20 +9164,14 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent SculptSession *ss = ob->sculpt; Sculpt *sd = CTX_data_tool_settings(C)->sculpt; PBVH *pbvh = ob->sculpt->pbvh; - float original_normal[3]; bool use_normals = RNA_boolean_get(op->ptr, "use_normals"); - int edge_sensitivity = RNA_int_get(op->ptr, "edge_sensitivity"); SculptCursorGeometryInfo sgi; float mouse[2]; mouse[0] = event->mval[0]; mouse[1] = event->mval[1]; - if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { - return OPERATOR_CANCELLED; - } - sculpt_vertex_random_access_init(ss); op->customdata = MEM_mallocN(2 * sizeof(float), "initial mouse position"); @@ -8854,16 +9185,7 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent ss->filter_cache = MEM_callocN(sizeof(FilterCache), "filter cache"); - SculptSearchSphereData searchdata = { - .ss = ss, - .sd = sd, - .radius_squared = FLT_MAX, - }; - BKE_pbvh_search_gather(pbvh, - sculpt_search_sphere_cb, - &searchdata, - &ss->filter_cache->nodes, - &ss->filter_cache->totnode); + BKE_pbvh_search_gather(pbvh, NULL, NULL, &ss->filter_cache->nodes, &ss->filter_cache->totnode); sculpt_undo_push_begin("Mask Expand"); @@ -8877,6 +9199,11 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent if (use_normals) { ss->filter_cache->normal_factor = MEM_callocN(sizeof(float) * vertex_count, "mask update normal factor"); + ss->filter_cache->edge_factor = MEM_callocN(sizeof(float) * vertex_count, + "mask update normal factor"); + for (int i = 0; i < vertex_count; i++) { + ss->filter_cache->edge_factor[i] = 1.0f; + } } ss->filter_cache->prev_mask = MEM_callocN(sizeof(float) * vertex_count, "prev mask"); @@ -8886,68 +9213,21 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent ss->filter_cache->mask_update_last_it = 1; ss->filter_cache->mask_update_current_it = 1; - ss->filter_cache->mask_update_it[(int)sculpt_active_vertex_get(ss)] = 1; - - copy_v3_v3(ss->filter_cache->mask_expand_initial_co, - sculpt_vertex_co_get(ss, sculpt_active_vertex_get(ss))); - - char *visited_vertices = MEM_callocN(vertex_count * sizeof(char), "visited vertices"); + ss->filter_cache->mask_update_it[sculpt_active_vertex_get(ss)] = 1; - sculpt_vertex_normal_get(ss, sculpt_active_vertex_get(ss), original_normal); + copy_v3_v3(ss->filter_cache->mask_expand_initial_co, sculpt_active_vertex_co_get(ss)); - GSQueue *queue = BLI_gsqueue_new(sizeof(VertexTopologyIterator)); - VertexTopologyIterator mevit; + SculptFloodFill flood; + sculpt_floodfill_init(ss, &flood); + sculpt_floodfill_add_active(sd, ob, ss, &flood, FLT_MAX); - const char symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; - for (char i = 0; i <= symm; ++i) { - if (is_symmetry_iteration_valid(i, symm)) { - float location[3]; - flip_v3_v3(location, sculpt_vertex_co_get(ss, sculpt_active_vertex_get(ss)), i); - if (i == 0) { - mevit.v = sculpt_active_vertex_get(ss); - mevit.edge_factor = 1.0f; - } - else { - mevit.v = sculpt_nearest_vertex_get(sd, ob, location, FLT_MAX, false); - mevit.edge_factor = 1.0f; - } - if (mevit.v != -1) { - sculpt_vertex_mask_set(ss, mevit.v, 1.0f); - mevit.it = 0; - BLI_gsqueue_push(queue, &mevit); - } - } - } - - while (!BLI_gsqueue_is_empty(queue)) { - VertexTopologyIterator c_mevit; - BLI_gsqueue_pop(queue, &c_mevit); - SculptVertexNeighborIter ni; - sculpt_vertex_neighbors_iter_begin(ss, c_mevit.v, ni) - { - if (visited_vertices[(int)ni.index] == 0) { - VertexTopologyIterator new_entry; - new_entry.v = ni.index; - new_entry.it = c_mevit.it + 1; - ss->filter_cache->mask_update_it[(int)new_entry.v] = new_entry.it; - visited_vertices[(int)ni.index] = 1; - if (ss->filter_cache->mask_update_last_it < new_entry.it) { - ss->filter_cache->mask_update_last_it = new_entry.it; - } - if (use_normals) { - float current_normal[3], prev_normal[3]; - sculpt_vertex_normal_get(ss, ni.index, current_normal); - sculpt_vertex_normal_get(ss, c_mevit.v, prev_normal); - new_entry.edge_factor = dot_v3v3(current_normal, prev_normal) * c_mevit.edge_factor; - ss->filter_cache->normal_factor[ni.index] = dot_v3v3(original_normal, current_normal) * - powf(c_mevit.edge_factor, edge_sensitivity); - CLAMP(ss->filter_cache->normal_factor[ni.index], 0, 1); - } - BLI_gsqueue_push(queue, &new_entry); - } - } - sculpt_vertex_neighbors_iter_end(ni); - } + MaskExpandFloodFillData fdata = { + .use_normals = use_normals, + .edge_sensitivity = RNA_int_get(op->ptr, "edge_sensitivity"), + }; + sculpt_active_vertex_normal_get(ss, fdata.original_normal); + sculpt_floodfill_execute(ss, &flood, mask_expand_floodfill_cb, &fdata); + sculpt_floodfill_free(&flood); if (use_normals) { for (int repeat = 0; repeat < 2; repeat++) { @@ -8962,11 +9242,9 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent ss->filter_cache->normal_factor[i] = avg / ni.size; } } - } - - BLI_gsqueue_free(queue); - MEM_freeN(visited_vertices); + MEM_SAFE_FREE(ss->filter_cache->edge_factor); + } SculptThreadedTaskData data = { .sd = sd, @@ -8977,19 +9255,17 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent .mask_expand_invert_mask = RNA_boolean_get(op->ptr, "invert"), .mask_expand_keep_prev_mask = RNA_boolean_get(op->ptr, "keep_previous_mask"), }; - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && - ss->filter_cache->totnode > SCULPT_THREADED_LIMIT); - - BLI_task_parallel_range(0, ss->filter_cache->totnode, &data, sculpt_expand_task_cb, &settings); + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings( + &settings, (sd->flags & SCULPT_USE_OPENMP), ss->filter_cache->totnode); + BKE_pbvh_parallel_range(0, ss->filter_cache->totnode, &data, sculpt_expand_task_cb, &settings); const char *status_str = TIP_( "Move the mouse to expand the mask from the active vertex. LBM: confirm mask, ESC/RMB: " "cancel"); ED_workspace_status_text(C, status_str); - sculpt_flush_update_step(C); + sculpt_flush_update_step(C, SCULPT_UPDATE_MASK); WM_event_add_modal_handler(C, op); return OPERATOR_RUNNING_MODAL; } @@ -9061,46 +9337,47 @@ void sculpt_geometry_preview_lines_update(bContext *C, SculptSession *ss, float } float brush_co[3]; - copy_v3_v3(brush_co, sculpt_vertex_co_get(ss, sculpt_active_vertex_get(ss))); + copy_v3_v3(brush_co, sculpt_active_vertex_co_get(ss)); char *visited_vertices = MEM_callocN(sculpt_vertex_count_get(ss) * sizeof(char), "visited vertices"); + /* Assuming an average of 6 edges per vertex in a triangulated mesh */ + const int max_preview_vertices = sculpt_vertex_count_get(ss) * 3 * 2; + if (ss->preview_vert_index_list == NULL) { - ss->preview_vert_index_list = MEM_callocN(4 * sizeof(int) * sculpt_vertex_count_get(ss), - "preview lines"); + ss->preview_vert_index_list = MEM_callocN(max_preview_vertices * sizeof(int), "preview lines"); } - BLI_Stack *not_visited_vertices = BLI_stack_new(sizeof(VertexTopologyIterator), - "Not visited vertices stack"); - VertexTopologyIterator mevit; - mevit.v = sculpt_active_vertex_get(ss); - BLI_stack_push(not_visited_vertices, &mevit); + GSQueue *not_visited_vertices = BLI_gsqueue_new(sizeof(int)); + int active_v = sculpt_active_vertex_get(ss); + BLI_gsqueue_push(not_visited_vertices, &active_v); - while (!BLI_stack_is_empty(not_visited_vertices)) { - VertexTopologyIterator c_mevit; - BLI_stack_pop(not_visited_vertices, &c_mevit); + while (!BLI_gsqueue_is_empty(not_visited_vertices)) { + int from_v; + BLI_gsqueue_pop(not_visited_vertices, &from_v); SculptVertexNeighborIter ni; - sculpt_vertex_neighbors_iter_begin(ss, c_mevit.v, ni) + sculpt_vertex_neighbors_iter_begin(ss, from_v, ni) { - VertexTopologyIterator new_entry; - new_entry.v = ni.index; - new_entry.it = c_mevit.it + 1; - ss->preview_vert_index_list[totpoints] = c_mevit.v; - totpoints++; - ss->preview_vert_index_list[totpoints] = new_entry.v; - totpoints++; - if (visited_vertices[(int)ni.index] == 0) { - visited_vertices[(int)ni.index] = 1; - if (len_squared_v3v3(brush_co, sculpt_vertex_co_get(ss, new_entry.v)) < radius * radius) { - BLI_stack_push(not_visited_vertices, &new_entry); + if (totpoints + (ni.size * 2) < max_preview_vertices) { + int to_v = ni.index; + ss->preview_vert_index_list[totpoints] = from_v; + totpoints++; + ss->preview_vert_index_list[totpoints] = to_v; + totpoints++; + if (visited_vertices[to_v] == 0) { + visited_vertices[to_v] = 1; + const float *co = sculpt_vertex_co_get(ss, to_v); + if (len_squared_v3v3(brush_co, co) < radius * radius) { + BLI_gsqueue_push(not_visited_vertices, &to_v); + } } } } sculpt_vertex_neighbors_iter_end(ni); } - BLI_stack_free(not_visited_vertices); + BLI_gsqueue_free(not_visited_vertices); MEM_freeN(visited_vertices); @@ -9234,8 +9511,7 @@ static void sculpt_transform_task_cb(void *__restrict userdata, } BKE_pbvh_vertex_iter_end; - BKE_pbvh_node_mark_redraw(node); - BKE_pbvh_node_mark_normals_update(node); + BKE_pbvh_node_mark_update(node); } void ED_sculpt_update_modal_transform(struct bContext *C) @@ -9295,18 +9571,17 @@ void ED_sculpt_update_modal_transform(struct bContext *C) mul_m4_m4m4(data.transform_mats[i], pivot_mat, data.transform_mats[i]); } - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && - ss->filter_cache->totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings( + &settings, (sd->flags & SCULPT_USE_OPENMP), ss->filter_cache->totnode); + BKE_pbvh_parallel_range( 0, ss->filter_cache->totnode, &data, sculpt_transform_task_cb, &settings); - if (ss->modifiers_active || ss->kb) { + if (ss->deform_modifiers_active || ss->shapekey_active) { sculpt_flush_stroke_deform(sd, ob, true); } - sculpt_flush_update_step(C); + sculpt_flush_update_step(C, SCULPT_UPDATE_COORDS); } void ED_sculpt_end_transform(struct bContext *C) @@ -9317,7 +9592,7 @@ void ED_sculpt_end_transform(struct bContext *C) sculpt_filter_cache_free(ss); } sculpt_undo_push_end(); - sculpt_flush_update_done(C, ob); + sculpt_flush_update_done(C, ob, SCULPT_UPDATE_COORDS); } typedef enum eSculptPivotPositionModes { @@ -9366,73 +9641,80 @@ static int sculpt_set_pivot_position_invoke(bContext *C, wmOperator *op, const w Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); const char symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; - if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { - return OPERATOR_CANCELLED; - } - int mode = RNA_enum_get(op->ptr, "mode"); BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true); - int vert_count = sculpt_vertex_count_get(ss); - /* Pivot to center */ if (mode == SCULPT_PIVOT_POSITION_ORIGIN) { zero_v3(ss->pivot_pos); } + /* Pivot to active vertex */ + else if (mode == SCULPT_PIVOT_POSITION_ACTIVE_VERTEX) { + copy_v3_v3(ss->pivot_pos, sculpt_active_vertex_co_get(ss)); + } + /* Pivot to raycast surface */ + else if (mode == SCULPT_PIVOT_POSITION_CURSOR_SURFACE) { + float stroke_location[3]; + float mouse[2]; + mouse[0] = event->mval[0]; + mouse[1] = event->mval[1]; + if (sculpt_stroke_get_location(C, stroke_location, mouse)) { + copy_v3_v3(ss->pivot_pos, stroke_location); + } + } + else { + PBVHNode **nodes; + int totnode; + BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); - /* Pivot to unmasked */ - if (mode == SCULPT_PIVOT_POSITION_UNMASKED) { float avg[3]; int total = 0; zero_v3(avg); - for (int i = 0; i < vert_count; i++) { - if (sculpt_vertex_mask_get(ss, i) < 1.0f && - check_vertex_pivot_symmetry(sculpt_vertex_co_get(ss, i), ss->pivot_pos, symm)) { - total++; - add_v3_v3(avg, sculpt_vertex_co_get(ss, i)); + + /* Pivot to unmasked */ + if (mode == SCULPT_PIVOT_POSITION_UNMASKED) { + for (int n = 0; n < totnode; n++) { + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) + { + const float mask = (vd.mask) ? *vd.mask : 0.0f; + if (mask < 1.0f) { + if (check_vertex_pivot_symmetry(vd.co, ss->pivot_pos, symm)) { + add_v3_v3(avg, vd.co); + total++; + } + } + } + BKE_pbvh_vertex_iter_end; } } - if (total > 0) { - mul_v3_fl(avg, 1.0f / total); - copy_v3_v3(ss->pivot_pos, avg); - } - } + /* Pivot to mask border */ + else if (mode == SCULPT_PIVOT_POSITION_MASK_BORDER) { + const float threshold = 0.2f; - /* Pivot to mask border */ - if (mode == SCULPT_PIVOT_POSITION_MASK_BORDER) { - float avg[3]; - int total = 0; - float threshold = 0.2f; - zero_v3(avg); - for (int i = 0; i < vert_count; i++) { - if (sculpt_vertex_mask_get(ss, i) < (0.5f + threshold) && - sculpt_vertex_mask_get(ss, i) > (0.5f - threshold) && - check_vertex_pivot_symmetry(sculpt_vertex_co_get(ss, i), ss->pivot_pos, symm)) { - total++; - add_v3_v3(avg, sculpt_vertex_co_get(ss, i)); + for (int n = 0; n < totnode; n++) { + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) + { + const float mask = (vd.mask) ? *vd.mask : 0.0f; + if (mask < (0.5f + threshold) && mask > (0.5f - threshold)) { + if (check_vertex_pivot_symmetry(vd.co, ss->pivot_pos, symm)) { + add_v3_v3(avg, vd.co); + total++; + } + } + } + BKE_pbvh_vertex_iter_end; } } + if (total > 0) { mul_v3_fl(avg, 1.0f / total); copy_v3_v3(ss->pivot_pos, avg); } - } - /* Pivot to active vertex */ - if (mode == SCULPT_PIVOT_POSITION_ACTIVE_VERTEX) { - copy_v3_v3(ss->pivot_pos, sculpt_vertex_co_get(ss, sculpt_active_vertex_get(ss))); - } - - /* Pivot to raycast surface */ - if (mode == SCULPT_PIVOT_POSITION_CURSOR_SURFACE) { - float stroke_location[3]; - float mouse[2]; - mouse[0] = event->mval[0]; - mouse[1] = event->mval[1]; - if (sculpt_stroke_get_location(C, stroke_location, mouse)) { - copy_v3_v3(ss->pivot_pos, stroke_location); - } + MEM_SAFE_FREE(nodes); } ED_region_tag_redraw(ar); diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index b814e87fcb0..93e4a777569 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -44,6 +44,13 @@ bool sculpt_mode_poll_view3d(struct bContext *C); bool sculpt_poll(struct bContext *C); bool sculpt_poll_view3d(struct bContext *C); +/* Updates */ + +typedef enum SculptUpdateType { + SCULPT_UPDATE_COORDS = 1 << 0, + SCULPT_UPDATE_MASK = 1 << 1, +} SculptUpdateType; + /* Stroke */ typedef struct SculptCursorGeometryInfo { @@ -58,9 +65,17 @@ bool sculpt_cursor_geometry_info_update(bContext *C, const float mouse[2], bool use_sampled_normal); void sculpt_geometry_preview_lines_update(bContext *C, struct SculptSession *ss, float radius); +void sculpt_pose_calc_pose_data(struct Sculpt *sd, + struct Object *ob, + struct SculptSession *ss, + float initial_location[3], + float radius, + float pose_offset, + float *r_pose_origin, + float *r_pose_factor); /* Sculpt PBVH abstraction API */ -float *sculpt_vertex_co_get(struct SculptSession *ss, int index); +const float *sculpt_vertex_co_get(struct SculptSession *ss, int index); /* Dynamic topology */ void sculpt_pbvh_clear(Object *ob); @@ -179,23 +194,20 @@ typedef struct SculptThreadedTaskData { int filter_type; float filter_strength; - int *node_mask; - /* 0=towards view, 1=flipped */ - float (*area_cos)[3]; - float (*area_nos)[3]; - int *count; + bool use_area_cos; + bool use_area_nos; bool any_vertex_sampled; float *prev_mask; float *pose_origin; float *pose_initial_co; + float *pose_factor; float (*transform_rot)[4], (*transform_trans)[4], (*transform_trans_inv)[4]; float max_distance_squared; float nearest_vertex_search_co[3]; - int nearest_vertex_index; int mask_expand_update_it; bool mask_expand_invert_mask; @@ -204,6 +216,10 @@ typedef struct SculptThreadedTaskData { float transform_mats[8][4][4]; + float dirty_mask_min; + float dirty_mask_max; + bool dirty_mask_dirty_only; + ThreadMutex mutex; } SculptThreadedTaskData; @@ -233,6 +249,7 @@ typedef struct { float radius_squared; float *center; bool original; + bool ignore_fully_masked; } SculptSearchSphereData; typedef struct { @@ -240,6 +257,7 @@ typedef struct { struct SculptSession *ss; float radius_squared; bool original; + bool ignore_fully_masked; struct DistRayAABB_Precalc *dist_ray_to_aabb_precalc; } SculptSearchCircleData; @@ -392,6 +410,7 @@ typedef struct FilterCache { int mask_update_last_it; int *mask_update_it; float *normal_factor; + float *edge_factor; float *prev_mask; float mask_expand_initial_co[3]; } FilterCache; @@ -413,6 +432,4 @@ void sculpt_update_object_bounding_box(struct Object *ob); bool sculpt_get_redraw_rect(struct ARegion *ar, struct RegionView3D *rv3d, Object *ob, rcti *rect); -#define SCULPT_THREADED_LIMIT 4 - #endif diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index 3783eb17562..5d95cc80280 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -39,8 +39,6 @@ #include "DNA_scene_types.h" #include "DNA_mesh_types.h" #include "DNA_screen_types.h" -#include "DNA_space_types.h" -#include "DNA_workspace_types.h" #include "BKE_ccg.h" #include "BKE_context.h" @@ -49,7 +47,6 @@ #include "BKE_paint.h" #include "BKE_key.h" #include "BKE_mesh.h" -#include "BKE_mesh_runtime.h" #include "BKE_scene.h" #include "BKE_subsurf.h" #include "BKE_subdiv_ccg.h" @@ -62,13 +59,11 @@ #include "WM_api.h" #include "WM_types.h" -#include "ED_paint.h" #include "ED_object.h" #include "ED_sculpt.h" #include "ED_undo.h" #include "bmesh.h" -#include "paint_intern.h" #include "sculpt_intern.h" typedef struct UndoSculpt { @@ -91,6 +86,7 @@ static void update_cb(PBVHNode *node, void *rebuild) struct PartialUpdateData { PBVH *pbvh; bool rebuild; + char *modified_grids; }; /** @@ -99,8 +95,24 @@ struct PartialUpdateData { static void update_cb_partial(PBVHNode *node, void *userdata) { struct PartialUpdateData *data = userdata; - if (BKE_pbvh_node_vert_update_check_any(data->pbvh, node)) { - update_cb(node, &(data->rebuild)); + if (BKE_pbvh_type(data->pbvh) == PBVH_GRIDS) { + int *node_grid_indices; + int totgrid; + bool update = false; + BKE_pbvh_node_get_grids(data->pbvh, node, &node_grid_indices, &totgrid, NULL, NULL, NULL); + for (int i = 0; i < totgrid; i++) { + if (data->modified_grids[node_grid_indices[i]] == 1) { + update = true; + } + } + if (update) { + update_cb(node, &(data->rebuild)); + } + } + else { + if (BKE_pbvh_node_vert_update_check_any(data->pbvh, node)) { + update_cb(node, &(data->rebuild)); + } } } @@ -140,7 +152,7 @@ static bool sculpt_undo_restore_coords(bContext *C, Depsgraph *depsgraph, Sculpt if (unode->maxvert) { /* regular mesh restore */ - if (ss->kb && !STREQ(ss->kb->name, unode->shapeName)) { + if (ss->shapekey_active && !STREQ(ss->shapekey_active->name, unode->shapeName)) { /* shape key has been changed before calling undo operator */ Key *key = BKE_key_from_object(ob); @@ -162,12 +174,12 @@ static bool sculpt_undo_restore_coords(bContext *C, Depsgraph *depsgraph, Sculpt index = unode->index; mvert = ss->mvert; - if (ss->kb) { + if (ss->shapekey_active) { float(*vertCos)[3]; - vertCos = BKE_keyblock_convert_to_vertcos(ob, ss->kb); + vertCos = BKE_keyblock_convert_to_vertcos(ob, ss->shapekey_active); if (unode->orig_co) { - if (ss->modifiers_active) { + if (ss->deform_modifiers_active) { for (int i = 0; i < unode->totvert; i++) { sculpt_undo_restore_deformed(ss, unode, i, index[i], vertCos[index[i]]); } @@ -185,36 +197,33 @@ static bool sculpt_undo_restore_coords(bContext *C, Depsgraph *depsgraph, Sculpt } /* propagate new coords to keyblock */ - sculpt_vertcos_to_key(ob, ss->kb, vertCos); + sculpt_vertcos_to_key(ob, ss->shapekey_active, vertCos); /* pbvh uses it's own mvert array, so coords should be */ /* propagated to pbvh here */ - BKE_pbvh_vert_coords_apply(ss->pbvh, vertCos, ss->kb->totelem); + BKE_pbvh_vert_coords_apply(ss->pbvh, vertCos, ss->shapekey_active->totelem); MEM_freeN(vertCos); } else { if (unode->orig_co) { - if (ss->modifiers_active) { + if (ss->deform_modifiers_active) { for (int i = 0; i < unode->totvert; i++) { - if (sculpt_undo_restore_deformed(ss, unode, i, index[i], mvert[index[i]].co)) { - mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE; - } + sculpt_undo_restore_deformed(ss, unode, i, index[i], mvert[index[i]].co); + mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE; } } else { for (int i = 0; i < unode->totvert; i++) { - if (test_swap_v3_v3(mvert[index[i]].co, unode->orig_co[i])) { - mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE; - } + swap_v3_v3(mvert[index[i]].co, unode->orig_co[i]); + mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE; } } } else { for (int i = 0; i < unode->totvert; i++) { - if (test_swap_v3_v3(mvert[index[i]].co, unode->co[i])) { - mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE; - } + swap_v3_v3(mvert[index[i]].co, unode->co[i]); + mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE; } } } @@ -351,10 +360,9 @@ static void sculpt_undo_bmesh_restore_generic(bContext *C, BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BKE_pbvh_parallel_range( 0, totnode, nodes, sculpt_undo_bmesh_restore_generic_task_cb, &settings); if (nodes) { @@ -491,7 +499,6 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase SculptUndoNode *unode; bool update = false, rebuild = false; bool need_mask = false; - bool partial_update = true; for (unode = lb->first; unode; unode = unode->next) { /* restore pivot */ @@ -533,6 +540,9 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase return; } + char *undo_modified_grids = NULL; + bool use_multires_undo = false; + for (unode = lb->first; unode; unode = unode->next) { if (!STREQ(unode->idname, ob->id.name)) { @@ -552,8 +562,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase continue; } - /* multi-res can't do partial updates since it doesn't flag edited vertices */ - partial_update = false; + use_multires_undo = true; } switch (unode->type) { @@ -583,21 +592,29 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase } } + if (use_multires_undo) { + int max_grid; + unode = lb->first; + max_grid = unode->maxgrid; + undo_modified_grids = MEM_callocN(sizeof(char) * max_grid, "undo_grids"); + for (unode = lb->first; unode; unode = unode->next) { + for (int i = 0; i < unode->totgrid; i++) { + undo_modified_grids[unode->grids[i]] = 1; + } + } + } + if (update || rebuild) { bool tag_update = false; /* we update all nodes still, should be more clever, but also * needs to work correct when exiting/entering sculpt mode and * the nodes get recreated, though in that case it could do all */ - if (partial_update) { - struct PartialUpdateData data = { - .rebuild = rebuild, - .pbvh = ss->pbvh, - }; - BKE_pbvh_search_callback(ss->pbvh, NULL, NULL, update_cb_partial, &data); - } - else { - BKE_pbvh_search_callback(ss->pbvh, NULL, NULL, update_cb, &rebuild); - } + struct PartialUpdateData data = { + .rebuild = rebuild, + .pbvh = ss->pbvh, + .modified_grids = undo_modified_grids, + }; + BKE_pbvh_search_callback(ss->pbvh, NULL, NULL, update_cb_partial, &data); BKE_pbvh_update_bounds(ss->pbvh, PBVH_UpdateBB | PBVH_UpdateOriginalBB | PBVH_UpdateRedraw); if (BKE_sculpt_multires_active(scene, ob)) { @@ -611,7 +628,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase tag_update |= ((Mesh *)ob->data)->id.us > 1 || !BKE_sculptsession_use_pbvh_draw(ob, v3d); - if (ss->kb || ss->modifiers_active) { + if (ss->shapekey_active || ss->deform_modifiers_active) { Mesh *mesh = ob->data; BKE_mesh_calc_normals(mesh); @@ -626,6 +643,8 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase sculpt_update_object_bounding_box(ob); } } + + MEM_SAFE_FREE(undo_modified_grids); } static void sculpt_undo_free_list(ListBase *lb) @@ -812,7 +831,7 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node, Sculpt unode->index = MEM_mapallocN(sizeof(int) * allvert, "SculptUndoNode.index"); } - if (ss->modifiers_active) { + if (ss->deform_modifiers_active) { unode->orig_co = MEM_callocN(allvert * sizeof(*unode->orig_co), "undoSculpt orig_cos"); } @@ -834,7 +853,7 @@ static void sculpt_undo_store_coords(Object *ob, SculptUndoNode *unode) normal_float_to_short_v3(unode->no[vd.i], vd.fno); } - if (ss->modifiers_active) { + if (ss->deform_modifiers_active) { copy_v3_v3(unode->orig_co[vd.i], ss->orig_cos[unode->index[vd.i]]); } } @@ -1063,8 +1082,8 @@ SculptUndoNode *sculpt_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType copy_v3_v3(unode->pivot_rot, ss->pivot_rot); /* store active shape key */ - if (ss->kb) { - BLI_strncpy(unode->shapeName, ss->kb->name, sizeof(ss->kb->name)); + if (ss->shapekey_active) { + BLI_strncpy(unode->shapeName, ss->shapekey_active->name, sizeof(ss->shapekey_active->name)); } else { unode->shapeName[0] = '\0'; diff --git a/source/blender/editors/sculpt_paint/sculpt_uv.c b/source/blender/editors/sculpt_paint/sculpt_uv.c index de03fea7bb1..8fbaf3396bd 100644 --- a/source/blender/editors/sculpt_paint/sculpt_uv.c +++ b/source/blender/editors/sculpt_paint/sculpt_uv.c @@ -38,7 +38,6 @@ #include "BKE_context.h" #include "BKE_customdata.h" #include "BKE_editmesh.h" -#include "BKE_main.h" #include "BKE_mesh_mapping.h" #include "BKE_paint.h" diff --git a/source/blender/editors/sound/sound_ops.c b/source/blender/editors/sound/sound_ops.c index 4e710d31cbb..69745663c02 100644 --- a/source/blender/editors/sound/sound_ops.c +++ b/source/blender/editors/sound/sound_ops.c @@ -273,8 +273,11 @@ static void sound_update_animation_flags(Scene *scene) static int sound_update_animation_flags_exec(bContext *C, wmOperator *UNUSED(op)) { + Scene *scene = CTX_data_scene(C); + BKE_main_id_tag_idcode(CTX_data_main(C), ID_SCE, LIB_TAG_DOIT, false); sound_update_animation_flags(CTX_data_scene(C)); + DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c index 944a0c74f4c..242acfd0261 100644 --- a/source/blender/editors/space_action/action_edit.c +++ b/source/blender/editors/space_action/action_edit.c @@ -1663,7 +1663,7 @@ static const EnumPropertyItem prop_actkeys_snap_types[] = { {ACTKEYS_SNAP_CFRA, "CFRA", 0, - "Current frame", + "Current Frame", "Snap selected keyframes to the current frame"}, {ACTKEYS_SNAP_NEAREST_FRAME, "NEAREST_FRAME", @@ -1789,17 +1789,17 @@ static const EnumPropertyItem prop_actkeys_mirror_types[] = { {ACTKEYS_MIRROR_CFRA, "CFRA", 0, - "By Times over Current frame", + "By Times Over Current Frame", "Flip times of selected keyframes using the current frame as the mirror line"}, {ACTKEYS_MIRROR_XAXIS, "XAXIS", 0, - "By Values over Value=0", + "By Values Over Value=0", "Flip values of selected keyframes (i.e. negative values become positive, and vice versa)"}, {ACTKEYS_MIRROR_MARKER, "MARKER", 0, - "By Times over First Selected Marker", + "By Times Over First Selected Marker", "Flip times of selected keyframes using the first selected marker as the reference point"}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c index 912cd0407e3..ca6efb5f69e 100644 --- a/source/blender/editors/space_action/action_select.c +++ b/source/blender/editors/space_action/action_select.c @@ -153,7 +153,8 @@ static void actkeys_find_key_in_list_element(bAnimContext *ac, float region_x, float *r_selx, float *r_frame, - bool *r_found) + bool *r_found, + bool *r_is_selected) { *r_found = false; @@ -182,6 +183,7 @@ static void actkeys_find_key_in_list_element(bAnimContext *ac, *r_selx = BKE_nla_tweakedit_remap(adt, ak->cfra, NLATIME_CONVERT_UNMAP); *r_frame = ak->cfra; *r_found = true; + *r_is_selected = (ak->sel & SELECT) != 0; break; } } @@ -197,14 +199,16 @@ static void actkeys_find_key_at_position(bAnimContext *ac, bAnimListElem **r_ale, float *r_selx, float *r_frame, - bool *r_found) + bool *r_found, + bool *r_is_selected) { *r_found = false; *r_ale = actkeys_find_list_element_at_position(ac, filter, region_x, region_y); if (*r_ale != NULL) { - actkeys_find_key_in_list_element(ac, *r_ale, region_x, r_selx, r_frame, r_found); + actkeys_find_key_in_list_element( + ac, *r_ale, region_x, r_selx, r_frame, r_found, r_is_selected); } } @@ -213,9 +217,11 @@ static bool actkeys_is_key_at_position(bAnimContext *ac, float region_x, float r bAnimListElem *ale; float selx, frame; bool found; + bool is_selected; int filter = ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS; - actkeys_find_key_at_position(ac, filter, region_x, region_y, &ale, &selx, &frame, &found); + actkeys_find_key_at_position( + ac, filter, region_x, region_y, &ale, &selx, &frame, &found, &is_selected); if (ale != NULL) { MEM_freeN(ale); @@ -339,7 +345,9 @@ static int actkeys_deselectall_exec(bContext *C, wmOperator *op) /* set notifier that keyframe selection have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL); - + if (ac.datatype == ANIMCONT_GPENCIL) { + WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL); + } return OPERATOR_FINISHED; } @@ -572,7 +580,9 @@ static int actkeys_box_select_exec(bContext *C, wmOperator *op) /* set notifier that keyframe selection have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL); - + if (ac.datatype == ANIMCONT_GPENCIL) { + WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL); + } return OPERATOR_FINISHED; } @@ -805,7 +815,9 @@ static int actkeys_lassoselect_exec(bContext *C, wmOperator *op) /* send notifier that keyframe selection has changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL); - + if (ac.datatype == ANIMCONT_GPENCIL) { + WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL); + } return OPERATOR_FINISHED; } @@ -871,7 +883,9 @@ static int action_circle_select_exec(bContext *C, wmOperator *op) /* send notifier that keyframe selection has changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL); - + if (ac.datatype == ANIMCONT_GPENCIL) { + WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL); + } return OPERATOR_FINISHED; } @@ -1099,7 +1113,9 @@ static int actkeys_columnselect_exec(bContext *C, wmOperator *op) /* set notifier that keyframe selection have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL); - + if (ac.datatype == ANIMCONT_GPENCIL) { + WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL); + } return OPERATOR_FINISHED; } @@ -1159,7 +1175,9 @@ static int actkeys_select_linked_exec(bContext *C, wmOperator *UNUSED(op)) /* set notifier that keyframe selection has changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL); - + if (ac.datatype == ANIMCONT_GPENCIL) { + WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL); + } return OPERATOR_FINISHED; } @@ -1243,7 +1261,9 @@ static int actkeys_select_more_exec(bContext *C, wmOperator *UNUSED(op)) /* set notifier that keyframe selection has changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL); - + if (ac.datatype == ANIMCONT_GPENCIL) { + WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL); + } return OPERATOR_FINISHED; } @@ -1278,7 +1298,9 @@ static int actkeys_select_less_exec(bContext *C, wmOperator *UNUSED(op)) /* set notifier that keyframe selection has changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL); - + if (ac.datatype == ANIMCONT_GPENCIL) { + WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL); + } return OPERATOR_FINISHED; } @@ -1665,20 +1687,29 @@ static void actkeys_mselect_channel_only(bAnimContext *ac, bAnimListElem *ale, s /* ------------------- */ -static void mouse_action_keys(bAnimContext *ac, - const int mval[2], - short select_mode, - const bool deselect_all, - const bool column, - const bool same_channel) +static int mouse_action_keys(bAnimContext *ac, + const int mval[2], + short select_mode, + const bool deselect_all, + const bool column, + const bool same_channel, + bool wait_to_deselect_others) { int filter = ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS; bAnimListElem *ale = NULL; bool found = false; + bool is_selected = false; float frame = 0.0f; /* frame of keyframe under mouse - NLA corrections not applied/included */ float selx = 0.0f; /* frame of keyframe under mouse */ - actkeys_find_key_at_position(ac, filter, mval[0], mval[1], &ale, &selx, &frame, &found); + int ret_value = OPERATOR_FINISHED; + + actkeys_find_key_at_position( + ac, filter, mval[0], mval[1], &ale, &selx, &frame, &found, &is_selected); + + if (select_mode != SELECT_REPLACE) { + wait_to_deselect_others = false; + } /* For replacing selection, if we have something to select, we have to clear existing selection. * The same goes if we found nothing to select, and deselect_all is true @@ -1687,56 +1718,61 @@ static void mouse_action_keys(bAnimContext *ac, /* reset selection mode for next steps */ select_mode = SELECT_ADD; - /* deselect all keyframes */ - deselect_action_keys(ac, 0, SELECT_SUBTRACT); - - /* highlight channel clicked on */ - if (ELEM(ac->datatype, ANIMCONT_ACTION, ANIMCONT_DOPESHEET, ANIMCONT_TIMELINE)) { - /* deselect all other channels first */ - ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR); - - /* Highlight Action-Group or F-Curve? */ - if (ale != NULL && ale->data) { - if (ale->type == ANIMTYPE_GROUP) { - bActionGroup *agrp = ale->data; - - agrp->flag |= AGRP_SELECTED; - ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, agrp, ANIMTYPE_GROUP); - } - else if (ELEM(ale->type, ANIMTYPE_FCURVE, ANIMTYPE_NLACURVE)) { - FCurve *fcu = ale->data; - - fcu->flag |= FCURVE_SELECTED; - ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, fcu, ale->type); + if (wait_to_deselect_others && is_selected) { + ret_value = OPERATOR_RUNNING_MODAL; + } + else { + /* deselect all keyframes */ + deselect_action_keys(ac, 0, SELECT_SUBTRACT); + + /* highlight channel clicked on */ + if (ELEM(ac->datatype, ANIMCONT_ACTION, ANIMCONT_DOPESHEET, ANIMCONT_TIMELINE)) { + /* deselect all other channels first */ + ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR); + + /* Highlight Action-Group or F-Curve? */ + if (ale != NULL && ale->data) { + if (ale->type == ANIMTYPE_GROUP) { + bActionGroup *agrp = ale->data; + + agrp->flag |= AGRP_SELECTED; + ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, agrp, ANIMTYPE_GROUP); + } + else if (ELEM(ale->type, ANIMTYPE_FCURVE, ANIMTYPE_NLACURVE)) { + FCurve *fcu = ale->data; + + fcu->flag |= FCURVE_SELECTED; + ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, fcu, ale->type); + } } } - } - else if (ac->datatype == ANIMCONT_GPENCIL) { - /* deselect all other channels first */ - ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR); - - /* Highlight GPencil Layer */ - if (ale != NULL && ale->data != NULL && ale->type == ANIMTYPE_GPLAYER) { - bGPdata *gpd = (bGPdata *)ale->id; - bGPDlayer *gpl = ale->data; - - gpl->flag |= GP_LAYER_SELECT; - /* Update other layer status. */ - if (BKE_gpencil_layer_getactive(gpd) != gpl) { - BKE_gpencil_layer_setactive(gpd, gpl); - BKE_gpencil_layer_autolock_set(gpd, false); - WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + else if (ac->datatype == ANIMCONT_GPENCIL) { + /* deselect all other channels first */ + ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR); + + /* Highlight GPencil Layer */ + if (ale != NULL && ale->data != NULL && ale->type == ANIMTYPE_GPLAYER) { + bGPdata *gpd = (bGPdata *)ale->id; + bGPDlayer *gpl = ale->data; + + gpl->flag |= GP_LAYER_SELECT; + /* Update other layer status. */ + if (BKE_gpencil_layer_getactive(gpd) != gpl) { + BKE_gpencil_layer_setactive(gpd, gpl); + BKE_gpencil_layer_autolock_set(gpd, false); + WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + } } } - } - else if (ac->datatype == ANIMCONT_MASK) { - /* deselect all other channels first */ - ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR); + else if (ac->datatype == ANIMCONT_MASK) { + /* deselect all other channels first */ + ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR); - if (ale != NULL && ale->data != NULL && ale->type == ANIMTYPE_MASKLAYER) { - MaskLayer *masklay = ale->data; + if (ale != NULL && ale->data != NULL && ale->type == ANIMTYPE_MASKLAYER) { + MaskLayer *masklay = ale->data; - masklay->flag |= MASK_LAYERFLAG_SELECT; + masklay->flag |= MASK_LAYERFLAG_SELECT; + } } } } @@ -1771,12 +1807,15 @@ static void mouse_action_keys(bAnimContext *ac, /* free this channel */ MEM_freeN(ale); } + + return ret_value; } /* handle clicking */ -static int actkeys_clickselect_invoke(bContext *C, wmOperator *op, const wmEvent *event) +static int actkeys_clickselect_exec(bContext *C, wmOperator *op) { bAnimContext ac; + int ret_value; /* get editor data */ if (ANIM_animdata_get_context(C, &ac) == 0) { @@ -1789,20 +1828,26 @@ static int actkeys_clickselect_invoke(bContext *C, wmOperator *op, const wmEvent /* select mode is either replace (deselect all, then add) or add/extend */ const short selectmode = RNA_boolean_get(op->ptr, "extend") ? SELECT_INVERT : SELECT_REPLACE; const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all"); + const bool wait_to_deselect_others = RNA_boolean_get(op->ptr, "wait_to_deselect_others"); + int mval[2]; /* column selection */ const bool column = RNA_boolean_get(op->ptr, "column"); const bool channel = RNA_boolean_get(op->ptr, "channel"); + mval[0] = RNA_int_get(op->ptr, "mouse_x"); + mval[1] = RNA_int_get(op->ptr, "mouse_y"); + /* select keyframe(s) based upon mouse position*/ - mouse_action_keys(&ac, event->mval, selectmode, deselect_all, column, channel); + ret_value = mouse_action_keys( + &ac, mval, selectmode, deselect_all, column, channel, wait_to_deselect_others); /* set notifier that keyframe selection (and channels too) have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL); WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL); /* for tweak grab to work */ - return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH; + return ret_value | OPERATOR_PASS_THROUGH; } void ACTION_OT_clickselect(wmOperatorType *ot) @@ -1815,13 +1860,16 @@ void ACTION_OT_clickselect(wmOperatorType *ot) ot->description = "Select keyframes by clicking on them"; /* callbacks */ - ot->invoke = actkeys_clickselect_invoke; ot->poll = ED_operator_action_active; + ot->exec = actkeys_clickselect_exec; + ot->invoke = WM_generic_select_invoke; + ot->modal = WM_generic_select_modal; /* flags */ ot->flag = OPTYPE_UNDO; /* properties */ + WM_operator_properties_generic_select(ot); prop = RNA_def_boolean( ot->srna, "extend", diff --git a/source/blender/editors/space_clip/clip_draw.c b/source/blender/editors/space_clip/clip_draw.c index e96dc91cedb..80b58954c8f 100644 --- a/source/blender/editors/space_clip/clip_draw.c +++ b/source/blender/editors/space_clip/clip_draw.c @@ -161,7 +161,7 @@ static void draw_movieclip_cache(SpaceClip *sc, ARegion *ar, MovieClip *clip, Sc /* cache background */ ED_region_cache_draw_background(ar); - /* cached segments -- could be usefu lto debug caching strategies */ + /* cached segments -- could be useful to debug caching strategies */ BKE_movieclip_get_cache_segments(clip, &sc->user, &totseg, &points); ED_region_cache_draw_cached_segments(ar, totseg, points, sfra, efra); @@ -404,177 +404,185 @@ static void draw_stabilization_border( } } -static void draw_track_path(SpaceClip *sc, MovieClip *UNUSED(clip), MovieTrackingTrack *track) -{ -#define MAX_STATIC_PATH 64 - int count = sc->path_length; - int i, a, b, curindex = -1; - float path_static[(MAX_STATIC_PATH + 1) * 2][2]; - float(*path)[2]; - int tiny = sc->flag & SC_SHOW_TINY_MARKER, framenr, start_frame; - MovieTrackingMarker *marker; +enum { + PATH_POINT_FLAG_KEYFRAME = (1 << 0), +}; - if (count == 0) { - return; - } +typedef struct TrachPathPoint { + float co[2]; + uchar flag; +} TrackPathPoint; - start_frame = framenr = ED_space_clip_get_clip_frame_number(sc); - - marker = BKE_tracking_marker_get(track, framenr); - if (marker->framenr != framenr || marker->flag & MARKER_DISABLED) { - return; - } - - if (count < MAX_STATIC_PATH) { - path = path_static; - } - else { - path = MEM_mallocN(sizeof(*path) * (count + 1) * 2, "path"); +static void marker_to_path_point(SpaceClip *sc, + const MovieTrackingTrack *track, + const MovieTrackingMarker *marker, + TrackPathPoint *point) +{ + add_v2_v2v2(point->co, marker->pos, track->offset); + ED_clip_point_undistorted_pos(sc, point->co, point->co); + point->flag = 0; + if ((marker->flag & MARKER_TRACKED) == 0) { + point->flag |= PATH_POINT_FLAG_KEYFRAME; } +} - a = count; - i = framenr - 1; - while (i >= framenr - count) { - marker = BKE_tracking_marker_get(track, i); - - if (!marker || marker->flag & MARKER_DISABLED) { +static int track_to_path_segment(SpaceClip *sc, + MovieTrackingTrack *track, + int direction, + TrackPathPoint *path) +{ + const int count = sc->path_length; + int current_frame = ED_space_clip_get_clip_frame_number(sc); + const MovieTrackingMarker *marker = BKE_tracking_marker_get_exact(track, current_frame); + /* Check whether there is marker at exact current frame. + * If not, we don't have anything to be put to path. */ + if (marker == NULL || (marker->flag & MARKER_DISABLED)) { + return 0; + } + /* Index inside of path array where we write data to. */ + int point_index = count; + int path_length = 0; + for (int i = 0; i < count; ++i) { + marker_to_path_point(sc, track, marker, &path[point_index]); + /* Move to the next marker along the path segment. */ + path_length++; + point_index += direction; + current_frame += direction; + marker = BKE_tracking_marker_get_exact(track, current_frame); + if (marker == NULL || (marker->flag & MARKER_DISABLED)) { + /* Reached end of tracked segment. */ break; } + } + return path_length; +} - if (marker->framenr == i) { - add_v2_v2v2(path[--a], marker->pos, track->offset); - ED_clip_point_undistorted_pos(sc, path[a], path[a]); +static void draw_track_path_points(const TrackPathPoint *path, + uint position_attribute, + const int start_point, + const int num_points) +{ + if (num_points == 0) { + return; + } + immBegin(GPU_PRIM_POINTS, num_points); + for (int i = 0; i < num_points; i++) { + const TrackPathPoint *point = &path[i + start_point]; + immVertex2fv(position_attribute, point->co); + } + immEnd(); +} - if (marker->framenr == start_frame) { - curindex = a; - } - } - else { - break; +static void draw_track_path_keyframe_points(const TrackPathPoint *path, + uint position_attribute, + const int start_point, + const int num_points) +{ + immBeginAtMost(GPU_PRIM_POINTS, num_points); + for (int i = 0; i < num_points; i++) { + const TrackPathPoint *point = &path[i + start_point]; + if (point->flag & PATH_POINT_FLAG_KEYFRAME) { + immVertex2fv(position_attribute, point->co); } + } + immEnd(); +} - i--; +static void draw_track_path_lines(const TrackPathPoint *path, + uint position_attribute, + const int start_point, + const int num_points) +{ + if (num_points < 2) { + return; + } + immBegin(GPU_PRIM_LINE_STRIP, num_points); + for (int i = 0; i < num_points; i++) { + const TrackPathPoint *point = &path[i + start_point]; + immVertex2fv(position_attribute, point->co); } + immEnd(); +} - b = count; - i = framenr; - while (i <= framenr + count) { - marker = BKE_tracking_marker_get(track, i); +static void draw_track_path(SpaceClip *sc, MovieClip *UNUSED(clip), MovieTrackingTrack *track) +{ +#define MAX_STATIC_PATH 64 - if (!marker || marker->flag & MARKER_DISABLED) { - break; - } + const int count = sc->path_length; + TrackPathPoint path_static[(MAX_STATIC_PATH + 1) * 2]; + TrackPathPoint *path; + const bool tiny = (sc->flag & SC_SHOW_TINY_MARKER) != 0; - if (marker->framenr == i) { - if (marker->framenr == start_frame) { - curindex = b; - } + if (count == 0) { + /* Early output, nothing to bother about here. */ + return; + } - add_v2_v2v2(path[b++], marker->pos, track->offset); - ED_clip_point_undistorted_pos(sc, path[b - 1], path[b - 1]); - } - else { - break; - } + /* Try to use stack allocated memory when possibly, only use heap allocation + * for really long paths. */ + path = (count < MAX_STATIC_PATH) ? path_static : + MEM_mallocN(sizeof(*path) * (count + 1) * 2, "path"); + /* Collect path information. */ + const int num_points_before = track_to_path_segment(sc, track, -1, path); + const int num_points_after = track_to_path_segment(sc, track, 1, path); + if (num_points_before == 0 && num_points_after == 0) { + return; + } - i++; + int num_all_points = num_points_before + num_points_after; + /* If both leading and trailing parts of the path are there the center point is counted twice. */ + if (num_points_before != 0 && num_points_after != 0) { + num_all_points -= 1; } - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const int path_start_index = count - num_points_before + 1; + const int path_center_index = count; + const uint position_attribute = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + /* Draw path outline. */ if (!tiny) { immUniformThemeColor(TH_MARKER_OUTLINE); - if (TRACK_VIEW_SELECTED(sc, track)) { - if ((b - a - 1) >= 1) { - GPU_point_size(5.0f); - - immBegin(GPU_PRIM_POINTS, b - a - 1); - - for (i = a; i < b; i++) { - if (i != curindex) { - immVertex2f(pos, path[i][0], path[i][1]); - } - } - - immEnd(); - } - } - - if ((b - a) >= 2) { - GPU_line_width(3.0f); - - immBegin(GPU_PRIM_LINE_STRIP, b - a); - - for (i = a; i < b; i++) { - immVertex2f(pos, path[i][0], path[i][1]); - } - - immEnd(); + GPU_point_size(5.0f); + draw_track_path_points(path, position_attribute, path_start_index, num_all_points); + GPU_point_size(7.0f); + draw_track_path_keyframe_points(path, position_attribute, path_start_index, num_all_points); } + /* Draw darker outline for actual path, all line segments at once. */ + GPU_line_width(3.0f); + draw_track_path_lines(path, position_attribute, path_start_index, num_all_points); } - if (TRACK_VIEW_SELECTED(sc, track)) { - GPU_point_size(3.0f); - - if ((curindex - a) >= 1) { - immUniformThemeColor(TH_PATH_BEFORE); - - immBegin(GPU_PRIM_POINTS, curindex - a); - - for (i = a; i < curindex; i++) { - immVertex2f(pos, path[i][0], path[i][1]); - } - - immEnd(); - } - - if ((b - curindex - 1) >= 1) { - immUniformThemeColor(TH_PATH_AFTER); - - immBegin(GPU_PRIM_POINTS, b - curindex - 1); - - for (i = curindex + 1; i < b; i++) { - immVertex2f(pos, path[i][0], path[i][1]); - } - - immEnd(); - } - } + /* Draw all points. */ + GPU_point_size(3.0f); + immUniformThemeColor(TH_PATH_BEFORE); + draw_track_path_points(path, position_attribute, path_start_index, num_points_before); + immUniformThemeColor(TH_PATH_AFTER); + draw_track_path_points(path, position_attribute, path_center_index, num_points_after); + /* Connect points with color coded segments. */ GPU_line_width(1); + immUniformThemeColor(TH_PATH_BEFORE); + draw_track_path_lines(path, position_attribute, path_start_index, num_points_before); + immUniformThemeColor(TH_PATH_AFTER); + draw_track_path_lines(path, position_attribute, path_center_index, num_points_after); + + /* Draw all bigger points corresponding to keyframes. */ + GPU_point_size(5.0f); + immUniformThemeColor(TH_PATH_KEYFRAME_BEFORE); + draw_track_path_keyframe_points(path, position_attribute, path_start_index, num_points_before); + immUniformThemeColor(TH_PATH_KEYFRAME_AFTER); + draw_track_path_keyframe_points(path, position_attribute, path_center_index, num_points_after); - if ((curindex - a + 1) >= 2) { - immUniformThemeColor(TH_PATH_BEFORE); - - immBegin(GPU_PRIM_LINE_STRIP, curindex - a + 1); - - for (i = a; i <= curindex; i++) { - immVertex2f(pos, path[i][0], path[i][1]); - } - - immEnd(); - } - - if ((b - curindex) >= 2) { - immUniformThemeColor(TH_PATH_AFTER); - - immBegin(GPU_PRIM_LINE_STRIP, b - curindex); - - for (i = curindex; i < b; i++) { - immVertex2f(pos, path[i][0], path[i][1]); - } - - immEnd(); + if (path != path_static) { + MEM_freeN(path); } immUnbindProgram(); - if (path != path_static) { - MEM_freeN(path); - } #undef MAX_STATIC_PATH } diff --git a/source/blender/editors/space_clip/clip_intern.h b/source/blender/editors/space_clip/clip_intern.h index 1fae4c91a48..bd54d4f0016 100644 --- a/source/blender/editors/space_clip/clip_intern.h +++ b/source/blender/editors/space_clip/clip_intern.h @@ -94,6 +94,7 @@ void CLIP_OT_view_zoom_out(struct wmOperatorType *ot); void CLIP_OT_view_zoom_ratio(struct wmOperatorType *ot); void CLIP_OT_view_all(struct wmOperatorType *ot); void CLIP_OT_view_selected(struct wmOperatorType *ot); +void CLIP_OT_view_center_cursor(struct wmOperatorType *ot); void CLIP_OT_change_frame(wmOperatorType *ot); void CLIP_OT_rebuild_proxy(struct wmOperatorType *ot); void CLIP_OT_mode_set(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_clip/clip_ops.c b/source/blender/editors/space_clip/clip_ops.c index da534cf9b40..192449a219d 100644 --- a/source/blender/editors/space_clip/clip_ops.c +++ b/source/blender/editors/space_clip/clip_ops.c @@ -78,7 +78,9 @@ #include "clip_intern.h" // own include -/******************** view navigation utilities *********************/ +/* -------------------------------------------------------------------- */ +/** \name View Navigation Utilities + * \{ */ static void sclip_zoom_set(const bContext *C, float zoom, @@ -162,7 +164,11 @@ static void sclip_zoom_set_factor_exec(bContext *C, const wmEvent *event, float ED_region_tag_redraw(ar); } -/******************** open clip operator ********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Open Clip Operator + * \{ */ static void clip_filesel(bContext *C, wmOperator *op, const char *path) { @@ -326,7 +332,11 @@ void CLIP_OT_open(wmOperatorType *ot) FILE_SORT_ALPHA); } -/******************* reload clip operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Reload Clip Operator + * \{ */ static int reload_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -355,7 +365,11 @@ void CLIP_OT_reload(wmOperatorType *ot) ot->exec = reload_exec; } -/********************** view pan operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name View Pan Operator + * \{ */ typedef struct ViewPanData { float x, y; @@ -376,7 +390,7 @@ static void view_pan_init(bContext *C, wmOperator *op, const wmEvent *event) /* Grab will be set when running from gizmo. */ vpd->own_cursor = (win->grabcursor == 0); if (vpd->own_cursor) { - WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR); + WM_cursor_modal_set(win, WM_CURSOR_NSEW_SCROLL); } vpd->x = event->x; @@ -525,7 +539,11 @@ void CLIP_OT_view_pan(wmOperatorType *ot) FLT_MAX); } -/********************** view zoom operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name View Zoom Operator + * \{ */ typedef struct ViewZoomData { float x, y; @@ -549,7 +567,7 @@ static void view_zoom_init(bContext *C, wmOperator *op, const wmEvent *event) /* Grab will be set when running from gizmo. */ vpd->own_cursor = (win->grabcursor == 0); if (vpd->own_cursor) { - WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR); + WM_cursor_modal_set(win, WM_CURSOR_NSEW_SCROLL); } if (U.viewzoom == USER_ZOOM_CONT) { @@ -729,7 +747,11 @@ void CLIP_OT_view_zoom(wmOperatorType *ot) WM_operator_properties_use_cursor_init(ot); } -/********************** view zoom in/out operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name View Zoom In/Out Operator + * \{ */ static int view_zoom_in_exec(bContext *C, wmOperator *op) { @@ -845,7 +867,11 @@ void CLIP_OT_view_zoom_out(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_HIDDEN); } -/********************** view zoom ratio operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name View Zoom Ratio Operator + * \{ */ static int view_zoom_ratio_exec(bContext *C, wmOperator *op) { @@ -887,8 +913,11 @@ void CLIP_OT_view_zoom_ratio(wmOperatorType *ot) -FLT_MAX, FLT_MAX); } +/** \} */ -/********************** view all operator *********************/ +/* -------------------------------------------------------------------- */ +/** \name View All Operator + * \{ */ static int view_all_exec(bContext *C, wmOperator *op) { @@ -961,8 +990,41 @@ void CLIP_OT_view_all(wmOperatorType *ot) prop = RNA_def_boolean(ot->srna, "fit_view", 0, "Fit View", "Fit frame to the viewport"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Center View To Cursor Operator + * \{ */ + +static int view_center_cursor_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceClip *sc = CTX_wm_space_clip(C); + ARegion *ar = CTX_wm_region(C); + + clip_view_center_to_point(sc, sc->cursor[0], sc->cursor[1]); -/********************** view selected operator *********************/ + ED_region_tag_redraw(ar); + + return OPERATOR_FINISHED; +} + +void CLIP_OT_view_center_cursor(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Center View to Cursor"; + ot->description = "Center the view so that the cursor is in the middle of the view"; + ot->idname = "CLIP_OT_view_center_cursor"; + + /* api callbacks */ + ot->exec = view_center_cursor_exec; + ot->poll = ED_space_clip_maskedit_poll; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name View Selected Operator + * \{ */ static int view_selected_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -992,8 +1054,11 @@ void CLIP_OT_view_selected(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_LOCK_BYPASS; } +/** \} */ -/********************** change frame operator *********************/ +/* -------------------------------------------------------------------- */ +/** \name Change Frame Operator + * \{ */ static bool change_frame_poll(bContext *C) { @@ -1109,8 +1174,11 @@ void CLIP_OT_change_frame(wmOperatorType *ot) /* rna */ RNA_def_int(ot->srna, "frame", 0, MINAFRAME, MAXFRAME, "Frame", "", MINAFRAME, MAXFRAME); } +/** \} */ -/********************** rebuild proxies operator *********************/ +/* -------------------------------------------------------------------- */ +/** \name Rebuild Proxies Operator + * \{ */ typedef struct ProxyBuildJob { Scene *scene; @@ -1521,8 +1589,11 @@ void CLIP_OT_rebuild_proxy(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER; } +/** \} */ -/********************** mode set operator *********************/ +/* -------------------------------------------------------------------- */ +/** \name Mode Set Operator + * \{ */ static int mode_set_exec(bContext *C, wmOperator *op) { @@ -1558,7 +1629,12 @@ void CLIP_OT_mode_set(wmOperatorType *ot) } #ifdef WITH_INPUT_NDOF -/********************** NDOF operator *********************/ + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name NDOF Operator + * \{ */ /* Combined pan/zoom from a 3D mouse device. * Z zooms, XY pans @@ -1608,9 +1684,14 @@ void CLIP_OT_view_ndof(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_LOCK_BYPASS; } + +/** \} */ + #endif /* WITH_INPUT_NDOF */ -/********************** Prefetch operator *********************/ +/* -------------------------------------------------------------------- */ +/** \name Prefetch Operator + * \{ */ static int clip_prefetch_modal(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) { @@ -1650,8 +1731,11 @@ void CLIP_OT_prefetch(wmOperatorType *ot) ot->invoke = clip_prefetch_invoke; ot->modal = clip_prefetch_modal; } +/** \} */ -/********************** Set scene frames *********************/ +/* -------------------------------------------------------------------- */ +/** \name Set Scene Frames Operator + * \{ */ static int clip_set_scene_frames_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -1686,8 +1770,11 @@ void CLIP_OT_set_scene_frames(wmOperatorType *ot) ot->poll = ED_space_clip_view_clip_poll; ot->exec = clip_set_scene_frames_exec; } +/** \} */ -/******************** set 3d cursor operator ********************/ +/* -------------------------------------------------------------------- */ +/** \name Set 3d Cursor Operator + * \{ */ static int clip_set_2d_cursor_exec(bContext *C, wmOperator *op) { @@ -1748,7 +1835,11 @@ void CLIP_OT_cursor_set(wmOperatorType *ot) 10.0f); } -/********************** Toggle lock to selection operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Toggle Lock To Selection Operator + * \{ */ static int lock_selection_togglee_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -1773,7 +1864,11 @@ void CLIP_OT_lock_selection_toggle(wmOperatorType *ot) ot->flag = OPTYPE_LOCK_BYPASS; } -/********************** macros *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Macros + * \{ */ void ED_operatormacros_clip(void) { @@ -1797,3 +1892,5 @@ void ED_operatormacros_clip(void) otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); RNA_boolean_set(otmacro->ptr, "release_confirm", true); } + +/** \} */ diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c index 2964a95648a..16305a9b17b 100644 --- a/source/blender/editors/space_clip/space_clip.c +++ b/source/blender/editors/space_clip/space_clip.c @@ -443,6 +443,7 @@ static void clip_operatortypes(void) WM_operatortype_append(CLIP_OT_view_zoom_ratio); WM_operatortype_append(CLIP_OT_view_all); WM_operatortype_append(CLIP_OT_view_selected); + WM_operatortype_append(CLIP_OT_view_center_cursor); WM_operatortype_append(CLIP_OT_change_frame); WM_operatortype_append(CLIP_OT_rebuild_proxy); WM_operatortype_append(CLIP_OT_mode_set); diff --git a/source/blender/editors/space_clip/tracking_ops.c b/source/blender/editors/space_clip/tracking_ops.c index 68198acfe52..f1bce00ea0b 100644 --- a/source/blender/editors/space_clip/tracking_ops.c +++ b/source/blender/editors/space_clip/tracking_ops.c @@ -2090,7 +2090,7 @@ static int keyframe_insert_exec(bContext *C, wmOperator *UNUSED(op)) void CLIP_OT_keyframe_insert(wmOperatorType *ot) { /* identifiers */ - ot->name = "Insert keyframe"; + ot->name = "Insert Keyframe"; ot->description = "Insert a keyframe to selected tracks at current frame"; ot->idname = "CLIP_OT_keyframe_insert"; @@ -2113,7 +2113,7 @@ static int keyframe_delete_exec(bContext *C, wmOperator *UNUSED(op)) void CLIP_OT_keyframe_delete(wmOperatorType *ot) { /* identifiers */ - ot->name = "Delete keyframe"; + ot->name = "Delete Keyframe"; ot->description = "Delete a keyframe from selected tracks at current frame"; ot->idname = "CLIP_OT_keyframe_delete"; diff --git a/source/blender/editors/space_clip/tracking_ops_solve.c b/source/blender/editors/space_clip/tracking_ops_solve.c index 1d2fc239a89..7e2671382b9 100644 --- a/source/blender/editors/space_clip/tracking_ops_solve.c +++ b/source/blender/editors/space_clip/tracking_ops_solve.c @@ -118,7 +118,11 @@ static void solve_camera_freejob(void *scv) MovieClip *clip = scj->clip; int solved; - WM_set_locked_interface(scj->wm, false); + /* WindowManager is missing in the job when initialization is incomplete. + * In this case the interface is not locked either. */ + if (scj->wm != NULL) { + WM_set_locked_interface(scj->wm, false); + } if (!scj->context) { /* job weren't fully initialized due to some error */ diff --git a/source/blender/editors/space_clip/tracking_ops_utils.c b/source/blender/editors/space_clip/tracking_ops_utils.c index 3970f1381bf..7579c9a49c6 100644 --- a/source/blender/editors/space_clip/tracking_ops_utils.c +++ b/source/blender/editors/space_clip/tracking_ops_utils.c @@ -57,11 +57,11 @@ void clip_tracking_clear_invisible_track_selection(SpaceClip *sc, MovieClip *cli void clip_tracking_hide_cursor(bContext *C) { wmWindow *win = CTX_wm_window(C); - WM_cursor_set(win, CURSOR_NONE); + WM_cursor_set(win, WM_CURSOR_NONE); } void clip_tracking_show_cursor(bContext *C) { wmWindow *win = CTX_wm_window(C); - WM_cursor_set(win, CURSOR_STD); + WM_cursor_set(win, WM_CURSOR_DEFAULT); } diff --git a/source/blender/editors/space_console/space_console.c b/source/blender/editors/space_console/space_console.c index f5c02dbd724..5cc2f00413a 100644 --- a/source/blender/editors/space_console/space_console.c +++ b/source/blender/editors/space_console/space_console.c @@ -149,11 +149,11 @@ static void console_main_region_init(wmWindowManager *wm, ARegion *ar) static void console_cursor(wmWindow *win, ScrArea *sa, ARegion *ar) { SpaceText *st = sa->spacedata.first; - int wmcursor = BC_TEXTEDITCURSOR; + int wmcursor = WM_CURSOR_TEXT_EDIT; if (st->text && BLI_rcti_isect_pt(&st->txtbar, win->eventstate->x - ar->winrct.xmin, st->txtbar.ymin)) { - wmcursor = CURSOR_STD; + wmcursor = WM_CURSOR_DEFAULT; } WM_cursor_set(win, wmcursor); diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h index 4b86f38f8e4..b0ff67844d8 100644 --- a/source/blender/editors/space_file/file_intern.h +++ b/source/blender/editors/space_file/file_intern.h @@ -32,10 +32,6 @@ struct FileSelectParams; struct SpaceFile; struct View2D; -/* file_ops.c */ -struct ARegion *file_tools_region(struct ScrArea *sa); -struct ARegion *file_tool_props_region(struct ScrArea *sa); - /* file_draw.c */ #define TILE_BORDER_X (UI_UNIT_X / 4) #define TILE_BORDER_Y (UI_UNIT_Y / 4) @@ -86,7 +82,6 @@ void FILE_OT_directory_new(struct wmOperatorType *ot); void FILE_OT_previous(struct wmOperatorType *ot); void FILE_OT_next(struct wmOperatorType *ot); void FILE_OT_refresh(struct wmOperatorType *ot); -void FILE_OT_bookmark_toggle(struct wmOperatorType *ot); void FILE_OT_filenum(struct wmOperatorType *ot); void FILE_OT_delete(struct wmOperatorType *ot); void FILE_OT_rename(struct wmOperatorType *ot); @@ -112,6 +107,7 @@ void file_sfile_to_operator_ex(bContext *C, struct SpaceFile *sfile, char *filepath); void file_sfile_to_operator(bContext *C, struct wmOperator *op, struct SpaceFile *sfile); + void file_operator_to_sfile(bContext *C, struct SpaceFile *sfile, struct wmOperator *op); /* filesel.c */ diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index 127196cca74..3cdcc07f081 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -1506,14 +1506,11 @@ void file_draw_check_cb(bContext *C, void *UNUSED(arg1), void *UNUSED(arg2)) bool file_draw_check_exists(SpaceFile *sfile) { if (sfile->op) { /* fails on reload */ - PropertyRNA *prop; - if ((prop = RNA_struct_find_property(sfile->op->ptr, "check_existing"))) { - if (RNA_property_boolean_get(sfile->op->ptr, prop)) { - char filepath[FILE_MAX]; - BLI_join_dirfile(filepath, sizeof(filepath), sfile->params->dir, sfile->params->file); - if (BLI_is_file(filepath)) { - return true; - } + if (sfile->params && (sfile->params->flag & FILE_CHECK_EXISTING)) { + char filepath[FILE_MAX]; + BLI_join_dirfile(filepath, sizeof(filepath), sfile->params->dir, sfile->params->file); + if (BLI_is_file(filepath)) { + return true; } } } @@ -2152,6 +2149,10 @@ void file_directory_enter_handle(bContext *C, void *UNUSED(arg_unused), void *UN SpaceFile *sfile = CTX_wm_space_file(C); if (sfile->params) { + char old_dir[sizeof(sfile->params->dir)]; + + BLI_strncpy(old_dir, sfile->params->dir, sizeof(old_dir)); + file_expand_directory(C); /* special case, user may have pasted a filepath into the directory */ @@ -2185,8 +2186,10 @@ void file_directory_enter_handle(bContext *C, void *UNUSED(arg_unused), void *UN BLI_cleanup_dir(BKE_main_blendfile_path(bmain), sfile->params->dir); if (filelist_is_dir(sfile->files, sfile->params->dir)) { - /* if directory exists, enter it immediately */ - ED_file_change_dir(C); + if (!STREQ(sfile->params->dir, old_dir)) { /* Avoids flickering when nothing's changed. */ + /* if directory exists, enter it immediately */ + ED_file_change_dir(C); + } /* don't do for now because it selects entire text instead of * placing cursor at the end */ @@ -2318,78 +2321,6 @@ void FILE_OT_hidedot(struct wmOperatorType *ot) ot->poll = ED_operator_file_active; /* <- important, handler is on window level */ } -ARegion *file_tools_region(ScrArea *sa) -{ - ARegion *ar, *arnew; - - if ((ar = BKE_area_find_region_type(sa, RGN_TYPE_TOOLS)) != NULL) { - return ar; - } - - /* add subdiv level; after header */ - ar = BKE_area_find_region_type(sa, RGN_TYPE_HEADER); - - /* is error! */ - if (ar == NULL) { - return NULL; - } - - arnew = MEM_callocN(sizeof(ARegion), "tools for file"); - BLI_insertlinkafter(&sa->regionbase, ar, arnew); - arnew->regiontype = RGN_TYPE_TOOLS; - arnew->alignment = RGN_ALIGN_LEFT; - - return arnew; -} - -ARegion *file_tool_props_region(ScrArea *sa) -{ - ARegion *ar, *arnew; - - if ((ar = BKE_area_find_region_type(sa, RGN_TYPE_TOOL_PROPS)) != NULL) { - return ar; - } - - /* add subdiv level; after execute region */ - ar = BKE_area_find_region_type(sa, RGN_TYPE_EXECUTE); - - /* is error! */ - if (ar == NULL) { - return NULL; - } - - arnew = MEM_callocN(sizeof(ARegion), "tool props for file"); - BLI_insertlinkafter(&sa->regionbase, ar, arnew); - arnew->regiontype = RGN_TYPE_TOOL_PROPS; - arnew->alignment = RGN_ALIGN_RIGHT; - - return arnew; -} - -static int file_bookmark_toggle_exec(bContext *C, wmOperator *UNUSED(unused)) -{ - ScrArea *sa = CTX_wm_area(C); - ARegion *ar = file_tools_region(sa); - - if (ar) { - ED_region_toggle_hidden(C, ar); - } - - return OPERATOR_FINISHED; -} - -void FILE_OT_bookmark_toggle(struct wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Toggle Bookmarks"; - ot->description = "Toggle bookmarks display"; - ot->idname = "FILE_OT_bookmark_toggle"; - - /* api callbacks */ - ot->exec = file_bookmark_toggle_exec; - ot->poll = ED_operator_file_active; /* <- important, handler is on window level */ -} - static bool file_filenum_poll(bContext *C) { SpaceFile *sfile = CTX_wm_space_file(C); @@ -2398,7 +2329,7 @@ static bool file_filenum_poll(bContext *C) return false; } - return sfile->params && (sfile->params->action_type == FILE_SAVE); + return sfile->params && (sfile->params->flag & FILE_CHECK_EXISTING); } /** @@ -2464,60 +2395,49 @@ void FILE_OT_filenum(struct wmOperatorType *ot) RNA_def_int(ot->srna, "increment", 1, -100, 100, "Increment", "", -100, 100); } -static int file_rename_exec(bContext *C, wmOperator *UNUSED(op)) +static void file_rename_state_activate(SpaceFile *sfile, int file_idx, bool require_selected) { - ScrArea *sa = CTX_wm_area(C); - SpaceFile *sfile = (SpaceFile *)CTX_wm_space_data(C); + const int numfiles = filelist_files_ensure(sfile->files); - if (sfile->params) { - int idx = sfile->params->highlight_file; - int numfiles = filelist_files_ensure(sfile->files); - if ((0 <= idx) && (idx < numfiles)) { - FileDirEntry *file = filelist_file(sfile->files, idx); + if ((file_idx >= 0) && (file_idx < numfiles)) { + FileDirEntry *file = filelist_file(sfile->files, file_idx); + + if ((require_selected == false) || + (filelist_entry_select_get(sfile->files, file, CHECK_ALL) & FILE_SEL_SELECTED)) { filelist_entry_select_index_set( - sfile->files, idx, FILE_SEL_ADD, FILE_SEL_EDITING, CHECK_ALL); + sfile->files, file_idx, FILE_SEL_ADD, FILE_SEL_EDITING, CHECK_ALL); BLI_strncpy(sfile->params->renamefile, file->relpath, FILE_MAXFILE); /* We can skip the pending state, * as we can directly set FILE_SEL_EDITING on the expected entry here. */ sfile->params->rename_flag = FILE_PARAMS_RENAME_ACTIVE; } + } +} + +static int file_rename_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event)) +{ + ScrArea *sa = CTX_wm_area(C); + SpaceFile *sfile = (SpaceFile *)CTX_wm_space_data(C); + + if (sfile->params) { + file_rename_state_activate(sfile, sfile->params->active_file, true); ED_area_tag_redraw(sa); } return OPERATOR_FINISHED; } -static bool file_rename_poll(bContext *C) +static int file_rename_exec(bContext *C, wmOperator *UNUSED(op)) { - bool poll = ED_operator_file_active(C); - SpaceFile *sfile = CTX_wm_space_file(C); - - if (sfile && sfile->params) { - int idx = sfile->params->highlight_file; - int numfiles = filelist_files_ensure(sfile->files); - - if ((0 <= idx) && (idx < numfiles)) { - FileDirEntry *file = filelist_file(sfile->files, idx); - if (FILENAME_IS_CURRPAR(file->relpath)) { - poll = false; - } - } + ScrArea *sa = CTX_wm_area(C); + SpaceFile *sfile = (SpaceFile *)CTX_wm_space_data(C); - if (sfile->params->highlight_file < 0) { - poll = false; - } - else { - char dir[FILE_MAX_LIBEXTRA]; - if (filelist_islibrary(sfile->files, dir, NULL)) { - poll = false; - } - } - } - else { - poll = false; + if (sfile->params) { + file_rename_state_activate(sfile, sfile->params->highlight_file, false); + ED_area_tag_redraw(sa); } - return poll; + return OPERATOR_FINISHED; } void FILE_OT_rename(struct wmOperatorType *ot) @@ -2528,8 +2448,9 @@ void FILE_OT_rename(struct wmOperatorType *ot) ot->idname = "FILE_OT_rename"; /* api callbacks */ + ot->invoke = file_rename_invoke; ot->exec = file_rename_exec; - ot->poll = file_rename_poll; + ot->poll = ED_operator_file_active; } static bool file_delete_poll(bContext *C) @@ -2573,23 +2494,29 @@ int file_delete_exec(bContext *C, wmOperator *op) int numfiles = filelist_files_ensure(sfile->files); int i; + const char *error_message = NULL; bool report_error = false; errno = 0; for (i = 0; i < numfiles; i++) { if (filelist_entry_select_index_get(sfile->files, i, CHECK_FILES)) { file = filelist_file(sfile->files, i); BLI_make_file_string(BKE_main_blendfile_path(bmain), str, sfile->params->dir, file->relpath); - if (BLI_delete(str, false, false) != 0 || BLI_exists(str)) { + if (BLI_delete_soft(str, &error_message) != 0 || BLI_exists(str)) { report_error = true; } } } if (report_error) { - BKE_reportf(op->reports, - RPT_ERROR, - "Could not delete file: %s", - errno ? strerror(errno) : "unknown error"); + if (error_message != NULL) { + BKE_reportf(op->reports, RPT_ERROR, "Could not delete file or directory: %s", error_message); + } + else { + BKE_reportf(op->reports, + RPT_ERROR, + "Could not delete file or directory: %s", + errno ? strerror(errno) : "unknown error"); + } } ED_fileselect_clear(wm, sa, sfile); @@ -2602,7 +2529,7 @@ void FILE_OT_delete(struct wmOperatorType *ot) { /* identifiers */ ot->name = "Delete Selected Files"; - ot->description = "Delete selected files"; + ot->description = "Move selected files to the trash or recycle bin"; ot->idname = "FILE_OT_delete"; /* api callbacks */ diff --git a/source/blender/editors/space_file/file_panels.c b/source/blender/editors/space_file/file_panels.c index b0fed1fafd4..9ba098fcf45 100644 --- a/source/blender/editors/space_file/file_panels.c +++ b/source/blender/editors/space_file/file_panels.c @@ -110,18 +110,22 @@ void file_tool_props_region_panels_register(ARegionType *art) BLI_addtail(&art->paneltypes, pt); } -static void file_panel_execution_cancel_button(uiBlock *block) +static void file_panel_execution_cancel_button(uiLayout *layout) { - uiDefButO(block, - UI_BTYPE_BUT, - "FILE_OT_cancel", - WM_OP_EXEC_REGION_WIN, - IFACE_("Cancel"), - 0, - 0, - UI_UNIT_X, - UI_UNIT_Y, - ""); + uiLayout *row = uiLayoutRow(layout, false); + uiLayoutSetScaleX(row, 0.8f); + uiLayoutSetFixedSize(row, true); + uiItemO(row, IFACE_("Cancel"), ICON_NONE, "FILE_OT_cancel"); +} + +static void file_panel_execution_execute_button(uiLayout *layout, const char *title) +{ + uiLayout *row = uiLayoutRow(layout, false); + uiLayoutSetScaleX(row, 0.8f); + uiLayoutSetFixedSize(row, true); + /* Just a display hint. */ + uiLayoutSetActiveDefault(row, true); + uiItemO(row, title, ICON_NONE, "FILE_OT_execute"); } static void file_panel_execution_buttons_draw(const bContext *C, Panel *pa) @@ -145,7 +149,6 @@ static void file_panel_execution_buttons_draw(const bContext *C, Panel *pa) RNA_pointer_create(&screen->id, &RNA_FileSelectParams, params, ¶ms_rna_ptr); row = uiLayoutRow(pa->layout, false); - uiLayoutSetScaleX(row, 1.3f); uiLayoutSetScaleY(row, 1.3f); /* callbacks for operator check functions */ @@ -176,7 +179,7 @@ static void file_panel_execution_buttons_draw(const bContext *C, Panel *pa) * immediate ui_apply_but_func but only after button deactivates */ UI_but_funcN_set(but, file_filename_enter_handle, NULL, but); - if (params->action_type == FILE_SAVE) { + if (params->flag & FILE_CHECK_EXISTING) { but_extra_rna_ptr = UI_but_extra_operator_icon_add( but, "FILE_OT_filenum", WM_OP_EXEC_REGION_WIN, ICON_ADD); RNA_int_set(but_extra_rna_ptr, "increment", 1); @@ -192,23 +195,16 @@ static void file_panel_execution_buttons_draw(const bContext *C, Panel *pa) UI_block_func_set(block, NULL, NULL, NULL); { - if (windows_layout == false) { - file_panel_execution_cancel_button(block); - } - but = uiDefButO(block, - UI_BTYPE_BUT, - "FILE_OT_execute", - WM_OP_EXEC_REGION_WIN, - params->title, - 0, - 0, - UI_UNIT_X, - UI_UNIT_Y, - ""); - /* Just a display hint. */ - UI_but_flag_enable(but, UI_BUT_ACTIVE_DEFAULT); + uiLayout *sub = uiLayoutRow(row, false); + uiLayoutSetOperatorContext(sub, WM_OP_EXEC_REGION_WIN); + if (windows_layout) { - file_panel_execution_cancel_button(block); + file_panel_execution_execute_button(sub, params->title); + file_panel_execution_cancel_button(sub); + } + else { + file_panel_execution_cancel_button(sub); + file_panel_execution_execute_button(sub, params->title); } } } diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 27cccf6bab1..d29233618de 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -831,7 +831,7 @@ void filelist_setfilter_options(FileList *filelist, } if ((filelist->filter_data.filter != filter) || (filelist->filter_data.filter_id != filter_id)) { filelist->filter_data.filter = filter; - filelist->filter_data.filter_id = filter_id; + filelist->filter_data.filter_id = (filter & FILE_TYPE_BLENDERLIB) ? filter_id : FILTER_ID_ALL; update = true; } if (!STREQ(filelist->filter_data.filter_glob, filter_glob)) { diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index e54f13e9356..bb3906a961b 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -49,6 +49,8 @@ #include "BLI_utildefines.h" #include "BLI_fnmatch.h" +#include "BLO_readfile.h" + #include "BLT_translation.h" #include "BKE_appdir.h" @@ -101,10 +103,9 @@ short ED_fileselect_set_params(SpaceFile *sfile) sizeof(sfile->params->dir), sizeof(sfile->params->file)); sfile->params->filter_glob[0] = '\0'; - /* set the default thumbnails size */ - sfile->params->thumbnail_size = 128; - /* Show size column by default. */ - sfile->params->details_flags = FILE_DETAILS_SIZE | FILE_DETAILS_DATETIME; + sfile->params->thumbnail_size = U_default.file_space_data.thumbnail_size; + sfile->params->details_flags = U_default.file_space_data.details_flags; + sfile->params->filter_id = U_default.file_space_data.filter_id; } params = sfile->params; @@ -167,6 +168,9 @@ short ED_fileselect_set_params(SpaceFile *sfile) params->flag &= ~FILE_DIRSEL_ONLY; } + if ((prop = RNA_struct_find_property(op->ptr, "check_existing"))) { + params->flag |= RNA_property_boolean_get(op->ptr, prop) ? FILE_CHECK_EXISTING : 0; + } if ((prop = RNA_struct_find_property(op->ptr, "hide_props_region"))) { params->flag |= RNA_property_boolean_get(op->ptr, prop) ? FILE_HIDE_TOOL_PROPS : 0; } @@ -241,15 +245,6 @@ short ED_fileselect_set_params(SpaceFile *sfile) } } - /* For now, always init filterid to 'all true' */ - params->filter_id = FILTER_ID_AC | FILTER_ID_AR | FILTER_ID_BR | FILTER_ID_CA | FILTER_ID_CU | - FILTER_ID_GD | FILTER_ID_GR | FILTER_ID_IM | FILTER_ID_LA | FILTER_ID_LS | - FILTER_ID_LT | FILTER_ID_MA | FILTER_ID_MB | FILTER_ID_MC | FILTER_ID_ME | - FILTER_ID_MSK | FILTER_ID_NT | FILTER_ID_OB | FILTER_ID_PA | - FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_SCE | FILTER_ID_SPK | - FILTER_ID_SO | FILTER_ID_TE | FILTER_ID_TXT | FILTER_ID_VF | FILTER_ID_WO | - FILTER_ID_CF | FILTER_ID_WS | FILTER_ID_LP; - if (U.uiflag & USER_HIDE_DOT) { params->flag |= FILE_HIDE_DOT; } @@ -271,30 +266,11 @@ short ED_fileselect_set_params(SpaceFile *sfile) params->sort = RNA_property_enum_get(op->ptr, prop); } else { - params->sort = FILE_SORT_ALPHA; - } - - if ((prop = RNA_struct_find_property(op->ptr, "action_type"))) { - params->action_type = RNA_property_enum_get(op->ptr, prop); + params->sort = U_default.file_space_data.sort_type; } if (params->display == FILE_DEFAULTDISPLAY) { - if (params->display_previous == FILE_DEFAULTDISPLAY) { - if (U.uiflag & USER_SHOW_THUMBNAILS) { - if (params->filter & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_FTFONT)) { - params->display = FILE_IMGDISPLAY; - } - else { - params->display = FILE_VERTICALDISPLAY; - } - } - else { - params->display = FILE_VERTICALDISPLAY; - } - } - else { - params->display = params->display_previous; - } + params->display = U_default.file_space_data.display_type; } if (is_relative_path) { @@ -308,10 +284,9 @@ short ED_fileselect_set_params(SpaceFile *sfile) else { /* default values, if no operator */ params->type = FILE_UNIX; - params->flag |= FILE_HIDE_DOT; + params->flag |= U_default.file_space_data.flag; params->flag &= ~FILE_DIRSEL_ONLY; params->display = FILE_VERTICALDISPLAY; - params->display_previous = FILE_DEFAULTDISPLAY; params->sort = FILE_SORT_ALPHA; params->filter = 0; params->filter_glob[0] = '\0'; @@ -347,6 +322,65 @@ short ED_fileselect_set_params(SpaceFile *sfile) return 1; } +/* The subset of FileSelectParams.flag items we store into preferences. */ +#define PARAMS_FLAGS_REMEMBERED (FILE_HIDE_DOT | FILE_SORT_INVERT) + +void ED_fileselect_set_params_from_userdef(SpaceFile *sfile) +{ + wmOperator *op = sfile->op; + UserDef_FileSpaceData *sfile_udata = &U.file_space_data; + + ED_fileselect_set_params(sfile); + + if (!op) { + return; + } + + if (!RNA_struct_property_is_set(op->ptr, "display_type")) { + sfile->params->display = sfile_udata->display_type; + } + if (!RNA_struct_property_is_set(op->ptr, "sort_method")) { + sfile->params->sort = sfile_udata->sort_type; + } + sfile->params->thumbnail_size = sfile_udata->thumbnail_size; + sfile->params->details_flags = sfile_udata->details_flags; + sfile->params->filter_id = sfile_udata->filter_id; + + /* Combine flags we take from params with the flags we take from userdef. */ + sfile->params->flag = (sfile->params->flag & ~PARAMS_FLAGS_REMEMBERED) | + (sfile_udata->flag & PARAMS_FLAGS_REMEMBERED); +} + +/** + * Update the user-preference data for the file space. In fact, this also contains some + * non-FileSelectParams data, but we can safely ignore this. + * + * \param temp_win_size: If the browser was opened in a temporary window, + * pass its size here so we can store that in the preferences. Otherwise NULL. + */ +void ED_fileselect_params_to_userdef(SpaceFile *sfile, int temp_win_size[2]) +{ + UserDef_FileSpaceData *sfile_udata_new = &U.file_space_data; + UserDef_FileSpaceData sfile_udata_old = U.file_space_data; + + sfile_udata_new->display_type = sfile->params->display; + sfile_udata_new->thumbnail_size = sfile->params->thumbnail_size; + sfile_udata_new->sort_type = sfile->params->sort; + sfile_udata_new->details_flags = sfile->params->details_flags; + sfile_udata_new->flag = sfile->params->flag & PARAMS_FLAGS_REMEMBERED; + sfile_udata_new->filter_id = sfile->params->filter_id; + + if (temp_win_size) { + sfile_udata_new->temp_win_sizex = temp_win_size[0]; + sfile_udata_new->temp_win_sizey = temp_win_size[1]; + } + + /* Tag prefs as dirty if something has changed. */ + if (memcmp(sfile_udata_new, &sfile_udata_old, sizeof(sfile_udata_old)) != 0) { + U.runtime.is_dirty = true; + } +} + void ED_fileselect_reset_params(SpaceFile *sfile) { sfile->params->type = FILE_UNIX; @@ -767,7 +801,6 @@ void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *ar) layout->tile_border_x * 2; layout->flag = FILE_LAYOUT_HOR; } - params->display_previous = params->display; layout->dirty = false; } @@ -924,6 +957,17 @@ void ED_fileselect_exit(wmWindowManager *wm, ScrArea *sa, SpaceFile *sfile) return; } if (sfile->op) { + wmWindow *temp_win = WM_window_is_temp_screen(wm->winactive) ? wm->winactive : NULL; + int win_size[2]; + + if (temp_win) { + /* Get DPI/pixelsize independent size to be stored in preferences. */ + WM_window_set_dpi(temp_win); /* Ensure the DPI is taken from the right window. */ + win_size[0] = WM_window_pixels_x(temp_win) / UI_DPI_FAC; + win_size[1] = WM_window_pixels_y(temp_win) / UI_DPI_FAC; + } + ED_fileselect_params_to_userdef(sfile, temp_win ? win_size : NULL); + WM_event_fileselect_event(wm, sfile->op, EVT_FILESELECT_EXTERNAL_CANCEL); sfile->op = NULL; } diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index d6a4eafc658..d63fcf402de 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -55,6 +55,40 @@ #include "filelist.h" #include "GPU_framebuffer.h" +static ARegion *file_execute_region_ensure(ScrArea *sa, ARegion *ar_prev) +{ + ARegion *ar; + + if ((ar = BKE_area_find_region_type(sa, RGN_TYPE_EXECUTE)) != NULL) { + return ar; + } + + ar = MEM_callocN(sizeof(ARegion), "execute region for file"); + BLI_insertlinkafter(&sa->regionbase, ar_prev, ar); + ar->regiontype = RGN_TYPE_EXECUTE; + ar->alignment = RGN_ALIGN_BOTTOM; + ar->flag = RGN_FLAG_DYNAMIC_SIZE; + + return ar; +} + +static ARegion *file_tool_props_region_ensure(ScrArea *sa, ARegion *ar_prev) +{ + ARegion *ar; + + if ((ar = BKE_area_find_region_type(sa, RGN_TYPE_TOOL_PROPS)) != NULL) { + return ar; + } + + /* add subdiv level; after execute region */ + ar = MEM_callocN(sizeof(ARegion), "tool props for file"); + BLI_insertlinkafter(&sa->regionbase, ar_prev, ar); + ar->regiontype = RGN_TYPE_TOOL_PROPS; + ar->alignment = RGN_ALIGN_RIGHT; + + return ar; +} + /* ******************** default callbacks for file space ***************** */ static SpaceLink *file_new(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) @@ -72,40 +106,20 @@ static SpaceLink *file_new(const ScrArea *UNUSED(area), const Scene *UNUSED(scen /* Ignore user preference "USER_HEADER_BOTTOM" here (always show top for new types). */ ar->alignment = RGN_ALIGN_TOP; - /* ui list region */ - ar = MEM_callocN(sizeof(ARegion), "ui region for file"); - BLI_addtail(&sfile->regionbase, ar); - ar->regiontype = RGN_TYPE_UI; - ar->alignment = RGN_ALIGN_TOP; - ar->flag |= RGN_FLAG_DYNAMIC_SIZE; - /* Tools region */ ar = MEM_callocN(sizeof(ARegion), "tools region for file"); BLI_addtail(&sfile->regionbase, ar); ar->regiontype = RGN_TYPE_TOOLS; ar->alignment = RGN_ALIGN_LEFT; - /* Tools region (lower split region) */ - ar = MEM_callocN(sizeof(ARegion), "lower tools region for file"); - BLI_addtail(&sfile->regionbase, ar); - ar->regiontype = RGN_TYPE_TOOLS; - ar->alignment = RGN_ALIGN_BOTTOM | RGN_SPLIT_PREV; - ar->flag |= RGN_FLAG_DYNAMIC_SIZE; - /* Execute region */ - ar = MEM_callocN(sizeof(ARegion), "execute region for file"); + /* ui list region */ + ar = MEM_callocN(sizeof(ARegion), "ui region for file"); BLI_addtail(&sfile->regionbase, ar); - ar->regiontype = RGN_TYPE_EXECUTE; - ar->alignment = RGN_ALIGN_BOTTOM; + ar->regiontype = RGN_TYPE_UI; + ar->alignment = RGN_ALIGN_TOP; ar->flag |= RGN_FLAG_DYNAMIC_SIZE; - /* Tool props region is added as needed. */ -#if 0 - /* Tool props (aka operator) region */ - ar = MEM_callocN(sizeof(ARegion), "tool props region for file"); - BLI_addtail(&sfile->regionbase, ar); - ar->regiontype = RGN_TYPE_TOOL_PROPS; - ar->alignment = RGN_ALIGN_RIGHT; -#endif + /* Tool props and execute region are added as needed, see file_refresh(). */ /* main region */ ar = MEM_callocN(sizeof(ARegion), "main region for file"); @@ -218,6 +232,46 @@ static SpaceLink *file_duplicate(SpaceLink *sl) return (SpaceLink *)sfilen; } +static void file_ensure_valid_region_state(bContext *C, + wmWindowManager *wm, + wmWindow *win, + ScrArea *sa, + SpaceFile *sfile, + FileSelectParams *params) +{ + ARegion *ar_ui = BKE_area_find_region_type(sa, RGN_TYPE_UI); + ARegion *ar_props = BKE_area_find_region_type(sa, RGN_TYPE_TOOL_PROPS); + ARegion *ar_execute = BKE_area_find_region_type(sa, RGN_TYPE_EXECUTE); + bool needs_init = false; /* To avoid multiple ED_area_initialize() calls. */ + + /* If there's an file-operation, ensure we have the option and execute region */ + if (sfile->op && (ar_props == NULL)) { + ar_execute = file_execute_region_ensure(sa, ar_ui); + ar_props = file_tool_props_region_ensure(sa, ar_execute); + + if (params->flag & FILE_HIDE_TOOL_PROPS) { + ar_props->flag |= RGN_FLAG_HIDDEN; + } + else { + ar_props->flag &= ~RGN_FLAG_HIDDEN; + } + + needs_init = true; + } + /* If there's _no_ file-operation, ensure we _don't_ have the option and execute region */ + else if ((sfile->op == NULL) && (ar_props != NULL)) { + BLI_assert(ar_execute != NULL); + + ED_region_remove(C, sa, ar_props); + ED_region_remove(C, sa, ar_execute); + needs_init = true; + } + + if (needs_init) { + ED_area_initialize(wm, win, sa); + } +} + static void file_refresh(const bContext *C, ScrArea *sa) { wmWindowManager *wm = CTX_wm_manager(C); @@ -287,21 +341,8 @@ static void file_refresh(const bContext *C, ScrArea *sa) } /* Might be called with NULL sa, see file_main_region_draw() below. */ - if (sa && BKE_area_find_region_type(sa, RGN_TYPE_TOOLS) == NULL) { - /* Create TOOLS region. */ - file_tools_region(sa); - - ED_area_initialize(wm, win, sa); - } - if (sa && sfile->op && BKE_area_find_region_type(sa, RGN_TYPE_TOOL_PROPS) == NULL) { - /* Create TOOL_PROPS region. */ - ARegion *region_props = file_tool_props_region(sa); - - if (params->flag & FILE_HIDE_TOOL_PROPS) { - region_props->flag |= RGN_FLAG_HIDDEN; - } - - ED_area_initialize(wm, win, sa); + if (sa) { + file_ensure_valid_region_state((bContext *)C, wm, win, sa, sfile, params); } ED_area_tag_redraw(sa); @@ -495,7 +536,6 @@ static void file_operatortypes(void) WM_operatortype_append(FILE_OT_previous); WM_operatortype_append(FILE_OT_next); WM_operatortype_append(FILE_OT_refresh); - WM_operatortype_append(FILE_OT_bookmark_toggle); WM_operatortype_append(FILE_OT_bookmark_add); WM_operatortype_append(FILE_OT_bookmark_delete); WM_operatortype_append(FILE_OT_bookmark_cleanup); diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c index 708d91a82bb..33cb1cb0075 100644 --- a/source/blender/editors/space_graph/graph_buttons.c +++ b/source/blender/editors/space_graph/graph_buttons.c @@ -862,7 +862,11 @@ static void graph_panel_driverVar__transChan(uiLayout *layout, ID *id, DriverVar sub = uiLayoutColumn(layout, true); uiItemR(sub, &dtar_ptr, "transform_type", 0, NULL, ICON_NONE); - if (ELEM(dtar->transChan, DTAR_TRANSCHAN_ROTX, DTAR_TRANSCHAN_ROTY, DTAR_TRANSCHAN_ROTZ)) { + if (ELEM(dtar->transChan, + DTAR_TRANSCHAN_ROTX, + DTAR_TRANSCHAN_ROTY, + DTAR_TRANSCHAN_ROTZ, + DTAR_TRANSCHAN_ROTW)) { uiItemR(sub, &dtar_ptr, "rotation_mode", 0, IFACE_("Mode"), ICON_NONE); } @@ -1152,8 +1156,12 @@ static void graph_draw_driver_settings_panel(uiLayout *layout, if ((dvar->type == DVAR_TYPE_ROT_DIFF) || (dvar->type == DVAR_TYPE_TRANSFORM_CHAN && - dvar->targets[0].transChan >= DTAR_TRANSCHAN_ROTX && - dvar->targets[0].transChan < DTAR_TRANSCHAN_SCALEX)) { + ELEM(dvar->targets[0].transChan, + DTAR_TRANSCHAN_ROTX, + DTAR_TRANSCHAN_ROTY, + DTAR_TRANSCHAN_ROTZ, + DTAR_TRANSCHAN_ROTW) && + dvar->targets[0].rotation_mode != DTAR_ROTMODE_QUATERNION)) { BLI_snprintf( valBuf, sizeof(valBuf), "%.3f (%4.1f°)", dvar->curval, RAD2DEGF(dvar->curval)); } diff --git a/source/blender/editors/space_graph/graph_draw.c b/source/blender/editors/space_graph/graph_draw.c index c94f1f1d79f..42a1566629a 100644 --- a/source/blender/editors/space_graph/graph_draw.c +++ b/source/blender/editors/space_graph/graph_draw.c @@ -879,10 +879,6 @@ static void graph_draw_driver_debug(bAnimContext *ac, ID *id, FCurve *fcu) float offset; float unitfac = ANIM_unit_mapping_get_factor(ac->scene, id, fcu, mapping_flag, &offset); - /* for now, only show when debugging driver... */ - // if ((driver->flag & DRIVER_FLAG_SHOWDEBUG) == 0) - // return; - const uint shdr_pos = GPU_vertformat_attr_add( immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c index 329067de545..59cf5f63de3 100644 --- a/source/blender/editors/space_graph/graph_edit.c +++ b/source/blender/editors/space_graph/graph_edit.c @@ -2506,28 +2506,28 @@ static const EnumPropertyItem prop_graphkeys_mirror_types[] = { {GRAPHKEYS_MIRROR_CFRA, "CFRA", 0, - "By Times over Current Frame", + "By Times Over Current Frame", "Flip times of selected keyframes using the current frame as the mirror line"}, {GRAPHKEYS_MIRROR_VALUE, "VALUE", 0, - "By Values over Cursor Value", + "By Values Over Cursor Value", "Flip values of selected keyframes using the cursor value (Y/Horizontal component) as the " "mirror line"}, {GRAPHKEYS_MIRROR_YAXIS, "YAXIS", 0, - "By Times over Time=0", + "By Times Over Time=0", "Flip times of selected keyframes, effectively reversing the order they appear in"}, {GRAPHKEYS_MIRROR_XAXIS, "XAXIS", 0, - "By Values over Value=0", + "By Values Over Value=0", "Flip values of selected keyframes (i.e. negative values become positive, and vice versa)"}, {GRAPHKEYS_MIRROR_MARKER, "MARKER", 0, - "By Times over First Selected Marker", + "By Times Over First Selected Marker", "Flip times of selected keyframes using the first selected marker as the reference point"}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/editors/space_image/CMakeLists.txt b/source/blender/editors/space_image/CMakeLists.txt index 61e6b065cba..5abcff436f1 100644 --- a/source/blender/editors/space_image/CMakeLists.txt +++ b/source/blender/editors/space_image/CMakeLists.txt @@ -29,6 +29,7 @@ set(INC ../../makesrna ../../render/extern/include ../../windowmanager + ../../../../intern/clog ../../../../intern/glew-mx ../../../../intern/guardedalloc ) @@ -42,6 +43,7 @@ set(SRC image_draw.c image_edit.c image_ops.c + image_undo.c space_image.c image_intern.h diff --git a/source/blender/editors/space_image/image_edit.c b/source/blender/editors/space_image/image_edit.c index ccd0a2bfd79..ec2b1cc7fbe 100644 --- a/source/blender/editors/space_image/image_edit.c +++ b/source/blender/editors/space_image/image_edit.c @@ -301,6 +301,18 @@ void ED_image_mouse_pos(SpaceImage *sima, ARegion *ar, const int mval[2], float co[1] = ((mval[1] - sy) / zoomy) / height; } +void ED_image_view_center_to_point(SpaceImage *sima, float x, float y) +{ + int width, height; + float aspx, aspy; + + ED_space_image_get_size(sima, &width, &height); + ED_space_image_get_aspect(sima, &aspx, &aspy); + + sima->xof = (x - 0.5f) * width * aspx; + sima->yof = (y - 0.5f) * height * aspy; +} + void ED_image_point_pos(SpaceImage *sima, ARegion *ar, float x, float y, float *xr, float *yr) { int sx, sy, width, height; @@ -476,3 +488,9 @@ bool ED_space_image_maskedit_mask_poll(bContext *C) return false; } + +bool ED_space_image_cursor_poll(bContext *C) +{ + return ED_operator_uvedit_space_image(C) || ED_space_image_maskedit_poll(C) || + ED_space_image_paint_curve(C); +} diff --git a/source/blender/editors/space_image/image_intern.h b/source/blender/editors/space_image/image_intern.h index 1abb6715fdb..f8ce065d46c 100644 --- a/source/blender/editors/space_image/image_intern.h +++ b/source/blender/editors/space_image/image_intern.h @@ -44,10 +44,12 @@ void draw_image_sample_line(struct SpaceImage *sima); /* image_ops.c */ bool space_image_main_region_poll(struct bContext *C); +bool space_image_view_center_cursor_poll(struct bContext *C); void IMAGE_OT_view_all(struct wmOperatorType *ot); void IMAGE_OT_view_pan(struct wmOperatorType *ot); void IMAGE_OT_view_selected(struct wmOperatorType *ot); +void IMAGE_OT_view_center_cursor(struct wmOperatorType *ot); void IMAGE_OT_view_zoom(struct wmOperatorType *ot); void IMAGE_OT_view_zoom_in(struct wmOperatorType *ot); void IMAGE_OT_view_zoom_out(struct wmOperatorType *ot); @@ -70,6 +72,7 @@ void IMAGE_OT_pack(struct wmOperatorType *ot); void IMAGE_OT_unpack(struct wmOperatorType *ot); void IMAGE_OT_invert(struct wmOperatorType *ot); +void IMAGE_OT_resize(struct wmOperatorType *ot); void IMAGE_OT_cycle_render_slot(struct wmOperatorType *ot); void IMAGE_OT_clear_render_slot(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index e338a450db6..a8dfad85232 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -103,7 +103,9 @@ #include "image_intern.h" -/******************** view navigation utilities *********************/ +/* -------------------------------------------------------------------- */ +/** \name View Navigation Utilities + * \{ */ static void sima_zoom_set( SpaceImage *sima, ARegion *ar, float zoom, const float location[2], const bool zoom_to_pos) @@ -211,7 +213,7 @@ static ImageUser *image_user_from_context(const bContext *C) } } -static bool image_buffer_exists_from_context(bContext *C) +static bool image_from_context_has_data_poll(bContext *C) { Image *ima = image_from_context(C); ImageUser *iuser = image_user_from_context(C); @@ -227,6 +229,16 @@ static bool image_buffer_exists_from_context(bContext *C) return has_buffer; } +/** + * Use this when the image buffer is accessed without the image user. + */ +static bool image_from_contect_has_data_poll_no_image_user(bContext *C) +{ + Image *ima = image_from_context(C); + + return BKE_image_has_ibuf(ima, NULL); +} + static bool image_not_packed_poll(bContext *C) { /* Do not run 'replace' on packed images, it does not give user expected results at all. */ @@ -280,7 +292,12 @@ static bool image_sample_poll(bContext *C) return true; } -/********************** view pan operator *********************/ + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name View Pan Operator + * \{ */ typedef struct ViewPanData { float x, y; @@ -300,7 +317,7 @@ static void image_view_pan_init(bContext *C, wmOperator *op, const wmEvent *even /* Grab will be set when running from gizmo. */ vpd->own_cursor = (win->grabcursor == 0); if (vpd->own_cursor) { - WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR); + WM_cursor_modal_set(win, WM_CURSOR_NSEW_SCROLL); } vpd->x = event->x; @@ -423,7 +440,11 @@ void IMAGE_OT_view_pan(wmOperatorType *ot) FLT_MAX); } -/********************** view zoom operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name View Zoom Operator + * \{ */ typedef struct ViewZoomData { float origx, origy; @@ -453,7 +474,7 @@ static void image_view_zoom_init(bContext *C, wmOperator *op, const wmEvent *eve /* Grab will be set when running from gizmo. */ vpd->own_cursor = (win->grabcursor == 0); if (vpd->own_cursor) { - WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR); + WM_cursor_modal_set(win, WM_CURSOR_NSEW_SCROLL); } vpd->origx = event->x; @@ -671,7 +692,12 @@ void IMAGE_OT_view_zoom(wmOperatorType *ot) } #ifdef WITH_INPUT_NDOF -/********************** NDOF operator *********************/ + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name NDOF Operator + * \{ */ /* Combined pan/zoom from a 3D mouse device. * Z zooms, XY pans @@ -721,9 +747,14 @@ void IMAGE_OT_view_ndof(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_LOCK_BYPASS; } + +/** \} */ + #endif /* WITH_INPUT_NDOF */ -/********************** view all operator *********************/ +/* -------------------------------------------------------------------- */ +/** \name View All Operator + * \{ */ /* Updates the fields of the View2D member of the SpaceImage struct. * Default behavior is to reset the position of the image and set the zoom to 1 @@ -800,7 +831,41 @@ void IMAGE_OT_view_all(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } -/********************** view selected operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Center View To Cursor Operator + * \{ */ + +static int view_center_cursor_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceImage *sima = CTX_wm_space_image(C); + ARegion *ar = CTX_wm_region(C); + + ED_image_view_center_to_point(sima, sima->cursor[0], sima->cursor[1]); + + ED_region_tag_redraw(ar); + + return OPERATOR_FINISHED; +} + +void IMAGE_OT_view_center_cursor(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Center View to Cursor"; + ot->description = "Center the view so that the cursor is in the middle of the view"; + ot->idname = "IMAGE_OT_view_center_cursor"; + + /* api callbacks */ + ot->exec = view_center_cursor_exec; + ot->poll = ED_space_image_cursor_poll; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name View Selected Operator + * \{ */ static int image_view_selected_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -866,7 +931,11 @@ void IMAGE_OT_view_selected(wmOperatorType *ot) ot->poll = image_view_selected_poll; } -/********************** view zoom in/out operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name View Zoom In/Out Operator + * \{ */ static int image_view_zoom_in_exec(bContext *C, wmOperator *op) { @@ -984,7 +1053,11 @@ void IMAGE_OT_view_zoom_out(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_HIDDEN); } -/********************** view zoom ratio operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name View Zoom Ratio Operator + * \{ */ static int image_view_zoom_ratio_exec(bContext *C, wmOperator *op) { @@ -1028,7 +1101,11 @@ void IMAGE_OT_view_zoom_ratio(wmOperatorType *ot) FLT_MAX); } -/********************** view border-zoom operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name View Border-Zoom Operator + * \{ */ static int image_view_zoom_border_exec(bContext *C, wmOperator *op) { @@ -1084,14 +1161,18 @@ void IMAGE_OT_view_zoom_border(wmOperatorType *ot) WM_operator_properties_gesture_box_zoom(ot); } -/**************** load/replace/save callbacks ******************/ +/* load/replace/save callbacks */ static void image_filesel(bContext *C, wmOperator *op, const char *path) { RNA_string_set(op->ptr, "filepath", path); WM_event_add_fileselect(C, op); } -/******************** open image operator ********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Open Image Operator + * \{ */ typedef struct ImageOpenData { PropertyPointerRNA pprop; @@ -1525,7 +1606,12 @@ void IMAGE_OT_open(wmOperatorType *ot) "Automatically detect animated sequences in selected images (based on file names)"); } -/******************** Match movie length operator ********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Match Movie Length Operator + * \{ */ + static int image_match_len_exec(bContext *C, wmOperator *UNUSED(op)) { Scene *scene = CTX_data_scene(C); @@ -1571,7 +1657,11 @@ void IMAGE_OT_match_movie_length(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_INTERNAL /* | OPTYPE_UNDO */; } -/******************** replace image operator ********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Replace Image Operator + * \{ */ static int image_replace_exec(bContext *C, wmOperator *op) { @@ -1656,7 +1746,11 @@ void IMAGE_OT_replace(wmOperatorType *ot) FILE_SORT_ALPHA); } -/******************** save image as operator ********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Save Image As Operator + * \{ */ static char imtype_best_depth(ImBuf *ibuf, const char imtype) { @@ -1970,7 +2064,7 @@ static void image_save_as_draw(bContext *UNUSED(C), wmOperator *op) static bool image_save_as_poll(bContext *C) { - if (!image_buffer_exists_from_context(C)) { + if (!image_from_context_has_data_poll(C)) { return false; } @@ -2021,12 +2115,16 @@ void IMAGE_OT_save_as(wmOperatorType *ot) FILE_TYPE_FOLDER | FILE_TYPE_IMAGE | FILE_TYPE_MOVIE, FILE_SPECIAL, FILE_SAVE, - WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH, + WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH | WM_FILESEL_SHOW_PROPS, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); } -/******************** save image operator ********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Save Image Operator + * \{ */ static bool image_file_path_saveable(bContext *C, Image *ima, ImageUser *iuser) { @@ -2067,7 +2165,7 @@ static bool image_file_path_saveable(bContext *C, Image *ima, ImageUser *iuser) static bool image_save_poll(bContext *C) { /* Can't save if there are no pixels. */ - if (image_buffer_exists_from_context(C) == false) { + if (image_from_context_has_data_poll(C) == false) { return false; } @@ -2147,7 +2245,11 @@ void IMAGE_OT_save(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/******************* save sequence operator ********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Save Sequence Operator + * \{ */ static int image_save_sequence_exec(bContext *C, wmOperator *op) { @@ -2233,13 +2335,17 @@ void IMAGE_OT_save_sequence(wmOperatorType *ot) /* api callbacks */ ot->exec = image_save_sequence_exec; - ot->poll = image_buffer_exists_from_context; + ot->poll = image_from_context_has_data_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/********************** save all operator **********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Save All Operator + * \{ */ static bool image_should_be_saved_when_modified(Image *ima) { @@ -2389,7 +2495,11 @@ void IMAGE_OT_save_all_modified(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/******************** reload image operator ********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Reload Image Operator + * \{ */ static int image_reload_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -2426,7 +2536,12 @@ void IMAGE_OT_reload(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER; /* no undo, image buffer is not handled by undo */ } -/********************** new image operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name New Image Operator + * \{ */ + #define IMA_DEF_NAME N_("Untitled") enum { @@ -2642,22 +2757,18 @@ void IMAGE_OT_new(wmOperatorType *ot) #undef IMA_DEF_NAME -/********************* invert operators *********************/ - -static bool image_invert_poll(bContext *C) -{ - Image *ima = image_from_context(C); +/** \} */ - return BKE_image_has_ibuf(ima, NULL); -} +/* -------------------------------------------------------------------- */ +/** \name Invert Operators + * \{ */ static int image_invert_exec(bContext *C, wmOperator *op) { Image *ima = image_from_context(C); ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); SpaceImage *sima = CTX_wm_space_image(C); - /* undo is supported only on image paint mode currently */ - bool support_undo = ((sima != NULL) && (sima->mode == SI_MODE_PAINT)); + const bool is_paint = ((sima != NULL) && (sima->mode == SI_MODE_PAINT)); /* flags indicate if this channel should be inverted */ const bool r = RNA_boolean_get(op->ptr, "invert_r"); @@ -2672,14 +2783,12 @@ static int image_invert_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - if (support_undo) { - ED_image_undo_push_begin(op->type->name, PAINT_MODE_TEXTURE_2D); - /* not strictly needed, because we only imapaint_dirty_region to invalidate all tiles - * but better do this right in case someone copies this for a tool that uses partial - * redraw better */ + ED_image_undo_push_begin_with_image(op->type->name, ima, ibuf); + + if (is_paint) { ED_imapaint_clear_partial_redraw(); - ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y, false); } + /* TODO: make this into an IMB_invert_channels(ibuf,r,g,b,a) method!? */ if (ibuf->rect_float) { @@ -2733,9 +2842,7 @@ static int image_invert_exec(bContext *C, wmOperator *op) ibuf->userflags |= IB_MIPMAP_INVALID; } - if (support_undo) { - ED_image_undo_push_end(); - } + ED_image_undo_push_end(); /* force GPU reupload, all image is invalid */ GPU_free_image(ima); @@ -2758,7 +2865,7 @@ void IMAGE_OT_invert(wmOperatorType *ot) /* api callbacks */ ot->exec = image_invert_exec; - ot->poll = image_invert_poll; + ot->poll = image_from_contect_has_data_poll_no_image_user; /* properties */ prop = RNA_def_boolean(ot->srna, "invert_r", 0, "Red", "Invert Red Channel"); @@ -2771,10 +2878,96 @@ void IMAGE_OT_invert(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Scale Operator + * \{ */ + +static int image_scale_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + Image *ima = image_from_context(C); + 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); + const int size[2] = {ibuf->x, ibuf->y}; + RNA_property_int_set_array(op->ptr, prop, size); + BKE_image_release_ibuf(ima, ibuf, NULL); + } + return WM_operator_props_dialog_popup(C, op, 200, 200); +} + +static int image_scale_exec(bContext *C, wmOperator *op) +{ + Image *ima = image_from_context(C); + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); + SpaceImage *sima = CTX_wm_space_image(C); + const bool is_paint = ((sima != NULL) && (sima->mode == SI_MODE_PAINT)); + + if (ibuf == NULL) { + /* TODO: this should actually never happen, but does for render-results -> cleanup */ + return OPERATOR_CANCELLED; + } + + if (is_paint) { + ED_imapaint_clear_partial_redraw(); + } + + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "size"); + int size[2]; + if (RNA_property_is_set(op->ptr, prop)) { + RNA_property_int_get_array(op->ptr, prop, size); + } + else { + size[0] = ibuf->x; + size[1] = ibuf->y; + RNA_property_int_set_array(op->ptr, prop, size); + } + + ED_image_undo_push_begin_with_image(op->type->name, ima, ibuf); + + ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; + IMB_scaleImBuf(ibuf, size[0], size[1]); + BKE_image_release_ibuf(ima, ibuf, NULL); + + ED_image_undo_push_end(); + + /* force GPU reupload, all image is invalid */ + GPU_free_image(ima); + + DEG_id_tag_update(&ima->id, 0); + WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima); + + return OPERATOR_FINISHED; } -/********************* pack operator *********************/ +void IMAGE_OT_resize(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Resize Image"; + ot->idname = "IMAGE_OT_resize"; + ot->description = "Resize the image"; + + /* api callbacks */ + ot->invoke = image_scale_invoke; + ot->exec = image_scale_exec; + ot->poll = image_from_contect_has_data_poll_no_image_user; + + /* properties */ + RNA_def_int_vector(ot->srna, "size", 2, NULL, 1, INT_MAX, "Size", "", 1, SHRT_MAX); + + /* flags */ + ot->flag = OPTYPE_REGISTER; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Pack Operator + * \{ */ static bool image_pack_test(bContext *C, wmOperator *op) { @@ -2827,7 +3020,11 @@ void IMAGE_OT_pack(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/********************* unpack operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Unpack Operator + * \{ */ static int image_unpack_exec(bContext *C, wmOperator *op) { @@ -2926,7 +3123,11 @@ void IMAGE_OT_unpack(wmOperatorType *ot) ot->srna, "id", NULL, MAX_ID_NAME - 2, "Image Name", "Image data-block name to unpack"); } -/******************** sample image operator ********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Sample Image Operator + * \{ */ typedef struct ImageSampleInfo { ARegionType *art; @@ -3339,7 +3540,12 @@ void IMAGE_OT_sample(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } -/******************** sample line operator ********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Sample Line Operator + * \{ */ + static int image_sample_line_exec(bContext *C, wmOperator *op) { SpaceImage *sima = CTX_wm_space_image(C); @@ -3421,10 +3627,14 @@ void IMAGE_OT_sample_line(wmOperatorType *ot) /* flags */ ot->flag = 0; /* no undo/register since this operates on the space */ - WM_operator_properties_gesture_straightline(ot, CURSOR_EDIT); + WM_operator_properties_gesture_straightline(ot, WM_CURSOR_EDIT); } -/******************** set curve point operator ********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Set Curve Point Operator + * \{ */ void IMAGE_OT_curves_point_set(wmOperatorType *ot) { @@ -3458,7 +3668,11 @@ void IMAGE_OT_curves_point_set(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } -/********************* cycle render slot operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Cycle Render Slot Operator + * \{ */ static bool image_cycle_render_slot_poll(bContext *C) { @@ -3504,7 +3718,11 @@ void IMAGE_OT_cycle_render_slot(wmOperatorType *ot) RNA_def_boolean(ot->srna, "reverse", 0, "Cycle in Reverse", ""); } -/********************* clear render slot operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Clear Render Slot Operator + * \{ */ static int image_clear_render_slot_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -3535,7 +3753,11 @@ void IMAGE_OT_clear_render_slot(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER; } -/********************* add render slot operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Add Render Slot Operator + * \{ */ static int image_add_render_slot_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -3564,7 +3786,11 @@ void IMAGE_OT_add_render_slot(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER; } -/********************* remove render slot operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Remove Render Slot Operator + * \{ */ static int image_remove_render_slot_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -3595,7 +3821,11 @@ void IMAGE_OT_remove_render_slot(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER; } -/********************** change frame operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Change Frame Operator + * \{ */ static bool change_frame_poll(bContext *C) { @@ -3746,7 +3976,11 @@ void IMAGE_OT_read_viewlayers(wmOperatorType *ot) ot->flag = 0; } -/* ********************* Render border operator ****************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Render Border Operator + * \{ */ static int render_border_exec(bContext *C, wmOperator *op) { @@ -3815,7 +4049,11 @@ void IMAGE_OT_render_border(wmOperatorType *ot) WM_operator_properties_border(ot); } -/* ********************* Clear render border operator ****************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Clear Render Border Operator + * \{ */ static int clear_render_border_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -3840,3 +4078,5 @@ void IMAGE_OT_clear_render_border(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } + +/** \} */ diff --git a/source/blender/editors/space_image/image_undo.c b/source/blender/editors/space_image/image_undo.c new file mode 100644 index 00000000000..577c4e24b11 --- /dev/null +++ b/source/blender/editors/space_image/image_undo.c @@ -0,0 +1,1058 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup spimage + * + * Overview + * ======== + * + * - Each undo step is a #ImageUndoStep + * - Each #ImageUndoStep stores a list of #UndoImageHandle + * - Each #UndoImageHandle stores a list of #UndoImageBuf + * (this is the undo systems equivalent of an #ImBuf). + * - Each #UndoImageBuf stores an array of #UndoImageTile + * The tiles are shared between #UndoImageBuf's to avoid duplication. + * + * When the undo system manages an image, there will always be a full copy (as a #UndoImageBuf) + * each new undo step only stores modified tiles. + */ + +#include "CLG_log.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_math.h" +#include "BLI_blenlib.h" +#include "BLI_utildefines.h" +#include "BLI_threads.h" + +#include "DNA_image_types.h" +#include "DNA_windowmanager_types.h" +#include "DNA_object_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" + +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + +#include "BKE_context.h" +#include "BKE_image.h" +#include "BKE_paint.h" +#include "BKE_undo_system.h" + +#include "DEG_depsgraph.h" + +#include "ED_paint.h" +#include "ED_undo.h" +#include "ED_util.h" +#include "ED_object.h" + +#include "GPU_draw.h" + +#include "WM_api.h" + +static CLG_LogRef LOG = {"ed.image.undo"}; + +/* -------------------------------------------------------------------- */ +/** \name Thread Locking + * \{ */ + +/* This is a non-global static resource, + * Maybe it should be exposed as part of the + * paint operation, but for now just give a public interface */ +static SpinLock paint_tiles_lock; + +void ED_image_paint_tile_lock_init(void) +{ + BLI_spin_init(&paint_tiles_lock); +} + +void ED_image_paint_tile_lock_end(void) +{ + BLI_spin_end(&paint_tiles_lock); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Paint Tiles + * + * Created on demand while painting, + * use to access the previous state for some paint operations. + * + * These buffers are also used for undo when available. + * + * \{ */ + +static ImBuf *imbuf_alloc_temp_tile(void) +{ + return IMB_allocImBuf( + ED_IMAGE_UNDO_TILE_SIZE, ED_IMAGE_UNDO_TILE_SIZE, 32, IB_rectfloat | IB_rect); +} + +typedef struct PaintTile { + struct PaintTile *next, *prev; + Image *image; + ImBuf *ibuf; + union { + float *fp; + uint *uint; + void *pt; + } rect; + ushort *mask; + bool valid; + bool use_float; + int x, y; +} PaintTile; + +static void ptile_free(PaintTile *ptile) +{ + if (ptile->rect.pt) { + MEM_freeN(ptile->rect.pt); + } + if (ptile->mask) { + MEM_freeN(ptile->mask); + } + MEM_freeN(ptile); +} + +static void ptile_free_list(ListBase *paint_tiles) +{ + for (PaintTile *ptile = paint_tiles->first, *ptile_next; ptile; ptile = ptile_next) { + ptile_next = ptile->next; + ptile_free(ptile); + } + BLI_listbase_clear(paint_tiles); +} + +static void ptile_invalidate_list(ListBase *paint_tiles) +{ + for (PaintTile *ptile = paint_tiles->first; ptile; ptile = ptile->next) { + ptile->valid = false; + } +} + +void *ED_image_paint_tile_find(ListBase *paint_tiles, + Image *image, + ImBuf *ibuf, + int x_tile, + int y_tile, + ushort **r_mask, + bool validate) +{ + for (PaintTile *ptile = paint_tiles->first; ptile; ptile = ptile->next) { + if (ptile->x == x_tile && ptile->y == y_tile) { + if (ptile->image == image && ptile->ibuf == ibuf) { + if (r_mask) { + /* allocate mask if requested. */ + if (!ptile->mask) { + ptile->mask = MEM_callocN(sizeof(ushort) * SQUARE(ED_IMAGE_UNDO_TILE_SIZE), + "UndoImageTile.mask"); + } + *r_mask = ptile->mask; + } + if (validate) { + ptile->valid = true; + } + return ptile->rect.pt; + } + } + } + return NULL; +} + +void *ED_image_paint_tile_push(ListBase *paint_tiles, + Image *image, + ImBuf *ibuf, + ImBuf **tmpibuf, + int x_tile, + int y_tile, + ushort **r_mask, + bool **r_valid, + bool use_thread_lock, + bool find_prev) +{ + const bool has_float = (ibuf->rect_float != NULL); + + /* check if tile is already pushed */ + + /* in projective painting we keep accounting of tiles, so if we need one pushed, just push! */ + if (find_prev) { + void *data = ED_image_paint_tile_find(paint_tiles, image, ibuf, x_tile, y_tile, r_mask, true); + if (data) { + return data; + } + } + + if (*tmpibuf == NULL) { + *tmpibuf = imbuf_alloc_temp_tile(); + } + + PaintTile *ptile = MEM_callocN(sizeof(PaintTile), "PaintTile"); + + ptile->image = image; + ptile->ibuf = ibuf; + + ptile->x = x_tile; + ptile->y = y_tile; + + /* add mask explicitly here */ + if (r_mask) { + *r_mask = ptile->mask = MEM_callocN(sizeof(ushort) * SQUARE(ED_IMAGE_UNDO_TILE_SIZE), + "PaintTile.mask"); + } + + ptile->rect.pt = MEM_mapallocN((ibuf->rect_float ? sizeof(float[4]) : sizeof(char[4])) * + SQUARE(ED_IMAGE_UNDO_TILE_SIZE), + "PaintTile.rect"); + + ptile->use_float = has_float; + ptile->valid = true; + + if (r_valid) { + *r_valid = &ptile->valid; + } + + IMB_rectcpy(*tmpibuf, + ibuf, + 0, + 0, + x_tile * ED_IMAGE_UNDO_TILE_SIZE, + y_tile * ED_IMAGE_UNDO_TILE_SIZE, + ED_IMAGE_UNDO_TILE_SIZE, + ED_IMAGE_UNDO_TILE_SIZE); + + if (has_float) { + SWAP(float *, ptile->rect.fp, (*tmpibuf)->rect_float); + } + else { + SWAP(uint *, ptile->rect.uint, (*tmpibuf)->rect); + } + + if (use_thread_lock) { + BLI_spin_lock(&paint_tiles_lock); + } + BLI_addtail(paint_tiles, ptile); + + if (use_thread_lock) { + BLI_spin_unlock(&paint_tiles_lock); + } + return ptile->rect.pt; +} + +static void ptile_restore_runtime_list(ListBase *paint_tiles) +{ + ImBuf *tmpibuf = imbuf_alloc_temp_tile(); + + for (PaintTile *ptile = paint_tiles->first; ptile; ptile = ptile->next) { + Image *image = ptile->image; + ImBuf *ibuf = BKE_image_acquire_ibuf(image, NULL, NULL); + const bool has_float = (ibuf->rect_float != NULL); + + if (has_float) { + SWAP(float *, ptile->rect.fp, tmpibuf->rect_float); + } + else { + SWAP(uint *, ptile->rect.uint, tmpibuf->rect); + } + + IMB_rectcpy( + ibuf, tmpibuf, ptile->x, ptile->y, 0, 0, ED_IMAGE_UNDO_TILE_SIZE, ED_IMAGE_UNDO_TILE_SIZE); + + if (has_float) { + SWAP(float *, ptile->rect.fp, tmpibuf->rect_float); + } + else { + SWAP(uint *, ptile->rect.uint, tmpibuf->rect); + } + + GPU_free_image(image); /* force OpenGL reload (maybe partial update will operate better?) */ + if (ibuf->rect_float) { + 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_DISPLAY_BUFFER_INVALID; + + BKE_image_release_ibuf(image, ibuf, NULL); + } + + IMB_freeImBuf(tmpibuf); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Image Undo Tile + * \{ */ + +static uint index_from_xy(uint tile_x, uint tile_y, const uint tiles_dims[2]) +{ + BLI_assert(tile_x < tiles_dims[0] && tile_y < tiles_dims[1]); + return (tile_y * tiles_dims[0]) + tile_x; +} + +typedef struct UndoImageTile { + union { + float *fp; + uint *uint; + void *pt; + } rect; + int users; +} UndoImageTile; + +static UndoImageTile *utile_alloc(bool has_float) +{ + UndoImageTile *utile = MEM_callocN(sizeof(*utile), "ImageUndoTile"); + if (has_float) { + utile->rect.fp = MEM_mallocN(sizeof(float[4]) * SQUARE(ED_IMAGE_UNDO_TILE_SIZE), __func__); + } + else { + utile->rect.uint = MEM_mallocN(sizeof(uint) * SQUARE(ED_IMAGE_UNDO_TILE_SIZE), __func__); + } + return utile; +} + +static void utile_init_from_imbuf( + UndoImageTile *utile, const uint x, const uint y, const ImBuf *ibuf, ImBuf *tmpibuf) +{ + const bool has_float = ibuf->rect_float; + + if (has_float) { + SWAP(float *, utile->rect.fp, tmpibuf->rect_float); + } + else { + SWAP(uint *, utile->rect.uint, tmpibuf->rect); + } + + IMB_rectcpy(tmpibuf, ibuf, 0, 0, x, y, ED_IMAGE_UNDO_TILE_SIZE, ED_IMAGE_UNDO_TILE_SIZE); + + if (has_float) { + SWAP(float *, utile->rect.fp, tmpibuf->rect_float); + } + else { + SWAP(uint *, utile->rect.uint, tmpibuf->rect); + } +} + +static void utile_restore( + const UndoImageTile *utile, const uint x, const uint y, ImBuf *ibuf, ImBuf *tmpibuf) +{ + const bool has_float = ibuf->rect_float; + float *prev_rect_float = tmpibuf->rect_float; + uint *prev_rect = tmpibuf->rect; + + if (has_float) { + tmpibuf->rect_float = utile->rect.fp; + } + else { + tmpibuf->rect = utile->rect.uint; + } + + IMB_rectcpy(ibuf, tmpibuf, x, y, 0, 0, ED_IMAGE_UNDO_TILE_SIZE, ED_IMAGE_UNDO_TILE_SIZE); + + tmpibuf->rect_float = prev_rect_float; + tmpibuf->rect = prev_rect; +} + +static void utile_decref(UndoImageTile *utile) +{ + utile->users -= 1; + BLI_assert(utile->users >= 0); + if (utile->users == 0) { + MEM_freeN(utile->rect.pt); + MEM_freeN(utile); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Image Undo Buffer + * \{ */ + +typedef struct UndoImageBuf { + struct UndoImageBuf *next, *prev; + + /** + * The buffer after the undo step has executed. + */ + struct UndoImageBuf *post; + + char ibuf_name[IMB_FILENAME_SIZE]; + + UndoImageTile **tiles; + + /** Can calculate these from dims, just for convenience. */ + uint tiles_len; + uint tiles_dims[2]; + + uint image_dims[2]; + + /** Store variables from the image. */ + struct { + short source; + bool use_float; + char gen_type; + } image_state; + +} UndoImageBuf; + +static UndoImageBuf *ubuf_from_image_no_tiles(Image *image, const ImBuf *ibuf) +{ + UndoImageBuf *ubuf = MEM_callocN(sizeof(*ubuf), __func__); + + ubuf->image_dims[0] = ibuf->x; + ubuf->image_dims[1] = ibuf->y; + + ubuf->tiles_dims[0] = ED_IMAGE_UNDO_TILE_NUMBER(ubuf->image_dims[0]); + ubuf->tiles_dims[1] = ED_IMAGE_UNDO_TILE_NUMBER(ubuf->image_dims[1]); + + ubuf->tiles_len = ubuf->tiles_dims[0] * ubuf->tiles_dims[1]; + ubuf->tiles = MEM_callocN(sizeof(*ubuf->tiles) * ubuf->tiles_len, __func__); + + BLI_strncpy(ubuf->ibuf_name, ibuf->name, sizeof(ubuf->ibuf_name)); + ubuf->image_state.gen_type = image->gen_type; + ubuf->image_state.source = image->source; + ubuf->image_state.use_float = ibuf->rect_float != NULL; + + return ubuf; +} + +static void ubuf_from_image_all_tiles(UndoImageBuf *ubuf, const ImBuf *ibuf) +{ + ImBuf *tmpibuf = imbuf_alloc_temp_tile(); + + const bool has_float = ibuf->rect_float; + int i = 0; + for (uint y_tile = 0; y_tile < ubuf->tiles_dims[1]; y_tile += 1) { + uint y = y_tile << ED_IMAGE_UNDO_TILE_BITS; + for (uint x_tile = 0; x_tile < ubuf->tiles_dims[0]; x_tile += 1) { + uint x = x_tile << ED_IMAGE_UNDO_TILE_BITS; + + BLI_assert(ubuf->tiles[i] == NULL); + UndoImageTile *utile = utile_alloc(has_float); + utile->users = 1; + utile_init_from_imbuf(utile, x, y, ibuf, tmpibuf); + ubuf->tiles[i] = utile; + + i += 1; + } + } + + IMB_freeImBuf(tmpibuf); +} + +/** Ensure we can copy the ubuf into the ibuf. */ +static void ubuf_ensure_compat_ibuf(const UndoImageBuf *ubuf, ImBuf *ibuf) +{ + /* We could have both float and rect buffers, + * in this case free the float buffer if it's unused. */ + if ((ibuf->rect_float != NULL) && (ubuf->image_state.use_float == false)) { + imb_freerectfloatImBuf(ibuf); + } + + if (ibuf->x == ubuf->image_dims[0] && ibuf->y == ubuf->image_dims[1] && + (ubuf->image_state.use_float ? (void *)ibuf->rect_float : (void *)ibuf->rect)) { + return; + } + + imb_freerectImbuf_all(ibuf); + IMB_rect_size_set(ibuf, ubuf->image_dims); + + if (ubuf->image_state.use_float) { + imb_addrectfloatImBuf(ibuf); + } + else { + imb_addrectImBuf(ibuf); + } +} + +static void ubuf_free(UndoImageBuf *ubuf) +{ + UndoImageBuf *ubuf_post = ubuf->post; + for (uint i = 0; i < ubuf->tiles_len; i++) { + UndoImageTile *utile = ubuf->tiles[i]; + utile_decref(utile); + } + MEM_freeN(ubuf->tiles); + MEM_freeN(ubuf); + if (ubuf_post) { + ubuf_free(ubuf_post); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Image Undo Handle + * \{ */ + +typedef struct UndoImageHandle { + struct UndoImageHandle *next, *prev; + + /** Each undo handle refers to a single image which may have multiple buffers. */ + UndoRefID_Image image_ref; + + /** + * List of #UndoImageBuf's to support multiple buffers per image. + * + * \note To properly support multiple buffers per image + * we would need to store an #ImageUser for each #UndoImageBuf. + * since when restoring the image we use: + * `BKE_image_acquire_ibuf(image, NULL, NULL)`. + */ + ListBase buffers; + +} UndoImageHandle; + +static void uhandle_restore_list(ListBase *undo_handles, bool use_init) +{ + ImBuf *tmpibuf = imbuf_alloc_temp_tile(); + + for (UndoImageHandle *uh = undo_handles->first; uh; uh = uh->next) { + /* Tiles only added to second set of tiles. */ + Image *image = uh->image_ref.ptr; + ImBuf *ibuf = BKE_image_acquire_ibuf(image, NULL, NULL); + if (UNLIKELY(ibuf == NULL)) { + CLOG_ERROR(&LOG, "Unable to get buffer for image '%s'", image->id.name + 2); + continue; + } + bool changed = false; + for (UndoImageBuf *ubuf_iter = uh->buffers.first; ubuf_iter; ubuf_iter = ubuf_iter->next) { + UndoImageBuf *ubuf = use_init ? ubuf_iter : ubuf_iter->post; + ubuf_ensure_compat_ibuf(ubuf, ibuf); + + int i = 0; + for (uint y_tile = 0; y_tile < ubuf->tiles_dims[1]; y_tile += 1) { + uint y = y_tile << ED_IMAGE_UNDO_TILE_BITS; + for (uint x_tile = 0; x_tile < ubuf->tiles_dims[0]; x_tile += 1) { + uint x = x_tile << ED_IMAGE_UNDO_TILE_BITS; + utile_restore(ubuf->tiles[i], x, y, ibuf, tmpibuf); + changed = true; + i += 1; + } + } + } + + if (changed) { + BKE_image_mark_dirty(image, ibuf); + GPU_free_image(image); /* force OpenGL reload */ + + if (ibuf->rect_float) { + 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_DISPLAY_BUFFER_INVALID; + + DEG_id_tag_update(&image->id, 0); + } + BKE_image_release_ibuf(image, ibuf, NULL); + } + + IMB_freeImBuf(tmpibuf); +} + +static void uhandle_free_list(ListBase *undo_handles) +{ + LISTBASE_FOREACH_MUTABLE (UndoImageHandle *, uh, undo_handles) { + LISTBASE_FOREACH_MUTABLE (UndoImageBuf *, ubuf, &uh->buffers) { + ubuf_free(ubuf); + } + MEM_freeN(uh); + } + BLI_listbase_clear(undo_handles); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Image Undo Internal Utilities + * \{ */ + +/** #UndoImageHandle utilities */ + +static UndoImageBuf *uhandle_lookup_ubuf(UndoImageHandle *uh, + const Image *UNUSED(image), + const char *ibuf_name) +{ + for (UndoImageBuf *ubuf = uh->buffers.first; ubuf; ubuf = ubuf->next) { + if (STREQ(ubuf->ibuf_name, ibuf_name)) { + return ubuf; + } + } + return NULL; +} + +static UndoImageBuf *uhandle_add_ubuf(UndoImageHandle *uh, Image *image, ImBuf *ibuf) +{ + BLI_assert(uhandle_lookup_ubuf(uh, image, ibuf->name) == NULL); + UndoImageBuf *ubuf = ubuf_from_image_no_tiles(image, ibuf); + BLI_addtail(&uh->buffers, ubuf); + + ubuf->post = NULL; + + return ubuf; +} + +static UndoImageBuf *uhandle_ensure_ubuf(UndoImageHandle *uh, Image *image, ImBuf *ibuf) +{ + UndoImageBuf *ubuf = uhandle_lookup_ubuf(uh, image, ibuf->name); + if (ubuf == NULL) { + ubuf = uhandle_add_ubuf(uh, image, ibuf); + } + return ubuf; +} + +static UndoImageHandle *uhandle_lookup_by_name(ListBase *undo_handles, const Image *image) +{ + for (UndoImageHandle *uh = undo_handles->first; uh; uh = uh->next) { + if (STREQ(image->id.name + 2, uh->image_ref.name + 2)) { + return uh; + } + } + return NULL; +} + +static UndoImageHandle *uhandle_lookup(ListBase *undo_handles, const Image *image) +{ + for (UndoImageHandle *uh = undo_handles->first; uh; uh = uh->next) { + if (image == uh->image_ref.ptr) { + return uh; + } + } + return NULL; +} + +static UndoImageHandle *uhandle_add(ListBase *undo_handles, Image *image) +{ + BLI_assert(uhandle_lookup(undo_handles, image) == NULL); + UndoImageHandle *uh = MEM_callocN(sizeof(*uh), __func__); + uh->image_ref.ptr = image; + BLI_addtail(undo_handles, uh); + return uh; +} + +static UndoImageHandle *uhandle_ensure(ListBase *undo_handles, Image *image) +{ + UndoImageHandle *uh = uhandle_lookup(undo_handles, image); + if (uh == NULL) { + uh = uhandle_add(undo_handles, image); + } + return uh; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Implements ED Undo System + * \{ */ + +typedef struct ImageUndoStep { + UndoStep step; + + /** #UndoImageHandle */ + ListBase handles; + + /** + * #PaintTile + * Run-time only data (active during a paint stroke). + */ + ListBase paint_tiles; + + bool is_encode_init; + ePaintMode paint_mode; + +} ImageUndoStep; + +/** + * Find the previous undo buffer from this one. + * \note We could look into undo steps even further back. + */ +static UndoImageBuf *ubuf_lookup_from_reference(ImageUndoStep *us_prev, + const Image *image, + const UndoImageBuf *ubuf) +{ + /* Use name lookup because because the pointer is cleared for previous steps. */ + UndoImageHandle *uh_prev = uhandle_lookup_by_name(&us_prev->handles, image); + if (uh_prev != NULL) { + UndoImageBuf *ubuf_reference = uhandle_lookup_ubuf(uh_prev, image, ubuf->ibuf_name); + if (ubuf_reference) { + ubuf_reference = ubuf_reference->post; + if ((ubuf_reference->image_dims[0] == ubuf->image_dims[0]) && + (ubuf_reference->image_dims[1] == ubuf->image_dims[1])) { + return ubuf_reference; + } + } + } + return NULL; +} + +static bool image_undosys_poll(bContext *C) +{ + Object *obact = CTX_data_active_object(C); + + ScrArea *sa = CTX_wm_area(C); + if (sa && (sa->spacetype == SPACE_IMAGE)) { + SpaceImage *sima = (SpaceImage *)sa->spacedata.first; + if ((obact && (obact->mode & OB_MODE_TEXTURE_PAINT)) || (sima->mode == SI_MODE_PAINT)) { + return true; + } + } + else { + if (obact && (obact->mode & OB_MODE_TEXTURE_PAINT)) { + return true; + } + } + return false; +} + +static void image_undosys_step_encode_init(struct bContext *UNUSED(C), UndoStep *us_p) +{ + ImageUndoStep *us = (ImageUndoStep *)us_p; + /* dummy, memory is cleared anyway. */ + us->is_encode_init = true; + BLI_listbase_clear(&us->handles); + BLI_listbase_clear(&us->paint_tiles); +} + +static bool image_undosys_step_encode(struct bContext *C, + struct Main *UNUSED(bmain), + UndoStep *us_p) +{ + /* Encoding is done along the way by adding tiles + * to the current 'ImageUndoStep' added by encode_init. + * + * This function ensures there are previous and current states of the image in the undo buffer. + */ + ImageUndoStep *us = (ImageUndoStep *)us_p; + + BLI_assert(us->step.data_size == 0); + + if (us->is_encode_init) { + + ImBuf *tmpibuf = imbuf_alloc_temp_tile(); + + ImageUndoStep *us_reference = (ImageUndoStep *)ED_undo_stack_get()->step_active; + while (us_reference && us_reference->step.type != BKE_UNDOSYS_TYPE_IMAGE) { + us_reference = (ImageUndoStep *)us_reference->step.prev; + } + + /* Initialize undo tiles from ptiles (if they exist). */ + for (PaintTile *ptile = us->paint_tiles.first, *ptile_next; ptile; ptile = ptile_next) { + if (ptile->valid) { + UndoImageHandle *uh = uhandle_ensure(&us->handles, ptile->image); + UndoImageBuf *ubuf_pre = uhandle_ensure_ubuf(uh, ptile->image, ptile->ibuf); + + UndoImageTile *utile = MEM_callocN(sizeof(*utile), "UndoImageTile"); + utile->users = 1; + utile->rect.pt = ptile->rect.pt; + ptile->rect.pt = NULL; + const uint tile_index = index_from_xy(ptile->x, ptile->y, ubuf_pre->tiles_dims); + + BLI_assert(ubuf_pre->tiles[tile_index] == NULL); + ubuf_pre->tiles[tile_index] = utile; + } + ptile_next = ptile->next; + ptile_free(ptile); + } + BLI_listbase_clear(&us->paint_tiles); + + for (UndoImageHandle *uh = us->handles.first; uh; uh = uh->next) { + for (UndoImageBuf *ubuf_pre = uh->buffers.first; ubuf_pre; ubuf_pre = ubuf_pre->next) { + + ImBuf *ibuf = BKE_image_acquire_ibuf(uh->image_ref.ptr, NULL, NULL); + + const bool has_float = ibuf->rect_float; + + BLI_assert(ubuf_pre->post == NULL); + ubuf_pre->post = ubuf_from_image_no_tiles(uh->image_ref.ptr, ibuf); + UndoImageBuf *ubuf_post = ubuf_pre->post; + + if (ubuf_pre->image_dims[0] != ubuf_post->image_dims[0] || + ubuf_pre->image_dims[1] != ubuf_post->image_dims[1]) { + ubuf_from_image_all_tiles(ubuf_post, ibuf); + } + else { + /* Search for the previous buffer. */ + UndoImageBuf *ubuf_reference = (us_reference ? + ubuf_lookup_from_reference( + us_reference, uh->image_ref.ptr, ubuf_post) : + NULL); + + int i = 0; + for (uint y_tile = 0; y_tile < ubuf_pre->tiles_dims[1]; y_tile += 1) { + uint y = y_tile << ED_IMAGE_UNDO_TILE_BITS; + for (uint x_tile = 0; x_tile < ubuf_pre->tiles_dims[0]; x_tile += 1) { + uint x = x_tile << ED_IMAGE_UNDO_TILE_BITS; + + if ((ubuf_reference != NULL) && ((ubuf_pre->tiles[i] == NULL) || + /* In this case the paint stroke as has added a tile + * which we have a duplicate reference available. */ + (ubuf_pre->tiles[i]->users == 1))) { + if (ubuf_pre->tiles[i] != NULL) { + /* If we have a reference, re-use this single use tile for the post state. */ + BLI_assert(ubuf_pre->tiles[i]->users == 1); + ubuf_post->tiles[i] = ubuf_pre->tiles[i]; + ubuf_pre->tiles[i] = NULL; + utile_init_from_imbuf(ubuf_post->tiles[i], x, y, ibuf, tmpibuf); + } + else { + BLI_assert(ubuf_post->tiles[i] == NULL); + ubuf_post->tiles[i] = ubuf_reference->tiles[i]; + ubuf_post->tiles[i]->users += 1; + } + BLI_assert(ubuf_pre->tiles[i] == NULL); + ubuf_pre->tiles[i] = ubuf_reference->tiles[i]; + ubuf_pre->tiles[i]->users += 1; + + BLI_assert(ubuf_pre->tiles[i] != NULL); + BLI_assert(ubuf_post->tiles[i] != NULL); + } + else { + UndoImageTile *utile = utile_alloc(has_float); + utile_init_from_imbuf(utile, x, y, ibuf, tmpibuf); + + if (ubuf_pre->tiles[i] != NULL) { + ubuf_post->tiles[i] = utile; + utile->users = 1; + } + else { + ubuf_pre->tiles[i] = utile; + ubuf_post->tiles[i] = utile; + utile->users = 2; + } + } + BLI_assert(ubuf_pre->tiles[i] != NULL); + BLI_assert(ubuf_post->tiles[i] != NULL); + i += 1; + } + } + } + BKE_image_release_ibuf(uh->image_ref.ptr, ibuf, NULL); + } + } + + IMB_freeImBuf(tmpibuf); + + /* Useful to debug tiles are stored correctly. */ + if (false) { + uhandle_restore_list(&us->handles, false); + } + } + else { + /* Happens when switching modes. */ + ePaintMode paint_mode = BKE_paintmode_get_active_from_context(C); + BLI_assert(ELEM(paint_mode, PAINT_MODE_TEXTURE_2D, PAINT_MODE_TEXTURE_3D)); + us->paint_mode = paint_mode; + } + + us_p->is_applied = true; + + return true; +} + +static void image_undosys_step_decode_undo_impl(ImageUndoStep *us, bool is_final) +{ + BLI_assert(us->step.is_applied == true); + uhandle_restore_list(&us->handles, !is_final); + us->step.is_applied = false; +} + +static void image_undosys_step_decode_redo_impl(ImageUndoStep *us) +{ + BLI_assert(us->step.is_applied == false); + uhandle_restore_list(&us->handles, false); + us->step.is_applied = true; +} + +static void image_undosys_step_decode_undo(ImageUndoStep *us, bool is_final) +{ + ImageUndoStep *us_iter = us; + while (us_iter->step.next && (us_iter->step.next->type == us_iter->step.type)) { + if (us_iter->step.next->is_applied == false) { + break; + } + us_iter = (ImageUndoStep *)us_iter->step.next; + } + while (us_iter != us || (!is_final && us_iter == us)) { + + image_undosys_step_decode_undo_impl(us_iter, is_final); + if (us_iter == us) { + break; + } + us_iter = (ImageUndoStep *)us_iter->step.prev; + } +} + +static void image_undosys_step_decode_redo(ImageUndoStep *us) +{ + ImageUndoStep *us_iter = us; + while (us_iter->step.prev && (us_iter->step.prev->type == us_iter->step.type)) { + if (us_iter->step.prev->is_applied == true) { + break; + } + us_iter = (ImageUndoStep *)us_iter->step.prev; + } + while (us_iter && (us_iter->step.is_applied == false)) { + image_undosys_step_decode_redo_impl(us_iter); + if (us_iter == us) { + break; + } + us_iter = (ImageUndoStep *)us_iter->step.next; + } +} + +static void image_undosys_step_decode( + struct bContext *C, struct Main *bmain, UndoStep *us_p, int dir, bool is_final) +{ + ImageUndoStep *us = (ImageUndoStep *)us_p; + if (dir < 0) { + image_undosys_step_decode_undo(us, is_final); + } + else { + image_undosys_step_decode_redo(us); + } + + if (us->paint_mode == PAINT_MODE_TEXTURE_3D) { + ED_object_mode_set(C, OB_MODE_TEXTURE_PAINT); + } + + /* Refresh texture slots. */ + ED_editors_init_for_undo(bmain); +} + +static void image_undosys_step_free(UndoStep *us_p) +{ + ImageUndoStep *us = (ImageUndoStep *)us_p; + uhandle_free_list(&us->handles); + + /* Typically this list will have been cleared. */ + ptile_free_list(&us->paint_tiles); +} + +static void image_undosys_foreach_ID_ref(UndoStep *us_p, + UndoTypeForEachIDRefFn foreach_ID_ref_fn, + void *user_data) +{ + ImageUndoStep *us = (ImageUndoStep *)us_p; + for (UndoImageHandle *uh = us->handles.first; uh; uh = uh->next) { + foreach_ID_ref_fn(user_data, ((UndoRefID *)&uh->image_ref)); + } +} + +/* Export for ED_undo_sys. */ +void ED_image_undosys_type(UndoType *ut) +{ + ut->name = "Image"; + ut->poll = image_undosys_poll; + ut->step_encode_init = image_undosys_step_encode_init; + ut->step_encode = image_undosys_step_encode; + ut->step_decode = image_undosys_step_decode; + ut->step_free = image_undosys_step_free; + + ut->step_foreach_ID_ref = image_undosys_foreach_ID_ref; + + ut->use_context = true; + + ut->step_size = sizeof(ImageUndoStep); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Utilities + * \{ */ + +ListBase *ED_image_paint_tile_list_get(void) +{ + UndoStack *ustack = ED_undo_stack_get(); + UndoStep *us_prev = ustack->step_init; + UndoStep *us_p = BKE_undosys_stack_init_or_active_with_type(ustack, BKE_UNDOSYS_TYPE_IMAGE); + ImageUndoStep *us = (ImageUndoStep *)us_p; + /* We should always have an undo push started when accessing tiles, + * not doing this means we won't have paint_mode correctly set. */ + BLI_assert(us_p == us_prev); + if (us_p != us_prev) { + /* Fallback value until we can be sure this never happens. */ + us->paint_mode = PAINT_MODE_TEXTURE_2D; + } + return &us->paint_tiles; +} + +/* restore painting image to previous state. Used for anchored and drag-dot style brushes*/ +void ED_image_undo_restore(UndoStep *us) +{ + ListBase *paint_tiles = &((ImageUndoStep *)us)->paint_tiles; + ptile_restore_runtime_list(paint_tiles); + ptile_invalidate_list(paint_tiles); +} + +static ImageUndoStep *image_undo_push_begin(const char *name, int paint_mode) +{ + UndoStack *ustack = ED_undo_stack_get(); + 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)); + us->paint_mode = paint_mode; + return us; +} + +void ED_image_undo_push_begin(const char *name, int paint_mode) +{ + image_undo_push_begin(name, paint_mode); +} + +void ED_image_undo_push_begin_with_image(const char *name, Image *image, ImBuf *ibuf) +{ + ImageUndoStep *us = image_undo_push_begin(name, PAINT_MODE_TEXTURE_2D); + + UndoImageHandle *uh = uhandle_ensure(&us->handles, image); + UndoImageBuf *ubuf_pre = uhandle_ensure_ubuf(uh, image, ibuf); + BLI_assert(ubuf_pre->post == NULL); + + ImageUndoStep *us_reference = (ImageUndoStep *)ED_undo_stack_get()->step_active; + while (us_reference && us_reference->step.type != BKE_UNDOSYS_TYPE_IMAGE) { + us_reference = (ImageUndoStep *)us_reference->step.prev; + } + UndoImageBuf *ubuf_reference = (us_reference ? + ubuf_lookup_from_reference(us_reference, image, ubuf_pre) : + NULL); + + if (ubuf_reference) { + memcpy(ubuf_pre->tiles, ubuf_reference->tiles, sizeof(*ubuf_pre->tiles) * ubuf_pre->tiles_len); + for (uint i = 0; i < ubuf_pre->tiles_len; i++) { + UndoImageTile *utile = ubuf_pre->tiles[i]; + utile->users += 1; + } + } + else { + ubuf_from_image_all_tiles(ubuf_pre, ibuf); + } +} + +void ED_image_undo_push_end(void) +{ + UndoStack *ustack = ED_undo_stack_get(); + BKE_undosys_step_push(ustack, NULL, NULL); + WM_file_tag_modified(); +} + +/** \} */ diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index 5fa4fe3e077..a88ecc91868 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -207,6 +207,7 @@ static void image_operatortypes(void) WM_operatortype_append(IMAGE_OT_view_all); WM_operatortype_append(IMAGE_OT_view_pan); WM_operatortype_append(IMAGE_OT_view_selected); + WM_operatortype_append(IMAGE_OT_view_center_cursor); WM_operatortype_append(IMAGE_OT_view_zoom); WM_operatortype_append(IMAGE_OT_view_zoom_in); WM_operatortype_append(IMAGE_OT_view_zoom_out); @@ -229,6 +230,7 @@ static void image_operatortypes(void) WM_operatortype_append(IMAGE_OT_unpack); WM_operatortype_append(IMAGE_OT_invert); + WM_operatortype_append(IMAGE_OT_resize); WM_operatortype_append(IMAGE_OT_cycle_render_slot); WM_operatortype_append(IMAGE_OT_clear_render_slot); diff --git a/source/blender/editors/space_nla/nla_select.c b/source/blender/editors/space_nla/nla_select.c index 1abf1a64263..938e7f09881 100644 --- a/source/blender/editors/space_nla/nla_select.c +++ b/source/blender/editors/space_nla/nla_select.c @@ -597,13 +597,19 @@ void NLA_OT_select_leftright(wmOperatorType *ot) /* ******************** Mouse-Click Select Operator *********************** */ /* select strip directly under mouse */ -static void mouse_nla_strips( - bContext *C, bAnimContext *ac, const int mval[2], short select_mode, const bool deselect_all) +static int mouse_nla_strips(bContext *C, + bAnimContext *ac, + const int mval[2], + short select_mode, + const bool deselect_all, + bool wait_to_deselect_others) { Scene *scene = ac->scene; bAnimListElem *ale = NULL; NlaStrip *strip = NULL; + int ret_value = OPERATOR_FINISHED; + nlaedit_strip_at_region_position(ac, mval[0], mval[1], &ale, &strip); /* if currently in tweakmode, exit tweakmode before changing selection states @@ -613,6 +619,10 @@ static void mouse_nla_strips( WM_operator_name_call(C, "NLA_OT_tweakmode_exit", WM_OP_EXEC_DEFAULT, NULL); } + if (select_mode != SELECT_REPLACE) { + wait_to_deselect_others = false; + } + /* For replacing selection, if we have something to select, we have to clear existing selection. * The same goes if we found nothing to select, and deselect_all is true * (deselect on nothing behavior). */ @@ -620,11 +630,16 @@ static void mouse_nla_strips( /* reset selection mode for next steps */ select_mode = SELECT_ADD; - /* deselect all strips */ - deselect_nla_strips(ac, 0, SELECT_SUBTRACT); + if (strip && wait_to_deselect_others && (strip->flag & DESELECT_STRIPS_CLEARACTIVE)) { + ret_value = OPERATOR_RUNNING_MODAL; + } + else { + /* deselect all strips */ + deselect_nla_strips(ac, 0, SELECT_SUBTRACT); - /* deselect all other channels first */ - ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR); + /* deselect all other channels first */ + ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR); + } } /* only select strip if we clicked on a valid channel and hit something */ @@ -658,14 +673,17 @@ static void mouse_nla_strips( /* free this channel */ MEM_freeN(ale); } + + return ret_value; } /* ------------------- */ /* handle clicking */ -static int nlaedit_clickselect_invoke(bContext *C, wmOperator *op, const wmEvent *event) +static int nlaedit_clickselect_exec(bContext *C, wmOperator *op) { bAnimContext ac; + int ret_value; /* get editor data */ if (ANIM_animdata_get_context(C, &ac) == 0) { @@ -675,15 +693,19 @@ static int nlaedit_clickselect_invoke(bContext *C, wmOperator *op, const wmEvent /* select mode is either replace (deselect all, then add) or add/extend */ const short selectmode = RNA_boolean_get(op->ptr, "extend") ? SELECT_INVERT : SELECT_REPLACE; const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all"); + const bool wait_to_deselect_others = RNA_boolean_get(op->ptr, "wait_to_deselect_others"); + int mval[2]; + mval[0] = RNA_int_get(op->ptr, "mouse_x"); + mval[1] = RNA_int_get(op->ptr, "mouse_y"); /* select strips based upon mouse position */ - mouse_nla_strips(C, &ac, event->mval, selectmode, deselect_all); + ret_value = mouse_nla_strips(C, &ac, mval, selectmode, deselect_all, wait_to_deselect_others); /* set notifier that things have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_SELECTED, NULL); /* for tweak grab to work */ - return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH; + return ret_value | OPERATOR_PASS_THROUGH; } void NLA_OT_click_select(wmOperatorType *ot) @@ -695,14 +717,17 @@ void NLA_OT_click_select(wmOperatorType *ot) ot->idname = "NLA_OT_click_select"; ot->description = "Handle clicks to select NLA Strips"; - /* api callbacks - absolutely no exec() this yet... */ - ot->invoke = nlaedit_clickselect_invoke; + /* callbacks */ ot->poll = ED_operator_nla_active; + ot->exec = nlaedit_clickselect_exec; + ot->invoke = WM_generic_select_invoke; + ot->modal = WM_generic_select_modal; /* flags */ ot->flag = OPTYPE_UNDO; /* properties */ + WM_operator_properties_generic_select(ot); prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", ""); // SHIFTKEY RNA_def_property_flag(prop, PROP_SKIP_SAVE); diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index 93121a6bef9..a5b18ff7589 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -47,6 +47,7 @@ #include "GPU_batch.h" #include "GPU_batch_presets.h" +#include "GPU_platform.h" #include "GPU_immediate.h" #include "GPU_matrix.h" #include "GPU_state.h" @@ -815,51 +816,8 @@ static void node_shader_buts_tex_environment(uiLayout *layout, bContext *C, Poin static void node_shader_buts_tex_environment_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) { - PointerRNA imaptr = RNA_pointer_get(ptr, "image"); PointerRNA iuserptr = RNA_pointer_get(ptr, "image_user"); - Image *ima = imaptr.data; - - uiLayoutSetContextPointer(layout, "image_user", &iuserptr); - uiTemplateID(layout, - C, - ptr, - "image", - "IMAGE_OT_new", - "IMAGE_OT_open", - NULL, - UI_TEMPLATE_ID_FILTER_ALL, - false); - - if (!ima) { - return; - } - - uiItemR(layout, &imaptr, "source", 0, IFACE_("Source"), ICON_NONE); - - if (!(ELEM(ima->source, IMA_SRC_GENERATED, IMA_SRC_VIEWER))) { - uiLayout *row = uiLayoutRow(layout, true); - const bool is_packed = BKE_image_has_packedfile(ima); - - if (is_packed) { - uiItemO(row, "", ICON_PACKAGE, "image.unpack"); - } - else { - uiItemO(row, "", ICON_UGLYPACKAGE, "image.pack"); - } - - row = uiLayoutRow(row, true); - uiLayoutSetEnabled(row, !is_packed); - uiItemR(row, &imaptr, "filepath", 0, "", ICON_NONE); - uiItemO(row, "", ICON_FILE_REFRESH, "image.reload"); - } - - /* multilayer? */ - if (ima->type == IMA_TYPE_MULTILAYER && ima->rr) { - uiTemplateImageLayers(layout, C, ima, iuserptr.data); - } - else if (ima->source != IMA_SRC_GENERATED) { - uiTemplateImageInfo(layout, C, ima, iuserptr.data); - } + uiTemplateImage(layout, C, ptr, "image", &iuserptr, 0, 0); uiItemR(layout, ptr, "interpolation", 0, IFACE_("Interpolation"), ICON_NONE); uiItemR(layout, ptr, "projection", 0, IFACE_("Projection"), ICON_NONE); diff --git a/source/blender/editors/space_node/node_add.c b/source/blender/editors/space_node/node_add.c index 01a30f677a3..664349b3c3b 100644 --- a/source/blender/editors/space_node/node_add.c +++ b/source/blender/editors/space_node/node_add.c @@ -309,7 +309,7 @@ void NODE_OT_add_reroute(wmOperatorType *ot) prop = RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", ""); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); /* internal */ - RNA_def_int(ot->srna, "cursor", BC_CROSSCURSOR, 0, INT_MAX, "Cursor", "", 0, INT_MAX); + RNA_def_int(ot->srna, "cursor", WM_CURSOR_CROSS, 0, INT_MAX, "Cursor", "", 0, INT_MAX); } /* ****************** Add File Node Operator ******************* */ diff --git a/source/blender/editors/space_node/node_draw.c b/source/blender/editors/space_node/node_draw.c index 770e6dd2a9e..2081c69a1a4 100644 --- a/source/blender/editors/space_node/node_draw.c +++ b/source/blender/editors/space_node/node_draw.c @@ -1456,16 +1456,16 @@ static void node_draw_hidden(const bContext *C, int node_get_resize_cursor(int directions) { if (directions == 0) { - return CURSOR_STD; + return WM_CURSOR_DEFAULT; } else if ((directions & ~(NODE_RESIZE_TOP | NODE_RESIZE_BOTTOM)) == 0) { - return CURSOR_Y_MOVE; + return WM_CURSOR_Y_MOVE; } else if ((directions & ~(NODE_RESIZE_RIGHT | NODE_RESIZE_LEFT)) == 0) { - return CURSOR_X_MOVE; + return WM_CURSOR_X_MOVE; } else { - return CURSOR_EDIT; + return WM_CURSOR_EDIT; } } @@ -1474,7 +1474,7 @@ void node_set_cursor(wmWindow *win, SpaceNode *snode, float cursor[2]) bNodeTree *ntree = snode->edittree; bNode *node; bNodeSocket *sock; - int wmcursor = CURSOR_STD; + int wmcursor = WM_CURSOR_DEFAULT; if (ntree) { if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_IN | SOCK_OUT)) { diff --git a/source/blender/editors/space_node/node_group.c b/source/blender/editors/space_node/node_group.c index 588fc4d4307..5d020ff5ab4 100644 --- a/source/blender/editors/space_node/node_group.c +++ b/source/blender/editors/space_node/node_group.c @@ -241,6 +241,8 @@ static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode) node->flag |= NODE_SELECT; } + bNodeLink *glinks_first = ntree->links.last; + /* Add internal links to the ntree */ for (link = wgroup->links.first; link; link = linkn) { linkn = link->next; @@ -248,6 +250,8 @@ static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode) BLI_addtail(&ntree->links, link); } + bNodeLink *glinks_last = ntree->links.last; + /* and copy across the animation, * note that the animation data's action can be NULL here */ if (wgroup->adt) { @@ -280,70 +284,64 @@ static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode) BKE_id_free(bmain, wgroup); /* restore external links to and from the gnode */ - /* note: the nodes have been copied to intermediate wgroup first (so need to use new_node), - * then transferred to ntree (new_node pointers remain valid). - */ /* input links */ - for (link = ngroup->links.first; link; link = link->next) { - if (link->fromnode->type == NODE_GROUP_INPUT) { - const char *identifier = link->fromsock->identifier; - int num_external_links = 0; - - /* find external links to this input */ - for (tlink = ntree->links.first; tlink; tlink = tlink->next) { - if (tlink->tonode == gnode && STREQ(tlink->tosock->identifier, identifier)) { - nodeAddLink(ntree, - tlink->fromnode, - tlink->fromsock, - link->tonode->new_node, - link->tosock->new_sock); - num_external_links++; + if (glinks_first != NULL) { + for (link = glinks_first->next; link != glinks_last->next; link = link->next) { + if (link->fromnode->type == NODE_GROUP_INPUT) { + const char *identifier = link->fromsock->identifier; + int num_external_links = 0; + + /* find external links to this input */ + for (tlink = ntree->links.first; tlink != glinks_first->next; tlink = tlink->next) { + if (tlink->tonode == gnode && STREQ(tlink->tosock->identifier, identifier)) { + nodeAddLink(ntree, tlink->fromnode, tlink->fromsock, link->tonode, link->tosock); + num_external_links++; + } } - } - /* if group output is not externally linked, - * convert the constant input value to ensure somewhat consistent behavior */ - if (num_external_links == 0) { - /* XXX TODO bNodeSocket *sock = node_group_find_input_socket(gnode, identifier); - BLI_assert(sock);*/ + /* if group output is not externally linked, + * convert the constant input value to ensure somewhat consistent behavior */ + if (num_external_links == 0) { + /* XXX TODO bNodeSocket *sock = node_group_find_input_socket(gnode, identifier); + BLI_assert(sock);*/ - /* XXX TODO - * nodeSocketCopy(ntree, link->tosock->new_sock, link->tonode->new_node, - * ntree, sock, gnode);*/ + /* XXX TODO + * nodeSocketCopy(ntree, link->tosock->new_sock, link->tonode->new_node, + * ntree, sock, gnode);*/ + } } } - } - /* output links */ - for (link = ntree->links.first; link; link = link->next) { - if (link->fromnode == gnode) { - const char *identifier = link->fromsock->identifier; - int num_internal_links = 0; - - /* find internal links to this output */ - for (tlink = ngroup->links.first; tlink; tlink = tlink->next) { - /* only use active output node */ - if (tlink->tonode->type == NODE_GROUP_OUTPUT && (tlink->tonode->flag & NODE_DO_OUTPUT)) { - if (STREQ(tlink->tosock->identifier, identifier)) { - nodeAddLink(ntree, - tlink->fromnode->new_node, - tlink->fromsock->new_sock, - link->tonode, - link->tosock); - num_internal_links++; + /* Also iterate over new links to cover passthrough links. */ + glinks_last = ntree->links.last; + + /* output links */ + for (link = ntree->links.first; link != glinks_first->next; link = link->next) { + if (link->fromnode == gnode) { + const char *identifier = link->fromsock->identifier; + int num_internal_links = 0; + + /* find internal links to this output */ + for (tlink = glinks_first->next; tlink != glinks_last->next; tlink = tlink->next) { + /* only use active output node */ + if (tlink->tonode->type == NODE_GROUP_OUTPUT && (tlink->tonode->flag & NODE_DO_OUTPUT)) { + if (STREQ(tlink->tosock->identifier, identifier)) { + nodeAddLink(ntree, tlink->fromnode, tlink->fromsock, link->tonode, link->tosock); + num_internal_links++; + } } } - } - /* if group output is not internally linked, - * convert the constant output value to ensure somewhat consistent behavior */ - if (num_internal_links == 0) { - /* XXX TODO bNodeSocket *sock = node_group_find_output_socket(gnode, identifier); - BLI_assert(sock);*/ + /* if group output is not internally linked, + * convert the constant output value to ensure somewhat consistent behavior */ + if (num_internal_links == 0) { + /* XXX TODO bNodeSocket *sock = node_group_find_output_socket(gnode, identifier); + BLI_assert(sock);*/ - /* XXX TODO - * nodeSocketCopy(ntree, link->tosock, link->tonode, ntree, sock, gnode); */ + /* XXX TODO + * nodeSocketCopy(ntree, link->tosock, link->tonode, ntree, sock, gnode); */ + } } } } @@ -717,7 +715,7 @@ static void node_group_make_insert_selected(const bContext *C, bNodeTree *ntree, ListBase anim_basepaths = {NULL, NULL}; float min[2], max[2], center[2]; int totselect; - bool expose_all = false; + bool expose_visible = false; bNode *input_node, *output_node; /* XXX rough guess, not nice but we don't have access to UI constants here ... */ @@ -735,7 +733,7 @@ static void node_group_make_insert_selected(const bContext *C, bNodeTree *ntree, /* auto-add interface for "solo" nodes */ if (totselect == 1) { - expose_all = true; + expose_visible = true; } /* move nodes over */ @@ -879,8 +877,8 @@ static void node_group_make_insert_selected(const bContext *C, bNodeTree *ntree, } } - /* expose all unlinked sockets too */ - if (expose_all) { + /* expose all unlinked sockets too but only the visible ones*/ + if (expose_visible) { for (node = ngroup->nodes.first; node; node = node->next) { if (node_group_make_use_node(node, gnode)) { for (sock = node->inputs.first; sock; sock = sock->next) { @@ -892,6 +890,9 @@ static void node_group_make_insert_selected(const bContext *C, bNodeTree *ntree, break; } } + if (sock->flag & (SOCK_HIDDEN | SOCK_UNAVAIL)) { + skip = true; + } if (skip) { continue; } @@ -913,6 +914,9 @@ static void node_group_make_insert_selected(const bContext *C, bNodeTree *ntree, skip = true; } } + if (sock->flag & (SOCK_HIDDEN | SOCK_UNAVAIL)) { + skip = true; + } if (skip) { continue; } diff --git a/source/blender/editors/space_node/node_relationships.c b/source/blender/editors/space_node/node_relationships.c index eef3f85319c..357ef31c51f 100644 --- a/source/blender/editors/space_node/node_relationships.c +++ b/source/blender/editors/space_node/node_relationships.c @@ -1103,7 +1103,7 @@ void NODE_OT_links_cut(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); /* internal */ - RNA_def_int(ot->srna, "cursor", BC_KNIFECURSOR, 0, INT_MAX, "Cursor", "", 0, INT_MAX); + RNA_def_int(ot->srna, "cursor", WM_CURSOR_KNIFE, 0, INT_MAX, "Cursor", "", 0, INT_MAX); } /* ********************** Detach links operator ***************** */ diff --git a/source/blender/editors/space_node/node_select.c b/source/blender/editors/space_node/node_select.c index 450cf28cce1..e22ef389516 100644 --- a/source/blender/editors/space_node/node_select.c +++ b/source/blender/editors/space_node/node_select.c @@ -430,11 +430,9 @@ void node_select_single(bContext *C, bNode *node) } static int node_mouse_select(bContext *C, + wmOperator *op, const int mval[2], - const bool extend, - const bool socket_select, - const bool deselect_all, - const bool wait_to_deselect_others) + bool wait_to_deselect_others) { Main *bmain = CTX_data_main(C); SpaceNode *snode = CTX_wm_space_node(C); @@ -445,8 +443,15 @@ static int node_mouse_select(bContext *C, float cursor[2]; int ret_value = OPERATOR_CANCELLED; - /* Waiting to deselect others is only allowed for basic selection. */ - BLI_assert(!(extend || socket_select) || !wait_to_deselect_others); + 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; + } /* get mouse coordinates in view2d space */ UI_view2d_region_to_view(&ar->v2d, mval[0], mval[1], &cursor[0], &cursor[1]); @@ -552,115 +557,40 @@ static int node_mouse_select(bContext *C, static int node_select_exec(bContext *C, wmOperator *op) { - int mval[2]; + 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"); - 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"); - /* perform the select */ - const int ret_value = node_mouse_select(C, mval, extend, socket_select, deselect_all, false); + const int ret_value = node_mouse_select(C, op, mval, wait_to_deselect_others); /* allow tweak event to work too */ return ret_value | OPERATOR_PASS_THROUGH; } -static int node_select_modal(bContext *C, wmOperator *op, const wmEvent *event) -{ - const short init_event_type = (short)POINTER_AS_INT(op->customdata); - - /* 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"); - - 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) { - return node_select_exec(C, op); - } - - if (init_event_type == 0) { - if (event->val == KM_PRESS) { - const int ret_value = node_mouse_select(C, mval, extend, socket_select, deselect_all, true); - - op->customdata = POINTER_FROM_INT((int)event->type); - if (ret_value & OPERATOR_RUNNING_MODAL) { - WM_event_add_modal_handler(C, op); - } - return ret_value | OPERATOR_PASS_THROUGH; - } - else { - /* If we are in init phase, and cannot validate init of modal operations, - * just fall back to basic exec. - */ - return node_select_exec(C, op); - } - } - else if (event->type == init_event_type && event->val == KM_RELEASE) { - const int ret_value = node_mouse_select(C, mval, extend, socket_select, deselect_all, false); - return ret_value | OPERATOR_PASS_THROUGH; - } - else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { - const int drag_delta[2] = { - mval[0] - event->mval[0], - mval[1] - event->mval[1], - }; - /* If user moves mouse more than defined threshold, we consider select operator as - * finished. Otherwise, it is still running until we get an 'release' event. In any - * case, we pass through event, but select op is not finished yet. */ - if (WM_event_drag_test_with_delta(event, drag_delta)) { - return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH; - } - else { - /* Important not to return anything other than PASS_THROUGH here, - * otherwise it prevents underlying tweak detection code to work properly. */ - return OPERATOR_PASS_THROUGH; - } - } - - return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH; -} - -static int node_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - RNA_int_set(op->ptr, "mouse_x", event->mval[0]); - RNA_int_set(op->ptr, "mouse_y", event->mval[1]); - - op->customdata = POINTER_FROM_INT(0); - - return node_select_modal(C, op, event); -} - void NODE_OT_select(wmOperatorType *ot) { + PropertyRNA *prop; + /* identifiers */ ot->name = "Select"; ot->idname = "NODE_OT_select"; ot->description = "Select the node under the cursor"; /* api callbacks */ - ot->invoke = node_select_invoke; ot->exec = node_select_exec; - ot->modal = node_select_modal; + ot->invoke = WM_generic_select_invoke; + ot->modal = WM_generic_select_modal; ot->poll = ED_operator_node_active; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - PropertyRNA *prop; - RNA_def_int(ot->srna, "mouse_x", 0, INT_MIN, INT_MAX, "Mouse X", "", INT_MIN, INT_MAX); - RNA_def_int(ot->srna, "mouse_y", 0, INT_MIN, INT_MAX, "Mouse Y", "", INT_MIN, INT_MAX); + WM_operator_properties_generic_select(ot); RNA_def_boolean(ot->srna, "extend", false, "Extend", ""); RNA_def_boolean(ot->srna, "socket_select", false, "Socket Select", ""); prop = RNA_def_boolean(ot->srna, diff --git a/source/blender/editors/space_outliner/outliner_collections.c b/source/blender/editors/space_outliner/outliner_collections.c index 309446db83b..03606282fcd 100644 --- a/source/blender/editors/space_outliner/outliner_collections.c +++ b/source/blender/editors/space_outliner/outliner_collections.c @@ -965,7 +965,7 @@ static int collection_isolate_exec(bContext *C, wmOperator *op) LayerCollection *layer_collection = BLI_gsetIterator_getKey(&collections_to_edit_iter); if (extend) { - BKE_layer_collection_isolate(scene, view_layer, layer_collection, true); + BKE_layer_collection_isolate_global(scene, view_layer, layer_collection, true); } else { PointerRNA ptr; diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index a2ca3254b30..3b86e04308e 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -2419,7 +2419,6 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) case OB_GPENCIL: data.icon = ICON_OUTLINER_OB_GREASEPENCIL; break; - break; } } else { diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c index 943993cb810..c55140db46f 100644 --- a/source/blender/editors/space_outliner/outliner_edit.c +++ b/source/blender/editors/space_outliner/outliner_edit.c @@ -674,7 +674,7 @@ void OUTLINER_OT_id_remap(wmOperatorType *ot) PropertyRNA *prop; /* identifiers */ - ot->name = "Outliner ID data Remap"; + ot->name = "Outliner ID Data Remap"; ot->idname = "OUTLINER_OT_id_remap"; /* callbacks */ @@ -770,7 +770,7 @@ static int outliner_id_copy_exec(bContext *C, wmOperator *op) BLI_make_file_string("/", str, BKE_tempdir_base(), "copybuffer.blend"); BKE_copybuffer_save(bmain, str, op->reports); - BKE_reportf(op->reports, RPT_INFO, "Copied %d selected data-blocks", num_ids); + BKE_reportf(op->reports, RPT_INFO, "Copied %d selected data-block(s)", num_ids); return OPERATOR_FINISHED; } @@ -804,7 +804,7 @@ static int outliner_id_paste_exec(bContext *C, wmOperator *op) WM_event_add_notifier(C, NC_WINDOW, NULL); - BKE_reportf(op->reports, RPT_INFO, "%d data-blocks pasted", num_pasted); + BKE_reportf(op->reports, RPT_INFO, "%d data-block(s) pasted", num_pasted); return OPERATOR_FINISHED; } @@ -2257,7 +2257,7 @@ static int outliner_orphans_purge_exec(bContext *C, wmOperator *op) BKE_id_multi_tagged_delete(bmain); - BKE_reportf(op->reports, RPT_INFO, "Deleted %d data-blocks", num_tagged[INDEX_ID_NULL]); + BKE_reportf(op->reports, RPT_INFO, "Deleted %d data-block(s)", num_tagged[INDEX_ID_NULL]); /* XXX: tree management normally happens from draw_outliner(), but when * you're clicking to fast on Delete object from context menu in diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c index 44e67fa1508..f1e884adc3d 100644 --- a/source/blender/editors/space_outliner/outliner_select.c +++ b/source/blender/editors/space_outliner/outliner_select.c @@ -263,7 +263,8 @@ static void do_outliner_object_select_recursive(ViewLayer *view_layer, for (base = FIRSTBASE(view_layer); base; base = base->next) { Object *ob = base->object; - if ((((base->flag & BASE_VISIBLE) != 0) && BKE_object_is_child_recursive(ob_parent, ob))) { + if ((((base->flag & BASE_VISIBLE_DEPSGRAPH) != 0) && + BKE_object_is_child_recursive(ob_parent, ob))) { ED_object_base_select(base, select ? BA_SELECT : BA_DESELECT); } } @@ -1198,7 +1199,7 @@ static void do_outliner_item_activate_tree_element(bContext *C, Object *ob = (Object *)outliner_search_back(soops, te, ID_OB); if ((ob != NULL) && (ob->data == tselem->id)) { Base *base = BKE_view_layer_base_find(view_layer, ob); - if ((base != NULL) && (base->flag & BASE_VISIBLE)) { + if ((base != NULL) && (base->flag & BASE_VISIBLE_DEPSGRAPH)) { do_outliner_activate_obdata(C, scene, view_layer, base, extend); } } diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index fd6a052b84d..7f7cfff12ef 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -1365,7 +1365,7 @@ static void outliner_add_layer_collection_objects( TreeElement *te_object = outliner_add_element(soops, tree, base->object, ten, 0, 0); te_object->directdata = base; - if (!(base->flag & BASE_VISIBLE)) { + if (!(base->flag & BASE_VISIBLE_DEPSGRAPH)) { te_object->flag |= TE_DISABLED; } } @@ -1398,7 +1398,7 @@ static void outliner_add_layer_collections_recursive(SpaceOutliner *soops, tselem->flag &= ~TSE_CLOSED; } - if (exclude || (lc->runtime_flag & LAYER_COLLECTION_VISIBLE) == 0) { + if (exclude || (lc->runtime_flag & LAYER_COLLECTION_VISIBLE_VIEW_LAYER) == 0) { ten->flag |= TE_DISABLED; } } @@ -2085,12 +2085,12 @@ static bool outliner_element_visible_get(ViewLayer *view_layer, } if (exclude_filter & SO_FILTER_OB_STATE_VISIBLE) { - if ((base->flag & BASE_VISIBLE) == 0) { + if ((base->flag & BASE_VISIBLE_DEPSGRAPH) == 0) { return false; } } else if (exclude_filter & SO_FILTER_OB_STATE_HIDDEN) { - if ((base->flag & BASE_VISIBLE) != 0) { + if ((base->flag & BASE_VISIBLE_DEPSGRAPH) != 0) { return false; } } diff --git a/source/blender/editors/space_outliner/space_outliner.c b/source/blender/editors/space_outliner/space_outliner.c index 79880c68120..68eea4f278b 100644 --- a/source/blender/editors/space_outliner/space_outliner.c +++ b/source/blender/editors/space_outliner/space_outliner.c @@ -229,6 +229,11 @@ static void outliner_main_region_listener(wmWindow *UNUSED(win), ED_region_tag_redraw(ar); } break; + case NC_TEXT: + if (ELEM(wmn->action, NA_ADDED, NA_REMOVED)) { + ED_region_tag_redraw(ar); + } + break; } } diff --git a/source/blender/editors/space_script/script_edit.c b/source/blender/editors/space_script/script_edit.c index 48248fe1dd2..2be05785d2b 100644 --- a/source/blender/editors/space_script/script_edit.c +++ b/source/blender/editors/space_script/script_edit.c @@ -106,6 +106,7 @@ static bool script_test_modal_operators(bContext *C) static int script_reload_exec(bContext *C, wmOperator *op) { + #ifdef WITH_PYTHON /* clear running operators */ @@ -114,6 +115,8 @@ static int script_reload_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + WM_script_tag_reload(); + /* TODO, this crashes on netrender and keying sets, need to look into why * disable for now unless running in debug mode */ WM_cursor_wait(1); diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index b24f8e8d00f..8a15c05dd4a 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -336,7 +336,7 @@ static int sequencer_add_scene_strip_exec(bContext *C, wmOperator *op) } seq = BKE_sequence_alloc(ed->seqbasep, start_frame, channel, SEQ_TYPE_SCENE); - seq->blend_mode = SEQ_TYPE_ALPHAOVER; + seq->blend_mode = SEQ_TYPE_CROSS; /* so alpha adjustment fade to the strip below */ seq->scene = sce_seq; @@ -420,7 +420,7 @@ static int sequencer_add_movieclip_strip_exec(bContext *C, wmOperator *op) } seq = BKE_sequence_alloc(ed->seqbasep, start_frame, channel, SEQ_TYPE_MOVIECLIP); - seq->blend_mode = SEQ_TYPE_ALPHAOVER; + seq->blend_mode = SEQ_TYPE_CROSS; seq->clip = clip; id_us_ensure_real(&seq->clip->id); @@ -504,7 +504,7 @@ static int sequencer_add_mask_strip_exec(bContext *C, wmOperator *op) } seq = BKE_sequence_alloc(ed->seqbasep, start_frame, channel, SEQ_TYPE_MASK); - seq->blend_mode = SEQ_TYPE_ALPHAOVER; + seq->blend_mode = SEQ_TYPE_CROSS; seq->mask = mask; id_us_ensure_real(&seq->mask->id); @@ -1091,8 +1091,14 @@ static int sequencer_add_effect_strip_exec(bContext *C, wmOperator *op) if (seq->type == SEQ_TYPE_COLOR) { SolidColorVars *colvars = (SolidColorVars *)seq->effectdata; RNA_float_get_array(op->ptr, "color", colvars->col); + seq->blend_mode = SEQ_TYPE_CROSS; /* so alpha adjustment fade to the strip below */ + } + else if (seq->type == SEQ_TYPE_ADJUSTMENT) { + seq->blend_mode = SEQ_TYPE_CROSS; + } + else if (seq->type == SEQ_TYPE_TEXT) { + seq->blend_mode = SEQ_TYPE_ALPHAOVER; } - seq->blend_mode = SEQ_TYPE_ALPHAOVER; /* an unset channel is a special case where we automatically go above * the other strips. */ @@ -1169,7 +1175,7 @@ void SEQUENCER_OT_effect_strip_add(struct wmOperatorType *ot) RNA_def_enum(ot->srna, "type", sequencer_prop_effect_types, - SEQ_TYPE_ALPHAOVER, + SEQ_TYPE_CROSS, "Type", "Sequencer effect type"); RNA_def_float_vector(ot->srna, diff --git a/source/blender/editors/space_sequencer/sequencer_buttons.c b/source/blender/editors/space_sequencer/sequencer_buttons.c index 6740c2baad2..7bec913900d 100644 --- a/source/blender/editors/space_sequencer/sequencer_buttons.c +++ b/source/blender/editors/space_sequencer/sequencer_buttons.c @@ -117,5 +117,6 @@ void sequencer_buttons_register(ARegionType *art) pt->poll = metadata_panel_context_poll; pt->draw = metadata_panel_context_draw; pt->flag |= PNL_DEFAULT_CLOSED; + pt->order = 10; BLI_addtail(&art->paneltypes, pt); } diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index bf555e8fe09..1a788237e6e 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -334,7 +334,7 @@ static void drawseqwave(View2D *v2d, value2 = (1.0f - f) * value2 + f * waveform->data[p * 3 + 4]; } - if (fcu) { + if (fcu && !BKE_fcurve_is_empty(fcu)) { float evaltime = x1_offset + (i * stepsize); volume = evaluate_fcurve(fcu, evaltime); } diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 22b73c32bfe..865dfb45278 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -1600,6 +1600,7 @@ static int sequencer_slip_exec(bContext *C, wmOperator *op) 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; } else { @@ -1693,6 +1694,7 @@ static int sequencer_slip_modal(bContext *C, wmOperator *op, const wmEvent *even if (sa) { ED_area_status_text(sa, NULL); } + DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); return OPERATOR_FINISHED; } @@ -3894,7 +3896,7 @@ void SEQUENCER_OT_change_effect_type(struct wmOperatorType *ot) ot->prop = RNA_def_enum(ot->srna, "type", sequencer_prop_effect_types, - SEQ_TYPE_ALPHAOVER, + SEQ_TYPE_CROSS, "Type", "Sequencer effect type"); } diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index affb6d3fd88..4c20fc1707a 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -319,7 +319,7 @@ void SEQUENCER_OT_select_inverse(struct wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -static int sequencer_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) +static int sequencer_select_exec(bContext *C, wmOperator *op) { View2D *v2d = UI_view2d_fromcontext(C); Scene *scene = CTX_data_scene(C); @@ -328,7 +328,13 @@ static int sequencer_select_invoke(bContext *C, wmOperator *op, const wmEvent *e const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all"); const bool linked_handle = RNA_boolean_get(op->ptr, "linked_handle"); const bool linked_time = RNA_boolean_get(op->ptr, "linked_time"); + bool wait_to_deselect_others = RNA_boolean_get(op->ptr, "wait_to_deselect_others"); int left_right = RNA_enum_get(op->ptr, "left_right"); + int mval[2]; + int ret_value = OPERATOR_CANCELLED; + + mval[0] = RNA_int_get(op->ptr, "mouse_x"); + mval[1] = RNA_int_get(op->ptr, "mouse_y"); Sequence *seq, *neighbor, *act_orig; int hand, sel_side; @@ -338,9 +344,13 @@ static int sequencer_select_invoke(bContext *C, wmOperator *op, const wmEvent *e return OPERATOR_CANCELLED; } + if (extend) { + wait_to_deselect_others = false; + } + marker = find_nearest_marker(SCE_MARKERS, 1); // XXX - dummy function for now - seq = find_nearest_seq(scene, v2d, &hand, event->mval); + seq = find_nearest_seq(scene, v2d, &hand, mval); // XXX - not nice, Ctrl+RMB needs to do left_right only when not over a strip if (seq && linked_time && (left_right == SEQ_SELECT_LR_MOUSE)) { @@ -364,6 +374,8 @@ static int sequencer_select_invoke(bContext *C, wmOperator *op, const wmEvent *e /* deselect_markers(0, 0); */ marker->flag |= SELECT; } + + ret_value = OPERATOR_FINISHED; } else if (left_right != SEQ_SELECT_LR_NONE) { /* use different logic for this */ @@ -374,7 +386,7 @@ static int sequencer_select_invoke(bContext *C, wmOperator *op, const wmEvent *e switch (left_right) { case SEQ_SELECT_LR_MOUSE: - x = UI_view2d_region_to_view_x(v2d, event->mval[0]); + x = UI_view2d_region_to_view_x(v2d, mval[0]); break; case SEQ_SELECT_LR_LEFT: x = CFRA - 1.0f; @@ -409,13 +421,27 @@ static int sequencer_select_invoke(bContext *C, wmOperator *op, const wmEvent *e } } } + + ret_value = OPERATOR_FINISHED; } else { act_orig = ed->act_seq; if (seq) { - if (!extend && !linked_handle) { + /* Are we trying to select a handle that's already selected? */ + const bool handle_selected = ((hand == SEQ_SIDE_LEFT) && (seq->flag & SEQ_LEFTSEL)) || + ((hand == SEQ_SIDE_RIGHT) && (seq->flag & SEQ_RIGHTSEL)); + + if (wait_to_deselect_others && (seq->flag & SELECT) && + (hand == SEQ_SIDE_NONE || handle_selected)) { + ret_value = OPERATOR_RUNNING_MODAL; + } + else if (!extend && !linked_handle) { ED_sequencer_deselect_all(scene); + ret_value = OPERATOR_FINISHED; + } + else { + ret_value = OPERATOR_FINISHED; } BKE_sequencer_active_set(scene, seq); @@ -509,6 +535,8 @@ static int sequencer_select_invoke(bContext *C, wmOperator *op, const wmEvent *e select_active_side(ed->seqbasep, sel_side, seq->machine, seq->startdisp); } } + + ret_value = OPERATOR_FINISHED; } else { if (extend && (seq->flag & SELECT) && ed->act_seq == act_orig) { @@ -525,6 +553,7 @@ static int sequencer_select_invoke(bContext *C, wmOperator *op, const wmEvent *e seq->flag ^= SEQ_RIGHTSEL; break; } + ret_value = OPERATOR_FINISHED; } else { seq->flag |= SELECT; @@ -542,9 +571,12 @@ static int sequencer_select_invoke(bContext *C, wmOperator *op, const wmEvent *e if (linked_time) { select_linked_time(ed->seqbasep, seq); } + + BLI_assert((ret_value & OPERATOR_CANCELLED) == 0); } else if (deselect_all) { ED_sequencer_deselect_all(scene); + ret_value = OPERATOR_FINISHED; } } @@ -552,8 +584,7 @@ static int sequencer_select_invoke(bContext *C, wmOperator *op, const wmEvent *e WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); - /* allowing tweaks */ - return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH; + return ret_value; } void SEQUENCER_OT_select(wmOperatorType *ot) @@ -565,6 +596,7 @@ void SEQUENCER_OT_select(wmOperatorType *ot) {SEQ_SELECT_LR_RIGHT, "RIGHT", 0, "Right", "Select right"}, {0, NULL, 0, NULL, NULL}, }; + PropertyRNA *prop; /* identifiers */ ot->name = "Select"; @@ -572,14 +604,16 @@ void SEQUENCER_OT_select(wmOperatorType *ot) ot->description = "Select a strip (last selected becomes the \"active strip\")"; /* api callbacks */ - ot->invoke = sequencer_select_invoke; + ot->exec = sequencer_select_exec; + ot->invoke = WM_generic_select_invoke; + ot->modal = WM_generic_select_modal; ot->poll = ED_operator_sequencer_active; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - PropertyRNA *prop; + WM_operator_properties_generic_select(ot); RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the selection"); prop = RNA_def_boolean(ot->srna, "deselect_all", diff --git a/source/blender/editors/space_text/space_text.c b/source/blender/editors/space_text/space_text.c index 9f39313b9ab..088f06e9da8 100644 --- a/source/blender/editors/space_text/space_text.c +++ b/source/blender/editors/space_text/space_text.c @@ -311,11 +311,11 @@ static void text_main_region_draw(const bContext *C, ARegion *ar) static void text_cursor(wmWindow *win, ScrArea *sa, ARegion *ar) { SpaceText *st = sa->spacedata.first; - int wmcursor = BC_TEXTEDITCURSOR; + int wmcursor = WM_CURSOR_TEXT_EDIT; if (st->text && BLI_rcti_isect_pt(&st->txtbar, win->eventstate->x - ar->winrct.xmin, st->txtbar.ymin)) { - wmcursor = CURSOR_STD; + wmcursor = WM_CURSOR_DEFAULT; } WM_cursor_set(win, wmcursor); diff --git a/source/blender/editors/space_text/text_format_lua.c b/source/blender/editors/space_text/text_format_lua.c index 347d46a4234..935e288c7be 100644 --- a/source/blender/editors/space_text/text_format_lua.c +++ b/source/blender/editors/space_text/text_format_lua.c @@ -48,7 +48,7 @@ static int txtfmt_lua_find_keyword(const char *string) /* Keep aligned args for readability. */ /* clang-format off */ - if (STR_LITERAL_STARTSWITH(string, "and", len)) { i = len; + if (STR_LITERAL_STARTSWITH(string, "and", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "break", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "do", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "else", len)) { i = len; @@ -66,8 +66,7 @@ static int txtfmt_lua_find_keyword(const char *string) } else if (STR_LITERAL_STARTSWITH(string, "then", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "until", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "while", len)) { i = len; - } else { i = 0; -} + } else { i = 0; } /* clang-format on */ @@ -96,7 +95,7 @@ static int txtfmt_lua_find_specialvar(const char *string) /* Keep aligned args for readability. */ /* clang-format off */ - if (STR_LITERAL_STARTSWITH(string, "assert", len)) { i = len; + if (STR_LITERAL_STARTSWITH(string, "assert", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "collectgarbage", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "dofile", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "error", len)) { i = len; @@ -124,8 +123,7 @@ static int txtfmt_lua_find_specialvar(const char *string) } else if (STR_LITERAL_STARTSWITH(string, "unpack", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "_VERSION", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "xpcall", len)) { i = len; - } else { i = 0; -} + } else { i = 0; } /* clang-format on */ @@ -140,18 +138,13 @@ static int txtfmt_lua_find_bool(const char *string) { int i, len; - if (STR_LITERAL_STARTSWITH(string, "nil", len)) { - i = len; - } - else if (STR_LITERAL_STARTSWITH(string, "true", len)) { - i = len; - } - else if (STR_LITERAL_STARTSWITH(string, "false", len)) { - i = len; - } - else { - i = 0; - } + /* Keep aligned args for readability. */ + /* clang-format off */ + + if (STR_LITERAL_STARTSWITH(string, "nil", len)) { i = len; + } else if (STR_LITERAL_STARTSWITH(string, "true", len)) { i = len; + } else if (STR_LITERAL_STARTSWITH(string, "false", len)) { i = len; + } else { i = 0; } /* clang-format on */ @@ -169,10 +162,9 @@ static char txtfmt_lua_format_identifier(const char *str) /* Keep aligned args for readability. */ /* clang-format off */ - if ((txtfmt_lua_find_specialvar(str)) != -1) { fmt = FMT_TYPE_SPECIAL; + if ((txtfmt_lua_find_specialvar(str)) != -1) { fmt = FMT_TYPE_SPECIAL; } else if ((txtfmt_lua_find_keyword(str)) != -1) { fmt = FMT_TYPE_KEYWORD; - } else { fmt = FMT_TYPE_DEFAULT; -} + } else { fmt = FMT_TYPE_DEFAULT; } /* clang-format on */ diff --git a/source/blender/editors/space_text/text_format_osl.c b/source/blender/editors/space_text/text_format_osl.c index fb9ddcb09cb..2da4488e901 100644 --- a/source/blender/editors/space_text/text_format_osl.c +++ b/source/blender/editors/space_text/text_format_osl.c @@ -40,7 +40,7 @@ static int txtfmt_osl_find_builtinfunc(const char *string) /* list is from * https://github.com/imageworks/OpenShadingLanguage/raw/master/src/doc/osl-languagespec.pdf */ - if (STR_LITERAL_STARTSWITH(string, "break", len)) { i = len; + if (STR_LITERAL_STARTSWITH(string, "break", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "closure", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "color", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "continue", len)) { i = len; @@ -86,7 +86,7 @@ static int txtfmt_osl_find_reserved(const char *string) /* list is from... * https://github.com/imageworks/OpenShadingLanguage/raw/master/src/doc/osl-languagespec.pdf */ - if (STR_LITERAL_STARTSWITH(string, "bool", len)) { i = len; + if (STR_LITERAL_STARTSWITH(string, "bool", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "case", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "catch", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "char", len)) { i = len; @@ -149,7 +149,7 @@ static int txtfmt_osl_find_specialvar(const char *string) /* clang-format off */ /* OSL shader types */ - if (STR_LITERAL_STARTSWITH(string, "shader", len)) { i = len; + if (STR_LITERAL_STARTSWITH(string, "shader", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "surface", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "volume", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "displacement", len)) { i = len; @@ -189,7 +189,7 @@ static char txtfmt_osl_format_identifier(const char *str) /* Keep aligned args for readability. */ /* clang-format off */ - if ((txtfmt_osl_find_specialvar(str)) != -1) { fmt = FMT_TYPE_SPECIAL; + if ((txtfmt_osl_find_specialvar(str)) != -1) { fmt = FMT_TYPE_SPECIAL; } else if ((txtfmt_osl_find_builtinfunc(str)) != -1) { fmt = FMT_TYPE_KEYWORD; } else if ((txtfmt_osl_find_reserved(str)) != -1) { fmt = FMT_TYPE_RESERVED; } else if ((txtfmt_osl_find_preprocessor(str)) != -1) { fmt = FMT_TYPE_DIRECTIVE; @@ -323,7 +323,7 @@ static void txtfmt_osl_format_line(SpaceText *st, TextLine *line, const bool do_ /* Special vars(v) or built-in keywords(b) */ /* keep in sync with 'txtfmt_osl_format_identifier()' */ - if ((i = txtfmt_osl_find_specialvar(str)) != -1) { prev = FMT_TYPE_SPECIAL; + if ((i = txtfmt_osl_find_specialvar(str)) != -1) { prev = FMT_TYPE_SPECIAL; } else if ((i = txtfmt_osl_find_builtinfunc(str)) != -1) { prev = FMT_TYPE_KEYWORD; } else if ((i = txtfmt_osl_find_reserved(str)) != -1) { prev = FMT_TYPE_RESERVED; } else if ((i = txtfmt_osl_find_preprocessor(str)) != -1) { prev = FMT_TYPE_DIRECTIVE; diff --git a/source/blender/editors/space_text/text_format_pov.c b/source/blender/editors/space_text/text_format_pov.c index a5e1a3845cf..21df7b5b76a 100644 --- a/source/blender/editors/space_text/text_format_pov.c +++ b/source/blender/editors/space_text/text_format_pov.c @@ -48,7 +48,7 @@ static int txtfmt_pov_find_keyword(const char *string) int i, len; /* Language Directives */ - if (STR_LITERAL_STARTSWITH(string, "deprecated", len)) { i = len; + if (STR_LITERAL_STARTSWITH(string, "deprecated", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "persistent", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "statistics", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "version", len)) { i = len; @@ -101,7 +101,7 @@ static int txtfmt_pov_find_reserved_keywords(const char *string) /* clang-format off */ /* Float Functions */ - if (STR_LITERAL_STARTSWITH(string, "conserve_energy", len)) { i = len; + if (STR_LITERAL_STARTSWITH(string, "conserve_energy", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "max_intersections", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "dimension_size", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "bitwise_and", len)) { i = len; @@ -261,7 +261,7 @@ static int txtfmt_pov_find_reserved_builtins(const char *string) /* clang-format off */ /* Language Keywords */ - if (STR_LITERAL_STARTSWITH(string, "reflection_exponent", len)) { i = len; + if (STR_LITERAL_STARTSWITH(string, "reflection_exponent", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "area_illumination", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "all_intersections", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "cutaway_textures", len)) { i = len; @@ -500,7 +500,7 @@ static int txtfmt_pov_find_specialvar(const char *string) { int i, len; /* Modifiers */ - if (STR_LITERAL_STARTSWITH(string, "dispersion_samples", len)) { i = len; + if (STR_LITERAL_STARTSWITH(string, "dispersion_samples", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "projected_through", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "double_illuminate", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "expand_thresholds", len)) { i = len; @@ -710,7 +710,7 @@ static int txtfmt_pov_find_bool(const char *string) /* clang-format off */ /* Built-in Constants */ - if (STR_LITERAL_STARTSWITH(string, "unofficial", len)) { i = len; + if (STR_LITERAL_STARTSWITH(string, "unofficial", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "false", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "no", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "off", len)) { i = len; diff --git a/source/blender/editors/space_text/text_format_pov_ini.c b/source/blender/editors/space_text/text_format_pov_ini.c index 04f4b992cc6..b349b38e551 100644 --- a/source/blender/editors/space_text/text_format_pov_ini.c +++ b/source/blender/editors/space_text/text_format_pov_ini.c @@ -49,7 +49,7 @@ static int txtfmt_ini_find_keyword(const char *string) /* clang-format off */ /* Language Directives */ - if (STR_LITERAL_STARTSWITH(string, "deprecated", len)) { i = len; + if (STR_LITERAL_STARTSWITH(string, "deprecated", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "statistics", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "declare", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "default", len)) { i = len; @@ -111,7 +111,7 @@ static int txtfmt_ini_find_reserved(const char *string) * list is from... * http://www.povray.org/documentation/view/3.7.0/212/ */ - if (STR_LITERAL_STARTSWITH(string, "RenderCompleteSoundEnabled", len)) { i = len; + if (STR_LITERAL_STARTSWITH(string, "RenderCompleteSoundEnabled", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "Create_Continue_Trace_Log", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "ParseErrorSoundEnabled", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "RenderErrorSoundEnabled", len)) { i = len; @@ -321,7 +321,7 @@ static int txtfmt_ini_find_bool(const char *string) /* clang-format off */ /* Built-in Constants */ - if (STR_LITERAL_STARTSWITH(string, "false", len)) { i = len; + if (STR_LITERAL_STARTSWITH(string, "false", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "no", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "off", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "true", len)) { i = len; diff --git a/source/blender/editors/space_text/text_format_py.c b/source/blender/editors/space_text/text_format_py.c index 98eeee61c3a..d84beb79be6 100644 --- a/source/blender/editors/space_text/text_format_py.c +++ b/source/blender/editors/space_text/text_format_py.c @@ -58,11 +58,11 @@ static int txtfmt_py_find_builtinfunc(const char *string) /* Keep aligned args for readability. */ /* clang-format off */ - if (STR_LITERAL_STARTSWITH(string, "and", len)) { i = len; - } else if (STR_LITERAL_STARTSWITH(string, "as", len)) { i = len; - } else if (STR_LITERAL_STARTSWITH(string, "assert", len)) { i = len; + if (STR_LITERAL_STARTSWITH(string, "assert", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "async", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "await", len)) { i = len; + } else if (STR_LITERAL_STARTSWITH(string, "and", len)) { i = len; + } else if (STR_LITERAL_STARTSWITH(string, "as", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "break", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "continue", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "del", len)) { i = len; @@ -114,7 +114,7 @@ static int txtfmt_py_find_specialvar(const char *string) /* Keep aligned args for readability. */ /* clang-format off */ - if (STR_LITERAL_STARTSWITH(string, "def", len)) { i = len; + if (STR_LITERAL_STARTSWITH(string, "def", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "class", len)) { i = len; } else { i = 0; } @@ -155,7 +155,7 @@ static int txtfmt_py_find_bool(const char *string) /* Keep aligned args for readability. */ /* clang-format off */ - if (STR_LITERAL_STARTSWITH(string, "None", len)) { i = len; + if (STR_LITERAL_STARTSWITH(string, "None", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "True", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "False", len)) { i = len; } else { i = 0; diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c index caefb5070fb..f9557225b6b 100644 --- a/source/blender/editors/space_text/text_ops.c +++ b/source/blender/editors/space_text/text_ops.c @@ -443,6 +443,8 @@ static int text_reload_exec(bContext *C, wmOperator *op) text_drawcache_tag_update(CTX_wm_space_text(C), 1); WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text); + text->flags &= ~TXT_ISDIRTY; + /* return to scroll position */ st->top = orig_top; txt_screen_clamp(st, ar); @@ -3482,7 +3484,7 @@ static int text_find_and_replace(bContext *C, wmOperator *op, short mode) } else { if (!found) { - BKE_reportf(op->reports, RPT_ERROR, "Text not found: %s", st->findstr); + BKE_reportf(op->reports, RPT_WARNING, "Text not found: %s", st->findstr); } } diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index b32bb0baec9..c7f13ce0e51 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -311,13 +311,6 @@ static void view3d_free(SpaceLink *sl) MEM_freeN(vd->runtime.properties_storage); } - if (vd->fx_settings.ssao) { - MEM_freeN(vd->fx_settings.ssao); - } - if (vd->fx_settings.dof) { - MEM_freeN(vd->fx_settings.dof); - } - if (vd->shading.prop) { IDP_FreeProperty(vd->shading.prop); vd->shading.prop = NULL; @@ -341,19 +334,20 @@ static SpaceLink *view3d_duplicate(SpaceLink *sl) v3dn->runtime.properties_storage = NULL; } + v3dn->local_collections_uuid = 0; + v3dn->flag &= ~V3D_LOCAL_COLLECTIONS; + if (v3dn->shading.type == OB_RENDER) { v3dn->shading.type = OB_SOLID; } + if (v3dn->shading.prop) { + v3dn->shading.prop = IDP_CopyProperty(v3do->shading.prop); + } + /* copy or clear inside new stuff */ v3dn->runtime.properties_storage = NULL; - if (v3dn->fx_settings.dof) { - v3dn->fx_settings.dof = MEM_dupallocN(v3do->fx_settings.dof); - } - if (v3dn->fx_settings.ssao) { - v3dn->fx_settings.ssao = MEM_dupallocN(v3do->fx_settings.ssao); - } return (SpaceLink *)v3dn; } @@ -577,7 +571,7 @@ static void view3d_lightcache_update(bContext *C) Scene *scene = CTX_data_scene(C); - if (strcmp(scene->r.engine, RE_engine_id_BLENDER_EEVEE) != 0) { + if (!BKE_scene_uses_blender_eevee(scene)) { /* Only do auto bake if eevee is the active engine */ return; } @@ -1039,10 +1033,10 @@ static void view3d_main_region_cursor(wmWindow *win, ScrArea *sa, ARegion *ar) ViewLayer *view_layer = WM_window_get_active_view_layer(win); Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer); if (obedit) { - WM_cursor_set(win, CURSOR_EDIT); + WM_cursor_set(win, WM_CURSOR_EDIT); } else { - WM_cursor_set(win, CURSOR_STD); + WM_cursor_set(win, WM_CURSOR_DEFAULT); } } @@ -1093,6 +1087,9 @@ static void view3d_header_region_listener(wmWindow *UNUSED(win), if (wmn->data & ND_GPENCIL_EDITMODE) { ED_region_tag_redraw(ar); } + else if (wmn->action == NA_EDITED) { + ED_region_tag_redraw(ar); + } break; case NC_BRUSH: ED_region_tag_redraw(ar); @@ -1457,7 +1454,7 @@ static int view3d_context(const bContext *C, const char *member, bContextDataRes if (view_layer->basact) { Object *ob = view_layer->basact->object; /* if hidden but in edit mode, we still display, can happen with animation */ - if ((view_layer->basact->flag & BASE_VISIBLE) != 0 || (ob->mode & OB_MODE_EDIT)) { + if ((view_layer->basact->flag & BASE_VISIBLE_DEPSGRAPH) != 0 || (ob->mode & OB_MODE_EDIT)) { CTX_data_pointer_set(result, &scene->id, &RNA_ObjectBase, view_layer->basact); } } @@ -1469,7 +1466,8 @@ static int view3d_context(const bContext *C, const char *member, bContextDataRes if (view_layer->basact) { Object *ob = view_layer->basact->object; /* if hidden but in edit mode, we still display, can happen with animation */ - if ((view_layer->basact->flag & BASE_VISIBLE) != 0 || (ob->mode & OB_MODE_EDIT) != 0) { + if ((view_layer->basact->flag & BASE_VISIBLE_DEPSGRAPH) != 0 || + (ob->mode & OB_MODE_EDIT) != 0) { CTX_data_id_pointer_set(result, &ob->id); } } diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index aafd36a5bb8..3ee9755cb06 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -1422,18 +1422,24 @@ static void draw_grid_unit_name( { if (!rv3d->is_persp && RV3D_VIEW_IS_AXIS(rv3d->view)) { const char *grid_unit = NULL; + int font_id = BLF_default(); ED_view3d_grid_view_scale(scene, v3d, rv3d, &grid_unit); if (grid_unit) { char numstr[32] = ""; - UI_FontThemeColor(BLF_default(), TH_TEXT_HI); + UI_FontThemeColor(font_id, TH_TEXT_HI); if (v3d->grid != 1.0f) { BLI_snprintf(numstr, sizeof(numstr), "%s x %.4g", grid_unit, v3d->grid); } *yoffset -= U.widget_unit; + BLF_enable(font_id, BLF_SHADOW); + BLF_shadow(font_id, 5, (const float[4]){0.0f, 0.0f, 0.0f, 1.0f}); + BLF_shadow_offset(font_id, 1, -1); BLF_draw_default_ascii( xoffset, *yoffset, 0.0f, numstr[0] ? numstr : grid_unit, sizeof(numstr)); + + BLF_disable(font_id, BLF_SHADOW); } } } diff --git a/source/blender/editors/space_view3d/view3d_draw_legacy.c b/source/blender/editors/space_view3d/view3d_draw_legacy.c index f77a42fb1c1..17b575cedae 100644 --- a/source/blender/editors/space_view3d/view3d_draw_legacy.c +++ b/source/blender/editors/space_view3d/view3d_draw_legacy.c @@ -179,7 +179,7 @@ static void validate_object_select_id( return; } - if (obact_eval && ((obact_eval->base_flag & BASE_VISIBLE) != 0)) { + if (obact_eval && ((obact_eval->base_flag & BASE_VISIBLE_DEPSGRAPH) != 0)) { Base *base = BKE_view_layer_base_find(view_layer, obact); DRW_select_buffer_context_create(&base, 1, -1); } @@ -226,7 +226,7 @@ void ED_view3d_backbuf_depth_validate(ViewContext *vc) ARegion *ar = vc->ar; Object *obact_eval = DEG_get_evaluated_object(vc->depsgraph, vc->obact); - if (obact_eval && ((obact_eval->base_flag & BASE_VISIBLE) != 0)) { + if (obact_eval && ((obact_eval->base_flag & BASE_VISIBLE_DEPSGRAPH) != 0)) { GPUViewport *viewport = WM_draw_region_get_viewport(ar, 0); DRW_draw_depth_object(vc->ar, viewport, obact_eval); } @@ -383,6 +383,14 @@ void ED_view3d_datamask(const bContext *C, r_cddata_masks->lmask |= CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL; r_cddata_masks->vmask |= CD_MASK_ORCO; } + else if (v3d->shading.type == OB_SOLID) { + if (v3d->shading.color_type == V3D_SHADING_TEXTURE_COLOR) { + r_cddata_masks->lmask |= CD_MASK_MLOOPUV; + } + if (v3d->shading.color_type == V3D_SHADING_VERTEX_COLOR) { + r_cddata_masks->lmask |= CD_MASK_MLOOPCOL; + } + } if ((CTX_data_mode_enum(C) == CTX_MODE_EDIT_MESH) && (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_WEIGHT)) { diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 4bc74ab8fba..3ad194a5d2b 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -2997,7 +2997,7 @@ static int viewselected_exec(bContext *C, wmOperator *op) ok = paintface_minmax(ob_eval, min, max); } else if (ob_eval && (ob_eval->mode & OB_MODE_PARTICLE_EDIT)) { - ok = PE_minmax(scene, CTX_data_view_layer(C), min, max); + ok = PE_minmax(depsgraph, scene, CTX_data_view_layer(C), min, max); } else if (ob_eval && (ob_eval->mode & (OB_MODE_SCULPT | OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT))) { diff --git a/source/blender/editors/space_view3d/view3d_gizmo_armature.c b/source/blender/editors/space_view3d/view3d_gizmo_armature.c index 615589347da..dbad06da5ec 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_armature.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_armature.c @@ -25,6 +25,7 @@ #include "BKE_armature.h" #include "BKE_action.h" #include "BKE_context.h" +#include "BKE_layer.h" #include "BKE_object.h" #include "DNA_object_types.h" diff --git a/source/blender/editors/space_view3d/view3d_gizmo_camera.c b/source/blender/editors/space_view3d/view3d_gizmo_camera.c index 42931d5abb5..ba5ca5fbd15 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_camera.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_camera.c @@ -24,6 +24,7 @@ #include "BKE_camera.h" #include "BKE_context.h" +#include "BKE_layer.h" #include "DNA_object_types.h" #include "DNA_camera_types.h" diff --git a/source/blender/editors/space_view3d/view3d_gizmo_empty.c b/source/blender/editors/space_view3d/view3d_gizmo_empty.c index b37f1e41294..793aec42dcd 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_empty.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_empty.c @@ -22,6 +22,7 @@ #include "BLI_utildefines.h" #include "BKE_context.h" +#include "BKE_layer.h" #include "BKE_object.h" #include "BKE_image.h" diff --git a/source/blender/editors/space_view3d/view3d_gizmo_forcefield.c b/source/blender/editors/space_view3d/view3d_gizmo_forcefield.c index 44ad1d14dba..90b1539c8a7 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_forcefield.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_forcefield.c @@ -21,6 +21,7 @@ #include "BLI_utildefines.h" #include "BKE_context.h" +#include "BKE_layer.h" #include "BKE_object.h" #include "DNA_object_types.h" diff --git a/source/blender/editors/space_view3d/view3d_gizmo_light.c b/source/blender/editors/space_view3d/view3d_gizmo_light.c index 35677b2e4c2..890de0ae611 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_light.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_light.c @@ -22,6 +22,7 @@ #include "BLI_utildefines.h" #include "BKE_context.h" +#include "BKE_layer.h" #include "BKE_object.h" #include "DEG_depsgraph.h" diff --git a/source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c b/source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c index b5b924c7f4a..d6d3a3dc563 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c @@ -518,9 +518,9 @@ static int gizmo_axis_test_select(bContext *UNUSED(C), wmGizmo *gz, const int mv static int gizmo_axis_cursor_get(wmGizmo *gz) { if (gz->highlight_part > 0) { - return CURSOR_EDIT; + return WM_CURSOR_EDIT; } - return BC_NSEW_SCROLLCURSOR; + return WM_CURSOR_NSEW_SCROLL; } void VIEW3D_GT_navigate_rotate(wmGizmoType *gzt) diff --git a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c index 2189191ad53..a5b7fac624d 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c @@ -407,6 +407,17 @@ static bool view3d_ruler_item_mousemove(RulerInfo *ruler_info, /** \name Ruler/Grease Pencil Conversion * \{ */ +/* Helper: Find the layer created as ruler. */ +static bGPDlayer *view3d_ruler_layer_get(bGPdata *gpd) +{ + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + if (gpl->flag & GP_LAYER_IS_RULER) { + return gpl; + } + } + return NULL; +} + #define RULER_ID "RulerData3D" static bool view3d_ruler_to_gpencil(bContext *C, wmGizmoGroup *gzgroup) { @@ -427,12 +438,12 @@ static bool view3d_ruler_to_gpencil(bContext *C, wmGizmoGroup *gzgroup) } gpd = scene->gpd; - gpl = BLI_findstring(&gpd->layers, ruler_name, offsetof(bGPDlayer, info)); + gpl = view3d_ruler_layer_get(gpd); if (gpl == NULL) { gpl = BKE_gpencil_layer_addnew(gpd, ruler_name, false); copy_v4_v4(gpl->color, U.gpencil_new_layer_col); gpl->thickness = 1; - gpl->flag |= GP_LAYER_HIDE; + gpl->flag |= GP_LAYER_HIDE | GP_LAYER_IS_RULER; } gpf = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_ADD_NEW); @@ -485,8 +496,7 @@ static bool view3d_ruler_from_gpencil(const bContext *C, wmGizmoGroup *gzgroup) if (scene->gpd) { bGPDlayer *gpl; - const char *ruler_name = RULER_ID; - gpl = BLI_findstring(&scene->gpd->layers, ruler_name, offsetof(bGPDlayer, info)); + gpl = view3d_ruler_layer_get(scene->gpd); if (gpl) { bGPDframe *gpf; gpf = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_USE_PREV); @@ -1013,9 +1023,9 @@ static void gizmo_ruler_exit(bContext *C, wmGizmo *gz, const bool cancel) static int gizmo_ruler_cursor_get(wmGizmo *gz) { if (gz->highlight_part == PART_LINE) { - return BC_CROSSCURSOR; + return WM_CURSOR_CROSS; } - return BC_NSEW_SCROLLCURSOR; + return WM_CURSOR_NSEW_SCROLL; } void VIEW3D_GT_ruler_item(wmGizmoType *gzt) diff --git a/source/blender/editors/space_view3d/view3d_ops.c b/source/blender/editors/space_view3d/view3d_ops.c index e5a145b0411..cfdd3dcbb6f 100644 --- a/source/blender/editors/space_view3d/view3d_ops.c +++ b/source/blender/editors/space_view3d/view3d_ops.c @@ -79,7 +79,7 @@ static int view3d_copybuffer_exec(bContext *C, wmOperator *op) BLI_make_file_string("/", str, BKE_tempdir_base(), "copybuffer.blend"); BKE_copybuffer_save(bmain, str, op->reports); - BKE_reportf(op->reports, RPT_INFO, "Copied %d selected objects", num_copied); + BKE_reportf(op->reports, RPT_INFO, "Copied %d selected object(s)", num_copied); return OPERATOR_FINISHED; } @@ -118,7 +118,7 @@ static int view3d_pastebuffer_exec(bContext *C, wmOperator *op) WM_event_add_notifier(C, NC_WINDOW, NULL); - BKE_reportf(op->reports, RPT_INFO, "%d objects pasted", num_pasted); + BKE_reportf(op->reports, RPT_INFO, "%d object(s) pasted", num_pasted); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index bcffad454fe..3eee76277e8 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -120,13 +120,13 @@ float ED_view3d_select_dist_px(void) } /* TODO: should return whether there is valid context to continue */ -void ED_view3d_viewcontext_init(bContext *C, ViewContext *vc) +void ED_view3d_viewcontext_init(bContext *C, ViewContext *vc, Depsgraph *depsgraph) { memset(vc, 0, sizeof(ViewContext)); vc->C = C; vc->ar = CTX_wm_region(C); vc->bmain = CTX_data_main(C); - vc->depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + vc->depsgraph = depsgraph; vc->scene = CTX_data_scene(C); vc->view_layer = CTX_data_view_layer(C); vc->v3d = CTX_wm_view3d(C); @@ -1020,10 +1020,12 @@ static void do_lasso_select_armature__doSelectBone(void *userData, is_ignore_flag |= BONESEL_TIP; } - if (is_inside_flag == (BONE_ROOTSEL | BONE_TIPSEL) || - BLI_lasso_is_edge_inside( - data->mcords, data->moves, UNPACK2(screen_co_a), UNPACK2(screen_co_b), INT_MAX)) { - is_inside_flag |= BONESEL_BONE; + if (is_ignore_flag == 0) { + if (is_inside_flag == (BONE_ROOTSEL | BONE_TIPSEL) || + BLI_lasso_is_edge_inside( + data->mcords, data->moves, UNPACK2(screen_co_a), UNPACK2(screen_co_b), INT_MAX)) { + is_inside_flag |= BONESEL_BONE; + } } ebone->temp.i = is_inside_flag | (is_ignore_flag >> 16); @@ -1344,11 +1346,12 @@ static int view3d_lasso_select_exec(bContext *C, wmOperator *op) const int(*mcords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcords_tot); if (mcords) { + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); view3d_operator_needs_opengl(C); BKE_object_update_select_id(CTX_data_main(C)); /* setup view context for argument to callbacks */ - ED_view3d_viewcontext_init(C, &vc); + ED_view3d_viewcontext_init(C, &vc, depsgraph); eSelectOp sel_op = RNA_enum_get(op->ptr, "mode"); bool changed_multi = view3d_lasso_select(C, &vc, mcords, mcords_tot, sel_op); @@ -1889,6 +1892,7 @@ static Base *mouse_select_eval_buffer(ViewContext *vc, /* mval comes from event->mval, only use within region handlers */ Base *ED_view3d_give_base_under_cursor(bContext *C, const int mval[2]) { + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewContext vc; Base *basact = NULL; uint buffer[MAXPICKBUF]; @@ -1897,7 +1901,7 @@ Base *ED_view3d_give_base_under_cursor(bContext *C, const int mval[2]) view3d_operator_needs_opengl(C); BKE_object_update_select_id(CTX_data_main(C)); - ED_view3d_viewcontext_init(C, &vc); + ED_view3d_viewcontext_init(C, &vc, depsgraph); const bool do_nearest = !XRAY_ACTIVE(vc.v3d); const int hits = mixed_bones_object_selectbuffer( @@ -1955,9 +1959,10 @@ static bool ed_object_select_pick(bContext *C, bool enumerate, bool object) { + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewContext vc; /* setup view context for argument to callbacks */ - ED_view3d_viewcontext_init(C, &vc); + ED_view3d_viewcontext_init(C, &vc, depsgraph); ARegion *ar = CTX_wm_region(C); Scene *scene = CTX_data_scene(C); @@ -3203,6 +3208,7 @@ static bool do_pose_box_select(bContext *C, ViewContext *vc, rcti *rect, const e static int view3d_box_select_exec(bContext *C, wmOperator *op) { + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewContext vc; rcti rect; bool changed_multi = false; @@ -3214,7 +3220,7 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op) BKE_object_update_select_id(CTX_data_main(C)); /* setup view context for argument to callbacks */ - ED_view3d_viewcontext_init(C, &vc); + ED_view3d_viewcontext_init(C, &vc, depsgraph); eSelectOp sel_op = RNA_enum_get(op->ptr, "mode"); WM_operator_properties_border_to_rcti(op, &rect); @@ -4004,6 +4010,7 @@ static bool object_circle_select(ViewContext *vc, /* not a real operator, only for circle test */ static int view3d_circle_select_exec(bContext *C, wmOperator *op) { + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewContext vc; const int radius = RNA_int_get(op->ptr, "radius"); const int mval[2] = {RNA_int_get(op->ptr, "x"), RNA_int_get(op->ptr, "y")}; @@ -4016,7 +4023,7 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op) const eSelectOp sel_op = ED_select_op_modal(RNA_enum_get(op->ptr, "mode"), WM_gesture_is_modal_first(gesture)); - ED_view3d_viewcontext_init(C, &vc); + ED_view3d_viewcontext_init(C, &vc, depsgraph); Object *obact = vc.obact; Object *obedit = vc.obedit; diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c index e5e1b1297f6..d7af307bc53 100644 --- a/source/blender/editors/space_view3d/view3d_view.c +++ b/source/blender/editors/space_view3d/view3d_view.c @@ -196,8 +196,8 @@ void ED_view3d_smooth_view_ex( sms.to_camera = true; /* restore view3d values in end */ } - /* skip smooth viewing for render engine draw */ - if (smooth_viewtx && v3d->shading.type != OB_RENDER) { + /* skip smooth viewing for external render engine draw */ + if (smooth_viewtx && !(v3d->shading.type == OB_RENDER && rv3d->render_engine)) { bool changed = false; /* zero means no difference */ if (sview->camera_old != sview->camera) { @@ -508,7 +508,7 @@ static bool view3d_camera_to_view_poll(bContext *C) void VIEW3D_OT_camera_to_view(wmOperatorType *ot) { /* identifiers */ - ot->name = "Align Camera To View"; + ot->name = "Align Camera to View"; ot->description = "Set camera view to active view"; ot->idname = "VIEW3D_OT_camera_to_view"; @@ -1583,7 +1583,13 @@ static uint free_localcollection_bit(Main *bmain, static void local_collections_reset_uuid(LayerCollection *layer_collection, const unsigned short local_view_bit) { - layer_collection->local_collections_bits |= local_view_bit; + if (layer_collection->flag & LAYER_COLLECTION_HIDE) { + layer_collection->local_collections_bits &= ~local_view_bit; + } + else { + layer_collection->local_collections_bits |= local_view_bit; + } + LISTBASE_FOREACH (LayerCollection *, child, &layer_collection->layer_collections) { local_collections_reset_uuid(child, local_view_bit); } diff --git a/source/blender/editors/space_view3d/view3d_walk.c b/source/blender/editors/space_view3d/view3d_walk.c index 91c05f5cac6..a7402a622d5 100644 --- a/source/blender/editors/space_view3d/view3d_walk.c +++ b/source/blender/editors/space_view3d/view3d_walk.c @@ -204,6 +204,8 @@ typedef struct WalkInfo { * (this would need to un-key all previous frames). */ bool anim_playing; + bool need_rotation_keyframe; + bool need_translation_keyframe; /** Previous 2D mouse values. */ int prev_mval[2]; @@ -538,6 +540,8 @@ static bool initWalkInfo(bContext *C, WalkInfo *walk, wmOperator *op) #endif walk->anim_playing = ED_screen_animation_playing(wm); + walk->need_rotation_keyframe = false; + walk->need_translation_keyframe = false; walk->time_lastdraw = PIL_check_seconds_timer(); @@ -577,7 +581,7 @@ static bool initWalkInfo(bContext *C, WalkInfo *walk, wmOperator *op) walk->ar->winrct.ymin + walk->center_mval[1]); /* remove the mouse cursor temporarily */ - WM_cursor_modal_set(win, CURSOR_NONE); + WM_cursor_modal_set(win, WM_CURSOR_NONE); return 1; } @@ -930,9 +934,12 @@ static void walkMoveCamera(bContext *C, /* we only consider autokeying on playback or if user confirmed walk on the same frame * otherwise we get a keyframe even if the user cancels. */ const bool use_autokey = is_confirm || walk->anim_playing; - ED_view3d_cameracontrol_update( walk->v3d_camera_control, use_autokey, C, do_rotate, do_translate); + if (use_autokey) { + walk->need_rotation_keyframe = false; + walk->need_translation_keyframe = false; + } } static float getFreeFallDistance(const float gravity, const float time) @@ -1280,9 +1287,10 @@ static int walkApply(bContext *C, WalkInfo *walk, bool is_confirm) add_v3_v3(rv3d->ofs, dvec_tmp); if (rv3d->persp == RV3D_CAMOB) { - const bool do_rotate = (moffset[0] || moffset[1]); - const bool do_translate = (walk->speed != 0.0f); - walkMoveCamera(C, walk, do_rotate, do_translate, is_confirm); + walk->need_rotation_keyframe |= (moffset[0] || moffset[1]); + walk->need_translation_keyframe |= (len_squared_v3(dvec_tmp) > FLT_EPSILON); + walkMoveCamera( + C, walk, walk->need_rotation_keyframe, walk->need_translation_keyframe, is_confirm); } } else { @@ -1322,7 +1330,10 @@ static void walkApply_ndof(bContext *C, WalkInfo *walk, bool is_confirm) walk->redraw = true; if (walk->rv3d->persp == RV3D_CAMOB) { - walkMoveCamera(C, walk, has_rotate, has_translate, is_confirm); + walk->need_rotation_keyframe |= has_rotate; + walk->need_translation_keyframe |= has_translate; + walkMoveCamera( + C, walk, walk->need_rotation_keyframe, walk->need_translation_keyframe, is_confirm); } } } diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index a95c7cf7456..b98c14150d5 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -2299,7 +2299,7 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op) } } - if (t->options & CTX_SCULPT) { + if ((t->options & CTX_SCULPT) && !(t->options & CTX_PAINT_CURVE)) { ED_sculpt_end_transform(C); } @@ -2346,9 +2346,11 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve } } - Object *ob = CTX_data_active_object(C); - if (ob && ob->mode == OB_MODE_SCULPT && ob->sculpt) { - options |= CTX_SCULPT; + if (CTX_wm_view3d(C) != NULL) { + Object *ob = CTX_data_active_object(C); + if (ob && ob->mode == OB_MODE_SCULPT && ob->sculpt) { + options |= CTX_SCULPT; + } } t->options = options; diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index b90fff29a84..ff2afbc0cd7 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -267,7 +267,6 @@ typedef struct TransDataNla { int handle; } TransDataNla; -struct GHash; struct LinkNode; /* header of TransDataEdgeSlideVert, TransDataEdgeSlideEdge */ diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index 1e783e0e7b8..64ad8b2091e 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -429,7 +429,7 @@ static void bone_children_clear_transflag(int mode, short around, ListBase *lb) bone->flag |= BONE_TRANSFORM_CHILD; } else { - bone->flag &= ~(BONE_TRANSFORM | BONE_TRANSFORM_MIRROR); + bone->flag &= ~BONE_TRANSFORM; } bone_children_clear_transflag(mode, around, &bone->childbase); @@ -455,14 +455,14 @@ int count_set_pose_transflags(Object *ob, bone->flag |= BONE_TRANSFORM; } else { - bone->flag &= ~(BONE_TRANSFORM | BONE_TRANSFORM_MIRROR); + bone->flag &= ~BONE_TRANSFORM; } bone->flag &= ~BONE_HINGE_CHILD_TRANSFORM; bone->flag &= ~BONE_TRANSFORM_CHILD; } else { - bone->flag &= ~(BONE_TRANSFORM | BONE_TRANSFORM_MIRROR); + bone->flag &= ~BONE_TRANSFORM; } } @@ -1542,8 +1542,8 @@ void autokeyframe_pose(bContext *C, Scene *scene, Object *ob, int tmode, short t } for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) { - if (pchan->bone->flag & (BONE_TRANSFORM | BONE_TRANSFORM_MIRROR)) { - + if ((pchan->bone->flag & BONE_TRANSFORM) || + ((pose->flag & POSE_MIRROR_EDIT) && (pchan->bone->flag & BONE_TRANSFORM_MIRROR))) { ListBase dsources = {NULL, NULL}; /* clear any 'unkeyed' flag it may have */ @@ -2237,9 +2237,10 @@ void special_aftertrans_update(bContext *C, TransInfo *t) /* Update motion paths once for all transformed bones in an object. */ GSetIterator gs_iter; GSET_ITER (gs_iter, motionpath_updates) { - bool current_frame_only = canceled; + const ePosePathCalcRange range = canceled ? POSE_PATH_CALC_RANGE_CURRENT_FRAME : + POSE_PATH_CALC_RANGE_CHANGED; ob = BLI_gsetIterator_getKey(&gs_iter); - ED_pose_recalculate_paths(C, t->scene, ob, current_frame_only); + ED_pose_recalculate_paths(C, t->scene, ob, range); } BLI_gset_free(motionpath_updates, NULL); } @@ -2250,7 +2251,7 @@ void special_aftertrans_update(bContext *C, TransInfo *t) /* pass */ } else if ((t->view_layer->basact) && (ob = t->view_layer->basact->object) && - (ob->mode & OB_MODE_PARTICLE_EDIT) && PE_get_current(t->scene, ob)) { + (ob->mode & OB_MODE_PARTICLE_EDIT) && PE_get_current(t->depsgraph, t->scene, ob)) { /* do nothing */ } else if (t->flag & T_CURSOR) { @@ -2320,8 +2321,9 @@ void special_aftertrans_update(bContext *C, TransInfo *t) if (motionpath_update) { /* Update motion paths once for all transformed objects. */ - bool current_frame_only = canceled; - ED_objects_recalculate_paths(C, t->scene, current_frame_only); + const eObjectPathCalcRange range = canceled ? OBJECT_PATH_CALC_RANGE_CURRENT_FRAME : + OBJECT_PATH_CALC_RANGE_CHANGED; + ED_objects_recalculate_paths(C, t->scene, range); } } @@ -2399,7 +2401,7 @@ void createTransData(bContext *C, TransInfo *t) } countAndCleanTransDataContainer(t); } - else if (t->options & CTX_SCULPT) { + else if ((t->options & CTX_SCULPT) && !(t->options & CTX_PAINT_CURVE)) { createTransSculpt(t); countAndCleanTransDataContainer(t); } @@ -2666,7 +2668,8 @@ void createTransData(bContext *C, TransInfo *t) } } } - else if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT) && PE_start_edit(PE_get_current(scene, ob))) { + else if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT) && + PE_start_edit(PE_get_current(t->depsgraph, scene, ob))) { createTransParticleVerts(C, t); countAndCleanTransDataContainer(t); t->flag |= T_POINTS; diff --git a/source/blender/editors/transform/transform_convert_graph.c b/source/blender/editors/transform/transform_convert_graph.c index 3eb4d220cb0..f3d7592127c 100644 --- a/source/blender/editors/transform/transform_convert_graph.c +++ b/source/blender/editors/transform/transform_convert_graph.c @@ -621,10 +621,10 @@ void flushTransGraphData(TransInfo *t) float inv_unit_scale = 1.0f / tdg->unit_scale; - /* handle snapping for time values - * - we should still be in NLA-mapping timespace - * - only apply to keyframes (but never to handles) - * - don't do this when canceling, or else these changes won't go away + /* Handle snapping for time values: + * - We should still be in NLA-mapping time-space. + * - Only apply to keyframes (but never to handles). + * - Don't do this when canceling, or else these changes won't go away. */ if ((t->state != TRANS_CANCEL) && (td->flag & TD_NOTIMESNAP) == 0) { switch (sipo->autosnap) { diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c index 9c46094ee83..f1928433491 100644 --- a/source/blender/editors/transform/transform_convert_mesh.c +++ b/source/blender/editors/transform/transform_convert_mesh.c @@ -1044,7 +1044,6 @@ void trans_mesh_customdata_correction_init(TransInfo *t) * accidentally break uv maps or vertex colors then */ (bm->shapenr <= 1) && (has_layer_math || (cd_loop_mdisp_offset != -1))) { use_origfaces = true; - cd_loop_mdisp_offset = cd_loop_mdisp_offset; } else { use_origfaces = false; @@ -1452,7 +1451,7 @@ void createTransUVs(bContext *C, TransInfo *t) const bool use_facesel = (ts->uv_flag & UV_SYNC_SELECTION) == 0; elementmap = BM_uv_element_map_create(em->bm, use_facesel, false, true); if (elementmap == NULL) { - return; + continue; } if (is_prop_connected) { diff --git a/source/blender/editors/transform/transform_convert_object.c b/source/blender/editors/transform/transform_convert_object.c index 6142e9628a4..6e85f6b9bf0 100644 --- a/source/blender/editors/transform/transform_convert_object.c +++ b/source/blender/editors/transform/transform_convert_object.c @@ -501,8 +501,12 @@ static void set_trans_object_base_deps_flag_cb(ID *id, static void flush_trans_object_base_deps_flag(Depsgraph *depsgraph, Object *object) { object->id.tag |= LIB_TAG_DOIT; - DEG_foreach_dependent_ID_component( - depsgraph, &object->id, DEG_OB_COMP_TRANSFORM, set_trans_object_base_deps_flag_cb, NULL); + DEG_foreach_dependent_ID_component(depsgraph, + &object->id, + DEG_OB_COMP_TRANSFORM, + DEG_FOREACH_COMPONENT_IGNORE_TRANSFORM_SOLVERS, + set_trans_object_base_deps_flag_cb, + NULL); } static void trans_object_base_deps_flag_finish(const TransInfo *t, ViewLayer *view_layer) @@ -930,11 +934,13 @@ void createTransTexspace(TransInfo *t) normalize_m3(td->axismtx); pseudoinverse_m3_m3(td->smtx, td->mtx, PSEUDOINVERSE_EPSILON); - if (BKE_object_obdata_texspace_get(ob, &texflag, &td->loc, &td->ext->size, &td->ext->rot)) { + if (BKE_object_obdata_texspace_get(ob, &texflag, &td->loc, &td->ext->size)) { ob->dtx |= OB_TEXSPACE; *texflag &= ~ME_AUTOSPACE; } + zero_v3(td->ext->rot); + copy_v3_v3(td->iloc, td->loc); copy_v3_v3(td->ext->irot, td->ext->rot); copy_v3_v3(td->ext->isize, td->ext->size); diff --git a/source/blender/editors/transform/transform_convert_particle.c b/source/blender/editors/transform/transform_convert_particle.c index 3b11e604cab..2a961da018b 100644 --- a/source/blender/editors/transform/transform_convert_particle.c +++ b/source/blender/editors/transform/transform_convert_particle.c @@ -51,7 +51,7 @@ void createTransParticleVerts(bContext *C, TransInfo *t) TransDataExtension *tx; Object *ob = CTX_data_active_object(C); ParticleEditSettings *pset = PE_settings(t->scene); - PTCacheEdit *edit = PE_get_current(t->scene, ob); + PTCacheEdit *edit = PE_get_current(t->depsgraph, t->scene, ob); ParticleSystem *psys = NULL; PTCacheEditPoint *point; PTCacheEditKey *key; @@ -200,7 +200,7 @@ void flushTransParticles(TransInfo *t) Scene *scene = t->scene; ViewLayer *view_layer = t->view_layer; Object *ob = OBACT(view_layer); - PTCacheEdit *edit = PE_get_current(scene, ob); + PTCacheEdit *edit = PE_get_current(t->depsgraph, scene, ob); ParticleSystem *psys = edit->psys; PTCacheEditPoint *point; PTCacheEditKey *key; diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index 76f699e3dc4..2e4f4344481 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -794,6 +794,12 @@ static void pose_transform_mirror_update(Object *ob, PoseInitData_Mirror *pid) for (bPoseChannel *pchan_orig = ob->pose->chanbase.first; pchan_orig; pchan_orig = pchan_orig->next) { + /* Clear the MIRROR flag from previous runs */ + pchan_orig->bone->flag &= ~BONE_TRANSFORM_MIRROR; + } + + for (bPoseChannel *pchan_orig = ob->pose->chanbase.first; pchan_orig; + pchan_orig = pchan_orig->next) { /* no layer check, correct mirror is more important */ if (pchan_orig->bone->flag & BONE_TRANSFORM) { bPoseChannel *pchan = BKE_pose_channel_get_mirrored(ob->pose, pchan_orig->name); @@ -1088,12 +1094,12 @@ static void recalcData_objects(TransInfo *t) GSetIterator gs_iter; GSET_ITER (gs_iter, motionpath_updates) { Object *ob = BLI_gsetIterator_getKey(&gs_iter); - ED_pose_recalculate_paths(t->context, t->scene, ob, true); + ED_pose_recalculate_paths(t->context, t->scene, ob, POSE_PATH_CALC_RANGE_CURRENT_FRAME); } BLI_gset_free(motionpath_updates, NULL); } else if (base && (base->object->mode & OB_MODE_PARTICLE_EDIT) && - PE_get_current(t->scene, base->object)) { + PE_get_current(t->depsgraph, t->scene, base->object)) { if (t->state != TRANS_CANCEL) { applyProject(t); } @@ -1146,7 +1152,7 @@ static void recalcData_objects(TransInfo *t) if (motionpath_update) { /* Update motion paths once for all transformed objects. */ - ED_objects_recalculate_paths(t->context, t->scene, true); + ED_objects_recalculate_paths(t->context, t->scene, OBJECT_PATH_CALC_RANGE_CHANGED); } if (t->options & CTX_OBMODE_XFORM_SKIP_CHILDREN) { @@ -1707,7 +1713,9 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve } } else { - if (ISMOUSE(t->launch_event) && (U.flag & USER_RELEASECONFIRM)) { + /* Release confirms preference should not affect node editor (T69288, T70504). */ + if (ISMOUSE(t->launch_event) && + ((U.flag & USER_RELEASECONFIRM) || (t->spacetype == SPACE_NODE))) { /* Global "release confirm" on mouse bindings */ t->flag |= T_RELEASE_CONFIRM; } @@ -1832,7 +1840,7 @@ static void freeTransCustomData(TransInfo *t, TransDataContainer *tc, TransCusto custom_data->data = NULL; } /* In case modes are switched in the same transform session. */ - custom_data->free_cb = false; + custom_data->free_cb = NULL; custom_data->use_free = false; } diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c index 25cf0fbbba2..157cf96a85e 100644 --- a/source/blender/editors/transform/transform_gizmo_3d.c +++ b/source/blender/editors/transform/transform_gizmo_3d.c @@ -516,9 +516,15 @@ static void protectflag_to_drawflags(short protectflag, short *drawflags) } /* for pose mode */ -static void protectflag_to_drawflags_pchan(RegionView3D *rv3d, const bPoseChannel *pchan) +static void protectflag_to_drawflags_pchan(RegionView3D *rv3d, + const bPoseChannel *pchan, + short orientation_type) { - protectflag_to_drawflags(pchan->protectflag, &rv3d->twdrawflag); + /* Protect-flags apply to local space in pose mode, so only let them influence axis + * visibility if we show the global orientation, otherwise it's confusing. */ + if (orientation_type == V3D_ORIENT_LOCAL) { + protectflag_to_drawflags(pchan->protectflag, &rv3d->twdrawflag); + } } /* for editmode*/ @@ -742,7 +748,14 @@ int ED_transform_calc_gizmo_stats(const bContext *C, bGPdata *gpd = CTX_data_gpencil_data(C); const bool is_gp_edit = GPENCIL_ANY_MODE(gpd); int a, totsel = 0; + const int pivot_point = scene->toolsettings->transform_pivot_point; + const short orientation_type = params->orientation_type ? + (params->orientation_type - 1) : + scene->orientation_slots[SCE_ORIENT_DEFAULT].type; + const short orientation_index_custom = + params->orientation_type ? params->orientation_index_custom : + scene->orientation_slots[SCE_ORIENT_DEFAULT].index_custom; /* transform widget matrix */ unit_m4(rv3d->twmat); @@ -756,12 +769,6 @@ int ED_transform_calc_gizmo_stats(const bContext *C, /* global, local or normal orientation? * if we could check 'totsel' now, this should be skipped with no selection. */ if (ob) { - const short orientation_type = params->orientation_type ? - (params->orientation_type - 1) : - scene->orientation_slots[SCE_ORIENT_DEFAULT].type; - const short orientation_index_custom = - params->orientation_type ? params->orientation_index_custom : - scene->orientation_slots[SCE_ORIENT_DEFAULT].index_custom; float mat[3][3]; ED_transform_calc_orientation_from_type_ex( C, mat, scene, rv3d, ob, obedit, orientation_type, orientation_index_custom, pivot_point); @@ -888,7 +895,7 @@ int ED_transform_calc_gizmo_stats(const bContext *C, calc_tw_center_with_matrix(tbounds, ebo->head, use_mat_local, mat_local); totsel++; } - if (ebo->flag & BONE_SELECTED) { + if (ebo->flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL)) { protectflag_to_drawflags_ebone(rv3d, ebo); } } @@ -1038,7 +1045,7 @@ int ED_transform_calc_gizmo_stats(const bContext *C, Bone *bone = pchan->bone; if (bone && (bone->flag & BONE_TRANSFORM)) { calc_tw_center_with_matrix(tbounds, pchan->pose_head, use_mat_local, mat_local); - protectflag_to_drawflags_pchan(rv3d, pchan); + protectflag_to_drawflags_pchan(rv3d, pchan, orientation_type); } } totsel += totsel_iter; @@ -1063,7 +1070,7 @@ int ED_transform_calc_gizmo_stats(const bContext *C, } } else if (ob && ob->mode & OB_MODE_PARTICLE_EDIT) { - PTCacheEdit *edit = PE_get_current(scene, ob); + PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob); PTCacheEditPoint *point; PTCacheEditKey *ek; int k; @@ -1122,7 +1129,12 @@ int ED_transform_calc_gizmo_stats(const bContext *C, calc_tw_center(tbounds, co); } } - protectflag_to_drawflags(base->object->protectflag, &rv3d->twdrawflag); + + /* Protect-flags apply to world space in object mode, so only let them influence axis + * visibility if we show the global orientation, otherwise it's confusing. */ + if (orientation_type == V3D_ORIENT_GLOBAL) { + protectflag_to_drawflags(base->object->protectflag, &rv3d->twdrawflag); + } totsel++; } @@ -1168,16 +1180,20 @@ static void gizmo_prepare_mat(const bContext *C, if (scene->toolsettings->transform_pivot_point == V3D_AROUND_ACTIVE) { bGPdata *gpd = CTX_data_gpencil_data(C); - Object *ob = OBACT(view_layer); if (gpd && (gpd->flag & GP_DATA_STROKE_EDITMODE)) { /* pass */ } - else if (ob->sculpt) { - SculptSession *ss = ob->sculpt; - copy_v3_v3(rv3d->twmat[3], ss->pivot_pos); - } - else if (ob != NULL) { - ED_object_calc_active_center(ob, false, rv3d->twmat[3]); + else { + Object *ob = OBACT(view_layer); + if (ob != NULL) { + if ((ob->mode & OB_MODE_ALL_SCULPT) && ob->sculpt) { + SculptSession *ss = ob->sculpt; + copy_v3_v3(rv3d->twmat[3], ss->pivot_pos); + } + else { + ED_object_calc_active_center(ob, false, rv3d->twmat[3]); + } + } } } break; @@ -1293,7 +1309,7 @@ static void gizmo_xform_message_subscribe(wmGizmoGroup *gzgroup, PointerRNA toolsettings_ptr; RNA_pointer_create(&scene->id, &RNA_ToolSettings, scene->toolsettings, &toolsettings_ptr); - if (type_fn == VIEW3D_GGT_xform_gizmo) { + if (ELEM(type_fn, VIEW3D_GGT_xform_gizmo, VIEW3D_GGT_xform_shear)) { extern PropertyRNA rna_ToolSettings_transform_pivot_point; const PropertyRNA *props[] = { &rna_ToolSettings_transform_pivot_point, @@ -1334,6 +1350,7 @@ static void gizmo_xform_message_subscribe(wmGizmoGroup *gzgroup, } WM_msg_subscribe_rna_anon_prop(mbus, Window, view_layer, &msg_sub_value_gz_tag_refresh); + WM_msg_subscribe_rna_anon_prop(mbus, EditBone, lock, &msg_sub_value_gz_tag_refresh); } void drawDial3d(const TransInfo *t) diff --git a/source/blender/editors/transform/transform_input.c b/source/blender/editors/transform/transform_input.c index e771fe43bd8..2821277ffa0 100644 --- a/source/blender/editors/transform/transform_input.c +++ b/source/blender/editors/transform/transform_input.c @@ -389,7 +389,7 @@ void initMouseInputMode(TransInfo *t, MouseInput *mi, MouseInputMode mode) /* INPUT_VECTOR, INPUT_CUSTOM_RATIO, INPUT_CUSTOM_RATIO_FLIP */ if (t->flag & T_MODAL) { t->flag |= T_MODAL_CURSOR_SET; - WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR); + WM_cursor_modal_set(win, WM_CURSOR_NSEW_SCROLL); } break; case HLP_SPRING: @@ -400,7 +400,7 @@ void initMouseInputMode(TransInfo *t, MouseInput *mi, MouseInputMode mode) case HLP_CARROW: if (t->flag & T_MODAL) { t->flag |= T_MODAL_CURSOR_SET; - WM_cursor_modal_set(win, CURSOR_NONE); + WM_cursor_modal_set(win, WM_CURSOR_NONE); } break; default: diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c index 1c264ba48ae..b2d8671fbce 100644 --- a/source/blender/editors/transform/transform_ops.c +++ b/source/blender/editors/transform/transform_ops.c @@ -302,11 +302,11 @@ static void TRANSFORM_OT_create_orientation(struct wmOperatorType *ot) WM_operatortype_props_advanced_begin(ot); RNA_def_boolean( - ot->srna, "use", false, "Use after creation", "Select orientation after its creation"); + ot->srna, "use", false, "Use After Creation", "Select orientation after its creation"); RNA_def_boolean(ot->srna, "overwrite", false, - "Overwrite previous", + "Overwrite Previous", "Overwrite previously created orientation with same name"); } diff --git a/source/blender/editors/transform/transform_orientations.c b/source/blender/editors/transform/transform_orientations.c index cbe9505d3f2..3159464072e 100644 --- a/source/blender/editors/transform/transform_orientations.c +++ b/source/blender/editors/transform/transform_orientations.c @@ -44,6 +44,7 @@ #include "BKE_curve.h" #include "BKE_context.h" #include "BKE_editmesh.h" +#include "BKE_layer.h" #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_workspace.h" diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c index 9c2642a46c1..f35a2808f22 100644 --- a/source/blender/editors/transform/transform_snap_object.c +++ b/source/blender/editors/transform/transform_snap_object.c @@ -47,6 +47,7 @@ #include "BKE_object.h" #include "BKE_anim.h" /* for duplis */ #include "BKE_editmesh.h" +#include "BKE_layer.h" #include "BKE_main.h" #include "BKE_tracking.h" #include "BKE_context.h" @@ -1717,7 +1718,7 @@ static short snapCurve(SnapData *snapdata, if (use_obedit == false) { /* Test BoundBox */ - BoundBox *bb = BKE_curve_texspace_get(cu, NULL, NULL, NULL); + BoundBox *bb = BKE_curve_boundbox_get(ob); if (bb && !snap_bound_box_check_dist( bb->vec[0], bb->vec[6], lpmat, snapdata->win_size, snapdata->mval, dist_px_sq)) { return 0; @@ -2367,6 +2368,7 @@ static short snapEditMesh(SnapObjectContext *sctx, if (treedata_vert && (snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX)) { BM_mesh_elem_table_ensure(em->bm, BM_VERT); + BM_mesh_elem_index_ensure(em->bm, BM_VERT); BLI_bvhtree_find_nearest_projected(treedata_vert->tree, lpmat, snapdata->win_size, @@ -2382,6 +2384,7 @@ static short snapEditMesh(SnapObjectContext *sctx, int last_index = nearest.index; nearest.index = -1; BM_mesh_elem_table_ensure(em->bm, BM_EDGE | BM_VERT); + BM_mesh_elem_index_ensure(em->bm, BM_EDGE | BM_VERT); BLI_bvhtree_find_nearest_projected(treedata_edge->tree, lpmat, snapdata->win_size, diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c index c2566d111cf..f5548119e0a 100644 --- a/source/blender/editors/util/ed_util.c +++ b/source/blender/editors/util/ed_util.c @@ -245,7 +245,7 @@ bool ED_editors_flush_edits(Main *bmain, bool for_render) * may cause a flush on saving: T53986. */ if ((ob->sculpt && ob->sculpt->cache) == 0) { /* flush multires changes (for sculpt) */ - multires_force_update(ob); + multires_flush_sculpt_updates(ob); has_edited = true; if (for_render) { diff --git a/source/blender/editors/uvedit/uvedit_draw.c b/source/blender/editors/uvedit/uvedit_draw.c index bd941968418..fafd54804c0 100644 --- a/source/blender/editors/uvedit/uvedit_draw.c +++ b/source/blender/editors/uvedit/uvedit_draw.c @@ -177,6 +177,7 @@ static void uvedit_get_batches(Object *ob, float *tot_area, float *tot_area_uv) { + float *tmp_tot_area, *tmp_tot_area_uv; int drawfaces = draw_uvs_face_check(scene->toolsettings); const bool draw_stretch = (sima->flag & SI_DRAW_STRETCH) != 0; const bool draw_faces = (sima->flag & SI_NO_DRAWFACES) == 0; @@ -193,7 +194,8 @@ static void uvedit_get_batches(Object *ob, } if (draw_stretch && (sima->dt_uvstretch == SI_UVDT_STRETCH_AREA)) { - batches->faces = DRW_mesh_batch_cache_get_edituv_faces_stretch_area(ob->data, NULL, NULL); + batches->faces = DRW_mesh_batch_cache_get_edituv_faces_stretch_area( + ob->data, &tmp_tot_area, &tmp_tot_area_uv); } else if (draw_stretch) { batches->faces = DRW_mesh_batch_cache_get_edituv_faces_stretch_angle(ob->data); @@ -207,11 +209,11 @@ static void uvedit_get_batches(Object *ob, DRW_mesh_batch_cache_create_requested(ob, ob->data, scene, false, false); - /* after create_requested we can load the actual areas */ - float tmp_tot_area, tmp_tot_area_uv; - DRW_mesh_batch_cache_get_edituv_faces_stretch_area(ob->data, &tmp_tot_area, &tmp_tot_area_uv); - *tot_area += tmp_tot_area; - *tot_area_uv += tmp_tot_area_uv; + if (draw_stretch && (sima->dt_uvstretch == SI_UVDT_STRETCH_AREA)) { + /* after create_requested we can load the actual areas */ + *tot_area += *tmp_tot_area; + *tot_area_uv += *tmp_tot_area_uv; + } } static void draw_uvs_shadow(SpaceImage *UNUSED(sima), diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index 57e2a84d248..0d258ba542b 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -4872,12 +4872,6 @@ static void UV_OT_reveal(wmOperatorType *ot) /** \name Set 2D Cursor Operator * \{ */ -static bool uv_set_2d_cursor_poll(bContext *C) -{ - return ED_operator_uvedit_space_image(C) || ED_space_image_maskedit_poll(C) || - ED_space_image_paint_curve(C); -} - static int uv_set_2d_cursor_exec(bContext *C, wmOperator *op) { SpaceImage *sima = CTX_wm_space_image(C); @@ -4923,7 +4917,7 @@ static void UV_OT_cursor_set(wmOperatorType *ot) /* api callbacks */ ot->exec = uv_set_2d_cursor_exec; ot->invoke = uv_set_2d_cursor_invoke; - ot->poll = uv_set_2d_cursor_poll; + ot->poll = ED_space_image_cursor_poll; /* properties */ RNA_def_float_vector(ot->srna, |