diff options
Diffstat (limited to 'source/blender/editors')
30 files changed, 703 insertions, 167 deletions
diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c index aec2b0f769a..00e2f221117 100644 --- a/source/blender/editors/animation/keyframes_general.c +++ b/source/blender/editors/animation/keyframes_general.c @@ -118,11 +118,13 @@ void clear_fcurve_keys(FCurve *fcu) /* ---------------- */ -void duplicate_fcurve_keys(FCurve *fcu) +bool duplicate_fcurve_keys(FCurve *fcu) { + bool changed = false; + /* this can only work when there is an F-Curve, and also when there are some BezTriples */ if (ELEM(NULL, fcu, fcu->bezt)) { - return; + return changed; } for (int i = 0; i < fcu->totvert; i++) { @@ -135,7 +137,7 @@ void duplicate_fcurve_keys(FCurve *fcu) memcpy(newbezt + i + 1, fcu->bezt + i, sizeof(BezTriple)); memcpy(newbezt + i + 2, fcu->bezt + i + 1, sizeof(BezTriple) * (fcu->totvert - (i + 1))); fcu->totvert++; - + changed = true; /* reassign pointers... (free old, and add new) */ MEM_freeN(fcu->bezt); fcu->bezt = newbezt; @@ -148,6 +150,7 @@ void duplicate_fcurve_keys(FCurve *fcu) BEZT_SEL_ALL(&fcu->bezt[i]); } } + return changed; } /* **************************************************** */ diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index 755e538f415..9da9845116d 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -1471,7 +1471,8 @@ static int curve_split_exec(bContext *C, wmOperator *op) Main *bmain = CTX_data_main(C); ViewLayer *view_layer = CTX_data_view_layer(C); View3D *v3d = CTX_wm_view3d(C); - int ok = -1; + bool changed = false; + int count_failed = 0; uint objects_len; Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( @@ -1489,7 +1490,7 @@ static int curve_split_exec(bContext *C, wmOperator *op) adduplicateflagNurb(obedit, v3d, &newnurb, SELECT, true); if (BLI_listbase_is_empty(&newnurb)) { - ok = MAX2(ok, 0); + count_failed += 1; continue; } @@ -1504,14 +1505,16 @@ static int curve_split_exec(bContext *C, wmOperator *op) WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, obedit); } - ok = 1; + changed = true; WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data); DEG_id_tag_update(obedit->data, 0); } MEM_freeN(objects); - if (ok == 0) { - BKE_report(op->reports, RPT_ERROR, "Cannot split current selection"); + if (changed == false) { + if (count_failed != 0) { + BKE_report(op->reports, RPT_ERROR, "Cannot split current selection"); + } return OPERATOR_CANCELLED; } return OPERATOR_FINISHED; @@ -4998,7 +5001,8 @@ static int spin_exec(bContext *C, wmOperator *op) View3D *v3d = CTX_wm_view3d(C); RegionView3D *rv3d = ED_view3d_context_rv3d(C); float cent[3], axis[3], viewmat[4][4]; - int ok = -1; + bool changed = false; + int count_failed = 0; RNA_float_get_array(op->ptr, "center", cent); RNA_float_get_array(op->ptr, "axis", axis); @@ -5025,11 +5029,11 @@ static int spin_exec(bContext *C, wmOperator *op) mul_m4_v3(obedit->imat, cent); if (!ed_editnurb_spin(viewmat, v3d, obedit, axis, cent)) { - ok = MAX2(ok, 0); + count_failed += 1; continue; } - ok = 1; + changed = true; if (ED_curve_updateAnimPaths(bmain, cu)) { WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, obedit); } @@ -5039,8 +5043,11 @@ static int spin_exec(bContext *C, wmOperator *op) } MEM_freeN(objects); - if (ok == 0) { - BKE_report(op->reports, RPT_ERROR, "Cannot spin"); + if (changed == false) { + if (count_failed != 0) { + BKE_report(op->reports, RPT_ERROR, "Cannot spin"); + } + return OPERATOR_CANCELLED; } return OPERATOR_FINISHED; } @@ -5889,7 +5896,9 @@ static int duplicate_exec(bContext *C, wmOperator *op) { ViewLayer *view_layer = CTX_data_view_layer(C); View3D *v3d = CTX_wm_view3d(C); - int ok = -1; + + bool changed = false; + int count_failed = 0; uint objects_len = 0; Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( @@ -5906,19 +5915,21 @@ static int duplicate_exec(bContext *C, wmOperator *op) adduplicateflagNurb(obedit, v3d, &newnurb, SELECT, false); if (BLI_listbase_is_empty(&newnurb)) { - ok = MAX2(ok, 0); + count_failed += 1; continue; } - ok = 1; + changed = true; BLI_movelisttolist(object_editcurve_get(obedit), &newnurb); DEG_id_tag_update(&cu->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_GEOM | ND_SELECT, &cu->id); } MEM_freeN(objects); - if (ok == 0) { - BKE_report(op->reports, RPT_ERROR, "Cannot duplicate current selection"); + if (changed == false) { + if (count_failed != 0) { + BKE_report(op->reports, RPT_ERROR, "Cannot duplicate current selection"); + } return OPERATOR_CANCELLED; } return OPERATOR_FINISHED; diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt index 1f31a384787..8fca0c46c82 100644 --- a/source/blender/editors/datafiles/CMakeLists.txt +++ b/source/blender/editors/datafiles/CMakeLists.txt @@ -776,7 +776,7 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES ops.curves.sculpt_comb ops.curves.sculpt_cut ops.curves.sculpt_delete - ops.curves.sculpt_grow + ops.curves.sculpt_grow_shrink ops.generic.cursor ops.generic.select ops.generic.select_box diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index a8fb344f366..3c8e6d6e5f5 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -994,9 +994,10 @@ static int gpencil_duplicate_exec(bContext *C, wmOperator *op) /* updates */ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + return OPERATOR_FINISHED; } - return OPERATOR_FINISHED; + return OPERATOR_CANCELLED; } void GPENCIL_OT_duplicate(wmOperatorType *ot) diff --git a/source/blender/editors/include/ED_keyframes_edit.h b/source/blender/editors/include/ED_keyframes_edit.h index 3a0bb9738ae..163797d395d 100644 --- a/source/blender/editors/include/ED_keyframes_edit.h +++ b/source/blender/editors/include/ED_keyframes_edit.h @@ -381,7 +381,7 @@ bool keyframe_region_circle_test(const KeyframeEdit_CircleData *data_circle, con void delete_fcurve_key(struct FCurve *fcu, int index, bool do_recalc); bool delete_fcurve_keys(struct FCurve *fcu); void clear_fcurve_keys(struct FCurve *fcu); -void duplicate_fcurve_keys(struct FCurve *fcu); +bool duplicate_fcurve_keys(struct FCurve *fcu); float get_default_rna_value(struct FCurve *fcu, struct PropertyRNA *prop, struct PointerRNA *ptr); typedef struct FCurveSegment { diff --git a/source/blender/editors/include/ED_numinput.h b/source/blender/editors/include/ED_numinput.h index 12e3ffee627..a1b4e3a4552 100644 --- a/source/blender/editors/include/ED_numinput.h +++ b/source/blender/editors/include/ED_numinput.h @@ -43,7 +43,7 @@ typedef struct NumInput { int str_cur; } NumInput; -/* NumInput.flag */ +/** #NumInput.flag */ enum { NUM_AFFECT_ALL = (1 << 0), /* (1 << 9) and above are reserved for internal flags! */ diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index c3571bb3788..3f9f26560b2 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -163,6 +163,11 @@ bool ED_view3d_camera_to_view_selected(struct Main *bmain, const struct Scene *scene, struct Object *camera_ob); +bool ED_view3d_camera_to_view_selected_with_set_clipping(struct Main *bmain, + struct Depsgraph *depsgraph, + const struct Scene *scene, + struct Object *camera_ob); + /** * Use to store the last view, before entering camera view. */ diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 0854b8b5cfa..4649fb06374 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -2948,7 +2948,7 @@ uiBlock *UI_region_block_find_mouse_over(const struct ARegion *region, */ struct ARegion *UI_region_searchbox_region_get(const struct ARegion *button_region); -/* uiFontStyle.align */ +/** #uiFontStyle.align */ typedef enum eFontStyle_Align { UI_STYLE_TEXT_LEFT = 0, UI_STYLE_TEXT_CENTER = 1, diff --git a/source/blender/editors/include/UI_view2d.h b/source/blender/editors/include/UI_view2d.h index 56b0bd04a71..47c554f84e0 100644 --- a/source/blender/editors/include/UI_view2d.h +++ b/source/blender/editors/include/UI_view2d.h @@ -447,7 +447,7 @@ typedef struct View2DEdgePanData { struct ARegion *region; /** View2d we're operating in. */ struct View2D *v2d; - /* Limit maximum pannable area */ + /** Limit maximum pannable area. */ struct rctf limit; /** Panning should only start once being in the inside rect once (e.g. adding nodes can happen diff --git a/source/blender/editors/interface/interface.cc b/source/blender/editors/interface/interface.cc index 9bd0498fcfb..b34d61d5192 100644 --- a/source/blender/editors/interface/interface.cc +++ b/source/blender/editors/interface/interface.cc @@ -6516,7 +6516,7 @@ uiBut *uiDefSearchButO_ptr(uiBlock *block, if (properties) { PointerRNA *ptr = UI_but_operator_ptr_get(but); - /* Copy idproperties. */ + /* Copy id-properties. */ ptr->data = IDP_CopyProperty(properties); } @@ -6755,7 +6755,7 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...) PointerRNA *opptr = UI_but_operator_ptr_get(but); wmOperatorType *ot = but->optype; - /* so the context is passed to itemf functions */ + /* So the context is passed to `itemf` functions. */ WM_operator_properties_sanitize(opptr, false); /* if the default property of the operator is enum and it is set, diff --git a/source/blender/editors/interface/interface_region_menu_pie.cc b/source/blender/editors/interface/interface_region_menu_pie.cc index d30797cc4d0..b11564f09c5 100644 --- a/source/blender/editors/interface/interface_region_menu_pie.cc +++ b/source/blender/editors/interface/interface_region_menu_pie.cc @@ -332,7 +332,7 @@ static void ui_pie_menu_level_invoke(bContext *C, void *argN, void *arg2) PointerRNA ptr; WM_operator_properties_create_ptr(&ptr, lvl->ot); - /* so the context is passed to itemf functions (some need it) */ + /* So the context is passed to `itemf` functions (some need it). */ WM_operator_properties_sanitize(&ptr, false); PropertyRNA *prop = RNA_struct_find_property(&ptr, lvl->propname); diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index 64d10f7e2af..73716f6edba 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -4399,6 +4399,11 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) knifetool_exit(op); ED_workspace_status_text(C, NULL); + /* Exit early to prevent undo push for empty cuts. */ + if (kcd->totkvert == 0) { + return OPERATOR_CANCELLED; + } + return OPERATOR_FINISHED; case KNF_MODAL_UNDO: if (BLI_stack_is_empty(kcd->undostack)) { diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 0ea807586f9..5a0a2b7a09a 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -2016,6 +2016,7 @@ static int edbm_duplicate_exec(bContext *C, wmOperator *op) uint objects_len = 0; Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( view_layer, CTX_wm_view3d(C), &objects_len); + bool changed = false; for (uint ob_index = 0; ob_index < objects_len; ob_index++) { Object *obedit = objects[ob_index]; @@ -2026,6 +2027,7 @@ static int edbm_duplicate_exec(bContext *C, wmOperator *op) BMOperator bmop; BMesh *bm = em->bm; + changed = true; EDBM_op_init(em, &bmop, @@ -2060,16 +2062,16 @@ static int edbm_duplicate_exec(bContext *C, wmOperator *op) } MEM_freeN(objects); - return OPERATOR_FINISHED; + return (changed) ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } static int edbm_duplicate_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { WM_cursor_wait(true); - edbm_duplicate_exec(C, op); + const int retval = edbm_duplicate_exec(C, op); WM_cursor_wait(false); - return OPERATOR_FINISHED; + return retval; } void MESH_OT_duplicate(wmOperatorType *ot) diff --git a/source/blender/editors/object/object_add.cc b/source/blender/editors/object/object_add.cc index 6a7920d4d75..ba11483722e 100644 --- a/source/blender/editors/object/object_add.cc +++ b/source/blender/editors/object/object_add.cc @@ -3564,6 +3564,7 @@ static int duplicate_exec(bContext *C, wmOperator *op) ViewLayer *view_layer = CTX_data_view_layer(C); const bool linked = RNA_boolean_get(op->ptr, "linked"); const eDupli_ID_Flags dupflag = (linked) ? (eDupli_ID_Flags)0 : (eDupli_ID_Flags)U.dupflag; + bool changed = false; /* We need to handle that here ourselves, because we may duplicate several objects, in which case * we also want to remap pointers between those... */ @@ -3582,6 +3583,7 @@ static int duplicate_exec(bContext *C, wmOperator *op) * the list is made in advance */ ED_object_base_select(base, BA_DESELECT); ED_object_base_select(basen, BA_SELECT); + changed = true; if (basen == nullptr) { continue; @@ -3598,6 +3600,10 @@ static int duplicate_exec(bContext *C, wmOperator *op) } CTX_DATA_END; + if (!changed) { + return OPERATOR_CANCELLED; + } + /* Note that this will also clear newid pointers and tags. */ copy_object_set_idnew(C); diff --git a/source/blender/editors/render/render_preview.cc b/source/blender/editors/render/render_preview.cc index 7404b3bf010..78e786a2130 100644 --- a/source/blender/editors/render/render_preview.cc +++ b/source/blender/editors/render/render_preview.cc @@ -830,7 +830,8 @@ static Scene *object_preview_scene_create(const struct ObjectPreviewData *previe DEG_graph_build_from_view_layer(depsgraph); DEG_evaluate_on_refresh(depsgraph); - ED_view3d_camera_to_view_selected(preview_data->pr_main, depsgraph, scene, camera_object); + ED_view3d_camera_to_view_selected_with_set_clipping( + preview_data->pr_main, depsgraph, scene, camera_object); BKE_scene_graph_update_tagged(depsgraph, preview_data->pr_main); diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index c422c8c2033..fe7683d12f5 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -31,6 +31,7 @@ set(SRC curves_sculpt_add.cc curves_sculpt_comb.cc curves_sculpt_delete.cc + curves_sculpt_grow_shrink.cc curves_sculpt_ops.cc curves_sculpt_snake_hook.cc paint_cursor.c diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc b/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc new file mode 100644 index 00000000000..d26af20799e --- /dev/null +++ b/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc @@ -0,0 +1,526 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <algorithm> + +#include "curves_sculpt_intern.hh" + +#include "BLI_enumerable_thread_specific.hh" +#include "BLI_float4x4.hh" +#include "BLI_kdtree.h" +#include "BLI_rand.hh" +#include "BLI_vector.hh" + +#include "PIL_time.h" + +#include "DEG_depsgraph.h" + +#include "BKE_attribute_math.hh" +#include "BKE_brush.h" +#include "BKE_bvhutils.h" +#include "BKE_context.h" +#include "BKE_curves.hh" +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" +#include "BKE_paint.h" +#include "BKE_spline.hh" + +#include "DNA_brush_enums.h" +#include "DNA_brush_types.h" +#include "DNA_curves_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" + +#include "ED_screen.h" +#include "ED_view3d.h" + +#include "curves_sculpt_intern.hh" + +/** + * The code below uses a suffix naming convention to indicate the coordinate space: + * - `cu`: Local space of the curves object that is being edited. + * - `su`: Local space of the surface object. + * - `wo`: World space. + * - `re`: 2D coordinates within the region. + */ + +namespace blender::ed::sculpt_paint { + +using bke::CurvesGeometry; + +/** + * Utility class to wrap different grow/shrink behaviors. + * It might be useful to use this for other future brushes as well, but better see if this + * abstraction holds up for a while before using it in more places. + */ +class CurvesEffect { + public: + virtual void execute(CurvesGeometry &curves, + Span<int> curve_indices, + Span<float> move_distances_cu) = 0; +}; + +/** + * Make curves smaller by trimming the end off. + */ +class ShrinkCurvesEffect : public CurvesEffect { + private: + Brush &brush_; + + public: + ShrinkCurvesEffect(Brush &brush) : brush_(brush) + { + } + + void execute(CurvesGeometry &curves, + const Span<int> curve_indices, + const Span<float> move_distances_cu) override + { + MutableSpan<float3> positions_cu = curves.positions(); + threading::parallel_for(curve_indices.index_range(), 256, [&](const IndexRange range) { + for (const int influence_i : range) { + const int curve_i = curve_indices[influence_i]; + const float move_distance_cu = move_distances_cu[influence_i]; + const IndexRange curve_points = curves.points_for_curve(curve_i); + this->shrink_curve(positions_cu, curve_points, move_distance_cu); + } + }); + } + + void shrink_curve(MutableSpan<float3> positions, + const IndexRange curve_points, + const float shrink_length) const + { + PolySpline spline; + spline.resize(curve_points.size()); + MutableSpan<float3> spline_positions = spline.positions(); + spline_positions.copy_from(positions.slice(curve_points)); + spline.mark_cache_invalid(); + const float min_length = brush_.curves_sculpt_settings->minimum_length; + const float old_length = spline.length(); + const float new_length = std::max(min_length, old_length - shrink_length); + const float length_factor = std::clamp(new_length / old_length, 0.0f, 1.0f); + + Vector<float> old_point_lengths; + old_point_lengths.append(0.0f); + for (const int i : spline_positions.index_range().drop_back(1)) { + const float3 &p1 = spline_positions[i]; + const float3 &p2 = spline_positions[i + 1]; + const float length = math::distance(p1, p2); + old_point_lengths.append(old_point_lengths.last() + length); + } + + for (const int i : spline_positions.index_range()) { + const float eval_length = old_point_lengths[i] * length_factor; + const Spline::LookupResult lookup = spline.lookup_evaluated_length(eval_length); + const float index_factor = lookup.evaluated_index + lookup.factor; + float3 p; + spline.sample_with_index_factors<float3>(spline_positions, {&index_factor, 1}, {&p, 1}); + positions[curve_points[i]] = p; + } + } +}; + +/** + * Make the curves longer by extrapolating them linearly. + */ +class ExtrapolateCurvesEffect : public CurvesEffect { + void execute(CurvesGeometry &curves, + const Span<int> curve_indices, + const Span<float> move_distances_cu) override + { + MutableSpan<float3> positions_cu = curves.positions(); + threading::parallel_for(curve_indices.index_range(), 256, [&](const IndexRange range) { + for (const int influence_i : range) { + const int curve_i = curve_indices[influence_i]; + const float move_distance_cu = move_distances_cu[influence_i]; + const IndexRange curve_points = curves.points_for_curve(curve_i); + + if (curve_points.size() <= 1) { + continue; + } + + const float3 old_last_pos_cu = positions_cu[curve_points.last()]; + /* Use some point within the curve rather than the end point to smooth out some random + * variation. */ + const float3 direction_reference_point = + positions_cu[curve_points[curve_points.size() / 2]]; + const float3 direction = math::normalize(old_last_pos_cu - direction_reference_point); + + const float3 new_last_pos_cu = old_last_pos_cu + direction * move_distance_cu; + this->move_last_point_and_resample(positions_cu, curve_points, new_last_pos_cu); + } + }); + } + + void move_last_point_and_resample(MutableSpan<float3> positions, + const IndexRange curve_points, + const float3 &new_last_point_position) const + { + Vector<float> old_lengths; + old_lengths.append(0.0f); + /* Used to (1) normalize the segment sizes over time and (2) support making zero-length + * segments */ + const float extra_length = 0.001f; + for (const int segment_i : IndexRange(curve_points.size() - 1)) { + const float3 &p1 = positions[curve_points[segment_i]]; + const float3 &p2 = positions[curve_points[segment_i] + 1]; + const float length = math::distance(p1, p2); + old_lengths.append(old_lengths.last() + length + extra_length); + } + Vector<float> point_factors; + for (float &old_length : old_lengths) { + point_factors.append(old_length / old_lengths.last()); + } + + PolySpline new_spline; + new_spline.resize(curve_points.size()); + MutableSpan<float3> new_spline_positions = new_spline.positions(); + for (const int i : IndexRange(curve_points.size() - 1)) { + new_spline_positions[i] = positions[curve_points[i]]; + } + new_spline_positions.last() = new_last_point_position; + new_spline.mark_cache_invalid(); + + for (const int i : IndexRange(curve_points.size())) { + const float factor = point_factors[i]; + const Spline::LookupResult lookup = new_spline.lookup_evaluated_factor(factor); + const float index_factor = lookup.evaluated_index + lookup.factor; + float3 p; + new_spline.sample_with_index_factors<float3>( + new_spline_positions, {&index_factor, 1}, {&p, 1}); + positions[curve_points[i]] = p; + } + } +}; + +/** + * Change the length of curves by scaling them uniformly. + */ +class ScaleCurvesEffect : public CurvesEffect { + private: + bool scale_up_; + Brush &brush_; + + public: + ScaleCurvesEffect(bool scale_up, Brush &brush) : scale_up_(scale_up), brush_(brush) + { + } + + void execute(CurvesGeometry &curves, + const Span<int> curve_indices, + const Span<float> move_distances_cu) override + { + MutableSpan<float3> positions_cu = curves.positions(); + threading::parallel_for(curve_indices.index_range(), 256, [&](const IndexRange range) { + for (const int influence_i : range) { + const int curve_i = curve_indices[influence_i]; + const float move_distance_cu = move_distances_cu[influence_i]; + const IndexRange points = curves.points_for_curve(curve_i); + + const float old_length = this->compute_poly_curve_length(positions_cu.slice(points)); + const float length_diff = scale_up_ ? move_distance_cu : -move_distance_cu; + const float min_length = brush_.curves_sculpt_settings->minimum_length; + const float new_length = std::max(min_length, old_length + length_diff); + const float scale_factor = safe_divide(new_length, old_length); + + const float3 &root_pos_cu = positions_cu[points[0]]; + for (float3 &pos_cu : positions_cu.slice(points.drop_front(1))) { + pos_cu = (pos_cu - root_pos_cu) * scale_factor + root_pos_cu; + } + } + }); + } + + float compute_poly_curve_length(const Span<float3> positions) + { + float length = 0.0f; + const int segments_num = positions.size() - 1; + for (const int segment_i : IndexRange(segments_num)) { + const float3 &p1 = positions[segment_i]; + const float3 &p2 = positions[segment_i + 1]; + length += math::distance(p1, p2); + } + return length; + } +}; + +class CurvesEffectOperation : public CurvesSculptStrokeOperation { + private: + std::unique_ptr<CurvesEffect> effect_; + float2 last_mouse_position_; + CurvesBrush3D brush_3d_; + + friend struct CurvesEffectOperationExecutor; + + public: + CurvesEffectOperation(std::unique_ptr<CurvesEffect> effect) : effect_(std::move(effect)) + { + } + + void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override; +}; + +/** + * Utility class that actually executes the update when the stroke is updated. That's useful + * because it avoids passing a very large number of parameters between functions. + */ +struct CurvesEffectOperationExecutor { + CurvesEffectOperation *self_ = nullptr; + Depsgraph *depsgraph_ = nullptr; + Scene *scene_ = nullptr; + Object *object_ = nullptr; + ARegion *region_ = nullptr; + View3D *v3d_ = nullptr; + RegionView3D *rv3d_ = nullptr; + + Curves *curves_id_ = nullptr; + CurvesGeometry *curves_ = nullptr; + + Brush *brush_ = nullptr; + float brush_radius_re_; + float brush_radius_sq_re_; + float brush_strength_; + eBrushFalloffShape falloff_shape_; + + float4x4 curves_to_world_mat_; + float4x4 world_to_curves_mat_; + + float2 brush_pos_start_re_; + float2 brush_pos_end_re_; + + struct Influences { + Vector<int> curve_indices; + Vector<float> move_distances_cu; + }; + + void execute(CurvesEffectOperation &self, bContext *C, const StrokeExtension &stroke_extension) + { + BLI_SCOPED_DEFER([&]() { self.last_mouse_position_ = stroke_extension.mouse_position; }); + + self_ = &self; + depsgraph_ = CTX_data_depsgraph_pointer(C); + scene_ = CTX_data_scene(C); + object_ = CTX_data_active_object(C); + region_ = CTX_wm_region(C); + v3d_ = CTX_wm_view3d(C); + rv3d_ = CTX_wm_region_view3d(C); + + curves_id_ = static_cast<Curves *>(object_->data); + curves_ = &CurvesGeometry::wrap(curves_id_->geometry); + + CurvesSculpt &curves_sculpt = *scene_->toolsettings->curves_sculpt; + brush_ = BKE_paint_brush(&curves_sculpt.paint); + brush_radius_re_ = BKE_brush_size_get(scene_, brush_); + brush_strength_ = BKE_brush_alpha_get(scene_, brush_); + brush_radius_sq_re_ = pow2f(brush_radius_re_); + falloff_shape_ = eBrushFalloffShape(brush_->falloff_shape); + + curves_to_world_mat_ = object_->obmat; + world_to_curves_mat_ = curves_to_world_mat_.inverted(); + + brush_pos_start_re_ = self.last_mouse_position_; + brush_pos_end_re_ = stroke_extension.mouse_position; + + if (stroke_extension.is_first) { + if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) { + if (std::optional<CurvesBrush3D> brush_3d = sample_curves_3d_brush( + *C, *object_, stroke_extension.mouse_position, brush_radius_re_)) { + self.brush_3d_ = *brush_3d; + } + } + + return; + } + + /* Compute influences. */ + threading::EnumerableThreadSpecific<Influences> influences_for_thread; + if (falloff_shape_ == PAINT_FALLOFF_SHAPE_TUBE) { + this->gather_influences_projected(influences_for_thread); + } + else if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) { + this->gather_influences_spherical(influences_for_thread); + } + + /* Execute effect. */ + threading::parallel_for_each(influences_for_thread, [&](const Influences &influences) { + BLI_assert(influences.curve_indices.size() == influences.move_distances_cu.size()); + self_->effect_->execute(*curves_, influences.curve_indices, influences.move_distances_cu); + }); + + curves_->tag_positions_changed(); + DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); + ED_region_tag_redraw(region_); + } + + void gather_influences_projected( + threading::EnumerableThreadSpecific<Influences> &influences_for_thread) + { + const Span<float3> positions_cu = curves_->positions(); + + float4x4 projection; + ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values); + + threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) { + Influences &local_influences = influences_for_thread.local(); + + for (const int curve_i : curves_range) { + const IndexRange points = curves_->points_for_curve(curve_i); + const int tot_segments = points.size() - 1; + float max_move_distance_cu = 0.0f; + for (const int segment_i : IndexRange(tot_segments)) { + const float3 &p1_cu = positions_cu[points[segment_i]]; + const float3 &p2_cu = positions_cu[points[segment_i] + 1]; + + float2 p1_re, p2_re; + ED_view3d_project_float_v2_m4(region_, p1_cu, p1_re, projection.values); + ED_view3d_project_float_v2_m4(region_, p2_cu, p2_re, projection.values); + + float2 closest_on_brush_re; + float2 closest_on_segment_re; + float lambda_on_brush; + float lambda_on_segment; + const float dist_to_brush_sq_re = closest_seg_seg_v2(closest_on_brush_re, + closest_on_segment_re, + &lambda_on_brush, + &lambda_on_segment, + brush_pos_start_re_, + brush_pos_end_re_, + p1_re, + p2_re); + + if (dist_to_brush_sq_re > brush_radius_sq_re_) { + continue; + } + + const float dist_to_brush_re = std::sqrt(dist_to_brush_sq_re); + const float radius_falloff = BKE_brush_curve_strength( + brush_, dist_to_brush_re, brush_radius_re_); + const float weight = brush_strength_ * radius_falloff; + + const float3 closest_on_segment_cu = math::interpolate(p1_cu, p2_cu, lambda_on_segment); + + float3 brush_start_pos_wo, brush_end_pos_wo; + ED_view3d_win_to_3d(v3d_, + region_, + curves_to_world_mat_ * closest_on_segment_cu, + brush_pos_start_re_, + brush_start_pos_wo); + ED_view3d_win_to_3d(v3d_, + region_, + curves_to_world_mat_ * closest_on_segment_cu, + brush_pos_end_re_, + brush_end_pos_wo); + const float3 brush_start_pos_cu = world_to_curves_mat_ * brush_start_pos_wo; + const float3 brush_end_pos_cu = world_to_curves_mat_ * brush_end_pos_wo; + + const float move_distance_cu = weight * + math::distance(brush_start_pos_cu, brush_end_pos_cu); + max_move_distance_cu = std::max(max_move_distance_cu, move_distance_cu); + } + if (max_move_distance_cu > 0.0f) { + local_influences.curve_indices.append(curve_i); + local_influences.move_distances_cu.append(max_move_distance_cu); + } + } + }); + } + + void gather_influences_spherical( + threading::EnumerableThreadSpecific<Influences> &influences_for_thread) + { + const Span<float3> positions_cu = curves_->positions(); + + float3 brush_pos_start_wo, brush_pos_end_wo; + ED_view3d_win_to_3d(v3d_, + region_, + curves_to_world_mat_ * self_->brush_3d_.position_cu, + brush_pos_start_re_, + brush_pos_start_wo); + ED_view3d_win_to_3d(v3d_, + region_, + curves_to_world_mat_ * self_->brush_3d_.position_cu, + brush_pos_end_re_, + brush_pos_end_wo); + const float3 brush_pos_start_cu = world_to_curves_mat_ * brush_pos_start_wo; + const float3 brush_pos_end_cu = world_to_curves_mat_ * brush_pos_end_wo; + const float3 brush_pos_diff_cu = brush_pos_end_cu - brush_pos_start_cu; + const float brush_pos_diff_length_cu = math::length(brush_pos_diff_cu); + const float brush_radius_cu = self_->brush_3d_.radius_cu; + const float brush_radius_sq_cu = pow2f(brush_radius_cu); + threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) { + Influences &local_influences = influences_for_thread.local(); + + for (const int curve_i : curves_range) { + const IndexRange points = curves_->points_for_curve(curve_i); + const int tot_segments = points.size() - 1; + float max_move_distance_cu = 0.0f; + for (const int segment_i : IndexRange(tot_segments)) { + const float3 &p1_cu = positions_cu[points[segment_i]]; + const float3 &p2_cu = positions_cu[points[segment_i] + 1]; + + float3 closest_on_segment_cu; + float3 closest_on_brush_cu; + isect_seg_seg_v3(p1_cu, + p2_cu, + brush_pos_start_cu, + brush_pos_end_cu, + closest_on_segment_cu, + closest_on_brush_cu); + + const float dist_to_brush_sq_cu = math::distance_squared(closest_on_segment_cu, + closest_on_brush_cu); + if (dist_to_brush_sq_cu > brush_radius_sq_cu) { + continue; + } + + const float dist_to_brush_cu = std::sqrt(dist_to_brush_sq_cu); + const float radius_falloff = BKE_brush_curve_strength( + brush_, dist_to_brush_cu, brush_radius_cu); + const float weight = brush_strength_ * radius_falloff; + + const float move_distance_cu = weight * brush_pos_diff_length_cu; + max_move_distance_cu = std::max(max_move_distance_cu, move_distance_cu); + } + if (max_move_distance_cu > 0.0f) { + local_influences.curve_indices.append(curve_i); + local_influences.move_distances_cu.append(max_move_distance_cu); + } + } + }); + } +}; + +void CurvesEffectOperation::on_stroke_extended(bContext *C, + const StrokeExtension &stroke_extension) +{ + CurvesEffectOperationExecutor executor; + executor.execute(*this, C, stroke_extension); +} + +std::unique_ptr<CurvesSculptStrokeOperation> new_grow_shrink_operation( + const BrushStrokeMode brush_mode, bContext *C) +{ + Scene &scene = *CTX_data_scene(C); + Brush &brush = *BKE_paint_brush(&scene.toolsettings->curves_sculpt->paint); + const bool use_scale_uniform = brush.curves_sculpt_settings->flag & + BRUSH_CURVES_SCULPT_FLAG_SCALE_UNIFORM; + const bool use_grow = (brush_mode == BRUSH_STROKE_INVERT) == ((brush.flag & BRUSH_DIR_IN) != 0); + + if (use_grow) { + if (use_scale_uniform) { + return std::make_unique<CurvesEffectOperation>( + std::make_unique<ScaleCurvesEffect>(true, brush)); + } + return std::make_unique<CurvesEffectOperation>(std::make_unique<ExtrapolateCurvesEffect>()); + } + if (use_scale_uniform) { + return std::make_unique<CurvesEffectOperation>( + std::make_unique<ScaleCurvesEffect>(false, brush)); + } + return std::make_unique<CurvesEffectOperation>(std::make_unique<ShrinkCurvesEffect>(brush)); +} + +} // namespace blender::ed::sculpt_paint diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh index d021627921f..03413221907 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh @@ -5,6 +5,7 @@ #include <optional> #include "curves_sculpt_intern.h" +#include "paint_intern.h" #include "BLI_math_vector.hh" @@ -36,6 +37,8 @@ std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation(); std::unique_ptr<CurvesSculptStrokeOperation> new_comb_operation(); std::unique_ptr<CurvesSculptStrokeOperation> new_delete_operation(); std::unique_ptr<CurvesSculptStrokeOperation> new_snake_hook_operation(); +std::unique_ptr<CurvesSculptStrokeOperation> new_grow_shrink_operation( + const BrushStrokeMode brush_mode, bContext *C); struct CurvesBrush3D { float3 position_cu; diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc index 208db92abf7..893b2640427 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc @@ -74,118 +74,6 @@ using blender::bke::CurvesGeometry; /** \name * SCULPT_CURVES_OT_brush_stroke * \{ */ -/** - * Resamples the curves to a shorter length. - */ -class ShrinkOperation : public CurvesSculptStrokeOperation { - private: - float2 last_mouse_position_; - - public: - void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override - { - BLI_SCOPED_DEFER([&]() { last_mouse_position_ = stroke_extension.mouse_position; }); - - if (stroke_extension.is_first) { - return; - } - - Scene &scene = *CTX_data_scene(C); - Object &object = *CTX_data_active_object(C); - ARegion *region = CTX_wm_region(C); - View3D *v3d = CTX_wm_view3d(C); - RegionView3D *rv3d = CTX_wm_region_view3d(C); - - CurvesSculpt &curves_sculpt = *scene.toolsettings->curves_sculpt; - Brush &brush = *BKE_paint_brush(&curves_sculpt.paint); - const float brush_radius = BKE_brush_size_get(&scene, &brush); - const float brush_strength = BKE_brush_alpha_get(&scene, &brush); - - const float4x4 ob_mat = object.obmat; - const float4x4 ob_imat = ob_mat.inverted(); - - float4x4 projection; - ED_view3d_ob_project_mat_get(rv3d, &object, projection.values); - - Curves &curves_id = *static_cast<Curves *>(object.data); - CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); - MutableSpan<float3> positions = curves.positions(); - - const float2 mouse_prev = last_mouse_position_; - const float2 mouse_cur = stroke_extension.mouse_position; - const float2 mouse_diff = mouse_cur - mouse_prev; - - threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) { - for (const int curve_i : curves_range) { - const IndexRange curve_points = curves.points_for_curve(curve_i); - const int last_point_i = curve_points.last(); - - const float3 old_tip_position = positions[last_point_i]; - - float2 old_tip_position_screen; - ED_view3d_project_float_v2_m4( - region, old_tip_position, old_tip_position_screen, projection.values); - - const float distance_screen = math::distance(old_tip_position_screen, mouse_prev); - if (distance_screen > brush_radius) { - continue; - } - - const float radius_falloff = pow2f(1.0f - distance_screen / brush_radius); - const float weight = brush_strength * radius_falloff; - - const float2 offset_tip_position_screen = old_tip_position_screen + weight * mouse_diff; - float3 offset_tip_position; - ED_view3d_win_to_3d(v3d, - region, - ob_mat * old_tip_position, - offset_tip_position_screen, - offset_tip_position); - offset_tip_position = ob_imat * offset_tip_position; - const float shrink_length = math::distance(offset_tip_position, old_tip_position); - - this->shrink_curve(positions, curve_points, shrink_length); - } - }); - - curves.tag_positions_changed(); - DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY); - ED_region_tag_redraw(region); - } - - void shrink_curve(MutableSpan<float3> positions, - const IndexRange curve_points, - const float shrink_length) const - { - PolySpline spline; - spline.resize(curve_points.size()); - MutableSpan<float3> spline_positions = spline.positions(); - spline_positions.copy_from(positions.slice(curve_points)); - spline.mark_cache_invalid(); - const float old_length = spline.length(); - const float new_length = std::max(0.0f, old_length - shrink_length); - const float length_factor = new_length / old_length; - - Vector<float> old_point_lengths; - old_point_lengths.append(0.0f); - for (const int i : spline_positions.index_range().drop_back(1)) { - const float3 &p1 = spline_positions[i]; - const float3 &p2 = spline_positions[i + 1]; - const float length = math::distance(p1, p2); - old_point_lengths.append(old_point_lengths.last() + length); - } - - for (const int i : spline_positions.index_range()) { - const float eval_length = old_point_lengths[i] * length_factor; - const Spline::LookupResult lookup = spline.lookup_evaluated_length(eval_length); - const float index_factor = lookup.evaluated_index + lookup.factor; - float3 p; - spline.sample_with_index_factors<float3>(spline_positions, {&index_factor, 1}, {&p, 1}); - positions[curve_points[i]] = p; - } - } -}; - class DensityAddOperation : public CurvesSculptStrokeOperation { private: /** Contains the root points of the curves that existed before this operation started. */ @@ -612,8 +500,10 @@ class DensityAddOperation : public CurvesSculptStrokeOperation { }; static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation(bContext *C, - wmOperator *UNUSED(op)) + wmOperator *op) { + const BrushStrokeMode mode = static_cast<BrushStrokeMode>(RNA_enum_get(op->ptr, "mode")); + Scene &scene = *CTX_data_scene(C); CurvesSculpt &curves_sculpt = *scene.toolsettings->curves_sculpt; Brush &brush = *BKE_paint_brush(&curves_sculpt.paint); @@ -626,10 +516,10 @@ static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation(bConte return new_snake_hook_operation(); case CURVES_SCULPT_TOOL_ADD: return new_add_operation(); + case CURVES_SCULPT_TOOL_GROW_SHRINK: + return new_grow_shrink_operation(mode, C); case CURVES_SCULPT_TOOL_TEST1: return std::make_unique<DensityAddOperation>(); - case CURVES_SCULPT_TOOL_TEST2: - return std::make_unique<ShrinkOperation>(); } BLI_assert_unreachable(); return {}; @@ -674,7 +564,9 @@ static void stroke_update_step(bContext *C, stroke_extension.is_first = false; } - op_data->operation->on_stroke_extended(C, stroke_extension); + if (op_data->operation) { + op_data->operation->on_stroke_extended(C, stroke_extension); + } } static void stroke_done(const bContext *C, PaintStroke *stroke) @@ -796,7 +688,7 @@ static int curves_sculptmode_toggle_exec(bContext *C, wmOperator *op) WM_toolsystem_update_from_context_view3d(C); WM_event_add_notifier(C, NC_SCENE | ND_MODE, nullptr); - return OPERATOR_CANCELLED; + return OPERATOR_FINISHED; } static void CURVES_OT_sculptmode_toggle(wmOperatorType *ot) diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c index d33cf70e117..87a271543d1 100644 --- a/source/blender/editors/space_action/action_edit.c +++ b/source/blender/editors/space_action/action_edit.c @@ -878,11 +878,12 @@ void ACTION_OT_keyframe_insert(wmOperatorType *ot) /* ******************** Duplicate Keyframes Operator ************************* */ -static void duplicate_action_keys(bAnimContext *ac) +static bool duplicate_action_keys(bAnimContext *ac) { ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; int filter; + bool changed = false; /* filter data */ if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) { @@ -898,10 +899,11 @@ static void duplicate_action_keys(bAnimContext *ac) /* loop through filtered data and delete selected keys */ for (ale = anim_data.first; ale; ale = ale->next) { if (ELEM(ale->type, ANIMTYPE_FCURVE, ANIMTYPE_NLACURVE)) { - duplicate_fcurve_keys((FCurve *)ale->key_data); + changed |= duplicate_fcurve_keys((FCurve *)ale->key_data); } else if (ale->type == ANIMTYPE_GPLAYER) { ED_gpencil_layer_frames_duplicate((bGPDlayer *)ale->data); + changed |= ED_gpencil_layer_frame_select_check((bGPDlayer *)ale->data); } else if (ale->type == ANIMTYPE_MASKLAYER) { ED_masklayer_frames_duplicate((MaskLayer *)ale->data); @@ -915,6 +917,8 @@ static void duplicate_action_keys(bAnimContext *ac) ANIM_animdata_update(ac, &anim_data); ANIM_animdata_freelist(&anim_data); + + return changed; } /* ------------------- */ @@ -929,7 +933,9 @@ static int actkeys_duplicate_exec(bContext *C, wmOperator *UNUSED(op)) } /* duplicate keyframes */ - duplicate_action_keys(&ac); + if (!duplicate_action_keys(&ac)) { + return OPERATOR_CANCELLED; + } /* set notifier that keyframes have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_ADDED, NULL); diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c index 7eba3d49616..09163842587 100644 --- a/source/blender/editors/space_action/space_action.c +++ b/source/blender/editors/space_action/space_action.c @@ -225,6 +225,9 @@ static void action_main_region_draw(const bContext *C, ARegion *region) /* reset view matrix */ UI_view2d_view_restore(C); + /* gizmos */ + WM_gizmomap_draw(region->gizmo_map, C, WM_GIZMOMAP_DRAWSTEP_2D); + /* scrubbing region */ ED_time_scrub_draw(region, scene, saction->flag & SACTION_DRAWTIME, true); } @@ -861,7 +864,7 @@ void ED_spacetype_action(void) art->draw_overlay = action_main_region_draw_overlay; art->listener = action_main_region_listener; art->message_subscribe = saction_main_region_message_subscribe; - art->keymapflag = ED_KEYMAP_VIEW2D | ED_KEYMAP_ANIMATION | ED_KEYMAP_FRAMES; + art->keymapflag = ED_KEYMAP_GIZMO | ED_KEYMAP_VIEW2D | ED_KEYMAP_ANIMATION | ED_KEYMAP_FRAMES; BLI_addhead(&st->regiontypes, art); diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index ceac53bde6b..08f4ecc0dd9 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -329,7 +329,7 @@ typedef struct FileListEntryCache { int previews_todo_count; } FileListEntryCache; -/* FileListCache.flags */ +/** #FileListCache.flags */ enum { FLC_IS_INIT = 1 << 0, FLC_PREVIEWS_ACTIVE = 1 << 1, @@ -359,7 +359,7 @@ typedef struct FileListFilter { FileAssetCatalogFilterSettingsHandle *asset_catalog_filter; } FileListFilter; -/* FileListFilter.flags */ +/** #FileListFilter.flags */ enum { FLF_DO_FILTER = 1 << 0, FLF_HIDE_DOT = 1 << 1, @@ -421,7 +421,7 @@ typedef struct FileList { short tags; /* FileListTags */ } FileList; -/* FileList.flags */ +/** #FileList.flags */ enum { FL_FORCE_RESET = 1 << 0, /* Don't do a full reset (unless #FL_FORCE_RESET is also set), only reset files representing main @@ -434,7 +434,7 @@ enum { FL_SORT_INVERT = 1 << 6, }; -/* FileList.tags */ +/** #FileList.tags */ enum FileListTags { /** The file list has references to main data (IDs) and needs special care. */ FILELIST_TAGS_USES_MAIN_DATA = (1 << 0), diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c index bc82e7e20c2..2083a26f638 100644 --- a/source/blender/editors/space_graph/graph_edit.c +++ b/source/blender/editors/space_graph/graph_edit.c @@ -623,11 +623,12 @@ void GRAPH_OT_paste(wmOperatorType *ot) /** \name Duplicate Keyframes Operator * \{ */ -static void duplicate_graph_keys(bAnimContext *ac) +static bool duplicate_graph_keys(bAnimContext *ac) { ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; int filter; + bool changed = false; /* Filter data. */ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | @@ -636,13 +637,15 @@ static void duplicate_graph_keys(bAnimContext *ac) /* Loop through filtered data and delete selected keys. */ for (ale = anim_data.first; ale; ale = ale->next) { - duplicate_fcurve_keys((FCurve *)ale->key_data); + changed |= duplicate_fcurve_keys((FCurve *)ale->key_data); ale->update |= ANIM_UPDATE_DEFAULT; } ANIM_animdata_update(ac, &anim_data); ANIM_animdata_freelist(&anim_data); + + return changed; } /* ------------------- */ @@ -657,7 +660,9 @@ static int graphkeys_duplicate_exec(bContext *C, wmOperator *UNUSED(op)) } /* Duplicate keyframes. */ - duplicate_graph_keys(&ac); + if (!duplicate_graph_keys(&ac)) { + return OPERATOR_CANCELLED; + } /* Set notifier that keyframes have changed. */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_ADDED, NULL); diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 68cfd1c9ef0..88ab7617932 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -2579,8 +2579,20 @@ static void reroute_node_draw( const int x = BLI_rctf_cent_x(&node.totr) - (width / 2); const int y = node.totr.ymax; - uiBut *label_but = uiDefBut( - &block, UI_BTYPE_LABEL, 0, showname, x, y, width, (short)NODE_DY, nullptr, 0, 0, 0, 0, nullptr); + uiBut *label_but = uiDefBut(&block, + UI_BTYPE_LABEL, + 0, + showname, + x, + y, + width, + (short)NODE_DY, + nullptr, + 0, + 0, + 0, + 0, + nullptr); UI_but_drawflag_disable(label_but, UI_BUT_TEXT_LEFT); } diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index 956bb581ee6..9d83f977fe0 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -1268,6 +1268,7 @@ static int node_duplicate_exec(bContext *C, wmOperator *op) SpaceNode *snode = CTX_wm_space_node(C); bNodeTree *ntree = snode->edittree; const bool keep_inputs = RNA_boolean_get(op->ptr, "keep_inputs"); + bool changed = false; ED_preview_kill_jobs(CTX_wm_manager(C), bmain); @@ -1280,6 +1281,7 @@ static int node_duplicate_exec(bContext *C, wmOperator *op) bNode *new_node = blender::bke::node_copy_with_mapping( ntree, *node, LIB_ID_COPY_DEFAULT, true, socket_map); node_map.add_new(node, new_node); + changed = true; } /* make sure we don't copy new nodes again! */ @@ -1288,6 +1290,10 @@ static int node_duplicate_exec(bContext *C, wmOperator *op) } } + if (!changed) { + return OPERATOR_CANCELLED; + } + /* Copy links between selected nodes. */ bNodeLink *lastlink = (bNodeLink *)ntree->links.last; LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index b77f780e413..0305ad279a0 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -1687,6 +1687,7 @@ static int sequencer_add_duplicate_exec(bContext *C, wmOperator *UNUSED(op)) SEQ_select_active_set(scene, seq); } seq->flag &= ~(SEQ_LEFTSEL + SEQ_RIGHTSEL + SEQ_LOCK); + seq->flag |= SEQ_IGNORE_CHANNEL_LOCK; SEQ_animation_duplicate(scene, seq, &fcurves_original_backup); SEQ_ensure_unique_name(seq, scene); } @@ -2457,6 +2458,9 @@ static void sequencer_copy_animation(Scene *scene, Sequence *seq) } GSet *fcurves = SEQ_fcurves_by_strip_get(seq, &scene->adt->action->curves); + if (fcurves == NULL) { + return; + } GSET_FOREACH_BEGIN (FCurve *, fcu, fcurves) { BLI_addtail(&fcurves_clipboard, BKE_fcurve_copy(fcu)); diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h index 194aa518cd7..781aa521880 100644 --- a/source/blender/editors/space_sequencer/sequencer_intern.h +++ b/source/blender/editors/space_sequencer/sequencer_intern.h @@ -103,6 +103,7 @@ void draw_seq_strip_thumbnail(struct View2D *v2d, float pixely); /* sequencer_draw_channels.c */ + void draw_channels(const struct bContext *C, struct ARegion *region); void channel_draw_context_init(const struct bContext *C, struct ARegion *region, @@ -271,6 +272,7 @@ void SEQUENCER_OT_view_selected(struct wmOperatorType *ot); void SEQUENCER_OT_view_ghost_border(struct wmOperatorType *ot); /* sequencer_channels_edit.c */ + void SEQUENCER_OT_rename_channel(struct wmOperatorType *ot); /* sequencer_preview.c */ diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index 66df1309d54..8e5931b127a 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -1700,7 +1700,9 @@ static int sequencer_box_select_invoke(bContext *C, wmOperator *op, const wmEven if (tweak) { int hand_dummy; - Sequence *seq = find_nearest_seq(scene, v2d, &hand_dummy, event->mval); + int mval[2]; + WM_event_drag_start_mval(event, region, mval); + Sequence *seq = find_nearest_seq(scene, v2d, &hand_dummy, mval); if (seq != NULL) { return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; } diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c index 744fc61ddcf..e6895c0f4a3 100644 --- a/source/blender/editors/space_view3d/view3d_utils.c +++ b/source/blender/editors/space_view3d/view3d_utils.c @@ -1440,16 +1440,19 @@ void ED_view3d_to_object(const Depsgraph *depsgraph, BKE_object_apply_mat4_ex(ob, mat, ob_eval->parent, ob_eval->parentinv, true); } -bool ED_view3d_camera_to_view_selected(struct Main *bmain, - Depsgraph *depsgraph, - const Scene *scene, - Object *camera_ob) +static bool view3d_camera_to_view_selected_impl(struct Main *bmain, + Depsgraph *depsgraph, + const Scene *scene, + Object *camera_ob, + float *r_clip_start, + float *r_clip_end) { Object *camera_ob_eval = DEG_get_evaluated_object(depsgraph, camera_ob); float co[3]; /* the new location to apply */ float scale; /* only for ortho cameras */ - if (BKE_camera_view_frame_fit_to_scene(depsgraph, scene, camera_ob_eval, co, &scale)) { + if (BKE_camera_view_frame_fit_to_scene( + depsgraph, scene, camera_ob_eval, co, &scale, r_clip_start, r_clip_end)) { ObjectTfmProtectedChannels obtfm; float obmat_new[4][4]; @@ -1475,6 +1478,38 @@ bool ED_view3d_camera_to_view_selected(struct Main *bmain, return false; } +bool ED_view3d_camera_to_view_selected(struct Main *bmain, + Depsgraph *depsgraph, + const Scene *scene, + Object *camera_ob) +{ + return view3d_camera_to_view_selected_impl(bmain, depsgraph, scene, camera_ob, NULL, NULL); +} + +bool ED_view3d_camera_to_view_selected_with_set_clipping(struct Main *bmain, + Depsgraph *depsgraph, + const Scene *scene, + Object *camera_ob) +{ + float clip_start; + float clip_end; + if (view3d_camera_to_view_selected_impl( + bmain, depsgraph, scene, camera_ob, &clip_start, &clip_end)) { + + ((Camera *)camera_ob->data)->clip_start = clip_start; + ((Camera *)camera_ob->data)->clip_end = clip_end; + + /* TODO: Support update via #ID_RECALC_PARAMETERS. */ + Object *camera_ob_eval = DEG_get_evaluated_object(depsgraph, camera_ob); + ((Camera *)camera_ob_eval->data)->clip_start = clip_start; + ((Camera *)camera_ob_eval->data)->clip_end = clip_end; + + return true; + } + + return false; +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/transform/transform_convert_sequencer.c b/source/blender/editors/transform/transform_convert_sequencer.c index 16a96001fd2..990d3680057 100644 --- a/source/blender/editors/transform/transform_convert_sequencer.c +++ b/source/blender/editors/transform/transform_convert_sequencer.c @@ -606,6 +606,11 @@ static void freeSeqData(TransInfo *t, TransDataContainer *tc, TransCustomData *c SeqCollection *transformed_strips = seq_transform_collection_from_transdata(tc); SEQ_collection_expand(seqbase_active_get(t), transformed_strips, SEQ_query_strip_effect_chain); + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, transformed_strips) { + seq->flag &= ~SEQ_IGNORE_CHANNEL_LOCK; + } + if (t->state == TRANS_CANCEL) { seq_transform_cancel(t, transformed_strips); SEQ_collection_free(transformed_strips); |